Coverage Report

Created: 2026-09-28 10:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/basic/source/runtime/runtime.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 <stdlib.h>
21
22
#include <algorithm>
23
#include <string_view>
24
#include <unordered_map>
25
26
#include <com/sun/star/container/XEnumerationAccess.hpp>
27
#include <com/sun/star/container/XIndexAccess.hpp>
28
#include <com/sun/star/script/XDefaultMethod.hpp>
29
#include <com/sun/star/uno/Any.hxx>
30
31
#include <comphelper/processfactory.hxx>
32
#include <comphelper/string.hxx>
33
#include <o3tl/safeint.hxx>
34
#include <sal/log.hxx>
35
36
#include <tools/wldcrd.hxx>
37
#include <comphelper/diagnose_ex.hxx>
38
39
#include <utility>
40
#include <vcl/svapp.hxx>
41
#include <vcl/settings.hxx>
42
43
#include <rtl/math.hxx>
44
#include <rtl/ustrbuf.hxx>
45
46
#include <svl/numformat.hxx>
47
#include <svl/zforlist.hxx>
48
49
#include <unicode/regex.h>
50
51
#include <basic/sbuno.hxx>
52
53
#include <codegen.hxx>
54
#include "comenumwrapper.hxx"
55
#include "ddectrl.hxx"
56
#include "dllmgr.hxx"
57
#include <errobject.hxx>
58
#include <image.hxx>
59
#include <iosys.hxx>
60
#include <opcodes.hxx>
61
#include <runtime.hxx>
62
#include <sb.hxx>
63
#include <sbintern.hxx>
64
#include <sbprop.hxx>
65
#include <sbunoobj.hxx>
66
#include <basic/codecompletecache.hxx>
67
#include <memory>
68
69
using com::sun::star::uno::Reference;
70
71
using namespace com::sun::star::uno;
72
using namespace com::sun::star::container;
73
using namespace com::sun::star::lang;
74
using namespace com::sun::star::script;
75
76
using namespace ::com::sun::star;
77
78
#if HAVE_FEATURE_SCRIPTING
79
80
static void lcl_clearImpl( SbxVariableRef const & refVar, SbxDataType const & eType );
81
static void lcl_eraseImpl( SbxVariableRef const & refVar, bool bVBAEnabled );
82
83
namespace
84
{
85
class ScopedWritableGuard
86
{
87
public:
88
    ScopedWritableGuard(const SbxVariableRef& rVar, bool bMakeWritable)
89
        : m_rVar(bMakeWritable && !rVar->CanWrite() ? rVar : SbxVariableRef())
90
    {
91
        if (m_rVar)
92
        {
93
            m_rVar->SetFlag(SbxFlagBits::Write);
94
        }
95
    }
96
    ~ScopedWritableGuard()
97
    {
98
        if (m_rVar)
99
        {
100
            m_rVar->ResetFlag(SbxFlagBits::Write);
101
        }
102
    }
103
104
private:
105
    SbxVariableRef m_rVar;
106
};
107
}
108
109
void StarBASIC::SetVBAEnabled( bool bEnabled )
110
{
111
    if ( bDocBasic )
112
    {
113
        bVBAEnabled = bEnabled;
114
    }
115
}
116
117
bool StarBASIC::isVBAEnabled() const
118
{
119
    return bDocBasic && (bVBAEnabled || SbiRuntime::isVBAEnabled());
120
}
121
122
struct SbiArgv {                   // Argv stack:
123
    SbxArrayRef    refArgv;             // Argv
124
    short nArgc;                        // Argc
125
126
    SbiArgv(SbxArrayRef refArgv_, short nArgc_) :
127
        refArgv(std::move(refArgv_)),
128
        nArgc(nArgc_) {}
129
};
130
131
struct SbiGosub {              // GOSUB-Stack:
132
    const sal_uInt8* pCode;         // Return-Pointer
133
    sal_uInt16 nStartForLvl;        // #118235: For Level in moment of gosub
134
135
    SbiGosub(const sal_uInt8* pCode_, sal_uInt16 nStartForLvl_) :
136
        pCode(pCode_),
137
        nStartForLvl(nStartForLvl_) {}
138
};
139
140
const SbiRuntime::pStep0 SbiRuntime::aStep0[] = { // all opcodes without operands
141
    &SbiRuntime::StepNOP,
142
    &SbiRuntime::StepEXP,
143
    &SbiRuntime::StepMUL,
144
    &SbiRuntime::StepDIV,
145
    &SbiRuntime::StepMOD,
146
    &SbiRuntime::StepPLUS,
147
    &SbiRuntime::StepMINUS,
148
    &SbiRuntime::StepNEG,
149
    &SbiRuntime::StepEQ,
150
    &SbiRuntime::StepNE,
151
    &SbiRuntime::StepLT,
152
    &SbiRuntime::StepGT,
153
    &SbiRuntime::StepLE,
154
    &SbiRuntime::StepGE,
155
    &SbiRuntime::StepIDIV,
156
    &SbiRuntime::StepAND,
157
    &SbiRuntime::StepOR,
158
    &SbiRuntime::StepXOR,
159
    &SbiRuntime::StepEQV,
160
    &SbiRuntime::StepIMP,
161
    &SbiRuntime::StepNOT,
162
    &SbiRuntime::StepCAT,
163
164
    &SbiRuntime::StepLIKE,
165
    &SbiRuntime::StepIS,
166
    // load/save
167
    &SbiRuntime::StepARGC,      // establish new Argv
168
    &SbiRuntime::StepARGV,      // TOS ==> current Argv
169
    &SbiRuntime::StepINPUT,     // Input ==> TOS
170
    &SbiRuntime::StepLINPUT,        // Line Input ==> TOS
171
    &SbiRuntime::StepGET,        // touch TOS
172
    &SbiRuntime::StepSET,        // save object TOS ==> TOS-1
173
    &SbiRuntime::StepPUT,       // TOS ==> TOS-1
174
    &SbiRuntime::StepPUTC,      // TOS ==> TOS-1, then ReadOnly
175
    &SbiRuntime::StepDIM,       // DIM
176
    &SbiRuntime::StepREDIM,         // REDIM
177
    &SbiRuntime::StepREDIMP,        // REDIM PRESERVE
178
    &SbiRuntime::StepERASE,         // delete TOS
179
    // branch
180
    &SbiRuntime::StepSTOP,          // program end
181
    &SbiRuntime::StepINITFOR,   // initialize FOR-Variable
182
    &SbiRuntime::StepNEXT,      // increment FOR-Variable
183
    &SbiRuntime::StepCASE,      // beginning CASE
184
    &SbiRuntime::StepENDCASE,   // end CASE
185
    &SbiRuntime::StepSTDERROR,      // standard error handling
186
    &SbiRuntime::StepNOERROR,   // no error handling
187
    &SbiRuntime::StepLEAVE,     // leave UP
188
    // E/A
189
    &SbiRuntime::StepCHANNEL,   // TOS = channel number
190
    &SbiRuntime::StepPRINT,     // print TOS
191
    &SbiRuntime::StepPRINTF,        // print TOS in field
192
    &SbiRuntime::StepWRITE,     // write TOS
193
    &SbiRuntime::StepRENAME,        // Rename Tos+1 to Tos
194
    &SbiRuntime::StepPROMPT,        // define Input Prompt from TOS
195
    &SbiRuntime::StepRESTART,   // Set restart point
196
    &SbiRuntime::StepCHANNEL0,  // set E/A-channel 0
197
    &SbiRuntime::StepEMPTY,     // empty expression on stack
198
    &SbiRuntime::StepERROR,     // TOS = error code
199
    &SbiRuntime::StepLSET,      // save object TOS ==> TOS-1
200
    &SbiRuntime::StepRSET,      // save object TOS ==> TOS-1
201
    &SbiRuntime::StepREDIMP_ERASE,// Copy array object for REDIMP
202
    &SbiRuntime::StepINITFOREACH,// Init for each loop
203
    &SbiRuntime::StepVBASET,// vba-like set statement
204
    &SbiRuntime::StepERASE_CLEAR,// vba-like set statement
205
    &SbiRuntime::StepARRAYACCESS,// access TOS as array
206
    &SbiRuntime::StepBYVAL,     // access TOS as array
207
};
208
209
const SbiRuntime::pStep1 SbiRuntime::aStep1[] = { // all opcodes with one operand
210
    &SbiRuntime::StepLOADNC,        // loading a numeric constant (+ID)
211
    &SbiRuntime::StepLOADSC,        // loading a string constant (+ID)
212
    &SbiRuntime::StepLOADI,     // Immediate Load (+value)
213
    &SbiRuntime::StepARGN,      // save a named Args in Argv (+StringID)
214
    &SbiRuntime::StepPAD,       // bring string to a definite length (+length)
215
    // branches
216
    &SbiRuntime::StepJUMP,      // jump (+Target)
217
    &SbiRuntime::StepJUMPT,     // evaluate TOS, conditional jump (+Target)
218
    &SbiRuntime::StepJUMPF,     // evaluate TOS, conditional jump (+Target)
219
    &SbiRuntime::StepONJUMP,        // evaluate TOS, jump into JUMP-table (+MaxVal)
220
    &SbiRuntime::StepGOSUB,     // UP-call (+Target)
221
    &SbiRuntime::StepRETURN,        // UP-return (+0 or Target)
222
    &SbiRuntime::StepTESTFOR,   // check FOR-variable, increment (+Endlabel)
223
    &SbiRuntime::StepCASETO,        // Tos+1 <= Case <= Tos), 2xremove (+Target)
224
    &SbiRuntime::StepERRHDL,        // error handler (+Offset)
225
    &SbiRuntime::StepRESUME,        // resume after errors (+0 or 1 or Label)
226
    // E/A
227
    &SbiRuntime::StepCLOSE,     // (+channel/0)
228
    &SbiRuntime::StepPRCHAR,        // (+char)
229
    // management
230
    &SbiRuntime::StepSETCLASS,  // check set + class names (+StringId)
231
    &SbiRuntime::StepTESTCLASS, // Check TOS class (+StringId)
232
    &SbiRuntime::StepLIB,       // lib for declare-call (+StringId)
233
    &SbiRuntime::StepBASED,     // TOS is incremented by BASE, BASE is pushed before
234
    &SbiRuntime::StepARGTYP,        // convert last parameter in Argv (+Type)
235
    &SbiRuntime::StepVBASETCLASS,// vba-like set statement
236
};
237
238
const SbiRuntime::pStep2 SbiRuntime::aStep2[] = {// all opcodes with two operands
239
    &SbiRuntime::StepRTL,       // load from RTL (+StringID+Typ)
240
    &SbiRuntime::StepFIND,      // load (+StringID+Typ)
241
    &SbiRuntime::StepELEM,          // load element (+StringID+Typ)
242
    &SbiRuntime::StepPARAM,     // Parameter (+Offset+Typ)
243
    // branches
244
    &SbiRuntime::StepCALL,      // Declare-Call (+StringID+Typ)
245
    &SbiRuntime::StepCALLC,     // CDecl-Declare-Call (+StringID+Typ)
246
    &SbiRuntime::StepCASEIS,        // Case-Test (+Test-Opcode+False-Target)
247
    // management
248
    &SbiRuntime::StepSTMNT,         // beginning of a statement (+Line+Col)
249
    // E/A
250
    &SbiRuntime::StepOPEN,          // (+StreamMode+Flags)
251
    // Objects
252
    &SbiRuntime::StepLOCAL,     // define local variable (+StringId+Typ)
253
    &SbiRuntime::StepPUBLIC,        // module global variable (+StringID+Typ)
254
    &SbiRuntime::StepGLOBAL,        // define global variable (+StringID+Typ)
255
    &SbiRuntime::StepCREATE,        // create object (+StringId+StringId)
256
    &SbiRuntime::StepSTATIC,     // static variable (+StringId+StringId)
257
    &SbiRuntime::StepTCREATE,    // user-defined objects (+StringId+StringId)
258
    &SbiRuntime::StepDCREATE,    // create object-array (+StringID+StringID)
259
    &SbiRuntime::StepGLOBAL_P,   // define global variable which is not overwritten
260
                                 // by the Basic on a restart (+StringID+Typ)
261
    &SbiRuntime::StepFIND_G,        // finds global variable with special treatment because of _GLOBAL_P
262
    &SbiRuntime::StepDCREATE_REDIMP, // redimension object array (+StringID+StringID)
263
    &SbiRuntime::StepFIND_CM,    // Search inside a class module (CM) to enable global search in time
264
    &SbiRuntime::StepPUBLIC_P,    // Search inside a class module (CM) to enable global search in time
265
    &SbiRuntime::StepFIND_STATIC,    // Search inside a class module (CM) to enable global search in time
266
};
267
268
269
//                              SbiRTLData
270
271
SbiRTLData::SbiRTLData()
272
    : nDirFlags(SbAttributes::NORMAL)
273
    , nCurDirPos(0)
274
{
275
}
276
277
SbiRTLData::~SbiRTLData()
278
{
279
}
280
281
//                              SbiInstance
282
283
// 16.10.96: #31460 new concept for StepInto/Over/Out
284
// The decision whether StepPoint shall be called is done with the help of
285
// the CallLevel. It's stopped when the current CallLevel is <= nBreakCallLvl.
286
// The current CallLevel can never be smaller than 1, as it's also incremented
287
// during the call of a method (also main). Therefore a BreakCallLvl from 0
288
// means that the program isn't stopped at all.
289
// (also have a look at: step2.cxx, SbiRuntime::StepSTMNT() )
290
291
292
void SbiInstance::CalcBreakCallLevel( BasicDebugFlags nFlags )
293
{
294
295
    nFlags &= ~BasicDebugFlags::Break;
296
297
    sal_uInt16 nRet;
298
    if (nFlags  == BasicDebugFlags::StepInto) {
299
        nRet = nCallLvl + 1;    // CallLevel+1 is also stopped
300
    } else if (nFlags == (BasicDebugFlags::StepOver | BasicDebugFlags::StepInto)) {
301
        nRet = nCallLvl;        // current CallLevel is stopped
302
    } else if (nFlags == BasicDebugFlags::StepOut) {
303
        nRet = nCallLvl - 1;    // smaller CallLevel is stopped
304
    } else {
305
        // Basic-IDE returns 0 instead of BasicDebugFlags::Continue, so also default=continue
306
        nRet = 0;               // CallLevel is always > 0 -> no StepPoint
307
    }
308
    nBreakCallLvl = nRet;           // take result
309
}
310
311
SbiInstance::SbiInstance( StarBASIC* p )
312
    : pIosys(new SbiIoSystem)
313
    , pDdeCtrl(new SbiDdeControl)
314
    , pBasic(p)
315
    , meFormatterLangType(LANGUAGE_DONTKNOW)
316
    , meFormatterDateOrder(DateOrder::YMD)
317
    , nStdDateIdx(0)
318
    , nStdTimeIdx(0)
319
    , nStdDateTimeIdx(0)
320
    , nErr(0)
321
    , nErl(0)
322
    , bReschedule(true)
323
    , bCompatibility(false)
324
    , pRun(nullptr)
325
    , nCallLvl(0)
326
    , nBreakCallLvl(0)
327
{
328
}
329
330
SbiInstance::~SbiInstance()
331
{
332
    while( pRun )
333
    {
334
        SbiRuntime* p = pRun->pNext;
335
        delete pRun;
336
        pRun = p;
337
    }
338
339
    try
340
    {
341
        int nSize = ComponentVector.size();
342
        if( nSize )
343
        {
344
            for( int i = nSize - 1 ; i >= 0 ; --i )
345
            {
346
                Reference< XComponent > xDlgComponent = ComponentVector[i];
347
                if( xDlgComponent.is() )
348
                    xDlgComponent->dispose();
349
            }
350
        }
351
    }
352
    catch( const Exception& )
353
    {
354
        TOOLS_WARN_EXCEPTION("basic", "SbiInstance::~SbiInstance: caught an exception while disposing the components" );
355
    }
356
}
357
358
SbiDllMgr* SbiInstance::GetDllMgr()
359
{
360
    if( !pDllMgr )
361
    {
362
        pDllMgr.reset(new SbiDllMgr);
363
    }
364
    return pDllMgr.get();
365
}
366
367
#endif
368
369
bool SbiRuntime::isVBAEnabled()
370
0
{
371
0
    SbiInstance* pInst = GetSbData()->pInst;
372
0
    return pInst && pInst->pRun && pInst->pRun->bVBAEnabled;
373
0
}
374
375
// #39629 create NumberFormatter with the help of a static method now
376
std::shared_ptr<SvNumberFormatter> const & SbiInstance::GetNumberFormatter()
377
0
{
378
0
    LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
379
0
    SvtSysLocale aSysLocale;
380
0
    DateOrder eDate = aSysLocale.GetLocaleData().getDateOrder();
381
0
    if( pNumberFormatter )
382
0
    {
383
0
        if( eLangType != meFormatterLangType ||
384
0
            eDate != meFormatterDateOrder )
385
0
        {
386
0
            pNumberFormatter.reset();
387
0
        }
388
0
    }
389
0
    meFormatterLangType = eLangType;
390
0
    meFormatterDateOrder = eDate;
391
0
    if( !pNumberFormatter )
392
0
    {
393
0
        pNumberFormatter = PrepareNumberFormatter( nStdDateIdx, nStdTimeIdx, nStdDateTimeIdx,
394
0
                &meFormatterLangType, &meFormatterDateOrder);
395
0
    }
396
0
    return pNumberFormatter;
397
0
}
398
399
// #39629 offer NumberFormatter static too
400
std::shared_ptr<SvNumberFormatter> SbiInstance::PrepareNumberFormatter( sal_uInt32 &rnStdDateIdx,
401
    sal_uInt32 &rnStdTimeIdx, sal_uInt32 &rnStdDateTimeIdx,
402
    LanguageType const * peFormatterLangType, DateOrder const * peFormatterDateOrder )
403
0
{
404
0
    LanguageType eLangType;
405
0
    if( peFormatterLangType )
406
0
    {
407
0
        eLangType = *peFormatterLangType;
408
0
    }
409
0
    else
410
0
    {
411
0
        eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
412
0
    }
413
0
    DateOrder eDate;
414
0
    if( peFormatterDateOrder )
415
0
    {
416
0
        eDate = *peFormatterDateOrder;
417
0
    }
418
0
    else
419
0
    {
420
0
        SvtSysLocale aSysLocale;
421
0
        eDate = aSysLocale.GetLocaleData().getDateOrder();
422
0
    }
423
424
0
    std::shared_ptr<SvNumberFormatter> pNumberFormatter =
425
0
            std::make_shared<SvNumberFormatter>( comphelper::getProcessComponentContext(), eLangType );
426
427
    // Several parser methods pass SvNumberFormatter::IsNumberFormat() a number
428
    // format index to parse against. Tell the formatter the proper date
429
    // evaluation order, which also determines the date acceptance patterns to
430
    // use if a format was passed. NfEvalDateFormat::Format restricts to the
431
    // format's locale's date patterns/order (no init/system locale match
432
    // tried) and falls back to NfEvalDateFormat::International if no specific (i.e. 0)
433
    // (or an unknown) format index was passed.
434
0
    pNumberFormatter->SetEvalDateFormat( NfEvalDateFormat::Format);
435
436
0
    sal_Int32 nCheckPos = 0;
437
0
    SvNumFormatType nType;
438
0
    rnStdTimeIdx = pNumberFormatter->GetStandardFormat( SvNumFormatType::TIME, eLangType );
439
440
    // the formatter's standard templates have only got a two-digit date
441
    // -> registering an own format
442
443
    // HACK, because the numberformatter doesn't swap the place holders
444
    // for month, day and year according to the system setting.
445
    // Problem: Print Year(Date) under engl. BS
446
    // also have a look at: basic/source/sbx/sbxdate.cxx
447
448
0
    OUString aDateStr;
449
0
    switch( eDate )
450
0
    {
451
0
        default:
452
0
        case DateOrder::MDY: aDateStr = "MM/DD/YYYY"; break;
453
0
        case DateOrder::DMY: aDateStr = "DD/MM/YYYY"; break;
454
0
        case DateOrder::YMD: aDateStr = "YYYY/MM/DD"; break;
455
0
    }
456
0
    OUString aStr( aDateStr );      // PutandConvertEntry() modifies string!
457
0
    pNumberFormatter->PutandConvertEntry( aStr, nCheckPos, nType,
458
0
        rnStdDateIdx, LANGUAGE_ENGLISH_US, eLangType, true);
459
0
    nCheckPos = 0;
460
0
    aDateStr += " HH:MM:SS";
461
0
    aStr = aDateStr;
462
0
    pNumberFormatter->PutandConvertEntry( aStr, nCheckPos, nType,
463
0
        rnStdDateTimeIdx, LANGUAGE_ENGLISH_US, eLangType, true);
464
0
    return pNumberFormatter;
465
0
}
466
467
#if HAVE_FEATURE_SCRIPTING
468
469
// Let engine run. If Flags == BasicDebugFlags::Continue, take Flags over
470
471
void SbiInstance::Stop()
472
{
473
    for( SbiRuntime* p = pRun; p; p = p->pNext )
474
    {
475
        p->Stop();
476
    }
477
}
478
479
// Allows Basic IDE to set watch mode to suppress errors
480
static bool bWatchMode = false;
481
482
void setBasicWatchMode( bool bOn )
483
{
484
    bWatchMode = bOn;
485
}
486
487
void SbiInstance::Error( ErrCode n )
488
{
489
    Error( n, OUString() );
490
}
491
492
void SbiInstance::Error( ErrCode n, const OUString& rMsg )
493
{
494
    if( !bWatchMode )
495
    {
496
        aErrorMsg = rMsg;
497
        pRun->Error( n );
498
    }
499
}
500
501
void SbiInstance::ErrorVB( sal_Int32 nVBNumber, const OUString& rMsg )
502
{
503
    if( !bWatchMode )
504
    {
505
        ErrCode n = StarBASIC::GetSfxFromVBError( static_cast< sal_uInt16 >( nVBNumber ) );
506
        if ( !n )
507
        {
508
            n = ErrCode(nVBNumber); // force orig number, probably should have a specific table of vb ( localized ) errors
509
        }
510
        aErrorMsg = rMsg;
511
        SbiRuntime::translateErrorToVba( n, aErrorMsg );
512
513
        pRun->Error( ERRCODE_BASIC_COMPAT, true/*bVBATranslationAlreadyDone*/ );
514
    }
515
}
516
517
void SbiInstance::setErrorVB( sal_Int32 nVBNumber )
518
{
519
    ErrCode n = StarBASIC::GetSfxFromVBError( static_cast< sal_uInt16 >( nVBNumber ) );
520
    if( !n )
521
    {
522
        n = ErrCode(nVBNumber); // force orig number, probably should have a specific table of vb ( localized ) errors
523
    }
524
    aErrorMsg = OUString();
525
    SbiRuntime::translateErrorToVba( n, aErrorMsg );
526
527
    nErr = n;
528
}
529
530
531
void SbiInstance::FatalError( ErrCode n )
532
{
533
    pRun->FatalError( n );
534
}
535
536
void SbiInstance::FatalError( ErrCode _errCode, const OUString& _details )
537
{
538
    pRun->FatalError( _errCode, _details );
539
}
540
541
void SbiInstance::Abort()
542
{
543
    StarBASIC* pErrBasic = GetCurrentBasic( pBasic );
544
    pErrBasic->RTError( nErr, aErrorMsg, pRun->nLine, pRun->nCol1, pRun->nCol2 );
545
    StarBASIC::Stop();
546
}
547
548
// can be unequal to pRTBasic
549
StarBASIC* GetCurrentBasic( StarBASIC* pRTBasic )
550
{
551
    StarBASIC* pCurBasic = pRTBasic;
552
    SbModule* pActiveModule = StarBASIC::GetActiveModule();
553
    if( pActiveModule )
554
    {
555
        SbxObject* pParent = pActiveModule->GetParent();
556
        if (StarBASIC *pBasic = dynamic_cast<StarBASIC*>(pParent))
557
            pCurBasic = pBasic;
558
    }
559
    return pCurBasic;
560
}
561
562
SbModule* SbiInstance::GetActiveModule()
563
{
564
    if( pRun )
565
    {
566
        return pRun->GetModule();
567
    }
568
    else
569
    {
570
        return nullptr;
571
    }
572
}
573
574
SbMethod* SbiInstance::GetCaller( sal_uInt16 nLevel )
575
{
576
    SbiRuntime* p = pRun;
577
    while( nLevel-- && p )
578
    {
579
        p = p->pNext;
580
    }
581
    return p ? p->GetCaller() : nullptr;
582
}
583
584
//                              SbiInstance
585
586
// Attention: pMeth can also be NULL (on a call of the init-code)
587
588
SbiRuntime::SbiRuntime( SbModule* pm, SbMethod* pe, sal_uInt32 nStart )
589
         : rBasic( *static_cast<StarBASIC*>(pm->pParent) ), pInst( GetSbData()->pInst ),
590
           pMod( pm ), pMeth( pe ), pImg( pMod->pImage.get() )
591
{
592
    nFlags    = pe ? pe->GetDebugFlags() : BasicDebugFlags::NONE;
593
    pIosys    = pInst->GetIoSystem();
594
    pCode     =
595
    pStmnt    = pImg->GetCode() + nStart;
596
    refExprStk = new SbxArray;
597
    SetVBAEnabled( pMod->IsVBASupport() );
598
    SetParameters( pe ? pe->GetParameters() : nullptr );
599
}
600
601
SbiRuntime::~SbiRuntime()
602
{
603
    ClearArgvStack();
604
    ClearForStack();
605
}
606
607
void SbiRuntime::SetVBAEnabled(bool bEnabled )
608
{
609
    bVBAEnabled = bEnabled;
610
    if ( bVBAEnabled )
611
    {
612
        if ( pMeth )
613
        {
614
            mpExtCaller = pMeth->mCaller;
615
        }
616
    }
617
    else
618
    {
619
        mpExtCaller = nullptr;
620
    }
621
}
622
623
// tdf#79426, tdf#125180 - adds the information about a missing parameter
624
void SbiRuntime::SetIsMissing( SbxVariable* pVar )
625
{
626
    SbxInfo* pInfo = pVar->GetInfo() ? pVar->GetInfo() : new SbxInfo();
627
    pInfo->AddParam( pVar->GetName(), SbxMISSING, pVar->GetFlags() );
628
    pVar->SetInfo( pInfo );
629
}
630
631
// tdf#102381 - turns a variable into the representation of a missing argument
632
void SbxSetMissingParameter(SbxVariable& rVar)
633
{
634
    // #57915 The semantics of a missing argument is represented by the value 448
635
    // (ERRCODE_BASIC_NAMED_NOT_FOUND) of the type error in VB.
636
    rVar.PutErr(448);
637
    SbiRuntime::SetIsMissing(&rVar);
638
}
639
640
// tdf#79426, tdf#125180 - checks if a variable contains the information about a missing parameter
641
bool SbiRuntime::IsMissing( SbxVariable* pVar, sal_uInt16 nIdx )
642
{
643
    return pVar->GetInfo() && pVar->GetInfo()->GetParam( nIdx ) && pVar->GetInfo()->GetParam( nIdx )->eType & SbxMISSING;
644
}
645
646
// Construction of the parameter list. All ByRef-parameters are directly
647
// taken over; copies of ByVal-parameters are created. If a particular
648
// data type is requested, it is converted.
649
650
void SbiRuntime::SetParameters( SbxArray* pParams )
651
{
652
    refParams = new SbxArray;
653
    // for the return value
654
    refParams->Put(pMeth, 0);
655
656
    SbxInfo* pInfo = pMeth ? pMeth->GetInfo() : nullptr;
657
    sal_uInt32 nParamCount = pParams ? pParams->Count() : 1;
658
    assert(nParamCount <= std::numeric_limits<sal_uInt16>::max());
659
    if( nParamCount > 1 )
660
    {
661
        for( sal_uInt32 i = 1 ; i < nParamCount ; i++ )
662
        {
663
            const SbxParamInfo* p = pInfo ? pInfo->GetParam( sal::static_int_cast<sal_uInt16>(i) ) : nullptr;
664
665
            // #111897 ParamArray
666
            if( p && ParamInfoFlag::checkFlagFor(p->nUserData, ParamInfo::ParamArray) )
667
            {
668
                SbxDimArray* pArray = new SbxDimArray( SbxVARIANT );
669
                sal_uInt32 nParamArrayParamCount = nParamCount - i;
670
                pArray->unoAddDim(0, nParamArrayParamCount - 1);
671
                for (sal_uInt32 j = i; j < nParamCount ; ++j)
672
                {
673
                    SbxVariable* v = pParams->Get(j);
674
                    sal_Int32 aDimIndex[1];
675
                    aDimIndex[0] = j - i;
676
                    pArray->Put(v, aDimIndex);
677
                }
678
                SbxVariable* pArrayVar = new SbxVariable( SbxVARIANT );
679
                pArrayVar->SetFlag( SbxFlagBits::ReadWrite );
680
                pArrayVar->PutObject( pArray );
681
                refParams->Put(pArrayVar, i);
682
683
                // Block ParamArray for missing parameter
684
                pInfo = nullptr;
685
                break;
686
            }
687
688
            SbxVariable* v = pParams->Get(i);
689
            assert(v);
690
            // methods are always byval!
691
            bool bByVal = dynamic_cast<const SbxMethod *>(v) != nullptr;
692
            SbxDataType t = v->GetType();
693
            bool bTargetTypeIsArray = false;
694
            if( p )
695
            {
696
                bByVal |= ( p->eType & SbxBYREF ) == 0;
697
                // tdf#79426, tdf#125180 - don't convert missing arguments to the requested parameter type
698
                if ( !IsMissing( v, 1 ) )
699
                {
700
                    t = static_cast<SbxDataType>( p->eType & 0x0FFF );
701
                }
702
703
                if( !bByVal && t != SbxVARIANT &&
704
                    (!v->IsFixed() || static_cast<SbxDataType>(v->GetType() & 0x0FFF ) != t) )
705
                {
706
                    bByVal = true;
707
                }
708
709
                bTargetTypeIsArray = ParamInfoFlag::checkFlagFor(p->nUserData, ParamInfo::WithBrackets);
710
            }
711
            if( bByVal )
712
            {
713
                // tdf#79426, tdf#125180 - don't convert missing arguments to the requested parameter type
714
                if( bTargetTypeIsArray && !IsMissing( v, 1 ) )
715
                {
716
                    t = SbxOBJECT;
717
                }
718
                SbxVariable* v2 = new SbxVariable( t );
719
                v2->SetFlag( SbxFlagBits::ReadWrite );
720
                // tdf#79426, tdf#125180 - if parameter was missing, re-add additional information about a missing parameter
721
                if ( IsMissing( v, 1 ) )
722
                {
723
                    SetIsMissing( v2 );
724
                }
725
                *v2 = *v;
726
                refParams->Put(v2, i);
727
            }
728
            else
729
            {
730
                // tdf#79426, tdf#125180 - don't convert missing arguments to the requested parameter type
731
                if( t != SbxVARIANT && !IsMissing( v, 1 ) && t != ( v->GetType() & 0x0FFF ) )
732
                {
733
                    if( p && (p->eType & SbxARRAY) )
734
                    {
735
                        Error( ERRCODE_BASIC_CONVERSION );
736
                    }
737
                    else
738
                    {
739
                        v->Convert( t );
740
                    }
741
                }
742
                refParams->Put(v, i);
743
            }
744
            if( p )
745
            {
746
                refParams->PutAlias(p->aName, i);
747
            }
748
        }
749
    }
750
751
    // ParamArray for missing parameter
752
    if( !pInfo )
753
        return;
754
755
    // #111897 Check first missing parameter for ParamArray
756
    const SbxParamInfo* p = pInfo->GetParam(sal::static_int_cast<sal_uInt16>(nParamCount));
757
    if( p && ParamInfoFlag::checkFlagFor(p->nUserData, ParamInfo::ParamArray) )
758
    {
759
        SbxDimArray* pArray = new SbxDimArray( SbxVARIANT );
760
        pArray->unoAddDim(0, -1);
761
        SbxVariable* pArrayVar = new SbxVariable( SbxVARIANT );
762
        pArrayVar->SetFlag( SbxFlagBits::ReadWrite );
763
        pArrayVar->PutObject( pArray );
764
        refParams->Put(pArrayVar, nParamCount);
765
    }
766
}
767
768
769
// execute a P-Code
770
771
bool SbiRuntime::Step()
772
{
773
    if( bRun )
774
    {
775
        static sal_uInt32 nLastTime = osl_getGlobalTimer();
776
777
        // in any case check casually!
778
        if( !( ++nOps & 0xF ) && pInst->IsReschedule() )
779
        {
780
            sal_uInt32 nTime = osl_getGlobalTimer();
781
            if (nTime - nLastTime > 5) // 20 ms
782
            {
783
                nLastTime = nTime;
784
                Application::Reschedule();
785
            }
786
        }
787
788
        // #i48868 blocked by next call level?
789
        while( bBlocked )
790
        {
791
            if( pInst->IsReschedule() ) // And what if not? Busy loop?
792
            {
793
                Application::Reschedule();
794
                nLastTime = osl_getGlobalTimer();
795
            }
796
        }
797
798
        SbiOpcode eOp = static_cast<SbiOpcode>( *pCode++ );
799
        sal_uInt32 nOp1;
800
        if (eOp <= SbiOpcode::SbOP0_END)
801
        {
802
            (this->*( aStep0[ int(eOp) ] ) )();
803
        }
804
        else if (eOp >= SbiOpcode::SbOP1_START && eOp <= SbiOpcode::SbOP1_END)
805
        {
806
            nOp1 = *pCode++; nOp1 |= *pCode++ << 8; nOp1 |= *pCode++ << 16; nOp1 |= *pCode++ << 24;
807
808
            (this->*( aStep1[ int(eOp) - int(SbiOpcode::SbOP1_START) ] ) )( nOp1 );
809
        }
810
        else if (eOp >= SbiOpcode::SbOP2_START && eOp <= SbiOpcode::SbOP2_END)
811
        {
812
            nOp1 = *pCode++; nOp1 |= *pCode++ << 8; nOp1 |= *pCode++ << 16; nOp1 |= *pCode++ << 24;
813
            sal_uInt32 nOp2 = *pCode++; nOp2 |= *pCode++ << 8; nOp2 |= *pCode++ << 16; nOp2 |= *pCode++ << 24;
814
            (this->*( aStep2[ int(eOp) - int(SbiOpcode::SbOP2_START) ] ) )( nOp1, nOp2 );
815
        }
816
        else
817
        {
818
            StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
819
        }
820
821
        ErrCode nErrCode = SbxBase::GetError();
822
        Error( nErrCode.IgnoreWarning() );
823
824
        // from 13.2.1997, new error handling:
825
        // ATTENTION: nError can be set already even if !nErrCode
826
        // since nError can now also be set from other RT-instances
827
828
        if( nError )
829
        {
830
            SbxBase::ResetError();
831
        }
832
833
        // from 15.3.96: display errors only if BASIC is still active
834
        // (especially not after compiler errors at the runtime)
835
        if( nError && bRun )
836
        {
837
            ErrCode err = nError;
838
            ClearExprStack();
839
            nError = ERRCODE_NONE;
840
            pInst->nErr = err;
841
            pInst->nErl = nLine;
842
            pErrCode    = pCode;
843
            pErrStmnt   = pStmnt;
844
            // An error occurred in an error handler
845
            // force parent handler ( if there is one )
846
            // to handle the error
847
            bool bLetParentHandleThis = false;
848
849
            // in the error handler? so std-error
850
            if ( !bInError )
851
            {
852
                bInError = true;
853
854
                if( !bError )           // On Error Resume Next
855
                {
856
                    StepRESUME( 1 );
857
                }
858
                else if( pError )       // On Error Goto ...
859
                {
860
                    pCode = pError;
861
                }
862
                else
863
                {
864
                    bLetParentHandleThis = true;
865
                }
866
            }
867
            else
868
            {
869
                bLetParentHandleThis = true;
870
                pError = nullptr; //terminate the handler
871
            }
872
            if ( bLetParentHandleThis )
873
            {
874
                // from 13.2.1997, new error handling:
875
                // consider superior error handlers
876
877
                // there's no error handler -> find one farther above
878
                SbiRuntime* pRtErrHdl = nullptr;
879
                SbiRuntime* pRt = this;
880
                while( (pRt = pRt->pNext) != nullptr )
881
                {
882
                    if( !pRt->bError || pRt->pError != nullptr )
883
                    {
884
                        pRtErrHdl = pRt;
885
                        break;
886
                    }
887
                }
888
889
890
                if( pRtErrHdl )
891
                {
892
                    // manipulate all the RTs that are below in the call-stack
893
                    pRt = this;
894
                    do
895
                    {
896
                        pRt->nError = err;
897
                        if( pRt != pRtErrHdl )
898
                        {
899
                            pRt->bRun = false;
900
                        }
901
                        else
902
                        {
903
                            break;
904
                        }
905
                        pRt = pRt->pNext;
906
                    }
907
                    while( pRt );
908
                }
909
                // no error-hdl found -> old behaviour
910
                else
911
                {
912
                    pInst->Abort();
913
                }
914
            }
915
        }
916
    }
917
    return bRun;
918
}
919
920
void SbiRuntime::Error( ErrCode n, bool bVBATranslationAlreadyDone )
921
{
922
    if( !n )
923
        return;
924
925
    nError = n;
926
    if( !isVBAEnabled() || bVBATranslationAlreadyDone )
927
        return;
928
929
    OUString aMsg = pInst->GetErrorMsg();
930
    sal_Int32 nVBAErrorNumber = translateErrorToVba( nError, aMsg );
931
    SbxVariable* pSbxErrObjVar = SbxErrObject::getErrObject().get();
932
    SbxErrObject* pGlobErr = static_cast< SbxErrObject* >( pSbxErrObjVar );
933
    if( pGlobErr != nullptr )
934
    {
935
        pGlobErr->setNumberAndDescription( nVBAErrorNumber, aMsg );
936
    }
937
    pInst->aErrorMsg = aMsg;
938
    nError = ERRCODE_BASIC_COMPAT;
939
}
940
941
void SbiRuntime::Error( ErrCode _errCode, const OUString& _details )
942
{
943
    if ( !_errCode )
944
        return;
945
946
    // Not correct for class module usage, remove for now
947
    //OSL_WARN_IF( pInst->pRun != this, "basic", "SbiRuntime::Error: can't propagate the error message details!" );
948
    if ( pInst->pRun == this )
949
    {
950
        pInst->Error( _errCode, _details );
951
        //OSL_WARN_IF( nError != _errCode, "basic", "SbiRuntime::Error: the instance is expected to propagate the error code back to me!" );
952
    }
953
    else
954
    {
955
        nError = _errCode;
956
    }
957
}
958
959
void SbiRuntime::FatalError( ErrCode n )
960
{
961
    StepSTDERROR();
962
    Error( n );
963
}
964
965
void SbiRuntime::FatalError( ErrCode _errCode, const OUString& _details )
966
{
967
    StepSTDERROR();
968
    Error( _errCode, _details );
969
}
970
971
sal_Int32 SbiRuntime::translateErrorToVba( ErrCode nError, OUString& rMsg )
972
{
973
    // If a message is defined use that ( in preference to
974
    // the defined one for the error ) NB #TODO
975
    // if there is an error defined it more than likely
976
    // is not the one you want ( some are the same though )
977
    // we really need a new vba compatible error list
978
    // tdf#123144 - always translate an error number to a vba error message
979
    StarBASIC::MakeErrorText( nError, rMsg );
980
    rMsg = StarBASIC::GetErrorText();
981
    // no num? most likely then it *is* really a vba err
982
    sal_uInt16 nVBErrorCode = StarBASIC::GetVBErrorCode( nError );
983
    sal_Int32 nVBAErrorNumber = ( nVBErrorCode == 0 ) ? sal_uInt32(nError) : nVBErrorCode;
984
    return nVBAErrorNumber;
985
}
986
987
//  Stacks
988
989
// The expression-stack is available for the continuous evaluation
990
// of expressions.
991
992
void SbiRuntime::PushVar( SbxVariable* pVar )
993
{
994
    if( pVar )
995
    {
996
        refExprStk->Put(pVar, nExprLvl++);
997
    }
998
}
999
1000
SbxVariableRef SbiRuntime::PopVar()
1001
{
1002
#ifdef DBG_UTIL
1003
    if( !nExprLvl )
1004
    {
1005
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
1006
        return new SbxVariable;
1007
    }
1008
#endif
1009
    SbxVariableRef xVar = refExprStk->Get(--nExprLvl);
1010
    SAL_INFO_IF( xVar->GetName() == "Cells", "basic", "PopVar: Name equals 'Cells'" );
1011
    // methods hold themselves in parameter 0
1012
    if( dynamic_cast<const SbxMethod *>(xVar.get()) != nullptr )
1013
    {
1014
        xVar->SetParameters(nullptr);
1015
    }
1016
    return xVar;
1017
}
1018
1019
void SbiRuntime::ClearExprStack()
1020
{
1021
    // Attention: Clear() doesn't suffice as methods must be deleted
1022
    while ( nExprLvl )
1023
    {
1024
        PopVar();
1025
    }
1026
    refExprStk->Clear();
1027
}
1028
1029
// Take variable from the expression-stack without removing it
1030
// n counts from 0
1031
1032
SbxVariable* SbiRuntime::GetTOS()
1033
{
1034
    short n = nExprLvl - 1;
1035
#ifdef DBG_UTIL
1036
    if( n < 0 )
1037
    {
1038
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
1039
        return new SbxVariable;
1040
    }
1041
#endif
1042
    return refExprStk->Get(static_cast<sal_uInt32>(n));
1043
}
1044
1045
1046
void SbiRuntime::TOSMakeTemp()
1047
{
1048
    SbxVariable* p = refExprStk->Get(nExprLvl - 1);
1049
    if ( p->GetType() == SbxEMPTY )
1050
    {
1051
        p->Broadcast( SfxHintId::BasicDataWanted );
1052
    }
1053
1054
    SbxVariable* pDflt = nullptr;
1055
    if ( bVBAEnabled &&  ( p->GetType() == SbxOBJECT || p->GetType() == SbxVARIANT  ) && ((pDflt = getDefaultProp(p)) != nullptr) )
1056
    {
1057
        pDflt->Broadcast( SfxHintId::BasicDataWanted );
1058
        // replacing new p on stack causes object pointed by
1059
        // pDft->pParent to be deleted, when p2->Compute() is
1060
        // called below pParent is accessed (but it's deleted)
1061
        // so set it to NULL now
1062
        pDflt->SetParent( nullptr );
1063
        p = new SbxVariable( *pDflt );
1064
        p->SetFlag( SbxFlagBits::ReadWrite );
1065
        refExprStk->Put(p, nExprLvl - 1);
1066
    }
1067
    else if( p->GetRefCount() != 1 )
1068
    {
1069
        SbxVariable* pNew = new SbxVariable( *p );
1070
        pNew->SetFlag( SbxFlagBits::ReadWrite );
1071
        refExprStk->Put(pNew, nExprLvl - 1);
1072
    }
1073
}
1074
1075
// the GOSUB-stack collects return-addresses for GOSUBs
1076
void SbiRuntime::PushGosub( const sal_uInt8* pc )
1077
{
1078
    if( pGosubStk.size() >= MAXRECURSION )
1079
    {
1080
        StarBASIC::FatalError( ERRCODE_BASIC_STACK_OVERFLOW );
1081
    }
1082
    pGosubStk.emplace_back(pc, nForLvl);
1083
}
1084
1085
void SbiRuntime::PopGosub()
1086
{
1087
    if( pGosubStk.empty() )
1088
    {
1089
        Error( ERRCODE_BASIC_NO_GOSUB );
1090
    }
1091
    else
1092
    {
1093
        pCode = pGosubStk.back().pCode;
1094
        pGosubStk.pop_back();
1095
    }
1096
}
1097
1098
// the Argv-stack collects current argument-vectors
1099
1100
void SbiRuntime::PushArgv()
1101
{
1102
    pArgvStk.emplace_back(refArgv, nArgc);
1103
    nArgc = 1;
1104
    refArgv.clear();
1105
}
1106
1107
void SbiRuntime::PopArgv()
1108
{
1109
    if( !pArgvStk.empty() )
1110
    {
1111
        refArgv = pArgvStk.back().refArgv;
1112
        nArgc = pArgvStk.back().nArgc;
1113
        pArgvStk.pop_back();
1114
    }
1115
}
1116
1117
1118
void SbiRuntime::ClearArgvStack()
1119
{
1120
    while( !pArgvStk.empty() )
1121
    {
1122
        PopArgv();
1123
    }
1124
}
1125
1126
// Push of the for-stack. The stack has increment, end, begin and variable.
1127
// After the creation of the stack-element the stack's empty.
1128
1129
void SbiRuntime::PushFor()
1130
{
1131
    SbiForStack* p = new SbiForStack;
1132
    p->eForType = ForType::To;
1133
    p->pNext = pForStk;
1134
    pForStk = p;
1135
1136
    p->refInc = PopVar();
1137
    p->refEnd = PopVar();
1138
    if (isVBAEnabled())
1139
    {
1140
        // tdf#150458: only calculate these once, coercing to double
1141
        // tdf#150460: shouldn't we do it in non-VBA / compat mode, too?
1142
        SbxVariableRef incCopy(new SbxVariable(SbxDOUBLE));
1143
        *incCopy = *p->refInc;
1144
        p->refInc = std::move(incCopy);
1145
        SbxVariableRef endCopy(new SbxVariable(SbxDOUBLE));
1146
        *endCopy = *p->refEnd;
1147
        p->refEnd = std::move(endCopy);
1148
    }
1149
    SbxVariableRef xBgn = PopVar();
1150
    p->refVar = PopVar();
1151
    // tdf#85371 - grant explicitly write access to the index variable
1152
    // since it could be the name of a method itself used in the next statement.
1153
    ScopedWritableGuard aGuard(p->refVar, p->refVar.get() == pMeth);
1154
    *(p->refVar) = *xBgn;
1155
    nForLvl++;
1156
}
1157
1158
void SbiRuntime::PushForEach()
1159
{
1160
    SbiForStack* p = new SbiForStack;
1161
    // Set default value in case of error which is ignored in Resume Next
1162
    p->eForType = ForType::EachArray;
1163
    p->pNext = pForStk;
1164
    pForStk = p;
1165
1166
    SbxVariableRef xObjVar = PopVar();
1167
    SbxBase* pObj(nullptr);
1168
    if (xObjVar)
1169
    {
1170
        SbxValues v(SbxVARIANT);
1171
        // Here it may retrieve the value, and change the type from SbxEMPTY to SbxOBJECT
1172
        xObjVar->Get(v);
1173
        if (v.eType == SbxOBJECT)
1174
            pObj = v.pObj;
1175
    }
1176
1177
    if (SbxDimArray* pArray = dynamic_cast<SbxDimArray*>(pObj))
1178
    {
1179
        p->refEnd = reinterpret_cast<SbxVariable*>(pArray);
1180
1181
        sal_Int32 nDims = pArray->GetDims();
1182
        p->pArrayLowerBounds.reset( new sal_Int32[nDims] );
1183
        p->pArrayUpperBounds.reset( new sal_Int32[nDims] );
1184
        p->pArrayCurIndices.reset( new sal_Int32[nDims] );
1185
        sal_Int32 lBound, uBound;
1186
        for( sal_Int32 i = 0 ; i < nDims ; i++ )
1187
        {
1188
            pArray->GetDim(i + 1, lBound, uBound);
1189
            p->pArrayCurIndices[i] = p->pArrayLowerBounds[i] = lBound;
1190
            p->pArrayUpperBounds[i] = uBound;
1191
        }
1192
    }
1193
    else if (BasicCollection* pCollection = dynamic_cast<BasicCollection*>(pObj))
1194
    {
1195
        p->eForType = ForType::EachCollection;
1196
        p->refEnd = pCollection;
1197
        p->nCurCollectionIndex = 0;
1198
    }
1199
    else if (SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>(pObj))
1200
    {
1201
        // XEnumerationAccess or XIndexAccess?
1202
        Any aAny = pUnoObj->getUnoAny();
1203
        Reference<XIndexAccess> xIndexAccess;
1204
        Reference< XEnumerationAccess > xEnumerationAccess;
1205
        if( aAny >>= xEnumerationAccess )
1206
        {
1207
            p->xEnumeration = xEnumerationAccess->createEnumeration();
1208
            p->eForType = ForType::EachXEnumeration;
1209
        }
1210
        // tdf#130307 - support for each loop for objects exposing XIndexAccess
1211
        else if (aAny >>= xIndexAccess)
1212
        {
1213
            p->eForType = ForType::EachXIndexAccess;
1214
            p->xIndexAccess = std::move(xIndexAccess);
1215
            p->nCurCollectionIndex = 0;
1216
        }
1217
        else if ( isVBAEnabled() && pUnoObj->isNativeCOMObject() )
1218
        {
1219
            uno::Reference< script::XInvocation > xInvocation;
1220
            if ( ( aAny >>= xInvocation ) && xInvocation.is() )
1221
            {
1222
                try
1223
                {
1224
                    p->xEnumeration = new ComEnumerationWrapper( xInvocation );
1225
                    p->eForType = ForType::EachXEnumeration;
1226
                }
1227
                catch(const uno::Exception& )
1228
                {}
1229
            }
1230
        }
1231
    }
1232
1233
    // Container variable
1234
    p->refVar = PopVar();
1235
    nForLvl++;
1236
}
1237
1238
1239
void SbiRuntime::PopFor()
1240
{
1241
    if( pForStk )
1242
    {
1243
        SbiForStack* p = pForStk;
1244
        pForStk = p->pNext;
1245
        delete p;
1246
        nForLvl--;
1247
    }
1248
}
1249
1250
1251
void SbiRuntime::ClearForStack()
1252
{
1253
    while( pForStk )
1254
    {
1255
        PopFor();
1256
    }
1257
}
1258
1259
SbiForStack* SbiRuntime::FindForStackItemForCollection( class BasicCollection const * pCollection )
1260
{
1261
    for (SbiForStack *p = pForStk; p; p = p->pNext)
1262
    {
1263
        SbxVariable* pVar = p->refEnd.is() ? p->refEnd.get() : nullptr;
1264
        if( p->eForType == ForType::EachCollection
1265
         && pVar != nullptr
1266
         && dynamic_cast<BasicCollection*>( pVar) == pCollection  )
1267
        {
1268
            return p;
1269
        }
1270
    }
1271
1272
    return nullptr;
1273
}
1274
1275
1276
//  DLL-calls
1277
1278
void SbiRuntime::DllCall
1279
    ( std::u16string_view aFuncName,
1280
      std::u16string_view aDLLName,
1281
      SbxArray* pArgs,          // parameter (from index 1, can be NULL)
1282
      SbxDataType eResType,     // return value
1283
      bool bCDecl )         // true: according to C-conventions
1284
{
1285
    SbxVariable* pRes = new SbxVariable( eResType );
1286
    SbiDllMgr* pDllMgr = pInst->GetDllMgr();
1287
    ErrCode nErr = pDllMgr->Call( aFuncName, aDLLName, pArgs, *pRes, bCDecl );
1288
    if( nErr )
1289
    {
1290
        Error( nErr );
1291
    }
1292
    PushVar( pRes );
1293
}
1294
1295
bool SbiRuntime::IsImageFlag( SbiImageFlags n ) const
1296
{
1297
    return pImg->IsFlag( n );
1298
}
1299
1300
sal_uInt16 SbiRuntime::GetBase() const
1301
{
1302
    return pImg->GetBase();
1303
}
1304
1305
void SbiRuntime::StepNOP()
1306
{}
1307
1308
void SbiRuntime::StepArith( SbxOperator eOp )
1309
{
1310
    SbxVariableRef p1 = PopVar();
1311
    TOSMakeTemp();
1312
    SbxVariable* p2 = GetTOS();
1313
1314
    // tdf#144353 - do not compute any operation with a missing optional variable
1315
    if ((p1->GetType() == SbxERROR && IsMissing(p1.get(), 1))
1316
        || (p2->GetType() == SbxERROR && IsMissing(p2, 1)))
1317
    {
1318
        Error(ERRCODE_BASIC_NOT_OPTIONAL);
1319
        return;
1320
    }
1321
1322
    p2->ResetFlag( SbxFlagBits::Fixed );
1323
    p2->Compute( eOp, *p1 );
1324
1325
    checkArithmeticOverflow( p2 );
1326
}
1327
1328
void SbiRuntime::StepUnary( SbxOperator eOp )
1329
{
1330
    TOSMakeTemp();
1331
    SbxVariable* p = GetTOS();
1332
    // tdf#144353 - do not compute any operation with a missing optional variable
1333
    if (p->GetType() == SbxERROR && IsMissing(p, 1))
1334
    {
1335
        Error(ERRCODE_BASIC_NOT_OPTIONAL);
1336
        return;
1337
    }
1338
    p->Compute( eOp, *p );
1339
}
1340
1341
void SbiRuntime::StepCompare( SbxOperator eOp )
1342
{
1343
    SbxVariableRef p1 = PopVar();
1344
    SbxVariableRef p2 = PopVar();
1345
1346
    // tdf#144353 - do not compare a missing optional variable
1347
    if ((p1->GetType() == SbxERROR && SbiRuntime::IsMissing(p1.get(), 1))
1348
        || (p2->GetType() == SbxERROR && SbiRuntime::IsMissing(p2.get(), 1)))
1349
    {
1350
        SbxBase::SetError(ERRCODE_BASIC_NOT_OPTIONAL);
1351
        return;
1352
    }
1353
1354
    // Make sure objects with default params have
1355
    // values ( and type ) set as appropriate
1356
    SbxDataType p1Type = p1->GetType();
1357
    SbxDataType p2Type = p2->GetType();
1358
    if ( p1Type == SbxEMPTY )
1359
    {
1360
        p1->Broadcast( SfxHintId::BasicDataWanted );
1361
        p1Type = p1->GetType();
1362
    }
1363
    if ( p2Type == SbxEMPTY )
1364
    {
1365
        p2->Broadcast( SfxHintId::BasicDataWanted );
1366
        p2Type = p2->GetType();
1367
    }
1368
    if ( p1Type == p2Type )
1369
    {
1370
        // if both sides are an object and have default props
1371
        // then we need to use the default props
1372
        // we don't need to worry if only one side ( lhs, rhs ) is an
1373
        // object ( object side will get coerced to correct type in
1374
        // Compare )
1375
        if ( p1Type ==  SbxOBJECT )
1376
        {
1377
            SbxVariable* pDflt = getDefaultProp( p1.get() );
1378
            if ( pDflt )
1379
            {
1380
                p1 = pDflt;
1381
                p1->Broadcast( SfxHintId::BasicDataWanted );
1382
            }
1383
            pDflt = getDefaultProp( p2.get() );
1384
            if ( pDflt )
1385
            {
1386
                p2 = pDflt;
1387
                p2->Broadcast( SfxHintId::BasicDataWanted );
1388
            }
1389
        }
1390
1391
    }
1392
    static SbxVariable* pTRUE = nullptr;
1393
    static SbxVariable* pFALSE = nullptr;
1394
    // why do this on non-windows ?
1395
    // why do this at all ?
1396
    // I dumbly follow the pattern :-/
1397
    if ( bVBAEnabled && ( p1->IsNull() || p2->IsNull() ) )
1398
    {
1399
        static SbxVariable* pNULL = []() {
1400
            SbxVariable* p = new SbxVariable;
1401
            p->PutNull();
1402
            p->AddFirstRef();
1403
            return p;
1404
        }();
1405
        PushVar( pNULL );
1406
    }
1407
    else if( p2->Compare( eOp, *p1 ) )
1408
    {
1409
        if( !pTRUE )
1410
        {
1411
            pTRUE = new SbxVariable;
1412
            pTRUE->PutBool( true );
1413
            pTRUE->AddFirstRef();
1414
        }
1415
        PushVar( pTRUE );
1416
    }
1417
    else
1418
    {
1419
        if( !pFALSE )
1420
        {
1421
            pFALSE = new SbxVariable;
1422
            pFALSE->PutBool( false );
1423
            pFALSE->AddFirstRef();
1424
        }
1425
        PushVar( pFALSE );
1426
    }
1427
}
1428
1429
void SbiRuntime::StepEXP()      { StepArith( SbxEXP );      }
1430
void SbiRuntime::StepMUL()      { StepArith( SbxMUL );      }
1431
void SbiRuntime::StepDIV()      { StepArith( SbxDIV );      }
1432
void SbiRuntime::StepIDIV()     { StepArith( SbxIDIV );     }
1433
void SbiRuntime::StepMOD()      { StepArith( SbxMOD );      }
1434
void SbiRuntime::StepPLUS()     { StepArith( SbxPLUS );     }
1435
void SbiRuntime::StepMINUS()        { StepArith( SbxMINUS );    }
1436
void SbiRuntime::StepCAT()      { StepArith( SbxCAT );      }
1437
void SbiRuntime::StepAND()      { StepArith( SbxAND );      }
1438
void SbiRuntime::StepOR()       { StepArith( SbxOR );       }
1439
void SbiRuntime::StepXOR()      { StepArith( SbxXOR );      }
1440
void SbiRuntime::StepEQV()      { StepArith( SbxEQV );      }
1441
void SbiRuntime::StepIMP()      { StepArith( SbxIMP );      }
1442
1443
void SbiRuntime::StepNEG()      { StepUnary( SbxNEG );      }
1444
void SbiRuntime::StepNOT()      { StepUnary( SbxNOT );      }
1445
1446
void SbiRuntime::StepEQ()       { StepCompare( SbxEQ );     }
1447
void SbiRuntime::StepNE()       { StepCompare( SbxNE );     }
1448
void SbiRuntime::StepLT()       { StepCompare( SbxLT );     }
1449
void SbiRuntime::StepGT()       { StepCompare( SbxGT );     }
1450
void SbiRuntime::StepLE()       { StepCompare( SbxLE );     }
1451
void SbiRuntime::StepGE()       { StepCompare( SbxGE );     }
1452
1453
namespace
1454
{
1455
    OUString VBALikeToRegexp(std::u16string_view sIn)
1456
    {
1457
        OUStringBuffer sResult("\\A"); // Match at the beginning of the input
1458
1459
        for (auto start = sIn.begin(), end = sIn.end(); start < end;)
1460
        {
1461
            switch (auto ch = *start++)
1462
            {
1463
            case '?':
1464
                sResult.append('.');
1465
                break;
1466
            case '*':
1467
                sResult.append(".*");
1468
                break;
1469
            case '#':
1470
                sResult.append("[0-9]");
1471
                break;
1472
            case '[':
1473
                sResult.append(ch);
1474
                if (start < end)
1475
                {
1476
                    if (*start == '!')
1477
                    {
1478
                        sResult.append('^');
1479
                        ++start;
1480
                    }
1481
                    else if (*start == '^')
1482
                        sResult.append('\\');
1483
                }
1484
                for (bool seenright = false; start < end && !seenright; ++start)
1485
                {
1486
                    switch (*start)
1487
                    {
1488
                    case '[':
1489
                    case '\\':
1490
                        sResult.append('\\');
1491
                        break;
1492
                    case ']':
1493
                        seenright = true;
1494
                        break;
1495
                    }
1496
                    sResult.append(*start);
1497
                }
1498
                break;
1499
            case '.':
1500
            case '^':
1501
            case '$':
1502
            case '+':
1503
            case '\\':
1504
            case '|':
1505
            case '{':
1506
            case '}':
1507
            case '(':
1508
            case ')':
1509
                sResult.append('\\');
1510
                [[fallthrough]];
1511
            default:
1512
                sResult.append(ch);
1513
            }
1514
        }
1515
1516
        sResult.append("\\z"); // Match if the current position is at the end of input
1517
1518
        return sResult.makeStringAndClear();
1519
    }
1520
}
1521
1522
void SbiRuntime::StepLIKE()
1523
{
1524
    SbxVariableRef refVar1 = PopVar();
1525
    SbxVariableRef refVar2 = PopVar();
1526
1527
    OUString value = refVar2->GetOUString();
1528
    OUString regex = VBALikeToRegexp(refVar1->GetOUString());
1529
1530
    bool bTextMode(true);
1531
    bool bCompatibility = ( GetSbData()->pInst && GetSbData()->pInst->IsCompatibility() );
1532
    if( bCompatibility )
1533
    {
1534
        bTextMode = IsImageFlag( SbiImageFlags::COMPARETEXT );
1535
    }
1536
    uint32_t searchFlags = UREGEX_UWORD | UREGEX_DOTALL; // Dot matches newline
1537
    if( bTextMode )
1538
    {
1539
        searchFlags |= UREGEX_CASE_INSENSITIVE;
1540
    }
1541
1542
    static uint32_t cachedSearchFlags = 0;
1543
    static OUString cachedRegex;
1544
    static std::optional<icu::RegexMatcher> oRegexMatcher;
1545
    UErrorCode nIcuErr = U_ZERO_ERROR;
1546
    if (regex != cachedRegex || searchFlags != cachedSearchFlags || !oRegexMatcher)
1547
    {
1548
        cachedRegex = regex;
1549
        cachedSearchFlags = searchFlags;
1550
        icu::UnicodeString sRegex(false, reinterpret_cast<const UChar*>(cachedRegex.getStr()),
1551
                                  cachedRegex.getLength());
1552
        oRegexMatcher.emplace(sRegex, cachedSearchFlags, nIcuErr);
1553
        if (U_FAILURE(nIcuErr))
1554
        {
1555
            // Bad regex; oRegexMatcher has no pattern - would crash in RegexMatcher::reset
1556
            oRegexMatcher.reset();
1557
            return Error(ERRCODE_BASIC_BAD_PATTERN);
1558
        }
1559
    }
1560
1561
    icu::UnicodeString sSource(false, reinterpret_cast<const UChar*>(value.getStr()),
1562
                               value.getLength());
1563
    oRegexMatcher->reset(sSource);
1564
1565
    bool bRes = oRegexMatcher->matches(nIcuErr);
1566
    if (nIcuErr)
1567
    {
1568
        Error(ERRCODE_BASIC_INTERNAL_ERROR);
1569
    }
1570
    SbxVariable* pRes = new SbxVariable;
1571
    pRes->PutBool( bRes );
1572
1573
    PushVar( pRes );
1574
}
1575
1576
// TOS and TOS-1 are both object variables and contain the same pointer
1577
1578
void SbiRuntime::StepIS()
1579
{
1580
    SbxVariableRef refVar1 = PopVar();
1581
    SbxVariableRef refVar2 = PopVar();
1582
1583
    SbxDataType eType1 = refVar1->GetType();
1584
    SbxDataType eType2 = refVar2->GetType();
1585
    if ( eType1 == SbxEMPTY )
1586
    {
1587
        refVar1->Broadcast( SfxHintId::BasicDataWanted );
1588
        eType1 = refVar1->GetType();
1589
    }
1590
    if ( eType2 == SbxEMPTY )
1591
    {
1592
        refVar2->Broadcast( SfxHintId::BasicDataWanted );
1593
        eType2 = refVar2->GetType();
1594
    }
1595
1596
    bool bRes = ( eType1 == SbxOBJECT && eType2 == SbxOBJECT );
1597
    if ( bVBAEnabled  && !bRes )
1598
    {
1599
        Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
1600
    }
1601
    bRes = ( bRes && refVar1->GetObject() == refVar2->GetObject() );
1602
    SbxVariable* pRes = new SbxVariable;
1603
    pRes->PutBool( bRes );
1604
    PushVar( pRes );
1605
}
1606
1607
// update the value of TOS
1608
1609
void SbiRuntime::StepGET()
1610
{
1611
    SbxVariable* p = GetTOS();
1612
    p->Broadcast( SfxHintId::BasicDataWanted );
1613
}
1614
1615
// #67607 copy Uno-Structs
1616
static bool checkUnoStructCopy( bool bVBA, SbxVariableRef const & refVal, SbxVariableRef const & refVar )
1617
{
1618
    SbxDataType eVarType = refVar->GetType();
1619
    SbxDataType eValType = refVal->GetType();
1620
1621
    // tdf#144353 - do not assign a missing optional variable to a property
1622
    if (refVal->GetType() == SbxERROR && SbiRuntime::IsMissing(refVal.get(), 1))
1623
    {
1624
        SbxBase::SetError(ERRCODE_BASIC_NOT_OPTIONAL);
1625
        return true;
1626
    }
1627
1628
    if ( ( bVBA && ( eVarType == SbxEMPTY ) ) || !refVar->CanWrite() )
1629
        return false;
1630
1631
    if ( eValType != SbxOBJECT )
1632
        return false;
1633
    // we seem to be duplicating parts of SbxValue=operator, maybe we should just move this to
1634
    // there :-/ not sure if for every '=' we would want struct handling
1635
    if( eVarType != SbxOBJECT )
1636
    {
1637
        if ( refVar->IsFixed() )
1638
            return false;
1639
    }
1640
    // #115826: Exclude ProcedureProperties to avoid call to Property Get procedure
1641
    else if( dynamic_cast<const SbProcedureProperty*>( refVar.get() ) != nullptr )
1642
        return false;
1643
1644
    SbxObjectRef xValObj = static_cast<SbxObject*>(refVal->GetObject());
1645
    if( !xValObj.is() || dynamic_cast<const SbUnoAnyObject*>( xValObj.get() ) != nullptr )
1646
        return false;
1647
1648
    SbUnoObject* pUnoVal =  dynamic_cast<SbUnoObject*>( xValObj.get() );
1649
    SbUnoStructRefObject* pUnoStructVal = dynamic_cast<SbUnoStructRefObject*>( xValObj.get() );
1650
    Any aAny;
1651
    // make doubly sure value is either a Uno object or
1652
    // a uno struct
1653
    if ( pUnoVal || pUnoStructVal )
1654
        aAny = pUnoVal ? pUnoVal->getUnoAny() : pUnoStructVal->getUnoAny();
1655
    else
1656
        return false;
1657
    if (  aAny.getValueTypeClass() != TypeClass_STRUCT )
1658
        return false;
1659
1660
    refVar->SetType( SbxOBJECT );
1661
    ErrCode eOldErr = SbxBase::GetError();
1662
    // There are some circumstances when calling GetObject
1663
    // will trigger an error, we need to squash those here.
1664
    // Alternatively it is possible that the same scenario
1665
    // could overwrite and existing error. Let's prevent that
1666
    SbxObjectRef xVarObj = static_cast<SbxObject*>(refVar->GetObject());
1667
    if ( eOldErr != ERRCODE_NONE )
1668
        SbxBase::SetError( eOldErr );
1669
    else
1670
        SbxBase::ResetError();
1671
1672
    SbUnoStructRefObject* pUnoStructObj = dynamic_cast<SbUnoStructRefObject*>( xVarObj.get() );
1673
1674
    OUString sClassName = pUnoVal ? pUnoVal->GetClassName() : pUnoStructVal->GetClassName();
1675
    OUString sName = pUnoVal ? pUnoVal->GetName() : pUnoStructVal->GetName();
1676
1677
    if ( pUnoStructObj )
1678
    {
1679
        StructRefInfo aInfo = pUnoStructObj->getStructInfo();
1680
        aInfo.setValue( aAny );
1681
    }
1682
    else
1683
    {
1684
        SbUnoObject* pNewUnoObj = new SbUnoObject( sName, aAny );
1685
        // #70324: adopt ClassName
1686
        pNewUnoObj->SetClassName( sClassName );
1687
        refVar->PutObject( pNewUnoObj );
1688
    }
1689
    return true;
1690
}
1691
1692
1693
// laying down TOS in TOS-1
1694
1695
void SbiRuntime::StepPUT()
1696
{
1697
    SbxVariableRef refVal = PopVar();
1698
    SbxVariableRef refVar = PopVar();
1699
    // store on its own method (inside a function)?
1700
    bool bFlagsChanged = false;
1701
    SbxFlagBits n = SbxFlagBits::NONE;
1702
    if( refVar.get() == pMeth )
1703
    {
1704
        bFlagsChanged = true;
1705
        n = refVar->GetFlags();
1706
        refVar->SetFlag( SbxFlagBits::Write );
1707
    }
1708
1709
    // if left side arg is an object or variant and right handside isn't
1710
    // either an object or a variant then try and see if a default
1711
    // property exists.
1712
    // to use e.g. Range{"A1") = 34
1713
    // could equate to Range("A1").Value = 34
1714
    if ( bVBAEnabled )
1715
    {
1716
        // yet more hacking at this, I feel we don't quite have the correct
1717
        // heuristics for dealing with obj1 = obj2 ( where obj2 ( and maybe
1718
        // obj1 ) has default member/property ) ) It seems that default props
1719
        // aren't dealt with if the object is a member of some parent object
1720
        bool bObjAssign = false;
1721
        if ( refVar->GetType() == SbxEMPTY )
1722
            refVar->Broadcast( SfxHintId::BasicDataWanted );
1723
        if ( refVar->GetType() == SbxOBJECT )
1724
        {
1725
            if  ( dynamic_cast<const SbxMethod *>(refVar.get()) != nullptr || ! refVar->GetParent() )
1726
            {
1727
                SbxVariable* pDflt = getDefaultProp( refVar.get() );
1728
1729
                if ( pDflt )
1730
                    refVar = pDflt;
1731
            }
1732
            else
1733
                bObjAssign = true;
1734
        }
1735
        if (  refVal->GetType() == SbxOBJECT  && !bObjAssign && ( dynamic_cast<const SbxMethod *>(refVal.get()) != nullptr || ! refVal->GetParent() ) )
1736
        {
1737
            SbxVariable* pDflt = getDefaultProp( refVal.get() );
1738
            if ( pDflt )
1739
                refVal = pDflt;
1740
        }
1741
    }
1742
1743
    if ( !checkUnoStructCopy( bVBAEnabled, refVal, refVar ) )
1744
        *refVar = *refVal;
1745
1746
    if( bFlagsChanged )
1747
        refVar->SetFlags( n );
1748
}
1749
1750
namespace {
1751
1752
// VBA Dim As New behavior handling, save init object information
1753
struct DimAsNewRecoverItem
1754
{
1755
    OUString        m_aObjClass;
1756
    OUString        m_aObjName;
1757
    SbxObject*      m_pObjParent;
1758
    SbModule*       m_pClassModule;
1759
1760
    DimAsNewRecoverItem()
1761
        : m_pObjParent( nullptr )
1762
        , m_pClassModule( nullptr )
1763
    {}
1764
1765
    DimAsNewRecoverItem( OUString aObjClass, OUString aObjName,
1766
                         SbxObject* pObjParent, SbModule* pClassModule )
1767
            : m_aObjClass(std::move( aObjClass ))
1768
            , m_aObjName(std::move( aObjName ))
1769
            , m_pObjParent( pObjParent )
1770
            , m_pClassModule( pClassModule )
1771
    {}
1772
1773
};
1774
1775
1776
struct SbxVariablePtrHash
1777
{
1778
    size_t operator()( SbxVariable* pVar ) const
1779
        { return reinterpret_cast<size_t>(pVar); }
1780
};
1781
1782
}
1783
1784
typedef std::unordered_map< SbxVariable*, DimAsNewRecoverItem,
1785
                              SbxVariablePtrHash >  DimAsNewRecoverHash;
1786
1787
namespace {
1788
1789
DimAsNewRecoverHash gaDimAsNewRecoverHash;
1790
1791
}
1792
1793
void removeDimAsNewRecoverItem( SbxVariable* pVar )
1794
{
1795
    DimAsNewRecoverHash::iterator it = gaDimAsNewRecoverHash.find( pVar );
1796
    if( it != gaDimAsNewRecoverHash.end() )
1797
    {
1798
        gaDimAsNewRecoverHash.erase( it );
1799
    }
1800
}
1801
1802
1803
// saving object variable
1804
// not-object variables will cause errors
1805
1806
constexpr OUString pCollectionStr = u"Collection"_ustr;
1807
1808
void SbiRuntime::StepSET_Impl( SbxVariableRef& refVal, SbxVariableRef& refVar, bool bHandleDefaultProp )
1809
{
1810
    // #67733 types with array-flag are OK too
1811
1812
    // Check var, !object is no error for sure if, only if type is fixed
1813
    SbxDataType eVarType = refVar->GetType();
1814
    if( !bHandleDefaultProp && eVarType != SbxOBJECT && !(eVarType & SbxARRAY) && refVar->IsFixed() )
1815
    {
1816
        Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
1817
        return;
1818
    }
1819
1820
    // Check value, !object is no error for sure if, only if type is fixed
1821
    SbxDataType eValType = refVal->GetType();
1822
    if( !bHandleDefaultProp && eValType != SbxOBJECT && !(eValType & SbxARRAY) && refVal->IsFixed() )
1823
    {
1824
        Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
1825
        return;
1826
    }
1827
1828
    // Getting in here causes problems with objects with default properties
1829
    // if they are SbxEMPTY I guess
1830
    if ( !bHandleDefaultProp || eValType == SbxOBJECT )
1831
    {
1832
    // activate GetObject for collections on refVal
1833
        SbxBase* pObjVarObj = refVal->GetObject();
1834
        if( pObjVarObj )
1835
        {
1836
            SbxVariableRef refObjVal = dynamic_cast<SbxObject*>( pObjVarObj );
1837
1838
            if( refObjVal.is() )
1839
            {
1840
                refVal = std::move(refObjVal);
1841
            }
1842
            else if( !(eValType & SbxARRAY) )
1843
            {
1844
                refVal = nullptr;
1845
            }
1846
        }
1847
    }
1848
1849
    // #52896 refVal can be invalid here, if uno-sequences - or more
1850
    // general arrays - are assigned to variables that are declared
1851
    // as an object!
1852
    if( !refVal.is() )
1853
    {
1854
        Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
1855
    }
1856
    else
1857
    {
1858
        bool bFlagsChanged = false;
1859
        SbxFlagBits n = SbxFlagBits::NONE;
1860
        if( refVar.get() == pMeth )
1861
        {
1862
            bFlagsChanged = true;
1863
            n = refVar->GetFlags();
1864
            refVar->SetFlag( SbxFlagBits::Write );
1865
        }
1866
        SbProcedureProperty* pProcProperty = dynamic_cast<SbProcedureProperty*>( refVar.get() );
1867
        if( pProcProperty )
1868
        {
1869
            pProcProperty->setSet( true );
1870
        }
1871
        if ( bHandleDefaultProp )
1872
        {
1873
            // get default properties for lhs & rhs where necessary
1874
            // SbxVariable* defaultProp = NULL; unused variable
1875
            // LHS try determine if a default prop exists
1876
            // again like in StepPUT (see there too ) we are tweaking the
1877
            // heuristics again for when to assign an object reference or
1878
            // use default members if they exist
1879
            // #FIXME we really need to get to the bottom of this mess
1880
            bool bObjAssign = false;
1881
            if ( refVar->GetType() == SbxOBJECT )
1882
            {
1883
                if ( dynamic_cast<const SbxMethod *>(refVar.get()) != nullptr || ! refVar->GetParent() )
1884
                {
1885
                    SbxVariable* pDflt = getDefaultProp( refVar.get() );
1886
                    if ( pDflt )
1887
                    {
1888
                        refVar = pDflt;
1889
                    }
1890
                }
1891
                else
1892
                    bObjAssign = true;
1893
            }
1894
            // RHS only get a default prop is the rhs has one
1895
            if (  refVal->GetType() == SbxOBJECT )
1896
            {
1897
                // check if lhs is a null object
1898
                // if it is then use the object not the default property
1899
                SbxObject* pObj = dynamic_cast<SbxObject*>( refVar.get() );
1900
1901
                // calling GetObject on a SbxEMPTY variable raises
1902
                // object not set errors, make sure it's an Object
1903
                if ( !pObj && refVar->GetType() == SbxOBJECT )
1904
                {
1905
                    SbxBase* pObjVarObj = refVar->GetObject();
1906
                    pObj = dynamic_cast<SbxObject*>( pObjVarObj );
1907
                }
1908
                SbxVariable* pDflt = nullptr;
1909
                if ( pObj && !bObjAssign )
1910
                {
1911
                    // lhs is either a valid object || or has a defaultProp
1912
                    pDflt = getDefaultProp( refVal.get() );
1913
                }
1914
                if ( pDflt )
1915
                {
1916
                    refVal = pDflt;
1917
                }
1918
            }
1919
        }
1920
1921
        // Handle Dim As New
1922
        bool bDimAsNew = bVBAEnabled && refVar->IsSet( SbxFlagBits::DimAsNew );
1923
        SbxBaseRef xPrevVarObj;
1924
        if( bDimAsNew )
1925
        {
1926
            xPrevVarObj = refVar->GetObject();
1927
        }
1928
        // Handle withevents
1929
        bool bWithEvents = refVar->IsSet( SbxFlagBits::WithEvents );
1930
        if ( bWithEvents )
1931
        {
1932
            Reference< XInterface > xComListener;
1933
1934
            SbxBase* pObj = refVal->GetObject();
1935
            SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pObj );
1936
            if( pUnoObj != nullptr )
1937
            {
1938
                Any aControlAny = pUnoObj->getUnoAny();
1939
                OUString aDeclareClassName = refVar->GetDeclareClassName();
1940
                OUString aPrefix = refVar->GetName();
1941
                SbxObjectRef xScopeObj = refVar->GetParent();
1942
                xComListener = createComListener( aControlAny, aDeclareClassName, aPrefix, xScopeObj );
1943
1944
                refVal->SetDeclareClassName( aDeclareClassName );
1945
                refVal->SetComListener( xComListener, &rBasic );        // Hold reference
1946
            }
1947
1948
        }
1949
1950
        // lhs is a property who's value is currently (Empty e.g. no broadcast yet)
1951
        // in this case if there is a default prop involved the value of the
1952
        // default property may in fact be void so the type will also be SbxEMPTY
1953
        // in this case we do not want to call checkUnoStructCopy 'cause that will
1954
        // cause an error also
1955
        if ( !checkUnoStructCopy( bHandleDefaultProp, refVal, refVar ) )
1956
        {
1957
            *refVar = *refVal;
1958
        }
1959
        if ( bDimAsNew )
1960
        {
1961
            if( dynamic_cast<const SbxObject*>( refVar.get() ) == nullptr )
1962
            {
1963
                SbxBase* pValObjBase = refVal->GetObject();
1964
                if( pValObjBase == nullptr )
1965
                {
1966
                    if( xPrevVarObj.is() )
1967
                    {
1968
                        // Object is overwritten with NULL, instantiate init object
1969
                        DimAsNewRecoverHash::iterator it = gaDimAsNewRecoverHash.find( refVar.get() );
1970
                        if( it != gaDimAsNewRecoverHash.end() )
1971
                        {
1972
                            const DimAsNewRecoverItem& rItem = it->second;
1973
                            if( rItem.m_pClassModule != nullptr )
1974
                            {
1975
                                SbClassModuleObject* pNewObj = new SbClassModuleObject(*rItem.m_pClassModule);
1976
                                pNewObj->SetName( rItem.m_aObjName );
1977
                                pNewObj->SetParent( rItem.m_pObjParent );
1978
                                refVar->PutObject( pNewObj );
1979
                            }
1980
                            else if( rItem.m_aObjClass.equalsIgnoreAsciiCase( pCollectionStr ) )
1981
                            {
1982
                                BasicCollection* pNewCollection = new BasicCollection( pCollectionStr );
1983
                                pNewCollection->SetName( rItem.m_aObjName );
1984
                                pNewCollection->SetParent( rItem.m_pObjParent );
1985
                                refVar->PutObject( pNewCollection );
1986
                            }
1987
                        }
1988
                    }
1989
                }
1990
                else
1991
                {
1992
                    // Does old value exist?
1993
                    bool bFirstInit = !xPrevVarObj.is();
1994
                    if( bFirstInit )
1995
                    {
1996
                        // Store information to instantiate object later
1997
                        SbxObject* pValObj = dynamic_cast<SbxObject*>( pValObjBase );
1998
                        if( pValObj != nullptr )
1999
                        {
2000
                            OUString aObjClass = pValObj->GetClassName();
2001
2002
                            SbClassModuleObject* pClassModuleObj = dynamic_cast<SbClassModuleObject*>( pValObjBase );
2003
                            if( pClassModuleObj != nullptr )
2004
                            {
2005
                                SbModule& rClassModule = pClassModuleObj->getClassModule();
2006
                                gaDimAsNewRecoverHash[refVar.get()] =
2007
                                    DimAsNewRecoverItem( aObjClass, pValObj->GetName(), pValObj->GetParent(), &rClassModule );
2008
                            }
2009
                            else if( aObjClass.equalsIgnoreAsciiCase( "Collection" ) )
2010
                            {
2011
                                gaDimAsNewRecoverHash[refVar.get()] =
2012
                                    DimAsNewRecoverItem( aObjClass, pValObj->GetName(), pValObj->GetParent(), nullptr );
2013
                            }
2014
                        }
2015
                    }
2016
                }
2017
            }
2018
        }
2019
2020
        if( bFlagsChanged )
2021
        {
2022
            refVar->SetFlags( n );
2023
        }
2024
    }
2025
}
2026
2027
void SbiRuntime::StepSET()
2028
{
2029
    SbxVariableRef refVal = PopVar();
2030
    SbxVariableRef refVar = PopVar();
2031
    StepSET_Impl( refVal, refVar, bVBAEnabled ); // this is really assignment
2032
}
2033
2034
void SbiRuntime::StepVBASET()
2035
{
2036
    SbxVariableRef refVal = PopVar();
2037
    SbxVariableRef refVar = PopVar();
2038
    // don't handle default property
2039
    StepSET_Impl( refVal, refVar ); // set obj = something
2040
}
2041
2042
2043
void SbiRuntime::StepLSET()
2044
{
2045
    SbxVariableRef refVal = PopVar();
2046
    SbxVariableRef refVar = PopVar();
2047
    if( refVar->GetType() != SbxSTRING ||
2048
        refVal->GetType() != SbxSTRING )
2049
    {
2050
        Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
2051
    }
2052
    else
2053
    {
2054
        SbxFlagBits n = refVar->GetFlags();
2055
        if( refVar.get() == pMeth )
2056
        {
2057
            refVar->SetFlag( SbxFlagBits::Write );
2058
        }
2059
        OUString aRefVarString = refVar->GetOUString();
2060
        OUString aRefValString = refVal->GetOUString();
2061
2062
        sal_Int32 nVarStrLen = aRefVarString.getLength();
2063
        sal_Int32 nValStrLen = aRefValString.getLength();
2064
        OUString aNewStr;
2065
        if( nVarStrLen > nValStrLen )
2066
        {
2067
            OUStringBuffer buf(aRefValString);
2068
            comphelper::string::padToLength(buf, nVarStrLen, ' ');
2069
            aNewStr = buf.makeStringAndClear();
2070
        }
2071
        else
2072
        {
2073
            aNewStr = aRefValString.copy( 0, nVarStrLen );
2074
        }
2075
2076
        refVar->PutString(aNewStr);
2077
        refVar->SetFlags( n );
2078
    }
2079
}
2080
2081
void SbiRuntime::StepRSET()
2082
{
2083
    SbxVariableRef refVal = PopVar();
2084
    SbxVariableRef refVar = PopVar();
2085
    if( refVar->GetType() != SbxSTRING || refVal->GetType() != SbxSTRING )
2086
    {
2087
        Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
2088
    }
2089
    else
2090
    {
2091
        SbxFlagBits n = refVar->GetFlags();
2092
        if( refVar.get() == pMeth )
2093
        {
2094
            refVar->SetFlag( SbxFlagBits::Write );
2095
        }
2096
        OUString aRefVarString = refVar->GetOUString();
2097
        OUString aRefValString = refVal->GetOUString();
2098
        sal_Int32 nVarStrLen = aRefVarString.getLength();
2099
        sal_Int32 nValStrLen = aRefValString.getLength();
2100
2101
        if (nVarStrLen > nValStrLen)
2102
        {
2103
            refVar->PutString(RepeatedUChar(' ', nVarStrLen - nValStrLen) + aRefValString);
2104
        }
2105
        else
2106
        {
2107
            refVar->PutString(aRefValString.copy(0, nVarStrLen));
2108
        }
2109
2110
        refVar->SetFlags( n );
2111
    }
2112
}
2113
2114
// laying down TOS in TOS-1, then set ReadOnly-Bit
2115
2116
void SbiRuntime::StepPUTC()
2117
{
2118
    SbxVariableRef refVal = PopVar();
2119
    SbxVariableRef refVar = PopVar();
2120
    refVar->SetFlag( SbxFlagBits::Write );
2121
    *refVar = *refVal;
2122
    refVar->ResetFlag( SbxFlagBits::Write );
2123
    refVar->SetFlag( SbxFlagBits::Const );
2124
}
2125
2126
// DIM
2127
// TOS = variable for the array with dimension information as parameter
2128
2129
void SbiRuntime::StepDIM()
2130
{
2131
    SbxVariableRef refVar = PopVar();
2132
    DimImpl( refVar );
2133
}
2134
2135
// #56204 swap out DIM-functionality into a help method (step0.cxx)
2136
void SbiRuntime::DimImpl(const SbxVariableRef& refVar)
2137
{
2138
    // If refDim then this DIM statement is terminating a ReDIM and
2139
    // previous StepERASE_CLEAR for an array, the following actions have
2140
    // been delayed from ( StepERASE_CLEAR ) 'till here
2141
    if ( refRedim.is() )
2142
    {
2143
        if ( !refRedimpArray.is() ) // only erase the array not ReDim Preserve
2144
        {
2145
            lcl_eraseImpl( refVar, bVBAEnabled );
2146
        }
2147
        SbxDataType eType = refVar->GetType();
2148
        lcl_clearImpl( refVar, eType );
2149
        refRedim = nullptr;
2150
    }
2151
    SbxArray* pDims = refVar->GetParameters();
2152
    // must have an even number of arguments
2153
    // have in mind that Arg[0] does not count!
2154
    if (pDims && !(pDims->Count() & 1))
2155
    {
2156
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
2157
    }
2158
    else
2159
    {
2160
        SbxDataType eType = refVar->IsFixed() ? refVar->GetType() : SbxVARIANT;
2161
        SbxDimArray* pArray = new SbxDimArray( eType );
2162
        // allow arrays without dimension information, too (VB-compatible)
2163
        if( pDims )
2164
        {
2165
            refVar->ResetFlag( SbxFlagBits::VarToDim );
2166
2167
            for (sal_uInt32 i = 1; i < pDims->Count();)
2168
            {
2169
                sal_Int32 lb = pDims->Get(i++)->GetLong();
2170
                sal_Int32 ub = pDims->Get(i++)->GetLong();
2171
                if( ub < lb )
2172
                {
2173
                    Error( ERRCODE_BASIC_OUT_OF_RANGE );
2174
                    ub = lb;
2175
                }
2176
                pArray->AddDim(lb, ub);
2177
                if ( lb != ub )
2178
                {
2179
                    pArray->setHasFixedSize( true );
2180
                }
2181
            }
2182
        }
2183
        else
2184
        {
2185
            // #62867 On creating an array of the length 0, create
2186
            // a dimension (like for Uno-Sequences of the length 0)
2187
            pArray->unoAddDim(0, -1);
2188
        }
2189
        SbxFlagBits nSavFlags = refVar->GetFlags();
2190
        refVar->ResetFlag( SbxFlagBits::Fixed );
2191
        refVar->PutObject( pArray );
2192
        refVar->SetFlags( nSavFlags );
2193
        refVar->SetParameters( nullptr );
2194
    }
2195
}
2196
2197
// REDIM
2198
// TOS  = variable for the array
2199
// argv = dimension information
2200
2201
void SbiRuntime::StepREDIM()
2202
{
2203
    // Nothing different than dim at the moment because
2204
    // a double dim is already recognized by the compiler.
2205
    StepDIM();
2206
}
2207
2208
2209
// Helper function for StepREDIMP and StepDCREATE_IMPL / bRedimp = true
2210
static void implCopyDimArray( SbxDimArray* pNewArray, SbxDimArray* pOldArray, sal_Int32 nMaxDimIndex,
2211
    sal_Int32 nActualDim, sal_Int32* pActualIndices, sal_Int32* pLowerBounds, sal_Int32* pUpperBounds )
2212
{
2213
    sal_Int32& ri = pActualIndices[nActualDim];
2214
    for( ri = pLowerBounds[nActualDim] ; ri <= pUpperBounds[nActualDim] ; ri++ )
2215
    {
2216
        if( nActualDim < nMaxDimIndex )
2217
        {
2218
            implCopyDimArray( pNewArray, pOldArray, nMaxDimIndex, nActualDim + 1,
2219
                pActualIndices, pLowerBounds, pUpperBounds );
2220
        }
2221
        else
2222
        {
2223
            SbxVariable* pSource = pOldArray->Get(pActualIndices);
2224
            if (pSource && pOldArray->GetRefCount() > 1)
2225
                // tdf#134692: old array will stay alive after the redim - we need to copy deep
2226
                pSource = new SbxVariable(*pSource);
2227
            pNewArray->Put(pSource, pActualIndices);
2228
        }
2229
    }
2230
}
2231
2232
// Returns true when actually restored
2233
static bool implRestorePreservedArray(SbxDimArray* pNewArray, SbxArrayRef& rrefRedimpArray, bool* pbWasError = nullptr)
2234
{
2235
    assert(pNewArray);
2236
    bool bResult = false;
2237
    if (pbWasError)
2238
        *pbWasError = false;
2239
    if (rrefRedimpArray)
2240
    {
2241
        SbxDimArray* pOldArray = static_cast<SbxDimArray*>(rrefRedimpArray.get());
2242
        const sal_Int32 nDimsNew = pNewArray->GetDims();
2243
        const sal_Int32 nDimsOld = pOldArray->GetDims();
2244
2245
        if (nDimsOld != nDimsNew)
2246
        {
2247
            StarBASIC::Error(ERRCODE_BASIC_OUT_OF_RANGE);
2248
            if (pbWasError)
2249
                *pbWasError = true;
2250
        }
2251
        else if (nDimsNew > 0)
2252
        {
2253
            // Store dims to use them for copying later
2254
            std::unique_ptr<sal_Int32[]> pLowerBounds(new sal_Int32[nDimsNew]);
2255
            std::unique_ptr<sal_Int32[]> pUpperBounds(new sal_Int32[nDimsNew]);
2256
            std::unique_ptr<sal_Int32[]> pActualIndices(new sal_Int32[nDimsNew]);
2257
            bool bNeedsPreallocation = true;
2258
2259
            // Compare bounds
2260
            for (sal_Int32 i = 1; i <= nDimsNew; i++)
2261
            {
2262
                sal_Int32 lBoundNew, uBoundNew;
2263
                sal_Int32 lBoundOld, uBoundOld;
2264
                pNewArray->GetDim(i, lBoundNew, uBoundNew);
2265
                pOldArray->GetDim(i, lBoundOld, uBoundOld);
2266
                lBoundNew = std::max(lBoundNew, lBoundOld);
2267
                uBoundNew = std::min(uBoundNew, uBoundOld);
2268
                sal_Int32 j = i - 1;
2269
                pActualIndices[j] = pLowerBounds[j] = lBoundNew;
2270
                pUpperBounds[j] = uBoundNew;
2271
                if (lBoundNew > uBoundNew) // No elements in the dimension -> no elements to restore
2272
                    bNeedsPreallocation = false;
2273
            }
2274
2275
            // Optimization: pre-allocate underlying container
2276
            if (bNeedsPreallocation)
2277
                pNewArray->Put(nullptr, pUpperBounds.get());
2278
2279
            // Copy data from old array by going recursively through all dimensions
2280
            // (It would be faster to work on the flat internal data array of an
2281
            // SbyArray but this solution is clearer and easier)
2282
            implCopyDimArray(pNewArray, pOldArray, nDimsNew - 1, 0, pActualIndices.get(),
2283
                             pLowerBounds.get(), pUpperBounds.get());
2284
            bResult = true;
2285
        }
2286
2287
        rrefRedimpArray.clear();
2288
    }
2289
    return bResult;
2290
}
2291
2292
// REDIM PRESERVE
2293
// TOS  = variable for the array
2294
// argv = dimension information
2295
2296
void SbiRuntime::StepREDIMP()
2297
{
2298
    SbxVariableRef refVar = PopVar();
2299
    DimImpl( refVar );
2300
2301
    // Now check, if we can copy from the old array
2302
    if( refRedimpArray.is() )
2303
    {
2304
        if (SbxDimArray* pNewArray = dynamic_cast<SbxDimArray*>(refVar->GetObject()))
2305
            implRestorePreservedArray(pNewArray, refRedimpArray);
2306
    }
2307
}
2308
2309
// REDIM_COPY
2310
// TOS  = Array-Variable, Reference to array is copied
2311
//        Variable is cleared as in ERASE
2312
2313
void SbiRuntime::StepREDIMP_ERASE()
2314
{
2315
    SbxVariableRef refVar = PopVar();
2316
    refRedim = refVar;
2317
    SbxDataType eType = refVar->GetType();
2318
    if( eType & SbxARRAY )
2319
    {
2320
        SbxBase* pElemObj = refVar->GetObject();
2321
        SbxDimArray* pDimArray = dynamic_cast<SbxDimArray*>( pElemObj );
2322
        if( pDimArray )
2323
        {
2324
            refRedimpArray = pDimArray;
2325
        }
2326
2327
    }
2328
    else if( refVar->IsFixed() )
2329
    {
2330
        refVar->Clear();
2331
    }
2332
    else
2333
    {
2334
        refVar->SetType( SbxEMPTY );
2335
    }
2336
}
2337
2338
static void lcl_clearImpl( SbxVariableRef const & refVar, SbxDataType const & eType )
2339
{
2340
    SbxFlagBits nSavFlags = refVar->GetFlags();
2341
    refVar->ResetFlag( SbxFlagBits::Fixed );
2342
    refVar->SetType( SbxDataType(eType & 0x0FFF) );
2343
    refVar->SetFlags( nSavFlags );
2344
    refVar->Clear();
2345
}
2346
2347
static void lcl_eraseImpl( SbxVariableRef const & refVar, bool bVBAEnabled )
2348
{
2349
    SbxDataType eType = refVar->GetType();
2350
    if( eType & SbxARRAY )
2351
    {
2352
        if ( bVBAEnabled )
2353
        {
2354
            SbxBase* pElemObj = refVar->GetObject();
2355
            SbxDimArray* pDimArray = dynamic_cast<SbxDimArray*>( pElemObj );
2356
            if( pDimArray )
2357
            {
2358
                if ( pDimArray->hasFixedSize() )
2359
                {
2360
                    // Clear all Value(s)
2361
                    pDimArray->SbxArray::Clear();
2362
                }
2363
                else
2364
                {
2365
                    pDimArray->Clear(); // clear dims and values
2366
                }
2367
            }
2368
            else
2369
            {
2370
                SbxArray* pArray = dynamic_cast<SbxArray*>( pElemObj );
2371
                if ( pArray )
2372
                {
2373
                    pArray->Clear();
2374
                }
2375
            }
2376
        }
2377
        else
2378
        {
2379
            // Arrays have on an erase to VB quite a complex behaviour. Here are
2380
            // only the type problems at REDIM (#26295) removed at first:
2381
            // Set type hard onto the array-type, because a variable with array is
2382
            // SbxOBJECT. At REDIM there's an SbxOBJECT-array generated then and
2383
            // the original type is lost -> runtime error
2384
            lcl_clearImpl( refVar, eType );
2385
        }
2386
    }
2387
    else if( refVar->IsFixed() )
2388
    {
2389
        refVar->Clear();
2390
    }
2391
    else
2392
    {
2393
        refVar->SetType( SbxEMPTY );
2394
    }
2395
}
2396
2397
// delete variable
2398
// TOS = variable
2399
2400
void SbiRuntime::StepERASE()
2401
{
2402
    SbxVariableRef refVar = PopVar();
2403
    lcl_eraseImpl( refVar, bVBAEnabled );
2404
}
2405
2406
void SbiRuntime::StepERASE_CLEAR()
2407
{
2408
    refRedim = PopVar();
2409
}
2410
2411
void SbiRuntime::StepARRAYACCESS()
2412
{
2413
    if( !refArgv.is() )
2414
    {
2415
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
2416
    }
2417
    SbxVariableRef refVar = PopVar();
2418
    refVar->SetParameters( refArgv.get() );
2419
    PopArgv();
2420
    PushVar( CheckArray( refVar.get() ) );
2421
}
2422
2423
void SbiRuntime::StepBYVAL()
2424
{
2425
    // Copy variable on stack to break call by reference
2426
    SbxVariableRef pVar = PopVar();
2427
    SbxDataType t = pVar->GetType();
2428
2429
    SbxVariable* pCopyVar = new SbxVariable( t );
2430
    pCopyVar->SetFlag( SbxFlagBits::ReadWrite );
2431
    *pCopyVar = *pVar;
2432
2433
    PushVar( pCopyVar );
2434
}
2435
2436
// establishing an argv
2437
// nOp1 stays as it is -> 1st element is the return value
2438
2439
void SbiRuntime::StepARGC()
2440
{
2441
    PushArgv();
2442
    refArgv = new SbxArray;
2443
    nArgc = 1;
2444
}
2445
2446
// storing an argument in Argv
2447
2448
void SbiRuntime::StepARGV()
2449
{
2450
    if( !refArgv.is() )
2451
    {
2452
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
2453
    }
2454
    else
2455
    {
2456
        SbxVariableRef pVal = PopVar();
2457
2458
        // Before fix of #94916:
2459
        if( dynamic_cast<const SbxMethod*>( pVal.get() ) != nullptr
2460
            || dynamic_cast<const SbUnoProperty*>( pVal.get() ) != nullptr
2461
            || dynamic_cast<const SbProcedureProperty*>( pVal.get() ) != nullptr )
2462
        {
2463
            // evaluate methods and properties!
2464
            SbxVariable* pRes = new SbxVariable( *pVal );
2465
            pVal = pRes;
2466
        }
2467
        refArgv->Put(pVal.get(), nArgc++);
2468
    }
2469
}
2470
2471
// Input to Variable. The variable is on TOS and is
2472
// is removed afterwards.
2473
void SbiRuntime::StepINPUT()
2474
{
2475
    OUStringBuffer sin;
2476
    char ch = 0;
2477
    ErrCode err;
2478
    // Skip whitespace
2479
    while( ( err = pIosys->GetError() ) == ERRCODE_NONE )
2480
    {
2481
        ch = pIosys->Read();
2482
        if( ch != ' ' && ch != '\t' && ch != '\n' )
2483
        {
2484
            break;
2485
        }
2486
    }
2487
    if( !err )
2488
    {
2489
        // Scan until comma or whitespace
2490
        char sep = ( ch == '"' ) ? ch : 0;
2491
        if( sep )
2492
        {
2493
            ch = pIosys->Read();
2494
        }
2495
        while( ( err = pIosys->GetError() ) == ERRCODE_NONE )
2496
        {
2497
            if( ch == sep )
2498
            {
2499
                ch = pIosys->Read();
2500
                if( ch != sep )
2501
                {
2502
                    break;
2503
                }
2504
            }
2505
            else if( !sep && (ch == ',' || ch == '\n') )
2506
            {
2507
                break;
2508
            }
2509
            sin.append( ch );
2510
            ch = pIosys->Read();
2511
        }
2512
        // skip whitespace
2513
        if( ch == ' ' || ch == '\t' )
2514
        {
2515
            while( ( err = pIosys->GetError() ) == ERRCODE_NONE )
2516
            {
2517
                if( ch != ' ' && ch != '\t' && ch != '\n' )
2518
                {
2519
                    break;
2520
                }
2521
                ch = pIosys->Read();
2522
            }
2523
        }
2524
    }
2525
    if( !err )
2526
    {
2527
        OUString s = sin.makeStringAndClear();
2528
        SbxVariableRef pVar = GetTOS();
2529
        // try to fill the variable with a numeric value first,
2530
        // then with a string value
2531
        if( !pVar->IsFixed() || pVar->IsNumeric() )
2532
        {
2533
            sal_Int32 nLen = 0;
2534
            if( !pVar->Scan( s, &nLen ) )
2535
            {
2536
                err = SbxBase::GetError();
2537
                SbxBase::ResetError();
2538
            }
2539
            // the value has to be scanned in completely
2540
            else if( nLen != s.getLength() && !pVar->PutString( s ) )
2541
            {
2542
                err = SbxBase::GetError();
2543
                SbxBase::ResetError();
2544
            }
2545
            else if( nLen != s.getLength() && pVar->IsNumeric() )
2546
            {
2547
                err = SbxBase::GetError();
2548
                SbxBase::ResetError();
2549
                if( !err )
2550
                {
2551
                    err = ERRCODE_BASIC_CONVERSION;
2552
                }
2553
            }
2554
        }
2555
        else
2556
        {
2557
            pVar->PutString( s );
2558
            err = SbxBase::GetError();
2559
            SbxBase::ResetError();
2560
        }
2561
    }
2562
    if( err == ERRCODE_BASIC_USER_ABORT )
2563
    {
2564
        Error( err );
2565
    }
2566
    else if( err )
2567
    {
2568
        if( pRestart && !pIosys->GetChannel() )
2569
        {
2570
            pCode = pRestart;
2571
        }
2572
        else
2573
        {
2574
            Error( err );
2575
        }
2576
    }
2577
    else
2578
    {
2579
        PopVar();
2580
    }
2581
}
2582
2583
// Line Input to Variable. The variable is on TOS and is
2584
// deleted afterwards.
2585
2586
void SbiRuntime::StepLINPUT()
2587
{
2588
    OString aInput;
2589
    pIosys->Read( aInput );
2590
    Error( pIosys->GetError() );
2591
    SbxVariableRef p = PopVar();
2592
    p->PutString(OStringToOUString(aInput, osl_getThreadTextEncoding()));
2593
}
2594
2595
// end of program
2596
2597
void SbiRuntime::StepSTOP()
2598
{
2599
    pInst->Stop();
2600
}
2601
2602
2603
void SbiRuntime::StepINITFOR()
2604
{
2605
    PushFor();
2606
}
2607
2608
void SbiRuntime::StepINITFOREACH()
2609
{
2610
    PushForEach();
2611
}
2612
2613
// increment FOR-variable
2614
2615
void SbiRuntime::StepNEXT()
2616
{
2617
    if( !pForStk )
2618
    {
2619
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
2620
        return;
2621
    }
2622
    if (pForStk->eForType != ForType::To)
2623
        return;
2624
    if (!pForStk->refVar)
2625
    {
2626
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
2627
        return;
2628
    }
2629
    // tdf#85371 - grant explicitly write access to the index variable
2630
    // since it could be the name of a method itself used in the next statement.
2631
    ScopedWritableGuard aGuard(pForStk->refVar, pForStk->refVar.get() == pMeth);
2632
    pForStk->refVar->Compute( SbxPLUS, *pForStk->refInc );
2633
}
2634
2635
// beginning CASE: TOS in CASE-stack
2636
2637
void SbiRuntime::StepCASE()
2638
{
2639
    if( !refCaseStk.is() )
2640
    {
2641
        refCaseStk = new SbxArray;
2642
    }
2643
    SbxVariableRef xVar = PopVar();
2644
    refCaseStk->Put(xVar.get(), refCaseStk->Count());
2645
}
2646
2647
// end CASE: free variable
2648
2649
void SbiRuntime::StepENDCASE()
2650
{
2651
    if (!refCaseStk.is() || !refCaseStk->Count())
2652
    {
2653
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
2654
    }
2655
    else
2656
    {
2657
        refCaseStk->Remove(refCaseStk->Count() - 1);
2658
    }
2659
}
2660
2661
2662
void SbiRuntime::StepSTDERROR()
2663
{
2664
    pError = nullptr; bError = true;
2665
    pInst->aErrorMsg.clear();
2666
    pInst->nErr = ERRCODE_NONE;
2667
    pInst->nErl = 0;
2668
    nError = ERRCODE_NONE;
2669
    SbxErrObject::getUnoErrObject()->Clear();
2670
}
2671
2672
void SbiRuntime::StepNOERROR()
2673
{
2674
    pInst->aErrorMsg.clear();
2675
    pInst->nErr = ERRCODE_NONE;
2676
    pInst->nErl = 0;
2677
    nError = ERRCODE_NONE;
2678
    SbxErrObject::getUnoErrObject()->Clear();
2679
    bError = false;
2680
}
2681
2682
// leave UP
2683
2684
void SbiRuntime::StepLEAVE()
2685
{
2686
    bRun = false;
2687
        // If VBA and we are leaving an ErrorHandler then clear the error ( it's been processed )
2688
    if ( bInError && pError )
2689
    {
2690
        SbxErrObject::getUnoErrObject()->Clear();
2691
    }
2692
}
2693
2694
void SbiRuntime::StepCHANNEL()      // TOS = channel number
2695
{
2696
    SbxVariableRef pChan = PopVar();
2697
    short nChan = pChan->GetInteger();
2698
    pIosys->SetChannel( nChan );
2699
    Error( pIosys->GetError() );
2700
}
2701
2702
void SbiRuntime::StepCHANNEL0()
2703
{
2704
    pIosys->ResetChannel();
2705
}
2706
2707
void SbiRuntime::StepPRINT()        // print TOS
2708
{
2709
    SbxVariableRef p = PopVar();
2710
    OUString s1 = p->GetOUString();
2711
    OUString s;
2712
    if( p->GetType() >= SbxINTEGER && p->GetType() <= SbxDOUBLE )
2713
    {
2714
        s = " ";    // one blank before
2715
    }
2716
    s += s1;
2717
    pIosys->Write( s );
2718
    Error( pIosys->GetError() );
2719
}
2720
2721
void SbiRuntime::StepPRINTF()       // print TOS in field
2722
{
2723
    SbxVariableRef p = PopVar();
2724
    OUString s1 = p->GetOUString();
2725
    OUStringBuffer s;
2726
    if( p->GetType() >= SbxINTEGER && p->GetType() <= SbxDOUBLE )
2727
    {
2728
        s.append(' ');
2729
    }
2730
    s.append(s1);
2731
    comphelper::string::padToLength(s, 14, ' ');
2732
    pIosys->Write( s );
2733
    Error( pIosys->GetError() );
2734
}
2735
2736
void SbiRuntime::StepWRITE()        // write TOS
2737
{
2738
    SbxVariableRef p = PopVar();
2739
    // Does the string have to be encapsulated?
2740
    char ch = 0;
2741
    switch (p->GetType() )
2742
    {
2743
    case SbxSTRING: ch = '"'; break;
2744
    case SbxCURRENCY:
2745
    case SbxBOOL:
2746
    case SbxDATE: ch = '#'; break;
2747
    default: break;
2748
    }
2749
    OUString s;
2750
    if( ch )
2751
    {
2752
        s += OUStringChar(ch);
2753
    }
2754
    s += p->GetOUString();
2755
    if( ch )
2756
    {
2757
        s += OUStringChar(ch);
2758
    }
2759
    pIosys->Write( s );
2760
    Error( pIosys->GetError() );
2761
}
2762
2763
void SbiRuntime::StepRENAME()       // Rename Tos+1 to Tos
2764
{
2765
    SbxVariableRef pTos1 = PopVar();
2766
    SbxVariableRef pTos  = PopVar();
2767
    OUString aDest = pTos1->GetOUString();
2768
    OUString aSource = pTos->GetOUString();
2769
2770
    if( hasUno() )
2771
    {
2772
        implStepRenameUCB( aSource, aDest );
2773
    }
2774
    else
2775
    {
2776
        implStepRenameOSL( aSource, aDest );
2777
    }
2778
}
2779
2780
// TOS = Prompt
2781
2782
void SbiRuntime::StepPROMPT()
2783
{
2784
    SbxVariableRef p = PopVar();
2785
    pIosys->SetPrompt(p->GetOUString());
2786
}
2787
2788
// Set Restart point
2789
2790
void SbiRuntime::StepRESTART()
2791
{
2792
    pRestart = pCode;
2793
}
2794
2795
// empty expression on stack for missing parameter
2796
2797
void SbiRuntime::StepEMPTY()
2798
{
2799
    // #57915 The semantics of StepEMPTY() is the representation of a missing argument.
2800
    // StepEmpty should now rather be named StepMISSING() but the name is kept to simplify matters.
2801
    SbxVariableRef xVar = new SbxVariable( SbxVARIANT );
2802
    // tdf#79426, tdf#125180 - add additional information about a missing parameter
2803
    SbxSetMissingParameter(*xVar);
2804
    PushVar( xVar.get() );
2805
}
2806
2807
// TOS = error code
2808
2809
void SbiRuntime::StepERROR()
2810
{
2811
    SbxVariableRef refCode = PopVar();
2812
    sal_uInt16 n = refCode->GetUShort();
2813
    ErrCode error = StarBASIC::GetSfxFromVBError( n );
2814
    if ( bVBAEnabled )
2815
    {
2816
        pInst->Error( error );
2817
    }
2818
    else
2819
    {
2820
        Error( error );
2821
    }
2822
}
2823
2824
// loading a numeric constant (+ID)
2825
2826
void SbiRuntime::StepLOADNC( sal_uInt32 nOp1 )
2827
{
2828
    // tdf#143707 - check if the data type character was added after the string termination symbol
2829
    SbxDataType eTypeStr;
2830
    // #57844 use localized function
2831
    OUString aStr = pImg->GetString(nOp1, &eTypeStr);
2832
    // also allow , !!!
2833
    sal_Int32 iComma = aStr.indexOf(',');
2834
    if( iComma >= 0 )
2835
    {
2836
        aStr = aStr.replaceAt(iComma, 1, u".");
2837
    }
2838
    sal_Int32 nParseEnd = 0;
2839
    rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
2840
    double n = ::rtl::math::stringToDouble( aStr, '.', ',', &eStatus, &nParseEnd );
2841
2842
    // tdf#131296 - retrieve data type put in SbiExprNode::Gen
2843
    SbxDataType eType = SbxDOUBLE;
2844
    if ( nParseEnd < aStr.getLength() )
2845
    {
2846
        // tdf#143707 - Check if there was a data type character after the numeric constant,
2847
        // added by older versions of the fix of the default values for strings.
2848
        switch ( aStr[nParseEnd] )
2849
        {
2850
            // See GetSuffixType in basic/source/comp/scanner.cxx for type characters
2851
            case '%': eType = SbxINTEGER; break;
2852
            case '&': eType = SbxLONG; break;
2853
            case '!': eType = SbxSINGLE; break;
2854
            case '@': eType = SbxCURRENCY; break;
2855
            // tdf#142460 - properly handle boolean values in string pool
2856
            case 'b': eType = SbxBOOL; break;
2857
        }
2858
    }
2859
    // tdf#143707 - if the data type character is different from the default value, it was added
2860
    // in basic/source/comp/symtbl.cxx. Hence, change the type of the numeric constant to be loaded.
2861
    else if (eTypeStr != SbxSTRING)
2862
    {
2863
        eType = eTypeStr;
2864
    }
2865
    SbxVariable* p = new SbxVariable( eType );
2866
    p->PutDouble( n );
2867
    // tdf#133913 - create variable with Variant/Type in order to prevent type conversion errors
2868
    p->ResetFlag( SbxFlagBits::Fixed );
2869
    PushVar( p );
2870
}
2871
2872
// loading a string constant (+ID)
2873
2874
void SbiRuntime::StepLOADSC( sal_uInt32 nOp1 )
2875
{
2876
    SbxVariable* p = new SbxVariable;
2877
    p->PutString( pImg->GetString( nOp1 ) );
2878
    PushVar( p );
2879
}
2880
2881
// Immediate Load (+value)
2882
// The opcode is not generated in SbiExprNode::Gen anymore; used for legacy images
2883
2884
void SbiRuntime::StepLOADI( sal_uInt32 nOp1 )
2885
{
2886
    SbxVariable* p = new SbxVariable;
2887
    p->PutInteger( static_cast<sal_Int16>( nOp1 ) );
2888
    PushVar( p );
2889
}
2890
2891
// store a named argument in Argv (+Arg-no. from 1!)
2892
2893
void SbiRuntime::StepARGN( sal_uInt32 nOp1 )
2894
{
2895
    if( !refArgv.is() )
2896
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
2897
    else
2898
    {
2899
        OUString aAlias( pImg->GetString( nOp1 ) );
2900
        SbxVariableRef pVal = PopVar();
2901
        if( bVBAEnabled &&
2902
                ( dynamic_cast<const SbxMethod*>( pVal.get()) != nullptr
2903
                  || dynamic_cast<const SbUnoProperty*>( pVal.get()) != nullptr
2904
                  || dynamic_cast<const SbProcedureProperty*>( pVal.get()) != nullptr ) )
2905
        {
2906
            // named variables ( that are Any especially properties ) can be empty at this point and need a broadcast
2907
            if ( pVal->GetType() == SbxEMPTY )
2908
                pVal->Broadcast( SfxHintId::BasicDataWanted );
2909
            // evaluate methods and properties!
2910
            SbxVariable* pRes = new SbxVariable( *pVal );
2911
            pVal = pRes;
2912
        }
2913
        refArgv->Put(pVal.get(), nArgc);
2914
        refArgv->PutAlias(aAlias, nArgc++);
2915
    }
2916
}
2917
2918
// converting the type of an argument in Argv for DECLARE-Fkt. (+type)
2919
2920
void SbiRuntime::StepARGTYP( sal_uInt32 nOp1 )
2921
{
2922
    if( !refArgv.is() )
2923
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
2924
    else
2925
    {
2926
        bool bByVal = (nOp1 & 0x8000) != 0;         // Is BYVAL requested?
2927
        SbxDataType t = static_cast<SbxDataType>(nOp1 & 0x7FFF);
2928
        SbxVariable* pVar = refArgv->Get(refArgv->Count() - 1); // last Arg
2929
2930
        // check BYVAL
2931
        if( pVar->GetRefCount() > 2 )       // 2 is normal for BYVAL
2932
        {
2933
            // parameter is a reference
2934
            if( bByVal )
2935
            {
2936
                // Call by Value is requested -> create a copy
2937
                pVar = new SbxVariable( *pVar );
2938
                pVar->SetFlag( SbxFlagBits::ReadWrite );
2939
                refExprStk->Put(pVar, refArgv->Count() - 1);
2940
            }
2941
            else
2942
                pVar->SetFlag( SbxFlagBits::Reference );     // Ref-Flag for DllMgr
2943
        }
2944
        else
2945
        {
2946
            // parameter is NO reference
2947
            if( bByVal )
2948
                pVar->ResetFlag( SbxFlagBits::Reference );   // no reference -> OK
2949
            else
2950
                Error( ERRCODE_BASIC_BAD_PARAMETERS );      // reference needed
2951
        }
2952
2953
        if( pVar->GetType() != t )
2954
        {
2955
            // variant for correct conversion
2956
            // besides error, if SbxBYREF
2957
            pVar->Convert( SbxVARIANT );
2958
            pVar->Convert( t );
2959
        }
2960
    }
2961
}
2962
2963
// bring string to a definite length (+length)
2964
2965
void SbiRuntime::StepPAD( sal_uInt32 nOp1 )
2966
{
2967
    SbxVariable* p = GetTOS();
2968
    OUString s = p->GetOUString();
2969
    sal_Int32 nLen(nOp1);
2970
    if( s.getLength() == nLen )
2971
        return;
2972
2973
    OUStringBuffer aBuf(s);
2974
    if (aBuf.getLength() > nLen)
2975
    {
2976
        comphelper::string::truncateToLength(aBuf, nLen);
2977
    }
2978
    else
2979
    {
2980
        comphelper::string::padToLength(aBuf, nLen, ' ');
2981
    }
2982
    s = aBuf.makeStringAndClear();
2983
    // Do not modify the original variable inadvertently
2984
    PopVar();
2985
    p = new SbxVariable;
2986
    p->PutString(s);
2987
    PushVar(p);
2988
}
2989
2990
// jump (+target)
2991
2992
void SbiRuntime::StepJUMP( sal_uInt32 nOp1 )
2993
{
2994
#ifdef DBG_UTIL
2995
    // #QUESTION shouldn't this be
2996
    // if( (sal_uInt8*)( nOp1+pImagGetCode() ) >= pImg->GetCodeSize() )
2997
    if( nOp1 >= pImg->GetCodeSize() )
2998
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
2999
#endif
3000
    pCode = pImg->GetCode() + nOp1;
3001
}
3002
3003
bool SbiRuntime::EvaluateTopOfStackAsBool()
3004
{
3005
    SbxVariableRef tos = PopVar();
3006
    // In a test e.g. If Null then
3007
        // will evaluate Null will act as if False
3008
    if ( bVBAEnabled && tos->IsNull() )
3009
    {
3010
        return false;
3011
    }
3012
3013
    // tdf#151503 - do not evaluate a missing optional variable to a boolean
3014
    if (tos->GetType() == SbxERROR && IsMissing(tos.get(), 1))
3015
    {
3016
        Error(ERRCODE_BASIC_NOT_OPTIONAL);
3017
        return false;
3018
    }
3019
3020
    if ( tos->IsObject() )
3021
    {
3022
        //GetBool applied to an Object attempts to dereference and evaluate
3023
            //the underlying value as Bool. Here, we're checking rather that
3024
            //it is not null
3025
        return tos->GetObject();
3026
    }
3027
    else
3028
    {
3029
        return tos->GetBool();
3030
    }
3031
}
3032
3033
// evaluate TOS, conditional jump (+target)
3034
3035
void SbiRuntime::StepJUMPT( sal_uInt32 nOp1 )
3036
{
3037
    if ( EvaluateTopOfStackAsBool() )
3038
    {
3039
        StepJUMP( nOp1 );
3040
    }
3041
}
3042
3043
// evaluate TOS, conditional jump (+target)
3044
3045
void SbiRuntime::StepJUMPF( sal_uInt32 nOp1 )
3046
{
3047
    if ( !EvaluateTopOfStackAsBool() )
3048
    {
3049
        StepJUMP( nOp1 );
3050
    }
3051
}
3052
3053
// evaluate TOS, jump into JUMP-table (+MaxVal)
3054
// looks like this:
3055
// ONJUMP 2
3056
// JUMP target1
3057
// JUMP target2
3058
3059
// if 0x8000 is set in the operand, push the return address (ON..GOSUB)
3060
3061
void SbiRuntime::StepONJUMP( sal_uInt32 nOp1 )
3062
{
3063
    SbxVariableRef p = PopVar();
3064
    sal_Int16 n = p->GetInteger();
3065
3066
    sal_uInt32 nMax = nOp1 & 0x7FFF;
3067
3068
    // tdf#160321 - do not execute the jump statement if the expression is out of range
3069
    if (n < 1 || o3tl::make_unsigned(n) > nMax)
3070
        n = static_cast<sal_Int16>(nMax + 1);
3071
    // tdf#173360 - only push a return address for a valid On...GoSub target
3072
    else if (nOp1 & 0x8000)
3073
        PushGosub(pCode + 5 * nMax);
3074
3075
    nOp1 = static_cast<sal_uInt32>(pCode - pImg->GetCode()) + 5 * --n;
3076
    StepJUMP( nOp1 );
3077
}
3078
3079
// UP-call (+target)
3080
3081
void SbiRuntime::StepGOSUB( sal_uInt32 nOp1 )
3082
{
3083
    PushGosub( pCode );
3084
    if( nOp1 >= pImg->GetCodeSize() )
3085
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
3086
    pCode = pImg->GetCode() + nOp1;
3087
}
3088
3089
// UP-return (+0 or target)
3090
3091
void SbiRuntime::StepRETURN( sal_uInt32 nOp1 )
3092
{
3093
    PopGosub();
3094
    if( nOp1 )
3095
        StepJUMP( nOp1 );
3096
}
3097
3098
// check FOR-variable (+Endlabel)
3099
3100
void SbiRuntime::StepTESTFOR( sal_uInt32 nOp1 )
3101
{
3102
    if( !pForStk )
3103
    {
3104
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
3105
        return;
3106
    }
3107
3108
    bool bEndLoop = false;
3109
    switch( pForStk->eForType )
3110
    {
3111
        case ForType::To:
3112
        {
3113
            SbxOperator eOp = ( pForStk->refInc->GetDouble() < 0 ) ? SbxLT : SbxGT;
3114
            if( pForStk->refVar->Compare( eOp, *pForStk->refEnd ) )
3115
                bEndLoop = true;
3116
            if (SbxBase::IsError())
3117
                pForStk->eForType = ForType::Error; // terminate loop at the next iteration
3118
            break;
3119
        }
3120
        case ForType::EachArray:
3121
        {
3122
            SbiForStack* p = pForStk;
3123
            if (!p->refEnd)
3124
            {
3125
                SbxBase::SetError(ERRCODE_BASIC_CONVERSION);
3126
                pForStk->eForType = ForType::Error; // terminate loop at the next iteration
3127
            }
3128
            else if (p->pArrayCurIndices == nullptr)
3129
            {
3130
                bEndLoop = true;
3131
            }
3132
            else
3133
            {
3134
                SbxDimArray* pArray = reinterpret_cast<SbxDimArray*>(p->refEnd.get());
3135
                sal_Int32 nDims = pArray->GetDims();
3136
3137
                // Empty array?
3138
                if( nDims == 1 && p->pArrayLowerBounds[0] > p->pArrayUpperBounds[0] )
3139
                {
3140
                    bEndLoop = true;
3141
                    break;
3142
                }
3143
                SbxVariable* pVal = pArray->Get(p->pArrayCurIndices.get());
3144
                *(p->refVar) = *pVal;
3145
3146
                bool bFoundNext = false;
3147
                for(sal_Int32 i = 0 ; i < nDims ; i++ )
3148
                {
3149
                    if( p->pArrayCurIndices[i] < p->pArrayUpperBounds[i] )
3150
                    {
3151
                        bFoundNext = true;
3152
                        p->pArrayCurIndices[i]++;
3153
                        for( sal_Int32 j = i - 1 ; j >= 0 ; j-- )
3154
                            p->pArrayCurIndices[j] = p->pArrayLowerBounds[j];
3155
                        break;
3156
                    }
3157
                }
3158
                if( !bFoundNext )
3159
                {
3160
                    p->pArrayCurIndices.reset();
3161
                }
3162
            }
3163
            break;
3164
        }
3165
        case ForType::EachCollection:
3166
        {
3167
            if (!pForStk->refEnd)
3168
            {
3169
                SbxBase::SetError(ERRCODE_BASIC_CONVERSION);
3170
                pForStk->eForType = ForType::Error; // terminate loop at the next iteration
3171
                break;
3172
            }
3173
3174
            BasicCollection* pCollection = static_cast<BasicCollection*>(pForStk->refEnd.get());
3175
            SbxArrayRef xItemArray = pCollection->xItemArray;
3176
            sal_Int32 nCount = xItemArray->Count();
3177
            if( pForStk->nCurCollectionIndex < nCount )
3178
            {
3179
                SbxVariable* pRes = xItemArray->Get(pForStk->nCurCollectionIndex);
3180
                pForStk->nCurCollectionIndex++;
3181
                (*pForStk->refVar) = *pRes;
3182
            }
3183
            else
3184
            {
3185
                bEndLoop = true;
3186
            }
3187
            break;
3188
        }
3189
        case ForType::EachXEnumeration:
3190
        {
3191
            SbiForStack* p = pForStk;
3192
            if (!p->xEnumeration)
3193
            {
3194
                SbxBase::SetError(ERRCODE_BASIC_CONVERSION);
3195
                pForStk->eForType = ForType::Error; // terminate loop at the next iteration
3196
            }
3197
            else if (p->xEnumeration->hasMoreElements())
3198
            {
3199
                Any aElem = p->xEnumeration->nextElement();
3200
                SbxVariableRef xVar = new SbxVariable( SbxVARIANT );
3201
                unoToSbxValue( xVar.get(), aElem );
3202
                (*pForStk->refVar) = *xVar;
3203
            }
3204
            else
3205
            {
3206
                bEndLoop = true;
3207
            }
3208
            break;
3209
        }
3210
        // tdf#130307 - support for each loop for objects exposing XIndexAccess
3211
        case ForType::EachXIndexAccess:
3212
        {
3213
            SbiForStack* p = pForStk;
3214
            if (!p->xIndexAccess)
3215
            {
3216
                SbxBase::SetError(ERRCODE_BASIC_CONVERSION);
3217
                pForStk->eForType = ForType::Error; // terminate loop at the next iteration
3218
            }
3219
            else if (pForStk->nCurCollectionIndex < p->xIndexAccess->getCount())
3220
            {
3221
                Any aElem = p->xIndexAccess->getByIndex(pForStk->nCurCollectionIndex);
3222
                pForStk->nCurCollectionIndex++;
3223
                SbxVariableRef xVar = new SbxVariable(SbxVARIANT);
3224
                unoToSbxValue(xVar.get(), aElem);
3225
                (*pForStk->refVar) = *xVar;
3226
            }
3227
            else
3228
            {
3229
                bEndLoop = true;
3230
            }
3231
            break;
3232
        }
3233
        case ForType::Error:
3234
        {
3235
            // We are in Resume Next mode after failed loop initialization
3236
            bEndLoop = true;
3237
            Error(ERRCODE_BASIC_BAD_PARAMETER);
3238
            break;
3239
        }
3240
    }
3241
    if( bEndLoop )
3242
    {
3243
        PopFor();
3244
        StepJUMP( nOp1 );
3245
    }
3246
}
3247
3248
// Tos+1 <= Tos+2 <= Tos, 2xremove (+Target)
3249
3250
void SbiRuntime::StepCASETO( sal_uInt32 nOp1 )
3251
{
3252
    if (!refCaseStk.is() || !refCaseStk->Count())
3253
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
3254
    else
3255
    {
3256
        SbxVariableRef xTo   = PopVar();
3257
        SbxVariableRef xFrom = PopVar();
3258
        SbxVariableRef xCase = refCaseStk->Get(refCaseStk->Count() - 1);
3259
        if( *xCase >= *xFrom && *xCase <= *xTo )
3260
            StepJUMP( nOp1 );
3261
    }
3262
}
3263
3264
3265
void SbiRuntime::StepERRHDL( sal_uInt32 nOp1 )
3266
{
3267
    const sal_uInt8* p = pCode;
3268
    StepJUMP( nOp1 );
3269
    pError = pCode;
3270
    pCode = p;
3271
    pInst->aErrorMsg.clear();
3272
    pInst->nErr = ERRCODE_NONE;
3273
    pInst->nErl = 0;
3274
    nError = ERRCODE_NONE;
3275
    SbxErrObject::getUnoErrObject()->Clear();
3276
}
3277
3278
// Resume after errors (+0=statement, 1=next or Label)
3279
3280
void SbiRuntime::StepRESUME( sal_uInt32 nOp1 )
3281
{
3282
    // #32714 Resume without error? -> error
3283
    if( !bInError )
3284
    {
3285
        Error( ERRCODE_BASIC_BAD_RESUME );
3286
        return;
3287
    }
3288
    if( nOp1 )
3289
    {
3290
        // set Code-pointer to the next statement
3291
        sal_uInt16 n1, n2;
3292
        pCode = pMod->FindNextStmnt( pErrCode, n1, n2, true, pImg );
3293
    }
3294
    else
3295
        pCode = pErrStmnt;
3296
    if ( pError ) // current in error handler ( and got a Resume Next statement )
3297
        SbxErrObject::getUnoErrObject()->Clear();
3298
3299
    if( nOp1 > 1 )
3300
        StepJUMP( nOp1 );
3301
    pInst->aErrorMsg.clear();
3302
    pInst->nErr = ERRCODE_NONE;
3303
    pInst->nErl = 0;
3304
    nError = ERRCODE_NONE;
3305
    bInError = false;
3306
}
3307
3308
// close channel (+channel, 0=all)
3309
void SbiRuntime::StepCLOSE( sal_uInt32 nOp1 )
3310
{
3311
    ErrCode err;
3312
    if( !nOp1 )
3313
        pIosys->Shutdown();
3314
    else
3315
    {
3316
        err = pIosys->GetError();
3317
        if( !err )
3318
        {
3319
            pIosys->Close();
3320
        }
3321
    }
3322
    err = pIosys->GetError();
3323
    Error( err );
3324
}
3325
3326
// output character (+char)
3327
3328
void SbiRuntime::StepPRCHAR( sal_uInt32 nOp1 )
3329
{
3330
    OUString s(static_cast<sal_Unicode>(nOp1));
3331
    pIosys->Write( s );
3332
    Error( pIosys->GetError() );
3333
}
3334
3335
// check whether TOS is a certain object class (+StringID)
3336
3337
bool SbiRuntime::implIsClass( SbxObject const * pObj, const OUString& aClass )
3338
{
3339
    bool bRet = true;
3340
3341
    if( !aClass.isEmpty() )
3342
    {
3343
        bRet = pObj->IsClass( aClass );
3344
        if( !bRet )
3345
            bRet = aClass.equalsIgnoreAsciiCase( "object" );
3346
        if( !bRet )
3347
        {
3348
            const OUString& aObjClass = pObj->GetClassName();
3349
            SbModule* pClassMod = GetSbData()->pClassFac->FindClass( aObjClass );
3350
            if( pClassMod )
3351
            {
3352
                SbClassData* pClassData = pClassMod->pClassData.get();
3353
                if (pClassData != nullptr )
3354
                {
3355
                    SbxVariable* pClassVar = pClassData->mxIfaces->Find( aClass, SbxClassType::DontCare );
3356
                    bRet = (pClassVar != nullptr);
3357
                }
3358
            }
3359
        }
3360
    }
3361
    return bRet;
3362
}
3363
3364
bool SbiRuntime::checkClass_Impl( const SbxVariableRef& refVal,
3365
    const OUString& aClass, bool bRaiseErrors, bool bDefault )
3366
{
3367
    bool bOk = bDefault;
3368
3369
    SbxDataType t = refVal->GetType();
3370
    SbxVariable* pVal = refVal.get();
3371
    // we don't know the type of uno properties that are (maybevoid)
3372
    if ( t == SbxEMPTY )
3373
    {
3374
        if ( auto pProp = dynamic_cast<SbUnoProperty*>( refVal.get() ) )
3375
        {
3376
            t = pProp->getRealType();
3377
        }
3378
    }
3379
    if( t == SbxOBJECT || bVBAEnabled )
3380
    {
3381
        SbxObject* pObj = dynamic_cast<SbxObject*>(pVal);
3382
        if (!pObj)
3383
        {
3384
            pObj = dynamic_cast<SbxObject*>(refVal->GetObject());
3385
        }
3386
        if( pObj )
3387
        {
3388
            if( !implIsClass( pObj, aClass ) )
3389
            {
3390
                SbUnoObject* pUnoObj(nullptr);
3391
                if (bVBAEnabled || CodeCompleteOptions::IsExtendedTypeDeclaration())
3392
                {
3393
                    pUnoObj = dynamic_cast<SbUnoObject*>(pObj);
3394
                }
3395
3396
                if (pUnoObj)
3397
                    bOk = checkUnoObjectType(*pUnoObj, aClass);
3398
                else
3399
                    bOk = false;
3400
                if ( !bOk && bRaiseErrors )
3401
                    Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
3402
            }
3403
            else
3404
            {
3405
                bOk = true;
3406
3407
                SbClassModuleObject* pClassModuleObject = dynamic_cast<SbClassModuleObject*>( pObj );
3408
                if( pClassModuleObject != nullptr )
3409
                    pClassModuleObject->triggerInitializeEvent();
3410
            }
3411
        }
3412
    }
3413
    else
3414
    {
3415
        if( bRaiseErrors )
3416
            Error( ERRCODE_BASIC_NEEDS_OBJECT );
3417
        bOk = false;
3418
    }
3419
    return bOk;
3420
}
3421
3422
void SbiRuntime::StepSETCLASS_impl( sal_uInt32 nOp1, bool bHandleDflt )
3423
{
3424
    SbxVariableRef refVal = PopVar();
3425
    SbxVariableRef refVar = PopVar();
3426
    OUString aClass( pImg->GetString( nOp1 ) );
3427
3428
    bool bOk = checkClass_Impl( refVal, aClass, true, true );
3429
    if( bOk )
3430
    {
3431
        StepSET_Impl( refVal, refVar, bHandleDflt ); // don't do handle default prop for a "proper" set
3432
    }
3433
}
3434
3435
void SbiRuntime::StepVBASETCLASS( sal_uInt32 nOp1 )
3436
{
3437
    StepSETCLASS_impl( nOp1, false );
3438
}
3439
3440
void SbiRuntime::StepSETCLASS( sal_uInt32 nOp1 )
3441
{
3442
    StepSETCLASS_impl( nOp1, true );
3443
}
3444
3445
void SbiRuntime::StepTESTCLASS( sal_uInt32 nOp1 )
3446
{
3447
    SbxVariableRef xObjVal = PopVar();
3448
    OUString aClass( pImg->GetString( nOp1 ) );
3449
    bool bDefault = !bVBAEnabled;
3450
    bool bOk = checkClass_Impl( xObjVal, aClass, false, bDefault );
3451
3452
    SbxVariable* pRet = new SbxVariable;
3453
    pRet->PutBool( bOk );
3454
    PushVar( pRet );
3455
}
3456
3457
// define library for following declare-call
3458
3459
void SbiRuntime::StepLIB( sal_uInt32 nOp1 )
3460
{
3461
    aLibName = pImg->GetString( nOp1 );
3462
}
3463
3464
// TOS is incremented by BASE, BASE is pushed before (+BASE)
3465
// This opcode is pushed before DIM/REDIM-commands,
3466
// if there's been only one index named.
3467
3468
void SbiRuntime::StepBASED( sal_uInt32 nOp1 )
3469
{
3470
    SbxVariable* p1 = new SbxVariable;
3471
    SbxVariableRef x2 = PopVar();
3472
3473
    // #109275 Check compatibility mode
3474
    bool bCompatible = ((nOp1 & 0x8000) != 0);
3475
    sal_uInt16 uBase = static_cast<sal_uInt16>(nOp1 & 1);       // Can only be 0 or 1
3476
    p1->PutInteger( uBase );
3477
    if( !bCompatible )
3478
    {
3479
        // tdf#85371 - grant explicitly write access to the dimension variable
3480
        // since in Star/OpenOffice Basic the upper index border is affected,
3481
        // and the dimension variable could be the name of the method itself.
3482
        ScopedWritableGuard aGuard(x2, x2.get() == pMeth);
3483
        x2->Compute( SbxPLUS, *p1 );
3484
    }
3485
    PushVar( x2.get() );  // first the Expr
3486
    PushVar( p1 );  // then the Base
3487
}
3488
3489
// the bits in the String-ID:
3490
// 0x8000 - Argv is reserved
3491
3492
SbxVariable* SbiRuntime::FindElement( SbxObject* pObj, sal_uInt32 nOp1, sal_uInt32 nOp2,
3493
                                      ErrCode nNotFound, bool bLocal, bool bStatic )
