All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.alephium.ralph.VariablesRef.scala Maven / Gradle / Ivy

// Copyright 2018 The Alephium Authors
// This file is part of the alephium project.
//
// The library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the library. If not, see .

package org.alephium.ralph

import org.alephium.protocol.vm._
import org.alephium.util.U256

sealed trait VarOffset[Ctx <: StatelessContext] {
  def add(offset: VarOffset[Ctx]): VarOffset[Ctx]
  def add(value: Int): VarOffset[Ctx]              = add(ConstantVarOffset[Ctx](value))
  def add(instrs: Seq[Instr[Ctx]]): VarOffset[Ctx] = add(VariableVarOffset[Ctx](instrs))
  def genCode(): Seq[Instr[Ctx]] = this match {
    case ConstantVarOffset(index)  => Seq(ConstInstr.u256(Val.U256(U256.unsafe(index))))
    case VariableVarOffset(instrs) => instrs
  }
}

object VarOffset {
  def calcArrayElementOffset[Ctx <: StatelessContext](
      state: Compiler.State[Ctx],
      offset: VarOffset[Ctx],
      index: Ast.Expr[Ctx],
      arrayType: Type.FixedSizeArray,
      flattenSize: Int
  ): VarOffset[Ctx] = {
    calcArrayElementOffset(state, index, arrayType) match {
      case ConstantVarOffset(value) =>
        offset.add(value * flattenSize)
      case VariableVarOffset(instrs) =>
        offset.add(instrs ++ Seq(ConstInstr.u256(Val.U256.unsafe(flattenSize)), U256Mul))
    }
  }

  def calcArrayElementOffset[Ctx <: StatelessContext](
      state: Compiler.State[Ctx],
      index: Ast.Expr[Ctx],
      arrayType: Type.FixedSizeArray
  ): VarOffset[Ctx] = {
    val arraySize = state.calcArraySize(arrayType)
    Compiler.State.getAndCheckConstantIndex(index) match {
      case Some(idx) =>
        ArrayRef.checkArrayIndex(idx, arraySize, index.sourceIndex)
        ConstantVarOffset(idx)
      case None =>
        val instrs = index.genCode(state) ++ Seq(
          Dup,
          ConstInstr.u256(Val.U256(U256.unsafe(arraySize))),
          U256Lt,
          Assert
        )
        VariableVarOffset(instrs)
    }
  }
}

final case class ConstantVarOffset[Ctx <: StatelessContext](value: Int) extends VarOffset[Ctx] {
  def add(offset: VarOffset[Ctx]): VarOffset[Ctx] = offset match {
    case ConstantVarOffset(v) => ConstantVarOffset(value + v)
    case VariableVarOffset(instrs) =>
      if (value == 0) {
        offset
      } else {
        VariableVarOffset(
          instrs ++ Seq[Instr[Ctx]](ConstInstr.u256(Val.U256(U256.unsafe(value))), U256Add)
        )
      }
  }
}

final case class VariableVarOffset[Ctx <: StatelessContext](instrs: Seq[Instr[Ctx]])
    extends VarOffset[Ctx] {
  def add(offset: VarOffset[Ctx]): VarOffset[Ctx] = offset match {
    case ConstantVarOffset(v) =>
      if (v == 0) {
        this
      } else {
        VariableVarOffset(
          instrs ++ Seq[Instr[Ctx]](ConstInstr.u256(Val.U256(U256.unsafe(v))), U256Add)
        )
      }
    case VariableVarOffset(codes) => VariableVarOffset(instrs ++ codes :+ U256Add)
  }
}

