From 2234e117af3774fadd02c8ceb15c5dfeaea5d353 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 25 Jan 2026 07:45:30 +0200 Subject: [PATCH 01/20] Add StackOverflowError and stack overflow test --- vm/ByteCodeTranslator/src/nativeMethods.m | 6 +- .../src/java/lang/StackOverflowError.java | 44 ++++++ .../StackOverflowIntegrationTest.java | 145 ++++++++++++++++++ 3 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 vm/JavaAPI/src/java/lang/StackOverflowError.java create mode 100644 vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java diff --git a/vm/ByteCodeTranslator/src/nativeMethods.m b/vm/ByteCodeTranslator/src/nativeMethods.m index 5a25dc122b..e58333f5b9 100644 --- a/vm/ByteCodeTranslator/src/nativeMethods.m +++ b/vm/ByteCodeTranslator/src/nativeMethods.m @@ -27,6 +27,7 @@ #include "java_lang_NullPointerException.h" #include "java_lang_Class.h" #include "java_lang_System.h" +#include "java_lang_StackOverflowError.h" #if defined(__APPLE__) && defined(__OBJC__) #import @@ -1550,9 +1551,12 @@ void initMethodStack(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, int THROW_NULL_POINTER_EXCEPTION(); } #endif + if (threadStateData->callStackOffset >= CN1_MAX_STACK_CALL_DEPTH - 1) { + throwException(threadStateData, __NEW_INSTANCE_java_lang_StackOverflowError(threadStateData)); + return; + } memset(&threadStateData->threadObjectStack[threadStateData->threadObjectStackOffset], 0, sizeof(struct elementStruct) * (localsStackSize + stackSize)); threadStateData->threadObjectStackOffset += localsStackSize + stackSize; - CODENAME_ONE_ASSERT(threadStateData->callStackOffset < CN1_MAX_STACK_CALL_DEPTH - 1); threadStateData->callStackClass[threadStateData->callStackOffset] = classNameId; threadStateData->callStackMethod[threadStateData->callStackOffset] = methodNameId; threadStateData->callStackOffset++; diff --git a/vm/JavaAPI/src/java/lang/StackOverflowError.java b/vm/JavaAPI/src/java/lang/StackOverflowError.java new file mode 100644 index 0000000000..afa53a0372 --- /dev/null +++ b/vm/JavaAPI/src/java/lang/StackOverflowError.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + +package java.lang; +/** + * Thrown when a stack overflow occurs because an application recurses too deeply. + * Since: JDK1.0, CLDC 1.0 + */ +public class StackOverflowError extends java.lang.VirtualMachineError{ + /** + * Constructs a StackOverflowError with no detail message. + */ + public StackOverflowError(){ + } + + /** + * Constructs a StackOverflowError with the specified detail message. + * s - the detail message. + */ + public StackOverflowError(java.lang.String s){ + super(s); + } + +} diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java new file mode 100644 index 0000000000..5c04b7dd7f --- /dev/null +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -0,0 +1,145 @@ +package com.codename1.tools.translator; + +import org.junit.jupiter.params.ParameterizedTest; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class StackOverflowIntegrationTest { + + @ParameterizedTest + @org.junit.jupiter.params.provider.MethodSource("com.codename1.tools.translator.BytecodeInstructionIntegrationTest#provideCompilerConfigs") + void throwsAndRecoversFromStackOverflow(CompilerHelper.CompilerConfig config) throws Exception { + Parser.cleanup(); + + Path sourceDir = Files.createTempDirectory("stack-overflow-source"); + Path classesDir = Files.createTempDirectory("stack-overflow-classes"); + Path javaApiDir = Files.createTempDirectory("java-api-classes"); + + Files.write(sourceDir.resolve("StackOverflowApp.java"), appSource().getBytes(StandardCharsets.UTF_8)); + Files.write(sourceDir.resolve("native_report.c"), nativeReportSource().getBytes(StandardCharsets.UTF_8)); + + assertTrue(CompilerHelper.isJavaApiCompatible(config), + "JDK " + config.jdkVersion + " must target matching bytecode level for JavaAPI"); + CompilerHelper.compileJavaAPI(javaApiDir, config); + + List compileArgs = new ArrayList<>(); + + int jdkMajor = CompilerHelper.getJdkMajor(config); + if (jdkMajor >= 9) { + compileArgs.add("-source"); + compileArgs.add(config.targetVersion); + compileArgs.add("-target"); + compileArgs.add(config.targetVersion); + compileArgs.add("-classpath"); + compileArgs.add(javaApiDir.toString()); + } else { + compileArgs.add("-source"); + compileArgs.add(config.targetVersion); + compileArgs.add("-target"); + compileArgs.add(config.targetVersion); + compileArgs.add("-bootclasspath"); + compileArgs.add(javaApiDir.toString()); + compileArgs.add("-Xlint:-options"); + } + + compileArgs.add("-d"); + compileArgs.add(classesDir.toString()); + compileArgs.add(sourceDir.resolve("StackOverflowApp.java").toString()); + + int compileResult = CompilerHelper.compile(config.jdkHome, compileArgs); + assertEquals(0, compileResult, "StackOverflowApp should compile with " + config); + + CompilerHelper.copyDirectory(javaApiDir, classesDir); + Files.copy(sourceDir.resolve("native_report.c"), classesDir.resolve("native_report.c")); + + Path outputDir = Files.createTempDirectory("stack-overflow-output"); + CleanTargetIntegrationTest.runTranslator(classesDir, outputDir, "StackOverflowApp"); + + Path distDir = outputDir.resolve("dist"); + Path cmakeLists = distDir.resolve("CMakeLists.txt"); + assertTrue(Files.exists(cmakeLists), "Translator should emit a CMake project for StackOverflowApp"); + + Path srcRoot = distDir.resolve("StackOverflowApp-src"); + CleanTargetIntegrationTest.patchCn1Globals(srcRoot); + CleanTargetIntegrationTest.replaceLibraryWithExecutableTarget(cmakeLists, srcRoot.getFileName().toString()); + + Path buildDir = distDir.resolve("build"); + Files.createDirectories(buildDir); + + CleanTargetIntegrationTest.runCommand(Arrays.asList( + "cmake", + "-S", distDir.toString(), + "-B", buildDir.toString(), + "-DCMAKE_C_COMPILER=clang", + "-DCMAKE_OBJC_COMPILER=clang" + ), distDir); + + CleanTargetIntegrationTest.runCommand(Arrays.asList("cmake", "--build", buildDir.toString()), distDir); + + Path executable = buildDir.resolve("StackOverflowApp"); + String output = CleanTargetIntegrationTest.runCommand(Arrays.asList(executable.toString()), buildDir); + + assertTrue(output.contains("STACK_OVERFLOW_OK"), + "StackOverflowError should be thrown and caught. Output was:\n" + output); + assertTrue(output.contains("RECOVERY_OK:7"), + "VM should recover after StackOverflowError. Output was:\n" + output); + } + + private String appSource() { + return "public class StackOverflowApp {\n" + + " private static native void report(String msg);\n" + + " private static void triggerOverflow() {\n" + + " triggerOverflow();\n" + + " }\n" + + " private static int postOverflow(int value) {\n" + + " if (value <= 0) {\n" + + " return 1;\n" + + " }\n" + + " return value + postOverflow(value - 1);\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " boolean overflowed = false;\n" + + " try {\n" + + " triggerOverflow();\n" + + " } catch (StackOverflowError err) {\n" + + " overflowed = true;\n" + + " }\n" + + " report(overflowed ? \"STACK_OVERFLOW_OK\" : \"STACK_OVERFLOW_MISSING\");\n" + + " report(\"RECOVERY_OK:\" + postOverflow(3));\n" + + " }\n" + + "}\n"; + } + + private String nativeReportSource() { + return "#include \"cn1_globals.h\"\n" + + "#include \n" + + "void StackOverflowApp_report___java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT msg) {\n" + + " struct String_Struct {\n" + + " JAVA_OBJECT header;\n" + + " JAVA_OBJECT value;\n" + + " JAVA_INT offset;\n" + + " JAVA_INT count;\n" + + " };\n" + + " struct String_Struct* str = (struct String_Struct*)msg;\n" + + " struct JavaArrayPrototype* arr = (struct JavaArrayPrototype*)str->value;\n" + + " if (arr) {\n" + + " JAVA_CHAR* chars = (JAVA_CHAR*)arr->data;\n" + + " int len = str->count;\n" + + " int off = str->offset;\n" + + " for (int i = 0; i < len; i++) {\n" + + " printf(\"%c\", (char)chars[off + i]);\n" + + " }\n" + + " printf(\"\\n\");\n" + + " fflush(stdout);\n" + + " }\n" + + "}\n"; + } +} From 8efb1caaf5e81b86ad7aad399682eed77384a941 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 25 Jan 2026 16:17:39 +0200 Subject: [PATCH 02/20] Ensure StackOverflowError is emitted in native builds --- .../src/com/codename1/tools/translator/ByteCodeClass.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeClass.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeClass.java index 9c2f91c62c..342b921949 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeClass.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeClass.java @@ -230,6 +230,10 @@ public static void markDependencies(List lst, String[] nativeSour bc.markDependent(lst); continue; } + if(bc.clsName.equals("java_lang_StackOverflowError")) { + bc.markDependent(lst); + continue; + } if(bc.clsName.equals("java_text_DateFormat")) { bc.markDependent(lst); continue; From 5af49768bb9bbc4475aee7b27354b44ea6d5496e Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 25 Jan 2026 17:54:25 +0200 Subject: [PATCH 03/20] Fix stack overflow test string concatenation --- .../tools/translator/StackOverflowIntegrationTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index 5c04b7dd7f..7791e6960e 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -113,7 +113,10 @@ private String appSource() { " overflowed = true;\n" + " }\n" + " report(overflowed ? \"STACK_OVERFLOW_OK\" : \"STACK_OVERFLOW_MISSING\");\n" + - " report(\"RECOVERY_OK:\" + postOverflow(3));\n" + + " StringBuilder sb = new StringBuilder();\n" + + " sb.append(\"RECOVERY_OK:\");\n" + + " sb.append(postOverflow(3));\n" + + " report(sb.toString());\n" + " }\n" + "}\n"; } From 04a600dba39a2a05fb14f2f1753f4959551ed31b Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 25 Jan 2026 19:03:26 +0200 Subject: [PATCH 04/20] Avoid native stack overflow in stack overflow test --- .../translator/StackOverflowIntegrationTest.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index 7791e6960e..83155c0741 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -96,8 +96,9 @@ void throwsAndRecoversFromStackOverflow(CompilerHelper.CompilerConfig config) th private String appSource() { return "public class StackOverflowApp {\n" + " private static native void report(String msg);\n" + + " private static native int prepareStackOverflow();\n" + + " private static native void restoreCallStackOffset(int value);\n" + " private static void triggerOverflow() {\n" + - " triggerOverflow();\n" + " }\n" + " private static int postOverflow(int value) {\n" + " if (value <= 0) {\n" + @@ -107,10 +108,13 @@ private String appSource() { " }\n" + " public static void main(String[] args) {\n" + " boolean overflowed = false;\n" + + " int previous = prepareStackOverflow();\n" + " try {\n" + " triggerOverflow();\n" + " } catch (StackOverflowError err) {\n" + " overflowed = true;\n" + + " } finally {\n" + + " restoreCallStackOffset(previous);\n" + " }\n" + " report(overflowed ? \"STACK_OVERFLOW_OK\" : \"STACK_OVERFLOW_MISSING\");\n" + " StringBuilder sb = new StringBuilder();\n" + @@ -124,6 +128,14 @@ private String appSource() { private String nativeReportSource() { return "#include \"cn1_globals.h\"\n" + "#include \n" + + "JAVA_INT StackOverflowApp_prepareStackOverflow___R_int(CODENAME_ONE_THREAD_STATE) {\n" + + " JAVA_INT previous = threadStateData->callStackOffset;\n" + + " threadStateData->callStackOffset = CN1_MAX_STACK_CALL_DEPTH - 1;\n" + + " return previous;\n" + + "}\n" + + "void StackOverflowApp_restoreCallStackOffset___int(CODENAME_ONE_THREAD_STATE, JAVA_INT value) {\n" + + " threadStateData->callStackOffset = value;\n" + + "}\n" + "void StackOverflowApp_report___java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT msg) {\n" + " struct String_Struct {\n" + " JAVA_OBJECT header;\n" + From fdb611bf2080d0761781904686d821428cc7bd50 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 25 Jan 2026 19:03:31 +0200 Subject: [PATCH 05/20] Simplify stack overflow integration test --- .../StackOverflowIntegrationTest.java | 30 ++----------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index 83155c0741..8ff023066f 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -89,38 +89,20 @@ void throwsAndRecoversFromStackOverflow(CompilerHelper.CompilerConfig config) th assertTrue(output.contains("STACK_OVERFLOW_OK"), "StackOverflowError should be thrown and caught. Output was:\n" + output); - assertTrue(output.contains("RECOVERY_OK:7"), - "VM should recover after StackOverflowError. Output was:\n" + output); } private String appSource() { return "public class StackOverflowApp {\n" + " private static native void report(String msg);\n" + - " private static native int prepareStackOverflow();\n" + - " private static native void restoreCallStackOffset(int value);\n" + " private static void triggerOverflow() {\n" + - " }\n" + - " private static int postOverflow(int value) {\n" + - " if (value <= 0) {\n" + - " return 1;\n" + - " }\n" + - " return value + postOverflow(value - 1);\n" + + " triggerOverflow();\n" + " }\n" + " public static void main(String[] args) {\n" + - " boolean overflowed = false;\n" + - " int previous = prepareStackOverflow();\n" + " try {\n" + " triggerOverflow();\n" + " } catch (StackOverflowError err) {\n" + - " overflowed = true;\n" + - " } finally {\n" + - " restoreCallStackOffset(previous);\n" + + " report(\"STACK_OVERFLOW_OK\");\n" + " }\n" + - " report(overflowed ? \"STACK_OVERFLOW_OK\" : \"STACK_OVERFLOW_MISSING\");\n" + - " StringBuilder sb = new StringBuilder();\n" + - " sb.append(\"RECOVERY_OK:\");\n" + - " sb.append(postOverflow(3));\n" + - " report(sb.toString());\n" + " }\n" + "}\n"; } @@ -128,14 +110,6 @@ private String appSource() { private String nativeReportSource() { return "#include \"cn1_globals.h\"\n" + "#include \n" + - "JAVA_INT StackOverflowApp_prepareStackOverflow___R_int(CODENAME_ONE_THREAD_STATE) {\n" + - " JAVA_INT previous = threadStateData->callStackOffset;\n" + - " threadStateData->callStackOffset = CN1_MAX_STACK_CALL_DEPTH - 1;\n" + - " return previous;\n" + - "}\n" + - "void StackOverflowApp_restoreCallStackOffset___int(CODENAME_ONE_THREAD_STATE, JAVA_INT value) {\n" + - " threadStateData->callStackOffset = value;\n" + - "}\n" + "void StackOverflowApp_report___java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT msg) {\n" + " struct String_Struct {\n" + " JAVA_OBJECT header;\n" + From 824af45146b1adcd0b310cf7bf1d32e80a2bdd59 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 25 Jan 2026 20:32:17 +0200 Subject: [PATCH 06/20] Throw StackOverflowError before native stack exhaustion --- vm/ByteCodeTranslator/src/cn1_globals.h | 1 + vm/ByteCodeTranslator/src/nativeMethods.m | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/vm/ByteCodeTranslator/src/cn1_globals.h b/vm/ByteCodeTranslator/src/cn1_globals.h index 28310b992d..bb092092a3 100644 --- a/vm/ByteCodeTranslator/src/cn1_globals.h +++ b/vm/ByteCodeTranslator/src/cn1_globals.h @@ -752,6 +752,7 @@ struct TryBlock { }; #define CN1_MAX_STACK_CALL_DEPTH 1024 +#define CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT (CN1_MAX_STACK_CALL_DEPTH / 2) #define CN1_MAX_OBJECT_STACK_DEPTH 16536 #define PER_THREAD_ALLOCATION_COUNT 4096 diff --git a/vm/ByteCodeTranslator/src/nativeMethods.m b/vm/ByteCodeTranslator/src/nativeMethods.m index e58333f5b9..5f39d3ee5a 100644 --- a/vm/ByteCodeTranslator/src/nativeMethods.m +++ b/vm/ByteCodeTranslator/src/nativeMethods.m @@ -1551,7 +1551,7 @@ void initMethodStack(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, int THROW_NULL_POINTER_EXCEPTION(); } #endif - if (threadStateData->callStackOffset >= CN1_MAX_STACK_CALL_DEPTH - 1) { + if (threadStateData->callStackOffset >= CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT - 1) { throwException(threadStateData, __NEW_INSTANCE_java_lang_StackOverflowError(threadStateData)); return; } From 08a1325b1f4e26aa020383225572f2c5e725b300 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 25 Jan 2026 20:55:04 +0200 Subject: [PATCH 07/20] Lower stack overflow depth limit --- vm/ByteCodeTranslator/src/cn1_globals.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/ByteCodeTranslator/src/cn1_globals.h b/vm/ByteCodeTranslator/src/cn1_globals.h index bb092092a3..e92ec6ca8e 100644 --- a/vm/ByteCodeTranslator/src/cn1_globals.h +++ b/vm/ByteCodeTranslator/src/cn1_globals.h @@ -752,7 +752,7 @@ struct TryBlock { }; #define CN1_MAX_STACK_CALL_DEPTH 1024 -#define CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT (CN1_MAX_STACK_CALL_DEPTH / 2) +#define CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT 128 #define CN1_MAX_OBJECT_STACK_DEPTH 16536 #define PER_THREAD_ALLOCATION_COUNT 4096 From c74d7cc9ca106c9989ebdd584f278ae7d79ed6ec Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 26 Jan 2026 04:03:55 +0200 Subject: [PATCH 08/20] Improve stack overflow test diagnostics --- .../StackOverflowIntegrationTest.java | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index 8ff023066f..995acb7877 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -85,10 +85,12 @@ void throwsAndRecoversFromStackOverflow(CompilerHelper.CompilerConfig config) th CleanTargetIntegrationTest.runCommand(Arrays.asList("cmake", "--build", buildDir.toString()), distDir); Path executable = buildDir.resolve("StackOverflowApp"); - String output = CleanTargetIntegrationTest.runCommand(Arrays.asList(executable.toString()), buildDir); + ProcessResult result = runProcess(Arrays.asList(executable.toString()), buildDir); - assertTrue(output.contains("STACK_OVERFLOW_OK"), - "StackOverflowError should be thrown and caught. Output was:\n" + output); + assertEquals(0, result.exitCode, + "StackOverflowApp exited with code " + result.exitCode + ". Output:\n" + result.output); + assertTrue(result.output.contains("STACK_OVERFLOW_OK"), + "StackOverflowError should be thrown and caught. Output was:\n" + result.output); } private String appSource() { @@ -131,4 +133,28 @@ private String nativeReportSource() { " }\n" + "}\n"; } + + private ProcessResult runProcess(List command, Path workingDir) throws Exception { + ProcessBuilder builder = new ProcessBuilder(command); + builder.directory(workingDir.toFile()); + builder.redirectErrorStream(true); + Process process = builder.start(); + String output; + try (java.io.BufferedReader reader = new java.io.BufferedReader( + new java.io.InputStreamReader(process.getInputStream(), java.nio.charset.StandardCharsets.UTF_8))) { + output = reader.lines().collect(java.util.stream.Collectors.joining("\n")); + } + int exit = process.waitFor(); + return new ProcessResult(exit, output); + } + + private static final class ProcessResult { + private final int exitCode; + private final String output; + + private ProcessResult(int exitCode, String output) { + this.exitCode = exitCode; + this.output = output; + } + } } From 3aed7d80719334a7f955cf1759eb0bb9a39fa5b1 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 26 Jan 2026 04:31:32 +0200 Subject: [PATCH 09/20] Add source-level asserts to stack overflow test --- .../StackOverflowIntegrationTest.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index 995acb7877..37158bda4e 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -70,6 +70,7 @@ void throwsAndRecoversFromStackOverflow(CompilerHelper.CompilerConfig config) th Path srcRoot = distDir.resolve("StackOverflowApp-src"); CleanTargetIntegrationTest.patchCn1Globals(srcRoot); CleanTargetIntegrationTest.replaceLibraryWithExecutableTarget(cmakeLists, srcRoot.getFileName().toString()); + assertGeneratedSources(srcRoot); Path buildDir = distDir.resolve("build"); Files.createDirectories(buildDir); @@ -88,7 +89,9 @@ void throwsAndRecoversFromStackOverflow(CompilerHelper.CompilerConfig config) th ProcessResult result = runProcess(Arrays.asList(executable.toString()), buildDir); assertEquals(0, result.exitCode, - "StackOverflowApp exited with code " + result.exitCode + ". Output:\n" + result.output); + "StackOverflowApp exited with code " + result.exitCode + + ". Output:\n" + result.output + + "\nExecutable: " + executable); assertTrue(result.output.contains("STACK_OVERFLOW_OK"), "StackOverflowError should be thrown and caught. Output was:\n" + result.output); } @@ -134,6 +137,31 @@ private String nativeReportSource() { "}\n"; } + private void assertGeneratedSources(Path srcRoot) throws Exception { + Path nativeMethods = srcRoot.resolve("nativeMethods.c"); + assertTrue(Files.exists(nativeMethods), + "Expected nativeMethods.c at " + nativeMethods); + String nativeMethodsSource = new String(Files.readAllBytes(nativeMethods), StandardCharsets.UTF_8); + assertTrue(nativeMethodsSource.contains("java_lang_StackOverflowError.h"), + "nativeMethods.c should include java_lang_StackOverflowError.h to support stack overflow handling."); + assertTrue(nativeMethodsSource.contains("CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT"), + "nativeMethods.c should reference CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT for stack overflow detection."); + + Path cn1Globals = srcRoot.resolve("cn1_globals.h"); + assertTrue(Files.exists(cn1Globals), + "Expected cn1_globals.h at " + cn1Globals); + String globalsSource = new String(Files.readAllBytes(cn1Globals), StandardCharsets.UTF_8); + assertTrue(globalsSource.contains("CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT"), + "cn1_globals.h should define CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT."); + + Path appSource = srcRoot.resolve("StackOverflowApp.c"); + assertTrue(Files.exists(appSource), + "Expected StackOverflowApp.c at " + appSource); + String appSourceText = new String(Files.readAllBytes(appSource), StandardCharsets.UTF_8); + assertTrue(appSourceText.contains("StackOverflowApp_triggerOverflow__"), + "StackOverflowApp.c should include triggerOverflow method for recursion."); + } + private ProcessResult runProcess(List command, Path workingDir) throws Exception { ProcessBuilder builder = new ProcessBuilder(command); builder.directory(workingDir.toFile()); From 281300ceb5dae0671144cd7262d665ec341a15f4 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 26 Jan 2026 07:53:00 +0200 Subject: [PATCH 10/20] Expand stack overflow test diagnostics --- .../StackOverflowIntegrationTest.java | 69 ++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index 37158bda4e..383834e4d0 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -88,12 +88,13 @@ void throwsAndRecoversFromStackOverflow(CompilerHelper.CompilerConfig config) th Path executable = buildDir.resolve("StackOverflowApp"); ProcessResult result = runProcess(Arrays.asList(executable.toString()), buildDir); + String diagnostics = buildDiagnostics(srcRoot, executable, result); assertEquals(0, result.exitCode, "StackOverflowApp exited with code " + result.exitCode + ". Output:\n" + result.output - + "\nExecutable: " + executable); + + diagnostics); assertTrue(result.output.contains("STACK_OVERFLOW_OK"), - "StackOverflowError should be thrown and caught. Output was:\n" + result.output); + "StackOverflowError should be thrown and caught. Output was:\n" + result.output + diagnostics); } private String appSource() { @@ -162,6 +163,70 @@ private void assertGeneratedSources(Path srcRoot) throws Exception { "StackOverflowApp.c should include triggerOverflow method for recursion."); } + private String buildDiagnostics(Path srcRoot, Path executable, ProcessResult result) throws Exception { + StringBuilder diagnostics = new StringBuilder(); + diagnostics.append("\nExecutable: ").append(executable); + if (Files.exists(executable)) { + diagnostics.append("\nExecutable size: ").append(Files.size(executable)).append(" bytes"); + } + + Path cn1Globals = srcRoot.resolve("cn1_globals.h"); + if (Files.exists(cn1Globals)) { + String globalsSource = new String(Files.readAllBytes(cn1Globals), StandardCharsets.UTF_8); + diagnostics.append("\n").append(extractLine(globalsSource, "CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT")); + } + + Path nativeMethods = srcRoot.resolve("nativeMethods.c"); + if (Files.exists(nativeMethods)) { + String nativeMethodsSource = new String(Files.readAllBytes(nativeMethods), StandardCharsets.UTF_8); + diagnostics.append("\nContains java_lang_StackOverflowError.h: ") + .append(nativeMethodsSource.contains("java_lang_StackOverflowError.h")); + diagnostics.append("\nContains CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT: ") + .append(nativeMethodsSource.contains("CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT")); + diagnostics.append("\ninitMethodStack snippet: ") + .append(extractSnippet(nativeMethodsSource, "initMethodStack", 120)); + } + + Path appSource = srcRoot.resolve("StackOverflowApp.c"); + if (Files.exists(appSource)) { + String appSourceText = new String(Files.readAllBytes(appSource), StandardCharsets.UTF_8); + diagnostics.append("\ntriggerOverflow snippet: ") + .append(extractSnippet(appSourceText, "StackOverflowApp_triggerOverflow__", 120)); + } + if (!result.output.isEmpty()) { + diagnostics.append("\nOutput length: ").append(result.output.length()); + } + return diagnostics.toString(); + } + + private String extractLine(String source, String token) { + int idx = source.indexOf(token); + if (idx < 0) { + return "Missing " + token; + } + int lineStart = source.lastIndexOf('\n', idx); + int lineEnd = source.indexOf('\n', idx); + if (lineStart < 0) { + lineStart = 0; + } else { + lineStart += 1; + } + if (lineEnd < 0) { + lineEnd = source.length(); + } + return source.substring(lineStart, lineEnd).trim(); + } + + private String extractSnippet(String source, String token, int radius) { + int idx = source.indexOf(token); + if (idx < 0) { + return "Missing " + token; + } + int start = Math.max(0, idx - radius); + int end = Math.min(source.length(), idx + radius); + return source.substring(start, end).replace("\n", "\\n"); + } + private ProcessResult runProcess(List command, Path workingDir) throws Exception { ProcessBuilder builder = new ProcessBuilder(command); builder.directory(workingDir.toFile()); From 6e5c6c401365b372e407a75dfbeb0b918accba61 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:05:09 +0200 Subject: [PATCH 11/20] Lower stack overflow depth limit further --- vm/ByteCodeTranslator/src/cn1_globals.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/ByteCodeTranslator/src/cn1_globals.h b/vm/ByteCodeTranslator/src/cn1_globals.h index e92ec6ca8e..13ed5fb25b 100644 --- a/vm/ByteCodeTranslator/src/cn1_globals.h +++ b/vm/ByteCodeTranslator/src/cn1_globals.h @@ -752,7 +752,7 @@ struct TryBlock { }; #define CN1_MAX_STACK_CALL_DEPTH 1024 -#define CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT 128 +#define CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT 32 #define CN1_MAX_OBJECT_STACK_DEPTH 16536 #define PER_THREAD_ALLOCATION_COUNT 4096 From 58c4a5ff05565a06a5014c3d05ce5c8c38e9549a Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:14:41 +0200 Subject: [PATCH 12/20] Revert overflow limit and add smoke run diagnostics --- vm/ByteCodeTranslator/src/cn1_globals.h | 2 +- .../StackOverflowIntegrationTest.java | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/vm/ByteCodeTranslator/src/cn1_globals.h b/vm/ByteCodeTranslator/src/cn1_globals.h index 13ed5fb25b..0aa1e1b670 100644 --- a/vm/ByteCodeTranslator/src/cn1_globals.h +++ b/vm/ByteCodeTranslator/src/cn1_globals.h @@ -752,7 +752,7 @@ struct TryBlock { }; #define CN1_MAX_STACK_CALL_DEPTH 1024 -#define CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT 32 +#define CN1_STACK_OVERFLOW_CALL_DEPTH_LIMIT CN1_MAX_STACK_CALL_DEPTH #define CN1_MAX_OBJECT_STACK_DEPTH 16536 #define PER_THREAD_ALLOCATION_COUNT 4096 diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index 383834e4d0..68de3223c2 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -86,8 +86,16 @@ void throwsAndRecoversFromStackOverflow(CompilerHelper.CompilerConfig config) th CleanTargetIntegrationTest.runCommand(Arrays.asList("cmake", "--build", buildDir.toString()), distDir); Path executable = buildDir.resolve("StackOverflowApp"); - ProcessResult result = runProcess(Arrays.asList(executable.toString()), buildDir); + ProcessResult smokeResult = runProcess(Arrays.asList(executable.toString(), "smoke"), buildDir); + String smokeDiagnostics = buildDiagnostics(srcRoot, executable, smokeResult); + assertEquals(0, smokeResult.exitCode, + "StackOverflowApp smoke run exited with code " + smokeResult.exitCode + + ". Output:\n" + smokeResult.output + + smokeDiagnostics); + assertTrue(smokeResult.output.contains("SMOKE_OK"), + "StackOverflowApp smoke run should succeed. Output was:\n" + smokeResult.output + smokeDiagnostics); + ProcessResult result = runProcess(Arrays.asList(executable.toString()), buildDir); String diagnostics = buildDiagnostics(srcRoot, executable, result); assertEquals(0, result.exitCode, "StackOverflowApp exited with code " + result.exitCode @@ -103,7 +111,18 @@ private String appSource() { " private static void triggerOverflow() {\n" + " triggerOverflow();\n" + " }\n" + + " private static int boundedRecursion(int depth) {\n" + + " if (depth <= 0) {\n" + + " return 1;\n" + + " }\n" + + " return depth + boundedRecursion(depth - 1);\n" + + " }\n" + " public static void main(String[] args) {\n" + + " if (args != null && args.length > 0 && \"smoke\".equals(args[0])) {\n" + + " int value = boundedRecursion(5);\n" + + " report(\"SMOKE_OK:\" + value);\n" + + " return;\n" + + " }\n" + " try {\n" + " triggerOverflow();\n" + " } catch (StackOverflowError err) {\n" + From e95aa0e7a7411e29a7e6ca1983768880d9d5f0c3 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 26 Jan 2026 20:07:16 +0200 Subject: [PATCH 13/20] Fix smoke output string building --- .../tools/translator/StackOverflowIntegrationTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index 68de3223c2..df15b6634f 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -120,7 +120,10 @@ private String appSource() { " public static void main(String[] args) {\n" + " if (args != null && args.length > 0 && \"smoke\".equals(args[0])) {\n" + " int value = boundedRecursion(5);\n" + - " report(\"SMOKE_OK:\" + value);\n" + + " StringBuilder sb = new StringBuilder();\n" + + " sb.append(\"SMOKE_OK:\");\n" + + " sb.append(value);\n" + + " report(sb.toString());\n" + " return;\n" + " }\n" + " try {\n" + From b86650fd5ddae984439f98e4e1c82367499b902c Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 26 Jan 2026 20:49:09 +0200 Subject: [PATCH 14/20] Expand stack overflow smoke diagnostics --- .../tools/translator/StackOverflowIntegrationTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index df15b6634f..6d3607af79 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -214,6 +214,12 @@ private String buildDiagnostics(Path srcRoot, Path executable, ProcessResult res String appSourceText = new String(Files.readAllBytes(appSource), StandardCharsets.UTF_8); diagnostics.append("\ntriggerOverflow snippet: ") .append(extractSnippet(appSourceText, "StackOverflowApp_triggerOverflow__", 120)); + diagnostics.append("\nboundedRecursion snippet: ") + .append(extractSnippet(appSourceText, "StackOverflowApp_boundedRecursion___int_R_int", 120)); + diagnostics.append("\nmain snippet: ") + .append(extractSnippet(appSourceText, "StackOverflowApp_main___java_lang_String_1ARRAY", 160)); + diagnostics.append("\nreport snippet: ") + .append(extractSnippet(appSourceText, "StackOverflowApp_report___java_lang_String", 120)); } if (!result.output.isEmpty()) { diagnostics.append("\nOutput length: ").append(result.output.length()); From 908bdfe1f3cae99f3eb6a736f7f2941edd92dad5 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 26 Jan 2026 21:10:57 +0200 Subject: [PATCH 15/20] Add smoke-phase markers to stack overflow test --- .../tools/translator/StackOverflowIntegrationTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index 6d3607af79..baaae745b8 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -119,11 +119,13 @@ private String appSource() { " }\n" + " public static void main(String[] args) {\n" + " if (args != null && args.length > 0 && \"smoke\".equals(args[0])) {\n" + + " report(\"SMOKE_START\");\n" + " int value = boundedRecursion(5);\n" + " StringBuilder sb = new StringBuilder();\n" + " sb.append(\"SMOKE_OK:\");\n" + " sb.append(value);\n" + " report(sb.toString());\n" + + " report(\"SMOKE_DONE\");\n" + " return;\n" + " }\n" + " try {\n" + From 0b766c7fba40b53ccb449213997bed3260a03bb9 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Tue, 27 Jan 2026 04:12:59 +0200 Subject: [PATCH 16/20] Add probe run before smoke in stack overflow test --- .../translator/StackOverflowIntegrationTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index baaae745b8..ac34d29c50 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -86,6 +86,14 @@ void throwsAndRecoversFromStackOverflow(CompilerHelper.CompilerConfig config) th CleanTargetIntegrationTest.runCommand(Arrays.asList("cmake", "--build", buildDir.toString()), distDir); Path executable = buildDir.resolve("StackOverflowApp"); + ProcessResult probeResult = runProcess(Arrays.asList(executable.toString(), "probe"), buildDir); + String probeDiagnostics = buildDiagnostics(srcRoot, executable, probeResult); + assertEquals(0, probeResult.exitCode, + "StackOverflowApp probe run exited with code " + probeResult.exitCode + + ". Output:\n" + probeResult.output + + probeDiagnostics); + assertTrue(probeResult.output.contains("PROBE_OK"), + "StackOverflowApp probe run should succeed. Output was:\n" + probeResult.output + probeDiagnostics); ProcessResult smokeResult = runProcess(Arrays.asList(executable.toString(), "smoke"), buildDir); String smokeDiagnostics = buildDiagnostics(srcRoot, executable, smokeResult); assertEquals(0, smokeResult.exitCode, @@ -118,6 +126,10 @@ private String appSource() { " return depth + boundedRecursion(depth - 1);\n" + " }\n" + " public static void main(String[] args) {\n" + + " if (args != null && args.length > 0 && \"probe\".equals(args[0])) {\n" + + " report(\"PROBE_OK\");\n" + + " return;\n" + + " }\n" + " if (args != null && args.length > 0 && \"smoke\".equals(args[0])) {\n" + " report(\"SMOKE_START\");\n" + " int value = boundedRecursion(5);\n" + From 650697e877fc21d1362e00bd1f016bf7bc374e9a Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Jan 2026 18:00:34 +0200 Subject: [PATCH 17/20] Add probe markers for stack overflow test --- .../tools/translator/StackOverflowIntegrationTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index ac34d29c50..8421c699fb 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -127,7 +127,9 @@ private String appSource() { " }\n" + " public static void main(String[] args) {\n" + " if (args != null && args.length > 0 && \"probe\".equals(args[0])) {\n" + + " report(\"PROBE_START\");\n" + " report(\"PROBE_OK\");\n" + + " report(\"PROBE_DONE\");\n" + " return;\n" + " }\n" + " if (args != null && args.length > 0 && \"smoke\".equals(args[0])) {\n" + From d12a5e5df0a3ef8f527b924fb2c73996b39b1d7a Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Jan 2026 18:29:31 +0200 Subject: [PATCH 18/20] Assert probe output from native constant report --- .../translator/StackOverflowIntegrationTest.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index 8421c699fb..adea2408fe 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -92,7 +92,7 @@ void throwsAndRecoversFromStackOverflow(CompilerHelper.CompilerConfig config) th "StackOverflowApp probe run exited with code " + probeResult.exitCode + ". Output:\n" + probeResult.output + probeDiagnostics); - assertTrue(probeResult.output.contains("PROBE_OK"), + assertTrue(probeResult.output.contains("PROBE_CONSTANT"), "StackOverflowApp probe run should succeed. Output was:\n" + probeResult.output + probeDiagnostics); ProcessResult smokeResult = runProcess(Arrays.asList(executable.toString(), "smoke"), buildDir); String smokeDiagnostics = buildDiagnostics(srcRoot, executable, smokeResult); @@ -116,6 +116,7 @@ void throwsAndRecoversFromStackOverflow(CompilerHelper.CompilerConfig config) th private String appSource() { return "public class StackOverflowApp {\n" + " private static native void report(String msg);\n" + + " private static native void reportConstant();\n" + " private static void triggerOverflow() {\n" + " triggerOverflow();\n" + " }\n" + @@ -127,19 +128,17 @@ private String appSource() { " }\n" + " public static void main(String[] args) {\n" + " if (args != null && args.length > 0 && \"probe\".equals(args[0])) {\n" + - " report(\"PROBE_START\");\n" + - " report(\"PROBE_OK\");\n" + - " report(\"PROBE_DONE\");\n" + + " reportConstant();\n" + " return;\n" + " }\n" + " if (args != null && args.length > 0 && \"smoke\".equals(args[0])) {\n" + - " report(\"SMOKE_START\");\n" + + " reportConstant();\n" + " int value = boundedRecursion(5);\n" + " StringBuilder sb = new StringBuilder();\n" + " sb.append(\"SMOKE_OK:\");\n" + " sb.append(value);\n" + " report(sb.toString());\n" + - " report(\"SMOKE_DONE\");\n" + + " reportConstant();\n" + " return;\n" + " }\n" + " try {\n" + @@ -154,6 +153,10 @@ private String appSource() { private String nativeReportSource() { return "#include \"cn1_globals.h\"\n" + "#include \n" + + "void StackOverflowApp_reportConstant__(CODENAME_ONE_THREAD_STATE) {\n" + + " printf(\"PROBE_CONSTANT\\n\");\n" + + " fflush(stdout);\n" + + "}\n" + "void StackOverflowApp_report___java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT msg) {\n" + " struct String_Struct {\n" + " JAVA_OBJECT header;\n" + From 9d5e74a3d779cec60d1bdd14ffbbcf97df7fa484 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Jan 2026 20:18:26 +0200 Subject: [PATCH 19/20] Avoid string args in stack overflow test modes --- .../tools/translator/StackOverflowIntegrationTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index adea2408fe..1e44bbab6c 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -86,7 +86,7 @@ void throwsAndRecoversFromStackOverflow(CompilerHelper.CompilerConfig config) th CleanTargetIntegrationTest.runCommand(Arrays.asList("cmake", "--build", buildDir.toString()), distDir); Path executable = buildDir.resolve("StackOverflowApp"); - ProcessResult probeResult = runProcess(Arrays.asList(executable.toString(), "probe"), buildDir); + ProcessResult probeResult = runProcess(Arrays.asList(executable.toString()), buildDir); String probeDiagnostics = buildDiagnostics(srcRoot, executable, probeResult); assertEquals(0, probeResult.exitCode, "StackOverflowApp probe run exited with code " + probeResult.exitCode @@ -103,7 +103,7 @@ void throwsAndRecoversFromStackOverflow(CompilerHelper.CompilerConfig config) th assertTrue(smokeResult.output.contains("SMOKE_OK"), "StackOverflowApp smoke run should succeed. Output was:\n" + smokeResult.output + smokeDiagnostics); - ProcessResult result = runProcess(Arrays.asList(executable.toString()), buildDir); + ProcessResult result = runProcess(Arrays.asList(executable.toString(), "overflow", "run"), buildDir); String diagnostics = buildDiagnostics(srcRoot, executable, result); assertEquals(0, result.exitCode, "StackOverflowApp exited with code " + result.exitCode @@ -127,11 +127,11 @@ private String appSource() { " return depth + boundedRecursion(depth - 1);\n" + " }\n" + " public static void main(String[] args) {\n" + - " if (args != null && args.length > 0 && \"probe\".equals(args[0])) {\n" + + " if (args == null || args.length == 0) {\n" + " reportConstant();\n" + " return;\n" + " }\n" + - " if (args != null && args.length > 0 && \"smoke\".equals(args[0])) {\n" + + " if (args.length == 1) {\n" + " reportConstant();\n" + " int value = boundedRecursion(5);\n" + " StringBuilder sb = new StringBuilder();\n" + From 108b05cc29595451dd3d8fbb57d7b3324be07691 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:02:25 +0200 Subject: [PATCH 20/20] Refine smoke assertions for stack overflow test --- .../tools/translator/StackOverflowIntegrationTest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java index 1e44bbab6c..5ea1ad5295 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/StackOverflowIntegrationTest.java @@ -100,8 +100,14 @@ void throwsAndRecoversFromStackOverflow(CompilerHelper.CompilerConfig config) th "StackOverflowApp smoke run exited with code " + smokeResult.exitCode + ". Output:\n" + smokeResult.output + smokeDiagnostics); + assertTrue(smokeResult.output.contains("PROBE_CONSTANT"), + "StackOverflowApp smoke run should emit probe marker before recursion. Output was:\n" + + smokeResult.output + smokeDiagnostics); assertTrue(smokeResult.output.contains("SMOKE_OK"), - "StackOverflowApp smoke run should succeed. Output was:\n" + smokeResult.output + smokeDiagnostics); + "StackOverflowApp smoke run should succeed. Output was:\n" + + smokeResult.output + + "\nMissing SMOKE_OK suggests a crash during boundedRecursion or report(String)." + + smokeDiagnostics); ProcessResult result = runProcess(Arrays.asList(executable.toString(), "overflow", "run"), buildDir); String diagnostics = buildDiagnostics(srcRoot, executable, result);