4768 lines
146 KiB
C++
4768 lines
146 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <algorithm>
|
|
#include <string_view>
|
|
#include <unordered_map>
|
|
|
|
#include <com/sun/star/beans/XPropertySet.hpp>
|
|
#include <com/sun/star/container/XEnumerationAccess.hpp>
|
|
#include <com/sun/star/container/XIndexAccess.hpp>
|
|
#include <com/sun/star/script/XDefaultMethod.hpp>
|
|
#include <com/sun/star/uno/Any.hxx>
|
|
#include <com/sun/star/util/SearchAlgorithms2.hpp>
|
|
|
|
#include <comphelper/processfactory.hxx>
|
|
#include <comphelper/string.hxx>
|
|
#include <o3tl/safeint.hxx>
|
|
#include <sal/log.hxx>
|
|
|
|
#include <tools/wldcrd.hxx>
|
|
#include <comphelper/diagnose_ex.hxx>
|
|
|
|
#include <utility>
|
|
#include <vcl/svapp.hxx>
|
|
#include <vcl/settings.hxx>
|
|
|
|
#include <rtl/math.hxx>
|
|
#include <rtl/ustrbuf.hxx>
|
|
#include <rtl/character.hxx>
|
|
|
|
#include <svl/numformat.hxx>
|
|
#include <svl/zforlist.hxx>
|
|
|
|
#include <unicode/regex.h>
|
|
|
|
#include <basic/sbuno.hxx>
|
|
|
|
#include <codegen.hxx>
|
|
#include "comenumwrapper.hxx"
|
|
#include "ddectrl.hxx"
|
|
#include "dllmgr.hxx"
|
|
#include <errobject.hxx>
|
|
#include <image.hxx>
|
|
#include <iosys.hxx>
|
|
#include <opcodes.hxx>
|
|
#include <runtime.hxx>
|
|
#include <sb.hxx>
|
|
#include <sbintern.hxx>
|
|
#include <sbprop.hxx>
|
|
#include <sbunoobj.hxx>
|
|
#include <basic/codecompletecache.hxx>
|
|
#include <memory>
|
|
|
|
using com::sun::star::uno::Reference;
|
|
|
|
using namespace com::sun::star::uno;
|
|
using namespace com::sun::star::container;
|
|
using namespace com::sun::star::lang;
|
|
using namespace com::sun::star::script;
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
#if HAVE_FEATURE_SCRIPTING
|
|
|
|
static void lcl_clearImpl( SbxVariableRef const & refVar, SbxDataType const & eType );
|
|
static void lcl_eraseImpl( SbxVariableRef const & refVar, bool bVBAEnabled );
|
|
|
|
namespace
|
|
{
|
|
class ScopedWritableGuard
|
|
{
|
|
public:
|
|
ScopedWritableGuard(const SbxVariableRef& rVar, bool bMakeWritable)
|
|
: m_rVar(bMakeWritable && !rVar->CanWrite() ? rVar : SbxVariableRef())
|
|
{
|
|
if (m_rVar)
|
|
{
|
|
m_rVar->SetFlag(SbxFlagBits::Write);
|
|
}
|
|
}
|
|
~ScopedWritableGuard()
|
|
{
|
|
if (m_rVar)
|
|
{
|
|
m_rVar->ResetFlag(SbxFlagBits::Write);
|
|
}
|
|
}
|
|
|
|
private:
|
|
SbxVariableRef m_rVar;
|
|
};
|
|
}
|
|
|
|
bool SbiRuntime::isVBAEnabled()
|
|
{
|
|
SbiInstance* pInst = GetSbData()->pInst;
|
|
return pInst && pInst->pRun && pInst->pRun->bVBAEnabled;
|
|
}
|
|
|
|
void StarBASIC::SetVBAEnabled( bool bEnabled )
|
|
{
|
|
if ( bDocBasic )
|
|
{
|
|
bVBAEnabled = bEnabled;
|
|
}
|
|
}
|
|
|
|
bool StarBASIC::isVBAEnabled() const
|
|
{
|
|
return bDocBasic && (bVBAEnabled || SbiRuntime::isVBAEnabled());
|
|
}
|
|
|
|
struct SbiArgv { // Argv stack:
|
|
SbxArrayRef refArgv; // Argv
|
|
short nArgc; // Argc
|
|
|
|
SbiArgv(SbxArrayRef refArgv_, short nArgc_) :
|
|
refArgv(std::move(refArgv_)),
|
|
nArgc(nArgc_) {}
|
|
};
|
|
|
|
struct SbiGosub { // GOSUB-Stack:
|
|
const sal_uInt8* pCode; // Return-Pointer
|
|
sal_uInt16 nStartForLvl; // #118235: For Level in moment of gosub
|
|
|
|
SbiGosub(const sal_uInt8* pCode_, sal_uInt16 nStartForLvl_) :
|
|
pCode(pCode_),
|
|
nStartForLvl(nStartForLvl_) {}
|
|
};
|
|
|
|
const SbiRuntime::pStep0 SbiRuntime::aStep0[] = { // all opcodes without operands
|
|
&SbiRuntime::StepNOP,
|
|
&SbiRuntime::StepEXP,
|
|
&SbiRuntime::StepMUL,
|
|
&SbiRuntime::StepDIV,
|
|
&SbiRuntime::StepMOD,
|
|
&SbiRuntime::StepPLUS,
|
|
&SbiRuntime::StepMINUS,
|
|
&SbiRuntime::StepNEG,
|
|
&SbiRuntime::StepEQ,
|
|
&SbiRuntime::StepNE,
|
|
&SbiRuntime::StepLT,
|
|
&SbiRuntime::StepGT,
|
|
&SbiRuntime::StepLE,
|
|
&SbiRuntime::StepGE,
|
|
&SbiRuntime::StepIDIV,
|
|
&SbiRuntime::StepAND,
|
|
&SbiRuntime::StepOR,
|
|
&SbiRuntime::StepXOR,
|
|
&SbiRuntime::StepEQV,
|
|
&SbiRuntime::StepIMP,
|
|
&SbiRuntime::StepNOT,
|
|
&SbiRuntime::StepCAT,
|
|
|
|
&SbiRuntime::StepLIKE,
|
|
&SbiRuntime::StepIS,
|
|
// load/save
|
|
&SbiRuntime::StepARGC, // establish new Argv
|
|
&SbiRuntime::StepARGV, // TOS ==> current Argv
|
|
&SbiRuntime::StepINPUT, // Input ==> TOS
|
|
&SbiRuntime::StepLINPUT, // Line Input ==> TOS
|
|
&SbiRuntime::StepGET, // touch TOS
|
|
&SbiRuntime::StepSET, // save object TOS ==> TOS-1
|
|
&SbiRuntime::StepPUT, // TOS ==> TOS-1
|
|
&SbiRuntime::StepPUTC, // TOS ==> TOS-1, then ReadOnly
|
|
&SbiRuntime::StepDIM, // DIM
|
|
&SbiRuntime::StepREDIM, // REDIM
|
|
&SbiRuntime::StepREDIMP, // REDIM PRESERVE
|
|
&SbiRuntime::StepERASE, // delete TOS
|
|
// branch
|
|
&SbiRuntime::StepSTOP, // program end
|
|
&SbiRuntime::StepINITFOR, // initialize FOR-Variable
|
|
&SbiRuntime::StepNEXT, // increment FOR-Variable
|
|
&SbiRuntime::StepCASE, // beginning CASE
|
|
&SbiRuntime::StepENDCASE, // end CASE
|
|
&SbiRuntime::StepSTDERROR, // standard error handling
|
|
&SbiRuntime::StepNOERROR, // no error handling
|
|
&SbiRuntime::StepLEAVE, // leave UP
|
|
// E/A
|
|
&SbiRuntime::StepCHANNEL, // TOS = channel number
|
|
&SbiRuntime::StepPRINT, // print TOS
|
|
&SbiRuntime::StepPRINTF, // print TOS in field
|
|
&SbiRuntime::StepWRITE, // write TOS
|
|
&SbiRuntime::StepRENAME, // Rename Tos+1 to Tos
|
|
&SbiRuntime::StepPROMPT, // define Input Prompt from TOS
|
|
&SbiRuntime::StepRESTART, // Set restart point
|
|
&SbiRuntime::StepCHANNEL0, // set E/A-channel 0
|
|
&SbiRuntime::StepEMPTY, // empty expression on stack
|
|
&SbiRuntime::StepERROR, // TOS = error code
|
|
&SbiRuntime::StepLSET, // save object TOS ==> TOS-1
|
|
&SbiRuntime::StepRSET, // save object TOS ==> TOS-1
|
|
&SbiRuntime::StepREDIMP_ERASE,// Copy array object for REDIMP
|
|
&SbiRuntime::StepINITFOREACH,// Init for each loop
|
|
&SbiRuntime::StepVBASET,// vba-like set statement
|
|
&SbiRuntime::StepERASE_CLEAR,// vba-like set statement
|
|
&SbiRuntime::StepARRAYACCESS,// access TOS as array
|
|
&SbiRuntime::StepBYVAL, // access TOS as array
|
|
};
|
|
|
|
const SbiRuntime::pStep1 SbiRuntime::aStep1[] = { // all opcodes with one operand
|
|
&SbiRuntime::StepLOADNC, // loading a numeric constant (+ID)
|
|
&SbiRuntime::StepLOADSC, // loading a string constant (+ID)
|
|
&SbiRuntime::StepLOADI, // Immediate Load (+value)
|
|
&SbiRuntime::StepARGN, // save a named Args in Argv (+StringID)
|
|
&SbiRuntime::StepPAD, // bring string to a definite length (+length)
|
|
// branches
|
|
&SbiRuntime::StepJUMP, // jump (+Target)
|
|
&SbiRuntime::StepJUMPT, // evaluate TOS, conditional jump (+Target)
|
|
&SbiRuntime::StepJUMPF, // evaluate TOS, conditional jump (+Target)
|
|
&SbiRuntime::StepONJUMP, // evaluate TOS, jump into JUMP-table (+MaxVal)
|
|
&SbiRuntime::StepGOSUB, // UP-call (+Target)
|
|
&SbiRuntime::StepRETURN, // UP-return (+0 or Target)
|
|
&SbiRuntime::StepTESTFOR, // check FOR-variable, increment (+Endlabel)
|
|
&SbiRuntime::StepCASETO, // Tos+1 <= Case <= Tos), 2xremove (+Target)
|
|
&SbiRuntime::StepERRHDL, // error handler (+Offset)
|
|
&SbiRuntime::StepRESUME, // resume after errors (+0 or 1 or Label)
|
|
// E/A
|
|
&SbiRuntime::StepCLOSE, // (+channel/0)
|
|
&SbiRuntime::StepPRCHAR, // (+char)
|
|
// management
|
|
&SbiRuntime::StepSETCLASS, // check set + class names (+StringId)
|
|
&SbiRuntime::StepTESTCLASS, // Check TOS class (+StringId)
|
|
&SbiRuntime::StepLIB, // lib for declare-call (+StringId)
|
|
&SbiRuntime::StepBASED, // TOS is incremented by BASE, BASE is pushed before
|
|
&SbiRuntime::StepARGTYP, // convert last parameter in Argv (+Type)
|
|
&SbiRuntime::StepVBASETCLASS,// vba-like set statement
|
|
};
|
|
|
|
const SbiRuntime::pStep2 SbiRuntime::aStep2[] = {// all opcodes with two operands
|
|
&SbiRuntime::StepRTL, // load from RTL (+StringID+Typ)
|
|
&SbiRuntime::StepFIND, // load (+StringID+Typ)
|
|
&SbiRuntime::StepELEM, // load element (+StringID+Typ)
|
|
&SbiRuntime::StepPARAM, // Parameter (+Offset+Typ)
|
|
// branches
|
|
&SbiRuntime::StepCALL, // Declare-Call (+StringID+Typ)
|
|
&SbiRuntime::StepCALLC, // CDecl-Declare-Call (+StringID+Typ)
|
|
&SbiRuntime::StepCASEIS, // Case-Test (+Test-Opcode+False-Target)
|
|
// management
|
|
&SbiRuntime::StepSTMNT, // beginning of a statement (+Line+Col)
|
|
// E/A
|
|
&SbiRuntime::StepOPEN, // (+StreamMode+Flags)
|
|
// Objects
|
|
&SbiRuntime::StepLOCAL, // define local variable (+StringId+Typ)
|
|
&SbiRuntime::StepPUBLIC, // module global variable (+StringID+Typ)
|
|
&SbiRuntime::StepGLOBAL, // define global variable (+StringID+Typ)
|
|
&SbiRuntime::StepCREATE, // create object (+StringId+StringId)
|
|
&SbiRuntime::StepSTATIC, // static variable (+StringId+StringId)
|
|
&SbiRuntime::StepTCREATE, // user-defined objects (+StringId+StringId)
|
|
&SbiRuntime::StepDCREATE, // create object-array (+StringID+StringID)
|
|
&SbiRuntime::StepGLOBAL_P, // define global variable which is not overwritten
|
|
// by the Basic on a restart (+StringID+Typ)
|
|
&SbiRuntime::StepFIND_G, // finds global variable with special treatment because of _GLOBAL_P
|
|
&SbiRuntime::StepDCREATE_REDIMP, // redimension object array (+StringID+StringID)
|
|
&SbiRuntime::StepFIND_CM, // Search inside a class module (CM) to enable global search in time
|
|
&SbiRuntime::StepPUBLIC_P, // Search inside a class module (CM) to enable global search in time
|
|
&SbiRuntime::StepFIND_STATIC, // Search inside a class module (CM) to enable global search in time
|
|
};
|
|
|
|
|
|
// SbiRTLData
|
|
|
|
SbiRTLData::SbiRTLData()
|
|
: nDirFlags(SbAttributes::NORMAL)
|
|
, nCurDirPos(0)
|
|
{
|
|
}
|
|
|
|
SbiRTLData::~SbiRTLData()
|
|
{
|
|
}
|
|
|
|
// SbiInstance
|
|
|
|
// 16.10.96: #31460 new concept for StepInto/Over/Out
|
|
// The decision whether StepPoint shall be called is done with the help of
|
|
// the CallLevel. It's stopped when the current CallLevel is <= nBreakCallLvl.
|
|
// The current CallLevel can never be smaller than 1, as it's also incremented
|
|
// during the call of a method (also main). Therefore a BreakCallLvl from 0
|
|
// means that the program isn't stopped at all.
|
|
// (also have a look at: step2.cxx, SbiRuntime::StepSTMNT() )
|
|
|
|
|
|
void SbiInstance::CalcBreakCallLevel( BasicDebugFlags nFlags )
|
|
{
|
|
|
|
nFlags &= ~BasicDebugFlags::Break;
|
|
|
|
sal_uInt16 nRet;
|
|
if (nFlags == BasicDebugFlags::StepInto) {
|
|
nRet = nCallLvl + 1; // CallLevel+1 is also stopped
|
|
} else if (nFlags == (BasicDebugFlags::StepOver | BasicDebugFlags::StepInto)) {
|
|
nRet = nCallLvl; // current CallLevel is stopped
|
|
} else if (nFlags == BasicDebugFlags::StepOut) {
|
|
nRet = nCallLvl - 1; // smaller CallLevel is stopped
|
|
} else {
|
|
// Basic-IDE returns 0 instead of BasicDebugFlags::Continue, so also default=continue
|
|
nRet = 0; // CallLevel is always > 0 -> no StepPoint
|
|
}
|
|
nBreakCallLvl = nRet; // take result
|
|
}
|
|
|
|
SbiInstance::SbiInstance( StarBASIC* p )
|
|
: pIosys(new SbiIoSystem)
|
|
, pDdeCtrl(new SbiDdeControl)
|
|
, pBasic(p)
|
|
, meFormatterLangType(LANGUAGE_DONTKNOW)
|
|
, meFormatterDateOrder(DateOrder::YMD)
|
|
, nStdDateIdx(0)
|
|
, nStdTimeIdx(0)
|
|
, nStdDateTimeIdx(0)
|
|
, nErr(0)
|
|
, nErl(0)
|
|
, bReschedule(true)
|
|
, bCompatibility(false)
|
|
, pRun(nullptr)
|
|
, nCallLvl(0)
|
|
, nBreakCallLvl(0)
|
|
{
|
|
}
|
|
|
|
SbiInstance::~SbiInstance()
|
|
{
|
|
while( pRun )
|
|
{
|
|
SbiRuntime* p = pRun->pNext;
|
|
delete pRun;
|
|
pRun = p;
|
|
}
|
|
|
|
try
|
|
{
|
|
int nSize = ComponentVector.size();
|
|
if( nSize )
|
|
{
|
|
for( int i = nSize - 1 ; i >= 0 ; --i )
|
|
{
|
|
Reference< XComponent > xDlgComponent = ComponentVector[i];
|
|
if( xDlgComponent.is() )
|
|
xDlgComponent->dispose();
|
|
}
|
|
}
|
|
}
|
|
catch( const Exception& )
|
|
{
|
|
TOOLS_WARN_EXCEPTION("basic", "SbiInstance::~SbiInstance: caught an exception while disposing the components" );
|
|
}
|
|
}
|
|
|
|
SbiDllMgr* SbiInstance::GetDllMgr()
|
|
{
|
|
if( !pDllMgr )
|
|
{
|
|
pDllMgr.reset(new SbiDllMgr);
|
|
}
|
|
return pDllMgr.get();
|
|
}
|
|
|
|
#endif
|
|
|
|
// #39629 create NumberFormatter with the help of a static method now
|
|
std::shared_ptr<SvNumberFormatter> const & SbiInstance::GetNumberFormatter()
|
|
{
|
|
LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
|
|
SvtSysLocale aSysLocale;
|
|
DateOrder eDate = aSysLocale.GetLocaleData().getDateOrder();
|
|
if( pNumberFormatter )
|
|
{
|
|
if( eLangType != meFormatterLangType ||
|
|
eDate != meFormatterDateOrder )
|
|
{
|
|
pNumberFormatter.reset();
|
|
}
|
|
}
|
|
meFormatterLangType = eLangType;
|
|
meFormatterDateOrder = eDate;
|
|
if( !pNumberFormatter )
|
|
{
|
|
pNumberFormatter = PrepareNumberFormatter( nStdDateIdx, nStdTimeIdx, nStdDateTimeIdx,
|
|
&meFormatterLangType, &meFormatterDateOrder);
|
|
}
|
|
return pNumberFormatter;
|
|
}
|
|
|
|
// #39629 offer NumberFormatter static too
|
|
std::shared_ptr<SvNumberFormatter> SbiInstance::PrepareNumberFormatter( sal_uInt32 &rnStdDateIdx,
|
|
sal_uInt32 &rnStdTimeIdx, sal_uInt32 &rnStdDateTimeIdx,
|
|
LanguageType const * peFormatterLangType, DateOrder const * peFormatterDateOrder )
|
|
{
|
|
LanguageType eLangType;
|
|
if( peFormatterLangType )
|
|
{
|
|
eLangType = *peFormatterLangType;
|
|
}
|
|
else
|
|
{
|
|
eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
|
|
}
|
|
DateOrder eDate;
|
|
if( peFormatterDateOrder )
|
|
{
|
|
eDate = *peFormatterDateOrder;
|
|
}
|
|
else
|
|
{
|
|
SvtSysLocale aSysLocale;
|
|
eDate = aSysLocale.GetLocaleData().getDateOrder();
|
|
}
|
|
|
|
std::shared_ptr<SvNumberFormatter> pNumberFormatter =
|
|
std::make_shared<SvNumberFormatter>( comphelper::getProcessComponentContext(), eLangType );
|
|
|
|
// Several parser methods pass SvNumberFormatter::IsNumberFormat() a number
|
|
// format index to parse against. Tell the formatter the proper date
|
|
// evaluation order, which also determines the date acceptance patterns to
|
|
// use if a format was passed. NF_EVALDATEFORMAT_FORMAT restricts to the
|
|
// format's locale's date patterns/order (no init/system locale match
|
|
// tried) and falls back to NF_EVALDATEFORMAT_INTL if no specific (i.e. 0)
|
|
// (or an unknown) format index was passed.
|
|
pNumberFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_FORMAT);
|
|
|
|
sal_Int32 nCheckPos = 0;
|
|
SvNumFormatType nType;
|
|
rnStdTimeIdx = pNumberFormatter->GetStandardFormat( SvNumFormatType::TIME, eLangType );
|
|
|
|
// the formatter's standard templates have only got a two-digit date
|
|
// -> registering an own format
|
|
|
|
// HACK, because the numberformatter doesn't swap the place holders
|
|
// for month, day and year according to the system setting.
|
|
// Problem: Print Year(Date) under engl. BS
|
|
// also have a look at: basic/source/sbx/sbxdate.cxx
|
|
|
|
OUString aDateStr;
|
|
switch( eDate )
|
|
{
|
|
default:
|
|
case DateOrder::MDY: aDateStr = "MM/DD/YYYY"; break;
|
|
case DateOrder::DMY: aDateStr = "DD/MM/YYYY"; break;
|
|
case DateOrder::YMD: aDateStr = "YYYY/MM/DD"; break;
|
|
}
|
|
OUString aStr( aDateStr ); // PutandConvertEntry() modifies string!
|
|
pNumberFormatter->PutandConvertEntry( aStr, nCheckPos, nType,
|
|
rnStdDateIdx, LANGUAGE_ENGLISH_US, eLangType, true);
|
|
nCheckPos = 0;
|
|
aDateStr += " HH:MM:SS";
|
|
aStr = aDateStr;
|
|
pNumberFormatter->PutandConvertEntry( aStr, nCheckPos, nType,
|
|
rnStdDateTimeIdx, LANGUAGE_ENGLISH_US, eLangType, true);
|
|
return pNumberFormatter;
|
|
}
|
|
|
|
#if HAVE_FEATURE_SCRIPTING
|
|
|
|
// Let engine run. If Flags == BasicDebugFlags::Continue, take Flags over
|
|
|
|
void SbiInstance::Stop()
|
|
{
|
|
for( SbiRuntime* p = pRun; p; p = p->pNext )
|
|
{
|
|
p->Stop();
|
|
}
|
|
}
|
|
|
|
// Allows Basic IDE to set watch mode to suppress errors
|
|
static bool bWatchMode = false;
|
|
|
|
void setBasicWatchMode( bool bOn )
|
|
{
|
|
bWatchMode = bOn;
|
|
}
|
|
|
|
void SbiInstance::Error( ErrCode n )
|
|
{
|
|
Error( n, OUString() );
|
|
}
|
|
|
|
void SbiInstance::Error( ErrCode n, const OUString& rMsg )
|
|
{
|
|
if( !bWatchMode )
|
|
{
|
|
aErrorMsg = rMsg;
|
|
pRun->Error( n );
|
|
}
|
|
}
|
|
|
|
void SbiInstance::ErrorVB( sal_Int32 nVBNumber, const OUString& rMsg )
|
|
{
|
|
if( !bWatchMode )
|
|
{
|
|
ErrCode n = StarBASIC::GetSfxFromVBError( static_cast< sal_uInt16 >( nVBNumber ) );
|
|
if ( !n )
|
|
{
|
|
n = ErrCode(nVBNumber); // force orig number, probably should have a specific table of vb ( localized ) errors
|
|
}
|
|
aErrorMsg = rMsg;
|
|
SbiRuntime::translateErrorToVba( n, aErrorMsg );
|
|
|
|
pRun->Error( ERRCODE_BASIC_COMPAT, true/*bVBATranslationAlreadyDone*/ );
|
|
}
|
|
}
|
|
|
|
void SbiInstance::setErrorVB( sal_Int32 nVBNumber )
|
|
{
|
|
ErrCode n = StarBASIC::GetSfxFromVBError( static_cast< sal_uInt16 >( nVBNumber ) );
|
|
if( !n )
|
|
{
|
|
n = ErrCode(nVBNumber); // force orig number, probably should have a specific table of vb ( localized ) errors
|
|
}
|
|
aErrorMsg = OUString();
|
|
SbiRuntime::translateErrorToVba( n, aErrorMsg );
|
|
|
|
nErr = n;
|
|
}
|
|
|
|
|
|
void SbiInstance::FatalError( ErrCode n )
|
|
{
|
|
pRun->FatalError( n );
|
|
}
|
|
|
|
void SbiInstance::FatalError( ErrCode _errCode, const OUString& _details )
|
|
{
|
|
pRun->FatalError( _errCode, _details );
|
|
}
|
|
|
|
void SbiInstance::Abort()
|
|
{
|
|
StarBASIC* pErrBasic = GetCurrentBasic( pBasic );
|
|
pErrBasic->RTError( nErr, aErrorMsg, pRun->nLine, pRun->nCol1, pRun->nCol2 );
|
|
StarBASIC::Stop();
|
|
}
|
|
|
|
// can be unequal to pRTBasic
|
|
StarBASIC* GetCurrentBasic( StarBASIC* pRTBasic )
|
|
{
|
|
StarBASIC* pCurBasic = pRTBasic;
|
|
SbModule* pActiveModule = StarBASIC::GetActiveModule();
|
|
if( pActiveModule )
|
|
{
|
|
SbxObject* pParent = pActiveModule->GetParent();
|
|
if (StarBASIC *pBasic = dynamic_cast<StarBASIC*>(pParent))
|
|
pCurBasic = pBasic;
|
|
}
|
|
return pCurBasic;
|
|
}
|
|
|
|
SbModule* SbiInstance::GetActiveModule()
|
|
{
|
|
if( pRun )
|
|
{
|
|
return pRun->GetModule();
|
|
}
|
|
else
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
SbMethod* SbiInstance::GetCaller( sal_uInt16 nLevel )
|
|
{
|
|
SbiRuntime* p = pRun;
|
|
while( nLevel-- && p )
|
|
{
|
|
p = p->pNext;
|
|
}
|
|
return p ? p->GetCaller() : nullptr;
|
|
}
|
|
|
|
// SbiInstance
|
|
|
|
// Attention: pMeth can also be NULL (on a call of the init-code)
|
|
|
|
SbiRuntime::SbiRuntime( SbModule* pm, SbMethod* pe, sal_uInt32 nStart )
|
|
: rBasic( *static_cast<StarBASIC*>(pm->pParent) ), pInst( GetSbData()->pInst ),
|
|
pMod( pm ), pMeth( pe ), pImg( pMod->pImage.get() )
|
|
{
|
|
nFlags = pe ? pe->GetDebugFlags() : BasicDebugFlags::NONE;
|
|
pIosys = pInst->GetIoSystem();
|
|
pCode =
|
|
pStmnt = pImg->GetCode() + nStart;
|
|
refExprStk = new SbxArray;
|
|
SetVBAEnabled( pMod->IsVBASupport() );
|
|
SetParameters( pe ? pe->GetParameters() : nullptr );
|
|
}
|
|
|
|
SbiRuntime::~SbiRuntime()
|
|
{
|
|
ClearArgvStack();
|
|
ClearForStack();
|
|
}
|
|
|
|
void SbiRuntime::SetVBAEnabled(bool bEnabled )
|
|
{
|
|
bVBAEnabled = bEnabled;
|
|
if ( bVBAEnabled )
|
|
{
|
|
if ( pMeth )
|
|
{
|
|
mpExtCaller = pMeth->mCaller;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mpExtCaller = nullptr;
|
|
}
|
|
}
|
|
|
|
// tdf#79426, tdf#125180 - adds the information about a missing parameter
|
|
void SbiRuntime::SetIsMissing( SbxVariable* pVar )
|
|
{
|
|
SbxInfo* pInfo = pVar->GetInfo() ? pVar->GetInfo() : new SbxInfo();
|
|
pInfo->AddParam( pVar->GetName(), SbxMISSING, pVar->GetFlags() );
|
|
pVar->SetInfo( pInfo );
|
|
}
|
|
|
|
// tdf#79426, tdf#125180 - checks if a variable contains the information about a missing parameter
|
|
bool SbiRuntime::IsMissing( SbxVariable* pVar, sal_uInt16 nIdx )
|
|
{
|
|
return pVar->GetInfo() && pVar->GetInfo()->GetParam( nIdx ) && pVar->GetInfo()->GetParam( nIdx )->eType & SbxMISSING;
|
|
}
|
|
|
|
// Construction of the parameter list. All ByRef-parameters are directly
|
|
// taken over; copies of ByVal-parameters are created. If a particular
|
|
// data type is requested, it is converted.
|
|
|
|
void SbiRuntime::SetParameters( SbxArray* pParams )
|
|
{
|
|
refParams = new SbxArray;
|
|
// for the return value
|
|
refParams->Put(pMeth, 0);
|
|
|
|
SbxInfo* pInfo = pMeth ? pMeth->GetInfo() : nullptr;
|
|
sal_uInt32 nParamCount = pParams ? pParams->Count() : 1;
|
|
assert(nParamCount <= std::numeric_limits<sal_uInt16>::max());
|
|
if( nParamCount > 1 )
|
|
{
|
|
for( sal_uInt32 i = 1 ; i < nParamCount ; i++ )
|
|
{
|
|
const SbxParamInfo* p = pInfo ? pInfo->GetParam( sal::static_int_cast<sal_uInt16>(i) ) : nullptr;
|
|
|
|
// #111897 ParamArray
|
|
if( p && (p->nUserData & PARAM_INFO_PARAMARRAY) != 0 )
|
|
{
|
|
SbxDimArray* pArray = new SbxDimArray( SbxVARIANT );
|
|
sal_uInt32 nParamArrayParamCount = nParamCount - i;
|
|
pArray->unoAddDim(0, nParamArrayParamCount - 1);
|
|
for (sal_uInt32 j = i; j < nParamCount ; ++j)
|
|
{
|
|
SbxVariable* v = pParams->Get(j);
|
|
sal_Int32 aDimIndex[1];
|
|
aDimIndex[0] = j - i;
|
|
pArray->Put(v, aDimIndex);
|
|
}
|
|
SbxVariable* pArrayVar = new SbxVariable( SbxVARIANT );
|
|
pArrayVar->SetFlag( SbxFlagBits::ReadWrite );
|
|
pArrayVar->PutObject( pArray );
|
|
refParams->Put(pArrayVar, i);
|
|
|
|
// Block ParamArray for missing parameter
|
|
pInfo = nullptr;
|
|
break;
|
|
}
|
|
|
|
SbxVariable* v = pParams->Get(i);
|
|
assert(v);
|
|
// methods are always byval!
|
|
bool bByVal = dynamic_cast<const SbxMethod *>(v) != nullptr;
|
|
SbxDataType t = v->GetType();
|
|
bool bTargetTypeIsArray = false;
|
|
if( p )
|
|
{
|
|
bByVal |= ( p->eType & SbxBYREF ) == 0;
|
|
// tdf#79426, tdf#125180 - don't convert missing arguments to the requested parameter type
|
|
if ( !IsMissing( v, 1 ) )
|
|
{
|
|
t = static_cast<SbxDataType>( p->eType & 0x0FFF );
|
|
}
|
|
|
|
if( !bByVal && t != SbxVARIANT &&
|
|
(!v->IsFixed() || static_cast<SbxDataType>(v->GetType() & 0x0FFF ) != t) )
|
|
{
|
|
bByVal = true;
|
|
}
|
|
|
|
bTargetTypeIsArray = (p->nUserData & PARAM_INFO_WITHBRACKETS) != 0;
|
|
}
|
|
if( bByVal )
|
|
{
|
|
// tdf#79426, tdf#125180 - don't convert missing arguments to the requested parameter type
|
|
if( bTargetTypeIsArray && !IsMissing( v, 1 ) )
|
|
{
|
|
t = SbxOBJECT;
|
|
}
|
|
SbxVariable* v2 = new SbxVariable( t );
|
|
v2->SetFlag( SbxFlagBits::ReadWrite );
|
|
// tdf#79426, tdf#125180 - if parameter was missing, readd additional information about a missing parameter
|
|
if ( IsMissing( v, 1 ) )
|
|
{
|
|
SetIsMissing( v2 );
|
|
}
|
|
*v2 = *v;
|
|
refParams->Put(v2, i);
|
|
}
|
|
else
|
|
{
|
|
// tdf#79426, tdf#125180 - don't convert missing arguments to the requested parameter type
|
|
if( t != SbxVARIANT && !IsMissing( v, 1 ) && t != ( v->GetType() & 0x0FFF ) )
|
|
{
|
|
if( p && (p->eType & SbxARRAY) )
|
|
{
|
|
Error( ERRCODE_BASIC_CONVERSION );
|
|
}
|
|
else
|
|
{
|
|
v->Convert( t );
|
|
}
|
|
}
|
|
refParams->Put(v, i);
|
|
}
|
|
if( p )
|
|
{
|
|
refParams->PutAlias(p->aName, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ParamArray for missing parameter
|
|
if( !pInfo )
|
|
return;
|
|
|
|
// #111897 Check first missing parameter for ParamArray
|
|
const SbxParamInfo* p = pInfo->GetParam(sal::static_int_cast<sal_uInt16>(nParamCount));
|
|
if( p && (p->nUserData & PARAM_INFO_PARAMARRAY) != 0 )
|
|
{
|
|
SbxDimArray* pArray = new SbxDimArray( SbxVARIANT );
|
|
pArray->unoAddDim(0, -1);
|
|
SbxVariable* pArrayVar = new SbxVariable( SbxVARIANT );
|
|
pArrayVar->SetFlag( SbxFlagBits::ReadWrite );
|
|
pArrayVar->PutObject( pArray );
|
|
refParams->Put(pArrayVar, nParamCount);
|
|
}
|
|
}
|
|
|
|
|
|
// execute a P-Code
|
|
|
|
bool SbiRuntime::Step()
|
|
{
|
|
if( bRun )
|
|
{
|
|
static sal_uInt32 nLastTime = osl_getGlobalTimer();
|
|
|
|
// in any case check casually!
|
|
if( !( ++nOps & 0xF ) && pInst->IsReschedule() )
|
|
{
|
|
sal_uInt32 nTime = osl_getGlobalTimer();
|
|
if (nTime - nLastTime > 5) // 20 ms
|
|
{
|
|
nLastTime = nTime;
|
|
Application::Reschedule();
|
|
}
|
|
}
|
|
|
|
// #i48868 blocked by next call level?
|
|
while( bBlocked )
|
|
{
|
|
if( pInst->IsReschedule() ) // And what if not? Busy loop?
|
|
{
|
|
Application::Reschedule();
|
|
nLastTime = osl_getGlobalTimer();
|
|
}
|
|
}
|
|
|
|
SbiOpcode eOp = static_cast<SbiOpcode>( *pCode++ );
|
|
sal_uInt32 nOp1;
|
|
if (eOp <= SbiOpcode::SbOP0_END)
|
|
{
|
|
(this->*( aStep0[ int(eOp) ] ) )();
|
|
}
|
|
else if (eOp >= SbiOpcode::SbOP1_START && eOp <= SbiOpcode::SbOP1_END)
|
|
{
|
|
nOp1 = *pCode++; nOp1 |= *pCode++ << 8; nOp1 |= *pCode++ << 16; nOp1 |= *pCode++ << 24;
|
|
|
|
(this->*( aStep1[ int(eOp) - int(SbiOpcode::SbOP1_START) ] ) )( nOp1 );
|
|
}
|
|
else if (eOp >= SbiOpcode::SbOP2_START && eOp <= SbiOpcode::SbOP2_END)
|
|
{
|
|
nOp1 = *pCode++; nOp1 |= *pCode++ << 8; nOp1 |= *pCode++ << 16; nOp1 |= *pCode++ << 24;
|
|
sal_uInt32 nOp2 = *pCode++; nOp2 |= *pCode++ << 8; nOp2 |= *pCode++ << 16; nOp2 |= *pCode++ << 24;
|
|
(this->*( aStep2[ int(eOp) - int(SbiOpcode::SbOP2_START) ] ) )( nOp1, nOp2 );
|
|
}
|
|
else
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
}
|
|
|
|
ErrCode nErrCode = SbxBase::GetError();
|
|
Error( nErrCode.IgnoreWarning() );
|
|
|
|
// from 13.2.1997, new error handling:
|
|
// ATTENTION: nError can be set already even if !nErrCode
|
|
// since nError can now also be set from other RT-instances
|
|
|
|
if( nError )
|
|
{
|
|
SbxBase::ResetError();
|
|
}
|
|
|
|
// from 15.3.96: display errors only if BASIC is still active
|
|
// (especially not after compiler errors at the runtime)
|
|
if( nError && bRun )
|
|
{
|
|
ErrCode err = nError;
|
|
ClearExprStack();
|
|
nError = ERRCODE_NONE;
|
|
pInst->nErr = err;
|
|
pInst->nErl = nLine;
|
|
pErrCode = pCode;
|
|
pErrStmnt = pStmnt;
|
|
// An error occurred in an error handler
|
|
// force parent handler ( if there is one )
|
|
// to handle the error
|
|
bool bLetParentHandleThis = false;
|
|
|
|
// in the error handler? so std-error
|
|
if ( !bInError )
|
|
{
|
|
bInError = true;
|
|
|
|
if( !bError ) // On Error Resume Next
|
|
{
|
|
StepRESUME( 1 );
|
|
}
|
|
else if( pError ) // On Error Goto ...
|
|
{
|
|
pCode = pError;
|
|
}
|
|
else
|
|
{
|
|
bLetParentHandleThis = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bLetParentHandleThis = true;
|
|
pError = nullptr; //terminate the handler
|
|
}
|
|
if ( bLetParentHandleThis )
|
|
{
|
|
// from 13.2.1997, new error handling:
|
|
// consider superior error handlers
|
|
|
|
// there's no error handler -> find one farther above
|
|
SbiRuntime* pRtErrHdl = nullptr;
|
|
SbiRuntime* pRt = this;
|
|
while( (pRt = pRt->pNext) != nullptr )
|
|
{
|
|
if( !pRt->bError || pRt->pError != nullptr )
|
|
{
|
|
pRtErrHdl = pRt;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if( pRtErrHdl )
|
|
{
|
|
// manipulate all the RTs that are below in the call-stack
|
|
pRt = this;
|
|
do
|
|
{
|
|
pRt->nError = err;
|
|
if( pRt != pRtErrHdl )
|
|
{
|
|
pRt->bRun = false;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
pRt = pRt->pNext;
|
|
}
|
|
while( pRt );
|
|
}
|
|
// no error-hdl found -> old behaviour
|
|
else
|
|
{
|
|
pInst->Abort();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bRun;
|
|
}
|
|
|
|
void SbiRuntime::Error( ErrCode n, bool bVBATranslationAlreadyDone )
|
|
{
|
|
if( !n )
|
|
return;
|
|
|
|
nError = n;
|
|
if( !isVBAEnabled() || bVBATranslationAlreadyDone )
|
|
return;
|
|
|
|
OUString aMsg = pInst->GetErrorMsg();
|
|
sal_Int32 nVBAErrorNumber = translateErrorToVba( nError, aMsg );
|
|
SbxVariable* pSbxErrObjVar = SbxErrObject::getErrObject().get();
|
|
SbxErrObject* pGlobErr = static_cast< SbxErrObject* >( pSbxErrObjVar );
|
|
if( pGlobErr != nullptr )
|
|
{
|
|
pGlobErr->setNumberAndDescription( nVBAErrorNumber, aMsg );
|
|
}
|
|
pInst->aErrorMsg = aMsg;
|
|
nError = ERRCODE_BASIC_COMPAT;
|
|
}
|
|
|
|
void SbiRuntime::Error( ErrCode _errCode, const OUString& _details )
|
|
{
|
|
if ( !_errCode )
|
|
return;
|
|
|
|
// Not correct for class module usage, remove for now
|
|
//OSL_WARN_IF( pInst->pRun != this, "basic", "SbiRuntime::Error: can't propagate the error message details!" );
|
|
if ( pInst->pRun == this )
|
|
{
|
|
pInst->Error( _errCode, _details );
|
|
//OSL_WARN_IF( nError != _errCode, "basic", "SbiRuntime::Error: the instance is expected to propagate the error code back to me!" );
|
|
}
|
|
else
|
|
{
|
|
nError = _errCode;
|
|
}
|
|
}
|
|
|
|
void SbiRuntime::FatalError( ErrCode n )
|
|
{
|
|
StepSTDERROR();
|
|
Error( n );
|
|
}
|
|
|
|
void SbiRuntime::FatalError( ErrCode _errCode, const OUString& _details )
|
|
{
|
|
StepSTDERROR();
|
|
Error( _errCode, _details );
|
|
}
|
|
|
|
sal_Int32 SbiRuntime::translateErrorToVba( ErrCode nError, OUString& rMsg )
|
|
{
|
|
// If a message is defined use that ( in preference to
|
|
// the defined one for the error ) NB #TODO
|
|
// if there is an error defined it more than likely
|
|
// is not the one you want ( some are the same though )
|
|
// we really need a new vba compatible error list
|
|
// tdf#123144 - always translate an error number to a vba error message
|
|
StarBASIC::MakeErrorText( nError, rMsg );
|
|
rMsg = StarBASIC::GetErrorText();
|
|
// no num? most likely then it *is* really a vba err
|
|
sal_uInt16 nVBErrorCode = StarBASIC::GetVBErrorCode( nError );
|
|
sal_Int32 nVBAErrorNumber = ( nVBErrorCode == 0 ) ? sal_uInt32(nError) : nVBErrorCode;
|
|
return nVBAErrorNumber;
|
|
}
|
|
|
|
// Stacks
|
|
|
|
// The expression-stack is available for the continuous evaluation
|
|
// of expressions.
|
|
|
|
void SbiRuntime::PushVar( SbxVariable* pVar )
|
|
{
|
|
if( pVar )
|
|
{
|
|
refExprStk->Put(pVar, nExprLvl++);
|
|
}
|
|
}
|
|
|
|
SbxVariableRef SbiRuntime::PopVar()
|
|
{
|
|
#ifdef DBG_UTIL
|
|
if( !nExprLvl )
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
return new SbxVariable;
|
|
}
|
|
#endif
|
|
SbxVariableRef xVar = refExprStk->Get(--nExprLvl);
|
|
SAL_INFO_IF( xVar->GetName() == "Cells", "basic", "PopVar: Name equals 'Cells'" );
|
|
// methods hold themselves in parameter 0
|
|
if( dynamic_cast<const SbxMethod *>(xVar.get()) != nullptr )
|
|
{
|
|
xVar->SetParameters(nullptr);
|
|
}
|
|
return xVar;
|
|
}
|
|
|
|
void SbiRuntime::ClearExprStack()
|
|
{
|
|
// Attention: Clear() doesn't suffice as methods must be deleted
|
|
while ( nExprLvl )
|
|
{
|
|
PopVar();
|
|
}
|
|
refExprStk->Clear();
|
|
}
|
|
|
|
// Take variable from the expression-stack without removing it
|
|
// n counts from 0
|
|
|
|
SbxVariable* SbiRuntime::GetTOS()
|
|
{
|
|
short n = nExprLvl - 1;
|
|
#ifdef DBG_UTIL
|
|
if( n < 0 )
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
return new SbxVariable;
|
|
}
|
|
#endif
|
|
return refExprStk->Get(static_cast<sal_uInt32>(n));
|
|
}
|
|
|
|
|
|
void SbiRuntime::TOSMakeTemp()
|
|
{
|
|
SbxVariable* p = refExprStk->Get(nExprLvl - 1);
|
|
if ( p->GetType() == SbxEMPTY )
|
|
{
|
|
p->Broadcast( SfxHintId::BasicDataWanted );
|
|
}
|
|
|
|
SbxVariable* pDflt = nullptr;
|
|
if ( bVBAEnabled && ( p->GetType() == SbxOBJECT || p->GetType() == SbxVARIANT ) && ((pDflt = getDefaultProp(p)) != nullptr) )
|
|
{
|
|
pDflt->Broadcast( SfxHintId::BasicDataWanted );
|
|
// replacing new p on stack causes object pointed by
|
|
// pDft->pParent to be deleted, when p2->Compute() is
|
|
// called below pParent is accessed (but it's deleted)
|
|
// so set it to NULL now
|
|
pDflt->SetParent( nullptr );
|
|
p = new SbxVariable( *pDflt );
|
|
p->SetFlag( SbxFlagBits::ReadWrite );
|
|
refExprStk->Put(p, nExprLvl - 1);
|
|
}
|
|
else if( p->GetRefCount() != 1 )
|
|
{
|
|
SbxVariable* pNew = new SbxVariable( *p );
|
|
pNew->SetFlag( SbxFlagBits::ReadWrite );
|
|
refExprStk->Put(pNew, nExprLvl - 1);
|
|
}
|
|
}
|
|
|
|
// the GOSUB-stack collects return-addresses for GOSUBs
|
|
void SbiRuntime::PushGosub( const sal_uInt8* pc )
|
|
{
|
|
if( pGosubStk.size() >= MAXRECURSION )
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_STACK_OVERFLOW );
|
|
}
|
|
pGosubStk.emplace_back(pc, nForLvl);
|
|
}
|
|
|
|
void SbiRuntime::PopGosub()
|
|
{
|
|
if( pGosubStk.empty() )
|
|
{
|
|
Error( ERRCODE_BASIC_NO_GOSUB );
|
|
}
|
|
else
|
|
{
|
|
pCode = pGosubStk.back().pCode;
|
|
pGosubStk.pop_back();
|
|
}
|
|
}
|
|
|
|
// the Argv-stack collects current argument-vectors
|
|
|
|
void SbiRuntime::PushArgv()
|
|
{
|
|
pArgvStk.emplace_back(refArgv, nArgc);
|
|
nArgc = 1;
|
|
refArgv.clear();
|
|
}
|
|
|
|
void SbiRuntime::PopArgv()
|
|
{
|
|
if( !pArgvStk.empty() )
|
|
{
|
|
refArgv = pArgvStk.back().refArgv;
|
|
nArgc = pArgvStk.back().nArgc;
|
|
pArgvStk.pop_back();
|
|
}
|
|
}
|
|
|
|
|
|
void SbiRuntime::ClearArgvStack()
|
|
{
|
|
while( !pArgvStk.empty() )
|
|
{
|
|
PopArgv();
|
|
}
|
|
}
|
|
|
|
// Push of the for-stack. The stack has increment, end, begin and variable.
|
|
// After the creation of the stack-element the stack's empty.
|
|
|
|
void SbiRuntime::PushFor()
|
|
{
|
|
SbiForStack* p = new SbiForStack;
|
|
p->eForType = ForType::To;
|
|
p->pNext = pForStk;
|
|
pForStk = p;
|
|
|
|
p->refInc = PopVar();
|
|
p->refEnd = PopVar();
|
|
if (isVBAEnabled())
|
|
{
|
|
// tdf#150458: only calculate these once, coercing to double
|
|
// tdf#150460: shouldn't we do it in non-VBA / compat mode, too?
|
|
SbxVariableRef incCopy(new SbxVariable(SbxDOUBLE));
|
|
*incCopy = *p->refInc;
|
|
p->refInc = std::move(incCopy);
|
|
SbxVariableRef endCopy(new SbxVariable(SbxDOUBLE));
|
|
*endCopy = *p->refEnd;
|
|
p->refEnd = std::move(endCopy);
|
|
}
|
|
SbxVariableRef xBgn = PopVar();
|
|
p->refVar = PopVar();
|
|
// tdf#85371 - grant explicitly write access to the index variable
|
|
// since it could be the name of a method itself used in the next statement.
|
|
ScopedWritableGuard aGuard(p->refVar, p->refVar.get() == pMeth);
|
|
*(p->refVar) = *xBgn;
|
|
nForLvl++;
|
|
}
|
|
|
|
void SbiRuntime::PushForEach()
|
|
{
|
|
SbiForStack* p = new SbiForStack;
|
|
// Set default value in case of error which is ignored in Resume Next
|
|
p->eForType = ForType::EachArray;
|
|
p->pNext = pForStk;
|
|
pForStk = p;
|
|
|
|
SbxVariableRef xObjVar = PopVar();
|
|
SbxBase* pObj(nullptr);
|
|
if (xObjVar)
|
|
{
|
|
SbxValues v(SbxVARIANT);
|
|
// Here it may retrieve the value, and change the type from SbxEMPTY to SbxOBJECT
|
|
xObjVar->Get(v);
|
|
if (v.eType == SbxOBJECT)
|
|
pObj = v.pObj;
|
|
}
|
|
|
|
if (SbxDimArray* pArray = dynamic_cast<SbxDimArray*>(pObj))
|
|
{
|
|
p->refEnd = reinterpret_cast<SbxVariable*>(pArray);
|
|
|
|
sal_Int32 nDims = pArray->GetDims();
|
|
p->pArrayLowerBounds.reset( new sal_Int32[nDims] );
|
|
p->pArrayUpperBounds.reset( new sal_Int32[nDims] );
|
|
p->pArrayCurIndices.reset( new sal_Int32[nDims] );
|
|
sal_Int32 lBound, uBound;
|
|
for( sal_Int32 i = 0 ; i < nDims ; i++ )
|
|
{
|
|
pArray->GetDim(i + 1, lBound, uBound);
|
|
p->pArrayCurIndices[i] = p->pArrayLowerBounds[i] = lBound;
|
|
p->pArrayUpperBounds[i] = uBound;
|
|
}
|
|
}
|
|
else if (BasicCollection* pCollection = dynamic_cast<BasicCollection*>(pObj))
|
|
{
|
|
p->eForType = ForType::EachCollection;
|
|
p->refEnd = pCollection;
|
|
p->nCurCollectionIndex = 0;
|
|
}
|
|
else if (SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>(pObj))
|
|
{
|
|
// XEnumerationAccess or XIndexAccess?
|
|
Any aAny = pUnoObj->getUnoAny();
|
|
Reference<XIndexAccess> xIndexAccess;
|
|
Reference< XEnumerationAccess > xEnumerationAccess;
|
|
if( aAny >>= xEnumerationAccess )
|
|
{
|
|
p->xEnumeration = xEnumerationAccess->createEnumeration();
|
|
p->eForType = ForType::EachXEnumeration;
|
|
}
|
|
// tdf#130307 - support for each loop for objects exposing XIndexAccess
|
|
else if (aAny >>= xIndexAccess)
|
|
{
|
|
p->eForType = ForType::EachXIndexAccess;
|
|
p->xIndexAccess = std::move(xIndexAccess);
|
|
p->nCurCollectionIndex = 0;
|
|
}
|
|
else if ( isVBAEnabled() && pUnoObj->isNativeCOMObject() )
|
|
{
|
|
uno::Reference< script::XInvocation > xInvocation;
|
|
if ( ( aAny >>= xInvocation ) && xInvocation.is() )
|
|
{
|
|
try
|
|
{
|
|
p->xEnumeration = new ComEnumerationWrapper( xInvocation );
|
|
p->eForType = ForType::EachXEnumeration;
|
|
}
|
|
catch(const uno::Exception& )
|
|
{}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Container variable
|
|
p->refVar = PopVar();
|
|
nForLvl++;
|
|
}
|
|
|
|
|
|
void SbiRuntime::PopFor()
|
|
{
|
|
if( pForStk )
|
|
{
|
|
SbiForStack* p = pForStk;
|
|
pForStk = p->pNext;
|
|
delete p;
|
|
nForLvl--;
|
|
}
|
|
}
|
|
|
|
|
|
void SbiRuntime::ClearForStack()
|
|
{
|
|
while( pForStk )
|
|
{
|
|
PopFor();
|
|
}
|
|
}
|
|
|
|
SbiForStack* SbiRuntime::FindForStackItemForCollection( class BasicCollection const * pCollection )
|
|
{
|
|
for (SbiForStack *p = pForStk; p; p = p->pNext)
|
|
{
|
|
SbxVariable* pVar = p->refEnd.is() ? p->refEnd.get() : nullptr;
|
|
if( p->eForType == ForType::EachCollection
|
|
&& pVar != nullptr
|
|
&& dynamic_cast<BasicCollection*>( pVar) == pCollection )
|
|
{
|
|
return p;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
// DLL-calls
|
|
|
|
void SbiRuntime::DllCall
|
|
( std::u16string_view aFuncName,
|
|
std::u16string_view aDLLName,
|
|
SbxArray* pArgs, // parameter (from index 1, can be NULL)
|
|
SbxDataType eResType, // return value
|
|
bool bCDecl ) // true: according to C-conventions
|
|
{
|
|
SbxVariable* pRes = new SbxVariable( eResType );
|
|
SbiDllMgr* pDllMgr = pInst->GetDllMgr();
|
|
ErrCode nErr = pDllMgr->Call( aFuncName, aDLLName, pArgs, *pRes, bCDecl );
|
|
if( nErr )
|
|
{
|
|
Error( nErr );
|
|
}
|
|
PushVar( pRes );
|
|
}
|
|
|
|
bool SbiRuntime::IsImageFlag( SbiImageFlags n ) const
|
|
{
|
|
return pImg->IsFlag( n );
|
|
}
|
|
|
|
sal_uInt16 SbiRuntime::GetBase() const
|
|
{
|
|
return pImg->GetBase();
|
|
}
|
|
|
|
void SbiRuntime::StepNOP()
|
|
{}
|
|
|
|
void SbiRuntime::StepArith( SbxOperator eOp )
|
|
{
|
|
SbxVariableRef p1 = PopVar();
|
|
TOSMakeTemp();
|
|
SbxVariable* p2 = GetTOS();
|
|
|
|
// tdf#144353 - do not compute any operation with a missing optional variable
|
|
if ((p1->GetType() == SbxERROR && IsMissing(p1.get(), 1))
|
|
|| (p2->GetType() == SbxERROR && IsMissing(p2, 1)))
|
|
{
|
|
Error(ERRCODE_BASIC_NOT_OPTIONAL);
|
|
return;
|
|
}
|
|
|
|
p2->ResetFlag( SbxFlagBits::Fixed );
|
|
p2->Compute( eOp, *p1 );
|
|
|
|
checkArithmeticOverflow( p2 );
|
|
}
|
|
|
|
void SbiRuntime::StepUnary( SbxOperator eOp )
|
|
{
|
|
TOSMakeTemp();
|
|
SbxVariable* p = GetTOS();
|
|
// tdf#144353 - do not compute any operation with a missing optional variable
|
|
if (p->GetType() == SbxERROR && IsMissing(p, 1))
|
|
{
|
|
Error(ERRCODE_BASIC_NOT_OPTIONAL);
|
|
return;
|
|
}
|
|
p->Compute( eOp, *p );
|
|
}
|
|
|
|
void SbiRuntime::StepCompare( SbxOperator eOp )
|
|
{
|
|
SbxVariableRef p1 = PopVar();
|
|
SbxVariableRef p2 = PopVar();
|
|
|
|
// tdf#144353 - do not compare a missing optional variable
|
|
if ((p1->GetType() == SbxERROR && SbiRuntime::IsMissing(p1.get(), 1))
|
|
|| (p2->GetType() == SbxERROR && SbiRuntime::IsMissing(p2.get(), 1)))
|
|
{
|
|
SbxBase::SetError(ERRCODE_BASIC_NOT_OPTIONAL);
|
|
return;
|
|
}
|
|
|
|
// Make sure objects with default params have
|
|
// values ( and type ) set as appropriate
|
|
SbxDataType p1Type = p1->GetType();
|
|
SbxDataType p2Type = p2->GetType();
|
|
if ( p1Type == SbxEMPTY )
|
|
{
|
|
p1->Broadcast( SfxHintId::BasicDataWanted );
|
|
p1Type = p1->GetType();
|
|
}
|
|
if ( p2Type == SbxEMPTY )
|
|
{
|
|
p2->Broadcast( SfxHintId::BasicDataWanted );
|
|
p2Type = p2->GetType();
|
|
}
|
|
if ( p1Type == p2Type )
|
|
{
|
|
// if both sides are an object and have default props
|
|
// then we need to use the default props
|
|
// we don't need to worry if only one side ( lhs, rhs ) is an
|
|
// object ( object side will get coerced to correct type in
|
|
// Compare )
|
|
if ( p1Type == SbxOBJECT )
|
|
{
|
|
SbxVariable* pDflt = getDefaultProp( p1.get() );
|
|
if ( pDflt )
|
|
{
|
|
p1 = pDflt;
|
|
p1->Broadcast( SfxHintId::BasicDataWanted );
|
|
}
|
|
pDflt = getDefaultProp( p2.get() );
|
|
if ( pDflt )
|
|
{
|
|
p2 = pDflt;
|
|
p2->Broadcast( SfxHintId::BasicDataWanted );
|
|
}
|
|
}
|
|
|
|
}
|
|
static SbxVariable* pTRUE = nullptr;
|
|
static SbxVariable* pFALSE = nullptr;
|
|
// why do this on non-windows ?
|
|
// why do this at all ?
|
|
// I dumbly follow the pattern :-/
|
|
if ( bVBAEnabled && ( p1->IsNull() || p2->IsNull() ) )
|
|
{
|
|
static SbxVariable* pNULL = []() {
|
|
SbxVariable* p = new SbxVariable;
|
|
p->PutNull();
|
|
p->AddFirstRef();
|
|
return p;
|
|
}();
|
|
PushVar( pNULL );
|
|
}
|
|
else if( p2->Compare( eOp, *p1 ) )
|
|
{
|
|
if( !pTRUE )
|
|
{
|
|
pTRUE = new SbxVariable;
|
|
pTRUE->PutBool( true );
|
|
pTRUE->AddFirstRef();
|
|
}
|
|
PushVar( pTRUE );
|
|
}
|
|
else
|
|
{
|
|
if( !pFALSE )
|
|
{
|
|
pFALSE = new SbxVariable;
|
|
pFALSE->PutBool( false );
|
|
pFALSE->AddFirstRef();
|
|
}
|
|
PushVar( pFALSE );
|
|
}
|
|
}
|
|
|
|
void SbiRuntime::StepEXP() { StepArith( SbxEXP ); }
|
|
void SbiRuntime::StepMUL() { StepArith( SbxMUL ); }
|
|
void SbiRuntime::StepDIV() { StepArith( SbxDIV ); }
|
|
void SbiRuntime::StepIDIV() { StepArith( SbxIDIV ); }
|
|
void SbiRuntime::StepMOD() { StepArith( SbxMOD ); }
|
|
void SbiRuntime::StepPLUS() { StepArith( SbxPLUS ); }
|
|
void SbiRuntime::StepMINUS() { StepArith( SbxMINUS ); }
|
|
void SbiRuntime::StepCAT() { StepArith( SbxCAT ); }
|
|
void SbiRuntime::StepAND() { StepArith( SbxAND ); }
|
|
void SbiRuntime::StepOR() { StepArith( SbxOR ); }
|
|
void SbiRuntime::StepXOR() { StepArith( SbxXOR ); }
|
|
void SbiRuntime::StepEQV() { StepArith( SbxEQV ); }
|
|
void SbiRuntime::StepIMP() { StepArith( SbxIMP ); }
|
|
|
|
void SbiRuntime::StepNEG() { StepUnary( SbxNEG ); }
|
|
void SbiRuntime::StepNOT() { StepUnary( SbxNOT ); }
|
|
|
|
void SbiRuntime::StepEQ() { StepCompare( SbxEQ ); }
|
|
void SbiRuntime::StepNE() { StepCompare( SbxNE ); }
|
|
void SbiRuntime::StepLT() { StepCompare( SbxLT ); }
|
|
void SbiRuntime::StepGT() { StepCompare( SbxGT ); }
|
|
void SbiRuntime::StepLE() { StepCompare( SbxLE ); }
|
|
void SbiRuntime::StepGE() { StepCompare( SbxGE ); }
|
|
|
|
namespace
|
|
{
|
|
OUString VBALikeToRegexp(std::u16string_view sIn)
|
|
{
|
|
OUStringBuffer sResult("\\A"); // Match at the beginning of the input
|
|
|
|
for (auto start = sIn.begin(), end = sIn.end(); start < end;)
|
|
{
|
|
switch (auto ch = *start++)
|
|
{
|
|
case '?':
|
|
sResult.append('.');
|
|
break;
|
|
case '*':
|
|
sResult.append(".*");
|
|
break;
|
|
case '#':
|
|
sResult.append("[0-9]");
|
|
break;
|
|
case '[':
|
|
sResult.append(ch);
|
|
if (start < end)
|
|
{
|
|
if (*start == '!')
|
|
{
|
|
sResult.append('^');
|
|
++start;
|
|
}
|
|
else if (*start == '^')
|
|
sResult.append('\\');
|
|
}
|
|
for (bool seenright = false; start < end && !seenright; ++start)
|
|
{
|
|
switch (*start)
|
|
{
|
|
case '[':
|
|
case '\\':
|
|
sResult.append('\\');
|
|
break;
|
|
case ']':
|
|
seenright = true;
|
|
break;
|
|
}
|
|
sResult.append(*start);
|
|
}
|
|
break;
|
|
case '.':
|
|
case '^':
|
|
case '$':
|
|
case '+':
|
|
case '\\':
|
|
case '|':
|
|
case '{':
|
|
case '}':
|
|
case '(':
|
|
case ')':
|
|
sResult.append('\\');
|
|
[[fallthrough]];
|
|
default:
|
|
sResult.append(ch);
|
|
}
|
|
}
|
|
|
|
sResult.append("\\z"); // Match if the current position is at the end of input
|
|
|
|
return sResult.makeStringAndClear();
|
|
}
|
|
}
|
|
|
|
void SbiRuntime::StepLIKE()
|
|
{
|
|
SbxVariableRef refVar1 = PopVar();
|
|
SbxVariableRef refVar2 = PopVar();
|
|
|
|
OUString value = refVar2->GetOUString();
|
|
OUString regex = VBALikeToRegexp(refVar1->GetOUString());
|
|
|
|
bool bTextMode(true);
|
|
bool bCompatibility = ( GetSbData()->pInst && GetSbData()->pInst->IsCompatibility() );
|
|
if( bCompatibility )
|
|
{
|
|
bTextMode = IsImageFlag( SbiImageFlags::COMPARETEXT );
|
|
}
|
|
sal_uInt32 searchFlags = UREGEX_UWORD | UREGEX_DOTALL; // Dot matches newline
|
|
if( bTextMode )
|
|
{
|
|
searchFlags |= UREGEX_CASE_INSENSITIVE;
|
|
}
|
|
|
|
static sal_uInt32 cachedSearchFlags = 0;
|
|
static OUString cachedRegex;
|
|
static std::optional<icu::RegexMatcher> oRegexMatcher;
|
|
UErrorCode nIcuErr = U_ZERO_ERROR;
|
|
if (regex != cachedRegex || searchFlags != cachedSearchFlags || !oRegexMatcher)
|
|
{
|
|
cachedRegex = regex;
|
|
cachedSearchFlags = searchFlags;
|
|
icu::UnicodeString sRegex(false, reinterpret_cast<const UChar*>(cachedRegex.getStr()),
|
|
cachedRegex.getLength());
|
|
oRegexMatcher.emplace(sRegex, cachedSearchFlags, nIcuErr);
|
|
}
|
|
|
|
icu::UnicodeString sSource(false, reinterpret_cast<const UChar*>(value.getStr()),
|
|
value.getLength());
|
|
oRegexMatcher->reset(sSource);
|
|
|
|
bool bRes = oRegexMatcher->matches(nIcuErr);
|
|
if (nIcuErr)
|
|
{
|
|
Error(ERRCODE_BASIC_INTERNAL_ERROR);
|
|
}
|
|
SbxVariable* pRes = new SbxVariable;
|
|
pRes->PutBool( bRes );
|
|
|
|
PushVar( pRes );
|
|
}
|
|
|
|
// TOS and TOS-1 are both object variables and contain the same pointer
|
|
|
|
void SbiRuntime::StepIS()
|
|
{
|
|
SbxVariableRef refVar1 = PopVar();
|
|
SbxVariableRef refVar2 = PopVar();
|
|
|
|
SbxDataType eType1 = refVar1->GetType();
|
|
SbxDataType eType2 = refVar2->GetType();
|
|
if ( eType1 == SbxEMPTY )
|
|
{
|
|
refVar1->Broadcast( SfxHintId::BasicDataWanted );
|
|
eType1 = refVar1->GetType();
|
|
}
|
|
if ( eType2 == SbxEMPTY )
|
|
{
|
|
refVar2->Broadcast( SfxHintId::BasicDataWanted );
|
|
eType2 = refVar2->GetType();
|
|
}
|
|
|
|
bool bRes = ( eType1 == SbxOBJECT && eType2 == SbxOBJECT );
|
|
if ( bVBAEnabled && !bRes )
|
|
{
|
|
Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
|
|
}
|
|
bRes = ( bRes && refVar1->GetObject() == refVar2->GetObject() );
|
|
SbxVariable* pRes = new SbxVariable;
|
|
pRes->PutBool( bRes );
|
|
PushVar( pRes );
|
|
}
|
|
|
|
// update the value of TOS
|
|
|
|
void SbiRuntime::StepGET()
|
|
{
|
|
SbxVariable* p = GetTOS();
|
|
p->Broadcast( SfxHintId::BasicDataWanted );
|
|
}
|
|
|
|
// #67607 copy Uno-Structs
|
|
static bool checkUnoStructCopy( bool bVBA, SbxVariableRef const & refVal, SbxVariableRef const & refVar )
|
|
{
|
|
SbxDataType eVarType = refVar->GetType();
|
|
SbxDataType eValType = refVal->GetType();
|
|
|
|
// tdf#144353 - do not assign a missing optional variable to a property
|
|
if (refVal->GetType() == SbxERROR && SbiRuntime::IsMissing(refVal.get(), 1))
|
|
{
|
|
SbxBase::SetError(ERRCODE_BASIC_NOT_OPTIONAL);
|
|
return true;
|
|
}
|
|
|
|
if ( ( bVBA && ( eVarType == SbxEMPTY ) ) || !refVar->CanWrite() )
|
|
return false;
|
|
|
|
if ( eValType != SbxOBJECT )
|
|
return false;
|
|
// we seem to be duplicating parts of SbxValue=operator, maybe we should just move this to
|
|
// there :-/ not sure if for every '=' we would want struct handling
|
|
if( eVarType != SbxOBJECT )
|
|
{
|
|
if ( refVar->IsFixed() )
|
|
return false;
|
|
}
|
|
// #115826: Exclude ProcedureProperties to avoid call to Property Get procedure
|
|
else if( dynamic_cast<const SbProcedureProperty*>( refVar.get() ) != nullptr )
|
|
return false;
|
|
|
|
SbxObjectRef xValObj = static_cast<SbxObject*>(refVal->GetObject());
|
|
if( !xValObj.is() || dynamic_cast<const SbUnoAnyObject*>( xValObj.get() ) != nullptr )
|
|
return false;
|
|
|
|
SbUnoObject* pUnoVal = dynamic_cast<SbUnoObject*>( xValObj.get() );
|
|
SbUnoStructRefObject* pUnoStructVal = dynamic_cast<SbUnoStructRefObject*>( xValObj.get() );
|
|
Any aAny;
|
|
// make doubly sure value is either a Uno object or
|
|
// a uno struct
|
|
if ( pUnoVal || pUnoStructVal )
|
|
aAny = pUnoVal ? pUnoVal->getUnoAny() : pUnoStructVal->getUnoAny();
|
|
else
|
|
return false;
|
|
if ( aAny.getValueTypeClass() != TypeClass_STRUCT )
|
|
return false;
|
|
|
|
refVar->SetType( SbxOBJECT );
|
|
ErrCode eOldErr = SbxBase::GetError();
|
|
// There are some circumstances when calling GetObject
|
|
// will trigger an error, we need to squash those here.
|
|
// Alternatively it is possible that the same scenario
|
|
// could overwrite and existing error. Let's prevent that
|
|
SbxObjectRef xVarObj = static_cast<SbxObject*>(refVar->GetObject());
|
|
if ( eOldErr != ERRCODE_NONE )
|
|
SbxBase::SetError( eOldErr );
|
|
else
|
|
SbxBase::ResetError();
|
|
|
|
SbUnoStructRefObject* pUnoStructObj = dynamic_cast<SbUnoStructRefObject*>( xVarObj.get() );
|
|
|
|
OUString sClassName = pUnoVal ? pUnoVal->GetClassName() : pUnoStructVal->GetClassName();
|
|
OUString sName = pUnoVal ? pUnoVal->GetName() : pUnoStructVal->GetName();
|
|
|
|
if ( pUnoStructObj )
|
|
{
|
|
StructRefInfo aInfo = pUnoStructObj->getStructInfo();
|
|
aInfo.setValue( aAny );
|
|
}
|
|
else
|
|
{
|
|
SbUnoObject* pNewUnoObj = new SbUnoObject( sName, aAny );
|
|
// #70324: adopt ClassName
|
|
pNewUnoObj->SetClassName( sClassName );
|
|
refVar->PutObject( pNewUnoObj );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
// laying down TOS in TOS-1
|
|
|
|
void SbiRuntime::StepPUT()
|
|
{
|
|
SbxVariableRef refVal = PopVar();
|
|
SbxVariableRef refVar = PopVar();
|
|
// store on its own method (inside a function)?
|
|
bool bFlagsChanged = false;
|
|
SbxFlagBits n = SbxFlagBits::NONE;
|
|
if( refVar.get() == pMeth )
|
|
{
|
|
bFlagsChanged = true;
|
|
n = refVar->GetFlags();
|
|
refVar->SetFlag( SbxFlagBits::Write );
|
|
}
|
|
|
|
// if left side arg is an object or variant and right handside isn't
|
|
// either an object or a variant then try and see if a default
|
|
// property exists.
|
|
// to use e.g. Range{"A1") = 34
|
|
// could equate to Range("A1").Value = 34
|
|
if ( bVBAEnabled )
|
|
{
|
|
// yet more hacking at this, I feel we don't quite have the correct
|
|
// heuristics for dealing with obj1 = obj2 ( where obj2 ( and maybe
|
|
// obj1 ) has default member/property ) ) It seems that default props
|
|
// aren't dealt with if the object is a member of some parent object
|
|
bool bObjAssign = false;
|
|
if ( refVar->GetType() == SbxEMPTY )
|
|
refVar->Broadcast( SfxHintId::BasicDataWanted );
|
|
if ( refVar->GetType() == SbxOBJECT )
|
|
{
|
|
if ( dynamic_cast<const SbxMethod *>(refVar.get()) != nullptr || ! refVar->GetParent() )
|
|
{
|
|
SbxVariable* pDflt = getDefaultProp( refVar.get() );
|
|
|
|
if ( pDflt )
|
|
refVar = pDflt;
|
|
}
|
|
else
|
|
bObjAssign = true;
|
|
}
|
|
if ( refVal->GetType() == SbxOBJECT && !bObjAssign && ( dynamic_cast<const SbxMethod *>(refVal.get()) != nullptr || ! refVal->GetParent() ) )
|
|
{
|
|
SbxVariable* pDflt = getDefaultProp( refVal.get() );
|
|
if ( pDflt )
|
|
refVal = pDflt;
|
|
}
|
|
}
|
|
|
|
if ( !checkUnoStructCopy( bVBAEnabled, refVal, refVar ) )
|
|
*refVar = *refVal;
|
|
|
|
if( bFlagsChanged )
|
|
refVar->SetFlags( n );
|
|
}
|
|
|
|
namespace {
|
|
|
|
// VBA Dim As New behavior handling, save init object information
|
|
struct DimAsNewRecoverItem
|
|
{
|
|
OUString m_aObjClass;
|
|
OUString m_aObjName;
|
|
SbxObject* m_pObjParent;
|
|
SbModule* m_pClassModule;
|
|
|
|
DimAsNewRecoverItem()
|
|
: m_pObjParent( nullptr )
|
|
, m_pClassModule( nullptr )
|
|
{}
|
|
|
|
DimAsNewRecoverItem( OUString aObjClass, OUString aObjName,
|
|
SbxObject* pObjParent, SbModule* pClassModule )
|
|
: m_aObjClass(std::move( aObjClass ))
|
|
, m_aObjName(std::move( aObjName ))
|
|
, m_pObjParent( pObjParent )
|
|
, m_pClassModule( pClassModule )
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
struct SbxVariablePtrHash
|
|
{
|
|
size_t operator()( SbxVariable* pVar ) const
|
|
{ return reinterpret_cast<size_t>(pVar); }
|
|
};
|
|
|
|
}
|
|
|
|
typedef std::unordered_map< SbxVariable*, DimAsNewRecoverItem,
|
|
SbxVariablePtrHash > DimAsNewRecoverHash;
|
|
|
|
namespace {
|
|
|
|
DimAsNewRecoverHash gaDimAsNewRecoverHash;
|
|
|
|
}
|
|
|
|
void removeDimAsNewRecoverItem( SbxVariable* pVar )
|
|
{
|
|
DimAsNewRecoverHash::iterator it = gaDimAsNewRecoverHash.find( pVar );
|
|
if( it != gaDimAsNewRecoverHash.end() )
|
|
{
|
|
gaDimAsNewRecoverHash.erase( it );
|
|
}
|
|
}
|
|
|
|
|
|
// saving object variable
|
|
// not-object variables will cause errors
|
|
|
|
constexpr OUString pCollectionStr = u"Collection"_ustr;
|
|
|
|
void SbiRuntime::StepSET_Impl( SbxVariableRef& refVal, SbxVariableRef& refVar, bool bHandleDefaultProp )
|
|
{
|
|
// #67733 types with array-flag are OK too
|
|
|
|
// Check var, !object is no error for sure if, only if type is fixed
|
|
SbxDataType eVarType = refVar->GetType();
|
|
if( !bHandleDefaultProp && eVarType != SbxOBJECT && !(eVarType & SbxARRAY) && refVar->IsFixed() )
|
|
{
|
|
Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
|
|
return;
|
|
}
|
|
|
|
// Check value, !object is no error for sure if, only if type is fixed
|
|
SbxDataType eValType = refVal->GetType();
|
|
if( !bHandleDefaultProp && eValType != SbxOBJECT && !(eValType & SbxARRAY) && refVal->IsFixed() )
|
|
{
|
|
Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
|
|
return;
|
|
}
|
|
|
|
// Getting in here causes problems with objects with default properties
|
|
// if they are SbxEMPTY I guess
|
|
if ( !bHandleDefaultProp || eValType == SbxOBJECT )
|
|
{
|
|
// activate GetObject for collections on refVal
|
|
SbxBase* pObjVarObj = refVal->GetObject();
|
|
if( pObjVarObj )
|
|
{
|
|
SbxVariableRef refObjVal = dynamic_cast<SbxObject*>( pObjVarObj );
|
|
|
|
if( refObjVal.is() )
|
|
{
|
|
refVal = std::move(refObjVal);
|
|
}
|
|
else if( !(eValType & SbxARRAY) )
|
|
{
|
|
refVal = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
// #52896 refVal can be invalid here, if uno-sequences - or more
|
|
// general arrays - are assigned to variables that are declared
|
|
// as an object!
|
|
if( !refVal.is() )
|
|
{
|
|
Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
|
|
}
|
|
else
|
|
{
|
|
bool bFlagsChanged = false;
|
|
SbxFlagBits n = SbxFlagBits::NONE;
|
|
if( refVar.get() == pMeth )
|
|
{
|
|
bFlagsChanged = true;
|
|
n = refVar->GetFlags();
|
|
refVar->SetFlag( SbxFlagBits::Write );
|
|
}
|
|
SbProcedureProperty* pProcProperty = dynamic_cast<SbProcedureProperty*>( refVar.get() );
|
|
if( pProcProperty )
|
|
{
|
|
pProcProperty->setSet( true );
|
|
}
|
|
if ( bHandleDefaultProp )
|
|
{
|
|
// get default properties for lhs & rhs where necessary
|
|
// SbxVariable* defaultProp = NULL; unused variable
|
|
// LHS try determine if a default prop exists
|
|
// again like in StepPUT (see there too ) we are tweaking the
|
|
// heuristics again for when to assign an object reference or
|
|
// use default members if they exist
|
|
// #FIXME we really need to get to the bottom of this mess
|
|
bool bObjAssign = false;
|
|
if ( refVar->GetType() == SbxOBJECT )
|
|
{
|
|
if ( dynamic_cast<const SbxMethod *>(refVar.get()) != nullptr || ! refVar->GetParent() )
|
|
{
|
|
SbxVariable* pDflt = getDefaultProp( refVar.get() );
|
|
if ( pDflt )
|
|
{
|
|
refVar = pDflt;
|
|
}
|
|
}
|
|
else
|
|
bObjAssign = true;
|
|
}
|
|
// RHS only get a default prop is the rhs has one
|
|
if ( refVal->GetType() == SbxOBJECT )
|
|
{
|
|
// check if lhs is a null object
|
|
// if it is then use the object not the default property
|
|
SbxObject* pObj = dynamic_cast<SbxObject*>( refVar.get() );
|
|
|
|
// calling GetObject on a SbxEMPTY variable raises
|
|
// object not set errors, make sure it's an Object
|
|
if ( !pObj && refVar->GetType() == SbxOBJECT )
|
|
{
|
|
SbxBase* pObjVarObj = refVar->GetObject();
|
|
pObj = dynamic_cast<SbxObject*>( pObjVarObj );
|
|
}
|
|
SbxVariable* pDflt = nullptr;
|
|
if ( pObj && !bObjAssign )
|
|
{
|
|
// lhs is either a valid object || or has a defaultProp
|
|
pDflt = getDefaultProp( refVal.get() );
|
|
}
|
|
if ( pDflt )
|
|
{
|
|
refVal = pDflt;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle Dim As New
|
|
bool bDimAsNew = bVBAEnabled && refVar->IsSet( SbxFlagBits::DimAsNew );
|
|
SbxBaseRef xPrevVarObj;
|
|
if( bDimAsNew )
|
|
{
|
|
xPrevVarObj = refVar->GetObject();
|
|
}
|
|
// Handle withevents
|
|
bool bWithEvents = refVar->IsSet( SbxFlagBits::WithEvents );
|
|
if ( bWithEvents )
|
|
{
|
|
Reference< XInterface > xComListener;
|
|
|
|
SbxBase* pObj = refVal->GetObject();
|
|
SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pObj );
|
|
if( pUnoObj != nullptr )
|
|
{
|
|
Any aControlAny = pUnoObj->getUnoAny();
|
|
OUString aDeclareClassName = refVar->GetDeclareClassName();
|
|
OUString aPrefix = refVar->GetName();
|
|
SbxObjectRef xScopeObj = refVar->GetParent();
|
|
xComListener = createComListener( aControlAny, aDeclareClassName, aPrefix, xScopeObj );
|
|
|
|
refVal->SetDeclareClassName( aDeclareClassName );
|
|
refVal->SetComListener( xComListener, &rBasic ); // Hold reference
|
|
}
|
|
|
|
}
|
|
|
|
// lhs is a property who's value is currently (Empty e.g. no broadcast yet)
|
|
// in this case if there is a default prop involved the value of the
|
|
// default property may in fact be void so the type will also be SbxEMPTY
|
|
// in this case we do not want to call checkUnoStructCopy 'cause that will
|
|
// cause an error also
|
|
if ( !checkUnoStructCopy( bHandleDefaultProp, refVal, refVar ) )
|
|
{
|
|
*refVar = *refVal;
|
|
}
|
|
if ( bDimAsNew )
|
|
{
|
|
if( dynamic_cast<const SbxObject*>( refVar.get() ) == nullptr )
|
|
{
|
|
SbxBase* pValObjBase = refVal->GetObject();
|
|
if( pValObjBase == nullptr )
|
|
{
|
|
if( xPrevVarObj.is() )
|
|
{
|
|
// Object is overwritten with NULL, instantiate init object
|
|
DimAsNewRecoverHash::iterator it = gaDimAsNewRecoverHash.find( refVar.get() );
|
|
if( it != gaDimAsNewRecoverHash.end() )
|
|
{
|
|
const DimAsNewRecoverItem& rItem = it->second;
|
|
if( rItem.m_pClassModule != nullptr )
|
|
{
|
|
SbClassModuleObject* pNewObj = new SbClassModuleObject(*rItem.m_pClassModule);
|
|
pNewObj->SetName( rItem.m_aObjName );
|
|
pNewObj->SetParent( rItem.m_pObjParent );
|
|
refVar->PutObject( pNewObj );
|
|
}
|
|
else if( rItem.m_aObjClass.equalsIgnoreAsciiCase( pCollectionStr ) )
|
|
{
|
|
BasicCollection* pNewCollection = new BasicCollection( pCollectionStr );
|
|
pNewCollection->SetName( rItem.m_aObjName );
|
|
pNewCollection->SetParent( rItem.m_pObjParent );
|
|
refVar->PutObject( pNewCollection );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Does old value exist?
|
|
bool bFirstInit = !xPrevVarObj.is();
|
|
if( bFirstInit )
|
|
{
|
|
// Store information to instantiate object later
|
|
SbxObject* pValObj = dynamic_cast<SbxObject*>( pValObjBase );
|
|
if( pValObj != nullptr )
|
|
{
|
|
OUString aObjClass = pValObj->GetClassName();
|
|
|
|
SbClassModuleObject* pClassModuleObj = dynamic_cast<SbClassModuleObject*>( pValObjBase );
|
|
if( pClassModuleObj != nullptr )
|
|
{
|
|
SbModule& rClassModule = pClassModuleObj->getClassModule();
|
|
gaDimAsNewRecoverHash[refVar.get()] =
|
|
DimAsNewRecoverItem( aObjClass, pValObj->GetName(), pValObj->GetParent(), &rClassModule );
|
|
}
|
|
else if( aObjClass.equalsIgnoreAsciiCase( "Collection" ) )
|
|
{
|
|
gaDimAsNewRecoverHash[refVar.get()] =
|
|
DimAsNewRecoverItem( aObjClass, pValObj->GetName(), pValObj->GetParent(), nullptr );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bFlagsChanged )
|
|
{
|
|
refVar->SetFlags( n );
|
|
}
|
|
}
|
|
}
|
|
|
|
void SbiRuntime::StepSET()
|
|
{
|
|
SbxVariableRef refVal = PopVar();
|
|
SbxVariableRef refVar = PopVar();
|
|
StepSET_Impl( refVal, refVar, bVBAEnabled ); // this is really assignment
|
|
}
|
|
|
|
void SbiRuntime::StepVBASET()
|
|
{
|
|
SbxVariableRef refVal = PopVar();
|
|
SbxVariableRef refVar = PopVar();
|
|
// don't handle default property
|
|
StepSET_Impl( refVal, refVar ); // set obj = something
|
|
}
|
|
|
|
|
|
void SbiRuntime::StepLSET()
|
|
{
|
|
SbxVariableRef refVal = PopVar();
|
|
SbxVariableRef refVar = PopVar();
|
|
if( refVar->GetType() != SbxSTRING ||
|
|
refVal->GetType() != SbxSTRING )
|
|
{
|
|
Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
|
|
}
|
|
else
|
|
{
|
|
SbxFlagBits n = refVar->GetFlags();
|
|
if( refVar.get() == pMeth )
|
|
{
|
|
refVar->SetFlag( SbxFlagBits::Write );
|
|
}
|
|
OUString aRefVarString = refVar->GetOUString();
|
|
OUString aRefValString = refVal->GetOUString();
|
|
|
|
sal_Int32 nVarStrLen = aRefVarString.getLength();
|
|
sal_Int32 nValStrLen = aRefValString.getLength();
|
|
OUString aNewStr;
|
|
if( nVarStrLen > nValStrLen )
|
|
{
|
|
OUStringBuffer buf(aRefValString);
|
|
comphelper::string::padToLength(buf, nVarStrLen, ' ');
|
|
aNewStr = buf.makeStringAndClear();
|
|
}
|
|
else
|
|
{
|
|
aNewStr = aRefValString.copy( 0, nVarStrLen );
|
|
}
|
|
|
|
refVar->PutString(aNewStr);
|
|
refVar->SetFlags( n );
|
|
}
|
|
}
|
|
|
|
void SbiRuntime::StepRSET()
|
|
{
|
|
SbxVariableRef refVal = PopVar();
|
|
SbxVariableRef refVar = PopVar();
|
|
if( refVar->GetType() != SbxSTRING || refVal->GetType() != SbxSTRING )
|
|
{
|
|
Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
|
|
}
|
|
else
|
|
{
|
|
SbxFlagBits n = refVar->GetFlags();
|
|
if( refVar.get() == pMeth )
|
|
{
|
|
refVar->SetFlag( SbxFlagBits::Write );
|
|
}
|
|
OUString aRefVarString = refVar->GetOUString();
|
|
OUString aRefValString = refVal->GetOUString();
|
|
sal_Int32 nVarStrLen = aRefVarString.getLength();
|
|
sal_Int32 nValStrLen = aRefValString.getLength();
|
|
|
|
OUStringBuffer aNewStr(nVarStrLen);
|
|
if (nVarStrLen > nValStrLen)
|
|
{
|
|
comphelper::string::padToLength(aNewStr, nVarStrLen - nValStrLen, ' ');
|
|
aNewStr.append(aRefValString);
|
|
}
|
|
else
|
|
{
|
|
aNewStr.append(aRefValString.subView(0, nVarStrLen));
|
|
}
|
|
refVar->PutString(aNewStr.makeStringAndClear());
|
|
|
|
refVar->SetFlags( n );
|
|
}
|
|
}
|
|
|
|
// laying down TOS in TOS-1, then set ReadOnly-Bit
|
|
|
|
void SbiRuntime::StepPUTC()
|
|
{
|
|
SbxVariableRef refVal = PopVar();
|
|
SbxVariableRef refVar = PopVar();
|
|
refVar->SetFlag( SbxFlagBits::Write );
|
|
*refVar = *refVal;
|
|
refVar->ResetFlag( SbxFlagBits::Write );
|
|
refVar->SetFlag( SbxFlagBits::Const );
|
|
}
|
|
|
|
// DIM
|
|
// TOS = variable for the array with dimension information as parameter
|
|
|
|
void SbiRuntime::StepDIM()
|
|
{
|
|
SbxVariableRef refVar = PopVar();
|
|
DimImpl( refVar );
|
|
}
|
|
|
|
// #56204 swap out DIM-functionality into a help method (step0.cxx)
|
|
void SbiRuntime::DimImpl(const SbxVariableRef& refVar)
|
|
{
|
|
// If refDim then this DIM statement is terminating a ReDIM and
|
|
// previous StepERASE_CLEAR for an array, the following actions have
|
|
// been delayed from ( StepERASE_CLEAR ) 'till here
|
|
if ( refRedim.is() )
|
|
{
|
|
if ( !refRedimpArray.is() ) // only erase the array not ReDim Preserve
|
|
{
|
|
lcl_eraseImpl( refVar, bVBAEnabled );
|
|
}
|
|
SbxDataType eType = refVar->GetType();
|
|
lcl_clearImpl( refVar, eType );
|
|
refRedim = nullptr;
|
|
}
|
|
SbxArray* pDims = refVar->GetParameters();
|
|
// must have an even number of arguments
|
|
// have in mind that Arg[0] does not count!
|
|
if (pDims && !(pDims->Count() & 1))
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
}
|
|
else
|
|
{
|
|
SbxDataType eType = refVar->IsFixed() ? refVar->GetType() : SbxVARIANT;
|
|
SbxDimArray* pArray = new SbxDimArray( eType );
|
|
// allow arrays without dimension information, too (VB-compatible)
|
|
if( pDims )
|
|
{
|
|
refVar->ResetFlag( SbxFlagBits::VarToDim );
|
|
|
|
for (sal_uInt32 i = 1; i < pDims->Count();)
|
|
{
|
|
sal_Int32 lb = pDims->Get(i++)->GetLong();
|
|
sal_Int32 ub = pDims->Get(i++)->GetLong();
|
|
if( ub < lb )
|
|
{
|
|
Error( ERRCODE_BASIC_OUT_OF_RANGE );
|
|
ub = lb;
|
|
}
|
|
pArray->AddDim(lb, ub);
|
|
if ( lb != ub )
|
|
{
|
|
pArray->setHasFixedSize( true );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// #62867 On creating an array of the length 0, create
|
|
// a dimension (like for Uno-Sequences of the length 0)
|
|
pArray->unoAddDim(0, -1);
|
|
}
|
|
SbxFlagBits nSavFlags = refVar->GetFlags();
|
|
refVar->ResetFlag( SbxFlagBits::Fixed );
|
|
refVar->PutObject( pArray );
|
|
refVar->SetFlags( nSavFlags );
|
|
refVar->SetParameters( nullptr );
|
|
}
|
|
}
|
|
|
|
// REDIM
|
|
// TOS = variable for the array
|
|
// argv = dimension information
|
|
|
|
void SbiRuntime::StepREDIM()
|
|
{
|
|
// Nothing different than dim at the moment because
|
|
// a double dim is already recognized by the compiler.
|
|
StepDIM();
|
|
}
|
|
|
|
|
|
// Helper function for StepREDIMP and StepDCREATE_IMPL / bRedimp = true
|
|
static void implCopyDimArray( SbxDimArray* pNewArray, SbxDimArray* pOldArray, sal_Int32 nMaxDimIndex,
|
|
sal_Int32 nActualDim, sal_Int32* pActualIndices, sal_Int32* pLowerBounds, sal_Int32* pUpperBounds )
|
|
{
|
|
sal_Int32& ri = pActualIndices[nActualDim];
|
|
for( ri = pLowerBounds[nActualDim] ; ri <= pUpperBounds[nActualDim] ; ri++ )
|
|
{
|
|
if( nActualDim < nMaxDimIndex )
|
|
{
|
|
implCopyDimArray( pNewArray, pOldArray, nMaxDimIndex, nActualDim + 1,
|
|
pActualIndices, pLowerBounds, pUpperBounds );
|
|
}
|
|
else
|
|
{
|
|
SbxVariable* pSource = pOldArray->Get(pActualIndices);
|
|
if (pSource && pOldArray->GetRefCount() > 1)
|
|
// tdf#134692: old array will stay alive after the redim - we need to copy deep
|
|
pSource = new SbxVariable(*pSource);
|
|
pNewArray->Put(pSource, pActualIndices);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns true when actually restored
|
|
static bool implRestorePreservedArray(SbxDimArray* pNewArray, SbxArrayRef& rrefRedimpArray, bool* pbWasError = nullptr)
|
|
{
|
|
assert(pNewArray);
|
|
bool bResult = false;
|
|
if (pbWasError)
|
|
*pbWasError = false;
|
|
if (rrefRedimpArray)
|
|
{
|
|
SbxDimArray* pOldArray = static_cast<SbxDimArray*>(rrefRedimpArray.get());
|
|
const sal_Int32 nDimsNew = pNewArray->GetDims();
|
|
const sal_Int32 nDimsOld = pOldArray->GetDims();
|
|
|
|
if (nDimsOld != nDimsNew)
|
|
{
|
|
StarBASIC::Error(ERRCODE_BASIC_OUT_OF_RANGE);
|
|
if (pbWasError)
|
|
*pbWasError = true;
|
|
}
|
|
else if (nDimsNew > 0)
|
|
{
|
|
// Store dims to use them for copying later
|
|
std::unique_ptr<sal_Int32[]> pLowerBounds(new sal_Int32[nDimsNew]);
|
|
std::unique_ptr<sal_Int32[]> pUpperBounds(new sal_Int32[nDimsNew]);
|
|
std::unique_ptr<sal_Int32[]> pActualIndices(new sal_Int32[nDimsNew]);
|
|
bool bNeedsPreallocation = true;
|
|
|
|
// Compare bounds
|
|
for (sal_Int32 i = 1; i <= nDimsNew; i++)
|
|
{
|
|
sal_Int32 lBoundNew, uBoundNew;
|
|
sal_Int32 lBoundOld, uBoundOld;
|
|
pNewArray->GetDim(i, lBoundNew, uBoundNew);
|
|
pOldArray->GetDim(i, lBoundOld, uBoundOld);
|
|
lBoundNew = std::max(lBoundNew, lBoundOld);
|
|
uBoundNew = std::min(uBoundNew, uBoundOld);
|
|
sal_Int32 j = i - 1;
|
|
pActualIndices[j] = pLowerBounds[j] = lBoundNew;
|
|
pUpperBounds[j] = uBoundNew;
|
|
if (lBoundNew > uBoundNew) // No elements in the dimension -> no elements to restore
|
|
bNeedsPreallocation = false;
|
|
}
|
|
|
|
// Optimization: pre-allocate underlying container
|
|
if (bNeedsPreallocation)
|
|
pNewArray->Put(nullptr, pUpperBounds.get());
|
|
|
|
// Copy data from old array by going recursively through all dimensions
|
|
// (It would be faster to work on the flat internal data array of an
|
|
// SbyArray but this solution is clearer and easier)
|
|
implCopyDimArray(pNewArray, pOldArray, nDimsNew - 1, 0, pActualIndices.get(),
|
|
pLowerBounds.get(), pUpperBounds.get());
|
|
bResult = true;
|
|
}
|
|
|
|
rrefRedimpArray.clear();
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
// REDIM PRESERVE
|
|
// TOS = variable for the array
|
|
// argv = dimension information
|
|
|
|
void SbiRuntime::StepREDIMP()
|
|
{
|
|
SbxVariableRef refVar = PopVar();
|
|
DimImpl( refVar );
|
|
|
|
// Now check, if we can copy from the old array
|
|
if( refRedimpArray.is() )
|
|
{
|
|
if (SbxDimArray* pNewArray = dynamic_cast<SbxDimArray*>(refVar->GetObject()))
|
|
implRestorePreservedArray(pNewArray, refRedimpArray);
|
|
}
|
|
}
|
|
|
|
// REDIM_COPY
|
|
// TOS = Array-Variable, Reference to array is copied
|
|
// Variable is cleared as in ERASE
|
|
|
|
void SbiRuntime::StepREDIMP_ERASE()
|
|
{
|
|
SbxVariableRef refVar = PopVar();
|
|
refRedim = refVar;
|
|
SbxDataType eType = refVar->GetType();
|
|
if( eType & SbxARRAY )
|
|
{
|
|
SbxBase* pElemObj = refVar->GetObject();
|
|
SbxDimArray* pDimArray = dynamic_cast<SbxDimArray*>( pElemObj );
|
|
if( pDimArray )
|
|
{
|
|
refRedimpArray = pDimArray;
|
|
}
|
|
|
|
}
|
|
else if( refVar->IsFixed() )
|
|
{
|
|
refVar->Clear();
|
|
}
|
|
else
|
|
{
|
|
refVar->SetType( SbxEMPTY );
|
|
}
|
|
}
|
|
|
|
static void lcl_clearImpl( SbxVariableRef const & refVar, SbxDataType const & eType )
|
|
{
|
|
SbxFlagBits nSavFlags = refVar->GetFlags();
|
|
refVar->ResetFlag( SbxFlagBits::Fixed );
|
|
refVar->SetType( SbxDataType(eType & 0x0FFF) );
|
|
refVar->SetFlags( nSavFlags );
|
|
refVar->Clear();
|
|
}
|
|
|
|
static void lcl_eraseImpl( SbxVariableRef const & refVar, bool bVBAEnabled )
|
|
{
|
|
SbxDataType eType = refVar->GetType();
|
|
if( eType & SbxARRAY )
|
|
{
|
|
if ( bVBAEnabled )
|
|
{
|
|
SbxBase* pElemObj = refVar->GetObject();
|
|
SbxDimArray* pDimArray = dynamic_cast<SbxDimArray*>( pElemObj );
|
|
if( pDimArray )
|
|
{
|
|
if ( pDimArray->hasFixedSize() )
|
|
{
|
|
// Clear all Value(s)
|
|
pDimArray->SbxArray::Clear();
|
|
}
|
|
else
|
|
{
|
|
pDimArray->Clear(); // clear dims and values
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SbxArray* pArray = dynamic_cast<SbxArray*>( pElemObj );
|
|
if ( pArray )
|
|
{
|
|
pArray->Clear();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Arrays have on an erase to VB quite a complex behaviour. Here are
|
|
// only the type problems at REDIM (#26295) removed at first:
|
|
// Set type hard onto the array-type, because a variable with array is
|
|
// SbxOBJECT. At REDIM there's an SbxOBJECT-array generated then and
|
|
// the original type is lost -> runtime error
|
|
lcl_clearImpl( refVar, eType );
|
|
}
|
|
}
|
|
else if( refVar->IsFixed() )
|
|
{
|
|
refVar->Clear();
|
|
}
|
|
else
|
|
{
|
|
refVar->SetType( SbxEMPTY );
|
|
}
|
|
}
|
|
|
|
// delete variable
|
|
// TOS = variable
|
|
|
|
void SbiRuntime::StepERASE()
|
|
{
|
|
SbxVariableRef refVar = PopVar();
|
|
lcl_eraseImpl( refVar, bVBAEnabled );
|
|
}
|
|
|
|
void SbiRuntime::StepERASE_CLEAR()
|
|
{
|
|
refRedim = PopVar();
|
|
}
|
|
|
|
void SbiRuntime::StepARRAYACCESS()
|
|
{
|
|
if( !refArgv.is() )
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
}
|
|
SbxVariableRef refVar = PopVar();
|
|
refVar->SetParameters( refArgv.get() );
|
|
PopArgv();
|
|
PushVar( CheckArray( refVar.get() ) );
|
|
}
|
|
|
|
void SbiRuntime::StepBYVAL()
|
|
{
|
|
// Copy variable on stack to break call by reference
|
|
SbxVariableRef pVar = PopVar();
|
|
SbxDataType t = pVar->GetType();
|
|
|
|
SbxVariable* pCopyVar = new SbxVariable( t );
|
|
pCopyVar->SetFlag( SbxFlagBits::ReadWrite );
|
|
*pCopyVar = *pVar;
|
|
|
|
PushVar( pCopyVar );
|
|
}
|
|
|
|
// establishing an argv
|
|
// nOp1 stays as it is -> 1st element is the return value
|
|
|
|
void SbiRuntime::StepARGC()
|
|
{
|
|
PushArgv();
|
|
refArgv = new SbxArray;
|
|
nArgc = 1;
|
|
}
|
|
|
|
// storing an argument in Argv
|
|
|
|
void SbiRuntime::StepARGV()
|
|
{
|
|
if( !refArgv.is() )
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
}
|
|
else
|
|
{
|
|
SbxVariableRef pVal = PopVar();
|
|
|
|
// Before fix of #94916:
|
|
if( dynamic_cast<const SbxMethod*>( pVal.get() ) != nullptr
|
|
|| dynamic_cast<const SbUnoProperty*>( pVal.get() ) != nullptr
|
|
|| dynamic_cast<const SbProcedureProperty*>( pVal.get() ) != nullptr )
|
|
{
|
|
// evaluate methods and properties!
|
|
SbxVariable* pRes = new SbxVariable( *pVal );
|
|
pVal = pRes;
|
|
}
|
|
refArgv->Put(pVal.get(), nArgc++);
|
|
}
|
|
}
|
|
|
|
// Input to Variable. The variable is on TOS and is
|
|
// is removed afterwards.
|
|
void SbiRuntime::StepINPUT()
|
|
{
|
|
OUStringBuffer sin;
|
|
char ch = 0;
|
|
ErrCode err;
|
|
// Skip whitespace
|
|
while( ( err = pIosys->GetError() ) == ERRCODE_NONE )
|
|
{
|
|
ch = pIosys->Read();
|
|
if( ch != ' ' && ch != '\t' && ch != '\n' )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if( !err )
|
|
{
|
|
// Scan until comma or whitespace
|
|
char sep = ( ch == '"' ) ? ch : 0;
|
|
if( sep )
|
|
{
|
|
ch = pIosys->Read();
|
|
}
|
|
while( ( err = pIosys->GetError() ) == ERRCODE_NONE )
|
|
{
|
|
if( ch == sep )
|
|
{
|
|
ch = pIosys->Read();
|
|
if( ch != sep )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if( !sep && (ch == ',' || ch == '\n') )
|
|
{
|
|
break;
|
|
}
|
|
sin.append( ch );
|
|
ch = pIosys->Read();
|
|
}
|
|
// skip whitespace
|
|
if( ch == ' ' || ch == '\t' )
|
|
{
|
|
while( ( err = pIosys->GetError() ) == ERRCODE_NONE )
|
|
{
|
|
if( ch != ' ' && ch != '\t' && ch != '\n' )
|
|
{
|
|
break;
|
|
}
|
|
ch = pIosys->Read();
|
|
}
|
|
}
|
|
}
|
|
if( !err )
|
|
{
|
|
OUString s = sin.makeStringAndClear();
|
|
SbxVariableRef pVar = GetTOS();
|
|
// try to fill the variable with a numeric value first,
|
|
// then with a string value
|
|
if( !pVar->IsFixed() || pVar->IsNumeric() )
|
|
{
|
|
sal_uInt16 nLen = 0;
|
|
if( !pVar->Scan( s, &nLen ) )
|
|
{
|
|
err = SbxBase::GetError();
|
|
SbxBase::ResetError();
|
|
}
|
|
// the value has to be scanned in completely
|
|
else if( nLen != s.getLength() && !pVar->PutString( s ) )
|
|
{
|
|
err = SbxBase::GetError();
|
|
SbxBase::ResetError();
|
|
}
|
|
else if( nLen != s.getLength() && pVar->IsNumeric() )
|
|
{
|
|
err = SbxBase::GetError();
|
|
SbxBase::ResetError();
|
|
if( !err )
|
|
{
|
|
err = ERRCODE_BASIC_CONVERSION;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pVar->PutString( s );
|
|
err = SbxBase::GetError();
|
|
SbxBase::ResetError();
|
|
}
|
|
}
|
|
if( err == ERRCODE_BASIC_USER_ABORT )
|
|
{
|
|
Error( err );
|
|
}
|
|
else if( err )
|
|
{
|
|
if( pRestart && !pIosys->GetChannel() )
|
|
{
|
|
pCode = pRestart;
|
|
}
|
|
else
|
|
{
|
|
Error( err );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PopVar();
|
|
}
|
|
}
|
|
|
|
// Line Input to Variable. The variable is on TOS and is
|
|
// deleted afterwards.
|
|
|
|
void SbiRuntime::StepLINPUT()
|
|
{
|
|
OString aInput;
|
|
pIosys->Read( aInput );
|
|
Error( pIosys->GetError() );
|
|
SbxVariableRef p = PopVar();
|
|
p->PutString(OStringToOUString(aInput, osl_getThreadTextEncoding()));
|
|
}
|
|
|
|
// end of program
|
|
|
|
void SbiRuntime::StepSTOP()
|
|
{
|
|
pInst->Stop();
|
|
}
|
|
|
|
|
|
void SbiRuntime::StepINITFOR()
|
|
{
|
|
PushFor();
|
|
}
|
|
|
|
void SbiRuntime::StepINITFOREACH()
|
|
{
|
|
PushForEach();
|
|
}
|
|
|
|
// increment FOR-variable
|
|
|
|
void SbiRuntime::StepNEXT()
|
|
{
|
|
if( !pForStk )
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
return;
|
|
}
|
|
if (pForStk->eForType != ForType::To)
|
|
return;
|
|
if (!pForStk->refVar)
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
return;
|
|
}
|
|
// tdf#85371 - grant explicitly write access to the index variable
|
|
// since it could be the name of a method itself used in the next statement.
|
|
ScopedWritableGuard aGuard(pForStk->refVar, pForStk->refVar.get() == pMeth);
|
|
pForStk->refVar->Compute( SbxPLUS, *pForStk->refInc );
|
|
}
|
|
|
|
// beginning CASE: TOS in CASE-stack
|
|
|
|
void SbiRuntime::StepCASE()
|
|
{
|
|
if( !refCaseStk.is() )
|
|
{
|
|
refCaseStk = new SbxArray;
|
|
}
|
|
SbxVariableRef xVar = PopVar();
|
|
refCaseStk->Put(xVar.get(), refCaseStk->Count());
|
|
}
|
|
|
|
// end CASE: free variable
|
|
|
|
void SbiRuntime::StepENDCASE()
|
|
{
|
|
if (!refCaseStk.is() || !refCaseStk->Count())
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
}
|
|
else
|
|
{
|
|
refCaseStk->Remove(refCaseStk->Count() - 1);
|
|
}
|
|
}
|
|
|
|
|
|
void SbiRuntime::StepSTDERROR()
|
|
{
|
|
pError = nullptr; bError = true;
|
|
pInst->aErrorMsg.clear();
|
|
pInst->nErr = ERRCODE_NONE;
|
|
pInst->nErl = 0;
|
|
nError = ERRCODE_NONE;
|
|
SbxErrObject::getUnoErrObject()->Clear();
|
|
}
|
|
|
|
void SbiRuntime::StepNOERROR()
|
|
{
|
|
pInst->aErrorMsg.clear();
|
|
pInst->nErr = ERRCODE_NONE;
|
|
pInst->nErl = 0;
|
|
nError = ERRCODE_NONE;
|
|
SbxErrObject::getUnoErrObject()->Clear();
|
|
bError = false;
|
|
}
|
|
|
|
// leave UP
|
|
|
|
void SbiRuntime::StepLEAVE()
|
|
{
|
|
bRun = false;
|
|
// If VBA and we are leaving an ErrorHandler then clear the error ( it's been processed )
|
|
if ( bInError && pError )
|
|
{
|
|
SbxErrObject::getUnoErrObject()->Clear();
|
|
}
|
|
}
|
|
|
|
void SbiRuntime::StepCHANNEL() // TOS = channel number
|
|
{
|
|
SbxVariableRef pChan = PopVar();
|
|
short nChan = pChan->GetInteger();
|
|
pIosys->SetChannel( nChan );
|
|
Error( pIosys->GetError() );
|
|
}
|
|
|
|
void SbiRuntime::StepCHANNEL0()
|
|
{
|
|
pIosys->ResetChannel();
|
|
}
|
|
|
|
void SbiRuntime::StepPRINT() // print TOS
|
|
{
|
|
SbxVariableRef p = PopVar();
|
|
OUString s1 = p->GetOUString();
|
|
OUString s;
|
|
if( p->GetType() >= SbxINTEGER && p->GetType() <= SbxDOUBLE )
|
|
{
|
|
s = " "; // one blank before
|
|
}
|
|
s += s1;
|
|
pIosys->Write( s );
|
|
Error( pIosys->GetError() );
|
|
}
|
|
|
|
void SbiRuntime::StepPRINTF() // print TOS in field
|
|
{
|
|
SbxVariableRef p = PopVar();
|
|
OUString s1 = p->GetOUString();
|
|
OUStringBuffer s;
|
|
if( p->GetType() >= SbxINTEGER && p->GetType() <= SbxDOUBLE )
|
|
{
|
|
s.append(' ');
|
|
}
|
|
s.append(s1);
|
|
comphelper::string::padToLength(s, 14, ' ');
|
|
pIosys->Write( s );
|
|
Error( pIosys->GetError() );
|
|
}
|
|
|
|
void SbiRuntime::StepWRITE() // write TOS
|
|
{
|
|
SbxVariableRef p = PopVar();
|
|
// Does the string have to be encapsulated?
|
|
char ch = 0;
|
|
switch (p->GetType() )
|
|
{
|
|
case SbxSTRING: ch = '"'; break;
|
|
case SbxCURRENCY:
|
|
case SbxBOOL:
|
|
case SbxDATE: ch = '#'; break;
|
|
default: break;
|
|
}
|
|
OUString s;
|
|
if( ch )
|
|
{
|
|
s += OUStringChar(ch);
|
|
}
|
|
s += p->GetOUString();
|
|
if( ch )
|
|
{
|
|
s += OUStringChar(ch);
|
|
}
|
|
pIosys->Write( s );
|
|
Error( pIosys->GetError() );
|
|
}
|
|
|
|
void SbiRuntime::StepRENAME() // Rename Tos+1 to Tos
|
|
{
|
|
SbxVariableRef pTos1 = PopVar();
|
|
SbxVariableRef pTos = PopVar();
|
|
OUString aDest = pTos1->GetOUString();
|
|
OUString aSource = pTos->GetOUString();
|
|
|
|
if( hasUno() )
|
|
{
|
|
implStepRenameUCB( aSource, aDest );
|
|
}
|
|
else
|
|
{
|
|
implStepRenameOSL( aSource, aDest );
|
|
}
|
|
}
|
|
|
|
// TOS = Prompt
|
|
|
|
void SbiRuntime::StepPROMPT()
|
|
{
|
|
SbxVariableRef p = PopVar();
|
|
OString aStr(OUStringToOString(p->GetOUString(), osl_getThreadTextEncoding()));
|
|
pIosys->SetPrompt( aStr );
|
|
}
|
|
|
|
// Set Restart point
|
|
|
|
void SbiRuntime::StepRESTART()
|
|
{
|
|
pRestart = pCode;
|
|
}
|
|
|
|
// empty expression on stack for missing parameter
|
|
|
|
void SbiRuntime::StepEMPTY()
|
|
{
|
|
// #57915 The semantics of StepEMPTY() is the representation of a missing argument.
|
|
// This is represented by the value 448 (ERRCODE_BASIC_NAMED_NOT_FOUND) of the type error
|
|
// in VB. StepEmpty should now rather be named StepMISSING() but the name is kept
|
|
// to simplify matters.
|
|
SbxVariableRef xVar = new SbxVariable( SbxVARIANT );
|
|
xVar->PutErr( 448 );
|
|
// tdf#79426, tdf#125180 - add additional information about a missing parameter
|
|
SetIsMissing( xVar.get() );
|
|
PushVar( xVar.get() );
|
|
}
|
|
|
|
// TOS = error code
|
|
|
|
void SbiRuntime::StepERROR()
|
|
{
|
|
SbxVariableRef refCode = PopVar();
|
|
sal_uInt16 n = refCode->GetUShort();
|
|
ErrCode error = StarBASIC::GetSfxFromVBError( n );
|
|
if ( bVBAEnabled )
|
|
{
|
|
pInst->Error( error );
|
|
}
|
|
else
|
|
{
|
|
Error( error );
|
|
}
|
|
}
|
|
|
|
// loading a numeric constant (+ID)
|
|
|
|
void SbiRuntime::StepLOADNC( sal_uInt32 nOp1 )
|
|
{
|
|
// tdf#143707 - check if the data type character was added after the string termination symbol
|
|
SbxDataType eTypeStr;
|
|
// #57844 use localized function
|
|
OUString aStr = pImg->GetString(nOp1, &eTypeStr);
|
|
// also allow , !!!
|
|
sal_Int32 iComma = aStr.indexOf(',');
|
|
if( iComma >= 0 )
|
|
{
|
|
aStr = aStr.replaceAt(iComma, 1, u".");
|
|
}
|
|
sal_Int32 nParseEnd = 0;
|
|
rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
|
|
double n = ::rtl::math::stringToDouble( aStr, '.', ',', &eStatus, &nParseEnd );
|
|
|
|
// tdf#131296 - retrieve data type put in SbiExprNode::Gen
|
|
SbxDataType eType = SbxDOUBLE;
|
|
if ( nParseEnd < aStr.getLength() )
|
|
{
|
|
// tdf#143707 - Check if there was a data type character after the numeric constant,
|
|
// added by older versions of the fix of the default values for strings.
|
|
switch ( aStr[nParseEnd] )
|
|
{
|
|
// See GetSuffixType in basic/source/comp/scanner.cxx for type characters
|
|
case '%': eType = SbxINTEGER; break;
|
|
case '&': eType = SbxLONG; break;
|
|
case '!': eType = SbxSINGLE; break;
|
|
case '@': eType = SbxCURRENCY; break;
|
|
// tdf#142460 - properly handle boolean values in string pool
|
|
case 'b': eType = SbxBOOL; break;
|
|
}
|
|
}
|
|
// tdf#143707 - if the data type character is different from the default value, it was added
|
|
// in basic/source/comp/symtbl.cxx. Hence, change the type of the numeric constant to be loaded.
|
|
else if (eTypeStr != SbxSTRING)
|
|
{
|
|
eType = eTypeStr;
|
|
}
|
|
SbxVariable* p = new SbxVariable( eType );
|
|
p->PutDouble( n );
|
|
// tdf#133913 - create variable with Variant/Type in order to prevent type conversion errors
|
|
p->ResetFlag( SbxFlagBits::Fixed );
|
|
PushVar( p );
|
|
}
|
|
|
|
// loading a string constant (+ID)
|
|
|
|
void SbiRuntime::StepLOADSC( sal_uInt32 nOp1 )
|
|
{
|
|
SbxVariable* p = new SbxVariable;
|
|
p->PutString( pImg->GetString( nOp1 ) );
|
|
PushVar( p );
|
|
}
|
|
|
|
// Immediate Load (+value)
|
|
// The opcode is not generated in SbiExprNode::Gen anymore; used for legacy images
|
|
|
|
void SbiRuntime::StepLOADI( sal_uInt32 nOp1 )
|
|
{
|
|
SbxVariable* p = new SbxVariable;
|
|
p->PutInteger( static_cast<sal_Int16>( nOp1 ) );
|
|
PushVar( p );
|
|
}
|
|
|
|
// store a named argument in Argv (+Arg-no. from 1!)
|
|
|
|
void SbiRuntime::StepARGN( sal_uInt32 nOp1 )
|
|
{
|
|
if( !refArgv.is() )
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
else
|
|
{
|
|
OUString aAlias( pImg->GetString( nOp1 ) );
|
|
SbxVariableRef pVal = PopVar();
|
|
if( bVBAEnabled &&
|
|
( dynamic_cast<const SbxMethod*>( pVal.get()) != nullptr
|
|
|| dynamic_cast<const SbUnoProperty*>( pVal.get()) != nullptr
|
|
|| dynamic_cast<const SbProcedureProperty*>( pVal.get()) != nullptr ) )
|
|
{
|
|
// named variables ( that are Any especially properties ) can be empty at this point and need a broadcast
|
|
if ( pVal->GetType() == SbxEMPTY )
|
|
pVal->Broadcast( SfxHintId::BasicDataWanted );
|
|
// evaluate methods and properties!
|
|
SbxVariable* pRes = new SbxVariable( *pVal );
|
|
pVal = pRes;
|
|
}
|
|
refArgv->Put(pVal.get(), nArgc);
|
|
refArgv->PutAlias(aAlias, nArgc++);
|
|
}
|
|
}
|
|
|
|
// converting the type of an argument in Argv for DECLARE-Fkt. (+type)
|
|
|
|
void SbiRuntime::StepARGTYP( sal_uInt32 nOp1 )
|
|
{
|
|
if( !refArgv.is() )
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
else
|
|
{
|
|
bool bByVal = (nOp1 & 0x8000) != 0; // Is BYVAL requested?
|
|
SbxDataType t = static_cast<SbxDataType>(nOp1 & 0x7FFF);
|
|
SbxVariable* pVar = refArgv->Get(refArgv->Count() - 1); // last Arg
|
|
|
|
// check BYVAL
|
|
if( pVar->GetRefCount() > 2 ) // 2 is normal for BYVAL
|
|
{
|
|
// parameter is a reference
|
|
if( bByVal )
|
|
{
|
|
// Call by Value is requested -> create a copy
|
|
pVar = new SbxVariable( *pVar );
|
|
pVar->SetFlag( SbxFlagBits::ReadWrite );
|
|
refExprStk->Put(pVar, refArgv->Count() - 1);
|
|
}
|
|
else
|
|
pVar->SetFlag( SbxFlagBits::Reference ); // Ref-Flag for DllMgr
|
|
}
|
|
else
|
|
{
|
|
// parameter is NO reference
|
|
if( bByVal )
|
|
pVar->ResetFlag( SbxFlagBits::Reference ); // no reference -> OK
|
|
else
|
|
Error( ERRCODE_BASIC_BAD_PARAMETERS ); // reference needed
|
|
}
|
|
|
|
if( pVar->GetType() != t )
|
|
{
|
|
// variant for correct conversion
|
|
// besides error, if SbxBYREF
|
|
pVar->Convert( SbxVARIANT );
|
|
pVar->Convert( t );
|
|
}
|
|
}
|
|
}
|
|
|
|
// bring string to a definite length (+length)
|
|
|
|
void SbiRuntime::StepPAD( sal_uInt32 nOp1 )
|
|
{
|
|
SbxVariable* p = GetTOS();
|
|
OUString s = p->GetOUString();
|
|
sal_Int32 nLen(nOp1);
|
|
if( s.getLength() == nLen )
|
|
return;
|
|
|
|
OUStringBuffer aBuf(s);
|
|
if (aBuf.getLength() > nLen)
|
|
{
|
|
comphelper::string::truncateToLength(aBuf, nLen);
|
|
}
|
|
else
|
|
{
|
|
comphelper::string::padToLength(aBuf, nLen, ' ');
|
|
}
|
|
s = aBuf.makeStringAndClear();
|
|
// Do not modify the original variable inadvertently
|
|
PopVar();
|
|
p = new SbxVariable;
|
|
p->PutString(s);
|
|
PushVar(p);
|
|
}
|
|
|
|
// jump (+target)
|
|
|
|
void SbiRuntime::StepJUMP( sal_uInt32 nOp1 )
|
|
{
|
|
#ifdef DBG_UTIL
|
|
// #QUESTION shouldn't this be
|
|
// if( (sal_uInt8*)( nOp1+pImagGetCode() ) >= pImg->GetCodeSize() )
|
|
if( nOp1 >= pImg->GetCodeSize() )
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
#endif
|
|
pCode = pImg->GetCode() + nOp1;
|
|
}
|
|
|
|
bool SbiRuntime::EvaluateTopOfStackAsBool()
|
|
{
|
|
SbxVariableRef tos = PopVar();
|
|
// In a test e.g. If Null then
|
|
// will evaluate Null will act as if False
|
|
if ( bVBAEnabled && tos->IsNull() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// tdf#151503 - do not evaluate a missing optional variable to a boolean
|
|
if (tos->GetType() == SbxERROR && IsMissing(tos.get(), 1))
|
|
{
|
|
Error(ERRCODE_BASIC_NOT_OPTIONAL);
|
|
return false;
|
|
}
|
|
|
|
if ( tos->IsObject() )
|
|
{
|
|
//GetBool applied to an Object attempts to dereference and evaluate
|
|
//the underlying value as Bool. Here, we're checking rather that
|
|
//it is not null
|
|
return tos->GetObject();
|
|
}
|
|
else
|
|
{
|
|
return tos->GetBool();
|
|
}
|
|
}
|
|
|
|
// evaluate TOS, conditional jump (+target)
|
|
|
|
void SbiRuntime::StepJUMPT( sal_uInt32 nOp1 )
|
|
{
|
|
if ( EvaluateTopOfStackAsBool() )
|
|
{
|
|
StepJUMP( nOp1 );
|
|
}
|
|
}
|
|
|
|
// evaluate TOS, conditional jump (+target)
|
|
|
|
void SbiRuntime::StepJUMPF( sal_uInt32 nOp1 )
|
|
{
|
|
if ( !EvaluateTopOfStackAsBool() )
|
|
{
|
|
StepJUMP( nOp1 );
|
|
}
|
|
}
|
|
|
|
// evaluate TOS, jump into JUMP-table (+MaxVal)
|
|
// looks like this:
|
|
// ONJUMP 2
|
|
// JUMP target1
|
|
// JUMP target2
|
|
|
|
// if 0x8000 is set in the operand, push the return address (ON..GOSUB)
|
|
|
|
void SbiRuntime::StepONJUMP( sal_uInt32 nOp1 )
|
|
{
|
|
SbxVariableRef p = PopVar();
|
|
sal_Int16 n = p->GetInteger();
|
|
|
|
if (nOp1 & 0x8000)
|
|
nOp1 &= 0x7FFF;
|
|
|
|
// tdf#160321 - do not execute the jump statement if the expression is out of range
|
|
if (n < 1 || o3tl::make_unsigned(n) > nOp1)
|
|
n = static_cast<sal_Int16>(nOp1 + 1);
|
|
else if (nOp1 & 0x8000)
|
|
PushGosub(pCode + 5 * nOp1);
|
|
|
|
nOp1 = static_cast<sal_uInt32>(pCode - pImg->GetCode()) + 5 * --n;
|
|
StepJUMP( nOp1 );
|
|
}
|
|
|
|
// UP-call (+target)
|
|
|
|
void SbiRuntime::StepGOSUB( sal_uInt32 nOp1 )
|
|
{
|
|
PushGosub( pCode );
|
|
if( nOp1 >= pImg->GetCodeSize() )
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
pCode = pImg->GetCode() + nOp1;
|
|
}
|
|
|
|
// UP-return (+0 or target)
|
|
|
|
void SbiRuntime::StepRETURN( sal_uInt32 nOp1 )
|
|
{
|
|
PopGosub();
|
|
if( nOp1 )
|
|
StepJUMP( nOp1 );
|
|
}
|
|
|
|
// check FOR-variable (+Endlabel)
|
|
|
|
void SbiRuntime::StepTESTFOR( sal_uInt32 nOp1 )
|
|
{
|
|
if( !pForStk )
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
return;
|
|
}
|
|
|
|
bool bEndLoop = false;
|
|
switch( pForStk->eForType )
|
|
{
|
|
case ForType::To:
|
|
{
|
|
SbxOperator eOp = ( pForStk->refInc->GetDouble() < 0 ) ? SbxLT : SbxGT;
|
|
if( pForStk->refVar->Compare( eOp, *pForStk->refEnd ) )
|
|
bEndLoop = true;
|
|
if (SbxBase::IsError())
|
|
pForStk->eForType = ForType::Error; // terminate loop at the next iteration
|
|
break;
|
|
}
|
|
case ForType::EachArray:
|
|
{
|
|
SbiForStack* p = pForStk;
|
|
if (!p->refEnd)
|
|
{
|
|
SbxBase::SetError(ERRCODE_BASIC_CONVERSION);
|
|
pForStk->eForType = ForType::Error; // terminate loop at the next iteration
|
|
}
|
|
else if (p->pArrayCurIndices == nullptr)
|
|
{
|
|
bEndLoop = true;
|
|
}
|
|
else
|
|
{
|
|
SbxDimArray* pArray = reinterpret_cast<SbxDimArray*>(p->refEnd.get());
|
|
sal_Int32 nDims = pArray->GetDims();
|
|
|
|
// Empty array?
|
|
if( nDims == 1 && p->pArrayLowerBounds[0] > p->pArrayUpperBounds[0] )
|
|
{
|
|
bEndLoop = true;
|
|
break;
|
|
}
|
|
SbxVariable* pVal = pArray->Get(p->pArrayCurIndices.get());
|
|
*(p->refVar) = *pVal;
|
|
|
|
bool bFoundNext = false;
|
|
for(sal_Int32 i = 0 ; i < nDims ; i++ )
|
|
{
|
|
if( p->pArrayCurIndices[i] < p->pArrayUpperBounds[i] )
|
|
{
|
|
bFoundNext = true;
|
|
p->pArrayCurIndices[i]++;
|
|
for( sal_Int32 j = i - 1 ; j >= 0 ; j-- )
|
|
p->pArrayCurIndices[j] = p->pArrayLowerBounds[j];
|
|
break;
|
|
}
|
|
}
|
|
if( !bFoundNext )
|
|
{
|
|
p->pArrayCurIndices.reset();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ForType::EachCollection:
|
|
{
|
|
if (!pForStk->refEnd)
|
|
{
|
|
SbxBase::SetError(ERRCODE_BASIC_CONVERSION);
|
|
pForStk->eForType = ForType::Error; // terminate loop at the next iteration
|
|
break;
|
|
}
|
|
|
|
BasicCollection* pCollection = static_cast<BasicCollection*>(pForStk->refEnd.get());
|
|
SbxArrayRef xItemArray = pCollection->xItemArray;
|
|
sal_Int32 nCount = xItemArray->Count();
|
|
if( pForStk->nCurCollectionIndex < nCount )
|
|
{
|
|
SbxVariable* pRes = xItemArray->Get(pForStk->nCurCollectionIndex);
|
|
pForStk->nCurCollectionIndex++;
|
|
(*pForStk->refVar) = *pRes;
|
|
}
|
|
else
|
|
{
|
|
bEndLoop = true;
|
|
}
|
|
break;
|
|
}
|
|
case ForType::EachXEnumeration:
|
|
{
|
|
SbiForStack* p = pForStk;
|
|
if (!p->xEnumeration)
|
|
{
|
|
SbxBase::SetError(ERRCODE_BASIC_CONVERSION);
|
|
pForStk->eForType = ForType::Error; // terminate loop at the next iteration
|
|
}
|
|
else if (p->xEnumeration->hasMoreElements())
|
|
{
|
|
Any aElem = p->xEnumeration->nextElement();
|
|
SbxVariableRef xVar = new SbxVariable( SbxVARIANT );
|
|
unoToSbxValue( xVar.get(), aElem );
|
|
(*pForStk->refVar) = *xVar;
|
|
}
|
|
else
|
|
{
|
|
bEndLoop = true;
|
|
}
|
|
break;
|
|
}
|
|
// tdf#130307 - support for each loop for objects exposing XIndexAccess
|
|
case ForType::EachXIndexAccess:
|
|
{
|
|
SbiForStack* p = pForStk;
|
|
if (!p->xIndexAccess)
|
|
{
|
|
SbxBase::SetError(ERRCODE_BASIC_CONVERSION);
|
|
pForStk->eForType = ForType::Error; // terminate loop at the next iteration
|
|
}
|
|
else if (pForStk->nCurCollectionIndex < p->xIndexAccess->getCount())
|
|
{
|
|
Any aElem = p->xIndexAccess->getByIndex(pForStk->nCurCollectionIndex);
|
|
pForStk->nCurCollectionIndex++;
|
|
SbxVariableRef xVar = new SbxVariable(SbxVARIANT);
|
|
unoToSbxValue(xVar.get(), aElem);
|
|
(*pForStk->refVar) = *xVar;
|
|
}
|
|
else
|
|
{
|
|
bEndLoop = true;
|
|
}
|
|
break;
|
|
}
|
|
case ForType::Error:
|
|
{
|
|
// We are in Resume Next mode after failed loop initialization
|
|
bEndLoop = true;
|
|
Error(ERRCODE_BASIC_BAD_PARAMETER);
|
|
break;
|
|
}
|
|
}
|
|
if( bEndLoop )
|
|
{
|
|
PopFor();
|
|
StepJUMP( nOp1 );
|
|
}
|
|
}
|
|
|
|
// Tos+1 <= Tos+2 <= Tos, 2xremove (+Target)
|
|
|
|
void SbiRuntime::StepCASETO( sal_uInt32 nOp1 )
|
|
{
|
|
if (!refCaseStk.is() || !refCaseStk->Count())
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
else
|
|
{
|
|
SbxVariableRef xTo = PopVar();
|
|
SbxVariableRef xFrom = PopVar();
|
|
SbxVariableRef xCase = refCaseStk->Get(refCaseStk->Count() - 1);
|
|
if( *xCase >= *xFrom && *xCase <= *xTo )
|
|
StepJUMP( nOp1 );
|
|
}
|
|
}
|
|
|
|
|
|
void SbiRuntime::StepERRHDL( sal_uInt32 nOp1 )
|
|
{
|
|
const sal_uInt8* p = pCode;
|
|
StepJUMP( nOp1 );
|
|
pError = pCode;
|
|
pCode = p;
|
|
pInst->aErrorMsg.clear();
|
|
pInst->nErr = ERRCODE_NONE;
|
|
pInst->nErl = 0;
|
|
nError = ERRCODE_NONE;
|
|
SbxErrObject::getUnoErrObject()->Clear();
|
|
}
|
|
|
|
// Resume after errors (+0=statement, 1=next or Label)
|
|
|
|
void SbiRuntime::StepRESUME( sal_uInt32 nOp1 )
|
|
{
|
|
// #32714 Resume without error? -> error
|
|
if( !bInError )
|
|
{
|
|
Error( ERRCODE_BASIC_BAD_RESUME );
|
|
return;
|
|
}
|
|
if( nOp1 )
|
|
{
|
|
// set Code-pointer to the next statement
|
|
sal_uInt16 n1, n2;
|
|
pCode = pMod->FindNextStmnt( pErrCode, n1, n2, true, pImg );
|
|
}
|
|
else
|
|
pCode = pErrStmnt;
|
|
if ( pError ) // current in error handler ( and got a Resume Next statement )
|
|
SbxErrObject::getUnoErrObject()->Clear();
|
|
|
|
if( nOp1 > 1 )
|
|
StepJUMP( nOp1 );
|
|
pInst->aErrorMsg.clear();
|
|
pInst->nErr = ERRCODE_NONE;
|
|
pInst->nErl = 0;
|
|
nError = ERRCODE_NONE;
|
|
bInError = false;
|
|
}
|
|
|
|
// close channel (+channel, 0=all)
|
|
void SbiRuntime::StepCLOSE( sal_uInt32 nOp1 )
|
|
{
|
|
ErrCode err;
|
|
if( !nOp1 )
|
|
pIosys->Shutdown();
|
|
else
|
|
{
|
|
err = pIosys->GetError();
|
|
if( !err )
|
|
{
|
|
pIosys->Close();
|
|
}
|
|
}
|
|
err = pIosys->GetError();
|
|
Error( err );
|
|
}
|
|
|
|
// output character (+char)
|
|
|
|
void SbiRuntime::StepPRCHAR( sal_uInt32 nOp1 )
|
|
{
|
|
OUString s(static_cast<sal_Unicode>(nOp1));
|
|
pIosys->Write( s );
|
|
Error( pIosys->GetError() );
|
|
}
|
|
|
|
// check whether TOS is a certain object class (+StringID)
|
|
|
|
bool SbiRuntime::implIsClass( SbxObject const * pObj, const OUString& aClass )
|
|
{
|
|
bool bRet = true;
|
|
|
|
if( !aClass.isEmpty() )
|
|
{
|
|
bRet = pObj->IsClass( aClass );
|
|
if( !bRet )
|
|
bRet = aClass.equalsIgnoreAsciiCase( "object" );
|
|
if( !bRet )
|
|
{
|
|
const OUString& aObjClass = pObj->GetClassName();
|
|
SbModule* pClassMod = GetSbData()->pClassFac->FindClass( aObjClass );
|
|
if( pClassMod )
|
|
{
|
|
SbClassData* pClassData = pClassMod->pClassData.get();
|
|
if (pClassData != nullptr )
|
|
{
|
|
SbxVariable* pClassVar = pClassData->mxIfaces->Find( aClass, SbxClassType::DontCare );
|
|
bRet = (pClassVar != nullptr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
bool SbiRuntime::checkClass_Impl( const SbxVariableRef& refVal,
|
|
const OUString& aClass, bool bRaiseErrors, bool bDefault )
|
|
{
|
|
bool bOk = bDefault;
|
|
|
|
SbxDataType t = refVal->GetType();
|
|
SbxVariable* pVal = refVal.get();
|
|
// we don't know the type of uno properties that are (maybevoid)
|
|
if ( t == SbxEMPTY )
|
|
{
|
|
if ( auto pProp = dynamic_cast<SbUnoProperty*>( refVal.get() ) )
|
|
{
|
|
t = pProp->getRealType();
|
|
}
|
|
}
|
|
if( t == SbxOBJECT || bVBAEnabled )
|
|
{
|
|
SbxObject* pObj = dynamic_cast<SbxObject*>(pVal);
|
|
if (!pObj)
|
|
{
|
|
pObj = dynamic_cast<SbxObject*>(refVal->GetObject());
|
|
}
|
|
if( pObj )
|
|
{
|
|
if( !implIsClass( pObj, aClass ) )
|
|
{
|
|
SbUnoObject* pUnoObj(nullptr);
|
|
if (bVBAEnabled || CodeCompleteOptions::IsExtendedTypeDeclaration())
|
|
{
|
|
pUnoObj = dynamic_cast<SbUnoObject*>(pObj);
|
|
}
|
|
|
|
if (pUnoObj)
|
|
bOk = checkUnoObjectType(*pUnoObj, aClass);
|
|
else
|
|
bOk = false;
|
|
if ( !bOk && bRaiseErrors )
|
|
Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
|
|
}
|
|
else
|
|
{
|
|
bOk = true;
|
|
|
|
SbClassModuleObject* pClassModuleObject = dynamic_cast<SbClassModuleObject*>( pObj );
|
|
if( pClassModuleObject != nullptr )
|
|
pClassModuleObject->triggerInitializeEvent();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( bRaiseErrors )
|
|
Error( ERRCODE_BASIC_NEEDS_OBJECT );
|
|
bOk = false;
|
|
}
|
|
return bOk;
|
|
}
|
|
|
|
void SbiRuntime::StepSETCLASS_impl( sal_uInt32 nOp1, bool bHandleDflt )
|
|
{
|
|
SbxVariableRef refVal = PopVar();
|
|
SbxVariableRef refVar = PopVar();
|
|
OUString aClass( pImg->GetString( nOp1 ) );
|
|
|
|
bool bOk = checkClass_Impl( refVal, aClass, true, true );
|
|
if( bOk )
|
|
{
|
|
StepSET_Impl( refVal, refVar, bHandleDflt ); // don't do handle default prop for a "proper" set
|
|
}
|
|
}
|
|
|
|
void SbiRuntime::StepVBASETCLASS( sal_uInt32 nOp1 )
|
|
{
|
|
StepSETCLASS_impl( nOp1, false );
|
|
}
|
|
|
|
void SbiRuntime::StepSETCLASS( sal_uInt32 nOp1 )
|
|
{
|
|
StepSETCLASS_impl( nOp1, true );
|
|
}
|
|
|
|
void SbiRuntime::StepTESTCLASS( sal_uInt32 nOp1 )
|
|
{
|
|
SbxVariableRef xObjVal = PopVar();
|
|
OUString aClass( pImg->GetString( nOp1 ) );
|
|
bool bDefault = !bVBAEnabled;
|
|
bool bOk = checkClass_Impl( xObjVal, aClass, false, bDefault );
|
|
|
|
SbxVariable* pRet = new SbxVariable;
|
|
pRet->PutBool( bOk );
|
|
PushVar( pRet );
|
|
}
|
|
|
|
// define library for following declare-call
|
|
|
|
void SbiRuntime::StepLIB( sal_uInt32 nOp1 )
|
|
{
|
|
aLibName = pImg->GetString( nOp1 );
|
|
}
|
|
|
|
// TOS is incremented by BASE, BASE is pushed before (+BASE)
|
|
// This opcode is pushed before DIM/REDIM-commands,
|
|
// if there's been only one index named.
|
|
|
|
void SbiRuntime::StepBASED( sal_uInt32 nOp1 )
|
|
{
|
|
SbxVariable* p1 = new SbxVariable;
|
|
SbxVariableRef x2 = PopVar();
|
|
|
|
// #109275 Check compatibility mode
|
|
bool bCompatible = ((nOp1 & 0x8000) != 0);
|
|
sal_uInt16 uBase = static_cast<sal_uInt16>(nOp1 & 1); // Can only be 0 or 1
|
|
p1->PutInteger( uBase );
|
|
if( !bCompatible )
|
|
{
|
|
// tdf#85371 - grant explicitly write access to the dimension variable
|
|
// since in Star/OpenOffice Basic the upper index border is affected,
|
|
// and the dimension variable could be the name of the method itself.
|
|
ScopedWritableGuard aGuard(x2, x2.get() == pMeth);
|
|
x2->Compute( SbxPLUS, *p1 );
|
|
}
|
|
PushVar( x2.get() ); // first the Expr
|
|
PushVar( p1 ); // then the Base
|
|
}
|
|
|
|
// the bits in the String-ID:
|
|
// 0x8000 - Argv is reserved
|
|
|
|
SbxVariable* SbiRuntime::FindElement( SbxObject* pObj, sal_uInt32 nOp1, sal_uInt32 nOp2,
|
|
ErrCode nNotFound, bool bLocal, bool bStatic )
|
|
{
|
|
bool bIsVBAInterOp = SbiRuntime::isVBAEnabled();
|
|
if( bIsVBAInterOp )
|
|
{
|
|
StarBASIC* pMSOMacroRuntimeLib = GetSbData()->pMSOMacroRuntimLib;
|
|
if( pMSOMacroRuntimeLib != nullptr )
|
|
{
|
|
pMSOMacroRuntimeLib->ResetFlag( SbxFlagBits::ExtSearch );
|
|
}
|
|
}
|
|
|
|
SbxVariable* pElem = nullptr;
|
|
if( !pObj )
|
|
{
|
|
Error( ERRCODE_BASIC_NO_OBJECT );
|
|
pElem = new SbxVariable;
|
|
}
|
|
else
|
|
{
|
|
bool bFatalError = false;
|
|
SbxDataType t = static_cast<SbxDataType>(nOp2);
|
|
OUString aName( pImg->GetString( nOp1 & 0x7FFF ) );
|
|
// Hacky capture of Evaluate [] syntax
|
|
// this should be tackled I feel at the pcode level
|
|
if ( bIsVBAInterOp && aName.startsWith("[") )
|
|
{
|
|
// emulate pcode here
|
|
StepARGC();
|
|
// pseudo StepLOADSC
|
|
OUString sArg = aName.copy( 1, aName.getLength() - 2 );
|
|
SbxVariable* p = new SbxVariable;
|
|
p->PutString( sArg );
|
|
PushVar( p );
|
|
StepARGV();
|
|
nOp1 = nOp1 | 0x8000; // indicate params are present
|
|
aName = "Evaluate";
|
|
}
|
|
if( bLocal )
|
|
{
|
|
if ( bStatic && pMeth )
|
|
{
|
|
pElem = pMeth->GetStatics()->Find( aName, SbxClassType::DontCare );
|
|
}
|
|
|
|
if ( !pElem )
|
|
{
|
|
pElem = refLocals->Find( aName, SbxClassType::DontCare );
|
|
}
|
|
}
|
|
if( !pElem )
|
|
{
|
|
bool bSave = rBasic.bNoRtl;
|
|
rBasic.bNoRtl = true;
|
|
pElem = pObj->Find( aName, SbxClassType::DontCare );
|
|
|
|
// #110004, #112015: Make private really private
|
|
if( bLocal && pElem ) // Local as flag for global search
|
|
{
|
|
if( pElem->IsSet( SbxFlagBits::Private ) )
|
|
{
|
|
SbiInstance* pInst_ = GetSbData()->pInst;
|
|
if( pInst_ && pInst_->IsCompatibility() && pObj != pElem->GetParent() )
|
|
{
|
|
pElem = nullptr; // Found but in wrong module!
|
|
}
|
|
// Interfaces: Use SbxFlagBits::ExtFound
|
|
}
|
|
}
|
|
rBasic.bNoRtl = bSave;
|
|
|
|
// is it a global uno-identifier?
|
|
if( bLocal && !pElem )
|
|
{
|
|
bool bSetName = true; // preserve normal behaviour
|
|
|
|
// i#i68894# if VBAInterOp favour searching vba globals
|
|
// over searching for uno classes
|
|
if ( bVBAEnabled )
|
|
{
|
|
// Try Find in VBA symbols space
|
|
pElem = rBasic.VBAFind( aName, SbxClassType::DontCare );
|
|
if ( pElem )
|
|
{
|
|
bSetName = false; // don't overwrite uno name
|
|
}
|
|
else
|
|
{
|
|
pElem = VBAConstantHelper::instance().getVBAConstant( aName );
|
|
}
|
|
}
|
|
|
|
if( !pElem )
|
|
{
|
|
// #72382 ATTENTION! ALWAYS returns a result now
|
|
// because of unknown modules!
|
|
SbUnoClass* pUnoClass = findUnoClass( aName );
|
|
if( pUnoClass )
|
|
{
|
|
pElem = new SbxVariable( t );
|
|
SbxValues aRes( SbxOBJECT );
|
|
aRes.pObj = pUnoClass;
|
|
pElem->SbxVariable::Put( aRes );
|
|
}
|
|
}
|
|
|
|
// #62939 If a uno-class has been found, the wrapper
|
|
// object has to be held, because the uno-class, e. g.
|
|
// "stardiv", has to be read out of the registry
|
|
// every time again otherwise
|
|
if( pElem )
|
|
{
|
|
// #63774 May not be saved too!!!
|
|
pElem->SetFlag( SbxFlagBits::DontStore );
|
|
pElem->SetFlag( SbxFlagBits::NoModify);
|
|
|
|
// #72382 save locally, all variables that have been declared
|
|
// implicit would become global automatically otherwise!
|
|
if ( bSetName )
|
|
{
|
|
pElem->SetName( aName );
|
|
}
|
|
refLocals->Put(pElem, refLocals->Count());
|
|
}
|
|
}
|
|
|
|
if( !pElem )
|
|
{
|
|
// not there and not in the object?
|
|
// don't establish if that thing has parameters!
|
|
if( nOp1 & 0x8000 )
|
|
{
|
|
bFatalError = true;
|
|
}
|
|
|
|
// else, if there are parameters, use different error code
|
|
if( !bLocal || pImg->IsFlag( SbiImageFlags::EXPLICIT ) )
|
|
{
|
|
// #39108 if explicit and as ELEM always a fatal error
|
|
bFatalError = true;
|
|
|
|
|
|
if( !( nOp1 & 0x8000 ) && nNotFound == ERRCODE_BASIC_PROC_UNDEFINED )
|
|
{
|
|
nNotFound = ERRCODE_BASIC_VAR_UNDEFINED;
|
|
}
|
|
}
|
|
if( bFatalError )
|
|
{
|
|
// #39108 use dummy variable instead of fatal error
|
|
if( !xDummyVar.is() )
|
|
{
|
|
xDummyVar = new SbxVariable( SbxVARIANT );
|
|
}
|
|
pElem = xDummyVar.get();
|
|
|
|
ClearArgvStack();
|
|
|
|
Error( nNotFound, aName );
|
|
}
|
|
else
|
|
{
|
|
if ( bStatic )
|
|
{
|
|
pElem = StepSTATIC_Impl( aName, t, 0 );
|
|
}
|
|
if ( !pElem )
|
|
{
|
|
pElem = new SbxVariable( t );
|
|
if( t != SbxVARIANT )
|
|
{
|
|
pElem->SetFlag( SbxFlagBits::Fixed );
|
|
}
|
|
pElem->SetName( aName );
|
|
refLocals->Put(pElem, refLocals->Count());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// #39108 Args can already be deleted!
|
|
if( !bFatalError )
|
|
{
|
|
SetupArgs( pElem, nOp1 );
|
|
}
|
|
// because a particular call-type is requested
|
|
if (SbxMethod* pMethod = dynamic_cast<SbxMethod*>(pElem))
|
|
{
|
|
// shall the type be converted?
|
|
SbxDataType t2 = pElem->GetType();
|
|
bool bSet = false;
|
|
if( (pElem->GetFlags() & SbxFlagBits::Fixed) == SbxFlagBits::NONE )
|
|
{
|
|
if( t != SbxVARIANT && t != t2 &&
|
|
t >= SbxINTEGER && t <= SbxSTRING )
|
|
{
|
|
pElem->SetType( t );
|
|
bSet = true;
|
|
}
|
|
}
|
|
// assign pElem to a Ref, to delete a temp-var if applicable
|
|
SbxVariableRef xDeleteRef = pElem;
|
|
|
|
// remove potential rests of the last call of the SbxMethod
|
|
// free Write before, so that there's no error
|
|
SbxFlagBits nSavFlags = pElem->GetFlags();
|
|
pElem->SetFlag( SbxFlagBits::ReadWrite | SbxFlagBits::NoBroadcast );
|
|
pElem->SbxValue::Clear();
|
|
pElem->SetFlags( nSavFlags );
|
|
|
|
// don't touch before setting, as e. g. LEFT()
|
|
// has to know the difference between Left$() and Left()
|
|
|
|
// because the methods' parameters are cut away in PopVar()
|
|
SbxVariable* pNew = new SbxMethod(*pMethod);
|
|
//OLD: SbxVariable* pNew = new SbxVariable( *pElem );
|
|
|
|
pElem->SetParameters(nullptr);
|
|
pNew->SetFlag( SbxFlagBits::ReadWrite );
|
|
|
|
if( bSet )
|
|
{
|
|
pElem->SetType( t2 );
|
|
}
|
|
pElem = pNew;
|
|
}
|
|
// consider index-access for UnoObjects
|
|
// definitely we want this for VBA where properties are often
|
|
// collections ( which need index access ), but let's only do
|
|
// this if we actually have params following
|
|
else if( bVBAEnabled && dynamic_cast<const SbUnoProperty*>( pElem) != nullptr && pElem->GetParameters() )
|
|
{
|
|
SbxVariableRef xDeleteRef = pElem;
|
|
|
|
// dissolve the notify while copying variable
|
|
SbxVariable* pNew = new SbxVariable( *pElem );
|
|
pElem->SetParameters( nullptr );
|
|
pElem = pNew;
|
|
}
|
|
}
|
|
return CheckArray( pElem );
|
|
}
|
|
|
|
// for current scope (e. g. query from BASIC-IDE)
|
|
SbxBase* SbiRuntime::FindElementExtern( const OUString& rName )
|
|
{
|
|
// don't expect pMeth to be != 0, as there are none set
|
|
// in the RunInit yet
|
|
|
|
SbxVariable* pElem = nullptr;
|
|
if( !pMod || rName.isEmpty() )
|
|
{
|
|
return nullptr;
|
|
}
|
|
if( refLocals.is() )
|
|
{
|
|
pElem = refLocals->Find( rName, SbxClassType::DontCare );
|
|
}
|
|
if ( !pElem && pMeth )
|
|
{
|
|
const OUString aMethName = pMeth->GetName();
|
|
// tdf#57308 - check if the name is the current method instance
|
|
if (pMeth->GetName() == rName)
|
|
{
|
|
pElem = pMeth;
|
|
}
|
|
else
|
|
{
|
|
// for statics, set the method's name in front
|
|
pElem = pMod->Find(aMethName + ":" + rName, SbxClassType::DontCare);
|
|
}
|
|
}
|
|
|
|
|
|
// search in parameter list
|
|
if( !pElem && pMeth )
|
|
{
|
|
SbxInfo* pInfo = pMeth->GetInfo();
|
|
if( pInfo && refParams.is() )
|
|
{
|
|
sal_uInt32 nParamCount = refParams->Count();
|
|
assert(nParamCount <= std::numeric_limits<sal_uInt16>::max());
|
|
sal_uInt16 j = 1;
|
|
const SbxParamInfo* pParam = pInfo->GetParam( j );
|
|
while( pParam )
|
|
{
|
|
if( pParam->aName.equalsIgnoreAsciiCase( rName ) )
|
|
{
|
|
if( j >= nParamCount )
|
|
{
|
|
// Parameter is missing
|
|
pElem = new SbxVariable( SbxSTRING );
|
|
pElem->PutString( u"<missing parameter>"_ustr);
|
|
}
|
|
else
|
|
{
|
|
pElem = refParams->Get(j);
|
|
}
|
|
break;
|
|
}
|
|
pParam = pInfo->GetParam( ++j );
|
|
}
|
|
}
|
|
}
|
|
|
|
// search in module
|
|
if( !pElem )
|
|
{
|
|
bool bSave = rBasic.bNoRtl;
|
|
rBasic.bNoRtl = true;
|
|
pElem = pMod->Find( rName, SbxClassType::DontCare );
|
|
rBasic.bNoRtl = bSave;
|
|
}
|
|
return pElem;
|
|
}
|
|
|
|
|
|
void SbiRuntime::SetupArgs( SbxVariable* p, sal_uInt32 nOp1 )
|
|
{
|
|
if( nOp1 & 0x8000 )
|
|
{
|
|
if( !refArgv.is() )
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
}
|
|
bool bHasNamed = false;
|
|
sal_uInt32 i;
|
|
sal_uInt32 nArgCount = refArgv->Count();
|
|
for( i = 1 ; i < nArgCount ; i++ )
|
|
{
|
|
if (!refArgv->GetAlias(i).isEmpty())
|
|
{
|
|
bHasNamed = true; break;
|
|
}
|
|
}
|
|
if( bHasNamed )
|
|
{
|
|
SbxInfo* pInfo = p->GetInfo();
|
|
if( !pInfo )
|
|
{
|
|
bool bError_ = true;
|
|
|
|
SbUnoMethod* pUnoMethod = dynamic_cast<SbUnoMethod*>( p );
|
|
SbUnoProperty* pUnoProperty = dynamic_cast<SbUnoProperty*>( p );
|
|
if( pUnoMethod || pUnoProperty )
|
|
{
|
|
SbUnoObject* pParentUnoObj = dynamic_cast<SbUnoObject*>( p->GetParent() );
|
|
if( pParentUnoObj )
|
|
{
|
|
Any aUnoAny = pParentUnoObj->getUnoAny();
|
|
Reference< XInvocation > xInvocation;
|
|
aUnoAny >>= xInvocation;
|
|
if( xInvocation.is() ) // TODO: if( xOLEAutomation.is() )
|
|
{
|
|
bError_ = false;
|
|
|
|
sal_uInt32 nCurPar = 1;
|
|
AutomationNamedArgsSbxArray* pArg =
|
|
new AutomationNamedArgsSbxArray( nArgCount );
|
|
OUString* pNames = pArg->getNames().getArray();
|
|
for( i = 1 ; i < nArgCount ; i++ )
|
|
{
|
|
SbxVariable* pVar = refArgv->Get(i);
|
|
OUString aName = refArgv->GetAlias(i);
|
|
if (!aName.isEmpty())
|
|
{
|
|
pNames[i] = aName;
|
|
}
|
|
pArg->Put(pVar, nCurPar++);
|
|
}
|
|
refArgv = pArg;
|
|
}
|
|
}
|
|
}
|
|
else if( bVBAEnabled && p->GetType() == SbxOBJECT && (dynamic_cast<const SbxMethod*>( p) == nullptr || !p->IsBroadcaster()) )
|
|
{
|
|
// Check for default method with named parameters
|
|
SbxBaseRef xObj = p->GetObject();
|
|
if (SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( xObj.get() ))
|
|
{
|
|
Any aAny = pUnoObj->getUnoAny();
|
|
|
|
if( aAny.getValueTypeClass() == TypeClass_INTERFACE )
|
|
{
|
|
Reference< XDefaultMethod > xDfltMethod( aAny, UNO_QUERY );
|
|
|
|
OUString sDefaultMethod;
|
|
if ( xDfltMethod.is() )
|
|
{
|
|
sDefaultMethod = xDfltMethod->getDefaultMethodName();
|
|
}
|
|
if ( !sDefaultMethod.isEmpty() )
|
|
{
|
|
SbxVariable* meth = pUnoObj->Find( sDefaultMethod, SbxClassType::Method );
|
|
if( meth != nullptr )
|
|
{
|
|
pInfo = meth->GetInfo();
|
|
}
|
|
if( pInfo )
|
|
{
|
|
bError_ = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if( bError_ )
|
|
{
|
|
Error( ERRCODE_BASIC_NO_NAMED_ARGS );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sal_uInt32 nCurPar = 1;
|
|
SbxArray* pArg = new SbxArray;
|
|
for( i = 1 ; i < nArgCount ; i++ )
|
|
{
|
|
SbxVariable* pVar = refArgv->Get(i);
|
|
OUString aName = refArgv->GetAlias(i);
|
|
if (!aName.isEmpty())
|
|
{
|
|
// nCurPar is set to the found parameter
|
|
sal_uInt16 j = 1;
|
|
const SbxParamInfo* pParam = pInfo->GetParam( j );
|
|
while( pParam )
|
|
{
|
|
if( pParam->aName.equalsIgnoreAsciiCase( aName ) )
|
|
{
|
|
nCurPar = j;
|
|
break;
|
|
}
|
|
pParam = pInfo->GetParam( ++j );
|
|
}
|
|
if( !pParam )
|
|
{
|
|
Error( ERRCODE_BASIC_NAMED_NOT_FOUND ); break;
|
|
}
|
|
}
|
|
pArg->Put(pVar, nCurPar++);
|
|
}
|
|
refArgv = pArg;
|
|
}
|
|
}
|
|
// own var as parameter 0
|
|
refArgv->Put(p, 0);
|
|
p->SetParameters( refArgv.get() );
|
|
PopArgv();
|
|
}
|
|
else
|
|
{
|
|
p->SetParameters( nullptr );
|
|
}
|
|
}
|
|
|
|
// getting an array element
|
|
|
|
SbxVariable* SbiRuntime::CheckArray( SbxVariable* pElem )
|
|
{
|
|
assert(pElem);
|
|
SbxArray* pPar;
|
|
if( ( pElem->GetType() & SbxARRAY ) && refRedim.get() != pElem )
|
|
{
|
|
SbxBase* pElemObj = pElem->GetObject();
|
|
SbxDimArray* pDimArray = dynamic_cast<SbxDimArray*>( pElemObj );
|
|
pPar = pElem->GetParameters();
|
|
if( pDimArray )
|
|
{
|
|
// parameters may be missing, if an array is
|
|
// passed as an argument
|
|
if( pPar )
|
|
pElem = pDimArray->Get( pPar );
|
|
}
|
|
else
|
|
{
|
|
SbxArray* pArray = dynamic_cast<SbxArray*>( pElemObj );
|
|
if( pArray )
|
|
{
|
|
if( !pPar )
|
|
{
|
|
Error( ERRCODE_BASIC_OUT_OF_RANGE );
|
|
pElem = new SbxVariable;
|
|
}
|
|
else
|
|
{
|
|
pElem = pArray->Get(pPar->Get(1)->GetInteger());
|
|
}
|
|
}
|
|
}
|
|
|
|
// #42940, set parameter 0 to NULL so that var doesn't contain itself
|
|
if( pPar )
|
|
{
|
|
pPar->Put(nullptr, 0);
|
|
}
|
|
}
|
|
// consider index-access for UnoObjects
|
|
else if( pElem->GetType() == SbxOBJECT &&
|
|
dynamic_cast<const SbxMethod*>( pElem) == nullptr &&
|
|
( !bVBAEnabled || dynamic_cast<const SbxProperty*>( pElem) == nullptr ) )
|
|
{
|
|
pPar = pElem->GetParameters();
|
|
if ( pPar )
|
|
{
|
|
// is it a uno-object?
|
|
SbxBaseRef pObj = pElem->GetObject();
|
|
if( pObj.is() )
|
|
{
|
|
if (SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pObj.get()))
|
|
{
|
|
Any aAny = pUnoObj->getUnoAny();
|
|
|
|
if( aAny.getValueTypeClass() == TypeClass_INTERFACE )
|
|
{
|
|
Reference< XIndexAccess > xIndexAccess( aAny, UNO_QUERY );
|
|
if ( !bVBAEnabled )
|
|
{
|
|
if( xIndexAccess.is() )
|
|
{
|
|
sal_uInt32 nParamCount = pPar->Count() - 1;
|
|
if( nParamCount != 1 )
|
|
{
|
|
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
|
|
return pElem;
|
|
}
|
|
|
|
// get index
|
|
sal_Int32 nIndex = pPar->Get(1)->GetLong();
|
|
Reference< XInterface > xRet;
|
|
try
|
|
{
|
|
Any aAny2 = xIndexAccess->getByIndex( nIndex );
|
|
aAny2 >>= xRet;
|
|
}
|
|
catch (const IndexOutOfBoundsException&)
|
|
{
|
|
// usually expect converting problem
|
|
StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
|
|
}
|
|
|
|
// #57847 always create a new variable, else error
|
|
// due to PutObject(NULL) at ReadOnly-properties
|
|
pElem = new SbxVariable( SbxVARIANT );
|
|
if( xRet.is() )
|
|
{
|
|
aAny <<= xRet;
|
|
|
|
// #67173 don't specify a name so that the real class name is entered
|
|
SbxObjectRef xWrapper = static_cast<SbxObject*>(new SbUnoObject( OUString(), aAny ));
|
|
pElem->PutObject( xWrapper.get() );
|
|
}
|
|
else
|
|
{
|
|
pElem->PutObject( nullptr );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// check if there isn't a default member between the current variable
|
|
// and the params, e.g.
|
|
// Dim rst1 As New ADODB.Recordset
|
|
// "
|
|
// val = rst1("FirstName")
|
|
// has the default 'Fields' member between rst1 and '("FirstName")'
|
|
Any x = aAny;
|
|
SbxVariable* pDflt = getDefaultProp( pElem );
|
|
if ( pDflt )
|
|
{
|
|
pDflt->Broadcast( SfxHintId::BasicDataWanted );
|
|
SbxBaseRef pDfltObj = pDflt->GetObject();
|
|
if( pDfltObj.is() )
|
|
{
|
|
if (SbUnoObject* pSbObj = dynamic_cast<SbUnoObject*>(pDfltObj.get()))
|
|
{
|
|
pUnoObj = pSbObj;
|
|
Any aUnoAny = pUnoObj->getUnoAny();
|
|
|
|
if( aUnoAny.getValueTypeClass() == TypeClass_INTERFACE )
|
|
x = std::move(aUnoAny);
|
|
pElem = pDflt;
|
|
}
|
|
}
|
|
}
|
|
OUString sDefaultMethod;
|
|
|
|
Reference< XDefaultMethod > xDfltMethod( x, UNO_QUERY );
|
|
|
|
if ( xDfltMethod.is() )
|
|
{
|
|
sDefaultMethod = xDfltMethod->getDefaultMethodName();
|
|
}
|
|
else if( xIndexAccess.is() )
|
|
{
|
|
sDefaultMethod = "getByIndex";
|
|
}
|
|
if ( !sDefaultMethod.isEmpty() )
|
|
{
|
|
SbxVariable* meth = pUnoObj->Find( sDefaultMethod, SbxClassType::Method );
|
|
SbxVariableRef refTemp = meth;
|
|
if ( refTemp.is() )
|
|
{
|
|
meth->SetParameters( pPar );
|
|
SbxVariable* pNew = new SbxMethod( *static_cast<SbxMethod*>(meth) );
|
|
pElem = pNew;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// #42940, set parameter 0 to NULL so that var doesn't contain itself
|
|
pPar->Put(nullptr, 0);
|
|
}
|
|
else if (BasicCollection* pCol = dynamic_cast<BasicCollection*>(pObj.get()))
|
|
{
|
|
pElem = new SbxVariable( SbxVARIANT );
|
|
pPar->Put(pElem, 0);
|
|
pCol->CollItem( pPar );
|
|
}
|
|
}
|
|
else if( bVBAEnabled ) // !pObj
|
|
{
|
|
SbxArray* pParam = pElem->GetParameters();
|
|
if( pParam != nullptr && !pElem->IsSet( SbxFlagBits::VarToDim ) )
|
|
{
|
|
Error( ERRCODE_BASIC_NO_OBJECT );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return pElem;
|
|
}
|
|
|
|
// loading an element from the runtime-library (+StringID+type)
|
|
|
|
void SbiRuntime::StepRTL( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
PushVar( FindElement( rBasic.pRtl.get(), nOp1, nOp2, ERRCODE_BASIC_PROC_UNDEFINED, false ) );
|
|
}
|
|
|
|
void SbiRuntime::StepFIND_Impl( SbxObject* pObj, sal_uInt32 nOp1, sal_uInt32 nOp2,
|
|
ErrCode nNotFound, bool bStatic )
|
|
{
|
|
if( !refLocals.is() )
|
|
{
|
|
refLocals = new SbxArray;
|
|
}
|
|
PushVar( FindElement( pObj, nOp1, nOp2, nNotFound, true/*bLocal*/, bStatic ) );
|
|
}
|
|
// loading a local/global variable (+StringID+type)
|
|
|
|
void SbiRuntime::StepFIND( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
StepFIND_Impl( pMod, nOp1, nOp2, ERRCODE_BASIC_PROC_UNDEFINED );
|
|
}
|
|
|
|
// Search inside a class module (CM) to enable global search in time
|
|
void SbiRuntime::StepFIND_CM( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
|
|
SbClassModuleObject* pClassModuleObject = dynamic_cast<SbClassModuleObject*>( pMod );
|
|
if( pClassModuleObject )
|
|
{
|
|
pMod->SetFlag( SbxFlagBits::GlobalSearch );
|
|
}
|
|
StepFIND_Impl( pMod, nOp1, nOp2, ERRCODE_BASIC_PROC_UNDEFINED);
|
|
|
|
if( pClassModuleObject )
|
|
{
|
|
pMod->ResetFlag( SbxFlagBits::GlobalSearch );
|
|
}
|
|
}
|
|
|
|
void SbiRuntime::StepFIND_STATIC( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
StepFIND_Impl( pMod, nOp1, nOp2, ERRCODE_BASIC_PROC_UNDEFINED, true );
|
|
}
|
|
|
|
// loading an object-element (+StringID+type)
|
|
// the object lies on TOS
|
|
|
|
void SbiRuntime::StepELEM( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
SbxVariableRef pObjVar = PopVar();
|
|
|
|
SbxObject* pObj = dynamic_cast<SbxObject*>( pObjVar.get() );
|
|
if( !pObj )
|
|
{
|
|
SbxBase* pObjVarObj = pObjVar->GetObject();
|
|
pObj = dynamic_cast<SbxObject*>( pObjVarObj );
|
|
}
|
|
|
|
// #56368 save reference at StepElem, otherwise objects could
|
|
// lose their reference too early in qualification chains like
|
|
// ActiveComponent.Selection(0).Text
|
|
// #74254 now per list
|
|
if( pObj )
|
|
{
|
|
aRefSaved.emplace_back(pObj );
|
|
}
|
|
PushVar( FindElement( pObj, nOp1, nOp2, ERRCODE_BASIC_NO_METHOD, false ) );
|
|
}
|
|
|
|
/** Loading of a parameter (+offset+type)
|
|
If the data type is wrong, create a copy and search for optionals including
|
|
the default value. The data type SbxEMPTY shows that no parameters are given.
|
|
Get( 0 ) may be EMPTY
|
|
|
|
@param nOp1
|
|
the index of the current parameter being processed,
|
|
where the entry of the index 0 is for the return value.
|
|
|
|
@param nOp2
|
|
the data type of the parameter.
|
|
*/
|
|
void SbiRuntime::StepPARAM( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
sal_uInt16 nIdx = static_cast<sal_uInt16>( nOp1 & 0x7FFF );
|
|
SbxDataType eType = static_cast<SbxDataType>(nOp2);
|
|
SbxVariable* pVar;
|
|
|
|
// #57915 solve missing in a cleaner way
|
|
sal_uInt32 nParamCount = refParams->Count();
|
|
if( nIdx >= nParamCount )
|
|
{
|
|
sal_uInt16 iLoop = nIdx;
|
|
while( iLoop >= nParamCount )
|
|
{
|
|
pVar = new SbxVariable();
|
|
pVar->PutErr( 448 ); // like in VB: Error-Code 448 (ERRCODE_BASIC_NAMED_NOT_FOUND)
|
|
// tdf#79426, tdf#125180 - add additional information about a missing parameter
|
|
SetIsMissing( pVar );
|
|
refParams->Put(pVar, iLoop);
|
|
iLoop--;
|
|
}
|
|
}
|
|
pVar = refParams->Get(nIdx);
|
|
|
|
// tdf#79426, tdf#125180 - check for optionals only if the parameter is actually missing
|
|
if( pVar->GetType() == SbxERROR && IsMissing( pVar, 1 ) && nIdx )
|
|
{
|
|
// if there's a parameter missing, it can be OPTIONAL
|
|
bool bOpt = false;
|
|
if( pMeth )
|
|
{
|
|
SbxInfo* pInfo = pMeth->GetInfo();
|
|
if ( pInfo )
|
|
{
|
|
const SbxParamInfo* pParam = pInfo->GetParam( nIdx );
|
|
if( pParam && ( pParam->nFlags & SbxFlagBits::Optional ) )
|
|
{
|
|
// tdf#136143 - reset SbxFlagBits::Fixed in order to prevent type conversion errors
|
|
pVar->ResetFlag( SbxFlagBits::Fixed );
|
|
// Default value?
|
|
sal_uInt16 nDefaultId = static_cast<sal_uInt16>(pParam->nUserData & 0x0ffff);
|
|
if( nDefaultId > 0 )
|
|
{
|
|
// tdf#143707 - check if the data type character was added after the string
|
|
// termination symbol, and convert the variable if it was present. The
|
|
// data type character was added in basic/source/comp/symtbl.cxx.
|
|
SbxDataType eTypeStr;
|
|
OUString aDefaultStr = pImg->GetString( nDefaultId, &eTypeStr );
|
|
pVar = new SbxVariable(pParam-> eType);
|
|
pVar->PutString( aDefaultStr );
|
|
if (eTypeStr != SbxSTRING)
|
|
pVar->Convert(eTypeStr);
|
|
refParams->Put(pVar, nIdx);
|
|
}
|
|
else if ( SbiRuntime::isVBAEnabled() && eType != SbxVARIANT )
|
|
{
|
|
// tdf#36737 - initialize the parameter with the default value of its type
|
|
pVar = new SbxVariable( pParam->eType );
|
|
refParams->Put(pVar, nIdx);
|
|
}
|
|
bOpt = true;
|
|
}
|
|
}
|
|
}
|
|
if( !bOpt )
|
|
{
|
|
Error( ERRCODE_BASIC_NOT_OPTIONAL );
|
|
}
|
|
}
|
|
else if( eType != SbxVARIANT && static_cast<SbxDataType>(pVar->GetType() & 0x0FFF ) != eType )
|
|
{
|
|
// tdf#43003 - convert parameter to the requested type
|
|
pVar->Convert(eType);
|
|
}
|
|
SetupArgs( pVar, nOp1 );
|
|
PushVar( CheckArray( pVar ) );
|
|
}
|
|
|
|
// Case-Test (+True-Target+Test-Opcode)
|
|
|
|
void SbiRuntime::StepCASEIS( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
if (!refCaseStk.is() || !refCaseStk->Count())
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
}
|
|
else
|
|
{
|
|
SbxVariableRef xComp = PopVar();
|
|
SbxVariableRef xCase = refCaseStk->Get(refCaseStk->Count() - 1);
|
|
if( xCase->Compare( static_cast<SbxOperator>(nOp2), *xComp ) )
|
|
{
|
|
StepJUMP( nOp1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
// call of a DLL-procedure (+StringID+type)
|
|
// the StringID's MSB shows that Argv is occupied
|
|
|
|
void SbiRuntime::StepCALL( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
OUString aName = pImg->GetString( nOp1 & 0x7FFF );
|
|
SbxArray* pArgs = nullptr;
|
|
if( nOp1 & 0x8000 )
|
|
{
|
|
pArgs = refArgv.get();
|
|
}
|
|
DllCall( aName, aLibName, pArgs, static_cast<SbxDataType>(nOp2), false );
|
|
aLibName.clear();
|
|
if( nOp1 & 0x8000 )
|
|
{
|
|
PopArgv();
|
|
}
|
|
}
|
|
|
|
// call of a DLL-procedure after CDecl (+StringID+type)
|
|
|
|
void SbiRuntime::StepCALLC( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
OUString aName = pImg->GetString( nOp1 & 0x7FFF );
|
|
SbxArray* pArgs = nullptr;
|
|
if( nOp1 & 0x8000 )
|
|
{
|
|
pArgs = refArgv.get();
|
|
}
|
|
DllCall( aName, aLibName, pArgs, static_cast<SbxDataType>(nOp2), true );
|
|
aLibName.clear();
|
|
if( nOp1 & 0x8000 )
|
|
{
|
|
PopArgv();
|
|
}
|
|
}
|
|
|
|
|
|
// beginning of a statement (+Line+Col)
|
|
|
|
void SbiRuntime::StepSTMNT( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
// If the Expr-Stack at the beginning of a statement contains a variable,
|
|
// some fool has called X as a function, although it's a variable!
|
|
bool bFatalExpr = false;
|
|
OUString sUnknownMethodName;
|
|
if( nExprLvl > 1 )
|
|
{
|
|
bFatalExpr = true;
|
|
}
|
|
else if( nExprLvl )
|
|
{
|
|
SbxVariable* p = refExprStk->Get(0);
|
|
if( p->GetRefCount() > 1 &&
|
|
refLocals.is() && refLocals->Find( p->GetName(), p->GetClass() ) )
|
|
{
|
|
sUnknownMethodName = p->GetName();
|
|
bFatalExpr = true;
|
|
}
|
|
}
|
|
|
|
ClearExprStack();
|
|
|
|
aRefSaved.clear();
|
|
|
|
// We have to cancel hard here because line and column
|
|
// would be wrong later otherwise!
|
|
if( bFatalExpr)
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_NO_METHOD, sUnknownMethodName );
|
|
return;
|
|
}
|
|
pStmnt = pCode - 9;
|
|
sal_uInt16 nOld = nLine;
|
|
nLine = static_cast<short>( nOp1 );
|
|
|
|
// #29955 & 0xFF, to filter out for-loop-level
|
|
nCol1 = static_cast<short>( nOp2 & 0xFF );
|
|
|
|
// find the next STMNT-command to set the final column
|
|
// of this statement
|
|
|
|
nCol2 = 0xffff;
|
|
sal_uInt16 n1, n2;
|
|
const sal_uInt8* p = pMod->FindNextStmnt( pCode, n1, n2 );
|
|
if( p )
|
|
{
|
|
if( n1 == nOp1 )
|
|
{
|
|
// #29955 & 0xFF, to filter out for-loop-level
|
|
nCol2 = (n2 & 0xFF) - 1;
|
|
}
|
|
}
|
|
|
|
// #29955 correct for-loop-level, #67452 NOT in the error-handler
|
|
if( !bInError )
|
|
{
|
|
// (there's a difference here in case of a jump out of a loop)
|
|
sal_uInt16 nExpectedForLevel = static_cast<sal_uInt16>( nOp2 / 0x100 );
|
|
if( !pGosubStk.empty() )
|
|
{
|
|
nExpectedForLevel = nExpectedForLevel + pGosubStk.back().nStartForLvl;
|
|
}
|
|
|
|
// if the actual for-level is too small it'd jump out
|
|
// of a loop -> corrected
|
|
while( nForLvl > nExpectedForLevel )
|
|
{
|
|
PopFor();
|
|
}
|
|
}
|
|
|
|
// 16.10.96: #31460 new concept for StepInto/Over/Out
|
|
// see explanation at SbiInstance::CalcBreakCallLevel
|
|
if( pInst->nCallLvl <= pInst->nBreakCallLvl )
|
|
{
|
|
StarBASIC* pStepBasic = GetCurrentBasic( &rBasic );
|
|
BasicDebugFlags nNewFlags = pStepBasic->StepPoint( nLine, nCol1, nCol2 );
|
|
|
|
pInst->CalcBreakCallLevel( nNewFlags );
|
|
}
|
|
|
|
// break points only at STMNT-commands in a new line!
|
|
else if( ( nOp1 != nOld )
|
|
&& ( nFlags & BasicDebugFlags::Break )
|
|
&& pMod->IsBP( static_cast<sal_uInt16>( nOp1 ) ) )
|
|
{
|
|
StarBASIC* pBreakBasic = GetCurrentBasic( &rBasic );
|
|
BasicDebugFlags nNewFlags = pBreakBasic->BreakPoint( nLine, nCol1, nCol2 );
|
|
|
|
pInst->CalcBreakCallLevel( nNewFlags );
|
|
}
|
|
}
|
|
|
|
// (+StreamMode+Flags)
|
|
// Stack: block length
|
|
// channel number
|
|
// file name
|
|
|
|
void SbiRuntime::StepOPEN( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
SbxVariableRef pName = PopVar();
|
|
SbxVariableRef pChan = PopVar();
|
|
SbxVariableRef pLen = PopVar();
|
|
short nBlkLen = pLen->GetInteger();
|
|
short nChan = pChan->GetInteger();
|
|
OString aName(OUStringToOString(pName->GetOUString(), osl_getThreadTextEncoding()));
|
|
pIosys->Open( nChan, aName, static_cast<StreamMode>( nOp1 ),
|
|
static_cast<SbiStreamFlags>( nOp2 ), nBlkLen );
|
|
Error( pIosys->GetError() );
|
|
}
|
|
|
|
// create object (+StringID+StringID)
|
|
|
|
void SbiRuntime::StepCREATE( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
OUString aClass( pImg->GetString( nOp2 ) );
|
|
SbxObjectRef pObj = SbxBase::CreateObject( aClass );
|
|
if( !pObj )
|
|
{
|
|
Error( ERRCODE_BASIC_INVALID_OBJECT );
|
|
}
|
|
else
|
|
{
|
|
OUString aName( pImg->GetString( nOp1 ) );
|
|
pObj->SetName( aName );
|
|
// the object must be able to call the BASIC
|
|
pObj->SetParent( &rBasic );
|
|
SbxVariableRef pNew = new SbxVariable;
|
|
pNew->PutObject( pObj.get() );
|
|
PushVar( pNew.get() );
|
|
}
|
|
}
|
|
|
|
void SbiRuntime::StepDCREATE( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
StepDCREATE_IMPL( nOp1, nOp2 );
|
|
}
|
|
|
|
void SbiRuntime::StepDCREATE_REDIMP( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
StepDCREATE_IMPL( nOp1, nOp2 );
|
|
}
|
|
|
|
// #56204 create object array (+StringID+StringID), DCREATE == Dim-Create
|
|
void SbiRuntime::StepDCREATE_IMPL( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
SbxVariableRef refVar = PopVar();
|
|
|
|
DimImpl( refVar );
|
|
|
|
// fill the array with instances of the requested class
|
|
SbxBase* pObj = refVar->GetObject();
|
|
if (!pObj)
|
|
{
|
|
StarBASIC::Error( ERRCODE_BASIC_INVALID_OBJECT );
|
|
return;
|
|
}
|
|
|
|
SbxDimArray* pArray = dynamic_cast<SbxDimArray*>(pObj);
|
|
if (!pArray)
|
|
return;
|
|
|
|
const sal_Int32 nDims = pArray->GetDims();
|
|
sal_Int32 nTotalSize = nDims > 0 ? 1 : 0;
|
|
|
|
// must be a one-dimensional array
|
|
sal_Int32 nLower, nUpper;
|
|
for( sal_Int32 i = 0 ; i < nDims ; ++i )
|
|
{
|
|
pArray->GetDim(i + 1, nLower, nUpper);
|
|
const sal_Int32 nSize = nUpper - nLower + 1;
|
|
nTotalSize *= nSize;
|
|
}
|
|
|
|
// Optimization: pre-allocate underlying container
|
|
if (nTotalSize > 0)
|
|
pArray->SbxArray::GetRef(nTotalSize - 1);
|
|
|
|
// First, fill those parts of the array that are preserved
|
|
bool bWasError = false;
|
|
const bool bRestored = implRestorePreservedArray(pArray, refRedimpArray, &bWasError);
|
|
if (bWasError)
|
|
nTotalSize = 0; // on error, don't create objects
|
|
|
|
// create objects and insert them into the array
|
|
OUString aClass( pImg->GetString( nOp2 ) );
|
|
OUString aName;
|
|
for( sal_Int32 i = 0 ; i < nTotalSize ; ++i )
|
|
{
|
|
if (!bRestored || !pArray->SbxArray::GetRef(i)) // For those left unset after preserve
|
|
{
|
|
SbxObjectRef pClassObj = SbxBase::CreateObject(aClass);
|
|
if (!pClassObj)
|
|
{
|
|
Error(ERRCODE_BASIC_INVALID_OBJECT);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (aName.isEmpty())
|
|
aName = pImg->GetString(nOp1);
|
|
pClassObj->SetName(aName);
|
|
// the object must be able to call the basic
|
|
pClassObj->SetParent(&rBasic);
|
|
pArray->SbxArray::Put(pClassObj.get(), i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SbiRuntime::StepTCREATE( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
OUString aName( pImg->GetString( nOp1 ) );
|
|
OUString aClass( pImg->GetString( nOp2 ) );
|
|
|
|
SbxObjectRef pCopyObj = createUserTypeImpl( aClass );
|
|
if( pCopyObj )
|
|
{
|
|
pCopyObj->SetName( aName );
|
|
}
|
|
SbxVariableRef pNew = new SbxVariable;
|
|
pNew->PutObject( pCopyObj.get() );
|
|
pNew->SetDeclareClassName( aClass );
|
|
PushVar( pNew.get() );
|
|
}
|
|
|
|
void SbiRuntime::implHandleSbxFlags( SbxVariable* pVar, SbxDataType t, sal_uInt32 nOp2 )
|
|
{
|
|
bool bWithEvents = ((t & 0xff) == SbxOBJECT && (nOp2 & SBX_TYPE_WITH_EVENTS_FLAG) != 0);
|
|
if( bWithEvents )
|
|
{
|
|
pVar->SetFlag( SbxFlagBits::WithEvents );
|
|
}
|
|
bool bDimAsNew = ((nOp2 & SBX_TYPE_DIM_AS_NEW_FLAG) != 0);
|
|
if( bDimAsNew )
|
|
{
|
|
pVar->SetFlag( SbxFlagBits::DimAsNew );
|
|
}
|
|
bool bFixedString = ((t & 0xff) == SbxSTRING && (nOp2 & SBX_FIXED_LEN_STRING_FLAG) != 0);
|
|
if( bFixedString )
|
|
{
|
|
sal_uInt16 nCount = static_cast<sal_uInt16>( nOp2 >> 17 ); // len = all bits above 0x10000
|
|
OUStringBuffer aBuf(nCount);
|
|
comphelper::string::padToLength(aBuf, nCount);
|
|
pVar->PutString(aBuf.makeStringAndClear());
|
|
}
|
|
|
|
bool bVarToDim = ((nOp2 & SBX_TYPE_VAR_TO_DIM_FLAG) != 0);
|
|
if( bVarToDim )
|
|
{
|
|
pVar->SetFlag( SbxFlagBits::VarToDim );
|
|
}
|
|
}
|
|
|
|
// establishing a local variable (+StringID+type)
|
|
|
|
void SbiRuntime::StepLOCAL( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
if( !refLocals.is() )
|
|
{
|
|
refLocals = new SbxArray;
|
|
}
|
|
OUString aName( pImg->GetString( nOp1 ) );
|
|
if( refLocals->Find( aName, SbxClassType::DontCare ) == nullptr )
|
|
{
|
|
SbxDataType t = static_cast<SbxDataType>(nOp2 & 0xffff);
|
|
SbxVariable* p = new SbxVariable( t );
|
|
p->SetName( aName );
|
|
implHandleSbxFlags( p, t, nOp2 );
|
|
refLocals->Put(p, refLocals->Count());
|
|
}
|
|
}
|
|
|
|
// establishing a module-global variable (+StringID+type)
|
|
|
|
void SbiRuntime::StepPUBLIC_Impl( sal_uInt32 nOp1, sal_uInt32 nOp2, bool bUsedForClassModule )
|
|
{
|
|
OUString aName( pImg->GetString( nOp1 ) );
|
|
SbxDataType t = static_cast<SbxDataType>(nOp2 & 0xffff);
|
|
bool bFlag = pMod->IsSet( SbxFlagBits::NoModify );
|
|
pMod->SetFlag( SbxFlagBits::NoModify );
|
|
SbxVariableRef p = pMod->Find( aName, SbxClassType::Property );
|
|
if( p.is() )
|
|
{
|
|
pMod->Remove (p.get());
|
|
}
|
|
SbProperty* pProp = pMod->GetProperty( aName, t );
|
|
if( !bUsedForClassModule )
|
|
{
|
|
pProp->SetFlag( SbxFlagBits::Private );
|
|
}
|
|
if( !bFlag )
|
|
{
|
|
pMod->ResetFlag( SbxFlagBits::NoModify );
|
|
}
|
|
if( pProp )
|
|
{
|
|
pProp->SetFlag( SbxFlagBits::DontStore );
|
|
// from 2.7.1996: HACK because of 'reference can't be saved'
|
|
pProp->SetFlag( SbxFlagBits::NoModify);
|
|
|
|
implHandleSbxFlags( pProp, t, nOp2 );
|
|
}
|
|
}
|
|
|
|
void SbiRuntime::StepPUBLIC( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
StepPUBLIC_Impl( nOp1, nOp2, false );
|
|
}
|
|
|
|
void SbiRuntime::StepPUBLIC_P( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
// Creates module variable that isn't reinitialised when
|
|
// between invocations ( for VBASupport & document basic only )
|
|
if( pMod->pImage->bFirstInit )
|
|
{
|
|
bool bUsedForClassModule = pImg->IsFlag( SbiImageFlags::CLASSMODULE );
|
|
StepPUBLIC_Impl( nOp1, nOp2, bUsedForClassModule );
|
|
}
|
|
}
|
|
|
|
// establishing a global variable (+StringID+type)
|
|
|
|
void SbiRuntime::StepGLOBAL( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
if( pImg->IsFlag( SbiImageFlags::CLASSMODULE ) )
|
|
{
|
|
StepPUBLIC_Impl( nOp1, nOp2, true );
|
|
}
|
|
OUString aName( pImg->GetString( nOp1 ) );
|
|
SbxDataType t = static_cast<SbxDataType>(nOp2 & 0xffff);
|
|
|
|
// Store module scope variables at module scope
|
|
// in non vba mode these are stored at the library level :/
|
|
// not sure if this really should not be enabled for ALL basic
|
|
SbxObject* pStorage = &rBasic;
|
|
if ( SbiRuntime::isVBAEnabled() )
|
|
{
|
|
pStorage = pMod;
|
|
pMod->AddVarName( aName );
|
|
}
|
|
|
|
bool bFlag = pStorage->IsSet( SbxFlagBits::NoModify );
|
|
rBasic.SetFlag( SbxFlagBits::NoModify );
|
|
SbxVariableRef p = pStorage->Find( aName, SbxClassType::Property );
|
|
if( p.is() )
|
|
{
|
|
pStorage->Remove (p.get());
|
|
}
|
|
p = pStorage->Make( aName, SbxClassType::Property, t );
|
|
if( !bFlag )
|
|
{
|
|
pStorage->ResetFlag( SbxFlagBits::NoModify );
|
|
}
|
|
if( p.is() )
|
|
{
|
|
p->SetFlag( SbxFlagBits::DontStore );
|
|
// from 2.7.1996: HACK because of 'reference can't be saved'
|
|
p->SetFlag( SbxFlagBits::NoModify);
|
|
}
|
|
}
|
|
|
|
|
|
// Creates global variable that isn't reinitialised when
|
|
// basic is restarted, P=PERSIST (+StringID+Typ)
|
|
|
|
void SbiRuntime::StepGLOBAL_P( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
if( pMod->pImage->bFirstInit )
|
|
{
|
|
StepGLOBAL( nOp1, nOp2 );
|
|
}
|
|
}
|
|
|
|
|
|
// Searches for global variable, behavior depends on the fact
|
|
// if the variable is initialised for the first time
|
|
|
|
void SbiRuntime::StepFIND_G( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
if( pMod->pImage->bFirstInit )
|
|
{
|
|
// Behave like always during first init
|
|
StepFIND( nOp1, nOp2 );
|
|
}
|
|
else
|
|
{
|
|
// Return dummy variable
|
|
SbxDataType t = static_cast<SbxDataType>(nOp2);
|
|
OUString aName( pImg->GetString( nOp1 & 0x7FFF ) );
|
|
|
|
SbxVariable* pDummyVar = new SbxVariable( t );
|
|
pDummyVar->SetName( aName );
|
|
PushVar( pDummyVar );
|
|
}
|
|
}
|
|
|
|
|
|
SbxVariable* SbiRuntime::StepSTATIC_Impl(
|
|
OUString const & aName, SbxDataType t, sal_uInt32 nOp2 )
|
|
{
|
|
SbxVariable* p = nullptr;
|
|
if ( pMeth )
|
|
{
|
|
SbxArray* pStatics = pMeth->GetStatics();
|
|
if( pStatics && ( pStatics->Find( aName, SbxClassType::DontCare ) == nullptr ) )
|
|
{
|
|
p = new SbxVariable( t );
|
|
if( t != SbxVARIANT )
|
|
{
|
|
p->SetFlag( SbxFlagBits::Fixed );
|
|
}
|
|
p->SetName( aName );
|
|
implHandleSbxFlags( p, t, nOp2 );
|
|
pStatics->Put(p, pStatics->Count());
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
// establishing a static variable (+StringID+type)
|
|
void SbiRuntime::StepSTATIC( sal_uInt32 nOp1, sal_uInt32 nOp2 )
|
|
{
|
|
OUString aName( pImg->GetString( nOp1 ) );
|
|
SbxDataType t = static_cast<SbxDataType>(nOp2 & 0xffff);
|
|
StepSTATIC_Impl( aName, t, nOp2 );
|
|
}
|
|
|
|
#endif
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|