sealed trait VariablesRefOffset[Ctx <: StatelessContext] extends Product with Serializable {
  def getStoreOffset: VarOffset[Ctx]
}
final case class LocalRefOffset[Ctx <: StatelessContext](offset: VarOffset[Ctx])
    extends VariablesRefOffset[Ctx] {
  def getStoreOffset: VarOffset[Ctx] = offset
}
final case class DataRefOffset[Ctx <: StatelessContext](
    immDataOffset: VarOffset[Ctx],
    mutDataOffset: VarOffset[Ctx]
) extends VariablesRefOffset[Ctx] {
  def getStoreOffset: VarOffset[Ctx] = mutDataOffset

  def calcArrayElementOffset(
      state: Compiler.State[Ctx],
      tpe: Type.FixedSizeArray,
      index: Ast.Expr[Ctx],
      isMutable: Boolean
  ): DataRefOffset[Ctx] = {
    val result      = state.flattenTypeMutability(tpe.baseType, isMutable)
    val mutDataSize = result.count(identity)
    val immDataSize = result.length - mutDataSize
    DataRefOffset(
      VarOffset.calcArrayElementOffset(state, immDataOffset, index, tpe, immDataSize),
      VarOffset.calcArrayElementOffset(state, mutDataOffset, index, tpe, mutDataSize)
    )
  }

  def calcStructFieldOffset(
      state: Compiler.State[Ctx],
      ast: Ast.Struct,
      field: Ast.Ident,
      isMutable: Boolean
  ): DataRefOffset[Ctx] = {
    val result = ast.calcFieldOffset(state, field, isMutable)
    DataRefOffset(immDataOffset.add(result._1), mutDataOffset.add(result._2))
  }
}

sealed trait VariablesRef[Ctx <: StatelessContext] {
  def ident: Ast.Ident
  def isLocal: Boolean
  def isMutable: Boolean
  def isTemplate: Boolean
  def tpe: Type

  def subRef(state: Compiler.State[Ctx], selector: Ast.DataSelector): VariablesRef[Ctx]
  def subRef(state: Compiler.State[Ctx], selectors: Seq[Ast.DataSelector]): VariablesRef[Ctx] = {
    selectors.foldLeft(this) { case (acc, selector) => acc.subRef(state, selector) }
  }
  def genLoadFieldsCode(state: Compiler.State[Ctx]): Seq[Seq[Instr[Ctx]]]

  def genLoadCode(state: Compiler.State[Ctx]): Seq[Instr[Ctx]]
  def genLoadCode(state: Compiler.State[Ctx], selector: Ast.DataSelector): Seq[Instr[Ctx]]
  def genStoreCode(state: Compiler.State[Ctx]): Seq[Seq[Instr[Ctx]]]
  def genStoreCode(state: Compiler.State[Ctx], selector: Ast.DataSelector): Seq[Seq[Instr[Ctx]]]
}

object VariablesRef {
  // scalastyle:off parameter.number
  // scalastyle:off method.length
  def init[Ctx <: StatelessContext](
      state: Compiler.State[Ctx],
      tpe: Type,
      baseName: String,
      isMutable: Boolean,
      isUnused: Boolean,
      isLocal: Boolean,
      isGenerated: Boolean,
      isTemplate: Boolean,
      varInfoBuilder: Compiler.VarInfoBuilder
  ): VariablesRef[Ctx] = {
    val refOffset = if (isLocal) {
      LocalRefOffset[Ctx](ConstantVarOffset(state.currentScopeState.varIndex))
    } else if (isTemplate) {
      LocalRefOffset[Ctx](ConstantVarOffset(state.templateVarIndex))
    } else {
      DataRefOffset[Ctx](
        ConstantVarOffset(state.immFieldsIndex),
        ConstantVarOffset(state.mutFieldsIndex)
      )
    }
    val varType = state.resolveType(tpe)
    val ref: VariablesRef[Ctx] = varType match {
      case arrayType: Type.FixedSizeArray =>
        val arraySize = state.calcArraySize(arrayType)
        (0 until arraySize).foreach { idx =>
          val ident = Ast.Ident(ArrayRef.arrayVarName(baseName, idx))
          state.addVariable(
            ident,
            arrayType.baseType,
            isMutable,
            isUnused,
            isLocal,
            isGenerated = true,
            isTemplate,
            varInfoBuilder
          )
        }
        val ident = Ast.Ident(baseName)
        ArrayRef.from(ident, arrayType, isLocal, isMutable, isTemplate, refOffset)
      case structType: Type.Struct =>
        val ast = state.getStruct(structType.id)
        ast.fields.foreach { field =>
          val ident = Ast.Ident(StructRef.structVarName(baseName, field.name))
          state.addVariable(
            ident,
            state.resolveType(field.tpe),
            isMutable && field.isMutable,
            isUnused,
            isLocal,
            isGenerated = true,
            isTemplate,
            varInfoBuilder
          )
        }
        val ident = Ast.Ident(baseName)
        StructRef.from(ident, isLocal, isMutable, isTemplate, refOffset, ast)
      case _ => // dead branch
        throw Compiler.Error(s"Expected array or struct type, got $varType", None)
    }
    state.addVariablesRef(ref.ident, isMutable, isUnused, isGenerated, ref)
    ref
  }
  // scalastyle:on parameter.number
  // scalastyle:on method.length
}

