From 6c3c4924464a568a037aeb28e346b695cd5c8bf1 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sun, 1 Feb 2026 11:35:40 -0600 Subject: [PATCH] rm jsstring and string_buffer --- docs/memory.md | 15 +- source/quickjs.c | 1181 +++++++++++----------------------------------- source/quickjs.h | 288 +++++------ 3 files changed, 404 insertions(+), 1080 deletions(-) diff --git a/docs/memory.md b/docs/memory.md index 97f9c199..d55ce42f 100644 --- a/docs/memory.md +++ b/docs/memory.md @@ -55,12 +55,21 @@ Objects are heap allocated, referenced by a pointer value. They are all preceded ### 64 bit build 56 bits capacity 1 bit memory reclamation flag: note that this obj has already been moved -3 bit reserved (per object) +2 bit reserved (per object) 1 bit stone: note that this obj is immutable 3 bit type: note the type of the object +1 bit: fwd: note that this obj is a forward linkage -Type 7: Forward -The forward type indicates that the object (an array, blob, pretext, or record) has grown beyond its capacity and is now residing at a new address. The remaining 61 bits contain the address of the enlarged object. Forward linkages are cleaned up by the memory reclaimer. +Last bit ..1: +The forward type indicates that the object (an array, blob, pretext, or record) has grown beyond its capacity and is now residing at a new address. The remaining 63 bits contain the address of the enlarged object. Forward linkages are cleaned up by the memory reclaimer. + +Type 7: C light C object + +Header +Pointer + +Capacity is an ID of a registered C type. +Pointer is a pointer to the opaque C object. Type 0: Array Header diff --git a/source/quickjs.c b/source/quickjs.c index e8a76e1d..30c528cf 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -190,11 +190,17 @@ static inline JS_BOOL JS_VALUE_IS_NUMBER (JSValue v) { #define KEY_GET_STR_BUF_SIZE 256 +int JS_IsPretext(JSValue v) { + if (!JS_IsText(v)) return 0; + JSText *text = (JSText *)JS_VALUE_GET_PTR(v); + return !objhdr_s(text->hdr); +} + /* 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)) { + if (JS_IsText (key)) { const char *cstr = JS_ToCString (ctx, key); if (cstr) { snprintf (buf, buf_size, "%s", cstr); @@ -206,7 +212,6 @@ static inline const char *JS_KeyGetStr (JSContext *ctx, char *buf, return buf; } - JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref) { ref->prev = ctx->top_gc_ref; @@ -290,12 +295,10 @@ typedef enum JSErrorEnum { ARG_SCOPE_END values are reserved. */ #define JS_MAX_LOCAL_VARS 65534 #define JS_STACK_SIZE_MAX 65534 -#define JS_STRING_LEN_MAX ((1 << 30) - 1) +#define JS_STRING_LEN_MAX ((1 << 56) - 1) #define __exception __attribute__ ((warn_unused_result)) -typedef struct JSString JSString; - /* Forward declaration for bytecode freeing */ struct JSFunctionBytecode; static void free_function_bytecode (JSRuntime *rt, @@ -305,7 +308,9 @@ static void free_function_bytecode (JSRuntime *rt, #define JS_VALUE_GET_OBJ(v) ((JSRecord *)JS_VALUE_GET_PTR (v)) #define JS_VALUE_GET_TEXT(v) ((JSText*)JS_VALUE_GET_PTR(v)) #define JS_VALUE_GET_BLOB(v) ((JSBlob*)JS_VALUE_GET_PTR(v)) -#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR (v)) +#define JS_VALUE_GET_FUNCTION(v) ((JSFunction*)JS_VALUE_GET_PTR(v)) +#define JS_VALUE_GET_FRAME(v) ((JSFrame*)JS_VALUE_GET_PTR(v)) +#define JS_VALUE_GET_CODE(v) ((JSFunctionBytecode*)JS_VALUE_GET_PTR(v)) typedef enum { JS_GC_PHASE_NONE, @@ -349,8 +354,6 @@ static void buddy_free(BuddyAllocator *b, void *ptr, size_t size); static void buddy_destroy(BuddyAllocator *b); struct JSRuntime { - JSMallocFunctions mf; - JSMallocState malloc_state; const char *rt_info; /* Buddy allocator for actor memory blocks */ @@ -359,43 +362,8 @@ struct JSRuntime { int class_count; /* size of class_array */ JSClass *class_array; - struct list_head context_list; /* list of JSContext.link */ - /* list of JSGCObjectHeader.link. List of allocated GC objects (used - by the garbage collector) */ - struct list_head gc_obj_list; - /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */ - struct list_head gc_zero_ref_count_list; - struct list_head tmp_obj_list; /* used during GC */ - JSGCPhaseEnum gc_phase : 8; - size_t malloc_gc_threshold; -#ifdef DUMP_LEAKS - struct list_head string_list; /* list of JSString.link */ -#endif - /* stack limitation */ - uintptr_t stack_size; /* in bytes, 0 if no limit */ - uintptr_t stack_top; - uintptr_t stack_limit; /* lower stack limit */ - - JSValue current_exception; - /* true if the current exception cannot be catched */ - BOOL current_exception_is_uncatchable : 8; - /* true if inside an out of memory error, to avoid recursing */ - BOOL in_out_of_memory : 8; - - struct JSStackFrame *current_stack_frame; - struct VMFrame *current_vm_frame; /* for trampoline VM */ - - JSInterruptHandler *interrupt_handler; - void *interrupt_opaque; - /* see JS_SetStripInfo() */ uint8_t strip_flags; - - void *user_opaque; - JSContext *js; - - /* Record-key IDs (for K_REC keys) */ - uint32_t rec_key_next; }; struct JSClass { @@ -478,49 +446,6 @@ typedef enum { JS_GC_OBJ_TYPE_FUNCTION, } JSGCObjectTypeEnum; -/* header for GC objects. GC objects are C data structures with a - reference count that can reference other GC objects. JS Objects are - a particular type of GC object. */ -struct JSGCObjectHeader { - int ref_count; /* must come first, 32-bit */ - JSGCObjectTypeEnum gc_obj_type : 4; - uint8_t mark : 4; /* used by the GC */ - uint8_t dummy1; /* not used by the GC */ - uint16_t dummy2; /* not used by the GC */ - struct list_head link; -}; - -enum mist_obj_type { - OBJ_ARRAY = 0, - OBJ_BLOB = 1, - OBJ_TEXT = 2, - OBJ_RECORD = 3, // js style objects - OBJ_FUNCTION = 4, - OBJ_CODE = 5, - OBJ_FRAME = 6, - OBJ_FORWARD = 7 -}; - -#define OBJHDR_CAP_SHIFT 8u -#define OBJHDR_CAP_MASK (((objhdr_t)1ull << 56) - 1ull) - -#define OBJHDR_S_BIT 3u -#define OBJHDR_P_BIT 4u -#define OBJHDR_A_BIT 5u -#define OBJHDR_R_BIT 7u - -#define OBJHDR_FLAG(bit) ((objhdr_t)1ull << (bit)) -#define OBJHDR_S_MASK OBJHDR_FLAG (OBJHDR_S_BIT) -#define OBJHDR_P_MASK OBJHDR_FLAG (OBJHDR_P_BIT) -#define OBJHDR_A_MASK OBJHDR_FLAG (OBJHDR_A_BIT) -#define OBJHDR_R_MASK OBJHDR_FLAG (OBJHDR_R_BIT) - -typedef uint64_t word_t; // one actor-memory word -typedef uint64_t objhdr_t; // header word -typedef uint64_t objref_t; // 56-bit word address (0 = null) - -static inline uint8_t objhdr_type (objhdr_t h) { return (uint8_t)(h & 7u); } -static inline bool objhdr_s (objhdr_t h) { return (h & OBJHDR_S_MASK) != 0; } static inline objhdr_t objhdr_set_s (objhdr_t h, bool s) { return s ? (h | OBJHDR_S_MASK) : (h & ~OBJHDR_S_MASK); } @@ -604,33 +529,6 @@ static inline uint32_t mist_string_get (const JSText *t, int idx) { return (uint32_t)(t->packed[word_idx] >> shift); } -static inline void mist_string_put (JSText *t, int idx, uint32_t c) { - int word_idx = idx / 2; - int shift = (1 - (idx % 2)) * 32; - word_t mask = (word_t)0xFFFFFFFF << shift; - t->packed[word_idx] = (t->packed[word_idx] & ~mask) | ((word_t)c << shift); -} - -/* Record (type=3): - Open-address table, slots 1..cap used, slot 0 reserved. - Capacity is (2^n - 1) mask; total slots = cap+1 (a power of two). - Layout words: - [hdr(cap=mask)] - [len_fields] - [key1][val1]...[key_cap][val_cap] - Total words = 2*(cap+1) (matches spec). -*/ - - -typedef struct mist_record { - objhdr_t hdr; // capacity is length of slots - word_t length; // number of fields it contains - word_t parent; // objref_t to parent record or 0 - word_t shape; // can be NULL if mapping - mist_slot - slot[]; // slot[0] corresponds to field #1, ..., slot[cap-1] is field #cap -} mist_record; - /* ============================================================ Record key helpers (JSValue keys) ============================================================ */ @@ -722,23 +620,28 @@ static inline uint64_t fash64_hash_one (uint64_t word) { return fash64_end (&s); } -static inline uint32_t JSText_len (const JSText *text) { - if (objhdr_s (text->hdr)) return (uint32_t)objhdr_cap56 (text->hdr); - return (uint32_t)text->length; +static inline word_t JSText_len (const JSText *text) { + if (objhdr_s (text->hdr)) return objhdr_cap56 (text->hdr); + return text->length; } -static inline JS_BOOL JSText_equal (const JSText *a, - const JSText *b) { - uint32_t len_a = JSText_len (a); - uint32_t len_b = JSText_len (b); +static inline uint32_t JSText_get (const JSText *text, word_t idx) { + word_t word_idx = idx >> 1; + int shift = (1 - (idx & 1)) * 32; + return (uint32_t)(text->packed[word_idx] >> shift); +} + +static inline JS_BOOL JSText_equal (const JSText *a, const JSText *b) { + word_t len_a = JSText_len (a); + word_t len_b = JSText_len (b); if (len_a != len_b) return FALSE; size_t word_count = (len_a + 1) / 2; - return memcmp (a->packed, b->packed, word_count * sizeof (uint64_t)) == 0; + return memcmp (a->packed, b->packed, word_count * sizeof (word_t)) == 0; } static JS_BOOL JSText_equal_ascii (const JSText *text, JSValue imm) { int len = MIST_GetImmediateASCIILen (imm); - if ((uint32_t)len != JSText_len (text)) return FALSE; + if (len != JSText_len (text)) return FALSE; for (int i = 0; i < len; i++) { uint32_t c = mist_string_get (text, i); if (c >= 0x80) return FALSE; @@ -753,48 +656,17 @@ static JS_BOOL JSText_equal_ascii (const JSText *text, JSValue imm) { For pre-text (s=0): compute hash on the fly. */ static uint64_t get_text_hash (JSText *text) { if (objhdr_s (text->hdr)) { - /* Stoned text: hash is stored in length field */ + if (text->length != 0) return text->length; + text->length = fash64_hash_words (text->packed, (text->length + 1) / 2, text->length); + if (!text->length) text->length = 1; return text->length; } else { - /* Pre-text: compute hash from packed UTF-32 */ uint64_t len = objhdr_cap56 (text->hdr); size_t word_count = (len + 1) / 2; return fash64_hash_words (text->packed, word_count, len); } } -/* Get hash for a JSValue if it's a text object */ -uint64_t get_value_hash (JSValue val) { - if (!JS_IsPtr (val)) return 0; - - JSText *text = (JSText *)JS_VALUE_GET_PTR (val); - if (objhdr_type (text->hdr) != OBJ_TEXT) return 0; - - return get_text_hash (text); -} - -/* Forward declarations for JSRecord - defined later */ -typedef struct JSRecord JSRecord; -typedef struct JSRecordEntry JSRecordEntry; - -/* Placeholder: these will be replaced with actual implementations after - * JSRecord is defined */ - -/* Forward declaration for bump allocator */ -static void *ctx_alloc(JSContext *ctx, size_t size); - -/* ============================================================ - Stone Arena Allocator - ============================================================ */ - -#define STONE_PAGE_SIZE 65536 - -typedef struct StoneArenaPage { - struct StoneArenaPage *next; - size_t used; - uint8_t data[STONE_PAGE_SIZE]; -} StoneArenaPage; - /* Pack UTF-32 characters into 64-bit words (2 chars per word) */ static void pack_utf32_to_words (const uint32_t *utf32, uint32_t len, uint64_t *packed) { @@ -826,25 +698,6 @@ static JSValue intern_text_to_value (JSContext *ctx, const uint32_t *utf32, static JSValue js_key_new (JSContext *ctx, const char *str); static JSValue js_key_new_len (JSContext *ctx, const char *str, size_t len); -typedef struct JSVarRef { - union { - JSGCObjectHeader header; /* must come first */ - struct { - int __gc_ref_count; /* corresponds to header.ref_count */ - uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ - uint8_t is_detached; - }; - }; - JSValue *pvalue; /* pointer to the value, either on the stack or - to 'value' */ - union { - JSValue value; /* used when is_detached = TRUE */ - struct { - struct list_head var_ref_link; /* JSStackFrame.var_ref_list list */ - }; /* used when is_detached = FALSE */ - }; -} JSVarRef; - /* must be large enough to have a negligible runtime cost and small enough to call the interrupt callback often. */ #define JS_INTERRUPT_COUNTER_INIT 10000 @@ -854,9 +707,9 @@ struct JSContext { struct list_head link; /* Actor memory block (bump allocation) */ - uint8_t *heap_base; /* start of current block */ - uint8_t *heap_free; /* bump pointer */ - uint8_t *heap_end; /* end of block */ + void *heap_base; /* start of current block */ + void *heap_free; /* bump pointer */ + void *heap_end; /* end of block */ size_t current_block_size; /* current block size (64KB initially) */ size_t next_block_size; /* doubles if <10% recovered after GC */ @@ -903,111 +756,21 @@ struct JSContext { int value_stack_top; /* current top index */ int value_stack_capacity; /* allocated capacity */ - /* Stone arena for interned strings (per context) */ - struct StoneArenaPage *st_pages; - struct JSText **st_text_array; /* indexed by ID */ - uint32_t *st_text_hash; /* hash table mapping to IDs */ - uint32_t st_text_count; /* number of interned texts */ - uint32_t st_text_size; /* hash table size */ - uint32_t st_text_resize; /* threshold for resize */ + JSValue current_exception; + BOOL current_exception_is_uncatchable : 8; + BOOL in_out_of_memory : 8; + + JSInterruptHandler *interrupt_handler; + void *interrupt_opaque; }; -/* ============================================================ - Stone Arena Functions (per-context interning) - ============================================================ */ - -/* Allocate bytes from the stone arena (bump allocator, never freed - * individually) */ -static void *st_alloc (JSContext *ctx, size_t bytes, size_t align) { - JSRuntime *rt = ctx->rt; - StoneArenaPage *page = ctx->st_pages; - size_t offset; - - if (page) { - /* Align the current offset */ - offset = (page->used + align - 1) & ~(align - 1); - if (offset + bytes <= STONE_PAGE_SIZE) { - page->used = offset + bytes; - return page->data + offset; - } - } - - /* Need a new page */ - if (bytes > STONE_PAGE_SIZE) { - /* Oversized allocation - allocate exactly */ - page - = js_malloc_rt (rt, sizeof (StoneArenaPage) - STONE_PAGE_SIZE + bytes); - if (!page) return NULL; - page->used = bytes; - } else { - page = js_malloc_rt (rt, sizeof (StoneArenaPage)); - if (!page) return NULL; - page->used = bytes; - } - page->next = ctx->st_pages; - ctx->st_pages = page; - return page->data; -} - -/* Free all stone arena pages (called when freeing the context) */ -static void st_free_all (JSContext *ctx) { - JSRuntime *rt = ctx->rt; - StoneArenaPage *page = ctx->st_pages; - while (page) { - StoneArenaPage *next = page->next; - js_free_rt (rt, page); - page = next; - } - ctx->st_pages = NULL; -} - -/* Resize the stone text intern table */ -static int st_text_resize (JSContext *ctx) { - JSRuntime *rt = ctx->rt; - uint32_t new_size = ctx->st_text_size ? ctx->st_text_size * 2 : 256; - uint32_t new_mask = new_size - 1; - - uint32_t *new_hash = js_mallocz_rt (rt, new_size * sizeof (uint32_t)); - if (!new_hash) return -1; - - JSText **new_array = js_realloc_rt ( - rt, ctx->st_text_array, (new_size + 1) * sizeof (JSText *)); - if (!new_array) { - js_free_rt (rt, new_hash); - return -1; - } - - /* Rehash existing entries */ - for (uint32_t i = 0; i < ctx->st_text_size; i++) { - uint32_t id = ctx->st_text_hash ? ctx->st_text_hash[i] : 0; - if (id != 0) { - JSText *text = ctx->st_text_array[id]; - uint64_t hash = text->length; /* hash stored in length for stoned text */ - uint32_t slot = hash & new_mask; - while (new_hash[slot] != 0) - slot = (slot + 1) & new_mask; - new_hash[slot] = id; - } - } - - js_free_rt (rt, ctx->st_text_hash); - ctx->st_text_hash = new_hash; - ctx->st_text_array = new_array; - ctx->st_text_size = new_size; - ctx->st_text_resize = new_size * 3 / 4; /* 75% load factor */ - - return 0; -} - /* Intern a UTF-32 string as a stone text, returning a JSValue string */ -static JSValue intern_text_to_value (JSContext *ctx, const uint32_t *utf32, - uint32_t len) { +static JSValue intern_text_to_value (JSContext *ctx, const uint32_t *utf32, uint32_t len) { /* Pack UTF-32 for hashing and comparison */ size_t word_count = (len + 1) / 2; uint64_t *packed = alloca (word_count * sizeof (uint64_t)); pack_utf32_to_words (utf32, len, packed); - /* Compute hash */ uint64_t hash = fash64_hash_words (packed, word_count, len); /* Look up in hash table */ @@ -1019,7 +782,7 @@ static JSValue intern_text_to_value (JSContext *ctx, const uint32_t *utf32, JSText *existing = ctx->st_text_array[id]; if (text_equal (existing, packed, len)) { /* Found existing entry */ - return JS_MKPTR (JS_TAG_STRING, existing); + return JS_MKPTR (existing); } slot = (slot + 1) & mask; } @@ -1052,7 +815,7 @@ static JSValue intern_text_to_value (JSContext *ctx, const uint32_t *utf32, ctx->st_text_hash[slot] = new_id; ctx->st_text_array[new_id] = text; - return JS_MKPTR (JS_TAG_STRING, text); + return JS_MKPTR (text); } /* Create a stoned, interned key from a UTF-8 C string. @@ -1146,109 +909,31 @@ typedef union JSFloat64Union { uint32_t u32[2]; } JSFloat64Union; -struct JSString { - JSRefCountHeader header; /* must come first, 32-bit */ - uint32_t pad; /* align to 64-bit */ - objhdr_t hdr; /* mist object header (64-bit) */ - int64_t len; /* length in characters (64-bit) */ // used for hash when stone - uint64_t u[]; /* packed UTF-32 characters */ -}; - -/* JS_IsString implementation: checks for immediate string or OBJ_TEXT pointer. - Both JSText and JSString now have objhdr_t at offset 8. */ -JS_BOOL JS_IsString (JSValue v) { +JS_BOOL JS_IsText (JSValue v) { /* Check tag - immediate string? */ if (MIST_IsImmediateASCII (v)) return 1; /* Check pointer type */ if (JS_IsPtr (v)) { - void *ptr = JS_VALUE_GET_PTR (v); - /* objhdr_t is at offset 8 (after dummy header/pad) */ - objhdr_t hdr = *((objhdr_t *)((char *)ptr + 8)); - return objhdr_type (hdr) == OBJ_TEXT; + objhdr_t *ptr = JS_VALUE_GET_PTR (v); + return objhdr_type (*ptr) == OBJ_TEXT; } return 0; } -JS_BOOL -JS_IsText (JSValue v) { return JS_IsString (v); } - -/* Base misty object - all object types that use mist headers start with this - */ -typedef struct JSMistBase { - JSGCObjectHeader header; - objhdr_t mist_hdr; -} JSMistBase; - -/* Get mist type from a JSValue that points to a misty object */ -static inline uint8_t JS_VALUE_GET_MIST_TYPE (JSValue v) { - JSMistBase *p = (JSMistBase *)JS_VALUE_GET_PTR (v); - return objhdr_type (p->mist_hdr); -} - -/* Check if a JSValue points to a mist-based object (has mist_hdr) */ -static inline JS_BOOL JS_VALUE_IS_MIST (JSValue v) { - if (!JS_IsPtr (v)) return FALSE; - JSMistBase *p = (JSMistBase *)JS_VALUE_GET_PTR (v); - uint8_t type = objhdr_type (p->mist_hdr); - return type != 0; -} - -/* Get gc_obj_type from a JSValue that points to a GC object. - Used to safely distinguish JSRecord from JSArray/JSRecord before casting. */ -static inline JSGCObjectTypeEnum js_gc_obj_type (JSValue v) { - return ((JSGCObjectHeader *)JS_VALUE_GET_PTR (v))->gc_obj_type; -} - /* Helper to get array capacity from mist_hdr */ static inline uint32_t js_array_cap (JSArray *arr) { - return (uint32_t)objhdr_cap56 (arr->mist_hdr); -} - -/* Helper to set array capacity in mist_hdr */ -static inline void js_array_set_cap (JSArray *arr, uint32_t cap) { - arr->mist_hdr = objhdr_make (cap, OBJ_ARRAY, false, false, false, - objhdr_s (arr->mist_hdr)); + return objhdr_cap56 (arr->mist_hdr); } /* JSRegExp: regular expression object data (must come before JSRecord/JSRecord) */ typedef struct JSRegExp { - JSString *pattern; - JSString *bytecode; /* also contains the flags */ + JSText *pattern; + JSText *bytecode; /* also contains the flags */ } JSRegExp; -/* Entry in a JSRecord hash table */ -typedef struct JSRecordEntry { - JSValue key; /* JS_NULL=empty, JS_EXCEPTION=tombstone, else valid key */ - JSValue val; -} JSRecordEntry; - -/* 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. */ -typedef struct JSRecord { - JSGCObjectHeader header; /* must come first */ - objhdr_t mist_hdr; /* type=OBJ_RECORD, cap=mask, stone bit via objhdr_s() */ - struct JSRecord *shape; /* NULL for dict-mode (always NULL for now) */ - struct JSRecord *proto; /* prototype chain, NULL stops walk */ - uint32_t len; /* number of live entries */ - uint32_t tombs; /* number of tombstones */ - uint32_t rec_id; /* unique ID for record-key hashing */ - uint16_t class_id; /* see JS_CLASS_x */ - uint8_t free_mark : 1; /* used when freeing objects with cycles */ - uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ - uint8_t __pad : 6; - uint8_t __pad2; - JSRecordEntry *tab; /* size = (mask+1), index 0 reserved */ - union { - void *opaque; - JSRegExp regexp; /* JS_CLASS_REGEXP */ - } u; -} JSRecord; - -#define rec_is_stone(rec) objhdr_s ((rec)->mist_hdr) -#define rec_set_stone(rec) ((rec)->mist_hdr = objhdr_set_s ((rec)->mist_hdr, true)) +#define obj_is_stone(rec) objhdr_s ((rec)->mist_hdr) +#define obj_set_stone(rec) ((rec)->mist_hdr = objhdr_set_s ((rec)->mist_hdr, true)) #define JS_VALUE_GET_RECORD(v) ((JSRecord *)JS_VALUE_GET_PTR (v)) @@ -1277,24 +962,26 @@ static inline void rec_tab_init (JSRecordEntry *tab, uint32_t mask) { JSRecord Core Operations ============================================================ */ +// can check if key by checking for 0 here static uint64_t js_key_hash (JSValue key) { - if (MIST_IsImmediateASCII (key)) return fash64_hash_one ((uint64_t)key); + if (MIST_IsImmediateASCII (val)) { + uint64_t h = fash64_hash_one (val); + return h ? h : 1; + } + if (!JS_IsPtr (key)) return 0; - void *ptr = JS_VALUE_GET_PTR (key); + objhdr_t *ptr = JS_VALUE_GET_PTR (key); - /* Both JSText and JSString now have objhdr_t at offset 8. - JSRecord and JSArray also have mist_hdr at offset 8. */ - objhdr_t hdr = *((objhdr_t *)((char *)ptr + 8)); - uint8_t type = objhdr_type (hdr); + uint8_t type = objhdr_type (*ptr); if (type == OBJ_TEXT) { /* For JSText (stoned strings), use get_text_hash */ - JSText *text = (JSText *)ptr; + JSText *text = ptr; return get_text_hash (text); } if (type == OBJ_RECORD) { - JSRecord *rec = (JSRecord *)ptr; + JSRecord *rec = ptr; if (rec->rec_id == 0) return 0; return fash64_hash_one (rec->rec_id); } @@ -1321,12 +1008,9 @@ static JS_BOOL js_key_equal (JSValue a, JSValue b) { if (!JS_IsPtr (a) || !JS_IsPtr (b)) return FALSE; - void *pa = JS_VALUE_GET_PTR (a); - void *pb = JS_VALUE_GET_PTR (b); - /* objhdr_t is at offset 8 for all heap objects (JSText, JSString, JSRecord) */ - objhdr_t ha = *((objhdr_t *)((char *)pa + 8)); - objhdr_t hb = *((objhdr_t *)((char *)pb + 8)); - uint8_t type_a = objhdr_type (ha); + objhdr_t *ha = JS_VALUE_GET_PTR (a); + objhdr_t *hb = JS_VALUE_GET_PTR (b); + uint8_t type_a = objhdr_type (*ha); uint8_t type_b = objhdr_type (hb); if (type_a != type_b) return FALSE; @@ -1369,14 +1053,14 @@ static JS_BOOL js_key_equal_str (JSValue a, const char *str) { /* Find slot for a key in record's own table. Returns slot index (>0) if found, or -(insert_slot) if not found. */ static int rec_find_slot (JSRecord *rec, JSValue k) { - uint32_t mask = (uint32_t)objhdr_cap56 (rec->mist_hdr); + uint64_t mask = objhdr_cap56 (rec->mist_hdr); uint64_t h64 = js_key_hash (k); - uint32_t slot = ((uint32_t)h64 & mask); + uint64_t slot = (h64 & mask); if (slot == 0) slot = 1; /* slot 0 is reserved */ - uint32_t first_tomb = 0; + uint64_t first_tomb = 0; - for (uint32_t i = 0; i <= mask; i++) { + for (uint64_t i = 0; i <= mask; i++) { JSValue slot_key = rec->tab[slot].key; if (rec_key_is_empty (slot_key)) { @@ -1425,9 +1109,9 @@ static JSValue rec_get (JSContext *ctx, JSRecord *rec, JSValue k) { } /* Resize record table when load factor too high */ -static int rec_resize (JSContext *ctx, JSRecord *rec, uint32_t new_mask) { - uint32_t old_mask = (uint32_t)objhdr_cap56 (rec->mist_hdr); - uint32_t new_size = new_mask + 1; +static int rec_resize (JSContext *ctx, JSRecord *rec, uint64_t new_mask) { + uint64_t old_mask = objhdr_cap56 (rec->mist_hdr); + uint64_t new_size = new_mask + 1; JSRecordEntry *new_tab = js_mallocz (ctx, sizeof (JSRecordEntry) * new_size); if (!new_tab) return -1; @@ -1513,7 +1197,7 @@ static JSRecord *js_new_record_class (JSContext *ctx, uint32_t initial_mask, size_t tab_size = sizeof(JSRecordEntry) * (initial_mask + 1); size_t total_size = sizeof(JSRecord) + tab_size; - JSRecord *rec = ctx_alloc(ctx, total_size); + JSRecord *rec = js_malloc(ctx, total_size); if (!rec) return NULL; rec->header.ref_count = 1; @@ -1545,43 +1229,6 @@ static JSRecord *js_new_record (JSContext *ctx, uint32_t initial_mask) { /* Forward declaration for remove_gc_object */ static void remove_gc_object (JSGCObjectHeader *h); -/* Free a record's resources (called during GC) */ -static void free_record (JSRuntime *rt, JSRecord *rec) { - JSClassFinalizer *finalizer; - - rec->free_mark = 1; /* used to tell the object is invalid when freeing cycles */ - - /* Class-specific finalization */ - if (rec->class_id != JS_CLASS_OBJECT && rec->class_id < rt->class_count) { - finalizer = rt->class_array[rec->class_id].finalizer; - if (finalizer) (*finalizer) (rt, JS_MKPTR (JS_TAG_OBJECT, rec)); - } - - /* Free all values in the table */ - uint32_t mask = (uint32_t)objhdr_cap56 (rec->mist_hdr); - for (uint32_t i = 1; i <= mask; i++) { - JSValue k = rec->tab[i].key; - if (!rec_key_is_empty (k) && !rec_key_is_tomb (k)) { - JS_FreeValueRT (rt, k); - JS_FreeValueRT (rt, rec->tab[i].val); - } - } - - /* Fail safe */ - rec->class_id = 0; - rec->u.opaque = NULL; - - remove_gc_object (&rec->header); - - /* Free if no strong refs, else queue for zero-ref processing */ - if (rec->header.ref_count == 0) - js_free_rt (rt, rec->tab); - else - list_add_tail (&rec->header.link, &rt->gc_zero_ref_count_list); - - if (rec->header.ref_count == 0) - js_free_rt (rt, rec); -} typedef enum { JS_FUNC_KIND_C, @@ -1863,7 +1510,6 @@ static __maybe_unused void JS_DumpValue (JSContext *ctx, const char *str, static void js_dump_value_write (void *opaque, const char *buf, size_t len); static JSValue js_function_apply (JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic); -static void free_array (JSRuntime *rt, JSArray *arr); static void js_regexp_finalizer (JSRuntime *rt, JSValue val); static void js_varref_object_finalizer (JSRuntime *rt, JSValue val); static void js_varref_object_gc_mark (JSRuntime *rt, JSValue val, @@ -2008,56 +1654,48 @@ void *js_mallocz_rt (JSRuntime *rt, size_t size) { /* Throw out of memory in case of error */ void *js_malloc (JSContext *ctx, size_t size) { - void *ptr; - ptr = js_malloc_rt (ctx->rt, size); - if (unlikely (!ptr)) { + /* Align size to 8 bytes */ + size = (size + 7) & ~7; + + /* Check if we have space in current block */ + if ((uint8_t *)ctx->heap_free + size > (uint8_t *)ctx->heap_end) { + /* TODO: trigger GC or allocate new block */ JS_ThrowOutOfMemory (ctx); return NULL; } + + void *ptr = ctx->heap_free; + ctx->heap_free = (uint8_t *)ctx->heap_free + size; return ptr; } /* Throw out of memory in case of error */ void *js_mallocz (JSContext *ctx, size_t size) { - void *ptr; - ptr = js_mallocz_rt (ctx->rt, size); - if (unlikely (!ptr)) { - JS_ThrowOutOfMemory (ctx); - return NULL; - } - return ptr; + void *ptr = js_malloc (ctx, size); + if (!ptr) return NULL; + return memset (ptr, 0, size); } -void js_free (JSContext *ctx, void *ptr) { js_free_rt (ctx->rt, ptr); } +void js_free (JSContext *ctx, void *ptr) { + assert(0, "bump allocator doesn't free individual allocations"); +} /* Throw out of memory in case of error */ void *js_realloc (JSContext *ctx, void *ptr, size_t size) { - void *ret; - ret = js_realloc_rt (ctx->rt, ptr, size); - if (unlikely (!ret && size != 0)) { - JS_ThrowOutOfMemory (ctx); + if (!ptr) return js_malloc (ctx, size); + if (size == 0) { + js_free (ctx, ptr); return NULL; } - return ret; -} - -/* store extra allocated size in *pslack if successful */ -void *js_realloc2 (JSContext *ctx, void *ptr, size_t size, size_t *pslack) { - void *ret; - ret = js_realloc_rt (ctx->rt, ptr, size); - if (unlikely (!ret && size != 0)) { - JS_ThrowOutOfMemory (ctx); - return NULL; - } - if (pslack) { - size_t new_size = js_malloc_usable_size_rt (ctx->rt, ret); - *pslack = (new_size > size) ? new_size - size : 0; - } - return ret; -} - -size_t js_malloc_usable_size (JSContext *ctx, const void *ptr) { - return js_malloc_usable_size_rt (ctx->rt, ptr); + + /* Bump allocator: allocate new space and copy */ + void *new_ptr = js_malloc (ctx, size); + if (!new_ptr) return NULL; + + /* Copy old data - we don't know old size, so copy up to new size */ + /* Caller must ensure this is safe */ + memcpy (new_ptr, ptr, size); + return new_ptr; } /* Throw out of memory exception in case of error */ @@ -2146,7 +1784,7 @@ static JSValue js_key_from_string (JSContext *ctx, JSValue val) { if (MIST_IsImmediateASCII (val)) { return val; /* Immediates can be used directly as keys */ } - if (JS_IsString (val)) { + if (JS_IsText (val)) { JSString *p = JS_VALUE_GET_STRING (val); int64_t len = p->len; /* Extract UTF-32 characters and intern */ @@ -2375,37 +2013,6 @@ static JSValue gc_copy_value(JSContext *ctx, JSValue v, uint8_t **to_free, uint8 static void gc_scan_object(JSContext *ctx, void *ptr, uint8_t **to_free, uint8_t *to_end); static size_t gc_object_size(void *ptr); -/* Check if pointer is in stone arena (not subject to GC) */ -static int is_stone_ptr(JSContext *ctx, void *ptr) { - StoneArenaPage *page = ctx->st_pages; - while (page) { - if ((uint8_t *)ptr >= page->data && - (uint8_t *)ptr < page->data + STONE_PAGE_SIZE) { - return 1; - } - page = page->next; - } - return 0; -} - -/* Bump allocator - allocate from current heap block */ -static void *ctx_alloc(JSContext *ctx, size_t size) { - size = (size + 7) & ~7; /* 8-byte align */ - - if (ctx->heap_free + size > ctx->heap_end) { - /* Block full - trigger GC */ - if (ctx_gc(ctx) < 0) return NULL; - if (ctx->heap_free + size > ctx->heap_end) { - return NULL; /* Still OOM after GC */ - } - } - - void *ptr = ctx->heap_free; - ctx->heap_free += size; - memset(ptr, 0, size); - return ptr; -} - /* Get size of a heap object based on its type */ static size_t gc_object_size(void *ptr) { /* All mist objects have header at offset 8 (after JSGCObjectHeader) */ @@ -2460,7 +2067,7 @@ static JSValue gc_copy_value(JSContext *ctx, JSValue v, uint8_t **to_free, uint8 if (objhdr_type(hdr) == OBJ_FORWARD) { /* Extract forwarding address from cap56 field */ void *new_ptr = (void *)(uintptr_t)objhdr_cap56(hdr); - return JS_MKPTR(JS_TAG_PTR, new_ptr); + return JS_MKFWD(new_ptr); } /* Copy object to new space */ @@ -2756,61 +2363,28 @@ void JS_SetInterruptHandler (JSRuntime *rt, JSInterruptHandler *cb, } void JS_SetStripInfo (JSRuntime *rt, int flags) { rt->strip_flags = flags; } - int JS_GetStripInfo (JSRuntime *rt) { return rt->strip_flags; } -/* atom_get_free/is_free/set_free removed */ - /* Allocate a string using bump allocation from context heap. Note: the string contents are uninitialized */ -static JSString *js_alloc_string (JSContext *ctx, int max_len) { - JSString *str; +static JSText *js_alloc_string (JSContext *ctx, int max_len) { + JSText *str; /* Allocate packed UTF-32: 2 chars per 64-bit word. */ size_t data_words = (max_len + 1) / 2; - size_t size = sizeof(JSString) + data_words * sizeof(uint64_t); + size_t size = sizeof(JSText) + data_words * sizeof(uint64_t); - str = ctx_alloc(ctx, size); + str = js_malloc(ctx, size); if (unlikely(!str)) { JS_ThrowOutOfMemory(ctx); return NULL; } - - str->header.ref_count = 1; - str->pad = 0; - /* Initialize objhdr_t with OBJ_TEXT type so JS_IsString can detect it */ + /* Initialize objhdr_t with OBJ_TEXT type so JS_IsText can detect it */ str->hdr = objhdr_make(max_len, OBJ_TEXT, false, false, false, false); str->len = max_len; return str; } -/* Legacy function for runtime-level string allocation (for stone arena use) */ -static JSString *js_alloc_string_rt (JSRuntime *rt, int max_len) { - /* For stone arena strings, use js_malloc_rt since they're not GC'd */ - size_t data_words = (max_len + 1) / 2; - size_t size = sizeof(JSString) + data_words * sizeof(uint64_t); - - JSString *str = js_malloc_rt(rt, size); - if (unlikely(!str)) return NULL; - - str->header.ref_count = 1; - str->pad = 0; - str->hdr = objhdr_make(max_len, OBJ_TEXT, false, false, false, false); - str->len = max_len; - - return str; -} - -/* same as JS_FreeValueRT() but faster */ -static inline void js_free_string (JSRuntime *rt, JSString *str) { - if (--str->header.ref_count <= 0) { -#ifdef DUMP_LEAKS - list_del (&str->link); -#endif - js_free_rt (rt, str); - } -} - static inline void JS_MarkValueEdgeEx (JSRuntime *rt, JSValue val, JSGCObjectHeader *parent, const char *edge, uint32_t atom, @@ -3164,9 +2738,6 @@ void JS_UpdateStackTop (JSRuntime *rt) { static JSString *js_alloc_string (JSContext *ctx, int max_len); -/* return the max count from the hash size */ -#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) - static inline int is_num (int c) { return c >= '0' && c <= '9'; } /* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */ @@ -3199,27 +2770,6 @@ static inline BOOL is_num_string (uint32_t *pval, const JSString *p) { } } -/* XXX: could use faster version ? */ -static inline uint32_t hash_string8 (const uint8_t *str, size_t len, - uint32_t h) { - size_t i; - - for (i = 0; i < len; i++) - h = h * 263 + str[i]; - return h; -} - -static uint32_t hash_string (const JSString *str, uint32_t h) { - /* hash_string32 inlined and adapted */ - size_t i; - for (i = 0; i < str->len; i++) { - h = h * 263 + string_get (str, i); - } - return h; -} - -/* hash_string_rope deleted */ - static __maybe_unused void JS_DumpChar (FILE *fo, int c, int sep) { if (c == sep || c == '\\') { fputc ('\\', fo); @@ -3347,10 +2897,11 @@ static JSValue js_new_string8_len (JSContext *ctx, const char *buf, int len) { /* Fall back to heap string */ str = js_alloc_string (ctx, len); - if (!str) return JS_EXCEPTION; + if (!str) return JS_ThrowMemoryError(ctx); for (i = 0; i < len; i++) - string_put (str, i, (uint8_t)buf[i]); - return JS_MKPTR (JS_TAG_STRING, str); + string_put (str, i, buf[i]); + + return JS_MKPTR(str); } static JSValue js_new_string8 (JSContext *ctx, const char *buf) { @@ -3365,17 +2916,7 @@ static JSValue js_new_string16_len (JSContext *ctx, const uint16_t *buf, if (!str) return JS_EXCEPTION; for (i = 0; i < len; i++) string_put (str, i, buf[i]); - return JS_MKPTR (JS_TAG_STRING, str); -} - -static JSValue js_new_string_char (JSContext *ctx, uint16_t c) { - if (c < 0x100) { - uint8_t ch8 = c; - return js_new_string8_len (ctx, (const char *)&ch8, 1); - } else { - uint16_t ch16 = c; - return js_new_string16_len (ctx, &ch16, 1); - } + return JS_MKPTR(str); } static JSValue js_sub_string (JSContext *ctx, JSString *p, int start, @@ -3383,63 +2924,13 @@ static JSValue js_sub_string (JSContext *ctx, JSString *p, int start, int i; int len = end - start; if (start == 0 && end == p->len) { - return JS_DupValue (ctx, JS_MKPTR (JS_TAG_STRING, p)); + return JS_DupValue (ctx, JS_MKPTR(p)); } JSString *str = js_alloc_string (ctx, len); if (!str) return JS_EXCEPTION; for (i = 0; i < len; i++) string_put (str, i, string_get (p, start + i)); - return JS_MKPTR (JS_TAG_STRING, str); -} - -typedef struct StringBuffer { - JSContext *ctx; - JSString *str; - int len; - int size; - /* int is_wide_char; removed */ - int error_status; -} StringBuffer; - -/* It is valid to call string_buffer_end() and all string_buffer functions even - if string_buffer_init() or another string_buffer function returns an error. - If the error_status is set, string_buffer_end() returns JS_EXCEPTION. - */ -static int string_buffer_init2 (JSContext *ctx, StringBuffer *s, int size, - int is_wide) { - s->ctx = ctx; - s->size = size; - s->len = 0; - /* s->is_wide_char = is_wide; removed */ - s->error_status = 0; - s->str = js_alloc_string (ctx, size); - if (unlikely (!s->str)) { - s->size = 0; - return s->error_status = -1; - } -#ifdef DUMP_LEAKS - /* the StringBuffer may reallocate the JSString, only link it at the end */ - list_del (&s->str->link); -#endif - return 0; -} - -static inline int string_buffer_init (JSContext *ctx, StringBuffer *s, - int size) { - return string_buffer_init2 (ctx, s, size, 0); -} - -static void string_buffer_free (StringBuffer *s) { - js_free (s->ctx, s->str); - s->str = NULL; -} - -static int string_buffer_set_error (StringBuffer *s) { - js_free (s->ctx, s->str); - s->str = NULL; - s->size = 0; - s->len = 0; - return s->error_status = -1; + return JS_MKPTR(str); } /* string_buffer_widen removed */ @@ -3588,7 +3079,7 @@ static int string_buffer_concat_value (StringBuffer *s, JSValue v) { buf[i] = MIST_GetImmediateASCIIChar (v, i); return string_buffer_write8 (s, (const uint8_t *)buf, len); } - if (JS_IsString (v)) { + if (JS_IsText (v)) { JSString *p = JS_VALUE_GET_STRING (v); return string_buffer_concat (s, p, 0, p->len); } @@ -3611,6 +3102,72 @@ static int string_buffer_concat_value (StringBuffer *s, JSValue v) { return res; } +static inline void mist_string_put(JSText *text, uint32_t utf32) +{ + int word_idx = text->len / 2; + int shift = (1 - (text->len % 2)) * 32; + word_t mask = (word_t)0xFFFFFFFF << shift; + text->packed[word_idx] = (text->packed[word_idx] & ~mask) | ((word_t)utf32 << shift); + text->length++; +} + +static JSText *text_puts(JSContext *ctx, JSText *text, const char *str) +{ + if (objhdr_s(text->hdr)) return NULL; + int len = strlen(str); + if (text->len + len > objhdr_cap56(text->hdr)) { + text = js_alloc_string(ctx, (text->len + len) * 2); + if (!text) return NULL; + } + for (int i = 0; i < len; i++) + mist_string_put(text, str[i]); + return text; +} + +static JSText *text_putc(JSContext *ctx, JSText *text, uint32_t c) +{ + if (objhdr_s(text->hdr)) return NULL; + if (text->len + 1 > objhdr_cap56(text->hdr)) { + text = js_alloc_string(ctx, (text->len + 1) * 2); + if (!text) return NULL; + } + mist_string_put(text, c); + return text; +} + +static JSText *text_concat(JSContext *ctx, JSText *text, JSValue v) +{ + if (objhdr_s(text->hdr)) return NULL; + + if (MIST_IsImmediateASCII(v)) { + int len = MIST_GetImmediateASCIILen(v); + word_t text_len = JSText_len(text); + if (text_len + len > objhdr_cap56(text->hdr)) { + text = js_alloc_string(ctx, (text_len + len) * 2); + if (!text) return NULL; + } + for (int i = 0; i < len; i++) + mist_string_put(text, MIST_GetImmediateASCIIChar(v, i)); + + return text; + } + + if (JS_IsText(v)) { + JSText *src = (JSText *)JS_VALUE_GET_PTR(v); + word_t src_len = JSText_len(src); + word_t text_len = JSText_len(text); + if (text_len + src_len > objhdr_cap56(text->hdr)) { + text = js_alloc_string(ctx, (text_len + src_len) * 2); + if (!text) return NULL; + } + for (int i = 0; i < src_len; i++) + mist_string_put(text, JSText_get(src, i)); + return text; + } + + return NULL; +} + static int string_buffer_concat_value_free (StringBuffer *s, JSValue v) { JSString *p; int res; @@ -3630,15 +3187,12 @@ static int string_buffer_concat_value_free (StringBuffer *s, JSValue v) { return res; } -static int string_buffer_fill (StringBuffer *s, int c, int count) { - /* XXX: optimize */ - if (s->len + count > s->size) { - if (string_buffer_realloc (s, s->len + count, c)) return -1; - } - while (count-- > 0) { - if (string_buffer_putc16 (s, c)) return -1; - } - return 0; +static JSValue text_concat_end(JSText *text) { + if (objhdr_s(text->hdr)) return JS_EXCEPTION; + objhdr_set_cap56(&text->hdr, text->len); + text->length = 0; + obj_set_stone(&text->hdr); + return JS_MKPTR(text); } static JSValue string_buffer_end (StringBuffer *s) { @@ -3666,7 +3220,7 @@ static JSValue string_buffer_end (StringBuffer *s) { /* str->is_wide_char = s->is_wide_char; removed */ str->len = s->len; s->str = NULL; - return JS_MKPTR (JS_TAG_STRING, str); + return JS_MKPTR(str); } /* Count leading ASCII characters in buffer */ @@ -3679,57 +3233,17 @@ static size_t count_ascii (const uint8_t *buf, size_t len) { /* create a string from a UTF-8 buffer */ JSValue JS_NewStringLen (JSContext *ctx, const char *buf, size_t buf_len) { - const uint8_t *p, *p_end, *p_start, *p_next; - uint32_t c; - StringBuffer b_s, *b = &b_s; - size_t len1; - - p_start = (const uint8_t *)buf; - p_end = p_start + buf_len; - len1 = count_ascii (p_start, buf_len); - p = p_start + len1; - if (len1 > JS_STRING_LEN_MAX) + if (buf_len > JS_STRING_LEN_MAX) return JS_ThrowInternalError (ctx, "string too long"); - if (p == p_end) { - /* ASCII string */ - return js_new_string8_len (ctx, buf, buf_len); - } else { - if (string_buffer_init (ctx, b, buf_len)) goto fail; - string_buffer_write8 (b, p_start, len1); - while (p < p_end) { - if (*p < 128) { - string_buffer_putc8 (b, *p++); - } else { - /* parse utf-8 sequence, return 0xFFFFFFFF for error */ - c = unicode_from_utf8 (p, p_end - p, &p_next); - if (c < 0x10000) { - p = p_next; - } else if (c <= 0x10FFFF) { - p = p_next; - /* surrogate pair */ - string_buffer_putc16 (b, get_hi_surrogate (c)); - c = get_lo_surrogate (c); - } else { - /* invalid char */ - c = 0xfffd; - /* skip the invalid chars */ - /* XXX: seems incorrect. Why not just use c = *p++; ? */ - while (p < p_end && (*p >= 0x80 && *p < 0xc0)) - p++; - if (p < p_end) { - p++; - while (p < p_end && (*p >= 0x80 && *p < 0xc0)) - p++; - } - } - string_buffer_putc16 (b, c); - } - } - } - return string_buffer_end (b); -fail: - string_buffer_free (b); + if (len <= MIST_ASCII_MAX_LEN) { + JSValue ret = MIST_TryNewImmediateASCII(buf, buf_len); + if (!JS_IsNull(ret)) return ret; + } + + JSValue ret = js_new_string8_len(ctx, buf, buf_len); + if (!JS_IsNull(ret)) return text_concat_end(ret); + return JS_EXCEPTION; } @@ -3854,7 +3368,7 @@ static JSValue JS_ConcatString1 (JSContext *ctx, const JSString *p1, for (i = 0; i < p2->len; i++) string_put (p, p1->len + i, string_get (p2, i)); } - return JS_MKPTR (JS_TAG_STRING, p); + return JS_MKPTR(p); } static BOOL JS_ConcatStringInPlace (JSContext *ctx, JSString *p1, @@ -3924,14 +3438,14 @@ static int js_string_compare_value (JSContext *ctx, JSValue op1, JSValue op2, } static JSValue JS_ConcatString (JSContext *ctx, JSValue op1, JSValue op2) { - if (unlikely (!JS_IsString (op1))) { + if (unlikely (!JS_IsText (op1))) { op1 = JS_ToStringFree (ctx, op1); if (JS_IsException (op1)) { JS_FreeValue (ctx, op2); return JS_EXCEPTION; } } - if (unlikely (!JS_IsString (op2))) { + if (unlikely (!JS_IsText (op2))) { op2 = JS_ToStringFree (ctx, op2); if (JS_IsException (op2)) { JS_FreeValue (ctx, op1); @@ -3979,7 +3493,7 @@ static JSValue JS_ConcatString (JSContext *ctx, JSValue op1, JSValue op2) { 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); + ret_val = JS_MKPTR(p); } JS_FreeValue (ctx, op1); @@ -4005,7 +3519,7 @@ JSValue JS_NewObjectProtoClass (JSContext *ctx, JSValue proto_val, rec->proto = JS_VALUE_GET_RECORD (proto_val); } - return JS_MKPTR (JS_TAG_OBJECT, rec); + return JS_MKPTR(rec); } JSValue JS_NewObjectClass (JSContext *ctx, int class_id) { @@ -4026,7 +3540,7 @@ JSValue JS_NewArrayLen (JSContext *ctx, uint32_t len) { size_t values_size = sizeof(JSValue) * cap; size_t total_size = sizeof(JSArray) + values_size; - arr = ctx_alloc(ctx, total_size); + arr = js_malloc(ctx, total_size); if (!arr) return JS_EXCEPTION; arr->header.ref_count = 1; @@ -4086,7 +3600,7 @@ static int js_method_set_properties (JSContext *ctx, JSValue func_obj, if (JS_VALUE_GET_TAG (func_obj) != JS_TAG_FUNCTION) return -1; JSFunction *f = JS_VALUE_GET_FUNCTION (func_obj); /* name is now JSValue text */ - if (JS_IsString (name)) { f->name = JS_DupValue (ctx, name); } + if (JS_IsText (name)) { f->name = JS_DupValue (ctx, name); } return 0; } @@ -4209,32 +3723,6 @@ static void js_varref_object_gc_mark (JSRuntime *rt, JSValue val, } } -/* Free intrinsic array (JS_TAG_ARRAY) */ -static void free_array (JSRuntime *rt, JSArray *arr) { - assert (arr->header.gc_obj_type == JS_GC_OBJ_TYPE_ARRAY); - - arr->free_mark = 1; /* used to tell the array is invalid when - freeing cycles */ - - uint32_t i; - for (i = 0; i < arr->len; i++) { - JS_FreeValueRT (rt, arr->values[i]); - } - js_free_rt (rt, arr->values); - - /* fail safe */ - arr->values = NULL; - arr->len = 0; - js_array_set_cap (arr, 0); - - remove_gc_object (&arr->header); - /* free if no strong refs, else queue for zero-ref processing */ - if (arr->header.ref_count == 0) - js_free_rt (rt, arr); - else - list_add_tail (&arr->header.link, &rt->gc_zero_ref_count_list); -} - static int js_intrinsic_array_ensure_capacity (JSContext *ctx, JSArray *arr, uint32_t min_cap) { uint32_t old_cap = js_array_cap (arr); @@ -4306,67 +3794,7 @@ static JSValue js_new_function (JSContext *ctx, JSFunctionKind kind) { func->name = JS_NULL; func->length = 0; func->free_mark = 0; - return JS_MKPTR (JS_TAG_FUNCTION, func); -} - -/* Free intrinsic function (JS_TAG_FUNCTION) */ -static void free_function (JSRuntime *rt, JSFunction *func) { - assert (func->header.gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION); - - func->free_mark = 1; /* used to tell the function is invalid when - freeing cycles */ - - JS_FreeValueRT (rt, func->name); - - switch (func->kind) { - case JS_FUNC_KIND_C: - if (func->u.cfunc.realm) JS_FreeContext (func->u.cfunc.realm); - break; - case JS_FUNC_KIND_BYTECODE: - if (func->u.func.function_bytecode) { - JSFunctionBytecode *b = func->u.func.function_bytecode; - JSVarRef **var_refs = func->u.func.var_refs; - if (var_refs) { - for (int i = 0; i < b->closure_var_count; i++) - free_var_ref (rt, var_refs[i]); - js_free_rt (rt, var_refs); - } - JS_FreeValueRT (rt, JS_MKPTR (JS_TAG_FUNCTION_BYTECODE, b)); - } - break; - case JS_FUNC_KIND_BOUND: - if (func->u.bound_function) { - JSBoundFunction *bf = func->u.bound_function; - JS_FreeValueRT (rt, bf->func_obj); - JS_FreeValueRT (rt, bf->this_val); - for (int i = 0; i < bf->argc; i++) { - JS_FreeValueRT (rt, bf->argv[i]); - } - js_free_rt (rt, bf); - } - break; - case JS_FUNC_KIND_C_DATA: - if (func->u.c_function_data_record) { - JSCFunctionDataRecord *s = func->u.c_function_data_record; - for (int i = 0; i < s->data_len; i++) { - JS_FreeValueRT (rt, s->data[i]); - } - js_free_rt (rt, s); - } - break; - default: - break; - } - - func->u.c_function_data_record = NULL; - func->length = 0; - func->name = JS_NULL; - - remove_gc_object (&func->header); - if (func->header.ref_count == 0) - js_free_rt (rt, func); - else - list_add_tail (&func->header.link, &rt->gc_zero_ref_count_list); + return JS_MKPTR(func); } static void mark_function_children (JSRuntime *rt, JSFunction *func, @@ -4384,7 +3812,7 @@ static void mark_function_children (JSRuntime *rt, JSFunction *func, if (var_ref) { mark_func (rt, &var_ref->header); } } } - JS_MarkValue (rt, JS_MKPTR (JS_TAG_FUNCTION_BYTECODE, b), mark_func); + JS_MarkValue (rt, JS_MKPTR(b), mark_func); } break; case JS_FUNC_KIND_BOUND: @@ -4428,7 +3856,7 @@ static void mark_function_children_decref (JSRuntime *rt, JSFunction *func) { } } } - JS_MarkValueEdge (rt, JS_MKPTR (JS_TAG_FUNCTION_BYTECODE, b), + JS_MarkValueEdge (rt, JS_MKPTR(b), &func->header, "func.bytecode"); } break; @@ -4559,8 +3987,7 @@ void __JS_FreeValue (JSContext *ctx, JSValue v) { /* garbage collection */ -static void add_gc_object (JSRuntime *rt, JSGCObjectHeader *h, - JSGCObjectTypeEnum type) { +static void add_gc_object (JSRuntime *rt, JSGCObjectHeader *h, JSGCObjectTypeEnum type) { h->mark = 0; h->gc_obj_type = type; list_add_tail (&h->link, &rt->gc_obj_list); @@ -4622,7 +4049,7 @@ static void mark_children (JSRuntime *rt, JSGCObjectHeader *gp, /* Class-specific marking */ if (rec->class_id != JS_CLASS_OBJECT && rec->class_id < rt->class_count) { JSClassGCMark *gc_mark = rt->class_array[rec->class_id].gc_mark; - if (gc_mark) gc_mark (rt, JS_MKPTR (JS_TAG_OBJECT, rec), mark_func); + if (gc_mark) gc_mark (rt, JS_MKPTR(rec), mark_func); } /* Mark hash table entries */ uint32_t mask = (uint32_t)objhdr_cap56 (rec->mist_hdr); @@ -4739,7 +4166,7 @@ static void mark_children_decref (JSRuntime *rt, JSGCObjectHeader *gp) { if (rec->class_id != JS_CLASS_OBJECT && rec->class_id < rt->class_count) { JSClassGCMark *gc_mark = rt->class_array[rec->class_id].gc_mark; if (gc_mark) { - gc_mark (rt, JS_MKPTR (JS_TAG_OBJECT, rec), gc_decref_child); + gc_mark (rt, JS_MKPTR(rec), gc_decref_child); } } /* Mark hash table entries */ @@ -5304,7 +4731,7 @@ static const char *get_prop_string (JSContext *ctx, JSValue obj, } JSValue val = rec->tab[slot].val; - if (!JS_IsString (val)) return NULL; + if (!JS_IsText (val)) return NULL; return JS_ToCString (ctx, val); } @@ -5322,7 +4749,7 @@ static const char *get_func_name (JSContext *ctx, JSValue func) { if (slot <= 0) return NULL; JSValue val = rec->tab[slot].val; - if (!JS_IsString (val)) return NULL; + if (!JS_IsText (val)) return NULL; return JS_ToCString (ctx, val); } @@ -5615,7 +5042,7 @@ JSValue JS_GetPrototype (JSContext *ctx, JSValue obj) { if (!p) val = JS_NULL; else - val = JS_DupValue (ctx, JS_MKPTR (JS_TAG_OBJECT, p)); + val = JS_DupValue (ctx, JS_MKPTR(p)); } else { /* Primitives have no prototype */ val = JS_NULL; @@ -5667,7 +5094,7 @@ JSValue JS_GetOwnPropertyNames (JSContext *ctx, JSValue obj) { /* Add string keys to array */ 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 (!rec_key_is_empty (k) && !rec_key_is_tomb (k) && JS_IsText (k)) { JS_SetPropertyUint32 (ctx, arr, count, JS_DupValue (ctx, k)); count++; } @@ -5710,9 +5137,9 @@ int JS_HasProperty (JSContext *ctx, JSValue obj, JSValue prop) { p = JS_VALUE_GET_OBJ (obj); for (;;) { /* JS_GetOwnPropertyInternal can free the prototype */ - JS_DupValue (ctx, JS_MKPTR (JS_TAG_OBJECT, p)); + JS_DupValue (ctx, JS_MKPTR(p)); ret = JS_GetOwnPropertyInternal (ctx, NULL, p, prop); - JS_FreeValue (ctx, JS_MKPTR (JS_TAG_OBJECT, p)); + JS_FreeValue (ctx, JS_MKPTR(p)); if (ret != 0) return ret; p = JS_OBJ_GET_PROTO (p); if (!p) break; @@ -5762,7 +5189,7 @@ static JSValue JS_GetPropertyValue (JSContext *ctx, JSValue this_obj, } /* Check for string property (immediate or heap) */ - if (JS_IsString (prop)) { + if (JS_IsText (prop)) { /* Intrinsic arrays don't support string keys */ if (js_is_array (this_obj)) { JS_FreeValue (ctx, prop); @@ -5821,7 +5248,7 @@ JSValue JS_GetPropertyNumber (JSContext *js, JSValue obj, int idx) { return JS_DupValue (js, a->values[idx]); } - if (JS_IsString (obj)) { + if (JS_IsText (obj)) { uint32_t len = js_string_get_length (obj); if (idx < 0 || (uint32_t)idx >= len) { return JS_NULL; } return js_sub_string (js, JS_VALUE_GET_STRING (obj), idx, idx + 1); @@ -5876,7 +5303,7 @@ static JSValue JS_Invoke (JSContext *ctx, JSValue this_val, JSValue method, Deletion fails only if object is stone. */ static int delete_property (JSContext *ctx, JSRecord *rec, JSValue key) { /* Cannot delete from a stone object */ - if (rec_is_stone (rec)) return FALSE; + if (obj_is_stone (rec)) return FALSE; int slot = rec_find_slot (rec, key); if (slot < 0) { @@ -5912,7 +5339,7 @@ int JS_SetProperty (JSContext *ctx, JSValue this_obj, JSValue prop, /* All objects are now records - use record set */ JSRecord *rec = (JSRecord *)JS_VALUE_GET_OBJ (this_obj); - if (unlikely (rec_is_stone (rec))) { + if (unlikely (obj_is_stone (rec))) { JS_FreeValue (ctx, val); JS_ThrowTypeError (ctx, "object is stone"); return -1; @@ -5974,7 +5401,7 @@ static int JS_SetPropertyValue (JSContext *ctx, JSValue this_obj, JSValue prop, return 0; } - if (JS_IsString (prop)) { + if (JS_IsText (prop)) { JSValue key = js_key_from_string (ctx, prop); JS_FreeValue (ctx, prop); return JS_SetProperty (ctx, this_obj, key, val); @@ -5999,7 +5426,7 @@ JSValue JS_GetPropertyKey (JSContext *ctx, JSValue this_obj, JSValue key) { } /* For string keys, create an interned key and use JS_GetProperty */ - if (JS_IsString (key)) { + if (JS_IsText (key)) { JSValue prop_key = js_key_from_string (ctx, key); return JS_GetProperty (ctx, this_obj, prop_key); } @@ -6017,7 +5444,7 @@ int JS_SetPropertyKey (JSContext *ctx, JSValue this_obj, JSValue key, return -1; } JSRecord *rec = JS_VALUE_GET_RECORD (this_obj); - if (rec_is_stone (rec)) { + if (obj_is_stone (rec)) { JS_FreeValue (ctx, val); JS_ThrowTypeError (ctx, "cannot modify frozen object"); return -1; @@ -6026,7 +5453,7 @@ int JS_SetPropertyKey (JSContext *ctx, JSValue this_obj, JSValue key, } /* For string keys, create an interned key */ - if (JS_IsString (key)) { + if (JS_IsText (key)) { JSValue prop_key = js_key_from_string (ctx, key); return JS_SetPropertyInternal (ctx, this_obj, prop_key, val); } @@ -6049,7 +5476,7 @@ int JS_HasPropertyKey (JSContext *ctx, JSValue obj, JSValue key) { } /* For string keys, create an interned key */ - if (JS_IsString (key)) { + if (JS_IsText (key)) { JSValue prop_key = js_key_from_string (ctx, key); return JS_HasProperty (ctx, obj, prop_key); } @@ -6063,7 +5490,7 @@ int JS_DeletePropertyKey (JSContext *ctx, JSValue obj, JSValue key) { if (js_is_record (key)) { if (!js_is_record (obj)) return FALSE; JSRecord *rec = JS_VALUE_GET_RECORD (obj); - if (rec_is_stone (rec)) { + if (obj_is_stone (rec)) { JS_ThrowTypeError (ctx, "cannot modify frozen object"); return -1; } @@ -6080,7 +5507,7 @@ int JS_DeletePropertyKey (JSContext *ctx, JSValue obj, JSValue key) { } /* For string keys, create an interned key */ - if (JS_IsString (key)) { + if (JS_IsText (key)) { JSValue prop_key = js_key_from_string (ctx, key); return JS_DeleteProperty (ctx, obj, prop_key); } @@ -6149,7 +5576,7 @@ static int JS_CheckDefineGlobalVar (JSContext *ctx, JSValue prop, int flags) { if (flags & DEFINE_GLOBAL_LEX_VAR) { if (slot > 0) goto fail_redeclaration; } else { - if (slot <= 0 && rec_is_stone (rec)) { + if (slot <= 0 && obj_is_stone (rec)) { char buf[KEY_GET_STR_BUF_SIZE]; JS_ThrowTypeError (ctx, "cannot define variable '%s'", JS_KeyGetStr (ctx, buf, sizeof (buf), prop)); @@ -6182,7 +5609,7 @@ static int JS_DefineGlobalVar (JSContext *ctx, JSValue prop, int def_flags) { } slot = rec_find_slot (rec, prop); if (slot > 0) return 0; /* already defined */ - if (rec_is_stone (rec)) return 0; + if (obj_is_stone (rec)) return 0; return rec_set_own (ctx, rec, prop, val); } @@ -6323,7 +5750,7 @@ int JS_DeleteProperty (JSContext *ctx, JSValue obj, JSValue prop) { } rec = (JSRecord *)JS_VALUE_GET_OBJ (obj); - if (rec_is_stone (rec)) { + if (obj_is_stone (rec)) { JS_ThrowTypeError (ctx, "cannot delete property of stone object"); return -1; } @@ -7177,7 +6604,6 @@ static JSValue JS_ToQuotedString (JSContext *ctx, JSValue val1) { return string_buffer_end (b); fail: JS_FreeValue (ctx, val); - string_buffer_free (b); return JS_EXCEPTION; } @@ -7339,20 +6765,20 @@ static void js_print_object (JSPrintValueState *s, JSRecord *p) { is_array = FALSE; if (p->class_id == JS_CLASS_REGEXP && s->ctx && !s->options.raw_dump) { JSValue str - = js_regexp_toString (s->ctx, JS_MKPTR (JS_TAG_OBJECT, p), 0, NULL); + = js_regexp_toString (s->ctx, JS_MKPTR(p), 0, NULL); if (JS_IsException (str)) goto default_obj; js_print_raw_string (s, str); JS_FreeValueRT (s->rt, str); comma_state = 2; } else if (p->class_id == JS_CLASS_ERROR && s->ctx && !s->options.raw_dump) { JSValue str - = js_error_toString (s->ctx, JS_MKPTR (JS_TAG_OBJECT, p), 0, NULL); + = js_error_toString (s->ctx, JS_MKPTR(p), 0, NULL); if (JS_IsException (str)) goto default_obj; js_print_raw_string (s, str); JS_FreeValueRT (s->rt, str); /* dump the stack if present */ - str = JS_GetProperty (s->ctx, JS_MKPTR (JS_TAG_OBJECT, p), JS_KEY_stack); - if (JS_IsString (str)) { + str = JS_GetProperty (s->ctx, JS_MKPTR(p), JS_KEY_stack); + if (JS_IsText (str)) { js_putc (s, '\n'); js_print_raw_string2 (s, str, TRUE); } @@ -7426,7 +6852,7 @@ static void js_print_value (JSPrintValueState *s, JSValue val) { case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: { JSFunctionBytecode *b = (JSFunctionBytecode *)p; js_puts (s, "[bytecode "); - if (JS_IsString (b->func_name)) { + if (JS_IsText (b->func_name)) { js_print_value (s, b->func_name); } else { js_puts (s, ""); @@ -7436,13 +6862,13 @@ static void js_print_value (JSPrintValueState *s, JSValue val) { case JS_GC_OBJ_TYPE_FUNCTION: { JSFunction *f = (JSFunction *)p; js_puts (s, "[Function"); - if (JS_IsString (f->name)) { + if (JS_IsText (f->name)) { js_putc (s, ' '); js_print_value (s, f->name); } else if (f->kind == JS_FUNC_KIND_BYTECODE && f->u.func.function_bytecode) { JSFunctionBytecode *b = f->u.func.function_bytecode; - if (JS_IsString (b->func_name)) { + if (JS_IsText (b->func_name)) { js_putc (s, ' '); js_print_value (s, b->func_name); } @@ -7614,7 +7040,7 @@ static __maybe_unused void JS_DumpObject (JSRuntime *rt, JSRecord *rec) { options.show_hidden = TRUE; options.raw_dump = TRUE; JS_PrintValueRT (rt, js_dump_value_write, stdout, - JS_MKPTR (JS_TAG_OBJECT, rec), &options); + JS_MKPTR(rec), &options); printf ("\n"); } @@ -8255,7 +7681,7 @@ static JSValue js_closure (JSContext *ctx, JSValue bfunc, } f = JS_VALUE_GET_FUNCTION (func_obj); /* Use bytecode func_name if valid, otherwise empty string */ - f->name = JS_IsString (b->func_name) ? JS_DupValue (ctx, b->func_name) + f->name = JS_IsText (b->func_name) ? JS_DupValue (ctx, b->func_name) : JS_KEY_empty; f->length = b->arg_count; /* arity = total parameter count */ return func_obj; @@ -9729,7 +9155,7 @@ restart: } /* Fall through to standard property lookup */ - if (JS_IsString (key)) { key = js_key_from_string (ctx, key); } + if (JS_IsText (key)) { key = js_key_from_string (ctx, key); } if (unlikely (JS_IsNull (ref_obj))) { JS_ThrowReferenceErrorNotDefined (ctx, key); goto exception; @@ -9790,7 +9216,7 @@ restart: } /* Fall through to standard property lookup */ - if (JS_IsString (key)) { key = js_key_from_string (ctx, key); } + if (JS_IsText (key)) { key = js_key_from_string (ctx, key); } if (unlikely (JS_IsNull (ref_obj))) { JS_ThrowReferenceErrorNotDefined (ctx, key); goto exception; @@ -9858,7 +9284,7 @@ restart: + JS_VALUE_GET_FLOAT64 (op2)); } /* 3) both strings? */ - else if (JS_IsString (op1) && JS_IsString (op2)) { + else if (JS_IsText (op1) && JS_IsText (op2)) { res = JS_ConcatString (ctx, op1, op2); if (JS_IsException (res)) goto exception; } @@ -9878,7 +9304,7 @@ restart: res = __JS_NewFloat64 (ctx, a + b); } else if (tag1 == JS_TAG_NULL || tag2 == JS_TAG_NULL) { /* null + string or string + null should throw */ - if (JS_IsString (op1) || JS_IsString (op2)) { + if (JS_IsText (op1) || JS_IsText (op2)) { JS_ThrowTypeError (ctx, "cannot concatenate null with string"); goto exception; } @@ -9918,7 +9344,7 @@ restart: + JS_VALUE_GET_FLOAT64 (rhs)); } /* 3) both strings? */ - else if (JS_IsString (lhs) && JS_IsString (rhs)) { + else if (JS_IsText (lhs) && JS_IsText (rhs)) { res = JS_ConcatString (ctx, lhs, rhs); if (JS_IsException (res)) goto exception; } @@ -10415,7 +9841,6 @@ restart: CASE (OP_template_concat) : { int n, i; JSValue out; - StringBuffer b_s, *b = &b_s; n = get_u16 (pc); pc += 2; @@ -10425,22 +9850,20 @@ restart: BREAK; } - if (string_buffer_init (ctx, b, 64)) goto exception; + JSText *concat = string_buffer_init(ctx, size); + if (!concat) goto exception; for (i = 0; i < n; i++) { JSValue v = sp[i - n]; JSValue s = js_cell_text (ctx, JS_NULL, 1, &v); if (JS_IsException (s)) { - string_buffer_free (b); for (int j = 0; j < n; j++) JS_FreeValue (ctx, sp[j - n]); sp -= n; goto exception; } - if (string_buffer_concat_value_free (b, s)) { - string_buffer_free (b); - for (int j = 0; j < n; j++) - JS_FreeValue (ctx, sp[j - n]); + concat = text_concat(ctx, concat, s); + if (!concat) { sp -= n; goto exception; } @@ -19088,50 +18511,12 @@ static JSValue js_create_function (JSContext *ctx, JSFunctionDef *fd) { } js_free (ctx, fd); - return JS_MKPTR (JS_TAG_FUNCTION_BYTECODE, b); + return JS_MKPTR(b); fail: js_free_function_def (ctx, fd); return JS_EXCEPTION; } -/* IC helper functions removed - shapes no longer used */ - -static void free_function_bytecode (JSRuntime *rt, JSFunctionBytecode *b) { - int i; - - free_bytecode_atoms (rt, b->byte_code_buf, b->byte_code_len, TRUE); - - if (b->vardefs) { - for (i = 0; i < b->arg_count + b->var_count; i++) { - JS_FreeValueRT (rt, b->vardefs[i].var_name); - } - } - for (i = 0; i < b->cpool_count; i++) - JS_FreeValueRT (rt, b->cpool[i]); - - for (i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - JS_FreeValueRT (rt, cv->var_name); - } - if (b->realm) JS_FreeContext (b->realm); - - JS_FreeValueRT (rt, b->func_name); - if (b->has_debug) { - JS_FreeValueRT (rt, b->debug.filename); - js_free_rt (rt, b->debug.pc2line_buf); - js_free_rt (rt, b->debug.source); - } - - /* IC removed */ - - remove_gc_object (&b->header); - if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) { - list_add_tail (&b->header.link, &rt->gc_zero_ref_count_list); - } else { - js_free_rt (rt, b); - } -} - static __exception int js_parse_directives (JSParseState *s) { char str[20]; JSParsePos pos; @@ -19802,7 +19187,7 @@ static JSValue JS_EvalObject (JSContext *ctx, JSValue this_obj, JSValue val, const char *str; size_t len; - if (!JS_IsString (val)) return JS_DupValue (ctx, val); + if (!JS_IsText (val)) return JS_DupValue (ctx, val); str = JS_ToCStringLen (ctx, &len, val); if (!str) return JS_EXCEPTION; ret = JS_EvalInternal (ctx, this_obj, str, len, "", flags, scope_idx); @@ -20021,7 +19406,7 @@ static int bc_put_key (BCWriterState *s, JSValue key) { } /* Handle heap strings */ - if (!JS_IsString (key)) { + if (!JS_IsText (key)) { /* Not a string - write empty */ bc_put_leb128 (s, 0); return 0; @@ -20257,7 +19642,7 @@ static int JS_WriteObjectTag (BCWriterState *s, JSValue obj) { if (pass == 1) bc_put_leb128 (s, prop_count); 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 (!rec_key_is_empty (k) && !rec_key_is_tomb (k) && JS_IsText (k)) { if (pass == 0) { prop_count++; } else { @@ -20766,7 +20151,7 @@ static JSValue JS_ReadFunctionTag (BCReaderState *s) { add_gc_object (ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); - obj = JS_MKPTR (JS_TAG_FUNCTION_BYTECODE, b); + obj = JS_MKPTR(b); #ifdef DUMP_READ_OBJECT bc_read_trace (s, "name: "); @@ -21000,7 +20385,7 @@ static JSValue JS_ReadObjectRec (BCReaderState *s) { JSString *p; p = JS_ReadString (s); if (!p) return JS_EXCEPTION; - obj = JS_MKPTR (JS_TAG_STRING, p); + obj = JS_MKPTR(p); } break; case BC_TAG_FUNCTION_BYTECODE: if (!s->allow_bytecode) goto invalid_tag; @@ -21026,7 +20411,7 @@ static JSValue JS_ReadObjectRec (BCReaderState *s) { return JS_ThrowSyntaxError (ctx, "invalid object reference (%u >= %u)", val, s->objects_count); } - obj = JS_DupValue (ctx, JS_MKPTR (JS_TAG_OBJECT, s->objects[val])); + obj = JS_DupValue (ctx, JS_MKPTR(s->objects[val])); } break; default: invalid_tag: @@ -21452,8 +20837,8 @@ static int js_is_regexp (JSContext *ctx, JSValue obj); static void js_regexp_finalizer (JSRuntime *rt, JSValue val) { JSRecord *p = JS_VALUE_GET_OBJ (val); JSRegExp *re = &p->u.regexp; - JS_FreeValueRT (rt, JS_MKPTR (JS_TAG_STRING, re->bytecode)); - JS_FreeValueRT (rt, JS_MKPTR (JS_TAG_STRING, re->pattern)); + JS_FreeValueRT (rt, JS_MKPTR(re->bytecode)); + JS_FreeValueRT (rt, JS_MKPTR(re->pattern)); } /* create a string containing the RegExp bytecode */ @@ -21607,9 +20992,9 @@ static JSValue js_regexp_constructor (JSContext *ctx, JSValue this_val, } re = js_get_regexp (ctx, pat, FALSE); if (re) { - pattern = JS_DupValue (ctx, JS_MKPTR (JS_TAG_STRING, re->pattern)); + pattern = JS_DupValue (ctx, JS_MKPTR(re->pattern)); if (JS_IsNull (flags1)) { - bc = JS_DupValue (ctx, JS_MKPTR (JS_TAG_STRING, re->bytecode)); + bc = JS_DupValue (ctx, JS_MKPTR(re->bytecode)); goto no_compilation; } else { flags = JS_ToString (ctx, flags1); @@ -21664,8 +21049,8 @@ static JSValue js_regexp_compile (JSContext *ctx, JSValue this_val, int argc, if (re1) { if (!JS_IsNull (flags1)) return JS_ThrowTypeError (ctx, "flags must be undefined"); - pattern = JS_DupValue (ctx, JS_MKPTR (JS_TAG_STRING, re1->pattern)); - bc = JS_DupValue (ctx, JS_MKPTR (JS_TAG_STRING, re1->bytecode)); + pattern = JS_DupValue (ctx, JS_MKPTR(re1->pattern)); + bc = JS_DupValue (ctx, JS_MKPTR(re1->bytecode)); } else { bc = JS_NULL; if (JS_IsNull (pattern1)) @@ -21676,8 +21061,8 @@ static JSValue js_regexp_compile (JSContext *ctx, JSValue this_val, int argc, bc = js_compile_regexp (ctx, pattern, flags1); if (JS_IsException (bc)) goto fail; } - JS_FreeValue (ctx, JS_MKPTR (JS_TAG_STRING, re->pattern)); - JS_FreeValue (ctx, JS_MKPTR (JS_TAG_STRING, re->bytecode)); + JS_FreeValue (ctx, JS_MKPTR(re->pattern)); + JS_FreeValue (ctx, JS_MKPTR(re->bytecode)); re->pattern = JS_VALUE_GET_STRING (pattern); re->bytecode = JS_VALUE_GET_STRING (bc); { @@ -21700,17 +21085,17 @@ static JSValue js_regexp_toString (JSContext *ctx, JSValue this_val, int argc, if (!JS_IsObject (this_val)) return JS_ThrowTypeErrorNotAnObject (ctx); - string_buffer_init (ctx, b, 0); - string_buffer_putc8 (b, '/'); + JSText *text = js_alloc_string(ctx, 0); + + text_putc(ctx, text, '/'); pattern = JS_GetProperty (ctx, this_val, JS_KEY_source); - if (string_buffer_concat_value_free (b, pattern)) goto fail; - string_buffer_putc8 (b, '/'); + text = text_concat(ctx, text, pattern); + text_putc(ctx, text, '/'); flags = JS_GetProperty (ctx, this_val, JS_KEY_flags); - if (string_buffer_concat_value_free (b, flags)) goto fail; - return string_buffer_end (b); + text = text_concat(ctx, text, flags); + return text_concat_end(text); fail: - string_buffer_free (b); return JS_EXCEPTION; } @@ -22588,7 +21973,7 @@ JSValue JS_JSONStringify (JSContext *ctx, JSValue obj, JSValue replacer, } else if (JS_IsNumber (v)) { v = JS_ToStringFree (ctx, v); if (JS_IsException (v)) goto exception; - } else if (!JS_IsString (v)) { + } else if (!JS_IsText (v)) { JS_FreeValue (ctx, v); continue; } @@ -22611,7 +21996,7 @@ JSValue JS_JSONStringify (JSContext *ctx, JSValue obj, JSValue replacer, int n; if (JS_ToInt32Clamp (ctx, &n, space, 0, 10, 0)) goto exception; jsc->gap = js_new_string8_len (ctx, " ", n); - } else if (JS_IsString (space)) { + } else if (JS_IsText (space)) { JSString *p = JS_VALUE_GET_STRING (space); jsc->gap = js_sub_string (ctx, p, 0, min_int (p->len, 10)); } else { @@ -23428,6 +22813,10 @@ static JSValue js_cell_character (JSContext *ctx, JSValue this_val, int argc, return JS_NewString (ctx, ""); } +static JSText *mist_text(JSContext *ctx, JSValue arg, int argc, JSValue *argv) { + +} + /* text(arg, format) - main text function */ static JSValue js_cell_text (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { @@ -24132,7 +23521,6 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, return string_buffer_end (b); fail_rx: - string_buffer_free (b); if (!JS_IsNull (orig_last_index) && !JS_IsException (orig_last_index)) { JS_SetPropertyStr (ctx, rx, "lastIndex", orig_last_index); } else { @@ -26221,7 +25609,7 @@ static JSValue js_cell_stone (JSContext *ctx, JSValue this_val, int argc, if (JS_IsObject (obj)) { JSRecord *rec = JS_VALUE_GET_RECORD (obj); - rec_set_stone (rec); + obj_set_stone (rec); return JS_DupValue (ctx, obj); } @@ -26717,28 +26105,7 @@ static JSValue js_cell_is_stone (JSContext *ctx, JSValue this_val, int argc, if (argc < 1) return JS_FALSE; JSValue obj = argv[0]; - - /* Check blob */ - blob *bd = js_get_blob (ctx, obj); - if (bd) return JS_NewBool (ctx, bd->is_stone); - - /* Non-objects are immutable by nature */ - if (JS_IsObject (obj)) { - JSRecord *rec = JS_VALUE_GET_RECORD (obj); - return JS_NewBool (ctx, rec_is_stone (rec)); - } - - if (JS_IsArray (ctx, obj)) { - JSArray *arr = JS_VALUE_GET_ARRAY (obj); - return JS_NewBool (ctx, objhdr_s (arr->mist_hdr)); - } - - return JS_NewBool (ctx, true); -} - -int JS_IsStone (JSContext *ctx, JSValue this_val) { - JSValue is = js_cell_is_stone (ctx, this_val, 1, &this_val); - return JS_VALUE_GET_BOOL (is); + return JS_NewBool(JS_IsStone(argv[0])); } /* is_text(val) */ diff --git a/source/quickjs.h b/source/quickjs.h index 6b5fdfd9..9278d24b 100644 --- a/source/quickjs.h +++ b/source/quickjs.h @@ -44,8 +44,39 @@ extern "C" { // #define BINARY64 // 64 bit word type (double) // #define DEC64 // 64 bit word type (dec) +enum mist_obj_type { + OBJ_ARRAY = 0, + OBJ_BLOB = 1, + OBJ_TEXT = 2, + OBJ_RECORD = 3, // js style objects + OBJ_FUNCTION = 4, + OBJ_CODE = 5, + OBJ_FRAME = 6, + OBJ_FORWARD = 7 +}; + +#define OBJHDR_CAP_SHIFT 8u +#define OBJHDR_CAP_MASK (((objhdr_t)1ull << 56) - 1ull) + +#define OBJHDR_S_BIT 3u +#define OBJHDR_P_BIT 4u +#define OBJHDR_A_BIT 5u +#define OBJHDR_R_BIT 7u + +#define OBJHDR_FLAG(bit) ((objhdr_t)1ull << (bit)) +#define OBJHDR_S_MASK OBJHDR_FLAG (OBJHDR_S_BIT) +#define OBJHDR_P_MASK OBJHDR_FLAG (OBJHDR_P_BIT) +#define OBJHDR_A_MASK OBJHDR_FLAG (OBJHDR_A_BIT) +#define OBJHDR_R_MASK OBJHDR_FLAG (OBJHDR_R_BIT) + +typedef uint64_t word_t; // one actor-memory word +typedef uint64_t objhdr_t; // header word +typedef uint64_t objref_t; // 56-bit word address (0 = null) + +static inline uint8_t objhdr_type (objhdr_t h) { return (uint8_t)(h & 7u); } +static inline int objhdr_s (objhdr_t h) { return (h & OBJHDR_S_MASK) != 0; } + /* - NaN boxing is used, always. A value is the length of the word. Half: 10 bits Float: 23 bits Double: 52 bits @@ -64,21 +95,8 @@ typedef struct JSContext JSContext; // Each actor - has its own GC typedef struct JSClass JSClass; typedef uint32_t JSClassID; -typedef struct JSGCRef { - JSValue val; - struct JSGCRef *prev; -} JSGCRef; - -/* stack of JSGCRef */ -JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref); -JSValue JS_PopGCRef(JSContext *ctx, JSGCRef *ref); - -#define JS_PUSH_VALUE(ctx, v) do { JS_PushGCRef(ctx, &v ## _ref); v ## _ref.val = v; } while (0) -#define JS_POP_VALUE(ctx, v) v = JS_PopGCRef(ctx, &v ## _ref) - -/* list of JSGCRef (they can be removed in any order, slower) */ -JSValue *JS_AddGCRef(JSContext *ctx, JSGCRef *ref); -void JS_DeleteGCRef(JSContext *ctx, JSGCRef *ref); +/* Forward declaration - JSGCRef moved after JSValue definition */ +struct JSGCRef; /* ============================================================ Mist Value Encoding (LSB-based type discrimination) @@ -130,23 +148,29 @@ enum { JS_TAG_UNINITIALIZED = 0x17, /* 10111 */ JS_TAG_STRING_IMM = 0x1B, /* 11011 - immediate ASCII (up to 7 chars) */ JS_TAG_CATCH_OFFSET = 0x1F, /* 11111 */ - - /* Object subtypes (stored in object header, not value) */ - JS_TAG_STRING = -8, /* mist_text pointer */ - JS_TAG_ARRAY = -6, /* JSArray pointer */ - JS_TAG_FUNCTION = -5, /* JSFunction pointer */ - JS_TAG_FUNCTION_BYTECODE = -2, - JS_TAG_OBJECT = -1, /* JSRecord/JSObject pointer */ - - /* Legacy - for gradual migration (to be removed) */ - JS_TAG_FIRST = -10, - JS_TAG_FLOAT64 = 8, /* unused in new encoding */ }; typedef struct JSRefCountHeader { int ref_count; } JSRefCountHeader; +/* JSGCRef - for rooting values during GC */ +typedef struct JSGCRef { + JSValue val; + struct JSGCRef *prev; +} JSGCRef; + +/* stack of JSGCRef */ +JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref); +JSValue JS_PopGCRef(JSContext *ctx, JSGCRef *ref); + +#define JS_PUSH_VALUE(ctx, v) do { JS_PushGCRef(ctx, &v ## _ref); v ## _ref.val = v; } while (0) +#define JS_POP_VALUE(ctx, v) v = JS_PopGCRef(ctx, &v ## _ref) + +/* list of JSGCRef (they can be removed in any order, slower) */ +JSValue *JS_AddGCRef(JSContext *ctx, JSGCRef *ref); +void JS_DeleteGCRef(JSContext *ctx, JSGCRef *ref); + /* ============================================================ Value Extraction ============================================================ */ @@ -185,10 +209,10 @@ JS_VALUE_GET_TAG (JSValue v) { ============================================================ */ #define JS_MKVAL(tag, val) _JS_MkVal (tag, val) -#define JS_MKPTR(tag, ptr) (((JSValue)(uintptr_t)(ptr)) | JS_TAG_PTR) +#define JS_MKPTR(ptr) (((JSValue)(uintptr_t)(ptr)) | JS_TAG_PTR) +#define JS_MKFWD(ptr) (((JSValue)(uintptr_t)(ptr)) | JS_TAG_FORWARD) -static inline JSValue -_JS_MkVal (int tag, int32_t val) { +static inline JSValue _JS_MkVal (int tag, int32_t val) { if (tag == JS_TAG_INT) { return ((JSValue)(uint32_t)val << 1); } /* Special tags encode payload in upper bits */ return ((JSValue)tag) | ((JSValue)(uint32_t)val << 5); @@ -239,8 +263,7 @@ __JS_NewFloat64 (JSContext *ctx, double d) { | JS_TAG_SHORT_FLOAT; } -static inline double -JS_VALUE_GET_FLOAT64 (JSValue v) { +static inline double JS_VALUE_GET_FLOAT64 (JSValue v) { if ((v & 7) != JS_TAG_SHORT_FLOAT) return 0.0; uint64_t sign = v >> 63; @@ -258,11 +281,6 @@ JS_VALUE_GET_FLOAT64 (JSValue v) { return u.d; } -static inline JS_BOOL -JS_VALUE_IS_NAN (JSValue v) { - return 0; /* NaN becomes NULL in Mist encoding */ -} - #define JS_TAG_IS_FLOAT64(tag) ((tag) == JS_TAG_SHORT_FLOAT) #define JS_NAN JS_MKVAL (JS_TAG_NULL, 0) @@ -272,11 +290,6 @@ static inline JSValue __JS_NewFloat64 (JSContext *ctx, double d); /* forward decl */ static inline double JS_VALUE_GET_FLOAT64 (JSValue v); -static inline JS_BOOL -JS_VALUE_IS_NAN (JSValue v) { - return 0; -} - #define JS_TAG_IS_FLOAT64(tag) (0) #define JS_NAN JS_MKVAL (JS_TAG_NULL, 0) @@ -286,18 +299,15 @@ JS_VALUE_IS_NAN (JSValue v) { Type Checks ============================================================ */ -static inline JS_BOOL -JS_IsInt (JSValue v) { - return (v & 1) == 0; -} -static inline JS_BOOL -JS_IsPtr (JSValue v) { - return (v & 3) == JS_TAG_PTR; -} -static inline JS_BOOL -JS_IsSpecial (JSValue v) { - return (v & 3) == JS_TAG_SPECIAL; +static inline JS_BOOL JS_IsInt (JSValue v) { return (v & 1) == 0; } +static inline JS_BOOL JS_IsPtr (JSValue v) { return (v & 3) == JS_TAG_PTR; } +static inline JS_BOOL JS_IsSpecial (JSValue v) { return (v & 3) == JS_TAG_SPECIAL; } + +static inline JS_BOOL JS_IsStone(JSValue v) { + if (!JS_IsPtr(v)) return 1; + return objhdr_s(*(objhdr_t *)JS_VALUE_GET_PTR(v)); } + #ifdef JS_PTR64 static inline JS_BOOL JS_IsShortFloat (JSValue v) { @@ -309,9 +319,6 @@ JS_IsShortFloat (JSValue v) { #define JS_VALUE_IS_BOTH_FLOAT(v1, v2) \ (JS_IsShortFloat (v1) && JS_IsShortFloat (v2)) -/* Pointer values have ref counts */ -#define JS_VALUE_HAS_REF_COUNT(v) JS_IsPtr (v) - /* ============================================================ Special Constants ============================================================ */ @@ -371,7 +378,6 @@ typedef struct JSMallocFunctions { typedef struct JSGCObjectHeader JSGCObjectHeader; JSValue JS_Stone (JSContext *ctx, JSValue this_val); -int JS_IsStone (JSContext *ctx, JSValue val); JSRuntime *JS_NewRuntime (void); /* info lifetime must exceed that of rt */ @@ -405,29 +411,6 @@ JSValue JS_GetClassProto (JSContext *ctx, JSClassID class_id); /* the following functions are used to select the intrinsic object to save memory */ JSContext *JS_NewContextRaw (JSRuntime *rt); -void JS_AddIntrinsicBaseObjects (JSContext *ctx); -void JS_AddIntrinsicEval (JSContext *ctx); -void JS_AddIntrinsicRegExpCompiler (JSContext *ctx); -void JS_AddIntrinsicRegExp (JSContext *ctx); -void JS_AddIntrinsicJSON (JSContext *ctx); - -JSValue js_string_codePointRange (JSContext *ctx, JSValue this_val, - int argc, JSValue *argv); - -void *js_malloc_rt (JSRuntime *rt, size_t size); -void js_free_rt (JSRuntime *rt, void *ptr); -void *js_realloc_rt (JSRuntime *rt, void *ptr, size_t size); -size_t js_malloc_usable_size_rt (JSRuntime *rt, const void *ptr); -void *js_mallocz_rt (JSRuntime *rt, size_t size); - -void *js_malloc (JSContext *ctx, size_t size); -void js_free (JSContext *ctx, void *ptr); -void *js_realloc (JSContext *ctx, void *ptr, size_t size); -size_t js_malloc_usable_size (JSContext *ctx, const void *ptr); -void *js_realloc2 (JSContext *ctx, void *ptr, size_t size, size_t *pslack); -void *js_mallocz (JSContext *ctx, size_t size); -char *js_strdup (JSContext *ctx, const char *str); -char *js_strndup (JSContext *ctx, const char *s, size_t n); typedef struct JSMemoryUsage { int64_t malloc_size, malloc_limit, memory_used_size; @@ -530,29 +513,24 @@ JS_NewFloat64 (JSContext *ctx, double d) { return __JS_NewFloat64 (ctx, d); } -static inline JS_BOOL -JS_IsNumber (JSValue v) { +static inline JS_BOOL JS_IsNumber (JSValue v) { int tag = JS_VALUE_GET_TAG (v); return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64 (tag); } -static inline JS_BOOL -JS_IsBool (JSValue v) { +static inline JS_BOOL JS_IsBool (JSValue v) { return JS_VALUE_GET_TAG (v) == JS_TAG_BOOL; } -static inline JS_BOOL -JS_IsNull (JSValue v) { +static inline JS_BOOL JS_IsNull (JSValue v) { return JS_VALUE_GET_TAG (v) == JS_TAG_NULL; } -static inline JS_BOOL -JS_IsException (JSValue v) { +static inline JS_BOOL JS_IsException (JSValue v) { return (JS_VALUE_GET_TAG (v) == JS_TAG_EXCEPTION); } -static inline JS_BOOL -JS_IsUninitialized (JSValue v) { +static inline JS_BOOL JS_IsUninitialized (JSValue v) { return (JS_VALUE_GET_TAG (v) == JS_TAG_UNINITIALIZED); } @@ -586,24 +564,45 @@ MIST_TryNewImmediateASCII (const char *str, size_t len) { return v; } -JS_BOOL JS_IsString (JSValue v); -JS_BOOL JS_IsText (JSValue v); - -/* Symbols removed in Mist encoding */ - -/* JS_IsFunction - check if value is a callable function. - With new tagging, must check gc_obj_type in JSGCObjectHeader. */ -JS_BOOL JS_IsFunction (JSValue v); - -static inline JS_BOOL -JS_IsInteger (JSValue v) { +static inline JS_BOOL JS_IsInteger (JSValue v) { return JS_VALUE_GET_TAG (v) == JS_TAG_INT; } -/* JS_IsObject - check if value is a settable object (record or array). - With new tagging, must check gc_obj_type in JSGCObjectHeader. */ -JS_BOOL JS_IsObject (JSValue v); -int JS_IsArray (JSContext *ctx, JSValue val); +static inline JS_BOOL JS_IsObject (JSValue v) { + return JS_IsPtr (v); +} + +static inline JS_BOOL JS_IsArray(JSValue v) { + return JS_IsObject(v) && objhdr_type(*(objhdr_t *)JS_VALUE_GET_PTR(v)) == OBJ_ARRAY; +} + +static inline JS_BOOL JS_IsRecord (JSValue v) { + return JS_IsObject(v) && objhdr_type(*(objhdr_t *)JS_VALUE_GET_PTR(v)) == OBJ_RECORD; +} + +static inline JS_BOOL JS_IsFunction (JSValue v) { + return JS_IsObject(v) && objhdr_type(*(objhdr_t *)JS_VALUE_GET_PTR(v)) == OBJ_FUNCTION; +} + +static inline JS_BOOL JS_IsCode (JSValue v) { + return JS_IsObject(v) && objhdr_type(*(objhdr_t *)JS_VALUE_GET_PTR(v)) == OBJ_CODE; +} + +static inline JS_BOOL JS_IsForwarded (JSValue v) { + return JS_IsObject(v) && objhdr_type(*(objhdr_t *)JS_VALUE_GET_PTR(v)) == OBJ_FORWARD; +} + +static inline JS_BOOL JS_IsFrame (JSValue v) { + return JS_IsObject(v) && objhdr_type(*(objhdr_t *)JS_VALUE_GET_PTR(v)) == OBJ_FRAME; +} + +static inline JS_BOOL JS_IsBlob (JSValue v) { + return JS_IsObject(v) && objhdr_type(*(objhdr_t *)JS_VALUE_GET_PTR(v)) == OBJ_BLOB; +} + +static inline JS_BOOL JS_IsText(JSValue v) { + return JS_IsObject(v) && objhdr_type(*(objhdr_t *)JS_VALUE_GET_PTR(v)) == OBJ_TEXT; +} // Fundamental int JS_GetLength (JSContext *ctx, JSValue obj, int64_t *pres); @@ -626,49 +625,12 @@ JSValue __js_printf_like (2, 3) JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...); JSValue JS_ThrowOutOfMemory (JSContext *ctx); -void __JS_FreeValue (JSContext *ctx, JSValue v); -static inline void -JS_FreeValue (JSContext *ctx, JSValue v) { - if (JS_VALUE_HAS_REF_COUNT (v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR (v); - if (--p->ref_count <= 0) { __JS_FreeValue (ctx, v); } - } -} -void __JS_FreeValueRT (JSRuntime *rt, JSValue v); -static inline void -JS_FreeValueRT (JSRuntime *rt, JSValue v) { - if (JS_VALUE_HAS_REF_COUNT (v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR (v); - if (--p->ref_count <= 0) { __JS_FreeValueRT (rt, v); } - } -} - -static inline JSValue -JS_DupValue (JSContext *ctx, JSValue v) { - if (JS_VALUE_HAS_REF_COUNT (v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR (v); - p->ref_count++; - } - return (JSValue)v; -} - -static inline JSValue -JS_DupValueRT (JSRuntime *rt, JSValue v) { - if (JS_VALUE_HAS_REF_COUNT (v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR (v); - p->ref_count++; - } - return (JSValue)v; -} - JS_BOOL JS_StrictEq (JSContext *ctx, JSValue op1, JSValue op2); JS_BOOL JS_SameValue (JSContext *ctx, JSValue op1, JSValue op2); -int JS_ToBool (JSContext *ctx, - JSValue val); /* return -1 for JS_EXCEPTION */ +int JS_ToBool (JSContext *ctx, JSValue val); /* return -1 for JS_EXCEPTION */ int JS_ToInt32 (JSContext *ctx, int32_t *pres, JSValue val); -static inline int -JS_ToUint32 (JSContext *ctx, uint32_t *pres, JSValue val) { +static inline int JS_ToUint32 (JSContext *ctx, uint32_t *pres, JSValue val) { return JS_ToInt32 (ctx, (int32_t *)pres, val); } int JS_ToInt64 (JSContext *ctx, int64_t *pres, JSValue val); @@ -676,26 +638,21 @@ int JS_ToFloat64 (JSContext *ctx, double *pres, JSValue val); /* return an exception if 'val' is a Number */ JSValue JS_NewStringLen (JSContext *ctx, const char *str1, size_t len1); -static inline JSValue -JS_NewString (JSContext *ctx, const char *str) { +static inline JSValue JS_NewString (JSContext *ctx, const char *str) { return JS_NewStringLen (ctx, str, strlen (str)); } JSValue JS_ToString (JSContext *ctx, JSValue val); JSValue JS_ToPropertyKey (JSContext *ctx, JSValue val); -const char *JS_ToCStringLen2 (JSContext *ctx, size_t *plen, JSValue val1, - JS_BOOL cesu8); -static inline const char * -JS_ToCStringLen (JSContext *ctx, size_t *plen, JSValue val1) { +const char *JS_ToCStringLen2 (JSContext *ctx, size_t *plen, JSValue val1, JS_BOOL cesu8); +static inline const char * JS_ToCStringLen (JSContext *ctx, size_t *plen, JSValue val1) { return JS_ToCStringLen2 (ctx, plen, val1, 0); } -static inline const char * -JS_ToCString (JSContext *ctx, JSValue val1) { +static inline const char * JS_ToCString (JSContext *ctx, JSValue val1) { return JS_ToCStringLen2 (ctx, NULL, val1, 0); } void JS_FreeCString (JSContext *ctx, const char *ptr); -JSValue JS_NewObjectProtoClass (JSContext *ctx, JSValue proto, - JSClassID class_id); +JSValue JS_NewObjectProtoClass (JSContext *ctx, JSValue proto, JSClassID class_id); JSValue JS_NewObjectClass (JSContext *ctx, int class_id); JSValue JS_NewObjectProto (JSContext *ctx, JSValue proto); JSValue JS_NewObject (JSContext *ctx); @@ -825,42 +782,33 @@ typedef union JSCFunctionType { JSValue JS_NewCFunction2 (JSContext *ctx, JSCFunction *func, const char *name, int length, JSCFunctionEnum cproto, int magic); -static inline JSValue -JS_NewCFunction (JSContext *ctx, JSCFunction *func, const char *name, - int length) { +static inline JSValue JS_NewCFunction (JSContext *ctx, JSCFunction *func, const char *name, int length) { return JS_NewCFunction2 (ctx, func, name, length, JS_CFUNC_generic, 0); } -static inline JSValue -JS_NewCFunctionMagic (JSContext *ctx, JSCFunctionMagic *func, const char *name, - int length, JSCFunctionEnum cproto, int magic) { +static inline JSValue JS_NewCFunctionMagic (JSContext *ctx, JSCFunctionMagic *func, const char *name, int length, JSCFunctionEnum cproto, int magic) { return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, length, cproto, magic); } /* Fixed-arity fast path constructors */ -static inline JSValue -JS_NewCFuncFixed0 (JSContext *ctx, JSCFunction0 *func, const char *name) { +static inline JSValue JS_NewCFuncFixed0 (JSContext *ctx, JSCFunction0 *func, const char *name) { return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 0, JS_CFUNC_0, 0); } -static inline JSValue -JS_NewCFuncFixed1 (JSContext *ctx, JSCFunction1 *func, const char *name) { +static inline JSValue JS_NewCFuncFixed1 (JSContext *ctx, JSCFunction1 *func, const char *name) { return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 1, JS_CFUNC_1, 0); } -static inline JSValue -JS_NewCFuncFixed2 (JSContext *ctx, JSCFunction2 *func, const char *name) { +static inline JSValue JS_NewCFuncFixed2 (JSContext *ctx, JSCFunction2 *func, const char *name) { return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 2, JS_CFUNC_2, 0); } -static inline JSValue -JS_NewCFuncFixed3 (JSContext *ctx, JSCFunction3 *func, const char *name) { +static inline JSValue JS_NewCFuncFixed3 (JSContext *ctx, JSCFunction3 *func, const char *name) { return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 3, JS_CFUNC_3, 0); } -static inline JSValue -JS_NewCFuncFixed4 (JSContext *ctx, JSCFunction4 *func, const char *name) { +static inline JSValue JS_NewCFuncFixed4 (JSContext *ctx, JSCFunction4 *func, const char *name) { return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 4, JS_CFUNC_4, 0); }