Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/basic/source/sbx/sbxvar.cxx
Line
Count
Source
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
22
#include <tools/stream.hxx>
23
#include <svl/SfxBroadcaster.hxx>
24
25
#include <basic/sbx.hxx>
26
#include <runtime.hxx>
27
#include "sbxres.hxx"
28
#include "sbxconv.hxx"
29
#include <sbunoobj.hxx>
30
#include <rtl/ustrbuf.hxx>
31
#include <sal/log.hxx>
32
#include <global.hxx>
33
#include <unotools/transliterationwrapper.hxx>
34
35
#include <com/sun/star/uno/XInterface.hpp>
36
#include <utility>
37
#include <filefmt.hxx>
38
using namespace com::sun::star::uno;
39
40
// SbxVariable
41
42
SbxVariable::SbxVariable()
43
0
{
44
0
}
Unexecuted instantiation: SbxVariable::SbxVariable()
Unexecuted instantiation: SbxVariable::SbxVariable()
45
46
SbxVariable::SbxVariable( const SbxVariable& r )
47
0
    : SvRefBase( r ),
48
0
      SbxValue( r ),
49
0
      m_aDeclareClassName( r.m_aDeclareClassName ),
50
0
      m_xComListener( r.m_xComListener),
51
0
      mpPar( r.mpPar ),
52
0
      pInfo( r.pInfo )
53
0
{
54
#if HAVE_FEATURE_SCRIPTING
55
    if( r.m_xComListener.is() )
56
    {
57
        registerComListenerVariableForBasic( this, r.m_pComListenerParentBasic );
58
    }
59
#endif
60
0
    if( r.CanRead() )
61
0
    {
62
0
        pParent = r.pParent;
63
0
        nUserData = r.nUserData;
64
0
        maName = r.maName;
65
0
        nHash = r.nHash;
66
0
    }
67
0
}
Unexecuted instantiation: SbxVariable::SbxVariable(SbxVariable const&)
Unexecuted instantiation: SbxVariable::SbxVariable(SbxVariable const&)
68
69
SbxEnsureParentVariable::SbxEnsureParentVariable(const SbxVariable& r)
70
0
    : SbxVariable(r)
71
0
    , xParent(const_cast<SbxVariable&>(r).GetParent())
