Back to index

lightning-sunbird  0.9+nobinonly
generate-assembly.cs
Go to the documentation of this file.
00001 using System;
00002 using System.Runtime.InteropServices;
00003 using System.Reflection;
00004 using System.Reflection.Emit;
00005 using Mozilla.XPCOM;
00006 using MethodDescriptor = Mozilla.XPCOM.TypeInfo.MethodDescriptor;
00007 using TypeTag = Mozilla.XPCOM.TypeInfo.TypeTag;
00008 using ParamFlags = Mozilla.XPCOM.TypeInfo.ParamFlags;
00009 
00010 public class Test
00011 {
00012     [DllImport("xpcom-dotnet.so")]
00013     static extern int StartXPCOM(out IntPtr srvmgr);
00014     
00015     static IntPtr srvmgr;
00016 
00017     [DllImport("test.so", EntryPoint="GetImpl")]
00018     public static extern IntPtr GetTestImpl();
00019 
00020     static void GenerateInterfaceMethod(TypeBuilder tb, MethodDescriptor desc)
00021     {
00022         if (!desc.IsVisible()) {
00023             Console.WriteLine("HIDDEN: {0}", desc);
00024             return;
00025         }
00026         const MethodAttributes attrs = MethodAttributes.Public |
00027             MethodAttributes.Abstract | MethodAttributes.Virtual;
00028         tb.DefineMethod(desc.name, attrs, desc.resultType, desc.argTypes);
00029         Console.WriteLine("\t{0}", desc);
00030     }
00031 
00032     static void EmitPtrAndFlagsStore(ILGenerator ilg, LocalBuilder bufLocal,
00033                                      int argnum, IntPtr ptr, sbyte flags)
00034     {
00035         //= bufLocal[argnum].ptr = ptr;
00036         ilg.Emit(OpCodes.Ldloc, bufLocal);
00037         ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 8);
00038         ilg.Emit(OpCodes.Add);
00039         ilg.Emit(OpCodes.Ldc_I4, ptr.ToInt32());
00040         ilg.Emit(OpCodes.Stind_I4);
00041 
00042         //= bufLocal[argnum].flags = flags;
00043         ilg.Emit(OpCodes.Ldloc, bufLocal);
00044         ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 13);
00045         ilg.Emit(OpCodes.Add);
00046         ilg.Emit(OpCodes.Ldc_I4, (Int32)flags);
00047         ilg.Emit(OpCodes.Stind_I1);
00048     }
00049 
00050     static void EmitTypeStore(ILGenerator ilg, LocalBuilder bufLocal,
00051                               TypeInfo.TypeDescriptor t, int argnum)
00052     {
00053         ilg.Emit(OpCodes.Ldloc, bufLocal);
00054         ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 12);
00055         ilg.Emit(OpCodes.Add);
00056         ilg.Emit(OpCodes.Ldc_I4, (Int32)t.tag);
00057         ilg.Emit(OpCodes.Stind_I4);
00058     }
00059 
00060     static void EmitComputeBufferLoc(ILGenerator ilg, LocalBuilder bufLocal,
00061                                      int argnum)
00062     {
00063         ilg.Emit(OpCodes.Ldloc, bufLocal);
00064         ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE);
00065         ilg.Emit(OpCodes.Add);
00066     }
00067 
00068     static void EmitPrepareArgStore(ILGenerator ilg, LocalBuilder bufLocal,
00069                                     int argnum)
00070     {
00071         EmitComputeBufferLoc(ilg, bufLocal, argnum);
00072         EmitLoadArg(ilg, argnum);
00073     }
00074 
00075     static void EmitLoadArg(ILGenerator ilg, int argnum)
00076     {
00077         switch (argnum) {
00078         case 0:
00079             ilg.Emit(OpCodes.Ldarg_1);
00080             break;
00081         case 1:
00082             ilg.Emit(OpCodes.Ldarg_2);
00083             break;
00084         case 2:
00085             ilg.Emit(OpCodes.Ldarg_3);
00086             break;
00087         default:
00088             if (argnum < 254)
00089                 ilg.Emit(OpCodes.Ldarg_S, argnum + 1);
00090             else
00091                 ilg.Emit(OpCodes.Ldarg, argnum + 1);
00092             break;
00093         }
00094     }
00095 
00096     static void EmitLoadReturnSlot_1(ILGenerator ilg, LocalBuilder bufLocal,
00097                                      int slotnum)
00098     {
00099         ilg.Emit(OpCodes.Ldloc, bufLocal);
00100         ilg.Emit(OpCodes.Ldc_I4, (slotnum - 1) * VARIANT_SIZE);
00101         ilg.Emit(OpCodes.Add);
00102         ilg.Emit(OpCodes.Ldind_I4);
00103     }
00104 
00105     static void EmitOutParamPrep(ILGenerator ilg, LocalBuilder bufLocal,
00106                                  TypeInfo.TypeDescriptor type, int argnum)
00107     {
00108         ilg.Emit(OpCodes.Nop);
00109         ilg.Emit(OpCodes.Ldloc, bufLocal);
00110         ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 13);
00111         ilg.Emit(OpCodes.Add);
00112         ilg.Emit(OpCodes.Ldc_I4, 1); // PTR_IS_DATA
00113         ilg.Emit(OpCodes.Stind_I1);
00114         
00115         ilg.Emit(OpCodes.Ldloc, bufLocal);
00116         ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 8); // offsetof(ptr)
00117         ilg.Emit(OpCodes.Add);
00118         ilg.Emit(OpCodes.Ldloc, bufLocal);
00119         ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 0); // offsetof(val)
00120         ilg.Emit(OpCodes.Add);
00121         ilg.Emit(OpCodes.Stind_I4); /* XXX 64-bitness! */
00122     }
00123 
00124     static void EmitProxyConstructor(TypeBuilder tb, FieldInfo thisField)
00125     {
00126         ConstructorBuilder ctor = 
00127             tb.DefineConstructor(MethodAttributes.Public,
00128                                  CallingConventions.Standard,
00129                                  new Type[1] { typeof(IntPtr) });
00130         ILGenerator ilg = ctor.GetILGenerator();
00131         ilg.Emit(OpCodes.Ldarg_0);
00132         ilg.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));
00133         ilg.Emit(OpCodes.Ldarg_0);
00134         ilg.Emit(OpCodes.Ldarg_1);
00135         ilg.Emit(OpCodes.Stfld, thisField);
00136         ilg.Emit(OpCodes.Ret);
00137     }
00138 
00139     const int VARIANT_SIZE = 16; /* sizeof(XPTCVariant) */
00140     
00141     unsafe static void GenerateProxyMethod(TypeBuilder tb,
00142                                            MethodDescriptor desc,
00143                                            FieldInfo thisField)
00144     {
00145         if (!desc.IsVisible()) {
00146             Console.WriteLine("HIDDEN: {0}", desc);
00147             return;
00148         }
00149         const MethodAttributes attrs = 
00150             MethodAttributes.Public | MethodAttributes.Virtual;
00151         Type ret = desc.resultType;
00152         MethodBuilder meth =
00153             tb.DefineMethod(desc.name, attrs, ret, desc.argTypes);
00154         ILGenerator ilg = meth.GetILGenerator();
00155         TypeInfo.ParamDescriptor[] args = desc.args;
00156         
00157         LocalBuilder bufLocal = 
00158             ilg.DeclareLocal(System.Type.GetType("System.Int32*"));
00159 
00160         Type marshalType = typeof(System.Runtime.InteropServices.Marshal);
00161 
00162         // Marshal.AllocCoTaskMem(constify(argBufSize))
00163         int argCount = args.Length;
00164         int argBufSize = VARIANT_SIZE * args.Length;
00165 
00166         ilg.Emit(OpCodes.Ldc_I4, argBufSize);
00167         ilg.Emit(OpCodes.Call, marshalType.GetMethod("AllocCoTaskMem"));
00168         ilg.Emit(OpCodes.Stloc, bufLocal);
00169 
00170         for (int i = 0; i < argCount; i++) {
00171             TypeInfo.ParamDescriptor param = args[i];
00172             TypeInfo.TypeDescriptor type = param.type;
00173             IntPtr ptr = IntPtr.Zero;
00174             sbyte flags = 0;
00175             EmitTypeStore(ilg, bufLocal, type, i);
00176 
00177             if ((param.flags & ParamFlags.Out) != 0) {
00178                 EmitOutParamPrep(ilg, bufLocal, type, i);
00179                 continue;
00180             }
00181             switch (type.tag) {
00182             case TypeTag.Int8:
00183             case TypeTag.Int16:
00184             case TypeTag.UInt8:
00185             case TypeTag.UInt16:
00186             case TypeTag.Char:
00187             case TypeTag.WChar:
00188             case TypeTag.UInt32:
00189                 EmitPrepareArgStore(ilg, bufLocal, i);
00190                 // XXX do I need to cast this?
00191                 ilg.Emit(OpCodes.Castclass, typeof(Int32));
00192                 ilg.Emit(OpCodes.Stind_I4);
00193                 break;
00194             case TypeTag.Int32:
00195                 EmitPrepareArgStore(ilg, bufLocal, i);
00196                 ilg.Emit(OpCodes.Stind_I4);
00197                 break;
00198             case TypeTag.String:
00199                 EmitPrepareArgStore(ilg, bufLocal, i);
00200                 // the string arg is now on the stack
00201                 ilg.Emit(OpCodes.Call,
00202                          marshalType.GetMethod("StringToCoTaskMemAnsi"));
00203                 ilg.Emit(OpCodes.Stind_I4);
00204                 break;
00205             default:
00206                 /*
00207                 String msg = String.Format("{0}: type {1} not supported",
00208                                     param.Name(), type.tag.ToString());
00209                 throw new Exception(msg);
00210                 */
00211                 break;
00212             }
00213             EmitPtrAndFlagsStore(ilg, bufLocal, i, ptr, flags);
00214         }
00215 
00216         //= (void)XPTC_InvokeByIndex(thisptr, desc.index, length, bufLocal);
00217         ilg.Emit(OpCodes.Ldarg_0);
00218         ilg.Emit(OpCodes.Ldfld, thisField);
00219         ilg.Emit(OpCodes.Ldc_I4, desc.index);
00220         ilg.Emit(OpCodes.Ldc_I4, args.Length);
00221         ilg.Emit(OpCodes.Ldloc_0);
00222         ilg.Emit(OpCodes.Call, typeof(Mozilla.XPCOM.Invoker).
00223                  GetMethod("XPTC_InvokeByIndex",
00224                            BindingFlags.Static | BindingFlags.NonPublic));
00225         ilg.Emit(OpCodes.Pop);
00226 
00227         if (ret == typeof(string)) {
00228             ilg.Emit(OpCodes.Ldstr, "FAKE RETURN STRING");
00229         } else if (ret == typeof(object)) {
00230             ilg.Emit(OpCodes.Newobj,
00231                      typeof(object).GetConstructor(new Type[0]));
00232         } else if (ret == typeof(int)) {
00233             EmitLoadReturnSlot_1(ilg, bufLocal, args.Length);
00234         } else if (ret == typeof(void)) {
00235             // Nothing
00236         } else {
00237             throw new Exception(String.Format("return type {0} not " +
00238                                               "supported yet",
00239                                               desc.result.type.tag));
00240         }
00241 
00242         //= Marshal.FreeCoTaskMem(bufLocal);
00243         ilg.Emit(OpCodes.Ldloc, bufLocal);
00244         ilg.Emit(OpCodes.Call, marshalType.GetMethod("FreeCoTaskMem"));
00245 
00246         ilg.Emit(OpCodes.Ret);
00247         Console.WriteLine("$\t{0}", desc);
00248     }
00249 
00250     public static void Main(string[] args)
00251     {
00252         int res = StartXPCOM(out srvmgr);
00253 
00254         if (res != 0) {
00255             Console.WriteLine("StartXPCOM failed: {0:X2}", res);
00256             return;
00257         }
00258 
00259         string ifaceName = args[0];
00260 
00261         MethodDescriptor[] descs = TypeInfo.GetMethodData(ifaceName);
00262         Console.WriteLine("Interface {0}:", ifaceName);
00263         
00264         AssemblyName an = new AssemblyName();
00265         an.Version = new Version(1, 0, 0, 0);
00266         an.Name = "Mozilla.XPCOM.Interfaces." + ifaceName;
00267 
00268         AppDomain currentDomain = AppDomain.CurrentDomain;
00269 
00270         AssemblyBuilderAccess access;
00271         if (args.Length > 1)
00272             access = AssemblyBuilderAccess.RunAndSave;
00273         else
00274             access = AssemblyBuilderAccess.Run;
00275         AssemblyBuilder ab = currentDomain.DefineDynamicAssembly(an, access);
00276         
00277         ModuleBuilder mb;
00278         if (args.Length > 1)
00279             mb = ab.DefineDynamicModule(an.Name, args[1]);
00280         else
00281             mb = ab.DefineDynamicModule(an.Name);
00282 
00283         TypeBuilder ifaceTb = mb.DefineType(ifaceName, (TypeAttributes.Public |
00284                                                    TypeAttributes.Interface));
00285 
00286         for (int i = 3; i < descs.Length; i++) {
00287             GenerateInterfaceMethod(ifaceTb, descs[i]);
00288         }
00289 
00290         ifaceTb.CreateType();
00291 
00292         TypeBuilder proxyTb = mb.DefineType(ifaceName + "$Proxy",
00293                                             (TypeAttributes.Class),
00294                                             typeof(object),
00295                                             new Type[1] { ifaceTb } );
00296         FieldBuilder thisField = proxyTb.DefineField("this", typeof(IntPtr),
00297                                                      FieldAttributes.Private);
00298         EmitProxyConstructor(proxyTb, thisField);
00299 
00300         for (int i = 3; i < descs.Length; i++) {
00301             GenerateProxyMethod(proxyTb, descs[i], thisField);
00302         }
00303 
00304         if (args.Length > 1)
00305             ab.Save(args[1]);
00306 
00307         Type proxyType = proxyTb.CreateType();
00308         Console.WriteLine("proxyType: {0}", proxyType);
00309         ConstructorInfo proxyCtor = 
00310             proxyType.GetConstructor(new Type[1] { typeof(IntPtr) });
00311         Console.WriteLine("proxyCtor: {0}", proxyCtor);
00312 
00313         IntPtr impl = GetTestImpl();
00314         Console.WriteLine("proxyThis: {0:X2}", impl.ToInt32());
00315         object proxy = proxyCtor.Invoke(new object[] { impl });
00316 
00317         MethodInfo proxyAdd = proxyType.GetMethod("add");
00318         Console.WriteLine("proxyAdd: {0}", proxyAdd);
00319         object proxyRet = proxyAdd.Invoke(proxy, new object[] { 3, 5 });
00320         Console.WriteLine("proxyRet: {0}", (int)proxyRet);
00321 
00322         MethodInfo proxySay = proxyType.GetMethod("say");
00323         Console.WriteLine("proxySay: {0}", proxySay);
00324         proxySay.Invoke(proxy, new object[] { "holy cow!" });
00325 
00326         PropertyInfo proxyIntProp = proxyType.GetProperty("intProp");
00327         Console.WriteLine("proxyIntProp: {0}", proxyIntProp);
00328         Console.WriteLine("proxyIntProp(get): {0}",
00329                           proxyIntProp.GetValue(proxy, null));
00330         proxyIntProp.SetValue(proxy, 31337, null);
00331         Console.WriteLine("proxyIntProp(get): {0}",
00332                           proxyIntProp.GetValue(proxy, null));
00333     }
00334 }