.scala-fortify_2.11.11.1.1.4.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 - 2025 Weber Informatics LLC | Privacy Policy