update text and object to be gc aware

This commit is contained in:
2026-02-02 22:37:42 -06:00
parent bdf0461e1f
commit ddbdd00496

View File

@@ -284,13 +284,13 @@ typedef enum JSErrorEnum {
struct JSFunctionBytecode;
#define JS_VALUE_GET_ARRAY(v) ((JSArray *)chase (v))
#define JS_VALUE_GET_OBJ(v) ((JSRecord *)JS_VALUE_GET_PTR (v))
#define JS_VALUE_GET_TEXT(v) ((JSText *)JS_VALUE_GET_PTR (v))
#define JS_VALUE_GET_OBJ(v) ((JSRecord *)chase (v))
#define JS_VALUE_GET_TEXT(v) ((JSText *)chase (v))
#define JS_VALUE_GET_BLOB(v) ((JSBlob *)JS_VALUE_GET_PTR (v))
#define JS_VALUE_GET_FUNCTION(v) ((JSFunction *)JS_VALUE_GET_PTR (v))
#define JS_VALUE_GET_FRAME(v) ((JSFrame *)JS_VALUE_GET_PTR (v))
#define JS_VALUE_GET_CODE(v) ((JSFunctionBytecode *)JS_VALUE_GET_PTR (v))
#define JS_VALUE_GET_STRING(v) ((JSText *)JS_VALUE_GET_PTR (v))
#define JS_VALUE_GET_STRING(v) ((JSText *)chase (v))
/* Compatibility: JS_TAG_STRING is an alias for text type checks */
#define JS_TAG_STRING JS_TAG_STRING_IMM
@@ -588,10 +588,12 @@ typedef struct JSCode {
/* ============================================================
Record key helpers (JSValue keys)
============================================================ */
/* GC-SAFE: No allocations. */
static inline JS_BOOL rec_key_is_empty (JSValue key) {
return JS_IsNull (key);
}
/* GC-SAFE: No allocations. */
static inline JS_BOOL rec_key_is_tomb (JSValue key) {
return JS_IsException (key);
}
@@ -1206,7 +1208,7 @@ typedef struct JSRegExp {
#define obj_is_stone(rec) objhdr_s ((rec)->mist_hdr)
#define obj_set_stone(rec) ((rec)->mist_hdr = objhdr_set_s ((rec)->mist_hdr, true))
#define JS_VALUE_GET_RECORD(v) ((JSRecord *)JS_VALUE_GET_PTR (v))
#define JS_VALUE_GET_RECORD(v) ((JSRecord *)chase (v))
/* Get prototype from object (works for both JSRecord and JSRecord since they
* share layout) */
@@ -1317,7 +1319,8 @@ static JS_BOOL js_key_equal_str (JSValue a, const char *str) {
return TRUE;
}
/* Find slot for a key in record's own table.
/* GC-SAFE: No allocations. Caller must pass freshly-chased rec.
Find slot for a key in record's own table.
Returns slot index (>0) if found, or -(insert_slot) if not found. */
static int rec_find_slot (JSRecord *rec, JSValue k) {
uint64_t mask = objhdr_cap56 (rec->mist_hdr);
@@ -1350,7 +1353,8 @@ static int rec_find_slot (JSRecord *rec, JSValue k) {
return first_tomb ? -(int)first_tomb : -1;
}
/* Get own property from record, returns JS_NULL if not found */
/* GC-SAFE: No allocations. Caller must pass freshly-chased rec.
Get own property from record, returns JS_NULL if not found */
static JSValue rec_get_own (JSContext *ctx, JSRecord *rec, JSValue k) {
(void)ctx;
@@ -1361,7 +1365,8 @@ static JSValue rec_get_own (JSContext *ctx, JSRecord *rec, JSValue k) {
return JS_NULL;
}
/* Get property with proto chain lookup */
/* GC-SAFE: No allocations. Walks prototype chain via direct pointers.
Caller must pass freshly-chased rec. */
static JSValue rec_get (JSContext *ctx, JSRecord *rec, JSValue k) {
if (rec_key_is_empty (k) || rec_key_is_tomb (k)) return JS_NULL;
@@ -1385,7 +1390,9 @@ static int rec_resize (JSContext *ctx, JSRecord *rec, uint64_t new_mask) {
return -1;
}
/* Set own property on record, returns 0 on success, -1 on error */
/* NOT GC-SAFE: May call rec_resize which allocates.
Caller must pass freshly-chased rec and handle potential staleness.
Currently safe because rec_resize always fails. */
static int rec_set_own (JSContext *ctx, JSRecord *rec, JSValue k, JSValue val) {
if (rec_key_is_empty (k) || rec_key_is_tomb (k)) {
return -1;
@@ -1683,7 +1690,7 @@ static void mark_function_children_decref (JSRuntime *rt, JSFunction *func);
static inline objhdr_t objhdr_set_cap56 (objhdr_t h, uint64_t cap);
/* JS_VALUE_GET_STRING is an alias for getting JSText from a string value */
#define JS_VALUE_GET_STRING(v) ((JSText *)JS_VALUE_GET_PTR(v))
/* Note: Uses chase() for GC safety - already defined at line 293 */
/* JS_ThrowMemoryError is an alias for JS_ThrowOutOfMemory */
#define JS_ThrowMemoryError(ctx) JS_ThrowOutOfMemory(ctx)
@@ -1859,7 +1866,7 @@ static inline uint32_t js_string_value_get (JSValue v, int idx) {
if (MIST_IsImmediateASCII (v)) {
return MIST_GetImmediateASCIIChar (v, idx);
} else {
JSText *s = JS_VALUE_GET_PTR (v);
JSText *s = JS_VALUE_GET_TEXT (v);
return string_get (s, idx);
}
}
@@ -1869,7 +1876,7 @@ static inline int js_string_value_len (JSValue v) {
if (MIST_IsImmediateASCII (v)) {
return MIST_GetImmediateASCIILen (v);
} else {
return JSText_len (JS_VALUE_GET_PTR (v));
return JSText_len (JS_VALUE_GET_TEXT (v));
}
}
@@ -1881,7 +1888,7 @@ static JSValue js_key_from_string (JSContext *ctx, JSValue val) {
return val; /* Immediates can be used directly as keys */
}
if (JS_IsText (val)) {
JSText *p = JS_VALUE_GET_PTR (val);
JSText *p = JS_VALUE_GET_TEXT (val);
int64_t len = p->length;
/* Extract UTF-32 characters and intern */
uint32_t *utf32_buf = alloca (len * sizeof (uint32_t));
@@ -4008,32 +4015,42 @@ JSValue JS_GetProperty (JSContext *ctx, JSValue obj, JSValue prop) {
return rec_get (ctx, rec, prop);
}
/* Get own property names from an object.
/* GC-SAFE: Collects keys to stack buffer before any allocation.
Returns a JSValue array of text keys. */
JSValue JS_GetOwnPropertyNames (JSContext *ctx, JSValue obj) {
JSRecord *rec;
uint32_t mask, count, i;
JSValue arr;
if (!JS_IsRecord (obj)) {
JS_ThrowTypeErrorNotAnObject (ctx);
return JS_EXCEPTION;
}
rec = (JSRecord *)JS_VALUE_GET_OBJ (obj);
arr = JS_NewArray (ctx);
if (JS_IsException (arr)) return JS_EXCEPTION;
/* Reading slots is GC-safe - no allocation */
JSRecord *rec = JS_VALUE_GET_OBJ (obj);
mask = (uint32_t)objhdr_cap56 (rec->mist_hdr);
count = 0;
/* Add string keys to array */
/* Count text keys first */
count = 0;
for (i = 1; i <= mask; i++) {
if (JS_IsText (rec->slots[i].key)) count++;
}
if (count == 0) return JS_NewArrayLen (ctx, 0);
/* Collect keys into stack buffer (JSValues are just uint64_t) */
JSValue *keys = alloca (count * sizeof (JSValue));
uint32_t idx = 0;
for (i = 1; i <= mask; i++) {
JSValue k = rec->slots[i].key;
if (!rec_key_is_empty (k) && !rec_key_is_tomb (k) && JS_IsText (k)) {
JS_SetPropertyUint32 (ctx, arr, count, k);
count++;
}
if (JS_IsText (k)) keys[idx++] = k;
}
/* Now allocate and fill - GC point, but keys are on stack */
JSValue arr = JS_NewArrayLen (ctx, count);
if (JS_IsException (arr)) return JS_EXCEPTION;
for (i = 0; i < count; i++) {
JS_SetPropertyUint32 (ctx, arr, i, keys[i]);
}
return arr;
@@ -4066,19 +4083,17 @@ int JS_GetOwnProperty (JSContext *ctx, JSValue *desc, JSValue obj, JSValue prop)
return JS_GetOwnPropertyInternal (ctx, desc, JS_VALUE_GET_OBJ (obj), prop);
}
/* return -1 if exception otherwise TRUE or FALSE */
/* GC-SAFE: Only calls rec_find_slot and reads prototype pointers.
return -1 if exception otherwise TRUE or FALSE */
int JS_HasProperty (JSContext *ctx, JSValue obj, JSValue prop) {
JSRecord *p;
int ret;
if (unlikely (!JS_IsRecord (obj))) return FALSE;
p = JS_VALUE_GET_OBJ (obj);
for (;;) {
/* JS_GetOwnPropertyInternal can free the prototype */
JS_MKPTR (p);
ret = JS_GetOwnPropertyInternal (ctx, NULL, p, prop);
/* JS_FreeValue removed - copying GC handles memory */
if (ret != 0) return ret;
p = JS_OBJ_GET_PROTO (p);
p = p->proto; /* Direct pointer chase is safe - no allocation */
if (!p) break;
}
return FALSE;
@@ -4336,6 +4351,9 @@ JSValue JS_GetPropertyKey (JSContext *ctx, JSValue this_obj, JSValue key) {
return JS_GetProperty (ctx, this_obj, key);
}
/* CAUTION: rec_set_own is NOT GC-safe if rec_resize is implemented.
Currently safe because rec_resize always fails.
When resize is implemented, rec pointer may become stale. */
int JS_SetPropertyKey (JSContext *ctx, JSValue this_obj, JSValue key, JSValue val) {
if (JS_IsRecord (key)) {
if (!JS_IsRecord (this_obj)) {
@@ -4360,7 +4378,8 @@ int JS_SetPropertyKey (JSContext *ctx, JSValue this_obj, JSValue key, JSValue va
return JS_SetPropertyInternal (ctx, this_obj, key, val);
}
/* Property existence check with JSValue key (supports object keys) */
/* GC-SAFE for record keys (no allocations).
String keys call js_key_from_string then JS_HasProperty which re-chases. */
int JS_HasPropertyKey (JSContext *ctx, JSValue obj, JSValue key) {
if (JS_IsRecord (key)) {
if (!JS_IsRecord (obj)) return FALSE;
@@ -4383,7 +4402,7 @@ int JS_HasPropertyKey (JSContext *ctx, JSValue obj, JSValue key) {
return JS_HasProperty (ctx, obj, key);
}
/* Property deletion with JSValue key (supports object keys) */
/* GC-SAFE: Only calls rec_find_slot and modifies slots directly */
int JS_DeletePropertyKey (JSContext *ctx, JSValue obj, JSValue key) {
if (JS_IsRecord (key)) {
if (!JS_IsRecord (obj)) return FALSE;
@@ -4398,7 +4417,6 @@ int JS_DeletePropertyKey (JSContext *ctx, JSValue obj, JSValue key) {
rec->slots[slot].key = JS_EXCEPTION; /* tombstone */
rec->slots[slot].val = JS_NULL;
rec->len--;
/* tombs tracking removed - not needed with copying GC */
return TRUE;
}
@@ -4459,7 +4477,8 @@ static JSValue JS_ThrowSyntaxErrorVarRedeclaration (JSContext *ctx,
return JS_ThrowSyntaxError (ctx, "redeclaration of '%s'", JS_KeyGetStr (ctx, buf, sizeof (buf), prop));
}
/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */
/* GC-SAFE: Only calls rec_find_slot (no allocations).
flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */
static int JS_CheckDefineGlobalVar (JSContext *ctx, JSValue prop, int flags) {
JSRecord *rec;
int slot;
@@ -4486,7 +4505,9 @@ static int JS_CheckDefineGlobalVar (JSContext *ctx, JSValue prop, int flags) {
return 0;
}
/* Simplified: def_flags is (0, DEFINE_GLOBAL_LEX_VAR) */
/* CAUTION: rec_set_own is NOT GC-safe if rec_resize is implemented.
Currently safe because rec_resize always fails.
def_flags is (0, DEFINE_GLOBAL_LEX_VAR) */
static int JS_DefineGlobalVar (JSContext *ctx, JSValue prop, int def_flags) {
JSRecord *rec;
JSValue val;
@@ -4515,6 +4536,7 @@ static int JS_DefineGlobalFunction (JSContext *ctx, JSValue prop, JSValue func,
return 0;
}
/* GC-SAFE: Only calls rec_find_slot and rec_get (no allocations) */
static JSValue JS_GetGlobalVar (JSContext *ctx, JSValue prop, BOOL throw_ref_error) {
JSRecord *rec;
int slot;
@@ -4535,7 +4557,8 @@ static JSValue JS_GetGlobalVar (JSContext *ctx, JSValue prop, BOOL throw_ref_err
return val;
}
/* construct a reference to a global variable */
/* GC-SAFE: Only calls rec_find_slot and JS_HasProperty (no allocations).
Construct a reference to a global variable */
static int JS_GetGlobalVarRef (JSContext *ctx, JSValue prop, JSValue *sp) {
JSRecord *rec;
int slot;
@@ -4562,7 +4585,8 @@ static int JS_GetGlobalVarRef (JSContext *ctx, JSValue prop, JSValue *sp) {
return 0;
}
/* use for strict variable access: test if the variable exists */
/* GC-SAFE: Only calls rec_find_slot and JS_HasProperty (no allocations).
Use for strict variable access: test if the variable exists */
static int JS_CheckGlobalVar (JSContext *ctx, JSValue prop) {
JSRecord *rec;
int slot, ret;
@@ -21066,11 +21090,27 @@ static JSValue js_cell_text_lower (JSContext *ctx, JSValue this_val, int argc, J
if (argc < 1) return JS_NULL;
if (!JS_VALUE_IS_TEXT (argv[0])) return JS_NULL;
JSText *p = JS_VALUE_GET_STRING (argv[0]);
JSText *b = pretext_init (ctx, (int)JSText_len (p));
/* Handle immediate ASCII - no GC concern */
if (MIST_IsImmediateASCII (argv[0])) {
int len = MIST_GetImmediateASCIILen (argv[0]);
JSText *b = pretext_init (ctx, len);
if (!b) return JS_EXCEPTION;
for (int i = 0; i < len; i++) {
uint32_t c = MIST_GetImmediateASCIIChar (argv[0], i);
if (c >= 'A' && c <= 'Z') c = c - 'A' + 'a';
b = pretext_putc (ctx, b, c);
if (!b) return JS_EXCEPTION;
}
return pretext_end (ctx, b);
}
/* Heap text: must re-chase after GC points */
int len = (int)JSText_len (JS_VALUE_GET_STRING (argv[0]));
JSText *b = pretext_init (ctx, len);
if (!b) return JS_EXCEPTION;
for (int i = 0; i < (int)JSText_len (p); i++) {
for (int i = 0; i < len; i++) {
JSText *p = JS_VALUE_GET_STRING (argv[0]); /* Re-chase each iteration */
uint32_t c = string_get (p, i);
if (c >= 'A' && c <= 'Z') c = c - 'A' + 'a';
b = pretext_putc (ctx, b, c);
@@ -21085,11 +21125,27 @@ static JSValue js_cell_text_upper (JSContext *ctx, JSValue this_val, int argc, J
if (argc < 1) return JS_NULL;
if (!JS_VALUE_IS_TEXT (argv[0])) return JS_NULL;
JSText *p = JS_VALUE_GET_STRING (argv[0]);
JSText *b = pretext_init (ctx, (int)JSText_len (p));
/* Handle immediate ASCII - no GC concern */
if (MIST_IsImmediateASCII (argv[0])) {
int len = MIST_GetImmediateASCIILen (argv[0]);
JSText *b = pretext_init (ctx, len);
if (!b) return JS_EXCEPTION;
for (int i = 0; i < len; i++) {
uint32_t c = MIST_GetImmediateASCIIChar (argv[0], i);
if (c >= 'a' && c <= 'z') c = c - 'a' + 'A';
b = pretext_putc (ctx, b, c);
if (!b) return JS_EXCEPTION;
}
return pretext_end (ctx, b);
}
/* Heap text: must re-chase after GC points */
int len = (int)JSText_len (JS_VALUE_GET_STRING (argv[0]));
JSText *b = pretext_init (ctx, len);
if (!b) return JS_EXCEPTION;
for (int i = 0; i < (int)JSText_len (p); i++) {
for (int i = 0; i < len; i++) {
JSText *p = JS_VALUE_GET_STRING (argv[0]); /* Re-chase each iteration */
uint32_t c = string_get (p, i);
if (c >= 'a' && c <= 'z') c = c - 'a' + 'A';
b = pretext_putc (ctx, b, c);
@@ -21121,6 +21177,9 @@ static JSValue js_cell_text_trim (JSContext *ctx, JSValue this_val, int argc, JS
}
size_t reject_len = strlen (reject);
/* Re-chase p after JS_ToCString which can trigger GC */
p = JS_VALUE_GET_STRING (str);
while (start < end) {
uint32_t c = string_get (p, start);
int found = 0;
@@ -21154,6 +21213,8 @@ static JSValue js_cell_text_trim (JSContext *ctx, JSValue this_val, int argc, JS
end--;
}
/* Re-chase before js_sub_string */
p = JS_VALUE_GET_STRING (str);
JSValue result = js_sub_string (ctx, p, start, end);
/* str removed - arg not owned */
return result;
@@ -21270,8 +21331,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
if (!JS_VALUE_IS_TEXT (argv[0]))
return JS_ThrowInternalError (ctx, "Replace must have text in arg0.");
JSText *sp = JS_VALUE_GET_STRING (argv[0]);
int len = (int)JSText_len (sp);
int len = (int)JSText_len (JS_VALUE_GET_STRING (argv[0]));
int32_t limit = -1;
if (argc > 3 && !JS_IsNull (argv[3])) {
@@ -21287,8 +21347,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
return JS_ThrowInternalError (
ctx, "Second arg of replace must be pattern or text.");
JSText *tp = JS_VALUE_GET_STRING (argv[1]);
int t_len = (int)JSText_len (tp);
int t_len = (int)JSText_len (JS_VALUE_GET_STRING (argv[1]));
if (t_len == 0) {
int32_t count = 0;
@@ -21311,6 +21370,8 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
}
if (boundary < len) {
/* Re-chase sp after GC points */
JSText *sp = JS_VALUE_GET_STRING (argv[0]);
JSValue ch = js_sub_string (ctx, sp, boundary, boundary + 1);
if (JS_IsException (ch)) goto fail_str_target;
b = pretext_concat_value (ctx, b, ch);
@@ -21327,6 +21388,10 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
while (pos <= len - t_len && (limit < 0 || count < limit)) {
int found = -1;
/* Re-chase sp and tp before string_cmp */
JSText *sp = JS_VALUE_GET_STRING (argv[0]);
JSText *tp = JS_VALUE_GET_STRING (argv[1]);
for (int i = pos; i <= len - t_len; i++) {
if (!string_cmp (sp, tp, i, 0, t_len)) {
found = i;
@@ -21336,12 +21401,14 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
if (found < 0) break;
if (found > pos) {
sp = JS_VALUE_GET_STRING (argv[0]); /* Re-chase before js_sub_string */
JSValue sub = js_sub_string (ctx, sp, pos, found);
if (JS_IsException (sub)) goto fail_str_target;
b = pretext_concat_value (ctx, b, sub);
if (!b) goto fail_str_target;
}
sp = JS_VALUE_GET_STRING (argv[0]); /* Re-chase before js_sub_string */
JSValue match = js_sub_string (ctx, sp, found, found + t_len);
if (JS_IsException (match)) goto fail_str_target;
@@ -21360,6 +21427,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
}
if (pos < len) {
JSText *sp = JS_VALUE_GET_STRING (argv[0]); /* Re-chase before js_sub_string */
JSValue sub = js_sub_string (ctx, sp, pos, len);
if (JS_IsException (sub)) goto fail_str_target;
b = pretext_concat_value (ctx, b, sub);
@@ -21385,6 +21453,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
if (JS_SetPropertyStr (ctx, rx, "lastIndex", JS_NewInt32 (ctx, 0)) < 0)
goto fail_rx;
JSText *sp = JS_VALUE_GET_STRING (argv[0]); /* Re-chase before js_sub_string */
JSValue sub_str = js_sub_string (ctx, sp, pos, len);
if (JS_IsException (sub_str)) goto fail_rx;
@@ -21432,6 +21501,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
if (match_len < 0) match_len = 0;
if (found > pos) {
sp = JS_VALUE_GET_STRING (argv[0]); /* Re-chase before js_sub_string */
JSValue prefix = js_sub_string (ctx, sp, pos, found);
if (JS_IsException (prefix)) goto fail_rx;
b = pretext_concat_value (ctx, b, prefix);
@@ -21459,6 +21529,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
}
if (pos < len) {
JSText *sp = JS_VALUE_GET_STRING (argv[0]); /* Re-chase before js_sub_string */
JSValue tail = js_sub_string (ctx, sp, pos, len);
if (JS_IsException (tail)) goto fail_rx;
b = pretext_concat_value (ctx, b, tail);
@@ -21498,8 +21569,7 @@ static JSValue js_cell_text_search (JSContext *ctx, JSValue this_val, int argc,
JSValue str = JS_ToString (ctx, argv[0]);
if (JS_IsException (str)) return str;
JSText *p = JS_VALUE_GET_STRING (str);
int len = (int)JSText_len (p);
int len = (int)JSText_len (JS_VALUE_GET_STRING (str));
int from = 0;
if (argc > 2 && !JS_IsNull (argv[2])) {
@@ -21522,11 +21592,13 @@ static JSValue js_cell_text_search (JSContext *ctx, JSValue this_val, int argc,
return target;
}
JSText *t = JS_VALUE_GET_STRING (target);
int t_len = (int)JSText_len (t);
int t_len = (int)JSText_len (JS_VALUE_GET_STRING (target));
int result = -1;
if (len >= t_len) {
/* Re-chase both p and t right before the loop */
JSText *p = JS_VALUE_GET_STRING (str);
JSText *t = JS_VALUE_GET_STRING (target);
for (int i = from; i <= len - t_len; i++) {
if (!string_cmp (p, t, i, 0, t_len)) {
result = i;
@@ -21553,6 +21625,8 @@ static JSValue js_cell_text_search (JSContext *ctx, JSValue this_val, int argc,
if (JS_SetPropertyStr (ctx, rx, "lastIndex", JS_NewInt32 (ctx, 0)) < 0)
goto fail_rx_search;
/* Re-chase p before js_sub_string */
JSText *p = JS_VALUE_GET_STRING (str);
JSValue sub_str = js_sub_string (ctx, p, from, len);
if (JS_IsException (sub_str)) goto fail_rx_search;
@@ -21633,8 +21707,7 @@ static JSValue js_cell_text_extract (JSContext *ctx, JSValue this_val, int argc,
JSValue str = JS_ToString (ctx, argv[0]);
if (JS_IsException (str)) return JS_EXCEPTION;
JSText *p = JS_VALUE_GET_STRING (str);
int len = (int)JSText_len (p);
int len = (int)JSText_len (JS_VALUE_GET_STRING (str));
int from = 0;
if (argc >= 3 && !JS_IsNull (argv[2])) {
@@ -21668,6 +21741,8 @@ static JSValue js_cell_text_extract (JSContext *ctx, JSValue this_val, int argc,
if (from == 0 && to == len) {
sub_str = str;
} else {
/* Re-chase p before js_sub_string */
JSText *p = JS_VALUE_GET_STRING (str);
sub_str = js_sub_string (ctx, p, from, to);
if (JS_IsException (sub_str)) goto fail_rx;
}
@@ -21733,9 +21808,11 @@ static JSValue js_cell_text_extract (JSContext *ctx, JSValue this_val, int argc,
JSValue needle_val = JS_ToString (ctx, argv[1]);
if (JS_IsException (needle_val)) return JS_EXCEPTION;
JSText *needle = JS_VALUE_GET_STRING (needle_val);
int needle_len = (int)JSText_len (needle);
int needle_len = (int)JSText_len (JS_VALUE_GET_STRING (needle_val));
/* Re-chase both p and needle right before js_str_find_range */
JSText *p = JS_VALUE_GET_STRING (str);
JSText *needle = JS_VALUE_GET_STRING (needle_val);
int pos = js_str_find_range (p, from, to, needle);
if (pos < 0) return JS_NULL;
@@ -21743,6 +21820,8 @@ static JSValue js_cell_text_extract (JSContext *ctx, JSValue this_val, int argc,
JSValue arr = JS_NewArrayLen (ctx, 1);
if (JS_IsException (arr)) return JS_EXCEPTION;
/* Re-chase p before js_sub_string */
p = JS_VALUE_GET_STRING (str);
JSValue match = js_sub_string (ctx, p, pos, pos + needle_len);
if (JS_IsException (match)) {
return JS_EXCEPTION;
@@ -21772,14 +21851,16 @@ static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc,
int is_record = JS_IsRecord (collection);
if (!is_array && !is_record) return JS_NULL;
JSText *sp = JS_VALUE_GET_STRING (text_val);
int len = (int)JSText_len (sp);
int len = (int)JSText_len (JS_VALUE_GET_STRING (text_val));
JSText *result = pretext_init (ctx, len);
if (!result) return JS_EXCEPTION;
int pos = 0;
while (pos < len) {
/* Re-chase sp at the start of each loop iteration */
JSText *sp = JS_VALUE_GET_STRING (text_val);
/* Find next '{' */
int brace_start = -1;
for (int i = pos; i < len; i++) {
@@ -21791,6 +21872,7 @@ static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc,
if (brace_start < 0) {
/* No more braces, copy rest of string */
sp = JS_VALUE_GET_STRING (text_val); /* Re-chase before js_sub_string */
JSValue tail = js_sub_string (ctx, sp, pos, len);
if (JS_IsException (tail)) return JS_EXCEPTION;
result = pretext_concat_value (ctx, result, tail);
@@ -21799,12 +21881,16 @@ static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc,
/* Copy text before brace */
if (brace_start > pos) {
sp = JS_VALUE_GET_STRING (text_val); /* Re-chase before js_sub_string */
JSValue prefix = js_sub_string (ctx, sp, pos, brace_start);
if (JS_IsException (prefix)) return JS_EXCEPTION;
result = pretext_concat_value (ctx, result, prefix);
if (!result) return JS_EXCEPTION;
}
/* Re-chase sp before searching for closing brace */
sp = JS_VALUE_GET_STRING (text_val);
/* Find closing '}' */
int brace_end = -1;
for (int i = brace_start + 1; i < len; i++) {
@@ -21816,6 +21902,7 @@ static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc,
if (brace_end < 0) {
/* No closing brace, copy '{' and continue */
sp = JS_VALUE_GET_STRING (text_val); /* Re-chase before js_sub_string */
JSValue ch = js_sub_string (ctx, sp, brace_start, brace_start + 1);
if (JS_IsException (ch)) return JS_EXCEPTION;
result = pretext_concat_value (ctx, result, ch);
@@ -21825,6 +21912,7 @@ static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc,
}
/* Extract content between braces */
sp = JS_VALUE_GET_STRING (text_val); /* Re-chase before js_sub_string */
JSValue middle = js_sub_string (ctx, sp, brace_start + 1, brace_end);
if (JS_IsException (middle)) return JS_EXCEPTION;
@@ -21842,7 +21930,9 @@ static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc,
JSValue name_val, format_spec;
if (colon_pos >= 0) {
middle_str = JS_VALUE_GET_STRING (middle); /* Re-chase before js_sub_string */
name_val = js_sub_string (ctx, middle_str, 0, colon_pos);
middle_str = JS_VALUE_GET_STRING (middle); /* Re-chase before js_sub_string */
format_spec = js_sub_string (ctx, middle_str, colon_pos + 1, middle_len);
} else {
name_val = middle;
@@ -21903,9 +21993,9 @@ static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc,
/* If still no substitution but we have a value, convert to text */
if (!made_substitution && !JS_IsNull (coll_value)) {
JSValue text_val = JS_ToString (ctx, coll_value);
if (JS_IsText (text_val)) {
substitution = text_val;
JSValue conv_text_val = JS_ToString (ctx, coll_value);
if (JS_IsText (conv_text_val)) {
substitution = conv_text_val;
made_substitution = 1;
}
}
@@ -21915,6 +22005,7 @@ static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc,
if (!result) return JS_EXCEPTION;
} else {
/* No substitution, keep original {name} or {name:format} */
sp = JS_VALUE_GET_STRING (text_val); /* Re-chase before js_sub_string */
JSValue orig = js_sub_string (ctx, sp, brace_start, brace_end + 1);
if (JS_IsException (orig)) return JS_EXCEPTION;
result = pretext_concat_value (ctx, result, orig);