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

miksilo.modularLanguages.deltas.bytecode.simpleBytecode.LabelledLocations.scala Maven / Gradle / Ivy

The newest version!
package miksilo.modularLanguages.deltas.bytecode.simpleBytecode

import miksilo.modularLanguages.core.deltas._
import miksilo.modularLanguages.core.deltas.grammars.LanguageGrammars
import miksilo.modularLanguages.core.node._
import miksilo.languageServer.core.language.{Compilation, Language}
import miksilo.modularLanguages.deltas.bytecode.ByteCodeSkeleton
import miksilo.modularLanguages.deltas.bytecode.attributes.CodeAttributeDelta._
import miksilo.modularLanguages.deltas.bytecode.attributes.StackMapTableAttributeDelta.{StackMapFrameGrammar, offsetGrammarKey}
import miksilo.modularLanguages.deltas.bytecode.attributes.{AttributeNameKey, CodeAttributeDelta, StackMapTableAttributeDelta}
import miksilo.modularLanguages.deltas.bytecode.coreInstructions.GotoDelta
import miksilo.modularLanguages.deltas.bytecode.coreInstructions.InstructionInstance.Instruction
import miksilo.modularLanguages.deltas.bytecode.coreInstructions.integers.integerCompare._
import miksilo.modularLanguages.deltas.bytecode.simpleBytecode.LabelDelta.Label

import scala.collection.immutable.ArraySeq
import scala.collection.mutable
import scala.collection.mutable.{ArrayBuffer, ListBuffer}

object LabelledLocations extends DeltaWithPhase with DeltaWithGrammar {

  def jump(delta: JumpInstruction, target: String): Node = delta.shape.create(JumpName -> target)
  def ifZero(target: String): Node = jump(IfZeroDelta, target)
  def ifNotZero(target: String): Node = jump(IfNotZero, target)
  def goTo(target: String): Node = jump(GotoDelta, target)
  def ifIntegerCompareGreaterEquals(target: String): Node = jump(IfIntegerCompareGreaterOrEqualDelta, target)
  def ifIntegerCompareLess(target: String): Node = jump(IfIntegerCompareLessDelta, target)
  def ifIntegerCompareGreater(target: String): Node = jump(IfIntegerCompareGreaterDelta, target)
  def ifIntegerCompareEquals(target: String): Node = jump(IfIntegerCompareEqualDelta, target)
  def ifIntegerCompareNotEquals(target: String): Node = jump(IfIntegerCompareNotEqualDelta, target)
  def ifIntegerCompareLessEquals(target: String): Node = jump(IfIntegerCompareLessOrEqualDelta, target)

  override def inject(language: Language): Unit = {
    super.inject(language)
    LabelDelta.inject(language)
  }

  def transformProgram(program: Node, compilation: Compilation): Unit = {

    val instructionDeltas = CodeAttributeDelta.instructions.get(compilation)
    def instructionSize(instruction: Node) = instructionDeltas(instruction.shape).getInstructionSize(compilation)

    val classFile = program
    val codeAnnotations = CodeAttributeDelta.getCodeAnnotations(classFile)

    for (codeAnnotation <- codeAnnotations) {
      processCodeAnnotation(codeAnnotation)
    }

    def processCodeAnnotation(codeAnnotation: CodeAttribute[Node]): Unit = {
      val instructions = codeAnnotation.instructions
      val labelLocations: Map[String, Int] = determineLabelLocations(instructions)
      codeAnnotation.attributes = codeAnnotation.attributes ++ getStackMapTable(labelLocations, instructions)
      codeAnnotation.instructions = getNewInstructions(instructions, labelLocations)
    }

    def determineLabelLocations(instructions: Seq[Node]): Map[String, Int] = {
      val targetLocations = mutable.Map[String, Int]()
      var location = 0
      for (instruction <- instructions) {
        if (instruction.shape == LabelDelta.shape) {
          targetLocations(new Label(instruction).name) = location
        }

        location += instructionSize(instruction)
      }
      targetLocations.toMap
    }

    def getNewInstructions(instructions: Seq[Instruction[Node]], targetLocations: Map[String, Int]): Seq[Node] = {
      var newInstructions = ListBuffer.empty[Node]

      var location = 0
      for (instruction <- instructions) {

        if (instruction.jumpBehavior.hasJumpInFirstArgument) {
          setInstructionArguments(instruction, Seq(targetLocations(getJumpInstructionLabel(instruction)) - location))
          instruction.data.remove(JumpName)
        }

        if (instruction.shape != LabelDelta.shape)
          newInstructions.addOne(instruction)

        location += instructionSize(instruction)
      }

      newInstructions.toSeq
    }
  }

  def getJumpInstructionLabel(instruction: Node): String = {
    instruction(JumpName).asInstanceOf[String]
  }

  def getStackMapTable(labelLocations: Map[String, Int], instructions: Seq[Node]): Seq[Node] = {
    val locationsWithFrame = instructions.filter(i => i.shape == LabelDelta.Shape).map(i => new Label(i)).
      map(i => (labelLocations(i.name), i.stackFrame))
    var locationAfterPreviousFrame = 0
    var stackFrames = ListBuffer.empty[Node]
    for ((location, frame) <- locationsWithFrame) {
      val offset = location - locationAfterPreviousFrame
      if (offset >= 0) { //TODO add a test-case for consecutive labels.
        frame(StackMapTableAttributeDelta.FrameOffset) = offset
        stackFrames += frame
        locationAfterPreviousFrame = location + 1
      }
    }
    if (stackFrames.nonEmpty) {
      Seq(StackMapTableAttributeDelta.Shape.create(
        AttributeNameKey -> StackMapTableAttributeDelta.entry,
        StackMapTableAttributeDelta.Maps -> stackFrames.toSeq))
    }
    else
      Seq.empty[Node]
  }

  override def dependencies: Set[Contract] = Set(StackMapTableAttributeDelta, ByteCodeSkeleton, IfIntegerCompareGreaterOrEqualDelta, GotoDelta, IfZeroDelta)

  override def description: String = "Replaces the jump instructions from bytecode. " +
    "The new instructions are similar to the old ones except that they use labels as target instead of instruction indices."

  override def transformGrammars(grammars: LanguageGrammars, language: Language): Unit = {
    replaceJumpIndicesWithLabels(grammars, language)
    removeOffsetFromStackMapFrameGrammars(grammars)
    import grammars._
    find(ByteCodeSkeleton.AttributeGrammar).findLabelled(StackMapTableAttributeDelta.Shape).removeMeFromOption()
  }

  def removeOffsetFromStackMapFrameGrammars(grammars: LanguageGrammars): Unit = {
    val offsetGrammar = grammars.find(offsetGrammarKey)
    import grammars._
    val offsetGrammarPaths = find(StackMapFrameGrammar).descendants.filter(path => path.value == offsetGrammar)
    offsetGrammarPaths.foreach(delta => delta.removeMe())
  }

  object JumpName extends NodeField
  def replaceJumpIndicesWithLabels(_grammars: LanguageGrammars, language: Language): Unit = {
    val grammars = _grammars
    import grammars._
    val instructionDeltas = CodeAttributeDelta.instructions.get(language).values
    val jumpInstructionDeltas = instructionDeltas.filter(v => v.jumpBehavior.hasJumpInFirstArgument)
    for(jump <- jumpInstructionDeltas)
    {
      val grammar = find(jump.shape)
      grammar.inner = jump.grammarName ~~> LabelDelta.getNameGrammar(grammars).as(JumpName) asNode jump.shape
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy