array gc
This commit is contained in:
566
source/quickjs.c
566
source/quickjs.c
File diff suppressed because it is too large
Load Diff
@@ -147,7 +147,6 @@ enum {
|
||||
JS_TAG_UNINITIALIZED = 0x17, /* 10111 */
|
||||
JS_TAG_STRING_IMM = 0x1B, /* 11011 - immediate ASCII (up to 7 chars) */
|
||||
JS_TAG_CATCH_OFFSET = 0x1F, /* 11111 */
|
||||
JS_TAG_FORWARD = 0x13, /* 10011 - forwarding pointer (GC internal use) */
|
||||
};
|
||||
|
||||
/* Compatibility tag aliases for external code */
|
||||
@@ -642,8 +641,18 @@ JSValue JS_NewObject (JSContext *ctx);
|
||||
|
||||
JSValue JS_NewArray (JSContext *ctx);
|
||||
JSValue JS_NewArrayLen (JSContext *ctx, uint32_t len);
|
||||
int JS_ArrayPush (JSContext *ctx, JSValue obj, JSValue val);
|
||||
|
||||
/* GC-safe push: takes pointer to array, updates it if array grows */
|
||||
int JS_ArrayPush (JSContext *ctx, JSValue *arr_ptr, JSValue val);
|
||||
JSValue JS_ArrayPop (JSContext *ctx, JSValue obj);
|
||||
|
||||
/* Intrinsic array operations - signatures match internal functions */
|
||||
JSValue JS_Array (JSContext *ctx, JSValue arg0, JSValue arg1, JSValue arg2, JSValue arg3);
|
||||
JSValue JS_ArrayFilter (JSContext *ctx, JSValue arr, JSValue fn);
|
||||
JSValue JS_ArraySort (JSContext *ctx, JSValue arr, JSValue selector);
|
||||
JSValue JS_ArrayFind (JSContext *ctx, JSValue arr, JSValue target_or_fn, JSValue reverse, JSValue from);
|
||||
JSValue JS_ArrFor (JSContext *ctx, JSValue arr, JSValue fn, JSValue reverse, JSValue exit_val);
|
||||
JSValue JS_ArrayReduce (JSContext *ctx, JSValue arr, JSValue fn, JSValue initial, JSValue reverse);
|
||||
JSValue JS_GetProperty (JSContext *ctx, JSValue this_obj, JSValue prop);
|
||||
|
||||
// For records
|
||||
|
||||
290
source/suite.c
290
source/suite.c
@@ -311,9 +311,9 @@ TEST(array_create) {
|
||||
|
||||
TEST(array_push_and_length) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 10));
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 20));
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 30));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 10));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 20));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 30));
|
||||
|
||||
int64_t len;
|
||||
JS_GetLength(ctx, arr, &len);
|
||||
@@ -323,9 +323,9 @@ TEST(array_push_and_length) {
|
||||
|
||||
TEST(array_get_by_index) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 100));
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 200));
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 300));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 100));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 200));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 300));
|
||||
|
||||
JSValue v0 = JS_GetPropertyUint32(ctx, arr, 0);
|
||||
JSValue v1 = JS_GetPropertyUint32(ctx, arr, 1);
|
||||
@@ -339,8 +339,8 @@ TEST(array_get_by_index) {
|
||||
|
||||
TEST(array_set_by_index) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 0));
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 0));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 0));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 0));
|
||||
|
||||
JS_SetPropertyUint32(ctx, arr, 0, JS_NewInt32(ctx, 55));
|
||||
JS_SetPropertyUint32(ctx, arr, 1, JS_NewInt32(ctx, 66));
|
||||
@@ -355,9 +355,9 @@ TEST(array_set_by_index) {
|
||||
|
||||
TEST(array_pop) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 1));
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 2));
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 3));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 1));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 2));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 3));
|
||||
|
||||
JSValue popped = JS_ArrayPop(ctx, arr);
|
||||
ASSERT_INT(popped, 3);
|
||||
@@ -370,7 +370,7 @@ TEST(array_pop) {
|
||||
|
||||
TEST(array_out_of_bounds_is_null) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 1));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 1));
|
||||
|
||||
JSValue val = JS_GetPropertyUint32(ctx, arr, 999);
|
||||
ASSERT(JS_IsNull(val));
|
||||
@@ -379,10 +379,10 @@ TEST(array_out_of_bounds_is_null) {
|
||||
|
||||
TEST(array_mixed_types) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 42));
|
||||
JS_ArrayPush(ctx, arr, JS_NewString(ctx, "hello"));
|
||||
JS_ArrayPush(ctx, arr, JS_TRUE);
|
||||
JS_ArrayPush(ctx, arr, JS_NULL);
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 42));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewString(ctx, "hello"));
|
||||
JS_ArrayPush(ctx, &arr, JS_TRUE);
|
||||
JS_ArrayPush(ctx, &arr, JS_NULL);
|
||||
|
||||
JSValue v0 = JS_GetPropertyUint32(ctx, arr, 0);
|
||||
JSValue v1 = JS_GetPropertyUint32(ctx, arr, 1);
|
||||
@@ -399,7 +399,7 @@ TEST(array_mixed_types) {
|
||||
TEST(array_many_elements_resize) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, i));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, i));
|
||||
}
|
||||
|
||||
int64_t len;
|
||||
@@ -487,6 +487,249 @@ TEST(strict_eq_bool) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
INTRINSIC ARRAY FUNCTION TESTS
|
||||
============================================================================ */
|
||||
|
||||
/* Helper C function: double the value */
|
||||
static JSValue cfunc_double(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1 || !JS_IsInt(argv[0])) return JS_NULL;
|
||||
return JS_NewInt32(ctx, JS_VALUE_GET_INT(argv[0]) * 2);
|
||||
}
|
||||
|
||||
/* Helper C function: check if value > 5 */
|
||||
static JSValue cfunc_gt5(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1 || !JS_IsInt(argv[0])) return JS_FALSE;
|
||||
return JS_VALUE_GET_INT(argv[0]) > 5 ? JS_TRUE : JS_FALSE;
|
||||
}
|
||||
|
||||
/* Helper C function: check if value is even */
|
||||
static JSValue cfunc_is_even(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1 || !JS_IsInt(argv[0])) return JS_FALSE;
|
||||
return (JS_VALUE_GET_INT(argv[0]) % 2) == 0 ? JS_TRUE : JS_FALSE;
|
||||
}
|
||||
|
||||
/* Helper C function: add two values */
|
||||
static JSValue cfunc_add(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 2 || !JS_IsInt(argv[0]) || !JS_IsInt(argv[1])) return JS_NULL;
|
||||
return JS_NewInt32(ctx, JS_VALUE_GET_INT(argv[0]) + JS_VALUE_GET_INT(argv[1]));
|
||||
}
|
||||
|
||||
/* Helper C function: check if value equals 30 */
|
||||
static JSValue cfunc_eq30(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
printf("HERE\n");
|
||||
if (argc < 1 || !JS_IsInt(argv[0])) return JS_FALSE;
|
||||
return JS_VALUE_GET_INT(argv[0]) == 30 ? JS_TRUE : JS_FALSE;
|
||||
}
|
||||
|
||||
TEST(array_slice_basic) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, i * 10)); /* [0, 10, 20, 30, 40] */
|
||||
}
|
||||
|
||||
/* JS_Array(arr, from, to) = slice */
|
||||
JSValue sliced = JS_Array(ctx, arr, JS_NewInt32(ctx, 1), JS_NewInt32(ctx, 4), JS_NULL);
|
||||
ASSERT(JS_IsArray(sliced));
|
||||
|
||||
int64_t len;
|
||||
JS_GetLength(ctx, sliced, &len);
|
||||
ASSERT(len == 3);
|
||||
|
||||
JSValue v0 = JS_GetPropertyUint32(ctx, sliced, 0);
|
||||
JSValue v1 = JS_GetPropertyUint32(ctx, sliced, 1);
|
||||
JSValue v2 = JS_GetPropertyUint32(ctx, sliced, 2);
|
||||
ASSERT_INT(v0, 10);
|
||||
ASSERT_INT(v1, 20);
|
||||
ASSERT_INT(v2, 30);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_concat_basic) {
|
||||
JSValue arr1 = JS_NewArray(ctx);
|
||||
JSValue arr2 = JS_NewArray(ctx);
|
||||
|
||||
JS_ArrayPush(ctx, &arr1, JS_NewInt32(ctx, 1));
|
||||
JS_ArrayPush(ctx, &arr1, JS_NewInt32(ctx, 2));
|
||||
JS_ArrayPush(ctx, &arr2, JS_NewInt32(ctx, 3));
|
||||
JS_ArrayPush(ctx, &arr2, JS_NewInt32(ctx, 4));
|
||||
|
||||
/* JS_Array(arr, arr2) = concat */
|
||||
JSValue result = JS_Array(ctx, arr1, arr2, JS_NULL, JS_NULL);
|
||||
ASSERT(JS_IsArray(result));
|
||||
|
||||
int64_t len;
|
||||
JS_GetLength(ctx, result, &len);
|
||||
ASSERT(len == 4);
|
||||
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, result, 0), 1);
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, result, 1), 2);
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, result, 2), 3);
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, result, 3), 4);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_sort_numbers) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 30));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 10));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 50));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 20));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 40));
|
||||
|
||||
JSValue sorted = JS_ArraySort(ctx, arr, JS_NULL);
|
||||
ASSERT(JS_IsArray(sorted));
|
||||
|
||||
int64_t len;
|
||||
JS_GetLength(ctx, sorted, &len);
|
||||
ASSERT(len == 5);
|
||||
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, sorted, 0), 10);
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, sorted, 1), 20);
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, sorted, 2), 30);
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, sorted, 3), 40);
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, sorted, 4), 50);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_find_value) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 10));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 20));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 30));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 40));
|
||||
|
||||
/* Find by value: JS_ArrayFind(arr, target, reverse, from) */
|
||||
JSValue idx = JS_ArrayFind(ctx, arr, JS_NewInt32(ctx, 30), JS_NULL, JS_NULL);
|
||||
ASSERT(JS_IsInt(idx));
|
||||
ASSERT(JS_VALUE_GET_INT(idx) == 2);
|
||||
|
||||
/* Not found returns null */
|
||||
JSValue not_found = JS_ArrayFind(ctx, arr, JS_NewInt32(ctx, 99), JS_NULL, JS_NULL);
|
||||
ASSERT(JS_IsNull(not_found));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_find_predicate) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 10));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 20));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 30));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 40));
|
||||
|
||||
/* Find by predicate function */
|
||||
JSValue func = JS_NewCFunction(ctx, cfunc_eq30, "eq30", 1);
|
||||
JSValue idx = JS_ArrayFind(ctx, arr, func, JS_NULL, JS_NULL);
|
||||
ASSERT(JS_IsInt(idx));
|
||||
ASSERT(JS_VALUE_GET_INT(idx) == 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_filter_basic) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, i));
|
||||
}
|
||||
|
||||
/* Filter for values > 5 */
|
||||
JSValue func = JS_NewCFunction(ctx, cfunc_gt5, "gt5", 1);
|
||||
JSValue filtered = JS_ArrayFilter(ctx, arr, func);
|
||||
ASSERT(JS_IsArray(filtered));
|
||||
|
||||
int64_t len;
|
||||
JS_GetLength(ctx, filtered, &len);
|
||||
ASSERT(len == 5); /* 6, 7, 8, 9, 10 */
|
||||
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, filtered, 0), 6);
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, filtered, 4), 10);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_filter_even) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, i));
|
||||
}
|
||||
|
||||
/* Filter for even values */
|
||||
JSValue func = JS_NewCFunction(ctx, cfunc_is_even, "is_even", 1);
|
||||
JSValue filtered = JS_ArrayFilter(ctx, arr, func);
|
||||
ASSERT(JS_IsArray(filtered));
|
||||
|
||||
int64_t len;
|
||||
JS_GetLength(ctx, filtered, &len);
|
||||
ASSERT(len == 5); /* 2, 4, 6, 8, 10 */
|
||||
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, filtered, 0), 2);
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, filtered, 1), 4);
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, filtered, 2), 6);
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, filtered, 3), 8);
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, filtered, 4), 10);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_map_double) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 1));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 2));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 3));
|
||||
|
||||
/* JS_Array(arr, fn, reverse, exit) = map */
|
||||
JSValue func = JS_NewCFunction(ctx, cfunc_double, "double", 1);
|
||||
JSValue mapped = JS_Array(ctx, arr, func, JS_NULL, JS_NULL);
|
||||
ASSERT(JS_IsArray(mapped));
|
||||
|
||||
int64_t len;
|
||||
JS_GetLength(ctx, mapped, &len);
|
||||
ASSERT(len == 3);
|
||||
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, mapped, 0), 2);
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, mapped, 1), 4);
|
||||
ASSERT_INT(JS_GetPropertyUint32(ctx, mapped, 2), 6);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_reduce_sum) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 1));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 2));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 3));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 4));
|
||||
|
||||
/* Sum: 1 + 2 + 3 + 4 = 10 */
|
||||
/* JS_ArrayReduce(arr, fn, initial, reverse) */
|
||||
JSValue func = JS_NewCFunction(ctx, cfunc_add, "add", 2);
|
||||
JSValue result = JS_ArrayReduce(ctx, arr, func, JS_NULL, JS_NULL);
|
||||
ASSERT_INT(result, 10);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_reduce_with_initial) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 1));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 2));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 3));
|
||||
|
||||
/* Sum with initial 100: 100 + 1 + 2 + 3 = 106 */
|
||||
JSValue func = JS_NewCFunction(ctx, cfunc_add, "add", 2);
|
||||
JSValue result = JS_ArrayReduce(ctx, arr, func, JS_NewInt32(ctx, 100), JS_NULL);
|
||||
ASSERT_INT(result, 106);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_foreach_basic) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 1));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 2));
|
||||
JS_ArrayPush(ctx, &arr, JS_NewInt32(ctx, 3));
|
||||
|
||||
/* JS_ArrFor(arr, fn, reverse, exit) */
|
||||
JSValue func = JS_NewCFunction(ctx, cfunc_double, "double", 1);
|
||||
JSValue result = JS_ArrFor(ctx, arr, func, JS_NULL, JS_NULL);
|
||||
ASSERT(JS_IsNull(result));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
GLOBAL OBJECT TEST
|
||||
============================================================================ */
|
||||
@@ -558,6 +801,19 @@ int run_c_test_suite(JSContext *ctx)
|
||||
RUN_TEST(array_mixed_types);
|
||||
RUN_TEST(array_many_elements_resize);
|
||||
|
||||
printf("\nIntrinsic Array Functions:\n");
|
||||
RUN_TEST(array_slice_basic);
|
||||
RUN_TEST(array_concat_basic);
|
||||
RUN_TEST(array_sort_numbers);
|
||||
RUN_TEST(array_find_value);
|
||||
RUN_TEST(array_find_predicate);
|
||||
RUN_TEST(array_filter_basic);
|
||||
RUN_TEST(array_filter_even);
|
||||
RUN_TEST(array_map_double);
|
||||
RUN_TEST(array_reduce_sum);
|
||||
RUN_TEST(array_reduce_with_initial);
|
||||
RUN_TEST(array_foreach_basic);
|
||||
|
||||
printf("\nType Checks:\n");
|
||||
RUN_TEST(type_checks);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user