You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by va...@apache.org on 2026/06/02 06:20:31 UTC
(couchdb) branch update-quickjs-jun-2-2026 created (now 72694d78e)
This is an automated email from the ASF dual-hosted git repository.
nickva pushed a change to branch update-quickjs-jun-2-2026
in repository https://gitbox.apache.org/repos/asf/couchdb.git
at 72694d78e QuickJS Update: 10-40% speedup, compilation fixes
This branch includes the following new commits:
new 72694d78e QuickJS Update: 10-40% speedup, compilation fixes
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(couchdb) 01/01: QuickJS Update: 10-40% speedup, compilation fixes
Posted by va...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
nickva pushed a commit to branch update-quickjs-jun-2-2026
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 72694d78e27e1d81a6ff6724300604ee57a83bd3
Author: Nick Vatamaniuc <va...@gmail.com>
AuthorDate: Tue Jun 2 00:23:35 2026 -0400
QuickJS Update: 10-40% speedup, compilation fixes
* Add custom malloc for small blocks /w an 11% speedup in v8 bench (as claimed
upstream). My own local `make microbench` showed a 13458 nsec -> 7385 nsec
improvement which is closer to 40%(!)
https://github.com/bellard/quickjs/commit/99e9181d117a0cb65d37cb11a55a34bf29853309
* Add Uint8Array base64/hex methods (initial patch by saghul)
https://github.com/bellard/quickjs/commit/e182e3df5c51017a7faa97a654b3f2b9c6330448
* Fix `memcpy()` undefined behavior if a pointer is NULL with zero size
https://github.com/bellard/quickjs/commit/1f50b39e99d12042fc2e2ce3b754719fa42af753
* Fixed error handling in `os.exec()`
https://github.com/bellard/quickjs/commit/b1b4733bd9f75509ffbf1243b754a8e61278b790
* Add missing NULL pointer check https://github.com/bellard/quickjs/issues/504
* Use __EMSCRIPTEN__ define instead of EMSCRIPTEN
https://github.com/bellard/quickjs/commit/9b90125510d8a57bb1b687a798237d8b7133fb49
* Heap-Use-After-Free in Atomics Operations via ResizableArrayBuffer
https://github.com/bellard/quickjs/issues/508
* Expose Unicode version in C API https://github.com/bellard/quickjs/issues/509
* Fix member access on non-decimal numeric literals
https://github.com/bellard/quickjs/commit/66afb7234cf53957094509014cc5e97fa152b0f4
* Avoid initializer-string warning for the digits array
https://github.com/bellard/quickjs/commit/d38eea93365879b5bff8b649d8dbb4dc1b849e56
WARNING: `couchjs.c` inlines refcount helpers `(JS_FreeValue/JS_DupValue/...)`
from `quickjs.h`. Rebar port compiler rebuilds couchjs.o when couchjs.c changes
not when included header changes. This update alters the header (refcount
layout) so a stale couchjs.o would mismatch the new libquickjs.a and corrupt
refcounts. TL;DR do clean -xffd build with this change.
---
.../patches/01-spidermonkey-185-mode.patch | 6 +-
src/couch_quickjs/patches/02-test262-errors.patch | 4 +-
src/couch_quickjs/quickjs/Changelog | 1 +
src/couch_quickjs/quickjs/cutils.c | 2 +-
src/couch_quickjs/quickjs/libunicode.c | 3 +-
src/couch_quickjs/quickjs/libunicode.h | 5 +
src/couch_quickjs/quickjs/quickjs-atom.h | 4 +
src/couch_quickjs/quickjs/quickjs-libc.c | 4 +-
src/couch_quickjs/quickjs/quickjs.c | 1914 ++++++++++++++++----
src/couch_quickjs/quickjs/quickjs.h | 15 +-
src/couch_quickjs/quickjs/test262.conf | 2 +-
src/couch_quickjs/quickjs/test262_errors.txt | 2 -
12 files changed, 1586 insertions(+), 376 deletions(-)
diff --git a/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch b/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch
index 65f473856..db9c45430 100644
--- a/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch
+++ b/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch
@@ -1,6 +1,6 @@
---- quickjs-master/quickjs.c 2026-03-23 13:45:52
-+++ quickjs/quickjs.c 2026-03-23 17:19:56
-@@ -31462,10 +31462,24 @@
+--- quickjs-master/quickjs.c 2026-05-21 13:02:15
++++ quickjs/quickjs.c 2026-06-02 00:18:00
+@@ -31848,10 +31848,24 @@
if (s->token.val == TOK_FUNCTION ||
(token_is_pseudo_keyword(s, JS_ATOM_async) &&
peek_token(s, TRUE) == TOK_FUNCTION)) {
diff --git a/src/couch_quickjs/patches/02-test262-errors.patch b/src/couch_quickjs/patches/02-test262-errors.patch
index db454f201..10a692f12 100644
--- a/src/couch_quickjs/patches/02-test262-errors.patch
+++ b/src/couch_quickjs/patches/02-test262-errors.patch
@@ -1,5 +1,5 @@
---- quickjs-master/test262_errors.txt 2026-03-23 13:45:52
-+++ quickjs/test262_errors.txt 2026-03-23 17:19:56
+--- quickjs-master/test262_errors.txt 2026-05-21 13:02:15
++++ quickjs/test262_errors.txt 2026-06-02 00:18:00
@@ -23,6 +23,8 @@
test262/test/language/module-code/ambiguous-export-bindings/namespace-unambiguous-if-export-star-as-from-and-import-star-as-and-export.js:74: SyntaxError: export 'foo' in module 'test262/test/language/module-code/ambiguous-export-bindings/namespace-unambiguous-if-import-star-as-and-export.js' is ambiguous
test262/test/language/module-code/ambiguous-export-bindings/namespace-unambiguous-if-export-star-as-from.js:75: SyntaxError: export 'foo' in module 'test262/test/language/module-code/ambiguous-export-bindings/namespace-unambiguous-if-export-star-as-from.js' is ambiguous
diff --git a/src/couch_quickjs/quickjs/Changelog b/src/couch_quickjs/quickjs/Changelog
index 3c08f0c58..697193f3e 100644
--- a/src/couch_quickjs/quickjs/Changelog
+++ b/src/couch_quickjs/quickjs/Changelog
@@ -1,3 +1,4 @@
+- added custom malloc for small blocks (11% faster on bench-v8)
- micro optimizations (30% faster on bench-v8)
- added resizable array buffers
- added ArrayBuffer.prototype.transfer
diff --git a/src/couch_quickjs/quickjs/cutils.c b/src/couch_quickjs/quickjs/cutils.c
index 52ff1649b..6a3aeca45 100644
--- a/src/couch_quickjs/quickjs/cutils.c
+++ b/src/couch_quickjs/quickjs/cutils.c
@@ -315,7 +315,7 @@ int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp)
#if 0
-#if defined(EMSCRIPTEN) || defined(__ANDROID__)
+#if defined(__EMSCRIPTEN__) || defined(__ANDROID__)
static void *rqsort_arg;
static int (*rqsort_cmp)(const void *, const void *, void *);
diff --git a/src/couch_quickjs/quickjs/libunicode.c b/src/couch_quickjs/quickjs/libunicode.c
index 0c510ccb1..0b7b6d0b1 100644
--- a/src/couch_quickjs/quickjs/libunicode.c
+++ b/src/couch_quickjs/quickjs/libunicode.c
@@ -1189,7 +1189,8 @@ int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
goto not_latin1;
}
buf = (int *)dbuf->buf;
- memcpy(buf, src, src_len * sizeof(int));
+ if (src_len != 0)
+ memcpy(buf, src, src_len * sizeof(int));
*pdst = (uint32_t *)buf;
return src_len;
not_latin1: ;
diff --git a/src/couch_quickjs/quickjs/libunicode.h b/src/couch_quickjs/quickjs/libunicode.h
index 5b02c82b4..c2fdeac89 100644
--- a/src/couch_quickjs/quickjs/libunicode.h
+++ b/src/couch_quickjs/quickjs/libunicode.h
@@ -26,6 +26,11 @@
#include <stdint.h>
+/* unicode standard version */
+#define LIBUNICODE_UNICODE_VERSION_MAJOR 17
+#define LIBUNICODE_UNICODE_VERSION_MINOR 0
+#define LIBUNICODE_UNICODE_VERSION_PATCH 0
+
/* define it to include all the unicode tables (40KB larger) */
#define CONFIG_ALL_UNICODE
diff --git a/src/couch_quickjs/quickjs/quickjs-atom.h b/src/couch_quickjs/quickjs/quickjs-atom.h
index dc64f2fb0..13c1ccdb9 100644
--- a/src/couch_quickjs/quickjs/quickjs-atom.h
+++ b/src/couch_quickjs/quickjs/quickjs-atom.h
@@ -190,6 +190,10 @@ DEF(unicodeSets, "unicodeSets")
DEF(not_equal, "not-equal")
DEF(timed_out, "timed-out")
DEF(ok, "ok")
+DEF(toISOString, "toISOString")
+DEF(alphabet, "alphabet")
+DEF(lastChunkHandling, "lastChunkHandling")
+DEF(omitPadding, "omitPadding")
/* */
DEF(toJSON, "toJSON")
DEF(maxByteLength, "maxByteLength")
diff --git a/src/couch_quickjs/quickjs/quickjs-libc.c b/src/couch_quickjs/quickjs/quickjs-libc.c
index c24b6d53e..84f990d94 100644
--- a/src/couch_quickjs/quickjs/quickjs-libc.c
+++ b/src/couch_quickjs/quickjs/quickjs-libc.c
@@ -3309,7 +3309,7 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val,
for(i = 0; i < exec_argc; i++)
JS_FreeCString(ctx, exec_argv[i]);
js_free(ctx, exec_argv);
- if (envp != environ) {
+ if (envp && envp != environ) {
char **p;
p = envp;
while (*p != NULL) {
@@ -3879,7 +3879,7 @@ void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt))
#define OS_PLATFORM "win32"
#elif defined(__APPLE__)
#define OS_PLATFORM "darwin"
-#elif defined(EMSCRIPTEN)
+#elif defined(__EMSCRIPTEN__)
#define OS_PLATFORM "js"
#else
#define OS_PLATFORM "linux"
diff --git a/src/couch_quickjs/quickjs/quickjs.c b/src/couch_quickjs/quickjs/quickjs.c
index 972f9787b..67b574917 100644
--- a/src/couch_quickjs/quickjs/quickjs.c
+++ b/src/couch_quickjs/quickjs/quickjs.c
@@ -49,7 +49,7 @@
#define OPTIMIZE 1
#define SHORT_OPCODES 1
-#if defined(EMSCRIPTEN)
+#if defined(__EMSCRIPTEN__)
#define DIRECT_DISPATCH 0
#else
#define DIRECT_DISPATCH 1
@@ -68,11 +68,11 @@
/* define to include Atomics.* operations which depend on the OS
threads */
-#if !defined(EMSCRIPTEN)
+#if !defined(__EMSCRIPTEN__)
#define CONFIG_ATOMICS
#endif
-#if !defined(EMSCRIPTEN)
+#if !defined(__EMSCRIPTEN__)
/* enable stack limitation */
#define CONFIG_STACK_CHECK
#endif
@@ -236,9 +236,80 @@ typedef enum {
typedef enum OPCodeEnum OPCodeEnum;
-struct JSRuntime {
+/* JS malloc */
+
+#define JS_MALLOC_ALIGN 8
+#define JS_MALLOC_ARENA_SIZE 4096
+#define JS_MALLOC_BLOCK_SIZE_COUNT 31
+#define JS_MALLOC_MIN_SMALL_SIZE 16
+#define JS_MALLOC_MAX_SMALL_SIZE 512
+
+/* allow iteration among the allocated blocks. Currently not used. May
+ be used to suppress the memory overhead of JSGCObjectHeader */
+//#define JS_MALLOC_USE_ITER
+
+#define FREE_NIL 0xffff
+
+/* 8 byte header */
+/* Notes:
+ - the header is necessary at least to recover a pointer to
+ JSMallocArena because we don't want to enforce a page
+ alignment on the system malloc().
+ - could store the block offset instead of (block_idx,
+ block_size_idx), but it would require a division to recover the block
+ index.
+*/
+typedef struct JSMallocBlockHeader {
+ union {
+ uint16_t block_idx; /* FREE_NIL if large block */
+ uint16_t free_next; /* FREE_NIL if none */
+ } u;
+ uint8_t block_size_idx;
+ uint8_t gc_obj_type : 7;
+ uint8_t mark : 1;
+ int ref_count;
+ __attribute__((aligned(JS_MALLOC_ALIGN))) uint8_t user_data[];
+} JSMallocBlockHeader;
+
+typedef struct JSMallocLargeBlockHeader {
+#ifdef JS_MALLOC_USE_ITER
+ struct list_head link;
+#endif
+ JSMallocBlockHeader header;
+} JSMallocLargeBlockHeader;
+
+typedef struct {
+ struct list_head free_link;
+ struct list_head link;
+ uint8_t block_size_idx;
+ uint16_t n_used_blocks; /* number of allocated blocks */
+ uint16_t n_blocks; /* total number of blocks */
+ uint16_t first_free_block; /* FREE_NIL if none */
+#ifdef JS_MALLOC_USE_ITER
+ /* bit set to 1 for allocated block */
+ uint32_t bitmap[((JS_MALLOC_ARENA_SIZE / JS_MALLOC_MIN_SMALL_SIZE) + 31) / 32];
+#endif
+ /* n_blocks memory blocks of identical size */
+ __attribute__((aligned(JS_MALLOC_ALIGN))) uint8_t blocks[];
+} JSMallocArena;
+
+typedef struct {
+ struct list_head arena_list[JS_MALLOC_BLOCK_SIZE_COUNT]; /* list of JSMallocArena.link (all arenas) */
+ struct list_head free_arena_list[JS_MALLOC_BLOCK_SIZE_COUNT]; /* list of JSMallocArena.free_link (arenas where n_used_blocks < n_blocks) */
+#ifdef JS_MALLOC_USE_ITER
+ struct list_head large_block_list; /* list of JSMallocLargeBlockHeader.link */
+#endif
+ __attribute__((aligned(JS_MALLOC_ALIGN))) uint8_t zero_size_block[sizeof(JSMallocBlockHeader)];
+
+ /* callbacks to the host malloc */
JSMallocFunctions mf;
JSMallocState malloc_state;
+} JSMallocContext;
+
+/* end JS Malloc */
+
+struct JSRuntime {
+ JSMallocContext malloc_ctx;
const char *rt_info;
int atom_hash_size; /* power of two */
@@ -354,12 +425,6 @@ 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 */
struct list_head link;
};
@@ -375,16 +440,10 @@ typedef struct {
} JSWeakRefHeader;
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; /* must come first */
+ 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 {
@@ -421,7 +480,6 @@ typedef uint128_t js_dlimb_t;
#endif
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
@@ -515,7 +573,6 @@ typedef enum {
#define JS_ATOM_HASH_PRIVATE JS_ATOM_HASH_MASK
struct JSString {
- JSRefCountHeader header; /* must come first, 32-bit */
uint32_t len : 31;
uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */
/* for JS_ATOM_TYPE_SYMBOL: hash = weakref_count, atom_type = 3,
@@ -534,7 +591,6 @@ struct JSString {
};
typedef 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 */
@@ -908,47 +964,40 @@ typedef struct JSShapeProperty {
} JSShapeProperty;
struct JSShape {
- /* hash table of size hash_mask + 1 before the start of the
- structure (see prop_hash_end()). */
JSGCObjectHeader header;
/* true if the shape is inserted in the shape hash table. If not,
JSShape.hash is not valid */
uint8_t is_hashed;
uint32_t hash; /* current hash value */
- uint32_t prop_hash_mask;
+ uint32_t prop_hash_mask; /* >= 2 */
int prop_size; /* allocated properties */
int prop_count; /* include deleted properties */
int deleted_prop_count;
JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */
JSObject *proto;
- JSShapeProperty prop[0]; /* prop_size elements */
+ uint32_t hash_table[]; /* prop_hash_mask + 1 elements */
+ /* followed by JSShapeProperty 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 */
- /* TRUE if the array prototype is "normal":
- - no small index properties which are get/set or non writable
- - its prototype is Object.prototype
- - Object.prototype has no small index properties which are get/set or non writable
- - the prototype of Object.prototype is null (always true as it is immutable)
- */
- uint8_t is_std_array_prototype : 1;
-
- 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 has_immutable_prototype : 1; /* cannot modify the prototype */
- 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 */
- };
- };
+ JSGCObjectHeader header;
+ /* TRUE if the array prototype is "normal":
+ - no small index properties which are get/set or non writable
+ - its prototype is Object.prototype
+ - Object.prototype has no small index properties which are get/set or non writable
+ - the prototype of Object.prototype is null (always true as it is immutable)
+ */
+ uint8_t is_std_array_prototype : 1;
+
+ 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 has_immutable_prototype : 1; /* cannot modify the prototype */
+ 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 */
/* count the number of weak references to this object. The object
structure is freed only if header.ref_count = 0 and
weakref_count = 0 */
@@ -1345,6 +1394,10 @@ static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv);
static JSVarRef *js_global_object_find_uninitialized_var(JSContext *ctx, JSObject *p,
JSAtom atom, BOOL is_lexical);
+static int typed_array_init(JSContext *ctx, JSValueConst obj,
+ JSValue buffer, uint64_t offset, uint64_t len,
+ BOOL track_rab);
+
static const JSClassExoticMethods js_arguments_exotic_methods;
static const JSClassExoticMethods js_string_exotic_methods;
@@ -1352,62 +1405,420 @@ static const JSClassExoticMethods js_proxy_exotic_methods;
static const JSClassExoticMethods js_module_ns_exotic_methods;
static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
+/* JS malloc */
+
+/* max overhead for size >= 64: 12.5% */
+static const uint16_t js_malloc_block_sizes[JS_MALLOC_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 get_block_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_MALLOC_BLOCK_SIZE_COUNT;
+ }
+}
+
+static JSMallocBlockHeader *get_zero_size_block(JSMallocContext *s)
+{
+ return (JSMallocBlockHeader *)s->zero_size_block;
+}
+
+static void js_malloc_init(JSMallocContext *s)
+{
+ int i;
+ memset(s, 0, sizeof(*s));
+ get_zero_size_block(s)->u.block_idx = FREE_NIL;
+ for(i = 0; i < JS_MALLOC_BLOCK_SIZE_COUNT; i++) {
+ init_list_head(&s->arena_list[i]);
+ init_list_head(&s->free_arena_list[i]);
+ }
+#ifdef JS_MALLOC_USE_ITER
+ init_list_head(&s->large_block_list);
+#endif
+}
+
+static void *get_arena_block(JSMallocArena *ar, unsigned int idx, unsigned int block_size)
+{
+ return ar->blocks + idx * block_size;
+}
+
+static inline JSMallocBlockHeader *js_rc(void *ptr)
+{
+ return container_of(ptr, JSMallocBlockHeader, user_data);
+}
+
+static no_inline JSMallocArena *js_malloc_new_arena(JSMallocContext *s, int block_size_idx)
+{
+ JSMallocBlockHeader *b;
+ JSMallocArena *ar;
+ int n_blocks, block_size, i;
+
+ block_size = js_malloc_block_sizes[block_size_idx];
+ n_blocks = (JS_MALLOC_ARENA_SIZE - sizeof(JSMallocArena)) / block_size;
+ ar = s->mf.js_malloc(&s->malloc_state, sizeof(JSMallocArena) + 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;
+#ifdef JS_MALLOC_USE_ITER
+ {
+ int n_bitmap_words = (n_blocks + 31) / 32;
+ for(i = 0; i < n_bitmap_words; i++)
+ ar->bitmap[i] = 0;
+ }
+#endif
+ for(i = 0; i < n_blocks - 1; i++) {
+ b = get_arena_block(ar, i, block_size);
+ b->u.free_next = i + 1;
+ b->block_size_idx = block_size_idx;
+ }
+ b = get_arena_block(ar, n_blocks - 1, block_size);
+ b->u.free_next = FREE_NIL;
+ b->block_size_idx = block_size_idx;
+
+ /* add to the head */
+ list_add(&ar->link, &s->arena_list[block_size_idx]);
+ list_add(&ar->free_link, &s->free_arena_list[block_size_idx]);
+ return ar;
+}
+
+static no_inline void *js_malloc_large(JSMallocContext *s, size_t size)
+{
+ JSMallocLargeBlockHeader *b;
+ b = s->mf.js_malloc(&s->malloc_state, sizeof(JSMallocLargeBlockHeader) + size);
+ if (!b)
+ return NULL;
+ b->header.u.block_idx = FREE_NIL;
+ b->header.block_size_idx = 0xff; /* fail safe */
+#ifdef JS_MALLOC_USE_ITER
+ list_add_tail(&b->link, &s->large_block_list);
+#endif
+ return b->header.user_data;
+}
+
+static void *__js_malloc(JSMallocContext *s, size_t size)
+{
+ size_t total_size;
+ if (unlikely(size == 0)) {
+ JSMallocBlockHeader *b = get_zero_size_block(s);
+ return b->user_data;
+ } else {
+ total_size = ((size + JS_MALLOC_ALIGN - 1) & ~(JS_MALLOC_ALIGN - 1)) +
+ sizeof(JSMallocBlockHeader);
+ if (total_size <= JS_MALLOC_MAX_SMALL_SIZE) { /* TEST */
+ int block_size_idx;
+ unsigned int block_idx, block_size;
+ JSMallocBlockHeader *b;
+ JSMallocArena *ar;
+ struct list_head *el, *head;
+
+ block_size_idx = get_block_size_index(total_size);
+ block_size = js_malloc_block_sizes[block_size_idx];
+ head = &s->free_arena_list[block_size_idx];
+ el = head->next;
+ if (unlikely(el == head)) {
+ ar = js_malloc_new_arena(s, block_size_idx);
+ if (!ar)
+ return NULL;
+ } else {
+ ar = list_entry(el, JSMallocArena, free_link);
+ }
+ block_idx = ar->first_free_block;
+ b = get_arena_block(ar, ar->first_free_block, 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);
+ }
+#ifdef JS_MALLOC_USE_ITER
+ ar->bitmap[block_idx / 32] |= 1 << (block_idx % 32);
+#endif
+ return b->user_data;
+ } else {
+ return js_malloc_large(s, size);
+ }
+ }
+}
+
+static void __js_free(JSMallocContext *s, void *ptr)
+{
+ JSMallocBlockHeader *b;
+
+ if (!ptr)
+ return;
+ b = container_of(ptr, JSMallocBlockHeader, user_data);
+ if (unlikely(b->u.block_idx == FREE_NIL)) {
+ /* large or zero size block */
+ if (b == get_zero_size_block(s)) {
+ /* nothing to do */
+ } else {
+ JSMallocLargeBlockHeader *lb = container_of(ptr, JSMallocLargeBlockHeader, header.user_data);
+#ifdef JS_MALLOC_USE_ITER
+ list_del(&lb->link);
+#endif
+ s->mf.js_free(&s->malloc_state, lb);
+ }
+ } else {
+ unsigned int block_idx = b->u.block_idx;
+ unsigned int block_size_idx = b->block_size_idx;
+ unsigned int block_size = js_malloc_block_sizes[block_size_idx];
+ JSMallocArena *ar = (JSMallocArena *)((uint8_t *)b - block_size * block_idx - sizeof(JSMallocArena));
+ b->u.free_next = ar->first_free_block;
+ ar->first_free_block = block_idx;
+#ifdef JS_MALLOC_USE_ITER
+ ar->bitmap[block_idx / 32] &= ~(1 << (block_idx % 32));
+#endif
+ /* add back to the free list if needed */
+ if (unlikely(ar->n_used_blocks == ar->n_blocks)) {
+ list_add(&ar->free_link, &s->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);
+ s->mf.js_free(&s->malloc_state, ar);
+ }
+ }
+}
+
+static void *__js_realloc(JSMallocContext *s, void *ptr, size_t size)
+{
+ JSMallocBlockHeader *b;
+ if (ptr == NULL) {
+ return __js_malloc(s, size);
+ } else if (size == 0) {
+ __js_free(s, ptr);
+ return NULL;
+ }
+ b = container_of(ptr, JSMallocBlockHeader, user_data);
+ if (b->u.block_idx == FREE_NIL) {
+ if (b == get_zero_size_block(s)) {
+ return __js_malloc(s, size);
+ } else {
+ JSMallocLargeBlockHeader *lb, *new_lb;
+ lb = container_of(ptr, JSMallocLargeBlockHeader, header.user_data);
+#ifdef JS_MALLOC_USE_ITER
+ list_del(&lb->link);
+#endif
+ new_lb = s->mf.js_realloc(&s->malloc_state, lb, sizeof(JSMallocLargeBlockHeader) + size);
+ if (!new_lb) {
+#ifdef JS_MALLOC_USE_ITER
+ /* add again in the list */
+ list_add_tail(&lb->link, &s->large_block_list);
+#endif
+ return NULL;
+ }
+ new_lb->header.u.block_idx = FREE_NIL;
+ new_lb->header.block_size_idx = 0xff; /* fail safe */
+#ifdef JS_MALLOC_USE_ITER
+ list_add_tail(&new_lb->link, &s->large_block_list);
+#endif
+ return new_lb->header.user_data;
+ }
+ } else {
+ unsigned int block_size_idx = b->block_size_idx;
+ size_t block_size = js_malloc_block_sizes[block_size_idx];
+ size_t total_size, old_size;
+ void *new_ptr;
+ JSMallocBlockHeader *new_b;
+
+ total_size = ((size + JS_MALLOC_ALIGN - 1) & ~(JS_MALLOC_ALIGN - 1)) +
+ sizeof(JSMallocBlockHeader);
+ if (total_size <= block_size)
+ return ptr;
+ new_ptr = __js_malloc(s, size);
+ if (!new_ptr)
+ return NULL;
+ new_b = container_of(new_ptr, JSMallocBlockHeader, user_data);
+ /* copy the GC data */
+ new_b->gc_obj_type = b->gc_obj_type;
+ new_b->mark = b->mark;
+ new_b->ref_count = b->ref_count;
+ /* copy the data */
+ old_size = block_size - sizeof(JSMallocBlockHeader);
+ if (size > old_size)
+ size = old_size;
+ memcpy(new_ptr, ptr, size);
+ __js_free(s, ptr);
+ return new_ptr;
+ }
+}
+
+static size_t __js_malloc_usable_size(JSMallocContext *s, const char *ptr)
+{
+ JSMallocBlockHeader *b;
+ if (!ptr)
+ return 0;
+ b = container_of(ptr, JSMallocBlockHeader, user_data);
+ if (b->u.block_idx == FREE_NIL) {
+ if (b == get_zero_size_block(s)) {
+ return 0;
+ } else {
+ JSMallocLargeBlockHeader *lb;
+ size_t size;
+ lb = container_of(ptr, JSMallocLargeBlockHeader, header.user_data);
+ if (s->mf.js_malloc_usable_size) {
+ size = s->mf.js_malloc_usable_size(lb);
+ if (size != 0)
+ size -= sizeof(JSMallocLargeBlockHeader);
+ return size;
+ } else {
+ return 0;
+ }
+ }
+ } else {
+ size_t block_size = js_malloc_block_sizes[b->block_size_idx];
+ return block_size - sizeof(*b);
+ }
+}
+
+static __maybe_unused void js_malloc_dump_arenas(JSMallocContext *s)
+{
+ struct list_head *el;
+ int block_size_idx;
+
+ printf("%20s %10s %10s\n", "PTR", "BLK_SIZE", "ALLOC");
+ for(block_size_idx = 0; block_size_idx < JS_MALLOC_BLOCK_SIZE_COUNT; block_size_idx++) {
+ int block_size = js_malloc_block_sizes[block_size_idx];
+ list_for_each(el, &s->arena_list[block_size_idx]) {
+ JSMallocArena *ar = list_entry(el, JSMallocArena, link);
+ printf("%20p %10u %9.1f%%\n",
+ ar, block_size,
+ (double)ar->n_used_blocks / ar->n_blocks * 100);
+ }
+ }
+}
+
+#ifdef JS_MALLOC_USE_ITER
+typedef void JSMallocIterFunc(void *opaque, void *ptr);
+
+/* iterate thru allocated blocks. The allocated block list should not
+ be modified while iterating. */
+static __maybe_unused void js_malloc_iter(JSMallocContext *s, JSMallocIterFunc *iter_func, void *iter_opaque)
+{
+ struct list_head *el;
+ int block_size_idx;
+ int i, j, n_words;
+ uint32_t bmp;
+
+ for(block_size_idx = 0; block_size_idx < JS_MALLOC_BLOCK_SIZE_COUNT; block_size_idx++) {
+ unsigned int block_size = js_malloc_block_sizes[block_size_idx];
+ list_for_each(el, &s->arena_list[block_size_idx]) {
+ JSMallocArena *ar = list_entry(el, JSMallocArena, link);
+ n_words = (ar->n_blocks + 31) / 32;
+ for(i = 0; i < n_words; i++) {
+ bmp = ar->bitmap[i];
+ while (bmp != 0) {
+ j = ctz32(bmp);
+ bmp &= ~(1 << j);
+ iter_func(iter_opaque, get_arena_block(ar, i * 32+ j, block_size));
+ }
+ }
+ }
+ }
+ list_for_each(el, &s->large_block_list) {
+ JSMallocLargeBlockHeader *lb = list_entry(el, JSMallocLargeBlockHeader, link);
+ iter_func(iter_opaque, lb->header.user_data);
+ }
+}
+#endif
+
+/* end JS malloc */
+
static void js_trigger_gc(JSRuntime *rt, size_t size)
{
BOOL force_gc;
#ifdef FORCE_GC_AT_MALLOC
force_gc = TRUE;
#else
- force_gc = ((rt->malloc_state.malloc_size + size) >
+ force_gc = ((rt->malloc_ctx.malloc_state.malloc_size + size) >
rt->malloc_gc_threshold);
#endif
if (force_gc) {
#ifdef DUMP_GC
printf("GC: size=%" PRIu64 "\n",
- (uint64_t)rt->malloc_state.malloc_size);
+ (uint64_t)rt->malloc_ctx.malloc_state.malloc_size);
#endif
JS_RunGC(rt);
- rt->malloc_gc_threshold = rt->malloc_state.malloc_size +
- (rt->malloc_state.malloc_size >> 1);
+ rt->malloc_gc_threshold = rt->malloc_ctx.malloc_state.malloc_size +
+ (rt->malloc_ctx.malloc_state.malloc_size >> 1);
}
}
-static size_t js_malloc_usable_size_unknown(const void *ptr)
-{
- return 0;
-}
-
-void *js_malloc_rt(JSRuntime *rt, size_t size)
+extern force_inline void *js_malloc_rt(JSRuntime *rt, size_t size)
{
- return rt->mf.js_malloc(&rt->malloc_state, size);
+ return __js_malloc(&rt->malloc_ctx, size);
}
-void js_free_rt(JSRuntime *rt, void *ptr)
+extern force_inline void js_free_rt(JSRuntime *rt, void *ptr)
{
- rt->mf.js_free(&rt->malloc_state, ptr);
+ __js_free(&rt->malloc_ctx, ptr);
}
-void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)
+extern force_inline void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)
{
- return rt->mf.js_realloc(&rt->malloc_state, ptr, size);
+ return __js_realloc(&rt->malloc_ctx, ptr, size);
}
-size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr)
+extern force_inline size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr)
{
- return rt->mf.js_malloc_usable_size(ptr);
+ return __js_malloc_usable_size(&rt->malloc_ctx, ptr);
}
void *js_mallocz_rt(JSRuntime *rt, size_t size)
{
void *ptr;
ptr = js_malloc_rt(rt, size);
- if (!ptr)
+ if (unlikely(!ptr))
return NULL;
return memset(ptr, 0, size);
}
/* Throw out of memory in case of error */
-void *js_malloc(JSContext *ctx, size_t size)
+extern force_inline void *js_malloc(JSContext *ctx, size_t size)
{
void *ptr;
ptr = js_malloc_rt(ctx->rt, size);
@@ -1419,7 +1830,7 @@ void *js_malloc(JSContext *ctx, size_t size)
}
/* Throw out of memory in case of error */
-void *js_mallocz(JSContext *ctx, size_t size)
+extern force_inline void *js_mallocz(JSContext *ctx, size_t size)
{
void *ptr;
ptr = js_mallocz_rt(ctx->rt, size);
@@ -1430,7 +1841,7 @@ void *js_mallocz(JSContext *ctx, size_t size)
return ptr;
}
-void js_free(JSContext *ctx, void *ptr)
+extern force_inline void js_free(JSContext *ctx, void *ptr)
{
js_free_rt(ctx->rt, ptr);
}
@@ -1525,7 +1936,7 @@ static void *js_realloc_bytecode_rt(void *opaque, void *ptr, size_t size)
avoid some overflows. */
return NULL;
} else {
- return rt->mf.js_realloc(&rt->malloc_state, ptr, size);
+ return js_realloc_rt(rt, ptr, size);
}
}
@@ -1657,12 +2068,9 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
if (!rt)
return NULL;
memset(rt, 0, sizeof(*rt));
- rt->mf = *mf;
- if (!rt->mf.js_malloc_usable_size) {
- /* use dummy function if none provided */
- rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown;
- }
- rt->malloc_state = ms;
+ js_malloc_init(&rt->malloc_ctx);
+ rt->malloc_ctx.mf = *mf;
+ rt->malloc_ctx.malloc_state = ms;
rt->malloc_gc_threshold = 256 * 1024;
init_list_head(&rt->context_list);
@@ -1723,7 +2131,7 @@ static size_t js_def_malloc_usable_size(const void *ptr)
return malloc_size(ptr);
#elif defined(_WIN32)
return _msize((void *)ptr);
-#elif defined(EMSCRIPTEN)
+#elif defined(__EMSCRIPTEN__)
return 0;
#elif defined(__linux__) || defined(__GLIBC__)
return malloc_usable_size((void *)ptr);
@@ -1803,7 +2211,7 @@ JSRuntime *JS_NewRuntime(void)
void JS_SetMemoryLimit(JSRuntime *rt, size_t limit)
{
- rt->malloc_state.malloc_limit = limit;
+ rt->malloc_ctx.malloc_state.malloc_limit = limit;
}
/* use -1 to disable automatic GC */
@@ -1910,7 +2318,7 @@ int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)
JS_FreeValue(ctx, res);
js_free(ctx, e);
if (pctx) {
- if (ctx->header.ref_count > 1)
+ if (js_rc(ctx)->ref_count > 1)
*pctx = ctx;
else
*pctx = NULL;
@@ -1941,7 +2349,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_rc(str)->ref_count = 1;
str->is_wide_char = is_wide_char;
str->len = max_len;
str->atom_type = 0;
@@ -1967,7 +2375,7 @@ static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char)
/* same as JS_FreeValueRT() but faster */
static inline void js_free_string(JSRuntime *rt, JSString *str)
{
- if (--str->header.ref_count <= 0) {
+ if (--js_rc(str)->ref_count <= 0) {
if (str->atom_type) {
JS_FreeAtomStruct(rt, str);
} else {
@@ -2016,14 +2424,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_rc(p)->mark = 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_rc(p)->ref_count != 0) {
if (!header_done) {
printf("Object leaks:\n");
JS_DumpObjectHeader(rt);
@@ -2036,7 +2444,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_rc(p)->ref_count == 0) {
count++;
}
}
@@ -2064,7 +2472,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_rc(p)->ref_count != 1) {
if (!header_done) {
header_done = TRUE;
if (rt->rt_info) {
@@ -2078,7 +2486,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_rc(p)->ref_count);
}
switch (p->atom_type) {
case JS_ATOM_TYPE_STRING:
@@ -2102,7 +2510,7 @@ void JS_FreeRuntime(JSRuntime *rt)
break;
}
if (rt->rt_info) {
- printf(":%u", p->header.ref_count);
+ printf(":%u", js_rc(p)->ref_count);
} else {
printf("\n");
}
@@ -2141,11 +2549,11 @@ void JS_FreeRuntime(JSRuntime *rt)
if (rt->rt_info) {
printf(" ");
} else {
- printf(" %6u ", str->header.ref_count);
+ printf(" %6u ", js_rc(str)->ref_count);
}
JS_DumpString(rt, str);
if (rt->rt_info) {
- printf(":%u", str->header.ref_count);
+ printf(":%u", js_rc(str)->ref_count);
} else {
printf("\n");
}
@@ -2156,7 +2564,7 @@ void JS_FreeRuntime(JSRuntime *rt)
printf("\n");
}
{
- JSMallocState *s = &rt->malloc_state;
+ JSMallocState *s = &rt->malloc_ctx.malloc_state;
if (s->malloc_count > 1) {
if (rt->rt_info)
printf("%s:1: ", rt->rt_info);
@@ -2168,8 +2576,8 @@ void JS_FreeRuntime(JSRuntime *rt)
#endif
{
- JSMallocState ms = rt->malloc_state;
- rt->mf.js_free(&ms, rt);
+ JSMallocState ms = rt->malloc_ctx.malloc_state;
+ rt->malloc_ctx.mf.js_free(&ms, rt);
}
}
@@ -2181,7 +2589,7 @@ JSContext *JS_NewContextRaw(JSRuntime *rt)
ctx = js_mallocz_rt(rt, sizeof(JSContext));
if (!ctx)
return NULL;
- ctx->header.ref_count = 1;
+ js_rc(ctx)->ref_count = 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]) *
@@ -2292,7 +2700,7 @@ static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag)
JSContext *JS_DupContext(JSContext *ctx)
{
- ctx->header.ref_count++;
+ js_rc(ctx)->ref_count++;
return ctx;
}
@@ -2350,9 +2758,9 @@ void JS_FreeContext(JSContext *ctx)
JSRuntime *rt = ctx->rt;
int i;
- if (--ctx->header.ref_count > 0)
+ if (--js_rc(ctx)->ref_count > 0)
return;
- assert(ctx->header.ref_count == 0);
+ assert(js_rc(ctx)->ref_count == 0);
#ifdef DUMP_ATOMS
JS_DumpAtoms(ctx->rt);
@@ -2584,8 +2992,8 @@ static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p)
printf("<null>");
return;
}
- printf("%d", p->header.ref_count);
- sep = (p->header.ref_count == 1) ? '\"' : '\'';
+ printf("%d", js_rc((void *)p)->ref_count);
+ sep = (js_rc((void *)p)->ref_count == 1) ? '\"' : '\'';
putchar(sep);
for(i = 0; i < p->len; i++) {
JS_DumpChar(stdout, string_get(p, i), sep);
@@ -2693,7 +3101,7 @@ static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v)
if (!__JS_AtomIsConst(v)) {
p = rt->atom_array[v];
- p->header.ref_count++;
+ js_rc(p)->ref_count++;
}
return v;
}
@@ -2706,7 +3114,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_rc(p)->ref_count++;
}
return v;
}
@@ -2775,7 +3183,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_rc(str)->ref_count--;
return i;
}
/* try and locate an already registered atom */
@@ -2791,7 +3199,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
p->len == len &&
js_string_memcmp(p, 0, str, 0, len) == 0) {
if (!__JS_AtomIsConst(i))
- p->header.ref_count++;
+ js_rc(p)->ref_count++;
goto done;
}
i = p->hash_next;
@@ -2831,7 +3239,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_rc(p)->ref_count = 1; /* not refcounted */
p->atom_type = JS_ATOM_TYPE_SYMBOL;
#ifdef DUMP_LEAKS
list_add_tail(&p->link, &rt->string_list);
@@ -2863,7 +3271,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_rc(p)->ref_count = 1;
p->is_wide_char = str->is_wide_char;
p->len = str->len;
#ifdef DUMP_LEAKS
@@ -2877,7 +3285,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_rc(p)->ref_count = 1;
p->is_wide_char = 1; /* Hack to represent NULL as a JSString */
p->len = 0;
#ifdef DUMP_LEAKS
@@ -2946,7 +3354,7 @@ static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
p->is_wide_char == 0 &&
memcmp(p->u.str8, str, len) == 0) {
if (!__JS_AtomIsConst(i))
- p->header.ref_count++;
+ js_rc(p)->ref_count++;
return i;
}
i = p->hash_next;
@@ -2958,7 +3366,7 @@ static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)
{
#if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */
if (unlikely(i == JS_ATOM_NULL)) {
- p->header.ref_count = INT32_MAX / 2;
+ js_rc(p)->ref_count = INT32_MAX / 2;
return;
}
#endif
@@ -3008,7 +3416,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_rc(p)->ref_count > 0)
return;
JS_FreeAtomStruct(rt, p);
}
@@ -4258,7 +4666,7 @@ static BOOL JS_ConcatStringInPlace(JSContext *ctx, JSString *p1, JSValueConst op
if (p2->len == 0)
return TRUE;
- if (p1->header.ref_count != 1)
+ if (js_rc(p1)->ref_count != 1)
return FALSE;
size1 = js_malloc_usable_size(ctx, p1);
if (p1->is_wide_char) {
@@ -4431,7 +4839,7 @@ static JSValue js_linearize_string_rope(JSContext *ctx, JSValue rope)
if (string_buffer_concat_value(b, rope))
goto fail;
ret = string_buffer_end(b);
- if (r->header.ref_count > 1) {
+ if (js_rc(r)->ref_count > 1) {
/* update the rope so that it won't need to be linearized again */
JS_FreeValue(ctx, r->left);
JS_FreeValue(ctx, r->right);
@@ -4484,7 +4892,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_rc(r)->ref_count = 1;
r->len = len;
r->is_wide_char = is_wide_char;
r->depth = depth + 1;
@@ -4703,28 +5111,13 @@ static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2)
static inline size_t get_shape_size(size_t hash_size, size_t prop_size)
{
- return hash_size * sizeof(uint32_t) + sizeof(JSShape) +
+ return sizeof(JSShape) + hash_size * sizeof(uint32_t) +
prop_size * sizeof(JSShapeProperty);
}
-static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size)
-{
- return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size);
-}
-
-static inline uint32_t *prop_hash_end(JSShape *sh)
-{
- return (uint32_t *)sh;
-}
-
-static inline void *get_alloc_from_shape(JSShape *sh)
-{
- return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1);
-}
-
static inline JSShapeProperty *get_shape_prop(JSShape *sh)
{
- return sh->prop;
+ return (JSShapeProperty *)((uint32_t *)(sh + 1) + sh->prop_hash_mask + 1);
}
static int init_shape_hash(JSRuntime *rt)
@@ -4813,20 +5206,17 @@ static inline JSShape *js_new_shape_nohash(JSContext *ctx, JSObject *proto,
int hash_size, int prop_size)
{
JSRuntime *rt = ctx->rt;
- void *sh_alloc;
JSShape *sh;
- sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size));
- if (!sh_alloc)
+ sh = js_malloc(ctx, get_shape_size(hash_size, prop_size));
+ if (!sh)
return NULL;
- sh = get_shape_from_alloc(sh_alloc, hash_size);
- sh->header.ref_count = 1;
+ js_rc(sh)->ref_count = 1;
add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
if (proto)
JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));
sh->proto = proto;
- memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) *
- hash_size);
+ memset(sh->hash_table, 0, sizeof(sh->hash_table[0]) * hash_size);
sh->prop_hash_mask = hash_size - 1;
sh->prop_size = prop_size;
sh->prop_count = 0;
@@ -4869,20 +5259,18 @@ static JSShape *js_new_shape(JSContext *ctx, JSObject *proto)
static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)
{
JSShape *sh;
- void *sh_alloc, *sh_alloc1;
size_t size;
JSShapeProperty *pr;
uint32_t i, hash_size;
hash_size = sh1->prop_hash_mask + 1;
size = get_shape_size(hash_size, sh1->prop_size);
- sh_alloc = js_malloc(ctx, size);
- if (!sh_alloc)
+ sh = js_malloc(ctx, size);
+ if (!sh)
return NULL;
- 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;
+ memcpy(&sh->header + 1, &sh1->header + 1,
+ size - sizeof(JSGCObjectHeader));
+ js_rc(sh)->ref_count = 1;
add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
sh->is_hashed = FALSE;
if (sh->proto) {
@@ -4896,7 +5284,7 @@ static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)
static JSShape *js_dup_shape(JSShape *sh)
{
- sh->header.ref_count++;
+ js_rc(sh)->ref_count++;
return sh;
}
@@ -4905,7 +5293,7 @@ static void js_free_shape0(JSRuntime *rt, JSShape *sh)
uint32_t i;
JSShapeProperty *pr;
- assert(sh->header.ref_count == 0);
+ assert(js_rc(sh)->ref_count == 0);
if (sh->is_hashed)
js_shape_hash_unlink(rt, sh);
if (sh->proto != NULL) {
@@ -4917,12 +5305,12 @@ static void js_free_shape0(JSRuntime *rt, JSShape *sh)
pr++;
}
remove_gc_object(&sh->header);
- js_free_rt(rt, get_alloc_from_shape(sh));
+ js_free_rt(rt, sh);
}
static void js_free_shape(JSRuntime *rt, JSShape *sh)
{
- if (unlikely(--sh->header.ref_count <= 0)) {
+ if (unlikely(--js_rc(sh)->ref_count <= 0)) {
js_free_shape0(rt, sh);
}
}
@@ -4940,7 +5328,6 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
JSShape *sh;
uint32_t new_size, new_hash_size, new_hash_mask, i;
JSShapeProperty *pr;
- void *sh_alloc;
intptr_t h;
JSShape *old_sh;
@@ -4961,35 +5348,41 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
/* resize the property shapes. Using js_realloc() is not possible in
case the GC runs during the allocation */
old_sh = sh;
- sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
- if (!sh_alloc)
+ sh = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
+ if (!sh)
return -1;
- sh = get_shape_from_alloc(sh_alloc, new_hash_size);
- list_del(&old_sh->header.link);
- /* copy all the shape properties */
- memcpy(sh, old_sh,
- sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count);
- list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
+ remove_gc_object(&old_sh->header);
+ js_rc(sh)->ref_count = 1;
+ add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
+
+ memcpy(&sh->header + 1, &old_sh->header + 1,
+ sizeof(JSShape) - sizeof(JSGCObjectHeader));
+
if (new_hash_size != (sh->prop_hash_mask + 1)) {
/* resize the hash table and the properties */
new_hash_mask = new_hash_size - 1;
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);
- for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) {
+ memset(sh->hash_table, 0,
+ sizeof(sh->hash_table[0]) * new_hash_size);
+ memcpy(get_shape_prop(sh), get_shape_prop(old_sh),
+ sizeof(JSShapeProperty) * old_sh->prop_count);
+ 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];
- prop_hash_end(sh)[-h - 1] = i + 1;
+ pr->hash_next = sh->hash_table[h];
+ sh->hash_table[h] = i + 1;
}
}
} else {
- /* just copy the previous hash table */
- memcpy(prop_hash_end(sh) - new_hash_size, prop_hash_end(old_sh) - new_hash_size,
- sizeof(prop_hash_end(sh)[0]) * new_hash_size);
+ /* just copy the previous hash table and the properties */
+ memcpy(sh->hash_table, old_sh->hash_table,
+ sizeof(sh->hash_table[0]) * new_hash_size);
+
+ memcpy(get_shape_prop(sh), get_shape_prop(old_sh),
+ sizeof(JSShapeProperty) * old_sh->prop_count);
}
- js_free(ctx, get_alloc_from_shape(old_sh));
+ js_free(ctx, old_sh);
*psh = sh;
sh->prop_size = new_size;
return 0;
@@ -4999,7 +5392,6 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
static int compact_properties(JSContext *ctx, JSObject *p)
{
JSShape *sh, *old_sh;
- void *sh_alloc;
intptr_t h;
uint32_t new_hash_size, i, j, new_hash_mask, new_size;
JSShapeProperty *old_pr, *pr;
@@ -5019,28 +5411,31 @@ static int compact_properties(JSContext *ctx, JSObject *p)
/* resize the hash table and the properties */
old_sh = sh;
- sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
- if (!sh_alloc)
+ sh = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
+ if (!sh)
return -1;
- sh = get_shape_from_alloc(sh_alloc, new_hash_size);
- list_del(&old_sh->header.link);
- memcpy(sh, old_sh, sizeof(JSShape));
- list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
+ remove_gc_object(&old_sh->header);
+
+ js_rc(sh)->ref_count = 1;
+ add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
- memset(prop_hash_end(sh) - new_hash_size, 0,
- sizeof(prop_hash_end(sh)[0]) * new_hash_size);
+ memcpy(&sh->header + 1, &old_sh->header + 1,
+ sizeof(JSShape) - sizeof(JSGCObjectHeader));
+
+ memset(sh->hash_table, 0, sizeof(sh->hash_table[0]) * new_hash_size);
+ sh->prop_hash_mask = new_hash_mask;
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) {
pr->atom = old_pr->atom;
pr->flags = old_pr->flags;
h = ((uintptr_t)old_pr->atom & new_hash_mask);
- pr->hash_next = prop_hash_end(sh)[-h - 1];
- prop_hash_end(sh)[-h - 1] = j + 1;
+ pr->hash_next = sh->hash_table[h];
+ sh->hash_table[h] = j + 1;
prop[j] = prop[i];
j++;
pr++;
@@ -5048,13 +5443,12 @@ static int compact_properties(JSContext *ctx, JSObject *p)
old_pr++;
}
assert(j == (sh->prop_count - sh->deleted_prop_count));
- sh->prop_hash_mask = new_hash_mask;
sh->prop_size = new_size;
sh->deleted_prop_count = 0;
sh->prop_count = j;
p->shape = sh;
- js_free(ctx, get_alloc_from_shape(old_sh));
+ js_free(ctx, old_sh);
/* reduce the size of the object properties */
new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
@@ -5101,8 +5495,8 @@ static int add_shape_property(JSContext *ctx, JSShape **psh,
/* add in hash table */
hash_mask = sh->prop_hash_mask;
h = atom & hash_mask;
- pr->hash_next = prop_hash_end(sh)[-h - 1];
- prop_hash_end(sh)[-h - 1] = sh->prop_count;
+ pr->hash_next = sh->hash_table[h];
+ sh->hash_table[h] = sh->prop_count;
return 0;
}
@@ -5143,13 +5537,15 @@ static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,
if (sh1->hash == h &&
sh1->proto == sh->proto &&
sh1->prop_count == ((n = sh->prop_count) + 1)) {
+ JSShapeProperty *prop = get_shape_prop(sh);
+ JSShapeProperty *prop1 = get_shape_prop(sh1);
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(prop1[i].atom != prop[i].atom) ||
+ unlikely(prop1[i].flags != prop[i].flags))
goto next;
}
- if (unlikely(sh1->prop[n].atom != atom) ||
- unlikely(sh1->prop[n].flags != prop_flags))
+ if (unlikely(prop1[n].atom != atom) ||
+ unlikely(prop1[n].flags != prop_flags))
goto next;
return sh1;
}
@@ -5165,11 +5561,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_rc(sh)->ref_count, " *"[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");
}
@@ -5193,7 +5589,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_rc(gp)->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
p = (JSObject *)gp;
if (!p->shape->is_hashed) {
JS_DumpShape(rt, -1, p->shape);
@@ -5317,7 +5713,7 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas
}
break;
}
- p->header.ref_count = 1;
+ js_rc(p)->ref_count = 1;
add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT);
if (props) {
for(i = 0; i < sh->prop_count; i++)
@@ -5715,7 +6111,7 @@ static force_inline JSShapeProperty *find_own_property1(JSObject *p,
intptr_t h;
sh = p->shape;
h = (uintptr_t)atom & sh->prop_hash_mask;
- h = prop_hash_end(sh)[-h - 1];
+ h = sh->hash_table[h];
prop = get_shape_prop(sh);
while (h) {
pr = &prop[h - 1];
@@ -5736,7 +6132,7 @@ static force_inline JSShapeProperty *find_own_property(JSProperty **ppr,
intptr_t h;
sh = p->shape;
h = (uintptr_t)atom & sh->prop_hash_mask;
- h = prop_hash_end(sh)[-h - 1];
+ h = sh->hash_table[h];
prop = get_shape_prop(sh);
while (h) {
pr = &prop[h - 1];
@@ -5759,8 +6155,8 @@ static void set_cycle_flag(JSContext *ctx, JSValueConst obj)
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_rc(var_ref)->ref_count > 0);
+ if (--js_rc(var_ref)->ref_count == 0) {
if (var_ref->is_detached) {
JS_FreeValueRT(rt, var_ref->value);
} else {
@@ -5969,7 +6365,7 @@ static void free_object(JSRuntime *rt, JSObject *p)
remove_gc_object(&p->header);
if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES) {
- if (p->header.ref_count == 0 && p->weakref_count == 0) {
+ if (js_rc(p)->ref_count == 0 && p->weakref_count == 0) {
js_free_rt(rt, p);
} else {
/* keep the object structure because there are may be
@@ -5981,14 +6377,14 @@ static void free_object(JSRuntime *rt, JSObject *p)
if (p->weakref_count == 0) {
js_free_rt(rt, p);
} else {
- p->header.mark = 0; /* reset the mark so that the weakref can be freed */
+ js_rc(p)->mark = 0; /* reset the mark so that the weakref can be freed */
}
}
}
static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)
{
- switch(gp->gc_obj_type) {
+ switch(js_rc(gp)->gc_obj_type) {
case JS_GC_OBJ_TYPE_JS_OBJECT:
free_object(rt, (JSObject *)gp);
break;
@@ -6017,7 +6413,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_rc(p)->ref_count == 0);
free_gc_object(rt, p);
}
rt->gc_phase = JS_GC_PHASE_NONE;
@@ -6071,7 +6467,7 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v)
if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
list_del(&p->link);
list_add(&p->link, &rt->gc_zero_ref_count_list);
- p->mark = 1; /* indicate that the object is about to be freed */
+ js_rc(p)->mark = 1; /* indicate that the object is about to be freed */
if (rt->gc_phase == JS_GC_PHASE_NONE) {
free_zero_refcount(rt);
}
@@ -6135,8 +6531,8 @@ static void gc_remove_weak_objects(JSRuntime *rt)
static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
JSGCObjectTypeEnum type)
{
- h->mark = 0;
- h->gc_obj_type = type;
+ js_rc(h)->mark = 0;
+ js_rc(h)->gc_obj_type = type;
list_add_tail(&h->link, &rt->gc_obj_list);
}
@@ -6163,7 +6559,7 @@ void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
JS_MarkFunc *mark_func)
{
- switch(gp->gc_obj_type) {
+ switch(js_rc(gp)->gc_obj_type) {
case JS_GC_OBJ_TYPE_JS_OBJECT:
{
JSObject *p = (JSObject *)gp;
@@ -6281,9 +6677,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_rc(p)->ref_count > 0);
+ js_rc(p)->ref_count--;
+ if (js_rc(p)->ref_count == 0 && js_rc(p)->mark == 1) {
list_del(&p->link);
list_add_tail(&p->link, &rt->tmp_obj_list);
}
@@ -6301,10 +6697,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_rc(p)->mark == 0);
mark_children(rt, p, gc_decref_child);
- p->mark = 1;
- if (p->ref_count == 0) {
+ js_rc(p)->mark = 1;
+ if (js_rc(p)->ref_count == 0) {
list_del(&p->link);
list_add_tail(&p->link, &rt->tmp_obj_list);
}
@@ -6313,19 +6709,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_rc(p)->ref_count++;
+ if (js_rc(p)->ref_count == 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_rc(p)->mark = 0; /* reset the mark for the next GC call */
}
}
static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p)
{
- p->ref_count++;
+ js_rc(p)->ref_count++;
}
static void gc_scan(JSRuntime *rt)
@@ -6336,8 +6732,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_rc(p)->ref_count > 0);
+ js_rc(p)->mark = 0; /* reset the mark for the next GC call */
mark_children(rt, p, gc_scan_incref_child);
}
@@ -6366,7 +6762,7 @@ static void gc_free_cycles(JSRuntime *rt)
/* Only need to free the GC object associated with JS values
or async functions. The rest will be automatically removed
because they must be referenced by them. */
- switch(p->gc_obj_type) {
+ switch(js_rc(p)->gc_obj_type) {
case JS_GC_OBJ_TYPE_JS_OBJECT:
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
@@ -6391,14 +6787,14 @@ 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 ||
- p->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION ||
- p->gc_obj_type == JS_GC_OBJ_TYPE_MODULE);
- if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT &&
+ assert(js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
+ js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE ||
+ js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION ||
+ js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_MODULE);
+ if (js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT &&
((JSObject *)p)->weakref_count != 0) {
/* keep the object because there are weak references to it */
- p->mark = 0;
+ js_rc(p)->mark = 0;
} else {
js_free_rt(rt, p);
}
@@ -6462,7 +6858,7 @@ static void compute_value_size(JSValueConst 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_rc(str)->ref_count;
hp->str_count += 1 / s_ref_count;
hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) +
1 - str->is_wide_char) / s_ref_count);
@@ -6527,9 +6923,9 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
JSMemoryUsage_helper mem = { 0 }, *hp = &mem;
memset(s, 0, sizeof(*s));
- s->malloc_count = rt->malloc_state.malloc_count;
- s->malloc_size = rt->malloc_state.malloc_size;
- s->malloc_limit = rt->malloc_state.malloc_limit;
+ s->malloc_count = rt->malloc_ctx.malloc_state.malloc_count;
+ s->malloc_size = rt->malloc_ctx.malloc_state.malloc_size;
+ s->malloc_limit = rt->malloc_ctx.malloc_state.malloc_limit;
s->memory_used_count = 2; /* rt + rt->class_array */
s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count;
@@ -6589,10 +6985,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_rc(gp)->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) {
compute_bytecode_size((JSFunctionBytecode *)gp, hp);
continue;
- } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) {
+ } else if (js_rc(gp)->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) {
continue;
}
p = (JSObject *)gp;
@@ -6670,7 +7066,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_rc(var_refs[i])->ref_count;
s->memory_used_count += 1 / ref_count;
s->js_func_size += sizeof(*var_refs[i]) / ref_count;
/* handle non object closed values */
@@ -6856,7 +7252,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_rc(gp)->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
p = (JSObject *)gp;
obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++;
}
@@ -7785,7 +8181,7 @@ static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,
/* WARNING: a varref is returned as a string ! */
prs->flags |= JS_PROP_VARREF;
pr->u.var_ref = JS_VALUE_GET_PTR(val);
- pr->u.var_ref->header.ref_count++;
+ js_rc(pr->u.var_ref)->ref_count++;
} else if (p->class_id == JS_CLASS_GLOBAL_OBJECT) {
JSVarRef *var_ref;
/* in the global object we use references */
@@ -8815,7 +9211,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_rc(sh)->ref_count != 1) {
/* if the shape is shared, clone it */
new_sh = js_clone_shape(ctx, sh);
if (!new_sh)
@@ -8827,7 +9223,7 @@ static JSProperty *add_property(JSContext *ctx,
p->shape = new_sh;
}
}
- assert(p->shape->header.ref_count == 1);
+ assert(js_rc(p->shape)->ref_count == 1);
if (add_shape_property(ctx, &p->shape, p, prop, prop_flags))
return NULL;
return &p->prop[p->shape->prop_count - 1];
@@ -8888,14 +9284,14 @@ static int remove_global_object_property(JSContext *ctx, JSObject *p,
JSProperty *pr1;
var_ref = pr->u.var_ref;
- if (var_ref->header.ref_count == 1)
+ if (js_rc(var_ref)->ref_count == 1)
return 0;
p1 = JS_VALUE_GET_OBJ(p->u.global_object.uninitialized_vars);
pr1 = add_property(ctx, p1, prs->atom, JS_PROP_C_W_E | JS_PROP_VARREF);
if (!pr1)
return -1;
pr1->u.var_ref = var_ref;
- var_ref->header.ref_count++;
+ js_rc(var_ref)->ref_count++;
JS_FreeValue(ctx, var_ref->value);
var_ref->is_lexical = FALSE;
var_ref->is_const = FALSE;
@@ -8914,7 +9310,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
redo:
sh = p->shape;
h1 = atom & sh->prop_hash_mask;
- h = prop_hash_end(sh)[-h1 - 1];
+ h = sh->hash_table[h1];
prop = get_shape_prop(sh);
lpr = NULL;
lpr_idx = 0; /* prevent warning */
@@ -8935,7 +9331,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
lpr = get_shape_prop(sh) + lpr_idx;
lpr->hash_next = pr->hash_next;
} else {
- prop_hash_end(sh)[-h1 - 1] = pr->hash_next;
+ sh->hash_table[h1] = pr->hash_next;
}
sh->deleted_prop_count++;
/* free the entry */
@@ -9036,7 +9432,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)) {
@@ -9817,7 +10213,7 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p,
if (prs1) {
delete_obj = p1;
var_ref = pr1->u.var_ref;
- var_ref->header.ref_count++;
+ js_rc(var_ref)->ref_count++;
} else {
var_ref = js_create_var_ref(ctx, FALSE);
if (!var_ref)
@@ -9902,7 +10298,7 @@ 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_rc(sh)->ref_count != 1) {
if (pprs)
idx = *pprs - get_shape_prop(sh);
/* clone the shape (the resulting one is no longer hashed) */
@@ -11193,7 +11589,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_rc(r)->ref_count = 1;
r->len = len;
return r;
}
@@ -11201,7 +11597,6 @@ 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->len = 1;
r->tab[0] = a;
return r;
@@ -11213,7 +11608,6 @@ static JSBigInt *js_bigint_set_si64(JSBigIntBuf *buf, int64_t a)
return js_bigint_set_si(buf, a);
#else
JSBigInt *r = (JSBigInt *)buf->big_int_buf;
- r->header.ref_count = 0; /* fail safe */
if (a >= INT32_MIN && a <= INT32_MAX) {
r->len = 1;
r->tab[0] = a;
@@ -11328,7 +11722,7 @@ static JSBigInt *js_bigint_normalize1(JSContext *ctx, JSBigInt *a, int l)
{
js_limb_t v;
- assert(a->header.ref_count == 1);
+ assert(js_rc(a)->ref_count == 1);
while (l > 1) {
v = a->tab[l - 1];
if ((v != 0 && v != -1) ||
@@ -12143,7 +12537,11 @@ static JSBigInt *js_bigint_from_string(JSContext *ctx,
}
/* 2 <= base <= 36 */
-static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
+static char const digits[36] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b',
+ 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
+};
/* special version going backwards */
/* XXX: use dtoa.c */
@@ -12438,7 +12836,7 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
to_digit((uint8_t)p[1]) < radix)) {
p++;
}
- if (!(flags & ATOD_INT_ONLY)) {
+ if (!(flags & ATOD_INT_ONLY) && radix == 10) {
if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {
is_float = TRUE;
p++;
@@ -12448,9 +12846,7 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
(*p == sep && to_digit((uint8_t)p[1]) < radix))
p++;
}
- if (p > p_start &&
- (((*p == 'e' || *p == 'E') && radix == 10) ||
- ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {
+ if (p > p_start && (*p == 'e' || *p == 'E')) {
const char *p1 = p + 1;
is_float = TRUE;
if (*p1 == '+') {
@@ -12487,19 +12883,9 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
}
buf[j] = '\0';
- if (flags & ATOD_ACCEPT_SUFFIX) {
- if (*p == 'n') {
- p++;
- atod_type = ATOD_TYPE_BIG_INT;
- } else {
- if (is_float && radix != 10)
- goto fail;
- }
- } else {
- if (atod_type == ATOD_TYPE_FLOAT64) {
- if (is_float && radix != 10)
- goto fail;
- }
+ if ((flags & ATOD_ACCEPT_SUFFIX) && *p == 'n') {
+ p++;
+ atod_type = ATOD_TYPE_BIG_INT;
}
switch(atod_type) {
@@ -13419,8 +13805,8 @@ static void js_print_string(JSPrintValueState *s, JSValueConst val)
int sep;
if (s->options.raw_dump && JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
JSString *p = JS_VALUE_GET_STRING(val);
- js_printf(s, "%d", p->header.ref_count);
- sep = (p->header.ref_count == 1) ? '\"' : '\'';
+ js_printf(s, "%d", js_rc(p)->ref_count);
+ sep = (js_rc(p)->ref_count == 1) ? '\"' : '\'';
} else {
sep = '\"';
}
@@ -14102,10 +14488,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_rc(p)->ref_count);
if (sh) {
printf("%3d%c %14p ",
- sh->header.ref_count,
+ js_rc(sh)->ref_count,
" *"[sh->is_hashed],
(void *)sh->proto);
} else {
@@ -14123,13 +14509,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_rc(p)->gc_obj_type == 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_rc(p)->ref_count);
+ switch(js_rc(p)->gc_obj_type) {
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
printf("[function bytecode]");
break;
@@ -14149,7 +14535,7 @@ static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
printf("[module]");
break;
default:
- printf("[unknown %d]", p->gc_obj_type);
+ printf("[unknown %d]", js_rc(p)->gc_obj_type);
break;
}
printf("\n");
@@ -16612,7 +16998,7 @@ static JSVarRef *js_create_var_ref(JSContext *ctx, BOOL is_lexical)
var_ref = js_malloc(ctx, sizeof(JSVarRef));
if (!var_ref)
return NULL;
- var_ref->header.ref_count = 1;
+ js_rc(var_ref)->ref_count = 1;
if (is_lexical)
var_ref->value = JS_UNINITIALIZED;
else
@@ -16652,7 +17038,7 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
if (var_ref) {
/* reference to the already created local variable */
assert(var_ref->pvalue == pvalue);
- var_ref->header.ref_count++;
+ js_rc(var_ref)->ref_count++;
return var_ref;
}
@@ -16660,7 +17046,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_rc(var_ref)->ref_count = 1;
add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
var_ref->is_detached = FALSE;
var_ref->is_lexical = FALSE;
@@ -16678,7 +17064,7 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
the JSVarRef of async functions during the GC. It would
have the advantage of allowing the release of unused stack
frames in a cycle. */
- async_func->header.ref_count++;
+ js_rc(async_func)->ref_count++;
}
var_ref->pvalue = pvalue;
return var_ref;
@@ -16709,7 +17095,7 @@ static JSVarRef *js_global_object_get_uninitialized_var(JSContext *ctx, JSObject
if (prs) {
assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
var_ref = pr->u.var_ref;
- var_ref->header.ref_count++;
+ js_rc(var_ref)->ref_count++;
return var_ref;
}
@@ -16722,7 +17108,7 @@ static JSVarRef *js_global_object_get_uninitialized_var(JSContext *ctx, JSObject
return NULL;
}
pr->u.var_ref = var_ref;
- var_ref->header.ref_count++;
+ js_rc(var_ref)->ref_count++;
return var_ref;
}
@@ -16741,7 +17127,7 @@ static JSVarRef *js_global_object_find_uninitialized_var(JSContext *ctx, JSObjec
if (prs) {
assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
var_ref = pr->u.var_ref;
- var_ref->header.ref_count++;
+ js_rc(var_ref)->ref_count++;
delete_property(ctx, p1, atom);
if (!is_lexical)
var_ref->value = JS_UNDEFINED;
@@ -16772,7 +17158,7 @@ static JSVarRef *js_closure_define_global_var(JSContext *ctx, JSClosureVar *cv,
if (prs) {
assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
var_ref = pr->u.var_ref;
- var_ref->header.ref_count++;
+ js_rc(var_ref)->ref_count++;
return var_ref;
}
@@ -16810,7 +17196,7 @@ static JSVarRef *js_closure_define_global_var(JSContext *ctx, JSClosureVar *cv,
return NULL;
} else {
var_ref = pr->u.var_ref;
- var_ref->header.ref_count++;
+ js_rc(var_ref)->ref_count++;
}
if (cv->var_kind == JS_VAR_GLOBAL_FUNCTION_DECL &&
(prs->flags & JS_PROP_CONFIGURABLE)) {
@@ -16820,7 +17206,7 @@ static JSVarRef *js_closure_define_global_var(JSContext *ctx, JSClosureVar *cv,
free_property(ctx->rt, pr, prs->flags);
prs->flags = flags | JS_PROP_VARREF;
pr->u.var_ref = var_ref;
- var_ref->header.ref_count++;
+ js_rc(var_ref)->ref_count++;
} else {
assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
prs->flags = (prs->flags & ~JS_PROP_C_W_E) | flags;
@@ -16852,7 +17238,7 @@ static JSVarRef *js_closure_define_global_var(JSContext *ctx, JSClosureVar *cv,
return NULL;
}
pr->u.var_ref = var_ref;
- var_ref->header.ref_count++;
+ js_rc(var_ref)->ref_count++;
return var_ref;
}
@@ -16868,7 +17254,7 @@ static JSVarRef *js_closure_global_var(JSContext *ctx, JSClosureVar *cv)
if (prs) {
assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
var_ref = pr->u.var_ref;
- var_ref->header.ref_count++;
+ js_rc(var_ref)->ref_count++;
return var_ref;
}
p = JS_VALUE_GET_OBJ(ctx->global_obj);
@@ -16883,7 +17269,7 @@ static JSVarRef *js_closure_global_var(JSContext *ctx, JSClosureVar *cv)
}
if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
var_ref = pr->u.var_ref;
- var_ref->header.ref_count++;
+ js_rc(var_ref)->ref_count++;
return var_ref;
}
}
@@ -16952,7 +17338,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_rc(var_ref)->ref_count++;
break;
default:
abort();
@@ -18412,7 +18798,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_rc(var_ref)->ref_count++;
} else {
var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref);
if (!var_ref)
@@ -20335,7 +20721,7 @@ static JSAsyncFunctionState *async_func_init(JSContext *ctx,
if (!s)
return NULL;
memset(s, 0, sizeof(*s));
- s->header.ref_count = 1;
+ js_rc(s)->ref_count = 1;
add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
sf = &s->frame;
@@ -20425,7 +20811,7 @@ static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
JS_FreeValueRT(rt, s->resolving_funcs[1]);
remove_gc_object(&s->header);
- if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && s->header.ref_count != 0) {
+ if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && js_rc(s)->ref_count != 0) {
list_add_tail(&s->header.link, &rt->gc_zero_ref_count_list);
} else {
js_free_rt(rt, s);
@@ -20434,7 +20820,7 @@ static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
{
- if (--s->header.ref_count == 0) {
+ if (--js_rc(s)->ref_count == 0) {
if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
list_del(&s->header.link);
list_add(&s->header.link, &rt->gc_zero_ref_count_list);
@@ -20657,7 +21043,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_rc(s)->ref_count++;
p->u.async_function_data = s;
}
return 0;
@@ -29092,7 +29478,7 @@ static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)
JS_FreeAtom(ctx, name);
return NULL;
}
- m->header.ref_count = 1;
+ js_rc(m)->ref_count = 1;
add_gc_object(ctx->rt, &m->header, JS_GC_OBJ_TYPE_MODULE);
m->module_name = name;
m->module_ns = JS_UNDEFINED;
@@ -29180,7 +29566,7 @@ static void js_free_module_def(JSRuntime *rt, JSModuleDef *m)
list_del(&m->link);
}
remove_gc_object(&m->header);
- if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && m->header.ref_count != 0) {
+ if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && js_rc(m)->ref_count != 0) {
list_add_tail(&m->header.link, &rt->gc_zero_ref_count_list);
} else {
js_free_rt(rt, m);
@@ -29900,7 +30286,7 @@ static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)
JS_PROP_VARREF);
if (!pr)
goto fail;
- var_ref->header.ref_count++;
+ js_rc(var_ref)->ref_count++;
pr->u.var_ref = var_ref;
}
break;
@@ -30191,7 +30577,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_rc(var_ref)->ref_count++;
var_refs[mi->var_idx] = var_ref;
#ifdef DUMP_MODULE_RESOLVE
printf("local export (var_ref=%p)\n", var_ref);
@@ -30207,7 +30593,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_rc(var_ref)->ref_count++;
me->u.local.var_ref = var_ref;
}
}
@@ -35567,7 +35953,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_rc(b)->ref_count = 1;
b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset);
b->byte_code_len = fd->byte_code.size;
@@ -35749,7 +36135,7 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
}
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_rc(b)->ref_count != 0) {
list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list);
} else {
js_free_rt(rt, b);
@@ -36770,7 +37156,6 @@ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx);
JS_FreeCString(ctx, str);
return ret;
-
}
JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
@@ -38184,8 +38569,6 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s)
uint64_t function_size;
memset(&bc, 0, sizeof(bc));
- bc.header.ref_count = 1;
- //bc.gc_header.mark = 0;
if (bc_get_u16(s, &v16))
goto fail;
@@ -38250,7 +38633,6 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s)
return JS_EXCEPTION;
memcpy(b, &bc, offsetof(JSFunctionBytecode, debug));
- b->header.ref_count = 1;
if (local_count != 0) {
b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
}
@@ -38261,6 +38643,7 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s)
b->cpool = (void *)((uint8_t*)b + cpool_offset);
}
+ js_rc(b)->ref_count = 1;
add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
@@ -48122,6 +48505,8 @@ static BOOL check_regexp_getter(JSContext *ctx,
return FALSE;
if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET)
return FALSE;
+ if (!pr->u.getset.getter)
+ return FALSE;
return JS_IsCFunction(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter),
func, magic);
}
@@ -51032,11 +51417,11 @@ static BOOL js_weakref_is_target(JSValueConst val)
/* XXX: add a specific JSWeakRef value type ? */
static BOOL js_weakref_is_live(JSValueConst val)
{
- int *pref_count;
+ void *p;
if (JS_IsUndefined(val))
return TRUE;
- pref_count = JS_VALUE_GET_PTR(val);
- return (*pref_count != 0);
+ p = JS_VALUE_GET_PTR(val);
+ return (js_rc(p)->ref_count != 0);
}
/* 'val' can be JS_UNDEFINED */
@@ -51049,15 +51434,15 @@ static void js_weakref_free(JSRuntime *rt, JSValue val)
/* 'mark' is tested to avoid freeing the object structure when
it is about to be freed in a cycle or in
free_zero_refcount() */
- if (p->weakref_count == 0 && p->header.ref_count == 0 &&
- p->header.mark == 0) {
+ if (p->weakref_count == 0 && js_rc(p)->ref_count == 0 &&
+ js_rc(p)->mark == 0) {
js_free_rt(rt, p);
}
} else if (JS_VALUE_GET_TAG(val) == JS_TAG_SYMBOL) {
JSString *p = JS_VALUE_GET_STRING(val);
assert(p->hash >= 1);
p->hash--;
- if (p->hash == 0 && p->header.ref_count == 0) {
+ if (p->hash == 0 && js_rc(p)->ref_count == 0) {
/* can remove the dummy structure */
js_free_rt(rt, p);
}
@@ -55391,7 +55776,7 @@ static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,
goto done;
}
}
- method = JS_GetPropertyStr(ctx, obj, "toISOString");
+ method = JS_GetProperty(ctx, obj, JS_ATOM_toISOString);
if (JS_IsException(method))
goto exception;
if (!JS_IsFunction(ctx, method)) {
@@ -58267,6 +58652,797 @@ static JSValue js_typed_array_toSorted(JSContext *ctx, JSValueConst this_val,
return ret;
}
+/* Uint8Array base64/hex (tc39 proposal-arraybuffer-base64) */
+
+enum {
+ B64_ALPHABET_BASE64 = 0,
+ B64_ALPHABET_BASE64URL = 1,
+};
+
+enum {
+ B64_LAST_LOOSE = 0,
+ B64_LAST_STRICT = 1,
+ B64_LAST_STOP_BEFORE_PARTIAL = 2,
+};
+
+static const unsigned char b64_enc[64] = {
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+ 'Q','R','S','T','U','V','W','X','Y','Z',
+ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p',
+ 'q','r','s','t','u','v','w','x','y','z',
+ '0','1','2','3','4','5','6','7','8','9',
+ '+','/'
+};
+
+static const unsigned char b64url_enc[64] = {
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+ 'Q','R','S','T','U','V','W','X','Y','Z',
+ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p',
+ 'q','r','s','t','u','v','w','x','y','z',
+ '0','1','2','3','4','5','6','7','8','9',
+ '-','_'
+};
+
+#define K_WS 64
+#define K_ER 65
+
+static const uint8_t b64_dec[256] = {
+ [ 0]=K_ER, [ 1]=K_ER, [ 2]=K_ER, [ 3]=K_ER, [ 4]=K_ER, [ 5]=K_ER, [ 6]=K_ER, [ 7]=K_ER,
+ [ 8]=K_ER, [ 9]=K_WS, [ 10]=K_WS, [ 11]=K_ER, [ 12]=K_WS, [ 13]=K_WS, [ 14]=K_ER, [ 15]=K_ER,
+ [ 16]=K_ER, [ 17]=K_ER, [ 18]=K_ER, [ 19]=K_ER, [ 20]=K_ER, [ 21]=K_ER, [ 22]=K_ER, [ 23]=K_ER,
+ [ 24]=K_ER, [ 25]=K_ER, [ 26]=K_ER, [ 27]=K_ER, [ 28]=K_ER, [ 29]=K_ER, [ 30]=K_ER, [ 31]=K_ER,
+ [' ']=K_WS, ['!']=K_ER, ['"']=K_ER, ['#']=K_ER, ['$']=K_ER, ['%']=K_ER, ['&']=K_ER, [ 39]=K_ER,
+ ['(']=K_ER, [')']=K_ER, ['*']=K_ER, ['+']= 62, [',']=K_ER, ['-']=K_ER, ['.']=K_ER, ['/']= 63,
+ ['0']= 52, ['1']= 53, ['2']= 54, ['3']= 55, ['4']= 56, ['5']= 57, ['6']= 58, ['7']= 59,
+ ['8']= 60, ['9']= 61, [':']=K_ER, [';']=K_ER, ['<']=K_ER, ['=']=K_ER, ['>']=K_ER, ['?']=K_ER,
+ ['@']=K_ER, ['A']= 0, ['B']= 1, ['C']= 2, ['D']= 3, ['E']= 4, ['F']= 5, ['G']= 6,
+ ['H']= 7, ['I']= 8, ['J']= 9, ['K']= 10, ['L']= 11, ['M']= 12, ['N']= 13, ['O']= 14,
+ ['P']= 15, ['Q']= 16, ['R']= 17, ['S']= 18, ['T']= 19, ['U']= 20, ['V']= 21, ['W']= 22,
+ ['X']= 23, ['Y']= 24, ['Z']= 25, ['[']=K_ER, [ 92]=K_ER, [']']=K_ER, ['^']=K_ER, ['_']=K_ER,
+ ['`']=K_ER, ['a']= 26, ['b']= 27, ['c']= 28, ['d']= 29, ['e']= 30, ['f']= 31, ['g']= 32,
+ ['h']= 33, ['i']= 34, ['j']= 35, ['k']= 36, ['l']= 37, ['m']= 38, ['n']= 39, ['o']= 40,
+ ['p']= 41, ['q']= 42, ['r']= 43, ['s']= 44, ['t']= 45, ['u']= 46, ['v']= 47, ['w']= 48,
+ ['x']= 49, ['y']= 50, ['z']= 51, ['{']=K_ER, ['|']=K_ER, ['}']=K_ER, ['~']=K_ER, [127]=K_ER,
+ [128]=K_ER, [129]=K_ER, [130]=K_ER, [131]=K_ER, [132]=K_ER, [133]=K_ER, [134]=K_ER, [135]=K_ER,
+ [136]=K_ER, [137]=K_ER, [138]=K_ER, [139]=K_ER, [140]=K_ER, [141]=K_ER, [142]=K_ER, [143]=K_ER,
+ [144]=K_ER, [145]=K_ER, [146]=K_ER, [147]=K_ER, [148]=K_ER, [149]=K_ER, [150]=K_ER, [151]=K_ER,
+ [152]=K_ER, [153]=K_ER, [154]=K_ER, [155]=K_ER, [156]=K_ER, [157]=K_ER, [158]=K_ER, [159]=K_ER,
+ [160]=K_ER, [161]=K_ER, [162]=K_ER, [163]=K_ER, [164]=K_ER, [165]=K_ER, [166]=K_ER, [167]=K_ER,
+ [168]=K_ER, [169]=K_ER, [170]=K_ER, [171]=K_ER, [172]=K_ER, [173]=K_ER, [174]=K_ER, [175]=K_ER,
+ [176]=K_ER, [177]=K_ER, [178]=K_ER, [179]=K_ER, [180]=K_ER, [181]=K_ER, [182]=K_ER, [183]=K_ER,
+ [184]=K_ER, [185]=K_ER, [186]=K_ER, [187]=K_ER, [188]=K_ER, [189]=K_ER, [190]=K_ER, [191]=K_ER,
+ [192]=K_ER, [193]=K_ER, [194]=K_ER, [195]=K_ER, [196]=K_ER, [197]=K_ER, [198]=K_ER, [199]=K_ER,
+ [200]=K_ER, [201]=K_ER, [202]=K_ER, [203]=K_ER, [204]=K_ER, [205]=K_ER, [206]=K_ER, [207]=K_ER,
+ [208]=K_ER, [209]=K_ER, [210]=K_ER, [211]=K_ER, [212]=K_ER, [213]=K_ER, [214]=K_ER, [215]=K_ER,
+ [216]=K_ER, [217]=K_ER, [218]=K_ER, [219]=K_ER, [220]=K_ER, [221]=K_ER, [222]=K_ER, [223]=K_ER,
+ [224]=K_ER, [225]=K_ER, [226]=K_ER, [227]=K_ER, [228]=K_ER, [229]=K_ER, [230]=K_ER, [231]=K_ER,
+ [232]=K_ER, [233]=K_ER, [234]=K_ER, [235]=K_ER, [236]=K_ER, [237]=K_ER, [238]=K_ER, [239]=K_ER,
+ [240]=K_ER, [241]=K_ER, [242]=K_ER, [243]=K_ER, [244]=K_ER, [245]=K_ER, [246]=K_ER, [247]=K_ER,
+ [248]=K_ER, [249]=K_ER, [250]=K_ER, [251]=K_ER, [252]=K_ER, [253]=K_ER, [254]=K_ER, [255]=K_ER,
+};
+
+static const uint8_t b64url_dec[256] = {
+ [ 0]=K_ER, [ 1]=K_ER, [ 2]=K_ER, [ 3]=K_ER, [ 4]=K_ER, [ 5]=K_ER, [ 6]=K_ER, [ 7]=K_ER,
+ [ 8]=K_ER, [ 9]=K_WS, [ 10]=K_WS, [ 11]=K_ER, [ 12]=K_WS, [ 13]=K_WS, [ 14]=K_ER, [ 15]=K_ER,
+ [ 16]=K_ER, [ 17]=K_ER, [ 18]=K_ER, [ 19]=K_ER, [ 20]=K_ER, [ 21]=K_ER, [ 22]=K_ER, [ 23]=K_ER,
+ [ 24]=K_ER, [ 25]=K_ER, [ 26]=K_ER, [ 27]=K_ER, [ 28]=K_ER, [ 29]=K_ER, [ 30]=K_ER, [ 31]=K_ER,
+ [' ']=K_WS, ['!']=K_ER, ['"']=K_ER, ['#']=K_ER, ['$']=K_ER, ['%']=K_ER, ['&']=K_ER, [ 39]=K_ER,
+ ['(']=K_ER, [')']=K_ER, ['*']=K_ER, ['+']=K_ER, [',']=K_ER, ['-']= 62, ['.']=K_ER, ['/']=K_ER,
+ ['0']= 52, ['1']= 53, ['2']= 54, ['3']= 55, ['4']= 56, ['5']= 57, ['6']= 58, ['7']= 59,
+ ['8']= 60, ['9']= 61, [':']=K_ER, [';']=K_ER, ['<']=K_ER, ['=']=K_ER, ['>']=K_ER, ['?']=K_ER,
+ ['@']=K_ER, ['A']= 0, ['B']= 1, ['C']= 2, ['D']= 3, ['E']= 4, ['F']= 5, ['G']= 6,
+ ['H']= 7, ['I']= 8, ['J']= 9, ['K']= 10, ['L']= 11, ['M']= 12, ['N']= 13, ['O']= 14,
+ ['P']= 15, ['Q']= 16, ['R']= 17, ['S']= 18, ['T']= 19, ['U']= 20, ['V']= 21, ['W']= 22,
+ ['X']= 23, ['Y']= 24, ['Z']= 25, ['[']=K_ER, [ 92]=K_ER, [']']=K_ER, ['^']=K_ER, ['_']= 63,
+ ['`']=K_ER, ['a']= 26, ['b']= 27, ['c']= 28, ['d']= 29, ['e']= 30, ['f']= 31, ['g']= 32,
+ ['h']= 33, ['i']= 34, ['j']= 35, ['k']= 36, ['l']= 37, ['m']= 38, ['n']= 39, ['o']= 40,
+ ['p']= 41, ['q']= 42, ['r']= 43, ['s']= 44, ['t']= 45, ['u']= 46, ['v']= 47, ['w']= 48,
+ ['x']= 49, ['y']= 50, ['z']= 51, ['{']=K_ER, ['|']=K_ER, ['}']=K_ER, ['~']=K_ER, [127]=K_ER,
+ [128]=K_ER, [129]=K_ER, [130]=K_ER, [131]=K_ER, [132]=K_ER, [133]=K_ER, [134]=K_ER, [135]=K_ER,
+ [136]=K_ER, [137]=K_ER, [138]=K_ER, [139]=K_ER, [140]=K_ER, [141]=K_ER, [142]=K_ER, [143]=K_ER,
+ [144]=K_ER, [145]=K_ER, [146]=K_ER, [147]=K_ER, [148]=K_ER, [149]=K_ER, [150]=K_ER, [151]=K_ER,
+ [152]=K_ER, [153]=K_ER, [154]=K_ER, [155]=K_ER, [156]=K_ER, [157]=K_ER, [158]=K_ER, [159]=K_ER,
+ [160]=K_ER, [161]=K_ER, [162]=K_ER, [163]=K_ER, [164]=K_ER, [165]=K_ER, [166]=K_ER, [167]=K_ER,
+ [168]=K_ER, [169]=K_ER, [170]=K_ER, [171]=K_ER, [172]=K_ER, [173]=K_ER, [174]=K_ER, [175]=K_ER,
+ [176]=K_ER, [177]=K_ER, [178]=K_ER, [179]=K_ER, [180]=K_ER, [181]=K_ER, [182]=K_ER, [183]=K_ER,
+ [184]=K_ER, [185]=K_ER, [186]=K_ER, [187]=K_ER, [188]=K_ER, [189]=K_ER, [190]=K_ER, [191]=K_ER,
+ [192]=K_ER, [193]=K_ER, [194]=K_ER, [195]=K_ER, [196]=K_ER, [197]=K_ER, [198]=K_ER, [199]=K_ER,
+ [200]=K_ER, [201]=K_ER, [202]=K_ER, [203]=K_ER, [204]=K_ER, [205]=K_ER, [206]=K_ER, [207]=K_ER,
+ [208]=K_ER, [209]=K_ER, [210]=K_ER, [211]=K_ER, [212]=K_ER, [213]=K_ER, [214]=K_ER, [215]=K_ER,
+ [216]=K_ER, [217]=K_ER, [218]=K_ER, [219]=K_ER, [220]=K_ER, [221]=K_ER, [222]=K_ER, [223]=K_ER,
+ [224]=K_ER, [225]=K_ER, [226]=K_ER, [227]=K_ER, [228]=K_ER, [229]=K_ER, [230]=K_ER, [231]=K_ER,
+ [232]=K_ER, [233]=K_ER, [234]=K_ER, [235]=K_ER, [236]=K_ER, [237]=K_ER, [238]=K_ER, [239]=K_ER,
+ [240]=K_ER, [241]=K_ER, [242]=K_ER, [243]=K_ER, [244]=K_ER, [245]=K_ER, [246]=K_ER, [247]=K_ER,
+ [248]=K_ER, [249]=K_ER, [250]=K_ER, [251]=K_ER, [252]=K_ER, [253]=K_ER, [254]=K_ER, [255]=K_ER,
+};
+
+static size_t b64_encode(const uint8_t *src, size_t len, char *dst,
+ const unsigned char *alpha)
+{
+ size_t i, j;
+
+ for (i = 0, j = 0; i + 3 <= len; i += 3, j += 4) {
+ uint32_t v = 65536*src[i] + 256*src[i + 1] + src[i + 2];
+ dst[j + 0] = alpha[(v >> 18) & 63];
+ dst[j + 1] = alpha[(v >> 12) & 63];
+ dst[j + 2] = alpha[(v >> 6) & 63];
+ dst[j + 3] = alpha[v & 63];
+ }
+
+ size_t rem = len - i;
+ if (rem == 1) {
+ uint32_t v = 65536*src[i];
+ dst[j++] = alpha[(v >> 18) & 63];
+ dst[j++] = alpha[(v >> 12) & 63];
+ dst[j++] = '=';
+ dst[j++] = '=';
+ } else if (rem == 2) {
+ uint32_t v = 65536*src[i] + 256*src[i + 1];
+ dst[j++] = alpha[(v >> 18) & 63];
+ dst[j++] = alpha[(v >> 12) & 63];
+ dst[j++] = alpha[(v >> 6) & 63];
+ dst[j++] = '=';
+ }
+ return j;
+}
+
+static size_t b64_skip_ws(const char *src, size_t len, size_t index,
+ const uint8_t *dec_table)
+{
+ while (index < len && dec_table[(unsigned char)src[index]] == K_WS)
+ index++;
+ return index;
+}
+
+/* Implements the FromBase64 abstract operation.
+ src/src_len: the input string (must be ASCII/latin1)
+ dst/max_len: output buffer
+ flags: b64_flags or b64_flags_url (selects valid characters)
+ last_chunk: B64_LAST_LOOSE, B64_LAST_STRICT, or B64_LAST_STOP_BEFORE_PARTIAL
+ *p_read: set to number of input characters consumed
+ *p_err: set to 1 on error, 0 on success
+ Returns: number of bytes written to dst */
+static size_t from_base64(const char *src, size_t src_len,
+ uint8_t *dst, size_t max_len,
+ const uint8_t *dec_table, int last_chunk,
+ size_t *p_read, int *p_err)
+{
+ size_t read = 0, written = 0;
+ uint32_t v, acc = 0;
+ int seen = 0;
+ size_t index = 0;
+ uint8_t ch;
+
+ *p_err = 0;
+
+ if (max_len == 0) {
+ *p_read = 0;
+ return 0;
+ }
+
+ for (;;) {
+ if (seen == 0) {
+ /* Fast path: decode complete groups of 4 valid characters.
+ Breaks out on whitespace, padding, invalid chars, or capacity. */
+ while (index + 4 <= src_len && written + 3 <= max_len) {
+ uint32_t v0, v1, v2, v3;
+ v0 = dec_table[(unsigned char)src[index]];
+ v1 = dec_table[(unsigned char)src[index + 1]];
+ v2 = dec_table[(unsigned char)src[index + 2]];
+ v3 = dec_table[(unsigned char)src[index + 3]];
+ if ((v0 | v1 | v2 | v3) >= 64)
+ break;
+ v = (v0 << 18) | (v1 << 12) | (v2 << 6) | v3;
+ dst[written] = (uint8_t)(v >> 16);
+ dst[written + 1] = (uint8_t)(v >> 8);
+ dst[written + 2] = (uint8_t)(v);
+ written += 3;
+ index += 4;
+ }
+ read = index;
+
+ if (written >= max_len) {
+ *p_read = read;
+ return written;
+ }
+ }
+
+ /* Slow path: handle whitespace, padding, partial groups, capacity. */
+ index = b64_skip_ws(src, src_len, index, dec_table);
+
+ if (index == src_len) {
+ if (seen > 0) {
+ if (last_chunk == B64_LAST_STOP_BEFORE_PARTIAL) {
+ *p_read = read;
+ return written;
+ }
+ if (last_chunk == B64_LAST_STRICT) {
+ *p_err = 1;
+ return 0;
+ }
+ /* loose */
+ if (seen == 1) {
+ *p_err = 1;
+ return 0;
+ }
+ break;
+ }
+ *p_read = src_len;
+ return written;
+ }
+
+ ch = src[index++];
+
+ if (ch == '=') {
+ if (seen < 2) {
+ *p_err = 1;
+ return 0;
+ }
+ index = b64_skip_ws(src, src_len, index, dec_table);
+ if (seen == 2) {
+ if (index == src_len) {
+ if (last_chunk == B64_LAST_STOP_BEFORE_PARTIAL) {
+ *p_read = read;
+ return written;
+ }
+ *p_err = 1;
+ return 0;
+ }
+ if (src[index] == '=') {
+ index++;
+ index = b64_skip_ws(src, src_len, index, dec_table);
+ } else {
+ *p_err = 1;
+ return 0;
+ }
+ }
+ /* After padding, only whitespace is allowed */
+ if (index != src_len) {
+ *p_err = 1;
+ return 0;
+ }
+ if (last_chunk == B64_LAST_STRICT) {
+ uint32_t mask = (seen == 2) ? 0xF : 0x3;
+ if (acc & mask) {
+ *p_err = 1;
+ return 0;
+ }
+ }
+ break;
+ }
+
+ v = dec_table[ch];
+ if (v >= 64) {
+ *p_err = 1;
+ return 0;
+ }
+
+ /* Check remaining capacity before committing to this group */
+ {
+ size_t remaining = max_len - written;
+ if ((remaining == 1 && seen == 2) ||
+ (remaining == 2 && seen == 3)) {
+ *p_read = read;
+ return written;
+ }
+ }
+
+ acc = (acc << 6) | v;
+ seen++;
+
+ if (seen == 4) {
+ dst[written] = (uint8_t)(acc >> 16);
+ dst[written + 1] = (uint8_t)(acc >> 8);
+ dst[written + 2] = (uint8_t)(acc);
+ written += 3;
+ acc = 0;
+ seen = 0;
+ read = index;
+ if (written >= max_len) {
+ *p_read = read;
+ return written;
+ }
+ }
+ }
+
+ if (seen == 2) {
+ dst[written++] = (uint8_t)(acc >> 4);
+ } else if (seen == 3) {
+ dst[written] = (uint8_t)(acc >> 10);
+ dst[written + 1] = (uint8_t)(acc >> 2);
+ written += 2;
+ }
+ *p_read = src_len;
+ return written;
+}
+
+/* Hex helpers */
+static const char u8a_hex_digits[] = "0123456789abcdef";
+
+static size_t u8a_hex_encode(const uint8_t *src, size_t len, char *dst)
+{
+ for (size_t i = 0; i < len; i++) {
+ dst[i * 2] = u8a_hex_digits[src[i] >> 4];
+ dst[i * 2 + 1] = u8a_hex_digits[src[i] & 0xF];
+ }
+ return len * 2;
+}
+
+/* Decode hex string to bytes.
+ Returns bytes written. Sets *p_read to chars consumed, *p_err on error. */
+static size_t u8a_hex_decode(const char *src, size_t src_len,
+ uint8_t *dst, size_t max_len,
+ size_t *p_read, int *p_err)
+{
+ size_t written = 0, i = 0;
+ *p_err = 0;
+
+ if (src_len & 1) {
+ *p_err = 1;
+ return 0;
+ }
+
+ while (i < src_len && written < max_len) {
+ int hi = from_hex(src[i]);
+ int lo = from_hex(src[i + 1]);
+ if (hi < 0 || lo < 0) {
+ *p_err = 1;
+ return 0;
+ }
+ dst[written++] = (uint8_t)((hi << 4) | lo);
+ i += 2;
+ }
+
+ *p_read = i;
+ return written;
+}
+
+static JSValue JS_NewUint8ArrayCopy(JSContext *ctx, const uint8_t *buf, size_t len)
+{
+ JSValue buffer, obj;
+ JSArrayBuffer *abuf;
+
+ buffer = js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL,
+ JS_CLASS_ARRAY_BUFFER,
+ (uint8_t *)buf,
+ js_array_buffer_free, NULL,
+ TRUE);
+ if (JS_IsException(buffer))
+ return JS_EXCEPTION;
+ obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_UINT8_ARRAY);
+ if (JS_IsException(obj)) {
+ JS_FreeValue(ctx, buffer);
+ return JS_EXCEPTION;
+ }
+ abuf = js_get_array_buffer(ctx, buffer);
+ assert(abuf != NULL);
+ if (typed_array_init(ctx, obj, buffer, 0, abuf->byte_length, /*track_rab*/FALSE)) {
+ // 'buffer' is freed on error above.
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ return obj;
+}
+
+/* Validate that this_val is a Uint8Array (type check only, no detach check).
+ Returns the JSObject pointer or NULL on error (throws). */
+static JSObject *check_uint8array(JSContext *ctx, JSValueConst this_val)
+{
+ JSObject *p;
+
+ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
+ goto fail;
+ p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id != JS_CLASS_UINT8_ARRAY)
+ goto fail;
+ return p;
+fail:
+ JS_ThrowTypeError(ctx, "not a Uint8Array");
+ return NULL;
+}
+
+/* Get the data pointer and length of a Uint8Array, checking for detached
+ buffers. Must be called after options are read (per spec ordering).
+ Returns 0 on success, -1 on error (throws). */
+static int get_uint8array_bytes(JSContext *ctx, JSObject *p,
+ uint8_t **pdata, size_t *plen)
+{
+ if (typed_array_is_oob(p)) {
+ JS_ThrowTypeErrorArrayBufferOOB(ctx);
+ *pdata = NULL; /* fail safe */
+ *plen = 0;
+ return -1;
+ }
+ *pdata = p->u.array.u.uint8_ptr;
+ *plen = p->u.array.count;
+ return 0;
+}
+
+/* Validate options is undefined or an object (GetOptionsObject).
+ Returns 0 on success, -1 on error (throws). */
+static int check_options_object(JSContext *ctx, JSValueConst options)
+{
+ if (JS_IsUndefined(options))
+ return 0;
+ if (!JS_IsObject(options)) {
+ JS_ThrowTypeError(ctx, "options must be an object");
+ return -1;
+ }
+ return 0;
+}
+
+/* Parse the 'alphabet' option from an options object.
+ Returns B64_ALPHABET_BASE64 or B64_ALPHABET_BASE64URL, or -1 on error. */
+static int parse_alphabet_option(JSContext *ctx, JSValueConst options)
+{
+ JSValue val;
+ const char *str;
+ int ret;
+
+ if (JS_IsUndefined(options))
+ return B64_ALPHABET_BASE64;
+
+ val = JS_GetProperty(ctx, options, JS_ATOM_alphabet);
+ if (JS_IsException(val))
+ return -1;
+ if (JS_IsUndefined(val))
+ return B64_ALPHABET_BASE64;
+ if (!JS_IsString(val)) {
+ JS_FreeValue(ctx, val);
+ JS_ThrowTypeError(ctx, "expected string for alphabet");
+ return -1;
+ }
+
+ str = JS_ToCString(ctx, val);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ return -1;
+
+ if (!strcmp(str, "base64"))
+ ret = B64_ALPHABET_BASE64;
+ else if (!strcmp(str, "base64url"))
+ ret = B64_ALPHABET_BASE64URL;
+ else {
+ JS_ThrowTypeError(ctx, "invalid alphabet");
+ ret = -1;
+ }
+ JS_FreeCString(ctx, str);
+ return ret;
+}
+
+/* Parse the 'lastChunkHandling' option. Returns mode or -1 on error. */
+static int parse_last_chunk_option(JSContext *ctx, JSValueConst options)
+{
+ JSValue val;
+ const char *str;
+ int ret;
+
+ if (JS_IsUndefined(options))
+ return B64_LAST_LOOSE;
+
+ val = JS_GetProperty(ctx, options, JS_ATOM_lastChunkHandling);
+ if (JS_IsException(val))
+ return -1;
+ if (JS_IsUndefined(val))
+ return B64_LAST_LOOSE;
+ if (!JS_IsString(val)) {
+ JS_FreeValue(ctx, val);
+ JS_ThrowTypeError(ctx, "expected string for lastChunkHandling");
+ return -1;
+ }
+
+ str = JS_ToCString(ctx, val);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ return -1;
+
+ if (!strcmp(str, "loose"))
+ ret = B64_LAST_LOOSE;
+ else if (!strcmp(str, "strict"))
+ ret = B64_LAST_STRICT;
+ else if (!strcmp(str, "stop-before-partial"))
+ ret = B64_LAST_STOP_BEFORE_PARTIAL;
+ else {
+ JS_ThrowTypeError(ctx, "invalid lastChunkHandling option");
+ ret = -1;
+ }
+ JS_FreeCString(ctx, str);
+ return ret;
+}
+
+/* Uint8Array.prototype.toBase64([options]) */
+static JSValue js_uint8array_to_base64(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ uint8_t *data;
+ size_t len;
+ JSValueConst options;
+ JSObject *p;
+ int alphabet, omit_padding;
+ size_t out_len, written;
+ JSString *ostr;
+ char *dst;
+
+ p = check_uint8array(ctx, this_val);
+ if (!p)
+ return JS_EXCEPTION;
+
+ options = argc > 0 ? argv[0] : JS_UNDEFINED;
+ if (check_options_object(ctx, options))
+ return JS_EXCEPTION;
+ alphabet = parse_alphabet_option(ctx, options);
+ if (alphabet < 0)
+ return JS_EXCEPTION;
+
+ omit_padding = 0;
+ if (!JS_IsUndefined(options)) {
+ JSValue op_val = JS_GetProperty(ctx, options, JS_ATOM_omitPadding);
+ if (JS_IsException(op_val))
+ return JS_EXCEPTION;
+ omit_padding = JS_ToBool(ctx, op_val);
+ JS_FreeValue(ctx, op_val);
+ }
+
+ if (get_uint8array_bytes(ctx, p, &data, &len))
+ return JS_EXCEPTION;
+
+ out_len = 4 * ((len + 2) / 3);
+
+ if (unlikely(out_len > JS_STRING_LEN_MAX))
+ return JS_ThrowRangeError(ctx, "output too large");
+
+ ostr = js_alloc_string(ctx, out_len, 0);
+ if (!ostr)
+ return JS_EXCEPTION;
+
+ dst = (char *)ostr->u.str8;
+ written = b64_encode(data, len, dst,
+ alphabet == B64_ALPHABET_BASE64URL ? b64url_enc : b64_enc);
+ if (omit_padding) {
+ while (written > 0 && dst[written - 1] == '=')
+ written--;
+ }
+ dst[written] = '\0';
+
+ ostr->len = written;
+ return JS_MKPTR(JS_TAG_STRING, ostr);
+}
+
+/* Uint8Array.prototype.toHex() */
+static JSValue js_uint8array_to_hex(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ uint8_t *data;
+ size_t len, out_len;
+ JSObject *p;
+ JSString *ostr;
+
+ p = check_uint8array(ctx, this_val);
+ if (!p)
+ return JS_EXCEPTION;
+ if (get_uint8array_bytes(ctx, p, &data, &len))
+ return JS_EXCEPTION;
+
+ out_len = len * 2;
+ if (unlikely(out_len > JS_STRING_LEN_MAX))
+ return JS_ThrowRangeError(ctx, "output too large");
+
+ ostr = js_alloc_string(ctx, out_len, 0);
+ if (!ostr)
+ return JS_EXCEPTION;
+
+ u8a_hex_encode(data, len, (char *)ostr->u.str8);
+ ostr->u.str8[out_len] = '\0';
+ return JS_MKPTR(JS_TAG_STRING, ostr);
+}
+
+/* Uint8Array.fromBase64(string[, options]) */
+static JSValue js_uint8array_from_base64(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *str;
+ size_t str_len, read_pos, decoded_len, out_cap;
+ int alphabet, last_chunk, err;
+ uint8_t *buf;
+ JSValue result;
+ JSValueConst options;
+
+ if (!JS_IsString(argv[0]))
+ return JS_ThrowTypeError(ctx, "expected string");
+
+ str = JS_ToCStringLen(ctx, &str_len, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+
+ options = argc > 1 ? argv[1] : JS_UNDEFINED;
+ if (check_options_object(ctx, options)) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+ alphabet = parse_alphabet_option(ctx, options);
+ if (alphabet < 0) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+ last_chunk = parse_last_chunk_option(ctx, options);
+ if (last_chunk < 0) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+
+ out_cap = (str_len / 4) * 3 + 3;
+ buf = js_malloc(ctx, out_cap);
+ if (!buf) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+
+ decoded_len = from_base64(str, str_len, buf, out_cap,
+ alphabet == B64_ALPHABET_BASE64URL
+ ? b64url_dec : b64_dec,
+ last_chunk, &read_pos, &err);
+ JS_FreeCString(ctx, str);
+
+ if (err) {
+ js_free(ctx, buf);
+ return JS_ThrowSyntaxError(ctx, "invalid base64 string");
+ }
+
+ result = JS_NewUint8ArrayCopy(ctx, buf, decoded_len);
+ js_free(ctx, buf);
+ return result;
+}
+
+/* Uint8Array.fromHex(string) */
+static JSValue js_uint8array_from_hex(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *str;
+ size_t str_len, read_pos, decoded_len, out_cap;
+ int err;
+ uint8_t *buf;
+ JSValue result;
+
+ if (!JS_IsString(argv[0]))
+ return JS_ThrowTypeError(ctx, "expected string");
+
+ str = JS_ToCStringLen(ctx, &str_len, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+
+ out_cap = str_len / 2 + 1;
+ buf = js_malloc(ctx, out_cap);
+ if (!buf) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+
+ decoded_len = u8a_hex_decode(str, str_len, buf, out_cap, &read_pos, &err);
+ JS_FreeCString(ctx, str);
+
+ if (err) {
+ js_free(ctx, buf);
+ return JS_ThrowSyntaxError(ctx, "invalid hex string");
+ }
+
+ /* XXX: could avoid the copy */
+ result = JS_NewUint8ArrayCopy(ctx, buf, decoded_len);
+ js_free(ctx, buf);
+ return result;
+}
+
+/* Return a { read, written } result object */
+static JSValue js_make_read_written(JSContext *ctx, size_t read, size_t written)
+{
+ JSValue obj = JS_NewObject(ctx);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ if (JS_DefinePropertyValueStr(ctx, obj, "read",
+ JS_NewUint32(ctx, read), JS_PROP_C_W_E) < 0)
+ goto fail;
+ if (JS_DefinePropertyValueStr(ctx, obj, "written",
+ JS_NewUint32(ctx, written), JS_PROP_C_W_E) < 0)
+ goto fail;
+ return obj;
+fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+/* Uint8Array.prototype.setFromBase64(string[, options]) */
+static JSValue js_uint8array_set_from_base64(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ uint8_t *data;
+ size_t len;
+ const char *str;
+ size_t str_len, read_pos, decoded_len;
+ JSObject *p;
+ int alphabet, last_chunk, err;
+ JSValueConst options;
+
+ p = check_uint8array(ctx, this_val);
+ if (!p)
+ return JS_EXCEPTION;
+
+ if (!JS_IsString(argv[0]))
+ return JS_ThrowTypeError(ctx, "expected string");
+
+ str = JS_ToCStringLen(ctx, &str_len, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+
+ options = argc > 1 ? argv[1] : JS_UNDEFINED;
+ if (check_options_object(ctx, options)) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+ alphabet = parse_alphabet_option(ctx, options);
+ if (alphabet < 0) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+ last_chunk = parse_last_chunk_option(ctx, options);
+ if (last_chunk < 0) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+
+ if (get_uint8array_bytes(ctx, p, &data, &len)) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+
+ decoded_len = from_base64(str, str_len, data, len,
+ alphabet == B64_ALPHABET_BASE64URL
+ ? b64url_dec : b64_dec,
+ last_chunk, &read_pos, &err);
+ JS_FreeCString(ctx, str);
+
+ if (err)
+ return JS_ThrowSyntaxError(ctx, "invalid base64 string");
+
+ return js_make_read_written(ctx, read_pos, decoded_len);
+}
+
+/* Uint8Array.prototype.setFromHex(string) */
+static JSValue js_uint8array_set_from_hex(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ uint8_t *data;
+ size_t len;
+ const char *str;
+ size_t str_len, read_pos, decoded_len;
+ JSObject *p;
+ int err;
+
+ p = check_uint8array(ctx, this_val);
+ if (!p)
+ return JS_EXCEPTION;
+
+ if (!JS_IsString(argv[0]))
+ return JS_ThrowTypeError(ctx, "expected string");
+
+ str = JS_ToCStringLen(ctx, &str_len, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+
+ if (get_uint8array_bytes(ctx, p, &data, &len)) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+
+ decoded_len = u8a_hex_decode(str, str_len, data, len, &read_pos, &err);
+ JS_FreeCString(ctx, str);
+
+ if (err)
+ return JS_ThrowSyntaxError(ctx, "invalid hex string");
+
+ return js_make_read_written(ctx, read_pos, decoded_len);
+}
+
static const JSCFunctionListEntry js_typed_array_base_funcs[] = {
JS_CFUNC_DEF("from", 1, js_typed_array_from ),
JS_CFUNC_DEF("of", 0, js_typed_array_of ),
@@ -58320,6 +59496,20 @@ static const JSCFunctionListEntry js_typed_array_funcs[] = {
JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 8, 0),
};
+static const JSCFunctionListEntry js_uint8array_proto_funcs[] = {
+ JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 1, 0),
+ JS_CFUNC_DEF("toBase64", 0, js_uint8array_to_base64),
+ JS_CFUNC_DEF("toHex", 0, js_uint8array_to_hex),
+ JS_CFUNC_DEF("setFromBase64", 1, js_uint8array_set_from_base64),
+ JS_CFUNC_DEF("setFromHex", 1, js_uint8array_set_from_hex),
+};
+
+static const JSCFunctionListEntry js_uint8array_funcs[] = {
+ JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 1, 0),
+ JS_CFUNC_DEF("fromBase64", 1, js_uint8array_from_base64),
+ JS_CFUNC_DEF("fromHex", 1, js_uint8array_from_hex),
+};
+
static JSValue js_typed_array_base_constructor(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv)
@@ -59020,19 +60210,16 @@ typedef enum AtomicsOpEnum {
ATOMICS_OP_LOAD,
} AtomicsOpEnum;
-static int js_atomics_get_ptr(JSContext *ctx, void **pptr,
- JSObject **pobj, uint64_t *pidx,
- int *psize_log2, JSClassID *pclass_id,
- JSValueConst obj, JSValueConst idx_val,
- int is_waitable)
+static JSObject *js_atomics_get_buf(JSContext *ctx,
+ JSValueConst obj, JSValueConst idx_val,
+ uint64_t *pidx, int is_waitable)
{
JSObject *p;
JSTypedArray *ta;
JSArrayBuffer *abuf;
- void *ptr;
uint64_t idx;
BOOL err;
- int size_log2, old_len;
+ int old_len;
if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
goto fail;
@@ -59046,59 +60233,44 @@ static int js_atomics_get_ptr(JSContext *ctx, void **pptr,
if (err) {
fail:
JS_ThrowTypeError(ctx, "integer TypedArray expected");
- return -1;
+ return NULL;
}
ta = p->u.typed_array;
abuf = ta->buffer->u.array_buffer;
if (!abuf->shared) {
if (is_waitable == 2) {
JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray");
- return -1;
+ return NULL;
}
if (abuf->detached) {
JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- return -1;
+ return NULL;
}
}
old_len = p->u.array.count;
if (JS_ToIndex(ctx, &idx, idx_val)) {
- return -1;
+ return NULL;
}
if (idx >= old_len)
goto oob;
- if (is_waitable == 1) {
- /* notify(): just avoid having an invalid pointer if overflow */
- if (idx >= p->u.array.count)
- ptr = NULL;
- } else {
+ if (is_waitable != 1) {
/* RevalidateAtomicAccess() */
if (typed_array_is_oob(p)) {
JS_ThrowTypeErrorArrayBufferOOB(ctx);
- return -1;
+ return NULL;
}
if (idx >= p->u.array.count) {
oob:
JS_ThrowRangeError(ctx, "out-of-bound access");
- return -1;
+ return NULL;
}
}
- size_log2 = typed_array_size_log2(p->class_id);
- ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
-
- *pptr = ptr;
- if (pobj)
- *pobj = p;
- if (pidx)
- *pidx = idx;
- if (psize_log2)
- *psize_log2 = size_log2;
- if (pclass_id)
- *pclass_id = p->class_id;
- return 0;
+ *pidx = idx;
+ return p;
}
static JSValue js_atomics_op(JSContext *ctx,
@@ -59109,12 +60281,12 @@ static JSValue js_atomics_op(JSContext *ctx,
uint64_t v, a, rep_val, idx;
void *ptr;
JSValue ret;
- JSClassID class_id;
JSObject *p;
- if (js_atomics_get_ptr(ctx, &ptr, &p, &idx, &size_log2, &class_id,
- argv[0], argv[1], 0))
+ p = js_atomics_get_buf(ctx, argv[0], argv[1], &idx, 0);
+ if (!p)
return JS_EXCEPTION;
+ size_log2 = typed_array_size_log2(p->class_id);
rep_val = 0;
if (op == ATOMICS_OP_LOAD) {
v = 0;
@@ -59144,9 +60316,10 @@ static JSValue js_atomics_op(JSContext *ctx,
return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
if (idx >= p->u.array.count)
return JS_ThrowRangeError(ctx, "out-of-bound access");
- }
-
- switch(op | (size_log2 << 3)) {
+ }
+ ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
+
+ switch(op | (size_log2 << 3)) {
#define OP(op_name, func_name) \
case ATOMICS_OP_ ## op_name | (0 << 3): \
@@ -59215,7 +60388,7 @@ static JSValue js_atomics_op(JSContext *ctx,
abort();
}
- switch(class_id) {
+ switch(p->class_id) {
case JS_CLASS_INT8_ARRAY:
a = (int8_t)a;
goto done;
@@ -59257,10 +60430,11 @@ static JSValue js_atomics_store(JSContext *ctx,
JSObject *p;
uint64_t idx;
int64_t v;
-
- if (js_atomics_get_ptr(ctx, &ptr, &p, &idx, &size_log2, NULL,
- argv[0], argv[1], 0))
+
+ p = js_atomics_get_buf(ctx, argv[0], argv[1], &idx, 0);
+ if (!p)
return JS_EXCEPTION;
+ size_log2 = typed_array_size_log2(p->class_id);
if (size_log2 == 3) {
ret = JS_ToBigIntFree(ctx, JS_DupValue(ctx, argv[2]));
if (JS_IsException(ret))
@@ -59286,6 +60460,8 @@ static JSValue js_atomics_store(JSContext *ctx,
if (idx >= p->u.array.count)
return JS_ThrowRangeError(ctx, "out-of-bound access");
+ ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
+
switch(size_log2) {
case 0:
atomic_store((_Atomic(uint8_t) *)ptr, v);
@@ -59374,8 +60550,10 @@ static JSValue js_atomics_wait(JSContext *ctx,
JSValueConst this_obj,
int argc, JSValueConst *argv)
{
+ JSObject *p;
int64_t v;
int32_t v32;
+ uint64_t idx;
void *ptr;
int64_t timeout;
struct timespec ts;
@@ -59383,9 +60561,12 @@ static JSValue js_atomics_wait(JSContext *ctx,
int ret, size_log2, res;
double d;
- if (js_atomics_get_ptr(ctx, &ptr, NULL, NULL, &size_log2, NULL,
- argv[0], argv[1], 2))
+ p = js_atomics_get_buf(ctx, argv[0], argv[1], &idx, 2);
+ if (!p)
return JS_EXCEPTION;
+ size_log2 = typed_array_size_log2(p->class_id);
+ ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
+
/* 'argv[0]' is a SharedArrayBuffer so it cannot be detached nor reduced */
if (size_log2 == 3) {
if (JS_ToBigInt64(ctx, &v, argv[2]))
@@ -59459,13 +60640,17 @@ static JSValue js_atomics_notify(JSContext *ctx,
{
struct list_head *el, *el1, waiter_list;
int32_t count, n;
+ uint64_t idx;
+ int size_log2;
void *ptr;
JSAtomicsWaiter *waiter;
JSArrayBuffer *abuf;
JSObject *p;
- if (js_atomics_get_ptr(ctx, &ptr, &p, NULL, NULL, NULL, argv[0], argv[1], 1))
+ p = js_atomics_get_buf(ctx, argv[0], argv[1], &idx, 1);
+ if (!p)
return JS_EXCEPTION;
+ size_log2 = typed_array_size_log2(p->class_id);
if (JS_IsUndefined(argv[2])) {
count = INT32_MAX;
@@ -59478,6 +60663,7 @@ static JSValue js_atomics_notify(JSContext *ctx,
abuf = p->u.typed_array->buffer->u.array_buffer;
if (abuf->shared && count > 0) {
/* 'argv[0]' is a SharedArrayBuffer so it cannot be detached nor reduced */
+ ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
pthread_mutex_lock(&js_atomics_mutex);
init_list_head(&waiter_list);
list_for_each_safe(el, el1, &js_atomics_waiter_list) {
@@ -59584,17 +60770,25 @@ int JS_AddIntrinsicTypedArrays(JSContext *ctx)
for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) {
char buf[ATOM_GET_STR_BUF_SIZE];
const char *name;
- const JSCFunctionListEntry *bpe;
name = JS_AtomGetStr(ctx, buf, sizeof(buf),
JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY);
- bpe = js_typed_array_funcs + typed_array_size_log2(i);
- obj = JS_NewCConstructor(ctx, i, name,
- ft.generic, 3, JS_CFUNC_constructor_magic, i,
- typed_array_base_func,
- bpe, 1,
- bpe, 1,
- 0);
+ if (i == JS_CLASS_UINT8_ARRAY) {
+ obj = JS_NewCConstructor(ctx, i, name,
+ ft.generic, 3, JS_CFUNC_constructor_magic, i,
+ typed_array_base_func,
+ js_uint8array_funcs, countof(js_uint8array_funcs),
+ js_uint8array_proto_funcs, countof(js_uint8array_proto_funcs),
+ 0);
+ } else {
+ const JSCFunctionListEntry *bpe = js_typed_array_funcs + typed_array_size_log2(i);
+ obj = JS_NewCConstructor(ctx, i, name,
+ ft.generic, 3, JS_CFUNC_constructor_magic, i,
+ typed_array_base_func,
+ bpe, 1,
+ bpe, 1,
+ 0);
+ }
if (JS_IsException(obj)) {
fail:
JS_FreeValue(ctx, typed_array_base_func);
diff --git a/src/couch_quickjs/quickjs/quickjs.h b/src/couch_quickjs/quickjs/quickjs.h
index 92cc000d0..57574e6b8 100644
--- a/src/couch_quickjs/quickjs/quickjs.h
+++ b/src/couch_quickjs/quickjs/quickjs.h
@@ -95,6 +95,7 @@ enum {
/* any larger tag is FLOAT64 if JS_NAN_BOXING */
};
+/* must match the layout of 'JSMallocBlockHeader' */
typedef struct JSRefCountHeader {
int ref_count;
} JSRefCountHeader;
@@ -675,10 +676,16 @@ JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char
JSValue JS_ThrowOutOfMemory(JSContext *ctx);
void __JS_FreeValue(JSContext *ctx, JSValue v);
+
+static inline JSRefCountHeader *__js_rc(void *ptr)
+{
+ return (JSRefCountHeader *)((uint32_t *)ptr - 1);
+}
+
static inline void JS_FreeValue(JSContext *ctx, JSValue v)
{
if (JS_VALUE_HAS_REF_COUNT(v)) {
- JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
+ JSRefCountHeader *p = __js_rc(JS_VALUE_GET_PTR(v));
if (--p->ref_count <= 0) {
__JS_FreeValue(ctx, v);
}
@@ -688,7 +695,7 @@ 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);
+ JSRefCountHeader *p = __js_rc(JS_VALUE_GET_PTR(v));
if (--p->ref_count <= 0) {
__JS_FreeValueRT(rt, v);
}
@@ -698,7 +705,7 @@ static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v)
static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v)
{
if (JS_VALUE_HAS_REF_COUNT(v)) {
- JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
+ JSRefCountHeader *p = __js_rc(JS_VALUE_GET_PTR(v));
p->ref_count++;
}
return (JSValue)v;
@@ -707,7 +714,7 @@ static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v)
static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v)
{
if (JS_VALUE_HAS_REF_COUNT(v)) {
- JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
+ JSRefCountHeader *p = __js_rc(JS_VALUE_GET_PTR(v));
p->ref_count++;
}
return (JSValue)v;
diff --git a/src/couch_quickjs/quickjs/test262.conf b/src/couch_quickjs/quickjs/test262.conf
index ab8b0a5bd..8fe86cc6f 100644
--- a/src/couch_quickjs/quickjs/test262.conf
+++ b/src/couch_quickjs/quickjs/test262.conf
@@ -236,7 +236,7 @@ u180e
Uint16Array
Uint32Array
Uint8Array
-uint8array-base64=skip
+uint8array-base64
Uint8ClampedArray
upsert
WeakMap
diff --git a/src/couch_quickjs/quickjs/test262_errors.txt b/src/couch_quickjs/quickjs/test262_errors.txt
index 4d6d0bd3e..dd1cceea6 100644
--- a/src/couch_quickjs/quickjs/test262_errors.txt
+++ b/src/couch_quickjs/quickjs/test262_errors.txt
@@ -33,8 +33,6 @@ test262/test/staging/sm/Function/function-name-for.js:13: Test262Error: Expected
test262/test/staging/sm/Function/implicit-this-in-parameter-expression.js:12: Test262Error: Expected SameValue(«[object Object]», «undefined») to be true
test262/test/staging/sm/Function/invalid-parameter-list.js:13: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
test262/test/staging/sm/Function/invalid-parameter-list.js:13: strict mode: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
-test262/test/staging/sm/TypedArray/prototype-constructor-identity.js:17: Test262Error: Expected SameValue(«2», «6») to be true
-test262/test/staging/sm/TypedArray/prototype-constructor-identity.js:17: strict mode: Test262Error: Expected SameValue(«2», «6») to be true
test262/test/staging/sm/async-functions/async-contains-unicode-escape.js:11: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
test262/test/staging/sm/async-functions/async-contains-unicode-escape.js:11: strict mode: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
test262/test/staging/sm/async-functions/await-in-arrow-parameters.js:10: Test262Error: AsyncFunction:(a = (b = await/r/g) => {}) => {} Expected a SyntaxError to be thrown but no exception was thrown at all