This commit is contained in:
2026-02-02 20:33:11 -06:00
parent beac9608ea
commit 0b86af1d4c
3 changed files with 606 additions and 263 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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);