summaryrefslogtreecommitdiffstats
path: root/basic/source/comp/dim.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'basic/source/comp/dim.cxx')
-rw-r--r--basic/source/comp/dim.cxx1363
1 files changed, 1363 insertions, 0 deletions
diff --git a/basic/source/comp/dim.cxx b/basic/source/comp/dim.cxx
new file mode 100644
index 000000000..cbc25b015
--- /dev/null
+++ b/basic/source/comp/dim.cxx
@@ -0,0 +1,1363 @@
+/* -*- 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/sberrors.hxx>
+#include <basic/sbstar.hxx>
+#include <basic/sbx.hxx>
+#include <sbunoobj.hxx>
+#include <parser.hxx>
+#include <sb.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/reflection/theCoreReflection.hpp>
+#include <comphelper/processfactory.hxx>
+#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 an 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) )
+ return;
+
+ 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 an 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 )
+ if (auto eOldScope = pOld->GetScope(); eOldScope != SbLOCAL && eOldScope != SbPARAM)
+ 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
+ {
+ // tdf#145371, tdf#136755 - only delete the variable beforehand REDIM
+ if (eOp == SbiOpcode::REDIM_)
+ {
+ SbiExpression aExpr(this, *pDef, nullptr);
+ aExpr.Gen();
+ aGen.Gen(bVBASupportOn ? SbiOpcode::ERASE_CLEAR_ : SbiOpcode::ERASE_);
+ }
+
+ pDef->SetDims( pDim->GetDims() );
+ SbiExpression aExpr2( this, *pDef, std::move(pDim) );
+ aExpr2.Gen();
+ aGen.Gen( SbiOpcode::DCREATE_, pDef->GetId(), pDef->GetTypeId() );
+ }
+ }
+ else
+ {
+ SbiExpression aExpr( this, *pDef );
+ aExpr.Gen();
+
+ /* tdf#88442
+ * Don't initialize a
+ * Global X as New SomeObjectType
+ * if it has already been initialized.
+ * This approach relies on JUMPT evaluating Object->NULL as being 'false'
+ * But the effect of this code is similar to inserting
+ * If IsNull(YourGlobal)
+ * Set YourGlobal = ' new obj
+ * End If ' If IsNull(YourGlobal)
+ * Only for globals. For locals that check is skipped as it's unnecessary
+ */
+ sal_uInt32 come_from = 0;
+ if ( pDef->GetScope() == SbGLOBAL )
+ {
+ come_from = aGen.Gen( SbiOpcode::JUMPT_, 0 );
+ aGen.Gen( SbiOpcode::FIND_, pDef->GetId(), pDef->GetTypeId() );
+ }
+
+ 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_ );
+
+ if ( come_from )
+ {
+ // See other tdf#88442 comment above where come_from is
+ // initialized. This is effectively 'inserting' the
+ // End If ' If IsNull(YourGlobal)
+ aGen.BackChain( come_from );
+ }
+ }
+ }
+ 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->AddDim(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 )
+ {
+ SbxObjectRef pCloneObj = cloneTypeObjectImpl( *pTypeObj );
+ pTypeElem->PutObject( pCloneObj.get() );
+ }
+ }
+ }
+ 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 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();
+ }
+ if (SbiSymDef* pOldDef = pDef->GetParams().Find(pPar->GetName(), false))
+ {
+ Error(ERRCODE_BASIC_VAR_DEFINED, pPar->GetName());
+ delete pPar;
+ pPar = pOldDef;
+ }
+ else
+ pDef->GetParams().Add( pPar );
+ SbiToken eTok = Next();
+ if( eTok != COMMA && eTok != RPAREN )
+ {
+ bool bError2 = true;
+ if( bOptional && bCompatible && eTok == EQ )
+ {
+ auto pDefaultExpr = std::make_unique<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( 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( pProc );
+ }
+ else
+ {
+ aPublics.Add( pDef );
+ }
+ assert(pDef);
+ pProc = pDef;
+ 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: */