sealed trait StructRef[Ctx <: StatelessContext] extends VariablesRef[Ctx] {
  def ast: Ast.Struct
  def tpe: Type.Struct = ast.tpe
  def refOffset: VariablesRefOffset[Ctx]

  def calcRefOffset(state: Compiler.State[Ctx], selector: Ast.Ident): VariablesRefOffset[Ctx]
  def calcDataOffset(state: Compiler.State[Ctx], selector: Ast.Ident): VarOffset[Ctx]

  private def getIdentSelector(selector: Ast.DataSelector): Ast.IdentSelector = {
    selector match {
      case Ast.IndexSelector(index) =>
        throw Compiler.Error(s"Expected ident selector, got index selector", index.sourceIndex)
      case selector: Ast.IdentSelector => selector
    }
  }

  def subRef(state: Compiler.State[Ctx], selector: Ast.DataSelector): VariablesRef[Ctx] = {
    subRef(state, getIdentSelector(selector).ident)
  }

  def subRef(state: Compiler.State[Ctx], ident: Ast.Ident): VariablesRef[Ctx] = {
    val field     = ast.getField(ident)
    val refOffset = calcRefOffset(state, ident)
    state.resolveType(field.tpe) match {
      case tpe: Type.FixedSizeArray =>
        ArrayRef.from(ident, tpe, isLocal, isMutable && field.isMutable, isTemplate, refOffset)
      case tpe: Type.Struct =>
        val struct       = state.getStruct(tpe.id)
        val isRefMutable = isMutable && field.isMutable
        StructRef.from(ident, isLocal, isRefMutable, isTemplate, refOffset, struct)
      case tpe =>
        throw Compiler.Error(s"Expected array or struct type, got $tpe", ident.sourceIndex)
    }
  }

  private def genStoreCode(
      state: Compiler.State[Ctx],
      selector: Ast.Ident,
      tpe: Type
  ): Seq[Instr[Ctx]] = {
    tpe match {
      case _: Type.FixedSizeArray | _: Type.Struct =>
        subRef(state, selector).genStoreCode(state).reverse.flatten
      case _ =>
        val offset = calcDataOffset(state, selector)
        state.genStoreCode(offset, isLocal)
    }
  }

  @SuppressWarnings(Array("org.wartremover.warts.Recursion"))
  def genStoreCode(state: Compiler.State[Ctx]): Seq[Seq[Instr[Ctx]]] = {
    ast.fields.map(field => genStoreCode(state, field.ident, state.resolveType(field.tpe)))
  }

  def genStoreCode(
      state: Compiler.State[Ctx],
      selector: Ast.DataSelector
  ): Seq[Seq[Instr[Ctx]]] = {
    val ident = getIdentSelector(selector).ident
    val field = ast.getField(ident)
    Seq(genStoreCode(state, field.ident, state.resolveType(field.tpe)))
  }

  private def genLoadCode(
      state: Compiler.State[Ctx],
      selector: Ast.Ident,
      tpe: Type
  ): Seq[Instr[Ctx]] = {
    tpe match {
      case _: Type.FixedSizeArray | _: Type.Struct =>
        subRef(state, selector).genLoadCode(state)
      case _ =>
        val field  = ast.getField(selector)
        val offset = calcDataOffset(state, selector)
        state.genLoadCode(ident, isTemplate, isLocal, isMutable && field.isMutable, tpe, offset)
    }
  }

  @SuppressWarnings(Array("org.wartremover.warts.Recursion"))
  def genLoadCode(state: Compiler.State[Ctx]): Seq[Instr[Ctx]] = {
    ast.fields.flatMap(field => genLoadCode(state, field.ident, state.resolveType(field.tpe)))
  }

