Use fast compiled callbacks in dynamic and flow helpers
This commit is contained in:
parent
c80900c503
commit
3be2892025
@ -809,7 +809,8 @@ open class ObjClass(
|
||||
if (initStmt is net.sergeych.lyng.Statement) {
|
||||
executeBytecodeWithSeed(instance.instanceScope, initStmt, "instance init")
|
||||
} else {
|
||||
initStmt.callOn(instance.instanceScope)
|
||||
(initStmt as? net.sergeych.lyng.BytecodeCallable)?.callOnFast(instance.instanceScope)
|
||||
?: initStmt.callOn(instance.instanceScope)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@ -821,13 +822,14 @@ open class ObjClass(
|
||||
c.instanceConstructor?.let { ctor ->
|
||||
val execScope =
|
||||
instance.instanceScope.createChildScope(args = argsForThis ?: Arguments.EMPTY, newThisObj = instance)
|
||||
ctor.callOn(execScope)
|
||||
(ctor as? net.sergeych.lyng.BytecodeCallable)?.callOnFast(execScope) ?: ctor.callOn(execScope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun callWithArgs(scope: Scope, vararg plainArgs: Obj): Obj {
|
||||
return callOn(scope.createChildScope(Arguments(*plainArgs)))
|
||||
val child = scope.createChildScope(Arguments(*plainArgs))
|
||||
return (this as? net.sergeych.lyng.BytecodeCallable)?.callOnFast(child) ?: callOn(child)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -142,7 +142,7 @@ object ObjDecimalSupport {
|
||||
}
|
||||
val child = requireScope().createChildScope()
|
||||
child.addConst(decimalContextVar, context)
|
||||
block.callOn(child)
|
||||
(block as? net.sergeych.lyng.BytecodeCallable)?.callOnFast(child) ?: block.callOn(child)
|
||||
}
|
||||
registerBuiltinConversions(decimalClass)
|
||||
registerInterop(decimalClass)
|
||||
|
||||
@ -64,13 +64,19 @@ open class ObjDynamic(var readCallback: Obj? = null, var writeCallback: Obj? = n
|
||||
return (callback as? BytecodeLambdaCallable)?.rebindClosure(context) ?: callback
|
||||
}
|
||||
|
||||
private suspend fun callCallback(callback: Obj, child: Scope): Obj {
|
||||
return (callback as? net.sergeych.lyng.BytecodeCallable)?.callOnFast(child) ?: callback.callOn(child)
|
||||
}
|
||||
|
||||
/**
|
||||
* Use read callback to dynamically resolve the field name. Note that it does not work
|
||||
* with method invocation which is implemented separately in [invokeInstanceMethod] below.
|
||||
*/
|
||||
override suspend fun readField(scope: Scope, name: String): ObjRecord {
|
||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
||||
return readCallback?.callOn(execBase.createChildScope(Arguments(ObjString(name))))?.let {
|
||||
return readCallback?.let { callback ->
|
||||
callCallback(callback, execBase.createChildScope(Arguments(ObjString(name))))
|
||||
}?.let {
|
||||
if (writeCallback != null)
|
||||
it.asMutable
|
||||
else
|
||||
@ -90,26 +96,34 @@ open class ObjDynamic(var readCallback: Obj? = null, var writeCallback: Obj? = n
|
||||
onNotFoundResult: (suspend () -> Obj?)?
|
||||
): Obj {
|
||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
||||
val over = readCallback?.callOn(execBase.createChildScope(Arguments(ObjString(name))))
|
||||
val over = readCallback?.let { callback ->
|
||||
callCallback(callback, execBase.createChildScope(Arguments(ObjString(name))))
|
||||
}
|
||||
return over?.invoke(scope, scope.thisObj, args)
|
||||
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
||||
}
|
||||
|
||||
override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
|
||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
||||
writeCallback?.callOn(execBase.createChildScope(Arguments(ObjString(name), newValue)))
|
||||
writeCallback?.let { callback ->
|
||||
callCallback(callback, execBase.createChildScope(Arguments(ObjString(name), newValue)))
|
||||
}
|
||||
?: super.writeField(scope, name, newValue)
|
||||
}
|
||||
|
||||
override suspend fun getAt(scope: Scope, index: Obj): Obj {
|
||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
||||
return readCallback?.callOn(execBase.createChildScope(Arguments(index)))
|
||||
return readCallback?.let { callback ->
|
||||
callCallback(callback, execBase.createChildScope(Arguments(index)))
|
||||
}
|
||||
?: super.getAt(scope, index)
|
||||
}
|
||||
|
||||
override suspend fun putAt(scope: Scope, index: Obj, newValue: Obj) {
|
||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
||||
writeCallback?.callOn(execBase.createChildScope(Arguments(index, newValue)))
|
||||
writeCallback?.let { callback ->
|
||||
callCallback(callback, execBase.createChildScope(Arguments(index, newValue)))
|
||||
}
|
||||
?: super.putAt(scope, index, newValue)
|
||||
}
|
||||
|
||||
@ -124,7 +138,7 @@ open class ObjDynamic(var readCallback: Obj? = null, var writeCallback: Obj? = n
|
||||
// Snapshot the caller scope to capture locals/args even if the runtime pools/reuses frames.
|
||||
// Module scope should stay late-bound to allow extern class rebinding and similar updates.
|
||||
delegate.builderScope = if (scope is net.sergeych.lyng.ModuleScope) null else scope.snapshotForClosure()
|
||||
builder.callOn(buildScope)
|
||||
(builder as? net.sergeych.lyng.BytecodeCallable)?.callOnFast(buildScope) ?: builder.callOn(buildScope)
|
||||
return delegate
|
||||
}
|
||||
|
||||
|
||||
@ -81,7 +81,7 @@ private suspend fun createLyngFlowInput(scope: Scope, producer: Obj, ownerSessio
|
||||
val runProducer: suspend CoroutineScope.() -> Unit = {
|
||||
var failure: Throwable? = null
|
||||
try {
|
||||
producer.callOn(builderScope)
|
||||
(producer as? net.sergeych.lyng.BytecodeCallable)?.callOnFast(builderScope) ?: producer.callOn(builderScope)
|
||||
} catch (x: ScriptFlowIsNoMoreCollected) {
|
||||
// premature flow closing, OK
|
||||
} catch (x: Throwable) {
|
||||
|
||||
@ -24,6 +24,7 @@ import net.sergeych.lyng.ScriptError
|
||||
import net.sergeych.lyng.Source
|
||||
import net.sergeych.lyng.Statement
|
||||
import net.sergeych.lyng.asFacade
|
||||
import net.sergeych.lyng.obj.ObjDynamic
|
||||
import net.sergeych.lyng.obj.ObjInt
|
||||
import net.sergeych.lyng.obj.ObjString
|
||||
import net.sergeych.lyng.obj.toInt
|
||||
@ -181,6 +182,30 @@ class CompilerVmReviewRegressionTest {
|
||||
assertEquals(42, scope.asFacade().call(callable, Arguments(ObjInt.of(40))).toInt())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dynamicCallbacksUsePreparedLambdaFastPath() = runTest {
|
||||
val script = Compiler.compile(
|
||||
Source(
|
||||
"<dynamic-fast-callbacks>",
|
||||
"""
|
||||
var seen = ""
|
||||
dynamic {
|
||||
get { name -> name + seen }
|
||||
set { name, value -> seen = name + "=" + value }
|
||||
}
|
||||
""".trimIndent()
|
||||
),
|
||||
Script.defaultImportManager
|
||||
)
|
||||
|
||||
val scope = Script.newScope()
|
||||
val dynamic = script.execute(scope) as ObjDynamic
|
||||
|
||||
assertEquals("foo", (dynamic.readField(scope, "foo").value as ObjString).value)
|
||||
dynamic.writeField(scope, "foo", ObjInt.of(7))
|
||||
assertEquals("barfoo=7", (dynamic.getAt(scope, ObjString("bar")) as ObjString).value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun subjectlessWhenReportsScriptError() = runTest {
|
||||
val ex = assertFailsWith<ScriptError> {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user