Bug fix. The local variable state at the end of an exception block (if the last instruction is a local variable store) needs to be merged into the exception handler state.

This commit is contained in:
jfrijters 2012-10-31 09:52:49 +00:00
Родитель 35d4dc950a
Коммит a68002325d
3 изменённых файлов: 63 добавлений и 31 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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)

Просмотреть файл

@ -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()
{