  def genLoadFieldsCode(state: Compiler.State[Ctx]): Seq[Seq[Instr[Ctx]]] = {
    ast.fields.foldLeft(Seq.empty[Seq[Instr[Ctx]]]) { case (acc, field) =>
      state.resolveType(field.tpe) match {
        case _: Type.Struct | _: Type.FixedSizeArray =>
          acc ++ subRef(state, field.ident).genLoadFieldsCode(state)
        case _ => acc :+ genLoadCode(state, field.ident)
      }
    }
  }

  @inline def genLoadCode(state: Compiler.State[Ctx], ident: Ast.Ident): Seq[Instr[Ctx]] = {
    val field = ast.getField(ident)
    genLoadCode(state, field.ident, state.resolveType(field.tpe))
  }

  def genLoadCode(state: Compiler.State[Ctx], selector: Ast.DataSelector): Seq[Instr[Ctx]] = {
    genLoadCode(state, getIdentSelector(selector).ident)
  }
}

final case class FieldStructRef[Ctx <: StatelessContext](
    ident: Ast.Ident,
    isLocal: Boolean,
    isMutable: Boolean,
    isTemplate: Boolean,
    refOffset: DataRefOffset[Ctx],
    ast: Ast.Struct
) extends StructRef[Ctx] {
  def calcRefOffset(state: Compiler.State[Ctx], selector: Ast.Ident): DataRefOffset[Ctx] = {
    refOffset.calcStructFieldOffset(state, ast, selector, isMutable)
  }

  def calcDataOffset(state: Compiler.State[Ctx], selector: Ast.Ident): VarOffset[Ctx] = {
    val field     = ast.getField(selector)
    val newOffset = calcRefOffset(state, selector)
    if (isMutable && field.isMutable) newOffset.mutDataOffset else newOffset.immDataOffset
  }
}

final case class LocalStructRef[Ctx <: StatelessContext](
    ident: Ast.Ident,
    isLocal: Boolean,
    isMutable: Boolean,
    isTemplate: Boolean,
    refOffset: LocalRefOffset[Ctx],
    ast: Ast.Struct
) extends StructRef[Ctx] {
  def calcRefOffset(state: Compiler.State[Ctx], selector: Ast.Ident): LocalRefOffset[Ctx] = {
    LocalRefOffset(refOffset.offset.add(ast.calcLocalOffset(state, selector)))
  }

  def calcDataOffset(state: Compiler.State[Ctx], selector: Ast.Ident): VarOffset[Ctx] = {
    calcRefOffset(state, selector).offset
  }
}

object StructRef {
  @inline def structVarName(baseName: String, field: String): String = s"_$baseName-$field"

  def from[Ctx <: StatelessContext](
      ident: Ast.Ident,
      isLocal: Boolean,
      isMutable: Boolean,
      isTemplate: Boolean,
      refOffset: VariablesRefOffset[Ctx],
      ast: Ast.Struct
  ): StructRef[Ctx] = {
    refOffset match {
      case offset: LocalRefOffset[Ctx] =>
        LocalStructRef(ident, isLocal, isMutable, isTemplate, offset, ast)
      case offset: DataRefOffset[Ctx] =>
        FieldStructRef(ident, isLocal, isMutable, isTemplate, offset, ast)
    }
  }
}

sealed trait ArrayRef[Ctx <: StatelessContext] extends VariablesRef[Ctx] {
  type Selector = Ast.Expr[Ctx]
  def refOffset: VariablesRefOffset[Ctx]
  def tpe: Type.FixedSizeArray

  def calcRefOffset(state: Compiler.State[Ctx], index: Ast.Expr[Ctx]): VariablesRefOffset[Ctx]

  private def getIndexSelector(selector: Ast.DataSelector): Ast.IndexSelector[Ctx] = {
    selector match {
      case selector: Ast.IndexSelector[Ctx @unchecked] => selector
      case Ast.IdentSelector(ident) =>
        throw Compiler.Error("Expected index selector, got ident selector", ident.sourceIndex)
    }
  }

