Skip to content

Commit 048951c

Browse files
Merged develop
2 parents 8516dab + f348f40 commit 048951c

File tree

10 files changed

+854
-334
lines changed

10 files changed

+854
-334
lines changed

rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/execution/Configuration.kt

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,37 @@ data class JarikoCallback(
287287
* @return true if the feature flag is on, false otherwise - default implementation returns the feature flag default value
288288
* @see FeatureFlag.on
289289
* */
290-
var featureFlagIsOn: ((featureFlag: FeatureFlag) -> Boolean) = { featureFlag -> featureFlag.on }
290+
var featureFlagIsOn: ((featureFlag: FeatureFlag) -> Boolean) = { featureFlag -> featureFlag.on },
291+
292+
/**
293+
* It is invoked whenever we start a telemetry trace.
294+
* @param trace The object containing all the information about this trace.
295+
*/
296+
var startJarikoTrace: ((trace: JarikoTrace) -> Unit) = {
297+
// Defaults to a no-op
298+
},
299+
300+
/**
301+
* It is invoked whenever we finish a telemetry trace.
302+
*/
303+
var finishJarikoTrace: (() -> Unit) = {
304+
// Defaults to a no-op
305+
},
306+
307+
/**
308+
* It is invoked whenever we start a telemetry trace defined as annotation in an RPG program.
309+
* @param trace The object containing all the information about this trace.
310+
*/
311+
var startRpgTrace: ((trace: RpgTrace) -> Unit) = {
312+
// Defaults to a no-op
313+
},
314+
315+
/**
316+
* It is invoked whenever we finish a telemetry trace defined as annotation in an RPG program.
317+
*/
318+
var finishRpgTrace: (() -> Unit) = {
319+
// Defaults to a no-op
320+
}
291321
)
292322

293323
/**

rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/execution/MainExecutionContext.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,12 @@ object MainExecutionContext {
8686
)
8787
}
8888
return mainProgram.runCatching {
89-
invoke(context.get())
89+
val ctx = context.get()
90+
val callback = ctx.configuration.jarikoCallback
91+
val trace = JarikoTrace(JarikoTraceKind.MainExecutionContext)
92+
callback.traceBlock(trace) {
93+
invoke(ctx)
94+
}
9095
}.onFailure {
9196
if (isRootContext) memorySliceMgr?.afterMainProgramInterpretation(false)
9297
}.onSuccess {

rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/ExpressionEvaluation.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -533,9 +533,12 @@ class ExpressionEvaluation(
533533

534534
override fun eval(expression: FunctionCall): Value = proxyLogging(expression) {
535535
val functionToCall = expression.function.name
536-
val function = systemInterface.findFunction(interpreterStatus.symbolTable, functionToCall)
537-
?: throw RuntimeException("Function $functionToCall cannot be found (${expression.position.line()})")
538-
FunctionWrapper(function = function, functionName = functionToCall, expression).let { functionWrapper ->
536+
val callback = MainExecutionContext.getConfiguration().jarikoCallback
537+
val trace = JarikoTrace(JarikoTraceKind.FunctionCall, functionToCall)
538+
callback.traceBlock(trace) {
539+
val function = systemInterface.findFunction(interpreterStatus.symbolTable, functionToCall)
540+
?: throw RuntimeException("Function $functionToCall cannot be found (${expression.position.line()})")
541+
val functionWrapper = FunctionWrapper(function = function, functionName = functionToCall, expression)
539542
val paramsValues = expression.args.map {
540543
if (it is DataRefExpr) {
541544
FunctionValue(variableName = it.variable.name, value = it.evalWith(this))

rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/internal_interpreter.kt

Lines changed: 191 additions & 130 deletions
Large diffs are not rendered by default.

rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/program.kt

Lines changed: 111 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* https://www.apache.org/licenses/LICENSE-2.0
8+
* https://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
15-
*
1615
*/
1716

1817
package com.smeup.rpgparser.interpreter
1918

