You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
986 lines
34 KiB
986 lines
34 KiB
3 years ago
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Linq;
|
||
|
using System.Text;
|
||
|
using System.Reflection;
|
||
|
|
||
|
using ILRuntime.Mono.Cecil;
|
||
|
using ILRuntime.Runtime;
|
||
|
using ILRuntime.Runtime.Intepreter.OpCodes;
|
||
|
using ILRuntime.Runtime.Intepreter;
|
||
|
using ILRuntime.Runtime.Intepreter.RegisterVM;
|
||
|
using ILRuntime.Runtime.Debugger;
|
||
|
using ILRuntime.CLR.TypeSystem;
|
||
|
using ILRuntime.Reflection;
|
||
|
namespace ILRuntime.CLR.Method
|
||
|
{
|
||
|
public class ILMethod : IMethod
|
||
|
{
|
||
|
OpCode[] body;
|
||
|
OpCodeR[] bodyRegister;
|
||
|
Dictionary<int, RegisterVMSymbol> registerSymbols;
|
||
|
bool symbolFixed;
|
||
|
MethodDefinition def;
|
||
|
List<IType> parameters;
|
||
|
ILRuntime.Runtime.Enviorment.AppDomain appdomain;
|
||
|
ILType declaringType;
|
||
|
ExceptionHandler[] exceptionHandler, exceptionHandlerR;
|
||
|
KeyValuePair<string, IType>[] genericParameters;
|
||
|
IType[] genericArguments;
|
||
|
Dictionary<int, int[]> jumptables, jumptablesR;
|
||
|
bool isDelegateInvoke;
|
||
|
bool jitPending;
|
||
|
ILRuntimeMethodInfo refletionMethodInfo;
|
||
|
ILRuntimeConstructorInfo reflectionCtorInfo;
|
||
|
int paramCnt, localVarCnt, stackRegisterCnt;
|
||
|
int jitFlags;
|
||
|
bool jitOnDemand;
|
||
|
bool jitImmediately;
|
||
|
int warmupCounter = 0;
|
||
|
Mono.Collections.Generic.Collection<Mono.Cecil.Cil.VariableDefinition> variables;
|
||
|
int hashCode = -1;
|
||
|
static int instance_id = 0x10000000;
|
||
|
|
||
|
const int JITWarmUpThreshold = 10;
|
||
|
|
||
|
public bool Compiling { get; set; }
|
||
|
|
||
|
public bool IsRegisterBodyReady { get { return bodyRegister != null; } }
|
||
|
|
||
|
public MethodDefinition Definition { get { return def; } }
|
||
|
|
||
|
public Dictionary<int, int[]> JumpTables { get { return jumptables; } }
|
||
|
public Dictionary<int, int[]> JumpTablesRegister { get { return jumptablesR; } }
|
||
|
|
||
|
internal Dictionary<int, RegisterVMSymbol> RegisterVMSymbols { get { return registerSymbols; } }
|
||
|
|
||
|
internal int JITFlags { get { return jitFlags; } }
|
||
|
|
||
|
internal bool IsRegisterVMSymbolFixed { get { return symbolFixed; } }
|
||
|
|
||
|
internal IDelegateAdapter DelegateAdapter { get; set; }
|
||
|
|
||
|
internal int StartLine { get; set; }
|
||
|
|
||
|
internal int EndLine { get; set; }
|
||
|
|
||
|
public ILRuntime.Runtime.Enviorment.AppDomain AppDomain { get { return appdomain; } }
|
||
|
|
||
|
public MethodInfo ReflectionMethodInfo
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (IsConstructor)
|
||
|
throw new NotSupportedException();
|
||
|
if (refletionMethodInfo == null)
|
||
|
refletionMethodInfo = new ILRuntimeMethodInfo(this);
|
||
|
return refletionMethodInfo;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public ConstructorInfo ReflectionConstructorInfo
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (!IsConstructor)
|
||
|
throw new NotSupportedException();
|
||
|
if (reflectionCtorInfo == null)
|
||
|
reflectionCtorInfo = new ILRuntimeConstructorInfo(this);
|
||
|
return reflectionCtorInfo;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal ExceptionHandler[] ExceptionHandler
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return exceptionHandler;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal ExceptionHandler[] ExceptionHandlerRegister
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return exceptionHandlerR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public string Name
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return def.Name;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public IType DeclearingType
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return declaringType;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool HasThis
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return def.HasThis;
|
||
|
}
|
||
|
}
|
||
|
public int GenericParameterCount
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (IsGenericInstance)
|
||
|
return 0;
|
||
|
return def.GenericParameters.Count;
|
||
|
}
|
||
|
}
|
||
|
public bool IsGenericInstance
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return genericParameters != null;
|
||
|
}
|
||
|
}
|
||
|
public Mono.Collections.Generic.Collection<Mono.Cecil.Cil.VariableDefinition> Variables
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return variables;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public KeyValuePair<string, IType>[] GenericArguments { get { return genericParameters; } }
|
||
|
|
||
|
public IType[] GenericArugmentsArray { get { return genericArguments; } }
|
||
|
|
||
|
public bool ShouldUseRegisterVM
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (bodyRegister != null)
|
||
|
{
|
||
|
body = null;
|
||
|
exceptionHandler = null;
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (jitImmediately)
|
||
|
{
|
||
|
InitCodeBody(true);
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (jitOnDemand)
|
||
|
{
|
||
|
warmupCounter++;
|
||
|
if (warmupCounter > JITWarmUpThreshold && !jitPending)
|
||
|
{
|
||
|
jitPending = true;
|
||
|
AppDomain.EnqueueJITCompileJob(this);
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
public ILMethod(MethodDefinition def, ILType type, ILRuntime.Runtime.Enviorment.AppDomain domain, int flags)
|
||
|
{
|
||
|
this.def = def;
|
||
|
declaringType = type;
|
||
|
this.jitFlags = flags;
|
||
|
if (def.ReturnType.IsGenericParameter)
|
||
|
{
|
||
|
ReturnType = FindGenericArgument(def.ReturnType.Name);
|
||
|
}
|
||
|
else
|
||
|
ReturnType = domain.GetType(def.ReturnType, type, this);
|
||
|
if (type.IsDelegate && def.Name == "Invoke")
|
||
|
isDelegateInvoke = true;
|
||
|
this.appdomain = domain;
|
||
|
paramCnt = def.HasParameters ? def.Parameters.Count : 0;
|
||
|
if(def.HasCustomAttributes)
|
||
|
{
|
||
|
for(int i = 0; i < def.CustomAttributes.Count; i++)
|
||
|
{
|
||
|
int f;
|
||
|
if(def.CustomAttributes[i].GetJITFlags(domain, out f))
|
||
|
{
|
||
|
this.jitFlags = f;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
jitImmediately = (jitFlags & ILRuntimeJITFlags.JITImmediately) == ILRuntimeJITFlags.JITImmediately;
|
||
|
jitOnDemand = (jitFlags & ILRuntimeJITFlags.JITOnDemand) == ILRuntimeJITFlags.JITOnDemand;
|
||
|
#if DEBUG && !DISABLE_ILRUNTIME_DEBUG
|
||
|
if (def.HasBody)
|
||
|
{
|
||
|
var sp = GetValidSequence(0, 1);
|
||
|
if (sp != null)
|
||
|
{
|
||
|
StartLine = sp.StartLine;
|
||
|
sp = GetValidSequence(def.Body.Instructions.Count - 1, -1);
|
||
|
if (sp != null)
|
||
|
{
|
||
|
EndLine = sp.EndLine;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
public void FixRegisterVMSymbol()
|
||
|
{
|
||
|
if (!symbolFixed && registerSymbols != null)
|
||
|
{
|
||
|
symbolFixed = true;
|
||
|
JITCompiler.FixSymbol(registerSymbols);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Mono.Cecil.Cil.SequencePoint GetValidSequence(int startIdx, int dir)
|
||
|
{
|
||
|
var seqMapping = def.DebugInformation.GetSequencePointMapping();
|
||
|
var cur = DebugService.FindSequencePoint(def.Body.Instructions[startIdx], seqMapping);
|
||
|
while (cur != null && cur.StartLine == 0x0feefee)
|
||
|
{
|
||
|
startIdx += dir;
|
||
|
if (startIdx >= 0 && startIdx < def.Body.Instructions.Count)
|
||
|
{
|
||
|
cur = DebugService.FindSequencePoint(def.Body.Instructions[startIdx], seqMapping);
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return cur;
|
||
|
}
|
||
|
|
||
|
public IType FindGenericArgument(string name)
|
||
|
{
|
||
|
IType res = declaringType.FindGenericArgument(name);
|
||
|
if (res == null && genericParameters != null)
|
||
|
{
|
||
|
foreach (var i in genericParameters)
|
||
|
{
|
||
|
if (i.Key == name)
|
||
|
return i.Value;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
return res;
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
internal OpCode[] Body
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (body == null)
|
||
|
InitCodeBody(false);
|
||
|
return body;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal OpCodeR[] BodyRegister
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (bodyRegister == null)
|
||
|
InitCodeBody(true);
|
||
|
return bodyRegister;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool HasBody
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return body != null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int LocalVariableCount
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return localVarCnt;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int StackRegisterCount
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return stackRegisterCnt;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool IsConstructor
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return def.IsConstructor;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool IsVirtual
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return def.IsVirtual;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool IsDelegateInvoke
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return isDelegateInvoke;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool IsStatic
|
||
|
{
|
||
|
get { return def.IsStatic; }
|
||
|
}
|
||
|
|
||
|
public int ParameterCount
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return paramCnt;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
public List<IType> Parameters
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (def.HasParameters && parameters == null)
|
||
|
{
|
||
|
InitParameters();
|
||
|
}
|
||
|
return parameters;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public IType ReturnType
|
||
|
{
|
||
|
get;
|
||
|
private set;
|
||
|
}
|
||
|
|
||
|
public void Prewarm(bool recursive)
|
||
|
{
|
||
|
HashSet<ILMethod> alreadyPrewarmed = null;
|
||
|
if (recursive)
|
||
|
{
|
||
|
alreadyPrewarmed = new HashSet<ILMethod>();
|
||
|
}
|
||
|
Prewarm(alreadyPrewarmed);
|
||
|
}
|
||
|
|
||
|
void PrewarmBody(HashSet<ILMethod> alreadyPrewarmed)
|
||
|
{
|
||
|
//当前方法用到的IType,提前InitializeMethods()。各个子调用,提前InitParameters()
|
||
|
var body = Body;
|
||
|
|
||
|
foreach (var ins in body)
|
||
|
{
|
||
|
switch (ins.Code)
|
||
|
{
|
||
|
case OpCodeEnum.Call:
|
||
|
case OpCodeEnum.Newobj:
|
||
|
case OpCodeEnum.Ldftn:
|
||
|
case OpCodeEnum.Ldvirtftn:
|
||
|
case OpCodeEnum.Callvirt:
|
||
|
{
|
||
|
var m = appdomain.GetMethod(ins.TokenInteger);
|
||
|
if (m is ILMethod)
|
||
|
{
|
||
|
ILMethod ilm = (ILMethod)m;
|
||
|
//如果参数alreadyPrewarmed不为空,则不仅prewarm当前方法,还会递归prewarm所有子调用
|
||
|
//如果参数alreadyPrewarmed为空,则只prewarm当前方法
|
||
|
if (alreadyPrewarmed != null)
|
||
|
{
|
||
|
ilm.Prewarm(alreadyPrewarmed);
|
||
|
}
|
||
|
}
|
||
|
else if (m is CLRMethod)
|
||
|
{
|
||
|
CLRMethod clrm = (CLRMethod)m;
|
||
|
ILRuntime.CLR.Utils.Extensions.GetTypeFlags(clrm.DeclearingType.TypeForCLR);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case OpCodeEnum.Ldfld:
|
||
|
case OpCodeEnum.Stfld:
|
||
|
case OpCodeEnum.Ldflda:
|
||
|
case OpCodeEnum.Ldsfld:
|
||
|
case OpCodeEnum.Ldsflda:
|
||
|
case OpCodeEnum.Stsfld:
|
||
|
case OpCodeEnum.Ldtoken:
|
||
|
{
|
||
|
//提前InitializeBaseType()
|
||
|
var t = appdomain.GetType((int)(ins.TokenLong >> 32));
|
||
|
if (t != null)
|
||
|
{
|
||
|
var baseType = t.BaseType;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PrewarmBodyRegister(HashSet<ILMethod> alreadyPrewarmed)
|
||
|
{
|
||
|
//当前方法用到的IType,提前InitializeMethods()。各个子调用,提前InitParameters()
|
||
|
var body = BodyRegister;
|
||
|
|
||
|
foreach (var ins in body)
|
||
|
{
|
||
|
switch (ins.Code)
|
||
|
{
|
||
|
case OpCodeREnum.Call:
|
||
|
case OpCodeREnum.Newobj:
|
||
|
case OpCodeREnum.Ldftn:
|
||
|
case OpCodeREnum.Ldvirtftn:
|
||
|
case OpCodeREnum.Callvirt:
|
||
|
{
|
||
|
var m = appdomain.GetMethod(ins.Operand);
|
||
|
if (m is ILMethod)
|
||
|
{
|
||
|
ILMethod ilm = (ILMethod)m;
|
||
|
//如果参数alreadyPrewarmed不为空,则不仅prewarm当前方法,还会递归prewarm所有子调用
|
||
|
//如果参数alreadyPrewarmed为空,则只prewarm当前方法
|
||
|
if (alreadyPrewarmed != null)
|
||
|
{
|
||
|
ilm.Prewarm(alreadyPrewarmed);
|
||
|
}
|
||
|
}
|
||
|
else if (m is CLRMethod)
|
||
|
{
|
||
|
CLRMethod clrm = (CLRMethod)m;
|
||
|
ILRuntime.CLR.Utils.Extensions.GetTypeFlags(clrm.DeclearingType.TypeForCLR);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case OpCodeREnum.Ldfld:
|
||
|
case OpCodeREnum.Stfld:
|
||
|
case OpCodeREnum.Ldflda:
|
||
|
case OpCodeREnum.Ldsfld:
|
||
|
case OpCodeREnum.Ldsflda:
|
||
|
case OpCodeREnum.Stsfld:
|
||
|
case OpCodeREnum.Ldtoken:
|
||
|
{
|
||
|
//提前InitializeBaseType()
|
||
|
var t = appdomain.GetType((int)(ins.OperandLong >> 32));
|
||
|
if (t != null)
|
||
|
{
|
||
|
var baseType = t.BaseType;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
private void Prewarm(HashSet<ILMethod> alreadyPrewarmed)
|
||
|
{
|
||
|
if (alreadyPrewarmed != null && alreadyPrewarmed.Add(this) == false)
|
||
|
return;
|
||
|
if (GenericParameterCount > 0 && !IsGenericInstance)
|
||
|
return;
|
||
|
//当前方法用到的CLR局部变量,提前InitializeFields()、GetTypeFlags()
|
||
|
for (int i = 0; i < LocalVariableCount; i++)
|
||
|
{
|
||
|
var v = Variables[i];
|
||
|
var vt = v.VariableType;
|
||
|
IType t;
|
||
|
if (vt.IsGenericParameter)
|
||
|
{
|
||
|
t = FindGenericArgument(vt.Name);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
t = appdomain.GetType(v.VariableType, DeclearingType, this);
|
||
|
}
|
||
|
if (t is CLRType)
|
||
|
{
|
||
|
CLRType ct = (CLRType)t;
|
||
|
var fields = ct.Fields;
|
||
|
ILRuntime.CLR.Utils.Extensions.GetTypeFlags(ct.TypeForCLR);
|
||
|
}
|
||
|
}
|
||
|
if (jitImmediately || jitOnDemand)
|
||
|
PrewarmBodyRegister(alreadyPrewarmed);
|
||
|
else
|
||
|
PrewarmBody(alreadyPrewarmed);
|
||
|
}
|
||
|
|
||
|
internal void InitCodeBody(bool register)
|
||
|
{
|
||
|
if (def.HasBody)
|
||
|
{
|
||
|
localVarCnt = def.Body.Variables.Count;
|
||
|
Dictionary<Mono.Cecil.Cil.Instruction, int> addr = new Dictionary<Mono.Cecil.Cil.Instruction, int>();
|
||
|
|
||
|
bool noRelease = false;
|
||
|
if (register)
|
||
|
{
|
||
|
JITCompiler jit = new JITCompiler(appdomain, declaringType, this);
|
||
|
bodyRegister = jit.Compile(out stackRegisterCnt, out jumptablesR, addr, out registerSymbols);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
InitStackCodeBody(addr);
|
||
|
if (jitOnDemand)
|
||
|
noRelease = bodyRegister == null;
|
||
|
}
|
||
|
if (def.Body.ExceptionHandlers.Count > 0)
|
||
|
{
|
||
|
ExceptionHandler[] ehs;
|
||
|
if (register)
|
||
|
{
|
||
|
if (exceptionHandlerR == null)
|
||
|
exceptionHandlerR = new Method.ExceptionHandler[def.Body.ExceptionHandlers.Count];
|
||
|
ehs = exceptionHandlerR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (exceptionHandler == null)
|
||
|
exceptionHandler = new Method.ExceptionHandler[def.Body.ExceptionHandlers.Count];
|
||
|
ehs = exceptionHandler;
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < def.Body.ExceptionHandlers.Count; i++)
|
||
|
{
|
||
|
var eh = def.Body.ExceptionHandlers[i];
|
||
|
ExceptionHandler e = new ExceptionHandler();
|
||
|
e.HandlerStart = addr[eh.HandlerStart];
|
||
|
e.HandlerEnd = eh.HandlerEnd != null ? addr[eh.HandlerEnd] - 1 : def.Body.Instructions.Count - 1;
|
||
|
e.TryStart = addr[eh.TryStart];
|
||
|
e.TryEnd = addr[eh.TryEnd] - 1;
|
||
|
switch (eh.HandlerType)
|
||
|
{
|
||
|
case Mono.Cecil.Cil.ExceptionHandlerType.Catch:
|
||
|
e.CatchType = appdomain.GetType(eh.CatchType, declaringType, this);
|
||
|
e.HandlerType = ExceptionHandlerType.Catch;
|
||
|
break;
|
||
|
case Mono.Cecil.Cil.ExceptionHandlerType.Finally:
|
||
|
e.HandlerType = ExceptionHandlerType.Finally;
|
||
|
break;
|
||
|
case Mono.Cecil.Cil.ExceptionHandlerType.Fault:
|
||
|
e.HandlerType = ExceptionHandlerType.Fault;
|
||
|
break;
|
||
|
default:
|
||
|
throw new NotImplementedException();
|
||
|
}
|
||
|
ehs[i] = e;
|
||
|
}
|
||
|
//Mono.Cecil.Cil.ExceptionHandlerType.
|
||
|
}
|
||
|
variables = def.Body.Variables;
|
||
|
#if !DEBUG || DISABLE_ILRUNTIME_DEBUG
|
||
|
//Release Method body to save memory
|
||
|
if(!noRelease)
|
||
|
def.Body = null;
|
||
|
#endif
|
||
|
}
|
||
|
else
|
||
|
body = new OpCode[0];
|
||
|
}
|
||
|
|
||
|
void InitStackCodeBody(Dictionary<Mono.Cecil.Cil.Instruction, int> addr)
|
||
|
{
|
||
|
body = new OpCode[def.Body.Instructions.Count];
|
||
|
for (int i = 0; i < body.Length; i++)
|
||
|
{
|
||
|
var c = def.Body.Instructions[i];
|
||
|
OpCode code = new OpCode();
|
||
|
code.Code = (OpCodeEnum)c.OpCode.Code;
|
||
|
addr[c] = i;
|
||
|
body[i] = code;
|
||
|
}
|
||
|
for (int i = 0; i < body.Length; i++)
|
||
|
{
|
||
|
var c = def.Body.Instructions[i];
|
||
|
InitToken(ref body[i], c.Operand, addr);
|
||
|
if (i > 0 && c.OpCode.Code == Mono.Cecil.Cil.Code.Callvirt && def.Body.Instructions[i - 1].OpCode.Code == Mono.Cecil.Cil.Code.Constrained)
|
||
|
{
|
||
|
body[i - 1].TokenLong = body[i].TokenInteger;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unsafe void InitToken(ref OpCode code, object token, Dictionary<Mono.Cecil.Cil.Instruction, int> addr)
|
||
|
{
|
||
|
switch (code.Code)
|
||
|
{
|
||
|
case OpCodeEnum.Leave:
|
||
|
case OpCodeEnum.Leave_S:
|
||
|
case OpCodeEnum.Br:
|
||
|
case OpCodeEnum.Br_S:
|
||
|
case OpCodeEnum.Brtrue:
|
||
|
case OpCodeEnum.Brtrue_S:
|
||
|
case OpCodeEnum.Brfalse:
|
||
|
case OpCodeEnum.Brfalse_S:
|
||
|
//比较流程控制
|
||
|
case OpCodeEnum.Beq:
|
||
|
case OpCodeEnum.Beq_S:
|
||
|
case OpCodeEnum.Bne_Un:
|
||
|
case OpCodeEnum.Bne_Un_S:
|
||
|
case OpCodeEnum.Bge:
|
||
|
case OpCodeEnum.Bge_S:
|
||
|
case OpCodeEnum.Bge_Un:
|
||
|
case OpCodeEnum.Bge_Un_S:
|
||
|
case OpCodeEnum.Bgt:
|
||
|
case OpCodeEnum.Bgt_S:
|
||
|
case OpCodeEnum.Bgt_Un:
|
||
|
case OpCodeEnum.Bgt_Un_S:
|
||
|
case OpCodeEnum.Ble:
|
||
|
case OpCodeEnum.Ble_S:
|
||
|
case OpCodeEnum.Ble_Un:
|
||
|
case OpCodeEnum.Ble_Un_S:
|
||
|
case OpCodeEnum.Blt:
|
||
|
case OpCodeEnum.Blt_S:
|
||
|
case OpCodeEnum.Blt_Un:
|
||
|
case OpCodeEnum.Blt_Un_S:
|
||
|
code.TokenInteger = addr[(Mono.Cecil.Cil.Instruction)token];
|
||
|
break;
|
||
|
case OpCodeEnum.Ldc_I4:
|
||
|
code.TokenInteger = (int)token;
|
||
|
break;
|
||
|
case OpCodeEnum.Ldc_I4_S:
|
||
|
code.TokenInteger = (sbyte)token;
|
||
|
break;
|
||
|
case OpCodeEnum.Ldc_I8:
|
||
|
code.TokenLong = (long)token;
|
||
|
break;
|
||
|
case OpCodeEnum.Ldc_R4:
|
||
|
{
|
||
|
float val = (float)token;
|
||
|
code.TokenInteger = *(int*)&val;
|
||
|
}
|
||
|
break;
|
||
|
case OpCodeEnum.Ldc_R8:
|
||
|
{
|
||
|
double val = (double)token;
|
||
|
code.TokenLong = *(long*)&val;
|
||
|
}
|
||
|
break;
|
||
|
case OpCodeEnum.Stloc:
|
||
|
case OpCodeEnum.Stloc_S:
|
||
|
case OpCodeEnum.Ldloc:
|
||
|
case OpCodeEnum.Ldloc_S:
|
||
|
case OpCodeEnum.Ldloca:
|
||
|
case OpCodeEnum.Ldloca_S:
|
||
|
{
|
||
|
Mono.Cecil.Cil.VariableDefinition vd = (Mono.Cecil.Cil.VariableDefinition)token;
|
||
|
code.TokenInteger = vd.Index;
|
||
|
}
|
||
|
break;
|
||
|
case OpCodeEnum.Ldarg_S:
|
||
|
case OpCodeEnum.Ldarg:
|
||
|
case OpCodeEnum.Ldarga:
|
||
|
case OpCodeEnum.Ldarga_S:
|
||
|
case OpCodeEnum.Starg:
|
||
|
case OpCodeEnum.Starg_S:
|
||
|
{
|
||
|
Mono.Cecil.ParameterDefinition vd = (Mono.Cecil.ParameterDefinition)token;
|
||
|
code.TokenInteger = vd.Index;
|
||
|
if (HasThis)
|
||
|
code.TokenInteger++;
|
||
|
}
|
||
|
break;
|
||
|
case OpCodeEnum.Call:
|
||
|
case OpCodeEnum.Newobj:
|
||
|
case OpCodeEnum.Ldftn:
|
||
|
case OpCodeEnum.Ldvirtftn:
|
||
|
case OpCodeEnum.Callvirt:
|
||
|
{
|
||
|
bool invalidToken;
|
||
|
var m = appdomain.GetMethod(token, declaringType, this, out invalidToken);
|
||
|
if (m != null)
|
||
|
{
|
||
|
if(code.Code == OpCodeEnum.Callvirt && m is ILMethod)
|
||
|
{
|
||
|
ILMethod ilm = (ILMethod)m;
|
||
|
if (!ilm.def.IsAbstract && !ilm.def.IsVirtual && !ilm.DeclearingType.IsInterface)
|
||
|
code.Code = OpCodeEnum.Call;
|
||
|
}
|
||
|
if (invalidToken)
|
||
|
code.TokenInteger = m.GetHashCode();
|
||
|
else
|
||
|
code.TokenInteger = token.GetHashCode();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//Cannot find method or the method is dummy
|
||
|
MethodReference _ref = (MethodReference)token;
|
||
|
int paramCnt = _ref.HasParameters ? _ref.Parameters.Count : 0;
|
||
|
if (_ref.HasThis)
|
||
|
paramCnt++;
|
||
|
code.TokenLong = paramCnt;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case OpCodeEnum.Constrained:
|
||
|
case OpCodeEnum.Box:
|
||
|
case OpCodeEnum.Unbox_Any:
|
||
|
case OpCodeEnum.Unbox:
|
||
|
case OpCodeEnum.Initobj:
|
||
|
case OpCodeEnum.Isinst:
|
||
|
case OpCodeEnum.Newarr:
|
||
|
case OpCodeEnum.Stobj:
|
||
|
case OpCodeEnum.Ldobj:
|
||
|
case OpCodeEnum.Castclass:
|
||
|
{
|
||
|
code.TokenInteger = GetTypeTokenHashCode(token);
|
||
|
}
|
||
|
break;
|
||
|
case OpCodeEnum.Stfld:
|
||
|
case OpCodeEnum.Ldfld:
|
||
|
case OpCodeEnum.Ldflda:
|
||
|
{
|
||
|
code.TokenLong = appdomain.GetStaticFieldIndex(token, declaringType, this);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case OpCodeEnum.Stsfld:
|
||
|
case OpCodeEnum.Ldsfld:
|
||
|
case OpCodeEnum.Ldsflda:
|
||
|
{
|
||
|
code.TokenLong = appdomain.GetStaticFieldIndex(token, declaringType, this);
|
||
|
}
|
||
|
break;
|
||
|
case OpCodeEnum.Ldstr:
|
||
|
{
|
||
|
long hashCode = appdomain.CacheString(token);
|
||
|
code.TokenLong = hashCode;
|
||
|
}
|
||
|
break;
|
||
|
case OpCodeEnum.Ldtoken:
|
||
|
{
|
||
|
if (token is FieldReference)
|
||
|
{
|
||
|
code.TokenInteger = 0;
|
||
|
code.TokenLong = appdomain.GetStaticFieldIndex(token, declaringType, this);
|
||
|
}
|
||
|
else if (token is TypeReference)
|
||
|
{
|
||
|
code.TokenInteger = 1;
|
||
|
code.TokenLong = GetTypeTokenHashCode(token);
|
||
|
}
|
||
|
else
|
||
|
throw new NotImplementedException();
|
||
|
}
|
||
|
break;
|
||
|
case OpCodeEnum.Switch:
|
||
|
{
|
||
|
PrepareJumpTable(token, addr);
|
||
|
code.TokenInteger = token.GetHashCode();
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal int GetTypeTokenHashCode(object token)
|
||
|
{
|
||
|
var t = appdomain.GetType(token, declaringType, this);
|
||
|
bool isGenericParameter = CheckHasGenericParamter(token);
|
||
|
if (t == null && isGenericParameter)
|
||
|
{
|
||
|
t = FindGenericArgument(((TypeReference)token).Name);
|
||
|
}
|
||
|
if (t != null)
|
||
|
{
|
||
|
if (t is ILType || isGenericParameter)
|
||
|
{
|
||
|
return t.GetHashCode();
|
||
|
}
|
||
|
else
|
||
|
return token.GetHashCode();
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
bool CheckHasGenericParamter(object token)
|
||
|
{
|
||
|
if (token is TypeReference)
|
||
|
{
|
||
|
TypeReference _ref = ((TypeReference)token);
|
||
|
if (_ref.IsArray)
|
||
|
return CheckHasGenericParamter(((ArrayType)_ref).ElementType);
|
||
|
if (_ref.IsGenericParameter)
|
||
|
return true;
|
||
|
if (_ref.IsGenericInstance)
|
||
|
{
|
||
|
GenericInstanceType gi = (GenericInstanceType)_ref;
|
||
|
foreach(var i in gi.GenericArguments)
|
||
|
{
|
||
|
if (CheckHasGenericParamter(i))
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void PrepareJumpTable(object token, Dictionary<Mono.Cecil.Cil.Instruction, int> addr)
|
||
|
{
|
||
|
int hashCode = token.GetHashCode();
|
||
|
|
||
|
if (jumptables == null)
|
||
|
jumptables = new Dictionary<int, int[]>();
|
||
|
if (jumptables.ContainsKey(hashCode))
|
||
|
return;
|
||
|
Mono.Cecil.Cil.Instruction[] e = token as Mono.Cecil.Cil.Instruction[];
|
||
|
int[] addrs = new int[e.Length];
|
||
|
for (int i = 0; i < e.Length; i++)
|
||
|
{
|
||
|
addrs[i] = addr[e[i]];
|
||
|
}
|
||
|
|
||
|
jumptables[hashCode] = addrs;
|
||
|
}
|
||
|
|
||
|
void InitParameters()
|
||
|
{
|
||
|
parameters = new List<IType>();
|
||
|
foreach (var i in def.Parameters)
|
||
|
{
|
||
|
IType type = null;
|
||
|
bool isByRef = false;
|
||
|
bool isArray = false;
|
||
|
int rank = 1;
|
||
|
TypeReference pt = i.ParameterType;
|
||
|
if (pt.IsByReference)
|
||
|
{
|
||
|
isByRef = true;
|
||
|
pt = ((ByReferenceType)pt).ElementType;
|
||
|
}
|
||
|
if (pt.IsArray)
|
||
|
{
|
||
|
isArray = true;
|
||
|
rank = ((ArrayType)pt).Rank;
|
||
|
pt = ((ArrayType)pt).ElementType;
|
||
|
}
|
||
|
if (pt.IsGenericParameter)
|
||
|
{
|
||
|
type = FindGenericArgument(pt.Name);
|
||
|
if (type == null && def.HasGenericParameters)
|
||
|
{
|
||
|
bool found = false;
|
||
|
foreach (var j in def.GenericParameters)
|
||
|
{
|
||
|
if (j.Name == pt.Name)
|
||
|
{
|
||
|
found = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (found)
|
||
|
{
|
||
|
type = new ILGenericParameterType(pt.Name);
|
||
|
}
|
||
|
else
|
||
|
throw new NotSupportedException("Cannot find Generic Parameter " + pt.Name + " in " + def.FullName);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
type = appdomain.GetType(pt, declaringType, this);
|
||
|
|
||
|
if (isArray)
|
||
|
type = type.MakeArrayType(rank);
|
||
|
if (isByRef)
|
||
|
type = type.MakeByRefType();
|
||
|
parameters.Add(type);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public IMethod MakeGenericMethod(IType[] genericArguments)
|
||
|
{
|
||
|
KeyValuePair<string, IType>[] genericParameters = new KeyValuePair<string, IType>[genericArguments.Length];
|
||
|
for (int i = 0; i < genericArguments.Length; i++)
|
||
|
{
|
||
|
string name = def.GenericParameters[i].Name;
|
||
|
IType val = genericArguments[i];
|
||
|
genericParameters[i] = new KeyValuePair<string, IType>(name, val);
|
||
|
}
|
||
|
|
||
|
ILMethod m = new ILMethod(def, declaringType, appdomain, jitFlags);
|
||
|
m.genericParameters = genericParameters;
|
||
|
m.genericArguments = genericArguments;
|
||
|
if (m.def.ReturnType.IsGenericParameter)
|
||
|
{
|
||
|
m.ReturnType = m.FindGenericArgument(m.def.ReturnType.Name);
|
||
|
}
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
string cachedName;
|
||
|
public override string ToString()
|
||
|
{
|
||
|
if (cachedName == null)
|
||
|
{
|
||
|
StringBuilder sb = new StringBuilder();
|
||
|
sb.Append(declaringType.FullName);
|
||
|
sb.Append('.');
|
||
|
sb.Append(Name);
|
||
|
sb.Append('(');
|
||
|
bool isFirst = true;
|
||
|
if (parameters == null)
|
||
|
InitParameters();
|
||
|
for (int i = 0; i < parameters.Count; i++)
|
||
|
{
|
||
|
if (isFirst)
|
||
|
isFirst = false;
|
||
|
else
|
||
|
sb.Append(", ");
|
||
|
sb.Append(parameters[i].FullName);
|
||
|
sb.Append(' ');
|
||
|
sb.Append(def.Parameters[i].Name);
|
||
|
}
|
||
|
sb.Append(')');
|
||
|
cachedName = sb.ToString();
|
||
|
}
|
||
|
return cachedName;
|
||
|
}
|
||
|
|
||
|
public override int GetHashCode()
|
||
|
{
|
||
|
if (hashCode == -1)
|
||
|
hashCode = System.Threading.Interlocked.Add(ref instance_id, 1);
|
||
|
return hashCode;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool? isExtend;
|
||
|
public bool IsExtend
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (isExtend == null)
|
||
|
{
|
||
|
isExtend = this.IsExtendMethod();
|
||
|
}
|
||
|
return isExtend.Value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|