Merge branch 'master' into fix_aot
This commit is contained in:
@@ -131,12 +131,16 @@ JSC_CCALL(actor_removetimer,
|
||||
)
|
||||
|
||||
/* Log callback bridge: called from JS_Log, calls ƿit log(channel, [msg, stack])
|
||||
Captures the register VM stack trace so the log system can show the real error site. */
|
||||
Captures the register VM stack trace so the log system can show the real error site.
|
||||
Uses a re-entrancy guard to prevent infinite recursion when JS_Call triggers
|
||||
js_poll_interrupts -> JS_RaiseDisrupt -> JS_Log -> js_log_callback -> ... */
|
||||
static int js_log_reentrancy = 0;
|
||||
static void js_log_callback(JSContext *ctx, const char *channel, const char *msg) {
|
||||
if (JS_IsNull(ctx->log_callback_js)) {
|
||||
if (JS_IsNull(ctx->log_callback_js) || js_log_reentrancy) {
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
return;
|
||||
}
|
||||
js_log_reentrancy = 1;
|
||||
JS_FRAME(ctx);
|
||||
JS_ROOT(stack, JS_GetStack(ctx));
|
||||
JS_ROOT(args_array, JS_NewArray(ctx));
|
||||
@@ -147,6 +151,7 @@ static void js_log_callback(JSContext *ctx, const char *channel, const char *msg
|
||||
argv[1] = args_array.val;
|
||||
JS_Call(ctx, ctx->log_callback_js, JS_NULL, 2, argv);
|
||||
JS_RestoreFrame(ctx, _js_gc_frame, _js_local_frame);
|
||||
js_log_reentrancy = 0;
|
||||
}
|
||||
|
||||
JSC_CCALL(actor_set_log,
|
||||
|
||||
@@ -230,8 +230,6 @@ static inline JS_BOOL JS_VALUE_IS_NUMBER (JSValue v) {
|
||||
/* JS_IsPretext, JS_KeyGetStr, JS_PushGCRef, JS_PopGCRef, JS_AddGCRef, JS_DeleteGCRef
|
||||
are defined after JSContext (they need its fields) */
|
||||
|
||||
/* Forward declarations for memory functions (now declared in quickjs.h) */
|
||||
void *js_realloc (JSContext *ctx, void *ptr, size_t size);
|
||||
|
||||
/* Forward declaration for string_get */
|
||||
static inline int string_get (const JSText *p, int idx);
|
||||
@@ -921,8 +919,9 @@ typedef struct JSBlob {
|
||||
} JSBlob;
|
||||
|
||||
typedef struct JSText {
|
||||
objhdr_t hdr; /* mist header */
|
||||
word_t length; /* length (or hash for stoned text) */
|
||||
objhdr_t hdr; /* mist header — cap56 = allocated capacity */
|
||||
word_t length; /* character count (always) */
|
||||
word_t hash; /* cached hash (stoned text only) */
|
||||
word_t packed[]; /* two chars per packed word */
|
||||
} JSText;
|
||||
|
||||
@@ -1044,7 +1043,6 @@ static inline uint64_t fash64_hash_one (uint64_t word) {
|
||||
}
|
||||
|
||||
static inline word_t JSText_len (const JSText *text) {
|
||||
if (objhdr_s (text->hdr)) return objhdr_cap56 (text->hdr);
|
||||
return text->length;
|
||||
}
|
||||
|
||||
@@ -1587,7 +1585,6 @@ JSText *pretext_putc (JSContext *ctx, JSText *s, uint32_t c);
|
||||
JSText *pretext_concat_value (JSContext *ctx, JSText *s, JSValue v);
|
||||
JSValue js_new_blob (JSContext *ctx, blob *b);
|
||||
/* Functions from header region (defined in runtime.c) */
|
||||
void *js_realloc (JSContext *ctx, void *ptr, size_t size);
|
||||
void *ct_alloc (JSContext *ctx, size_t bytes, size_t align);
|
||||
void ct_free_all (JSContext *ctx);
|
||||
int ct_resize (JSContext *ctx);
|
||||
@@ -1639,13 +1636,6 @@ static inline int to_digit (int c) {
|
||||
else return 36;
|
||||
}
|
||||
|
||||
no_inline int js_realloc_array (JSContext *ctx, void **parray, int elem_size, int *psize, int req_size);
|
||||
static inline int js_resize_array (JSContext *ctx, void **parray, int elem_size, int *psize, int req_size) {
|
||||
if (unlikely (req_size > *psize))
|
||||
return js_realloc_array (ctx, parray, elem_size, psize, req_size);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSText *js_alloc_string (JSContext *ctx, int max_len);
|
||||
JSValue js_key_from_string (JSContext *ctx, JSValue val);
|
||||
|
||||
@@ -587,6 +587,11 @@ JSValue __js_printf_like (2, 3)
|
||||
|
||||
/* Log to "memory" channel + disrupt. Skips JS callback (can't allocate). */
|
||||
JSValue JS_RaiseOOM (JSContext *ctx);
|
||||
#define JS_ThrowOutOfMemory JS_RaiseOOM
|
||||
#define JS_ThrowReferenceError JS_RaiseDisrupt
|
||||
#define JS_ThrowTypeError JS_RaiseDisrupt
|
||||
#define JS_ThrowInternalError JS_RaiseDisrupt
|
||||
#define JS_ThrowRangeError JS_RaiseDisrupt
|
||||
|
||||
/* ============================================================
|
||||
8. Function Creation and Invocation
|
||||
@@ -1010,9 +1015,6 @@ void *js_debugger_val_address (JSContext *js, JSValue val);
|
||||
============================================================ */
|
||||
void *js_malloc (JSContext *ctx, size_t size);
|
||||
void *js_mallocz (JSContext *ctx, size_t size);
|
||||
void *js_realloc (JSContext *ctx, void *ptr, size_t size);
|
||||
void js_free (JSContext *ctx, void *ptr);
|
||||
char *js_strdup (JSContext *ctx, const char *str);
|
||||
|
||||
/* Runtime-level memory functions */
|
||||
void *js_malloc_rt (size_t size);
|
||||
|
||||
446
source/runtime.c
446
source/runtime.c
@@ -110,16 +110,16 @@ JS_BOOL JS_IsFrame(JSValue v) {
|
||||
}
|
||||
|
||||
uint64_t get_text_hash (JSText *text) {
|
||||
uint64_t len = objhdr_cap56 (text->hdr);
|
||||
uint64_t len = text->length;
|
||||
size_t word_count = (len + 1) / 2;
|
||||
|
||||
if (objhdr_s (text->hdr)) {
|
||||
/* Stoned text: check for cached hash */
|
||||
if (text->length != 0) return text->length;
|
||||
/* Compute and cache hash using content length from header */
|
||||
text->length = fash64_hash_words (text->packed, word_count, len);
|
||||
if (!text->length) text->length = 1;
|
||||
return text->length;
|
||||
if (text->hash != 0) return text->hash;
|
||||
/* Compute and cache hash */
|
||||
text->hash = fash64_hash_words (text->packed, word_count, len);
|
||||
if (!text->hash) text->hash = 1;
|
||||
return text->hash;
|
||||
} else {
|
||||
/* Pre-text: compute hash on the fly */
|
||||
return fash64_hash_words (text->packed, word_count, len);
|
||||
@@ -137,7 +137,7 @@ void pack_utf32_to_words (const uint32_t *utf32, uint32_t len, uint64_t *packed)
|
||||
|
||||
/* Compare two packed UTF-32 texts for equality */
|
||||
int text_equal (JSText *a, const uint64_t *packed_b, uint32_t len_b) {
|
||||
uint32_t len_a = (uint32_t)objhdr_cap56 (a->hdr);
|
||||
uint32_t len_a = (uint32_t)a->length;
|
||||
if (len_a != len_b) return 0;
|
||||
size_t word_count = (len_a + 1) / 2;
|
||||
return memcmp (a->packed, packed_b, word_count * sizeof (uint64_t)) == 0;
|
||||
@@ -150,6 +150,7 @@ int JS_IsPretext (JSValue v) {
|
||||
}
|
||||
|
||||
JSValue *JS_PushGCRef (JSContext *ctx, JSGCRef *ref) {
|
||||
assert(ref != ctx->top_gc_ref && "JS_ROOT used in a loop — same address pushed twice");
|
||||
ref->prev = ctx->top_gc_ref;
|
||||
ctx->top_gc_ref = ref;
|
||||
ref->val = JS_NULL;
|
||||
@@ -157,11 +158,13 @@ JSValue *JS_PushGCRef (JSContext *ctx, JSGCRef *ref) {
|
||||
}
|
||||
|
||||
JSValue JS_PopGCRef (JSContext *ctx, JSGCRef *ref) {
|
||||
assert(ctx->top_gc_ref == ref && "JS_PopGCRef: not popping top of stack — mismatched push/pop");
|
||||
ctx->top_gc_ref = ref->prev;
|
||||
return ref->val;
|
||||
}
|
||||
|
||||
JSValue *JS_AddGCRef (JSContext *ctx, JSGCRef *ref) {
|
||||
assert(ref != ctx->last_gc_ref && "JS_AddGCRef: same address added twice — cycle in GC ref list");
|
||||
ref->prev = ctx->last_gc_ref;
|
||||
ctx->last_gc_ref = ref;
|
||||
ref->val = JS_NULL;
|
||||
@@ -193,6 +196,7 @@ JSLocalRef *JS_GetLocalFrame (JSContext *ctx) {
|
||||
}
|
||||
|
||||
void JS_PushLocalRef (JSContext *ctx, JSLocalRef *ref) {
|
||||
assert(ref != ctx->top_local_ref && "JS_LOCAL used in a loop — same address pushed twice");
|
||||
ref->prev = ctx->top_local_ref;
|
||||
ctx->top_local_ref = ref;
|
||||
}
|
||||
@@ -291,24 +295,6 @@ int ct_resize (JSContext *ctx) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *js_realloc (JSContext *ctx, void *ptr, size_t size) {
|
||||
void *new_ptr;
|
||||
|
||||
/* Align size to 8 bytes */
|
||||
size = (size + 7) & ~7;
|
||||
|
||||
if (!ptr) {
|
||||
/* New allocation */
|
||||
new_ptr = js_malloc (ctx, size);
|
||||
if (!new_ptr) return NULL;
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
/* Bump allocator: just allocate new space.
|
||||
Caller is responsible for protecting ptr and copying data. */
|
||||
new_ptr = js_malloc (ctx, size);
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
JSValue intern_text_to_value (JSContext *ctx, const uint32_t *utf32, uint32_t len) {
|
||||
/* Pack UTF-32 for hashing and comparison */
|
||||
@@ -349,7 +335,8 @@ JSValue intern_text_to_value (JSContext *ctx, const uint32_t *utf32, uint32_t le
|
||||
|
||||
/* Initialize the text */
|
||||
text->hdr = objhdr_make (len, OBJ_TEXT, false, false, false, true); /* s=1 for stoned */
|
||||
text->length = hash; /* Store hash in length field for stoned text */
|
||||
text->length = len;
|
||||
text->hash = hash;
|
||||
memcpy (text->packed, packed, word_count * sizeof (uint64_t));
|
||||
|
||||
/* Add to intern table */
|
||||
@@ -583,6 +570,8 @@ JSValue rec_get (JSContext *ctx, JSRecord *rec, JSValue k) {
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
size_t gc_object_size (void *ptr); /* forward declaration for growth-forward size storage */
|
||||
|
||||
int rec_resize (JSContext *ctx, JSValue *pobj, uint64_t new_mask) {
|
||||
/* Protect the source object with a GC ref in case js_malloc triggers GC */
|
||||
JSGCRef obj_ref;
|
||||
@@ -646,7 +635,9 @@ int rec_resize (JSContext *ctx, JSValue *pobj, uint64_t new_mask) {
|
||||
}
|
||||
|
||||
/* Install forward header at old location so stale references can find the new record */
|
||||
size_t old_size = gc_object_size (rec);
|
||||
rec->mist_hdr = objhdr_make_fwd (new_rec);
|
||||
*((size_t *)((uint8_t *)rec + sizeof (objhdr_t))) = old_size;
|
||||
|
||||
/* Update caller's JSValue to point to new record */
|
||||
*pobj = JS_MKPTR (new_rec);
|
||||
@@ -865,11 +856,6 @@ void *js_mallocz (JSContext *ctx, size_t size) {
|
||||
return memset (ptr, 0, size);
|
||||
}
|
||||
|
||||
void js_free (JSContext *ctx, void *ptr) {
|
||||
/* Bump allocator doesn't free individual allocations - GC handles it */
|
||||
(void)ctx;
|
||||
(void)ptr;
|
||||
}
|
||||
|
||||
/* Parser memory functions - use system allocator to avoid GC issues */
|
||||
|
||||
@@ -930,48 +916,14 @@ JSValue ppretext_end (JSContext *ctx, PPretext *p) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
string_put (str, i, p->data[i]);
|
||||
}
|
||||
str->hdr = objhdr_set_cap56 (str->hdr, len);
|
||||
str->length = len;
|
||||
str->hash = 0;
|
||||
str->hdr = objhdr_set_s (str->hdr, true);
|
||||
|
||||
ppretext_free (p);
|
||||
return JS_MKPTR (str);
|
||||
}
|
||||
|
||||
no_inline int js_realloc_array (JSContext *ctx, void **parray, int elem_size, int *psize, int req_size) {
|
||||
int new_size;
|
||||
void *new_array;
|
||||
void *old_array = *parray;
|
||||
int old_size = *psize;
|
||||
|
||||
/* XXX: potential arithmetic overflow */
|
||||
new_size = max_int (req_size, old_size * 3 / 2);
|
||||
|
||||
/* Protect source object with a GC ref before allocating (GC may move it) */
|
||||
JSGCRef src_ref;
|
||||
JS_PushGCRef (ctx, &src_ref);
|
||||
if (old_array) {
|
||||
src_ref.val = JS_MKPTR (old_array);
|
||||
}
|
||||
|
||||
new_array = js_malloc (ctx, new_size * elem_size);
|
||||
if (!new_array) {
|
||||
JS_PopGCRef (ctx, &src_ref);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get possibly-moved source pointer after GC */
|
||||
if (old_array) {
|
||||
old_array = (void *)chase (src_ref.val);
|
||||
memcpy (new_array, old_array, old_size * elem_size);
|
||||
}
|
||||
JS_PopGCRef (ctx, &src_ref);
|
||||
|
||||
*psize = new_size;
|
||||
*parray = new_array;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Append a JSValue string to a PPretext (parser pretext) */
|
||||
PPretext *ppretext_append_jsvalue (PPretext *p, JSValue str) {
|
||||
@@ -1386,6 +1338,7 @@ JSValue gc_copy_value (JSContext *ctx, JSValue v, uint8_t *from_base, uint8_t *f
|
||||
*to_free += size;
|
||||
|
||||
*hdr_ptr = objhdr_make_fwd (new_ptr);
|
||||
*((size_t *)((uint8_t *)hdr_ptr + sizeof (objhdr_t))) = size;
|
||||
|
||||
return JS_MKPTR (new_ptr);
|
||||
}
|
||||
@@ -1737,6 +1690,70 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Finalize garbage records that have class finalizers */
|
||||
{
|
||||
uint8_t *p = from_base;
|
||||
uint8_t *prev_p = NULL;
|
||||
size_t prev_size = 0;
|
||||
uint8_t prev_type = 0;
|
||||
while (p < from_end) {
|
||||
objhdr_t hdr = *(objhdr_t *)p;
|
||||
uint8_t type = objhdr_type (hdr);
|
||||
size_t size;
|
||||
if (type == OBJ_FORWARD) {
|
||||
size = *((size_t *)(p + sizeof (objhdr_t)));
|
||||
if (size == 0 || size > (size_t)(from_end - from_base) || (size & 7) != 0) {
|
||||
uint64_t *w = (uint64_t *)p;
|
||||
fprintf (stderr, "gc_finalize_walk: bad fwd size=%zu at p=%p prev_p=%p prev_type=%d prev_size=%zu\n"
|
||||
" words: [0]=0x%llx [1]=0x%llx [2]=0x%llx [3]=0x%llx\n"
|
||||
" prev words: [0]=0x%llx [1]=0x%llx [2]=0x%llx [3]=0x%llx\n",
|
||||
size, (void *)p, (void *)prev_p, prev_type, prev_size,
|
||||
(unsigned long long)w[0], (unsigned long long)w[1],
|
||||
(unsigned long long)w[2], (unsigned long long)w[3],
|
||||
prev_p ? ((unsigned long long *)prev_p)[0] : 0,
|
||||
prev_p ? ((unsigned long long *)prev_p)[1] : 0,
|
||||
prev_p ? ((unsigned long long *)prev_p)[2] : 0,
|
||||
prev_p ? ((unsigned long long *)prev_p)[3] : 0);
|
||||
break;
|
||||
}
|
||||
} else if (type != OBJ_ARRAY && type != OBJ_BLOB && type != OBJ_TEXT &&
|
||||
type != OBJ_RECORD && type != OBJ_FUNCTION && type != OBJ_FRAME) {
|
||||
uint64_t *w = (uint64_t *)p;
|
||||
fprintf (stderr, "gc_finalize_walk: bad type=%d at p=%p hdr=0x%llx prev_p=%p prev_type=%d prev_size=%zu\n"
|
||||
" words: [0]=0x%llx [1]=0x%llx [2]=0x%llx [3]=0x%llx\n"
|
||||
" prev words: [0]=0x%llx [1]=0x%llx [2]=0x%llx [3]=0x%llx\n",
|
||||
type, (void *)p, (unsigned long long)hdr, (void *)prev_p, prev_type, prev_size,
|
||||
(unsigned long long)w[0], (unsigned long long)w[1],
|
||||
(unsigned long long)w[2], (unsigned long long)w[3],
|
||||
prev_p ? ((unsigned long long *)prev_p)[0] : 0,
|
||||
prev_p ? ((unsigned long long *)prev_p)[1] : 0,
|
||||
prev_p ? ((unsigned long long *)prev_p)[2] : 0,
|
||||
prev_p ? ((unsigned long long *)prev_p)[3] : 0);
|
||||
break;
|
||||
} else {
|
||||
size = gc_object_size (p);
|
||||
if (type == OBJ_RECORD) {
|
||||
JSRecord *rec = (JSRecord *)p;
|
||||
uint32_t class_id = REC_GET_CLASS_ID (rec);
|
||||
if (class_id != 0 && (int)class_id < ctx->class_count) {
|
||||
JSClassFinalizer *fn = ctx->class_array[class_id].finalizer;
|
||||
if (fn) {
|
||||
#ifdef DUMP_GC_FINALIZER
|
||||
fprintf (stderr, "gc_finalize: class_id=%u name=%s rec=%p\n",
|
||||
class_id, ctx->class_array[class_id].class_name, (void *)rec);
|
||||
#endif
|
||||
fn (rt, JS_MKPTR (rec));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prev_p = p;
|
||||
prev_type = type;
|
||||
prev_size = size;
|
||||
p += size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return old block (in poison mode, just poison it and leak) */
|
||||
heap_block_free (rt, from_base, old_heap_size);
|
||||
|
||||
@@ -1911,7 +1928,8 @@ JSText *js_alloc_string (JSContext *ctx, int max_len) {
|
||||
}
|
||||
/* Initialize objhdr_t with OBJ_TEXT type and capacity in cap56 */
|
||||
str->hdr = objhdr_make (max_len, OBJ_TEXT, false, false, false, false);
|
||||
str->length = 0; /* length starts at 0, capacity is in hdr */
|
||||
str->length = 0;
|
||||
str->hash = 0;
|
||||
/* Zero packed data so odd-length strings have deterministic padding.
|
||||
js_malloc is a bump allocator and does not zero memory; without this,
|
||||
the last word's unused low 32 bits contain garbage, causing
|
||||
@@ -2088,6 +2106,37 @@ void JS_FreeContext (JSContext *ctx) {
|
||||
|
||||
for (i = 0; i < ctx->class_count; i++) {
|
||||
}
|
||||
|
||||
/* Finalize all remaining records with class finalizers before teardown */
|
||||
if (ctx->heap_base) {
|
||||
uint8_t *p = ctx->heap_base;
|
||||
while (p < ctx->heap_free) {
|
||||
objhdr_t hdr = *(objhdr_t *)p;
|
||||
uint8_t type = objhdr_type (hdr);
|
||||
size_t size;
|
||||
if (type == OBJ_FORWARD) {
|
||||
size = *((size_t *)(p + sizeof (objhdr_t)));
|
||||
} else {
|
||||
size = gc_object_size (p);
|
||||
if (type == OBJ_RECORD) {
|
||||
JSRecord *rec = (JSRecord *)p;
|
||||
uint32_t class_id = REC_GET_CLASS_ID (rec);
|
||||
if (class_id != 0 && (int)class_id < ctx->class_count) {
|
||||
JSClassFinalizer *fn = ctx->class_array[class_id].finalizer;
|
||||
if (fn) {
|
||||
#ifdef DUMP_GC_FINALIZER
|
||||
fprintf (stderr, "teardown_finalize: class_id=%u name=%s rec=%p\n",
|
||||
class_id, ctx->class_array[class_id].class_name, (void *)rec);
|
||||
#endif
|
||||
fn (rt, JS_MKPTR (rec));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p += size;
|
||||
}
|
||||
}
|
||||
|
||||
js_free_rt (ctx->class_array);
|
||||
js_free_rt (ctx->class_proto);
|
||||
|
||||
@@ -2459,10 +2508,7 @@ JSText *pretext_concat_value (JSContext *ctx, JSText *s, JSValue v) {
|
||||
JSValue pretext_end (JSContext *ctx, JSText *s) {
|
||||
if (!s) return JS_EXCEPTION;
|
||||
int len = (int)s->length;
|
||||
if (len == 0) {
|
||||
js_free (ctx, s);
|
||||
return JS_KEY_empty;
|
||||
}
|
||||
if (len == 0) return JS_KEY_empty;
|
||||
/* Promote short ASCII strings to immediate values */
|
||||
if (len <= MIST_ASCII_MAX_LEN) {
|
||||
char buf[MIST_ASCII_MAX_LEN];
|
||||
@@ -2477,9 +2523,8 @@ JSValue pretext_end (JSContext *ctx, JSText *s) {
|
||||
if (!JS_IsNull (imm)) return imm;
|
||||
}
|
||||
}
|
||||
/* Set final length in capacity field and clear length for hash storage */
|
||||
s->hdr = objhdr_set_cap56 (s->hdr, len);
|
||||
s->length = 0;
|
||||
/* length is already set by caller; cap56 stays as allocated capacity */
|
||||
s->hash = 0;
|
||||
s->hdr = objhdr_set_s (s->hdr, true); /* mark as stone */
|
||||
return JS_MKPTR (s);
|
||||
}
|
||||
@@ -2962,7 +3007,9 @@ static int js_array_grow (JSContext *ctx, JSValue *arr_ptr, word_t min_cap) {
|
||||
new_arr->values[i] = JS_NULL;
|
||||
|
||||
/* Install forward header at old location */
|
||||
size_t old_arr_size = gc_object_size (arr);
|
||||
arr->mist_hdr = objhdr_make_fwd (new_arr);
|
||||
*((size_t *)((uint8_t *)arr + sizeof (objhdr_t))) = old_arr_size;
|
||||
|
||||
/* Update the tracked JSValue to point to new array */
|
||||
*arr_ptr = JS_MKPTR (new_arr);
|
||||
@@ -4151,25 +4198,10 @@ static __exception int JS_ToLength (JSContext *ctx, int64_t *plen, JSValue val)
|
||||
}
|
||||
|
||||
static JSValue js_dtoa2 (JSContext *ctx, double d, int radix, int n_digits, int flags) {
|
||||
char static_buf[128], *buf, *tmp_buf;
|
||||
int len, len_max;
|
||||
JSValue res;
|
||||
char buf[1088];
|
||||
JSDTOATempMem dtoa_mem;
|
||||
len_max = js_dtoa_max_len (d, radix, n_digits, flags);
|
||||
|
||||
/* longer buffer may be used if radix != 10 */
|
||||
if (len_max > sizeof (static_buf) - 1) {
|
||||
tmp_buf = js_malloc (ctx, len_max + 1);
|
||||
if (!tmp_buf) return JS_EXCEPTION;
|
||||
buf = tmp_buf;
|
||||
} else {
|
||||
tmp_buf = NULL;
|
||||
buf = static_buf;
|
||||
}
|
||||
len = js_dtoa (buf, d, radix, n_digits, flags, &dtoa_mem);
|
||||
res = js_new_string8_len (ctx, buf, len);
|
||||
js_free (ctx, tmp_buf);
|
||||
return res;
|
||||
int len = js_dtoa (buf, d, radix, n_digits, flags, &dtoa_mem);
|
||||
return js_new_string8_len (ctx, buf, len);
|
||||
}
|
||||
|
||||
JSValue JS_ToString (JSContext *ctx, JSValue val) {
|
||||
@@ -5016,7 +5048,7 @@ JSValue js_compile_regexp (JSContext *ctx, JSValue pattern, JSValue flags) {
|
||||
|
||||
ret
|
||||
= js_new_string8_len (ctx, (const char *)re_bytecode_buf, re_bytecode_len);
|
||||
js_free (ctx, re_bytecode_buf);
|
||||
js_free_rt (re_bytecode_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -5375,8 +5407,8 @@ static JSValue js_regexp_exec (JSContext *ctx, JSValue this_val, int argc, JSVal
|
||||
}
|
||||
for (int ci = 0; ci < imm_len; ci++)
|
||||
string_put (hs, ci, MIST_GetImmediateASCIIChar (str_ref.val, ci));
|
||||
hs->hdr = objhdr_set_cap56 (hs->hdr, imm_len);
|
||||
hs->length = 0;
|
||||
hs->length = imm_len;
|
||||
hs->hash = 0;
|
||||
hs->hdr = objhdr_set_s (hs->hdr, true);
|
||||
str_ref.val = JS_MKPTR (hs);
|
||||
}
|
||||
@@ -6128,12 +6160,7 @@ static JSValue js_cell_number (JSContext *ctx, JSValue this_val, int argc, JSVal
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
char *clean = js_malloc (ctx, strlen (str) + 1);
|
||||
if (!clean) {
|
||||
JS_FreeCString (ctx, format);
|
||||
JS_FreeCString (ctx, str);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
char *clean = alloca (strlen (str) + 1);
|
||||
|
||||
const char *p = str;
|
||||
char *q = clean;
|
||||
@@ -6174,7 +6201,6 @@ static JSValue js_cell_number (JSContext *ctx, JSValue this_val, int argc, JSVal
|
||||
*q = '\0';
|
||||
char *endptr;
|
||||
long long n = strtoll (str, &endptr, 2);
|
||||
js_free (ctx, clean);
|
||||
JS_FreeCString (ctx, format);
|
||||
JS_FreeCString (ctx, str);
|
||||
if (endptr == str) return JS_NULL;
|
||||
@@ -6183,7 +6209,6 @@ static JSValue js_cell_number (JSContext *ctx, JSValue this_val, int argc, JSVal
|
||||
*q = '\0';
|
||||
char *endptr;
|
||||
long long n = strtoll (str, &endptr, 8);
|
||||
js_free (ctx, clean);
|
||||
JS_FreeCString (ctx, format);
|
||||
JS_FreeCString (ctx, str);
|
||||
if (endptr == str) return JS_NULL;
|
||||
@@ -6192,7 +6217,6 @@ static JSValue js_cell_number (JSContext *ctx, JSValue this_val, int argc, JSVal
|
||||
*q = '\0';
|
||||
char *endptr;
|
||||
long long n = strtoll (str, &endptr, 16);
|
||||
js_free (ctx, clean);
|
||||
JS_FreeCString (ctx, format);
|
||||
JS_FreeCString (ctx, str);
|
||||
if (endptr == str) return JS_NULL;
|
||||
@@ -6201,14 +6225,12 @@ static JSValue js_cell_number (JSContext *ctx, JSValue this_val, int argc, JSVal
|
||||
*q = '\0';
|
||||
char *endptr;
|
||||
long long n = strtoll (str, &endptr, 32);
|
||||
js_free (ctx, clean);
|
||||
JS_FreeCString (ctx, format);
|
||||
JS_FreeCString (ctx, str);
|
||||
if (endptr == str) return JS_NULL;
|
||||
return JS_NewInt64 (ctx, n);
|
||||
} else if (strcmp (format, "j") == 0) {
|
||||
/* JavaScript style prefix */
|
||||
js_free (ctx, clean);
|
||||
JS_FreeCString (ctx, format);
|
||||
int radix = 10;
|
||||
const char *start = str;
|
||||
@@ -6240,7 +6262,6 @@ static JSValue js_cell_number (JSContext *ctx, JSValue this_val, int argc, JSVal
|
||||
*q = '\0';
|
||||
|
||||
double d = strtod (clean, NULL);
|
||||
js_free (ctx, clean);
|
||||
JS_FreeCString (ctx, format);
|
||||
JS_FreeCString (ctx, str);
|
||||
if (isnan (d)) return JS_NULL;
|
||||
@@ -6443,13 +6464,9 @@ static JSValue js_cell_number_to_radix_string (JSContext *ctx, double num, int r
|
||||
return JS_NewString (ctx, result);
|
||||
}
|
||||
|
||||
/* Helper: add separator every n digits from right */
|
||||
static char *add_separator (JSContext *ctx, const char *str, char sep, int n) {
|
||||
if (n <= 0) {
|
||||
char *result = js_malloc (ctx, strlen (str) + 1);
|
||||
if (result) strcpy (result, str);
|
||||
return result;
|
||||
}
|
||||
/* Helper: add separator every n digits from right, returns JSValue string */
|
||||
static JSValue add_separator (JSContext *ctx, const char *str, char sep, int n, int prepend_neg) {
|
||||
if (n <= 0) return JS_NewString (ctx, str);
|
||||
|
||||
int negative = (str[0] == '-');
|
||||
const char *start = negative ? str + 1 : str;
|
||||
@@ -6457,34 +6474,35 @@ static char *add_separator (JSContext *ctx, const char *str, char sep, int n) {
|
||||
/* Find decimal point */
|
||||
const char *decimal = strchr (start, '.');
|
||||
int int_len = decimal ? (int)(decimal - start) : (int)strlen (start);
|
||||
int dec_len = decimal ? (int)strlen (decimal) : 0;
|
||||
|
||||
int num_seps = (int_len - 1) / n;
|
||||
int result_len = strlen (str) + num_seps + 1;
|
||||
char *result = js_malloc (ctx, result_len);
|
||||
if (!result) return NULL;
|
||||
int total = (negative ? 1 : 0) + prepend_neg + int_len + num_seps + dec_len;
|
||||
|
||||
char *q = result;
|
||||
if (negative) *q++ = '-';
|
||||
JSText *pt = pretext_init (ctx, total);
|
||||
if (!pt) return JS_EXCEPTION;
|
||||
|
||||
int pos = 0;
|
||||
if (prepend_neg) string_put (pt, pos++, '-');
|
||||
if (negative) string_put (pt, pos++, '-');
|
||||
|
||||
int count = int_len % n;
|
||||
if (count == 0) count = n;
|
||||
|
||||
for (int i = 0; i < int_len; i++) {
|
||||
if (i > 0 && count == 0) {
|
||||
*q++ = sep;
|
||||
string_put (pt, pos++, (uint32_t)sep);
|
||||
count = n;
|
||||
}
|
||||
*q++ = start[i];
|
||||
string_put (pt, pos++, (uint32_t)(unsigned char)start[i]);
|
||||
count--;
|
||||
}
|
||||
|
||||
if (decimal) {
|
||||
strcpy (q, decimal);
|
||||
} else {
|
||||
*q = '\0';
|
||||
}
|
||||
for (int i = 0; i < dec_len; i++)
|
||||
string_put (pt, pos++, (uint32_t)(unsigned char)decimal[i]);
|
||||
|
||||
return result;
|
||||
pt->length = pos;
|
||||
return pretext_end (ctx, pt);
|
||||
}
|
||||
|
||||
/* Helper: format number with format string */
|
||||
@@ -6522,7 +6540,6 @@ static JSValue js_cell_format_number (JSContext *ctx, double num, const char *fo
|
||||
if (format[i] != '\0') return JS_NULL;
|
||||
|
||||
char buf[128];
|
||||
char *result_str = NULL;
|
||||
|
||||
switch (style) {
|
||||
case 'e': {
|
||||
@@ -6548,22 +6565,13 @@ static JSValue js_cell_format_number (JSContext *ctx, double num, const char *fo
|
||||
/* Space separated */
|
||||
if (separation == 0) separation = 3;
|
||||
snprintf (buf, sizeof (buf), "%.*f", places, num);
|
||||
result_str = add_separator (ctx, buf, ' ', separation);
|
||||
if (!result_str) return JS_EXCEPTION;
|
||||
JSValue ret = JS_NewString (ctx, result_str);
|
||||
js_free (ctx, result_str);
|
||||
return ret;
|
||||
return add_separator (ctx, buf, ' ', separation, 0);
|
||||
}
|
||||
case 'u': {
|
||||
/* Underbar separated */
|
||||
snprintf (buf, sizeof (buf), "%.*f", places, num);
|
||||
if (separation > 0) {
|
||||
result_str = add_separator (ctx, buf, '_', separation);
|
||||
if (!result_str) return JS_EXCEPTION;
|
||||
JSValue ret = JS_NewString (ctx, result_str);
|
||||
js_free (ctx, result_str);
|
||||
return ret;
|
||||
}
|
||||
if (separation > 0)
|
||||
return add_separator (ctx, buf, '_', separation, 0);
|
||||
return JS_NewString (ctx, buf);
|
||||
}
|
||||
case 'd':
|
||||
@@ -6572,11 +6580,7 @@ static JSValue js_cell_format_number (JSContext *ctx, double num, const char *fo
|
||||
if (separation == 0) separation = 3;
|
||||
if (places == 0 && style == 'd') places = 2;
|
||||
snprintf (buf, sizeof (buf), "%.*f", places, num);
|
||||
result_str = add_separator (ctx, buf, ',', separation);
|
||||
if (!result_str) return JS_EXCEPTION;
|
||||
JSValue ret = JS_NewString (ctx, result_str);
|
||||
js_free (ctx, result_str);
|
||||
return ret;
|
||||
return add_separator (ctx, buf, ',', separation, 0);
|
||||
}
|
||||
case 'v': {
|
||||
/* European style: comma decimal, period separator */
|
||||
@@ -6585,13 +6589,8 @@ static JSValue js_cell_format_number (JSContext *ctx, double num, const char *fo
|
||||
for (char *p = buf; *p; p++) {
|
||||
if (*p == '.') *p = ',';
|
||||
}
|
||||
if (separation > 0) {
|
||||
result_str = add_separator (ctx, buf, '.', separation);
|
||||
if (!result_str) return JS_EXCEPTION;
|
||||
JSValue ret = JS_NewString (ctx, result_str);
|
||||
js_free (ctx, result_str);
|
||||
return ret;
|
||||
}
|
||||
if (separation > 0)
|
||||
return add_separator (ctx, buf, '.', separation, 0);
|
||||
return JS_NewString (ctx, buf);
|
||||
}
|
||||
case 'i': {
|
||||
@@ -6607,26 +6606,8 @@ static JSValue js_cell_format_number (JSContext *ctx, double num, const char *fo
|
||||
memmove (buf + (places - len), buf, len + 1);
|
||||
memset (buf, '0', places - len);
|
||||
}
|
||||
if (separation > 0) {
|
||||
result_str = add_separator (ctx, buf, '_', separation);
|
||||
if (!result_str) return JS_EXCEPTION;
|
||||
if (neg) {
|
||||
char *final = js_malloc (ctx, strlen (result_str) + 2);
|
||||
if (!final) {
|
||||
js_free (ctx, result_str);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
final[0] = '-';
|
||||
strcpy (final + 1, result_str);
|
||||
js_free (ctx, result_str);
|
||||
JSValue ret = JS_NewString (ctx, final);
|
||||
js_free (ctx, final);
|
||||
return ret;
|
||||
}
|
||||
JSValue ret = JS_NewString (ctx, result_str);
|
||||
js_free (ctx, result_str);
|
||||
return ret;
|
||||
}
|
||||
if (separation > 0)
|
||||
return add_separator (ctx, buf, '_', separation, neg);
|
||||
if (neg) {
|
||||
memmove (buf + 1, buf, strlen (buf) + 1);
|
||||
buf[0] = '-';
|
||||
@@ -6848,74 +6829,85 @@ static JSValue js_cell_text (JSContext *ctx, JSValue this_val, int argc, JSValue
|
||||
|
||||
if (format == 'h') {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
char *result = js_malloc (ctx, byte_len * 2 + 1);
|
||||
if (!result) return JS_EXCEPTION;
|
||||
int exact = (int)(byte_len * 2);
|
||||
JSText *pt = pretext_init (ctx, exact);
|
||||
if (!pt) return JS_EXCEPTION;
|
||||
bd = (JSBlob *)chase (argv[0]);
|
||||
data = (const uint8_t *)bd->bits;
|
||||
for (size_t i = 0; i < byte_len; i++) {
|
||||
result[i * 2] = hex[(data[i] >> 4) & 0xF];
|
||||
result[i * 2 + 1] = hex[data[i] & 0xF];
|
||||
string_put (pt, (int)(i * 2), (uint32_t)hex[(data[i] >> 4) & 0xF]);
|
||||
string_put (pt, (int)(i * 2 + 1), (uint32_t)hex[data[i] & 0xF]);
|
||||
}
|
||||
result[byte_len * 2] = '\0';
|
||||
JSValue ret = JS_NewString (ctx, result);
|
||||
js_free (ctx, result);
|
||||
return ret;
|
||||
pt->length = exact;
|
||||
return pretext_end (ctx, pt);
|
||||
} else if (format == 'b') {
|
||||
char *result = js_malloc (ctx, bd->length + 1);
|
||||
if (!result) return JS_EXCEPTION;
|
||||
int exact = (int)bd->length;
|
||||
JSText *pt = pretext_init (ctx, exact);
|
||||
if (!pt) return JS_EXCEPTION;
|
||||
bd = (JSBlob *)chase (argv[0]);
|
||||
data = (const uint8_t *)bd->bits;
|
||||
for (size_t i = 0; i < (size_t)bd->length; i++) {
|
||||
size_t byte_idx = i / 8;
|
||||
size_t bit_idx = i % 8;
|
||||
result[i] = (data[byte_idx] & (1u << bit_idx)) ? '1' : '0';
|
||||
string_put (pt, (int)i,
|
||||
(data[byte_idx] & (1u << bit_idx)) ? '1' : '0');
|
||||
}
|
||||
result[bd->length] = '\0';
|
||||
JSValue ret = JS_NewString (ctx, result);
|
||||
js_free (ctx, result);
|
||||
return ret;
|
||||
pt->length = exact;
|
||||
return pretext_end (ctx, pt);
|
||||
} else if (format == 'o') {
|
||||
size_t octal_len = ((size_t)bd->length + 2) / 3;
|
||||
char *result = js_malloc (ctx, octal_len + 1);
|
||||
if (!result) return JS_EXCEPTION;
|
||||
for (size_t i = 0; i < octal_len; i++) {
|
||||
int exact = (int)(((size_t)bd->length + 2) / 3);
|
||||
JSText *pt = pretext_init (ctx, exact);
|
||||
if (!pt) return JS_EXCEPTION;
|
||||
bd = (JSBlob *)chase (argv[0]);
|
||||
data = (const uint8_t *)bd->bits;
|
||||
for (int i = 0; i < exact; i++) {
|
||||
int val = 0;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
size_t bit_pos = i * 3 + (size_t)j;
|
||||
size_t bit_pos = (size_t)i * 3 + (size_t)j;
|
||||
if (bit_pos < (size_t)bd->length) {
|
||||
size_t byte_idx = bit_pos / 8;
|
||||
size_t bit_idx = bit_pos % 8;
|
||||
if (data[byte_idx] & (1u << bit_idx)) val |= (1 << j);
|
||||
}
|
||||
}
|
||||
result[i] = (char)('0' + val);
|
||||
string_put (pt, i, (uint32_t)('0' + val));
|
||||
}
|
||||
result[octal_len] = '\0';
|
||||
JSValue ret = JS_NewString (ctx, result);
|
||||
js_free (ctx, result);
|
||||
return ret;
|
||||
pt->length = exact;
|
||||
return pretext_end (ctx, pt);
|
||||
} else if (format == 't') {
|
||||
static const char b32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
size_t b32_len = ((size_t)bd->length + 4) / 5;
|
||||
char *result = js_malloc (ctx, b32_len + 1);
|
||||
if (!result) return JS_EXCEPTION;
|
||||
for (size_t i = 0; i < b32_len; i++) {
|
||||
int exact = (int)(((size_t)bd->length + 4) / 5);
|
||||
JSText *pt = pretext_init (ctx, exact);
|
||||
if (!pt) return JS_EXCEPTION;
|
||||
bd = (JSBlob *)chase (argv[0]);
|
||||
data = (const uint8_t *)bd->bits;
|
||||
for (int i = 0; i < exact; i++) {
|
||||
int val = 0;
|
||||
for (int j = 0; j < 5; j++) {
|
||||
size_t bit_pos = i * 5 + (size_t)j;
|
||||
size_t bit_pos = (size_t)i * 5 + (size_t)j;
|
||||
if (bit_pos < (size_t)bd->length) {
|
||||
size_t byte_idx = bit_pos / 8;
|
||||
size_t bit_idx = bit_pos % 8;
|
||||
if (data[byte_idx] & (1u << bit_idx)) val |= (1 << j);
|
||||
}
|
||||
}
|
||||
result[i] = b32[val & 31];
|
||||
string_put (pt, i, (uint32_t)b32[val & 31]);
|
||||
}
|
||||
result[b32_len] = '\0';
|
||||
JSValue ret = JS_NewString (ctx, result);
|
||||
js_free (ctx, result);
|
||||
return ret;
|
||||
pt->length = exact;
|
||||
return pretext_end (ctx, pt);
|
||||
} else {
|
||||
if (bd->length % 8 != 0)
|
||||
return JS_RaiseDisrupt (ctx,
|
||||
"text: blob not byte-aligned for UTF-8");
|
||||
return JS_NewStringLen (ctx, (const char *)data, byte_len);
|
||||
/* Copy blob data to a temp buffer before JS_NewStringLen, because
|
||||
JS_NewStringLen allocates internally (js_alloc_string) which can
|
||||
trigger GC, moving the blob and invalidating data. */
|
||||
char *tmp = pjs_malloc (byte_len);
|
||||
if (!tmp) return JS_ThrowMemoryError (ctx);
|
||||
memcpy (tmp, data, byte_len);
|
||||
JSValue result = JS_NewStringLen (ctx, tmp, byte_len);
|
||||
pjs_free (tmp);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7020,32 +7012,28 @@ static JSValue js_cell_text (JSContext *ctx, JSValue this_val, int argc, JSValue
|
||||
|
||||
const char *pref = "function ";
|
||||
const char *suff = "() {\n [native code]\n}";
|
||||
const char *name = "";
|
||||
const char *name_cstr = NULL;
|
||||
int nlen = 0;
|
||||
|
||||
if (!JS_IsNull (fn->name)) {
|
||||
name_cstr = JS_ToCString (ctx, fn->name);
|
||||
if (name_cstr) name = name_cstr;
|
||||
if (name_cstr) nlen = (int)strlen (name_cstr);
|
||||
}
|
||||
|
||||
size_t plen = strlen (pref);
|
||||
size_t nlen = strlen (name);
|
||||
size_t slen = strlen (suff);
|
||||
int plen = (int)strlen (pref);
|
||||
int slen = (int)strlen (suff);
|
||||
|
||||
char *result = js_malloc (ctx, plen + nlen + slen + 1);
|
||||
if (!result) {
|
||||
JSText *pt = pretext_init (ctx, plen + nlen + slen);
|
||||
if (!pt) {
|
||||
if (name_cstr) JS_FreeCString (ctx, name_cstr);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
memcpy (result, pref, plen);
|
||||
memcpy (result + plen, name, nlen);
|
||||
memcpy (result + plen + nlen, suff, slen + 1);
|
||||
|
||||
JSValue ret = JS_NewString (ctx, result);
|
||||
js_free (ctx, result);
|
||||
pt = pretext_puts8 (ctx, pt, pref);
|
||||
if (pt && name_cstr) pt = pretext_write8 (ctx, pt, (const uint8_t *)name_cstr, nlen);
|
||||
if (name_cstr) JS_FreeCString (ctx, name_cstr);
|
||||
return ret;
|
||||
if (pt) pt = pretext_puts8 (ctx, pt, suff);
|
||||
return pretext_end (ctx, pt);
|
||||
}
|
||||
|
||||
return JS_ToString (ctx, arg);
|
||||
@@ -9005,7 +8993,6 @@ static JSValue js_cell_array_sort (JSContext *ctx, JSValue this_val, int argc, J
|
||||
if (str_keys) {
|
||||
for (word_t j = 0; j < i; j++)
|
||||
JS_FreeCString (ctx, str_keys[j]);
|
||||
js_free (ctx, str_keys);
|
||||
}
|
||||
JS_PopGCRef (ctx, &result_ref);
|
||||
JS_PopGCRef (ctx, &arr_ref);
|
||||
@@ -9321,18 +9308,13 @@ static JSValue js_cell_fn_apply (JSContext *ctx, JSValue this_val, int argc, JSV
|
||||
if (len == 0)
|
||||
return JS_CallInternal (ctx, argv[0], JS_NULL, 0, NULL, 0);
|
||||
|
||||
JSValue *args = js_malloc (ctx, sizeof (JSValue) * len);
|
||||
if (!args) return JS_EXCEPTION;
|
||||
arr = JS_VALUE_GET_ARRAY (argv[1]); /* re-chase after malloc */
|
||||
JSValue *args = alloca (sizeof (JSValue) * len);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
args[i] = arr->values[i];
|
||||
}
|
||||
|
||||
JSValue result = JS_CallInternal (ctx, argv[0], JS_NULL, len, args, 0);
|
||||
|
||||
js_free (ctx, args);
|
||||
return result;
|
||||
return JS_CallInternal (ctx, argv[0], JS_NULL, len, args, 0);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
@@ -9390,7 +9372,9 @@ static int blob_grow (JSContext *ctx, JSValue *pblob, size_t need_bits) {
|
||||
memcpy (nb->bits, old->bits, old_words * sizeof (word_t));
|
||||
|
||||
/* Install forward pointer at old location */
|
||||
size_t old_blob_size = gc_object_size (old);
|
||||
old->mist_hdr = objhdr_make_fwd (nb);
|
||||
*((size_t *)((uint8_t *)old + sizeof (objhdr_t))) = old_blob_size;
|
||||
|
||||
/* Update caller's JSValue */
|
||||
*pblob = JS_MKPTR (nb);
|
||||
|
||||
Reference in New Issue
Block a user