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