summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2015-07-02 20:17:57 +0200
committerLukas Rytz <lukas.rytz@gmail.com>2015-07-23 15:02:12 +0200
commit1b0703e74d22ed54b95a134216835569a20d2325 (patch)
treefefe75959b5711ed54a30a3813562d79893a46a8
parente511375a902e19cbed2340e7b66272692307df93 (diff)
downloadscala-1b0703e74d22ed54b95a134216835569a20d2325.tar.gz
scala-1b0703e74d22ed54b95a134216835569a20d2325.tar.bz2
scala-1b0703e74d22ed54b95a134216835569a20d2325.zip
[backport] SI-9376 don't crash when inlining a closure body that throws.
If the closure body method has return type Nothing$, add an `ATHROW` instruction after the callsite. This is required for computing stack map frames, as explained in a comment in BCodeBodyBuilder.adapt. Similar for closure bodies with return type Null$.
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala22
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala4
2 files changed, 25 insertions, 1 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
index 0ec550981a..cd36fd8bba 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
@@ -12,7 +12,7 @@ import scala.collection.mutable
import scala.reflect.internal.util.Collections._
import scala.tools.asm.commons.CodeSizeEvaluator
import scala.tools.asm.tree.analysis._
-import scala.tools.asm.{MethodWriter, ClassWriter, Label, Opcodes}
+import scala.tools.asm.{MethodWriter, ClassWriter, Label, Opcodes, Type}
import scala.tools.asm.tree._
import GenBCode._
import scala.collection.convert.decorateAsScala._
@@ -331,6 +331,26 @@ object BytecodeUtils {
}
/**
+ * This method is used by optimizer components to eliminate phantom values of instruction
+ * that load a value of type `Nothing$` or `Null$`. Such values on the stack don't interact well
+ * with stack map frames.
+ *
+ * For example, `opt.getOrElse(throw e)` is re-written to an invocation of the lambda body, a
+ * method with return type `Nothing$`. Similarly for `opt.getOrElse(null)` and `Null$`.
+ *
+ * During bytecode generation this is handled by BCodeBodyBuilder.adapt. See the comment in that
+ * method which explains the issue with such phantom values.
+ */
+ def fixLoadedNothingOrNullValue(loadedType: Type, loadInstr: AbstractInsnNode, methodNode: MethodNode, bTypes: BTypes): Unit = {
+ if (loadedType == bTypes.coreBTypes.RT_NOTHING.toASMType) {
+ methodNode.instructions.insert(loadInstr, new InsnNode(Opcodes.ATHROW))
+ } else if (loadedType == bTypes.coreBTypes.RT_NULL.toASMType) {
+ methodNode.instructions.insert(loadInstr, new InsnNode(Opcodes.ACONST_NULL))
+ methodNode.instructions.insert(loadInstr, new InsnNode(Opcodes.POP))
+ }
+ }
+
+ /**
* A wrapper to make ASM's Analyzer a bit easier to use.
*/
class AsmAnalyzer[V <: Value](methodNode: MethodNode, classInternalName: InternalName, interpreter: Interpreter[V] = new BasicInterpreter) {
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
index 1648a53ed8..743a454678 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
@@ -283,6 +283,10 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
val isInterface = bodyOpcode == INVOKEINTERFACE
val bodyInvocation = new MethodInsnNode(bodyOpcode, lambdaBodyHandle.getOwner, lambdaBodyHandle.getName, lambdaBodyHandle.getDesc, isInterface)
methodNode.instructions.insertBefore(invocation, bodyInvocation)
+
+ val returnType = Type.getReturnType(lambdaBodyHandle.getDesc)
+ fixLoadedNothingOrNullValue(returnType, bodyInvocation, methodNode, btypes) // see comment of that method
+
methodNode.instructions.remove(invocation)
// update the call graph