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 registerSymbols; bool symbolFixed; MethodDefinition def; List parameters; ILRuntime.Runtime.Enviorment.AppDomain appdomain; ILType declaringType; ExceptionHandler[] exceptionHandler, exceptionHandlerR; KeyValuePair[] genericParameters; IType[] genericArguments; Dictionary 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 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 JumpTables { get { return jumptables; } } public Dictionary JumpTablesRegister { get { return jumptablesR; } } internal Dictionary 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 Variables { get { return variables; } } public KeyValuePair[] 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 Parameters { get { if (def.HasParameters && parameters == null) { InitParameters(); } return parameters; } } public IType ReturnType { get; private set; } public void Prewarm(bool recursive) { HashSet alreadyPrewarmed = null; if (recursive) { alreadyPrewarmed = new HashSet(); } Prewarm(alreadyPrewarmed); } void PrewarmBody(HashSet 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 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 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 addr = new Dictionary(); 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 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 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 addr) { int hashCode = token.GetHashCode(); if (jumptables == null) jumptables = new Dictionary(); 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(); 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[] genericParameters = new KeyValuePair[genericArguments.Length]; for (int i = 0; i < genericArguments.Length; i++) { string name = def.GenericParameters[i].Name; IType val = genericArguments[i]; genericParameters[i] = new KeyValuePair(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; } } } }