Repository.mk | 5 bridges/Library_net_uno.mk | 33 bridges/Module_bridges.mk | 1 bridges/source/net_uno/net_base.cxx | 234 ++ bridges/source/net_uno/net_base.hxx | 58 bridges/source/net_uno/net_bridge.cxx | 292 +++ bridges/source/net_uno/net_bridge.hxx | 59 bridges/source/net_uno/net_data.cxx | 855 ++++++++++ bridges/source/net_uno/net_func.cxx | 251 ++ bridges/source/net_uno/net_proxy.cxx | 175 ++ bridges/source/net_uno/net_proxy.hxx | 36 codemaker/source/netmaker/netproduce.cxx | 104 - include/bridges/net_uno/net_context.hxx | 38 include/bridges/net_uno/net_types.hxx | 79 include/sal/log-areas.dox | 1 include/uno/lbnames.h | 2 net_ure/DotnetLibrary_net_bridge.mk | 22 net_ure/qa/basetypes/AnyTests.cs | 1 net_ure/source/basetypes/Any.cs | 48 net_ure/source/basetypes/IQueryInterface.cs | 22 net_ure/source/bootstrap/bootstrap.cxx | 84 net_ure/source/bridge/NativeBootstrap.cs | 32 net_ure/source/bridge/helper/DisposeGuard.cs | 25 net_ure/source/bridge/helper/StructHelper.cs | 56 net_ure/source/bridge/helper/TypeHelper.cs | 125 + net_ure/source/bridge/helper/WeakBase.cs | 77 net_ure/source/bridge/helper/WeakComponentBase.cs | 40 net_ure/source/bridge/native/InteropMethods.cs | 50 net_ure/source/bridge/native/InteropTypes.cs | 86 + net_ure/source/bridge/native/Marshaller.cs | 608 +++++++ net_ure/source/bridge/native/NativeBootstrap.cs | 49 net_ure/source/bridge/native/NativeUnoProxy.cs | 58 net_ure/source/bridge/native/NetEnvironment.cs | 227 ++ net_ure/source/bridge/native/WeakIndexTable.cs | 103 + net_ure/source/bridge/native/WeakOidTypeTable.cs | 179 ++ odk/Package_examples.mk | 2 odk/examples/DevelopersGuide/FirstSteps/FirstUnoContact/csharp/FirstUnoContact.cs | 29 odk/examples/DevelopersGuide/FirstSteps/FirstUnoContact/csharp/Makefile | 75 38 files changed, 4154 insertions(+), 67 deletions(-)
New commits: commit c3c7b48fa9d4fd5b8f28033ca7453151df9d5f7f Author: RMZeroFour <[email protected]> AuthorDate: Tue Sep 3 01:59:15 2024 +0530 Commit: Hossein <[email protected]> CommitDate: Wed Sep 18 08:10:36 2024 +0200 .NET Bindings: Native bridge for .NET This patch includes all marshalling and proxy handling code on the .NET side as well as the native side needed for a fully functional UNO bridge. It also includes some changes and corrections to net_basetypes and netmaker needed for the bridge to work properly. It also includes the FirstUnoContact example in C# as demonstration. Change-Id: I406932938a4415d24408fb41ddfa7d8eeb5d1f94 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170916 Tested-by: Jenkins Reviewed-by: Hossein <[email protected]> diff --git a/Repository.mk b/Repository.mk index af89d1d50dce..b4f3b36796bd 100644 --- a/Repository.mk +++ b/Repository.mk @@ -603,7 +603,10 @@ $(eval $(call gb_Helper_register_libraries_for_install,PLAINLIBS_URE,ure, \ $(if $(filter MSC,$(COM)),$(if $(filter-out AARCH64_TRUE,$(CPUNAME)_$(CROSS_COMPILING)),cli_uno)) \ ) \ i18nlangtag \ - $(if $(ENABLE_DOTNET),net_bootstrap) \ + $(if $(ENABLE_DOTNET), \ + net_bootstrap \ + net_uno \ + ) \ $(if $(ENABLE_JAVA), \ java_uno \ jpipe \ diff --git a/bridges/Library_net_uno.mk b/bridges/Library_net_uno.mk new file mode 100644 index 000000000000..5579c3178492 --- /dev/null +++ b/bridges/Library_net_uno.mk @@ -0,0 +1,33 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Library_Library,net_uno)) + +$(eval $(call gb_Library_use_udk_api,net_uno)) + +$(eval $(call gb_Library_set_include,net_uno,\ + -I$(SRCDIR)/bridges/source/net_uno \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_Library_use_libraries,net_uno,\ + cppu \ + sal \ + salhelper \ +)) + +$(eval $(call gb_Library_add_exception_objects,net_uno,\ + bridges/source/net_uno/net_base \ + bridges/source/net_uno/net_bridge \ + bridges/source/net_uno/net_data \ + bridges/source/net_uno/net_func \ + bridges/source/net_uno/net_proxy \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/bridges/Module_bridges.mk b/bridges/Module_bridges.mk index a943e6537653..a8a07c0e39c6 100644 --- a/bridges/Module_bridges.mk +++ b/bridges/Module_bridges.mk @@ -11,6 +11,7 @@ $(eval $(call gb_Module_Module,bridges)) $(eval $(call gb_Module_add_targets,bridges,\ Library_cpp_uno \ + $(if $(ENABLE_DOTNET),Library_net_uno) \ $(if $(ENABLE_JAVA),\ Jar_java_uno \ Library_java_uno \ diff --git a/bridges/source/net_uno/net_base.cxx b/bridges/source/net_uno/net_base.cxx new file mode 100644 index 000000000000..1b3b5b2d7223 --- /dev/null +++ b/bridges/source/net_uno/net_base.cxx @@ -0,0 +1,234 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#include "net_base.hxx" + +#include <string_view> +#include <unordered_map> + +#include <o3tl/string_view.hxx> +#include <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> + +namespace net_uno +{ +namespace +{ +const std::unordered_map<std::u16string_view, std::u16string_view> s_typeNames{ + { u"System.Void", u"void" }, + { u"System.Boolean", u"boolean" }, + { u"System.Char", u"char" }, + { u"System.SByte", u"byte" }, + { u"System.Int16", u"short" }, + { u"System.UInt16", u"unsigned short" }, + { u"System.Int32", u"long" }, + { u"System.UInt32", u"unsigned long" }, + { u"System.Int64", u"hyper" }, + { u"System.UInt64", u"unsigned hyper" }, + { u"System.Single", u"float" }, + { u"System.Double", u"double" }, + { u"System.String", u"string" }, + { u"System.Type", u"type" }, + { u"com.sun.star.uno.Any", u"any" }, + { u"com.sun.star.uno.UnoException", u"com.sun.star.uno.Exception" }, + { u"com.sun.star.uno.IQueryInterface", u"com.sun.star.uno.XInterface" }, +}; + +const std::unordered_map<OUString, typelib_TypeClass> s_typeClasses{ + { u"System.Void"_ustr, typelib_TypeClass_VOID }, + { u"System.Boolean"_ustr, typelib_TypeClass_BOOLEAN }, + { u"System.Char"_ustr, typelib_TypeClass_CHAR }, + { u"System.SByte"_ustr, typelib_TypeClass_BYTE }, + { u"System.Int16"_ustr, typelib_TypeClass_SHORT }, + { u"System.UInt16"_ustr, typelib_TypeClass_UNSIGNED_SHORT }, + { u"System.Int32"_ustr, typelib_TypeClass_LONG }, + { u"System.UInt32"_ustr, typelib_TypeClass_UNSIGNED_LONG }, + { u"System.Int64"_ustr, typelib_TypeClass_HYPER }, + { u"System.UInt64"_ustr, typelib_TypeClass_UNSIGNED_HYPER }, + { u"System.Single"_ustr, typelib_TypeClass_FLOAT }, + { u"System.Double"_ustr, typelib_TypeClass_DOUBLE }, + { u"System.String"_ustr, typelib_TypeClass_STRING }, + { u"System.Type"_ustr, typelib_TypeClass_TYPE }, + { u"com.sun.star.uno.Any"_ustr, typelib_TypeClass_ANY }, +}; + +void map_uno_type_to_net(typelib_TypeDescriptionReference* pTDRef, OUStringBuffer& buffer) +{ + switch (pTDRef->eTypeClass) + { + case typelib_TypeClass_VOID: + buffer.append(u"System.Void"); + break; + case typelib_TypeClass_CHAR: + buffer.append(u"System.Char"); + break; + case typelib_TypeClass_BOOLEAN: + buffer.append(u"System.Boolean"); + break; + case typelib_TypeClass_BYTE: + buffer.append(u"System.SByte"); + break; + case typelib_TypeClass_SHORT: + buffer.append(u"System.Int16"); + break; + case typelib_TypeClass_UNSIGNED_SHORT: + buffer.append(u"System.UInt16"); + break; + case typelib_TypeClass_LONG: + buffer.append(u"System.Int32"); + break; + case typelib_TypeClass_UNSIGNED_LONG: + buffer.append(u"System.UInt32"); + break; + case typelib_TypeClass_HYPER: + buffer.append(u"System.Int64"); + break; + case typelib_TypeClass_UNSIGNED_HYPER: + buffer.append(u"System.UInt64"); + break; + case typelib_TypeClass_FLOAT: + buffer.append(u"System.Single"); + break; + case typelib_TypeClass_DOUBLE: + buffer.append(u"System.Double"); + break; + case typelib_TypeClass_STRING: + buffer.append(u"System.String"); + break; + case typelib_TypeClass_TYPE: + buffer.append(u"System.Type"); + break; + case typelib_TypeClass_ANY: + buffer.append(u"com.sun.star.uno.Any"); + break; + + case typelib_TypeClass_ENUM: + case typelib_TypeClass_EXCEPTION: + // These have the same name on both sides + buffer.append(OUString::unacquired(&pTDRef->pTypeName)); + break; + + case typelib_TypeClass_STRUCT: + // These have the same name on both sides + // TODO: What about polymorphic structs? Merge this with above cases if fine + buffer.append(OUString::unacquired(&pTDRef->pTypeName)); + break; + + case typelib_TypeClass_INTERFACE: + { + // These have the same name on both sides + if (u"com.sun.star.uno.XInterface"_ustr.equals(pTDRef->pTypeName)) + // Except XInterface, which does not exist on the .NET side + buffer.append(u"com.sun.star.uno.IQueryInterface"); + else + buffer.append(OUString::unacquired(&pTDRef->pTypeName)); + break; + } + + case typelib_TypeClass_SEQUENCE: + { + TypeDescHolder seqType(pTDRef); + typelib_TypeDescriptionReference* pElementTDRef + = reinterpret_cast<typelib_IndirectTypeDescription*>(seqType.get())->pType; + map_uno_type_to_net(pElementTDRef, buffer); + buffer.append(u"[]"); + break; + } + + default: + { + throw BridgeRuntimeError(SAL_WHERE, "could not map given type info " + + OUString::unacquired(&pTDRef->pTypeName)); + } + } +} + +void map_net_type_to_uno(std::u16string_view sTypeName, OUStringBuffer& buffer) +{ + size_t bracketsStart = sTypeName.find_last_not_of(u"[]"); + bool isSequence = bracketsStart != std::u16string_view::npos; + std::u16string_view sBrackets = isSequence ? sTypeName.substr(bracketsStart + 1) : u""; + std::u16string_view sFullName = isSequence ? sTypeName.substr(0, bracketsStart + 1) : sTypeName; + + size_t genericsStart = sFullName.find_first_of(u'<'); + size_t genericsEnd = sFullName.find_last_of(u'>'); + bool hasGenerics = genericsStart != std::u16string_view::npos; + std::u16string_view sGenericParams + = hasGenerics ? sFullName.substr(genericsStart + 1, genericsEnd - genericsStart - 1) : u""; + std::u16string_view sBaseName = hasGenerics ? sFullName.substr(0, genericsStart) : sFullName; + + // Sequence brackets go at the beginning of UNO name + for (size_t i = 0; i < sBrackets.size() / 2; i++) + buffer.append(u"[]"); + + // Builtin types + auto it = s_typeNames.find(sBaseName); + if (it != s_typeNames.end()) + { + buffer.append(it->second); + } + else + { + buffer.append(sBaseName); + if (hasGenerics) + { + buffer.append(u'<'); + for (size_t i = 0; i != std::string_view::npos;) + { + std::u16string_view genericParam(o3tl::getToken(sGenericParams, u',', i)); + map_net_type_to_uno(genericParam, buffer); + if (i != std::string_view::npos) + buffer.append(u','); + } + buffer.append(u'>'); + } + } +} +} + +OUString map_uno_type_to_net(typelib_TypeDescriptionReference* pTDRef) +{ + OUStringBuffer buffer; + map_uno_type_to_net(pTDRef, buffer); + return buffer.makeStringAndClear(); +} + +typelib_TypeDescriptionReference* map_net_type_to_uno(const OUString& sTypeName) +{ + typelib_TypeDescriptionReference* retVal = nullptr; + + // Simple types + auto it = s_typeClasses.find(sTypeName); + if (it != s_typeClasses.end()) + { + retVal = *typelib_static_type_getByTypeClass(it->second); + typelib_typedescriptionreference_acquire(retVal); + return retVal; + } + + // Complex types (structs, interfaces, sequences) + OUStringBuffer buffer; + map_net_type_to_uno(sTypeName, buffer); + OUString convertedName = buffer.makeStringAndClear(); + + typelib_TypeDescription* pTD = nullptr; + typelib_typedescription_getByName(&pTD, convertedName.pData); + if (pTD) + { + retVal = pTD->pWeakRef; + typelib_typedescriptionreference_acquire(retVal); + typelib_typedescription_release(pTD); + return retVal; + } + + throw BridgeRuntimeError(SAL_WHERE, "could not map given type name " + sTypeName); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/bridges/source/net_uno/net_base.hxx b/bridges/source/net_uno/net_base.hxx new file mode 100644 index 000000000000..a9d8a1c37767 --- /dev/null +++ b/bridges/source/net_uno/net_base.hxx @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <typelib/typedescription.hxx> +#include <sal/log.hxx> + +namespace net_uno +{ +OUString map_uno_type_to_net(typelib_TypeDescriptionReference* pTDRef); +typelib_TypeDescriptionReference* map_net_type_to_uno(const OUString& sTypeName); + +struct BridgeRuntimeError +{ + OUString m_location; + OUString m_message; + + explicit BridgeRuntimeError(const char* location, const OUString& message) + : m_location(OUString::createFromAscii(location)) + , m_message(message) + { + } +}; + +class TypeDescHolder +{ +public: + TypeDescHolder(const TypeDescHolder&) = delete; + TypeDescHolder& operator=(const TypeDescHolder&) = delete; + + explicit TypeDescHolder(typelib_TypeDescriptionReference* td_ref) + : m_td(nullptr) + { + TYPELIB_DANGER_GET(&m_td, td_ref); + if (!m_td) + { + throw BridgeRuntimeError(SAL_WHERE, "could not get type description for " + + OUString::unacquired(&td_ref->pTypeName)); + } + } + ~TypeDescHolder() { TYPELIB_DANGER_RELEASE(m_td); } + + typelib_TypeDescription* get() const { return m_td; } + +private: + typelib_TypeDescription* m_td; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/bridges/source/net_uno/net_bridge.cxx b/bridges/source/net_uno/net_bridge.cxx new file mode 100644 index 000000000000..9a1632fa9744 --- /dev/null +++ b/bridges/source/net_uno/net_bridge.cxx @@ -0,0 +1,292 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#include "net_bridge.hxx" + +#include <bridges/net_uno/net_context.hxx> +#include <sal/log.hxx> + +namespace net_uno +{ +namespace +{ +void SAL_CALL Mapping_acquire(uno_Mapping* mapping) SAL_THROW_EXTERN_C() +{ + Mapping* that = static_cast<Mapping*>(mapping); + that->m_bridge->acquire(); +} + +void SAL_CALL Mapping_release(uno_Mapping* mapping) SAL_THROW_EXTERN_C() +{ + Mapping* that = static_cast<Mapping*>(mapping); + that->m_bridge->release(); +} + +void SAL_CALL Mapping_free(uno_Mapping* mapping) SAL_THROW_EXTERN_C() +{ + Mapping* that = static_cast<Mapping*>(mapping); + delete that->m_bridge; +} + +void SAL_CALL Mapping_net2uno(uno_Mapping* mapping, void** ppOut, void* pIn, + typelib_InterfaceTypeDescription* pTD) SAL_THROW_EXTERN_C() +{ + assert(ppOut && pTD && "### null ptr!"); + + Mapping* that = static_cast<Mapping*>(mapping); + Bridge* bridge = that->m_bridge; + + if (pIn) + { + Value interfaceValue; + interfaceValue.interfaceData = pIn; + bridge->map_net_value_to_uno(*ppOut, &interfaceValue, pTD->aBase.pWeakRef, true, true); + } +} + +void SAL_CALL Mapping_uno2net(uno_Mapping* mapping, void** ppOut, void* pIn, + typelib_InterfaceTypeDescription* pTD) SAL_THROW_EXTERN_C() +{ + assert(ppOut && pTD && "### null ptr!"); + + Mapping* that = static_cast<Mapping*>(mapping); + Bridge* bridge = that->m_bridge; + + if (pIn) + { + Value interfaceValue; + bridge->map_uno_to_net_value(&pIn, &interfaceValue, pTD->aBase.pWeakRef, false); + *ppOut = interfaceValue.interfaceData; + } +} +} + +Bridge::Bridge(uno_Environment* uno_net_env, uno_ExtEnvironment* uno_env, bool registered_net2uno) + : m_ref(1) + , m_uno_env(uno_env) + , m_net_env(uno_net_env) + , m_registered_net2uno(registered_net2uno) +{ + assert(m_net_env && m_uno_env); + + (*m_uno_env->aBase.acquire)(&m_uno_env->aBase); + (*m_net_env->acquire)(m_net_env); + + // net2uno + m_net2uno.acquire = Mapping_acquire; + m_net2uno.release = Mapping_release; + m_net2uno.mapInterface = Mapping_net2uno; + m_net2uno.m_bridge = this; + + // uno2net + m_uno2net.acquire = Mapping_acquire; + m_uno2net.release = Mapping_release; + m_uno2net.mapInterface = Mapping_uno2net; + m_uno2net.m_bridge = this; +} + +Bridge::~Bridge() +{ + (*m_net_env->release)(m_net_env); + (*m_uno_env->aBase.release)(&m_uno_env->aBase); +} + +void Bridge::acquire() +{ + if (osl_atomic_increment(&m_ref) == 1) + { + if (m_registered_net2uno) + { + uno_Mapping* mapping = &m_net2uno; + uno_registerMapping(&mapping, Mapping_free, m_net_env, &m_uno_env->aBase, nullptr); + } + else + { + uno_Mapping* mapping = &m_uno2net; + uno_registerMapping(&mapping, Mapping_free, &m_uno_env->aBase, m_net_env, nullptr); + } + } +} + +void Bridge::release() +{ + if (osl_atomic_decrement(&m_ref) == 0) + { + uno_revokeMapping(m_registered_net2uno ? &m_net2uno : &m_uno2net); + } +} + +extern "C" { +static void net_env_disposing(uno_Environment* env) +{ + // TODO: more rigorous lifetime control + // TODO: invoke some dispose routine on .NET side + delete static_cast<Context*>(env->pContext); +} + +SAL_DLLPUBLIC_EXPORT void uno_initEnvironment(uno_Environment* net_env) SAL_THROW_EXTERN_C() +{ + // The code creating the uno_Environment needs to initialize + // pContext with a Context object, complete with all callbacks. + assert(net_env->pContext); + + net_env->pExtEnv = nullptr; + net_env->environmentDisposing = net_env_disposing; +} + +SAL_DLLPUBLIC_EXPORT void uno_ext_getMapping(uno_Mapping** ppMapping, uno_Environment* pFrom, + uno_Environment* pTo) SAL_THROW_EXTERN_C() +{ + assert(ppMapping && pFrom && pTo); + + if (*ppMapping) + { + (*(*ppMapping)->release)(*ppMapping); + *ppMapping = nullptr; + } + + const OUString& from_env_typename = OUString::unacquired(&pFrom->pTypeName); + const OUString& to_env_typename = OUString::unacquired(&pTo->pTypeName); + + uno_Mapping* mapping = nullptr; + if (from_env_typename == UNO_LB_NET && to_env_typename == UNO_LB_UNO) + { + Bridge* bridge = new Bridge(pFrom, pTo->pExtEnv, true); + mapping = &bridge->m_net2uno; + uno_registerMapping(&mapping, Mapping_free, pFrom, &pTo->pExtEnv->aBase, nullptr); + } + else if (from_env_typename == UNO_LB_UNO && to_env_typename == UNO_LB_NET) + { + Bridge* bridge = new Bridge(pTo, pFrom->pExtEnv, false); + mapping = &bridge->m_uno2net; + uno_registerMapping(&mapping, Mapping_free, &pFrom->pExtEnv->aBase, pTo, nullptr); + } + *ppMapping = mapping; +} + +SAL_DLLPUBLIC_EXPORT IntPtr allocateMemory(int nBytes) { return std::malloc(nBytes); } + +SAL_DLLPUBLIC_EXPORT void freeMemory(IntPtr pMemory) { std::free(pMemory); } + +SAL_DLLPUBLIC_EXPORT void releaseProxy(IntPtr pBridge, IntPtr pUnoInterface, IntPtr pTypeDesc) +{ + Bridge* bridge = static_cast<Bridge*>(pBridge); + uno_Interface* pUnoI = static_cast<uno_Interface*>(pUnoInterface); + typelib_TypeDescription* pTD = static_cast<typelib_TypeDescription*>(pTypeDesc); + + // Revoke from UNO; already revoked from .NET + (*bridge->m_uno_env->revokeInterface)(bridge->m_uno_env, pUnoI); + + (*pUnoI->release)(pUnoI); + typelib_typedescription_release(pTD); + bridge->release(); +} + +SAL_DLLPUBLIC_EXPORT sal_Bool dispatchCall(IntPtr pBridge, IntPtr pUnoInterface, IntPtr pTypeDesc, + String sFunctionName, Value* pArgs, Value* pRet, + Value* pExc) +{ + Bridge* bridge = static_cast<Bridge*>(pBridge); + Context* context = static_cast<Context*>(bridge->m_net_env->pContext); + + OUString sMethodName(sFunctionName); + + try + { + uno_Interface* pUnoI = static_cast<uno_Interface*>(pUnoInterface); + typelib_InterfaceTypeDescription* pTD + = static_cast<typelib_InterfaceTypeDescription*>(pTypeDesc); + + for (sal_Int32 i = 0; i < pTD->nAllMembers; ++i) + { + // Try to avoid getting typedescription as long as possible because + // of Mutex.acquire() in typelib_typedescriptionreference_getDescription() + typelib_TypeDescriptionReference* memberType = pTD->ppAllMembers[i]; + + // Check method_name against fully qualified typeName of memberType + // typeName is of the form <name> "::" <method_name> *(":@" <idx> "," <idx> ":" <name>) + const OUString& typeName = OUString::unacquired(&memberType->pTypeName); + sal_Int32 offset = typeName.indexOf(':') + 2; + sal_Int32 remainder = typeName.getLength() - offset; + assert(offset >= 2 && offset < typeName.getLength() && typeName[offset - 1] == ':'); + + switch (memberType->eTypeClass) + { + case typelib_TypeClass_INTERFACE_METHOD: + if ((sMethodName.getLength() == remainder + || (sMethodName.getLength() < remainder + && typeName[offset + sMethodName.getLength()] == ':')) + && typeName.match(sMethodName, offset)) + { + TypeDescHolder memberTD(memberType); + typelib_InterfaceMethodTypeDescription* pMethodTD + = reinterpret_cast<typelib_InterfaceMethodTypeDescription*>( + memberTD.get()); + return bridge->call_uno_func(pUnoI, memberTD.get(), + pMethodTD->pReturnTypeRef, pMethodTD->nParams, + pMethodTD->pParams, pArgs, pRet, pExc); + } + break; + + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + if (sMethodName.getLength() > 4 + && (sMethodName.getLength() - 4 == remainder + || (sMethodName.getLength() - 4 < remainder + && typeName[offset + (sMethodName.getLength() - 4)] == ':')) + && sMethodName[1] == 'e' && sMethodName[2] == 't' + && rtl_ustr_compare_WithLength( + typeName.getStr() + offset, sMethodName.getLength() - 4, + sMethodName.getStr() + 4, sMethodName.getLength() - 4) + == 0) + { + TypeDescHolder memberTD(memberType); + typelib_InterfaceAttributeTypeDescription* pAttribTD + = reinterpret_cast<typelib_InterfaceAttributeTypeDescription*>( + memberTD.get()); + if (sMethodName[0] == 'g') + { + return bridge->call_uno_func(pUnoI, memberTD.get(), + pAttribTD->pAttributeTypeRef, 0, nullptr, + pArgs, pRet, pExc); + } + else if (sMethodName[0] == 's' && !pAttribTD->bReadOnly) + { + typelib_MethodParameter param; + param.pTypeRef = pAttribTD->pAttributeTypeRef; + param.bIn = true; + param.bOut = false; + + return bridge->call_uno_func(pUnoI, memberTD.get(), + pAttribTD->pAttributeTypeRef, 1, ¶m, + pArgs, pRet, pExc); + } + } + break; + + default: + break; + } + } + + // No matching method info found + throw BridgeRuntimeError(SAL_WHERE, "could not find function " + sMethodName + + " to call from type " + + OUString::unacquired(&pTD->aBase.pTypeName)); + } + catch (const BridgeRuntimeError& err) + { + SAL_WARN("bridges", ".NET bridge error: " << err.m_message); + context->throwError(err.m_location.getStr(), err.m_message.getStr()); + return false; + } +} +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/bridges/source/net_uno/net_bridge.hxx b/bridges/source/net_uno/net_bridge.hxx new file mode 100644 index 000000000000..19c07243743a --- /dev/null +++ b/bridges/source/net_uno/net_bridge.hxx @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#pragma once + +#include "net_base.hxx" + +#include <bridges/net_uno/net_types.hxx> +#include <uno/dispatcher.hxx> +#include <uno/environment.hxx> +#include <uno/mapping.hxx> + +namespace net_uno +{ +struct Bridge; + +struct Mapping : public uno_Mapping +{ + Bridge* m_bridge; +}; + +struct Bridge +{ + mutable oslInterlockedCount m_ref; + uno_ExtEnvironment* m_uno_env; + uno_Environment* m_net_env; + + Mapping m_net2uno; + Mapping m_uno2net; + bool m_registered_net2uno; + + Bridge(uno_Environment* net_env, uno_ExtEnvironment* uno_env, bool registered_net2uno); + ~Bridge(); + + void acquire(); + void release(); + + void map_uno_to_net_value(void* pUnoData, Value* pValue, + typelib_TypeDescriptionReference* pTDRef, bool destructValue); + void map_net_value_to_uno(void* pUnoData, Value* pValue, + typelib_TypeDescriptionReference* pTDRef, bool destructObject, + bool assignObject); + + bool call_uno_func(uno_Interface* pUnoI, const typelib_TypeDescription* pMethodTD, + typelib_TypeDescriptionReference* pReturnTDRef, int nParams, + typelib_MethodParameter* pParams, Value* pArgs, Value* pRet, Value* pExc); + void call_net_func(IntPtr pNetI, const typelib_TypeDescription* pMethodTD, + typelib_TypeDescriptionReference* pReturnTDRef, int nParams, + typelib_MethodParameter* pParams, void** pArgs, void* pRet, uno_Any** pExc); +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/bridges/source/net_uno/net_data.cxx b/bridges/source/net_uno/net_data.cxx new file mode 100644 index 000000000000..b41e9cb960d2 --- /dev/null +++ b/bridges/source/net_uno/net_data.cxx @@ -0,0 +1,855 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#include "net_bridge.hxx" +#include "net_proxy.hxx" + +#include <cstring> +#include <unordered_map> +#include <vector> + +#include <bridges/net_uno/net_context.hxx> + +namespace net_uno +{ +namespace +{ +size_t net_sizeof(typelib_TypeClass eTypeClass) +{ + static const std::unordered_map<typelib_TypeClass, size_t> s_sizes{ + { typelib_TypeClass_BOOLEAN, sizeof(sal_Bool) }, + { typelib_TypeClass_BYTE, sizeof(sal_Int8) }, + { typelib_TypeClass_CHAR, sizeof(sal_Unicode) }, + { typelib_TypeClass_SHORT, sizeof(sal_Int16) }, + { typelib_TypeClass_UNSIGNED_SHORT, sizeof(sal_uInt16) }, + { typelib_TypeClass_LONG, sizeof(sal_Int32) }, + { typelib_TypeClass_UNSIGNED_LONG, sizeof(sal_uInt32) }, + { typelib_TypeClass_HYPER, sizeof(sal_Int64) }, + { typelib_TypeClass_UNSIGNED_HYPER, sizeof(sal_uInt64) }, + { typelib_TypeClass_FLOAT, sizeof(float) }, + { typelib_TypeClass_DOUBLE, sizeof(double) }, + { typelib_TypeClass_ENUM, sizeof(sal_Int32) }, + { typelib_TypeClass_STRING, sizeof(IntPtr) }, + { typelib_TypeClass_TYPE, sizeof(IntPtr) }, + { typelib_TypeClass_ANY, sizeof(IntPtr) + sizeof(IntPtr) }, + { typelib_TypeClass_SEQUENCE, sizeof(IntPtr) + sizeof(sal_Int32) }, + { typelib_TypeClass_INTERFACE, sizeof(IntPtr) }, + { typelib_TypeClass_EXCEPTION, sizeof(IntPtr) }, + { typelib_TypeClass_STRUCT, sizeof(IntPtr) }, + }; + return s_sizes.at(eTypeClass); +} + +IntPtr alloc_net_string(const OUString& str) +{ + const sal_Unicode* src = str.getStr(); + sal_Int32 len = str.getLength(); + + void* dst = std::malloc((len + 1) * sizeof(sal_Unicode)); + std::memcpy(dst, src, len * sizeof(sal_Unicode)); + static_cast<sal_Unicode*>(dst)[len] = u' + + return dst; +} + +uno_Sequence* alloc_uno_sequence(sal_Int32 nElements, sal_Int32 nElementSize, void* data) +{ + void* mem = std::malloc(SAL_SEQUENCE_HEADER_SIZE + nElements * nElementSize); + + uno_Sequence* seq = static_cast<uno_Sequence*>(mem); + seq->nRefCount = 1; + seq->nElements = nElements; + + if (data) + std::memcpy(seq->elements, data, nElements * nElementSize); + + return seq; +} + +void marshal_data(void* pUnoData, void* pNetData, typelib_TypeDescriptionReference* pTDRef, + bool bDestructValue, Bridge& bridge) +{ + switch (pTDRef->eTypeClass) + { + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + case typelib_TypeClass_CHAR: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + case typelib_TypeClass_FLOAT: + case typelib_TypeClass_DOUBLE: + case typelib_TypeClass_ENUM: + std::memcpy(pNetData, pUnoData, net_sizeof(pTDRef->eTypeClass)); + break; + case typelib_TypeClass_STRING: + { + IntPtr* ppNetStr = static_cast<IntPtr*>(pNetData); + rtl_uString* pUnoStr = *static_cast<rtl_uString**>(pUnoData); + + if (bDestructValue && pNetData) + std::free(pNetData); + + *ppNetStr = alloc_net_string(OUString::unacquired(&pUnoStr)); + break; + } + case typelib_TypeClass_TYPE: + { + IntPtr* ppNetType = static_cast<IntPtr*>(pNetData); + typelib_TypeDescriptionReference* pUnoType + = *static_cast<typelib_TypeDescriptionReference**>(pUnoData); + + if (bDestructValue && pNetData) + std::free(pNetData); + + *ppNetType = alloc_net_string(map_uno_type_to_net(pUnoType)); + break; + } + case typelib_TypeClass_ANY: + { + Value::Any* ppNetAny = static_cast<Value::Any*>(pNetData); + uno_Any* pUnoAny = static_cast<uno_Any*>(pUnoData); + + if (bDestructValue && ppNetAny->type) + std::free(ppNetAny->type); + if (bDestructValue && ppNetAny->data) + std::free(ppNetAny->data); + + ppNetAny->type = alloc_net_string(map_uno_type_to_net(pUnoAny->pType)); + + switch (pUnoAny->pType->eTypeClass) + { + case typelib_TypeClass_VOID: + ppNetAny->data = nullptr; + break; + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + case typelib_TypeClass_CHAR: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_FLOAT: + case typelib_TypeClass_ENUM: + std::memcpy(&ppNetAny->data, pUnoAny->pData, sizeof(IntPtr)); + break; + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + case typelib_TypeClass_DOUBLE: + { + size_t size = net_sizeof(pUnoAny->pType->eTypeClass); + if (size <= sizeof(IntPtr)) + { + std::memcpy(&ppNetAny->data, pUnoAny->pData, sizeof(IntPtr)); + } + else + { + IntPtr mem = std::malloc(size); + std::memcpy(mem, pUnoAny->pData, size); + std::free(ppNetAny->data); + ppNetAny->data = mem; + } + break; + } + case typelib_TypeClass_ANY: + case typelib_TypeClass_SEQUENCE: + { + IntPtr mem = std::malloc(net_sizeof(pUnoAny->pType->eTypeClass)); + marshal_data(pUnoAny->pData, mem, pUnoAny->pType, bDestructValue, bridge); + std::free(ppNetAny->data); + ppNetAny->data = mem; + break; + } + case typelib_TypeClass_STRING: + case typelib_TypeClass_TYPE: + case typelib_TypeClass_INTERFACE: + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_STRUCT: + marshal_data(pUnoAny->pData, &ppNetAny->data, pUnoAny->pType, bDestructValue, + bridge); + break; + default: + { + throw BridgeRuntimeError(SAL_WHERE, + "could not map " + + OUString::unacquired(&pUnoAny->pType->pTypeName) + + " out of an UNO any"); + } + } + break; + } + case typelib_TypeClass_SEQUENCE: + { + Value::Sequence* ppNetSeq = static_cast<Value::Sequence*>(pNetData); + uno_Sequence* pUnoSeq = *static_cast<uno_Sequence**>(pUnoData); + + if (bDestructValue && ppNetSeq->data) + std::free(ppNetSeq->data); + + ppNetSeq->length = pUnoSeq->nElements; + + TypeDescHolder type(pTDRef); + typelib_TypeDescriptionReference* pElemTDRef + = reinterpret_cast<typelib_IndirectTypeDescription*>(type.get())->pType; + + size_t nNetElemSize = net_sizeof(pElemTDRef->eTypeClass); + + switch (pElemTDRef->eTypeClass) + { + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + case typelib_TypeClass_CHAR: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + case typelib_TypeClass_FLOAT: + case typelib_TypeClass_DOUBLE: + case typelib_TypeClass_ENUM: + { + ppNetSeq->data = std::malloc(nNetElemSize * ppNetSeq->length); + std::memcpy(ppNetSeq->data, pUnoSeq->elements, nNetElemSize * ppNetSeq->length); + break; + } + case typelib_TypeClass_ANY: + case typelib_TypeClass_SEQUENCE: + case typelib_TypeClass_STRING: + case typelib_TypeClass_TYPE: + case typelib_TypeClass_INTERFACE: + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_STRUCT: + { + TypeDescHolder elemType(pElemTDRef); + sal_Int32 nUnoElemSize = elemType.get()->nSize; + + ppNetSeq->data = std::malloc(nUnoElemSize * ppNetSeq->length); + + // Convert each element + for (int nPos = 0; nPos < ppNetSeq->length; ++nPos) + { + void* pNetElem = static_cast<char*>(ppNetSeq->data) + (nPos * nNetElemSize); + void* pUnoElem = pUnoSeq->elements + (nPos * nUnoElemSize); + marshal_data(pUnoElem, pNetElem, pElemTDRef, bDestructValue, bridge); + } + break; + } + default: + { + throw BridgeRuntimeError( + SAL_WHERE, "could not map " + OUString::unacquired(&pElemTDRef->pTypeName) + + " out of an UNO sequence"); + } + } + break; + } + case typelib_TypeClass_INTERFACE: + { + IntPtr* ppNetI = static_cast<IntPtr*>(pNetData); + uno_Interface* pUnoI = *static_cast<uno_Interface**>(pUnoData); + + if (pUnoI) + { + Context* pCtx = static_cast<Context*>(bridge.m_net_env->pContext); + + // Get oid and type name + rtl_uString* pOid = nullptr; + (*bridge.m_uno_env->getObjectIdentifier)(bridge.m_uno_env, &pOid, pUnoI); + const OUString& sOid = OUString::unacquired(&pOid); + + OUString sTypeName = map_uno_type_to_net(pTDRef); + + // Get the proxy if already created, else create new + *ppNetI = pCtx->lookupInterface(sOid.getStr(), sTypeName.getStr()); + if (!*ppNetI) + { + TypeDescHolder type(pTDRef); + typelib_InterfaceTypeDescription* pTD + = reinterpret_cast<typelib_InterfaceTypeDescription*>(type.get()); + + // TODO: check whether liftime control is correct + bridge.acquire(); + (*pUnoI->acquire)(pUnoI); + typelib_typedescription_acquire(&pTD->aBase); + (*bridge.m_uno_env->registerInterface)( + bridge.m_uno_env, reinterpret_cast<void**>(&pUnoI), pOid, pTD); + *ppNetI + = pCtx->createProxy(sOid.getStr(), sTypeName.getStr(), &bridge, pUnoI, pTD); + } + } + else + { + *ppNetI = nullptr; + } + break; + } + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_STRUCT: + { + IntPtr* ppNetStruct = static_cast<IntPtr*>(pNetData); + void* pUnoStruct = pUnoData; + + if (bDestructValue && *ppNetStruct) + std::free(*ppNetStruct); + + TypeDescHolder type(pTDRef); + typelib_CompoundTypeDescription* pCompTD + = reinterpret_cast<typelib_CompoundTypeDescription*>(type.get()); + if (!type.get()->bComplete) + typelib_typedescription_complete( + reinterpret_cast<typelib_TypeDescription**>(&pCompTD)); + + size_t nBytes = 0; + std::vector<std::pair<typelib_TypeDescriptionReference*, sal_Int32>> vecMembers; + for (typelib_CompoundTypeDescription* pHierTD = pCompTD; pHierTD; + pHierTD = pHierTD->pBaseTypeDescription) + { + for (int n = pHierTD->nMembers - 1; n >= 0; n--) + { + vecMembers.emplace_back(pHierTD->ppTypeRefs[n], pHierTD->pMemberOffsets[n]); + nBytes += net_sizeof(pHierTD->ppTypeRefs[n]->eTypeClass); + } + } + + *ppNetStruct = std::malloc(nBytes); + + size_t nNetOffset = 0; + int nPos = vecMembers.size() - 1; + for (; nPos >= 0; nPos--) + { + auto[pMemberTDRef, nUnoOffset] = vecMembers[nPos]; + + void* pUnoField = static_cast<char*>(pUnoStruct) + nUnoOffset; + void* pNetField = static_cast<char*>(*ppNetStruct) + nNetOffset; + + switch (pMemberTDRef->eTypeClass) + { + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + case typelib_TypeClass_CHAR: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + case typelib_TypeClass_FLOAT: + case typelib_TypeClass_DOUBLE: + case typelib_TypeClass_ENUM: + std::memcpy(pNetField, pUnoField, net_sizeof(pMemberTDRef->eTypeClass)); + break; + case typelib_TypeClass_ANY: + case typelib_TypeClass_SEQUENCE: + case typelib_TypeClass_STRING: + case typelib_TypeClass_TYPE: + case typelib_TypeClass_INTERFACE: + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_STRUCT: + marshal_data(pUnoField, pNetField, pMemberTDRef, bDestructValue, bridge); + break; + default: + { + throw BridgeRuntimeError( + SAL_WHERE, + "could not map " + OUString::unacquired(&pMemberTDRef->pTypeName) + + " out of an UNO " + OUString::unacquired(&pTDRef->pTypeName)); + } + } + + nNetOffset += net_sizeof(pMemberTDRef->eTypeClass); + } + break; + } + default: + { + throw BridgeRuntimeError(SAL_WHERE, "could not map " + + OUString::unacquired(&pTDRef->pTypeName) + + " value to .NET"); + } + } +} + +void unmarshal_data(void* pUnoData, void* pNetData, typelib_TypeDescriptionReference* pTDRef, + bool bDestructObject, bool bAssignData, Bridge& bridge) +{ + switch (pTDRef->eTypeClass) + { + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + case typelib_TypeClass_CHAR: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + case typelib_TypeClass_FLOAT: + case typelib_TypeClass_DOUBLE: + case typelib_TypeClass_ENUM: + if (bAssignData) + std::memcpy(pUnoData, pNetData, net_sizeof(pTDRef->eTypeClass)); + break; + case typelib_TypeClass_STRING: + { + rtl_uString** ppUnoStr = static_cast<rtl_uString**>(pUnoData); + IntPtr pNetStr = *static_cast<IntPtr*>(pNetData); + + if (bDestructObject && ppUnoStr) + rtl_uString_release(*ppUnoStr); + + if (bAssignData) + { + *ppUnoStr = nullptr; + if (pNetStr) + rtl_uString_newFromStr(ppUnoStr, static_cast<String>(pNetStr)); + else + rtl_uString_new(ppUnoStr); + } + + std::free(pNetStr); + break; + } + case typelib_TypeClass_TYPE: + { + typelib_TypeDescriptionReference** ppUnoType + = static_cast<typelib_TypeDescriptionReference**>(pUnoData); + IntPtr pNetType = *static_cast<IntPtr*>(pNetData); + + if (bDestructObject && ppUnoType) + typelib_typedescriptionreference_release(*ppUnoType); + + if (bAssignData) + *ppUnoType = map_net_type_to_uno(OUString(static_cast<String>(pNetType))); + + std::free(pNetType); + break; + } + case typelib_TypeClass_ANY: + { + uno_Any* pUnoAny = static_cast<uno_Any*>(pUnoData); + Value::Any* pNetAny = static_cast<Value::Any*>(pNetData); + + if (bDestructObject && pUnoData) + uno_any_destruct(pUnoAny, nullptr); + + typelib_TypeDescriptionReference* pValueTDRef + = map_net_type_to_uno(OUString(static_cast<String>(pNetAny->type))); + std::free(pNetAny->type); + + if (bAssignData) + { + switch (pValueTDRef->eTypeClass) + { + case typelib_TypeClass_VOID: + { + pUnoAny->pType = pValueTDRef; + pUnoAny->pData = &pUnoAny->pReserved; + break; + } + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + case typelib_TypeClass_CHAR: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_FLOAT: + case typelib_TypeClass_ENUM: + { + pUnoAny->pType = pValueTDRef; + pUnoAny->pData = &pUnoAny->pReserved; + std::memcpy(pUnoAny->pData, &pNetAny->data, sizeof(IntPtr)); + break; + } + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + case typelib_TypeClass_DOUBLE: + { + pUnoAny->pType = pValueTDRef; + size_t size = net_sizeof(pValueTDRef->eTypeClass); + if (size <= sizeof(IntPtr)) + { + pUnoAny->pData = &pUnoAny->pReserved; + std::memcpy(pUnoAny->pData, &pNetAny->data, sizeof(IntPtr)); + } + else + { + void* mem = std::malloc(size); + std::memcpy(mem, pNetAny->data, size); + pUnoAny->pData = mem; + std::free(pNetAny->data); + } + break; + } + case typelib_TypeClass_STRING: + case typelib_TypeClass_TYPE: + case typelib_TypeClass_INTERFACE: + { + if (pNetAny->data) + { + pUnoAny->pType = pValueTDRef; + pUnoAny->pData = &pUnoAny->pReserved; + pUnoAny->pReserved = nullptr; + unmarshal_data(pUnoAny->pData, &pNetAny->data, pValueTDRef, + bDestructObject, true, bridge); + } + else + { + uno_any_construct(pUnoAny, nullptr, nullptr, nullptr); + } + break; + } + case typelib_TypeClass_ANY: + case typelib_TypeClass_SEQUENCE: + { + if (pNetAny->data) + { + pUnoAny->pType = pValueTDRef; + pUnoAny->pData = &pUnoAny->pReserved; + pUnoAny->pReserved = nullptr; + unmarshal_data(pUnoAny->pData, &pNetAny->data, pValueTDRef, + bDestructObject, true, bridge); + } + else + { + uno_any_construct(pUnoAny, nullptr, nullptr, nullptr); + } + break; + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + if (pNetAny->data) + { + pUnoAny->pType = pValueTDRef; + TypeDescHolder valueType(pValueTDRef); + void* mem = std::malloc(valueType.get()->nSize); + unmarshal_data(mem, &pNetAny->data, pValueTDRef, bDestructObject, true, + bridge); + pUnoAny->pData = mem; + } + else + { + uno_any_construct(pUnoAny, nullptr, nullptr, nullptr); + } + break; + } + default: + { + throw BridgeRuntimeError(SAL_WHERE, + "could not map " + + OUString::unacquired(&pValueTDRef->pTypeName) + + " into an UNO any"); + } + } + } + else + { + switch (pValueTDRef->eTypeClass) + { + case typelib_TypeClass_VOID: + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + case typelib_TypeClass_CHAR: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_FLOAT: + case typelib_TypeClass_ENUM: + break; + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + case typelib_TypeClass_DOUBLE: + { + size_t size = net_sizeof(pValueTDRef->eTypeClass); + if (pNetAny->data && size > sizeof(IntPtr)) + std::free(pNetAny->data); + break; + } + case typelib_TypeClass_ANY: + case typelib_TypeClass_SEQUENCE: + { + if (pNetAny->data) + { + unmarshal_data(pUnoAny->pData, &pNetAny->data, pValueTDRef, + bDestructObject, false, bridge); + std::free(pNetAny->data); + } + break; + } + case typelib_TypeClass_STRING: + case typelib_TypeClass_TYPE: + case typelib_TypeClass_INTERFACE: + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + if (pNetAny->data) + { + unmarshal_data(pUnoAny->pData, &pNetAny->data, pValueTDRef, + bDestructObject, false, bridge); + } + break; + } + default: + { + throw BridgeRuntimeError(SAL_WHERE, + "could not map " + + OUString::unacquired(&pValueTDRef->pTypeName) + + " into an UNO any"); + } + } + } + break; + } + case typelib_TypeClass_SEQUENCE: + { + uno_Sequence** ppUnoSeq = static_cast<uno_Sequence**>(pUnoData); + Value::Sequence* pNetSeq = static_cast<Value::Sequence*>(pNetData); + + TypeDescHolder type(pTDRef); + if (bDestructObject && ppUnoSeq) + uno_destructData(ppUnoSeq, type.get(), nullptr); + + typelib_TypeDescriptionReference* pElemTDRef + = reinterpret_cast<typelib_IndirectTypeDescription*>(type.get())->pType; + size_t nNetElemSize = net_sizeof(pElemTDRef->eTypeClass); + + switch (pElemTDRef->eTypeClass) + { + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + case typelib_TypeClass_CHAR: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + case typelib_TypeClass_FLOAT: + case typelib_TypeClass_DOUBLE: + case typelib_TypeClass_ENUM: + if (bAssignData) + *ppUnoSeq + = alloc_uno_sequence(pNetSeq->length, nNetElemSize, pNetSeq->data); + break; + case typelib_TypeClass_ANY: + case typelib_TypeClass_SEQUENCE: + case typelib_TypeClass_STRING: + case typelib_TypeClass_TYPE: + case typelib_TypeClass_INTERFACE: + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_STRUCT: + { + TypeDescHolder elemType(pElemTDRef); + sal_Int32 nUnoElemSize = elemType.get()->nSize; + + *ppUnoSeq = alloc_uno_sequence(pNetSeq->length, nUnoElemSize, nullptr); + int nPos = 0; + try + { + // Convert each element + for (; nPos < pNetSeq->length; ++nPos) + { + void* pNetElem + = static_cast<char*>(pNetSeq->data) + (nPos * nNetElemSize); + void* pUnoElem = (*ppUnoSeq)->elements + (nPos * nUnoElemSize); + unmarshal_data(pUnoElem, pNetElem, pElemTDRef, bDestructObject, + bAssignData, bridge); + } + } + catch (...) + { + if (bAssignData) + { + // Clean up already converted elements + for (int nClean = 0; nClean < nPos; ++nClean) + { + void* pUnoElem = (*ppUnoSeq)->elements + (nClean * nUnoElemSize); + uno_destructData(pUnoElem, elemType.get(), nullptr); + } + } + throw; + } + + break; + } + default: + { + throw BridgeRuntimeError( + SAL_WHERE, "could not map " + OUString::unacquired(&pElemTDRef->pTypeName) + + " into an UNO sequence"); + } + } + + std::free(pNetSeq->data); + break; + } + case typelib_TypeClass_INTERFACE: + { + uno_Interface** ppUnoI = static_cast<uno_Interface**>(pUnoData); + IntPtr pNetI = *static_cast<IntPtr*>(pNetData); + + TypeDescHolder type(pTDRef); + if (bDestructObject && ppUnoI) + uno_destructData(ppUnoI, type.get(), nullptr); + + if (bAssignData) + { + if (pNetI) + { + Context* pCtx = static_cast<Context*>(bridge.m_net_env->pContext); + + // Get oid and type description + OUString sOid(pCtx->lookupObjectId(pNetI)); + + typelib_InterfaceTypeDescription* pInterfaceTD + = reinterpret_cast<typelib_InterfaceTypeDescription*>(type.get()); + + // Get the proxy if already created, else create new + *ppUnoI = nullptr; + (*bridge.m_uno_env->getRegisteredInterface)(bridge.m_uno_env, + reinterpret_cast<void**>(ppUnoI), + sOid.pData, pInterfaceTD); + + if (!*ppUnoI) + *ppUnoI = new NetProxy(bridge, pNetI, pInterfaceTD, sOid); + } + else + { + *ppUnoI = nullptr; + } + } + + break; + } + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_STRUCT: + { + void* pUnoStruct = pUnoData; + IntPtr pNetStruct = *static_cast<IntPtr*>(pNetData); + + TypeDescHolder type(pTDRef); + if (bDestructObject && pNetStruct) + uno_destructData(pNetStruct, type.get(), nullptr); + + typelib_CompoundTypeDescription* pCompTD + = reinterpret_cast<typelib_CompoundTypeDescription*>(type.get()); + if (!type.get()->bComplete) + typelib_typedescription_complete( + reinterpret_cast<typelib_TypeDescription**>(&pCompTD)); + + std::vector<std::pair<typelib_TypeDescriptionReference*, sal_Int32>> vecMembers; + for (typelib_CompoundTypeDescription* pHierTD = pCompTD; pHierTD; + pHierTD = pHierTD->pBaseTypeDescription) + { + for (int n = pHierTD->nMembers - 1; n >= 0; n--) + { + vecMembers.emplace_back(pHierTD->ppTypeRefs[n], pHierTD->pMemberOffsets[n]); + } + } + + size_t nNetOffset = 0; + int nPos = vecMembers.size() - 1; + try + { + for (; nPos >= 0; nPos--) + { + auto[pMemberTDRef, nUnoOffset] = vecMembers[nPos]; + + void* pUnoField = static_cast<char*>(pUnoStruct) + nUnoOffset; + void* pNetField = static_cast<char*>(pNetStruct) + nNetOffset; + + switch (pMemberTDRef->eTypeClass) + { + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + case typelib_TypeClass_CHAR: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + case typelib_TypeClass_FLOAT: + case typelib_TypeClass_DOUBLE: + case typelib_TypeClass_ENUM: + if (bAssignData) + std::memcpy(pUnoField, pNetField, + net_sizeof(pMemberTDRef->eTypeClass)); + break; + case typelib_TypeClass_ANY: + case typelib_TypeClass_SEQUENCE: + case typelib_TypeClass_STRING: + case typelib_TypeClass_TYPE: + case typelib_TypeClass_INTERFACE: + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_STRUCT: + unmarshal_data(pUnoField, pNetField, pMemberTDRef, bDestructObject, + bAssignData, bridge); + break; + default: + { + throw BridgeRuntimeError( + SAL_WHERE, + "could not map " + OUString::unacquired(&pMemberTDRef->pTypeName) + + " into an UNO " + OUString::unacquired(&pTDRef->pTypeName)); + } + } + + nNetOffset += net_sizeof(pMemberTDRef->eTypeClass); + } + } + catch (...) + { + if (bAssignData) + { + // Clean up already converted fields + for (int nClean = vecMembers.size() - 1; nClean > nPos; nClean--) + { + auto[pMemberTDRef, nUnoOffset] = vecMembers[nClean]; + void* pUnoField = static_cast<char*>(pUnoStruct) + nUnoOffset; + uno_type_destructData(pUnoField, pMemberTDRef, nullptr); + } + } + throw; + } + + std::free(pNetStruct); + break; + } + default: + { + throw BridgeRuntimeError(SAL_WHERE, "could not map " + + OUString::unacquired(&pTDRef->pTypeName) + + " value to UNO"); + } + } +} +} + +void Bridge::map_uno_to_net_value(void* pUnoData, Value* pValue, + typelib_TypeDescriptionReference* pTDRef, bool bDestructValue) +{ + marshal_data(pUnoData, pValue, pTDRef, bDestructValue, *this); +} + +void Bridge::map_net_value_to_uno(void* pUnoData, Value* pValue, + typelib_TypeDescriptionReference* pTDRef, bool bDestructObject, + bool bAssignObject) +{ + unmarshal_data(pUnoData, pValue, pTDRef, bDestructObject, bAssignObject, *this); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/bridges/source/net_uno/net_func.cxx b/bridges/source/net_uno/net_func.cxx new file mode 100644 index 000000000000..788bfe1a76c4 --- /dev/null +++ b/bridges/source/net_uno/net_func.cxx @@ -0,0 +1,251 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#include "net_bridge.hxx" + +#include <bridges/net_uno/net_context.hxx> + +namespace net_uno +{ +void Bridge::call_net_func(IntPtr pNetI, const typelib_TypeDescription* pMethodTD, + typelib_TypeDescriptionReference* pReturnTDRef, int nParams, + typelib_MethodParameter* pParams, void** pArgs, void* pRet, + uno_Any** pExc) +{ + Value* pArgsRetExc = static_cast<Value*>(alloca((nParams + 2) * sizeof(Value))); + + // Convert in and inout arguments + for (int i = 0; i < nParams; ++i) + { + const typelib_MethodParameter& param = pParams[i]; + if (param.bIn) + { + map_uno_to_net_value(pArgs[i], &pArgsRetExc[i], param.pTypeRef, false); + } + } + + OUString sMethodName = OUString::unacquired(&pMethodTD->pTypeName); + Context* pCtx = static_cast<Context*>(m_net_env->pContext); + bool error = !pCtx->dispatchCall(pNetI, sMethodName.getStr(), pArgsRetExc, + &pArgsRetExc[nParams], &pArgsRetExc[nParams + 1]); + + // Convert out and inout arguments + for (int i = 0; i < nParams; ++i) + { + const typelib_MethodParameter& param = pParams[i]; + try + { + if (param.bOut) + map_net_value_to_uno(pArgs[i], &pArgsRetExc[i], param.pTypeRef, + param.bIn && param.bOut, true); + } + catch (...) + { + // Clean up uno out args + for (int n = 0; n < i; ++n) + { + const typelib_MethodParameter& param2 = pParams[n]; + if (param2.bOut) + { + uno_type_destructData(pArgs[n], param2.pTypeRef, nullptr); + } + } + throw; + } + } + + if (error) + { + map_net_value_to_uno(*pExc, &pArgsRetExc[nParams + 1], + cppu::UnoType<css::uno::Any>::get().getTypeLibType(), false, true); + } + else + { + if (pReturnTDRef && pReturnTDRef->eTypeClass != typelib_TypeClass_VOID) + { + map_net_value_to_uno(pRet, &pArgsRetExc[nParams], pReturnTDRef, false, true); + } + + *pExc = nullptr; + } +} + +bool Bridge::call_uno_func(uno_Interface* pUnoI, const typelib_TypeDescription* pMethodTD, + typelib_TypeDescriptionReference* pReturnTDRef, int nParams, + typelib_MethodParameter* pParams, Value* pArgs, Value* pRet, Value* pExc) +{ + union largest { + sal_Int64 n; + double d; + void* p; + uno_Any a; + }; + + // Calculate size of memory required for return value + sal_Int32 nReturnSize = sizeof(largest); + if (pReturnTDRef) + { + if (pReturnTDRef->eTypeClass == typelib_TypeClass_VOID) + { + nReturnSize = 0; + } + else if (pReturnTDRef->eTypeClass == typelib_TypeClass_STRUCT + || pReturnTDRef->eTypeClass == typelib_TypeClass_EXCEPTION) + { + TypeDescHolder returnTD(pReturnTDRef); + if (o3tl::make_unsigned(returnTD.get()->nSize) > sizeof(largest)) + nReturnSize = returnTD.get()->nSize; + } + } + + // Prepare a memory block to contain all the converted arguments and return value + // + // The memory block contains pointers to small arguments stored in the same block. + // If an argument is larger then `largest` union, such as a struct, then the pointer + // points to an extra block of memory. + // + // The argument pointers are followed by the return value. If the return value is + // larger than the `largest` union, this is a pointer to an extra block containing + // the return value instead. + // + // For example: 2 arguments and return value + // | Pointer 1 (void*) + // | Pointer 2 (void*) + // | Return Value (void*) + // | Argument 1 (largest*) + // | Argument 2 (largest*) + + // Complete memory block + char* mem = static_cast<char*>( + alloca(nParams * sizeof(void*) + nReturnSize + nParams * sizeof(largest))); + // Array of argument pointers; at the start of the memory block + void** uno_args = reinterpret_cast<void**>(mem); + // Pointer to return value memory; after all argument pointers + void* uno_ret = nReturnSize == 0 ? nullptr : mem + nParams * sizeof(void*); + // Space for actual arguments; after return value + largest* uno_args_mem = reinterpret_cast<largest*>(mem + nParams * sizeof(void*) + nReturnSize); + + for (sal_Int32 i = 0; i < nParams; ++i) + { + const typelib_MethodParameter& param = pParams[i]; + typelib_TypeDescriptionReference* type = param.pTypeRef; + + uno_args[i] = &uno_args_mem[i]; + if (type->eTypeClass == typelib_TypeClass_STRUCT + || type->eTypeClass == typelib_TypeClass_EXCEPTION) + { + TypeDescHolder td(type); + if (o3tl::make_unsigned(td.get()->nSize) > sizeof(largest)) + uno_args[i] = alloca(td.get()->nSize); + } + + if (param.bIn) + { + try + { + // in, in/out params + map_net_value_to_uno(uno_args[i], &pArgs[i], type, false, true); + } + catch (...) + { + // cleanup uno in args + for (sal_Int32 n = 0; n < i; ++n) + { + const typelib_MethodParameter& param2 = pParams[n]; + if (param2.bIn) + { + uno_type_destructData(uno_args[n], param2.pTypeRef, nullptr); + } + } + throw; + } + } + } + + uno_Any uno_exc_holder; + uno_Any* uno_exc = &uno_exc_holder; + + // Propagate function call to binary uno + (*pUnoI->pDispatcher)(pUnoI, pMethodTD, uno_ret, uno_args, &uno_exc); + + if (!uno_exc) + { + // Convert out arguments and destruct previously converted uno args + for (sal_Int32 i = 0; i < nParams; ++i) + { + const typelib_MethodParameter& param = pParams[i]; + typelib_TypeDescriptionReference* type = param.pTypeRef; + + if (param.bOut) + { + try + { + map_uno_to_net_value(uno_args[i], &pArgs[i], param.pTypeRef, false); + } + catch (...) + { + // Cleanup rest of uno args + for (sal_Int32 n = i; n < nParams; ++n) + { + uno_type_destructData(uno_args[n], pParams[n].pTypeRef, nullptr); + } + + // Cleanup uno return value + uno_type_destructData(uno_ret, pReturnTDRef, nullptr); + + throw; + } + } + + // Cleanup args + if (type->eTypeClass > typelib_TypeClass_DOUBLE + && type->eTypeClass != typelib_TypeClass_ENUM) + { + uno_type_destructData(uno_args[i], type, nullptr); + } + } + + if (pReturnTDRef && pReturnTDRef->eTypeClass != typelib_TypeClass_VOID) + { + // Convert uno return value + try + { + map_uno_to_net_value(uno_ret, pRet, pReturnTDRef, false); + uno_type_destructData(uno_ret, pReturnTDRef, nullptr); + } + catch (...) + { + uno_type_destructData(uno_ret, pReturnTDRef, nullptr); + throw; + } + } + + return true; + } + else // An exception occurred + { + // Destruct uno in arguments + for (sal_Int32 i = 0; i < nParams; ++i) + { + const typelib_MethodParameter& param = pParams[i]; + if (param.bIn) + { + uno_type_destructData(uno_args[i], param.pTypeRef, nullptr); + } + } + + map_uno_to_net_value(uno_exc, pExc, cppu::UnoType<css::uno::Any>::get().getTypeLibType(), + false); + + return false; + } +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/bridges/source/net_uno/net_proxy.cxx b/bridges/source/net_uno/net_proxy.cxx new file mode 100644 index 000000000000..d4c9c1869012 --- /dev/null +++ b/bridges/source/net_uno/net_proxy.cxx @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#include "net_proxy.hxx" +#include "net_base.hxx" + +#include <bridges/net_uno/net_context.hxx> + +namespace net_uno +{ +namespace +{ +void SAL_CALL NetProxy_acquire(uno_Interface* pUnoI) SAL_THROW_EXTERN_C() +{ + NetProxy* that = static_cast<NetProxy*>(pUnoI); + that->acquire(); +} + +void SAL_CALL NetProxy_release(uno_Interface* pUnoI) SAL_THROW_EXTERN_C() +{ + NetProxy* that = static_cast<NetProxy*>(pUnoI); + that->release(); +} + +void SAL_CALL NetProxy_free([[maybe_unused]] uno_ExtEnvironment* pEnv, void* pUnoI) + SAL_THROW_EXTERN_C() +{ + NetProxy* that = static_cast<NetProxy*>(pUnoI); + delete that; +} + +void SAL_CALL NetProxy_dispatch(uno_Interface* pUnoI, const typelib_TypeDescription* pMemberTD, + void* pUnoRet, void** pUnoArgs, uno_Any** pUnoExc) + SAL_THROW_EXTERN_C() +{ + NetProxy* proxy = static_cast<NetProxy*>(pUnoI); + + try + { + switch (pMemberTD->eTypeClass) + { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + { + const typelib_InterfaceAttributeTypeDescription* pAttribTD + = reinterpret_cast<const typelib_InterfaceAttributeTypeDescription*>(pMemberTD); + + if (pUnoRet) // getter + { + proxy->m_bridge.call_net_func(proxy->m_netI, pMemberTD, + pAttribTD->pAttributeTypeRef, 0, nullptr, nullptr, + pUnoRet, pUnoExc); + } + else // setter + { + typelib_MethodParameter param; + param.pTypeRef = pAttribTD->pAttributeTypeRef; + param.bIn = true; + param.bOut = false; + + proxy->m_bridge.call_net_func(proxy->m_netI, pMemberTD, nullptr, 1, ¶m, + pUnoArgs, nullptr, pUnoExc); + } + break; + } + case typelib_TypeClass_INTERFACE_METHOD: + { + const typelib_InterfaceMethodTypeDescription* pMethodTD + = reinterpret_cast<const typelib_InterfaceMethodTypeDescription*>(pMemberTD); + + sal_Int32 nMemberPos = pMethodTD->aBase.nPosition; + assert(nMemberPos < proxy->m_TD->nAllMembers); + + sal_Int32 nFunctionPos = proxy->m_TD->pMapMemberIndexToFunctionIndex[nMemberPos]; + assert(nFunctionPos < proxy->m_TD->nMapFunctionIndexToMemberIndex); + + switch (nFunctionPos) + { + case 1: // acquire() + NetProxy_acquire(proxy); + *pUnoExc = nullptr; + break; + case 2: // release() + NetProxy_release(proxy); + *pUnoExc = nullptr; + break; + default: // arbitrary method call + proxy->m_bridge.call_net_func( + proxy->m_netI, pMemberTD, pMethodTD->pReturnTypeRef, pMethodTD->nParams, + pMethodTD->pParams, pUnoArgs, pUnoRet, pUnoExc); + break; + } + break; + } + default: + { + throw BridgeRuntimeError(SAL_WHERE, + "could not find function " + + OUString::unacquired(&pMemberTD->pTypeName)); + } + } + } + catch (const BridgeRuntimeError& err) + { + SAL_WARN("bridges", ".NET bridge error: " << err.m_message); + + css::uno::RuntimeException exc("[net_uno bridge error] " + err.m_message, + css::uno::Reference<css::uno::XInterface>()); + uno_type_any_construct(*pUnoExc, &exc, + cppu::UnoType<css::uno::RuntimeException>::get().getTypeLibType(), + nullptr); + } +} +} + +NetProxy::NetProxy(Bridge& rBridge, IntPtr pNetI, typelib_InterfaceTypeDescription* pTD, + const OUString& sOid) + : m_ref(1) + , m_bridge(rBridge) + , m_netI(nullptr) + , m_oid(sOid) + , m_TD(pTD) +{ + void* that = this; + (*m_bridge.m_uno_env->registerProxyInterface)(m_bridge.m_uno_env, &that, NetProxy_free, + m_oid.pData, m_TD); + + OUString sInterfaceName = map_uno_type_to_net(pTD->aBase.pWeakRef); + + Context* pCtx = static_cast<Context*>(m_bridge.m_net_env->pContext); + m_netI = pCtx->registerInterface(pNetI, m_oid.getStr(), sInterfaceName.getStr()); + + m_bridge.acquire(); + + uno_Interface::acquire = NetProxy_acquire; + uno_Interface::release = NetProxy_release; + uno_Interface::pDispatcher = NetProxy_dispatch; +} + +NetProxy::~NetProxy() +{ + OUString sInterfaceName = map_uno_type_to_net(m_TD->aBase.pWeakRef); + static_cast<Context*>(m_bridge.m_net_env->pContext) + ->revokeInterface(m_oid.getStr(), sInterfaceName.getStr()); + + m_bridge.release(); +} + +void NetProxy::acquire() +{ + // rebirth of proxy zombie + if (osl_atomic_increment(&m_ref) == 1) + { + void* that = this; + (*m_bridge.m_uno_env->registerProxyInterface)(m_bridge.m_uno_env, &that, NetProxy_free, + m_oid.pData, m_TD); + } +} + +void NetProxy::release() +{ + // revoke from uno env on last release + if (osl_atomic_decrement(&m_ref) == 0) + { + (*m_bridge.m_uno_env->revokeInterface)(m_bridge.m_uno_env, this); + } +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/bridges/source/net_uno/net_proxy.hxx b/bridges/source/net_uno/net_proxy.hxx new file mode 100644 index 000000000000..428073b06e84 --- /dev/null +++ b/bridges/source/net_uno/net_proxy.hxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#pragma once + +#include "net_bridge.hxx" + +#include <bridges/net_uno/net_types.hxx> +#include <rtl/ustring.hxx> + +namespace net_uno +{ +struct NetProxy : public uno_Interface +{ + mutable oslInterlockedCount m_ref; + Bridge& m_bridge; + IntPtr m_netI; + OUString m_oid; + typelib_InterfaceTypeDescription* m_TD; + + NetProxy(Bridge& rBridge, IntPtr pNetI, typelib_InterfaceTypeDescription* pTD, + const OUString& sOid); + ~NetProxy(); + + void acquire(); + void release(); +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/codemaker/source/netmaker/netproduce.cxx b/codemaker/source/netmaker/netproduce.cxx index 8f9ad8e007c5..d1fa788f28de 100644 --- a/codemaker/source/netmaker/netproduce.cxx +++ b/codemaker/source/netmaker/netproduce.cxx @@ -377,6 +377,30 @@ void NetProducer::producePlainStruct(std::string_view name, file.endBlock(); file.endLine(); // extra blank line + // generate deconstructor + file.beginLine().append("public void Deconstruct("); + separatedForeach(allFields, [&file]() { file.append(", "); }, + [this, &file](const auto& member) { + file.append("out ") + .append(getNetName(member.type)) + .append(" ") + .append(getSafeIdentifier(member.name)); + }); + file.append(")").endLine(); + file.beginBlock(); + for (const auto& member : allFields) + { + file.beginLine() + .append(getSafeIdentifier(member.name)) + .append(" = ") + .append("this.") + .append(getSafeIdentifier(member.name)) + .append(";") + .endLine(); + } + file.endBlock(); + file.endLine(); // extra blank line + // generate struct fields for (const auto& member : entity->getDirectMembers()) { @@ -586,6 +610,30 @@ void NetProducer::produceException(std::string_view name, file.endBlock(); file.endLine(); // extra blank line + // generate deconstructor + file.beginLine().append("public void Deconstruct("); + separatedForeach(allFields, [&file]() { file.append(", "); }, + [this, &file](const auto& member) { + file.append("out ") + .append(getNetName(member.type)) + .append(" ") + .append(getSafeIdentifier(member.name)); + }); + file.append(")").endLine(); + file.beginBlock(); + for (const auto& member : allFields) + { + file.beginLine() + .append(getSafeIdentifier(member.name)) + .append(" = ") + .append("this.") + .append(getSafeIdentifier(member.name)) + .append(";") + .endLine(); + } + file.endBlock(); + file.endLine(); // extra blank line + // generate exception fields for (const auto& member : entity->getDirectMembers()) { @@ -918,15 +966,16 @@ void NetProducer::produceService( .append("ctx.getServiceManager();") .endLine() .beginLine() - .append(returnType) - .append(" srv = (") - .append(returnType) - .append(")mcf.createInstanceWithContext(\"") + .append("com.sun.star.uno.Any srv = new com.sun.star.uno.Any(") + .append("mcf.createInstanceWithContext(\"") .append(name) - .append("\", ctx);") + .append("\", ctx));") .endLine() .beginLine() - .append("return srv;") + .append("return srv.cast<") + .append(returnType) + .append(">();") + .beginLine() .endLine() .endBlock(); @@ -949,8 +998,8 @@ void NetProducer::produceService( .endLine() .beginBlock() .beginLine() - .append( - "throw new com.sun.star.uno.DeploymentException(\"Could not create service ") + .append("throw new com.sun.star.uno.DeploymentException(") + .append("\"Could not create service ") .append(name) .append(" from given XComponentContext\", ctx);") .endLine() @@ -989,10 +1038,8 @@ void NetProducer::produceService( .append("ctx.getServiceManager();") .endLine() .beginLine() - .append(returnType) - .append(" srv = (") - .append(returnType) - .append(")mcf.createInstanceWithArgumentsAndContext(\"") + .append("com.sun.star.uno.Any srv = new com.sun.star.uno.Any(") + .append("mcf.createInstanceWithArgumentsAndContext(\"") .append(name) .append("\", "); if (restParam) @@ -1014,7 +1061,14 @@ void NetProducer::produceService( }); file.append(" }"); } - file.append(", ctx);").endLine().beginLine().append("return srv;").endLine().endBlock(); + file.append(", ctx));") + .endLine() + .beginLine() + .append("return srv.cast<") + .append(returnType) + .append(">();") + .endLine() + .endBlock(); for (const auto& e : ctor.exceptions) { @@ -1035,8 +1089,8 @@ void NetProducer::produceService( .endLine() .beginBlock() .beginLine() - .append( - "throw new com.sun.star.uno.DeploymentException(\"Could not create service ") + .append("throw new com.sun.star.uno.DeploymentException(") + .append("\"Could not create service ") .append(name) .append(" from given XComponentContext\", ctx);") .endLine() @@ -1089,12 +1143,23 @@ void NetProducer::produceSingleton( .beginBlock(); file.beginLine() + .append("try") + .endLine() + .beginBlock() + .beginLine() .append("com.sun.star.uno.Any sgtn = ctx.getValueByName(\"/singletons/") .append(name) .append("\");") - .endLine(); + .endLine() + .beginLine() + .append("return sgtn.cast<") + .append(getNetName(entity->getBase())) + .append(">();") + .endLine() + .endBlock(); + file.beginLine() - .append("if (!sgtn.hasValue())") + .append("catch") .endLine() .beginBlock() .beginLine() @@ -1103,11 +1168,6 @@ void NetProducer::produceSingleton( .append(" from given XComponentContext\", ctx);") .endLine() .endBlock(); - file.beginLine() - .append("return (") - .append(getNetName(entity->getBase())) - .append(")sgtn.Value;") - .endLine(); file.endBlock().endBlock(); diff --git a/include/bridges/net_uno/net_context.hxx b/include/bridges/net_uno/net_context.hxx new file mode 100644 index 000000000000..33dc2594629f --- /dev/null +++ b/include/bridges/net_uno/net_context.hxx @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#pragma once + +#include <bridges/net_uno/net_types.hxx> + +namespace net_uno +{ +typedef IntPtr (*CreateProxyFunc)(String pOid, String pInterfaceName, IntPtr pBridge, + IntPtr pUnoInterface, IntPtr pTD); +typedef String (*LookupObjectIdFunc)(IntPtr pNetInterface); +typedef IntPtr (*RegisterInterfaceFunc)(IntPtr pNetInterface, String pOid, String pInterfaceName); +typedef IntPtr (*LookupInterfaceFunc)(String pOid, String pInterfaceName); +typedef void (*RevokeInterfaceFunc)(String pOid, String pInterfaceName); +typedef sal_Int8 (*DispatchCallFunc)(IntPtr pNetInterface, String pMethodName, Value* pArgs, + Value* pRet, Value* pExc); +typedef void (*ThrowErrorFunc)(String pWhere, String pMessage); + +struct Context +{ + CreateProxyFunc createProxy; + LookupObjectIdFunc lookupObjectId; + RegisterInterfaceFunc registerInterface; + LookupInterfaceFunc lookupInterface; + RevokeInterfaceFunc revokeInterface; + DispatchCallFunc dispatchCall; + ThrowErrorFunc throwError; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/include/bridges/net_uno/net_types.hxx b/include/bridges/net_uno/net_types.hxx new file mode 100644 index 000000000000..9bb6f50f4d11 --- /dev/null +++ b/include/bridges/net_uno/net_types.hxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#pragma once + +#include <sal/types.h> + +namespace net_uno +{ +using IntPtr = void*; +using String = const sal_Unicode*; + +union Value { + // bool + sal_Bool boolData; + + // byte + sal_Int8 byteData; + + // char + sal_Unicode charData; + + // short + sal_Int16 shortData; + sal_uInt16 unsigShortData; + + // long + sal_Int32 longData; + sal_uInt32 unsigLongData; + + // hyper + sal_Int64 hyperData; + sal_uInt64 unsigHyperData; + + // float/double + float floatData; + double doubleData; + + // string + IntPtr stringData; + + // type + IntPtr typeData; + + // any + struct Any + { + IntPtr data; + IntPtr type; + } anyData; + + // enum + sal_Int32 enumData; + + // struct + IntPtr structData; + + // exception + IntPtr exceptionData; + + // sequence + struct Sequence + { + IntPtr data; + sal_Int32 length; + } sequenceData; + + // interface + IntPtr interfaceData; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox index 5fb0751cafd3..a079f64589a7 100644 --- a/include/sal/log-areas.dox +++ b/include/sal/log-areas.dox @@ -641,6 +641,7 @@ certain functionality. @li @c lingucomponent @li @c linguistic @li @c lwp - lotuswordpro +@li @c net @li @c opencl @li @c opencl.device @li @c opencl.file diff --git a/include/uno/lbnames.h b/include/uno/lbnames.h index 56f9d9942142..2c59cbc44354 100644 --- a/include/uno/lbnames.h +++ b/include/uno/lbnames.h @@ -51,6 +51,8 @@ provoking error here, because PP ignores #error #define UNO_LB_JAVA "java" /** Environment type name for CLI (Common Language Infrastructure). */ #define UNO_LB_CLI "cli" +/** Environment type name for new .NET Bindings. */ +#define UNO_LB_NET "net" #endif diff --git a/net_ure/DotnetLibrary_net_bridge.mk b/net_ure/DotnetLibrary_net_bridge.mk index 30b320b05b3f..d6a47c947e7e 100644 --- a/net_ure/DotnetLibrary_net_bridge.mk +++ b/net_ure/DotnetLibrary_net_bridge.mk @@ -9,13 +9,33 @@ $(eval $(call gb_DotnetLibrary_DotnetLibrary,net_bridge,$(gb_DotnetLibrary_CS))) $(eval $(call gb_DotnetLibrary_add_sources,net_bridge,\ - net_ure/source/bridge/NativeBootstrap \ + net_ure/source/bridge/helper/DisposeGuard \ + net_ure/source/bridge/helper/StructHelper \ + net_ure/source/bridge/helper/TypeHelper \ + net_ure/source/bridge/helper/WeakBase \ + net_ure/source/bridge/helper/WeakComponentBase \ +)) + +$(eval $(call gb_DotnetLibrary_add_sources,net_bridge,\ + net_ure/source/bridge/native/InteropMethods \ + net_ure/source/bridge/native/InteropTypes \ + net_ure/source/bridge/native/Marshaller \ + net_ure/source/bridge/native/NativeBootstrap \ + net_ure/source/bridge/native/NativeUnoProxy \ + net_ure/source/bridge/native/NetEnvironment \ + net_ure/source/bridge/native/WeakIndexTable \ + net_ure/source/bridge/native/WeakOidTypeTable \ )) $(eval $(call gb_DotnetLibrary_add_properties,net_bridge,\ + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> \ <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> \ )) +$(eval $(call gb_DotnetLibrary_add_items,net_bridge,\ + <PackageReference Include="System.Reflection.DispatchProxy" Version="4.7.1" /> \ +)) + $(eval $(call gb_DotnetLibrary_link_library,net_bridge,net_uretypes)) $(eval $(call gb_DotnetLibrary_add_properties,net_bridge,\ diff --git a/net_ure/qa/basetypes/AnyTests.cs b/net_ure/qa/basetypes/AnyTests.cs index a66fc1639d56..bedaa7ac478e 100644 --- a/net_ure/qa/basetypes/AnyTests.cs +++ b/net_ure/qa/basetypes/AnyTests.cs @@ -28,7 +28,6 @@ public class AnyTests public void with_RejectsInvalidParams() { Assert.That(() => Any.with<Any>(Any.VOID), Throws.ArgumentException); - Assert.That(() => Any.with<int>(null), Throws.ArgumentException); } [Test] diff --git a/net_ure/source/basetypes/Any.cs b/net_ure/source/basetypes/Any.cs index 03222a9f1f4f..b5d46660af8a 100644 --- a/net_ure/source/basetypes/Any.cs +++ b/net_ure/source/basetypes/Any.cs @@ -17,9 +17,51 @@ namespace com.sun.star.uno public Type Type { get; private set; } public Object Value { get; private set; } + public Any(object value) => setValue(value?.GetType() ?? typeof(void), value); public Any(Type type, object value) => setValue(type, value); - public Any(object value) : this(value?.GetType() ?? typeof(void), value) { } - public static Any with<T>(object value) => new Any(typeof(T), value); + public static Any with<T>(T value) => new Any(typeof(T), value); + + public bool contains<T>() + { + if (typeof(IQueryInterface).IsAssignableFrom(Type)) + { + if (typeof(T).IsAssignableFrom(Type)) + // Special case for already implemented interface + return true; + else if (typeof(IQueryInterface).IsAssignableFrom(typeof(T))) + // Special case for contained IQueryInterface + return ((IQueryInterface)Value).queryInterface(typeof(T)).hasValue(); + } + return typeof(T).IsAssignableFrom(Type); + } + + public T cast<T>() + { + if (typeof(IQueryInterface).IsAssignableFrom(Type)) + { + if (typeof(T).IsAssignableFrom(Type)) + // Special case for already implemented interface + return (T)Value; + else if (typeof(IQueryInterface).IsAssignableFrom(typeof(T))) + // Special case for contained IQueryInterface + return (T)((IQueryInterface)Value).queryInterface(typeof(T)).Value; + } + return (T)Value; + } + + public T castOrDefault<T>(T fallback = default) + { + if (typeof(IQueryInterface).IsAssignableFrom(Type)) + { + if (typeof(T).IsAssignableFrom(Type)) + // Special case for already implemented interface + return (T)Value; + else if (typeof(IQueryInterface).IsAssignableFrom(typeof(T))) + // Special case for contained IQueryInterface + return (T)((IQueryInterface)Value).queryInterface(typeof(T)).Value; + } + return typeof(T).IsAssignableFrom(Type) ? (T)Value : fallback; + } public bool hasValue() => Type != typeof(void); @@ -53,6 +95,6 @@ namespace com.sun.star.uno public override bool Equals(object obj) => obj is Any other && equals(other); public override int GetHashCode() => (Type, Value).GetHashCode(); - public override string ToString() => $"uno.Any {{ Type = {Type}, Value = {Value ?? "Null"} }}"; + public override string ToString() => $"com.sun.star.uno.Any {{ Type = {Type}, Value = {Value ?? "Null"} }}"; } } diff --git a/net_ure/source/basetypes/IQueryInterface.cs b/net_ure/source/basetypes/IQueryInterface.cs index 714beb54f333..8517c79baa48 100644 --- a/net_ure/source/basetypes/IQueryInterface.cs +++ b/net_ure/source/basetypes/IQueryInterface.cs @@ -12,6 +12,26 @@ namespace com.sun.star.uno { public interface IQueryInterface { - // Kept empty for now, until the .NET bridge is in place + Any queryInterface(Type type); + } + + public static class IQueryInterfaceExtensions + { + public static bool implements<T>(this IQueryInterface iqi) + where T : IQueryInterface + => iqi is T ? true : iqi.queryInterface(typeof(T)).hasValue(); + + public static T query<T>(this IQueryInterface iqi) + where T : IQueryInterface + => iqi is T t ? t : (T)iqi.queryInterface(typeof(T)).Value; + + public static T queryOrDefault<T>(this IQueryInterface iqi, T fallback = default) + where T : IQueryInterface + { + if (iqi is T t) + return t; + Any result = iqi.queryInterface(typeof(T)); + return result.hasValue() ? (T)result.Value : fallback; + } } } diff --git a/net_ure/source/bootstrap/bootstrap.cxx b/net_ure/source/bootstrap/bootstrap.cxx index 97bff2ab6506..4f4f45355a8a 100644 --- a/net_ure/source/bootstrap/bootstrap.cxx +++ b/net_ure/source/bootstrap/bootstrap.cxx @@ -9,41 +9,113 @@ #include <string_view> +#include <bridges/net_uno/net_context.hxx> #include <cppuhelper/bootstrap.hxx> #include <o3tl/string_view.hxx> #include <rtl/bootstrap.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <uno/mapping.hxx> +#include <com/sun/star/lang/XComponent.hpp> #include <com/sun/star/uno/XComponentContext.hpp> using namespace ::com::sun::star; using namespace ::com::sun::star::uno; +namespace net_uno +{ extern "C" { +SAL_DLLPUBLIC_EXPORT IntPtr bootstrap(const Context aContext) +{ + try + { + // Bootstrap UNO and start a LibreOffice process if needed + Reference<XComponentContext> xContext(::cppu::bootstrap()); + + // Get a mapping between the C++ and .NET environments + Environment cpp_env(CPPU_CURRENT_LANGUAGE_BINDING_NAME); + Environment net_env(u"" UNO_LB_NET ""_ustr, new Context(aContext)); + Mapping mapping(cpp_env.get(), net_env.get()); + if (!mapping.is()) + { + Reference<lang::XComponent> xComp(xContext, UNO_QUERY); + if (xComp.is()) + { + xComp->dispose(); + } + + throw RuntimeException(u"could not get mapping between C++ and .NET"_ustr); + } + + // Map the XComponentContext to a .NET proxy + return mapping.mapInterface(xContext.get(), cppu::UnoType<decltype(xContext)>::get()); + } + catch (const Exception& exc) + { + SAL_WARN("net", ".NET bootstrap error: " << exc.Message); + aContext.throwError((u"" SAL_WHERE ""_ustr).getStr(), exc.Message.getStr()); + return nullptr; + } +} -SAL_DLLPUBLIC_EXPORT void* bootstrap(const sal_Unicode* sParams) +SAL_DLLPUBLIC_EXPORT IntPtr defaultBootstrap_InitialComponentContext(const sal_Unicode* sIniFile, + const sal_Unicode* sParams, -e ... etc. - the rest is truncated
