// // Copyright (c), 2009 Sigbjorn Finne // // Class: InvokeBridge // // Purpose: Collection of (static) methods for dynamically creating // objects and accessing methods/fields on them. // using System; using System.Collections; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Reflection; using System.Reflection.Emit; using System.Text; using System.Globalization; [assembly:AssemblyVersion("0.4.0.0")] [assembly:AssemblyCulture("")] namespace HsInvoker { // // NOTE: if you change the method signatures and/or add // to the interface, the IID of the interface will change // when exporting its COM interface. This will have to be // accounted for in InvokerClient.h, or you will scratch your // head for quite a while.. [ClassInterface(ClassInterfaceType.AutoDual), GuidAttribute("39D497D9-60E0-3525-B7F2-7BC096D3A2A3"), ComVisible(true) ] public class HsInvokeBridge2 { protected Dictionary m_assemblies; protected Dictionary m_assemblyNames; public HsInvokeBridge2() { m_assemblies = new Dictionary(); m_assemblyNames = new Dictionary(); Assembly corAss = Assembly.Load("mscorlib.dll"); m_assemblies.Add(corAss.Location,corAss.GetName()); AddAssemblyMapping("System", corAss.GetName()); } // // Method: CreateObject(String* assemName, String* objSpec, Object* args[]) // // Purpose: Given a fully qualified name of a class/type, try // to create an instance of it. Make sure the fresh instance // isn't unboxed or otherwise transformed by fixing its PInvoke type // to an interface pointer. [return:MarshalAs( UnmanagedType.Interface ) ] public object CreateObject(String assemName, String objSpec, object[] args) { Object instance = null; // Unravel the name of the class/type. TypeName typeName = TypeName.ParseType(objSpec); if (assemName != null && assemName.Length > 0) { typeName.m_assembly = assemName; } // Try creating the instance.. try { instance = CreateInstance(typeName,args); } catch (Exception e) { Logger.Debug("Unable to create instance '{0}' {1} {2} {3}", objSpec, e,typeName,args.Length); throw(e); } if ( instance == null ) { Logger.Debug("Unable to create instance '{0}' {1} {2}", objSpec,typeName,args.Length); } return instance; } // // Method: FindMethod // // Reliably resolve a type's method by name and argument types + // the types the (generic) method is parameterized over. // public static MethodInfo FindMethod(Type type, String methName, BindingFlags flgs, Binder bndr, Type[] typeArgs, Type[] paramTypes) { MethodInfo mi = null; if ( paramTypes == null ) { mi = type.GetMethod(methName, flgs); if ( mi != null && typeArgs != null && typeArgs.Length > 0 ) { return mi.MakeGenericMethod(typeArgs); } else if ( mi != null ) { return mi; } } mi = type.GetMethod(methName, flgs, bndr, paramTypes, null); if ( mi != null ) { return mi; } /* Don't give up just yet..it might be overloaded and generic..spin through methods ourselves.. */ foreach(MethodInfo mmi in type.GetMethods(flgs)) { // int i=0;i 0 ) { Type[] ts = closedMethod.GetGenericArguments(); // Console.WriteLine("Instant: {0} {1} {2} {3} {4}", closedMethod, ( ts != null ? 0 : ts.Length), typeArgs.Length, type.ContainsGenericParameters, closedMethod.ContainsGenericParameters); try { closedMethod = closedMethod.MakeGenericMethod(typeArgs); } catch(Exception) { // Console.WriteLine("MakeGen: {0}", e); continue; } } ParameterInfo[] ps = closedMethod.GetParameters(); // left-to-right matching, should reuse the HsBinder method here. ToDo. if (ps.Length == paramTypes.Length) { bool doneFor = true; for (int p = 0; p < ps.Length; p++) { if ( !ps[p].ParameterType.IsAssignableFrom(paramTypes[p]) ) { doneFor=false; break; // this is not the method we're looking for } } if (doneFor) { // if we're here, we got the right method return closedMethod; } } } catch (Exception) { continue; } } return null; } // // Method: InvokeMethod // // Purpose: Given a pointer to an already created object, look up // one of its method. If found, invoke the method passing it // 'args' as arguments. // // Comments: the format of the method-spec is "methodName(type1,..,typeN)" [N>=0] // public Object InvokeMethod(Object obj, String methName, String genericTypeArgs, Object[] args, int arg_len, [MarshalAs(UnmanagedType.LPArray,SizeParamIndex=4)]int[] arg_tys, int res_ty) { // Get the methods from the type MethodInfo[] methods = obj.GetType().GetMethods(); if ( methods==null ) { Logger.Debug("InvokeMethod: No matching types found"); return null; } BindingFlags flgs = (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.InvokeMethod); /* Caller is assumed to catch any exceptions raised. */ try { Type ty = obj.GetType(); int idx; //Console.WriteLine("Calling-method \"{0}\" {1} {2} {3}", methName, ty.ContainsGenericParameters, res_ty, ty); Type[] methTyArgs = new Type[0]; Type[] typeArgs; /* Both types and methods can be parameterized over types. We need * to keep the two binding sites apart. For the type's parameters we * need to separately instantiate those before getting on to the method's * * Somewhat hackily, we pass along a single type-param string from Haskell, * separating the type's instantiated types from the method's by the _;;_ * delimiter. */ if ( genericTypeArgs!=null && (idx = genericTypeArgs.IndexOf("_;;_")) >= 0 ) { methTyArgs = fromTypeArgString(genericTypeArgs.Substring(idx+4)); typeArgs = fromTypeArgString(genericTypeArgs.Substring(0,idx)); } else { typeArgs = fromTypeArgString(genericTypeArgs); } /* If the underlying type is generic, instantiate it now with the above type arguments */ if ( ty.ContainsGenericParameters ) { ty = ty.MakeGenericType(typeArgs); } Type[] paramTys = new Type[args.Length]; for(int i=0;i.Tn.methodName(type1,..,typeN)" [N>=0] // public Object InvokeStaticMethod(String assemName, String typeAndMethName, String genericTypeArgs, Object[] args, int arg_len, [MarshalAs(UnmanagedType.LPArray,SizeParamIndex=4)]int[] arg_tys, int res_ty) { String className = ""; String methName = typeAndMethName; Type t; int lastDot = typeAndMethName.LastIndexOf('.'); if (lastDot > (-1) ) { className = typeAndMethName.Substring(0,lastDot); methName = typeAndMethName.Substring(lastDot+1); } // Unravel the name of the class/type. TypeName typeName = TypeName.ParseType(className); if (assemName != null && assemName.Length > 0) { typeName.m_assembly = assemName; } try { t = GetType(typeName.m_class); if ( t==null ) { try { Assembly localA = Assembly.LoadFrom(typeName.m_assembly); t = localA.GetType(typeName.m_class); } catch (Exception) { ; } } if ( t==null ) { Logger.Debug("InvokeStaticMethod: Type '{0}' not found", className); return null; } } catch (Exception e) { Logger.Debug("InvokeStaticMethod: Type {0} not found", className); throw(e); } BindingFlags flgs = ( BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.InvokeMethod); //Console.WriteLine("Calling \"{0}\" {1} {2}", methName, t.ContainsGenericParameters, res_ty); try { int idx; Type[] methTyArgs = new Type[0]; Type[] typeArgs; //Console.WriteLine("generic: {0}", genericTypeArgs); if ( genericTypeArgs != null && (idx = genericTypeArgs.IndexOf("_;;_")) >= 0 ) { methTyArgs = fromTypeArgString(genericTypeArgs.Substring(idx+4)); typeArgs = fromTypeArgString(genericTypeArgs.Substring(0,idx)); } else { typeArgs = fromTypeArgString(genericTypeArgs); } if ( t.ContainsGenericParameters ) { t = t.MakeGenericType(typeArgs); } Type[] paramTys = new Type[args.Length]; for(int i=0;i 0 ) { int i = tyParamIdx; while ( i < typeName.Length ) { i++; if ( typeName[i] < '0' || typeName[i] > '9' ) { break; } } // Upto and including the arity -- System.Action`2 baseTypeName = typeName.Substring(0,i); TypeName.ParseTypeArgs(typeName,i,ref tyArgs); } if ( (idx = baseTypeName.LastIndexOf(".")) > 0 ) { String cName = baseTypeName.Substring(idx+1); asName = typeName.Substring(0,idx); AssemblyName assemName = LocateAssembly(asName,baseTypeName); // Console.WriteLine("ty: {0} {1} {2} {3}", baseTypeName, typeName, assemName, asName); if ( assemName != null ) { try { String stuff = String.Format("{0},{1}", baseTypeName, assemName.FullName); // Console.WriteLine("st: {0}", stuff); Type t = Type.GetType(stuff); // Console.WriteLine("st: {0} %{1}%", stuff,t); if ( t!=null ) { // save away the mapping for next time.. AddAssemblyMapping(asName,assemName); return InstantiateGeneric(t,tyArgs); } Assembly localA = Assembly.Load(assemName.FullName); t = localA.GetType(baseTypeName); // Console.WriteLine("st: {0} {1} %{2}%", stuff,localA, t); if ( t!=null ) { // save away the mapping for next time.. AddAssemblyMapping(asName,assemName); return InstantiateGeneric(t,tyArgs); } } catch (Exception) { ; } } } foreach(KeyValuePair kv in m_assemblyNames) { try { ArrayList al = (ArrayList)kv.Value; for(int jx=0;jx(); } if ( m_assemblyNames.ContainsKey(nsName) ) { ArrayList al = m_assemblyNames[nsName]; if ( !al.Contains(aName) ) { al.Add(aName); } // (basic!) Q: do we need to update the hashtable with new a-list or is it not a copy? } else { ArrayList al = new ArrayList(); al.Add(aName); m_assemblyNames.Add(nsName,al); } } [ComVisible(false)] private Type[] fromTypeArgString(String genericTypeArgs) { int tyArgs = 0; int idx = 0; int i = 0; int nidx = 0; Type[] typeArgs; /* Count them... */ if ( genericTypeArgs != null && genericTypeArgs.Length > 0 ) { tyArgs++; } while ( idx < genericTypeArgs.Length ) { if ( (idx=genericTypeArgs.IndexOf(';',idx)) > 0 ) { tyArgs++; idx++; } else { break; } } // Console.WriteLine("xx: '{0}'", tyArgs); /* Create array to hold the args.. */ typeArgs = new Type[tyArgs]; idx = 0; while ( tyArgs > 0 ) { String arg; tyArgs--; if ( (nidx=genericTypeArgs.IndexOf(';',idx)) > 0 ) { arg = genericTypeArgs.Substring(idx,nidx-idx); } else { arg = genericTypeArgs.Substring(idx); } // Unravel the name of the class/type. TypeName argTypeName = TypeName.ParseType(arg); Type aty = null; idx = nidx+1; try { aty = GetType(argTypeName.m_class); if ( aty != null ) { try { Assembly localA = Assembly.LoadFrom(argTypeName.m_assembly); aty = localA.GetType(argTypeName.m_class); } catch (Exception) { ; } } } catch (Exception){ ; } typeArgs[i]=aty; i++; } return typeArgs; } protected Object CreateInstance(TypeName typeName, Object[] args) { Type t = GetType(typeName.m_class); if ( t!=null ) { try { Assembly localA = Assembly.LoadFrom(typeName.m_assembly); t = localA.GetType(typeName.m_class); } catch (Exception) { ; } } if ( t!=null ) { if ( t.IsSubclassOf(typeof(System.ValueType)) ) { if ( args.Length == 1 ) { return Convert.ChangeType(args[0],t); } } try { BindingFlags flgs =(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance); Object o =Activator.CreateInstance(t, flgs, null, args, null); return o; } catch (Exception e) { Logger.Debug("Constructor failure: {0}", e); return null; } } return null; } }; }