Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix/ls24004538/nested call error stack #650

Merged
merged 6 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package com.smeup.rpgparser.interpreter

import com.smeup.rpgparser.execution.MainExecutionContext

class ProgramInterpreter(val systemInterface: SystemInterface) {

fun execute(rpgProgram: RpgProgram, initialValues: LinkedHashMap<String, Value>) {
rpgProgram.execute(systemInterface = systemInterface, params = initialValues)
MainExecutionContext.getProgramStack().push(rpgProgram)
try {
rpgProgram.execute(systemInterface = systemInterface, params = initialValues)
} finally {
MainExecutionContext.getProgramStack().pop()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,20 @@ class RpgProgram(val cu: CompilationUnit, val name: String = "<UNNAMED RPG PROGR
}
if (!initialized) {
initialized = true
val caller = if (MainExecutionContext.getProgramStack().isNotEmpty()) {
MainExecutionContext.getProgramStack().peek()

/**
* As the RPG program stack is managed outside of this method, it is up to the caller of this method
* to ensure it is in the correct state, that is:
* - `lastIndex` is this RpgProgram
* - `lastIndex - 1` is the RpgProgram that calls this RpgProgram
*
* Note: If these two rules are not followed at this point, do not expect RpgPrograms to behave correctly.
* that means something is wrong with `MainExecutionContext.getProgramStack()` push and pop logic.
*/
val programStack = MainExecutionContext.getProgramStack()
val caller = if (programStack.size > 1) {
val parentProgramIndex = programStack.lastIndex - 1
programStack[parentProgramIndex]
} else {
null
}
Expand All @@ -149,7 +161,6 @@ class RpgProgram(val cu: CompilationUnit, val name: String = "<UNNAMED RPG PROGR

activationGroup = ActivationGroup(activationGroupType, activationGroupType.assignedName(caller))
}
MainExecutionContext.getProgramStack().push(this)
MainExecutionContext.getConfiguration().jarikoCallback.onEnterPgm(name, interpreter.getGlobalSymbolTable())
// set reinitialization to false because symboltable cleaning currently is handled directly
// in internal interpreter before exit
Expand All @@ -160,7 +171,6 @@ class RpgProgram(val cu: CompilationUnit, val name: String = "<UNNAMED RPG PROGR
changedInitialValues = params().map { interpreter[it.name] }
// here clear symbol table if needed
interpreter.doSomethingAfterExecution()
MainExecutionContext.getProgramStack().pop()
}.nanoseconds
if (MainExecutionContext.isLoggingEnabled) {
logHandlers.renderLog(LazyLogEntry.produceStatement(logSource, "INTERPRETATION", "END"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@ import com.smeup.rpgparser.parsing.parsetreetoast.acceptBody
import com.smeup.rpgparser.parsing.parsetreetoast.error
import com.smeup.rpgparser.parsing.parsetreetoast.isInt
import com.smeup.rpgparser.parsing.parsetreetoast.toAst
import com.smeup.rpgparser.utils.ComparisonOperator
import com.smeup.rpgparser.utils.divideAtIndex
import com.smeup.rpgparser.utils.resizeTo
import com.smeup.rpgparser.utils.substringOfLength
import com.smeup.rpgparser.utils.*
import com.strumenta.kolasu.model.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
Expand Down Expand Up @@ -739,6 +736,9 @@ data class CallStmt(
}

program ?: throw callIssueException
if (program is RpgProgram) {
MainExecutionContext.getProgramStack().push(program)
}

// Ignore exceeding params
val targetProgramParams = program.params()
Expand Down Expand Up @@ -787,11 +787,14 @@ data class CallStmt(
}
} catch (e: Exception) { // TODO Catch a more specific exception?
if (errorIndicator == null) {
if (program is RpgProgram) {
MainExecutionContext.getProgramStack().pop()
}
throw e
}

interpreter.getIndicators()[errorIndicator] = BooleanValue.TRUE
MainExecutionContext.getConfiguration().jarikoCallback.onCallPgmError.invoke(popRuntimeErrorEvent())
MainExecutionContext.getProgramStack().pop()
null
}
paramValuesAtTheEnd?.forEachIndexed { index, value ->
Expand All @@ -803,6 +806,9 @@ data class CallStmt(
currentParam.result?.let { interpreter.assign(it, value) }
}
}

if (program is RpgProgram)
MainExecutionContext.getProgramStack().pop()
}

override fun getStatementLogRenderer(source: LogSourceProvider, action: String): LazyLogEntry {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,16 @@ open class MULANGT10BaseCodopTest : MULANGTTest() {
assertEquals(expected, "smeup/MUDRNRAPU00268".outputOf(configuration = smeupConfig))
}

/**
* State of context after a CALL stack failed setting an error indicator
* @see #LS24004538
*/
@Test
fun executeMUDRNRAPU00269() {
val expected = listOf("ok")
assertEquals(expected, "smeup/MUDRNRAPU00269".outputOf(configuration = smeupConfig))
}

/**
* Comparing number and `*ZEROS` by using `IFxx`.
* @see #LS24004528
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* This program has the sole purpose of calling a program that throws a runtime exception
C CALL 'ERRORPGM'
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
D £TESTDS DS
D £TESTPA 10

* Call of a program that throws an error and setting the error indicator
* Call of a program that throws an error and sets the error indicator
C CALL 'ERRORPGM' 35

* If call failed but the error indicator was set
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
V* ==============================================================
V* 30/10/2024 APU002 Creation
V* ==============================================================
O * PROGRAM GOAL
O * Call a program with an error indicator that calls another program
O * that throws an error and ensure the execution context is appropriate
V* ==============================================================
O * JARIKO ANOMALY
O * Before the fix, the error was about the caller not resolving
O * symbols in the correct compilation unit
O *
O * Note: see MUDRNRAPU00268.rpgle for a deeper explanation
O * on how the error was reproduced
V* ==============================================================

* Test declarations
D £TESTDS DS
D £TESTPA 10

* Call of a program that throws an error and sets the error indicator
C CALL 'ERRORPGM2' 35

* If call failed but the error indicator was set
* Execution should continue and the context should reference the correct program
C IF P_Test(£TESTPA:'EmiMsg(':'')='Y'
C 'ok' DSPLY
C ENDIF

* Test procedure
PP_Test B
D P_Test Pi 30000 VARYING
D $A 15 CONST
D $B 1N OPTIONS(*NOPASS)
D $C 1 OPTIONS(*NOPASS)
C RETURN 'Y'
P E

C SETON LR