72
0
{
73
0
    assert(GetParent() == xParent.get());
74
0
}
Unexecuted instantiation: SbxEnsureParentVariable::SbxEnsureParentVariable(SbxVariable const&)
Unexecuted instantiation: SbxEnsureParentVariable::SbxEnsureParentVariable(SbxVariable const&)
75
76
void SbxEnsureParentVariable::SetParent(SbxObject* p)
77
0
{
78
0
    assert(GetParent() == xParent.get());
79
0
    SbxVariable::SetParent(p);
80
0
    xParent = SbxObjectRef(p);
81
0
    assert(GetParent() == xParent.get());
82
0
}
83
84
0
SbxVariable::SbxVariable( SbxDataType t ) : SbxValue( t )
85
0
{
86
0
}
Unexecuted instantiation: SbxVariable::SbxVariable(SbxDataType)
Unexecuted instantiation: SbxVariable::SbxVariable(SbxDataType)
87
88
SbxVariable::~SbxVariable()
89
0
{
90
#if HAVE_FEATURE_SCRIPTING
91
    if( IsSet( SbxFlagBits::DimAsNew ))
92
    {
93
        removeDimAsNewRecoverItem( this );
94
    }
95
#endif
96
0
    mpBroadcaster.reset();
97
0
}
98
99
// Broadcasting
100
101
SfxBroadcaster& SbxVariable::GetBroadcaster()
102
0
{
103
0
    if( !mpBroadcaster )
104
0
    {
105
0
        mpBroadcaster.reset( new SfxBroadcaster );
106
0
    }
107
0
    return *mpBroadcaster;
108
0
}
109
110
SbxArray* SbxVariable::GetParameters() const
111
0
{
112
0
    return mpPar.get();
113
0
}
114
115
116
// Perhaps some day one could cut the parameter 0.
117
// Then the copying will be dropped...
118
119
void SbxVariable::Broadcast( SfxHintId nHintId )
120
0
{
121
0
    if( !mpBroadcaster || IsSet( SbxFlagBits::NoBroadcast ) )
122
0
        return;
123
124
    // Because the method could be called from outside, check the
125
    // rights here again
126
0
    if( nHintId == SfxHintId::BasicDataWanted )
127
0
    {
128
0
        if( !CanRead() )
129
0
        {
130
0
            return;
131
0
        }
132
0
    }
133
0
    if( nHintId == SfxHintId::BasicDataChanged )
134
0
    {
135
0
        if( !CanWrite() )
136
0
        {
137
0
            return;
138
0
        }
139
0
    }
140
141
    //fdo#86843 Add a ref during the following block to guard against
142
    //getting deleted before completing this method
143
0
    SbxVariableRef aBroadcastGuard(this);
144
145
    // Avoid further broadcasting
146
0
    std::unique_ptr<SfxBroadcaster> pSave = std::move(mpBroadcaster);
147
0
    SbxFlagBits nSaveFlags = GetFlags();
148
0
    SetFlag( SbxFlagBits::ReadWrite );
149
0
    if( mpPar.is() )
150
0
    {
151
        // Register this as element 0, but don't change over the parent!
152
0
        mpPar->GetRef(0) = this;
153
0
    }
154
0
    pSave->Broadcast( SbxHint( nHintId, this ) );
155
0
    mpBroadcaster = std::move(pSave);
156
0
    SetFlags( nSaveFlags );
157
0
}
158
159
SbxInfo* SbxVariable::GetInfo()
160
0
{
161
0
    if( !pInfo.is() )
162
0
    {
163
0
        Broadcast( SfxHintId::BasicInfoWanted );
164
0
        if( pInfo.is() )
165
0
        {
166
0
            SetModified( true );
167
0
        }
168
0
    }
169
0
    return pInfo.get();
170
0
}
171
172
void SbxVariable::SetInfo( SbxInfo* p )
173
0
{
174
0
    pInfo = p;
175
0
}
176
177
void SbxVariable::SetParameters( SbxArray* p )
178
0
{
179
0
    mpPar = p;
180
0
}
181
182
183
// Name of the variables
184
185
// static
186
OUString SbxVariable::NameToCaseInsensitiveName(const OUString& rName)
187
0
{
188
0
    return SbGlobal::GetTransliteration().transliterate(rName, 0, rName.getLength());
189
0
}
190
191
void SbxVariable::SetName( const OUString& rName )
192
0
{
193
0
    maName = rName;
194
0
    nHash = MakeHashCode( rName );
195
0
    maNameCI.clear();
196
0
}
197
198
const OUString& SbxVariable::GetName( SbxNameType t ) const
199
0
{
200
0
    static const char cSuffixes[] = "  %&!#@ $";
201
0
    if( t == SbxNameType::NONE )
202
0
    {
203
0
        return maName;
204
0
    }
205
0
    if (t == SbxNameType::CaseInsensitive)
206
0
    {
207
0
        if (maNameCI.isEmpty() && !maName.isEmpty())
208
0
            maNameCI = NameToCaseInsensitiveName(maName);
209
0
        return maNameCI;
210
0
    }
211
    // Request parameter-information (not for objects)
212
0
    const_cast<SbxVariable*>(this)->GetInfo();
213
    // Append nothing, if it is a simple property (no empty brackets)
214
0
    if (!pInfo.is() || (pInfo->m_Params.empty() && GetClass() == SbxClassType::Property))
215
0
    {
216
0
        return maName;
217
0
    }
218
0
    OUStringBuffer aTmp( maName );
219
0
    if( t == SbxNameType::ShortTypes )
220
0
    {
221
0
        sal_Unicode cType = ' ';
222
        // short type? Then fetch it, possible this is 0.
223
0
        SbxDataType et = GetType();
224
0
        if( et <= SbxSTRING )
225
0
        {
226
0
            assert(et >= 0 && size_t(et) < std::size(cSuffixes) - 1);
227
0
            cType = cSuffixes[ et ];
228
0
        }
229
0
        if( cType != ' ' )
230
0
        {
231
0
            aTmp.append(cType);
232
0
        }
233
0
    }
234
0
    aTmp.append("(");
235
236
0
    bool first = true;
237
0
    for (auto const& i : pInfo->m_Params)
238
0
    {
239
0
        int nt = i->eType & 0x0FFF;
240
0
        if (!first)
241
0
        {
242
0
            aTmp.append(",");
243
0
        }
244
0
        first = false;
245
0
        if( i->nFlags & SbxFlagBits::Optional )
246
0
        {
247
0
            aTmp.append( GetSbxRes( StringId::Optional ) );
248
0
        }
249
0
        if( i->eType & SbxBYREF )
250
0
        {
251
0
            aTmp.append( GetSbxRes( StringId::ByRef ) );
252
0
        }
253
0
        aTmp.append( i->aName );
254
0
        sal_Unicode cType = ' ';
255
        // short type? Then fetch it, possible this is 0.
256
0
        if( t == SbxNameType::ShortTypes )
257
0
        {
258
0
            if( nt <= SbxSTRING )
259
0
            {
260
0
                cType = cSuffixes[ nt ];
261
0
            }
262
0
        }
263
0
        if( cType != ' ' )
264
0
        {
265
0
            aTmp.append(cType);
266
0
            if( i->eType & SbxARRAY )
267
0
            {
268
0
                aTmp.append("()");
269
0
            }
270
0
        }
271
0
        else
272
0
        {
273
0
            if( i->eType & SbxARRAY )
274
0
            {
275
0
                aTmp.append("()");
276
0
            }
277
            // long type?
278
0
            aTmp.append(GetSbxRes( StringId::As ));
279
0
            if( nt < 32 )
280
0
            {
281
0
                aTmp.append(GetSbxRes( static_cast<StringId>( static_cast<int>( StringId::Types ) + nt ) ));
282
0
            }
283
0
            else
284
0
            {
285
0
                aTmp.append(GetSbxRes( StringId::Any ));
286
0
            }
287
0
        }
288
0
    }
289
0
    aTmp.append(")");
290
0
    const_cast<SbxVariable*>(this)->aToolString = aTmp.makeStringAndClear();
291
0
    return aToolString;
292
0
}
293
294
// Operators
295
296
SbxVariable& SbxVariable::operator=( const SbxVariable& r )
297
0
{
298
0
    if (this != &r)
299
0
    {
300
0
        SbxValue::operator=( r );
301
        // tdf#144353 - copy information about a missing parameter. See SbiRuntime::SetIsMissing.
302
        // We cannot unconditionally assign the data about a variable because we would overwrite
303
        // the information about parameters (name, type, flags, and ids). For instance, in the case
304
        // where a method will be initialized with a literal.
305
0
        if (!pInfo)
306
0
            pInfo = r.pInfo;
307
0
        m_aDeclareClassName = r.m_aDeclareClassName;
308
0
        m_xComListener = r.m_xComListener;
309
0
        m_pComListenerParentBasic = r.m_pComListenerParentBasic;
310
#if HAVE_FEATURE_SCRIPTING
311
        if( m_xComListener.is() )
312
        {
313
            registerComListenerVariableForBasic( this, m_pComListenerParentBasic );
314
        }
315
#endif
316
0
    }
317
0
    return *this;
318
0
}
319
320
// Conversion
321
322
SbxDataType SbxVariable::GetType() const
323
0
{
324
0
    if( aData.eType == SbxOBJECT )
325
0
    {
326
0
        return aData.pObj ? aData.pObj->GetType() : SbxOBJECT;
327
0
    }
328
0
    else if( aData.eType == SbxVARIANT )
329
0
    {
330
0
        return aData.pObj ? aData.pObj->GetType() : SbxVARIANT;
331
0
    }
332
0
    else
333
0
    {
334
0
        return aData.eType;
335
0
    }
336
0
}
337
338
SbxClassType SbxVariable::GetClass() const
339
0
{
340
0
    return SbxClassType::Variable;
341
0
}
342
343
void SbxVariable::SetModified( bool b )
344
0
{
345
0
    if( IsSet( SbxFlagBits::NoModify ) )
346
0
    {
347
0
        return;
348
0
    }
349
0
    SbxBase::SetModified( b );
350
0
    if( pParent && pParent != this ) //??? HotFix: Recursion out here MM
351
0
    {
352
0
        pParent->SetModified( b );
353
0
    }
354
0
}
355
356
void SbxVariable::SetParent( SbxObject* p )
357
0
{
358
#ifdef DBG_UTIL
359
    // Will the parent of a SbxObject be set?
360
    if (p && dynamic_cast<SbxObject*>(this))
361
    {
362
        // then this had to be a child of the new parent
363
        bool bFound = false;
364
        SbxArray *pChildren = p->GetObjects();
365
        if ( pChildren )
366
        {
367
            for (sal_uInt32 nIdx = 0; !bFound && nIdx < pChildren->Count(); ++nIdx)
368
            {
369
                bFound = (this == pChildren->Get(nIdx));
370
            }
371
        }
372
        SAL_INFO_IF(
373
            !bFound, "basic.sbx",
374
            "dangling: [" << GetName() << "].SetParent([" << p->GetName()
375
                << "])");
376
    }
377
#endif
378
379
0
    pParent = p;
380
0
}
381
382
const OUString& SbxVariable::GetDeclareClassName() const
383
0
{
384
0
    return m_aDeclareClassName;
385
0
}
386
387
void SbxVariable::SetDeclareClassName( const OUString& rDeclareClassName )
388
0
{
389
0
    m_aDeclareClassName = rDeclareClassName;
390
0
}
391
392
void SbxVariable::SetComListener( const css::uno::Reference< css::uno::XInterface >& xComListener,
393
                                  StarBASIC* pParentBasic )
