optimize VM
This commit is contained in:
parent
d8454a11fc
commit
90718c3c17
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
package net.sergeych.lyng.bytecode
|
package net.sergeych.lyng.bytecode
|
||||||
|
|
||||||
internal actual fun vmIterDebug(message: String, error: Throwable?) {
|
internal actual val vmIterDebugEnabled: Boolean = false
|
||||||
|
|
||||||
|
internal actual fun vmIterDebugWrite(message: String, error: Throwable?) {
|
||||||
// no-op on Android
|
// no-op on Android
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,28 +34,24 @@ class CmdVm {
|
|||||||
frame.applyCaptureRecords()
|
frame.applyCaptureRecords()
|
||||||
binder?.invoke(frame, args)
|
binder?.invoke(frame, args)
|
||||||
val cmds = fn.cmds
|
val cmds = fn.cmds
|
||||||
try {
|
while (true) {
|
||||||
while (result == null) {
|
try {
|
||||||
try {
|
while (result == null) {
|
||||||
while (result == null) {
|
val cmd = cmds[frame.ip]
|
||||||
val cmd = cmds[frame.ip]
|
frame.ip += 1
|
||||||
frame.ip += 1
|
if (cmd.isFast) {
|
||||||
if (cmd.isFast) {
|
cmd.performFast(frame)
|
||||||
cmd.performFast(frame)
|
} else {
|
||||||
} else {
|
cmd.perform(frame)
|
||||||
cmd.perform(frame)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
if (!frame.handleException(e)) {
|
|
||||||
frame.cancelIterators()
|
|
||||||
throw e
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
if (!frame.handleException(e)) {
|
||||||
|
frame.cancelIterators()
|
||||||
|
throw e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
|
||||||
frame.cancelIterators()
|
|
||||||
throw e
|
|
||||||
}
|
}
|
||||||
frame.cancelIterators()
|
frame.cancelIterators()
|
||||||
return result ?: ObjVoid
|
return result ?: ObjVoid
|
||||||
@ -71,6 +67,7 @@ sealed class Cmd {
|
|||||||
open fun performFast(frame: CmdFrame) {
|
open fun performFast(frame: CmdFrame) {
|
||||||
error("fast command not supported: ${this::class.simpleName}")
|
error("fast command not supported: ${this::class.simpleName}")
|
||||||
}
|
}
|
||||||
|
|
||||||
open suspend fun perform(frame: CmdFrame) {
|
open suspend fun perform(frame: CmdFrame) {
|
||||||
error("slow command not supported: ${this::class.simpleName}")
|
error("slow command not supported: ${this::class.simpleName}")
|
||||||
}
|
}
|
||||||
@ -179,6 +176,7 @@ class CmdConstObj(internal val constId: Int, internal val dst: Int) : Cmd() {
|
|||||||
else -> frame.setObj(dst, obj)
|
else -> frame.setObj(dst, obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is BytecodeConst.StringVal -> frame.setObj(dst, ObjString(c.value))
|
is BytecodeConst.StringVal -> frame.setObj(dst, ObjString(c.value))
|
||||||
else -> error("CONST_OBJ expects ObjRef/StringVal at $constId")
|
else -> error("CONST_OBJ expects ObjRef/StringVal at $constId")
|
||||||
}
|
}
|
||||||
@ -255,7 +253,10 @@ class CmdLoadThisVariant(
|
|||||||
if (candidate.isInstanceOf(typeName)) return@run candidate
|
if (candidate.isInstanceOf(typeName)) return@run candidate
|
||||||
if (typeClass != null) {
|
if (typeClass != null) {
|
||||||
val inst = candidate as? net.sergeych.lyng.obj.ObjInstance
|
val inst = candidate as? net.sergeych.lyng.obj.ObjInstance
|
||||||
if (inst != null && (inst.objClass === typeClass || inst.objClass.allParentsSet.contains(typeClass))) {
|
if (inst != null && (inst.objClass === typeClass || inst.objClass.allParentsSet.contains(
|
||||||
|
typeClass
|
||||||
|
))
|
||||||
|
) {
|
||||||
return@run inst
|
return@run inst
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,7 +285,10 @@ class CmdMakeRange(
|
|||||||
val descending = frame.slotToObj(descendingSlot).toBool()
|
val descending = frame.slotToObj(descendingSlot).toBool()
|
||||||
val stepObj = frame.slotToObj(stepSlot)
|
val stepObj = frame.slotToObj(stepSlot)
|
||||||
val step = if (stepObj.isNull) null else stepObj
|
val step = if (stepObj.isNull) null else stepObj
|
||||||
frame.storeObjResult(dst, ObjRange(start, end, isEndInclusive = inclusive, isDescending = descending, step = step))
|
frame.storeObjResult(
|
||||||
|
dst,
|
||||||
|
ObjRange(start, end, isEndInclusive = inclusive, isDescending = descending, step = step)
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -372,6 +376,7 @@ class CmdCheckIs(internal val objSlot: Int, internal val typeSlot: Int, internal
|
|||||||
val rightDecl = typeDeclFromObj(frame.ensureScope(), typeObj) ?: return frame.setBool(dst, false)
|
val rightDecl = typeDeclFromObj(frame.ensureScope(), typeObj) ?: return frame.setBool(dst, false)
|
||||||
typeDeclIsSubtype(frame.ensureScope(), leftDecl, rightDecl)
|
typeDeclIsSubtype(frame.ensureScope(), leftDecl, rightDecl)
|
||||||
}
|
}
|
||||||
|
|
||||||
typeObj is ObjTypeExpr -> matchesTypeDecl(frame.ensureScope(), obj, typeObj.typeDecl)
|
typeObj is ObjTypeExpr -> matchesTypeDecl(frame.ensureScope(), obj, typeObj.typeDecl)
|
||||||
typeObj is ObjClass -> obj.isInstanceOf(typeObj)
|
typeObj is ObjClass -> obj.isInstanceOf(typeObj)
|
||||||
else -> false
|
else -> false
|
||||||
@ -393,6 +398,7 @@ class CmdAssertIs(internal val objSlot: Int, internal val typeSlot: Int) : Cmd()
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ObjTypeExpr -> {
|
is ObjTypeExpr -> {
|
||||||
if (!matchesTypeDecl(frame.ensureScope(), obj, typeObj.typeDecl)) {
|
if (!matchesTypeDecl(frame.ensureScope(), obj, typeObj.typeDecl)) {
|
||||||
frame.ensureScope().raiseClassCastError(
|
frame.ensureScope().raiseClassCastError(
|
||||||
@ -400,6 +406,7 @@ class CmdAssertIs(internal val objSlot: Int, internal val typeSlot: Int) : Cmd()
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> frame.ensureScope().raiseClassCastError(
|
else -> frame.ensureScope().raiseClassCastError(
|
||||||
"${typeObj.inspect(frame.ensureScope())} is not the class instance"
|
"${typeObj.inspect(frame.ensureScope())} is not the class instance"
|
||||||
)
|
)
|
||||||
@ -428,6 +435,7 @@ class CmdMakeQualifiedView(
|
|||||||
base
|
base
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ObjTypeExpr -> base
|
is ObjTypeExpr -> base
|
||||||
else -> frame.ensureScope().raiseClassCastError(
|
else -> frame.ensureScope().raiseClassCastError(
|
||||||
"${typeObj.inspect(frame.ensureScope())} is not the class instance"
|
"${typeObj.inspect(frame.ensureScope())} is not the class instance"
|
||||||
@ -459,11 +467,13 @@ class CmdRangeIntBounds(
|
|||||||
val start = (range.start as ObjInt).value
|
val start = (range.start as ObjInt).value
|
||||||
val end = (range.end as ObjInt).value
|
val end = (range.end as ObjInt).value
|
||||||
frame.setInt(startSlot, start)
|
frame.setInt(startSlot, start)
|
||||||
frame.setInt(endSlot, if (range.isDescending) {
|
frame.setInt(
|
||||||
if (range.isEndInclusive) end - 1 else end
|
endSlot, if (range.isDescending) {
|
||||||
} else {
|
if (range.isEndInclusive) end - 1 else end
|
||||||
if (range.isEndInclusive) end + 1 else end
|
} else {
|
||||||
})
|
if (range.isEndInclusive) end + 1 else end
|
||||||
|
}
|
||||||
|
)
|
||||||
frame.setBool(descendingSlot, range.isDescending)
|
frame.setBool(descendingSlot, range.isDescending)
|
||||||
frame.setBool(okSlot, true)
|
frame.setBool(okSlot, true)
|
||||||
return
|
return
|
||||||
@ -974,6 +984,7 @@ class CmdInvIntLocal(internal val src: Int, internal val dst: Int) : Cmd() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdCmpEqInt(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
class CmdCmpEqInt(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setBool(dst, frame.getInt(a) == frame.getInt(b))
|
frame.setBool(dst, frame.getInt(a) == frame.getInt(b))
|
||||||
@ -3118,6 +3129,7 @@ private suspend fun assignDestructureTarget(frame: CmdFrame, ref: ObjRef, value:
|
|||||||
assignDestructurePattern(frame, ref, value, pos)
|
assignDestructurePattern(frame, ref, value, pos)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
is LocalSlotRef -> {
|
is LocalSlotRef -> {
|
||||||
val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = ref.captureOwnerScopeId != null)
|
val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = ref.captureOwnerScopeId != null)
|
||||||
if (index != null) {
|
if (index != null) {
|
||||||
@ -3132,6 +3144,7 @@ private suspend fun assignDestructureTarget(frame: CmdFrame, ref: ObjRef, value:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is LocalVarRef -> {
|
is LocalVarRef -> {
|
||||||
val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = false)
|
val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = false)
|
||||||
if (index != null) {
|
if (index != null) {
|
||||||
@ -3146,6 +3159,7 @@ private suspend fun assignDestructureTarget(frame: CmdFrame, ref: ObjRef, value:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is FastLocalVarRef -> {
|
is FastLocalVarRef -> {
|
||||||
val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = false)
|
val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = false)
|
||||||
if (index != null) {
|
if (index != null) {
|
||||||
@ -3160,6 +3174,7 @@ private suspend fun assignDestructureTarget(frame: CmdFrame, ref: ObjRef, value:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
ref.setAt(pos, frame.ensureScope(), value)
|
ref.setAt(pos, frame.ensureScope(), value)
|
||||||
@ -3213,7 +3228,8 @@ class CmdDeclExtProperty(internal val constId: Int, internal val slot: Int) : Cm
|
|||||||
if (decl.property.setter != null) {
|
if (decl.property.setter != null) {
|
||||||
val setterName = extensionPropertySetterName(decl.extTypeName, decl.property.name)
|
val setterName = extensionPropertySetterName(decl.extTypeName, decl.property.name)
|
||||||
val setterWrapper = ObjExtensionPropertySetterCallable(decl.property.name, decl.property)
|
val setterWrapper = ObjExtensionPropertySetterCallable(decl.property.name, decl.property)
|
||||||
frame.ensureScope().addItem(setterName, false, setterWrapper, decl.visibility, recordType = ObjRecord.Type.Fun)
|
frame.ensureScope()
|
||||||
|
.addItem(setterName, false, setterWrapper, decl.visibility, recordType = ObjRecord.Type.Fun)
|
||||||
val setterLocal = resolveLocalSlotIndex(frame.fn, setterName, preferCapture = false)
|
val setterLocal = resolveLocalSlotIndex(frame.fn, setterName, preferCapture = false)
|
||||||
if (setterLocal != null) {
|
if (setterLocal != null) {
|
||||||
frame.setObjUnchecked(frame.fn.scopeSlotCount + setterLocal, setterWrapper)
|
frame.setObjUnchecked(frame.fn.scopeSlotCount + setterLocal, setterWrapper)
|
||||||
@ -3341,6 +3357,7 @@ class CmdListLiteral(
|
|||||||
list.ensureCapacity(list.size + value.list.size)
|
list.ensureCapacity(list.size + value.list.size)
|
||||||
list.addAll(value.list)
|
list.addAll(value.list)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> frame.ensureScope().raiseError("Spread element must be list")
|
else -> frame.ensureScope().raiseError("Spread element must be list")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -3415,7 +3432,9 @@ class CmdGetMemberSlot(
|
|||||||
val (methodIdResolved, methodOnObjClass) = decodeMemberId(methodId)
|
val (methodIdResolved, methodOnObjClass) = decodeMemberId(methodId)
|
||||||
val fieldRec = if (fieldIdResolved >= 0) {
|
val fieldRec = if (fieldIdResolved >= 0) {
|
||||||
when {
|
when {
|
||||||
inst != null -> inst.fieldRecordForId(fieldIdResolved) ?: inst.objClass.fieldRecordForId(fieldIdResolved)
|
inst != null -> inst.fieldRecordForId(fieldIdResolved)
|
||||||
|
?: inst.objClass.fieldRecordForId(fieldIdResolved)
|
||||||
|
|
||||||
cls != null && fieldOnObjClass -> cls.objClass.fieldRecordForId(fieldIdResolved)
|
cls != null && fieldOnObjClass -> cls.objClass.fieldRecordForId(fieldIdResolved)
|
||||||
cls != null -> cls.fieldRecordForId(fieldIdResolved)
|
cls != null -> cls.fieldRecordForId(fieldIdResolved)
|
||||||
else -> receiver.objClass.fieldRecordForId(fieldIdResolved)
|
else -> receiver.objClass.fieldRecordForId(fieldIdResolved)
|
||||||
@ -3424,7 +3443,10 @@ class CmdGetMemberSlot(
|
|||||||
val rec = fieldRec ?: run {
|
val rec = fieldRec ?: run {
|
||||||
if (methodIdResolved >= 0) {
|
if (methodIdResolved >= 0) {
|
||||||
when {
|
when {
|
||||||
inst != null -> inst.methodRecordForId(methodIdResolved) ?: inst.objClass.methodRecordForId(methodIdResolved)
|
inst != null -> inst.methodRecordForId(methodIdResolved) ?: inst.objClass.methodRecordForId(
|
||||||
|
methodIdResolved
|
||||||
|
)
|
||||||
|
|
||||||
cls != null && methodOnObjClass -> cls.objClass.methodRecordForId(methodIdResolved)
|
cls != null && methodOnObjClass -> cls.objClass.methodRecordForId(methodIdResolved)
|
||||||
cls != null -> cls.methodRecordForId(methodIdResolved)
|
cls != null -> cls.methodRecordForId(methodIdResolved)
|
||||||
else -> receiver.objClass.methodRecordForId(methodIdResolved)
|
else -> receiver.objClass.methodRecordForId(methodIdResolved)
|
||||||
@ -3456,9 +3478,15 @@ class CmdGetMemberSlot(
|
|||||||
} else {
|
} else {
|
||||||
rawName
|
rawName
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun autoCallIfMethod(resolved: ObjRecord, recv: Obj): Obj {
|
suspend fun autoCallIfMethod(resolved: ObjRecord, recv: Obj): Obj {
|
||||||
return if (resolved.type == ObjRecord.Type.Fun && !resolved.isAbstract) {
|
return if (resolved.type == ObjRecord.Type.Fun && !resolved.isAbstract) {
|
||||||
resolved.value.invoke(frame.ensureScope(), resolved.receiver ?: recv, Arguments.EMPTY, resolved.declaringClass)
|
resolved.value.invoke(
|
||||||
|
frame.ensureScope(),
|
||||||
|
resolved.receiver ?: recv,
|
||||||
|
Arguments.EMPTY,
|
||||||
|
resolved.declaringClass
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
resolved.value
|
resolved.value
|
||||||
}
|
}
|
||||||
@ -3489,7 +3517,9 @@ class CmdSetMemberSlot(
|
|||||||
val (methodIdResolved, methodOnObjClass) = decodeMemberId(methodId)
|
val (methodIdResolved, methodOnObjClass) = decodeMemberId(methodId)
|
||||||
val fieldRec = if (fieldIdResolved >= 0) {
|
val fieldRec = if (fieldIdResolved >= 0) {
|
||||||
when {
|
when {
|
||||||
inst != null -> inst.fieldRecordForId(fieldIdResolved) ?: inst.objClass.fieldRecordForId(fieldIdResolved)
|
inst != null -> inst.fieldRecordForId(fieldIdResolved)
|
||||||
|
?: inst.objClass.fieldRecordForId(fieldIdResolved)
|
||||||
|
|
||||||
cls != null && fieldOnObjClass -> cls.objClass.fieldRecordForId(fieldIdResolved)
|
cls != null && fieldOnObjClass -> cls.objClass.fieldRecordForId(fieldIdResolved)
|
||||||
cls != null -> cls.fieldRecordForId(fieldIdResolved)
|
cls != null -> cls.fieldRecordForId(fieldIdResolved)
|
||||||
else -> receiver.objClass.fieldRecordForId(fieldIdResolved)
|
else -> receiver.objClass.fieldRecordForId(fieldIdResolved)
|
||||||
@ -3498,7 +3528,10 @@ class CmdSetMemberSlot(
|
|||||||
val rec = fieldRec ?: run {
|
val rec = fieldRec ?: run {
|
||||||
if (methodIdResolved >= 0) {
|
if (methodIdResolved >= 0) {
|
||||||
when {
|
when {
|
||||||
inst != null -> inst.methodRecordForId(methodIdResolved) ?: inst.objClass.methodRecordForId(methodIdResolved)
|
inst != null -> inst.methodRecordForId(methodIdResolved) ?: inst.objClass.methodRecordForId(
|
||||||
|
methodIdResolved
|
||||||
|
)
|
||||||
|
|
||||||
cls != null && methodOnObjClass -> cls.objClass.methodRecordForId(methodIdResolved)
|
cls != null && methodOnObjClass -> cls.objClass.methodRecordForId(methodIdResolved)
|
||||||
cls != null -> cls.methodRecordForId(methodIdResolved)
|
cls != null -> cls.methodRecordForId(methodIdResolved)
|
||||||
else -> receiver.objClass.methodRecordForId(methodIdResolved)
|
else -> receiver.objClass.methodRecordForId(methodIdResolved)
|
||||||
@ -3692,10 +3725,12 @@ class CmdCallMemberSlot(
|
|||||||
if (callArgs.isEmpty()) (rec.value as ObjProperty).callGetter(scope, receiver, decl)
|
if (callArgs.isEmpty()) (rec.value as ObjProperty).callGetter(scope, receiver, decl)
|
||||||
else scope.raiseError("property $name cannot be called with arguments")
|
else scope.raiseError("property $name cannot be called with arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjRecord.Type.Fun -> {
|
ObjRecord.Type.Fun -> {
|
||||||
val callScope = inst?.instanceScope ?: scope
|
val callScope = inst?.instanceScope ?: scope
|
||||||
rec.value.invoke(callScope, receiver, callArgs, decl)
|
rec.value.invoke(callScope, receiver, callArgs, decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjRecord.Type.Delegated -> {
|
ObjRecord.Type.Delegated -> {
|
||||||
val delegate = when (receiver) {
|
val delegate = when (receiver) {
|
||||||
is ObjInstance -> {
|
is ObjInstance -> {
|
||||||
@ -3707,9 +3742,13 @@ class CmdCallMemberSlot(
|
|||||||
if (del != null) break
|
if (del != null) break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
del ?: scope.raiseError("Internal error: delegated member $name has no delegate (tried $storageName)")
|
del
|
||||||
|
?: scope.raiseError("Internal error: delegated member $name has no delegate (tried $storageName)")
|
||||||
}
|
}
|
||||||
is ObjClass -> rec.delegate ?: scope.raiseError("Internal error: delegated member $name has no delegate")
|
|
||||||
|
is ObjClass -> rec.delegate
|
||||||
|
?: scope.raiseError("Internal error: delegated member $name has no delegate")
|
||||||
|
|
||||||
else -> rec.delegate ?: scope.raiseError("Internal error: delegated member $name has no delegate")
|
else -> rec.delegate ?: scope.raiseError("Internal error: delegated member $name has no delegate")
|
||||||
}
|
}
|
||||||
val allArgs = (listOf(receiver, ObjString(name)) + callArgs.list).toTypedArray()
|
val allArgs = (listOf(receiver, ObjString(name)) + callArgs.list).toTypedArray()
|
||||||
@ -3718,6 +3757,7 @@ class CmdCallMemberSlot(
|
|||||||
propVal.invoke(scope, receiver, callArgs, decl)
|
propVal.invoke(scope, receiver, callArgs, decl)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> frame.ensureScope().raiseError("member $name is not callable")
|
else -> frame.ensureScope().raiseError("member $name is not callable")
|
||||||
}
|
}
|
||||||
frame.storeObjResult(dst, result)
|
frame.storeObjResult(dst, result)
|
||||||
@ -3939,11 +3979,12 @@ class BytecodeLambdaCallable(
|
|||||||
?: context.parent?.get(name)
|
?: context.parent?.get(name)
|
||||||
?: context.get(name)
|
?: context.get(name)
|
||||||
?: continue
|
?: continue
|
||||||
val value = if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is ObjProperty) {
|
val value =
|
||||||
context.resolve(record, name)
|
if (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || record.value is ObjProperty) {
|
||||||
} else {
|
context.resolve(record, name)
|
||||||
record.value
|
} else {
|
||||||
}
|
record.value
|
||||||
|
}
|
||||||
frame.frame.setObj(i, value)
|
frame.frame.setObj(i, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4000,6 +4041,7 @@ class CmdFrame(
|
|||||||
private var scopeDepth = 0
|
private var scopeDepth = 0
|
||||||
private var virtualDepth = 0
|
private var virtualDepth = 0
|
||||||
private val iterStack = ArrayDeque<Obj>()
|
private val iterStack = ArrayDeque<Obj>()
|
||||||
|
|
||||||
internal data class TryHandler(
|
internal data class TryHandler(
|
||||||
val exceptionSlot: Int,
|
val exceptionSlot: Int,
|
||||||
val catchIp: Int,
|
val catchIp: Int,
|
||||||
@ -4007,6 +4049,7 @@ class CmdFrame(
|
|||||||
val iterDepthAtPush: Int,
|
val iterDepthAtPush: Int,
|
||||||
var inCatch: Boolean = false
|
var inCatch: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val tryStack = ArrayDeque<TryHandler>()
|
internal val tryStack = ArrayDeque<TryHandler>()
|
||||||
private var pendingThrowable: Throwable? = null
|
private var pendingThrowable: Throwable? = null
|
||||||
|
|
||||||
@ -4045,6 +4088,7 @@ class CmdFrame(
|
|||||||
else -> obj
|
else -> obj
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val obj = frame.getObj(localIndex)
|
val obj = frame.getObj(localIndex)
|
||||||
when (obj) {
|
when (obj) {
|
||||||
@ -4055,6 +4099,7 @@ class CmdFrame(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun isFastLocalSlot(slot: Int): Boolean {
|
internal fun isFastLocalSlot(slot: Int): Boolean {
|
||||||
if (slot < fn.scopeSlotCount) return false
|
if (slot < fn.scopeSlotCount) return false
|
||||||
val localIndex = slot - fn.scopeSlotCount
|
val localIndex = slot - fn.scopeSlotCount
|
||||||
@ -4195,6 +4240,7 @@ class CmdFrame(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CaptureOwnerFrameKind.MODULE -> {
|
CaptureOwnerFrameKind.MODULE -> {
|
||||||
val slotId = entry.slotIndex
|
val slotId = entry.slotIndex
|
||||||
val target = moduleScope
|
val target = moduleScope
|
||||||
@ -4325,9 +4371,9 @@ class CmdFrame(
|
|||||||
|
|
||||||
suspend fun handleException(t: Throwable): Boolean {
|
suspend fun handleException(t: Throwable): Boolean {
|
||||||
val handler = tryStack.lastOrNull() ?: return false
|
val handler = tryStack.lastOrNull() ?: return false
|
||||||
vmIterDebug(
|
vmIterDebug {
|
||||||
"handleException fn=${fn.name} throwable=${t::class.simpleName} message=${t.message} catchIp=${handler.catchIp} finallyIp=${handler.finallyIp} iterDepth=${iterStack.size}"
|
"handleException fn=${fn.name} throwable=${t::class.simpleName} message=${t.message} catchIp=${handler.catchIp} finallyIp=${handler.finallyIp} iterDepth=${iterStack.size}"
|
||||||
)
|
}
|
||||||
val finallyIp = handler.finallyIp
|
val finallyIp = handler.finallyIp
|
||||||
if (t is ReturnException || t is LoopBreakContinueException) {
|
if (t is ReturnException || t is LoopBreakContinueException) {
|
||||||
if (finallyIp >= 0) {
|
if (finallyIp >= 0) {
|
||||||
@ -4453,42 +4499,50 @@ class CmdFrame(
|
|||||||
fun pushIterator(iter: Obj) {
|
fun pushIterator(iter: Obj) {
|
||||||
iterStack.addLast(iter)
|
iterStack.addLast(iter)
|
||||||
if (iter.objClass.className == "FlowIterator") {
|
if (iter.objClass.className == "FlowIterator") {
|
||||||
vmIterDebug("pushIterator fn=${fn.name} depth=${iterStack.size} iterClass=${iter.objClass.className}")
|
vmIterDebug { "pushIterator fn=${fn.name} depth=${iterStack.size} iterClass=${iter.objClass.className}" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun popIterator() {
|
fun popIterator() {
|
||||||
val iter = iterStack.lastOrNull()
|
val iter = iterStack.lastOrNull()
|
||||||
if (iter != null && iter.objClass.className == "FlowIterator") {
|
if (iter != null && iter.objClass.className == "FlowIterator") {
|
||||||
vmIterDebug("popIterator fn=${fn.name} depth=${iterStack.size} iterClass=${iter.objClass.className}")
|
vmIterDebug { "popIterator fn=${fn.name} depth=${iterStack.size} iterClass=${iter.objClass.className}" }
|
||||||
}
|
}
|
||||||
iterStack.removeLastOrNull()
|
iterStack.removeLastOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun cancelTopIterator() {
|
suspend fun cancelTopIterator() {
|
||||||
val iter = iterStack.removeLastOrNull() ?: return
|
val iter = iterStack.removeLastOrNull() ?: return
|
||||||
vmIterDebug("cancelTopIterator fn=${fn.name} depthAfter=${iterStack.size} iterClass=${iter.objClass.className}")
|
vmIterDebug { "cancelTopIterator fn=${fn.name} depthAfter=${iterStack.size} iterClass=${iter.objClass.className}" }
|
||||||
iter.invokeInstanceMethod(ensureScope(), "cancelIteration") { ObjVoid }
|
iter.invokeInstanceMethod(ensureScope(), "cancelIteration") { ObjVoid }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun cancelIterators() {
|
suspend fun cancelIterators() {
|
||||||
while (iterStack.isNotEmpty()) {
|
while (iterStack.isNotEmpty()) {
|
||||||
val iter = iterStack.removeLast()
|
val iter = iterStack.removeLast()
|
||||||
vmIterDebug("cancelIterators fn=${fn.name} depthAfter=${iterStack.size} iterClass=${iter.objClass.className}")
|
vmIterDebug { "cancelIterators fn=${fn.name} depthAfter=${iterStack.size} iterClass=${iter.objClass.className}" }
|
||||||
iter.invokeInstanceMethod(ensureScope(), "cancelIteration") { ObjVoid }
|
try {
|
||||||
|
iter.invokeInstanceMethod(ensureScope(), "cancelIteration") { ObjVoid }
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
vmIterDebug(e) {
|
||||||
|
"cancelIterators: cancelIteration failed fn=${fn.name} depthAfter=${iterStack.size} iterClass=${iter.objClass.className}"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun cancelIteratorsToDepth(depth: Int, reason: String) {
|
private suspend fun cancelIteratorsToDepth(depth: Int, reason: String) {
|
||||||
while (iterStack.size > depth) {
|
while (iterStack.size > depth) {
|
||||||
val iter = iterStack.removeLast()
|
val iter = iterStack.removeLast()
|
||||||
vmIterDebug(
|
vmIterDebug {
|
||||||
"cancelIteratorsToDepth fn=${fn.name} reason=$reason targetDepth=$depth depthAfter=${iterStack.size} iterClass=${iter.objClass.className}"
|
"cancelIteratorsToDepth fn=${fn.name} reason=$reason targetDepth=$depth depthAfter=${iterStack.size} iterClass=${iter.objClass.className}"
|
||||||
)
|
}
|
||||||
try {
|
try {
|
||||||
iter.invokeInstanceMethod(ensureScope(), "cancelIteration") { ObjVoid }
|
iter.invokeInstanceMethod(ensureScope(), "cancelIteration") { ObjVoid }
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
vmIterDebug("cancelIteratorsToDepth: cancelIteration failed fn=${fn.name} reason=$reason", e)
|
vmIterDebug(e) {
|
||||||
|
"cancelIteratorsToDepth: cancelIteration failed fn=${fn.name} reason=$reason"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4549,10 +4603,12 @@ class CmdFrame(
|
|||||||
existing.write(value)
|
existing.write(value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
is RecordSlotRef -> {
|
is RecordSlotRef -> {
|
||||||
existing.write(value)
|
existing.write(value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4629,10 +4685,12 @@ class CmdFrame(
|
|||||||
existing.write(ObjInt.of(value))
|
existing.write(ObjInt.of(value))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
is RecordSlotRef -> {
|
is RecordSlotRef -> {
|
||||||
existing.write(ObjInt.of(value))
|
existing.write(ObjInt.of(value))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4659,10 +4717,12 @@ class CmdFrame(
|
|||||||
existing.write(ObjInt.of(value))
|
existing.write(ObjInt.of(value))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
is RecordSlotRef -> {
|
is RecordSlotRef -> {
|
||||||
existing.write(ObjInt.of(value))
|
existing.write(ObjInt.of(value))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4712,10 +4772,12 @@ class CmdFrame(
|
|||||||
existing.write(ObjReal.of(value))
|
existing.write(ObjReal.of(value))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
is RecordSlotRef -> {
|
is RecordSlotRef -> {
|
||||||
existing.write(ObjReal.of(value))
|
existing.write(ObjReal.of(value))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4736,10 +4798,12 @@ class CmdFrame(
|
|||||||
existing.write(ObjReal.of(value))
|
existing.write(ObjReal.of(value))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
is RecordSlotRef -> {
|
is RecordSlotRef -> {
|
||||||
existing.write(ObjReal.of(value))
|
existing.write(ObjReal.of(value))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4783,10 +4847,12 @@ class CmdFrame(
|
|||||||
existing.write(if (value) ObjTrue else ObjFalse)
|
existing.write(if (value) ObjTrue else ObjFalse)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
is RecordSlotRef -> {
|
is RecordSlotRef -> {
|
||||||
existing.write(if (value) ObjTrue else ObjFalse)
|
existing.write(if (value) ObjTrue else ObjFalse)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4807,10 +4873,12 @@ class CmdFrame(
|
|||||||
existing.write(if (value) ObjTrue else ObjFalse)
|
existing.write(if (value) ObjTrue else ObjFalse)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
is RecordSlotRef -> {
|
is RecordSlotRef -> {
|
||||||
existing.write(if (value) ObjTrue else ObjFalse)
|
existing.write(if (value) ObjTrue else ObjFalse)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4903,6 +4971,7 @@ class CmdFrame(
|
|||||||
null -> ObjNull
|
null -> ObjNull
|
||||||
else -> raw
|
else -> raw
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> frame.getRawObj(local) ?: ObjNull
|
else -> frame.getRawObj(local) ?: ObjNull
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4929,10 +4998,12 @@ class CmdFrame(
|
|||||||
existing.write(value)
|
existing.write(value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
is RecordSlotRef -> {
|
is RecordSlotRef -> {
|
||||||
existing.write(value)
|
existing.write(value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5018,15 +5089,18 @@ class CmdFrame(
|
|||||||
}
|
}
|
||||||
namedSeen = true
|
namedSeen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
value is ObjList -> {
|
value is ObjList -> {
|
||||||
if (namedSeen) scope.raiseIllegalArgument("positional splat cannot follow named arguments")
|
if (namedSeen) scope.raiseIllegalArgument("positional splat cannot follow named arguments")
|
||||||
positional.addAll(value.list)
|
positional.addAll(value.list)
|
||||||
}
|
}
|
||||||
|
|
||||||
value.isInstanceOf(ObjIterable) -> {
|
value.isInstanceOf(ObjIterable) -> {
|
||||||
if (namedSeen) scope.raiseIllegalArgument("positional splat cannot follow named arguments")
|
if (namedSeen) scope.raiseIllegalArgument("positional splat cannot follow named arguments")
|
||||||
val list = (value.invokeInstanceMethod(scope, "toList") as ObjList).list
|
val list = (value.invokeInstanceMethod(scope, "toList") as ObjList).list
|
||||||
positional.addAll(list)
|
positional.addAll(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> scope.raiseClassCastError("expected list of objects for splat argument")
|
else -> scope.raiseClassCastError("expected list of objects for splat argument")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -5075,6 +5149,7 @@ class CmdFrame(
|
|||||||
else -> obj
|
else -> obj
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val obj = frame.getObj(localIndex)
|
val obj = frame.getObj(localIndex)
|
||||||
when (obj) {
|
when (obj) {
|
||||||
|
|||||||
@ -17,4 +17,18 @@
|
|||||||
|
|
||||||
package net.sergeych.lyng.bytecode
|
package net.sergeych.lyng.bytecode
|
||||||
|
|
||||||
internal expect fun vmIterDebug(message: String, error: Throwable? = null)
|
internal expect val vmIterDebugEnabled: Boolean
|
||||||
|
|
||||||
|
internal expect fun vmIterDebugWrite(message: String, error: Throwable? = null)
|
||||||
|
|
||||||
|
internal inline fun vmIterDebug(message: () -> String) {
|
||||||
|
if (vmIterDebugEnabled) {
|
||||||
|
vmIterDebugWrite(message())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal inline fun vmIterDebug(error: Throwable, message: () -> String) {
|
||||||
|
if (vmIterDebugEnabled) {
|
||||||
|
vmIterDebugWrite(message(), error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -131,7 +131,7 @@ class ScriptTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Helpers to test iterator cancellation semantics ---
|
// --- Helpers to test iterator cancellation semantics ---
|
||||||
class ObjTestIterable : Obj() {
|
class ObjTestIterable(private val throwOnCancel: Boolean = false) : Obj() {
|
||||||
|
|
||||||
var cancelCount: Int = 0
|
var cancelCount: Int = 0
|
||||||
|
|
||||||
@ -145,6 +145,13 @@ class ScriptTest {
|
|||||||
addFn("cancelCount") { thisAs<ObjTestIterable>().cancelCount.toObj() }
|
addFn("cancelCount") { thisAs<ObjTestIterable>().cancelCount.toObj() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun onCancel() {
|
||||||
|
cancelCount += 1
|
||||||
|
if (throwOnCancel) {
|
||||||
|
throw IllegalStateException("cancel failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ObjTestIterator(private val owner: ObjTestIterable) : Obj() {
|
class ObjTestIterator(private val owner: ObjTestIterable) : Obj() {
|
||||||
@ -154,7 +161,7 @@ class ScriptTest {
|
|||||||
private fun hasNext(): Boolean = i < 5
|
private fun hasNext(): Boolean = i < 5
|
||||||
private fun next(): Obj = ObjInt((++i).toLong())
|
private fun next(): Obj = ObjInt((++i).toLong())
|
||||||
private fun cancelIteration() {
|
private fun cancelIteration() {
|
||||||
owner.cancelCount += 1
|
owner.onCancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -221,6 +228,33 @@ class ScriptTest {
|
|||||||
assertEquals(1, ti.cancelCount)
|
assertEquals(1, ti.cancelCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testVmCancelsAllIteratorsWhenOneCancelFails() = runTest {
|
||||||
|
val scope = Script.newScope()
|
||||||
|
val outer = ObjTestIterable()
|
||||||
|
val inner = ObjTestIterable(throwOnCancel = true)
|
||||||
|
scope.addConst("outer", outer)
|
||||||
|
scope.addConst("inner", inner)
|
||||||
|
|
||||||
|
try {
|
||||||
|
scope.eval(
|
||||||
|
"""
|
||||||
|
for (o in outer) {
|
||||||
|
for (i in inner) {
|
||||||
|
throw "boom"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
fail("Exception expected")
|
||||||
|
} catch (_: Exception) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(1, inner.cancelCount)
|
||||||
|
assertEquals(1, outer.cancelCount)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun parseNewlines() {
|
fun parseNewlines() {
|
||||||
fun check(expected: String, type: Token.Type, row: Int, col: Int, src: String, offset: Int = 0) {
|
fun check(expected: String, type: Token.Type, row: Int, col: Int, src: String, offset: Int = 0) {
|
||||||
|
|||||||
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
package net.sergeych.lyng.bytecode
|
package net.sergeych.lyng.bytecode
|
||||||
|
|
||||||
internal actual fun vmIterDebug(message: String, error: Throwable?) {
|
internal actual val vmIterDebugEnabled: Boolean = false
|
||||||
|
|
||||||
|
internal actual fun vmIterDebugWrite(message: String, error: Throwable?) {
|
||||||
// no-op on JS
|
// no-op on JS
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,18 @@ package net.sergeych.lyng.bytecode
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
|
private fun parseEnabledFlag(value: String?): Boolean {
|
||||||
|
return when (value?.lowercase()) {
|
||||||
|
"1", "true", "yes", "on" -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal actual val vmIterDebugEnabled: Boolean = run {
|
||||||
|
parseEnabledFlag(System.getProperty("LYNG_VM_ITER_DEBUG"))
|
||||||
|
|| parseEnabledFlag(System.getenv("LYNG_VM_ITER_DEBUG"))
|
||||||
|
}
|
||||||
|
|
||||||
private val vmIterLogFilePath: String =
|
private val vmIterLogFilePath: String =
|
||||||
System.getenv("LYNG_VM_DEBUG_LOG")
|
System.getenv("LYNG_VM_DEBUG_LOG")
|
||||||
?.takeIf { it.isNotBlank() }
|
?.takeIf { it.isNotBlank() }
|
||||||
@ -27,7 +39,7 @@ private val vmIterLogFilePath: String =
|
|||||||
|
|
||||||
private val vmIterLogLock = Any()
|
private val vmIterLogLock = Any()
|
||||||
|
|
||||||
internal actual fun vmIterDebug(message: String, error: Throwable?) {
|
internal actual fun vmIterDebugWrite(message: String, error: Throwable?) {
|
||||||
runCatching {
|
runCatching {
|
||||||
val line = buildString {
|
val line = buildString {
|
||||||
append(Instant.now().toString())
|
append(Instant.now().toString())
|
||||||
|
|||||||
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
package net.sergeych.lyng.bytecode
|
package net.sergeych.lyng.bytecode
|
||||||
|
|
||||||
internal actual fun vmIterDebug(message: String, error: Throwable?) {
|
internal actual val vmIterDebugEnabled: Boolean = false
|
||||||
|
|
||||||
|
internal actual fun vmIterDebugWrite(message: String, error: Throwable?) {
|
||||||
// no-op on Native
|
// no-op on Native
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
package net.sergeych.lyng.bytecode
|
package net.sergeych.lyng.bytecode
|
||||||
|
|
||||||
internal actual fun vmIterDebug(message: String, error: Throwable?) {
|
internal actual val vmIterDebugEnabled: Boolean = false
|
||||||
|
|
||||||
|
internal actual fun vmIterDebugWrite(message: String, error: Throwable?) {
|
||||||
// no-op on wasmJs
|
// no-op on wasmJs
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user