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
private set
public val coveringFromInnermost: List<TryCatchBlockNodeInfo>
get() = tryBlocksMetaInfo.currentIntervals.reverse()
public fun getStartNodes(label: LabelNode): List<TryCatchBlockNodeInfo> {
return tryBlocksMetaInfo.intervalStarts.get(label)
}
@@ -53,48 +50,14 @@ public abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) {
}
if (curInstr is LabelNode) {
updateCoveringTryBlocks(curInstr, directOrder)
updateCoveringLocalVars(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)
tryBlocksMetaInfo.processCurrent(curInstr, directOrder)
localVarsMetaInfo.processCurrent(curInstr, directOrder)
}
}
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 ->
var result = instructionIndex(t1.handler) - instructionIndex(t2.handler)
if (result == 0) {
@@ -107,11 +70,13 @@ public abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) {
result
}
Collections.sort<TryCatchBlockNodeInfo>(tryBlocksMetaInfo.allIntervals, comp)
Collections.sort<TryCatchBlockNodeInfo>(intervals, comp)
return intervals;
}
protected fun substituteTryBlockNodes(node: MethodNode) {
node.tryCatchBlocks.clear()
sortTryCatchBlocks(tryBlocksMetaInfo.allIntervals)
for (info in tryBlocksMetaInfo.getMeaningfulIntervals()) {
node.tryCatchBlocks.add(info.node)
}
@@ -134,7 +99,7 @@ class IntervalMetaInfo<T : SplittableInterval<T>> {
val allIntervals: ArrayList<T> = arrayListOf()
val currentIntervals: Stack<T> = Stack()
val currentIntervals: MutableSet<T> = linkedSetOf()
fun addNewInterval(newInfo: T) {
intervalStarts.put(newInfo.startLabel, newInfo)
@@ -156,6 +121,18 @@ class IntervalMetaInfo<T : SplittableInterval<T>> {
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> {
val splittedPair = interval.split(by, keepStart)
if (!keepStart) {
@@ -167,9 +144,7 @@ class IntervalMetaInfo<T : SplittableInterval<T>> {
return splittedPair
}
fun closeIntervals(curIns: LabelNode, directOrder: Boolean) = if (!directOrder) intervalStarts.get(curIns) else intervalEnds.get(curIns)
fun openIntervals(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)
}
private fun Interval.isMeaningless(): Boolean {
@@ -698,7 +698,6 @@ public class InlineCodegen extends CallGenerator {
curInstr = curInstr.getNext();
}
processor.sortTryCatchBlocks();
processor.substituteTryBlockNodes(intoNode);
//processor.substituteLocalVarTable(intoNode);
@@ -128,10 +128,12 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
continue;
}
List<TryCatchBlockNodeInfo> currentCoveringNodesFromOuterMost = getTryBlocksMetaInfo().getCurrentIntervals();
checkCoveringBlocksInvariant(currentCoveringNodesFromOuterMost);
if (currentCoveringNodesFromOuterMost.isEmpty() ||
currentCoveringNodesFromOuterMost.get(0).getOnlyCopyNotProcess()) {
List<TryCatchBlockNodeInfo> currentCoveringNodesFromInnermost =
sortTryCatchBlocks(new ArrayList<TryCatchBlockNodeInfo>(getTryBlocksMetaInfo().getCurrentIntervals()));
checkCoveringBlocksInvariant(Lists.reverse(currentCoveringNodesFromInnermost));
if (currentCoveringNodesFromInnermost.isEmpty() ||
currentCoveringNodesFromInnermost.get(currentCoveringNodesFromInnermost.size() - 1).getOnlyCopyNotProcess()) {
curIns = curIns.getPrevious();
continue;
}
@@ -146,14 +148,14 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
// 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.
// 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();
checkClusterInvariant(clustersFromInnermost);
List<TryCatchBlockNodeInfo> additionalNodesToSplit = new ArrayList<TryCatchBlockNodeInfo>();
while (tryCatchBlockIterator.hasNext()) {
TryBlockCluster clusterToFindFinally = tryCatchBlockIterator.next();
TryBlockCluster<TryCatchBlockNodeInfo> clusterToFindFinally = tryCatchBlockIterator.next();
List<TryCatchBlockNodeInfo> clusterBlocks = clusterToFindFinally.getBlocks();
TryCatchBlockNodeInfo nodeWithDefaultHandlerIfExists = clusterBlocks.get(clusterBlocks.size() - 1);
@@ -401,7 +403,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
//TODO add assert
}
sortTryCatchBlocks();
sortTryCatchBlocks(tryBlocksMetaInfo.getAllIntervals());
}
private static LabelNode getNewOrOldLabel(LabelNode oldHandler, @NotNull Set<LabelNode> labelsInsideFinally) {
@@ -412,14 +414,6 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
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) {
for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) {
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);
}
}
@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);
}
}
@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);
}
}
}
}