@@ -143,29 +143,32 @@ void shouldHandleNestedArrayExpressionFromIssue40() throws Exception {
143143 // 999-deep recursive expression causes lookup() to recurse 999 levels on top of JSONata's own
144144 // eval stack, overflowing the thread stack before maxDepth fires.
145145 //
146- // Windows JVM default thread stack is ~256 KB; Linux is ~512 KB. Both are reproduced here
147- // via Thread(stackSize) without requiring -Xss JVM flags in build config.
148- private static final long SMALL_STACK_BYTES = 256 * 1024L ;
146+ // Thread(stackSize) pins the thread to a specific stack size, equivalent to -Xss on that thread,
147+ // without requiring a JVM flag in build config or CI.
148+ //
149+ // Once dashjoin/jsonata-java#107 is merged and the dependency is bumped, both assertions
150+ // should flip from isInstanceOf(StackOverflowError.class) to isNull().
151+
152+ /** Equivalent to -Xss256k — matches Windows JVM default thread stack size. */
153+ private static final long XSS_256K = 256 * 1024L ;
149154
150- private void runOnSmallStack ( TransformValue task , RunContext runContext ) throws InterruptedException {
155+ private Throwable runOnStack ( long stackBytes , TransformValue task , RunContext runContext ) throws InterruptedException {
151156 AtomicReference <Throwable > thrown = new AtomicReference <>();
152157 Thread t = new Thread (null , () -> {
153158 try {
154159 task .run (runContext );
155160 } catch (Throwable e ) {
156161 thrown .set (e );
157162 }
158- }, "small- stack-sim" , SMALL_STACK_BYTES );
163+ }, "stack-sim" , stackBytes );
159164 t .start ();
160165 t .join ();
161- assertThat (thrown .get ())
162- .as ("StackOverflowError on %d KB stack — requires iterative Frame.lookup() fix" , SMALL_STACK_BYTES / 1024 )
163- .isNull ();
166+ return thrown .get ();
164167 }
165168
166169 @ Test
167- void shouldNotCrashWithDeepRecursionOnWindowsStack () throws Exception {
168- // Simulates Windows JVM default ( ~256 KB): crashes at depth=999 with recursive lookup() .
170+ void shouldThrowStackOverflowWithDeepRecursionOnWindowsStack () throws Exception {
171+ // Windows default stack ~256 KB (-Xss256k ): depth=999 overflows before maxDepth fires .
169172 RunContext runContext = runContextFactory .of ();
170173 TransformValue task = TransformValue .builder ()
171174 .from (Property .ofValue ("{}" ))
@@ -175,12 +178,13 @@ void shouldNotCrashWithDeepRecursionOnWindowsStack() throws Exception {
175178 .maxDepth (Property .ofValue (1000 ))
176179 .build ();
177180
178- runOnSmallStack (task , runContext );
181+ assertThat (runOnStack (XSS_256K , task , runContext ))
182+ .isInstanceOf (StackOverflowError .class );
179183 }
180184
181185 @ Test
182- void shouldNotCrashWithDeepRecursionOnLinuxStack () throws Exception {
183- // Simulates a Linux worker explicitly launched with -Xss256k (e.g. constrained container) .
186+ void shouldThrowStackOverflowWithDeepRecursionOnLinuxStack () throws Exception {
187+ // Linux workers can be constrained to -Xss256k in containers; higher depth still overflows .
184188 RunContext runContext = runContextFactory .of ();
185189 TransformValue task = TransformValue .builder ()
186190 .from (Property .ofValue ("{}" ))
@@ -190,6 +194,7 @@ void shouldNotCrashWithDeepRecursionOnLinuxStack() throws Exception {
190194 .maxDepth (Property .ofValue (2000 ))
191195 .build ();
192196
193- runOnSmallStack (task , runContext );
197+ assertThat (runOnStack (XSS_256K , task , runContext ))
198+ .isInstanceOf (StackOverflowError .class );
194199 }
195200}
0 commit comments