public static Instruction ApplyLocalVariableTranslation(
this Instruction instruction,
int translate,
IEnumerable<VariableDefinition> variables)
{
Contract.Requires(instruction != null);
Contract.Requires(variables != null);
int? initialVariableIndex;
if (translate == 0 || !instruction.TryGetVariableIndex(out initialVariableIndex)) { return instruction; }
Contract.Assert(initialVariableIndex.HasValue);
var newIndex = initialVariableIndex.Value + translate;
if (newIndex < 0)
{
throw new InvalidOperationException("A variable index less than 0 cannot be used.");
}
if (newIndex > ushort.MaxValue)
{
throw new InvalidOperationException($"A variable index greater than {ushort.MaxValue} cannot be used.");
}
var indexedVariable = new Lazy<VariableDefinition>(
() =>
{
var value = instruction.Operand as VariableDefinition ??
variables.FirstOrDefault(variable => variable.Index == initialVariableIndex.Value);
if (value == null)
{
throw new InvalidOperationException("An instruction references a variable that cannot be found.");
}
return value;
});
if (instruction.OpCode.Code.IsStoreVariableOpCode())
{
switch(newIndex)
{
case 0:
return Instruction.Create(OpCodes.Stloc_0);
case 1:
return Instruction.Create(OpCodes.Stloc_1);
case 2:
return Instruction.Create(OpCodes.Stloc_2);
case 3:
return Instruction.Create(OpCodes.Stloc_3);
default:
// only make a new instruction if the opcode needs to change
if (newIndex <= byte.MaxValue)
{
return instruction.OpCode.Code == Code.Stloc_S ?
instruction :
Instruction.Create(OpCodes.Stloc_S, indexedVariable.Value);
}
return instruction.OpCode.Code == Code.Stloc ?
instruction :
Instruction.Create(OpCodes.Stloc, indexedVariable.Value);
}
}
if (instruction.OpCode.Code.IsLoadVariableOpCode())
{
switch (newIndex)
{
case 0:
return Instruction.Create(OpCodes.Ldloc_0);
case 1:
return Instruction.Create(OpCodes.Ldloc_1);
case 2:
return Instruction.Create(OpCodes.Ldloc_2);
case 3:
return Instruction.Create(OpCodes.Ldloc_3);
default:
// only make a new instruction if the opcode needs to change
if (newIndex <= byte.MaxValue)
{
return instruction.OpCode.Code == Code.Ldloc_S ?
instruction :
Instruction.Create(OpCodes.Ldloc_S, indexedVariable.Value);
}
return instruction.OpCode.Code == Code.Ldloc ?
instruction :
Instruction.Create(OpCodes.Ldloc, indexedVariable.Value);
}
}
if (!instruction.OpCode.Code.IsLoadVariableAddressOpCode())
{
throw new InvalidOperationException("Expected a variable store or load opcode.");
}
// only make a new instruction if the opcode needs to change
if (newIndex <= byte.MaxValue)
{
return instruction.OpCode.Code == Code.Ldloca_S ?
instruction :
Instruction.Create(OpCodes.Ldloca_S, indexedVariable.Value);
}
return instruction.OpCode.Code == Code.Ldloca ?
instruction :
Instruction.Create(OpCodes.Ldloca, indexedVariable.Value);
}