From 893deaec235f1154f71495baea1849b340ed8c6b Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Mon, 2 Feb 2026 23:39:12 -0600 Subject: [PATCH] suite.c all works --- source/quickjs.c | 306 +++++++++++++++++++++++++++++++++-------------- source/suite.c | 37 ++++-- 2 files changed, 237 insertions(+), 106 deletions(-) diff --git a/source/quickjs.c b/source/quickjs.c index 7f117039..574c167a 100644 --- a/source/quickjs.c +++ b/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")); diff --git a/source/suite.c b/source/suite.c index 90ca018c..f3295bd7 100644 --- a/source/suite.c +++ b/source/suite.c @@ -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);