/* -*- 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 <basic/sbx.hxx>
#include <sbunoobj.hxx>
#include <parser.hxx>
#include <svtools/miscopt.hxx>
#include <osl/diagnose.h>
#include <com/sun/star/reflection/theCoreReflection.hpp>
#include <comphelper/processfactory.hxx>
#include <com/sun/star/reflection/XInterfaceMemberTypeDescription.hpp>
#include <com/sun/star/reflection/XIdlMethod.hpp>
#include <com/sun/star/uno/Exception.hpp>
#include <basic/codecompletecache.hxx>
#include <memory>
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
// Declaration of a variable
// If there are errors it will be parsed up to the comma or the newline.
// Return-value: a new instance, which were inserted and then deleted.
// Array-Index were returned as SbiExprList
SbiSymDef* SbiParser::VarDecl( SbiExprListPtr* ppDim, bool bStatic, bool bConst )
{
bool bWithEvents = false;
if( Peek() == WITHEVENTS )
{
Next();
bWithEvents = true;
}
if( !TestSymbol() ) return nullptr;
SbxDataType t = eScanType;
SbiSymDef* pDef = bConst ? new SbiConstDef( aSym ) : new SbiSymDef( aSym );
SbiExprListPtr pDim;
// Brackets?
if( Peek() == LPAREN )
{
pDim = SbiExprList::ParseDimList( this );
if( !pDim->GetDims() )
pDef->SetWithBrackets();
}
pDef->SetType( t );
if( bStatic )
pDef->SetStatic();
if( bWithEvents )
pDef->SetWithEvents();
TypeDecl( *pDef );
if( !ppDim && pDim )
{
if(pDim->GetDims() )
Error( ERRCODE_BASIC_EXPECTED, "()" );
}
else if( ppDim )
*ppDim = std::move(pDim);
return pDef;
}
// Resolving of a AS-Type-Declaration
// The data type were inserted into the handed over variable
void SbiParser::TypeDecl( SbiSymDef& rDef, bool bAsNewAlreadyParsed )
{
SbxDataType eType = rDef.GetType();
if( bAsNewAlreadyParsed || Peek() == AS )
{
short nSize = 0;
if( !bAsNewAlreadyParsed )
Next();
rDef.SetDefinedAs();
SbiToken eTok = Next();
if( !bAsNewAlreadyParsed && eTok == NEW )
{
rDef.SetNew();
eTok = Next();
}
switch( eTok )
{
case ANY:
if( rDef.IsNew() )
Error( ERRCODE_BASIC_SYNTAX );
eType = SbxVARIANT; break;
case TINTEGER:
case TLONG:
case TSINGLE:
case TDOUBLE:
case TCURRENCY:
case TDATE:
case TSTRING:
case TOBJECT:
case ERROR_:
case TBOOLEAN:
case TVARIANT:
case TBYTE:
if( rDef.IsNew() )
Error( ERRCODE_BASIC_SYNTAX );
eType = (eTok==TBYTE) ? SbxBYTE : SbxDataType( eTok - TINTEGER + SbxINTEGER );
if( eType == SbxSTRING )
{
// STRING*n ?
if( Peek() == MUL )
{ // fixed size!
Next();
SbiConstExpression aSize( this );
nSize = aSize.GetShortValue();
if( nSize < 0 || (bVBASupportOn && nSize <= 0) )
Error( ERRCODE_BASIC_OUT_OF_RANGE );
else
rDef.SetFixedStringLength( nSize );
}
}
break;
case SYMBOL: // can only be a TYPE or a object class!
if( eScanType != SbxVARIANT )
Error( ERRCODE_BASIC_SYNTAX );
else
{
OUString aCompleteName = aSym;
// #52709 DIM AS NEW for Uno with full-qualified name
if( Peek() == DOT )
{
OUString aDotStr( '.' );
while( Peek() == DOT )
{
aCompleteName += aDotStr;
Next();
SbiToken ePeekTok = Peek();
if( ePeekTok == SYMBOL || IsKwd( ePeekTok ) )
{
Next();
aCompleteName += aSym;
}
else
{
Next();
Error( ERRCODE_BASIC_UNEXPECTED, SYMBOL );
break;
}
}
}
else if( rEnumArray->Find( aCompleteName, SbxClassType::Object ) || ( IsVBASupportOn() && VBAConstantHelper::instance().isVBAConstantType( aCompleteName ) ) )
{
eType = SbxLONG;
break;
}
// Take over in the string pool
rDef.SetTypeId( aGblStrings.Add( aCompleteName ) );
if( rDef.IsNew() && pProc == nullptr )
aRequiredTypes.push_back( aCompleteName );
}
eType = SbxOBJECT;
break;
case FIXSTRING: // new syntax for complex UNO types
rDef.SetTypeId( aGblStrings.Add( aSym ) );
eType = SbxOBJECT;
break;
default:
Error( ERRCODE_BASIC_UNEXPECTED, eTok );
Next();
}
// The variable could have been declared with a suffix
if( rDef.GetType() != SbxVARIANT )
{
if( rDef.GetType() != eType )
Error( ERRCODE_BASIC_VAR_DEFINED, rDef.GetName() );
else if( eType == SbxSTRING && rDef.GetLen() != nSize )
Error( ERRCODE_BASIC_VAR_DEFINED, rDef.GetName() );
}
rDef.SetType( eType );
rDef.SetLen( nSize );
}
}
// Here variables, arrays and structures were defined.
// DIM/PRIVATE/PUBLIC/GLOBAL
void SbiParser::Dim()
{
DefVar( SbiOpcode::DIM_, pProc && bVBASupportOn && pProc->IsStatic() );
}
void SbiParser::DefVar( SbiOpcode eOp, bool bStatic )
{
SbiSymPool* pOldPool = pPool;
bool bSwitchPool = false;
bool bPersistentGlobal = false;
SbiToken eFirstTok = eCurTok;
if( pProc && ( eCurTok == GLOBAL || eCurTok == PUBLIC || eCurTok == PRIVATE ) )
Error( ERRCODE_BASIC_NOT_IN_SUBR, eCurTok );
if( eCurTok == PUBLIC || eCurTok == GLOBAL )
{
bSwitchPool = true; // at the right moment switch to the global pool
if( eCurTok == GLOBAL )
bPersistentGlobal = true;
}
// behavior in VBA is that a module scope variable's lifetime is
// tied to the document. e.g. a module scope variable is global
if( GetBasic()->IsDocBasic() && bVBASupportOn && !pProc )
bPersistentGlobal = true;
// PRIVATE is a synonymous for DIM
// _CONST_?
bool bConst = false;
if( eCurTok == CONST_ )
bConst = true;
else if( Peek() == CONST_ )
{
Next();
bConst = true;
}
// #110004 It can also be a sub/function
if( !bConst && (eCurTok == SUB || eCurTok == FUNCTION || eCurTok == PROPERTY ||
eCurTok == STATIC || eCurTok == ENUM || eCurTok == DECLARE || eCurTok == TYPE) )
{
// Next token is read here, because !bConst
bool bPrivate = ( eFirstTok == PRIVATE );
if( eCurTok == STATIC )
{
Next();
DefStatic( bPrivate );
}
else if( eCurTok == SUB || eCurTok == FUNCTION || eCurTok == PROPERTY )
{
// End global chain if necessary (not done in
// SbiParser::Parse() under these conditions
if( bNewGblDefs && nGblChain == 0 )
{
nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
bNewGblDefs = false;
}
Next();
DefProc( false, bPrivate );
return;
}
else if( eCurTok == ENUM )
{
Next();
DefEnum( bPrivate );
return;
}
else if( eCurTok == DECLARE )
{
Next();
DefDeclare( bPrivate );
return;
}
// #i109049
else if( eCurTok == TYPE )
{
Next();
DefType(); // TODO: Use bPrivate in DefType()
return;
}
}
// SHARED were ignored
if( Peek() == SHARED ) Next();
// PRESERVE only at REDIM
if( Peek() == PRESERVE )
{
Next();
if( eOp == SbiOpcode::REDIM_ )
eOp = SbiOpcode::REDIMP_;
else
Error( ERRCODE_BASIC_UNEXPECTED, eCurTok );
}
SbiSymDef* pDef;
SbiExprListPtr pDim;
// #40689, Statics -> Module-Initialising, skip in Sub
sal_uInt32 nEndOfStaticLbl = 0;
if( !bVBASupportOn && bStatic )
{
nEndOfStaticLbl = aGen.Gen( SbiOpcode::JUMP_, 0 );
aGen.Statement(); // catch up on static here
}
bool bDefined = false;
while( ( pDef = VarDecl( &pDim, bStatic, bConst ) ) != nullptr )
{
/*fprintf(stderr, "Actual sub: \n");
fprintf(stderr, "Symbol name: %s\n",OUStringToOString(pDef->GetName(),RTL_TEXTENCODING_UTF8).getStr());*/
EnableErrors();
// search variable:
if( bSwitchPool )
pPool = &aGlobals;
SbiSymDef* pOld = pPool->Find( pDef->GetName() );
// search also in the Runtime-Library
bool bRtlSym = false;
if( !pOld )
{
pOld = CheckRTLForSym( pDef->GetName(), SbxVARIANT );
if( pOld )
bRtlSym = true;
}
if( pOld && !(eOp == SbiOpcode::REDIM_ || eOp == SbiOpcode::REDIMP_) )
{
if( pDef->GetScope() == SbLOCAL && pOld->GetScope() != SbLOCAL )
pOld = nullptr;
}
if( pOld )
{
bDefined = true;
// always an error at a RTL-S
if( !bRtlSym && (eOp == SbiOpcode::REDIM_ || eOp == SbiOpcode::REDIMP_) )
{
// compare the attributes at a REDIM
SbxDataType eDefType;
bool bError_ = false;
if( pOld->IsStatic() )
{
bError_ = true;
}
else if( pOld->GetType() != ( eDefType = pDef->GetType() ) )
{
if( !( eDefType == SbxVARIANT && !pDef->IsDefinedAs() ) )
bError_ = true;
}
if( bError_ )
Error( ERRCODE_BASIC_VAR_DEFINED, pDef->GetName() );
}
else
Error( ERRCODE_BASIC_VAR_DEFINED, pDef->GetName() );
delete pDef; pDef = pOld;
}
else
pPool->Add( pDef );
// #36374: Create the variable in front of the distinction IsNew()
// Otherwise error at Dim Identifier As New Type and option explicit
if( !bDefined && !(eOp == SbiOpcode::REDIM_ || eOp == SbiOpcode::REDIMP_)
&& ( !bConst || pDef->GetScope() == SbGLOBAL ) )
{
// Declare variable or global constant
SbiOpcode eOp2;
switch ( pDef->GetScope() )
{
case SbGLOBAL: eOp2 = bPersistentGlobal ? SbiOpcode::GLOBAL_P_ : SbiOpcode::GLOBAL_;
goto global;
case SbPUBLIC: eOp2 = bPersistentGlobal ? SbiOpcode::PUBLIC_P_ : SbiOpcode::PUBLIC_;
// #40689, no own Opcode anymore
if( bVBASupportOn && bStatic )
{
eOp2 = SbiOpcode::STATIC_;
break;
}
global: aGen.BackChain( nGblChain );
nGblChain = 0;
bGblDefs = bNewGblDefs = true;
break;
default: eOp2 = SbiOpcode::LOCAL_;
}
sal_uInt32 nOpnd2 = sal::static_int_cast< sal_uInt16 >( pDef->GetType() );
if( pDef->IsWithEvents() )
nOpnd2 |= SBX_TYPE_WITH_EVENTS_FLAG;
if( bCompatible && pDef->IsNew() )
nOpnd2 |= SBX_TYPE_DIM_AS_NEW_FLAG;
short nFixedStringLength = pDef->GetFixedStringLength();
if( nFixedStringLength >= 0 )
nOpnd2 |= (SBX_FIXED_LEN_STRING_FLAG + (sal_uInt32(nFixedStringLength) << 17)); // len = all bits above 0x10000
if( pDim != nullptr && pDim->GetDims() > 0 )
nOpnd2 |= SBX_TYPE_VAR_TO_DIM_FLAG;
aGen.Gen( eOp2, pDef->GetId(), nOpnd2 );
}
// Initialising for self-defined data types
// and per NEW created variable
if( pDef->GetType() == SbxOBJECT
&& pDef->GetTypeId() )
{
if( !bCompatible && !pDef->IsNew() )
{
OUString aTypeName( aGblStrings.Find( pDef->GetTypeId() ) );
if( rTypeArray->Find( aTypeName, SbxClassType::Object ) == nullptr )
{
if( CodeCompleteOptions::IsExtendedTypeDeclaration() )
{
if(!IsUnoInterface(aTypeName))
Error( ERRCODE_BASIC_UNDEF_TYPE, aTypeName );
}
else
Error( ERRCODE_BASIC_UNDEF_TYPE, aTypeName );
}
}
if( bConst )
{
Error( ERRCODE_BASIC_SYNTAX );
}
if( pDim )
{
if( eOp == SbiOpcode::REDIMP_ )
{
SbiExpression aExpr( this, *pDef, nullptr );
aExpr.Gen();
aGen.Gen( SbiOpcode::REDIMP_ERASE_ );
pDef->SetDims( pDim->GetDims() );
SbiExpression aExpr2( this, *pDef, std::move(pDim) );
aExpr2.Gen();
aGen.Gen( SbiOpcode::DCREATE_REDIMP_, pDef->GetId(), pDef->GetTypeId() );
}
else
{
pDef->SetDims( pDim->GetDims() );
SbiExpression aExpr( this, *pDef, std::move(pDim) );
aExpr.Gen();
aGen.Gen( SbiOpcode::DCREATE_, pDef->GetId(), pDef->GetTypeId() );
}
}
else
{
SbiExpression aExpr( this, *pDef );
aExpr.Gen();
SbiOpcode eOp_ = pDef->IsNew() ? SbiOpcode::CREATE_ : SbiOpcode::TCREATE_;
aGen.Gen( eOp_, pDef->GetId(), pDef->GetTypeId() );
if ( bVBASupportOn )
aGen.Gen( SbiOpcode::VBASET_ );
else
aGen.Gen( SbiOpcode::SET_ );
}
}
else
{
if( bConst )
{
// Definition of the constants
if( pDim )
{
Error( ERRCODE_BASIC_SYNTAX );
}
SbiExpression aVar( this, *pDef );
if( !TestToken( EQ ) )
goto MyBreak; // (see below)
SbiConstExpression aExpr( this );
if( !bDefined && aExpr.IsValid() )
{
if( pDef->GetScope() == SbGLOBAL )
{
// Create code only for the global constant!
aVar.Gen();
aExpr.Gen();
aGen.Gen( SbiOpcode::PUTC_ );
}
SbiConstDef* pConst = pDef->GetConstDef();
if( aExpr.GetType() == SbxSTRING )
pConst->Set( aExpr.GetString() );
else
pConst->Set( aExpr.GetValue(), aExpr.GetType() );
}
}
else if( pDim )
{
// Dimension the variable
// Delete the var at REDIM beforehand
if( eOp == SbiOpcode::REDIM_ )
{
SbiExpression aExpr( this, *pDef, nullptr );
aExpr.Gen();
if ( bVBASupportOn )
// delete the array but
// clear the variable ( this
// allows the processing of
// the param to happen as normal without errors ( ordinary ERASE just clears the array )
aGen.Gen( SbiOpcode::ERASE_CLEAR_ );
else
aGen.Gen( SbiOpcode::ERASE_ );
}
else if( eOp == SbiOpcode::REDIMP_ )
{
SbiExpression aExpr( this, *pDef, nullptr );
aExpr.Gen();
aGen.Gen( SbiOpcode::REDIMP_ERASE_ );
}
pDef->SetDims( pDim->GetDims() );
if( bPersistentGlobal )
pDef->SetGlobal( true );
SbiExpression aExpr( this, *pDef, std::move(pDim) );
aExpr.Gen();
pDef->SetGlobal( false );
aGen.Gen( (eOp == SbiOpcode::STATIC_) ? SbiOpcode::DIM_ : eOp );
}
}
if( !TestComma() )
goto MyBreak;
// Implementation of bSwitchPool (see above): pPool must not be set to &aGlobals
// at the VarDecl-Call.
// Apart from that the behavior should be absolutely identical,
// i.e., pPool had to be reset always at the end of the loop.
// also at a break
pPool = pOldPool;
continue; // Skip MyBreak
MyBreak:
pPool = pOldPool;
break;
}
// #40689, finalize the jump over statics declarations
if( !bVBASupportOn && bStatic )
{
// maintain the global chain
nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
bGblDefs = bNewGblDefs = true;
// Register for Sub a jump to the end of statics
aGen.BackChain( nEndOfStaticLbl );
}
}
// Here were Arrays redimensioned.
void SbiParser::ReDim()
{
DefVar( SbiOpcode::REDIM_, pProc && bVBASupportOn && pProc->IsStatic() );
}
// ERASE array, ...
void SbiParser::Erase()
{
while( !bAbort )
{
SbiExpression aExpr( this, SbLVALUE );
aExpr.Gen();
aGen.Gen( SbiOpcode::ERASE_ );
if( !TestComma() ) break;
}
}
// Declaration of a data type
void SbiParser::Type()
{
DefType();
}
void SbiParser::DefType()
{
// Read the new Token lesen. It had to be a symbol
if (!TestSymbol())
return;
if (rTypeArray->Find(aSym,SbxClassType::Object))
{
Error( ERRCODE_BASIC_VAR_DEFINED, aSym );
return;
}
SbxObject *pType = new SbxObject(aSym);
bool bDone = false;
while( !bDone && !IsEof() )
{
std::unique_ptr<SbiSymDef> pElem;
SbiExprListPtr pDim;
switch( Peek() )
{
case ENDTYPE :
bDone = true;
Next();
break;
case EOLN :
case REM :
Next();
break;
default:
pElem.reset(VarDecl(&pDim, false, false));
if( !pElem )
bDone = true; // Error occurred
}
if( pElem )
{
SbxArray *pTypeMembers = pType->GetProperties();
OUString aElemName = pElem->GetName();
if( pTypeMembers->Find( aElemName, SbxClassType::DontCare) )
{
Error (ERRCODE_BASIC_VAR_DEFINED);
}
else
{
SbxDataType eElemType = pElem->GetType();
SbxProperty *pTypeElem = new SbxProperty( aElemName, eElemType );
if( pDim )
{
SbxDimArray* pArray = new SbxDimArray( pElem->GetType() );
if ( pDim->GetSize() )
{
// Dimension the target array
for ( short i=0; i<pDim->GetSize();++i )
{
sal_Int32 lb = nBase;
SbiExprNode* pNode = pDim->Get(i)->GetExprNode();
sal_Int32 ub = pNode->GetNumber();
if ( !pDim->Get( i )->IsBased() ) // each dim is low/up
{
if ( ++i >= pDim->GetSize() ) // trouble
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
pNode = pDim->Get(i)->GetExprNode();
lb = ub;
ub = pNode->GetNumber();
}
else if ( !bCompatible )
ub += nBase;
pArray->AddDim32( lb, ub );
}
pArray->setHasFixedSize( true );
}
else
pArray->unoAddDim( 0, -1 ); // variant array
SbxFlagBits nSavFlags = pTypeElem->GetFlags();
// need to reset the FIXED flag
// when calling PutObject ( because the type will not match Object )
pTypeElem->ResetFlag( SbxFlagBits::Fixed );
pTypeElem->PutObject( pArray );
pTypeElem->SetFlags( nSavFlags );
}
// Nested user type?
if( eElemType == SbxOBJECT )
{
sal_uInt16 nElemTypeId = pElem->GetTypeId();
if( nElemTypeId != 0 )
{
OUString aTypeName( aGblStrings.Find( nElemTypeId ) );
SbxObject* pTypeObj = static_cast< SbxObject* >( rTypeArray->Find( aTypeName, SbxClassType::Object ) );
if( pTypeObj != nullptr )
{
SbxObject* pCloneObj = cloneTypeObjectImpl( *pTypeObj );
pTypeElem->PutObject( pCloneObj );
}
}
}
pTypeMembers->Insert( pTypeElem, pTypeMembers->Count() );
}
}
}
pType->Remove( "Name", SbxClassType::DontCare );
pType->Remove( "Parent", SbxClassType::DontCare );
rTypeArray->Insert (pType,rTypeArray->Count());
}
// Declaration of Enum type
void SbiParser::Enum()
{
DefEnum( false );
}
void SbiParser::DefEnum( bool bPrivate )
{
// Read a the new Token. It had to be a symbol
if (!TestSymbol())
return;
OUString aEnumName = aSym;
if( rEnumArray->Find(aEnumName,SbxClassType::Object) )
{
Error( ERRCODE_BASIC_VAR_DEFINED, aSym );
return;
}
SbxObject *pEnum = new SbxObject( aEnumName );
if( bPrivate )
{
pEnum->SetFlag( SbxFlagBits::Private );
}
SbiSymDef* pElem;
bool bDone = false;
// Starting with -1 to make first default value 0 after ++
sal_Int32 nCurrentEnumValue = -1;
while( !bDone && !IsEof() )
{
switch( Peek() )
{
case ENDENUM :
pElem = nullptr;
bDone = true;
Next();
break;
case EOLN :
case REM :
pElem = nullptr;
Next();
break;
default:
{
SbiExprListPtr pDim;
pElem = VarDecl( &pDim, false, true );
if( !pElem )
{
bDone = true; // Error occurred
break;
}
else if( pDim )
{
Error( ERRCODE_BASIC_SYNTAX );
bDone = true; // Error occurred
break;
}
SbiExpression aVar( this, *pElem );
if( Peek() == EQ )
{
Next();
SbiConstExpression aExpr( this );
if( aExpr.IsValid() )
{
SbxVariableRef xConvertVar = new SbxVariable();
if( aExpr.GetType() == SbxSTRING )
xConvertVar->PutString( aExpr.GetString() );
else
xConvertVar->PutDouble( aExpr.GetValue() );
nCurrentEnumValue = xConvertVar->GetLong();
}
}
else
nCurrentEnumValue++;
SbiSymPool* pPoolToUse = bPrivate ? pPool : &aGlobals;
SbiSymDef* pOld = pPoolToUse->Find( pElem->GetName() );
if( pOld )
{
Error( ERRCODE_BASIC_VAR_DEFINED, pElem->GetName() );
bDone = true; // Error occurred
break;
}
pPool->Add( pElem );
if( !bPrivate )
{
aGen.BackChain( nGblChain );
nGblChain = 0;
bGblDefs = bNewGblDefs = true;
aGen.Gen(
SbiOpcode::GLOBAL_, pElem->GetId(),
sal::static_int_cast< sal_uInt16 >( pElem->GetType() ) );
aVar.Gen();
sal_uInt16 nStringId = aGen.GetParser()->aGblStrings.Add( nCurrentEnumValue, SbxLONG );
aGen.Gen( SbiOpcode::NUMBER_, nStringId );
aGen.Gen( SbiOpcode::PUTC_ );
}
SbiConstDef* pConst = pElem->GetConstDef();
pConst->Set( nCurrentEnumValue, SbxLONG );
}
}
if( pElem )
{
SbxArray *pEnumMembers = pEnum->GetProperties();
SbxProperty *pEnumElem = new SbxProperty( pElem->GetName(), SbxLONG );
pEnumElem->PutLong( nCurrentEnumValue );
pEnumElem->ResetFlag( SbxFlagBits::Write );
pEnumElem->SetFlag( SbxFlagBits::Const );
pEnumMembers->Insert( pEnumElem, pEnumMembers->Count() );
}
}
pEnum->Remove( "Name", SbxClassType::DontCare );
pEnum->Remove( "Parent", SbxClassType::DontCare );
rEnumArray->Insert( pEnum, rEnumArray->Count() );
}
// Procedure-Declaration
// the first Token is already read in (SUB/FUNCTION)
// xxx Name [LIB "name"[ALIAS "name"]][(Parameter)][AS TYPE]
SbiProcDef* SbiParser::ProcDecl( bool bDecl )
{
bool bFunc = ( eCurTok == FUNCTION );
bool bProp = ( eCurTok == GET || eCurTok == SET || eCurTok == LET );
if( !TestSymbol() ) return nullptr;
OUString aName( aSym );
SbxDataType eType = eScanType;
SbiProcDef* pDef = new SbiProcDef( this, aName, true );
pDef->SetType( eType );
if( Peek() == CDECL_ )
{
Next(); pDef->SetCdecl(true);
}
if( Peek() == LIB )
{
Next();
if( Next() == FIXSTRING )
{
pDef->GetLib() = aSym;
}
else
{
Error( ERRCODE_BASIC_SYNTAX );
}
}
if( Peek() == ALIAS )
{
Next();
if( Next() == FIXSTRING )
{
pDef->GetAlias() = aSym;
}
else
{
Error( ERRCODE_BASIC_SYNTAX );
}
}
if( !bDecl )
{
// CDECL, LIB and ALIAS are invalid
if( !pDef->GetLib().isEmpty() )
{
Error( ERRCODE_BASIC_UNEXPECTED, LIB );
}
if( !pDef->GetAlias().isEmpty() )
{
Error( ERRCODE_BASIC_UNEXPECTED, ALIAS );
}
if( pDef->IsCdecl() )
{
Error( ERRCODE_BASIC_UNEXPECTED, CDECL_ );
}
pDef->SetCdecl( false );
pDef->GetLib().clear();
pDef->GetAlias().clear();
}
else if( pDef->GetLib().isEmpty() )
{
// ALIAS and CDECL only together with LIB
if( !pDef->GetAlias().isEmpty() )
{
Error( ERRCODE_BASIC_UNEXPECTED, ALIAS );
}
if( pDef->IsCdecl() )
{
Error( ERRCODE_BASIC_UNEXPECTED, CDECL_ );
}
pDef->SetCdecl( false );
pDef->GetAlias().clear();
}
// Brackets?
if( Peek() == LPAREN )
{
Next();
if( Peek() == RPAREN )
{
Next();
}
else
{
for(;;)
{
bool bByVal = false;
bool bOptional = false;
bool bParamArray = false;
while( Peek() == BYVAL || Peek() == BYREF || Peek() == OPTIONAL_ )
{
if( Peek() == BYVAL )
{
bByVal = true;
}
else if ( Peek() == BYREF )
{
bByVal = false;
}
else if ( Peek() == OPTIONAL_ )
{
bOptional = true;
}
Next();
}
if( bCompatible && Peek() == PARAMARRAY )
{
if( bByVal || bOptional )
{
Error( ERRCODE_BASIC_UNEXPECTED, PARAMARRAY );
}
Next();
bParamArray = true;
}
SbiSymDef* pPar = VarDecl( nullptr, false, false );
if( !pPar )
{
break;
}
if( bByVal )
{
pPar->SetByVal(true);
}
if( bOptional )
{
pPar->SetOptional();
}
if( bParamArray )
{
pPar->SetParamArray();
}
pDef->GetParams().Add( pPar );
SbiToken eTok = Next();
if( eTok != COMMA && eTok != RPAREN )
{
bool bError2 = true;
if( bOptional && bCompatible && eTok == EQ )
{
std::unique_ptr<SbiConstExpression> pDefaultExpr(new SbiConstExpression( this ));
SbxDataType eType2 = pDefaultExpr->GetType();
sal_uInt16 nStringId;
if( eType2 == SbxSTRING )
{
nStringId = aGblStrings.Add( pDefaultExpr->GetString() );
}
else
{
nStringId = aGblStrings.Add( pDefaultExpr->GetValue(), eType2 );
}
pPar->SetDefaultId( nStringId );
pDefaultExpr.reset();
eTok = Next();
if( eTok == COMMA || eTok == RPAREN )
{
bError2 = false;
}
}
if( bError2 )
{
Error( ERRCODE_BASIC_EXPECTED, RPAREN );
break;
}
}
if( eTok == RPAREN )
{
break;
}
}
}
}
TypeDecl( *pDef );
if( eType != SbxVARIANT && pDef->GetType() != eType )
{
Error( ERRCODE_BASIC_BAD_DECLARATION, aName );
}
if( pDef->GetType() == SbxVARIANT && !( bFunc || bProp ) )
{
pDef->SetType( SbxEMPTY );
}
return pDef;
}
// DECLARE
void SbiParser::Declare()
{
DefDeclare( false );
}
void SbiParser::DefDeclare( bool bPrivate )
{
Next();
if( eCurTok == PTRSAFE )
Next();
if( eCurTok != SUB && eCurTok != FUNCTION )
{
Error( ERRCODE_BASIC_UNEXPECTED, eCurTok );
}
else
{
bool bFunction = (eCurTok == FUNCTION);
SbiProcDef* pDef = ProcDecl( true );
if( pDef )
{
if( pDef->GetLib().isEmpty() )
{
Error( ERRCODE_BASIC_EXPECTED, LIB );
}
// Is it already there?
SbiSymDef* pOld = aPublics.Find( pDef->GetName() );
if( pOld )
{
SbiProcDef* p = pOld->GetProcDef();
if( !p )
{
// Declared as a variable
Error( ERRCODE_BASIC_BAD_DECLARATION, pDef->GetName() );
delete pDef;
pDef = nullptr;
}
else
{
pDef->Match( std::unique_ptr<SbiProcDef>(p) );
}
}
else
{
aPublics.Add( pDef );
}
if ( pDef )
{
pDef->SetPublic( !bPrivate );
// New declare handling
if( !pDef->GetLib().isEmpty())
{
if( bNewGblDefs && nGblChain == 0 )
{
nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
bNewGblDefs = false;
}
sal_uInt16 nSavLine = nLine;
aGen.Statement();
pDef->Define();
pDef->SetLine1( nSavLine );
pDef->SetLine2( nSavLine );
SbiSymPool& rPool = pDef->GetParams();
sal_uInt16 nParCount = rPool.GetSize();
SbxDataType eType = pDef->GetType();
if( bFunction )
{
aGen.Gen( SbiOpcode::PARAM_, 0, sal::static_int_cast< sal_uInt16 >( eType ) );
}
if( nParCount > 1 )
{
aGen.Gen( SbiOpcode::ARGC_ );
for( sal_uInt16 i = 1 ; i < nParCount ; ++i )
{
SbiSymDef* pParDef = rPool.Get( i );
SbxDataType eParType = pParDef->GetType();
aGen.Gen( SbiOpcode::PARAM_, i, sal::static_int_cast< sal_uInt16 >( eParType ) );
aGen.Gen( SbiOpcode::ARGV_ );
sal_uInt16 nTyp = sal::static_int_cast< sal_uInt16 >( pParDef->GetType() );
if( pParDef->IsByVal() )
{
// Reset to avoid additional byval in call to wrapper function
pParDef->SetByVal( false );
nTyp |= 0x8000;
}
aGen.Gen( SbiOpcode::ARGTYP_, nTyp );
}
}
aGen.Gen( SbiOpcode::LIB_, aGblStrings.Add( pDef->GetLib() ) );
SbiOpcode eOp = pDef->IsCdecl() ? SbiOpcode::CALLC_ : SbiOpcode::CALL_;
sal_uInt16 nId = pDef->GetId();
if( !pDef->GetAlias().isEmpty() )
{
nId = ( nId & 0x8000 ) | aGblStrings.Add( pDef->GetAlias() );
}
if( nParCount > 1 )
{
nId |= 0x8000;
}
aGen.Gen( eOp, nId, sal::static_int_cast< sal_uInt16 >( eType ) );
if( bFunction )
{
aGen.Gen( SbiOpcode::PUT_ );
}
aGen.Gen( SbiOpcode::LEAVE_ );
}
}
}
}
}
void SbiParser::Attribute()
{
// TODO: Need to implement the method as an attributed object.
while( Next() != EQ )
{
if( Next() != DOT)
{
break;
}
}
if( eCurTok != EQ )
{
Error( ERRCODE_BASIC_SYNTAX );
}
else
{
SbiExpression aValue( this );
}
// Don't generate any code - just discard it.
}
// Call of a SUB or a FUNCTION
void SbiParser::Call()
{
SbiExpression aVar( this, SbSYMBOL );
aVar.Gen( FORCE_CALL );
aGen.Gen( SbiOpcode::GET_ );
}
// SUB/FUNCTION
void SbiParser::SubFunc()
{
DefProc( false, false );
}
// Read in of a procedure
void SbiParser::DefProc( bool bStatic, bool bPrivate )
{
sal_uInt16 l1 = nLine;
bool bSub = ( eCurTok == SUB );
bool bProperty = ( eCurTok == PROPERTY );
PropertyMode ePropertyMode = PropertyMode::NONE;
if( bProperty )
{
Next();
if( eCurTok == GET )
{
ePropertyMode = PropertyMode::Get;
}
else if( eCurTok == LET )
{
ePropertyMode = PropertyMode::Let;
}
else if( eCurTok == SET )
{
ePropertyMode = PropertyMode::Set;
}
else
{
Error( ERRCODE_BASIC_EXPECTED, "Get or Let or Set" );
}
}
SbiToken eExit = eCurTok;
SbiProcDef* pDef = ProcDecl( false );
if( !pDef )
{
return;
}
pDef->setPropertyMode( ePropertyMode );
// Is the Proc already declared?
SbiSymDef* pOld = aPublics.Find( pDef->GetName() );
if( pOld )
{
pProc = pOld->GetProcDef();
if( !pProc )
{
// Declared as a variable
Error( ERRCODE_BASIC_BAD_DECLARATION, pDef->GetName() );
delete pDef;
return;
}
// #100027: Multiple declaration -> Error
// #112787: Not for setup, REMOVE for 8
else if( pProc->IsUsedForProcDecl() )
{
PropertyMode ePropMode = pDef->getPropertyMode();
if( ePropMode == PropertyMode::NONE || ePropMode == pProc->getPropertyMode() )
{
Error( ERRCODE_BASIC_PROC_DEFINED, pDef->GetName() );
delete pDef;
return;
}
}
pDef->Match( std::unique_ptr<SbiProcDef>(pProc) );
pProc = pDef;
}
else
{
aPublics.Add( pDef );
pProc = pDef;
}
if( !pProc )
{
return;
}
pProc->SetPublic( !bPrivate );
// Now we set the search hierarchy for symbols as well as the
// current procedure.
aPublics.SetProcId( pProc->GetId() );
pProc->GetParams().SetParent( &aPublics );
if( bStatic )
{
if ( bVBASupportOn )
{
pProc->SetStatic();
}
else
{
Error( ERRCODE_BASIC_NOT_IMPLEMENTED ); // STATIC SUB ...
}
}
else
{
pProc->SetStatic( false );
}
// Normal case: Local variable->parameter->global variable
pProc->GetLocals().SetParent( &pProc->GetParams() );
pPool = &pProc->GetLocals();
pProc->Define();
OpenBlock( eExit );
StmntBlock( bSub ? ENDSUB : (bProperty ? ENDPROPERTY : ENDFUNC) );
sal_uInt16 l2 = nLine;
pProc->SetLine1( l1 );
pProc->SetLine2( l2 );
pPool = &aPublics;
aPublics.SetProcId( 0 );
// Open labels?
pProc->GetLabels().CheckRefs();
CloseBlock();
aGen.Gen( SbiOpcode::LEAVE_ );
pProc = nullptr;
}
// STATIC variable|procedure
void SbiParser::Static()
{
DefStatic( false );
}
void SbiParser::DefStatic( bool bPrivate )
{
SbiSymPool* p;
switch( Peek() )
{
case SUB:
case FUNCTION:
case PROPERTY:
// End global chain if necessary (not done in
// SbiParser::Parse() under these conditions
if( bNewGblDefs && nGblChain == 0 )
{
nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
bNewGblDefs = false;
}
Next();
DefProc( true, bPrivate );
break;
default:
if( !pProc )
{
Error( ERRCODE_BASIC_NOT_IN_SUBR );
}
// Reset the Pool, so that STATIC-Declarations go into the
// global Pool
p = pPool;
pPool = &aPublics;
DefVar( SbiOpcode::STATIC_, true );
pPool = p;
break;
}
}
bool SbiParser::IsUnoInterface(const OUString& sTypeName)
{
try
{
return css::reflection::theCoreReflection::get(
comphelper::getProcessComponentContext())->forName(sTypeName).is();
}
catch(const Exception&)
{
OSL_FAIL("Could not create reflection.CoreReflection.");
}
return false;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V668 There is no sense in testing the 'pProc' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.