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