From 320a97bf3b8dd57c35376dfedcb5cb67aef44051 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Sat, 24 Oct 2026 20:19:50 -0500 Subject: [PATCH] Throw Node's TypeError for util.diff of two distinct empty sequences util.diff([], []) with two arrays read one element past its trace row while backtracking. Node's myersDiff answers undefined for two empty sequences, and util.diff's ArrayPrototypeReverse throws "Cannot convert undefined or null to object"; scr_util_diff now throws the same TypeError before it allocates the trace. --- packages/runtime/src/scr_util_callable.c | 11 +++++++++++ tests/corpus/7705-util-diff.cjs | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/runtime/src/scr_util_callable.c b/packages/runtime/src/scr_util_callable.c index df6a68272..574b9d423 100644 --- a/packages/runtime/src/scr_util_callable.c +++ b/packages/runtime/src/scr_util_callable.c @@ -881,6 +881,17 @@ ScrDyn *scr_util_diff(ScrDyn *actual, ScrDyn *expected) { scr_throw_error_msg_code(SCR_ERR_RANGE, message, (size_t)n, "ERR_OUT_OF_RANGE"); return NULL; } + /* Two distinct empty sequences: Node's myersDiff reads past its + * one-element state, finds no end and answers undefined, which the + * ArrayPrototypeReverse of util.diff rejects. The trace below would be read + * out of bounds. */ + if (max == 0) { + uc_diff_seq_free(&a); + uc_diff_seq_free(&e); + static const char message[] = "Cannot convert undefined or null to object"; + scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + return NULL; + } size_t width = 2 * max + 1; int32_t *v = calloc(width, sizeof *v); int32_t **trace = malloc((max + 1) * sizeof *trace); diff --git a/tests/corpus/7705-util-diff.cjs b/tests/corpus/7705-util-diff.cjs index 9e52fd165..489586a3b 100644 --- a/tests/corpus/7705-util-diff.cjs +++ b/tests/corpus/7705-util-diff.cjs @@ -7,7 +7,7 @@ const show = (a, e) => { try { console.log(JSON.stringify(util.diff(a, e))); } catch (err) { console.log(err.name, err.code, err.message); } }; show("abc", "abd"); show("kitten", "sitting"); show("", "x"); show("same", "same"); show("", ""); -show(["a", "b", "c"], ["a", "c", "d"]); show(["x"], []); show([], ["y", "z"]); +show(["a", "b", "c"], ["a", "c", "d"]); show(["x"], []); show([], ["y", "z"]); show([], []); const arr = ["p"]; show(arr, arr); show(1, 1); show(1, 2); show("a", 2); show(["a", 1], ["a"]); show(["a"], [, "b"]); show(null, "x"); show("x", undefined); show("héllo😀", "hello😀"); show({}, "x"); show("the quick brown fox", "the quack brown box");