Cette question concerne l'analyse de pile statique du code C# IL personnalisé et la façon de concevoir les opcodes pour satisfaire le compilateur.Modification du code C# IL - conserver la pile intacte
J'ai un code qui modifie les méthodes C# existantes en y ajoutant mon propre code. Pour éviter que la méthode d'origine ne retourne avant que mon code ne soit exécuté, il remplace tous les opcodes RET par un BR endlabel et ajoute cette étiquette à la fin du code d'origine. Je rajoute ensuite plus de code là et enfin un RET.
Tout cela fonctionne très bien en général, mais échoue sur certaines méthodes. Voici un exemple simple:
public static string SomeMethod(int val)
{
switch (val)
{
case 0:
return "string1".convert();
case 1:
return "string2".convert();
case 2:
return "string3".convert();
// ...
}
return "";
}
qui est représenté par ce code IL:
.method public hidebysig static string SomeMethod(int32 val) cil managed
{
.maxstack 1
.locals val ([0] int32 num)
L_0000: ldarg.0
L_0001: stloc.0
L_0002: ldloc.0
L_0003: switch (L_002e, L_004f, L_0044, ...)
L_002c: br.s L_0091
L_002e: ldstr "string1"
L_0033: call string Foo::convert(string)
L_0038: ret
L_0039: ldstr "string2"
L_003e: call string Foo::convert(string)
L_0043: ret
L_0044: ldstr "string3"
L_0049: call string Foo::convert(string)
L_004e: ret
...
L_0091: ldstr ""
L_0096: ret
}
Après mon programme a modifié, le code ressemble à ceci:
.method public hidebysig static string SomeMethod(int32 val) cil managed
{
.maxstack 1
.locals val ([0] int32 num)
L_0000: ldarg.0
L_0001: stloc.0
L_0002: ldloc.0
L_0003: switch (L_002e, L_004f, L_0044, ...)
L_002c: br.s L_0091
L_002e: ldstr "string1"
L_0033: call string Foo::convert(string)
L_0038: br L_009b // was ret
L_0039: ldstr "string2"
L_003e: call string Foo::convert(string)
L_0043: br L_009b // was ret
L_0044: ldstr "string3"
L_0049: call string Foo::convert(string)
L_004e: br L_009b // was ret
...
L_0091: ldstr ""
L_0096: br L_009b // was ret
L_009b: my code here
...
L_0200: ret
}
et je reçois une erreur de compilation:
Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for FooBar ---> System.InvalidProgramException: Invalid IL code in (wrapper dynamic-method) Foo:SomeMethod (int): IL_0000: ldnull
Existe-t-il un moyen simple de remplacer les TER de manière générique et de garder l'analyseur statique satisfait?
Résolu le problème. Le remplacement de RET par BR augmente la longueur du code et les sauts courts peuvent devenir illégaux. La solution est de les remplacer par de longs sauts. Testé et fonctionne. –
Vous pouvez également utiliser une clause try-finally; cela évitera tous vos problèmes. Bien sûr, cela n'a de sens que si vous voulez * toujours * exécuter ce code - il s'exécutera également sur une exception. Selon le code que vous injectez, cela peut être une bonne chose ou une mauvaise chose. – Luaan
bonne prise sur vous propre erreur :) vous devriez répondre à vous-même –