diff --git a/runtime/JsrInliner.cs b/runtime/JsrInliner.cs index 06bfc080..cea7484a 100644 --- a/runtime/JsrInliner.cs +++ b/runtime/JsrInliner.cs @@ -431,17 +431,7 @@ namespace IKVM.Internal { if (method.ExceptionTable[j].startIndex <= i && i < method.ExceptionTable[j].endIndex) { - // NOTE this used to be CopyLocalsAndSubroutines, but it doesn't (always) make - // sense to copy the subroutine state - // TODO figure out if there are circumstances under which it does make sense - // to copy the active subroutine state - // UPDATE subroutines must be copied as well, but I think I now have a better - // understanding of subroutine merges, so the problems that triggered the previous - // change here hopefully won't arise anymore - InstructionState ex = state[i].CopyLocalsAndSubroutines(); - ex.PushObject(); - int idx = method.ExceptionTable[j].handlerIndex; - state[idx] += ex; + MergeExceptionHandler(method.ExceptionTable[j].handlerIndex, state[i]); } } state[i].CopyTo(s); @@ -1060,6 +1050,13 @@ namespace IKVM.Internal { throw new VerifyError("Stack size too large"); } + for (int j = 0; j < method.ExceptionTable.Length; j++) + { + if (method.ExceptionTable[j].endIndex == i + 1) + { + MergeExceptionHandler(method.ExceptionTable[j].handlerIndex, s); + } + } try { // another big switch to handle the opcode targets @@ -1278,6 +1275,20 @@ namespace IKVM.Internal } } + private void MergeExceptionHandler(int handlerIndex, InstructionState curr) + { + // NOTE this used to be CopyLocalsAndSubroutines, but it doesn't (always) make + // sense to copy the subroutine state + // TODO figure out if there are circumstances under which it does make sense + // to copy the active subroutine state + // UPDATE subroutines must be copied as well, but I think I now have a better + // understanding of subroutine merges, so the problems that triggered the previous + // change here hopefully won't arise anymore + InstructionState ex = curr.CopyLocalsAndSubroutines(); + ex.PushObject(); + state[handlerIndex] += ex; + } + private ClassFile.ConstantPoolItemMI GetMethodref(int index) { try diff --git a/runtime/LocalVars.cs b/runtime/LocalVars.cs index a56bbd1e..21f5399e 100644 --- a/runtime/LocalVars.cs +++ b/runtime/LocalVars.cs @@ -421,6 +421,15 @@ struct LocalVarInfo && (instructions[i].NormalizedOpCode != NormalizedByteCode.__astore || !VerifierTypeWrapper.IsFaultBlockException(codeInfo.GetRawStackTypeWrapper(i, 0)))) { curr.Store(i, instructions[i].NormalizedArg1); + // if this is a store at the end of an exception block, + // we need to propagate the new state to the exception handler + for (int j = 0; j < exceptions.Length; j++) + { + if (exceptions[j].endIndex == i + 1) + { + state[exceptions[j].handlerIndex].Merge(curr); + } + } } if (instructions[i].NormalizedOpCode == NormalizedByteCode.__invokespecial) diff --git a/runtime/verifier.cs b/runtime/verifier.cs index dd5356eb..77ebe897 100644 --- a/runtime/verifier.cs +++ b/runtime/verifier.cs @@ -1296,26 +1296,7 @@ sealed class MethodAnalyzer { if(method.ExceptionTable[j].startIndex <= i && i < method.ExceptionTable[j].endIndex) { - int idx = method.ExceptionTable[j].handlerIndex; - InstructionState ex = state[i].CopyLocals(); - int catch_type = method.ExceptionTable[j].catch_type; - if(catch_type == 0) - { - TypeWrapper tw; - if (!faultTypes.TryGetValue(idx, out tw)) - { - tw = VerifierTypeWrapper.MakeFaultBlockException(this, idx); - faultTypes.Add(idx, tw); - } - ex.PushType(tw); - } - else - { - // TODO if the exception type is unloadable we should consider pushing - // Throwable as the type and recording a loader constraint - ex.PushType(GetConstantPoolClassType(catch_type)); - } - state[idx] += ex; + MergeExceptionHandler(j, state[i]); } } state[i].CopyTo(s); @@ -2275,6 +2256,13 @@ sealed class MethodAnalyzer { throw new VerifyError("Stack size too large"); } + for(int j = 0; j < method.ExceptionTable.Length; j++) + { + if(method.ExceptionTable[j].endIndex == i + 1) + { + MergeExceptionHandler(j, s); + } + } try { switch(ByteCodeMetaData.GetFlowControl(instr.NormalizedOpCode)) @@ -2327,6 +2315,30 @@ sealed class MethodAnalyzer } } + private void MergeExceptionHandler(int exceptionIndex, InstructionState curr) + { + int idx = method.ExceptionTable[exceptionIndex].handlerIndex; + InstructionState ex = curr.CopyLocals(); + int catch_type = method.ExceptionTable[exceptionIndex].catch_type; + if (catch_type == 0) + { + TypeWrapper tw; + if (!faultTypes.TryGetValue(idx, out tw)) + { + tw = VerifierTypeWrapper.MakeFaultBlockException(this, idx); + faultTypes.Add(idx, tw); + } + ex.PushType(tw); + } + else + { + // TODO if the exception type is unloadable we should consider pushing + // Throwable as the type and recording a loader constraint + ex.PushType(GetConstantPoolClassType(catch_type)); + } + state[idx] += ex; + } + // this verification pass must run on the unmodified bytecode stream private void VerifyPassTwo() {