1 /*
2  * (C) Copyright 2001-2015 Diomidis Spinellis
3  *
4  * This file is part of CScout.
5  *
6  * CScout is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * CScout is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with CScout.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *
20  * For documentation read the corresponding .h file
21  *
22  */
23 
24 #include <map>
25 #include <string>
26 #include <deque>
27 #include <vector>
28 #include <stack>
29 #include <iterator>
30 #include <fstream>
31 #include <iostream>
32 #include <list>
33 #include <set>
34 
35 #include "cpp.h"
36 #include "debug.h"
37 #include "error.h"
38 #include "attr.h"
39 #include "metrics.h"
40 #include "fileid.h"
41 #include "tokid.h"
42 #include "token.h"
43 #include "parse.tab.h"
44 #include "ptoken.h"
45 #include "fchar.h"
46 #include "pltoken.h"
47 #include "macro.h"
48 #include "pdtoken.h"
49 #include "ctoken.h"
50 #include "type.h"
51 #include "stab.h"
52 #include "fdep.h"
53 #include "call.h"
54 #include "fcall.h"
55 #include "mcall.h"
56 #include "globobj.h"
57 #include "ctag.h"
58 
59 
60 int Block::current_block = -1;
61 vectorBlock Block::scope_block;
62 Stab Function::label;
63 Block Block::param_block;	// Function parameter declarations
64 bool Block::use_param;		// Declare in param_block when true
65 
Id(const Token & tok,Type typ,FCall * fc,GlobObj * go)66 Id::Id(const Token& tok, Type typ, FCall *fc, GlobObj *go) :
67 	token(tok), type(typ), fcall(fc), glob(go)
68 {
69 }
70 
71 // Called when entering a scope
72 void
enter()73 Block::enter()
74 {
75 	scope_block.push_back(Block());
76 	current_block++;
77 }
78 
79 // Called when entering a function block statement
80 void
param_enter()81 Block::param_enter()
82 {
83 	if (DP())
84 		cout << "On param_enter " << param_block.obj << "\n";
85 	scope_block.push_back(param_block);
86 	current_block++;
87 	use_param = false;
88 }
89 
90 void
exit()91 Block::exit()
92 {
93 	scope_block.pop_back();
94 	current_block--;
95 }
96 
97 // Called when exiting a function parameter list
98 void
param_exit()99 Block::param_exit()
100 {
101 	// Do not clobber parameter block when exiting function pointer
102 	// arguments appearing in old-style argument declarations
103 	if (!use_param)
104 		param_block = scope_block.back();
105 	scope_block.pop_back();
106 	current_block--;
107 	if (DP())
108 		cout << "On param_exit " << param_block.obj << "\n";
109 }
110 
111 /*
112  * Define name to be the identifier id
113  */
114 void
define(Stab Block::* table,const Token & tok,const Type & typ,FCall * fc,GlobObj * go)115 Block::define(Stab Block::*table, const Token& tok, const Type& typ, FCall *fc, GlobObj *go)
116 {
117 	(scope_block[current_block].*table).define(tok, typ, fc, go);
118 }
119 
120 // Called when exiting a function block statement
121 void
param_clear(void)122 Block::param_clear(void)
123 {
124 	param_block.obj.clear();
125 	param_block.tag.clear();
126 }
127 
128 void
clear()129 Block::clear()
130 {
131 	param_clear();
132 	scope_block.clear();
133 	current_block = -1;
134 }
135 
136 /*
137  * Define the tok object to be of type typ
138  */
139 void
obj_define(const Token & tok,Type typ)140 obj_define(const Token& tok, Type typ)
141 {
142 	tok.set_ec_attribute(is_ordinary);
143 	enum e_storage_class sc = typ.get_storage_class();
144 	Id const * id;
145 
146 	if (DP())
147 		cout << "Define object [" << tok.get_name() << "]: " << typ << "\n";
148 	if (Block::use_param && Block::current_block == Block::cu_block) {
149 		// Old-style function definition declarations
150 		// No checking
151 		if ((id = Block::param_block.obj.lookup(tok.get_name())))
152 			Token::unify(id->get_token(), tok);
153 		else
154 			/*
155 			 * @error
156 			 * While processing an old-style (K&amp;R) parameter
157 			 * declaration, a declared parameter did not match
158 			 * any of the parameters appearing in the function's
159 			 * arguments definition
160 			 */
161 			Error::error(E_ERR, "declared parameter does not appear in old-style function parameter list: " + tok.get_name());
162 		(Block::param_block.obj).define(tok, typ);
163 		return;
164 	}
165 	if (sc == c_unspecified && typ.is_cfunction())
166 		// If the declaration of an identifier for a function has no
167 		// storage-class specifier, its linkage is determined exactly
168 		// as if it were declared with the storage class specifier
169 		// extern 6.2.2-5
170 		sc = c_extern;
171 	if (sc == c_extern && (id = obj_lookup(tok.get_name()))) {
172 		// If the declaration of an identifier contains extern the identifier
173 		// has the same linkage as the prior visible declaration of the identifier
174 		// 6.2.2-4
175 		enum e_storage_class sc2 = id->get_type().get_storage_class();
176 		if (sc2 != sc) {
177 			typ.set_storage_class(basic(b_abstract, s_none, sc2));
178 			sc = sc2;
179 		}
180 	}
181 	if (DP())
182 		cout << "Type of " << tok << " is " << typ << (typ.is_typedef() ? " (typedef)\n" : "\n");
183 	if (Block::get_scope_level() == Block::cu_block) {
184 		// Function *definitions* are added from FCall::set_current_fun
185 		// We don't add extern variables
186 		if (!typ.is_cfunction() && sc != c_extern)
187 			CTag::add(tok, typ, sc);
188 
189 		// Special rules for definitions at file scope
190 		// ANSI 3.1.2.2 p. 22
191 		switch (sc) {
192 		case c_static:
193 			tok.set_ec_attribute(is_cscope);
194 			if ((id = Block::scope_block[Block::cu_block].obj.lookup(tok.get_name()))) {
195 				if (id->get_type().get_storage_class() == c_unspecified)
196 					/*
197 					 * @error
198 					 * An identifier is declared twice
199 					 * with compilation or linkage
200 					 * unit scope with conflicting
201 					 * declarations
202 					 */
203 					Error::error(E_ERR, "conflicting declarations for identifier " + id->get_name());
204 				Token::unify(id->get_token(), tok);
205 			}
206 			if (!typ.is_cfunction())
207 				Fchar::get_fileid().metrics().add_fvar();
208 			break;
209 		case c_typedef:
210 			tok.set_ec_attribute(is_cscope);
211 			tok.set_ec_attribute(is_typedef);
212 			break;
213 		case c_enum:
214 			tok.set_ec_attribute(is_cscope);
215 			tok.set_ec_attribute(is_enumeration);
216 			break;
217 		default:
218 			if (!typ.is_cfunction())
219 				Fchar::get_fileid().metrics().add_pvar();
220 			tok.set_ec_attribute(is_lscope);
221 			break;
222 		}
223 		// A definition contributing data to the current CU
224 		if (sc != c_extern && sc != c_typedef && sc != c_enum && !typ.is_cfunction()) {
225 			if (DP())
226 				Error::error(E_DEBUG, "Add provider through obj_define");
227 			Fdep::add_provider(Fchar::get_fileid());
228 		}
229 	} else {
230 		// Definitions at function block scope
231 		if (sc != c_extern &&
232 		    Block::scope_block[Block::current_block].obj.lookup(tok.get_name())) {
233 			/*
234 			 * @error
235 			 * An identifier is declared twice within the
236 			 * same block
237 			 */
238 			Error::error(E_ERR, "Duplicate definition of identifier " + tok.get_name());
239 			return;
240 		}
241 	}
242 	// Locate/create the appropriate FCall object
243 	FCall *fc = NULL;
244 	if (typ.is_cfunction() && !typ.is_typedef()) {
245 		if (DP())
246 			cout << "Looking for function " << tok.get_name() << '\n';
247 		tok.set_ec_attribute(is_cfunction);
248 		if (sc == c_extern || (sc == c_unspecified && Block::current_block == Block::cu_block)) {
249 			// Extern linkage: get it from the lu block which we do not normaly search
250 			if ((id = Block::scope_block[Block::lu_block].obj.lookup(tok.get_name())) != NULL)
251 				fc = id->get_fcall();
252 		} else {
253 			// Static linkage: get it from the normal blocks
254 			if ((id = obj_lookup(tok.get_name())) != NULL)
255 				fc = id->get_fcall();
256 		}
257 		// Try to match the function against one in another project
258 		if (!fc) {
259 			Token utok(tok.unique());	// To handle identical files
260 			fc = dynamic_cast<FCall *>(Call::get_call(utok));
261 			if (!fc) {
262 				if (DP())
263 					cout << "Creating new call\n";
264 				fc = new FCall(utok, typ, tok.get_name());
265 			}
266 		}
267 		fc->metrics().set_metric(FunMetrics::em_nparam, typ.get_nparam());
268 	}
269 
270 	static Stab Block::*objptr = &Block::obj;
271 	Block::define(objptr, tok, typ, fc);
272 	/*
273 	 * Identifiers with extern scope are also added to the linkage unit
274 	 * definitions.  These definitions are not searched, for locating objects,
275 	 * but are used for unification of objects, simulating the linking phase.
276 	 */
277 	if (sc == c_extern || (sc == c_unspecified && Block::current_block == Block::cu_block)) {
278 		GlobObj *go = NULL;
279 		if ((id = Block::scope_block[Block::lu_block].obj.lookup(tok.get_name())) != NULL) {
280 			Token::unify(id->get_token(), tok);
281 			go = id->get_glob();
282 		} else {
283 			/*
284 			 * Create all the GlobObj elements we track at their declaration/definition point,
285 			 * so that id is correctly initialized with one.  However, we add a def, only if it
286 			 * is a definition of a global variable, rather than a declaration.
287 			 */
288 			if (!typ.is_cfunction()) {
289 				if (DP())
290 					cout << "Define global variable identifier " << tok.get_name() << endl;
291 				// Try to match the global against one in another project
292 				Token utok(tok.unique());	// To handle identical files
293 				go = GlobObj::get_glob(utok);
294 				if (!go) {
295 					if (DP())
296 						cout << "Creating new glob\n";
297 					go = new GlobObj(utok, typ, tok.get_name());
298 				}
299 			}
300 			id = Block::scope_block[Block::lu_block].obj.define(tok, typ, fc, go);
301 		}
302 		/*
303 		 * We test go, because it might be null if the object is defined as a function in one
304 		 * and as a variable in another.
305 		 */
306 		if (go && !typ.is_cfunction() && sc == c_unspecified && Block::current_block == Block::cu_block)
307 			go->add_def(Fchar::get_fileid());
308 	}
309 }
310 
311 /*
312  * Define the tok struct/union/enum tag to be of type typ
313  */
314 void
tag_define(const Token & tok,const Type & typ)315 tag_define(const Token& tok, const Type& typ)
316 {
317 	// Store CTags info
318 	CTag::add(tok, typ);
319 	// For structures and unions (but not for enums) store their members
320 	if (typ.is_su() && !typ.is_incomplete()) {
321 		const vector <Id>& members = typ.get_members_by_ordinal();
322 		for (vector <Id>::const_iterator i = members.begin(); i != members.end(); i++)
323 			if (i->get_token().non_empty())
324 				CTag::add(i->get_token(), typ.ctags_kind(), tok.get_name());
325 	}
326 
327 	// Update symbol table
328 	tok.set_ec_attribute(is_suetag);
329 	static Stab Block::*tagptr = &Block::tag;
330 	const Id *id;
331 
332 	if (DP())
333 		cout << "Define tag [" << tok.get_name() << "]: " << typ << "\n";
334 	if (Block::use_param && Block::current_block == Block::cu_block)
335 		(Block::param_block.tag).define(tok, typ);
336 	else if ((id = Block::scope_block[Block::current_block].tag.lookup(tok.get_name())) &&
337 		 !id->get_type().is_incomplete())
338 		/*
339 		 * @error
340 		 * A structure, union, or enumeration tag was defined
341 		 * twice for the same entity
342 		 */
343 		Error::error(E_ERR, "Duplicate definition of tag  " + tok.get_name());
344 	else
345 		Block::define(tagptr, tok, typ);
346 }
347 
348 
349 /*
350  * Lookup in the block pointed by table (obj or tag) for name
351  * and return the relevant identifier or NULL if not defined
352  * and the definition's scope level.
353  */
354 pair <Id const *, int>
lookup(const Stab Block::* table,const string & name)355 Block::lookup(const Stab Block::*table, const string& name)
356 {
357 	Id const * id;
358 
359 	for (int i = current_block; i != lu_block; i--)
360 		if ((id = (scope_block[i].*table).lookup(name)))
361 			return pair <Id const *, int>(id, i);
362 	return pair <Id const *, int>(NULL, 0);
363 }
364 
365 Id const *
lookup(const string & s) const366 Stab::lookup(const string& s) const
367 {
368 	map<string, Id>::const_iterator i;
369 
370 	 i = m.find(s);
371 	 if (i == m.end())
372 	 	return (NULL);
373 	else
374 		return &((*i).second);
375 }
376 
377 
378 Id const *
obj_lookup(const string & name)379 obj_lookup(const string& name)
380 {
381 	static Stab Block::*objptr = &Block::obj;
382 	pair <Id const *, int> r = Block::lookup(objptr, name);
383 	Id const *id = r.first;
384 	if (id) {
385 		enum e_storage_class sc = r.first->get_type().get_storage_class();
386 		if (!r.first->get_type().is_cfunction() &&
387 		    (sc == c_extern || (sc == c_unspecified && r.second == Block::cu_block))) {
388 			if (DP())
389 				cout << "Lookup global variable identifier " << name << endl;
390 			GlobObj *go = id->get_glob();
391 			// Try to match the global against one in another project
392 			if (!go) {
393 				Token utok(id->get_token().unique());	// To handle identical files
394 				go = GlobObj::get_glob(utok);
395 				if (!go) {
396 					if (DP())
397 						cout << "Creating new glob\n";
398 					go = new GlobObj(utok, id->get_type(), name);
399 				}
400 			}
401 			go->add_ref(Fchar::get_fileid());
402 		}
403 	}
404 	return id;
405 }
406 
407 
408 Id *
define(const Token & tok,const Type & typ,FCall * fc,GlobObj * go)409 Stab::define(const Token& tok, const Type& typ, FCall *fc, GlobObj *go)
410 {
411 	Stab_element::key_type key(tok.get_name());
412 	Stab_element::mapped_type data(Id(tok, typ, fc, go));
413 	/*
414 	 * Efficient implementation of
415 	 * m[tok.get_name()] = Id(tok, typ, fc, go);
416 	 * return (Id *)Stab::lookup(tok.get_name());
417 	 * See Meyers Effective STL, Item 24.
418 	 */
419 	Stab_element::iterator i = m.lower_bound(key);
420 	if (i != m.end() && i->first == key) {
421 		i->second = data;
422 		return &(i->second);
423 	} else
424 		return &(m.insert(i, Stab_element::value_type(key, data))->second);
425 }
426 
427 /*
428  * Define a local label (gcc extension)
429  */
430 void
local_label_define(const Token & tok)431 local_label_define(const Token& tok)
432 {
433 	tok.set_ec_attribute(is_label);
434 	static Stab Block::*llptr = &Block::local_label;
435 	const Id *id;
436 
437 	if (DP())
438 		cout << "Define local label [" << tok.get_name() << "\n";
439 	if ((id = Block::scope_block[Block::current_block].local_label.lookup(tok.get_name())))
440 		/*
441 		 * @error
442 		 * A local label was defined more than once in the same block
443 		 */
444 		Error::error(E_ERR, "Duplicate local label definition " + tok.get_name());
445 	else
446 		Block::define(llptr, tok, Type());
447 }
448 
449 /*
450  * Define tok as a label in the current function.
451  * If it is already defined, unify it with the previous definition.
452  * Locally defined labels take precedence
453  */
454 void
label_define(const Token & tok)455 label_define(const Token& tok)
456 {
457 	tok.set_ec_attribute(is_label);
458 	bool is_local;
459 	static Stab Block::*llptr = &Block::local_label;
460 
461 	Id const *id;
462 	// Search first for local, then for function label
463 	if ((id = local_label_lookup(tok.get_name())))
464 		is_local = true;
465 	else {
466 		id = Function::label.lookup(tok.get_name());
467 		is_local = false;
468 	}
469 	if (id) {
470 		if (id->get_type().is_valid())
471 			/*
472 			 * @error
473 			 * The same <code>goto</code> label is defined more
474 			 * than once in a given function
475 			 */
476 			Error::error(E_ERR, "label " + tok.get_name() + " already defined");
477 		Token::unify(id->get_token(), tok);
478 	}
479 	if (is_local)
480 		Block::define(llptr, tok, label());
481 	else
482 		Function::label.define(tok, label());
483 }
484 
485 /*
486  * Use tok as a label in the current function (with goto).
487  * If it is already defined, unify it with the previous definition.
488  * Local labels take precedence in searching
489  */
490 void
label_use(const Token & tok)491 label_use(const Token& tok)
492 {
493 	Id const *id;
494 	if ((id = local_label_lookup(tok.get_name())) == NULL)
495 		id = Function::label.lookup(tok.get_name());
496 	if (id)
497 		Token::unify(id->get_token(), tok);
498 	else
499 		Function::label.define(tok, Type());
500 }
501 
502 // Called at the end of a function definition
503 void
exit()504 Function::exit()
505 {
506 	Stab_element::const_iterator i;
507 
508 	for (i = label.begin(); i != label.end(); i++)
509 		if (!Stab::get_id(i).get_type().is_valid())
510 			/*
511 			 * @error
512 			 * A <code>goto</code>
513 			 * label used within a function was never defined
514 			 */
515 			Error::error(E_ERR, "undefined label " + Stab::get_name(i));
516 	label.clear();
517 }
518 
519 ostream&
operator <<(ostream & o,const Stab & s)520 operator<<(ostream& o,const Stab &s)
521 {
522 	Stab_element::const_iterator i;
523 
524 	o << "{";
525 	for (i = s.m.begin(); i != s.m.end(); i++)
526 		o << (*i).first << ": " << ((*i).second.get_type()) << "\n";
527 	o << "} ";
528 	return o;
529 }
530