Files
odfstudio/basic/source/sbx/sbxvar.cxx
T
Caolán McNamara 2399c6b9e5 clear the old value before loading an old format SbxVariable
as the newer format already does

The old format path set the new type without clearing an existing value
first, so the new type bogusly redefined whatever the union still held.

Signed-off-by: Caolán McNamara <caolan.mcnamara@collabora.com>
Change-Id: I70d02026d24443ab353be37c0a57685b4e7ac969
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13600
Reviewed-by: Noel Grandin <noel.grandin@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 27d9427be60d57d3054c01c8806c81b89bd3872d)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211530
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
2026-09-29 08:46:04 +02:00

609 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("(");
bool first = true;
for (auto const& i : pInfo->m_Params)
{
int nt = i->eType & 0x0FFF;
if (!first)
{
aTmp.append(",");
}
first = false;
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;
SbxValue::Clear();
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: */