Enforce bytecode-only compilation in tests

This commit is contained in:
sergeych committed 2026-01-28 19:39:21 +03:00
1 parent 490faea2ba
commit 9a15470cdb
45 files changed
+152 -62

No files matched your search

@@ -1332,11 +1332,10 @@ class BytecodeCompiler(
return when (stmt) {
is ExpressionStatement -> compileRefWithFallback(stmt.ref, null, stmt.pos)
else -> {
val slot = allocSlot()
val id = builder.addFallback(stmt)
builder.emit(Opcode.EVAL_FALLBACK, id, slot)
updateSlotType(slot, SlotType.OBJ)
CompiledValue(slot, SlotType.OBJ)
throw BytecodeFallbackException(
"Bytecode fallback: unsupported argument expression",
stmt.pos
)
}
}
}
@@ -1490,12 +1489,10 @@ class BytecodeCompiler(
}
private fun emitFallbackStatement(stmt: Statement): CompiledValue {
val slot = allocSlot()
val id = builder.addFallback(stmt)
builder.emit(Opcode.EVAL_FALLBACK, id, slot)
builder.emit(Opcode.BOX_OBJ, slot, slot)
updateSlotType(slot, SlotType.OBJ)
return CompiledValue(slot, SlotType.OBJ)
throw BytecodeFallbackException(
"Bytecode fallback: unsupported statement",
stmt.pos
)
}
private fun compileStatementValueOrFallback(stmt: Statement, needResult: Boolean = true): CompiledValue? {
@@ -1808,10 +1805,10 @@ class BytecodeCompiler(
val rangeObj = ensureObjSlot(rangeValue)
val okSlot = allocSlot()
builder.emit(Opcode.RANGE_INT_BOUNDS, rangeObj.slot, iSlot, endSlot, okSlot)
val fallbackLabel = builder.label()
val badRangeLabel = builder.label()
builder.emit(
Opcode.JMP_IF_FALSE,
listOf(CmdBuilder.Operand.IntVal(okSlot), CmdBuilder.Operand.LabelRef(fallbackLabel))
listOf(CmdBuilder.Operand.IntVal(okSlot), CmdBuilder.Operand.LabelRef(badRangeLabel))
)
val breakFlagSlot = allocSlot()
val falseId = builder.addConst(BytecodeConst.Bool(false))
@@ -1863,9 +1860,11 @@ class BytecodeCompiler(
builder.mark(afterElse)
}
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(doneLabel)))
builder.mark(fallbackLabel)
val fallbackId = builder.addFallback(stmt)
builder.emit(Opcode.EVAL_FALLBACK, fallbackId, resultSlot)
builder.mark(badRangeLabel)
val msgId = builder.addConst(BytecodeConst.StringVal("expected Int range"))
builder.emit(Opcode.CONST_OBJ, msgId, resultSlot)
val posId = builder.addConst(BytecodeConst.PosVal(stmt.pos))
builder.emit(Opcode.THROW, posId, resultSlot)
builder.mark(doneLabel)
return resultSlot
}
@@ -2081,11 +2080,10 @@ class BytecodeCompiler(
return when (stmt) {
is ExpressionStatement -> compileRefWithFallback(stmt.ref, SlotType.BOOL, stmt.pos)
else -> {
val slot = allocSlot()
val id = builder.addFallback(ToBoolStatement(stmt, pos))
builder.emit(Opcode.EVAL_FALLBACK, id, slot)
updateSlotType(slot, SlotType.BOOL)
CompiledValue(slot, SlotType.BOOL)
throw BytecodeFallbackException(
"Bytecode fallback: unsupported condition",
pos
)
}
}
}
@@ -2237,21 +2235,10 @@ class BytecodeCompiler(
compiled = null
}
}
val slot = allocSlot()
val stmt = if (forceType == SlotType.BOOL) {
ToBoolStatement(ExpressionStatement(ref, pos), pos)
} else {
ExpressionStatement(ref, pos)
}
val id = builder.addFallback(stmt)
builder.emit(Opcode.EVAL_FALLBACK, id, slot)
if (forceType == null) {
builder.emit(Opcode.BOX_OBJ, slot, slot)
updateSlotType(slot, SlotType.OBJ)
return CompiledValue(slot, SlotType.OBJ)
}
updateSlotType(slot, forceType)
return CompiledValue(slot, forceType)
throw BytecodeFallbackException(
"Bytecode fallback: unsupported expression",
pos
)
}
private fun refSlot(ref: LocalSlotRef): Int = ref.slot
@@ -0,0 +1,28 @@
/*
* Copyright 2026 Sergey S. Chernov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.bytecode
import net.sergeych.lyng.Pos
class BytecodeFallbackException(
message: String,
val pos: Pos? = null,
) : RuntimeException(message) {
override fun toString(): String =
pos?.let { "${super.toString()} at $it" } ?: super.toString()
}
@@ -44,7 +44,12 @@ class BytecodeStatement private constructor(
): Statement {
if (statement is BytecodeStatement) return statement
val hasUnsupported = containsUnsupportedStatement(statement)
if (hasUnsupported) return unwrapDeep(statement)
if (hasUnsupported) {
throw BytecodeFallbackException(
"Bytecode fallback: unsupported statement in '$nameHint'",
statement.pos
)
}
val safeLocals = allowLocalSlots
val compiler = BytecodeCompiler(
allowLocalSlots = safeLocals,
@@ -52,22 +57,10 @@ class BytecodeStatement private constructor(
rangeLocalNames = rangeLocalNames
)
val compiled = compiler.compileStatement(nameHint, statement)
val fn = compiled ?: run {
val builder = CmdBuilder()
val slot = 0
val id = builder.addFallback(statement)
builder.emit(Opcode.EVAL_FALLBACK, id, slot)
builder.emit(Opcode.RET, slot)
builder.build(
nameHint,
localCount = 1,
addrCount = 0,
returnLabels = returnLabels,
localSlotNames = emptyArray(),
localSlotMutables = BooleanArray(0),
localSlotDepths = IntArray(0)
)
}
val fn = compiled ?: throw BytecodeFallbackException(
"Bytecode fallback: failed to compile '$nameHint'",
statement.pos
)
return BytecodeStatement(statement, fn)
}