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

.scala-fortify_2.12.19.1.1.3.source-code.Translator.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 2016-2024 Lightbend, Inc. All rights reserved.
 * No information contained herein may be reproduced or transmitted in any form
 * or by any means without the express written permission of Lightbend, Inc.
 */

package com.lightbend.tools.fortify.plugin

import scala.tools.nsc

import com.fortify.frontend.nst

import nst._
import nodes._

// T's upper bound could just be `nsc.Global` but I think it's nicer
//   if we declare only the compiler-cake layers we actually use.
// most of the Scala AST types come from Trees.
// we need SymbolTable to get the Constant extractor.

class Translator[T <: nsc.ast.Trees with nsc.symtab.SymbolTable](
    val global: T,
    val showSourceInfo: Boolean = true,
    log: String => Unit = println)
    extends TranslatorBase
    with TranslatorHelpers[T]
    with Closure[T] {

  type SourceFile = scala.reflect.internal.util.SourceFile

  import global._

  /// top level stuff

  // kludge alert: using ListBuffer then calling distinct at the end
  // obviously isn't the optimal data structure.  if you change it
  // make sure to preserve order, so we get reproducible results,
  // which we want for end-to-end testing.  (we could use an ordered
  // set, or just sort on the way out.)
  val seenSymbols = collection.mutable.ListBuffer.empty[Symbol]

  def seen(symbol: Symbol): Unit = {
    seenSymbols += (if (symbol.isJavaDefined && symbol.isModuleClass)
                      symbol.companion
                    else
                      symbol)
    ()
  }

  // when we see the lambda body, we need to know what the target type
  // was (e.g. scala.Function1), but we only have that information at
  // the call site (as far as I know anyway), so we record it when
  // we see it so we can use it later.  keys are DefDef symbols,
  // values are symbols of SAM types
  val lambdaTypes = collection.mutable.Map.empty[Symbol, Type]

  def translateAll(tree: Tree): List[STClassDecl] = {
    val result = collection.mutable.ListBuffer[STClassDecl]()
    def recurse(tree: Tree): Unit =
      tree match {
        case PackageDef(_, stats) =>
          stats.foreach(recurse)
        case cd: ClassDef =>
          result ++= translate(cd)
          ()
        case _ =>
      }
    recurse(tree)
    result.toList
  }

  // includes everything we need to pass around whenever we're
  // recursing into trees:
  // * symbol is the symbol of the enclosing (Scala) DefDef
  // * body is the (NST) block we're currently adding statements to
  // * matchResult is the (NST) temporary variable used to store the result
  //   of a general pattern match
  // * caseLabelSuffix is the number we're currently appending to case
  //   labels in a general pattern match, to keep them unique
  // * defaultLabel is how we keep track of how to jump to the end
  //   of a switch-style pattern match
  case class Context(
      symbol: Symbol,
      body: STBlock,
      matchResult: Option[STVarAccess] = None,
      caseLabelSuffix: Int = -1,
      defaultLabel: Symbol = NoSymbol
  )

  def translate(classDef: ClassDef): List[STClassDecl] = {
    val clazz = toClassDecl(classDef.symbol)
    if (classDef.symbol.isModuleClass)
      clazz.addModifiers(NSTModifiers.Synthetic)
    val clazzes = collection.mutable.ListBuffer[STClassDecl](clazz)
    if (classDef.symbol.isModuleClass && classDef.symbol.originalOwner.hasPackageFlag && !isImplClass(
        classDef.symbol)) {
      addModuleInit(classDef, clazz)
      for (mainClazz <- synthesizeMain(classDef))
        clazzes += mainClazz
    }
    classDef.impl.body.foreach {
      case dd: DefDef =>
        if (shouldTranslate(dd))
          clazz.addFunction(translate(dd, classDef.pos))
      case vd: ValDef =>
        clazz.addField(toFieldDecl(vd.symbol))
      case x =>
        log(s"???: ${showRaw(x)}")
        ()
    }
    // some of the call sites might be inside other lambda bodies, so we can't
    // just march down the list, lambdaTypes might not be full ahead of time.
    while (lambdaTypes.nonEmpty) classDef.impl.body.foreach {
      case dd: DefDef =>
        if (lambdaTypes.isDefinedAt(dd.symbol)) {
          clazzes += translateLambda(dd)
          lambdaTypes.remove(dd.symbol)
        }
      case _ =>
    }
    clazzes.toList
  }

  def translate(dd: DefDef, info: SourceInfo): STFunDecl = {
    resetLocalNames()
    val params = paramNames(dd)
    val fun = toFunDecl(dd.symbol, params)
    if (!isAbstract(dd.symbol)) {
      // we have to pre-register the param symbols with uniquifyVariable
      // in order to prevent them from ever being renamed.  (if there's a local
      // variable in the body with the same name we want to rename the
      // local, not the parameter.)  note that we use dd.vparamss
      // not dd.symbol.paramLists; the latter symbols aren't the same
      // symbols that we'll see in the body.  (the existence of these
      // two different sets of symbols for parameters is a common
      // scalac hacker gotcha, says Adriaan)
      for (param <- params)
        uniquifyVariable(param)
      val context = Context(dd.symbol, new STBlock(info))
      if (fun.getReturnType == VoidType)
        translateStatement(dd.rhs, context)
      else {
        val expr = translateExpression(dd.rhs, context)
        context.body.add {
          val ret = new STReturnStmt(expr.getSourceInfo)
          ret.setExpression(expr)
          ret
        }
      }
      fun.setBody(context.body)
    }
    fun
  }

  def translateStatement(tree: Tree, context: Context): Unit =
    tree match {

      // literal Unit
      case Literal(Constant(())) =>
      // elide

      // another type of literal Unit
      case Select(_, TermName("UNIT")) if tree.tpe == definitions.BoxedUnitTpe =>
      // elide

      // local val
      case ValDef(_, TermName(_), tt @ TypeTree(), rhs) =>
        val name = variableName(tree.symbol, uniquify = true)
        val decl = new STVarDecl(tree.pos, name, typeForType(tt.tpe))
        context.body.add(decl)
        val assign = new STAssignmentStmt(tree.pos)
        assign.setLeft(new STVarAccess(tree.pos, name))
        assign.setRight(translateExpression(rhs, context))
        context.body.add(assign)

      // assign local val
      case Assign(id @ Ident(TermName(_)), rhs) =>
        val assign = new STAssignmentStmt(tree.pos)
        assign.setLeft(
          new STVarAccess(tree.pos, variableName(id.symbol, uniquify = true)))
        assign.setRight(translateExpression(rhs, context))
        context.body.add(assign)

      // assign field
      case Assign(sel @ Select(qualifier, TermName(_)), rhs) =>
        seen(sel.symbol)
        val assign = new STAssignmentStmt(tree.pos)
        assign.setLeft(
          new STFieldAccess(
            sel.pos,
            translateExpression(qualifier, context),
            variableName(sel.symbol),
            typeForType(qualifier.tpe, ref = false)))
        assign.setRight(translateExpression(rhs, context))
        context.body.add(assign)

      // if (no else)
      case If(condTree, thenTree, EmptyTree) =>
        val statement = new STIfElse(tree.pos)
        statement.setPredicate(translateExpression(condTree, context))
        val result = new STBlock(thenTree.pos)
        translateStatement(thenTree, context.copy(body = result))
        statement.setIfBody(result)
        context.body.add(statement)

      // if + else
      case If(condTree, thenTree, elseTree) =>
        val result = new STIfElse(tree.pos)
        result.setPredicate(translateExpression(condTree, context))
        val thenBlock = new STBlock(thenTree.pos)
        translateStatement(thenTree, context.copy(body = thenBlock))
        result.setIfBody(thenBlock)
        elseTree match {
          case Literal(Constant(())) | EmptyTree =>
          // omit
          case _ =>
            val elseBlock = new STBlock(elseTree.pos)
            translateStatement(elseTree, context.copy(body = elseBlock))
            result.setElseBody(elseBlock)
        }
        context.body.add(result)

      // while(true) / do while(true)
      case LabelDef(
            TermName(name),
            List(),
            block @ Block(statements, Apply(Ident(TermName(name2)), List())))
          if (name.startsWith("while$") || name.startsWith("doWhile$")) && name2 == name =>
        translateWhileTrue(tree, block.pos, statements, context)

      // while (normal case)
      case LabelDef(
            TermName(name),
            List(),
            If(condition, block @ Block(statements, Apply(_, List())), Literal(Constant(()))))
          if name.startsWith("while$") =>
        translateWhile(tree, condition, block.pos, statements, context)

      // do while (normal case)
      case LabelDef(
            TermName(name),
            List(),
            block @ Block(
              statements,
              If(condition, Apply(Ident(TermName(name2)), List()), Literal(Constant(())))))
          if name.startsWith("doWhile$") && name2 == name =>
        translateDoWhile(tree, condition, block.pos, statements, context)

      // array update
      case Apply(select @ Select(qualifier, nme.update), List(index, rhs))
          if isArrayType(qualifier.tpe) =>
        val loc = translateExpression(qualifier, context)
        val deref = new STDereference(qualifier.pos, loc)
        val access = new STArrayAccess(tree.pos)
        access.setBase(deref)
        access.setIndex(translateExpression(index, context))
        val result = new STAssignmentStmt(tree.pos)
        result.setLeft(access)
        result.setRight(translateExpression(rhs, context))
        context.body.add(result)

      // synchronized
      case Apply(TypeApply(Select(obj, nme.synchronized_), _), List(body)) =>
        context.body.add {
          val call = new STFunCall(tree.pos)
          call.setVirtual()
          call.setName("__synchronize")
          call.addArgument(translateExpression(obj, context))
          call
        }
        context.body.add {
          val block = new STBlock(body.pos)
          block.setFoldable(false)
          block.setLabel(nextLabel("__synchronized____L__"))
          translateStatement(body, context.copy(body = block))
          block
        }

      // try (statement position)
      case Try(block, catches, finalizer) =>
        // Java translator has the label numbers in 1-2-0 order.
        // surely doesn't matter? might as well just match it, though
        val finallyLabel = nextLabel("__finally____L__")
        val tryLabel = nextLabel("__try____L__")
        context.body.add {
          val body = new STBlock(block.pos)
          body.setFoldable(false)
          body.setLabel(tryLabel)
          translateStatement(block, context.copy(body = body))
          body.add(new STGoto(block.pos, finallyLabel))
          body
        }
        def translateCatch(cd: CaseDef): STBlock =
          (cd: @unchecked) match {
            case CaseDef(bind @ Bind(TermName(_), rhs), _, body) =>
              generateCatch(cd.pos, variableName(bind.symbol, uniquify = true), rhs.tpe, body)
            case CaseDef(Typed(Ident(termNames.WILDCARD), rhs), EmptyTree, body) =>
              generateCatch(cd.pos, temporaries.next(), rhs.tpe, body)
            case CaseDef(Ident(termNames.WILDCARD), EmptyTree, body) =>
              generateCatch(cd.pos, temporaries.next(), definitions.ThrowableTpe, body)
          }
        def generateCatch(info: SourceInfo, name: String, tpe: Type, body: Tree): STBlock = {
          val result = new STBlock(body.pos)
          result.setLabel(nextLabel("__catch____L__"))
          result.setFoldable(false)
          result.add(new STVarDecl(info, name, typeForType(tpe)))
          translateStatement(body, context.copy(body = result))
          result.add(new STGoto(info, finallyLabel))
          result
        }
        catches
          .map(translateCatch)
          .foreach(context.body.add(_))
        context.body.add {
          val finalBlock = new STBlock(
            if (finalizer == EmptyTree) finalizer.pos
            else tree.pos)
          finalBlock.setFoldable(false)
          finalBlock.setLabel(finallyLabel)
          if (finalizer != EmptyTree)
            translateStatement(finalizer, context.copy(body = finalBlock))
          finalBlock
        }

      case Throw(expr) =>
        context.body.add(
          new STThrow(tree.pos, translateExpression(expr, context)))

      case Return(Literal(Constant(())))
          if context.symbol.tpe.resultType.typeSymbol == definitions.UnitClass =>
        context.body.add(new STReturnStmt(tree.pos))

      // Peculiar shape generated by `return x match {...}`
      // https://github.com/lightbend/scala-fortify/issues/393
      case Return(ld: LabelDef) if treeInfo.hasSynthCaseSymbol(ld) =>
        // we're dropping the `return` here as if it didn't exist,
        // so we're actually generating incorrect code, hence
        // bug 393 remains open
        translateStatement(ld, context)

      case Return(expr) =>
        val result = new STReturnStmt(tree.pos)
        result.setExpression(translateExpression(expr, context))
        context.body.add(result)

      // tail-call-optimized method
      case LabelDef(TermName(_), List(Ident(nme.THIS), params @ _*), expr) =>
        translateStatement(expr, context)

      // not completely sure we don't sometimes need to insert a
      // typecast here -- something to keep an eye out for
      case Typed(expr, _) =>
        translateStatement(expr, context)

      // block
      case block @ Block(stats, expr) if !isPatternMatch(block) =>
        (stats :+ expr).foreach(translateStatement(_, context))

      // general pattern match
      case Block(stats, expr) =>
        val block = new STBlock(tree.pos)
        val blockContext =
          context.copy(body = block, caseLabelSuffix = nextCaseLabelSuffix())
        (stats :+ expr).foreach(translateStatement(_, blockContext))
        context.body.add(block)

      // pattern match case
      case LabelDef(TermName(labelName), _, expr) if treeInfo.hasSynthCaseSymbol(tree) =>
        val block = new STBlock(tree.pos)
        block.setLabel(caseLabel(context, labelName))
        translateStatement(expr, context.copy(body = block))
        context.body.add(block)

      // jump to pattern match case
      case Apply(id @ Ident(TermName(labelName)), List()) if treeInfo.hasSynthCaseSymbol(id) =>
        context.body.add(new STGoto(tree.pos, caseLabel(context, labelName)))

      // jump to end of pattern match (with value)
      case Apply(id @ Ident(TermName(labelName)), List(expr)) if treeInfo.hasSynthCaseSymbol(id) =>
        context.matchResult match {
          case None =>
            translateStatement(expr, context)
            context.body.add(
              new STGoto(tree.pos, caseLabel(context, labelName)))
          case Some(access) =>
            val assign = new STAssignmentStmt(expr.pos)
            assign.setLeft(access)
            assign.setRight(translateExpression(expr, context))
            context.body.add(assign)
            context.body.add(
              new STGoto(tree.pos, caseLabel(context, labelName)))
        }

      // end of pattern match
      case LabelDef(TermName(labelName), List(Ident(TermName(in))), rhs)
          if treeInfo.hasSynthCaseSymbol(tree) =>
        val block = new STBlock(tree.pos)
        block.setLabel(caseLabel(context, labelName))
        context.body.add(block)
        rhs match {
          case Ident(TermName(out)) if in == out =>
          // all good!
          case Literal(Constant(())) =>
          // elide
          case _ =>
            context.body.add(unknown(context, tree))
        }

      // switch-style pattern match
      case Match(Typed(expr, _), cases) if cases.nonEmpty =>
        val (inputAccess, inputDecl) =
          useTemporary(typeForType(expr.tpe), expr.pos)
        context.body.add(inputDecl)
        val assign = new STAssignmentStmt(expr.pos)
        assign.setLeft(inputAccess)
        assign.setRight(translateExpression(expr, context))
        context.body.add(assign)
        def ifElseChain(cases: List[Tree], context: Context): Unit =
          cases match {
            case List(CaseDef(Ident(termNames.WILDCARD), EmptyTree, rhs)) =>
              translateStatement(rhs, context)
            case List(firstCase, moreCases @ _*) =>
              val CaseDef(lhs, EmptyTree, rhs) = cases.head: @unchecked
              val alternatives: List[Tree] =
                lhs match {
                  case Alternative(alternatives) =>
                    alternatives
                  case _ =>
                    List(lhs)
                }
              val ifelse = new STIfElse(firstCase.pos)
              val compare =
                combineAlternatives(alternatives, inputAccess, context)
              ifelse.setPredicate(compare)
              val thenBlock = new STBlock(rhs.pos)
              translateStatement(rhs, context.copy(body = thenBlock))
              ifelse.setIfBody(thenBlock)
              val elseBlock = new STBlock(tree.pos)
              ifElseChain(cases.tail, context.copy(body = elseBlock))
              ifelse.setElseBody(elseBlock)
              context.body.add(ifelse)
            case _ =>
              context.body.add(unknown(context, tree))
          }
        ifElseChain(cases, context.copy(caseLabelSuffix = nextCaseLabelSuffix()))

      case _ =>
        val expr = translateExpression(tree, context)
        // drop dangling references to (usually) temporaries
        if (!expr.isInstanceOf[STVarAccess])
          context.body.add(expr)
    }

  private def combineAlternatives(
      alternatives: List[Tree],
      access: STVarAccess,
      context: Context): STExpression =
    alternatives
      .map(alt =>
        new STOpExp(alt.pos, NSTOperators.Equal, access, translateExpression(alt, context)))
      .reduceLeft { (sofar, next) =>
        new STOpExp(sofar.getSourceInfo, NSTOperators.Or, sofar, next)
      }

  def translateExpression(tree: Tree, context: Context): STExpression =
    tree match {

      // a && b
      case Apply(select @ Select(qualifier, nme.ZAND), List(arg)) if isBooleanType(qualifier.tpe) =>
        val (access, decl) =
          useTemporary(typeForType(qualifier.tpe), qualifier.pos)
        context.body.add(decl)
        context.body.add {
          val statement = new STIfElse(tree.pos)
          statement.setPredicate(translateExpression(qualifier, context))
          statement.setIfBody {
            val block = new STBlock(arg.pos)
            val assign = new STAssignmentStmt(arg.pos)
            assign.setLeft(access)
            assign.setRight(
              translateExpression(arg, context.copy(body = block)))
            block.add(assign)
            block
          }
          statement.setElseBody {
            val block = new STBlock(qualifier.pos)
            val assign = new STAssignmentStmt(qualifier.pos)
            assign.setLeft(access)
            assign.setRight(STLiteralExp.create(qualifier.pos, false))
            block.add(assign)
            block
          }
          statement
        }
        access

      // a || b
      case Apply(select @ Select(qualifier, nme.ZOR), List(arg)) if isBooleanType(qualifier.tpe) =>
        val (access, decl) =
          useTemporary(typeForType(qualifier.tpe), qualifier.pos)
        context.body.add(decl)
        context.body.add {
          val statement = new STIfElse(tree.pos)
          statement.setPredicate(translateExpression(qualifier, context))
          statement.setIfBody {
            val block = new STBlock(qualifier.pos)
            val assign = new STAssignmentStmt(qualifier.pos)
            assign.setLeft(access)
            assign.setRight(STLiteralExp.create(qualifier.pos, true))
            block.add(assign)
            block
          }
          statement.setElseBody {
            val block = new STBlock(arg.pos)
            val assign = new STAssignmentStmt(arg.pos)
            assign.setLeft(access)
            assign.setRight(
              translateExpression(arg, context.copy(body = block)))
            block.add(assign)
            block
          }
          statement
        }
        access

      // if + else
      case If(condTree, thenTree, elseTree) =>
        val statement = new STIfElse(tree.pos)
        statement.setPredicate(translateExpression(condTree, context))
        val (access, decl) = useTemporary(typeForType(tree.tpe), tree.pos)
        context.body.add(decl)
        def subBody(subtree: Tree): STBlock = {
          val result = new STBlock(subtree.pos)
          val assign = new STAssignmentStmt(subtree.pos)
          assign.setLeft(access)
          assign.setRight(
            translateExpression(subtree, context.copy(body = result)))
          result.add(assign)
          result
        }
        statement.setIfBody(subBody(thenTree))
        statement.setElseBody(subBody(elseTree))
        context.body.add(statement)
        access

      // while(true) / do while(true)
      case LabelDef(
            TermName(name),
            List(),
            block @ Block(
              List(Block(statements, Apply(Ident(TermName(name2)), List()))),
              result))
          if (name.startsWith("while$") || name.startsWith("doWhile$")) && name2 == name =>
        translateWhileTrue(tree, block.pos, statements, context)
        translateExpression(result, context)

      // while (normal case)
      case LabelDef(
            TermName(name),
            List(),
            Block(
              List(
                If(condition, block @ Block(statements, Apply(_, List())), Literal(_))),
              result)) if name.startsWith("while$") =>
        translateWhile(tree, condition, block.pos, statements, context)
        translateExpression(result, context)

      // do while (normal case)
      case LabelDef(
            TermName(name),
            List(),
            block @ Block(
              List(
                Block(
                  statements,
                  If(condition, Apply(Ident(TermName(name2)), List()), Literal(_)))),
              result)) if name.startsWith("doWhile$") && name2 == name =>
        translateDoWhile(tree, condition, block.pos, statements, context)
        translateExpression(result, context)

      // static field access
      case Select(qualifier, selector) if tree.symbol.isStaticMember =>
        seen(tree.symbol)
        new STStaticFieldAccess(
          tree.pos,
          selector.toString,
          typeForSymbol(qualifier.tpe.typeSymbol, ref = false))

      // lambda
      case Function(_, Apply(sel @ Select(This(_), TermName(_)), args)) =>
        lambdaTypes(sel.symbol) = tree.tpe
        val lamName = s"""${className(context.symbol.owner)}${sel.symbol.name}"""
        val lamType = new STType.STClassType(tree.pos, lamName)
        val lamRefType = new STType.STPointerType(tree.pos, lamType)
        val (access, decl) = useTemporary(lamRefType, tree.pos)
        context.body.add(decl)
        val assign = new STAssignmentStmt(tree.pos)
        assign.setLeft(access)
        assign.setRight {
          val rhs = new STAllocation(tree.pos)
          rhs.setType(lamType)
          rhs
        }
        context.body.add(assign)
        context.body.add {
          val call = new STFunCall(tree.pos)
          val closedOver =
            for {
              (param, arg) <- sel.symbol.info.params zip args
              if param.isPrivateThis
            } yield (param, arg)
          call.setName(
            s"$lamName~~innerinit^~L$lamName^" + closedOver
              .map(_._1.tpe)
              .map(typeString)
              .map(unDollar)
              .mkString)
          call.addArgument(access)
          call.addArgument(new STVarAccess(tree.pos, "this~"))
          // pass in the values we closed over
          for ((_, arg) <- closedOver)
            call.addArgument(translateExpression(arg, context))
          call.setVirtual()
          call
        }
        access

      // this
      case This(_) if tree.tpe.typeSymbol == context.symbol.owner =>
        new STVarAccess(tree.pos, "this~")

      // module access, kind 1 of 4
      // some (not all) module accesses have a `This` node,
      // for reasons that are murky to me
      case Select(th @ This(TypeName(_)), selector) if tree.symbol.isModule =>
        seen(tree.symbol)
        new STStaticFieldAccess(
          tree.pos,
          "MODULE$",
          new STType.STClassType(tree.pos, tree.symbol.fullName + "$"))

      // module access, kind 2 of 4
      case Select(_, selector) if tree.symbol.isModule =>
        seen(tree.symbol)
        new STStaticFieldAccess(
          tree.pos,
          "MODULE$",
          new STType.STClassType(tree.pos, tree.symbol.fullName + "$"))

      // module access, kind 3 of 4
      case This(_) if tree.symbol.isModuleClass =>
        seen(tree.symbol)
        new STStaticFieldAccess(
          tree.pos,
          "MODULE$",
          new STType.STClassType(tree.pos, tree.symbol.fullName + "$"))

      // module access, kind 4 of 4
      case Ident(TermName(_)) if tree.symbol.isModule =>
        seen(tree.symbol)
        new STStaticFieldAccess(
          tree.pos,
          "MODULE$",
          new STType.STClassType(tree.pos, tree.symbol.fullName + "$"))

      // field access
      case Select(qualifier, TermName(_)) =>
        seen(tree.symbol)
        new STFieldAccess(
          tree.pos,
          new STDereference(qualifier.pos, translateExpression(qualifier, context)),
          variableName(tree.symbol),
          typeForType(qualifier.tpe, ref = false))

      // super call
      case Apply(sel @ Select(sup @ Super(_, _), _), args) =>
        val call = new STFunCall(tree.pos)
        call.setVirtual()
        call.setName(methodName(sel.symbol))
        call.addArgument(new STVarAccess(tree.pos, "this~"))
        for (arg <- args)
          call.addArgument(translateExpression(arg, context))
        call

      // synchronized
      case Apply(TypeApply(Select(obj, nme.synchronized_), _), List(body)) =>
        val (access, decl) = useTemporary(typeForType(tree.tpe), tree.pos)
        context.body.add(decl)
        context.body.add {
          val call = new STFunCall(tree.pos)
          call.setVirtual()
          call.setName("__synchronize")
          call.addArgument(translateExpression(obj, context))
          call
        }
        context.body.add {
          val block = new STBlock(body.pos)
          block.setFoldable(false)
          block.setLabel(nextLabel("__synchronized____L__"))
          block.add {
            val assign = new STAssignmentStmt(tree.pos)
            assign.setLeft(access)
            assign.setRight(
              translateExpression(body, context.copy(body = block)))
            assign
          }
          block
        }
        access

      // array creation (variant 1 of 3)
      case Apply(
            Select(Select(Ident(nme.scala_), nme.Array), nme.apply),
            List(expr0, WrapArray(ArrayValue(tt @ TypeTree(), exprs)))) =>
        translateArrayCreation(context, tree.pos, expr0 +: exprs, tree.tpe, tt.tpe)

      // array creation (variant 2 of 3)
      case Apply(
            Select(Select(Ident(nme.scala_), nme.Array), nme.apply),
            List(WrapArray(av @ ArrayValue(tt @ TypeTree(), exprs)), _)) =>
        translateArrayCreation(context, tree.pos, exprs, av.tpe, tt.tpe)

      // array creation (variant 3 of 3)
      case ArrayValue(tt @ TypeTree(), exprs) =>
        translateArrayCreation(context, tree.pos, exprs, tree.tpe, tt.tpe)

      // .asInstanceOf
      case Apply(TypeApply(Select(obj, nme.asInstanceOf_Ob), List(typeArg)), List()) =>
        new STTypeCast(tree.pos, typeForType(typeArg.tpe), translateExpression(obj, context))

      // .isInstanceOf
      case Apply(TypeApply(Select(obj, nme.isInstanceOf_Ob), List(typeArg)), List()) =>
        val call = new STFunCall(tree.pos)
        call.setVirtual()
        call.setName("__instanceof")
        call.addArgument(translateExpression(obj, context))
        call.addArgument(
          new STTypeCast(obj.pos, typeForType(typeArg.tpe), STLiteralExp.create(tree.pos))
        ) // :null:
        call

      // access local variable
      case Ident(TermName(_)) =>
        new STVarAccess(tree.pos, variableName(tree.symbol, uniquify = true))

      // not completely sure we don't sometimes need to insert a
      // typecast here -- something to keep an eye out for
      case Typed(expr, _) =>
        translateExpression(expr, context)

      /// operators

      // string concatenation
      case Apply(select @ Select(qualifier, nme.PLUS), List(arg))
          if qualifier.tpe <:< definitions.StringTpe =>
        new STOpExp(
          tree.pos,
          NSTOperators.Add,
          translateExpression(qualifier, context),
          if (arg.tpe <:< definitions.StringTpe)
            translateExpression(arg, context)
          else {
            val call = new STFunCall(arg.pos)
            call.setName(methodName(definitions.Object_toString))
            call.addArgument(translateExpression(arg, context))
            call
          }
        )

      // numeric conversion
      case Apply(select @ Select(qualifier, TermName(name)), List())
          if conversionOps(name) && isNumericType(qualifier.tpe) =>
        new STTypeCast(
          tree.pos,
          typeForType(qualifier.tpe),
          translateExpression(qualifier, context))

      // binary ops (numeric or boolean)
      case Apply(select @ Select(qualifier, TermName(name)), List(arg))
          if arithmeticOps.isDefinedAt(name) && (isNumericType(qualifier.tpe) || isBooleanType(
            qualifier.tpe)) =>
        new STOpExp(
          tree.pos,
          arithmeticOps(name),
          translateExpression(qualifier, context),
          translateExpression(arg, context))

      // unary plus
      case Apply(select @ Select(qualifier, TermName("unary_$plus")), List())
          if isNumericType(qualifier.tpe) =>
        translateExpression(qualifier, context)

      // unary minus
      case Apply(select @ Select(qualifier, TermName("unary_$minus")), List())
          if isNumericType(qualifier.tpe) =>
        new STOpExp(
          tree.pos,
          NSTOperators.Subtract,
          STLiteralExp.create(tree.pos, 0),
          translateExpression(qualifier, context))

      // unary bitwise complement
      case Apply(select @ Select(qualifier, TermName("unary_$tilde")), List())
          if isNumericType(qualifier.tpe) =>
        new STOpExp(tree.pos, NSTOperators.Bwcomplement, translateExpression(qualifier, context))

      // equality & inequality (null on right)
      case Apply(Select(lhs, TermName("$eq$eq")), List(Literal(Constant(null))))
          if isReferenceType(lhs.tpe) =>
        new STOpExp(
          tree.pos,
          NSTOperators.Equal,
          translateExpression(lhs, context),
          STLiteralExp.create(tree.pos)
        ) // :null:
      case Apply(Select(lhs, TermName("$bang$eq")), List(Literal(Constant(null))))
          if isReferenceType(lhs.tpe) =>
        new STOpExp(
          tree.pos,
          NSTOperators.NotEqual,
          translateExpression(lhs, context),
          STLiteralExp.create(tree.pos)
        ) // :null:

      // equality & inequality (null on left)
      case Apply(Select(Literal(Constant(null)), TermName("$eq$eq")), List(rhs))
          if isReferenceType(rhs.tpe) =>
        new STOpExp(
          tree.pos,
          NSTOperators.Equal,
          STLiteralExp.create(tree.pos), // :null:
          translateExpression(rhs, context))
      case Apply(Select(Literal(Constant(null)), TermName("$bang$eq")), List(rhs))
          if isReferenceType(rhs.tpe) =>
        new STOpExp(
          tree.pos,
          NSTOperators.NotEqual,
          STLiteralExp.create(tree.pos), // :null:
          translateExpression(rhs, context))

      // reference equality & inequality
      // l == r  becomes  if (l eq null) r eq null else l.equals(r)
      case Apply(select @ Select(lhs, TermName("$eq$eq")), List(rhs)) if isReferenceType(lhs.tpe) =>
        translateEqOp(context, tree.pos, tree.tpe, select, lhs, rhs, negate = false)
      case Apply(select @ Select(lhs, TermName("$bang$eq")), List(rhs))
          if isReferenceType(lhs.tpe) =>
        translateEqOp(context, tree.pos, tree.tpe, select, lhs, rhs, negate = true)

      // reference ops
      case Apply(select @ Select(qualifier, TermName(name)), List(arg))
          if referenceOps.isDefinedAt(name) && isReferenceType(
            qualifier.tpe) =>
        new STOpExp(
          tree.pos,
          referenceOps(name),
          translateExpression(qualifier, context),
          translateExpression(arg, context))

      // not
      case Apply(select @ Select(qualifier, nme.UNARY_!), List()) if isBooleanType(qualifier.tpe) =>
        new STOpExp(tree.pos, NSTOperators.Not, translateExpression(qualifier, context))

      // array length
      case Apply(select @ Select(qualifier, nme.length), List()) if isArrayType(qualifier.tpe) =>
        new STOpExp(tree.pos, NSTOperators.Arraylen, translateExpression(qualifier, context))

      // array access
      case Apply(select @ Select(qualifier, nme.apply), List(arg)) if isArrayType(qualifier.tpe) =>
        val loc = translateExpression(qualifier, context)
        val deref = new STDereference(qualifier.pos, loc)
        val result = new STArrayAccess(tree.pos)
        result.setBase(deref)
        result.setIndex(translateExpression(arg, context))
        result

      // constructor invocation (new)
      case Apply(sel @ Select(n @ New(tpe), _), args) =>
        seen(sel.symbol)
        val (access, decl) = useTemporary(typeForType(tpe.tpe), n.pos)
        context.body.add(decl)
        val assign = new STAssignmentStmt(tree.pos)
        assign.setLeft(access)
        assign.setRight {
          val rhs = new STAllocation(tree.pos)
          rhs.setType(typeForSymbol(tpe.symbol, ref = false))
          rhs
        }
        context.body.add(assign)
        context.body.add {
          val call = new STFunCall(tree.pos)
          call.setName(methodName(sel.symbol))
          call.addArgument(access)
          for (arg <- args)
            call.addArgument(translateExpression(arg, context))
          call
        }
        access

      /// literals

      // for testing handling of unknown trees
      case Literal(Constant(Constants.KnownUnknown)) =>
        unknown(context, tree, Some("additional infos for testing"))

      case Literal(Constant(s: String)) =>
        STLiteralExp.create(tree.pos, s)

      case Literal(Constant(i: Int)) =>
        STLiteralExp.create(tree.pos, i)

      case Literal(Constant(l: Long)) =>
        STLiteralExp.create(tree.pos, l)

      case Literal(Constant(c: Char)) =>
        STLiteralExp.create(tree.pos, c)

      case Literal(Constant(b: Boolean)) =>
        STLiteralExp.create(tree.pos, b)

      case Literal(Constant(d: Double)) =>
        STLiteralExp.create(tree.pos, d)

      case Literal(Constant(f: Float)) =>
        STLiteralExp.create(tree.pos, f.toDouble)

      case Literal(Constant(s: Short)) =>
        new STTypeCast(tree.pos, typeForType(tree.tpe), STLiteralExp.create(tree.pos, s.toInt))

      case Literal(Constant(b: Byte)) =>
        new STTypeCast(tree.pos, typeForType(tree.tpe), STLiteralExp.create(tree.pos, b.toInt))

      case Literal(Constant(())) =>
        new STStaticFieldAccess(tree.pos, "UNIT", typeForType(tree.tpe, ref = true))

      case Literal(Constant(null)) =>
        STLiteralExp.create(tree.pos) // :null:

      case Literal(Constant(symbol: Symbol)) if hasJavaEnumFlag(symbol) =>
        seen(symbol)
        new STStaticFieldAccess(tree.pos, symbol.name.toString, typeForType(tree.tpe, ref = false))

      // class literal without type parameters
      case Literal(Constant(TypeRef(_, symbol, _))) =>
        translateClassLiteral(tree.pos, symbol)

      // class literal with type parameters
      case Literal(Constant(ExistentialType(_, TypeRef(_, symbol, _)))) =>
        translateClassLiteral(tree.pos, symbol)

      case Literal(Constant(x)) =>
        unknown(context, tree, Some(s"unknown literal type: ${x.getClass}"))

      // jump to end of switch-style pattern match
      case Apply(_, Nil) if tree.symbol == context.defaultLabel =>
        context.body.add(
          new STGoto(tree.pos, context.defaultLabel.name.toString))
        STLiteralExp.create(tree.pos, "unreachable")

      // jump to end of switch-style pattern match
      case Block(List(app @ Apply(_, Nil)), _) if app.symbol == context.defaultLabel =>
        context.body.add(
          new STGoto(tree.pos, context.defaultLabel.name.toString))
        STLiteralExp.create(tree.pos, "unreachable")

      // tail-call-optimized method
      case LabelDef(TermName(_), List(Ident(nme.THIS), params @ _*), expr) =>
        translateExpression(expr, context)

      // the tail call itself
      case Apply(id @ Ident(TermName(_)), args) =>
        val call = new STFunCall(tree.pos)
        call.setVirtual()
        call.setName(methodName(context.symbol))
        for (arg <- args)
          call.addArgument(translateExpression(arg, context))
        // if a local tail recursive method closes over local variables
        // in an enclosing scope, there are additional arguments to hold
        // the values of those locals.  the "- 1" is because args
        // includes a this-reference.  issue: lightbend/scala-fortify#200
        for (arg <- context.symbol.asMethod.paramss.flatten
            .drop(args.size - 1))
          call.addArgument(new STVarAccess(tree.pos, variableName(arg)))
        call

      // try (expression position)
      case Try(block, catches, finalizer) =>
        val (access, decl) = useTemporary(typeForType(block.tpe), block.pos)
        context.body.add(decl)
        // Java translator has the label numbers in 1-2-0 order.
        // surely doesn't matter? might as well just match it, though
        val finallyLabel = nextLabel("__finally____L__")
        val tryLabel = nextLabel("__try____L__")
        context.body.add {
          val body = new STBlock(block.pos)
          body.setFoldable(false)
          body.setLabel(tryLabel)
          val assign = new STAssignmentStmt(block.pos)
          assign.setLeft(access)
          assign.setRight(
            translateExpression(block, context.copy(body = body)))
          body.add(assign)
          body.add(new STGoto(block.pos, finallyLabel))
          body
        }
        def translateCatch(cd: CaseDef): STBlock =
          (cd: @unchecked) match {
            case CaseDef(bind @ Bind(TermName(_), rhs), _, body) =>
              generateCatch(cd.pos, variableName(bind.symbol, uniquify = true), rhs.tpe, body)
            case CaseDef(Typed(Ident(termNames.WILDCARD), rhs), EmptyTree, body) =>
              generateCatch(cd.pos, temporaries.next(), rhs.tpe, body)
            case CaseDef(Ident(termNames.WILDCARD), EmptyTree, body) =>
              generateCatch(cd.pos, temporaries.next(), definitions.ThrowableTpe, body)
          }
        def generateCatch(info: SourceInfo, name: String, tpe: Type, body: Tree): STBlock = {
          val result = new STBlock(body.pos)
          result.setLabel(nextLabel("__catch____L__"))
          result.setFoldable(false)
          result.add(new STVarDecl(info, name, typeForType(tpe)))
          val assign = new STAssignmentStmt(block.pos)
          assign.setLeft(access)
          assign.setRight(
            translateExpression(body, context.copy(body = result)))
          result.add(assign)
          result.add(new STGoto(info, finallyLabel))
          result
        }
        catches
          .map(translateCatch)
          .foreach(context.body.add(_))
        context.body.add {
          val finalBlock = new STBlock(
            if (finalizer == EmptyTree) finalizer.pos
            else tree.pos)
          finalBlock.setFoldable(false)
          finalBlock.setLabel(finallyLabel)
          if (finalizer != EmptyTree)
            translateStatement(finalizer, context.copy(body = finalBlock))
          finalBlock
        }
        access

      // throw
      case Throw(expr) =>
        context.body.add(
          new STThrow(tree.pos, translateExpression(expr, context)))
        STLiteralExp.create(tree.pos) // :null:

      // return
      case Return(_) =>
        translateStatement(tree, context)
        STLiteralExp.create(tree.pos) // :null:

      // ordinary method call
      case Apply(select @ Select(qualifier, _), args) =>
        seen(select.symbol)
        val call = new STFunCall(tree.pos)
        call.setVirtual()
        call.setName(methodName(select.symbol))
        if (qualifier.symbol != null)
          seen(qualifier.symbol)
        if (!select.symbol.isStaticMember)
          call.addArgument(translateExpression(qualifier, context))
        for (arg <- args)
          call.addArgument(translateExpression(arg, context))
        call

      // block
      case block @ Block(stats, expr) if !isPatternMatch(block) =>
        stats.foreach(translateStatement(_, context))
        translateExpression(expr, context)

      // general pattern match
      case Block(stats, expr) =>
        val (access, decl) = useTemporary(typeForType(expr.tpe), tree.pos)
        context.body.add(decl)
        val block = new STBlock(tree.pos)
        val blockContext = context.copy(
          body = block,
          matchResult = Some(access),
          caseLabelSuffix = nextCaseLabelSuffix())
        (stats :+ expr).foreach(translateStatement(_, blockContext))
        context.body.add(block)
        access

      // ApplyDynamic. could be a method invocation via a structural
      // type.  seems good-enough to just generate code for the
      // constituent parts, and then for the method call, just
      // cast :null: to the expected type; doing better would likely not improve analysis?
      case ApplyDynamic(expr, args) =>
        context.body.add(translateExpression(expr, context))
        for (arg <- args)
          context.body.add(translateExpression(arg, context))
        new STTypeCast(tree.pos, typeForType(tree.tpe), STLiteralExp.create(tree.pos)) // :null:

      // switch-style pattern match
      case Match(Typed(expr, _), cases) if cases.nonEmpty =>
        val (inputAccess, inputDecl) =
          useTemporary(typeForType(expr.tpe), expr.pos)
        val (resultAccess, resultDecl) =
          useTemporary(typeForType(tree.tpe), tree.pos)
        context.body.add(inputDecl)
        val assign = new STAssignmentStmt(expr.pos)
        assign.setLeft(inputAccess)
        assign.setRight(translateExpression(expr, context))
        context.body.add(assign)
        context.body.add(resultDecl)

        val newContext = context.copy(
          defaultLabel = cases
            .collectFirst {
              case CaseDef(Ident(nme.WILDCARD), EmptyTree, body @ LabelDef(_, Nil, _))
                  if treeInfo.hasSynthCaseSymbol(body) =>
                body.symbol
            }
            .getOrElse(NoSymbol))

        def ifElseChain(cases: List[Tree]): STNode =
          cases match {
            case List(
                  lastCase @ CaseDef(
                    Ident(termNames.WILDCARD),
                    EmptyTree,
                    LabelDef(TermName(_), List(), rhs))) =>
              val block = new STBlock(lastCase.pos)
              block.setFoldable(true)
              block.setLabel(newContext.defaultLabel.name.toString)
              val assign = new STAssignmentStmt(lastCase.pos)
              assign.setLeft(resultAccess)
              assign.setRight(
                translateExpression(rhs, newContext.copy(body = block)))
              block.add(assign)
              block
            case List(CaseDef(Ident(termNames.WILDCARD), EmptyTree, rhs)) =>
              val assign = new STAssignmentStmt(cases.head.pos)
              assign.setLeft(resultAccess)
              assign.setRight(translateExpression(rhs, newContext))
              assign
            case List(firstCase, moreCases @ _*) =>
              val CaseDef(lhs, EmptyTree, rhs) = cases.head: @unchecked
              val alternatives: List[Tree] =
                lhs match {
                  case Alternative(alternatives) =>
                    alternatives
                  case _ =>
                    List(lhs)
                }
              val ifelse = new STIfElse(firstCase.pos)
              val compare =
                combineAlternatives(alternatives, inputAccess, newContext)
              ifelse.setPredicate(compare)
              val thenBlock = new STBlock(rhs.pos)
              val assign = new STAssignmentStmt(rhs.pos)
              assign.setLeft(resultAccess)
              assign.setRight(
                translateExpression(rhs, newContext.copy(body = thenBlock)))
              thenBlock.add(assign)
              ifelse.setIfBody(thenBlock)
              val elseBlock = new STBlock(tree.pos)
              elseBlock.add(ifElseChain(cases.tail))
              ifelse.setElseBody(elseBlock)
              ifelse
            case _ =>
              unknown(context, tree)
          }
        context.body.add(ifElseChain(cases))
        resultAccess

      case x =>
        unknown(context, x)
    }

  /// lambdas

  def translateLambda(dd: DefDef): STClassDecl = {
    val name = s"""${className(dd.symbol.owner)}${dd.symbol.name}"""
    val lambdaType = lambdaTypes(dd.symbol)
    val (closedOver, params) = dd.symbol.info.params.partition(_.isPrivateThis)
    val clazz = {
      val result = new STClassDecl(dd.symbol.pos)
      result.addModifiers(NSTModifiers.Private, NSTModifiers.Final)
      result.setSimpleName(s"lambda [${typeForType(lambdaType, ref = false)}]")
      result.setName(name)
      result.addExtends(typeForType(lambdaType, ref = false))
      result.addField {
        val field = new STFieldDecl(dd.pos)
        field.setName("outer~this")
        field.setType(typeForType(dd.symbol.owner.tpe))
        field
      }
      for (param <- closedOver)
        result.addField {
          val field = new STFieldDecl(dd.pos)
          field.setName(variableName(param))
          field.setType(typeForType(param.tpe))
          field
        }
      result
    }
    // lambda body
    clazz.addFunction {
      val result = translate(dd, dd.pos)
      // we're calling addFront repeatedly here, so we have to add
      // things in reverse order of how we want them to appear
      for (param <- closedOver.reverse) {
        // expected:   ~t~x@1 :=: [LambdaVar$anonfun$incrementer$1] (:*: lambda~this) :.: ~t~x@1;
        result.addFront {
          val assign = new STAssignmentStmt(dd.pos)
          assign.setLeft(new STVarAccess(dd.pos, variableName(param)))
          assign.setRight(
            new STFieldAccess(
              dd.pos,
              new STVarAccess(dd.pos, "lambda~this"),
              variableName(param),
              new STType.STClassType(dd.pos, clazz.getName)))
          assign
        }
        result.addFront(
          new STVarDecl(dd.pos, variableName(param), typeForType(param.tpe)))
      }
      result.addFront {
        val assign = new STAssignmentStmt(dd.pos)
        assign.setLeft(new STVarAccess(dd.pos, "this~"))
        assign.setRight(
          new STFieldAccess(
            dd.pos,
            new STVarAccess(dd.pos, "lambda~this"),
            "outer~this",
            new STType.STClassType(dd.pos, clazz.getName)))
        assign
      }
      result.addFront(
        new STVarDecl(dd.pos, "this~", typeForType(dd.symbol.owner.tpe)))
      // drop closed-over parameters, leave real parameters
      while (result.getArglist.size > params.size) result.popArg()
      result.pushArg(
        new STVarDecl(
          dd.pos,
          "lambda~this",
          new STType.STPointerType(
            dd.pos,
            new STType.STClassType(dd.pos, clazz.getName))))
      val samType = lambdaTypes(dd.symbol)
      val samMethod = definitions.samOf(samType)
      result.setName(
        s"$name~~${unDollar(samMethod.name.toString)}~L$name^${params.map(_.tpe).map(
            typeString).map(unDollar).mkString}")
      result.setSimpleName(toSimpleName(samMethod))
      result.addOverride(methodName(samMethod))
      result
    }
    // constructor
    clazz.addFunction {
      val result = new STFunDecl(dd.pos)
      result.setReturnType(VoidType)
      result.setModifiers(NSTModifiers.Public)
      result.setSimpleName("innerinit^")
      result.addParameter(
        new STVarDecl(
          dd.pos,
          "this~",
          new STType.STPointerType(
            dd.pos,
            new STType.STClassType(dd.pos, clazz.getName))))
      result.addParameter(
        new STVarDecl(dd.pos, "outer~this", typeForType(dd.symbol.owner.tpe)))
      result.setName {
        val argTypes =
          closedOver
            .map(_.tpe)
            .map(typeString)
            .map(unDollar)
            .mkString
        s"${clazz.getName}~~innerinit^~L${clazz.getName}^$argTypes"
      }
      for (param <- closedOver)
        result.addParameter(
          new STVarDecl(dd.pos, variableName(param), typeForType(param.tpe)))
      result.setBody {
        val body = new STBlock(dd.pos)
        body.add {
          val assign = new STAssignmentStmt(dd.pos)
          assign.setLeft(
            new STFieldAccess(
              dd.pos,
              new STVarAccess(dd.pos, "this~"),
              "outer~this",
              new STType.STClassType(dd.pos, clazz.getName)))
          assign.setRight(new STVarAccess(dd.pos, "outer~this"))
          assign
        }
        for (param <- closedOver)
          body.add {
            val assign = new STAssignmentStmt(dd.pos)
            assign.setLeft(
              new STFieldAccess(
                dd.pos,
                new STVarAccess(dd.pos, "this~"),
                variableName(param),
                new STType.STClassType(dd.pos, clazz.getName)))
            assign.setRight(new STVarAccess(dd.pos, variableName(param)))
            assign
          }
        body
      }
      result
    }
    clazz
  }

  /// while

  def translateWhile(
      tree: Tree,
      condition: Tree,
      blockPos: Position,
      statements: List[Tree],
      context: Context): Unit = {
    val result = new STWhileStmt(tree.pos)
    result.setPredicate(translateExpression(condition, context))
    val whileBody = new STBlock(blockPos)
    val innerContext = context.copy(body = whileBody)
    for (s <- statements)
      translateStatement(s, innerContext)
    result.setBody(whileBody)
    context.body.add(result)
  }

  def translateDoWhile(
      tree: Tree,
      condition: Tree,
      blockPos: Position,
      statements: List[Tree],
      context: Context): Unit = {
    val result = new STWhileStmt(tree.pos)
    result.setPredicate(translateExpression(condition, context))
    val label = nextLabel("__doWhile____L__")
    context.body.add(new STGoto(tree.pos, label))
    val whileBody = new STBlock(blockPos)
    whileBody.setFoldable(true)
    whileBody.setLabel(label)
    val innerContext = context.copy(body = whileBody)
    for (s <- statements)
      translateStatement(s, innerContext)
    result.setBody(whileBody)
    context.body.add(result)
  }

  // emitting a label/goto pair (matching the tree shape)
  // seems like an equally valid choice, but we choose instead to
  // emit an actual while loop.  the result is the same regardless
  // of whether it's "while" or "do while".
  def translateWhileTrue(
      tree: Tree,
      blockPos: Position,
      statements: List[Tree],
      context: Context): Unit = {
    val result = new STWhileStmt(tree.pos)
    result.setPredicate(STLiteralExp.create(tree.pos, true))
    val whileBody = new STBlock(blockPos)
    val innerContext = context.copy(body = whileBody)
    for (s <- statements)
      translateStatement(s, innerContext)
    result.setBody(whileBody)
    context.body.add(result)
  }

  /// array creation

  def translateArrayCreation(
      context: Context,
      pos: Position,
      exprs: Seq[Tree],
      resultType: Type,
      eltType: Type): STExpression = {
    val (access, decl) = useTemporary(typeForType(resultType), pos)
    context.body.add(decl)
    context.body.add {
      val assign = new STAssignmentStmt(pos)
      assign.setLeft(access)
      assign.setRight {
        val rhs = new STAllocation(pos)
        rhs.addIndex(STLiteralExp.create(pos, exprs.size))
        rhs.setType(new STType.STArrayType(pos, typeForType(eltType, ref = false)))
        rhs
      }
      assign
    }
    for ((expr, index) <- exprs.zipWithIndex) {
      val assign = new STAssignmentStmt(pos)
      val itemAccess = new STArrayAccess(pos)
      itemAccess.setBase(new STDereference(pos, access))
      itemAccess.setIndex(STLiteralExp.create(pos, index))
      assign.setLeft(itemAccess)
      assign.setRight(translateExpression(expr, context))
      context.body.add(assign)
    }
    access
  }

  /// class literals

  def translateClassLiteral(pos: Position, symbol: Symbol): STExpression = {
    val call = new STFunCall(pos)
    call.setVirtual()
    call.setName("~classliteral")
    call.addArgument(
      new STTypeCast(pos, typeForSymbol(symbol, ref = true), STLiteralExp.create(pos))
    ) // :null:
    new STTypeCast(
      pos,
      new STType.STPointerType(pos, new STType.STClassType("java.lang.Class")),
      call)
  }

  /// equality & inequality

  def translateEqOp(
      context: Context,
      pos: Position,
      tpe: Type,
      select: Tree,
      lhs: Tree,
      rhs: Tree,
      negate: Boolean): STExpression = {
    seen(definitions.Object_equals)
    val (lhsAccess, lhsDecl) = useTemporary(typeForType(lhs.tpe), pos)
    context.body.add(lhsDecl)
    context.body.add {
      val assign = new STAssignmentStmt(pos)
      assign.setLeft(lhsAccess)
      assign.setRight(translateExpression(lhs, context))
      assign
    }
    val (rhsAccess, rhsDecl) = useTemporary(typeForType(rhs.tpe), pos)
    context.body.add(rhsDecl)
    context.body.add {
      val assign = new STAssignmentStmt(pos)
      assign.setLeft(rhsAccess)
      assign.setRight(translateExpression(rhs, context))
      assign
    }
    val (resultAccess, resultDecl) =
      useTemporary(typeForType(tpe), pos)
    context.body.add(resultDecl)
    context.body.add {
      val ifElse = new STIfElse(pos)
      ifElse.setPredicate(
        new STOpExp(pos, NSTOperators.Equal, lhsAccess, STLiteralExp.create(pos))
      ) // :null:
      ifElse.setIfBody {
        val block = new STBlock(pos)
        val assign = new STAssignmentStmt(pos)
        assign.setLeft(resultAccess)
        assign.setRight(
          new STOpExp(pos, NSTOperators.Equal, rhsAccess, STLiteralExp.create(pos))
        ) // :null:
        block.add(assign)
        block
      }
      ifElse.setElseBody {
        val block = new STBlock(pos)
        val assign = new STAssignmentStmt(pos)
        assign.setLeft(resultAccess)
        assign.setRight {
          val call = new STFunCall(pos)
          call.setVirtual()
          call.setName(methodName(definitions.Object_equals))
          call.addArgument(lhsAccess)
          call.addArgument(rhsAccess)
          call
        }
        block.add(assign)
        block
      }
      ifElse
    }
    if (negate)
      new STOpExp(pos, NSTOperators.Not, resultAccess)
    else
      resultAccess
  }

  /// unique names for things

  private val caseLabelSuffixNumbers = Iterator.from(1)
  def nextCaseLabelSuffix(): Int = caseLabelSuffixNumbers.next()

  def caseLabel(context: Context, name: String): String =
    s"__${name}_${context.caseLabelSuffix}"

  /// temporaries

  val temporaries = Iterator.from(0).map(n => s"~t$n")
  def useTemporary(tpe: STType, pos: Position): (STVarAccess, STVarDecl) = {
    val temp = temporaries.next()
    val access = new STVarAccess(pos, temp)
    val decl = new STVarDecl(pos, temp, tpe)
    (access, decl)
  }

  /// throwing up our hands

  def unknown(context: Context, tree: Tree, more: Option[String] = None): STExpression = {
    log(s"unknown code ${tree.summaryString} at ${tree.pos}:")
    log(s"  $tree")
    log(s"  tree: ${showRaw(tree)}")
    for (msg <- more)
      log(s"additional info: $msg")
    context.body.add(STLiteralExp.create(tree.pos, s"???: ${showRaw(tree)}"))
    new STTypeCast(tree.pos, typeForType(tree.tpe), STLiteralExp.create(tree.pos)) // :null:
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy