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:
+20
-45
@@ -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);
|
||||
|
||||
+9
-15
@@ -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) {
|
||||
|
||||
}
|
||||
+15
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user