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