evaluator: significant performance enhancements

This commit is contained in:
alex committed 2023-09-21 17:21:53 -07:00
1 parent 1a759b9746
commit 4758e92676
21 files changed
+257 -68

No files matched your search

@@ -1,3 +1,3 @@
package gay.pizza.pork.evaluator package gay.pizza.pork.evaluator
class Arguments(val values: List<Any>) typealias ArgumentList = List<Any>
@@ -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
} }
@@ -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())
}
@@ -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
} }
@@ -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)
} }
} }
@@ -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 {
val visitor = this
return object : BlockFunction() {
override fun call(): Any {
var value: Any? = null var value: Any? = null
for (expression in node.expressions) { for (expression in node.expressions) {
value = expression.visit(this) 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
}
} }
} }
@@ -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!!)
try {
return blockFunction.call() return blockFunction.call()
} catch (e: PorkError) {
throw e
} catch (e: Exception) {
val stackForError = stack.copy()
throw PorkError(e, stackForError)
} finally {
scope.disown()
stack.pop()
}
} }
} }
@@ -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] }
}
} }
@@ -0,0 +1,3 @@
package gay.pizza.pork.evaluator
class PorkError(cause: Exception, stack: CallStack) : RuntimeException(stack.toString(), cause)
@@ -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")
}
} }
@@ -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)
}
}
@@ -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"
} }
@@ -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)
}
}
@@ -2,5 +2,6 @@ package gay.pizza.pork.evaluator
enum class ValueStoreType { enum class ValueStoreType {
Let, Let,
Var Var,
ReuseReady
} }
+4 -8
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)
} }
@@ -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) {
@@ -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)
} }
} }
@@ -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())
} }
} }
@@ -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)
} }
+6
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"
@@ -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())
} }
} }
} }