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 +#include + +#include +#include +#include + +namespace net_uno +{ +namespace +{ +const std::unordered_map 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 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(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 +#include +#include + +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 +#include + +namespace net_uno +{ +namespace +{ +void SAL_CALL Mapping_acquire(uno_Mapping* mapping) SAL_THROW_EXTERN_C() +{ + Mapping* that = static_cast(mapping); + that->m_bridge->acquire(); +} + +void SAL_CALL Mapping_release(uno_Mapping* mapping) SAL_THROW_EXTERN_C() +{ + Mapping* that = static_cast(mapping); + that->m_bridge->release(); +} + +void SAL_CALL Mapping_free(uno_Mapping* mapping) SAL_THROW_EXTERN_C() +{ + Mapping* that = static_cast(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); + 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); + 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(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(pBridge); + uno_Interface* pUnoI = static_cast(pUnoInterface); + typelib_TypeDescription* pTD = static_cast(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(pBridge); + Context* context = static_cast(bridge->m_net_env->pContext); + + OUString sMethodName(sFunctionName); + + try + { + uno_Interface* pUnoI = static_cast(pUnoInterface); + typelib_InterfaceTypeDescription* pTD + = static_cast(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 "::" *(":@" "," ":" ) + 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( + 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( + 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 +#include +#include +#include + +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 +#include +#include + +#include + +namespace net_uno +{ +namespace +{ +size_t net_sizeof(typelib_TypeClass eTypeClass) +{ + static const std::unordered_map 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(dst)[len] = u'\0'; + + 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(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(pNetData); + rtl_uString* pUnoStr = *static_cast(pUnoData); + + if (bDestructValue && pNetData) + std::free(pNetData); + + *ppNetStr = alloc_net_string(OUString::unacquired(&pUnoStr)); + break; + } + case typelib_TypeClass_TYPE: + { + IntPtr* ppNetType = static_cast(pNetData); + typelib_TypeDescriptionReference* pUnoType + = *static_cast(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(pNetData); + uno_Any* pUnoAny = static_cast(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(pNetData); + uno_Sequence* pUnoSeq = *static_cast(pUnoData); + + if (bDestructValue && ppNetSeq->data) + std::free(ppNetSeq->data); + + ppNetSeq->length = pUnoSeq->nElements; + + TypeDescHolder type(pTDRef); + typelib_TypeDescriptionReference* pElemTDRef + = reinterpret_cast(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(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(pNetData); + uno_Interface* pUnoI = *static_cast(pUnoData); + + if (pUnoI) + { + Context* pCtx = static_cast(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(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(&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(pNetData); + void* pUnoStruct = pUnoData; + + if (bDestructValue && *ppNetStruct) + std::free(*ppNetStruct); + + TypeDescHolder type(pTDRef); + typelib_CompoundTypeDescription* pCompTD + = reinterpret_cast(type.get()); + if (!type.get()->bComplete) + typelib_typedescription_complete( + reinterpret_cast(&pCompTD)); + + size_t nBytes = 0; + std::vector> 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(pUnoStruct) + nUnoOffset; + void* pNetField = static_cast(*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(pUnoData); + IntPtr pNetStr = *static_cast(pNetData); + + if (bDestructObject && ppUnoStr) + rtl_uString_release(*ppUnoStr); + + if (bAssignData) + { + *ppUnoStr = nullptr; + if (pNetStr) + rtl_uString_newFromStr(ppUnoStr, static_cast(pNetStr)); + else + rtl_uString_new(ppUnoStr); + } + + std::free(pNetStr); + break; + } + case typelib_TypeClass_TYPE: + { + typelib_TypeDescriptionReference** ppUnoType + = static_cast(pUnoData); + IntPtr pNetType = *static_cast(pNetData); + + if (bDestructObject && ppUnoType) + typelib_typedescriptionreference_release(*ppUnoType); + + if (bAssignData) + *ppUnoType = map_net_type_to_uno(OUString(static_cast(pNetType))); + + std::free(pNetType); + break; + } + case typelib_TypeClass_ANY: + { + uno_Any* pUnoAny = static_cast(pUnoData); + Value::Any* pNetAny = static_cast(pNetData); + + if (bDestructObject && pUnoData) + uno_any_destruct(pUnoAny, nullptr); + + typelib_TypeDescriptionReference* pValueTDRef + = map_net_type_to_uno(OUString(static_cast(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(pUnoData); + Value::Sequence* pNetSeq = static_cast(pNetData); + + TypeDescHolder type(pTDRef); + if (bDestructObject && ppUnoSeq) + uno_destructData(ppUnoSeq, type.get(), nullptr); + + typelib_TypeDescriptionReference* pElemTDRef + = reinterpret_cast(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(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(pUnoData); + IntPtr pNetI = *static_cast(pNetData); + + TypeDescHolder type(pTDRef); + if (bDestructObject && ppUnoI) + uno_destructData(ppUnoI, type.get(), nullptr); + + if (bAssignData) + { + if (pNetI) + { + Context* pCtx = static_cast(bridge.m_net_env->pContext); + + // Get oid and type description + OUString sOid(pCtx->lookupObjectId(pNetI)); + + typelib_InterfaceTypeDescription* pInterfaceTD + = reinterpret_cast(type.get()); + + // Get the proxy if already created, else create new + *ppUnoI = nullptr; + (*bridge.m_uno_env->getRegisteredInterface)(bridge.m_uno_env, + reinterpret_cast(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(pNetData); + + TypeDescHolder type(pTDRef); + if (bDestructObject && pNetStruct) + uno_destructData(pNetStruct, type.get(), nullptr); + + typelib_CompoundTypeDescription* pCompTD + = reinterpret_cast(type.get()); + if (!type.get()->bComplete) + typelib_typedescription_complete( + reinterpret_cast(&pCompTD)); + + std::vector> 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(pUnoStruct) + nUnoOffset; + void* pNetField = static_cast(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(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 + +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(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(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::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( + alloca(nParams * sizeof(void*) + nReturnSize + nParams * sizeof(largest))); + // Array of argument pointers; at the start of the memory block + void** uno_args = reinterpret_cast(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(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::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 + +namespace net_uno +{ +namespace +{ +void SAL_CALL NetProxy_acquire(uno_Interface* pUnoI) SAL_THROW_EXTERN_C() +{ + NetProxy* that = static_cast(pUnoI); + that->acquire(); +} + +void SAL_CALL NetProxy_release(uno_Interface* pUnoI) SAL_THROW_EXTERN_C() +{ + NetProxy* that = static_cast(pUnoI); + that->release(); +} + +void SAL_CALL NetProxy_free([[maybe_unused]] uno_ExtEnvironment* pEnv, void* pUnoI) + SAL_THROW_EXTERN_C() +{ + NetProxy* that = static_cast(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(pUnoI); + + try + { + switch (pMemberTD->eTypeClass) + { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + { + const typelib_InterfaceAttributeTypeDescription* pAttribTD + = reinterpret_cast(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(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()); + uno_type_any_construct(*pUnoExc, &exc, + cppu::UnoType::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(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(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 +#include + +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 + +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 + +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,\ + true \ true \ )) +$(eval $(call gb_DotnetLibrary_add_items,net_bridge,\ + \ +)) + $(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.VOID), Throws.ArgumentException); - Assert.That(() => Any.with(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(object value) => new Any(typeof(T), value); + public static Any with(T value) => new Any(typeof(T), value); + + public bool contains() + { + 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() + { + 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 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(this IQueryInterface iqi) + where T : IQueryInterface + => iqi is T ? true : iqi.queryInterface(typeof(T)).hasValue(); + + public static T query(this IQueryInterface iqi) + where T : IQueryInterface + => iqi is T t ? t : (T)iqi.queryInterface(typeof(T)).Value; + + public static T queryOrDefault(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 +#include #include #include #include +#include +#include +#include +#include #include using namespace ::com::sun::star; using namespace ::com::sun::star::uno; +namespace net_uno +{ extern "C" { - -SAL_DLLPUBLIC_EXPORT void* bootstrap(const sal_Unicode* sParams) +SAL_DLLPUBLIC_EXPORT IntPtr bootstrap(const Context aContext) { try { + // Bootstrap UNO and start a LibreOffice process if needed + Reference 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 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::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 IntPtr defaultBootstrap_InitialComponentContext(const sal_Unicode* sIniFile, + const sal_Unicode* sParams, + const Context aContext) +{ + try + { + // Set bootstrap parameters, merged into a single string (at least for now) + // to avoid dealing with lifetimes and memory of multiple strings if (sParams) { OUString paramsStr(sParams); for (size_t i = 0; i != std::u16string_view::npos;) { - std::u16string_view name(o3tl::getToken(paramsStr, u'=', i)); - OUString key(name.substr(0, name.find_first_of('|'))); + std::u16string_view name(o3tl::getToken(paramsStr, u'|', i)); + OUString key(name.substr(0, name.find_first_of('='))); OUString val(name.substr(key.getLength() + 1)); ::rtl::Bootstrap::set(key, val); } } - Reference xContext(::cppu::bootstrap()); - return xContext.get(); + // Bootstrap UNO + Reference xContext; + if (sIniFile) + { + xContext = ::cppu::defaultBootstrap_InitialComponentContext(OUString(sIniFile)); + } + else + { + xContext = ::cppu::defaultBootstrap_InitialComponentContext(); + } + + // 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 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::get()); } - catch (...) + catch (const Exception& exc) { + SAL_WARN("net", ".NET bootstrap error: " << exc.Message); + aContext.throwError((u"" SAL_WHERE ""_ustr).getStr(), exc.Message.getStr()); return nullptr; } } } +} /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/net_ure/source/bridge/NativeBootstrap.cs b/net_ure/source/bridge/NativeBootstrap.cs deleted file mode 100644 index d7a3cee9c8cf..000000000000 --- a/net_ure/source/bridge/NativeBootstrap.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.Runtime.InteropServices; -using System.Linq; - -namespace com.sun.star.uno -{ - public static class NativeBootstrap - { - public static XComponentContext bootstrap() - { - Interop.bootstrap(null); - return null; - } - - private static class Interop - { - private const string BOOTSTRAP_LIBRARY = "net_bootstrap"; - - [DllImport(BOOTSTRAP_LIBRARY, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr bootstrap([MarshalAs(UnmanagedType.LPWStr)] string sParams); - } - } -} diff --git a/net_ure/source/bridge/helper/DisposeGuard.cs b/net_ure/source/bridge/helper/DisposeGuard.cs new file mode 100644 index 000000000000..afe8a0b0ead2 --- /dev/null +++ b/net_ure/source/bridge/helper/DisposeGuard.cs @@ -0,0 +1,25 @@ +/* + * 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 com.sun.star.lang; + +namespace com.sun.star.uno.helper +{ + // This class can be used to auto-dispose UNO components + // from managed code with a using block/statement. + public class DisposeGuard : IDisposable + { + private readonly XComponent _component; + public DisposeGuard(XComponent component) => _component = component; + + public void Dispose() => _component?.dispose(); + } + +} diff --git a/net_ure/source/bridge/helper/StructHelper.cs b/net_ure/source/bridge/helper/StructHelper.cs new file mode 100644 index 000000000000..89731d9a40c5 --- /dev/null +++ b/net_ure/source/bridge/helper/StructHelper.cs @@ -0,0 +1,56 @@ +/* + * 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.Linq; +using System.Reflection; + +namespace com.sun.star.uno.helper +{ + internal static class StructHelper + { + private static Dictionary, Type[])> _ctorCache; + private static Dictionary, Type[])> _dtorCache; + static StructHelper() + { + _ctorCache = new Dictionary, Type[])>(); + _dtorCache = new Dictionary, Type[])>(); + } + + public static (Func, Type[]) GetConstructor(Type type) + { + if (_ctorCache.TryGetValue(type, out (Func, Type[]) cached)) + return cached; + + ConstructorInfo ctor = type.GetConstructors().First(c => c.GetParameters().Length > 0); + + Func func = args => ctor.Invoke(args); + Type[] paramTypes = ctor.GetParameters() + .Select(p => TypeHelper.RemoveReference(p.ParameterType)) + .ToArray(); + + return _ctorCache[type] = (func, paramTypes); + } + + public static (Action, Type[]) GetDeconstructor(Type type) + { + if (_dtorCache.TryGetValue(type, out (Action, Type[]) cached)) + return cached; + + MethodInfo dtor = type.GetMethod("Deconstruct", BindingFlags.Instance | BindingFlags.Public); + + Action action = (target, args) => dtor.Invoke(target, args); + Type[] paramTypes = dtor.GetParameters() + .Select(p => TypeHelper.RemoveReference(p.ParameterType)) + .ToArray(); + + return _dtorCache[type] = (action, paramTypes); + } + } +} diff --git a/net_ure/source/bridge/helper/TypeHelper.cs b/net_ure/source/bridge/helper/TypeHelper.cs new file mode 100644 index 000000000000..5810ab0767e1 --- /dev/null +++ b/net_ure/source/bridge/helper/TypeHelper.cs @@ -0,0 +1,125 @@ +/* + * 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.Linq; +using System.Reflection; + +namespace com.sun.star.uno.helper +{ + internal static class TypeHelper + { + private static List _assemblies; + private static Dictionary _cache; + + static TypeHelper() + { + _assemblies = AppDomain.CurrentDomain.GetAssemblies() + .OrderBy(a => !a.FullName.StartsWith("net_")) + .ThenBy(a => a != Assembly.GetExecutingAssembly()) + .ToList(); + _cache = new Dictionary(); + + // TODO: verify this works as intended + AppDomain.CurrentDomain.AssemblyLoad += (_, e) => _assemblies.Add(e.LoadedAssembly); + } + + public static Type RemoveReference(Type type) => type.IsByRef ? type.GetElementType() : type; + + public static Type ParseType(string encoded) + { + if (_cache.TryGetValue(encoded, out Type cachedType)) + return cachedType; + + string baseName = encoded; + int arrayDims = 0; + + int genericDepth = -1; + int genericTypeCount = 0; + foreach (char c in encoded) + { + switch (c) + { + case '<': + genericDepth++; + break; + case ',': + if (genericDepth == 0) + genericTypeCount++; + break; + case '>': + if (genericDepth-- == 0) + genericTypeCount++; + break; + } + } + Type[] genericTypes = new Type[genericTypeCount]; + + genericDepth = -1; + int genericStart = -1; + int genericIndex = 0; + for (int i = 0; i < encoded.Length; i++) + { + char c = encoded[i]; + switch (c) + { + case '<': + if (++genericDepth == 0) + { + baseName = encoded.Substring(0, i); + genericStart = i + 1; + } + break; + case ',': + if (genericDepth == 0) + { + string genericType = encoded.Substring(genericStart, i - genericStart); + genericTypes[genericIndex++] = ParseType(genericType); + genericStart = i + 1; + } + break; + case '>': + if (genericDepth-- == 0) + { + string genericType = encoded.Substring(genericStart, i - genericStart); + genericTypes[genericIndex++] = ParseType(genericType); + genericStart = i + 1; + } + break; + case ']': + if (genericDepth == 0) + { + ++arrayDims; + } + break; + } + } + + Type returnType = genericTypes.Length > 0 + ? LoadType($"{baseName}`{genericTypes.Length}").MakeGenericType(genericTypes) + : LoadType(baseName); + + for (int i = 0; i < arrayDims; ++i) + returnType = returnType.MakeArrayType(); + + return _cache[encoded] = returnType; + } + + private static Type LoadType(string name) + { + foreach (Assembly assembly in _assemblies) + { + Type t = assembly.GetType(name); + if (t != null) + return t; + } + throw new RuntimeException($"could not load type {name}", null); + } + } +} diff --git a/net_ure/source/bridge/helper/WeakBase.cs b/net_ure/source/bridge/helper/WeakBase.cs new file mode 100644 index 000000000000..7b6a4f1dc36a --- /dev/null +++ b/net_ure/source/bridge/helper/WeakBase.cs @@ -0,0 +1,77 @@ +/* + * 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.Concurrent; +using System.Collections.Generic; +using System.Linq; + +using com.sun.star.uno; +using com.sun.star.lang; + +namespace com.sun.star.uno.helper +{ + // This class can be used as a helper base class for UNO interfaces. + // It allows taking weak references to the object (com.sun.star.uno.XWeak) + // and using the object from StarBasic (com.sun.star.lang.XTypeProvider). + public class WeakBase : IQueryInterface, XWeak, XTypeProvider + { + private WeakAdapter _adapter; + ~WeakBase() => _adapter?.RaiseTargetDestructing(); + + private static readonly ConcurrentDictionary _providedTypes; + static WeakBase() => _providedTypes = new ConcurrentDictionary(); + + // IQueryInterface + public Any queryInterface(Type type) + => type.IsAssignableFrom(GetType()) ? new Any(type, this) : Any.VOID; + + // XWeak + public XAdapter queryAdapter() + { + if (_adapter == null) + _adapter = new WeakAdapter(this); + return _adapter; + } + + // XTypeProvider + public Type[] getTypes() + { + return _providedTypes.GetOrAdd(GetType(), + type => type.GetInterfaces() + .Where(i => typeof(IQueryInterface).IsAssignableFrom(i)) + .ToArray() + ); + } + + public sbyte[] getImplementationId() => Array.Empty(); + + // An XAdapter implementation that holds a weak reference to an interface. + private class WeakAdapter : IQueryInterface, XAdapter + { + private event Action _onDispose; + + private WeakReference _weakRef; + public WeakAdapter(IQueryInterface obj) + => _weakRef = new WeakReference(obj); + + public void RaiseTargetDestructing() => _onDispose?.Invoke(); + + // IQueryInterface + public Any queryInterface(Type type) + => type.IsAssignableFrom(GetType()) ? new Any(type, this) : Any.VOID; + + // XAdapter + public IQueryInterface queryAdapted() + => _weakRef.TryGetTarget(out IQueryInterface target) ? target : null; + + public void addReference(XReference reference) => _onDispose += reference.dispose; + public void removeReference(XReference reference) => _onDispose -= reference.dispose; + } + } +} diff --git a/net_ure/source/bridge/helper/WeakComponentBase.cs b/net_ure/source/bridge/helper/WeakComponentBase.cs new file mode 100644 index 000000000000..5bdd79f4dd82 --- /dev/null +++ b/net_ure/source/bridge/helper/WeakComponentBase.cs @@ -0,0 +1,40 @@ +/* + * 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.Concurrent; +using System.Collections.Generic; +using System.Linq; + +using com.sun.star.uno; +using com.sun.star.lang; + +namespace com.sun.star.uno.helper +{ + // This class can be used as a helper base class for UNO interfaces. + // It allows taking weak references to the object (com.sun.star.uno.XWeak), + // using the object from StarBasic (com.sun.star.lang.XTypeProvider) + // and explicitly controlling its lifetime (com.sun.star.lang.XComponent) + public class WeakComponentBase : WeakBase, XComponent + { + private readonly List _listeners; + public WeakComponentBase() => _listeners = new List(); + + // XComponent + public void dispose() + { + EventObject e = new EventObject(this); + foreach (XEventListener listener in _listeners) + listener.disposing(e); + _listeners.Clear(); + } + + public void addEventListener(XEventListener listener) => _listeners.Add(listener); + public void removeEventListener(XEventListener listener) => _listeners.Remove(listener); + } +} diff --git a/net_ure/source/bridge/native/InteropMethods.cs b/net_ure/source/bridge/native/InteropMethods.cs new file mode 100644 index 000000000000..454ad888dedb --- /dev/null +++ b/net_ure/source/bridge/native/InteropMethods.cs @@ -0,0 +1,50 @@ +/* + * 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.Runtime.InteropServices; + +namespace com.sun.star.uno.native +{ + internal static class InteropMethods + { + private const string BOOTSTRAP_LIBRARY = "net_bootstrap"; + private const string UNO_LIBRARY = "net_uno"; + + [DllImport(BOOTSTRAP_LIBRARY, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern IntPtr bootstrap(InteropContext pCtx); + + [DllImport(BOOTSTRAP_LIBRARY, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern IntPtr defaultBootstrap_InitialComponentContext( + [MarshalAs(UnmanagedType.LPWStr)] string sIniFile, + [MarshalAs(UnmanagedType.LPWStr)] string sParams, + InteropContext pCtx + ); + + [DllImport(UNO_LIBRARY, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern IntPtr allocateMemory(int nBytes); + + [DllImport(UNO_LIBRARY, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern void freeMemory(IntPtr pMemory); + + [DllImport(UNO_LIBRARY, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern void releaseProxy(IntPtr pBridgeHandle, + IntPtr pInterfaceHandle, IntPtr pTypeHandle); + + [DllImport(UNO_LIBRARY, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static unsafe extern byte dispatchCall( + IntPtr pBridgeHandle, + IntPtr pInterfaceHandle, + IntPtr pTypeHandle, + [MarshalAs(UnmanagedType.LPWStr)] string sMethod, + InteropValue* pNetArgs, + InteropValue* pNetRet, + InteropValue* pNetExc + ); + } +} diff --git a/net_ure/source/bridge/native/InteropTypes.cs b/net_ure/source/bridge/native/InteropTypes.cs new file mode 100644 index 000000000000..46517edd8fd5 --- /dev/null +++ b/net_ure/source/bridge/native/InteropTypes.cs @@ -0,0 +1,86 @@ +/* + * 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.Runtime.InteropServices; + +namespace com.sun.star.uno.native +{ + [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] + internal struct InteropValue + { + [FieldOffset(0)] public byte boolData; // (System.Bool is not blittable) + [FieldOffset(0)] public sbyte byteData; + [FieldOffset(0)] public char charData; + [FieldOffset(0)] public short shortData; + [FieldOffset(0)] public ushort unsigShortData; + [FieldOffset(0)] public int longData; + [FieldOffset(0)] public uint unsigLongData; + [FieldOffset(0)] public long hyperData; + [FieldOffset(0)] public ulong unsigHyperData; + [FieldOffset(0)] public float floatData; + [FieldOffset(0)] public double doubleData; + [FieldOffset(0)] public IntPtr stringData; + [FieldOffset(0)] public IntPtr typeData; + [FieldOffset(0)] public Any anyData; + [FieldOffset(0)] public int enumData; + [FieldOffset(0)] public IntPtr structData; + [FieldOffset(0)] public IntPtr exceptionData; + [FieldOffset(0)] public Sequence sequenceData; + [FieldOffset(0)] public IntPtr interfaceData; + + [StructLayout(LayoutKind.Sequential)] + public struct Any + { + public IntPtr data; + public IntPtr type; + } + + [StructLayout(LayoutKind.Sequential)] + public struct Sequence + { + public IntPtr data; + public int length; + } + }; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + internal delegate IntPtr CreateProxyFunc(string pOid, string pInterfaceName, IntPtr pBridge, + IntPtr pUnoInterface, IntPtr pTypeDesc); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + internal delegate string LookupObjectIdFunc(IntPtr pNetInterface); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + internal delegate IntPtr RegisterInterfaceFunc(IntPtr pNetInterface, string pOid, string pInterfaceName); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + internal delegate IntPtr LookupInterfaceFunc(string pOid, string pInterfaceName); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + internal delegate void RevokeInterfaceFunc(string pOid, string pInterfaceName); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + internal delegate byte DispatchCallFunc(IntPtr pNetInterface, string pMethodName, + IntPtr pArgs, IntPtr pRet, IntPtr pExc); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + internal delegate void ThrowErrorFunc(string pMessage, string pWhere); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct InteropContext + { + public CreateProxyFunc createProxy; + public LookupObjectIdFunc lookupObjectId; + public RegisterInterfaceFunc registerInterface; + public LookupInterfaceFunc lookupInterface; + public RevokeInterfaceFunc revokeInterface; + public DispatchCallFunc dispatchCall; + public ThrowErrorFunc throwError; + }; +} diff --git a/net_ure/source/bridge/native/Marshaller.cs b/net_ure/source/bridge/native/Marshaller.cs new file mode 100644 index 000000000000..4112ab948ae3 --- /dev/null +++ b/net_ure/source/bridge/native/Marshaller.cs @@ -0,0 +1,608 @@ +/* + * 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.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using com.sun.star.uno.helper; + +namespace com.sun.star.uno.native +{ + internal class Marshaller + { + private readonly NetEnvironment _env; + public Marshaller(NetEnvironment env) => _env = env; + + public unsafe void MarshalObject(Type type, object src, InteropValue* value, bool destructValue) + => MarshalObject(type, src, (void*)value, destructValue); + + public unsafe void UnmarshalObject(Type type, ref object dst, InteropValue* value, bool assignObject) + => UnmarshalObject(type, ref dst, (void*)value, assignObject); + + private unsafe void MarshalObject(Type type, object source, void* value, bool destructValue) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + *(byte*)value = (byte)((bool)source ? 1 : 0); + break; + case TypeCode.SByte: + *(sbyte*)value = (sbyte)source; + break; + case TypeCode.Char: + *(char*)value = (char)source; + break; + case TypeCode.Int16: + *(short*)value = (short)source; + break; + case TypeCode.UInt16: + *(ushort*)value = (ushort)source; + break; + case TypeCode.Int32: + *(int*)value = (int)source; + break; + case TypeCode.UInt32: + *(uint*)value = (uint)source; + break; + case TypeCode.Int64: + *(long*)value = (long)source; + break; + case TypeCode.UInt64: + *(ulong*)value = (ulong)source; + break; + case TypeCode.Single: + *(float*)value = (float)source; + break; + case TypeCode.Double: + *(double*)value = (double)source; + break; + case TypeCode.String: + { + IntPtr* valueStr = (IntPtr*)value; + string netStr = (string)source; + + if (destructValue && *valueStr != IntPtr.Zero) + InteropMethods.freeMemory(*valueStr); + + *valueStr = MarshalString(netStr); + break; + } + default: + if (type == typeof(void)) + { + *(IntPtr*)value = IntPtr.Zero; + } + else if (type == typeof(Type)) + { + IntPtr* valueType = (IntPtr*)value; + Type netType = (Type)source; + + if (destructValue && *valueType != IntPtr.Zero) + InteropMethods.freeMemory(*valueType); + *valueType = MarshalString(netType.FullName); + } + else if (type == typeof(Any)) + { + InteropValue.Any* valueAny = (InteropValue.Any*)value; + Any netAny = (Any)source; + + switch (Type.GetTypeCode(netAny.Type)) + { + case TypeCode.Boolean: + case TypeCode.SByte: + case TypeCode.Char: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Single: + MarshalObject(netAny.Type, netAny.Value, (void*)&valueAny->data, destructValue); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Double: + { + int size = SizeOf(netAny.Type); + if (size <= IntPtr.Size) + MarshalObject(netAny.Type, netAny.Value, (void*)&valueAny->data, destructValue); + else + { + IntPtr mem = InteropMethods.allocateMemory(size); + MarshalObject(netAny.Type, netAny.Value, (void*)mem, false); + if (destructValue) + InteropMethods.freeMemory(valueAny->data); + valueAny->data = mem; + } + break; + } + default: + if (netAny.Type == typeof(Any) || netAny.Type.IsArray) + { + IntPtr mem = InteropMethods.allocateMemory(SizeOf(netAny.Type)); + MarshalObject(netAny.Type, netAny.Value, (void*)mem, false); + if (destructValue) + InteropMethods.freeMemory(valueAny->data); + valueAny->data = mem; + } + else + { + MarshalObject(netAny.Type, netAny.Value, (void*)&valueAny->data, destructValue); + } + break; + } + + MarshalObject(typeof(Type), netAny.Type, (void*)&valueAny->type, destructValue); + } + else if (type.IsEnum) + { + *(int*)value = (int)source; + } + else if (type.IsArray) + { + InteropValue.Sequence* valueSeq = (InteropValue.Sequence*)value; + Array netArray = (Array)source; + + Type elemType = type.GetElementType(); + int elemSize = SizeOf(elemType); + + int memSize = elemSize * netArray.Length; + IntPtr mem = InteropMethods.allocateMemory(memSize); + + switch (Type.GetTypeCode(elemType)) + { + case TypeCode.Boolean: + fixed (void* src = (bool[])netArray) + Buffer.MemoryCopy(src, (void*)mem, memSize, memSize); + break; + case TypeCode.SByte: + fixed (void* src = (sbyte[])netArray) + Buffer.MemoryCopy(src, (void*)mem, memSize, memSize); + break; + case TypeCode.Char: + fixed (void* src = (char[])netArray) + Buffer.MemoryCopy(src, (void*)mem, memSize, memSize); + break; + case TypeCode.Int16: + fixed (void* src = (short[])netArray) + Buffer.MemoryCopy(src, (void*)mem, memSize, memSize); + break; + case TypeCode.UInt16: + fixed (void* src = (ushort[])netArray) + Buffer.MemoryCopy(src, (void*)mem, memSize, memSize); + break; + case TypeCode.Int32: + fixed (void* src = (int[])netArray) + Buffer.MemoryCopy(src, (void*)mem, memSize, memSize); + break; + case TypeCode.UInt32: + fixed (void* src = (uint[])netArray) + Buffer.MemoryCopy(src, (void*)mem, memSize, memSize); + break; + case TypeCode.Int64: + fixed (void* src = (long[])netArray) + Buffer.MemoryCopy(src, (void*)mem, memSize, memSize); + break; + case TypeCode.UInt64: + fixed (void* src = (ulong[])netArray) + Buffer.MemoryCopy(src, (void*)mem, memSize, memSize); + break; + case TypeCode.Single: + fixed (void* src = (float[])netArray) + Buffer.MemoryCopy(src, (void*)mem, memSize, memSize); + break; + case TypeCode.Double: + fixed (void* src = (double[])netArray) + Buffer.MemoryCopy(src, (void*)mem, memSize, memSize); + break; + default: + if (elemType.IsEnum) + { + fixed (void* src = (int[])netArray) + Buffer.MemoryCopy(src, (void*)mem, memSize, memSize); + } + else + { + for (int i = 0; i < netArray.Length; i++) + MarshalObject(elemType, netArray.GetValue(i), (void*)(mem + elemSize * i), destructValue); + } + break; + } + + if (destructValue && valueSeq->data != IntPtr.Zero) + InteropMethods.freeMemory(valueSeq->data); + + valueSeq->data = mem; + valueSeq->length = netArray.Length; + } + else if (typeof(IQueryInterface).IsAssignableFrom(type)) + { + *(IntPtr*)value = source is NativeUnoProxy nup + ? _env.LookupInterface(nup.Oid, nup.Type) + : _env.RegisterLocal(source, type); + } + else + { + IntPtr* valueStruct = (IntPtr*)value; + + (Action dtor, Type[] fieldTypes) = StructHelper.GetDeconstructor(type); + object[] fields = new object[fieldTypes.Length]; + dtor.Invoke(source, fields); + + IntPtr mem = InteropMethods.allocateMemory(fieldTypes.Sum(SizeOf)); + + int offset = 0; + for (int i = 0; i < fieldTypes.Length; i++) + { + MarshalObject(fieldTypes[i], fields[i], (void*)(mem + offset), destructValue); + offset += SizeOf(fieldTypes[i]); + } + + if (destructValue && *valueStruct != IntPtr.Zero) + InteropMethods.freeMemory(*valueStruct); + + *valueStruct = mem; + } + break; + } + } + + private unsafe void UnmarshalObject(Type type, ref object target, void* value, bool assignObject) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + if (assignObject) + target = *(byte*)value != 0; + break; + case TypeCode.SByte: + if (assignObject) + target = *(byte*)value; + break; + case TypeCode.Char: + if (assignObject) + target = *(char*)value; + break; + case TypeCode.Int16: + if (assignObject) + target = *(short*)value; + break; + case TypeCode.UInt16: + if (assignObject) + target = *(ushort*)value; + break; + case TypeCode.Int32: + if (assignObject) + target = *(int*)value; + break; + case TypeCode.UInt32: + if (assignObject) + target = *(uint*)value; + break; + case TypeCode.Int64: + if (assignObject) + target = *(long*)value; + break; + case TypeCode.UInt64: + if (assignObject) + target = *(ulong*)value; + break; + case TypeCode.Single: + if (assignObject) + target = *(float*)value; + break; + case TypeCode.Double: + if (assignObject) + target = *(double*)value; + break; + case TypeCode.String: + { + IntPtr valueStr = *(IntPtr*)value; + if (assignObject) + target = UnmarshalString(valueStr); + else + InteropMethods.freeMemory(valueStr); + } + break; + default: + if (type == typeof(void)) + { + if (assignObject) + target = null; + } + else if (type == typeof(Type)) + { + IntPtr valueType = *(IntPtr*)value; + if (assignObject) + target = TypeHelper.ParseType(UnmarshalString(valueType)); + else + InteropMethods.freeMemory(valueType); + } + else if (type == typeof(Any)) + { + InteropValue.Any* valueAny = (InteropValue.Any*)value; + + object contents = null; + Type containedType = TypeHelper.ParseType(UnmarshalString(valueAny->type)); + + if (assignObject) + { + switch (Type.GetTypeCode(containedType)) + { + case TypeCode.Boolean: + case TypeCode.SByte: + case TypeCode.Char: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Single: + UnmarshalObject(containedType, ref contents, (void*)&valueAny->data, true); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Double: + { + int size = SizeOf(containedType); + if (size <= IntPtr.Size) + { + UnmarshalObject(containedType, ref contents, (void*)&valueAny->data, true); + } + else + { + UnmarshalObject(containedType, ref contents, (void*)valueAny->data, true); + InteropMethods.freeMemory(valueAny->data); + } + break; + } + default: + if (containedType == typeof(Any) || containedType.IsArray) + { + UnmarshalObject(containedType, ref contents, (void*)valueAny->data, true); + InteropMethods.freeMemory(valueAny->data); + } + else + { + UnmarshalObject(containedType, ref contents, (void*)&valueAny->data, true); + } + break; + } + target = new Any(containedType, contents); + } + else + { + switch (Type.GetTypeCode(containedType)) + { + case TypeCode.Boolean: + case TypeCode.SByte: + case TypeCode.Char: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Single: + break; + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Double: + if (SizeOf(containedType) > IntPtr.Size) + InteropMethods.freeMemory(valueAny->data); + break; + default: + if (containedType == typeof(Any) || containedType.IsArray) + { + UnmarshalObject(containedType, ref contents, (void*)valueAny->data, false); + InteropMethods.freeMemory(valueAny->data); + } + else + { + UnmarshalObject(containedType, ref contents, (void*)&valueAny->data, false); + } + break; + } + } + } + else if (type.IsEnum) + { + target = *(int*)value; + } + else if (type.IsArray) + { + InteropValue.Sequence* valueSeq = (InteropValue.Sequence*)value; + + Type elemType = type.GetElementType(); + int elemSize = SizeOf(elemType); + + if (assignObject) + { + int memSize = elemSize * valueSeq->length; + Array netArray = Array.CreateInstance(elemType, valueSeq->length); + + switch (Type.GetTypeCode(elemType)) + { + case TypeCode.Boolean: + fixed (void* dst = (bool[])netArray) + Buffer.MemoryCopy((void*)valueSeq->data, dst, memSize, memSize); + break; + case TypeCode.SByte: + fixed (void* dst = (sbyte[])netArray) + Buffer.MemoryCopy((void*)valueSeq->data, dst, memSize, memSize); + break; + case TypeCode.Char: + fixed (void* dst = (char[])netArray) + Buffer.MemoryCopy((void*)valueSeq->data, dst, memSize, memSize); + break; + case TypeCode.Int16: + fixed (void* dst = (short[])netArray) + Buffer.MemoryCopy((void*)valueSeq->data, dst, memSize, memSize); + break; + case TypeCode.UInt16: + fixed (void* dst = (ushort[])netArray) + Buffer.MemoryCopy((void*)valueSeq->data, dst, memSize, memSize); + break; + case TypeCode.Int32: + fixed (void* dst = (int[])netArray) + Buffer.MemoryCopy((void*)valueSeq->data, dst, memSize, memSize); + break; + case TypeCode.UInt32: + fixed (void* dst = (uint[])netArray) + Buffer.MemoryCopy((void*)valueSeq->data, dst, memSize, memSize); + break; + case TypeCode.Int64: + fixed (void* dst = (long[])netArray) + Buffer.MemoryCopy((void*)valueSeq->data, dst, memSize, memSize); + break; + case TypeCode.UInt64: + fixed (void* dst = (ulong[])netArray) + Buffer.MemoryCopy((void*)valueSeq->data, dst, memSize, memSize); + break; + case TypeCode.Single: + fixed (void* dst = (float[])netArray) + Buffer.MemoryCopy((void*)valueSeq->data, dst, memSize, memSize); + break; + case TypeCode.Double: + fixed (void* dst = (double[])netArray) + Buffer.MemoryCopy((void*)valueSeq->data, dst, memSize, memSize); + break; + default: + if (elemType.IsEnum) + { + fixed (void* dst = (int[])netArray) + Buffer.MemoryCopy((void*)valueSeq->data, dst, memSize, memSize); + } + else + { + object inner = null; + for (int i = 0; i < netArray.Length; i++) + { + UnmarshalObject(elemType, ref inner, (void*)(valueSeq->data + elemSize * i), true); + netArray.SetValue(inner, i); + } + } + break; + } + target = netArray; + } + else + { + switch (Type.GetTypeCode(elemType)) + { + case TypeCode.Boolean: + case TypeCode.SByte: + case TypeCode.Char: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + break; + default: + if (elemType.IsEnum || typeof(IQueryInterface).IsAssignableFrom(type)) + { + break; + } + else + { + object dummy = null; + for (int i = 0; i < valueSeq->length; i++) + UnmarshalObject(elemType, ref dummy, (void*)(valueSeq->data + elemSize * i), false); + } + break; + } + } + + InteropMethods.freeMemory(valueSeq->data); + } + else if (typeof(IQueryInterface).IsAssignableFrom(type)) + { + if (assignObject) + target = _env.GetRegisteredObject(*(IntPtr*)value); + } + else + { + IntPtr valueStruct = *(IntPtr*)value; + + (Func ctor, Type[] paramTypes) = StructHelper.GetConstructor(type); + if (assignObject) + { + object[] args = new object[paramTypes.Length]; + int offset = 0; + for (int i = 0; i < paramTypes.Length; i++) + { + UnmarshalObject(paramTypes[i], ref args[i], (void*)(valueStruct + offset), true); + offset += SizeOf(paramTypes[i]); + } + target = ctor(args); + } + else + { + object dummy = null; + int offset = 0; + for (int i = 0; i < paramTypes.Length; i++) + { + UnmarshalObject(paramTypes[i], ref dummy, (void*)(valueStruct + offset), false); + offset += SizeOf(paramTypes[i]); + } + } + + InteropMethods.freeMemory(valueStruct); + } + break; + } + } + + private unsafe IntPtr MarshalString(string str) + { + char* buffer = (char*)InteropMethods.allocateMemory((str.Length + 1) * sizeof(char)); + fixed (char* data = str) + { + Buffer.MemoryCopy(data, buffer, str.Length * sizeof(char), str.Length * sizeof(char)); + buffer[str.Length] = '\0'; + } + return (IntPtr)buffer; + } + + private string UnmarshalString(IntPtr ptr) + { + string s = Marshal.PtrToStringUni(ptr); + InteropMethods.freeMemory(ptr); + return s; + } + + private int SizeOf(Type type) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: return sizeof(bool); + case TypeCode.SByte: return sizeof(sbyte); + case TypeCode.Char: return sizeof(char); + case TypeCode.Int16: return sizeof(short); + case TypeCode.UInt16: return sizeof(ushort); + case TypeCode.Int32: return sizeof(int); + case TypeCode.UInt32: return sizeof(uint); + case TypeCode.Int64: return sizeof(long); + case TypeCode.UInt64: return sizeof(ulong); + case TypeCode.Single: return sizeof(float); + case TypeCode.Double: return sizeof(double); + } + if (type.IsEnum) + return sizeof(int); + else if (type.IsArray) + return IntPtr.Size + sizeof(int); + else if (type == typeof(Any)) + return IntPtr.Size * 2; + return IntPtr.Size; + } + } +} diff --git a/net_ure/source/bridge/native/NativeBootstrap.cs b/net_ure/source/bridge/native/NativeBootstrap.cs new file mode 100644 index 000000000000..a6dae072c973 --- /dev/null +++ b/net_ure/source/bridge/native/NativeBootstrap.cs @@ -0,0 +1,49 @@ +/* + * 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.Linq; +using System.Runtime.InteropServices; + +using com.sun.star.uno.native; + +namespace com.sun.star.uno +{ + public static class NativeBootstrap + { + private static LinkedList> _environments; + static NativeBootstrap() => _environments = new LinkedList>(); + + public static XComponentContext bootstrap() + { + NetEnvironment env = new NetEnvironment(); + _environments.AddLast(new WeakReference(env)); + + IntPtr result = InteropMethods.bootstrap(env.Context); + return (XComponentContext)env.GetRegisteredObject(result); + } + + public static XComponentContext defaultBootstrap_InitialComponentContext( + string iniFile = null, IDictionary parameters = null) + { + iniFile = string.IsNullOrWhiteSpace(iniFile) ? null : iniFile; + + string joinedParams = string.Join("|", + parameters?.Select(p => $"{p.Key}={p.Value}") ?? Array.Empty()); + joinedParams = string.IsNullOrWhiteSpace(joinedParams) ? null : joinedParams; + + NetEnvironment env = new NetEnvironment(); + _environments.AddLast(new WeakReference(env)); + + IntPtr result = InteropMethods.defaultBootstrap_InitialComponentContext( + iniFile, joinedParams, env.Context); + return (XComponentContext)env.GetRegisteredObject(result); + } + } +} diff --git a/net_ure/source/bridge/native/NativeUnoProxy.cs b/net_ure/source/bridge/native/NativeUnoProxy.cs new file mode 100644 index 000000000000..3795b7559061 --- /dev/null +++ b/net_ure/source/bridge/native/NativeUnoProxy.cs @@ -0,0 +1,58 @@ +/* + * 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; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using com.sun.star.uno; + +namespace com.sun.star.uno.native +{ + internal class NativeUnoProxy : DispatchProxy + { + private IntPtr _bridgeHandle; + private IntPtr _interfaceHandle; + private IntPtr _typeHandle; + private NetEnvironment _env; + + public string Oid { get; private set; } + public Type Type { get; private set; } + + ~NativeUnoProxy() => _env.ReleaseProxy(this, _bridgeHandle, _interfaceHandle, _typeHandle); + + public static object CreateProxy(string oid, Type interfaceType, + IntPtr pBridge, IntPtr pUnoInterface, IntPtr pTypeDesc, NetEnvironment env) + { + if (!typeof(IQueryInterface).IsAssignableFrom(interfaceType)) + throw new RuntimeException($"interfaceType must be IQueryInterface or a derived interface, was {interfaceType}", null); + + MethodInfo method = typeof(DispatchProxy).GetMethod("Create", Type.EmptyTypes); + MethodInfo typedMethod = method.MakeGenericMethod(interfaceType, typeof(NativeUnoProxy)); + + NativeUnoProxy proxy = (NativeUnoProxy)typedMethod.Invoke(null, null); + proxy.Oid = oid; + proxy.Type = interfaceType; + proxy._bridgeHandle = pBridge; + proxy._interfaceHandle = pUnoInterface; + proxy._typeHandle = pTypeDesc; + proxy._env = env; + + return proxy; + } + + protected override object Invoke(MethodInfo targetMethod, object[] args) + => _env.InvokeMethod(targetMethod, args, _bridgeHandle, _interfaceHandle, _typeHandle); + + public override bool Equals(object obj) => obj is NativeUnoProxy nup && Oid == nup.Oid; + public override int GetHashCode() => Oid.GetHashCode(); + public override string ToString() => $"UNO Proxy {{ OID = {Oid}, Interface = {Type} }}"; + } +} diff --git a/net_ure/source/bridge/native/NetEnvironment.cs b/net_ure/source/bridge/native/NetEnvironment.cs new file mode 100644 index 000000000000..829def392d57 --- /dev/null +++ b/net_ure/source/bridge/native/NetEnvironment.cs @@ -0,0 +1,227 @@ +/* + * 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; + } + } + } +} diff --git a/net_ure/source/bridge/native/WeakIndexTable.cs b/net_ure/source/bridge/native/WeakIndexTable.cs new file mode 100644 index 000000000000..cbabf1c67b63 --- /dev/null +++ b/net_ure/source/bridge/native/WeakIndexTable.cs @@ -0,0 +1,103 @@ +/* + * 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.Linq; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace com.sun.star.uno.native +{ + internal class WeakIndexTable + { + private readonly Dictionary> _objectByIndex; + private readonly Dictionary, int> _indexByObject; + private readonly List _cleanupIndices; + private readonly object _lock; + + private int _counter; + private int _updateOps; + + public WeakIndexTable() + { + _objectByIndex = new Dictionary>(); + _indexByObject = new Dictionary, int>(new WeakReferenceComparer()); + _cleanupIndices = new List(); + _lock = new object(); + + _counter = 1; // index 0 is reserved for null + _updateOps = 0; + } + + public object Lookup(int index) + { + if (index == 0) + return null; + + if (_objectByIndex.TryGetValue(index, out WeakReference weakRef)) + if (weakRef.TryGetTarget(out object obj)) + return obj; + + throw new RuntimeException($"no object was found at index {index}", null); + } + + public int Register(object obj) + { + if (obj == null) + return 0; + + WeakReference weakRef = new WeakReference(obj); + if (_indexByObject.TryGetValue(weakRef, out int index)) + return index; + + lock (_lock) + { + _objectByIndex.Add(_counter, weakRef); + _indexByObject.Add(weakRef, _counter); + + _updateOps++; + AutoClean(); + + return _counter++; + } + } + + private void AutoClean() + { + if (_updateOps >= _objectByIndex.Count) + { + _updateOps = 0; + foreach (int key in _objectByIndex.Keys) + { + WeakReference weakRef = _objectByIndex[key]; + if (!weakRef.TryGetTarget(out _)) + _cleanupIndices.Add(key); + } + + foreach (int key in _cleanupIndices) + _objectByIndex.Remove(key); + _cleanupIndices.Clear(); + } + } + + private class WeakReferenceComparer : IEqualityComparer> + { + public bool Equals(WeakReference x, WeakReference y) + { + if (x.TryGetTarget(out object first)) + if (y.TryGetTarget(out object second)) + return ReferenceEquals(first, second); + return false; + } + + public int GetHashCode(WeakReference obj) + => obj.TryGetTarget(out object target) ? target.GetHashCode() : 0; + } + } +} diff --git a/net_ure/source/bridge/native/WeakOidTypeTable.cs b/net_ure/source/bridge/native/WeakOidTypeTable.cs new file mode 100644 index 000000000000..25c49c3bedf1 --- /dev/null +++ b/net_ure/source/bridge/native/WeakOidTypeTable.cs @@ -0,0 +1,179 @@ +/* + * 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.Linq; +using System.Threading; + +namespace com.sun.star.uno.native +{ + internal class WeakOidTypeTable + { + private readonly Dictionary _level1Map; + private readonly List<(string, Type)> _cleanupEntries; + private readonly object _lock; + + private int _count; + private int _updateOps; + + public WeakOidTypeTable() + { + _level1Map = new Dictionary(); + _cleanupEntries = new List<(string, Type)>(); + _lock = new object(); + + _count = 0; + _updateOps = 0; + } + + public object RegisterInterface(object obj, string oid, Type type) + { + lock (_lock) + { + PurgeStaleEntries(); + + if (!_level1Map.ContainsKey(oid)) + _level1Map[oid] = new Level1Entry(); + + Level1Entry level1 = _level1Map[oid]; + if (level1.TryGetCompatible(type, out Level2Entry level2)) + { + if (level2.TryGetTarget(out object o)) + { + level2.Acquire(); + return o; + } + } + + level1[type] = new Level2Entry(obj); + ++_count; + ++_updateOps; + + return obj; + } + } + + public bool RevokeInterface(string oid, Type type) + { + lock (_lock) + { + Level2Entry level2 = null; + + if (_level1Map.TryGetValue(oid, out Level1Entry level1)) + { + if (level1.TryGetCompatible(type, out level2)) + { + if (level2.Release()) + { + level1.Remove(type); + if (level1.Count == 0) + _level1Map.Remove(oid); + + --_count; + ++_updateOps; + } + } + } + + PurgeStaleEntries(); + + return level2 != null; + } + } + + public Object GetInterface(string oid, Type type) + { + lock (_lock) + { + if (_level1Map.TryGetValue(oid, out Level1Entry level1)) + if (level1.TryGetCompatible(type, out Level2Entry level2)) + if (level2.TryGetTarget(out object obj)) + return obj; + return null; + } + } + + public void Clear() + { + lock (_lock) + { + _level1Map.Clear(); + _updateOps = 0; + } + } + + private void PurgeStaleEntries() + { + if (_updateOps >= _count) + { + _updateOps = 0; + + foreach (string oid in _level1Map.Keys) + { + Level1Entry level1 = _level1Map[oid]; + foreach (Type type in level1.Keys) + { + Level2Entry level2 = level1[type]; + if (!level2.TryGetTarget(out _)) + { + _cleanupEntries.Add((oid, type)); + --_count; + } + } + } + + foreach ((string oid, Type type) in _cleanupEntries) + { + _level1Map[oid].Remove(type); + if (_level1Map[oid].Count == 0) + _level1Map.Remove(oid); + } + _cleanupEntries.Clear(); + } + } + + private class Level1Entry : Dictionary + { + public bool TryGetCompatible(Type type, out Level2Entry level2) + { + if (TryGetValue(type, out level2)) + return true; + + foreach (Type stored in Keys) + { + if (type.IsAssignableFrom(stored)) + { + level2 = this[stored]; + return true; + } + } + + level2 = null; + return false; + } + } + + private class Level2Entry + { + private readonly WeakReference _ref; + private int _count; + + public Level2Entry(object obj) + { + _ref = new WeakReference(obj); + _count = 1; + } + + public bool TryGetTarget(out object obj) => _ref.TryGetTarget(out obj); + + public void Acquire() => ++_count; + public bool Release() => --_count == 0; + } + } +} diff --git a/odk/Package_examples.mk b/odk/Package_examples.mk index 568735ec1ff7..b2f2ba90244c 100644 --- a/odk/Package_examples.mk +++ b/odk/Package_examples.mk @@ -180,6 +180,8 @@ $(eval $(call gb_Package_add_files_with_dir,odk_examples,$(SDKDIRNAME)/examples, DevelopersGuide/Extensions/DialogWithHelp/help/en/com.foocorp.foo-ext/subfolder/anotherpage.xhp \ DevelopersGuide/FirstSteps/FirstUnoContact/cxx/FirstUnoContact.cxx \ DevelopersGuide/FirstSteps/FirstUnoContact/cxx/Makefile \ + DevelopersGuide/FirstSteps/FirstUnoContact/csharp/FirstUnoContact.cs \ + DevelopersGuide/FirstSteps/FirstUnoContact/csharp/Makefile \ DevelopersGuide/FirstSteps/FirstUnoContact/java/FirstUnoContact.java \ DevelopersGuide/FirstSteps/FirstUnoContact/java/Makefile \ DevelopersGuide/FirstSteps/FirstUnoContact/java/build.xml \ diff --git a/odk/examples/DevelopersGuide/FirstSteps/FirstUnoContact/csharp/FirstUnoContact.cs b/odk/examples/DevelopersGuide/FirstSteps/FirstUnoContact/csharp/FirstUnoContact.cs new file mode 100644 index 000000000000..d2918d5b6b74 --- /dev/null +++ b/odk/examples/DevelopersGuide/FirstSteps/FirstUnoContact/csharp/FirstUnoContact.cs @@ -0,0 +1,29 @@ +/* + * 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 com.sun.star.lang; +using com.sun.star.uno; + +try +{ + XComponentContext xContext = NativeBootstrap.bootstrap(); + Console.WriteLine("Connected to a running office..."); + + XMultiComponentFactory xMCF = xContext.getServiceManager(); + Console.WriteLine("Remote service manager is {0}", xMCF is null ? "not available" : "available"); + + return 0; +} +catch (UnoException e) +{ + Console.Error.WriteLine(e.Message); + + return 1; +} diff --git a/odk/examples/DevelopersGuide/FirstSteps/FirstUnoContact/csharp/Makefile b/odk/examples/DevelopersGuide/FirstSteps/FirstUnoContact/csharp/Makefile new file mode 100644 index 000000000000..c3257605e853 --- /dev/null +++ b/odk/examples/DevelopersGuide/FirstSteps/FirstUnoContact/csharp/Makefile @@ -0,0 +1,75 @@ +# Builds the FirstUnoContact example of the Developers Guide. + +PRJ = ../../../../.. +SETTINGS = $(PRJ)/settings + +include $(SETTINGS)/settings.mk +include $(SETTINGS)/std.mk + +# Settings +APP_NAME = FirstUnoContact +APP_LANG = cs + +APP_SRC_DIR = $(subst /,$(PS),$(CURDIR)) +APP_BIN_DIR = $(subst /,$(PS),$(OUT_BIN)) +APP_MISC_DIR = $(subst /,$(PS),$(OUT_MISC)/$(APP_NAME)) + +APP_PROJ_NAME = $(APP_NAME).$(APP_LANG)proj +APP_PROJ_FILE = $(APP_MISC_DIR)/$(APP_PROJ_NAME) + +APP_EXE_NAME = $(APP_NAME)$(EXE_EXT) +APP_EXE_FILE = $(APP_BIN_DIR)/$(APP_EXE_NAME) + +DOTNET_FLAGS = -c Release +LO_NUPKG_ID = LibreOffice.Bindings +LO_NUPKG_VERSION = 0.1.0 +LO_NUPKG_DIR = $(abspath $(PRJ)/dotnet) + +# Targets +.PHONY: ALL +ALL : $(APP_NAME) + +include $(SETTINGS)/stdtarget.mk + +$(APP_PROJ_FILE) : + -$(MKDIR) $(@D) + $(ECHO) "" > $@ + $(ECHO) " " >> $@ + $(ECHO) " $(APP_NAME)" >> $@ + $(ECHO) " net8.0" >> $@ + $(ECHO) " exe" >> $@ + $(ECHO) " true" >> $@ + $(ECHO) " false" >> $@ + $(ECHO) " true" >> $@ + $(ECHO) " $(LO_NUPKG_DIR)" >> $@ + $(ECHO) " " >> $@ + $(ECHO) " " >> $@ + $(ECHO) " " >> $@ + $(ECHO) " " >> $@ + $(ECHO) " " >> $@ + $(ECHO) "" >> $@ + $(ECHOLINE) >> $@ + +$(APP_EXE_FILE) : $(APP_PROJ_FILE) + -$(MKDIR) $(@D) + $(SDK_DOTNET) publish $< $(DOTNET_FLAGS) -o $(