19+
import com.smeup.rpgparser.execution.Configuration
2020
import com.smeup.rpgparser.execution.MainExecutionContext
2121
import com.smeup.rpgparser.logging.ProgramUsageType
2222
import com.smeup.rpgparser.parsing.ast.*
@@ -39,9 +39,13 @@ class RpgProgram(val cu: CompilationUnit, val name: String = "<UNNAMED RPG PROGR
3939

4040
private var systemInterface: SystemInterface? = null
4141

42+
private val configuration: Configuration by lazy {
43+
MainExecutionContext.getConfiguration()
44+
}
45+
4246
private val interpreter: InternalInterpreter by lazy {
4347
val interpreterCore = InternalInterpreter(this.systemInterface!!)
44-
MainExecutionContext.getConfiguration().jarikoCallback.onInterpreterCreation(interpreterCore)
48+
configuration.jarikoCallback.onInterpreterCreation(interpreterCore)
4549
interpreterCore
4650
}
4751

@@ -82,102 +86,122 @@ class RpgProgram(val cu: CompilationUnit, val name: String = "<UNNAMED RPG PROGR
8286
}
8387

8488
override fun execute(systemInterface: SystemInterface, params: LinkedHashMap<String, Value>): List<Value> {
85-
val expectedKeys = params().asSequence().map { it.name }.toSet()
89+
val callback = configuration.jarikoCallback
90+
val trace = JarikoTrace(JarikoTraceKind.RpgProgram, this.name)
91+
return callback.traceBlock(trace) {
92+
val expectedKeys = params().asSequence().map { it.name }.toSet()
8693

87-
// Original params passed from the caller
88-
val callerParams = LinkedHashMap(params)
94+
// Original params passed from the caller
95+
val callerParams = LinkedHashMap(params)
8996

90-
if (expectedKeys.size <= params.size) {
91-
require(params.keys.toSet() == params().asSequence().map { it.name }.toSet()) {
92-
"Expected params: ${params().asSequence().map { it.name }.joinToString(", ")}"
93-
}
94-
} else {
95-
require(params().asSequence().map { it.name }.toSet().all { it in expectedKeys }) {
96-
"Expected params: ${params().asSequence().map { it.name }.joinToString(", ")}"
97-
}
98-
99-
// Set not passed params to NullValue
100-
params().forEach {
101-
if (it.name !in params.keys) {
102-
params[it.name] = NullValue
97+
if (expectedKeys.size <= params.size) {
98+
require(params.keys.toSet() == params().asSequence().map { it.name }.toSet()) {
99+
"Expected params: ${params().asSequence().map { it.name }.joinToString(", ")}"
100+
}
101+
} else {
102+
require(params().asSequence().map { it.name }.toSet().all { it in expectedKeys }) {
103+
"Expected params: ${params().asSequence().map { it.name }.joinToString(", ")}"
103104
}
104-
}
105-
}
106-
this.systemInterface = systemInterface
107-
val logSource = { LogSourceData.fromProgram(name) }
108-
logHandlers.renderLog(LazyLogEntry.produceStatement(logSource, "INTERPRETATION", "START"))
109-
val changedInitialValues: List<Value>
110-
val elapsed = measureNanoTime {
111-
interpreter.setInterpretationContext(object : InterpretationContext {
112-
private var iDataWrapUpChoice: DataWrapUpChoice? = null
113-
override val currentProgramName: String
114-
get() = name
115-
override fun shouldReinitialize() = false
116-
override var dataWrapUpChoice: DataWrapUpChoice?
117-
get() = iDataWrapUpChoice
118-
set(value) {
119-
iDataWrapUpChoice = value
120-
}
121-
})
122105

123-
for (pv in params) {
124-
val expectedType = params().find { it.name == pv.key }!!.type
125-
val coercedValue = coerce(pv.value, expectedType)
126-
require(coercedValue.assignableTo(expectedType)) {
127-
"param ${pv.key} was expected to have type $expectedType. It has value: $coercedValue"
106+
// Set not passed params to NullValue
107+
params().forEach {
108+
if (it.name !in params.keys) {
109+
params[it.name] = NullValue
110+
}
128111
}
129112
}
130-
if (!initialized) {
131-
initialized = true
132-
133-
/**
134-
* As the RPG program stack is managed outside of this method, it is up to the caller of this method
135-
* to ensure it is in the correct state, that is:
136-
* - `lastIndex` is this RpgProgram
137-
* - `lastIndex - 1` is the RpgProgram that calls this RpgProgram
138-
*
139-
* Note: If these two rules are not followed at this point, do not expect RpgPrograms to behave correctly.
140-
* that means something is wrong with `MainExecutionContext.getProgramStack()` push and pop logic.
141-
*/
142-
val programStack = MainExecutionContext.getProgramStack()
143-
val caller = if (programStack.size > 1) {
144-
val parentProgramIndex = programStack.lastIndex - 1
145-
programStack[parentProgramIndex]
146-
} else {
147-
null
113+
this.systemInterface = systemInterface
114+
val logSource = { LogSourceData.fromProgram(name) }
115+
logHandlers.renderLog(LazyLogEntry.produceStatement(logSource, "INTERPRETATION", "START"))
116+
val changedInitialValues: List<Value>
117+
val elapsed = measureNanoTime {
118+
interpreter.setInterpretationContext(object : InterpretationContext {
119+
private var iDataWrapUpChoice: DataWrapUpChoice? = null
120+
override val currentProgramName: String
121+
get() = name
122+
123+
override fun shouldReinitialize() = false
124+
override var dataWrapUpChoice: DataWrapUpChoice?
125+
get() = iDataWrapUpChoice
126+
set(value) {
127+
iDataWrapUpChoice = value
128+
}
129+
})
130+
131+
for (pv in params) {
132+
val expectedType = params().find { it.name == pv.key }!!.type
133+
val coercedValue = coerce(pv.value, expectedType)
134+
require(coercedValue.assignableTo(expectedType)) {
135+
"param ${pv.key} was expected to have type $expectedType. It has value: $coercedValue"
136+
}
148137
}
138+
if (!initialized) {
139+
initialized = true
140+
141+
/**
142+
* As the RPG program stack is managed outside of this method, it is up to the caller of this method
143+
* to ensure it is in the correct state, that is:
144+
* - `lastIndex` is this RpgProgram
145+
* - `lastIndex - 1` is the RpgProgram that calls this RpgProgram
146+
*
147+
* Note: If these two rules are not followed at this point, do not expect RpgPrograms to behave correctly.
148+
* that means something is wrong with `MainExecutionContext.getProgramStack()` push and pop logic.
149+
*/
150+
val programStack = MainExecutionContext.getProgramStack()
151+
val caller = if (programStack.size > 1) {
152+
val parentProgramIndex = programStack.lastIndex - 1
153+
programStack[parentProgramIndex]
154+
} else {
155+
null
156+
}
149157

150-
val activationGroupType = cu.activationGroupType()?.let {
151-
when {
152-
// When there is no caller use the default activation group
153-
it is CallerActivationGroup && caller == null ->
154-
NamedActivationGroup(MainExecutionContext.getConfiguration().defaultActivationGroupName)
155-
else -> it
158+
val activationGroupType = cu.activationGroupType()?.let {
159+
when {
160+
// When there is no caller use the default activation group
161+
it is CallerActivationGroup && caller == null ->
162+
NamedActivationGroup(configuration.defaultActivationGroupName)
163+
164+
else -> it
165+
}
166+
} ?: when (caller) {
167+
// for main program, which does not have a caller, activation group is fixed by config
168+
null -> NamedActivationGroup(configuration.defaultActivationGroupName)
169+
else -> CallerActivationGroup
156170
}
157-
} ?: when (caller) {
158-
// for main program, which does not have a caller, activation group is fixed by config
159-
null -> NamedActivationGroup(MainExecutionContext.getConfiguration().defaultActivationGroupName)
160-
else -> CallerActivationGroup
161-
}
162171

163-
activationGroup = ActivationGroup(activationGroupType, activationGroupType.assignedName(caller))
172+
activationGroup = ActivationGroup(activationGroupType, activationGroupType.assignedName(caller))
173+
}
174+
configuration.jarikoCallback.onEnterPgm(
175+
name,
176+
interpreter.getGlobalSymbolTable()
177+
)
178+
// set reinitialization to false because symboltable cleaning currently is handled directly
179+
// in internal interpreter before exit
180+
// todo i don't know whether parameter reinitialization has still sense
181+
interpreter.execute(this.cu, params, false, callerParams)
182+
configuration.jarikoCallback.onExitPgm(
183+
name,
184+
interpreter.getGlobalSymbolTable(),
185+
null
186+
)
187+
params.keys.forEach { params[it] = interpreter[it] }
188+
changedInitialValues = params().map { interpreter[it.name] }
189+
// here clear symbol table if needed
190+
interpreter.doSomethingAfterExecution()
191+
}.nanoseconds
192+
if (MainExecutionContext.isLoggingEnabled) {
193+
logHandlers.renderLog(LazyLogEntry.produceStatement(logSource, "INTERPRETATION", "END"))
194+
logHandlers.renderLog(
195+
LazyLogEntry.producePerformanceAndUpdateAnalytics(
196+
logSource,
197+
ProgramUsageType.Interpretation,
198+
"INTERPRETATION",
199+
elapsed
200+
)
201+
)
164202
}
165-
MainExecutionContext.getConfiguration().jarikoCallback.onEnterPgm(name, interpreter.getGlobalSymbolTable())
166-
// set reinitialization to false because symboltable cleaning currently is handled directly
167-
// in internal interpreter before exit
168-
// todo i don't know whether parameter reinitialization has still sense
169-
interpreter.execute(this.cu, params, false, callerParams)
170-
MainExecutionContext.getConfiguration().jarikoCallback.onExitPgm(name, interpreter.getGlobalSymbolTable(), null)
171-
params.keys.forEach { params[it] = interpreter[it] }
172-
changedInitialValues = params().map { interpreter[it.name] }
173-
// here clear symbol table if needed
174-
interpreter.doSomethingAfterExecution()
175-
}.nanoseconds
176-
if (MainExecutionContext.isLoggingEnabled) {
177-
logHandlers.renderLog(LazyLogEntry.produceStatement(logSource, "INTERPRETATION", "END"))
178-
logHandlers.renderLog(LazyLogEntry.producePerformanceAndUpdateAnalytics(logSource, ProgramUsageType.Interpretation, "INTERPRETATION", elapsed))
203+
changedInitialValues
179204
}
180-
return changedInitialValues
181205
}
182206

183207
override fun equals(other: Any?) =
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2019 Sme.UP S.p.A.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* imitations under the License.
15+
*
16+
*/
17+
18+
package com.smeup.rpgparser.interpreter
19+
20+
import com.smeup.rpgparser.execution.JarikoCallback
21+
22+
/**
23+
* Kind of a trace
24+
*/
25+
enum class JarikoTraceKind {
26+
Parsing,
27+
SymbolTable,
28+
CompositeStatement,
29+
CallStmt,
30+
ExecuteSubroutine,
31+
FunctionCall,
32+
MainExecutionContext,
33+
RpgProgram
34+
}
35+
36+
/**
37+
* A trace emitted by Jariko for telemetry purposes
38+
*/
39+
data class JarikoTrace(
40+
val kind: JarikoTraceKind,
41+
val description: String = ""
42+
)
43+
44+
/**
45+
* A trace emitted by an RPG program for telemetry purposes
46+
*/
47+
data class RpgTrace(
48+
val program: String,
49+
val description: String? = null
50+
)
51+
52+
internal fun <T> JarikoCallback.traceBlock(trace: JarikoTrace, block: () -> T): T {
53+
startJarikoTrace(trace)
54+
try {
55+
return block()
56+
} catch (e: Exception) {
57+
throw e
58+
} finally {
59+
finishJarikoTrace()
60+
}
61+
}

0 commit comments

Comments
 (0)