.NET Bindings: Native bridge for .NET

This patch includes all marshalling and proxy handling code on the
.NET side as well as the native side needed for a fully functional
UNO bridge.

It also includes some changes and corrections to net_basetypes and
netmaker needed for the bridge to work properly.

It also includes the FirstUnoContact example in C# as demonstration.

Change-Id: I406932938a4415d24408fb41ddfa7d8eeb5d1f94
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170916
Tested-by: Jenkins
Reviewed-by: Hossein <hossein@libreoffice.org>
This commit is contained in:
RMZeroFour 2024-09-03 01:59:15 +05:30 committed by Hossein
parent 074714fab8
commit c3c7b48fa9
38 changed files with 4155 additions and 68 deletions

View file

@ -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 \

View file

@ -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:

View file

@ -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 \

View file

@ -0,0 +1,234 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "net_base.hxx"
#include <string_view>
#include <unordered_map>
#include <o3tl/string_view.hxx>
#include <rtl/ustring.hxx>
#include <rtl/ustrbuf.hxx>
namespace net_uno
{
namespace
{
const std::unordered_map<std::u16string_view, std::u16string_view> s_typeNames{
{ u"System.Void", u"void" },
{ u"System.Boolean", u"boolean" },
{ u"System.Char", u"char" },
{ u"System.SByte", u"byte" },
{ u"System.Int16", u"short" },
{ u"System.UInt16", u"unsigned short" },
{ u"System.Int32", u"long" },
{ u"System.UInt32", u"unsigned long" },
{ u"System.Int64", u"hyper" },
{ u"System.UInt64", u"unsigned hyper" },
{ u"System.Single", u"float" },
{ u"System.Double", u"double" },
{ u"System.String", u"string" },
{ u"System.Type", u"type" },
{ u"com.sun.star.uno.Any", u"any" },
{ u"com.sun.star.uno.UnoException", u"com.sun.star.uno.Exception" },
{ u"com.sun.star.uno.IQueryInterface", u"com.sun.star.uno.XInterface" },
};
const std::unordered_map<OUString, typelib_TypeClass> s_typeClasses{
{ u"System.Void"_ustr, typelib_TypeClass_VOID },
{ u"System.Boolean"_ustr, typelib_TypeClass_BOOLEAN },
{ u"System.Char"_ustr, typelib_TypeClass_CHAR },
{ u"System.SByte"_ustr, typelib_TypeClass_BYTE },
{ u"System.Int16"_ustr, typelib_TypeClass_SHORT },
{ u"System.UInt16"_ustr, typelib_TypeClass_UNSIGNED_SHORT },
{ u"System.Int32"_ustr, typelib_TypeClass_LONG },
{ u"System.UInt32"_ustr, typelib_TypeClass_UNSIGNED_LONG },
{ u"System.Int64"_ustr, typelib_TypeClass_HYPER },
{ u"System.UInt64"_ustr, typelib_TypeClass_UNSIGNED_HYPER },
{ u"System.Single"_ustr, typelib_TypeClass_FLOAT },
{ u"System.Double"_ustr, typelib_TypeClass_DOUBLE },
{ u"System.String"_ustr, typelib_TypeClass_STRING },
{ u"System.Type"_ustr, typelib_TypeClass_TYPE },
{ u"com.sun.star.uno.Any"_ustr, typelib_TypeClass_ANY },
};
void map_uno_type_to_net(typelib_TypeDescriptionReference* pTDRef, OUStringBuffer& buffer)
{
switch (pTDRef->eTypeClass)
{
case typelib_TypeClass_VOID:
buffer.append(u"System.Void");
break;
case typelib_TypeClass_CHAR:
buffer.append(u"System.Char");
break;
case typelib_TypeClass_BOOLEAN:
buffer.append(u"System.Boolean");
break;
case typelib_TypeClass_BYTE:
buffer.append(u"System.SByte");
break;
case typelib_TypeClass_SHORT:
buffer.append(u"System.Int16");
break;
case typelib_TypeClass_UNSIGNED_SHORT:
buffer.append(u"System.UInt16");
break;
case typelib_TypeClass_LONG:
buffer.append(u"System.Int32");
break;
case typelib_TypeClass_UNSIGNED_LONG:
buffer.append(u"System.UInt32");
break;
case typelib_TypeClass_HYPER:
buffer.append(u"System.Int64");
break;
case typelib_TypeClass_UNSIGNED_HYPER:
buffer.append(u"System.UInt64");
break;
case typelib_TypeClass_FLOAT:
buffer.append(u"System.Single");
break;
case typelib_TypeClass_DOUBLE:
buffer.append(u"System.Double");
break;
case typelib_TypeClass_STRING:
buffer.append(u"System.String");
break;
case typelib_TypeClass_TYPE:
buffer.append(u"System.Type");
break;
case typelib_TypeClass_ANY:
buffer.append(u"com.sun.star.uno.Any");
break;
case typelib_TypeClass_ENUM:
case typelib_TypeClass_EXCEPTION:
// These have the same name on both sides
buffer.append(OUString::unacquired(&pTDRef->pTypeName));
break;
case typelib_TypeClass_STRUCT:
// These have the same name on both sides
// TODO: What about polymorphic structs? Merge this with above cases if fine
buffer.append(OUString::unacquired(&pTDRef->pTypeName));
break;
case typelib_TypeClass_INTERFACE:
{
// These have the same name on both sides
if (u"com.sun.star.uno.XInterface"_ustr.equals(pTDRef->pTypeName))
// Except XInterface, which does not exist on the .NET side
buffer.append(u"com.sun.star.uno.IQueryInterface");
else
buffer.append(OUString::unacquired(&pTDRef->pTypeName));
break;
}
case typelib_TypeClass_SEQUENCE:
{
TypeDescHolder seqType(pTDRef);
typelib_TypeDescriptionReference* pElementTDRef
= reinterpret_cast<typelib_IndirectTypeDescription*>(seqType.get())->pType;
map_uno_type_to_net(pElementTDRef, buffer);
buffer.append(u"[]");
break;
}
default:
{
throw BridgeRuntimeError(SAL_WHERE, "could not map given type info "
+ OUString::unacquired(&pTDRef->pTypeName));
}
}
}
void map_net_type_to_uno(std::u16string_view sTypeName, OUStringBuffer& buffer)
{
size_t bracketsStart = sTypeName.find_last_not_of(u"[]");
bool isSequence = bracketsStart != std::u16string_view::npos;
std::u16string_view sBrackets = isSequence ? sTypeName.substr(bracketsStart + 1) : u"";
std::u16string_view sFullName = isSequence ? sTypeName.substr(0, bracketsStart + 1) : sTypeName;
size_t genericsStart = sFullName.find_first_of(u'<');
size_t genericsEnd = sFullName.find_last_of(u'>');
bool hasGenerics = genericsStart != std::u16string_view::npos;
std::u16string_view sGenericParams
= hasGenerics ? sFullName.substr(genericsStart + 1, genericsEnd - genericsStart - 1) : u"";
std::u16string_view sBaseName = hasGenerics ? sFullName.substr(0, genericsStart) : sFullName;
// Sequence brackets go at the beginning of UNO name
for (size_t i = 0; i < sBrackets.size() / 2; i++)
buffer.append(u"[]");
// Builtin types
auto it = s_typeNames.find(sBaseName);
if (it != s_typeNames.end())
{
buffer.append(it->second);
}
else
{
buffer.append(sBaseName);
if (hasGenerics)
{
buffer.append(u'<');
for (size_t i = 0; i != std::string_view::npos;)
{
std::u16string_view genericParam(o3tl::getToken(sGenericParams, u',', i));
map_net_type_to_uno(genericParam, buffer);
if (i != std::string_view::npos)
buffer.append(u',');
}
buffer.append(u'>');
}
}
}
}
OUString map_uno_type_to_net(typelib_TypeDescriptionReference* pTDRef)
{
OUStringBuffer buffer;
map_uno_type_to_net(pTDRef, buffer);
return buffer.makeStringAndClear();
}
typelib_TypeDescriptionReference* map_net_type_to_uno(const OUString& sTypeName)
{
typelib_TypeDescriptionReference* retVal = nullptr;
// Simple types
auto it = s_typeClasses.find(sTypeName);
if (it != s_typeClasses.end())
{
retVal = *typelib_static_type_getByTypeClass(it->second);
typelib_typedescriptionreference_acquire(retVal);
return retVal;
}
// Complex types (structs, interfaces, sequences)
OUStringBuffer buffer;
map_net_type_to_uno(sTypeName, buffer);
OUString convertedName = buffer.makeStringAndClear();
typelib_TypeDescription* pTD = nullptr;
typelib_typedescription_getByName(&pTD, convertedName.pData);
if (pTD)
{
retVal = pTD->pWeakRef;
typelib_typedescriptionreference_acquire(retVal);
typelib_typedescription_release(pTD);
return retVal;
}
throw BridgeRuntimeError(SAL_WHERE, "could not map given type name " + sTypeName);
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View file

@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <rtl/ustring.hxx>
#include <typelib/typedescription.hxx>
#include <sal/log.hxx>
namespace net_uno
{
OUString map_uno_type_to_net(typelib_TypeDescriptionReference* pTDRef);
typelib_TypeDescriptionReference* map_net_type_to_uno(const OUString& sTypeName);
struct BridgeRuntimeError
{
OUString m_location;
OUString m_message;
explicit BridgeRuntimeError(const char* location, const OUString& message)
: m_location(OUString::createFromAscii(location))
, m_message(message)
{
}
};
class TypeDescHolder
{
public:
TypeDescHolder(const TypeDescHolder&) = delete;
TypeDescHolder& operator=(const TypeDescHolder&) = delete;
explicit TypeDescHolder(typelib_TypeDescriptionReference* td_ref)
: m_td(nullptr)
{
TYPELIB_DANGER_GET(&m_td, td_ref);
if (!m_td)
{
throw BridgeRuntimeError(SAL_WHERE, "could not get type description for "
+ OUString::unacquired(&td_ref->pTypeName));
}
}
~TypeDescHolder() { TYPELIB_DANGER_RELEASE(m_td); }
typelib_TypeDescription* get() const { return m_td; }
private:
typelib_TypeDescription* m_td;
};
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View file

@ -0,0 +1,292 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "net_bridge.hxx"
#include <bridges/net_uno/net_context.hxx>
#include <sal/log.hxx>
namespace net_uno
{
namespace
{
void SAL_CALL Mapping_acquire(uno_Mapping* mapping) SAL_THROW_EXTERN_C()
{
Mapping* that = static_cast<Mapping*>(mapping);
that->m_bridge->acquire();
}
void SAL_CALL Mapping_release(uno_Mapping* mapping) SAL_THROW_EXTERN_C()
{
Mapping* that = static_cast<Mapping*>(mapping);
that->m_bridge->release();
}
void SAL_CALL Mapping_free(uno_Mapping* mapping) SAL_THROW_EXTERN_C()
{
Mapping* that = static_cast<Mapping*>(mapping);
delete that->m_bridge;
}
void SAL_CALL Mapping_net2uno(uno_Mapping* mapping, void** ppOut, void* pIn,
typelib_InterfaceTypeDescription* pTD) SAL_THROW_EXTERN_C()
{
assert(ppOut && pTD && "### null ptr!");
Mapping* that = static_cast<Mapping*>(mapping);
Bridge* bridge = that->m_bridge;
if (pIn)
{
Value interfaceValue;
interfaceValue.interfaceData = pIn;
bridge->map_net_value_to_uno(*ppOut, &interfaceValue, pTD->aBase.pWeakRef, true, true);
}
}
void SAL_CALL Mapping_uno2net(uno_Mapping* mapping, void** ppOut, void* pIn,
typelib_InterfaceTypeDescription* pTD) SAL_THROW_EXTERN_C()
{
assert(ppOut && pTD && "### null ptr!");
Mapping* that = static_cast<Mapping*>(mapping);
Bridge* bridge = that->m_bridge;
if (pIn)
{
Value interfaceValue;
bridge->map_uno_to_net_value(&pIn, &interfaceValue, pTD->aBase.pWeakRef, false);
*ppOut = interfaceValue.interfaceData;
}
}
}
Bridge::Bridge(uno_Environment* uno_net_env, uno_ExtEnvironment* uno_env, bool registered_net2uno)
: m_ref(1)
, m_uno_env(uno_env)
, m_net_env(uno_net_env)
, m_registered_net2uno(registered_net2uno)
{
assert(m_net_env && m_uno_env);
(*m_uno_env->aBase.acquire)(&m_uno_env->aBase);
(*m_net_env->acquire)(m_net_env);
// net2uno
m_net2uno.acquire = Mapping_acquire;
m_net2uno.release = Mapping_release;
m_net2uno.mapInterface = Mapping_net2uno;
m_net2uno.m_bridge = this;
// uno2net
m_uno2net.acquire = Mapping_acquire;
m_uno2net.release = Mapping_release;
m_uno2net.mapInterface = Mapping_uno2net;
m_uno2net.m_bridge = this;
}
Bridge::~Bridge()
{
(*m_net_env->release)(m_net_env);
(*m_uno_env->aBase.release)(&m_uno_env->aBase);
}
void Bridge::acquire()
{
if (osl_atomic_increment(&m_ref) == 1)
{
if (m_registered_net2uno)
{
uno_Mapping* mapping = &m_net2uno;
uno_registerMapping(&mapping, Mapping_free, m_net_env, &m_uno_env->aBase, nullptr);
}
else
{
uno_Mapping* mapping = &m_uno2net;
uno_registerMapping(&mapping, Mapping_free, &m_uno_env->aBase, m_net_env, nullptr);
}
}
}
void Bridge::release()
{
if (osl_atomic_decrement(&m_ref) == 0)
{
uno_revokeMapping(m_registered_net2uno ? &m_net2uno : &m_uno2net);
}
}
extern "C" {
static void net_env_disposing(uno_Environment* env)
{
// TODO: more rigorous lifetime control
// TODO: invoke some dispose routine on .NET side
delete static_cast<Context*>(env->pContext);
}
SAL_DLLPUBLIC_EXPORT void uno_initEnvironment(uno_Environment* net_env) SAL_THROW_EXTERN_C()
{
// The code creating the uno_Environment needs to initialize
// pContext with a Context object, complete with all callbacks.
assert(net_env->pContext);
net_env->pExtEnv = nullptr;
net_env->environmentDisposing = net_env_disposing;
}
SAL_DLLPUBLIC_EXPORT void uno_ext_getMapping(uno_Mapping** ppMapping, uno_Environment* pFrom,
uno_Environment* pTo) SAL_THROW_EXTERN_C()
{
assert(ppMapping && pFrom && pTo);
if (*ppMapping)
{
(*(*ppMapping)->release)(*ppMapping);
*ppMapping = nullptr;
}
const OUString& from_env_typename = OUString::unacquired(&pFrom->pTypeName);
const OUString& to_env_typename = OUString::unacquired(&pTo->pTypeName);
uno_Mapping* mapping = nullptr;
if (from_env_typename == UNO_LB_NET && to_env_typename == UNO_LB_UNO)
{
Bridge* bridge = new Bridge(pFrom, pTo->pExtEnv, true);
mapping = &bridge->m_net2uno;
uno_registerMapping(&mapping, Mapping_free, pFrom, &pTo->pExtEnv->aBase, nullptr);
}
else if (from_env_typename == UNO_LB_UNO && to_env_typename == UNO_LB_NET)
{
Bridge* bridge = new Bridge(pTo, pFrom->pExtEnv, false);
mapping = &bridge->m_uno2net;
uno_registerMapping(&mapping, Mapping_free, &pFrom->pExtEnv->aBase, pTo, nullptr);
}
*ppMapping = mapping;
}
SAL_DLLPUBLIC_EXPORT IntPtr allocateMemory(int nBytes) { return std::malloc(nBytes); }
SAL_DLLPUBLIC_EXPORT void freeMemory(IntPtr pMemory) { std::free(pMemory); }
SAL_DLLPUBLIC_EXPORT void releaseProxy(IntPtr pBridge, IntPtr pUnoInterface, IntPtr pTypeDesc)
{
Bridge* bridge = static_cast<Bridge*>(pBridge);
uno_Interface* pUnoI = static_cast<uno_Interface*>(pUnoInterface);
typelib_TypeDescription* pTD = static_cast<typelib_TypeDescription*>(pTypeDesc);
// Revoke from UNO; already revoked from .NET
(*bridge->m_uno_env->revokeInterface)(bridge->m_uno_env, pUnoI);
(*pUnoI->release)(pUnoI);
typelib_typedescription_release(pTD);
bridge->release();
}
SAL_DLLPUBLIC_EXPORT sal_Bool dispatchCall(IntPtr pBridge, IntPtr pUnoInterface, IntPtr pTypeDesc,
String sFunctionName, Value* pArgs, Value* pRet,
Value* pExc)
{
Bridge* bridge = static_cast<Bridge*>(pBridge);
Context* context = static_cast<Context*>(bridge->m_net_env->pContext);
OUString sMethodName(sFunctionName);
try
{
uno_Interface* pUnoI = static_cast<uno_Interface*>(pUnoInterface);
typelib_InterfaceTypeDescription* pTD
= static_cast<typelib_InterfaceTypeDescription*>(pTypeDesc);
for (sal_Int32 i = 0; i < pTD->nAllMembers; ++i)
{
// Try to avoid getting typedescription as long as possible because
// of Mutex.acquire() in typelib_typedescriptionreference_getDescription()
typelib_TypeDescriptionReference* memberType = pTD->ppAllMembers[i];
// Check method_name against fully qualified typeName of memberType
// typeName is of the form <name> "::" <method_name> *(":@" <idx> "," <idx> ":" <name>)
const OUString& typeName = OUString::unacquired(&memberType->pTypeName);
sal_Int32 offset = typeName.indexOf(':') + 2;
sal_Int32 remainder = typeName.getLength() - offset;
assert(offset >= 2 && offset < typeName.getLength() && typeName[offset - 1] == ':');
switch (memberType->eTypeClass)
{
case typelib_TypeClass_INTERFACE_METHOD:
if ((sMethodName.getLength() == remainder
|| (sMethodName.getLength() < remainder
&& typeName[offset + sMethodName.getLength()] == ':'))
&& typeName.match(sMethodName, offset))
{
TypeDescHolder memberTD(memberType);
typelib_InterfaceMethodTypeDescription* pMethodTD
= reinterpret_cast<typelib_InterfaceMethodTypeDescription*>(
memberTD.get());
return bridge->call_uno_func(pUnoI, memberTD.get(),
pMethodTD->pReturnTypeRef, pMethodTD->nParams,
pMethodTD->pParams, pArgs, pRet, pExc);
}
break;
case typelib_TypeClass_INTERFACE_ATTRIBUTE:
if (sMethodName.getLength() > 4
&& (sMethodName.getLength() - 4 == remainder
|| (sMethodName.getLength() - 4 < remainder
&& typeName[offset + (sMethodName.getLength() - 4)] == ':'))
&& sMethodName[1] == 'e' && sMethodName[2] == 't'
&& rtl_ustr_compare_WithLength(
typeName.getStr() + offset, sMethodName.getLength() - 4,
sMethodName.getStr() + 4, sMethodName.getLength() - 4)
== 0)
{
TypeDescHolder memberTD(memberType);
typelib_InterfaceAttributeTypeDescription* pAttribTD
= reinterpret_cast<typelib_InterfaceAttributeTypeDescription*>(
memberTD.get());
if (sMethodName[0] == 'g')
{
return bridge->call_uno_func(pUnoI, memberTD.get(),
pAttribTD->pAttributeTypeRef, 0, nullptr,
pArgs, pRet, pExc);
}
else if (sMethodName[0] == 's' && !pAttribTD->bReadOnly)
{
typelib_MethodParameter param;
param.pTypeRef = pAttribTD->pAttributeTypeRef;
param.bIn = true;
param.bOut = false;
return bridge->call_uno_func(pUnoI, memberTD.get(),
pAttribTD->pAttributeTypeRef, 1, &param,
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: */

View file

@ -0,0 +1,59 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "net_base.hxx"
#include <bridges/net_uno/net_types.hxx>
#include <uno/dispatcher.hxx>
#include <uno/environment.hxx>
#include <uno/mapping.hxx>
namespace net_uno
{
struct Bridge;
struct Mapping : public uno_Mapping
{
Bridge* m_bridge;
};
struct Bridge
{
mutable oslInterlockedCount m_ref;
uno_ExtEnvironment* m_uno_env;
uno_Environment* m_net_env;
Mapping m_net2uno;
Mapping m_uno2net;
bool m_registered_net2uno;
Bridge(uno_Environment* net_env, uno_ExtEnvironment* uno_env, bool registered_net2uno);
~Bridge();
void acquire();
void release();
void map_uno_to_net_value(void* pUnoData, Value* pValue,
typelib_TypeDescriptionReference* pTDRef, bool destructValue);
void map_net_value_to_uno(void* pUnoData, Value* pValue,
typelib_TypeDescriptionReference* pTDRef, bool destructObject,
bool assignObject);
bool call_uno_func(uno_Interface* pUnoI, const typelib_TypeDescription* pMethodTD,
typelib_TypeDescriptionReference* pReturnTDRef, int nParams,
typelib_MethodParameter* pParams, Value* pArgs, Value* pRet, Value* pExc);
void call_net_func(IntPtr pNetI, const typelib_TypeDescription* pMethodTD,
typelib_TypeDescriptionReference* pReturnTDRef, int nParams,
typelib_MethodParameter* pParams, void** pArgs, void* pRet, uno_Any** pExc);
};
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View file

@ -0,0 +1,855 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "net_bridge.hxx"
#include "net_proxy.hxx"
#include <cstring>
#include <unordered_map>
#include <vector>
#include <bridges/net_uno/net_context.hxx>
namespace net_uno
{
namespace
{
size_t net_sizeof(typelib_TypeClass eTypeClass)
{
static const std::unordered_map<typelib_TypeClass, size_t> s_sizes{
{ typelib_TypeClass_BOOLEAN, sizeof(sal_Bool) },
{ typelib_TypeClass_BYTE, sizeof(sal_Int8) },
{ typelib_TypeClass_CHAR, sizeof(sal_Unicode) },
{ typelib_TypeClass_SHORT, sizeof(sal_Int16) },
{ typelib_TypeClass_UNSIGNED_SHORT, sizeof(sal_uInt16) },
{ typelib_TypeClass_LONG, sizeof(sal_Int32) },
{ typelib_TypeClass_UNSIGNED_LONG, sizeof(sal_uInt32) },
{ typelib_TypeClass_HYPER, sizeof(sal_Int64) },
{ typelib_TypeClass_UNSIGNED_HYPER, sizeof(sal_uInt64) },
{ typelib_TypeClass_FLOAT, sizeof(float) },
{ typelib_TypeClass_DOUBLE, sizeof(double) },
{ typelib_TypeClass_ENUM, sizeof(sal_Int32) },
{ typelib_TypeClass_STRING, sizeof(IntPtr) },
{ typelib_TypeClass_TYPE, sizeof(IntPtr) },
{ typelib_TypeClass_ANY, sizeof(IntPtr) + sizeof(IntPtr) },
{ typelib_TypeClass_SEQUENCE, sizeof(IntPtr) + sizeof(sal_Int32) },
{ typelib_TypeClass_INTERFACE, sizeof(IntPtr) },
{ typelib_TypeClass_EXCEPTION, sizeof(IntPtr) },
{ typelib_TypeClass_STRUCT, sizeof(IntPtr) },
};
return s_sizes.at(eTypeClass);
}
IntPtr alloc_net_string(const OUString& str)
{
const sal_Unicode* src = str.getStr();
sal_Int32 len = str.getLength();
void* dst = std::malloc((len + 1) * sizeof(sal_Unicode));
std::memcpy(dst, src, len * sizeof(sal_Unicode));
static_cast<sal_Unicode*>(dst)[len] = u'\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<uno_Sequence*>(mem);
seq->nRefCount = 1;
seq->nElements = nElements;
if (data)
std::memcpy(seq->elements, data, nElements * nElementSize);
return seq;
}
void marshal_data(void* pUnoData, void* pNetData, typelib_TypeDescriptionReference* pTDRef,
bool bDestructValue, Bridge& bridge)
{
switch (pTDRef->eTypeClass)
{
case typelib_TypeClass_BOOLEAN:
case typelib_TypeClass_BYTE:
case typelib_TypeClass_CHAR:
case typelib_TypeClass_SHORT:
case typelib_TypeClass_UNSIGNED_SHORT:
case typelib_TypeClass_LONG:
case typelib_TypeClass_UNSIGNED_LONG:
case typelib_TypeClass_HYPER:
case typelib_TypeClass_UNSIGNED_HYPER:
case typelib_TypeClass_FLOAT:
case typelib_TypeClass_DOUBLE:
case typelib_TypeClass_ENUM:
std::memcpy(pNetData, pUnoData, net_sizeof(pTDRef->eTypeClass));
break;
case typelib_TypeClass_STRING:
{
IntPtr* ppNetStr = static_cast<IntPtr*>(pNetData);
rtl_uString* pUnoStr = *static_cast<rtl_uString**>(pUnoData);
if (bDestructValue && pNetData)
std::free(pNetData);
*ppNetStr = alloc_net_string(OUString::unacquired(&pUnoStr));
break;
}
case typelib_TypeClass_TYPE:
{
IntPtr* ppNetType = static_cast<IntPtr*>(pNetData);
typelib_TypeDescriptionReference* pUnoType
= *static_cast<typelib_TypeDescriptionReference**>(pUnoData);
if (bDestructValue && pNetData)
std::free(pNetData);
*ppNetType = alloc_net_string(map_uno_type_to_net(pUnoType));
break;
}
case typelib_TypeClass_ANY:
{
Value::Any* ppNetAny = static_cast<Value::Any*>(pNetData);
uno_Any* pUnoAny = static_cast<uno_Any*>(pUnoData);
if (bDestructValue && ppNetAny->type)
std::free(ppNetAny->type);
if (bDestructValue && ppNetAny->data)
std::free(ppNetAny->data);
ppNetAny->type = alloc_net_string(map_uno_type_to_net(pUnoAny->pType));
switch (pUnoAny->pType->eTypeClass)
{
case typelib_TypeClass_VOID:
ppNetAny->data = nullptr;
break;
case typelib_TypeClass_BOOLEAN:
case typelib_TypeClass_BYTE:
case typelib_TypeClass_CHAR:
case typelib_TypeClass_SHORT:
case typelib_TypeClass_UNSIGNED_SHORT:
case typelib_TypeClass_LONG:
case typelib_TypeClass_UNSIGNED_LONG:
case typelib_TypeClass_FLOAT:
case typelib_TypeClass_ENUM:
std::memcpy(&ppNetAny->data, pUnoAny->pData, sizeof(IntPtr));
break;
case typelib_TypeClass_HYPER:
case typelib_TypeClass_UNSIGNED_HYPER:
case typelib_TypeClass_DOUBLE:
{
size_t size = net_sizeof(pUnoAny->pType->eTypeClass);
if (size <= sizeof(IntPtr))
{
std::memcpy(&ppNetAny->data, pUnoAny->pData, sizeof(IntPtr));
}
else
{
IntPtr mem = std::malloc(size);
std::memcpy(mem, pUnoAny->pData, size);
std::free(ppNetAny->data);
ppNetAny->data = mem;
}
break;
}
case typelib_TypeClass_ANY:
case typelib_TypeClass_SEQUENCE:
{
IntPtr mem = std::malloc(net_sizeof(pUnoAny->pType->eTypeClass));
marshal_data(pUnoAny->pData, mem, pUnoAny->pType, bDestructValue, bridge);
std::free(ppNetAny->data);
ppNetAny->data = mem;
break;
}
case typelib_TypeClass_STRING:
case typelib_TypeClass_TYPE:
case typelib_TypeClass_INTERFACE:
case typelib_TypeClass_EXCEPTION:
case typelib_TypeClass_STRUCT:
marshal_data(pUnoAny->pData, &ppNetAny->data, pUnoAny->pType, bDestructValue,
bridge);
break;
default:
{
throw BridgeRuntimeError(SAL_WHERE,
"could not map "
+ OUString::unacquired(&pUnoAny->pType->pTypeName)
+ " out of an UNO any");
}
}
break;
}
case typelib_TypeClass_SEQUENCE:
{
Value::Sequence* ppNetSeq = static_cast<Value::Sequence*>(pNetData);
uno_Sequence* pUnoSeq = *static_cast<uno_Sequence**>(pUnoData);
if (bDestructValue && ppNetSeq->data)
std::free(ppNetSeq->data);
ppNetSeq->length = pUnoSeq->nElements;
TypeDescHolder type(pTDRef);
typelib_TypeDescriptionReference* pElemTDRef
= reinterpret_cast<typelib_IndirectTypeDescription*>(type.get())->pType;
size_t nNetElemSize = net_sizeof(pElemTDRef->eTypeClass);
switch (pElemTDRef->eTypeClass)
{
case typelib_TypeClass_BOOLEAN:
case typelib_TypeClass_BYTE:
case typelib_TypeClass_CHAR:
case typelib_TypeClass_SHORT:
case typelib_TypeClass_UNSIGNED_SHORT:
case typelib_TypeClass_LONG:
case typelib_TypeClass_UNSIGNED_LONG:
case typelib_TypeClass_HYPER:
case typelib_TypeClass_UNSIGNED_HYPER:
case typelib_TypeClass_FLOAT:
case typelib_TypeClass_DOUBLE:
case typelib_TypeClass_ENUM:
{
ppNetSeq->data = std::malloc(nNetElemSize * ppNetSeq->length);
std::memcpy(ppNetSeq->data, pUnoSeq->elements, nNetElemSize * ppNetSeq->length);
break;
}
case typelib_TypeClass_ANY:
case typelib_TypeClass_SEQUENCE:
case typelib_TypeClass_STRING:
case typelib_TypeClass_TYPE:
case typelib_TypeClass_INTERFACE:
case typelib_TypeClass_EXCEPTION:
case typelib_TypeClass_STRUCT:
{
TypeDescHolder elemType(pElemTDRef);
sal_Int32 nUnoElemSize = elemType.get()->nSize;
ppNetSeq->data = std::malloc(nUnoElemSize * ppNetSeq->length);
// Convert each element
for (int nPos = 0; nPos < ppNetSeq->length; ++nPos)
{
void* pNetElem = static_cast<char*>(ppNetSeq->data) + (nPos * nNetElemSize);
void* pUnoElem = pUnoSeq->elements + (nPos * nUnoElemSize);
marshal_data(pUnoElem, pNetElem, pElemTDRef, bDestructValue, bridge);
}
break;
}
default:
{
throw BridgeRuntimeError(
SAL_WHERE, "could not map " + OUString::unacquired(&pElemTDRef->pTypeName)
+ " out of an UNO sequence");
}
}
break;
}
case typelib_TypeClass_INTERFACE:
{
IntPtr* ppNetI = static_cast<IntPtr*>(pNetData);
uno_Interface* pUnoI = *static_cast<uno_Interface**>(pUnoData);
if (pUnoI)
{
Context* pCtx = static_cast<Context*>(bridge.m_net_env->pContext);
// Get oid and type name
rtl_uString* pOid = nullptr;
(*bridge.m_uno_env->getObjectIdentifier)(bridge.m_uno_env, &pOid, pUnoI);
const OUString& sOid = OUString::unacquired(&pOid);
OUString sTypeName = map_uno_type_to_net(pTDRef);
// Get the proxy if already created, else create new
*ppNetI = pCtx->lookupInterface(sOid.getStr(), sTypeName.getStr());
if (!*ppNetI)
{
TypeDescHolder type(pTDRef);
typelib_InterfaceTypeDescription* pTD
= reinterpret_cast<typelib_InterfaceTypeDescription*>(type.get());
// TODO: check whether liftime control is correct
bridge.acquire();
(*pUnoI->acquire)(pUnoI);
typelib_typedescription_acquire(&pTD->aBase);
(*bridge.m_uno_env->registerInterface)(
bridge.m_uno_env, reinterpret_cast<void**>(&pUnoI), pOid, pTD);
*ppNetI
= pCtx->createProxy(sOid.getStr(), sTypeName.getStr(), &bridge, pUnoI, pTD);
}
}
else
{
*ppNetI = nullptr;
}
break;
}
case typelib_TypeClass_EXCEPTION:
case typelib_TypeClass_STRUCT:
{
IntPtr* ppNetStruct = static_cast<IntPtr*>(pNetData);
void* pUnoStruct = pUnoData;
if (bDestructValue && *ppNetStruct)
std::free(*ppNetStruct);
TypeDescHolder type(pTDRef);
typelib_CompoundTypeDescription* pCompTD
= reinterpret_cast<typelib_CompoundTypeDescription*>(type.get());
if (!type.get()->bComplete)
typelib_typedescription_complete(
reinterpret_cast<typelib_TypeDescription**>(&pCompTD));
size_t nBytes = 0;
std::vector<std::pair<typelib_TypeDescriptionReference*, sal_Int32>> vecMembers;
for (typelib_CompoundTypeDescription* pHierTD = pCompTD; pHierTD;
pHierTD = pHierTD->pBaseTypeDescription)
{
for (int n = pHierTD->nMembers - 1; n >= 0; n--)
{
vecMembers.emplace_back(pHierTD->ppTypeRefs[n], pHierTD->pMemberOffsets[n]);
nBytes += net_sizeof(pHierTD->ppTypeRefs[n]->eTypeClass);
}
}
*ppNetStruct = std::malloc(nBytes);
size_t nNetOffset = 0;
int nPos = vecMembers.size() - 1;
for (; nPos >= 0; nPos--)
{
auto[pMemberTDRef, nUnoOffset] = vecMembers[nPos];
void* pUnoField = static_cast<char*>(pUnoStruct) + nUnoOffset;
void* pNetField = static_cast<char*>(*ppNetStruct) + nNetOffset;
switch (pMemberTDRef->eTypeClass)
{
case typelib_TypeClass_BOOLEAN:
case typelib_TypeClass_BYTE:
case typelib_TypeClass_CHAR:
case typelib_TypeClass_SHORT:
case typelib_TypeClass_UNSIGNED_SHORT:
case typelib_TypeClass_LONG:
case typelib_TypeClass_UNSIGNED_LONG:
case typelib_TypeClass_HYPER:
case typelib_TypeClass_UNSIGNED_HYPER:
case typelib_TypeClass_FLOAT:
case typelib_TypeClass_DOUBLE:
case typelib_TypeClass_ENUM:
std::memcpy(pNetField, pUnoField, net_sizeof(pMemberTDRef->eTypeClass));
break;
case typelib_TypeClass_ANY:
case typelib_TypeClass_SEQUENCE:
case typelib_TypeClass_STRING:
case typelib_TypeClass_TYPE:
case typelib_TypeClass_INTERFACE:
case typelib_TypeClass_EXCEPTION:
case typelib_TypeClass_STRUCT:
marshal_data(pUnoField, pNetField, pMemberTDRef, bDestructValue, bridge);
break;
default:
{
throw BridgeRuntimeError(
SAL_WHERE,
"could not map " + OUString::unacquired(&pMemberTDRef->pTypeName)
+ " out of an UNO " + OUString::unacquired(&pTDRef->pTypeName));
}
}
nNetOffset += net_sizeof(pMemberTDRef->eTypeClass);
}
break;
}
default:
{
throw BridgeRuntimeError(SAL_WHERE, "could not map "
+ OUString::unacquired(&pTDRef->pTypeName)
+ " value to .NET");
}
}
}
void unmarshal_data(void* pUnoData, void* pNetData, typelib_TypeDescriptionReference* pTDRef,
bool bDestructObject, bool bAssignData, Bridge& bridge)
{
switch (pTDRef->eTypeClass)
{
case typelib_TypeClass_BOOLEAN:
case typelib_TypeClass_BYTE:
case typelib_TypeClass_CHAR:
case typelib_TypeClass_SHORT:
case typelib_TypeClass_UNSIGNED_SHORT:
case typelib_TypeClass_LONG:
case typelib_TypeClass_UNSIGNED_LONG:
case typelib_TypeClass_HYPER:
case typelib_TypeClass_UNSIGNED_HYPER:
case typelib_TypeClass_FLOAT:
case typelib_TypeClass_DOUBLE:
case typelib_TypeClass_ENUM:
if (bAssignData)
std::memcpy(pUnoData, pNetData, net_sizeof(pTDRef->eTypeClass));
break;
case typelib_TypeClass_STRING:
{
rtl_uString** ppUnoStr = static_cast<rtl_uString**>(pUnoData);
IntPtr pNetStr = *static_cast<IntPtr*>(pNetData);
if (bDestructObject && ppUnoStr)
rtl_uString_release(*ppUnoStr);
if (bAssignData)
{
*ppUnoStr = nullptr;
if (pNetStr)
rtl_uString_newFromStr(ppUnoStr, static_cast<String>(pNetStr));
else
rtl_uString_new(ppUnoStr);
}
std::free(pNetStr);
break;
}
case typelib_TypeClass_TYPE:
{
typelib_TypeDescriptionReference** ppUnoType
= static_cast<typelib_TypeDescriptionReference**>(pUnoData);
IntPtr pNetType = *static_cast<IntPtr*>(pNetData);
if (bDestructObject && ppUnoType)
typelib_typedescriptionreference_release(*ppUnoType);
if (bAssignData)
*ppUnoType = map_net_type_to_uno(OUString(static_cast<String>(pNetType)));
std::free(pNetType);
break;
}
case typelib_TypeClass_ANY:
{
uno_Any* pUnoAny = static_cast<uno_Any*>(pUnoData);
Value::Any* pNetAny = static_cast<Value::Any*>(pNetData);
if (bDestructObject && pUnoData)
uno_any_destruct(pUnoAny, nullptr);
typelib_TypeDescriptionReference* pValueTDRef
= map_net_type_to_uno(OUString(static_cast<String>(pNetAny->type)));
std::free(pNetAny->type);
if (bAssignData)
{
switch (pValueTDRef->eTypeClass)
{
case typelib_TypeClass_VOID:
{
pUnoAny->pType = pValueTDRef;
pUnoAny->pData = &pUnoAny->pReserved;
break;
}
case typelib_TypeClass_BOOLEAN:
case typelib_TypeClass_BYTE:
case typelib_TypeClass_CHAR:
case typelib_TypeClass_SHORT:
case typelib_TypeClass_UNSIGNED_SHORT:
case typelib_TypeClass_LONG:
case typelib_TypeClass_UNSIGNED_LONG:
case typelib_TypeClass_FLOAT:
case typelib_TypeClass_ENUM:
{
pUnoAny->pType = pValueTDRef;
pUnoAny->pData = &pUnoAny->pReserved;
std::memcpy(pUnoAny->pData, &pNetAny->data, sizeof(IntPtr));
break;
}
case typelib_TypeClass_HYPER:
case typelib_TypeClass_UNSIGNED_HYPER:
case typelib_TypeClass_DOUBLE:
{
pUnoAny->pType = pValueTDRef;
size_t size = net_sizeof(pValueTDRef->eTypeClass);
if (size <= sizeof(IntPtr))
{
pUnoAny->pData = &pUnoAny->pReserved;
std::memcpy(pUnoAny->pData, &pNetAny->data, sizeof(IntPtr));
}
else
{
void* mem = std::malloc(size);
std::memcpy(mem, pNetAny->data, size);
pUnoAny->pData = mem;
std::free(pNetAny->data);
}
break;
}
case typelib_TypeClass_STRING:
case typelib_TypeClass_TYPE:
case typelib_TypeClass_INTERFACE:
{
if (pNetAny->data)
{
pUnoAny->pType = pValueTDRef;
pUnoAny->pData = &pUnoAny->pReserved;
pUnoAny->pReserved = nullptr;
unmarshal_data(pUnoAny->pData, &pNetAny->data, pValueTDRef,
bDestructObject, true, bridge);
}
else
{
uno_any_construct(pUnoAny, nullptr, nullptr, nullptr);
}
break;
}
case typelib_TypeClass_ANY:
case typelib_TypeClass_SEQUENCE:
{
if (pNetAny->data)
{
pUnoAny->pType = pValueTDRef;
pUnoAny->pData = &pUnoAny->pReserved;
pUnoAny->pReserved = nullptr;
unmarshal_data(pUnoAny->pData, &pNetAny->data, pValueTDRef,
bDestructObject, true, bridge);
}
else
{
uno_any_construct(pUnoAny, nullptr, nullptr, nullptr);
}
break;
}
case typelib_TypeClass_STRUCT:
case typelib_TypeClass_EXCEPTION:
{
if (pNetAny->data)
{
pUnoAny->pType = pValueTDRef;
TypeDescHolder valueType(pValueTDRef);
void* mem = std::malloc(valueType.get()->nSize);
unmarshal_data(mem, &pNetAny->data, pValueTDRef, bDestructObject, true,
bridge);
pUnoAny->pData = mem;
}
else
{
uno_any_construct(pUnoAny, nullptr, nullptr, nullptr);
}
break;
}
default:
{
throw BridgeRuntimeError(SAL_WHERE,
"could not map "
+ OUString::unacquired(&pValueTDRef->pTypeName)
+ " into an UNO any");
}
}
}
else
{
switch (pValueTDRef->eTypeClass)
{
case typelib_TypeClass_VOID:
case typelib_TypeClass_BOOLEAN:
case typelib_TypeClass_BYTE:
case typelib_TypeClass_CHAR:
case typelib_TypeClass_SHORT:
case typelib_TypeClass_UNSIGNED_SHORT:
case typelib_TypeClass_LONG:
case typelib_TypeClass_UNSIGNED_LONG:
case typelib_TypeClass_FLOAT:
case typelib_TypeClass_ENUM:
break;
case typelib_TypeClass_HYPER:
case typelib_TypeClass_UNSIGNED_HYPER:
case typelib_TypeClass_DOUBLE:
{
size_t size = net_sizeof(pValueTDRef->eTypeClass);
if (pNetAny->data && size > sizeof(IntPtr))
std::free(pNetAny->data);
break;
}
case typelib_TypeClass_ANY:
case typelib_TypeClass_SEQUENCE:
{
if (pNetAny->data)
{
unmarshal_data(pUnoAny->pData, &pNetAny->data, pValueTDRef,
bDestructObject, false, bridge);
std::free(pNetAny->data);
}
break;
}
case typelib_TypeClass_STRING:
case typelib_TypeClass_TYPE:
case typelib_TypeClass_INTERFACE:
case typelib_TypeClass_STRUCT:
case typelib_TypeClass_EXCEPTION:
{
if (pNetAny->data)
{
unmarshal_data(pUnoAny->pData, &pNetAny->data, pValueTDRef,
bDestructObject, false, bridge);
}
break;
}
default:
{
throw BridgeRuntimeError(SAL_WHERE,
"could not map "
+ OUString::unacquired(&pValueTDRef->pTypeName)
+ " into an UNO any");
}
}
}
break;
}
case typelib_TypeClass_SEQUENCE:
{
uno_Sequence** ppUnoSeq = static_cast<uno_Sequence**>(pUnoData);
Value::Sequence* pNetSeq = static_cast<Value::Sequence*>(pNetData);
TypeDescHolder type(pTDRef);
if (bDestructObject && ppUnoSeq)
uno_destructData(ppUnoSeq, type.get(), nullptr);
typelib_TypeDescriptionReference* pElemTDRef
= reinterpret_cast<typelib_IndirectTypeDescription*>(type.get())->pType;
size_t nNetElemSize = net_sizeof(pElemTDRef->eTypeClass);
switch (pElemTDRef->eTypeClass)
{
case typelib_TypeClass_BOOLEAN:
case typelib_TypeClass_BYTE:
case typelib_TypeClass_CHAR:
case typelib_TypeClass_SHORT:
case typelib_TypeClass_UNSIGNED_SHORT:
case typelib_TypeClass_LONG:
case typelib_TypeClass_UNSIGNED_LONG:
case typelib_TypeClass_HYPER:
case typelib_TypeClass_UNSIGNED_HYPER:
case typelib_TypeClass_FLOAT:
case typelib_TypeClass_DOUBLE:
case typelib_TypeClass_ENUM:
if (bAssignData)
*ppUnoSeq
= alloc_uno_sequence(pNetSeq->length, nNetElemSize, pNetSeq->data);
break;
case typelib_TypeClass_ANY:
case typelib_TypeClass_SEQUENCE:
case typelib_TypeClass_STRING:
case typelib_TypeClass_TYPE:
case typelib_TypeClass_INTERFACE:
case typelib_TypeClass_EXCEPTION:
case typelib_TypeClass_STRUCT:
{
TypeDescHolder elemType(pElemTDRef);
sal_Int32 nUnoElemSize = elemType.get()->nSize;
*ppUnoSeq = alloc_uno_sequence(pNetSeq->length, nUnoElemSize, nullptr);
int nPos = 0;
try
{
// Convert each element
for (; nPos < pNetSeq->length; ++nPos)
{
void* pNetElem
= static_cast<char*>(pNetSeq->data) + (nPos * nNetElemSize);
void* pUnoElem = (*ppUnoSeq)->elements + (nPos * nUnoElemSize);
unmarshal_data(pUnoElem, pNetElem, pElemTDRef, bDestructObject,
bAssignData, bridge);
}
}
catch (...)
{
if (bAssignData)
{
// Clean up already converted elements
for (int nClean = 0; nClean < nPos; ++nClean)
{
void* pUnoElem = (*ppUnoSeq)->elements + (nClean * nUnoElemSize);
uno_destructData(pUnoElem, elemType.get(), nullptr);
}
}
throw;
}
break;
}
default:
{
throw BridgeRuntimeError(
SAL_WHERE, "could not map " + OUString::unacquired(&pElemTDRef->pTypeName)
+ " into an UNO sequence");
}
}
std::free(pNetSeq->data);
break;
}
case typelib_TypeClass_INTERFACE:
{
uno_Interface** ppUnoI = static_cast<uno_Interface**>(pUnoData);
IntPtr pNetI = *static_cast<IntPtr*>(pNetData);
TypeDescHolder type(pTDRef);
if (bDestructObject && ppUnoI)
uno_destructData(ppUnoI, type.get(), nullptr);
if (bAssignData)
{
if (pNetI)
{
Context* pCtx = static_cast<Context*>(bridge.m_net_env->pContext);
// Get oid and type description
OUString sOid(pCtx->lookupObjectId(pNetI));
typelib_InterfaceTypeDescription* pInterfaceTD
= reinterpret_cast<typelib_InterfaceTypeDescription*>(type.get());
// Get the proxy if already created, else create new
*ppUnoI = nullptr;
(*bridge.m_uno_env->getRegisteredInterface)(bridge.m_uno_env,
reinterpret_cast<void**>(ppUnoI),
sOid.pData, pInterfaceTD);
if (!*ppUnoI)
*ppUnoI = new NetProxy(bridge, pNetI, pInterfaceTD, sOid);
}
else
{
*ppUnoI = nullptr;
}
}
break;
}
case typelib_TypeClass_EXCEPTION:
case typelib_TypeClass_STRUCT:
{
void* pUnoStruct = pUnoData;
IntPtr pNetStruct = *static_cast<IntPtr*>(pNetData);
TypeDescHolder type(pTDRef);
if (bDestructObject && pNetStruct)
uno_destructData(pNetStruct, type.get(), nullptr);
typelib_CompoundTypeDescription* pCompTD
= reinterpret_cast<typelib_CompoundTypeDescription*>(type.get());
if (!type.get()->bComplete)
typelib_typedescription_complete(
reinterpret_cast<typelib_TypeDescription**>(&pCompTD));
std::vector<std::pair<typelib_TypeDescriptionReference*, sal_Int32>> vecMembers;
for (typelib_CompoundTypeDescription* pHierTD = pCompTD; pHierTD;
pHierTD = pHierTD->pBaseTypeDescription)
{
for (int n = pHierTD->nMembers - 1; n >= 0; n--)
{
vecMembers.emplace_back(pHierTD->ppTypeRefs[n], pHierTD->pMemberOffsets[n]);
}
}
size_t nNetOffset = 0;
int nPos = vecMembers.size() - 1;
try
{
for (; nPos >= 0; nPos--)
{
auto[pMemberTDRef, nUnoOffset] = vecMembers[nPos];
void* pUnoField = static_cast<char*>(pUnoStruct) + nUnoOffset;
void* pNetField = static_cast<char*>(pNetStruct) + nNetOffset;
switch (pMemberTDRef->eTypeClass)
{
case typelib_TypeClass_BOOLEAN:
case typelib_TypeClass_BYTE:
case typelib_TypeClass_CHAR:
case typelib_TypeClass_SHORT:
case typelib_TypeClass_UNSIGNED_SHORT:
case typelib_TypeClass_LONG:
case typelib_TypeClass_UNSIGNED_LONG:
case typelib_TypeClass_HYPER:
case typelib_TypeClass_UNSIGNED_HYPER:
case typelib_TypeClass_FLOAT:
case typelib_TypeClass_DOUBLE:
case typelib_TypeClass_ENUM:
if (bAssignData)
std::memcpy(pUnoField, pNetField,
net_sizeof(pMemberTDRef->eTypeClass));
break;
case typelib_TypeClass_ANY:
case typelib_TypeClass_SEQUENCE:
case typelib_TypeClass_STRING:
case typelib_TypeClass_TYPE:
case typelib_TypeClass_INTERFACE:
case typelib_TypeClass_EXCEPTION:
case typelib_TypeClass_STRUCT:
unmarshal_data(pUnoField, pNetField, pMemberTDRef, bDestructObject,
bAssignData, bridge);
break;
default:
{
throw BridgeRuntimeError(
SAL_WHERE,
"could not map " + OUString::unacquired(&pMemberTDRef->pTypeName)
+ " into an UNO " + OUString::unacquired(&pTDRef->pTypeName));
}
}
nNetOffset += net_sizeof(pMemberTDRef->eTypeClass);
}
}
catch (...)
{
if (bAssignData)
{
// Clean up already converted fields
for (int nClean = vecMembers.size() - 1; nClean > nPos; nClean--)
{
auto[pMemberTDRef, nUnoOffset] = vecMembers[nClean];
void* pUnoField = static_cast<char*>(pUnoStruct) + nUnoOffset;
uno_type_destructData(pUnoField, pMemberTDRef, nullptr);
}
}
throw;
}
std::free(pNetStruct);
break;
}
default:
{
throw BridgeRuntimeError(SAL_WHERE, "could not map "
+ OUString::unacquired(&pTDRef->pTypeName)
+ " value to UNO");
}
}
}
}
void Bridge::map_uno_to_net_value(void* pUnoData, Value* pValue,
typelib_TypeDescriptionReference* pTDRef, bool bDestructValue)
{
marshal_data(pUnoData, pValue, pTDRef, bDestructValue, *this);
}
void Bridge::map_net_value_to_uno(void* pUnoData, Value* pValue,
typelib_TypeDescriptionReference* pTDRef, bool bDestructObject,
bool bAssignObject)
{
unmarshal_data(pUnoData, pValue, pTDRef, bDestructObject, bAssignObject, *this);
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View file

@ -0,0 +1,251 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "net_bridge.hxx"
#include <bridges/net_uno/net_context.hxx>
namespace net_uno
{
void Bridge::call_net_func(IntPtr pNetI, const typelib_TypeDescription* pMethodTD,
typelib_TypeDescriptionReference* pReturnTDRef, int nParams,
typelib_MethodParameter* pParams, void** pArgs, void* pRet,
uno_Any** pExc)
{
Value* pArgsRetExc = static_cast<Value*>(alloca((nParams + 2) * sizeof(Value)));
// Convert in and inout arguments
for (int i = 0; i < nParams; ++i)
{
const typelib_MethodParameter& param = pParams[i];
if (param.bIn)
{
map_uno_to_net_value(pArgs[i], &pArgsRetExc[i], param.pTypeRef, false);
}
}
OUString sMethodName = OUString::unacquired(&pMethodTD->pTypeName);
Context* pCtx = static_cast<Context*>(m_net_env->pContext);
bool error = !pCtx->dispatchCall(pNetI, sMethodName.getStr(), pArgsRetExc,
&pArgsRetExc[nParams], &pArgsRetExc[nParams + 1]);
// Convert out and inout arguments
for (int i = 0; i < nParams; ++i)
{
const typelib_MethodParameter& param = pParams[i];
try
{
if (param.bOut)
map_net_value_to_uno(pArgs[i], &pArgsRetExc[i], param.pTypeRef,
param.bIn && param.bOut, true);
}
catch (...)
{
// Clean up uno out args
for (int n = 0; n < i; ++n)
{
const typelib_MethodParameter& param2 = pParams[n];
if (param2.bOut)
{
uno_type_destructData(pArgs[n], param2.pTypeRef, nullptr);
}
}
throw;
}
}
if (error)
{
map_net_value_to_uno(*pExc, &pArgsRetExc[nParams + 1],
cppu::UnoType<css::uno::Any>::get().getTypeLibType(), false, true);
}
else
{
if (pReturnTDRef && pReturnTDRef->eTypeClass != typelib_TypeClass_VOID)
{
map_net_value_to_uno(pRet, &pArgsRetExc[nParams], pReturnTDRef, false, true);
}
*pExc = nullptr;
}
}
bool Bridge::call_uno_func(uno_Interface* pUnoI, const typelib_TypeDescription* pMethodTD,
typelib_TypeDescriptionReference* pReturnTDRef, int nParams,
typelib_MethodParameter* pParams, Value* pArgs, Value* pRet, Value* pExc)
{
union largest {
sal_Int64 n;
double d;
void* p;
uno_Any a;
};
// Calculate size of memory required for return value
sal_Int32 nReturnSize = sizeof(largest);
if (pReturnTDRef)
{
if (pReturnTDRef->eTypeClass == typelib_TypeClass_VOID)
{
nReturnSize = 0;
}
else if (pReturnTDRef->eTypeClass == typelib_TypeClass_STRUCT
|| pReturnTDRef->eTypeClass == typelib_TypeClass_EXCEPTION)
{
TypeDescHolder returnTD(pReturnTDRef);
if (o3tl::make_unsigned(returnTD.get()->nSize) > sizeof(largest))
nReturnSize = returnTD.get()->nSize;
}
}
// Prepare a memory block to contain all the converted arguments and return value
//
// The memory block contains pointers to small arguments stored in the same block.
// If an argument is larger then `largest` union, such as a struct, then the pointer
// points to an extra block of memory.
//
// The argument pointers are followed by the return value. If the return value is
// larger than the `largest` union, this is a pointer to an extra block containing
// the return value instead.
//
// For example: 2 arguments and return value
// | Pointer 1 (void*)
// | Pointer 2 (void*)
// | Return Value (void*)
// | Argument 1 (largest*)
// | Argument 2 (largest*)
// Complete memory block
char* mem = static_cast<char*>(
alloca(nParams * sizeof(void*) + nReturnSize + nParams * sizeof(largest)));
// Array of argument pointers; at the start of the memory block
void** uno_args = reinterpret_cast<void**>(mem);
// Pointer to return value memory; after all argument pointers
void* uno_ret = nReturnSize == 0 ? nullptr : mem + nParams * sizeof(void*);
// Space for actual arguments; after return value
largest* uno_args_mem = reinterpret_cast<largest*>(mem + nParams * sizeof(void*) + nReturnSize);
for (sal_Int32 i = 0; i < nParams; ++i)
{
const typelib_MethodParameter& param = pParams[i];
typelib_TypeDescriptionReference* type = param.pTypeRef;
uno_args[i] = &uno_args_mem[i];
if (type->eTypeClass == typelib_TypeClass_STRUCT
|| type->eTypeClass == typelib_TypeClass_EXCEPTION)
{
TypeDescHolder td(type);
if (o3tl::make_unsigned(td.get()->nSize) > sizeof(largest))
uno_args[i] = alloca(td.get()->nSize);
}
if (param.bIn)
{
try
{
// in, in/out params
map_net_value_to_uno(uno_args[i], &pArgs[i], type, false, true);
}
catch (...)
{
// cleanup uno in args
for (sal_Int32 n = 0; n < i; ++n)
{
const typelib_MethodParameter& param2 = pParams[n];
if (param2.bIn)
{
uno_type_destructData(uno_args[n], param2.pTypeRef, nullptr);
}
}
throw;
}
}
}
uno_Any uno_exc_holder;
uno_Any* uno_exc = &uno_exc_holder;
// Propagate function call to binary uno
(*pUnoI->pDispatcher)(pUnoI, pMethodTD, uno_ret, uno_args, &uno_exc);
if (!uno_exc)
{
// Convert out arguments and destruct previously converted uno args
for (sal_Int32 i = 0; i < nParams; ++i)
{
const typelib_MethodParameter& param = pParams[i];
typelib_TypeDescriptionReference* type = param.pTypeRef;
if (param.bOut)
{
try
{
map_uno_to_net_value(uno_args[i], &pArgs[i], param.pTypeRef, false);
}
catch (...)
{
// Cleanup rest of uno args
for (sal_Int32 n = i; n < nParams; ++n)
{
uno_type_destructData(uno_args[n], pParams[n].pTypeRef, nullptr);
}
// Cleanup uno return value
uno_type_destructData(uno_ret, pReturnTDRef, nullptr);
throw;
}
}
// Cleanup args
if (type->eTypeClass > typelib_TypeClass_DOUBLE
&& type->eTypeClass != typelib_TypeClass_ENUM)
{
uno_type_destructData(uno_args[i], type, nullptr);
}
}
if (pReturnTDRef && pReturnTDRef->eTypeClass != typelib_TypeClass_VOID)
{
// Convert uno return value
try
{
map_uno_to_net_value(uno_ret, pRet, pReturnTDRef, false);
uno_type_destructData(uno_ret, pReturnTDRef, nullptr);
}
catch (...)
{
uno_type_destructData(uno_ret, pReturnTDRef, nullptr);
throw;
}
}
return true;
}
else // An exception occurred
{
// Destruct uno in arguments
for (sal_Int32 i = 0; i < nParams; ++i)
{
const typelib_MethodParameter& param = pParams[i];
if (param.bIn)
{
uno_type_destructData(uno_args[i], param.pTypeRef, nullptr);
}
}
map_uno_to_net_value(uno_exc, pExc, cppu::UnoType<css::uno::Any>::get().getTypeLibType(),
false);
return false;
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View file

@ -0,0 +1,175 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "net_proxy.hxx"
#include "net_base.hxx"
#include <bridges/net_uno/net_context.hxx>
namespace net_uno
{
namespace
{
void SAL_CALL NetProxy_acquire(uno_Interface* pUnoI) SAL_THROW_EXTERN_C()
{
NetProxy* that = static_cast<NetProxy*>(pUnoI);
that->acquire();
}
void SAL_CALL NetProxy_release(uno_Interface* pUnoI) SAL_THROW_EXTERN_C()
{
NetProxy* that = static_cast<NetProxy*>(pUnoI);
that->release();
}
void SAL_CALL NetProxy_free([[maybe_unused]] uno_ExtEnvironment* pEnv, void* pUnoI)
SAL_THROW_EXTERN_C()
{
NetProxy* that = static_cast<NetProxy*>(pUnoI);
delete that;
}
void SAL_CALL NetProxy_dispatch(uno_Interface* pUnoI, const typelib_TypeDescription* pMemberTD,
void* pUnoRet, void** pUnoArgs, uno_Any** pUnoExc)
SAL_THROW_EXTERN_C()
{
NetProxy* proxy = static_cast<NetProxy*>(pUnoI);
try
{
switch (pMemberTD->eTypeClass)
{
case typelib_TypeClass_INTERFACE_ATTRIBUTE:
{
const typelib_InterfaceAttributeTypeDescription* pAttribTD
= reinterpret_cast<const typelib_InterfaceAttributeTypeDescription*>(pMemberTD);
if (pUnoRet) // getter
{
proxy->m_bridge.call_net_func(proxy->m_netI, pMemberTD,
pAttribTD->pAttributeTypeRef, 0, nullptr, nullptr,
pUnoRet, pUnoExc);
}
else // setter
{
typelib_MethodParameter param;
param.pTypeRef = pAttribTD->pAttributeTypeRef;
param.bIn = true;
param.bOut = false;
proxy->m_bridge.call_net_func(proxy->m_netI, pMemberTD, nullptr, 1, &param,
pUnoArgs, nullptr, pUnoExc);
}
break;
}
case typelib_TypeClass_INTERFACE_METHOD:
{
const typelib_InterfaceMethodTypeDescription* pMethodTD
= reinterpret_cast<const typelib_InterfaceMethodTypeDescription*>(pMemberTD);
sal_Int32 nMemberPos = pMethodTD->aBase.nPosition;
assert(nMemberPos < proxy->m_TD->nAllMembers);
sal_Int32 nFunctionPos = proxy->m_TD->pMapMemberIndexToFunctionIndex[nMemberPos];
assert(nFunctionPos < proxy->m_TD->nMapFunctionIndexToMemberIndex);
switch (nFunctionPos)
{
case 1: // acquire()
NetProxy_acquire(proxy);
*pUnoExc = nullptr;
break;
case 2: // release()
NetProxy_release(proxy);
*pUnoExc = nullptr;
break;
default: // arbitrary method call
proxy->m_bridge.call_net_func(
proxy->m_netI, pMemberTD, pMethodTD->pReturnTypeRef, pMethodTD->nParams,
pMethodTD->pParams, pUnoArgs, pUnoRet, pUnoExc);
break;
}
break;
}
default:
{
throw BridgeRuntimeError(SAL_WHERE,
"could not find function "
+ OUString::unacquired(&pMemberTD->pTypeName));
}
}
}
catch (const BridgeRuntimeError& err)
{
SAL_WARN("bridges", ".NET bridge error: " << err.m_message);
css::uno::RuntimeException exc("[net_uno bridge error] " + err.m_message,
css::uno::Reference<css::uno::XInterface>());
uno_type_any_construct(*pUnoExc, &exc,
cppu::UnoType<css::uno::RuntimeException>::get().getTypeLibType(),
nullptr);
}
}
}
NetProxy::NetProxy(Bridge& rBridge, IntPtr pNetI, typelib_InterfaceTypeDescription* pTD,
const OUString& sOid)
: m_ref(1)
, m_bridge(rBridge)
, m_netI(nullptr)
, m_oid(sOid)
, m_TD(pTD)
{
void* that = this;
(*m_bridge.m_uno_env->registerProxyInterface)(m_bridge.m_uno_env, &that, NetProxy_free,
m_oid.pData, m_TD);
OUString sInterfaceName = map_uno_type_to_net(pTD->aBase.pWeakRef);
Context* pCtx = static_cast<Context*>(m_bridge.m_net_env->pContext);
m_netI = pCtx->registerInterface(pNetI, m_oid.getStr(), sInterfaceName.getStr());
m_bridge.acquire();
uno_Interface::acquire = NetProxy_acquire;
uno_Interface::release = NetProxy_release;
uno_Interface::pDispatcher = NetProxy_dispatch;
}
NetProxy::~NetProxy()
{
OUString sInterfaceName = map_uno_type_to_net(m_TD->aBase.pWeakRef);
static_cast<Context*>(m_bridge.m_net_env->pContext)
->revokeInterface(m_oid.getStr(), sInterfaceName.getStr());
m_bridge.release();
}
void NetProxy::acquire()
{
// rebirth of proxy zombie
if (osl_atomic_increment(&m_ref) == 1)
{
void* that = this;
(*m_bridge.m_uno_env->registerProxyInterface)(m_bridge.m_uno_env, &that, NetProxy_free,
m_oid.pData, m_TD);
}
}
void NetProxy::release()
{
// revoke from uno env on last release
if (osl_atomic_decrement(&m_ref) == 0)
{
(*m_bridge.m_uno_env->revokeInterface)(m_bridge.m_uno_env, this);
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View file

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "net_bridge.hxx"
#include <bridges/net_uno/net_types.hxx>
#include <rtl/ustring.hxx>
namespace net_uno
{
struct NetProxy : public uno_Interface
{
mutable oslInterlockedCount m_ref;
Bridge& m_bridge;
IntPtr m_netI;
OUString m_oid;
typelib_InterfaceTypeDescription* m_TD;
NetProxy(Bridge& rBridge, IntPtr pNetI, typelib_InterfaceTypeDescription* pTD,
const OUString& sOid);
~NetProxy();
void acquire();
void release();
};
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View file

@ -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();

View file

@ -0,0 +1,38 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <bridges/net_uno/net_types.hxx>
namespace net_uno
{
typedef IntPtr (*CreateProxyFunc)(String pOid, String pInterfaceName, IntPtr pBridge,
IntPtr pUnoInterface, IntPtr pTD);
typedef String (*LookupObjectIdFunc)(IntPtr pNetInterface);
typedef IntPtr (*RegisterInterfaceFunc)(IntPtr pNetInterface, String pOid, String pInterfaceName);
typedef IntPtr (*LookupInterfaceFunc)(String pOid, String pInterfaceName);
typedef void (*RevokeInterfaceFunc)(String pOid, String pInterfaceName);
typedef sal_Int8 (*DispatchCallFunc)(IntPtr pNetInterface, String pMethodName, Value* pArgs,
Value* pRet, Value* pExc);
typedef void (*ThrowErrorFunc)(String pWhere, String pMessage);
struct Context
{
CreateProxyFunc createProxy;
LookupObjectIdFunc lookupObjectId;
RegisterInterfaceFunc registerInterface;
LookupInterfaceFunc lookupInterface;
RevokeInterfaceFunc revokeInterface;
DispatchCallFunc dispatchCall;
ThrowErrorFunc throwError;
};
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View file

@ -0,0 +1,79 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <sal/types.h>
namespace net_uno
{
using IntPtr = void*;
using String = const sal_Unicode*;
union Value {
// bool
sal_Bool boolData;
// byte
sal_Int8 byteData;
// char
sal_Unicode charData;
// short
sal_Int16 shortData;
sal_uInt16 unsigShortData;
// long
sal_Int32 longData;
sal_uInt32 unsigLongData;
// hyper
sal_Int64 hyperData;
sal_uInt64 unsigHyperData;
// float/double
float floatData;
double doubleData;
// string
IntPtr stringData;
// type
IntPtr typeData;
// any
struct Any
{
IntPtr data;
IntPtr type;
} anyData;
// enum
sal_Int32 enumData;
// struct
IntPtr structData;
// exception
IntPtr exceptionData;
// sequence
struct Sequence
{
IntPtr data;
sal_Int32 length;
} sequenceData;
// interface
IntPtr interfaceData;
};
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View file

@ -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

View file

@ -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

View file

@ -9,13 +9,33 @@
$(eval $(call gb_DotnetLibrary_DotnetLibrary,net_bridge,$(gb_DotnetLibrary_CS)))
$(eval $(call gb_DotnetLibrary_add_sources,net_bridge,\
net_ure/source/bridge/NativeBootstrap \
net_ure/source/bridge/helper/DisposeGuard \
net_ure/source/bridge/helper/StructHelper \
net_ure/source/bridge/helper/TypeHelper \
net_ure/source/bridge/helper/WeakBase \
net_ure/source/bridge/helper/WeakComponentBase \
))
$(eval $(call gb_DotnetLibrary_add_sources,net_bridge,\
net_ure/source/bridge/native/InteropMethods \
net_ure/source/bridge/native/InteropTypes \
net_ure/source/bridge/native/Marshaller \
net_ure/source/bridge/native/NativeBootstrap \
net_ure/source/bridge/native/NativeUnoProxy \
net_ure/source/bridge/native/NetEnvironment \
net_ure/source/bridge/native/WeakIndexTable \
net_ure/source/bridge/native/WeakOidTypeTable \
))
$(eval $(call gb_DotnetLibrary_add_properties,net_bridge,\
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> \
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> \
))
$(eval $(call gb_DotnetLibrary_add_items,net_bridge,\
<PackageReference Include="System.Reflection.DispatchProxy" Version="4.7.1" /> \
))
$(eval $(call gb_DotnetLibrary_link_library,net_bridge,net_uretypes))
$(eval $(call gb_DotnetLibrary_add_properties,net_bridge,\

View file

@ -28,7 +28,6 @@ public class AnyTests
public void with_RejectsInvalidParams()
{
Assert.That(() => Any.with<Any>(Any.VOID), Throws.ArgumentException);
Assert.That(() => Any.with<int>(null), Throws.ArgumentException);
}
[Test]

View file

@ -17,9 +17,51 @@ namespace com.sun.star.uno
public Type Type { get; private set; }
public Object Value { get; private set; }
public Any(object value) => setValue(value?.GetType() ?? typeof(void), value);
public Any(Type type, object value) => setValue(type, value);
public Any(object value) : this(value?.GetType() ?? typeof(void), value) { }
public static Any with<T>(object value) => new Any(typeof(T), value);
public static Any with<T>(T value) => new Any(typeof(T), value);
public bool contains<T>()
{
if (typeof(IQueryInterface).IsAssignableFrom(Type))
{
if (typeof(T).IsAssignableFrom(Type))
// Special case for already implemented interface
return true;
else if (typeof(IQueryInterface).IsAssignableFrom(typeof(T)))
// Special case for contained IQueryInterface
return ((IQueryInterface)Value).queryInterface(typeof(T)).hasValue();
}
return typeof(T).IsAssignableFrom(Type);
}
public T cast<T>()
{
if (typeof(IQueryInterface).IsAssignableFrom(Type))
{
if (typeof(T).IsAssignableFrom(Type))
// Special case for already implemented interface
return (T)Value;
else if (typeof(IQueryInterface).IsAssignableFrom(typeof(T)))
// Special case for contained IQueryInterface
return (T)((IQueryInterface)Value).queryInterface(typeof(T)).Value;
}
return (T)Value;
}
public T castOrDefault<T>(T fallback = default)
{
if (typeof(IQueryInterface).IsAssignableFrom(Type))
{
if (typeof(T).IsAssignableFrom(Type))
// Special case for already implemented interface
return (T)Value;
else if (typeof(IQueryInterface).IsAssignableFrom(typeof(T)))
// Special case for contained IQueryInterface
return (T)((IQueryInterface)Value).queryInterface(typeof(T)).Value;
}
return typeof(T).IsAssignableFrom(Type) ? (T)Value : fallback;
}
public bool hasValue() => Type != typeof(void);
@ -53,6 +95,6 @@ namespace com.sun.star.uno
public override bool Equals(object obj) => obj is Any other && equals(other);
public override int GetHashCode() => (Type, Value).GetHashCode();
public override string ToString() => $"uno.Any {{ Type = {Type}, Value = {Value ?? "Null"} }}";
public override string ToString() => $"com.sun.star.uno.Any {{ Type = {Type}, Value = {Value ?? "Null"} }}";
}
}

View file

@ -12,6 +12,26 @@ namespace com.sun.star.uno
{
public interface IQueryInterface
{
// Kept empty for now, until the .NET bridge is in place
Any queryInterface(Type type);
}
public static class IQueryInterfaceExtensions
{
public static bool implements<T>(this IQueryInterface iqi)
where T : IQueryInterface
=> iqi is T ? true : iqi.queryInterface(typeof(T)).hasValue();
public static T query<T>(this IQueryInterface iqi)
where T : IQueryInterface
=> iqi is T t ? t : (T)iqi.queryInterface(typeof(T)).Value;
public static T queryOrDefault<T>(this IQueryInterface iqi, T fallback = default)
where T : IQueryInterface
{
if (iqi is T t)
return t;
Any result = iqi.queryInterface(typeof(T));
return result.hasValue() ? (T)result.Value : fallback;
}
}
}

View file

@ -9,41 +9,113 @@
#include <string_view>
#include <bridges/net_uno/net_context.hxx>
#include <cppuhelper/bootstrap.hxx>
#include <o3tl/string_view.hxx>
#include <rtl/bootstrap.hxx>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <uno/mapping.hxx>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
namespace net_uno
{
extern "C" {
SAL_DLLPUBLIC_EXPORT 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<XComponentContext> xContext(::cppu::bootstrap());
// Get a mapping between the C++ and .NET environments
Environment cpp_env(CPPU_CURRENT_LANGUAGE_BINDING_NAME);
Environment net_env(u"" UNO_LB_NET ""_ustr, new Context(aContext));
Mapping mapping(cpp_env.get(), net_env.get());
if (!mapping.is())
{
Reference<lang::XComponent> xComp(xContext, UNO_QUERY);
if (xComp.is())
{
xComp->dispose();
}
throw RuntimeException(u"could not get mapping between C++ and .NET"_ustr);
}
// Map the XComponentContext to a .NET proxy
return mapping.mapInterface(xContext.get(), cppu::UnoType<decltype(xContext)>::get());
}
catch (const Exception& exc)
{
SAL_WARN("net", ".NET bootstrap error: " << exc.Message);
aContext.throwError((u"" SAL_WHERE ""_ustr).getStr(), exc.Message.getStr());
return nullptr;
}
}
SAL_DLLPUBLIC_EXPORT 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<XComponentContext> xContext(::cppu::bootstrap());
return xContext.get();
// Bootstrap UNO
Reference<XComponentContext> 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<lang::XComponent> xComp(xContext, UNO_QUERY);
if (xComp.is())
{
xComp->dispose();
}
throw RuntimeException(u"could not get mapping between C++ and .NET"_ustr);
}
// Map the XComponentContext to a .NET proxy
return mapping.mapInterface(xContext.get(), cppu::UnoType<decltype(xContext)>::get());
}
catch (...)
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: */

View file

@ -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);
}
}
}

View file

@ -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();
}
}

View file

@ -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, (Func<object[], object>, Type[])> _ctorCache;
private static Dictionary<Type, (Action<object, object[]>, Type[])> _dtorCache;
static StructHelper()
{
_ctorCache = new Dictionary<Type, (Func<object[], object>, Type[])>();
_dtorCache = new Dictionary<Type, (Action<object, object[]>, Type[])>();
}
public static (Func<object[], object>, Type[]) GetConstructor(Type type)
{
if (_ctorCache.TryGetValue(type, out (Func<object[], object>, Type[]) cached))
return cached;
ConstructorInfo ctor = type.GetConstructors().First(c => c.GetParameters().Length > 0);
Func<object[], object> func = args => ctor.Invoke(args);
Type[] paramTypes = ctor.GetParameters()
.Select(p => TypeHelper.RemoveReference(p.ParameterType))
.ToArray();
return _ctorCache[type] = (func, paramTypes);
}
public static (Action<object, object[]>, Type[]) GetDeconstructor(Type type)
{
if (_dtorCache.TryGetValue(type, out (Action<object, object[]>, Type[]) cached))
return cached;
MethodInfo dtor = type.GetMethod("Deconstruct", BindingFlags.Instance | BindingFlags.Public);
Action<object, object[]> action = (target, args) => dtor.Invoke(target, args);
Type[] paramTypes = dtor.GetParameters()
.Select(p => TypeHelper.RemoveReference(p.ParameterType))
.ToArray();
return _dtorCache[type] = (action, paramTypes);
}
}
}

View file

@ -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<Assembly> _assemblies;
private static Dictionary<string, Type> _cache;
static TypeHelper()
{
_assemblies = AppDomain.CurrentDomain.GetAssemblies()
.OrderBy(a => !a.FullName.StartsWith("net_"))
.ThenBy(a => a != Assembly.GetExecutingAssembly())
.ToList();
_cache = new Dictionary<string, Type>();
// 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);
}
}
}

View file

@ -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<Type, Type[]> _providedTypes;
static WeakBase() => _providedTypes = new ConcurrentDictionary<Type, Type[]>();
// 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<sbyte>();
// An XAdapter implementation that holds a weak reference to an interface.
private class WeakAdapter : IQueryInterface, XAdapter
{
private event Action _onDispose;
private WeakReference<IQueryInterface> _weakRef;
public WeakAdapter(IQueryInterface obj)
=> _weakRef = new WeakReference<IQueryInterface>(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;
}
}
}

View file

@ -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<XEventListener> _listeners;
public WeakComponentBase() => _listeners = new List<XEventListener>();
// 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);
}
}

View file

@ -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
);
}
}

View file

@ -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;
};
}

View file

@ -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<object, object[]> 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<object[], object> 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;
}
}
}

View file

@ -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<WeakReference<NetEnvironment>> _environments;
static NativeBootstrap() => _environments = new LinkedList<WeakReference<NetEnvironment>>();
public static XComponentContext bootstrap()
{
NetEnvironment env = new NetEnvironment();
_environments.AddLast(new WeakReference<NetEnvironment>(env));
IntPtr result = InteropMethods.bootstrap(env.Context);
return (XComponentContext)env.GetRegisteredObject(result);
}
public static XComponentContext defaultBootstrap_InitialComponentContext(
string iniFile = null, IDictionary<string, string> parameters = null)
{
iniFile = string.IsNullOrWhiteSpace(iniFile) ? null : iniFile;
string joinedParams = string.Join("|",
parameters?.Select(p => $"{p.Key}={p.Value}") ?? Array.Empty<string>());
joinedParams = string.IsNullOrWhiteSpace(joinedParams) ? null : joinedParams;
NetEnvironment env = new NetEnvironment();
_environments.AddLast(new WeakReference<NetEnvironment>(env));
IntPtr result = InteropMethods.defaultBootstrap_InitialComponentContext(
iniFile, joinedParams, env.Context);
return (XComponentContext)env.GetRegisteredObject(result);
}
}
}

View file

@ -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} }}";
}
}

View file

@ -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<int, string> _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<int, string>();
}
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;
}
}
}
}

View file

@ -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<int, WeakReference<object>> _objectByIndex;
private readonly Dictionary<WeakReference<object>, int> _indexByObject;
private readonly List<int> _cleanupIndices;
private readonly object _lock;
private int _counter;
private int _updateOps;
public WeakIndexTable()
{
_objectByIndex = new Dictionary<int, WeakReference<object>>();
_indexByObject = new Dictionary<WeakReference<object>, int>(new WeakReferenceComparer());
_cleanupIndices = new List<int>();
_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<object> 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<object> weakRef = new WeakReference<object>(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<object> weakRef = _objectByIndex[key];
if (!weakRef.TryGetTarget(out _))
_cleanupIndices.Add(key);
}
foreach (int key in _cleanupIndices)
_objectByIndex.Remove(key);
_cleanupIndices.Clear();
}
}
private class WeakReferenceComparer : IEqualityComparer<WeakReference<object>>
{
public bool Equals(WeakReference<object> x, WeakReference<object> y)
{
if (x.TryGetTarget(out object first))
if (y.TryGetTarget(out object second))
return ReferenceEquals(first, second);
return false;
}
public int GetHashCode(WeakReference<object> obj)
=> obj.TryGetTarget(out object target) ? target.GetHashCode() : 0;
}
}
}

View file

@ -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<string, Level1Entry> _level1Map;
private readonly List<(string, Type)> _cleanupEntries;
private readonly object _lock;
private int _count;
private int _updateOps;
public WeakOidTypeTable()
{
_level1Map = new Dictionary<string, Level1Entry>();
_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<Type, Level2Entry>
{
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<object> _ref;
private int _count;
public Level2Entry(object obj)
{
_ref = new WeakReference<object>(obj);
_count = 1;
}
public bool TryGetTarget(out object obj) => _ref.TryGetTarget(out obj);
public void Acquire() => ++_count;
public bool Release() => --_count == 0;
}
}
}

View file

@ -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 \

View file

@ -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;
}

View file

@ -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) "<Project Sdk=\"Microsoft.NET.Sdk\">" > $@
$(ECHO) " <PropertyGroup>" >> $@
$(ECHO) " <AssemblyName>$(APP_NAME)</AssemblyName>" >> $@
$(ECHO) " <TargetFramework>net8.0</TargetFramework>" >> $@
$(ECHO) " <OutputType>exe</OutputType>" >> $@
$(ECHO) " <PublishSingleFile>true</PublishSingleFile>" >> $@
$(ECHO) " <SelfContained>false</SelfContained>" >> $@
$(ECHO) " <IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>" >> $@
$(ECHO) " <RestoreAdditionalProjectSources>$(LO_NUPKG_DIR)</RestoreAdditionalProjectSources>" >> $@
$(ECHO) " </PropertyGroup>" >> $@
$(ECHO) " <ItemGroup>" >> $@
$(ECHO) " <PackageReference Include=\"$(LO_NUPKG_ID)\" Version=\"$(LO_NUPKG_VERSION)\" />" >> $@
$(ECHO) " <Compile Include=\"$(APP_SRC_DIR)/*.$(APP_LANG)\" />" >> $@
$(ECHO) " </ItemGroup>" >> $@
$(ECHO) "</Project>" >> $@
$(ECHOLINE) >> $@
$(APP_EXE_FILE) : $(APP_PROJ_FILE)
-$(MKDIR) $(@D)
$(SDK_DOTNET) publish $< $(DOTNET_FLAGS) -o $(<D)
$(COPY) $(<D)/$(APP_EXE_NAME) $@
.PHONY: $(APP_NAME)
$(APP_NAME) : $(APP_EXE_FILE)
$(ECHO) --------------------------------------------------------
$(ECHO) Use the following commands to run the example:
$(ECHO) $(MAKE) run OR $(MAKE) $(APP_NAME).run
$(ECHO)
$(ECHO) And the following commands to clean the example:
$(ECHO) $(MAKE) clean OR $(MAKE) $(APP_NAME).clean
$(ECHO) --------------------------------------------------------
.PHONY: $(APP_NAME).run run
$(APP_NAME).run run : $(APP_EXE_FILE)
cd $(<D) && ./$(<F)
.PHONY: $(APP_NAME).clean clean
$(APP_NAME).clean clean :
-$(DELRECURSIVE) $(APP_MISC_DIR)
-$(DEL) $(APP_EXE_FILE)