Fix for KT-7792: Invalid class file generation: java.lang.ClassFormatError: Invalid length 65522 in LocalVariableTable in class file

#KT-7792 Fixed
This commit is contained in:
Michael Bogdanov
2015-05-22 15:21:20 +03:00
parent 37c47ac980
commit 8cf21f24cd
7 changed files with 114 additions and 61 deletions
@@ -34,9 +34,6 @@ public abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) {
public var nextFreeLocalIndex: Int = parameterSize public var nextFreeLocalIndex: Int = parameterSize
private set private set
public val coveringFromInnermost: List<TryCatchBlockNodeInfo>
get() = tryBlocksMetaInfo.currentIntervals.reverse()
public fun getStartNodes(label: LabelNode): List<TryCatchBlockNodeInfo> { public fun getStartNodes(label: LabelNode): List<TryCatchBlockNodeInfo> {
return tryBlocksMetaInfo.intervalStarts.get(label) return tryBlocksMetaInfo.intervalStarts.get(label)
} }
@@ -53,48 +50,14 @@ public abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) {
} }
if (curInstr is LabelNode) { if (curInstr is LabelNode) {
updateCoveringTryBlocks(curInstr, directOrder) tryBlocksMetaInfo.processCurrent(curInstr, directOrder)
updateCoveringLocalVars(curInstr, directOrder) localVarsMetaInfo.processCurrent(curInstr, directOrder)
}
}
//Keep information about try blocks that cover current instruction -
// pushing and popping it to stack entering and exiting tryCatchBlock start and end labels
protected open fun updateCoveringTryBlocks(curIns: LabelNode, directOrder: Boolean) {
for (startNode in tryBlocksMetaInfo.closeIntervals(curIns, directOrder)) {
assert(!startNode.isEmpty(), {"Try block should be non-empty"})
val pop = tryBlocksMetaInfo.currentIntervals.pop()
//Temporary disabled cause during patched structure of exceptions changed
// if (startNode != pop) {
// throw RuntimeException("Wrong try-catch structure " + startNode + " " + pop + " " + infosToClose.size())
// };
}
//Reversing list order cause we should pop external block before internal one
// (originally internal blocks goes before external one, such invariant preserved via sortTryCatchBlocks method)
for (info in tryBlocksMetaInfo.openIntervals(curIns, directOrder).reverse()) {
assert(!info.isEmpty(), {"Try block should be non-empty"})
tryBlocksMetaInfo.currentIntervals.add(info)
}
}
protected open fun updateCoveringLocalVars(curIns: LabelNode, directOrder: Boolean) {
localVarsMetaInfo.closeIntervals(curIns, directOrder).filterNot {
it.isEmpty()
} forEach {
localVarsMetaInfo.currentIntervals.pop()
}
localVarsMetaInfo.openIntervals(curIns, directOrder).filterNot {
it.isEmpty()
} forEach {
localVarsMetaInfo.currentIntervals.add(it)
} }
} }
public abstract fun instructionIndex(inst: AbstractInsnNode): Int public abstract fun instructionIndex(inst: AbstractInsnNode): Int
public fun sortTryCatchBlocks() { public fun sortTryCatchBlocks(intervals: List<TryCatchBlockNodeInfo>): List<TryCatchBlockNodeInfo> {
val comp = Comparator { t1: TryCatchBlockNodeInfo, t2: TryCatchBlockNodeInfo -> val comp = Comparator { t1: TryCatchBlockNodeInfo, t2: TryCatchBlockNodeInfo ->
var result = instructionIndex(t1.handler) - instructionIndex(t2.handler) var result = instructionIndex(t1.handler) - instructionIndex(t2.handler)
if (result == 0) { if (result == 0) {
@@ -107,11 +70,13 @@ public abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) {
result result
} }
Collections.sort<TryCatchBlockNodeInfo>(tryBlocksMetaInfo.allIntervals, comp) Collections.sort<TryCatchBlockNodeInfo>(intervals, comp)
return intervals;
} }
protected fun substituteTryBlockNodes(node: MethodNode) { protected fun substituteTryBlockNodes(node: MethodNode) {
node.tryCatchBlocks.clear() node.tryCatchBlocks.clear()
sortTryCatchBlocks(tryBlocksMetaInfo.allIntervals)
for (info in tryBlocksMetaInfo.getMeaningfulIntervals()) { for (info in tryBlocksMetaInfo.getMeaningfulIntervals()) {
node.tryCatchBlocks.add(info.node) node.tryCatchBlocks.add(info.node)
} }
@@ -134,7 +99,7 @@ class IntervalMetaInfo<T : SplittableInterval<T>> {
val allIntervals: ArrayList<T> = arrayListOf() val allIntervals: ArrayList<T> = arrayListOf()
val currentIntervals: Stack<T> = Stack() val currentIntervals: MutableSet<T> = linkedSetOf()
fun addNewInterval(newInfo: T) { fun addNewInterval(newInfo: T) {
intervalStarts.put(newInfo.startLabel, newInfo) intervalStarts.put(newInfo.startLabel, newInfo)
@@ -156,6 +121,18 @@ class IntervalMetaInfo<T : SplittableInterval<T>> {
return currentIntervals.map { split(it, by, keepStart) } return currentIntervals.map { split(it, by, keepStart) }
} }
fun processCurrent(curIns: LabelNode, directOrder: Boolean) {
getInterval(curIns, directOrder).forEach {
val b = currentIntervals.add(it)
assert(b, "Wrong interval structure: $curIns, $it")
}
getInterval(curIns, !directOrder).forEach {
val b = currentIntervals.remove(it)
assert(b, "Wrong interval structure: $curIns, $it")
}
}
fun split(interval: T, by : Interval, keepStart: Boolean): SplittedPair<T> { fun split(interval: T, by : Interval, keepStart: Boolean): SplittedPair<T> {
val splittedPair = interval.split(by, keepStart) val splittedPair = interval.split(by, keepStart)
if (!keepStart) { if (!keepStart) {
@@ -167,9 +144,7 @@ class IntervalMetaInfo<T : SplittableInterval<T>> {
return splittedPair return splittedPair
} }
fun closeIntervals(curIns: LabelNode, directOrder: Boolean) = if (!directOrder) intervalStarts.get(curIns) else intervalEnds.get(curIns) fun getInterval(curIns: LabelNode, isOpen: Boolean) = if (isOpen) intervalStarts.get(curIns) else intervalEnds.get(curIns)
fun openIntervals(curIns: LabelNode, directOrder: Boolean) = if (directOrder) intervalStarts.get(curIns) else intervalEnds.get(curIns)
} }
private fun Interval.isMeaningless(): Boolean { private fun Interval.isMeaningless(): Boolean {
@@ -698,7 +698,6 @@ public class InlineCodegen extends CallGenerator {
curInstr = curInstr.getNext(); curInstr = curInstr.getNext();
} }
processor.sortTryCatchBlocks();
processor.substituteTryBlockNodes(intoNode); processor.substituteTryBlockNodes(intoNode);
//processor.substituteLocalVarTable(intoNode); //processor.substituteLocalVarTable(intoNode);
@@ -128,10 +128,12 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
continue; continue;
} }
List<TryCatchBlockNodeInfo> currentCoveringNodesFromOuterMost = getTryBlocksMetaInfo().getCurrentIntervals(); List<TryCatchBlockNodeInfo> currentCoveringNodesFromInnermost =
checkCoveringBlocksInvariant(currentCoveringNodesFromOuterMost); sortTryCatchBlocks(new ArrayList<TryCatchBlockNodeInfo>(getTryBlocksMetaInfo().getCurrentIntervals()));
if (currentCoveringNodesFromOuterMost.isEmpty() || checkCoveringBlocksInvariant(Lists.reverse(currentCoveringNodesFromInnermost));
currentCoveringNodesFromOuterMost.get(0).getOnlyCopyNotProcess()) {
if (currentCoveringNodesFromInnermost.isEmpty() ||
currentCoveringNodesFromInnermost.get(currentCoveringNodesFromInnermost.size() - 1).getOnlyCopyNotProcess()) {
curIns = curIns.getPrevious(); curIns = curIns.getPrevious();
continue; continue;
} }
@@ -146,14 +148,14 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
// Each group that corresponds to try/*catches*/finally contains tryCatch block with default handler. // Each group that corresponds to try/*catches*/finally contains tryCatch block with default handler.
// For each such group we should insert corresponding finally before non-local return. // For each such group we should insert corresponding finally before non-local return.
// So we split all try blocks on current instructions to groups and process them independently // So we split all try blocks on current instructions to groups and process them independently
List<TryBlockCluster<TryCatchBlockNodeInfo>> clustersFromInnermost = InlinePackage.doClustering(getCoveringFromInnermost()); List<TryBlockCluster<TryCatchBlockNodeInfo>> clustersFromInnermost = InlinePackage.doClustering(currentCoveringNodesFromInnermost);
Iterator<TryBlockCluster<TryCatchBlockNodeInfo>> tryCatchBlockIterator = clustersFromInnermost.iterator(); Iterator<TryBlockCluster<TryCatchBlockNodeInfo>> tryCatchBlockIterator = clustersFromInnermost.iterator();
checkClusterInvariant(clustersFromInnermost); checkClusterInvariant(clustersFromInnermost);
List<TryCatchBlockNodeInfo> additionalNodesToSplit = new ArrayList<TryCatchBlockNodeInfo>(); List<TryCatchBlockNodeInfo> additionalNodesToSplit = new ArrayList<TryCatchBlockNodeInfo>();
while (tryCatchBlockIterator.hasNext()) { while (tryCatchBlockIterator.hasNext()) {
TryBlockCluster clusterToFindFinally = tryCatchBlockIterator.next(); TryBlockCluster<TryCatchBlockNodeInfo> clusterToFindFinally = tryCatchBlockIterator.next();
List<TryCatchBlockNodeInfo> clusterBlocks = clusterToFindFinally.getBlocks(); List<TryCatchBlockNodeInfo> clusterBlocks = clusterToFindFinally.getBlocks();
TryCatchBlockNodeInfo nodeWithDefaultHandlerIfExists = clusterBlocks.get(clusterBlocks.size() - 1); TryCatchBlockNodeInfo nodeWithDefaultHandlerIfExists = clusterBlocks.get(clusterBlocks.size() - 1);
@@ -401,7 +403,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
//TODO add assert //TODO add assert
} }
sortTryCatchBlocks(); sortTryCatchBlocks(tryBlocksMetaInfo.getAllIntervals());
} }
private static LabelNode getNewOrOldLabel(LabelNode oldHandler, @NotNull Set<LabelNode> labelsInsideFinally) { private static LabelNode getNewOrOldLabel(LabelNode oldHandler, @NotNull Set<LabelNode> labelsInsideFinally) {
@@ -412,14 +414,6 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
return oldHandler; return oldHandler;
} }
//Keep information about try blocks that cover current instruction -
// pushing and popping it to stack entering and exiting tryCatchBlock start and end labels
@Override
protected void updateCoveringTryBlocks(@NotNull LabelNode curIns, boolean directOrder) {
super.updateCoveringTryBlocks(curIns, directOrder);
checkCoveringBlocksInvariant(getTryBlocksMetaInfo().getCurrentIntervals());
}
private static boolean hasFinallyBlocks(List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo) { private static boolean hasFinallyBlocks(List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo) {
for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) { for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) {
if (!block.getOnlyCopyNotProcess() && block.getNode().type == null) { if (!block.getOnlyCopyNotProcess() && block.getNode().type == null) {
@@ -0,0 +1,40 @@
import test.*
public class ClassA {
val LOCK = Object()
var result = "fail"
fun test(name1: String?, name2: String, cond: Boolean) {
mySynchronized (LOCK) {
var name = name1
if (name == null) {
if (cond) {
result = "NLR" + name2
return
}
name = name2
}
result = name + name2
val length = name.length()
}
}
}
fun box(): String {
val classA = ClassA()
classA.test(null, "2", true)
if (classA.result != "NLR2") return "fail 1: ${classA.result}"
classA.test(null, "K", false)
if (classA.result != "KK") return "fail 1: ${classA.result}"
classA.test("O", "K", false)
return classA.result
}
@@ -0,0 +1,15 @@
package test
inline fun <R> mySynchronized(lock: Any, block: () -> R): R {
monitorCall(lock)
try {
return block()
}
finally {
monitorCall(lock)
}
}
fun monitorCall(lock: Any) {
}
@@ -725,6 +725,21 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
doTestMultiFileWithInlineCheck(fileName); doTestMultiFileWithInlineCheck(fileName);
} }
} }
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Variables extends AbstractBlackBoxInlineCodegenTest {
public void testAllFilesPresentInVariables() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables"), Pattern.compile("^(.+)\\.1.kt$"), true);
}
@TestMetadata("kt7792.1.kt")
public void testKt7792() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables/kt7792.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
}
} }
} }
@@ -725,6 +725,21 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
doBoxTestWithInlineCheck(fileName); doBoxTestWithInlineCheck(fileName);
} }
} }
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Variables extends AbstractCompileKotlinAgainstInlineKotlinTest {
public void testAllFilesPresentInVariables() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables"), Pattern.compile("^(.+)\\.1.kt$"), true);
}
@TestMetadata("kt7792.1.kt")
public void testKt7792() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables/kt7792.1.kt");
doBoxTestWithInlineCheck(fileName);
}
}
} }
} }