diff --git a/mozilla/extensions/mono/src/Makefile b/mozilla/extensions/mono/src/Makefile new file mode 100644 index 00000000000..f2b7accbd91 --- /dev/null +++ b/mozilla/extensions/mono/src/Makefile @@ -0,0 +1,16 @@ +CXXFLAGS = -shared -g -fPIC -fno-rtti $(shell pkg-config --cflags mozilla-xpcom) +LDFLAGS = $(shell pkg-config --libs mozilla-xpcom) + +all: xpcom-dotnet.so xpcom-dotnet.dll + +MCS ?= mcs +MCSFLAGS ?= /debug + +xpcom-dotnet.so: typeinfo.cpp xpcom-core.cpp wrapped-clr.cpp + $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) + +xpcom-dotnet.dll: typeinfo.cs xptinvoke.cs wrapped-clr.cs components.cs proxy-generator.cs interface-generator.cs + $(MCS) $(MCSFLAGS) -unsafe /t:library /out:$@ $^ + +clean: + rm -f xpcom-dotnet.{so,dll} diff --git a/mozilla/extensions/mono/src/components.cs b/mozilla/extensions/mono/src/components.cs new file mode 100644 index 00000000000..974f77c1a30 --- /dev/null +++ b/mozilla/extensions/mono/src/components.cs @@ -0,0 +1,95 @@ +using System; +using Mozilla.XPCOM; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; + +namespace Mozilla.XPCOM { + +public class Components +{ + [DllImport("xpcom-dotnet.so")] + static extern int StartXPCOM(out IntPtr srvmgr); + + static IntPtr serviceManager = IntPtr.Zero; + + public static IntPtr ServiceManager { + get { + return serviceManager; + } + } + + static void RegisterAppDomainHooks() + { + AppDomain.CurrentDomain.TypeResolve += + new ResolveEventHandler(TypeResolve); + AppDomain.CurrentDomain.AssemblyResolve += + new ResolveEventHandler(AssemblyResolve); + Console.WriteLine("XPCOM AppDomain hooks registered."); + } + + static Assembly AssemblyResolve(object sender, ResolveEventArgs args) + { + Console.WriteLine("Resolving: {0}", args.Name); + return null; + } + + public static AssemblyBuilder ProxyAssembly { + get { + return ProxyGenerator.ProxyAssembly; + } + } + + static Assembly TypeResolve(object sender, ResolveEventArgs args) + { + if (args.Name.StartsWith("Mozilla.XPCOM.Proxies.")) { + ProxyGenerator gen = new ProxyGenerator(args.Name); + return gen.Generate(); + } + +#if NOTYET + if (args.Name.StartsWith("Mozilla.XPCOM.Interfaces.")) { + InterfaceGenerator gen = new InterfaceGenerator(args.Name); + return gen.Generate(); + } +#endif + + return null; + } + + public static void Init() + { + if (serviceManager != IntPtr.Zero) + return; + + int res = StartXPCOM(out serviceManager); + if (res != 0) { + throw new Exception(String.Format("StartXPCOM failed: {0:X2}", + res)); + } + + RegisterAppDomainHooks(); + } + + [DllImport("test.so", EntryPoint="GetImpl")] + public static extern IntPtr GetTestImpl(); + + public static object CreateInstance(string contractId, Type iface) + { + String typeName = iface.FullName; + String proxyName = typeName.Replace(".Interfaces.", ".Proxies."); + Console.WriteLine("Need proxy class {0} for {1}", proxyName, typeName); + Type proxyType = System.Type.GetType(proxyName); + if (contractId == "@off.net/test-component;1") { + ConstructorInfo ctor = + proxyType.GetConstructor(BindingFlags.NonPublic | + BindingFlags.Instance, + null, new Type[1] { typeof(IntPtr) }, + null); + return ctor.Invoke(new object[] { GetTestImpl() }); + } + return null; + } +} + +} diff --git a/mozilla/extensions/mono/src/interface-generator.cs b/mozilla/extensions/mono/src/interface-generator.cs new file mode 100644 index 00000000000..2d42865e1c2 --- /dev/null +++ b/mozilla/extensions/mono/src/interface-generator.cs @@ -0,0 +1,139 @@ +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; + +namespace Mozilla.XPCOM +{ + +class InterfaceGenerator +{ + static ModuleBuilder module; + + static InterfaceGenerator() + { + AssemblyName an = new AssemblyName(); + an.Version = new Version(1, 0, 0, 0); + an.Name = "Mozilla.XPCOM.Interfaces"; + + AppDomain curDom = AppDomain.CurrentDomain; + AssemblyBuilder ab = + curDom.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run); + + module = ab.DefineDynamicModule(an.Name); + } + + String typeName; + TypeBuilder tb; + + internal InterfaceGenerator(String name) + { + typeName = name; + } + + const PropertyAttributes PROPERTY_ATTRS = PropertyAttributes.None; + const MethodAttributes METHOD_ATTRS = MethodAttributes.Public | + MethodAttributes.Abstract | MethodAttributes.Virtual; + PropertyBuilder lastProperty = null; + + Type FixupInterfaceType(TypeInfo.ParamDescriptor desc) + { + try { + return TypeInfo.TypeForIID(desc.GetIID()); + } catch (Exception e) { + // Console.WriteLine(e); + return typeof(object); + } + } + + void GenerateInterfaceMethod(TypeInfo.MethodDescriptor desc) + { + if (desc == null || !desc.IsVisible()) + return; + + MethodAttributes methodAttrs = METHOD_ATTRS; + + String methodName = desc.name; + if (desc.IsGetter()) { + methodName = "get_" + desc.name; + methodAttrs |= MethodAttributes.SpecialName; + } else if (desc.IsSetter()) { + methodName = "set_" + desc.name; + methodAttrs |= MethodAttributes.SpecialName; + } + + // Fix up interface types in parameters + Type ret = desc.resultType; + if (ret == typeof(object)) + ret = FixupInterfaceType(desc.args[desc.args.Length - 1]); + Type[] argTypes = (Type[])desc.argTypes.Clone(); + for (int i = 0; i < argTypes.Length; i++) { + if (argTypes[i] == typeof(object)) + argTypes[i] = FixupInterfaceType(desc.args[i]); + } + MethodBuilder meth = tb.DefineMethod(methodName, methodAttrs, ret, + desc.argTypes); + + if (desc.IsSetter()) { + if (lastProperty != null && lastProperty.Name == desc.name) { + lastProperty.SetSetMethod(meth); + } else { + tb.DefineProperty(desc.name, PROPERTY_ATTRS, desc.resultType, + new Type[0]).SetSetMethod(meth); + } + lastProperty = null; + } else if (desc.IsGetter()) { + lastProperty = tb.DefineProperty(desc.name, PROPERTY_ATTRS, + desc.resultType, new Type[0]); + lastProperty.SetGetMethod(meth); + } else { + lastProperty = null; + } + } + + internal Assembly Generate() + { + if (module.GetType(typeName) != null) + return module.Assembly; + + String ifaceName = typeName.Replace("Mozilla.XPCOM.Interfaces.", ""); + TypeInfo.MethodDescriptor[] descs = + TypeInfo.GetMethodData(ifaceName); + + ushort inheritedMethodCount; + String parentName = TypeInfo.GetParentInfo(ifaceName, + out inheritedMethodCount); + Type parentType; + + if (parentName != null) { + parentName = "Mozilla.XPCOM.Interfaces." + parentName; + parentType = module.Assembly.GetType(parentName); + if (parentType == null) { + InterfaceGenerator gen = new InterfaceGenerator(parentName); + parentType = gen.Generate().GetType(parentName); + } + } else { + parentType = typeof(object); + } + + tb = module.DefineType(typeName, + TypeAttributes.Public | TypeAttributes.Interface, + parentType); + + for (int i = inheritedMethodCount; i < descs.Length; i++) { + if (descs[i] != null) + GenerateInterfaceMethod(descs[i]); + } + + tb.CreateType(); + + return module.Assembly; + } + +} + +} diff --git a/mozilla/extensions/mono/src/proxy-generator.cs b/mozilla/extensions/mono/src/proxy-generator.cs new file mode 100644 index 00000000000..7a710ec8fb8 --- /dev/null +++ b/mozilla/extensions/mono/src/proxy-generator.cs @@ -0,0 +1,392 @@ +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; + +namespace Mozilla.XPCOM +{ + +public class BaseProxy +{ + protected IntPtr thisptr; + protected BaseProxy(IntPtr ptr) { thisptr = ptr; } +} + +class ProxyGenerator +{ + void EmitPtrAndFlagsStore(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); + } + + void EmitTypeStore(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); + } + + void EmitComputeBufferLoc(int argnum) + { + ilg.Emit(OpCodes.Ldloc, bufLocal); + ilg.Emit(OpCodes.Ldc_I4, argnum * VARIANT_SIZE); + ilg.Emit(OpCodes.Add); + } + + void EmitPrepareArgStore(int argnum) + { + EmitComputeBufferLoc(argnum); + EmitLoadArg(argnum); + } + + void EmitLoadArg(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; + } + } + + void EmitLoadReturnSlot_1(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); + } + + void EmitOutParamPrep(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! */ + } + + void EmitProxyConstructor() + { + ConstructorBuilder ctor = + tb.DefineConstructor(MethodAttributes.Family, + CallingConventions.Standard, + new Type[1] { typeof(IntPtr) }); + ILGenerator ilg = ctor.GetILGenerator(); + ilg.Emit(OpCodes.Ldarg_0); + ilg.Emit(OpCodes.Ldarg_1); + ilg.Emit(OpCodes.Call, baseProxyCtor); + ilg.Emit(OpCodes.Ret); + } + + const int VARIANT_SIZE = 16; // sizeof(XPTCVariant) + + const PropertyAttributes PROPERTY_ATTRS = PropertyAttributes.None; + + PropertyBuilder lastProperty; + + Type FixupInterfaceType(TypeInfo.ParamDescriptor desc) + { + try { + return TypeInfo.TypeForIID(desc.GetIID()); + } catch (Exception e) { + // Console.WriteLine(e); + return typeof(object); + } + } + + unsafe void GenerateProxyMethod(MethodDescriptor desc) + { + if (!desc.IsVisible()) { + Console.WriteLine("HIDDEN: {0}", desc); + return; + } + MethodAttributes methodAttrs = + MethodAttributes.Public | MethodAttributes.Virtual; + + String methodName = desc.name; + if (desc.IsGetter()) { + methodName = "get_" + desc.name; + methodAttrs |= MethodAttributes.SpecialName; + } else if (desc.IsSetter()) { + methodName = "set_" + desc.name; + methodAttrs |= MethodAttributes.SpecialName; + } + + // Fix up interface types in parameters + Type ret = desc.resultType; + if (ret == typeof(object)) + ret = FixupInterfaceType(desc.args[desc.args.Length - 1]); + Type[] argTypes = (Type[])desc.argTypes.Clone(); + for (int i = 0; i < argTypes.Length; i++) { + if (argTypes[i] == typeof(object)) + argTypes[i] = FixupInterfaceType(desc.args[i]); + } + MethodBuilder meth = tb.DefineMethod(methodName, methodAttrs, ret, argTypes); + + ilg = meth.GetILGenerator(); + bufLocal = ilg.DeclareLocal(System.Type.GetType("System.Int32*")); + LocalBuilder guidLocal = ilg.DeclareLocal(typeof(System.Guid)); + TypeInfo.ParamDescriptor[] args = desc.args; + + 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(type, i); + + if (param.IsOut()) { + EmitOutParamPrep(type, i); + if (!param.IsIn()) + 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(i); + // XXX do I need to cast this? + ilg.Emit(OpCodes.Castclass, typeof(Int32)); + ilg.Emit(OpCodes.Stind_I4); + break; + case TypeTag.Int32: + EmitPrepareArgStore(i); + ilg.Emit(OpCodes.Stind_I4); + break; + case TypeTag.NSIDPtr: + EmitPrepareArgStore(i); + ilg.Emit(OpCodes.Stind_I4); // XXX 64-bitness + break; + case TypeTag.String: + EmitPrepareArgStore(i); + // the string arg is now on the stack + ilg.Emit(OpCodes.Call, + marshalType.GetMethod("StringToCoTaskMemAnsi")); + ilg.Emit(OpCodes.Stind_I4); + break; + case TypeTag.Interface: + EmitPrepareArgStore(i); + // MRP is the object passed as this arg + ilg.Emit(OpCodes.Ldloca_S, guidLocal); + ilg.Emit(OpCodes.Ldstr, param.GetIID().ToString()); + ilg.Emit(OpCodes.Call, guidCtor); + ilg.Emit(OpCodes.Ldloca_S, guidLocal); + // stack is now objarg, ref guid + ilg.Emit(OpCodes.Call, typeof(CLRWrapper).GetMethod("Wrap")); + // now stack has the IntPtr in position to be stored. + 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(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(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); + + ilg = null; + bufLocal = null; + + if (desc.IsSetter()) { + if (lastProperty != null && lastProperty.Name == desc.name) { + lastProperty.SetSetMethod(meth); + } else { + tb.DefineProperty(desc.name, PROPERTY_ATTRS, desc.resultType, + new Type[0]).SetSetMethod(meth); + } + lastProperty = null; + } else if (desc.IsGetter()) { + lastProperty = tb.DefineProperty(desc.name, PROPERTY_ATTRS, + desc.resultType, new Type[0]); + lastProperty.SetGetMethod(meth); + } else { + lastProperty = null; + } + + } + + static ModuleBuilder module; + static AssemblyBuilder builder; + static ConstructorInfo baseProxyCtor; + static ConstructorInfo guidCtor; + + internal static AssemblyBuilder ProxyAssembly { + get { return builder; } + } + + static ProxyGenerator() + { + AssemblyName an = new AssemblyName(); + an.Version = new Version(1, 0, 0, 0); + an.Name = "Mozilla.XPCOM.Proxies"; + + AppDomain curDom = AppDomain.CurrentDomain; + builder = curDom.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave); + + String proxyDll = + System.Environment.GetEnvironmentVariable("XPCOM_DOTNET_SAVE_PROXIES"); + if (proxyDll != null) { + module = builder.DefineDynamicModule(an.Name, proxyDll); + } else { + module = builder.DefineDynamicModule(an.Name); + } + + baseProxyCtor = typeof(BaseProxy). + GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, + null, new Type[1] { typeof(IntPtr) }, null); + guidCtor = typeof(System.Guid). + GetConstructor(new Type[1] {typeof(string)}); + } + + TypeBuilder tb; + FieldInfo thisField; + LocalBuilder bufLocal; + ILGenerator ilg; + + String proxyName; + + internal ProxyGenerator(String name) + { + proxyName = name; + } + + internal Assembly Generate() + { + if (module.GetType(proxyName) != null) + return module.Assembly; + + String baseIfaceName = proxyName.Replace("Mozilla.XPCOM.Proxies.", ""); + String ifaceName = "Mozilla.XPCOM.Interfaces." + baseIfaceName; + + Type ifaceType = System.Type.GetType(ifaceName + + ",Mozilla.XPCOM.Interfaces", true); + + ushort inheritedMethodCount; + String parentName = TypeInfo.GetParentInfo(baseIfaceName, + out inheritedMethodCount); + + Type parentType; + if (parentName == null) { + parentType = typeof(BaseProxy); + } else { + parentType = System.Type.GetType("Mozilla.XPCOM.Proxies." + + parentName + + ",Mozilla.XPCOM.Proxies"); + } + + Console.WriteLine("Defining {0} (inherits {1}, impls {2})", + proxyName, parentType, ifaceName); + tb = module.DefineType(proxyName, TypeAttributes.Class, parentType, + new Type[1] { ifaceType }); + + thisField = typeof(BaseProxy).GetField("thisptr", + BindingFlags.NonPublic | + BindingFlags.Instance); + + EmitProxyConstructor(); + + MethodDescriptor[] descs = TypeInfo.GetMethodData(baseIfaceName); + + for (int i = inheritedMethodCount; i < descs.Length; i++) { + if (descs[i] != null) + GenerateProxyMethod(descs[i]); + } + + tb.CreateType(); + + thisField = null; + tb = null; + + return module.Assembly; + } +} + +} diff --git a/mozilla/extensions/mono/src/typeinfo.cpp b/mozilla/extensions/mono/src/typeinfo.cpp new file mode 100644 index 00000000000..3dd09f53a17 --- /dev/null +++ b/mozilla/extensions/mono/src/typeinfo.cpp @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +nsIInterfaceInfoManager* infomgr; + +extern "C" nsIEnumerator * +typeinfo_EnumerateInterfacesStart() +{ + nsIEnumerator *e; + if (NS_FAILED(infomgr->EnumerateInterfaces(&e)) || NS_FAILED(e->First())) + return 0; + + return e; +} + +extern "C" char * +typeinfo_EnumerateInterfacesNext(nsIEnumerator *e) +{ + nsCOMPtr isup; + while (1) { + if (e->IsDone() != NS_ENUMERATOR_FALSE || + NS_FAILED(e->CurrentItem(getter_AddRefs(isup))) || !isup) { + break; + } + + e->Next(); + nsCOMPtr iface(do_QueryInterface(isup)); + if (!iface) + break; + + PRBool scriptable; + if (NS_SUCCEEDED(iface->IsScriptable(&scriptable)) && + !scriptable) { + continue; + } + + char *name; + iface->GetName(&name); + return name; + } + + return 0; +} + +extern "C" void +typeinfo_EnumerateInterfacesStop(nsIEnumerator *e) +{ + NS_IF_RELEASE(e); +} + +extern "C" int +typeinfo_GetParentInfo(const char *ifaceName, + char **parentName, + PRUint16 *parentMethodCount) +{ + nsIInterfaceInfo *iinfo; + nsresult res; + + res = infomgr->GetInfoForName(ifaceName, &iinfo); + if (NS_FAILED(res)) + return res; + + nsCOMPtr parent; + res = iinfo->GetParent(getter_AddRefs(parent)); + if (NS_FAILED(res)) + return res; + + if (!parent) { + *parentName = 0; + *parentMethodCount = 0; + return NS_OK; + } + + res = parent->GetName(parentName); + if (NS_FAILED(res)) + return res; + + res = parent->GetMethodCount(parentMethodCount); + return res; +} + +extern "C" int +typeinfo_GetAllMethodData(const char *ifaceName, + const nsXPTMethodInfo ***infos, + PRUint16 *methodCount) +{ + nsIInterfaceInfo *iinfo; + nsresult res; + + res = infomgr->GetInfoForName(ifaceName, &iinfo); + if (NS_FAILED(res)) + return res; + + res = iinfo->GetMethodCount(methodCount); + if (NS_FAILED(res)) + return res; + + const nsXPTMethodInfo **infoarr = + new const nsXPTMethodInfo*[*methodCount]; + + *infos = &infoarr[0]; + + for (int i = 0; i < *methodCount; i++) { + res = iinfo->GetMethodInfo(i, &infoarr[i]); + if (NS_FAILED(res)) { + fprintf(stderr, "Getting method info for %s:%d: %08x\n", + ifaceName, i, res); + } + } + + return NS_OK; +} + +extern "C" int +typeinfo_GetMethodData(const char *ifaceName, int methIndex, + const nsXPTMethodInfo **info) +{ + nsIInterfaceInfo *iinfo; + nsresult res; + *info = 0; + + res = infomgr->GetInfoForName(ifaceName, &iinfo); + if (NS_FAILED(res)) + return res; + + return iinfo->GetMethodInfo(methIndex, info); +} + +extern "C" int +typeinfo_GetMethodData_iid_index(const nsID *iid, int index, + const nsXPTMethodInfo **info) +{ + nsIInterfaceInfo *iinfo; + nsresult res; + *info = 0; + + res = infomgr->GetInfoForIID(iid, &iinfo); + if (NS_FAILED(res)) + return res; + + return iinfo->GetMethodInfo(index, info); +} + +extern "C" int +typeinfo_GetMethodData_byname(const char *ifaceName, const char *methName, + PRUint16 *index, const nsXPTMethodInfo **info) +{ + nsIInterfaceInfo *iinfo; + nsresult res; + *info = 0; + + res = infomgr->GetInfoForName(ifaceName, &iinfo); + if (NS_FAILED(res)) + return res; + + return iinfo->GetMethodInfoForName(methName, index, info); +} + +extern "C" int +typeinfo_GetIIDForParam(const char *ifaceName, int methIndex, + int param, nsID *iid) +{ + nsIInterfaceInfo *iinfo; + nsresult res; + + // fprintf(stderr, "getting IID for %s:%d:%d\n", ifaceName, methIndex, + // param); + + res = infomgr->GetInfoForName(ifaceName, &iinfo); + if (NS_FAILED(res)) { + fprintf(stderr, "%d: FAILED (%08x)\n", __LINE__, res); + return res; + } + + const nsXPTMethodInfo *meth; + res = iinfo->GetMethodInfo(methIndex, &meth); + if (NS_FAILED(res)) { + fprintf(stderr, "%d: FAILED (%08x)\n", __LINE__, res); + return res; + } + + const nsXPTParamInfo& paramInfo = meth->GetParam(param); + res = iinfo->GetIIDForParamNoAlloc(methIndex, ¶mInfo, iid); + + char *idstr = iid->ToString(); + // fprintf(stderr, "outgoing iid is %s\n", idstr); + nsCRT::free(idstr); + if (NS_FAILED(res)) { + fprintf(stderr, "%d: FAILED (%08x)\n", __LINE__, res); + return res; + } + + return res; +} + +extern "C" int +typeinfo_GetNameForIID(nsID *iid, char **ifaceName) +{ + *ifaceName = 0; + return infomgr->GetNameForIID(iid, ifaceName); +} + +extern "C" nsAString * +typeinfo_WrapUnicode(const PRUnichar *chars, PRUint32 length) +{ + return new nsString(chars, length); +} + +extern "C" void +typeinfo_FreeWrappedUnicode(nsAString *str) +{ + delete str; +} diff --git a/mozilla/extensions/mono/src/typeinfo.cs b/mozilla/extensions/mono/src/typeinfo.cs new file mode 100644 index 00000000000..2679905d27f --- /dev/null +++ b/mozilla/extensions/mono/src/typeinfo.cs @@ -0,0 +1,530 @@ +using System; +using System.Runtime.InteropServices; +using Mozilla.XPCOM; + +namespace Mozilla.XPCOM { + +/* This doesn't need to be public, but making it 'internal' causes stress with + * TypeDescriptor.op_Implicit(XPTType). */ +[StructLayout(LayoutKind.Sequential)] +public struct XPTType +{ + public const byte FlagMask = 0xE0; + public const byte TagMask = 0x1F; + public byte prefix; + public byte arg1; + public byte arg2; + public byte padding; + public UInt16 arg3; + + static implicit operator XPTType(TypeInfo.TypeDescriptor td) + { + XPTType t = new XPTType(); + t.prefix = (byte)(td.flags | td.tag); + t.arg1 = td.arg1; + t.arg2 = td.arg2; + t.arg3 = td.arg3; + return t; + } +} + +[StructLayout(LayoutKind.Sequential)] +struct XPTParamDescriptor +{ + public byte param_flags; + public byte padding1; + public XPTType type; +} + +[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)] +struct XPTMethodDescriptor +{ + public String name; + public IntPtr args; /* XPTParamDescriptor */ + public IntPtr result; /* XPTParamDescriptor */ + public byte flags; + public byte numArgs; +} + +public class TypeInfo +{ + [Flags] + public enum TypeFlags : byte + { + Pointer = 0x80, UniquePointer = 0x40, Reference = 0x20 + } + + public enum TypeTag : byte + { + Int8 = 0, Int16 = 1, Int32 = 2, Int64 = 3, + UInt8 = 4, UInt16 = 5, UInt32 = 6, UInt64 = 7, + Float = 8, Double = 9, Bool = 10, Char = 11, + WChar = 12, Void = 13, NSIdPtr = 14, AString = 15, + String = 16, WString = 17, Interface = 18, InterfaceIs = 19, + Array = 20, StringSizeIs = 21, WStringSizeIs = 22, UTF8String = 23, + CString = 24, AString_dup = 25 + } + + public struct TypeDescriptor + { + public TypeFlags flags; + public TypeTag tag; + public byte arg1; + public byte arg2; + public UInt16 arg3; + + public override string ToString() + { + string res = String.Empty; + if ((Int32)flags != 0) + res = "[" + flags.ToString() + "]"; + res += tag.ToString(); + return res; + } + + public bool IsScalar() + { + if (tag <= TypeTag.WChar || + tag == TypeTag.String || tag == TypeTag.WString) { + return true; + } + return false; + } + + static implicit operator TypeDescriptor(XPTType t) + { + TypeDescriptor td = new TypeInfo.TypeDescriptor(); + td.flags = (TypeFlags)(t.prefix & XPTType.FlagMask); + td.tag = (TypeTag)(t.prefix & XPTType.TagMask); + td.arg1 = t.arg1; + td.arg2 = t.arg2; + td.arg3 = t.arg3; + return td; + } + + public Type AsCLRType() + { + switch (tag) { + case TypeTag.Int8: + return typeof(sbyte); + case TypeTag.Int16: + return typeof(Int16); + case TypeTag.Int32: + return typeof(Int32); + case TypeTag.Int64: + return typeof(Int64); + + case TypeTag.UInt8: + return typeof(byte); + case TypeTag.UInt16: + return typeof(UInt16); + case TypeTag.UInt32: + return typeof(UInt32); + case TypeTag.UInt64: + return typeof(UInt64); + + case TypeTag.Float: + return typeof(Single); + case TypeTag.Double: + return typeof(Double); + + case TypeTag.Bool: + return typeof(bool); + + case TypeTag.String: + return typeof(string); + case TypeTag.WString: + return typeof(string); + + case TypeTag.Interface: + return typeof(object); + case TypeTag.InterfaceIs: + return typeof(object); + + case TypeTag.Char: + return typeof(byte); + case TypeTag.WChar: + return typeof(char); + + case TypeTag.NSIdPtr: + return Type.GetType("System.Guid&"); + // return typeof(Guid).MakeByRefType(); NET_2_0 + + default: + string msg = String.Format("type {0} not yet supported ", + tag.ToString()); + + throw new Exception(msg); + } + } + } + + [Flags] + public enum ParamFlags : byte + { + In = 0x80, Out = 0x40, RetVal = 0x20, Shared = 0x10, Dipper = 0x08 + } + + public struct ParamDescriptor + { + public MethodDescriptor method; + public int index; + public ParamFlags flags; + public TypeDescriptor type; + + public Type GetCLRType() + { + Type t = type.AsCLRType(); + if (IsOut() && !IsRetVal()) { + Type reft = System.Type.GetType(t.FullName + "&"); + Console.WriteLine("{0} -> {1}", t.FullName, reft.FullName); + t = reft; + } + return t; + } + + public override string ToString() + { + return "[" + flags.ToString() + "] " + type.ToString(); + } + + public string Name() + { + return method.Name() + ":" + index; + } + + public bool IsOut() + { + return (flags & ParamFlags.Out) != 0; + } + + public bool IsIn() + { + return (flags & ParamFlags.In) != 0; + } + + public bool IsRetVal() + { + return (flags & ParamFlags.RetVal) != 0; + } + + public Guid GetIID() + { + if (type.tag != TypeTag.Interface) { + throw new Exception(String.Format("{0} not an interface type", + this)); + } + + Guid iid; + int res; + res = typeinfo_GetIIDForParam(method.ifaceName, method.index, + index, out iid); + if (res != 0) { + throw new + Exception(String.Format("GetIIDForParam failed: {0:X8}", + res)); + } + return iid; + } + + public string GetInterfaceName() + { + return TypeInfo.NameForIID(GetIID()); + } + } + + [Flags] + public enum MethodFlags : byte + { + Getter = 0x80, Setter = 0x40, NotXPCOM = 0x20, Constructor = 0x10, + Hidden = 0x08 + } + + public class MethodDescriptor + { + public int index; + public String name; + public ParamDescriptor[] args; + public ParamDescriptor result; + public Type[] argTypes; + public Type resultType; + public MethodFlags flags; + public String ifaceName; + + public bool IsVisible() + { + return (flags & (MethodFlags.Hidden | MethodFlags.NotXPCOM)) == 0; + } + + public bool IsSetter() + { + return (flags & MethodFlags.Setter) != 0; + } + + public bool IsGetter() + { + return (flags & MethodFlags.Getter) != 0; + } + + public string Name() + { + return ifaceName + ":" + name + "(" + index + ")"; + } + + public Type FindResultType() + { + for (int i = 0; i < args.Length; i++) { + if (args[i].IsRetVal()) + return args[i].GetCLRType(); + } + return typeof(void); + } + + public void BuildArgTypes() + { + if (!IsVisible()) { + argTypes = new Type[] { }; + return; + } + + if (resultType != typeof(void)) + argTypes = new Type[args.Length - 1]; + else + argTypes = new Type[args.Length]; + int j = 0; + for (int i = 0; i < args.Length; i++) { + if (args[i].IsRetVal()) + continue; + argTypes[j++] = args[i].GetCLRType(); + } + } + + public override string ToString() + { + string res = String.Empty; + if (flags != 0) + res = "[" + flags.ToString() + "] "; + res += result.ToString() + " " + name + "("; + for (int i = 0; i < args.Length; i++) { + if (i != 0) + res += ", "; + res += args[i].ToString(); + } + res += ")"; + return res; + } + } + + [DllImport("xpcom-dotnet.so")] + static extern int typeinfo_GetAllMethodData(String iface, + out IntPtr infos, + out UInt16 count); + + [DllImport("xpcom-dotnet.so")] + static extern int typeinfo_GetMethodData(String iface, int method, + out IntPtr info); + + + [DllImport("xpcom-dotnet.so")] + static extern int typeinfo_GetMethodData_byname(String iface, + String method, + out UInt16 index, + out IntPtr info); + + [DllImport("xpcom-dotnet.so")] + static extern int typeinfo_GetMethodData_iid_index(ref Guid iid, int index, + out IntPtr xptinfo); + + [DllImport("xpcom-dotnet.so")] + static extern int typeinfo_GetIIDForParam(String iface, int method, + int param, out Guid iid); + + [DllImport("xpcom-dotnet.so")] + static extern int typeinfo_GetNameForIID(ref Guid iid, out String name); + + [DllImport("xpcom-dotnet.so")] + public static extern IntPtr typeinfo_EnumerateInterfacesStart(); + + [DllImport("xpcom-dotnet.so")] + public static extern String typeinfo_EnumerateInterfacesNext(IntPtr e); + + [DllImport("xpcom-dotnet.so")] + public static extern void typeinfo_EnumerateInterfacesStop(IntPtr e); + + [DllImport("xpcom-dotnet.so")] + public static extern int typeinfo_GetParentInfo(String name, + out String parentName, + out UInt16 methodCount); + + public static String GetParentInfo(String name, out UInt16 methodCount) + { + String parentName; + int res = typeinfo_GetParentInfo(name, out parentName, + out methodCount); + if (res != 0) { + throw new Exception(String.Format("GetParentInfo({0}) " + + "failed: {1:X8}", name, res)); + } + return parentName; + } + + static ParamDescriptor BuildParamDescriptor(IntPtr paramptr, int index, + MethodDescriptor method) + { + XPTParamDescriptor xptpd = (XPTParamDescriptor) + Marshal.PtrToStructure(paramptr, typeof(XPTParamDescriptor)); + ParamDescriptor pd = new ParamDescriptor(); + pd.method = method; + pd.index = index; + pd.flags = (ParamFlags) xptpd.param_flags; + pd.type = xptpd.type; + return pd; + } + + static ParamDescriptor BuildRetValDescriptor(IntPtr paramptr, + MethodDescriptor method) + { + return BuildParamDescriptor(paramptr, -1, method); + } + + static ParamDescriptor[] BuildParamDescriptorArray(IntPtr paramptr, + int count, + MethodDescriptor method) + { + ParamDescriptor[] parr = new ParamDescriptor[count]; + + for (int i = 0; i < count; i++) { + parr[i] = BuildParamDescriptor(paramptr, i, method); + paramptr = (IntPtr)(paramptr.ToInt32() + 8); + } + return parr; + } + + static MethodDescriptor ConvertMethodDescriptor(IntPtr xptinfo, + string ifaceName, + int index) + { + XPTMethodDescriptor info; + info = (XPTMethodDescriptor) + Marshal.PtrToStructure(xptinfo, typeof(XPTMethodDescriptor)); + MethodDescriptor meth = new MethodDescriptor(); + meth.ifaceName = ifaceName; + meth.index = index; + meth.name = System.Char.ToUpper(info.name[0]) + info.name.Substring(1); + meth.flags = (MethodFlags)info.flags; + meth.args = BuildParamDescriptorArray(info.args, info.numArgs, meth); + meth.result = BuildRetValDescriptor(info.result, meth); + if (meth.IsVisible()) { + meth.resultType = meth.FindResultType(); + meth.BuildArgTypes(); + } + return meth; + } + + static public MethodDescriptor[] GetMethodData(string ifaceName) + { + IntPtr buffer; + UInt16 count; + + int res = typeinfo_GetAllMethodData(ifaceName, out buffer, out count); + if (res != 0) { + throw new Exception(String.Format("GetAllMethodData({0}) " + + "failed: {1:X8}", + ifaceName, res)); + } + + MethodDescriptor[] methods = new MethodDescriptor[count]; + + for (int i = 0; i < (UInt16)count; i++) { + IntPtr infoptr = Marshal.ReadIntPtr(buffer, i * + Marshal.SizeOf(typeof(IntPtr))); + try { + methods[i] = ConvertMethodDescriptor(infoptr, ifaceName, i); + } catch (Exception e) { + Console.WriteLine("skipping {0}[{1}]: {2}", + ifaceName, i, e.Message); + } + } + + if (count > 0) + Marshal.FreeCoTaskMem(buffer); + + return methods; + } + + static public MethodDescriptor GetMethodData(Guid iid, int index) + { + IntPtr xptinfo; + int res = typeinfo_GetMethodData_iid_index(ref iid, index, + out xptinfo); + if (res != 0) { + throw new Exception(String.Format("GetMethodData({0}.{1}) " + + "failed: {2:X8}", + iid, index, res)); + } + + String name; + res = typeinfo_GetNameForIID(ref iid, out name); + if (res != 0) { + throw new Exception(String.Format("GetNameForIID failed: {0:X8}", + res)); + } + return ConvertMethodDescriptor(xptinfo, name, index); + } + + static public String NameForIID(Guid iid) + { + String name; + int res = typeinfo_GetNameForIID(ref iid, out name); + if (res != 0) { + throw new + Exception(String.Format("GetNameForIID failed: {0:X8}", + res)); + } + return name; + } + + static public Type TypeForIID(Guid iid) + { + String ifaceName = "Mozilla.XPCOM.Interfaces." + NameForIID(iid) + + ",Mozilla.XPCOM.Interfaces"; + return System.Type.GetType(ifaceName); + } + + static public MethodDescriptor GetMethodData(string ifaceName, + string methodName) + { + IntPtr xptinfo; + UInt16 index; + int res = typeinfo_GetMethodData_byname(ifaceName, methodName, + out index, out xptinfo); + + if (res != 0) { + throw new Exception(String.Format("GetMethodData({0}.{1}) " + + "failed: {2:X8}", + ifaceName, methodName, res)); + } + + // Console.WriteLine("{0} is index {1}", methodName, index); + + return ConvertMethodDescriptor(xptinfo, ifaceName, (int)index); + } + + static public MethodDescriptor GetMethodData(String ifaceName, + int methodIndex) + { + IntPtr xptinfo; + + int res = typeinfo_GetMethodData(ifaceName, methodIndex, out xptinfo); + + if (xptinfo == IntPtr.Zero) { + throw new Exception(String.Format("GetMethodData({0}:{1}) failed:" + + " {2:X8}", + ifaceName, methodIndex, res)); + } + + return ConvertMethodDescriptor(xptinfo, ifaceName, methodIndex); + } +} + +} // namespace Mozilla.XPCOM diff --git a/mozilla/extensions/mono/src/wrapped-clr.cpp b/mozilla/extensions/mono/src/wrapped-clr.cpp new file mode 100644 index 00000000000..5a806dc5e0d --- /dev/null +++ b/mozilla/extensions/mono/src/wrapped-clr.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +#include + +class WrappedCLR : public nsXPTCStubBase +{ +public: + typedef int (*MethodInvoker)(int, nsXPTCMiniVariant *); + + NS_DECL_ISUPPORTS + NS_IMETHOD GetInterfaceInfo(nsIInterfaceInfo **aInfo); + NS_IMETHOD CallMethod(PRUint16 methodIndex, + const nsXPTMethodInfo *info, + nsXPTCMiniVariant *params); + WrappedCLR(MethodInvoker del, const nsIID &id) : + mIID(id), mDelegate(del) { } + virtual ~WrappedCLR() { } +private: + nsIID mIID; + MethodInvoker mDelegate; +}; + +NS_IMPL_ISUPPORTS1(WrappedCLR, nsISupports); + +NS_IMETHODIMP +WrappedCLR::GetInterfaceInfo(nsIInterfaceInfo **info) +{ + extern nsIInterfaceInfoManager *infomgr; + return infomgr->GetInfoForIID(&mIID, info); +} + +NS_IMETHODIMP +WrappedCLR::CallMethod(PRUint16 methodIndex, + const nsXPTMethodInfo *info, + nsXPTCMiniVariant *params) +{ + fprintf(stderr, "Calling %s via %p\n", info->GetName(), mDelegate); + return mDelegate(methodIndex, params); +} + +extern "C" WrappedCLR * +WrapCLRObject(WrappedCLR::MethodInvoker del, const nsIID &id) +{ + char *idstr = id.ToString(); + fprintf(stderr, "Wrapping %p as %s\n", del, idstr); + nsCRT::free(idstr); + return new WrappedCLR(del, id); +} diff --git a/mozilla/extensions/mono/src/wrapped-clr.cs b/mozilla/extensions/mono/src/wrapped-clr.cs new file mode 100644 index 00000000000..39862749b12 --- /dev/null +++ b/mozilla/extensions/mono/src/wrapped-clr.cs @@ -0,0 +1,57 @@ +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using Mozilla.XPCOM; + +using MethodDescriptor = Mozilla.XPCOM.TypeInfo.MethodDescriptor; +using ParamDescriptor = Mozilla.XPCOM.TypeInfo.ParamDescriptor; +using ParamFlags = Mozilla.XPCOM.TypeInfo.ParamFlags; +using TypeDescriptor = Mozilla.XPCOM.TypeInfo.TypeDescriptor; +using TypeFlags = Mozilla.XPCOM.TypeInfo.TypeFlags; +using TypeTag = Mozilla.XPCOM.TypeInfo.TypeTag; + +namespace Mozilla.XPCOM { +internal class CLRWrapper +{ + public static IntPtr Wrap(object o, ref Guid iid) + { + CLRWrapper wrapper = new CLRWrapper(o, ref iid); + return wrapper.MakeXPCOMProxy(); + } + + int InvokeMethod(int index, IntPtr args) + { + Console.WriteLine("invoking method {0} on {1}", index, + wrappedObj); + MethodDescriptor desc = TypeInfo.GetMethodData(wrappedAsIID, index); + Type ifaceType = TypeInfo.TypeForIID(wrappedAsIID); + // Console.WriteLine("ifaceType: {0}", ifaceType); + MethodInfo meth = ifaceType.GetMethod(desc.name); + // Console.WriteLine("meth: {0}", meth); + // This might just throw on you, if it's not a void-taking method + meth.Invoke(wrappedObj, new object[0]); + return 0; + } + + CLRWrapper(object o, ref Guid iid) + { + wrappedObj = o; + wrappedAsIID = iid; + } + + object wrappedObj; + Guid wrappedAsIID; + + delegate int MethodInvoker(int index, IntPtr args); + + [DllImport("xpcom-dotnet.so")] + static extern IntPtr WrapCLRObject(MethodInvoker del, ref Guid iid); + + IntPtr MakeXPCOMProxy() + { + return WrapCLRObject(new MethodInvoker(this.InvokeMethod), + ref wrappedAsIID); + } +} + +} // namespace Mozilla.XPCOM diff --git a/mozilla/extensions/mono/src/xpcom-core.cpp b/mozilla/extensions/mono/src/xpcom-core.cpp new file mode 100644 index 00000000000..eb2486bf257 --- /dev/null +++ b/mozilla/extensions/mono/src/xpcom-core.cpp @@ -0,0 +1,16 @@ +#include +#include +#include +#include +#include + +extern "C" int +StartXPCOM(nsIServiceManager** srvmgr) +{ + nsresult res = NS_InitXPCOM2(srvmgr, 0, 0); + if (NS_SUCCEEDED(res)) { + extern nsIInterfaceInfoManager *infomgr; + infomgr = XPTI_GetInterfaceInfoManager(); + } + return res; +} diff --git a/mozilla/extensions/mono/src/xptinvoke.cs b/mozilla/extensions/mono/src/xptinvoke.cs new file mode 100644 index 00000000000..54cf5f1bd52 --- /dev/null +++ b/mozilla/extensions/mono/src/xptinvoke.cs @@ -0,0 +1,261 @@ +using System; +using System.Runtime.InteropServices; +using Mozilla.XPCOM; +using System.Globalization; + +using MethodDescriptor = Mozilla.XPCOM.TypeInfo.MethodDescriptor; +using ParamDescriptor = Mozilla.XPCOM.TypeInfo.ParamDescriptor; +using ParamFlags = Mozilla.XPCOM.TypeInfo.ParamFlags; +using TypeDescriptor = Mozilla.XPCOM.TypeInfo.TypeDescriptor; +using TypeFlags = Mozilla.XPCOM.TypeInfo.TypeFlags; +using TypeTag = Mozilla.XPCOM.TypeInfo.TypeTag; + +namespace Mozilla.XPCOM { + +[StructLayout(LayoutKind.Explicit)] +struct XPTCMiniVariant +{ + [FieldOffset(0)] + public sbyte i8; + [FieldOffset(0)] + public Int16 i16; + [FieldOffset(0)] + public Int32 i32; + [FieldOffset(0)] + public Int64 i64; + [FieldOffset(0)] + public byte u8; + [FieldOffset(0)] + public UInt16 u16; + [FieldOffset(0)] + public UInt32 u32; + [FieldOffset(0)] + public UInt64 u64; + [FieldOffset(0)] + public float f; + [FieldOffset(0)] + public double d; + [FieldOffset(0)] + public Int32 b; /* PRBool */ + [FieldOffset(0)] + public byte c; + [FieldOffset(0)] + public char wc; + [FieldOffset(0)] + public IntPtr p; + [FieldOffset(0),MarshalAs(UnmanagedType.LPStr)] + public string str; +} + +[StructLayout(LayoutKind.Explicit)] +struct XPTCVariant +{ + [FieldOffset(0)] + public XPTCMiniVariant val; + + [FieldOffset(8)] + public IntPtr ptr; + + [FieldOffset(12)] /* XXX assumes 4-byte IntPtr! */ + public XPTType type; + + [FieldOffset(13)] + public sbyte flags; +} + +public class Invoker +{ + static void MarshalOneArg(ParamDescriptor param, object arg, + IntPtr buffer) + { + string msg; + if (param.flags != TypeInfo.ParamFlags.In) { + msg = String.Format("{0} is {1} (only In " + + "supported)", param.Name(), + param.flags.ToString()); + throw new Exception(msg); + } + + TypeInfo.TypeDescriptor type = param.type; + + if ((type.flags & TypeFlags.Reference) != 0) { + if ((type.flags & TypeFlags.Pointer) == 0) { + throw new Exception("TD is Reference but " + + "not Pointer?! (" + + param.ToString() + ")"); + } + + if (arg == null) { + throw new Exception(param.Name() + + ": null passed as arg for " + + "Reference param"); + } + } + + if (type.IsScalar()) { + + XPTCVariant variant = new XPTCVariant(); + variant.type = type; + variant.flags = 0; + variant.ptr = IntPtr.Zero; + Marshal.StructureToPtr(variant, buffer, false); + + IntPtr p; + switch (type.tag) { + + case TypeTag.Int8: + case TypeTag.Int16: + case TypeTag.Int32: + case TypeTag.UInt8: + case TypeTag.UInt16: + case TypeTag.UInt32: + case TypeTag.Char: + case TypeTag.WChar: + Marshal.WriteInt32(buffer, (Int32)arg); + break; + + case TypeTag.UInt64: + case TypeTag.Int64: + Marshal.WriteInt64(buffer, (Int64)arg); + break; + + case TypeTag.Bool: + bool b = (bool)arg; + Marshal.WriteInt32(buffer, b ? 1 : 0); + break; + + case TypeTag.Float: + float[] f = new float[] { (float)arg }; + Marshal.Copy(f, 0, buffer, 1); + break; + case TypeTag.Double: + double[] d = new double[] { (double)arg }; + Marshal.Copy(d, 0, buffer, 1); + break; + + case TypeTag.String: + Marshal.WriteIntPtr(buffer, + Marshal.StringToCoTaskMemAnsi((string)arg)); + break; + case TypeTag.WString: + Marshal.WriteIntPtr(buffer, + Marshal.StringToCoTaskMemUni((string)arg)); + break; + + default: + msg = String.Format("{0}: type {1} not supported", + param.Name(), type.tag.ToString()); + throw new Exception(msg); + } + + Console.WriteLine("{0} @ {1:X2}", param.Name(), + buffer.ToInt32()); + return; + } + + if (type.tag == TypeTag.Interface) { + Guid iid = param.GetIID(); + Console.WriteLine("{0} is interface {1}", + param.Name(), iid); + Marshal.WriteIntPtr(buffer, CLRWrapper.Wrap(arg, ref iid)); + Console.WriteLine("{0} @ {1:X2}", param.Name(), + buffer.ToInt32()); + return; + } + + msg = String.Format("{0} type {1} not yet supported ", + param.Name(), type.tag.ToString()); + throw new Exception(msg); + } + + [DllImport("xpcom-dotnet.so")] + public static extern IntPtr WrapCLRObject(IntPtr obj, Guid id); + + [DllImport("xpcom-dotnet.so")] + static extern IntPtr typeinfo_WrapUnicode(IntPtr str, + UInt32 length); + + [DllImport("xpcom-dotnet.so")] + static extern void typeinfo_FreeWrappedUnicode(IntPtr str); + + static IntPtr MarshalArgs(MethodDescriptor desc, object[] args) + { + if (args.Length != desc.args.Length) { + string msg = String.Format("{0} needs {1} args, {2} passed", + desc.name, desc.args.Length, + args.Length); + throw new Exception(msg); + } + + int variantsz = 16; /* sizeof(nsXPTCVariant) */ + int size = variantsz * args.Length; + IntPtr argbuf = Marshal.AllocCoTaskMem(size); + + for (int i = 0; i < args.Length; i++) { + ParamDescriptor param = desc.args[i]; + IntPtr current = (IntPtr)(argbuf.ToInt32() + variantsz * i); + object arg = args[i]; + + MarshalOneArg(param, arg, current); + } + + return argbuf; + } + + static void FreeOneMarshalledArg(ParamDescriptor param, IntPtr buffer) + { + } + + static void DemarshalArgs(MethodDescriptor desc, + IntPtr argbuf) + { + int variantsz = Marshal.SizeOf(typeof(XPTCVariant)); + for (int i = 0; i < desc.args.Length; i++) { + ParamDescriptor param = desc.args[i]; + IntPtr current = (IntPtr)(argbuf.ToInt32() + variantsz * i); + FreeOneMarshalledArg(param, current); + } + Marshal.FreeCoTaskMem(argbuf); + } + + public static int XPTC_InvokeByIndexSafe(IntPtr that, Int32 index, + UInt32 argCount, IntPtr args) + { + Console.WriteLine("XPTC_IBI: {0:X2}:{1}({2}:{3:X2})", + that.ToInt32(), index, argCount, args.ToInt32()); + return XPTC_InvokeByIndex(that, index, argCount, args); + } + + [DllImport("libxpcom.so")] + static extern int XPTC_InvokeByIndex(IntPtr that, Int32 methodIndex, + UInt32 argCount, + IntPtr args); + + public static object Invoke(IntPtr that, string iface, + string method, params object[] args) + { + return Invoke(that, TypeInfo.GetMethodData(iface, method), args); + } + + public static object Invoke(IntPtr that, string iface, + int method, params object[] args) + { + return Invoke(that, TypeInfo.GetMethodData(iface, method), args); + } + + public static object Invoke(IntPtr that, MethodDescriptor desc, + params object[] args) + { + IntPtr argbuf = MarshalArgs(desc, args); + int res = XPTC_InvokeByIndex(that, desc.index, + (UInt32)args.Length, argbuf); + DemarshalArgs(desc, argbuf); + if (res != 0) { + throw new Exception(String.Format("XPCOM Error: {0:X2}", + res)); + } + return null; + } +} + +} // namespace Mozilla.XPCOM diff --git a/mozilla/extensions/mono/tools/generate-interfaces.cs b/mozilla/extensions/mono/tools/generate-interfaces.cs new file mode 100644 index 00000000000..08e17babe32 --- /dev/null +++ b/mozilla/extensions/mono/tools/generate-interfaces.cs @@ -0,0 +1,168 @@ +using System; +using Mozilla.XPCOM; +using System.Reflection; +using System.Reflection.Emit; +using System.Collections; + +public class Generate +{ + ModuleBuilder mb; + Hashtable ifaceTable = new Hashtable(); + bool verbose = false; + PropertyBuilder lastProperty = null; + const PropertyAttributes PROPERTY_ATTRS = PropertyAttributes.None; + const MethodAttributes METHOD_ATTRS = MethodAttributes.Public | + MethodAttributes.Abstract | MethodAttributes.Virtual; + + Type FixupInterfaceType(TypeInfo.ParamDescriptor desc) + { + try { + String ifaceName = desc.GetInterfaceName(); + return EmitOneInterface(ifaceName); + } catch (Exception e) { + Console.WriteLine(e); + return typeof(object); + } + } + + void GenerateInterfaceMethod(TypeBuilder tb, + TypeInfo.MethodDescriptor desc) + { + if (desc == null || !desc.IsVisible()) + return; + + MethodAttributes methodAttrs = METHOD_ATTRS; + + String methodName = desc.name; + if (desc.IsGetter()) { + methodName = "get_" + desc.name; + methodAttrs |= MethodAttributes.SpecialName; + } else if (desc.IsSetter()) { + methodName = "set_" + desc.name; + methodAttrs |= MethodAttributes.SpecialName; + } + + // Fix up interface types in parameters + Type ret = desc.resultType; + if (ret == typeof(object)) + ret = FixupInterfaceType(desc.args[desc.args.Length - 1]); + Type[] argTypes = (Type[])desc.argTypes.Clone(); + for (int i = 0; i < argTypes.Length; i++) { + if (argTypes[i] == typeof(object)) + argTypes[i] = FixupInterfaceType(desc.args[i]); + } + MethodBuilder meth = tb.DefineMethod(methodName, methodAttrs, ret, + argTypes); + + if (desc.IsSetter()) { + if (lastProperty != null && lastProperty.Name == desc.name) { + lastProperty.SetSetMethod(meth); + } else { + tb.DefineProperty(desc.name, PROPERTY_ATTRS, ret, new Type[0]) + .SetSetMethod(meth); + } + lastProperty = null; + } else if (desc.IsGetter()) { + lastProperty = tb.DefineProperty(desc.name, PROPERTY_ATTRS, ret, + new Type[0]); + lastProperty.SetGetMethod(meth); + } else { + lastProperty = null; + } + } + + Type EmitOneInterface(String name) + { + if (ifaceTable.ContainsKey(name)) + return (Type)ifaceTable[name]; + Console.WriteLine("Interface: {0}", name); + + ushort inheritedMethodCount; + String parentName = TypeInfo.GetParentInfo(name, + out inheritedMethodCount); + Type parentType; + + if (parentName == null) + parentType = typeof(object); + else + parentType = EmitOneInterface(parentName); + + TypeBuilder ifaceTb = mb.DefineType("Mozilla.XPCOM.Interfaces." + name, + TypeAttributes.Public | + TypeAttributes.Interface, + parentType); + ifaceTable.Add(name, ifaceTb); + + TypeInfo.MethodDescriptor[] descs; + try { + descs = TypeInfo.GetMethodData(name); + } catch (Exception ex) { + Console.WriteLine("skipping interface {0}: {1}", name, + ex.Message); + return null; + } + + if (descs == null) { + Console.WriteLine("No descs!"); + return null; + } + + if (verbose) { + Console.WriteLine("{0} has {1} methods ({2} inherited from {3})", + name, descs.Length, inheritedMethodCount, + parentName); + } + for (int i = inheritedMethodCount; i < descs.Length; i++) { + GenerateInterfaceMethod(ifaceTb, descs[i]); + } + + ifaceTb.CreateType(); + return ifaceTb; + } + + void GenerateInterfaceAssembly(string[] args) + { + String dllName = args[0]; + String onlyInterface = null; + if (args.Length > 1) + onlyInterface = args[1]; + AssemblyName an = new AssemblyName(); + an.Version = new Version(1, 0, 0, 0); + an.Name = "Mozilla.XPCOM.Interfaces"; + + AssemblyBuilder ab = AppDomain.CurrentDomain. + DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave); + mb = ab.DefineDynamicModule(an.Name, dllName); + + if (onlyInterface != null) { + verbose = true; + EmitOneInterface(onlyInterface); + } else { + IntPtr e = TypeInfo.typeinfo_EnumerateInterfacesStart(); + if (e == IntPtr.Zero) + return; + + while (true) { + String name = TypeInfo.typeinfo_EnumerateInterfacesNext(e); + if (name == null) + break; + EmitOneInterface(name); + } + + TypeInfo.typeinfo_EnumerateInterfacesStop(e); + } + + ab.Save(dllName); + + Console.WriteLine("Generated metadata for {0} interfaces in {1}", + ifaceTable.Count, dllName); + } + + public static void Main(String[] args) + { + Components.Init(); + + Generate gen = new Generate(); + gen.GenerateInterfaceAssembly(args); + } +}