diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /basic/source/classes/sbxmod.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'basic/source/classes/sbxmod.cxx')
-rw-r--r-- | basic/source/classes/sbxmod.cxx | 2682 |
1 files changed, 2682 insertions, 0 deletions
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: */ |