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