Enforce bytecode-only statements and wrap defaults
This commit is contained in:
parent
489dae6604
commit
26564438e2
@ -28,21 +28,7 @@ class BlockStatement(
|
||||
override val pos: Pos = startPos
|
||||
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val target = if (scope.skipScopeCreation) scope else scope.createChildScope(startPos)
|
||||
if (slotPlan.isNotEmpty()) target.applySlotPlan(slotPlan)
|
||||
if (captureSlots.isNotEmpty()) {
|
||||
val captureRecords = scope.captureRecords
|
||||
if (captureRecords == null) {
|
||||
scope.raiseIllegalState("missing bytecode capture records")
|
||||
}
|
||||
for (i in captureSlots.indices) {
|
||||
val capture = captureSlots[i]
|
||||
val rec = captureRecords.getOrNull(i)
|
||||
?: scope.raiseSymbolNotFound("capture ${capture.name} not found")
|
||||
target.updateSlotFor(capture.name, rec)
|
||||
}
|
||||
}
|
||||
return block.execute(target)
|
||||
return interpreterDisabled(scope, "block statement")
|
||||
}
|
||||
|
||||
fun statements(): List<Statement> = block.debugStatements()
|
||||
|
||||
@ -17,20 +17,14 @@
|
||||
package net.sergeych.lyng
|
||||
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjClass
|
||||
import net.sergeych.lyng.obj.ObjProperty
|
||||
import net.sergeych.lyng.obj.ObjRecord
|
||||
import net.sergeych.lyng.obj.ObjVoid
|
||||
|
||||
class ClassInstanceInitDeclStatement(
|
||||
val initStatement: Statement,
|
||||
override val pos: Pos,
|
||||
) : Statement() {
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val cls = scope.thisObj as? ObjClass
|
||||
?: scope.raiseIllegalState("instance init declaration requires class scope")
|
||||
cls.instanceInitializers += initStatement
|
||||
return ObjVoid
|
||||
return interpreterDisabled(scope, "class instance init declaration")
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,24 +42,7 @@ class ClassInstanceFieldDeclStatement(
|
||||
override val pos: Pos,
|
||||
) : Statement() {
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val cls = scope.thisObj as? ObjClass
|
||||
?: scope.raiseIllegalState("instance field declaration requires class scope")
|
||||
cls.createField(
|
||||
name,
|
||||
net.sergeych.lyng.obj.ObjNull,
|
||||
isMutable = isMutable,
|
||||
visibility = visibility,
|
||||
writeVisibility = writeVisibility,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride,
|
||||
isTransient = isTransient,
|
||||
declaringClass = cls,
|
||||
type = ObjRecord.Type.Field,
|
||||
fieldId = fieldId
|
||||
)
|
||||
if (!isAbstract) initStatement?.let { cls.instanceInitializers += it }
|
||||
return ObjVoid
|
||||
return interpreterDisabled(scope, "class instance field declaration")
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,22 +61,7 @@ class ClassInstancePropertyDeclStatement(
|
||||
override val pos: Pos,
|
||||
) : Statement() {
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val cls = scope.thisObj as? ObjClass
|
||||
?: scope.raiseIllegalState("instance property declaration requires class scope")
|
||||
cls.addProperty(
|
||||
name = name,
|
||||
visibility = visibility,
|
||||
writeVisibility = writeVisibility,
|
||||
declaringClass = cls,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride,
|
||||
pos = pos,
|
||||
prop = prop,
|
||||
methodId = methodId
|
||||
)
|
||||
if (!isAbstract) initStatement?.let { cls.instanceInitializers += it }
|
||||
return ObjVoid
|
||||
return interpreterDisabled(scope, "class instance property declaration")
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,23 +79,6 @@ class ClassInstanceDelegatedDeclStatement(
|
||||
override val pos: Pos,
|
||||
) : Statement() {
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val cls = scope.thisObj as? ObjClass
|
||||
?: scope.raiseIllegalState("instance delegated declaration requires class scope")
|
||||
cls.createField(
|
||||
name,
|
||||
net.sergeych.lyng.obj.ObjUnset,
|
||||
isMutable = isMutable,
|
||||
visibility = visibility,
|
||||
writeVisibility = writeVisibility,
|
||||
isAbstract = isAbstract,
|
||||
isClosed = isClosed,
|
||||
isOverride = isOverride,
|
||||
isTransient = isTransient,
|
||||
declaringClass = cls,
|
||||
type = ObjRecord.Type.Delegated,
|
||||
methodId = methodId
|
||||
)
|
||||
if (!isAbstract) initStatement?.let { cls.instanceInitializers += it }
|
||||
return ObjVoid
|
||||
return interpreterDisabled(scope, "class instance delegated declaration")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1928,6 +1928,64 @@ class Compiler(
|
||||
)
|
||||
}
|
||||
|
||||
private fun wrapDefaultArgsBytecode(
|
||||
args: ArgsDeclaration,
|
||||
forcedLocalSlots: Map<String, Int>,
|
||||
forcedLocalScopeId: Int
|
||||
): ArgsDeclaration {
|
||||
if (!compileBytecode) return args
|
||||
if (args.params.none { it.defaultValue is Statement }) return args
|
||||
val updated = args.params.map { param ->
|
||||
val defaultValue = param.defaultValue
|
||||
val stmt = defaultValue as? Statement
|
||||
if (stmt == null) return@map param
|
||||
val bytecode = when (stmt) {
|
||||
is BytecodeStatement -> stmt
|
||||
is BytecodeBodyProvider -> stmt.bytecodeBody()
|
||||
else -> null
|
||||
}
|
||||
if (bytecode != null) {
|
||||
param
|
||||
} else {
|
||||
val wrapped = wrapFunctionBytecode(
|
||||
stmt,
|
||||
"argDefault@${param.name}",
|
||||
forcedLocalSlots = forcedLocalSlots,
|
||||
forcedLocalScopeId = forcedLocalScopeId
|
||||
)
|
||||
param.copy(defaultValue = wrapped)
|
||||
}
|
||||
}
|
||||
return if (updated == args.params) args else args.copy(params = updated)
|
||||
}
|
||||
|
||||
private fun wrapParsedArgsBytecode(
|
||||
args: List<ParsedArgument>?,
|
||||
forcedLocalSlots: Map<String, Int> = emptyMap(),
|
||||
forcedLocalScopeId: Int? = null
|
||||
): List<ParsedArgument>? {
|
||||
if (!compileBytecode || args == null || args.isEmpty()) return args
|
||||
var changed = false
|
||||
val updated = args.mapIndexed { index, arg ->
|
||||
val stmt = arg.value as? Statement ?: return@mapIndexed arg
|
||||
val bytecode = when (stmt) {
|
||||
is BytecodeStatement -> stmt
|
||||
is BytecodeBodyProvider -> stmt.bytecodeBody()
|
||||
else -> null
|
||||
}
|
||||
if (bytecode != null) return@mapIndexed arg
|
||||
val wrapped = wrapFunctionBytecode(
|
||||
stmt,
|
||||
"arg@${index}",
|
||||
forcedLocalSlots = forcedLocalSlots,
|
||||
forcedLocalScopeId = forcedLocalScopeId
|
||||
)
|
||||
changed = true
|
||||
arg.copy(value = wrapped)
|
||||
}
|
||||
return if (changed) updated else args
|
||||
}
|
||||
|
||||
private fun containsDelegatedRefs(stmt: Statement): Boolean {
|
||||
val target = if (stmt is BytecodeStatement) stmt.original else stmt
|
||||
return when (target) {
|
||||
@ -5827,6 +5885,7 @@ class Compiler(
|
||||
if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true)) {
|
||||
argsList = parseArgsNoTailBlock()
|
||||
}
|
||||
argsList = wrapParsedArgsBytecode(argsList)
|
||||
baseSpecs += BaseSpec(baseId.value, argsList)
|
||||
} while (cc.skipTokenOfType(Token.Type.COMMA, isOptional = true))
|
||||
}
|
||||
@ -5978,7 +6037,7 @@ class Compiler(
|
||||
val classTypeParams = typeParamDecls.map { it.name }.toSet()
|
||||
classCtx?.typeParams = classTypeParams
|
||||
pendingTypeParamStack.add(classTypeParams)
|
||||
val constructorArgsDeclaration: ArgsDeclaration?
|
||||
var constructorArgsDeclaration: ArgsDeclaration?
|
||||
try {
|
||||
constructorArgsDeclaration =
|
||||
if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true))
|
||||
@ -6009,6 +6068,16 @@ class Compiler(
|
||||
val mutable = param.accessType?.isMutable ?: false
|
||||
declareSlotNameIn(classSlotPlan, param.name, mutable, isDelegated = false)
|
||||
}
|
||||
val ctorForcedLocalSlots = LinkedHashMap<String, Int>()
|
||||
if (constructorArgsDeclaration != null) {
|
||||
val snapshot = slotPlanIndices(classSlotPlan)
|
||||
for (param in constructorArgsDeclaration!!.params) {
|
||||
val idx = snapshot[param.name] ?: continue
|
||||
ctorForcedLocalSlots[param.name] = idx
|
||||
}
|
||||
constructorArgsDeclaration =
|
||||
wrapDefaultArgsBytecode(constructorArgsDeclaration!!, ctorForcedLocalSlots, classSlotPlan.id)
|
||||
}
|
||||
constructorArgsDeclaration?.params?.forEach { param ->
|
||||
if (param.accessType != null) {
|
||||
classCtx?.declaredMembers?.add(param.name)
|
||||
@ -6036,6 +6105,7 @@ class Compiler(
|
||||
// Parse args without consuming any following block so that a class body can follow safely
|
||||
argsList = parseArgsNoTailBlock()
|
||||
}
|
||||
argsList = wrapParsedArgsBytecode(argsList, ctorForcedLocalSlots, classSlotPlan.id)
|
||||
baseSpecs += BaseSpec(baseName, argsList)
|
||||
} while (cc.skipTokenOfType(Token.Type.COMMA, isOptional = true))
|
||||
}
|
||||
@ -6717,7 +6787,7 @@ class Compiler(
|
||||
}
|
||||
val typeParams = mergedTypeParamDecls.map { it.name }.toSet()
|
||||
pendingTypeParamStack.add(typeParams)
|
||||
val argsDeclaration: ArgsDeclaration
|
||||
var argsDeclaration: ArgsDeclaration
|
||||
val returnTypeMini: MiniTypeRef?
|
||||
val returnTypeDecl: TypeDecl?
|
||||
try {
|
||||
@ -6811,6 +6881,17 @@ class Compiler(
|
||||
val typeParamNames = mergedTypeParamDecls.map { it.name }
|
||||
val paramNames: Set<String> = paramNamesList.toSet()
|
||||
val paramSlotPlan = buildParamSlotPlan(paramNamesList + typeParamNames)
|
||||
val paramSlotPlanSnapshot = slotPlanIndices(paramSlotPlan)
|
||||
val forcedLocalSlots = LinkedHashMap<String, Int>()
|
||||
for (name in paramNamesList) {
|
||||
val idx = paramSlotPlanSnapshot[name] ?: continue
|
||||
forcedLocalSlots[name] = idx
|
||||
}
|
||||
for (name in typeParamNames) {
|
||||
val idx = paramSlotPlanSnapshot[name] ?: continue
|
||||
forcedLocalSlots[name] = idx
|
||||
}
|
||||
argsDeclaration = wrapDefaultArgsBytecode(argsDeclaration, forcedLocalSlots, paramSlotPlan.id)
|
||||
val capturePlan = CapturePlan(paramSlotPlan, isFunction = true, propagateToParentFunction = false)
|
||||
val rangeParamNames = argsDeclaration.params
|
||||
.filter { isRangeType(it.type) }
|
||||
@ -6887,12 +6968,6 @@ class Compiler(
|
||||
inferredReturnClass
|
||||
}
|
||||
}
|
||||
val paramSlotPlanSnapshot = slotPlanIndices(paramSlotPlan)
|
||||
val forcedLocalSlots = LinkedHashMap<String, Int>()
|
||||
for (name in paramNamesList) {
|
||||
val idx = paramSlotPlanSnapshot[name] ?: continue
|
||||
forcedLocalSlots[name] = idx
|
||||
}
|
||||
val fnStatements = rawFnStatements?.let { stmt ->
|
||||
if (!compileBytecode) return@let stmt
|
||||
val paramKnownClasses = mutableMapOf<String, ObjClass>()
|
||||
|
||||
@ -18,9 +18,6 @@
|
||||
package net.sergeych.lyng
|
||||
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjNull
|
||||
import net.sergeych.lyng.obj.ObjRecord
|
||||
import net.sergeych.lyng.obj.ObjString
|
||||
|
||||
class DelegatedVarDeclStatement(
|
||||
val name: String,
|
||||
@ -35,23 +32,6 @@ class DelegatedVarDeclStatement(
|
||||
override val pos: Pos = startPos
|
||||
|
||||
override suspend fun execute(context: Scope): Obj {
|
||||
val initValue = initializer.execute(context)
|
||||
val accessTypeStr = if (isMutable) "Var" else "Val"
|
||||
val accessType = ObjString(accessTypeStr)
|
||||
val finalDelegate = try {
|
||||
initValue.invokeInstanceMethod(context, "bind", Arguments(ObjString(name), accessType, ObjNull))
|
||||
} catch (e: Exception) {
|
||||
initValue
|
||||
}
|
||||
val rec = context.addItem(
|
||||
name,
|
||||
isMutable,
|
||||
ObjNull,
|
||||
visibility,
|
||||
recordType = ObjRecord.Type.Delegated,
|
||||
isTransient = isTransient
|
||||
)
|
||||
rec.delegate = finalDelegate
|
||||
return finalDelegate
|
||||
return interpreterDisabled(context, "delegated var declaration")
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,6 @@ package net.sergeych.lyng
|
||||
|
||||
import net.sergeych.lyng.obj.ListLiteralRef
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjVoid
|
||||
|
||||
class DestructuringVarDeclStatement(
|
||||
val pattern: ListLiteralRef,
|
||||
@ -30,20 +29,6 @@ class DestructuringVarDeclStatement(
|
||||
override val pos: Pos,
|
||||
) : Statement() {
|
||||
override suspend fun execute(context: Scope): Obj {
|
||||
val value = initializer.execute(context)
|
||||
for (name in names) {
|
||||
context.addItem(name, true, ObjVoid, visibility, isTransient = isTransient)
|
||||
}
|
||||
pattern.setAt(pos, context, value)
|
||||
if (!isMutable) {
|
||||
for (name in names) {
|
||||
val rec = context.objects[name]!!
|
||||
val immutableRec = rec.copy(isMutable = false)
|
||||
context.objects[name] = immutableRec
|
||||
context.localBindings[name] = immutableRec
|
||||
context.updateSlotFor(name, immutableRec)
|
||||
}
|
||||
}
|
||||
return ObjVoid
|
||||
return interpreterDisabled(context, "destructuring declaration")
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,8 +17,6 @@
|
||||
package net.sergeych.lyng
|
||||
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjEnumClass
|
||||
import net.sergeych.lyng.obj.ObjRecord
|
||||
|
||||
class EnumDeclStatement(
|
||||
val declaredName: String,
|
||||
@ -30,17 +28,7 @@ class EnumDeclStatement(
|
||||
override val pos: Pos = startPos
|
||||
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val enumClass = ObjEnumClass.createSimpleEnum(qualifiedName, entries)
|
||||
scope.addItem(declaredName, false, enumClass, recordType = ObjRecord.Type.Enum)
|
||||
if (lifted) {
|
||||
for (entry in entries) {
|
||||
val rec = enumClass.getInstanceMemberOrNull(entry, includeAbstract = false, includeStatic = true)
|
||||
if (rec != null) {
|
||||
scope.addItem(entry, false, rec.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return enumClass
|
||||
return interpreterDisabled(scope, "enum declaration")
|
||||
}
|
||||
|
||||
override suspend fun callOn(scope: Scope): Obj {
|
||||
|
||||
@ -17,11 +17,7 @@
|
||||
package net.sergeych.lyng
|
||||
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjClass
|
||||
import net.sergeych.lyng.obj.ObjExtensionPropertyGetterCallable
|
||||
import net.sergeych.lyng.obj.ObjExtensionPropertySetterCallable
|
||||
import net.sergeych.lyng.obj.ObjProperty
|
||||
import net.sergeych.lyng.obj.ObjRecord
|
||||
|
||||
class ExtensionPropertyDeclStatement(
|
||||
val extTypeName: String,
|
||||
@ -33,28 +29,6 @@ class ExtensionPropertyDeclStatement(
|
||||
override val pos: Pos = startPos
|
||||
|
||||
override suspend fun execute(context: Scope): Obj {
|
||||
val type = context[extTypeName]?.value ?: context.raiseSymbolNotFound("class $extTypeName not found")
|
||||
if (type !is ObjClass) context.raiseClassCastError("$extTypeName is not the class instance")
|
||||
context.addExtension(
|
||||
type,
|
||||
property.name,
|
||||
ObjRecord(
|
||||
property,
|
||||
isMutable = false,
|
||||
visibility = visibility,
|
||||
writeVisibility = setterVisibility,
|
||||
declaringClass = null,
|
||||
type = ObjRecord.Type.Property
|
||||
)
|
||||
)
|
||||
val getterName = extensionPropertyGetterName(extTypeName, property.name)
|
||||
val getterWrapper = ObjExtensionPropertyGetterCallable(property.name, property)
|
||||
context.addItem(getterName, false, getterWrapper, visibility, recordType = ObjRecord.Type.Fun)
|
||||
if (property.setter != null) {
|
||||
val setterName = extensionPropertySetterName(extTypeName, property.name)
|
||||
val setterWrapper = ObjExtensionPropertySetterCallable(property.name, property)
|
||||
context.addItem(setterName, false, setterWrapper, visibility, recordType = ObjRecord.Type.Fun)
|
||||
}
|
||||
return property
|
||||
return interpreterDisabled(context, "extension property declaration")
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,6 @@ package net.sergeych.lyng
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjClass
|
||||
import net.sergeych.lyng.obj.ObjException
|
||||
import net.sergeych.lyng.obj.ObjUnknownException
|
||||
import net.sergeych.lyng.obj.ObjVoid
|
||||
|
||||
class TryStatement(
|
||||
val body: Statement,
|
||||
@ -38,43 +36,7 @@ class TryStatement(
|
||||
)
|
||||
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
var result: Obj = ObjVoid
|
||||
try {
|
||||
result = body.execute(scope)
|
||||
} catch (e: ReturnException) {
|
||||
throw e
|
||||
} catch (e: LoopBreakContinueException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
val caughtObj = when (e) {
|
||||
is ExecutionError -> e.errorObject
|
||||
else -> ObjUnknownException(scope, e.message ?: e.toString())
|
||||
}
|
||||
var isCaught = false
|
||||
for (cdata in catches) {
|
||||
var match: Obj? = null
|
||||
for (exceptionClassName in cdata.classNames) {
|
||||
val exObj = resolveExceptionClass(scope, exceptionClassName)
|
||||
if (caughtObj.isInstanceOf(exObj)) {
|
||||
match = caughtObj
|
||||
break
|
||||
}
|
||||
}
|
||||
if (match != null) {
|
||||
val catchContext = scope.createChildScope(pos = cdata.catchVarPos).apply {
|
||||
skipScopeCreation = true
|
||||
}
|
||||
catchContext.addItem(cdata.catchVarName, false, caughtObj)
|
||||
result = cdata.block.execute(catchContext)
|
||||
isCaught = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!isCaught) throw e
|
||||
} finally {
|
||||
finallyClause?.execute(scope)
|
||||
}
|
||||
return result
|
||||
return interpreterDisabled(scope, "try statement")
|
||||
}
|
||||
|
||||
private fun resolveExceptionClass(scope: Scope, name: String): ObjClass {
|
||||
|
||||
@ -18,9 +18,6 @@ package net.sergeych.lyng
|
||||
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjClass
|
||||
import net.sergeych.lyng.obj.ObjNull
|
||||
import net.sergeych.lyng.obj.ObjRecord
|
||||
import net.sergeych.lyng.obj.ObjUnset
|
||||
|
||||
class VarDeclStatement(
|
||||
val name: String,
|
||||
@ -36,15 +33,6 @@ class VarDeclStatement(
|
||||
override val pos: Pos = startPos
|
||||
|
||||
override suspend fun execute(context: Scope): Obj {
|
||||
val initValue = initializer?.execute(context)?.byValueCopy() ?: ObjUnset
|
||||
context.addItem(
|
||||
name,
|
||||
isMutable,
|
||||
initValue,
|
||||
visibility,
|
||||
recordType = ObjRecord.Type.Other,
|
||||
isTransient = isTransient
|
||||
)
|
||||
return initValue
|
||||
return interpreterDisabled(context, "var declaration")
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
package net.sergeych.lyng
|
||||
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjVoid
|
||||
|
||||
sealed class WhenCondition(open val expr: Statement, open val pos: Pos) {
|
||||
abstract suspend fun matches(scope: Scope, value: Obj): Boolean
|
||||
@ -67,14 +66,6 @@ class WhenStatement(
|
||||
override val pos: Pos,
|
||||
) : Statement() {
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val whenValue = value.execute(scope)
|
||||
for (case in cases) {
|
||||
for (condition in case.conditions) {
|
||||
if (condition.matches(scope, whenValue)) {
|
||||
return case.block.execute(scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
return elseCase?.execute(scope) ?: ObjVoid
|
||||
return interpreterDisabled(scope, "when statement")
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,10 +19,10 @@ package net.sergeych.lyng
|
||||
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjClass
|
||||
import net.sergeych.lyng.obj.ObjException
|
||||
import net.sergeych.lyng.obj.ObjInt
|
||||
import net.sergeych.lyng.obj.ObjIterable
|
||||
import net.sergeych.lyng.obj.ObjNull
|
||||
import net.sergeych.lyng.obj.ObjException
|
||||
import net.sergeych.lyng.obj.ObjRange
|
||||
import net.sergeych.lyng.obj.ObjRecord
|
||||
import net.sergeych.lyng.obj.ObjString
|
||||
@ -71,6 +71,10 @@ abstract class Statement(
|
||||
|
||||
suspend fun call(scope: Scope, vararg args: Obj) = execute(scope.createChildScope(args = Arguments(*args)))
|
||||
|
||||
protected fun interpreterDisabled(scope: Scope, label: String): Nothing {
|
||||
return scope.raiseIllegalState("interpreter execution is not supported; $label requires bytecode")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class IfStatement(
|
||||
@ -80,11 +84,7 @@ class IfStatement(
|
||||
override val pos: Pos,
|
||||
) : Statement() {
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
return if (condition.execute(scope).toBool()) {
|
||||
ifBody.execute(scope)
|
||||
} else {
|
||||
elseBody?.execute(scope) ?: ObjVoid
|
||||
}
|
||||
return interpreterDisabled(scope, "if statement")
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,89 +103,7 @@ class ForInStatement(
|
||||
override val pos: Pos,
|
||||
) : Statement() {
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val forContext = scope.createChildScope(pos)
|
||||
if (loopSlotPlan.isNotEmpty()) {
|
||||
forContext.applySlotPlan(loopSlotPlan)
|
||||
}
|
||||
|
||||
val loopSO = forContext.addItem(loopVarName, true, ObjNull)
|
||||
val loopSlotIndex = forContext.getSlotIndexOf(loopVarName) ?: -1
|
||||
|
||||
if (constRange != null && PerfFlags.PRIMITIVE_FASTOPS) {
|
||||
return loopIntRange(
|
||||
forContext,
|
||||
constRange.start,
|
||||
constRange.endExclusive,
|
||||
loopSO,
|
||||
loopSlotIndex,
|
||||
body,
|
||||
elseStatement,
|
||||
label,
|
||||
canBreak
|
||||
)
|
||||
}
|
||||
|
||||
val sourceObj = source.execute(forContext)
|
||||
return if (sourceObj is ObjRange && sourceObj.isIntRange && !sourceObj.hasExplicitStep && PerfFlags.PRIMITIVE_FASTOPS) {
|
||||
loopIntRange(
|
||||
forContext,
|
||||
sourceObj.start!!.toLong(),
|
||||
if (sourceObj.isEndInclusive) sourceObj.end!!.toLong() + 1 else sourceObj.end!!.toLong(),
|
||||
loopSO,
|
||||
loopSlotIndex,
|
||||
body,
|
||||
elseStatement,
|
||||
label,
|
||||
canBreak
|
||||
)
|
||||
} else if (sourceObj.isInstanceOf(ObjIterable)) {
|
||||
loopIterable(forContext, sourceObj, loopSO, body, elseStatement, label, canBreak)
|
||||
} else {
|
||||
val size = runCatching { sourceObj.readField(forContext, "size").value.toInt() }
|
||||
.getOrElse {
|
||||
throw ScriptError(
|
||||
pos,
|
||||
"object is not enumerable: no size in $sourceObj",
|
||||
it
|
||||
)
|
||||
}
|
||||
|
||||
var result: Obj = ObjVoid
|
||||
var breakCaught = false
|
||||
|
||||
if (size > 0) {
|
||||
var current = runCatching { sourceObj.getAt(forContext, ObjInt.of(0)) }
|
||||
.getOrElse {
|
||||
throw ScriptError(
|
||||
pos,
|
||||
"object is not enumerable: no index access for ${sourceObj.inspect(scope)}",
|
||||
it
|
||||
)
|
||||
}
|
||||
var index = 0
|
||||
while (true) {
|
||||
loopSO.value = current
|
||||
try {
|
||||
result = body.execute(forContext)
|
||||
} catch (lbe: LoopBreakContinueException) {
|
||||
if (lbe.label == label || lbe.label == null) {
|
||||
breakCaught = true
|
||||
if (lbe.doContinue) continue
|
||||
result = lbe.result
|
||||
break
|
||||
} else {
|
||||
throw lbe
|
||||
}
|
||||
}
|
||||
if (++index >= size) break
|
||||
current = sourceObj.getAt(forContext, ObjInt.of(index.toLong()))
|
||||
}
|
||||
}
|
||||
if (!breakCaught && elseStatement != null) {
|
||||
result = elseStatement.execute(scope)
|
||||
}
|
||||
result
|
||||
}
|
||||
return interpreterDisabled(scope, "for-in statement")
|
||||
}
|
||||
|
||||
private suspend fun loopIntRange(
|
||||
@ -310,29 +228,7 @@ class WhileStatement(
|
||||
override val pos: Pos,
|
||||
) : Statement() {
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
var result: Obj = ObjVoid
|
||||
var wasBroken = false
|
||||
while (condition.execute(scope).toBool()) {
|
||||
val loopScope = scope.createChildScope().apply { skipScopeCreation = true }
|
||||
if (canBreak) {
|
||||
try {
|
||||
result = body.execute(loopScope)
|
||||
} catch (lbe: LoopBreakContinueException) {
|
||||
if (lbe.label == label || lbe.label == null) {
|
||||
if (lbe.doContinue) continue
|
||||
result = lbe.result
|
||||
wasBroken = true
|
||||
break
|
||||
} else {
|
||||
throw lbe
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = body.execute(loopScope)
|
||||
}
|
||||
}
|
||||
if (!wasBroken) elseStatement?.let { s -> result = s.execute(scope) }
|
||||
return result
|
||||
return interpreterDisabled(scope, "while statement")
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,30 +241,7 @@ class DoWhileStatement(
|
||||
override val pos: Pos,
|
||||
) : Statement() {
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
var wasBroken = false
|
||||
var result: Obj = ObjVoid
|
||||
while (true) {
|
||||
val doScope = scope.createChildScope().apply { skipScopeCreation = true }
|
||||
try {
|
||||
result = body.execute(doScope)
|
||||
} catch (e: LoopBreakContinueException) {
|
||||
if (e.label == label || e.label == null) {
|
||||
if (!e.doContinue) {
|
||||
result = e.result
|
||||
wasBroken = true
|
||||
break
|
||||
}
|
||||
// continue: fall through to condition check
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
if (!condition.execute(doScope).toBool()) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!wasBroken) elseStatement?.let { s -> result = s.execute(scope) }
|
||||
return result
|
||||
return interpreterDisabled(scope, "do-while statement")
|
||||
}
|
||||
}
|
||||
|
||||
@ -378,12 +251,7 @@ class BreakStatement(
|
||||
override val pos: Pos,
|
||||
) : Statement() {
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val returnValue = resultExpr?.execute(scope)
|
||||
throw LoopBreakContinueException(
|
||||
doContinue = false,
|
||||
label = label,
|
||||
result = returnValue ?: ObjVoid
|
||||
)
|
||||
return interpreterDisabled(scope, "break statement")
|
||||
}
|
||||
}
|
||||
|
||||
@ -392,10 +260,7 @@ class ContinueStatement(
|
||||
override val pos: Pos,
|
||||
) : Statement() {
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
throw LoopBreakContinueException(
|
||||
doContinue = true,
|
||||
label = label,
|
||||
)
|
||||
return interpreterDisabled(scope, "continue statement")
|
||||
}
|
||||
}
|
||||
|
||||
@ -405,8 +270,7 @@ class ReturnStatement(
|
||||
override val pos: Pos,
|
||||
) : Statement() {
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val returnValue = resultExpr?.execute(scope) ?: ObjVoid
|
||||
throw ReturnException(returnValue, label)
|
||||
return interpreterDisabled(scope, "return statement")
|
||||
}
|
||||
}
|
||||
|
||||
@ -415,28 +279,7 @@ class ThrowStatement(
|
||||
override val pos: Pos,
|
||||
) : Statement() {
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
var errorObject = throwExpr.execute(scope)
|
||||
val throwScope = scope.createChildScope(pos = pos)
|
||||
if (errorObject is ObjString) {
|
||||
errorObject = ObjException(throwScope, errorObject.value).apply { getStackTrace() }
|
||||
}
|
||||
if (!errorObject.isInstanceOf(ObjException.Root)) {
|
||||
throwScope.raiseError("this is not an exception object: $errorObject")
|
||||
}
|
||||
if (errorObject is ObjException) {
|
||||
errorObject = ObjException(
|
||||
errorObject.exceptionClass,
|
||||
throwScope,
|
||||
errorObject.message,
|
||||
errorObject.extraData,
|
||||
errorObject.useStackTrace
|
||||
).apply { getStackTrace() }
|
||||
throwScope.raiseError(errorObject)
|
||||
} else {
|
||||
val msg = errorObject.invokeInstanceMethod(scope, "message").toString(scope).value
|
||||
throwScope.raiseError(errorObject, pos, msg)
|
||||
}
|
||||
return ObjVoid
|
||||
return interpreterDisabled(scope, "throw statement")
|
||||
}
|
||||
}
|
||||
|
||||
@ -444,7 +287,7 @@ class ExpressionStatement(
|
||||
val ref: net.sergeych.lyng.obj.ObjRef,
|
||||
override val pos: Pos
|
||||
) : Statement() {
|
||||
override suspend fun execute(scope: Scope): Obj = ref.evalValue(scope)
|
||||
override suspend fun execute(scope: Scope): Obj = interpreterDisabled(scope, "expression statement")
|
||||
}
|
||||
|
||||
fun Statement.raise(text: String): Nothing {
|
||||
@ -459,17 +302,17 @@ fun Statement.require(cond: Boolean, message: () -> String) {
|
||||
fun statement(pos: Pos, isStaticConst: Boolean = false, isConst: Boolean = false, f: suspend (Scope) -> Obj): Statement =
|
||||
object : Statement(isStaticConst, isConst) {
|
||||
override val pos: Pos = pos
|
||||
override suspend fun execute(scope: Scope): Obj = f(scope)
|
||||
override suspend fun execute(scope: Scope): Obj = interpreterDisabled(scope, "statement bridge")
|
||||
}
|
||||
|
||||
fun statement(isStaticConst: Boolean = false, isConst: Boolean = false, f: suspend Scope.() -> Obj): Statement =
|
||||
object : Statement(isStaticConst, isConst) {
|
||||
override val pos: Pos = Pos.builtIn
|
||||
override suspend fun execute(scope: Scope): Obj = f(scope)
|
||||
override suspend fun execute(scope: Scope): Obj = interpreterDisabled(scope, "statement bridge")
|
||||
}
|
||||
|
||||
object NopStatement: Statement(true, true, ObjType.Void) {
|
||||
override val pos: Pos = Pos.builtIn
|
||||
|
||||
override suspend fun execute(scope: Scope): Obj = ObjVoid
|
||||
override suspend fun execute(scope: Scope): Obj = interpreterDisabled(scope, "nop statement")
|
||||
}
|
||||
|
||||
@ -734,7 +734,7 @@ class ScriptTest {
|
||||
listOf(
|
||||
ArgsDeclaration.Item("a"),
|
||||
ArgsDeclaration.Item("b"),
|
||||
ArgsDeclaration.Item("c", defaultValue = statement { ObjInt(100) }),
|
||||
ArgsDeclaration.Item("c", defaultValue = ObjExternCallable.fromBridge { ObjInt(100) }),
|
||||
), ttEnd
|
||||
)
|
||||
pa.assignToContext(c)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user