/*
 * Copyright (c) 2003 - 2007, Nils R. Weller
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Functions to create, populate and search scopes with and for
 * structure and enum definitions and variable declarations
 */ 
#include "scope.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "token.h"
#include "decl.h"
#include "misc.h"
#include "expr.h"
#include "error.h"
#include "type.h"
#include "symlist.h"
#include "functions.h"
#include "debug.h"
#include "n_libc.h"

struct scope global_scope = {
	0,
	NULL,
	0,
	{ NULL, NULL, 0, 0 },
	{ 0, 0, 0 },
	{ 0, 0, 0 },
	{ 0, 0, 0 },
	{ 0, 0, 0 },
	{ 0, 0, 0 },
	{ 0, 0, 0 },
	{ 0, 0, 0 },
	NULL, NULL,
	{ NULL, NULL, 0, 0 },
	{ NULL, NULL, 0, 0 },
	NULL, NULL, NULL
};
struct scope	*curscope;
static struct scope	*scopelist_tail = &global_scope;
struct decl		*static_init_vars;
struct decl		*siv_tail;
struct decl		*static_uninit_vars;
struct decl		*siuv_tail;
struct sym_entry	*extern_vars;
struct sym_entry	*extern_vars_tail;

/*
 * Looks up structure definition (not instance!) with tag ``tag''. If
 * ``nested'' is nonzero, the lookup will use scopes above the current
 * scope. If it's not, the lookup will only consider the current scope
 * On success, a pointer to the structure definition is returned, else a
 * null pointer
 */
struct ty_struct *
lookup_struct(struct scope *s, const char *tag, int nested) {
	do {
		struct ty_struct	*ts;

		for (ts = s->struct_defs.head; ts != NULL; ts = ts->next) {
			if (ts->tag != NULL
				&& strcmp(ts->tag, tag) == 0) {
				return ts;
			}
		}
		if (nested == 0) {
			break;
		}
	} while ((s = s->parent) != NULL);

	return NULL;
}

struct ty_enum *
lookup_enum(struct scope *s, const char *tag, int nested) {
	int				i;
	char			*p;

	do {
		if (s->enum_defs.data) {
			for (i = 0; i < s->enum_defs.ndecls; ++i) {
				p = s->enum_defs.data[i]->tag;
				if (p != NULL && strcmp(p, tag) == 0) {
					return s->enum_defs.data[i];
				}
			}
		}
		if (nested == 0) {
			break;
		}
	} while ((s = s->parent) != NULL);
	return NULL;
}

struct type *
lookup_typedef(struct scope *s, const char *name) {
	struct decl	**d;
	int		i;
	size_t		namelen = strlen(name);

	do {
		if (s->typedef_hash.used) {
			struct sym_entry	*se;

#if 0
			se = lookup_hash(s->typedef_hash, s->n_typedef_slots,
				name, namelen);
#endif
			se = new_lookup_hash(&s->typedef_hash, name, namelen);
			if (se != NULL) {
				return se->dec->dtype;
			}	
		} else {	
			if (s->typedef_decls.data) {
				d = s->typedef_decls.data;
				for (i = 0; i < s->typedef_decls.ndecls; ++i) {
					if (strcmp(d[i]->dtype->name, name)
						== 0) {
						return d[i]->dtype;	
					}
				}
			}
		}
	} while ((s = s->parent) != NULL);
	return NULL;
}

	
struct scope *
new_scope(int type) {
	struct scope	*ret;
	static struct scope	nullscope;
	static unsigned long	scopeno = 1; /* 0 = global scope */

#ifdef DEBUG2
	puts("opening new scope");
#endif
	ret = n_xmalloc(sizeof *ret);
	*ret = nullscope;

	ret->type = type;
	ret->parent = curscope;
	ret->scopeno = scopeno++;
	scopelist_tail->next = ret;
	scopelist_tail = ret;
	curscope = ret;

	return ret;
}

void
close_scope(void) {
#if defined(DEBUG) || defined(DEBUG2) || defined(DEBUG3)
	if ((curscope = curscope->parent) == NULL) {
		fprintf(stderr,
			"Fatal error: Attempting to close global scope\n");
		++errors; /* exit() handler checks this */
		abort();
	}
#else
	if (curscope->parent == NULL) {
		++errors;
	} else {
		curscope = curscope->parent;
	}
#endif
}
	

