Some cleanups in inline exception handlers. Review by ureche.
git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@25539 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
parent
6d31a5bd62
commit
7c625135ca
|
@ -62,7 +62,6 @@ abstract class InlineExceptionHandlers extends SubComponent {
|
|||
* Inlining Exception Handlers
|
||||
*/
|
||||
class InlineExceptionHandlersPhase(prev: Phase) extends ICodePhase(prev) {
|
||||
|
||||
def name = phaseName
|
||||
|
||||
/* This map is used to keep track of duplicated exception handlers
|
||||
|
@ -71,20 +70,22 @@ abstract class InlineExceptionHandlers extends SubComponent {
|
|||
* -some exception handler duplicates expect the exception on the stack while others expect it in a local
|
||||
* => Option[Local]
|
||||
*/
|
||||
var handlerCopies: Map[BasicBlock, Option[(Option[Local], BasicBlock)]] = Map.empty
|
||||
private val handlerCopies = perRunCaches.newMap[BasicBlock, Option[(Option[Local], BasicBlock)]]
|
||||
/* This map is the inverse of handlerCopies, used to compute the stack of duplicate blocks */
|
||||
var handlerCopiesInverted: Map[BasicBlock, (BasicBlock, TypeKind)] = Map.empty
|
||||
private val handlerCopiesInverted = perRunCaches.newMap[BasicBlock, (BasicBlock, TypeKind)]
|
||||
private def handlerLocal(bb: BasicBlock): Option[Local] =
|
||||
for (v <- handlerCopies get bb ; (local, block) <- v ; l <- local) yield l
|
||||
|
||||
/* Type Flow Analysis */
|
||||
val tfa: analysis.MethodTFA = new analysis.MethodTFA()
|
||||
var tfaCache: Map[Int, tfa.lattice.Elem] = Map.empty
|
||||
var analyzedMethod: IMethod = null
|
||||
private val tfa: analysis.MethodTFA = new analysis.MethodTFA()
|
||||
private var tfaCache: Map[Int, tfa.lattice.Elem] = Map.empty
|
||||
private var analyzedMethod: IMethod = null
|
||||
|
||||
/* Blocks that need to be analyzed */
|
||||
var todoBlocks: List[BasicBlock] = Nil
|
||||
private var todoBlocks: List[BasicBlock] = Nil
|
||||
|
||||
/* Used only for warnings */
|
||||
var currentClass: IClass = null
|
||||
private var currentClass: IClass = null
|
||||
|
||||
/** Apply exception handler inlining to a class */
|
||||
override def apply(c: IClass): Unit =
|
||||
|
@ -92,11 +93,10 @@ abstract class InlineExceptionHandlers extends SubComponent {
|
|||
val startTime = System.currentTimeMillis
|
||||
currentClass = c
|
||||
|
||||
log("Starting " + c.toString)
|
||||
for (method <- c.methods)
|
||||
apply(method)
|
||||
log("Starting " + c)
|
||||
c.methods foreach applyMethod
|
||||
|
||||
log("Finished " + c.toString + "... " + (System.currentTimeMillis - startTime) + "ms")
|
||||
log("Finished " + c + "... " + (System.currentTimeMillis - startTime) + "ms")
|
||||
currentClass = null
|
||||
}
|
||||
|
||||
|
@ -109,23 +109,21 @@ abstract class InlineExceptionHandlers extends SubComponent {
|
|||
* TODO: Should we have an inlining depth limit? A nested sequence of n try-catch blocks can lead to at most 2n
|
||||
* inlined blocks, so worst case scenario we double the size of the code
|
||||
*/
|
||||
def apply(method: IMethod): Unit = {
|
||||
|
||||
private def applyMethod(method: IMethod): Unit = {
|
||||
if (method.code ne null) {
|
||||
// create the list of starting blocks
|
||||
todoBlocks = global.icodes.linearizer.linearize(method)
|
||||
|
||||
while (todoBlocks.length > 0) {
|
||||
while (todoBlocks.nonEmpty) {
|
||||
val levelBlocks = todoBlocks
|
||||
todoBlocks = Nil
|
||||
for (bblock <- levelBlocks)
|
||||
apply(bblock) // new blocks will be added to todoBlocks
|
||||
levelBlocks foreach applyBasicBlock // new blocks will be added to todoBlocks
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup the references after we finished the file
|
||||
handlerCopies = Map.empty
|
||||
handlerCopiesInverted = Map.empty
|
||||
handlerCopies.clear()
|
||||
handlerCopiesInverted.clear()
|
||||
todoBlocks = Nil
|
||||
|
||||
// Type flow analysis cleanup
|
||||
|
@ -135,7 +133,7 @@ abstract class InlineExceptionHandlers extends SubComponent {
|
|||
}
|
||||
|
||||
/** Apply exception handler inlining to a basic block */
|
||||
def apply(bblock: BasicBlock): Unit = {
|
||||
private def applyBasicBlock(bblock: BasicBlock): Unit = {
|
||||
/*
|
||||
* The logic of this entire method:
|
||||
* - for each basic block, we look at each instruction until we find a THROW instruction
|
||||
|
@ -152,117 +150,83 @@ abstract class InlineExceptionHandlers extends SubComponent {
|
|||
* - we compute the necessary code to put the exception in its place, clear the stack and JUMP
|
||||
* - we change the THROW exception to the new Clear stack + JUMP code
|
||||
*/
|
||||
for ((instr, index) <- bblock.zipWithIndex)
|
||||
if (instr.isInstanceOf[THROW]) {
|
||||
for {
|
||||
(instr @ THROW(clazz), index) <- bblock.zipWithIndex
|
||||
// Decide if any handler fits this exception
|
||||
// If not, then nothing to do, we cannot determine statically which handler will catch the exception
|
||||
(handler, caughtException) <- findExceptionHandler(toTypeKind(clazz.tpe), bblock.exceptionSuccessors)
|
||||
} {
|
||||
log(" Replacing " + instr + " in " + bblock + " to new handler")
|
||||
|
||||
// Decide if any handler fits this exception
|
||||
val THROW(clazz) = instr
|
||||
findExceptionHandler(toTypeKind(clazz.tpe), bblock.exceptionSuccessors) match {
|
||||
// Solve the stack and drop the element that we already stored, which should be the exception
|
||||
// needs to be done here to be the first thing before code becomes altered
|
||||
val typeInfo = getTypesAtInstruction(bblock, index)
|
||||
|
||||
case None =>
|
||||
// nothing to do, we cannot determine statically which handler will catch the exception
|
||||
// Duplicate exception handler
|
||||
duplicateExceptionHandlerCache(handler) match {
|
||||
case None =>
|
||||
log(" Could not duplicate handler for " + instr + " in " + bblock)
|
||||
|
||||
case Some((handler, caughtException)) =>
|
||||
var onStackException: TypeKind = null
|
||||
var thrownException: TypeKind = null
|
||||
log(" Replacing " + instr.toString + " in " + bblock.toString + " to new handler")
|
||||
case Some((exceptionLocalOpt, newHandler)) =>
|
||||
val onStackException = typeInfo.head
|
||||
val thrownException = toTypeKind(clazz.tpe)
|
||||
|
||||
// Solve the stack and drop the element that we already stored, which should be the exception
|
||||
// needs to be done here to be the first thing before code becomes altered
|
||||
var typeInfo = getTypesAtInstruction(bblock, index)
|
||||
// A couple of sanity checks, to make sure we don't touch code we can't safely handle
|
||||
val canReplaceHandler = (
|
||||
typeInfo.nonEmpty
|
||||
&& (index == bblock.length - 1)
|
||||
&& (onStackException <:< thrownException)
|
||||
)
|
||||
// in other words: what's on the stack MUST conform to what's in the THROW(..)!
|
||||
|
||||
// Duplicate exception handler
|
||||
duplicateExceptionHandlerWithCaching(handler) match {
|
||||
if (!canReplaceHandler) {
|
||||
currentClass.cunit.warning(NoPosition, "Unable to inline the exception handler inside incorrect" +
|
||||
" block:\n" + bblock.mkString("\n") + "\nwith stack: " + typeInfo + " just " +
|
||||
"before instruction index " + index)
|
||||
}
|
||||
else {
|
||||
// Prepare the new code to replace the THROW instruction
|
||||
val newCode = exceptionLocalOpt match {
|
||||
// the handler duplicate expects the exception in a local: easy one :)
|
||||
case Some(local) =>
|
||||
// in the first cycle we remove the exception Type
|
||||
STORE_LOCAL(local) +: typeInfo.tail.map(x => DROP(x)) :+ JUMP(newHandler)
|
||||
|
||||
case None =>
|
||||
log(" Could not duplicate handler for " + instr.toString + " in " + bblock.toString)
|
||||
// we already have the exception on top of the stack, only need to JUMP
|
||||
case None if typeInfo.length == 1 =>
|
||||
JUMP(newHandler) :: Nil
|
||||
|
||||
case Some((exceptionLocalOpt, newHandler)) =>
|
||||
// we have the exception on top of the stack but we have other stuff on the stack
|
||||
// create a local, load exception, clear the stack and finally store the exception on the stack
|
||||
case _ =>
|
||||
val exceptionType = typeInfo.head
|
||||
// Here we could create a single local for all exceptions of a certain type. TODO: try that.
|
||||
val localName = currentClass.cunit.freshTermName("exception$")
|
||||
val localType = exceptionType
|
||||
val localSymbol = bblock.method.symbol.newValue(NoPosition, localName).setInfo(localType.toType)
|
||||
val local = new Local(localSymbol, localType, false)
|
||||
|
||||
var canReplaceHandler = true
|
||||
onStackException = typeInfo.head
|
||||
thrownException = toTypeKind(clazz.tpe)
|
||||
|
||||
// A couple of sanity checks, to make sure we don't touch code we can't safely handle
|
||||
if (typeInfo.length < 1) canReplaceHandler = false
|
||||
if (index != bblock.length - 1) canReplaceHandler = false
|
||||
if (!(onStackException <:< thrownException)) canReplaceHandler = false
|
||||
// in other words: what's on the stack MUST conform to what's in the THROW(..)!
|
||||
|
||||
if (!canReplaceHandler) {
|
||||
|
||||
assert(currentClass ne null)
|
||||
currentClass.cunit.warning(NoPosition, "Unable to inline the exception handler inside incorrect" +
|
||||
" block:\n" + bblock.mkString("\n") + "\nwith stack: " + typeInfo.toString + " just " +
|
||||
"before instruction index " + index)
|
||||
|
||||
} else {
|
||||
|
||||
var newCode: List[Instruction] = Nil
|
||||
var replaceType = -1
|
||||
|
||||
// Prepare the new code to replace the THROW instruction
|
||||
exceptionLocalOpt match {
|
||||
|
||||
// the handler duplicate expects the exception in a local: easy one :)
|
||||
case Some(exceptionLocal) =>
|
||||
replaceType = 1
|
||||
val exceptionType = typeInfo.head
|
||||
|
||||
newCode ::= STORE_LOCAL(exceptionLocal)
|
||||
while (typeInfo.length > 1) {
|
||||
typeInfo = typeInfo.tail // in the first cycle we remove the exception Type
|
||||
newCode ::= DROP(typeInfo.head)
|
||||
}
|
||||
newCode ::= JUMP(newHandler)
|
||||
|
||||
// we already have the exception on top of the stack, only need to JUMP
|
||||
case None if (typeInfo.length == 1) =>
|
||||
replaceType = 2
|
||||
newCode = JUMP(newHandler) :: Nil
|
||||
|
||||
// we have the exception on top of the stack but we have other stuff on the stack
|
||||
// create a local, load exception, clear the stack and finally store the exception on the stack
|
||||
case None =>
|
||||
replaceType = 3
|
||||
val exceptionType = typeInfo.head
|
||||
|
||||
assert(currentClass ne null)
|
||||
assert(currentClass.cunit ne null)
|
||||
|
||||
// Here we could create a single local for all exceptions of a certain type. TODO: try that.
|
||||
val localName = currentClass.cunit.freshTermName("exception$")
|
||||
val localType = exceptionType
|
||||
val localSymbol = bblock.method.symbol.newValue(NoPosition, localName).setInfo(localType.toType)
|
||||
val local = new Local(localSymbol, localType, false)
|
||||
bblock.method.addLocal(local)
|
||||
|
||||
// Save the exception, drop the stack and place back the exception
|
||||
newCode ::= STORE_LOCAL(local)
|
||||
while (typeInfo.length > 1) {
|
||||
typeInfo = typeInfo.tail // in the first cycle we remove the exception Type
|
||||
newCode ::= DROP(typeInfo.head)
|
||||
}
|
||||
newCode ::= LOAD_LOCAL(local)
|
||||
newCode ::= JUMP(newHandler)
|
||||
}
|
||||
// replace THROW by the new code
|
||||
bblock.replaceInstruction(instr, newCode.reverse)
|
||||
|
||||
// notify the successors changed for the current block
|
||||
// notify the predecessors changed for the inlined handler block
|
||||
bblock.touched = true
|
||||
newHandler.touched = true
|
||||
|
||||
log(" Replaced " + instr.toString + " in " + bblock.toString + " to new handler")
|
||||
log("OPTIMIZED[" + replaceType + "] class " + currentClass.toString + " method " +
|
||||
bblock.method.toString + " block " + bblock.toString + " newhandler " +
|
||||
newHandler.toString + ":\n\t\t" + onStackException.toString + " <:< " +
|
||||
thrownException.toString + " <:< " + caughtException.toString)
|
||||
}
|
||||
bblock.method.addLocal(local)
|
||||
|
||||
// Save the exception, drop the stack and place back the exception
|
||||
STORE_LOCAL(local) :: typeInfo.tail.map(x => DROP(x)) ::: List(LOAD_LOCAL(local), JUMP(newHandler))
|
||||
}
|
||||
// replace THROW by the new code
|
||||
bblock.replaceInstruction(instr, newCode)
|
||||
|
||||
// notify the successors changed for the current block
|
||||
// notify the predecessors changed for the inlined handler block
|
||||
bblock.touched = true
|
||||
newHandler.touched = true
|
||||
|
||||
log(" Replaced " + instr + " in " + bblock + " to new handler")
|
||||
log("OPTIMIZED class " + currentClass + " method " +
|
||||
bblock.method + " block " + bblock + " newhandler " +
|
||||
newHandler + ":\n\t\t" + onStackException + " <:< " +
|
||||
thrownException + " <:< " + caughtException)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,64 +234,58 @@ abstract class InlineExceptionHandlers extends SubComponent {
|
|||
* Gets the types on the stack at a certain point in the program. Note that we want to analyze the method lazily
|
||||
* and therefore use the analyzedMethod variable
|
||||
*/
|
||||
def getTypesAtInstruction(bblock: BasicBlock, index: Int): List[TypeKind] = {
|
||||
|
||||
private def getTypesAtInstruction(bblock: BasicBlock, index: Int): List[TypeKind] = {
|
||||
// get the stack at the block entry
|
||||
var typeInfo = getTypesAtBlockEntry(bblock)
|
||||
|
||||
// perform tfa to the current instruction
|
||||
log(" stack at the beginning of block " + bblock.toString + " in function " +
|
||||
bblock.method.toString + ": " + typeInfo.stack.toString)
|
||||
log(" stack at the beginning of block " + bblock + " in function " +
|
||||
bblock.method + ": " + typeInfo.stack)
|
||||
for (i <- 0 to (index - 1)) {
|
||||
typeInfo = tfa.interpret(typeInfo, bblock(i))
|
||||
log(" stack after interpret: " + typeInfo.stack.toString + " after instruction " +
|
||||
bblock(i).toString)
|
||||
log(" stack after interpret: " + typeInfo.stack + " after instruction " +
|
||||
bblock(i))
|
||||
}
|
||||
log(" stack before instruction " + index + " of block " + bblock.toString + " in function " +
|
||||
bblock.method.toString + ": " + typeInfo.stack.toString)
|
||||
log(" stack before instruction " + index + " of block " + bblock + " in function " +
|
||||
bblock.method + ": " + typeInfo.stack)
|
||||
|
||||
// return the result
|
||||
typeInfo.stack.types
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the stack at the block entry. Normally the typeFlowAnalysis should be run again, but we know how to compute
|
||||
* the stack for handler duplicates. For the locals, it's safe to assume the info from the original handler is
|
||||
* still valid (a more precise analysis can be done, but it's not necessary)
|
||||
*/
|
||||
def getTypesAtBlockEntry(bblock: BasicBlock): tfa.lattice.Elem = {
|
||||
|
||||
private def getTypesAtBlockEntry(bblock: BasicBlock): tfa.lattice.Elem = {
|
||||
// lazily perform tfa, because it's expensive
|
||||
// cache results by block label, as rewriting the code messes up the block's hashCode
|
||||
if (analyzedMethod eq null) {
|
||||
analyzedMethod = bblock.method
|
||||
tfa.init(bblock.method)
|
||||
tfa.run
|
||||
log(" performed tfa on method: " + bblock.method.toString)
|
||||
log(" performed tfa on method: " + bblock.method)
|
||||
|
||||
for (block <- bblock.method.code.blocks.sortWith(_.label < _.label))
|
||||
for (block <- bblock.method.code.blocks.sortBy(_.label))
|
||||
tfaCache += block.label -> tfa.in(block)
|
||||
}
|
||||
|
||||
log(" getting typeinfo at the beginning of block " + bblock.toString)
|
||||
log(" getting typeinfo at the beginning of block " + bblock)
|
||||
|
||||
if (tfaCache contains bblock.label)
|
||||
tfaCache(bblock.label)
|
||||
else {
|
||||
tfaCache.getOrElse(bblock.label, {
|
||||
// this block was not analyzed, but it's a copy of some other block so its stack should be the same
|
||||
log(" getting typeinfo at the beginning of block " + bblock.toString + " as a copy of " +
|
||||
handlerCopiesInverted(bblock).toString)
|
||||
log(" getting typeinfo at the beginning of block " + bblock + " as a copy of " +
|
||||
handlerCopiesInverted(bblock))
|
||||
val (origBlock, exception) = handlerCopiesInverted(bblock)
|
||||
val typeInfo = getTypesAtBlockEntry(origBlock)
|
||||
val stack = handlerCopies(origBlock).get._1 match {
|
||||
case Some(_) => Nil // empty stack, the handler copy expects an empty stack
|
||||
case None => List(exception) // one slot on the stack for the exception
|
||||
}
|
||||
val stack =
|
||||
if (handlerLocal(origBlock).nonEmpty) Nil // empty stack, the handler copy expects an empty stack
|
||||
else List(exception) // one slot on the stack for the exception
|
||||
|
||||
// If we use the mutability property, it crashes the analysis
|
||||
tfa.lattice.IState(new analysis.VarBinding(typeInfo.vars), new icodes.TypeStack(stack))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -367,109 +325,62 @@ abstract class InlineExceptionHandlers extends SubComponent {
|
|||
* We don't want to select a handler if there's at least one that's more specific!
|
||||
*/
|
||||
def findExceptionHandler(thrownException: TypeKind, handlers: List[BasicBlock]): Option[(BasicBlock, TypeKind)] = {
|
||||
|
||||
// function to extract exeption type
|
||||
def extractException(bb: BasicBlock): Option[TypeKind] =
|
||||
if (bb.length >= 1) {
|
||||
bb.head match {
|
||||
case LOAD_EXCEPTION(clazz) => Some(toTypeKind(clazz.tpe))
|
||||
case _ => None
|
||||
}
|
||||
} else
|
||||
None
|
||||
|
||||
var finalHandlerData: Option[(BasicBlock, TypeKind)] = None
|
||||
|
||||
breakable {
|
||||
for (handler <- handlers)
|
||||
extractException(handler) match {
|
||||
case Some(caughtException) if (thrownException <:< caughtException) =>
|
||||
// we'll do inlining here: createdException <:< thrownException <:< caughtException, good!
|
||||
finalHandlerData = Some((handler, caughtException))
|
||||
break
|
||||
case Some(caughtException) if (caughtException <:< thrownException) =>
|
||||
// we can't do inlining here, the handling mechanism is more precise than we can reason about
|
||||
finalHandlerData = None
|
||||
break
|
||||
case _ =>
|
||||
// no result yet, look deeper in the handler stack :)
|
||||
}
|
||||
for (handler <- handlers ; LOAD_EXCEPTION(clazz) <- handler take 1) {
|
||||
val caughtException = toTypeKind(clazz.tpe)
|
||||
// we'll do inlining here: createdException <:< thrownException <:< caughtException, good!
|
||||
if (thrownException <:< caughtException)
|
||||
return Some((handler, caughtException))
|
||||
// we can't do inlining here, the handling mechanism is more precise than we can reason about
|
||||
if (caughtException <:< thrownException)
|
||||
return None
|
||||
// no result yet, look deeper in the handler stack
|
||||
}
|
||||
|
||||
finalHandlerData
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function takes care of duplicating the basic block code for inlining the handler
|
||||
*
|
||||
* Note: This function does not duplicate the same basic block twice. It wil contain a map of the duplicated
|
||||
* basic blocks
|
||||
*/
|
||||
def duplicateExceptionHandlerWithCaching(handler: BasicBlock): Option[(Option[Local], BasicBlock)] = {
|
||||
|
||||
if (!(handlerCopies contains handler))
|
||||
handlerCopies = handlerCopies + (handler -> duplicateExceptionHandler(handler))
|
||||
|
||||
handlerCopies(handler)
|
||||
}
|
||||
private def duplicateExceptionHandlerCache(handler: BasicBlock) =
|
||||
handlerCopies.getOrElseUpdate(handler, duplicateExceptionHandler(handler))
|
||||
|
||||
/** This function takes care of actual duplication */
|
||||
def duplicateExceptionHandler(handler: BasicBlock): Option[(Option[Local], BasicBlock)] = {
|
||||
private def duplicateExceptionHandler(handler: BasicBlock): Option[(Option[Local], BasicBlock)] = {
|
||||
log(" duplicating handler block " + handler)
|
||||
|
||||
log(" duplicating handler block " + handler.toString)
|
||||
|
||||
// Sanitiy checks
|
||||
var canDuplicate = true
|
||||
if (handler.length < 2) canDuplicate = false
|
||||
if (!(handler(0).isInstanceOf[LOAD_EXCEPTION])) canDuplicate = false
|
||||
|
||||
canDuplicate match {
|
||||
case true =>
|
||||
|
||||
val LOAD_EXCEPTION(caughtClass) = handler(0)
|
||||
handler take 2 match {
|
||||
case Seq(LOAD_EXCEPTION(caughtClass), next) =>
|
||||
val (dropCount, exceptionLocal) = next match {
|
||||
case STORE_LOCAL(local) => (2, Some(local)) // we drop both LOAD_EXCEPTION and STORE_LOCAL
|
||||
case _ => (1, None) // we only drop the LOAD_EXCEPTION and expect the exception on the stack
|
||||
}
|
||||
val caughtException = toTypeKind(caughtClass.tpe)
|
||||
|
||||
val exceptionLocal: Option[Local] =
|
||||
if (handler(1).isInstanceOf[STORE_LOCAL])
|
||||
STORE_LOCAL.unapply(handler(1).asInstanceOf[STORE_LOCAL])
|
||||
else
|
||||
None
|
||||
|
||||
val dropIntstructions =
|
||||
if (exceptionLocal == None)
|
||||
1 // we only drop the LOAD_EXCEPTION and expect the exception on the stack
|
||||
else
|
||||
2 // we drop both LOAD_EXCEPTION and STORE_LOCAL
|
||||
|
||||
// copy the exception handler code once again, dropping the LOAD_EXCEPTION
|
||||
val copy = handler.code.newBlock
|
||||
for (instr <- handler.drop(dropIntstructions))
|
||||
copy.emit(instr, instr.pos)
|
||||
copy.close
|
||||
copy.emitOnly(handler drop dropCount: _*)
|
||||
|
||||
// extend the handlers of the handler to the copy
|
||||
for (parentHandler <-handler.method.exh)
|
||||
if (parentHandler covers handler) {
|
||||
parentHandler.addCoveredBlock(copy)
|
||||
// notify the parent handler that the successors changed
|
||||
parentHandler.startBlock.touched = true
|
||||
}
|
||||
for (parentHandler <- handler.method.exh ; if parentHandler covers handler) {
|
||||
parentHandler.addCoveredBlock(copy)
|
||||
// notify the parent handler that the successors changed
|
||||
parentHandler.startBlock.touched = true
|
||||
}
|
||||
|
||||
// notify the successors of the inlined handler might have changed
|
||||
copy.touched = true
|
||||
copy.touched = true
|
||||
handler.touched = true
|
||||
|
||||
log(" duplicated handler block " + handler.toString + " to " + copy.toString)
|
||||
log(" duplicated handler block " + handler + " to " + copy)
|
||||
|
||||
// announce the duplicate handler
|
||||
handlerCopiesInverted = handlerCopiesInverted + (copy -> ((handler, caughtException)))
|
||||
todoBlocks = copy :: todoBlocks
|
||||
handlerCopiesInverted(copy) = ((handler, caughtException))
|
||||
todoBlocks ::= copy
|
||||
|
||||
Some((exceptionLocal, copy))
|
||||
|
||||
case false =>
|
||||
assert(currentClass ne null)
|
||||
case _ =>
|
||||
currentClass.cunit.warning(NoPosition, "Unable to inline the exception handler due to incorrect format:\n" +
|
||||
handler.mkString("\n"))
|
||||
None
|
||||
|
|
Loading…
Reference in New Issue