evaluator: significant performance enhancements

This commit is contained in:
Alex Zenla 2023-09-21 17:21:53 -07:00
parent 1a759b9746
commit 4758e92676
Signed by: alex
GPG Key ID: C0780728420EBFE5
21 changed files with 260 additions and 71 deletions

View File

@ -1,3 +1,3 @@
package gay.pizza.pork.evaluator package gay.pizza.pork.evaluator
class Arguments(val values: List<Any>) typealias ArgumentList = List<Any>

View File

@ -1,5 +1,5 @@
package gay.pizza.pork.evaluator package gay.pizza.pork.evaluator
fun interface BlockFunction { abstract class BlockFunction {
fun call(): Any abstract fun call(): Any
} }

View File

@ -0,0 +1,20 @@
package gay.pizza.pork.evaluator
class CallStack(val functionStack: MutableList<FunctionContext> = mutableListOf()) {
fun push(context: FunctionContext) {
functionStack.add(context)
}
fun pop() {
functionStack.removeLast()
}
override fun toString(): String = buildString {
appendLine("Pork Stacktrace:")
for (item in functionStack.asReversed()) {
appendLine(" at ${item.name}")
}
}.trimEnd()
fun copy(): CallStack = CallStack(functionStack.toMutableList())
}

View File

@ -1,5 +1,5 @@
package gay.pizza.pork.evaluator package gay.pizza.pork.evaluator
fun interface CallableFunction { fun interface CallableFunction {
fun call(arguments: Arguments): Any fun call(arguments: ArgumentList, stack: CallStack): Any
} }

View File

