using System; using System.Collections.Generic; using System.Linq; using System.Text; using ILRuntime.CLR.Method; using ILRuntime.CLR.TypeSystem; using ILRuntime.Runtime.Intepreter; using System.Reflection; using System.Threading; namespace ILRuntime.Runtime.Enviorment { public class CrossBindingCodeGenerator { class PropertyGenerateInfo { public string Name; public Type ReturnType; public string GetterBody; public string SettingBody; public string Modifier; public string OverrideString; } public static string GenerateCrossBindingAdapterCode(Type baseType, string nameSpace) { StringBuilder sb = new StringBuilder(); List virtMethods = new List(); GetMethods(baseType, virtMethods); string clsName, realClsName; bool isByRef; baseType.GetClassName(out clsName, out realClsName, out isByRef, true); sb.Append(@"using System; using ILRuntime.CLR.Method; using ILRuntime.Runtime.Enviorment; using ILRuntime.Runtime.Intepreter; namespace "); sb.AppendLine(nameSpace); sb.Append(@"{ public class "); sb.Append(clsName); sb.AppendLine(@"Adapter : CrossBindingAdaptor {"); sb.Append(@" public override Type BaseCLRType { get { return typeof("); sb.Append(realClsName); sb.AppendLine(@"); } } public override Type AdaptorType { get { return typeof(Adapter); } } public override object CreateCLRInstance(ILRuntime.Runtime.Enviorment.AppDomain appdomain, ILTypeInstance instance) { return new Adapter(appdomain, instance); } "); sb.AppendLine(string.Format(" public class Adapter : {0}, CrossBindingAdaptorType", realClsName)); sb.AppendLine(" {"); GenerateCrossBindingMethodInfo(sb, virtMethods); sb.AppendLine(@" bool isInvokingToString; ILTypeInstance instance; ILRuntime.Runtime.Enviorment.AppDomain appdomain; public Adapter() { } public Adapter(ILRuntime.Runtime.Enviorment.AppDomain appdomain, ILTypeInstance instance) { this.appdomain = appdomain; this.instance = instance; } public ILTypeInstance ILInstance { get { return instance; } } "); GenerateCrossBindingMethodBody(sb, virtMethods); sb.Append(@" public override string ToString() { IMethod m = appdomain.ObjectType.GetMethod("); sb.AppendLine("\"ToString\", 0);"); sb.AppendLine(@" m = instance.Type.GetVirtualMethod(m); if (m == null || m is ILMethod) { if (!isInvokingToString) { isInvokingToString = true; string res = instance.ToString(); isInvokingToString = false; return res; } else return instance.Type.FullName; } else return instance.Type.FullName;"); sb.AppendLine(" }"); sb.AppendLine(" }"); sb.AppendLine(" }"); sb.AppendLine("}"); return sb.ToString(); } static void GenerateCrossBindingMethodBody(StringBuilder sb, List virtMethods) { int index = 0; Dictionary pendingProperties = new Dictionary(); foreach (var i in virtMethods) { if (ShouldSkip(i)) continue; bool isProperty = i.IsSpecialName && (i.Name.StartsWith("get_") || i.Name.StartsWith("set_")); PropertyGenerateInfo pInfo = null; bool isGetter = false; bool isIndexFunc = false; bool isByRef; string clsName, realClsName; StringBuilder oriBuilder = null; if (isProperty) { string pName = i.Name.Substring(4); if (i.Name == "get_Item" || i.Name == "set_Item") { StringBuilder sBuilder = new StringBuilder(); var p = i.GetParameters()[0]; p.ParameterType.GetClassName(out clsName, out realClsName, out isByRef, true); pName = string.Format("this [{0}]", realClsName + " " + p.Name); isIndexFunc = true; } isGetter = i.Name.StartsWith("get_"); oriBuilder = sb; sb = new StringBuilder(); if (!pendingProperties.TryGetValue(pName, out pInfo)) { pInfo = new PropertyGenerateInfo(); pInfo.Name = pName; pendingProperties[pName] = pInfo; } if (pInfo.ReturnType == null) { if (isGetter) { pInfo.ReturnType = i.ReturnType; } else { pInfo.ReturnType = i.GetParameters()[0].ParameterType; } } } var param = i.GetParameters(); string modifier = i.IsFamily ? "protected" : "public"; string overrideStr = i.DeclaringType.IsInterface ? "" : (i.IsFinal ? "new " : "override "); string returnString = ""; if (i.ReturnType != typeof(void)) { i.ReturnType.GetClassName(out clsName, out realClsName, out isByRef, true); returnString = "return "; } else realClsName = "void"; if (!isProperty) { sb.Append(string.Format(" {0} {3}{1} {2}(", modifier, realClsName, i.Name, overrideStr)); GetParameterDefinition(sb, param, true); sb.AppendLine(@") {"); } else { pInfo.Modifier = modifier; pInfo.OverrideString = overrideStr; } if (!i.IsAbstract) { sb.AppendLine(string.Format(" if (m{0}_{1}.CheckShouldInvokeBase(this.instance))", i.Name, index)); if (isProperty) { string baseMethodName = isIndexFunc ? string.Format("base[{0}]", i.GetParameters()[0].Name) : string.Format("base.{0}", i.Name.Substring(4)); if (isGetter) { sb.AppendLine(string.Format(" return {0};", baseMethodName)); } else { sb.AppendLine(string.Format(" {0} = value;", baseMethodName)); } } else sb.AppendLine(string.Format(" {2}base.{0}({1});", i.Name, GetParameterName(param, true), returnString)); sb.AppendLine(" else"); sb.AppendLine(string.Format(" {3}m{0}_{1}.Invoke(this.instance{2});", i.Name, index, GetParameterName(param, false), returnString)); } else { sb.AppendLine(string.Format(" {3}m{0}_{1}.Invoke(this.instance{2});", i.Name, index, GetParameterName(param, false), returnString)); } if (isProperty) { if (isGetter) { pInfo.GetterBody = sb.ToString(); } else { pInfo.SettingBody = sb.ToString(); } sb = oriBuilder; } else { sb.AppendLine(" }"); sb.AppendLine(); } index++; } foreach (var i in pendingProperties) { var pInfo = i.Value; string clsName, realClsName; bool isByRef; pInfo.ReturnType.GetClassName(out clsName, out realClsName, out isByRef, true); sb.AppendLine(string.Format(" {0} {3}{1} {2}", pInfo.Modifier, realClsName, pInfo.Name, pInfo.OverrideString)); sb.AppendLine(" {"); if (!string.IsNullOrEmpty(pInfo.GetterBody)) { sb.AppendLine(" get"); sb.AppendLine(" {"); sb.AppendLine(pInfo.GetterBody); sb.AppendLine(" }"); } if (!string.IsNullOrEmpty(pInfo.SettingBody)) { sb.AppendLine(" set"); sb.AppendLine(" {"); sb.AppendLine(pInfo.SettingBody); sb.AppendLine(" }"); } sb.AppendLine(" }"); sb.AppendLine(); } } static void GenerateCrossBindingMethodInfo(StringBuilder sb, List virtMethods) { int index = 0; foreach (var i in virtMethods) { if (ShouldSkip(i)) continue; var param = i.GetParameters(); if (NeedGenerateCrossBindingMethodClass(param)) { GenerateCrossBindingMethodClass(sb, i.Name, index, param, i.ReturnType); sb.AppendLine(string.Format(" {0}_{1}Info m{0}_{1} = new {0}_{1}Info();", i.Name, index)); } else { if (i.ReturnType != typeof(void)) { sb.AppendLine(string.Format(" CrossBindingFunctionInfo<{0}> m{1}_{2} = new CrossBindingFunctionInfo<{0}>(\"{1}\");", GetParametersString(param, i.ReturnType), i.Name, index)); } else { if (param.Length > 0) sb.AppendLine(string.Format(" CrossBindingMethodInfo<{0}> m{1}_{2} = new CrossBindingMethodInfo<{0}>(\"{1}\");", GetParametersString(param, i.ReturnType), i.Name, index)); else sb.AppendLine(string.Format(" CrossBindingMethodInfo m{0}_{1} = new CrossBindingMethodInfo(\"{0}\");", i.Name, index)); } } index++; } } static bool ShouldSkip(MethodInfo info) { var paramInfos = info.GetParameters(); if (info.Name == "ToString" || info.Name == "GetHashCode" || info.Name == "Finalize") return paramInfos.Length == 0; if (info.Name == "Equals" && paramInfos.Length == 1 && paramInfos[0].ParameterType == typeof(object)) return true; if (info.IsAssembly || info.IsFamilyOrAssembly || info.IsPrivate || info.IsFinal) return true; if (info.GetCustomAttributes(typeof(ObsoleteAttribute), true).Length > 0) return true; for (int i = 0; i < paramInfos.Length; ++i) { var paramType = paramInfos[i].ParameterType; if (paramType.IsByRef) paramType = paramType.GetElementType(); if (paramType.IsPointer || paramType.IsNotPublic || paramType.IsNested && !paramType.IsNestedPublic) { return true; } } var returnType = info.ReturnType; if (returnType.IsByRef) returnType = returnType.GetElementType(); if (returnType.IsNotPublic || returnType.IsNested && !returnType.IsNestedPublic) return true; return false; } static string GetParametersString(ParameterInfo[] param, Type returnType) { StringBuilder sb = new StringBuilder(); bool first = true; foreach (var i in param) { if (!first) sb.Append(", "); else first = false; string clsName, realClsName; bool isByRef; i.ParameterType.GetClassName(out clsName, out realClsName, out isByRef, true); sb.Append(realClsName); } if (returnType != typeof(void)) { if (!first) sb.Append(", "); string clsName, realClsName; bool isByRef; returnType.GetClassName(out clsName, out realClsName, out isByRef, true); sb.Append(realClsName); } return sb.ToString(); } static string GetParametersTypeString(ParameterInfo[] param, Type returnType) { StringBuilder sb = new StringBuilder(); bool first = true; foreach (var i in param) { if (!first) sb.Append(", "); else first = false; string clsName, realClsName; bool isByRef; sb.Append("typeof("); i.ParameterType.GetClassName(out clsName, out realClsName, out isByRef, true); sb.Append(realClsName); sb.Append(")"); if (isByRef) sb.Append(".MakeByRefType()"); } if (returnType != typeof(void)) { if (!first) sb.Append(", "); string clsName, realClsName; bool isByRef; returnType.GetClassName(out clsName, out realClsName, out isByRef, true); sb.Append(realClsName); } return sb.ToString(); } static bool NeedGenerateCrossBindingMethodClass(ParameterInfo[] param) { if (param.Length > 5) return true; foreach (var i in param) { if (i.IsOut || i.ParameterType.IsByRef) return true; } return false; } static string GetParameterName(ParameterInfo[] param, bool first) { StringBuilder sb = new StringBuilder(); foreach (var p in param) { if (!first) sb.Append(", "); else first = false; if (p.IsOut) sb.Append("out "); else if (p.ParameterType.IsByRef) sb.Append("ref "); sb.Append(p.Name); } return sb.ToString(); } static void GetParameterDefinition(StringBuilder sb, ParameterInfo[] param, bool first) { string clsName, realClsName; bool isByRef; foreach (var p in param) { if (!first) sb.Append(", "); else first = false; p.ParameterType.GetClassName(out clsName, out realClsName, out isByRef, true); if (p.IsOut) sb.Append("out "); else if (isByRef) sb.Append("ref "); sb.Append(realClsName); sb.Append(" "); sb.Append(p.Name); } } static void GenerateCrossBindingMethodClass(StringBuilder sb, string funcName, int index, ParameterInfo[] param, Type returnType) { sb.AppendLine(string.Format(" class {0}_{1}Info : CrossBindingMethodInfo", funcName, index)); sb.Append(@" { static Type[] pTypes = new Type[] {"); sb.Append(GetParametersTypeString(param, returnType)); sb.AppendLine("};"); sb.AppendLine(); sb.AppendLine(string.Format(" public {0}_{1}Info()", funcName, index)); sb.AppendLine(string.Format(" : base(\"{0}\")", funcName)); sb.Append(@" { } protected override Type ReturnType { get { return "); string clsName, realClsName; bool isByRef; returnType.GetClassName(out clsName, out realClsName, out isByRef, true); string rtRealName = realClsName; bool hasReturn = returnType != typeof(void); if (!hasReturn) sb.Append("null"); else { sb.AppendFormat("typeof({0})", realClsName); } sb.AppendLine(@"; } } protected override Type[] Parameters { get { return pTypes; } }"); sb.AppendFormat(" public {0} Invoke(ILTypeInstance instance", !hasReturn ? "void" : realClsName); GetParameterDefinition(sb, param, false); sb.AppendLine(@") { EnsureMethod(instance);"); GenInitParams(sb, param); sb.AppendLine(@" if (method != null) { invoking = true;"); if (hasReturn) sb.AppendLine(string.Format(" {0} __res = default({0});", rtRealName)); sb.AppendLine(@" try { using (var ctx = domain.BeginInvoke(method)) {"); Dictionary refIndex = new Dictionary(); int idx = 0; foreach (var p in param) { if (p.ParameterType.IsByRef) { sb.AppendLine(GetPushString(p.ParameterType.GetElementType(), p.Name)); refIndex[p] = idx++; } } sb.AppendLine(" ctx.PushObject(instance);"); foreach (var p in param) { if (p.ParameterType.IsByRef) { sb.AppendLine(string.Format(" ctx.PushReference({0});", refIndex[p])); } else { sb.AppendLine(GetPushString(p.ParameterType, p.Name)); } } sb.AppendLine(" ctx.Invoke();"); if (hasReturn) sb.AppendLine(GetReadString(returnType, rtRealName, "", "__res")); foreach (var p in param) { if (p.ParameterType.IsByRef) { p.ParameterType.GetClassName(out clsName, out realClsName, out isByRef, true); sb.AppendLine(GetReadString(p.ParameterType.GetElementType(), realClsName, refIndex[p].ToString(), p.Name)); } } sb.AppendLine(" }"); sb.AppendLine(@" } finally { invoking = false; }"); if (hasReturn) sb.AppendLine(" return __res;"); sb.AppendLine(" }"); if (hasReturn) sb.AppendLine(@" else return default(TResult);"); sb.AppendLine(@" } public override void Invoke(ILTypeInstance instance) { throw new NotSupportedException(); } }"); } static void GetMethods(Type type, List list) { if (type == null) return; MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); foreach (var i in methods) { if ((i.IsVirtual || i.IsAbstract || type.IsInterface) && !i.ContainsGenericParameters) { if (list.Any(m => IsMethodEqual(m, i))) continue; list.Add(i); } } var interfaceArray = type.GetInterfaces(); if (interfaceArray != null) { for (int i = 0; i < interfaceArray.Length; ++i) { GetMethods(interfaceArray[i], list); } } } static bool IsMethodEqual(MethodInfo left, MethodInfo right) { var leftParams = left.GetParameters(); var rightParams = right.GetParameters(); if (leftParams.Length != rightParams.Length) { return false; } // 有些继承了多个interface的类,且这几个interface的类有相同函数名的时候,子类实现的接口就会带上interfere的fullName string leftMethodName = left.Name.Replace(left.DeclaringType.FullName, ""); string rightMethodName = right.Name.Replace(right.DeclaringType.FullName, ""); if (leftMethodName != rightMethodName) { return false; } for (int i = 0; i < leftParams.Length; ++i) { if (leftParams[i].ParameterType != rightParams[i].ParameterType) { return false; } } return true; } static void GenInitParams(StringBuilder sb, ParameterInfo[] param) { foreach (var p in param) { if (p.IsOut) { sb.AppendLine(string.Format(" {0} = default({1});", p.Name, p.ParameterType.GetElementType().FullName)); } } } static string GetPushString(Type type, string argName) { if (type.IsPrimitive) { if (type == typeof(int)) { return string.Format(" ctx.PushInteger({0});", argName); } else if (type == typeof(long)) { return string.Format(" ctx.PushLong({0});", argName); } else if (type == typeof(short)) { return string.Format(" ctx.PushInteger({0});", argName); } else if (type == typeof(bool)) { return string.Format(" ctx.PushBool({0});", argName); } else if (type == typeof(ushort)) { return string.Format(" ctx.PushInteger({0});", argName); } else if (type == typeof(float)) { return string.Format(" ctx.PushFloat({0});", argName); } else if (type == typeof(double)) { return string.Format(" ctx.PushDouble({0});", argName); } else if (type == typeof(byte)) { return string.Format(" ctx.PushInteger({0});", argName); } else if (type == typeof(sbyte)) { return string.Format(" ctx.PushInteger({0});", argName); } else if (type == typeof(uint)) { return string.Format(" ctx.PushInteger((int){0});", argName); } else if (type == typeof(char)) { return string.Format(" ctx.PushInteger((int){0});", argName); } else if (type == typeof(ulong)) { return string.Format(" ctx.PushLong((long){0});", argName); } else throw new NotImplementedException(); } else { return string.Format(" ctx.PushObject({0});", argName); } } static string GetReadString(Type type, string realClsName, string argName, string valName) { if (type.IsPrimitive) { if (type == typeof(int)) { return string.Format(" {1} = ctx.ReadInteger({0});", argName, valName); } else if (type == typeof(long)) { return string.Format(" {1} = ctx.ReadLong({0});", argName, valName); } else if (type == typeof(short)) { return string.Format(" {1} = (short)ctx.ReadInteger({0});", argName, valName); } else if (type == typeof(bool)) { return string.Format(" {1} = ctx.ReadBool({0});", argName, valName); } else if (type == typeof(ushort)) { return string.Format(" {1} = (ushort)ctx.ReadInteger({0});", argName, valName); } else if (type == typeof(float)) { return string.Format(" {1} = ctx.ReadFloat({0});", argName, valName); } else if (type == typeof(double)) { return string.Format(" {1} = ctx.ReadDouble({0});", argName, valName); } else if (type == typeof(byte)) { return string.Format(" {1} = (byte)ctx.ReadInteger({0});", argName, valName); } else if (type == typeof(sbyte)) { return string.Format(" {1} = (sbyte)ctx.ReadInteger({0});", argName, valName); } else if (type == typeof(uint)) { return string.Format(" {1} = (uint)ctx.ReadInteger({0});", argName, valName); } else if (type == typeof(char)) { return string.Format(" {1} = (char)ctx.ReadInteger({0});", argName, valName); } else if (type == typeof(ulong)) { return string.Format(" {1} = (ulong)ctx.ReadLong({0});", argName, valName); } else throw new NotImplementedException(); } else { return string.Format(" {2} = ctx.ReadObject<{1}>({0});", argName, realClsName, valName); } } } }