From 6e1914aea991eb94027c079e45ad8e8a32bdb640 Mon Sep 17 00:00:00 2001 From: domenico Date: Mon, 25 Nov 2024 16:06:27 +0100 Subject: [PATCH 1/2] Add test case --- .../smeup/rpgparser/evaluation/InterpreterTest.kt | 6 ++++++ .../src/test/resources/GOTOTST10.rpgle | 13 +++++++++++++ .../src/test/resources/GOTOTST10B.rpgle | 10 ++++++++++ 3 files changed, 29 insertions(+) create mode 100644 rpgJavaInterpreter-core/src/test/resources/GOTOTST10.rpgle create mode 100644 rpgJavaInterpreter-core/src/test/resources/GOTOTST10B.rpgle diff --git a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/evaluation/InterpreterTest.kt b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/evaluation/InterpreterTest.kt index 61a1c9dc1..cb1565df8 100644 --- a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/evaluation/InterpreterTest.kt +++ b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/evaluation/InterpreterTest.kt @@ -1399,6 +1399,12 @@ Test 6 assertEquals(expected, outputOf("GOTOTST9")) } + @Test + fun executeGOTOTST10() { + val expected = listOf("ok", "ok") + assertEquals(expected, outputOf("GOTOTST10")) + } + @Test fun executeGotoENDSR() { assertEquals(listOf("1", "2", "3"), outputOf("GOTOENDSR")) diff --git a/rpgJavaInterpreter-core/src/test/resources/GOTOTST10.rpgle b/rpgJavaInterpreter-core/src/test/resources/GOTOTST10.rpgle new file mode 100644 index 000000000..0827bc777 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/GOTOTST10.rpgle @@ -0,0 +1,13 @@ + V* ============================================================== + V* 25/11/2024 APU002 Creation + V* ============================================================== + O * PROGRAM GOAL + O * Execute a subroutine with a CALL to a program performing a top-level GOTO + V* ============================================================== + C EXSR SR1 + C SETON LR + + C SR1 BEGSR + C CALL 'GOTOTST10B' + C 'ok' DSPLY + C ENDSR diff --git a/rpgJavaInterpreter-core/src/test/resources/GOTOTST10B.rpgle b/rpgJavaInterpreter-core/src/test/resources/GOTOTST10B.rpgle new file mode 100644 index 000000000..9f56f4895 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/GOTOTST10B.rpgle @@ -0,0 +1,10 @@ + V* ============================================================== + V* 25/11/2024 APU002 Creation + V* ============================================================== + O * PROGRAM GOAL + O * Being called by 'GOTOTST10.rpgle' + V* ============================================================== + C GOTO TAG1 + C 'ko' DSPLY + C TAG1 TAG + C 'ok' DSPLY \ No newline at end of file From 8e8c96c79365b17b1ab2980d44b6609bbfd224f3 Mon Sep 17 00:00:00 2001 From: domenico Date: Mon, 25 Nov 2024 16:07:01 +0100 Subject: [PATCH 2/2] Restore GotoTopLevelException promotion logic --- .../rpgparser/interpreter/control_flow_exceptions.kt | 10 ---------- .../rpgparser/interpreter/internal_interpreter.kt | 12 +++++++++--- .../com/smeup/rpgparser/parsing/ast/statements.kt | 4 ++-- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/control_flow_exceptions.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/control_flow_exceptions.kt index 1bcb423a6..b960f759e 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/control_flow_exceptions.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/control_flow_exceptions.kt @@ -1,6 +1,5 @@ package com.smeup.rpgparser.interpreter -import com.smeup.rpgparser.execution.MainExecutionContext import com.smeup.rpgparser.parsing.ast.Statement import com.smeup.rpgparser.utils.indexOfTag import com.smeup.rpgparser.utils.runIfNotEmpty @@ -44,15 +43,6 @@ class GotoException private constructor(val tag: String) : ControlFlowException( internal fun indexOfTaggedStatement(statements: List) = statements.indexOfTag(tag) } -/** - * Produce a scoped goto exception - */ -internal fun produceGotoInCurrentScope(tag: String): ControlFlowException { - val shouldLookupTopLevel = MainExecutionContext.getSubroutineStack().isEmpty() - val normalizedTag = tag.lowercase() - return if (shouldLookupTopLevel) GotoTopLevelException(normalizedTag) else GotoException(normalizedTag) -} - class InterpreterTimeoutException(val programName: String, val elapsed: Long, val expected: Long) : ControlFlowException() { fun ratio(): Double = if (elapsed <= 0) 0.0 else elapsed.toDouble() / expected.toDouble() override fun toString(): String { diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/internal_interpreter.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/internal_interpreter.kt index 6b96db6c6..aa5b4d216 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/internal_interpreter.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/internal_interpreter.kt @@ -30,6 +30,7 @@ import com.smeup.rpgparser.parsing.facade.SourceReference import com.smeup.rpgparser.parsing.facade.dumpSource import com.smeup.rpgparser.parsing.facade.relative import com.smeup.rpgparser.parsing.parsetreetoast.RpgType +import com.smeup.rpgparser.parsing.parsetreetoast.error import com.smeup.rpgparser.parsing.parsetreetoast.resolveAndValidate import com.smeup.rpgparser.parsing.parsetreetoast.todo import com.smeup.rpgparser.utils.ComparisonOperator.* @@ -427,10 +428,15 @@ open class InternalInterpreter( val unwrappedStatement = main.stmts.explode(true) // Recursive deal with top level goto flow - while (throwable is GotoTopLevelException) { + while (throwable is GotoTopLevelException || throwable is GotoException) { // We need to know the statement unwrapped in order to jump directly into a nested tag - val offset = throwable.indexOfTaggedStatement(unwrappedStatement) - require(0 <= offset && offset < unwrappedStatement.size) { "Offset $offset is not valid." } + val (offset, tag) = when (throwable) { + is GotoException -> Pair(throwable.indexOfTaggedStatement(unwrappedStatement), throwable.tag) + is GotoTopLevelException -> Pair(throwable.indexOfTaggedStatement(unwrappedStatement), throwable.tag) + else -> Pair(-1, "") + } + if (unwrappedStatement.size <= offset || offset < 0) + main.error("GOTO offset $offset is not valid. Cannot find TAG '$tag'") throwable = kotlin.runCatching { executeUnwrappedAt(unwrappedStatement, offset) }.exceptionOrNull() diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/statements.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/statements.kt index cfd0eb9c2..11f35191c 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/statements.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/statements.kt @@ -1911,7 +1911,7 @@ data class GotoStmt(val tag: String, override val position: Position? = null) : get() = "GOTO" override fun execute(interpreter: InterpreterCore) { - throw produceGotoInCurrentScope(tag) + throw GotoException(tag) } } @@ -1935,7 +1935,7 @@ data class CabStmt( SMALLER -> interpreter.setIndicators(this, BooleanValue.FALSE, BooleanValue.TRUE, BooleanValue.FALSE) else -> interpreter.setIndicators(this, BooleanValue.FALSE, BooleanValue.FALSE, BooleanValue.TRUE) } - if (comparisonResult.isVerified) throw produceGotoInCurrentScope(tag) + if (comparisonResult.isVerified) throw GotoException(tag) } }