static void
do_store_decl(struct scope *s, struct dec_block *destdec, struct decl *d) {
	struct statement	*st;
	
	if (destdec->ndecls >= destdec->nslots) {
		if (destdec->nslots == 0) {
			destdec->nslots = 8;
		}
		destdec->nslots *= 2;
		destdec->data = n_xrealloc(destdec->data,
			   	sizeof *destdec->data * destdec->nslots);
	}
	destdec->data[ destdec->ndecls++ ] = d;

	if (s != NULL) {
		st = alloc_statement();
		st->type = ST_DECL;
		st->data = d;
		append_statement(&s->code, &s->code_tail, st);
	}	
}


static int
check_redef(struct scope *s, struct type *ty, int *need_def) {
	struct decl	*d;

	if ((d = lookup_symbol(s, ty->name, 0)) == NULL) {
		return 0;
	}

	if (s == &global_scope) {
		int	compres;
		/*
		 * Declarations at file scope are tentative. They
		 * might be repeated as often as desired, as long
		 * as none of them differs
		 */
		if ((compres = compare_types(d->dtype, ty,
			CMPTY_SIGN|CMPTY_CONST|CMPTY_TENTDEC)) == -1
			|| (d->dtype->storage
				&& ty->storage
				&& d->dtype->storage != ty->storage)) {
			/*
			 * 09/30/07: gcc and tinycc allow arbitrary
			 * redeclarations of static/extern storage
			 * specifiers, e.g.
			 *
			 *     static int foo;
			 *     extern int foo;
			 *
			 * BitchX and probably other programs have
			 * come to depend on this, so we must support
			 * it too
			 */
			if (compres != -1) {
				/*
				 * Yes, storage is only difference
				 *
				 * XXX symbol management is almost
				 * certainly wrong, and will cause
				 * problems for nasm, because the
				 * ``has_def'' stuff below is not
				 * handled
				 */
				warningfl(d->tok, "Redeclaration of "
					"`%s' with different storage "
					"class, this may cause problems "
					"with some assemblers",
					ty->name);
				d->dtype->storage = ty->storage;
			} else {	
				errorfl(d->tok,
					"Redefinition of `%s' with "
					"conflicting type",
					ty->name);
				return -1;
			}
		}
		if (d->dtype->is_func) {
			/*
			 * Might have to replace old function entry
			 * with new one if this is a definition
			 * and the old one is a declaration. In
			 * either case, one can be deallocated
			 */
			if (d->dtype->is_def && ty->is_def) {
				errorfl(d->tok,
					"Multiple definitions of function `%s'",
					ty->name);
			} else if (ty->is_def) {
				if (d->dtype->tlist->tfunc->nargs == -1) {
					ty->tlist->tfunc->was_just_declared = 1;
				}	
				d->dtype = ty;
			}
		}

		*need_def = 0;
		if (ty->storage != TOK_KEY_EXTERN && !ty->is_func) {
			if (!d->has_def) {
				d->has_def = 1;
				*need_def = 1;
			}
		}
		if (ty->tlist != NULL && ty->tlist->type == TN_ARRAY_OF) {
			/* May have to update array information */
			if (d->dtype->tlist->arrarg->const_value == NULL) {
				d->dtype->tlist->arrarg->const_value =
					ty->tlist->arrarg->const_value;
			}
		}

		return 1; /* already has entry */
	} else {
		struct token	*ttmp;
		ttmp = errorfl_mk_tok(
			ty->file, ty->line, NULL);

		errorfl(ttmp,
			"Multiple definitions of `%s'", ty->name);
		return -1;
	}

	/* NOTREACHED */
	return 0;
}


#if 0
/*
 * The stuff below handles a particularly ugly part of the C language;
 * local external declarations, particularly for functions, are
 * permitted! This sucks big time because some assemblers, such as
 * nasm, always require an explicit symbol declaration to get access
 * to an external object. This in turn means duplicate symbols will
 * cause problems, such that we have to check for them. On a per-
 * scope basis, multiple declarations have already been handled
 * neatly for some time. However, e.g. local declarations overriden by
 * subsequent global definitions in the same module, or just
 * subsequent declarations, caused errors.
 *
 * 
 */
struct local_extern_decls {
	struct decl		*dec;
	struct local_func_decl	*next;
};

struct local_func_decl		*local_func_decl_list;
static struct local_func_decl 	*local_func_decl_list_tail;


put_local_func_decl(dec[i]);
#endif

/*
 * Stores declaration referenced by ``d'' in current scope (curscope)
 */
void
store_decl_scope(struct scope *s, struct decl **dec) {
	struct type		*t;
	struct dec_block	*destdec = NULL;
	int			i;

	for (i = 0; dec[i] != NULL; ++i) {
		t = dec[i]->dtype;

		if (t->storage == TOK_KEY_TYPEDEF) {
			/* Store dtype */
			if (s != &global_scope) {
				destdec = &s->typedef_decls;
			} else {
				if (!s->typedef_hash.used) {
					/* Global scope - need large table! */
					new_make_hash_table(&s->typedef_hash,
						SYM_HTAB_GLOBAL_SCOPE);	
#if 0
					s->typedef_hash
						= n_xmalloc(64 * sizeof 
							*s->typedef_hash);
					memset(s->typedef_hash, 0,
						64 * sizeof *s->typedef_hash);
					s->n_typedef_slots = 63;
#endif
				}
#if 0
				put_hash_table(s->typedef_hash,
					s->n_typedef_slots,
					make_sym_entry(dec[i]));
#endif
				new_put_hash_table(&s->typedef_hash,
					make_sym_entry(dec[i]));	
			}	
#ifdef DEBUG2
			printf("storing typedef %s\n",
				t->name ? t->name : "unnamed typedef");
#endif
		} else {
			/* This is a ``real'' declaration */
			if (t->name != NULL) {
				/*
				 * Not anonymous - protect against
				 * redefinition 
				 */
				int	need_def;
				int	rc = check_redef(s, t, &need_def);

				if (rc == 1) {
					/*
					 * Multiple tentative definitions -
					 * OK
					 */
					/* XXX deal with initializers! */
					if (!need_def) {
						/* Need not be allocated */
						continue;
					} else {
						dec[i]->has_def = 1;
					}	
				} else if (rc == -1) {
					/* Redefinition - bad */
					return;
				}
			}

			if (s->type == SCOPE_CODE
				&& (t->storage == TOK_KEY_EXTERN
				|| (t->is_func
					&& t->storage != TOK_KEY_STATIC
					&& t->tlist->type == TN_FUNCTION))) {
				/*
				 * XXX 07/22/07:  
				 * Local extern declarations are terrible to
				 * deal with.. for now we always put them
				 * into the global scope to avoid
				 * redeclarations of symbols
				 *
				 * 08/22/07: This was missing the TN_FUNCTION
				 * check; without function pointers don't
				 * work
				 */
				s = &global_scope;
			}	 
			if (s == &global_scope) {
				/* Global or file scope declaration */
#ifdef DEBUG2
				printf("Storing global declaration %s\n",
					t->name);
#endif
				if (t->storage == TOK_KEY_AUTO) {
					errorfl(dec[i]->tok /*XXX*/,
	"Bogus storage class specifier in global variable declaration");
					return;
				} else if (t->storage == TOK_KEY_EXTERN
					|| (t->is_func
						&& !t->is_def
						&& t->storage
							!= TOK_KEY_STATIC)) {
					t->storage = TOK_KEY_EXTERN;
					destdec = &s->extern_decls;
				} else if (t->storage == 0) {
					/*
					 * Need this to distinguish between
					 * global and local static variables.
					 */
					t->storage = TOK_KEY_EXTERN;
					destdec = &s->static_decls;
				} else if (t->storage == TOK_KEY_STATIC) { 
					destdec = &s->static_decls;
				} else {
					printf("BAD STORAGE CLASS FOR %s "
						"- %d\n", t->name, t->storage);
					abort();
				}	
			} else {
				/*
				 * Local declaration (static or 
				 * automatic or register)
				 */ 
#ifdef DEBUG2
				printf("Storing local declaration %s\n",
					t->name? t->name:
						"unknown declaration");
#endif

				if (t->storage == TOK_KEY_EXTERN) {
					destdec = &s->extern_decls;
				} else if (t->storage == TOK_KEY_STATIC) {
					destdec = &s->static_decls;
				} else if (t->storage == TOK_KEY_REGISTER) {
					/* destdec = &s->register_decls; */
					destdec = &s->automatic_decls;
				} else if (t->is_func && t->tlist->type == TN_FUNCTION) {
					/* XXX not working */
					t->storage = TOK_KEY_EXTERN;
					destdec = &s->extern_decls;
				} else {
					/*
					 * 08/21/07: Removed because this is
					 * never used
					 */
/*					t->storage = TOK_KEY_AUTO;*/
					destdec = &s->automatic_decls;
				}	
				if (t->is_func
					&& t->tlist->type == TN_FUNCTION) {
					/* Record local function declaration */
#if 0
					printf("putting local fdec `%s'\n", t->name);
					put_local_func_decl(dec[i]);
#endif
				}
			}

			if (destdec == &s->extern_decls) {
				append_symlist(NULL, &extern_vars,
					&extern_vars_tail,
					dec[i]);
			}
						

			if (destdec == &s->static_decls
				&& !dec[i]->dtype->is_func) {
				if (dec[i]->init != NULL) {
					append_decl(
						&static_init_vars, 
						&siv_tail, 
						dec[i]);
				} else {
					append_decl(
						&static_uninit_vars,
						&siuv_tail,
						dec[i]);
				}
			}

			/*
			 * NOTE: A declaration need not have a name to be
			 * valid! (e.g. anonymous unions and bitfields)
			 */
			append_symlist(s,
				&s->slist, &s->slist_tail, dec[i]);
		}
		if (destdec) {
			if (dec[i]->dtype->storage == TOK_KEY_STATIC
				&& !dec[i]->dtype->is_func
				&& dec[i]->asmname == NULL) {
				char	*newname;
				size_t	len = strlen(dec[i]->dtype->name);

				/*
				 * Change variable name to avoid name clashes
				 * with other static variables. The old name
				 * will be kept in the symbol list. And since
				 * all lookups go through this list, the
				 * identifier is still visible as what it was
				 * originally called
				 */
				len += sizeof "_Static_ " + 8;
				newname = n_xmalloc(len+sizeof "_Static_"+8);
				sprintf(newname, "_Static_%s%lu",
					dec[i]->dtype->name, s->scopeno); 
				dec[i]->dtype->name = newname;
			} else if (dec[i]->dtype->storage == TOK_KEY_EXTERN
				|| dec[i]->dtype->storage == TOK_KEY_STATIC
				|| dec[i]->dtype->is_func) {
				if (dec[i]->asmname != NULL) {
					dec[i]->dtype->name = dec[i]->asmname;
				}
			}	
			do_store_decl(s, destdec, dec[i]);
		}
	}
}



