1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <config_features.h>
21 #include <o3tl/safeint.hxx>
22 #include <tools/debug.hxx>
23 #include <tools/stream.hxx>
24 #include <basic/sbx.hxx>
25 #include <runtime.hxx>
26 
27 #include <optional>
28 
29 struct SbxVarEntry
30 {
31     SbxVariableRef mpVar;
32     std::optional<OUString> maAlias;
33 };
34 
35 
36 //  SbxArray
37 
SbxArray(SbxDataType t)38 SbxArray::SbxArray( SbxDataType t ) : SbxBase()
39 {
40     eType = t;
41     if( t != SbxVARIANT )
42         SetFlag( SbxFlagBits::Fixed );
43 }
44 
operator =(const SbxArray & rArray)45 SbxArray& SbxArray::operator=( const SbxArray& rArray )
46 {
47     if( &rArray != this )
48     {
49         eType = rArray.eType;
50         Clear();
51         for( const auto& rpSrcRef : rArray.mVarEntries )
52         {
53             SbxVariableRef pSrc_ = rpSrcRef.mpVar;
54             if( !pSrc_.is() )
55                 continue;
56 
57             if( eType != SbxVARIANT )
58             {
59                 // Convert no objects
60                 if( eType != SbxOBJECT || pSrc_->GetClass() != SbxClassType::Object )
61                 {
62                     pSrc_->Convert(eType);
63                 }
64             }
65             mVarEntries.push_back( rpSrcRef );
66         }
67     }
68     return *this;
69 }
70 
~SbxArray()71 SbxArray::~SbxArray()
72 {
73 }
74 
GetType() const75 SbxDataType SbxArray::GetType() const
76 {
77     return static_cast<SbxDataType>( eType | SbxARRAY );
78 }
79 
Clear()80 void SbxArray::Clear()
81 {
82     mVarEntries.clear();
83 }
84 
Count() const85 sal_uInt32 SbxArray::Count() const
86 {
87     return mVarEntries.size();
88 }
89 
GetRef(sal_uInt32 nIdx)90 SbxVariableRef& SbxArray::GetRef( sal_uInt32 nIdx )
91 {
92     // If necessary extend the array
93     DBG_ASSERT( nIdx <= SBX_MAXINDEX32, "SBX: Array-Index > SBX_MAXINDEX32" );
94     // Very Hot Fix
95     if( nIdx > SBX_MAXINDEX32 )
96     {
97         SetError( ERRCODE_BASIC_OUT_OF_RANGE );
98         nIdx = 0;
99     }
100     if ( mVarEntries.size() <= nIdx )
101         mVarEntries.resize(nIdx+1);
102 
103     return mVarEntries[nIdx].mpVar;
104 }
105 
Get(sal_uInt32 nIdx)106 SbxVariable* SbxArray::Get( sal_uInt32 nIdx )
107 {
108     if( !CanRead() )
109     {
110         SetError( ERRCODE_BASIC_PROP_WRITEONLY );
111         return nullptr;
112     }
113     SbxVariableRef& rRef = GetRef( nIdx );
114 
115     if ( !rRef.is() )
116         rRef = new SbxVariable( eType );
117 
118     return rRef.get();
119 }
120 
Put(SbxVariable * pVar,sal_uInt32 nIdx)121 void SbxArray::Put( SbxVariable* pVar, sal_uInt32 nIdx )
122 {
123     if( !CanWrite() )
124         SetError( ERRCODE_BASIC_PROP_READONLY );
125     else
126     {
127         if( pVar )
128             if( eType != SbxVARIANT )
129                 // Convert no objects
130                 if( eType != SbxOBJECT || pVar->GetClass() != SbxClassType::Object )
131                     pVar->Convert( eType );
132         SbxVariableRef& rRef = GetRef( nIdx );
133         // tdf#122250. It is possible that I hold the last reference to myself, so check, otherwise I might
134         // call SetFlag on myself after I have died.
135         bool removingMyself = rRef && rRef->GetParameters() == this && GetRefCount() == 1;
136         if( rRef.get() != pVar )
137         {
138             rRef = pVar;
139             if (!removingMyself)
140                 SetFlag( SbxFlagBits::Modified );
141         }
142     }
143 }
144 
GetAlias(sal_uInt32 nIdx)145 OUString SbxArray::GetAlias( sal_uInt32 nIdx )
146 {
147     if( !CanRead() )
148     {
149         SetError( ERRCODE_BASIC_PROP_WRITEONLY );
150         return OUString();
151     }
152     SbxVarEntry& rRef = reinterpret_cast<SbxVarEntry&>(GetRef( nIdx ));
153 
154     if (!rRef.maAlias)
155         return OUString();
156 
157     return *rRef.maAlias;
158 }
159 
PutAlias(const OUString & rAlias,sal_uInt32 nIdx)160 void SbxArray::PutAlias( const OUString& rAlias, sal_uInt32 nIdx )
161 {
162     if( !CanWrite() )
163     {
164         SetError( ERRCODE_BASIC_PROP_READONLY );
165     }
166     else
167     {
168         SbxVarEntry& rRef = reinterpret_cast<SbxVarEntry&>( GetRef( nIdx ) );
169         rRef.maAlias = rAlias;
170     }
171 }
172 
Insert(SbxVariable * pVar,sal_uInt32 nIdx)173 void SbxArray::Insert( SbxVariable* pVar, sal_uInt32 nIdx )
174 {
175     DBG_ASSERT( mVarEntries.size() <= SBX_MAXINDEX32, "SBX: Array gets too big" );
176     if( mVarEntries.size() > SBX_MAXINDEX32 )
177     {
178             return;
179     }
180     SbxVarEntry p;
181     p.mpVar = pVar;
182     size_t nSize = mVarEntries.size();
183     if( nIdx > nSize )
184     {
185         nIdx = nSize;
186     }
187     if( eType != SbxVARIANT && pVar )
188     {
189         p.mpVar->Convert(eType);
190     }
191     if( nIdx == nSize )
192     {
193         mVarEntries.push_back( p );
194     }
195     else
196     {
197         mVarEntries.insert( mVarEntries.begin() + nIdx, p );
198     }
199     SetFlag( SbxFlagBits::Modified );
200 }
201 
Remove(sal_uInt32 nIdx)202 void SbxArray::Remove( sal_uInt32 nIdx )
203 {
204     if( nIdx < mVarEntries.size() )
205     {
206         mVarEntries.erase( mVarEntries.begin() + nIdx );
207         SetFlag( SbxFlagBits::Modified );
208     }
209 }
210 
Remove(SbxVariable const * pVar)211 void SbxArray::Remove( SbxVariable const * pVar )
212 {
213     if( pVar )
214     {
215         for( size_t i = 0; i < mVarEntries.size(); i++ )
216         {
217             if (mVarEntries[i].mpVar.get() == pVar)
218             {
219                 Remove( i ); break;
220             }
221         }
222     }
223 }
224 
225 // Taking over of the data from the passed array, at which
226 // the variable of the same name will be overwritten.
227 
Merge(SbxArray * p)228 void SbxArray::Merge( SbxArray* p )
229 {
230     if (!p)
231         return;
232 
233     for (auto& rEntry1: p->mVarEntries)
234     {
235         if (!rEntry1.mpVar.is())
236             continue;
237 
238         OUString aName = rEntry1.mpVar->GetName();
239         sal_uInt16 nHash = rEntry1.mpVar->GetHashCode();
240 
241         // Is the element by the same name already inside?
242         // Then overwrite!
243         for (auto& rEntry2: mVarEntries)
244         {
245             if (!rEntry2.mpVar.is())
246                 continue;
247 
248             if (rEntry2.mpVar->GetHashCode() == nHash &&
249                 rEntry2.mpVar->GetName().equalsIgnoreAsciiCase(aName))
250             {
251                 // Take this element and clear the original.
252                 rEntry2.mpVar = rEntry1.mpVar;
253                 rEntry1.mpVar.clear();
254                 break;
255             }
256         }
257 
258         if (rEntry1.mpVar.is())
259         {
260             // We don't have element with the same name.  Add a new entry.
261             SbxVarEntry aNewEntry;
262             aNewEntry.mpVar = rEntry1.mpVar;
263             if (rEntry1.maAlias)
264                 aNewEntry.maAlias = *rEntry1.maAlias;
265             mVarEntries.push_back(aNewEntry);
266         }
267     }
268 }
269 
270 // Search of an element by his name and type. If an element is an object,
271 // it will also be scanned...
272 
Find(const OUString & rName,SbxClassType t)273 SbxVariable* SbxArray::Find( const OUString& rName, SbxClassType t )
274 {
275     SbxVariable* p = nullptr;
276     if( mVarEntries.empty() )
277         return nullptr;
278     bool bExtSearch = IsSet( SbxFlagBits::ExtSearch );
279     sal_uInt16 nHash = SbxVariable::MakeHashCode( rName );
280     for (auto& rEntry : mVarEntries)
281     {
282         if (!rEntry.mpVar.is() || !rEntry.mpVar->IsVisible())
283             continue;
284 
285         // The very secure search works as well, if there is no hashcode!
286         sal_uInt16 nVarHash = rEntry.mpVar->GetHashCode();
287         if ( (!nVarHash || nVarHash == nHash)
288             && (t == SbxClassType::DontCare || rEntry.mpVar->GetClass() == t)
289             && (rEntry.mpVar->GetName().equalsIgnoreAsciiCase(rName)))
290         {
291             p = rEntry.mpVar.get();
292             p->ResetFlag(SbxFlagBits::ExtFound);
293             break;
294         }
295 
296         // Did we have an array/object with extended search?
297         if (bExtSearch && rEntry.mpVar->IsSet(SbxFlagBits::ExtSearch))
298         {
299             switch (rEntry.mpVar->GetClass())
300             {
301                 case SbxClassType::Object:
302                 {
303                     // Objects are not allowed to scan their parent.
304                     SbxFlagBits nOld = rEntry.mpVar->GetFlags();
305                     rEntry.mpVar->ResetFlag(SbxFlagBits::GlobalSearch);
306                     p = static_cast<SbxObject&>(*rEntry.mpVar).Find(rName, t);
307                     rEntry.mpVar->SetFlags(nOld);
308                 }
309                 break;
310                 case SbxClassType::Array:
311                     // Casting SbxVariable to SbxArray?  Really?
312                     p = reinterpret_cast<SbxArray&>(*rEntry.mpVar).Find(rName, t);
313                 break;
314                 default:
315                     ;
316             }
317 
318             if (p)
319             {
320                 p->SetFlag(SbxFlagBits::ExtFound);
321                 break;
322             }
323         }
324     }
325     return p;
326 }
327 
LoadData(SvStream & rStrm,sal_uInt16)328 bool SbxArray::LoadData( SvStream& rStrm, sal_uInt16 /*nVer*/ )
329 {
330     sal_uInt16 nElem;
331     Clear();
332     bool bRes = true;
333     SbxFlagBits f = nFlags;
334     nFlags |= SbxFlagBits::Write;
335     rStrm.ReadUInt16( nElem );
336     nElem &= 0x7FFF;
337     for( sal_uInt32 n = 0; n < nElem; n++ )
338     {
339         sal_uInt16 nIdx;
340         rStrm.ReadUInt16( nIdx );
341         SbxVariableRef pVar = static_cast<SbxVariable*>(Load( rStrm ).get());
342         if( pVar )
343         {
344             SbxVariableRef& rRef = GetRef( nIdx );
345             rRef = pVar;
346         }
347         else
348         {
349             bRes = false;
350             break;
351         }
352     }
353     nFlags = f;
354     return bRes;
355 }
356 
StoreData(SvStream & rStrm) const357 bool SbxArray::StoreData( SvStream& rStrm ) const
358 {
359     sal_uInt32 nElem = 0;
360     // Which elements are even defined?
361     for( auto& rEntry: mVarEntries )
362     {
363         if (rEntry.mpVar.is() && !(rEntry.mpVar->GetFlags() & SbxFlagBits::DontStore))
364             nElem++;
365     }
366     rStrm.WriteUInt16( nElem );
367     for( size_t n = 0; n < mVarEntries.size(); n++ )
368     {
369         const SbxVarEntry& rEntry = mVarEntries[n];
370         if (rEntry.mpVar.is() && !(rEntry.mpVar->GetFlags() & SbxFlagBits::DontStore))
371         {
372             rStrm.WriteUInt16( n );
373             if (!rEntry.mpVar->Store(rStrm))
374                 return false;
375         }
376     }
377     return true;
378 }
379 
380 // #100883 Method to set method directly to parameter array
PutDirect(SbxVariable * pVar,sal_uInt32 nIdx)381 void SbxArray::PutDirect( SbxVariable* pVar, sal_uInt32 nIdx )
382 {
383     SbxVariableRef& rRef = GetRef( nIdx );
384     rRef = pVar;
385 }
386 
387 
388 //  SbxArray
389 
SbxDimArray(SbxDataType t)390 SbxDimArray::SbxDimArray( SbxDataType t ) : SbxArray( t ), mbHasFixedSize( false )
391 {
392 }
393 
operator =(const SbxDimArray & rArray)394 SbxDimArray& SbxDimArray::operator=( const SbxDimArray& rArray )
395 {
396     if( &rArray != this )
397     {
398         SbxArray::operator=( static_cast<const SbxArray&>(rArray) );
399         m_vDimensions = rArray.m_vDimensions;
400         mbHasFixedSize = rArray.mbHasFixedSize;
401     }
402     return *this;
403 }
404 
~SbxDimArray()405 SbxDimArray::~SbxDimArray()
406 {
407 }
408 
Clear()409 void SbxDimArray::Clear()
410 {
411     m_vDimensions.clear();
412     SbxArray::Clear();
413 }
414 
415 // Add a dimension
416 
AddDimImpl(sal_Int32 lb,sal_Int32 ub,bool bAllowSize0)417 void SbxDimArray::AddDimImpl( sal_Int32 lb, sal_Int32 ub, bool bAllowSize0 )
418 {
419     ErrCode eRes = ERRCODE_NONE;
420     if( ub < lb && !bAllowSize0 )
421     {
422         eRes = ERRCODE_BASIC_OUT_OF_RANGE;
423         ub = lb;
424     }
425     SbxDim d;
426     d.nLbound = lb;
427     d.nUbound = ub;
428     d.nSize   = ub - lb + 1;
429     m_vDimensions.push_back(d);
430     if( eRes )
431         SetError( eRes );
432 }
433 
AddDim(sal_Int32 lb,sal_Int32 ub)434 void SbxDimArray::AddDim( sal_Int32 lb, sal_Int32 ub )
435 {
436     AddDimImpl( lb, ub, false );
437 }
438 
unoAddDim(sal_Int32 lb,sal_Int32 ub)439 void SbxDimArray::unoAddDim( sal_Int32 lb, sal_Int32 ub )
440 {
441     AddDimImpl( lb, ub, true );
442 }
443 
444 
445 // Readout dimension data
446 
GetDim(sal_Int32 n,sal_Int32 & rlb,sal_Int32 & rub) const447 bool SbxDimArray::GetDim( sal_Int32 n, sal_Int32& rlb, sal_Int32& rub ) const
448 {
449     if( n < 1 || n > static_cast<sal_Int32>(m_vDimensions.size()) )
450     {
451         SetError( ERRCODE_BASIC_OUT_OF_RANGE );
452         rub = rlb = 0;
453         return false;
454     }
455     SbxDim d = m_vDimensions[n - 1];
456     rub = d.nUbound;
457     rlb = d.nLbound;
458     return true;
459 }
460 
461 // Element-Ptr with the help of an index list
462 
Offset(const sal_Int32 * pIdx)463 sal_uInt32 SbxDimArray::Offset( const sal_Int32* pIdx )
464 {
465     sal_uInt32 nPos = 0;
466     for( const auto& rDimension : m_vDimensions )
467     {
468         sal_Int32 nIdx = *pIdx++;
469         if( nIdx < rDimension.nLbound || nIdx > rDimension.nUbound )
470         {
471             nPos = sal_uInt32(SBX_MAXINDEX32) + 1; break;
472         }
473         nPos = nPos * rDimension.nSize + nIdx - rDimension.nLbound;
474     }
475     if( m_vDimensions.empty() || nPos > SBX_MAXINDEX32 )
476     {
477         SetError( ERRCODE_BASIC_OUT_OF_RANGE );
478         nPos = 0;
479     }
480     return nPos;
481 }
482 
Get(const sal_Int32 * pIdx)483 SbxVariable* SbxDimArray::Get( const sal_Int32* pIdx )
484 {
485     return SbxArray::Get( Offset( pIdx ) );
486 }
487 
Put(SbxVariable * p,const sal_Int32 * pIdx)488 void SbxDimArray::Put( SbxVariable* p, const sal_Int32* pIdx  )
489 {
490     SbxArray::Put( p, Offset( pIdx ) );
491 }
492 
493 // Element-Number with the help of Parameter-Array
Offset(SbxArray * pPar)494 sal_uInt32 SbxDimArray::Offset( SbxArray* pPar )
495 {
496 #if HAVE_FEATURE_SCRIPTING
497     if (m_vDimensions.empty() || !pPar ||
498         ((m_vDimensions.size() != sal::static_int_cast<size_t>(pPar->Count() - 1))
499             && SbiRuntime::isVBAEnabled()))
500     {
501         SetError( ERRCODE_BASIC_OUT_OF_RANGE );
502         return 0;
503     }
504 #endif
505     sal_uInt32 nPos = 0;
506     sal_uInt32 nOff = 1;    // Non element 0!
507     for (auto const& vDimension : m_vDimensions)
508     {
509         sal_Int32 nIdx = pPar->Get( nOff++ )->GetLong();
510         if( nIdx < vDimension.nLbound || nIdx > vDimension.nUbound )
511         {
512             nPos = sal_uInt32(SBX_MAXINDEX32)+1;
513             break;
514         }
515         nPos = nPos * vDimension.nSize + nIdx - vDimension.nLbound;
516         if (IsError())
517             break;
518     }
519     if( nPos > o3tl::make_unsigned(SBX_MAXINDEX32) )
520     {
521         SetError( ERRCODE_BASIC_OUT_OF_RANGE );
522         nPos = 0;
523     }
524     return nPos;
525 }
526 
Get(SbxArray * pPar)527 SbxVariable* SbxDimArray::Get( SbxArray* pPar )
528 {
529     return SbxArray::Get( Offset( pPar ) );
530 }
531 
LoadData(SvStream & rStrm,sal_uInt16 nVer)532 bool SbxDimArray::LoadData( SvStream& rStrm, sal_uInt16 nVer )
533 {
534     short nTmp(0);
535     rStrm.ReadInt16(nTmp);
536 
537     if (nTmp > 0)
538     {
539         auto nDimension = o3tl::make_unsigned(nTmp);
540 
541         const size_t nMinRecordSize = 4;
542         const size_t nMaxPossibleRecords = rStrm.remainingSize() / nMinRecordSize;
543         if (nDimension > nMaxPossibleRecords)
544         {
545             SAL_WARN("basic", "SbxDimArray::LoadData more entries claimed than stream could contain");
546             return false;
547         }
548 
549         for (decltype(nDimension) i = 0; i < nDimension && rStrm.GetError() == ERRCODE_NONE; ++i)
550         {
551             sal_Int16 lb(0), ub(0);
552             rStrm.ReadInt16( lb ).ReadInt16( ub );
553             AddDim( lb, ub );
554         }
555     }
556     return SbxArray::LoadData( rStrm, nVer );
557 }
558 
StoreData(SvStream & rStrm) const559 bool SbxDimArray::StoreData( SvStream& rStrm ) const
560 {
561     assert(m_vDimensions.size() <= sal::static_int_cast<size_t>(std::numeric_limits<sal_Int16>::max()));
562     rStrm.WriteInt16( m_vDimensions.size() );
563     for( sal_Int32 i = 1; i <= static_cast<sal_Int32>(m_vDimensions.size()); i++ )
564     {
565         sal_Int32 lb32, ub32;
566         GetDim(i, lb32, ub32);
567         assert(lb32 >= -SBX_MAXINDEX && ub32 <= SBX_MAXINDEX);
568         rStrm.WriteInt16(lb32).WriteInt16(ub32);
569     }
570     return SbxArray::StoreData( rStrm );
571 }
572 
573 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
574