Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Various cleanups in the backend.

all.clean test-opt: all pass.
  • Loading branch information...
commit 0735fd9c94af1a5e8d9ffd37d1b8abfaca191aa6 1 parent 0b22588
Paul Phillips paulp authored
12 src/compiler/scala/tools/nsc/ast/Trees.scala
View
@@ -59,6 +59,11 @@ trait Trees extends reflect.generic.Trees { self: SymbolTable =>
/** Apply `f' to each subtree */
def foreach(f: Tree => Unit) { new ForeachTreeTraverser(f).traverse(tree) }
+
+ /** If 'pf' is defined for a given subtree, call super.traverse(pf(tree)),
+ * otherwise super.traverse(tree).
+ */
+ def foreachWithHook(pf: PartialFunction[Tree, Tree]) { new ForeachWithHookTreeTraverser(pf).traverse(tree) }
/** Find all subtrees matching predicate `p' */
def filter(f: Tree => Boolean): List[Tree] = {
@@ -1003,6 +1008,13 @@ trait Trees extends reflect.generic.Trees { self: SymbolTable =>
posAssigner.traverse(tree)
tree
}
+
+ class ForeachWithHookTreeTraverser(pf: PartialFunction[Tree, Tree]) extends Traverser {
+ override def traverse(tree: Tree) {
+ val t = if (pf isDefinedAt tree) pf(tree) else tree
+ super.traverse(t)
+ }
+ }
class ForeachTreeTraverser(f: Tree => Unit) extends Traverser {
override def traverse(t: Tree) {
221 src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala
View
@@ -8,11 +8,9 @@ package scala.tools.nsc
package backend
package icode
-//import scala.tools.nsc.ast._
-import scala.collection.mutable.{Map, Set}
-import scala.collection.mutable.LinkedHashSet
-import scala.tools.nsc.util.{Position,NoPosition}
-import scala.tools.nsc.backend.icode.analysis.ProgramPoint
+import scala.collection.{ mutable, immutable }
+import util.{ Position, NoPosition }
+import backend.icode.analysis.ProgramPoint
trait BasicBlocks {
self: ICodes =>
@@ -79,7 +77,7 @@ trait BasicBlocks {
/** Local variables that are in scope at entry of this basic block. Used
* for debugging information.
*/
- var varsInScope: Set[Local] = new LinkedHashSet()
+ var varsInScope: mutable.Set[Local] = new mutable.LinkedHashSet()
/** ICode instructions, used as temporary storage while emitting code.
* Once closed is called, only the `instrs' array should be used.
@@ -90,11 +88,8 @@ trait BasicBlocks {
private var instrs: Array[Instruction] = _
- override def toList: List[Instruction] = {
- if (closed)
- instrs.toList
- else instructionList
- }
+ override def toList: List[Instruction] =
+ if (closed) instrs.toList else instructionList
/** Return an iterator over the instructions in this basic block. */
def iterator: Iterator[Instruction] =
@@ -108,7 +103,7 @@ trait BasicBlocks {
def fromList(is: List[Instruction]) {
code.touched = true
- instrs = toInstructionArray(is)
+ instrs = is.toArray
closed = true
}
@@ -117,17 +112,9 @@ trait BasicBlocks {
*/
def indexOf(inst: Instruction): Int = {
assert(closed)
- var i = 0
- while (i < instrs.length) {
- if (instrs(i) eq inst) return i
- i += 1
- }
- -1
+ instrs indexWhere (_ eq inst)
}
- /** Compute an hashCode for the block */
-// override def hashCode() = label;
-
/** Apply a function to all the instructions of the block. */
override def foreach[U](f: Instruction => U) = {
if (!closed) {
@@ -138,26 +125,7 @@ trait BasicBlocks {
}
/** The number of instructions in this basic block so far. */
- def length: Int =
- if (closed) instrs.length else instructionList.length
-
- /** Return the index of the instruction which produced the value
- * consumed by the given instruction.
- */
- def findDef(pos: Int): Option[Int] = {
- assert(closed)
- var i = pos
- var d = 0
- while (i > 0) {
- i -= 1
- val prod = instrs(i).produced
- if (prod > 0 && d == 0)
- return Some(i)
- d += (instrs(i).consumed - instrs(i).produced)
- }
- None
- }
-
+ def length = if (closed) instrs.length else instructionList.length
/** Return the n-th instruction. */
def apply(n: Int): Instruction =
@@ -184,22 +152,18 @@ trait BasicBlocks {
*/
def replaceInstruction(oldInstr: Instruction, newInstr: Instruction): Boolean = {
assert(closed, "Instructions can be replaced only after the basic block is closed")
-
- var i = 0
- var changed = false
- while (i < instrs.length && !changed) {
- if (instrs(i) eq oldInstr) {
- newInstr.setPos(oldInstr.pos)
- instrs(i) = newInstr
- changed = true
+
+ indexOf(oldInstr) match {
+ case -1 => false
+ case idx =>
+ newInstr setPos oldInstr.pos
+ instrs(idx) = newInstr
code.touched = true
- }
- i += 1
+ true
}
- changed
}
- /** Replaces <code>iold</code> with <code>is</code>. It does not update
+ /** Replaces <code>oldInstr</code> with <code>is</code>. It does not update
* the position field in the newly inserted instructions, so it behaves
* differently than the one-instruction versions of this function.
*
@@ -207,51 +171,23 @@ trait BasicBlocks {
* @param is ..
* @return ..
*/
- def replaceInstruction(iold: Instruction, is: List[Instruction]): Boolean = {
+ def replaceInstruction(oldInstr: Instruction, is: List[Instruction]): Boolean = {
assert(closed, "Instructions can be replaced only after the basic block is closed")
-
- var i = 0
- var changed = false
-
- while (i < instrs.length && (instrs(i) ne iold))
- i += 1
-
- if (i < instrs.length) {
- val newInstrs = new Array[Instruction](instrs.length + is.length - 1);
- changed = true
- code.touched = true
-
- Array.copy(instrs, 0, newInstrs, 0, i)
- var j = i
- for (x <- is) {
- newInstrs(j) = x
- j += 1
- }
- if (i + 1 < instrs.length)
- Array.copy(instrs, i + 1, newInstrs, j, instrs.length - i - 1)
- instrs = newInstrs;
+
+ indexOf(oldInstr) match {
+ case -1 => false
+ case idx =>
+ instrs = instrs.patch(idx, is, 1)
+ code.touched = true
+ true
}
-
- changed
}
/** Insert instructions in 'is' immediately after index 'idx'. */
def insertAfter(idx: Int, is: List[Instruction]) {
assert(closed, "Instructions can be replaced only after the basic block is closed")
-
- var i = idx + 1
- if (i < instrs.length) {
- val newInstrs = new Array[Instruction](instrs.length + is.length);
- Array.copy(instrs, 0, newInstrs, 0, i)
- var j = i
- for (x <- is) {
- newInstrs(j) = x
- j += 1
- }
- if (i + 1 < instrs.length)
- Array.copy(instrs, i + 1, newInstrs, j, instrs.length - i)
- instrs = newInstrs;
- }
+
+ instrs = instrs.patch(idx + 1, is, 0)
code.touched = true
}
@@ -261,18 +197,7 @@ trait BasicBlocks {
*/
def removeInstructionsAt(positions: Int*) {
assert(closed)
- val removed = positions.toList
- val newInstrs = new Array[Instruction](instrs.length - positions.length)
- var i = 0
- var j = 0
- while (i < instrs.length) {
- if (!removed.contains(i)) {
- newInstrs(j) = instrs(i)
- j += 1
- }
- i += 1
- }
- instrs = newInstrs
+ instrs = instrs.indices.toArray filterNot positions.toSet map instrs
code.touched = true
}
@@ -292,33 +217,14 @@ trait BasicBlocks {
*
* @param map ...
*/
- def subst(map: Map[Instruction, Instruction]) {
- if (!closed) substOnList(map) else {
- var i = 0
- while (i < instrs.length) {
- map get instrs(i) match {
- case Some(instr) =>
- val changed = replaceInstruction(i, instr)
- code.touched |= changed
- case None => ()
- }
- i += 1
+ def subst(map: Map[Instruction, Instruction]): Unit =
+ if (!closed)
+ instructionList = instructionList map (x => map.getOrElse(x, x))
+ else
+ instrs.zipWithIndex collect {
+ case (oldInstr, i) if map contains oldInstr =>
+ code.touched |= replaceInstruction(i, map(oldInstr))
}
- }
- }
-
- private def substOnList(map: Map[Instruction, Instruction]) {
- def subst(l: List[Instruction]): List[Instruction] = l match {
- case Nil => Nil
- case x :: xs =>
- map.get(x) match {
- case Some(newInstr) => newInstr :: subst(xs)
- case None => x :: subst(xs)
- }
- }
-
- instructionList = subst(instructionList)
- }
////////////////////// Emit //////////////////////
@@ -327,10 +233,8 @@ trait BasicBlocks {
* using the same source position as the last emitted instruction
*/
def emit(instr: Instruction) {
- if (!instructionList.isEmpty)
- emit(instr, instructionList.head.pos)
- else
- emit(instr, NoPosition)
+ val pos = if (instructionList.isEmpty) NoPosition else instructionList.head.pos
+ emit(instr, pos)
}
/** Emitting does not set touched to true. During code generation this is a hotspot and
@@ -374,7 +278,7 @@ trait BasicBlocks {
closed = true
setFlag(DIRTYSUCCS)
instructionList = instructionList.reverse
- instrs = toInstructionArray(instructionList)
+ instrs = instructionList.toArray
}
def open {
@@ -406,34 +310,20 @@ trait BasicBlocks {
/** Return the last instruction of this basic block. */
def lastInstruction =
- if (closed)
- instrs(instrs.length - 1)
- else
- instructionList.head
+ if (closed) instrs.last
+ else instructionList.head
def firstInstruction =
- if (closed)
- instrs(0)
- else
- instructionList.last
-
- /** Convert the list to an array */
- private def toInstructionArray(l: List[Instruction]): Array[Instruction] = {
- var array = new Array[Instruction](l.length)
- var i: Int = 0
-
- l foreach (x => { array(i) = x; i += 1 })
- array
- }
+ if (closed) instrs(0)
+ else instructionList.last
/** Cached value of successors. Must be recomputed whenver a block in the current method is changed. */
private var succs: List[BasicBlock] = Nil
-
- def successors : List[BasicBlock] = {
- if (touched) {
- resetFlag(DIRTYSUCCS)
- succs = if (isEmpty) Nil else {
- var res = lastInstruction match {
+ private def updateSuccs() {
+ resetFlag(DIRTYSUCCS)
+ succs =
+ if (isEmpty) Nil else {
+ val last = lastInstruction match {
case JUMP(whereto) => List(whereto)
case CJUMP(success, failure, _, _) => failure :: success :: Nil
case CZJUMP(success, failure, _, _) => failure :: success :: Nil
@@ -447,15 +337,14 @@ trait BasicBlocks {
}
else Nil
}
- method.exh.foreach {
- e: ExceptionHandler =>
- if (e.covers(this)) res = e.startBlock :: res
- }
- val res1 = res ++ exceptionalSucc(this, res)
- res1
+
+ val res = (method.exh.reverse collect { case x if x covers this => x.startBlock }) ++ last
+ res ++ exceptionalSucc(this, res)
}
- }
-// println("reusing cached successors for " + this + " in method " + method)
+ }
+
+ def successors : List[BasicBlock] = {
+ if (touched) updateSuccs()
succs
}
@@ -496,7 +385,7 @@ trait BasicBlocks {
def predecessors: List[BasicBlock] = {
if (hasFlag(DIRTYPREDS)) {
resetFlag(DIRTYPREDS)
- preds = code.blocks.iterator.filter (_.successors.contains(this)).toList
+ preds = code.blocks filter (_.successors contains this) toList
}
preds
}
22 src/compiler/scala/tools/nsc/backend/icode/Checkers.scala
View
@@ -573,24 +573,16 @@ abstract class Checkers {
def error(msg: String) {
Console.println(method.toString() + " in block: " + basicBlock.label)
- printLastIntructions
+ printLastInstructions
Checkers.this.global.error("ICode checker: " + method + ": " + msg)
}
/** Prints the last 4 instructions. */
- def printLastIntructions {
- var printed = 0
- var buf: List[Instruction] = Nil
-
- for (i <- basicBlock.reverse) {
- if (i == instruction || (printed > 0 && printed < 3)) {
- buf = i :: buf
- printed += 1
- }
- }
- buf foreach Console.println
- Console.println("at: " + (buf.head.pos))
+ def printLastInstructions {
+ val buf = basicBlock.reverse dropWhile (_ != instruction) take 4 reverse;
+ buf foreach (Console println _)
+ Console.println("at: " + buf.head.pos)
}
def error(msg: String, stack: TypeStack) {
@@ -602,8 +594,6 @@ abstract class Checkers {
/** Return true if <code>k1</code> is a subtype of any of the following
* types.
*/
- def isOneOf(k1: TypeKind, kinds: TypeKind*) =
- kinds.exists( k => k1 <:< k)
-
+ def isOneOf(k1: TypeKind, kinds: TypeKind*) = kinds exists (k1 <:< _)
}
}
196 src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
View
@@ -26,10 +26,10 @@ abstract class GenICode extends SubComponent {
import icodes._
import icodes.opcodes._
import definitions.{
- ArrayClass, ObjectClass, ThrowableClass, StringClass, NothingClass, NullClass,
+ ArrayClass, ObjectClass, ThrowableClass, StringClass, NothingClass, NullClass, AnyRefClass,
Object_equals, Object_isInstanceOf, Object_asInstanceOf, ScalaRunTimeModule,
BoxedNumberClass, BoxedCharacterClass,
- getMember
+ getMember, getPrimitiveCompanion
}
import scalaPrimitives.{
isArrayOp, isComparisonOp, isLogicalOp,
@@ -727,10 +727,10 @@ abstract class GenICode extends SubComponent {
} else { // normal method call
if (settings.debug.value)
log("Gen CALL_METHOD with sym: " + sym + " isStaticSymbol: " + sym.isStaticMember);
- var invokeStyle =
+ val invokeStyle =
if (sym.isStaticMember)
Static(false)
- else if (sym.hasFlag(Flags.PRIVATE) || sym.isClassConstructor)
+ else if (sym.isPrivate || sym.isClassConstructor)
Static(true)
else
Dynamic
@@ -987,58 +987,44 @@ abstract class GenICode extends SubComponent {
* Generate code that loads args into label parameters.
*/
private def genLoadLabelArguments(args: List[Tree], label: Label, ctx: Context): Context = {
- if (settings.debug.value)
+ if (settings.debug.value) {
assert(args.length == label.params.length,
"Wrong number of arguments in call to label " + label.symbol)
+ }
+
var ctx1 = ctx
- var arg = args
- var param = label.params
- val stores: ListBuffer[Instruction] = new ListBuffer
- // store arguments in reverse order on the stack
- while (arg != Nil) {
- arg.head match {
- case This(_) if param.head.name == nme.THIS =>
- //println("skipping trivial argument for " + param.head)
- () // skip trivial arguments
- case Ident(_) if arg.head.symbol == param.head =>
- //println("skipping trivial argument for " + param.head)
- () // skip trivial arguments
- case _ =>
- val Some(l) = ctx.method.lookupLocal(param.head)
- ctx1 = genLoad(arg.head, ctx1, l.kind)
- if (param.head.name == nme.THIS)
- STORE_THIS(toTypeKind(ctx1.clazz.symbol.tpe)).setPos(arg.head.pos) +=: stores
- else {
- STORE_LOCAL(l).setPos(arg.head.pos) +=: stores
- }
- }
- arg = arg.tail
- param = param.tail
+ def isTrivial(kv: (Tree, Symbol)) = kv match {
+ case (This(_), p) if p.name == nme.THIS => true
+ case (arg @ Ident(_), p) if arg.symbol == p => true
+ case _ => false
}
-
- //println("stores: " + stores)
- ctx1.bb.emit(stores)
+
+ val stores = args zip label.params filterNot isTrivial map {
+ case (arg, param) =>
+ val local = ctx.method.lookupLocal(param).get
+ ctx1 = genLoad(arg, ctx1, local.kind)
+
+ val store =
+ if (param.name == nme.THIS) STORE_THIS(toTypeKind(ctx1.clazz.symbol.tpe))
+ else STORE_LOCAL(local)
+
+ store setPos arg.pos
+ }
+
+ // store arguments in reverse order on the stack
+ ctx1.bb.emit(stores.reverse)
ctx1
}
- private def genLoadArguments(args: List[Tree], tpes: List[Type], ctx: Context): Context = {
- var ctx1 = ctx
- var arg = args
- var tpe = tpes
- while (arg != Nil) {
- ctx1 = genLoad(arg.head, ctx1, toTypeKind(tpe.head))
- arg = arg.tail
- tpe = tpe.tail
+ private def genLoadArguments(args: List[Tree], tpes: List[Type], ctx: Context): Context =
+ (args zip tpes).foldLeft(ctx) {
+ case (res, (arg, tpe)) =>
+ genLoad(arg, res, toTypeKind(tpe))
}
- ctx1
- }
private def genLoadModule(ctx: Context, sym: Symbol, pos: Position) {
- if (definitions.primitiveCompanions(sym))
- ctx.bb.emit(LOAD_MODULE(definitions.getModule("scala.runtime." + sym.name)), pos)
- else
- ctx.bb.emit(LOAD_MODULE(sym), pos)
+ ctx.bb.emit(LOAD_MODULE(getPrimitiveCompanion(sym) getOrElse sym), pos)
}
def genConversion(from: TypeKind, to: TypeKind, ctx: Context, cast: Boolean) = {
@@ -1228,22 +1214,18 @@ abstract class GenICode extends SubComponent {
* TODO: restrict the scanning to smaller subtrees than the whole method.
* It is sufficient to scan the trees of the innermost enclosing block.
*/
- private def scanForLabels(tree: Tree, ctx: Context): Unit =
- new Traverser() {
- override def traverse(tree: Tree): Unit = tree match {
-
- case LabelDef(name, params, rhs) =>
- if (!ctx.labels.contains(tree.symbol)) {
- ctx.labels += (tree.symbol -> (new Label(tree.symbol) setParams(params map (_.symbol))));
- ctx.method.addLocals(params map (p => new Local(p.symbol, toTypeKind(p.symbol.info), false)));
- }
- super.traverse(rhs)
+ //
+ private def scanForLabels(tree: Tree, ctx: Context): Unit = tree foreachWithHook {
+ case t @ LabelDef(_, params, rhs) =>
+ ctx.labels.getOrElseUpdate(t.symbol, {
+ val locals = params map (p => new Local(p.symbol, toTypeKind(p.symbol.info), false))
+ ctx.method addLocals locals
+
+ new Label(t.symbol) setParams (params map (_.symbol))
+ })
+ rhs
+ }
- case _ =>
- super.traverse(tree)
- }
- } traverse(tree);
-
/**
* Generate code for conditional expressions. The two basic blocks
* represent the continuation in case of success/failure of the
@@ -1354,7 +1336,7 @@ abstract class GenICode extends SubComponent {
def getTempLocal: Local = ctx.method.lookupLocal(eqEqTempName) match {
case Some(local) => local
case None =>
- val local = ctx.makeLocal(l.pos, definitions.AnyRefClass.typeConstructor, eqEqTempName.toString)
+ val local = ctx.makeLocal(l.pos, AnyRefClass.typeConstructor, eqEqTempName.toString)
//assert(!l.pos.source.isEmpty, "bad position, unit = "+unit+", tree = "+l+", pos = "+l.pos.source)
// Note - I commented these out because they were crashing the test case in ticket #2426
// (and I have also had to comment them out at various times while working on equality.)
@@ -1486,15 +1468,9 @@ abstract class GenICode extends SubComponent {
}
/** Does this tree have a try-catch block? */
- def mayCleanStack(tree: Tree): Boolean = {
- var hasTry = false
- new Traverser() {
- override def traverse(t: Tree) = t match {
- case Try(_, _, _) => hasTry = true
- case _ => super.traverse(t)
- }
- }.traverse(tree);
- hasTry
+ def mayCleanStack(tree: Tree): Boolean = tree exists {
+ case Try(_, _, _) => true
+ case _ => false
}
/**
@@ -1589,13 +1565,8 @@ abstract class GenICode extends SubComponent {
log("Prune fixpoint reached in " + n + " iterations.");
}
- def getMaxType(ts: List[Type]): TypeKind = {
- def maxType(a: TypeKind, b: TypeKind): TypeKind =
- a maxType b;
-
- val kinds = ts map toTypeKind
- kinds reduceLeft maxType
- }
+ def getMaxType(ts: List[Type]): TypeKind =
+ ts map toTypeKind reduceLeft (_ maxType _)
def isLoopHeaderLabel(name: Name): Boolean =
name.startsWith("while$") || name.startsWith("doWhile$")
@@ -1615,7 +1586,7 @@ abstract class GenICode extends SubComponent {
* All LabelDefs are entered into the context label map, since it makes no sense
* to delay it any more: they will be used at some point.
*/
- class DuplicateLabels(boundLabels: collection.Set[Symbol]) extends Transformer {
+ class DuplicateLabels(boundLabels: Set[Symbol]) extends Transformer {
val labels: mutable.Map[Symbol, Symbol] = new HashMap
var method: Symbol = _
var ctx: Context = _
@@ -1627,31 +1598,26 @@ abstract class GenICode extends SubComponent {
}
override def transform(t: Tree): Tree = {
+ val sym = t.symbol
+ def getLabel(pos: Position, name: Name) =
+ labels.getOrElseUpdate(sym,
+ method.newLabel(sym.pos, unit.fresh.newName(pos, name.toString)) setInfo sym.tpe
+ )
+
t match {
- case t @ Apply(fun, args) if (t.symbol.isLabel && !boundLabels(t.symbol)) =>
- if (!labels.isDefinedAt(t.symbol)) {
- val oldLabel = t.symbol
- val sym = method.newLabel(oldLabel.pos, unit.fresh.newName(oldLabel.pos, oldLabel.name.toString))
- sym.setInfo(oldLabel.tpe)
- labels(oldLabel) = sym
- }
- val tree = Apply(global.gen.mkAttributedRef(labels(t.symbol)), transformTrees(args)).setPos(t.pos)
+ case t @ Apply(_, args) if sym.isLabel && !boundLabels(sym) =>
+ val newSym = getLabel(sym.pos, sym.name)
+ val tree = Apply(global.gen.mkAttributedRef(newSym), transformTrees(args)) setPos t.pos
tree.tpe = t.tpe
tree
case t @ LabelDef(name, params, rhs) =>
- val name1 = unit.fresh.newName(t.pos, name.toString)
- if (!labels.isDefinedAt(t.symbol)) {
- val oldLabel = t.symbol
- val sym = method.newLabel(oldLabel.pos, name1)
- sym.setInfo(oldLabel.tpe)
- labels(oldLabel) = sym
- }
- val tree = treeCopy.LabelDef(t, name1, params, transform(rhs))
- tree.symbol = labels(t.symbol)
+ val newSym = getLabel(t.pos, name)
+ val tree = treeCopy.LabelDef(t, newSym.name, params, transform(rhs))
+ tree.symbol = newSym
- ctx.labels += (tree.symbol -> (new Label(tree.symbol) setParams(params map (_.symbol))));
- ctx.method.addLocals(params map (p => new Local(p.symbol, toTypeKind(p.symbol.info), false)));
+ ctx.labels += (newSym -> (new Label(newSym) setParams (params map (_.symbol))))
+ ctx.method.addLocals(params map (p => new Local(p.symbol, toTypeKind(p.symbol.info), false)))
tree
@@ -1672,7 +1638,7 @@ abstract class GenICode extends SubComponent {
override def equals(other: Any) = f == other;
}
- def duplicateFinalizer(boundLabels: collection.Set[Symbol], targetCtx: Context, finalizer: Tree) = {
+ def duplicateFinalizer(boundLabels: Set[Symbol], targetCtx: Context, finalizer: Tree) = {
(new DuplicateLabels(boundLabels))(targetCtx, finalizer)
}
@@ -1806,10 +1772,9 @@ abstract class GenICode extends SubComponent {
/** Return a new context for a new basic block. */
def newBlock: Context = {
val block = method.code.newBlock
- handlers foreach (h => h addCoveredBlock block)
- currentExceptionHandlers foreach (h => h.addBlock(block))
- block.varsInScope = new HashSet()
- block.varsInScope ++= scope.varsInScope
+ handlers foreach (_ addCoveredBlock block)
+ currentExceptionHandlers foreach (_ addBlock block)
+ block.varsInScope = new HashSet() ++= scope.varsInScope
new Context(this) setBasicBlock block
}
@@ -1854,7 +1819,7 @@ abstract class GenICode extends SubComponent {
* exception handler.
*/
def enterHandler(exh: ExceptionHandler): Context = {
- currentExceptionHandlers = exh :: currentExceptionHandlers
+ currentExceptionHandlers ::= exh
val ctx = newBlock
exh.setStartBlock(ctx.bb)
ctx
@@ -1905,7 +1870,7 @@ abstract class GenICode extends SubComponent {
* } ))</code>
*/
def Try(body: Context => Context,
- handlers: List[(Symbol, TypeKind, (Context => Context))],
+ handlers: List[(Symbol, TypeKind, Context => Context)],
finalizer: Tree,
tree: Tree) = if (forMSIL) TryMsil(body, handlers, finalizer, tree) else {
@@ -1918,7 +1883,7 @@ abstract class GenICode extends SubComponent {
// we need to save bound labels before any code generation is performed on
// the current context (otherwise, any new labels in the finalizer that need to
// be duplicated would be incorrectly considered bound -- see #2850).
- val boundLabels: collection.Set[Symbol] = Set.empty ++ labels.keySet
+ val boundLabels: Set[Symbol] = Set.empty ++ labels.keySet
if (guardResult) {
tmp = this.makeLocal(tree.pos, tree.tpe, "tmp")
@@ -2086,15 +2051,8 @@ abstract class GenICode extends SubComponent {
* jumps to the given basic block.
*/
def patch(code: Code) {
- def substMap: mutable.Map[Instruction, Instruction] = {
- val map = new HashMap[Instruction, Instruction]()
-
- toPatch foreach (i => map += (i -> patch(i)))
- map
- }
-
- val map = substMap
- code.blocks foreach (_.subst(map))
+ val map = toPatch map (i => (i -> patch(i))) toMap;
+ code.blocks foreach (_ subst map)
}
/**
@@ -2177,17 +2135,13 @@ abstract class GenICode extends SubComponent {
class Scope(val outer: Scope) {
val locals: ListBuffer[Local] = new ListBuffer
- def add(l: Local) =
- locals += l
-
- def remove(l: Local) =
- locals -= l
+ def add(l: Local) = locals += l
+ def remove(l: Local) = locals -= l
/** Return all locals that are in scope. */
def varsInScope: Buffer[Local] = outer.varsInScope.clone() ++= locals
- override def toString() =
- outer.toString() + locals.mkString("[", ", ", "]")
+ override def toString() = locals.mkString(outer.toString + "[", ", ", "]")
}
object EmptyScope extends Scope(null) {
121 src/compiler/scala/tools/nsc/backend/icode/Members.scala
View
@@ -10,11 +10,9 @@ package icode
import java.io.PrintWriter
-import scala.collection.mutable.HashMap
-import scala.collection.mutable.{Set, HashSet, ListBuffer}
-import scala.{Symbol => scala_Symbol}
-
-import scala.tools.nsc.symtab.Flags
+import scala.collection.{ mutable, immutable }
+import mutable.{ HashMap, ListBuffer }
+import symtab.Flags.{ DEFERRED }
trait Members { self: ICodes =>
import global._
@@ -38,31 +36,32 @@ trait Members { self: ICodes =>
private var _touched = false
def touched = _touched
- def touched_=(b: Boolean): Unit = if (b) {
- blocks foreach (_.touched = true)
- _touched = true
- } else
- _touched = false
+ def touched_=(b: Boolean): Unit = {
+ if (b)
+ blocks foreach (_.touched = true)
+
+ _touched = b
+ }
// Constructor code
startBlock = newBlock
def removeBlock(b: BasicBlock) {
if (settings.debug.value) {
- assert(blocks.forall(p => !(p.successors.contains(b))),
- "Removing block that is still referenced in method code " + b + "preds: " + b.predecessors);
- if (b == startBlock)
- assert(b.successors.length == 1,
- "Removing start block with more than one successor.");
+ assert(blocks forall (p => !(p.successors contains b)),
+ "Removing block that is still referenced in method code " + b + "preds: " + b.predecessors
+ )
+ assert(b != startBlock || b.successors.length == 1,
+ "Removing start block with more than one successor."
+ )
}
if (b == startBlock)
- startBlock = b.successors.head;
- blocks -= b
+ startBlock = b.successors.head
+
+ blocks -= b
assert(!blocks.contains(b))
- for (handler <- method.exh if handler.covers(b))
- handler.covered -= b
-
+ method.exh filter (_ covers b) foreach (_.covered -= b)
touched = true
}
@@ -114,9 +113,9 @@ trait Members { self: ICodes =>
def lookupMethod(s: Name) = methods find (_.symbol.name == s)
/* determines whether or not this class contains a static ctor. */
- def containsStaticCtor: Boolean = methods.exists(_.isStaticCtor)
+ def containsStaticCtor: Boolean = methods exists (_.isStaticCtor)
/* returns this methods static ctor if it has one. */
- def lookupStaticCtor: Option[IMethod] = methods.find(_.isStaticCtor)
+ def lookupStaticCtor: Option[IMethod] = methods find (_.isStaticCtor)
}
/** Represent a field in ICode */
@@ -150,72 +149,61 @@ trait Members { self: ICodes =>
/** method parameters */
var params: List[Local] = Nil
+ def hasCode = code != null
def setCode(code: Code): IMethod = {
this.code = code;
this
}
def addLocal(l: Local): Local =
- locals find (l.==) match {
- case Some(loc) => loc
- case None =>
- locals = l :: locals;
- l
+ locals find (_ == l) getOrElse {
+ locals ::= l
+ l
}
- def addLocals(ls: List[Local]) {
- ls foreach addLocal
- }
- def addParam(p: Local) {
- if (!(params contains p)) {
- params = p :: params;
- locals = p :: locals;
+ def addParam(p: Local): Unit =
+ if (params contains p) ()
+ else {
+ params ::= p
+ locals ::= p
}
- }
-
- def addParams(as: List[Local]) {
- as foreach addParam
- }
- def lookupLocal(n: Name): Option[Local] =
- locals find ((l) => l.sym.name == n);
+ def addLocals(ls: List[Local]) = ls foreach addLocal
+ def addParams(as: List[Local]) = as foreach addParam
- def lookupLocal(sym: Symbol): Option[Local] =
- locals find ((l) => l.sym == sym);
+ def lookupLocal(n: Name): Option[Local] = locals find (_.sym.name == n)
+ def lookupLocal(sym: Symbol): Option[Local] = locals find (_.sym == sym)
- def addHandler(e: ExceptionHandler) {
- exh = e :: exh
- }
+ def addHandler(e: ExceptionHandler) = exh ::= e
/** Is this method deferred ('abstract' in Java sense) */
- def isDeferred = (
- symbol.hasFlag(Flags.DEFERRED) ||
- symbol.owner.hasFlag(Flags.INTERFACE) ||
- native
- );
+ /** XXX How is this distinct from symbol.isDeferred? */
+ def isDeferred = (symbol hasFlag DEFERRED) || symbol.owner.isInterface || native
def isStatic: Boolean = symbol.isStaticMember
/* determines whether or not this method is the class static constructor. */
+ /* XXX Why rawname? */
def isStaticCtor: Boolean = isStatic && symbol.rawname == nme.CONSTRUCTOR
override def toString() = symbol.fullName
import opcodes._
- def checkLocals: Unit = if (code ne null) {
- Console.println("[checking locals of " + this + "]")
- for (bb <- code.blocks; i <- bb) i match {
- case LOAD_LOCAL(l) =>
- if (!this.locals.contains(l))
- Console.println("Local " + l + " is not declared in " + this)
- case STORE_LOCAL(l) =>
- if (!this.locals.contains(l))
- Console.println("Local " + l + " is not declared in " + this)
- case _ => ()
+ def checkLocals: Unit = {
+ def localsSet = code.blocks.flatten collect {
+ case LOAD_LOCAL(l) => l
+ case STORE_LOCAL(l) => l
+ } toSet
+
+ if (code != null) {
+ Console.println("[checking locals of " + this + "]")
+ locals filterNot localsSet foreach { l =>
+ Console.println("Local " + l + " is not declared in " + this)
+ }
}
}
-
+
/** Merge together blocks that have a single successor which has a
* single predecessor. Exception handlers are taken into account (they
* might force to break a block of straight line code like that).
@@ -275,11 +263,10 @@ trait Members { self: ICodes =>
/** PC-based ranges for this local variable's visibility */
var ranges: List[(Int, Int)] = Nil
- override def equals(other: Any): Boolean = (
- other.isInstanceOf[Local] &&
- other.asInstanceOf[Local].sym == this.sym
- );
-
+ override def equals(other: Any): Boolean = other match {
+ case x: Local => sym == x.sym
+ case _ => false
+ }
override def hashCode = sym.hashCode
override def toString(): String = sym.toString()
39 src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala
View
@@ -321,7 +321,7 @@ trait Opcodes { self: ICodes =>
override def toString(): String =
"CALL_METHOD " + hostClass.fullName + method.fullName +" ("+style.toString()+")";
- var hostClass: Symbol = method.owner;
+ var hostClass: Symbol = method.owner
def setHostClass(cls: Symbol): this.type = { hostClass = cls; this }
/** This is specifically for preserving the target native Array type long
@@ -330,29 +330,27 @@ trait Opcodes { self: ICodes =>
var targetTypeKind: TypeKind = UNIT // the default should never be used, so UNIT should fail fast.
def setTargetTypeKind(tk: TypeKind) = targetTypeKind = tk
- override def consumed = method.tpe.paramTypes.length + (
- style match {
- case Dynamic | InvokeDynamic => 1
- case Static(true) => 1
- case Static(false) => 0
- case SuperCall(_) => 1
- }
- )
+ private def params = method.info.paramTypes
+ private def consumesInstance = style match {
+ case Static(false) => 0
+ case _ => 1
+ }
+ override def consumed = params.length + consumesInstance
override def consumedTypes = {
- val args = method.tpe.paramTypes map toTypeKind
- style match {
- case Dynamic | Static(true) => AnyRefReference :: args
- case _ => args
- }
+ val args = params map toTypeKind
+ if (consumesInstance > 0) AnyRefReference :: args
+ else args
}
-
- override def produced =
- if(toTypeKind(method.tpe.resultType) == UNIT)
- 0
- else if(method.isConstructor)
- 0
+
+ override def produced =
+ if (producedType == UNIT || method.isConstructor) 0
else 1
+
+ private def producedType: TypeKind = toTypeKind(method.info.resultType)
+ override def producedTypes =
+ if (produced == 0) Nil
+ else List(producedType)
/** object identity is equality for CALL_METHODs. Needed for
* being able to store such instructions into maps, when more
@@ -611,7 +609,6 @@ trait Opcodes { self: ICodes =>
/** This class represents a method invocation style. */
sealed abstract class InvokeStyle {
-
/** Is this a dynamic method call? */
def isDynamic: Boolean = this match {
case Dynamic => true
13 src/compiler/scala/tools/nsc/backend/icode/Repository.scala
View
@@ -24,17 +24,13 @@ trait Repository {
def available(sym: Symbol) = classes.contains(sym) || loaded.contains(sym)
/** The icode of the given class, if available */
- def icode(sym: Symbol): Option[IClass] =
- if (classes.contains(sym)) Some(classes(sym))
- else if (loaded.contains(sym)) Some(loaded(sym))
- else None
+ def icode(sym: Symbol): Option[IClass] = (classes get sym) orElse (loaded get sym)
/** The icode of the given class. If not available, it loads
* its bytecode.
*/
- def icode(sym: Symbol, force: Boolean): IClass =
- if (available(sym)) icode(sym).get
- else {
+ def icode(sym: Symbol, force: Boolean): IClass =
+ icode(sym) getOrElse {
log("loading " + sym)
load(sym)
assert(available(sym))
@@ -46,7 +42,6 @@ trait Repository {
val (c1, c2) = icodeReader.readClass(sym)
assert(c1.symbol == sym || c2.symbol == sym)
- loaded += (c1.symbol -> c1)
- loaded += (c2.symbol -> c2)
+ loaded += (c1.symbol -> c1, c2.symbol -> c2)
}
}
70 src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala
View
@@ -84,30 +84,20 @@ abstract class CopyPropagation {
/* Return the value bound to the given local. */
def getBinding(l: Local): Value = {
- var target = l
- var stop = false
- var value: Value = Deref(LocalVar(target))
-
- while (bindings.isDefinedAt(LocalVar(target)) && !stop) {
-// Console.println("finding binding for " + target)
- value = bindings(LocalVar(target))
- value match {
- case Deref(LocalVar(t)) => target = t
- case _ => stop = true
- }
+ def loop(lv: Local): Option[Value] = (bindings get LocalVar(lv)) match {
+ case Some(Deref(LocalVar(t))) => loop(t)
+ case x => x
}
- value
+ loop(l) getOrElse Deref(LocalVar(l))
}
/* Return the binding for the given field of the given record */
def getBinding(r: Record, f: Symbol): Value = {
- assert(r.bindings.isDefinedAt(f),
- "Record " + r + " does not contain a field " + f);
-
- var target: Value = r.bindings(f);
- target match {
+ assert(r.bindings contains f, "Record " + r + " does not contain a field " + f)
+
+ r.bindings(f) match {
case Deref(LocalVar(l)) => getBinding(l)
- case _ => target
+ case target => target
}
}
@@ -115,17 +105,10 @@ abstract class CopyPropagation {
* If the field holds a reference to a local, the returned value is the
* binding of that local.
*/
- def getFieldValue(r: Record, f: Symbol): Option[Value] = {
- if(!r.bindings.isDefinedAt(f)) None else {
- var target: Value = r.bindings(f)
- target match {
- case Deref(LocalVar(l)) => Some(getBinding(l))
- case Deref(Field(r1, f1)) => getFieldValue(r1, f1) orElse Some(target)
-// case Deref(This) => Some(target)
-// case Const(k) => Some(target)
- case _ => Some(target)
- }
- }
+ def getFieldValue(r: Record, f: Symbol): Option[Value] = r.bindings get f map {
+ case Deref(LocalVar(l)) => getBinding(l)
+ case target @ Deref(Field(r1, f1)) => getFieldValue(r1, f1) getOrElse target
+ case target => target
}
/** The same as getFieldValue, but never returns Record/Field values. Use
@@ -133,26 +116,23 @@ abstract class CopyPropagation {
* or a constant/this value).
*/
def getFieldNonRecordValue(r: Record, f: Symbol): Option[Value] = {
- assert(r.bindings.isDefinedAt(f),
- "Record " + r + " does not contain a field " + f);
+ assert(r.bindings contains f, "Record " + r + " does not contain a field " + f)
- var target: Value = r.bindings(f)
- target match {
+ r.bindings(f) match {
case Deref(LocalVar(l)) =>
val alias = getAlias(l)
val derefAlias = Deref(LocalVar(alias))
- getBinding(alias) match {
- case Record(_, _) => Some(derefAlias)
- case Deref(Field(r1, f1)) =>
- getFieldNonRecordValue(r1, f1) orElse Some(derefAlias)
- case Boxed(_) => Some(derefAlias)
- case v => Some(v)
- }
- case Deref(Field(r1, f1)) =>
- getFieldNonRecordValue(r1, f1) orElse None
- case Deref(This) => Some(target)
- case Const(k) => Some(target)
- case _ => None
+
+ Some(getBinding(alias) match {
+ case Record(_, _) => derefAlias
+ case Deref(Field(r1, f1)) => getFieldNonRecordValue(r1, f1) getOrElse derefAlias
+ case Boxed(_) => derefAlias
+ case v => v
+ })
+ case Deref(Field(r1, f1)) => getFieldNonRecordValue(r1, f1)
+ case target @ Deref(This) => Some(target)
+ case target @ Const(k) => Some(target)
+ case _ => None
}
}
82 src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala
View
@@ -16,14 +16,15 @@ import scala.collection.{mutable, immutable}
abstract class TypeFlowAnalysis {
val global: Global
import global._
+ import definitions.{ ObjectClass, NothingClass, AnyRefClass, StringClass }
/** The lattice of ICode types.
*/
object typeLattice extends CompleteLattice {
type Elem = icodes.TypeKind
- val Object = icodes.REFERENCE(global.definitions.ObjectClass)
- val All = icodes.REFERENCE(global.definitions.NothingClass)
+ val Object = icodes.REFERENCE(ObjectClass)
+ val All = icodes.REFERENCE(NothingClass)
def top = Object
def bottom = All
@@ -43,7 +44,7 @@ abstract class TypeFlowAnalysis {
override val top = new TypeStack
override val bottom = new TypeStack
- val exceptionHandlerStack: TypeStack = new TypeStack(List(REFERENCE(definitions.AnyRefClass)))
+ val exceptionHandlerStack: TypeStack = new TypeStack(List(REFERENCE(AnyRefClass)))
def lub2(exceptional: Boolean)(s1: TypeStack, s2: TypeStack) = {
if (s1 eq bottom) s2
@@ -59,10 +60,7 @@ abstract class TypeFlowAnalysis {
/** A map which returns the bottom type for unfound elements */
class VarBinding extends mutable.HashMap[icodes.Local, icodes.TypeKind] {
- override def get(l: icodes.Local) = super.get(l) match {
- case Some(t) => Some(t)
- case None => Some(typeLattice.bottom)
- }
+ override def get(l: icodes.Local) = super.get(l) orElse Some(typeLattice.bottom)
def this(o: VarBinding) = {
this()
@@ -80,29 +78,22 @@ abstract class TypeFlowAnalysis {
override val top = new Elem(new VarBinding, typeStackLattice.top)
override val bottom = new Elem(new VarBinding, typeStackLattice.bottom)
-// var lubs = 0
-
def lub2(exceptional: Boolean)(a: Elem, b: Elem) = {
- val IState(env1, s1) = a
- val IState(env2, s2) = b
-
-// lubs += 1
+ val IState(env1, _) = a
+ val IState(env2, _) = b
val resultingLocals = new VarBinding
-
- for (binding1 <- env1.iterator) {
- val tp2 = env2(binding1._1)
- resultingLocals += ((binding1._1, typeLattice.lub2(exceptional)(binding1._2, tp2)))
+ env1 foreach { case (k, v) =>
+ resultingLocals += ((k, typeLattice.lub2(exceptional)(v, env2(k))))
}
-
- for (binding2 <- env2.iterator if resultingLocals(binding2._1) eq typeLattice.bottom) {
- val tp1 = env1(binding2._1)
- resultingLocals += ((binding2._1, typeLattice.lub2(exceptional)(binding2._2, tp1)))
+ env2 collect { case (k, v) if resultingLocals(k) eq typeLattice.bottom =>
+ resultingLocals += ((k, typeLattice.lub2(exceptional)(v, env1(k))))
}
-
- IState(resultingLocals,
+ val stack =
if (exceptional) typeStackLattice.exceptionHandlerStack
- else typeStackLattice.lub2(exceptional)(a.stack, b.stack))
+ else typeStackLattice.lub2(exceptional)(a.stack, b.stack)
+
+ IState(resultingLocals, stack)
}
}
@@ -115,7 +106,7 @@ abstract class TypeFlowAnalysis {
type P = BasicBlock
val lattice = typeFlowLattice
- val STRING = icodes.REFERENCE(TypeFlowAnalysis.this.global.definitions.StringClass)
+ val STRING = icodes.REFERENCE(StringClass)
var method: IMethod = _
/** Initialize the in/out maps for the analysis of the given method. */
@@ -131,8 +122,7 @@ abstract class TypeFlowAnalysis {
}
// start block has var bindings for each of its parameters
- val entryBindings = new VarBinding
- m.params.foreach(p => entryBindings += ((p, p.kind)))
+ val entryBindings = new VarBinding ++= (m.params map (p => ((p, p.kind))))
in(m.code.startBlock) = lattice.IState(entryBindings, typeStackLattice.bottom)
m.exh foreach { e =>
@@ -143,7 +133,7 @@ abstract class TypeFlowAnalysis {
/** reinitialize the analysis, keeping around solutions from a previous run. */
def reinit(m: icodes.IMethod) {
- if ((this.method eq null) || (this.method.symbol != m.symbol))
+ if (this.method == null || this.method.symbol != m.symbol)
init(m)
else reinit {
for (b <- m.code.blocks; if !in.isDefinedAt(b)) {
@@ -154,12 +144,12 @@ abstract class TypeFlowAnalysis {
}
/* else
in(b) = typeFlowLattice.bottom
-*/ }
+*/ }
out(b) = typeFlowLattice.bottom
}
- for (exh <- m.exh; if !in.isDefinedAt(exh.startBlock)) {
- worklist += exh.startBlock
- in(exh.startBlock) = lattice.IState(in(exh.startBlock).vars, typeStackLattice.exceptionHandlerStack)
+ m.exh map (_.startBlock) filterNot (in contains _) foreach { start =>
+ worklist += start
+ in(start) = lattice.IState(in(start).vars, typeStackLattice.exceptionHandlerStack)
}
}
}
@@ -187,12 +177,12 @@ abstract class TypeFlowAnalysis {
def blockTransfer(b: BasicBlock, in: lattice.Elem): lattice.Elem = {
b.foldLeft(in)(interpret)
}
-
/** The flow function of a given basic block. */
- var flowFun: immutable.Map[BasicBlock, TransferFunction] = new immutable.HashMap
+ /* var flowFun: immutable.Map[BasicBlock, TransferFunction] = new immutable.HashMap */
/** Fill flowFun with a transfer function per basic block. */
-/* private def buildFlowFunctions(blocks: List[BasicBlock]) {
+/*
+ private def buildFlowFunctions(blocks: List[BasicBlock]) {
def transfer(b: BasicBlock): TransferFunction = {
var gens: List[Gen] = Nil
var consumed: Int = 0
@@ -487,25 +477,9 @@ abstract class TypeFlowAnalysis {
stack push ConcatClass
}
- case CALL_METHOD(method, style) => style match {
- case Dynamic | InvokeDynamic =>
- stack.pop(1 + method.info.paramTypes.length)
- stack.push(toTypeKind(method.info.resultType))
-
- case Static(onInstance) =>
- if (onInstance) {
- stack.pop(1 + method.info.paramTypes.length)
- if (!method.isConstructor)
- stack.push(toTypeKind(method.info.resultType));
- } else {
- stack.pop(method.info.paramTypes.length)
- stack.push(toTypeKind(method.info.resultType))
- }
-
- case SuperCall(mix) =>
- stack.pop(1 + method.info.paramTypes.length)
- stack.push(toTypeKind(method.info.resultType))
- }
+ case cm @ CALL_METHOD(_, _) =>
+ stack pop cm.consumed
+ cm.producedTypes foreach (stack push _)
case BOX(kind) =>
stack.pop
154 src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
View
@@ -29,6 +29,12 @@ abstract class GenJVM extends SubComponent {
import global._
import icodes._
import icodes.opcodes._
+ import definitions.{
+ NullClass, RuntimeNullClass, NothingClass, RuntimeNothingClass,
+ AnyClass, ObjectClass, ThrowsClass, ThrowableClass, ClassfileAnnotationClass,
+ DeprecatedAttr,
+ getPrimitiveCompanion
+ }
val phaseName = "jvm"
@@ -78,6 +84,10 @@ abstract class GenJVM extends SubComponent {
val MIN_SWITCH_DENSITY = 0.7
val INNER_CLASSES_FLAGS =
(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT)
+
+ val PublicStatic = ACC_PUBLIC | ACC_STATIC
+ val PublicStaticFinal = ACC_PUBLIC | ACC_STATIC | ACC_FINAL
+
val StringBuilderClass = definitions.getClass2("scala.StringBuilder", "scala.collection.mutable.StringBuilder").fullName
val BoxesRunTime = "scala.runtime.BoxesRunTime"
@@ -191,7 +201,7 @@ abstract class GenJVM extends SubComponent {
remoteClass = false
if (parents.isEmpty)
- parents = definitions.ObjectClass.tpe :: parents;
+ parents = ObjectClass.tpe :: parents;
for (annot <- c.symbol.annotations) annot match {
case AnnotationInfo(tp, _, _) if tp.typeSymbol == SerializableAttr =>
@@ -295,7 +305,7 @@ abstract class GenJVM extends SubComponent {
!m.symbol.isGetter &&
!m.symbol.isSetter) yield javaName(m.symbol)
- val constructor = beanInfoClass.addNewMethod(JAccessFlags.ACC_PUBLIC, "<init>", JType.VOID, javaTypes(Nil), javaNames(Nil))
+ val constructor = beanInfoClass.addNewMethod(ACC_PUBLIC, "<init>", JType.VOID, javaTypes(Nil), javaNames(Nil))
val jcode = constructor.getCode().asInstanceOf[JExtendedCode]
val strKind = new JObjectType(javaName(definitions.StringClass))
val stringArrayKind = new JArrayType(strKind)
@@ -354,7 +364,7 @@ abstract class GenJVM extends SubComponent {
// put some random value; the actual number is determined at the end
buf.putShort(0xbaba.toShort)
- for (AnnotationInfo(tp, List(exc), _) <- excs.distinct if tp.typeSymbol == definitions.ThrowsClass) {
+ for (AnnotationInfo(tp, List(exc), _) <- excs.distinct if tp.typeSymbol == ThrowsClass) {
val Literal(const) = exc
buf.putShort(
cpool.addClass(
@@ -372,7 +382,7 @@ abstract class GenJVM extends SubComponent {
*/
private def shouldEmitAnnotation(annot: AnnotationInfo) =
(annot.atp.typeSymbol.initialize.hasFlag(Flags.JAVA) &&
- annot.atp.typeSymbol.isNonBottomSubClass(definitions.ClassfileAnnotationClass) &&
+ annot.atp.typeSymbol.isNonBottomSubClass(ClassfileAnnotationClass) &&
annot.args.isEmpty)
private def emitJavaAnnotations(cpool: JConstantPool, buf: ByteBuffer, annotations: List[AnnotationInfo]): Int = {
@@ -498,7 +508,7 @@ abstract class GenJVM extends SubComponent {
jmember.addAttribute(attr)
}
- val toEmit = annotations.filter(shouldEmitAnnotation(_))
+ val toEmit = annotations filter shouldEmitAnnotation
if (toEmit.isEmpty) return
val buf: ByteBuffer = ByteBuffer.allocate(2048)
@@ -507,8 +517,8 @@ abstract class GenJVM extends SubComponent {
}
def addParamAnnotations(jmethod: JMethod, pannotss: List[List[AnnotationInfo]]) {
- val annotations = pannotss map (annots => annots.filter(shouldEmitAnnotation(_)))
- if (annotations.forall(_.isEmpty)) return;
+ val annotations = pannotss map (_ filter shouldEmitAnnotation)
+ if (annotations forall (_.isEmpty)) return
val buf: ByteBuffer = ByteBuffer.allocate(2048)
@@ -536,10 +546,8 @@ abstract class GenJVM extends SubComponent {
}
def addInnerClasses(jclass: JClass) {
- def addOwnInnerClasses(cls: Symbol) {
- for (sym <- cls.info.decls.iterator if sym.isClass)
- innerClasses = innerClasses + sym;
- }
+ def addOwnInnerClasses(cls: Symbol): Unit =
+ innerClasses ++= (cls.info.decls filter (_.isClass))
// add inner classes which might not have been referenced yet
atPhase(currentRun.erasurePhase.next) {
@@ -554,11 +562,11 @@ abstract class GenJVM extends SubComponent {
for (innerSym <- innerClasses.toList sortBy (_.name.length)) {
var outerName = javaName(innerSym.rawowner)
// remove the trailing '$'
- if (outerName.endsWith("$") && isTopLevelModule(innerSym.rawowner))
- outerName = outerName.substring(0, outerName.length - 1)
+ if (outerName.endsWith("$") && isTopLevelModule(innerSym.rawowner))
+ outerName = outerName dropRight 1
var flags = javaFlags(innerSym)
if (innerSym.rawowner.hasFlag(Flags.MODULE))
- flags |= JAccessFlags.ACC_STATIC
+ flags |= ACC_STATIC
innerClassesAttr.addEntry(javaName(innerSym),
outerName,
@@ -579,24 +587,23 @@ abstract class GenJVM extends SubComponent {
def genField(f: IField) {
if (settings.debug.value)
- log("Adding field: " + f.symbol.fullName);
- var attributes = 0
-
- f.symbol.annotations foreach { a => a match {
- case AnnotationInfo(tp, _, _) if tp.typeSymbol == TransientAtt =>
- attributes = attributes | JAccessFlags.ACC_TRANSIENT
- case AnnotationInfo(tp, _, _) if tp.typeSymbol == VolatileAttr =>
- attributes = attributes | JAccessFlags.ACC_VOLATILE
- case _ => ();
- }}
+ log("Adding field: " + f.symbol.fullName)
+
+ val attributes = f.symbol.annotations.map(_.atp.typeSymbol).foldLeft(0) {
+ case (res, TransientAtt) => res | ACC_TRANSIENT
+ case (res, VolatileAttr) => res | ACC_VOLATILE
+ case (res, _) => res
+ }
+
var flags = javaFlags(f.symbol)
- if (!f.symbol.hasFlag(Flags.MUTABLE))
- flags = flags | JAccessFlags.ACC_FINAL
+ if (!f.symbol.isMutable)
+ flags |= ACC_FINAL
val jfield =
jclass.addNewField(flags | attributes,
javaName(f.symbol),
- javaType(f.symbol.tpe));
+ javaType(f.symbol.tpe))
+
addGenericSignature(jfield, f.symbol, clasz.symbol)
addAnnotations(jfield, f.symbol.annotations)
}
@@ -615,11 +622,11 @@ abstract class GenJVM extends SubComponent {
var flags = javaFlags(m.symbol)
if (jclass.isInterface())
- flags = flags | JAccessFlags.ACC_ABSTRACT;
+ flags |= ACC_ABSTRACT
// native methods of objects are generated in mirror classes
if (method.native)
- flags = flags | JAccessFlags.ACC_NATIVE
+ flags |= ACC_NATIVE
jmethod = jclass.addNewMethod(flags,
javaName(m.symbol),
@@ -662,7 +669,7 @@ abstract class GenJVM extends SubComponent {
}
addGenericSignature(jmethod, m.symbol, clasz.symbol)
- val (excs, others) = splitAnnotations(m.symbol.annotations, definitions.ThrowsClass)
+ val (excs, others) = splitAnnotations(m.symbol.annotations, ThrowsClass)
addExceptionsAttribute(jmethod, excs)
addAnnotations(jmethod, others)
addParamAnnotations(jmethod, m.params.map(_.sym.annotations))
@@ -670,7 +677,7 @@ abstract class GenJVM extends SubComponent {
private def addRemoteException(jmethod: JMethod, meth: Symbol) {
def isRemoteThrows(ainfo: AnnotationInfo) = ainfo match {
- case AnnotationInfo(tp, List(arg), _) if tp.typeSymbol == definitions.ThrowsClass =>
+ case AnnotationInfo(tp, List(arg), _) if tp.typeSymbol == ThrowsClass =>
arg match {
case Literal(Constant(tpe: Type)) if tpe.typeSymbol == RemoteException.typeSymbol => true
case _ => false
@@ -681,7 +688,7 @@ abstract class GenJVM extends SubComponent {
if (remoteClass ||
(meth.hasAnnotation(RemoteAttr) && jmethod.isPublic())) {
val c = Constant(RemoteException)
- val ainfo = AnnotationInfo(definitions.ThrowsClass.tpe, List(Literal(c).setType(c.tpe)), List())
+ val ainfo = AnnotationInfo(ThrowsClass.tpe, List(Literal(c).setType(c.tpe)), List())
if (!meth.annotations.exists(isRemoteThrows)) {
meth.addAnnotation(ainfo)
}
@@ -709,15 +716,13 @@ abstract class GenJVM extends SubComponent {
}
def addModuleInstanceField {
- import JAccessFlags._
- jclass.addNewField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC,
+ jclass.addNewField(PublicStaticFinal,
nme.MODULE_INSTANCE_FIELD.toString,
jclass.getType())
}
def addStaticInit(cls: JClass, mopt: Option[IMethod]) {
- import JAccessFlags._
- val clinitMethod = cls.addNewMethod(ACC_PUBLIC | ACC_STATIC,
+ val clinitMethod = cls.addNewMethod(PublicStatic,
"<clinit>",
JType.VOID,
JType.EMPTY_ARRAY,
@@ -741,12 +746,11 @@ abstract class GenJVM extends SubComponent {
// add serialVUID code
serialVUID match {
case Some(value) =>
- import Flags._
- import definitions._
+ import Flags.{ STATIC, FINAL }
val fieldName = "serialVersionUID"
val fieldSymbol = clasz.symbol.newValue(NoPosition, newTermName(fieldName))
.setFlag(STATIC | FINAL)
- .setInfo(longType)
+ .setInfo(definitions.longType)
clasz.addField(new IField(fieldSymbol))
lastBlock.emit(CONSTANT(Constant(value)))
lastBlock.emit(STORE_FIELD(fieldSymbol, true))
@@ -780,9 +784,7 @@ abstract class GenJVM extends SubComponent {
serialVUID match {
case Some(value) =>
val fieldName = "serialVersionUID"
- jclass.addNewField(JAccessFlags.ACC_STATIC | JAccessFlags.ACC_PUBLIC | JAccessFlags.ACC_FINAL,
- fieldName,
- JType.LONG)
+ jclass.addNewField(PublicStaticFinal, fieldName, JType.LONG)
clinit.emitPUSH(value)
clinit.emitPUTSTATIC(jclass.getName(), fieldName, JType.LONG)
case None => ()
@@ -813,32 +815,27 @@ abstract class GenJVM extends SubComponent {
def addForwarder(jclass: JClass, module: Symbol, m: Symbol) {
import JAccessFlags._
val moduleName = javaName(module) // + "$"
- val mirrorName = moduleName.substring(0, moduleName.length() - 1)
-
+ val mirrorName = moduleName dropRight 1
val methodInfo = module.thisType.memberInfo(m)
- val paramJavaTypes = methodInfo.paramTypes map toTypeKind
- val paramNames: Array[String] = new Array[String](paramJavaTypes.length);
-
- for (i <- 0 until paramJavaTypes.length)
- paramNames(i) = "x_" + i
+ val paramJavaTypes = methodInfo.paramTypes map toTypeKind
+ val paramNames = paramJavaTypes.indices map ("x_" + _) toArray
- val mirrorMethod = jclass.addNewMethod(ACC_PUBLIC | ACC_FINAL | ACC_STATIC,
+ val mirrorMethod = jclass.addNewMethod(
+ PublicStaticFinal,
javaName(m),
javaType(methodInfo.resultType),
javaTypes(paramJavaTypes),
- paramNames);
+ paramNames
+ )
val mirrorCode = mirrorMethod.getCode().asInstanceOf[JExtendedCode];
mirrorCode.emitGETSTATIC(moduleName,
nme.MODULE_INSTANCE_FIELD.toString,
new JObjectType(moduleName));
- var i = 0
- var index = 0
- var argTypes = mirrorMethod.getArgumentTypes()
- while (i < argTypes.length) {
- mirrorCode.emitLOAD(index, argTypes(i))
- index = index + argTypes(i).getSize()
- i += 1
+
+ mirrorMethod.getArgumentTypes().foldLeft(0) { (index, argType) =>
+ mirrorCode.emitLOAD(index, argType)
+ index + argType.getSize
}
mirrorCode.emitINVOKEVIRTUAL(moduleName, mirrorMethod.getName(), javaType(m).asInstanceOf[JMethodType])
@@ -849,7 +846,7 @@ abstract class GenJVM extends SubComponent {
if (!m.hasFlag(Flags.DEFERRED))
addGenericSignature(mirrorMethod, m, module)
- val (throws, others) = splitAnnotations(m.annotations, definitions.ThrowsClass)
+ val (throws, others) = splitAnnotations(m.annotations, ThrowsClass)
addExceptionsAttribute(mirrorMethod, throws)
addAnnotations(mirrorMethod, others)
addParamAnnotations(mirrorMethod, m.info.params.map(_.annotations))
@@ -885,14 +882,14 @@ abstract class GenJVM extends SubComponent {
/** Should method `m' get a forwarder in the mirror class? */
def shouldForward(m: Symbol): Boolean =
atPhase(currentRun.picklerPhase) (
- m.owner != definitions.ObjectClass
+ m.owner != ObjectClass
&& m.isMethod
&& !m.hasFlag(Flags.CASE | Flags.PRIVATE | Flags.PROTECTED | Flags.DEFERRED | Flags.SPECIALIZED)
&& !m.isConstructor
&& !m.isStaticMember
- && !(m.owner == definitions.AnyClass)
+ && !(m.owner == AnyClass)
&& !module.isSubClass(module.companionClass)
- && !conflictsIn(definitions.ObjectClass, m.name)
+ && !conflictsIn(ObjectClass, m.name)
&& !conflictsInCommonParent(m.name)
&& !conflictsIn(module.companionClass, m.name)
)
@@ -914,7 +911,7 @@ abstract class GenJVM extends SubComponent {
def dumpMirrorClass(clasz: Symbol, sourceFile: String) {
import JAccessFlags._
val moduleName = javaName(clasz) // + "$"
- val mirrorName = moduleName.substring(0, moduleName.length() - 1)
+ val mirrorName = moduleName dropRight 1
val mirrorClass = fjbgContext.JClass(ACC_SUPER | ACC_PUBLIC | ACC_FINAL,
mirrorName,
"java.lang.Object",
@@ -1036,7 +1033,7 @@ abstract class GenJVM extends SubComponent {
if (settings.debug.value)
log("Adding exception handler " + e + "at block: " + e.startBlock + " for " + method +
" from: " + p._1 + " to: " + p._2 + " catching: " + e.cls);
- val cls = if (e.cls == NoSymbol || e.cls == definitions.ThrowableClass) null
+ val cls = if (e.cls == NoSymbol || e.cls == ThrowableClass) null
else javaName(e.cls)
jcode.addExceptionHandler(p._1, p._2,
labels(e.startBlock).getAnchor(),
@@ -1746,21 +1743,20 @@ abstract class GenJVM extends SubComponent {
def javaName(sym: Symbol): String = {
val suffix = moduleSuffix(sym)
- if (sym == definitions.NothingClass)
- return javaName(definitions.RuntimeNothingClass)
- else if (sym == definitions.NullClass)
- return javaName(definitions.RuntimeNullClass)
- else if (definitions.primitiveCompanions(sym.companionModule))
- return javaName(definitions.getModule("scala.runtime." + sym.name))
-
- if (sym.isClass && !sym.rawowner.isPackageClass && !sym.isModuleClass) {
- innerClasses = innerClasses + sym;
+ if (sym == NothingClass) javaName(RuntimeNothingClass)
+ else if (sym == NullClass) javaName(RuntimeNullClass)
+ else getPrimitiveCompanion(sym.companionModule) match {
+ case Some(sym) => javaName(sym)
+ case _ =>
+ if (sym.isClass && !sym.rawowner.isPackageClass && !sym.isModuleClass)
+ innerClasses = innerClasses + sym
+
+ val prefix =
+ if (sym.isClass || (sym.isModule && !sym.isMethod)) sym.fullName('/')
+ else sym.simpleName.toString.trim()
+
+ prefix + suffix
}
-
- (if (sym.isClass || (sym.isModule && !sym.isMethod))
- sym.fullName('/')
- else
- sym.simpleName.toString.trim()) + suffix
}
def javaNames(syms: List[Symbol]): Array[String] = {
@@ -1826,7 +1822,7 @@ abstract class GenJVM extends SubComponent {
sym.hasFlag(Flags.INTERFACE) ||
(sym.hasFlag(Flags.JAVA) &&
- sym.isNonBottomSubClass(definitions.ClassfileAnnotationClass))
+ sym.isNonBottomSubClass(ClassfileAnnotationClass))
}
56 src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala
View
@@ -196,7 +196,7 @@ abstract class ClosureElimination extends SubComponent {
/** is field 'f' accessible from method 'm'? */
def accessible(f: Symbol, m: Symbol): Boolean =
- f.isPublic || (f.hasFlag(Flags.PROTECTED) && (enclPackage(f) == enclPackage(m)))
+ f.isPublic || (f.isProtected && enclPackage(f) == enclPackage(m))
private def enclPackage(sym: Symbol): Symbol =
if ((sym == NoSymbol) || sym.isPackageClass) sym else enclPackage(sym.owner)
@@ -206,40 +206,30 @@ abstract class ClosureElimination extends SubComponent {
/** Peephole optimization. */
class PeepholeOpt(peep: (Instruction, Instruction) => Option[List[Instruction]]) {
-
- private var method: IMethod = null
-
- def transformMethod(m: IMethod): Unit = if (m.code ne null) {
- method = m
- for (b <- m.code.blocks)
- transformBlock(b)
+ def transformMethod(m: IMethod) =
+ if (m.hasCode)
+ m.code.blocks filter (_.size >= 2) foreach (b => transformBlock(m, b))
+
+ private def doPeep(instructions: List[Instruction]): List[Instruction] = {
+ var h = instructions.head
+ var t = instructions.tail
+ var newInstructions: List[Instruction] = Nil
+ var seen: List[Instruction] = Nil
+
+ while (t != Nil) {
+ val peepResult = peep(h, t.head) map (xs => seen.reverse ::: xs ::: t.tail)
+ peepResult foreach (newInstructions = _)
+
+ seen ::= h
+ h = t.head
+ t = t.tail
+ }
+ if (newInstructions.isEmpty) instructions
+ else doPeep(newInstructions)
}
- def transformBlock(b: BasicBlock): Unit = if (b.size >= 2) {
- var newInstructions: List[Instruction] = Nil;
-
- newInstructions = b.toList
-
- var redo = false
- do {
- var h = newInstructions.head;
- var t = newInstructions.tail;
- var seen: List[Instruction] = Nil;
- redo = false;
-
- while (t != Nil) {
- peep(h, t.head) match {
- case Some(newInstrs) =>
- newInstructions = seen.reverse ::: newInstrs ::: t.tail;
- redo = true;
- case None =>
- ()
- }
- seen = h :: seen;
- h = t.head;
- t = t.tail
- }
- } while (redo);
+ private def transformBlock(m: IMethod, b: BasicBlock): Unit = {
+ val newInstructions = doPeep(b.toList)
b.fromList(newInstructions)
}
}
27 src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
View
@@ -7,9 +7,8 @@
package scala.tools.nsc
package backend.opt
-import scala.collection._
-import scala.collection.immutable.{Map, HashMap, Set, HashSet}
-import scala.tools.nsc.symtab._
+import scala.collection.{ mutable, immutable }
+import symtab._
/**
*/
@@ -53,7 +52,7 @@ abstract class DeadCodeElimination extends SubComponent {
val rdef = new reachingDefinitions.ReachingDefinitionsAnalysis;
/** Use-def chain: give the reaching definitions at the beginning of given instruction. */
- var defs: Map[(BasicBlock, Int), Set[rdef.lattice.Definition]] = HashMap.empty
+ var defs: immutable.Map[(BasicBlock, Int), immutable.Set[rdef.lattice.Definition]] = immutable.HashMap.empty
/** Useful instructions which have not been scanned yet. */
val worklist: mutable.Set[(BasicBlock, Int)] = new mutable.LinkedHashSet
@@ -90,7 +89,7 @@ abstract class DeadCodeElimination extends SubComponent {
/** collect reaching definitions and initial useful instructions for this method. */
def collectRDef(m: IMethod): Unit = if (m.code ne null) {
- defs = HashMap.empty; worklist.clear; useful.clear;
+ defs = immutable.HashMap.empty; worklist.clear; useful.clear;
rdef.init(m);
rdef.run;
@@ -253,20 +252,12 @@ abstract class DeadCodeElimination extends SubComponent {
res
}
- private def findInstruction(bb: BasicBlock, i: Instruction): (BasicBlock, Int) = {
- def find(bb: BasicBlock): Option[(BasicBlock, Int)] = {
- var xs = bb.toList
- xs.zipWithIndex find { hd => hd._1 eq i } match {
- case Some((_, idx)) => Some(bb, idx)
- case None => None
- }
+ private def findInstruction(bb: BasicBlock, i: Instruction): (BasicBlock, Int) = {
+ for (b <- linearizer.linearizeAt(method, bb)) {
+ val idx = b.toList indexWhere (_ eq i)
+ if (idx != -1)
+ return (b, idx)
}
-
- for (b <- linearizer.linearizeAt(method, bb))
- find(b) match {
- case Some(p) => return p
- case None => ()
- }
abort("could not find init in: " + method)
}
49 src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
View
@@ -302,7 +302,6 @@ abstract class Inliners extends SubComponent {
class CallerCalleeInfo(val caller: IMethodInfo, val inc: IMethodInfo) {
def isLargeSum = caller.length + inc.length - 1 > SMALL_METHOD_SIZE
-
/** Inline 'inc' into 'caller' at the given block and instruction.
* The instruction must be a CALL_METHOD.
*/
@@ -385,41 +384,19 @@ abstract class Inliners extends SubComponent {
def isInlined(l: Local) = inlinedLocals isDefinedAt l
val newInstr = i match {
- case THIS(clasz) =>
- LOAD_LOCAL(inlinedThis)
-
- case STORE_THIS(_) =>
- STORE_LOCAL(inlinedThis)
-
- case JUMP(whereto) =>
- JUMP(inlinedBlock(whereto))
-
- case CJUMP(success, failure, cond, kind) =>
- CJUMP(inlinedBlock(success), inlinedBlock(failure), cond, kind)
-
- case CZJUMP(success, failure, cond, kind) =>
- CZJUMP(inlinedBlock(success), inlinedBlock(failure), cond, kind)
-
- case SWITCH(tags, labels) =>
- SWITCH(tags, labels map inlinedBlock)
-
- case RETURN(kind) =>
- JUMP(afterBlock)
-
- case LOAD_LOCAL(l) if isInlined(l) =>
- LOAD_LOCAL(inlinedLocals(l))
-
- case STORE_LOCAL(l) if isInlined(l) =>
- STORE_LOCAL(inlinedLocals(l))
-
- case LOAD_LOCAL(l) => assertLocal(l)
- case STORE_LOCAL(l) => assertLocal(l)
-
- case SCOPE_ENTER(l) if isInlined(l) =>
- SCOPE_ENTER(inlinedLocals(l))
-
- case SCOPE_EXIT(l) if isInlined(l) =>
- SCOPE_EXIT(inlinedLocals(l))
+ case THIS(clasz) => LOAD_LOCAL(inlinedThis)
+ case STORE_THIS(_) => STORE_LOCAL(inlinedThis)
+ case JUMP(whereto) => JUMP(inlinedBlock(whereto))
+ case CJUMP(succ, fail, cond, kind) => CJUMP(inlinedBlock(succ), inlinedBlock(fail), cond, kind)
+ case CZJUMP(succ, fail, cond, kind) => CZJUMP(inlinedBlock(succ), inlinedBlock(fail), cond, kind)
+ case SWITCH(tags, labels) => SWITCH(tags, labels map inlinedBlock)
+ case RETURN(_) => JUMP(afterBlock)
+ case LOAD_LOCAL(l) if isInlined(l) => LOAD_LOCAL(inlinedLocals(l))
+ case STORE_LOCAL(l) if isInlined(l) => STORE_LOCAL(inlinedLocals(l))
+ case LOAD_LOCAL(l) => assertLocal(l)
+ case STORE_LOCAL(l) => assertLocal(l)
+ case SCOPE_ENTER(l) if isInlined(l) => SCOPE_ENTER(inlinedLocals(l))
+ case SCOPE_EXIT(l) if isInlined(l) => SCOPE_EXIT(inlinedLocals(l))
case nw @ NEW(sym) =>
val r = NEW(sym)
11 src/compiler/scala/tools/nsc/symtab/Definitions.scala
View
@@ -580,9 +580,14 @@ trait Definitions extends reflect.generic.StandardDefinitions {
val boxedClass = new HashMap[Symbol, Symbol]
val boxedModule = new HashMap[Symbol, Symbol]
- val unboxMethod = new HashMap[Symbol, Symbol] // Type -> Method
- val boxMethod = new HashMap[Symbol, Symbol] // Type -> Method
- val primitiveCompanions = new HashSet[Symbol]
+ val unboxMethod = new HashMap[Symbol, Symbol] // Type -> Method
+ val boxMethod = new HashMap[Symbol, Symbol] // Type -> Method
+ val primitiveCompanions = new HashSet[Symbol] // AnyVal -> Companion
+
+ /** Maps a companion object like scala.Int to scala.runtime.Int. */
+ def getPrimitiveCompanion(sym: Symbol) =
+ if (primitiveCompanions(sym)) Some(getModule("scala.runtime." + sym.name))
+ else None
def isUnbox(m: Symbol) = unboxMethod.valuesIterator contains m
def isBox(m: Symbol) = boxMethod.valuesIterator contains m
Please sign in to comment.
Something went wrong with that request. Please try again.