  def genLoadCode(
      state: Compiler.State[Ctx],
      index: Ast.Expr[Ctx]
  ): Seq[Instr[Ctx]] = {
    tpe.baseType match {
      case _: Type.FixedSizeArray | _: Type.Struct =>
        subRef(state, index).genLoadCode(state)
      case _ =>
        val offset = refOffset match {
          case LocalRefOffset(offset) => offset
          case DataRefOffset(immDataOffset, mutDataOffset) =>
            if (isMutable) mutDataOffset else immDataOffset
        }
        state.genLoadCode(
          ident,
          isTemplate,
          isLocal,
          isMutable,
          tpe.baseType,
          offset.add(VarOffset.calcArrayElementOffset(state, index, tpe))
        )
    }
  }

  def genLoadCode(
      state: Compiler.State[Ctx],
      selector: Ast.DataSelector
  ): Seq[Instr[Ctx]] = {
    genLoadCode(state, getIndexSelector(selector).index)
  }

  def genStoreCode(
      state: Compiler.State[Ctx],
      selector: Ast.DataSelector
  ): Seq[Seq[Instr[Ctx]]] = {
    val index = getIndexSelector(selector).index
    tpe.baseType match {
      case _: Type.FixedSizeArray | _: Type.Struct =>
        subRef(state, index).genStoreCode(state)
      case _ =>
        val offset = refOffset.getStoreOffset
        Seq(
          state.genStoreCode(
            offset.add(VarOffset.calcArrayElementOffset(state, index, tpe)),
            isLocal
          )
        )
    }
  }

  def genLoadFieldsCode(state: Compiler.State[Ctx]): Seq[Seq[Instr[Ctx]]] = {
    val arraySize = state.calcArraySize(tpe)
    (0 until arraySize).foldLeft(Seq.empty[Seq[Instr[Ctx]]]) { case (acc, index) =>
      val indexExpr = Ast.Const[Ctx](Val.U256(U256.unsafe(index)))
      tpe.baseType match {
        case _: Type.Struct | _: Type.FixedSizeArray =>
          acc ++ subRef(state, indexExpr).genLoadFieldsCode(state)
        case _ => acc :+ genLoadCode(state, indexExpr)
      }
    }
  }

  def subRef(state: Compiler.State[Ctx], selector: Ast.DataSelector): VariablesRef[Ctx] = {
    subRef(state, getIndexSelector(selector).index)
  }

  def subRef(state: Compiler.State[Ctx], index: Ast.Expr[Ctx]): VariablesRef[Ctx] = {
    val newRefOffset = calcRefOffset(state, index)
    tpe.baseType match {
      case baseType: Type.FixedSizeArray =>
        ArrayRef.from(ident, baseType, isLocal, isMutable, isTemplate, newRefOffset)
      case tpe: Type.Struct =>
        val struct = state.getStruct(tpe.id)
        StructRef.from(ident, isLocal, isMutable, isTemplate, newRefOffset, struct)
      case _ =>
        throw Compiler.Error(
          s"Expect array or struct type, have ${tpe.baseType}",
          index.sourceIndex
        )
    }
  }
}

