tag array coverage

This commit is contained in:
2026-01-23 22:51:46 -06:00
parent a01b48dabc
commit c6440ff98c
3 changed files with 49 additions and 38 deletions

View File

@@ -56,7 +56,7 @@ static:
# Bootstrap: build cell from scratch using meson (only needed once) # Bootstrap: build cell from scratch using meson (only needed once)
# Also installs core scripts to ~/.cell/core # Also installs core scripts to ~/.cell/core
bootstrap: bootstrap:
meson setup build_bootstrap -Dbuildtype=debugoptimized meson setup build_bootstrap -Dbuildtype=debugoptimized -Db_sanitize=address
meson compile -C build_bootstrap meson compile -C build_bootstrap
cp build_bootstrap/cell . cp build_bootstrap/cell .
cp build_bootstrap/libcell_runtime.dylib . cp build_bootstrap/libcell_runtime.dylib .

View File

@@ -5048,10 +5048,11 @@ static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)
} }
} }
/* Free intrinsic array (JS_TAG_ARRAY) */ /* Free intrinsic array (JS_TAG_ARRAY) */
static void free_array(JSRuntime *rt, JSArray *arr) static void free_array(JSRuntime *rt, JSArray *arr)
{ {
assert(arr->header.gc_obj_type == JS_GC_OBJ_TYPE_ARRAY);
uint32_t i; uint32_t i;
for (i = 0; i < arr->len; i++) { for (i = 0; i < arr->len; i++) {
JS_FreeValueRT(rt, arr->values[i]); JS_FreeValueRT(rt, arr->values[i]);
@@ -5063,25 +5064,34 @@ static void free_array(JSRuntime *rt, JSArray *arr)
static int js_intrinsic_array_ensure_capacity(JSContext *ctx, JSArray *arr, uint32_t min_cap) static int js_intrinsic_array_ensure_capacity(JSContext *ctx, JSArray *arr, uint32_t min_cap)
{ {
if (min_cap <= arr->cap) if (min_cap <= arr->cap) return 0;
return 0;
uint32_t new_cap = arr->cap ? arr->cap : JS_ARRAY_INITIAL_SIZE; uint32_t old_cap = arr->cap;
while (new_cap < min_cap) { uint32_t new_cap = old_cap ? old_cap : JS_ARRAY_INITIAL_SIZE;
if (new_cap > UINT32_MAX / 2) {
new_cap = min_cap;
break;
}
new_cap *= 2;
}
JSValue *new_values = js_realloc(ctx, arr->values, sizeof(JSValue) * new_cap); while (new_cap < min_cap) {
if (!new_values) if (new_cap > UINT32_MAX / 2) { new_cap = min_cap; break; }
return -1; new_cap *= 2;
}
arr->values = new_values; JSValue *new_values = js_realloc(ctx, arr->values, sizeof(JSValue) * new_cap);
arr->cap = new_cap; if (!new_values) return -1;
return 0;
for (uint32_t i = old_cap; i < new_cap; i++) new_values[i] = JS_NULL;
arr->values = new_values;
arr->cap = new_cap;
return 0;
}
static int js_intrinsic_array_push(JSContext *ctx, JSArray *arr, JSValue val)
{
if (js_intrinsic_array_ensure_capacity(ctx, arr, arr->len + 1) < 0) {
JS_FreeValue(ctx, val);
return -1;
}
arr->values[arr->len++] = val;
return 0;
} }
static int js_intrinsic_array_set(JSContext *ctx, JSArray *arr, uint32_t idx, JSValue val) static int js_intrinsic_array_set(JSContext *ctx, JSArray *arr, uint32_t idx, JSValue val)
@@ -5109,23 +5119,6 @@ static int js_intrinsic_array_set(JSContext *ctx, JSArray *arr, uint32_t idx, JS
return TRUE; return TRUE;
} }
/* Push element to intrinsic array, growing if needed. Returns -1 on error, 0 on success. */
static int js_intrinsic_array_push(JSContext *ctx, JSArray *arr, JSValue val)
{
if (arr->len >= arr->cap) {
uint32_t new_cap = arr->cap ? arr->cap * 2 : JS_ARRAY_INITIAL_SIZE;
JSValue *new_values = js_realloc(ctx, arr->values, sizeof(JSValue) * new_cap);
if (!new_values) {
JS_FreeValue(ctx, val);
return -1;
}
arr->values = new_values;
arr->cap = new_cap;
}
arr->values[arr->len++] = val;
return 0;
}
static void js_c_function_finalizer(JSRuntime *rt, JSValue val) static void js_c_function_finalizer(JSRuntime *rt, JSValue val)
{ {
JSObject *p = JS_VALUE_GET_OBJ(val); JSObject *p = JS_VALUE_GET_OBJ(val);
@@ -5219,6 +5212,8 @@ static void free_object(JSRuntime *rt, JSObject *p)
JSShape *sh; JSShape *sh;
JSShapeProperty *pr; JSShapeProperty *pr;
assert(p->header.gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT);
p->free_mark = 1; /* used to tell the object is invalid when p->free_mark = 1; /* used to tell the object is invalid when
freeing cycles */ freeing cycles */
/* free all the fields */ /* free all the fields */
@@ -7580,7 +7575,8 @@ static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop,
JSValueConst func, int def_flags) JSValueConst func, int def_flags)
{ {
(void)def_flags; (void)def_flags;
if (JS_SetPropertyInternal(ctx, ctx->global_obj, prop, func) < 0) /* JS_SetPropertyInternal consumes the value, so we must dup it */
if (JS_SetPropertyInternal(ctx, ctx->global_obj, prop, JS_DupValue(ctx, func)) < 0)
return -1; return -1;
return 0; return 0;
} }
@@ -8102,6 +8098,7 @@ static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
case JS_TAG_NULL: case JS_TAG_NULL:
ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
break; break;
case JS_TAG_ARRAY:
case JS_TAG_OBJECT: case JS_TAG_OBJECT:
JS_FreeValue(ctx, val); JS_FreeValue(ctx, val);
return JS_ThrowTypeError(ctx, "cannot convert object to number"); return JS_ThrowTypeError(ctx, "cannot convert object to number");
@@ -8558,6 +8555,7 @@ static JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToP
return JS_AtomToString(ctx, JS_ATOM_null); return JS_AtomToString(ctx, JS_ATOM_null);
case JS_TAG_EXCEPTION: case JS_TAG_EXCEPTION:
return JS_EXCEPTION; return JS_EXCEPTION;
case JS_TAG_ARRAY:
case JS_TAG_OBJECT: case JS_TAG_OBJECT:
return JS_AtomToString(ctx, JS_ATOM_true); return JS_AtomToString(ctx, JS_ATOM_true);
case JS_TAG_FUNCTION_BYTECODE: case JS_TAG_FUNCTION_BYTECODE:
@@ -9245,6 +9243,9 @@ static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
case JS_GC_OBJ_TYPE_JS_CONTEXT: case JS_GC_OBJ_TYPE_JS_CONTEXT:
printf("[js_context]"); printf("[js_context]");
break; break;
case JS_GC_OBJ_TYPE_ARRAY:
printf("[array]");
break;
default: default:
printf("[unknown %d]", p->gc_obj_type); printf("[unknown %d]", p->gc_obj_type);
break; break;
@@ -9585,6 +9586,12 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
} }
} }
break; break;
case JS_TAG_ARRAY:
if (tag1 != tag2)
res = FALSE;
else
res = JS_VALUE_GET_PTR(op1) == JS_VALUE_GET_PTR(op2);
break;
case JS_TAG_OBJECT: case JS_TAG_OBJECT:
if (tag1 != tag2) if (tag1 != tag2)
res = FALSE; res = FALSE;
@@ -11844,9 +11851,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
} }
ret = js_method_set_properties(ctx, sp[-1], atom, 0, obj); ret = js_method_set_properties(ctx, sp[-1], atom, 0, obj);
if (ret >= 0) { if (ret >= 0) {
/* JS_SetProperty consumes value, so don't free sp[-1] on success */
ret = JS_SetProperty(ctx, obj, atom, value); ret = JS_SetProperty(ctx, obj, atom, value);
} else {
JS_FreeValue(ctx, sp[-1]);
} }
JS_FreeValue(ctx, sp[-1]);
if (is_computed) { if (is_computed) {
JS_FreeAtom(ctx, atom); JS_FreeAtom(ctx, atom);
JS_FreeValue(ctx, sp[-2]); JS_FreeValue(ctx, sp[-2]);

View File

@@ -3462,7 +3462,9 @@ return {
test_gc_cycle_array_self: function() { test_gc_cycle_array_self: function() {
var arr = [] var arr = []
push(arr, arr) for (var i = 0; i < 10; i++) {
push(arr, arr)
}
if (arr[0] != arr) throw "array self cycle failed" if (arr[0] != arr) throw "array self cycle failed"
}, },