Add minimal bytecode compiler scaffold
This commit is contained in:
parent
f42ea0a04c
commit
d8b00a805c
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
class BytecodeBuilder {
|
||||||
|
data class Instr(val op: Opcode, val operands: IntArray)
|
||||||
|
|
||||||
|
private val instructions = mutableListOf<Instr>()
|
||||||
|
private val constPool = mutableListOf<BytecodeConst>()
|
||||||
|
|
||||||
|
fun addConst(c: BytecodeConst): Int {
|
||||||
|
constPool += c
|
||||||
|
return constPool.lastIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
fun emit(op: Opcode, vararg operands: Int) {
|
||||||
|
instructions += Instr(op, operands.copyOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(name: String, localCount: Int): BytecodeFunction {
|
||||||
|
val slotWidth = when {
|
||||||
|
localCount < 256 -> 1
|
||||||
|
localCount < 65536 -> 2
|
||||||
|
else -> 4
|
||||||
|
}
|
||||||
|
val constIdWidth = if (constPool.size < 65536) 2 else 4
|
||||||
|
val ipWidth = 2
|
||||||
|
val code = ByteArrayOutput()
|
||||||
|
for (ins in instructions) {
|
||||||
|
code.writeU8(ins.op.code.toInt() and 0xFF)
|
||||||
|
val kinds = operandKinds(ins.op)
|
||||||
|
if (kinds.size != ins.operands.size) {
|
||||||
|
error("Operand count mismatch for ${ins.op}: expected ${kinds.size}, got ${ins.operands.size}")
|
||||||
|
}
|
||||||
|
for (i in kinds.indices) {
|
||||||
|
val v = ins.operands[i]
|
||||||
|
when (kinds[i]) {
|
||||||
|
OperandKind.SLOT -> code.writeUInt(v, slotWidth)
|
||||||
|
OperandKind.CONST -> code.writeUInt(v, constIdWidth)
|
||||||
|
OperandKind.IP -> code.writeUInt(v, ipWidth)
|
||||||
|
OperandKind.COUNT -> code.writeUInt(v, 2)
|
||||||
|
OperandKind.ID -> code.writeUInt(v, 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BytecodeFunction(
|
||||||
|
name = name,
|
||||||
|
localCount = localCount,
|
||||||
|
slotWidth = slotWidth,
|
||||||
|
ipWidth = ipWidth,
|
||||||
|
constIdWidth = constIdWidth,
|
||||||
|
constants = constPool.toList(),
|
||||||
|
code = code.toByteArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun operandKinds(op: Opcode): List<OperandKind> {
|
||||||
|
return when (op) {
|
||||||
|
Opcode.NOP, Opcode.RET_VOID -> emptyList()
|
||||||
|
Opcode.MOVE_OBJ, Opcode.MOVE_INT, Opcode.MOVE_REAL, Opcode.MOVE_BOOL,
|
||||||
|
Opcode.INT_TO_REAL, Opcode.REAL_TO_INT, Opcode.BOOL_TO_INT, Opcode.INT_TO_BOOL,
|
||||||
|
Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
||||||
|
Opcode.CONST_NULL ->
|
||||||
|
listOf(OperandKind.SLOT)
|
||||||
|
Opcode.CONST_OBJ, Opcode.CONST_INT, Opcode.CONST_REAL, Opcode.CONST_BOOL ->
|
||||||
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
|
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
|
||||||
|
Opcode.ADD_REAL, Opcode.SUB_REAL, Opcode.MUL_REAL, Opcode.DIV_REAL,
|
||||||
|
Opcode.AND_INT, Opcode.OR_INT, Opcode.XOR_INT, Opcode.SHL_INT, Opcode.SHR_INT, Opcode.USHR_INT,
|
||||||
|
Opcode.CMP_EQ_INT, Opcode.CMP_NEQ_INT, Opcode.CMP_LT_INT, Opcode.CMP_LTE_INT,
|
||||||
|
Opcode.CMP_GT_INT, Opcode.CMP_GTE_INT,
|
||||||
|
Opcode.CMP_EQ_REAL, Opcode.CMP_NEQ_REAL, Opcode.CMP_LT_REAL, Opcode.CMP_LTE_REAL,
|
||||||
|
Opcode.CMP_GT_REAL, Opcode.CMP_GTE_REAL,
|
||||||
|
Opcode.CMP_EQ_BOOL, Opcode.CMP_NEQ_BOOL,
|
||||||
|
Opcode.CMP_EQ_INT_REAL, Opcode.CMP_EQ_REAL_INT, Opcode.CMP_LT_INT_REAL, Opcode.CMP_LT_REAL_INT,
|
||||||
|
Opcode.CMP_LTE_INT_REAL, Opcode.CMP_LTE_REAL_INT, Opcode.CMP_GT_INT_REAL, Opcode.CMP_GT_REAL_INT,
|
||||||
|
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT,
|
||||||
|
Opcode.AND_BOOL, Opcode.OR_BOOL ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
|
Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET ->
|
||||||
|
listOf(OperandKind.SLOT)
|
||||||
|
Opcode.JMP ->
|
||||||
|
listOf(OperandKind.IP)
|
||||||
|
Opcode.JMP_IF_TRUE, Opcode.JMP_IF_FALSE ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.IP)
|
||||||
|
Opcode.CALL_DIRECT, Opcode.CALL_FALLBACK ->
|
||||||
|
listOf(OperandKind.ID, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT)
|
||||||
|
Opcode.CALL_VIRTUAL ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT)
|
||||||
|
Opcode.GET_FIELD ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.SLOT)
|
||||||
|
Opcode.SET_FIELD ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.ID, OperandKind.SLOT)
|
||||||
|
Opcode.GET_INDEX ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
|
Opcode.SET_INDEX ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
|
Opcode.EVAL_FALLBACK ->
|
||||||
|
listOf(OperandKind.ID, OperandKind.SLOT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum class OperandKind {
|
||||||
|
SLOT,
|
||||||
|
CONST,
|
||||||
|
IP,
|
||||||
|
COUNT,
|
||||||
|
ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ByteArrayOutput {
|
||||||
|
private val data = ArrayList<Byte>(256)
|
||||||
|
|
||||||
|
fun writeU8(v: Int) {
|
||||||
|
data.add((v and 0xFF).toByte())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeUInt(v: Int, width: Int) {
|
||||||
|
var value = v
|
||||||
|
var remaining = width
|
||||||
|
while (remaining-- > 0) {
|
||||||
|
writeU8(value)
|
||||||
|
value = value ushr 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toByteArray(): ByteArray = data.toByteArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,267 @@
|
|||||||
|
/*
|
||||||
|
* 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.ExpressionStatement
|
||||||
|
import net.sergeych.lyng.obj.*
|
||||||
|
|
||||||
|
class BytecodeCompiler {
|
||||||
|
private val builder = BytecodeBuilder()
|
||||||
|
private var nextSlot = 0
|
||||||
|
|
||||||
|
fun compileExpression(name: String, stmt: ExpressionStatement): BytecodeFunction? {
|
||||||
|
val value = compileRef(stmt.ref) ?: return null
|
||||||
|
builder.emit(Opcode.RET, value.slot)
|
||||||
|
val localCount = maxOf(nextSlot, value.slot + 1)
|
||||||
|
return builder.build(name, localCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class CompiledValue(val slot: Int, val type: SlotType)
|
||||||
|
|
||||||
|
private fun allocSlot(): Int = nextSlot++
|
||||||
|
|
||||||
|
private fun compileRef(ref: ObjRef): CompiledValue? {
|
||||||
|
return when (ref) {
|
||||||
|
is ConstRef -> compileConst(ref.constValue)
|
||||||
|
is LocalSlotRef -> {
|
||||||
|
if (ref.name.isEmpty()) return null
|
||||||
|
if (refDepth(ref) != 0) return null
|
||||||
|
CompiledValue(refSlot(ref), SlotType.UNKNOWN)
|
||||||
|
}
|
||||||
|
is BinaryOpRef -> compileBinary(ref)
|
||||||
|
is UnaryOpRef -> compileUnary(ref)
|
||||||
|
is AssignRef -> compileAssign(ref)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compileConst(obj: Obj): CompiledValue? {
|
||||||
|
val slot = allocSlot()
|
||||||
|
when (obj) {
|
||||||
|
is ObjInt -> {
|
||||||
|
val id = builder.addConst(BytecodeConst.IntVal(obj.value))
|
||||||
|
builder.emit(Opcode.CONST_INT, id, slot)
|
||||||
|
return CompiledValue(slot, SlotType.INT)
|
||||||
|
}
|
||||||
|
is ObjReal -> {
|
||||||
|
val id = builder.addConst(BytecodeConst.RealVal(obj.value))
|
||||||
|
builder.emit(Opcode.CONST_REAL, id, slot)
|
||||||
|
return CompiledValue(slot, SlotType.REAL)
|
||||||
|
}
|
||||||
|
is ObjBool -> {
|
||||||
|
val id = builder.addConst(BytecodeConst.Bool(obj.value))
|
||||||
|
builder.emit(Opcode.CONST_BOOL, id, slot)
|
||||||
|
return CompiledValue(slot, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
is ObjString -> {
|
||||||
|
val id = builder.addConst(BytecodeConst.StringVal(obj.value))
|
||||||
|
builder.emit(Opcode.CONST_OBJ, id, slot)
|
||||||
|
return CompiledValue(slot, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
ObjNull -> {
|
||||||
|
builder.emit(Opcode.CONST_NULL, slot)
|
||||||
|
return CompiledValue(slot, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val id = builder.addConst(BytecodeConst.ObjRef(obj))
|
||||||
|
builder.emit(Opcode.CONST_OBJ, id, slot)
|
||||||
|
return CompiledValue(slot, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compileUnary(ref: UnaryOpRef): CompiledValue? {
|
||||||
|
val a = compileRef(unaryOperand(ref)) ?: return null
|
||||||
|
val out = allocSlot()
|
||||||
|
return when (unaryOp(ref)) {
|
||||||
|
UnaryOp.NEGATE -> when (a.type) {
|
||||||
|
SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.NEG_INT, a.slot, out)
|
||||||
|
CompiledValue(out, SlotType.INT)
|
||||||
|
}
|
||||||
|
SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.NEG_REAL, a.slot, out)
|
||||||
|
CompiledValue(out, SlotType.REAL)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
UnaryOp.NOT -> {
|
||||||
|
if (a.type != SlotType.BOOL) return null
|
||||||
|
builder.emit(Opcode.NOT_BOOL, a.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
UnaryOp.BITNOT -> {
|
||||||
|
if (a.type != SlotType.INT) return null
|
||||||
|
builder.emit(Opcode.INV_INT, a.slot, out)
|
||||||
|
CompiledValue(out, SlotType.INT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compileBinary(ref: BinaryOpRef): CompiledValue? {
|
||||||
|
val a = compileRef(binaryLeft(ref)) ?: return null
|
||||||
|
val b = compileRef(binaryRight(ref)) ?: return null
|
||||||
|
if (a.type != b.type && a.type != SlotType.UNKNOWN && b.type != SlotType.UNKNOWN) return null
|
||||||
|
val out = allocSlot()
|
||||||
|
val op = binaryOp(ref)
|
||||||
|
return when (op) {
|
||||||
|
BinOp.PLUS -> when (a.type) {
|
||||||
|
SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.ADD_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.INT)
|
||||||
|
}
|
||||||
|
SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.ADD_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.REAL)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
BinOp.MINUS -> when (a.type) {
|
||||||
|
SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.SUB_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.INT)
|
||||||
|
}
|
||||||
|
SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.SUB_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.REAL)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
BinOp.STAR -> when (a.type) {
|
||||||
|
SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.MUL_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.INT)
|
||||||
|
}
|
||||||
|
SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.MUL_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.REAL)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
BinOp.SLASH -> when (a.type) {
|
||||||
|
SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.DIV_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.INT)
|
||||||
|
}
|
||||||
|
SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.DIV_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.REAL)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
BinOp.PERCENT -> {
|
||||||
|
if (a.type != SlotType.INT) return null
|
||||||
|
builder.emit(Opcode.MOD_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.INT)
|
||||||
|
}
|
||||||
|
BinOp.EQ -> {
|
||||||
|
if (a.type != SlotType.INT) return null
|
||||||
|
builder.emit(Opcode.CMP_EQ_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
BinOp.NEQ -> {
|
||||||
|
if (a.type != SlotType.INT) return null
|
||||||
|
builder.emit(Opcode.CMP_NEQ_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
BinOp.LT -> {
|
||||||
|
if (a.type != SlotType.INT) return null
|
||||||
|
builder.emit(Opcode.CMP_LT_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
BinOp.LTE -> {
|
||||||
|
if (a.type != SlotType.INT) return null
|
||||||
|
builder.emit(Opcode.CMP_LTE_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
BinOp.GT -> {
|
||||||
|
if (a.type != SlotType.INT) return null
|
||||||
|
builder.emit(Opcode.CMP_GT_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
BinOp.GTE -> {
|
||||||
|
if (a.type != SlotType.INT) return null
|
||||||
|
builder.emit(Opcode.CMP_GTE_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
BinOp.BAND -> {
|
||||||
|
if (a.type != SlotType.INT) return null
|
||||||
|
builder.emit(Opcode.AND_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.INT)
|
||||||
|
}
|
||||||
|
BinOp.BOR -> {
|
||||||
|
if (a.type != SlotType.INT) return null
|
||||||
|
builder.emit(Opcode.OR_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.INT)
|
||||||
|
}
|
||||||
|
BinOp.BXOR -> {
|
||||||
|
if (a.type != SlotType.INT) return null
|
||||||
|
builder.emit(Opcode.XOR_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.INT)
|
||||||
|
}
|
||||||
|
BinOp.SHL -> {
|
||||||
|
if (a.type != SlotType.INT) return null
|
||||||
|
builder.emit(Opcode.SHL_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.INT)
|
||||||
|
}
|
||||||
|
BinOp.SHR -> {
|
||||||
|
if (a.type != SlotType.INT) return null
|
||||||
|
builder.emit(Opcode.SHR_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.INT)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compileAssign(ref: AssignRef): CompiledValue? {
|
||||||
|
val target = assignTarget(ref) ?: return null
|
||||||
|
if (refDepth(target) != 0) return null
|
||||||
|
val value = compileRef(assignValue(ref)) ?: return null
|
||||||
|
val slot = refSlot(target)
|
||||||
|
when (value.type) {
|
||||||
|
SlotType.INT -> builder.emit(Opcode.MOVE_INT, value.slot, slot)
|
||||||
|
SlotType.REAL -> builder.emit(Opcode.MOVE_REAL, value.slot, slot)
|
||||||
|
SlotType.BOOL -> builder.emit(Opcode.MOVE_BOOL, value.slot, slot)
|
||||||
|
else -> builder.emit(Opcode.MOVE_OBJ, value.slot, slot)
|
||||||
|
}
|
||||||
|
return CompiledValue(slot, value.type)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refSlot(ref: LocalSlotRef): Int = ref.slot
|
||||||
|
|
||||||
|
private fun refDepth(ref: LocalSlotRef): Int = refDepthAccessor(ref)
|
||||||
|
|
||||||
|
private fun binaryLeft(ref: BinaryOpRef): ObjRef = binaryLeftAccessor(ref)
|
||||||
|
private fun binaryRight(ref: BinaryOpRef): ObjRef = binaryRightAccessor(ref)
|
||||||
|
private fun binaryOp(ref: BinaryOpRef): BinOp = binaryOpAccessor(ref)
|
||||||
|
|
||||||
|
private fun unaryOperand(ref: UnaryOpRef): ObjRef = unaryOperandAccessor(ref)
|
||||||
|
private fun unaryOp(ref: UnaryOpRef): UnaryOp = unaryOpAccessor(ref)
|
||||||
|
|
||||||
|
private fun assignTarget(ref: AssignRef): LocalSlotRef? = assignTargetAccessor(ref)
|
||||||
|
private fun assignValue(ref: AssignRef): ObjRef = assignValueAccessor(ref)
|
||||||
|
|
||||||
|
// Accessor helpers to avoid exposing fields directly in ObjRef classes.
|
||||||
|
private fun refDepthAccessor(ref: LocalSlotRef): Int = ref.depth
|
||||||
|
private fun binaryLeftAccessor(ref: BinaryOpRef): ObjRef = ref.left
|
||||||
|
private fun binaryRightAccessor(ref: BinaryOpRef): ObjRef = ref.right
|
||||||
|
private fun binaryOpAccessor(ref: BinaryOpRef): BinOp = ref.op
|
||||||
|
private fun unaryOperandAccessor(ref: UnaryOpRef): ObjRef = ref.a
|
||||||
|
private fun unaryOpAccessor(ref: UnaryOpRef): UnaryOp = ref.op
|
||||||
|
private fun assignTargetAccessor(ref: AssignRef): LocalSlotRef? = ref.target as? LocalSlotRef
|
||||||
|
private fun assignValueAccessor(ref: AssignRef): ObjRef = ref.value
|
||||||
|
}
|
||||||
@ -89,7 +89,7 @@ enum class BinOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** R-value reference for unary operations. */
|
/** R-value reference for unary operations. */
|
||||||
class UnaryOpRef(private val op: UnaryOp, private val a: ObjRef) : ObjRef {
|
class UnaryOpRef(internal val op: UnaryOp, internal val a: ObjRef) : ObjRef {
|
||||||
override suspend fun get(scope: Scope): ObjRecord {
|
override suspend fun get(scope: Scope): ObjRecord {
|
||||||
val v = a.evalValue(scope)
|
val v = a.evalValue(scope)
|
||||||
if (PerfFlags.PRIMITIVE_FASTOPS) {
|
if (PerfFlags.PRIMITIVE_FASTOPS) {
|
||||||
@ -141,7 +141,7 @@ class UnaryOpRef(private val op: UnaryOp, private val a: ObjRef) : ObjRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** R-value reference for binary operations. */
|
/** R-value reference for binary operations. */
|
||||||
class BinaryOpRef(private val op: BinOp, private val left: ObjRef, private val right: ObjRef) : ObjRef {
|
class BinaryOpRef(internal val op: BinOp, internal val left: ObjRef, internal val right: ObjRef) : ObjRef {
|
||||||
override suspend fun get(scope: Scope): ObjRecord {
|
override suspend fun get(scope: Scope): ObjRecord {
|
||||||
return evalValue(scope).asReadonly
|
return evalValue(scope).asReadonly
|
||||||
}
|
}
|
||||||
@ -2403,8 +2403,8 @@ class ImplicitThisMethodCallRef(
|
|||||||
*/
|
*/
|
||||||
class LocalSlotRef(
|
class LocalSlotRef(
|
||||||
val name: String,
|
val name: String,
|
||||||
private val slot: Int,
|
internal val slot: Int,
|
||||||
private val depth: Int,
|
internal val depth: Int,
|
||||||
private val atPos: Pos,
|
private val atPos: Pos,
|
||||||
) : ObjRef {
|
) : ObjRef {
|
||||||
override fun forEachVariable(block: (String) -> Unit) {
|
override fun forEachVariable(block: (String) -> Unit) {
|
||||||
@ -2657,8 +2657,8 @@ class AssignIfNullRef(
|
|||||||
|
|
||||||
/** Simple assignment: target = value */
|
/** Simple assignment: target = value */
|
||||||
class AssignRef(
|
class AssignRef(
|
||||||
private val target: ObjRef,
|
internal val target: ObjRef,
|
||||||
private val value: ObjRef,
|
internal val value: ObjRef,
|
||||||
private val atPos: Pos,
|
private val atPos: Pos,
|
||||||
) : ObjRef {
|
) : ObjRef {
|
||||||
override suspend fun get(scope: Scope): ObjRecord {
|
override suspend fun get(scope: Scope): ObjRecord {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user