This commit is contained in:
2026-02-01 18:59:52 -06:00
parent 6150406905
commit e53f55fb23
2 changed files with 90 additions and 139 deletions

View File

@@ -102,6 +102,15 @@
/* test the GC by forcing it before each object allocation */
// #define FORCE_GC_AT_MALLOC
/* Forward declarations for heap object types */
typedef struct JSArray JSArray;
typedef struct JSBlob JSBlob;
typedef struct JSText JSText;
typedef struct JSRecord JSRecord;
typedef struct JSFunction JSFunction;
typedef struct JSFrame JSFrame;
typedef struct JSCode JSCode;
static inline JS_BOOL JS_VALUE_IS_TEXT (JSValue v) {
int tag = JS_VALUE_GET_TAG (v);
return tag == JS_TAG_STRING || tag == JS_TAG_STRING_IMM;
@@ -216,7 +225,7 @@ JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref)
{
ref->prev = ctx->top_gc_ref;
ctx->top_gc_ref = ref;
ref->val = JS_UNDEFINED;
ref->val = JS_NULL;
return &ref->val;
}
@@ -230,7 +239,7 @@ JSValue *JS_AddGCRef(JSContext *ctx, JSGCRef *ref)
{
ref->prev = ctx->last_gc_ref;
ctx->last_gc_ref = ref;
ref->val = JS_UNDEFINED;
ref->val = JS_NULL;
return &ref->val;
}
@@ -488,7 +497,7 @@ typedef struct slot {
typedef struct JSRecord {
objhdr_t hdr;
JSRecord *proto;
struct JSRecord *proto;
word_t length;
slot slots[]; // slot[0] is never used
// slot[0].key first 32 bits = class id
@@ -510,7 +519,7 @@ typedef struct JSFrame {
JSValue vars[]; // var[0] is this
} JSFrame;
typedef strut JSCode {
typedef struct JSCode {
objhdr_t hdr;
word_t arity;
word_t size; // capacity of an activation from that will execute
@@ -2781,16 +2790,6 @@ static JSValue js_new_string8 (JSContext *ctx, const char *buf) {
return js_new_string8_len (ctx, buf, strlen (buf));
}
static JSValue js_new_string16_len (JSContext *ctx, const uint16_t *buf, int len) {
JSText *str;
int i;
str = js_alloc_string (ctx, len);
if (!str) return JS_EXCEPTION;
for (i = 0; i < len; i++)
string_put (str, i, buf[i]);
return JS_MKPTR(str);
}
static JSValue js_sub_string (JSContext *ctx, JSText *p, int start, int end) {
int i;
int len = end - start;
@@ -2813,11 +2812,6 @@ static JSText *pretext_init(JSContext *ctx, int capacity) {
return s;
}
/* Free a pretext on error paths */
static void pretext_free(JSContext *ctx, JSText *s) {
if (s) js_free(ctx, s);
}
/* Reallocate a pretext to hold new_len characters */
static no_inline JSText *pretext_realloc(JSContext *ctx, JSText *s, int new_len) {
if (new_len > JS_STRING_LEN_MAX) {
@@ -2849,21 +2843,8 @@ static no_inline JSText *pretext_putc_slow(JSContext *ctx, JSText *s, uint32_t c
return s;
}
/* 0 <= c <= 0xff */
static JSText *pretext_putc8(JSContext *ctx, JSText *s, uint32_t c) {
int len = (int)s->length;
int cap = (int)objhdr_cap56(s->hdr);
if (unlikely(len >= cap)) {
s = pretext_realloc(ctx, s, len + 1);
if (!s) return NULL;
}
string_put(s, len, c);
s->length++;
return s;
}
/* 0 <= c <= 0xffff */
static JSText *pretext_putc16(JSContext *ctx, JSText *s, uint32_t c) {
/* 0 <= c <= 0x10ffff */
static JSText *pretext_putc(JSContext *ctx, JSText *s, uint32_t c) {
int len = (int)s->length;
int cap = (int)objhdr_cap56(s->hdr);
if (likely(len < cap)) {
@@ -2874,17 +2855,6 @@ static JSText *pretext_putc16(JSContext *ctx, JSText *s, uint32_t c) {
return pretext_putc_slow(ctx, s, c);
}
/* 0 <= c <= 0x10ffff */
static JSText *pretext_putc(JSContext *ctx, JSText *s, uint32_t c) {
if (unlikely(c >= 0x10000)) {
/* surrogate pair */
s = pretext_putc16(ctx, s, get_hi_surrogate(c));
if (!s) return NULL;
c = get_lo_surrogate(c);
}
return pretext_putc16(ctx, s, c);
}
static JSText *pretext_write32(JSContext *ctx, JSText *s, const uint32_t *p, int len) {
int cur_len = (int)s->length;
int cap = (int)objhdr_cap56(s->hdr);
@@ -2899,19 +2869,8 @@ static JSText *pretext_write32(JSContext *ctx, JSText *s, const uint32_t *p, int
return s;
}
static int string_getc(const JSText *p, int *pidx) {
int idx, c, c1;
idx = *pidx;
c = string_get(p, idx++);
if (is_hi_surrogate(c) && idx < (int)JSText_len(p)) {
c1 = string_get(p, idx);
if (is_lo_surrogate(c1)) {
c = from_surrogate(c, c1);
idx++;
}
}
*pidx = idx;
return c;
static inline int string_getc(const JSText *p, int *pidx) {
return string_get(p, (*pidx)++);
}
static JSText *pretext_write8(JSContext *ctx, JSText *s, const uint8_t *p, int len) {
@@ -2928,19 +2887,6 @@ static JSText *pretext_write8(JSContext *ctx, JSText *s, const uint8_t *p, int l
return s;
}
static JSText *pretext_write16(JSContext *ctx, JSText *s, const uint16_t *p, int len) {
int cur_len = (int)s->length;
int cap = (int)objhdr_cap56(s->hdr);
if (cur_len + len > cap) {
s = pretext_realloc(ctx, s, cur_len + len);
if (!s) return NULL;
}
for (int i = 0; i < len; i++) {
string_put(s, cur_len + i, p[i]);
}
s->length += len;
return s;
}
/* appending an ASCII string */
static JSText *pretext_puts8(JSContext *ctx, JSText *s, const char *str) {
@@ -3033,29 +2979,62 @@ static JSValue pretext_end(JSContext *ctx, JSText *s) {
return JS_MKPTR(s);
}
/* Count leading ASCII characters in buffer */
static size_t count_ascii (const uint8_t *buf, size_t len) {
size_t i;
for (i = 0; i < len && buf[i] < 128; i++)
;
return i;
}
/* create a string from a UTF-8 buffer */
JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) {
if (buf_len > JS_STRING_LEN_MAX)
return JS_ThrowInternalError(ctx, "string too long");
/* Try immediate ASCII first (<=7 ASCII chars) */
if (buf_len <= MIST_ASCII_MAX_LEN) {
JSValue ret = MIST_TryNewImmediateASCII(buf, buf_len);
if (!JS_IsNull(ret)) return ret;
}
JSText *str = js_alloc_string(ctx, (int)buf_len);
/* Count actual codepoints for allocation */
const uint8_t *p = (const uint8_t *)buf;
const uint8_t *end = p + buf_len;
int codepoint_count = 0;
while (p < end) {
if (*p < 128) {
p++;
codepoint_count++;
} else {
const uint8_t *next;
int c = unicode_from_utf8(p, (int)(end - p), &next);
if (c < 0) {
/* Invalid UTF-8 byte, treat as single byte */
p++;
} else {
p = next;
}
codepoint_count++;
}
}
JSText *str = js_alloc_string(ctx, codepoint_count);
if (!str) return JS_ThrowMemoryError(ctx);
for (size_t i = 0; i < buf_len; i++)
string_put(str, (int)i, (unsigned char)buf[i]);
str->length = buf_len;
/* Decode UTF-8 to UTF-32 */
p = (const uint8_t *)buf;
int i = 0;
while (p < end) {
uint32_t c;
if (*p < 128) {
c = *p++;
} else {
const uint8_t *next;
int decoded = unicode_from_utf8(p, (int)(end - p), &next);
if (decoded < 0) {
/* Invalid UTF-8 byte, use replacement char or the byte itself */
c = *p++;
} else {
c = (uint32_t)decoded;
p = next;
}
}
string_put(str, i++, c);
}
str->length = codepoint_count;
return pretext_end(ctx, str);
}
@@ -5988,7 +5967,7 @@ static JSValue JS_ToQuotedString(JSContext *ctx, JSValue val1) {
b = pretext_init(ctx, (int)JSText_len(p) + 2);
if (!b) goto fail;
b = pretext_putc8(ctx, b, '\"');
b = pretext_putc(ctx, b, '\"');
if (!b) goto fail;
for (i = 0; i < (int)JSText_len(p);) {
c = string_getc(p, &i);
@@ -6011,9 +5990,9 @@ static JSValue JS_ToQuotedString(JSContext *ctx, JSValue val1) {
case '\"':
case '\\':
quote:
b = pretext_putc8(ctx, b, '\\');
b = pretext_putc(ctx, b, '\\');
if (!b) goto fail;
b = pretext_putc8(ctx, b, c);
b = pretext_putc(ctx, b, c);
if (!b) goto fail;
break;
default:
@@ -6028,7 +6007,7 @@ static JSValue JS_ToQuotedString(JSContext *ctx, JSValue val1) {
break;
}
}
b = pretext_putc8(ctx, b, '\"');
b = pretext_putc(ctx, b, '\"');
if (!b) goto fail;
JS_FreeValue(ctx, val);
return pretext_end(ctx, b);
@@ -9274,17 +9253,12 @@ restart:
JSValue v = sp[i - n];
JSValue s = js_cell_text(ctx, JS_NULL, 1, &v);
if (JS_IsException(s)) {
pretext_free(ctx, b);
for (int j = 0; j < n; j++)
JS_FreeValue(ctx, sp[j - n]);
sp -= n;
goto exception;
}
b = pretext_concat_value(ctx, b, s);
JS_FreeValue(ctx, s);
if (!b) {
for (int j = 0; j < n; j++)
JS_FreeValue(ctx, sp[j - n]);
sp -= n;
goto exception;
}
@@ -9292,8 +9266,6 @@ restart:
out = pretext_end(ctx, b);
if (JS_IsException(out)) {
for (int j = 0; j < n; j++)
JS_FreeValue(ctx, sp[j - n]);
sp -= n;
goto exception;
}
@@ -9972,7 +9944,7 @@ static __exception int js_parse_template_part(JSParseState *s,
break;
}
if (c == '\\') {
b = pretext_putc8(s->ctx, b, c);
b = pretext_putc(s->ctx, b, c);
if (!b) goto fail;
if (p >= s->buf_end) goto unexpected_eof;
c = *p++;
@@ -10005,7 +9977,6 @@ static __exception int js_parse_template_part(JSParseState *s,
unexpected_eof:
js_parse_error(s, "unexpected end of string");
fail:
pretext_free(s->ctx, b);
return -1;
}
@@ -10129,7 +10100,6 @@ invalid_utf8:
invalid_char:
if (do_throw) js_parse_error(s, "unexpected end of string");
fail:
pretext_free(s->ctx, b);
return -1;
}
@@ -10170,7 +10140,7 @@ static __exception int js_parse_regexp(JSParseState *s) {
/* XXX: incorrect as the first character in a class */
in_class = FALSE;
} else if (c == '\\') {
b = pretext_putc8(s->ctx, b, c);
b = pretext_putc(s->ctx, b, c);
if (!b) goto fail;
c = *p++;
if (c == '\n' || c == '\r')
@@ -10234,8 +10204,6 @@ static __exception int js_parse_regexp(JSParseState *s) {
s->buf_ptr = p;
return 0;
fail:
pretext_free(s->ctx, b);
pretext_free(s->ctx, b2);
return -1;
}
@@ -10969,7 +10937,6 @@ static int json_parse_string(JSParseState *s, const uint8_t **pp, int sep) {
end_of_input:
js_parse_error(s, "Unexpected end of JSON input");
fail:
pretext_free(s->ctx, b);
return -1;
}
@@ -20875,7 +20842,6 @@ fail:
JS_FreeValue(ctx, str_val);
js_free(ctx, capture);
js_free(ctx, utf16_buf);
pretext_free(ctx, b);
return JS_EXCEPTION;
}
@@ -21274,11 +21240,11 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
if (ret < 0) goto exception;
if (ret) {
if (js_get_length64(ctx, &len, val)) goto exception;
jsc->b = pretext_putc8(ctx, jsc->b, '[');
jsc->b = pretext_putc(ctx, jsc->b, '[');
if (!jsc->b) goto exception;
for (i = 0; i < len; i++) {
if (i > 0) {
jsc->b = pretext_putc8(ctx, jsc->b, ',');
jsc->b = pretext_putc(ctx, jsc->b, ',');
if (!jsc->b) goto exception;
}
jsc->b = pretext_concat_value(ctx, jsc->b, sep);
@@ -21296,12 +21262,12 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
if (js_json_to_str(ctx, jsc, val, v, indent1)) goto exception;
}
if (len > 0 && !JS_IsEmptyString(jsc->gap)) {
jsc->b = pretext_putc8(ctx, jsc->b, '\n');
jsc->b = pretext_putc(ctx, jsc->b, '\n');
if (!jsc->b) goto exception;
jsc->b = pretext_concat_value(ctx, jsc->b, indent);
if (!jsc->b) goto exception;
}
jsc->b = pretext_putc8(ctx, jsc->b, ']');
jsc->b = pretext_putc(ctx, jsc->b, ']');
if (!jsc->b) goto exception;
} else {
if (!JS_IsNull(jsc->property_list))
@@ -21310,7 +21276,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
tab = JS_GetOwnPropertyNames(ctx, val);
if (JS_IsException(tab)) goto exception;
if (js_get_length64(ctx, &len, tab)) goto exception;
jsc->b = pretext_putc8(ctx, jsc->b, '{');
jsc->b = pretext_putc(ctx, jsc->b, '{');
if (!jsc->b) goto exception;
has_content = FALSE;
for (i = 0; i < len; i++) {
@@ -21323,7 +21289,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
if (JS_IsException(v)) goto exception;
if (!JS_IsNull(v)) {
if (has_content) {
jsc->b = pretext_putc8(ctx, jsc->b, ',');
jsc->b = pretext_putc(ctx, jsc->b, ',');
if (!jsc->b) goto exception;
}
prop = JS_ToQuotedStringFree(ctx, prop);
@@ -21335,7 +21301,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
if (!jsc->b) goto exception;
jsc->b = pretext_concat_value(ctx, jsc->b, prop);
if (!jsc->b) goto exception;
jsc->b = pretext_putc8(ctx, jsc->b, ':');
jsc->b = pretext_putc(ctx, jsc->b, ':');
if (!jsc->b) goto exception;
jsc->b = pretext_concat_value(ctx, jsc->b, sep1);
if (!jsc->b) goto exception;
@@ -21344,12 +21310,12 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
}
}
if (has_content && !JS_IsEmptyString(jsc->gap)) {
jsc->b = pretext_putc8(ctx, jsc->b, '\n');
jsc->b = pretext_putc(ctx, jsc->b, '\n');
if (!jsc->b) goto exception;
jsc->b = pretext_concat_value(ctx, jsc->b, indent);
if (!jsc->b) goto exception;
}
jsc->b = pretext_putc8(ctx, jsc->b, '}');
jsc->b = pretext_putc(ctx, jsc->b, '}');
if (!jsc->b) goto exception;
}
if (check_exception_free(ctx, js_cell_pop(ctx, jsc->stack, 0, NULL)))
@@ -21489,7 +21455,6 @@ JSValue JS_JSONStringify(JSContext *ctx, JSValue obj, JSValue replacer,
exception:
ret = JS_EXCEPTION;
done1:
pretext_free(ctx, jsc->b);
done:
JS_FreeValue(ctx, wrapper);
JS_FreeValue(ctx, jsc->empty);
@@ -22235,18 +22200,13 @@ static JSValue js_cell_character (JSContext *ctx, JSValue this_val, int argc,
if (codepoint < 0x80) {
char buf[2] = { (char)codepoint, '\0' };
return JS_NewString (ctx, buf);
} else if (codepoint <= 0xFFFF) {
uint16_t buf[2] = { (uint16_t)codepoint, 0 };
return js_new_string16_len (ctx, buf, 1);
} else {
/* Encode as surrogate pair */
codepoint -= 0x10000;
uint16_t buf[3];
buf[0] = 0xD800 + (codepoint >> 10);
buf[1] = 0xDC00 + (codepoint & 0x3FF);
buf[2] = 0;
return js_new_string16_len (ctx, buf, 2);
}
/* Create single-codepoint UTF-32 string */
JSText *str = js_alloc_string (ctx, 1);
if (!str) return JS_EXCEPTION;
string_put (str, 0, codepoint);
str->length = 1;
return pretext_end (ctx, str);
}
/* Handle float - convert to integer if non-negative and within range */
@@ -22259,18 +22219,13 @@ static JSValue js_cell_character (JSContext *ctx, JSValue this_val, int argc,
if (codepoint < 0x80) {
char buf[2] = { (char)codepoint, '\0' };
return JS_NewString (ctx, buf);
} else if (codepoint <= 0xFFFF) {
uint16_t buf[2] = { (uint16_t)codepoint, 0 };
return js_new_string16_len (ctx, buf, 1);
} else {
/* Encode as surrogate pair */
codepoint -= 0x10000;
uint16_t buf[3];
buf[0] = 0xD800 + (codepoint >> 10);
buf[1] = 0xDC00 + (codepoint & 0x3FF);
buf[2] = 0;
return js_new_string16_len (ctx, buf, 2);
}
/* Create single-codepoint UTF-32 string */
JSText *str = js_alloc_string (ctx, 1);
if (!str) return JS_EXCEPTION;
string_put (str, 0, codepoint);
str->length = 1;
return pretext_end (ctx, str);
}
return JS_NewString (ctx, "");
@@ -22481,7 +22436,6 @@ static JSValue js_cell_text (JSContext *ctx, JSValue this_val, int argc,
if (!JS_VALUE_IS_TEXT(item)) {
JS_FreeValue(ctx, item);
pretext_free(ctx, b);
if (sep_alloc) JS_FreeCString(ctx, separator);
return JS_ThrowTypeError(ctx, "text: array element is not a string");
}
@@ -22498,7 +22452,6 @@ static JSValue js_cell_text (JSContext *ctx, JSValue this_val, int argc,
return pretext_end(ctx, b);
array_fail:
pretext_free(ctx, b);
if (sep_alloc) JS_FreeCString(ctx, separator);
return JS_EXCEPTION;
}
@@ -22872,7 +22825,6 @@ static JSValue js_cell_text_replace(JSContext *ctx, JSValue this_val,
return pretext_end(ctx, b);
fail_str_target:
pretext_free(ctx, b);
return JS_EXCEPTION;
}
@@ -22991,7 +22943,6 @@ static JSValue js_cell_text_replace(JSContext *ctx, JSValue this_val,
return pretext_end(ctx, b);
fail_rx:
pretext_free(ctx, b);
if (!JS_IsNull(orig_last_index) && !JS_IsException(orig_last_index)) {
JS_SetPropertyStr(ctx, rx, "lastIndex", orig_last_index);
} else {

View File

@@ -91,7 +91,7 @@ static inline int objhdr_s (objhdr_t h) { return (h & OBJHDR_S_MASK) != 0; }
#define JS_BOOL int
typedef struct JSRuntime JSRuntime; // the entire VM
typedef struct JSContext JSContext; // Each actor - has its own GC
typedef struct JSContext JSContext; // Each actor
typedef struct JSClass JSClass;
typedef uint32_t JSClassID;