Import from 1.9a8 tarball
[mozilla-extra.git] / extensions / mono / src / proxy-generator.cs
blobbc6de5c2895fd451fdaf601a7df8c9eae59d0b47
1 using System;
2 using System.Runtime.InteropServices;
3 using System.Reflection;
4 using System.Reflection.Emit;
5 using Mozilla.XPCOM;
6 using MethodDescriptor = Mozilla.XPCOM.TypeInfo.MethodDescriptor;
7 using TypeTag = Mozilla.XPCOM.TypeInfo.TypeTag;
8 using ParamFlags = Mozilla.XPCOM.TypeInfo.ParamFlags;
10 namespace Mozilla.XPCOM
13 public class BaseProxy
15 protected IntPtr thisptr;
16 protected BaseProxy(IntPtr ptr) { thisptr = ptr; }
19 class ProxyGenerator
21 void EmitPtrAndFlagsStore(int argnum, IntPtr ptr, sbyte flags)
23 //= bufLocal[argnum].ptr = ptr;
24 ilg.Emit(OpCodes.Ldloc, bufLocal);
25 ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 8);
26 ilg.Emit(OpCodes.Add);
27 ilg.Emit(OpCodes.Ldc_I4, ptr.ToInt32());
28 ilg.Emit(OpCodes.Stind_I4);
30 //= bufLocal[argnum].flags = flags;
31 ilg.Emit(OpCodes.Ldloc, bufLocal);
32 ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 13);
33 ilg.Emit(OpCodes.Add);
34 ilg.Emit(OpCodes.Ldc_I4, (Int32)flags);
35 ilg.Emit(OpCodes.Stind_I1);
38 void EmitTypeStore(TypeInfo.TypeDescriptor t, int argnum)
40 ilg.Emit(OpCodes.Ldloc, bufLocal);
41 ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 12);
42 ilg.Emit(OpCodes.Add);
43 ilg.Emit(OpCodes.Ldc_I4, (Int32)t.tag);
44 ilg.Emit(OpCodes.Stind_I4);
47 void EmitComputeBufferLoc(int argnum)
49 ilg.Emit(OpCodes.Ldloc, bufLocal);
50 ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE);
51 ilg.Emit(OpCodes.Add);
54 void EmitPrepareArgStore(int argnum)
56 EmitComputeBufferLoc(argnum);
57 EmitLoadArg(argnum);
60 void EmitLoadArg(int argnum)
62 switch (argnum) {
63 case 0:
64 ilg.Emit(OpCodes.Ldarg_1);
65 break;
66 case 1:
67 ilg.Emit(OpCodes.Ldarg_2);
68 break;
69 case 2:
70 ilg.Emit(OpCodes.Ldarg_3);
71 break;
72 default:
73 if (argnum < 254)
74 ilg.Emit(OpCodes.Ldarg_S, argnum + 1);
75 else
76 ilg.Emit(OpCodes.Ldarg, argnum + 1);
77 break;
81 void EmitLoadReturnSlot_1(int slotnum)
83 ilg.Emit(OpCodes.Ldloc, bufLocal);
84 ilg.Emit(OpCodes.Ldc_I4, (slotnum - 1) * VARIANT_SIZE);
85 ilg.Emit(OpCodes.Add);
86 ilg.Emit(OpCodes.Ldind_I4);
89 void EmitOutParamPrep(TypeInfo.TypeDescriptor type, int argnum)
91 ilg.Emit(OpCodes.Nop);
92 ilg.Emit(OpCodes.Ldloc, bufLocal);
93 ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 13);
94 ilg.Emit(OpCodes.Add);
95 ilg.Emit(OpCodes.Ldc_I4, 1); // PTR_IS_DATA
96 ilg.Emit(OpCodes.Stind_I1);
98 ilg.Emit(OpCodes.Ldloc, bufLocal);
99 ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 8); // offsetof(ptr)
100 ilg.Emit(OpCodes.Add);
101 ilg.Emit(OpCodes.Ldloc, bufLocal);
102 ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 0); // offsetof(val)
103 ilg.Emit(OpCodes.Add);
104 ilg.Emit(OpCodes.Stind_I4); /* XXX 64-bitness! */
107 void EmitProxyConstructor()
109 ConstructorBuilder ctor =
110 tb.DefineConstructor(MethodAttributes.Family,
111 CallingConventions.Standard,
112 new Type[1] { typeof(IntPtr) });
113 ILGenerator ilg = ctor.GetILGenerator();
114 ilg.Emit(OpCodes.Ldarg_0);
115 ilg.Emit(OpCodes.Ldarg_1);
116 ilg.Emit(OpCodes.Call, baseProxyCtor);
117 ilg.Emit(OpCodes.Ret);
120 const int VARIANT_SIZE = 16; // sizeof(XPTCVariant)
122 const PropertyAttributes PROPERTY_ATTRS = PropertyAttributes.None;
124 PropertyBuilder lastProperty;
126 Type FixupInterfaceType(TypeInfo.ParamDescriptor desc)
128 try {
129 return TypeInfo.TypeForIID(desc.GetIID());
130 } catch (Exception e) {
131 // Console.WriteLine(e);
132 return typeof(object);
136 unsafe void GenerateProxyMethod(MethodDescriptor desc)
138 if (!desc.IsVisible()) {
139 Console.WriteLine("HIDDEN: {0}", desc);
140 return;
142 MethodAttributes methodAttrs =
143 MethodAttributes.Public | MethodAttributes.Virtual;
145 String methodName = desc.name;
146 if (desc.IsGetter()) {
147 methodName = "get_" + desc.name;
148 methodAttrs |= MethodAttributes.SpecialName;
149 } else if (desc.IsSetter()) {
150 methodName = "set_" + desc.name;
151 methodAttrs |= MethodAttributes.SpecialName;
154 // Fix up interface types in parameters
155 Type ret = desc.resultType;
156 if (ret == typeof(object))
157 ret = FixupInterfaceType(desc.args[desc.args.Length - 1]);
158 Type[] argTypes = (Type[])desc.argTypes.Clone();
159 for (int i = 0; i < argTypes.Length; i++) {
160 if (argTypes[i] == typeof(object))
161 argTypes[i] = FixupInterfaceType(desc.args[i]);
163 MethodBuilder meth = tb.DefineMethod(methodName, methodAttrs, ret, argTypes);
165 ilg = meth.GetILGenerator();
166 bufLocal = ilg.DeclareLocal(System.Type.GetType("System.Int32*"));
167 LocalBuilder guidLocal = ilg.DeclareLocal(typeof(System.Guid));
168 TypeInfo.ParamDescriptor[] args = desc.args;
170 Type marshalType = typeof(System.Runtime.InteropServices.Marshal);
172 // Marshal.AllocCoTaskMem(constify(argBufSize))
173 int argCount = args.Length;
174 int argBufSize = VARIANT_SIZE * args.Length;
176 ilg.Emit(OpCodes.Ldc_I4, argBufSize);
177 ilg.Emit(OpCodes.Call, marshalType.GetMethod("AllocCoTaskMem"));
178 ilg.Emit(OpCodes.Stloc, bufLocal);
180 for (int i = 0; i < argCount; i++) {
181 TypeInfo.ParamDescriptor param = args[i];
182 TypeInfo.TypeDescriptor type = param.type;
183 IntPtr ptr = IntPtr.Zero;
184 sbyte flags = 0;
185 EmitTypeStore(type, i);
187 if (param.IsOut()) {
188 EmitOutParamPrep(type, i);
189 if (!param.IsIn())
190 continue;
192 switch (type.tag) {
193 case TypeTag.Int8:
194 case TypeTag.Int16:
195 case TypeTag.UInt8:
196 case TypeTag.UInt16:
197 case TypeTag.Char:
198 case TypeTag.WChar:
199 case TypeTag.UInt32:
200 EmitPrepareArgStore(i);
201 // XXX do I need to cast this?
202 ilg.Emit(OpCodes.Castclass, typeof(Int32));
203 ilg.Emit(OpCodes.Stind_I4);
204 break;
205 case TypeTag.Int32:
206 EmitPrepareArgStore(i);
207 ilg.Emit(OpCodes.Stind_I4);
208 break;
209 case TypeTag.NSIdPtr:
210 EmitPrepareArgStore(i);
211 ilg.Emit(OpCodes.Stind_I4); // XXX 64-bitness
212 break;
213 case TypeTag.String:
214 EmitPrepareArgStore(i);
215 // the string arg is now on the stack
216 ilg.Emit(OpCodes.Call,
217 marshalType.GetMethod("StringToCoTaskMemAnsi"));
218 ilg.Emit(OpCodes.Stind_I4);
219 break;
220 case TypeTag.Interface:
221 EmitPrepareArgStore(i);
222 // MRP is the object passed as this arg
223 ilg.Emit(OpCodes.Ldloca_S, guidLocal);
224 ilg.Emit(OpCodes.Ldstr, param.GetIID().ToString());
225 ilg.Emit(OpCodes.Call, guidCtor);
226 ilg.Emit(OpCodes.Ldloca_S, guidLocal);
227 // stack is now objarg, ref guid
228 ilg.Emit(OpCodes.Call, typeof(CLRWrapper).GetMethod("Wrap"));
229 // now stack has the IntPtr in position to be stored.
230 ilg.Emit(OpCodes.Stind_I4);
231 break;
232 default:
234 String msg = String.Format("{0}: type {1} not supported",
235 param.Name(), type.tag.ToString());
236 throw new Exception(msg);
238 break;
240 EmitPtrAndFlagsStore(i, ptr, flags);
243 //= (void)XPTC_InvokeByIndex(thisptr, desc.index, length, bufLocal);
244 ilg.Emit(OpCodes.Ldarg_0);
245 ilg.Emit(OpCodes.Ldfld, thisField);
246 ilg.Emit(OpCodes.Ldc_I4, desc.index);
247 ilg.Emit(OpCodes.Ldc_I4, args.Length);
248 ilg.Emit(OpCodes.Ldloc_0);
249 ilg.Emit(OpCodes.Call, typeof(Mozilla.XPCOM.Invoker).
250 GetMethod("XPTC_InvokeByIndex",
251 BindingFlags.Static | BindingFlags.NonPublic));
252 ilg.Emit(OpCodes.Pop);
254 if (ret == typeof(string)) {
255 ilg.Emit(OpCodes.Ldstr, "FAKE RETURN STRING");
256 } else if (ret == typeof(object)) {
257 ilg.Emit(OpCodes.Newobj,
258 typeof(object).GetConstructor(new Type[0]));
259 } else if (ret == typeof(int)) {
260 EmitLoadReturnSlot_1(args.Length);
261 } else if (ret == typeof(void)) {
262 // Nothing
263 } else {
264 throw new Exception(String.Format("return type {0} not " +
265 "supported yet",
266 desc.result.type.tag));
269 //= Marshal.FreeCoTaskMem(bufLocal);
270 ilg.Emit(OpCodes.Ldloc, bufLocal);
271 ilg.Emit(OpCodes.Call, marshalType.GetMethod("FreeCoTaskMem"));
273 ilg.Emit(OpCodes.Ret);
275 ilg = null;
276 bufLocal = null;
278 if (desc.IsSetter()) {
279 if (lastProperty != null && lastProperty.Name == desc.name) {
280 lastProperty.SetSetMethod(meth);
281 } else {
282 tb.DefineProperty(desc.name, PROPERTY_ATTRS, desc.resultType,
283 new Type[0]).SetSetMethod(meth);
285 lastProperty = null;
286 } else if (desc.IsGetter()) {
287 lastProperty = tb.DefineProperty(desc.name, PROPERTY_ATTRS,
288 desc.resultType, new Type[0]);
289 lastProperty.SetGetMethod(meth);
290 } else {
291 lastProperty = null;
296 static ModuleBuilder module;
297 static AssemblyBuilder builder;
298 static ConstructorInfo baseProxyCtor;
299 static ConstructorInfo guidCtor;
301 internal static AssemblyBuilder ProxyAssembly {
302 get { return builder; }
305 static ProxyGenerator()
307 AssemblyName an = new AssemblyName();
308 an.Version = new Version(1, 0, 0, 0);
309 an.Name = "Mozilla.XPCOM.Proxies";
311 AppDomain curDom = AppDomain.CurrentDomain;
312 builder = curDom.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave);
314 String proxyDll =
315 System.Environment.GetEnvironmentVariable("XPCOM_DOTNET_SAVE_PROXIES");
316 if (proxyDll != null) {
317 module = builder.DefineDynamicModule(an.Name, proxyDll);
318 } else {
319 module = builder.DefineDynamicModule(an.Name);
322 baseProxyCtor = typeof(BaseProxy).
323 GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,
324 null, new Type[1] { typeof(IntPtr) }, null);
325 guidCtor = typeof(System.Guid).
326 GetConstructor(new Type[1] {typeof(string)});
329 TypeBuilder tb;
330 FieldInfo thisField;
331 LocalBuilder bufLocal;
332 ILGenerator ilg;
334 String proxyName;
336 internal ProxyGenerator(String name)
338 proxyName = name;
341 internal Assembly Generate()
343 if (module.GetType(proxyName) != null)
344 return module.Assembly;
346 String baseIfaceName = proxyName.Replace("Mozilla.XPCOM.Proxies.", "");
347 String ifaceName = "Mozilla.XPCOM.Interfaces." + baseIfaceName;
349 Type ifaceType = System.Type.GetType(ifaceName +
350 ",Mozilla.XPCOM.Interfaces", true);
352 ushort inheritedMethodCount;
353 String parentName = TypeInfo.GetParentInfo(baseIfaceName,
354 out inheritedMethodCount);
356 Type parentType;
357 if (parentName == null) {
358 parentType = typeof(BaseProxy);
359 } else {
360 parentType = System.Type.GetType("Mozilla.XPCOM.Proxies." +
361 parentName +
362 ",Mozilla.XPCOM.Proxies");
365 Console.WriteLine("Defining {0} (inherits {1}, impls {2})",
366 proxyName, parentType, ifaceName);
367 tb = module.DefineType(proxyName, TypeAttributes.Class, parentType,
368 new Type[1] { ifaceType });
370 thisField = typeof(BaseProxy).GetField("thisptr",
371 BindingFlags.NonPublic |
372 BindingFlags.Instance);
374 EmitProxyConstructor();
376 MethodDescriptor[] descs = TypeInfo.GetMethodData(baseIfaceName);
378 for (int i = inheritedMethodCount; i < descs.Length; i++) {
379 if (descs[i] != null)
380 GenerateProxyMethod(descs[i]);
383 tb.CreateType();
385 thisField = null;
386 tb = null;
388 return module.Assembly;