2012-10-07 4 views

Répondre

8

Voici comment injecter un final.

Vous devez d'abord corriger vos déclarations de retour. Vous n'en voulez qu'un.

Instruction FixReturns() 
{ 
    if (Method.ReturnType == TypeSystem.Void) 
    { 
     var instructions = body.Instructions; 
     var lastRet = Instruction.Create(OpCodes.Ret); 
     instructions.Add(lastRet); 

     for (var index = 0; index < instructions.Count - 1; index++) 
     { 
      var instruction = instructions[index]; 
      if (instruction.OpCode == OpCodes.Ret) 
      { 
       instructions[index] = Instruction.Create(OpCodes.Leave, lastRet); 
      } 
     } 
     return lastRet; 
    } 
    else 
    { 
     var instructions = body.Instructions; 
     var returnVariable = new VariableDefinition("methodTimerReturn", Method.ReturnType); 
     body.Variables.Add(returnVariable); 
     var lastLd = Instruction.Create(OpCodes.Ldloc, returnVariable); 
     instructions.Add(lastLd); 
     instructions.Add(Instruction.Create(OpCodes.Ret)); 

     for (var index = 0; index < instructions.Count - 2; index++) 
     { 
      var instruction = instructions[index]; 
      if (instruction.OpCode == OpCodes.Ret) 
      { 
       instructions[index] = Instruction.Create(OpCodes.Leave, lastLd); 
       instructions.Insert(index, Instruction.Create(OpCodes.Stloc, returnVariable)); 
       index++; 
      } 
     } 
     return lastLd; 
    } 
} 

Ensuite, trouvez la première instruction. Vous devrez ignorer 2 s'il s'agit d'un constructeur d'instance.

Instruction FirstInstructionSkipCtor() 
{ 
    if (Method.IsConstructor && !Method.IsStatic) 
    { 
     return body.Instructions.Skip(2).First(); 
    } 
    return body.Instructions.First(); 
} 

point ensuite ensemble

void InnerProcess() 
{ 
    body = Method.Body; 
    body.SimplifyMacros(); 
    ilProcessor = body.GetILProcessor(); 

    var returnInstruction = FixReturns(); 

    var firstInstruction = FirstInstructionSkipCtor(); 

    var beforeReturn = Instruction.Create(OpCodes.Nop); 
    ilProcessor.InsertBefore(returnInstruction, beforeReturn); 

    InjectIlForFinaly(returnInstruction); 

    var handler = new ExceptionHandler(ExceptionHandlerType.Finally) 
     { 
      TryStart = firstInstruction, 
      TryEnd = beforeReturn, 
      HandlerStart = beforeReturn, 
      HandlerEnd = returnInstruction, 
     }; 

    body.ExceptionHandlers.Add(handler); 
    body.InitLocals = true; 
    body.OptimizeMacros(); 
} 
+0

Donc, c'est fondamentalement le même processus que décrit dans ce post, en passant simplement ExceptionHandlerType.Finalement dans le nouveau ExceptionHandler(). Et si vous voulez essayer/catch/finally, vous créez deux instances "ExceptionHandler" et les ajoutez à la méthode, une pour le catch, une pour le finally. Si c'est le cas, les noms sont certainement déroutants puisque vous ne créez pas vraiment de nouveaux gestionnaires d'exceptions, au moins pour 'finally'. Merci pour les pointeurs. – naasking

1

trouvé l'exemple vérifié très instructif et utile. J'ai rencontré un problème, cependant, dans des conditions plus complexes. Dans FixReturns(), à la fois les portées retournées void et non-void, la méthode de changement de ret -> laisse à travers la création de nouvelles instructions les orphelins les originaux. Cela peut laisser d'autres instructions avec l'orphelin en tant qu'opérande (par exemple, des branches à la ret originale). Le code résultant finit par être invalide.

Nous avons simplement mis à jour la paire d'opcode/opérande des instructions ret existantes par rapport à la création de nouvelles et tout semble bien.

Cheers.

Questions connexes