From a37bd73048d53dfc902de852088722f9926abb99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Sun, 28 Jun 2026 00:09:32 +0200 Subject: [PATCH] Add arena allocator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adapted from commit 99e9181d117a ("added custom malloc for small blocks (11% faster on bench-v8)" https://github.com/bellard/quickjs). Benchmarks: ``` ┌─────────────────┬──────┬──────────┬─────────────┬─────────┐ │ Benchmark │ NG │ NG+arena │ NG+arena+mi │ Bellard │ ├─────────────────┼──────┼──────────┼─────────────┼─────────┤ │ Richards │ 1207 │ 1221 │ 1221 │ 1210 │ ├─────────────────┼──────┼──────────┼─────────────┼─────────┤ │ DeltaBlue │ 1211 │ 1289 │ 1309 │ 1295 │ ├─────────────────┼──────┼──────────┼─────────────┼─────────┤ │ Crypto │ 1070 │ 1075 │ 1106 │ 973 │ ├─────────────────┼──────┼──────────┼─────────────┼─────────┤ │ RayTrace │ 1977 │ 2883 │ 2942 │ 2866 │ ├─────────────────┼──────┼──────────┼─────────────┼─────────┤ │ EarleyBoyer │ 2690 │ 3540 │ 3610 │ 3548 │ ├─────────────────┼──────┼──────────┼─────────────┼─────────┤ │ RegExp │ 432 │ 492 │ 499 │ 557 │ ├─────────────────┼──────┼──────────┼─────────────┼─────────┤ │ Splay │ 3569 │ 5802 │ 5851 │ 6087 │ ├─────────────────┼──────┼──────────┼─────────────┼─────────┤ │ NavierStokes │ 2202 │ 2192 │ 2192 │ 1843 │ ├─────────────────┼──────┼──────────┼─────────────┼─────────┤ │ Score (geomean) │ 1511 │ 1783 │ 1817 │ 1764 │ └─────────────────┴──────┴──────────┴─────────────┴─────────┘ ``` --- quickjs.c | 749 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 548 insertions(+), 201 deletions(-) diff --git a/quickjs.c b/quickjs.c index 79a8482b7..7e74c111a 100644 --- a/quickjs.c +++ b/quickjs.c @@ -256,6 +256,60 @@ typedef struct JSMallocState { void *opaque; /* user opaque */ } JSMallocState; +/* Small-block "arena" allocator. + js_{malloc,free,realloc,calloc}_rt serve allocations up to ~512 + bytes from size-classed 4KB arenas carved out of the underlying + JSMallocFunctions allocator, so the backing allocator (system malloc, + mimalloc, ...) sees roughly one request per arena refill instead of one per + object. */ + +#define JS_ARENA_ALIGN 8 +#define JS_ARENA_SIZE 4096 +#define JS_ARENA_BLOCK_SIZE_COUNT 31 +#define JS_ARENA_MAX_SMALL_SIZE 512 +#define JS_ARENA_FREE_NIL 0xffff + +#if defined(__SANITIZE_ADDRESS__) +/* route every allocation through the backing malloc so ASan sees each block */ +#define JS_ARENA_LARGE_BLOCKS_ONLY 1 +#else +#define JS_ARENA_LARGE_BLOCKS_ONLY 0 +#endif + +/* 8-byte header preceding every user allocation. It carries the allocator + bookkeeping (block_idx/free_next + block_size_idx) and, the + GC/refcount fields (gc_obj_type/mark/ref_count) that would otherwise sit in + the object body. The 8-byte size keeps user_data JS_ARENA_ALIGN-aligned. */ +typedef struct JSMallocBlockHeader { + union { + uint16_t block_idx; /* JS_ARENA_FREE_NIL => large or zero-size block */ + uint16_t free_next; /* next free block index while on a free list */ + } u; + uint8_t block_size_idx; + /* GC/refcount header merged into the allocator header: + the object body keeps only JSGCObjectHeader.link (no ref_count/flags). */ + uint8_t gc_obj_type : 7; /* JSGCObjectTypeEnum for GC objects */ + uint8_t mark : 1; /* used by the cycle collector */ + int ref_count; + _Alignas(JS_ARENA_ALIGN) uint8_t user_data[]; +} JSMallocBlockHeader; + +typedef struct JSArena { + struct list_head free_link; /* in free_arena_list[idx] while not full */ + struct list_head link; /* in arena_list[idx] for the whole lifetime */ + uint8_t block_size_idx; + uint16_t n_used_blocks; + uint16_t n_blocks; + uint16_t first_free_block; /* JS_ARENA_FREE_NIL if none */ + _Alignas(JS_ARENA_ALIGN) uint8_t blocks[]; +} JSArena; + +typedef struct JSArenaState { + struct list_head arena_list[JS_ARENA_BLOCK_SIZE_COUNT]; + struct list_head free_arena_list[JS_ARENA_BLOCK_SIZE_COUNT]; + _Alignas(JS_ARENA_ALIGN) uint8_t zero_size_block[sizeof(JSMallocBlockHeader)]; +} JSArenaState; + typedef struct JSRuntimeFinalizerState { struct JSRuntimeFinalizerState *next; JSRuntimeFinalizer *finalizer; @@ -270,6 +324,7 @@ typedef struct JSValueLink { struct JSRuntime { JSMallocFunctions mf; JSMallocState malloc_state; + JSArenaState arena_state; const char *rt_info; int atom_hash_size; /* power of two */ @@ -395,26 +450,17 @@ typedef enum { 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 : 1; /* used by the GC */ - uint8_t dummy0 : 3; - uint8_t dummy1; /* not used by the GC */ - uint16_t dummy2; /* not used by the GC */ + /* ref_count/gc_obj_type/mark live in the allocator block header (js_rc(p), + the 8 bytes before every allocation); only the GC list link remains in + the object body. */ struct list_head link; }; 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; - uint8_t is_lexical; /* only used with global variables */ - uint8_t is_const; /* only used with global variables */ - }; - }; + JSGCObjectHeader header; /* {link}; must come first so &p->header == p */ + uint8_t is_detached; + uint8_t is_lexical; /* only used with global variables */ + uint8_t is_const; /* only used with global variables */ JSValue *pvalue; /* pointer to the value, either on the stack or to 'value' */ union { @@ -426,9 +472,17 @@ typedef struct JSVarRef { }; } JSVarRef; -typedef struct JSRefCountHeader { - int ref_count; -} JSRefCountHeader; +/* Accessors for the reference count and GC mark/type. These fields live in the + arena block header (the 8 bytes before every allocation, reached via + js_rc()), not in the object body. The macros yield lvalues, and the argument + is always the object/header pointer (whose address equals the GC header's, + since the header is the first member). */ +static inline JSMallocBlockHeader *js_rc(const void *p) { + return (JSMallocBlockHeader *)((uint8_t *)(uintptr_t)p - offsetof(JSMallocBlockHeader, user_data)); +} +#define JS_REF_COUNT(p) (js_rc(p)->ref_count) +#define JS_GC_TYPE(p) (js_rc(p)->gc_obj_type) +#define JS_GC_MARK(p) (js_rc(p)->mark) /* bigint */ typedef int32_t js_slimb_t; @@ -447,7 +501,6 @@ typedef uint64_t js_dlimb_t; typedef struct JSBigInt { - JSRefCountHeader header; /* must come first, 32-bit */ uint32_t len; /* number of limbs, >= 1 */ js_limb_t tab[]; /* two's complement representation, always normalized so that 'len' is the minimum @@ -616,7 +669,6 @@ typedef enum { #define JS_ATOM_HASH_MASK ((1 << 28) - 1) struct JSString { - JSRefCountHeader header; /* must come first, 32-bit */ uint32_t len : 31; uint32_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3, @@ -638,7 +690,6 @@ typedef struct JSStringSlice { } JSStringSlice; struct JSStringRope { - JSRefCountHeader header; /* must come first, 32-bit */ uint32_t len; uint8_t is_wide_char; /* 0 = 8 bits, 1 = 16 bits characters */ uint8_t depth; /* max depth of the rope tree */ @@ -940,7 +991,6 @@ typedef enum { } JSModuleStatus; struct JSModuleDef { - JSRefCountHeader header; /* must come first, 32-bit */ JSAtom module_name; struct list_head link; @@ -1037,28 +1087,23 @@ struct JSShape { int deleted_prop_count; JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ JSObject *proto; - JSShapeProperty prop[]; /* prop_size elements */ + uint32_t hash_table[]; /* prop_hash_mask + 1 elements, then prop[prop_size] */ }; struct JSObject { - union { - JSGCObjectHeader header; - struct { - int __gc_ref_count; /* corresponds to header.ref_count */ - uint8_t __gc_mark : 7; /* corresponds to header.mark/gc_obj_type */ - uint8_t is_prototype : 1; /* object may be used as prototype */ - - uint8_t extensible : 1; - uint8_t free_mark : 1; /* only used when freeing objects with cycles */ - uint8_t is_exotic : 1; /* true if object has exotic property handlers */ - uint8_t fast_array : 1; /* true if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_MAPPED_ARGUMENTS and typed arrays) */ - uint8_t is_constructor : 1; /* true if object is a constructor function */ - uint8_t is_uncatchable_error : 1; /* if true, error is not catchable */ - uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ - uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ - uint16_t class_id; /* see JS_CLASS_x */ - }; - }; + /* ref_count/gc_obj_type/mark live in the allocator block header; the object + body keeps only the GC list link plus the object's own flags. */ + JSGCObjectHeader header; /* {link}; must come first so &p->header == p */ + uint8_t is_prototype : 1; /* object may be used as prototype */ + uint8_t extensible : 1; + uint8_t free_mark : 1; /* only used when freeing objects with cycles */ + uint8_t is_exotic : 1; /* true if object has exotic property handlers */ + uint8_t fast_array : 1; /* true if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_MAPPED_ARGUMENTS and typed arrays) */ + uint8_t is_constructor : 1; /* true if object is a constructor function */ + uint8_t is_uncatchable_error : 1; /* if true, error is not catchable */ + uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ + uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ + uint16_t class_id; /* see JS_CLASS_x */ /* byte offsets: 16/24 */ JSShape *shape; /* prototype and property names + flag */ JSProperty *prop; /* array of properties */ @@ -1579,8 +1624,8 @@ static JSValue js_bool(bool v) static JSValue js_dup(JSValueConst v) { if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); - p->ref_count++; + void *p = JS_VALUE_GET_PTR(v); + JS_REF_COUNT(p)++; } return unsafe_unconst(v); } @@ -1621,6 +1666,279 @@ static size_t js_malloc_usable_size_unknown(const void *ptr) return 0; } +/* max overhead for size >= 64: 12.5% */ +static const uint16_t arena_block_sizes[JS_ARENA_BLOCK_SIZE_COUNT] = { + 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128, + 144, 160, 176, 192, 208, 224, 240, 256, + 288, 320, 352, 384, 416, 448, 480, 512, +}; + +static int arena_get_size_index(size_t size) +{ + if (size <= 16) + return 0; + else if (size <= 128) + return (size + 7) / 8 - 2; + else if (size <= 256) + return (size + 15) / 16 + 6; + else if (size <= 512) + return (size + 31) / 32 + 14; + else + return JS_ARENA_BLOCK_SIZE_COUNT; +} + +static inline JSMallocBlockHeader *arena_zero_block(JSRuntime *rt) +{ + return (JSMallocBlockHeader *)rt->arena_state.zero_size_block; +} + +static void js_arena_init(JSRuntime *rt) +{ + JSArenaState *s = &rt->arena_state; + int i; + arena_zero_block(rt)->u.block_idx = JS_ARENA_FREE_NIL; + for (i = 0; i < JS_ARENA_BLOCK_SIZE_COUNT; i++) { + init_list_head(&s->arena_list[i]); + init_list_head(&s->free_arena_list[i]); + } +} + +static inline void *arena_get_block(JSArena *ar, unsigned int idx, + unsigned int block_size) +{ + return ar->blocks + (size_t)idx * block_size; +} + +static no_inline JSArena *arena_new(JSRuntime *rt, int block_size_idx) +{ + JSMallocBlockHeader *b; + JSArena *ar; + int n_blocks, block_size, i; + + block_size = arena_block_sizes[block_size_idx]; + n_blocks = (JS_ARENA_SIZE - sizeof(JSArena)) / block_size; + ar = rt->mf.js_malloc(rt->malloc_state.opaque, + sizeof(JSArena) + (size_t)n_blocks * block_size); + if (!ar) + return NULL; + ar->block_size_idx = block_size_idx; + ar->n_blocks = n_blocks; + ar->n_used_blocks = 0; + ar->first_free_block = 0; + for (i = 0; i < n_blocks - 1; i++) { + b = arena_get_block(ar, i, block_size); + b->u.free_next = i + 1; + b->block_size_idx = block_size_idx; + } + b = arena_get_block(ar, n_blocks - 1, block_size); + b->u.free_next = JS_ARENA_FREE_NIL; + b->block_size_idx = block_size_idx; + list_add(&ar->link, &rt->arena_state.arena_list[block_size_idx]); + list_add(&ar->free_link, &rt->arena_state.free_arena_list[block_size_idx]); + return ar; +} + +static no_inline void *arena_malloc_large(JSRuntime *rt, size_t size) +{ + JSMallocBlockHeader *b; + b = rt->mf.js_malloc(rt->malloc_state.opaque, + sizeof(JSMallocBlockHeader) + size); + if (!b) + return NULL; + b->u.block_idx = JS_ARENA_FREE_NIL; + b->block_size_idx = 0xff; /* fail safe */ + return b->user_data; +} + +static no_inline void *arena_calloc_large(JSRuntime *rt, size_t size) +{ + JSMallocBlockHeader *b; + b = rt->mf.js_calloc(rt->malloc_state.opaque, 1, + sizeof(JSMallocBlockHeader) + size); + if (!b) + return NULL; + b->u.block_idx = JS_ARENA_FREE_NIL; + b->block_size_idx = 0xff; /* fail safe */ + return b->user_data; +} + +static void *js_arena_malloc(JSRuntime *rt, size_t size) +{ + size_t total_size; + + if (unlikely(size == 0)) + return arena_zero_block(rt)->user_data; + total_size = ((size + JS_ARENA_ALIGN - 1) & ~(size_t)(JS_ARENA_ALIGN - 1)) + + sizeof(JSMallocBlockHeader); + if (!JS_ARENA_LARGE_BLOCKS_ONLY && total_size <= JS_ARENA_MAX_SMALL_SIZE) { + int block_size_idx; + unsigned int block_idx, block_size; + JSMallocBlockHeader *b; + JSArena *ar; + struct list_head *el, *head; + + block_size_idx = arena_get_size_index(total_size); + block_size = arena_block_sizes[block_size_idx]; + head = &rt->arena_state.free_arena_list[block_size_idx]; + el = head->next; + if (unlikely(el == head)) { + ar = arena_new(rt, block_size_idx); + if (!ar) + return NULL; + } else { + ar = list_entry(el, JSArena, free_link); + } + block_idx = ar->first_free_block; + b = arena_get_block(ar, block_idx, block_size); + ar->first_free_block = b->u.free_next; + b->u.block_idx = block_idx; + ar->n_used_blocks++; + if (unlikely(ar->n_used_blocks == ar->n_blocks)) + list_del(&ar->free_link); + return b->user_data; + } else { + return arena_malloc_large(rt, size); + } +} + +static void js_arena_free(JSRuntime *rt, void *ptr) +{ + JSMallocBlockHeader *b; + + if (!ptr) + return; + b = container_of(ptr, JSMallocBlockHeader, user_data); + if (unlikely(b->u.block_idx == JS_ARENA_FREE_NIL)) { + /* large or zero-size block */ + if (b == arena_zero_block(rt)) { + /* nothing to do */ + } else { + rt->mf.js_free(rt->malloc_state.opaque, b); + } + } else { + unsigned int block_idx = b->u.block_idx; + unsigned int block_size_idx = b->block_size_idx; + unsigned int block_size = arena_block_sizes[block_size_idx]; + JSArena *ar = (JSArena *)((uint8_t *)b - + (size_t)block_size * block_idx - + sizeof(JSArena)); + b->u.free_next = ar->first_free_block; + ar->first_free_block = block_idx; + if (unlikely(ar->n_used_blocks == ar->n_blocks)) + list_add(&ar->free_link, + &rt->arena_state.free_arena_list[block_size_idx]); + ar->n_used_blocks--; + if (unlikely(ar->n_used_blocks == 0)) { + list_del(&ar->link); + list_del(&ar->free_link); + rt->mf.js_free(rt->malloc_state.opaque, ar); + } + } +} + +static size_t js_arena_usable_size(JSRuntime *rt, const void *ptr) +{ + const JSMallocBlockHeader *b; + + if (!ptr) + return 0; + b = container_of(ptr, JSMallocBlockHeader, user_data); + if (b->u.block_idx == JS_ARENA_FREE_NIL) { + if (b == arena_zero_block(rt)) { + return 0; + } else { + size_t size = rt->mf.js_malloc_usable_size(b); + if (size != 0) + size -= sizeof(JSMallocBlockHeader); + return size; + } + } else { + return arena_block_sizes[b->block_size_idx] - sizeof(JSMallocBlockHeader); + } +} + +static void *js_arena_realloc(JSRuntime *rt, void *ptr, size_t size) +{ + JSMallocBlockHeader *b; + + /* js_realloc_rt already handles ptr == NULL and size == 0 */ + b = container_of(ptr, JSMallocBlockHeader, user_data); + if (b->u.block_idx == JS_ARENA_FREE_NIL) { + if (b == arena_zero_block(rt)) { + return js_arena_malloc(rt, size); + } else { + JSMallocBlockHeader *nb; + nb = rt->mf.js_realloc(rt->malloc_state.opaque, b, + sizeof(JSMallocBlockHeader) + size); + if (!nb) + return NULL; + nb->u.block_idx = JS_ARENA_FREE_NIL; + nb->block_size_idx = 0xff; + return nb->user_data; + } + } else { + unsigned int block_size = arena_block_sizes[b->block_size_idx]; + size_t total_size, old_usable; + void *new_ptr; + + total_size = ((size + JS_ARENA_ALIGN - 1) & ~(size_t)(JS_ARENA_ALIGN - 1)) + + sizeof(JSMallocBlockHeader); + if (total_size <= block_size) + return ptr; /* still fits the current size class */ + new_ptr = js_arena_malloc(rt, size); + if (!new_ptr) + return NULL; + { + /* carry the merged GC/refcount fields to the relocated block */ + JSMallocBlockHeader *nb = container_of(new_ptr, JSMallocBlockHeader, user_data); + nb->gc_obj_type = b->gc_obj_type; + nb->mark = b->mark; + nb->ref_count = b->ref_count; + } + old_usable = block_size - sizeof(JSMallocBlockHeader); + if (size > old_usable) + size = old_usable; + memcpy(new_ptr, ptr, size); + js_arena_free(rt, ptr); + return new_ptr; + } +} + +static void *js_arena_calloc(JSRuntime *rt, size_t count, size_t size) +{ + size_t n = count * size; /* overflow already checked by js_calloc_rt */ + size_t total_size = ((n + JS_ARENA_ALIGN - 1) & ~(size_t)(JS_ARENA_ALIGN - 1)) + + sizeof(JSMallocBlockHeader); + if (!JS_ARENA_LARGE_BLOCKS_ONLY && total_size <= JS_ARENA_MAX_SMALL_SIZE) { + /* Small blocks are carved from recycled (dirty) arena memory, so they + must be zeroed explicitly. */ + void *ptr = js_arena_malloc(rt, n); + if (unlikely(!ptr)) + return NULL; + return memset(ptr, 0, n); + } + /* Large blocks come straight from the backing allocator, so let js_calloc + do the zeroing. */ + return arena_calloc_large(rt, n); +} + +/* free any arenas still mapped at runtime teardown (normally none: empty + arenas are released eagerly as their last block is freed) */ +static void js_arena_free_all(JSRuntime *rt) +{ + JSArenaState *s = &rt->arena_state; + struct list_head *el, *el1; + int i; + for (i = 0; i < JS_ARENA_BLOCK_SIZE_COUNT; i++) { + list_for_each_safe(el, el1, &s->arena_list[i]) { + JSArena *ar = list_entry(el, JSArena, link); + rt->mf.js_free(rt->malloc_state.opaque, ar); + } + init_list_head(&s->arena_list[i]); + init_list_head(&s->free_arena_list[i]); + } +} + void *js_calloc_rt(JSRuntime *rt, size_t count, size_t size) { void *ptr; @@ -1638,12 +1956,12 @@ void *js_calloc_rt(JSRuntime *rt, size_t count, size_t size) if (unlikely(s->malloc_size + (count * size) > s->malloc_limit - 1)) return NULL; - ptr = rt->mf.js_calloc(s->opaque, count, size); + ptr = js_arena_calloc(rt, count, size); if (!ptr) return NULL; s->malloc_count++; - s->malloc_size += rt->mf.js_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + s->malloc_size += js_arena_usable_size(rt, ptr) + MALLOC_OVERHEAD; return ptr; } @@ -1661,12 +1979,12 @@ void *js_malloc_rt(JSRuntime *rt, size_t size) if (unlikely(s->malloc_size + size > s->malloc_limit - 1)) return NULL; - ptr = rt->mf.js_malloc(s->opaque, size); + ptr = js_arena_malloc(rt, size); if (!ptr) return NULL; s->malloc_count++; - s->malloc_size += rt->mf.js_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + s->malloc_size += js_arena_usable_size(rt, ptr) + MALLOC_OVERHEAD; return ptr; } @@ -1678,14 +1996,14 @@ void js_free_rt(JSRuntime *rt, void *ptr) return; s = &rt->malloc_state; - size_t free_size = rt->mf.js_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + size_t free_size = js_arena_usable_size(rt, ptr) + MALLOC_OVERHEAD; if (unlikely(free_size > s->malloc_size)) { printf("js_free_rt: malloc_size underflow: freeing %zu but only %zu tracked\n", free_size, s->malloc_size); abort(); } s->malloc_count--; s->malloc_size -= free_size; - rt->mf.js_free(s->opaque, ptr); + js_arena_free(rt, ptr); } void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size) @@ -1702,23 +2020,23 @@ void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size) js_free_rt(rt, ptr); return NULL; } - old_size = rt->mf.js_malloc_usable_size(ptr); + old_size = js_arena_usable_size(rt, ptr); s = &rt->malloc_state; /* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */ if (s->malloc_size + size - old_size > s->malloc_limit - 1) return NULL; - ptr = rt->mf.js_realloc(s->opaque, ptr, size); + ptr = js_arena_realloc(rt, ptr, size); if (!ptr) return NULL; - s->malloc_size += rt->mf.js_malloc_usable_size(ptr) - old_size; + s->malloc_size += js_arena_usable_size(rt, ptr) - old_size; return ptr; } size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr) { - return rt->mf.js_malloc_usable_size(ptr); + return js_arena_usable_size(rt, ptr); } /** @@ -1992,6 +2310,7 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) ms.malloc_count++; ms.malloc_size += rt->mf.js_malloc_usable_size(rt) + MALLOC_OVERHEAD; rt->malloc_state = ms; + js_arena_init(rt); rt->malloc_gc_threshold = 256 * 1024; init_list_head(&rt->context_list); @@ -2238,7 +2557,7 @@ static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char); if (unlikely(!str)) return NULL; - str->header.ref_count = 1; + JS_REF_COUNT(str) = 1; str->is_wide_char = is_wide_char; str->len = max_len; str->kind = JS_STRING_KIND_NORMAL; @@ -2267,7 +2586,7 @@ static inline void js_free_string0(JSRuntime *rt, JSString *str); /* same as JS_FreeValueRT() but faster */ static inline void js_free_string(JSRuntime *rt, JSString *str) { - if (--str->header.ref_count <= 0) + if (--JS_REF_COUNT(str) <= 0) js_free_string0(rt, str); } @@ -2330,14 +2649,14 @@ void JS_FreeRuntime(JSRuntime *rt) referenced externally */ list_for_each(el, &rt->gc_obj_list) { p = list_entry(el, JSGCObjectHeader, link); - p->mark = 0; + JS_GC_MARK(p) = 0; } gc_decref(rt); header_done = false; list_for_each(el, &rt->gc_obj_list) { p = list_entry(el, JSGCObjectHeader, link); - if (p->ref_count != 0) { + if (JS_REF_COUNT(p) != 0) { if (!header_done) { printf("Object leaks:\n"); JS_DumpObjectHeader(rt); @@ -2351,7 +2670,7 @@ void JS_FreeRuntime(JSRuntime *rt) count = 0; list_for_each(el, &rt->gc_obj_list) { p = list_entry(el, JSGCObjectHeader, link); - if (p->ref_count == 0) { + if (JS_REF_COUNT(p) == 0) { count++; } } @@ -2379,7 +2698,7 @@ void JS_FreeRuntime(JSRuntime *rt) for(i = 0; i < rt->atom_size; i++) { JSAtomStruct *p = rt->atom_array[i]; if (!atom_is_free(p) /* && p->str*/) { - if (i >= JS_ATOM_END || p->header.ref_count != 1) { + if (i >= JS_ATOM_END || JS_REF_COUNT(p) != 1) { if (!header_done) { header_done = true; if (rt->rt_info) { @@ -2393,7 +2712,7 @@ void JS_FreeRuntime(JSRuntime *rt) if (rt->rt_info) { printf(" "); } else { - printf(" %6u %6u ", i, p->header.ref_count); + printf(" %6u %6u ", i, JS_REF_COUNT(p)); } switch (p->atom_type) { case JS_ATOM_TYPE_STRING: @@ -2417,7 +2736,7 @@ void JS_FreeRuntime(JSRuntime *rt) break; } if (rt->rt_info) { - printf(":%u", p->header.ref_count); + printf(":%u", JS_REF_COUNT(p)); } else { printf("\n"); } @@ -2457,11 +2776,11 @@ void JS_FreeRuntime(JSRuntime *rt) if (rt->rt_info) { printf(" "); } else { - printf(" %6u ", str->header.ref_count); + printf(" %6u ", JS_REF_COUNT(str)); } JS_DumpString(rt, str); if (rt->rt_info) { - printf(":%u", str->header.ref_count); + printf(":%u", JS_REF_COUNT(str)); } else { printf("\n"); } @@ -2497,6 +2816,8 @@ void JS_FreeRuntime(JSRuntime *rt) leak &= check_dump_flag(rt, JS_ABORT_ON_LEAKS); + js_arena_free_all(rt); + { JSMallocState *ms = &rt->malloc_state; rt->mf.js_free(ms->opaque, rt); @@ -2514,7 +2835,7 @@ JSContext *JS_NewContextRaw(JSRuntime *rt) ctx = js_mallocz_rt(rt, sizeof(JSContext)); if (!ctx) return NULL; - ctx->header.ref_count = 1; + JS_REF_COUNT(ctx) = 1; add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT); ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) * @@ -2628,7 +2949,7 @@ static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag) JSContext *JS_DupContext(JSContext *ctx) { - ctx->header.ref_count++; + JS_REF_COUNT(ctx)++; return ctx; } @@ -2693,9 +3014,9 @@ void JS_FreeContext(JSContext *ctx) JSRuntime *rt = ctx->rt; int i; - if (--ctx->header.ref_count > 0) + if (--JS_REF_COUNT(ctx) > 0) return; - assert(ctx->header.ref_count == 0); + assert(JS_REF_COUNT(ctx) == 0); #ifdef ENABLE_DUMPS // JS_DUMP_ATOMS if (check_dump_flag(rt, JS_DUMP_ATOMS)) @@ -2919,8 +3240,8 @@ static __maybe_unused void JS_DumpString(JSRuntime *rt, JSString *p) printf(""); return; } - if (p->header.ref_count != 1) - printf("%d", p->header.ref_count); + if (JS_REF_COUNT(p) != 1) + printf("%d", JS_REF_COUNT(p)); if (p->is_wide_char) putchar('L'); sep = '\"'; @@ -3042,7 +3363,7 @@ JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v) if (!__JS_AtomIsConst(v)) { p = rt->atom_array[v]; - p->header.ref_count++; + JS_REF_COUNT(p)++; } return v; } @@ -3055,7 +3376,7 @@ JSAtom JS_DupAtom(JSContext *ctx, JSAtom v) if (!__JS_AtomIsConst(v)) { rt = ctx->rt; p = rt->atom_array[v]; - p->header.ref_count++; + JS_REF_COUNT(p)++; } return v; } @@ -3121,7 +3442,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) i = js_get_atom_index(rt, str); /* reduce string refcount and increase atom's unless constant */ if (__JS_AtomIsConst(i)) - str->header.ref_count--; + JS_REF_COUNT(str)--; return i; } /* try and locate an already registered atom */ @@ -3137,7 +3458,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) p->len == len && js_string_memcmp(p, str, len) == 0) { if (!__JS_AtomIsConst(i)) - p->header.ref_count++; + JS_REF_COUNT(p)++; goto done; } i = p->hash_next; @@ -3177,7 +3498,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) js_free_rt(rt, new_array); goto fail; } - p->header.ref_count = 1; /* not refcounted */ + JS_REF_COUNT(p) = 1; /* not refcounted */ p->atom_type = JS_ATOM_TYPE_SYMBOL; #ifdef ENABLE_DUMPS // JS_DUMP_LEAKS list_add_tail(&p->link, &rt->string_list); @@ -3209,7 +3530,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) 1 - str->is_wide_char); if (unlikely(!p)) goto fail; - p->header.ref_count = 1; + JS_REF_COUNT(p) = 1; p->is_wide_char = str->is_wide_char; p->len = str->len; p->kind = JS_STRING_KIND_NORMAL; @@ -3224,7 +3545,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */ if (!p) return JS_ATOM_NULL; - p->header.ref_count = 1; + JS_REF_COUNT(p) = 1; p->is_wide_char = 1; /* Hack to represent NULL as a JSString */ p->len = 0; p->kind = JS_STRING_KIND_NORMAL; @@ -3296,7 +3617,7 @@ static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, p->is_wide_char == 0 && memcmp(str8(p), str, len) == 0) { if (!__JS_AtomIsConst(i)) - p->header.ref_count++; + JS_REF_COUNT(p)++; return i; } i = p->hash_next; @@ -3349,7 +3670,7 @@ static void __JS_FreeAtom(JSRuntime *rt, uint32_t i) JSAtomStruct *p; p = rt->atom_array[i]; - if (--p->header.ref_count > 0) + if (--JS_REF_COUNT(p) > 0) return; JS_FreeAtomStruct(rt, p); } @@ -3982,7 +4303,7 @@ static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end) slice = (void *)&q[1]; slice->parent = p; slice->start = start << p->is_wide_char; // chars -> bytes - p->header.ref_count++; + JS_REF_COUNT(p)++; return JS_MKPTR(JS_TAG_STRING, q); } if (p->is_wide_char) { @@ -4892,7 +5213,7 @@ static JSValue js_new_string_rope(JSContext *ctx, JSValue op1, JSValue op2) r = js_malloc(ctx, sizeof(*r)); if (!r) goto fail; - r->header.ref_count = 1; + JS_REF_COUNT(r) = 1; r->len = len; r->is_wide_char = is_wide_char; r->depth = depth + 1; @@ -5051,7 +5372,7 @@ static JSValue js_linearize_string_rope(JSContext *ctx, JSValueConst rope) if (string_buffer_concat_value(b, rope)) goto fail; ret = string_buffer_end(b); - if (r->header.ref_count > 1) { + if (JS_REF_COUNT(r) > 1) { /* update the rope so that it won't need to be linearized again */ JS_FreeValue(ctx, r->left); JS_FreeValue(ctx, r->right); @@ -5116,7 +5437,7 @@ static JSValue JS_ConcatString2(JSContext *ctx, JSValue op1, JSValue op2) if (p2->len == 0) { goto ret_op1; } - if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char + if (JS_REF_COUNT(p1) == 1 && p1->is_wide_char == p2->is_wide_char && js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) { /* Concatenate in place in available space at the end of p1 */ if (p1->is_wide_char) { @@ -5224,17 +5545,26 @@ static inline size_t get_shape_size(size_t hash_size, size_t prop_size) static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size) { - return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); + (void)hash_size; + return (JSShape *)sh_alloc; /* shape sits at the allocation start */ } +/* one-past-the-end of the hash bucket array; buckets are addressed as + prop_hash_end(sh)[-h - 1] for h in [0, prop_hash_mask], in BOTH layouts. */ static inline uint32_t *prop_hash_end(JSShape *sh) { - return (uint32_t *)sh; + return sh->hash_table + sh->prop_hash_mask + 1; +} + +/* the JSShapeProperty array */ +static inline JSShapeProperty *get_shape_prop(JSShape *sh) +{ + return (JSShapeProperty *)(void *)(sh->hash_table + sh->prop_hash_mask + 1); } static inline void *get_alloc_from_shape(JSShape *sh) { - return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1); + return sh; /* shape sits at the allocation start */ } static int init_shape_hash(JSRuntime *rt) @@ -5330,14 +5660,16 @@ static inline JSShape *js_new_shape_nohash(JSContext *ctx, JSObject *proto, if (!sh_alloc) return NULL; sh = get_shape_from_alloc(sh_alloc, hash_size); - sh->header.ref_count = 1; + JS_REF_COUNT(sh) = 1; add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); if (proto) js_dup(JS_MKPTR(JS_TAG_OBJECT, proto)); sh->proto = proto; + /* prop_hash_mask must be set before prop_hash_end(sh) is used, as the hash + location depends on it in the merged-header layout. */ + sh->prop_hash_mask = hash_size - 1; memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) * hash_size); - sh->prop_hash_mask = hash_size - 1; sh->prop_size = prop_size; sh->prop_count = 0; sh->deleted_prop_count = 0; @@ -5426,13 +5758,13 @@ static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1) sh_alloc1 = get_alloc_from_shape(sh1); memcpy(sh_alloc, sh_alloc1, size); sh = get_shape_from_alloc(sh_alloc, hash_size); - sh->header.ref_count = 1; + JS_REF_COUNT(sh) = 1; add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); sh->is_hashed = false; if (sh->proto) { js_dup(JS_MKPTR(JS_TAG_OBJECT, sh->proto)); } - for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) { + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { JS_DupAtom(ctx, pr->atom); } return sh; @@ -5440,7 +5772,7 @@ static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1) static JSShape *js_dup_shape(JSShape *sh) { - sh->header.ref_count++; + JS_REF_COUNT(sh)++; return sh; } @@ -5449,13 +5781,13 @@ static void js_free_shape0(JSRuntime *rt, JSShape *sh) uint32_t i; JSShapeProperty *pr; - assert(sh->header.ref_count == 0); + assert(JS_REF_COUNT(sh) == 0); if (sh->is_hashed) js_shape_hash_unlink(rt, sh); if (sh->proto != NULL) { JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); } - pr = sh->prop; + pr = get_shape_prop(sh); for(i = 0; i < sh->prop_count; i++) { JS_FreeAtomRT(rt, pr->atom); pr++; @@ -5466,7 +5798,7 @@ static void js_free_shape0(JSRuntime *rt, JSShape *sh) static void js_free_shape(JSRuntime *rt, JSShape *sh) { - if (unlikely(--sh->header.ref_count <= 0)) { + if (unlikely(--JS_REF_COUNT(sh) <= 0)) { js_free_shape0(rt, sh); } } @@ -5510,15 +5842,22 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh, return -1; sh = get_shape_from_alloc(sh_alloc, new_hash_size); list_del(&old_sh->header.link); - /* copy all the fields and the properties */ - memcpy(sh, old_sh, - sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count); + /* copy the shape header, then the properties. Their location relative + to the struct differs by layout, so copy via get_shape_prop(). */ + memcpy(sh, old_sh, sizeof(JSShape)); list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + /* the GC/refcount fields live in the block header, not the struct, so + the memcpy above did not carry them: transfer them explicitly */ + JS_REF_COUNT(sh) = JS_REF_COUNT(old_sh); + JS_GC_TYPE(sh) = JS_GC_TYPE(old_sh); + JS_GC_MARK(sh) = JS_GC_MARK(old_sh); new_hash_mask = new_hash_size - 1; sh->prop_hash_mask = new_hash_mask; + memcpy(get_shape_prop(sh), get_shape_prop(old_sh), + sizeof(JSShapeProperty) * old_sh->prop_count); memset(prop_hash_end(sh) - new_hash_size, 0, sizeof(prop_hash_end(sh)[0]) * new_hash_size); - for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) { + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { if (pr->atom != JS_ATOM_NULL) { h = ((uintptr_t)pr->atom & new_hash_mask); pr->hash_next = prop_hash_end(sh)[-h - 1]; @@ -5575,13 +5914,21 @@ static int compact_properties(JSContext *ctx, JSObject *p) list_del(&old_sh->header.link); memcpy(sh, old_sh, sizeof(JSShape)); list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); - + /* the GC/refcount fields live in the block header, not the struct, so the + memcpy above did not carry them: transfer them explicitly */ + JS_REF_COUNT(sh) = JS_REF_COUNT(old_sh); + JS_GC_TYPE(sh) = JS_GC_TYPE(old_sh); + JS_GC_MARK(sh) = JS_GC_MARK(old_sh); + + /* set the new hash mask before prop_hash_end()/get_shape_prop() are used, + as their locations depend on it in the merged-header layout */ + sh->prop_hash_mask = new_hash_mask; memset(prop_hash_end(sh) - new_hash_size, 0, sizeof(prop_hash_end(sh)[0]) * new_hash_size); j = 0; - old_pr = old_sh->prop; - pr = sh->prop; + old_pr = get_shape_prop(old_sh); + pr = get_shape_prop(sh); prop = p->prop; for(i = 0; i < sh->prop_count; i++) { if (old_pr->atom != JS_ATOM_NULL) { @@ -5643,7 +5990,7 @@ static int add_shape_property(JSContext *ctx, JSShape **psh, } /* Initialize the new shape property. The object property at p->prop[sh->prop_count] is uninitialized */ - prop = sh->prop; + prop = get_shape_prop(sh); pr = &prop[sh->prop_count++]; pr->atom = JS_DupAtom(ctx, atom); pr->flags = prop_flags; @@ -5693,12 +6040,12 @@ static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh, sh1->proto == sh->proto && sh1->prop_count == ((n = sh->prop_count) + 1)) { for(i = 0; i < n; i++) { - if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) || - unlikely(sh1->prop[i].flags != sh->prop[i].flags)) + if (unlikely(get_shape_prop(sh1)[i].atom != get_shape_prop(sh)[i].atom) || + unlikely(get_shape_prop(sh1)[i].flags != get_shape_prop(sh)[i].flags)) goto next; } - if (unlikely(sh1->prop[n].atom != atom) || - unlikely(sh1->prop[n].flags != prop_flags)) + if (unlikely(get_shape_prop(sh1)[n].atom != atom) || + unlikely(get_shape_prop(sh1)[n].flags != prop_flags)) goto next; return sh1; } @@ -5714,11 +6061,11 @@ static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) /* XXX: should output readable class prototype */ printf("%5d %3d%c %14p %5d %5d", i, - sh->header.ref_count, " *"[sh->is_hashed], + JS_REF_COUNT(sh), " *"[sh->is_hashed], (void *)sh->proto, sh->prop_size, sh->prop_count); for(j = 0; j < sh->prop_count; j++) { printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), - sh->prop[j].atom)); + get_shape_prop(sh)[j].atom)); } printf("\n"); } @@ -5742,7 +6089,7 @@ static __maybe_unused void JS_DumpShapes(JSRuntime *rt) /* dump non-hashed shapes */ list_for_each(el, &rt->gc_obj_list) { gp = list_entry(el, JSGCObjectHeader, link); - if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + if (JS_GC_TYPE(gp) == JS_GC_OBJ_TYPE_JS_OBJECT) { p = (JSObject *)gp; if (!p->shape->is_hashed) { JS_DumpShape(rt, -1, p->shape); @@ -5782,7 +6129,7 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas js_free(ctx, p); fail: if (props) { - JSShapeProperty *prs = sh->prop; + JSShapeProperty *prs = get_shape_prop(sh); for(i = 0; i < sh->prop_count; i++) { free_property(ctx->rt, &props[i], prs->flags); prs++; @@ -5864,7 +6211,7 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas } break; } - p->header.ref_count = 1; + JS_REF_COUNT(p) = 1; add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT); if (props) { for(i = 0; i < sh->prop_count; i++) @@ -5971,7 +6318,7 @@ JSValue JS_NewObjectFrom(JSContext *ctx, int count, const JSAtom *props, p = JS_VALUE_GET_OBJ(obj); sh = p->shape; assert(sh->is_hashed); - assert(sh->header.ref_count == 1); + assert(JS_REF_COUNT(sh) == 1); js_shape_hash_unlink(rt, sh); if (resize_properties(ctx, &sh, p, count)) { js_shape_hash_link(rt, sh); @@ -5981,7 +6328,7 @@ JSValue JS_NewObjectFrom(JSContext *ctx, int count, const JSAtom *props, p->shape = sh; for (i = 0; i < count; i++) { atom = props[i]; - pr = &sh->prop[i]; + pr = &get_shape_prop(sh)[i]; sh->hash = shape_hash(shape_hash(sh->hash, atom), JS_PROP_C_W_E); h = atom & sh->prop_hash_mask; hash = &prop_hash_end(sh)[-h - 1]; @@ -6468,7 +6815,7 @@ static inline JSShapeProperty *find_own_property1(JSObject *p, JSAtom atom) sh = p->shape; h = (uintptr_t)atom & sh->prop_hash_mask; h = prop_hash_end(sh)[-h - 1]; - prop = sh->prop; + prop = get_shape_prop(sh); while (h) { pr = &prop[h - 1]; if (likely(pr->atom == atom)) { @@ -6489,7 +6836,7 @@ static inline JSShapeProperty *find_own_property(JSProperty **ppr, sh = p->shape; h = (uintptr_t)atom & sh->prop_hash_mask; h = prop_hash_end(sh)[-h - 1]; - prop = sh->prop; + prop = get_shape_prop(sh); while (h) { pr = &prop[h - 1]; if (likely(pr->atom == atom)) { @@ -6506,8 +6853,8 @@ static inline JSShapeProperty *find_own_property(JSProperty **ppr, static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) { if (var_ref) { - assert(var_ref->header.ref_count > 0); - if (--var_ref->header.ref_count == 0) { + assert(JS_REF_COUNT(var_ref) > 0); + if (--JS_REF_COUNT(var_ref) == 0) { if (var_ref->is_detached) { JS_FreeValueRT(rt, var_ref->value); remove_gc_object(&var_ref->header); @@ -6678,7 +7025,7 @@ static void free_object(JSRuntime *rt, JSObject *p) freeing cycles */ /* free all the fields */ sh = p->shape; - pr = sh->prop; + pr = get_shape_prop(sh); for(i = 0; i < sh->prop_count; i++) { free_property(rt, &p->prop[i], pr->flags); pr++; @@ -6707,7 +7054,7 @@ static void free_object(JSRuntime *rt, JSObject *p) p->u.func.home_object = NULL; remove_gc_object(&p->header); - if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) { + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && JS_REF_COUNT(p) != 0) { list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); } else { js_free_rt(rt, p); @@ -6716,7 +7063,7 @@ static void free_object(JSRuntime *rt, JSObject *p) static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp) { - switch(gp->gc_obj_type) { + switch(JS_GC_TYPE(gp)) { case JS_GC_OBJ_TYPE_JS_OBJECT: free_object(rt, (JSObject *)gp); break; @@ -6739,7 +7086,7 @@ static void free_zero_refcount(JSRuntime *rt) if (el == &rt->gc_zero_ref_count_list) break; p = list_entry(el, JSGCObjectHeader, link); - assert(p->ref_count == 0); + assert(JS_REF_COUNT(p) == 0); free_gc_object(rt, p); } rt->gc_phase = JS_GC_PHASE_NONE; @@ -6815,8 +7162,8 @@ static void js_free_value_rt(JSRuntime *rt, JSValue v) 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) { + void *p = JS_VALUE_GET_PTR(v); + if (--JS_REF_COUNT(p) <= 0) { js_free_value_rt(rt, v); } } @@ -6832,8 +7179,8 @@ void JS_FreeValue(JSContext *ctx, JSValue v) static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, JSGCObjectTypeEnum type) { - h->mark = 0; - h->gc_obj_type = type; + JS_GC_MARK(h) = 0; + JS_GC_TYPE(h) = type; list_add_tail(&h->link, &rt->gc_obj_list); } @@ -6875,7 +7222,7 @@ static void mark_weak_map_value(JSRuntime *rt, JSWeakRefRecord *first_weak_ref, static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, JS_MarkFunc *mark_func) { - switch(gp->gc_obj_type) { + switch(JS_GC_TYPE(gp)) { case JS_GC_OBJ_TYPE_JS_OBJECT: { JSObject *p = (JSObject *)gp; @@ -6885,7 +7232,7 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, sh = p->shape; mark_func(rt, &sh->header); /* mark all the fields */ - prs = sh->prop; + prs = get_shape_prop(sh); for(i = 0; i < sh->prop_count; i++) { JSProperty *pr = &p->prop[i]; if (prs->atom != JS_ATOM_NULL) { @@ -6973,9 +7320,9 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p) { - assert(p->ref_count > 0); - p->ref_count--; - if (p->ref_count == 0 && p->mark == 1) { + assert(JS_REF_COUNT(p) > 0); + JS_REF_COUNT(p)--; + if (JS_REF_COUNT(p) == 0 && JS_GC_MARK(p) == 1) { list_del(&p->link); list_add_tail(&p->link, &rt->tmp_obj_list); } @@ -6993,10 +7340,10 @@ static void gc_decref(JSRuntime *rt) tmp_obj_list */ list_for_each_safe(el, el1, &rt->gc_obj_list) { p = list_entry(el, JSGCObjectHeader, link); - assert(p->mark == 0); + assert(JS_GC_MARK(p) == 0); mark_children(rt, p, gc_decref_child); - p->mark = 1; - if (p->ref_count == 0) { + JS_GC_MARK(p) = 1; + if (JS_REF_COUNT(p) == 0) { list_del(&p->link); list_add_tail(&p->link, &rt->tmp_obj_list); } @@ -7005,19 +7352,19 @@ static void gc_decref(JSRuntime *rt) static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p) { - p->ref_count++; - if (p->ref_count == 1) { + JS_REF_COUNT(p)++; + if (JS_REF_COUNT(p) == 1) { /* ref_count was 0: remove from tmp_obj_list and add at the end of gc_obj_list */ list_del(&p->link); list_add_tail(&p->link, &rt->gc_obj_list); - p->mark = 0; /* reset the mark for the next GC call */ + JS_GC_MARK(p) = 0; /* reset the mark for the next GC call */ } } static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p) { - p->ref_count++; + JS_REF_COUNT(p)++; } static void gc_scan(JSRuntime *rt) @@ -7028,8 +7375,8 @@ static void gc_scan(JSRuntime *rt) /* keep the objects with a refcount > 0 and their children. */ list_for_each(el, &rt->gc_obj_list) { p = list_entry(el, JSGCObjectHeader, link); - assert(p->ref_count > 0); - p->mark = 0; /* reset the mark for the next GC call */ + assert(JS_REF_COUNT(p) > 0); + JS_GC_MARK(p) = 0; /* reset the mark for the next GC call */ mark_children(rt, p, gc_scan_incref_child); } @@ -7058,7 +7405,7 @@ static void gc_free_cycles(JSRuntime *rt) /* Only need to free the GC object associated with JS values. The rest will be automatically removed because they must be referenced by them. */ - switch(p->gc_obj_type) { + switch(JS_GC_TYPE(p)) { case JS_GC_OBJ_TYPE_JS_OBJECT: case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: #ifdef ENABLE_DUMPS // JS_DUMP_GC_FREE @@ -7083,8 +7430,8 @@ static void gc_free_cycles(JSRuntime *rt) list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) { p = list_entry(el, JSGCObjectHeader, link); - assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || - p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + assert(JS_GC_TYPE(p) == JS_GC_OBJ_TYPE_JS_OBJECT || + JS_GC_TYPE(p) == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); js_free_rt(rt, p); } @@ -7134,7 +7481,7 @@ 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; + double s_ref_count = JS_REF_COUNT(str); 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); @@ -7256,10 +7603,10 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) JSShapeProperty *prs; /* XXX: could count the other GC object types too */ - if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) { + if (JS_GC_TYPE(gp) == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) { compute_bytecode_size((JSFunctionBytecode *)gp, hp); continue; - } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) { + } else if (JS_GC_TYPE(gp) != JS_GC_OBJ_TYPE_JS_OBJECT) { continue; } p = (JSObject *)gp; @@ -7269,7 +7616,7 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) s->memory_used_count++; s->prop_size += sh->prop_size * sizeof(*p->prop); s->prop_count += sh->prop_count; - prs = sh->prop; + 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)) { @@ -7324,7 +7671,7 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) s->js_func_size += b->closure_var_count * sizeof(*var_refs); for (i = 0; i < b->closure_var_count; i++) { if (var_refs[i]) { - double ref_count = var_refs[i]->header.ref_count; + double ref_count = JS_REF_COUNT(var_refs[i]); s->memory_used_count += 1 / ref_count; s->js_func_size += sizeof(*var_refs[i]) / ref_count; /* handle non object closed values */ @@ -7518,7 +7865,7 @@ void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) list_for_each(el, &rt->gc_obj_list) { JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); JSObject *p; - if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + if (JS_GC_TYPE(gp) == JS_GC_OBJ_TYPE_JS_OBJECT) { p = (JSObject *)gp; obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++; } @@ -9148,7 +9495,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, exotic_count = 0; tab_exotic = NULL; sh = p->shape; - for(i = 0, prs = sh->prop; i < sh->prop_count; i++, prs++) { + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { atom = prs->atom; if (atom != JS_ATOM_NULL) { is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); @@ -9235,7 +9582,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, num_sorted = true; sh = p->shape; - for(i = 0, prs = sh->prop; i < sh->prop_count; i++, prs++) { + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { atom = prs->atom; if (atom != JS_ATOM_NULL) { is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); @@ -9781,7 +10128,7 @@ static JSProperty *add_property(JSContext *ctx, p->shape = js_dup_shape(new_sh); js_free_shape(ctx->rt, sh); return &p->prop[new_sh->prop_count - 1]; - } else if (sh->header.ref_count != 1) { + } else if (JS_REF_COUNT(sh) != 1) { /* if the shape is shared, clone it */ new_sh = js_clone_shape(ctx, sh); if (!new_sh) @@ -9793,7 +10140,7 @@ static JSProperty *add_property(JSContext *ctx, p->shape = new_sh; } } - assert(p->shape->header.ref_count == 1); + assert(JS_REF_COUNT(p->shape) == 1); if (add_shape_property(ctx, &p->shape, p, prop, prop_flags)) return NULL; return &p->prop[p->shape->prop_count - 1]; @@ -9861,7 +10208,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) sh = p->shape; h1 = atom & sh->prop_hash_mask; h = prop_hash_end(sh)[-h1 - 1]; - prop = sh->prop; + prop = get_shape_prop(sh); lpr = NULL; lpr_idx = 0; /* prevent warning */ while (h != 0) { @@ -9872,13 +10219,13 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) return false; /* realloc the shape if needed */ if (lpr) - lpr_idx = lpr - sh->prop; + 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 = &sh->prop[lpr_idx]; + lpr = &get_shape_prop(sh)[lpr_idx]; lpr->hash_next = pr->hash_next; } else { prop_hash_end(sh)[-h1 - 1] = pr->hash_next; @@ -9967,7 +10314,7 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, if (ret) return -1; /* JS_ToArrayLengthFree() must be done before the read-only test */ - if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE))) + if (unlikely(!(get_shape_prop(p->shape)[0].flags & JS_PROP_WRITABLE))) return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); if (likely(p->fast_array)) { @@ -10011,7 +10358,7 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, passes in case one of the property is not configurable */ cur_len = len; - for(i = 0, pr = sh->prop; i < sh->prop_count; + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { if (pr->atom != JS_ATOM_NULL && JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { @@ -10022,7 +10369,7 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, } } - for(i = 0, pr = sh->prop; i < sh->prop_count; + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { if (pr->atom != JS_ATOM_NULL && JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { @@ -10031,7 +10378,7 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, delete_property(ctx, p, pr->atom); /* WARNING: the shape may have been modified */ sh = p->shape; - pr = &sh->prop[i]; + pr = &get_shape_prop(sh)[i]; } } } @@ -10089,7 +10436,7 @@ static int add_fast_array_element(JSContext *ctx, JSObject *p, if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) { array_len = JS_VALUE_GET_INT(p->prop[0].u.value); if (new_len > array_len) { - if (unlikely(!(p->shape->prop->flags & JS_PROP_WRITABLE))) { + if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) { JS_FreeValue(ctx, val); return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); } @@ -10662,7 +11009,7 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p, plen = &p->prop[0]; JS_ToUint32(ctx, &len, plen->u.value); if ((idx + 1) > len) { - pslen = p->shape->prop; + pslen = get_shape_prop(p->shape); if (unlikely(!(pslen->flags & JS_PROP_WRITABLE))) return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); /* XXX: should update the length after defining @@ -10770,9 +11117,9 @@ static int js_shape_prepare_update(JSContext *ctx, JSObject *p, sh = p->shape; if (sh->is_hashed) { - if (sh->header.ref_count != 1) { + if (JS_REF_COUNT(sh) != 1) { if (pprs) - idx = *pprs - sh->prop; + idx = *pprs - get_shape_prop(sh); /* clone the shape (the resulting one is no longer hashed) */ sh = js_clone_shape(ctx, sh); if (!sh) @@ -10780,7 +11127,7 @@ static int js_shape_prepare_update(JSContext *ctx, JSObject *p, js_free_shape(ctx->rt, p->shape); p->shape = sh; if (pprs) - *pprs = &sh->prop[idx]; + *pprs = &get_shape_prop(sh)[idx]; } else { js_shape_hash_unlink(ctx->rt, sh); sh->is_hashed = false; @@ -10970,7 +11317,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, property is read-only. */ if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { - prs = p->shape->prop; + prs = get_shape_prop(p->shape); if (js_update_property_flags(ctx, p, &prs, prs->flags & ~JS_PROP_WRITABLE)) return -1; @@ -12257,7 +12604,7 @@ static JSBigInt *js_bigint_new(JSContext *ctx, int len) r = js_malloc(ctx, sizeof(JSBigInt) + len * sizeof(js_limb_t)); if (!r) return NULL; - r->header.ref_count = 1; + JS_REF_COUNT(r) = 1; r->len = len; return r; } @@ -12265,7 +12612,8 @@ static JSBigInt *js_bigint_new(JSContext *ctx, int len) static JSBigInt *js_bigint_set_si(JSBigIntBuf *buf, js_slimb_t a) { JSBigInt *r = (JSBigInt *)buf->big_int_buf; - r->header.ref_count = 0; /* fail safe */ + /* r points into a stack JSBigIntBuf, not an arena block, so it has no + block-header ref_count slot; this temp is never refcounted/freed. */ r->len = 1; r->tab[0] = a; return r; @@ -12274,7 +12622,7 @@ static JSBigInt *js_bigint_set_si(JSBigIntBuf *buf, js_slimb_t a) static JSBigInt *js_bigint_set_si64(JSBigIntBuf *buf, int64_t a) { JSBigInt *r = (JSBigInt *)buf->big_int_buf; - r->header.ref_count = 0; /* fail safe */ + /* stack JSBigIntBuf: no block-header ref_count slot (see js_bigint_set_si) */ if (a >= INT32_MIN && a <= INT32_MAX) { r->len = 1; r->tab[0] = a; @@ -12375,7 +12723,7 @@ static JSBigInt *js_bigint_normalize1(JSContext *ctx, JSBigInt *a, int l) { js_limb_t v; - assert(a->header.ref_count == 1); + assert(JS_REF_COUNT(a) == 1); while (l > 1) { v = a->tab[l - 1]; if ((v != 0 && v != -1) || @@ -14377,10 +14725,10 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) sh = p->shape; /* the shape can be NULL while freeing an object */ printf("%14p %4d ", (void *)p, - p->header.ref_count); + JS_REF_COUNT(p)); if (sh) { printf("%3d%c %14p ", - sh->header.ref_count, + JS_REF_COUNT(sh), " *"[sh->is_hashed], (void *)sh->proto); } else { @@ -14424,7 +14772,7 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) if (sh) { printf("{ "); - for(i = 0, prs = sh->prop; i < sh->prop_count; i++, prs++) { + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { if (prs->atom != JS_ATOM_NULL) { pr = &p->prop[i]; if (!is_first) @@ -14471,13 +14819,13 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) { - if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + if (JS_GC_TYPE(p) == JS_GC_OBJ_TYPE_JS_OBJECT) { JS_DumpObject(rt, (JSObject *)p); } else { printf("%14p %4d ", (void *)p, - p->ref_count); - switch(p->gc_obj_type) { + JS_REF_COUNT(p)); + switch(JS_GC_TYPE(p)) { case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: printf("[function bytecode]"); break; @@ -14494,7 +14842,7 @@ static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) printf("[js_context]"); break; default: - printf("[unknown %d]", p->gc_obj_type); + printf("[unknown %d]", JS_GC_TYPE(p)); break; } printf("\n"); @@ -16410,7 +16758,7 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) JSShapeProperty *prs; /* check that there are no enumerable normal fields */ sh = p->shape; - for(i = 0, prs = sh->prop; i < sh->prop_count; i++, prs++) { + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { if (prs->flags & JS_PROP_ENUMERABLE) goto normal_case; } @@ -16502,7 +16850,7 @@ static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) JSShapeProperty *prs; if (it->idx >= sh->prop_count) goto done; - prs = &sh->prop[it->idx]; + prs = &get_shape_prop(sh)[it->idx]; prop = prs->atom; it->idx++; if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE)) @@ -17069,7 +17417,7 @@ static JSVarRef *js_create_var_ref(JSContext *ctx, bool is_gc_object) var_ref = js_malloc(ctx, sizeof(JSVarRef)); if (!var_ref) return NULL; - var_ref->header.ref_count = 1; + JS_REF_COUNT(var_ref) = 1; var_ref->is_detached = true; var_ref->value = JS_UNDEFINED; var_ref->pvalue = &var_ref->value; @@ -17105,7 +17453,7 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, var_ref = sf->var_refs[var_ref_idx]; if (var_ref) { /* reference to the already created local variable */ - var_ref->header.ref_count++; + JS_REF_COUNT(var_ref)++; return var_ref; } @@ -17113,7 +17461,7 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, var_ref = js_malloc(ctx, sizeof(JSVarRef)); if (!var_ref) return NULL; - var_ref->header.ref_count = 1; + JS_REF_COUNT(var_ref) = 1; var_ref->is_detached = false; var_ref->is_lexical = false; var_ref->is_const = false; @@ -17128,7 +17476,7 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, var_ref = js_malloc(ctx, sizeof(JSVarRef)); if (!var_ref) return NULL; - var_ref->header.ref_count = 1; + JS_REF_COUNT(var_ref) = 1; var_ref->is_detached = true; var_ref->value = js_dup(*pvalue); var_ref->pvalue = &var_ref->value; @@ -17170,7 +17518,7 @@ static JSValue js_closure2(JSContext *ctx, JSValue func_obj, case JS_CLOSURE_REF: case JS_CLOSURE_GLOBAL_REF: var_ref = cur_var_refs[cv->var_idx]; - var_ref->header.ref_count++; + JS_REF_COUNT(var_ref)++; break; default: abort(); @@ -18678,7 +19026,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, goto exception; if (opcode == OP_make_var_ref_ref) { var_ref = var_refs[idx]; - var_ref->header.ref_count++; + JS_REF_COUNT(var_ref)++; } else { var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref); if (!var_ref) @@ -20972,7 +21320,7 @@ static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s) static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s) { - if (--s->header.ref_count == 0) { + if (--JS_REF_COUNT(s) == 0) { js_async_function_free0(rt, s); } } @@ -21014,7 +21362,7 @@ static int js_async_function_resolve_create(JSContext *ctx, return -1; } p = JS_VALUE_GET_OBJ(resolving_funcs[i]); - s->header.ref_count++; + JS_REF_COUNT(s)++; p->u.async_function_data = s; } return 0; @@ -21126,7 +21474,7 @@ static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, s = js_mallocz(ctx, sizeof(*s)); if (!s) return JS_EXCEPTION; - s->header.ref_count = 1; + JS_REF_COUNT(s) = 1; add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); s->is_active = false; s->resolving_funcs[0] = JS_UNDEFINED; @@ -29770,7 +30118,7 @@ static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name) JS_FreeAtom(ctx, name); return NULL; } - m->header.ref_count = 1; + JS_REF_COUNT(m) = 1; m->module_name = name; m->module_ns = JS_UNDEFINED; m->func_obj = JS_UNDEFINED; @@ -30586,7 +30934,7 @@ static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) JS_PROP_VARREF); if (!pr) goto fail; - var_ref->header.ref_count++; + JS_REF_COUNT(var_ref)++; pr->u.var_ref = var_ref; } break; @@ -30677,7 +31025,7 @@ static JSVarRef *js_create_module_var(JSContext *ctx, bool is_lexical) var_ref = js_malloc(ctx, sizeof(JSVarRef)); if (!var_ref) return NULL; - var_ref->header.ref_count = 1; + JS_REF_COUNT(var_ref) = 1; if (is_lexical) var_ref->value = JS_UNINITIALIZED; else @@ -30707,7 +31055,7 @@ static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m) p = JS_VALUE_GET_OBJ(func_obj); p->u.func.function_bytecode = b; - b->header.ref_count++; + JS_REF_COUNT(b)++; p->u.func.home_object = NULL; p->u.func.var_refs = NULL; if (b->closure_var_count) { @@ -30934,7 +31282,7 @@ static int js_inner_module_linking(JSContext *ctx, JSModuleDef *m, p1 = JS_VALUE_GET_OBJ(res_m->func_obj); var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; } - var_ref->header.ref_count++; + JS_REF_COUNT(var_ref)++; var_refs[mi->var_idx] = var_ref; module_trace(ctx, "local export (var_ref=%p)\n", (void *)var_ref); @@ -30949,7 +31297,7 @@ static int js_inner_module_linking(JSContext *ctx, JSModuleDef *m, JSExportEntry *me = &m->export_entries[i]; if (me->export_type == JS_EXPORT_TYPE_LOCAL) { var_ref = var_refs[me->u.local.var_idx]; - var_ref->header.ref_count++; + JS_REF_COUNT(var_ref)++; me->u.local.var_ref = var_ref; } } @@ -36350,7 +36698,7 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) b = js_mallocz(ctx, function_size); if (!b) goto fail; - b->header.ref_count = 1; + JS_REF_COUNT(b) = 1; b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset); b->byte_code_len = fd->byte_code.size; @@ -36471,7 +36819,7 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) js_free_rt(rt, b->source); remove_gc_object(&b->header); - if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) { + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && JS_REF_COUNT(b) != 0) { list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list); } else { js_free_rt(rt, b); @@ -38266,7 +38614,7 @@ static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj) for(pass = 0; pass < 2; pass++) { if (pass == 1) bc_put_leb128(s, prop_count); - for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) { + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { atom = pr->atom; if (atom != JS_ATOM_NULL && (pr->flags & JS_PROP_ENUMERABLE)) { if (pr->flags & JS_PROP_TMASK) { @@ -38998,7 +39346,6 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) int closure_var_offset, vardefs_offset; memset(&bc, 0, sizeof(bc)); - bc.header.ref_count = 1; //bc.gc_header.mark = 0; if (bc_get_u16(s, &v16)) @@ -39055,7 +39402,7 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) memcpy(b, &bc, sizeof(*b)); bc.func_name = JS_ATOM_NULL; - b->header.ref_count = 1; + JS_REF_COUNT(b) = 1; if (local_count != 0) { b->vardefs = (void *)((uint8_t*)b + vardefs_offset); } @@ -43438,7 +43785,7 @@ static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, ctx->std_array_prototype)) { uint32_t array_len, new_len; if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT && - (p->shape->prop->flags & JS_PROP_WRITABLE))) { + (get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) { array_len = JS_VALUE_GET_INT(p->prop[0].u.value); new_len = array_len + argc; if (likely(new_len >= array_len && new_len <= (uint32_t)INT32_MAX)) { /* no overflow and within fast-array bounds */ @@ -62160,12 +62507,12 @@ static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref) bool enqueue = !rt->in_free; if (enqueue && JS_IsObject(fre->held_val)) { JSObject *p = JS_VALUE_GET_OBJ(fre->held_val); - if (p->free_mark || p->header.mark) + if (p->free_mark || JS_GC_MARK(p)) enqueue = false; } if (enqueue && JS_IsObject(fre->cb)) { JSObject *p = JS_VALUE_GET_OBJ(fre->cb); - if (p->free_mark || p->header.mark) + if (p->free_mark || JS_GC_MARK(p)) enqueue = false; } if (enqueue) {