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