Skip to content

Commit

Permalink
Merge pull request #702 from smeup/develop_before_revert/LS25000142/f…
Browse files Browse the repository at this point in the history
…ix-concurrence-between-ds-and-file

Develop before revert/LS25000520/Fix concurrence between DS and File
  • Loading branch information
lanarimarco authored Feb 5, 2025
2 parents 4451181 + 953d680 commit 3fdc667
Show file tree
Hide file tree
Showing 17 changed files with 1,514 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,25 @@ class SymbolTable : ISymbolTable {
if (MainExecutionContext.isLoggingEnabled) getWithLogging(dataName) else getInternal(dataName)

override fun dataDefinitionByName(dataName: String): AbstractDataDefinition? {
return names[dataName.uppercase()] ?: parentSymbolTable?.let { (parentSymbolTable as SymbolTable).names[dataName.uppercase()] }
/*
* Attempts to resolve a Data Definition by its name, searching in the following order:
* 1. Root scope: Checks if a Data Definition with the given name exists directly within the current scope.
* 2. Unqualified Data Structures: Searches within the fields of any *unqualified* Data Structures
* declared in the current scope. This handles cases where a field is accessed without dot notation.
* 3. Parent Symbol Table: If not found in the current scope, delegates the search to the parent Symbol Table.
*/
return names.compute(dataName.uppercase()) { key, value ->
value ?: names
.asSequence()
.filter { name ->
name.value.type is DataStructureType &&
!(name.value.type as AbstractDataStructureType).isQualified &&
name.value is DataDefinition
}
.map { it.value }
.flatMap { dataStructure -> (dataStructure as DataDefinition).fields }
.firstOrNull { field -> field.name.equals(key, ignoreCase = true) } as AbstractDataDefinition?
} ?: parentSymbolTable?.let { (parentSymbolTable as SymbolTable).names[dataName.uppercase()] }
}

override operator fun set(data: AbstractDataDefinition, value: Value): Value? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,15 +672,15 @@ class JarikoCallbackTest : AbstractTest() {
*/
@Test
fun executeERROR36CallBackTest() {
TABDS01LDbMock().usePopulated {
TABDS01LDbMock().usePopulated({
executePgmCallBackTest(
pgm = "ERROR36",
sourceReferenceType = SourceReferenceType.Program,
sourceId = "ERROR36",
lines = listOf(6),
reloadConfig = it.createReloadConfig()
)
}
})
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package com.smeup.rpgparser.interpreter

import com.smeup.rpgparser.AbstractTest
import com.smeup.rpgparser.PerformanceTest
import com.smeup.rpgparser.parsing.ast.CompilationUnit
import org.junit.experimental.categories.Category
import kotlin.test.*
import kotlin.time.DurationUnit
import kotlin.time.measureTime

/**
* The purpose of this test suite is to validate the behaviour around Symbol Table.
* The resources about this suite starts with `ST` (Symbol Table), followed by any string which describes the purpose.
*/
class SymbolTableTest : AbstractTest() {
/**
* Performance test for accessing standalone fields and data structure fields in a symbol table.
*
* This test measures the execution time required to perform repeated lookups of standalone fields and
* data structure fields in a symbol table created from an Abstract Syntax Tree (AST). It verifies that
* the AST can be produced successfully and evaluates the performance of symbol table lookups for
* specific fields.
*
* Steps:
* 1. Produce an AST from the specified example program (`symboltable/ST_PERFORMANCE_ACCESS01`).
* 2. Create a symbol table (`ISymbolTable`) from the AST.
* 3. Perform 1,000,000 lookups for the standalone field `VAR1`.
* 4. Perform 1,000,000 lookups for the last field of the data structure `DS10_FLD50`.
* 5. Measure and log the total execution time for these operations.
*
* The goal is to evaluate the efficiency of symbol table lookups and ensure performance is within an acceptable range.
*
* @throws TimeoutException if the test does not complete within 6 seconds
* @see ISymbolTable
*/
@Test
@Category(PerformanceTest::class)
fun executeST_F_WITH_DS_UNQUALIFIED1_PERFORMANCE() {
assertASTCanBeProduced(
exampleName = "symboltable/ST_PERFORMANCE_ACCESS01",
afterAstCreation = { ast ->
val symbolTable: ISymbolTable = ast.createSymbolTable()

val timeVAR1 = measureTime {
for (i in 1..1_000_000) {
symbolTable.dataDefinitionByName("VAR1")
}
}.also { time ->
println("Time execution during the resolution of VAR1: $time")
}

val timeDS10_FLD50 = measureTime {
for (i in 1..1_000_000) {
symbolTable.dataDefinitionByName("DS10_FLD50")
}
}.also { time ->
println("Time execution during the resolution of DS10_FLD50: $time")
}

println("Ratio execution during the resolution of Standalone and DS field: ${timeVAR1 / timeDS10_FLD50}")

assertTrue((timeVAR1 + timeDS10_FLD50).toLong(DurationUnit.MILLISECONDS) < 3000)
}
)
}

/**
* Test for validating symbol table lookups of standalone fields and data structure fields.
*
* This test verifies the correctness of symbol table entries for:
* 1. A standalone field (`VAR1`).
* 2. Fields within a data structure (`DS10_FLD50` and `DS10_FLD51`).
*
* Steps:
* 1. Produce an Abstract Syntax Tree (AST) from the example program `symboltable/ST_PERFORMANCE_ACCESS01`.
* 2. Create a symbol table (`ISymbolTable`) from the AST.
* 3. Perform lookups for:
* - `VAR1`: A standalone field expected to be found as a `DataDefinition`.
* - `DS10_FLD50`: A field in the data structure `DS1`, expected to be found as a `FieldDefinition`.
* - `DS10_FLD51`: Another field, expected not to exist in the symbol table (returns `null`).
* 4. Assert the types and properties of the retrieved definitions:
* - Verify `VAR1` is a `DataDefinition`.
* - Verify `DS10_FLD50` is a `FieldDefinition` and belongs to the parent data structure `DS1`.
* - Verify `DS10_FLD51` is not found in the symbol table (`null`).
*
* Assertions:
* - The type and parent relationships of the retrieved definitions are validated.
* - The name of the parent data structure for `DS10_FLD50` is confirmed as `DS1`.
* - It is asserted that `DS10_FLD51` does not exist in the symbol table.
*
* @see ISymbolTable
* @see DataDefinition
* @see FieldDefinition
*/
@Test
fun executeST_F_WITH_DS_UNQUALIFIED1() {
assertASTCanBeProduced(
exampleName = "symboltable/ST_PERFORMANCE_ACCESS01",
afterAstCreation = { ast ->
val symbolTable: ISymbolTable = ast.createSymbolTable()

val dataDefinition = symbolTable.dataDefinitionByName("VAR1")
val fieldDefinition1 = symbolTable.dataDefinitionByName("DS10_FLD50")
val fieldDefinition2 = symbolTable.dataDefinitionByName("DS10_FLD51")

assertIs<DataDefinition>(dataDefinition)
assertIs<FieldDefinition>(fieldDefinition1)
assertIs<DataDefinition>(fieldDefinition1.parent)
assertEquals("DS10", (fieldDefinition1.parent as DataDefinition).name, "DS10_FLD50 is field DS1.")
assertNull(fieldDefinition2, "DS10_FLD51 field not found.")
}
)
}

/**
* Creates a symbol table for the current `CompilationUnit`, mapping each `DataDefinition` to its corresponding `Value`.
*
* The symbol table (`ISymbolTable`) acts as a container for `DataDefinition`-to-`Value` mappings.
* For each `DataDefinition` in the `CompilationUnit`, this function generates a pair consisting of:
* - The `DataDefinition` as the key.
* - The associated `Value`, which is either the default value of the `DataDefinition` or a blank value based on its type.
*
* @return an `ISymbolTable` containing mappings of all `DataDefinition` objects in the `CompilationUnit`
*/
private fun CompilationUnit.createSymbolTable(): ISymbolTable {
val symbolTable: ISymbolTable = SymbolTable()
for (pair in this.dataDefinitions.map { dataDefinition -> makePairDataDefinitionValue(dataDefinition) }) {
symbolTable[pair.first] = pair.second
}

return symbolTable
}

/**
* Creates a key-value pair for a `DataDefinition` and its associated `Value`.
*
* This function takes a `DataDefinition` and generates a pair consisting of:
* - The `DataDefinition` itself as the key.
* - The associated `Value`, which is determined as follows:
* - If the `DataDefinition` has a `defaultValue`, that is used.
* - Otherwise, a blank value is generated based on the `DataDefinition`'s type.
*
* @param dataDefinition the `DataDefinition` for which the key-value pair is created
* @return a `Pair` where the key is the `DataDefinition` and the value is the associated `Value`
*/
private fun makePairDataDefinitionValue(dataDefinition: DataDefinition): Pair<DataDefinition, Value> {
return Pair(
dataDefinition,
dataDefinition.defaultValue ?: dataDefinition.type.blank()
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.smeup.rpgparser.interpreter.DataDefinition
import com.smeup.rpgparser.interpreter.DataStructureType
import com.smeup.rpgparser.interpreter.StringType
import com.smeup.rpgparser.parsing.parsetreetoast.resolveAndValidate
import com.smeup.rpgparser.smeup.dbmock.C5RREGHLDbMock
import com.smeup.rpgparser.smeup.dbmock.MULANGTLDbMock
import org.junit.Test
import kotlin.test.*
Expand Down Expand Up @@ -201,13 +202,13 @@ open class MULANGT02ConstAndDSpecTest : MULANGTTest() {
*/
@Test
fun executeMUDRNRAPU00101() {
MULANGTLDbMock().usePopulated {
MULANGTLDbMock().usePopulated({
val expected = listOf("HELLO THERE")
assertEquals(
expected = expected,
"smeup/MUDRNRAPU00101".outputOf(configuration = smeupConfig)
)
}
})
}

/**
Expand Down Expand Up @@ -269,10 +270,10 @@ open class MULANGT02ConstAndDSpecTest : MULANGTTest() {

@Test
fun executeMUDRNRAPU00202() {
MULANGTLDbMock().usePopulated {
MULANGTLDbMock().usePopulated({
val expected = listOf("ok")
assertEquals(expected, "smeup/MUDRNRAPU00202".outputOf(configuration = smeupConfig))
}
})
}

/**
Expand Down Expand Up @@ -861,6 +862,20 @@ open class MULANGT02ConstAndDSpecTest : MULANGTTest() {
assertEquals(expected, "smeup/MUDRNRAPU00171".outputOf(configuration = turnOnZAddLegacyFlagConfig))
}

/**
* Writing on a field of DS which use `EXTNAME` of a file.
* @see #LS25000142
*/
@Test
fun executeMUDRNRAPU00189() {
MULANGTLDbMock().usePopulated({
val expected = listOf("IBMI", "", "IBMI", "MULANGT00", "", "", "IBMI", "MULANGT00")
assertEquals(expected, "smeup/MUDRNRAPU00189".outputOf(configuration = smeupConfig))
},
listOf(mapOf("MLSYST" to "IBMI", "MLPROG" to "MULANGT00"))
)
}

/**
* Reading from a field of DS with dot notation. This DS have the same fields of another.
* @see #LS25000142
Expand Down Expand Up @@ -909,4 +924,25 @@ open class MULANGT02ConstAndDSpecTest : MULANGTTest() {
assertIs<StringType>(bDefinition?.type)
assertEquals(aDefinition?.elementSize(), bDefinition?.elementSize())
}
}

/**
* Writing on a field of DS which use `EXTNAME` of a file. In this case the file in `EXTNAME` is different
* from `F` spec but shares same fields.
* @see #LS25000430
*/
@Test
@Ignore("Is requested an improvement for mocked values and by changing the metadata and KLIST.")
fun executeMUDRNRAPU00192() {
C5RREGHLDbMock().usePopulated({
val expected = listOf(
"01", "2009", "", "", "",
"01", "2009", "", "", "1234007"
)
assertEquals(expected, "smeup/MUDRNRAPU00192".outputOf(configuration = smeupConfig))
},
listOf(
mapOf("R5AZIE" to "01", "R5ESER" to "2009", "R5TPCN" to "", "R5SOGG" to "", "R5CONT" to "1234007")
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -421,9 +421,9 @@ open class MULANGT10BaseCodopTest : MULANGTTest() {
@Test
fun executeMUDRNRAPU00272() {
val expected = listOf("ok", "ok", "ok", "ok")
C5ADFF9LDbMock().usePopulated {
C5ADFF9LDbMock().usePopulated({
assertEquals(expected, "smeup/MUDRNRAPU00272".outputOf(configuration = smeupConfig))
}
})
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ open class MULANGT50FileAccess1Test : MULANGTTest() {

@Test
fun executeMUDRNRAPU00254() {
MULANGTLDbMock().usePopulated {
MULANGTLDbMock().usePopulated({
val expected = listOf("1.000000000")
assertEquals(expected, "smeup/MUDRNRAPU00254".outputOf(configuration = smeupConfig))
}
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.smeup.rpgparser.smeup.dbmock

import com.smeup.rpgparser.interpreter.FileMetadata
import com.smeup.rpgparser.utils.DbMock
import java.io.File

class C5ADFF9LDbMock : DbMock {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.smeup.rpgparser.smeup.dbmock

import com.smeup.rpgparser.interpreter.FileMetadata
import com.smeup.rpgparser.utils.DbMock
import java.io.File

class C5RREGHLDbMock : DbMock {
override val metadata =
FileMetadata.createInstance(File("src/test/resources/smeup/metadata/C5RREGHL.json").inputStream())
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.smeup.rpgparser.smeup.dbmock

import com.smeup.rpgparser.interpreter.FileMetadata
import com.smeup.rpgparser.utils.DbMock
import java.io.File

class MULANGTLDbMock : DbMock {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.smeup.rpgparser.smeup.dbmock

import com.smeup.rpgparser.interpreter.FileMetadata
import com.smeup.rpgparser.utils.DbMock
import java.io.File

class TABDS01LDbMock : DbMock {
Expand Down
Loading

0 comments on commit 3fdc667

Please sign in to comment.