diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /basic/source/classes | |
parent | Initial commit. (diff) | |
download | libreoffice-cb75148ebd0135178ff46f89a30139c44f8d2040.tar.xz libreoffice-cb75148ebd0135178ff46f89a30139c44f8d2040.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'basic/source/classes')
-rw-r--r-- | basic/source/classes/codecompletecache.cxx | 197 | ||||
-rw-r--r-- | basic/source/classes/errobject.cxx | 219 | ||||
-rw-r--r-- | basic/source/classes/eventatt.cxx | 548 | ||||
-rw-r--r-- | basic/source/classes/global.cxx | 45 | ||||
-rw-r--r-- | basic/source/classes/image.cxx | 718 | ||||
-rw-r--r-- | basic/source/classes/propacc.cxx | 179 | ||||
-rw-r--r-- | basic/source/classes/sb.cxx | 2225 | ||||
-rw-r--r-- | basic/source/classes/sbintern.cxx | 52 | ||||
-rw-r--r-- | basic/source/classes/sbunoobj.cxx | 4926 | ||||
-rw-r--r-- | basic/source/classes/sbxmod.cxx | 2676 |
10 files changed, 11785 insertions, 0 deletions
diff --git a/basic/source/classes/codecompletecache.cxx b/basic/source/classes/codecompletecache.cxx new file mode 100644 index 000000000..8f03797c3 --- /dev/null +++ b/basic/source/classes/codecompletecache.cxx @@ -0,0 +1,197 @@ +/* -*- 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 <basic/codecompletecache.hxx> +#include <iostream> +#include <officecfg/Office/BasicIDE.hxx> +#include <officecfg/Office/Common.hxx> + +namespace +{ +CodeCompleteOptions& theCodeCompleteOptions() +{ + static CodeCompleteOptions SINGLETON; + return SINGLETON; +} +} + +CodeCompleteOptions::CodeCompleteOptions() +{ + bIsAutoCorrectOn = officecfg::Office::BasicIDE::Autocomplete::AutoCorrect::get(); + bIsAutoCloseParenthesisOn = officecfg::Office::BasicIDE::Autocomplete::AutocloseParenthesis::get(); + bIsAutoCloseQuotesOn = officecfg::Office::BasicIDE::Autocomplete::AutocloseDoubleQuotes::get(); + bIsProcedureAutoCompleteOn = officecfg::Office::BasicIDE::Autocomplete::AutocloseProc::get(); + bIsCodeCompleteOn = officecfg::Office::BasicIDE::Autocomplete::CodeComplete::get(); + bExtendedTypeDeclarationOn = officecfg::Office::BasicIDE::Autocomplete::UseExtended::get(); +} + +bool CodeCompleteOptions::IsCodeCompleteOn() +{ + return officecfg::Office::Common::Misc::ExperimentalMode::get() && theCodeCompleteOptions().bIsCodeCompleteOn; +} + +void CodeCompleteOptions::SetCodeCompleteOn( bool b ) +{ + theCodeCompleteOptions().bIsCodeCompleteOn = b; +} + +bool CodeCompleteOptions::IsExtendedTypeDeclaration() +{ + return officecfg::Office::Common::Misc::ExperimentalMode::get() && theCodeCompleteOptions().bExtendedTypeDeclarationOn; +} + +void CodeCompleteOptions::SetExtendedTypeDeclaration( bool b ) +{ + theCodeCompleteOptions().bExtendedTypeDeclarationOn = b; +} + +bool CodeCompleteOptions::IsProcedureAutoCompleteOn() +{ + return officecfg::Office::Common::Misc::ExperimentalMode::get() && theCodeCompleteOptions().bIsProcedureAutoCompleteOn; +} + +void CodeCompleteOptions::SetProcedureAutoCompleteOn( bool b ) +{ + theCodeCompleteOptions().bIsProcedureAutoCompleteOn = b; +} + +bool CodeCompleteOptions::IsAutoCloseQuotesOn() +{ + return officecfg::Office::Common::Misc::ExperimentalMode::get() && theCodeCompleteOptions().bIsAutoCloseQuotesOn; +} + +void CodeCompleteOptions::SetAutoCloseQuotesOn( bool b ) +{ + theCodeCompleteOptions().bIsAutoCloseQuotesOn = b; +} + +bool CodeCompleteOptions::IsAutoCloseParenthesisOn() +{ + return officecfg::Office::Common::Misc::ExperimentalMode::get() && theCodeCompleteOptions().bIsAutoCloseParenthesisOn; +} + +void CodeCompleteOptions::SetAutoCloseParenthesisOn( bool b ) +{ + theCodeCompleteOptions().bIsAutoCloseParenthesisOn = b; +} + +bool CodeCompleteOptions::IsAutoCorrectOn() +{ + return officecfg::Office::Common::Misc::ExperimentalMode::get() && theCodeCompleteOptions().bIsAutoCorrectOn; +} + +void CodeCompleteOptions::SetAutoCorrectOn( bool b ) +{ + theCodeCompleteOptions().bIsAutoCorrectOn = b; +} + +std::ostream& operator<< (std::ostream& aStream, const CodeCompleteDataCache& aCache) +{ + aStream << "Global variables" << std::endl; + for (auto const& globalVar : aCache.aGlobalVars) + { + aStream << globalVar.first << "," << globalVar.second << std::endl; + } + aStream << "Local variables" << std::endl; + for (auto const& varScope : aCache.aVarScopes) + { + aStream << varScope.first << std::endl; + CodeCompleteVarTypes aVarTypes = varScope.second; + for (auto const& varType : aVarTypes) + { + aStream << "\t" << varType.first << "," << varType.second << std::endl; + } + } + aStream << "-----------------" << std::endl; + return aStream; +} + +void CodeCompleteDataCache::Clear() +{ + aVarScopes.clear(); + aGlobalVars.clear(); +} + +void CodeCompleteDataCache::InsertGlobalVar( const OUString& sVarName, const OUString& sVarType ) +{ + aGlobalVars.emplace( sVarName, sVarType ); +} + +void CodeCompleteDataCache::InsertLocalVar( const OUString& sProcName, const OUString& sVarName, const OUString& sVarType ) +{ + CodeCompleteVarScopes::const_iterator aIt = aVarScopes.find( sProcName ); + if( aIt == aVarScopes.end() ) //new procedure + { + CodeCompleteVarTypes aTypes; + aTypes.emplace( sVarName, sVarType ); + aVarScopes.emplace( sProcName, aTypes ); + } + else + { + CodeCompleteVarTypes aTypes = aVarScopes[ sProcName ]; + aTypes.emplace( sVarName, sVarType ); + aVarScopes[ sProcName ] = aTypes; + } +} + +OUString CodeCompleteDataCache::GetVarType( std::u16string_view sVarName ) const +{ + for (auto const& varScope : aVarScopes) + { + CodeCompleteVarTypes aTypes = varScope.second; + for (auto const& elem : aTypes) + { + if( elem.first.equalsIgnoreAsciiCase( sVarName ) ) + { + return elem.second; + } + } + } + //not a local, search global scope + for (auto const& globalVar : aGlobalVars) + { + if( globalVar.first.equalsIgnoreAsciiCase( sVarName ) ) + return globalVar.second; + } + return OUString(); //not found +} + +OUString CodeCompleteDataCache::GetCorrectCaseVarName( std::u16string_view sVarName, std::u16string_view sActProcName ) const +{ + for (auto const& varScope : aVarScopes) + { + CodeCompleteVarTypes aTypes = varScope.second; + for (auto const& elem : aTypes) + { + if( elem.first.equalsIgnoreAsciiCase( sVarName ) && varScope.first.equalsIgnoreAsciiCase( sActProcName ) ) + { + return elem.first; + } + } + } + // search global scope + for (auto const& globalVar : aGlobalVars) + { + if( globalVar.first.equalsIgnoreAsciiCase( sVarName ) ) + return globalVar.first; + } + return OUString(); //not found +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/classes/errobject.cxx b/basic/source/classes/errobject.cxx new file mode 100644 index 000000000..85423101b --- /dev/null +++ b/basic/source/classes/errobject.cxx @@ -0,0 +1,219 @@ +/* -*- 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 <mutex> + +#include <errobject.hxx> +#include <sbxbase.hxx> + +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/script/XDefaultProperty.hpp> +#include <sbintern.hxx> +#include <runtime.hxx> + +using namespace ::com::sun::star; +using namespace ::ooo; + +class ErrObject : public ::cppu::WeakImplHelper< vba::XErrObject, + script::XDefaultProperty > +{ + OUString m_sHelpFile; + OUString m_sSource; + OUString m_sDescription; + sal_Int32 m_nNumber; + sal_Int32 m_nHelpContext; + +public: + ErrObject(); + + // Attributes + virtual ::sal_Int32 SAL_CALL getNumber() override; + virtual void SAL_CALL setNumber( ::sal_Int32 _number ) override; + virtual ::sal_Int32 SAL_CALL getHelpContext() override; + virtual void SAL_CALL setHelpContext( ::sal_Int32 _helpcontext ) override; + virtual OUString SAL_CALL getHelpFile() override; + virtual void SAL_CALL setHelpFile( const OUString& _helpfile ) override; + virtual OUString SAL_CALL getDescription() override; + virtual void SAL_CALL setDescription( const OUString& _description ) override; + virtual OUString SAL_CALL getSource() override; + virtual void SAL_CALL setSource( const OUString& _source ) override; + + // Methods + virtual void SAL_CALL Clear( ) override; + virtual void SAL_CALL Raise( const uno::Any& Number, const uno::Any& Source, const uno::Any& Description, const uno::Any& HelpFile, const uno::Any& HelpContext ) override; + // XDefaultProperty + virtual OUString SAL_CALL getDefaultPropertyName( ) override; + + // Helper method + /// @throws css::uno::RuntimeException + void setData( const uno::Any& Number, const uno::Any& Source, const uno::Any& Description, + const uno::Any& HelpFile, const uno::Any& HelpContext ); +}; + +ErrObject::ErrObject() : m_nNumber(0), m_nHelpContext(0) +{ +} + +sal_Int32 SAL_CALL +ErrObject::getNumber() +{ + return m_nNumber; +} + +void SAL_CALL +ErrObject::setNumber( ::sal_Int32 _number ) +{ + GetSbData()->pInst->setErrorVB( _number ); + OUString _description = GetSbData()->pInst->GetErrorMsg(); + setData( uno::Any( _number ), uno::Any(), uno::Any( _description ), uno::Any(), uno::Any() ); +} + +::sal_Int32 SAL_CALL +ErrObject::getHelpContext() +{ + return m_nHelpContext; +} +void SAL_CALL +ErrObject::setHelpContext( ::sal_Int32 _helpcontext ) +{ + m_nHelpContext = _helpcontext; +} + +OUString SAL_CALL +ErrObject::getHelpFile() +{ + return m_sHelpFile; +} + +void SAL_CALL +ErrObject::setHelpFile( const OUString& _helpfile ) +{ + m_sHelpFile = _helpfile; +} + +OUString SAL_CALL +ErrObject::getDescription() +{ + return m_sDescription; +} + +void SAL_CALL +ErrObject::setDescription( const OUString& _description ) +{ + m_sDescription = _description; +} + +OUString SAL_CALL +ErrObject::getSource() +{ + return m_sSource; +} + +void SAL_CALL +ErrObject::setSource( const OUString& _source ) +{ + m_sSource = _source; +} + +// Methods +void SAL_CALL +ErrObject::Clear( ) +{ + m_sHelpFile.clear(); + m_sSource = m_sHelpFile; + m_sDescription = m_sSource; + m_nNumber = 0; + m_nHelpContext = 0; +} + +void SAL_CALL +ErrObject::Raise( const uno::Any& Number, const uno::Any& Source, const uno::Any& Description, const uno::Any& HelpFile, const uno::Any& HelpContext ) +{ + setData( Number, Source, Description, HelpFile, HelpContext ); + if ( m_nNumber ) + GetSbData()->pInst->ErrorVB( m_nNumber, m_sDescription ); +} + +// XDefaultProperty +OUString SAL_CALL +ErrObject::getDefaultPropertyName( ) +{ + return "Number"; +} + +void ErrObject::setData( const uno::Any& Number, const uno::Any& Source, const uno::Any& Description, const uno::Any& HelpFile, const uno::Any& HelpContext ) +{ + if ( !Number.hasValue() ) + throw uno::RuntimeException("Missing Required Parameter" ); + Number >>= m_nNumber; + Description >>= m_sDescription; + Source >>= m_sSource; + HelpFile >>= m_sHelpFile; + HelpContext >>= m_nHelpContext; +} + +// SbxErrObject +SbxErrObject::SbxErrObject( const OUString& rName, const uno::Any& rUnoObj ) + : SbUnoObject( rName, rUnoObj ) + , m_pErrObject( nullptr ) +{ + rUnoObj >>= m_xErr; + if ( m_xErr.is() ) + { + SetDfltProperty( uno::Reference< script::XDefaultProperty >( m_xErr, uno::UNO_QUERY_THROW )->getDefaultPropertyName() ) ; + m_pErrObject = static_cast< ErrObject* >( m_xErr.get() ); + } +} + +SbxErrObject::~SbxErrObject() +{ +} + +uno::Reference< vba::XErrObject > const & +SbxErrObject::getUnoErrObject() +{ + SbxErrObject* pGlobErr = static_cast< SbxErrObject* >( getErrObject().get() ); + return pGlobErr->m_xErr; +} + +SbxVariableRef const & +SbxErrObject::getErrObject() +{ + SbxVariableRef& rGlobErr = GetSbxData_Impl().m_aGlobErr; + { + static std::mutex aMutex; + std::scoped_lock aGuard(aMutex); + if (!rGlobErr) + rGlobErr = new SbxErrObject("Err", + uno::Any(uno::Reference<vba::XErrObject>(new ErrObject()))); + } + return rGlobErr; +} + +void SbxErrObject::setNumberAndDescription( ::sal_Int32 _number, const OUString& _description ) +{ + if( m_pErrObject != nullptr ) + { + m_pErrObject->setData( uno::Any( _number ), uno::Any(), uno::Any( _description ), uno::Any(), uno::Any() ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/classes/eventatt.cxx b/basic/source/classes/eventatt.cxx new file mode 100644 index 000000000..23185e0ee --- /dev/null +++ b/basic/source/classes/eventatt.cxx @@ -0,0 +1,548 @@ +/* -*- 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 <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> + +#include <com/sun/star/awt/XControlContainer.hpp> +#include <com/sun/star/awt/XControl.hpp> +#include <com/sun/star/awt/DialogProvider.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/script/XLibraryContainer.hpp> +#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp> +#include <com/sun/star/script/provider/XScriptProviderSupplier.hpp> +#include <com/sun/star/script/provider/XScriptProvider.hpp> +#include <com/sun/star/io/XInputStreamProvider.hpp> + +#include <basic/basicmanagerrepository.hxx> +#include <basic/basmgr.hxx> + +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/svapp.hxx> +#include <sbunoobj.hxx> +#include <basic/sberrors.hxx> +#include <basic/sbstar.hxx> +#include <basic/sbmeth.hxx> +#include <basic/sbuno.hxx> +#include <runtime.hxx> +#include <sbintern.hxx> +#include <eventatt.hxx> + +#include <cppuhelper/implbase.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::reflection; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::io; +using namespace ::cppu; + +namespace { + +void SFURL_firing_impl( const ScriptEvent& aScriptEvent, Any* pRet, const Reference< frame::XModel >& xModel ) +{ + SAL_INFO("basic", "Processing script url " << aScriptEvent.ScriptCode); + try + { + Reference< provider::XScriptProvider > xScriptProvider; + if ( xModel.is() ) + { + Reference< provider::XScriptProviderSupplier > xSupplier( xModel, UNO_QUERY ); + OSL_ENSURE( xSupplier.is(), "SFURL_firing_impl: failed to get script provider supplier" ); + if ( xSupplier.is() ) + xScriptProvider.set( xSupplier->getScriptProvider() ); + } + else + { + Reference< XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + Reference< provider::XScriptProviderFactory > xFactory = + provider::theMasterScriptProviderFactory::get( xContext ); + + Any aCtx; + aCtx <<= OUString("user"); + xScriptProvider = xFactory->createScriptProvider( aCtx ); + } + + if ( !xScriptProvider.is() ) + { + SAL_INFO("basic", "Failed to create msp"); + return; + } + Sequence< Any > inArgs( 0 ); + Sequence< Any > outArgs( 0 ); + Sequence< sal_Int16 > outIndex; + + // get Arguments for script + inArgs = aScriptEvent.Arguments; + + Reference< provider::XScript > xScript = xScriptProvider->getScript( aScriptEvent.ScriptCode ); + + if ( !xScript.is() ) + { + SAL_INFO("basic", "Failed to Failed to obtain XScript"); + return; + } + + Any result = xScript->invoke( inArgs, outIndex, outArgs ); + if ( pRet ) + { + *pRet = result; + } + } + catch ( const RuntimeException& ) + { + TOOLS_INFO_EXCEPTION("basic", "" ); + } + catch ( const Exception& ) + { + TOOLS_INFO_EXCEPTION("basic", "" ); + } + +} + + +class BasicScriptListener_Impl : public WeakImplHelper< XScriptListener > +{ + StarBASICRef maBasicRef; + Reference< frame::XModel > m_xModel; + + void firing_impl(const ScriptEvent& aScriptEvent, Any* pRet); + +public: + BasicScriptListener_Impl( StarBASIC* pBasic, const Reference< frame::XModel >& xModel ) + : maBasicRef( pBasic ), m_xModel( xModel ) {} + + // Methods of XAllListener + virtual void SAL_CALL firing(const ScriptEvent& aScriptEvent) override; + virtual Any SAL_CALL approveFiring(const ScriptEvent& aScriptEvent) override; + + // Methods of XEventListener + virtual void SAL_CALL disposing(const EventObject& Source) override; +}; + +// Methods XAllListener +void BasicScriptListener_Impl::firing( const ScriptEvent& aScriptEvent ) +{ + SolarMutexGuard g; + + firing_impl( aScriptEvent, nullptr ); +} + +Any BasicScriptListener_Impl::approveFiring( const ScriptEvent& aScriptEvent ) +{ + SolarMutexGuard g; + + Any aRetAny; + firing_impl( aScriptEvent, &aRetAny ); + return aRetAny; +} + +// Methods XEventListener +void BasicScriptListener_Impl::disposing(const EventObject& ) +{ + // TODO: ??? + //SolarMutexGuard aGuard; + //xSbxObj.Clear(); +} + + +void BasicScriptListener_Impl::firing_impl( const ScriptEvent& aScriptEvent, Any* pRet ) +{ + if( aScriptEvent.ScriptType == "StarBasic" ) + { + // Full qualified name? + OUString aMacro( aScriptEvent.ScriptCode ); + OUString aLibName; + OUString aLocation; + if( comphelper::string::getTokenCount(aMacro, '.') == 3 ) + { + sal_Int32 nLast = 0; + std::u16string_view aFullLibName = o3tl::getToken(aMacro, 0, '.', nLast ); + + size_t nIndex = aFullLibName.find( ':' ); + if (nIndex != std::u16string_view::npos) + { + aLocation = aFullLibName.substr( 0, nIndex ); + aLibName = aFullLibName.substr( nIndex + 1 ); + } + + aMacro = aMacro.copy( nLast ); + } + + SbxObject* p = maBasicRef.get(); + SbxObject* pParent = p->GetParent(); + SbxObject* pParentParent = pParent ? pParent->GetParent() : nullptr; + + StarBASICRef xAppStandardBasic; + StarBASICRef xDocStandardBasic; + if( pParentParent ) + { + // Own basic must be document library + xAppStandardBasic = static_cast<StarBASIC*>(pParentParent); + xDocStandardBasic = static_cast<StarBASIC*>(pParent); + } + else if( pParent ) + { + OUString aName = p->GetName(); + if( aName == "Standard" ) + { + // Own basic is doc standard lib + xDocStandardBasic = static_cast<StarBASIC*>(p); + } + xAppStandardBasic = static_cast<StarBASIC*>(pParent); + } + else + { + xAppStandardBasic = static_cast<StarBASIC*>(p); + } + + bool bSearchLib = true; + StarBASICRef xLibSearchBasic; + if( aLocation == "application" ) + { + xLibSearchBasic = xAppStandardBasic; + } + else if( aLocation == "document" ) + { + xLibSearchBasic = xDocStandardBasic; + } + else + { + bSearchLib = false; + } + SbxVariable* pMethVar = nullptr; + // Be still tolerant and make default search if no search basic exists + if( bSearchLib && xLibSearchBasic.is() ) + { + sal_Int32 nCount = xLibSearchBasic->GetObjects()->Count(); + for( sal_Int32 nObj = -1; nObj < nCount ; nObj++ ) + { + StarBASIC* pBasic; + if( nObj == -1 ) + { + pBasic = xLibSearchBasic.get(); + } + else + { + SbxVariable* pVar = xLibSearchBasic->GetObjects()->Get(nObj); + pBasic = dynamic_cast<StarBASIC*>( pVar ); + } + if( pBasic ) + { + OUString aName = pBasic->GetName(); + if( aName == aLibName ) + { + // Search only in the lib, not automatically in application basic + SbxFlagBits nFlags = pBasic->GetFlags(); + pBasic->ResetFlag( SbxFlagBits::GlobalSearch ); + pMethVar = pBasic->Find( aMacro, SbxClassType::DontCare ); + pBasic->SetFlags( nFlags ); + break; + } + } + } + } + + // Default: Be tolerant and search everywhere + if( (!pMethVar || dynamic_cast<const SbMethod*>( pMethVar) == nullptr) && maBasicRef.is() ) + { + pMethVar = maBasicRef->FindQualified( aMacro, SbxClassType::DontCare ); + } + SbMethod* pMeth = dynamic_cast<SbMethod*>( pMethVar ); + if( !pMeth ) + { + return; + } + // Setup parameters + SbxArrayRef xArray; + sal_Int32 nCnt = aScriptEvent.Arguments.getLength(); + if( nCnt ) + { + xArray = new SbxArray; + const Any *pArgs = aScriptEvent.Arguments.getConstArray(); + for( sal_Int32 i = 0; i < nCnt; i++ ) + { + SbxVariableRef xVar = new SbxVariable( SbxVARIANT ); + unoToSbxValue( xVar.get(), pArgs[i] ); + xArray->Put(xVar.get(), sal::static_int_cast<sal_uInt32>(i + 1)); + } + } + + // Call method + SbxVariableRef xValue = pRet ? new SbxVariable : nullptr; + if( xArray.is() ) + { + pMeth->SetParameters( xArray.get() ); + } + pMeth->Call( xValue.get() ); + if( pRet ) + { + *pRet = sbxToUnoValue( xValue.get() ); + } + pMeth->SetParameters( nullptr ); + } + else // scripting framework script + { + //callBasic via scripting framework + SFURL_firing_impl( aScriptEvent, pRet, m_xModel ); + } +} + +css::uno::Reference< css::container::XNameContainer > implFindDialogLibForDialog( const Any& rDlgAny, SbxObject* pBasic ) +{ + css::uno::Reference< css::container::XNameContainer > aRetDlgLib; + + SbxVariable* pDlgLibContVar = pBasic->Find("DialogLibraries", SbxClassType::Object); + if( auto pDlgLibContUnoObj = dynamic_cast<SbUnoObject*>( pDlgLibContVar) ) + { + Any aDlgLibContAny = pDlgLibContUnoObj->getUnoAny(); + + Reference< XLibraryContainer > xDlgLibContNameAccess( aDlgLibContAny, UNO_QUERY ); + OSL_ENSURE( xDlgLibContNameAccess.is(), "implFindDialogLibForDialog: no lib container for the given dialog!" ); + if( xDlgLibContNameAccess.is() ) + { + Sequence< OUString > aLibNames = xDlgLibContNameAccess->getElementNames(); + const OUString* pLibNames = aLibNames.getConstArray(); + sal_Int32 nLibNameCount = aLibNames.getLength(); + + for( sal_Int32 iLib = 0 ; iLib < nLibNameCount ; iLib++ ) + { + if ( !xDlgLibContNameAccess->isLibraryLoaded( pLibNames[ iLib ] ) ) + // if the library isn't loaded, then the dialog cannot originate from this lib + continue; + + Any aDlgLibAny = xDlgLibContNameAccess->getByName( pLibNames[ iLib ] ); + + Reference< XNameContainer > xDlgLibNameCont( aDlgLibAny, UNO_QUERY ); + OSL_ENSURE( xDlgLibNameCont.is(), "implFindDialogLibForDialog: invalid dialog lib!" ); + if( xDlgLibNameCont.is() ) + { + Sequence< OUString > aDlgNames = xDlgLibNameCont->getElementNames(); + const OUString* pDlgNames = aDlgNames.getConstArray(); + sal_Int32 nDlgNameCount = aDlgNames.getLength(); + + for( sal_Int32 iDlg = 0 ; iDlg < nDlgNameCount ; iDlg++ ) + { + Any aDlgAny = xDlgLibNameCont->getByName( pDlgNames[ iDlg ] ); + if( aDlgAny == rDlgAny ) + { + aRetDlgLib = xDlgLibNameCont; + break; + } + } + } + } + } + } + + return aRetDlgLib; +} + +css::uno::Reference< css::container::XNameContainer > implFindDialogLibForDialogBasic( const Any& aAnyISP, SbxObject* pBasic, StarBASIC*& pFoundBasic ) +{ + css::uno::Reference< css::container::XNameContainer > aDlgLib; + // Find dialog library for dialog, direct access is not possible here + StarBASIC* pStartedBasic = static_cast<StarBASIC*>(pBasic); + SbxObject* pParentBasic = pStartedBasic ? pStartedBasic->GetParent() : nullptr; + SbxObject* pParentParentBasic = pParentBasic ? pParentBasic->GetParent() : nullptr; + + SbxObject* pSearchBasic1 = nullptr; + SbxObject* pSearchBasic2 = nullptr; + if( pParentParentBasic ) + { + pSearchBasic1 = pParentBasic; + pSearchBasic2 = pParentParentBasic; + } + else + { + pSearchBasic1 = pStartedBasic; + pSearchBasic2 = pParentBasic; + } + if( pSearchBasic1 ) + { + aDlgLib = implFindDialogLibForDialog( aAnyISP, pSearchBasic1 ); + + if ( aDlgLib.is() ) + pFoundBasic = static_cast<StarBASIC*>(pSearchBasic1); + + else if( pSearchBasic2 ) + { + aDlgLib = implFindDialogLibForDialog( aAnyISP, pSearchBasic2 ); + if ( aDlgLib.is() ) + pFoundBasic = static_cast<StarBASIC*>(pSearchBasic2); + } + } + return aDlgLib; +} + +} + +void RTL_Impl_CreateUnoDialog( SbxArray& rPar ) +{ + Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() ); + + // We need at least 1 parameter + if (rPar.Count() < 2) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // Get dialog + SbxBaseRef pObj = rPar.Get(1)->GetObject(); + SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>(pObj.get()); + if( !pUnoObj ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + Any aAnyISP = pUnoObj->getUnoAny(); + TypeClass eType = aAnyISP.getValueType().getTypeClass(); + + if( eType != TypeClass_INTERFACE ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // Create new uno dialog + Reference< XNameContainer > xDialogModel( xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.awt.UnoControlDialogModel", xContext), UNO_QUERY ); + if( !xDialogModel.is() ) + { + return; + } + Reference< XInputStreamProvider > xISP; + aAnyISP >>= xISP; + if( !xISP.is() ) + { + return; + } + + // Import the DialogModel + Reference< XInputStream > xInput( xISP->createInputStream() ); + + // i83963 Force decoration + uno::Reference< beans::XPropertySet > xDlgModPropSet( xDialogModel, uno::UNO_QUERY ); + if( xDlgModPropSet.is() ) + { + try + { + bool bDecoration = true; + OUString aDecorationPropName("Decoration"); + Any aDecorationAny = xDlgModPropSet->getPropertyValue( aDecorationPropName ); + aDecorationAny >>= bDecoration; + if( !bDecoration ) + { + xDlgModPropSet->setPropertyValue( aDecorationPropName, Any( true ) ); + xDlgModPropSet->setPropertyValue( "Title", Any( OUString() ) ); + } + } + catch(const UnknownPropertyException& ) + {} + } + + css::uno::Reference< css::container::XNameContainer > aDlgLib; + bool bDocDialog = false; + StarBASIC* pFoundBasic = nullptr; + SAL_INFO("basic", "About to try get a hold of ThisComponent"); + Reference< frame::XModel > xModel = StarBASIC::GetModelFromBasic( GetSbData()->pInst->GetBasic() ) ; + aDlgLib = implFindDialogLibForDialogBasic( aAnyISP, GetSbData()->pInst->GetBasic(), pFoundBasic ); + // If we found the dialog then it belongs to the Search basic + if ( !pFoundBasic ) + { + Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( xContext ); + Reference< container::XEnumeration > xModels; + Reference< container::XEnumerationAccess > xComponents = xDesktop->getComponents(); + if ( xComponents.is() ) + { + xModels = xComponents->createEnumeration(); + } + if ( xModels.is() ) + { + while ( xModels->hasMoreElements() ) + { + Reference< frame::XModel > xNextModel( xModels->nextElement(), UNO_QUERY ); + if ( xNextModel.is() ) + { + BasicManager* pMgr = basic::BasicManagerRepository::getDocumentBasicManager( xNextModel ); + if ( pMgr ) + { + aDlgLib = implFindDialogLibForDialogBasic( aAnyISP, pMgr->GetLib(0), pFoundBasic ); + } + if ( aDlgLib.is() ) + { + bDocDialog = true; + xModel = xNextModel; + break; + } + } + } + } + } + if ( pFoundBasic ) + { + bDocDialog = pFoundBasic->IsDocBasic(); + } + Reference< XScriptListener > xScriptListener = new BasicScriptListener_Impl( GetSbData()->pInst->GetBasic(), xModel ); + + // Create a "living" Dialog + Reference< XControl > xCntrl; + try + { + Reference< XDialogProvider > xDlgProv; + if( bDocDialog ) + xDlgProv = css::awt::DialogProvider::createWithModelAndScripting( xContext, xModel, xInput, aDlgLib, xScriptListener ); + else + xDlgProv = css::awt::DialogProvider::createWithModelAndScripting( xContext, uno::Reference< frame::XModel >(), xInput, aDlgLib, xScriptListener ); + + xCntrl.set( xDlgProv->createDialog(OUString() ), UNO_QUERY_THROW ); + // Add dialog model to dispose vector + Reference< XComponent > xDlgComponent( xCntrl->getModel(), UNO_QUERY ); + GetSbData()->pInst->getComponentVector().push_back( xDlgComponent ); + // need ThisComponent from calling script + } + // preserve existing bad behaviour, it's possible... but probably + // illegal to open 2 dialogs ( they ARE modal ) when this happens, sometimes + // create dialog fails. So, in this case let's not throw, just leave basic + // detect the unset object. + catch(const uno::Exception& ) + { + } + + // Return dialog + Any aRetVal; + aRetVal <<= xCntrl; + SbxVariableRef refVar = rPar.Get(0); + unoToSbxValue( refVar.get(), aRetVal ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/classes/global.cxx b/basic/source/classes/global.cxx new file mode 100644 index 000000000..d2e3622b4 --- /dev/null +++ b/basic/source/classes/global.cxx @@ -0,0 +1,45 @@ +/* -*- 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/. + */ + +#include <comphelper/processfactory.hxx> +#include <i18nlangtag/lang.h> +#include <i18nutil/transliteration.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +#include <global.hxx> + +namespace +{ + class lclTransliterationWrapper + { + private: + utl::TransliterationWrapper m_aTransliteration; + public: + lclTransliterationWrapper() + : m_aTransliteration( + comphelper::getProcessComponentContext(), + TransliterationFlags::IGNORE_CASE ) + { + const LanguageType eOfficeLanguage = Application::GetSettings().GetLanguageTag().getLanguageType(); + m_aTransliteration.loadModuleIfNeeded( eOfficeLanguage ); + } + utl::TransliterationWrapper& getTransliteration() { return m_aTransliteration; } + }; + +} + +utl::TransliterationWrapper& SbGlobal::GetTransliteration() +{ + static lclTransliterationWrapper theTransliterationWrapper; + return theTransliterationWrapper.getTransliteration(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/classes/image.cxx b/basic/source/classes/image.cxx new file mode 100644 index 000000000..e5f9ac3f5 --- /dev/null +++ b/basic/source/classes/image.cxx @@ -0,0 +1,718 @@ +/* -*- 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 <tools/stream.hxx> +#include <tools/tenccvt.hxx> +#include <osl/thread.h> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> +#include <basic/sbx.hxx> +#include <sb.hxx> +#include <sbxprop.hxx> +#include <string.h> +#include <image.hxx> +#include <codegen.hxx> +#include <memory> +#include <string_view> + +SbiImage::SbiImage() + : bError(false) + , nFlags(SbiImageFlags::NONE) + , nStringSize(0) + , nDimBase(0) + , eCharSet(osl_getThreadTextEncoding()) + , nStringIdx(0) + , nStringOff(0) + , bInit(false) + , bFirstInit(true) +{ +} + +SbiImage::~SbiImage() +{ +} + +void SbiImage::Clear() +{ + mvStringOffsets.clear(); + pStrings.reset(); + aCode.clear(); + aLegacyPCode.clear(); + nFlags = SbiImageFlags::NONE; + nStringSize= 0; + eCharSet = osl_getThreadTextEncoding(); + nDimBase = 0; + bError = false; +} + +// Open Record +static sal_uInt64 SbiOpenRecord( SvStream& r, FileOffset nSignature, sal_uInt16 nElem ) +{ + sal_uInt64 nPos = r.Tell(); + r.WriteUInt16( static_cast<sal_uInt16>( nSignature ) ) + .WriteInt32( 0 ).WriteUInt16( nElem ); + return nPos; +} + +// Close Record +static void SbiCloseRecord( SvStream& r, sal_uInt64 nOff ) +{ + sal_uInt64 nPos = r.Tell(); + r.Seek( nOff + 2 ); + r.WriteInt32(nPos - nOff - 8 ); + r.Seek( nPos ); +} + +constexpr sal_uInt32 nUnicodeDataMagicNumber = 0x556E6920; // "Uni " BE + +static bool GetToUnicodePoolData(SvStream& r, sal_uInt64 nLen, sal_uInt64 nNext) +{ + const auto nPos = r.Tell(); + // Check space for legacy data, magic number and Unicode data + bool bResult = nPos + nLen + sizeof(sal_uInt32) + nLen * sizeof(sal_Unicode) <= nNext; + if (bResult) + { + r.SeekRel(nLen); // Skip legacy data + sal_uInt32 nMagic = 0; + r.ReadUInt32(nMagic); + if (nMagic != nUnicodeDataMagicNumber) + { + r.Seek(nPos); // return + bResult = false; + } + } + return bResult; +} + +bool SbiImage::Load( SvStream& r, sal_uInt32& nVersion ) +{ + + sal_uInt16 nSign, nCount; + sal_uInt32 nLen; + + Clear(); + // Read Master-Record + r.ReadUInt16( nSign ).ReadUInt32( nLen ).ReadUInt16( nCount ); + sal_uInt64 nLast = r.Tell() + nLen; + bool bBadVer = false; + if( nSign == static_cast<sal_uInt16>( FileOffset::Module ) ) + { + sal_uInt32 nCharSet; // System charset + sal_uInt32 lDimBase; + sal_uInt16 nTmpFlags; + sal_uInt16 nReserved1; + sal_uInt32 nReserved2; + sal_uInt32 nReserved3; + r.ReadUInt32( nVersion ).ReadUInt32( nCharSet ).ReadUInt32( lDimBase ) + .ReadUInt16( nTmpFlags ).ReadUInt16( nReserved1 ).ReadUInt32( nReserved2 ).ReadUInt32( nReserved3 ); + nFlags = static_cast<SbiImageFlags>(nTmpFlags); + eCharSet = nCharSet; + eCharSet = GetSOLoadTextEncoding( eCharSet ); + bBadVer = ( nVersion > B_CURVERSION ); + nDimBase = static_cast<sal_uInt16>(lDimBase); + } + + bool bLegacy = ( nVersion < B_EXT_IMG_VERSION ); + + sal_uInt64 nNext; + while( ( nNext = r.Tell() ) < nLast ) + { + + r.ReadUInt16( nSign ).ReadUInt32( nLen ).ReadUInt16( nCount ); + nNext += nLen + 8; + if( r.GetError() == ERRCODE_NONE ) + { + switch( static_cast<FileOffset>( nSign ) ) + { + case FileOffset::Name: + aName = r.ReadUniOrByteString(eCharSet); + break; + case FileOffset::Comment: + aComment = r.ReadUniOrByteString(eCharSet ); + break; + case FileOffset::Source: + { + aOUSource = r.ReadUniOrByteString(eCharSet); + break; + } + case FileOffset::ExtSource: + { + //assuming an empty string with just the lead 32bit/16bit len indicator + const size_t nMinStringSize = (eCharSet == RTL_TEXTENCODING_UNICODE) ? 4 : 2; + const sal_uInt64 nMaxStrings = r.remainingSize() / nMinStringSize; + if (nCount > nMaxStrings) + { + SAL_WARN("basic", "Parsing error: " << nMaxStrings << + " max possible entries, but " << nCount << " claimed, truncating"); + nCount = nMaxStrings; + } + for( sal_uInt16 j = 0; j < nCount; ++j) + { + aOUSource += r.ReadUniOrByteString(eCharSet); + } + break; + } + case FileOffset::PCode: + if( bBadVer ) break; + aCode.resize(nLen); + r.ReadBytes(aCode.data(), aCode.size()); + if ( bLegacy ) + { + aLegacyPCode = std::move(aCode); + + PCodeBuffConvertor<sal_uInt16, sal_uInt32> aLegacyToNew(aLegacyPCode.data(), + aLegacyPCode.size()); + aLegacyToNew.convert(); + aCode = aLegacyToNew.GetBuffer(); + // we don't release the legacy buffer + // right now, that's because the module + // needs it to fix up the method + // nStart members. When that is done + // the module can release the buffer + // or it can wait until this routine + // is called again or when this class // destructs all of which will trigger + // release of the buffer. + } + break; + case FileOffset::Publics: + case FileOffset::PoolDir: + case FileOffset::SymPool: + case FileOffset::LineRanges: + break; + case FileOffset::StringPool: + { + // the data layout is: nCount of 32-bit offsets into both legacy 1-byte char stream + // and resulting char buffer (1:1 correspondence assumed; 16 of 32 bits used); + // 32-bit length N of following 1-byte char stream (16 bits used); N bytes of 1-byte + // char stream; then optional magic number and stream of N sal_Unicode characters. + + if( bBadVer ) break; + //assuming an empty string with just the lead 32bit len indicator + const sal_uInt64 nMinStringSize = 4; + const sal_uInt64 nMaxStrings = r.remainingSize() / nMinStringSize; + if (nCount > nMaxStrings) + { + SAL_WARN("basic", "Parsing error: " << nMaxStrings << + " max possible entries, but " << nCount << " claimed, truncating"); + nCount = nMaxStrings; + } + MakeStrings( nCount ); + for (size_t i = 0; i < mvStringOffsets.size() && r.good(); i++) + { + sal_uInt32 nOff; + r.ReadUInt32( nOff ); + mvStringOffsets[ i ] = static_cast<sal_uInt16>(nOff); + } + r.ReadUInt32( nLen ); + if (r.good()) + { + pStrings.reset(new sal_Unicode[ nLen ]); + nStringSize = static_cast<sal_uInt16>(nLen); + + if (GetToUnicodePoolData(r, nLen, nNext)) + { + OUString s = read_uInt16s_ToOUString(r, nLen); + memcpy(pStrings.get(), s.getStr(), s.getLength() * sizeof(sal_Unicode)); + } + else + { + std::unique_ptr<char[]> pByteStrings(new char[nLen]); + r.ReadBytes(pByteStrings.get(), nLen); + for (size_t j = 0; j < mvStringOffsets.size(); j++) + { + sal_uInt16 nOff2 = static_cast<sal_uInt16>(mvStringOffsets[j]); + OUString aStr(pByteStrings.get() + nOff2, strlen(pByteStrings.get() + nOff2), eCharSet); + memcpy(pStrings.get() + nOff2, aStr.getStr(), (aStr.getLength() + 1) * sizeof(sal_Unicode)); + } + } + } + break; + } + case FileOffset::UserTypes: + { + //assuming an empty string with just the lead 32bit/16bit len indicator + const size_t nMinStringSize = (eCharSet == RTL_TEXTENCODING_UNICODE) ? 4 : 2; + const sal_uInt64 nMinRecordSize = nMinStringSize + sizeof(sal_Int16); + const sal_uInt64 nMaxRecords = r.remainingSize() / nMinRecordSize; + if (nCount > nMaxRecords) + { + SAL_WARN("basic", "Parsing error: " << nMaxRecords << + " max possible entries, but " << nCount << " claimed, truncating"); + nCount = nMaxRecords; + } + + // User defined types; ref.: SbiParser::DefType + for (sal_uInt16 i = 0; i < nCount; i++) + { + OUString aTypeName = r.ReadUniOrByteString(eCharSet); + + sal_uInt16 nTypeMembers; + r.ReadUInt16(nTypeMembers); + + const sal_uInt64 nMaxTypeMembers = r.remainingSize() / 8; + if (nTypeMembers > nMaxTypeMembers) + { + SAL_WARN("basic", "Parsing error: " << nMaxTypeMembers << + " max possible entries, but " << nTypeMembers << " claimed, truncating"); + nTypeMembers = nMaxTypeMembers; + } + + SbxObject *pType = new SbxObject(aTypeName); + SbxArray *pTypeMembers = pType->GetProperties(); + + for (sal_uInt16 j = 0; j < nTypeMembers; j++) + { + OUString aMemberName = r.ReadUniOrByteString(eCharSet); + + sal_Int16 aIntMemberType; + r.ReadInt16(aIntMemberType); + SbxDataType aMemberType = static_cast< SbxDataType > ( aIntMemberType ); + + SbxProperty *pTypeElem = new SbxProperty( aMemberName, aMemberType ); + + sal_uInt32 aIntFlag; + r.ReadUInt32(aIntFlag); + SbxFlagBits nElemFlags = static_cast< SbxFlagBits > ( aIntFlag ); + + pTypeElem->SetFlags(nElemFlags); + + sal_Int16 hasObject; + r.ReadInt16(hasObject); + + if (hasObject == 1) + { + if(aMemberType == SbxOBJECT) + { + // nested user defined types + // declared before use, so it is ok to reference it by name on load + OUString aNestedTypeName = r.ReadUniOrByteString(eCharSet); + SbxObject* pNestedTypeObj = static_cast< SbxObject* >( rTypes->Find( aNestedTypeName, SbxClassType::Object ) ); + if (pNestedTypeObj) + { + SbxObjectRef pCloneObj = cloneTypeObjectImpl( *pNestedTypeObj ); + pTypeElem->PutObject( pCloneObj.get() ); + } + } + else + { + // an array + SbxDimArray* pArray = new SbxDimArray( + static_cast<SbxDataType>(aMemberType & 0x0FFF)); + + sal_Int16 isFixedSize; + r.ReadInt16(isFixedSize); + if (isFixedSize == 1) + pArray->setHasFixedSize( true ); + + sal_Int32 nDims; + r.ReadInt32(nDims); + for (sal_Int32 d = 0; d < nDims; d++) + { + sal_Int32 lBound; + sal_Int32 uBound; + r.ReadInt32(lBound).ReadInt32(uBound); + pArray->unoAddDim(lBound, uBound); + } + + const SbxFlagBits nSavFlags = pTypeElem->GetFlags(); + // need to reset the FIXED flag + // when calling PutObject ( because the type will not match Object ) + pTypeElem->ResetFlag(SbxFlagBits::Fixed); + pTypeElem->PutObject( pArray ); + pTypeElem->SetFlags(nSavFlags); + } + } + + pTypeMembers->Insert(pTypeElem, pTypeMembers->Count()); + + } + + pType->Remove( "Name", SbxClassType::DontCare ); + pType->Remove( "Parent", SbxClassType::DontCare ); + + AddType(pType); + } + break; + } + case FileOffset::ModEnd: + goto done; + default: + break; + } + } + else + { + break; + } + r.Seek( nNext ); + } +done: + r.Seek( nLast ); + if (!r.good()) + { + bError = true; + } + return !bError; +} + +bool SbiImage::Save( SvStream& r, sal_uInt32 nVer ) +{ + bool bLegacy = ( nVer < B_EXT_IMG_VERSION ); + + // detect if old code exceeds legacy limits + // if so, then disallow save + if ( bLegacy && ExceedsLegacyLimits() ) + { + SbiImage aEmptyImg; + aEmptyImg.aName = aName; + aEmptyImg.Save( r, B_LEGACYVERSION ); + return true; + } + // First of all the header + sal_uInt64 nStart = SbiOpenRecord( r, FileOffset::Module, 1 ); + sal_uInt64 nPos; + + eCharSet = GetSOStoreTextEncoding( eCharSet ); + if ( bLegacy ) + { + r.WriteInt32( B_LEGACYVERSION ); + } + else + { + r.WriteInt32( B_CURVERSION ); + } + r .WriteInt32( eCharSet ) + .WriteInt32( nDimBase ) + .WriteInt16( static_cast<sal_uInt16>(nFlags) ) + .WriteInt16( 0 ) + .WriteInt32( 0 ) + .WriteInt32( 0 ); + + // Name? + if (!aName.isEmpty() && r.good()) + { + nPos = SbiOpenRecord( r, FileOffset::Name, 1 ); + r.WriteUniOrByteString( aName, eCharSet ); + SbiCloseRecord( r, nPos ); + } + // Comment? + if (!aComment.isEmpty() && r.good()) + { + nPos = SbiOpenRecord( r, FileOffset::Comment, 1 ); + r.WriteUniOrByteString( aComment, eCharSet ); + SbiCloseRecord( r, nPos ); + } + // Source? + if (!aOUSource.isEmpty() && r.good()) + { + nPos = SbiOpenRecord( r, FileOffset::Source, 1 ); + r.WriteUniOrByteString( aOUSource, eCharSet ); + SbiCloseRecord( r, nPos ); + } + // Binary data? + if (aCode.size() && r.good()) + { + nPos = SbiOpenRecord( r, FileOffset::PCode, 1 ); + if ( bLegacy ) + { + PCodeBuffConvertor<sal_uInt32, sal_uInt16> aNewToLegacy(aCode.data(), aCode.size()); + aNewToLegacy.convert(); + aLegacyPCode = aNewToLegacy.GetBuffer(); + r.WriteBytes(aLegacyPCode.data(), aLegacyPCode.size()); + } + else + { + r.WriteBytes(aCode.data(), aCode.size()); + } + SbiCloseRecord( r, nPos ); + } + // String-Pool? + if( !mvStringOffsets.empty() ) + { + nPos = SbiOpenRecord( r, FileOffset::StringPool, mvStringOffsets.size() ); + // For every String: + // sal_uInt32 Offset of the Strings in the Stringblock + for (size_t i = 0; i < mvStringOffsets.size() && r.good(); i++) + { + r.WriteUInt32( mvStringOffsets[ i ] ); + } + // Then the String-Block + std::unique_ptr<char[]> pByteStrings(new char[ nStringSize ]); + for( size_t i = 0; i < mvStringOffsets.size(); i++ ) + { + sal_uInt16 nOff = static_cast<sal_uInt16>(mvStringOffsets[ i ]); + OString aStr(OUStringToOString(std::u16string_view(pStrings.get() + nOff), eCharSet)); + memcpy( pByteStrings.get() + nOff, aStr.getStr(), (aStr.getLength() + 1) * sizeof( char ) ); + } + r.WriteUInt32( nStringSize ); + r.WriteBytes(pByteStrings.get(), nStringSize); + pByteStrings.reset(); + + // Now write magic number and store the same data in UTF-16; this is backward compatible: + // old readers will not read this data after having read legacy data, and will proceed + // straight to the end of the record. So no version restriction here. + r.WriteUInt32(nUnicodeDataMagicNumber); + write_uInt16s_FromOUString(r, std::u16string_view(pStrings.get(), nStringSize)); + + SbiCloseRecord( r, nPos ); + } + // User defined types + if ( rTypes.is() ) + { + sal_uInt32 nTypes = rTypes->Count(); + assert(nTypes <= std::numeric_limits<sal_uInt16>::max()); + if (nTypes > 0 ) + { + nPos = SbiOpenRecord( r, FileOffset::UserTypes, sal::static_int_cast<sal_uInt16>(nTypes) ); + + for (sal_uInt32 i = 0; i < nTypes; i++) + { + SbxObject* pType = static_cast<SbxObject*>(rTypes->Get(i)); + OUString aTypeName = pType->GetClassName(); + + r.WriteUniOrByteString( aTypeName, eCharSet ); + + SbxArray *pTypeMembers = pType->GetProperties(); + sal_uInt32 nTypeMembers = pTypeMembers->Count(); + assert(nTypeMembers <= std::numeric_limits<sal_uInt16>::max()); + + r.WriteInt16(sal::static_int_cast<sal_uInt16>(nTypeMembers)); + + for (sal_uInt32 j = 0; j < nTypeMembers; j++) + { + + SbxProperty* pTypeElem = static_cast<SbxProperty*>(pTypeMembers->Get(j)); + + const OUString& aElemName = pTypeElem->GetName(); + r.WriteUniOrByteString( aElemName, eCharSet ); + + SbxDataType dataType = pTypeElem->GetType(); + r.WriteInt16(dataType); + + SbxFlagBits nElemFlags = pTypeElem->GetFlags(); + r.WriteUInt32(static_cast< sal_uInt32 > (nElemFlags) ); + + SbxBase* pElemObject = pTypeElem->GetObject(); + + if (pElemObject) + { + r.WriteInt16(1); // has elem Object + + if( dataType == SbxOBJECT ) + { + // nested user defined types + // declared before use, so it is ok to reference it by name on load + SbxObject* pNestedType = static_cast< SbxObject* > ( pElemObject ); + r.WriteUniOrByteString( pNestedType->GetClassName(), eCharSet ); + } + else + { + // an array + SbxDimArray* pArray = static_cast< SbxDimArray* > ( pElemObject ); + + bool bFixedSize = pArray->hasFixedSize(); + if (bFixedSize) + r.WriteInt16(1); + else + r.WriteInt16(0); + + sal_Int32 nDims = pArray->GetDims(); + r.WriteInt32(nDims); + + for (sal_Int32 d = 1; d <= nDims; d++) + { + sal_Int32 lBound; + sal_Int32 uBound; + pArray->GetDim(d, lBound, uBound); + r.WriteInt32(lBound).WriteInt32(uBound); + } + } + } + else + r.WriteInt16(0); // no elem Object + + } + } + SbiCloseRecord( r, nPos ); + } + } + // Set overall length + SbiCloseRecord( r, nStart ); + if (!r.good()) + { + bError = true; + } + return !bError; +} + +void SbiImage::MakeStrings( short nSize ) +{ + nStringIdx = 0; + nStringOff = 0; + nStringSize = 1024; + pStrings.reset( new sal_Unicode[ nStringSize ]); + mvStringOffsets.resize(nSize); + if (nSize != 0) { + memset( mvStringOffsets.data(), 0, nSize * sizeof( sal_uInt32 ) ); + } + memset( pStrings.get(), 0, nStringSize * sizeof( sal_Unicode ) ); +} + +// Add a string to StringPool. The String buffer is dynamically +// growing in 1K-Steps +void SbiImage::AddString( const OUString& r ) +{ + if( nStringIdx >= mvStringOffsets.size() ) + { + bError = true; + } + if( bError ) + return; + + sal_Int32 len = r.getLength() + 1; + sal_uInt32 needed = nStringOff + len; + if( needed > 0xFFFFFF00 ) + { + bError = true; // out of mem! + } + else if( needed > nStringSize ) + { + sal_uInt32 nNewLen = needed + 1024; + nNewLen &= 0xFFFFFC00; // trim to 1K border + std::unique_ptr<sal_Unicode[]> p(new sal_Unicode[nNewLen]); + memcpy( p.get(), pStrings.get(), nStringSize * sizeof( sal_Unicode ) ); + pStrings = std::move(p); + nStringSize = sal::static_int_cast< sal_uInt16 >(nNewLen); + } + if( !bError ) + { + mvStringOffsets[ nStringIdx++ ] = nStringOff; + memcpy( pStrings.get() + nStringOff, r.getStr(), len * sizeof( sal_Unicode ) ); + nStringOff = nStringOff + len; + // Last String? The update the size of the buffer + if( nStringIdx >= mvStringOffsets.size() ) + { + nStringSize = nStringOff; + } + } +} + +// Add code block +// The block was fetched by the compiler from class SbBuffer and +// is already created with new. Additionally it contains all Integers +// in Big Endian format, so can be directly read/written. +void SbiImage::AddCode(std::vector<sal_uInt8>&& v) +{ + aCode = std::move(v); +} + +// Add user type +void SbiImage::AddType(SbxObject const * pObject) +{ + if( !rTypes.is() ) + { + rTypes = new SbxArray; + } + SbxObject *pCopyObject = new SbxObject(*pObject); + rTypes->Insert(pCopyObject, rTypes->Count()); +} + +void SbiImage::AddEnum(SbxObject* pObject) // Register enum type +{ + if( !rEnums.is() ) + { + rEnums = new SbxArray; + } + rEnums->Insert(pObject, rEnums->Count()); +} + +// Note: IDs start with 1 +OUString SbiImage::GetString( sal_uInt32 nId, SbxDataType *eType ) const +{ + if( nId && nId <= mvStringOffsets.size() ) + { + sal_uInt32 nOff = mvStringOffsets[ nId - 1 ]; + sal_Unicode* pStr = pStrings.get() + nOff; + + sal_uInt32 nNextOff = (nId < mvStringOffsets.size()) ? mvStringOffsets[ nId ] : nStringSize; + sal_uInt32 nLen = nNextOff - nOff - 1; + // #i42467: Special treatment for vbNullChar + if (*pStr == 0) + { + if( nLen == 1 ) + { + return OUString( u'\0'); + } + } + else + { + // tdf#143707 - check if the data type character was added after the string termination + // symbol. It was added in basic/source/comp/symtbl.cxx. + OUString aOUStr(pStr); + if (eType != nullptr) + { + *eType = SbxSTRING; + if (o3tl::make_unsigned(aOUStr.getLength()) < nLen) + { + const sal_Unicode pTypeChar = *(pStrings.get() + nOff + aOUStr.getLength() + 1); + switch (pTypeChar) + { + // See GetSuffixType in basic/source/comp/scanner.cxx for type characters + case '%': *eType = SbxINTEGER; break; + case '&': *eType = SbxLONG; break; + case '!': *eType = SbxSINGLE; break; + case '#': *eType = SbxDOUBLE; break; + case '@': *eType = SbxCURRENCY; break; + // tdf#142460 - properly handle boolean values in string pool + case 'b': *eType = SbxBOOL; break; + } + } + } + return aOUStr; + } + } + return OUString(); +} + +const SbxObject* SbiImage::FindType (const OUString& aTypeName) const +{ + return rTypes.is() ? static_cast<SbxObject*>(rTypes->Find(aTypeName,SbxClassType::Object)) : nullptr; +} + +sal_uInt16 SbiImage::CalcLegacyOffset( sal_Int32 nOffset ) +{ + return SbiCodeGen::calcLegacyOffSet(aCode.data(), nOffset); +} + +sal_uInt32 SbiImage::CalcNewOffset( sal_Int16 nOffset ) +{ + return SbiCodeGen::calcNewOffSet(aLegacyPCode.data(), nOffset); +} + +void SbiImage::ReleaseLegacyBuffer() +{ + aLegacyPCode.clear(); +} + +bool SbiImage::ExceedsLegacyLimits() +{ + return (nStringSize > 0xFF00) || (CalcLegacyOffset(aCode.size()) > 0xFF00); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/classes/propacc.cxx b/basic/source/classes/propacc.cxx new file mode 100644 index 000000000..4c948c303 --- /dev/null +++ b/basic/source/classes/propacc.cxx @@ -0,0 +1,179 @@ +/* -*- 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 <propacc.hxx> + +#include <basic/sberrors.hxx> +#include <basic/sbstar.hxx> +#include <basic/sbuno.hxx> +#include <sbunoobj.hxx> + +#include <comphelper/propertysetinfo.hxx> +#include <comphelper/sequence.hxx> +#include <o3tl/any.hxx> + +#include <algorithm> + +using com::sun::star::uno::Reference; +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace cppu; + +static bool SbCompare_UString_PropertyValue_Impl(PropertyValue const & lhs, const OUString& rhs) +{ + return lhs.Name.compareTo(rhs) < 0; +} + + +SbPropertyValues::SbPropertyValues() = default; + + +SbPropertyValues::~SbPropertyValues() = default; + +Reference< XPropertySetInfo > SbPropertyValues::getPropertySetInfo() +{ + // create on demand? + if (!m_xInfo.is()) + { + assert(m_aPropInfos.empty()); + for (auto const& it : m_aPropVals) + m_aPropInfos.emplace_back(it.Name, it.Handle, cppu::UnoType<void>::get(), 0, 0); + m_xInfo.set(new ::comphelper::PropertySetInfo(m_aPropInfos)); + } + return m_xInfo; +} + + +size_t SbPropertyValues::GetIndex_Impl( const OUString &rPropName ) const +{ + SbPropertyValueArr_Impl::const_iterator it = std::lower_bound( + m_aPropVals.begin(), m_aPropVals.end(), rPropName, + SbCompare_UString_PropertyValue_Impl ); + if (it == m_aPropVals.end() || it->Name != rPropName) + { + throw beans::UnknownPropertyException( + "Property not found: " + rPropName, + const_cast<SbPropertyValues&>(*this)); + } + return it - m_aPropVals.begin(); +} + + +void SbPropertyValues::setPropertyValue( + const OUString& aPropertyName, + const Any& aValue) +{ + size_t const nIndex = GetIndex_Impl( aPropertyName ); + PropertyValue & rPropVal = m_aPropVals[nIndex]; + rPropVal.Value = aValue; +} + + +Any SbPropertyValues::getPropertyValue( + const OUString& aPropertyName) +{ + size_t const nIndex = GetIndex_Impl( aPropertyName ); + return m_aPropVals[nIndex].Value; +} + + +void SbPropertyValues::addPropertyChangeListener( + const OUString&, + const Reference< XPropertyChangeListener >& ) +{} + + +void SbPropertyValues::removePropertyChangeListener( + const OUString&, + const Reference< XPropertyChangeListener >& ) +{} + + +void SbPropertyValues::addVetoableChangeListener( + const OUString&, + const Reference< XVetoableChangeListener >& ) +{} + + +void SbPropertyValues::removeVetoableChangeListener( + const OUString&, + const Reference< XVetoableChangeListener >& ) +{} + + +Sequence< PropertyValue > SbPropertyValues::getPropertyValues() +{ + return comphelper::containerToSequence(m_aPropVals); +} + + +void SbPropertyValues::setPropertyValues(const Sequence< PropertyValue >& rPropertyValues ) +{ + if (!m_aPropVals.empty()) + throw IllegalArgumentException("m_aPropVals not empty", static_cast<cppu::OWeakObject*>(this), -1); + + for (const PropertyValue& i : rPropertyValues) + { + m_aPropVals.push_back(i); + } +} + + +void RTL_Impl_CreatePropertySet( SbxArray& rPar ) +{ + // We need at least one parameter + // TODO: In this case < 2 is not correct ;-) + if (rPar.Count() < 2) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // Get class names of struct + + Reference< XInterface > xInterface = static_cast<OWeakObject*>(new SbPropertyValues()); + + SbxVariableRef refVar = rPar.Get(0); + if( xInterface.is() ) + { + // Set PropertyValues + Any aArgAsAny = sbxToUnoValue(rPar.Get(1), + cppu::UnoType<Sequence<PropertyValue>>::get() ); + auto pArg = o3tl::doAccess<Sequence<PropertyValue>>(aArgAsAny); + Reference< XPropertyAccess > xPropAcc( xInterface, UNO_QUERY ); + xPropAcc->setPropertyValues( *pArg ); + + // Build a SbUnoObject and return it + auto xUnoObj = tools::make_ref<SbUnoObject>( "stardiv.uno.beans.PropertySet", Any(xInterface) ); + if( xUnoObj->getUnoAny().hasValue() ) + { + // Return object + refVar->PutObject( xUnoObj.get() ); + return; + } + } + + // Object could not be created + refVar->PutObject( nullptr ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/classes/sb.cxx b/basic/source/classes/sb.cxx new file mode 100644 index 000000000..4e3e716a5 --- /dev/null +++ b/basic/source/classes/sb.cxx @@ -0,0 +1,2225 @@ +/* -*- 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 <sb.hxx> +#include <o3tl/safeint.hxx> +#include <rtl/ustrbuf.hxx> +#include <tools/stream.hxx> +#include <tools/debug.hxx> +#include <vcl/errinf.hxx> +#include <comphelper/solarmutex.hxx> +#include <basic/sbx.hxx> +#include <vcl/svapp.hxx> +#include <comphelper/processfactory.hxx> +#include <image.hxx> +#include <sbunoobj.hxx> +#include <sbjsmeth.hxx> +#include <sbjsmod.hxx> +#include <sbintern.hxx> +#include <runtime.hxx> +#include <basic/sberrors.hxx> +#include <basic/sbuno.hxx> +#include <sbprop.hxx> +#include <sbobjmod.hxx> +#include <stdobj.hxx> +#include <basic.hrc> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/util/XCloseBroadcaster.hpp> +#include <com/sun/star/util/XCloseListener.hpp> +#include <sal/log.hxx> +#include <errobject.hxx> +#include <memory> +#include <unordered_map> + +#include <com/sun/star/script/ModuleType.hpp> +#include <com/sun/star/script/ModuleInfo.hpp> + +#include <strings.hrc> + +using namespace ::com::sun::star::script; + +constexpr OUStringLiteral SB_RTLNAME = u"@SBRTL"; +// i#i68894# +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using com::sun::star::uno::Reference; +using com::sun::star::uno::Any; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::lang::XMultiServiceFactory; + + +class DocBasicItem : public ::cppu::WeakImplHelper< util::XCloseListener > +{ +public: + explicit DocBasicItem( StarBASIC& rDocBasic ); + virtual ~DocBasicItem() override; + + const SbxObjectRef& getClassModules() const { return mxClassModules; } + bool isDocClosed() const { return mbDocClosed; } + + void clearDependingVarsOnDelete( StarBASIC& rDeletedBasic ); + + void startListening(); + void stopListening(); + + void setDisposed( bool bDisposed ) + { + mbDisposed = bDisposed; + } + + virtual void SAL_CALL queryClosing( const lang::EventObject& rSource, sal_Bool bGetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const lang::EventObject& rSource ) override; + virtual void SAL_CALL disposing( const lang::EventObject& rSource ) override; + +private: + StarBASIC& mrDocBasic; + SbxObjectRef mxClassModules; + bool mbDocClosed; + bool mbDisposed; +}; + + +DocBasicItem::DocBasicItem( StarBASIC& rDocBasic ) : + mrDocBasic( rDocBasic ), + mxClassModules( new SbxObject( OUString() ) ), + mbDocClosed( false ), + mbDisposed( false ) +{ +} + +DocBasicItem::~DocBasicItem() +{ + // tdf#90969 HACK: don't use SolarMutexGuard - there is a horrible global + // map GaDocBasicItems holding instances, and these get deleted from exit + // handlers, when the SolarMutex is already dead + comphelper::SolarMutex *pSolarMutex = comphelper::SolarMutex::get(); + if ( pSolarMutex ) + pSolarMutex->acquire(); + + try + { + stopListening(); + mxClassModules.clear(); // release with SolarMutex locked + } + catch (...) + { + assert(false); + } + + pSolarMutex = comphelper::SolarMutex::get(); + if ( pSolarMutex ) + pSolarMutex->release(); +} + +void DocBasicItem::clearDependingVarsOnDelete( StarBASIC& rDeletedBasic ) +{ + mrDocBasic.implClearDependingVarsOnDelete( &rDeletedBasic ); +} + +void DocBasicItem::startListening() +{ + Any aThisComp; + mrDocBasic.GetUNOConstant( "ThisComponent", aThisComp ); + Reference< util::XCloseBroadcaster > xCloseBC( aThisComp, UNO_QUERY ); + mbDisposed = !xCloseBC.is(); + if( xCloseBC.is() ) + { + try { xCloseBC->addCloseListener( this ); } catch(const uno::Exception& ) {} + } +} + +void DocBasicItem::stopListening() +{ + if( mbDisposed ) return; + mbDisposed = true; + Any aThisComp; + if (!mrDocBasic.GetUNOConstant("ThisComponent", aThisComp)) + return; + + Reference< util::XCloseBroadcaster > xCloseBC( aThisComp, UNO_QUERY ); + if( xCloseBC.is() ) + { + try { xCloseBC->removeCloseListener( this ); } catch(const uno::Exception& ) {} + } +} + +void SAL_CALL DocBasicItem::queryClosing( const lang::EventObject& /*rSource*/, sal_Bool /*bGetsOwnership*/ ) +{ +} + +void SAL_CALL DocBasicItem::notifyClosing( const lang::EventObject& /*rEvent*/ ) +{ + stopListening(); + mbDocClosed = true; +} + +void SAL_CALL DocBasicItem::disposing( const lang::EventObject& /*rEvent*/ ) +{ + stopListening(); +} + + +namespace { + +typedef ::rtl::Reference< DocBasicItem > DocBasicItemRef; + +std::unordered_map< const StarBASIC *, DocBasicItemRef > gaDocBasicItems; + +const DocBasicItem* lclFindDocBasicItem( const StarBASIC* pDocBasic ) +{ + auto it = gaDocBasicItems.find( pDocBasic ); + auto end = gaDocBasicItems.end(); + return (it != end) ? it->second.get() : nullptr; +} + +void lclInsertDocBasicItem( StarBASIC& rDocBasic ) +{ + DocBasicItemRef& rxDocBasicItem = gaDocBasicItems[ &rDocBasic ]; + rxDocBasicItem.set( new DocBasicItem( rDocBasic ) ); + rxDocBasicItem->startListening(); +} + +void lclRemoveDocBasicItem( StarBASIC& rDocBasic ) +{ + auto it = gaDocBasicItems.find( &rDocBasic ); + if( it != gaDocBasicItems.end() ) + { + it->second->stopListening(); + gaDocBasicItems.erase( it ); + } + for( auto& rEntry : gaDocBasicItems ) + { + rEntry.second->clearDependingVarsOnDelete( rDocBasic ); + } +} + +StarBASIC* lclGetDocBasicForModule( SbModule* pModule ) +{ + StarBASIC* pRetBasic = nullptr; + SbxObject* pCurParent = pModule; + while( pCurParent->GetParent() != nullptr ) + { + pCurParent = pCurParent->GetParent(); + StarBASIC* pDocBasic = dynamic_cast<StarBASIC*>( pCurParent ); + if( pDocBasic != nullptr && pDocBasic->IsDocBasic() ) + { + pRetBasic = pDocBasic; + break; + } + } + return pRetBasic; +} + +} // namespace + + +SbxObject* StarBASIC::getVBAGlobals( ) +{ + if ( !pVBAGlobals.is() ) + { + Any aThisDoc; + if ( GetUNOConstant("ThisComponent", aThisDoc) ) + { + Reference< XMultiServiceFactory > xDocFac( aThisDoc, UNO_QUERY ); + if ( xDocFac.is() ) + { + try + { + xDocFac->createInstance("ooo.vba.VBAGlobals"); + } + catch(const Exception& ) + { + // Ignore + } + } + } + pVBAGlobals = static_cast<SbUnoObject*>(Find( "VBAGlobals" , SbxClassType::DontCare )); + } + return pVBAGlobals.get(); +} + +// i#i68894# +SbxVariable* StarBASIC::VBAFind( const OUString& rName, SbxClassType t ) +{ + if( rName == "ThisComponent" ) + { + return nullptr; + } + // rename to init globals + if ( getVBAGlobals( ) ) + { + return pVBAGlobals->Find( rName, t ); + } + return nullptr; +} + +namespace { + +// Create array for conversion SFX <-> VB error code +struct SFX_VB_ErrorItem +{ + sal_uInt16 nErrorVB; + ErrCode nErrorSFX; +}; + +} + +const SFX_VB_ErrorItem SFX_VB_ErrorTab[] = +{ + { 1, ERRCODE_BASIC_EXCEPTION }, // #87844 Map exception to error code 1 + { 2, ERRCODE_BASIC_SYNTAX }, + { 3, ERRCODE_BASIC_NO_GOSUB }, + { 4, ERRCODE_BASIC_REDO_FROM_START }, + { 5, ERRCODE_BASIC_BAD_ARGUMENT }, + { 6, ERRCODE_BASIC_MATH_OVERFLOW }, + { 7, ERRCODE_BASIC_NO_MEMORY }, + { 8, ERRCODE_BASIC_ALREADY_DIM }, + { 9, ERRCODE_BASIC_OUT_OF_RANGE }, + { 10, ERRCODE_BASIC_DUPLICATE_DEF }, + { 11, ERRCODE_BASIC_ZERODIV }, + { 12, ERRCODE_BASIC_VAR_UNDEFINED }, + { 13, ERRCODE_BASIC_CONVERSION }, + { 14, ERRCODE_BASIC_BAD_PARAMETER }, + { 18, ERRCODE_BASIC_USER_ABORT }, + { 20, ERRCODE_BASIC_BAD_RESUME }, + { 28, ERRCODE_BASIC_STACK_OVERFLOW }, + { 35, ERRCODE_BASIC_PROC_UNDEFINED }, + { 48, ERRCODE_BASIC_BAD_DLL_LOAD }, + { 49, ERRCODE_BASIC_BAD_DLL_CALL }, + { 51, ERRCODE_BASIC_INTERNAL_ERROR }, + { 52, ERRCODE_BASIC_BAD_CHANNEL }, + { 53, ERRCODE_BASIC_FILE_NOT_FOUND }, + { 54, ERRCODE_BASIC_BAD_FILE_MODE }, + { 55, ERRCODE_BASIC_FILE_ALREADY_OPEN }, + { 57, ERRCODE_BASIC_IO_ERROR }, + { 58, ERRCODE_BASIC_FILE_EXISTS }, + { 59, ERRCODE_BASIC_BAD_RECORD_LENGTH }, + { 61, ERRCODE_BASIC_DISK_FULL }, + { 62, ERRCODE_BASIC_READ_PAST_EOF }, + { 63, ERRCODE_BASIC_BAD_RECORD_NUMBER }, + { 67, ERRCODE_BASIC_TOO_MANY_FILES }, + { 68, ERRCODE_BASIC_NO_DEVICE }, + { 70, ERRCODE_BASIC_ACCESS_DENIED }, + { 71, ERRCODE_BASIC_NOT_READY }, + { 73, ERRCODE_BASIC_NOT_IMPLEMENTED }, + { 74, ERRCODE_BASIC_DIFFERENT_DRIVE }, + { 75, ERRCODE_BASIC_ACCESS_ERROR }, + { 76, ERRCODE_BASIC_PATH_NOT_FOUND }, + { 91, ERRCODE_BASIC_NO_OBJECT }, + { 93, ERRCODE_BASIC_BAD_PATTERN }, + { 94, ERRCODE_BASIC_IS_NULL }, + { 250, ERRCODE_BASIC_DDE_ERROR }, + { 280, ERRCODE_BASIC_DDE_WAITINGACK }, + { 281, ERRCODE_BASIC_DDE_OUTOFCHANNELS }, + { 282, ERRCODE_BASIC_DDE_NO_RESPONSE }, + { 283, ERRCODE_BASIC_DDE_MULT_RESPONSES }, + { 284, ERRCODE_BASIC_DDE_CHANNEL_LOCKED }, + { 285, ERRCODE_BASIC_DDE_NOTPROCESSED }, + { 286, ERRCODE_BASIC_DDE_TIMEOUT }, + { 287, ERRCODE_BASIC_DDE_USER_INTERRUPT }, + { 288, ERRCODE_BASIC_DDE_BUSY }, + { 289, ERRCODE_BASIC_DDE_NO_DATA }, + { 290, ERRCODE_BASIC_DDE_WRONG_DATA_FORMAT }, + { 291, ERRCODE_BASIC_DDE_PARTNER_QUIT }, + { 292, ERRCODE_BASIC_DDE_CONV_CLOSED }, + { 293, ERRCODE_BASIC_DDE_NO_CHANNEL }, + { 294, ERRCODE_BASIC_DDE_INVALID_LINK }, + { 295, ERRCODE_BASIC_DDE_QUEUE_OVERFLOW }, + { 296, ERRCODE_BASIC_DDE_LINK_ALREADY_EST }, + { 297, ERRCODE_BASIC_DDE_LINK_INV_TOPIC }, + { 298, ERRCODE_BASIC_DDE_DLL_NOT_FOUND }, + { 323, ERRCODE_BASIC_CANNOT_LOAD }, + { 341, ERRCODE_BASIC_BAD_INDEX }, + { 366, ERRCODE_BASIC_NO_ACTIVE_OBJECT }, + { 380, ERRCODE_BASIC_BAD_PROP_VALUE }, + { 382, ERRCODE_BASIC_PROP_READONLY }, + { 394, ERRCODE_BASIC_PROP_WRITEONLY }, + { 420, ERRCODE_BASIC_INVALID_OBJECT }, + { 423, ERRCODE_BASIC_NO_METHOD }, + { 424, ERRCODE_BASIC_NEEDS_OBJECT }, + { 425, ERRCODE_BASIC_INVALID_USAGE_OBJECT }, + { 430, ERRCODE_BASIC_NO_OLE }, + { 438, ERRCODE_BASIC_BAD_METHOD }, + { 440, ERRCODE_BASIC_OLE_ERROR }, + { 445, ERRCODE_BASIC_BAD_ACTION }, + { 446, ERRCODE_BASIC_NO_NAMED_ARGS }, + { 447, ERRCODE_BASIC_BAD_LOCALE }, + { 448, ERRCODE_BASIC_NAMED_NOT_FOUND }, + { 449, ERRCODE_BASIC_NOT_OPTIONAL }, + { 450, ERRCODE_BASIC_WRONG_ARGS }, + { 451, ERRCODE_BASIC_NOT_A_COLL }, + { 452, ERRCODE_BASIC_BAD_ORDINAL }, + { 453, ERRCODE_BASIC_DLLPROC_NOT_FOUND }, + { 460, ERRCODE_BASIC_BAD_CLIPBD_FORMAT }, + { 951, ERRCODE_BASIC_UNEXPECTED }, + { 952, ERRCODE_BASIC_EXPECTED }, + { 953, ERRCODE_BASIC_SYMBOL_EXPECTED }, + { 954, ERRCODE_BASIC_VAR_EXPECTED }, + { 955, ERRCODE_BASIC_LABEL_EXPECTED }, + { 956, ERRCODE_BASIC_LVALUE_EXPECTED }, + { 957, ERRCODE_BASIC_VAR_DEFINED }, + { 958, ERRCODE_BASIC_PROC_DEFINED }, + { 959, ERRCODE_BASIC_LABEL_DEFINED }, + { 960, ERRCODE_BASIC_UNDEF_VAR }, + { 961, ERRCODE_BASIC_UNDEF_ARRAY }, + { 962, ERRCODE_BASIC_UNDEF_PROC }, + { 963, ERRCODE_BASIC_UNDEF_LABEL }, + { 964, ERRCODE_BASIC_UNDEF_TYPE }, + { 965, ERRCODE_BASIC_BAD_EXIT }, + { 966, ERRCODE_BASIC_BAD_BLOCK }, + { 967, ERRCODE_BASIC_BAD_BRACKETS }, + { 968, ERRCODE_BASIC_BAD_DECLARATION }, + { 969, ERRCODE_BASIC_BAD_PARAMETERS }, + { 970, ERRCODE_BASIC_BAD_CHAR_IN_NUMBER }, + { 971, ERRCODE_BASIC_MUST_HAVE_DIMS }, + { 972, ERRCODE_BASIC_NO_IF }, + { 973, ERRCODE_BASIC_NOT_IN_SUBR }, + { 974, ERRCODE_BASIC_NOT_IN_MAIN }, + { 975, ERRCODE_BASIC_WRONG_DIMS }, + { 976, ERRCODE_BASIC_BAD_OPTION }, + { 977, ERRCODE_BASIC_CONSTANT_REDECLARED }, + { 978, ERRCODE_BASIC_PROG_TOO_LARGE }, + { 979, ERRCODE_BASIC_NO_STRINGS_ARRAYS }, + { 1000, ERRCODE_BASIC_PROPERTY_NOT_FOUND }, + { 1001, ERRCODE_BASIC_METHOD_NOT_FOUND }, + { 1002, ERRCODE_BASIC_ARG_MISSING }, + { 1003, ERRCODE_BASIC_BAD_NUMBER_OF_ARGS }, + { 1004, ERRCODE_BASIC_METHOD_FAILED }, + { 1005, ERRCODE_BASIC_SETPROP_FAILED }, + { 1006, ERRCODE_BASIC_GETPROP_FAILED }, + { 1007, ERRCODE_BASIC_COMPAT }, + { 0xFFFF, ErrCode(0xFFFFFFFFUL) } // End mark +}; + +// The StarBASIC factory is a hack. When a SbModule is created, its pointer +// is saved and given to the following SbProperties/SbMethods. This restores +// the Module-relationship. But it works only when a module is loaded. +// Can cause troubles with separately loaded properties! + +SbxBaseRef SbiFactory::Create( sal_uInt16 nSbxId, sal_uInt32 nCreator ) +{ + if( nCreator == SBXCR_SBX ) + { + switch( nSbxId ) + { + case SBXID_BASIC: + return new StarBASIC( nullptr ); + case SBXID_BASICMOD: + return new SbModule( "" ); + case SBXID_BASICPROP: + return new SbProperty( "", SbxVARIANT, nullptr ); + case SBXID_BASICMETHOD: + return new SbMethod( "", SbxVARIANT, nullptr ); + case SBXID_JSCRIPTMOD: + return new SbJScriptModule; + case SBXID_JSCRIPTMETH: + return new SbJScriptMethod( SbxVARIANT ); + } + } + return nullptr; +} + +SbxObjectRef SbiFactory::CreateObject( const OUString& rClass ) +{ + if( rClass.equalsIgnoreAsciiCase( "StarBASIC" ) ) + { + return new StarBASIC( nullptr ); + } + else if( rClass.equalsIgnoreAsciiCase( "StarBASICModule" ) ) + { + return new SbModule( OUString() ); + } + else if( rClass.equalsIgnoreAsciiCase( "Collection" ) ) + { + return new BasicCollection( "Collection" ); + } + else if( rClass.equalsIgnoreAsciiCase( "FileSystemObject" ) ) + { + try + { + Reference< XMultiServiceFactory > xFactory( comphelper::getProcessServiceFactory(), UNO_SET_THROW ); + OUString aServiceName("ooo.vba.FileSystemObject"); + Reference< XInterface > xInterface( xFactory->createInstance( aServiceName ), UNO_SET_THROW ); + return new SbUnoObject( aServiceName, uno::Any( xInterface ) ); + } + catch(const Exception& ) + { + } + } + return nullptr; +} + + +SbxBaseRef SbOLEFactory::Create( sal_uInt16, sal_uInt32 ) +{ + // Not supported + return nullptr; +} + +SbxObjectRef SbOLEFactory::CreateObject( const OUString& rClassName ) +{ + SbxObjectRef pRet = createOLEObject_Impl( rClassName ); + return pRet; +} + + +// SbFormFactory, show user forms by: dim as new <user form name> + +SbxBaseRef SbFormFactory::Create( sal_uInt16, sal_uInt32 ) +{ + // Not supported + return nullptr; +} + +SbxObjectRef SbFormFactory::CreateObject( const OUString& rClassName ) +{ + if( SbModule* pMod = GetSbData()->pMod ) + { + if( SbxVariable* pVar = pMod->Find( rClassName, SbxClassType::Object ) ) + { + if( SbUserFormModule* pFormModule = dynamic_cast<SbUserFormModule*>( pVar->GetObject() ) ) + { + bool bInitState = pFormModule->getInitState(); + if( bInitState ) + { + // Not the first instantiate, reset + pFormModule->ResetApiObj( false/*bTriggerTerminateEvent*/ ); + pFormModule->setInitState( false ); + } + else + { + pFormModule->Load(); + } + return pFormModule->CreateInstance(); + } + } + } + return nullptr; +} + + +// SbTypeFactory + +SbxObjectRef cloneTypeObjectImpl( const SbxObject& rTypeObj ) +{ + SbxObjectRef pRet = new SbxObject( rTypeObj ); + pRet->PutObject( pRet.get() ); + + // Copy the properties, not only the reference to them + SbxArray* pProps = pRet->GetProperties(); + sal_uInt32 nCount = pProps->Count(); + for( sal_uInt32 i = 0 ; i < nCount ; i++ ) + { + SbxVariable* pVar = pProps->Get(i); + SbxProperty* pProp = dynamic_cast<SbxProperty*>( pVar ); + if( pProp ) + { + SbxProperty* pNewProp = new SbxProperty( *pProp ); + SbxDataType eVarType = pVar->GetType(); + if( eVarType & SbxARRAY ) + { + SbxBase* pParObj = pVar->GetObject(); + SbxDimArray* pSource = dynamic_cast<SbxDimArray*>( pParObj ); + SbxDimArray* pDest = new SbxDimArray( pVar->GetType() ); + + pDest->setHasFixedSize( pSource && pSource->hasFixedSize() ); + if (pSource && pSource->GetDims() && pSource->hasFixedSize()) + { + sal_Int32 lb = 0; + sal_Int32 ub = 0; + for (sal_Int32 j = 1; j <= pSource->GetDims(); ++j) + { + pSource->GetDim(j, lb, ub); + pDest->AddDim(lb, ub); + } + } + else + { + pDest->unoAddDim(0, -1); // variant array + } + SbxFlagBits nSavFlags = pVar->GetFlags(); + pNewProp->ResetFlag( SbxFlagBits::Fixed ); + // need to reset the FIXED flag + // when calling PutObject ( because the type will not match Object ) + pNewProp->PutObject( pDest ); + pNewProp->SetFlags( nSavFlags ); + } + if( eVarType == SbxOBJECT ) + { + SbxBase* pObjBase = pVar->GetObject(); + SbxObject* pSrcObj = dynamic_cast<SbxObject*>( pObjBase ); + SbxObjectRef pDestObj; + if( pSrcObj != nullptr ) + pDestObj = cloneTypeObjectImpl( *pSrcObj ); + pNewProp->PutObject( pDestObj.get() ); + } + pProps->PutDirect( pNewProp, i ); + } + } + return pRet; +} + +SbxBaseRef SbTypeFactory::Create( sal_uInt16, sal_uInt32 ) +{ + // Not supported + return nullptr; +} + +SbxObjectRef SbTypeFactory::CreateObject( const OUString& rClassName ) +{ + SbxObjectRef pRet; + SbModule* pMod = GetSbData()->pMod; + if( pMod ) + { + const SbxObject* pObj = pMod->FindType( rClassName ); + if( pObj ) + { + pRet = cloneTypeObjectImpl( *pObj ); + } + } + return pRet; +} + +SbxObjectRef createUserTypeImpl( const OUString& rClassName ) +{ + SbxObjectRef pRetObj = GetSbData()->pTypeFac->CreateObject( rClassName ); + return pRetObj; +} + + +SbClassModuleObject::SbClassModuleObject( SbModule* pClassModule ) + : SbModule( pClassModule->GetName() ) + , mpClassModule( pClassModule ) + , mbInitializeEventDone( false ) +{ + aOUSource = pClassModule->aOUSource; + aComment = pClassModule->aComment; + // see comment in destructor about these two + pImage.reset(pClassModule->pImage.get()); + pBreaks = pClassModule->pBreaks; + + SetClassName( pClassModule->GetName() ); + + // Allow search only internally + ResetFlag( SbxFlagBits::GlobalSearch ); + + // Copy the methods from original class module + SbxArray* pClassMethods = pClassModule->GetMethods().get(); + sal_uInt32 nMethodCount = pClassMethods->Count(); + sal_uInt32 i; + for( i = 0 ; i < nMethodCount ; i++ ) + { + SbxVariable* pVar = pClassMethods->Get(i); + + // Exclude SbIfaceMapperMethod to copy them in a second step + SbIfaceMapperMethod* pIfaceMethod = dynamic_cast<SbIfaceMapperMethod*>( pVar ); + if( !pIfaceMethod ) + { + SbMethod* pMethod = dynamic_cast<SbMethod*>( pVar ); + if( pMethod ) + { + SbxFlagBits nFlags_ = pMethod->GetFlags(); + pMethod->SetFlag( SbxFlagBits::NoBroadcast ); + SbMethod* pNewMethod = new SbMethod( *pMethod ); + pNewMethod->ResetFlag( SbxFlagBits::NoBroadcast ); + pMethod->SetFlags( nFlags_ ); + pNewMethod->pMod = this; + pNewMethod->SetParent( this ); + pMethods->PutDirect( pNewMethod, i ); + StartListening(pNewMethod->GetBroadcaster(), DuplicateHandling::Prevent); + } + } + } + + // Copy SbIfaceMapperMethod in a second step to ensure that + // the corresponding base methods have already been copied + for( i = 0 ; i < nMethodCount ; i++ ) + { + SbxVariable* pVar = pClassMethods->Get(i); + + SbIfaceMapperMethod* pIfaceMethod = dynamic_cast<SbIfaceMapperMethod*>( pVar ); + if( pIfaceMethod ) + { + SbMethod* pImplMethod = pIfaceMethod->getImplMethod(); + if( !pImplMethod ) + { + OSL_FAIL( "No ImplMethod" ); + continue; + } + + // Search for own copy of ImplMethod + SbxVariable* p = pMethods->Find( pImplMethod->GetName(), SbxClassType::Method ); + SbMethod* pImplMethodCopy = dynamic_cast<SbMethod*>( p ); + if( !pImplMethodCopy ) + { + OSL_FAIL( "Found no ImplMethod copy" ); + continue; + } + SbIfaceMapperMethod* pNewIfaceMethod = + new SbIfaceMapperMethod( pIfaceMethod->GetName(), pImplMethodCopy ); + pMethods->PutDirect( pNewIfaceMethod, i ); + } + } + + // Copy the properties from original class module + SbxArray* pClassProps = pClassModule->GetProperties(); + sal_uInt32 nPropertyCount = pClassProps->Count(); + for( i = 0 ; i < nPropertyCount ; i++ ) + { + SbxVariable* pVar = pClassProps->Get(i); + SbProcedureProperty* pProcedureProp = dynamic_cast<SbProcedureProperty*>( pVar ); + if( pProcedureProp ) + { + SbxFlagBits nFlags_ = pProcedureProp->GetFlags(); + pProcedureProp->SetFlag( SbxFlagBits::NoBroadcast ); + SbProcedureProperty* pNewProp = new SbProcedureProperty + ( pProcedureProp->GetName(), pProcedureProp->GetType() ); + pNewProp->SetFlags( nFlags_ ); // Copy flags + pNewProp->ResetFlag( SbxFlagBits::NoBroadcast ); // except the Broadcast if it was set + pProcedureProp->SetFlags( nFlags_ ); + pProps->PutDirect( pNewProp, i ); + StartListening(pNewProp->GetBroadcaster(), DuplicateHandling::Prevent); + } + else + { + SbxProperty* pProp = dynamic_cast<SbxProperty*>( pVar ); + if( pProp ) + { + SbxFlagBits nFlags_ = pProp->GetFlags(); + pProp->SetFlag( SbxFlagBits::NoBroadcast ); + SbxProperty* pNewProp = new SbxProperty( *pProp ); + + // Special handling for modules instances and collections, they need + // to be instantiated, otherwise all refer to the same base object + SbxDataType eVarType = pProp->GetType(); + if( eVarType == SbxOBJECT ) + { + SbxBase* pObjBase = pProp->GetObject(); + SbxObject* pObj = dynamic_cast<SbxObject*>( pObjBase ); + if( pObj != nullptr ) + { + const OUString& aObjClass = pObj->GetClassName(); + + SbClassModuleObject* pClassModuleObj = dynamic_cast<SbClassModuleObject*>( pObjBase ); + if( pClassModuleObj != nullptr ) + { + SbModule* pLclClassModule = pClassModuleObj->getClassModule(); + SbClassModuleObject* pNewObj = new SbClassModuleObject( pLclClassModule ); + pNewObj->SetName( pProp->GetName() ); + pNewObj->SetParent( pLclClassModule->pParent ); + pNewProp->PutObject( pNewObj ); + } + else if( aObjClass.equalsIgnoreAsciiCase( "Collection" ) ) + { + BasicCollection* pNewCollection = new BasicCollection( "Collection" ); + pNewCollection->SetName( pProp->GetName() ); + pNewCollection->SetParent( pClassModule->pParent ); + pNewProp->PutObject( pNewCollection ); + } + } + } + + pNewProp->ResetFlag( SbxFlagBits::NoBroadcast ); + pNewProp->SetParent( this ); + pProps->PutDirect( pNewProp, i ); + pProp->SetFlags( nFlags_ ); + } + } + } + SetModuleType( ModuleType::CLASS ); + mbVBACompat = pClassModule->mbVBACompat; +} + +SbClassModuleObject::~SbClassModuleObject() +{ + // do not trigger termination event when document is already closed + if( StarBASIC::IsRunning() ) + if( StarBASIC* pDocBasic = lclGetDocBasicForModule( this ) ) + if( const DocBasicItem* pDocBasicItem = lclFindDocBasicItem( pDocBasic ) ) + if( !pDocBasicItem->isDocClosed() ) + triggerTerminateEvent(); + + // prevent the base class destructor from deleting these because + // we do not actually own them + pImage.release(); + pBreaks = nullptr; +} + +void SbClassModuleObject::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + handleProcedureProperties( rBC, rHint ); +} + +SbxVariable* SbClassModuleObject::Find( const OUString& rName, SbxClassType t ) +{ + SbxVariable* pRes = SbxObject::Find( rName, t ); + if( pRes ) + { + triggerInitializeEvent(); + + SbIfaceMapperMethod* pIfaceMapperMethod = dynamic_cast<SbIfaceMapperMethod*>( pRes ); + if( pIfaceMapperMethod ) + { + pRes = pIfaceMapperMethod->getImplMethod(); + pRes->SetFlag( SbxFlagBits::ExtFound ); + } + } + return pRes; +} + +void SbClassModuleObject::triggerInitializeEvent() +{ + if( mbInitializeEventDone ) + { + return; + } + + mbInitializeEventDone = true; + + // Search method + SbxVariable* pMeth = SbxObject::Find("Class_Initialize", SbxClassType::Method); + if( pMeth ) + { + SbxValues aVals; + pMeth->Get( aVals ); + } +} + +void SbClassModuleObject::triggerTerminateEvent() +{ + if( !mbInitializeEventDone || GetSbData()->bRunInit ) + { + return; + } + // Search method + SbxVariable* pMeth = SbxObject::Find("Class_Terminate", SbxClassType::Method ); + if( pMeth ) + { + SbxValues aVals; + pMeth->Get( aVals ); + } +} + + +SbClassData::SbClassData() +{ + mxIfaces = new SbxArray(); +} + +void SbClassData::clear() +{ + mxIfaces->Clear(); + maRequiredTypes.clear(); +} + +SbClassFactory::SbClassFactory() +{ + xClassModules = new SbxObject( OUString() ); +} + +SbClassFactory::~SbClassFactory() +{} + +void SbClassFactory::AddClassModule( SbModule* pClassModule ) +{ + SbxObjectRef xToUseClassModules = xClassModules; + + if( StarBASIC* pDocBasic = lclGetDocBasicForModule( pClassModule ) ) + if( const DocBasicItem* pDocBasicItem = lclFindDocBasicItem( pDocBasic ) ) + xToUseClassModules = pDocBasicItem->getClassModules(); + + SbxObject* pParent = pClassModule->GetParent(); + xToUseClassModules->Insert( pClassModule ); + pClassModule->SetParent( pParent ); +} + +void SbClassFactory::RemoveClassModule( SbModule* pClassModule ) +{ + xClassModules->Remove( pClassModule ); +} + +SbxBaseRef SbClassFactory::Create( sal_uInt16, sal_uInt32 ) +{ + // Not supported + return nullptr; +} + +SbxObjectRef SbClassFactory::CreateObject( const OUString& rClassName ) +{ + SbxObjectRef xToUseClassModules = xClassModules; + + if( SbModule* pMod = GetSbData()->pMod ) + { + if( StarBASIC* pDocBasic = lclGetDocBasicForModule( pMod ) ) + { + if( const DocBasicItem* pDocBasicItem = lclFindDocBasicItem( pDocBasic ) ) + { + xToUseClassModules = pDocBasicItem->getClassModules(); + } + } + } + SbxVariable* pVar = xToUseClassModules->Find( rClassName, SbxClassType::Object ); + SbxObjectRef pRet; + if( pVar ) + { + SbModule* pVarMod = static_cast<SbModule*>(pVar); + pRet = new SbClassModuleObject( pVarMod ); + } + return pRet; +} + +SbModule* SbClassFactory::FindClass( const OUString& rClassName ) +{ + SbxVariable* pVar = xClassModules->Find( rClassName, SbxClassType::DontCare ); + SbModule* pMod = pVar ? static_cast<SbModule*>(pVar) : nullptr; + return pMod; +} + +StarBASIC::StarBASIC( StarBASIC* p, bool bIsDocBasic ) + : SbxObject("StarBASIC"), bDocBasic( bIsDocBasic ) +{ + SetParent( p ); + bNoRtl = bBreak = false; + bVBAEnabled = false; + + if( !GetSbData()->nInst++ ) + { + GetSbData()->pSbFac.reset( new SbiFactory ); + AddFactory( GetSbData()->pSbFac.get() ); + GetSbData()->pTypeFac.reset(new SbTypeFactory); + AddFactory( GetSbData()->pTypeFac.get() ); + GetSbData()->pClassFac.reset(new SbClassFactory); + AddFactory( GetSbData()->pClassFac.get() ); + GetSbData()->pOLEFac.reset(new SbOLEFactory); + AddFactory( GetSbData()->pOLEFac.get() ); + GetSbData()->pFormFac.reset(new SbFormFactory); + AddFactory( GetSbData()->pFormFac.get() ); + GetSbData()->pUnoFac.reset( new SbUnoFactory ); + AddFactory( GetSbData()->pUnoFac.get() ); + } + pRtl = new SbiStdObject(SB_RTLNAME, this ); + // Search via StarBasic is always global + SetFlag( SbxFlagBits::GlobalSearch ); + pVBAGlobals = nullptr; + bQuit = false; + + if( bDocBasic ) + { + lclInsertDocBasicItem( *this ); + } +} + +// #51727 Override SetModified so that the modified state +// is not given to the parent +void StarBASIC::SetModified( bool b ) +{ + SbxBase::SetModified( b ); +} + +StarBASIC::~StarBASIC() +{ + // Needs to be first action as it can trigger events + disposeComVariablesForBasic( this ); + + if( !--GetSbData()->nInst ) + { + RemoveFactory( GetSbData()->pSbFac.get() ); + GetSbData()->pSbFac.reset(); + RemoveFactory( GetSbData()->pUnoFac.get() ); + GetSbData()->pUnoFac.reset(); + RemoveFactory( GetSbData()->pTypeFac.get() ); + GetSbData()->pTypeFac.reset(); + RemoveFactory( GetSbData()->pClassFac.get() ); + GetSbData()->pClassFac.reset(); + RemoveFactory( GetSbData()->pOLEFac.get() ); + GetSbData()->pOLEFac.reset(); + RemoveFactory( GetSbData()->pFormFac.get() ); + GetSbData()->pFormFac.reset(); + + if( SbiGlobals::pGlobals ) + { + delete SbiGlobals::pGlobals; + SbiGlobals::pGlobals = nullptr; + } + } + else if( bDocBasic ) + { + ErrCode eOld = SbxBase::GetError(); + + lclRemoveDocBasicItem( *this ); + + SbxBase::ResetError(); + if( eOld != ERRCODE_NONE ) + { + SbxBase::SetError( eOld ); + } + } + + // #100326 Set Parent NULL in registered listeners + if( xUnoListeners.is() ) + { + sal_uInt32 uCount = xUnoListeners->Count(); + for( sal_uInt32 i = 0 ; i < uCount ; i++ ) + { + SbxVariable* pListenerObj = xUnoListeners->Get(i); + pListenerObj->SetParent( nullptr ); + } + xUnoListeners = nullptr; + } + + clearUnoMethodsForBasic( this ); +} + +void StarBASIC::implClearDependingVarsOnDelete( StarBASIC* pDeletedBasic ) +{ + if( this != pDeletedBasic ) + { + for( const auto& pModule: pModules) + { + pModule->ClearVarsDependingOnDeletedBasic( pDeletedBasic ); + } + } + + for (sal_uInt32 nObj = 0; nObj < pObjs->Count(); nObj++) + { + SbxVariable* pVar = pObjs->Get(nObj); + StarBASIC* pBasic = dynamic_cast<StarBASIC*>( pVar ); + if( pBasic && pBasic != pDeletedBasic ) + { + pBasic->implClearDependingVarsOnDelete( pDeletedBasic ); + } + } +} + + +SbModule* StarBASIC::MakeModule( const OUString& rName, const OUString& rSrc ) +{ + ModuleInfo aInfo; + aInfo.ModuleType = ModuleType::NORMAL; + return MakeModule( rName, aInfo, rSrc ); +} +SbModule* StarBASIC::MakeModule( const OUString& rName, const ModuleInfo& mInfo, const OUString& rSrc ) +{ + + SAL_INFO( + "basic", + "create module " << rName << " type mInfo " << mInfo.ModuleType); + SbModule* p = nullptr; + switch ( mInfo.ModuleType ) + { + case ModuleType::DOCUMENT: + // In theory we should be able to create Object modules + // in ordinary basic ( in vba mode thought these are create + // by the application/basic and not by the user ) + p = new SbObjModule( rName, mInfo, isVBAEnabled() ); + break; + case ModuleType::CLASS: + p = new SbModule( rName, isVBAEnabled() ); + p->SetModuleType( ModuleType::CLASS ); + break; + case ModuleType::FORM: + p = new SbUserFormModule( rName, mInfo, isVBAEnabled() ); + break; + default: + p = new SbModule( rName, isVBAEnabled() ); + break; + } + p->SetSource32( rSrc ); + p->SetParent( this ); + pModules.emplace_back(p); + SetModified( true ); + return p; +} + +void StarBASIC::Insert( SbxVariable* pVar ) +{ + if( auto pModule = dynamic_cast<SbModule*>(pVar) ) + { + pModules.emplace_back(pModule); + pVar->SetParent( this ); + StartListening(pVar->GetBroadcaster(), DuplicateHandling::Prevent); + } + else + { + bool bWasModified = IsModified(); + SbxObject::Insert( pVar ); + if( !bWasModified && pVar->IsSet( SbxFlagBits::DontStore ) ) + { + SetModified( false ); + } + } +} + +void StarBASIC::Remove( SbxVariable* pVar ) +{ + SbModule* pModule = dynamic_cast<SbModule*>(pVar); + if( pModule ) + { + // #87540 Can be last reference! + SbModuleRef xVar = pModule; + pModules.erase(std::remove(pModules.begin(), pModules.end(), xVar)); + pVar->SetParent( nullptr ); + EndListening( pVar->GetBroadcaster() ); + } + else + { + SbxObject::Remove( pVar ); + } +} + +void StarBASIC::Clear() +{ + pModules.clear(); +} + +SbModule* StarBASIC::FindModule( std::u16string_view rName ) +{ + for (const auto& pModule: pModules) + { + if( pModule->GetName().equalsIgnoreAsciiCase( rName ) ) + { + return pModule.get(); + } + } + return nullptr; +} + + +struct ClassModuleRunInitItem +{ + SbModule* m_pModule; + bool m_bProcessing; + bool m_bRunInitDone; + + ClassModuleRunInitItem() + : m_pModule( nullptr ) + , m_bProcessing( false ) + , m_bRunInitDone( false ) + {} + explicit ClassModuleRunInitItem( SbModule* pModule ) + : m_pModule( pModule ) + , m_bProcessing( false ) + , m_bRunInitDone( false ) + {} +}; + +// Derive from unordered_map type instead of typedef +// to allow forward declaration in sbmod.hxx +class ModuleInitDependencyMap : public + std::unordered_map< OUString, ClassModuleRunInitItem > +{}; + +void SbModule::implProcessModuleRunInit( ModuleInitDependencyMap& rMap, ClassModuleRunInitItem& rItem ) +{ + rItem.m_bProcessing = true; + + SbModule* pModule = rItem.m_pModule; + if( pModule->pClassData != nullptr ) + { + std::vector< OUString >& rReqTypes = pModule->pClassData->maRequiredTypes; + for( const auto& rStr : rReqTypes ) + { + // Is required type a class module? + ModuleInitDependencyMap::iterator itFind = rMap.find( rStr ); + if( itFind != rMap.end() ) + { + ClassModuleRunInitItem& rParentItem = itFind->second; + if( rParentItem.m_bProcessing ) + { + // TODO: raise error? + OSL_FAIL( "Cyclic module dependency detected" ); + continue; + } + + if( !rParentItem.m_bRunInitDone ) + { + implProcessModuleRunInit( rMap, rParentItem ); + } + } + } + } + + pModule->RunInit(); + rItem.m_bRunInitDone = true; + rItem.m_bProcessing = false; +} + +// Run Init-Code of all modules (including inserted libraries) +void StarBASIC::InitAllModules( StarBASIC const * pBasicNotToInit ) +{ + SolarMutexGuard guard; + + // Init own modules + for (const auto& pModule: pModules) + { + pModule->Compile(); + } + // compile modules first then RunInit ( otherwise there is + // can be order dependency, e.g. classmodule A has a member + // of type classmodule B and classmodule B hasn't been compiled yet ) + + // Consider required types to init in right order. Class modules + // that are required by other modules have to be initialized first. + ModuleInitDependencyMap aMIDMap; + for (const auto& pModule: pModules) + { + OUString aModuleName = pModule->GetName(); + if( pModule->isProxyModule() ) + { + aMIDMap[aModuleName] = ClassModuleRunInitItem( pModule.get() ); + } + } + + for (auto & elem : aMIDMap) + { + ClassModuleRunInitItem& rItem = elem.second; + SbModule::implProcessModuleRunInit( aMIDMap, rItem ); + } + + // Call RunInit on standard modules + for (const auto& pModule: pModules) + { + if( !pModule->isProxyModule() ) + { + pModule->RunInit(); + } + } + + // Check all objects if they are BASIC, + // if yes initialize + for (sal_uInt32 nObj = 0; nObj < pObjs->Count(); nObj++) + { + SbxVariable* pVar = pObjs->Get(nObj); + StarBASIC* pBasic = dynamic_cast<StarBASIC*>( pVar ); + if( pBasic && pBasic != pBasicNotToInit ) + { + pBasic->InitAllModules(); + } + } +} + +// #88329 Put modules back to not initialised state to +// force reinitialisation at next start +void StarBASIC::DeInitAllModules() +{ + // Deinit own modules + for (const auto& pModule: pModules) + { + if( pModule->pImage && !pModule->isProxyModule() && dynamic_cast<SbObjModule*>( pModule.get()) == nullptr ) + { + pModule->pImage->bInit = false; + } + } + + for (sal_uInt32 nObj = 0; nObj < pObjs->Count(); nObj++) + { + SbxVariable* pVar = pObjs->Get(nObj); + StarBASIC* pBasic = dynamic_cast<StarBASIC*>( pVar ); + if( pBasic ) + { + pBasic->DeInitAllModules(); + } + } +} + +// This implementation at first searches within the runtime library, +// then it looks for an element within one module. This module can be +// a public var or an entrypoint. If it is not found and we look for a +// method and a module with the given name is found the search continues +// for entrypoint "Main". +// If this fails again a conventional search over objects is performed. +SbxVariable* StarBASIC::Find( const OUString& rName, SbxClassType t ) +{ + SbxVariable* pRes = nullptr; + SbModule* pNamed = nullptr; + // "Extended" search in Runtime Lib + // but only if SbiRuntime has not set the flag + if( !bNoRtl ) + { + if( t == SbxClassType::DontCare || t == SbxClassType::Object ) + { + if( rName.equalsIgnoreAsciiCase( SB_RTLNAME ) ) + { + pRes = pRtl.get(); + } + } + if( !pRes ) + { + pRes = static_cast<SbiStdObject*>(pRtl.get())->Find( rName, t ); + } + if( pRes ) + { + pRes->SetFlag( SbxFlagBits::ExtFound ); + } + } + // Search module + if( !pRes ) + { + for (const auto& pModule: pModules) + { + if( pModule->IsVisible() ) + { + // Remember module for Main() call + // or is the name equal?!? + if( pModule->GetName().equalsIgnoreAsciiCase( rName ) ) + { + if( t == SbxClassType::Object || t == SbxClassType::DontCare ) + { + pRes = pModule.get(); break; + } + pNamed = pModule.get(); + } + // Only variables qualified by the Module Name e.g. Sheet1.foo + // should work for Document && Class type Modules + sal_Int32 nType = pModule->GetModuleType(); + if ( nType == ModuleType::DOCUMENT || nType == ModuleType::FORM ) + { + continue; + } + // otherwise check if the element is available + // unset GBLSEARCH-Flag (due to recursion) + SbxFlagBits nGblFlag = pModule->GetFlags() & SbxFlagBits::GlobalSearch; + pModule->ResetFlag( SbxFlagBits::GlobalSearch ); + pRes = pModule->Find( rName, t ); + pModule->SetFlag( nGblFlag ); + if( pRes ) + { + break; + } + } + } + } + static constexpr OUStringLiteral aMainStr(u"Main"); + if( !pRes && pNamed && ( t == SbxClassType::Method || t == SbxClassType::DontCare ) && + !pNamed->GetName().equalsIgnoreAsciiCase( aMainStr ) ) + { + pRes = pNamed->Find( aMainStr, SbxClassType::Method ); + } + if( !pRes ) + { + pRes = SbxObject::Find( rName, t ); + } + return pRes; +} + +bool StarBASIC::Call( const OUString& rName, SbxArray* pParam ) +{ + bool bRes = SbxObject::Call( rName, pParam ); + if( !bRes ) + { + ErrCode eErr = SbxBase::GetError(); + SbxBase::ResetError(); + if( eErr != ERRCODE_NONE ) + { + RTError( eErr, OUString(), 0, 0, 0 ); + } + } + return bRes; +} + +// Find method via name (e.g. query via BASIC IDE) +SbxBase* StarBASIC::FindSBXInCurrentScope( const OUString& rName ) +{ + if( !GetSbData()->pInst ) + { + return nullptr; + } + if( !GetSbData()->pInst->pRun ) + { + return nullptr; + } + return GetSbData()->pInst->pRun->FindElementExtern( rName ); +} + +void StarBASIC::QuitAndExitApplication() +{ + Stop(); + bQuit = true; +} + +void StarBASIC::Stop() +{ + SbiInstance* p = GetSbData()->pInst; + if( p ) + p->Stop(); +} + +bool StarBASIC::IsRunning() +{ + return GetSbData()->pInst != nullptr; +} + +SbMethod* StarBASIC::GetActiveMethod( sal_uInt16 nLevel ) +{ + if( GetSbData()->pInst ) + { + return GetSbData()->pInst->GetCaller( nLevel ); + } + else + { + return nullptr; + } +} + +SbModule* StarBASIC::GetActiveModule() +{ + if( GetSbData()->pInst && !GetSbData()->bCompilerError ) + { + return GetSbData()->pInst->GetActiveModule(); + } + else + { + return GetSbData()->pCompMod; + } +} + +BasicDebugFlags StarBASIC::BreakPoint( sal_Int32 l, sal_Int32 c1, sal_Int32 c2 ) +{ + SetErrorData( ERRCODE_NONE, l, c1, c2 ); + bBreak = true; + if( GetSbData()->aBreakHdl.IsSet() ) + { + return GetSbData()->aBreakHdl.Call( this ); + } + else + { + return BreakHdl(); + } +} + +BasicDebugFlags StarBASIC::StepPoint( sal_Int32 l, sal_Int32 c1, sal_Int32 c2 ) +{ + SetErrorData( ERRCODE_NONE, l, c1, c2 ); + bBreak = false; + if( GetSbData()->aBreakHdl.IsSet() ) + { + return GetSbData()->aBreakHdl.Call( this ); + } + else + { + return BreakHdl(); + } +} + +BasicDebugFlags StarBASIC::BreakHdl() +{ + return aBreakHdl.IsSet() ? aBreakHdl.Call( this ) : BasicDebugFlags::Continue; +} + +// Calls for error handler and break handler +sal_uInt16 StarBASIC::GetLine() { return GetSbData()->nLine; } +sal_uInt16 StarBASIC::GetCol1() { return GetSbData()->nCol1; } +sal_uInt16 StarBASIC::GetCol2() { return GetSbData()->nCol2; } + +// Specific to error handler +ErrCode const & StarBASIC::GetErrorCode() { return GetSbData()->nCode; } +const OUString& StarBASIC::GetErrorText() { return GetSbData()->aErrMsg; } + +// From 1996-03-29: +// The mapping between the old and the new error codes take place by searching +// through the table SFX_VB_ErrorTab[]. This is indeed not with good performance, +// but it consumes much less memory than corresponding switch blocks. +// Because the conversion of error codes has not to be fast. There is no +// binary search by VB Error -> Error SFX. + +// Map back new error codes to old, Sbx-compatible +sal_uInt16 StarBASIC::GetVBErrorCode( ErrCode nError ) +{ + sal_uInt16 nRet = 0; + + if( SbiRuntime::isVBAEnabled() ) + { + if ( nError == ERRCODE_BASIC_ARRAY_FIX ) + return 10; + else if ( nError == ERRCODE_BASIC_STRING_OVERFLOW ) + return 14; + else if ( nError == ERRCODE_BASIC_EXPR_TOO_COMPLEX ) + return 16; + else if ( nError == ERRCODE_BASIC_OPER_NOT_PERFORM ) + return 17; + else if ( nError == ERRCODE_BASIC_TOO_MANY_DLL ) + return 47; + else if ( nError == ERRCODE_BASIC_LOOP_NOT_INIT ) + return 92; + else + nRet = 0; + } + + // search loop + const SFX_VB_ErrorItem* pErrItem; + sal_uInt16 nIndex = 0; + do + { + pErrItem = SFX_VB_ErrorTab + nIndex; + if( pErrItem->nErrorSFX == nError ) + { + nRet = pErrItem->nErrorVB; + break; + } + nIndex++; + } + while( pErrItem->nErrorVB != 0xFFFF ); // up to end mark + return nRet; +} + +ErrCode StarBASIC::GetSfxFromVBError( sal_uInt16 nError ) +{ + ErrCode nRet = ERRCODE_NONE; + + if( SbiRuntime::isVBAEnabled() ) + { + switch( nError ) + { + case 1: + case 2: + case 4: + case 8: + case 12: + case 73: + return ERRCODE_NONE; + case 10: + return ERRCODE_BASIC_ARRAY_FIX; + case 14: + return ERRCODE_BASIC_STRING_OVERFLOW; + case 16: + return ERRCODE_BASIC_EXPR_TOO_COMPLEX; + case 17: + return ERRCODE_BASIC_OPER_NOT_PERFORM; + case 47: + return ERRCODE_BASIC_TOO_MANY_DLL; + case 92: + return ERRCODE_BASIC_LOOP_NOT_INIT; + default: + nRet = ERRCODE_NONE; + } + } + const SFX_VB_ErrorItem* pErrItem; + sal_uInt16 nIndex = 0; + do + { + pErrItem = SFX_VB_ErrorTab + nIndex; + if( pErrItem->nErrorVB == nError ) + { + nRet = pErrItem->nErrorSFX; + break; + } + else if( pErrItem->nErrorVB > nError ) + { + break; // couldn't found anymore + } + nIndex++; + } + while( pErrItem->nErrorVB != 0xFFFF ); // up to end mark + return nRet; +} + +// set Error- / Break-data +void StarBASIC::SetErrorData( ErrCode nCode, sal_uInt16 nLine, + sal_uInt16 nCol1, sal_uInt16 nCol2 ) +{ + SbiGlobals& aGlobals = *GetSbData(); + aGlobals.nCode = nCode; + aGlobals.nLine = nLine; + aGlobals.nCol1 = nCol1; + aGlobals.nCol2 = nCol2; +} + +void StarBASIC::MakeErrorText( ErrCode nId, std::u16string_view aMsg ) +{ + SolarMutexGuard aSolarGuard; + sal_uInt16 nOldID = GetVBErrorCode( nId ); + + TranslateId pErrorMsg; + for (std::pair<TranslateId, ErrCode> const *pItem = RID_BASIC_START; pItem->second; ++pItem) + { + if (nId == pItem->second) + { + pErrorMsg = pItem->first; + break; + } + } + + if (pErrorMsg) + { + // merge message with additional text + OUString sError = BasResId(pErrorMsg); + OUStringBuffer aMsg1(sError); + // replace argument placeholder with %s + OUString aSrgStr( "$(ARG1)" ); + sal_Int32 nResult = sError.indexOf(aSrgStr); + + if( nResult >= 0 ) + { + aMsg1.remove(nResult, aSrgStr.getLength()); + aMsg1.insert(nResult, aMsg); + } + else if (!aMsg.empty()) + { + // tdf#123144 - create a meaningful error message + aMsg1 = BasResId(STR_ADDITIONAL_INFO) + .replaceFirst("$ERR", aMsg1) + .replaceFirst("$MSG", aMsg); + } + GetSbData()->aErrMsg = aMsg1.makeStringAndClear(); + } + // tdf#123144 - don't use an artificial error message if there is a custom one + else if (!aMsg.empty()) + { + GetSbData()->aErrMsg = aMsg; + } + else if( nOldID != 0 ) + { + OUString aStdMsg = "Error " + OUString::number(nOldID) + + ": No error text available!"; + GetSbData()->aErrMsg = aStdMsg; + } + else + { + GetSbData()->aErrMsg.clear(); + } +} + +bool StarBASIC::CError( ErrCode code, const OUString& rMsg, + sal_Int32 l, sal_Int32 c1, sal_Int32 c2 ) +{ + SolarMutexGuard aSolarGuard; + + // compiler error during runtime -> stop program + if( IsRunning() ) + { + // #109018 Check if running Basic is affected + StarBASIC* pStartedBasic = GetSbData()->pInst->GetBasic(); + if( pStartedBasic != this ) + { + return false; + } + Stop(); + } + + // set flag, so that GlobalRunInit notice the error + GetSbData()->bGlobalInitErr = true; + + // tinker the error message + MakeErrorText( code, rMsg ); + + // Implementation of the code for the string transport to SFX-Error + if( !rMsg.isEmpty() ) + { + code = *new StringErrorInfo( code, rMsg ); + } + SetErrorData( code, l, c1, c2 ); + GetSbData()->bCompilerError = true; + bool bRet; + if( GetSbData()->aErrHdl.IsSet() ) + { + bRet = GetSbData()->aErrHdl.Call( this ); + } + else + { + bRet = ErrorHdl(); + } + GetSbData()->bCompilerError = false; // only true for error handler + return bRet; +} + +bool StarBASIC::RTError( ErrCode code, const OUString& rMsg, sal_Int32 l, sal_Int32 c1, sal_Int32 c2 ) +{ + SolarMutexGuard aSolarGuard; + + ErrCode c = code; + if( c.GetClass() == ErrCodeClass::Compiler ) + { + c = ERRCODE_NONE; + } + MakeErrorText( c, rMsg ); + + // Implementation of the code for the string transport to SFX-Error + if( !rMsg.isEmpty() ) + { + // very confusing, even though MakeErrorText sets up the error text + // seems that this is not used ( if rMsg already has content ) + // In the case of VBA MakeErrorText also formats the error to be a little more + // like vba ( adds an error number etc ) + if ( SbiRuntime::isVBAEnabled() && ( code == ERRCODE_BASIC_COMPAT ) ) + { + OUString aTmp = "\'" + OUString::number(SbxErrObject::getUnoErrObject()->getNumber()) + + "\'\n" + (!GetSbData()->aErrMsg.isEmpty() ? GetSbData()->aErrMsg : rMsg); + code = *new StringErrorInfo( code, aTmp ); + } + else + { + code = *new StringErrorInfo( code, rMsg ); + } + } + + SetErrorData( code, l, c1, c2 ); + if( GetSbData()->aErrHdl.IsSet() ) + { + return GetSbData()->aErrHdl.Call( this ); + } + else + { + return ErrorHdl(); + } +} + +void StarBASIC::Error( ErrCode n, const OUString& rMsg ) +{ + if( GetSbData()->pInst ) + { + GetSbData()->pInst->Error( n, rMsg ); + } +} + +void StarBASIC::FatalError( ErrCode n ) +{ + if( GetSbData()->pInst ) + { + GetSbData()->pInst->FatalError( n ); + } +} + +void StarBASIC::FatalError( ErrCode _errCode, const OUString& _details ) +{ + if( GetSbData()->pInst ) + { + GetSbData()->pInst->FatalError( _errCode, _details ); + } +} + +ErrCode StarBASIC::GetErrBasic() +{ + if( GetSbData()->pInst ) + { + return GetSbData()->pInst->GetErr(); + } + else + { + return ERRCODE_NONE; + } +} + +// make the additional message for the RTL function error accessible +OUString StarBASIC::GetErrorMsg() +{ + if( GetSbData()->pInst ) + { + return GetSbData()->pInst->GetErrorMsg(); + } + else + { + return OUString(); + } +} + +sal_Int32 StarBASIC::GetErl() +{ + if( GetSbData()->pInst ) + { + return GetSbData()->pInst->GetErl(); + } + else + { + return 0; + } +} + +bool StarBASIC::ErrorHdl() +{ + return aErrorHdl.Call( this ); +} + +Link<StarBASIC*,bool> const & StarBASIC::GetGlobalErrorHdl() +{ + return GetSbData()->aErrHdl; +} + +void StarBASIC::SetGlobalErrorHdl( const Link<StarBASIC*,bool>& rLink ) +{ + GetSbData()->aErrHdl = rLink; +} + +void StarBASIC::SetGlobalBreakHdl( const Link<StarBASIC*,BasicDebugFlags>& rLink ) +{ + GetSbData()->aBreakHdl = rLink; +} + +SbxArrayRef const & StarBASIC::getUnoListeners() +{ + if( !xUnoListeners.is() ) + { + xUnoListeners = new SbxArray(); + } + return xUnoListeners; +} + + +bool StarBASIC::LoadData( SvStream& r, sal_uInt16 nVer ) +{ + if( !SbxObject::LoadData( r, nVer ) ) + { + return false; + } + // #95459 Delete dialogs, otherwise endless recursion + // in SbxVariable::GetType() if dialogs are accessed + sal_uInt32 nObjCount = pObjs->Count(); + std::unique_ptr<SbxVariable*[]> ppDeleteTab(new SbxVariable*[ nObjCount ]); + sal_uInt32 nObj; + + for( nObj = 0 ; nObj < nObjCount ; nObj++ ) + { + SbxVariable* pVar = pObjs->Get(nObj); + StarBASIC* pBasic = dynamic_cast<StarBASIC*>( pVar ); + ppDeleteTab[nObj] = pBasic ? nullptr : pVar; + } + for( nObj = 0 ; nObj < nObjCount ; nObj++ ) + { + SbxVariable* pVar = ppDeleteTab[nObj]; + if( pVar ) + { + pObjs->Remove( pVar ); + } + } + ppDeleteTab.reset(); + + sal_uInt16 nMod(0); + pModules.clear(); + r.ReadUInt16( nMod ); + const size_t nMinSbxSize(14); + const size_t nMaxPossibleEntries = r.remainingSize() / nMinSbxSize; + if (nMod > nMaxPossibleEntries) + { + nMod = nMaxPossibleEntries; + SAL_WARN("basic", "Parsing error: " << nMaxPossibleEntries << + " max possible entries, but " << nMod << " claimed, truncating"); + } + for (sal_uInt16 i = 0; i < nMod; ++i) + { + SbxBaseRef pBase = SbxBase::Load( r ); + SbModule* pMod = dynamic_cast<SbModule*>(pBase.get()); + if( !pMod ) + { + return false; + } + else if( dynamic_cast<const SbJScriptModule*>( pMod) != nullptr ) + { + // assign Ref, so that pMod will be deleted + SbModuleRef xDeleteRef = pMod; + } + else + { + pMod->SetParent( this ); + pModules.emplace_back(pMod ); + } + } + // HACK for SFX-Bullshit! + SbxVariable* p = Find( "FALSE", SbxClassType::Property ); + if( p ) + { + Remove( p ); + } + p = Find( "TRUE", SbxClassType::Property ); + if( p ) + { + Remove( p ); + } + // End of the hacks! + // Search via StarBASIC is at all times global + DBG_ASSERT( IsSet( SbxFlagBits::GlobalSearch ), "Basic loaded without GBLSEARCH" ); + SetFlag( SbxFlagBits::GlobalSearch ); + return true; +} + +bool StarBASIC::StoreData( SvStream& r ) const +{ + if( !SbxObject::StoreData( r ) ) + { + return false; + } + assert(pModules.size() < SAL_MAX_UINT16); + r.WriteUInt16( static_cast<sal_uInt16>(pModules.size())); + for( const auto& rpModule: pModules ) + { + if( !rpModule->Store( r ) ) + { + return false; + } + } + return true; +} + +bool StarBASIC::GetUNOConstant( const OUString& rName, css::uno::Any& aOut ) +{ + bool bRes = false; + SbUnoObject* pGlobs = dynamic_cast<SbUnoObject*>( Find( rName, SbxClassType::DontCare ) ); + if ( pGlobs ) + { + aOut = pGlobs->getUnoAny(); + bRes = true; + } + return bRes; +} + +Reference< frame::XModel > StarBASIC::GetModelFromBasic( SbxObject* pBasic ) +{ + OSL_PRECOND( pBasic != nullptr, "getModelFromBasic: illegal call!" ); + if ( !pBasic ) + { + return nullptr; + } + // look for the ThisComponent variable, first in the parent (which + // might be the document's Basic), then in the parent's parent (which might be + // the application Basic) + static const OUStringLiteral sThisComponent( u"ThisComponent"); + SbxVariable* pThisComponent = nullptr; + + SbxObject* pLookup = pBasic->GetParent(); + while ( pLookup && !pThisComponent ) + { + pThisComponent = pLookup->Find( sThisComponent, SbxClassType::Object ); + pLookup = pLookup->GetParent(); + } + if ( !pThisComponent ) + { + SAL_WARN("basic", "Failed to get ThisComponent"); + // the application Basic, at the latest, should have this variable + return nullptr; + } + + Any aThisComponentAny( sbxToUnoValue( pThisComponent ) ); + Reference< frame::XModel > xModel( aThisComponentAny, UNO_QUERY ); + if ( !xModel.is() ) + { + // it's no XModel. Okay, ThisComponent nowadays is allowed to be a controller. + Reference< frame::XController > xController( aThisComponentAny, UNO_QUERY ); + if ( xController.is() ) + { + xModel = xController->getModel(); + } + } + if ( !xModel.is() ) + { + return nullptr; + } + + return xModel; +} + +void StarBASIC::DetachAllDocBasicItems() +{ + for (auto const& item : gaDocBasicItems) + { + DocBasicItemRef xItem = item.second; + xItem->setDisposed(true); + } +} + +// #118116 Implementation Collection object + + +constexpr OUStringLiteral pCountStr = u"Count"; +constexpr OUStringLiteral pAddStr = u"Add"; +constexpr OUStringLiteral pItemStr = u"Item"; +constexpr OUStringLiteral pRemoveStr = u"Remove"; +constexpr sal_uInt16 nCountHash = SbxVariable::MakeHashCode(pCountStr); +constexpr sal_uInt16 nAddHash = SbxVariable::MakeHashCode(pAddStr); +constexpr sal_uInt16 nItemHash = SbxVariable::MakeHashCode(pItemStr); +constexpr sal_uInt16 nRemoveHash = SbxVariable::MakeHashCode(pRemoveStr); + +SbxInfoRef BasicCollection::xAddInfo; +SbxInfoRef BasicCollection::xItemInfo; + +BasicCollection::BasicCollection( const OUString& rClass ) + : SbxObject( rClass ) +{ + Initialize(); +} + +BasicCollection::~BasicCollection() +{} + +void BasicCollection::Clear() +{ + SbxObject::Clear(); + Initialize(); +} + +void BasicCollection::Initialize() +{ + xItemArray = new SbxArray(); + SetType( SbxOBJECT ); + SetFlag( SbxFlagBits::Fixed ); + ResetFlag( SbxFlagBits::Write ); + SbxVariable* p; + p = Make( pCountStr, SbxClassType::Property, SbxINTEGER ); + p->ResetFlag( SbxFlagBits::Write ); + p->SetFlag( SbxFlagBits::DontStore ); + p = Make( pAddStr, SbxClassType::Method, SbxEMPTY ); + p->SetFlag( SbxFlagBits::DontStore ); + p = Make( pItemStr, SbxClassType::Method, SbxVARIANT ); + p->SetFlag( SbxFlagBits::DontStore ); + p = Make( pRemoveStr, SbxClassType::Method, SbxEMPTY ); + p->SetFlag( SbxFlagBits::DontStore ); + if ( !xAddInfo.is() ) + { + xAddInfo = new SbxInfo; + xAddInfo->AddParam( "Item", SbxVARIANT ); + xAddInfo->AddParam( "Key", SbxVARIANT, SbxFlagBits::Read | SbxFlagBits::Optional ); + xAddInfo->AddParam( "Before", SbxVARIANT, SbxFlagBits::Read | SbxFlagBits::Optional ); + xAddInfo->AddParam( "After", SbxVARIANT, SbxFlagBits::Read | SbxFlagBits::Optional ); + } + if ( !xItemInfo.is() ) + { + xItemInfo = new SbxInfo; + xItemInfo->AddParam( "Index", SbxVARIANT, SbxFlagBits::Read | SbxFlagBits::Optional); + } +} + +void BasicCollection::Notify( SfxBroadcaster& rCst, const SfxHint& rHint ) +{ + const SbxHint* p = dynamic_cast<const SbxHint*>(&rHint); + if( p ) + { + const SfxHintId nId = p->GetId(); + bool bRead = nId == SfxHintId::BasicDataWanted; + bool bWrite = nId == SfxHintId::BasicDataChanged; + bool bRequestInfo = nId == SfxHintId::BasicInfoWanted; + SbxVariable* pVar = p->GetVar(); + SbxArray* pArg = pVar->GetParameters(); + OUString aVarName( pVar->GetName() ); + if( bRead || bWrite ) + { + if( pVar->GetHashCode() == nCountHash + && aVarName.equalsIgnoreAsciiCase( pCountStr ) ) + { + pVar->PutLong(xItemArray->Count()); + } + else if( pVar->GetHashCode() == nAddHash + && aVarName.equalsIgnoreAsciiCase( pAddStr ) ) + { + CollAdd( pArg ); + } + else if( pVar->GetHashCode() == nItemHash + && aVarName.equalsIgnoreAsciiCase( pItemStr ) ) + { + CollItem( pArg ); + } + else if( pVar->GetHashCode() == nRemoveHash + && aVarName.equalsIgnoreAsciiCase( pRemoveStr ) ) + { + CollRemove( pArg ); + } + else + { + SbxObject::Notify( rCst, rHint ); + } + return; + } + else if ( bRequestInfo ) + { + if( pVar->GetHashCode() == nAddHash + && aVarName.equalsIgnoreAsciiCase( pAddStr ) ) + { + pVar->SetInfo( xAddInfo.get() ); + } + else if( pVar->GetHashCode() == nItemHash + && aVarName.equalsIgnoreAsciiCase( pItemStr ) ) + { + pVar->SetInfo( xItemInfo.get() ); + } + } + } + SbxObject::Notify( rCst, rHint ); +} + +sal_Int32 BasicCollection::implGetIndex( SbxVariable const * pIndexVar ) +{ + sal_Int32 nIndex = -1; + if( pIndexVar->GetType() == SbxSTRING ) + { + nIndex = implGetIndexForName( pIndexVar->GetOUString() ); + } + else + { + nIndex = pIndexVar->GetLong() - 1; + } + return nIndex; +} + +sal_Int32 BasicCollection::implGetIndexForName(const OUString& rName) +{ + sal_Int32 nCount = xItemArray->Count(); + sal_Int32 nNameHash = MakeHashCode( rName ); + + // tdf#144245 - case-insensitive operation for non-ASCII characters + OUString aNameCI; // Only initialize when matching hash found + + for( sal_Int32 i = 0 ; i < nCount ; i++ ) + { + SbxVariable* pVar = xItemArray->Get(i); + if (pVar->GetHashCode() == nNameHash) + { + if (aNameCI.isEmpty() && !rName.isEmpty()) + aNameCI = SbxVariable::NameToCaseInsensitiveName(rName); + if (aNameCI == pVar->GetName(SbxNameType::CaseInsensitive)) + return i; + } + } + return -1; +} + +void BasicCollection::CollAdd( SbxArray* pPar_ ) +{ + sal_uInt32 nCount = pPar_->Count(); + if( nCount < 2 || nCount > 5 ) + { + SetError( ERRCODE_BASIC_WRONG_ARGS ); + return; + } + + SbxVariable* pItem = pPar_->Get(1); + if( pItem ) + { + sal_uInt32 nNextIndex; + if( nCount < 4 ) + { + nNextIndex = xItemArray->Count(); + } + else + { + SbxVariable* pBefore = pPar_->Get(3); + if( nCount == 5 ) + { + if( !( pBefore->IsErr() || ( pBefore->GetType() == SbxEMPTY ) ) ) + { + SetError( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + SbxVariable* pAfter = pPar_->Get(4); + sal_Int32 nAfterIndex = implGetIndex( pAfter ); + if( nAfterIndex == -1 ) + { + SetError( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + nNextIndex = sal::static_int_cast<sal_uInt32>(nAfterIndex + 1); + } + else // if( nCount == 4 ) + { + sal_Int32 nBeforeIndex = implGetIndex( pBefore ); + if( nBeforeIndex == -1 ) + { + SetError( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + nNextIndex = sal::static_int_cast<sal_uInt32>(nBeforeIndex); + } + } + + auto pNewItem = tools::make_ref<SbxVariable>( *pItem ); + if( nCount >= 3 ) + { + SbxVariable* pKey = pPar_->Get(2); + if( !( pKey->IsErr() || ( pKey->GetType() == SbxEMPTY ) ) ) + { + if( pKey->GetType() != SbxSTRING ) + { + SetError( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + OUString aKey = pKey->GetOUString(); + if( implGetIndexForName( aKey ) != -1 ) + { + SetError( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + pNewItem->SetName( aKey ); + } + } + pNewItem->SetFlag( SbxFlagBits::ReadWrite ); + xItemArray->Insert(pNewItem.get(), nNextIndex); + } + else + { + SetError( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } +} + +void BasicCollection::CollItem( SbxArray* pPar_ ) +{ + if (pPar_->Count() != 2) + { + SetError( ERRCODE_BASIC_WRONG_ARGS ); + return; + } + SbxVariable* pRes = nullptr; + SbxVariable* p = pPar_->Get(1); + sal_Int32 nIndex = implGetIndex( p ); + if (nIndex >= 0 && o3tl::make_unsigned(nIndex) < xItemArray->Count()) + { + pRes = xItemArray->Get(nIndex); + } + if( !pRes ) + { + SetError( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + *(pPar_->Get(0)) = *pRes; + } +} + +void BasicCollection::CollRemove( SbxArray* pPar_ ) +{ + if (pPar_ == nullptr || pPar_->Count() != 2) + { + SetError( ERRCODE_BASIC_WRONG_ARGS ); + return; + } + + SbxVariable* p = pPar_->Get(1); + sal_Int32 nIndex = implGetIndex( p ); + if (nIndex >= 0 && o3tl::make_unsigned(nIndex) < xItemArray->Count()) + { + xItemArray->Remove( nIndex ); + + // Correct for stack if necessary + SbiInstance* pInst = GetSbData()->pInst; + SbiRuntime* pRT = pInst ? pInst->pRun : nullptr; + if( pRT ) + { + SbiForStack* pStack = pRT->FindForStackItemForCollection( this ); + if( pStack != nullptr ) + { + if( pStack->nCurCollectionIndex >= nIndex ) + { + --pStack->nCurCollectionIndex; + } + } + } + } + else + { + SetError( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/classes/sbintern.cxx b/basic/source/classes/sbintern.cxx new file mode 100644 index 000000000..fd72949d5 --- /dev/null +++ b/basic/source/classes/sbintern.cxx @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sbintern.hxx> +#include <sbunoobj.hxx> +#include <basic/basmgr.hxx> + +SbiGlobals* SbiGlobals::pGlobals = nullptr; + +SbiGlobals* GetSbData() +{ + if (!SbiGlobals::pGlobals) + SbiGlobals::pGlobals = new SbiGlobals; + return SbiGlobals::pGlobals; +} + +SbiGlobals::SbiGlobals() + : pInst(nullptr) + , pMod(nullptr) + , pCompMod(nullptr) // JSM + , nInst(0) + , nCode(ERRCODE_NONE) + , nLine(0) + , nCol1(0) + , nCol2(0) + , bCompilerError(false) + , bGlobalInitErr(false) + , bRunInit(false) + , bBlockCompilerError(false) + , pMSOMacroRuntimLib(nullptr) +{ +} + +SbiGlobals::~SbiGlobals() = default; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 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: */ diff --git a/basic/source/classes/sbxmod.cxx b/basic/source/classes/sbxmod.cxx new file mode 100644 index 000000000..af2b4b2c9 --- /dev/null +++ b/basic/source/classes/sbxmod.cxx @@ -0,0 +1,2676 @@ +/* -*- 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 <utility> +#include <vcl/svapp.hxx> +#include <tools/stream.hxx> +#include <tools/diagnose_ex.h> +#include <svl/SfxBroadcaster.hxx> +#include <basic/codecompletecache.hxx> +#include <basic/sbx.hxx> +#include <basic/sbuno.hxx> +#include <sbjsmeth.hxx> +#include <sbjsmod.hxx> +#include <sbintern.hxx> +#include <sbprop.hxx> +#include <image.hxx> +#include <opcodes.hxx> +#include <runtime.hxx> +#include <token.hxx> +#include <sbunoobj.hxx> + +#include <sal/log.hxx> + +#include <basic/sberrors.hxx> +#include <sbobjmod.hxx> +#include <basic/vbahelper.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/implbase.hxx> +#include <unotools/eventcfg.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/script/ModuleType.hpp> +#include <com/sun/star/script/vba/XVBACompatibility.hpp> +#include <com/sun/star/script/vba/VBAScriptEventId.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/document/XDocumentEventBroadcaster.hpp> +#include <com/sun/star/document/XDocumentEventListener.hpp> + +#ifdef UNX +#include <sys/resource.h> +#endif + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/asyncquithandler.hxx> +#include <map> +#include <com/sun/star/reflection/ProxyFactory.hpp> +#include <com/sun/star/uno/XAggregation.hpp> +#include <com/sun/star/script/XInvocation.hpp> + +#include <com/sun/star/awt/DialogProvider.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <ooo/vba/VbQueryClose.hpp> +#include <memory> +#include <sbxmod.hxx> +#include <parser.hxx> + +#include <limits> + +using namespace com::sun::star; +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::uno; + +typedef ::cppu::WeakImplHelper< XInvocation > DocObjectWrapper_BASE; +typedef std::map< sal_Int16, Any > OutParamMap; + +namespace { + +class DocObjectWrapper : public DocObjectWrapper_BASE +{ + Reference< XAggregation > m_xAggProxy; + Reference< XInvocation > m_xAggInv; + Reference< XTypeProvider > m_xAggregateTypeProv; + Sequence< Type > m_Types; + SbModule* m_pMod; + /// @throws css::uno::RuntimeException + SbMethodRef getMethod( const OUString& aName ); + /// @throws css::uno::RuntimeException + SbPropertyRef getProperty( const OUString& aName ); + +public: + explicit DocObjectWrapper( SbModule* pMod ); + + virtual Sequence< sal_Int8 > SAL_CALL getImplementationId() override + { + return css::uno::Sequence<sal_Int8>(); + } + + virtual Reference< XIntrospectionAccess > SAL_CALL getIntrospection( ) override; + + virtual Any SAL_CALL invoke( const OUString& aFunctionName, const Sequence< Any >& aParams, Sequence< ::sal_Int16 >& aOutParamIndex, Sequence< Any >& aOutParam ) override; + virtual void SAL_CALL setValue( const OUString& aPropertyName, const Any& aValue ) override; + virtual Any SAL_CALL getValue( const OUString& aPropertyName ) override; + virtual sal_Bool SAL_CALL hasMethod( const OUString& aName ) override; + virtual sal_Bool SAL_CALL hasProperty( const OUString& aName ) override; + virtual Any SAL_CALL queryInterface( const Type& aType ) override; + + virtual Sequence< Type > SAL_CALL getTypes() override; +}; + +} + +DocObjectWrapper::DocObjectWrapper( SbModule* pVar ) : m_pMod( pVar ) +{ + SbObjModule* pMod = dynamic_cast<SbObjModule*>( pVar ); + if ( !pMod ) + return; + + if ( pMod->GetModuleType() != ModuleType::DOCUMENT ) + return; + + // Use proxy factory service to create aggregatable proxy. + SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pMod->GetObject() ); + Reference< XInterface > xIf; + if ( pUnoObj ) + { + Any aObj = pUnoObj->getUnoAny(); + aObj >>= xIf; + if ( xIf.is() ) + { + m_xAggregateTypeProv.set( xIf, UNO_QUERY ); + m_xAggInv.set( xIf, UNO_QUERY ); + } + } + if ( xIf.is() ) + { + try + { + Reference< XProxyFactory > xProxyFac = ProxyFactory::create( comphelper::getProcessComponentContext() ); + m_xAggProxy = xProxyFac->createProxy( xIf ); + } + catch(const Exception& ) + { + TOOLS_WARN_EXCEPTION( "basic", "DocObjectWrapper::DocObjectWrapper" ); + } + } + + if ( !m_xAggProxy.is() ) + return; + + osl_atomic_increment( &m_refCount ); + + /* i35609 - Fix crash on Solaris. The setDelegator call needs + to be in its own block to ensure that all temporary Reference + instances that are acquired during the call are released + before m_refCount is decremented again */ + { + m_xAggProxy->setDelegator( static_cast< cppu::OWeakObject * >( this ) ); + } + + osl_atomic_decrement( &m_refCount ); +} + +Sequence< Type > SAL_CALL DocObjectWrapper::getTypes() +{ + if ( !m_Types.hasElements() ) + { + Sequence< Type > sTypes; + if ( m_xAggregateTypeProv.is() ) + { + sTypes = m_xAggregateTypeProv->getTypes(); + } + m_Types = comphelper::concatSequences(sTypes, + Sequence { cppu::UnoType<XInvocation>::get() }); + } + return m_Types; +} + +Reference< XIntrospectionAccess > SAL_CALL +DocObjectWrapper::getIntrospection( ) +{ + return nullptr; +} + +Any SAL_CALL +DocObjectWrapper::invoke( const OUString& aFunctionName, const Sequence< Any >& aParams, Sequence< ::sal_Int16 >& aOutParamIndex, Sequence< Any >& aOutParam ) +{ + if ( m_xAggInv.is() && m_xAggInv->hasMethod( aFunctionName ) ) + return m_xAggInv->invoke( aFunctionName, aParams, aOutParamIndex, aOutParam ); + SbMethodRef pMethod = getMethod( aFunctionName ); + if ( !pMethod.is() ) + throw RuntimeException("DocObjectWrapper::invoke - Could not get the method reference!"); + // check number of parameters + sal_Int32 nParamsCount = aParams.getLength(); + SbxInfo* pInfo = pMethod->GetInfo(); + if ( pInfo ) + { + sal_Int32 nSbxOptional = 0; + sal_uInt16 n = 1; + for ( const SbxParamInfo* pParamInfo = pInfo->GetParam( n ); pParamInfo; pParamInfo = pInfo->GetParam( ++n ) ) + { + if ( pParamInfo->nFlags & SbxFlagBits::Optional ) + ++nSbxOptional; + else + nSbxOptional = 0; + } + sal_Int32 nSbxCount = n - 1; + if ( nParamsCount < nSbxCount - nSbxOptional ) + { + throw RuntimeException( "wrong number of parameters!" ); + } + } + // set parameters + SbxArrayRef xSbxParams; + if ( nParamsCount > 0 ) + { + xSbxParams = new SbxArray; + const Any* pParams = aParams.getConstArray(); + for ( sal_Int32 i = 0; i < nParamsCount; ++i ) + { + SbxVariableRef xSbxVar = new SbxVariable( SbxVARIANT ); + unoToSbxValue( xSbxVar.get(), pParams[i] ); + xSbxParams->Put(xSbxVar.get(), static_cast<sal_uInt32>(i) + 1); + + // Enable passing by ref + if ( xSbxVar->GetType() != SbxVARIANT ) + xSbxVar->SetFlag( SbxFlagBits::Fixed ); + } + } + if ( xSbxParams.is() ) + pMethod->SetParameters( xSbxParams.get() ); + + // call method + SbxVariableRef xReturn = new SbxVariable; + + pMethod->Call( xReturn.get() ); + Any aReturn; + // get output parameters + if ( xSbxParams.is() ) + { + SbxInfo* pInfo_ = pMethod->GetInfo(); + if ( pInfo_ ) + { + OutParamMap aOutParamMap; + for (sal_uInt32 n = 1, nCount = xSbxParams->Count(); n < nCount; ++n) + { + assert(n <= std::numeric_limits<sal_uInt16>::max()); + const SbxParamInfo* pParamInfo = pInfo_->GetParam( sal::static_int_cast<sal_uInt16>(n) ); + if ( pParamInfo && ( pParamInfo->eType & SbxBYREF ) != 0 ) + { + SbxVariable* pVar = xSbxParams->Get(n); + if ( pVar ) + { + SbxVariableRef xVar = pVar; + aOutParamMap.emplace( n - 1, sbxToUnoValue( xVar.get() ) ); + } + } + } + sal_Int32 nOutParamCount = aOutParamMap.size(); + aOutParamIndex.realloc( nOutParamCount ); + aOutParam.realloc( nOutParamCount ); + sal_Int16* pOutParamIndex = aOutParamIndex.getArray(); + Any* pOutParam = aOutParam.getArray(); + for (auto const& outParam : aOutParamMap) + { + *pOutParamIndex = outParam.first; + *pOutParam = outParam.second; + ++pOutParamIndex; + ++pOutParam; + } + } + } + + // get return value + aReturn = sbxToUnoValue( xReturn.get() ); + + pMethod->SetParameters( nullptr ); + + return aReturn; +} + +void SAL_CALL +DocObjectWrapper::setValue( const OUString& aPropertyName, const Any& aValue ) +{ + if ( m_xAggInv.is() && m_xAggInv->hasProperty( aPropertyName ) ) + return m_xAggInv->setValue( aPropertyName, aValue ); + + SbPropertyRef pProperty = getProperty( aPropertyName ); + if ( !pProperty.is() ) + throw UnknownPropertyException(aPropertyName); + unoToSbxValue( pProperty.get(), aValue ); +} + +Any SAL_CALL +DocObjectWrapper::getValue( const OUString& aPropertyName ) +{ + if ( m_xAggInv.is() && m_xAggInv->hasProperty( aPropertyName ) ) + return m_xAggInv->getValue( aPropertyName ); + + SbPropertyRef pProperty = getProperty( aPropertyName ); + if ( !pProperty.is() ) + throw UnknownPropertyException(aPropertyName); + + SbxVariable* pProp = pProperty.get(); + if ( pProp->GetType() == SbxEMPTY ) + pProperty->Broadcast( SfxHintId::BasicDataWanted ); + + Any aRet = sbxToUnoValue( pProp ); + return aRet; +} + +sal_Bool SAL_CALL +DocObjectWrapper::hasMethod( const OUString& aName ) +{ + if ( m_xAggInv.is() && m_xAggInv->hasMethod( aName ) ) + return true; + return getMethod( aName ).is(); +} + +sal_Bool SAL_CALL +DocObjectWrapper::hasProperty( const OUString& aName ) +{ + bool bRes = false; + if ( m_xAggInv.is() && m_xAggInv->hasProperty( aName ) ) + bRes = true; + else bRes = getProperty( aName ).is(); + return bRes; +} + +Any SAL_CALL DocObjectWrapper::queryInterface( const Type& aType ) +{ + Any aRet = DocObjectWrapper_BASE::queryInterface( aType ); + if ( aRet.hasValue() ) + return aRet; + else if ( m_xAggProxy.is() ) + aRet = m_xAggProxy->queryAggregation( aType ); + return aRet; +} + +SbMethodRef DocObjectWrapper::getMethod( const OUString& aName ) +{ + SbMethodRef pMethod; + if ( m_pMod ) + { + SbxFlagBits nSaveFlgs = m_pMod->GetFlags(); + // Limit search to this module + m_pMod->ResetFlag( SbxFlagBits::GlobalSearch ); + pMethod = dynamic_cast<SbMethod*>(m_pMod->SbModule::Find(aName, SbxClassType::Method)); + m_pMod->SetFlags( nSaveFlgs ); + } + + return pMethod; +} + +SbPropertyRef DocObjectWrapper::getProperty( const OUString& aName ) +{ + SbPropertyRef pProperty; + if ( m_pMod ) + { + SbxFlagBits nSaveFlgs = m_pMod->GetFlags(); + // Limit search to this module. + m_pMod->ResetFlag( SbxFlagBits::GlobalSearch ); + pProperty = dynamic_cast<SbProperty*>(m_pMod->SbModule::Find(aName, SbxClassType::Property)); + m_pMod->SetFlag( nSaveFlgs ); + } + + return pProperty; +} + + +uno::Reference< frame::XModel > getDocumentModel( StarBASIC* pb ) +{ + uno::Reference< frame::XModel > xModel; + if( pb && pb->IsDocBasic() ) + { + uno::Any aDoc; + if( pb->GetUNOConstant( "ThisComponent", aDoc ) ) + xModel.set( aDoc, uno::UNO_QUERY ); + } + return xModel; +} + +static uno::Reference< vba::XVBACompatibility > getVBACompatibility( const uno::Reference< frame::XModel >& rxModel ) +{ + uno::Reference< vba::XVBACompatibility > xVBACompat; + try + { + uno::Reference< beans::XPropertySet > xModelProps( rxModel, uno::UNO_QUERY_THROW ); + xVBACompat.set( xModelProps->getPropertyValue( "BasicLibraries" ), uno::UNO_QUERY ); + } + catch(const uno::Exception& ) + { + } + return xVBACompat; +} + +static bool getDefaultVBAMode( StarBASIC* pb ) +{ + uno::Reference< frame::XModel > xModel( getDocumentModel( pb ) ); + if (!xModel.is()) + return false; + uno::Reference< vba::XVBACompatibility > xVBACompat = getVBACompatibility( xModel ); + return xVBACompat.is() && xVBACompat->getVBACompatibilityMode(); +} + +// A Basic module has set EXTSEARCH, so that the elements, that the module contains, +// could be found from other module. + +SbModule::SbModule( const OUString& rName, bool bVBACompat ) + : SbxObject( "StarBASICModule" ), + pBreaks(nullptr), mbVBACompat( bVBACompat ), bIsProxyModule( false ) +{ + SetName( rName ); + SetFlag( SbxFlagBits::ExtSearch | SbxFlagBits::GlobalSearch ); + SetModuleType( script::ModuleType::NORMAL ); + + // #i92642: Set name property to initial name + SbxVariable* pNameProp = pProps->Find( "Name", SbxClassType::Property ); + if( pNameProp != nullptr ) + { + pNameProp->PutString( GetName() ); + } +} + +SbModule::~SbModule() +{ + SAL_INFO("basic","Module named " << GetName() << " is destructing"); + pImage.reset(); + delete pBreaks; + pClassData.reset(); + mxWrapper = nullptr; +} + +uno::Reference< script::XInvocation > const & +SbModule::GetUnoModule() +{ + if ( !mxWrapper.is() ) + mxWrapper = new DocObjectWrapper( this ); + + SAL_INFO("basic","Module named " << GetName() << " returning wrapper mxWrapper (0x" << mxWrapper.get() <<")" ); + return mxWrapper; +} + +bool SbModule::IsCompiled() const +{ + return pImage != nullptr; +} + +const SbxObject* SbModule::FindType( const OUString& aTypeName ) const +{ + return pImage ? pImage->FindType( aTypeName ) : nullptr; +} + + +// From the code generator: deletion of images and the opposite of validation for entries + +void SbModule::StartDefinitions() +{ + pImage.reset(); + if( pClassData ) + pClassData->clear(); + + // methods and properties persist, but they are invalid; + // at least are the information under certain conditions clogged + sal_uInt32 i; + for (i = 0; i < pMethods->Count(); i++) + { + SbMethod* p = dynamic_cast<SbMethod*>(pMethods->Get(i)); + if( p ) + p->bInvalid = true; + } + for (i = 0; i < pProps->Count();) + { + SbProperty* p = dynamic_cast<SbProperty*>(pProps->Get(i)); + if( p ) + pProps->Remove( i ); + else + i++; + } +} + +// request/create method + +SbMethod* SbModule::GetMethod( const OUString& rName, SbxDataType t ) +{ + SbxVariable* p = pMethods->Find( rName, SbxClassType::Method ); + SbMethod* pMeth = dynamic_cast<SbMethod*>( p ); + if( p && !pMeth ) + { + pMethods->Remove( p ); + } + if( !pMeth ) + { + pMeth = new SbMethod( rName, t, this ); + pMeth->SetParent( this ); + pMeth->SetFlags( SbxFlagBits::Read ); + pMethods->Put(pMeth, pMethods->Count()); + StartListening(pMeth->GetBroadcaster(), DuplicateHandling::Prevent); + } + // The method is per default valid, because it could be + // created from the compiler (code generator) as well. + pMeth->bInvalid = false; + pMeth->ResetFlag( SbxFlagBits::Fixed ); + pMeth->SetFlag( SbxFlagBits::Write ); + pMeth->SetType( t ); + pMeth->ResetFlag( SbxFlagBits::Write ); + if( t != SbxVARIANT ) + { + pMeth->SetFlag( SbxFlagBits::Fixed ); + } + return pMeth; +} + +SbMethod* SbModule::FindMethod( const OUString& rName, SbxClassType t ) +{ + return dynamic_cast<SbMethod*> (pMethods->Find( rName, t )); +} + + +// request/create property + +SbProperty* SbModule::GetProperty( const OUString& rName, SbxDataType t ) +{ + SbxVariable* p = pProps->Find( rName, SbxClassType::Property ); + SbProperty* pProp = dynamic_cast<SbProperty*>( p ); + if( p && !pProp ) + { + pProps->Remove( p ); + } + if( !pProp ) + { + pProp = new SbProperty( rName, t, this ); + pProp->SetFlag( SbxFlagBits::ReadWrite ); + pProp->SetParent( this ); + pProps->Put(pProp, pProps->Count()); + StartListening(pProp->GetBroadcaster(), DuplicateHandling::Prevent); + } + return pProp; +} + +void SbModule::GetProcedureProperty( const OUString& rName, SbxDataType t ) +{ + SbxVariable* p = pProps->Find( rName, SbxClassType::Property ); + SbProcedureProperty* pProp = dynamic_cast<SbProcedureProperty*>( p ); + if( p && !pProp ) + { + pProps->Remove( p ); + } + if( !pProp ) + { + tools::SvRef<SbProcedureProperty> pNewProp = new SbProcedureProperty( rName, t ); + pNewProp->SetFlag( SbxFlagBits::ReadWrite ); + pNewProp->SetParent( this ); + pProps->Put(pNewProp.get(), pProps->Count()); + StartListening(pNewProp->GetBroadcaster(), DuplicateHandling::Prevent); + } +} + +void SbModule::GetIfaceMapperMethod( const OUString& rName, SbMethod* pImplMeth ) +{ + SbxVariable* p = pMethods->Find( rName, SbxClassType::Method ); + SbIfaceMapperMethod* pMapperMethod = dynamic_cast<SbIfaceMapperMethod*>( p ); + if( p && !pMapperMethod ) + { + pMethods->Remove( p ); + } + if( !pMapperMethod ) + { + pMapperMethod = new SbIfaceMapperMethod( rName, pImplMeth ); + pMapperMethod->SetParent( this ); + pMapperMethod->SetFlags( SbxFlagBits::Read ); + pMethods->Put(pMapperMethod, pMethods->Count()); + } + pMapperMethod->bInvalid = false; +} + +SbIfaceMapperMethod::~SbIfaceMapperMethod() +{ +} + + +// From the code generator: remove invalid entries + +void SbModule::EndDefinitions( bool bNewState ) +{ + for (sal_uInt32 i = 0; i < pMethods->Count();) + { + SbMethod* p = dynamic_cast<SbMethod*>(pMethods->Get(i)); + if( p ) + { + if( p->bInvalid ) + { + pMethods->Remove( p ); + } + else + { + p->bInvalid = bNewState; + i++; + } + } + else + i++; + } + SetModified( true ); +} + +void SbModule::Clear() +{ + pImage.reset(); + if( pClassData ) + pClassData->clear(); + SbxObject::Clear(); +} + + +SbxVariable* SbModule::Find( const OUString& rName, SbxClassType t ) +{ + // make sure a search in an uninstantiated class module will fail + SbxVariable* pRes = SbxObject::Find( rName, t ); + if ( bIsProxyModule && !GetSbData()->bRunInit ) + { + return nullptr; + } + if( !pRes && pImage ) + { + SbiInstance* pInst = GetSbData()->pInst; + if( pInst && pInst->IsCompatibility() ) + { + // Put enum types as objects into module, + // allows MyEnum.First notation + SbxArrayRef xArray = pImage->GetEnums(); + if( xArray.is() ) + { + SbxVariable* pEnumVar = xArray->Find( rName, SbxClassType::DontCare ); + SbxObject* pEnumObject = dynamic_cast<SbxObject*>( pEnumVar ); + if( pEnumObject ) + { + bool bPrivate = pEnumObject->IsSet( SbxFlagBits::Private ); + OUString aEnumName = pEnumObject->GetName(); + + pRes = new SbxVariable( SbxOBJECT ); + pRes->SetName( aEnumName ); + pRes->SetParent( this ); + pRes->SetFlag( SbxFlagBits::Read ); + if( bPrivate ) + { + pRes->SetFlag( SbxFlagBits::Private ); + } + pRes->PutObject( pEnumObject ); + } + } + } + } + return pRes; +} + +// Parent and BASIC are one! + +void SbModule::SetParent( SbxObject* p ) +{ + pParent = p; +} + +void SbModule::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + const SbxHint* pHint = dynamic_cast<const SbxHint*>(&rHint); + if( !pHint ) + return; + + SbxVariable* pVar = pHint->GetVar(); + SbProperty* pProp = dynamic_cast<SbProperty*>( pVar ); + SbMethod* pMeth = dynamic_cast<SbMethod*>( pVar ); + SbProcedureProperty* pProcProperty = dynamic_cast<SbProcedureProperty*>( pVar ); + if( pProcProperty ) + { + + if( pHint->GetId() == SfxHintId::BasicDataWanted ) + { + OUString aProcName = "Property Get " + + pProcProperty->GetName(); + + SbxVariable* pMethVar = Find( aProcName, SbxClassType::Method ); + if( pMethVar ) + { + SbxValues aVals; + aVals.eType = SbxVARIANT; + + SbxArray* pArg = pVar->GetParameters(); + sal_uInt32 nVarParCount = (pArg != nullptr) ? pArg->Count() : 0; + if( nVarParCount > 1 ) + { + auto xMethParameters = tools::make_ref<SbxArray>(); + xMethParameters->Put(pMethVar, 0); // Method as parameter 0 + for( sal_uInt32 i = 1 ; i < nVarParCount ; ++i ) + { + SbxVariable* pPar = pArg->Get(i); + xMethParameters->Put(pPar, i); + } + + pMethVar->SetParameters( xMethParameters.get() ); + pMethVar->Get( aVals ); + pMethVar->SetParameters( nullptr ); + } + else + { + pMethVar->Get( aVals ); + } + + pVar->Put( aVals ); + } + } + else if( pHint->GetId() == SfxHintId::BasicDataChanged ) + { + SbxVariable* pMethVar = nullptr; + + bool bSet = pProcProperty->isSet(); + if( bSet ) + { + pProcProperty->setSet( false ); + + OUString aProcName = "Property Set " + + pProcProperty->GetName(); + pMethVar = Find( aProcName, SbxClassType::Method ); + } + if( !pMethVar ) // Let + { + OUString aProcName = "Property Let " + + pProcProperty->GetName(); + pMethVar = Find( aProcName, SbxClassType::Method ); + } + + if( pMethVar ) + { + // Setup parameters + SbxArrayRef xArray = new SbxArray; + xArray->Put(pMethVar, 0); // Method as parameter 0 + xArray->Put(pVar, 1); + pMethVar->SetParameters( xArray.get() ); + + SbxValues aVals; + pMethVar->Get( aVals ); + pMethVar->SetParameters( nullptr ); + } + } + } + if( pProp ) + { + if( pProp->GetModule() != this ) + SetError( ERRCODE_BASIC_BAD_ACTION ); + } + else if( pMeth ) + { + if( pHint->GetId() == SfxHintId::BasicDataWanted ) + { + if( pMeth->bInvalid && !Compile() ) + { + // auto compile has not worked! + StarBASIC::Error( ERRCODE_BASIC_BAD_PROP_VALUE ); + } + else + { + // Call of a subprogram + SbModule* pOld = GetSbData()->pMod; + GetSbData()->pMod = this; + Run( static_cast<SbMethod*>(pVar) ); + GetSbData()->pMod = pOld; + } + } + } + else + { + // #i92642: Special handling for name property to avoid + // side effects when using name as variable implicitly + bool bForwardToSbxObject = true; + + const SfxHintId nId = pHint->GetId(); + if( (nId == SfxHintId::BasicDataWanted || nId == SfxHintId::BasicDataChanged) && + pVar->GetName().equalsIgnoreAsciiCase( "name" ) ) + { + bForwardToSbxObject = false; + } + if( bForwardToSbxObject ) + { + SbxObject::Notify( rBC, rHint ); + } + } +} + +// The setting of the source makes the image invalid +// and scans the method definitions newly in + +void SbModule::SetSource32( const OUString& r ) +{ + // Default basic mode to library container mode, but... allow Option VBASupport 0/1 override + SetVBACompat( getDefaultVBAMode( static_cast< StarBASIC*>( GetParent() ) ) ); + aOUSource = r; + StartDefinitions(); + SbiTokenizer aTok( r ); + aTok.SetCompatible( IsVBACompat() ); + + while( !aTok.IsEof() ) + { + SbiToken eEndTok = NIL; + + // Searching for SUB or FUNCTION + SbiToken eLastTok = NIL; + while( !aTok.IsEof() ) + { + // #32385: not by declare + SbiToken eCurTok = aTok.Next(); + if( eLastTok != DECLARE ) + { + if( eCurTok == SUB ) + { + eEndTok = ENDSUB; break; + } + if( eCurTok == FUNCTION ) + { + eEndTok = ENDFUNC; break; + } + if( eCurTok == PROPERTY ) + { + eEndTok = ENDPROPERTY; break; + } + if( eCurTok == OPTION ) + { + eCurTok = aTok.Next(); + if( eCurTok == COMPATIBLE ) + { + aTok.SetCompatible( true ); + } + else if ( ( eCurTok == VBASUPPORT ) && ( aTok.Next() == NUMBER ) ) + { + bool bIsVBA = ( aTok.GetDbl()== 1 ); + SetVBACompat( bIsVBA ); + aTok.SetCompatible( bIsVBA ); + } + } + } + eLastTok = eCurTok; + } + // Definition of the method + SbMethod* pMeth = nullptr; + if( eEndTok != NIL ) + { + sal_uInt16 nLine1 = aTok.GetLine(); + if( aTok.Next() == SYMBOL ) + { + OUString aName_( aTok.GetSym() ); + SbxDataType t = aTok.GetType(); + if( t == SbxVARIANT && eEndTok == ENDSUB ) + { + t = SbxVOID; + } + pMeth = GetMethod( aName_, t ); + pMeth->nLine1 = pMeth->nLine2 = nLine1; + // The method is for a start VALID + pMeth->bInvalid = false; + } + else + { + eEndTok = NIL; + } + } + // Skip up to END SUB/END FUNCTION + if( eEndTok != NIL ) + { + while( !aTok.IsEof() ) + { + if( aTok.Next() == eEndTok ) + { + pMeth->nLine2 = aTok.GetLine(); + break; + } + } + if( aTok.IsEof() ) + { + pMeth->nLine2 = aTok.GetLine(); + } + } + } + EndDefinitions( true ); +} + +// Broadcast of a hint to all Basics + +static void SendHint_( SbxObject* pObj, SfxHintId nId, SbMethod* p ) +{ + // Self a BASIC? + if( dynamic_cast<const StarBASIC *>(pObj) != nullptr && pObj->IsBroadcaster() ) + pObj->GetBroadcaster().Broadcast( SbxHint( nId, p ) ); + // Then ask for the subobjects + SbxArray* pObjs = pObj->GetObjects(); + for (sal_uInt32 i = 0; i < pObjs->Count(); i++) + { + SbxVariable* pVar = pObjs->Get(i); + if( dynamic_cast<const SbxObject *>(pVar) != nullptr ) + SendHint_( dynamic_cast<SbxObject*>( pVar), nId, p ); + } +} + +static void SendHint( SbxObject* pObj, SfxHintId nId, SbMethod* p ) +{ + while( pObj->GetParent() ) + pObj = pObj->GetParent(); + SendHint_( pObj, nId, p ); +} + +// #57841 Clear Uno-Objects, which were helt in RTL functions, +// at the end of the program, so that nothing were helt. +static void ClearUnoObjectsInRTL_Impl_Rek( StarBASIC* pBasic ) +{ + // delete the return value of CreateUnoService + SbxVariable* pVar = pBasic->GetRtl()->Find( "CreateUnoService", SbxClassType::Method ); + if( pVar ) + { + pVar->SbxValue::Clear(); + } + // delete the return value of CreateUnoDialog + pVar = pBasic->GetRtl()->Find( "CreateUnoDialog", SbxClassType::Method ); + if( pVar ) + { + pVar->SbxValue::Clear(); + } + // delete the return value of CDec + pVar = pBasic->GetRtl()->Find( "CDec", SbxClassType::Method ); + if( pVar ) + { + pVar->SbxValue::Clear(); + } + // delete return value of CreateObject + pVar = pBasic->GetRtl()->Find( "CreateObject", SbxClassType::Method ); + if( pVar ) + { + pVar->SbxValue::Clear(); + } + // Go over all Sub-Basics + SbxArray* pObjs = pBasic->GetObjects(); + sal_uInt32 nCount = pObjs->Count(); + for( sal_uInt32 i = 0 ; i < nCount ; i++ ) + { + SbxVariable* pObjVar = pObjs->Get(i); + StarBASIC* pSubBasic = dynamic_cast<StarBASIC*>( pObjVar ); + if( pSubBasic ) + { + ClearUnoObjectsInRTL_Impl_Rek( pSubBasic ); + } + } +} + +static void ClearUnoObjectsInRTL_Impl( StarBASIC* pBasic ) +{ + // #67781 Delete return values of the Uno-methods + clearUnoMethods(); + + ClearUnoObjectsInRTL_Impl_Rek( pBasic ); + + // Search for the topmost Basic + SbxObject* p = pBasic; + while( p->GetParent() ) + p = p->GetParent(); + if( static_cast<StarBASIC*>(p) != pBasic ) + ClearUnoObjectsInRTL_Impl_Rek( static_cast<StarBASIC*>(p) ); +} + + +void SbModule::SetVBACompat( bool bCompat ) +{ + if( mbVBACompat == bCompat ) + return; + + mbVBACompat = bCompat; + // initialize VBA document API + if( mbVBACompat ) try + { + StarBASIC* pBasic = static_cast< StarBASIC* >( GetParent() ); + uno::Reference< lang::XMultiServiceFactory > xFactory( getDocumentModel( pBasic ), uno::UNO_QUERY_THROW ); + xFactory->createInstance( "ooo.vba.VBAGlobals" ); + } + catch( Exception& ) + { + } +} + +namespace +{ + class RunInitGuard + { + protected: + std::unique_ptr<SbiRuntime> m_xRt; + SbiGlobals* m_pSbData; + SbModule* m_pOldMod; + public: + RunInitGuard(SbModule* pModule, SbMethod* pMethod, sal_uInt32 nArg, SbiGlobals* pSbData) + : m_xRt(new SbiRuntime(pModule, pMethod, nArg)) + , m_pSbData(pSbData) + , m_pOldMod(pSbData->pMod) + { + m_xRt->pNext = pSbData->pInst->pRun; + m_pSbData->pMod = pModule; + m_pSbData->pInst->pRun = m_xRt.get(); + } + void run() + { + while (m_xRt->Step()) {} + } + virtual ~RunInitGuard() + { + m_pSbData->pInst->pRun = m_xRt->pNext; + m_pSbData->pMod = m_pOldMod; + m_xRt.reset(); + } + }; + + class RunGuard : public RunInitGuard + { + private: + bool m_bDelInst; + public: + RunGuard(SbModule* pModule, SbMethod* pMethod, sal_uInt32 nArg, SbiGlobals* pSbData, bool bDelInst) + : RunInitGuard(pModule, pMethod, nArg, pSbData) + , m_bDelInst(bDelInst) + { + if (m_xRt->pNext) + m_xRt->pNext->block(); + } + virtual ~RunGuard() override + { + if (m_xRt->pNext) + m_xRt->pNext->unblock(); + + // #63710 It can happen by an another thread handling at events, + // that the show call returns to a dialog (by closing the + // dialog per UI), before a by an event triggered further call returned, + // which stands in Basic more top in the stack and that had been run on + // a Basic-Breakpoint. Then would the instance below destroyed. And if the Basic, + // that stand still in the call, further runs, there is a GPF. + // Thus here had to be wait until the other call comes back. + if (m_bDelInst) + { + // Compare here with 1 instead of 0, because before nCallLvl-- + while (m_pSbData->pInst->nCallLvl != 1 && !Application::IsQuit()) + Application::Yield(); + } + + m_pSbData->pInst->nCallLvl--; // Call-Level down again + + // Exist an higher-ranking runtime instance? + // Then take over BasicDebugFlags::Break, if set + SbiRuntime* pRtNext = m_xRt->pNext; + if (pRtNext && (m_xRt->GetDebugFlags() & BasicDebugFlags::Break)) + pRtNext->SetDebugFlags(BasicDebugFlags::Break); + } + }; +} + +// Run a Basic-subprogram +void SbModule::Run( SbMethod* pMeth ) +{ + SAL_INFO("basic","About to run " << pMeth->GetName() << ", vba compatmode is " << mbVBACompat ); + + static sal_uInt16 nMaxCallLevel = 0; + + SbiGlobals* pSbData = GetSbData(); + + bool bDelInst = pSbData->pInst == nullptr; + bool bQuit = false; + StarBASICRef xBasic; + uno::Reference< frame::XModel > xModel; + uno::Reference< script::vba::XVBACompatibility > xVBACompat; + if( bDelInst ) + { + // #32779: Hold Basic during the execution + xBasic = static_cast<StarBASIC*>( GetParent() ); + + pSbData->pInst = new SbiInstance( static_cast<StarBASIC*>(GetParent()) ); + + /* If a VBA script in a document is started, get the VBA compatibility + interface from the document Basic library container, and notify all + VBA script listeners about the started script. */ + if( mbVBACompat ) + { + StarBASIC* pBasic = static_cast< StarBASIC* >( GetParent() ); + if( pBasic && pBasic->IsDocBasic() ) try + { + xModel.set( getDocumentModel( pBasic ), uno::UNO_SET_THROW ); + xVBACompat.set( getVBACompatibility( xModel ), uno::UNO_SET_THROW ); + xVBACompat->broadcastVBAScriptEvent( script::vba::VBAScriptEventId::SCRIPT_STARTED, GetName() ); + } + catch(const uno::Exception& ) + { + } + } + + // Launcher problem + // i80726 The Find below will generate an error in Testtool so we reset it unless there was one before already + bool bWasError = SbxBase::GetError() != ERRCODE_NONE; + SbxVariable* pMSOMacroRuntimeLibVar = Find( "Launcher", SbxClassType::Object ); + if ( !bWasError && (SbxBase::GetError() == ERRCODE_BASIC_PROC_UNDEFINED) ) + SbxBase::ResetError(); + if( pMSOMacroRuntimeLibVar ) + { + StarBASIC* pMSOMacroRuntimeLib = dynamic_cast<StarBASIC*>( pMSOMacroRuntimeLibVar ); + if( pMSOMacroRuntimeLib ) + { + SbxFlagBits nGblFlag = pMSOMacroRuntimeLib->GetFlags() & SbxFlagBits::GlobalSearch; + pMSOMacroRuntimeLib->ResetFlag( SbxFlagBits::GlobalSearch ); + SbxVariable* pAppSymbol = pMSOMacroRuntimeLib->Find( "Application", SbxClassType::Method ); + pMSOMacroRuntimeLib->SetFlag( nGblFlag ); + if( pAppSymbol ) + { + pMSOMacroRuntimeLib->SetFlag( SbxFlagBits::ExtSearch ); // Could have been disabled before + pSbData->pMSOMacroRuntimLib = pMSOMacroRuntimeLib; + } + } + } + + if( nMaxCallLevel == 0 ) + { +#ifdef UNX + struct rlimit rl; + getrlimit ( RLIMIT_STACK, &rl ); +#endif +#if defined LINUX + // Empiric value, 900 = needed bytes/Basic call level + // for Linux including 10% safety margin + nMaxCallLevel = rl.rlim_cur / 900; +#elif defined __sun + // Empiric value, 1650 = needed bytes/Basic call level + // for Solaris including 10% safety margin + nMaxCallLevel = rl.rlim_cur / 1650; +#elif defined _WIN32 + nMaxCallLevel = 5800; +#else + nMaxCallLevel = MAXRECURSION; +#endif + } + } + + // Recursion to deep? + if( ++pSbData->pInst->nCallLvl <= nMaxCallLevel ) + { + // Define a globale variable in all Mods + GlobalRunInit( /* bBasicStart = */ bDelInst ); + + // Appeared a compiler error? Then we don't launch + if( !pSbData->bGlobalInitErr ) + { + if( bDelInst ) + { + SendHint( GetParent(), SfxHintId::BasicStart, pMeth ); + + // 1996-10-16: #31460 New concept for StepInto/Over/Out + // For an explanation see runtime.cxx at SbiInstance::CalcBreakCallLevel() + // Identify the BreakCallLevel + pSbData->pInst->CalcBreakCallLevel( pMeth->GetDebugFlags() ); + } + + auto xRuntimeGuard(std::make_unique<RunGuard>(this, pMeth, pMeth->nStart, pSbData, bDelInst)); + + if ( mbVBACompat ) + { + pSbData->pInst->EnableCompatibility( true ); + } + + xRuntimeGuard->run(); + + xRuntimeGuard.reset(); + + if( bDelInst ) + { + // #57841 Clear Uno-Objects, which were helt in RTL functions, + // at the end of the program, so that nothing were helt. + ClearUnoObjectsInRTL_Impl( xBasic.get() ); + + clearNativeObjectWrapperVector(); + + SAL_WARN_IF(pSbData->pInst->nCallLvl != 0,"basic","BASIC-Call-Level > 0"); + delete pSbData->pInst; + pSbData->pInst = nullptr; + bDelInst = false; + + // #i30690 + SolarMutexGuard aSolarGuard; + SendHint( GetParent(), SfxHintId::BasicStop, pMeth ); + + GlobalRunDeInit(); + + if( xVBACompat.is() ) + { + // notify all VBA script listeners about the stopped script + try + { + xVBACompat->broadcastVBAScriptEvent( script::vba::VBAScriptEventId::SCRIPT_STOPPED, GetName() ); + } + catch(const uno::Exception& ) + { + } + // VBA always ensures screenupdating is enabled after completing + ::basic::vba::lockControllersOfAllDocuments( xModel, false ); + ::basic::vba::enableContainerWindowsOfAllDocuments( xModel, true ); + } + } + } + else + pSbData->pInst->nCallLvl--; // Call-Level down again + } + else + { + pSbData->pInst->nCallLvl--; // Call-Level down again + StarBASIC::FatalError( ERRCODE_BASIC_STACK_OVERFLOW ); + } + + StarBASIC* pBasic = dynamic_cast<StarBASIC*>( GetParent() ); + if( bDelInst ) + { + // #57841 Clear Uno-Objects, which were helt in RTL functions, + // the end of the program, so that nothing were helt. + ClearUnoObjectsInRTL_Impl( xBasic.get() ); + + delete pSbData->pInst; + pSbData->pInst = nullptr; + } + if ( pBasic && pBasic->IsDocBasic() && pBasic->IsQuitApplication() && !pSbData->pInst ) + bQuit = true; + if ( bQuit ) + { + Application::PostUserEvent( LINK( &AsyncQuitHandler::instance(), AsyncQuitHandler, OnAsyncQuit ) ); + } +} + +// Execute of the init method of a module after the loading +// or the compilation +void SbModule::RunInit() +{ + if( !(pImage + && !pImage->bInit + && pImage->IsFlag( SbiImageFlags::INITCODE )) ) + return; + + SbiGlobals* pSbData = GetSbData(); + + // Set flag, so that RunInit get active (Testtool) + pSbData->bRunInit = true; + + // The init code starts always here + auto xRuntimeGuard(std::make_unique<RunInitGuard>(this, nullptr, 0, pSbData)); + xRuntimeGuard->run(); + xRuntimeGuard.reset(); + + pImage->bInit = true; + pImage->bFirstInit = false; + + // RunInit is not active anymore + pSbData->bRunInit = false; +} + +// Delete with private/dim declared variables + +void SbModule::AddVarName( const OUString& aName ) +{ + // see if the name is added already + for ( const auto& rModuleVariableName: mModuleVariableNames ) + { + if ( aName == rModuleVariableName ) + return; + } + mModuleVariableNames.push_back( aName ); +} + +void SbModule::RemoveVars() +{ + for ( const auto& rModuleVariableName: mModuleVariableNames ) + { + // We don't want a Find being called in a derived class ( e.g. + // SbUserform because it could trigger say an initialise event + // which would cause basic to be re-run in the middle of the init ( and remember RemoveVars is called from compile and we don't want code to run as part of the compile ) + SbxVariableRef p = SbModule::Find( rModuleVariableName, SbxClassType::Property ); + if( p.is() ) + Remove( p.get() ); + } +} + +void SbModule::ClearPrivateVars() +{ + for (sal_uInt32 i = 0; i < pProps->Count(); i++) + { + SbProperty* p = dynamic_cast<SbProperty*>(pProps->Get(i)); + if( p ) + { + // Delete not the arrays, only their content + if( p->GetType() & SbxARRAY ) + { + SbxArray* pArray = dynamic_cast<SbxArray*>( p->GetObject() ); + if( pArray ) + { + for (sal_uInt32 j = 0; j < pArray->Count(); j++) + { + SbxVariable* pj = pArray->Get(j); + pj->SbxValue::Clear(); + } + } + } + else + { + p->SbxValue::Clear(); + } + } + } +} + +void SbModule::implClearIfVarDependsOnDeletedBasic( SbxVariable* pVar, StarBASIC* pDeletedBasic ) +{ + if( pVar->SbxValue::GetType() != SbxOBJECT || dynamic_cast<const SbProcedureProperty*>( pVar) != nullptr ) + return; + + SbxObject* pObj = dynamic_cast<SbxObject*>( pVar->GetObject() ); + if( pObj == nullptr ) + return; + + SbxObject* p = pObj; + + SbModule* pMod = dynamic_cast<SbModule*>( p ); + if( pMod != nullptr ) + pMod->ClearVarsDependingOnDeletedBasic( pDeletedBasic ); + + while( (p = p->GetParent()) != nullptr ) + { + StarBASIC* pBasic = dynamic_cast<StarBASIC*>( p ); + if( pBasic != nullptr && pBasic == pDeletedBasic ) + { + pVar->SbxValue::Clear(); + break; + } + } +} + +void SbModule::ClearVarsDependingOnDeletedBasic( StarBASIC* pDeletedBasic ) +{ + for (sal_uInt32 i = 0; i < pProps->Count(); i++) + { + SbProperty* p = dynamic_cast<SbProperty*>(pProps->Get(i)); + if( p ) + { + if( p->GetType() & SbxARRAY ) + { + SbxArray* pArray = dynamic_cast<SbxArray*>( p->GetObject() ); + if( pArray ) + { + for (sal_uInt32 j = 0; j < pArray->Count(); j++) + { + SbxVariable* pVar = pArray->Get(j); + implClearIfVarDependsOnDeletedBasic( pVar, pDeletedBasic ); + } + } + } + else + { + implClearIfVarDependsOnDeletedBasic( p, pDeletedBasic ); + } + } + } +} + +void StarBASIC::ClearAllModuleVars() +{ + // Initialise the own module + for (const auto& rModule: pModules) + { + // Initialise only, if the startcode was already executed + if( rModule->pImage && rModule->pImage->bInit && !rModule->isProxyModule() && dynamic_cast<const SbObjModule*>( rModule.get()) == nullptr ) + rModule->ClearPrivateVars(); + } + +} + +// Execution of the init-code of all module +void SbModule::GlobalRunInit( bool bBasicStart ) +{ + // If no Basic-Start, only initialise, if the module is not initialised + if( !bBasicStart ) + if( !pImage || pImage->bInit ) + return; + + // Initialise GlobalInitErr-Flag for Compiler-Error + // With the help of this flags could be located in SbModule::Run() after the call of + // GlobalRunInit, if at the initialising of the module + // an error occurred. Then it will not be launched. + GetSbData()->bGlobalInitErr = false; + + // Parent of the module is a Basic + StarBASIC *pBasic = dynamic_cast<StarBASIC*>( GetParent() ); + if( !pBasic ) + return; + + pBasic->InitAllModules(); + + SbxObject* pParent_ = pBasic->GetParent(); + if( !pParent_ ) + return; + + StarBASIC * pParentBasic = dynamic_cast<StarBASIC*>( pParent_ ); + if( !pParentBasic ) + return; + + pParentBasic->InitAllModules( pBasic ); + + // #109018 Parent can also have a parent (library in doc) + SbxObject* pParentParent = pParentBasic->GetParent(); + if( pParentParent ) + { + StarBASIC * pParentParentBasic = dynamic_cast<StarBASIC*>( pParentParent ); + if( pParentParentBasic ) + pParentParentBasic->InitAllModules( pParentBasic ); + } +} + +void SbModule::GlobalRunDeInit() +{ + StarBASIC *pBasic = dynamic_cast<StarBASIC*>( GetParent() ); + if( pBasic ) + { + pBasic->DeInitAllModules(); + + SbxObject* pParent_ = pBasic->GetParent(); + if( pParent_ ) + pBasic = dynamic_cast<StarBASIC*>( pParent_ ); + if( pBasic ) + pBasic->DeInitAllModules(); + } +} + +// Search for the next STMNT-Command in the code. This was used from the STMNT- +// Opcode to set the endcolumn. + +const sal_uInt8* SbModule::FindNextStmnt( const sal_uInt8* p, sal_uInt16& nLine, sal_uInt16& nCol ) const +{ + return FindNextStmnt( p, nLine, nCol, false ); +} + +const sal_uInt8* SbModule::FindNextStmnt( const sal_uInt8* p, sal_uInt16& nLine, sal_uInt16& nCol, + bool bFollowJumps, const SbiImage* pImg ) const +{ + sal_uInt32 nPC = static_cast<sal_uInt32>( p - pImage->GetCode() ); + while( nPC < pImage->GetCodeSize() ) + { + SbiOpcode eOp = static_cast<SbiOpcode>( *p++ ); + nPC++; + if( bFollowJumps && eOp == SbiOpcode::JUMP_ && pImg ) + { + SAL_WARN_IF( !pImg, "basic", "FindNextStmnt: pImg==NULL with FollowJumps option" ); + sal_uInt32 nOp1 = *p++; nOp1 |= *p++ << 8; + nOp1 |= *p++ << 16; nOp1 |= *p++ << 24; + p = pImg->GetCode() + nOp1; + } + else if( eOp >= SbiOpcode::SbOP1_START && eOp <= SbiOpcode::SbOP1_END ) + { + p += 4; + nPC += 4; + } + else if( eOp == SbiOpcode::STMNT_ ) + { + sal_uInt32 nl, nc; + nl = *p++; nl |= *p++ << 8; + nl |= *p++ << 16 ; nl |= *p++ << 24; + nc = *p++; nc |= *p++ << 8; + nc |= *p++ << 16 ; nc |= *p++ << 24; + nLine = static_cast<sal_uInt16>(nl); nCol = static_cast<sal_uInt16>(nc); + return p; + } + else if( eOp >= SbiOpcode::SbOP2_START && eOp <= SbiOpcode::SbOP2_END ) + { + p += 8; + nPC += 8; + } + else if( eOp < SbiOpcode::SbOP0_START || eOp > SbiOpcode::SbOP0_END ) + { + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + break; + } + } + return nullptr; +} + +// Test, if a line contains STMNT-Opcodes + +bool SbModule::IsBreakable( sal_uInt16 nLine ) const +{ + if( !pImage ) + return false; + const sal_uInt8* p = pImage->GetCode(); + sal_uInt16 nl, nc; + while( ( p = FindNextStmnt( p, nl, nc ) ) != nullptr ) + if( nl == nLine ) + return true; + return false; +} + +bool SbModule::IsBP( sal_uInt16 nLine ) const +{ + if( pBreaks ) + { + for( size_t i = 0; i < pBreaks->size(); i++ ) + { + sal_uInt16 b = pBreaks->operator[]( i ); + if( b == nLine ) + return true; + if( b < nLine ) + break; + } + } + return false; +} + +bool SbModule::SetBP( sal_uInt16 nLine ) +{ + if( !IsBreakable( nLine ) ) + return false; + if( !pBreaks ) + pBreaks = new SbiBreakpoints; + auto it = std::find_if(pBreaks->begin(), pBreaks->end(), + [&nLine](const sal_uInt16 b) { return b <= nLine; }); + if (it != pBreaks->end() && *it == nLine) + return true; + pBreaks->insert( it, nLine ); + + // #38568: Set during runtime as well here BasicDebugFlags::Break + if( GetSbData()->pInst && GetSbData()->pInst->pRun ) + GetSbData()->pInst->pRun->SetDebugFlags( BasicDebugFlags::Break ); + + return IsBreakable( nLine ); +} + +bool SbModule::ClearBP( sal_uInt16 nLine ) +{ + bool bRes = false; + if( pBreaks ) + { + auto it = std::find_if(pBreaks->begin(), pBreaks->end(), + [&nLine](const sal_uInt16 b) { return b <= nLine; }); + bRes = (it != pBreaks->end()) && (*it == nLine); + if (bRes) + { + pBreaks->erase(it); + } + if( pBreaks->empty() ) + { + delete pBreaks; + pBreaks = nullptr; + } + } + return bRes; +} + +void SbModule::ClearAllBP() +{ + delete pBreaks; + pBreaks = nullptr; +} + +void +SbModule::fixUpMethodStart( bool bCvtToLegacy, SbiImage* pImg ) const +{ + if ( !pImg ) + pImg = pImage.get(); + for (sal_uInt32 i = 0; i < pMethods->Count(); i++) + { + SbMethod* pMeth = dynamic_cast<SbMethod*>(pMethods->Get(i)); + if( pMeth ) + { + //fixup method start positions + if ( bCvtToLegacy ) + pMeth->nStart = pImg->CalcLegacyOffset( pMeth->nStart ); + else + pMeth->nStart = pImg->CalcNewOffset( static_cast<sal_uInt16>(pMeth->nStart) ); + } + } + +} + +bool SbModule::LoadData( SvStream& rStrm, sal_uInt16 nVer ) +{ + Clear(); + if( !SbxObject::LoadData( rStrm, 1 ) ) + return false; + // As a precaution... + SetFlag( SbxFlagBits::ExtSearch | SbxFlagBits::GlobalSearch ); + sal_uInt8 bImage; + rStrm.ReadUChar( bImage ); + if( !bImage ) + return true; + + std::unique_ptr<SbiImage> p(new SbiImage); + sal_uInt32 nImgVer = 0; + + if( !p->Load( rStrm, nImgVer ) ) + { + return false; + } + // If the image is in old format, we fix up the method start offsets + if ( nImgVer < B_EXT_IMG_VERSION ) + { + fixUpMethodStart( false, p.get() ); + p->ReleaseLegacyBuffer(); + } + aComment = p->aComment; + SetName( p->aName ); + if( p->GetCodeSize() ) + { + aOUSource = p->aOUSource; + // Old version: image away + if( nVer == 1 ) + { + SetSource32( p->aOUSource ); + } + else + pImage = std::move(p); + } + else + { + SetSource32( p->aOUSource ); + } + return true; +} + +bool SbModule::StoreData( SvStream& rStrm ) const +{ + bool bFixup = ( pImage && !pImage->ExceedsLegacyLimits() ); + if ( bFixup ) + fixUpMethodStart( true ); + bool bRet = SbxObject::StoreData( rStrm ); + if ( !bRet ) + return false; + + if( pImage ) + { + pImage->aOUSource = aOUSource; + pImage->aComment = aComment; + pImage->aName = GetName(); + rStrm.WriteUChar( 1 ); + // # PCode is saved only for legacy formats only + // It should be noted that it probably isn't necessary + // It would be better not to store the image ( more flexible with + // formats ) + bool bRes = pImage->Save( rStrm, B_LEGACYVERSION ); + if ( bFixup ) + fixUpMethodStart( false ); // restore method starts + return bRes; + + } + else + { + SbiImage aImg; + aImg.aOUSource = aOUSource; + aImg.aComment = aComment; + aImg.aName = GetName(); + rStrm.WriteUChar( 1 ); + return aImg.Save( rStrm ); + } +} + +bool SbModule::ExceedsLegacyModuleSize() +{ + if ( !IsCompiled() ) + Compile(); + return pImage && pImage->ExceedsLegacyLimits(); +} + +namespace { + +class ErrorHdlResetter +{ + Link<StarBASIC*,bool> mErrHandler; + bool mbError; +public: + ErrorHdlResetter() + : mErrHandler(StarBASIC::GetGlobalErrorHdl()) // save error handler + , mbError( false ) + { + // set new error handler + StarBASIC::SetGlobalErrorHdl( LINK( this, ErrorHdlResetter, BasicErrorHdl ) ); + } + ~ErrorHdlResetter() + { + // restore error handler + StarBASIC::SetGlobalErrorHdl(mErrHandler); + } + DECL_LINK( BasicErrorHdl, StarBASIC *, bool ); + bool HasError() const { return mbError; } +}; + +} + +IMPL_LINK( ErrorHdlResetter, BasicErrorHdl, StarBASIC *, /*pBasic*/, bool) +{ + mbError = true; + return false; +} + +void SbModule::GetCodeCompleteDataFromParse(CodeCompleteDataCache& aCache) +{ + ErrorHdlResetter aErrHdl; + SbxBase::ResetError(); + + auto pParser = std::make_unique<SbiParser>(static_cast<StarBASIC*>(GetParent()), this ); + pParser->SetCodeCompleting(true); + + while( pParser->Parse() ) {} + SbiSymPool* pPool = pParser->pPool; + aCache.Clear(); + for( sal_uInt16 i = 0; i < pPool->GetSize(); ++i ) + { + SbiSymDef* pSymDef = pPool->Get(i); + //std::cerr << "i: " << i << ", type: " << pSymDef->GetType() << "; name:" << pSymDef->GetName() << std::endl; + if( (pSymDef->GetType() != SbxEMPTY) && (pSymDef->GetType() != SbxNULL) ) + aCache.InsertGlobalVar( pSymDef->GetName(), pParser->aGblStrings.Find(pSymDef->GetTypeId()) ); + + SbiSymPool& rChildPool = pSymDef->GetPool(); + for(sal_uInt16 j = 0; j < rChildPool.GetSize(); ++j ) + { + SbiSymDef* pChildSymDef = rChildPool.Get(j); + //std::cerr << "j: " << j << ", type: " << pChildSymDef->GetType() << "; name:" << pChildSymDef->GetName() << std::endl; + if( (pChildSymDef->GetType() != SbxEMPTY) && (pChildSymDef->GetType() != SbxNULL) ) + aCache.InsertLocalVar( pSymDef->GetName(), pChildSymDef->GetName(), pParser->aGblStrings.Find(pChildSymDef->GetTypeId()) ); + } + } +} + + +OUString SbModule::GetKeywordCase( const OUString& sKeyword ) +{ + return SbiParser::GetKeywordCase( sKeyword ); +} + +bool SbModule::HasExeCode() +{ + // And empty Image always has the Global Chain set up + static const unsigned char pEmptyImage[] = { 0x45, 0x0 , 0x0, 0x0, 0x0 }; + // lets be stricter for the moment than VBA + + if (!IsCompiled()) + { + ErrorHdlResetter aGblErrHdl; + Compile(); + if (aGblErrHdl.HasError()) //assume unsafe on compile error + return true; + } + + bool bRes = false; + if (pImage && (pImage->GetCodeSize() != 5 || (memcmp(pImage->GetCode(), pEmptyImage, pImage->GetCodeSize()) != 0 ))) + bRes = true; + + return bRes; +} + +// Store only image, no source +void SbModule::StoreBinaryData( SvStream& rStrm ) +{ + if (!Compile()) + return; + + if (!SbxObject::StoreData(rStrm)) + return; + + pImage->aOUSource.clear(); + pImage->aComment = aComment; + pImage->aName = GetName(); + + rStrm.WriteUChar(1); + pImage->Save(rStrm); + + pImage->aOUSource = aOUSource; +} + +// Called for >= OO 1.0 passwd protected libraries only + +void SbModule::LoadBinaryData( SvStream& rStrm ) +{ + OUString aKeepSource = aOUSource; + LoadData( rStrm, 2 ); + LoadCompleted(); + aOUSource = aKeepSource; +} + +bool SbModule::LoadCompleted() +{ + SbxArray* p = GetMethods().get(); + sal_uInt32 i; + for (i = 0; i < p->Count(); i++) + { + SbMethod* q = dynamic_cast<SbMethod*>(p->Get(i)); + if( q ) + q->pMod = this; + } + p = GetProperties(); + for (i = 0; i < p->Count(); i++) + { + SbProperty* q = dynamic_cast<SbProperty*>(p->Get(i)); + if( q ) + q->pMod = this; + } + return true; +} + +void SbModule::handleProcedureProperties( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + bool bDone = false; + + const SbxHint* pHint = dynamic_cast<const SbxHint*>(&rHint); + if( pHint ) + { + SbxVariable* pVar = pHint->GetVar(); + SbProcedureProperty* pProcProperty = dynamic_cast<SbProcedureProperty*>( pVar ); + if( pProcProperty ) + { + bDone = true; + + if( pHint->GetId() == SfxHintId::BasicDataWanted ) + { + OUString aProcName = "Property Get " + + pProcProperty->GetName(); + + SbxVariable* pMeth = Find( aProcName, SbxClassType::Method ); + if( pMeth ) + { + SbxValues aVals; + aVals.eType = SbxVARIANT; + + SbxArray* pArg = pVar->GetParameters(); + sal_uInt32 nVarParCount = (pArg != nullptr) ? pArg->Count() : 0; + if( nVarParCount > 1 ) + { + SbxArrayRef xMethParameters = new SbxArray; + xMethParameters->Put(pMeth, 0); // Method as parameter 0 + for( sal_uInt32 i = 1 ; i < nVarParCount ; ++i ) + { + SbxVariable* pPar = pArg->Get(i); + xMethParameters->Put(pPar, i); + } + + pMeth->SetParameters( xMethParameters.get() ); + pMeth->Get( aVals ); + pMeth->SetParameters( nullptr ); + } + else + { + pMeth->Get( aVals ); + } + + pVar->Put( aVals ); + } + } + else if( pHint->GetId() == SfxHintId::BasicDataChanged ) + { + SbxVariable* pMeth = nullptr; + + bool bSet = pProcProperty->isSet(); + if( bSet ) + { + pProcProperty->setSet( false ); + + OUString aProcName = "Property Set " + + pProcProperty->GetName(); + pMeth = Find( aProcName, SbxClassType::Method ); + } + if( !pMeth ) // Let + { + OUString aProcName = "Property Let " + + pProcProperty->GetName(); + pMeth = Find( aProcName, SbxClassType::Method ); + } + + if( pMeth ) + { + // Setup parameters + SbxArrayRef xArray = new SbxArray; + xArray->Put(pMeth, 0); // Method as parameter 0 + xArray->Put(pVar, 1); + pMeth->SetParameters( xArray.get() ); + + SbxValues aVals; + pMeth->Get( aVals ); + pMeth->SetParameters( nullptr ); + } + } + } + } + + if( !bDone ) + SbModule::Notify( rBC, rHint ); +} + + +// Implementation SbJScriptModule (Basic module for JavaScript source code) +SbJScriptModule::SbJScriptModule() + :SbModule( "" ) +{ +} + +bool SbJScriptModule::LoadData( SvStream& rStrm, sal_uInt16 ) +{ + Clear(); + if( !SbxObject::LoadData( rStrm, 1 ) ) + return false; + + // Get the source string + aOUSource = rStrm.ReadUniOrByteString( osl_getThreadTextEncoding() ); + return true; +} + +bool SbJScriptModule::StoreData( SvStream& rStrm ) const +{ + if( !SbxObject::StoreData( rStrm ) ) + return false; + + // Write the source string + OUString aTmp = aOUSource; + rStrm.WriteUniOrByteString( aTmp, osl_getThreadTextEncoding() ); + return true; +} + + +SbMethod::SbMethod( const OUString& r, SbxDataType t, SbModule* p ) + : SbxMethod( r, t ), pMod( p ) +{ + bInvalid = true; + nStart = 0; + nDebugFlags = BasicDebugFlags::NONE; + nLine1 = 0; + nLine2 = 0; + refStatics = new SbxArray; + mCaller = nullptr; + // HACK due to 'Reference could not be saved' + SetFlag( SbxFlagBits::NoModify ); +} + +SbMethod::SbMethod( const SbMethod& r ) + : SvRefBase( r ), SbxMethod( r ) +{ + pMod = r.pMod; + bInvalid = r.bInvalid; + nStart = r.nStart; + nDebugFlags = r.nDebugFlags; + nLine1 = r.nLine1; + nLine2 = r.nLine2; + refStatics = r.refStatics; + mCaller = r.mCaller; + SetFlag( SbxFlagBits::NoModify ); +} + +SbMethod::~SbMethod() +{ +} + +void SbMethod::ClearStatics() +{ + refStatics = new SbxArray; + +} +SbxArray* SbMethod::GetStatics() +{ + return refStatics.get(); +} + +bool SbMethod::LoadData( SvStream& rStrm, sal_uInt16 nVer ) +{ + if( !SbxMethod::LoadData( rStrm, 1 ) ) + return false; + + sal_uInt16 nFlag; + rStrm.ReadUInt16( nFlag ); + + sal_Int16 nTempStart = static_cast<sal_Int16>(nStart); + + if( nVer == 2 ) + { + rStrm.ReadUInt16( nLine1 ).ReadUInt16( nLine2 ).ReadInt16( nTempStart ).ReadCharAsBool( bInvalid ); + //tdf#94617 + if (nFlag & 0x8000) + { + sal_uInt16 nMult = nFlag & 0x7FFF; + sal_Int16 const nMax = std::numeric_limits<sal_Int16>::max(); + nStart = nMult * nMax + nTempStart; + } + else + { + nStart = nTempStart; + } + } + else + { + nStart = nTempStart; + } + + // HACK due to 'Reference could not be saved' + SetFlag( SbxFlagBits::NoModify ); + + return true; +} + +bool SbMethod::StoreData( SvStream& rStrm ) const +{ + if( !SbxMethod::StoreData( rStrm ) ) + return false; + + //tdf#94617 + sal_Int16 nMax = std::numeric_limits<sal_Int16>::max(); + sal_Int16 nStartTemp = nStart % nMax; + sal_uInt16 nDebugFlagsTemp = nStart / nMax; + nDebugFlagsTemp |= 0x8000; + + rStrm.WriteUInt16( nDebugFlagsTemp ) + .WriteInt16( nLine1 ) + .WriteInt16( nLine2 ) + .WriteInt16( nStartTemp ) + .WriteBool( bInvalid ); + + return true; +} + +void SbMethod::GetLineRange( sal_uInt16& l1, sal_uInt16& l2 ) +{ + l1 = nLine1; l2 = nLine2; +} + +// Could later be deleted + +SbxInfo* SbMethod::GetInfo() +{ + return pInfo.get(); +} + +// Interface to execute a method of the applications +// With special RefCounting, so that the Basic was not fired of by CloseDocument() +// The return value will be delivered as string. +ErrCode SbMethod::Call( SbxValue* pRet, SbxVariable* pCaller ) +{ + if ( pCaller ) + { + SAL_INFO("basic", "SbMethod::Call Have been passed a caller 0x" << pCaller ); + mCaller = pCaller; + } + // Increment the RefCount of the module + tools::SvRef<SbModule> pMod_ = static_cast<SbModule*>(GetParent()); + + tools::SvRef<StarBASIC> xHolder = static_cast<StarBASIC*>(pMod_->GetParent()); + + // Establish the values to get the return value + SbxValues aVals; + aVals.eType = SbxVARIANT; + + // #104083: Compile BEFORE get + if( bInvalid && !pMod_->Compile() ) + StarBASIC::Error( ERRCODE_BASIC_BAD_PROP_VALUE ); + + // tdf#143582 - clear return value of the method before calling it + const SbxFlagBits nSavFlags = GetFlags(); + SetFlag(SbxFlagBits::ReadWrite | SbxFlagBits::NoBroadcast); + Clear(); + SetFlags(nSavFlags); + + Get( aVals ); + if ( pRet ) + pRet->Put( aVals ); + + // Was there an error + ErrCode nErr = SbxBase::GetError(); + SbxBase::ResetError(); + + mCaller = nullptr; + return nErr; +} + + +// #100883 Own Broadcast for SbMethod +void SbMethod::Broadcast( SfxHintId nHintId ) +{ + if( !mpBroadcaster || IsSet( SbxFlagBits::NoBroadcast ) ) + return; + + // Because the method could be called from outside, test here once again + // the authorisation + if( nHintId == SfxHintId::BasicDataWanted ) + if( !CanRead() ) + return; + if( nHintId == SfxHintId::BasicDataChanged ) + if( !CanWrite() ) + return; + + if( pMod && !pMod->IsCompiled() ) + pMod->Compile(); + + // Block broadcasts while creating new method + std::unique_ptr<SfxBroadcaster> pSaveBroadcaster = std::move(mpBroadcaster); + SbMethodRef xThisCopy = new SbMethod( *this ); + if( mpPar.is() ) + { + // Enregister this as element 0, but don't reset the parent! + if( GetType() != SbxVOID ) { + mpPar->PutDirect( xThisCopy.get(), 0 ); + } + SetParameters( nullptr ); + } + + mpBroadcaster = std::move(pSaveBroadcaster); + mpBroadcaster->Broadcast( SbxHint( nHintId, xThisCopy.get() ) ); + + SbxFlagBits nSaveFlags = GetFlags(); + SetFlag( SbxFlagBits::ReadWrite ); + pSaveBroadcaster = std::move(mpBroadcaster); + Put( xThisCopy->GetValues_Impl() ); + mpBroadcaster = std::move(pSaveBroadcaster); + SetFlags( nSaveFlags ); +} + + +// Implementation of SbJScriptMethod (method class as a wrapper for JavaScript-functions) + +SbJScriptMethod::SbJScriptMethod( SbxDataType t ) + : SbMethod( "", t, nullptr ) +{ +} + +SbJScriptMethod::~SbJScriptMethod() +{} + + +SbObjModule::SbObjModule( const OUString& rName, const css::script::ModuleInfo& mInfo, bool bIsVbaCompatible ) + : SbModule( rName, bIsVbaCompatible ) +{ + SetModuleType( mInfo.ModuleType ); + if ( mInfo.ModuleType == script::ModuleType::FORM ) + { + SetClassName( "Form" ); + } + else if ( mInfo.ModuleObject.is() ) + { + SetUnoObject( uno::Any( mInfo.ModuleObject ) ); + } +} + +SbObjModule::~SbObjModule() +{ +} + +void +SbObjModule::SetUnoObject( const uno::Any& aObj ) +{ + SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pDocObject.get() ); + if ( pUnoObj && pUnoObj->getUnoAny() == aObj ) // object is equal, nothing to do + return; + pDocObject = new SbUnoObject( GetName(), aObj ); + + css::uno::Reference< css::lang::XServiceInfo > xServiceInfo( aObj, css::uno::UNO_QUERY_THROW ); + if( xServiceInfo->supportsService( "ooo.vba.excel.Worksheet" ) ) + { + SetClassName( "Worksheet" ); + } + else if( xServiceInfo->supportsService( "ooo.vba.excel.Workbook" ) ) + { + SetClassName( "Workbook" ); + } +} + +SbxVariable* +SbObjModule::GetObject() +{ + return pDocObject.get(); +} +SbxVariable* +SbObjModule::Find( const OUString& rName, SbxClassType t ) +{ + SbxVariable* pVar = nullptr; + if ( pDocObject ) + pVar = pDocObject->Find( rName, t ); + if ( !pVar ) + pVar = SbModule::Find( rName, t ); + return pVar; +} + +void SbObjModule::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + SbModule::handleProcedureProperties( rBC, rHint ); +} + + +typedef ::cppu::WeakImplHelper< + awt::XTopWindowListener, + awt::XWindowListener, + document::XDocumentEventListener > FormObjEventListener_BASE; + +class FormObjEventListenerImpl: + public FormObjEventListener_BASE +{ + SbUserFormModule* mpUserForm; + uno::Reference< lang::XComponent > mxComponent; + uno::Reference< frame::XModel > mxModel; + bool mbDisposed; + bool mbOpened; + bool mbActivated; + bool mbShowing; + +public: + FormObjEventListenerImpl(const FormObjEventListenerImpl&) = delete; + const FormObjEventListenerImpl& operator=(const FormObjEventListenerImpl&) = delete; + FormObjEventListenerImpl( SbUserFormModule* pUserForm, uno::Reference< lang::XComponent > xComponent, uno::Reference< frame::XModel > xModel ) : + mpUserForm( pUserForm ), mxComponent(std::move( xComponent)), mxModel(std::move( xModel )), + mbDisposed( false ), mbOpened( false ), mbActivated( false ), mbShowing( false ) + { + if ( mxComponent.is() ) + { + try + { + uno::Reference< awt::XTopWindow >( mxComponent, uno::UNO_QUERY_THROW )->addTopWindowListener( this ); + } + catch(const uno::Exception& ) {} + try + { + uno::Reference< awt::XWindow >( mxComponent, uno::UNO_QUERY_THROW )->addWindowListener( this ); + } + catch(const uno::Exception& ) {} + } + + if ( mxModel.is() ) + { + try + { + uno::Reference< document::XDocumentEventBroadcaster >( mxModel, uno::UNO_QUERY_THROW )->addDocumentEventListener( this ); + } + catch(const uno::Exception& ) {} + } + } + + virtual ~FormObjEventListenerImpl() override + { + removeListener(); + } + + bool isShowing() const { return mbShowing; } + + void removeListener() + { + if ( mxComponent.is() && !mbDisposed ) + { + try + { + uno::Reference< awt::XTopWindow >( mxComponent, uno::UNO_QUERY_THROW )->removeTopWindowListener( this ); + } + catch(const uno::Exception& ) {} + try + { + uno::Reference< awt::XWindow >( mxComponent, uno::UNO_QUERY_THROW )->removeWindowListener( this ); + } + catch(const uno::Exception& ) {} + } + mxComponent.clear(); + + if ( mxModel.is() && !mbDisposed ) + { + try + { + uno::Reference< document::XDocumentEventBroadcaster >( mxModel, uno::UNO_QUERY_THROW )->removeDocumentEventListener( this ); + } + catch(const uno::Exception& ) {} + } + mxModel.clear(); + } + + virtual void SAL_CALL windowOpened( const lang::EventObject& /*e*/ ) override + { + if ( mpUserForm ) + { + mbOpened = true; + mbShowing = true; + if ( mbActivated ) + { + mbOpened = mbActivated = false; + mpUserForm->triggerActivateEvent(); + } + } + } + + + virtual void SAL_CALL windowClosing( const lang::EventObject& /*e*/ ) override + { +#ifdef IN_THE_FUTURE + uno::Reference< awt::XDialog > xDialog( e.Source, uno::UNO_QUERY ); + if ( xDialog.is() ) + { + uno::Reference< awt::XControl > xControl( xDialog, uno::UNO_QUERY ); + if ( xControl->getPeer().is() ) + { + uno::Reference< document::XVbaMethodParameter > xVbaMethodParameter( xControl->getPeer(), uno::UNO_QUERY ); + if ( xVbaMethodParameter.is() ) + { + sal_Int8 nCancel = 0; + sal_Int8 nCloseMode = ::ooo::vba::VbQueryClose::vbFormControlMenu; + + Sequence< Any > aParams; + aParams.realloc(2); + aParams[0] <<= nCancel; + aParams[1] <<= nCloseMode; + + mpUserForm->triggerMethod( "Userform_QueryClose", aParams); + return; + + } + } + } + + mpUserForm->triggerMethod( "Userform_QueryClose" ); +#endif + } + + + virtual void SAL_CALL windowClosed( const lang::EventObject& /*e*/ ) override + { + mbOpened = false; + mbShowing = false; + } + + virtual void SAL_CALL windowMinimized( const lang::EventObject& /*e*/ ) override + { + } + + virtual void SAL_CALL windowNormalized( const lang::EventObject& /*e*/ ) override + { + } + + virtual void SAL_CALL windowActivated( const lang::EventObject& /*e*/ ) override + { + if ( mpUserForm ) + { + mbActivated = true; + if ( mbOpened ) + { + mbOpened = mbActivated = false; + mpUserForm->triggerActivateEvent(); + } + } + } + + virtual void SAL_CALL windowDeactivated( const lang::EventObject& /*e*/ ) override + { + if ( mpUserForm ) + mpUserForm->triggerDeactivateEvent(); + } + + virtual void SAL_CALL windowResized( const awt::WindowEvent& /*e*/ ) override + { + if ( mpUserForm ) + { + mpUserForm->triggerResizeEvent(); + mpUserForm->triggerLayoutEvent(); + } + } + + virtual void SAL_CALL windowMoved( const awt::WindowEvent& /*e*/ ) override + { + if ( mpUserForm ) + mpUserForm->triggerLayoutEvent(); + } + + virtual void SAL_CALL windowShown( const lang::EventObject& /*e*/ ) override + { + } + + virtual void SAL_CALL windowHidden( const lang::EventObject& /*e*/ ) override + { + } + + virtual void SAL_CALL documentEventOccured( const document::DocumentEvent& rEvent ) override + { + // early disposing on document event "OnUnload", to be sure Basic still exists when calling VBA "UserForm_Terminate" + if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC ) ) + { + removeListener(); + mbDisposed = true; + if ( mpUserForm ) + mpUserForm->ResetApiObj(); // will trigger "UserForm_Terminate" + } + } + + virtual void SAL_CALL disposing( const lang::EventObject& /*Source*/ ) override + { + removeListener(); + mbDisposed = true; + if ( mpUserForm ) + mpUserForm->ResetApiObj( false ); // pass false (too late to trigger VBA events here) + } +}; + +SbUserFormModule::SbUserFormModule( const OUString& rName, const css::script::ModuleInfo& mInfo, bool bIsCompat ) + : SbObjModule( rName, mInfo, bIsCompat ) + , m_mInfo( mInfo ) + , mbInit( false ) +{ + m_xModel.set( mInfo.ModuleObject, uno::UNO_QUERY_THROW ); +} + +SbUserFormModule::~SbUserFormModule() +{ +} + +void SbUserFormModule::ResetApiObj( bool bTriggerTerminateEvent ) +{ + SAL_INFO("basic", " SbUserFormModule::ResetApiObj( " << (bTriggerTerminateEvent ? "true )" : "false )") ); + if ( bTriggerTerminateEvent && m_xDialog.is() ) // probably someone close the dialog window + { + triggerTerminateEvent(); + } + pDocObject = nullptr; + m_xDialog = nullptr; +} + +void SbUserFormModule::triggerMethod( const OUString& aMethodToRun ) +{ + Sequence< Any > aArguments; + triggerMethod( aMethodToRun, aArguments ); +} + +void SbUserFormModule::triggerMethod( const OUString& aMethodToRun, Sequence< Any >& aArguments ) +{ + SAL_INFO("basic", "trigger " << aMethodToRun); + // Search method + SbxVariable* pMeth = SbObjModule::Find( aMethodToRun, SbxClassType::Method ); + if( !pMeth ) + return; + + if ( aArguments.hasElements() ) // Setup parameters + { + auto xArray = tools::make_ref<SbxArray>(); + xArray->Put(pMeth, 0); // Method as parameter 0 + + for ( sal_Int32 i = 0; i < aArguments.getLength(); ++i ) + { + auto xSbxVar = tools::make_ref<SbxVariable>( SbxVARIANT ); + unoToSbxValue( xSbxVar.get(), aArguments[i] ); + xArray->Put(xSbxVar.get(), static_cast<sal_uInt32>(i) + 1); + + // Enable passing by ref + if ( xSbxVar->GetType() != SbxVARIANT ) + xSbxVar->SetFlag( SbxFlagBits::Fixed ); + } + pMeth->SetParameters( xArray.get() ); + + SbxValues aVals; + pMeth->Get( aVals ); + + auto pArguments = aArguments.getArray(); + for ( sal_Int32 i = 0; i < aArguments.getLength(); ++i ) + { + pArguments[i] = sbxToUnoValue(xArray->Get(static_cast<sal_uInt32>(i) + 1)); + } + pMeth->SetParameters( nullptr ); + } + else + { + SbxValues aVals; + pMeth->Get( aVals ); + } +} + +void SbUserFormModule::triggerActivateEvent() +{ + triggerMethod( "UserForm_Activate" ); +} + +void SbUserFormModule::triggerDeactivateEvent() +{ + triggerMethod( "Userform_Deactivate" ); +} + +void SbUserFormModule::triggerInitializeEvent() +{ + if ( mbInit ) + return; + triggerMethod("Userform_Initialize"); + mbInit = true; +} + +void SbUserFormModule::triggerTerminateEvent() +{ + triggerMethod("Userform_Terminate"); + mbInit=false; +} + +void SbUserFormModule::triggerLayoutEvent() +{ + triggerMethod("Userform_Layout"); +} + +void SbUserFormModule::triggerResizeEvent() +{ + triggerMethod("Userform_Resize"); +} + +SbUserFormModuleInstance* SbUserFormModule::CreateInstance() +{ + SbUserFormModuleInstance* pInstance = new SbUserFormModuleInstance( this, GetName(), m_mInfo, IsVBACompat() ); + return pInstance; +} + +SbUserFormModuleInstance::SbUserFormModuleInstance( SbUserFormModule* pParentModule, + const OUString& rName, const css::script::ModuleInfo& mInfo, bool bIsVBACompat ) + : SbUserFormModule( rName, mInfo, bIsVBACompat ) + , m_pParentModule( pParentModule ) +{ +} + +bool SbUserFormModuleInstance::IsClass( const OUString& rName ) const +{ + bool bParentNameMatches = m_pParentModule->GetName().equalsIgnoreAsciiCase( rName ); + bool bRet = bParentNameMatches || SbxObject::IsClass( rName ); + return bRet; +} + +SbxVariable* SbUserFormModuleInstance::Find( const OUString& rName, SbxClassType t ) +{ + SbxVariable* pVar = m_pParentModule->Find( rName, t ); + return pVar; +} + + +void SbUserFormModule::Load() +{ + // forces a load + if ( !pDocObject.is() ) + InitObject(); +} + + +void SbUserFormModule::Unload() +{ + sal_Int8 nCancel = 0; + + Sequence< Any > aParams = { Any(nCancel), Any(sal_Int8(::ooo::vba::VbQueryClose::vbFormCode)) }; + + triggerMethod( "Userform_QueryClose", aParams); + + aParams[0] >>= nCancel; + // basic boolean ( and what the user might use ) can be ambiguous ( e.g. basic true = -1 ) + // test against 0 ( false ) and assume anything else is true + // ( Note: ) this used to work ( something changes somewhere ) + if (nCancel != 0) + { + return; + } + + if ( m_xDialog.is() ) + { + triggerTerminateEvent(); + } + // Search method + SbxVariable* pMeth = SbObjModule::Find( "UnloadObject", SbxClassType::Method ); + if( !pMeth ) + return; + + SAL_INFO("basic", "Attempting to run the UnloadObjectMethod"); + m_xDialog.clear(); //release ref to the uno object + SbxValues aVals; + bool bWaitForDispose = true; // assume dialog is showing + if (m_DialogListener) + { + bWaitForDispose = m_DialogListener->isShowing(); + SAL_INFO("basic", "Showing " << bWaitForDispose ); + } + pMeth->Get( aVals); + if ( !bWaitForDispose ) + { + // we've either already got a dispose or we are never going to get one + ResetApiObj(); + } // else wait for dispose + SAL_INFO("basic", "UnloadObject completed (we hope)"); +} + + +void SbUserFormModule::InitObject() +{ + try + { + SbUnoObject* pGlobs = static_cast<SbUnoObject*>(GetParent()->Find( "VBAGlobals", SbxClassType::DontCare )); + if ( m_xModel.is() && pGlobs ) + { + // broadcast INITIALIZE_USERFORM script event before the dialog is created + Reference< script::vba::XVBACompatibility > xVBACompat( getVBACompatibility( m_xModel ), uno::UNO_SET_THROW ); + xVBACompat->broadcastVBAScriptEvent( script::vba::VBAScriptEventId::INITIALIZE_USERFORM, GetName() ); + uno::Reference< lang::XMultiServiceFactory > xVBAFactory( pGlobs->getUnoAny(), uno::UNO_QUERY_THROW ); + uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext(); + OUString sDialogUrl( "vnd.sun.star.script:" ); + OUString sProjectName( "Standard" ); + + try + { + Reference< beans::XPropertySet > xProps( m_xModel, UNO_QUERY_THROW ); + uno::Reference< script::vba::XVBACompatibility > xVBAMode( xProps->getPropertyValue( "BasicLibraries" ), uno::UNO_QUERY_THROW ); + sProjectName = xVBAMode->getProjectName(); + } + catch(const Exception& ) {} + + sDialogUrl += sProjectName + "." + GetName() + "?location=document"; + + uno::Reference< awt::XDialogProvider > xProvider = awt::DialogProvider::createWithModel( xContext, m_xModel ); + m_xDialog = xProvider->createDialog( sDialogUrl ); + + // create vba api object + uno::Sequence< uno::Any > aArgs + { + uno::Any(), + Any(m_xDialog), + Any(m_xModel), + Any(GetParent()->GetName()) + }; + pDocObject = new SbUnoObject( GetName(), uno::Any( xVBAFactory->createInstanceWithArguments( "ooo.vba.msforms.UserForm", aArgs ) ) ); + + uno::Reference< lang::XComponent > xComponent( m_xDialog, uno::UNO_QUERY_THROW ); + + // the dialog must be disposed at the end! + StarBASIC* pParentBasic = nullptr; + SbxObject* pCurObject = this; + do + { + SbxObject* pObjParent = pCurObject->GetParent(); + pParentBasic = dynamic_cast<StarBASIC*>( pObjParent ); + pCurObject = pObjParent; + } + while( pParentBasic == nullptr && pCurObject != nullptr ); + + SAL_WARN_IF( pParentBasic == nullptr, "basic", "pParentBasic == NULL" ); + registerComponentToBeDisposedForBasic( xComponent, pParentBasic ); + + // if old listener object exists, remove it from dialog and document model + if( m_DialogListener.is() ) + m_DialogListener->removeListener(); + m_DialogListener.set( new FormObjEventListenerImpl( this, xComponent, m_xModel ) ); + + triggerInitializeEvent(); + } + } + catch(const uno::Exception& ) + { + } + +} + +SbxVariable* +SbUserFormModule::Find( const OUString& rName, SbxClassType t ) +{ + if ( !pDocObject.is() && !GetSbData()->bRunInit && GetSbData()->pInst ) + InitObject(); + return SbObjModule::Find( rName, t ); +} + +SbProperty::SbProperty( const OUString& r, SbxDataType t, SbModule* p ) + : SbxProperty( r, t ), pMod( p ) +{ +} + +SbProperty::~SbProperty() +{} + + +SbProcedureProperty::~SbProcedureProperty() +{} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |