From 4ed90cdd8ccab0e7fe4e8bc2a8e22f43daa3a68d Mon Sep 17 00:00:00 2001 From: lanarimarco Date: Thu, 12 Dec 2024 14:52:57 +0100 Subject: [PATCH 1/3] test: add test --- .../com/smeup/rpgparser/evaluation/InterpreterTest.kt | 6 ++++++ .../src/test/resources/NULLPTR01.rpgle | 9 +++++++++ 2 files changed, 15 insertions(+) create mode 100644 rpgJavaInterpreter-core/src/test/resources/NULLPTR01.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 58321033a..fb524033c 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 @@ -2619,4 +2619,10 @@ Test 6 jarikoKillerThread.start() jarikoExecutorThread.join() } + + @Test + fun executeNULLPTR01() { + // Test that the program does not throw a NullPointerException + "NULLPTR01".outputOf() + } } diff --git a/rpgJavaInterpreter-core/src/test/resources/NULLPTR01.rpgle b/rpgJavaInterpreter-core/src/test/resources/NULLPTR01.rpgle new file mode 100644 index 000000000..8057258bd --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/NULLPTR01.rpgle @@ -0,0 +1,9 @@ + D NUMAZI C 58 + D £JAXSWK S 100 DIM(300) + + D DS + * Error: + * java.lang.NullPointerException: Cannot invoke "com.smeup.rpgparser.RpgParser$Ds_nameContext.getText()" + * because the return value of "com.smeup.rpgparser.RpgParser$DspecContext.ds_name()" is null + D £JAXSW2 100 DIM(%elem(£JAXSWK)) + D £JAXSW2Key 10 OVERLAY(£JAXSW2:01) \ No newline at end of file From dcfc655138671f9f4d4ac695c7fdef38cb59abfc Mon Sep 17 00:00:00 2001 From: lanarimarco Date: Thu, 12 Dec 2024 16:25:12 +0100 Subject: [PATCH 2/3] fix: in evaluateNumberOfElementsOf skip search for D spec where StatementContext.dspec().ds_name() is null --- .../interpreter/compile_time_interpreter.kt | 47 +++++++++---------- .../src/test/resources/NULLPTR01.rpgle | 6 ++- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/compile_time_interpreter.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/compile_time_interpreter.kt index 506fc8656..3facde487 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/compile_time_interpreter.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/compile_time_interpreter.kt @@ -16,7 +16,6 @@ package com.smeup.rpgparser.interpreter -import com.smeup.rpgparser.RpgParser import com.smeup.rpgparser.RpgParser.* import com.smeup.rpgparser.execution.MainExecutionContext import com.smeup.rpgparser.parsing.ast.* @@ -32,10 +31,10 @@ import com.strumenta.kolasu.model.tryToResolve * It should consider only statically evaluable elements. */ interface CompileTimeInterpreter { - fun evaluate(rContext: RpgParser.RContext, expression: Expression): Value - fun evaluateElementSizeOf(rContext: RpgParser.RContext, expression: Expression, conf: ToAstConfiguration, procedureName: String? = null): Int - fun evaluateTypeOf(rContext: RpgParser.RContext, expression: Expression, conf: ToAstConfiguration, procedureName: String? = null): Type - fun evaluateNumberOfElementsOf(rContext: RpgParser.RContext, declName: String): Int + fun evaluate(rContext: RContext, expression: Expression): Value + fun evaluateElementSizeOf(rContext: RContext, expression: Expression, conf: ToAstConfiguration, procedureName: String? = null): Int + fun evaluateTypeOf(rContext: RContext, expression: Expression, conf: ToAstConfiguration, procedureName: String? = null): Type + fun evaluateNumberOfElementsOf(rContext: RContext, declName: String): Int } object CommonCompileTimeInterpreter : BaseCompileTimeInterpreter(emptyList()) @@ -45,15 +44,15 @@ class InjectableCompileTimeInterpreter( fileDefinitions: Map>? = null, delegatedCompileTimeInterpreter: CompileTimeInterpreter? = null ) : BaseCompileTimeInterpreter(knownDataDefinitions, fileDefinitions, delegatedCompileTimeInterpreter) { - override fun evaluateNumberOfElementsOf(rContext: RpgParser.RContext, declName: String): Int { + override fun evaluateNumberOfElementsOf(rContext: RContext, declName: String): Int { return mockedDecls[declName]?.numberOfElements() ?: super.evaluateNumberOfElementsOf(rContext, declName) } - override fun evaluateElementSizeOf(rContext: RpgParser.RContext, declName: String, conf: ToAstConfiguration, procedureName: String?): Int { + override fun evaluateElementSizeOf(rContext: RContext, declName: String, conf: ToAstConfiguration, procedureName: String?): Int { return mockedDecls[declName]?.elementSize() ?: super.evaluateElementSizeOf(rContext, declName, conf, procedureName) } - override fun evaluateTypeOf(rContext: RpgParser.RContext, declName: String, conf: ToAstConfiguration, procedureName: String?): Type { + override fun evaluateTypeOf(rContext: RContext, declName: String, conf: ToAstConfiguration, procedureName: String?): Type { return mockedDecls[declName] ?: super.evaluateTypeOf(rContext, declName, conf, procedureName) } @@ -72,7 +71,7 @@ open class BaseCompileTimeInterpreter( private val delegatedCompileTimeInterpreter: CompileTimeInterpreter? = null ) : CompileTimeInterpreter { - override fun evaluate(rContext: RpgParser.RContext, expression: Expression): Value { + override fun evaluate(rContext: RContext, expression: Expression): Value { return when (expression) { is NumberOfElementsExpr -> IntValue(evaluateNumberOfElementsOf(rContext, expression.value).toLong()) is IntLiteral -> IntValue(expression.value) @@ -87,7 +86,7 @@ open class BaseCompileTimeInterpreter( } } - private fun evaluateNumberOfElementsOf(rContext: RpgParser.RContext, expression: Expression): Int { + private fun evaluateNumberOfElementsOf(rContext: RContext, expression: Expression): Int { return when (expression) { is DataRefExpr -> { try { @@ -104,7 +103,7 @@ open class BaseCompileTimeInterpreter( } } - override fun evaluateNumberOfElementsOf(rContext: RpgParser.RContext, declName: String): Int { + override fun evaluateNumberOfElementsOf(rContext: RContext, declName: String): Int { val conf = MainExecutionContext.getConfiguration().options.toAstConfiguration knownDataDefinitions.forEach { if (it.name == declName) { @@ -119,7 +118,7 @@ open class BaseCompileTimeInterpreter( .forEach { when { it.dspec() != null -> { - val name = it.dspec().ds_name().text + val name = it.dspec().ds_name()?.text if (name == declName) { return it.dspec().toAst( conf = conf, @@ -146,7 +145,7 @@ open class BaseCompileTimeInterpreter( throw NotFoundAtCompileTimeException(declName) } - open fun evaluateElementSizeOf(rContext: RpgParser.RContext, declName: String, conf: ToAstConfiguration, procedureName: String?): Int { + open fun evaluateElementSizeOf(rContext: RContext, declName: String, conf: ToAstConfiguration, procedureName: String?): Int { knownDataDefinitions.forEach { if (it.name.equals(declName, ignoreCase = true)) { return it.elementSize() @@ -158,7 +157,7 @@ open class BaseCompileTimeInterpreter( return findSize(rContext.getStatements(procedureName), declName, conf, false)!! } - private fun findSize(statements: List, declName: String, conf: ToAstConfiguration, innerBlock: Boolean = true): Int? { + private fun findSize(statements: List, declName: String, conf: ToAstConfiguration, innerBlock: Boolean = true): Int? { statements.forEach { kotlin.runCatching { when { @@ -226,7 +225,7 @@ open class BaseCompileTimeInterpreter( throw NotFoundAtCompileTimeException(declName) } - override fun evaluateElementSizeOf(rContext: RpgParser.RContext, expression: Expression, conf: ToAstConfiguration, procedureName: String?): Int { + override fun evaluateElementSizeOf(rContext: RContext, expression: Expression, conf: ToAstConfiguration, procedureName: String?): Int { return try { evaluateTypeOf(rContext, expression, conf, procedureName).elementSize() } catch (e: RuntimeException) { @@ -238,7 +237,7 @@ open class BaseCompileTimeInterpreter( } } - override fun evaluateTypeOf(rContext: RpgParser.RContext, expression: Expression, conf: ToAstConfiguration, procedureName: String?): Type { + override fun evaluateTypeOf(rContext: RContext, expression: Expression, conf: ToAstConfiguration, procedureName: String?): Type { return when (expression) { is DataRefExpr -> { try { @@ -272,7 +271,7 @@ open class BaseCompileTimeInterpreter( } } - open fun evaluateTypeOf(rContext: RpgParser.RContext, declName: String, conf: ToAstConfiguration, procedureName: String?): Type { + open fun evaluateTypeOf(rContext: RContext, declName: String, conf: ToAstConfiguration, procedureName: String?): Type { knownDataDefinitions.forEach { if (it.name.equals(declName, ignoreCase = true)) { return it.type @@ -301,7 +300,7 @@ open class BaseCompileTimeInterpreter( return findType(statements, match.originalName, conf) } - private fun findType(statements: List, declName: String, conf: ToAstConfiguration, innerBlock: Boolean = true): Type? { + private fun findType(statements: List, declName: String, conf: ToAstConfiguration, innerBlock: Boolean = true): Type? { statements .forEach { it -> kotlin.runCatching { @@ -320,7 +319,7 @@ open class BaseCompileTimeInterpreter( knownDataDefinitions = knownDataDefinitions, fileDefinitions = fileDefinitions, parentDataDefinitions = emptyList() - )?.type + ).type } else { it.dcl_ds().parm_fixed().find { it.ds_name().text.equals(declName, ignoreCase = true) @@ -371,8 +370,8 @@ open class BaseCompileTimeInterpreter( } } - private fun RpgParser.BlockContext.findType(declName: String, conf: ToAstConfiguration): Type? { - return this.findAllDescendants(type = RpgParser.StatementContext::class, includingMe = false).let { descendants -> + private fun BlockContext.findType(declName: String, conf: ToAstConfiguration): Type? { + return this.findAllDescendants(type = StatementContext::class, includingMe = false).let { descendants -> findType(descendants, declName, conf) } } @@ -397,14 +396,14 @@ open class BaseCompileTimeInterpreter( } } - private fun Parm_fixedContext.findType(conf: ToAstConfiguration): Type? { + private fun Parm_fixedContext.findType(conf: ToAstConfiguration): Type { return this.toAst(conf, emptyList()).type } - private fun RpgParser.RContext.getStatements(procedureName: String?): List { + private fun RContext.getStatements(procedureName: String?): List { val statements: MutableList = mutableListOf() if (procedureName != null) { - val procedureContext: RpgParser.ProcedureContext? = this.procedure().firstOrNull { it.beginProcedure().psBegin().ps_name().text.equals(procedureName, ignoreCase = true) } + val procedureContext: ProcedureContext? = this.procedure().firstOrNull { it.beginProcedure().psBegin().ps_name().text.equals(procedureName, ignoreCase = true) } if (procedureContext != null) { statements.addAll( procedureContext.subprocedurestatement().mapNotNull { it.subroutine() }.flatMap { it.statement() } + diff --git a/rpgJavaInterpreter-core/src/test/resources/NULLPTR01.rpgle b/rpgJavaInterpreter-core/src/test/resources/NULLPTR01.rpgle index 8057258bd..007b24c41 100644 --- a/rpgJavaInterpreter-core/src/test/resources/NULLPTR01.rpgle +++ b/rpgJavaInterpreter-core/src/test/resources/NULLPTR01.rpgle @@ -3,7 +3,9 @@ D DS * Error: - * java.lang.NullPointerException: Cannot invoke "com.smeup.rpgparser.RpgParser$Ds_nameContext.getText()" - * because the return value of "com.smeup.rpgparser.RpgParser$DspecContext.ds_name()" is null + * java.lang.NullPointerException + * at com.smeup.rpgparser.interpreter.BaseCompileTimeInterpreter.evaluateNumberOfElementsOf(compile_time_interpreter.kt:122) + * at com.smeup.rpgparser.interpreter.InjectableCompileTimeInterpreter.evaluateNumberOfElementsOf(compile_time_interpreter.kt:49) + * ... D £JAXSW2 100 DIM(%elem(£JAXSWK)) D £JAXSW2Key 10 OVERLAY(£JAXSW2:01) \ No newline at end of file From 124ccc70f0b5844e5f764eaef3fcde05162666a2 Mon Sep 17 00:00:00 2001 From: lanarimarco Date: Thu, 12 Dec 2024 17:03:28 +0100 Subject: [PATCH 3/3] fix: executePgmCallBackTest does not work well in windows platform --- .../src/test/kotlin/com/smeup/rpgparser/AbstractTest.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/AbstractTest.kt b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/AbstractTest.kt index e0fc7976e..f25781a6d 100644 --- a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/AbstractTest.kt +++ b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/AbstractTest.kt @@ -449,11 +449,15 @@ abstract class AbstractTest { "Errors don't correspond.\n" + "Expected:\n${expectedSorted.map { "Line ${it.key}, \"${it.value}\"" }.joinToString(separator = "\n") { it } }\n" + "Actual:\n${found.map { "Line ${it.key}, \"${it.value}\"" }.joinToString(separator = "\n") { it } }\n", - found == expectedSorted + found.toString().sanitize() == expectedSorted.toString().sanitize() ) } } + private fun String.sanitize(): String { + return this.replace(Regex("\\s+"), " ").replace(Regex("[^Print]"), "") + } + private fun Map.Entry.contains(list: Map): Boolean { list.forEach { if (this.value.contains(it.value) && this.key == it.key) {