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 38 SbxArray::SbxArray( SbxDataType t ) 39 { 40 eType = t; 41 if( t != SbxVARIANT ) 42 SetFlag( SbxFlagBits::Fixed ); 43 } 44 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 71 SbxArray::~SbxArray() 72 { 73 } 74 75 SbxDataType SbxArray::GetType() const 76 { 77 return static_cast<SbxDataType>( eType | SbxARRAY ); 78 } 79 80 void SbxArray::Clear() 81 { 82 mVarEntries.clear(); 83 } 84 85 sal_uInt32 SbxArray::Count() const 86 { 87 return mVarEntries.size(); 88 } 89 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 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 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 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 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 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 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 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 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 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 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 357 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 381 void SbxArray::PutDirect( SbxVariable* pVar, sal_uInt32 nIdx ) 382 { 383 SbxVariableRef& rRef = GetRef( nIdx ); 384 rRef = pVar; 385 } 386 387 388 // SbxArray 389 390 SbxDimArray::SbxDimArray( SbxDataType t ) : SbxArray( t ), mbHasFixedSize( false ) 391 { 392 } 393 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 405 SbxDimArray::~SbxDimArray() 406 { 407 } 408 409 void SbxDimArray::Clear() 410 { 411 m_vDimensions.clear(); 412 SbxArray::Clear(); 413 } 414 415 // Add a dimension 416 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 434 void SbxDimArray::AddDim( sal_Int32 lb, sal_Int32 ub ) 435 { 436 AddDimImpl( lb, ub, false ); 437 } 438 439 void SbxDimArray::unoAddDim( sal_Int32 lb, sal_Int32 ub ) 440 { 441 AddDimImpl( lb, ub, true ); 442 } 443 444 445 // Readout dimension data 446 447 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 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 483 SbxVariable* SbxDimArray::Get( const sal_Int32* pIdx ) 484 { 485 return SbxArray::Get( Offset( pIdx ) ); 486 } 487 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 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 527 SbxVariable* SbxDimArray::Get( SbxArray* pPar ) 528 { 529 return SbxArray::Get( Offset( pPar ) ); 530 } 531 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 559 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