394
0
{
395
0
    m_xComListener = xComListener;
396
0
    m_pComListenerParentBasic = pParentBasic;
397
#if HAVE_FEATURE_SCRIPTING
398
    registerComListenerVariableForBasic( this, pParentBasic );
399
#endif
400
0
}
401
402
void SbxVariable::ClearComListener()
403
0
{
404
0
    m_xComListener.clear();
405
0
}
406
407
408
// Loading/Saving
409
410
bool SbxVariable::LoadData( SvStream& rStrm, sal_uInt16 nVer )
411
0
{
412
0
    sal_uInt8 cMark;
413
0
    rStrm.ReadUChar( cMark );
414
0
    if( cMark == 0xFF )
415
0
    {
416
0
        if( !SbxValue::LoadData( rStrm, nVer ) )
417
0
        {
418
0
            return false;
419
0
        }
420
0
        maName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
421
0
                                                                RTL_TEXTENCODING_ASCII_US);
422
0
        sal_uInt32 nTemp;
423
0
        rStrm.ReadUInt32( nTemp );
424
0
        nUserData = nTemp;
425
0
    }
426
0
    else
427
0
    {
428
0
        sal_uInt16 nType;
429
0
        rStrm.SeekRel( -1 );
430
0
        rStrm.ReadUInt16( nType );
431
0
        maName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
432
0
                                                                RTL_TEXTENCODING_ASCII_US);
