Enforce bytecode-only statements and wrap defaults

This commit is contained in:
Sergey Chernov 2026-02-13 06:44:00 +03:00
parent 489dae6604
commit 26564438e2
12 changed files with 113 additions and 396 deletions

View File

@ -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()

View File

@ -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")
}
}

View File

@ -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>()

View File

@ -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")
}
}

View File

@ -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")
}
}

View File

@ -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 {

View File

@ -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")
}
}

View File

@ -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 {

View File

@ -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")
}
}

View File

@ -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")
}
}

View File

@ -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")
}

View File

@ -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)