void
complete_type(struct ty_struct *oldts, struct ty_struct *ts) {
	struct ty_struct	*oldlink;
	int			oldrefs;

	oldlink = oldts->next;
	oldrefs = oldts->references;
	if (ts->incomplete) {
		return;
	}	
	memcpy(oldts, ts, sizeof *ts);
	oldts->next = oldlink;
	oldts->incomplete = 0;
	oldts->references =
		oldrefs + ts->references;
}					

/*
 * Stores structure or enum definition (not instance!) in current scope.
 * If ts is not a null pointer, it will be stored. Otherwise, te will be
 * stored
 */
void
store_def_scope(struct scope *sc,
		struct ty_struct *ts,
		struct ty_enum *te,
		struct token *tok) {
	int		i;
	char		*p;
	struct sd	*s = NULL;
	struct ed	*e = NULL;

	if (ts != NULL) {
		/*
		 * We must first check whether we are completing an incomplete
		 * structure, i.e. one that was originally declared using e.g.
		 * ``struct foo;'' or ``typedef struct foo bar;''
		 */
		struct scope	*destscope = curscope;

		while (destscope->type == SCOPE_STRUCT) {
			destscope = destscope->parent;
		}	
		
		ts->parentscope = destscope;
		if (ts->tag != NULL) {
			struct ty_struct	*oldts;

			if ((oldts = lookup_struct(destscope, ts->tag, 1))
				!= NULL) {
				if (oldts->incomplete) {
					complete_type(oldts, ts);
					return;
				} else if (destscope
					== oldts->parentscope) {
					if (!ts->incomplete) {
						errorfl(tok->prev,
							"Multiple definitions "
							"of structure or "
							"union `%s'", ts->tag);
					}	
					return;
				}	
			}
		}	

		/* Completely new structure type */
		s = &destscope->struct_defs;
		if (s->head == NULL) {
			s->head = s->tail = ts;
		} else {
			s->tail->next = ts;
			s->tail = s->tail->next;
		}	
		++s->ndecls;
#ifdef DEBUG2
		printf("stored structure with tag %s\n", ts->tag);
#endif
	} else if (te == NULL) {
		puts("Fatal error in store_def_scope - te = 0");
		abort();
	} else {
		struct scope	*destscope = curscope;

		while (destscope->type == SCOPE_STRUCT) {
			destscope = destscope->parent;
		}	

		e = & /*sc*/ destscope->enum_defs;
		if (te->tag != NULL) {
			/*
			 * Check whether we have a multiple definition error.
			 * It is not necessary to check for incomplete types,
			 * as with structures, because enum forward references
			 * are invalid (XXX might be implemented as extension
			 * later)
			 */
			for (i = 0; i < e->ndecls; ++i) {
				p = e->data[i]->tag;
				if (p && strcmp(p, te->tag) == 0) {
					errorfl(tok,
						"Multiple definitions of "
						"enum `%s'", p);
					return;
				}
			}
			if (lookup_struct(sc, te->tag, 0) != NULL) {
				errorfl(tok,
				"Multiple definitions of structure `%s'",
					te->tag);
			}
		}
			
		if (e->ndecls >= e->nslots) {
			if (e->nslots == 0) {
				e->nslots = 1;
			}
			e->nslots *= 2;
			e->data = n_xrealloc(e->data,
					e->nslots * sizeof *e->data);
		}
		e->data[ e->ndecls++ ] = te;
	}
}

