summaryrefslogtreecommitdiffstats
path: root/basic/source/classes
diff options
context:
space:
mode:
Diffstat (limited to 'basic/source/classes')
-rw-r--r--basic/source/classes/codecompletecache.cxx197
-rw-r--r--basic/source/classes/errobject.cxx219
-rw-r--r--basic/source/classes/eventatt.cxx548
-rw-r--r--basic/source/classes/global.cxx45
-rw-r--r--basic/source/classes/image.cxx696
-rw-r--r--basic/source/classes/propacc.cxx176
-rw-r--r--basic/source/classes/sb.cxx2237
-rw-r--r--basic/source/classes/sbintern.cxx52
-rw-r--r--basic/source/classes/sbunoobj.cxx4904
-rw-r--r--basic/source/classes/sbxmod.cxx2682
10 files changed, 11756 insertions, 0 deletions
diff --git a/basic/source/classes/codecompletecache.cxx b/basic/source/classes/codecompletecache.cxx
new file mode 100644
index 0000000000..8f03797c32
--- /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 0000000000..85423101b8
--- /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 0000000000..47930d1f31
--- /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 <comphelper/diagnose_ex.hxx>
+#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 0000000000..d2e3622b4d
--- /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 0000000000..ee49094702
--- /dev/null
+++ b/basic/source/classes/image.cxx
@@ -0,0 +1,696 @@
+/* -*- 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_IMG_VERSION_13 );
+ nDimBase = static_cast<sal_uInt16>(lDimBase);
+ }
+
+ bool bLegacy = ( nVersion < B_IMG_VERSION_12 );
+
+ 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 )
+{
+ // First of all the header
+ sal_uInt64 nStart = SbiOpenRecord( r, FileOffset::Module, 1 );
+ sal_uInt64 nPos;
+
+ eCharSet = GetSOStoreTextEncoding( eCharSet );
+ r .WriteInt32( nVer )
+ .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 );
+ 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);
+}
+
+bool SbiImage::ExceedsImgVersion12Limits()
+{
+ const sal_Int16 nMax = std::numeric_limits<sal_Int16>::max();
+ return nStringSize >= nMax || CalcLegacyOffset(aCode.size()) >= nMax;
+}
+
+/* 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 0000000000..f9eacc3d12
--- /dev/null
+++ b/basic/source/classes/propacc.cxx
@@ -0,0 +1,176 @@
+/* -*- 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", getXWeak(), -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(getXWeak(new SbPropertyValues()));
+
+ SbxVariableRef refVar = rPar.Get(0);
+ // 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 0000000000..8187e61266
--- /dev/null
+++ b/basic/source/classes/sb.cxx
@@ -0,0 +1,2237 @@
+/* -*- 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 OUString SB_RTLNAME = u"@SBRTL"_ustr;
+// 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 );
+ mbVBASupport = pClassModule->mbVBASupport;
+}
+
+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 this because:
+ // coverity[leaked_storage] - we do not actually own it
+ 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.emplace();
+ AddFactory( &*GetSbData()->pSbFac );
+ GetSbData()->pTypeFac.emplace();
+ AddFactory( &*GetSbData()->pTypeFac );
+ GetSbData()->pClassFac.reset(new SbClassFactory);
+ AddFactory( GetSbData()->pClassFac.get() );
+ GetSbData()->pOLEFac.emplace();
+ AddFactory( &*GetSbData()->pOLEFac );
+ GetSbData()->pFormFac.emplace();
+ AddFactory( &*GetSbData()->pFormFac );
+ GetSbData()->pUnoFac.emplace();
+ AddFactory( &*GetSbData()->pUnoFac );
+ }
+ 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 );
+ GetSbData()->pSbFac.reset();
+ RemoveFactory( &*GetSbData()->pUnoFac );
+ GetSbData()->pUnoFac.reset();
+ RemoveFactory( &*GetSbData()->pTypeFac );
+ GetSbData()->pTypeFac.reset();
+ RemoveFactory( GetSbData()->pClassFac.get() );
+ GetSbData()->pClassFac.reset();
+ RemoveFactory( &*GetSbData()->pOLEFac );
+ GetSbData()->pOLEFac.reset();
+ RemoveFactory( &*GetSbData()->pFormFac );
+ 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;
+ std::erase(pModules, 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 OUString aMainStr(u"Main"_ustr);
+ 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();
+ if( eErr != ERRCODE_NONE )
+ {
+ RTError(eErr, SbxBase::GetErrorMsg(), 0, 0, 0);
+ }
+ SbxBase::ResetError();
+ }
+ 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
+ErrCodeMsg 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( const ErrCodeMsg& 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
+ ErrCodeMsg nErr = code;
+ if( !rMsg.isEmpty() )
+ {
+ nErr = ErrCodeMsg( code, rMsg );
+ }
+ SetErrorData( nErr, 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
+ ErrCodeMsg nErr = code;
+ 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);
+ nErr = ErrCodeMsg( code, aTmp );
+ }
+ else
+ {
+ nErr = ErrCodeMsg( code, rMsg );
+ }
+ }
+
+ SetErrorData( nErr, 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;
+}
+
+std::pair<bool, sal_uInt32> StarBASIC::StoreData( SvStream& r ) const
+{
+ auto [bSuccess, nVersion] = SbxObject::StoreData(r);
+ if( !bSuccess )
+ {
+ return { false, 0 };
+ }
+ assert(pModules.size() < SAL_MAX_UINT16);
+ r.WriteUInt16( static_cast<sal_uInt16>(pModules.size()));
+ for( const auto& rpModule: pModules )
+ {
+ const auto& [bSuccessModule, nVersionModule] = rpModule->Store(r);
+ if( !bSuccessModule )
+ {
+ return { false, 0 };
+ }
+ else if (nVersionModule > nVersion)
+ {
+ nVersion = nVersionModule;
+ }
+ }
+ return { true, nVersion };
+}
+
+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 constexpr 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
+
+
+// [-loplugin:ostr]
+constexpr OUStringLiteral pCountStr = u"Count";
+// [-loplugin:ostr]
+constexpr OUStringLiteral pAddStr = u"Add";
+// [-loplugin:ostr]
+constexpr OUStringLiteral pItemStr = u"Item";
+// [-loplugin:ostr]
+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 0000000000..fd72949d54
--- /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 0000000000..bf740aaafc
--- /dev/null
+++ b/basic/source/classes/sbunoobj.cxx
@@ -0,0 +1,4904 @@
+/* -*- 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 <comphelper/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 OUString ID_DBG_SUPPORTEDINTERFACES = u"Dbg_SupportedInterfaces"_ustr;
+constexpr OUString ID_DBG_PROPERTIES = u"Dbg_Properties"_ustr;
+constexpr OUString ID_DBG_METHODS = u"Dbg_Methods"_ustr;
+
+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
+ {
+ if (eBaseType == SbxBYTE && pVal->GetByte() > 127)
+ {
+ // Basic Byte type is unsigned; cppu::UnoType<sal_uInt8> corresponds to UNO boolean,
+ // so values 128-255 are only representable starting with UNO short types
+ eBaseType = SbxUSHORT;
+ }
+ 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_LONG:
+ {
+ sal_uInt32 n = pVar->GetLong();
+ 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( "\"" + aName + "\":" );
+ 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
+ + " not available.\n(TypeClass is not TypeClass_INTERFACE)\n" );
+ }
+ else
+ {
+ Reference< XTypeProvider > xTypeProvider( *x, UNO_QUERY );
+
+ aRet.append( "Supported interfaces by object "
+ + getDbgObjectName(rUnoObj)
+ + "\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( OUString::Concat("*** ERROR: No IdlClass for type \"")
+ + OUString::unacquired(&pTD->pTypeName)
+ + "\"\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("Properties of object " + 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( " " + 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("Methods of object " + 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 )
+ + " " + pVar->GetName() + " ( " );
+
+ // 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>( ID_DBG_SUPPORTEDINTERFACES, SbxSTRING, SbxSTRING, aProp, -1, false, false );
+ QuickInsert( xVarRef.get() );
+
+ // Id == -2: output the properties
+ xVarRef = tools::make_ref<SbUnoProperty>( ID_DBG_PROPERTIES, SbxSTRING, SbxSTRING, aProp, -2, false, false );
+ QuickInsert( xVarRef.get() );
+
+ // Id == -3: output the Methods
+ xVarRef = tools::make_ref<SbUnoProperty>( 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 = getXWeak();
+ 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 bOldReschedule = false;
+ SbiInstance* pInst = GetSbData()->pInst;
+ if( pInst && pInst->IsCompatibility() )
+ {
+ bOldReschedule = pInst->IsReschedule();
+ if ( bOldReschedule )
+ pInst->EnableReschedule( false );
+ }
+
+ 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 (bOldReschedule)
+ 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, std::move(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("Properties of object " + 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 )
+ + " " + 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( "\"" + aName + "\":" );
+ 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 0000000000..59eb93f665
--- /dev/null
+++ b/basic/source/classes/sbxmod.cxx
@@ -0,0 +1,2682 @@
+/* -*- 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 <comphelper/diagnose_ex.hxx>
+#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( getXWeak() );
+ }
+
+ 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 bVBASupport )
+ : SbxObject( "StarBASICModule" ),
+ pBreaks(nullptr), mbVBASupport(bVBASupport), mbCompat(bVBASupport), 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
+ SetVBASupport( getDefaultVBAMode( static_cast< StarBASIC*>( GetParent() ) ) );
+ aOUSource = r;
+ StartDefinitions();
+ SbiTokenizer aTok( r );
+ aTok.SetCompatible( IsVBASupport() );
+
+ 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 )
+ {
+ mbCompat = true;
+ aTok.SetCompatible( true );
+ }
+ else if ( ( eCurTok == VBASUPPORT ) && ( aTok.Next() == NUMBER ) )
+ {
+ bool bIsVBA = ( aTok.GetDbl()== 1 );
+ SetVBASupport( 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 held in RTL functions,
+// at the end of the program, so that nothing is held
+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::SetVBASupport( bool bSupport )
+{
+ if( mbVBASupport == bSupport )
+ return;
+
+ mbVBASupport = bSupport;
+ // initialize VBA document API
+ if( mbVBASupport ) try
+ {
+ mbCompat = true;
+ 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 " << mbVBASupport );
+
+ 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( mbVBASupport )
+ {
+ 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() );
+ }
+
+ {
+ RunGuard xRuntimeGuard(this, pMeth, pMeth->nStart, pSbData, bDelInst);
+
+ if (mbVBASupport)
+ pSbData->pInst->EnableCompatibility(true);
+
+ xRuntimeGuard.run();
+ }
+
+ if( bDelInst )
+ {
+ // #57841 Clear Uno-Objects, which were held in RTL functions,
+ // at the end of the program, so that nothing is held.
+ 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 held in RTL functions,
+ // the end of the program, so that nothing is held.
+ 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
+ RunInitGuard(this, nullptr, 0, pSbData).run();
+
+ 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_IMG_VERSION_12 )
+ {
+ 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;
+}
+
+std::pair<bool, sal_uInt32> SbModule::StoreData( SvStream& rStrm ) const
+{
+ bool bFixup = ( pImage && !pImage->ExceedsLegacyLimits() );
+ if ( bFixup )
+ fixUpMethodStart( true );
+ const auto& [bSuccess, nVersion] = SbxObject::StoreData(rStrm);
+ if (!bSuccess)
+ return { false, 0 };
+
+ 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, nVersion );
+ if ( bFixup )
+ fixUpMethodStart( false ); // restore method starts
+ return { bRes, nVersion };
+
+ }
+ else
+ {
+ SbiImage aImg;
+ aImg.aOUSource = aOUSource;
+ aImg.aComment = aComment;
+ aImg.aName = GetName();
+ rStrm.WriteUChar( 1 );
+ return { aImg.Save(rStrm, nVersion), nVersion };
+ }
+}
+
+bool SbModule::ExceedsImgVersion12ModuleSize()
+{
+ if ( !IsCompiled() )
+ Compile();
+ return pImage && pImage->ExceedsImgVersion12Limits();
+}
+
+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( std::u16string_view 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;
+
+ const auto& [bSuccess, nVersion] = SbxObject::StoreData(rStrm);
+ if (!bSuccess)
+ return;
+
+ pImage->aOUSource.clear();
+ pImage->aComment = aComment;
+ pImage->aName = GetName();
+
+ rStrm.WriteUChar(1);
+ pImage->Save(rStrm, nVersion);
+
+ 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;
+}
+
+std::pair<bool, sal_uInt32> SbJScriptModule::StoreData( SvStream& rStrm ) const
+{
+ const auto& [bSuccess, nVersion] = SbxObject::StoreData(rStrm);
+ if( !bSuccess )
+ return { false, 0 };
+
+ // Write the source string
+ OUString aTmp = aOUSource;
+ rStrm.WriteUniOrByteString( aTmp, osl_getThreadTextEncoding() );
+ return { true, nVersion };
+}
+
+
+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;
+}
+
+std::pair<bool, sal_uInt32> SbMethod::StoreData( SvStream& rStrm ) const
+{
+ auto [bSuccess, nVersion] = SbxMethod::StoreData(rStrm);
+ if( !bSuccess )
+ return { false, 0 };
+
+ //tdf#94617
+ const sal_uInt32 nMax = std::numeric_limits<sal_Int16>::max();
+ // tdf#142391 - store method using binary format 0x13 only when actually needed, i.e.,
+ // when method starts at an offset that would overflow 16 bits
+ const sal_Int16 nStartTemp = nStart % nMax;
+ sal_uInt16 nDebugFlagsTemp = static_cast<sal_uInt16>(nDebugFlags);
+ if (nStart >= nMax)
+ {
+ assert(nStart <= nMax * 0x7FFF); // Larger addresses can't be stored in version 13
+ nDebugFlagsTemp = (nStart / nMax) | 0x8000;
+ nVersion = B_IMG_VERSION_13;
+ }
+
+ rStrm.WriteUInt16( nDebugFlagsTemp )
+ .WriteInt16( nLine1 )
+ .WriteInt16( nLine2 )
+ .WriteInt16( nStartTemp )
+ .WriteBool( bInvalid );
+
+ return { true, nVersion };
+}
+
+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
+ Clear();
+
+ 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 = 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 ) )
+ {
+ SolarMutexGuard g;
+ 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, IsVBASupport() );
+ 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: */