diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /binaryurp | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'binaryurp')
40 files changed, 5654 insertions, 0 deletions
diff --git a/binaryurp/CppunitTest_binaryurp_test-cache.mk b/binaryurp/CppunitTest_binaryurp_test-cache.mk new file mode 100644 index 000000000..97f5b78f0 --- /dev/null +++ b/binaryurp/CppunitTest_binaryurp_test-cache.mk @@ -0,0 +1,22 @@ +# -*- 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_CppunitTest_CppunitTest,binaryurp_test-cache)) + +$(eval $(call gb_CppunitTest_use_external,binaryurp_test-cache,boost_headers)) + +$(eval $(call gb_CppunitTest_use_libraries,binaryurp_test-cache,\ + sal \ +)) + +$(eval $(call gb_CppunitTest_add_exception_objects,binaryurp_test-cache,\ + binaryurp/qa/test-cache \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/binaryurp/CppunitTest_binaryurp_test-unmarshal.mk b/binaryurp/CppunitTest_binaryurp_test-unmarshal.mk new file mode 100644 index 000000000..11c70f5b9 --- /dev/null +++ b/binaryurp/CppunitTest_binaryurp_test-unmarshal.mk @@ -0,0 +1,33 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_CppunitTest_CppunitTest,binaryurp_test-unmarshal)) + +$(eval $(call gb_CppunitTest_use_external,binaryurp_test-unmarshal,boost_headers)) + +$(eval $(call gb_CppunitTest_use_libraries,binaryurp_test-unmarshal,\ + cppu \ + cppuhelper \ + sal \ + salhelper \ +)) + +$(eval $(call gb_CppunitTest_use_library_objects,binaryurp_test-unmarshal,\ + binaryurp \ +)) + +$(eval $(call gb_CppunitTest_use_api,binaryurp_test-unmarshal,\ + udkapi \ +)) + +$(eval $(call gb_CppunitTest_add_exception_objects,binaryurp_test-unmarshal,\ + binaryurp/qa/test-unmarshal \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/binaryurp/IwyuFilter_binaryurp.yaml b/binaryurp/IwyuFilter_binaryurp.yaml new file mode 100644 index 000000000..49e10617c --- /dev/null +++ b/binaryurp/IwyuFilter_binaryurp.yaml @@ -0,0 +1,34 @@ +--- +assumeFilename: binaryurp/source/bridge.cxx +excludelist: + binaryurp/source/marshal.hxx: + # Don't propose hxx -> h change in URE libs + - rtl/byteseq.hxx + binaryurp/source/reader.hxx: + # Don't propose hxx -> h change in URE libs + - rtl/byteseq.hxx + binaryurp/source/incomingrequest.hxx: + # Don't propose hxx -> h change in URE libs + - rtl/byteseq.hxx + binaryurp/source/lessoperators.cxx: + # Don't propose hxx -> h change in URE libs + - rtl/byteseq.hxx + binaryurp/source/outgoingrequests.cxx: + # Don't propose hxx -> h change in URE libs + - rtl/byteseq.hxx + # Actually needed + - lessoperators.hxx + binaryurp/source/bridgefactory.cxx: + # Actually needed + - com/sun/star/connection/XConnection.hpp + - com/sun/star/uno/XComponentContext.hpp + - com/sun/star/uno/XInterface.hpp + binaryurp/source/bridge.cxx: + # Actually needed + - com/sun/star/bridge/XInstanceProvider.hpp + - com/sun/star/connection/XConnection.hpp + - com/sun/star/lang/XEventListener.hpp + - com/sun/star/uno/XInterface.hpp + binaryurp/source/unmarshal.cxx: + # Actually used + - vector diff --git a/binaryurp/Library_binaryurp.mk b/binaryurp/Library_binaryurp.mk new file mode 100644 index 000000000..439f1f7d4 --- /dev/null +++ b/binaryurp/Library_binaryurp.mk @@ -0,0 +1,40 @@ +# -*- 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,binaryurp)) + +$(eval $(call gb_Library_set_componentfile,binaryurp,binaryurp/source/binaryurp,ure/services)) + +$(eval $(call gb_Library_use_udk_api,binaryurp)) + +$(eval $(call gb_Library_use_external,binaryurp,boost_headers)) + +$(eval $(call gb_Library_use_libraries,binaryurp,\ + cppu \ + cppuhelper \ + sal \ + salhelper \ +)) + +$(eval $(call gb_Library_add_exception_objects,binaryurp,\ + binaryurp/source/binaryany \ + binaryurp/source/bridge \ + binaryurp/source/bridgefactory \ + binaryurp/source/currentcontext \ + binaryurp/source/incomingrequest \ + binaryurp/source/lessoperators \ + binaryurp/source/marshal \ + binaryurp/source/outgoingrequests \ + binaryurp/source/proxy \ + binaryurp/source/reader \ + binaryurp/source/unmarshal \ + binaryurp/source/writer \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/binaryurp/Makefile b/binaryurp/Makefile new file mode 100644 index 000000000..ccb1c85a0 --- /dev/null +++ b/binaryurp/Makefile @@ -0,0 +1,7 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- + +module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST)))) + +include $(module_directory)/../solenv/gbuild/partial_build.mk + +# vim: set noet sw=4 ts=4: diff --git a/binaryurp/Module_binaryurp.mk b/binaryurp/Module_binaryurp.mk new file mode 100644 index 000000000..fcfd303f0 --- /dev/null +++ b/binaryurp/Module_binaryurp.mk @@ -0,0 +1,21 @@ +# -*- 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_Module_Module,binaryurp)) + +$(eval $(call gb_Module_add_targets,binaryurp,\ + Library_binaryurp \ +)) + +$(eval $(call gb_Module_add_check_targets,binaryurp,\ + CppunitTest_binaryurp_test-cache \ + CppunitTest_binaryurp_test-unmarshal \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/binaryurp/README.md b/binaryurp/README.md new file mode 100644 index 000000000..ac26bec06 --- /dev/null +++ b/binaryurp/README.md @@ -0,0 +1,8 @@ +# UNO Remote Protocol (URP) + +UNO provides a binary protocol called the UNO Remote Protocol (URP) that provides +a bridge between UNO environments. This bridge allows processes and objects +to send method calls and to receive return values. UNO objects in different +environments are connected by way of this interprocess bridge. The underlying +connection is made through a socket or pipe. Remote UNO objects are connected +by means of TCP/IP using the high-level protocol of the URP. diff --git a/binaryurp/qa/test-cache.cxx b/binaryurp/qa/test-cache.cxx new file mode 100644 index 000000000..c4696f783 --- /dev/null +++ b/binaryurp/qa/test-cache.cxx @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cstddef> + +#include <sal/types.h> +#include <cppunit/TestAssert.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> + +#include "../source/cache.hxx" + +namespace { + +class Test: public CppUnit::TestFixture { +private: + CPPUNIT_TEST_SUITE(Test); + CPPUNIT_TEST(testNothingLostFromLruList); + CPPUNIT_TEST_SUITE_END(); + + void testNothingLostFromLruList(); +}; + +// cf. jurt/test/com/sun/star/lib/uno/protocols/urp/Cache_Test.java: +void Test::testNothingLostFromLruList() { + int a[8]; + for (std::size_t i = 0; i != std::size(a); ++i) { + for (std::size_t j = 0; j != i; ++j) { + a[j] = 0; + } + for (;;) { + binaryurp::Cache< int > c(4); + for (std::size_t k = 0; k != i; ++k) { + bool f; + c.add(a[k], &f); + } + bool f; + CPPUNIT_ASSERT_EQUAL( + 6, + c.add(-1, &f) + c.add(-2, &f) + c.add(-3, &f) + c.add(-4, &f)); + std::size_t j = i; + while (j != 0 && a[j - 1] == 3) { + --j; + } + if (j == 0) { + break; + } + ++a[j - 1]; + for (std::size_t k = j; k != i; ++k) { + a[k] = 0; + } + } + } +} + +CPPUNIT_TEST_SUITE_REGISTRATION(Test); + +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/qa/test-unmarshal.cxx b/binaryurp/qa/test-unmarshal.cxx new file mode 100644 index 000000000..df3a96d8f --- /dev/null +++ b/binaryurp/qa/test-unmarshal.cxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <cppu/unotype.hxx> +#include <cppunit/TestAssert.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> +#include <rtl/ref.hxx> +#include <rtl/string.h> +#include <sal/types.h> +#include <typelib/typedescription.hxx> + +#include "../source/bridge.hxx" +#include "../source/cache.hxx" +#include "../source/readerstate.hxx" +#include "../source/unmarshal.hxx" + +namespace { + +class Test: public CppUnit::TestFixture { +private: + CPPUNIT_TEST_SUITE(Test); + CPPUNIT_TEST(testTypeOfBooleanSequence); + CPPUNIT_TEST(testTypeOfVoidSequence); + CPPUNIT_TEST_SUITE_END(); + + void testTypeOfBooleanSequence(); + + void testTypeOfVoidSequence(); +}; + +void Test::testTypeOfBooleanSequence() { + binaryurp::ReaderState state; + css::uno::Sequence<sal_Int8> buf{ + static_cast<sal_Int8>(static_cast<sal_uInt8>(20 | 0x80)), + // sequence type | cache flag + static_cast<sal_Int8>( + static_cast<sal_uInt8>(binaryurp::cache::ignore >> 8)), + static_cast<sal_Int8>( + static_cast<sal_uInt8>(binaryurp::cache::ignore & 0xFF)), + RTL_CONSTASCII_LENGTH("[]boolean"), + '[', ']', 'b', 'o', 'o', 'l', 'e', 'a', 'n' }; + binaryurp::Unmarshal m(rtl::Reference< binaryurp::Bridge >(), state, buf); + css::uno::TypeDescription t(m.readType()); + CPPUNIT_ASSERT( + t.equals( + css::uno::TypeDescription( + cppu::UnoType< css::uno::Sequence< bool > >::get()))); + m.done(); +} + +void Test::testTypeOfVoidSequence() { + binaryurp::ReaderState state; + css::uno::Sequence<sal_Int8> buf{ + static_cast<sal_Int8>(static_cast<sal_uInt8>(20 | 0x80)), + // sequence type | cache flag + static_cast<sal_Int8>( + static_cast<sal_uInt8>(binaryurp::cache::ignore >> 8)), + static_cast<sal_Int8>( + static_cast<sal_uInt8>(binaryurp::cache::ignore & 0xFF)), + RTL_CONSTASCII_LENGTH("[]void"), '[', ']', 'v', 'o', 'i', 'd' }; + binaryurp::Unmarshal m(rtl::Reference< binaryurp::Bridge >(), state, buf); + try { + m.readType(); + CPPUNIT_FAIL("exception expected"); + } catch (const css::io::IOException &) {} +} + +CPPUNIT_TEST_SUITE_REGISTRATION(Test); + +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/binaryany.cxx b/binaryurp/source/binaryany.cxx new file mode 100644 index 000000000..7f6e14a6f --- /dev/null +++ b/binaryurp/source/binaryany.cxx @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> +#include <utility> + +#include <typelib/typeclass.h> +#include <typelib/typedescription.hxx> +#include <uno/any2.h> + +#include "binaryany.hxx" + +namespace binaryurp { + +namespace { + +// Cf. com::sun::star::uno::Any move ctor in +// include/com/sun/star/uno/Any.hxx: +void moveInternals(uno_Any & from, uno_Any & to) { + uno_any_construct(&to, nullptr, nullptr, nullptr); + std::swap(from.pType, to.pType); + std::swap(from.pData, to.pData); + std::swap(from.pReserved, to.pReserved); + if (to.pData == &from.pReserved) { + to.pData = &to.pReserved; + } + // This leaves to.pData (where "to" is now VOID) dangling to somewhere (cf. + // CONSTRUCT_EMPTY_ANY, cppu/source/uno/prim.hxx), but what's relevant is + // only that it isn't a nullptr (as e.g. >>= -> uno_type_assignData -> + // _assignData takes a null pSource to mean "construct a default value"). +} + +} + +BinaryAny::BinaryAny() noexcept { + uno_any_construct(&data_, nullptr, nullptr, nullptr); +} + +BinaryAny::BinaryAny(css::uno::TypeDescription const & type, void * value) + noexcept +{ + assert(type.is()); + uno_any_construct(&data_, value, type.get(), nullptr); +} + +BinaryAny::BinaryAny(uno_Any const & raw) noexcept { + assert(raw.pType != nullptr); + data_.pType = raw.pType; + typelib_typedescriptionreference_acquire(data_.pType); + data_.pData = raw.pData == &raw.pReserved ? &data_.pReserved : raw.pData; + data_.pReserved = raw.pReserved; +} + +BinaryAny::BinaryAny(BinaryAny const & other) noexcept { + uno_type_any_construct(&data_, other.data_.pData, other.data_.pType, nullptr); +} + +BinaryAny::BinaryAny(BinaryAny && other) noexcept { + moveInternals(other.data_, data_); +} + +BinaryAny::~BinaryAny() noexcept { + uno_any_destruct(&data_, nullptr); +} + +BinaryAny & BinaryAny::operator =(BinaryAny const & other) noexcept { + if (&other != this) { + uno_type_any_assign(&data_, other.data_.pData, other.data_.pType, nullptr, nullptr); + } + return *this; +} + +BinaryAny & BinaryAny::operator =(BinaryAny && other) noexcept { + uno_any_destruct(&data_, nullptr); + moveInternals(other.data_, data_); + return *this; +} + +css::uno::TypeDescription BinaryAny::getType() const noexcept { + return css::uno::TypeDescription(data_.pType); +} + +void * BinaryAny::getValue(css::uno::TypeDescription const & type) const + noexcept +{ + assert(type.is()); + assert( + type.get()->eTypeClass == typelib_TypeClass_ANY || + type.equals(css::uno::TypeDescription(data_.pType))); + return type.get()->eTypeClass == typelib_TypeClass_ANY + ? &data_ : data_.pData; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/binaryany.hxx b/binaryurp/source/binaryany.hxx new file mode 100644 index 000000000..c04fe8f86 --- /dev/null +++ b/binaryurp/source/binaryany.hxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <uno/any2.h> + +namespace com::sun::star::uno { class TypeDescription; } + +namespace binaryurp { + +class BinaryAny { +public: + BinaryAny() noexcept; + + BinaryAny(com::sun::star::uno::TypeDescription const & type, void * value) + noexcept; + + explicit BinaryAny(uno_Any const & raw) noexcept; + // takes over raw.pData (but copies raw.pType); raw must not be passed + // to uno_any_destruct + + BinaryAny(BinaryAny const & other) noexcept; + + BinaryAny(BinaryAny && other) noexcept; + + ~BinaryAny() noexcept; + + BinaryAny & operator =(BinaryAny const & other) noexcept; + + BinaryAny & operator =(BinaryAny && other) noexcept; + + uno_Any& get() noexcept { return data_; } + + com::sun::star::uno::TypeDescription getType() const noexcept; + + void * getValue(com::sun::star::uno::TypeDescription const & type) const + noexcept; + +private: + mutable uno_Any data_; + // mutable so that getValue() can return a non-const void *, as in turn + // required at various places in binary UNO +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/binaryurp.component b/binaryurp/source/binaryurp.component new file mode 100644 index 000000000..b5b0f4da1 --- /dev/null +++ b/binaryurp/source/binaryurp.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.bridge.BridgeFactory" + constructor="com_sun_star_comp_bridge_BridgeFactory_get_implementation" single-instance="true"> + <service name="com.sun.star.bridge.BridgeFactory"/> + </implementation> +</component> diff --git a/binaryurp/source/bridge.cxx b/binaryurp/source/bridge.cxx new file mode 100644 index 000000000..7d73d6dd7 --- /dev/null +++ b/binaryurp/source/bridge.cxx @@ -0,0 +1,1054 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <limits> +#include <memory> +#include <utility> +#include <vector> + +#include <com/sun/star/bridge/InvalidProtocolChangeException.hpp> +#include <com/sun/star/bridge/XBridge.hpp> +#include <com/sun/star/bridge/XInstanceProvider.hpp> +#include <com/sun/star/bridge/XProtocolProperties.hpp> +#include <com/sun/star/connection/XConnection.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/EventObject.hpp> +#include <com/sun/star/lang/XEventListener.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/XInterface.hpp> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/weak.hxx> +#include <osl/mutex.hxx> +#include <osl/thread.hxx> +#include <rtl/byteseq.hxx> +#include <rtl/random.h> +#include <rtl/ref.hxx> +#include <rtl/string.h> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <sal/types.h> +#include <typelib/typeclass.h> +#include <typelib/typedescription.h> +#include <typelib/typedescription.hxx> +#include <uno/dispatcher.hxx> +#include <uno/environment.hxx> +#include <uno/lbnames.h> + +#include "binaryany.hxx" +#include "bridge.hxx" +#include "bridgefactory.hxx" +#include "incomingreply.hxx" +#include "lessoperators.hxx" +#include "outgoingrequest.hxx" +#include "outgoingrequests.hxx" +#include "proxy.hxx" +#include "reader.hxx" + +namespace binaryurp { + +namespace { + +sal_Int32 random() { + sal_Int32 n; + rtlRandomPool pool = rtl_random_createPool(); + rtl_random_getBytes(pool, &n, sizeof n); + rtl_random_destroyPool(pool); + return n; +} + +OUString toString(css::uno::TypeDescription const & type) { + typelib_TypeDescription * d = type.get(); + assert(d != nullptr && d->pTypeName != nullptr); + return OUString(d->pTypeName); +} + +extern "C" void freeProxyCallback( + SAL_UNUSED_PARAMETER uno_ExtEnvironment *, void * pProxy) +{ + assert(pProxy != nullptr); + static_cast< Proxy * >(pProxy)->do_free(); +} + +bool isThread(salhelper::Thread * thread) { + assert(thread != nullptr); + return osl::Thread::getCurrentIdentifier() == thread->getIdentifier(); +} + +class AttachThread { +public: + explicit AttachThread(uno_ThreadPool threadPool); + + ~AttachThread(); + + const rtl::ByteSequence& getTid() const noexcept { return tid_;} + +private: + AttachThread(const AttachThread&) = delete; + AttachThread& operator=(const AttachThread&) = delete; + + uno_ThreadPool threadPool_; + rtl::ByteSequence tid_; +}; + +AttachThread::AttachThread(uno_ThreadPool threadPool): threadPool_(threadPool) { + sal_Sequence * s = nullptr; + uno_getIdOfCurrentThread(&s); + tid_ = rtl::ByteSequence(s, rtl::BYTESEQ_NOACQUIRE); + uno_threadpool_attach(threadPool_); +} + +AttachThread::~AttachThread() { + uno_threadpool_detach(threadPool_); + uno_releaseIdFromCurrentThread(); +} + + +class PopOutgoingRequest { +public: + PopOutgoingRequest( + OutgoingRequests & requests, rtl::ByteSequence tid, + OutgoingRequest const & request); + + ~PopOutgoingRequest(); + + void clear(); + +private: + PopOutgoingRequest(const PopOutgoingRequest&) = delete; + PopOutgoingRequest& operator=(const PopOutgoingRequest&) = delete; + + OutgoingRequests & requests_; + rtl::ByteSequence tid_; + bool cleared_; +}; + +PopOutgoingRequest::PopOutgoingRequest( + OutgoingRequests & requests, rtl::ByteSequence tid, + OutgoingRequest const & request): + requests_(requests), tid_(std::move(tid)), cleared_(false) +{ + requests_.push(tid_, request); +} + +PopOutgoingRequest::~PopOutgoingRequest() { + if (!cleared_) { + requests_.pop(tid_); + } +} + +void PopOutgoingRequest::clear() { + cleared_ = true; +} + +} + +struct Bridge::SubStub { + com::sun::star::uno::UnoInterfaceReference object; + + sal_uInt32 references; +}; + +Bridge::Bridge( + rtl::Reference< BridgeFactory > const & factory, OUString name, + css::uno::Reference< css::connection::XConnection > const & connection, + css::uno::Reference< css::bridge::XInstanceProvider > provider): + factory_(factory), name_(std::move(name)), connection_(connection), + provider_(std::move(provider)), + binaryUno_(UNO_LB_UNO), + cppToBinaryMapping_(CPPU_CURRENT_LANGUAGE_BINDING_NAME, UNO_LB_UNO), + binaryToCppMapping_(UNO_LB_UNO, CPPU_CURRENT_LANGUAGE_BINDING_NAME), + protPropTid_( + reinterpret_cast< sal_Int8 const * >(".UrpProtocolPropertiesTid"), + RTL_CONSTASCII_LENGTH(".UrpProtocolPropertiesTid")), + protPropOid_("UrpProtocolProperties"), + protPropType_( + cppu::UnoType< + css::uno::Reference< css::bridge::XProtocolProperties > >::get()), + protPropRequest_("com.sun.star.bridge.XProtocolProperties::requestChange"), + protPropCommit_("com.sun.star.bridge.XProtocolProperties::commitChange"), + state_(STATE_INITIAL), threadPool_(nullptr), currentContextMode_(false), + proxies_(0), calls_(0), normalCall_(false), activeCalls_(0), + mode_(MODE_REQUESTED) +{ + assert(factory.is() && connection.is()); + if (!binaryUno_.is()) { + throw css::uno::RuntimeException("URP: no binary UNO environment"); + } + if (!(cppToBinaryMapping_.is() && binaryToCppMapping_.is())) { + throw css::uno::RuntimeException("URP: no C++ UNO mapping"); + } + passive_.set(); + // coverity[uninit_member] - random_ is set in due course by the reader_ thread's state machine +} + +void Bridge::start() { + rtl::Reference r(new Reader(this)); + rtl::Reference w(new Writer(this)); + { + std::lock_guard g(mutex_); + assert( + state_ == STATE_INITIAL && threadPool_ == nullptr && !writer_.is() && + !reader_.is()); + threadPool_ = uno_threadpool_create(); + assert(threadPool_ != nullptr); + reader_ = r; + writer_ = w; + state_ = STATE_STARTED; + } + // It is important to call reader_->launch() last here; both + // Writer::execute and Reader::execute can call Bridge::terminate, but + // Writer::execute is initially blocked in unblocked_.wait() until + // Reader::execute has called bridge_->sendRequestChangeRequest(), so + // effectively only reader_->launch() can lead to an early call to + // Bridge::terminate + w->launch(); + r->launch(); +} + +void Bridge::terminate(bool final) { + uno_ThreadPool tp; + // Make sure function-local variables (Stubs s, etc.) are destroyed before + // the final uno_threadpool_destroy/threadPool_ = 0: + { + rtl::Reference< Reader > r; + rtl::Reference< Writer > w; + bool joinW; + Listeners ls; + { + std::unique_lock g(mutex_); + switch (state_) { + case STATE_INITIAL: // via ~Bridge -> dispose -> terminate + case STATE_FINAL: + return; + case STATE_STARTED: + break; + case STATE_TERMINATED: + if (final) { + g.unlock(); + terminated_.wait(); + { + std::lock_guard g2(mutex_); + tp = threadPool_; + threadPool_ = nullptr; + if (reader_.is()) { + if (!isThread(reader_.get())) { + r = reader_; + } + reader_.clear(); + } + if (writer_.is()) { + if (!isThread(writer_.get())) { + w = writer_; + } + writer_.clear(); + } + state_ = STATE_FINAL; + } + assert(!(r.is() && w.is())); + if (r.is()) { + r->join(); + } else if (w.is()) { + w->join(); + } + if (tp != nullptr) { + uno_threadpool_destroy(tp); + } + } + return; + } + tp = threadPool_; + assert(!(final && isThread(reader_.get()))); + if (!isThread(reader_.get())) { + std::swap(reader_, r); + } + w = writer_; + joinW = !isThread(writer_.get()); + assert(!final || joinW); + if (joinW) { + writer_.clear(); + } + ls.swap(listeners_); + state_ = final ? STATE_FINAL : STATE_TERMINATED; + } + try { + connection_->close(); + } catch (const css::io::IOException & e) { + SAL_INFO("binaryurp", "caught IO exception '" << e << '\''); + } + assert(w.is()); + w->stop(); + if (r.is()) { + r->join(); + } + if (joinW) { + w->join(); + } + assert(tp != nullptr); + uno_threadpool_dispose(tp); + Stubs s; + { + std::lock_guard g(mutex_); + s.swap(stubs_); + } + for (auto & stub : s) + { + for (auto & item : stub.second) + { + SAL_INFO( + "binaryurp", + "stub '" << stub.first << "', '" << toString(item.first) + << "' still mapped at Bridge::terminate"); + binaryUno_.get()->pExtEnv->revokeInterface( + binaryUno_.get()->pExtEnv, item.second.object.get()); + } + } + factory_->removeBridge(this); + for (auto const& listener : ls) + { + try { + listener->disposing( + css::lang::EventObject( + static_cast< cppu::OWeakObject * >(this))); + } catch (const css::uno::RuntimeException & e) { + SAL_WARN("binaryurp", "caught " << e); + } + } + } + if (final) { + uno_threadpool_destroy(tp); + } + { + std::lock_guard g(mutex_); + if (final) { + threadPool_ = nullptr; + } + } + terminated_.set(); +} + + +BinaryAny Bridge::mapCppToBinaryAny(css::uno::Any const & cppAny) { + css::uno::Any in(cppAny); + BinaryAny out; + out.~BinaryAny(); + uno_copyAndConvertData( + &out.get(), &in, + css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()).get(), + cppToBinaryMapping_.get()); + return out; +} + +uno_ThreadPool Bridge::getThreadPool() { + std::lock_guard g(mutex_); + checkDisposed(); + assert(threadPool_ != nullptr); + return threadPool_; +} + +rtl::Reference< Writer > Bridge::getWriter() { + std::lock_guard g(mutex_); + checkDisposed(); + assert(writer_.is()); + return writer_; +} + +css::uno::UnoInterfaceReference Bridge::registerIncomingInterface( + OUString const & oid, css::uno::TypeDescription const & type) +{ + assert(type.is()); + if (oid.isEmpty()) { + return css::uno::UnoInterfaceReference(); + } + css::uno::UnoInterfaceReference obj(findStub(oid, type)); + if (!obj.is()) { + binaryUno_.get()->pExtEnv->getRegisteredInterface( + binaryUno_.get()->pExtEnv, + reinterpret_cast< void ** >(&obj.m_pUnoI), oid.pData, + reinterpret_cast< typelib_InterfaceTypeDescription * >(type.get())); + if (obj.is()) { + makeReleaseCall(oid, type); + } else { + obj.set(new Proxy(this, oid, type), SAL_NO_ACQUIRE); + { + std::lock_guard g(mutex_); + assert(proxies_ < std::numeric_limits< std::size_t >::max()); + ++proxies_; + } + binaryUno_.get()->pExtEnv->registerProxyInterface( + binaryUno_.get()->pExtEnv, + reinterpret_cast< void ** >(&obj.m_pUnoI), &freeProxyCallback, + oid.pData, + reinterpret_cast< typelib_InterfaceTypeDescription * >( + type.get())); + } + } + return obj; +} + +OUString Bridge::registerOutgoingInterface( + css::uno::UnoInterfaceReference const & object, + css::uno::TypeDescription const & type) +{ + assert(type.is()); + if (!object.is()) { + return OUString(); + } + OUString oid; + if (!Proxy::isProxy(this, object, &oid)) { + binaryUno_.get()->pExtEnv->getObjectIdentifier( + binaryUno_.get()->pExtEnv, &oid.pData, object.get()); + std::lock_guard g(mutex_); + Stubs::iterator i(stubs_.find(oid)); + Stub newStub; + Stub * stub = i == stubs_.end() ? &newStub : &i->second; + Stub::iterator j(stub->find(type)); + //TODO: Release sub-stub if it is not successfully sent to remote side + // (otherwise, stub will leak until terminate()): + if (j == stub->end()) { + j = stub->emplace(type, SubStub()).first; + if (stub == &newStub) { + i = stubs_.emplace(oid, Stub()).first; + std::swap(i->second, newStub); + j = i->second.find(type); + assert(j != i->second.end()); + } + j->second.object = object; + j->second.references = 1; + binaryUno_.get()->pExtEnv->registerInterface( + binaryUno_.get()->pExtEnv, + reinterpret_cast< void ** >(&j->second.object.m_pUnoI), + oid.pData, + reinterpret_cast< typelib_InterfaceTypeDescription * >( + type.get())); + } else { + assert(stub != &newStub); + if (j->second.references == SAL_MAX_UINT32) { + throw css::uno::RuntimeException( + "URP: stub reference count overflow"); + } + ++j->second.references; + } + } + return oid; +} + +css::uno::UnoInterfaceReference Bridge::findStub( + OUString const & oid, css::uno::TypeDescription const & type) +{ + assert(!oid.isEmpty() && type.is()); + std::lock_guard g(mutex_); + Stubs::iterator i(stubs_.find(oid)); + if (i != stubs_.end()) { + Stub::iterator j(i->second.find(type)); + if (j != i->second.end()) { + return j->second.object; + } + for (auto const& item : i->second) + { + if (typelib_typedescription_isAssignableFrom( + type.get(), item.first.get())) + { + return item.second.object; + } + } + } + return css::uno::UnoInterfaceReference(); +} + +void Bridge::releaseStub( + OUString const & oid, css::uno::TypeDescription const & type) +{ + assert(!oid.isEmpty() && type.is()); + css::uno::UnoInterfaceReference obj; + bool unused; + { + std::lock_guard g(mutex_); + Stubs::iterator i(stubs_.find(oid)); + if (i == stubs_.end()) { + throw css::uno::RuntimeException("URP: release unknown stub"); + } + Stub::iterator j(i->second.find(type)); + if (j == i->second.end()) { + throw css::uno::RuntimeException("URP: release unknown stub"); + } + assert(j->second.references > 0); + --j->second.references; + if (j->second.references == 0) { + obj = j->second.object; + i->second.erase(j); + if (i->second.empty()) { + stubs_.erase(i); + } + } + unused = becameUnused(); + } + if (obj.is()) { + binaryUno_.get()->pExtEnv->revokeInterface( + binaryUno_.get()->pExtEnv, obj.get()); + } + terminateWhenUnused(unused); +} + +void Bridge::resurrectProxy(Proxy & proxy) { + uno_Interface * p = &proxy; + binaryUno_.get()->pExtEnv->registerProxyInterface( + binaryUno_.get()->pExtEnv, + reinterpret_cast< void ** >(&p), &freeProxyCallback, + proxy.getOid().pData, + reinterpret_cast< typelib_InterfaceTypeDescription * >( + proxy.getType().get())); + assert(p == &proxy); +} + +void Bridge::revokeProxy(Proxy & proxy) { + binaryUno_.get()->pExtEnv->revokeInterface( + binaryUno_.get()->pExtEnv, &proxy); +} + +void Bridge::freeProxy(Proxy & proxy) { + try { + makeReleaseCall(proxy.getOid(), proxy.getType()); + } catch (const css::uno::RuntimeException & e) { + SAL_INFO( + "binaryurp", "caught runtime exception '" << e << '\''); + } catch (const std::exception & e) { + SAL_WARN("binaryurp", "caught C++ exception '" << e.what() << '\''); + } + bool unused; + { + std::lock_guard g(mutex_); + assert(proxies_ > 0); + --proxies_; + unused = becameUnused(); + } + terminateWhenUnused(unused); +} + +void Bridge::incrementCalls(bool normalCall) noexcept { + std::lock_guard g(mutex_); + assert(calls_ < std::numeric_limits< std::size_t >::max()); + ++calls_; + normalCall_ |= normalCall; +} + +void Bridge::decrementCalls() { + bool unused; + { + std::lock_guard g(mutex_); + assert(calls_ > 0); + --calls_; + unused = becameUnused(); + } + terminateWhenUnused(unused); +} + +void Bridge::incrementActiveCalls() noexcept { + std::lock_guard g(mutex_); + assert( + activeCalls_ <= calls_ && + activeCalls_ < std::numeric_limits< std::size_t >::max()); + ++activeCalls_; + passive_.reset(); +} + +void Bridge::decrementActiveCalls() noexcept { + std::lock_guard g(mutex_); + assert(activeCalls_ <= calls_ && activeCalls_ > 0); + --activeCalls_; + if (activeCalls_ == 0) { + passive_.set(); + } +} + +bool Bridge::makeCall( + OUString const & oid, css::uno::TypeDescription const & member, + bool setter, std::vector< BinaryAny >&& inArguments, + BinaryAny * returnValue, std::vector< BinaryAny > * outArguments) +{ + std::unique_ptr< IncomingReply > resp; + { + uno_ThreadPool tp = getThreadPool(); + AttachThread att(tp); + PopOutgoingRequest pop( + outgoingRequests_, att.getTid(), + OutgoingRequest(OutgoingRequest::KIND_NORMAL, member, setter)); + sendRequest( + att.getTid(), oid, css::uno::TypeDescription(), member, + std::move(inArguments)); + pop.clear(); + incrementCalls(true); + incrementActiveCalls(); + void * job; + uno_threadpool_enter(tp, &job); + resp.reset(static_cast< IncomingReply * >(job)); + decrementActiveCalls(); + decrementCalls(); + } + if (resp == nullptr) + { + throw css::lang::DisposedException( + "Binary URP bridge disposed during call", + static_cast< cppu::OWeakObject * >(this)); + } + *returnValue = resp->returnValue; + if (!resp->exception) { + *outArguments = resp->outArguments; + } + return resp->exception; +} + +void Bridge::sendRequestChangeRequest() { + assert(mode_ == MODE_REQUESTED); + random_ = random(); + std::vector< BinaryAny > a; + a.emplace_back( + css::uno::TypeDescription(cppu::UnoType< sal_Int32 >::get()), + &random_); + sendProtPropRequest(OutgoingRequest::KIND_REQUEST_CHANGE, a); +} + +void Bridge::handleRequestChangeReply( + bool exception, BinaryAny const & returnValue) +{ + try { + throwException(exception, returnValue); + } catch (css::uno::RuntimeException & e) { + // Before OOo 2.2, Java URP would throw a RuntimeException when + // receiving a requestChange message (see i#35277 "Java URP: Support + // Manipulation of Protocol Properties"): + if (mode_ != MODE_REQUESTED) { + throw; + } + SAL_WARN( + "binaryurp", + "requestChange caught " << e << " in state 'requested'"); + mode_ = MODE_NORMAL; + getWriter()->unblock(); + decrementCalls(); + return; + } + sal_Int32 n = *static_cast< sal_Int32 * >( + returnValue.getValue( + css::uno::TypeDescription(cppu::UnoType< sal_Int32 >::get()))); + sal_Int32 exp = 0; + switch (mode_) { + case MODE_REQUESTED: + case MODE_REPLY_1: + exp = 1; + break; + case MODE_REPLY_MINUS1: + exp = -1; + mode_ = MODE_REQUESTED; + break; + case MODE_REPLY_0: + exp = 0; + mode_ = MODE_WAIT; + break; + default: + assert(false); // this cannot happen + break; + } + if (n != exp) { + throw css::uno::RuntimeException( + "URP: requestChange reply with unexpected return value received", + static_cast< cppu::OWeakObject * >(this)); + } + decrementCalls(); + switch (exp) { + case -1: + sendRequestChangeRequest(); + break; + case 0: + break; + case 1: + sendCommitChangeRequest(); + break; + default: + assert(false); // this cannot happen + break; + } +} + +void Bridge::handleCommitChangeReply( + bool exception, BinaryAny const & returnValue) +{ + bool bCcMode = true; + try { + throwException(exception, returnValue); + } catch (const css::bridge::InvalidProtocolChangeException &) { + bCcMode = false; + } + if (bCcMode) { + setCurrentContextMode(); + } + assert(mode_ == MODE_REQUESTED || mode_ == MODE_REPLY_1); + mode_ = MODE_NORMAL; + getWriter()->unblock(); + decrementCalls(); +} + +void Bridge::handleRequestChangeRequest( + rtl::ByteSequence const & tid, std::vector< BinaryAny > const & inArguments) +{ + assert(inArguments.size() == 1); + switch (mode_) { + case MODE_REQUESTED: + { + sal_Int32 n2 = *static_cast< sal_Int32 * >( + inArguments[0].getValue( + css::uno::TypeDescription( + cppu::UnoType< sal_Int32 >::get()))); + sal_Int32 ret; + if (n2 > random_) { + ret = 1; + mode_ = MODE_REPLY_0; + } else if (n2 == random_) { + ret = -1; + mode_ = MODE_REPLY_MINUS1; + } else { + ret = 0; + mode_ = MODE_REPLY_1; + } + getWriter()->sendDirectReply( + tid, protPropRequest_, false, + BinaryAny( + css::uno::TypeDescription( + cppu::UnoType< sal_Int32 >::get()), + &ret), + std::vector< BinaryAny >()); + break; + } + case MODE_NORMAL: + { + mode_ = MODE_NORMAL_WAIT; + sal_Int32 ret = 1; + getWriter()->queueReply( + tid, protPropRequest_, false, false, + BinaryAny( + css::uno::TypeDescription( + cppu::UnoType< sal_Int32 >::get()), + &ret), + std::vector< BinaryAny >(), false); + break; + } + default: + throw css::uno::RuntimeException( + "URP: unexpected requestChange request received", + static_cast< cppu::OWeakObject * >(this)); + } +} + +void Bridge::handleCommitChangeRequest( + rtl::ByteSequence const & tid, std::vector< BinaryAny > const & inArguments) +{ + bool bCcMode = false; + bool bExc = false; + BinaryAny ret; + assert(inArguments.size() == 1); + css::uno::Sequence< css::bridge::ProtocolProperty > s; + [[maybe_unused]] bool ok = (mapBinaryToCppAny(inArguments[0]) >>= s); + assert(ok); + for (const auto & pp : std::as_const(s)) { + if (pp.Name == "CurrentContext") { + bCcMode = true; + } else { + bCcMode = false; + bExc = true; + ret = mapCppToBinaryAny( + css::uno::Any( + css::bridge::InvalidProtocolChangeException( + "InvalidProtocolChangeException", + css::uno::Reference< css::uno::XInterface >(), pp, + 1))); + break; + } + } + switch (mode_) { + case MODE_WAIT: + getWriter()->sendDirectReply( + tid, protPropCommit_, bExc, ret, std::vector< BinaryAny >()); + if (bCcMode) { + setCurrentContextMode(); + mode_ = MODE_NORMAL; + getWriter()->unblock(); + } else { + mode_ = MODE_REQUESTED; + sendRequestChangeRequest(); + } + break; + case MODE_NORMAL_WAIT: + getWriter()->queueReply( + tid, protPropCommit_, false, false, ret, std::vector< BinaryAny >(), + bCcMode); + mode_ = MODE_NORMAL; + break; + default: + throw css::uno::RuntimeException( + "URP: unexpected commitChange request received", + static_cast< cppu::OWeakObject * >(this)); + } +} + +OutgoingRequest Bridge::lastOutgoingRequest(rtl::ByteSequence const & tid) { + OutgoingRequest req(outgoingRequests_.top(tid)); + outgoingRequests_.pop(tid); + return req; +} + +bool Bridge::isProtocolPropertiesRequest( + std::u16string_view oid, css::uno::TypeDescription const & type) const +{ + return oid == protPropOid_ && type.equals(protPropType_); +} + +void Bridge::setCurrentContextMode() { + std::lock_guard g(mutex_); + currentContextMode_ = true; +} + +bool Bridge::isCurrentContextMode() { + std::lock_guard g(mutex_); + return currentContextMode_; +} + +Bridge::~Bridge() { +#if OSL_DEBUG_LEVEL > 0 + { + std::lock_guard g(mutex_); + SAL_WARN_IF( + state_ == STATE_STARTED || state_ == STATE_TERMINATED, "binaryurp", + "undisposed bridge \"" << name_ <<"\" in state " << state_ + << ", potential deadlock ahead"); + } +#endif + dispose(); +} + +css::uno::Reference< css::uno::XInterface > Bridge::getInstance( + OUString const & sInstanceName) +{ + if (sInstanceName.isEmpty()) { + throw css::uno::RuntimeException( + "XBridge::getInstance sInstanceName must be non-empty", + static_cast< cppu::OWeakObject * >(this)); + } + for (sal_Int32 i = 0; i != sInstanceName.getLength(); ++i) { + if (sInstanceName[i] > 0x7F) { + throw css::uno::RuntimeException( + "XBridge::getInstance sInstanceName contains non-ASCII" + " character"); + } + } + css::uno::TypeDescription ifc(cppu::UnoType<css::uno::XInterface>::get()); + typelib_TypeDescription * p = ifc.get(); + std::vector< BinaryAny > inArgs; + inArgs.emplace_back( + css::uno::TypeDescription(cppu::UnoType< css::uno::Type >::get()), + &p); + BinaryAny ret; + std::vector< BinaryAny> outArgs; + bool bExc = makeCall( + sInstanceName, + css::uno::TypeDescription( + "com.sun.star.uno.XInterface::queryInterface"), + false, std::move(inArgs), &ret, &outArgs); + throwException(bExc, ret); + auto const t = ret.getType(); + if (t.get()->eTypeClass == typelib_TypeClass_VOID) { + return {}; + } + if (!t.equals(ifc)) { + throw css::uno::RuntimeException( + "initial object queryInterface for OID \"" + sInstanceName + "\" returned ANY of type " + + OUString::unacquired(&t.get()->pTypeName)); + } + auto const val = *static_cast< uno_Interface ** >(ret.getValue(ifc)); + if (val == nullptr) { + throw css::uno::RuntimeException( + "initial object queryInterface for OID \"" + sInstanceName + + "\" returned null css.uno.XInterface ANY"); + } + return css::uno::Reference< css::uno::XInterface >( + static_cast< css::uno::XInterface * >( + binaryToCppMapping_.mapInterface( + val, + ifc.get())), + SAL_NO_ACQUIRE); +} + +OUString Bridge::getName() { + return name_; +} + +OUString Bridge::getDescription() { + OUString b = name_ + ":" + connection_->getDescription(); + return b; +} + +void Bridge::dispose() { + // For terminate(true) not to deadlock, an external protocol must ensure + // that dispose is not called from a thread pool worker thread (that dispose + // is never called from the reader or writer thread is already ensured + // internally): + terminate(true); + // OOo expects dispose to not return while there are still remote calls in + // progress; an external protocol must ensure that dispose is not called + // from within an incoming or outgoing remote call, as passive_.wait() would + // otherwise deadlock: + passive_.wait(); +} + +void Bridge::addEventListener( + css::uno::Reference< css::lang::XEventListener > const & xListener) +{ + assert(xListener.is()); + { + std::lock_guard g(mutex_); + assert(state_ != STATE_INITIAL); + if (state_ == STATE_STARTED) { + listeners_.push_back(xListener); + return; + } + } + xListener->disposing( + css::lang::EventObject(static_cast< cppu::OWeakObject * >(this))); +} + +void Bridge::removeEventListener( + css::uno::Reference< css::lang::XEventListener > const & aListener) +{ + std::lock_guard g(mutex_); + Listeners::iterator i( + std::find(listeners_.begin(), listeners_.end(), aListener)); + if (i != listeners_.end()) { + listeners_.erase(i); + } +} + +void Bridge::sendCommitChangeRequest() { + assert(mode_ == MODE_REQUESTED || mode_ == MODE_REPLY_1); + css::uno::Sequence< css::bridge::ProtocolProperty > s(1); + s.getArray()[0].Name = "CurrentContext"; + std::vector< BinaryAny > a { mapCppToBinaryAny(css::uno::Any(s)) }; + sendProtPropRequest(OutgoingRequest::KIND_COMMIT_CHANGE, a); +} + +void Bridge::sendProtPropRequest( + OutgoingRequest::Kind kind, std::vector< BinaryAny > const & inArguments) +{ + assert( + kind == OutgoingRequest::KIND_REQUEST_CHANGE || + kind == OutgoingRequest::KIND_COMMIT_CHANGE); + incrementCalls(false); + css::uno::TypeDescription member( + kind == OutgoingRequest::KIND_REQUEST_CHANGE + ? protPropRequest_ : protPropCommit_); + PopOutgoingRequest pop( + outgoingRequests_, protPropTid_, OutgoingRequest(kind, member, false)); + getWriter()->sendDirectRequest( + protPropTid_, protPropOid_, protPropType_, member, inArguments); + pop.clear(); +} + +void Bridge::makeReleaseCall( + OUString const & oid, css::uno::TypeDescription const & type) +{ + //HACK to decouple the processing of release calls from all other threads. Normally, sending + // the release request should use the current thread's TID (via AttachThread), which would cause + // that asynchronous request to be processed by a physical thread that is paired with the + // physical thread processing the normal synchronous call stack (see ThreadIdHashMap in + // cppu/source/threadpool/threadpool.hxx). However, that can lead to deadlock when a thread + // illegally makes a synchronous UNO call with the SolarMutex locked (e.g., + // SfxBaseModel::postEvent_Impl in sfx2/source/doc/sfxbasemodel.cxx doing documentEventOccurred + // and notifyEvent calls), and while that call is on the stack the remote side sends back some + // release request on the same logical UNO thread for an object that wants to acquire the + // SolarMutex in its destructor (e.g., SwXTextDocument in sw/inc/unotxdoc.hxx holding its + // m_pImpl via an sw::UnoImplPtr). While the correct approach would be to not make UNO calls + // with the SolarMutex (or any other mutex) locked, fixing that would probably be a heroic + // effort. So for now live with this hack, hoping that it does not introduce any new issues of + // its own: + static auto const tid = [] { + static sal_Int8 const id[] = {'r', 'e', 'l', 'e', 'a', 's', 'e', 'h', 'a', 'c', 'k'}; + return rtl::ByteSequence(id, std::size(id)); + }(); + sendRequest( + tid, oid, type, + css::uno::TypeDescription("com.sun.star.uno.XInterface::release"), + std::vector< BinaryAny >()); +} + +void Bridge::sendRequest( + rtl::ByteSequence const & tid, OUString const & oid, + css::uno::TypeDescription const & type, + css::uno::TypeDescription const & member, + std::vector< BinaryAny >&& inArguments) +{ + getWriter()->queueRequest(tid, oid, type, member, std::move(inArguments)); +} + +void Bridge::throwException(bool exception, BinaryAny const & value) { + if (exception) { + cppu::throwException(mapBinaryToCppAny(value)); + } +} + +css::uno::Any Bridge::mapBinaryToCppAny(BinaryAny const & binaryAny) { + BinaryAny in(binaryAny); + css::uno::Any out; + out.~Any(); + uno_copyAndConvertData( + &out, &in.get(), + css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()).get(), + binaryToCppMapping_.get()); + return out; +} + +bool Bridge::becameUnused() const { + return stubs_.empty() && proxies_ == 0 && calls_ == 0 && normalCall_; +} + +void Bridge::terminateWhenUnused(bool unused) { + if (unused) { + // That the current thread considers the bridge unused implies that it + // is not within an incoming or outgoing remote call (so calling + // terminate cannot lead to deadlock): + terminate(false); + } +} + +void Bridge::checkDisposed() { + assert(state_ != STATE_INITIAL); + if (state_ != STATE_STARTED) { + throw css::lang::DisposedException( + "Binary URP bridge already disposed", + static_cast< cppu::OWeakObject * >(this)); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/bridge.hxx b/binaryurp/source/bridge.hxx new file mode 100644 index 000000000..9da6640fa --- /dev/null +++ b/binaryurp/source/bridge.hxx @@ -0,0 +1,281 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <cstddef> +#include <map> +#include <mutex> +#include <vector> + +#include <com/sun/star/bridge/XBridge.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <cppuhelper/implbase.hxx> +#include <osl/conditn.hxx> +#include <osl/mutex.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <uno/environment.hxx> +#include <uno/mapping.hxx> +#include <uno/threadpool.h> + +#include "outgoingrequest.hxx" +#include "outgoingrequests.hxx" +#include "writer.hxx" + +namespace binaryurp { + class BinaryAny; + class BridgeFactory; + class Proxy; + class Reader; +} +namespace com::sun::star { + namespace bridge { class XInstanceProvider; } + namespace connection { class XConnection; } + namespace lang { class XEventListener; } + namespace uno { + class Any; + class TypeDescription; + class UnoInterfaceReference; + class XInterface; + } +} +namespace rtl { class ByteSequence; } + +namespace binaryurp { + +class Bridge: + public cppu::WeakImplHelper< + com::sun::star::bridge::XBridge, com::sun::star::lang::XComponent > +{ +public: + Bridge( + rtl::Reference< BridgeFactory > const & factory, + OUString name, + com::sun::star::uno::Reference< + com::sun::star::connection::XConnection > const & connection, + com::sun::star::uno::Reference< + com::sun::star::bridge::XInstanceProvider > provider); + + void start(); + + // Internally waits for all incoming and outgoing remote calls to terminate, + // so must not be called from within such a call; when final is true, also + // joins all remaining threads (reader, writer, and worker threads from the + // thread pool), so must not be called with final set to true from such a + // thread: + void terminate(bool final); + + const com::sun::star::uno::Reference< com::sun::star::connection::XConnection >& + getConnection() const { return connection_;} + + const com::sun::star::uno::Reference< com::sun::star::bridge::XInstanceProvider >& + getProvider() const { return provider_;} + + com::sun::star::uno::Mapping & getCppToBinaryMapping() { return cppToBinaryMapping_;} + + BinaryAny mapCppToBinaryAny(com::sun::star::uno::Any const & cppAny); + + uno_ThreadPool getThreadPool(); + + rtl::Reference< Writer > getWriter(); + + com::sun::star::uno::UnoInterfaceReference registerIncomingInterface( + OUString const & oid, + com::sun::star::uno::TypeDescription const & type); + + OUString registerOutgoingInterface( + com::sun::star::uno::UnoInterfaceReference const & object, + com::sun::star::uno::TypeDescription const & type); + + com::sun::star::uno::UnoInterfaceReference findStub( + OUString const & oid, + com::sun::star::uno::TypeDescription const & type); + + void releaseStub( + OUString const & oid, + com::sun::star::uno::TypeDescription const & type); + + void resurrectProxy(Proxy & proxy); + + void revokeProxy(Proxy & proxy); + + void freeProxy(Proxy & proxy); + + void incrementCalls(bool normalCall) noexcept; + + void decrementCalls(); + + void incrementActiveCalls() noexcept; + + void decrementActiveCalls() noexcept; + + bool makeCall( + OUString const & oid, + com::sun::star::uno::TypeDescription const & member, bool setter, + std::vector< BinaryAny >&& inArguments, BinaryAny * returnValue, + std::vector< BinaryAny > * outArguments); + + // Only called from reader_ thread: + void sendRequestChangeRequest(); + + // Only called from reader_ thread: + void handleRequestChangeReply( + bool exception, BinaryAny const & returnValue); + + // Only called from reader_ thread: + void handleCommitChangeReply(bool exception, BinaryAny const & returnValue); + + // Only called from reader_ thread: + void handleRequestChangeRequest( + rtl::ByteSequence const & tid, + std::vector< BinaryAny > const & inArguments); + + // Only called from reader_ thread: + void handleCommitChangeRequest( + rtl::ByteSequence const & tid, + std::vector< BinaryAny > const & inArguments); + + OutgoingRequest lastOutgoingRequest(rtl::ByteSequence const & tid); + + bool isProtocolPropertiesRequest( + std::u16string_view oid, + com::sun::star::uno::TypeDescription const & type) const; + + void setCurrentContextMode(); + + bool isCurrentContextMode(); + +private: + Bridge(const Bridge&) = delete; + Bridge& operator=(const Bridge&) = delete; + + virtual ~Bridge() override; + + virtual com::sun::star::uno::Reference< com::sun::star::uno::XInterface > + SAL_CALL getInstance(OUString const & sInstanceName) override; + + virtual OUString SAL_CALL getName() override; + + virtual OUString SAL_CALL getDescription() override; + + virtual void SAL_CALL dispose() override; + + virtual void SAL_CALL addEventListener( + com::sun::star::uno::Reference< com::sun::star::lang::XEventListener > + const & xListener) override; + + virtual void SAL_CALL removeEventListener( + com::sun::star::uno::Reference< com::sun::star::lang::XEventListener > + const & aListener) override; + + // Only called from reader_ thread: + void sendCommitChangeRequest(); + + // Only called from reader_ thread: + void sendProtPropRequest( + OutgoingRequest::Kind kind, + std::vector< BinaryAny > const & inArguments); + + void makeReleaseCall( + OUString const & oid, + com::sun::star::uno::TypeDescription const & type); + + void sendRequest( + rtl::ByteSequence const & tid, OUString const & oid, + com::sun::star::uno::TypeDescription const & type, + com::sun::star::uno::TypeDescription const & member, + std::vector< BinaryAny >&& inArguments); + + void throwException(bool exception, BinaryAny const & value); + + com::sun::star::uno::Any mapBinaryToCppAny(BinaryAny const & binaryAny); + + bool becameUnused() const; + + void terminateWhenUnused(bool unused); + + // Must only be called with mutex_ locked: + void checkDisposed(); + + typedef + std::vector< + com::sun::star::uno::Reference< + com::sun::star::lang::XEventListener > > + Listeners; + + struct SubStub; + + typedef std::map< com::sun::star::uno::TypeDescription, SubStub > Stub; + + typedef std::map< OUString, Stub > Stubs; + + enum State { STATE_INITIAL, STATE_STARTED, STATE_TERMINATED, STATE_FINAL }; + + enum Mode { + MODE_REQUESTED, MODE_REPLY_MINUS1, MODE_REPLY_0, MODE_REPLY_1, + MODE_WAIT, MODE_NORMAL, MODE_NORMAL_WAIT }; + + rtl::Reference< BridgeFactory > factory_; + OUString name_; + com::sun::star::uno::Reference< com::sun::star::connection::XConnection > + connection_; + com::sun::star::uno::Reference< com::sun::star::bridge::XInstanceProvider > + provider_; + com::sun::star::uno::Environment binaryUno_; + com::sun::star::uno::Mapping cppToBinaryMapping_; + com::sun::star::uno::Mapping binaryToCppMapping_; + rtl::ByteSequence protPropTid_; + OUString protPropOid_; + com::sun::star::uno::TypeDescription protPropType_; + com::sun::star::uno::TypeDescription protPropRequest_; + com::sun::star::uno::TypeDescription protPropCommit_; + OutgoingRequests outgoingRequests_; + osl::Condition passive_; + // to guarantee that passive_ is eventually set (to avoid deadlock, see + // dispose), activeCalls_ only counts those calls for which it can be + // guaranteed that incrementActiveCalls is indeed followed by + // decrementActiveCalls, without an intervening exception + osl::Condition terminated_; + + std::mutex mutex_; + State state_; + Listeners listeners_; + uno_ThreadPool threadPool_; + rtl::Reference< Writer > writer_; + rtl::Reference< Reader > reader_; + bool currentContextMode_; + Stubs stubs_; + std::size_t proxies_; + std::size_t calls_; + bool normalCall_; + std::size_t activeCalls_; + + // Only accessed from reader_ thread: + Mode mode_; + sal_Int32 random_; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/bridgefactory.cxx b/binaryurp/source/bridgefactory.cxx new file mode 100644 index 000000000..a2dd1fa97 --- /dev/null +++ b/binaryurp/source/bridgefactory.cxx @@ -0,0 +1,192 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <algorithm> +#include <cassert> + +#include <com/sun/star/bridge/BridgeExistsException.hpp> +#include <com/sun/star/connection/XConnection.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/safeint.hxx> +#include <rtl/ref.hxx> +#include <sal/log.hxx> +#include <sal/types.h> + +#include "bridge.hxx" +#include "bridgefactory.hxx" + +namespace binaryurp { + +void BridgeFactory::removeBridge( + css::uno::Reference< css::bridge::XBridge > const & bridge) +{ + assert(bridge.is()); + OUString n(bridge->getName()); + osl::MutexGuard g(m_aMutex); + if (n.isEmpty()) + { + unnamed_.erase(std::remove(unnamed_.begin(), unnamed_.end(), bridge), unnamed_.end()); + } + else + { + BridgeMap::iterator i(named_.find(n)); + if (i != named_.end() && i->second == bridge) + named_.erase(i); + } +} + +BridgeFactory::BridgeFactory(): + BridgeFactoryBase(m_aMutex) +{ +} + +BridgeFactory::~BridgeFactory() {} + +OUString BridgeFactory::getImplementationName() +{ + return "com.sun.star.comp.bridge.BridgeFactory"; +} + +sal_Bool BridgeFactory::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence< OUString > BridgeFactory::getSupportedServiceNames() +{ + return { "com.sun.star.bridge.BridgeFactory" }; +} + +css::uno::Reference< css::bridge::XBridge > BridgeFactory::createBridge( + OUString const & sName, OUString const & sProtocol, + css::uno::Reference< css::connection::XConnection > const & aConnection, + css::uno::Reference< css::bridge::XInstanceProvider > const & + anInstanceProvider) +{ + rtl::Reference< Bridge > b; + { + osl::MutexGuard g(m_aMutex); + if (rBHelper.bDisposed) { + throw css::lang::DisposedException( + "BridgeFactory disposed", + static_cast< cppu::OWeakObject * >(this)); + } + if (named_.find(sName) != named_.end()) { + throw css::bridge::BridgeExistsException( + sName, static_cast< cppu::OWeakObject * >(this)); + } + if (sProtocol != "urp" || !aConnection.is()) { + throw css::lang::IllegalArgumentException( + ("BridgeFactory::createBridge: sProtocol != urp ||" + " aConnection == null"), + static_cast< cppu::OWeakObject * >(this), -1); + } + b.set(new Bridge(this, sName, aConnection, anInstanceProvider)); + if (sName.isEmpty()) { + unnamed_.emplace_back(b.get()); + } else { + named_[sName] = b.get(); + } + } + b->start(); + return b; +} + +css::uno::Reference< css::bridge::XBridge > BridgeFactory::getBridge( + OUString const & sName) +{ + osl::MutexGuard g(m_aMutex); + BridgeMap::iterator i(named_.find(sName)); + return i == named_.end() + ? css::uno::Reference< css::bridge::XBridge >() : i->second; +} + +css::uno::Sequence< css::uno::Reference< css::bridge::XBridge > > +BridgeFactory::getExistingBridges() { + osl::MutexGuard g(m_aMutex); + if (unnamed_.size() > SAL_MAX_INT32) { + throw css::uno::RuntimeException( + "BridgeFactory::getExistingBridges: too many", + static_cast< cppu::OWeakObject * >(this)); + } + sal_Int32 n = static_cast< sal_Int32 >(unnamed_.size()); + if (named_.size() > o3tl::make_unsigned(SAL_MAX_INT32 - n)) { + throw css::uno::RuntimeException( + "BridgeFactory::getExistingBridges: too many", + static_cast< cppu::OWeakObject * >(this)); + } + n = static_cast< sal_Int32 >(n + named_.size()); + css::uno::Sequence< css::uno::Reference< css::bridge::XBridge > > s(n); + auto r = asNonConstRange(s); + sal_Int32 i = 0; + for (auto const& item : unnamed_) + r[i++] = item; + + for (auto const& item : named_) + r[i++] = item.second; + + return s; +} + +void BridgeFactory::disposing() { + BridgeVector l1; + BridgeMap l2; + { + osl::MutexGuard g(m_aMutex); + l1.swap(unnamed_); + l2.swap(named_); + } + for (auto const& item : l1) + { + try { + css::uno::Reference<css::lang::XComponent>( + item, css::uno::UNO_QUERY_THROW)->dispose(); + } catch (css::uno::Exception & e) { + SAL_WARN("binaryurp", "ignoring " << e); + } + } + for (auto const& item : l2) + { + try { + css::uno::Reference<css::lang::XComponent>( + item.second, css::uno::UNO_QUERY_THROW)->dispose(); + } catch (css::uno::Exception & e) { + SAL_WARN("binaryurp", "ignoring " << e); + } + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_bridge_BridgeFactory_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new binaryurp::BridgeFactory); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/bridgefactory.hxx b/binaryurp/source/bridgefactory.hxx new file mode 100644 index 000000000..590238960 --- /dev/null +++ b/binaryurp/source/bridgefactory.hxx @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <vector> +#include <map> + +#include <com/sun/star/bridge/XBridgeFactory2.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <sal/types.h> + +namespace com::sun::star { + namespace connection { class XConnection; } + namespace uno { + class XComponentContext; + class XInterface; + } +} + +namespace binaryurp { + +// That BridgeFactory derives from XComponent appears to be a historic mistake; +// the implementation does not care about a disposed state: + +typedef + cppu::WeakComponentImplHelper< + com::sun::star::lang::XServiceInfo, + com::sun::star::bridge::XBridgeFactory2 > + BridgeFactoryBase; + +class BridgeFactory : private cppu::BaseMutex, public BridgeFactoryBase +{ +public: + void removeBridge( + com::sun::star::uno::Reference< com::sun::star::bridge::XBridge > + const & bridge); + + using BridgeFactoryBase::acquire; + using BridgeFactoryBase::release; + + BridgeFactory(const BridgeFactory&) = delete; + BridgeFactory& operator=(const BridgeFactory&) = delete; + + BridgeFactory(); + + virtual ~BridgeFactory() override; + +private: + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + virtual com::sun::star::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + virtual com::sun::star::uno::Reference< com::sun::star::bridge::XBridge > + SAL_CALL createBridge( + OUString const & sName, OUString const & sProtocol, + com::sun::star::uno::Reference< + com::sun::star::connection::XConnection > const & aConnection, + com::sun::star::uno::Reference< + com::sun::star::bridge::XInstanceProvider > const & + anInstanceProvider) override; + + virtual com::sun::star::uno::Reference< com::sun::star::bridge::XBridge > + SAL_CALL getBridge( + OUString const & sName) override; + + virtual + com::sun::star::uno::Sequence< + com::sun::star::uno::Reference< com::sun::star::bridge::XBridge > > + SAL_CALL getExistingBridges() override; + + void SAL_CALL disposing() override; + + typedef + std::vector< + com::sun::star::uno::Reference< com::sun::star::bridge::XBridge > > + BridgeVector; + + typedef + std::map< + OUString, + com::sun::star::uno::Reference< com::sun::star::bridge::XBridge > > + BridgeMap; + + BridgeVector unnamed_; + BridgeMap named_; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/cache.hxx b/binaryurp/source/cache.hxx new file mode 100644 index 000000000..bd8648efd --- /dev/null +++ b/binaryurp/source/cache.hxx @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <cassert> +#include <cstddef> +#include <map> +#include <list> + +#include <sal/types.h> + +namespace binaryurp { + +namespace cache { + +enum { size = 256, ignore = 0xFFFF }; + +} + +template< typename T > class Cache { +public: + typedef sal_uInt16 IdxType; + + explicit Cache(std::size_t size): + size_(size) + { + assert(size < cache::ignore); + } + + IdxType add( const T& rContent, bool* pbFound) { + assert( pbFound != nullptr); + if( !size_) { + *pbFound = false; + return cache::ignore; + } + // try to insert into the map + list_.push_front( rContent); // create a temp entry + auto const [it, inserted] = map_.emplace( list_.begin(), 0 ); + *pbFound = !inserted; + + if( !inserted) { // insertion not needed => found the entry + list_.pop_front(); // remove the temp entry + list_.splice( list_.begin(), list_, it->first); // the found entry is moved to front + return it->second; + } + + // test insertion successful => it was new so we keep it + IdxType n = static_cast<IdxType>( map_.size() - 1); + if( n >= size_) { // cache full => replace the LRU entry + // find the least recently used element in the map + typename LruItMap::iterator lru = map_.find( --list_.end()); + n = lru->second; + map_.erase( lru); // remove it from the map + list_.pop_back(); // remove from the list + } + it->second = n; + return n; + } + +private: + Cache(const Cache&) = delete; + Cache& operator=(const Cache&) = delete; + + typedef std::list<T> LruList; // last recently used list + typedef typename LruList::iterator LruListIt; + struct CmpT{ bool operator()( const LruListIt& rA, const LruListIt& rB) const { return (*rA<*rB);}}; + typedef std::map< LruListIt, IdxType, CmpT > LruItMap; // a map into a LruList + + std::size_t size_; + LruItMap map_; + LruList list_; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/currentcontext.cxx b/binaryurp/source/currentcontext.cxx new file mode 100644 index 000000000..acaf606d2 --- /dev/null +++ b/binaryurp/source/currentcontext.cxx @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/uno/RuntimeException.hpp> +#include <rtl/ustring.hxx> +#include <uno/current_context.h> +#include <uno/dispatcher.hxx> +#include <uno/lbnames.h> + +#include "currentcontext.hxx" + +namespace binaryurp::current_context { + +css::uno::UnoInterfaceReference get() { + css::uno::UnoInterfaceReference cc; + if (!uno_getCurrentContext( + reinterpret_cast< void ** >(&cc.m_pUnoI), + OUString(UNO_LB_UNO).pData, nullptr)) + { + throw css::uno::RuntimeException("uno_getCurrentContext failed"); + } + return cc; +} + +void set(css::uno::UnoInterfaceReference const & value) { + css::uno::UnoInterfaceReference cc(value); + if (!uno_setCurrentContext( + cc.m_pUnoI, + OUString(UNO_LB_UNO).pData, nullptr)) + { + throw css::uno::RuntimeException("uno_setCurrentContext failed"); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/currentcontext.hxx b/binaryurp/source/currentcontext.hxx new file mode 100644 index 000000000..b5f55a824 --- /dev/null +++ b/binaryurp/source/currentcontext.hxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +namespace com::sun::star::uno +{ +class UnoInterfaceReference; +} + +namespace binaryurp::current_context +{ +com::sun::star::uno::UnoInterfaceReference get(); + +void set(com::sun::star::uno::UnoInterfaceReference const& value); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/incomingreply.hxx b/binaryurp/source/incomingreply.hxx new file mode 100644 index 000000000..c2f5353ff --- /dev/null +++ b/binaryurp/source/incomingreply.hxx @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <utility> +#include <vector> + +#include "binaryany.hxx" + +namespace binaryurp { + +struct IncomingReply { +private: + IncomingReply(const IncomingReply&) = delete; + IncomingReply& operator=(const IncomingReply&) = delete; +public: + IncomingReply( + bool theException, BinaryAny theReturnValue, + std::vector< BinaryAny >&& theOutArguments): + exception(theException), returnValue(std::move(theReturnValue)), + outArguments(std::move(theOutArguments)) + {} + + bool exception; + + BinaryAny returnValue; + + std::vector< BinaryAny > outArguments; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/incomingrequest.cxx b/binaryurp/source/incomingrequest.cxx new file mode 100644 index 000000000..6f4107693 --- /dev/null +++ b/binaryurp/source/incomingrequest.cxx @@ -0,0 +1,286 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> +#include <utility> +#include <vector> + +#include <com/sun/star/bridge/XInstanceProvider.hpp> +#include <com/sun/star/container/NoSuchElementException.hpp> +#include <cppuhelper/exc_hlp.hxx> +#include <o3tl/runtimetooustring.hxx> +#include <rtl/byteseq.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <sal/types.h> +#include <typelib/typedescription.hxx> +#include <uno/dispatcher.hxx> + +#include "binaryany.hxx" +#include "bridge.hxx" +#include "currentcontext.hxx" +#include "incomingrequest.hxx" +#include "specialfunctionids.hxx" + +namespace binaryurp { + +IncomingRequest::IncomingRequest( + rtl::Reference< Bridge > const & bridge, rtl::ByteSequence tid, + OUString oid, css::uno::UnoInterfaceReference object, + css::uno::TypeDescription type, sal_uInt16 functionId, + bool synchronous, css::uno::TypeDescription const & member, bool setter, + std::vector< BinaryAny >&& inArguments, bool currentContextMode, + css::uno::UnoInterfaceReference currentContext): + bridge_(bridge), tid_(std::move(tid)), oid_(std::move(oid)), object_(std::move(object)), type_(std::move(type)), + member_(member), currentContext_(std::move(currentContext)), + inArguments_(std::move(inArguments)), functionId_(functionId), + synchronous_(synchronous), setter_(setter), currentContextMode_(currentContextMode) +{ + assert(bridge.is()); + assert(member.is()); + assert(member.get()->bComplete); +} + +IncomingRequest::~IncomingRequest() {} + +void IncomingRequest::execute() const { + BinaryAny ret; + std::vector< BinaryAny > outArgs; + bool isExc; + try { + bool resetCc = false; + css::uno::UnoInterfaceReference oldCc; + if (currentContextMode_) { + oldCc = current_context::get(); + current_context::set(currentContext_); + resetCc = true; + } + try { + try { + isExc = !execute_throw(&ret, &outArgs); + } catch (const std::exception & e) { + throw css::uno::RuntimeException( + "caught C++ exception: " + + o3tl::runtimeToOUString(e.what())); + } + } catch (const css::uno::RuntimeException &) { + css::uno::Any exc(cppu::getCaughtException()); + ret = bridge_->mapCppToBinaryAny(exc); + isExc = true; + } + if (resetCc) { + current_context::set(oldCc); + } + } catch (const css::uno::RuntimeException &) { + css::uno::Any exc(cppu::getCaughtException()); + ret = bridge_->mapCppToBinaryAny(exc); + isExc = true; + } + if (synchronous_) { + bridge_->decrementActiveCalls(); + try { + bridge_->getWriter()->queueReply( + tid_, member_, setter_, isExc, ret, std::move(outArgs), false); + return; + } catch (const css::uno::RuntimeException & e) { + SAL_INFO("binaryurp", "caught " << e); + } catch (const std::exception & e) { + SAL_INFO("binaryurp", "caught C++ exception " << e.what()); + } + bridge_->terminate(false); + } else { + if (isExc) { + SAL_INFO("binaryurp", "oneway method raised exception"); + } + bridge_->decrementCalls(); + } +} + +static size_t size_t_round(size_t val) +{ + return (val + (sizeof(size_t)-1)) & ~(sizeof(size_t)-1); +} + +bool IncomingRequest::execute_throw( + BinaryAny * returnValue, std::vector< BinaryAny > * outArguments) const +{ + assert(returnValue != nullptr); + assert( + returnValue->getType().equals( + css::uno::TypeDescription(cppu::UnoType<void>::get()))); + assert(outArguments != nullptr); + assert(outArguments->empty()); + bool isExc = false; + switch (functionId_) { + case SPECIAL_FUNCTION_ID_RESERVED: + assert(false); // this cannot happen + break; + case SPECIAL_FUNCTION_ID_RELEASE: + bridge_->releaseStub(oid_, type_); + break; + case SPECIAL_FUNCTION_ID_QUERY_INTERFACE: + if (!object_.is()) { + css::uno::Reference< css::uno::XInterface > ifc; + css::uno::Reference< css::bridge::XInstanceProvider > prov( + bridge_->getProvider()); + if (prov.is()) { + try { + ifc = prov->getInstance(oid_); + } catch (const css::container::NoSuchElementException & e) { + SAL_INFO("binaryurp", "initial element " << oid_ << ": " << e); + } + } + if (ifc.is()) { + css::uno::UnoInterfaceReference unoIfc( + static_cast< uno_Interface * >( + bridge_->getCppToBinaryMapping().mapInterface( + ifc.get(), + (css::uno::TypeDescription( + cppu::UnoType< + css::uno::Reference< + css::uno::XInterface > >::get()). + get()))), + SAL_NO_ACQUIRE); + *returnValue = BinaryAny( + css::uno::TypeDescription( + cppu::UnoType< + css::uno::Reference< + css::uno::XInterface > >::get()), + &unoIfc.m_pUnoI); + } + break; + } + [[fallthrough]]; + default: + { + assert(object_.is()); + css::uno::TypeDescription retType; + std::vector< std::vector< char > > outBufs; + std::vector< void * > args; + switch (member_.get()->eTypeClass) { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + { + css::uno::TypeDescription t( + reinterpret_cast< + typelib_InterfaceAttributeTypeDescription * >( + member_.get())-> + pAttributeTypeRef); + if (setter_) { + assert(inArguments_.size() == 1); + args.push_back(inArguments_[0].getValue(t)); + } else { + assert(inArguments_.empty()); + retType = t; + } + break; + } + case typelib_TypeClass_INTERFACE_METHOD: + { + typelib_InterfaceMethodTypeDescription * mtd = + reinterpret_cast< + typelib_InterfaceMethodTypeDescription * >( + member_.get()); + retType = css::uno::TypeDescription(mtd->pReturnTypeRef); + std::vector< BinaryAny >::const_iterator i( + inArguments_.begin()); + for (sal_Int32 j = 0; j != mtd->nParams; ++j) { + void * p; + if (mtd->pParams[j].bIn) { + p = i++->getValue( + css::uno::TypeDescription( + mtd->pParams[j].pTypeRef)); + } else { + outBufs.emplace_back(size_t_round( + css::uno::TypeDescription( + mtd->pParams[j].pTypeRef). + get()->nSize)); + p = outBufs.back().data(); + } + args.push_back(p); + if (mtd->pParams[j].bOut) { + outArguments->push_back(BinaryAny()); + } + } + assert(i == inArguments_.end()); + break; + } + default: + assert(false); // this cannot happen + break; + } + size_t nSize = 0; + if (retType.is()) + nSize = size_t_round(retType.get()->nSize); + std::vector< char > retBuf(nSize); + uno_Any exc; + uno_Any * pexc = &exc; + (*object_.get()->pDispatcher)( + object_.get(), member_.get(), retBuf.empty() ? nullptr : retBuf.data(), + args.empty() ? nullptr : args.data(), &pexc); + isExc = pexc != nullptr; + if (isExc) { + *returnValue = BinaryAny( + css::uno::TypeDescription( + cppu::UnoType< css::uno::Any >::get()), + &exc); + uno_any_destruct(&exc, nullptr); + } else { + if (!retBuf.empty()) { + *returnValue = BinaryAny(retType, retBuf.data()); + uno_destructData(retBuf.data(), retType.get(), nullptr); + } + if (!outArguments->empty()) { + assert( + member_.get()->eTypeClass == + typelib_TypeClass_INTERFACE_METHOD); + typelib_InterfaceMethodTypeDescription * mtd = + reinterpret_cast< + typelib_InterfaceMethodTypeDescription * >( + member_.get()); + std::vector< BinaryAny >::iterator i(outArguments->begin()); + std::vector< std::vector< char > >::iterator j( + outBufs.begin()); + for (sal_Int32 k = 0; k != mtd->nParams; ++k) { + if (mtd->pParams[k].bOut) { + *i++ = BinaryAny( + css::uno::TypeDescription( + mtd->pParams[k].pTypeRef), + args[k]); + } + if (!mtd->pParams[k].bIn) { + uno_type_destructData( + (j++)->data(), mtd->pParams[k].pTypeRef, nullptr); + } + } + assert(i == outArguments->end()); + assert(j == outBufs.end()); + } + } + break; + } + } + return !isExc; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/incomingrequest.hxx b/binaryurp/source/incomingrequest.hxx new file mode 100644 index 000000000..faff4f5a5 --- /dev/null +++ b/binaryurp/source/incomingrequest.hxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <vector> + +#include <rtl/byteseq.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <typelib/typedescription.hxx> +#include <uno/dispatcher.hxx> + +namespace binaryurp { + class BinaryAny; + class Bridge; +} + +namespace binaryurp { + +class IncomingRequest { +private: + IncomingRequest(const IncomingRequest&) = delete; + IncomingRequest& operator=(const IncomingRequest&) = delete; +public: + IncomingRequest( + rtl::Reference< Bridge > const & bridge, rtl::ByteSequence tid, + OUString oid, + com::sun::star::uno::UnoInterfaceReference object, + com::sun::star::uno::TypeDescription type, + sal_uInt16 functionId, bool synchronous, + com::sun::star::uno::TypeDescription const & member, bool setter, + std::vector< BinaryAny >&& inArguments, bool currentContextMode, + com::sun::star::uno::UnoInterfaceReference currentContext); + + ~IncomingRequest(); + + void execute() const; + +private: + bool execute_throw( + BinaryAny * returnValue, std::vector< BinaryAny > * outArguments) const; + + rtl::Reference< Bridge > bridge_; + rtl::ByteSequence tid_; + OUString oid_; // initial object queryInterface; release + com::sun::star::uno::UnoInterfaceReference object_; + com::sun::star::uno::TypeDescription type_; + com::sun::star::uno::TypeDescription member_; + com::sun::star::uno::UnoInterfaceReference currentContext_; + std::vector< BinaryAny > inArguments_; + sal_uInt16 functionId_; + bool synchronous_; + bool setter_; + bool currentContextMode_; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/lessoperators.cxx b/binaryurp/source/lessoperators.cxx new file mode 100644 index 000000000..acab81175 --- /dev/null +++ b/binaryurp/source/lessoperators.cxx @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <algorithm> +#include <cassert> + +#include <rtl/byteseq.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <typelib/typeclass.h> +#include <typelib/typedescription.hxx> + +#include "lessoperators.hxx" + +namespace com::sun::star::uno { + +bool operator <(TypeDescription const & left, TypeDescription const & right) { + assert(left.is() && right.is()); + typelib_TypeClass tc1 = left.get()->eTypeClass; + typelib_TypeClass tc2 = right.get()->eTypeClass; + return tc1 < tc2 || + (tc1 == tc2 && + (OUString::unacquired(&left.get()->pTypeName) < + OUString::unacquired(&right.get()->pTypeName))); +} + +} + +namespace rtl { + +bool operator <(ByteSequence const & left, ByteSequence const & right) { + const sal_Int32 nLen = std::min( left.getLength(), right.getLength()); + for( sal_Int32 i = 0; i < nLen; ++i ) + { + if (left[i] < right[i]) { + return true; + } + if (right[i] < left[i]) { + return false; + } + } + return left.getLength() < right.getLength(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/lessoperators.hxx b/binaryurp/source/lessoperators.hxx new file mode 100644 index 000000000..306710de6 --- /dev/null +++ b/binaryurp/source/lessoperators.hxx @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +namespace com::sun::star::uno { class TypeDescription; } +namespace rtl { class ByteSequence; } + +namespace com::sun::star::uno { + +bool operator <(TypeDescription const & left, TypeDescription const & right); + +} + +namespace rtl { + +bool operator <(ByteSequence const & left, ByteSequence const & right); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/marshal.cxx b/binaryurp/source/marshal.cxx new file mode 100644 index 000000000..7d60cbf4d --- /dev/null +++ b/binaryurp/source/marshal.cxx @@ -0,0 +1,300 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> +#include <vector> + +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <cppu/unotype.hxx> +#include <rtl/byteseq.hxx> +#include <rtl/string.hxx> +#include <rtl/textcvt.h> +#include <rtl/textenc.h> +#include <rtl/ustring.h> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <typelib/typeclass.h> +#include <typelib/typedescription.h> +#include <typelib/typedescription.hxx> +#include <uno/dispatcher.hxx> + +#include "binaryany.hxx" +#include "bridge.hxx" +#include "cache.hxx" +#include "lessoperators.hxx" +#include "marshal.hxx" + +namespace binaryurp { + +namespace { + +void write64(std::vector< unsigned char > * buffer, sal_uInt64 value) { + Marshal::write8(buffer, value >> 56); + Marshal::write8(buffer, (value >> 48) & 0xFF); + Marshal::write8(buffer, (value >> 40) & 0xFF); + Marshal::write8(buffer, (value >> 32) & 0xFF); + Marshal::write8(buffer, (value >> 24) & 0xFF); + Marshal::write8(buffer, (value >> 16) & 0xFF); + Marshal::write8(buffer, (value >> 8) & 0xFF); + Marshal::write8(buffer, value & 0xFF); +} + +void writeCompressed(std::vector< unsigned char > * buffer, sal_uInt32 value) { + if (value < 0xFF) { + Marshal::write8(buffer, static_cast< sal_uInt8 >(value)); + } else { + Marshal::write8(buffer, 0xFF); + Marshal::write32(buffer, value); + } +} + +void writeString( + std::vector< unsigned char > * buffer, OUString const & value) +{ + assert(buffer != nullptr); + OString v; + if (!value.convertToString( + &v, RTL_TEXTENCODING_UTF8, + (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | + RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))) + { + throw css::uno::RuntimeException( + "UNO string contains invalid UTF-16 sequence"); + } + writeCompressed(buffer, static_cast< sal_uInt32 >(v.getLength())); + buffer->insert(buffer->end(), v.getStr(), v.getStr() + v.getLength()); +} + +} + +Marshal::Marshal(rtl::Reference< Bridge > const & bridge, WriterState & state): + bridge_(bridge), state_(state) +{ + assert(bridge.is()); +} + +Marshal::~Marshal() {} + +void Marshal::write8(std::vector< unsigned char > * buffer, sal_uInt8 value) { + assert(buffer != nullptr); + buffer->push_back(value); +} + +void Marshal::write16(std::vector< unsigned char > * buffer, sal_uInt16 value) { + write8(buffer, value >> 8); + write8(buffer, value & 0xFF); +} + +void Marshal::write32(std::vector< unsigned char > * buffer, sal_uInt32 value) { + write8(buffer, value >> 24); + write8(buffer, (value >> 16) & 0xFF); + write8(buffer, (value >> 8) & 0xFF); + write8(buffer, value & 0xFF); +} + +void Marshal::writeValue( + std::vector< unsigned char > * buffer, + css::uno::TypeDescription const & type, BinaryAny const & value) +{ + assert( + type.is() && + (type.get()->eTypeClass == typelib_TypeClass_ANY || + value.getType().equals(type))); + writeValue(buffer, type, value.getValue(type)); +} + +void Marshal::writeType( + std::vector< unsigned char > * buffer, + css::uno::TypeDescription const & value) +{ + value.makeComplete(); + assert(value.is()); + typelib_TypeClass tc = value.get()->eTypeClass; + if (tc <= typelib_TypeClass_ANY) { + write8(buffer, static_cast< sal_uInt8 >(tc)); + } else { + bool found; + sal_uInt16 idx = state_.typeCache.add(value, &found); + if (found) { + write8(buffer, static_cast< sal_uInt8 >(tc)); + write16(buffer, idx); + } else { + write8(buffer, static_cast< sal_uInt8 >(tc) | 0x80); + write16(buffer, idx); + writeString(buffer, OUString(value.get()->pTypeName)); + } + } +} + +void Marshal::writeOid( + std::vector< unsigned char > * buffer, OUString const & oid) +{ + bool found; + sal_uInt16 idx; + if ( oid.isEmpty() ) { + found = true; + idx = cache::ignore; + } else { + idx = state_.oidCache.add(oid, &found); + } + if (found) { + write8(buffer, 0); + } else { + writeString(buffer, oid); + } + write16(buffer, idx); +} + +void Marshal::writeTid( + std::vector< unsigned char > * buffer, rtl::ByteSequence const & tid) +{ + bool found; + sal_uInt16 idx = state_.tidCache.add(tid, &found); + if (found) { + write8(buffer, 0); + } else { + sal_Sequence * p = tid.getHandle(); + writeValue( + buffer, + css::uno::TypeDescription( + cppu::UnoType< css::uno::Sequence< sal_Int8 > >::get()), &p); + } + write16(buffer, idx); +} + +void Marshal::writeValue( + std::vector< unsigned char > * buffer, + css::uno::TypeDescription const & type, void const * value) +{ + assert(buffer != nullptr && type.is()); + type.makeComplete(); + switch (type.get()->eTypeClass) { + case typelib_TypeClass_VOID: + break; + case typelib_TypeClass_BOOLEAN: + assert(*static_cast< sal_uInt8 const * >(value) <= 1); + [[fallthrough]]; + case typelib_TypeClass_BYTE: + write8(buffer, *static_cast< sal_uInt8 const * >(value)); + break; + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_CHAR: + write16(buffer, *static_cast< sal_uInt16 const * >(value)); + break; + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_FLOAT: + case typelib_TypeClass_ENUM: + write32(buffer, *static_cast< sal_uInt32 const * >(value)); + break; + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + case typelib_TypeClass_DOUBLE: + write64(buffer, *static_cast< sal_uInt64 const * >(value)); + break; + case typelib_TypeClass_STRING: + writeString( + buffer, + OUString(*static_cast< rtl_uString * const * >(value))); + break; + case typelib_TypeClass_TYPE: + writeType( + buffer, + css::uno::TypeDescription( + *static_cast< typelib_TypeDescriptionReference * const * >( + value))); + break; + case typelib_TypeClass_ANY: + { + uno_Any const * p = static_cast< uno_Any const * >(value); + css::uno::TypeDescription t(p->pType); + writeType(buffer, t); + writeValue(buffer, t, p->pData); + break; + } + case typelib_TypeClass_SEQUENCE: + { + sal_Sequence * p = *static_cast< sal_Sequence * const * >(value); + writeCompressed(buffer, static_cast< sal_uInt32 >(p->nElements)); + css::uno::TypeDescription ctd( + reinterpret_cast< typelib_IndirectTypeDescription * >( + type.get())-> + pType); + assert(ctd.is()); + if (ctd.get()->eTypeClass == typelib_TypeClass_BYTE) { + buffer->insert( + buffer->end(), p->elements, p->elements + p->nElements); + } else { + for (sal_Int32 i = 0; i != p->nElements; ++i) { + writeValue(buffer, ctd, p->elements + i * ctd.get()->nSize); + } + } + break; + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + writeMemberValues(buffer, type, value); + break; + case typelib_TypeClass_INTERFACE: + writeOid( + buffer, + bridge_->registerOutgoingInterface( + css::uno::UnoInterfaceReference( + *static_cast< uno_Interface * const * >(value)), + type)); + break; + default: + assert(false); // this cannot happen + break; + } +} + +void Marshal::writeMemberValues( + std::vector< unsigned char > * buffer, + css::uno::TypeDescription const & type, void const * aggregateValue) +{ + assert( + type.is() && + (type.get()->eTypeClass == typelib_TypeClass_STRUCT || + type.get()->eTypeClass == typelib_TypeClass_EXCEPTION) && + aggregateValue != nullptr); + type.makeComplete(); + typelib_CompoundTypeDescription * ctd = + reinterpret_cast< typelib_CompoundTypeDescription * >(type.get()); + if (ctd->pBaseTypeDescription != nullptr) { + writeMemberValues( + buffer, + css::uno::TypeDescription(&ctd->pBaseTypeDescription->aBase), + aggregateValue); + } + for (sal_Int32 i = 0; i != ctd->nMembers; ++i) { + writeValue( + buffer, css::uno::TypeDescription(ctd->ppTypeRefs[i]), + (static_cast< char const * >(aggregateValue) + + ctd->pMemberOffsets[i])); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/marshal.hxx b/binaryurp/source/marshal.hxx new file mode 100644 index 000000000..93930e356 --- /dev/null +++ b/binaryurp/source/marshal.hxx @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <vector> + +#include <rtl/byteseq.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <typelib/typedescription.hxx> + +namespace binaryurp { + class BinaryAny; + class Bridge; + struct WriterState; +} + +namespace binaryurp { + +class Marshal { +public: + Marshal(rtl::Reference< Bridge > const & bridge, WriterState & state); + + ~Marshal(); + + static void write8(std::vector< unsigned char > * buffer, sal_uInt8 value); + + static void write16( + std::vector< unsigned char > * buffer, sal_uInt16 value); + + static void write32( + std::vector< unsigned char > * buffer, sal_uInt32 value); + + void writeValue( + std::vector< unsigned char > * buffer, + com::sun::star::uno::TypeDescription const & type, + BinaryAny const & value); + + void writeType( + std::vector< unsigned char > * buffer, + com::sun::star::uno::TypeDescription const & value); + + void writeOid( + std::vector< unsigned char > * buffer, OUString const & oid); + + void writeTid( + std::vector< unsigned char > * buffer, rtl::ByteSequence const & tid); + +private: + Marshal(const Marshal&) = delete; + Marshal& operator=(const Marshal&) = delete; + + void writeValue( + std::vector< unsigned char > * buffer, + com::sun::star::uno::TypeDescription const & type, void const * value); + + void writeMemberValues( + std::vector< unsigned char > * buffer, + com::sun::star::uno::TypeDescription const & type, + void const * aggregateValue); + + rtl::Reference< Bridge > bridge_; + WriterState & state_; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/outgoingrequest.hxx b/binaryurp/source/outgoingrequest.hxx new file mode 100644 index 000000000..efa673eac --- /dev/null +++ b/binaryurp/source/outgoingrequest.hxx @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <typelib/typedescription.hxx> +#include <utility> + +namespace binaryurp { + +struct OutgoingRequest { + enum Kind { KIND_NORMAL, KIND_REQUEST_CHANGE, KIND_COMMIT_CHANGE }; + + OutgoingRequest( + Kind theKind, com::sun::star::uno::TypeDescription theMember, + bool theSetter): + member(std::move(theMember)), kind(theKind), setter(theSetter) + {} + + com::sun::star::uno::TypeDescription member; + + Kind kind; + + bool setter; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/outgoingrequests.cxx b/binaryurp/source/outgoingrequests.cxx new file mode 100644 index 000000000..3cdc80e82 --- /dev/null +++ b/binaryurp/source/outgoingrequests.cxx @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> + +#include <com/sun/star/uno/RuntimeException.hpp> +#include <rtl/byteseq.hxx> + +#include "lessoperators.hxx" +#include "outgoingrequest.hxx" +#include "outgoingrequests.hxx" + +namespace binaryurp { + +OutgoingRequests::OutgoingRequests() {} + +OutgoingRequests::~OutgoingRequests() {} + +void OutgoingRequests::push( + rtl::ByteSequence const & tid, OutgoingRequest const & request) +{ + std::scoped_lock g(mutex_); + map_[tid].push_back(request); +} + +OutgoingRequest OutgoingRequests::top(rtl::ByteSequence const & tid) { + std::scoped_lock g(mutex_); + Map::iterator i(map_.find(tid)); + if (i == map_.end()) { + throw css::uno::RuntimeException( + "URP: reply for unknown TID"); + } + assert(!i->second.empty()); + return i->second.back(); +} + +void OutgoingRequests::pop(rtl::ByteSequence const & tid) noexcept { + std::scoped_lock g(mutex_); + Map::iterator i(map_.find(tid)); + assert(i != map_.end()); + i->second.pop_back(); + if (i->second.empty()) { + map_.erase(i); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/outgoingrequests.hxx b/binaryurp/source/outgoingrequests.hxx new file mode 100644 index 000000000..698b6db9e --- /dev/null +++ b/binaryurp/source/outgoingrequests.hxx @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <map> +#include <mutex> +#include <vector> + +namespace binaryurp +{ +struct OutgoingRequest; +} +namespace rtl +{ +class ByteSequence; +} + +namespace binaryurp +{ +class OutgoingRequests +{ +public: + OutgoingRequests(); + + ~OutgoingRequests(); + + void push(rtl::ByteSequence const& tid, OutgoingRequest const& request); + + OutgoingRequest top(rtl::ByteSequence const& tid); + + void pop(rtl::ByteSequence const& tid) noexcept; + +private: + OutgoingRequests(const OutgoingRequests&) = delete; + OutgoingRequests& operator=(const OutgoingRequests&) = delete; + + typedef std::map<rtl::ByteSequence, std::vector<OutgoingRequest>> Map; + + std::mutex mutex_; + Map map_; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/proxy.cxx b/binaryurp/source/proxy.cxx new file mode 100644 index 000000000..49705e06a --- /dev/null +++ b/binaryurp/source/proxy.cxx @@ -0,0 +1,239 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> +#include <exception> +#include <utility> +#include <vector> + +#include <cppuhelper/exc_hlp.hxx> +#include <o3tl/runtimetooustring.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <typelib/typedescription.h> +#include <typelib/typedescription.hxx> +#include <uno/any2.h> +#include <uno/dispatcher.h> +#include <uno/dispatcher.hxx> + +#include "binaryany.hxx" +#include "bridge.hxx" +#include "proxy.hxx" + +namespace binaryurp { + +namespace { + +extern "C" void proxy_acquireInterface(uno_Interface * pInterface) { + assert(pInterface != nullptr); + static_cast< Proxy * >(pInterface)->do_acquire(); +} + +extern "C" void proxy_releaseInterface(uno_Interface * pInterface) { + assert(pInterface != nullptr); + static_cast< Proxy * >(pInterface)->do_release(); +} + +extern "C" void proxy_dispatchInterface( + uno_Interface * pUnoI, typelib_TypeDescription const * pMemberType, + void * pReturn, void ** pArgs, uno_Any ** ppException) +{ + assert(pUnoI != nullptr); + static_cast< Proxy * >(pUnoI)->do_dispatch( + pMemberType, pReturn, pArgs, ppException); +} + +} + +Proxy::Proxy( + rtl::Reference< Bridge > const & bridge, OUString oid, + css::uno::TypeDescription type): + bridge_(bridge), oid_(std::move(oid)), type_(std::move(type)), references_(1) +{ + assert(bridge.is()); + acquire = &proxy_acquireInterface; + release = &proxy_releaseInterface; + pDispatcher = &proxy_dispatchInterface; +} + + +void Proxy::do_acquire() { + if (++references_ == 1) { + bridge_->resurrectProxy(*this); + } +} + +void Proxy::do_release() { + if (--references_ == 0) { + bridge_->revokeProxy(*this); + } +} + +void Proxy::do_free() { + bridge_->freeProxy(*this); + delete this; +} + +void Proxy::do_dispatch( + typelib_TypeDescription const * member, void * returnValue, + void ** arguments, uno_Any ** exception) const +{ + try { + try { + do_dispatch_throw(member, returnValue, arguments, exception); + } catch (const std::exception & e) { + throw css::uno::RuntimeException( + "caught C++ exception: " + o3tl::runtimeToOUString(e.what())); + } + } catch (const css::uno::RuntimeException &) { + css::uno::Any exc(cppu::getCaughtException()); + uno_copyAndConvertData( + *exception, &exc, + (css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()). + get()), + bridge_->getCppToBinaryMapping().get()); + } +} + +bool Proxy::isProxy( + rtl::Reference< Bridge > const & bridge, + css::uno::UnoInterfaceReference const & object, OUString * oid) +{ + assert(object.is()); + return object.m_pUnoI->acquire == &proxy_acquireInterface && + static_cast< Proxy * >(object.m_pUnoI)->isProxy(bridge, oid); +} + +Proxy::~Proxy() {} + +void Proxy::do_dispatch_throw( + typelib_TypeDescription const * member, void * returnValue, + void ** arguments, uno_Any ** exception) const +{ + //TODO: Optimize queryInterface: + assert(member != nullptr); + bool bSetter = false; + std::vector< BinaryAny > inArgs; + switch (member->eTypeClass) { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + bSetter = returnValue == nullptr; + if (bSetter) { + inArgs.emplace_back( + css::uno::TypeDescription( + reinterpret_cast< + typelib_InterfaceAttributeTypeDescription const * >( + member)-> + pAttributeTypeRef), + arguments[0]); + } + break; + case typelib_TypeClass_INTERFACE_METHOD: + { + typelib_InterfaceMethodTypeDescription const * mtd = + reinterpret_cast< + typelib_InterfaceMethodTypeDescription const * >(member); + for (sal_Int32 i = 0; i != mtd->nParams; ++i) { + if (mtd->pParams[i].bIn) { + inArgs.emplace_back( + css::uno::TypeDescription(mtd->pParams[i].pTypeRef), + arguments[i]); + } + } + break; + } + default: + assert(false); // this cannot happen + break; + } + BinaryAny ret; + std::vector< BinaryAny > outArgs; + if (bridge_->makeCall( + oid_, + css::uno::TypeDescription( + const_cast< typelib_TypeDescription * >(member)), + bSetter, std::move(inArgs), &ret, &outArgs)) + { + assert(ret.getType().get()->eTypeClass == typelib_TypeClass_EXCEPTION); + uno_any_construct( + *exception, ret.getValue(ret.getType()), ret.getType().get(), nullptr); + } else { + switch (member->eTypeClass) { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + if (!bSetter) { + css::uno::TypeDescription t( + reinterpret_cast< + typelib_InterfaceAttributeTypeDescription const * >( + member)-> + pAttributeTypeRef); + uno_copyData(returnValue, ret.getValue(t), t.get(), nullptr); + } + break; + case typelib_TypeClass_INTERFACE_METHOD: + { + typelib_InterfaceMethodTypeDescription const * mtd = + reinterpret_cast< + typelib_InterfaceMethodTypeDescription const * >( + member); + css::uno::TypeDescription t(mtd->pReturnTypeRef); + if (t.get()->eTypeClass != typelib_TypeClass_VOID) { + uno_copyData(returnValue, ret.getValue(t), t.get(), nullptr); + } + std::vector< BinaryAny >::iterator i(outArgs.begin()); + for (sal_Int32 j = 0; j != mtd->nParams; ++j) { + if (mtd->pParams[j].bOut) { + css::uno::TypeDescription pt(mtd->pParams[j].pTypeRef); + if (mtd->pParams[j].bIn) { + (void) uno_assignData( + arguments[j], pt.get(), i++->getValue(pt), + pt.get(), nullptr, nullptr, nullptr); + } else { + uno_copyData( + arguments[j], i++->getValue(pt), pt.get(), nullptr); + } + } + } + assert(i == outArgs.end()); + break; + } + default: + assert(false); // this cannot happen + break; + } + *exception = nullptr; + } +} + +bool Proxy::isProxy( + rtl::Reference< Bridge > const & bridge, OUString * oid) const +{ + assert(oid != nullptr); + if (bridge == bridge_) { + *oid = oid_; + return true; + } else { + return false; + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/proxy.hxx b/binaryurp/source/proxy.hxx new file mode 100644 index 000000000..4e1fa3656 --- /dev/null +++ b/binaryurp/source/proxy.hxx @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <atomic> +#include <cstddef> + +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <typelib/typedescription.h> +#include <typelib/typedescription.hxx> +#include <uno/any2.h> +#include <uno/dispatcher.h> + +namespace binaryurp { class Bridge; } +namespace com::sun::star::uno { class UnoInterfaceReference; } + +namespace binaryurp { + +class Proxy: public uno_Interface { +public: + Proxy( + rtl::Reference< Bridge > const & bridge, OUString oid, + com::sun::star::uno::TypeDescription type); + + const OUString& getOid() const { return oid_;} + + const com::sun::star::uno::TypeDescription& getType() const { return type_;} + + void do_acquire(); + + void do_release(); + + void do_free(); + + void do_dispatch( + typelib_TypeDescription const * member, void * returnValue, + void ** arguments, uno_Any ** exception) const; + + static bool isProxy( + rtl::Reference< Bridge > const & bridge, + com::sun::star::uno::UnoInterfaceReference const & object, + OUString * oid); + +private: + Proxy(const Proxy&) = delete; + Proxy& operator=(const Proxy&) = delete; + + ~Proxy(); + + void do_dispatch_throw( + typelib_TypeDescription const * member, void * returnValue, + void ** arguments, uno_Any ** exception) const; + + bool isProxy(rtl::Reference< Bridge > const & bridge, OUString * oid) + const; + + rtl::Reference< Bridge > bridge_; + OUString oid_; + com::sun::star::uno::TypeDescription type_; + std::atomic<std::size_t> references_; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/reader.cxx b/binaryurp/source/reader.cxx new file mode 100644 index 000000000..cbd18f1d8 --- /dev/null +++ b/binaryurp/source/reader.cxx @@ -0,0 +1,480 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> +#include <exception> +#include <memory> +#include <vector> + +#include <com/sun/star/connection/XConnection.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Type.hxx> +#include <com/sun/star/uno/XCurrentContext.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <cppu/unotype.hxx> +#include <o3tl/safeint.hxx> +#include <rtl/byteseq.h> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <sal/types.h> +#include <typelib/typeclass.h> +#include <typelib/typedescription.h> +#include <typelib/typedescription.hxx> + +#include "binaryany.hxx" +#include "bridge.hxx" +#include "incomingreply.hxx" +#include "incomingrequest.hxx" +#include "outgoingrequest.hxx" +#include "reader.hxx" +#include "specialfunctionids.hxx" +#include "unmarshal.hxx" + +namespace binaryurp { + +namespace { + +css::uno::Sequence< sal_Int8 > read( + css::uno::Reference< css::connection::XConnection > const & connection, + sal_uInt32 size, bool eofOk) +{ + assert(connection.is()); + if (size > SAL_MAX_INT32) { + throw css::uno::RuntimeException( + "binaryurp::Reader: block size too large"); + } + css::uno::Sequence< sal_Int8 > buf; + sal_Int32 n = connection->read(buf, static_cast< sal_Int32 >(size)); + if (n == 0 && eofOk) { + return css::uno::Sequence< sal_Int8 >(); + } + if (o3tl::make_unsigned(n) != size) { + throw css::io::IOException( + "binaryurp::Reader: premature end of input"); + } + assert(o3tl::make_unsigned(buf.getLength()) == size); + return buf; +} + +extern "C" void request(void * pThreadSpecificData) { + assert(pThreadSpecificData != nullptr); + std::unique_ptr< IncomingRequest >( + static_cast< IncomingRequest * >(pThreadSpecificData))-> + execute(); +} + +} + +Reader::Reader(rtl::Reference< Bridge > const & bridge): + Thread("binaryurpReader"), bridge_(bridge) +{ + assert(bridge.is()); +} + +Reader::~Reader() {} + +void Reader::execute() { + try { + bridge_->sendRequestChangeRequest(); + css::uno::Reference< css::connection::XConnection > con( + bridge_->getConnection()); + for (;;) { + css::uno::Sequence< sal_Int8 > s(read(con, 8, true)); + if (!s.hasElements()) { + break; + } + Unmarshal header(bridge_, state_, s); + sal_uInt32 size = header.read32(); + sal_uInt32 count = header.read32(); + header.done(); + if (count == 0) { + throw css::io::IOException( + "binaryurp::Reader: block with zero message count received"); + } + Unmarshal block(bridge_, state_, read(con, size, false)); + for (sal_uInt32 i = 0; i != count; ++i) { + readMessage(block); + } + block.done(); + } + } catch (const css::uno::Exception & e) { + SAL_WARN("binaryurp", "caught UNO exception '" << e << '\''); + } catch (const std::exception & e) { + SAL_WARN("binaryurp", "caught C++ exception '" << e.what() << '\''); + } + bridge_->terminate(false); + bridge_.clear(); +} + +void Reader::readMessage(Unmarshal & unmarshal) { + sal_uInt8 flags1 = unmarshal.read8(); + bool newType; + bool newOid; + bool newTid; + bool forceSynchronous; + sal_uInt16 functionId; + if ((flags1 & 0x80) != 0) { // bit 7: LONGHEADER + if ((flags1 & 0x40) == 0) { // bit 6: REQUEST + readReplyMessage(unmarshal, flags1); + return; + } + newType = (flags1 & 0x20) != 0; // bit 5: NEWTYPE + newOid = (flags1 & 0x10) != 0; // bit 4: NEWOID + newTid = (flags1 & 0x08) != 0; // bit 3: NEWTID + if ((flags1 & 0x01) != 0) { // bit 0: MOREFLAGSS + sal_uInt8 flags2 = unmarshal.read8(); + forceSynchronous = (flags2 & 0x80) != 0; // bit 7: MUSTREPLY + if (((flags2 & 0x40) != 0) != forceSynchronous) { + // bit 6: SYNCHRONOUS + throw css::uno::RuntimeException( + "URP: request message with MUSTREPLY != SYNCHRONOUS" + " received"); + } + } else { + forceSynchronous = false; + } + functionId = ((flags1 & 0x04) != 0) // bit 2: FUNCTIONID16 + ? unmarshal.read16() : unmarshal.read8(); + } else { + newType = false; + newOid = false; + newTid = false; + forceSynchronous = false; + functionId = ((flags1 & 0x40) != 0) // bit 6: FUNCTIONID14 + ? ((flags1 & 0x3F) << 8) | unmarshal.read8() : flags1 & 0x3F; + } + css::uno::TypeDescription type; + if (newType) { + type = unmarshal.readType(); + lastType_ = type; + } else { + if (!lastType_.is()) { + throw css::uno::RuntimeException( + "URP: request message with NEWTYPE received when last" + " interface type has not yet been set"); + } + type = lastType_; + } + OUString oid; + if (newOid) { + oid = unmarshal.readOid(); + if (oid.isEmpty()) { + throw css::io::IOException( + "binaryurp::Unmarshal: empty OID"); + } + lastOid_ = oid; + } else { + if (lastOid_.isEmpty()) { + throw css::uno::RuntimeException( + "URP: request message with NEWOID received when last OID has" + " not yet been set"); + } + oid = lastOid_; + } + rtl::ByteSequence tid(getTid(unmarshal, newTid)); + lastTid_ = tid; + type.makeComplete(); + if (type.get()->eTypeClass != typelib_TypeClass_INTERFACE) { + throw css::uno::RuntimeException( + "URP: request message with non-interface interface type received"); + } + typelib_InterfaceTypeDescription * itd = + reinterpret_cast< typelib_InterfaceTypeDescription * >(type.get()); + if (functionId >= itd->nMapFunctionIndexToMemberIndex) { + throw css::uno::RuntimeException( + "URP: request message with unknown function ID received"); + } + sal_Int32 memberId = itd->pMapFunctionIndexToMemberIndex[functionId]; + css::uno::TypeDescription memberTd(itd->ppAllMembers[memberId]); + memberTd.makeComplete(); + assert(memberTd.is()); + bool protProps = bridge_->isProtocolPropertiesRequest(oid, type); + bool ccMode = !protProps && functionId != SPECIAL_FUNCTION_ID_RELEASE && + bridge_->isCurrentContextMode(); + css::uno::UnoInterfaceReference cc; + if (ccMode) { + css::uno::TypeDescription t( + cppu::UnoType<css::uno::XCurrentContext>::get()); + cc.set( + *static_cast< uno_Interface ** >( + unmarshal.readValue(t).getValue(t))); + } + bool oneWay = + memberTd.get()->eTypeClass == typelib_TypeClass_INTERFACE_METHOD && + (reinterpret_cast< typelib_InterfaceMethodTypeDescription * >( + memberTd.get())-> + bOneWay); + SAL_INFO_IF( + !oneWay && forceSynchronous, "binaryurp", + ("superfluous MUSTREPLY/SYNCHRONOUS ignored in request message with" + " non-oneway function ID")); + bool synchronous = !oneWay || forceSynchronous; + bool bSetter = false; + std::vector< BinaryAny > inArgs; + switch (memberTd.get()->eTypeClass) { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + bSetter = itd->pMapMemberIndexToFunctionIndex[memberId] != functionId; + // pMapMemberIndexToFunctionIndex contains function index of + // attribute getter + if (bSetter) { + inArgs.push_back( + unmarshal.readValue( + css::uno::TypeDescription( + reinterpret_cast< + typelib_InterfaceAttributeTypeDescription * >( + memberTd.get())-> + pAttributeTypeRef))); + } + break; + case typelib_TypeClass_INTERFACE_METHOD: + { + typelib_InterfaceMethodTypeDescription * mtd = + reinterpret_cast< typelib_InterfaceMethodTypeDescription * >( + memberTd.get()); + for (sal_Int32 i = 0; i != mtd->nParams; ++i) { + if (mtd->pParams[i].bIn) { + inArgs.push_back( + unmarshal.readValue( + css::uno::TypeDescription( + mtd->pParams[i].pTypeRef))); + } + } + break; + } + default: + assert(false); // this cannot happen + break; + } + bridge_->incrementCalls( + !protProps && functionId != SPECIAL_FUNCTION_ID_RELEASE); + if (protProps) { + switch (functionId) { + case SPECIAL_FUNCTION_ID_REQUEST_CHANGE: + bridge_->handleRequestChangeRequest(tid, inArgs); + break; + case SPECIAL_FUNCTION_ID_COMMIT_CHANGE: + bridge_->handleCommitChangeRequest(tid, inArgs); + break; + default: + throw css::uno::RuntimeException( + "URP: request message with UrpProtocolProperties OID and" + " unknown function ID received"); + } + } else { + css::uno::UnoInterfaceReference obj; + switch (functionId) { + case SPECIAL_FUNCTION_ID_QUERY_INTERFACE: + obj = bridge_->findStub(oid, type); + if (!obj.is()) { + assert( + inArgs.size() == 1 + && inArgs[0].getType().equals( + css::uno::TypeDescription( + cppu::UnoType< css::uno::Type >::get()))); + if (!(type.equals( + css::uno::TypeDescription( + cppu::UnoType< + css::uno::Reference< + css::uno::XInterface > >::get())) + && (css::uno::TypeDescription( + *static_cast< + typelib_TypeDescriptionReference ** >( + inArgs[0].getValue(inArgs[0].getType()))). + equals( + css::uno::TypeDescription( + cppu::UnoType< + css::uno::Reference< + css::uno::XInterface > >::get()))))) + { + throw css::uno::RuntimeException( + "URP: queryInterface request message with unknown OID '" + + oid + "' received"); + } + } + break; + case SPECIAL_FUNCTION_ID_RESERVED: + throw css::uno::RuntimeException( + "URP: request message with unknown function ID 1 received"); + case SPECIAL_FUNCTION_ID_RELEASE: + break; + default: + obj = bridge_->findStub(oid, type); + if (!obj.is()) { + throw css::uno::RuntimeException( + "URP: request message with unknown OID received"); + } + break; + } + std::unique_ptr< IncomingRequest > req( + new IncomingRequest( + bridge_, tid, oid, obj, type, functionId, synchronous, memberTd, + bSetter, std::move(inArgs), ccMode, cc)); + if (synchronous) { + bridge_->incrementActiveCalls(); + } + uno_threadpool_putJob( + bridge_->getThreadPool(), tid.getHandle(), req.get(), &request, + !synchronous); + req.release(); + } +} + +void Reader::readReplyMessage(Unmarshal & unmarshal, sal_uInt8 flags1) { + rtl::ByteSequence tid(getTid(unmarshal, (flags1 & 0x08) != 0)); + // bit 3: NEWTID + lastTid_ = tid; + OutgoingRequest req(bridge_->lastOutgoingRequest(tid)); + bool exc = (flags1 & 0x20) != 0; // bit 5: EXCEPTION + BinaryAny ret; + std::vector< BinaryAny > outArgs; + if (exc) { + ret = unmarshal.readValue( + css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get())); + if (!typelib_typedescription_isAssignableFrom( + (css::uno::TypeDescription( + cppu::UnoType< css::uno::RuntimeException >::get()). + get()), + ret.getType().get())) + { + sal_Int32 n = 0; + typelib_TypeDescriptionReference ** p = nullptr; + switch (req.member.get()->eTypeClass) { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + { + typelib_InterfaceAttributeTypeDescription * atd = + reinterpret_cast< + typelib_InterfaceAttributeTypeDescription * >( + req.member.get()); + n = req.setter ? atd->nSetExceptions : atd->nGetExceptions; + p = req.setter + ? atd->ppSetExceptions : atd->ppGetExceptions; + break; + } + case typelib_TypeClass_INTERFACE_METHOD: + { + typelib_InterfaceMethodTypeDescription * mtd = + reinterpret_cast< + typelib_InterfaceMethodTypeDescription * >( + req.member.get()); + n = mtd->nExceptions; + p = mtd->ppExceptions; + break; + } + default: + assert(false); // this cannot happen + break; + } + bool bOk = false; + for (sal_Int32 i = 0; i != n; ++i) { + if (typelib_typedescriptionreference_isAssignableFrom( + p[i], + reinterpret_cast< typelib_TypeDescriptionReference * >( + ret.getType().get()))) + { + bOk = true; + break; + } + } + if (!bOk) { + throw css::uno::RuntimeException( + "URP: reply message with bad exception type received"); + } + } + } else { + switch (req.member.get()->eTypeClass) { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + if (!req.setter) { + ret = unmarshal.readValue( + css::uno::TypeDescription( + reinterpret_cast< + typelib_InterfaceAttributeTypeDescription * >( + req.member.get())-> + pAttributeTypeRef)); + } + break; + case typelib_TypeClass_INTERFACE_METHOD: + { + typelib_InterfaceMethodTypeDescription * mtd = + reinterpret_cast< + typelib_InterfaceMethodTypeDescription * >( + req.member.get()); + ret = unmarshal.readValue( + css::uno::TypeDescription(mtd->pReturnTypeRef)); + for (sal_Int32 i = 0; i != mtd->nParams; ++i) { + if (mtd->pParams[i].bOut) { + outArgs.push_back( + unmarshal.readValue( + css::uno::TypeDescription( + mtd->pParams[i].pTypeRef))); + } + } + break; + } + default: + assert(false); // this cannot happen + break; + } + } + switch (req.kind) { + case OutgoingRequest::KIND_NORMAL: + { + std::unique_ptr< IncomingReply > resp( + new IncomingReply(exc, ret, std::move(outArgs))); + uno_threadpool_putJob( + bridge_->getThreadPool(), tid.getHandle(), resp.get(), nullptr, + false); + resp.release(); + break; + } + case OutgoingRequest::KIND_REQUEST_CHANGE: + assert(outArgs.empty()); + bridge_->handleRequestChangeReply(exc, ret); + break; + case OutgoingRequest::KIND_COMMIT_CHANGE: + assert(outArgs.empty()); + bridge_->handleCommitChangeReply(exc, ret); + break; + default: + assert(false); // this cannot happen + break; + } +} + +rtl::ByteSequence Reader::getTid(Unmarshal & unmarshal, bool newTid) const { + if (newTid) { + return unmarshal.readTid(); + } + if (lastTid_.getLength() == 0) { + throw css::uno::RuntimeException( + "URP: message with NEWTID received when last TID has not yet been" + " set"); + } + return lastTid_; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/reader.hxx b/binaryurp/source/reader.hxx new file mode 100644 index 000000000..2f3ec0dd9 --- /dev/null +++ b/binaryurp/source/reader.hxx @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <rtl/byteseq.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <salhelper/thread.hxx> +#include <typelib/typedescription.hxx> + +#include "readerstate.hxx" + +namespace binaryurp +{ +class Bridge; +class Unmarshal; +} + +namespace binaryurp +{ +class Reader : public salhelper::Thread +{ +public: + explicit Reader(rtl::Reference<Bridge> const& bridge); + +private: + virtual ~Reader() override; + + virtual void execute() override; + + void readMessage(Unmarshal& unmarshal); + + void readReplyMessage(Unmarshal& unmarshal, sal_uInt8 flags1); + + rtl::ByteSequence getTid(Unmarshal& unmarshal, bool newTid) const; + + rtl::Reference<Bridge> bridge_; + com::sun::star::uno::TypeDescription lastType_; + OUString lastOid_; + rtl::ByteSequence lastTid_; + ReaderState state_; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/readerstate.hxx b/binaryurp/source/readerstate.hxx new file mode 100644 index 000000000..a93a45dfc --- /dev/null +++ b/binaryurp/source/readerstate.hxx @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <rtl/byteseq.hxx> +#include <rtl/ustring.hxx> +#include <typelib/typedescription.hxx> + +#include "cache.hxx" + +namespace binaryurp +{ +struct ReaderState +{ +private: + ReaderState(const ReaderState&) = delete; + ReaderState& operator=(const ReaderState&) = delete; + +public: + ReaderState() {} + + com::sun::star::uno::TypeDescription typeCache[cache::size]; + OUString oidCache[cache::size]; + rtl::ByteSequence tidCache[cache::size]; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/specialfunctionids.hxx b/binaryurp/source/specialfunctionids.hxx new file mode 100644 index 000000000..e2b6ae0f8 --- /dev/null +++ b/binaryurp/source/specialfunctionids.hxx @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +namespace binaryurp +{ +enum SpecialFunctionIds +{ + SPECIAL_FUNCTION_ID_QUERY_INTERFACE = 0, + + SPECIAL_FUNCTION_ID_RESERVED = 1, + + SPECIAL_FUNCTION_ID_RELEASE = 2, + + SPECIAL_FUNCTION_ID_REQUEST_CHANGE = 4, + + SPECIAL_FUNCTION_ID_COMMIT_CHANGE = 5 +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/unmarshal.cxx b/binaryurp/source/unmarshal.cxx new file mode 100644 index 000000000..7d943d37b --- /dev/null +++ b/binaryurp/source/unmarshal.cxx @@ -0,0 +1,491 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> +#include <cstdlib> +#include <new> +#include <utility> +#include <vector> + +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <cppu/unotype.hxx> +#include <rtl/byteseq.hxx> +#include <rtl/ref.hxx> +#include <rtl/textcvt.h> +#include <rtl/textenc.h> +#include <rtl/ustring.h> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <typelib/typeclass.h> +#include <typelib/typedescription.h> +#include <typelib/typedescription.hxx> +#include <uno/any2.h> +#include <uno/data.h> +#include <uno/dispatcher.hxx> + +#include "binaryany.hxx" +#include "bridge.hxx" +#include "cache.hxx" +#include "readerstate.hxx" +#include "unmarshal.hxx" + +namespace binaryurp { + +namespace { + +void * allocate(sal_Size size) { + void * p = std::malloc(size); + if (p == nullptr) { + throw std::bad_alloc(); + } + return p; +} + +std::vector< BinaryAny >::iterator copyMemberValues( + css::uno::TypeDescription const & type, + std::vector< BinaryAny >::iterator const & it, void * buffer) noexcept +{ + assert( + type.is() && + (type.get()->eTypeClass == typelib_TypeClass_STRUCT || + type.get()->eTypeClass == typelib_TypeClass_EXCEPTION) && + buffer != nullptr); + type.makeComplete(); + std::vector< BinaryAny >::iterator i(it); + typelib_CompoundTypeDescription * ctd = + reinterpret_cast< typelib_CompoundTypeDescription * >(type.get()); + if (ctd->pBaseTypeDescription != nullptr) { + i = copyMemberValues( + css::uno::TypeDescription(&ctd->pBaseTypeDescription->aBase), i, + buffer); + } + for (sal_Int32 j = 0; j != ctd->nMembers; ++j) { + uno_type_copyData( + static_cast< char * >(buffer) + ctd->pMemberOffsets[j], + i++->getValue(css::uno::TypeDescription(ctd->ppTypeRefs[j])), + ctd->ppTypeRefs[j], nullptr); + } + return i; +} + +} + +Unmarshal::Unmarshal( + rtl::Reference< Bridge > bridge, ReaderState & state, + css::uno::Sequence< sal_Int8 > const & buffer): + bridge_(std::move(bridge)), state_(state), buffer_(buffer) +{ + data_ = reinterpret_cast< sal_uInt8 const * >(buffer_.getConstArray()); + end_ = data_ + buffer_.getLength(); +} + +Unmarshal::~Unmarshal() {} + +sal_uInt8 Unmarshal::read8() { + check(1); + return *data_++; +} + +sal_uInt16 Unmarshal::read16() { + check(2); + sal_uInt16 n = static_cast< sal_uInt16 >(*data_++) << 8; + return n | *data_++; +} + +sal_uInt32 Unmarshal::read32() { + check(4); + sal_uInt32 n = static_cast< sal_uInt32 >(*data_++) << 24; + n |= static_cast< sal_uInt32 >(*data_++) << 16; + n |= static_cast< sal_uInt32 >(*data_++) << 8; + return n | *data_++; +} + +css::uno::TypeDescription Unmarshal::readType() { + sal_uInt8 flags = read8(); + typelib_TypeClass tc = static_cast< typelib_TypeClass >(flags & 0x7F); + switch (tc) { + case typelib_TypeClass_VOID: + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + 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_CHAR: + case typelib_TypeClass_STRING: + case typelib_TypeClass_TYPE: + case typelib_TypeClass_ANY: + if ((flags & 0x80) != 0) { + throw css::io::IOException( + "binaryurp::Unmarshal: cache flag of simple type is set"); + } + return css::uno::TypeDescription( + *typelib_static_type_getByTypeClass(tc)); + case typelib_TypeClass_SEQUENCE: + case typelib_TypeClass_ENUM: + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_INTERFACE: + { + sal_uInt16 idx = readCacheIndex(); + if ((flags & 0x80) == 0) { + if (idx == cache::ignore || !state_.typeCache[idx].is()) { + throw css::io::IOException( + "binaryurp::Unmarshal: unknown type cache index"); + } + return state_.typeCache[idx]; + } else { + OUString const str(readString()); + css::uno::TypeDescription t(str); + if (!t.is() || t.get()->eTypeClass != tc) { + + throw css::io::IOException( + "binaryurp::Unmarshal: type with unknown name: " + str); + } + for (css::uno::TypeDescription t2(t); + t2.get()->eTypeClass == typelib_TypeClass_SEQUENCE;) + { + t2.makeComplete(); + t2 = css::uno::TypeDescription( + reinterpret_cast< typelib_IndirectTypeDescription * >( + t2.get())->pType); + if (!t2.is()) { + throw css::io::IOException( + "binaryurp::Unmarshal: sequence type with unknown" + " component type"); + } + switch (t2.get()->eTypeClass) { + case typelib_TypeClass_VOID: + case typelib_TypeClass_EXCEPTION: + throw css::io::IOException( + "binaryurp::Unmarshal: sequence type with bad" + " component type"); + default: + break; + } + } + if (idx != cache::ignore) { + state_.typeCache[idx] = t; + } + return t; + } + } + default: + throw css::io::IOException( + "binaryurp::Unmarshal: type of unknown type class"); + } +} + +OUString Unmarshal::readOid() { + OUString oid(readString()); + for (sal_Int32 i = 0; i != oid.getLength(); ++i) { + if (oid[i] > 0x7F) { + throw css::io::IOException( + "binaryurp::Unmarshal: OID contains non-ASCII character"); + } + } + sal_uInt16 idx = readCacheIndex(); + if (oid.isEmpty() && idx != cache::ignore) { + if (state_.oidCache[idx].isEmpty()) { + throw css::io::IOException( + "binaryurp::Unmarshal: unknown OID cache index"); + } + return state_.oidCache[idx]; + } + if (idx != cache::ignore) { + state_.oidCache[idx] = oid; + } + return oid; +} + +rtl::ByteSequence Unmarshal::readTid() { + rtl::ByteSequence tid( + *static_cast< sal_Sequence * const * >( + readSequence( + css::uno::TypeDescription( + cppu::UnoType< css::uno::Sequence< sal_Int8 > >::get())). + getValue( + css::uno::TypeDescription( + cppu::UnoType< css::uno::Sequence< sal_Int8 > >::get())))); + sal_uInt16 idx = readCacheIndex(); + if (tid.getLength() == 0) { + if (idx == cache::ignore || state_.tidCache[idx].getLength() == 0) { + throw css::io::IOException( + "binaryurp::Unmarshal: unknown TID cache index"); + } + return state_.tidCache[idx]; + } + if (idx != cache::ignore) { + state_.tidCache[idx] = tid; + } + return tid; +} + +BinaryAny Unmarshal::readValue(css::uno::TypeDescription const & type) { + assert(type.is()); + switch (type.get()->eTypeClass) { + default: + std::abort(); // this cannot happen + // pseudo fall-through to avoid compiler warnings + case typelib_TypeClass_VOID: + return BinaryAny(); + case typelib_TypeClass_BOOLEAN: + { + sal_uInt8 v = read8(); + if (v > 1) { + throw css::io::IOException( + "binaryurp::Unmarshal: boolean of unknown value"); + } + return BinaryAny(type, &v); + } + case typelib_TypeClass_BYTE: + { + sal_uInt8 v = read8(); + return BinaryAny(type, &v); + } + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_CHAR: + { + sal_uInt16 v = read16(); + return BinaryAny(type, &v); + } + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_FLOAT: + { + sal_uInt32 v = read32(); + return BinaryAny(type, &v); + } + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + case typelib_TypeClass_DOUBLE: + { + sal_uInt64 v = read64(); + return BinaryAny(type, &v); + } + case typelib_TypeClass_STRING: + { + OUString v(readString()); + return BinaryAny(type, &v.pData); + } + case typelib_TypeClass_TYPE: + { + css::uno::TypeDescription v(readType()); + typelib_TypeDescription * p = v.get(); + return BinaryAny(type, &p); + } + case typelib_TypeClass_ANY: + { + css::uno::TypeDescription t(readType()); + if (t.get()->eTypeClass == typelib_TypeClass_ANY) { + throw css::io::IOException( + "binaryurp::Unmarshal: any of type ANY"); + } + return readValue(t); + } + case typelib_TypeClass_SEQUENCE: + type.makeComplete(); + return readSequence(type); + case typelib_TypeClass_ENUM: + { + sal_Int32 v = static_cast< sal_Int32 >(read32()); + type.makeComplete(); + typelib_EnumTypeDescription * etd = + reinterpret_cast< typelib_EnumTypeDescription * >(type.get()); + bool bFound = false; + for (sal_Int32 i = 0; i != etd->nEnumValues; ++i) { + if (etd->pEnumValues[i] == v) { + bFound = true; + break; + } + } + if (!bFound) { + throw css::io::IOException( + "binaryurp::Unmarshal: unknown enum value"); + } + return BinaryAny(type, &v); + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + std::vector< BinaryAny > as; + readMemberValues(type, &as); + void * buf = allocate(type.get()->nSize); + copyMemberValues(type, as.begin(), buf); + uno_Any raw; + raw.pType = reinterpret_cast< typelib_TypeDescriptionReference * >( + type.get()); + raw.pData = buf; + raw.pReserved = nullptr; + return BinaryAny(raw); + } + case typelib_TypeClass_INTERFACE: + { + css::uno::UnoInterfaceReference obj( + bridge_->registerIncomingInterface(readOid(), type)); + return BinaryAny(type, &obj.m_pUnoI); + } + } +} + +void Unmarshal::done() const { + if (data_ != end_) { + throw css::io::IOException( + "binaryurp::Unmarshal: block contains excess data"); + } +} + +void Unmarshal::check(sal_Int32 size) const { + if (end_ - data_ < size) { + throw css::io::IOException( + "binaryurp::Unmarshal: trying to read past end of block"); + } +} + +sal_uInt32 Unmarshal::readCompressed() { + sal_uInt8 n = read8(); + return n == 0xFF ? read32() : n; +} + +sal_uInt16 Unmarshal::readCacheIndex() { + sal_uInt16 idx = read16(); + if (idx >= cache::size && idx != cache::ignore) { + throw css::io::IOException( + "binaryurp::Unmarshal: cache index out of range"); + } + return idx; +} + +sal_uInt64 Unmarshal::read64() { + check(8); + sal_uInt64 n = static_cast< sal_uInt64 >(*data_++) << 56; + n |= static_cast< sal_uInt64 >(*data_++) << 48; + n |= static_cast< sal_uInt64 >(*data_++) << 40; + n |= static_cast< sal_uInt64 >(*data_++) << 32; + n |= static_cast< sal_uInt64 >(*data_++) << 24; + n |= static_cast< sal_uInt64 >(*data_++) << 16; + n |= static_cast< sal_uInt64 >(*data_++) << 8; + return n | *data_++; +} + +OUString Unmarshal::readString() { + sal_uInt32 n = readCompressed(); + if (n > SAL_MAX_INT32) { + throw css::uno::RuntimeException( + "binaryurp::Unmarshal: string size too large"); + } + check(static_cast< sal_Int32 >(n)); + OUString s; + if (!rtl_convertStringToUString( + &s.pData, reinterpret_cast< char const * >(data_), + static_cast< sal_Int32 >(n), RTL_TEXTENCODING_UTF8, + (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR))) + { + throw css::io::IOException( + "binaryurp::Unmarshal: string does not contain UTF-8"); + } + data_ += n; + return s; +} + +BinaryAny Unmarshal::readSequence(css::uno::TypeDescription const & type) { + assert(type.is() && type.get()->eTypeClass == typelib_TypeClass_SEQUENCE); + sal_uInt32 n = readCompressed(); + if (n > SAL_MAX_INT32) { + throw css::uno::RuntimeException( + "binaryurp::Unmarshal: sequence size too large"); + } + if (n == 0) { + return BinaryAny(type, nullptr); + } + css::uno::TypeDescription ctd( + reinterpret_cast< typelib_IndirectTypeDescription * >( + type.get())->pType); + if (ctd.get()->eTypeClass == typelib_TypeClass_BYTE) { + check(static_cast< sal_Int32 >(n)); + rtl::ByteSequence s( + reinterpret_cast< sal_Int8 const * >(data_), + static_cast< sal_Int32 >(n)); + data_ += n; + sal_Sequence * p = s.getHandle(); + return BinaryAny(type, &p); + } + std::vector< BinaryAny > as; + as.reserve(n); + for (sal_uInt32 i = 0; i != n; ++i) { + as.push_back(readValue(ctd)); + } + assert(ctd.get()->nSize >= 0); + sal_uInt64 size = static_cast< sal_uInt64 >(n) * + static_cast< sal_uInt64 >(ctd.get()->nSize); + // sal_uInt32 * sal_Int32 -> sal_uInt64 cannot overflow + if (size > SAL_MAX_SIZE - SAL_SEQUENCE_HEADER_SIZE) { + throw css::uno::RuntimeException( + "binaryurp::Unmarshal: sequence size too large"); + } + void * buf = allocate( + SAL_SEQUENCE_HEADER_SIZE + static_cast< sal_Size >(size)); + static_cast< sal_Sequence * >(buf)->nRefCount = 0; + static_cast< sal_Sequence * >(buf)->nElements = + static_cast< sal_Int32 >(n); + for (sal_uInt32 i = 0; i != n; ++i) { + uno_copyData( + static_cast< sal_Sequence * >(buf)->elements + i * ctd.get()->nSize, + as[i].getValue(ctd), ctd.get(), nullptr); + } + return BinaryAny(type, &buf); +} + +void Unmarshal::readMemberValues( + css::uno::TypeDescription const & type, std::vector< BinaryAny > * values) +{ + assert( + type.is() && + (type.get()->eTypeClass == typelib_TypeClass_STRUCT || + type.get()->eTypeClass == typelib_TypeClass_EXCEPTION) && + values != nullptr); + type.makeComplete(); + typelib_CompoundTypeDescription * ctd = + reinterpret_cast< typelib_CompoundTypeDescription * >(type.get()); + if (ctd->pBaseTypeDescription != nullptr) { + readMemberValues( + css::uno::TypeDescription(&ctd->pBaseTypeDescription->aBase), + values); + } + values->reserve(values->size() + ctd->nMembers); + for (sal_Int32 i = 0; i != ctd->nMembers; ++i) { + values->push_back( + readValue(css::uno::TypeDescription(ctd->ppTypeRefs[i]))); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/unmarshal.hxx b/binaryurp/source/unmarshal.hxx new file mode 100644 index 000000000..1972c30d0 --- /dev/null +++ b/binaryurp/source/unmarshal.hxx @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <vector> + +#include <com/sun/star/uno/Sequence.hxx> +#include <rtl/ref.hxx> +#include <sal/types.h> +#include <typelib/typedescription.hxx> + +namespace binaryurp { + class BinaryAny; + class Bridge; + struct ReaderState; +} +namespace com::sun::star::uno { class TypeDescription; } + +namespace binaryurp { + +class Unmarshal { +public: + Unmarshal( + rtl::Reference< Bridge > bridge, ReaderState & state, + com::sun::star::uno::Sequence< sal_Int8 > const & buffer); + + ~Unmarshal(); + + sal_uInt8 read8(); + + sal_uInt16 read16(); + + sal_uInt32 read32(); + + com::sun::star::uno::TypeDescription readType(); + + OUString readOid(); + + rtl::ByteSequence readTid(); + + BinaryAny readValue(com::sun::star::uno::TypeDescription const & type); + + void done() const; + +private: + Unmarshal(const Unmarshal&) = delete; + Unmarshal& operator=(const Unmarshal&) = delete; + + void check(sal_Int32 size) const; + + sal_uInt32 readCompressed(); + + sal_uInt16 readCacheIndex(); + + sal_uInt64 read64(); + + OUString readString(); + + BinaryAny readSequence(com::sun::star::uno::TypeDescription const & type); + + void readMemberValues( + com::sun::star::uno::TypeDescription const & type, + std::vector< BinaryAny > * values); + + rtl::Reference< Bridge > bridge_; + ReaderState & state_; + com::sun::star::uno::Sequence< sal_Int8 > buffer_; + sal_uInt8 const * data_; + sal_uInt8 const * end_; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/writer.cxx b/binaryurp/source/writer.cxx new file mode 100644 index 000000000..539d8a2c5 --- /dev/null +++ b/binaryurp/source/writer.cxx @@ -0,0 +1,455 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> +#include <cstddef> +#include <cstring> +#include <exception> +#include <limits> +#include <utility> +#include <vector> + +#include <com/sun/star/connection/XConnection.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/uno/XCurrentContext.hpp> +#include <cppuhelper/exc_hlp.hxx> +#include <sal/log.hxx> +#include <uno/dispatcher.hxx> + +#include "binaryany.hxx" +#include "bridge.hxx" +#include "currentcontext.hxx" +#include "specialfunctionids.hxx" +#include "writer.hxx" + +namespace binaryurp { + +Writer::Item::Item() + : request(false) + , setter(false) + , exception(false) + , setCurrentContextMode(false) +{} + +Writer::Item::Item( + rtl::ByteSequence theTid, OUString theOid, + css::uno::TypeDescription theType, + css::uno::TypeDescription theMember, + std::vector< BinaryAny >&& inArguments, + css::uno::UnoInterfaceReference theCurrentContext): + tid(std::move(theTid)), oid(std::move(theOid)), type(std::move(theType)), member(std::move(theMember)), + currentContext(std::move(theCurrentContext)), arguments(std::move(inArguments)), + request(true), setter(false), exception(false), setCurrentContextMode(false) +{} + +Writer::Item::Item( + rtl::ByteSequence theTid, + css::uno::TypeDescription theMember, bool theSetter, + bool theException, BinaryAny theReturnValue, + std::vector< BinaryAny >&& outArguments, + bool theSetCurrentContextMode): + tid(std::move(theTid)), member(std::move(theMember)), + returnValue(std::move(theReturnValue)), arguments(std::move(outArguments)), + request(false), setter(theSetter), + exception(theException), setCurrentContextMode(theSetCurrentContextMode) +{} + +Writer::Writer(rtl::Reference< Bridge > const & bridge): + Thread("binaryurpWriter"), bridge_(bridge), marshal_(bridge, state_), + stop_(false) +{ + assert(bridge.is()); +} + +void Writer::sendDirectRequest( + rtl::ByteSequence const & tid, OUString const & oid, + css::uno::TypeDescription const & type, + css::uno::TypeDescription const & member, + std::vector< BinaryAny > const & inArguments) +{ + assert(!unblocked_.check()); + sendRequest( + tid, oid, type, member, inArguments, false, + css::uno::UnoInterfaceReference()); +} + +void Writer::sendDirectReply( + rtl::ByteSequence const & tid, css::uno::TypeDescription const & member, + bool exception, BinaryAny const & returnValue, + std::vector< BinaryAny > const & outArguments) +{ + assert(!unblocked_.check()); + sendReply(tid, member, false, exception, returnValue,outArguments); +} + +void Writer::queueRequest( + rtl::ByteSequence const & tid, OUString const & oid, + css::uno::TypeDescription const & type, + css::uno::TypeDescription const & member, + std::vector< BinaryAny >&& inArguments) +{ + css::uno::UnoInterfaceReference cc(current_context::get()); + std::lock_guard g(mutex_); + queue_.emplace_back(tid, oid, type, member, std::move(inArguments), cc); + items_.set(); +} + +void Writer::queueReply( + rtl::ByteSequence const & tid, + com::sun::star::uno::TypeDescription const & member, bool setter, + bool exception, BinaryAny const & returnValue, + std::vector< BinaryAny >&& outArguments, bool setCurrentContextMode) +{ + std::lock_guard g(mutex_); + queue_.emplace_back( + tid, member, setter, exception, returnValue, std::move(outArguments), + setCurrentContextMode); + items_.set(); +} + +void Writer::unblock() { + // Assumes that osl::Condition::set works as a memory barrier, so that + // changes made by preceding sendDirectRequest/Reply calls are visible to + // subsequent sendRequest/Reply calls: + unblocked_.set(); +} + +void Writer::stop() { + { + std::lock_guard g(mutex_); + stop_ = true; + } + unblocked_.set(); + items_.set(); +} + +Writer::~Writer() {} + +void Writer::execute() { + try { + unblocked_.wait(); + for (;;) { + items_.wait(); + Item item; + { + std::lock_guard g(mutex_); + if (stop_) { + return; + } + assert(!queue_.empty()); + item = queue_.front(); + queue_.pop_front(); + if (queue_.empty()) { + items_.reset(); + } + } + if (item.request) { + sendRequest( + item.tid, item.oid, item.type, item.member, item.arguments, + (item.oid != "UrpProtocolProperties" && + !item.member.equals( + css::uno::TypeDescription( + "com.sun.star.uno.XInterface::release")) && + bridge_->isCurrentContextMode()), + item.currentContext); + } else { + sendReply( + item.tid, item.member, item.setter, item.exception, + item.returnValue, item.arguments); + if (item.setCurrentContextMode) { + bridge_->setCurrentContextMode(); + } + } + } + } catch (const css::uno::Exception & e) { + SAL_INFO("binaryurp", "caught " << e); + } catch (const std::exception & e) { + SAL_INFO("binaryurp", "caught C++ exception " << e.what()); + } + bridge_->terminate(false); + bridge_.clear(); +} + +void Writer::sendRequest( + rtl::ByteSequence const & tid, OUString const & oid, + css::uno::TypeDescription const & type, + css::uno::TypeDescription const & member, + std::vector< BinaryAny > const & inArguments, bool currentContextMode, + css::uno::UnoInterfaceReference const & currentContext) +{ + assert(tid.getLength() != 0); + assert(!oid.isEmpty()); + assert(member.is()); + css::uno::TypeDescription t(type); + sal_Int32 functionId = 0; + bool bForceSynchronous = false; + member.makeComplete(); + switch (member.get()->eTypeClass) { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + { + typelib_InterfaceAttributeTypeDescription * atd = + reinterpret_cast< typelib_InterfaceAttributeTypeDescription * >( + member.get()); + assert(atd->pInterface != nullptr); + if (!t.is()) { + t = css::uno::TypeDescription(&atd->pInterface->aBase); + } + t.makeComplete(); + functionId = atd->pInterface->pMapMemberIndexToFunctionIndex[ + atd->aBase.nPosition]; + if (!inArguments.empty()) { // setter + ++functionId; + } + break; + } + case typelib_TypeClass_INTERFACE_METHOD: + { + typelib_InterfaceMethodTypeDescription * mtd = + reinterpret_cast< typelib_InterfaceMethodTypeDescription * >( + member.get()); + assert(mtd->pInterface != nullptr); + if (!t.is()) { + t = css::uno::TypeDescription(&mtd->pInterface->aBase); + } + t.makeComplete(); + functionId = mtd->pInterface->pMapMemberIndexToFunctionIndex[ + mtd->aBase.nPosition]; + bForceSynchronous = mtd->bOneWay && + functionId != SPECIAL_FUNCTION_ID_RELEASE; + break; + } + default: + assert(false); // this cannot happen + break; + } + assert(functionId >= 0); + if (functionId > SAL_MAX_UINT16) { + throw css::uno::RuntimeException("function ID too large for URP"); + } + std::vector< unsigned char > buf; + bool newType = !(lastType_.is() && t.equals(lastType_)); + bool newOid = oid != lastOid_; + bool newTid = tid != lastTid_; + if (newType || newOid || newTid || bForceSynchronous || functionId > 0x3FFF) + // > 14 bit function ID + { + Marshal::write8( + &buf, + (0xC0 | (newType ? 0x20 : 0) | (newOid ? 0x10 : 0) | + (newTid ? 0x08 : 0) | (functionId > 0xFF ? 0x04 : 0) | + (bForceSynchronous ? 0x01 : 0))); + // bit 7: LONGHEADER, bit 6: REQUEST, bit 5: NEWTYPE, bit 4: NEWOID, + // bit 3: NEWTID, bit 2: FUNCTIONID16, bit 0: MOREFLAGS + if (bForceSynchronous) { + Marshal::write8(&buf, 0xC0); // bit 7: MUSTREPLY, bit 6: SYNCHRONOUS + } + if (functionId <= 0xFF) { + Marshal::write8(&buf, static_cast< sal_uInt8 >(functionId)); + } else { + Marshal::write16(&buf, static_cast< sal_uInt16 >(functionId)); + } + if (newType) { + marshal_.writeType(&buf, t); + } + if (newOid) { + marshal_.writeOid(&buf, oid); + } + if (newTid) { + marshal_.writeTid(&buf, tid); + } + } else if (functionId <= 0x3F) { // <= 6 bit function ID + Marshal::write8(&buf, static_cast< sal_uInt8 >(functionId)); + // bit 7: !LONGHEADER, bit 6: !FUNCTIONID14 + } else { + Marshal::write8( + &buf, static_cast< sal_uInt8 >(0x40 | (functionId >> 8))); + // bit 7: !LONGHEADER, bit 6: FUNCTIONID14 + Marshal::write8(&buf, functionId & 0xFF); + } + if (currentContextMode) { + css::uno::UnoInterfaceReference cc(currentContext); + marshal_.writeValue( + &buf, + css::uno::TypeDescription( + cppu::UnoType< + css::uno::Reference< css::uno::XCurrentContext > >::get()), + BinaryAny( + css::uno::TypeDescription( + cppu::UnoType< + css::uno::Reference< + css::uno::XCurrentContext > >::get()), + &cc.m_pUnoI)); + } + switch (member.get()->eTypeClass) { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + if (!inArguments.empty()) { // setter + assert(inArguments.size() == 1); + marshal_.writeValue( + &buf, + css::uno::TypeDescription( + reinterpret_cast< + typelib_InterfaceAttributeTypeDescription * >( + member.get())-> + pAttributeTypeRef), + inArguments.front()); + } + break; + case typelib_TypeClass_INTERFACE_METHOD: + { + typelib_InterfaceMethodTypeDescription * mtd = + reinterpret_cast< typelib_InterfaceMethodTypeDescription * >( + member.get()); + std::vector< BinaryAny >::const_iterator i(inArguments.begin()); + for (sal_Int32 j = 0; j != mtd->nParams; ++j) { + if (mtd->pParams[j].bIn) { + marshal_.writeValue( + &buf, + css::uno::TypeDescription(mtd->pParams[j].pTypeRef), + *i++); + } + } + assert(i == inArguments.end()); + break; + } + default: + assert(false); // this cannot happen + break; + } + sendMessage(buf); + lastType_ = t; + lastOid_ = oid; + lastTid_ = tid; +} + +void Writer::sendReply( + rtl::ByteSequence const & tid, + com::sun::star::uno::TypeDescription const & member, bool setter, + bool exception, BinaryAny const & returnValue, + std::vector< BinaryAny > const & outArguments) +{ + assert(tid.getLength() != 0); + assert(member.is()); + assert(member.get()->bComplete); + std::vector< unsigned char > buf; + bool newTid = tid != lastTid_; + Marshal::write8(&buf, 0x80 | (exception ? 0x20 : 0) | (newTid ? 0x08 : 0)); + // bit 7: LONGHEADER; bit 6: !REQUEST; bit 5: EXCEPTION; bit 3: NEWTID + if (newTid) { + marshal_.writeTid(&buf, tid); + } + if (exception) { + marshal_.writeValue( + &buf, + css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()), + returnValue); + } else { + switch (member.get()->eTypeClass) { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + if (!setter) { + marshal_.writeValue( + &buf, + css::uno::TypeDescription( + reinterpret_cast< + typelib_InterfaceAttributeTypeDescription * >( + member.get())-> + pAttributeTypeRef), + returnValue); + } + break; + case typelib_TypeClass_INTERFACE_METHOD: + { + typelib_InterfaceMethodTypeDescription * mtd = + reinterpret_cast< + typelib_InterfaceMethodTypeDescription * >( + member.get()); + marshal_.writeValue( + &buf, css::uno::TypeDescription(mtd->pReturnTypeRef), + returnValue); + std::vector< BinaryAny >::const_iterator i( + outArguments.begin()); + for (sal_Int32 j = 0; j != mtd->nParams; ++j) { + if (mtd->pParams[j].bOut) { + marshal_.writeValue( + &buf, + css::uno::TypeDescription(mtd->pParams[j].pTypeRef), + *i++); + } + } + assert(i == outArguments.end()); + break; + } + default: + assert(false); // this cannot happen + break; + } + } + sendMessage(buf); + lastTid_ = tid; + bridge_->decrementCalls(); +} + +void Writer::sendMessage(std::vector< unsigned char > const & buffer) { + std::vector< unsigned char > header; + if (buffer.size() > SAL_MAX_UINT32) { + throw css::uno::RuntimeException( + "message too large for URP"); + } + Marshal::write32(&header, static_cast< sal_uInt32 >(buffer.size())); + Marshal::write32(&header, 1); + assert(!buffer.empty()); + unsigned char const * p = buffer.data(); + std::vector< unsigned char >::size_type n = buffer.size(); + assert(header.size() <= SAL_MAX_INT32); + /*static_*/assert(SAL_MAX_INT32 <= std::numeric_limits<std::size_t>::max()); + std::size_t k = SAL_MAX_INT32 - header.size(); + if (n < k) { + k = n; + } + css::uno::Sequence<sal_Int8> s(header.size() + k); + assert(!header.empty()); + std::memcpy(s.getArray(), header.data(), header.size()); + for (;;) { + std::memcpy(s.getArray() + s.getLength() - k, p, k); + try { + bridge_->getConnection()->write(s); + } catch (const css::io::IOException & e) { + css::uno::Any exc(cppu::getCaughtException()); + throw css::lang::WrappedTargetRuntimeException( + "Binary URP write raised IO exception: " + e.Message, + css::uno::Reference< css::uno::XInterface >(), exc); + } + n -= k; + if (n == 0) { + break; + } + p += k; + k = SAL_MAX_INT32; + if (n < k) { + k = n; + } + s.realloc(k); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/writer.hxx b/binaryurp/source/writer.hxx new file mode 100644 index 000000000..e2061502a --- /dev/null +++ b/binaryurp/source/writer.hxx @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <deque> +#include <mutex> +#include <vector> + +#include <osl/conditn.hxx> +#include <rtl/byteseq.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <salhelper/thread.hxx> +#include <typelib/typedescription.hxx> +#include <uno/dispatcher.hxx> + +#include "binaryany.hxx" +#include "marshal.hxx" +#include "writerstate.hxx" + +namespace binaryurp { class Bridge; } + +namespace binaryurp { + +class Writer: public salhelper::Thread +{ +public: + explicit Writer(rtl::Reference< Bridge > const & bridge); + + // Only called from Bridge::reader_ thread, and only before Bridge::writer_ + // thread is unblocked: + void sendDirectRequest( + rtl::ByteSequence const & tid, OUString const & oid, + com::sun::star::uno::TypeDescription const & type, + com::sun::star::uno::TypeDescription const & member, + std::vector< BinaryAny > const & inArguments); + + // Only called from Bridge::reader_ thread, and only before Bridge::writer_ + // thread is unblocked: + void sendDirectReply( + rtl::ByteSequence const & tid, + com::sun::star::uno::TypeDescription const & member, + bool exception, BinaryAny const & returnValue, + std::vector< BinaryAny > const & outArguments); + + void queueRequest( + rtl::ByteSequence const & tid, OUString const & oid, + com::sun::star::uno::TypeDescription const & type, + com::sun::star::uno::TypeDescription const & member, + std::vector< BinaryAny >&& inArguments); + + void queueReply( + rtl::ByteSequence const & tid, + com::sun::star::uno::TypeDescription const & member, bool setter, + bool exception, BinaryAny const & returnValue, + std::vector< BinaryAny >&& outArguments, + bool setCurrentContextMode); + + void unblock(); + + void stop(); + +private: + virtual ~Writer() override; + + virtual void execute() override; + + void sendRequest( + rtl::ByteSequence const & tid, OUString const & oid, + com::sun::star::uno::TypeDescription const & type, + com::sun::star::uno::TypeDescription const & member, + std::vector< BinaryAny > const & inArguments, bool currentContextMode, + com::sun::star::uno::UnoInterfaceReference const & currentContext); + + void sendReply( + rtl::ByteSequence const & tid, + com::sun::star::uno::TypeDescription const & member, bool setter, + bool exception, BinaryAny const & returnValue, + std::vector< BinaryAny > const & outArguments); + + void sendMessage(std::vector< unsigned char > const & buffer); + + struct Item { + Item(); + + // Request: + Item( + rtl::ByteSequence theTid, OUString theOid, + com::sun::star::uno::TypeDescription theType, + com::sun::star::uno::TypeDescription theMember, + std::vector< BinaryAny >&& inArguments, + com::sun::star::uno::UnoInterfaceReference theCurrentContext); + + // Reply: + Item( + rtl::ByteSequence theTid, + com::sun::star::uno::TypeDescription theMember, + bool theSetter, bool theException, BinaryAny theReturnValue, + std::vector< BinaryAny >&& outArguments, + bool theSetCurrentContextMode); + + rtl::ByteSequence tid; // request + reply + OUString oid; // request + com::sun::star::uno::TypeDescription type; // request + com::sun::star::uno::TypeDescription member; // request + reply + com::sun::star::uno::UnoInterfaceReference currentContext; // request + BinaryAny returnValue; // reply + std::vector< BinaryAny > arguments; // request: inArguments; reply: outArguments + bool request; + bool setter; // reply + bool exception; // reply + bool setCurrentContextMode; // reply + }; + + rtl::Reference< Bridge > bridge_; + WriterState state_; + Marshal marshal_; + com::sun::star::uno::TypeDescription lastType_; + OUString lastOid_; + rtl::ByteSequence lastTid_; + osl::Condition unblocked_; + osl::Condition items_; + + std::mutex mutex_; + std::deque< Item > queue_; + bool stop_; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/binaryurp/source/writerstate.hxx b/binaryurp/source/writerstate.hxx new file mode 100644 index 000000000..72477ad17 --- /dev/null +++ b/binaryurp/source/writerstate.hxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <rtl/byteseq.hxx> +#include <rtl/ustring.hxx> +#include <typelib/typedescription.hxx> + +#include "cache.hxx" + +namespace binaryurp { + +struct WriterState { +private: + WriterState(const WriterState&) = delete; + WriterState& operator=(const WriterState&) = delete; +public: + WriterState(): + typeCache(cache::size), oidCache(cache::size), tidCache(cache::size) {} + + Cache< com::sun::star::uno::TypeDescription > typeCache; + + Cache< OUString > oidCache; + + Cache< rtl::ByteSequence > tidCache; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |