/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_basic.hxx"
#include <tools/fsys.hxx>
#include <vcl/svapp.hxx>
#include <tools/wldcrd.hxx>
#include <svl/zforlist.hxx>
#include <unotools/syslocale.hxx>
#include "runtime.hxx"
#include "sbintern.hxx"
#include "opcodes.hxx"
#include "codegen.hxx"
#include "iosys.hxx"
#include "image.hxx"
#include "ddectrl.hxx"
#include "dllmgr.hxx"
#include <comphelper/processfactory.hxx>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include "sbunoobj.hxx"
#include "errobject.hxx"
#include "sbtrace.hxx"
#include "comenumwrapper.hxx"

using namespace ::com::sun::star;

bool SbiRuntime::isVBAEnabled()
{
	bool result = false;
	SbiInstance* pInst = pINST;
	if ( pInst && pINST->pRun )
		result = pInst->pRun->bVBAEnabled;
	return result; 
}

// #91147 Global reschedule flag
static sal_Bool bStaticGlobalEnableReschedule = sal_True;

void StarBASIC::StaticEnableReschedule( sal_Bool bReschedule )
{
    bStaticGlobalEnableReschedule = bReschedule;
}
void StarBASIC::SetVBAEnabled( sal_Bool bEnabled )
{
    if ( bDocBasic )
    {
        bVBAEnabled = bEnabled;
    }
}

sal_Bool StarBASIC::isVBAEnabled()
{
    if ( bDocBasic )
    { 
        if( SbiRuntime::isVBAEnabled() )
            return sal_True;
        return bVBAEnabled;
    }
    return sal_False;
}


struct SbiArgvStack {					// Argv stack:
	SbiArgvStack*  pNext;	   			// Stack Chain
	SbxArrayRef    refArgv;	 			// Argv
	short nArgc;						// Argc
};

SbiRuntime::pStep0 SbiRuntime::aStep0[] = {	// Alle Opcodes ohne Operanden
	&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,
	// Laden/speichern
	&SbiRuntime::StepARGC,		// neuen Argv einrichten
	&SbiRuntime::StepARGV,		// TOS ==> aktueller Argv
	&SbiRuntime::StepINPUT,	 	// Input ==> TOS
	&SbiRuntime::StepLINPUT,	 	// Line Input ==> TOS
	&SbiRuntime::StepGET,        // TOS anfassen
	&SbiRuntime::StepSET,        // Speichern Objekt TOS ==> TOS-1
	&SbiRuntime::StepPUT,		// TOS ==> TOS-1
	&SbiRuntime::StepPUTC,		// TOS ==> TOS-1, dann ReadOnly
	&SbiRuntime::StepDIM,		// DIM
	&SbiRuntime::StepREDIM, 		// REDIM
	&SbiRuntime::StepREDIMP,		// REDIM PRESERVE
	&SbiRuntime::StepERASE, 		// TOS loeschen
	// Verzweigen
	&SbiRuntime::StepSTOP,  	  	// Programmende
	&SbiRuntime::StepINITFOR,  	// FOR-Variable initialisieren
	&SbiRuntime::StepNEXT,	  	// FOR-Variable inkrementieren
	&SbiRuntime::StepCASE,	  	// Anfang CASE
	&SbiRuntime::StepENDCASE,  	// Ende CASE
	&SbiRuntime::StepSTDERROR,  	// Standard-Fehlerbehandlung
	&SbiRuntime::StepNOERROR,  	// keine Fehlerbehandlung
	&SbiRuntime::StepLEAVE,		// UP verlassen
	// E/A
	&SbiRuntime::StepCHANNEL,  	// TOS = Kanalnummer
	&SbiRuntime::StepPRINT,	  	// print TOS
	&SbiRuntime::StepPRINTF,	  	// print TOS in field
	&SbiRuntime::StepWRITE,	  	// write TOS
	&SbiRuntime::StepRENAME,	  	// Rename Tos+1 to Tos
	&SbiRuntime::StepPROMPT,	  	// Input Prompt aus TOS definieren
	&SbiRuntime::StepRESTART,  	// Set restart point
	&SbiRuntime::StepCHANNEL0,	// E/A-Kanal 0 einstellen
	&SbiRuntime::StepEMPTY,		// Leeren Ausdruck auf Stack
	&SbiRuntime::StepERROR,	  	// TOS = Fehlercode
	&SbiRuntime::StepLSET,		// Speichern Objekt TOS ==> TOS-1
	&SbiRuntime::StepRSET,		// Speichern Objekt 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
};

