This would have caught the issue discussed in709b1f3ddb
"Make sure VCLXPopupMenu has unique RTTI". (The commit message talks about RTTI there, while what Clang actually compared for an optimized implementation of a dynamic_cast to a final class is vtable pointers, but the overall picture remains the same. Both RTTI and vtables are emitted along the key function, and if that is missing or inline, they are emitted for each dynamic library individually, and as internal symbols on macOS.) This commit also addresses all the issues found by the improved loplugin:dyncastvisibility on Linux. See the newly added TODO in compilerplugins/clang/dyncastvisibility.cxx and86b86ac87e
"Give DocumentEventHolder (aka EventHolder<DocumentEvent>) a key function" for an issue with key functions for class template instantiations. Change-Id: Ia19155efb1d23692c92b9c97ff17f18ae7a1f3ee Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176576 Reviewed-by: Stephan Bergmann <stephan.bergmann@allotropia.de> Tested-by: Jenkins
607 lines
16 KiB
C++
607 lines
16 KiB
C++
/* -*- 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 <config_features.h>
|
|
|
|
#include <tools/stream.hxx>
|
|
#include <svl/SfxBroadcaster.hxx>
|
|
|
|
#include <basic/sbx.hxx>
|
|
#include <runtime.hxx>
|
|
#include "sbxres.hxx"
|
|
#include "sbxconv.hxx"
|
|
#include <sbunoobj.hxx>
|
|
#include <rtl/ustrbuf.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <global.hxx>
|
|
#include <unotools/transliterationwrapper.hxx>
|
|
|
|
#include <com/sun/star/uno/XInterface.hpp>
|
|
#include <utility>
|
|
#include <filefmt.hxx>
|
|
using namespace com::sun::star::uno;
|
|
|
|
// SbxVariable
|
|
|
|
SbxVariable::SbxVariable()
|
|
{
|
|
}
|
|
|
|
SbxVariable::SbxVariable( const SbxVariable& r )
|
|
: SvRefBase( r ),
|
|
SbxValue( r ),
|
|
m_aDeclareClassName( r.m_aDeclareClassName ),
|
|
m_xComListener( r.m_xComListener),
|
|
mpPar( r.mpPar ),
|
|
pInfo( r.pInfo )
|
|
{
|
|
#if HAVE_FEATURE_SCRIPTING
|
|
if( r.m_xComListener.is() )
|
|
{
|
|
registerComListenerVariableForBasic( this, r.m_pComListenerParentBasic );
|
|
}
|
|
#endif
|
|
if( r.CanRead() )
|
|
{
|
|
pParent = r.pParent;
|
|
nUserData = r.nUserData;
|
|
maName = r.maName;
|
|
nHash = r.nHash;
|
|
}
|
|
}
|
|
|
|
SbxEnsureParentVariable::SbxEnsureParentVariable(const SbxVariable& r)
|
|
: SbxVariable(r)
|
|
, xParent(const_cast<SbxVariable&>(r).GetParent())
|
|
{
|
|
assert(GetParent() == xParent.get());
|
|
}
|
|
|
|
void SbxEnsureParentVariable::SetParent(SbxObject* p)
|
|
{
|
|
assert(GetParent() == xParent.get());
|
|
SbxVariable::SetParent(p);
|
|
xParent = SbxObjectRef(p);
|
|
assert(GetParent() == xParent.get());
|
|
}
|
|
|
|
SbxVariable::SbxVariable( SbxDataType t ) : SbxValue( t )
|
|
{
|
|
}
|
|
|
|
SbxVariable::~SbxVariable()
|
|
{
|
|
#if HAVE_FEATURE_SCRIPTING
|
|
if( IsSet( SbxFlagBits::DimAsNew ))
|
|
{
|
|
removeDimAsNewRecoverItem( this );
|
|
}
|
|
#endif
|
|
mpBroadcaster.reset();
|
|
}
|
|
|
|
// Broadcasting
|
|
|
|
SfxBroadcaster& SbxVariable::GetBroadcaster()
|
|
{
|
|
if( !mpBroadcaster )
|
|
{
|
|
mpBroadcaster.reset( new SfxBroadcaster );
|
|
}
|
|
return *mpBroadcaster;
|
|
}
|
|
|
|
SbxArray* SbxVariable::GetParameters() const
|
|
{
|
|
return mpPar.get();
|
|
}
|
|
|
|
|
|
// Perhaps some day one could cut the parameter 0.
|
|
// Then the copying will be dropped...
|
|
|
|
void SbxVariable::Broadcast( SfxHintId nHintId )
|
|
{
|
|
if( !mpBroadcaster || IsSet( SbxFlagBits::NoBroadcast ) )
|
|
return;
|
|
|
|
// Because the method could be called from outside, check the
|
|
// rights here again
|
|
if( nHintId == SfxHintId::BasicDataWanted )
|
|
{
|
|
if( !CanRead() )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if( nHintId == SfxHintId::BasicDataChanged )
|
|
{
|
|
if( !CanWrite() )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
//fdo#86843 Add a ref during the following block to guard against
|
|
//getting deleted before completing this method
|
|
SbxVariableRef aBroadcastGuard(this);
|
|
|
|
// Avoid further broadcasting
|
|
std::unique_ptr<SfxBroadcaster> pSave = std::move(mpBroadcaster);
|
|
SbxFlagBits nSaveFlags = GetFlags();
|
|
SetFlag( SbxFlagBits::ReadWrite );
|
|
if( mpPar.is() )
|
|
{
|
|
// Register this as element 0, but don't change over the parent!
|
|
mpPar->GetRef(0) = this;
|
|
}
|
|
pSave->Broadcast( SbxHint( nHintId, this ) );
|
|
mpBroadcaster = std::move(pSave);
|
|
SetFlags( nSaveFlags );
|
|
}
|
|
|
|
SbxInfo* SbxVariable::GetInfo()
|
|
{
|
|
if( !pInfo.is() )
|
|
{
|
|
Broadcast( SfxHintId::BasicInfoWanted );
|
|
if( pInfo.is() )
|
|
{
|
|
SetModified( true );
|
|
}
|
|
}
|
|
return pInfo.get();
|
|
}
|
|
|
|
void SbxVariable::SetInfo( SbxInfo* p )
|
|
{
|
|
pInfo = p;
|
|
}
|
|
|
|
void SbxVariable::SetParameters( SbxArray* p )
|
|
{
|
|
mpPar = p;
|
|
}
|
|
|
|
|
|
// Name of the variables
|
|
|
|
// static
|
|
OUString SbxVariable::NameToCaseInsensitiveName(const OUString& rName)
|
|
{
|
|
return SbGlobal::GetTransliteration().transliterate(rName, 0, rName.getLength());
|
|
}
|
|
|
|
void SbxVariable::SetName( const OUString& rName )
|
|
{
|
|
maName = rName;
|
|
nHash = MakeHashCode( rName );
|
|
maNameCI.clear();
|
|
}
|
|
|
|
const OUString& SbxVariable::GetName( SbxNameType t ) const
|
|
{
|
|
static const char cSuffixes[] = " %&!#@ $";
|
|
if( t == SbxNameType::NONE )
|
|
{
|
|
return maName;
|
|
}
|
|
if (t == SbxNameType::CaseInsensitive)
|
|
{
|
|
if (maNameCI.isEmpty() && !maName.isEmpty())
|
|
maNameCI = NameToCaseInsensitiveName(maName);
|
|
return maNameCI;
|
|
}
|
|
// Request parameter-information (not for objects)
|
|
const_cast<SbxVariable*>(this)->GetInfo();
|
|
// Append nothing, if it is a simple property (no empty brackets)
|
|
if (!pInfo.is() || (pInfo->m_Params.empty() && GetClass() == SbxClassType::Property))
|
|
{
|
|
return maName;
|
|
}
|
|
OUStringBuffer aTmp( maName );
|
|
if( t == SbxNameType::ShortTypes )
|
|
{
|
|
sal_Unicode cType = ' ';
|
|
// short type? Then fetch it, possible this is 0.
|
|
SbxDataType et = GetType();
|
|
if( et <= SbxSTRING )
|
|
{
|
|
assert(et >= 0 && size_t(et) < std::size(cSuffixes) - 1);
|
|
cType = cSuffixes[ et ];
|
|
}
|
|
if( cType != ' ' )
|
|
{
|
|
aTmp.append(cType);
|
|
}
|
|
}
|
|
aTmp.append("(");
|
|
|
|
for (SbxParams::const_iterator iter = pInfo->m_Params.begin(); iter != pInfo->m_Params.end(); ++iter)
|
|
{
|
|
auto const& i = *iter;
|
|
int nt = i->eType & 0x0FFF;
|
|
if (iter != pInfo->m_Params.begin())
|
|
{
|
|
aTmp.append(",");
|
|
}
|
|
if( i->nFlags & SbxFlagBits::Optional )
|
|
{
|
|
aTmp.append( GetSbxRes( StringId::Optional ) );
|
|
}
|
|
if( i->eType & SbxBYREF )
|
|
{
|
|
aTmp.append( GetSbxRes( StringId::ByRef ) );
|
|
}
|
|
aTmp.append( i->aName );
|
|
sal_Unicode cType = ' ';
|
|
// short type? Then fetch it, possible this is 0.
|
|
if( t == SbxNameType::ShortTypes )
|
|
{
|
|
if( nt <= SbxSTRING )
|
|
{
|
|
cType = cSuffixes[ nt ];
|
|
}
|
|
}
|
|
if( cType != ' ' )
|
|
{
|
|
aTmp.append(cType);
|
|
if( i->eType & SbxARRAY )
|
|
{
|
|
aTmp.append("()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( i->eType & SbxARRAY )
|
|
{
|
|
aTmp.append("()");
|
|
}
|
|
// long type?
|
|
aTmp.append(GetSbxRes( StringId::As ));
|
|
if( nt < 32 )
|
|
{
|
|
aTmp.append(GetSbxRes( static_cast<StringId>( static_cast<int>( StringId::Types ) + nt ) ));
|
|
}
|
|
else
|
|
{
|
|
aTmp.append(GetSbxRes( StringId::Any ));
|
|
}
|
|
}
|
|
}
|
|
aTmp.append(")");
|
|
const_cast<SbxVariable*>(this)->aToolString = aTmp.makeStringAndClear();
|
|
return aToolString;
|
|
}
|
|
|
|
// Operators
|
|
|
|
SbxVariable& SbxVariable::operator=( const SbxVariable& r )
|
|
{
|
|
if (this != &r)
|
|
{
|
|
SbxValue::operator=( r );
|
|
// tdf#144353 - copy information about a missing parameter. See SbiRuntime::SetIsMissing.
|
|
// We cannot unconditionally assign the data about a variable because we would overwrite
|
|
// the information about parameters (name, type, flags, and ids). For instance, in the case
|
|
// where a method will be initialized with a literal.
|
|
if (!pInfo)
|
|
pInfo = r.pInfo;
|
|
m_aDeclareClassName = r.m_aDeclareClassName;
|
|
m_xComListener = r.m_xComListener;
|
|
m_pComListenerParentBasic = r.m_pComListenerParentBasic;
|
|
#if HAVE_FEATURE_SCRIPTING
|
|
if( m_xComListener.is() )
|
|
{
|
|
registerComListenerVariableForBasic( this, m_pComListenerParentBasic );
|
|
}
|
|
#endif
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
// Conversion
|
|
|
|
SbxDataType SbxVariable::GetType() const
|
|
{
|
|
if( aData.eType == SbxOBJECT )
|
|
{
|
|
return aData.pObj ? aData.pObj->GetType() : SbxOBJECT;
|
|
}
|
|
else if( aData.eType == SbxVARIANT )
|
|
{
|
|
return aData.pObj ? aData.pObj->GetType() : SbxVARIANT;
|
|
}
|
|
else
|
|
{
|
|
return aData.eType;
|
|
}
|
|
}
|
|
|
|
SbxClassType SbxVariable::GetClass() const
|
|
{
|
|
return SbxClassType::Variable;
|
|
}
|
|
|
|
void SbxVariable::SetModified( bool b )
|
|
{
|
|
if( IsSet( SbxFlagBits::NoModify ) )
|
|
{
|
|
return;
|
|
}
|
|
SbxBase::SetModified( b );
|
|
if( pParent && pParent != this ) //??? HotFix: Recursion out here MM
|
|
{
|
|
pParent->SetModified( b );
|
|
}
|
|
}
|
|
|
|
void SbxVariable::SetParent( SbxObject* p )
|
|
{
|
|
#ifdef DBG_UTIL
|
|
// Will the parent of a SbxObject be set?
|
|
if (p && dynamic_cast<SbxObject*>(this))
|
|
{
|
|
// then this had to be a child of the new parent
|
|
bool bFound = false;
|
|
SbxArray *pChildren = p->GetObjects();
|
|
if ( pChildren )
|
|
{
|
|
for (sal_uInt32 nIdx = 0; !bFound && nIdx < pChildren->Count(); ++nIdx)
|
|
{
|
|
bFound = (this == pChildren->Get(nIdx));
|
|
}
|
|
}
|
|
SAL_INFO_IF(
|
|
!bFound, "basic.sbx",
|
|
"dangling: [" << GetName() << "].SetParent([" << p->GetName()
|
|
<< "])");
|
|
}
|
|
#endif
|
|
|
|
pParent = p;
|
|
}
|
|
|
|
const OUString& SbxVariable::GetDeclareClassName() const
|
|
{
|
|
return m_aDeclareClassName;
|
|
}
|
|
|
|
void SbxVariable::SetDeclareClassName( const OUString& rDeclareClassName )
|
|
{
|
|
m_aDeclareClassName = rDeclareClassName;
|
|
}
|
|
|
|
void SbxVariable::SetComListener( const css::uno::Reference< css::uno::XInterface >& xComListener,
|
|
StarBASIC* pParentBasic )
|
|
{
|
|
m_xComListener = xComListener;
|
|
m_pComListenerParentBasic = pParentBasic;
|
|
#if HAVE_FEATURE_SCRIPTING
|
|
registerComListenerVariableForBasic( this, pParentBasic );
|
|
#endif
|
|
}
|
|
|
|
void SbxVariable::ClearComListener()
|
|
{
|
|
m_xComListener.clear();
|
|
}
|
|
|
|
|
|
// Loading/Saving
|
|
|
|
bool SbxVariable::LoadData( SvStream& rStrm, sal_uInt16 nVer )
|
|
{
|
|
sal_uInt8 cMark;
|
|
rStrm.ReadUChar( cMark );
|
|
if( cMark == 0xFF )
|
|
{
|
|
if( !SbxValue::LoadData( rStrm, nVer ) )
|
|
{
|
|
return false;
|
|
}
|
|
maName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
|
|
RTL_TEXTENCODING_ASCII_US);
|
|
sal_uInt32 nTemp;
|
|
rStrm.ReadUInt32( nTemp );
|
|
nUserData = nTemp;
|
|
}
|
|
else
|
|
{
|
|
sal_uInt16 nType;
|
|
rStrm.SeekRel( -1 );
|
|
rStrm.ReadUInt16( nType );
|
|
maName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
|
|
RTL_TEXTENCODING_ASCII_US);
|
|
sal_uInt32 nTemp;
|
|
rStrm.ReadUInt32( nTemp );
|
|
nUserData = nTemp;
|
|
// correction: old methods have instead of SbxNULL now SbxEMPTY
|
|
if( nType == SbxNULL && GetClass() == SbxClassType::Method )
|
|
{
|
|
nType = SbxEMPTY;
|
|
}
|
|
SbxValues aTmp;
|
|
OUString aTmpString;
|
|
OUString aVal;
|
|
aTmp.eType = aData.eType = static_cast<SbxDataType>(nType);
|
|
aTmp.pOUString = &aVal;
|
|
switch( nType )
|
|
{
|
|
case SbxBOOL:
|
|
case SbxERROR:
|
|
case SbxINTEGER:
|
|
rStrm.ReadInt16( aTmp.nInteger ); break;
|
|
case SbxLONG:
|
|
rStrm.ReadInt32( aTmp.nLong ); break;
|
|
case SbxSINGLE:
|
|
{
|
|
// Floats as ASCII
|
|
aTmpString = read_uInt16_lenPrefixed_uInt8s_ToOUString(
|
|
rStrm, RTL_TEXTENCODING_ASCII_US);
|
|
double d;
|
|
SbxDataType t;
|
|
if( ImpScan( aTmpString, d, t, nullptr ) != ERRCODE_NONE || t == SbxDOUBLE )
|
|
{
|
|
aTmp.nSingle = 0;
|
|
return false;
|
|
}
|
|
aTmp.nSingle = static_cast<float>(d);
|
|
break;
|
|
}
|
|
case SbxDATE:
|
|
case SbxDOUBLE:
|
|
{
|
|
// Floats as ASCII
|
|
aTmpString = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
|
|
RTL_TEXTENCODING_ASCII_US);
|
|
SbxDataType t;
|
|
if( ImpScan( aTmpString, aTmp.nDouble, t, nullptr ) != ERRCODE_NONE )
|
|
{
|
|
aTmp.nDouble = 0;
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case SbxSTRING:
|
|
aVal = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
|
|
RTL_TEXTENCODING_ASCII_US);
|
|
break;
|
|
case SbxEMPTY:
|
|
case SbxNULL:
|
|
break;
|
|
default:
|
|
aData.eType = SbxNULL;
|
|
SAL_WARN( "basic.sbx", "Loaded a non-supported data type" );
|
|
return false;
|
|
}
|
|
// putt value
|
|
if( nType != SbxNULL && nType != SbxEMPTY && !Put( aTmp ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
rStrm.ReadUChar( cMark );
|
|
// cMark is also a version number!
|
|
// 1: initial version
|
|
// 2: with nUserData
|
|
if( cMark )
|
|
{
|
|
if( cMark > 2 )
|
|
{
|
|
return false;
|
|
}
|
|
pInfo = new SbxInfo;
|
|
pInfo->LoadData( rStrm, static_cast<sal_uInt16>(cMark) );
|
|
}
|
|
Broadcast( SfxHintId::BasicDataChanged );
|
|
nHash = MakeHashCode( maName );
|
|
SetModified( true );
|
|
return true;
|
|
}
|
|
|
|
std::pair<bool, sal_uInt32> SbxVariable::StoreData( SvStream& rStrm ) const
|
|
{
|
|
rStrm.WriteUChar( 0xFF ); // Marker
|
|
bool bValStore;
|
|
if( dynamic_cast<const SbxMethod *>(this) != nullptr )
|
|
{
|
|
// #50200 Avoid that objects , which during the runtime
|
|
// as return-value are saved in the method as a value were saved
|
|
SbxVariable* pThis = const_cast<SbxVariable*>(this);
|
|
SbxFlagBits nSaveFlags = GetFlags();
|
|
pThis->SetFlag( SbxFlagBits::Write );
|
|
pThis->SbxValue::Clear();
|
|
pThis->SetFlags( nSaveFlags );
|
|
|
|
// So that the method will not be executed in any case!
|
|
// CAST, to avoid const!
|
|
pThis->SetFlag( SbxFlagBits::NoBroadcast );
|
|
bValStore = SbxValue::StoreData( rStrm ).first;
|
|
pThis->ResetFlag( SbxFlagBits::NoBroadcast );
|
|
}
|
|
else
|
|
{
|
|
bValStore = SbxValue::StoreData( rStrm ).first;
|
|
}
|
|
if( !bValStore )
|
|
{
|
|
return { false, 0 };
|
|
}
|
|
write_uInt16_lenPrefixed_uInt8s_FromOUString(rStrm, maName,
|
|
RTL_TEXTENCODING_ASCII_US);
|
|
rStrm.WriteUInt32( nUserData );
|
|
if( pInfo.is() )
|
|
{
|
|
rStrm.WriteUChar( 2 ); // Version 2: with UserData!
|
|
pInfo->StoreData( rStrm );
|
|
}
|
|
else
|
|
{
|
|
rStrm.WriteUChar( 0 );
|
|
}
|
|
return { true, B_IMG_VERSION_12 };
|
|
}
|
|
|
|
// SbxInfo
|
|
|
|
SbxInfo::SbxInfo()
|
|
: nHelpId(0)
|
|
{}
|
|
|
|
SbxInfo::SbxInfo( OUString a, sal_uInt32 n )
|
|
: aHelpFile(std::move( a )), nHelpId( n )
|
|
{}
|
|
|
|
SbxHint::~SbxHint() = default;
|
|
|
|
void SbxVariable::Dump( SvStream& rStrm, bool bFill )
|
|
{
|
|
OString aBNameStr(OUStringToOString(GetName( SbxNameType::ShortTypes ), RTL_TEXTENCODING_ASCII_US));
|
|
rStrm.WriteOString( "Variable( " )
|
|
.WriteOString( OString::number(reinterpret_cast<sal_IntPtr>(this)) ).WriteOString( "==" )
|
|
.WriteOString( aBNameStr );
|
|
OString aBParentNameStr(OUStringToOString(GetParent()->GetName(), RTL_TEXTENCODING_ASCII_US));
|
|
if ( GetParent() )
|
|
{
|
|
rStrm.WriteOString( " in parent '" ).WriteOString( aBParentNameStr ).WriteOString( "'" );
|
|
}
|
|
else
|
|
{
|
|
rStrm.WriteOString( " no parent" );
|
|
}
|
|
rStrm.WriteOString( " ) " );
|
|
|
|
// output also the object at object-vars
|
|
if ( GetValues_Impl().eType == SbxOBJECT &&
|
|
GetValues_Impl().pObj &&
|
|
GetValues_Impl().pObj != this &&
|
|
GetValues_Impl().pObj != GetParent() )
|
|
{
|
|
rStrm.WriteOString( " contains " );
|
|
static_cast<SbxObject*>(GetValues_Impl().pObj)->Dump( rStrm, bFill );
|
|
}
|
|
else
|
|
{
|
|
rStrm << endl;
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|