diff options
Diffstat (limited to '')
-rw-r--r-- | basic/source/classes/sbunoobj.cxx | 4926 |
1 files changed, 4926 insertions, 0 deletions
diff --git a/basic/source/classes/sbunoobj.cxx b/basic/source/classes/sbunoobj.cxx new file mode 100644 index 000000000..604ac8d86 --- /dev/null +++ b/basic/source/classes/sbunoobj.cxx @@ -0,0 +1,4926 @@ +/* -*- 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 <o3tl/any.hxx> +#include <o3tl/safeint.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <vcl/errcode.hxx> +#include <svl/hint.hxx> + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <comphelper/interfacecontainer4.hxx> +#include <comphelper/extract.hxx> +#include <comphelper/processfactory.hxx> +#include <cppuhelper/weakref.hxx> + +#include <rtl/math.hxx> +#include <rtl/ustrbuf.hxx> + +#include <com/sun/star/script/ArrayWrapper.hpp> +#include <com/sun/star/script/CannotConvertException.hpp> +#include <com/sun/star/script/NativeObjectWrapper.hpp> + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/DeploymentException.hpp> +#include <com/sun/star/lang/XTypeProvider.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyConcept.hpp> +#include <com/sun/star/beans/MethodConcept.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/theIntrospection.hpp> +#include <com/sun/star/script/BasicErrorException.hpp> +#include <com/sun/star/script/InvocationAdapterFactory.hpp> +#include <com/sun/star/script/XAllListener.hpp> +#include <com/sun/star/script/Converter.hpp> +#include <com/sun/star/script/XDefaultProperty.hpp> +#include <com/sun/star/script/XDirectInvocation.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/reflection/XIdlArray.hpp> +#include <com/sun/star/reflection/XIdlReflection.hpp> +#include <com/sun/star/reflection/XServiceConstructorDescription.hpp> +#include <com/sun/star/reflection/XSingletonTypeDescription.hpp> +#include <com/sun/star/reflection/theCoreReflection.hpp> +#include <com/sun/star/bridge/oleautomation/NamedArgument.hpp> +#include <com/sun/star/bridge/oleautomation/Date.hpp> +#include <com/sun/star/bridge/oleautomation/Decimal.hpp> +#include <com/sun/star/bridge/oleautomation/Currency.hpp> +#include <com/sun/star/bridge/oleautomation/XAutomationObject.hpp> +#include <com/sun/star/script/XAutomationInvocation.hpp> + +#include <rtlproto.hxx> + +#include <basic/sbstar.hxx> +#include <basic/sbuno.hxx> +#include <basic/sberrors.hxx> +#include <sbunoobj.hxx> +#include <sbintern.hxx> +#include <runtime.hxx> + +#include <algorithm> +#include <math.h> +#include <memory> +#include <string_view> +#include <unordered_map> +#include <com/sun/star/reflection/XTypeDescriptionEnumerationAccess.hpp> +#include <com/sun/star/reflection/XConstantsTypeDescription.hpp> + +using com::sun::star::uno::Reference; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::reflection; +using namespace com::sun::star::beans; +using namespace com::sun::star::script; +using namespace com::sun::star::container; +using namespace com::sun::star::bridge; +using namespace cppu; + + +// Identifiers for creating the strings for dbg_Properties +constexpr OUStringLiteral ID_DBG_SUPPORTEDINTERFACES = u"Dbg_SupportedInterfaces"; +constexpr OUStringLiteral ID_DBG_PROPERTIES = u"Dbg_Properties"; +constexpr OUStringLiteral ID_DBG_METHODS = u"Dbg_Methods"; + +char const aSeqLevelStr[] = "[]"; + +// Gets the default property for a uno object. Note: There is some +// redirection built in. The property name specifies the name +// of the default property. + +bool SbUnoObject::getDefaultPropName( SbUnoObject const * pUnoObj, OUString& sDfltProp ) +{ + bool bResult = false; + Reference< XDefaultProperty> xDefaultProp( pUnoObj->maTmpUnoObj, UNO_QUERY ); + if ( xDefaultProp.is() ) + { + sDfltProp = xDefaultProp->getDefaultPropertyName(); + if ( !sDfltProp.isEmpty() ) + bResult = true; + } + return bResult; +} + +SbxVariable* getDefaultProp( SbxVariable* pRef ) +{ + SbxVariable* pDefaultProp = nullptr; + if ( pRef->GetType() == SbxOBJECT ) + { + SbxObject* pObj = dynamic_cast<SbxObject*>(pRef); + if (!pObj) + { + SbxBase* pObjVarObj = pRef->GetObject(); + pObj = dynamic_cast<SbxObject*>( pObjVarObj ); + } + if (SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>(pObj)) + { + pDefaultProp = pUnoObj->GetDfltProperty(); + } + } + return pDefaultProp; +} + +void SetSbUnoObjectDfltPropName( SbxObject* pObj ) +{ + SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pObj ); + if ( pUnoObj ) + { + OUString sDfltPropName; + + if ( SbUnoObject::getDefaultPropName( pUnoObj, sDfltPropName ) ) + { + pUnoObj->SetDfltProperty( sDfltPropName ); + } + } +} + +// save CoreReflection statically +static Reference< XIdlReflection > getCoreReflection_Impl() +{ + return css::reflection::theCoreReflection::get( + comphelper::getProcessComponentContext()); +} + +// save CoreReflection statically +static Reference< XHierarchicalNameAccess > const & getCoreReflection_HierarchicalNameAccess_Impl() +{ + static Reference< XHierarchicalNameAccess > xCoreReflection_HierarchicalNameAccess; + + if( !xCoreReflection_HierarchicalNameAccess.is() ) + { + Reference< XIdlReflection > xCoreReflection = getCoreReflection_Impl(); + if( xCoreReflection.is() ) + { + xCoreReflection_HierarchicalNameAccess = + Reference< XHierarchicalNameAccess >( xCoreReflection, UNO_QUERY ); + } + } + return xCoreReflection_HierarchicalNameAccess; +} + +// Hold TypeProvider statically +static Reference< XHierarchicalNameAccess > const & getTypeProvider_Impl() +{ + static Reference< XHierarchicalNameAccess > xAccess; + + // Do we have already CoreReflection; if not obtain it + if( !xAccess.is() ) + { + Reference< XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + if( xContext.is() ) + { + xContext->getValueByName( + "/singletons/com.sun.star.reflection.theTypeDescriptionManager" ) + >>= xAccess; + OSL_ENSURE( xAccess.is(), "### TypeDescriptionManager singleton not accessible!?" ); + } + if( !xAccess.is() ) + { + throw DeploymentException( + "/singletons/com.sun.star.reflection.theTypeDescriptionManager singleton not accessible" ); + } + } + return xAccess; +} + +// Hold TypeConverter statically +static Reference< XTypeConverter > const & getTypeConverter_Impl() +{ + static Reference< XTypeConverter > xTypeConverter; + + // Do we have already CoreReflection; if not obtain it + if( !xTypeConverter.is() ) + { + Reference< XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + if( xContext.is() ) + { + xTypeConverter = Converter::create(xContext); + } + if( !xTypeConverter.is() ) + { + throw DeploymentException( + "com.sun.star.script.Converter service not accessible" ); + } + } + return xTypeConverter; +} + + +// #111851 factory function to create an OLE object +SbUnoObject* createOLEObject_Impl( const OUString& aType ) +{ + static const Reference<XMultiServiceFactory> xOLEFactory = [] { + Reference<XMultiServiceFactory> xFactory; + Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() ); + if( xContext.is() ) + { + Reference<XMultiComponentFactory> xSMgr = xContext->getServiceManager(); + xFactory.set( + xSMgr->createInstanceWithContext( "com.sun.star.bridge.OleObjectFactory", xContext ), + UNO_QUERY ); + } + return xFactory; + }(); + + SbUnoObject* pUnoObj = nullptr; + if( xOLEFactory.is() ) + { + // some type names available in VBA can not be directly used in COM + OUString aOLEType = aType; + if ( aOLEType == "SAXXMLReader30" ) + { + aOLEType = "Msxml2.SAXXMLReader.3.0"; + } + Reference< XInterface > xOLEObject = xOLEFactory->createInstance( aOLEType ); + if( xOLEObject.is() ) + { + pUnoObj = new SbUnoObject( aType, Any(xOLEObject) ); + OUString sDfltPropName; + + if ( SbUnoObject::getDefaultPropName( pUnoObj, sDfltPropName ) ) + pUnoObj->SetDfltProperty( sDfltPropName ); + } + } + return pUnoObj; +} + + +namespace +{ + void lcl_indent( OUStringBuffer& _inout_rBuffer, sal_Int32 _nLevel ) + { + while ( _nLevel-- > 0 ) + { + _inout_rBuffer.append( " " ); + } + } +} + +static void implAppendExceptionMsg( OUStringBuffer& _inout_rBuffer, const Exception& _e, std::u16string_view _rExceptionType, sal_Int32 _nLevel ) +{ + _inout_rBuffer.append( "\n" ); + lcl_indent( _inout_rBuffer, _nLevel ); + _inout_rBuffer.append( "Type: " ); + + if ( _rExceptionType.empty() ) + _inout_rBuffer.append( "Unknown" ); + else + _inout_rBuffer.append( _rExceptionType ); + + _inout_rBuffer.append( "\n" ); + lcl_indent( _inout_rBuffer, _nLevel ); + _inout_rBuffer.append( "Message: " ); + _inout_rBuffer.append( _e.Message ); + +} + +// construct an error message for the exception +static OUString implGetExceptionMsg( const Exception& e, std::u16string_view aExceptionType_ ) +{ + OUStringBuffer aMessageBuf; + implAppendExceptionMsg( aMessageBuf, e, aExceptionType_, 0 ); + return aMessageBuf.makeStringAndClear(); +} + +static OUString implGetExceptionMsg( const Any& _rCaughtException ) +{ + auto e = o3tl::tryAccess<Exception>(_rCaughtException); + OSL_PRECOND( e, "implGetExceptionMsg: illegal argument!" ); + if ( !e ) + { + return OUString(); + } + return implGetExceptionMsg( *e, _rCaughtException.getValueTypeName() ); +} + +static Any convertAny( const Any& rVal, const Type& aDestType ) +{ + Any aConvertedVal; + const Reference< XTypeConverter >& xConverter = getTypeConverter_Impl(); + try + { + aConvertedVal = xConverter->convertTo( rVal, aDestType ); + } + catch( const IllegalArgumentException& ) + { + StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, + implGetExceptionMsg( ::cppu::getCaughtException() ) ); + return aConvertedVal; + } + catch( const CannotConvertException& e2 ) + { + StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, + implGetExceptionMsg( e2, u"com.sun.star.lang.IllegalArgumentException" ) ); + return aConvertedVal; + } + return aConvertedVal; +} + + +// #105565 Special Object to wrap a strongly typed Uno Any + + +// TODO: source out later +static Reference<XIdlClass> TypeToIdlClass( const Type& rType ) +{ + return getCoreReflection_Impl()->forName(rType.getTypeName()); +} + +// Exception type unknown +template< class EXCEPTION > +static OUString implGetExceptionMsg( const EXCEPTION& e ) +{ + return implGetExceptionMsg( e, cppu::UnoType<decltype(e)>::get().getTypeName() ); +} + +static void implHandleBasicErrorException( BasicErrorException const & e ) +{ + ErrCode nError = StarBASIC::GetSfxFromVBError( static_cast<sal_uInt16>(e.ErrorCode) ); + StarBASIC::Error( nError, e.ErrorMessageArgument ); +} + +static void implHandleWrappedTargetException( const Any& _rWrappedTargetException ) +{ + Any aExamine( _rWrappedTargetException ); + + // completely strip the first InvocationTargetException, its error message isn't of any + // interest to the user, it just says something like "invoking the UNO method went wrong.". + InvocationTargetException aInvocationError; + if ( aExamine >>= aInvocationError ) + aExamine = aInvocationError.TargetException; + + BasicErrorException aBasicError; + + ErrCode nError( ERRCODE_BASIC_EXCEPTION ); + OUStringBuffer aMessageBuf; + + // strip any other WrappedTargetException instances, but this time preserve the error messages. + WrappedTargetException aWrapped; + sal_Int32 nLevel = 0; + while ( aExamine >>= aWrapped ) + { + // special handling for BasicErrorException errors + if ( aWrapped.TargetException >>= aBasicError ) + { + nError = StarBASIC::GetSfxFromVBError( static_cast<sal_uInt16>(aBasicError.ErrorCode) ); + aMessageBuf.append( aBasicError.ErrorMessageArgument ); + aExamine.clear(); + break; + } + + // append this round's message + implAppendExceptionMsg( aMessageBuf, aWrapped, aExamine.getValueTypeName(), nLevel ); + if ( aWrapped.TargetException.getValueTypeClass() == TypeClass_EXCEPTION ) + // there is a next chain element + aMessageBuf.append( "\nTargetException:" ); + + // next round + aExamine = aWrapped.TargetException; + ++nLevel; + } + + if ( auto e = o3tl::tryAccess<Exception>(aExamine) ) + { + // the last element in the chain is still an exception, but no WrappedTargetException + implAppendExceptionMsg( aMessageBuf, *e, aExamine.getValueTypeName(), nLevel ); + } + + StarBASIC::Error( nError, aMessageBuf.makeStringAndClear() ); +} + +static void implHandleAnyException( const Any& _rCaughtException ) +{ + BasicErrorException aBasicError; + WrappedTargetException aWrappedError; + + if ( _rCaughtException >>= aBasicError ) + { + implHandleBasicErrorException( aBasicError ); + } + else if ( _rCaughtException >>= aWrappedError ) + { + implHandleWrappedTargetException( _rCaughtException ); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, implGetExceptionMsg( _rCaughtException ) ); + } +} + +namespace { + +// NativeObjectWrapper handling +struct ObjectItem +{ + SbxObjectRef m_xNativeObj; + + explicit ObjectItem( SbxObject* pNativeObj ) + : m_xNativeObj( pNativeObj ) + {} +}; + +} + +typedef std::vector< ObjectItem > NativeObjectWrapperVector; + +namespace { + +NativeObjectWrapperVector gaNativeObjectWrapperVector; + +} + +void clearNativeObjectWrapperVector() +{ + gaNativeObjectWrapperVector.clear(); +} + +static sal_uInt32 lcl_registerNativeObjectWrapper( SbxObject* pNativeObj ) +{ + sal_uInt32 nIndex = gaNativeObjectWrapperVector.size(); + gaNativeObjectWrapperVector.emplace_back( pNativeObj ); + return nIndex; +} + +static SbxObject* lcl_getNativeObject( sal_uInt32 nIndex ) +{ + SbxObjectRef xRetObj; + if( nIndex < gaNativeObjectWrapperVector.size() ) + { + ObjectItem& rItem = gaNativeObjectWrapperVector[ nIndex ]; + xRetObj = rItem.m_xNativeObj; + } + return xRetObj.get(); +} + +// convert from Uno to Sbx +static SbxDataType unoToSbxType( TypeClass eType ) +{ + SbxDataType eRetType = SbxVOID; + + switch( eType ) + { + case TypeClass_INTERFACE: + case TypeClass_TYPE: + case TypeClass_STRUCT: + case TypeClass_EXCEPTION: eRetType = SbxOBJECT; break; + + case TypeClass_ENUM: eRetType = SbxLONG; break; + case TypeClass_SEQUENCE: + eRetType = SbxDataType( SbxOBJECT | SbxARRAY ); + break; + + + case TypeClass_ANY: eRetType = SbxVARIANT; break; + case TypeClass_BOOLEAN: eRetType = SbxBOOL; break; + case TypeClass_CHAR: eRetType = SbxCHAR; break; + case TypeClass_STRING: eRetType = SbxSTRING; break; + case TypeClass_FLOAT: eRetType = SbxSINGLE; break; + case TypeClass_DOUBLE: eRetType = SbxDOUBLE; break; + case TypeClass_BYTE: eRetType = SbxINTEGER; break; + case TypeClass_SHORT: eRetType = SbxINTEGER; break; + case TypeClass_LONG: eRetType = SbxLONG; break; + case TypeClass_HYPER: eRetType = SbxSALINT64; break; + case TypeClass_UNSIGNED_SHORT: eRetType = SbxUSHORT; break; + case TypeClass_UNSIGNED_LONG: eRetType = SbxULONG; break; + case TypeClass_UNSIGNED_HYPER: eRetType = SbxSALUINT64;break; + default: break; + } + return eRetType; +} + +static SbxDataType unoToSbxType( const Reference< XIdlClass >& xIdlClass ) +{ + SbxDataType eRetType = SbxVOID; + if( xIdlClass.is() ) + { + TypeClass eType = xIdlClass->getTypeClass(); + eRetType = unoToSbxType( eType ); + } + return eRetType; +} + +static void implSequenceToMultiDimArray( SbxDimArray*& pArray, Sequence< sal_Int32 >& indices, Sequence< sal_Int32 >& sizes, const Any& aValue, sal_Int32 dimension, bool bIsZeroIndex, Type const * pType ) +{ + const Type& aType = aValue.getValueType(); + TypeClass eTypeClass = aType.getTypeClass(); + + sal_Int32 dimCopy = dimension; + + if ( eTypeClass == TypeClass_SEQUENCE ) + { + Reference< XIdlClass > xIdlTargetClass = TypeToIdlClass( aType ); + Reference< XIdlArray > xIdlArray = xIdlTargetClass->getArray(); + typelib_TypeDescription * pTD = nullptr; + aType.getDescription( &pTD ); + Type aElementType( reinterpret_cast<typelib_IndirectTypeDescription *>(pTD)->pType ); + ::typelib_typedescription_release( pTD ); + + sal_Int32 nLen = xIdlArray->getLen( aValue ); + for ( sal_Int32 index = 0; index < nLen; ++index ) + { + auto pindices = indices.getArray(); + Any aElementAny = xIdlArray->get( aValue, static_cast<sal_uInt32>(index) ); + // This detects the dimension were currently processing + if ( dimCopy == dimension ) + { + ++dimCopy; + if ( sizes.getLength() < dimCopy ) + { + sizes.realloc( sizes.getLength() + 1 ); + sizes.getArray()[ sizes.getLength() - 1 ] = nLen; + indices.realloc( indices.getLength() + 1 ); + pindices = indices.getArray(); + } + } + + if ( bIsZeroIndex ) + pindices[ dimCopy - 1 ] = index; + else + pindices[ dimCopy - 1] = index + 1; + + implSequenceToMultiDimArray( pArray, indices, sizes, aElementAny, dimCopy, bIsZeroIndex, &aElementType ); + } + + } + else + { + if ( !indices.hasElements() ) + { + // Should never ever get here ( indices.getLength() + // should equal number of dimensions in the array ) + // And that should at least be 1 ! + // #QUESTION is there a better error? + StarBASIC::Error( ERRCODE_BASIC_INVALID_OBJECT ); + return; + } + + SbxDataType eSbxElementType = unoToSbxType( pType ? pType->getTypeClass() : aValue.getValueTypeClass() ); + if ( !pArray ) + { + pArray = new SbxDimArray( eSbxElementType ); + sal_Int32 nIndexLen = indices.getLength(); + + // Dimension the array + for ( sal_Int32 index = 0; index < nIndexLen; ++index ) + { + if ( bIsZeroIndex ) + pArray->unoAddDim(0, sizes[index] - 1); + else + pArray->unoAddDim(1, sizes[index]); + + } + } + + if ( pArray ) + { + auto xVar = tools::make_ref<SbxVariable>( eSbxElementType ); + unoToSbxValue( xVar.get(), aValue ); + + sal_Int32* pIndices = indices.getArray(); + pArray->Put(xVar.get(), pIndices); + + } + } +} + +void unoToSbxValue( SbxVariable* pVar, const Any& aValue ) +{ + const Type& aType = aValue.getValueType(); + TypeClass eTypeClass = aType.getTypeClass(); + switch( eTypeClass ) + { + case TypeClass_TYPE: + { + // Map Type to IdlClass + Type aType_; + aValue >>= aType_; + Reference<XIdlClass> xClass = TypeToIdlClass( aType_ ); + Any aClassAny; + aClassAny <<= xClass; + + // instantiate SbUnoObject + SbUnoObject* pSbUnoObject = new SbUnoObject( OUString(), aClassAny ); + SbxObjectRef xWrapper = static_cast<SbxObject*>(pSbUnoObject); + + // If the object is invalid deliver null + if( !pSbUnoObject->getUnoAny().hasValue() ) + { + pVar->PutObject( nullptr ); + } + else + { + pVar->PutObject( xWrapper.get() ); + } + } + break; + // Interfaces and Structs must be wrapped in a SbUnoObject + case TypeClass_INTERFACE: + case TypeClass_STRUCT: + case TypeClass_EXCEPTION: + { + if( eTypeClass == TypeClass_STRUCT ) + { + ArrayWrapper aWrap; + NativeObjectWrapper aNativeObjectWrapper; + if ( aValue >>= aWrap ) + { + SbxDimArray* pArray = nullptr; + Sequence< sal_Int32 > indices; + Sequence< sal_Int32 > sizes; + implSequenceToMultiDimArray( pArray, indices, sizes, aWrap.Array, /*dimension*/0, aWrap.IsZeroIndex, nullptr ); + if ( pArray ) + { + SbxDimArrayRef xArray = pArray; + SbxFlagBits nFlags = pVar->GetFlags(); + pVar->ResetFlag( SbxFlagBits::Fixed ); + pVar->PutObject( xArray.get() ); + pVar->SetFlags( nFlags ); + } + else + pVar->PutEmpty(); + break; + } + else if ( aValue >>= aNativeObjectWrapper ) + { + sal_uInt32 nIndex = 0; + if( aNativeObjectWrapper.ObjectId >>= nIndex ) + { + SbxObject* pObj = lcl_getNativeObject( nIndex ); + pVar->PutObject( pObj ); + } + else + pVar->PutEmpty(); + break; + } + else + { + SbiInstance* pInst = GetSbData()->pInst; + if( pInst && pInst->IsCompatibility() ) + { + oleautomation::Date aDate; + if( aValue >>= aDate ) + { + pVar->PutDate( aDate.Value ); + break; + } + else + { + oleautomation::Decimal aDecimal; + if( aValue >>= aDecimal ) + { + pVar->PutDecimal( aDecimal ); + break; + } + else + { + oleautomation::Currency aCurrency; + if( aValue >>= aCurrency ) + { + pVar->PutCurrency( aCurrency.Value ); + break; + } + } + } + } + } + } + // instantiate a SbUnoObject + SbUnoObject* pSbUnoObject = new SbUnoObject( OUString(), aValue ); + //If this is called externally e.g. from the scripting + //framework then there is no 'active' runtime the default property will not be set up + //only a vba object will have XDefaultProp set anyway so... this + //test seems a bit of overkill + //if ( SbiRuntime::isVBAEnabled() ) + { + OUString sDfltPropName; + + if ( SbUnoObject::getDefaultPropName( pSbUnoObject, sDfltPropName ) ) + { + pSbUnoObject->SetDfltProperty( sDfltPropName ); + } + } + SbxObjectRef xWrapper = static_cast<SbxObject*>(pSbUnoObject); + + // If the object is invalid deliver null + if( !pSbUnoObject->getUnoAny().hasValue() ) + { + pVar->PutObject( nullptr ); + } + else + { + pVar->PutObject( xWrapper.get() ); + } + } + break; + + + case TypeClass_ENUM: + { + sal_Int32 nEnum = 0; + enum2int( nEnum, aValue ); + pVar->PutLong( nEnum ); + } + break; + + case TypeClass_SEQUENCE: + { + Reference< XIdlClass > xIdlTargetClass = TypeToIdlClass( aType ); + Reference< XIdlArray > xIdlArray = xIdlTargetClass->getArray(); + sal_Int32 i, nLen = xIdlArray->getLen( aValue ); + + typelib_TypeDescription * pTD = nullptr; + aType.getDescription( &pTD ); + assert( pTD && pTD->eTypeClass == typelib_TypeClass_SEQUENCE ); + Type aElementType( reinterpret_cast<typelib_IndirectTypeDescription *>(pTD)->pType ); + ::typelib_typedescription_release( pTD ); + + // build an Array in Basic + SbxDimArrayRef xArray; + SbxDataType eSbxElementType = unoToSbxType( aElementType.getTypeClass() ); + xArray = new SbxDimArray( eSbxElementType ); + if( nLen > 0 ) + { + xArray->unoAddDim(0, nLen - 1); + + // register the elements as variables + for( i = 0 ; i < nLen ; i++ ) + { + // convert elements + Any aElementAny = xIdlArray->get( aValue, static_cast<sal_uInt32>(i) ); + auto xVar = tools::make_ref<SbxVariable>( eSbxElementType ); + unoToSbxValue( xVar.get(), aElementAny ); + + // put into the Array + xArray->Put(xVar.get(), &i); + } + } + else + { + xArray->unoAddDim(0, -1); + } + + // return the Array + SbxFlagBits nFlags = pVar->GetFlags(); + pVar->ResetFlag( SbxFlagBits::Fixed ); + pVar->PutObject( xArray.get() ); + pVar->SetFlags( nFlags ); + + } + break; + + + case TypeClass_BOOLEAN: pVar->PutBool( *o3tl::forceAccess<bool>(aValue) ); break; + case TypeClass_CHAR: + { + pVar->PutChar( *o3tl::forceAccess<sal_Unicode>(aValue) ); + break; + } + case TypeClass_STRING: { OUString val; aValue >>= val; pVar->PutString( val ); } break; + case TypeClass_FLOAT: { float val = 0; aValue >>= val; pVar->PutSingle( val ); } break; + case TypeClass_DOUBLE: { double val = 0; aValue >>= val; pVar->PutDouble( val ); } break; + case TypeClass_BYTE: { sal_Int8 val = 0; aValue >>= val; pVar->PutInteger( val ); } break; + case TypeClass_SHORT: { sal_Int16 val = 0; aValue >>= val; pVar->PutInteger( val ); } break; + case TypeClass_LONG: { sal_Int32 val = 0; aValue >>= val; pVar->PutLong( val ); } break; + case TypeClass_HYPER: { sal_Int64 val = 0; aValue >>= val; pVar->PutInt64( val ); } break; + case TypeClass_UNSIGNED_SHORT: { sal_uInt16 val = 0; aValue >>= val; pVar->PutUShort( val ); } break; + case TypeClass_UNSIGNED_LONG: { sal_uInt32 val = 0; aValue >>= val; pVar->PutULong( val ); } break; + case TypeClass_UNSIGNED_HYPER: { sal_uInt64 val = 0; aValue >>= val; pVar->PutUInt64( val ); } break; + default: pVar->PutEmpty(); break; + } +} + +// Deliver the reflection for Sbx types +static Type getUnoTypeForSbxBaseType( SbxDataType eType ) +{ + Type aRetType = cppu::UnoType<void>::get(); + switch( eType ) + { + case SbxNULL: aRetType = cppu::UnoType<XInterface>::get(); break; + case SbxINTEGER: aRetType = cppu::UnoType<sal_Int16>::get(); break; + case SbxLONG: aRetType = cppu::UnoType<sal_Int32>::get(); break; + case SbxSINGLE: aRetType = cppu::UnoType<float>::get(); break; + case SbxDOUBLE: aRetType = cppu::UnoType<double>::get(); break; + case SbxCURRENCY: aRetType = cppu::UnoType<oleautomation::Currency>::get(); break; + case SbxDECIMAL: aRetType = cppu::UnoType<oleautomation::Decimal>::get(); break; + case SbxDATE: { + SbiInstance* pInst = GetSbData()->pInst; + if( pInst && pInst->IsCompatibility() ) + aRetType = cppu::UnoType<double>::get(); + else + aRetType = cppu::UnoType<oleautomation::Date>::get(); + } + break; + case SbxSTRING: aRetType = cppu::UnoType<OUString>::get(); break; + case SbxBOOL: aRetType = cppu::UnoType<sal_Bool>::get(); break; + case SbxVARIANT: aRetType = cppu::UnoType<Any>::get(); break; + case SbxCHAR: aRetType = cppu::UnoType<cppu::UnoCharType>::get(); break; + case SbxBYTE: aRetType = cppu::UnoType<sal_Int8>::get(); break; + case SbxUSHORT: aRetType = cppu::UnoType<cppu::UnoUnsignedShortType>::get(); break; + case SbxULONG: aRetType = ::cppu::UnoType<sal_uInt32>::get(); break; + // map machine-dependent ones to long for consistency + case SbxINT: aRetType = ::cppu::UnoType<sal_Int32>::get(); break; + case SbxUINT: aRetType = ::cppu::UnoType<sal_uInt32>::get(); break; + default: break; + } + return aRetType; +} + +// Converting of Sbx to Uno without a know target class for TypeClass_ANY +static Type getUnoTypeForSbxValue( const SbxValue* pVal ) +{ + Type aRetType = cppu::UnoType<void>::get(); + if( !pVal ) + return aRetType; + + // convert SbxType to Uno + SbxDataType eBaseType = pVal->SbxValue::GetType(); + if( eBaseType == SbxOBJECT ) + { + SbxBaseRef xObj = pVal->GetObject(); + if( !xObj.is() ) + { + aRetType = cppu::UnoType<XInterface>::get(); + return aRetType; + } + + if( auto pArray = dynamic_cast<SbxDimArray*>( xObj.get() ) ) + { + sal_Int32 nDims = pArray->GetDims(); + Type aElementType = getUnoTypeForSbxBaseType( static_cast<SbxDataType>(pArray->GetType() & 0xfff) ); + TypeClass eElementTypeClass = aElementType.getTypeClass(); + + // Normal case: One dimensional array + sal_Int32 nLower, nUpper; + if (nDims == 1 && pArray->GetDim(1, nLower, nUpper)) + { + if( eElementTypeClass == TypeClass_VOID || eElementTypeClass == TypeClass_ANY ) + { + // If all elements of the arrays are from the same type, take + // this one - otherwise the whole will be considered as Any-Sequence + bool bNeedsInit = true; + + for (sal_Int32 aIdx[1] = { nLower }; aIdx[0] <= nUpper; ++aIdx[0]) + { + SbxVariableRef xVar = pArray->Get(aIdx); + Type aType = getUnoTypeForSbxValue( xVar.get() ); + if( bNeedsInit ) + { + if( aType.getTypeClass() == TypeClass_VOID ) + { + // if only first element is void: different types -> []any + // if all elements are void: []void is not allowed -> []any + aElementType = cppu::UnoType<Any>::get(); + break; + } + aElementType = aType; + bNeedsInit = false; + } + else if( aElementType != aType ) + { + // different types -> AnySequence + aElementType = cppu::UnoType<Any>::get(); + break; + } + } + } + + OUString aSeqTypeName = aSeqLevelStr + aElementType.getTypeName(); + aRetType = Type( TypeClass_SEQUENCE, aSeqTypeName ); + } + // #i33795 Map also multi dimensional arrays to corresponding sequences + else if( nDims > 1 ) + { + if( eElementTypeClass == TypeClass_VOID || eElementTypeClass == TypeClass_ANY ) + { + // For this check the array's dim structure does not matter + sal_uInt32 nFlatArraySize = pArray->Count(); + + bool bNeedsInit = true; + for( sal_uInt32 i = 0 ; i < nFlatArraySize ; i++ ) + { + SbxVariableRef xVar = pArray->SbxArray::Get(i); + Type aType = getUnoTypeForSbxValue( xVar.get() ); + if( bNeedsInit ) + { + if( aType.getTypeClass() == TypeClass_VOID ) + { + // if only first element is void: different types -> []any + // if all elements are void: []void is not allowed -> []any + aElementType = cppu::UnoType<Any>::get(); + break; + } + aElementType = aType; + bNeedsInit = false; + } + else if( aElementType != aType ) + { + // different types -> AnySequence + aElementType = cppu::UnoType<Any>::get(); + break; + } + } + } + + OUStringBuffer aSeqTypeName; + for(sal_Int32 iDim = 0 ; iDim < nDims ; iDim++ ) + { + aSeqTypeName.append(aSeqLevelStr); + } + aSeqTypeName.append(aElementType.getTypeName()); + aRetType = Type( TypeClass_SEQUENCE, aSeqTypeName.makeStringAndClear() ); + } + } + // No array, but ... + else if( auto obj = dynamic_cast<SbUnoObject*>( xObj.get() ) ) + { + aRetType = obj->getUnoAny().getValueType(); + } + // SbUnoAnyObject? + else if( auto any = dynamic_cast<SbUnoAnyObject*>( xObj.get() ) ) + { + aRetType = any->getValue().getValueType(); + } + // Otherwise it is a No-Uno-Basic-Object -> default==deliver void + } + // No object, convert basic type + else + { + aRetType = getUnoTypeForSbxBaseType( eBaseType ); + } + return aRetType; +} + +// converting of Sbx to Uno without known target class for TypeClass_ANY +static Any sbxToUnoValueImpl( const SbxValue* pVar, bool bBlockConversionToSmallestType = false ) +{ + SbxDataType eBaseType = pVar->SbxValue::GetType(); + if( eBaseType == SbxOBJECT ) + { + SbxBaseRef xObj = pVar->GetObject(); + if( xObj.is() ) + { + if( auto obj = dynamic_cast<SbUnoAnyObject*>( xObj.get() ) ) + return obj->getValue(); + if( auto pClassModuleObj = dynamic_cast<SbClassModuleObject*>( xObj.get() ) ) + { + Any aRetAny; + SbModule* pClassModule = pClassModuleObj->getClassModule(); + if( pClassModule->createCOMWrapperForIface( aRetAny, pClassModuleObj ) ) + return aRetAny; + } + if( dynamic_cast<const SbUnoObject*>( xObj.get() ) == nullptr ) + { + // Create NativeObjectWrapper to identify object in case of callbacks + SbxObject* pObj = dynamic_cast<SbxObject*>( pVar->GetObject() ); + if( pObj != nullptr ) + { + NativeObjectWrapper aNativeObjectWrapper; + sal_uInt32 nIndex = lcl_registerNativeObjectWrapper( pObj ); + aNativeObjectWrapper.ObjectId <<= nIndex; + Any aRetAny; + aRetAny <<= aNativeObjectWrapper; + return aRetAny; + } + } + } + } + + Type aType = getUnoTypeForSbxValue( pVar ); + TypeClass eType = aType.getTypeClass(); + + if( !bBlockConversionToSmallestType ) + { + // #79615 Choose "smallest" representation for int values + // because up cast is allowed, downcast not + switch( eType ) + { + case TypeClass_FLOAT: + case TypeClass_DOUBLE: + { + double d = pVar->GetDouble(); + if( rtl::math::approxEqual(d, floor( d )) ) + { + if( d >= -128 && d <= 127 ) + aType = ::cppu::UnoType<sal_Int8>::get(); + else if( d >= SbxMININT && d <= SbxMAXINT ) + aType = ::cppu::UnoType<sal_Int16>::get(); + else if( d >= -SbxMAXLNG && d <= SbxMAXLNG ) + aType = ::cppu::UnoType<sal_Int32>::get(); + } + break; + } + case TypeClass_SHORT: + { + sal_Int16 n = pVar->GetInteger(); + if( n >= -128 && n <= 127 ) + aType = ::cppu::UnoType<sal_Int8>::get(); + break; + } + case TypeClass_LONG: + { + sal_Int32 n = pVar->GetLong(); + if( n >= -128 && n <= 127 ) + aType = ::cppu::UnoType<sal_Int8>::get(); + else if( n >= SbxMININT && n <= SbxMAXINT ) + aType = ::cppu::UnoType<sal_Int16>::get(); + break; + } + case TypeClass_UNSIGNED_SHORT: + { + sal_uInt16 n = pVar->GetUShort(); + if( n <= 255 ) + aType = cppu::UnoType<sal_uInt8>::get(); + break; + } + case TypeClass_UNSIGNED_LONG: + { + sal_uInt32 n = pVar->GetLong(); + if( n <= 255 ) + aType = cppu::UnoType<sal_uInt8>::get(); + else if( n <= SbxMAXUINT ) + aType = cppu::UnoType<cppu::UnoUnsignedShortType>::get(); + break; + } + // TODO: need to add hyper types ? + default: break; + } + } + + return sbxToUnoValue( pVar, aType ); +} + + +// Helper function for StepREDIMP +static Any implRekMultiDimArrayToSequence( SbxDimArray* pArray, + const Type& aElemType, sal_Int32 nMaxDimIndex, sal_Int32 nActualDim, + sal_Int32* pActualIndices, sal_Int32* pLowerBounds, sal_Int32* pUpperBounds ) +{ + sal_Int32 nSeqLevel = nMaxDimIndex - nActualDim + 1; + OUStringBuffer aSeqTypeName; + sal_Int32 i; + for( i = 0 ; i < nSeqLevel ; i++ ) + { + aSeqTypeName.append(aSeqLevelStr); + } + aSeqTypeName.append(aElemType.getTypeName()); + Type aSeqType( TypeClass_SEQUENCE, aSeqTypeName.makeStringAndClear() ); + + // Create Sequence instance + Any aRetVal; + Reference< XIdlClass > xIdlTargetClass = TypeToIdlClass( aSeqType ); + xIdlTargetClass->createObject( aRetVal ); + + // Alloc sequence according to array bounds + sal_Int32 nUpper = pUpperBounds[nActualDim]; + sal_Int32 nLower = pLowerBounds[nActualDim]; + sal_Int32 nSeqSize = nUpper - nLower + 1; + Reference< XIdlArray > xArray = xIdlTargetClass->getArray(); + xArray->realloc( aRetVal, nSeqSize ); + + sal_Int32& ri = pActualIndices[nActualDim]; + + for( ri = nLower,i = 0 ; ri <= nUpper ; ri++,i++ ) + { + Any aElementVal; + + if( nActualDim < nMaxDimIndex ) + { + aElementVal = implRekMultiDimArrayToSequence( pArray, aElemType, + nMaxDimIndex, nActualDim + 1, pActualIndices, pLowerBounds, pUpperBounds ); + } + else + { + SbxVariable* pSource = pArray->Get(pActualIndices); + aElementVal = sbxToUnoValue( pSource, aElemType ); + } + + try + { + // transfer to the sequence + xArray->set( aRetVal, i, aElementVal ); + } + catch( const IllegalArgumentException& ) + { + StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, + implGetExceptionMsg( ::cppu::getCaughtException() ) ); + } + catch (const IndexOutOfBoundsException&) + { + StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE ); + } + } + return aRetVal; +} + +// Map old interface +Any sbxToUnoValue( const SbxValue* pVar ) +{ + return sbxToUnoValueImpl( pVar ); +} + +// function to find a global identifier in +// the UnoScope and to wrap it for Sbx +static bool implGetTypeByName( const OUString& rName, Type& rRetType ) +{ + bool bSuccess = false; + + const Reference< XHierarchicalNameAccess >& xTypeAccess = getTypeProvider_Impl(); + if( xTypeAccess->hasByHierarchicalName( rName ) ) + { + Any aRet = xTypeAccess->getByHierarchicalName( rName ); + Reference< XTypeDescription > xTypeDesc; + aRet >>= xTypeDesc; + + if( xTypeDesc.is() ) + { + rRetType = Type( xTypeDesc->getTypeClass(), xTypeDesc->getName() ); + bSuccess = true; + } + } + return bSuccess; +} + + +// converting of Sbx to Uno with known target class +Any sbxToUnoValue( const SbxValue* pVar, const Type& rType, Property const * pUnoProperty ) +{ + Any aRetVal; + + // #94560 No conversion of empty/void for MAYBE_VOID properties + if( pUnoProperty && pUnoProperty->Attributes & PropertyAttribute::MAYBEVOID ) + { + if( pVar->IsEmpty() ) + return aRetVal; + } + + SbxDataType eBaseType = pVar->SbxValue::GetType(); + if( eBaseType == SbxOBJECT ) + { + SbxBaseRef xObj = pVar->GetObject(); + if ( auto obj = dynamic_cast<SbUnoAnyObject*>( xObj.get() ) ) + { + return obj->getValue(); + } + } + + TypeClass eType = rType.getTypeClass(); + switch( eType ) + { + case TypeClass_INTERFACE: + case TypeClass_STRUCT: + case TypeClass_EXCEPTION: + { + Reference< XIdlClass > xIdlTargetClass = TypeToIdlClass( rType ); + + // null reference? + if( pVar->IsNull() && eType == TypeClass_INTERFACE ) + { + Reference< XInterface > xRef; + OUString aClassName = xIdlTargetClass->getName(); + Type aClassType( xIdlTargetClass->getTypeClass(), aClassName ); + aRetVal.setValue( &xRef, aClassType ); + } + else + { + // #112368 Special conversion for Decimal, Currency and Date + if( eType == TypeClass_STRUCT ) + { + SbiInstance* pInst = GetSbData()->pInst; + if( pInst && pInst->IsCompatibility() ) + { + if( rType == cppu::UnoType<oleautomation::Decimal>::get()) + { + oleautomation::Decimal aDecimal; + pVar->fillAutomationDecimal( aDecimal ); + aRetVal <<= aDecimal; + break; + } + else if( rType == cppu::UnoType<oleautomation::Currency>::get()) + { + // assumes per previous code that ole Currency is Int64 + aRetVal <<= pVar->GetInt64(); + break; + } + else if( rType == cppu::UnoType<oleautomation::Date>::get()) + { + oleautomation::Date aDate; + aDate.Value = pVar->GetDate(); + aRetVal <<= aDate; + break; + } + } + } + + SbxBaseRef pObj = pVar->GetObject(); + if( auto obj = dynamic_cast<SbUnoObject*>( pObj.get() ) ) + { + aRetVal = obj->getUnoAny(); + } + else if( auto structRef = dynamic_cast<SbUnoStructRefObject*>( pObj.get() ) ) + { + aRetVal = structRef->getUnoAny(); + } + else + { + // null object -> null XInterface + Reference<XInterface> xInt; + aRetVal <<= xInt; + } + } + } + break; + + case TypeClass_TYPE: + { + if( eBaseType == SbxOBJECT ) + { + // XIdlClass? + Reference< XIdlClass > xIdlClass; + + SbxBaseRef pObj = pVar->GetObject(); + if( auto obj = dynamic_cast<SbUnoObject*>( pObj.get() ) ) + { + Any aUnoAny = obj->getUnoAny(); + aUnoAny >>= xIdlClass; + } + + if( xIdlClass.is() ) + { + OUString aClassName = xIdlClass->getName(); + Type aType( xIdlClass->getTypeClass(), aClassName ); + aRetVal <<= aType; + } + } + else if( eBaseType == SbxSTRING ) + { + OUString aTypeName = pVar->GetOUString(); + Type aType; + bool bSuccess = implGetTypeByName( aTypeName, aType ); + if( bSuccess ) + { + aRetVal <<= aType; + } + } + } + break; + + + case TypeClass_ENUM: + { + aRetVal = int2enum( pVar->GetLong(), rType ); + } + break; + + case TypeClass_SEQUENCE: + { + SbxBaseRef xObj = pVar->GetObject(); + if( auto pArray = dynamic_cast<SbxDimArray*>( xObj.get() ) ) + { + sal_Int32 nDims = pArray->GetDims(); + + // Normal case: One dimensional array + sal_Int32 nLower, nUpper; + if (nDims == 1 && pArray->GetDim(1, nLower, nUpper)) + { + sal_Int32 nSeqSize = nUpper - nLower + 1; + + // create the instance of the required sequence + Reference< XIdlClass > xIdlTargetClass = TypeToIdlClass( rType ); + xIdlTargetClass->createObject( aRetVal ); + Reference< XIdlArray > xArray = xIdlTargetClass->getArray(); + xArray->realloc( aRetVal, nSeqSize ); + + // Element-Type + OUString aClassName = xIdlTargetClass->getName(); + typelib_TypeDescription * pSeqTD = nullptr; + typelib_typedescription_getByName( &pSeqTD, aClassName.pData ); + assert( pSeqTD ); + Type aElemType( reinterpret_cast<typelib_IndirectTypeDescription *>(pSeqTD)->pType ); + + // convert all array member and register them + sal_Int32 aIdx[1]; + aIdx[0] = nLower; + for (sal_Int32 i = 0 ; i < nSeqSize; ++i, ++aIdx[0]) + { + SbxVariableRef xVar = pArray->Get(aIdx); + + // Convert the value of Sbx to Uno + Any aAnyValue = sbxToUnoValue( xVar.get(), aElemType ); + + try + { + // insert in the sequence + xArray->set( aRetVal, i, aAnyValue ); + } + catch( const IllegalArgumentException& ) + { + StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, + implGetExceptionMsg( ::cppu::getCaughtException() ) ); + } + catch (const IndexOutOfBoundsException&) + { + StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE ); + } + } + } + // #i33795 Map also multi dimensional arrays to corresponding sequences + else if( nDims > 1 ) + { + // Element-Type + typelib_TypeDescription * pSeqTD = nullptr; + Type aCurType( rType ); + sal_Int32 nSeqLevel = 0; + Type aElemType; + do + { + OUString aTypeName = aCurType.getTypeName(); + typelib_typedescription_getByName( &pSeqTD, aTypeName.pData ); + assert( pSeqTD ); + if( pSeqTD->eTypeClass == typelib_TypeClass_SEQUENCE ) + { + aCurType = Type( reinterpret_cast<typelib_IndirectTypeDescription *>(pSeqTD)->pType ); + nSeqLevel++; + } + else + { + aElemType = aCurType; + break; + } + } + while( true ); + + if( nSeqLevel == nDims ) + { + std::unique_ptr<sal_Int32[]> pLowerBounds(new sal_Int32[nDims]); + std::unique_ptr<sal_Int32[]> pUpperBounds(new sal_Int32[nDims]); + std::unique_ptr<sal_Int32[]> pActualIndices(new sal_Int32[nDims]); + for(sal_Int32 i = 1 ; i <= nDims ; i++ ) + { + sal_Int32 lBound, uBound; + pArray->GetDim(i, lBound, uBound); + + sal_Int32 j = i - 1; + pActualIndices[j] = pLowerBounds[j] = lBound; + pUpperBounds[j] = uBound; + } + + aRetVal = implRekMultiDimArrayToSequence( pArray, aElemType, + nDims - 1, 0, pActualIndices.get(), pLowerBounds.get(), pUpperBounds.get() ); + } + } + } + } + break; + + + // for Any use the class independent converting routine + case TypeClass_ANY: + { + aRetVal = sbxToUnoValueImpl( pVar ); + } + break; + + case TypeClass_BOOLEAN: + { + aRetVal <<= pVar->GetBool(); + break; + } + case TypeClass_CHAR: + { + aRetVal <<= pVar->GetChar(); + break; + } + case TypeClass_STRING: aRetVal <<= pVar->GetOUString(); break; + case TypeClass_FLOAT: aRetVal <<= pVar->GetSingle(); break; + case TypeClass_DOUBLE: aRetVal <<= pVar->GetDouble(); break; + + case TypeClass_BYTE: + { + sal_Int16 nVal = pVar->GetInteger(); + bool bOverflow = false; + if( nVal < -128 ) + { + bOverflow = true; + nVal = -128; + } + else if( nVal > 255 ) // 128..255 map to -128..-1 + { + bOverflow = true; + nVal = 127; + } + if( bOverflow ) + StarBASIC::Error( ERRCODE_BASIC_MATH_OVERFLOW ); + + sal_Int8 nByteVal = static_cast<sal_Int8>(nVal); + aRetVal <<= nByteVal; + break; + } + case TypeClass_SHORT: aRetVal <<= pVar->GetInteger(); break; + case TypeClass_LONG: aRetVal <<= pVar->GetLong(); break; + case TypeClass_HYPER: aRetVal <<= pVar->GetInt64(); break; + case TypeClass_UNSIGNED_SHORT: aRetVal <<= pVar->GetUShort(); break; + case TypeClass_UNSIGNED_LONG: aRetVal <<= pVar->GetULong(); break; + case TypeClass_UNSIGNED_HYPER: aRetVal <<= pVar->GetUInt64(); break; + default: break; + } + + return aRetVal; +} + +static void processAutomationParams( SbxArray* pParams, Sequence< Any >& args, sal_uInt32 nParamCount ) +{ + AutomationNamedArgsSbxArray* pArgNamesArray = dynamic_cast<AutomationNamedArgsSbxArray*>( pParams ); + + args.realloc( nParamCount ); + Any* pAnyArgs = args.getArray(); + bool bBlockConversionToSmallestType = GetSbData()->pInst->IsCompatibility(); + sal_uInt32 i = 0; + if( pArgNamesArray ) + { + Sequence< OUString >& rNameSeq = pArgNamesArray->getNames(); + OUString* pNames = rNameSeq.getArray(); + Any aValAny; + for( i = 0 ; i < nParamCount ; i++ ) + { + sal_uInt32 iSbx = i + 1; + + aValAny = sbxToUnoValueImpl(pParams->Get(iSbx), + bBlockConversionToSmallestType ); + + OUString aParamName = pNames[iSbx]; + if( !aParamName.isEmpty() ) + { + oleautomation::NamedArgument aNamedArgument; + aNamedArgument.Name = aParamName; + aNamedArgument.Value = aValAny; + pAnyArgs[i] <<= aNamedArgument; + } + else + { + pAnyArgs[i] = aValAny; + } + } + } + else + { + for( i = 0 ; i < nParamCount ; i++ ) + { + pAnyArgs[i] = sbxToUnoValueImpl(pParams->Get(i + 1), + bBlockConversionToSmallestType ); + } + } + +} + +namespace { + +enum class INVOKETYPE +{ + GetProp = 0, + Func +}; + +} + +static Any invokeAutomationMethod( const OUString& Name, Sequence< Any > const & args, SbxArray* pParams, sal_uInt32 nParamCount, Reference< XInvocation > const & rxInvocation, INVOKETYPE invokeType ) +{ + Sequence< sal_Int16 > OutParamIndex; + Sequence< Any > OutParam; + + Any aRetAny; + switch( invokeType ) + { + case INVOKETYPE::Func: + aRetAny = rxInvocation->invoke( Name, args, OutParamIndex, OutParam ); + break; + case INVOKETYPE::GetProp: + { + Reference< XAutomationInvocation > xAutoInv( rxInvocation, UNO_QUERY ); + aRetAny = xAutoInv->invokeGetProperty( Name, args, OutParamIndex, OutParam ); + break; + } + default: + assert(false); break; + + } + const sal_Int16* pIndices = OutParamIndex.getConstArray(); + sal_uInt32 nLen = OutParamIndex.getLength(); + if( nLen ) + { + const Any* pNewValues = OutParam.getConstArray(); + for( sal_uInt32 j = 0 ; j < nLen ; j++ ) + { + sal_Int16 iTarget = pIndices[ j ]; + if( o3tl::make_unsigned(iTarget) >= nParamCount ) + break; + unoToSbxValue(pParams->Get(j + 1), pNewValues[j]); + } + } + return aRetAny; +} + +// Debugging help method to readout the implemented interfaces of an object +static OUString Impl_GetInterfaceInfo( const Reference< XInterface >& x, const Reference< XIdlClass >& xClass, sal_uInt16 nRekLevel ) +{ + Type aIfaceType = cppu::UnoType<XInterface>::get(); + static Reference< XIdlClass > xIfaceClass = TypeToIdlClass( aIfaceType ); + + OUStringBuffer aRetStr; + for( sal_uInt16 i = 0 ; i < nRekLevel ; i++ ) + aRetStr.append( " " ); + aRetStr.append( xClass->getName() ); + OUString aClassName = xClass->getName(); + Type aClassType( xClass->getTypeClass(), aClassName ); + + // checking if the interface is really supported + if( !x->queryInterface( aClassType ).hasValue() ) + { + aRetStr.append( " (ERROR: Not really supported!)\n" ); + } + // Are there super interfaces? + else + { + aRetStr.append( "\n" ); + + // get the super interfaces + Sequence< Reference< XIdlClass > > aSuperClassSeq = xClass->getSuperclasses(); + const Reference< XIdlClass >* pClasses = aSuperClassSeq.getConstArray(); + sal_uInt32 nSuperIfaceCount = aSuperClassSeq.getLength(); + for( sal_uInt32 j = 0 ; j < nSuperIfaceCount ; j++ ) + { + const Reference< XIdlClass >& rxIfaceClass = pClasses[j]; + if( !rxIfaceClass->equals( xIfaceClass ) ) + aRetStr.append( Impl_GetInterfaceInfo( x, rxIfaceClass, nRekLevel + 1 ) ); + } + } + return aRetStr.makeStringAndClear(); +} + +static OUString getDbgObjectNameImpl(SbUnoObject& rUnoObj) +{ + OUString aName = rUnoObj.GetClassName(); + if( aName.isEmpty() ) + { + Any aToInspectObj = rUnoObj.getUnoAny(); + Reference< XInterface > xObj(aToInspectObj, css::uno::UNO_QUERY); + if( xObj.is() ) + { + Reference< XServiceInfo > xServiceInfo( xObj, UNO_QUERY ); + if( xServiceInfo.is() ) + aName = xServiceInfo->getImplementationName(); + } + } + return aName; +} + +static OUString getDbgObjectName(SbUnoObject& rUnoObj) +{ + OUString aName = getDbgObjectNameImpl(rUnoObj); + if( aName.isEmpty() ) + aName += "Unknown"; + + OUStringBuffer aRet; + if( aName.getLength() > 20 ) + { + aRet.append( "\n" ); + } + aRet.append( "\"" ); + aRet.append( aName ); + aRet.append( "\":" ); + return aRet.makeStringAndClear(); +} + +OUString getBasicObjectTypeName( SbxObject* pObj ) +{ + if (pObj) + { + if (SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>(pObj)) + { + return getDbgObjectNameImpl(*pUnoObj); + } + else if (SbUnoStructRefObject* pUnoStructObj = dynamic_cast<SbUnoStructRefObject*>(pObj)) + { + return pUnoStructObj->GetClassName(); + } + } + return OUString(); +} + +namespace { + +bool matchesBasicTypeName( + css::uno::Reference<css::reflection::XIdlClass> const & unoType, OUString const & basicTypeName) +{ + if (unoType->getName().endsWithIgnoreAsciiCase(basicTypeName)) { + return true; + } + auto const sups = unoType->getSuperclasses(); + return std::any_of( + sups.begin(), sups.end(), + [&basicTypeName](auto const & t) { return matchesBasicTypeName(t, basicTypeName); }); +} + +} + +bool checkUnoObjectType(SbUnoObject& rUnoObj, const OUString& rClass) +{ + Any aToInspectObj = rUnoObj.getUnoAny(); + + // Return true for XInvocation based objects as interface type names don't count then + Reference< XInvocation > xInvocation( aToInspectObj, UNO_QUERY ); + if( xInvocation.is() ) + { + return true; + } + bool bResult = false; + Reference< XTypeProvider > xTypeProvider( aToInspectObj, UNO_QUERY ); + if( xTypeProvider.is() ) + { + /* Although interfaces in the ooo.vba namespace obey the IDL rules and + have a leading 'X', in Basic we want to be able to do something + like 'Dim wb As Workbooks' or 'Dim lb As MSForms.Label'. Here we + add a leading 'X' to the class name and a leading dot to the entire + type name. This results e.g. in '.XWorkbooks' or '.MSForms.XLabel' + which matches the interface names 'ooo.vba.excel.XWorkbooks' or + 'ooo.vba.msforms.XLabel'. + */ + OUString aClassName; + if ( SbiRuntime::isVBAEnabled() ) + { + aClassName = "."; + sal_Int32 nClassNameDot = rClass.lastIndexOf( '.' ); + if( nClassNameDot >= 0 ) + { + aClassName += OUString::Concat(rClass.subView( 0, nClassNameDot + 1 )) + "X" + rClass.subView( nClassNameDot + 1 ); + } + else + { + aClassName += "X" + rClass; + } + } + else // assume extended type declaration support for basic ( can't get here + // otherwise. + aClassName = rClass; + + Sequence< Type > aTypeSeq = xTypeProvider->getTypes(); + const Type* pTypeArray = aTypeSeq.getConstArray(); + sal_uInt32 nIfaceCount = aTypeSeq.getLength(); + for( sal_uInt32 j = 0 ; j < nIfaceCount ; j++ ) + { + const Type& rType = pTypeArray[j]; + + Reference<XIdlClass> xClass = TypeToIdlClass( rType ); + if( !xClass.is() ) + { + OSL_FAIL("failed to get XIdlClass for type"); + break; + } + OUString aInterfaceName = xClass->getName(); + if ( aInterfaceName == "com.sun.star.bridge.oleautomation.XAutomationObject" ) + { + // there is a hack in the extensions/source/ole/oleobj.cxx to return the typename of the automation object, lets check if it + // matches + Reference< XInvocation > xInv( aToInspectObj, UNO_QUERY ); + if ( xInv.is() ) + { + OUString sTypeName; + xInv->getValue( "$GetTypeName" ) >>= sTypeName; + if ( sTypeName.isEmpty() || sTypeName == "IDispatch" ) + { + // can't check type, leave it pass + bResult = true; + } + else + { + bResult = sTypeName == rClass; + } + } + break; // finished checking automation object + } + + if ( matchesBasicTypeName(xClass, aClassName) ) + { + bResult = true; + break; + } + } + } + return bResult; +} + +// Debugging help method to readout the implemented interfaces of an object +static OUString Impl_GetSupportedInterfaces(SbUnoObject& rUnoObj) +{ + Any aToInspectObj = rUnoObj.getUnoAny(); + + // allow only TypeClass interface + OUStringBuffer aRet; + auto x = o3tl::tryAccess<Reference<XInterface>>(aToInspectObj); + if( !x ) + { + aRet.append( ID_DBG_SUPPORTEDINTERFACES ); + aRet.append( " not available.\n(TypeClass is not TypeClass_INTERFACE)\n" ); + } + else + { + Reference< XTypeProvider > xTypeProvider( *x, UNO_QUERY ); + + aRet.append( "Supported interfaces by object " ); + aRet.append(getDbgObjectName(rUnoObj)); + aRet.append( "\n" ); + if( xTypeProvider.is() ) + { + // get the interfaces of the implementation + Sequence< Type > aTypeSeq = xTypeProvider->getTypes(); + const Type* pTypeArray = aTypeSeq.getConstArray(); + sal_uInt32 nIfaceCount = aTypeSeq.getLength(); + for( sal_uInt32 j = 0 ; j < nIfaceCount ; j++ ) + { + const Type& rType = pTypeArray[j]; + + Reference<XIdlClass> xClass = TypeToIdlClass( rType ); + if( xClass.is() ) + { + aRet.append( Impl_GetInterfaceInfo( *x, xClass, 1 ) ); + } + else + { + typelib_TypeDescription * pTD = nullptr; + rType.getDescription( &pTD ); + + aRet.append( "*** ERROR: No IdlClass for type \"" ); + aRet.append( pTD->pTypeName ); + aRet.append( "\"\n*** Please check type library\n" ); + } + } + } + } + return aRet.makeStringAndClear(); +} + + +// Debugging help method SbxDataType -> String +static OUString Dbg_SbxDataType2String( SbxDataType eType ) +{ + OUStringBuffer aRet; + switch( +eType ) + { + case SbxEMPTY: aRet.append("SbxEMPTY"); break; + case SbxNULL: aRet.append("SbxNULL"); break; + case SbxINTEGER: aRet.append("SbxINTEGER"); break; + case SbxLONG: aRet.append("SbxLONG"); break; + case SbxSINGLE: aRet.append("SbxSINGLE"); break; + case SbxDOUBLE: aRet.append("SbxDOUBLE"); break; + case SbxCURRENCY: aRet.append("SbxCURRENCY"); break; + case SbxDECIMAL: aRet.append("SbxDECIMAL"); break; + case SbxDATE: aRet.append("SbxDATE"); break; + case SbxSTRING: aRet.append("SbxSTRING"); break; + case SbxOBJECT: aRet.append("SbxOBJECT"); break; + case SbxERROR: aRet.append("SbxERROR"); break; + case SbxBOOL: aRet.append("SbxBOOL"); break; + case SbxVARIANT: aRet.append("SbxVARIANT"); break; + case SbxDATAOBJECT: aRet.append("SbxDATAOBJECT"); break; + case SbxCHAR: aRet.append("SbxCHAR"); break; + case SbxBYTE: aRet.append("SbxBYTE"); break; + case SbxUSHORT: aRet.append("SbxUSHORT"); break; + case SbxULONG: aRet.append("SbxULONG"); break; + case SbxSALINT64: aRet.append("SbxINT64"); break; + case SbxSALUINT64: aRet.append("SbxUINT64"); break; + case SbxINT: aRet.append("SbxINT"); break; + case SbxUINT: aRet.append("SbxUINT"); break; + case SbxVOID: aRet.append("SbxVOID"); break; + case SbxHRESULT: aRet.append("SbxHRESULT"); break; + case SbxPOINTER: aRet.append("SbxPOINTER"); break; + case SbxDIMARRAY: aRet.append("SbxDIMARRAY"); break; + case SbxCARRAY: aRet.append("SbxCARRAY"); break; + case SbxUSERDEF: aRet.append("SbxUSERDEF"); break; + case SbxLPSTR: aRet.append("SbxLPSTR"); break; + case SbxLPWSTR: aRet.append("SbxLPWSTR"); break; + case SbxCoreSTRING: aRet.append("SbxCoreSTRING"); break; + case SbxOBJECT | SbxARRAY: aRet.append("SbxARRAY"); break; + default: aRet.append("Unknown Sbx-Type!");break; + } + return aRet.makeStringAndClear(); +} + +// Debugging help method to display the properties of a SbUnoObjects +static OUString Impl_DumpProperties(SbUnoObject& rUnoObj) +{ + OUStringBuffer aRet; + aRet.append("Properties of object "); + aRet.append(getDbgObjectName(rUnoObj)); + + // analyse the Uno-Infos to recognise the arrays + Reference< XIntrospectionAccess > xAccess = rUnoObj.getIntrospectionAccess(); + if( !xAccess.is() ) + { + Reference< XInvocation > xInvok = rUnoObj.getInvocation(); + if( xInvok.is() ) + xAccess = xInvok->getIntrospection(); + } + if( !xAccess.is() ) + { + aRet.append( "\nUnknown, no introspection available\n" ); + return aRet.makeStringAndClear(); + } + + Sequence<Property> props = xAccess->getProperties( PropertyConcept::ALL - PropertyConcept::DANGEROUS ); + sal_uInt32 nUnoPropCount = props.getLength(); + const Property* pUnoProps = props.getConstArray(); + + SbxArray* pProps = rUnoObj.GetProperties(); + sal_uInt32 nPropCount = pProps->Count(); + sal_uInt32 nPropsPerLine = 1 + nPropCount / 30; + for( sal_uInt32 i = 0; i < nPropCount; i++ ) + { + SbxVariable* pVar = pProps->Get(i); + if( pVar ) + { + OUStringBuffer aPropStr; + if( (i % nPropsPerLine) == 0 ) + aPropStr.append( "\n" ); + + // output the type and name + // Is it in Uno a sequence? + SbxDataType eType = pVar->GetFullType(); + + bool bMaybeVoid = false; + if( i < nUnoPropCount ) + { + const Property& rProp = pUnoProps[ i ]; + + // For MAYBEVOID freshly convert the type from Uno, + // so not just SbxEMPTY is returned. + if( rProp.Attributes & PropertyAttribute::MAYBEVOID ) + { + eType = unoToSbxType( rProp.Type.getTypeClass() ); + bMaybeVoid = true; + } + if( eType == SbxOBJECT ) + { + Type aType = rProp.Type; + if( aType.getTypeClass() == TypeClass_SEQUENCE ) + eType = SbxDataType( SbxOBJECT | SbxARRAY ); + } + } + aPropStr.append( Dbg_SbxDataType2String( eType ) ); + if( bMaybeVoid ) + aPropStr.append( "/void" ); + aPropStr.append( " " ); + aPropStr.append( pVar->GetName() ); + + if( i == nPropCount - 1 ) + aPropStr.append( "\n" ); + else + aPropStr.append( "; " ); + + aRet.append( aPropStr ); + } + } + return aRet.makeStringAndClear(); +} + +// Debugging help method to display the methods of an SbUnoObjects +static OUString Impl_DumpMethods(SbUnoObject& rUnoObj) +{ + OUStringBuffer aRet; + aRet.append("Methods of object "); + aRet.append(getDbgObjectName(rUnoObj)); + + // XIntrospectionAccess, so that the types of the parameter could be outputted + Reference< XIntrospectionAccess > xAccess = rUnoObj.getIntrospectionAccess(); + if( !xAccess.is() ) + { + Reference< XInvocation > xInvok = rUnoObj.getInvocation(); + if( xInvok.is() ) + xAccess = xInvok->getIntrospection(); + } + if( !xAccess.is() ) + { + aRet.append( "\nUnknown, no introspection available\n" ); + return aRet.makeStringAndClear(); + } + Sequence< Reference< XIdlMethod > > methods = xAccess->getMethods + ( MethodConcept::ALL - MethodConcept::DANGEROUS ); + const Reference< XIdlMethod >* pUnoMethods = methods.getConstArray(); + + SbxArray* pMethods = rUnoObj.GetMethods(); + sal_uInt32 nMethodCount = pMethods->Count(); + if( !nMethodCount ) + { + aRet.append( "\nNo methods found\n" ); + return aRet.makeStringAndClear(); + } + sal_uInt32 nPropsPerLine = 1 + nMethodCount / 30; + for( sal_uInt32 i = 0; i < nMethodCount; i++ ) + { + SbxVariable* pVar = pMethods->Get(i); + if( pVar ) + { + if( (i % nPropsPerLine) == 0 ) + aRet.append( "\n" ); + + // address the method + const Reference< XIdlMethod >& rxMethod = pUnoMethods[i]; + + // Is it in Uno a sequence? + SbxDataType eType = pVar->GetFullType(); + if( eType == SbxOBJECT ) + { + Reference< XIdlClass > xClass = rxMethod->getReturnType(); + if( xClass.is() && xClass->getTypeClass() == TypeClass_SEQUENCE ) + eType = SbxDataType( SbxOBJECT | SbxARRAY ); + } + // output the name and the type + aRet.append( Dbg_SbxDataType2String( eType ) ); + aRet.append( " " ); + aRet.append ( pVar->GetName() ); + aRet.append( " ( " ); + + // the get-method mustn't have a parameter + Sequence< Reference< XIdlClass > > aParamsSeq = rxMethod->getParameterTypes(); + sal_uInt32 nParamCount = aParamsSeq.getLength(); + const Reference< XIdlClass >* pParams = aParamsSeq.getConstArray(); + + if( nParamCount > 0 ) + { + for( sal_uInt32 j = 0; j < nParamCount; j++ ) + { + aRet.append ( Dbg_SbxDataType2String( unoToSbxType( pParams[ j ] ) ) ); + if( j < nParamCount - 1 ) + aRet.append( ", " ); + } + } + else + aRet.append( "void" ); + + aRet.append( " ) " ); + + if( i == nMethodCount - 1 ) + aRet.append( "\n" ); + else + aRet.append( "; " ); + } + } + return aRet.makeStringAndClear(); +} + + +// Implementation SbUnoObject +void SbUnoObject::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + if( bNeedIntrospection ) + doIntrospection(); + + const SbxHint* pHint = dynamic_cast<const SbxHint*>(&rHint); + if( !pHint ) + return; + + SbxVariable* pVar = pHint->GetVar(); + SbxArray* pParams = pVar->GetParameters(); + SbUnoProperty* pProp = dynamic_cast<SbUnoProperty*>( pVar ); + SbUnoMethod* pMeth = dynamic_cast<SbUnoMethod*>( pVar ); + if( pProp ) + { + bool bInvocation = pProp->isInvocationBased(); + if( pHint->GetId() == SfxHintId::BasicDataWanted ) + { + // Test-Properties + sal_Int32 nId = pProp->nId; + if( nId < 0 ) + { + // Id == -1: Display implemented interfaces according the ClassProvider + if( nId == -1 ) // Property ID_DBG_SUPPORTEDINTERFACES" + { + OUString aRetStr = Impl_GetSupportedInterfaces(*this); + pVar->PutString( aRetStr ); + } + // Id == -2: output properties + else if( nId == -2 ) // Property ID_DBG_PROPERTIES + { + // now all properties must be created + implCreateAll(); + OUString aRetStr = Impl_DumpProperties(*this); + pVar->PutString( aRetStr ); + } + // Id == -3: output the methods + else if( nId == -3 ) // Property ID_DBG_METHODS + { + // now all properties must be created + implCreateAll(); + OUString aRetStr = Impl_DumpMethods(*this); + pVar->PutString( aRetStr ); + } + return; + } + + if( !bInvocation && mxUnoAccess.is() ) + { + try + { + if ( maStructInfo ) + { + StructRefInfo aMember = maStructInfo->getStructMember( pProp->GetName() ); + if ( aMember.isEmpty() ) + { + StarBASIC::Error( ERRCODE_BASIC_PROPERTY_NOT_FOUND ); + } + else + { + if ( pProp->isUnoStruct() ) + { + SbUnoStructRefObject* pSbUnoObject = new SbUnoStructRefObject( pProp->GetName(), std::move(aMember) ); + SbxObjectRef xWrapper = static_cast<SbxObject*>(pSbUnoObject); + pVar->PutObject( xWrapper.get() ); + } + else + { + Any aRetAny = aMember.getValue(); + // take over the value from Uno to Sbx + unoToSbxValue( pVar, aRetAny ); + } + return; + } + } + // get the value + Reference< XPropertySet > xPropSet( mxUnoAccess->queryAdapter( cppu::UnoType<XPropertySet>::get()), UNO_QUERY ); + Any aRetAny = xPropSet->getPropertyValue( pProp->GetName() ); + // The use of getPropertyValue (instead of using the index) is + // suboptimal, but the refactoring to XInvocation is already pending + // Otherwise it is possible to use FastPropertySet + + // take over the value from Uno to Sbx + unoToSbxValue( pVar, aRetAny ); + } + catch( const Exception& ) + { + implHandleAnyException( ::cppu::getCaughtException() ); + } + } + else if( bInvocation && mxInvocation.is() ) + { + try + { + sal_uInt32 nParamCount = pParams ? (pParams->Count() - 1) : 0; + bool bCanBeConsideredAMethod = mxInvocation->hasMethod( pProp->GetName() ); + Any aRetAny; + if ( bCanBeConsideredAMethod && nParamCount ) + { + // Automation properties have methods, so... we need to invoke this through + // XInvocation + Sequence<Any> args; + processAutomationParams( pParams, args, nParamCount ); + aRetAny = invokeAutomationMethod( pProp->GetName(), args, pParams, nParamCount, mxInvocation, INVOKETYPE::GetProp ); + } + else + aRetAny = mxInvocation->getValue( pProp->GetName() ); + // take over the value from Uno to Sbx + unoToSbxValue( pVar, aRetAny ); + if( pParams && bCanBeConsideredAMethod ) + pVar->SetParameters( nullptr ); + + } + catch( const Exception& ) + { + implHandleAnyException( ::cppu::getCaughtException() ); + } + } + } + else if( pHint->GetId() == SfxHintId::BasicDataChanged ) + { + if( !bInvocation && mxUnoAccess.is() ) + { + if( pProp->aUnoProp.Attributes & PropertyAttribute::READONLY ) + { + StarBASIC::Error( ERRCODE_BASIC_PROP_READONLY ); + return; + } + if ( maStructInfo ) + { + StructRefInfo aMember = maStructInfo->getStructMember( pProp->GetName() ); + if ( aMember.isEmpty() ) + { + StarBASIC::Error( ERRCODE_BASIC_PROPERTY_NOT_FOUND ); + } + else + { + Any aAnyValue = sbxToUnoValue( pVar, pProp->aUnoProp.Type, &pProp->aUnoProp ); + aMember.setValue( aAnyValue ); + } + return; + } + // take over the value from Uno to Sbx + Any aAnyValue = sbxToUnoValue( pVar, pProp->aUnoProp.Type, &pProp->aUnoProp ); + try + { + // set the value + Reference< XPropertySet > xPropSet( mxUnoAccess->queryAdapter( cppu::UnoType<XPropertySet>::get()), UNO_QUERY ); + xPropSet->setPropertyValue( pProp->GetName(), aAnyValue ); + // The use of getPropertyValue (instead of using the index) is + // suboptimal, but the refactoring to XInvocation is already pending + // Otherwise it is possible to use FastPropertySet + } + catch( const Exception& ) + { + implHandleAnyException( ::cppu::getCaughtException() ); + } + } + else if( bInvocation && mxInvocation.is() ) + { + // take over the value from Uno to Sbx + Any aAnyValue = sbxToUnoValueImpl( pVar ); + try + { + // set the value + mxInvocation->setValue( pProp->GetName(), aAnyValue ); + } + catch( const Exception& ) + { + implHandleAnyException( ::cppu::getCaughtException() ); + } + } + } + } + else if( pMeth ) + { + bool bInvocation = pMeth->isInvocationBased(); + if( pHint->GetId() == SfxHintId::BasicDataWanted ) + { + // number of Parameter -1 because of Param0 == this + sal_uInt32 nParamCount = pParams ? (pParams->Count() - 1) : 0; + Sequence<Any> args; + bool bOutParams = false; + + if( !bInvocation && mxUnoAccess.is() ) + { + // get info + const Sequence<ParamInfo>& rInfoSeq = pMeth->getParamInfos(); + const ParamInfo* pParamInfos = rInfoSeq.getConstArray(); + sal_uInt32 nUnoParamCount = rInfoSeq.getLength(); + sal_uInt32 nAllocParamCount = nParamCount; + + // ignore surplus parameter; alternative: throw an error + if( nParamCount > nUnoParamCount ) + { + nParamCount = nUnoParamCount; + nAllocParamCount = nParamCount; + } + else if( nParamCount < nUnoParamCount ) + { + SbiInstance* pInst = GetSbData()->pInst; + if( pInst && pInst->IsCompatibility() ) + { + // Check types + bool bError = false; + for( sal_uInt32 i = nParamCount ; i < nUnoParamCount ; i++ ) + { + const ParamInfo& rInfo = pParamInfos[i]; + const Reference< XIdlClass >& rxClass = rInfo.aType; + if( rxClass->getTypeClass() != TypeClass_ANY ) + { + bError = true; + StarBASIC::Error( ERRCODE_BASIC_NOT_OPTIONAL ); + } + } + if( !bError ) + nAllocParamCount = nUnoParamCount; + } + } + + if( nAllocParamCount > 0 ) + { + args.realloc( nAllocParamCount ); + Any* pAnyArgs = args.getArray(); + for( sal_uInt32 i = 0 ; i < nParamCount ; i++ ) + { + const ParamInfo& rInfo = pParamInfos[i]; + const Reference< XIdlClass >& rxClass = rInfo.aType; + + css::uno::Type aType( rxClass->getTypeClass(), rxClass->getName() ); + + // ATTENTION: Don't forget for Sbx-Parameter the offset! + pAnyArgs[i] = sbxToUnoValue(pParams->Get(i + 1), aType); + + // If it is not certain check whether the out-parameter are available. + if( !bOutParams ) + { + ParamMode aParamMode = rInfo.aMode; + if( aParamMode != ParamMode_IN ) + bOutParams = true; + } + } + } + } + else if( bInvocation && pParams && mxInvocation.is() ) + { + processAutomationParams( pParams, args, nParamCount ); + } + + // call the method + GetSbData()->bBlockCompilerError = true; // #106433 Block compiler errors for API calls + try + { + if( !bInvocation && mxUnoAccess.is() ) + { + Any aRetAny = pMeth->m_xUnoMethod->invoke( getUnoAny(), args ); + + // take over the value from Uno to Sbx + unoToSbxValue( pVar, aRetAny ); + + // Did we to copy back the Out-Parameter? + if( bOutParams ) + { + const Any* pAnyArgs = args.getConstArray(); + + // get info + const Sequence<ParamInfo>& rInfoSeq = pMeth->getParamInfos(); + const ParamInfo* pParamInfos = rInfoSeq.getConstArray(); + + sal_uInt32 j; + for( j = 0 ; j < nParamCount ; j++ ) + { + const ParamInfo& rInfo = pParamInfos[j]; + ParamMode aParamMode = rInfo.aMode; + if( aParamMode != ParamMode_IN ) + unoToSbxValue(pParams->Get(j + 1), pAnyArgs[j]); + } + } + } + else if( bInvocation && mxInvocation.is() ) + { + Any aRetAny = invokeAutomationMethod( pMeth->GetName(), args, pParams, nParamCount, mxInvocation, INVOKETYPE::Func ); + unoToSbxValue( pVar, aRetAny ); + } + + // remove parameter here, because this was not done anymore in unoToSbxValue() + // for arrays + if( pParams ) + pVar->SetParameters( nullptr ); + } + catch( const Exception& ) + { + implHandleAnyException( ::cppu::getCaughtException() ); + } + GetSbData()->bBlockCompilerError = false; // #106433 Unblock compiler errors + } + } + else + SbxObject::Notify( rBC, rHint ); +} + + +SbUnoObject::SbUnoObject( const OUString& aName_, const Any& aUnoObj_ ) + : SbxObject( aName_ ) + , bNeedIntrospection( true ) + , bNativeCOMObject( false ) +{ + // beat out again the default properties of Sbx + Remove( "Name", SbxClassType::DontCare ); + Remove( "Parent", SbxClassType::DontCare ); + + // check the type of the objects + TypeClass eType = aUnoObj_.getValueType().getTypeClass(); + Reference< XInterface > x; + if( eType == TypeClass_INTERFACE ) + { + // get the interface from the Any + aUnoObj_ >>= x; + if( !x.is() ) + return; + } + + // Did the object have an invocation itself? + mxInvocation.set( x, UNO_QUERY ); + + if( mxInvocation.is() ) + { + + // get the ExactName + mxExactNameInvocation.set( mxInvocation, UNO_QUERY ); + + // The remainder refers only to the introspection + Reference< XTypeProvider > xTypeProvider( x, UNO_QUERY ); + if( !xTypeProvider.is() ) + { + bNeedIntrospection = false; + return; + } + + // Ignore introspection based members for COM objects to avoid + // hiding of equally named COM symbols, e.g. XInvocation::getValue + Reference< oleautomation::XAutomationObject > xAutomationObject( aUnoObj_, UNO_QUERY ); + if( xAutomationObject.is() ) + bNativeCOMObject = true; + } + + maTmpUnoObj = aUnoObj_; + + + //*** Define the name *** + bool bFatalError = true; + + // Is it an interface or a struct? + bool bSetClassName = false; + OUString aClassName_; + if( eType == TypeClass_STRUCT || eType == TypeClass_EXCEPTION ) + { + // Struct is Ok + bFatalError = false; + + // insert the real name of the class + if( aName_.isEmpty() ) + { + aClassName_ = aUnoObj_.getValueType().getTypeName(); + bSetClassName = true; + } + StructRefInfo aThisStruct( maTmpUnoObj, maTmpUnoObj.getValueType(), 0 ); + maStructInfo = std::make_shared<SbUnoStructRefObject>( GetName(), aThisStruct ); + } + else if( eType == TypeClass_INTERFACE ) + { + // Interface works always through the type in the Any + bFatalError = false; + } + if( bSetClassName ) + SetClassName( aClassName_ ); + + // Neither interface nor Struct -> FatalError + if( bFatalError ) + { + StarBASIC::FatalError( ERRCODE_BASIC_EXCEPTION ); + return; + } + + // pass the introspection primal on demand +} + +SbUnoObject::~SbUnoObject() +{ +} + + +// pass the introspection on Demand +void SbUnoObject::doIntrospection() +{ + if( !bNeedIntrospection ) + return; + + Reference<XComponentContext> xContext = comphelper::getProcessComponentContext(); + + if (!xContext.is()) + return; + + + // get the introspection service + Reference<XIntrospection> xIntrospection; + + try + { + xIntrospection = theIntrospection::get(xContext); + } + catch ( const css::uno::DeploymentException& ) + { + } + + if (!xIntrospection.is()) + return; + + bNeedIntrospection = false; + + // pass the introspection + try + { + mxUnoAccess = xIntrospection->inspect( maTmpUnoObj ); + } + catch( const RuntimeException& e ) + { + StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, implGetExceptionMsg( e ) ); + } + + if( !mxUnoAccess.is() ) + { + // #51475 mark to indicate an invalid object (no mxMaterialHolder) + return; + } + + // get MaterialHolder from access + mxMaterialHolder.set( mxUnoAccess, UNO_QUERY ); + + // get ExactName from access + mxExactName.set( mxUnoAccess, UNO_QUERY ); +} + + +// Start of a list of all SbUnoMethod-Instances +static SbUnoMethod* pFirst = nullptr; + +void clearUnoMethodsForBasic( StarBASIC const * pBasic ) +{ + SbUnoMethod* pMeth = pFirst; + while( pMeth ) + { + SbxObject* pObject = pMeth->GetParent(); + if ( pObject ) + { + StarBASIC* pModBasic = dynamic_cast< StarBASIC* >( pObject->GetParent() ); + if ( pModBasic == pBasic ) + { + // for now the solution is to remove the method from the list and to clear it, + // but in case the element should be correctly transferred to another StarBASIC, + // we should either set module parent to NULL without clearing it, or even + // set the new StarBASIC as the parent of the module + // pObject->SetParent( NULL ); + + if( pMeth == pFirst ) + pFirst = pMeth->pNext; + else if( pMeth->pPrev ) + pMeth->pPrev->pNext = pMeth->pNext; + if( pMeth->pNext ) + pMeth->pNext->pPrev = pMeth->pPrev; + + pMeth->pPrev = nullptr; + pMeth->pNext = nullptr; + + pMeth->SbxValue::Clear(); + pObject->SbxValue::Clear(); + + // start from the beginning after object clearing, the cycle will end since the method is removed each time + pMeth = pFirst; + } + else + pMeth = pMeth->pNext; + } + else + pMeth = pMeth->pNext; + } +} + +void clearUnoMethods() +{ + SbUnoMethod* pMeth = pFirst; + while( pMeth ) + { + pMeth->SbxValue::Clear(); + pMeth = pMeth->pNext; + } +} + + +SbUnoMethod::SbUnoMethod +( + const OUString& aName_, + SbxDataType eSbxType, + Reference< XIdlMethod > const & xUnoMethod_, + bool bInvocation +) + : SbxMethod( aName_, eSbxType ) + , mbInvocation( bInvocation ) +{ + m_xUnoMethod = xUnoMethod_; + pParamInfoSeq = nullptr; + + // enregister the method in a list + pNext = pFirst; + pPrev = nullptr; + pFirst = this; + if( pNext ) + pNext->pPrev = this; +} + +SbUnoMethod::~SbUnoMethod() +{ + pParamInfoSeq.reset(); + + if( this == pFirst ) + pFirst = pNext; + else if( pPrev ) + pPrev->pNext = pNext; + if( pNext ) + pNext->pPrev = pPrev; +} + +SbxInfo* SbUnoMethod::GetInfo() +{ + if( !pInfo.is() && m_xUnoMethod.is() ) + { + SbiInstance* pInst = GetSbData()->pInst; + if( pInst && pInst->IsCompatibility() ) + { + pInfo = new SbxInfo(); + + const Sequence<ParamInfo>& rInfoSeq = getParamInfos(); + const ParamInfo* pParamInfos = rInfoSeq.getConstArray(); + sal_uInt32 nParamCount = rInfoSeq.getLength(); + + for( sal_uInt32 i = 0 ; i < nParamCount ; i++ ) + { + const ParamInfo& rInfo = pParamInfos[i]; + OUString aParamName = rInfo.aName; + + pInfo->AddParam( aParamName, SbxVARIANT, SbxFlagBits::Read ); + } + } + } + return pInfo.get(); +} + +const Sequence<ParamInfo>& SbUnoMethod::getParamInfos() +{ + if (!pParamInfoSeq) + { + Sequence<ParamInfo> aTmp; + if (m_xUnoMethod.is()) + aTmp = m_xUnoMethod->getParameterInfos(); + pParamInfoSeq.reset( new Sequence<ParamInfo>(aTmp) ); + } + return *pParamInfoSeq; +} + +SbUnoProperty::SbUnoProperty +( + const OUString& aName_, + SbxDataType eSbxType, + SbxDataType eRealSbxType, + Property aUnoProp_, + sal_Int32 nId_, + bool bInvocation, + bool bUnoStruct +) + : SbxProperty( aName_, eSbxType ) + , aUnoProp(std::move( aUnoProp_ )) + , nId( nId_ ) + , mbInvocation( bInvocation ) + , mRealType( eRealSbxType ) + , mbUnoStruct( bUnoStruct ) +{ + // as needed establish a dummy array so that SbiRuntime::CheckArray() works + static SbxArrayRef xDummyArray = new SbxArray( SbxVARIANT ); + if( eSbxType & SbxARRAY ) + PutObject( xDummyArray.get() ); +} + +SbUnoProperty::~SbUnoProperty() +{} + + +SbxVariable* SbUnoObject::Find( const OUString& rName, SbxClassType t ) +{ + static Reference< XIdlMethod > xDummyMethod; + static Property aDummyProp; + + SbxVariable* pRes = SbxObject::Find( rName, t ); + + if( bNeedIntrospection ) + doIntrospection(); + + // New 1999-03-04: Create properties on demand. Therefore search now via + // IntrospectionAccess if a property or a method of the required name exist + if( !pRes ) + { + OUString aUName( rName ); + if( mxUnoAccess.is() && !bNativeCOMObject ) + { + if( mxExactName.is() ) + { + OUString aUExactName = mxExactName->getExactName( aUName ); + if( !aUExactName.isEmpty() ) + { + aUName = aUExactName; + } + } + if( mxUnoAccess->hasProperty( aUName, PropertyConcept::ALL - PropertyConcept::DANGEROUS ) ) + { + const Property& rProp = mxUnoAccess-> + getProperty( aUName, PropertyConcept::ALL - PropertyConcept::DANGEROUS ); + + // If the property could be void the type had to be set to Variant + SbxDataType eSbxType; + if( rProp.Attributes & PropertyAttribute::MAYBEVOID ) + eSbxType = SbxVARIANT; + else + eSbxType = unoToSbxType( rProp.Type.getTypeClass() ); + + SbxDataType eRealSbxType = ( ( rProp.Attributes & PropertyAttribute::MAYBEVOID ) ? unoToSbxType( rProp.Type.getTypeClass() ) : eSbxType ); + // create the property and superimpose it + auto pProp = tools::make_ref<SbUnoProperty>( rProp.Name, eSbxType, eRealSbxType, rProp, 0, false, ( rProp.Type.getTypeClass() == css::uno::TypeClass_STRUCT ) ); + QuickInsert( pProp.get() ); + pRes = pProp.get(); + } + else if( mxUnoAccess->hasMethod( aUName, + MethodConcept::ALL - MethodConcept::DANGEROUS ) ) + { + // address the method + const Reference< XIdlMethod >& rxMethod = mxUnoAccess-> + getMethod( aUName, MethodConcept::ALL - MethodConcept::DANGEROUS ); + + // create SbUnoMethod and superimpose it + auto xMethRef = tools::make_ref<SbUnoMethod>( rxMethod->getName(), + unoToSbxType( rxMethod->getReturnType() ), rxMethod, false ); + QuickInsert( xMethRef.get() ); + pRes = xMethRef.get(); + } + + // If nothing was found check via XNameAccess + if( !pRes ) + { + try + { + Reference< XNameAccess > xNameAccess( mxUnoAccess->queryAdapter( cppu::UnoType<XPropertySet>::get()), UNO_QUERY ); + + if( xNameAccess.is() && xNameAccess->hasByName( rName ) ) + { + Any aAny = xNameAccess->getByName( rName ); + + // ATTENTION: Because of XNameAccess, the variable generated here + // may not be included as a fixed property in the object and therefore + // won't be stored anywhere. + // If this leads to problems, it has to be created + // synthetically or a class SbUnoNameAccessProperty, + // which checks the existence on access and which + // is disposed if the name is not found anymore. + pRes = new SbxVariable( SbxVARIANT ); + unoToSbxValue( pRes, aAny ); + } + } + catch( const NoSuchElementException& e ) + { + StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, implGetExceptionMsg( e ) ); + } + catch( const Exception& ) + { + // Establish so that the exception error will not be overwritten + if( !pRes ) + pRes = new SbxVariable( SbxVARIANT ); + + implHandleAnyException( ::cppu::getCaughtException() ); + } + } + } + if( !pRes && mxInvocation.is() ) + { + if( mxExactNameInvocation.is() ) + { + OUString aUExactName = mxExactNameInvocation->getExactName( aUName ); + if( !aUExactName.isEmpty() ) + { + aUName = aUExactName; + } + } + + try + { + if( mxInvocation->hasProperty( aUName ) ) + { + // create a property and superimpose it + auto xVarRef = tools::make_ref<SbUnoProperty>( aUName, SbxVARIANT, SbxVARIANT, aDummyProp, 0, true, false ); + QuickInsert( xVarRef.get() ); + pRes = xVarRef.get(); + } + else if( mxInvocation->hasMethod( aUName ) ) + { + // create SbUnoMethode and superimpose it + auto xMethRef = tools::make_ref<SbUnoMethod>( aUName, SbxVARIANT, xDummyMethod, true ); + QuickInsert( xMethRef.get() ); + pRes = xMethRef.get(); + } + else + { + Reference< XDirectInvocation > xDirectInvoke( mxInvocation, UNO_QUERY ); + if ( xDirectInvoke.is() && xDirectInvoke->hasMember( aUName ) ) + { + auto xMethRef = tools::make_ref<SbUnoMethod>( aUName, SbxVARIANT, xDummyMethod, true ); + QuickInsert( xMethRef.get() ); + pRes = xMethRef.get(); + } + + } + } + catch( const RuntimeException& e ) + { + // Establish so that the exception error will not be overwritten + if( !pRes ) + pRes = new SbxVariable( SbxVARIANT ); + + StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, implGetExceptionMsg( e ) ); + } + } + } + + // At the very end checking if the Dbg_-Properties are meant + + if( !pRes ) + { + if( rName.equalsIgnoreAsciiCase(ID_DBG_SUPPORTEDINTERFACES) || + rName.equalsIgnoreAsciiCase(ID_DBG_PROPERTIES) || + rName.equalsIgnoreAsciiCase(ID_DBG_METHODS) ) + { + // Create + implCreateDbgProperties(); + + // Now they have to be found regular + pRes = SbxObject::Find( rName, SbxClassType::DontCare ); + } + } + return pRes; +} + + +// help method to create the dbg_-Properties +void SbUnoObject::implCreateDbgProperties() +{ + Property aProp; + + // Id == -1: display the implemented interfaces corresponding the ClassProvider + auto xVarRef = tools::make_ref<SbUnoProperty>( OUString(ID_DBG_SUPPORTEDINTERFACES), SbxSTRING, SbxSTRING, aProp, -1, false, false ); + QuickInsert( xVarRef.get() ); + + // Id == -2: output the properties + xVarRef = tools::make_ref<SbUnoProperty>( OUString(ID_DBG_PROPERTIES), SbxSTRING, SbxSTRING, aProp, -2, false, false ); + QuickInsert( xVarRef.get() ); + + // Id == -3: output the Methods + xVarRef = tools::make_ref<SbUnoProperty>( OUString(ID_DBG_METHODS), SbxSTRING, SbxSTRING, aProp, -3, false, false ); + QuickInsert( xVarRef.get() ); +} + +void SbUnoObject::implCreateAll() +{ + // throw away all existing methods and properties + pMethods = tools::make_ref<SbxArray>(); + pProps = tools::make_ref<SbxArray>(); + + if( bNeedIntrospection ) doIntrospection(); + + // get introspection + Reference< XIntrospectionAccess > xAccess = mxUnoAccess; + if( !xAccess.is() || bNativeCOMObject ) + { + if( mxInvocation.is() ) + xAccess = mxInvocation->getIntrospection(); + else if( bNativeCOMObject ) + return; + } + if( !xAccess.is() ) + return; + + // Establish properties + Sequence<Property> props = xAccess->getProperties( PropertyConcept::ALL - PropertyConcept::DANGEROUS ); + sal_uInt32 nPropCount = props.getLength(); + const Property* pProps_ = props.getConstArray(); + + sal_uInt32 i; + for( i = 0 ; i < nPropCount ; i++ ) + { + const Property& rProp = pProps_[ i ]; + + // If the property could be void the type had to be set to Variant + SbxDataType eSbxType; + if( rProp.Attributes & PropertyAttribute::MAYBEVOID ) + eSbxType = SbxVARIANT; + else + eSbxType = unoToSbxType( rProp.Type.getTypeClass() ); + + SbxDataType eRealSbxType = ( ( rProp.Attributes & PropertyAttribute::MAYBEVOID ) ? unoToSbxType( rProp.Type.getTypeClass() ) : eSbxType ); + // Create property and superimpose it + auto xVarRef = tools::make_ref<SbUnoProperty>( rProp.Name, eSbxType, eRealSbxType, rProp, i, false, ( rProp.Type.getTypeClass() == css::uno::TypeClass_STRUCT ) ); + QuickInsert( xVarRef.get() ); + } + + // Create Dbg_-Properties + implCreateDbgProperties(); + + // Create methods + Sequence< Reference< XIdlMethod > > aMethodSeq = xAccess->getMethods + ( MethodConcept::ALL - MethodConcept::DANGEROUS ); + sal_uInt32 nMethCount = aMethodSeq.getLength(); + const Reference< XIdlMethod >* pMethods_ = aMethodSeq.getConstArray(); + for( i = 0 ; i < nMethCount ; i++ ) + { + // address method + const Reference< XIdlMethod >& rxMethod = pMethods_[i]; + + // Create SbUnoMethod and superimpose it + auto xMethRef = tools::make_ref<SbUnoMethod> + ( rxMethod->getName(), unoToSbxType( rxMethod->getReturnType() ), rxMethod, false ); + QuickInsert( xMethRef.get() ); + } +} + + +// output the value +Any SbUnoObject::getUnoAny() +{ + Any aRetAny; + if( bNeedIntrospection ) doIntrospection(); + if ( maStructInfo ) + aRetAny = maTmpUnoObj; + else if( mxMaterialHolder.is() ) + aRetAny = mxMaterialHolder->getMaterial(); + else if( mxInvocation.is() ) + aRetAny <<= mxInvocation; + return aRetAny; +} + +// help method to create a Uno-Struct per CoreReflection +static SbUnoObjectRef Impl_CreateUnoStruct( const OUString& aClassName ) +{ + // get CoreReflection + Reference< XIdlReflection > xCoreReflection = getCoreReflection_Impl(); + if( !xCoreReflection.is() ) + return nullptr; + + // search for the class + Reference< XIdlClass > xClass; + const Reference< XHierarchicalNameAccess >& xHarryName = + getCoreReflection_HierarchicalNameAccess_Impl(); + if( xHarryName.is() && xHarryName->hasByHierarchicalName( aClassName ) ) + xClass = xCoreReflection->forName( aClassName ); + if( !xClass.is() ) + return nullptr; + + // Is it really a struct? + TypeClass eType = xClass->getTypeClass(); + if ( ( eType != TypeClass_STRUCT ) && ( eType != TypeClass_EXCEPTION ) ) + return nullptr; + + // create an instance + Any aNewAny; + xClass->createObject( aNewAny ); + // make a SbUnoObject out of it + SbUnoObjectRef pUnoObj = new SbUnoObject( aClassName, aNewAny ); + return pUnoObj; +} + + +// Factory-Class to create Uno-Structs per DIM AS NEW +SbxBaseRef SbUnoFactory::Create( sal_uInt16, sal_uInt32 ) +{ + // Via SbxId nothing works in Uno + return nullptr; +} + +SbxObjectRef SbUnoFactory::CreateObject( const OUString& rClassName ) +{ + return Impl_CreateUnoStruct( rClassName ).get(); +} + + +// Provisional interface for the UNO-Connection +// Deliver a SbxObject, that wrap a Uno-Interface +SbxObjectRef GetSbUnoObject( const OUString& aName, const Any& aUnoObj_ ) +{ + return new SbUnoObject( aName, aUnoObj_ ); +} + +// Force creation of all properties for debugging +void createAllObjectProperties( SbxObject* pObj ) +{ + if( !pObj ) + return; + + SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pObj ); + SbUnoStructRefObject* pUnoStructObj = dynamic_cast<SbUnoStructRefObject*>( pObj ); + if( pUnoObj ) + { + pUnoObj->createAllProperties(); + } + else if ( pUnoStructObj ) + { + pUnoStructObj->createAllProperties(); + } +} + + +void RTL_Impl_CreateUnoStruct( SbxArray& rPar ) +{ + // We need 1 parameter minimum + if (rPar.Count() < 2) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // get the name of the class of the struct + OUString aClassName = rPar.Get(1)->GetOUString(); + + // try to create Struct with the same name + SbUnoObjectRef xUnoObj = Impl_CreateUnoStruct( aClassName ); + if( !xUnoObj.is() ) + { + return; + } + // return the object + SbxVariableRef refVar = rPar.Get(0); + refVar->PutObject( xUnoObj.get() ); +} + +void RTL_Impl_CreateUnoService( SbxArray& rPar ) +{ + // We need 1 Parameter minimum + if (rPar.Count() < 2) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // get the name of the class of the struct + OUString aServiceName = rPar.Get(1)->GetOUString(); + + // search for the service and instantiate it + Reference< XMultiServiceFactory > xFactory( comphelper::getProcessServiceFactory() ); + Reference< XInterface > xInterface; + try + { + xInterface = xFactory->createInstance( aServiceName ); + } + catch( const Exception& ) + { + implHandleAnyException( ::cppu::getCaughtException() ); + } + + SbxVariableRef refVar = rPar.Get(0); + if( xInterface.is() ) + { + // Create a SbUnoObject out of it and return it + SbUnoObjectRef xUnoObj = new SbUnoObject( aServiceName, Any(xInterface) ); + if( xUnoObj->getUnoAny().hasValue() ) + { + // return the object + refVar->PutObject( xUnoObj.get() ); + } + else + { + refVar->PutObject( nullptr ); + } + } + else + { + refVar->PutObject( nullptr ); + } +} + +void RTL_Impl_CreateUnoServiceWithArguments( SbxArray& rPar ) +{ + // We need 2 parameter minimum + if (rPar.Count() < 3) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // get the name of the class of the struct + OUString aServiceName = rPar.Get(1)->GetOUString(); + Any aArgAsAny = sbxToUnoValue(rPar.Get(2), + cppu::UnoType<Sequence<Any>>::get() ); + Sequence< Any > aArgs; + aArgAsAny >>= aArgs; + + // search for the service and instantiate it + Reference< XMultiServiceFactory > xFactory( comphelper::getProcessServiceFactory() ); + Reference< XInterface > xInterface; + try + { + xInterface = xFactory->createInstanceWithArguments( aServiceName, aArgs ); + } + catch( const Exception& ) + { + implHandleAnyException( ::cppu::getCaughtException() ); + } + + SbxVariableRef refVar = rPar.Get(0); + if( xInterface.is() ) + { + // Create a SbUnoObject out of it and return it + SbUnoObjectRef xUnoObj = new SbUnoObject( aServiceName, Any(xInterface) ); + if( xUnoObj->getUnoAny().hasValue() ) + { + // return the object + refVar->PutObject( xUnoObj.get() ); + } + else + { + refVar->PutObject( nullptr ); + } + } + else + { + refVar->PutObject( nullptr ); + } +} + +void RTL_Impl_GetProcessServiceManager( SbxArray& rPar ) +{ + SbxVariableRef refVar = rPar.Get(0); + + // get the global service manager + Reference< XMultiServiceFactory > xFactory( comphelper::getProcessServiceFactory() ); + + // Create a SbUnoObject out of it and return it + SbUnoObjectRef xUnoObj = new SbUnoObject( "ProcessServiceManager", Any(xFactory) ); + refVar->PutObject( xUnoObj.get() ); +} + +void RTL_Impl_HasInterfaces( SbxArray& rPar ) +{ + // We need 2 parameter minimum + sal_uInt32 nParCount = rPar.Count(); + if( nParCount < 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // variable for the return value + SbxVariableRef refVar = rPar.Get(0); + refVar->PutBool( false ); + + // get the Uno-Object + SbxBaseRef pObj = rPar.Get(1)->GetObject(); + auto obj = dynamic_cast<SbUnoObject*>( pObj.get() ); + if( obj == nullptr ) + { + return; + } + Any aAny = obj->getUnoAny(); + auto x = o3tl::tryAccess<Reference<XInterface>>(aAny); + if( !x ) + { + return; + } + + // get CoreReflection + Reference< XIdlReflection > xCoreReflection = getCoreReflection_Impl(); + if( !xCoreReflection.is() ) + { + return; + } + for( sal_uInt32 i = 2 ; i < nParCount ; i++ ) + { + // get the name of the interface of the struct + OUString aIfaceName = rPar.Get(i)->GetOUString(); + + // search for the class + Reference< XIdlClass > xClass = xCoreReflection->forName( aIfaceName ); + if( !xClass.is() ) + { + return; + } + // check if the interface will be supported + OUString aClassName = xClass->getName(); + Type aClassType( xClass->getTypeClass(), aClassName ); + if( !(*x)->queryInterface( aClassType ).hasValue() ) + { + return; + } + } + + // Everything works; then return TRUE + refVar->PutBool( true ); +} + +void RTL_Impl_IsUnoStruct( SbxArray& rPar ) +{ + // We need 1 parameter minimum + if (rPar.Count() < 2) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // variable for the return value + SbxVariableRef refVar = rPar.Get(0); + refVar->PutBool( false ); + + // get the Uno-Object + SbxVariableRef xParam = rPar.Get(1); + if( !xParam->IsObject() ) + { + return; + } + SbxBaseRef pObj = xParam->GetObject(); + auto obj = dynamic_cast<SbUnoObject*>( pObj.get() ); + if( obj == nullptr ) + { + return; + } + Any aAny = obj->getUnoAny(); + TypeClass eType = aAny.getValueType().getTypeClass(); + if( eType == TypeClass_STRUCT ) + { + refVar->PutBool( true ); + } +} + + +void RTL_Impl_EqualUnoObjects( SbxArray& rPar ) +{ + if (rPar.Count() < 3) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // variable for the return value + SbxVariableRef refVar = rPar.Get(0); + refVar->PutBool( false ); + + // get the Uno-Objects + SbxVariableRef xParam1 = rPar.Get(1); + if( !xParam1->IsObject() ) + { + return; + } + SbxBaseRef pObj1 = xParam1->GetObject(); + auto obj1 = dynamic_cast<SbUnoObject*>( pObj1.get() ); + if( obj1 == nullptr ) + { + return; + } + Any aAny1 = obj1->getUnoAny(); + TypeClass eType1 = aAny1.getValueType().getTypeClass(); + if( eType1 != TypeClass_INTERFACE ) + { + return; + } + Reference< XInterface > x1; + aAny1 >>= x1; + + SbxVariableRef xParam2 = rPar.Get(2); + if( !xParam2->IsObject() ) + { + return; + } + SbxBaseRef pObj2 = xParam2->GetObject(); + auto obj2 = dynamic_cast<SbUnoObject*>( pObj2.get() ); + if( obj2 == nullptr ) + { + return; + } + Any aAny2 = obj2->getUnoAny(); + TypeClass eType2 = aAny2.getValueType().getTypeClass(); + if( eType2 != TypeClass_INTERFACE ) + { + return; + } + Reference< XInterface > x2; + aAny2 >>= x2; + + if( x1 == x2 ) + { + refVar->PutBool( true ); + } +} + + +// helper wrapper function to interact with TypeProvider and +// XTypeDescriptionEnumerationAccess. +// if it fails for whatever reason +// returned Reference<> be null e.g. .is() will be false + +static Reference< XTypeDescriptionEnumeration > getTypeDescriptorEnumeration( const OUString& sSearchRoot, + const Sequence< TypeClass >& types, + TypeDescriptionSearchDepth depth ) +{ + Reference< XTypeDescriptionEnumeration > xEnum; + Reference< XTypeDescriptionEnumerationAccess> xTypeEnumAccess( getTypeProvider_Impl(), UNO_QUERY ); + if ( xTypeEnumAccess.is() ) + { + try + { + xEnum = xTypeEnumAccess->createTypeDescriptionEnumeration( + sSearchRoot, types, depth ); + } + catch(const NoSuchTypeNameException& /*nstne*/ ) {} + catch(const InvalidTypeNameException& /*nstne*/ ) {} + } + return xEnum; +} + +VBAConstantHelper& +VBAConstantHelper::instance() +{ + static VBAConstantHelper aHelper; + return aHelper; +} + +void VBAConstantHelper::init() +{ + if ( isInited ) + return; + + Reference< XTypeDescriptionEnumeration > xEnum = getTypeDescriptorEnumeration( "ooo.vba", {TypeClass_CONSTANTS}, TypeDescriptionSearchDepth_INFINITE ); + + if ( !xEnum.is()) + { + return; //NULL; + } + while ( xEnum->hasMoreElements() ) + { + Reference< XConstantsTypeDescription > xConstants( xEnum->nextElement(), UNO_QUERY ); + if ( xConstants.is() ) + { + // store constant group name + OUString sFullName = xConstants->getName(); + sal_Int32 indexLastDot = sFullName.lastIndexOf('.'); + OUString sLeafName( sFullName ); + if ( indexLastDot > -1 ) + { + sLeafName = sFullName.copy( indexLastDot + 1); + } + aConstCache.push_back( sLeafName ); // assume constant group names are unique + const Sequence< Reference< XConstantTypeDescription > > aConsts = xConstants->getConstants(); + for (const auto& ctd : aConsts) + { + // store constant member name + sFullName = ctd->getName(); + indexLastDot = sFullName.lastIndexOf('.'); + sLeafName = sFullName; + if ( indexLastDot > -1 ) + { + sLeafName = sFullName.copy( indexLastDot + 1); + } + aConstHash[ sLeafName.toAsciiLowerCase() ] = ctd->getConstantValue(); + } + } + } + isInited = true; +} + +bool +VBAConstantHelper::isVBAConstantType( std::u16string_view rName ) +{ + init(); + bool bConstant = false; + + for (auto const& elem : aConstCache) + { + if( o3tl::equalsIgnoreAsciiCase(rName, elem) ) + { + bConstant = true; + break; + } + } + return bConstant; +} + +SbxVariable* +VBAConstantHelper::getVBAConstant( const OUString& rName ) +{ + SbxVariable* pConst = nullptr; + init(); + + auto it = aConstHash.find( rName.toAsciiLowerCase() ); + + if ( it != aConstHash.end() ) + { + pConst = new SbxVariable( SbxVARIANT ); + pConst->SetName( rName ); + unoToSbxValue( pConst, it->second ); + } + + return pConst; +} + +// Function to search for a global identifier in the +// UnoScope and to wrap it for Sbx +SbUnoClass* findUnoClass( const OUString& rName ) +{ + // #105550 Check if module exists + SbUnoClass* pUnoClass = nullptr; + + const Reference< XHierarchicalNameAccess >& xTypeAccess = getTypeProvider_Impl(); + if( xTypeAccess->hasByHierarchicalName( rName ) ) + { + Any aRet = xTypeAccess->getByHierarchicalName( rName ); + Reference< XTypeDescription > xTypeDesc; + aRet >>= xTypeDesc; + + if( xTypeDesc.is() ) + { + TypeClass eTypeClass = xTypeDesc->getTypeClass(); + if( eTypeClass == TypeClass_MODULE || eTypeClass == TypeClass_CONSTANTS ) + { + pUnoClass = new SbUnoClass( rName ); + } + } + } + return pUnoClass; +} + +SbxVariable* SbUnoClass::Find( const OUString& rName, SbxClassType ) +{ + SbxVariable* pRes = SbxObject::Find( rName, SbxClassType::Variable ); + + // If nothing were located the submodule isn't known yet + if( !pRes ) + { + // If it is already a class, ask for the field + if( m_xClass.is() ) + { + // Is it a field(?) + Reference< XIdlField > xField = m_xClass->getField( rName ); + if( xField.is() ) + { + try + { + Any aAny = xField->get( {} ); //TODO: does this make sense? + + // Convert to Sbx + pRes = new SbxVariable( SbxVARIANT ); + pRes->SetName( rName ); + unoToSbxValue( pRes, aAny ); + } + catch( const Exception& ) + { + implHandleAnyException( ::cppu::getCaughtException() ); + } + } + } + else + { + // expand fully qualified name + OUString aNewName = GetName() + + "." + + rName; + + // get CoreReflection + Reference< XIdlReflection > xCoreReflection = getCoreReflection_Impl(); + if( xCoreReflection.is() ) + { + // Is it a constant? + Reference< XHierarchicalNameAccess > xHarryName( xCoreReflection, UNO_QUERY ); + if( xHarryName.is() ) + { + try + { + Any aValue = xHarryName->getByHierarchicalName( aNewName ); + TypeClass eType = aValue.getValueType().getTypeClass(); + + // Interface located? Then it is a class + if( eType == TypeClass_INTERFACE ) + { + Reference< XIdlClass > xClass( aValue, UNO_QUERY ); + if( xClass.is() ) + { + pRes = new SbxVariable( SbxVARIANT ); + SbxObjectRef xWrapper = static_cast<SbxObject*>(new SbUnoClass( aNewName, xClass )); + pRes->PutObject( xWrapper.get() ); + } + } + else + { + pRes = new SbxVariable( SbxVARIANT ); + unoToSbxValue( pRes, aValue ); + } + } + catch( const NoSuchElementException& ) + { + } + } + + // Otherwise take it again as class + if( !pRes ) + { + SbUnoClass* pNewClass = findUnoClass( aNewName ); + if( pNewClass ) + { + pRes = new SbxVariable( SbxVARIANT ); + SbxObjectRef xWrapper = static_cast<SbxObject*>(pNewClass); + pRes->PutObject( xWrapper.get() ); + } + } + + // A UNO service? + if( !pRes ) + { + SbUnoService* pUnoService = findUnoService( aNewName ); + if( pUnoService ) + { + pRes = new SbxVariable( SbxVARIANT ); + SbxObjectRef xWrapper = static_cast<SbxObject*>(pUnoService); + pRes->PutObject( xWrapper.get() ); + } + } + + // A UNO singleton? + if( !pRes ) + { + SbUnoSingleton* pUnoSingleton = findUnoSingleton( aNewName ); + if( pUnoSingleton ) + { + pRes = new SbxVariable( SbxVARIANT ); + SbxObjectRef xWrapper = static_cast<SbxObject*>(pUnoSingleton); + pRes->PutObject( xWrapper.get() ); + } + } + } + } + + if( pRes ) + { + pRes->SetName( rName ); + + // Insert variable, so that it could be found later + QuickInsert( pRes ); + + // Take us out as listener at once, + // the values are all constant + if( pRes->IsBroadcaster() ) + EndListening( pRes->GetBroadcaster(), true ); + } + } + return pRes; +} + + +SbUnoService* findUnoService( const OUString& rName ) +{ + SbUnoService* pSbUnoService = nullptr; + + const Reference< XHierarchicalNameAccess >& xTypeAccess = getTypeProvider_Impl(); + if( xTypeAccess->hasByHierarchicalName( rName ) ) + { + Any aRet = xTypeAccess->getByHierarchicalName( rName ); + Reference< XTypeDescription > xTypeDesc; + aRet >>= xTypeDesc; + + if( xTypeDesc.is() ) + { + TypeClass eTypeClass = xTypeDesc->getTypeClass(); + if( eTypeClass == TypeClass_SERVICE ) + { + Reference< XServiceTypeDescription2 > xServiceTypeDesc( xTypeDesc, UNO_QUERY ); + if( xServiceTypeDesc.is() ) + pSbUnoService = new SbUnoService( rName, xServiceTypeDesc ); + } + } + } + return pSbUnoService; +} + +SbxVariable* SbUnoService::Find( const OUString& rName, SbxClassType ) +{ + SbxVariable* pRes = SbxObject::Find( rName, SbxClassType::Method ); + + if( !pRes ) + { + // If it is already a class ask for a field + if( m_bNeedsInit && m_xServiceTypeDesc.is() ) + { + m_bNeedsInit = false; + + Sequence< Reference< XServiceConstructorDescription > > aSCDSeq = m_xServiceTypeDesc->getConstructors(); + const Reference< XServiceConstructorDescription >* pCtorSeq = aSCDSeq.getConstArray(); + int nCtorCount = aSCDSeq.getLength(); + for( int i = 0 ; i < nCtorCount ; ++i ) + { + Reference< XServiceConstructorDescription > xCtor = pCtorSeq[i]; + + OUString aName( xCtor->getName() ); + if( aName.isEmpty() ) + { + if( xCtor->isDefaultConstructor() ) + { + aName = "create"; + } + } + + if( !aName.isEmpty() ) + { + // Create and insert SbUnoServiceCtor + SbxVariableRef xSbCtorRef = new SbUnoServiceCtor( aName, xCtor ); + QuickInsert( xSbCtorRef.get() ); + } + } + pRes = SbxObject::Find( rName, SbxClassType::Method ); + } + } + + return pRes; +} + +void SbUnoService::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + const SbxHint* pHint = dynamic_cast<const SbxHint*>(&rHint); + if( !pHint ) + return; + + SbxVariable* pVar = pHint->GetVar(); + SbxArray* pParams = pVar->GetParameters(); + SbUnoServiceCtor* pUnoCtor = dynamic_cast<SbUnoServiceCtor*>( pVar ); + if( pUnoCtor && pHint->GetId() == SfxHintId::BasicDataWanted ) + { + // Parameter count -1 because of Param0 == this + sal_uInt32 nParamCount = pParams ? (pParams->Count() - 1) : 0; + Sequence<Any> args; + + Reference< XServiceConstructorDescription > xCtor = pUnoCtor->getServiceCtorDesc(); + Sequence< Reference< XParameter > > aParameterSeq = xCtor->getParameters(); + const Reference< XParameter >* pParameterSeq = aParameterSeq.getConstArray(); + sal_uInt32 nUnoParamCount = aParameterSeq.getLength(); + + // Default: Ignore not needed parameters + bool bParameterError = false; + + // Is the last parameter a rest parameter? + bool bRestParameterMode = false; + if( nUnoParamCount > 0 ) + { + Reference< XParameter > xLastParam = pParameterSeq[ nUnoParamCount - 1 ]; + if( xLastParam.is() ) + { + if( xLastParam->isRestParameter() ) + bRestParameterMode = true; + } + } + + // Too many parameters with context as first parameter? + sal_uInt32 nSbxParameterOffset = 1; + sal_uInt32 nParameterOffsetByContext = 0; + Reference < XComponentContext > xFirstParamContext; + if( nParamCount > nUnoParamCount ) + { + // Check if first parameter is a context and use it + // then in createInstanceWithArgumentsAndContext + Any aArg0 = sbxToUnoValue(pParams->Get(nSbxParameterOffset)); + if( (aArg0 >>= xFirstParamContext) && xFirstParamContext.is() ) + nParameterOffsetByContext = 1; + } + + sal_uInt32 nEffectiveParamCount = nParamCount - nParameterOffsetByContext; + sal_uInt32 nAllocParamCount = nEffectiveParamCount; + if( nEffectiveParamCount > nUnoParamCount ) + { + if( !bRestParameterMode ) + { + nEffectiveParamCount = nUnoParamCount; + nAllocParamCount = nUnoParamCount; + } + } + // Not enough parameters? + else if( nUnoParamCount > nEffectiveParamCount ) + { + // RestParameterMode only helps if one (the last) parameter is missing + int nDiff = nUnoParamCount - nEffectiveParamCount; + if( !bRestParameterMode || nDiff > 1 ) + { + bParameterError = true; + StarBASIC::Error( ERRCODE_BASIC_NOT_OPTIONAL ); + } + } + + if( !bParameterError ) + { + bool bOutParams = false; + if( nAllocParamCount > 0 ) + { + args.realloc( nAllocParamCount ); + Any* pAnyArgs = args.getArray(); + for( sal_uInt32 i = 0 ; i < nEffectiveParamCount ; i++ ) + { + sal_uInt32 iSbx = i + nSbxParameterOffset + nParameterOffsetByContext; + + // bRestParameterMode allows nEffectiveParamCount > nUnoParamCount + Reference< XParameter > xParam; + if( i < nUnoParamCount ) + { + xParam = pParameterSeq[i]; + if( !xParam.is() ) + continue; + + Reference< XTypeDescription > xParamTypeDesc = xParam->getType(); + if( !xParamTypeDesc.is() ) + continue; + css::uno::Type aType( xParamTypeDesc->getTypeClass(), xParamTypeDesc->getName() ); + + // sbx parameter needs offset 1 + pAnyArgs[i] = sbxToUnoValue(pParams->Get(iSbx), aType); + + // Check for out parameter if not already done + if( !bOutParams && xParam->isOut() ) + bOutParams = true; + } + else + { + pAnyArgs[i] = sbxToUnoValue(pParams->Get(iSbx)); + } + } + } + + // "Call" ctor using createInstanceWithArgumentsAndContext + Reference < XComponentContext > xContext( + xFirstParamContext.is() + ? xFirstParamContext + : comphelper::getProcessComponentContext() ); + Reference< XMultiComponentFactory > xServiceMgr( xContext->getServiceManager() ); + + Any aRetAny; + OUString aServiceName = GetName(); + Reference < XInterface > xRet; + try + { + xRet = xServiceMgr->createInstanceWithArgumentsAndContext( aServiceName, args, xContext ); + } + catch( const Exception& ) + { + implHandleAnyException( ::cppu::getCaughtException() ); + } + aRetAny <<= xRet; + unoToSbxValue( pVar, aRetAny ); + + // Copy back out parameters? + if( bOutParams ) + { + const Any* pAnyArgs = args.getConstArray(); + + for( sal_uInt32 j = 0 ; j < nUnoParamCount ; j++ ) + { + Reference< XParameter > xParam = pParameterSeq[j]; + if( !xParam.is() ) + continue; + + if( xParam->isOut() ) + unoToSbxValue(pParams->Get(j + 1), pAnyArgs[j]); + } + } + } + } + else + SbxObject::Notify( rBC, rHint ); +} + + +SbUnoServiceCtor::SbUnoServiceCtor( const OUString& aName_, Reference< XServiceConstructorDescription > const & xServiceCtorDesc ) + : SbxMethod( aName_, SbxOBJECT ) + , m_xServiceCtorDesc( xServiceCtorDesc ) +{ +} + +SbUnoServiceCtor::~SbUnoServiceCtor() +{ +} + +SbxInfo* SbUnoServiceCtor::GetInfo() +{ + return nullptr; +} + + +SbUnoSingleton* findUnoSingleton( const OUString& rName ) +{ + SbUnoSingleton* pSbUnoSingleton = nullptr; + + const Reference< XHierarchicalNameAccess >& xTypeAccess = getTypeProvider_Impl(); + if( xTypeAccess->hasByHierarchicalName( rName ) ) + { + Any aRet = xTypeAccess->getByHierarchicalName( rName ); + Reference< XTypeDescription > xTypeDesc; + aRet >>= xTypeDesc; + + if( xTypeDesc.is() ) + { + TypeClass eTypeClass = xTypeDesc->getTypeClass(); + if( eTypeClass == TypeClass_SINGLETON ) + { + Reference< XSingletonTypeDescription > xSingletonTypeDesc( xTypeDesc, UNO_QUERY ); + if( xSingletonTypeDesc.is() ) + pSbUnoSingleton = new SbUnoSingleton( rName ); + } + } + } + return pSbUnoSingleton; +} + +SbUnoSingleton::SbUnoSingleton( const OUString& aName_ ) + : SbxObject( aName_ ) +{ + SbxVariableRef xGetMethodRef = new SbxMethod( "get", SbxOBJECT ); + QuickInsert( xGetMethodRef.get() ); +} + +void SbUnoSingleton::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + const SbxHint* pHint = dynamic_cast<const SbxHint*>(&rHint); + if( pHint ) + { + SbxVariable* pVar = pHint->GetVar(); + SbxArray* pParams = pVar->GetParameters(); + sal_uInt32 nParamCount = pParams ? (pParams->Count() - 1) : 0; + sal_uInt32 nAllowedParamCount = 1; + + Reference < XComponentContext > xContextToUse; + if( nParamCount > 0 ) + { + // Check if first parameter is a context and use it then + Reference < XComponentContext > xFirstParamContext; + Any aArg1 = sbxToUnoValue(pParams->Get(1)); + if( (aArg1 >>= xFirstParamContext) && xFirstParamContext.is() ) + xContextToUse = xFirstParamContext; + } + + if( !xContextToUse.is() ) + { + xContextToUse = comphelper::getProcessComponentContext(); + --nAllowedParamCount; + } + + if( nParamCount > nAllowedParamCount ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + Any aRetAny; + if( xContextToUse.is() ) + { + OUString aSingletonName = "/singletons/" + + GetName(); + Reference < XInterface > xRet; + xContextToUse->getValueByName( aSingletonName ) >>= xRet; + aRetAny <<= xRet; + } + unoToSbxValue( pVar, aRetAny ); + } + else + { + SbxObject::Notify( rBC, rHint ); + } +} + +namespace { + +// Implementation of an EventAttacher-drawn AllListener, which +// solely transmits several events to a general AllListener +class BasicAllListener_Impl : public WeakImplHelper< XAllListener > +{ + void firing_impl(const AllEventObject& Event, Any* pRet); + +public: + SbxObjectRef xSbxObj; + OUString aPrefixName; + + explicit BasicAllListener_Impl( OUString aPrefixName ); + + // Methods of XAllListener + virtual void SAL_CALL firing(const AllEventObject& Event) override; + virtual Any SAL_CALL approveFiring(const AllEventObject& Event) override; + + // Methods of XEventListener + virtual void SAL_CALL disposing(const EventObject& Source) override; +}; + +} + +BasicAllListener_Impl::BasicAllListener_Impl(OUString aPrefixName_) + : aPrefixName(std::move( aPrefixName_ )) +{ +} + +void BasicAllListener_Impl::firing_impl( const AllEventObject& Event, Any* pRet ) +{ + SolarMutexGuard guard; + + if( !xSbxObj.is() ) + return; + + OUString aMethodName = aPrefixName + Event.MethodName; + + SbxVariable * pP = xSbxObj.get(); + while( pP->GetParent() ) + { + pP = pP->GetParent(); + StarBASIC * pLib = dynamic_cast<StarBASIC*>( pP ); + if( pLib ) + { + // Create in a Basic Array + SbxArrayRef xSbxArray = new SbxArray( SbxVARIANT ); + const Any * pArgs = Event.Arguments.getConstArray(); + sal_Int32 nCount = Event.Arguments.getLength(); + for( sal_Int32 i = 0; i < nCount; i++ ) + { + // Convert elements + SbxVariableRef xVar = new SbxVariable( SbxVARIANT ); + unoToSbxValue( xVar.get(), pArgs[i] ); + xSbxArray->Put(xVar.get(), i + 1); + } + + pLib->Call( aMethodName, xSbxArray.get() ); + + // get the return value from the Param-Array, if requested + if( pRet ) + { + SbxVariable* pVar = xSbxArray->Get(0); + if( pVar ) + { + // #95792 Avoid a second call + SbxFlagBits nFlags = pVar->GetFlags(); + pVar->SetFlag( SbxFlagBits::NoBroadcast ); + *pRet = sbxToUnoValueImpl( pVar ); + pVar->SetFlags( nFlags ); + } + } + break; + } + } +} + + +// Methods of Listener +void BasicAllListener_Impl::firing( const AllEventObject& Event ) +{ + firing_impl( Event, nullptr ); +} + +Any BasicAllListener_Impl::approveFiring( const AllEventObject& Event ) +{ + Any aRetAny; + firing_impl( Event, &aRetAny ); + return aRetAny; +} + + +// Methods of XEventListener +void BasicAllListener_Impl ::disposing(const EventObject& ) +{ + SolarMutexGuard guard; + + xSbxObj.clear(); +} + + +// class InvocationToAllListenerMapper +// helper class to map XInvocation to XAllListener (also in project eventattacher!) + +namespace { + +class InvocationToAllListenerMapper : public WeakImplHelper< XInvocation > +{ +public: + InvocationToAllListenerMapper( const Reference< XIdlClass >& ListenerType, + const Reference< XAllListener >& AllListener, Any Helper ); + + // XInvocation + virtual Reference< XIntrospectionAccess > SAL_CALL getIntrospection() override; + virtual Any SAL_CALL invoke(const OUString& FunctionName, const Sequence< Any >& Params, Sequence< sal_Int16 >& OutParamIndex, Sequence< Any >& OutParam) override; + virtual void SAL_CALL setValue(const OUString& PropertyName, const Any& Value) override; + virtual Any SAL_CALL getValue(const OUString& PropertyName) override; + virtual sal_Bool SAL_CALL hasMethod(const OUString& Name) override; + virtual sal_Bool SAL_CALL hasProperty(const OUString& Name) override; + +private: + Reference< XAllListener > m_xAllListener; + Reference< XIdlClass > m_xListenerType; + Any m_Helper; +}; + +} + +// Function to replace AllListenerAdapterService::createAllListerAdapter +static Reference< XInterface > createAllListenerAdapter +( + const Reference< XInvocationAdapterFactory2 >& xInvocationAdapterFactory, + const Reference< XIdlClass >& xListenerType, + const Reference< XAllListener >& xListener, + const Any& Helper +) +{ + Reference< XInterface > xAdapter; + if( xInvocationAdapterFactory.is() && xListenerType.is() && xListener.is() ) + { + Reference< XInvocation > xInvocationToAllListenerMapper = + new InvocationToAllListenerMapper(xListenerType, xListener, Helper); + Type aListenerType( xListenerType->getTypeClass(), xListenerType->getName() ); + xAdapter = xInvocationAdapterFactory->createAdapter( xInvocationToAllListenerMapper, {aListenerType} ); + } + return xAdapter; +} + + +// InvocationToAllListenerMapper +InvocationToAllListenerMapper::InvocationToAllListenerMapper + ( const Reference< XIdlClass >& ListenerType, const Reference< XAllListener >& AllListener, Any Helper ) + : m_xAllListener( AllListener ) + , m_xListenerType( ListenerType ) + , m_Helper(std::move( Helper )) +{ +} + + +Reference< XIntrospectionAccess > SAL_CALL InvocationToAllListenerMapper::getIntrospection() +{ + return Reference< XIntrospectionAccess >(); +} + + +Any SAL_CALL InvocationToAllListenerMapper::invoke(const OUString& FunctionName, const Sequence< Any >& Params, + Sequence< sal_Int16 >&, Sequence< Any >&) +{ + Any aRet; + + // Check if to firing or approveFiring has to be called + Reference< XIdlMethod > xMethod = m_xListenerType->getMethod( FunctionName ); + bool bApproveFiring = false; + if( !xMethod.is() ) + return aRet; + Reference< XIdlClass > xReturnType = xMethod->getReturnType(); + Sequence< Reference< XIdlClass > > aExceptionSeq = xMethod->getExceptionTypes(); + if( ( xReturnType.is() && xReturnType->getTypeClass() != TypeClass_VOID ) || + aExceptionSeq.hasElements() ) + { + bApproveFiring = true; + } + else + { + Sequence< ParamInfo > aParamSeq = xMethod->getParameterInfos(); + sal_uInt32 nParamCount = aParamSeq.getLength(); + if( nParamCount > 1 ) + { + const ParamInfo* pInfo = aParamSeq.getConstArray(); + for( sal_uInt32 i = 0 ; i < nParamCount ; i++ ) + { + if( pInfo[ i ].aMode != ParamMode_IN ) + { + bApproveFiring = true; + break; + } + } + } + } + + AllEventObject aAllEvent; + aAllEvent.Source = static_cast<OWeakObject*>(this); + aAllEvent.Helper = m_Helper; + aAllEvent.ListenerType = Type(m_xListenerType->getTypeClass(), m_xListenerType->getName() ); + aAllEvent.MethodName = FunctionName; + aAllEvent.Arguments = Params; + if( bApproveFiring ) + aRet = m_xAllListener->approveFiring( aAllEvent ); + else + m_xAllListener->firing( aAllEvent ); + return aRet; +} + + +void SAL_CALL InvocationToAllListenerMapper::setValue(const OUString&, const Any&) +{} + + +Any SAL_CALL InvocationToAllListenerMapper::getValue(const OUString&) +{ + return Any(); +} + + +sal_Bool SAL_CALL InvocationToAllListenerMapper::hasMethod(const OUString& Name) +{ + Reference< XIdlMethod > xMethod = m_xListenerType->getMethod( Name ); + return xMethod.is(); +} + + +sal_Bool SAL_CALL InvocationToAllListenerMapper::hasProperty(const OUString& Name) +{ + Reference< XIdlField > xField = m_xListenerType->getField( Name ); + return xField.is(); +} + + +// create Uno-Service +// 1. Parameter == Prefix-Name of the macro +// 2. Parameter == fully qualified name of the listener +void SbRtl_CreateUnoListener(StarBASIC * pBasic, SbxArray & rPar, bool) +{ + // We need 2 parameters + if (rPar.Count() != 3) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // get the name of the class of the struct + OUString aPrefixName = rPar.Get(1)->GetOUString(); + OUString aListenerClassName = rPar.Get(2)->GetOUString(); + + // get the CoreReflection + Reference< XIdlReflection > xCoreReflection = getCoreReflection_Impl(); + if( !xCoreReflection.is() ) + return; + + // get the AllListenerAdapterService + Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() ); + + // search the class + Reference< XIdlClass > xClass = xCoreReflection->forName( aListenerClassName ); + if( !xClass.is() ) + return; + + // From 1999-11-30: get the InvocationAdapterFactory + Reference< XInvocationAdapterFactory2 > xInvocationAdapterFactory = + InvocationAdapterFactory::create( xContext ); + + rtl::Reference<BasicAllListener_Impl> xAllLst = new BasicAllListener_Impl( aPrefixName ); + Any aTmp; + Reference< XInterface > xLst = createAllListenerAdapter( xInvocationAdapterFactory, xClass, xAllLst, aTmp ); + if( !xLst.is() ) + return; + + OUString aClassName = xClass->getName(); + Type aClassType( xClass->getTypeClass(), aClassName ); + aTmp = xLst->queryInterface( aClassType ); + if( !aTmp.hasValue() ) + return; + + SbUnoObject* pUnoObj = new SbUnoObject( aListenerClassName, aTmp ); + xAllLst->xSbxObj = pUnoObj; + xAllLst->xSbxObj->SetParent( pBasic ); + + // #100326 Register listener object to set Parent NULL in Dtor + SbxArrayRef xBasicUnoListeners = pBasic->getUnoListeners(); + xBasicUnoListeners->Insert(pUnoObj, xBasicUnoListeners->Count()); + + // return the object + SbxVariableRef refVar = rPar.Get(0); + refVar->PutObject( xAllLst->xSbxObj.get() ); +} + + +// Represents the DefaultContext property of the ProcessServiceManager +// in the Basic runtime system. +void RTL_Impl_GetDefaultContext( SbxArray& rPar ) +{ + SbxVariableRef refVar = rPar.Get(0); + + Any aContextAny( comphelper::getProcessComponentContext() ); + + SbUnoObjectRef xUnoObj = new SbUnoObject( "DefaultContext", aContextAny ); + refVar->PutObject( xUnoObj.get() ); +} + + +// Creates a Basic wrapper object for a strongly typed Uno value +// 1. parameter: Uno type as full qualified type name, e.g. "byte[]" +void RTL_Impl_CreateUnoValue( SbxArray& rPar ) +{ + // 2 parameters needed + if (rPar.Count() != 3) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // get the name of the class of the struct + OUString aTypeName = rPar.Get(1)->GetOUString(); + SbxVariable* pVal = rPar.Get(2); + + if( aTypeName == "type" ) + { + SbxDataType eBaseType = pVal->SbxValue::GetType(); + OUString aValTypeName; + if( eBaseType == SbxSTRING ) + { + aValTypeName = pVal->GetOUString(); + } + else if( eBaseType == SbxOBJECT ) + { + // XIdlClass? + Reference< XIdlClass > xIdlClass; + + SbxBaseRef pObj = pVal->GetObject(); + if( auto obj = dynamic_cast<SbUnoObject*>( pObj.get() ) ) + { + Any aUnoAny = obj->getUnoAny(); + aUnoAny >>= xIdlClass; + } + + if( xIdlClass.is() ) + { + aValTypeName = xIdlClass->getName(); + } + } + Type aType; + bool bSuccess = implGetTypeByName( aValTypeName, aType ); + if( bSuccess ) + { + Any aTypeAny( aType ); + SbxVariableRef refVar = rPar.Get(0); + SbxObjectRef xUnoAnyObject = new SbUnoAnyObject( aTypeAny ); + refVar->PutObject( xUnoAnyObject.get() ); + } + return; + } + + // Check the type + const Reference< XHierarchicalNameAccess >& xTypeAccess = getTypeProvider_Impl(); + Any aRet; + try + { + aRet = xTypeAccess->getByHierarchicalName( aTypeName ); + } + catch( const NoSuchElementException& e1 ) + { + StarBASIC::Error( ERRCODE_BASIC_EXCEPTION, + implGetExceptionMsg( e1, u"com.sun.star.container.NoSuchElementException" ) ); + return; + } + Reference< XTypeDescription > xTypeDesc; + aRet >>= xTypeDesc; + TypeClass eTypeClass = xTypeDesc->getTypeClass(); + Type aDestType( eTypeClass, aTypeName ); + + + // Preconvert value + Any aVal = sbxToUnoValueImpl( pVal ); + Any aConvertedVal = convertAny( aVal, aDestType ); + + SbxVariableRef refVar = rPar.Get(0); + SbxObjectRef xUnoAnyObject = new SbUnoAnyObject( aConvertedVal ); + refVar->PutObject( xUnoAnyObject.get() ); +} + +namespace { + +class ModuleInvocationProxy : public WeakImplHelper< XInvocation, XComponent > +{ + std::mutex m_aMutex; + OUString m_aPrefix; + SbxObjectRef m_xScopeObj; + bool m_bProxyIsClassModuleObject; + + ::comphelper::OInterfaceContainerHelper4<XEventListener> m_aListeners; + +public: + ModuleInvocationProxy( std::u16string_view aPrefix, SbxObjectRef const & xScopeObj ); + + // XInvocation + virtual Reference< XIntrospectionAccess > SAL_CALL getIntrospection() override; + virtual void SAL_CALL setValue( const OUString& rProperty, const Any& rValue ) override; + virtual Any SAL_CALL getValue( const OUString& rProperty ) override; + virtual sal_Bool SAL_CALL hasMethod( const OUString& rName ) override; + virtual sal_Bool SAL_CALL hasProperty( const OUString& rProp ) override; + + virtual Any SAL_CALL invoke( const OUString& rFunction, + const Sequence< Any >& rParams, + Sequence< sal_Int16 >& rOutParamIndex, + Sequence< Any >& rOutParam ) override; + + // XComponent + virtual void SAL_CALL dispose() override; + virtual void SAL_CALL addEventListener( const Reference< XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const Reference< XEventListener >& aListener ) override; +}; + +} + +ModuleInvocationProxy::ModuleInvocationProxy( std::u16string_view aPrefix, SbxObjectRef const & xScopeObj ) + : m_aPrefix( OUString::Concat(aPrefix) + "_" ) + , m_xScopeObj( xScopeObj ) +{ + m_bProxyIsClassModuleObject = xScopeObj.is() && dynamic_cast<const SbClassModuleObject*>( xScopeObj.get() ) != nullptr; +} + +Reference< XIntrospectionAccess > SAL_CALL ModuleInvocationProxy::getIntrospection() +{ + return Reference< XIntrospectionAccess >(); +} + +void SAL_CALL ModuleInvocationProxy::setValue(const OUString& rProperty, const Any& rValue) +{ + if( !m_bProxyIsClassModuleObject ) + throw UnknownPropertyException(); + + SolarMutexGuard guard; + + OUString aPropertyFunctionName = "Property Set " + + m_aPrefix + + rProperty; + + SbxVariable* p = m_xScopeObj->Find( aPropertyFunctionName, SbxClassType::Method ); + SbMethod* pMeth = dynamic_cast<SbMethod*>( p ); + if( pMeth == nullptr ) + { + // TODO: Check vba behavior concerning missing function + //StarBASIC::Error( ERRCODE_BASIC_NO_METHOD, aFunctionName ); + throw UnknownPropertyException(aPropertyFunctionName); + } + + // Setup parameter + SbxArrayRef xArray = new SbxArray; + SbxVariableRef xVar = new SbxVariable( SbxVARIANT ); + unoToSbxValue( xVar.get(), rValue ); + xArray->Put(xVar.get(), 1); + + // Call property method + SbxVariableRef xValue = new SbxVariable; + pMeth->SetParameters( xArray.get() ); + pMeth->Call( xValue.get() ); + pMeth->SetParameters( nullptr ); + + // TODO: OutParameter? + + +} + +Any SAL_CALL ModuleInvocationProxy::getValue(const OUString& rProperty) +{ + if( !m_bProxyIsClassModuleObject ) + { + throw UnknownPropertyException(); + } + SolarMutexGuard guard; + + OUString aPropertyFunctionName = "Property Get " + + m_aPrefix + + rProperty; + + SbxVariable* p = m_xScopeObj->Find( aPropertyFunctionName, SbxClassType::Method ); + SbMethod* pMeth = dynamic_cast<SbMethod*>( p ); + if( pMeth == nullptr ) + { + // TODO: Check vba behavior concerning missing function + //StarBASIC::Error( ERRCODE_BASIC_NO_METHOD, aFunctionName ); + throw UnknownPropertyException(aPropertyFunctionName); + } + + // Call method + SbxVariableRef xValue = new SbxVariable; + pMeth->Call( xValue.get() ); + Any aRet = sbxToUnoValue( xValue.get() ); + return aRet; +} + +sal_Bool SAL_CALL ModuleInvocationProxy::hasMethod( const OUString& ) +{ + return false; +} + +sal_Bool SAL_CALL ModuleInvocationProxy::hasProperty( const OUString& ) +{ + return false; +} + +Any SAL_CALL ModuleInvocationProxy::invoke( const OUString& rFunction, + const Sequence< Any >& rParams, + Sequence< sal_Int16 >&, + Sequence< Any >& ) +{ + SolarMutexGuard guard; + + Any aRet; + SbxObjectRef xScopeObj = m_xScopeObj; + if( !xScopeObj.is() ) + { + return aRet; + } + OUString aFunctionName = m_aPrefix + + rFunction; + + bool bSetRescheduleBack = false; + bool bOldReschedule = true; + SbiInstance* pInst = GetSbData()->pInst; + if( pInst && pInst->IsCompatibility() ) + { + bOldReschedule = pInst->IsReschedule(); + if ( bOldReschedule ) + { + pInst->EnableReschedule( false ); + bSetRescheduleBack = true; + } + } + + SbxVariable* p = xScopeObj->Find( aFunctionName, SbxClassType::Method ); + SbMethod* pMeth = dynamic_cast<SbMethod*>( p ); + if( pMeth == nullptr ) + { + // TODO: Check vba behavior concerning missing function + //StarBASIC::Error( ERRCODE_BASIC_NO_METHOD, aFunctionName ); + return aRet; + } + + // Setup parameters + SbxArrayRef xArray; + sal_Int32 nParamCount = rParams.getLength(); + if( nParamCount ) + { + xArray = new SbxArray; + const Any *pArgs = rParams.getConstArray(); + for( sal_Int32 i = 0 ; i < nParamCount ; i++ ) + { + SbxVariableRef xVar = new SbxVariable( SbxVARIANT ); + unoToSbxValue( xVar.get(), pArgs[i] ); + xArray->Put(xVar.get(), sal::static_int_cast<sal_uInt16>(i + 1)); + } + } + + // Call method + SbxVariableRef xValue = new SbxVariable; + if( xArray.is() ) + pMeth->SetParameters( xArray.get() ); + pMeth->Call( xValue.get() ); + aRet = sbxToUnoValue( xValue.get() ); + pMeth->SetParameters( nullptr ); + + if( bSetRescheduleBack ) + pInst->EnableReschedule( bOldReschedule ); + + // TODO: OutParameter? + + return aRet; +} + +void SAL_CALL ModuleInvocationProxy::dispose() +{ + std::unique_lock aGuard( m_aMutex ); + + EventObject aEvent( static_cast<XComponent*>(this) ); + m_aListeners.disposeAndClear( aGuard, aEvent ); + + m_xScopeObj = nullptr; +} + +void SAL_CALL ModuleInvocationProxy::addEventListener( const Reference< XEventListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + m_aListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL ModuleInvocationProxy::removeEventListener( const Reference< XEventListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + m_aListeners.removeInterface( aGuard, xListener ); +} + + +Reference< XInterface > createComListener( const Any& aControlAny, const OUString& aVBAType, + std::u16string_view aPrefix, + const SbxObjectRef& xScopeObj ) +{ + Reference< XInterface > xRet; + + Reference< XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + Reference< XMultiComponentFactory > xServiceMgr( xContext->getServiceManager() ); + + Reference< XInvocation > xProxy = new ModuleInvocationProxy( aPrefix, xScopeObj ); + + Sequence<Any> args{ aControlAny, Any(aVBAType), Any(xProxy) }; + + try + { + xRet = xServiceMgr->createInstanceWithArgumentsAndContext( + "com.sun.star.custom.UnoComListener", + args, xContext ); + } + catch( const Exception& ) + { + implHandleAnyException( ::cppu::getCaughtException() ); + } + + return xRet; +} + +typedef std::vector< WeakReference< XComponent > > ComponentRefVector; + +namespace { + +struct StarBasicDisposeItem +{ + StarBASIC* m_pBasic; + SbxArrayRef m_pRegisteredVariables; + ComponentRefVector m_vComImplementsObjects; + + explicit StarBasicDisposeItem( StarBASIC* pBasic ) + : m_pBasic( pBasic ) + , m_pRegisteredVariables(new SbxArray()) + { + } +}; + +} + +typedef std::vector< StarBasicDisposeItem* > DisposeItemVector; + +static DisposeItemVector GaDisposeItemVector; + +static DisposeItemVector::iterator lcl_findItemForBasic( StarBASIC const * pBasic ) +{ + return std::find_if(GaDisposeItemVector.begin(), GaDisposeItemVector.end(), + [&pBasic](StarBasicDisposeItem* pItem) { return pItem->m_pBasic == pBasic; }); +} + +static StarBasicDisposeItem* lcl_getOrCreateItemForBasic( StarBASIC* pBasic ) +{ + DisposeItemVector::iterator it = lcl_findItemForBasic( pBasic ); + StarBasicDisposeItem* pItem = (it != GaDisposeItemVector.end()) ? *it : nullptr; + if( pItem == nullptr ) + { + pItem = new StarBasicDisposeItem( pBasic ); + GaDisposeItemVector.push_back( pItem ); + } + return pItem; +} + +void registerComponentToBeDisposedForBasic + ( const Reference< XComponent >& xComponent, StarBASIC* pBasic ) +{ + StarBasicDisposeItem* pItem = lcl_getOrCreateItemForBasic( pBasic ); + pItem->m_vComImplementsObjects.emplace_back(xComponent ); +} + +void registerComListenerVariableForBasic( SbxVariable* pVar, StarBASIC* pBasic ) +{ + StarBasicDisposeItem* pItem = lcl_getOrCreateItemForBasic( pBasic ); + SbxArray* pArray = pItem->m_pRegisteredVariables.get(); + pArray->Put(pVar, pArray->Count()); +} + +void disposeComVariablesForBasic( StarBASIC const * pBasic ) +{ + DisposeItemVector::iterator it = lcl_findItemForBasic( pBasic ); + if( it == GaDisposeItemVector.end() ) + return; + + StarBasicDisposeItem* pItem = *it; + + SbxArray* pArray = pItem->m_pRegisteredVariables.get(); + sal_uInt32 nCount = pArray->Count(); + for( sal_uInt32 i = 0 ; i < nCount ; ++i ) + { + SbxVariable* pVar = pArray->Get(i); + pVar->ClearComListener(); + } + + ComponentRefVector& rv = pItem->m_vComImplementsObjects; + for (auto const& elem : rv) + { + Reference< XComponent > xComponent( elem.get(), UNO_QUERY ); + if (xComponent.is()) + xComponent->dispose(); + } + + delete pItem; + GaDisposeItemVector.erase( it ); +} + + +// Handle module implements mechanism for OLE types +bool SbModule::createCOMWrapperForIface( Any& o_rRetAny, SbClassModuleObject* pProxyClassModuleObject ) +{ + // For now: Take first interface that allows to instantiate COM wrapper + // TODO: Check if support for multiple interfaces is needed + + Reference< XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + Reference< XMultiComponentFactory > xServiceMgr( xContext->getServiceManager() ); + Reference< XSingleServiceFactory > xComImplementsFactory + ( + xServiceMgr->createInstanceWithContext( "com.sun.star.custom.ComImplementsFactory", xContext ), + UNO_QUERY + ); + if( !xComImplementsFactory.is() ) + return false; + + bool bSuccess = false; + + SbxArray* pModIfaces = pClassData->mxIfaces.get(); + sal_uInt32 nCount = pModIfaces->Count(); + for( sal_uInt32 i = 0 ; i < nCount ; ++i ) + { + SbxVariable* pVar = pModIfaces->Get(i); + const OUString& aIfaceName = pVar->GetName(); + + if( !aIfaceName.isEmpty() ) + { + OUString aPureIfaceName = aIfaceName; + sal_Int32 indexLastDot = aIfaceName.lastIndexOf('.'); + if ( indexLastDot > -1 ) + { + aPureIfaceName = aIfaceName.copy( indexLastDot + 1 ); + } + Reference< XInvocation > xProxy = new ModuleInvocationProxy( aPureIfaceName, pProxyClassModuleObject ); + + Sequence<Any> args{ Any(aIfaceName), Any(xProxy) }; + + Reference< XInterface > xRet; + try + { + xRet = xComImplementsFactory->createInstanceWithArguments( args ); + bSuccess = true; + } + catch( const Exception& ) + { + implHandleAnyException( ::cppu::getCaughtException() ); + } + + if( bSuccess ) + { + Reference< XComponent > xComponent( xProxy, UNO_QUERY ); + if( xComponent.is() ) + { + StarBASIC* pParentBasic = nullptr; + SbxObject* pCurObject = this; + do + { + SbxObject* pObjParent = pCurObject->GetParent(); + pParentBasic = dynamic_cast<StarBASIC*>( pObjParent ); + pCurObject = pObjParent; + } + while( pParentBasic == nullptr && pCurObject != nullptr ); + + assert( pParentBasic != nullptr ); + registerComponentToBeDisposedForBasic( xComponent, pParentBasic ); + } + + o_rRetAny <<= xRet; + break; + } + } + } + + return bSuccess; +} + + +// Due to an incorrect behavior IE returns an object instead of a string +// in some scenarios. Calling toString at the object may correct this. +// Helper function used in sbxvalue.cxx +bool handleToStringForCOMObjects( SbxObject* pObj, SbxValue* pVal ) +{ + bool bSuccess = false; + + if( auto pUnoObj = dynamic_cast<SbUnoObject*>( pObj) ) + { + // Only for native COM objects + if( pUnoObj->isNativeCOMObject() ) + { + SbxVariableRef pMeth = pObj->Find( "toString", SbxClassType::Method ); + if ( pMeth.is() ) + { + SbxValues aRes; + pMeth->Get( aRes ); + pVal->Put( aRes ); + bSuccess = true; + } + } + } + return bSuccess; +} + +Any StructRefInfo::getValue() +{ + Any aRet; + uno_any_destruct( + &aRet, reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + typelib_TypeDescription * pTD = nullptr; + maType.getDescription(&pTD); + uno_any_construct( + &aRet, getInst(), pTD, + reinterpret_cast< uno_AcquireFunc >(cpp_acquire) ); + typelib_typedescription_release(pTD); + return aRet; +} + +void StructRefInfo::setValue( const Any& rValue ) +{ + bool bSuccess = uno_type_assignData( getInst(), + maType.getTypeLibType(), + const_cast<void*>(rValue.getValue()), + rValue.getValueTypeRef(), + reinterpret_cast< uno_QueryInterfaceFunc >(cpp_queryInterface), + reinterpret_cast< uno_AcquireFunc >(cpp_acquire), + reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + OSL_ENSURE(bSuccess, + "StructRefInfo::setValue: ooops... the value could not be assigned!"); +} + +OUString StructRefInfo::getTypeName() const +{ + return maType.getTypeName(); +} + +void* StructRefInfo::getInst() +{ + return const_cast<char *>(static_cast<char const *>(maAny.getValue()) + mnPos); +} + +TypeClass StructRefInfo::getTypeClass() const +{ + return maType.getTypeClass(); +} + +SbUnoStructRefObject::SbUnoStructRefObject( const OUString& aName_, StructRefInfo aMemberInfo ) : SbxObject( aName_ ), maMemberInfo(std::move( aMemberInfo )), mbMemberCacheInit( false ) +{ + SetClassName( maMemberInfo.getTypeName() ); +} + +SbUnoStructRefObject::~SbUnoStructRefObject() +{ +} + +void SbUnoStructRefObject::initMemberCache() +{ + if ( mbMemberCacheInit ) + return; + typelib_TypeDescription * pTD = nullptr; + maMemberInfo.getType().getDescription(&pTD); + for ( typelib_CompoundTypeDescription * pCompTypeDescr = reinterpret_cast<typelib_CompoundTypeDescription *>(pTD); + pCompTypeDescr; + pCompTypeDescr = pCompTypeDescr->pBaseTypeDescription ) + { + typelib_TypeDescriptionReference ** ppTypeRefs = pCompTypeDescr->ppTypeRefs; + rtl_uString ** ppNames = pCompTypeDescr->ppMemberNames; + sal_Int32 * pMemberOffsets = pCompTypeDescr->pMemberOffsets; + for ( sal_Int32 nPos = pCompTypeDescr->nMembers; nPos--; ) + { + OUString aName( ppNames[nPos] ); + maFields[ aName ] = std::make_unique<StructRefInfo>( maMemberInfo.getRootAnyRef(), ppTypeRefs[nPos], maMemberInfo.getPos() + pMemberOffsets[nPos] ); + } + } + typelib_typedescription_release(pTD); + mbMemberCacheInit = true; +} + +SbxVariable* SbUnoStructRefObject::Find( const OUString& rName, SbxClassType t ) +{ + SbxVariable* pRes = SbxObject::Find( rName, t ); + if ( !pRes ) + { + if ( !mbMemberCacheInit ) + initMemberCache(); + StructFieldInfo::iterator it = maFields.find( rName ); + if ( it != maFields.end() ) + { + SbxDataType eSbxType; + eSbxType = unoToSbxType( it->second->getTypeClass() ); + SbxDataType eRealSbxType = eSbxType; + Property aProp; + aProp.Name = rName; + aProp.Type = css::uno::Type( it->second->getTypeClass(), it->second->getTypeName() ); + const bool bIsStruct = aProp.Type.getTypeClass() == css::uno::TypeClass_STRUCT; + SbUnoProperty* pProp = new SbUnoProperty( rName, eSbxType, eRealSbxType, std::move(aProp), 0, false, bIsStruct ); + SbxVariableRef xVarRef = pProp; + QuickInsert( xVarRef.get() ); + pRes = xVarRef.get(); + } + } + + if( !pRes ) + { + if( rName.equalsIgnoreAsciiCase(ID_DBG_SUPPORTEDINTERFACES) || + rName.equalsIgnoreAsciiCase(ID_DBG_PROPERTIES) || + rName.equalsIgnoreAsciiCase(ID_DBG_METHODS) ) + { + // Create + implCreateDbgProperties(); + + // Now they have to be found regular + pRes = SbxObject::Find( rName, SbxClassType::DontCare ); + } + } + + return pRes; +} + +// help method to create the dbg_-Properties +void SbUnoStructRefObject::implCreateDbgProperties() +{ + Property aProp; + + // Id == -1: display the implemented interfaces corresponding the ClassProvider + SbxVariableRef xVarRef = new SbUnoProperty( ID_DBG_SUPPORTEDINTERFACES, SbxSTRING, SbxSTRING, aProp, -1, false, false ); + QuickInsert( xVarRef.get() ); + + // Id == -2: output the properties + xVarRef = new SbUnoProperty( ID_DBG_PROPERTIES, SbxSTRING, SbxSTRING, aProp, -2, false, false ); + QuickInsert( xVarRef.get() ); + + // Id == -3: output the Methods + xVarRef = new SbUnoProperty( ID_DBG_METHODS, SbxSTRING, SbxSTRING, aProp, -3, false, false ); + QuickInsert( xVarRef.get() ); +} + +void SbUnoStructRefObject::implCreateAll() +{ + // throw away all existing methods and properties + pMethods = new SbxArray; + pProps = new SbxArray; + + if (!mbMemberCacheInit) + initMemberCache(); + + for (auto const& field : maFields) + { + const OUString& rName = field.first; + SbxDataType eSbxType; + eSbxType = unoToSbxType( field.second->getTypeClass() ); + SbxDataType eRealSbxType = eSbxType; + Property aProp; + aProp.Name = rName; + aProp.Type = css::uno::Type( field.second->getTypeClass(), field.second->getTypeName() ); + const bool bIsStruct = aProp.Type.getTypeClass() == css::uno::TypeClass_STRUCT; + SbUnoProperty* pProp = new SbUnoProperty( rName, eSbxType, eRealSbxType, std::move(aProp), 0, false, bIsStruct ); + SbxVariableRef xVarRef = pProp; + QuickInsert( xVarRef.get() ); + } + + // Create Dbg_-Properties + implCreateDbgProperties(); +} + + // output the value +Any SbUnoStructRefObject::getUnoAny() +{ + return maMemberInfo.getValue(); +} + +OUString SbUnoStructRefObject::Impl_DumpProperties() +{ + OUStringBuffer aRet; + aRet.append("Properties of object "); + aRet.append( getDbgObjectName() ); + + sal_uInt32 nPropCount = pProps->Count(); + sal_uInt32 nPropsPerLine = 1 + nPropCount / 30; + for( sal_uInt32 i = 0; i < nPropCount; i++ ) + { + SbxVariable* pVar = pProps->Get(i); + if( pVar ) + { + OUStringBuffer aPropStr; + if( (i % nPropsPerLine) == 0 ) + { + aPropStr.append( "\n" ); + } + // output the type and name + // Is it in Uno a sequence? + SbxDataType eType = pVar->GetFullType(); + + const OUString& aName( pVar->GetName() ); + StructFieldInfo::iterator it = maFields.find( aName ); + + if ( it != maFields.end() ) + { + const StructRefInfo& rPropInfo = *it->second; + + if( eType == SbxOBJECT ) + { + if( rPropInfo.getTypeClass() == TypeClass_SEQUENCE ) + { + eType = SbxDataType( SbxOBJECT | SbxARRAY ); + } + } + } + aPropStr.append( Dbg_SbxDataType2String( eType ) ); + + aPropStr.append( " " ); + aPropStr.append( pVar->GetName() ); + + if( i == nPropCount - 1 ) + { + aPropStr.append( "\n" ); + } + else + { + aPropStr.append( "; " ); + } + aRet.append( aPropStr ); + } + } + return aRet.makeStringAndClear(); +} + +void SbUnoStructRefObject::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + if ( !mbMemberCacheInit ) + initMemberCache(); + const SbxHint* pHint = dynamic_cast<const SbxHint*>(&rHint); + if( !pHint ) + return; + + SbxVariable* pVar = pHint->GetVar(); + SbUnoProperty* pProp = dynamic_cast<SbUnoProperty*>( pVar ); + if( pProp ) + { + StructFieldInfo::iterator it = maFields.find( pProp->GetName() ); + // handle get/set of members of struct + if( pHint->GetId() == SfxHintId::BasicDataWanted ) + { + // Test-Properties + sal_Int32 nId = pProp->nId; + if( nId < 0 ) + { + // Id == -1: Display implemented interfaces according the ClassProvider + if( nId == -1 ) // Property ID_DBG_SUPPORTEDINTERFACES" + { + OUString aRet = OUString::Concat( ID_DBG_SUPPORTEDINTERFACES ) + + " not available.\n(TypeClass is not TypeClass_INTERFACE)\n"; + + pVar->PutString( aRet ); + } + // Id == -2: output properties + else if( nId == -2 ) // Property ID_DBG_PROPERTIES + { + // by now all properties must be established + implCreateAll(); + OUString aRetStr = Impl_DumpProperties(); + pVar->PutString( aRetStr ); + } + // Id == -3: output the methods + else if( nId == -3 ) // Property ID_DBG_METHODS + { + // by now all properties must be established + implCreateAll(); + OUString aRet = "Methods of object " + + getDbgObjectName() + + "\nNo methods found\n"; + pVar->PutString( aRet ); + } + return; + } + + if ( it != maFields.end() ) + { + Any aRetAny = it->second->getValue(); + unoToSbxValue( pVar, aRetAny ); + } + else + StarBASIC::Error( ERRCODE_BASIC_PROPERTY_NOT_FOUND ); + } + else if( pHint->GetId() == SfxHintId::BasicDataChanged ) + { + if ( it != maFields.end() ) + { + // take over the value from Uno to Sbx + Any aAnyValue = sbxToUnoValue( pVar, pProp->aUnoProp.Type, &pProp->aUnoProp ); + it->second->setValue( aAnyValue ); + } + else + StarBASIC::Error( ERRCODE_BASIC_PROPERTY_NOT_FOUND ); + } + } + else + SbxObject::Notify( rBC, rHint ); +} + +StructRefInfo SbUnoStructRefObject::getStructMember( const OUString& rMemberName ) +{ + if (!mbMemberCacheInit) + { + initMemberCache(); + } + StructFieldInfo::iterator it = maFields.find( rMemberName ); + + css::uno::Type aFoundType; + sal_Int32 nFoundPos = -1; + + if ( it != maFields.end() ) + { + aFoundType = it->second->getType(); + nFoundPos = it->second->getPos(); + } + StructRefInfo aRet( maMemberInfo.getRootAnyRef(), aFoundType, nFoundPos ); + return aRet; +} + +OUString SbUnoStructRefObject::getDbgObjectName() const +{ + OUString aName = GetClassName(); + if( aName.isEmpty() ) + { + aName += "Unknown"; + } + OUStringBuffer aRet; + if( aName.getLength() > 20 ) + { + aRet.append( "\n" ); + } + aRet.append( "\"" ); + aRet.append( aName ); + aRet.append( "\":" ); + return aRet.makeStringAndClear(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |