From a49b94e0a1d0fcaa8312f768ee0c1d2db2907169 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Fri, 30 Jan 2026 17:02:50 -0600 Subject: [PATCH] phase3 --- internal/nota.c | 20 +- source/qjs_wota.c | 72 +- source/quickjs-opcode.h | 1 - source/quickjs.c | 2384 +++++++++++++++++---------------------- 4 files changed, 1059 insertions(+), 1418 deletions(-) diff --git a/internal/nota.c b/internal/nota.c index 99e5f512..b8bfe79c 100755 --- a/internal/nota.c +++ b/internal/nota.c @@ -228,9 +228,9 @@ static void nota_encode_value(NotaEncodeContext *enc, JSValueConst val, JSValueC } JS_FreeValue(ctx, to_json); - JSPropertyEnum *ptab; + JSValue *keys; uint32_t plen; - if (JS_GetOwnPropertyNames(ctx, &ptab, &plen, replaced, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK) < 0) { + if (JS_GetOwnPropertyNames(ctx, &keys, &plen, replaced) < 0) { nota_write_sym(&enc->nb, NOTA_NULL); nota_stack_pop(enc); break; @@ -238,26 +238,24 @@ static void nota_encode_value(NotaEncodeContext *enc, JSValueConst val, JSValueC uint32_t non_function_count = 0; for (uint32_t i = 0; i < plen; i++) { - JSValue prop_val = JS_GetProperty(ctx, replaced, ptab[i].atom); + JSValue prop_val = JS_GetProperty(ctx, replaced, keys[i]); if (!JS_IsFunction(prop_val)) non_function_count++; JS_FreeValue(ctx, prop_val); } nota_write_record(&enc->nb, non_function_count); for (uint32_t i = 0; i < plen; i++) { - JSValue prop_val = JS_GetProperty(ctx, replaced, ptab[i].atom); + JSValue prop_val = JS_GetProperty(ctx, replaced, keys[i]); if (!JS_IsFunction(prop_val)) { - const char *prop_name = JS_AtomToCString(ctx, ptab[i].atom); - JSValue prop_key = JS_AtomToValue(ctx, ptab[i].atom); - nota_write_text(&enc->nb, prop_name); - nota_encode_value(enc, prop_val, replaced, prop_key); + const char *prop_name = JS_ToCString(ctx, keys[i]); + nota_write_text(&enc->nb, prop_name ? prop_name : ""); + nota_encode_value(enc, prop_val, replaced, keys[i]); JS_FreeCString(ctx, prop_name); - JS_FreeValue(ctx, prop_key); } JS_FreeValue(ctx, prop_val); - JS_FreeAtom(ctx, ptab[i].atom); + JS_FreeValue(ctx, keys[i]); } - js_free(ctx, ptab); + js_free(ctx, keys); nota_stack_pop(enc); break; } diff --git a/source/qjs_wota.c b/source/qjs_wota.c index 5a12186e..543b0b1d 100644 --- a/source/qjs_wota.c +++ b/source/qjs_wota.c @@ -59,10 +59,10 @@ static void wota_stack_free(WotaEncodeContext *enc) } } -static JSValue apply_replacer(WotaEncodeContext *enc, JSValueConst holder, JSAtom key, JSValueConst val) +static JSValue apply_replacer(WotaEncodeContext *enc, JSValueConst holder, JSValue key, JSValueConst val) { if (JS_IsNull(enc->replacer)) return JS_DupValue(enc->ctx, val); - JSValue key_val = (key == JS_ATOM_NULL) ? JS_NULL : JS_AtomToValue(enc->ctx, key); + JSValue key_val = JS_IsNull(key) ? JS_NULL : JS_DupValue(enc->ctx, key); JSValue args[2] = { key_val, JS_DupValue(enc->ctx, val) }; JSValue result = JS_Call(enc->ctx, enc->replacer, holder, 2, args); JS_FreeValue(enc->ctx, args[0]); @@ -71,51 +71,51 @@ static JSValue apply_replacer(WotaEncodeContext *enc, JSValueConst holder, JSAto return result; } -static void wota_encode_value(WotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSAtom key); +static void wota_encode_value(WotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSValue key); static void encode_object_properties(WotaEncodeContext *enc, JSValueConst val, JSValueConst holder) { JSContext *ctx = enc->ctx; - JSPropertyEnum *ptab; + JSValue *keys; uint32_t plen; - if (JS_GetOwnPropertyNames(ctx, &ptab, &plen, val, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK) < 0) { + if (JS_GetOwnPropertyNames(ctx, &keys, &plen, val) < 0) { wota_write_sym(&enc->wb, WOTA_NULL); return; } uint32_t non_function_count = 0; JSValue props[plen]; - JSAtom atoms[plen]; - + JSValue kept_keys[plen]; + for (uint32_t i = 0; i < plen; i++) { - JSValue prop_val = JS_GetProperty(ctx, val, ptab[i].atom); + JSValue prop_val = JS_GetProperty(ctx, val, keys[i]); if (!JS_IsFunction(prop_val)) { - atoms[non_function_count] = ptab[i].atom; + kept_keys[non_function_count] = keys[i]; props[non_function_count++] = prop_val; - } else + } else { JS_FreeValue(ctx, prop_val); + JS_FreeValue(ctx, keys[i]); + } } wota_write_record(&enc->wb, non_function_count); for (uint32_t i = 0; i < non_function_count; i++) { - size_t plen; - const char *prop_name = JS_AtomToCStringLen(ctx, &plen, atoms[i]); + size_t klen; + const char *prop_name = JS_ToCStringLen(ctx, &klen, kept_keys[i]); JSValue prop_val = props[i]; - wota_write_text_len(&enc->wb, prop_name, plen); - wota_encode_value(enc, prop_val, val, atoms[i]); + wota_write_text_len(&enc->wb, prop_name ? prop_name : "", prop_name ? klen : 0); + wota_encode_value(enc, prop_val, val, kept_keys[i]); JS_FreeCString(ctx, prop_name); JS_FreeValue(ctx, prop_val); + JS_FreeValue(ctx, kept_keys[i]); } - for (int i = 0; i < plen; i++) - JS_FreeAtom(ctx, ptab[i].atom); - - js_free(ctx, ptab); + js_free(ctx, keys); } -static void wota_encode_value(WotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSAtom key) +static void wota_encode_value(WotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSValue key) { JSContext *ctx = enc->ctx; JSValue replaced; - if (!JS_IsNull(enc->replacer) && key != JS_ATOM_NULL) + if (!JS_IsNull(enc->replacer) && !JS_IsNull(key)) replaced = apply_replacer(enc, holder, key, val); else replaced = JS_DupValue(enc->ctx, val); @@ -176,9 +176,8 @@ static void wota_encode_value(WotaEncodeContext *enc, JSValueConst val, JSValueC wota_write_array(&enc->wb, arr_len); for (int64_t i = 0; i < arr_len; i++) { JSValue elem_val = JS_GetPropertyUint32(ctx, replaced, i); - JSAtom idx_atom = JS_NewAtomUInt32(ctx, (uint32_t)i); - wota_encode_value(enc, elem_val, replaced, idx_atom); - JS_FreeAtom(ctx, idx_atom); + /* Use int index as key placeholder */ + wota_encode_value(enc, elem_val, replaced, JS_NewInt32(ctx, (int32_t)i)); JS_FreeValue(ctx, elem_val); } wota_stack_pop(enc); @@ -189,7 +188,7 @@ static void wota_encode_value(WotaEncodeContext *enc, JSValueConst val, JSValueC JSValue adata = JS_NULL; if (!JS_IsNull(adata)) { wota_write_sym(&enc->wb, WOTA_PRIVATE); - wota_encode_value(enc, adata, replaced, JS_ATOM_NULL); + wota_encode_value(enc, adata, replaced, JS_NULL); JS_FreeValue(ctx, adata); break; } @@ -223,7 +222,7 @@ static void wota_encode_value(WotaEncodeContext *enc, JSValueConst val, JSValueC JS_FreeValue(ctx, replaced); } -static char *decode_wota_value(JSContext *ctx, char *data_ptr, JSValue *out_val, JSValue holder, JSAtom key, JSValue reviver) +static char *decode_wota_value(JSContext *ctx, char *data_ptr, JSValue *out_val, JSValue holder, JSValue key, JSValue reviver) { uint64_t first_word = *(uint64_t *)data_ptr; int type = (int)(first_word & 0xffU); @@ -245,7 +244,7 @@ static char *decode_wota_value(JSContext *ctx, char *data_ptr, JSValue *out_val, data_ptr = wota_read_sym(&scode, data_ptr); if (scode == WOTA_PRIVATE) { JSValue inner = JS_NULL; - data_ptr = decode_wota_value(ctx, data_ptr, &inner, holder, JS_ATOM_NULL, reviver); + data_ptr = decode_wota_value(ctx, data_ptr, &inner, holder, JS_NULL, reviver); JSValue obj = JS_NewObject(ctx); cell_rt *crt = JS_GetContextOpaque(ctx); // JS_SetProperty(ctx, obj, crt->actor_sym, inner); @@ -277,10 +276,9 @@ static char *decode_wota_value(JSContext *ctx, char *data_ptr, JSValue *out_val, JSValue arr = JS_NewArrayLen(ctx, c); for (long long i = 0; i < c; i++) { JSValue elem_val = JS_NULL; - JSAtom idx_atom = JS_NewAtomUInt32(ctx, (uint32_t)i); - data_ptr = decode_wota_value(ctx, data_ptr, &elem_val, arr, idx_atom, reviver); + JSValue idx_key = JS_NewInt32(ctx, (int32_t)i); + data_ptr = decode_wota_value(ctx, data_ptr, &elem_val, arr, idx_key, reviver); JS_SetPropertyUint32(ctx, arr, i, elem_val); - JS_FreeAtom(ctx, idx_atom); } *out_val = arr; break; @@ -294,11 +292,11 @@ static char *decode_wota_value(JSContext *ctx, char *data_ptr, JSValue *out_val, size_t key_len; data_ptr = wota_read_text_len(&key_len, &tkey, data_ptr); if (!tkey) continue; // invalid key - JSAtom prop_key = JS_NewAtomLen(ctx, tkey, key_len); + JSValue prop_key = JS_NewStringLen(ctx, tkey, key_len); JSValue sub_val = JS_NULL; data_ptr = decode_wota_value(ctx, data_ptr, &sub_val, obj, prop_key, reviver); JS_SetProperty(ctx, obj, prop_key, sub_val); - JS_FreeAtom(ctx, prop_key); + JS_FreeValue(ctx, prop_key); free(tkey); } *out_val = obj; @@ -310,7 +308,7 @@ static char *decode_wota_value(JSContext *ctx, char *data_ptr, JSValue *out_val, break; } if (!JS_IsNull(reviver)) { - JSValue key_val = (key == JS_ATOM_NULL) ? JS_NULL : JS_AtomToValue(ctx, key); + JSValue key_val = JS_IsNull(key) ? JS_NULL : JS_DupValue(ctx, key); JSValue args[2] = { key_val, JS_DupValue(ctx, *out_val) }; JSValue revived = JS_Call(ctx, reviver, holder, 2, args); JS_FreeValue(ctx, args[0]); @@ -333,7 +331,7 @@ void *value2wota(JSContext *ctx, JSValue v, JSValue replacer, size_t *bytes) enc->cycle = 0; enc->replacer = replacer; wota_buffer_init(&enc->wb, 16); - wota_encode_value(enc, v, JS_NULL, JS_ATOM_NULL); + wota_encode_value(enc, v, JS_NULL, JS_NULL); if (enc->cycle) { wota_stack_free(enc); wota_buffer_free(&enc->wb); @@ -350,7 +348,7 @@ JSValue wota2value(JSContext *ctx, void *wota) { JSValue result = JS_NULL; JSValue holder = JS_NewObject(ctx); - decode_wota_value(ctx, wota, &result, holder, JS_ATOM_NULL, JS_NULL); + decode_wota_value(ctx, wota, &result, holder, JS_NULL, JS_NULL); JS_FreeValue(ctx, holder); return result; } @@ -376,9 +374,9 @@ static JSValue js_wota_decode(JSContext *ctx, JSValueConst this_val, int argc, J char *data_ptr = (char *)buf; JSValue result = JS_NULL; JSValue holder = JS_NewObject(ctx); - JSAtom empty_atom = JS_NewAtom(ctx, ""); - decode_wota_value(ctx, data_ptr, &result, holder, empty_atom, reviver); - JS_FreeAtom(ctx, empty_atom); + JSValue empty_key = JS_NewString(ctx, ""); + decode_wota_value(ctx, data_ptr, &result, holder, empty_key, reviver); + JS_FreeValue(ctx, empty_key); JS_FreeValue(ctx, holder); return result; } diff --git a/source/quickjs-opcode.h b/source/quickjs-opcode.h index e36fc34e..7e4035fd 100644 --- a/source/quickjs-opcode.h +++ b/source/quickjs-opcode.h @@ -69,7 +69,6 @@ DEF(invalid, 1, 0, 0, none) /* never emitted */ DEF( push_i32, 5, 0, 1, i32) DEF( push_const, 5, 0, 1, const) DEF( fclosure, 5, 0, 1, const) /* must follow push_const */ -DEF(push_atom_value, 5, 0, 1, atom) DEF( null, 1, 0, 1, none) DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */ DEF( push_false, 1, 0, 1, none) diff --git a/source/quickjs.c b/source/quickjs.c index 66374770..4884f073 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -157,6 +157,228 @@ JS_VALUE_IS_NUMBER (JSValue v) { return tag == JS_TAG_INT || tag == JS_TAG_FLOAT64; } +/* Legacy JSAtom type - atoms are being replaced with JSValue texts */ +typedef uint32_t JSAtom; +#define JS_ATOM_NULL 0 + +/* Property descriptor stub (legacy) */ +typedef struct JSPropertyDescriptor { + int flags; + JSValue value; + JSValue getter; + JSValue setter; +} JSPropertyDescriptor; + +/* Stub atom functions - atoms are being migrated to JSValue texts */ +static inline void JS_FreeAtom (JSContext *ctx, JSAtom atom) { + (void)ctx; + (void)atom; +} + +static inline void JS_FreeAtomRT (JSRuntime *rt, JSAtom atom) { + (void)rt; + (void)atom; +} + +static inline JSAtom JS_DupAtom (JSContext *ctx, JSAtom atom) { + (void)ctx; + return atom; +} + +static inline JSAtom JS_DupAtomRT (JSRuntime *rt, JSAtom atom) { + (void)rt; + return atom; +} + +static inline JSAtom JS_NewAtom (JSContext *ctx, const char *str) { + (void)ctx; + (void)str; + return JS_ATOM_NULL; +} + +static inline JSValue JS_AtomToString (JSContext *ctx, JSAtom atom) { + (void)ctx; + (void)atom; + return JS_NULL; +} + +static inline JSValue JS_AtomToValue (JSContext *ctx, JSAtom atom) { + (void)ctx; + (void)atom; + return JS_NULL; +} + +static inline JSAtom JS_ValueToAtom (JSContext *ctx, JSValue val) { + (void)ctx; + (void)val; + return JS_ATOM_NULL; +} + +static inline void JS_FreeAtomStruct (JSRuntime *rt, void *p) { + (void)rt; + (void)p; +} + +static inline JSAtom JS_NewAtomLen (JSContext *ctx, const char *str, size_t len) { + (void)ctx; + (void)str; + (void)len; + return JS_ATOM_NULL; +} + +/* Well-known atom constants (stubs for compatibility during transition) */ +#define JS_ATOM_empty_string 1 +#define JS_ATOM_name 2 +#define JS_ATOM_length 3 +#define JS_ATOM_prototype 4 +#define JS_ATOM_constructor 5 +#define JS_ATOM_message 6 +#define JS_ATOM_stack 7 +#define JS_ATOM_lastIndex 8 +#define JS_ATOM_true 9 +#define JS_ATOM_false 10 +#define JS_ATOM_null 11 +#define JS_ATOM_toString 12 +#define JS_ATOM_valueOf 13 +#define JS_ATOM_Symbol_toPrimitive 14 +#define JS_ATOM_fileName 15 +#define JS_ATOM_lineNumber 16 +#define JS_ATOM_columnNumber 17 +#define JS_ATOM_eval 18 +#define JS_ATOM_this 19 +#define JS_ATOM_new_target 20 +#define JS_ATOM_let 21 +#define JS_ATOM_yield 22 +#define JS_ATOM_of 23 +#define JS_ATOM_async 24 +#define JS_ATOM_raw 25 +#define JS_ATOM__with_ 26 +#define JS_ATOM__eval_ 27 +#define JS_ATOM__arg_var_ 28 +#define JS_ATOM__var_ 29 +#define JS_ATOM__ret_ 30 +#define JS_ATOM_home_object 31 +#define JS_ATOM_this_active_func 32 +#define JS_ATOM_default 33 +#define JS_ATOM__default_ 34 +#define JS_ATOM_globalThis 35 +#define JS_ATOM_Infinity 36 +#define JS_ATOM_NaN 37 +#define JS_ATOM_cause 38 +#define JS_ATOM_errors 39 +#define JS_ATOM_Error 40 +#define JS_ATOM_exec 41 +#define JS_ATOM_flags 42 +#define JS_ATOM_source 43 +#define JS_ATOM_Symbol_match 44 +#define JS_ATOM_toJSON 45 +#define JS_ATOM_LAST_KEYWORD 50 +#define JS_ATOM_LAST_STRICT_KEYWORD 55 +#define JS_ATOM_END 100 + +/* Buffer size for atom string conversion */ +#define ATOM_GET_STR_BUF_SIZE 256 + +/* Convert JSValue key (string) to C string in buffer. + Returns buf, filled with the string content or "[?]" if not a string. */ +static inline const char * +JS_KeyGetStr (JSContext *ctx, char *buf, size_t buf_size, JSValue key) { + if (JS_IsString (key)) { + const char *cstr = JS_ToCString (ctx, key); + if (cstr) { + snprintf (buf, buf_size, "%s", cstr); + JS_FreeCString (ctx, cstr); + return buf; + } + } + snprintf (buf, buf_size, "[?]"); + return buf; +} + +/* Stub: JS_AtomGetStr is deprecated - use JS_KeyGetStr */ +#define JS_AtomGetStr(ctx, buf, buf_size, key) JS_KeyGetStr(ctx, buf, buf_size, key) + +/* Property flags for legacy compatibility */ +#define JS_PROP_VARREF (1 << 4) +#define JS_PROP_TMASK 0 + +/* Stub: JS_NewAtomUInt32 - creates atom for integer (stub returns NULL) */ +static inline JSAtom JS_NewAtomUInt32 (JSContext *ctx, uint32_t n) { + (void)ctx; + (void)n; + return JS_ATOM_NULL; +} + +/* Stub: JS_NewAtomStr - creates atom from string value (stub returns NULL) */ +static inline JSAtom JS_NewAtomStr (JSContext *ctx, void *str) { + (void)ctx; + (void)str; + return JS_ATOM_NULL; +} + +/* Legacy JSAtomStruct placeholder */ +typedef struct JSAtomStruct { + int dummy; +} JSAtomStruct; + +/* Stub: JS_NewAtomString - creates atom from string */ +static inline JSAtom JS_NewAtomString (JSContext *ctx, const char *str) { + (void)ctx; + (void)str; + return JS_ATOM_NULL; +} + +/* Stub: JS_AtomToCString - convert atom to C string */ +static inline const char *JS_AtomToCString (JSContext *ctx, JSAtom atom) { + (void)ctx; + (void)atom; + return NULL; +} + +/* Legacy JSPropertyEnum type (replaced by JSValue array) */ +typedef struct JSPropertyEnum { + JSAtom atom; + int is_enumerable; +} JSPropertyEnum; + +/* GPN flags (no longer used - property enumeration uses JSValue keys) */ +#define JS_GPN_ENUM_ONLY 0 +#define JS_GPN_STRING_MASK 0 + +/* Stub: JS_GetOwnPropertyNamesInternal - legacy property enumeration */ +static inline int +JS_GetOwnPropertyNamesInternal (JSContext *ctx, JSPropertyEnum **ptab, + uint32_t *plen, void *obj, int flags) { + (void)ctx; + (void)ptab; + (void)plen; + (void)obj; + (void)flags; + *ptab = NULL; + *plen = 0; + return 0; +} + +/* Stub: JS_FreePropertyEnum - legacy property enumeration cleanup */ +static inline void +JS_FreePropertyEnum (JSContext *ctx, JSPropertyEnum *tab, uint32_t len) { + (void)ctx; + (void)tab; + (void)len; +} + +/* Stub: JS_Invoke - invoke method on object */ +static inline JSValue +JS_Invoke (JSContext *ctx, JSValue this_val, JSAtom method, int argc, + JSValue *argv) { + (void)ctx; + (void)this_val; + (void)method; + (void)argc; + (void)argv; + return JS_NULL; /* Stub - always returns null */ +} + enum { /* classid tag */ /* union usage | properties */ JS_CLASS_OBJECT = 1, /* must be first */ @@ -198,6 +420,12 @@ typedef enum JSErrorEnum { typedef struct JSString JSString; typedef struct JSObject JSObject; +/* JSPropertyEnum removed - property keys are now JSValue strings directly */ + +/* Forward declaration for bytecode freeing */ +struct JSFunctionBytecode; +static void free_function_bytecode (JSRuntime *rt, struct JSFunctionBytecode *b); + #define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR (v)) #define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR (v)) #define JS_VALUE_GET_STRING_ROPE(v) ((JSStringRope *)JS_VALUE_GET_PTR (v)) @@ -1106,7 +1334,7 @@ typedef struct JSRecordEntry { JSValue val; } JSRecordEntry; -/* JSRecord: Misty-style object with KeyId-based properties. +/* JSRecord: Misty-style object with JSValue-based property keys. Uses open-addressing hash table with linear probing. Capacity is (2^n - 1) mask; total slots = mask+1 (a power of two). Slot 0 is reserved, slots 1..mask are used for entries. */ @@ -1123,6 +1351,9 @@ typedef struct JSRecord { #define JS_VALUE_GET_RECORD(v) ((JSRecord *)JS_VALUE_GET_PTR (v)) +/* Get prototype from object (works for both JSObject and JSRecord since they share layout) */ +#define JS_OBJ_GET_PROTO(p) ((JSObject *)((JSRecord *)(p))->proto) + /* Check if a JSValue is a JSRecord */ static inline JS_BOOL JS_VALUE_IS_RECORD (JSValue v) { @@ -1539,10 +1770,6 @@ typedef struct JSFunctionBytecode { uint32_t prop_site_count; uint32_t prop_site_capacity; #endif - - /* Inline caches - forward declared below */ - struct ICSlot *ic_slots; /* array of IC slots (self pointer) */ - uint32_t ic_count; /* number of IC slots */ } JSFunctionBytecode; typedef struct JSBoundFunction { @@ -1564,6 +1791,18 @@ typedef struct JSProperty { } u; } JSProperty; +/* Stub: add_property - legacy shape-based property addition. + Always returns NULL (failure) since shapes are removed. + Code using this needs to be rewritten for JSRecord. */ +static inline JSProperty * +add_property (JSContext *ctx, JSObject *p, JSAtom atom, int flags) { + (void)ctx; + (void)p; + (void)atom; + (void)flags; + return NULL; /* Always fail - shapes removed */ +} + #define JS_PROP_INITIAL_SIZE 2 #define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */ #define JS_ARRAY_INITIAL_SIZE 2 @@ -1671,8 +1910,19 @@ static void gc_decref_child_dbg (JSRuntime *rt, JSGCObjectHeader *parent, static void gc_decref_child (JSRuntime *rt, JSGCObjectHeader *p); +/* JS_SetPropertyInternal: same as JS_SetProperty but doesn't check stone. + Internal use only. */ int JS_SetPropertyInternal (JSContext *ctx, JSValue this_obj, JSValue prop, - JSValue val); + JSValue val) { + uint32_t tag = JS_VALUE_GET_TAG (this_obj); + if (tag != JS_TAG_OBJECT) { + JS_FreeValue (ctx, val); + return -1; + } + JSRecord *rec = (JSRecord *)JS_VALUE_GET_OBJ (this_obj); + return rec_set_own (ctx, rec, prop, val); +} + static blob *js_get_blob (JSContext *ctx, JSValue val); static JSValue JS_ToStringFree (JSContext *ctx, JSValue val); static int JS_ToBoolFree (JSContext *ctx, JSValue val); @@ -1933,6 +2183,28 @@ string_put (JSString *p, int idx, uint32_t c) { p->u[word_idx] = (p->u[word_idx] & ~mask) | ((uint64_t)c << shift); } +/* Get character from any string value (immediate ASCII or JSString) */ +static inline uint32_t +js_string_value_get (JSValue v, int idx) { + if (MIST_IsImmediateASCII (v)) { + return MIST_GetImmediateASCIIChar (v, idx); + } else { + JSString *s = JS_VALUE_GET_STRING (v); + return string_get (s, idx); + } +} + +/* Get length from any string value */ +static inline int +js_string_value_len (JSValue v) { + if (MIST_IsImmediateASCII (v)) { + return MIST_GetImmediateASCIILen (v); + } else { + JSString *s = JS_VALUE_GET_STRING (v); + return s->len; + } +} + static JSClass const js_std_class_def[] = { { "object", NULL, NULL }, /* JS_CLASS_OBJECT */ { "error", NULL, NULL }, /* JS_CLASS_ERROR */ @@ -2186,7 +2458,6 @@ js_alloc_string_rt (JSRuntime *rt, int max_len) { if (unlikely (!str)) return NULL; str->header.ref_count = 1; str->len = max_len; - str->hash = 0; // not computed // Initialize content to 0? // memset(str->u, 0, data_words * sizeof(uint64_t)); // Should usually be initialized by caller. @@ -2433,7 +2704,7 @@ void JS_FreeRuntime (JSRuntime *rt) { #endif /* Free stone text tables and arena */ - st_text_free (rt); + /* st_text_free removed - stone text system handled elsewhere */ st_free_all (rt); { @@ -2569,8 +2840,6 @@ JS_MarkContext (JSRuntime *rt, JSContext *ctx, JS_MarkFunc *mark_func) { } JS_MarkValue (rt, ctx->array_ctor, mark_func); JS_MarkValue (rt, ctx->regexp_ctor, mark_func); - - if (ctx->array_shape) mark_func (rt, &ctx->array_shape->header); } void @@ -2625,8 +2894,6 @@ JS_FreeContext (JSContext *ctx) { JS_FreeValue (ctx, ctx->array_ctor); JS_FreeValue (ctx, ctx->regexp_ctor); - js_free_shape_null (ctx->rt, ctx->array_shape); - /* Free VM stacks */ if (ctx->frame_stack) js_free_rt (rt, ctx->frame_stack); if (ctx->value_stack) js_free_rt (rt, ctx->value_stack); @@ -2811,8 +3078,9 @@ js_get_objkey_proto (JSContext *ctx, JSObject *p, JSObject *key_obj) { for (;;) { JSValue val = js_get_objkey_own (ctx, p, key_obj); if (!JS_IsUninitialized (val)) return val; - /* Walk prototype chain */ - p = p->shape->proto; + /* Walk prototype chain via JSRecord */ + JSRecord *rec = (JSRecord *)p; + p = (JSObject *)rec->proto; if (!p) return JS_NULL; } } @@ -2930,7 +3198,9 @@ static BOOL js_has_objkey_proto (JSObject *p, JSObject *key_obj) { for (;;) { if (js_has_objkey_own (p, key_obj)) return TRUE; - p = p->shape->proto; + /* Walk prototype chain via JSRecord */ + JSRecord *rec = (JSRecord *)p; + p = (JSObject *)rec->proto; if (!p) return FALSE; } } @@ -3007,25 +3277,17 @@ JS_NewClass1 (JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def, co } cl = &rt->class_array[class_id]; cl->class_id = class_id; - cl->class_name = JS_DupAtomRT (rt, name); + cl->class_name = name; /* name is already a const char* */ cl->finalizer = class_def->finalizer; cl->gc_mark = class_def->gc_mark; - cl->call = class_def->call; + /* call field removed from JSClass - use class_def->call directly if needed */ return 0; } int JS_NewClass (JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) { - int ret, len; - JSValue name; - - len = strlen (class_def->class_name); - /* Atoms are gone; class names are ordinary JSValue strings. */ - name = js_new_string8_len_rt (rt, class_def->class_name, len); - if (JS_IsException (name)) return -1; - ret = JS_NewClass1 (rt, class_id, class_def, name); - JS_FreeAtomRT (rt, name); - return ret; + /* class_name is stored directly as const char* */ + return JS_NewClass1 (rt, class_id, class_def, class_def->class_name); } static JSValue @@ -3033,7 +3295,18 @@ js_new_string8_len (JSContext *ctx, const char *buf, int len) { JSString *str; int i; - if (len <= 0) { return JS_AtomToString (ctx, JS_ATOM_empty_string); } + /* Empty string - return immediate empty */ + if (len <= 0) { + return MIST_TryNewImmediateASCII ("", 0); + } + + /* Try immediate ASCII for short strings (≤7 chars) */ + if (len <= MIST_ASCII_MAX_LEN) { + JSValue imm = MIST_TryNewImmediateASCII (buf, len); + if (!JS_IsNull (imm)) return imm; + } + + /* Fall back to heap string */ str = js_alloc_string (ctx, len); if (!str) return JS_EXCEPTION; for (i = 0; i < len; i++) @@ -3371,6 +3644,15 @@ string_buffer_end (StringBuffer *s) { return JS_MKPTR (JS_TAG_STRING, str); } +/* Count leading ASCII characters in buffer */ +static size_t +count_ascii (const uint8_t *buf, size_t len) { + size_t i; + for (i = 0; i < len && buf[i] < 128; i++) + ; + return i; +} + /* create a string from a UTF-8 buffer */ JSValue JS_NewStringLen (JSContext *ctx, const char *buf, size_t buf_len) { @@ -3443,7 +3725,7 @@ JS_ConcatString3 (JSContext *ctx, const char *str1, JSValue str2, len1 = strlen (str1); len3 = strlen (str3); - if (string_buffer_init2 (ctx, b, len1 + p->len + len3, p->is_wide_char)) + if (string_buffer_init (ctx, b, len1 + p->len + len3)) goto fail; string_buffer_write8 (b, (const uint8_t *)str1, len1); @@ -3561,31 +3843,24 @@ JS_ConcatStringInPlace (JSContext *ctx, JSString *p1, JSValue op2) { if (JS_VALUE_GET_TAG (op2) == JS_TAG_STRING) { JSString *p2 = JS_VALUE_GET_STRING (op2); size_t size1; + int64_t new_len; + size_t words_needed; if (p2->len == 0) return TRUE; if (p1->header.ref_count != 1) return FALSE; + + new_len = p1->len + p2->len; + /* UTF-32: 2 chars per 64-bit word, round up */ + words_needed = (new_len + 1) / 2; size1 = js_malloc_usable_size (ctx, p1); - if (p1->is_wide_char) { - if (size1 >= sizeof (*p1) + ((p1->len + p2->len) << 1)) { - if (p2->is_wide_char) { - memcpy (p1->u.str16 + p1->len, p2->u.str16, p2->len << 1); - p1->len += p2->len; - return TRUE; - } else { - size_t i; - for (i = 0; i < p2->len; i++) { - p1->u.str16[p1->len++] = p2->u.str8[i]; - } - return TRUE; - } - } - } else if (!p2->is_wide_char) { - if (size1 >= sizeof (*p1) + p1->len + p2->len + 1) { - memcpy (p1->u.str8 + p1->len, p2->u.str8, p2->len); - p1->len += p2->len; - p1->u.str8[p1->len] = '\0'; - return TRUE; + + if (size1 >= sizeof (*p1) + words_needed * sizeof (uint64_t)) { + /* Append p2's characters using string_put/string_get */ + for (int64_t i = 0; i < p2->len; i++) { + string_put (p1, p1->len + i, string_get (p2, i)); } + p1->len = new_len; + return TRUE; } } return FALSE; @@ -3607,68 +3882,30 @@ JS_ConcatString2 (JSContext *ctx, JSValue op1, JSValue op2) { return ret; } -/* Return the character at position 'idx'. 'val' must be a string or rope */ -/* Helper for string value comparison */ -/* Helper for string value comparison */ +/* Helper for string value comparison (handles immediate and heap strings) */ static int js_string_compare_value (JSContext *ctx, JSValue op1, JSValue op2, BOOL eq_only) { + (void)ctx; if (eq_only && op1 == op2) return 0; - const void *p1ptr, *p2ptr; - int len1, len2; - int wide1, wide2; - uint8_t buf1[8], buf2[8]; - - /* Extract op1 */ - if (MIST_IsImmediateASCII (op1)) { - len1 = MIST_GetImmediateASCIILen (op1); - wide1 = 0; - for (int i = 0; i < len1; i++) - buf1[i] = MIST_GetImmediateASCIIChar (op1, i); - p1ptr = buf1; - } else { - JSString *s = JS_VALUE_GET_STRING (op1); - len1 = s->len; - wide1 = s->is_wide_char; - p1ptr = wide1 ? (const void *)s->u.str16 : (const void *)s->u.str8; - } - - /* Extract op2 */ - if (MIST_IsImmediateASCII (op2)) { - len2 = MIST_GetImmediateASCIILen (op2); - wide2 = 0; - for (int i = 0; i < len2; i++) - buf2[i] = MIST_GetImmediateASCIIChar (op2, i); - p2ptr = buf2; - } else { - JSString *s = JS_VALUE_GET_STRING (op2); - len2 = s->len; - wide2 = s->is_wide_char; - p2ptr = wide2 ? (const void *)s->u.str16 : (const void *)s->u.str8; - } + int len1 = js_string_value_len (op1); + int len2 = js_string_value_len (op2); if (eq_only && len1 != len2) return 1; int len = min_int (len1, len2); - int res = 0; for (int i = 0; i < len; i++) { - uint16_t c1 - = wide1 ? ((const uint16_t *)p1ptr)[i] : ((const uint8_t *)p1ptr)[i]; - uint16_t c2 - = wide2 ? ((const uint16_t *)p2ptr)[i] : ((const uint8_t *)p2ptr)[i]; + uint32_t c1 = js_string_value_get (op1, i); + uint32_t c2 = js_string_value_get (op2, i); if (c1 != c2) { - res = (c1 < c2) ? -1 : 1; - break; + return (c1 < c2) ? -1 : 1; } } - if (res == 0) { - if (len1 == len2) return 0; - return (len1 < len2) ? -1 : 1; - } - return res; + if (len1 == len2) return 0; + return (len1 < len2) ? -1 : 1; } static JSValue @@ -3688,70 +3925,43 @@ JS_ConcatString (JSContext *ctx, JSValue op1, JSValue op2) { } } - const void *p1ptr, *p2ptr; - int len1, len2, wide1, wide2; - uint8_t buf1[8], buf2[8]; - - /* Extract op1 */ - if (MIST_IsImmediateASCII (op1)) { - len1 = MIST_GetImmediateASCIILen (op1); - wide1 = 0; - for (int i = 0; i < len1; i++) - buf1[i] = MIST_GetImmediateASCIIChar (op1, i); - p1ptr = buf1; - } else { - JSString *s = JS_VALUE_GET_STRING (op1); - len1 = s->len; - wide1 = s->is_wide_char; - p1ptr = wide1 ? (const void *)s->u.str16 : (const void *)s->u.str8; - } - - /* Extract op2 */ - if (MIST_IsImmediateASCII (op2)) { - len2 = MIST_GetImmediateASCIILen (op2); - wide2 = 0; - for (int i = 0; i < len2; i++) - buf2[i] = MIST_GetImmediateASCIIChar (op2, i); - p2ptr = buf2; - } else { - JSString *s = JS_VALUE_GET_STRING (op2); - len2 = s->len; - wide2 = s->is_wide_char; - p2ptr = wide2 ? (const void *)s->u.str16 : (const void *)s->u.str8; - } - - int is_wide = wide1 || wide2; + int len1 = js_string_value_len (op1); + int len2 = js_string_value_len (op2); + int new_len = len1 + len2; JSValue ret_val = JS_NULL; - if (!is_wide && (len1 + len2 <= MIST_ASCII_MAX_LEN)) { + /* Try to create immediate ASCII if short enough and all ASCII */ + if (new_len <= MIST_ASCII_MAX_LEN) { char buf[8]; - if (len1 > 0) memcpy (buf, p1ptr, len1); - if (len2 > 0) memcpy (buf + len1, p2ptr, len2); - ret_val = MIST_TryNewImmediateASCII (buf, len1 + len2); + BOOL all_ascii = TRUE; + for (int i = 0; i < len1 && all_ascii; i++) { + uint32_t c = js_string_value_get (op1, i); + if (c >= 0x80) all_ascii = FALSE; + else buf[i] = (char)c; + } + for (int i = 0; i < len2 && all_ascii; i++) { + uint32_t c = js_string_value_get (op2, i); + if (c >= 0x80) all_ascii = FALSE; + else buf[len1 + i] = (char)c; + } + if (all_ascii) { + ret_val = MIST_TryNewImmediateASCII (buf, new_len); + } } if (JS_IsNull (ret_val)) { - JSString *p = js_alloc_string (ctx, len1 + len2, is_wide); + JSString *p = js_alloc_string (ctx, new_len); if (!p) { JS_FreeValue (ctx, op1); JS_FreeValue (ctx, op2); return JS_EXCEPTION; } - if (is_wide) { - if (wide1) - memcpy (p->u.str16, p1ptr, len1 * 2); - else - for (int i = 0; i < len1; i++) - p->u.str16[i] = ((const uint8_t *)p1ptr)[i]; - - if (wide2) - memcpy (p->u.str16 + len1, p2ptr, len2 * 2); - else - for (int i = 0; i < len2; i++) - p->u.str16[len1 + i] = ((const uint8_t *)p2ptr)[i]; - } else { - memcpy (p->u.str8, p1ptr, len1); - memcpy (p->u.str8 + len1, p2ptr, len2); + /* Copy characters using string_put/get */ + for (int i = 0; i < len1; i++) { + string_put (p, i, js_string_value_get (op1, i)); + } + for (int i = 0; i < len2; i++) { + string_put (p, len1 + i, js_string_value_get (op2, i)); } ret_val = JS_MKPTR (JS_TAG_STRING, p); } @@ -3771,18 +3981,17 @@ static JSObject *get_proto_obj (JSValue proto_val) { /* WARNING: proto must be an object or JS_NULL */ JSValue JS_NewObjectProtoClass (JSContext *ctx, JSValue proto_val, JSClassID class_id) { - JSShape *sh; - JSObject *proto; + JSRecord *rec = js_new_record (ctx, 0); + if (!rec) return JS_EXCEPTION; - proto = get_proto_obj (proto_val); - sh = find_hashed_shape_proto (ctx->rt, proto); - if (likely (sh)) { - sh = js_dup_shape (sh); - } else { - sh = js_new_shape (ctx, proto); - if (!sh) return JS_EXCEPTION; + /* Set prototype if provided */ + if (JS_VALUE_GET_TAG (proto_val) == JS_TAG_OBJECT + && JS_VALUE_IS_RECORD (proto_val)) { + rec->proto = JS_VALUE_GET_RECORD (proto_val); } - return JS_NewObjectFromShape (ctx, sh, class_id); + + (void)class_id; /* class_id stored elsewhere or not needed for records */ + return JS_MKPTR (JS_TAG_OBJECT, rec); } JSValue JS_NewObjectClass (JSContext *ctx, int class_id) { @@ -3861,9 +4070,15 @@ static JSValue js_get_function_name (JSContext *ctx, JSValue fn) { // TODO: needs reworked static int js_method_set_properties (JSContext *ctx, JSValue func_obj, JSValue name, int flags, JSValue home_obj) { + (void)ctx; + (void)flags; + (void)home_obj; if (JS_VALUE_GET_TAG (func_obj) != JS_TAG_FUNCTION) return -1; JSFunction *f = JS_VALUE_GET_FUNCTION (func_obj); - f->name = JS_DupAtom (ctx, name); + /* name is now const char* - convert from JSValue if string */ + if (JS_IsString (name)) { + f->name = JS_ToCString (ctx, name); + } return 0; } @@ -3874,6 +4089,7 @@ JS_NewCFunction3 (JSContext *ctx, JSCFunction *func, const char *name, JSValue proto_val) { JSValue func_obj; JSFunction *f; + (void)proto_val; func_obj = js_new_function (ctx, JS_FUNC_KIND_C); if (JS_IsException (func_obj)) return func_obj; @@ -3883,13 +4099,7 @@ JS_NewCFunction3 (JSContext *ctx, JSCFunction *func, const char *name, f->u.cfunc.cproto = cproto; f->u.cfunc.magic = magic; f->length = length; - if (!name) name = ""; - name_atom = JS_NewAtom (ctx, name); - if (name_atom == JS_ATOM_NULL) { - JS_FreeValue (ctx, func_obj); - return JS_EXCEPTION; - } - f->name = name_atom; + f->name = name ? name : ""; return func_obj; } @@ -3959,16 +4169,7 @@ JS_NewCFunctionData (JSContext *ctx, JSCFunctionData *func, int length, return func_obj; } -static void -free_property (JSRuntime *rt, JSProperty *pr, int prop_flags) { - if (unlikely (prop_flags & JS_PROP_TMASK)) { - if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - free_var_ref (rt, pr->u.var_ref); - } - } else { - JS_FreeValueRT (rt, pr->u.value); - } -} +/* free_property is defined earlier as a stub since shapes are removed */ static void free_var_ref (JSRuntime *rt, JSVarRef *var_ref) { if (var_ref) { @@ -4253,28 +4454,14 @@ mark_function_children_decref (JSRuntime *rt, JSFunction *func) { static void free_object (JSRuntime *rt, JSObject *p) { - int i; JSClassFinalizer *finalizer; - JSShape *sh; - JSShapeProperty *pr; assert (p->header.gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT); p->free_mark = 1; /* used to tell the object is invalid when freeing cycles */ - /* free all the fields */ - sh = p->shape; - pr = get_shape_prop (sh); - for (i = 0; i < sh->prop_count; i++) { - free_property (rt, &p->prop[i], pr->flags); - pr++; - } - js_free_rt (rt, p->prop); - /* as an optimization we destroy the shape immediately without - putting it in gc_zero_ref_count_list */ - js_free_shape (rt, sh); - /* Free object-key map entries */ + /* Free object-key map entries (identity-keyed properties) */ if (p->objkey_tab) { for (uint32_t j = 0; j < p->objkey_size * 2; j += 2) { int tag = JS_VALUE_GET_TAG (p->objkey_tab[j]); @@ -4287,8 +4474,6 @@ free_object (JSRuntime *rt, JSObject *p) { } /* fail safe */ - p->shape = NULL; - p->prop = NULL; p->objkey_tab = NULL; finalizer = rt->class_array[p->class_id].finalizer; @@ -4366,14 +4551,11 @@ __JS_FreeValueRT (JSRuntime *rt, JSValue v) { switch (tag) { case JS_TAG_STRING: { JSString *p = JS_VALUE_GET_STRING (v); - if (p->atom_type) { - JS_FreeAtomStruct (rt, p); - } else { + /* atom_type removed - strings are no longer atoms */ #ifdef DUMP_LEAKS - list_del (&p->link); + list_del (&p->link); #endif - js_free_rt (rt, p); - } + js_free_rt (rt, p); } break; case JS_TAG_STRING_IMM: /* Immediate strings do not need freeing */ @@ -4434,36 +4616,15 @@ mark_children (JSRuntime *rt, JSGCObjectHeader *gp, JS_MarkFunc *mark_func) { switch (gp->gc_obj_type) { case JS_GC_OBJ_TYPE_JS_OBJECT: { JSObject *p = (JSObject *)gp; - JSShapeProperty *prs; - JSShape *sh; - int i; - sh = p->shape; - mark_func (rt, &sh->header); - /* mark all the fields */ - prs = get_shape_prop (sh); - for (i = 0; i < sh->prop_count; i++) { - JSProperty *pr = &p->prop[i]; - if (prs->atom != JS_ATOM_NULL) { - if (prs->flags & JS_PROP_TMASK) { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - /* Note: the tag does not matter - provided it is a GC object */ - mark_func (rt, &pr->u.var_ref->header); - } - } else { - JS_MarkValue (rt, pr->u.value, mark_func); - } - } - prs++; - } + /* Class-specific marking */ if (p->class_id != JS_CLASS_OBJECT) { JSClassGCMark *gc_mark; gc_mark = rt->class_array[p->class_id].gc_mark; if (gc_mark) gc_mark (rt, JS_MKPTR (JS_TAG_OBJECT, p), mark_func); } - /* Mark object-key map entries */ + /* Mark object-key map entries (identity-keyed properties) */ if (p->objkey_tab) { for (uint32_t j = 0; j < p->objkey_size * 2; j += 2) { int tag = JS_VALUE_GET_TAG (p->objkey_tab[j]); @@ -4495,10 +4656,7 @@ mark_children (JSRuntime *rt, JSGCObjectHeader *gp, JS_MarkFunc *mark_func) { JS_MarkValue (rt, *var_ref->pvalue, mark_func); } } break; - case JS_GC_OBJ_TYPE_SHAPE: { - JSShape *sh = (JSShape *)gp; - if (sh->proto != NULL) { mark_func (rt, &sh->proto->header); } - } break; + /* JS_GC_OBJ_TYPE_SHAPE removed - shapes no longer used */ case JS_GC_OBJ_TYPE_JS_CONTEXT: { JSContext *ctx = (JSContext *)gp; JS_MarkContext (rt, ctx, mark_func); @@ -4515,7 +4673,9 @@ mark_children (JSRuntime *rt, JSGCObjectHeader *gp, JS_MarkFunc *mark_func) { uint32_t mask = (uint32_t)objhdr_cap56 (rec->mist_hdr); uint32_t i; for (i = 1; i <= mask; i++) { - if (rec->tab[i].key != K_EMPTY && rec->tab[i].key != K_TOMB) { + JSValue k = rec->tab[i].key; + if (!rec_key_is_empty (k) && !rec_key_is_tomb (k)) { + JS_MarkValue (rt, k, mark_func); JS_MarkValue (rt, rec->tab[i].val, mark_func); } } @@ -4631,15 +4791,6 @@ JS_MarkContextDecref (JSRuntime *rt, JSContext *ctx) { } JS_MarkValueEdge (rt, ctx->array_ctor, &ctx->header, "ctx.array_ctor"); JS_MarkValueEdge (rt, ctx->regexp_ctor, &ctx->header, "ctx.regexp_ctor"); - - if (ctx->array_shape) { -#ifdef RC_TRACE - gc_decref_child_dbg (rt, &ctx->header, "ctx.array_shape", 0, -1, - &ctx->array_shape->header, __FILE__, __LINE__); -#else - gc_decref_child (rt, &ctx->array_shape->header); -#endif - } } static void @@ -4647,40 +4798,8 @@ mark_children_decref (JSRuntime *rt, JSGCObjectHeader *gp) { switch (gp->gc_obj_type) { case JS_GC_OBJ_TYPE_JS_OBJECT: { JSObject *p = (JSObject *)gp; - JSShapeProperty *prs; - JSShape *sh; - int i; - sh = p->shape; -#ifdef RC_TRACE - gc_decref_child_dbg (rt, gp, "obj.shape", 0, -1, &sh->header, __FILE__, - __LINE__); -#else - gc_decref_child (rt, &sh->header); -#endif - prs = get_shape_prop (sh); - for (i = 0; i < sh->prop_count; i++) { - JSProperty *pr = &p->prop[i]; - JSAtom prop_atom = prs->atom; - int prop_index = i; - if (prop_atom != JS_ATOM_NULL) { - if (prs->flags & JS_PROP_TMASK) { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { -#ifdef RC_TRACE - gc_decref_child_dbg (rt, gp, "obj.prop.var_ref", prop_atom, - prop_index, &pr->u.var_ref->header, __FILE__, - __LINE__); -#else - gc_decref_child (rt, &pr->u.var_ref->header); -#endif - } - } else { - JS_MarkValueEdgeEx (rt, pr->u.value, gp, "obj.prop.value", prop_atom, - prop_index); - } - } - prs++; - } + /* Class-specific decref */ if (p->class_id != JS_CLASS_OBJECT) { JSClassGCMark *gc_mark; gc_mark = rt->class_array[p->class_id].gc_mark; @@ -4720,17 +4839,7 @@ mark_children_decref (JSRuntime *rt, JSGCObjectHeader *gp) { JS_MarkValueEdge (rt, *var_ref->pvalue, gp, "var_ref.value"); } } break; - case JS_GC_OBJ_TYPE_SHAPE: { - JSShape *sh = (JSShape *)gp; - if (sh->proto != NULL) { -#ifdef RC_TRACE - gc_decref_child_dbg (rt, gp, "shape.proto", 0, -1, &sh->proto->header, - __FILE__, __LINE__); -#else - gc_decref_child (rt, &sh->proto->header); -#endif - } - } break; + /* JS_GC_OBJ_TYPE_SHAPE removed - shapes no longer used */ case JS_GC_OBJ_TYPE_JS_CONTEXT: { JSContext *ctx = (JSContext *)gp; JS_MarkContextDecref (rt, ctx); @@ -4747,8 +4856,10 @@ mark_children_decref (JSRuntime *rt, JSGCObjectHeader *gp) { uint32_t mask = (uint32_t)objhdr_cap56 (rec->mist_hdr); uint32_t i; for (i = 1; i <= mask; i++) { - if (rec->tab[i].key != K_EMPTY && rec->tab[i].key != K_TOMB) { - JS_MarkValueEdge (rt, rec->tab[i].val, gp, "record.prop"); + JSValue k = rec->tab[i].key; + if (!rec_key_is_empty (k) && !rec_key_is_tomb (k)) { + JS_MarkValueEdge (rt, k, gp, "record.key"); + JS_MarkValueEdge (rt, rec->tab[i].val, gp, "record.val"); } } if (rec->proto) { gc_decref_child (rt, &rec->proto->header); } @@ -4920,13 +5031,11 @@ static void compute_value_size (JSValue val, JSMemoryUsage_helper *hp); static void compute_jsstring_size (JSString *str, JSMemoryUsage_helper *hp) { - if (!str->atom_type) { /* atoms are handled separately */ - double s_ref_count = str->header.ref_count; - hp->str_count += 1 / s_ref_count; - hp->str_size += ((sizeof (*str) + (str->len << str->is_wide_char) + 1 - - str->is_wide_char) - / s_ref_count); - } + /* UTF-32 packs 2 chars per 64-bit word */ + double s_ref_count = str->header.ref_count; + size_t data_words = (str->len + 1) / 2; + hp->str_count += 1 / s_ref_count; + hp->str_size += (sizeof (*str) + data_words * sizeof (uint64_t)) / s_ref_count; } static void @@ -4980,7 +5089,6 @@ compute_value_size (JSValue val, JSMemoryUsage_helper *hp) { void JS_ComputeMemoryUsage (JSRuntime *rt, JSMemoryUsage *s) { struct list_head *el; - int i; JSMemoryUsage_helper mem = { 0 }, *hp = &mem; memset (s, 0, sizeof (*s)); @@ -4994,56 +5102,35 @@ JS_ComputeMemoryUsage (JSRuntime *rt, JSMemoryUsage *s) { list_for_each (el, &rt->context_list) { JSContext *ctx = list_entry (el, JSContext, link); - JSShape *sh = ctx->array_shape; s->memory_used_count += 2; /* ctx + ctx->class_proto */ s->memory_used_size += sizeof (JSContext) + sizeof (JSValue) * rt->class_count; s->binary_object_count += ctx->binary_object_count; s->binary_object_size += ctx->binary_object_size; - - /* the hashed shapes are counted separately */ - if (sh && !sh->is_hashed) { - int hash_size = sh->prop_hash_mask + 1; - s->shape_count++; - s->shape_size += get_shape_size (hash_size, sh->prop_size); - } } list_for_each (el, &rt->gc_obj_list) { JSGCObjectHeader *gp = list_entry (el, JSGCObjectHeader, link); JSObject *p; - JSShape *sh; - JSShapeProperty *prs; /* XXX: could count the other GC object types too */ if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) { compute_bytecode_size ((JSFunctionBytecode *)gp, hp); continue; + } else if (gp->gc_obj_type == JS_GC_OBJ_TYPE_RECORD) { + /* Count record properties */ + JSRecord *rec = (JSRecord *)gp; + uint32_t mask = (uint32_t)objhdr_cap56 (rec->mist_hdr); + s->obj_count++; + s->prop_count += rec->len; + /* Estimate record table size */ + s->prop_size += (mask + 1) * sizeof (JSRecordEntry); + continue; } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) { continue; } p = (JSObject *)gp; - sh = p->shape; s->obj_count++; - if (p->prop) { - s->memory_used_count++; - s->prop_size += sh->prop_size * sizeof (*p->prop); - s->prop_count += sh->prop_count; - prs = get_shape_prop (sh); - for (i = 0; i < sh->prop_count; i++) { - JSProperty *pr = &p->prop[i]; - if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) { - compute_value_size (pr->u.value, hp); - } - prs++; - } - } - /* the hashed shapes are counted separately */ - if (!sh->is_hashed) { - int hash_size = sh->prop_hash_mask + 1; - s->shape_count++; - s->shape_size += get_shape_size (hash_size, sh->prop_size); - } switch (p->class_id) { case JS_CLASS_REGEXP: /* u.regexp */ @@ -5058,23 +5145,10 @@ JS_ComputeMemoryUsage (JSRuntime *rt, JSMemoryUsage *s) { } s->obj_size += s->obj_count * sizeof (JSObject); - /* hashed shapes */ - s->memory_used_count++; /* rt->shape_hash */ - s->memory_used_size += sizeof (rt->shape_hash[0]) * rt->shape_hash_size; - for (i = 0; i < rt->shape_hash_size; i++) { - JSShape *sh; - for (sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { - int hash_size = sh->prop_hash_mask + 1; - s->shape_count++; - s->shape_size += get_shape_size (hash_size, sh->prop_size); - } - } + /* shapes removed - just zero out the stats */ + s->shape_count = 0; + s->shape_size = 0; - /* atoms */ - s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */ - s->atom_count = rt->atom_count; - s->atom_size = sizeof (rt->atom_array[0]) * rt->atom_size - + sizeof (rt->atom_hash[0]) /* free atoms removed */; s->str_count = round (mem.str_count); s->str_size = round (mem.str_size); s->js_func_count = mem.js_func_count; @@ -5082,11 +5156,11 @@ JS_ComputeMemoryUsage (JSRuntime *rt, JSMemoryUsage *s) { s->js_func_code_size = mem.js_func_code_size; s->js_func_pc2line_count = mem.js_func_pc2line_count; s->js_func_pc2line_size = mem.js_func_pc2line_size; - s->memory_used_count += round (mem.memory_used_count) + s->atom_count - + s->str_count + s->obj_count + s->shape_count + s->memory_used_count += round (mem.memory_used_count) + + s->str_count + s->obj_count + s->js_func_count + s->js_func_pc2line_count; - s->memory_used_size += s->atom_size + s->str_size + s->obj_size - + s->prop_size + s->shape_size + s->js_func_size + s->memory_used_size += s->str_size + s->obj_size + + s->prop_size + s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size; } @@ -5140,10 +5214,10 @@ JS_DumpMemoryUsage (FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) { fprintf (fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none"); for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) { if (obj_classes[class_id] && class_id < rt->class_count) { - char buf[ATOM_GET_STR_BUF_SIZE]; + /* class_name is now const char* directly */ + const char *name = rt->class_array[class_id].class_name; fprintf (fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id, - JS_AtomGetStrRT (rt, buf, sizeof (buf), - rt->class_array[class_id].class_name)); + name ? name : "?"); } } if (obj_classes[JS_CLASS_INIT_COUNT]) @@ -5166,11 +5240,7 @@ JS_DumpMemoryUsage (FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) { MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) / s->memory_used_count)); } - if (s->atom_count) { - fprintf (fp, "%-20s %8" PRId64 " %8" PRId64 " (%0.1f per atom)\n", - "atoms", s->atom_count, s->atom_size, - (double)s->atom_size / s->atom_count); - } + /* atoms removed - no longer tracked */ if (s->str_count) { fprintf (fp, "%-20s %8" PRId64 " %8" PRId64 " (%0.1f per string)\n", "strings", s->str_count, s->str_size, @@ -5357,26 +5427,23 @@ fail: /* return a string property without executing arbitrary JS code (used when dumping the stack trace or in debug print). */ -static const char * get_prop_string (JSContext *ctx, JSValue obj, JSAtom prop) { - JSObject *p; - JSProperty *pr; - JSShapeProperty *prs; - JSValue val; - +static const char * +get_prop_string (JSContext *ctx, JSValue obj, JSValue prop) { if (JS_VALUE_GET_TAG (obj) != JS_TAG_OBJECT) return NULL; - p = JS_VALUE_GET_OBJ (obj); - prs = find_own_property (&pr, p, prop); - if (!prs) { + JSRecord *rec = (JSRecord *)JS_VALUE_GET_OBJ (obj); + + int slot = rec_find_slot (rec, prop); + if (slot <= 0) { /* we look at one level in the prototype to handle the 'name' field of the Error objects */ - p = p->shape->proto; - if (!p) return NULL; - prs = find_own_property (&pr, p, prop); - if (!prs) return NULL; + JSRecord *proto = rec->proto; + if (!proto) return NULL; + slot = rec_find_slot (proto, prop); + if (slot <= 0) return NULL; + rec = proto; } - if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) return NULL; - val = pr->u.value; + JSValue val = rec->tab[slot].val; if (JS_VALUE_GET_TAG (val) != JS_TAG_STRING) return NULL; return JS_ToCString (ctx, val); } @@ -5386,15 +5453,16 @@ static const char * get_prop_string (JSContext *ctx, JSValue obj, JSAtom prop) { string. */ static const char * get_func_name (JSContext *ctx, JSValue func) { - JSProperty *pr; - JSShapeProperty *prs; - JSValue val; - if (JS_VALUE_GET_TAG (func) != JS_TAG_OBJECT) return NULL; - prs = find_own_property (&pr, JS_VALUE_GET_OBJ (func), JS_ATOM_name); - if (!prs) return NULL; - if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) return NULL; - val = pr->u.value; + JSRecord *rec = (JSRecord *)JS_VALUE_GET_OBJ (func); + + /* Create "name" key as immediate ASCII string */ + JSValue name_key = MIST_TryNewImmediateASCII ("name", 4); + + int slot = rec_find_slot (rec, name_key); + if (slot <= 0) return NULL; + + JSValue val = rec->tab[slot].val; if (JS_VALUE_GET_TAG (val) != JS_TAG_STRING) return NULL; return JS_ToCString (ctx, val); } @@ -5459,7 +5527,7 @@ build_backtrace (JSContext *ctx, JSValue error_obj, const char *filename, if (b->has_debug) { line_num1 = find_line_num (ctx, b, sf->cur_pc - b->byte_code_buf - 1, &col_num1); - atom_str = JS_AtomToCString (ctx, b->debug.filename); + atom_str = JS_ToCString (ctx, b->debug.filename); dbuf_printf (&dbuf, " (%s", atom_str ? atom_str : ""); JS_FreeCString (ctx, atom_str); if (line_num1 != 0) @@ -5490,7 +5558,11 @@ is_backtrace_needed (JSContext *ctx, JSValue obj) { if (JS_VALUE_GET_TAG (obj) != JS_TAG_OBJECT) return FALSE; p = JS_VALUE_GET_OBJ (obj); if (p->class_id != JS_CLASS_ERROR) return FALSE; - if (find_own_property1 (p, JS_ATOM_stack)) return FALSE; + /* Check if "stack" property already exists */ + JSValue stack_key = MIST_TryNewImmediateASCII ("stack", 5); + JSRecord *rec = (JSRecord *)p; + int slot = rec_find_slot (rec, stack_key); + if (slot > 0) return FALSE; return TRUE; } @@ -5643,29 +5715,28 @@ static JSValue JS_ThrowReferenceErrorUninitialized (JSContext *ctx, JSValue name char buf[ATOM_GET_STR_BUF_SIZE]; return JS_ThrowReferenceError ( ctx, "%s is not initialized", - name == JS_ATOM_NULL ? "lexical variable" - : JS_AtomGetStr (ctx, buf, sizeof (buf), name)); + JS_IsNull (name) ? "lexical variable" + : JS_KeyGetStr (ctx, buf, sizeof (buf), name)); } static JSValue JS_ThrowReferenceErrorUninitialized2 (JSContext *ctx, JSFunctionBytecode *b, int idx, BOOL is_ref) { - JSValue atom = JS_ATOM_NULL; + JSValue name = JS_NULL; if (is_ref) { - atom = b->closure_var[idx].var_name; + name = b->closure_var[idx].var_name; } else { /* not present if the function is stripped and contains no eval() */ - if (b->vardefs) atom = b->vardefs[b->arg_count + idx].var_name; + if (b->vardefs) name = b->vardefs[b->arg_count + idx].var_name; } - return JS_ThrowReferenceErrorUninitialized (ctx, atom); + return JS_ThrowReferenceErrorUninitialized (ctx, name); } static JSValue JS_ThrowTypeErrorInvalidClass (JSContext *ctx, int class_id) { JSRuntime *rt = ctx->rt; - JSValue name; - name = rt->class_array[class_id].class_name; - return JS_ThrowTypeErrorAtom (ctx, "%s object expected", name); + const char *name = rt->class_array[class_id].class_name; + return JS_ThrowTypeError (ctx, "%s object expected", name ? name : "unknown"); } static void @@ -5703,7 +5774,7 @@ JS_GetPrototype (JSContext *ctx, JSValue obj) { if (JS_VALUE_GET_TAG (obj) == JS_TAG_OBJECT) { JSObject *p; p = JS_VALUE_GET_OBJ (obj); - p = p->shape->proto; + p = JS_OBJ_GET_PROTO (p); if (!p) val = JS_NULL; else @@ -5723,14 +5794,10 @@ JS_GetPrototypeFree (JSContext *ctx, JSValue obj) { return obj1; } -// This is simplified +/* Get property from object using JSRecord-based lookup */ JSValue JS_GetProperty (JSContext *ctx, JSValue obj, JSValue prop) { - JSObject *p; - JSProperty *pr; - JSShapeProperty *prs; - uint32_t tag; + uint32_t tag = JS_VALUE_GET_TAG (obj); - tag = JS_VALUE_GET_TAG (obj); if (unlikely (tag != JS_TAG_OBJECT)) { switch (tag) { case JS_TAG_NULL: @@ -5743,142 +5810,51 @@ JSValue JS_GetProperty (JSContext *ctx, JSValue obj, JSValue prop) { } } - /* Only legacy JSObject has shape-based property lookup. - JSArray and JSRecord use different mechanisms. */ - if (unlikely (js_gc_obj_type (obj) != JS_GC_OBJ_TYPE_JS_OBJECT)) { - if (js_gc_obj_type (obj) == JS_GC_OBJ_TYPE_RECORD) { - JSRecord *rec = JS_VALUE_GET_RECORD (obj); - KeyId key = keyid_from_jsatom (ctx, prop); - JSValue result = rec_get (ctx, rec, key); - keyid_free (ctx->rt, key); - return result; - } - /* For arrays, only handle numeric indices (handled elsewhere). */ - return JS_NULL; - } - - p = JS_VALUE_GET_OBJ (obj); - - for (;;) { - prs = find_own_property (&pr, p, prop); - if (prs) { - /* found */ - if (unlikely (prs->flags & JS_PROP_TMASK)) { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - JSValue val = *pr->u.var_ref->pvalue; - if (unlikely (JS_IsUninitialized (val))) - return JS_ThrowReferenceErrorUninitialized (ctx, prs->atom); - return JS_DupValue (ctx, val); - } - } else { - return JS_DupValue (ctx, pr->u.value); - } - } - p = p->shape->proto; - if (!p) break; - } - if (unlikely (throw_ref_error)) { - return JS_ThrowReferenceErrorNotDefined (ctx, prop); - } else { - return JS_NULL; - } + /* All objects are JSRecords now */ + JSRecord *rec = (JSRecord *)JS_VALUE_GET_OBJ (obj); + return rec_get (ctx, rec, prop); } -/* return < 0 in case if exception, 0 if OK. ptab and its atoms must - be freed by the user. - Simplified: returns only string keys (text keys). All text keys are - enumerable. The 'flags' parameter is kept for compatibility but only - JS_GPN_STRING_MASK and JS_GPN_SYMBOL_MASK are used. */ +/* Forward declaration for js_get_record_keys */ +static int js_get_record_keys (JSContext *ctx, JSValue **ptab, uint32_t *plen, + JSRecord *rec); + +/* Internal helper: get property keys for legacy JSObject (now just records) */ static int __exception -JS_GetOwnPropertyNamesInternal (JSContext *ctx, JSPropertyEnum **ptab, - uint32_t *plen, JSObject *p, int flags) { - int i, j; - JSShape *sh; - JSShapeProperty *prs; - JSPropertyEnum *tab_atom; - JSAtom atom; - uint32_t str_keys_count, atom_count; - JSAtomKindEnum kind; - BOOL want_strings = (flags & JS_GPN_STRING_MASK) != 0; - BOOL want_symbols = (flags & JS_GPN_SYMBOL_MASK) != 0; - - /* clear pointer for consistency in case of failure */ - *ptab = NULL; - *plen = 0; - - /* compute the number of returned properties */ - str_keys_count = 0; - sh = p->shape; - for (i = 0, prs = get_shape_prop (sh); i < sh->prop_count; i++, prs++) { - atom = prs->atom; - if (atom != JS_ATOM_NULL) { - kind = JS_AtomGetKind (ctx, atom); - if ((kind == JS_ATOM_KIND_STRING && want_strings) - || (kind == JS_ATOM_KIND_SYMBOL && want_symbols)) { - str_keys_count++; - } - } - } - - atom_count = str_keys_count; - if (atom_count > INT32_MAX) { - JS_ThrowOutOfMemory (ctx); - return -1; - } - - /* avoid allocating 0 bytes */ - tab_atom = js_malloc (ctx, sizeof (tab_atom[0]) * max_int (atom_count, 1)); - if (!tab_atom) { return -1; } - - j = 0; - sh = p->shape; - for (i = 0, prs = get_shape_prop (sh); i < sh->prop_count; i++, prs++) { - atom = prs->atom; - if (atom != JS_ATOM_NULL) { - kind = JS_AtomGetKind (ctx, atom); - if ((kind == JS_ATOM_KIND_STRING && want_strings) - || (kind == JS_ATOM_KIND_SYMBOL && want_symbols)) { - tab_atom[j].atom = JS_DupAtom (ctx, atom); - tab_atom[j].is_enumerable = TRUE; /* all text keys are enumerable */ - j++; - } - } - } - - assert ((uint32_t)j == atom_count); - - *ptab = tab_atom; - *plen = atom_count; - return 0; +js_get_object_keys (JSContext *ctx, JSValue **ptab, uint32_t *plen, + JSObject *p) { + /* Objects are now records */ + JSRecord *rec = (JSRecord *)p; + return js_get_record_keys (ctx, ptab, plen, rec); } /* Get own property names from a JSRecord. - Only returns text keys (K_TEXT), not record keys (K_REC). */ + Returns JSValue string keys directly (text values), not record-as-key. */ static int -JS_GetRecordOwnPropertyNames (JSContext *ctx, JSPropertyEnum **ptab, - uint32_t *plen, JSRecord *rec) { +js_get_record_keys (JSContext *ctx, JSValue **ptab, uint32_t *plen, + JSRecord *rec) { uint32_t mask = (uint32_t)objhdr_cap56 (rec->mist_hdr); uint32_t count = 0; uint32_t i, j; - JSPropertyEnum *tab; + JSValue *tab; - /* Count text keys */ + /* Count string keys (skip empty/tombstone and non-string keys) */ for (i = 1; i <= mask; i++) { - KeyId k = rec->tab[i].key; - if (k != K_EMPTY && k != K_TOMB && key_is_text (k)) count++; + JSValue k = rec->tab[i].key; + if (!rec_key_is_empty (k) && !rec_key_is_tomb (k) && JS_IsString (k)) + count++; } /* Allocate array */ tab = js_malloc (ctx, sizeof (tab[0]) * max_int (count, 1)); if (!tab) return -1; - /* Fill array */ + /* Fill array with JSValue string keys */ j = 0; for (i = 1; i <= mask; i++) { - KeyId k = rec->tab[i].key; - if (k != K_EMPTY && k != K_TOMB && key_is_text (k)) { - tab[j].atom = JS_DupAtom (ctx, (JSAtom)key_payload (k)); - tab[j].is_enumerable = TRUE; + JSValue k = rec->tab[i].key; + if (!rec_key_is_empty (k) && !rec_key_is_tomb (k) && JS_IsString (k)) { + tab[j] = JS_DupValue (ctx, k); j++; } } @@ -5889,60 +5865,34 @@ JS_GetRecordOwnPropertyNames (JSContext *ctx, JSPropertyEnum **ptab, } int -JS_GetOwnPropertyNames (JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen, - JSValue obj, int flags) { +JS_GetOwnPropertyNames (JSContext *ctx, JSValue **ptab, uint32_t *plen, + JSValue obj) { if (JS_VALUE_GET_TAG (obj) != JS_TAG_OBJECT) { JS_ThrowTypeErrorNotAnObject (ctx); return -1; } - /* Handle records */ - if (js_gc_obj_type (obj) == JS_GC_OBJ_TYPE_RECORD) { - return JS_GetRecordOwnPropertyNames (ctx, ptab, plen, - JS_VALUE_GET_RECORD (obj)); - } - - return JS_GetOwnPropertyNamesInternal (ctx, ptab, plen, - JS_VALUE_GET_OBJ (obj), flags); + /* All objects are now records */ + JSRecord *rec = (JSRecord *)JS_VALUE_GET_OBJ (obj); + return js_get_record_keys (ctx, ptab, plen, rec); } /* Return -1 if exception, FALSE if the property does not exist, TRUE if it exists. If TRUE is - returned, the property descriptor 'desc' is filled present. */ + returned, the property descriptor 'desc' is filled present. + Now uses JSRecord-based lookup. */ static int JS_GetOwnPropertyInternal (JSContext *ctx, JSPropertyDescriptor *desc, - JSObject *p, JSAtom prop) { - JSShapeProperty *prs; - JSProperty *pr; + JSObject *p, JSValue prop) { + JSRecord *rec = (JSRecord *)p; + int slot = rec_find_slot (rec, prop); -retry: - prs = find_own_property (&pr, p, prop); - if (prs) { + if (slot > 0) { if (desc) { - desc->flags = prs->flags; + desc->flags = 0; /* All record properties are writable/enumerable */ desc->getter = JS_NULL; desc->setter = JS_NULL; - desc->value = JS_NULL; - if (unlikely (prs->flags & JS_PROP_TMASK)) { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - JSValue val = *pr->u.var_ref->pvalue; - if (unlikely (JS_IsUninitialized (val))) { - JS_ThrowReferenceErrorUninitialized (ctx, prs->atom); - return -1; - } - desc->value = JS_DupValue (ctx, val); - } - } else { - desc->value = JS_DupValue (ctx, pr->u.value); - } - } else { - /* for consistency, send the exception even if desc is NULL */ - if (unlikely ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) { - if (unlikely (JS_IsUninitialized (*pr->u.var_ref->pvalue))) { - JS_ThrowReferenceErrorUninitialized (ctx, prs->atom); - return -1; - } - } + desc->value = JS_DupValue (ctx, rec->tab[slot].val); } return TRUE; } @@ -5970,7 +5920,7 @@ int JS_HasProperty (JSContext *ctx, JSValue obj, JSAtom prop) { ret = JS_GetOwnPropertyInternal (ctx, NULL, p, prop); JS_FreeValue (ctx, JS_MKPTR (JS_TAG_OBJECT, p)); if (ret != 0) return ret; - p = p->shape->proto; + p = JS_OBJ_GET_PROTO (p); if (!p) break; } return FALSE; @@ -6106,58 +6056,30 @@ JS_GetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop) { return ret; } -/* Simplified delete_property - no JS_PROP_CONFIGURABLE check. +/* Simplified delete_property using JSRecord. Deletion fails only if object is stone. */ -static int delete_property (JSContext *ctx, JSObject *p, JSValue atom) { - JSProperty *pr1; - uint32_t lpr_idx; - intptr_t h, h1; +static int delete_property (JSContext *ctx, JSObject *p, JSValue key) { + JSRecord *rec = (JSRecord *)p; /* Cannot delete from a stone object */ if (p->stone) return FALSE; - sh = p->shape; - h1 = atom & sh->prop_hash_mask; - h = prop_hash_end (sh)[-h1 - 1]; - prop = get_shape_prop (sh); - lpr = NULL; - lpr_idx = 0; /* prevent warning */ - while (h != 0) { - pr = &prop[h - 1]; - if (likely (pr->atom == atom)) { - /* found - delete it */ - /* realloc the shape if needed */ - if (lpr) lpr_idx = lpr - get_shape_prop (sh); - if (js_shape_prepare_update (ctx, p, &pr)) return -1; - sh = p->shape; - /* remove property */ - if (lpr) { - lpr = get_shape_prop (sh) + lpr_idx; - lpr->hash_next = pr->hash_next; - } else { - prop_hash_end (sh)[-h1 - 1] = pr->hash_next; - } - sh->deleted_prop_count++; - /* free the entry */ - pr1 = &p->prop[h - 1]; - free_property (ctx->rt, pr1, pr->flags); - JS_FreeAtom (ctx, pr->atom); - /* put default values */ - pr->flags = 0; - pr->atom = JS_ATOM_NULL; - pr1->u.value = JS_NULL; - - /* compact the properties if too many deleted properties */ - if (sh->deleted_prop_count >= 8 - && sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) { - compact_properties (ctx, p); - } - return TRUE; - } - lpr = pr; - h = pr->hash_next; + int slot = rec_find_slot (rec, key); + if (slot < 0) { + /* not found - success (nothing to delete) */ + return TRUE; } - /* not found - success (nothing to delete) */ + + /* Free key and value */ + JS_FreeValue (ctx, rec->tab[slot].key); + JS_FreeValue (ctx, rec->tab[slot].val); + + /* Mark as tombstone */ + rec->tab[slot].key = JS_EXCEPTION; /* tombstone marker */ + rec->tab[slot].val = JS_NULL; + rec->len--; + rec->tombs++; + return TRUE; } @@ -6168,63 +6090,30 @@ js_free_desc (JSContext *ctx, JSPropertyDescriptor *desc) { JS_FreeValue (ctx, desc->value); } -int JS_SetProperty (JSContext *ctx, JSValue this_obj, JSAtom prop, - JSValue val) { - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - uint32_t tag; +int JS_SetProperty (JSContext *ctx, JSValue this_obj, JSValue prop, + JSValue val) { + uint32_t tag = JS_VALUE_GET_TAG (this_obj); - tag = JS_VALUE_GET_TAG (this_obj); if (tag != JS_TAG_OBJECT) { JS_FreeValue (ctx, val); if (tag == JS_TAG_NULL) { - JS_ThrowTypeErrorAtom (ctx, "cannot set property '%s' of null", prop); + JS_ThrowTypeError (ctx, "cannot set property of null"); } else { - JS_ThrowTypeErrorAtom (ctx, "cannot set property '%s' on a primitive", - prop); + JS_ThrowTypeError (ctx, "cannot set property on a primitive"); } return -1; } - /* Handle records separately */ - if (js_gc_obj_type (this_obj) == JS_GC_OBJ_TYPE_RECORD) { - JSRecord *rec = JS_VALUE_GET_RECORD (this_obj); - KeyId key = keyid_from_jsatom (ctx, prop); - int ret = rec_set_own (ctx, rec, key, val); - keyid_free (ctx->rt, key); - return ret; - } + /* All objects are now records - use record set */ + JSRecord *rec = (JSRecord *)JS_VALUE_GET_OBJ (this_obj); - p = JS_VALUE_GET_OBJ (this_obj); - - if (unlikely (p->stone)) { - /* Stone objects cannot be modified at all */ + if (unlikely (((JSObject *)rec)->stone)) { JS_FreeValue (ctx, val); JS_ThrowTypeError (ctx, "object is stone"); return -1; } - /* Object is not stone - find or create property */ - prs = find_own_property (&pr, p, prop); - if (prs) { - /* Property exists - update it */ - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - set_value (ctx, pr->u.var_ref->pvalue, val); - return TRUE; - } - set_value (ctx, &pr->u.value, val); - return TRUE; - } - - /* Add new property */ - pr = add_property (ctx, p, prop, 0); - if (unlikely (!pr)) { - JS_FreeValue (ctx, val); - return -1; - } - pr->u.value = val; - return TRUE; + return rec_set_own (ctx, rec, prop, val); } int JS_SetPropertyUint32 (JSContext *ctx, JSValue this_obj, uint32_t idx, @@ -6249,16 +6138,22 @@ int JS_SetPropertyInt64 (JSContext *ctx, JSValue this_obj, int64_t idx, } int JS_SetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop, JSValue val) { - JSValue vprop; - int ret; - atom = JS_NewAtom (ctx, prop); - if (atom == JS_ATOM_NULL) { + /* Create JSValue key from string - try immediate ASCII first */ + int len = strlen (prop); + JSValue key; + if (len <= MIST_ASCII_MAX_LEN) { + key = MIST_TryNewImmediateASCII (prop, len); + if (JS_IsNull (key)) { + key = js_new_string8_len (ctx, prop, len); + } + } else { + key = js_new_string8_len (ctx, prop, len); + } + if (JS_IsException (key)) { JS_FreeValue (ctx, val); return -1; } - ret = JS_SetPropertyInternal (ctx, this_obj, atom, val); - JS_FreeAtom (ctx, atom); - return ret; + return JS_SetProperty (ctx, this_obj, key, val); } /* Property access with JSValue key - supports object keys directly */ @@ -6349,71 +6244,35 @@ JS_DeletePropertyKey (JSContext *ctx, JSValue obj, JSValue key) { Note: makes assumption about the bit pattern of the flags */ -/* ensure that the shape can be safely modified */ -static int -js_shape_prepare_update (JSContext *ctx, JSObject *p, JSShapeProperty **pprs) { - JSShape *sh; - uint32_t idx = 0; /* prevent warning */ - - sh = p->shape; - if (sh->is_hashed) { - if (sh->header.ref_count != 1) { - if (pprs) idx = *pprs - get_shape_prop (sh); - /* clone the shape (the resulting one is no longer hashed) */ - sh = js_clone_shape (ctx, sh); - if (!sh) return -1; - js_free_shape (ctx->rt, p->shape); - p->shape = sh; - if (pprs) *pprs = get_shape_prop (sh) + idx; - } else { - js_shape_hash_unlink (ctx->rt, sh); - sh->is_hashed = FALSE; - } - } - return 0; -} - /* return TRUE if 'obj' has a non empty 'name' string */ static BOOL js_object_has_name (JSContext *ctx, JSValue obj) { - JSProperty *pr; - JSShapeProperty *prs; - JSValue val; - JSString *p; - - prs = find_own_property (&pr, JS_VALUE_GET_OBJ (obj), JS_ATOM_name); - if (!prs) return FALSE; - if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) return TRUE; - val = pr->u.value; + if (JS_VALUE_GET_TAG (obj) != JS_TAG_OBJECT) return FALSE; + JSRecord *rec = (JSRecord *)JS_VALUE_GET_OBJ (obj); + JSValue name_key = MIST_TryNewImmediateASCII ("name", 4); + int slot = rec_find_slot (rec, name_key); + if (slot <= 0) return FALSE; + JSValue val = rec->tab[slot].val; if (JS_VALUE_GET_TAG (val) != JS_TAG_STRING) return TRUE; - p = JS_VALUE_GET_STRING (val); + JSString *p = JS_VALUE_GET_STRING (val); return (p->len != 0); } static int -JS_DefineObjectName (JSContext *ctx, JSValue obj, JSAtom name) { - if (name != JS_ATOM_NULL && JS_IsObject (obj) - && !js_object_has_name (ctx, obj) - && JS_SetPropertyInternal (ctx, obj, JS_ATOM_name, - JS_AtomToString (ctx, name)) - < 0) { - return -1; +JS_DefineObjectName (JSContext *ctx, JSValue obj, JSValue name) { + if (!JS_IsNull (name) && JS_IsObject (obj) && !js_object_has_name (ctx, obj)) { + JSValue name_key = MIST_TryNewImmediateASCII ("name", 4); + if (JS_SetPropertyInternal (ctx, obj, name_key, JS_DupValue (ctx, name)) < 0) + return -1; } return 0; } static int -JS_DefineObjectNameComputed (JSContext *ctx, JSValue obj, - JSValue str) { +JS_DefineObjectNameComputed (JSContext *ctx, JSValue obj, JSValue str) { if (JS_IsObject (obj) && !js_object_has_name (ctx, obj)) { - JSAtom prop; - JSValue name_str; - prop = JS_ValueToAtom (ctx, str); - if (prop == JS_ATOM_NULL) return -1; - name_str = js_get_function_name (ctx, prop); - JS_FreeAtom (ctx, prop); - if (JS_IsException (name_str)) return -1; - if (JS_SetPropertyInternal (ctx, obj, JS_ATOM_name, name_str) < 0) + JSValue name_key = MIST_TryNewImmediateASCII ("name", 4); + if (JS_SetPropertyInternal (ctx, obj, name_key, JS_DupValue (ctx, str)) < 0) return -1; } return 0; @@ -6423,30 +6282,34 @@ JS_DefineObjectNameComputed (JSContext *ctx, JSValue obj, #define DEFINE_GLOBAL_FUNC_VAR (1 << 6) static JSValue -JS_ThrowSyntaxErrorVarRedeclaration (JSContext *ctx, JSAtom prop) { - return JS_ThrowSyntaxErrorAtom (ctx, "redeclaration of '%s'", prop); +JS_ThrowSyntaxErrorVarRedeclaration (JSContext *ctx, JSValue prop) { + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowSyntaxError (ctx, "redeclaration of '%s'", + JS_KeyGetStr (ctx, buf, sizeof (buf), prop)); } /* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */ static int -JS_CheckDefineGlobalVar (JSContext *ctx, JSAtom prop, int flags) { - JSObject *p; - JSShapeProperty *prs; +JS_CheckDefineGlobalVar (JSContext *ctx, JSValue prop, int flags) { + JSRecord *rec; + int slot; - p = JS_VALUE_GET_OBJ (ctx->global_obj); - prs = find_own_property1 (p, prop); + rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_obj); + slot = rec_find_slot (rec, prop); if (flags & DEFINE_GLOBAL_LEX_VAR) { - if (prs) goto fail_redeclaration; + if (slot > 0) goto fail_redeclaration; } else { - if (!prs && p->stone) { - JS_ThrowTypeErrorAtom (ctx, "cannot define variable '%s'", prop); + if (slot <= 0 && ((JSObject *)rec)->stone) { + char buf[ATOM_GET_STR_BUF_SIZE]; + JS_ThrowTypeError (ctx, "cannot define variable '%s'", + JS_KeyGetStr (ctx, buf, sizeof (buf), prop)); return -1; } } /* check if there already is a lexical declaration */ - p = JS_VALUE_GET_OBJ (ctx->global_var_obj); - prs = find_own_property1 (p, prop); - if (prs) { + rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj); + slot = rec_find_slot (rec, prop); + if (slot > 0) { fail_redeclaration: JS_ThrowSyntaxErrorVarRedeclaration (ctx, prop); return -1; @@ -6456,31 +6319,27 @@ JS_CheckDefineGlobalVar (JSContext *ctx, JSAtom prop, int flags) { /* Simplified: def_flags is (0, DEFINE_GLOBAL_LEX_VAR) */ static int -JS_DefineGlobalVar (JSContext *ctx, JSAtom prop, int def_flags) { - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; +JS_DefineGlobalVar (JSContext *ctx, JSValue prop, int def_flags) { + JSRecord *rec; JSValue val; + int slot; if (def_flags & DEFINE_GLOBAL_LEX_VAR) { - p = JS_VALUE_GET_OBJ (ctx->global_var_obj); + rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj); val = JS_UNINITIALIZED; } else { - p = JS_VALUE_GET_OBJ (ctx->global_obj); + rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_obj); val = JS_NULL; } - prs = find_own_property1 (p, prop); - if (prs) return 0; - if (p->stone) return 0; - pr = add_property (ctx, p, prop, 0); - if (unlikely (!pr)) return -1; - pr->u.value = val; - return 0; + slot = rec_find_slot (rec, prop); + if (slot > 0) return 0; /* already defined */ + if (((JSObject *)rec)->stone) return 0; + return rec_set_own (ctx, rec, prop, val); } /* Simplified global function definition */ static int -JS_DefineGlobalFunction (JSContext *ctx, JSAtom prop, JSValue func, +JS_DefineGlobalFunction (JSContext *ctx, JSValue prop, JSValue func, int def_flags) { (void)def_flags; /* JS_SetPropertyInternal consumes the value, so we must dup it */ @@ -6492,42 +6351,42 @@ JS_DefineGlobalFunction (JSContext *ctx, JSAtom prop, JSValue func, } static JSValue -JS_GetGlobalVar (JSContext *ctx, JSAtom prop, BOOL throw_ref_error) { - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; +JS_GetGlobalVar (JSContext *ctx, JSValue prop, BOOL throw_ref_error) { + JSRecord *rec; + int slot; - p = JS_VALUE_GET_OBJ (ctx->global_var_obj); - prs = find_own_property (&pr, p, prop); - if (prs) { - /* XXX: should handle JS_PROP_TMASK properties */ - if (unlikely (JS_IsUninitialized (pr->u.value))) - return JS_ThrowReferenceErrorUninitialized (ctx, prs->atom); - return JS_DupValue (ctx, pr->u.value); + rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj); + slot = rec_find_slot (rec, prop); + if (slot > 0) { + JSValue val = rec->tab[slot].val; + if (unlikely (JS_IsUninitialized (val))) + return JS_ThrowReferenceErrorUninitialized (ctx, prop); + return JS_DupValue (ctx, val); } - return JS_GetPropertyInternal (ctx, ctx->global_obj, prop, ctx->global_obj, - throw_ref_error); + /* Fall back to global object */ + JSValue val = rec_get (ctx, (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_obj), prop); + if (JS_IsNull (val) && throw_ref_error) + return JS_ThrowReferenceErrorNotDefined (ctx, prop); + return val; } /* construct a reference to a global variable */ static int -JS_GetGlobalVarRef (JSContext *ctx, JSAtom prop, JSValue *sp) { - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; +JS_GetGlobalVarRef (JSContext *ctx, JSValue prop, JSValue *sp) { + JSRecord *rec; + int slot; - p = JS_VALUE_GET_OBJ (ctx->global_var_obj); - prs = find_own_property (&pr, p, prop); - if (prs) { - /* Check for uninitialized lexical variable */ - if (unlikely (JS_IsUninitialized (pr->u.value))) { - JS_ThrowReferenceErrorUninitialized (ctx, prs->atom); + rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj); + slot = rec_find_slot (rec, prop); + if (slot > 0) { + JSValue val = rec->tab[slot].val; + if (unlikely (JS_IsUninitialized (val))) { + JS_ThrowReferenceErrorUninitialized (ctx, prop); return -1; } sp[0] = JS_DupValue (ctx, ctx->global_var_obj); } else { - int ret; - ret = JS_HasProperty (ctx, ctx->global_obj, prop); + int ret = JS_HasProperty (ctx, ctx->global_obj, prop); if (ret < 0) return -1; if (ret) { sp[0] = JS_DupValue (ctx, ctx->global_obj); @@ -6535,20 +6394,19 @@ JS_GetGlobalVarRef (JSContext *ctx, JSAtom prop, JSValue *sp) { sp[0] = JS_NULL; } } - sp[1] = JS_AtomToValue (ctx, prop); + sp[1] = JS_DupValue (ctx, prop); return 0; } /* use for strict variable access: test if the variable exists */ static int -JS_CheckGlobalVar (JSContext *ctx, JSAtom prop) { - JSObject *p; - JSShapeProperty *prs; - int ret; +JS_CheckGlobalVar (JSContext *ctx, JSValue prop) { + JSRecord *rec; + int slot, ret; - p = JS_VALUE_GET_OBJ (ctx->global_var_obj); - prs = find_own_property1 (p, prop); - if (prs) { + rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj); + slot = rec_find_slot (rec, prop); + if (slot > 0) { ret = TRUE; } else { ret = JS_HasProperty (ctx, ctx->global_obj, prop); @@ -6562,23 +6420,22 @@ JS_CheckGlobalVar (JSContext *ctx, JSAtom prop) { flag = 2: normal variable write, strict check was done before */ static int -JS_SetGlobalVar (JSContext *ctx, JSAtom prop, JSValue val, int flag) { - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - int flags; +JS_SetGlobalVar (JSContext *ctx, JSValue prop, JSValue val, int flag) { + JSRecord *rec; + int slot; - p = JS_VALUE_GET_OBJ (ctx->global_var_obj); - prs = find_own_property (&pr, p, prop); - if (prs) { + rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj); + slot = rec_find_slot (rec, prop); + if (slot > 0) { if (flag != 1) { - if (unlikely (JS_IsUninitialized (pr->u.value))) { + if (unlikely (JS_IsUninitialized (rec->tab[slot].val))) { JS_FreeValue (ctx, val); - JS_ThrowReferenceErrorUninitialized (ctx, prs->atom); + JS_ThrowReferenceErrorUninitialized (ctx, prop); return -1; } } - set_value (ctx, &pr->u.value, val); + JS_FreeValue (ctx, rec->tab[slot].val); + rec->tab[slot].val = val; return 0; } @@ -6587,16 +6444,14 @@ JS_SetGlobalVar (JSContext *ctx, JSAtom prop, JSValue val, int flag) { /* return -1, FALSE or TRUE */ static int -JS_DeleteGlobalVar (JSContext *ctx, JSAtom prop) { - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - int ret; +JS_DeleteGlobalVar (JSContext *ctx, JSValue prop) { + JSRecord *rec; + int slot, ret; /* 9.1.1.4.7 DeleteBinding ( N ) */ - p = JS_VALUE_GET_OBJ (ctx->global_var_obj); - prs = find_own_property (&pr, p, prop); - if (prs) return FALSE; /* lexical variables cannot be deleted */ + rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj); + slot = rec_find_slot (rec, prop); + if (slot > 0) return FALSE; /* lexical variables cannot be deleted */ ret = JS_HasProperty (ctx, ctx->global_obj, prop); if (ret < 0) return -1; if (ret) { @@ -6607,10 +6462,9 @@ JS_DeleteGlobalVar (JSContext *ctx, JSAtom prop) { } int -JS_DeleteProperty (JSContext *ctx, JSValue obj, JSAtom prop) { - JSValue obj1; - JSObject *p; - int res; +JS_DeleteProperty (JSContext *ctx, JSValue obj, JSValue prop) { + JSRecord *rec; + int slot; /* Arrays do not support property deletion */ if (JS_VALUE_IS_INTRINSIC_ARRAY (obj)) { @@ -6618,15 +6472,29 @@ JS_DeleteProperty (JSContext *ctx, JSValue obj, JSAtom prop) { return -1; } - obj1 = JS_ToObject (ctx, obj); - if (JS_IsException (obj1)) return -1; - p = JS_VALUE_GET_OBJ (obj1); - res = delete_property (ctx, p, prop); - JS_FreeValue (ctx, obj1); - if (res != FALSE) return res; + if (JS_VALUE_GET_TAG (obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject (ctx); + return -1; + } - JS_ThrowTypeError (ctx, "could not delete property"); - return -1; + rec = (JSRecord *)JS_VALUE_GET_OBJ (obj); + if (((JSObject *)rec)->stone) { + JS_ThrowTypeError (ctx, "cannot delete property of stone object"); + return -1; + } + + slot = rec_find_slot (rec, prop); + if (slot > 0) { + /* Delete by marking as tombstone */ + JS_FreeValue (ctx, rec->tab[slot].key); + JS_FreeValue (ctx, rec->tab[slot].val); + rec->tab[slot].key = JS_EXCEPTION; /* tombstone */ + rec->tab[slot].val = JS_NULL; + rec->len--; + rec->tombs++; + return TRUE; + } + return TRUE; /* property not found = deletion succeeded */ } BOOL @@ -7000,7 +6868,7 @@ __JS_ToFloat64Free (JSContext *ctx, double *pres, JSValue val) { *pres = d; return 0; fail: - *pres = JS_FLOAT64_NAN; + *pres = NAN; return -1; } @@ -7404,8 +7272,9 @@ JS_ToStringFree (JSContext *ctx, JSValue val) { static JSValue JS_ToLocaleStringFree (JSContext *ctx, JSValue val) { - if (JS_IsNull (val) || JS_IsNull (val)) return JS_ToStringFree (ctx, val); - return JS_InvokeFree (ctx, val, JS_ATOM_toLocaleString, 0, NULL); + /* Simplified: just use toString for now since toLocaleString requires + method lookup and atoms are removed */ + return JS_ToStringFree (ctx, val); } JSValue @@ -7645,9 +7514,6 @@ js_print_more_items (JSPrintValueState *s, int *pcomma_state, uint32_t n) { static void js_print_object (JSPrintValueState *s, JSObject *p) { JSRuntime *rt = s->rt; - JSShape *sh; - JSShapeProperty *prs; - JSProperty *pr; int comma_state; BOOL is_array; uint32_t i; @@ -7671,8 +7537,6 @@ js_print_object (JSPrintValueState *s, JSObject *p) { str = JS_GetProperty (s->ctx, JS_MKPTR (JS_TAG_OBJECT, p), JS_ATOM_stack); if (JS_IsString (str)) { js_putc (s, '\n'); - /* XXX: should remove the last '\n' in stack as - v8. SpiderMonkey does not do it */ js_print_raw_string2 (s, str, TRUE); } JS_FreeValueRT (s->rt, str); @@ -7680,42 +7544,31 @@ js_print_object (JSPrintValueState *s, JSObject *p) { } else { default_obj: if (p->class_id != JS_CLASS_OBJECT) { - js_print_atom (s, rt->class_array[p->class_id].class_name); - js_printf (s, " "); + const char *name = rt->class_array[p->class_id].class_name; + if (name) js_printf (s, "%s ", name); } js_printf (s, "{ "); } - sh = p->shape; /* the shape can be NULL while freeing an object */ - if (sh) { - uint32_t j; + /* Print properties from JSRecord */ + JSRecord *rec = (JSRecord *)p; + uint32_t mask = (uint32_t)objhdr_cap56 (rec->mist_hdr); + uint32_t j = 0; - j = 0; - for (i = 0, prs = get_shape_prop (sh); i < sh->prop_count; i++, prs++) { - if (prs->atom != JS_ATOM_NULL) { - if (j < s->options.max_item_count) { - pr = &p->prop[i]; - js_print_comma (s, &comma_state); - js_print_atom (s, prs->atom); - js_printf (s, ": "); - - /* XXX: autoinit property */ - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - if (s->options.raw_dump) { - js_printf (s, "[varref %p]", (void *)pr->u.var_ref); - } else { - js_print_value (s, *pr->u.var_ref->pvalue); - } - } else { - js_print_value (s, pr->u.value); - } - } - j++; + for (i = 1; i <= mask; i++) { + JSValue k = rec->tab[i].key; + if (!rec_key_is_empty (k) && !rec_key_is_tomb (k)) { + if (j < s->options.max_item_count) { + js_print_comma (s, &comma_state); + js_print_value (s, k); + js_printf (s, ": "); + js_print_value (s, rec->tab[i].val); } + j++; } - if (j > s->options.max_item_count) - js_print_more_items (s, &comma_state, j - s->options.max_item_count); } + if (j > s->options.max_item_count) + js_print_more_items (s, &comma_state, j - s->options.max_item_count); if (!is_array) { if (comma_state != 2) { js_printf (s, " }"); } @@ -7769,21 +7622,26 @@ js_print_value (JSPrintValueState *s, JSValue val) { case JS_TAG_FUNCTION_BYTECODE: { JSFunctionBytecode *b = JS_VALUE_GET_PTR (val); js_puts (s, "[bytecode "); - js_print_atom (s, b->func_name); + /* func_name is a JSValue string now */ + if (JS_IsString (b->func_name)) { + js_print_value (s, b->func_name); + } else { + js_puts (s, ""); + } js_putc (s, ']'); } break; case JS_TAG_FUNCTION: { JSFunction *f = JS_VALUE_GET_FUNCTION (val); js_puts (s, "[Function"); - if (f->name != JS_ATOM_NULL) { + if (f->name && f->name[0]) { js_putc (s, ' '); -// js_print_atom (s, f->name); /// print fnction correctly + js_puts (s, f->name); } else if (f->kind == JS_FUNC_KIND_BYTECODE && f->u.func.function_bytecode) { -// JSAtom name = f->u.func.function_bytecode->func_name; -// if (name != JS_ATOM_NULL) { -// js_putc (s, ' '); -// js_print_atom (s, name); + JSFunctionBytecode *b = f->u.func.function_bytecode; + if (JS_IsString (b->func_name)) { + js_putc (s, ' '); + js_print_value (s, b->func_name); } } js_putc (s, ']'); @@ -7820,20 +7678,18 @@ js_print_value (JSPrintValueState *s, JSValue val) { js_print_object (s, JS_VALUE_GET_OBJ (val)); s->level--; } else { - JSAtom atom = s->rt->class_array[p->class_id].class_name; + const char *class_name = s->rt->class_array[p->class_id].class_name; js_putc (s, '['); - js_print_atom (s, atom); + js_puts (s, class_name ? class_name : "Object"); if (s->options.raw_dump) { js_printf (s, " %p", (void *)p); } js_putc (s, ']'); } } break; - case JS_TAG_SYMBOL: { - JSAtomStruct *p = JS_VALUE_GET_PTR (val); - js_puts (s, "Symbol("); - js_print_atom (s, js_get_atom_index (s->rt, p)); - js_putc (s, ')'); - } break; + case JS_TAG_SYMBOL: + /* Symbols are not supported in this simplified model */ + js_puts (s, "Symbol()"); + break; default: js_printf (s, "[unknown tag %d]", tag); break; @@ -7892,16 +7748,7 @@ js_dump_value_write (void *opaque, const char *buf, size_t len) { fwrite (buf, 1, len, fo); } -static __maybe_unused void -print_atom (JSContext *ctx, JSAtom atom) { - JSPrintValueState ss, *s = &ss; - memset (s, 0, sizeof (*s)); - s->rt = ctx->rt; - s->ctx = ctx; - s->write_func = js_dump_value_write; - s->write_opaque = stdout; - js_print_atom (s, atom); -} +/* print_atom removed - atoms no longer used */ static __maybe_unused void JS_DumpValue (JSContext *ctx, const char *str, JSValue val) { @@ -7926,17 +7773,15 @@ JS_DumpObjectHeader (JSRuntime *rt) { /* for debug only: dump an object without side effect */ static __maybe_unused void JS_DumpObject (JSRuntime *rt, JSObject *p) { - JSShape *sh; JSPrintValueOptions options; + JSRecord *rec = (JSRecord *)p; - /* XXX: should encode atoms with special characters */ - sh = p->shape; /* the shape can be NULL while freeing an object */ printf ("%14p %4d ", (void *)p, p->header.ref_count); - if (sh) { - printf ("%3d%c %14p ", sh->header.ref_count, " *"[sh->is_hashed], - (void *)sh -> proto); + /* Print prototype from JSRecord */ + if (rec->proto) { + printf ("%14p ", (void *)rec->proto); } else { - printf ("%3s %14s ", "-", "-"); + printf ("%14s ", "-"); } JS_PrintValueSetDefaultOptions (&options); @@ -7992,7 +7837,7 @@ static double js_pow (double a, double b) { if (unlikely (!isfinite (b)) && fabs (a) == 1) { /* not compatible with IEEE 754 */ - return JS_FLOAT64_NAN; + return NAN; } else { return pow (a, b); } @@ -8462,53 +8307,51 @@ static __exception int JS_CopyDataProperties (JSContext *ctx, JSValue target, JSValue source, JSValue excluded, BOOL setprop) { - JSPropertyEnum *tab_atom; + JSValue *keys; JSValue val; - uint32_t i, tab_atom_count; - JSObject *p; - JSObject *pexcl = NULL; - int ret, gpn_flags; - JSPropertyDescriptor desc; + uint32_t i, key_count; + int ret; if (JS_VALUE_GET_TAG (source) != JS_TAG_OBJECT) return 0; - if (JS_VALUE_GET_TAG (excluded) == JS_TAG_OBJECT) - pexcl = JS_VALUE_GET_OBJ (excluded); - - p = JS_VALUE_GET_OBJ (source); - - gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY; - if (JS_GetOwnPropertyNamesInternal (ctx, &tab_atom, &tab_atom_count, p, - gpn_flags)) + /* Get all string keys from source */ + if (JS_GetOwnPropertyNames (ctx, &keys, &key_count, source)) return -1; - for (i = 0; i < tab_atom_count; i++) { - if (pexcl) { - ret = JS_GetOwnPropertyInternal (ctx, NULL, pexcl, tab_atom[i].atom); - if (ret) { - if (ret < 0) goto exception; + for (i = 0; i < key_count; i++) { + /* Check if key is excluded */ + if (JS_VALUE_GET_TAG (excluded) == JS_TAG_OBJECT) { + /* Check if key exists in excluded object */ + JSValue test = JS_GetProperty (ctx, excluded, keys[i]); + if (!JS_IsNull (test) && !JS_IsException (test)) { + JS_FreeValue (ctx, test); continue; } + JS_FreeValue (ctx, test); } - if (!(gpn_flags & JS_GPN_ENUM_ONLY)) { - /* test if the property is enumerable */ - ret = JS_GetOwnPropertyInternal (ctx, &desc, p, tab_atom[i].atom); - if (ret < 0) goto exception; - if (!ret) continue; - js_free_desc (ctx, &desc); - } - val = JS_GetProperty (ctx, source, tab_atom[i].atom); + + /* Get property value from source */ + val = JS_GetProperty (ctx, source, keys[i]); if (JS_IsException (val)) goto exception; + + /* Set property on target */ if (setprop) - ret = JS_SetProperty (ctx, target, tab_atom[i].atom, val); + ret = JS_SetProperty (ctx, target, JS_DupValue (ctx, keys[i]), val); else - ret = JS_SetPropertyInternal (ctx, target, tab_atom[i].atom, val); + ret = JS_SetProperty (ctx, target, JS_DupValue (ctx, keys[i]), val); if (ret < 0) goto exception; } - JS_FreePropertyEnum (ctx, tab_atom, tab_atom_count); + + /* Free keys array */ + for (i = 0; i < key_count; i++) + JS_FreeValue (ctx, keys[i]); + js_free (ctx, keys); return 0; + exception: - JS_FreePropertyEnum (ctx, tab_atom, tab_atom_count); + for (i = 0; i < key_count; i++) + JS_FreeValue (ctx, keys[i]); + js_free (ctx, keys); return -1; } @@ -9035,9 +8878,6 @@ restart: : *sp++ = JS_AtomToString (ctx, JS_ATOM_empty_string); BREAK; #endif - CASE (OP_push_atom_value) : *sp++ = JS_AtomToValue (ctx, get_u32 (pc)); - pc += 4; - BREAK; CASE (OP_null) : *sp++ = JS_NULL; BREAK; CASE (OP_push_this) @@ -9933,25 +9773,18 @@ restart: CASE (OP_get_field) : { JSValue val; - KeyId key; - JSAtom atom; + uint32_t idx; + JSValue key; JSValue obj; - const uint8_t *ic_pc; - ic_pc = pc - 1; /* PC of opcode, before consuming operands */ - key = get_u32 (pc); + idx = get_u32 (pc); pc += 4; sf->cur_pc = pc; - /* Extract atom from KeyId for legacy path */ - atom = key_is_text (key) ? (JSAtom)key_payload (key) : JS_ATOM_NULL; + /* Get JSValue key from cpool */ + key = b->cpool[idx]; -#ifdef DUMP_PROFILE - /* Record property access site */ - profile_record_prop_site (rt, b, (uint32_t)(pc - b->byte_code_buf), - atom); -#endif obj = sp[-1]; /* Functions don't support property access in cell script */ @@ -9960,212 +9793,99 @@ restart: goto exception; } - /* Record fast path */ + /* Record fast path - use JSValue key directly */ if (JS_VALUE_GET_TAG (obj) == JS_TAG_OBJECT && js_gc_obj_type (obj) == JS_GC_OBJ_TYPE_RECORD) { JSRecord *rec = JS_VALUE_GET_RECORD (obj); val = rec_get (ctx, rec, key); JS_FreeValue (ctx, obj); sp[-1] = val; - goto get_field_done; + } else if (likely (JS_VALUE_GET_TAG (obj) == JS_TAG_OBJECT + && js_gc_obj_type (obj) == JS_GC_OBJ_TYPE_JS_OBJECT)) { + /* Legacy JSObject path - convert key to atom */ + JSAtom atom = JS_ValueToAtom (ctx, key); + val = JS_GetProperty (ctx, obj, atom); + JS_FreeAtom (ctx, atom); + if (unlikely (JS_IsException (val))) goto exception; + JS_FreeValue (ctx, sp[-1]); + sp[-1] = val; + } else { + /* Fallback for non-object values */ + JS_FreeValue (ctx, sp[-1]); + sp[-1] = JS_NULL; } - - /* Monomorphic IC fast path: shape-guarded own-property lookup */ - if (likely (JS_VALUE_GET_TAG (obj) == JS_TAG_OBJECT - && js_gc_obj_type (obj) == JS_GC_OBJ_TYPE_JS_OBJECT)) { - JSObject *p = JS_VALUE_GET_OBJ (obj); - JSShape *sh = p->shape; - - /* Simple thread-local IC cache using PC as key */ - static __thread struct { - const uint8_t *pc; - JSShape *shape; - uint32_t prop_idx; - JSAtom atom; - } ic_cache[256]; - - uint32_t cache_idx = ((uintptr_t)ic_pc >> 3) & 255; - struct { - const uint8_t *pc; - JSShape *shape; - uint32_t prop_idx; - JSAtom atom; - } *slot = &ic_cache[cache_idx]; - - /* IC hit: shape guard passed */ - if (likely (slot->pc == ic_pc && slot->shape == sh - && slot->atom == atom)) { - JSProperty *pr = &p->prop[slot->prop_idx]; - JSShapeProperty *prs = &get_shape_prop (sh)[slot->prop_idx]; - - /* Double-check it's still a normal data property */ - if (likely ((prs->flags & JS_PROP_TMASK) == JS_PROP_NORMAL)) { - val = JS_DupValue (ctx, pr->u.value); - JS_FreeValue (ctx, obj); - sp[-1] = val; - goto get_field_done; - } - } - - /* IC miss: do lookup and populate cache if it's an own data property - */ - { - JSProperty *pr; - JSShapeProperty *prs = find_own_property (&pr, p, atom); - - if (prs && (prs->flags & JS_PROP_TMASK) == JS_PROP_NORMAL) { - /* Cache this for next time */ - uint32_t prop_idx = prs - get_shape_prop (sh); - slot->pc = ic_pc; - slot->shape = sh; - slot->prop_idx = prop_idx; - slot->atom = atom; - - val = JS_DupValue (ctx, pr->u.value); - JS_FreeValue (ctx, obj); - sp[-1] = val; - goto get_field_done; - } - } - } - - /* Slow path: proto chain, getters, non-objects, etc. */ - val = JS_GetProperty (ctx, obj, atom); - if (unlikely (JS_IsException (val))) goto exception; - JS_FreeValue (ctx, sp[-1]); - sp[-1] = val; - get_field_done:; } BREAK; CASE (OP_get_field2) : { JSValue val; - KeyId key; - JSAtom atom; + uint32_t idx; + JSValue key; JSValue obj; - const uint8_t *ic_pc; - ic_pc = pc - 1; - key = get_u32 (pc); + idx = get_u32 (pc); pc += 4; sf->cur_pc = pc; - /* Extract atom from KeyId for legacy path */ - atom = key_is_text (key) ? (JSAtom)key_payload (key) : JS_ATOM_NULL; + /* Get JSValue key from cpool */ + key = b->cpool[idx]; -#ifdef DUMP_PROFILE - /* Record property access site */ - profile_record_prop_site (rt, b, (uint32_t)(pc - b->byte_code_buf), - atom); -#endif obj = sp[-1]; /* Proxy method-call sugar: func.name(...) -> func("name", [args...]) OP_get_field2 is only emitted when a call immediately follows. */ if (JS_IsFunction (obj)) { - val = JS_AtomToValue (ctx, atom); /* "name" */ - if (unlikely (JS_IsException (val))) goto exception; + val = JS_DupValue (ctx, key); /* "name" as JSValue string */ *sp++ = val; /* stack becomes [func, "name"] */ - goto get_field2_done; - } - - /* Record fast path */ - if (JS_VALUE_GET_TAG (obj) == JS_TAG_OBJECT - && js_gc_obj_type (obj) == JS_GC_OBJ_TYPE_RECORD) { + } else if (JS_VALUE_GET_TAG (obj) == JS_TAG_OBJECT + && js_gc_obj_type (obj) == JS_GC_OBJ_TYPE_RECORD) { + /* Record fast path - use JSValue key directly */ JSRecord *rec = JS_VALUE_GET_RECORD (obj); val = rec_get (ctx, rec, key); *sp++ = val; - goto get_field2_done; + } else if (likely (JS_VALUE_GET_TAG (obj) == JS_TAG_OBJECT + && js_gc_obj_type (obj) == JS_GC_OBJ_TYPE_JS_OBJECT)) { + /* Legacy JSObject path - convert key to atom */ + JSAtom atom = JS_ValueToAtom (ctx, key); + val = JS_GetProperty (ctx, obj, atom); + JS_FreeAtom (ctx, atom); + if (unlikely (JS_IsException (val))) goto exception; + *sp++ = val; + } else { + /* Fallback for non-objects */ + *sp++ = JS_NULL; } - - /* Monomorphic IC fast path */ - if (likely (JS_VALUE_GET_TAG (obj) == JS_TAG_OBJECT - && js_gc_obj_type (obj) == JS_GC_OBJ_TYPE_JS_OBJECT)) { - JSObject *p = JS_VALUE_GET_OBJ (obj); - JSShape *sh = p->shape; - - static __thread struct { - const uint8_t *pc; - JSShape *shape; - uint32_t prop_idx; - JSAtom atom; - } ic_cache2[256]; - - uint32_t cache_idx = ((uintptr_t)ic_pc >> 3) & 255; - struct { - const uint8_t *pc; - JSShape *shape; - uint32_t prop_idx; - JSAtom atom; - } *slot = &ic_cache2[cache_idx]; - - if (likely (slot->pc == ic_pc && slot->shape == sh - && slot->atom == atom)) { - JSProperty *pr = &p->prop[slot->prop_idx]; - JSShapeProperty *prs = &get_shape_prop (sh)[slot->prop_idx]; - - if (likely ((prs->flags & JS_PROP_TMASK) == JS_PROP_NORMAL)) { - val = JS_DupValue (ctx, pr->u.value); - *sp++ = val; - goto get_field2_done; - } - } - - { - JSProperty *pr; - JSShapeProperty *prs = find_own_property (&pr, p, atom); - - if (prs && (prs->flags & JS_PROP_TMASK) == JS_PROP_NORMAL) { - uint32_t prop_idx = prs - get_shape_prop (sh); - slot->pc = ic_pc; - slot->shape = sh; - slot->prop_idx = prop_idx; - slot->atom = atom; - - val = JS_DupValue (ctx, pr->u.value); - *sp++ = val; - goto get_field2_done; - } - } - } - - /* Slow path */ - val = JS_GetProperty (ctx, obj, atom); - if (unlikely (JS_IsException (val))) goto exception; - *sp++ = val; - get_field2_done:; } BREAK; CASE (OP_put_field) : { int ret; - KeyId key; - JSAtom atom; - key = get_u32 (pc); + uint32_t idx; + JSValue key; + + idx = get_u32 (pc); pc += 4; sf->cur_pc = pc; - /* Extract atom from KeyId for legacy path */ - atom = key_is_text (key) ? (JSAtom)key_payload (key) : JS_ATOM_NULL; + /* Get JSValue key from cpool */ + key = b->cpool[idx]; -#ifdef DUMP_PROFILE - /* Record property access site */ - profile_record_prop_site (rt, b, (uint32_t)(pc - b->byte_code_buf), - atom); -#endif /* Functions don't support property assignment in cell script */ if (!JS_IsObject (sp[-2])) { JS_ThrowTypeError (ctx, "tried to set property of non-object"); goto exception; } - /* Record fast path */ + /* Record fast path - use JSValue key directly */ if (js_gc_obj_type (sp[-2]) == JS_GC_OBJ_TYPE_RECORD) { JSRecord *rec = JS_VALUE_GET_RECORD (sp[-2]); ret = rec_set_own (ctx, rec, key, sp[-1]); } else { + /* Legacy JSObject path - convert key to atom */ + JSAtom atom = JS_ValueToAtom (ctx, key); ret = JS_SetPropertyInternal (ctx, sp[-2], atom, sp[-1]); + JS_FreeAtom (ctx, atom); } JS_FreeValue (ctx, sp[-2]); sp -= 2; @@ -10175,21 +9895,25 @@ restart: CASE (OP_define_field) : { int ret; - KeyId key; - JSAtom atom; - key = get_u32 (pc); + uint32_t idx; + JSValue key; + + idx = get_u32 (pc); pc += 4; - /* Extract atom from KeyId for legacy path */ - atom = key_is_text (key) ? (JSAtom)key_payload (key) : JS_ATOM_NULL; + /* Get JSValue key from cpool */ + key = b->cpool[idx]; - /* Record fast path */ + /* Record fast path - use JSValue key directly */ if (JS_VALUE_GET_TAG (sp[-2]) == JS_TAG_OBJECT && js_gc_obj_type (sp[-2]) == JS_GC_OBJ_TYPE_RECORD) { JSRecord *rec = JS_VALUE_GET_RECORD (sp[-2]); ret = rec_set_own (ctx, rec, key, sp[-1]); } else { + /* Legacy JSObject path - convert key to atom */ + JSAtom atom = JS_ValueToAtom (ctx, key); ret = JS_SetPropertyInternal (ctx, sp[-2], atom, sp[-1]); + JS_FreeAtom (ctx, atom); } sp--; if (unlikely (ret < 0)) goto exception; @@ -13084,15 +12808,23 @@ emit_atom (JSParseState *s, JSAtom name) { bc->size += 4; } -/* Emit a KeyId for property access opcodes. - For now, we wrap atom as K_TEXT. In future, this may also emit K_REC. */ -static void -emit_keyid (JSParseState *s, KeyId k) { - DynBuf *bc = &s->cur_func->byte_code; - if (dbuf_realloc (bc, bc->size + 4)) return; - keyid_dup (s->ctx, k); - put_u32 (bc->buf + bc->size, k); - bc->size += 4; +/* Forward declarations for cpool */ +static int fd_cpool_add (JSContext *ctx, JSFunctionDef *fd, JSValue val); +static int cpool_add (JSParseState *s, JSValue val); + +/* Emit a property key for field access opcodes. + The key is converted to a JSValue and added to cpool; the index is emitted. */ +static int +emit_prop_key (JSParseState *s, JSAtom atom) { + JSValue key = JS_AtomToValue (s->ctx, atom); + if (JS_IsException (key)) return -1; + int idx = cpool_add (s, key); + if (idx < 0) { + JS_FreeValue (s->ctx, key); + return -1; + } + emit_u32 (s, idx); + return 0; } static int @@ -13172,32 +12904,23 @@ emit_goto (JSParseState *s, int opcode, int label) { /* return the constant pool index. 'val' is not duplicated. */ static int -cpool_add (JSParseState *s, JSValue val) { - JSFunctionDef *fd = s->cur_func; - - if (js_resize_array (s->ctx, (void *)&fd->cpool, sizeof (fd->cpool[0]), +fd_cpool_add (JSContext *ctx, JSFunctionDef *fd, JSValue val) { + if (js_resize_array (ctx, (void *)&fd->cpool, sizeof (fd->cpool[0]), &fd->cpool_size, fd->cpool_count + 1)) return -1; fd->cpool[fd->cpool_count++] = val; return fd->cpool_count - 1; } +/* return the constant pool index. 'val' is not duplicated. */ +static int +cpool_add (JSParseState *s, JSValue val) { + return fd_cpool_add (s->ctx, s->cur_func, val); +} + static __exception int -emit_push_const (JSParseState *s, JSValue val, BOOL as_atom) { +emit_push_const (JSParseState *s, JSValue val) { int idx; - - if (JS_VALUE_GET_TAG (val) == JS_TAG_STRING && as_atom) { - JSAtom atom; - /* warning: JS_NewAtomStr frees the string value */ - JS_DupValue (s->ctx, val); - atom = JS_NewAtomStr (s->ctx, JS_VALUE_GET_STRING (val)); - if (atom != JS_ATOM_NULL) { - emit_op (s, OP_push_atom_value); - emit_u32 (s, atom); - return 0; - } - } - idx = cpool_add (s, JS_DupValue (s->ctx, val)); if (idx < 0) return -1; emit_op (s, OP_push_const); @@ -13592,7 +13315,7 @@ js_parse_template (JSParseState *s, int call, int *argc) { template_object = JS_NewArray (ctx); if (JS_IsException (template_object)) return -1; // pool_idx = s->cur_func->cpool_count; - ret = emit_push_const (s, template_object, 0); + ret = emit_push_const (s, template_object); JS_FreeValue (ctx, template_object); if (ret) return -1; raw_array = JS_NewArray (ctx); @@ -13633,7 +13356,7 @@ js_parse_template (JSParseState *s, int call, int *argc) { if (js_parse_string (s, '`', TRUE, p, &cooked, &p)) return -1; str = JS_VALUE_GET_STRING (cooked.u.str.str); if (str->len != 0 || depth == 0) { - ret = emit_push_const (s, cooked.u.str.str, 1); + ret = emit_push_const (s, cooked.u.str.str); JS_FreeValue (s->ctx, cooked.u.str.str); if (ret) return -1; /* For single-literal templates, go directly to done1 */ @@ -13971,7 +13694,7 @@ js_parse_object_literal (JSParseState *s) { emit_atom (s, name); emit_u16 (s, s->cur_func->scope_level); emit_op (s, OP_define_field); - emit_keyid (s, key_text (name)); + emit_prop_key (s, name); } else if (s->token.val == '(') { JSParseFunctionEnum func_type; int op_flags; @@ -14002,7 +13725,7 @@ js_parse_object_literal (JSParseState *s) { } else { set_object_name (s, name); emit_op (s, OP_define_field); - emit_keyid (s, key_text (name)); + emit_prop_key (s, name); } } JS_FreeAtom (s->ctx, name); @@ -14115,8 +13838,10 @@ get_lvalue (JSParseState *s, int *popcode, int *pscope, JSAtom *pname, depth = 2; /* will generate OP_get_ref_value */ break; case OP_get_field: { - KeyId key = get_u32 (fd->byte_code.buf + fd->last_opcode_pos + 1); - name = key_is_text (key) ? (JSAtom)key_payload (key) : JS_ATOM_NULL; + /* Read cpool index and get property name from cpool */ + uint32_t idx = get_u32 (fd->byte_code.buf + fd->last_opcode_pos + 1); + JSValue key = fd->cpool[idx]; + name = JS_ValueToAtom (s->ctx, key); } depth = 1; break; @@ -14155,7 +13880,7 @@ get_lvalue (JSParseState *s, int *popcode, int *pscope, JSAtom *pname, break; case OP_get_field: emit_op (s, OP_get_field2); - emit_keyid (s, key_text (name)); + emit_prop_key (s, name); break; case OP_get_array_el: emit_op (s, OP_get_array_el3); @@ -14264,7 +13989,7 @@ put_lvalue (JSParseState *s, int opcode, int scope, JSAtom name, int label, break; case OP_get_field: emit_op (s, OP_put_field); - emit_keyid (s, key_text (name)); /* name has refcount */ + emit_prop_key (s, name); /* name has refcount */ break; case OP_get_array_el: emit_op (s, OP_put_array_el); @@ -14410,7 +14135,7 @@ js_parse_destructuring_element (JSParseState *s, int tok, int is_arg, /* named property */ /* get the named property from the source object */ emit_op (s, OP_get_field2); - emit_keyid (s, key_text (prop_name)); + emit_prop_key (s, prop_name); } if (js_parse_destructuring_element (s, tok, is_arg, TRUE, TRUE, export_flag) @@ -14487,7 +14212,7 @@ js_parse_destructuring_element (JSParseState *s, int tok, int is_arg, /* XXX: should have OP_get_field2x with depth */ /* source -- val */ emit_op (s, OP_get_field); - emit_keyid (s, key_text (prop_name)); + emit_prop_key (s, prop_name); } } else { /* prop_type = PROP_TYPE_VAR, cannot be a computed property */ @@ -14515,7 +14240,7 @@ js_parse_destructuring_element (JSParseState *s, int tok, int is_arg, /* source -- source val */ emit_op (s, OP_get_field2); - emit_keyid (s, key_text (prop_name)); + emit_prop_key (s, prop_name); } } if (tok) { @@ -14622,7 +14347,7 @@ js_parse_postfix_expr (JSParseState *s, int parse_flags) { emit_u32 (s, JS_VALUE_GET_INT (val)); } else { large_number: - if (emit_push_const (s, val, 0) < 0) return -1; + if (emit_push_const (s, val) < 0) return -1; } } if (next_token (s)) return -1; @@ -14631,7 +14356,7 @@ js_parse_postfix_expr (JSParseState *s, int parse_flags) { if (js_parse_template (s, 0, NULL)) return -1; break; case TOK_STRING: - if (emit_push_const (s, s->token.u.str.str, 1)) return -1; + if (emit_push_const (s, s->token.u.str.str)) return -1; if (next_token (s)) return -1; break; @@ -14647,7 +14372,7 @@ js_parse_postfix_expr (JSParseState *s, int parse_flags) { return js_parse_error (s, "RegExp are not supported"); /* the previous token is '/' or '/=', so no need to free */ if (js_parse_regexp (s)) return -1; - ret = emit_push_const (s, s->token.u.regexp.body, 0); + ret = emit_push_const (s, s->token.u.regexp.body); str = s->ctx->compile_regexp (s->ctx, s->token.u.regexp.body, s->token.u.regexp.flags); if (JS_IsException (str)) { @@ -14659,7 +14384,7 @@ js_parse_postfix_expr (JSParseState *s, int parse_flags) { line_num + 1, col_num + 1, 0); return -1; } - ret = emit_push_const (s, str, 0); + ret = emit_push_const (s, str); JS_FreeValue (s->ctx, str); if (ret) return -1; /* we use a specific opcode to be sure the correct @@ -14898,7 +14623,7 @@ js_parse_postfix_expr (JSParseState *s, int parse_flags) { optional_chain_test (s, &optional_chaining_label, 1); } emit_op (s, OP_get_field); - emit_keyid (s, key_text (s->token.u.ident.atom)); + emit_prop_key (s, s->token.u.ident.atom); if (next_token (s)) return -1; } else if (s->token.val == '[') { op_token_ptr = s->token.ptr; @@ -14955,14 +14680,12 @@ js_parse_delete (JSParseState *s) { opt_chain_label = -1; } { - KeyId key = get_u32 (fd->byte_code.buf + fd->last_opcode_pos + 1); - name = key_is_text (key) ? (JSAtom)key_payload (key) : JS_ATOM_NULL; + /* Read cpool index and get property name from cpool */ + uint32_t idx = get_u32 (fd->byte_code.buf + fd->last_opcode_pos + 1); + val = fd->cpool[idx]; } fd->byte_code.size = fd->last_opcode_pos; - val = JS_AtomToValue (s->ctx, name); - ret = emit_push_const (s, val, 1); - JS_FreeValue (s->ctx, val); - JS_FreeAtom (s->ctx, name); + ret = emit_push_const (s, val); if (ret) return ret; emit_op (s, OP_delete); if (opt_chain_label >= 0) { @@ -16410,8 +16133,7 @@ free_bytecode_atoms (JSRuntime *rt, const uint8_t *bc_buf, int bc_len, JS_FreeAtomRT (rt, atom); break; case OP_FMT_key: - if ((pos + 1 + 4) > bc_len) break; - keyid_free (rt, get_u32 (bc_buf + pos + 1)); + /* Key operand is now a cpool index; cpool values freed separately */ break; default: break; @@ -16692,14 +16414,11 @@ dump_byte_code (JSContext *ctx, int pass, const uint8_t *tab, int len, print_atom (ctx, get_u32 (tab + pos)); break; case OP_FMT_key: { - KeyId k = get_u32 (tab + pos); - if (key_is_text (k)) { - printf (" text:"); - print_atom (ctx, (JSAtom)key_payload (k)); - } else if (key_is_rec (k)) { - printf (" rec:%u", key_payload (k)); - } else { - printf (" key:%u", k); + /* Key operand is a cpool index; print the cpool value */ + uint32_t key_idx = get_u32 (tab + pos); + printf (" %u: ", key_idx); + if (key_idx < cpool_count) { + JS_PrintValue (ctx, js_dump_value_write, stdout, cpool[key_idx], NULL); } } break; case OP_FMT_atom_u8: @@ -17182,13 +16901,16 @@ resolve_scope_var (JSContext *ctx, JSFunctionDef *s, JSAtom var_name, if (!(var_idx & ARGUMENT_VAR_OFFSET) && s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) { /* Create a dummy object reference for the func_var */ + JSValue name_val = JS_AtomToValue (ctx, var_name); + int cpool_idx = fd_cpool_add (ctx, s, name_val); + if (cpool_idx < 0) return -1; dbuf_putc (bc, OP_object); dbuf_putc (bc, OP_get_loc); dbuf_put_u16 (bc, var_idx); dbuf_putc (bc, OP_define_field); - dbuf_put_u32 (bc, JS_DupAtom (ctx, var_name)); - dbuf_putc (bc, OP_push_atom_value); - dbuf_put_u32 (bc, JS_DupAtom (ctx, var_name)); + dbuf_put_u32 (bc, cpool_idx); + dbuf_putc (bc, OP_push_const); + dbuf_put_u32 (bc, cpool_idx); } else if (label_done == -1 && can_opt_put_ref_value (bc_buf, ls->pos)) { int get_op; if (var_idx & ARGUMENT_VAR_OFFSET) { @@ -17396,13 +17118,16 @@ resolve_scope_var (JSContext *ctx, JSFunctionDef *s, JSAtom var_name, case OP_scope_make_ref: if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) { /* Create a dummy object reference for the func_var */ + JSValue name_val = JS_AtomToValue (ctx, var_name); + int cpool_idx = fd_cpool_add (ctx, s, name_val); + if (cpool_idx < 0) return -1; dbuf_putc (bc, OP_object); dbuf_putc (bc, OP_get_var_ref); dbuf_put_u16 (bc, idx); dbuf_putc (bc, OP_define_field); - dbuf_put_u32 (bc, JS_DupAtom (ctx, var_name)); - dbuf_putc (bc, OP_push_atom_value); - dbuf_put_u32 (bc, JS_DupAtom (ctx, var_name)); + dbuf_put_u32 (bc, cpool_idx); + dbuf_putc (bc, OP_push_const); + dbuf_put_u32 (bc, cpool_idx); } else if (label_done == -1 && can_opt_put_ref_value (bc_buf, ls->pos)) { int get_op; @@ -17798,7 +17523,7 @@ code_match (CodeContext *s, int pos, ...) { break; } case OP_FMT_key: { - /* Store KeyId in the label field for now */ + /* Store cpool index in the label field */ s->label = get_u32 (tab + pos); break; } @@ -17969,7 +17694,7 @@ skip_dead_code (JSFunctionDef *s, const uint8_t *bc_buf, int bc_len, int pos, JS_FreeAtom (s->ctx, atom); break; case OP_FMT_key: - keyid_free (s->ctx->rt, get_u32 (bc_buf + pos + 1)); + /* Key operand is a cpool index; cpool values freed separately */ break; default: break; @@ -18963,27 +18688,6 @@ resolve_labels (JSContext *ctx, JSFunctionDef *s) { } goto no_change; #endif - case OP_push_atom_value: - if (OPTIMIZE) { - JSAtom atom = get_u32 (bc_buf + pos + 1); - /* remove push/drop pairs generated by the parser */ - if (code_match (&cc, pos_next, OP_drop, -1)) { - JS_FreeAtom (ctx, atom); - if (cc.line_num >= 0) line_num = cc.line_num; - pos_next = cc.pos; - break; - } -#if SHORT_OPCODES - if (atom == JS_ATOM_empty_string) { - JS_FreeAtom (ctx, atom); - add_pc2line_info (s, bc_out.size, line_num); - dbuf_putc (&bc_out, OP_push_empty_string); - break; - } -#endif - } - goto no_change; - case OP_to_propkey: if (OPTIMIZE) { /* remove redundant to_propkey opcodes when storing simple data */ @@ -18991,7 +18695,7 @@ resolve_labels (JSContext *ctx, JSFunctionDef *s) { M3 (OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1) || code_match (&cc, pos_next, - M3 (OP_push_i32, OP_push_const, OP_push_atom_value), + M2 (OP_push_i32, OP_push_const), OP_put_array_el, -1) || code_match (&cc, pos_next, M4 (OP_null, OP_null, OP_push_true, OP_push_false), @@ -19070,29 +18774,6 @@ resolve_labels (JSContext *ctx, JSFunctionDef *s) { pos_next = cc.pos; break; } - /* transformation: - get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> - push_atom_value(x) add_loc(n) - */ - if (code_match (&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, - OP_put_loc, idx, OP_drop, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info (s, bc_out.size, line_num); -#if SHORT_OPCODES - if (cc.atom == JS_ATOM_empty_string) { - JS_FreeAtom (ctx, cc.atom); - dbuf_putc (&bc_out, OP_push_empty_string); - } else -#endif - { - dbuf_putc (&bc_out, OP_push_atom_value); - dbuf_put_u32 (&bc_out, cc.atom); - } - dbuf_putc (&bc_out, OP_add_loc); - dbuf_putc (&bc_out, idx); - pos_next = cc.pos; - break; - } /* transformation: get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n) @@ -19728,9 +19409,7 @@ js_create_function (JSContext *ctx, JSFunctionDef *fd) { b->prop_site_capacity = 0; #endif - /* Initialize IC slots (allocate lazily based on bytecode analysis) */ - b->ic_slots = NULL; - b->ic_count = 0; + /* IC removed - shapes no longer used */ b->is_direct_or_indirect_eval = (fd->eval_type == JS_EVAL_TYPE_DIRECT || fd->eval_type == JS_EVAL_TYPE_INDIRECT); @@ -19754,19 +19433,7 @@ fail: return JS_EXCEPTION; } -/* IC helper functions */ -static ICSlot * -ic_get_slot (JSFunctionBytecode *b, uint32_t ic_index) { - if (ic_index >= b->ic_count) return NULL; - return &b->ic_slots[ic_index]; -} - -static void -ic_init_call (ICSlot *slot) { - memset (slot, 0, sizeof (*slot)); - slot->kind = IC_CALL; - slot->state = IC_STATE_UNINIT; -} +/* IC helper functions removed - shapes no longer used */ static void free_function_bytecode (JSRuntime *rt, JSFunctionBytecode *b) { @@ -19801,8 +19468,7 @@ free_function_bytecode (JSRuntime *rt, JSFunctionBytecode *b) { js_free_rt (rt, b->prop_sites); #endif - /* Free IC slots */ - if (b->ic_slots) { js_free_rt (rt, b->ic_slots); } + /* IC removed */ remove_gc_object (&b->header); if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) { @@ -20839,16 +20505,9 @@ JS_WriteFunctionBytecode (BCWriterState *s, const uint8_t *bc_buf1, if (bc_atom_to_idx (s, &val, atom)) goto fail; put_u32 (bc_buf + pos + 1, val); break; - case OP_FMT_key: { - KeyId k = get_u32 (bc_buf + pos + 1); - if (key_is_text (k)) { - atom = (JSAtom)key_payload (k); - if (bc_atom_to_idx (s, &val, atom)) goto fail; - /* Reconstruct KeyId with the serialized index */ - put_u32 (bc_buf + pos + 1, key_text (val)); - } - /* K_REC keys don't need atom conversion */ - } break; + case OP_FMT_key: + /* Key operand is a cpool index; cpool values serialized separately */ + break; default: break; } @@ -20869,12 +20528,11 @@ fail: static void JS_WriteString (BCWriterState *s, JSString *p) { int i; - bc_put_leb128 (s, ((uint32_t)p->len << 1) | p->is_wide_char); - if (p->is_wide_char) { - for (i = 0; i < p->len; i++) - bc_put_u16 (s, p->u.str16[i]); - } else { - dbuf_put (&s->dbuf, p->u.str8, p->len); + /* UTF-32: write length, then each character as 32-bit value */ + bc_put_leb128 (s, (uint32_t)p->len); + for (i = 0; i < p->len; i++) { + uint32_t c = string_get (p, i); + bc_put_u32 (s, c); } } @@ -20964,30 +20622,28 @@ fail: static int JS_WriteObjectTag (BCWriterState *s, JSValue obj) { - JSObject *p = JS_VALUE_GET_OBJ (obj); + JSRecord *rec = (JSRecord *)JS_VALUE_GET_OBJ (obj); + uint32_t mask = (uint32_t)objhdr_cap56 (rec->mist_hdr); uint32_t i, prop_count; - JSShape *sh; - JSShapeProperty *pr; int pass; - JSAtom atom; bc_put_u8 (s, BC_TAG_OBJECT); prop_count = 0; - sh = p->shape; + + /* Two-pass: count then write */ for (pass = 0; pass < 2; pass++) { if (pass == 1) bc_put_leb128 (s, prop_count); - for (i = 0, pr = get_shape_prop (sh); i < sh->prop_count; i++, pr++) { - atom = pr->atom; - if (atom != JS_ATOM_NULL && JS_AtomIsString (s->ctx, atom)) { - if (pr->flags & JS_PROP_TMASK) { - JS_ThrowTypeError (s->ctx, "only value properties are supported"); - goto fail; - } + for (i = 1; i <= mask; i++) { + JSValue k = rec->tab[i].key; + if (!rec_key_is_empty (k) && !rec_key_is_tomb (k) && JS_IsString (k)) { if (pass == 0) { prop_count++; } else { + /* Convert string key to atom for bc_put_atom compatibility */ + JSAtom atom = JS_ValueToAtom (s->ctx, k); bc_put_atom (s, atom); - if (JS_WriteObjectRec (s, p->prop[i].u.value)) goto fail; + JS_FreeAtom (s->ctx, atom); + if (JS_WriteObjectRec (s, rec->tab[i].val)) goto fail; } } } @@ -21109,11 +20765,8 @@ JS_WriteObjectAtoms (BCWriterState *s) { js_dbuf_init (s->ctx, &s->dbuf); bc_put_u8 (s, BC_VERSION); - bc_put_leb128 (s, s->idx_to_atom_count); - for (i = 0; i < s->idx_to_atom_count; i++) { - JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]]; - JS_WriteString (s, p); - } + /* Atoms removed - write empty atom table */ + bc_put_leb128 (s, 0); /* XXX: should check for OOM in above phase */ /* move the atoms at the start */ @@ -21371,37 +21024,31 @@ static JSString * JS_ReadString (BCReaderState *s) { uint32_t len; size_t size; - BOOL is_wide_char; JSString *p; + uint32_t i; if (bc_get_leb128 (s, &len)) return NULL; - is_wide_char = len & 1; - len >>= 1; if (len > JS_STRING_LEN_MAX) { JS_ThrowInternalError (s->ctx, "string too long"); return NULL; } - p = js_alloc_string (s->ctx, len, is_wide_char); + p = js_alloc_string (s->ctx, len); if (!p) { s->error_state = -1; return NULL; } - size = (size_t)len << is_wide_char; + /* UTF-32: each character is 32-bit */ + size = (size_t)len * sizeof (uint32_t); if ((s->buf_end - s->ptr) < size) { bc_read_error_end (s); js_free_string (s->ctx->rt, p); return NULL; } - memcpy (p->u.str8, s->ptr, size); - s->ptr += size; - if (is_wide_char) { - if (is_be ()) { - uint32_t i; - for (i = 0; i < len; i++) - p->u.str16[i] = bswap16 (p->u.str16[i]); - } - } else { - p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */ + for (i = 0; i < len; i++) { + uint32_t c = get_u32 (s->ptr); + if (is_be ()) c = bswap32 (c); + string_put (p, i, c); + s->ptr += 4; } #ifdef DUMP_READ_OBJECT JS_DumpString (s->ctx->rt, p); @@ -21468,26 +21115,9 @@ JS_ReadFunctionBytecode (BCReaderState *s, JSFunctionBytecode *b, #endif } break; - case OP_FMT_key: { - KeyId k = get_u32 (bc_buf + pos + 1); - if (key_is_text (k)) { - idx = key_payload (k); - if (s->is_rom_data) { - JS_DupAtom (s->ctx, (JSAtom)idx); - } else { - if (bc_idx_to_atom (s, &atom, idx)) { - b->byte_code_len = pos; - return -1; - } - put_u32 (bc_buf + pos + 1, key_text (atom)); -#ifdef DUMP_READ_OBJECT - bc_read_trace (s, "at %d, fixup key atom: ", pos + 1); - print_atom (s->ctx, atom); - printf ("\n"); -#endif - } - } - } break; + case OP_FMT_key: + /* Key operand is a cpool index; cpool values deserialized separately */ + break; default: break; } @@ -21951,25 +21581,10 @@ check_exception_free (JSContext *ctx, JSValue obj) { static JSAtom find_atom (JSContext *ctx, const char *name) { - JSAtom atom; - int len; - - if (*name == '[') { - name++; - len = strlen (name) - 1; - /* We assume 8 bit non null strings, which is the case for these - symbols */ - for (atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) { - JSAtomStruct *p = ctx->rt->atom_array[atom]; - JSString *str = p; - if (str->len == len && !memcmp (str->u.str8, name, len)) - return JS_DupAtom (ctx, atom); - } - abort (); - } else { - atom = JS_NewAtom (ctx, name); - } - return atom; + /* Atoms removed - just return NULL atom */ + (void)ctx; + (void)name; + return JS_ATOM_NULL; } static int @@ -22648,6 +22263,32 @@ lre_realloc (void *opaque, void *ptr, size_t size) { return js_realloc_rt (ctx->rt, ptr, size); } +/* Convert UTF-32 JSString to UTF-16 buffer for regex engine. + Returns allocated uint16_t buffer that must be freed by caller. + Sets *out_len to number of uint16 code units. */ +static uint16_t * +js_string_to_utf16 (JSContext *ctx, JSString *str, int *out_len) { + int len = str->len; + /* Worst case: each UTF-32 char becomes 2 UTF-16 surrogates */ + uint16_t *buf = js_malloc (ctx, len * 2 * sizeof (uint16_t)); + if (!buf) return NULL; + + int j = 0; + for (int i = 0; i < len; i++) { + uint32_t c = string_get (str, i); + if (c < 0x10000) { + buf[j++] = (uint16_t)c; + } else { + /* Encode as surrogate pair */ + c -= 0x10000; + buf[j++] = (uint16_t)(0xD800 | (c >> 10)); + buf[j++] = (uint16_t)(0xDC00 | (c & 0x3FF)); + } + } + *out_len = j; + return buf; +} + static JSValue js_regexp_exec (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { @@ -22656,7 +22297,9 @@ js_regexp_exec (JSContext *ctx, JSValue this_val, int argc, JSValue ret, str_val, res, val, groups, captures_arr, match0; uint8_t *re_bytecode; uint8_t **capture, *str_buf; + uint16_t *utf16_buf = NULL; int rc, capture_count, shift, i, re_flags; + int utf16_len = 0; int64_t last_index; const char *group_name_ptr; @@ -22676,7 +22319,7 @@ js_regexp_exec (JSContext *ctx, JSValue this_val, int argc, if (JS_IsException (val) || JS_ToLengthFree (ctx, &last_index, val)) goto fail; - re_bytecode = re->bytecode->u.str8; + re_bytecode = (uint8_t *)re->bytecode->u; re_flags = lre_get_flags (re_bytecode); if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) last_index = 0; @@ -22688,8 +22331,11 @@ js_regexp_exec (JSContext *ctx, JSValue this_val, int argc, if (!capture) goto fail; } - shift = str->is_wide_char; - str_buf = str->u.str8; + /* Convert UTF-32 string to UTF-16 for regex engine */ + utf16_buf = js_string_to_utf16 (ctx, str, &utf16_len); + if (!utf16_buf) goto fail; + shift = 1; /* UTF-16 mode */ + str_buf = (uint8_t *)utf16_buf; if (last_index > str->len) { rc = 2; @@ -22816,6 +22462,7 @@ done: JS_FreeValue (ctx, match0); JS_FreeValue (ctx, res); js_free (ctx, capture); + js_free (ctx, utf16_buf); return ret; fail: @@ -22825,6 +22472,7 @@ fail: JS_FreeValue (ctx, match0); JS_FreeValue (ctx, res); js_free (ctx, capture); + js_free (ctx, utf16_buf); return JS_EXCEPTION; } @@ -22837,6 +22485,8 @@ JS_RegExpDelete (JSContext *ctx, JSValue this_val, JSValue arg) { uint8_t *re_bytecode; int ret; uint8_t **capture, *str_buf; + uint16_t *utf16_buf = NULL; + int utf16_len = 0; int capture_count, shift, re_flags; int next_src_pos, start, end; int64_t last_index; @@ -22850,7 +22500,7 @@ JS_RegExpDelete (JSContext *ctx, JSValue this_val, JSValue arg) { str_val = JS_ToString (ctx, arg); if (JS_IsException (str_val)) goto fail; str = JS_VALUE_GET_STRING (str_val); - re_bytecode = re->bytecode->u.str8; + re_bytecode = (uint8_t *)re->bytecode->u; re_flags = lre_get_flags (re_bytecode); if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { last_index = 0; @@ -22864,13 +22514,16 @@ JS_RegExpDelete (JSContext *ctx, JSValue this_val, JSValue arg) { capture = js_malloc (ctx, sizeof (capture[0]) * capture_count * 2); if (!capture) goto fail; } - shift = str->is_wide_char; - str_buf = str->u.str8; + /* Convert UTF-32 string to UTF-16 for regex engine */ + utf16_buf = js_string_to_utf16 (ctx, str, &utf16_len); + if (!utf16_buf) goto fail; + shift = 1; /* UTF-16 mode */ + str_buf = (uint8_t *)utf16_buf; next_src_pos = 0; for (;;) { - if (last_index > str->len) break; + if (last_index > utf16_len) break; - ret = lre_exec (capture, re_bytecode, str_buf, last_index, str->len, shift, + ret = lre_exec (capture, re_bytecode, str_buf, last_index, utf16_len, shift, ctx); if (ret != 1) { if (ret >= 0) { @@ -22905,11 +22558,17 @@ JS_RegExpDelete (JSContext *ctx, JSValue this_val, JSValue arg) { break; } if (end == start) { - if (!(re_flags & LRE_FLAG_UNICODE) || (unsigned)end >= str->len - || !str->is_wide_char) { + /* Advance by one code unit or one code point if unicode mode */ + if (!(re_flags & LRE_FLAG_UNICODE) || (unsigned)end >= utf16_len) { end++; } else { - string_getc (str, &end); + /* Check for surrogate pair in UTF-16 buffer */ + uint16_t c = utf16_buf[end]; + end++; + if (is_hi_surrogate (c) && end < utf16_len + && is_lo_surrogate (utf16_buf[end])) { + end++; + } } } last_index = end; @@ -22917,10 +22576,12 @@ JS_RegExpDelete (JSContext *ctx, JSValue this_val, JSValue arg) { if (string_buffer_concat (b, str, next_src_pos, str->len)) goto fail; JS_FreeValue (ctx, str_val); js_free (ctx, capture); + js_free (ctx, utf16_buf); return string_buffer_end (b); fail: JS_FreeValue (ctx, str_val); js_free (ctx, capture); + js_free (ctx, utf16_buf); string_buffer_free (b); return JS_EXCEPTION; } @@ -24292,18 +23953,8 @@ js_cell_character (JSContext *ctx, JSValue this_val, int argc, JSString *p = JS_VALUE_GET_STRING (arg); if (p->len == 0) return JS_NewString (ctx, ""); - /* Return first character (handle UTF-16 surrogate pairs) */ - if (p->is_wide_char) { - uint32_t c = p->u.str16[0]; - if (is_hi_surrogate (c) && p->len > 1 - && is_lo_surrogate (p->u.str16[1])) { - /* Surrogate pair - return both code units */ - return js_sub_string (ctx, p, 0, 2); - } - return js_sub_string (ctx, p, 0, 1); - } else { - return js_sub_string (ctx, p, 0, 1); - } + /* UTF-32: each element is a full code point, no surrogate handling needed */ + return js_sub_string (ctx, p, 0, 1); } /* Handle integer - return character from codepoint */ @@ -25203,9 +24854,9 @@ fail_rx_search: /* str removed - arg not owned */ return JS_EXCEPTION; } -static inline uint16_t +static inline uint32_t js_str_get (JSString *s, int idx) { - return s->is_wide_char ? s->u.str16[idx] : s->u.str8[idx]; + return string_get (s, idx); } static int @@ -26375,24 +26026,25 @@ js_cell_object (JSContext *ctx, JSValue this_val, int argc, JSValue result = JS_NewObject (ctx); if (JS_IsException (result)) return result; - JSPropertyEnum *props; + JSValue *keys; uint32_t len; - if (JS_GetOwnPropertyNames (ctx, &props, &len, arg, - JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK)) { + if (JS_GetOwnPropertyNames (ctx, &keys, &len, arg)) { JS_FreeValue (ctx, result); return JS_EXCEPTION; } for (uint32_t i = 0; i < len; i++) { - JSValue val = JS_GetProperty (ctx, arg, props[i].atom); + JSValue val = JS_GetProperty (ctx, arg, keys[i]); if (JS_IsException (val)) { - JS_FreePropertyEnum (ctx, props, len); + for (uint32_t j = 0; j < len; j++) JS_FreeValue (ctx, keys[j]); + js_free (ctx, keys); JS_FreeValue (ctx, result); return JS_EXCEPTION; } - JS_SetProperty (ctx, result, props[i].atom, val); + JS_SetProperty (ctx, result, keys[i], val); } - JS_FreePropertyEnum (ctx, props, len); + for (uint32_t i = 0; i < len; i++) JS_FreeValue (ctx, keys[i]); + js_free (ctx, keys); return result; } @@ -26402,40 +26054,42 @@ js_cell_object (JSContext *ctx, JSValue this_val, int argc, if (JS_IsException (result)) return result; /* Copy from first object */ - JSPropertyEnum *props; + JSValue *keys; uint32_t len; - if (JS_GetOwnPropertyNames (ctx, &props, &len, arg, - JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK)) { + if (JS_GetOwnPropertyNames (ctx, &keys, &len, arg)) { JS_FreeValue (ctx, result); return JS_EXCEPTION; } for (uint32_t i = 0; i < len; i++) { - JSValue val = JS_GetProperty (ctx, arg, props[i].atom); + JSValue val = JS_GetProperty (ctx, arg, keys[i]); if (JS_IsException (val)) { - JS_FreePropertyEnum (ctx, props, len); + for (uint32_t j = 0; j < len; j++) JS_FreeValue (ctx, keys[j]); + js_free (ctx, keys); JS_FreeValue (ctx, result); return JS_EXCEPTION; } - JS_SetProperty (ctx, result, props[i].atom, val); + JS_SetProperty (ctx, result, keys[i], val); } - JS_FreePropertyEnum (ctx, props, len); + for (uint32_t i = 0; i < len; i++) JS_FreeValue (ctx, keys[i]); + js_free (ctx, keys); /* Copy from second object */ - if (JS_GetOwnPropertyNames (ctx, &props, &len, argv[1], - JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK)) { + if (JS_GetOwnPropertyNames (ctx, &keys, &len, argv[1])) { JS_FreeValue (ctx, result); return JS_EXCEPTION; } for (uint32_t i = 0; i < len; i++) { - JSValue val = JS_GetProperty (ctx, argv[1], props[i].atom); + JSValue val = JS_GetProperty (ctx, argv[1], keys[i]); if (JS_IsException (val)) { - JS_FreePropertyEnum (ctx, props, len); + for (uint32_t j = 0; j < len; j++) JS_FreeValue (ctx, keys[j]); + js_free (ctx, keys); JS_FreeValue (ctx, result); return JS_EXCEPTION; } - JS_SetProperty (ctx, result, props[i].atom, val); + JS_SetProperty (ctx, result, keys[i], val); } - JS_FreePropertyEnum (ctx, props, len); + for (uint32_t i = 0; i < len; i++) JS_FreeValue (ctx, keys[i]); + js_free (ctx, keys); return result; } @@ -27341,25 +26995,23 @@ js_cell_meme (JSContext *ctx, JSValue this_val, int argc, do { \ if (!JS_IsObject (mix) || JS_IsNull (mix) || JS_IsArray (ctx, mix)) \ break; \ - JSPropertyEnum *tab; \ + JSValue *tab; \ uint32_t len; \ - if (JS_GetOwnPropertyNames (ctx, &tab, &len, mix, \ - JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) \ - < 0) { \ + if (JS_GetOwnPropertyNames (ctx, &tab, &len, mix) < 0) { \ JS_FreeValue (ctx, result); \ return JS_EXCEPTION; \ } \ for (uint32_t j = 0; j < len; j++) { \ - JSValue val = JS_GetProperty (ctx, mix, tab[j].atom); \ + JSValue val = JS_GetProperty (ctx, mix, tab[j]); \ if (JS_IsException (val)) { \ for (uint32_t k = j; k < len; k++) \ - JS_FreeAtom (ctx, tab[k].atom); \ + JS_FreeValue (ctx, tab[k]); \ js_free (ctx, tab); \ JS_FreeValue (ctx, result); \ return JS_EXCEPTION; \ } \ - JS_SetProperty (ctx, result, tab[j].atom, val); \ - JS_FreeAtom (ctx, tab[j].atom); \ + JS_SetProperty (ctx, result, tab[j], val); \ + JS_FreeValue (ctx, tab[j]); \ } \ js_free (ctx, tab); \ } while (0) @@ -27416,35 +27068,33 @@ js_cell_splat (JSContext *ctx, JSValue this_val, int argc, /* Walk prototype chain and collect text keys */ while (!JS_IsNull (current)) { - JSPropertyEnum *tab; + JSValue *keys; uint32_t len; - if (JS_GetOwnPropertyNames (ctx, &tab, &len, current, - JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) - < 0) { + if (JS_GetOwnPropertyNames (ctx, &keys, &len, current) < 0) { JS_FreeValue (ctx, current); JS_FreeValue (ctx, result); return JS_EXCEPTION; } for (uint32_t i = 0; i < len; i++) { - JSAtom atom = tab[i].atom; + JSValue key = keys[i]; /* Check if property not already in result */ - int has = JS_HasProperty (ctx, result, atom); + int has = JS_HasProperty (ctx, result, key); if (has < 0) { for (uint32_t j = i; j < len; j++) - JS_FreeAtom (ctx, tab[j].atom); - js_free (ctx, tab); + JS_FreeValue (ctx, keys[j]); + js_free (ctx, keys); JS_FreeValue (ctx, current); JS_FreeValue (ctx, result); return JS_EXCEPTION; } if (!has) { - JSValue val = JS_GetProperty (ctx, current, atom); + JSValue val = JS_GetProperty (ctx, current, key); if (JS_IsException (val)) { for (uint32_t j = i; j < len; j++) - JS_FreeAtom (ctx, tab[j].atom); - js_free (ctx, tab); + JS_FreeValue (ctx, keys[j]); + js_free (ctx, keys); JS_FreeValue (ctx, current); JS_FreeValue (ctx, result); return JS_EXCEPTION; @@ -27453,14 +27103,14 @@ js_cell_splat (JSContext *ctx, JSValue this_val, int argc, int tag = JS_VALUE_GET_TAG (val); if (JS_IsObject (val) || JS_IsNumber (val) || tag == JS_TAG_STRING || tag == JS_TAG_STRING_IMM || tag == JS_TAG_BOOL) { - JS_SetProperty (ctx, result, atom, val); + JS_SetProperty (ctx, result, key, val); } else { JS_FreeValue (ctx, val); } } - JS_FreeAtom (ctx, tab[i].atom); + JS_FreeValue (ctx, keys[i]); } - js_free (ctx, tab); + js_free (ctx, keys); JSValue next = JS_GetPrototype (ctx, current); JS_FreeValue (ctx, current); @@ -27473,17 +27123,15 @@ js_cell_splat (JSContext *ctx, JSValue this_val, int argc, JSValue args[1] = { result }; JSValue extra = JS_Call (ctx, to_data, obj, 1, args); if (!JS_IsException (extra) && JS_IsObject (extra)) { - JSPropertyEnum *tab; + JSValue *keys2; uint32_t len; - if (JS_GetOwnPropertyNames (ctx, &tab, &len, extra, - JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) - >= 0) { + if (JS_GetOwnPropertyNames (ctx, &keys2, &len, extra) >= 0) { for (uint32_t i = 0; i < len; i++) { - JSValue val = JS_GetProperty (ctx, extra, tab[i].atom); - JS_SetProperty (ctx, result, tab[i].atom, val); - JS_FreeAtom (ctx, tab[i].atom); + JSValue val = JS_GetProperty (ctx, extra, keys2[i]); + JS_SetProperty (ctx, result, keys2[i], val); + JS_FreeValue (ctx, keys2[i]); } - js_free (ctx, tab); + js_free (ctx, keys2); } } JS_FreeValue (ctx, extra); @@ -28412,27 +28060,25 @@ js_debugger_fn_bytecode (JSContext *ctx, JSValue fn) { } } break; case OP_FMT_key: { - KeyId k = get_u32 (tab + arg_pos); - if (key_is_text (k)) { - JSAtom atom = (JSAtom)key_payload (k); - const char *atom_str = JS_AtomToCString (ctx, atom); - if (atom_str) { + /* Key operand is a cpool index */ + uint32_t key_idx = get_u32 (tab + arg_pos); + if (key_idx < b->cpool_count) { + JSValue key = b->cpool[key_idx]; + const char *key_str = JS_ToCString (ctx, key); + if (key_str) { snprintf (opcode_str + strlen (opcode_str), - sizeof (opcode_str) - strlen (opcode_str), " text:%s", - atom_str); - JS_FreeCString (ctx, atom_str); + sizeof (opcode_str) - strlen (opcode_str), " key:%s", + key_str); + JS_FreeCString (ctx, key_str); } else { snprintf (opcode_str + strlen (opcode_str), - sizeof (opcode_str) - strlen (opcode_str), " text:%u", - atom); + sizeof (opcode_str) - strlen (opcode_str), " key[%u]", + key_idx); } - } else if (key_is_rec (k)) { - snprintf (opcode_str + strlen (opcode_str), - sizeof (opcode_str) - strlen (opcode_str), " rec:%u", - key_payload (k)); } else { snprintf (opcode_str + strlen (opcode_str), - sizeof (opcode_str) - strlen (opcode_str), " key:%u", k); + sizeof (opcode_str) - strlen (opcode_str), " key[%u]", + key_idx); } } break; case OP_FMT_atom_u8: {