Back to index

lightning-sunbird  0.9+nobinonly
proxy-generator.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 namespace Mozilla.XPCOM
00011 {
00012 
00013 public class BaseProxy
00014 {
00015     protected IntPtr thisptr;
00016     protected BaseProxy(IntPtr ptr) { thisptr = ptr; }
00017 }
00018 
00019 class ProxyGenerator
00020 {
00021     void EmitPtrAndFlagsStore(int argnum, IntPtr ptr, sbyte flags)
00022     {
00023         //= bufLocal[argnum].ptr = ptr;
00024         ilg.Emit(OpCodes.Ldloc, bufLocal);
00025         ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 8);
00026         ilg.Emit(OpCodes.Add);
00027         ilg.Emit(OpCodes.Ldc_I4, ptr.ToInt32());
00028         ilg.Emit(OpCodes.Stind_I4);
00029 
00030         //= bufLocal[argnum].flags = flags;
00031         ilg.Emit(OpCodes.Ldloc, bufLocal);
00032         ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 13);
00033         ilg.Emit(OpCodes.Add);
00034         ilg.Emit(OpCodes.Ldc_I4, (Int32)flags);
00035         ilg.Emit(OpCodes.Stind_I1);
00036     }
00037 
00038     void EmitTypeStore(TypeInfo.TypeDescriptor t, int argnum)
00039     {
00040         ilg.Emit(OpCodes.Ldloc, bufLocal);
00041         ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 12);
00042         ilg.Emit(OpCodes.Add);
00043         ilg.Emit(OpCodes.Ldc_I4, (Int32)t.tag);
00044         ilg.Emit(OpCodes.Stind_I4);
00045     }
00046 
00047     void EmitComputeBufferLoc(int argnum)
00048     {
00049         ilg.Emit(OpCodes.Ldloc, bufLocal);
00050         ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE);
00051         ilg.Emit(OpCodes.Add);
00052     }
00053 
00054     void EmitPrepareArgStore(int argnum)
00055     {
00056         EmitComputeBufferLoc(argnum);
00057         EmitLoadArg(argnum);
00058     }
00059 
00060     void EmitLoadArg(int argnum)
00061     {
00062         switch (argnum) {
00063         case 0:
00064             ilg.Emit(OpCodes.Ldarg_1);
00065             break;
00066         case 1:
00067             ilg.Emit(OpCodes.Ldarg_2);
00068             break;
00069         case 2:
00070             ilg.Emit(OpCodes.Ldarg_3);
00071             break;
00072         default:
00073             if (argnum < 254)
00074                 ilg.Emit(OpCodes.Ldarg_S, argnum + 1);
00075             else
00076                 ilg.Emit(OpCodes.Ldarg, argnum + 1);
00077             break;
00078         }
00079     }
00080 
00081     void EmitLoadReturnSlot_1(int slotnum)
00082     {
00083         ilg.Emit(OpCodes.Ldloc, bufLocal);
00084         ilg.Emit(OpCodes.Ldc_I4, (slotnum - 1) * VARIANT_SIZE);
00085         ilg.Emit(OpCodes.Add);
00086         ilg.Emit(OpCodes.Ldind_I4);
00087     }
00088 
00089     void EmitOutParamPrep(TypeInfo.TypeDescriptor type, int argnum)
00090     {
00091         ilg.Emit(OpCodes.Nop);
00092         ilg.Emit(OpCodes.Ldloc, bufLocal);
00093         ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 13);
00094         ilg.Emit(OpCodes.Add);
00095         ilg.Emit(OpCodes.Ldc_I4, 1); // PTR_IS_DATA
00096         ilg.Emit(OpCodes.Stind_I1);
00097         
00098         ilg.Emit(OpCodes.Ldloc, bufLocal);
00099         ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 8); // offsetof(ptr)
00100         ilg.Emit(OpCodes.Add);
00101         ilg.Emit(OpCodes.Ldloc, bufLocal);
00102         ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 0); // offsetof(val)
00103         ilg.Emit(OpCodes.Add);
00104         ilg.Emit(OpCodes.Stind_I4); /* XXX 64-bitness! */
00105     }
00106 
00107     void EmitProxyConstructor()
00108     {
00109         ConstructorBuilder ctor = 
00110             tb.DefineConstructor(MethodAttributes.Family,
00111                                  CallingConventions.Standard,
00112                                  new Type[1] { typeof(IntPtr) });
00113         ILGenerator ilg = ctor.GetILGenerator();
00114         ilg.Emit(OpCodes.Ldarg_0);
00115         ilg.Emit(OpCodes.Ldarg_1);
00116         ilg.Emit(OpCodes.Call, baseProxyCtor);
00117         ilg.Emit(OpCodes.Ret);
00118     }
00119 
00120     const int VARIANT_SIZE = 16; // sizeof(XPTCVariant)
00121 
00122     const PropertyAttributes PROPERTY_ATTRS = PropertyAttributes.None;
00123 
00124     PropertyBuilder lastProperty;
00125 
00126     Type FixupInterfaceType(TypeInfo.ParamDescriptor desc)
00127     {
00128         try {
00129             return TypeInfo.TypeForIID(desc.GetIID());
00130         } catch (Exception e) {
00131             // Console.WriteLine(e);
00132             return typeof(object);
00133         }
00134     }
00135 
00136     unsafe void GenerateProxyMethod(MethodDescriptor desc)
00137     {
00138         if (!desc.IsVisible()) {
00139             Console.WriteLine("HIDDEN: {0}", desc);
00140             return;
00141         }
00142         MethodAttributes methodAttrs = 
00143             MethodAttributes.Public | MethodAttributes.Virtual;
00144 
00145         String methodName = desc.name;
00146         if (desc.IsGetter()) {
00147             methodName = "get_" + desc.name;
00148             methodAttrs |= MethodAttributes.SpecialName;
00149         } else if (desc.IsSetter()) {
00150             methodName = "set_" + desc.name;
00151             methodAttrs |= MethodAttributes.SpecialName;
00152         }
00153             
00154         // Fix up interface types in parameters
00155         Type ret = desc.resultType;
00156         if (ret == typeof(object))
00157             ret = FixupInterfaceType(desc.args[desc.args.Length - 1]);
00158         Type[] argTypes = (Type[])desc.argTypes.Clone();
00159         for (int i = 0; i < argTypes.Length; i++) {
00160             if (argTypes[i] == typeof(object))
00161                 argTypes[i] = FixupInterfaceType(desc.args[i]);
00162         }
00163         MethodBuilder meth = tb.DefineMethod(methodName, methodAttrs, ret, argTypes);
00164 
00165         ilg = meth.GetILGenerator();
00166         bufLocal = ilg.DeclareLocal(System.Type.GetType("System.Int32*"));
00167         LocalBuilder guidLocal = ilg.DeclareLocal(typeof(System.Guid));
00168         TypeInfo.ParamDescriptor[] args = desc.args;
00169 
00170         Type marshalType = typeof(System.Runtime.InteropServices.Marshal);
00171 
00172         // Marshal.AllocCoTaskMem(constify(argBufSize))
00173         int argCount = args.Length;
00174         int argBufSize = VARIANT_SIZE * args.Length;
00175 
00176         ilg.Emit(OpCodes.Ldc_I4, argBufSize);
00177         ilg.Emit(OpCodes.Call, marshalType.GetMethod("AllocCoTaskMem"));
00178         ilg.Emit(OpCodes.Stloc, bufLocal);
00179 
00180         for (int i = 0; i < argCount; i++) {
00181             TypeInfo.ParamDescriptor param = args[i];
00182             TypeInfo.TypeDescriptor type = param.type;
00183             IntPtr ptr = IntPtr.Zero;
00184             sbyte flags = 0;
00185             EmitTypeStore(type, i);
00186 
00187             if (param.IsOut()) {
00188                 EmitOutParamPrep(type, i);
00189                 if (!param.IsIn())
00190                     continue;
00191             }
00192             switch (type.tag) {
00193             case TypeTag.Int8:
00194             case TypeTag.Int16:
00195             case TypeTag.UInt8:
00196             case TypeTag.UInt16:
00197             case TypeTag.Char:
00198             case TypeTag.WChar:
00199             case TypeTag.UInt32:
00200                 EmitPrepareArgStore(i);
00201                 // XXX do I need to cast this?
00202                 ilg.Emit(OpCodes.Castclass, typeof(Int32));
00203                 ilg.Emit(OpCodes.Stind_I4);
00204                 break;
00205             case TypeTag.Int32:
00206                 EmitPrepareArgStore(i);
00207                 ilg.Emit(OpCodes.Stind_I4);
00208                 break;
00209             case TypeTag.NSIdPtr:
00210                 EmitPrepareArgStore(i);
00211                 ilg.Emit(OpCodes.Stind_I4); // XXX 64-bitness
00212                 break;
00213             case TypeTag.String:
00214                 EmitPrepareArgStore(i);
00215                 // the string arg is now on the stack
00216                 ilg.Emit(OpCodes.Call,
00217                          marshalType.GetMethod("StringToCoTaskMemAnsi"));
00218                 ilg.Emit(OpCodes.Stind_I4);
00219                 break;
00220             case TypeTag.Interface:
00221                 EmitPrepareArgStore(i);
00222                 // MRP is the object passed as this arg
00223                 ilg.Emit(OpCodes.Ldloca_S, guidLocal);
00224                 ilg.Emit(OpCodes.Ldstr, param.GetIID().ToString());
00225                 ilg.Emit(OpCodes.Call, guidCtor);
00226                 ilg.Emit(OpCodes.Ldloca_S, guidLocal);
00227                 // stack is now objarg, ref guid
00228                 ilg.Emit(OpCodes.Call, typeof(CLRWrapper).GetMethod("Wrap"));
00229                 // now stack has the IntPtr in position to be stored.
00230                 ilg.Emit(OpCodes.Stind_I4);
00231                 break;
00232             default:
00233                 /*
00234                 String msg = String.Format("{0}: type {1} not supported",
00235                                     param.Name(), type.tag.ToString());
00236                 throw new Exception(msg);
00237                 */
00238                 break;
00239             }
00240             EmitPtrAndFlagsStore(i, ptr, flags);
00241         }
00242 
00243         //= (void)XPTC_InvokeByIndex(thisptr, desc.index, length, bufLocal);
00244         ilg.Emit(OpCodes.Ldarg_0);
00245         ilg.Emit(OpCodes.Ldfld, thisField);
00246         ilg.Emit(OpCodes.Ldc_I4, desc.index);
00247         ilg.Emit(OpCodes.Ldc_I4, args.Length);
00248         ilg.Emit(OpCodes.Ldloc_0);
00249         ilg.Emit(OpCodes.Call, typeof(Mozilla.XPCOM.Invoker).
00250                  GetMethod("XPTC_InvokeByIndex",
00251                            BindingFlags.Static | BindingFlags.NonPublic));
00252         ilg.Emit(OpCodes.Pop);
00253 
00254         if (ret == typeof(string)) {
00255             ilg.Emit(OpCodes.Ldstr, "FAKE RETURN STRING");
00256         } else if (ret == typeof(object)) {
00257             ilg.Emit(OpCodes.Newobj,
00258                      typeof(object).GetConstructor(new Type[0]));
00259         } else if (ret == typeof(int)) {
00260             EmitLoadReturnSlot_1(args.Length);
00261         } else if (ret == typeof(void)) {
00262             // Nothing
00263         } else {
00264             throw new Exception(String.Format("return type {0} not " +
00265                                               "supported yet",
00266                                               desc.result.type.tag));
00267         }
00268 
00269         //= Marshal.FreeCoTaskMem(bufLocal);
00270         ilg.Emit(OpCodes.Ldloc, bufLocal);
00271         ilg.Emit(OpCodes.Call, marshalType.GetMethod("FreeCoTaskMem"));
00272 
00273         ilg.Emit(OpCodes.Ret);
00274 
00275         ilg = null;
00276         bufLocal = null;
00277 
00278         if (desc.IsSetter()) {
00279             if (lastProperty != null && lastProperty.Name == desc.name) {
00280                 lastProperty.SetSetMethod(meth);
00281             } else {
00282                 tb.DefineProperty(desc.name, PROPERTY_ATTRS, desc.resultType,
00283                                   new Type[0]).SetSetMethod(meth);
00284             }
00285             lastProperty = null;
00286         } else if (desc.IsGetter()) {
00287             lastProperty = tb.DefineProperty(desc.name, PROPERTY_ATTRS,
00288                                              desc.resultType, new Type[0]);
00289             lastProperty.SetGetMethod(meth);
00290         } else {
00291             lastProperty = null;
00292         }
00293 
00294     }
00295 
00296     static ModuleBuilder module;
00297     static AssemblyBuilder builder;
00298     static ConstructorInfo baseProxyCtor;
00299     static ConstructorInfo guidCtor;
00300 
00301     internal static AssemblyBuilder ProxyAssembly {
00302         get { return builder; }
00303     }
00304 
00305     static ProxyGenerator()
00306     {
00307         AssemblyName an = new AssemblyName();
00308         an.Version = new Version(1, 0, 0, 0);
00309         an.Name = "Mozilla.XPCOM.Proxies";
00310         
00311         AppDomain curDom = AppDomain.CurrentDomain;
00312         builder = curDom.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave);
00313         
00314         String proxyDll =
00315             System.Environment.GetEnvironmentVariable("XPCOM_DOTNET_SAVE_PROXIES");
00316         if (proxyDll != null) {
00317             module = builder.DefineDynamicModule(an.Name, proxyDll);
00318         } else {
00319             module = builder.DefineDynamicModule(an.Name);
00320         }
00321 
00322         baseProxyCtor = typeof(BaseProxy).
00323             GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,
00324                            null, new Type[1] { typeof(IntPtr) }, null);
00325         guidCtor = typeof(System.Guid).
00326             GetConstructor(new Type[1] {typeof(string)});
00327     }
00328 
00329     TypeBuilder tb;
00330     FieldInfo thisField;
00331     LocalBuilder bufLocal;
00332     ILGenerator ilg;
00333 
00334     String proxyName;
00335 
00336     internal ProxyGenerator(String name)
00337     {
00338         proxyName = name;
00339     }
00340 
00341     internal Assembly Generate()
00342     {
00343         if (module.GetType(proxyName) != null)
00344             return module.Assembly;
00345 
00346         String baseIfaceName = proxyName.Replace("Mozilla.XPCOM.Proxies.", "");
00347         String ifaceName = "Mozilla.XPCOM.Interfaces." + baseIfaceName;
00348 
00349         Type ifaceType = System.Type.GetType(ifaceName + 
00350                                              ",Mozilla.XPCOM.Interfaces", true);
00351 
00352         ushort inheritedMethodCount;
00353         String parentName = TypeInfo.GetParentInfo(baseIfaceName,
00354                                                    out inheritedMethodCount);
00355 
00356         Type parentType;
00357         if (parentName == null) {
00358             parentType = typeof(BaseProxy);
00359         } else {
00360             parentType = System.Type.GetType("Mozilla.XPCOM.Proxies." +
00361                                              parentName +
00362                                              ",Mozilla.XPCOM.Proxies");
00363         }
00364 
00365         Console.WriteLine("Defining {0} (inherits {1}, impls {2})",
00366                           proxyName, parentType, ifaceName);
00367         tb = module.DefineType(proxyName, TypeAttributes.Class, parentType,
00368                                new Type[1] { ifaceType });
00369         
00370         thisField = typeof(BaseProxy).GetField("thisptr",
00371                                                BindingFlags.NonPublic |
00372                                                BindingFlags.Instance);
00373 
00374         EmitProxyConstructor();
00375         
00376         MethodDescriptor[] descs = TypeInfo.GetMethodData(baseIfaceName);
00377 
00378         for (int i = inheritedMethodCount; i < descs.Length; i++) {
00379             if (descs[i] != null) 
00380                 GenerateProxyMethod(descs[i]);
00381         }
00382 
00383         tb.CreateType();
00384 
00385         thisField = null;
00386         tb = null;
00387 
00388         return module.Assembly;
00389     }
00390 }
00391 
00392 }