3494
{
3495
    bool bIsVBAInterOp = SbiRuntime::isVBAEnabled();
3496
    if( bIsVBAInterOp )
3497
    {
3498
        StarBASIC* pMSOMacroRuntimeLib = GetSbData()->pMSOMacroRuntimLib;
3499
        if( pMSOMacroRuntimeLib != nullptr )
3500
        {
3501
            pMSOMacroRuntimeLib->ResetFlag( SbxFlagBits::ExtSearch );
3502
        }
3503
    }
3504
3505
    SbxVariable* pElem = nullptr;
3506
    if( !pObj )
3507
    {
3508
        Error( ERRCODE_BASIC_NO_OBJECT );
3509
        pElem = new SbxVariable;
3510
    }
3511
    else
3512
    {
3513
        bool bFatalError = false;
3514
        SbxDataType t = static_cast<SbxDataType>(nOp2);
3515
        OUString aName( pImg->GetString( nOp1 & 0x7FFF ) );
3516
        // Hacky capture of Evaluate [] syntax
3517
        // this should be tackled I feel at the pcode level
3518
        if ( bIsVBAInterOp && aName.startsWith("[") )
3519
        {
3520
            // emulate pcode here
3521
            StepARGC();
3522
            // pseudo StepLOADSC
3523
            OUString sArg = aName.copy( 1, aName.getLength() - 2 );
3524
            SbxVariable* p = new SbxVariable;
3525
            p->PutString( sArg );
3526
            PushVar( p );
3527
            StepARGV();
3528
            nOp1 = nOp1 | 0x8000; // indicate params are present
3529
            aName = "Evaluate";
3530
        }
3531
        if( bLocal )
3532
        {
3533
            if ( bStatic && pMeth )
3534
            {
3535
                pElem = pMeth->GetStatics()->Find( aName, SbxClassType::DontCare );
3536
            }
3537
3538
            if ( !pElem )
3539
            {
3540
                pElem = refLocals->Find( aName, SbxClassType::DontCare );
3541
            }
3542
        }
3543
        if( !pElem )
3544
        {
3545
            bool bSave = rBasic.bNoRtl;
3546
            rBasic.bNoRtl = true;
3547
            pElem = pObj->Find( aName, SbxClassType::DontCare );
3548
3549
            // #110004, #112015: Make private really private
3550
            if( bLocal && pElem )   // Local as flag for global search
3551
            {
3552
                if( pElem->IsSet( SbxFlagBits::Private ) )
3553
                {
3554
                    SbiInstance* pInst_ = GetSbData()->pInst;
3555
                    if( pInst_ && pInst_->IsCompatibility() && pObj != pElem->GetParent() )
3556
                    {
3557
                        pElem = nullptr;   // Found but in wrong module!
3558
                    }
3559
                    // Interfaces: Use SbxFlagBits::ExtFound
3560
                }
3561
            }
3562
            rBasic.bNoRtl = bSave;
3563
3564
            // is it a global uno-identifier?
3565
            if( bLocal && !pElem )
3566
            {
3567
                bool bSetName = true; // preserve normal behaviour
3568
3569
                // i#i68894# if VBAInterOp favour searching vba globals
3570
                // over searching for uno classes
3571
                if ( bVBAEnabled )
3572
                {
3573
                    // Try Find in VBA symbols space
3574
                    pElem = rBasic.VBAFind( aName, SbxClassType::DontCare );
3575
                    if ( pElem )
3576
                    {
3577
                        bSetName = false; // don't overwrite uno name
3578
                    }
3579
                    else
3580
                    {
3581
                        pElem = VBAConstantHelper::instance().getVBAConstant( aName );
3582
                    }
3583
                }
3584
3585
                if( !pElem )
3586
                {
3587
                    // #72382 ATTENTION! ALWAYS returns a result now
3588
                    // because of unknown modules!
3589
                    SbUnoClass* pUnoClass = findUnoClass( aName );
3590
                    if( pUnoClass )
3591
                    {
3592
                        pElem = new SbxVariable( t );
3593
                        SbxValues aRes( SbxOBJECT );
3594
                        aRes.pObj = pUnoClass;
3595
                        pElem->SbxVariable::Put( aRes );
3596
                    }
3597
                }
3598
3599
                // #62939 If a uno-class has been found, the wrapper
3600
                // object has to be held, because the uno-class, e. g.
3601
                // "stardiv", has to be read out of the registry
3602
                // every time again otherwise
3603
                if( pElem )
3604
                {
3605
                    // #63774 May not be saved too!!!
3606
                    pElem->SetFlag( SbxFlagBits::DontStore );
3607
                    pElem->SetFlag( SbxFlagBits::NoModify);
3608
3609
                    // #72382 save locally, all variables that have been declared
3610
                    // implicit would become global automatically otherwise!
3611
                    if ( bSetName )
3612
                    {
3613
                        pElem->SetName( aName );
3614
                    }
3615
                    refLocals->Put(pElem, refLocals->Count());
3616
                }
3617
            }
3618
3619
            if( !pElem )
3620
            {
3621
                // not there and not in the object?
3622
                // don't establish if that thing has parameters!
3623
                if( nOp1 & 0x8000 )
3624
                {
3625
                    bFatalError = true;
3626
                }
3627
3628
                // else, if there are parameters, use different error code
3629
                if( !bLocal || pImg->IsFlag( SbiImageFlags::EXPLICIT ) )
3630
                {
3631
                    // #39108 if explicit and as ELEM always a fatal error
3632
                    bFatalError = true;
3633
3634
3635
                    if( !( nOp1 & 0x8000 ) && nNotFound == ERRCODE_BASIC_PROC_UNDEFINED )
3636
                    {
3637
                        nNotFound = ERRCODE_BASIC_VAR_UNDEFINED;
3638
                    }
3639
                }
3640
                if( bFatalError )
3641
                {
3642
                    // #39108 use dummy variable instead of fatal error
3643
                    if( !xDummyVar.is() )
3644
                    {
3645
                        xDummyVar = new SbxVariable( SbxVARIANT );
3646
                    }
3647
                    pElem = xDummyVar.get();
3648
3649
                    ClearArgvStack();
3650
3651
                    Error( nNotFound, aName );
3652
                }
3653
                else
3654
                {
3655
                    if ( bStatic )
3656
                    {
3657
                        pElem = StepSTATIC_Impl( aName, t, 0 );
3658
                    }
3659
                    if ( !pElem )
3660
                    {
3661
                        pElem = new SbxVariable( t );
3662
                        if( t != SbxVARIANT )
3663
                        {
3664
                            pElem->SetFlag( SbxFlagBits::Fixed );
3665
                        }
3666
                        pElem->SetName( aName );
3667
                        refLocals->Put(pElem, refLocals->Count());
3668
                    }
3669
                }
3670
            }
3671
        }
3672
        // #39108 Args can already be deleted!
3673
        if( !bFatalError )
3674
        {
3675
            SetupArgs( pElem, nOp1 );
3676
        }
3677
        // because a particular call-type is requested
3678
        if (SbxMethod* pMethod = dynamic_cast<SbxMethod*>(pElem))
3679
        {
3680
            // shall the type be converted?
3681
            SbxDataType t2 = pElem->GetType();
3682
            bool bSet = false;
3683
            if( (pElem->GetFlags() & SbxFlagBits::Fixed) == SbxFlagBits::NONE )
3684
            {
3685
                if( t != SbxVARIANT && t != t2 &&
3686
                    t >= SbxINTEGER && t <= SbxSTRING )
3687
                {
3688
                    pElem->SetType( t );
3689
                    bSet = true;
3690
                }
3691
            }
3692
            // assign pElem to a Ref, to delete a temp-var if applicable
3693
            SbxVariableRef xDeleteRef = pElem;
3694
3695
            // remove potential rests of the last call of the SbxMethod
3696
            // free Write before, so that there's no error
3697
            SbxFlagBits nSavFlags = pElem->GetFlags();
3698
            pElem->SetFlag( SbxFlagBits::ReadWrite | SbxFlagBits::NoBroadcast );
3699
            pElem->SbxValue::Clear();
3700
            pElem->SetFlags( nSavFlags );
3701
3702
            // don't touch before setting, as e. g. LEFT()
3703
            // has to know the difference between Left$() and Left()
3704
3705
            // because the methods' parameters are cut away in PopVar()
3706
            SbxVariable* pNew = new SbxMethod(*pMethod);
3707
            //OLD: SbxVariable* pNew = new SbxVariable( *pElem );
3708
3709
            pElem->SetParameters(nullptr);
3710
            pNew->SetFlag( SbxFlagBits::ReadWrite );
3711
3712
            if( bSet )
3713
            {
3714
                pElem->SetType( t2 );
3715
            }
3716
            pElem = pNew;
3717
        }
3718
        // consider index-access for UnoObjects
3719
        // definitely we want this for VBA where properties are often
3720
        // collections ( which need index access ), but let's only do
3721
        // this if we actually have params following
3722
        else if( bVBAEnabled && dynamic_cast<const SbUnoProperty*>( pElem) != nullptr && pElem->GetParameters() )
3723
        {
3724
            SbxVariableRef xDeleteRef = pElem;
3725
3726
            // dissolve the notify while copying variable
3727
            SbxVariable* pNew = new SbxVariable( *pElem );
3728
            pElem->SetParameters( nullptr );
3729
            pElem = pNew;
3730
        }
3731
    }
3732
    return CheckArray( pElem );
3733
}
3734
3735
// for current scope (e. g. query from BASIC-IDE)
3736
SbxBase* SbiRuntime::FindElementExtern( const OUString& rName )
3737
{
3738
    // don't expect pMeth to be != 0, as there are none set
3739
    // in the RunInit yet
3740
3741
    SbxVariable* pElem = nullptr;
3742
    if( !pMod || rName.isEmpty() )
3743
    {
3744
        return nullptr;
3745
    }
3746
    if( refLocals.is() )
3747
    {
3748
        pElem = refLocals->Find( rName, SbxClassType::DontCare );
3749
    }
3750
    if ( !pElem && pMeth )
3751
    {
3752
        const OUString aMethName = pMeth->GetName();
3753
        // tdf#57308 - check if the name is the current method instance
3754
        if (pMeth->GetName() == rName)
3755
        {
3756
            pElem = pMeth;
3757
        }
3758
        else
3759
        {
3760
            // for statics, set the method's name in front
3761
            pElem = pMod->Find(aMethName + ":" + rName, SbxClassType::DontCare);
3762
        }
3763
    }
3764
3765
3766
    // search in parameter list
3767
    if( !pElem && pMeth )
3768
    {
3769
        SbxInfo* pInfo = pMeth->GetInfo();
3770
        if( pInfo && refParams.is() )
3771
        {
3772
            sal_uInt32 nParamCount = refParams->Count();
3773
            assert(nParamCount <= std::numeric_limits<sal_uInt16>::max());
3774
            sal_uInt16 j = 1;
3775
            const SbxParamInfo* pParam = pInfo->GetParam( j );
3776
            while( pParam )
3777
            {
3778
                if( pParam->aName.equalsIgnoreAsciiCase( rName ) )
3779
                {
3780
                    if( j >= nParamCount )
3781
                    {
3782
                        // Parameter is missing
3783
                        pElem = new SbxVariable( SbxSTRING );
3784
                        pElem->PutString( u"<missing parameter>"_ustr);
3785
                    }
3786
                    else
3787
                    {
3788
                        pElem = refParams->Get(j);
3789
                    }
3790
                    break;
3791
                }
3792
                pParam = pInfo->GetParam( ++j );
3793
            }
3794
        }
3795
    }
3796
3797
    // search in module
3798
    if( !pElem )
3799
    {
3800
        bool bSave = rBasic.bNoRtl;
3801
        rBasic.bNoRtl = true;
3802
        pElem = pMod->Find( rName, SbxClassType::DontCare );
3803
        rBasic.bNoRtl = bSave;
3804
    }
3805
    return pElem;
3806
}
3807
3808
3809
void SbiRuntime::SetupArgs( SbxVariable* p, sal_uInt32 nOp1 )
3810
{
3811
    if( nOp1 & 0x8000 )
3812
    {
3813
        if( !refArgv.is() )
3814
        {
3815
            StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
3816
        }
3817
        bool bHasNamed = false;
3818
        sal_uInt32 i;
3819
        sal_uInt32 nArgCount = refArgv->Count();
3820
        for( i = 1 ; i < nArgCount ; i++ )
3821
        {
3822
            if (!refArgv->GetAlias(i).isEmpty())
3823
            {
3824
                bHasNamed = true; break;
3825
            }
3826
        }
3827
        if( bHasNamed )
3828
        {
3829
            SbxInfo* pInfo = p->GetInfo();
3830
            if( !pInfo )
3831
            {
3832
                bool bError_ = true;
3833
3834
                SbUnoMethod* pUnoMethod = dynamic_cast<SbUnoMethod*>( p );
3835
                SbUnoProperty* pUnoProperty = dynamic_cast<SbUnoProperty*>( p );
3836
                if( pUnoMethod || pUnoProperty )
3837
                {
3838
                    SbUnoObject* pParentUnoObj = dynamic_cast<SbUnoObject*>( p->GetParent()  );
3839
                    if( pParentUnoObj )
3840
                    {
3841
                        Any aUnoAny = pParentUnoObj->getUnoAny();
3842
                        Reference< XInvocation > xInvocation;
3843
                        aUnoAny >>= xInvocation;
3844
                        if( xInvocation.is() )  // TODO: if( xOLEAutomation.is() )
3845
                        {
3846
                            bError_ = false;
3847
3848
                            sal_uInt32 nCurPar = 1;
3849
                            AutomationNamedArgsSbxArray* pArg =
3850
                                new AutomationNamedArgsSbxArray( nArgCount );
3851
                            OUString* pNames = pArg->getNames().getArray();
3852
                            for( i = 1 ; i < nArgCount ; i++ )
3853
                            {
3854
                                SbxVariable* pVar = refArgv->Get(i);
3855
                                OUString aName = refArgv->GetAlias(i);
3856
                                if (!aName.isEmpty())
3857
                                {
3858
                                    pNames[i] = aName;
3859
                                }
3860
                                pArg->Put(pVar, nCurPar++);
3861
                            }
3862
                            refArgv = pArg;
3863
                        }
3864
                    }
3865
                }
3866
                else if( bVBAEnabled && p->GetType() == SbxOBJECT && (dynamic_cast<const SbxMethod*>( p) == nullptr || !p->IsBroadcaster()) )
3867
                {
3868
                    // Check for default method with named parameters
3869
                    SbxBaseRef xObj = p->GetObject();
3870
                    if (SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( xObj.get() ))
3871
                    {
3872
                        Any aAny = pUnoObj->getUnoAny();
3873
3874
                        if( aAny.getValueTypeClass() == TypeClass_INTERFACE )
3875
                        {
3876
                            Reference< XDefaultMethod > xDfltMethod( aAny, UNO_QUERY );
3877
3878
                            OUString sDefaultMethod;
3879
                            if ( xDfltMethod.is() )
3880
                            {
3881
                                sDefaultMethod = xDfltMethod->getDefaultMethodName();
3882
                            }
3883
                            if ( !sDefaultMethod.isEmpty() )
3884
                            {
3885
                                SbxVariable* meth = pUnoObj->Find( sDefaultMethod, SbxClassType::Method );
3886
                                if( meth != nullptr )
3887
                                {
3888
                                    pInfo = meth->GetInfo();
3889
                                }
3890
                                if( pInfo )
3891
                                {
3892
                                    bError_ = false;
3893
                                }
3894
                            }
3895
                        }
3896
                    }
3897
                }
3898
                if( bError_ )
3899
                {
3900
                    Error( ERRCODE_BASIC_NO_NAMED_ARGS );
3901
                }
3902
            }
3903
            else
3904
            {
3905
                sal_uInt32 nCurPar = 1;
3906
                SbxArray* pArg = new SbxArray;
3907
                for( i = 1 ; i < nArgCount ; i++ )
3908
                {
3909
                    SbxVariable* pVar = refArgv->Get(i);
3910
                    OUString aName = refArgv->GetAlias(i);
3911
                    if (!aName.isEmpty())
3912
                    {
3913
                        // nCurPar is set to the found parameter
3914
                        sal_uInt16 j = 1;
3915
                        const SbxParamInfo* pParam = pInfo->GetParam( j );
3916
                        while( pParam )
3917
                        {
3918
                            if( pParam->aName.equalsIgnoreAsciiCase( aName ) )
3919
                            {
3920
                                nCurPar = j;
3921
                                break;
3922
                            }
3923
                            pParam = pInfo->GetParam( ++j );
3924
                        }
3925
                        if( !pParam )
3926
                        {
3927
                            Error( ERRCODE_BASIC_NAMED_NOT_FOUND ); break;
3928
                        }
3929
                    }
3930
                    pArg->Put(pVar, nCurPar++);
3931
                }
3932
                // tdf#143706 - mark skipped named optional parameters as missing/empty
3933
                SbxMethod* pSbxMeth = dynamic_cast<SbxMethod*>(p);
3934
                const bool bIsRuntimeFunction = pSbxMeth && pSbxMeth->IsRuntimeFunction();
3935
                for (sal_uInt32 j = 1; j < pArg->Count(); j++)
3936
                {
3937
                    if (!pArg->GetRef(j))
3938
                    {
3939
                        SbxVariableRef xVar;
3940
                        if (bIsRuntimeFunction)
3941
                            xVar = new SbxVariable(SbxEMPTY);
3942
                        else
3943
                        {
3944
                            // Same as in SbiRuntime::StepEMPTY(), because the compiler does not
3945
                            // generate a StepEMPTY for StepPARAM when there is no COMMA token
3946
                            xVar = new SbxVariable(SbxVARIANT);
3947
                            SbxSetMissingParameter(*xVar);
3948
                        }
3949
                        pArg->Put(xVar.get(), j);
3950
                    }
3951
                }
3952
                refArgv = pArg;
3953
            }
3954
        }
3955
        // own var as parameter 0
3956
        refArgv->Put(p, 0);
3957
        p->SetParameters( refArgv.get() );
3958
        PopArgv();
3959
    }
3960
    else
3961
    {
3962
        p->SetParameters( nullptr );
3963
    }
3964
}
3965
3966
// getting an array element
3967
3968
SbxVariable* SbiRuntime::CheckArray( SbxVariable* pElem )
3969
{
3970
    assert(pElem);
3971
    if( ( pElem->GetType() & SbxARRAY ) && refRedim.get() != pElem )
3972
    {
3973
        SbxBase* pElemObj = pElem->GetObject();
3974
        SbxArray* pPar = pElem->GetParameters();
3975
        if (SbxDimArray* pDimArray = dynamic_cast<SbxDimArray*>(pElemObj))
3976
        {
3977
            // parameters may be missing, if an array is
3978
            // passed as an argument
3979
            if( pPar )
3980
            {
3981
                bool parIsArrayIndex = true;
3982
                if (dynamic_cast<const SbxMethod*>(pElem))
3983
                {
3984
                    // If this was a method, then there are two possibilities:
3985
                    // 1. pPar is this method's parameters.
3986
                    // 2. pPar is the indexes into the array returned from the method.
3987
                    // To disambiguate, check the 0th element of pPar.
3988
                    if (dynamic_cast<const SbxMethod*>(pPar->Get(0)))
3989
                    {
3990
                        // pPar was the parameters to the method, not indexes into the array
3991
                        parIsArrayIndex = false;
3992
                    }
3993
                }
3994
                if (parIsArrayIndex)
3995
                    pElem = pDimArray->Get(pPar);
3996
            }
3997
        }
3998
        else if (SbxArray* pArray = dynamic_cast<SbxArray*>(pElemObj))
3999
        {
4000
            if( !pPar )
4001
            {
4002
                Error( ERRCODE_BASIC_OUT_OF_RANGE );
4003
                pElem = new SbxVariable;
4004
            }
4005
            else
4006
            {
4007
                pElem = pArray->Get(pPar->Get(1)->GetInteger());
4008
            }
4009
        }
4010
4011
        // #42940, set parameter 0 to NULL so that var doesn't contain itself
4012
        if( pPar )
4013
        {
4014
            pPar->Put(nullptr, 0);
4015
        }
4016
    }
4017
    // consider index-access for UnoObjects
4018
    else if( pElem->GetType() == SbxOBJECT &&
4019
            dynamic_cast<const SbxMethod*>( pElem) == nullptr &&
4020
            ( !bVBAEnabled || dynamic_cast<const SbxProperty*>( pElem) == nullptr ) )
4021
    {
4022
        if (SbxArray* pPar = pElem->GetParameters())
4023
        {
4024
            // is it a uno-object?
4025
            SbxBaseRef pObj = pElem->GetObject();
4026
            if( pObj.is() )
4027
            {
4028
                if (SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pObj.get()))
4029
                {
4030
                    Any aAny = pUnoObj->getUnoAny();
4031
4032
                    if( aAny.getValueTypeClass() == TypeClass_INTERFACE )
4033
                    {
4034
                        Reference< XIndexAccess > xIndexAccess( aAny, UNO_QUERY );
4035
                        if ( !bVBAEnabled )
4036
                        {
4037
                            if( xIndexAccess.is() )
4038
                            {
4039
                                sal_uInt32 nParamCount = pPar->Count() - 1;
4040
                                if( nParamCount != 1 )
4041
                                {
4042
                                    StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4043
                                    return pElem;
4044
                                }
4045
4046
                                // get index
4047
                                sal_Int32 nIndex = pPar->Get(1)->GetLong();
4048
                                Reference< XInterface > xRet;
4049
                                try
4050
                                {
4051
                                    Any aAny2 = xIndexAccess->getByIndex( nIndex );
4052
                                    aAny2 >>= xRet;
4053
                                }
4054
                                catch (const IndexOutOfBoundsException&)
4055
                                {
4056
                                    // usually expect converting problem
4057
                                    StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
4058
                                }
4059
4060
                                // #57847 always create a new variable, else error
4061
                                // due to PutObject(NULL) at ReadOnly-properties
4062
                                pElem = new SbxVariable( SbxVARIANT );
4063
                                if( xRet.is() )
4064
                                {
4065
                                    aAny <<= xRet;
4066
4067
                                    // #67173 don't specify a name so that the real class name is entered
4068
                                    SbxObjectRef xWrapper = static_cast<SbxObject*>(new SbUnoObject( OUString(), aAny ));
4069
                                    pElem->PutObject( xWrapper.get() );
4070
                                }
4071
                                else
4072
                                {
4073
                                    pElem->PutObject( nullptr );
4074
                                }
4075
                            }
4076
                        }
4077
                        else
4078
                        {
4079
                            // check if there isn't a default member between the current variable
4080
                            // and the params, e.g.
4081
                            //   Dim rst1 As New ADODB.Recordset
4082
                            //      "
4083
                            //   val = rst1("FirstName")
4084
                            // has the default 'Fields' member between rst1 and '("FirstName")'
4085
                            Any x = aAny;
4086
                            SbxVariable* pDflt = getDefaultProp( pElem );
4087
                            if ( pDflt )
4088
                            {
4089
                                pDflt->Broadcast( SfxHintId::BasicDataWanted );
4090
                                SbxBaseRef pDfltObj = pDflt->GetObject();
4091
                                if( pDfltObj.is() )
4092
                                {
4093
                                    if (SbUnoObject* pSbObj = dynamic_cast<SbUnoObject*>(pDfltObj.get()))
4094
                                    {
4095
                                        pUnoObj = pSbObj;
4096
                                        Any aUnoAny = pUnoObj->getUnoAny();
4097
4098
                                        if( aUnoAny.getValueTypeClass() == TypeClass_INTERFACE )
4099
                                            x = std::move(aUnoAny);
4100
                                        pElem = pDflt;
4101
                                    }
4102
                                }
4103
                            }
4104
                            OUString sDefaultMethod;
4105
4106
                            Reference< XDefaultMethod > xDfltMethod( x, UNO_QUERY );
4107
4108
                            if ( xDfltMethod.is() )
4109
                            {
4110
                                sDefaultMethod = xDfltMethod->getDefaultMethodName();
4111
                            }
4112
                            else if( xIndexAccess.is() )
4113
                            {
4114
                                sDefaultMethod = "getByIndex";
4115
                            }
4116
                            if ( !sDefaultMethod.isEmpty() )
4117
                            {
4118
                                SbxVariable* meth = pUnoObj->Find( sDefaultMethod, SbxClassType::Method );
4119
                                SbxVariableRef refTemp = meth;
4120
                                if ( refTemp.is() )
4121
                                {
4122
                                    meth->SetParameters( pPar );
4123
                                    SbxVariable* pNew = new SbxMethod( *static_cast<SbxMethod*>(meth) );
4124
                                    pElem = pNew;
4125
                                }
4126
                            }
4127
                        }
4128
                    }
4129
4130
                    // #42940, set parameter 0 to NULL so that var doesn't contain itself
4131
                    pPar->Put(nullptr, 0);
4132
                }
4133
                else if (BasicCollection* pCol = dynamic_cast<BasicCollection*>(pObj.get()))
4134
                {
4135
                    pElem = new SbxVariable( SbxVARIANT );
4136
                    pPar->Put(pElem, 0);
4137
                    pCol->CollItem( pPar );
4138
                }
4139
            }
4140
            else if( bVBAEnabled )  // !pObj
4141
            {
4142
                SbxArray* pParam = pElem->GetParameters();
4143
                if( pParam != nullptr && !pElem->IsSet( SbxFlagBits::VarToDim ) )
4144
                {
4145
                    Error( ERRCODE_BASIC_NO_OBJECT );
4146
                }
4147
            }
4148
        }
4149
    }
4150
4151
    return pElem;
4152
}
4153
4154
// loading an element from the runtime-library (+StringID+type)
4155
4156
void SbiRuntime::StepRTL( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4157
{
4158
    PushVar( FindElement( rBasic.pRtl.get(), nOp1, nOp2, ERRCODE_BASIC_PROC_UNDEFINED, false ) );
4159
}
4160
4161
void SbiRuntime::StepFIND_Impl( SbxObject* pObj, sal_uInt32 nOp1, sal_uInt32 nOp2,
4162
                                ErrCode nNotFound, bool bStatic )
4163
{
4164
    if( !refLocals.is() )
4165
    {
4166
        refLocals = new SbxArray;
4167
    }
4168
    PushVar( FindElement( pObj, nOp1, nOp2, nNotFound, true/*bLocal*/, bStatic ) );
4169
}
4170
// loading a local/global variable (+StringID+type)
4171
4172
void SbiRuntime::StepFIND( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4173
{
4174
    StepFIND_Impl( pMod, nOp1, nOp2, ERRCODE_BASIC_PROC_UNDEFINED );
4175
}
4176
4177
// Search inside a class module (CM) to enable global search in time
4178
void SbiRuntime::StepFIND_CM( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4179
{
4180
4181
    SbClassModuleObject* pClassModuleObject = dynamic_cast<SbClassModuleObject*>( pMod );
4182
    if( pClassModuleObject )
4183
    {
4184
        pMod->SetFlag( SbxFlagBits::GlobalSearch );
4185
    }
4186
    StepFIND_Impl( pMod, nOp1, nOp2, ERRCODE_BASIC_PROC_UNDEFINED);
4187
4188
    if( pClassModuleObject )
4189
    {
4190
        pMod->ResetFlag( SbxFlagBits::GlobalSearch );
4191
    }
4192
}
4193
4194
void SbiRuntime::StepFIND_STATIC( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4195
{
4196
    StepFIND_Impl( pMod, nOp1, nOp2, ERRCODE_BASIC_PROC_UNDEFINED, true );
4197
}
4198
4199
// loading an object-element (+StringID+type)
4200
// the object lies on TOS
4201
4202
void SbiRuntime::StepELEM( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4203
{
4204
    SbxVariableRef pObjVar = PopVar();
4205
4206
    SbxObject* pObj = dynamic_cast<SbxObject*>( pObjVar.get() );
4207
    if( !pObj )
4208
    {
4209
        SbxBase* pObjVarObj = pObjVar->GetObject();
4210
        pObj = dynamic_cast<SbxObject*>( pObjVarObj );
4211
    }
4212
4213
    // #56368 save reference at StepElem, otherwise objects could
4214
    // lose their reference too early in qualification chains like
4215
    // ActiveComponent.Selection(0).Text
4216
    // #74254 now per list
4217
    if( pObj )
4218
    {
4219
        aRefSaved.emplace_back(pObj );
4220
    }
4221
    PushVar( FindElement( pObj, nOp1, nOp2, ERRCODE_BASIC_NO_METHOD, false ) );
4222
}
4223
4224
/** Loading of a parameter (+offset+type)
4225
    If the data type is wrong, create a copy and search for optionals including
4226
    the default value. The data type SbxEMPTY shows that no parameters are given.
4227
    Get( 0 ) may be EMPTY
4228
4229
    @param nOp1
4230
    the index of the current parameter being processed,
4231
    where the entry of the index 0 is for the return value.
4232
4233
    @param nOp2
4234
    the data type of the parameter.
4235
 */
4236
void SbiRuntime::StepPARAM( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4237
{
4238
    sal_uInt16 nIdx = static_cast<sal_uInt16>( nOp1 & 0x7FFF );
4239
    SbxDataType eType = static_cast<SbxDataType>(nOp2);
4240
    SbxVariable* pVar;
4241
4242
    // #57915 solve missing in a cleaner way
4243
    sal_uInt32 nParamCount = refParams->Count();
4244
    if( nIdx >= nParamCount )
4245
    {
4246
        sal_uInt16 iLoop = nIdx;
4247
        while( iLoop >= nParamCount )
4248
        {
4249
            pVar = new SbxVariable();
4250
            // tdf#79426, tdf#125180 - add additional information about a missing parameter
4251
            SbxSetMissingParameter(*pVar);
4252
            refParams->Put(pVar, iLoop);
4253
            iLoop--;
4254
        }
4255
    }
4256
    pVar = refParams->Get(nIdx);
4257
4258
    // tdf#79426, tdf#125180 - check for optionals only if the parameter is actually missing
4259
    if( pVar->GetType() == SbxERROR && IsMissing( pVar, 1 ) && nIdx )
4260
    {
4261
        // if there's a parameter missing, it can be OPTIONAL
4262
        bool bOpt = false;
4263
        if( pMeth )
4264
        {
4265
            SbxInfo* pInfo = pMeth->GetInfo();
4266
            if ( pInfo )
4267
            {
4268
                const SbxParamInfo* pParam = pInfo->GetParam( nIdx );
4269
                if( pParam && ( pParam->nFlags & SbxFlagBits::Optional ) )
4270
                {
4271
                    // tdf#136143 - reset SbxFlagBits::Fixed in order to prevent type conversion errors
4272
                    pVar->ResetFlag( SbxFlagBits::Fixed );
4273
                    // Default value?
4274
                    sal_uInt16 nDefaultId = static_cast<sal_uInt16>(pParam->nUserData & 0x0ffff);
4275
                    if( nDefaultId > 0 )
4276
                    {
4277
                        // tdf#143707 - check if the data type character was added after the string
4278
                        // termination symbol, and convert the variable if it was present. The
4279
                        // data type character was added in basic/source/comp/symtbl.cxx.
4280
                        SbxDataType eTypeStr;
4281
                        OUString aDefaultStr = pImg->GetString( nDefaultId, &eTypeStr );
4282
                        pVar = new SbxVariable(pParam-> eType);
4283
                        pVar->PutString( aDefaultStr );
4284
                        if (eTypeStr != SbxSTRING)
4285
                            pVar->Convert(eTypeStr);
4286
                        refParams->Put(pVar, nIdx);
4287
                    }
4288
                    else if ( SbiRuntime::isVBAEnabled() && eType != SbxVARIANT )
4289
                    {
4290
                        // tdf#36737 - initialize the parameter with the default value of its type
4291
                        pVar = new SbxVariable( pParam->eType );
4292
                        refParams->Put(pVar, nIdx);
4293
                    }
4294
                    bOpt = true;
4295
                }
4296
            }
4297
        }
4298
        if( !bOpt )
4299
        {
4300
            Error( ERRCODE_BASIC_NOT_OPTIONAL );
4301
        }
4302
    }
4303
    else if( eType != SbxVARIANT && static_cast<SbxDataType>(pVar->GetType() & 0x0FFF ) != eType )
4304
    {
4305
        // tdf#43003 - convert parameter to the requested type
4306
        pVar->Convert(eType);
4307
    }
4308
    SetupArgs( pVar, nOp1 );
4309
    PushVar( CheckArray( pVar ) );
4310
}
4311
4312
// Case-Test (+True-Target+Test-Opcode)
4313
4314
void SbiRuntime::StepCASEIS( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4315
{
4316
    if (!refCaseStk.is() || !refCaseStk->Count())
4317
    {
4318
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
4319
    }
4320
    else
4321
    {
4322
        SbxVariableRef xComp = PopVar();
4323
        SbxVariableRef xCase = refCaseStk->Get(refCaseStk->Count() - 1);
4324
        if( xCase->Compare( static_cast<SbxOperator>(nOp2), *xComp ) )
4325
        {
4326
            StepJUMP( nOp1 );
4327
        }
4328
    }
4329
}
4330
4331
// call of a DLL-procedure (+StringID+type)
4332
// the StringID's MSB shows that Argv is occupied
4333
4334
void SbiRuntime::StepCALL( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4335
{
4336
    OUString aName = pImg->GetString( nOp1 & 0x7FFF );
4337
    SbxArray* pArgs = nullptr;
4338
    if( nOp1 & 0x8000 )
4339
    {
4340
        pArgs = refArgv.get();
4341
    }
4342
    DllCall( aName, aLibName, pArgs, static_cast<SbxDataType>(nOp2), false );
4343
    aLibName.clear();
4344
    if( nOp1 & 0x8000 )
4345
    {
4346
        PopArgv();
4347
    }
4348
}
4349
4350
// call of a DLL-procedure after CDecl (+StringID+type)
4351
4352
void SbiRuntime::StepCALLC( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4353
{
4354
    OUString aName = pImg->GetString( nOp1 & 0x7FFF );
4355
    SbxArray* pArgs = nullptr;
4356
    if( nOp1 & 0x8000 )
4357
    {
4358
        pArgs = refArgv.get();
4359
    }
4360
    DllCall( aName, aLibName, pArgs, static_cast<SbxDataType>(nOp2), true );
4361
    aLibName.clear();
4362
    if( nOp1 & 0x8000 )
4363
    {
4364
        PopArgv();
4365
    }
4366
}
4367
4368
4369
// beginning of a statement (+Line+Col)
4370
4371
void SbiRuntime::StepSTMNT( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4372
{
4373
    // If the Expr-Stack at the beginning of a statement contains a variable,
4374
    // some fool has called X as a function, although it's a variable!
4375
    bool bFatalExpr = false;
4376
    OUString sUnknownMethodName;
4377
    if( nExprLvl > 1 )
4378
    {
4379
        bFatalExpr = true;
4380
    }
4381
    else if( nExprLvl )
4382
    {
4383
        SbxVariable* p = refExprStk->Get(0);
4384
        if( p->GetRefCount() > 1 &&
4385
            refLocals.is() && refLocals->Find( p->GetName(), p->GetClass() ) )
4386
        {
4387
            sUnknownMethodName = p->GetName();
4388
            bFatalExpr = true;
4389
        }
4390
    }
4391
4392
    ClearExprStack();
4393
4394
    aRefSaved.clear();
4395
4396
    // We have to cancel hard here because line and column
4397
    // would be wrong later otherwise!
4398
    if( bFatalExpr)
4399
    {
4400
        StarBASIC::FatalError( ERRCODE_BASIC_NO_METHOD, sUnknownMethodName );
4401
        return;
4402
    }
4403
    pStmnt = pCode - 9;
4404
    sal_uInt16 nOld = nLine;
4405
    nLine = static_cast<short>( nOp1 );
4406
4407
    // #29955 & 0xFF, to filter out for-loop-level
4408
    nCol1 = static_cast<short>( nOp2 & 0xFF );
4409
4410
    // find the next STMNT-command to set the final column
4411
    // of this statement
4412
4413
    nCol2 = 0xffff;
4414
    sal_uInt16 n1, n2;
4415
    const sal_uInt8* p = pMod->FindNextStmnt( pCode, n1, n2 );
4416
    if( p )
4417
    {
4418
        if( n1 == nOp1 )
4419
        {
4420
            // #29955 & 0xFF, to filter out for-loop-level
4421
            nCol2 = (n2 & 0xFF) - 1;
4422
        }
4423
    }
4424
4425
    // #29955 correct for-loop-level, #67452 NOT in the error-handler
4426
    if( !bInError )
4427
    {
4428
        // (there's a difference here in case of a jump out of a loop)
4429
        sal_uInt16 nExpectedForLevel = static_cast<sal_uInt16>( nOp2 / 0x100 );
4430
        if( !pGosubStk.empty() )
4431
        {
4432
            nExpectedForLevel = nExpectedForLevel + pGosubStk.back().nStartForLvl;
4433
        }
4434
4435
        // if the actual for-level is too small it'd jump out
4436
        // of a loop -> corrected
4437
        while( nForLvl > nExpectedForLevel )
4438
        {
4439
            PopFor();
4440
        }
4441
    }
4442
4443
    // 16.10.96: #31460 new concept for StepInto/Over/Out
4444
    // see explanation at SbiInstance::CalcBreakCallLevel
4445
    if( pInst->nCallLvl <= pInst->nBreakCallLvl )
4446
    {
4447
        StarBASIC* pStepBasic = GetCurrentBasic( &rBasic );
4448
        BasicDebugFlags nNewFlags = pStepBasic->StepPoint( nLine, nCol1, nCol2 );
4449
4450
        pInst->CalcBreakCallLevel( nNewFlags );
4451
    }
4452
4453
    // break points only at STMNT-commands in a new line!
4454
    else if( ( nOp1 != nOld )
4455
        && ( nFlags & BasicDebugFlags::Break )
4456
        && pMod->IsBP( static_cast<sal_uInt16>( nOp1 ) ) )
4457
    {
4458
        StarBASIC* pBreakBasic = GetCurrentBasic( &rBasic );
4459
        BasicDebugFlags nNewFlags = pBreakBasic->BreakPoint( nLine, nCol1, nCol2 );
4460
4461
        pInst->CalcBreakCallLevel( nNewFlags );
4462
    }
4463
}
4464
4465
// (+StreamMode+Flags)
4466
// Stack: block length
4467
//        channel number
4468
//        file name
4469
4470
void SbiRuntime::StepOPEN( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4471
{
4472
    SbxVariableRef pName = PopVar();
4473
    SbxVariableRef pChan = PopVar();
4474
    SbxVariableRef pLen  = PopVar();
4475
    short nBlkLen = pLen->GetInteger();
4476
    short nChan   = pChan->GetInteger();
4477
    OUString aName = pName->GetOUString();
4478
    pIosys->Open( nChan, aName, static_cast<StreamMode>( nOp1 ),
4479
                  static_cast<SbiStreamFlags>( nOp2 ), nBlkLen );
4480
    Error( pIosys->GetError() );
4481
}
4482
4483
// create object (+StringID+StringID)
4484
4485
void SbiRuntime::StepCREATE( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4486
{
4487
    OUString aClass( pImg->GetString( nOp2 ) );
4488
    SbxObjectRef pObj = SbxBase::CreateObject( aClass );
4489
    if( !pObj )
4490
    {
4491
        Error( ERRCODE_BASIC_INVALID_OBJECT );
4492
    }
4493
    else
4494
    {
4495
        OUString aName( pImg->GetString( nOp1 ) );
4496
        pObj->SetName( aName );
4497
        // the object must be able to call the BASIC
4498
        pObj->SetParent( &rBasic );
4499
        SbxVariableRef pNew = new SbxVariable;
4500
        pNew->PutObject( pObj.get() );
4501
        PushVar( pNew.get() );
4502
    }
4503
}
4504
4505
void SbiRuntime::StepDCREATE( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4506
{
4507
    StepDCREATE_IMPL( nOp1, nOp2 );
4508
}
4509
4510
void SbiRuntime::StepDCREATE_REDIMP( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4511
{
4512
    StepDCREATE_IMPL( nOp1, nOp2 );
4513
}
4514
4515
// #56204 create object array (+StringID+StringID), DCREATE == Dim-Create
4516
void SbiRuntime::StepDCREATE_IMPL( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4517
{
4518
    SbxVariableRef refVar = PopVar();
4519
4520
    DimImpl( refVar );
4521
4522
    // fill the array with instances of the requested class
4523
    SbxBase* pObj = refVar->GetObject();
4524
    if (!pObj)
4525
    {
4526
        StarBASIC::Error( ERRCODE_BASIC_INVALID_OBJECT );
4527
        return;
4528
    }
4529
4530
    SbxDimArray* pArray = dynamic_cast<SbxDimArray*>(pObj);
4531
    if (!pArray)
4532
        return;
4533
4534
    const sal_Int32 nDims = pArray->GetDims();
4535
    sal_Int32 nTotalSize = nDims > 0 ? 1 : 0;
4536
4537
    // must be a one-dimensional array
4538
    sal_Int32 nLower, nUpper;
4539
    for( sal_Int32 i = 0 ; i < nDims ; ++i )
4540
    {
4541
        pArray->GetDim(i + 1, nLower, nUpper);
4542
        const sal_Int32 nSize = nUpper - nLower + 1;
4543
        nTotalSize *= nSize;
4544
    }
4545
4546
    // Optimization: pre-allocate underlying container
4547
    if (nTotalSize > 0)
4548
        pArray->SbxArray::GetRef(nTotalSize - 1);
4549
4550
    // First, fill those parts of the array that are preserved
4551
    bool bWasError = false;
4552
    const bool bRestored = implRestorePreservedArray(pArray, refRedimpArray, &bWasError);
4553
    if (bWasError)
4554
        nTotalSize = 0; // on error, don't create objects
4555
4556
    // create objects and insert them into the array
4557
    OUString aClass( pImg->GetString( nOp2 ) );
4558
    OUString aName;
4559
    for( sal_Int32 i = 0 ; i < nTotalSize ; ++i )
4560
    {
4561
        if (!bRestored || !pArray->SbxArray::GetRef(i)) // For those left unset after preserve
4562
        {
4563
            SbxObjectRef pClassObj = SbxBase::CreateObject(aClass);
4564
            if (!pClassObj)
4565
            {
4566
                Error(ERRCODE_BASIC_INVALID_OBJECT);
4567
                break;
4568
            }
4569
            else
4570
            {
4571
                if (aName.isEmpty())
4572
                    aName = pImg->GetString(nOp1);
4573
                pClassObj->SetName(aName);
4574
                // the object must be able to call the basic
4575
                pClassObj->SetParent(&rBasic);
4576
                pArray->SbxArray::Put(pClassObj.get(), i);
4577
            }
4578
        }
4579
    }
4580
}
4581
4582
void SbiRuntime::StepTCREATE( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4583
{
4584
    OUString aName( pImg->GetString( nOp1 ) );
4585
    OUString aClass( pImg->GetString( nOp2 ) );
4586
4587
    SbxObjectRef pCopyObj = createUserTypeImpl( aClass );
4588
    if( pCopyObj )
4589
    {
4590
        pCopyObj->SetName( aName );
4591
    }
4592
    SbxVariableRef pNew = new SbxVariable;
4593
    pNew->PutObject( pCopyObj.get() );
4594
    pNew->SetDeclareClassName( aClass );
4595
    PushVar( pNew.get() );
4596
}
4597
4598
void SbiRuntime::implHandleSbxFlags( SbxVariable* pVar, SbxDataType t, sal_uInt32 nOp2 )
4599
{
4600
    bool bWithEvents = ((t & 0xff) == SbxOBJECT && (nOp2 & SBX_TYPE_WITH_EVENTS_FLAG) != 0);
4601
    if( bWithEvents )
4602
    {
4603
        pVar->SetFlag( SbxFlagBits::WithEvents );
4604
    }
4605
    bool bDimAsNew = ((nOp2 & SBX_TYPE_DIM_AS_NEW_FLAG) != 0);
4606
    if( bDimAsNew )
4607
    {
4608
        pVar->SetFlag( SbxFlagBits::DimAsNew );
4609
    }
4610
    bool bFixedString = ((t & 0xff) == SbxSTRING && (nOp2 & SBX_FIXED_LEN_STRING_FLAG) != 0);
4611
    if( bFixedString )
4612
    {
4613
        sal_uInt16 nCount = static_cast<sal_uInt16>( nOp2 >> 17 );      // len = all bits above 0x10000
4614
        pVar->PutString(OUString::Concat(RepeatedUChar('\0', nCount)));
4615
    }
4616
4617
    bool bVarToDim = ((nOp2 & SBX_TYPE_VAR_TO_DIM_FLAG) != 0);
4618
    if( bVarToDim )
4619
    {
4620
        pVar->SetFlag( SbxFlagBits::VarToDim );
4621
    }
4622
}
4623
4624
// establishing a local variable (+StringID+type)
4625
4626
void SbiRuntime::StepLOCAL( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4627
{
4628
    if( !refLocals.is() )
4629
    {
4630
        refLocals = new SbxArray;
4631
    }
4632
    OUString aName( pImg->GetString( nOp1 ) );
4633
    if( refLocals->Find( aName, SbxClassType::DontCare ) == nullptr )
4634
    {
4635
        SbxDataType t = static_cast<SbxDataType>(nOp2 & 0xffff);
4636
        SbxVariable* p = new SbxVariable( t );
4637
        p->SetName( aName );
4638
        implHandleSbxFlags( p, t, nOp2 );
4639
        refLocals->Put(p, refLocals->Count());
4640
    }
4641
}
4642
4643
// establishing a module-global variable (+StringID+type)
4644
4645
void SbiRuntime::StepPUBLIC_Impl( sal_uInt32 nOp1, sal_uInt32 nOp2, bool bUsedForClassModule )
4646
{
4647
    OUString aName( pImg->GetString( nOp1 ) );
4648
    SbxDataType t = static_cast<SbxDataType>(nOp2 & 0xffff);
4649
    bool bFlag = pMod->IsSet( SbxFlagBits::NoModify );
4650
    pMod->SetFlag( SbxFlagBits::NoModify );
4651
    SbxVariableRef p = pMod->Find( aName, SbxClassType::Property );
4652
    if( p.is() )
4653
    {
4654
        pMod->Remove (p.get());
4655
    }
4656
    SbProperty* pProp = pMod->GetProperty( aName, t );
4657
    if( !bUsedForClassModule )
4658
    {
4659
        pProp->SetFlag( SbxFlagBits::Private );
4660
    }
4661
    if( !bFlag )
4662
    {
4663
        pMod->ResetFlag( SbxFlagBits::NoModify );
4664
    }
4665
    if( pProp )
4666
    {
4667
        pProp->SetFlag( SbxFlagBits::DontStore );
4668
        // from 2.7.1996: HACK because of 'reference can't be saved'
4669
        pProp->SetFlag( SbxFlagBits::NoModify);
4670
4671
        implHandleSbxFlags( pProp, t, nOp2 );
4672
    }
4673
}
4674
4675
void SbiRuntime::StepPUBLIC( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4676
{
4677
    StepPUBLIC_Impl( nOp1, nOp2, false );
4678
}
4679
4680
void SbiRuntime::StepPUBLIC_P( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4681
{
4682
    // Creates module variable that isn't reinitialised when
4683
    // between invocations ( for VBASupport & document basic only )
4684
    if( pMod->pImage->bFirstInit )
4685
    {
4686
        bool bUsedForClassModule = pImg->IsFlag( SbiImageFlags::CLASSMODULE );
4687
        StepPUBLIC_Impl( nOp1, nOp2, bUsedForClassModule );
4688
    }
4689
}
4690
4691
// establishing a global variable (+StringID+type)
4692
4693
void SbiRuntime::StepGLOBAL( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4694
{
4695
    if( pImg->IsFlag( SbiImageFlags::CLASSMODULE ) )
4696
    {
4697
        StepPUBLIC_Impl( nOp1, nOp2, true );
4698
    }
4699
    OUString aName( pImg->GetString( nOp1 ) );
4700
    SbxDataType t = static_cast<SbxDataType>(nOp2 & 0xffff);
4701
4702
    // Store module scope variables at module scope
4703
    // in non vba mode these are stored at the library level :/
4704
    // not sure if this really should not be enabled for ALL basic
4705
    SbxObject* pStorage = &rBasic;
4706
    if ( SbiRuntime::isVBAEnabled() )
4707
    {
4708
        pStorage = pMod;
4709
        pMod->AddVarName( aName );
4710
    }
4711
4712
    bool bFlag = pStorage->IsSet( SbxFlagBits::NoModify );
4713
    rBasic.SetFlag( SbxFlagBits::NoModify );
4714
    SbxVariableRef p = pStorage->Find( aName, SbxClassType::Property );
4715
    if( p.is() )
4716
    {
4717
        pStorage->Remove (p.get());
4718
    }
4719
    p = pStorage->Make( aName, SbxClassType::Property, t );
4720
    if( !bFlag )
4721
    {
4722
        pStorage->ResetFlag( SbxFlagBits::NoModify );
4723
    }
4724
    if( p.is() )
4725
    {
4726
        p->SetFlag( SbxFlagBits::DontStore );
4727
        // from 2.7.1996: HACK because of 'reference can't be saved'
4728
        p->SetFlag( SbxFlagBits::NoModify);
4729
    }
4730
}
4731
4732
4733
// Creates global variable that isn't reinitialised when
4734
// basic is restarted, P=PERSIST (+StringID+Typ)
4735
4736
void SbiRuntime::StepGLOBAL_P( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4737
{
4738
    if( pMod->pImage->bFirstInit )
4739
    {
4740
        StepGLOBAL( nOp1, nOp2 );
4741
    }
4742
}
4743
4744
4745
// Searches for global variable, behavior depends on the fact
4746
// if the variable is initialised for the first time
4747
4748
void SbiRuntime::StepFIND_G( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4749
{
4750
    if( pMod->pImage->bFirstInit )
4751
    {
4752
        // Behave like always during first init
4753
        StepFIND( nOp1, nOp2 );
4754
    }
4755
    else
4756
    {
4757
        // Return dummy variable
4758
        SbxDataType t = static_cast<SbxDataType>(nOp2);
4759
        OUString aName( pImg->GetString( nOp1 & 0x7FFF ) );
4760
4761
        SbxVariable* pDummyVar = new SbxVariable( t );
4762
        pDummyVar->SetName( aName );
4763
        PushVar( pDummyVar );
4764
    }
4765
}
4766
4767
4768
SbxVariable* SbiRuntime::StepSTATIC_Impl(
4769
    OUString const & aName, SbxDataType t, sal_uInt32 nOp2 )
4770
{
4771
    SbxVariable* p = nullptr;
4772
    if ( pMeth )
4773
    {
4774
        SbxArray* pStatics = pMeth->GetStatics();
4775
        if( pStatics && ( pStatics->Find( aName, SbxClassType::DontCare ) == nullptr ) )
4776
        {
4777
            p = new SbxVariable( t );
4778
            if( t != SbxVARIANT )
4779
            {
4780
                p->SetFlag( SbxFlagBits::Fixed );
4781
            }
4782
            p->SetName( aName );
4783
            implHandleSbxFlags( p, t, nOp2 );
4784
            pStatics->Put(p, pStatics->Count());
4785
        }
4786
    }
4787
    return p;
4788
}
4789
// establishing a static variable (+StringID+type)
4790
void SbiRuntime::StepSTATIC( sal_uInt32 nOp1, sal_uInt32 nOp2 )
4791
{
4792
    OUString aName( pImg->GetString( nOp1 ) );
4793
    SbxDataType t = static_cast<SbxDataType>(nOp2 & 0xffff);
4794
    StepSTATIC_Impl( aName, t, nOp2 );
4795
}
4796
4797
#endif
4798
4799
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */