Bug Summary

File:home/maarten/src/libreoffice/core/basic/source/runtime/runtime.cxx
Warning:line 683, column 29
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

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