final case class FieldArrayRef[Ctx <: StatelessContext](
    ident: Ast.Ident,
    tpe: Type.FixedSizeArray,
    isLocal: Boolean,
    isMutable: Boolean,
    isTemplate: Boolean,
    refOffset: DataRefOffset[Ctx]
) extends ArrayRef[Ctx] {
  def calcRefOffset(state: Compiler.State[Ctx], index: Ast.Expr[Ctx]): DataRefOffset[Ctx] = {
    refOffset.calcArrayElementOffset(state, tpe, index, isMutable)
  }

  private def storeImmFieldArrayIndexVar(
      state: Compiler.State[Ctx],
      instrs: Seq[Instr[Ctx]]
  ): (Ast.Ident, Seq[Instr[Ctx]]) = {
    val ident = state.getImmFieldArrayVarIndex()
    (ident, instrs ++ state.genStoreCode(ident).flatten)
  }

  private def storeMutFieldArrayIndexVar(
      state: Compiler.State[Ctx],
      instrs: Seq[Instr[Ctx]]
  ): (Ast.Ident, Seq[Instr[Ctx]]) = {
    val ident = state.getMutFieldArrayVarIndex()
    (ident, instrs ++ state.genStoreCode(ident).flatten)
  }

  // scalastyle:off method.length
  def genLoadCode(state: Compiler.State[Ctx]): Seq[Instr[Ctx]] = {
    refOffset match {
      case DataRefOffset(VariableVarOffset(immInstrs), VariableVarOffset(mutInstrs)) =>
        val (ident0, codes0) = storeImmFieldArrayIndexVar(state, immInstrs)
        val (ident1, codes1) = storeMutFieldArrayIndexVar(state, mutInstrs)
        val immVarIndex      = state.genLoadCode(ident0)
        val mutVarIndex      = state.genLoadCode(ident1)
        state
          .flattenTypeMutability(tpe, isMutable)
          .foldLeft((codes0 ++ codes1, 0, 0)) { case ((codes, immIndex, mutIndex), isMutable) =>
            if (isMutable) {
              val offsetCodes =
                mutVarIndex ++ Seq(ConstInstr.u256(Val.U256(U256.unsafe(mutIndex))), U256Add)
              val loadCodes = state.genLoadCode(
                ident,
                isTemplate,
                isLocal,
                isMutable,
                tpe.elementType,
                VariableVarOffset(offsetCodes)
              )
              (codes ++ loadCodes, immIndex, mutIndex + 1)
            } else {
              val offsetCodes =
                immVarIndex ++ Seq(ConstInstr.u256(Val.U256(U256.unsafe(immIndex))), U256Add)
              val loadCodes = state.genLoadCode(
                ident,
                isTemplate,
                isLocal,
                isMutable,
                tpe.elementType,
                VariableVarOffset(offsetCodes)
              )
              (codes ++ loadCodes, immIndex + 1, mutIndex)
            }
          }
          ._1
      case DataRefOffset(ConstantVarOffset(immIndex), ConstantVarOffset(mutIndex)) =>
        state
          .flattenTypeMutability(tpe, isMutable)
          .foldLeft((Seq.empty[Instr[Ctx]], immIndex, mutIndex)) {
            case ((codes, immIndex, mutIndex), isMutable) =>
              if (isMutable) {
                val loadCodes = state.genLoadCode(
                  ident,
                  isTemplate,
                  isLocal,
                  isMutable,
                  tpe.elementType,
                  ConstantVarOffset(mutIndex)
                )
                (codes ++ loadCodes, immIndex, mutIndex + 1)
              } else {
                val loadCodes = state.genLoadCode(
                  ident,
                  isTemplate,
                  isLocal,
                  isMutable,
                  tpe.elementType,
                  ConstantVarOffset(immIndex)
                )
                (codes ++ loadCodes, immIndex + 1, mutIndex)
              }
          }
          ._1
      case _ => throw Compiler.Error("Invalid variable offset", ident.sourceIndex) // dead branch
    }
  }
  // scalastyle:on method.length

  def genStoreCode(state: Compiler.State[Ctx]): Seq[Seq[Instr[Ctx]]] = {
    val flattenSize = state.flattenTypeLength(Seq(tpe))
    refOffset match {
      case DataRefOffset(_, VariableVarOffset(mutInstrs)) =>
        val (ident, codes) = storeMutFieldArrayIndexVar(state, mutInstrs)
        val mutVarIndex    = state.genLoadCode(ident)
        val storeCodes = (0 until flattenSize) map { idx =>
          val calcOffsetCode = mutVarIndex ++ Seq(
            ConstInstr.u256(Val.U256(U256.unsafe(idx))),
            U256Add
          )
          state.genStoreCode(VariableVarOffset(calcOffsetCode), isLocal)
        }
        storeCodes :+ codes
      case DataRefOffset(_, ConstantVarOffset(value)) =>
        (0 until flattenSize) map { idx =>
          state.genStoreCode(ConstantVarOffset(value + idx), isLocal)
        }
      case _ => throw Compiler.Error("Invalid variable offset", ident.sourceIndex) // dead branch
    }
  }
}

