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