CVE-2025-43457
Overview
Impact:
Description: Processing maliciously crafted web content may lead to an unexpected Safari crash
Researchers: Gary Kwong, Hossein Lotfi (@hosselot) of Trend Micro Zero Day Initiative
Key insight. Representing a constant-size array and its butterfly as one fused DFG node hid the butterfly from allocation-sinking, GC and materialization analyses, so elimination could leave the array referencing an unmaterialized/freed butterfly; splitting it into NewButterflyWithSize + NewArrayWithButterfly makes the butterfly a first-class, correctly-tracked allocation.
Patch Details
diff --git a/JSTests/stress/array-allocation-elimination-closure-capture.js b/JSTests/stress/array-allocation-elimination-closure-capture.js
new file mode 100644
index 000000000000..c8235d83e04a
--- /dev/null
+++ b/JSTests/stress/array-allocation-elimination-closure-capture.js
@@ -0,0 +1,207 @@
+function assert(condition, message) {
+ if (!condition)
+ throw new Error(message || "Assertion failed");
+}
+
+// Test array allocation with closure capture scenarios
+
+// Function that creates a closure value
+function createClosureValue(multiplier) {
+ return function(x) {
+ return x * multiplier;
+ };
+}
+
+// Function that allocates an array with fixed size
+function allocateArrayForClosure(baseValue) {
+ const arr = new Array(5);
+ for (let i = 0; i < 5; i++) {
+ arr[i] = baseValue + i;
+ }
+ return arr;
+}
+
+// Function that uses array and captures closure
+function processWithClosure(closureFn, arr) {
+ let result = 0;
+ for (let i = 0; i < arr.length; i++) {
+ result += closureFn(arr[i]);
+ }
+ return result;
+}
+
+// Complex scenario: array allocation, closure capture, and usage all in different functions
+function complexClosureTest(multiplier, baseValue) {
+ const closureFn = createClosureValue(multiplier);
+ const arr = allocateArrayForClosure(baseValue);
+ return processWithClosure(closureFn, arr);
+}
+
+// Scenario where closure modifies array behavior
+function closureModifiesArray(capturedValue) {
+ return function(baseValue) {
+ const arr = new Array(4);
+ for (let i = 0; i < 4; i++) {
+ arr[i] = (baseValue + i) + capturedValue;
+ }
+
+ // Array usage within the closure
+ let sum = 0;
+ for (let i = 0; i < arr.length; i++) {
+ sum += arr[i] * 2;
+ }
+ return sum;
+ };
+}
+
+// Nested closure with array
+function nestedClosureWithArray(outerValue) {
+ return function(middleValue) {
+ return function(baseValue) {
+ const arr = new Array(3);
+ for (let i = 0; i < 3; i++) {
+ arr[i] = baseValue + i + outerValue + middleValue;
+ }
+
+ // Process array using captured values
+ let product = 1;
+ for (let i = 0; i < arr.length; i++) {
+ product *= (arr[i] % 10) || 1; // avoid zero
+ }
+ return product;
+ };
+ };
+}
+
+let escapedArray = null;
+// Array that sometimes escapes through closure
+function conditionalEscapeViaClosure(shouldEscape) {
+
+ return function(baseValue) {
+ const arr = new Array(4);
+ for (let i = 0; i < 4; i++) {
+ arr[i] = baseValue + i * 2;
+ }
+
+ if (shouldEscape) {
+ escapedArray = arr;
+ return arr.length;
+ } else {
+ // Use array locally
+ let sum = 0;
+ for (let i = 0; i < arr.length; i++) {
+ sum += arr[i];
+ }
+ return sum;
+ }
+ };
+}
+
+// Closure that captures array from outer scope
+function captureArrayInClosure(baseValue) {
+ const arr = new Array(6);
+ for (let i = 0; i < 6; i++) {
+ arr[i] = baseValue + i;
+ }
+
+ // Return closure that uses the captured array
+ return function(multiplier) {
+ let result = 0;
+ for (let i = 0; i < arr.length; i++) {
+ result += arr[i] * multiplier;
+ }
+ return result;
+ };
+}
+
+// Multiple arrays with closure interactions
+function multiArrayClosure(factor1, factor2) {
+ return function(baseValue) {
+ const arr1 = new Array(3);
+ const arr2 = new Array(3);
+
+ for (let i = 0; i < 3; i++) {
+ arr1[i] = (baseValue + i) * factor1;
+ arr2[i] = (baseValue + i) * factor2;
+ }
+
+ // Combine arrays using closure variables
+ let combined = 0;
+ for (let i = 0; i < 3; i++) {
+ combined += arr1[i] + arr2[i];
+ }
+ return combined;
+ };
+}
+
+noInline(complexClosureTest);
+noInline(closureModifiesArray);
+noInline(nestedClosureWithArray);
+noInline(conditionalEscapeViaClosure);
+noInline(captureArrayInClosure);
+noInline(multiArrayClosure);
+
+// Run tests
+for (let i = 0; i < testLoopCount; i++) {
+ const baseValue = i % 8;
+ const multiplier = 1 + (i % 3); // multipliers 1-3
+
+ // Test complex closure scenario (array size 5)
+ let result1 = complexClosureTest(multiplier, baseValue);
+ let expected1 = 0;
+ for (let j = 0; j < 5; j++) {
+ expected1 += (baseValue + j) * multiplier;
+ }
+ assert(result1 === expected1, `complexClosureTest(${multiplier}, ${baseValue}) failed: got ${result1}, expected ${expected1}`);
+
+ // Test closure that modifies array behavior (array size 4)
+ const closureFn = closureModifiesArray(i % 5);
+ let result2 = closureFn(baseValue);
+ let expected2 = 0;
+ for (let j = 0; j < 4; j++) {
+ expected2 += (baseValue + j + (i % 5)) * 2;
+ }
+ assert(result2 === expected2, `closureModifiesArray test failed: got ${result2}, expected ${expected2}`);
+
+ // Test nested closure (array size 3)
+ const nestedFn = nestedClosureWithArray(i % 3)(i % 4);
+ let result3 = nestedFn(baseValue);
+ let expected3 = 1;
+ for (let j = 0; j < 3; j++) {
+ let val = (baseValue + j + (i % 3) + (i % 4)) % 10;
+ if (val !== 0) expected3 *= val;
+ }
+ assert(result3 === expected3, `nestedClosureWithArray test failed: got ${result3}, expected ${expected3}`);
+
+ // Test conditional escape via closure - non-escaping case (array size 4)
+ const condEscapeFn = conditionalEscapeViaClosure(false);
+ let result4 = condEscapeFn(baseValue);
+ let expected4 = 0;
+ for (let j = 0; j < 4; j++) {
+ expected4 += baseValue + j * 2;
+ }
+ assert(result4 === expected4, `conditionalEscapeViaClosure(false) test failed: got ${result4}, expected ${expected4}`);
+
+ // Test conditional escape via closure - escaping case (array size 4)
+ const condEscapeFn2 = conditionalEscapeViaClosure(true);
+ let result5 = condEscapeFn2(baseValue);
+ assert(result5 === 4, `conditionalEscapeViaClosure(true) test failed: got ${result5}, expected 4`);
+
+ // Test capture array in closure (array size 6)
+ const capturedFn = captureArrayInClosure(baseValue);
+ let result6 = capturedFn(multiplier);
+ let expected6 = 0;
+ for (let j = 0; j < 6; j++) {
Background
Advisory: Apple Security Advisory
| Attribute | Value |
|---|---|
| CVE | CVE-2025-43457 |
| Bugzilla | 298606 |
| Component | JSC |
| Bug Class | UAF |
| Severity | medium |
| Commit | f014a32890763b13… |
| Advisory | Apple Advisory |
Terminology
- DFG allocation sinking / elimination — A DFG optimization that defers or removes object allocations, rematerializing them only on paths that need them (e.g. at OSR exit).
- Butterfly — The out-of-line storage for a JSObject/JSArray’s indexed and named properties; a separate heap allocation from the cell.
- Phantom / Materialize nodes — DFG nodes representing a sunk allocation (Phantom) and its later rebuild (Materialize) during OSR exit.
- DFGDoesGC / DFGMayExit / Clobberize — Analyses that model whether a node can GC, can exit, and what state it reads/writes; they must know every allocation to be correct.
Root Cause Analysis
In the DFG JIT, a JS array allocation of known constant length was represented by a single fused node type (NewArrayWithConstantSize, with Phantom/Materialize variants) that allocated both the JSArray cell and its backing butterfly. That fusion hid the butterfly allocation from the DFG’s allocation-sinking and supporting analyses: the butterfly was not a first-class allocation the way the object was, so allocation elimination / object sinking, GC modeling (DFGDoesGC), may-exit analysis (DFGMayExit), and effect modeling (DFGClobberize) reasoned about the array without correctly accounting for a separately-lived butterfly. Under array-allocation-elimination (the added tests cover closure-capture, conditional-usage and cross-function cases), that mismatch let the optimizer sink/eliminate or materialize the allocation in a way where the array could reference a butterfly that had not been (re)materialized or had been treated as not needing GC — a use-after-free / stale-butterfly access when the array was materialized at OSR exit or used after sinking. The fix decomposes the operation: a new NewButterflyWithSize node explicitly allocates the butterfly, and NewArrayWithButterfly (with PhantomNewArrayWithButterfly / PhantomNewButterflyWithSize / MaterializeNewButterflyWithSize variants) builds the array from that butterfly. ConstantFoldingPhase now inserts a NewButterflyWithSize and converts the array node to NewArrayWithButterfly (running CFA carefully so the default folder doesn’t re-handle it), and every analysis (DoesGC, MayExit, FixupPhase, LoopUnrollingPhase, BackwardsPropagation, Clobberize, AbstractInterpreter, CloneHelper, Node conversion) is updated to know both node kinds. The restored invariant is that the butterfly is a first-class, separately-tracked allocation, so allocation sinking, materialization and GC modeling handle its lifetime correctly and never leave the array pointing at an unmaterialized/freed butterfly.
Attack Path
1. Write array-allocating hot code
Run JS that allocates constant-size arrays in patterns amenable to allocation elimination (captured in closures, conditionally used, or passed across inlined functions), so the DFG creates NewArrayWithConstantSize nodes.
2. Trigger allocation sinking
Get the DFG to sink/eliminate the fused array allocation, which mis-models the butterfly as not separately allocated or not GC-relevant.
3. Force materialization / GC
Cause an OSR exit that materializes the sunk array, or a GC, at a point where the butterfly is stale or unmaterialized.
4. Use-after-free
The array is used with a freed/unmaterialized butterfly, corrupting memory in the WebContent process.
Impact Assessment
A DFG allocation-elimination correctness bug where a fused array-plus-butterfly node hid the butterfly from GC/sinking/materialization analyses, producing a use-after-free / stale-butterfly access in the WebContent process. It is reachable from ordinary JS array code that the JIT optimizes, and while the observed effect is a crash, an array pointing at a controlled freed butterfly is a strong path toward type confusion and read/write. Confined to WebContent. Rated medium.
Audit Directions
Other fused allocations
Look for DFG nodes that allocate a cell plus out-of-line storage in one node (typed arrays, spreads, arguments) and verify sinking/materialization tracks each sub-allocation separately.
Node-kind coverage
Grep DFG phase switch statements for the new NewButterflyWithSize/NewArrayWithButterfly cases to ensure every analysis (DoesGC, MayExit, Clobberize, AI, safe-to-execute) handles them, since a missed case reintroduces the mismatch.
Materialization order
Audit MaterializeNewButterflyWithSize/PhantomNewArrayWithButterfly handling at OSR exit to confirm the butterfly is materialized before the array that references it.
Changed Functions
| Function | File | Change | Note |
|---|---|---|---|
DFGConstantFoldingPhase (array-with-constant-size folding) |
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp |
modified | Inserts an explicit NewButterflyWithSize node and converts the array to NewArrayWithButterfly (gated by !hasAnyArrayStorage, executing CFA and marking alreadyHandled) instead of the fused convertToNewArrayWithConstantSize. |
Node::convertToNewArrayWithButterfly (was convertToNewArrayWithConstantSize) |
Source/JavaScriptCore/dfg/DFGNode.cpp |
modified | New conversion that builds a NewArrayWithButterfly from a separate butterfly node, replacing the fused constant-size conversion. |
DFGDoesGC / DFGMayExit / DFGFixupPhase / DFGLoopUnrollingPhase / DFGBackwardsPropagationPhase / DFGClobberize / DFGAbstractInterpreterInlines |
Source/JavaScriptCore/dfg/DFGDoesGC.cpp |
modified | All updated to handle NewButterflyWithSize / NewArrayWithButterfly (and their Phantom/Materialize forms) so GC, exit, effect and type analyses track the butterfly as a first-class allocation. |
Files Changed
Source Files
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.hSource/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cppSource/JavaScriptCore/dfg/DFGClobberize.hSource/JavaScriptCore/dfg/DFGCloneHelper.hSource/JavaScriptCore/dfg/DFGConstantFoldingPhase.cppSource/JavaScriptCore/dfg/DFGDoesGC.cppSource/JavaScriptCore/dfg/DFGFixupPhase.cppSource/JavaScriptCore/dfg/DFGLoopUnrollingPhase.cppSource/JavaScriptCore/dfg/DFGMayExit.cppSource/JavaScriptCore/dfg/DFGNode.cppSource/JavaScriptCore/dfg/DFGNode.hSource/JavaScriptCore/dfg/DFGNodeType.hSource/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cppSource/JavaScriptCore/dfg/DFGObjectMaterializationData.hSource/JavaScriptCore/dfg/DFGOperations.cppSource/JavaScriptCore/dfg/DFGOperations.hSource/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cppSource/JavaScriptCore/dfg/DFGPromotedHeapLocation.hSource/JavaScriptCore/dfg/DFGSafeToExecute.hSource/JavaScriptCore/dfg/DFGSpeculativeJIT.cppSource/JavaScriptCore/dfg/DFGSpeculativeJIT.hSource/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cppSource/JavaScriptCore/dfg/DFGSpeculativeJIT64.cppSource/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cppSource/JavaScriptCore/dfg/DFGUseKind.hSource/JavaScriptCore/dfg/DFGValidate.cppSource/JavaScriptCore/ftl/FTLCapabilities.cppSource/JavaScriptCore/ftl/FTLExitTimeObjectMaterialization.cppSource/JavaScriptCore/ftl/FTLLowerDFGToB3.cppSource/JavaScriptCore/ftl/FTLOperations.cppSource/JavaScriptCore/runtime/IndexingType.cppSource/JavaScriptCore/runtime/IndexingType.h
Test Files
JSTests/stress/array-allocation-elimination-closure-capture.jsJSTests/stress/array-allocation-elimination-conditional-usage.jsJSTests/stress/array-allocation-elimination-cross-function.js
Regression Test
The fix ships this proof-of-concept / regression test:
diff --git a/JSTests/stress/array-allocation-elimination-closure-capture.js b/JSTests/stress/array-allocation-elimination-closure-capture.js
new file mode 100644
index 000000000000..c8235d83e04a
--- /dev/null
+++ b/JSTests/stress/array-allocation-elimination-closure-capture.js
@@ -0,0 +1,207 @@
+function assert(condition, message) {
+ if (!condition)
+ throw new Error(message || "Assertion failed");
+}
+
+// Test array allocation with closure capture scenarios
+
+// Function that creates a closure value
+function createClosureValue(multiplier) {
+ return function(x) {
+ return x * multiplier;
+ };
+}
+
+// Function that allocates an array with fixed size
+function allocateArrayForClosure(baseValue) {
+ const arr = new Array(5);
+ for (let i = 0; i < 5; i++) {
+ arr[i] = baseValue + i;
+ }
+ return arr;
+}
+
+// Function that uses array and captures closure
+function processWithClosure(closureFn, arr) {
+ let result = 0;
+ for (let i = 0; i < arr.length; i++) {
+ result += closureFn(arr[i]);
+ }
+ return result;
+}
+
+// Complex scenario: array allocation, closure capture, and usage all in different functions
+function complexClosureTest(multiplier, baseValue) {
+ const closureFn = createClosureValue(multiplier);
+ const arr = allocateArrayForClosure(baseValue);
+ return processWithClosure(closureFn, arr);
+}
+
+// Scenario where closure modifies array behavior
+function closureModifiesArray(capturedValue) {
+ return function(baseValue) {
+ const arr = new Array(4);
+ for (let i = 0; i < 4; i++) {
+ arr[i] = (baseValue + i) + capturedValue;
+ }
+
+ // Array usage within the closure
+ let sum = 0;
+ for (let i = 0; i < arr.length; i++) {
+ sum += arr[i] * 2;
+ }
+ return sum;
+ };
+}
+
+// Nested closure with array
+function nestedClosureWithArray(outerValue) {
+ return function(middleValue) {
+ return function(baseValue) {
+ const arr = new Array(3);
+ for (let i = 0; i < 3; i++) {
+ arr[i] = baseValue + i + outerValue + middleValue;
+ }
+
+ // Process array using captured values
+ let product = 1;
+ for (let i = 0; i < arr.length; i++) {
+ product *= (arr[i] % 10) || 1; // avoid zero
+ }
+ return product;
+ };
+ };
+}
+
+let escapedArray = null;
+// Array that sometimes escapes through closure
+function conditionalEscapeViaClosure(shouldEscape) {
+
+ return function(baseValue) {
+ const arr = new Array(4);
+ for (let i = 0; i < 4; i++) {
+ arr[i] = baseValue + i * 2;
+ }
+
+ if (shouldEscape) {
+ escapedArray = arr;
+ return arr.length;
+ } else {
+ // Use array locally
+ let sum = 0;
+ for (let i = 0; i < arr.length; i++) {
+ sum += arr[i];
+ }
+ return sum;
+ }
+ };
+}
+
+// Closure that captures array from outer scope
+function captureArrayInClosure(baseValue) {
+ const arr = new Array(6);
+ for (let i = 0; i < 6; i++) {
+ arr[i] = baseValue + i;
+ }
+
+ // Return closure that uses the captured array
+ return function(multiplier) {
+ let result = 0;
+ for (let i = 0; i < arr.length; i++) {
+ result += arr[i] * multiplier;
+ }
+ return result;
+ };
+}
+
+// Multiple arrays with closure interactions
+function multiArrayClosure(factor1, factor2) {
+ return function(baseValue) {
+ const arr1 = new Array(3);
+ const arr2 = new Array(3);
+
+ for (let i = 0; i < 3; i++) {
+ arr1[i] = (baseValue + i) * factor1;
+ arr2[i] = (baseValue + i) * factor2;
+ }
+
+ // Combine arrays using closure variables
+ let combined = 0;
+ for (let i = 0; i < 3; i++) {
+ combined += arr1[i] + arr2[i];
+ }
+ return combined;
+ };
+}
+
+noInline(complexClosureTest);
+noInline(closureModifiesArray);
+noInline(nestedClosureWithArray);
+noInline(conditionalEscapeViaClosure);
+noInline(captureArrayInClosure);
+noInline(multiArrayClosure);
+
+// Run tests
+for (let i = 0; i < testLoopCount; i++) {
+ const baseValue = i % 8;
+ const multiplier = 1 + (i % 3); // multipliers 1-3
+
+ // Test complex closure scenario (array size 5)
+ let result1 = complexClosureTest(multiplier, baseValue);
+ let expected1 = 0;
+ for (let j = 0; j < 5; j++) {
+ expected1 += (baseValue + j) * multiplier;
+ }
+ assert(result1 === expected1, `complexClosureTest(${multiplier}, ${baseValue}) failed: got ${result1}, expected ${expected1}`);
+
+ // Test closure that modifies array behavior (array size 4)
+ const closureFn = closureModifiesArray(i % 5);
+ let result2 = closureFn(baseValue);
+ let expected2 = 0;
+ for (let j = 0; j < 4; j++) {
+ expected2 += (baseValue + j + (i % 5)) * 2;
+ }
+ assert(result2 === expected2, `closureModifiesArray test failed: got ${result2}, expected ${expected2}`);
+
+ // Test nested closure (array size 3)
+ const nestedFn = nestedClosureWithArray(i % 3)(i % 4);
+ let result3 = nestedFn(baseValue);
+ let expected3 = 1;
+ for (let j = 0; j < 3; j++) {
+ let val = (baseValue + j + (i % 3) + (i % 4)) % 10;
+ if (val !== 0) expected3 *= val;
+ }
+ assert(result3 === expected3, `nestedClosureWithArray test failed: got ${result3}, expected ${expected3}`);
+
+ // Test conditional escape via closure - non-escaping case (array size 4)
+ const condEscapeFn = conditionalEscapeViaClosure(false);
+ let result4 = condEscapeFn(baseValue);
+ let expected4 = 0;
+ for (let j = 0; j < 4; j++) {
+ expected4 += baseValue + j * 2;
+ }
+ assert(result4 === expected4, `conditionalEscapeViaClosure(false) test failed: got ${result4}, expected ${expected4}`);
+
+ // Test conditional escape via closure - escaping case (array size 4)
+ const condEscapeFn2 = conditionalEscapeViaClosure(true);
+ let result5 = condEscapeFn2(baseValue);
+ assert(result5 === 4, `conditionalEscapeViaClosure(true) test failed: got ${result5}, expected 4`);
+
+ // Test capture array in closure (array size 6)
+ const capturedFn = captureArrayInClosure(baseValue);
+ let result6 = capturedFn(multiplier);
+ let expected6 = 0;
+ for (let j = 0; j < 6; j++) {
+ expected6 += (baseValue + j) * multiplier;
+ }
+ assert(result6 === expected6, `captureArrayInClosure test failed: got ${result6}, expected ${expected6}`);
+
+ // Test multiple arrays with closure (arrays size 3 each)
+ const multiArrayFn = multiArrayClosure(2, 3);
+ let result7 = multiArrayFn(baseValue);
+ let expected7 = 0;
+ for (let j = 0; j < 3; j++) {
+ expected7 += (baseValue + j) * 2 + (baseValue + j) * 3; // factor1 + factor2
+ }
+ assert(result7 === expected7, `multiArrayClosure test failed: got ${result7}, expected ${expected7}`);
+}
diff --git a/JSTests/stress/array-allocation-elimination-conditional-usage.js b/JSTests/stress/array-allocation-elimination-conditional-usage.js
new file mode 100644
index 000000000000..9875274dbee5
--- /dev/null
+++ b/JSTests/stress/array-allocation-elimination-conditional-usage.js
@@ -0,0 +1,108 @@
+function assert(condition, message) {
+ if (!condition)
+ throw new Error(message || "Assertion failed");
+}
+
+// Test conditional array usage - some branches use array, others don't
+function testConditionalArrayUsage(useArray, writeValue) {
+ const arr = new Array(5);
+
+ if (useArray) {
+ // Branch that uses the array
+ arr[0] = writeValue;
+ arr[2] = writeValue + 10;
+ arr[4] = writeValue + 20;
+
+ let sum = 0;
+ for (let i = 0; i < 5; i++) {
+ if (arr[i] !== undefined) {
+ sum += arr[i];
+ }
+ }
+ return sum;
+ } else {
+ // Branch that doesn't use the array at all
+ return writeValue * 3 + 30; // Same result as the sum above when writeValue = 0
+ }
+}
+
+// Test with nested conditionals
+function testNestedConditionalArray(outer, inner, value) {
+ const arr = new Array(3);
+
+ if (outer) {
+ arr[0] = value;
+
+ if (inner) {
+ arr[1] = value * 2;
+ arr[2] = value * 3;
+ return arr[0] + arr[1] + arr[2]; // value * 6
+ } else {
+ // Only use first element
+ return arr[0]; // value
+ }
+ } else {
+ // Don't use array at all
+ return inner ? value * 6 : value;
+ }
+}
+
+// Test with early return that skips array usage
+function testEarlyReturnArray(shouldReturn, value) {
+ const arr = new Array(4);
+
+ if (shouldReturn) {
+ return value * 42;
+ }
+
+ // This array usage should be eliminated when shouldReturn is true
+ arr[0] = value;
+ arr[1] = value + 1;
+ arr[3] = value + 3;
+
+ return arr[0] + arr[1] + arr[3]; // value * 3 + 4
+}
+
+noInline(testConditionalArrayUsage);
+noInline(testNestedConditionalArray);
+noInline(testEarlyReturnArray);
+
+// Run tests to trigger optimization
+for (let i = 0; i < testLoopCount; i++) {
+ // Test the branch that uses the array
+ let result1 = testConditionalArrayUsage(true, i % 100);
+ let expected1 = (i % 100) + (i % 100 + 10) + (i % 100 + 20);
+ assert(result1 === expected1, `testConditionalArrayUsage(true, ${i % 100}) failed: got ${result1}, expected ${expected1}`);
+
+ // Test the branch that doesn't use the array
+ let result2 = testConditionalArrayUsage(false, i % 100);
+ let expected2 = (i % 100) * 3 + 30;
+ assert(result2 === expected2, `testConditionalArrayUsage(false, ${i % 100}) failed: got ${result2}, expected ${expected2}`);
+
... (truncated)