@ -7,7 +7,7 @@ class CompilationUnitContext(
val compilationUnit: CompilationUnit, val compilationUnit: CompilationUnit,
val evaluator: Evaluator, val evaluator: Evaluator,
rootScope: Scope, rootScope: Scope,
name: String = "unknown" val name: String = "unknown"
) { ) {
val internalScope = rootScope.fork("internal $name") val internalScope = rootScope.fork("internal $name")
val externalScope = rootScope.fork("external $name") val externalScope = rootScope.fork("external $name")
@ -40,7 +40,7 @@ class CompilationUnitContext(
private fun definitionValue(definition: Definition): Any = when (definition) { private fun definitionValue(definition: Definition): Any = when (definition) {
is FunctionDefinition -> FunctionContext(this, definition) is FunctionDefinition -> FunctionContext(this, definition)
is LetDefinition -> { is LetDefinition -> {
EvaluationVisitor(internalScope.fork("let ${definition.symbol.id}")) EvaluationVisitor(internalScope.fork("let ${definition.symbol.id}"), CallStack())
.visit(definition.value) .visit(definition.value)
} }
} }

View File

@ -3,7 +3,8 @@ package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.* import gay.pizza.pork.ast.*
import kotlin.math.abs import kotlin.math.abs
class EvaluationVisitor(root: Scope) : NodeVisitor<Any> { @Suppress("JavaIoSerializableObjectMustHaveReadResolve")
class EvaluationVisitor(root: Scope, val stack: CallStack) : NodeVisitor<Any> {
private var currentScope: Scope = root private var currentScope: Scope = root
override fun visitIntegerLiteral(node: IntegerLiteral): Any = node.value override fun visitIntegerLiteral(node: IntegerLiteral): Any = node.value
@ -16,9 +17,15 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
throw RuntimeException("Unable to iterate on value that is not a iterable.") throw RuntimeException("Unable to iterate on value that is not a iterable.")
} }
var reuseScope: Scope? = null
for (item in value) { for (item in value) {
try { try {
scoped { if (reuseScope == null) {
reuseScope = currentScope.fork(name = "ForIn")
}
scoped(reuseScope, node = node) {
currentScope.define(node.symbol.id, item ?: None) currentScope.define(node.symbol.id, item ?: None)
result = blockFunction.call() result = blockFunction.call()
} }
@ -28,6 +35,7 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
continue continue
} }
} }
reuseScope?.disown()
return result ?: None return result ?: None
} }
@ -46,7 +54,7 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
override fun visitFunctionCall(node: FunctionCall): Any { override fun visitFunctionCall(node: FunctionCall): Any {
val arguments = node.arguments.map { it.visit(this) } val arguments = node.arguments.map { it.visit(this) }
val functionValue = currentScope.value(node.symbol.id) as CallableFunction val functionValue = currentScope.value(node.symbol.id) as CallableFunction
return functionValue.call(Arguments(arguments)) return functionValue.call(arguments, stack)
} }
override fun visitLetAssignment(node: LetAssignment): Any { override fun visitLetAssignment(node: LetAssignment): Any {
@ -71,6 +79,7 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
override fun visitWhile(node: While): Any { override fun visitWhile(node: While): Any {
val blockFunction = node.block.visit(this) as BlockFunction val blockFunction = node.block.visit(this) as BlockFunction
var result: Any? = null var result: Any? = null
var reuseScope: Scope? = null
while (true) { while (true) {
val value = node.condition.visit(this) val value = node.condition.visit(this)
if (value !is Boolean) { if (value !is Boolean) {
@ -78,13 +87,17 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
} }
if (!value) break if (!value) break
try { try {
scoped { result = blockFunction.call() } if (reuseScope == null) {
reuseScope = currentScope.fork(name = "While")
}
scoped(reuseScope, node = node) { result = blockFunction.call() }
} catch (_: BreakMarker) { } catch (_: BreakMarker) {
break break
} catch (_: ContinueMarker) { } catch (_: ContinueMarker) {
continue continue
} }
} }
reuseScope?.disown()
return result ?: None return result ?: None
} }
@ -173,10 +186,10 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
val condition = node.condition.visit(this) val condition = node.condition.visit(this)
return if (condition == true) { return if (condition == true) {
val blockFunction = node.thenBlock.visit(this) as BlockFunction val blockFunction = node.thenBlock.visit(this) as BlockFunction
scoped { blockFunction.call() } scoped(node = node) { blockFunction.call() }
} else if (node.elseBlock != null) { } else if (node.elseBlock != null) {
val blockFunction = node.elseBlock!!.visit(this) as BlockFunction val blockFunction = node.elseBlock!!.visit(this) as BlockFunction
scoped { blockFunction.call() } scoped(node = node) { blockFunction.call() }
} else None } else None
} }
@ -350,12 +363,17 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
} }
} }
override fun visitBlock(node: Block): BlockFunction = BlockFunction { override fun visitBlock(node: Block): BlockFunction {
var value: Any? = null val visitor = this
for (expression in node.expressions) { return object : BlockFunction() {
value = expression.visit(this) override fun call(): Any {
var value: Any? = null
for (expression in node.expressions) {
value = expression.visit(visitor)
}
return value ?: None
}
} }
value ?: None
} }
override fun visitFunctionDefinition(node: FunctionDefinition): Any { override fun visitFunctionDefinition(node: FunctionDefinition): Any {
@ -393,12 +411,18 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
override fun visitContinue(node: Continue): Any = ContinueMarker override fun visitContinue(node: Continue): Any = ContinueMarker
private inline fun <T> scoped(block: () -> T): T { private inline fun <T> scoped(reuseScope: Scope? = null, node: Node? = null, block: () -> T): T {
currentScope = currentScope.fork() val previousScope = currentScope
currentScope = reuseScope ?: currentScope.fork(name = node?.type?.name)
try { try {
return block() return block()
} finally { } finally {
currentScope = currentScope.leave() if (reuseScope == null) {
currentScope = currentScope.leave(disown = true)
} else {
reuseScope.markForReuse()
currentScope = previousScope
}
} }
} }

View File

@ -3,6 +3,8 @@ package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.FunctionDefinition import gay.pizza.pork.ast.FunctionDefinition
class FunctionContext(val compilationUnitContext: CompilationUnitContext, val node: FunctionDefinition) : CallableFunction { class FunctionContext(val compilationUnitContext: CompilationUnitContext, val node: FunctionDefinition) : CallableFunction {
val name: String = "${compilationUnitContext.name} ${node.symbol.id}"
private fun resolveMaybeNative(): CallableFunction? = if (node.native == null) { private fun resolveMaybeNative(): CallableFunction? = if (node.native == null) {
null null
} else { } else {
@ -14,19 +16,19 @@ class FunctionContext(val compilationUnitContext: CompilationUnitContext, val no
private val nativeCached by lazy { resolveMaybeNative() } private val nativeCached by lazy { resolveMaybeNative() }
override fun call(arguments: Arguments): Any { override fun call(arguments: ArgumentList, stack: CallStack): Any {
if (nativeCached != null) { if (nativeCached != null) {
return nativeCached!!.call(arguments) return nativeCached!!.call(arguments, stack)
} }
val scope = compilationUnitContext.internalScope.fork() val scope = compilationUnitContext.internalScope.fork(node.symbol.id)
for ((index, spec) in node.arguments.withIndex()) { for ((index, spec) in node.arguments.withIndex()) {
if (spec.multiple) { if (spec.multiple) {
val list = arguments.values.subList(index, arguments.values.size - 1) val list = arguments.subList(index, arguments.size - 1)
scope.define(spec.symbol.id, list) scope.define(spec.symbol.id, list)
break break
} else { } else {
scope.define(spec.symbol.id, arguments.values[index]) scope.define(spec.symbol.id, arguments[index])
} }
} }
@ -34,8 +36,19 @@ class FunctionContext(val compilationUnitContext: CompilationUnitContext, val no
throw RuntimeException("Native or Block is required for FunctionDefinition") throw RuntimeException("Native or Block is required for FunctionDefinition")
} }
val visitor = EvaluationVisitor(scope) val visitor = EvaluationVisitor(scope, stack)
stack.push(this)
val blockFunction = visitor.visitBlock(node.block!!) val blockFunction = visitor.visitBlock(node.block!!)
return blockFunction.call() try {
return blockFunction.call()
} catch (e: PorkError) {
throw e
} catch (e: Exception) {
val stackForError = stack.copy()
throw PorkError(e, stackForError)
} finally {
scope.disown()
stack.pop()
}
} }
} }

View File

@ -5,22 +5,37 @@ import gay.pizza.pork.ast.ArgumentSpec
class InternalNativeProvider(val quiet: Boolean = false) : NativeProvider { class InternalNativeProvider(val quiet: Boolean = false) : NativeProvider {
private val functions = mutableMapOf( private val functions = mutableMapOf(
"print" to CallableFunction(::printValues), "print" to CallableFunction(::printValues),
"println" to CallableFunction(::printLine) "println" to CallableFunction(::printLine),
"listSet" to CallableFunction(::setInList),
"listInitWith" to CallableFunction(::listInitWith)
) )
override fun provideNativeFunction(definition: String, arguments: List<ArgumentSpec>): CallableFunction { override fun provideNativeFunction(definition: String, arguments: List<ArgumentSpec>): CallableFunction {
return functions[definition] ?: throw RuntimeException("Unknown Internal Function: $definition") return functions[definition] ?: throw RuntimeException("Unknown Internal Function: $definition")
} }
private fun printValues(arguments: Arguments): Any { private fun printValues(arguments: ArgumentList, stack: CallStack): Any {
if (quiet || arguments.values.isEmpty()) return None if (quiet || arguments.isEmpty()) return None
print(arguments.values.joinToString(" ")) print(arguments.joinToString(" "))
return None return None
} }
private fun printLine(arguments: Arguments): Any { private fun printLine(arguments: ArgumentList, stack: CallStack): Any {
if (quiet) return None if (quiet) return None
println(arguments.values.joinToString(" ")) println(arguments.joinToString(" "))
return None return None
} }
private fun setInList(arguments: ArgumentList, stack: CallStack): Any {
@Suppress("UNCHECKED_CAST")
val list = arguments[0] as MutableList<Any>
val value = arguments[2]
list[(arguments[1] as Number).toInt()] = value
return value
}
private fun listInitWith(arguments: ArgumentList, stack: CallStack): Any {
val size = (arguments[0] as Number).toInt()
return MutableList(size) { arguments[1] }
}
} }

View File

@ -0,0 +1,3 @@
package gay.pizza.pork.evaluator
class PorkError(cause: Exception, stack: CallStack) : RuntimeException(stack.toString(), cause)

View File

@ -1,19 +1,25 @@
package gay.pizza.pork.evaluator package gay.pizza.pork.evaluator
class Scope( class Scope(
val parent: Scope? = null, var parent: Scope? = null,
inherits: List<Scope> = emptyList(), var inherits: List<Scope> = emptyList(),
val name: String? = null var name: String? = null
) { ) {
private val inherited = inherits.toMutableList() private var isCurrentlyFree = false
private val variables = mutableMapOf<String, ValueStore>() private val variables = mutableMapOf<String, ValueStore>()
fun define(name: String, value: Any, type: ValueStoreType = ValueStoreType.Let) { fun define(name: String, value: Any, type: ValueStoreType = ValueStoreType.Let) {
val previous = variables.put(name, ValueStore(value, type)) val existing = variables[name]
if (previous != null) { if (existing != null) {
variables[name] = previous if (existing.type == ValueStoreType.ReuseReady) {
existing.type = type
existing.value = value
return
}
throw RuntimeException("Variable '${name}' is already defined") throw RuntimeException("Variable '${name}' is already defined")
} }
val store = ValueStoreCache.obtain(value, type)
variables[name] = store
} }
fun set(name: String, value: Any) { fun set(name: String, value: Any) {
@ -40,13 +46,13 @@ class Scope(
val holder = variables[name] val holder = variables[name]
if (holder == null) { if (holder == null) {
if (parent != null) { if (parent != null) {
val parentMaybeFound = parent.valueHolderOrNotFound(name) val parentMaybeFound = parent!!.valueHolderOrNotFound(name)
if (parentMaybeFound !== NotFound.Holder) { if (parentMaybeFound !== NotFound.Holder) {
return parentMaybeFound return parentMaybeFound
} }
} }
for (inherit in inherited) { for (inherit in inherits) {
val inheritMaybeFound = inherit.valueHolderOrNotFound(name) val inheritMaybeFound = inherit.valueHolderOrNotFound(name)
if (inheritMaybeFound !== NotFound.Holder) { if (inheritMaybeFound !== NotFound.Holder) {
return inheritMaybeFound return inheritMaybeFound
@ -54,21 +60,29 @@ class Scope(
} }
return NotFound.Holder return NotFound.Holder
} }
if (holder.type == ValueStoreType.ReuseReady) {
throw RuntimeException("Attempt to reuse ValueStore in the reused state, prior to definition.")
}
return holder return holder
} }
fun fork(name: String? = null): Scope = fun fork(name: String? = null): Scope =
Scope(this, name = name) ScopeCache.obtain(this, name = name)
internal fun inherit(scope: Scope) { internal fun inherit(scope: Scope) {
inherited.add(scope) val copy = inherits.toMutableList()
copy.add(scope)
inherits = copy
} }
fun leave(): Scope { fun leave(disown: Boolean = false): Scope {
if (parent == null) { val currentParent = parent ?: throw RuntimeException("Attempted to leave the root scope!")
throw RuntimeException("Attempted to leave the root scope!")
if (disown) {
disown()
} }
return parent
return currentParent
} }
fun crawlScopePath( fun crawlScopePath(
@ -79,7 +93,7 @@ class Scope(
block(key, path) block(key, path)
} }
for (inherit in inherited) { for (inherit in inherits) {
val mutablePath = path.toMutableList() val mutablePath = path.toMutableList()
mutablePath.add("inherit ${inherit.name ?: "unknown"}") mutablePath.add("inherit ${inherit.name ?: "unknown"}")
inherit.crawlScopePath(mutablePath, block) inherit.crawlScopePath(mutablePath, block)
@ -87,12 +101,56 @@ class Scope(
if (parent != null) { if (parent != null) {
val mutablePath = path.toMutableList() val mutablePath = path.toMutableList()
mutablePath.add("parent ${parent.name ?: "unknown"}") mutablePath.add("parent ${parent?.name ?: "unknown"}")
parent.crawlScopePath(mutablePath, block) parent?.crawlScopePath(mutablePath, block)
} }
} }
fun markForReuse() {
for (store in variables.values) {
store.type = ValueStoreType.ReuseReady
store.value = None
}
}
fun disown() {
for (store in variables.values) {
store.disown()
}
name = null
parent = null
inherits = emptyList()
variables.clear()
isCurrentlyFree = true
ScopeCache.put(this)
}
fun adopt(parent: Scope? = null, inherits: List<Scope>, name: String? = null) {
if (!isCurrentlyFree) {
throw RuntimeException("Scope is not free, but adopt() was attempted.")
}
this.parent = parent
this.inherits = inherits
this.name = name
}
private object NotFound { private object NotFound {
val Holder = ValueStore(NotFound, ValueStoreType.Let) val Holder = ValueStore(NotFound, ValueStoreType.Let)
} }
val path: String
get() = buildString {
val list = mutableListOf<String?>()
var current: Scope? = this@Scope
while (current != null) {
list.add(current.name ?: "unknown")
current = current.parent
}
append(list.reversed().joinToString(" -> "))
}
companion object {
fun root(): Scope = Scope(name = "root")
}
} }

View File

@ -0,0 +1,18 @@
package gay.pizza.pork.evaluator
object ScopeCache {
private val cache = mutableListOf<Scope>()
fun obtain(parent: Scope? = null, inherits: List<Scope> = emptyList(), name: String? = null): Scope {
val cachedScope = cache.removeFirstOrNull()
if (cachedScope != null) {
cachedScope.adopt(parent = parent, inherits = inherits, name = name)
return cachedScope
}
return Scope(parent = parent, inherits = inherits, name = name)
}
fun put(scope: Scope) {
cache.add(scope)
}
}

View File

@ -1,5 +1,23 @@
package gay.pizza.pork.evaluator package gay.pizza.pork.evaluator
class ValueStore(var value: Any, val type: ValueStoreType) { class ValueStore(var value: Any, var type: ValueStoreType) {
var isCurrentlyFree = false
fun disown() {
isCurrentlyFree = true
value = None
type = ValueStoreType.ReuseReady
ValueStoreCache.put(this)
}
fun adopt(value: Any, type: ValueStoreType) {
if (!isCurrentlyFree) {
throw RuntimeException("Attempted to adopt a ValueStore that is not free.")
}
isCurrentlyFree = false
this.value = value
this.type = type
}
override fun toString(): String = "${type.name}: $value" override fun toString(): String = "${type.name}: $value"
} }

View File

@ -0,0 +1,18 @@
package gay.pizza.pork.evaluator
object ValueStoreCache {
private val cache = mutableListOf<ValueStore>()
fun obtain(value: Any, type: ValueStoreType): ValueStore {
val cached = cache.removeFirstOrNull()
if (cached != null) {
cached.adopt(value, type)
return cached
}
return ValueStore(value, type)
}
fun put(store: ValueStore) {
cache.add(store)
}
}

View File

@ -2,5 +2,6 @@ package gay.pizza.pork.evaluator
enum class ValueStoreType { enum class ValueStoreType {
Let, Let,
Var Var,
ReuseReady
} }

View File

@ -1,7 +1,4 @@
import local SDL2 import local SDL2
import java java.util.List
import java java.util.ArrayList
import java java.util.Collections
let cellSize = 16 let cellSize = 16
let gridWidth = 64 let gridWidth = 64
@ -59,8 +56,7 @@ func drawCells(renderer, cells, swap) {
func createCellGrid() { func createCellGrid() {
let numCells = gridWidth * gridHeight let numCells = gridWidth * gridHeight
let init = java_util_Collections_nCopies(numCells, 0) listInitWith(numCells, 0)
java_util_ArrayList_new_collection(init)
} }
func getCell(cells, swap, x, y) { func getCell(cells, swap, x, y) {
@ -77,8 +73,8 @@ func setCell(cells, swap, x, y, state) {
let mask = if swap { 2 } else { 1 } let mask = if swap { 2 } else { 1 }
let idx = x + y * gridWidth let idx = x + y * gridWidth
let value = cells[idx] let value = cells[idx]
if state { java_util_ArrayList_set(cells, idx, value | mask) } if state { listSet(cells, idx, value | mask) }
else { java_util_ArrayList_set(cells, idx, value & (~mask)) } else { listSet(cells, idx, value & (~mask)) }
} }
} }
@ -156,7 +152,7 @@ export func main() {
drawGrid(rend) drawGrid(rend)
drawCells(rend, cells, page) drawCells(rend, cells, page)
if (modifiers & KMOD_LSHIFT) == KMOD_LSHIFT { if (modifiers & KMOD_LSHIFT) != KMOD_LSHIFT {
page = not page page = not page
gameOfLife(cells, page) gameOfLife(cells, page)
} }

View File

@ -22,7 +22,7 @@ class JavaNativeProvider : NativeProvider {
returnTypeClass, returnTypeClass,
parameterClasses parameterClasses
) )
return CallableFunction { functionArguments -> handle.invokeWithArguments(functionArguments.values) ?: None } return CallableFunction { functionArguments, _ -> handle.invokeWithArguments(functionArguments) ?: None }
} }
private fun lookupClass(name: String): Class<*> = when (name) { private fun lookupClass(name: String): Class<*> = when (name) {

View File

@ -13,17 +13,17 @@ class JnaNativeProvider : NativeProvider {
val library = NativeLibrary.getInstance(functionDefinition.library) val library = NativeLibrary.getInstance(functionDefinition.library)
val function = library.getFunction(functionDefinition.function) val function = library.getFunction(functionDefinition.function)
?: throw RuntimeException("Failed to find function ${functionDefinition.function} in library ${functionDefinition.library}") ?: throw RuntimeException("Failed to find function ${functionDefinition.function} in library ${functionDefinition.library}")
return CallableFunction { functionArgs -> return CallableFunction { functionArgs, _ ->
val ffiArgs = mutableListOf<Any?>() val ffiArgs = mutableListOf<Any?>()
for ((index, spec) in arguments.withIndex()) { for ((index, spec) in arguments.withIndex()) {
val ffiType = functionDefinition.parameters[index] val ffiType = functionDefinition.parameters[index]
if (spec.multiple) { if (spec.multiple) {
val variableArguments = functionArgs.values val variableArguments = functionArgs
.subList(index, functionArgs.values.size) .subList(index, functionArgs.size)
ffiArgs.addAll(variableArguments) ffiArgs.addAll(variableArguments)
break break
} else { } else {
val converted = convert(ffiType, functionArgs.values[index]) val converted = convert(ffiType, functionArgs[index])
ffiArgs.add(converted) ffiArgs.add(converted)
} }
} }

View File

@ -58,6 +58,6 @@ abstract class Tool {
addNativeProvider("ffi", JnaNativeProvider()) addNativeProvider("ffi", JnaNativeProvider())
addNativeProvider("java", JavaNativeProvider()) addNativeProvider("java", JavaNativeProvider())
}) })
main.call(Arguments(emptyList())) main.call(emptyList(), CallStack())
} }
} }

View File

@ -11,6 +11,5 @@ fun main(args: Array<String>) {
} }
val path = PlatformFsProvider.resolve(args[0]) val path = PlatformFsProvider.resolve(args[0])
val tool = FileTool(path) val tool = FileTool(path)
val scope = Scope() tool.run(Scope.root())
tool.run(scope)
} }

View File

@ -3,3 +3,9 @@ export func print(values...)
export func println(values...) export func println(values...)
native internal "println" native internal "println"
export func listSet(list, index, value)
native internal "listSet"
export func listInitWith(size, value)
native internal "listInitWith"

View File

@ -19,7 +19,7 @@ class RunCommand : CliktCommand(help = "Run Program", name = "run") {
override fun run() { override fun run() {
val tool = FileTool(PlatformFsProvider.resolve(path)) val tool = FileTool(PlatformFsProvider.resolve(path))
val scope = Scope() val scope = Scope.root()
val main = tool.loadMainFunction(scope, setupEvaluator = { val main = tool.loadMainFunction(scope, setupEvaluator = {
addNativeProvider("internal", InternalNativeProvider(quiet = quiet)) addNativeProvider("internal", InternalNativeProvider(quiet = quiet))
addNativeProvider("ffi", JnaNativeProvider()) addNativeProvider("ffi", JnaNativeProvider())
@ -35,7 +35,7 @@ class RunCommand : CliktCommand(help = "Run Program", name = "run") {
} }
maybeLoopAndMeasure(loop, measure) { maybeLoopAndMeasure(loop, measure) {
main.call(Arguments(emptyList())) main.call(emptyList(), CallStack())
} }
} }
} }