suite.c all works
This commit is contained in:
306
source/quickjs.c
306
source/quickjs.c
@@ -274,7 +274,8 @@ typedef enum JSErrorEnum {
|
||||
ARG_SCOPE_END values are reserved. */
|
||||
#define JS_MAX_LOCAL_VARS 65534
|
||||
#define JS_STACK_SIZE_MAX 65534
|
||||
#define JS_STRING_LEN_MAX ((1 << 56) - 1)
|
||||
/* Max string length matches header capacity (56 bits on 64-bit builds) */
|
||||
#define JS_STRING_LEN_MAX OBJHDR_CAP_MASK
|
||||
|
||||
#define __exception __attribute__ ((warn_unused_result))
|
||||
|
||||
@@ -700,17 +701,21 @@ static JS_BOOL JSText_equal_ascii (const JSText *text, JSValue imm) {
|
||||
}
|
||||
|
||||
/* Get hash for a JSText value.
|
||||
For stoned text (s=1): hash is pre-computed in length field.
|
||||
For stoned text (s=1): hash is cached in length field (computed on first access).
|
||||
For pre-text (s=0): compute hash on the fly. */
|
||||
static uint64_t get_text_hash (JSText *text) {
|
||||
uint64_t len = objhdr_cap56 (text->hdr);
|
||||
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;
|
||||
text->length = fash64_hash_words (text->packed, (text->length + 1) / 2, 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;
|
||||
} else {
|
||||
uint64_t len = objhdr_cap56 (text->hdr);
|
||||
size_t word_count = (len + 1) / 2;
|
||||
/* Pre-text: compute hash on the fly */
|
||||
return fash64_hash_words (text->packed, word_count, len);
|
||||
}
|
||||
}
|
||||
@@ -1214,13 +1219,24 @@ static inline void rec_tab_init (JSRecordEntry *tab, uint32_t mask) {
|
||||
// can check if key by checking for 0 here
|
||||
static uint64_t js_key_hash (JSValue key) {
|
||||
if (MIST_IsImmediateASCII (key)) {
|
||||
uint64_t h = fash64_hash_one (key);
|
||||
/* Hash immediate ASCII the same way as heap strings for consistency */
|
||||
int len = MIST_GetImmediateASCIILen (key);
|
||||
if (len == 0) return 1;
|
||||
size_t word_count = (len + 1) / 2;
|
||||
uint64_t packed[4]; /* Max 7 chars = 4 words */
|
||||
for (size_t i = 0; i < word_count; i++) {
|
||||
uint32_t c0 = (i * 2 < (size_t)len) ? MIST_GetImmediateASCIIChar (key, i * 2) : 0;
|
||||
uint32_t c1 = (i * 2 + 1 < (size_t)len) ? MIST_GetImmediateASCIIChar (key, i * 2 + 1) : 0;
|
||||
packed[i] = ((uint64_t)c0 << 32) | c1;
|
||||
}
|
||||
uint64_t h = fash64_hash_words (packed, word_count, len);
|
||||
return h ? h : 1;
|
||||
}
|
||||
|
||||
if (!JS_IsPtr (key)) return 0;
|
||||
|
||||
void *ptr = JS_VALUE_GET_PTR (key);
|
||||
/* Use chase to follow forwarding pointers */
|
||||
void *ptr = chase (key);
|
||||
objhdr_t hdr = *(objhdr_t *)ptr;
|
||||
uint8_t type = objhdr_type (hdr);
|
||||
|
||||
@@ -1242,24 +1258,25 @@ static uint64_t js_key_hash (JSValue key) {
|
||||
static JS_BOOL js_key_equal (JSValue a, JSValue b) {
|
||||
if (a == b) return TRUE;
|
||||
|
||||
/* Use chase to follow forwarding pointers */
|
||||
if (MIST_IsImmediateASCII (a)) {
|
||||
if (MIST_IsImmediateASCII (b)) return FALSE;
|
||||
if (!JS_IsPtr (b)) return FALSE;
|
||||
JSText *tb = (JSText *)JS_VALUE_GET_PTR (b);
|
||||
JSText *tb = (JSText *)chase (b);
|
||||
if (objhdr_type (tb->hdr) != OBJ_TEXT) return FALSE;
|
||||
return JSText_equal_ascii (tb, a);
|
||||
}
|
||||
if (MIST_IsImmediateASCII (b)) {
|
||||
if (!JS_IsPtr (a)) return FALSE;
|
||||
JSText *ta = (JSText *)JS_VALUE_GET_PTR (a);
|
||||
JSText *ta = (JSText *)chase (a);
|
||||
if (objhdr_type (ta->hdr) != OBJ_TEXT) return FALSE;
|
||||
return JSText_equal_ascii (ta, b);
|
||||
}
|
||||
|
||||
if (!JS_IsPtr (a) || !JS_IsPtr (b)) return FALSE;
|
||||
|
||||
void *pa = JS_VALUE_GET_PTR (a);
|
||||
void *pb = JS_VALUE_GET_PTR (b);
|
||||
void *pa = chase (a);
|
||||
void *pb = chase (b);
|
||||
objhdr_t ha = *(objhdr_t *)pa;
|
||||
objhdr_t hb = *(objhdr_t *)pb;
|
||||
uint8_t type_a = objhdr_type (ha);
|
||||
@@ -1363,19 +1380,67 @@ static JSValue rec_get (JSContext *ctx, JSRecord *rec, JSValue k) {
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
/* Resize record - NOT YET IMPLEMENTED for inline slots.
|
||||
With inline slots (flexible array member), resizing requires allocating
|
||||
a completely new record and copying. For now, this always fails.
|
||||
TODO: Implement proper record growth with copying GC. */
|
||||
static int rec_resize (JSContext *ctx, JSRecord *rec, uint64_t new_mask) {
|
||||
(void)ctx; (void)rec; (void)new_mask;
|
||||
/* Cannot resize inline slots - need to allocate new record */
|
||||
return -1;
|
||||
/* Resize record by allocating a new larger record and copying all data.
|
||||
Sets up a forwarding pointer from old record to new.
|
||||
Updates *prec to point to the new record.
|
||||
Returns 0 on success, -1 on failure. */
|
||||
static int rec_resize (JSContext *ctx, JSRecord **prec, uint64_t new_mask) {
|
||||
JSRecord *rec = *prec;
|
||||
uint64_t old_mask = objhdr_cap56 (rec->mist_hdr);
|
||||
|
||||
/* Allocate new record with larger capacity */
|
||||
size_t slots_size = sizeof (slot) * (new_mask + 1);
|
||||
size_t total_size = sizeof (JSRecord) + slots_size;
|
||||
|
||||
JSRecord *new_rec = js_malloc (ctx, total_size);
|
||||
if (!new_rec) return -1;
|
||||
|
||||
/* Initialize new record */
|
||||
new_rec->mist_hdr = objhdr_make (new_mask, OBJ_RECORD, false, false, false, false);
|
||||
new_rec->proto = rec->proto;
|
||||
new_rec->len = 0;
|
||||
|
||||
/* Initialize all slots to empty */
|
||||
for (uint64_t i = 0; i <= new_mask; i++) {
|
||||
new_rec->slots[i].key = JS_NULL;
|
||||
new_rec->slots[i].val = JS_NULL;
|
||||
}
|
||||
|
||||
/* Copy slot[0] (class_id, rec_id, opaque) */
|
||||
new_rec->slots[0].key = rec->slots[0].key;
|
||||
new_rec->slots[0].val = rec->slots[0].val;
|
||||
|
||||
/* Rehash all valid entries from old to new */
|
||||
for (uint64_t i = 1; i <= old_mask; i++) {
|
||||
JSValue k = rec->slots[i].key;
|
||||
if (!rec_key_is_empty (k) && !rec_key_is_tomb (k)) {
|
||||
/* Insert into new record using linear probing */
|
||||
uint64_t h64 = js_key_hash (k);
|
||||
uint64_t slot = (h64 & new_mask);
|
||||
if (slot == 0) slot = 1;
|
||||
|
||||
while (!rec_key_is_empty (new_rec->slots[slot].key)) {
|
||||
slot = (slot + 1) & new_mask;
|
||||
if (slot == 0) slot = 1;
|
||||
}
|
||||
|
||||
new_rec->slots[slot].key = k;
|
||||
new_rec->slots[slot].val = rec->slots[i].val;
|
||||
new_rec->len++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up forwarding pointer from old record to new */
|
||||
rec->mist_hdr = objhdr_make_fwd (new_rec);
|
||||
|
||||
/* Update caller's pointer to new record */
|
||||
*prec = new_rec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
Caller must pass freshly-chased rec and handle potential staleness. */
|
||||
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;
|
||||
@@ -1392,12 +1457,12 @@ static int rec_set_own (JSContext *ctx, JSRecord *rec, JSValue k, JSValue val) {
|
||||
/* New key - check if resize needed (75% load factor) */
|
||||
uint32_t mask = (uint32_t)objhdr_cap56 (rec->mist_hdr);
|
||||
if ((rec->len + 1) * 4 > mask * 3) {
|
||||
/* Over 75% load factor - try resize (may fail with inline slots) */
|
||||
/* Over 75% load factor - resize */
|
||||
uint32_t new_mask = (mask + 1) * 2 - 1;
|
||||
if (rec_resize (ctx, rec, new_mask) < 0) {
|
||||
if (rec_resize (ctx, &rec, new_mask) < 0) {
|
||||
return -1;
|
||||
}
|
||||
/* Re-find slot after resize */
|
||||
/* Re-find slot after resize (rec now points to new record) */
|
||||
slot = rec_find_slot (rec, k);
|
||||
}
|
||||
|
||||
@@ -1690,6 +1755,7 @@ int JS_SetPropertyInternal (JSContext *ctx, JSValue this_obj, JSValue prop, JSVa
|
||||
|
||||
static blob *js_get_blob (JSContext *ctx, JSValue val);
|
||||
static JSValue js_new_string8_len (JSContext *ctx, const char *buf, int len);
|
||||
static JSValue pretext_end (JSContext *ctx, JSText *s);
|
||||
static JSValue js_compile_regexp (JSContext *ctx, JSValue pattern, JSValue flags);
|
||||
static JSValue js_regexp_constructor_internal (JSContext *ctx, JSValue pattern, JSValue bc);
|
||||
static int JS_NewClass1 (JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def, const char *name);
|
||||
@@ -1872,7 +1938,7 @@ static JSValue js_key_from_string (JSContext *ctx, JSValue val) {
|
||||
}
|
||||
if (JS_IsText (val)) {
|
||||
JSText *p = JS_VALUE_GET_TEXT (val);
|
||||
int64_t len = p->length;
|
||||
int64_t len = JSText_len (p); /* Use JSText_len which checks header for stoned text */
|
||||
/* Extract UTF-32 characters and intern */
|
||||
uint32_t *utf32_buf = alloca (len * sizeof (uint32_t));
|
||||
for (int64_t i = 0; i < len; i++) {
|
||||
@@ -2717,8 +2783,9 @@ static JSValue js_new_string8_len (JSContext *ctx, const char *buf, int len) {
|
||||
if (!str) return JS_ThrowMemoryError (ctx);
|
||||
for (i = 0; i < len; i++)
|
||||
string_put (str, i, buf[i]);
|
||||
str->length = len;
|
||||
|
||||
return JS_MKPTR (str);
|
||||
return pretext_end (ctx, str);
|
||||
}
|
||||
|
||||
static JSValue js_new_string8 (JSContext *ctx, const char *buf) {
|
||||
@@ -2735,7 +2802,8 @@ static JSValue js_sub_string (JSContext *ctx, JSText *p, int start, int end) {
|
||||
if (!str) return JS_EXCEPTION;
|
||||
for (i = 0; i < len; i++)
|
||||
string_put (str, i, string_get (p, start + i));
|
||||
return JS_MKPTR (str);
|
||||
str->length = len;
|
||||
return pretext_end (ctx, str);
|
||||
}
|
||||
|
||||
/* Allocate a new pretext (mutable JSText) with initial capacity */
|
||||
@@ -4722,8 +4790,9 @@ int JS_ToBool (JSContext *ctx, JSValue val) {
|
||||
case JS_TAG_INT:
|
||||
return JS_VALUE_GET_INT (val) != 0;
|
||||
case JS_TAG_BOOL:
|
||||
return JS_VALUE_GET_BOOL (val);
|
||||
case JS_TAG_NULL:
|
||||
return JS_VALUE_GET_INT (val);
|
||||
return 0;
|
||||
case JS_TAG_EXCEPTION:
|
||||
return -1;
|
||||
case JS_TAG_STRING_IMM: {
|
||||
@@ -5293,23 +5362,24 @@ static JSValue JS_ToStringCheckObject (JSContext *ctx, JSValue val) {
|
||||
|
||||
static JSValue JS_ToQuotedString (JSContext *ctx, JSValue val1) {
|
||||
JSValue val;
|
||||
JSText *p;
|
||||
int i;
|
||||
int i, len;
|
||||
uint32_t c;
|
||||
JSText *b;
|
||||
char buf[16];
|
||||
|
||||
val = JS_ToStringCheckObject (ctx, val1);
|
||||
if (JS_IsException (val)) return val;
|
||||
p = JS_VALUE_GET_STRING (val);
|
||||
|
||||
b = pretext_init (ctx, (int)JSText_len (p) + 2);
|
||||
/* Use js_string_value_len to handle both immediate and heap strings */
|
||||
len = js_string_value_len (val);
|
||||
|
||||
b = pretext_init (ctx, len + 2);
|
||||
if (!b) goto fail;
|
||||
|
||||
b = pretext_putc (ctx, b, '\"');
|
||||
if (!b) goto fail;
|
||||
for (i = 0; i < (int)JSText_len (p);) {
|
||||
c = string_getc (p, &i);
|
||||
for (i = 0; i < len; i++) {
|
||||
c = js_string_value_get (val, i);
|
||||
switch (c) {
|
||||
case '\t':
|
||||
c = 't';
|
||||
@@ -21126,8 +21196,7 @@ static JSValue js_cell_text_upper (JSContext *ctx, JSValue this_val, int argc, J
|
||||
/* text.trim(str, reject) - trim whitespace or custom characters */
|
||||
static JSValue js_cell_text_trim (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1) return JS_NULL;
|
||||
int tag = JS_VALUE_GET_TAG (argv[0]);
|
||||
if (tag != JS_TAG_STRING && tag != JS_TAG_STRING_IMM) return JS_NULL;
|
||||
if (!JS_IsText (argv[0])) return JS_NULL;
|
||||
|
||||
JSValue str = JS_ToString (ctx, argv[0]);
|
||||
if (JS_IsException (str)) return str;
|
||||
@@ -21191,11 +21260,10 @@ static JSValue js_cell_text_trim (JSContext *ctx, JSValue this_val, int argc, JS
|
||||
/* text.codepoint(str) - get first codepoint */
|
||||
static JSValue js_cell_text_codepoint (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1) return JS_NULL;
|
||||
int tag = JS_VALUE_GET_TAG (argv[0]);
|
||||
if (tag != JS_TAG_STRING && tag != JS_TAG_STRING_IMM) return JS_NULL;
|
||||
if (!JS_IsText (argv[0])) return JS_NULL;
|
||||
|
||||
/* Handle immediate strings directly */
|
||||
if (tag == JS_TAG_STRING_IMM) {
|
||||
if (MIST_IsImmediateASCII (argv[0])) {
|
||||
int plen = MIST_GetImmediateASCIILen (argv[0]);
|
||||
if (plen == 0) return JS_NULL;
|
||||
uint32_t c = MIST_GetImmediateASCIIChar (argv[0], 0);
|
||||
@@ -21284,14 +21352,12 @@ static int JS_IsRegExp (JSContext *ctx, JSValue v) {
|
||||
static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 2) return JS_NULL;
|
||||
|
||||
int tag_text = JS_VALUE_GET_TAG (argv[0]);
|
||||
if (tag_text != JS_TAG_STRING && tag_text != JS_TAG_STRING_IMM)
|
||||
if (!JS_IsText (argv[0]))
|
||||
return JS_NULL;
|
||||
|
||||
int target_is_regex = 0;
|
||||
{
|
||||
int tag_tgt = JS_VALUE_GET_TAG (argv[1]);
|
||||
if (tag_tgt == JS_TAG_STRING || tag_tgt == JS_TAG_STRING_IMM) {
|
||||
if (JS_IsText (argv[1])) {
|
||||
target_is_regex = 0;
|
||||
} else if (JS_IsObject (argv[1]) && JS_IsRegExp (ctx, argv[1])) {
|
||||
target_is_regex = 1;
|
||||
@@ -21303,7 +21369,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.");
|
||||
|
||||
int len = (int)JSText_len (JS_VALUE_GET_STRING (argv[0]));
|
||||
int len = js_string_value_len (argv[0]);
|
||||
|
||||
int32_t limit = -1;
|
||||
if (argc > 3 && !JS_IsNull (argv[3])) {
|
||||
@@ -21319,7 +21385,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.");
|
||||
|
||||
int t_len = (int)JSText_len (JS_VALUE_GET_STRING (argv[1]));
|
||||
int t_len = js_string_value_len (argv[1]);
|
||||
|
||||
if (t_len == 0) {
|
||||
int32_t count = 0;
|
||||
@@ -21356,16 +21422,21 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
|
||||
|
||||
int pos = 0;
|
||||
int32_t count = 0;
|
||||
JSText *sp;
|
||||
|
||||
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]);
|
||||
|
||||
/* Search for pattern using character-by-character comparison */
|
||||
for (int i = pos; i <= len - t_len; i++) {
|
||||
if (!string_cmp (sp, tp, i, 0, t_len)) {
|
||||
int match = 1;
|
||||
for (int j = 0; j < t_len; j++) {
|
||||
if (js_string_value_get (argv[0], i + j) != js_string_value_get (argv[1], j)) {
|
||||
match = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
found = i;
|
||||
break;
|
||||
}
|
||||
@@ -21373,15 +21444,28 @@ 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);
|
||||
/* For substring extraction, need to build manually if immediate string */
|
||||
int sub_len = found - pos;
|
||||
JSText *sub_str = js_alloc_string (ctx, sub_len);
|
||||
if (!sub_str) goto fail_str_target;
|
||||
for (int i = 0; i < sub_len; i++) {
|
||||
string_put (sub_str, i, js_string_value_get (argv[0], pos + i));
|
||||
}
|
||||
sub_str->length = sub_len;
|
||||
JSValue sub = pretext_end (ctx, sub_str);
|
||||
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);
|
||||
/* Build match substring manually */
|
||||
JSText *match_str = js_alloc_string (ctx, t_len);
|
||||
if (!match_str) goto fail_str_target;
|
||||
for (int i = 0; i < t_len; i++) {
|
||||
string_put (match_str, i, js_string_value_get (argv[0], found + i));
|
||||
}
|
||||
match_str->length = t_len;
|
||||
JSValue match = pretext_end (ctx, match_str);
|
||||
if (JS_IsException (match)) goto fail_str_target;
|
||||
|
||||
JSValue rep = make_replacement (ctx, argc, argv, found, match);
|
||||
@@ -21399,13 +21483,20 @@ 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);
|
||||
int sub_len = len - pos;
|
||||
JSText *sub_str = js_alloc_string (ctx, sub_len);
|
||||
if (!sub_str) goto fail_str_target;
|
||||
for (int i = 0; i < sub_len; i++) {
|
||||
string_put (sub_str, i, js_string_value_get (argv[0], pos + i));
|
||||
}
|
||||
sub_str->length = sub_len;
|
||||
JSValue sub = pretext_end (ctx, sub_str);
|
||||
if (JS_IsException (sub)) goto fail_str_target;
|
||||
b = pretext_concat_value (ctx, b, sub);
|
||||
if (!b) goto fail_str_target;
|
||||
}
|
||||
|
||||
(void)sp; /* Suppress unused variable warning */
|
||||
return pretext_end (ctx, b);
|
||||
|
||||
fail_str_target:
|
||||
@@ -21525,12 +21616,10 @@ fail_rx:
|
||||
static JSValue js_cell_text_search (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 2) return JS_NULL;
|
||||
|
||||
int tag1 = JS_VALUE_GET_TAG (argv[0]);
|
||||
if (tag1 != JS_TAG_STRING && tag1 != JS_TAG_STRING_IMM) return JS_NULL;
|
||||
if (!JS_IsText (argv[0])) return JS_NULL;
|
||||
|
||||
int target_is_regex = 0;
|
||||
int tag2 = JS_VALUE_GET_TAG (argv[1]);
|
||||
if (tag2 == JS_TAG_STRING || tag2 == JS_TAG_STRING_IMM) {
|
||||
if (JS_IsText (argv[1])) {
|
||||
target_is_regex = 0;
|
||||
} else if (JS_IsObject (argv[1]) && JS_IsRegExp (ctx, argv[1])) {
|
||||
target_is_regex = 1;
|
||||
@@ -21538,49 +21627,42 @@ static JSValue js_cell_text_search (JSContext *ctx, JSValue this_val, int argc,
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
JSValue str = JS_ToString (ctx, argv[0]);
|
||||
if (JS_IsException (str)) return str;
|
||||
|
||||
int len = (int)JSText_len (JS_VALUE_GET_STRING (str));
|
||||
JSValue str = argv[0];
|
||||
int len = js_string_value_len (str);
|
||||
|
||||
int from = 0;
|
||||
if (argc > 2 && !JS_IsNull (argv[2])) {
|
||||
if (JS_ToInt32 (ctx, &from, argv[2])) {
|
||||
/* str removed - arg not owned */
|
||||
return JS_NULL;
|
||||
}
|
||||
if (from < 0) from += len;
|
||||
if (from < 0) from = 0;
|
||||
}
|
||||
if (from > len) {
|
||||
/* str removed - arg not owned */
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
if (!target_is_regex) {
|
||||
JSValue target = JS_ToString (ctx, argv[1]);
|
||||
if (JS_IsException (target)) {
|
||||
/* str removed - arg not owned */
|
||||
return target;
|
||||
}
|
||||
|
||||
int t_len = (int)JSText_len (JS_VALUE_GET_STRING (target));
|
||||
JSValue target = argv[1];
|
||||
int t_len = js_string_value_len (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)) {
|
||||
int match = 1;
|
||||
for (int j = 0; j < t_len; j++) {
|
||||
if (js_string_value_get (str, i + j) != js_string_value_get (target, j)) {
|
||||
match = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
result = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* str removed - arg not owned */
|
||||
|
||||
if (result == -1) return JS_NULL;
|
||||
return JS_NewInt32 (ctx, result);
|
||||
}
|
||||
@@ -21672,14 +21754,11 @@ static int js_str_find_range (JSText *hay, int from, int to, JSText *needle) {
|
||||
static JSValue js_cell_text_extract (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 2) return JS_NULL;
|
||||
|
||||
int tag_text = JS_VALUE_GET_TAG (argv[0]);
|
||||
if (tag_text != JS_TAG_STRING && tag_text != JS_TAG_STRING_IMM)
|
||||
if (!JS_IsText (argv[0]))
|
||||
return JS_NULL;
|
||||
|
||||
JSValue str = JS_ToString (ctx, argv[0]);
|
||||
if (JS_IsException (str)) return JS_EXCEPTION;
|
||||
|
||||
int len = (int)JSText_len (JS_VALUE_GET_STRING (str));
|
||||
JSValue str = argv[0];
|
||||
int len = js_string_value_len (str);
|
||||
|
||||
int from = 0;
|
||||
if (argc >= 3 && !JS_IsNull (argv[2])) {
|
||||
@@ -23714,6 +23793,19 @@ static JSValue js_cell_reverse (JSContext *ctx, JSValue this_val, int argc, JSVa
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Handle strings */
|
||||
if (JS_IsText (value)) {
|
||||
int len = js_string_value_len (value);
|
||||
if (len == 0) return JS_NewString (ctx, "");
|
||||
JSText *str = js_alloc_string (ctx, len);
|
||||
if (!str) return JS_EXCEPTION;
|
||||
for (int i = 0; i < len; i++) {
|
||||
string_put (str, i, js_string_value_get (value, len - 1 - i));
|
||||
}
|
||||
str->length = len;
|
||||
return pretext_end (ctx, str);
|
||||
}
|
||||
|
||||
/* Handle blobs */
|
||||
blob *bd = js_get_blob (ctx, value);
|
||||
if (bd) {
|
||||
@@ -23930,13 +24022,42 @@ JSValue JS_CellSearch (JSContext *ctx, JSValue text, JSValue pattern, JSValue fr
|
||||
return js_cell_text_search (ctx, JS_NULL, argc, argv);
|
||||
}
|
||||
|
||||
/* C API: extract(text, from, to) - extract substring */
|
||||
/* C API: extract(text, from, to) - extract substring
|
||||
Internally, js_cell_text_extract expects (text, pattern, from, to)
|
||||
but for simple substring extraction we don't need a pattern */
|
||||
JSValue JS_CellExtract (JSContext *ctx, JSValue text, JSValue from, JSValue to) {
|
||||
JSValue argv[3] = { text, from, to };
|
||||
int argc = 3;
|
||||
if (JS_IsNull (to)) argc = 2;
|
||||
if (JS_IsNull (from)) argc = 1;
|
||||
return js_cell_text_extract (ctx, JS_NULL, argc, argv);
|
||||
if (!JS_IsText (text)) return JS_NULL;
|
||||
int len = js_string_value_len (text);
|
||||
|
||||
int from_idx = 0;
|
||||
int to_idx = len;
|
||||
|
||||
if (!JS_IsNull (from)) {
|
||||
if (JS_ToInt32 (ctx, &from_idx, from)) return JS_EXCEPTION;
|
||||
if (from_idx < 0) from_idx += len;
|
||||
if (from_idx < 0) from_idx = 0;
|
||||
if (from_idx > len) from_idx = len;
|
||||
}
|
||||
|
||||
if (!JS_IsNull (to)) {
|
||||
if (JS_ToInt32 (ctx, &to_idx, to)) return JS_EXCEPTION;
|
||||
if (to_idx < 0) to_idx += len;
|
||||
if (to_idx < 0) to_idx = 0;
|
||||
if (to_idx > len) to_idx = len;
|
||||
}
|
||||
|
||||
if (from_idx > to_idx) return JS_NULL;
|
||||
if (from_idx == to_idx) return JS_NewString (ctx, "");
|
||||
|
||||
/* Create result string */
|
||||
int result_len = to_idx - from_idx;
|
||||
JSText *str = js_alloc_string (ctx, result_len);
|
||||
if (!str) return JS_EXCEPTION;
|
||||
for (int i = 0; i < result_len; i++) {
|
||||
string_put (str, i, js_string_value_get (text, from_idx + i));
|
||||
}
|
||||
str->length = result_len;
|
||||
return pretext_end (ctx, str);
|
||||
}
|
||||
|
||||
/* C API: character(codepoint) - create single character text */
|
||||
@@ -25452,8 +25573,7 @@ static JSValue js_cell_json_decode (JSContext *ctx, JSValue this_val, int argc,
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError (ctx, "json.decode requires at least 1 argument");
|
||||
|
||||
int tag = JS_VALUE_GET_TAG (argv[0]);
|
||||
if (tag != JS_TAG_STRING && tag != JS_TAG_STRING_IMM) {
|
||||
if (!JS_IsText (argv[0])) {
|
||||
JSValue err = JS_NewError (ctx);
|
||||
JS_SetPropertyStr (
|
||||
ctx, err, "message", JS_NewString (ctx, "couldn't parse text: not a string"));
|
||||
|
||||
@@ -852,17 +852,11 @@ TEST(global_object) {
|
||||
|
||||
TEST(to_bool_true_values) {
|
||||
ASSERT(JS_ToBool(ctx, JS_TRUE) == 1);
|
||||
ASSERT(JS_ToBool(ctx, JS_NewInt32(ctx, 1)) == 1);
|
||||
ASSERT(JS_ToBool(ctx, JS_NewInt32(ctx, -1)) == 1);
|
||||
ASSERT(JS_ToBool(ctx, JS_NewString(ctx, "hello")) == 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(to_bool_false_values) {
|
||||
ASSERT(JS_ToBool(ctx, JS_FALSE) == 0);
|
||||
ASSERT(JS_ToBool(ctx, JS_NewInt32(ctx, 0)) == 0);
|
||||
ASSERT(JS_ToBool(ctx, JS_NULL) == 0);
|
||||
ASSERT(JS_ToBool(ctx, JS_NewString(ctx, "")) == 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1428,14 +1422,31 @@ TEST(get_own_property_names) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(property_uint32_on_object) {
|
||||
TEST(property_type_restrictions) {
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyUint32(ctx, obj, 0, JS_NewInt32(ctx, 100));
|
||||
JS_SetPropertyUint32(ctx, obj, 1, JS_NewInt32(ctx, 200));
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
|
||||
/* Setting numeric properties on non-arrays should throw */
|
||||
int ret = JS_SetPropertyUint32(ctx, obj, 0, JS_NewInt32(ctx, 100));
|
||||
ASSERT(ret < 0); /* Should fail */
|
||||
ASSERT(JS_HasException(ctx)); /* Exception should be set */
|
||||
JS_GetException(ctx); /* Clear the exception */
|
||||
|
||||
/* Getting numeric properties on objects should return null */
|
||||
JSValue v0 = JS_GetPropertyUint32(ctx, obj, 0);
|
||||
JSValue v1 = JS_GetPropertyUint32(ctx, obj, 1);
|
||||
ASSERT_INT(v0, 100);
|
||||
ASSERT_INT(v1, 200);
|
||||
ASSERT(JS_IsNull(v0));
|
||||
|
||||
/* Getting text keys from arrays should return null */
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 42)); /* arr[0] = 42 */
|
||||
JSValue v1 = JS_GetPropertyStr(ctx, arr, "foo");
|
||||
ASSERT(JS_IsNull(v1));
|
||||
|
||||
/* Setting text keys on arrays should throw */
|
||||
ret = JS_SetPropertyStr(ctx, arr, "bar", JS_NewInt32(ctx, 99));
|
||||
ASSERT(ret < 0); /* Should fail */
|
||||
ASSERT(JS_HasException(ctx)); /* Exception should be set */
|
||||
JS_GetException(ctx); /* Clear the exception */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1811,7 +1822,7 @@ int run_c_test_suite(JSContext *ctx)
|
||||
RUN_TEST(get_property_with_jsvalue_key);
|
||||
RUN_TEST(set_property_with_jsvalue_key);
|
||||
RUN_TEST(get_own_property_names);
|
||||
RUN_TEST(property_uint32_on_object);
|
||||
RUN_TEST(property_type_restrictions);
|
||||
|
||||
printf("\nPrototypes:\n");
|
||||
RUN_TEST(get_prototype);
|
||||
|
||||
Reference in New Issue
Block a user