final case class LocalArrayRef[Ctx <: StatelessContext](
    ident: Ast.Ident,
    tpe: Type.FixedSizeArray,
    isLocal: Boolean,
    isMutable: Boolean,
    isTemplate: Boolean,
    refOffset: LocalRefOffset[Ctx]
) extends ArrayRef[Ctx] {
  def calcRefOffset(state: Compiler.State[Ctx], index: Ast.Expr[Ctx]): LocalRefOffset[Ctx] = {
    LocalRefOffset(
      VarOffset.calcArrayElementOffset(
        state,
        refOffset.offset,
        index,
        tpe,
        state.flattenTypeLength(Seq(tpe.baseType))
      )
    )
  }

  private def storeLocalArrayIndexVar(
      state: Compiler.State[Ctx],
      instrs: Seq[Instr[Ctx]]
  ): (Ast.Ident, Seq[Instr[Ctx]]) = {
    val ident = state.getLocalArrayVarIndex()
    (ident, instrs ++ state.genStoreCode(ident).flatten)
  }

  def genLoadCode(state: Compiler.State[Ctx]): Seq[Instr[Ctx]] = {
    refOffset match {
      case LocalRefOffset(VariableVarOffset(instrs)) =>
        val flattenSize    = state.flattenTypeLength(Seq(tpe))
        val (ident, codes) = storeLocalArrayIndexVar(state, instrs)
        val loadCodes = (0 until flattenSize).flatMap { idx =>
          val calcOffsetCode = state.genLoadCode(ident) ++ Seq(
            ConstInstr.u256(Val.U256(U256.unsafe(idx))),
            U256Add
          )
          state.genLoadCode(
            ident,
            isTemplate,
            isLocal,
            isMutable,
            tpe.elementType,
            VariableVarOffset(calcOffsetCode)
          )
        }
        codes ++ loadCodes
      case LocalRefOffset(ConstantVarOffset(value)) =>
        val flattenSize = state.flattenTypeLength(Seq(tpe))
        (0 until flattenSize).flatMap(idx =>
          state.genLoadCode(
            ident,
            isTemplate,
            isLocal,
            isMutable,
            tpe.elementType,
            ConstantVarOffset(value + idx)
          )
        )
    }
  }

  def genStoreCode(state: Compiler.State[Ctx]): Seq[Seq[Instr[Ctx]]] = {
    val flattenSize = state.flattenTypeLength(Seq(tpe))
    refOffset match {
      case LocalRefOffset(VariableVarOffset(instrs)) =>
        val (ident, codes) = storeLocalArrayIndexVar(state, instrs)
        val mutVarIndex    = state.genLoadCode(ident)
        val storeCodes = (0 until flattenSize) map { idx =>
          val calcOffsetCode = mutVarIndex ++ Seq(
            ConstInstr.u256(Val.U256(U256.unsafe(idx))),
            U256Add
          )
          state.genStoreCode(VariableVarOffset(calcOffsetCode), isLocal)
        }
        storeCodes :+ codes
      case LocalRefOffset(ConstantVarOffset(value)) =>
        (0 until flattenSize) map { idx =>
          state.genStoreCode(ConstantVarOffset(value + idx), isLocal)
        }
    }
  }
}

object ArrayRef {
  @inline def arrayVarName(baseName: String, idx: Int): String = s"_$baseName-$idx"

  @inline def checkArrayIndex(
      index: Int,
      arraySize: Int,
      sourceIndex: Option[SourceIndex]
  ): Unit = {
    if (index < 0 || index >= arraySize) {
      throw Compiler.Error(s"Invalid array index: $index, array size: $arraySize", sourceIndex)
    }
  }

  def from[Ctx <: StatelessContext](
      ident: Ast.Ident,
      tpe: Type.FixedSizeArray,
      isLocal: Boolean,
      isMutable: Boolean,
      isTemplate: Boolean,
      refOffset: VariablesRefOffset[Ctx]
  ): ArrayRef[Ctx] = {
    refOffset match {
      case offset: LocalRefOffset[Ctx] =>
        LocalArrayRef(ident, tpe, isLocal, isMutable, isTemplate, offset)
      case offset: DataRefOffset[Ctx] =>
        FieldArrayRef(ident, tpe, isLocal, isMutable, isTemplate, offset)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy