update text and object to be gc aware
This commit is contained in:
211
source/quickjs.c
211
source/quickjs.c
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user