diff options
Diffstat (limited to 'binaryurp/source/writer.cxx')
-rw-r--r-- | binaryurp/source/writer.cxx | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/binaryurp/source/writer.cxx b/binaryurp/source/writer.cxx new file mode 100644 index 0000000000..539d8a2c53 --- /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: */ |