/* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.InteropServices; using com.sun.star.uno.helper; namespace com.sun.star.uno.native { internal class NetEnvironment { public InteropContext Context { get; } private readonly Marshaller _marshaller; private readonly WeakIndexTable _indices; private readonly WeakOidTypeTable _locals; private readonly WeakOidTypeTable _proxies; private readonly string _oidSuffix; private readonly Dictionary _oids; public NetEnvironment() { Context = new InteropContext() { createProxy = CreateProxy, lookupObjectId = LookupOid, registerInterface = RegisterInterface, lookupInterface = LookupInterface, revokeInterface = RevokeInterface, dispatchCall = DispatchCall, throwError = ThrowError, }; _marshaller = new Marshaller(this); _indices = new WeakIndexTable(); _locals = new WeakOidTypeTable(); _proxies = new WeakOidTypeTable(); _oidSuffix = $";net[0];{Guid.NewGuid()}"; _oids = new Dictionary(); } public IntPtr CreateProxy(string oid, string interfaceName, IntPtr pBridge, IntPtr pUnoInterface, IntPtr pTypeDesc) { Type interfaceType = TypeHelper.ParseType(interfaceName); object proxy = NativeUnoProxy.CreateProxy(oid, interfaceType, pBridge, pUnoInterface, pTypeDesc, this); int index = _indices.Register(proxy); _oids.Add(index, oid); _proxies.RegisterInterface(proxy, oid, interfaceType); return (IntPtr)index; } public string LookupOid(IntPtr pObject) => _oids[(int)pObject]; public IntPtr RegisterInterface(IntPtr pObject, string pOid, string pInterfaceName) { Type interfaceType = TypeHelper.ParseType(pInterfaceName); object obj = _indices.Lookup((int)pObject); obj = (obj is NativeUnoProxy ? _proxies : _locals) .RegisterInterface(obj, pOid, interfaceType); return (IntPtr)_indices.Register(obj); } public IntPtr RegisterLocal(object obj, Type interfaceType) { int index = _indices.Register(obj); if (!_oids.ContainsKey(index)) { string oid = $"{index}{_oidSuffix}"; _oids.Add(index, oid); } _locals.RegisterInterface(obj, _oids[index], interfaceType); return (IntPtr)index; } public IntPtr LookupInterface(string pOid, string pInterfaceName) { Type interfaceType = TypeHelper.ParseType(pInterfaceName); return LookupInterface(pOid, interfaceType); } public IntPtr LookupInterface(string pOid, Type type) { object obj = _proxies.GetInterface(pOid, type) ?? _locals.GetInterface(pOid, type); return (IntPtr)_indices.Register(obj); } public void RevokeInterface(string pOid, string pInterfaceName) { Type interfaceType = TypeHelper.ParseType(pInterfaceName); if (!_proxies.RevokeInterface(pOid, interfaceType)) _locals.RevokeInterface(pOid, interfaceType); } public void ThrowError(string pWhere, string pMessage) { throw new RuntimeException($"{pMessage} at {pWhere}", null); } public object GetRegisteredObject(IntPtr pIndex) { return _indices.Lookup((int)pIndex); } public unsafe void ReleaseProxy(NativeUnoProxy nup, IntPtr pBridge, IntPtr pUnoInterface, IntPtr pTypeDesc) { _oids.Remove(_indices.Register(nup)); _proxies.RevokeInterface(nup.Oid, nup.Type); InteropMethods.releaseProxy(pBridge, pUnoInterface, pTypeDesc); } public unsafe object InvokeMethod(MethodInfo targetMethod, object[] args, IntPtr bridgeHandle, IntPtr interfaceHandle, IntPtr typeHandle) { ParameterInfo[] parameters = targetMethod.GetParameters(); InteropValue* values = stackalloc InteropValue[parameters.Length + 2]; InteropValue* retVal = &values[parameters.Length]; InteropValue* excVal = &values[parameters.Length + 1]; for (int i = 0; i < parameters.Length; i++) { ParameterInfo param = parameters[i]; Type paramType = TypeHelper.RemoveReference(param.ParameterType); bool isInOrInOut = !(param.ParameterType.IsByRef && param.IsOut); if (isInOrInOut) _marshaller.MarshalObject(paramType, args[i], &values[i], false); } bool error = InteropMethods.dispatchCall( bridgeHandle, interfaceHandle, typeHandle, targetMethod.Name, values, retVal, excVal) == 0; for (int i = 0; i < parameters.Length; i++) { ParameterInfo param = parameters[i]; Type paramType = TypeHelper.RemoveReference(param.ParameterType); bool isInOutOrOut = param.ParameterType.IsByRef; if (isInOutOrOut) _marshaller.UnmarshalObject(paramType, ref args[i], &values[i], true); } if (error) { object exception = null; _marshaller.UnmarshalObject(typeof(Any), ref exception, excVal, true); throw (Exception)((Any)exception).Value; } if (targetMethod.ReturnType != typeof(void)) { object result = null; _marshaller.UnmarshalObject(targetMethod.ReturnType, ref result, retVal, true); return result; } return null; } public unsafe byte DispatchCall(IntPtr pNetInterface, string pMethodName, IntPtr pArgs, IntPtr pRet, IntPtr pExc) { try { int methodNameStart = pMethodName.IndexOf(':') + 2; int methodNameEnd = pMethodName.IndexOf(':', methodNameStart); if (methodNameEnd > methodNameStart) pMethodName = pMethodName.Substring(methodNameStart, methodNameEnd - methodNameStart); else pMethodName = pMethodName.Substring(methodNameStart); object target = _indices.Lookup((int)pNetInterface); if (target == null) throw new RuntimeException($"{pMethodName} was called on a null or unregistered interface", null); Type targetType = target.GetType(); MethodInfo targetMethod = targetType.GetMethod(pMethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy); if (targetMethod == null) throw new RuntimeException($"could not find method {pMethodName} on interface {target.GetType()}", null); ParameterInfo[] parameters = targetMethod.GetParameters(); object[] args = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { ParameterInfo param = parameters[i]; Type paramType = TypeHelper.RemoveReference(param.ParameterType); bool isInOrInOut = !(param.ParameterType.IsByRef && param.IsOut); if (isInOrInOut) _marshaller.UnmarshalObject(paramType, ref args[i], &((InteropValue*)(void*)pArgs)[i], true); } object ret = targetMethod.Invoke(target, args); for (int i = 0; i < parameters.Length; i++) { ParameterInfo param = parameters[i]; Type paramType = TypeHelper.RemoveReference(param.ParameterType); bool isInOutOrOut = param.ParameterType.IsByRef; if (isInOutOrOut) _marshaller.MarshalObject(paramType, args[i], &((InteropValue*)(void*)pArgs)[i], false); } if (targetMethod.ReturnType != typeof(void)) _marshaller.MarshalObject(targetMethod.ReturnType, ret, (InteropValue*)(void*)pRet, false); return 1; } catch (Exception exception) { _marshaller.MarshalObject(typeof(Any), new Any(exception.InnerException), (InteropValue*)(void*)pExc, false); return 0; } } } }