433
0
        sal_uInt32 nTemp;
434
0
        rStrm.ReadUInt32( nTemp );
435
0
        nUserData = nTemp;
436
        // correction: old methods have instead of SbxNULL now SbxEMPTY
437
0
        if( nType == SbxNULL && GetClass() == SbxClassType::Method )
438
0
        {
439
0
            nType = SbxEMPTY;
440
0
        }
441
0
        SbxValues aTmp;
442
0
        OUString aTmpString;
443
0
        OUString aVal;
444
0
        aTmp.eType = aData.eType = static_cast<SbxDataType>(nType);
445
0
        aTmp.pOUString = &aVal;
446
0
        switch( nType )
447
0
        {
448
0
        case SbxBOOL:
449
0
        case SbxERROR:
450
0
        case SbxINTEGER:
451
0
            rStrm.ReadInt16( aTmp.nInteger ); break;
452
0
        case SbxLONG:
453
0
            rStrm.ReadInt32( aTmp.nLong ); break;
454
0
        case SbxSINGLE:
455
0
        {
456
            // Floats as ASCII
457
0
            aTmpString = read_uInt16_lenPrefixed_uInt8s_ToOUString(
458
0
                    rStrm, RTL_TEXTENCODING_ASCII_US);
459
0
            double d;
460
0
            SbxDataType t;
461
0
            if( ImpScan( aTmpString, d, t, nullptr ) != ERRCODE_NONE || t == SbxDOUBLE )
462
0
            {
463
0
                aTmp.nSingle = 0;
464
0
                return false;
465
0
            }
466
0
            aTmp.nSingle = static_cast<float>(d);
467
0
            break;
468
0
        }
469
0
        case SbxDATE:
470
0
        case SbxDOUBLE:
471
0
        {
472
            // Floats as ASCII
473
0
            aTmpString = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
474
0
                                                                        RTL_TEXTENCODING_ASCII_US);
475
0
            SbxDataType t;
476
0
            if( ImpScan( aTmpString, aTmp.nDouble, t, nullptr ) != ERRCODE_NONE )
477
0
            {
478
0
                aTmp.nDouble = 0;
479
0
                return false;
480
0
            }
481
0
            break;
482
0
        }
483
0
        case SbxSTRING:
484
0
            aVal = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
485
0
                                                                  RTL_TEXTENCODING_ASCII_US);
486
0
            break;
487
0
        case SbxEMPTY:
488
0
        case SbxNULL:
489
0
            break;
490
0
        default:
491
0
            aData.eType = SbxNULL;
492
0
            SAL_WARN( "basic.sbx", "Loaded a non-supported data type" );
493
0
            return false;
494
0
        }
495
        // putt value
496
0
        if( nType != SbxNULL && nType != SbxEMPTY && !Put( aTmp ) )
497
0
        {
498
0
            return false;
499
0
        }
500
0
    }
501
0
    rStrm.ReadUChar( cMark );