SbiRuntime::pStep1 SbiRuntime::aStep1[] = {	// Alle Opcodes mit einem Operanden
	&SbiRuntime::StepLOADNC,	  	// Laden einer numerischen Konstanten (+ID)
	&SbiRuntime::StepLOADSC,	  	// Laden einer Stringkonstanten (+ID)
	&SbiRuntime::StepLOADI,	  	// Immediate Load (+Wert)
	&SbiRuntime::StepARGN,		// Speichern eines named Args in Argv (+StringID)
	&SbiRuntime::StepPAD,	  	// String auf feste Laenge bringen (+Laenge)
	// Verzweigungen
	&SbiRuntime::StepJUMP,	  	// Sprung (+Target)
	&SbiRuntime::StepJUMPT,	  	// TOS auswerten), bedingter Sprung (+Target)
	&SbiRuntime::StepJUMPF,	  	// TOS auswerten), bedingter Sprung (+Target)
	&SbiRuntime::StepONJUMP,	  	// TOS auswerten), Sprung in JUMP-Tabelle (+MaxVal)
	&SbiRuntime::StepGOSUB,		// UP-Aufruf (+Target)
	&SbiRuntime::StepRETURN,		// UP-Return (+0 oder Target)
	&SbiRuntime::StepTESTFOR,	// FOR-Variable testen), inkrementieren (+Endlabel)
	&SbiRuntime::StepCASETO,		// Tos+1 <= Case <= Tos), 2xremove (+Target)
	&SbiRuntime::StepERRHDL,		// Fehler-Handler (+Offset)
	&SbiRuntime::StepRESUME,		// Resume nach Fehlern (+0 or 1 or Label)
	// E/A
	&SbiRuntime::StepCLOSE,		// (+Kanal/0)
	&SbiRuntime::StepPRCHAR,		// (+char)
	// Verwaltung
	&SbiRuntime::StepSETCLASS,	// Set + Klassennamen testen (+StringId)
	&SbiRuntime::StepTESTCLASS,	// Check TOS class (+StringId)
	&SbiRuntime::StepLIB,  		// Lib fuer Declare-Call (+StringId)
	&SbiRuntime::StepBASED,	  	// TOS wird um BASE erhoeht, BASE davor gepusht
	&SbiRuntime::StepARGTYP,	  	// Letzten Parameter in Argv konvertieren (+Typ)
	&SbiRuntime::StepVBASETCLASS,// vba-like set statement
};

SbiRuntime::pStep2 SbiRuntime::aStep2[] = {// Alle Opcodes mit zwei Operanden
	&SbiRuntime::StepRTL,  	    // Laden aus RTL (+StringID+Typ)
	&SbiRuntime::StepFIND,	    // Laden (+StringID+Typ)
	&SbiRuntime::StepELEM,  		// Laden Element (+StringID+Typ)
	&SbiRuntime::StepPARAM,		// Parameter (+Offset+Typ)
	// Verzweigen
	&SbiRuntime::StepCALL,	  	// Declare-Call (+StringID+Typ)
	&SbiRuntime::StepCALLC,	  	// CDecl-Declare-Call (+StringID+Typ)
	&SbiRuntime::StepCASEIS,		// Case-Test (+Test-Opcode+False-Target)
	// Verwaltung
	&SbiRuntime::StepSTMNT, 		// Beginn eines Statements (+Line+Col)
	// E/A
	&SbiRuntime::StepOPEN,  		// (+SvStreamFlags+Flags)
	// Objekte
	&SbiRuntime::StepLOCAL,		// Lokale Variable definieren (+StringId+Typ)
	&SbiRuntime::StepPUBLIC,		// Modulglobale Variable (+StringID+Typ)
	&SbiRuntime::StepGLOBAL,	  	// Globale Variable definieren (+StringID+Typ)
	&SbiRuntime::StepCREATE,		// Objekt kreieren (+StringId+StringId)
	&SbiRuntime::StepSTATIC,     // Statische Variable (+StringId+StringId)
	&SbiRuntime::StepTCREATE,    // User Defined Objekte (+StringId+StringId)
	&SbiRuntime::StepDCREATE,    // Objekt-Array kreieren (+StringID+StringID)
	&SbiRuntime::StepGLOBAL_P,   // Globale Variable definieren, die beim Neustart 
                                        // von Basic nicht ueberschrieben wird (+StringID+Typ)
	&SbiRuntime::StepFIND_G,    	// Sucht globale Variable mit Spezialbehandlung wegen _GLOBAL_P
	&SbiRuntime::StepDCREATE_REDIMP, // Objekt-Array redimensionieren (+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()
{
	pDir		= 0;
	nDirFlags	= 0;
	nCurDirPos	= 0;
	pWildCard	= NULL;
}

SbiRTLData::~SbiRTLData()
{
	delete pDir;
	pDir = 0;
	delete pWildCard;
}

//////////////////////////////////////////////////////////////////////////
//								SbiInstance								//
//////////////////////////////////////////////////////////////////////////

// 16.10.96: #31460 Neues Konzept fuer StepInto/Over/Out
// Die Entscheidung, ob StepPoint aufgerufen werden soll, wird anhand des
// CallLevels getroffen. Angehalten wird, wenn der aktuelle CallLevel <=
// nBreakCallLvl ist. Der aktuelle CallLevel kann niemals kleiner als 1
// sein, da er beim Aufruf einer Methode (auch main) inkrementiert wird.
// Daher bedeutet ein BreakCallLvl von 0, dass das Programm gar nicht
// angehalten wird.
// (siehe auch step2.cxx, SbiRuntime::StepSTMNT() )

// Hilfsfunktion, um den BreakCallLevel gemaess der der Debug-Flags zu ermitteln
void SbiInstance::CalcBreakCallLevel( sal_uInt16 nFlags )
{
	// Break-Flag wegfiltern
	nFlags &= ~((sal_uInt16)SbDEBUG_BREAK);

	sal_uInt16 nRet;
	switch( nFlags )
	{
		case SbDEBUG_STEPINTO:
			nRet = nCallLvl + 1;	// CallLevel+1 wird auch angehalten
			break;
		case SbDEBUG_STEPOVER | SbDEBUG_STEPINTO:
			nRet = nCallLvl;		// Aktueller CallLevel wird angehalten
			break;
		case SbDEBUG_STEPOUT:
			nRet = nCallLvl - 1;	// Kleinerer CallLevel wird angehalten
			break;
		case SbDEBUG_CONTINUE:
		// Basic-IDE liefert 0 statt SbDEBUG_CONTINUE, also auch default=continue
		default:
			nRet = 0;				// CallLevel ist immer >0 -> kein StepPoint
	}
	nBreakCallLvl = nRet;			// Ergebnis uebernehmen
}

SbiInstance::SbiInstance( StarBASIC* p )
{
	pBasic   = p;
	pNext    = NULL;
	pRun     = NULL;
	pIosys   = new SbiIoSystem;
	pDdeCtrl = new SbiDdeControl;
	pDllMgr	 = 0; // on demand
	pNumberFormatter = 0; // on demand
	nCallLvl = 0;
	nBreakCallLvl = 0;
	nErr	 =
	nErl	 = 0;
	bReschedule = sal_True;
	bCompatibility = sal_False;
}

SbiInstance::~SbiInstance()
{
	while( pRun )
	{
		SbiRuntime* p = pRun->pNext;
		delete pRun;
		pRun = p;
	}
	delete pIosys;
	delete pDdeCtrl;
	delete pDllMgr;
	delete pNumberFormatter;

	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& )
	{
		DBG_ERROR( "SbiInstance::~SbiInstance: caught an exception while disposing the components!" );
	}

    ComponentVector.clear();
}

SbiDllMgr* SbiInstance::GetDllMgr()
{
	if( !pDllMgr )
		pDllMgr = new SbiDllMgr;
	return pDllMgr;
}

// #39629 NumberFormatter jetzt ueber statische Methode anlegen
SvNumberFormatter* SbiInstance::GetNumberFormatter()
{
    LanguageType eLangType = GetpApp()->GetSettings().GetLanguage();
    SvtSysLocale aSysLocale;
    DateFormat eDate = aSysLocale.GetLocaleData().getDateFormat();
	if( pNumberFormatter )
    {
        if( eLangType != meFormatterLangType ||
            eDate != meFormatterDateFormat )
        {
            delete pNumberFormatter;
            pNumberFormatter = NULL;
        }
    }
    meFormatterLangType = eLangType;
    meFormatterDateFormat = eDate;
	if( !pNumberFormatter )
		PrepareNumberFormatter( pNumberFormatter, nStdDateIdx, nStdTimeIdx, nStdDateTimeIdx,
        &meFormatterLangType, &meFormatterDateFormat );
	return pNumberFormatter;
}

// #39629 NumberFormatter auch statisch anbieten
void SbiInstance::PrepareNumberFormatter( SvNumberFormatter*& rpNumberFormatter,
	sal_uInt32 &rnStdDateIdx, sal_uInt32 &rnStdTimeIdx, sal_uInt32 &rnStdDateTimeIdx,
    LanguageType* peFormatterLangType, DateFormat* peFormatterDateFormat )
{
	com::sun::star::uno::Reference< com::sun::star::lang::XMultiServiceFactory > 
		xFactory = comphelper::getProcessServiceFactory();

    LanguageType eLangType;
    if( peFormatterLangType )
        eLangType = *peFormatterLangType;
    else
        eLangType = GetpApp()->GetSettings().GetLanguage();

    DateFormat eDate;
    if( peFormatterDateFormat )
        eDate = *peFormatterDateFormat;
    else
    {
        SvtSysLocale aSysLocale;
        eDate = aSysLocale.GetLocaleData().getDateFormat();
    }

    rpNumberFormatter = new SvNumberFormatter( xFactory, eLangType );

	xub_StrLen nCheckPos = 0; short nType;
	rnStdTimeIdx = rpNumberFormatter->GetStandardFormat( NUMBERFORMAT_TIME, eLangType );

	// Standard-Vorlagen des Formatters haben nur zweistellige
	// Jahreszahl. Deshalb eigenes Format registrieren

	// HACK, da der Numberformatter in PutandConvertEntry die Platzhalter
	// fuer Monat, Tag, Jahr nicht entsprechend der Systemeinstellung
	// austauscht. Problem: Print Year(Date) unter engl. BS
	// siehe auch svtools\source\sbx\sbxdate.cxx

	String aDateStr;
	switch( eDate )
	{
		case MDY: aDateStr = String( RTL_CONSTASCII_USTRINGPARAM("MM.TT.JJJJ") ); break;
		case DMY: aDateStr = String( RTL_CONSTASCII_USTRINGPARAM("TT.MM.JJJJ") ); break;
		case YMD: aDateStr = String( RTL_CONSTASCII_USTRINGPARAM("JJJJ.MM.TT") ); break;
		default:  aDateStr = String( RTL_CONSTASCII_USTRINGPARAM("MM.TT.JJJJ") );
	}
	String aStr( aDateStr );
	rpNumberFormatter->PutandConvertEntry( aStr, nCheckPos, nType,
		rnStdDateIdx, LANGUAGE_GERMAN, eLangType );
	nCheckPos = 0;
	String aStrHHMMSS( RTL_CONSTASCII_USTRINGPARAM(" HH:MM:SS") );
	aStr = aDateStr;
	aStr += aStrHHMMSS;
	rpNumberFormatter->PutandConvertEntry( aStr, nCheckPos, nType,
		rnStdDateTimeIdx, LANGUAGE_GERMAN, eLangType );
}



// Engine laufenlassen. Falls Flags == SbDEBUG_CONTINUE, Flags uebernehmen

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( SbError n )
{
	Error( n, String() );
}

void SbiInstance::Error( SbError n, const String& rMsg )
{
	if( !bWatchMode )
	{
		aErrorMsg = rMsg;
		pRun->Error( n );
	}
}

void SbiInstance::ErrorVB( sal_Int32 nVBNumber, const String& rMsg )
{
	if( !bWatchMode )
	{
		SbError n = StarBASIC::GetSfxFromVBError( static_cast< sal_uInt16 >( nVBNumber ) );
		if ( !n )
			n = nVBNumber; // force orig number, probably should have a specific table of vb ( localized ) errors

		aErrorMsg = rMsg;
		SbiRuntime::translateErrorToVba( n, aErrorMsg );

		bool bVBATranslationAlreadyDone = true;
		pRun->Error( SbERR_BASIC_COMPAT, bVBATranslationAlreadyDone );
	}
}

void SbiInstance::setErrorVB( sal_Int32 nVBNumber, const String& rMsg )
{
	SbError n = StarBASIC::GetSfxFromVBError( static_cast< sal_uInt16 >( nVBNumber ) );
	if( !n )
		n = nVBNumber; // force orig number, probably should have a specific table of vb ( localized ) errors

	aErrorMsg = rMsg;
	SbiRuntime::translateErrorToVba( n, aErrorMsg );

	nErr = n;
}


void SbiInstance::FatalError( SbError n )
{
	pRun->FatalError( n );
}

void SbiInstance::FatalError( SbError _errCode, const String& _details )
{
	pRun->FatalError( _errCode, _details );
}

void SbiInstance::Abort()
{
	// Basic suchen, in dem der Fehler auftrat
	StarBASIC* pErrBasic = GetCurrentBasic( pBasic );
	pErrBasic->RTError( nErr, aErrorMsg, pRun->nLine, pRun->nCol1, pRun->nCol2 );
	pBasic->Stop();
}

// Hilfsfunktion, um aktives Basic zu finden, kann ungleich pRTBasic sein
StarBASIC* GetCurrentBasic( StarBASIC* pRTBasic )
{
	StarBASIC* pCurBasic = pRTBasic;
	SbModule* pActiveModule = pRTBasic->GetActiveModule();
	if( pActiveModule )
	{
		SbxObject* pParent = pActiveModule->GetParent();
		if( pParent && pParent->ISA(StarBASIC) )
			pCurBasic = (StarBASIC*)pParent;
	}
	return pCurBasic;
}

SbModule* SbiInstance::GetActiveModule()
{
	if( pRun )
		return pRun->GetModule();
	else
		return NULL;
}

SbMethod* SbiInstance::GetCaller( sal_uInt16 nLevel )
{
	SbiRuntime* p = pRun;
	while( nLevel-- && p )
		p = p->pNext;
	if( p )
		return p->GetCaller();
	else
		return NULL;
}

SbxArray* SbiInstance::GetLocals( SbMethod* pMeth )
{
	SbiRuntime* p = pRun;
	while( p && p->GetMethod() != pMeth )
		p = p->pNext;
	if( p )
		return p->GetLocals();
	else
		return NULL;
}

//////////////////////////////////////////////////////////////////////////
//								SbiInstance								//
//////////////////////////////////////////////////////////////////////////

// Achtung: pMeth kann auch NULL sein (beim Aufruf des Init-Codes)

SbiRuntime::SbiRuntime( SbModule* pm, SbMethod* pe, sal_uInt32 nStart )
		 : rBasic( *(StarBASIC*)pm->pParent ), pInst( pINST ), 
		   pMod( pm ), pMeth( pe ), pImg( pMod->pImage ), m_nLastTime(0)
{
	nFlags	  = pe ? pe->GetDebugFlags() : 0;
	pIosys	  = pInst->pIosys;
	pArgvStk  = NULL;
	pGosubStk = NULL;
	pForStk   = NULL;
	pError    = NULL;
	pErrCode  =
	pErrStmnt =
	pRestart  = NULL;
	pNext     = NULL;
	pCode     =
	pStmnt    = (const sal_uInt8* ) pImg->GetCode() + nStart;
	bRun      =
	bError    = sal_True;
	bInError  = sal_False;
	bBlocked  = sal_False;
	nLine	  = 0;
	nCol1	  = 0;
	nCol2	  = 0;
	nExprLvl  = 0;
	nArgc     = 0;
	nError	  = 0;
	nGosubLvl = 0;
	nForLvl   = 0;
	nOps	  = 0;
	refExprStk = new SbxArray;
	SetVBAEnabled( pMod->IsVBACompat() );
#if defined GCC
	SetParameters( pe ? pe->GetParameters() : (class SbxArray *)NULL );
#else
	SetParameters( pe ? pe->GetParameters() : NULL );
#endif
	pRefSaveList = NULL;
	pItemStoreList = NULL;
}

SbiRuntime::~SbiRuntime()
{
	ClearGosubStack();
	ClearArgvStack();
	ClearForStack();

	// #74254 Items zum Sichern temporaere Referenzen freigeben
	ClearRefs();
	while( pItemStoreList )
	{
		RefSaveItem* pToDeleteItem = pItemStoreList;
		pItemStoreList = pToDeleteItem->pNext;
		delete pToDeleteItem;
	}
}

void SbiRuntime::SetVBAEnabled(bool bEnabled )
{
	bVBAEnabled = bEnabled; 
}

// Aufbau der Parameterliste. Alle ByRef-Parameter werden direkt
// uebernommen; von ByVal-Parametern werden Kopien angelegt. Falls
// ein bestimmter Datentyp verlangt wird, wird konvertiert.

void SbiRuntime::SetParameters( SbxArray* pParams )
{
	refParams = new SbxArray;
	// fuer den Returnwert
	refParams->Put( pMeth, 0 );

	SbxInfo* pInfo = pMeth ? pMeth->GetInfo() : NULL;
	sal_uInt16 nParamCount = pParams ? pParams->Count() : 1;
	if( nParamCount > 1 )
	{
		for( sal_uInt16 i = 1 ; i < nParamCount ; i++ )
		{
			const SbxParamInfo* p = pInfo ? pInfo->GetParam( i ) : NULL;

			// #111897 ParamArray
			if( p && (p->nUserData & PARAM_INFO_PARAMARRAY) != 0 )
			{
				SbxDimArray* pArray = new SbxDimArray( SbxVARIANT );
				sal_uInt16 nParamArrayParamCount = nParamCount - i;
				pArray->unoAddDim( 0, nParamArrayParamCount - 1 );
				for( sal_uInt16 j = i ; j < nParamCount ; j++ )
				{
					SbxVariable* v = pParams->Get( j );
					short nDimIndex = j - i;
					pArray->Put( v, &nDimIndex );
				}
				SbxVariable* pArrayVar = new SbxVariable( SbxVARIANT );
				pArrayVar->SetFlag( SBX_READWRITE );
				pArrayVar->PutObject( pArray );
				refParams->Put( pArrayVar, i );

				// Block ParamArray for missing parameter
				pInfo = NULL;
				break;
			}

			SbxVariable* v = pParams->Get( i );
			// Methoden sind immer byval!
			sal_Bool bByVal = v->IsA( TYPE(SbxMethod) );
			SbxDataType t = v->GetType();
			bool bTargetTypeIsArray = false;
			if( p )
			{
				bByVal |= sal_Bool( ( p->eType & SbxBYREF ) == 0 );
				t = (SbxDataType) ( p->eType & 0x0FFF );

				if( !bByVal && t != SbxVARIANT && 
					(!v->IsFixed() || (SbxDataType)(v->GetType() & 0x0FFF ) != t) )
						bByVal = sal_True;

				bTargetTypeIsArray = (p->nUserData & PARAM_INFO_WITHBRACKETS) != 0;
			}
			if( bByVal )
			{
				if( bTargetTypeIsArray )
					t = SbxOBJECT;
				SbxVariable* v2 = new SbxVariable( t );
				v2->SetFlag( SBX_READWRITE );
				*v2 = *v;
				refParams->Put( v2, i );
			}
			else
			{
				if( t != SbxVARIANT && t != ( v->GetType() & 0x0FFF ) )
				{
					// Array konvertieren??
					if( p && (p->eType & SbxARRAY) )
						Error( SbERR_CONVERSION );
					else
						v->Convert( t );
				}
				refParams->Put( v, i );
			}
			if( p )
				refParams->PutAlias( p->aName, i );
		}
	}

	// ParamArray for missing parameter
	if( pInfo )
	{
		// #111897 Check first missing parameter for ParamArray
		const SbxParamInfo* p = pInfo->GetParam( 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( SBX_READWRITE );
			pArrayVar->PutObject( pArray );
			refParams->Put( pArrayVar, nParamCount );
		}
	}
}


// Einen P-Code ausfuehren

sal_Bool SbiRuntime::Step()
{
	if( bRun )
	{
		// Unbedingt gelegentlich die Kontrolle abgeben!
		if( !( ++nOps & 0xF ) && pInst->IsReschedule() && bStaticGlobalEnableReschedule )
		{
			sal_uInt32 nTime = osl_getGlobalTimer();
			if (nTime - m_nLastTime > 5 ) // 20 ms
			{
				Application::Reschedule();
				m_nLastTime = nTime;
			}
		}

		// #i48868 blocked by next call level?
		while( bBlocked )
		{
			if( pInst->IsReschedule() && bStaticGlobalEnableReschedule )
				Application::Reschedule();
		}

#ifdef DBG_TRACE_BASIC
		sal_uInt32 nPC = ( pCode - (const sal_uInt8* )pImg->GetCode() );
		dbg_traceStep( pMod, nPC, pINST->nCallLvl );
#endif

		SbiOpcode eOp = (SbiOpcode ) ( *pCode++ );
		sal_uInt32 nOp1, nOp2;
        if (eOp < SbOP0_END)
		{
			(this->*( aStep0[ eOp ] ) )();
		}
        else if (eOp >= SbOP1_START && eOp < SbOP1_END)
		{
			nOp1 = *pCode++; nOp1 |= *pCode++ << 8; nOp1 |= *pCode++ << 16; nOp1 |= *pCode++ << 24;
			
			(this->*( aStep1[ eOp - SbOP1_START ] ) )( nOp1 );
		}
        else if (eOp >= SbOP2_START && eOp < SbOP2_END)
		{
			nOp1 = *pCode++; nOp1 |= *pCode++ << 8; nOp1 |= *pCode++ << 16; nOp1 |= *pCode++ << 24;
			nOp2 = *pCode++; nOp2 |= *pCode++ << 8; nOp2 |= *pCode++ << 16; nOp2 |= *pCode++ << 24;
			(this->*( aStep2[ eOp - SbOP2_START ] ) )( nOp1, nOp2 );
		}
		else
			StarBASIC::FatalError( SbERR_INTERNAL_ERROR );

		// SBX-Fehler aufgetreten?
		SbError nSbError = SbxBase::GetError();
		Error( ERRCODE_TOERROR(nSbError) );			// Warnings rausfiltern

		// AB 13.2.1997, neues Error-Handling:
		// ACHTUNG: Hier kann nError auch dann gesetzt sein, wenn !nSbError,
		// da nError jetzt auch von anderen RT-Instanzen gesetzt werden kann

		if( nError )
			SbxBase::ResetError();

		// AB,15.3.96: Fehler nur anzeigen, wenn BASIC noch aktiv
		// (insbesondere nicht nach Compiler-Fehlern zur Laufzeit)
		if( nError && bRun )
		{
#ifdef DBG_TRACE_BASIC
			SbError nTraceErr = nError;
			String aTraceErrMsg = GetSbData()->aErrMsg;
			bool bTraceErrHandled = true;
#endif
			SbError err = nError;
			ClearExprStack();
			nError = 0;
			pInst->nErr = err;
			pInst->nErl = nLine;
			pErrCode    = pCode;
			pErrStmnt   = pStmnt;
			// An error occured in an error handler 
			// force parent handler ( if there is one )
			// to handle the error
			bool bLetParentHandleThis = false;

			// Im Error Handler? Dann Std-Error
			if ( !bInError )
			{
				bInError = sal_True;

				if( !bError )			// On Error Resume Next
					StepRESUME( 1 );
				else if( pError )		// On Error Goto ...
					pCode = pError;
				else 
					bLetParentHandleThis = true;
            }
			else
			{
				bLetParentHandleThis = true;
				pError = NULL; //terminate the handler
			}		
			if ( bLetParentHandleThis )
			{
				// AB 13.2.1997, neues Error-Handling:
				// Uebergeordnete Error-Handler beruecksichtigen

				// Wir haben keinen Error-Handler -> weiter oben suchen
				SbiRuntime* pRtErrHdl = NULL;
				SbiRuntime* pRt = this;
				while( NULL != (pRt = pRt->pNext) )
				{
					// Gibt es einen Error-Handler?
					if( pRt->bError == sal_False || pRt->pError != NULL )
					{
						pRtErrHdl = pRt;
						break;
					}
				}

				// Error-Hdl gefunden?
				if( pRtErrHdl )
				{
					// (Neuen) Error-Stack anlegen
					SbErrorStack*& rErrStack = GetSbData()->pErrStack;
					if( rErrStack )
						delete rErrStack;
					rErrStack = new SbErrorStack();

					// Alle im Call-Stack darunter stehenden RTs manipulieren
					pRt = this;
					do
					{
						// Fehler setzen
						pRt->nError = err;
						if( pRt != pRtErrHdl )
							pRt->bRun = sal_False;

						// In Error-Stack eintragen
						SbErrorStackEntry *pEntry = new SbErrorStackEntry
							( pRt->pMeth, pRt->nLine, pRt->nCol1, pRt->nCol2 );
						rErrStack->C40_INSERT(SbErrorStackEntry, pEntry, rErrStack->Count() );

						// Nach RT mit Error-Handler aufhoeren
						if( pRt == pRtErrHdl )
							break;
                           pRt = pRt->pNext;
					}
					while( pRt );
				}
				// Kein Error-Hdl gefunden -> altes Vorgehen
				else
				{
#ifdef DBG_TRACE_BASIC
					bTraceErrHandled = false;
#endif
					pInst->Abort();
				}

				// ALT: Nur
				// pInst->Abort();
			}

#ifdef DBG_TRACE_BASIC
			dbg_traceNotifyError( nTraceErr, aTraceErrMsg, bTraceErrHandled, pINST->nCallLvl );
#endif
		}
	}
	return bRun;
}

void SbiRuntime::Error( SbError n, bool bVBATranslationAlreadyDone )
{
	if( n )
	{
		nError = n;
		if( isVBAEnabled() && !bVBATranslationAlreadyDone )
		{
			String aMsg = pInst->GetErrorMsg();
			sal_Int32 nVBAErrorNumber = translateErrorToVba( nError, aMsg );
			SbxVariable* pSbxErrObjVar = SbxErrObject::getErrObject();
			SbxErrObject* pGlobErr = static_cast< SbxErrObject* >( pSbxErrObjVar );
			if( pGlobErr != NULL )
				pGlobErr->setNumberAndDescription( nVBAErrorNumber, aMsg );

			pInst->aErrorMsg = aMsg;
			nError = SbERR_BASIC_COMPAT;
		}
	}
}

void SbiRuntime::Error( SbError _errCode, const String& _details )
{
    if ( _errCode )
    {
		// Not correct for class module usage, remove for now
        //OSL_ENSURE( pInst->pRun == this, "SbiRuntime::Error: can't propagate the error message details!" );
        if ( pInst->pRun == this )
        {
            pInst->Error( _errCode, _details );
            //OSL_POSTCOND( nError == _errCode, "SbiRuntime::Error: the instance is expecte to propagate the error code back to me!" );
        }
        else
        {
            nError = _errCode;
        }
    }
}

void SbiRuntime::FatalError( SbError n )
{
	StepSTDERROR();
	Error( n );
}

void SbiRuntime::FatalError( SbError _errCode, const String& _details )
{
	StepSTDERROR();
	Error( _errCode, _details );
}

sal_Int32 SbiRuntime::translateErrorToVba( SbError nError, String& 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
	if ( !rMsg.Len() )
	{
		// TEST, has to be vb here always
#ifdef DBG_UTIL
		SbError nTmp = StarBASIC::GetSfxFromVBError( (sal_uInt16)nError );
		DBG_ASSERT( nTmp, "No VB error!" );
#endif

		StarBASIC::MakeErrorText( nError, rMsg );	
		rMsg = StarBASIC::GetErrorText();
		if ( !rMsg.Len() ) // no message for err no, need localized resource here
			rMsg = String( RTL_CONSTASCII_USTRINGPARAM("Internal Object Error:") );
	}
	// no num? most likely then it *is* really a vba err
	sal_uInt16 nVBErrorCode = StarBASIC::GetVBErrorCode( nError );
	sal_Int32 nVBAErrorNumber = ( nVBErrorCode == 0 ) ? nError : nVBErrorCode;
	return nVBAErrorNumber;
}

//////////////////////////////////////////////////////////////////////////
//
//	Parameter, Locals, Caller
//
//////////////////////////////////////////////////////////////////////////

SbMethod* SbiRuntime::GetCaller()
{
	return pMeth;
}

SbxArray* SbiRuntime::GetLocals()
{
	return refLocals;
}

SbxArray* SbiRuntime::GetParams()
{
	return refParams;
}

//////////////////////////////////////////////////////////////////////////
//
//	Stacks
//
//////////////////////////////////////////////////////////////////////////

// Der Expression-Stack steht fuer die laufende Auswertung von Expressions
// zur Verfuegung.

void SbiRuntime::PushVar( SbxVariable* pVar )
{
	if( pVar )
		refExprStk->Put( pVar, nExprLvl++ );
}

SbxVariableRef SbiRuntime::PopVar()
{
#ifdef DBG_UTIL
	if( !nExprLvl )
	{
		StarBASIC::FatalError( SbERR_INTERNAL_ERROR );
		return new SbxVariable;
	}
#endif
	SbxVariableRef xVar = refExprStk->Get( --nExprLvl );
#ifdef DBG_UTIL
	if ( xVar->GetName().EqualsAscii( "Cells" ) )
		DBG_TRACE( "" );
#endif
	// Methods halten im 0.Parameter sich selbst, also weghauen
	if( xVar->IsA( TYPE(SbxMethod) ) )
		xVar->SetParameters(0);
	return xVar;
}

sal_Bool SbiRuntime::ClearExprStack()
{
	// Achtung: Clear() reicht nicht, da Methods geloescht werden muessen
	while ( nExprLvl )
	{
		PopVar();
	}
	refExprStk->Clear();
	return sal_False;
}

// Variable auf dem Expression-Stack holen, ohne sie zu entfernen
// n zaehlt ab 0.

SbxVariable* SbiRuntime::GetTOS( short n )
{
	n = nExprLvl - n - 1;
#ifdef DBG_UTIL
	if( n < 0 )
	{
		StarBASIC::FatalError( SbERR_INTERNAL_ERROR );
		return new SbxVariable;
	}
#endif
	return refExprStk->Get( (sal_uInt16) n );
}

// Sicherstellen, dass TOS eine temporaere Variable ist

void SbiRuntime::TOSMakeTemp()
{
	SbxVariable* p = refExprStk->Get( nExprLvl - 1 );
	if( p->GetRefCount() != 1 )
	{
		SbxVariable* pNew = new SbxVariable( *p );
		pNew->SetFlag( SBX_READWRITE );
		refExprStk->Put( pNew, nExprLvl - 1 );
	}
}

// Der GOSUB-Stack nimmt Returnadressen fuer GOSUBs auf

void SbiRuntime::PushGosub( const sal_uInt8* pc )
{
	if( ++nGosubLvl > MAXRECURSION )
		StarBASIC::FatalError( SbERR_STACK_OVERFLOW );
	SbiGosubStack* p = new SbiGosubStack;
	p->pCode  = pc;
	p->pNext  = pGosubStk;
	p->nStartForLvl = nForLvl;
	pGosubStk = p;
}

void SbiRuntime::PopGosub()
{
	if( !pGosubStk )
		Error( SbERR_NO_GOSUB );
	else
	{
		SbiGosubStack* p = pGosubStk;
		pCode = p->pCode;
		pGosubStk = p->pNext;
		delete p;
		nGosubLvl--;
	}
}

// Entleeren des GOSUB-Stacks

void SbiRuntime::ClearGosubStack()
{
	SbiGosubStack* p;
	while(( p = pGosubStk ) != NULL )
		pGosubStk = p->pNext, delete p;
	nGosubLvl = 0;
}

// Der Argv-Stack nimmt aktuelle Argument-Vektoren auf

void SbiRuntime::PushArgv()
{
	SbiArgvStack* p = new SbiArgvStack;
	p->refArgv = refArgv;
	p->nArgc = nArgc;
	nArgc = 1;
	refArgv.Clear();
	p->pNext = pArgvStk;
	pArgvStk = p;
}

void SbiRuntime::PopArgv()
{
	if( pArgvStk )
	{
		SbiArgvStack* p = pArgvStk;
		pArgvStk = p->pNext;
		refArgv = p->refArgv;
		nArgc = p->nArgc;
		delete p;
	}
}

// Entleeren des Argv-Stacks

void SbiRuntime::ClearArgvStack()
{
	while( pArgvStk )
		PopArgv();
}

// Push des For-Stacks. Der Stack hat Inkrement, Ende, Beginn und Variable.
// Nach Aufbau des Stack-Elements ist der Stack leer.

void SbiRuntime::PushFor()
{
	SbiForStack* p = new SbiForStack;
	p->eForType = FOR_TO;
	p->pNext = pForStk;
	pForStk = p;
	// Der Stack ist wie folgt aufgebaut:
	p->refInc = PopVar();
	p->refEnd = PopVar();
	SbxVariableRef xBgn = PopVar();
	p->refVar = PopVar();
	*(p->refVar) = *xBgn;
	nForLvl++;
}

