Files
libreoffice/basic/source/sbx/sbxvar.cxx
Stephan Bergmann 16e3b84d2e Improve loplugin:dyncastvisibility to check for non-inline key functions
This would have caught the issue discussed in
709b1f3ddb "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 and
86b86ac87e "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
2024-11-14 12:49:22 +01:00

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: */