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) {
|
if (initStmt is net.sergeych.lyng.Statement) {
|
||||||
executeBytecodeWithSeed(instance.instanceScope, initStmt, "instance init")
|
executeBytecodeWithSeed(instance.instanceScope, initStmt, "instance init")
|
||||||
} else {
|
} else {
|
||||||
initStmt.callOn(instance.instanceScope)
|
(initStmt as? net.sergeych.lyng.BytecodeCallable)?.callOnFast(instance.instanceScope)
|
||||||
|
?: initStmt.callOn(instance.instanceScope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -821,13 +822,14 @@ open class ObjClass(
|
|||||||
c.instanceConstructor?.let { ctor ->
|
c.instanceConstructor?.let { ctor ->
|
||||||
val execScope =
|
val execScope =
|
||||||
instance.instanceScope.createChildScope(args = argsForThis ?: Arguments.EMPTY, newThisObj = instance)
|
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 {
|
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()
|
val child = requireScope().createChildScope()
|
||||||
child.addConst(decimalContextVar, context)
|
child.addConst(decimalContextVar, context)
|
||||||
block.callOn(child)
|
(block as? net.sergeych.lyng.BytecodeCallable)?.callOnFast(child) ?: block.callOn(child)
|
||||||
}
|
}
|
||||||
registerBuiltinConversions(decimalClass)
|
registerBuiltinConversions(decimalClass)
|
||||||
registerInterop(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
|
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
|
* 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.
|
* with method invocation which is implemented separately in [invokeInstanceMethod] below.
|
||||||
*/
|
*/
|
||||||
override suspend fun readField(scope: Scope, name: String): ObjRecord {
|
override suspend fun readField(scope: Scope, name: String): ObjRecord {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
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)
|
if (writeCallback != null)
|
||||||
it.asMutable
|
it.asMutable
|
||||||
else
|
else
|
||||||
@ -90,26 +96,34 @@ open class ObjDynamic(var readCallback: Obj? = null, var writeCallback: Obj? = n
|
|||||||
onNotFoundResult: (suspend () -> Obj?)?
|
onNotFoundResult: (suspend () -> Obj?)?
|
||||||
): Obj {
|
): Obj {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
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)
|
return over?.invoke(scope, scope.thisObj, args)
|
||||||
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
|
override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
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)
|
?: super.writeField(scope, name, newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getAt(scope: Scope, index: Obj): Obj {
|
override suspend fun getAt(scope: Scope, index: Obj): Obj {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
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)
|
?: super.getAt(scope, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun putAt(scope: Scope, index: Obj, newValue: Obj) {
|
override suspend fun putAt(scope: Scope, index: Obj, newValue: Obj) {
|
||||||
val execBase = builderScope?.let { scope.applyClosure(it) } ?: scope
|
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)
|
?: 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.
|
// 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.
|
// 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()
|
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
|
return delegate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -81,7 +81,7 @@ private suspend fun createLyngFlowInput(scope: Scope, producer: Obj, ownerSessio
|
|||||||
val runProducer: suspend CoroutineScope.() -> Unit = {
|
val runProducer: suspend CoroutineScope.() -> Unit = {
|
||||||
var failure: Throwable? = null
|
var failure: Throwable? = null
|
||||||
try {
|
try {
|
||||||
producer.callOn(builderScope)
|
(producer as? net.sergeych.lyng.BytecodeCallable)?.callOnFast(builderScope) ?: producer.callOn(builderScope)
|
||||||
} catch (x: ScriptFlowIsNoMoreCollected) {
|
} catch (x: ScriptFlowIsNoMoreCollected) {
|
||||||
// premature flow closing, OK
|
// premature flow closing, OK
|
||||||
} catch (x: Throwable) {
|
} catch (x: Throwable) {
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import net.sergeych.lyng.ScriptError
|
|||||||
import net.sergeych.lyng.Source
|
import net.sergeych.lyng.Source
|
||||||
import net.sergeych.lyng.Statement
|
import net.sergeych.lyng.Statement
|
||||||
import net.sergeych.lyng.asFacade
|
import net.sergeych.lyng.asFacade
|
||||||
|
import net.sergeych.lyng.obj.ObjDynamic
|
||||||
import net.sergeych.lyng.obj.ObjInt
|
import net.sergeych.lyng.obj.ObjInt
|
||||||
import net.sergeych.lyng.obj.ObjString
|
import net.sergeych.lyng.obj.ObjString
|
||||||
import net.sergeych.lyng.obj.toInt
|
import net.sergeych.lyng.obj.toInt
|
||||||
@ -181,6 +182,30 @@ class CompilerVmReviewRegressionTest {
|
|||||||
assertEquals(42, scope.asFacade().call(callable, Arguments(ObjInt.of(40))).toInt())
|
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
|
@Test
|
||||||
fun subjectlessWhenReportsScriptError() = runTest {
|
fun subjectlessWhenReportsScriptError() = runTest {
|
||||||
val ex = assertFailsWith<ScriptError> {
|
val ex = assertFailsWith<ScriptError> {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user