.scala-fortify_3.3.4.1.1.4.source-code.Translator.scala Maven / Gradle / Ivy
/*
* 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
package plugin
import scala.language.implicitConversions
import com.fortify.frontend.nst
import nst.*
import nodes.*
import dotty.tools.dotc
import dotc.ast.tpd
import dotc.core.Contexts.Context
import dotc.core.Constants
import dotc.core.Flags.*
import dotc.core.StdNames.*
import dotc.util.SourcePosition
import dotc.core.Symbols.NoSymbol
import dotc.core.Types.{Type, TypeRef, ClassInfo, JavaArrayType}
class Translator(
val showSourceInfo: Boolean = true,
log: String => Unit = println,
)(using ctx: Context)
extends TranslatorBase
with TranslatorHelpers
with Closure:
def trace(s: =>String): Unit = ()
type SourceFile = dotc.util.SourceFile
type Tree = tpd.Tree
def translateAll(tree: Tree): List[STClassDecl] =
trace(s"compilation unit: ${tree.sourcePos}")
(new tpd.TreeAccumulator[List[tpd.TypeDef]]:
def apply(tds: List[tpd.TypeDef], tree: Tree)(using Context): List[tpd.TypeDef] =
tree match
// REAL
// case PackageDef(_, stats) =>
// stats.foreach(recurse)
case td @ tpd.TypeDef(_, rhs) =>
apply(td :: tds, rhs)
case _ =>
foldOver(tds, tree)
)
.apply(Nil, tree)
// it's maddening that the order seems to be nondeterministic and we must sort.
// I may need to deal with this in some better way eventually?
.sortBy(_.symbol.fullName)
.flatMap(translate)
def translate(classDef: tpd.TypeDef): List[STClassDecl] =
trace(s"ClassDef: ${classDef.symbol}")
val clazz = toClassDecl(classDef.symbol)
if classDef.symbol.is(ModuleClass) then
clazz.addModifiers(NSTModifiers.Synthetic)
val clazzes = collection.mutable.ListBuffer[STClassDecl](clazz)
if classDef.symbol.is(ModuleClass) && classDef.symbol.originalOwner.is(PackageClass) then
addModuleInit(classDef, clazz)
for mainClazz <- synthesizeMain(classDef) do
clazzes += mainClazz
val tpl = classDef.rhs.asInstanceOf[tpd.Template]
val bodyAndConstructor =
if classDef.symbol.is(Trait)
then tpl.body
else tpl.body :+ tpl.constr
bodyAndConstructor.foreach:
case dd: tpd.DefDef =>
if shouldTranslate(dd) then
clazz.addFunction(translate(dd, classDef.sourcePos))
case vd: tpd.ValDef =>
clazz.addField(toFieldDecl(vd.symbol))
case x =>
import dotty.tools.dotc.core.Decorators.i
log(i"???: $x ${x.getClass}")
()
// 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.
var lambdaKeys = Set[Symbol]() // guard against accidental infinite loop (issue 478)
while lambdaTypes.nonEmpty && lambdaKeys != lambdaTypes.keys do
lambdaKeys = lambdaTypes.keys.toSet
classDef.rhs.asInstanceOf[tpd.Template].body.foreach:
case dd: tpd.DefDef if lambdaTypes.isDefinedAt(dd.symbol) =>
if dd.symbol.is(Bridge) then
import tpd._
dd.rhs match
case Block(List(Apply(sel @ Select(_, _), _)), _)
if lambdaTypes.isDefinedAt(dd.symbol) =>
lambdaTypes(sel.symbol) = lambdaTypes(dd.symbol)
case Apply(sel @ Select(_, _), _)
if lambdaTypes.isDefinedAt(dd.symbol) =>
lambdaTypes(sel.symbol) = lambdaTypes(dd.symbol)
case _ =>
log(s"unrecognized lambda bridge at ${dd.sourcePos}: ${dd.rhs}")
else
clazzes += translateLambda(dd)
lambdaTypes.remove(dd.symbol)
case _ =>
for key <- lambdaTypes.keys do
log(s"couldn't find lambda for DefDef $key (at ${key.sourcePos})")
clazzes.toList
def translate(dd: tpd.DefDef, sourceInfo: SourceInfo): STFunDecl =
trace(s"DefDef: ${dd.symbol}")
resetLocalNames()
val params = paramNames(dd)
val fun = toFunDecl(dd.symbol, paramNames(dd), dd.paramss.flatten.map(_.name), dd.paramss.flatten.map(_.sourcePos), dd.symbol.info.paramInfoss.flatten)
if !dd.symbol.is(Deferred) then
// 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.)
for param <- dd.paramss.flatten.map(_.symbol) do
uniquifyVariable(param)
val context = TranslationContext(dd.symbol, new STBlock(sourceInfo))
if fun.getReturnType == VoidType then
translateStatement(dd.rhs, context)
else
val expr = translateExpression(dd.rhs, context)
context.emit:
val ret = new STReturnStmt(expr.getSourceInfo)
ret.setExpression(expr)
ret
fun.setBody(context.body)
end if
fun
/// 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.is(JavaDefined) && symbol.is(ModuleClass)
then symbol.companionClass
else symbol)
()
// when we see the lambda body, we need to know what the target type
// was (e.g. scala.Function1), and how many of the params are actually
// the "environment" (closed-over symbols), but we only have that information
// at the definition 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 and the environment size
val lambdaTypes = collection.mutable.Map.empty[Symbol, (Type, Int)]
// 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 pattern match
// * matchLabel is how we keep track of how to jump to the end
// of a pattern match
case class TranslationContext(
symbol: Symbol,
body: STBlock,
matchResult: Option[STVarAccess] = None,
matchLabel: Symbol = NoSymbol,
):
def emit(node: STNode): Unit =
trace(s"emitting ${node.getClass.getSimpleName}: $node")
body.add(node)
def translateStatement(tree: Tree, context: TranslationContext): Unit =
import tpd._
// println(tree)
tree match
case EmptyTree =>
trace("statement: empty tree (eliding)")
case Literal(value) if value.tag == Constants.UnitTag =>
trace("statement: literal Unit (eliding)")
case tree @ ValDef(_, _, _) =>
trace("statement: local val")
val name = variableName(tree.symbol, uniquify = true)
val decl = new STVarDecl(tree.sourcePos, name, typeForType(tree.symbol.info))
context.emit(decl)
val assign = new STAssignmentStmt(tree.sourcePos)
assign.setLeft(new STVarAccess(tree.sourcePos, name))
assign.setRight(translateExpression(tree.rhs, context))
context.emit(assign)
case Assign(id @ Ident(_), rhs) if !id.symbol.owner.isClass =>
trace("statement: assign to local val")
val assign = new STAssignmentStmt(tree.sourcePos)
assign.setLeft(
new STVarAccess(tree.sourcePos, variableName(id.symbol, uniquify = true)))
assign.setRight(translateExpression(rhs, context))
context.emit(assign)
case Assign(sel @ Select(th @ This(id @ Ident(_)), _), rhs) =>
trace("statement: assign to field (kind 1 of 3)")
seen(sel.symbol)
val assign = new STAssignmentStmt(tree.sourcePos)
assign.setLeft(
new STFieldAccess(
tree.sourcePos,
translateExpression(th, context),
variableName(sel.symbol),
typeForType(th.tpe, ref = false)))
assign.setRight(translateExpression(rhs, context))
context.emit(assign)
case Assign(sel @ Select(id @ Ident(_), _), rhs) =>
trace("statement: assign to field (kind 2 of 3)")
seen(sel.symbol)
val assign = new STAssignmentStmt(tree.sourcePos)
assign.setLeft(
new STFieldAccess(
tree.sourcePos,
translateExpression(id, context),
variableName(sel.symbol),
typeForType(id.tpe, ref = false)))
assign.setRight(translateExpression(rhs, context))
context.emit(assign)
case Assign(id @ Ident(_), rhs) =>
trace("statement: assign to field (kind 3 of 3)")
seen(id.symbol)
val assign = new STAssignmentStmt(tree.sourcePos)
assign.setLeft(
new STFieldAccess(
tree.sourcePos,
new STVarAccess(id.sourcePos, "this~"),
variableName(id.symbol),
typeForType(id.symbol.owner.info, ref = false)))
assign.setRight(translateExpression(rhs, context))
context.emit(assign)
case If(condTree, thenTree, EmptyTree) =>
trace("statement: if (one-legged)")
val statement = new STIfElse(tree.sourcePos)
statement.setPredicate(translateExpression(condTree, context))
val result = new STBlock(thenTree.sourcePos)
translateStatement(thenTree, context.copy(body = result))
statement.setIfBody(result)
context.emit(statement)
case If(condTree, thenTree, elseTree) =>
trace("statement: if + else")
val result = new STIfElse(tree.sourcePos)
result.setPredicate(translateExpression(condTree, context))
val thenBlock = new STBlock(thenTree.sourcePos)
translateStatement(thenTree, context.copy(body = thenBlock))
result.setIfBody(thenBlock)
elseTree match
case Literal(Constants.Constant(_: Unit)) =>
// omit
case _ =>
val elseBlock = new STBlock(elseTree.sourcePos)
translateStatement(elseTree, context.copy(body = elseBlock))
result.setElseBody(elseBlock)
context.emit(result)
case WhileDo(condition, body) =>
trace("statement: while")
// condition can be empty tree in tail recursion encoding
val generatedCondition =
if condition == EmptyTree
then STLiteralExp.create(tree.sourcePos, true) // :true:
else translateExpression(condition, context)
val result = new STWhileStmt(tree.sourcePos)
result.setPredicate(generatedCondition)
val whileBody = new STBlock(body.sourcePos)
val innerContext = context.copy(body = whileBody)
translateStatement(body, innerContext)
result.setBody(whileBody)
context.emit(result)
// array update
case Apply(select @ Select(qualifier, nme.primitive.arrayUpdate), List(index, rhs))
if isArrayType(qualifier.tpe) =>
trace("statement: array update")
val loc = translateExpression(qualifier, context)
val deref = new STDereference(qualifier.sourcePos, loc)
val access = new STArrayAccess(tree.sourcePos)
access.setBase(deref)
access.setIndex(translateExpression(index, context))
val result = new STAssignmentStmt(tree.sourcePos)
result.setLeft(access)
result.setRight(translateExpression(rhs, context))
context.emit(result)
case Apply(TypeApply(Select(obj, nme.synchronized_), _), List(body)) =>
trace("statement: synchronized")
context.emit:
val call = new STFunCall(tree.sourcePos)
call.setVirtual()
call.setName("__synchronize")
call.addArgument(translateExpression(obj, context))
call
context.emit:
val block = new STBlock(body.sourcePos)
block.setFoldable(false)
block.setLabel(nextLabel("__synchronized____L__"))
translateStatement(body, context.copy(body = block))
block
case Try(block, catches, finalizer) =>
trace("statement: try")
// 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.emit:
val body = new STBlock(block.sourcePos)
body.setFoldable(false)
body.setLabel(tryLabel)
translateStatement(block, context.copy(body = body))
body.add(new STGoto(block.sourcePos, finallyLabel))
body
def translateCatch(cd: CaseDef): STBlock =
(cd: @unchecked) match
case CaseDef(bind @ Bind(_, rhs), _, body) =>
generateCatch(cd.sourcePos, variableName(bind.symbol, uniquify = true), rhs.tpe, body)
case CaseDef(Typed(Ident(nme.WILDCARD), rhs), EmptyTree, body) =>
generateCatch(cd.sourcePos, temporaries.next(), rhs.tpe, body)
case CaseDef(Ident(nme.WILDCARD), EmptyTree, body) =>
generateCatch(cd.sourcePos, temporaries.next(), defn.ThrowableType, body)
def generateCatch(info: SourceInfo, name: String, tpe: Type, body: Tree): STBlock =
val result = new STBlock(body.sourcePos)
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.emit(_))
context.emit:
val finalBlock = new STBlock(
if finalizer == EmptyTree
then finalizer.sourcePos
else tree.sourcePos)
finalBlock.setFoldable(false)
finalBlock.setLabel(finallyLabel)
if finalizer != EmptyTree then
translateStatement(finalizer, context.copy(body = finalBlock))
finalBlock
case Apply(Ident(nme.throw_), List(expr)) =>
trace("statement: throw")
context.emit(
new STThrow(tree.sourcePos, translateExpression(expr, context)))
case Labeled(bind @ Bind(name, _), expr) =>
trace("statement: general pattern match")
translateStatement(expr,
context.copy(matchLabel = bind.symbol))
context.emit:
val block = new STBlock(tree.sourcePos)
block.setFoldable(true)
block.setLabel(bind.symbol.name.toString)
block
case Return(expr, target) if context.matchLabel eq target.symbol =>
trace("statement: return from pattern match")
context.matchResult match
case Some(access) =>
val assign = new STAssignmentStmt(expr.sourcePos)
assign.setLeft(access)
assign.setRight(translateExpression(expr, context))
context.emit(assign)
case None =>
translateStatement(expr, context)
context.emit(
new STGoto(tree.sourcePos, context.matchLabel.name.toString))
case Return(Literal(Constants.Constant(())), _)
if context.symbol.info.resultType.typeSymbol == defn.UnitClass =>
trace("statement: return unit")
context.emit(new STReturnStmt(tree.sourcePos))
case Return(expr, target) =>
trace("statement: return")
if expr.tpe.typeSymbol == defn.UnitClass then
translateStatement(expr, context)
context.emit(new STReturnStmt(tree.sourcePos))
else
val result = new STReturnStmt(tree.sourcePos)
result.setExpression(translateExpression(expr, context))
context.emit(result)
case block @ Block(stats, expr) => // if !isPatternMatch(block) =>
trace("statement: block")
(stats :+ expr).foreach(translateStatement(_, context))
case Match(expr, cases) if cases.nonEmpty =>
trace("statement: switch-style pattern match")
val (inputAccess, inputDecl) =
useTemporary(typeForType(expr.tpe), expr.sourcePos)
context.emit(inputDecl)
val assign = new STAssignmentStmt(expr.sourcePos)
assign.setLeft(inputAccess)
assign.setRight(translateExpression(expr, context))
context.emit(assign)
def ifElseChain(cases: List[Tree], context: TranslationContext): Unit =
cases match
case List(CaseDef(Ident(nme.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 expr =>
List(expr)
val ifelse = new STIfElse(firstCase.sourcePos)
val compare =
combineAlternatives(alternatives, inputAccess, context)
ifelse.setPredicate(compare)
val thenBlock = new STBlock(rhs.sourcePos)
translateStatement(rhs, context.copy(body = thenBlock))
ifelse.setIfBody(thenBlock)
val elseBlock = new STBlock(tree.sourcePos)
ifElseChain(cases.tail, context.copy(body = elseBlock))
ifelse.setElseBody(elseBlock)
context.emit(ifelse)
case _ =>
context.emit(unknown(context, tree))
ifElseChain(cases, context)
case _ =>
trace("statement: treating as expression")
val expr = translateExpression(tree, context)
// drop dangling references to (usually) temporaries
if !expr.isInstanceOf[STVarAccess] then
context.emit(expr)
// helper for switch-style pattern matches
private def combineAlternatives(
alternatives: List[Tree],
access: STVarAccess,
context: TranslationContext): STExpression =
alternatives
.map: alt =>
new STOpExp(alt.sourcePos, NSTOperators.Equal, access, translateExpression(alt, context))
.reduceLeft: (sofar, next) =>
new STOpExp(sofar.getSourceInfo, NSTOperators.Or, sofar, next)
def translateExpression(tree: Tree, context: TranslationContext): STExpression =
import tpd._
// println(tree)
val result = tree match
case Apply(select @ Select(qualifier, nme.And), List(arg))
if isBooleanType(qualifier.tpe) =>
trace("expression: &&")
val (access, decl) =
useTemporary(typeForType(qualifier.tpe), qualifier.sourcePos)
context.emit(decl)
context.emit:
val statement = new STIfElse(tree.sourcePos)
statement.setPredicate(translateExpression(qualifier, context))
statement.setIfBody {
val block = new STBlock(arg.sourcePos)
val assign = new STAssignmentStmt(arg.sourcePos)
assign.setLeft(access)
assign.setRight(
translateExpression(arg, context.copy(body = block)))
block.add(assign)
block
}
statement.setElseBody {
val block = new STBlock(qualifier.sourcePos)
val assign = new STAssignmentStmt(qualifier.sourcePos)
assign.setLeft(access)
assign.setRight(STLiteralExp.create(qualifier.sourcePos, false))
block.add(assign)
block
}
statement
access
case Apply(select @ Select(qualifier, nme.Or), List(arg))
if isBooleanType(qualifier.tpe) =>
trace("expression: ||")
val (access, decl) =
useTemporary(typeForType(qualifier.tpe), qualifier.sourcePos)
context.emit(decl)
context.emit:
val statement = new STIfElse(tree.sourcePos)
statement.setPredicate(translateExpression(qualifier, context))
statement.setIfBody:
val block = new STBlock(qualifier.sourcePos)
val assign = new STAssignmentStmt(qualifier.sourcePos)
assign.setLeft(access)
assign.setRight(STLiteralExp.create(qualifier.sourcePos, true))
block.add(assign)
block
statement.setElseBody:
val block = new STBlock(arg.sourcePos)
val assign = new STAssignmentStmt(arg.sourcePos)
assign.setLeft(access)
assign.setRight(
translateExpression(arg, context.copy(body = block)))
block.add(assign)
block
statement
access
// if + else
case If(condTree, thenTree, elseTree) =>
trace("expression: if + else")
val statement = new STIfElse(tree.sourcePos)
statement.setPredicate(translateExpression(condTree, context))
val (access, decl) = useTemporary(typeForType(tree.tpe), tree.sourcePos)
context.emit(decl)
def subBody(subtree: Tree): STBlock =
val result = new STBlock(subtree.sourcePos)
val assign = new STAssignmentStmt(subtree.sourcePos)
assign.setLeft(access)
assign.setRight(
translateExpression(subtree, context.copy(body = result)))
result.add(assign)
result
statement.setIfBody(subBody(thenTree))
statement.setElseBody(subBody(elseTree))
context.emit(statement)
access
case WhileDo(condition, body) =>
// note: occurs in expression position (with empty condition)
// in lazy val encoding
trace("expression: while")
val result = new STWhileStmt(tree.sourcePos)
result.setPredicate(
condition match
case EmptyTree =>
STLiteralExp.create(tree.sourcePos, true) // :true:
case _ =>
translateExpression(condition, context)
)
val whileBody = new STBlock(body.sourcePos)
val innerContext = context.copy(body = whileBody)
translateStatement(body, innerContext)
result.setBody(whileBody)
context.emit(result)
STLiteralExp.create(tree.sourcePos) // :null:
case Select(qualifier, selector) if tree.symbol.is(JavaStatic) =>
trace("expression: static field access")
seen(tree.symbol)
new STStaticFieldAccess(
tree.sourcePos,
selector.toString,
typeForSymbol(qualifier.tpe.typeSymbol.companionClass, ref = false))
case Labeled(bind @ Bind(name, _), expr) =>
trace("expression: general pattern match")
val (access, decl) = useTemporary(typeForType(tree.tpe), tree.sourcePos)
context.emit(decl)
val block = new STBlock(tree.sourcePos)
val blockContext = context.copy(
body = block,
matchResult = Some(access),
matchLabel = bind.symbol)
translateStatement(expr, blockContext)
context.emit(block)
context.emit:
val block = new STBlock(tree.sourcePos)
block.setFoldable(true)
block.setLabel(blockContext.matchLabel.name.toString)
block
access
case Return(Literal(Constants.Constant(())), target) if context.matchLabel eq target.symbol =>
trace("expression: return (jump) to label, no value")
context.emit(
new STGoto(tree.sourcePos, context.matchLabel.name.toString))
STLiteralExp.create(tree.sourcePos) // :null:
case Return(expr, target) if context.matchLabel eq target.symbol =>
trace("expression: return value from pattern match")
val assign = new STAssignmentStmt(expr.sourcePos)
if context.matchResult.isDefined then
assign.setLeft(context.matchResult.get)
assign.setRight(translateExpression(expr, context))
context.emit(assign)
context.emit(
new STGoto(tree.sourcePos, context.matchLabel.name.toString))
STLiteralExp.create(tree.sourcePos) // :null:
case Return(expr, target) =>
trace("expression: return")
translateStatement(tree, context)
STLiteralExp.create(tree.sourcePos) // :null:
// lambda
case Closure(env, fun @ Select(This(_), _), functionalInterface) =>
trace("expression: lambda")
lambdaTypes(fun.symbol) = (tree.tpe, env.size)
val lamName =
s"""${className(context.symbol.owner)}${fun.symbol.name}"""
.replaceFirst("\\$adapted(\\$\\w+)$", "$1")
val lamType = new STType.STClassType(tree.sourcePos, lamName)
val lamRefType = new STType.STPointerType(tree.sourcePos, lamType)
val (access, decl) = useTemporary(lamRefType, tree.sourcePos)
context.emit(decl)
val assign = new STAssignmentStmt(tree.sourcePos)
assign.setLeft(access)
assign.setRight:
val rhs = new STAllocation(tree.sourcePos)
rhs.setType(lamType)
rhs
context.emit(assign)
context.emit:
val call = new STFunCall(tree.sourcePos)
call.setName(
s"$lamName~~innerinit^~L$lamName^" +
fun.symbol.info.paramInfoss.flatten.take(env.size)
.map(typeString)
.map(unDollar)
.mkString
)
call.addArgument(access)
call.addArgument(new STVarAccess(tree.sourcePos, "this~"))
// pass in the values we closed over
for arg <- env do
call.addArgument(translateExpression(arg, context))
call.setVirtual()
call
access
case This(_) if tree.tpe.typeSymbol == context.symbol.owner =>
trace("expression: this")
new STVarAccess(tree.sourcePos, "this~")
case Select(_, selector) if tree.symbol.is(Module) =>
trace("expression: module access (kind 1)")
seen(tree.symbol)
new STStaticFieldAccess(
tree.sourcePos,
"MODULE$",
new STType.STClassType(tree.sourcePos, className(tree.symbol) + "$"))
case This(_) if tree.symbol.is(ModuleClass) =>
trace("expression: module access (kind 2)")
seen(tree.symbol)
new STStaticFieldAccess(
tree.sourcePos,
"MODULE$",
new STType.STClassType(tree.sourcePos, className(tree.symbol)))
case Ident(_) if tree.symbol.is(Module) =>
trace("expression: module access (kind 3)")
seen(tree.symbol)
new STStaticFieldAccess(
tree.sourcePos,
"MODULE$",
new STType.STClassType(tree.sourcePos, className(tree.symbol) + "$"))
case Select(sel @ This(id @ Ident(_)), _) =>
trace("expression: field access (kind 1)")
seen(id.symbol)
val result = new STFieldAccess(
tree.sourcePos,
new STDereference(id.sourcePos, translateExpression(sel, context)),
variableName(tree.symbol),
typeForType(sel.tpe, ref = false))
result
case Select(qualifier, _) =>
trace("expression: field access (kind 2)")
seen(qualifier.symbol)
val result = new STFieldAccess(
tree.sourcePos,
new STDereference(qualifier.sourcePos, translateExpression(qualifier, context)),
variableName(tree.symbol),
typeForType(qualifier.tpe, ref = false))
result
case Ident(_) if tree.symbol.owner.isClass =>
trace("expression: field access (kind 3)")
seen(tree.symbol)
val result = new STFieldAccess(
tree.sourcePos,
new STVarAccess(tree.sourcePos, "this~"),
variableName(tree.symbol),
typeForType(tree.symbol.owner.info, ref = false))
result
// super call
case Apply(sel @ Select(sup @ Super(_, _), _), args) =>
trace("expression: super call")
val call = new STFunCall(tree.sourcePos)
call.setVirtual()
call.setName(methodName(sel.symbol))
call.addArgument(new STVarAccess(tree.sourcePos, "this~"))
for arg <- args do
call.addArgument(translateExpression(arg, context))
call
// synchronized
case Apply(TypeApply(Select(obj, nme.synchronized_), _), List(body)) =>
trace("expression: synchronized")
val (access, decl) = useTemporary(typeForType(tree.tpe), tree.sourcePos)
context.emit(decl)
context.emit:
val call = new STFunCall(tree.sourcePos)
call.setVirtual()
call.setName("__synchronize")
call.addArgument(translateExpression(obj, context))
call
context.emit:
val block = new STBlock(body.sourcePos)
block.setFoldable(false)
block.setLabel(nextLabel("__synchronized____L__"))
block.add {
val assign = new STAssignmentStmt(tree.sourcePos)
assign.setLeft(access)
assign.setRight(
translateExpression(body, context.copy(body = block)))
assign
}
block
access
case jsl: JavaSeqLiteral =>
trace("expression: array creation (kind 1)")
val JavaArrayType(eltType) = jsl.tpe: @unchecked
translateArrayCreation(context, tree.sourcePos, jsl.elems, jsl.tpe, eltType)
case Apply(Select(Ident(Arrays), NewArrayMethod), args :+ (jsl: JavaSeqLiteral)) =>
trace("expression: array creation (kind 2)")
// `Array.ofDim` calls are rewritten to this by the arrayConstructors transform
val call = new STFunCall(tree.sourcePos)
call.setVirtual()
call.setName(methodName(defn.newArrayMethod))
seen(defn.newArrayMethod)
for arg <- args do
call.addArgument(translateExpression(arg, context))
// `newArray` wants an `Array` of dimensions
val JavaArrayType(eltType) = jsl.tpe: @unchecked
call.addArgument(
translateArrayCreation(context, jsl.sourcePos,jsl.elems, jsl.tpe, eltType))
call
case TypeApply(Select(obj, nme.asInstanceOf_), List(typeArg)) =>
trace("expression: asInstanceOf")
new STTypeCast(tree.sourcePos, typeForType(typeArg.tpe), translateExpression(obj, context))
case TypeApply(Select(obj, nme.isInstanceOf_), List(typeArg)) =>
trace("expression: isInstanceOf")
val call = new STFunCall(tree.sourcePos)
call.setVirtual()
call.setName("__instanceof")
call.addArgument(translateExpression(obj, context))
call.addArgument(
new STTypeCast(obj.sourcePos, typeForType(typeArg.tpe), STLiteralExp.create(tree.sourcePos))
) // :null:
call
case Ident(nme.UNIT) =>
trace("expression: literal Unit")
new STStaticFieldAccess(tree.sourcePos, "UNIT", typeForType(tree.tpe, ref = false))
case Ident(_) =>
trace("expression: access local variable")
new STVarAccess(tree.sourcePos, variableName(tree.symbol, uniquify = true))
case Typed(expr, _) =>
trace("expression: Typed")
// not completely sure we don't sometimes need to insert a
// typecast here -- something to keep an eye out for
translateExpression(expr, context)
/// operators
case Apply(select @ Select(qualifier, nme.PLUS), List(arg))
if qualifier.tpe <:< defn.StringType =>
trace("expression: string concatenation")
new STOpExp(
tree.sourcePos,
NSTOperators.Add,
translateExpression(qualifier, context),
if arg.tpe <:< defn.StringType
then translateExpression(arg, context)
else
val call = new STFunCall(arg.sourcePos)
call.setName(methodName(defn.Any_toString))
call.addArgument(translateExpression(arg, context))
call
)
case Apply(select @ Select(qualifier, name), List(arg))
if arithmeticOps.isDefinedAt(name) && (isNumericType(qualifier.tpe) || isBooleanType(
qualifier.tpe)) =>
trace("expression: binary operator") // numeric or boolean
new STOpExp(
tree.sourcePos,
arithmeticOps(name),
translateExpression(qualifier, context),
translateExpression(arg, context))
case Apply(Select(lhs, nme.Equals | NmeEq), List(Literal(Constants.Constant(null)))) =>
trace("expression: equal, null on right")
new STOpExp(
tree.sourcePos,
NSTOperators.Equal,
translateExpression(lhs, context),
STLiteralExp.create(tree.sourcePos)
) // :null:
case Apply(Select(lhs, nme.NotEquals | NmeNe), List(Literal(Constants.Constant(null)))) =>
trace("expression: not equal, null on right")
new STOpExp(
tree.sourcePos,
NSTOperators.NotEqual,
translateExpression(lhs, context),
STLiteralExp.create(tree.sourcePos)
) // :null:
case Apply(Select(Apply(Select(lhs, nme.Equals | NmeEq), List(Literal(Constants.Constant(null)))), nme.UNARY_!),List()) =>
trace("expression: equal, null on right")
// for some reason in Scala 3, unlike 2, != has been rewritten to ! plus ==
// by the time we see it. regardless I've left the previous case in
// just in case it doesn't always happen that way?
new STOpExp(
tree.sourcePos,
NSTOperators.NotEqual,
translateExpression(lhs, context),
STLiteralExp.create(tree.sourcePos)
) // :null:
case Apply(Select(Literal(Constants.Constant(null)), nme.Equals | NmeEq), List(rhs)) =>
trace("expression: equal, null on left")
new STOpExp(
tree.sourcePos,
NSTOperators.Equal,
STLiteralExp.create(tree.sourcePos), // :null:
translateExpression(rhs, context))
case Apply(Select(Literal(Constants.Constant(null)), nme.NotEquals | NmeNe), List(rhs)) =>
trace("expression: not equal, null on left")
new STOpExp(
tree.sourcePos,
NSTOperators.NotEqual,
STLiteralExp.create(tree.sourcePos), // :null:
translateExpression(rhs, context))
case Apply(Select(Apply(Select(Literal(Constants.Constant(null)), nme.Equals | NmeEq),List(rhs)), nme.UNARY_!), List()) =>
trace("expression: equal")
// for some reason in Scala 3, unlike 2, != has been rewritten to ! plus ==
// by the time we see it. regardless I've left the previous case in
// just in case it doesn't always happen that way?
new STOpExp(
tree.sourcePos,
NSTOperators.NotEqual,
STLiteralExp.create(tree.sourcePos), // :null:
translateExpression(rhs, context))
case Apply(sel @ Select(lhs @ Literal(Constants.Constant(_: String)), nme.Equals | nme.NotEquals), List(rhs)) =>
trace("expression: reference equal (or not), string literal on left")
val call = new STFunCall(tree.sourcePos)
call.setVirtual()
call.setName(methodName(defn.Any_equals))
seen(defn.Any_equals)
call.addArgument(translateExpression(lhs, context))
call.addArgument(translateExpression(rhs, context))
if sel.name == nme.Equals
then call
else new STOpExp(tree.sourcePos, NSTOperators.Not, call)
case Apply(select @ Select(lhs, nme.Equals), List(rhs)) =>
trace("expression: reference equal")
translateEqOp(context, tree.sourcePos, tree.tpe, select, lhs, rhs, negate = false)
case Apply(select @ Select(lhs, nme.NotEquals), List(rhs)) =>
trace("expression: reference not equal")
translateEqOp(context, tree.sourcePos, tree.tpe, select, lhs, rhs, negate = true)
case Apply(select @ Select(qualifier, name), List(arg))
if referenceOps.isDefinedAt(name) && isReferenceType(qualifier.tpe) =>
trace("expression: reference operator")
new STOpExp(
tree.sourcePos,
referenceOps(name),
translateExpression(qualifier, context),
translateExpression(arg, context))
case Apply(select @ Select(qualifier, nme.UNARY_!), List()) if isBooleanType(qualifier.tpe) =>
trace("expression: not")
new STOpExp(tree.sourcePos, NSTOperators.Not, translateExpression(qualifier, context))
case Apply(select @ Select(qualifier, nme.primitive.arrayLength), List())
if isArrayType(qualifier.tpe) =>
trace("expression: array length")
new STOpExp(tree.sourcePos, NSTOperators.Arraylen, translateExpression(qualifier, context))
case Apply(select @ Select(qualifier, nme.primitive.arrayApply), List(arg))
if isArrayType(qualifier.tpe) =>
trace("expression: array access")
val loc = translateExpression(qualifier, context)
val deref = new STDereference(qualifier.sourcePos, loc)
val result = new STArrayAccess(tree.sourcePos)
result.setBase(deref)
result.setIndex(translateExpression(arg, context))
result
case Apply(sel @ Select(n @ New(tpe), nme.CONSTRUCTOR), args) =>
trace("expression: constructor call")
seen(sel.symbol)
val (access, decl) = useTemporary(typeForType(tpe.tpe), n.sourcePos)
context.emit(decl)
val assign = new STAssignmentStmt(tree.sourcePos)
assign.setLeft(access)
assign.setRight:
val rhs = new STAllocation(tree.sourcePos)
rhs.setType(typeForSymbol(tpe.symbol, ref = false))
rhs
context.emit(assign)
context.emit:
val call = new STFunCall(tree.sourcePos)
call.setName(methodName(sel.symbol))
call.addArgument(access)
for arg <- args do
call.addArgument(translateExpression(arg, context))
call
access
/// literals
case Literal(Constants.Constant(plugin.Constants.KnownUnknown)) =>
// for testing handling of unknown trees
trace("expression: special testing constant")
unknown(context, tree, Some("additional infos for testing"))
case Literal(Constants.Constant(s: String)) =>
trace("expression: constant String")
STLiteralExp.create(tree.sourcePos, s)
case Literal(Constants.Constant(i: Int)) =>
trace("expression: constant Int")
STLiteralExp.create(tree.sourcePos, i)
case Literal(Constants.Constant(l: Long)) =>
trace("expression: constant Long")
STLiteralExp.create(tree.sourcePos, l)
case Literal(Constants.Constant(c: Char)) =>
trace("expression: constant Char")
STLiteralExp.create(tree.sourcePos, c)
case Literal(Constants.Constant(b: Boolean)) =>
trace("expression: constant Boolean")
STLiteralExp.create(tree.sourcePos, b)
case Literal(Constants.Constant(d: Double)) =>
trace("expression: constant Double")
STLiteralExp.create(tree.sourcePos, d)
case Literal(Constants.Constant(f: Float)) =>
trace("expression: constant Float")
STLiteralExp.create(tree.sourcePos, f.toDouble)
case Literal(Constants.Constant(s: Short)) =>
trace("expression: constant Short")
new STTypeCast(tree.sourcePos, typeForType(tree.tpe), STLiteralExp.create(tree.sourcePos, s.toInt))
case Literal(Constants.Constant(b: Byte)) =>
trace("expression: constant Byte")
new STTypeCast(tree.sourcePos, typeForType(tree.tpe), STLiteralExp.create(tree.sourcePos, b.toInt))
case Literal(Constants.Constant(())) =>
trace("expression: literal Unit")
new STStaticFieldAccess(tree.sourcePos, "UNIT", typeForType(tree.tpe, ref = true))
case Literal(Constants.Constant(null)) =>
trace("expression: literal null")
STLiteralExp.create(tree.sourcePos) // :null:
case Literal(Constants.Constant(TypeRef(_, symbol: Symbol))) =>
trace("expression: class literal (TypeRef)")
translateClassLiteral(tree.sourcePos, symbol)
case Literal(Constants.Constant(ClassInfo(_, symbol, _, _, _))) =>
trace("expression: class literal (ClassInfo)")
translateClassLiteral(tree.sourcePos, symbol)
case Literal(Constants.Constant(JavaArrayType(elemType))) =>
trace("expression: class literal (JavaArrayType)")
translateClassLiteral(tree.sourcePos, elemType.typeSymbol)
// unknown literal
case Literal(Constants.Constant(x)) =>
trace("expression: unknown literal")
unknown(context, tree, Some(s"unknown literal type: ${x.getClass}"))
// try (expression position)
case Try(block, catches, finalizer) =>
val (access, decl) = useTemporary(typeForType(block.tpe), block.sourcePos)
context.emit(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.emit:
val body = new STBlock(block.sourcePos)
body.setFoldable(false)
body.setLabel(tryLabel)
val assign = new STAssignmentStmt(block.sourcePos)
assign.setLeft(access)
assign.setRight(
translateExpression(block, context.copy(body = body)))
body.add(assign)
body.add(new STGoto(block.sourcePos, finallyLabel))
body
def translateCatch(cd: CaseDef): STBlock =
(cd: @unchecked) match
case CaseDef(bind @ Bind(_, rhs), _, body) =>
generateCatch(cd.sourcePos, variableName(bind.symbol, uniquify = true), rhs.tpe, body)
case CaseDef(Typed(Ident(nme.WILDCARD), rhs), EmptyTree, body) =>
generateCatch(cd.sourcePos, temporaries.next(), rhs.tpe, body)
case CaseDef(Ident(nme.WILDCARD), EmptyTree, body) =>
generateCatch(cd.sourcePos, temporaries.next(), defn.ThrowableType, body)
def generateCatch(info: SourceInfo, name: String, tpe: Type, body: Tree): STBlock =
val result = new STBlock(body.sourcePos)
result.setLabel(nextLabel("__catch____L__"))
result.setFoldable(false)
result.add(new STVarDecl(info, name, typeForType(tpe)))
val assign = new STAssignmentStmt(block.sourcePos)
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.emit(_))
context.emit:
val finalBlock = new STBlock(
if finalizer == EmptyTree
then finalizer.sourcePos
else tree.sourcePos)
finalBlock.setFoldable(false)
finalBlock.setLabel(finallyLabel)
if finalizer != EmptyTree then
translateStatement(finalizer, context.copy(body = finalBlock))
finalBlock
access
case Apply(Ident(nme.throw_), List(expr)) =>
trace("expression: throw")
context.emit(
new STThrow(tree.sourcePos, translateExpression(expr, context)))
STLiteralExp.create(tree.sourcePos) // :null:
case Apply(select @ Select(qualifier, _), args) =>
trace("expression: method call (Select)")
seen(select.symbol)
val call = new STFunCall(tree.sourcePos)
call.setVirtual()
call.setName(methodName(select.symbol))
if qualifier.symbol.exists then
seen(qualifier.symbol)
if !select.symbol.is(JavaDefined) || select.symbol.isConstructor || !select.symbol.isStatic || ctx.platform.isMainMethod(select.symbol) then
call.addArgument(translateExpression(qualifier, context))
for arg <- args do
call.addArgument(translateExpression(arg, context))
call
case Apply(ident @ Ident(_), args) =>
// (Scala 3 specific)
trace("expression: method call (Ident)")
val select @ Select(qualifier, _) = tpd.desugarIdent(ident): @unchecked
seen(select.symbol)
val call = new STFunCall(tree.sourcePos)
call.setVirtual()
call.setName(methodName(ident.symbol))
if qualifier.symbol.exists then
seen(qualifier.symbol)
call.addArgument(translateExpression(qualifier, context))
for arg <- args do
call.addArgument(translateExpression(arg, context))
call
case block @ Block(stats, expr) =>
trace("expression: block")
stats.foreach(translateStatement(_, context))
translateExpression(expr, context)
case Match(expr, cases) if cases.nonEmpty =>
trace("expression: switch-style pattern match")
val (inputAccess, inputDecl) =
useTemporary(typeForType(expr.tpe), expr.sourcePos)
val (resultAccess, resultDecl) =
useTemporary(typeForType(tree.tpe), tree.sourcePos)
context.emit(inputDecl)
val assign = new STAssignmentStmt(expr.sourcePos)
assign.setLeft(inputAccess)
assign.setRight(translateExpression(expr, context))
context.emit(assign)
context.emit(resultDecl)
val newContext = context.copy(
matchLabel = cases
.collectFirst:
case CaseDef(Ident(nme.WILDCARD), EmptyTree, body @ Labeled(_, _)) =>
body.symbol
.getOrElse(NoSymbol))
def ifElseChain(cases: List[Tree]): STNode =
cases match
case List(
lastCase @ CaseDef(
Ident(nme.WILDCARD),
EmptyTree,
Labeled(Bind(_, _), rhs))) =>
val block = new STBlock(lastCase.sourcePos)
block.setFoldable(true)
block.setLabel(newContext.matchLabel.name.toString)
val assign = new STAssignmentStmt(lastCase.sourcePos)
assign.setLeft(resultAccess)
assign.setRight(
translateExpression(rhs, newContext.copy(body = block)))
block.add(assign)
block
case List(CaseDef(Ident(nme.WILDCARD), EmptyTree, rhs)) =>
val assign = new STAssignmentStmt(cases.head.sourcePos)
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 expr =>
List(expr)
val ifelse = new STIfElse(firstCase.sourcePos)
val compare =
combineAlternatives(alternatives, inputAccess, newContext)
ifelse.setPredicate(compare)
val thenBlock = new STBlock(rhs.sourcePos)
val assign = new STAssignmentStmt(rhs.sourcePos)
assign.setLeft(resultAccess)
assign.setRight(
translateExpression(rhs, newContext.copy(body = thenBlock)))
thenBlock.add(assign)
ifelse.setIfBody(thenBlock)
val elseBlock = new STBlock(tree.sourcePos)
elseBlock.add(ifElseChain(cases.tail))
ifelse.setElseBody(elseBlock)
ifelse
case _ =>
unknown(context, tree)
context.emit(ifElseChain(cases))
resultAccess
case x =>
trace("expression: unknown")
unknown(context, x)
// println(s"$tree => \n $result")
result
end translateExpression
/// lambdas
def translateLambda(dd: tpd.DefDef): STClassDecl =
trace(s"lambda: ${dd.symbol}")
val name = s"""${className(dd.symbol.owner)}${dd.symbol.name}"""
val (lambdaType, envSize) = lambdaTypes(dd.symbol)
val (closedOver, params) = paramNames(dd).splitAt(envSize)
val clazz =
val result = new STClassDecl(dd.symbol.sourcePos)
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.sourcePos)
field.setName("outer~this")
field.setType(typeForType(dd.symbol.owner.info))
field
for param <- closedOver do
result.addField:
val field = new STFieldDecl(dd.sourcePos)
field.setName(variableName(param))
field.setType(typeForType(param.info))
field
result
// lambda body
clazz.addFunction:
val result = translate(dd, dd.sourcePos)
// 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 do
// expected: ~t~x@1 :=: [LambdaVar$anonfun$incrementer$1] (:*: lambda~this) :.: ~t~x@1;
result.addFront:
val assign = new STAssignmentStmt(dd.sourcePos)
assign.setLeft(new STVarAccess(dd.sourcePos, variableName(param)))
assign.setRight(
new STFieldAccess(
dd.sourcePos,
new STVarAccess(dd.sourcePos, "lambda~this"),
variableName(param),
new STType.STClassType(dd.sourcePos, clazz.getName)))
assign
result.addFront(
new STVarDecl(dd.sourcePos, variableName(param), typeForType(param.info)))
result.addFront:
val assign = new STAssignmentStmt(dd.sourcePos)
assign.setLeft(new STVarAccess(dd.sourcePos, "this~"))
assign.setRight(
new STFieldAccess(
dd.sourcePos,
new STVarAccess(dd.sourcePos, "lambda~this"),
"outer~this",
new STType.STClassType(dd.sourcePos, clazz.getName)))
assign
result.addFront(
new STVarDecl(dd.sourcePos, "this~", typeForType(dd.symbol.owner.info)))
// drop closed-over parameters, leave real parameters
while result.getArglist.size > params.size do
result.popArg()
result.pushArg(
new STVarDecl(
dd.sourcePos,
"lambda~this",
new STType.STPointerType(
dd.sourcePos,
new STType.STClassType(dd.sourcePos, clazz.getName))))
val (samType, _) = lambdaTypes(dd.symbol)
val samMethod =
val Seq(samMethodDenot) = samType.possibleSamMethods
samMethodDenot.symbol
result.setName(
s"$name~~${unDollar(samMethod.name.toString)}~L$name^${params.map(p => typeString(p.info)).map(unDollar).mkString}")
result.setSimpleName(toSimpleName(samMethod))
result.addOverride(methodName(samMethod))
result
// constructor
clazz.addFunction:
val result = new STFunDecl(dd.sourcePos)
result.setReturnType(VoidType)
result.setModifiers(NSTModifiers.Public)
result.setSimpleName("innerinit^")
result.addParameter(
new STVarDecl(
dd.sourcePos,
"this~",
new STType.STPointerType(
dd.sourcePos,
new STType.STClassType(dd.sourcePos, clazz.getName))))
result.addParameter(
new STVarDecl(dd.sourcePos, "outer~this", typeForType(dd.symbol.owner.info)))
result.setName:
val argTypes =
closedOver
.map(_.info)
.map(typeString)
.map(unDollar)
.mkString
s"${clazz.getName}~~innerinit^~L${clazz.getName}^$argTypes"
for param <- closedOver do
result.addParameter(
new STVarDecl(dd.sourcePos, variableName(param), typeForType(param.info)))
result.setBody:
val body = new STBlock(dd.sourcePos)
body.add:
val assign = new STAssignmentStmt(dd.sourcePos)
assign.setLeft(
new STFieldAccess(
dd.sourcePos,
new STVarAccess(dd.sourcePos, "this~"),
"outer~this",
new STType.STClassType(dd.sourcePos, clazz.getName)))
assign.setRight(new STVarAccess(dd.sourcePos, "outer~this"))
assign
for param <- closedOver do
body.add:
val assign = new STAssignmentStmt(dd.sourcePos)
assign.setLeft(
new STFieldAccess(
dd.sourcePos,
new STVarAccess(dd.sourcePos, "this~"),
variableName(param),
new STType.STClassType(dd.sourcePos, clazz.getName)))
assign.setRight(new STVarAccess(dd.sourcePos, variableName(param)))
assign
body
result
clazz
/// array creation
def translateArrayCreation(
context: TranslationContext,
pos: SourcePosition,
exprs: Seq[Tree],
resultType: Type,
eltType: Type): STExpression =
val (access, decl) = useTemporary(typeForType(resultType), pos)
context.emit(decl)
context.emit:
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, false)))
rhs
assign
for (expr, index) <- exprs.zipWithIndex do
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.emit(assign)
access
/// class literals
def translateClassLiteral(pos: SourcePosition, 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
// `l == r` becomes `if (l.eq(null)) r.eq(null) else l.equals(r)`
def translateEqOp(
context: TranslationContext,
pos: SourcePosition,
tpe: Type,
select: Tree,
lhs: Tree,
rhs: Tree,
negate: Boolean): STExpression =
val (lhsAccess, lhsDecl) = useTemporary(typeForType(lhs.tpe), pos)
context.emit(lhsDecl)
context.emit:
val assign = new STAssignmentStmt(pos)
assign.setLeft(lhsAccess)
assign.setRight(translateExpression(lhs, context))
assign
val (rhsAccess, rhsDecl) = useTemporary(typeForType(rhs.tpe), pos)
context.emit(rhsDecl)
context.emit:
val assign = new STAssignmentStmt(pos)
assign.setLeft(rhsAccess)
assign.setRight(translateExpression(rhs, context))
assign
val (resultAccess, resultDecl) =
useTemporary(typeForType(tpe), pos)
context.emit(resultDecl)
context.emit:
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(defn.Any_equals))
call.addArgument(lhsAccess)
call.addArgument(rhsAccess)
call
}
block.add(assign)
block
ifElse
if negate
then STOpExp(pos, NSTOperators.Not, resultAccess)
else resultAccess
/// temporaries
val temporaries = Iterator.from(0).map(n => s"~t$n")
def useTemporary(tpe: STType, pos: SourcePosition): (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: TranslationContext, tree: Tree, more: Option[String] = None): STExpression =
log(s"unknown code at ${tree.sourcePos}: $tree")
for msg <- more do
log(s"additional info: $msg")
context.emit(STLiteralExp.create(tree.sourcePos, s"???: $tree")) // s"???: ${showRaw(tree)}"))
new STTypeCast(tree.sourcePos, typeForType(tree.tpe), STLiteralExp.create(tree.sourcePos)) // :null:
/// these aren't stable identifiers, sigh. so make them so.
val NmeEq = nme.eq
val NmeNe = nme.ne
val Arrays = defn.DottyArraysModule.name
val NewArrayMethod = defn.newArrayMethod.name
end Translator
class TracingTranslator(
showSourceInfo: Boolean = true,
log: String => Unit = println,
)(using ctx: Context) extends Translator(showSourceInfo, log):
override def trace(s: =>String): Unit =
println(" " * indent + s)
var indent = 0
val width = 90
private def escapedChar(ch: Char): String = (ch: @annotation.switch) match
case '\b' => "\\b"
case '\t' => "\\t"
case '\n' => "\\n"
case '\f' => "\\f"
case '\r' => "\\r"
case '"' => "\\\""
case '\'' => "\\\'"
case '\\' => "\\\\"
case _ => if ch.isControl then f"${"\\"}u${ch.toInt}%04x" else String.valueOf(ch).nn
private def escapedString(str: String): String =
str.flatMap(escapedChar)
def recurse[T1, T2](msg: String, body: => T1, input: T2): T1 =
val inputMessage =
val inputString = escapedString(input.toString)
if inputString.length > width
then s"${inputString.take(width - 3)}..."
else inputString
trace(s"$msg==> $inputMessage")
try
indent += 1
val result = body
if result != () then
trace(s"<== ${escapedString(result.toString)}")
result
finally indent -= 1
override def translate(classDef: tpd.TypeDef): List[STClassDecl] =
recurse("ClassDef", super.translate(classDef), classDef)
override def translate(dd: tpd.DefDef, sourceInfo: SourceInfo): STFunDecl =
recurse("DefDef", super.translate(dd, sourceInfo), dd)
override def translateStatement(tree: Tree, context: TranslationContext): Unit =
recurse("", super.translateStatement(tree, context), tree)
override def translateExpression(tree: Tree, context: TranslationContext): STExpression =
recurse("", super.translateExpression(tree, context), tree)
override def translateLambda(dd: tpd.DefDef): STClassDecl =
recurse("lambda", super.translateLambda(dd), dd)
© 2015 - 2024 Weber Informatics LLC | Privacy Policy