struct decl *
lookup_symbol(struct scope *s, const char *name, int nested) {
	struct sym_entry	*se;

	se = lookup_symbol_se(s, name, nested);
	if (se == NULL) {
		return NULL;
	}
	return se->dec;
}


struct /*decl*/ sym_entry *
lookup_symbol_se(struct scope *s, const char *name, int nested) {
	size_t			len; 

	len = strlen(name);
	do {
		struct sym_entry	*se;
		
		if (!s->sym_hash.used) {
			/* Linear scan */
			for (se = s->slist; se != NULL; se = se->next) {
				if (se->namelen == len) {
					if (se->name[0] == name[0]) {
						if (strcmp(&se->name[1],
							&name[1]) == 0) {
							return se  /*->dec*/;
						}
					}
				}
			}
		} else {
			/* Hash lookup (key is length) */
#if 0 
			se = lookup_hash(s->sym_hash,
					s->n_hash_slots, name, len);
#endif
			se = new_lookup_hash(&s->sym_hash, name, len);
			if (se != NULL) {
				return se /*->dec*/;
			}	
		}

		if (nested == 0) {
			break;
		}
	} while ((s = s->parent) != NULL);

	return NULL;
}

struct decl *
access_symbol(struct scope *s, const char *name, int nested) {
	struct decl	*ret;

	if ((ret = lookup_symbol(s, name, nested)) != NULL) {
		if (ret->is_alias) {
			ret = ret->is_alias;
		}
		++ret->references;
		if (ret->dtype->tstruc != NULL) {
			++ret->dtype->tstruc->references;
		}	
	}	
	return ret;
}

struct decl *
put_implicit(const char *name) {
	struct decl	*d[2];
	struct type	*ty;
	struct type_node	*tnode;

	d[0] = alloc_decl();
	ty = alloc_type();
	ty->name = (char *)name;
	ty->code = TY_INT;
	ty->storage = TOK_KEY_EXTERN;
	ty->is_func = 1;
	ty->implicit = 1;
	ty->sign = TOK_KEY_SIGNED;
	tnode = alloc_type_node();
	tnode->type = TN_FUNCTION;
	tnode->tfunc = alloc_ty_func();
	tnode->tfunc->scope = NULL;
	tnode->tfunc->nargs = -1;
	tnode->tfunc->ret = NULL;
	tnode->tfunc->type = FDTYPE_ISO;
	ty->tlist = tnode;

	d[0]->dtype = ty;
	d[0]->references = 1;
	d[1] = NULL;

	store_decl_scope(&global_scope, d);
	return d[0];
}



syntax highlighted by Code2HTML, v. 0.9.1