void SbiRuntime::PushForEach()
{
	SbiForStack* p = new SbiForStack;
	p->pNext = pForStk;
	pForStk = p;

	SbxVariableRef xObjVar = PopVar();
	SbxBase* pObj = xObjVar.Is() ? xObjVar->GetObject() : NULL;
	if( pObj == NULL )
	{
		Error( SbERR_NO_OBJECT );
		return;
	}

	bool bError_ = false;
	BasicCollection* pCollection;
	SbxDimArray* pArray;
	SbUnoObject* pUnoObj;
	if( (pArray = PTR_CAST(SbxDimArray,pObj)) != NULL )
	{
		p->eForType = FOR_EACH_ARRAY;
		p->refEnd = (SbxVariable*)pArray;

		short nDims = pArray->GetDims();
		p->pArrayLowerBounds = new sal_Int32[nDims];
		p->pArrayUpperBounds = new sal_Int32[nDims];
		p->pArrayCurIndices  = new sal_Int32[nDims];
		sal_Int32 lBound, uBound;
		for( short i = 0 ; i < nDims ; i++ )
		{
			pArray->GetDim32( i+1, lBound, uBound );
			p->pArrayCurIndices[i] = p->pArrayLowerBounds[i] = lBound;
			p->pArrayUpperBounds[i] = uBound;
		}
	}
	else if( (pCollection = PTR_CAST(BasicCollection,pObj)) != NULL )
	{
		p->eForType = FOR_EACH_COLLECTION;
		p->refEnd = pCollection;
		p->nCurCollectionIndex = 0;
	}
	else if( (pUnoObj = PTR_CAST(SbUnoObject,pObj)) != NULL )
	{
		// XEnumerationAccess?
		Any aAny = pUnoObj->getUnoAny();
		Reference< XEnumerationAccess > xEnumerationAccess;
		if( (aAny >>= xEnumerationAccess) )
		{
			p->xEnumeration = xEnumerationAccess->createEnumeration();
			p->eForType = FOR_EACH_XENUMERATION;
		}
		else if ( isVBAEnabled() && pUnoObj->isNativeCOMObject() )
        {
            uno::Reference< script::XInvocation > xInvocation;
            if ( ( aAny >>= xInvocation ) && xInvocation.is() )
            {
                try
                {
                    p->xEnumeration = new ComEnumerationWrapper( xInvocation );
                    p->eForType = FOR_EACH_XENUMERATION;
                }
                catch( uno::Exception& )
                {}
            }

            if ( !p->xEnumeration.is() )
                bError_ = true;
        }
        else
		{
			bError_ = true;
		}
	}
	else
	{
		bError_ = true;
	}

	if( bError_ )
	{
		Error( SbERR_CONVERSION );
		return;
	}

	// Container variable
	p->refVar = PopVar();
	nForLvl++;
}

// Poppen des FOR-Stacks

void SbiRuntime::PopFor()
{
	if( pForStk )
	{
		SbiForStack* p = pForStk;
		pForStk = p->pNext;
		delete p;
		nForLvl--;
	}
}

// Entleeren des FOR-Stacks

void SbiRuntime::ClearForStack()
{
	while( pForStk )
		PopFor();
}

SbiForStack* SbiRuntime::FindForStackItemForCollection( class BasicCollection* pCollection )
{
	SbiForStack* pRet = NULL;

	SbiForStack* p = pForStk;
	while( p )
	{
		SbxVariable* pVar = p->refEnd.Is() ? (SbxVariable*)p->refEnd : NULL;
		if( p->eForType == FOR_EACH_COLLECTION && pVar != NULL &&
			(pCollection = PTR_CAST(BasicCollection,pVar)) == pCollection )
		{
			pRet = p;
			break;
		}
	}

	return pRet;
}


//////////////////////////////////////////////////////////////////////////
//
//	DLL-Aufrufe
//
//////////////////////////////////////////////////////////////////////////

void SbiRuntime::DllCall
	( const String& aFuncName,	// Funktionsname
	  const String& aDLLName,	// Name der DLL
	  SbxArray* pArgs,			// Parameter (ab Index 1, kann NULL sein)
	  SbxDataType eResType,		// Returnwert
	  sal_Bool bCDecl )				// sal_True: nach C-Konventionen
{
	// No DllCall for "virtual" portal users
	if( needSecurityRestrictions() )
	{
		StarBASIC::Error(SbERR_NOT_IMPLEMENTED);
		return;
	}

	// MUSS NOCH IMPLEMENTIERT WERDEN
	/*
	String aMsg;
	aMsg = "FUNC=";
	aMsg += pFunc;
	aMsg += " DLL=";
	aMsg += pDLL;
	MessBox( NULL, WB_OK, String( "DLL-CALL" ), aMsg ).Execute();
	Error( SbERR_NOT_IMPLEMENTED );
	*/

	SbxVariable* pRes = new SbxVariable( eResType );
	SbiDllMgr* pDllMgr = pInst->GetDllMgr();
	SbError nErr = pDllMgr->Call( aFuncName, aDLLName, pArgs, *pRes, bCDecl );
	if( nErr )
		Error( nErr );
	PushVar( pRes );
}

sal_uInt16 SbiRuntime::GetImageFlag( sal_uInt16 n ) const
{ 
	return pImg->GetFlag( n ); 
} 

sal_uInt16 SbiRuntime::GetBase()
{ 
	return pImg->GetBase();
}