502
    // cMark is also a version number!
503
    // 1: initial version
504
    // 2: with nUserData
505
0
    if( cMark )
506
0
    {
507
0
        if( cMark > 2 )
508
0
        {
509
0
            return false;
510
0
        }
511
0
        pInfo = new SbxInfo;
512
0
        pInfo->LoadData( rStrm, static_cast<sal_uInt16>(cMark) );
513
0
    }
514
0
    Broadcast( SfxHintId::BasicDataChanged );
515
0
    nHash =  MakeHashCode( maName );
516
0
    SetModified( true );
517
0
    return true;
518
0
}
519
520
std::pair<bool, sal_uInt32> SbxVariable::StoreData( SvStream& rStrm ) const
521
0
{
522
0
    rStrm.WriteUChar( 0xFF );      // Marker
523
0
    bool bValStore;
524
0
    if( dynamic_cast<const SbxMethod *>(this) != nullptr )
525
0
    {
526
        // #50200 Avoid that objects , which during the runtime
527
        // as return-value are saved in the method as a value were saved
528
0
        SbxVariable* pThis = const_cast<SbxVariable*>(this);
529
0
        SbxFlagBits nSaveFlags = GetFlags();
530
0
        pThis->SetFlag( SbxFlagBits::Write );
531
0
        pThis->SbxValue::Clear();
532
0
        pThis->SetFlags( nSaveFlags );
533
534
        // So that the method will not be executed in any case!
535
        // CAST, to avoid const!
536
0
        pThis->SetFlag( SbxFlagBits::NoBroadcast );
537
0
        bValStore = SbxValue::StoreData( rStrm ).first;
538
0
        pThis->ResetFlag( SbxFlagBits::NoBroadcast );
539
0
    }
540
0
    else
541
0
    {
542
0
        bValStore = SbxValue::StoreData( rStrm ).first;
543
0
    }
544
0
    if( !bValStore )
545
0
    {
546
0
        return { false, 0 };
547
0
    }
548
0
    write_uInt16_lenPrefixed_uInt8s_FromOUString(rStrm, maName,
549
0
                                                      RTL_TEXTENCODING_ASCII_US);
550
0
    rStrm.WriteUInt32( nUserData );
551
0
    if( pInfo.is() )
552
0
    {
553
0
        rStrm.WriteUChar( 2 );     // Version 2: with UserData!
554
0
        pInfo->StoreData( rStrm );
555
0
    }
556
0
    else
557
0
    {
558
0
        rStrm.WriteUChar( 0 );
559
0
    }
560
0
    return { true, B_IMG_VERSION_12 };
561
0
}
562
563
// SbxInfo
564
565
SbxInfo::SbxInfo()
566
0
        :  nHelpId(0)
567
0
{}
568
569
SbxInfo::SbxInfo( OUString a, sal_uInt32 n )
570
0
       : aHelpFile(std::move( a )), nHelpId( n )
571
0
{}
572
573
0
SbxHint::~SbxHint() = default;
574
575
void SbxVariable::Dump( SvStream& rStrm, bool bFill )
576
0
{
577
0
    OString aBNameStr(OUStringToOString(GetName( SbxNameType::ShortTypes ), RTL_TEXTENCODING_ASCII_US));
578
0
    rStrm.WriteOString( "Variable( " )
579
0
         .WriteOString( OString::number(reinterpret_cast<sal_IntPtr>(this)) ).WriteOString( "==" )
580
0
         .WriteOString( aBNameStr );
581
0
    OString aBParentNameStr(OUStringToOString(GetParent()->GetName(), RTL_TEXTENCODING_ASCII_US));
582
0
    if ( GetParent() )
583
0
    {
584
0
        rStrm.WriteOString( " in parent '" ).WriteOString( aBParentNameStr ).WriteOString( "'" );
585
0
    }
586
0
    else
587
0
    {
588
0
        rStrm.WriteOString( " no parent" );
589
0
    }
590
0
    rStrm.WriteOString( " ) " );
591
592
    // output also the object at object-vars
593
0
    if ( GetValues_Impl().eType == SbxOBJECT &&
594
0
            GetValues_Impl().pObj &&
595
0
            GetValues_Impl().pObj != this &&
596
0
            GetValues_Impl().pObj != GetParent() )
597
0
    {
598
0
        rStrm.WriteOString( " contains " );
599
0
        static_cast<SbxObject*>(GetValues_Impl().pObj)->Dump( rStrm, bFill );
600
0
    }
601
0
    else
602
0
    {
603
0
        rStrm << endl;
604
0
    }
605
0
}
606
607
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */