xref: /core/basic/source/comp/dim.cxx (revision b80069e1)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <basic/sberrors.hxx>
21 #include <basic/sbstar.hxx>
22 #include <basic/sbx.hxx>
23 #include <sbunoobj.hxx>
24 #include <parser.hxx>
25 #include <sb.hxx>
26 #include <osl/diagnose.h>
27 #include <com/sun/star/reflection/theCoreReflection.hpp>
28 #include <comphelper/processfactory.hxx>
29 #include <com/sun/star/uno/Exception.hpp>
30 #include <basic/codecompletecache.hxx>
31 #include <memory>
32 
33 using namespace ::com::sun::star;
34 using namespace ::com::sun::star::uno;
35 
36 // Declaration of a variable
37 // If there are errors it will be parsed up to the comma or the newline.
38 // Return-value: a new instance, which were inserted and then deleted.
39 // Array-Index were returned as SbiExprList
40 
41 SbiSymDef* SbiParser::VarDecl( SbiExprListPtr* ppDim, bool bStatic, bool bConst )
42 {
43     bool bWithEvents = false;
44     if( Peek() == WITHEVENTS )
45     {
46         Next();
47         bWithEvents = true;
48     }
49     if( !TestSymbol() ) return nullptr;
50     SbxDataType t = eScanType;
51     SbiSymDef* pDef = bConst ? new SbiConstDef( aSym ) : new SbiSymDef( aSym );
52     SbiExprListPtr pDim;
53     // Brackets?
54     if( Peek() == LPAREN )
55     {
56         pDim = SbiExprList::ParseDimList( this );
57         if( !pDim->GetDims() )
58             pDef->SetWithBrackets();
59     }
60     pDef->SetType( t );
61     if( bStatic )
62         pDef->SetStatic();
63     if( bWithEvents )
64         pDef->SetWithEvents();
65     TypeDecl( *pDef );
66     if( !ppDim && pDim )
67     {
68         if(pDim->GetDims() )
69             Error( ERRCODE_BASIC_EXPECTED, "()" );
70     }
71     else if( ppDim )
72         *ppDim = std::move(pDim);
73     return pDef;
74 }
75 
76 // Resolving of an AS-Type-Declaration
77 // The data type were inserted into the handed over variable
78 
79 void SbiParser::TypeDecl( SbiSymDef& rDef, bool bAsNewAlreadyParsed )
80 {
81     SbxDataType eType = rDef.GetType();
82     if( !(bAsNewAlreadyParsed || Peek() == AS) )
83         return;
84 
85     short nSize = 0;
86     if( !bAsNewAlreadyParsed )
87         Next();
88     rDef.SetDefinedAs();
89     SbiToken eTok = Next();
90     if( !bAsNewAlreadyParsed && eTok == NEW )
91     {
92         rDef.SetNew();
93         eTok = Next();
94     }
95     switch( eTok )
96     {
97         case ANY:
98             if( rDef.IsNew() )
99                 Error( ERRCODE_BASIC_SYNTAX );
100             eType = SbxVARIANT; break;
101         case TINTEGER:
102         case TLONG:
103         case TSINGLE:
104         case TDOUBLE:
105         case TCURRENCY:
106         case TDATE:
107         case TSTRING:
108         case TOBJECT:
109         case ERROR_:
110         case TBOOLEAN:
111         case TVARIANT:
112         case TBYTE:
113             if( rDef.IsNew() )
114                 Error( ERRCODE_BASIC_SYNTAX );
115             eType = (eTok==TBYTE) ? SbxBYTE : SbxDataType( eTok - TINTEGER + SbxINTEGER );
116             if( eType == SbxSTRING )
117             {
118                 // STRING*n ?
119                 if( Peek() == MUL )
120                 {       // fixed size!
121                     Next();
122                     SbiConstExpression aSize( this );
123                     nSize = aSize.GetShortValue();
124                     if( nSize < 0 || (bVBASupportOn && nSize <= 0) )
125                         Error( ERRCODE_BASIC_OUT_OF_RANGE );
126                     else
127                         rDef.SetFixedStringLength( nSize );
128                 }
129             }
130             break;
131         case SYMBOL: // can only be a TYPE or an object class!
132             if( eScanType != SbxVARIANT )
133                 Error( ERRCODE_BASIC_SYNTAX );
134             else
135             {
136                 OUString aCompleteName = aSym;
137 
138                 // #52709 DIM AS NEW for Uno with full-qualified name
139                 if( Peek() == DOT )
140                 {
141                     OUString aDotStr( '.' );
142                     while( Peek() == DOT )
143                     {
144                         aCompleteName += aDotStr;
145                         Next();
146                         SbiToken ePeekTok = Peek();
147                         if( ePeekTok == SYMBOL || IsKwd( ePeekTok ) )
148                         {
149                             Next();
150                             aCompleteName += aSym;
151                         }
152                         else
153                         {
154                             Next();
155                             Error( ERRCODE_BASIC_UNEXPECTED, SYMBOL );
156                             break;
157                         }
158                     }
159                 }
160                 else if( rEnumArray->Find( aCompleteName, SbxClassType::Object ) || ( IsVBASupportOn() && VBAConstantHelper::instance().isVBAConstantType( aCompleteName ) ) )
161                 {
162                     eType = SbxLONG;
163                     break;
164                 }
165 
166                 // Take over in the string pool
167                 rDef.SetTypeId( aGblStrings.Add( aCompleteName ) );
168 
169                 if( rDef.IsNew() && pProc == nullptr )
170                     aRequiredTypes.push_back( aCompleteName );
171             }
172             eType = SbxOBJECT;
173             break;
174         case FIXSTRING: // new syntax for complex UNO types
175             rDef.SetTypeId( aGblStrings.Add( aSym ) );
176             eType = SbxOBJECT;
177             break;
178         default:
179             Error( ERRCODE_BASIC_UNEXPECTED, eTok );
180             Next();
181     }
182     // The variable could have been declared with a suffix
183     if( rDef.GetType() != SbxVARIANT )
184     {
185         if( rDef.GetType() != eType )
186             Error( ERRCODE_BASIC_VAR_DEFINED, rDef.GetName() );
187         else if( eType == SbxSTRING && rDef.GetLen() != nSize )
188             Error( ERRCODE_BASIC_VAR_DEFINED, rDef.GetName() );
189     }
190     rDef.SetType( eType );
191     rDef.SetLen( nSize );
192 }
193 
194 // Here variables, arrays and structures were defined.
195 // DIM/PRIVATE/PUBLIC/GLOBAL
196 
197 void SbiParser::Dim()
198 {
199     DefVar( SbiOpcode::DIM_, pProc && bVBASupportOn && pProc->IsStatic() );
200 }
201 
202 void SbiParser::DefVar( SbiOpcode eOp, bool bStatic )
203 {
204     SbiSymPool* pOldPool = pPool;
205     bool bSwitchPool = false;
206     bool bPersistentGlobal = false;
207     SbiToken eFirstTok = eCurTok;
208 
209     if( pProc && ( eCurTok == GLOBAL || eCurTok == PUBLIC || eCurTok == PRIVATE ) )
210         Error( ERRCODE_BASIC_NOT_IN_SUBR, eCurTok );
211     if( eCurTok == PUBLIC || eCurTok == GLOBAL )
212     {
213         bSwitchPool = true;     // at the right moment switch to the global pool
214         if( eCurTok == GLOBAL )
215             bPersistentGlobal = true;
216     }
217     // behavior in VBA is that a module scope variable's lifetime is
218     // tied to the document. e.g. a module scope variable is global
219     if(  GetBasic()->IsDocBasic() && bVBASupportOn && !pProc )
220         bPersistentGlobal = true;
221     // PRIVATE is a synonymous for DIM
222     // _CONST_?
223     bool bConst = false;
224     if( eCurTok == CONST_ )
225         bConst = true;
226     else if( Peek() == CONST_ )
227     {
228         Next();
229         bConst = true;
230     }
231 
232     // #110004 It can also be a sub/function
233     if( !bConst && (eCurTok == SUB || eCurTok == FUNCTION || eCurTok == PROPERTY ||
234                     eCurTok == STATIC || eCurTok == ENUM || eCurTok == DECLARE || eCurTok == TYPE) )
235     {
236         // Next token is read here, because !bConst
237         bool bPrivate = ( eFirstTok == PRIVATE );
238 
239         if( eCurTok == STATIC )
240         {
241             Next();
242             DefStatic( bPrivate );
243         }
244         else if( eCurTok == SUB || eCurTok == FUNCTION || eCurTok == PROPERTY )
245         {
246             // End global chain if necessary (not done in
247             // SbiParser::Parse() under these conditions
248             if( bNewGblDefs && nGblChain == 0 )
249             {
250                 nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
251                 bNewGblDefs = false;
252             }
253             Next();
254             DefProc( false, bPrivate );
255             return;
256         }
257         else if( eCurTok == ENUM )
258         {
259             Next();
260             DefEnum( bPrivate );
261             return;
262         }
263         else if( eCurTok == DECLARE )
264         {
265             Next();
266             DefDeclare( bPrivate );
267             return;
268         }
269         // #i109049
270         else if( eCurTok == TYPE )
271         {
272             Next();
273             DefType(); // TODO: Use bPrivate in DefType()
274             return;
275         }
276     }
277 
278     // SHARED were ignored
279     if( Peek() == SHARED ) Next();
280 
281     // PRESERVE only at REDIM
282     if( Peek() == PRESERVE )
283     {
284         Next();
285         if( eOp == SbiOpcode::REDIM_ )
286             eOp = SbiOpcode::REDIMP_;
287         else
288             Error( ERRCODE_BASIC_UNEXPECTED, eCurTok );
289     }
290     SbiSymDef* pDef;
291     SbiExprListPtr pDim;
292 
293     // #40689, Statics -> Module-Initialising, skip in Sub
294     sal_uInt32 nEndOfStaticLbl = 0;
295     if( !bVBASupportOn && bStatic )
296     {
297         nEndOfStaticLbl = aGen.Gen( SbiOpcode::JUMP_, 0 );
298         aGen.Statement();   // catch up on static here
299     }
300 
301     bool bDefined = false;
302     while( ( pDef = VarDecl( &pDim, bStatic, bConst ) ) != nullptr )
303     {
304         /*fprintf(stderr, "Actual sub: \n");
305         fprintf(stderr, "Symbol name: %s\n",OUStringToOString(pDef->GetName(),RTL_TEXTENCODING_UTF8).getStr());*/
306         EnableErrors();
307         // search variable:
308         if( bSwitchPool )
309             pPool = &aGlobals;
310         SbiSymDef* pOld = pPool->Find( pDef->GetName() );
311         // search also in the Runtime-Library
312         bool bRtlSym = false;
313         if( !pOld )
314         {
315             pOld = CheckRTLForSym( pDef->GetName(), SbxVARIANT );
316             if( pOld )
317                 bRtlSym = true;
318         }
319         if( pOld && eOp != SbiOpcode::REDIM_ && eOp != SbiOpcode::REDIMP_ )
320         {
321             if( pDef->GetScope() == SbLOCAL )
322                 if (auto eOldScope = pOld->GetScope(); eOldScope != SbLOCAL && eOldScope != SbPARAM)
323                     pOld = nullptr;
324         }
325         if( pOld )
326         {
327             bDefined = true;
328             // always an error at a RTL-S
329             if( !bRtlSym && (eOp == SbiOpcode::REDIM_ || eOp == SbiOpcode::REDIMP_) )
330             {
331                 // compare the attributes at a REDIM
332                 SbxDataType eDefType;
333                 bool bError_ = false;
334                 if( pOld->IsStatic() )
335                 {
336                     bError_ = true;
337                 }
338                 else if( pOld->GetType() != ( eDefType = pDef->GetType() ) )
339                 {
340                     if( eDefType != SbxVARIANT || pDef->IsDefinedAs() )
341                         bError_ = true;
342                 }
343                 if( bError_ )
344                     Error( ERRCODE_BASIC_VAR_DEFINED, pDef->GetName() );
345             }
346             else
347                 Error( ERRCODE_BASIC_VAR_DEFINED, pDef->GetName() );
348             delete pDef; pDef = pOld;
349         }
350         else
351             pPool->Add( pDef );
352 
353         // #36374: Create the variable in front of the distinction IsNew()
354         // Otherwise error at Dim Identifier As New Type and option explicit
355         if( !bDefined && eOp != SbiOpcode::REDIM_ && eOp != SbiOpcode::REDIMP_
356                       && ( !bConst || pDef->GetScope() == SbGLOBAL ) )
357         {
358             // Declare variable or global constant
359             SbiOpcode eOp2;
360             switch ( pDef->GetScope() )
361             {
362                 case SbGLOBAL:  eOp2 = bPersistentGlobal ? SbiOpcode::GLOBAL_P_ : SbiOpcode::GLOBAL_;
363                                 goto global;
364                 case SbPUBLIC:  eOp2 = bPersistentGlobal ? SbiOpcode::PUBLIC_P_ : SbiOpcode::PUBLIC_;
365                                 // #40689, no own Opcode anymore
366                                 if( bVBASupportOn && bStatic )
367                                 {
368                                     eOp2 = SbiOpcode::STATIC_;
369                                     break;
370                                 }
371                 global:         aGen.BackChain( nGblChain );
372                                 nGblChain = 0;
373                                 bGblDefs = bNewGblDefs = true;
374                                 break;
375                 default:        eOp2 = SbiOpcode::LOCAL_;
376             }
377             sal_uInt32 nOpnd2 = sal::static_int_cast< sal_uInt16 >( pDef->GetType() );
378             if( pDef->IsWithEvents() )
379                 nOpnd2 |= SBX_TYPE_WITH_EVENTS_FLAG;
380 
381             if( bCompatible && pDef->IsNew() )
382                 nOpnd2 |= SBX_TYPE_DIM_AS_NEW_FLAG;
383 
384             short nFixedStringLength = pDef->GetFixedStringLength();
385             if( nFixedStringLength >= 0 )
386                 nOpnd2 |= (SBX_FIXED_LEN_STRING_FLAG + (sal_uInt32(nFixedStringLength) << 17));     // len = all bits above 0x10000
387 
388             if( pDim != nullptr && pDim->GetDims() > 0 )
389                 nOpnd2 |= SBX_TYPE_VAR_TO_DIM_FLAG;
390 
391             aGen.Gen( eOp2, pDef->GetId(), nOpnd2 );
392         }
393 
394         // Initialising for self-defined data types
395         // and per NEW created variable
396         if( pDef->GetType() == SbxOBJECT
397          && pDef->GetTypeId() )
398         {
399             if( !bCompatible && !pDef->IsNew() )
400             {
401                 OUString aTypeName( aGblStrings.Find( pDef->GetTypeId() ) );
402                 if( rTypeArray->Find( aTypeName, SbxClassType::Object ) == nullptr )
403                 {
404                     if( CodeCompleteOptions::IsExtendedTypeDeclaration() )
405                     {
406                         if(!IsUnoInterface(aTypeName))
407                             Error( ERRCODE_BASIC_UNDEF_TYPE, aTypeName );
408                     }
409                     else
410                         Error( ERRCODE_BASIC_UNDEF_TYPE, aTypeName );
411                 }
412             }
413 
414             if( bConst )
415             {
416                 Error( ERRCODE_BASIC_SYNTAX );
417             }
418 
419             if( pDim )
420             {
421                 if( eOp == SbiOpcode::REDIMP_ )
422                 {
423                     SbiExpression aExpr( this, *pDef, nullptr );
424                     aExpr.Gen();
425                     aGen.Gen( SbiOpcode::REDIMP_ERASE_ );
426 
427                     pDef->SetDims( pDim->GetDims() );
428                     SbiExpression aExpr2( this, *pDef, std::move(pDim) );
429                     aExpr2.Gen();
430                     aGen.Gen( SbiOpcode::DCREATE_REDIMP_, pDef->GetId(), pDef->GetTypeId() );
431                 }
432                 else
433                 {
434                     // tdf#136755 - delete the variable beforehand REDIM
435                     SbiExpression aExpr(this, *pDef, nullptr);
436                     aExpr.Gen();
437                     aGen.Gen(bVBASupportOn ? SbiOpcode::ERASE_CLEAR_ : SbiOpcode::ERASE_);
438 
439                     pDef->SetDims( pDim->GetDims() );
440                     SbiExpression aExpr2( this, *pDef, std::move(pDim) );
441                     aExpr2.Gen();
442                     aGen.Gen( SbiOpcode::DCREATE_, pDef->GetId(), pDef->GetTypeId() );
443                 }
444             }
445             else
446             {
447                 SbiExpression aExpr( this, *pDef );
448                 aExpr.Gen();
449 
450                 /* tdf#88442
451                  * Don't initialize a
452                  *      Global X as New SomeObjectType
453                  * if it has already been initialized.
454                  * This approach relies on JUMPT evaluating Object->NULL as being 'false'
455                  * But the effect of this code is similar to inserting
456                  *  If IsNull(YourGlobal)
457                  *      Set YourGlobal = ' new obj
458                  *  End If ' If IsNull(YourGlobal)
459                  * Only for globals. For locals that check is skipped as it's unnecessary
460                  */
461                 sal_uInt32 come_from = 0;
462                 if ( pDef->GetScope() == SbGLOBAL )
463                 {
464                     come_from = aGen.Gen( SbiOpcode::JUMPT_, 0 );
465                     aGen.Gen( SbiOpcode::FIND_, pDef->GetId(), pDef->GetTypeId() );
466                 }
467 
468                 SbiOpcode eOp_ = pDef->IsNew() ? SbiOpcode::CREATE_ : SbiOpcode::TCREATE_;
469                 aGen.Gen( eOp_, pDef->GetId(), pDef->GetTypeId() );
470                 if ( bVBASupportOn )
471                     aGen.Gen( SbiOpcode::VBASET_ );
472                 else
473                     aGen.Gen( SbiOpcode::SET_ );
474 
475                 if ( come_from )
476                 {
477                     // See other tdf#88442 comment above where come_from is
478                         // initialized. This is effectively 'inserting' the
479                         // End If ' If IsNull(YourGlobal)
480                     aGen.BackChain( come_from );
481                 }
482             }
483         }
484         else
485         {
486             if( bConst )
487             {
488                 // Definition of the constants
489                 if( pDim )
490                 {
491                     Error( ERRCODE_BASIC_SYNTAX );
492                 }
493                 SbiExpression aVar( this, *pDef );
494                 if( !TestToken( EQ ) )
495                     goto MyBreak;   // (see below)
496                 SbiConstExpression aExpr( this );
497                 if( !bDefined && aExpr.IsValid() )
498                 {
499                     if( pDef->GetScope() == SbGLOBAL )
500                     {
501                         // Create code only for the global constant!
502                         aVar.Gen();
503                         aExpr.Gen();
504                         aGen.Gen( SbiOpcode::PUTC_ );
505                     }
506                     SbiConstDef* pConst = pDef->GetConstDef();
507                     if( aExpr.GetType() == SbxSTRING )
508                         pConst->Set( aExpr.GetString() );
509                     else
510                         pConst->Set( aExpr.GetValue(), aExpr.GetType() );
511                 }
512             }
513             else if( pDim )
514             {
515                 // Dimension the variable
516                 // Delete the var at REDIM beforehand
517                 if( eOp == SbiOpcode::REDIM_ )
518                 {
519                     SbiExpression aExpr( this, *pDef, nullptr );
520                     aExpr.Gen();
521                     if ( bVBASupportOn )
522                         // delete the array but
523                         // clear the variable ( this
524                         // allows the processing of
525                         // the param to happen as normal without errors ( ordinary ERASE just clears the array )
526                         aGen.Gen( SbiOpcode::ERASE_CLEAR_ );
527                     else
528                         aGen.Gen( SbiOpcode::ERASE_ );
529                 }
530                 else if( eOp == SbiOpcode::REDIMP_ )
531                 {
532                     SbiExpression aExpr( this, *pDef, nullptr );
533                     aExpr.Gen();
534                     aGen.Gen( SbiOpcode::REDIMP_ERASE_ );
535                 }
536                 pDef->SetDims( pDim->GetDims() );
537                 if( bPersistentGlobal )
538                     pDef->SetGlobal( true );
539                 SbiExpression aExpr( this, *pDef, std::move(pDim) );
540                 aExpr.Gen();
541                 pDef->SetGlobal( false );
542                 aGen.Gen( (eOp == SbiOpcode::STATIC_) ? SbiOpcode::DIM_ : eOp );
543             }
544         }
545         if( !TestComma() )
546             goto MyBreak;
547 
548         // Implementation of bSwitchPool (see above): pPool must not be set to &aGlobals
549         // at the VarDecl-Call.
550         // Apart from that the behavior should be absolutely identical,
551         // i.e., pPool had to be reset always at the end of the loop.
552         // also at a break
553         pPool = pOldPool;
554         continue;       // Skip MyBreak
555     MyBreak:
556         pPool = pOldPool;
557         break;
558     }
559 
560     // #40689, finalize the jump over statics declarations
561     if( !bVBASupportOn && bStatic )
562     {
563         // maintain the global chain
564         nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
565         bGblDefs = bNewGblDefs = true;
566 
567         // Register for Sub a jump to the end of statics
568         aGen.BackChain( nEndOfStaticLbl );
569     }
570 
571 }
572 
573 // Here were Arrays redimensioned.
574 
575 void SbiParser::ReDim()
576 {
577     DefVar( SbiOpcode::REDIM_, pProc && bVBASupportOn && pProc->IsStatic() );
578 }
579 
580 // ERASE array, ...
581 
582 void SbiParser::Erase()
583 {
584     while( !bAbort )
585     {
586         SbiExpression aExpr( this, SbLVALUE );
587         aExpr.Gen();
588         aGen.Gen( SbiOpcode::ERASE_ );
589         if( !TestComma() ) break;
590     }
591 }
592 
593 // Declaration of a data type
594 
595 void SbiParser::Type()
596 {
597     DefType();
598 }
599 
600 void SbiParser::DefType()
601 {
602     // Read the new Token lesen. It had to be a symbol
603     if (!TestSymbol())
604         return;
605 
606     if (rTypeArray->Find(aSym,SbxClassType::Object))
607     {
608         Error( ERRCODE_BASIC_VAR_DEFINED, aSym );
609         return;
610     }
611 
612     SbxObject *pType = new SbxObject(aSym);
613 
614     bool bDone = false;
615 
616     while( !bDone && !IsEof() )
617     {
618         std::unique_ptr<SbiSymDef> pElem;
619         SbiExprListPtr pDim;
620         switch( Peek() )
621         {
622             case ENDTYPE :
623                 bDone = true;
624                 Next();
625             break;
626 
627             case EOLN :
628             case REM :
629                 Next();
630             break;
631 
632             default:
633                 pElem.reset(VarDecl(&pDim, false, false));
634                 if( !pElem )
635                     bDone = true;   // Error occurred
636         }
637         if( pElem )
638         {
639             SbxArray *pTypeMembers = pType->GetProperties();
640             OUString aElemName = pElem->GetName();
641             if( pTypeMembers->Find( aElemName, SbxClassType::DontCare) )
642             {
643                 Error (ERRCODE_BASIC_VAR_DEFINED);
644             }
645             else
646             {
647                 SbxDataType eElemType = pElem->GetType();
648                 SbxProperty *pTypeElem = new SbxProperty( aElemName, eElemType );
649                 if( pDim )
650                 {
651                     SbxDimArray* pArray = new SbxDimArray( pElem->GetType() );
652                     if ( pDim->GetSize() )
653                     {
654                         // Dimension the target array
655 
656                         for ( short i=0; i<pDim->GetSize();++i )
657                         {
658                             sal_Int32 lb = nBase;
659                             SbiExprNode* pNode =  pDim->Get(i)->GetExprNode();
660                             sal_Int32 ub = pNode->GetNumber();
661                             if ( !pDim->Get( i )->IsBased() ) // each dim is low/up
662                             {
663                                 if (  ++i >= pDim->GetSize() ) // trouble
664                                     StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
665                                 pNode =  pDim->Get(i)->GetExprNode();
666                                 lb = ub;
667                                 ub = pNode->GetNumber();
668                             }
669                             else if ( !bCompatible )
670                                 ub += nBase;
671                             pArray->AddDim(lb, ub);
672                         }
673                         pArray->setHasFixedSize( true );
674                     }
675                     else
676                         pArray->unoAddDim(0, -1); // variant array
677                     SbxFlagBits nSavFlags = pTypeElem->GetFlags();
678                     // need to reset the FIXED flag
679                     // when calling PutObject ( because the type will not match Object )
680                     pTypeElem->ResetFlag( SbxFlagBits::Fixed );
681                     pTypeElem->PutObject( pArray );
682                     pTypeElem->SetFlags( nSavFlags );
683                 }
684                 // Nested user type?
685                 if( eElemType == SbxOBJECT )
686                 {
687                     sal_uInt16 nElemTypeId = pElem->GetTypeId();
688                     if( nElemTypeId != 0 )
689                     {
690                         OUString aTypeName( aGblStrings.Find( nElemTypeId ) );
691                         SbxObject* pTypeObj = static_cast< SbxObject* >( rTypeArray->Find( aTypeName, SbxClassType::Object ) );
692                         if( pTypeObj != nullptr )
693                         {
694                             SbxObject* pCloneObj = cloneTypeObjectImpl( *pTypeObj );
695                             pTypeElem->PutObject( pCloneObj );
696                         }
697                     }
698                 }
699                 pTypeMembers->Insert(pTypeElem, pTypeMembers->Count());
700             }
701         }
702     }
703 
704     pType->Remove( "Name", SbxClassType::DontCare );
705     pType->Remove( "Parent", SbxClassType::DontCare );
706 
707     rTypeArray->Insert(pType, rTypeArray->Count());
708 }
709 
710 
711 // Declaration of Enum type
712 
713 void SbiParser::Enum()
714 {
715     DefEnum( false );
716 }
717 
718 void SbiParser::DefEnum( bool bPrivate )
719 {
720     // Read the new Token. It had to be a symbol
721     if (!TestSymbol())
722         return;
723 
724     OUString aEnumName = aSym;
725     if( rEnumArray->Find(aEnumName,SbxClassType::Object) )
726     {
727         Error( ERRCODE_BASIC_VAR_DEFINED, aSym );
728         return;
729     }
730 
731     SbxObject *pEnum = new SbxObject( aEnumName );
732     if( bPrivate )
733     {
734         pEnum->SetFlag( SbxFlagBits::Private );
735     }
736     SbiSymDef* pElem;
737     bool bDone = false;
738 
739     // Starting with -1 to make first default value 0 after ++
740     sal_Int32 nCurrentEnumValue = -1;
741     while( !bDone && !IsEof() )
742     {
743         switch( Peek() )
744         {
745             case ENDENUM :
746                 pElem = nullptr;
747                 bDone = true;
748                 Next();
749             break;
750 
751             case EOLN :
752             case REM :
753                 pElem = nullptr;
754                 Next();
755             break;
756 
757             default:
758             {
759                 SbiExprListPtr pDim;
760                 pElem = VarDecl( &pDim, false, true );
761                 if( !pElem )
762                 {
763                     bDone = true;   // Error occurred
764                     break;
765                 }
766                 else if( pDim )
767                 {
768                     Error( ERRCODE_BASIC_SYNTAX );
769                     bDone = true;   // Error occurred
770                     break;
771                 }
772 
773                 SbiExpression aVar( this, *pElem );
774                 if( Peek() == EQ )
775                 {
776                     Next();
777 
778                     SbiConstExpression aExpr( this );
779                     if( aExpr.IsValid() )
780                     {
781                         SbxVariableRef xConvertVar = new SbxVariable();
782                         if( aExpr.GetType() == SbxSTRING )
783                             xConvertVar->PutString( aExpr.GetString() );
784                         else
785                             xConvertVar->PutDouble( aExpr.GetValue() );
786 
787                         nCurrentEnumValue = xConvertVar->GetLong();
788                     }
789                 }
790                 else
791                     nCurrentEnumValue++;
792 
793                 SbiSymPool* pPoolToUse = bPrivate ? pPool : &aGlobals;
794 
795                 SbiSymDef* pOld = pPoolToUse->Find( pElem->GetName() );
796                 if( pOld )
797                 {
798                     Error( ERRCODE_BASIC_VAR_DEFINED, pElem->GetName() );
799                     bDone = true;   // Error occurred
800                     break;
801                 }
802 
803                 pPool->Add( pElem );
804 
805                 if( !bPrivate )
806                 {
807                     aGen.BackChain( nGblChain );
808                     nGblChain = 0;
809                     bGblDefs = bNewGblDefs = true;
810                     aGen.Gen(
811                         SbiOpcode::GLOBAL_, pElem->GetId(),
812                         sal::static_int_cast< sal_uInt16 >( pElem->GetType() ) );
813 
814                     aVar.Gen();
815                     sal_uInt16 nStringId = aGen.GetParser()->aGblStrings.Add( nCurrentEnumValue, SbxLONG );
816                     aGen.Gen( SbiOpcode::NUMBER_, nStringId );
817                     aGen.Gen( SbiOpcode::PUTC_ );
818                 }
819 
820                 SbiConstDef* pConst = pElem->GetConstDef();
821                 pConst->Set( nCurrentEnumValue, SbxLONG );
822             }
823         }
824         if( pElem )
825         {
826             SbxArray *pEnumMembers = pEnum->GetProperties();
827             SbxProperty *pEnumElem = new SbxProperty( pElem->GetName(), SbxLONG );
828             pEnumElem->PutLong( nCurrentEnumValue );
829             pEnumElem->ResetFlag( SbxFlagBits::Write );
830             pEnumElem->SetFlag( SbxFlagBits::Const );
831             pEnumMembers->Insert(pEnumElem, pEnumMembers->Count());
832         }
833     }
834 
835     pEnum->Remove( "Name", SbxClassType::DontCare );
836     pEnum->Remove( "Parent", SbxClassType::DontCare );
837 
838     rEnumArray->Insert(pEnum, rEnumArray->Count());
839 }
840 
841 
842 // Procedure-Declaration
843 // the first Token is already read in (SUB/FUNCTION)
844 // xxx Name [LIB "name"[ALIAS "name"]][(Parameter)][AS TYPE]
845 
846 SbiProcDef* SbiParser::ProcDecl( bool bDecl )
847 {
848     bool bFunc = ( eCurTok == FUNCTION );
849     bool bProp = ( eCurTok == GET || eCurTok == SET || eCurTok == LET );
850     if( !TestSymbol() ) return nullptr;
851     OUString aName( aSym );
852     SbxDataType eType = eScanType;
853     SbiProcDef* pDef = new SbiProcDef( this, aName, true );
854     pDef->SetType( eType );
855     if( Peek() == CDECL_ )
856     {
857         Next(); pDef->SetCdecl(true);
858     }
859     if( Peek() == LIB )
860     {
861         Next();
862         if( Next() == FIXSTRING )
863         {
864             pDef->GetLib() = aSym;
865         }
866         else
867         {
868             Error( ERRCODE_BASIC_SYNTAX );
869         }
870     }
871     if( Peek() == ALIAS )
872     {
873         Next();
874         if( Next() == FIXSTRING )
875         {
876             pDef->GetAlias() = aSym;
877         }
878         else
879         {
880             Error( ERRCODE_BASIC_SYNTAX );
881         }
882     }
883     if( !bDecl )
884     {
885         // CDECL, LIB and ALIAS are invalid
886         if( !pDef->GetLib().isEmpty() )
887         {
888             Error( ERRCODE_BASIC_UNEXPECTED, LIB );
889         }
890         if( !pDef->GetAlias().isEmpty() )
891         {
892             Error( ERRCODE_BASIC_UNEXPECTED, ALIAS );
893         }
894         if( pDef->IsCdecl() )
895         {
896             Error( ERRCODE_BASIC_UNEXPECTED, CDECL_ );
897         }
898         pDef->SetCdecl( false );
899         pDef->GetLib().clear();
900         pDef->GetAlias().clear();
901     }
902     else if( pDef->GetLib().isEmpty() )
903     {
904         // ALIAS and CDECL only together with LIB
905         if( !pDef->GetAlias().isEmpty() )
906         {
907             Error( ERRCODE_BASIC_UNEXPECTED, ALIAS );
908         }
909         if( pDef->IsCdecl() )
910         {
911             Error( ERRCODE_BASIC_UNEXPECTED, CDECL_ );
912         }
913         pDef->SetCdecl( false );
914         pDef->GetAlias().clear();
915     }
916     // Brackets?
917     if( Peek() == LPAREN )
918     {
919         Next();
920         if( Peek() == RPAREN )
921         {
922             Next();
923         }
924         else
925         {
926             for(;;)
927             {
928                 bool bByVal = false;
929                 bool bOptional = false;
930                 bool bParamArray = false;
931                 while( Peek() == BYVAL || Peek() == BYREF || Peek() == OPTIONAL_ )
932                 {
933                     if( Peek() == BYVAL )
934                     {
935                         bByVal = true;
936                     }
937                     else if ( Peek() == BYREF )
938                     {
939                         bByVal = false;
940                     }
941                     else if ( Peek() == OPTIONAL_ )
942                     {
943                         bOptional = true;
944                     }
945                     Next();
946                 }
947                 if( bCompatible && Peek() == PARAMARRAY )
948                 {
949                     if( bByVal || bOptional )
950                     {
951                         Error( ERRCODE_BASIC_UNEXPECTED, PARAMARRAY );
952                     }
953                     Next();
954                     bParamArray = true;
955                 }
956                 SbiSymDef* pPar = VarDecl( nullptr, false, false );
957                 if( !pPar )
958                 {
959                     break;
960                 }
961                 if( bByVal )
962                 {
963                     pPar->SetByVal(true);
964                 }
965                 if( bOptional )
966                 {
967                     pPar->SetOptional();
968                 }
969                 if( bParamArray )
970                 {
971                     pPar->SetParamArray();
972                 }
973                 if (SbiSymDef* pOldDef = pDef->GetParams().Find(pPar->GetName(), false))
974                 {
975                     Error(ERRCODE_BASIC_VAR_DEFINED, pPar->GetName());
976                     delete pPar;
977                     pPar = pOldDef;
978                 }
979                 else
980                     pDef->GetParams().Add( pPar );
981                 SbiToken eTok = Next();
982                 if( eTok != COMMA && eTok != RPAREN )
983                 {
984                     bool bError2 = true;
985                     if( bOptional && bCompatible && eTok == EQ )
986                     {
987                         auto pDefaultExpr = std::make_unique<SbiConstExpression>(this);
988                         SbxDataType eType2 = pDefaultExpr->GetType();
989 
990                         sal_uInt16 nStringId;
991                         if( eType2 == SbxSTRING )
992                         {
993                             nStringId = aGblStrings.Add( pDefaultExpr->GetString() );
994                         }
995                         else
996                         {
997                             nStringId = aGblStrings.Add( pDefaultExpr->GetValue(), eType2 );
998                         }
999                         pPar->SetDefaultId( nStringId );
1000                         pDefaultExpr.reset();
1001 
1002                         eTok = Next();
1003                         if( eTok == COMMA || eTok == RPAREN )
1004                         {
1005                             bError2 = false;
1006                         }
1007                     }
1008                     if( bError2 )
1009                     {
1010                         Error( ERRCODE_BASIC_EXPECTED, RPAREN );
1011                         break;
1012                     }
1013                 }
1014                 if( eTok == RPAREN )
1015                 {
1016                     break;
1017                 }
1018             }
1019         }
1020     }
1021     TypeDecl( *pDef );
1022     if( eType != SbxVARIANT && pDef->GetType() != eType )
1023     {
1024         Error( ERRCODE_BASIC_BAD_DECLARATION, aName );
1025     }
1026     if( pDef->GetType() == SbxVARIANT && !( bFunc || bProp ) )
1027     {
1028         pDef->SetType( SbxEMPTY );
1029     }
1030     return pDef;
1031 }
1032 
1033 // DECLARE
1034 
1035 void SbiParser::Declare()
1036 {
1037     DefDeclare( false );
1038 }
1039 
1040 void SbiParser::DefDeclare( bool bPrivate )
1041 {
1042     Next();
1043     if( eCurTok == PTRSAFE )
1044         Next();
1045 
1046     if( eCurTok != SUB && eCurTok != FUNCTION )
1047     {
1048       Error( ERRCODE_BASIC_UNEXPECTED, eCurTok );
1049     }
1050     else
1051     {
1052         bool bFunction = (eCurTok == FUNCTION);
1053 
1054         SbiProcDef* pDef = ProcDecl( true );
1055         if( pDef )
1056         {
1057             if( pDef->GetLib().isEmpty() )
1058             {
1059                 Error( ERRCODE_BASIC_EXPECTED, LIB );
1060             }
1061             // Is it already there?
1062             SbiSymDef* pOld = aPublics.Find( pDef->GetName() );
1063             if( pOld )
1064             {
1065                 SbiProcDef* p = pOld->GetProcDef();
1066                 if( !p )
1067                 {
1068                     // Declared as a variable
1069                     Error( ERRCODE_BASIC_BAD_DECLARATION, pDef->GetName() );
1070                     delete pDef;
1071                     pDef = nullptr;
1072                 }
1073                 else
1074                 {
1075                     pDef->Match( p );
1076                 }
1077             }
1078             else
1079             {
1080                 aPublics.Add( pDef );
1081             }
1082             if ( pDef )
1083             {
1084                 pDef->SetPublic( !bPrivate );
1085 
1086                 // New declare handling
1087                 if( !pDef->GetLib().isEmpty())
1088                 {
1089                     if( bNewGblDefs && nGblChain == 0 )
1090                     {
1091                         nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
1092                         bNewGblDefs = false;
1093                     }
1094 
1095                     sal_uInt16 nSavLine = nLine;
1096                     aGen.Statement();
1097                     pDef->Define();
1098                     pDef->SetLine1( nSavLine );
1099                     pDef->SetLine2( nSavLine );
1100 
1101                     SbiSymPool& rPool = pDef->GetParams();
1102                     sal_uInt16 nParCount = rPool.GetSize();
1103 
1104                     SbxDataType eType = pDef->GetType();
1105                     if( bFunction )
1106                     {
1107                         aGen.Gen( SbiOpcode::PARAM_, 0, sal::static_int_cast< sal_uInt16 >( eType ) );
1108                     }
1109                     if( nParCount > 1 )
1110                     {
1111                         aGen.Gen( SbiOpcode::ARGC_ );
1112 
1113                         for( sal_uInt16 i = 1 ; i < nParCount ; ++i )
1114                         {
1115                             SbiSymDef* pParDef = rPool.Get( i );
1116                             SbxDataType eParType = pParDef->GetType();
1117 
1118                             aGen.Gen( SbiOpcode::PARAM_, i, sal::static_int_cast< sal_uInt16 >( eParType ) );
1119                             aGen.Gen( SbiOpcode::ARGV_ );
1120 
1121                             sal_uInt16 nTyp = sal::static_int_cast< sal_uInt16 >( pParDef->GetType() );
1122                             if( pParDef->IsByVal() )
1123                             {
1124                                 // Reset to avoid additional byval in call to wrapper function
1125                                 pParDef->SetByVal( false );
1126                                 nTyp |= 0x8000;
1127                             }
1128                             aGen.Gen( SbiOpcode::ARGTYP_, nTyp );
1129                         }
1130                     }
1131 
1132                     aGen.Gen( SbiOpcode::LIB_, aGblStrings.Add( pDef->GetLib() ) );
1133 
1134                     SbiOpcode eOp = pDef->IsCdecl() ? SbiOpcode::CALLC_ : SbiOpcode::CALL_;
1135                     sal_uInt16 nId = pDef->GetId();
1136                     if( !pDef->GetAlias().isEmpty() )
1137                     {
1138                         nId = ( nId & 0x8000 ) | aGblStrings.Add( pDef->GetAlias() );
1139                     }
1140                     if( nParCount > 1 )
1141                     {
1142                         nId |= 0x8000;
1143                     }
1144                     aGen.Gen( eOp, nId, sal::static_int_cast< sal_uInt16 >( eType ) );
1145 
1146                     if( bFunction )
1147                     {
1148                         aGen.Gen( SbiOpcode::PUT_ );
1149                     }
1150                     aGen.Gen( SbiOpcode::LEAVE_ );
1151                 }
1152             }
1153         }
1154     }
1155 }
1156 
1157 void SbiParser::Attribute()
1158 {
1159     // TODO: Need to implement the method as an attributed object.
1160     while( Next() != EQ )
1161     {
1162         if( Next() != DOT)
1163         {
1164             break;
1165         }
1166     }
1167 
1168     if( eCurTok != EQ )
1169     {
1170         Error( ERRCODE_BASIC_SYNTAX );
1171     }
1172     else
1173     {
1174         SbiExpression aValue( this );
1175     }
1176     // Don't generate any code - just discard it.
1177 }
1178 
1179 // Call of a SUB or a FUNCTION
1180 
1181 void SbiParser::Call()
1182 {
1183     SbiExpression aVar( this, SbSYMBOL );
1184     aVar.Gen( FORCE_CALL );
1185     aGen.Gen( SbiOpcode::GET_ );
1186 }
1187 
1188 // SUB/FUNCTION
1189 
1190 void SbiParser::SubFunc()
1191 {
1192     DefProc( false, false );
1193 }
1194 
1195 // Read in of a procedure
1196 
1197 void SbiParser::DefProc( bool bStatic, bool bPrivate )
1198 {
1199     sal_uInt16 l1 = nLine;
1200     bool bSub = ( eCurTok == SUB );
1201     bool bProperty = ( eCurTok == PROPERTY );
1202     PropertyMode ePropertyMode = PropertyMode::NONE;
1203     if( bProperty )
1204     {
1205         Next();
1206         if( eCurTok == GET )
1207         {
1208             ePropertyMode = PropertyMode::Get;
1209         }
1210         else if( eCurTok == LET )
1211         {
1212             ePropertyMode = PropertyMode::Let;
1213         }
1214         else if( eCurTok == SET )
1215         {
1216             ePropertyMode = PropertyMode::Set;
1217         }
1218         else
1219         {
1220             Error( ERRCODE_BASIC_EXPECTED, "Get or Let or Set" );
1221         }
1222     }
1223 
1224     SbiToken eExit = eCurTok;
1225     SbiProcDef* pDef = ProcDecl( false );
1226     if( !pDef )
1227     {
1228         return;
1229     }
1230     pDef->setPropertyMode( ePropertyMode );
1231 
1232     // Is the Proc already declared?
1233     SbiSymDef* pOld = aPublics.Find( pDef->GetName() );
1234     if( pOld )
1235     {
1236         pProc = pOld->GetProcDef();
1237         if( !pProc )
1238         {
1239             // Declared as a variable
1240             Error( ERRCODE_BASIC_BAD_DECLARATION, pDef->GetName() );
1241             delete pDef;
1242             return;
1243         }
1244         // #100027: Multiple declaration -> Error
1245         // #112787: Not for setup, REMOVE for 8
1246         else if( pProc->IsUsedForProcDecl() )
1247         {
1248             PropertyMode ePropMode = pDef->getPropertyMode();
1249             if( ePropMode == PropertyMode::NONE || ePropMode == pProc->getPropertyMode() )
1250             {
1251                 Error( ERRCODE_BASIC_PROC_DEFINED, pDef->GetName() );
1252                 delete pDef;
1253                 return;
1254             }
1255         }
1256 
1257         pDef->Match( pProc );
1258     }
1259     else
1260     {
1261         aPublics.Add( pDef );
1262     }
1263     assert(pDef);
1264     pProc = pDef;
1265     pProc->SetPublic( !bPrivate );
1266 
1267     // Now we set the search hierarchy for symbols as well as the
1268     // current procedure.
1269     aPublics.SetProcId( pProc->GetId() );
1270     pProc->GetParams().SetParent( &aPublics );
1271     if( bStatic )
1272     {
1273         if ( bVBASupportOn )
1274         {
1275             pProc->SetStatic();
1276         }
1277         else
1278         {
1279             Error( ERRCODE_BASIC_NOT_IMPLEMENTED ); // STATIC SUB ...
1280         }
1281     }
1282     else
1283     {
1284         pProc->SetStatic( false );
1285     }
1286     // Normal case: Local variable->parameter->global variable
1287     pProc->GetLocals().SetParent( &pProc->GetParams() );
1288     pPool = &pProc->GetLocals();
1289 
1290     pProc->Define();
1291     OpenBlock( eExit );
1292     StmntBlock( bSub ? ENDSUB : (bProperty ? ENDPROPERTY : ENDFUNC) );
1293     sal_uInt16 l2 = nLine;
1294     pProc->SetLine1( l1 );
1295     pProc->SetLine2( l2 );
1296     pPool = &aPublics;
1297     aPublics.SetProcId( 0 );
1298     // Open labels?
1299     pProc->GetLabels().CheckRefs();
1300     CloseBlock();
1301     aGen.Gen( SbiOpcode::LEAVE_ );
1302     pProc = nullptr;
1303 }
1304 
1305 // STATIC variable|procedure
1306 
1307 void SbiParser::Static()
1308 {
1309     DefStatic( false );
1310 }
1311 
1312 void SbiParser::DefStatic( bool bPrivate )
1313 {
1314     SbiSymPool* p;
1315 
1316     switch( Peek() )
1317     {
1318     case SUB:
1319     case FUNCTION:
1320     case PROPERTY:
1321         // End global chain if necessary (not done in
1322         // SbiParser::Parse() under these conditions
1323         if( bNewGblDefs && nGblChain == 0 )
1324         {
1325             nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
1326             bNewGblDefs = false;
1327         }
1328         Next();
1329         DefProc( true, bPrivate );
1330         break;
1331     default:
1332         if( !pProc )
1333         {
1334             Error( ERRCODE_BASIC_NOT_IN_SUBR );
1335         }
1336         // Reset the Pool, so that STATIC-Declarations go into the
1337         // global Pool
1338         p = pPool;
1339         pPool = &aPublics;
1340         DefVar( SbiOpcode::STATIC_, true );
1341         pPool = p;
1342         break;
1343     }
1344 }
1345 
1346 bool SbiParser::IsUnoInterface(const OUString& sTypeName)
1347 {
1348     try
1349     {
1350         return css::reflection::theCoreReflection::get(
1351             comphelper::getProcessComponentContext())->forName(sTypeName).is();
1352     }
1353     catch(const Exception&)
1354     {
1355         OSL_FAIL("Could not create reflection.CoreReflection.");
1356     }
1357     return false;
1358 }
1359 
1360 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1361