335 строки
12 KiB
C#
335 строки
12 KiB
C#
using System;
|
|
using System.Runtime.InteropServices;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
using Mozilla.XPCOM;
|
|
using MethodDescriptor = Mozilla.XPCOM.TypeInfo.MethodDescriptor;
|
|
using TypeTag = Mozilla.XPCOM.TypeInfo.TypeTag;
|
|
using ParamFlags = Mozilla.XPCOM.TypeInfo.ParamFlags;
|
|
|
|
public class Test
|
|
{
|
|
[DllImport("xpcom-dotnet.so")]
|
|
static extern int StartXPCOM(out IntPtr srvmgr);
|
|
|
|
static IntPtr srvmgr;
|
|
|
|
[DllImport("test.so", EntryPoint="GetImpl")]
|
|
public static extern IntPtr GetTestImpl();
|
|
|
|
static void GenerateInterfaceMethod(TypeBuilder tb, MethodDescriptor desc)
|
|
{
|
|
if (!desc.IsVisible()) {
|
|
Console.WriteLine("HIDDEN: {0}", desc);
|
|
return;
|
|
}
|
|
const MethodAttributes attrs = MethodAttributes.Public |
|
|
MethodAttributes.Abstract | MethodAttributes.Virtual;
|
|
tb.DefineMethod(desc.name, attrs, desc.resultType, desc.argTypes);
|
|
Console.WriteLine("\t{0}", desc);
|
|
}
|
|
|
|
static void EmitPtrAndFlagsStore(ILGenerator ilg, LocalBuilder bufLocal,
|
|
int argnum, IntPtr ptr, sbyte flags)
|
|
{
|
|
//= bufLocal[argnum].ptr = ptr;
|
|
ilg.Emit(OpCodes.Ldloc, bufLocal);
|
|
ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 8);
|
|
ilg.Emit(OpCodes.Add);
|
|
ilg.Emit(OpCodes.Ldc_I4, ptr.ToInt32());
|
|
ilg.Emit(OpCodes.Stind_I4);
|
|
|
|
//= bufLocal[argnum].flags = flags;
|
|
ilg.Emit(OpCodes.Ldloc, bufLocal);
|
|
ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 13);
|
|
ilg.Emit(OpCodes.Add);
|
|
ilg.Emit(OpCodes.Ldc_I4, (Int32)flags);
|
|
ilg.Emit(OpCodes.Stind_I1);
|
|
}
|
|
|
|
static void EmitTypeStore(ILGenerator ilg, LocalBuilder bufLocal,
|
|
TypeInfo.TypeDescriptor t, int argnum)
|
|
{
|
|
ilg.Emit(OpCodes.Ldloc, bufLocal);
|
|
ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 12);
|
|
ilg.Emit(OpCodes.Add);
|
|
ilg.Emit(OpCodes.Ldc_I4, (Int32)t.tag);
|
|
ilg.Emit(OpCodes.Stind_I4);
|
|
}
|
|
|
|
static void EmitComputeBufferLoc(ILGenerator ilg, LocalBuilder bufLocal,
|
|
int argnum)
|
|
{
|
|
ilg.Emit(OpCodes.Ldloc, bufLocal);
|
|
ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE);
|
|
ilg.Emit(OpCodes.Add);
|
|
}
|
|
|
|
static void EmitPrepareArgStore(ILGenerator ilg, LocalBuilder bufLocal,
|
|
int argnum)
|
|
{
|
|
EmitComputeBufferLoc(ilg, bufLocal, argnum);
|
|
EmitLoadArg(ilg, argnum);
|
|
}
|
|
|
|
static void EmitLoadArg(ILGenerator ilg, int argnum)
|
|
{
|
|
switch (argnum) {
|
|
case 0:
|
|
ilg.Emit(OpCodes.Ldarg_1);
|
|
break;
|
|
case 1:
|
|
ilg.Emit(OpCodes.Ldarg_2);
|
|
break;
|
|
case 2:
|
|
ilg.Emit(OpCodes.Ldarg_3);
|
|
break;
|
|
default:
|
|
if (argnum < 254)
|
|
ilg.Emit(OpCodes.Ldarg_S, argnum + 1);
|
|
else
|
|
ilg.Emit(OpCodes.Ldarg, argnum + 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void EmitLoadReturnSlot_1(ILGenerator ilg, LocalBuilder bufLocal,
|
|
int slotnum)
|
|
{
|
|
ilg.Emit(OpCodes.Ldloc, bufLocal);
|
|
ilg.Emit(OpCodes.Ldc_I4, (slotnum - 1) * VARIANT_SIZE);
|
|
ilg.Emit(OpCodes.Add);
|
|
ilg.Emit(OpCodes.Ldind_I4);
|
|
}
|
|
|
|
static void EmitOutParamPrep(ILGenerator ilg, LocalBuilder bufLocal,
|
|
TypeInfo.TypeDescriptor type, int argnum)
|
|
{
|
|
ilg.Emit(OpCodes.Nop);
|
|
ilg.Emit(OpCodes.Ldloc, bufLocal);
|
|
ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 13);
|
|
ilg.Emit(OpCodes.Add);
|
|
ilg.Emit(OpCodes.Ldc_I4, 1); // PTR_IS_DATA
|
|
ilg.Emit(OpCodes.Stind_I1);
|
|
|
|
ilg.Emit(OpCodes.Ldloc, bufLocal);
|
|
ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 8); // offsetof(ptr)
|
|
ilg.Emit(OpCodes.Add);
|
|
ilg.Emit(OpCodes.Ldloc, bufLocal);
|
|
ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE + 0); // offsetof(val)
|
|
ilg.Emit(OpCodes.Add);
|
|
ilg.Emit(OpCodes.Stind_I4); /* XXX 64-bitness! */
|
|
}
|
|
|
|
static void EmitProxyConstructor(TypeBuilder tb, FieldInfo thisField)
|
|
{
|
|
ConstructorBuilder ctor =
|
|
tb.DefineConstructor(MethodAttributes.Public,
|
|
CallingConventions.Standard,
|
|
new Type[1] { typeof(IntPtr) });
|
|
ILGenerator ilg = ctor.GetILGenerator();
|
|
ilg.Emit(OpCodes.Ldarg_0);
|
|
ilg.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));
|
|
ilg.Emit(OpCodes.Ldarg_0);
|
|
ilg.Emit(OpCodes.Ldarg_1);
|
|
ilg.Emit(OpCodes.Stfld, thisField);
|
|
ilg.Emit(OpCodes.Ret);
|
|
}
|
|
|
|
const int VARIANT_SIZE = 16; /* sizeof(XPTCVariant) */
|
|
|
|
unsafe static void GenerateProxyMethod(TypeBuilder tb,
|
|
MethodDescriptor desc,
|
|
FieldInfo thisField)
|
|
{
|
|
if (!desc.IsVisible()) {
|
|
Console.WriteLine("HIDDEN: {0}", desc);
|
|
return;
|
|
}
|
|
const MethodAttributes attrs =
|
|
MethodAttributes.Public | MethodAttributes.Virtual;
|
|
Type ret = desc.resultType;
|
|
MethodBuilder meth =
|
|
tb.DefineMethod(desc.name, attrs, ret, desc.argTypes);
|
|
ILGenerator ilg = meth.GetILGenerator();
|
|
TypeInfo.ParamDescriptor[] args = desc.args;
|
|
|
|
LocalBuilder bufLocal =
|
|
ilg.DeclareLocal(System.Type.GetType("System.Int32*"));
|
|
|
|
Type marshalType = typeof(System.Runtime.InteropServices.Marshal);
|
|
|
|
// Marshal.AllocCoTaskMem(constify(argBufSize))
|
|
int argCount = args.Length;
|
|
int argBufSize = VARIANT_SIZE * args.Length;
|
|
|
|
ilg.Emit(OpCodes.Ldc_I4, argBufSize);
|
|
ilg.Emit(OpCodes.Call, marshalType.GetMethod("AllocCoTaskMem"));
|
|
ilg.Emit(OpCodes.Stloc, bufLocal);
|
|
|
|
for (int i = 0; i < argCount; i++) {
|
|
TypeInfo.ParamDescriptor param = args[i];
|
|
TypeInfo.TypeDescriptor type = param.type;
|
|
IntPtr ptr = IntPtr.Zero;
|
|
sbyte flags = 0;
|
|
EmitTypeStore(ilg, bufLocal, type, i);
|
|
|
|
if ((param.flags & ParamFlags.Out) != 0) {
|
|
EmitOutParamPrep(ilg, bufLocal, type, i);
|
|
continue;
|
|
}
|
|
switch (type.tag) {
|
|
case TypeTag.Int8:
|
|
case TypeTag.Int16:
|
|
case TypeTag.UInt8:
|
|
case TypeTag.UInt16:
|
|
case TypeTag.Char:
|
|
case TypeTag.WChar:
|
|
case TypeTag.UInt32:
|
|
EmitPrepareArgStore(ilg, bufLocal, i);
|
|
// XXX do I need to cast this?
|
|
ilg.Emit(OpCodes.Castclass, typeof(Int32));
|
|
ilg.Emit(OpCodes.Stind_I4);
|
|
break;
|
|
case TypeTag.Int32:
|
|
EmitPrepareArgStore(ilg, bufLocal, i);
|
|
ilg.Emit(OpCodes.Stind_I4);
|
|
break;
|
|
case TypeTag.String:
|
|
EmitPrepareArgStore(ilg, bufLocal, i);
|
|
// the string arg is now on the stack
|
|
ilg.Emit(OpCodes.Call,
|
|
marshalType.GetMethod("StringToCoTaskMemAnsi"));
|
|
ilg.Emit(OpCodes.Stind_I4);
|
|
break;
|
|
default:
|
|
/*
|
|
String msg = String.Format("{0}: type {1} not supported",
|
|
param.Name(), type.tag.ToString());
|
|
throw new Exception(msg);
|
|
*/
|
|
break;
|
|
}
|
|
EmitPtrAndFlagsStore(ilg, bufLocal, i, ptr, flags);
|
|
}
|
|
|
|
//= (void)XPTC_InvokeByIndex(thisptr, desc.index, length, bufLocal);
|
|
ilg.Emit(OpCodes.Ldarg_0);
|
|
ilg.Emit(OpCodes.Ldfld, thisField);
|
|
ilg.Emit(OpCodes.Ldc_I4, desc.index);
|
|
ilg.Emit(OpCodes.Ldc_I4, args.Length);
|
|
ilg.Emit(OpCodes.Ldloc_0);
|
|
ilg.Emit(OpCodes.Call, typeof(Mozilla.XPCOM.Invoker).
|
|
GetMethod("XPTC_InvokeByIndex",
|
|
BindingFlags.Static | BindingFlags.NonPublic));
|
|
ilg.Emit(OpCodes.Pop);
|
|
|
|
if (ret == typeof(string)) {
|
|
ilg.Emit(OpCodes.Ldstr, "FAKE RETURN STRING");
|
|
} else if (ret == typeof(object)) {
|
|
ilg.Emit(OpCodes.Newobj,
|
|
typeof(object).GetConstructor(new Type[0]));
|
|
} else if (ret == typeof(int)) {
|
|
EmitLoadReturnSlot_1(ilg, bufLocal, args.Length);
|
|
} else if (ret == typeof(void)) {
|
|
// Nothing
|
|
} else {
|
|
throw new Exception(String.Format("return type {0} not " +
|
|
"supported yet",
|
|
desc.result.type.tag));
|
|
}
|
|
|
|
//= Marshal.FreeCoTaskMem(bufLocal);
|
|
ilg.Emit(OpCodes.Ldloc, bufLocal);
|
|
ilg.Emit(OpCodes.Call, marshalType.GetMethod("FreeCoTaskMem"));
|
|
|
|
ilg.Emit(OpCodes.Ret);
|
|
Console.WriteLine("$\t{0}", desc);
|
|
}
|
|
|
|
public static void Main(string[] args)
|
|
{
|
|
int res = StartXPCOM(out srvmgr);
|
|
|
|
if (res != 0) {
|
|
Console.WriteLine("StartXPCOM failed: {0:X2}", res);
|
|
return;
|
|
}
|
|
|
|
string ifaceName = args[0];
|
|
|
|
MethodDescriptor[] descs = TypeInfo.GetMethodData(ifaceName);
|
|
Console.WriteLine("Interface {0}:", ifaceName);
|
|
|
|
AssemblyName an = new AssemblyName();
|
|
an.Version = new Version(1, 0, 0, 0);
|
|
an.Name = "Mozilla.XPCOM.Interfaces." + ifaceName;
|
|
|
|
AppDomain currentDomain = AppDomain.CurrentDomain;
|
|
|
|
AssemblyBuilderAccess access;
|
|
if (args.Length > 1)
|
|
access = AssemblyBuilderAccess.RunAndSave;
|
|
else
|
|
access = AssemblyBuilderAccess.Run;
|
|
AssemblyBuilder ab = currentDomain.DefineDynamicAssembly(an, access);
|
|
|
|
ModuleBuilder mb;
|
|
if (args.Length > 1)
|
|
mb = ab.DefineDynamicModule(an.Name, args[1]);
|
|
else
|
|
mb = ab.DefineDynamicModule(an.Name);
|
|
|
|
TypeBuilder ifaceTb = mb.DefineType(ifaceName, (TypeAttributes.Public |
|
|
TypeAttributes.Interface));
|
|
|
|
for (int i = 3; i < descs.Length; i++) {
|
|
GenerateInterfaceMethod(ifaceTb, descs[i]);
|
|
}
|
|
|
|
ifaceTb.CreateType();
|
|
|
|
TypeBuilder proxyTb = mb.DefineType(ifaceName + "$Proxy",
|
|
(TypeAttributes.Class),
|
|
typeof(object),
|
|
new Type[1] { ifaceTb } );
|
|
FieldBuilder thisField = proxyTb.DefineField("this", typeof(IntPtr),
|
|
FieldAttributes.Private);
|
|
EmitProxyConstructor(proxyTb, thisField);
|
|
|
|
for (int i = 3; i < descs.Length; i++) {
|
|
GenerateProxyMethod(proxyTb, descs[i], thisField);
|
|
}
|
|
|
|
if (args.Length > 1)
|
|
ab.Save(args[1]);
|
|
|
|
Type proxyType = proxyTb.CreateType();
|
|
Console.WriteLine("proxyType: {0}", proxyType);
|
|
ConstructorInfo proxyCtor =
|
|
proxyType.GetConstructor(new Type[1] { typeof(IntPtr) });
|
|
Console.WriteLine("proxyCtor: {0}", proxyCtor);
|
|
|
|
IntPtr impl = GetTestImpl();
|
|
Console.WriteLine("proxyThis: {0:X2}", impl.ToInt32());
|
|
object proxy = proxyCtor.Invoke(new object[] { impl });
|
|
|
|
MethodInfo proxyAdd = proxyType.GetMethod("add");
|
|
Console.WriteLine("proxyAdd: {0}", proxyAdd);
|
|
object proxyRet = proxyAdd.Invoke(proxy, new object[] { 3, 5 });
|
|
Console.WriteLine("proxyRet: {0}", (int)proxyRet);
|
|
|
|
MethodInfo proxySay = proxyType.GetMethod("say");
|
|
Console.WriteLine("proxySay: {0}", proxySay);
|
|
proxySay.Invoke(proxy, new object[] { "holy cow!" });
|
|
|
|
PropertyInfo proxyIntProp = proxyType.GetProperty("intProp");
|
|
Console.WriteLine("proxyIntProp: {0}", proxyIntProp);
|
|
Console.WriteLine("proxyIntProp(get): {0}",
|
|
proxyIntProp.GetValue(proxy, null));
|
|
proxyIntProp.SetValue(proxy, 31337, null);
|
|
Console.WriteLine("proxyIntProp(get): {0}",
|
|
proxyIntProp.GetValue(proxy, null));
|
|
}
|
|
}
|