diff --git a/fd.c b/fd.c index 3709f897..24a1aed6 100644 --- a/fd.c +++ b/fd.c @@ -502,10 +502,9 @@ JSC_SCALL(fd_readdir, ret = JS_ThrowInternalError(js, "FindFirstFile failed for %s", path); } else { ret = JS_NewArray(js); - int i = 0; do { if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue; - JS_SetPropertyUint32(js, ret, i++, JS_NewString(js, ffd.cFileName)); + JS_ArrayPush(js, ret,JS_NewString(js, ffd.cFileName)); } while (FindNextFile(hFind, &ffd) != 0); FindClose(hFind); } @@ -515,10 +514,9 @@ JSC_SCALL(fd_readdir, d = opendir(str); if (d) { ret = JS_NewArray(js); - int i = 0; while ((dir = readdir(d)) != NULL) { if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) continue; - JS_SetPropertyUint32(js, ret, i++, JS_NewString(js, dir->d_name)); + JS_ArrayPush(js, ret, JS_NewString(js, dir->d_name)); } closedir(d); } else { diff --git a/source/cell.c b/source/cell.c index 19eab92b..02f2cb25 100644 --- a/source/cell.c +++ b/source/cell.c @@ -304,31 +304,4 @@ int uncaught_exception(JSContext *js, JSValue v) JS_FreeValue(js, exp); JS_FreeValue(js, v); return 0; - - - exp = JS_GetException(js); - if (JS_IsNull(rt->on_exception)) { - const char *str = JS_ToCString(js, exp); - if (str) { - printf("Uncaught exception: %s\n", str); - JS_FreeCString(js, str); - } - - JSValue stack = JS_GetPropertyStr(js, exp, "stack"); - if (!JS_IsNull(stack)) { - const char *stack_str = JS_ToCString(js, stack); - if (stack_str) { - printf("Stack trace:\n%s\n", stack_str); - JS_FreeCString(js, stack_str); - } - } - JS_FreeValue(js, stack); - } else { - JSValue ret = JS_Call(js, rt->on_exception, JS_NULL, 1, &exp); - JS_FreeValue(js, ret); - } - - JS_FreeValue(js, exp); - JS_FreeValue(js, v); - return 0; } \ No newline at end of file diff --git a/source/qjs_wota.c b/source/qjs_wota.c index 238a927d..9b2a4b1e 100644 --- a/source/qjs_wota.c +++ b/source/qjs_wota.c @@ -275,8 +275,7 @@ static char *decode_wota_value(JSContext *ctx, char *data_ptr, JSValue *out_val, case WOTA_ARR: { long long c; data_ptr = wota_read_array(&c, data_ptr); - JSValue arr = JS_NewArray(ctx); - JS_SetLength(ctx, arr, c); + JSValue arr = JS_NewArrayLen(ctx, c); for (long long i = 0; i < c; i++) { JSValue elem_val = JS_NULL; JSAtom idx_atom = JS_NewAtomUInt32(ctx, (uint32_t)i); diff --git a/source/quickjs.c b/source/quickjs.c index 9e3fde3c..7c528e07 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -876,8 +876,6 @@ static __maybe_unused void JS_DumpShapes(JSRuntime *rt); static void js_dump_value_write(void *opaque, const char *buf, size_t len); static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); -static void js_array_finalizer(JSRuntime *rt, JSValue val); -static void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); static void free_array(JSRuntime *rt, JSArray *arr); static void js_c_function_finalizer(JSRuntime *rt, JSValue val); static void js_c_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); @@ -920,6 +918,12 @@ static JSValue JS_ToObject(JSContext *ctx, JSValueConst val); static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val); static JSValue js_cell_text(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); +static JSValue js_cell_push(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +static JSValue js_cell_pop(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +static JSValue js_cell_array_find(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); static JSProperty *add_property(JSContext *ctx, JSObject *p, JSAtom prop, int prop_flags); JSValue JS_ThrowOutOfMemory(JSContext *ctx); @@ -4751,7 +4755,7 @@ JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto) } /* Create an intrinsic array with specified capacity */ -static JSValue JS_NewArrayLen(JSContext *ctx, uint32_t len) +JSValue JS_NewArrayLen(JSContext *ctx, uint32_t len) { JSRuntime *rt = ctx->rt; JSArray *arr; @@ -5093,27 +5097,6 @@ static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) } } -static void js_array_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - int i; - - for(i = 0; i < p->u.array.count; i++) { - JS_FreeValueRT(rt, p->u.array.u.values[i]); - } - js_free_rt(rt, p->u.array.u.values); -} - -static void js_array_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - int i; - - for(i = 0; i < p->u.array.count; i++) { - JS_MarkValue(rt, p->u.array.u.values[i], mark_func); - } -} /* Free intrinsic array (JS_TAG_ARRAY) */ static void free_array(JSRuntime *rt, JSArray *arr) @@ -6698,6 +6681,18 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, return JS_NULL; case JS_TAG_EXCEPTION: return JS_EXCEPTION; + case JS_TAG_ARRAY: + { + JSArray *arr = JS_VALUE_GET_ARRAY(obj); + /* Only integer indexed access is allowed on arrays */ + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx < arr->len) { + return JS_DupValue(ctx, arr->values[idx]); + } + } + } + return JS_NULL; case JS_TAG_STRING: { JSString *p1 = JS_VALUE_GET_STRING(obj); @@ -7129,6 +7124,19 @@ JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val) return atom; } +static uint32_t js_string_get_length(JSValueConst val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { + JSString *p = JS_VALUE_GET_STRING(val); + return p->len; + } else if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING_ROPE) { + JSStringRope *r = JS_VALUE_GET_PTR(val); + return r->len; + } else { + return 0; + } +} + static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, JSValue prop) { @@ -7138,37 +7146,38 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, int this_tag = JS_VALUE_GET_TAG(this_obj); if (this_tag == JS_TAG_NULL) { - JS_FreeValue(js, prop); + JS_FreeValue(ctx, prop); return JS_NULL; } if (this_tag == JS_TAG_ARRAY) { + JSArray *arr = JS_VALUE_GET_ARRAY(this_obj); if (prop_tag == JS_TAG_INT) { int idx = JS_VALUE_GET_INT(prop); - if (idx < 0 || idx >= JS_VALUE_GET_ARRAY(this_obj)->len) { + if (idx < 0 || (uint32_t)idx >= arr->len) { return JS_NULL; } - return JS_DupValue(ctx, JS_VALUE_GET_ARRAY(this_obj)->values[idx]); + return JS_DupValue(ctx, arr->values[idx]); } if (prop_tag == JS_TAG_FLOAT64) { double d = JS_VALUE_GET_FLOAT64(prop); - if (d < 0 || d >= JS_VALUE_GET_ARRAY(this_obj)->len) { + uint32_t idx = (uint32_t)d; + if (d < 0 || d != (double)idx || idx >= arr->len) { return JS_NULL; } - return JS_DupValue(ctx, JS_VALUE_GET_ARRAY(this_obj)->values[(int)d]); + return JS_DupValue(ctx, arr->values[idx]); } - return JS_EXCEPTION; - } - - if (this_tag == JS_TAG_OBJECT) { - + JS_FreeValue(ctx, prop); + return JS_ThrowTypeError(ctx, "array index must be a number"); } if (this_tag == JS_TAG_OBJECT) { + /* Records only accept string or object keys */ if (prop_tag == JS_TAG_INT || prop_tag == JS_TAG_FLOAT64) { - return JS_EXCEPTION; + JS_FreeValue(ctx, prop); + return JS_ThrowTypeError(ctx, "record index must be text or record"); } if (prop_tag == JS_TAG_STRING || prop_tag == JS_TAG_STRING_ROPE) { @@ -7178,7 +7187,48 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, JS_FreeAtom(ctx, atom); return ret; } + + if (prop_tag == JS_TAG_OBJECT) { + /* Object key - handled via symbol lookup */ + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (atom == JS_ATOM_NULL) + return JS_EXCEPTION; + ret = JS_GetProperty(ctx, this_obj, atom); + JS_FreeAtom(ctx, atom); + return ret; + } + + JS_FreeValue(ctx, prop); + return JS_ThrowTypeError(ctx, "record index must be text or record"); } + + if (this_tag == JS_TAG_STRING || this_tag == JS_TAG_STRING_ROPE) { + if (prop_tag == JS_TAG_INT) { + int idx = JS_VALUE_GET_INT(prop); + uint32_t len = js_string_get_length(this_obj); + if (idx < 0 || (uint32_t)idx >= len) { + return JS_NULL; + } + return js_sub_string(ctx, JS_VALUE_GET_STRING(this_obj), idx, idx + 1); + } + + if (prop_tag == JS_TAG_FLOAT64) { + double d = JS_VALUE_GET_FLOAT64(prop); + uint32_t idx = (uint32_t)d; + uint32_t len = js_string_get_length(this_obj); + if (d < 0 || d != (double)idx || idx >= len) { + return JS_NULL; + } + return js_sub_string(ctx, JS_VALUE_GET_STRING(this_obj), idx, idx + 1); + } + + JS_FreeValue(ctx, prop); + return JS_ThrowTypeError(ctx, "string index must be a number"); + } + + JS_FreeValue(ctx, prop); + return JS_ThrowTypeError(ctx, "cannot read property of this value: %d", this_tag); } JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, @@ -7405,6 +7455,23 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, tag = JS_VALUE_GET_TAG(this_obj); if (unlikely(tag != JS_TAG_OBJECT)) { + if (tag == JS_TAG_ARRAY) { + /* Arrays only accept integer index via JS_SetPropertyValue */ + JS_FreeValue(ctx, val); + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + JSArray *arr = JS_VALUE_GET_ARRAY(this_obj); + if (idx < arr->len) { + /* This path shouldn't be used for arrays - use JS_SetPropertyValue */ + JS_ThrowTypeError(ctx, "use JS_SetPropertyValue for array element access"); + } else { + JS_ThrowRangeError(ctx, "array index out of bounds"); + } + } else { + JS_ThrowTypeError(ctx, "array index must be a number"); + } + return -1; + } if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { p = NULL; p1 = JS_VALUE_GET_OBJ(obj); @@ -7545,6 +7612,8 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, JSValue prop, JSValue val, int flags) { + JSAtom atom; + int ret; uint32_t prop_tag = JS_VALUE_GET_TAG(prop); int this_tag = JS_VALUE_GET_TAG(this_obj); @@ -7577,12 +7646,13 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, double d = JS_VALUE_GET_FLOAT64(prop); JSArray *arr = JS_VALUE_GET_ARRAY(this_obj); JS_FreeValue(ctx, prop); - if (d < 0 || d >= arr->len || d != (double)(uint32_t)d) { + uint32_t idx = (uint32_t)d; + if (d < 0 || d != (double)idx || idx >= arr->len) { JS_FreeValue(ctx, val); JS_ThrowRangeError(ctx, "array index out of bounds"); return -1; } - set_value(ctx, &arr->values[(uint32_t)d], val); + set_value(ctx, &arr->values[idx], val); return TRUE; } JS_FreeValue(ctx, prop); @@ -7592,7 +7662,9 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, if (this_tag == JS_TAG_OBJECT) { if (prop_tag != JS_TAG_STRING && prop_tag != JS_TAG_STRING_ROPE && prop_tag != JS_TAG_OBJECT) { - JS_ThrowTypeError(ctx, "record index must be a text or record"); + JS_FreeValue(ctx, prop); + JS_FreeValue(ctx, val); + JS_ThrowTypeError(ctx, "record index must be text or record"); return -1; } @@ -7606,6 +7678,12 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, JS_FreeAtom(ctx, atom); return ret; } + + /* Not an array or object */ + JS_FreeValue(ctx, prop); + JS_FreeValue(ctx, val); + JS_ThrowTypeError(ctx, "cannot set property on this value"); + return -1; } int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, @@ -7848,6 +7926,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, return -1; } JS_ThrowTypeError(ctx, "intrinsic arrays only support numeric indices"); + assert(0); return -1; } @@ -7983,26 +8062,9 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); } } else if (prs->flags & JS_PROP_LENGTH) { - if (flags & JS_PROP_HAS_VALUE) { - /* Note: no JS code is executable because - 'val' is guaranted to be a Uint32 */ - res = set_array_length(ctx, p, JS_DupValue(ctx, val), - flags); - } else { - res = TRUE; - } - /* still need to reset the writable flag if - needed. The JS_PROP_LENGTH is kept because the - Uint32 test is still done if the length - property is read-only. */ - if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == - JS_PROP_HAS_WRITABLE) { - prs = get_shape_prop(p->shape); - if (js_update_property_flags(ctx, p, &prs, - prs->flags & ~JS_PROP_WRITABLE)) - return -1; - } - return res; + /* Object-based arrays removed - length property not supported */ + JS_ThrowTypeError(ctx, "cannot set length property"); + return -1; } else { if (flags & JS_PROP_HAS_VALUE) { JS_FreeValue(ctx, pr->u.value); @@ -8438,6 +8500,15 @@ int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags) JSObject *p; int res; + /* Arrays do not support property deletion */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_ARRAY) { + if (flags & (JS_PROP_THROW | JS_PROP_THROW_STRICT)) { + JS_ThrowTypeError(ctx, "cannot delete array element"); + return -1; + } + return FALSE; + } + obj1 = JS_ToObject(ctx, obj); if (JS_IsException(obj1)) return -1; @@ -8446,7 +8517,7 @@ int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags) JS_FreeValue(ctx, obj1); if (res != FALSE) return res; - if (flags & JS_PROP_THROW || + if (flags & JS_PROP_THROW || flags & JS_PROP_THROW_STRICT) { JS_ThrowTypeError(ctx, "could not delete property"); return -1; @@ -9467,19 +9538,6 @@ static void js_print_float64(JSPrintValueState *s, double d) s->write_func(s->write_opaque, buf, len); } -static uint32_t js_string_get_length(JSValueConst val) -{ - if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { - JSString *p = JS_VALUE_GET_STRING(val); - return p->len; - } else if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING_ROPE) { - JSStringRope *r = JS_VALUE_GET_PTR(val); - return r->len; - } else { - return 0; - } -} - static void js_dump_char(JSPrintValueState *s, int c, int sep) { if (c == sep || c == '\\') { @@ -9867,6 +9925,29 @@ static void js_print_value(JSPrintValueState *s, JSValueConst val) js_putc(s, ')'); } break; + case JS_TAG_ARRAY: + { + JSArray *arr = JS_VALUE_GET_ARRAY(val); + uint32_t i, count; + js_putc(s, '['); + count = min_uint32(arr->len, s->options.max_item_count); + for (i = 0; i < count; i++) { + if (i > 0) + js_puts(s, ", "); + if (s->level < s->options.max_depth) { + s->level++; + js_print_value(s, arr->values[i]); + s->level--; + } else { + js_puts(s, "..."); + } + } + if (arr->len > count) { + js_printf(s, ", ... %u more", arr->len - count); + } + js_putc(s, ']'); + } + break; default: js_printf(s, "[unknown tag %d]", tag); break; @@ -24460,6 +24541,18 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) if (JS_WriteFunctionTag(s, obj)) goto fail; break; + case JS_TAG_ARRAY: + { + JSArray *arr = JS_VALUE_GET_ARRAY(obj); + uint32_t i; + bc_put_u8(s, BC_TAG_ARRAY); + bc_put_leb128(s, arr->len); + for (i = 0; i < arr->len; i++) { + if (JS_WriteObjectRec(s, arr->values[i])) + goto fail; + } + } + break; case JS_TAG_OBJECT: { JSObject *p = JS_VALUE_GET_OBJ(obj); @@ -25158,38 +25251,39 @@ static JSValue JS_ReadArray(BCReaderState *s, int tag) JSValue obj; uint32_t len, i; JSValue val; - int ret, prop_flags; BOOL is_template; - obj = JS_NewArray(ctx); - if (BC_add_object_ref(s, obj)) - goto fail; is_template = (tag == BC_TAG_TEMPLATE_OBJECT); + if (bc_get_leb128(s, &len)) - goto fail; - for(i = 0; i < len; i++) { + return JS_EXCEPTION; + + /* Create intrinsic array with preallocated capacity */ + obj = JS_NewArrayLen(ctx, len); + if (JS_IsException(obj)) + return JS_EXCEPTION; + + /* Note: intrinsic arrays don't support object reference tracking + for circular references - they're simpler value containers */ + + /* Read and set each element directly */ + JSArray *arr = JS_VALUE_GET_ARRAY(obj); + for (i = 0; i < len; i++) { val = JS_ReadObjectRec(s); if (JS_IsException(val)) goto fail; - if (is_template) - prop_flags = JS_PROP_ENUMERABLE; - else - prop_flags = JS_PROP_C_W_E; - ret = JS_DefinePropertyValueUint32(ctx, obj, i, val, - prop_flags); - if (ret < 0) - goto fail; + /* Replace the null placeholder with the read value */ + JS_FreeValue(ctx, arr->values[i]); + arr->values[i] = val; } + + /* Template objects have additional 'raw' property - not supported for intrinsic arrays */ if (is_template) { val = JS_ReadObjectRec(s); if (JS_IsException(val)) goto fail; - if (!JS_IsNull(val)) { - ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0); - if (ret < 0) - goto fail; - } - JS_PreventExtensions(ctx, obj); + /* Skip the raw property for intrinsic arrays */ + JS_FreeValue(ctx, val); } return obj; fail: @@ -26570,32 +26664,8 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, goto fail; } - t = JS_NewInt32(ctx, (capture[0] - str_buf) >> shift); - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index, t, prop_flags) < 0) - goto fail; + - t = str_val, str_val = JS_NULL; - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, t, prop_flags) < 0) - goto fail; - - t = groups, groups = JS_NULL; - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups, - t, prop_flags) < 0) { - goto fail; - } - - if (!JS_IsNull(indices)) { - t = indices_groups, indices_groups = JS_NULL; - if (JS_DefinePropertyValue(ctx, indices, JS_ATOM_groups, - t, prop_flags) < 0) { - goto fail; - } - t = indices, indices = JS_NULL; - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_indices, - t, prop_flags) < 0) { - goto fail; - } - } } ret = obj; obj = JS_NULL; @@ -29188,8 +29258,6 @@ static JSValue js_cell_text_extract(JSContext *ctx, JSValueConst this_val, } JS_DefinePropertyValueUint32(ctx, arr, 0, match, JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, arr, "index", JS_NewInt32(ctx, pos), JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, arr, "input", JS_DupValue(ctx, str), JS_PROP_C_W_E); /* str removed - arg not owned */ return arr; @@ -29218,13 +29286,10 @@ static JSValue js_cell_array(JSContext *ctx, JSValueConst this_val, if (d < 0) return JS_NULL; int64_t len = (int64_t)floor(d); - JSValue result = JS_NewArray(ctx); + JSValue result = JS_NewArrayLen(ctx, len); if (JS_IsException(result)) return result; - if (argc < 2 || JS_IsNull(argv[1])) { - /* Just set length */ - JS_SetPropertyStr(ctx, result, "length", JS_NewInt64(ctx, len > 100 ? 100 : len)); - } else if (JS_IsFunction(ctx, argv[1])) { + if (JS_IsFunction(ctx, argv[1])) { /* Fill with function results */ JSValueConst func = argv[1]; int arity = 0; @@ -29264,8 +29329,7 @@ static JSValue js_cell_array(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; if (argc < 2 || JS_IsNull(argv[1])) { - /* Copy */ - JSValue result = JS_NewArray(ctx); + JSValue result = JS_NewArrayLen(ctx, len); if (JS_IsException(result)) return result; for (int64_t i = 0; i < len; i++) { JSValue val = JS_GetPropertyInt64(ctx, arg, i); @@ -29284,7 +29348,7 @@ static JSValue js_cell_array(JSContext *ctx, JSValueConst this_val, int reverse = argc > 2 && JS_ToBool(ctx, argv[2]); JSValue exit_val = argc > 3 ? argv[3] : JS_NULL; - JSValue result = JS_NewArray(ctx); + JSValue result = JS_NewArrayLen(ctx, len); if (JS_IsException(result)) return result; if (reverse) { @@ -29340,7 +29404,7 @@ static JSValue js_cell_array(JSContext *ctx, JSValueConst this_val, if (js_get_length64(ctx, &len2, argv[1])) return JS_EXCEPTION; - JSValue result = JS_NewArray(ctx); + JSValue result = JS_NewArrayLen(ctx, len + len2); if (JS_IsException(result)) return result; int64_t idx = 0; @@ -29417,7 +29481,7 @@ static JSValue js_cell_array(JSContext *ctx, JSValueConst this_val, if (argc < 2 || JS_IsNull(argv[1])) { /* Split into characters */ - JSValue result = JS_NewArray(ctx); + JSValue result = JS_NewArrayLen(ctx, len); if (JS_IsException(result)) { return result; } @@ -29444,7 +29508,23 @@ static JSValue js_cell_array(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } - JSValue result = JS_NewArray(ctx); + size_t sep_len = strlen(sep); + + /* Count the number of parts first */ + int64_t count = 0; + if (sep_len == 0) { + count = len; + } else { + const char *pos = cstr; + const char *found; + count = 1; + while ((found = strstr(pos, sep)) != NULL) { + count++; + pos = found + sep_len; + } + } + + JSValue result = JS_NewArrayLen(ctx, count); if (JS_IsException(result)) { JS_FreeCString(ctx, cstr); JS_FreeCString(ctx, sep); @@ -29452,7 +29532,6 @@ static JSValue js_cell_array(JSContext *ctx, JSValueConst this_val, } int64_t idx = 0; - size_t sep_len = strlen(sep); const char *pos = cstr; const char *found; @@ -31035,7 +31114,7 @@ static JSValue js_cell_reverse(JSContext *ctx, JSValueConst this_val, * proto() function - get prototype of an object * ============================================================================ */ - static JSValue js_cell_pop(JSContext *ctx, JSValueConst this_val, +static JSValue js_cell_pop(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { if (argc < 1) @@ -31050,11 +31129,33 @@ static JSValue js_cell_reverse(JSContext *ctx, JSValueConst this_val, if (arr->len == 0) return JS_NULL; - JSValue last = JS_DupValue(ctx, arr->values[arr->len-1]); + /* Transfer ownership: take value without dup, clear slot, decrement len */ + JSValue last = arr->values[arr->len - 1]; + arr->values[arr->len - 1] = JS_NULL; arr->len--; return last; } +JSValue JS_ArrayPush(JSContext *ctx, JSValueConst obj, JSValueConst val) +{ + if (!JS_IsArray(ctx, obj)) + return JS_ThrowTypeError(ctx, "not an array"); + + JSValue stack[2]; + stack[0] = obj; + stack[1] = val; + + return js_cell_push(ctx, JS_NULL, 2, stack); +} + +JSValue JS_ArrayPop(JSContext *ctx, JSValueConst obj) +{ + if (!JS_IsArray(ctx, obj)) + return JS_ThrowTypeError(ctx, "not an array"); + + return js_cell_pop(ctx, JS_NULL, 1, &obj); +} + static JSValue js_cell_push(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { diff --git a/source/quickjs.h b/source/quickjs.h index db84c565..935cff35 100644 --- a/source/quickjs.h +++ b/source/quickjs.h @@ -669,9 +669,12 @@ JSValue JS_NewObject(JSContext *ctx); JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val); JSValue JS_NewArray(JSContext *ctx); +JSValue JS_NewArrayLen(JSContext *ctx, uint32_t len); int JS_IsArray(JSContext *ctx, JSValueConst val); int JS_GetLength(JSContext *ctx, JSValueConst obj, int64_t *pres); int JS_SetLength(JSContext *ctx, JSValueConst obj, int64_t len); +JSValue JS_ArrayPush(JSContext *ctx, JSValueConst obj, JSValueConst val); +JSValue JS_ArrayPop(JSContext *ctx, JSValueConst obj); JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, JSAtom prop, JSValueConst receiver,