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 
21 #include <memory>
22 #include <parser.hxx>
23 
24 #include <osl/diagnose.h>
25 
26 #include <stdio.h>
27 #include <rtl/character.hxx>
28 #include <basic/sberrors.hxx>
29 
30 // All symbol names are laid down int the symbol-pool's stringpool, so that
31 // all symbols are handled in the same case. On saving the code-image, the
32 // global stringpool with the respective symbols is also saved.
33 // The local stringpool holds all the symbols that don't move to the image
34 // (labels, constant names etc.).
35 
SbiStringPool()36 SbiStringPool::SbiStringPool( )
37 {}
38 
~SbiStringPool()39 SbiStringPool::~SbiStringPool()
40 {}
41 
Find(sal_uInt32 n) const42 OUString SbiStringPool::Find( sal_uInt32 n ) const
43 {
44     if( n == 0 || n > aData.size() )
45         return OUString();
46     else
47         return aData[n - 1];
48 }
49 
Add(const OUString & rVal)50 short SbiStringPool::Add( const OUString& rVal )
51 {
52     sal_uInt32 n = aData.size();
53     for( sal_uInt32 i = 0; i < n; ++i )
54     {
55         OUString& p = aData[i];
56         if( p == rVal )
57             return i+1;
58     }
59 
60     aData.push_back(rVal);
61     return static_cast<short>(++n);
62 }
63 
Add(double n,SbxDataType t)64 short SbiStringPool::Add( double n, SbxDataType t )
65 {
66     char buf[40]{};
67     switch( t )
68     {
69         // tdf#142460 - properly handle boolean values in string pool
70         case SbxBOOL: snprintf( buf, sizeof(buf), "%db", static_cast<short>(n) ); break;
71         // tdf#131296 - store numeric value including its type character
72         // See GetSuffixType in basic/source/comp/scanner.cxx for type characters
73         case SbxINTEGER: snprintf( buf, sizeof(buf), "%d%%", static_cast<short>(n) ); break;
74         case SbxLONG:
75             snprintf( buf, sizeof(buf), "%" SAL_PRIdINT32 "&", static_cast<sal_Int32>(n) ); break;
76         case SbxSINGLE:  snprintf( buf, sizeof(buf), "%.6g!", static_cast<float>(n) ); break;
77         case SbxDOUBLE:  snprintf( buf, sizeof(buf), "%.16g", n ); break; // default processing in SbiRuntime::StepLOADNC - no type character
78         case SbxCURRENCY: snprintf(buf, sizeof(buf), "%.16g@", n); break;
79         default: assert(false); break; // should not happen
80     }
81     return Add( OUString::createFromAscii( buf ) );
82 }
83 
SbiSymPool(SbiStringPool & r,SbiSymScope s,SbiParser * pP)84 SbiSymPool::SbiSymPool( SbiStringPool& r, SbiSymScope s, SbiParser* pP ) :
85     rStrings(r),
86     pParent(nullptr),
87     pParser(pP),
88     eScope(s),
89     nProcId(0),
90     nCur(0)
91 {
92 }
93 
~SbiSymPool()94 SbiSymPool::~SbiSymPool()
95 {}
96 
97 
First()98 SbiSymDef* SbiSymPool::First()
99 {
100     nCur = sal_uInt16(-1);
101     return Next();
102 }
103 
Next()104 SbiSymDef* SbiSymPool::Next()
105 {
106     if (m_Data.size() <= ++nCur)
107         return nullptr;
108     else
109         return m_Data[ nCur ].get();
110 }
111 
112 
AddSym(const OUString & rName)113 SbiSymDef* SbiSymPool::AddSym( const OUString& rName )
114 {
115     SbiSymDef* p = new SbiSymDef( rName );
116     p->nPos    = m_Data.size();
117     p->nId     = rStrings.Add( rName );
118     p->nProcId = nProcId;
119     p->pIn     = this;
120     m_Data.insert( m_Data.begin() + p->nPos, std::unique_ptr<SbiSymDef>(p) );
121     return p;
122 }
123 
AddProc(const OUString & rName)124 SbiProcDef* SbiSymPool::AddProc( const OUString& rName )
125 {
126     SbiProcDef* p = new SbiProcDef( pParser, rName );
127     p->nPos    = m_Data.size();
128     p->nId     = rStrings.Add( rName );
129     // procs are always local
130     p->nProcId = 0;
131     p->pIn     = this;
132     m_Data.insert( m_Data.begin() + p->nPos, std::unique_ptr<SbiProcDef>(p) );
133     return p;
134 }
135 
136 // adding an externally constructed symbol definition
137 
Add(SbiSymDef * pDef)138 void SbiSymPool::Add( SbiSymDef* pDef )
139 {
140     if( !(pDef && pDef->pIn != this) )
141         return;
142 
143     if( pDef->pIn )
144     {
145 #ifdef DBG_UTIL
146 
147         pParser->Error( ERRCODE_BASIC_INTERNAL_ERROR, "Dbl Pool" );
148 #endif
149         return;
150     }
151 
152     pDef->nPos = m_Data.size();
153     if( !pDef->nId )
154     {
155         // A unique name must be created in the string pool
156         // for static variables (Form ProcName:VarName)
157         OUString aName( pDef->aName );
158         if( pDef->IsStatic() )
159         {
160             aName = pParser->aGblStrings.Find( nProcId )
161                   + ":"
162                   + pDef->aName;
163         }
164         pDef->nId = rStrings.Add( aName );
165     }
166 
167     if( !pDef->GetProcDef() )
168     {
169         pDef->nProcId = nProcId;
170     }
171     pDef->pIn = this;
172     m_Data.insert( m_Data.begin() + pDef->nPos, std::unique_ptr<SbiSymDef>(pDef) );
173 }
174 
175 
Find(const OUString & rName,bool bSearchInParents)176 SbiSymDef* SbiSymPool::Find( const OUString& rName, bool bSearchInParents )
177 {
178     sal_uInt16 nCount = m_Data.size();
179     for( sal_uInt16 i = 0; i < nCount; i++ )
180     {
181         SbiSymDef &r = *m_Data[ nCount - i - 1 ];
182         if( ( !r.nProcId || ( r.nProcId == nProcId)) &&
183             ( r.aName.equalsIgnoreAsciiCase(rName)))
184         {
185             return &r;
186         }
187     }
188     if( bSearchInParents && pParent )
189     {
190         return pParent->Find( rName );
191     }
192     else
193     {
194         return nullptr;
195     }
196 }
197 
198 
199 // find via position (from 0)
200 
Get(sal_uInt16 n)201 SbiSymDef* SbiSymPool::Get( sal_uInt16 n )
202 {
203     if (m_Data.size() <= n)
204     {
205         return nullptr;
206     }
207     else
208     {
209         return m_Data[ n ].get();
210     }
211 }
212 
Define(const OUString & rName)213 sal_uInt32 SbiSymPool::Define( const OUString& rName )
214 {
215     SbiSymDef* p = Find( rName );
216     if( p )
217     {
218         if( p->IsDefined() )
219         {
220             pParser->Error( ERRCODE_BASIC_LABEL_DEFINED, rName );
221         }
222     }
223     else
224     {
225         p = AddSym( rName );
226     }
227     return p->Define();
228 }
229 
Reference(const OUString & rName)230 sal_uInt32 SbiSymPool::Reference( const OUString& rName )
231 {
232     SbiSymDef* p = Find( rName );
233     if( !p )
234     {
235         p = AddSym( rName );
236     }
237     // to be sure
238     pParser->aGen.GenStmnt();
239     return p->Reference();
240 }
241 
242 
CheckRefs()243 void SbiSymPool::CheckRefs()
244 {
245     for (std::unique_ptr<SbiSymDef> & r : m_Data)
246     {
247         if( !r->IsDefined() )
248         {
249             pParser->Error( ERRCODE_BASIC_UNDEF_LABEL, r->GetName() );
250         }
251     }
252 }
253 
SbiSymDef(const OUString & rName)254 SbiSymDef::SbiSymDef( const OUString& rName ) :
255     aName(rName),
256     eType(SbxEMPTY),
257     pIn(nullptr),
258     nLen(0),
259     nDims(0),
260     nId(0),
261     nTypeId(0),
262     nProcId(0),
263     nPos(0),
264     nChain(0),
265     bNew(false),
266     bChained(false),
267     bByVal(false),
268     bOpt(false),
269     bStatic(false),
270     bAs(false),
271     bGlobal(false),
272     bParamArray(false),
273     bWithEvents(false),
274     bWithBrackets(false),
275     nDefaultId(0),
276     nFixedStringLength(-1)
277 {
278 }
279 
~SbiSymDef()280 SbiSymDef::~SbiSymDef()
281 {
282 }
283 
GetProcDef()284 SbiProcDef* SbiSymDef::GetProcDef()
285 {
286     return nullptr;
287 }
288 
GetConstDef()289 SbiConstDef* SbiSymDef::GetConstDef()
290 {
291     return nullptr;
292 }
293 
294 
GetName()295 const OUString& SbiSymDef::GetName()
296 {
297     if( pIn )
298     {
299         aName = pIn->rStrings.Find( nId );
300     }
301     return aName;
302 }
303 
304 
SetType(SbxDataType t)305 void SbiSymDef::SetType( SbxDataType t )
306 {
307     if( t == SbxVARIANT && pIn )
308     {
309         //See if there have been any deftype statements to set the default type
310         //of a variable based on its starting letter
311         sal_Unicode cu = aName[0];
312         if( cu < 256 )
313         {
314             unsigned char ch = static_cast<unsigned char>(cu);
315             if( ch == '_' )
316             {
317                 ch = 'Z';
318             }
319             int ch2 = rtl::toAsciiUpperCase( ch );
320             int nIndex = ch2 - 'A';
321             if (nIndex >= 0 && nIndex < N_DEF_TYPES)
322                 t = pIn->pParser->eDefTypes[nIndex];
323         }
324     }
325     eType = t;
326 }
327 
328 // construct a backchain, if not yet defined
329 // the value that shall be stored as an operand is returned
330 
Reference()331 sal_uInt32 SbiSymDef::Reference()
332 {
333     if( !bChained )
334     {
335         sal_uInt32 n = nChain;
336         nChain = pIn->pParser->aGen.GetOffset();
337         return n;
338     }
339     else return nChain;
340 }
341 
342 
Define()343 sal_uInt32 SbiSymDef::Define()
344 {
345     sal_uInt32 n = pIn->pParser->aGen.GetPC();
346     pIn->pParser->aGen.GenStmnt();
347     if( nChain )
348     {
349         pIn->pParser->aGen.BackChain( nChain );
350     }
351     nChain = n;
352     bChained = true;
353     return nChain;
354 }
355 
356 // A symbol definition may have its own pool. This is the case
357 // for objects and procedures (local variable)
358 
GetPool()359 SbiSymPool& SbiSymDef::GetPool()
360 {
361     if( !pPool )
362     {
363         pPool = std::make_unique<SbiSymPool>( pIn->pParser->aGblStrings, SbLOCAL, pIn->pParser );// is dumped
364     }
365     return *pPool;
366 }
367 
GetScope() const368 SbiSymScope SbiSymDef::GetScope() const
369 {
370     return pIn ? pIn->GetScope() : SbLOCAL;
371 }
372 
373 
374 // The procedure definition has three pools:
375 // 1) aParams: is filled by the definition. Contains the
376 //    parameters' names, like they're used inside the body.
377 //    The first element is the return value.
378 // 2) pPool: all local variables
379 // 3) aLabels: labels
380 
SbiProcDef(SbiParser * pParser,const OUString & rName,bool bProcDecl)381 SbiProcDef::SbiProcDef( SbiParser* pParser, const OUString& rName,
382                         bool bProcDecl )
383          : SbiSymDef( rName )
384          , aParams( pParser->aGblStrings, SbPARAM, pParser )  // is dumped
385          , aLabels( pParser->aLclStrings, SbLOCAL, pParser )  // is not dumped
386          , mbProcDecl( bProcDecl )
387 {
388     aParams.SetParent( &pParser->aPublics );
389     pPool = std::make_unique<SbiSymPool>( pParser->aGblStrings, SbLOCAL, pParser );
390     pPool->SetParent( &aParams );
391     nLine1  =
392     nLine2  = 0;
393     mePropMode = PropertyMode::NONE;
394     bPublic = true;
395     bCdecl  = false;
396     bStatic = false;
397     // For return values the first element of the parameter
398     // list is always defined with name and type of the proc
399     aParams.AddSym( aName );
400 }
401 
~SbiProcDef()402 SbiProcDef::~SbiProcDef()
403 {}
404 
GetProcDef()405 SbiProcDef* SbiProcDef::GetProcDef()
406 {
407     return this;
408 }
409 
SetType(SbxDataType t)410 void SbiProcDef::SetType( SbxDataType t )
411 {
412     SbiSymDef::SetType( t );
413     aParams.Get( 0 )->SetType( eType );
414 }
415 
416 // match with a forward-declaration
417 // if the match is OK, pOld is replaced by this in the pool
418 // pOld is deleted in any case!
419 
Match(SbiProcDef * pOld)420 void SbiProcDef::Match( SbiProcDef* pOld )
421 {
422     SbiSymDef *pn=nullptr;
423     // parameter 0 is the function name
424     sal_uInt16 i;
425     for( i = 1; i < aParams.GetSize(); i++ )
426     {
427         SbiSymDef* po = pOld->aParams.Get( i );
428         pn = aParams.Get( i );
429         // no type matching - that is done during running
430         // but is it maybe called with too little parameters?
431         if( !po && !pn->IsOptional() && !pn->IsParamArray() )
432         {
433             break;
434         }
435         pOld->aParams.Next();
436     }
437 
438     if( pn && i < aParams.GetSize() && pOld->pIn )
439     {
440         // mark the whole line
441         pOld->pIn->GetParser()->SetCol1( 0 );
442         pOld->pIn->GetParser()->Error( ERRCODE_BASIC_BAD_DECLARATION, aName );
443     }
444 
445     if( !pIn && pOld->pIn )
446     {
447         // Replace old entry with the new one
448         nPos = pOld->nPos;
449         nId  = pOld->nId;
450         pIn  = pOld->pIn;
451 
452         // don't delete pOld twice, if it's stored in m_Data
453         if (pOld == pIn->m_Data[nPos].get())
454             pOld = nullptr;
455         pIn->m_Data[nPos].reset(this);
456     }
457     delete pOld;
458 }
459 
setPropertyMode(PropertyMode ePropMode)460 void SbiProcDef::setPropertyMode( PropertyMode ePropMode )
461 {
462     mePropMode = ePropMode;
463     if( mePropMode == PropertyMode::NONE )
464         return;
465 
466     // Prop name = original scanned procedure name
467     maPropName = aName;
468 
469     // CompleteProcName includes "Property xxx "
470     // to avoid conflicts with other symbols
471     OUString aCompleteProcName = "Property ";
472     switch( mePropMode )
473     {
474     case PropertyMode::Get:  aCompleteProcName += "Get "; break;
475     case PropertyMode::Let:  aCompleteProcName += "Let "; break;
476     case PropertyMode::Set:  aCompleteProcName += "Set "; break;
477     case PropertyMode::NONE: OSL_FAIL( "Illegal PropertyMode PropertyMode::NONE" ); break;
478     }
479     aCompleteProcName += aName;
480     aName = aCompleteProcName;
481 }
482 
483 
SbiConstDef(const OUString & rName)484 SbiConstDef::SbiConstDef( const OUString& rName )
485            : SbiSymDef( rName )
486 {
487     nVal = 0; eType = SbxINTEGER;
488 }
489 
Set(double n,SbxDataType t)490 void SbiConstDef::Set( double n, SbxDataType t )
491 {
492     aVal.clear(); nVal = n; eType = t;
493 }
494 
Set(const OUString & n)495 void SbiConstDef::Set( const OUString& n )
496 {
497     aVal = n; nVal = 0; eType = SbxSTRING;
498 }
499 
~SbiConstDef()500 SbiConstDef::~SbiConstDef()
501 {}
502 
GetConstDef()503 SbiConstDef* SbiConstDef::GetConstDef()
504 {
505     return this;
506 }
507 
508 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
509