From bc16901bd6941c5c775bdfca5d20b5e59f59302f Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 3 Nov 2006 06:14:43 +0000 Subject: [PATCH] Expanded weak function pointers with type checking Added function typing enumeration --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40137 --- sourcepawn/compiler/pawncc.c | 2 +- sourcepawn/compiler/sc.h | 100 ++++++++------ sourcepawn/compiler/sc1.c | 228 +++++++++++++++++++++++++++++++- sourcepawn/compiler/sc2.c | 2 +- sourcepawn/compiler/sc3.c | 195 ++++++++++++++++++++++++++- sourcepawn/compiler/sctracker.c | 84 ++++++++++++ sourcepawn/compiler/sctracker.h | 38 ++++++ 7 files changed, 596 insertions(+), 53 deletions(-) diff --git a/sourcepawn/compiler/pawncc.c b/sourcepawn/compiler/pawncc.c index ff78485c..dc919128 100644 --- a/sourcepawn/compiler/pawncc.c +++ b/sourcepawn/compiler/pawncc.c @@ -45,7 +45,7 @@ int main(int argc, char *argv[]) int err; uint32_t i; sp_file_t *spf; - memfile_t *dbgtab = NULL; + memfile_t *dbgtab = NULL; //dbgcrab unsigned char *dbgptr = NULL; uint32_t sections[FS_Number] = {1,1,0,0,0,1,0,0,0,0,0,0}; FILE *fp; diff --git a/sourcepawn/compiler/sc.h b/sourcepawn/compiler/sc.h index c6e0e8de..ecd01dd0 100644 --- a/sourcepawn/compiler/sc.h +++ b/sourcepawn/compiler/sc.h @@ -57,6 +57,8 @@ #define sDEF_AMXSTACK 4096 /* default stack size for AMX files */ #define PREPROC_TERM '\x7f'/* termination character for preprocessor expressions (the "DEL" code) */ #define sDEF_PREFIX "sourcemod.inc" /* default prefix filename */ +#define sARGS_MAX 32 /* number of arguments a function can have, max */ +#define sTAGS_MAX 16 /* maximum number of tags on an argument */ typedef union { void *pv; /* e.g. a name */ @@ -283,7 +285,7 @@ typedef struct s_stringpair { */ #define tFIRST 256 /* value of first multi-character operator */ #define tMIDDLE 280 /* value of last multi-character operator */ -#define tLAST 329 /* value of last multi-character match-able token */ +#define tLAST 330 /* value of last multi-character match-able token */ /* multi-character operators */ #define taMULT 256 /* *= */ #define taDIV 257 /* /= */ @@ -327,51 +329,52 @@ typedef struct s_stringpair { #define tEXIT 294 #define tFOR 295 #define tFORWARD 296 -#define tGOTO 297 -#define tIF 298 -#define tNATIVE 299 -#define tNEW 300 -#define tDECL 301 -#define tOPERATOR 302 -#define tPUBLIC 303 -#define tRETURN 304 -#define tSIZEOF 305 -#define tSLEEP 306 -#define tSTATE 307 -#define tSTATIC 308 -#define tSTOCK 309 -#define tSWITCH 310 -#define tTAGOF 311 -#define tTHEN 312 -#define tWHILE 313 +#define tFUNCENUM 297 +#define tGOTO 298 +#define tIF 299 +#define tNATIVE 300 +#define tNEW 301 +#define tDECL 302 +#define tOPERATOR 303 +#define tPUBLIC 304 +#define tRETURN 305 +#define tSIZEOF 306 +#define tSLEEP 307 +#define tSTATE 308 +#define tSTATIC 309 +#define tSTOCK 310 +#define tSWITCH 311 +#define tTAGOF 312 +#define tTHEN 313 +#define tWHILE 314 /* compiler directives */ -#define tpASSERT 314 /* #assert */ -#define tpDEFINE 315 -#define tpELSE 316 /* #else */ -#define tpELSEIF 317 /* #elseif */ -#define tpEMIT 318 -#define tpENDIF 319 -#define tpENDINPUT 320 -#define tpENDSCRPT 321 -#define tpERROR 322 -#define tpFILE 323 -#define tpIF 324 /* #if */ -#define tINCLUDE 325 -#define tpLINE 326 -#define tpPRAGMA 327 -#define tpTRYINCLUDE 328 -#define tpUNDEF 329 +#define tpASSERT 315 /* #assert */ +#define tpDEFINE 316 +#define tpELSE 317 /* #else */ +#define tpELSEIF 318 /* #elseif */ +#define tpEMIT 319 +#define tpENDIF 320 +#define tpENDINPUT 321 +#define tpENDSCRPT 322 +#define tpERROR 323 +#define tpFILE 324 +#define tpIF 325 /* #if */ +#define tINCLUDE 326 +#define tpLINE 327 +#define tpPRAGMA 328 +#define tpTRYINCLUDE 329 +#define tpUNDEF 330 /* semicolon is a special case, because it can be optional */ -#define tTERM 330 /* semicolon or newline */ -#define tENDEXPR 331 /* forced end of expression */ +#define tTERM 331 /* semicolon or newline */ +#define tENDEXPR 332 /* forced end of expression */ /* other recognized tokens */ -#define tNUMBER 332 /* integer number */ -#define tRATIONAL 333 /* rational number */ -#define tSYMBOL 334 -#define tLABEL 335 -#define tSTRING 336 -#define tEXPR 337 /* for assigment to "lastst" only (see SC1.C) */ -#define tENDLESS 338 /* endless loop, for assigment to "lastst" only */ +#define tNUMBER 333 /* integer number */ +#define tRATIONAL 334 /* rational number */ +#define tSYMBOL 335 +#define tLABEL 336 +#define tSTRING 337 +#define tEXPR 338 /* for assigment to "lastst" only (see SC1.C) */ +#define tENDLESS 339 /* endless loop, for assigment to "lastst" only */ /* (reversed) evaluation of staging buffer */ #define sSTARTREORDER 0x01 @@ -432,9 +435,11 @@ typedef enum s_optmark { #if INT_MAX<0x8000u #define PUBLICTAG 0x8000u #define FIXEDTAG 0x4000u + #define FUNCTAG 0x2000u #else #define PUBLICTAG 0x80000000Lu #define FIXEDTAG 0x40000000Lu + #define FUNCTAG 0x20000000Lu #endif #define TAGMASK (~PUBLICTAG) #define CELL_MAX (((ucell)1 << (sizeof(cell)*8-1)) - 1) @@ -451,6 +456,7 @@ typedef enum s_optmark { int pc_compile(int argc, char **argv); int pc_addconstant(char *name,cell value,int tag); int pc_addtag(char *name); +int pc_addfunctag(char *name); int pc_enablewarning(int number,int enable); /* @@ -517,6 +523,7 @@ SC_FUNC void delete_consttable(constvalue *table); SC_FUNC symbol *add_constant(char *name,cell val,int vclass,int tag); SC_FUNC void exporttag(int tag); SC_FUNC void sc_attachdocumentation(symbol *sym); +SC_FUNC constvalue *find_tag_byval(int tag); /* function prototypes in SC2.C */ #define PUSHSTK_P(v) { stkitem s_; s_.pv=(v); pushstk(s_); } @@ -801,6 +808,7 @@ SC_VDECL char *pc_deprecate; /* if non-NULL, mark next declaration as deprecate SC_VDECL int sc_curstates; /* ID of the current state list */ SC_VDECL int pc_optimize; /* (peephole) optimization level */ SC_VDECL int pc_memflags; /* special flags for the stack/heap usage */ +SC_VDECL int pc_functag; /* global function tag */ SC_VDECL constvalue sc_automaton_tab; /* automaton table */ SC_VDECL constvalue sc_state_tab; /* state table */ @@ -815,6 +823,12 @@ SC_VDECL jmp_buf errbuf; /* target of longjmp() on a fatal error */ SC_VDECL int sc_makereport; /* generate a cross-reference report */ #endif +#if defined WIN32 +#if !defined snprintf +#define snprintf _snprintf +#endif +#endif + #endif /* SC_SKIP_VDECL */ #endif /* SC_H_INCLUDED */ diff --git a/sourcepawn/compiler/sc1.c b/sourcepawn/compiler/sc1.c index 189b9238..656d9297 100644 --- a/sourcepawn/compiler/sc1.c +++ b/sourcepawn/compiler/sc1.c @@ -70,6 +70,8 @@ #define VERSION_STR "3.2." SVN_REVSTR #define VERSION_INT 0x0302 +int pc_functag = 0; + static void resetglobals(void); static void initglobals(void); static char *get_extension(char *filename); @@ -126,6 +128,7 @@ static void doswitch(void); static void dogoto(void); static void dolabel(void); static void doreturn(void); +static void dofuncenum(void); static void dobreak(void); static void docont(void); static void dosleep(void); @@ -320,6 +323,7 @@ int pc_compile(int argc, char *argv[]) inst_datetime_defines(); #endif resetglobals(); + funcenums_free(); sc_ctrlchar=sc_ctrlchar_org; sc_packstr=lcl_packstr; sc_needsemicolon=lcl_needsemicolon; @@ -380,6 +384,7 @@ int pc_compile(int argc, char *argv[]) /* reset "defined" flag of all functions and global variables */ reduce_referrers(&glbtab); delete_symbols(&glbtab,0,TRUE,FALSE); + funcenums_free(); #if !defined NO_DEFINE delete_substtable(); inst_datetime_defines(); @@ -577,6 +582,7 @@ int pc_addtag(char *name) if (strcmp(name,ptr->name)==0) return tag; /* tagname is known, return its sequence number */ tag &= (int)~FIXEDTAG; + tag &= (int)~FUNCTAG; if (tag>last) last=tag; ptr=ptr->next; @@ -590,6 +596,43 @@ int pc_addtag(char *name) return tag; } +int pc_addfunctag(char *name) +{ + cell val; + constvalue *ptr; + int last,tag; + + if (name==NULL) { + /* no tagname was given, check for one */ + if (lex(&val,&name)!=tLABEL) { + lexpush(); + return 0; /* untagged */ + } /* if */ + } /* if */ + + assert(strchr(name,':')==NULL); /* colon should already have been stripped */ + last=0; + ptr=tagname_tab.next; + while (ptr!=NULL) { + tag=(int)(ptr->value & TAGMASK); + if (strcmp(name,ptr->name)==0) + return tag; /* tagname is known, return its sequence number */ + tag &= (int)~FIXEDTAG; + tag &= (int)~FUNCTAG; + if (tag>last) + last=tag; + ptr=ptr->next; + } /* while */ + + /* tagname currently unknown, add it */ + tag=last+1; /* guaranteed not to exist already */ + if (isupper(*name)) + tag |= (int)FIXEDTAG; + tag |= (int)FUNCTAG; + append_constval(&tagname_tab,name,(cell)tag,0); + return tag; +} + static void resetglobals(void) { /* reset the subset of global variables that is modified by the first pass */ @@ -1204,6 +1247,7 @@ static void setconstants(void) assert(sc_status==statIDLE); append_constval(&tagname_tab,"_",0,0);/* "untagged" */ append_constval(&tagname_tab,"bool",1,0); + pc_functag = pc_addfunctag("Function"); add_constant("true",1,sGLOBAL,1); /* boolean flags */ add_constant("false",0,sGLOBAL,1); @@ -1363,6 +1407,9 @@ static void parse(void) case tENUM: decl_enum(sGLOBAL); break; + case tFUNCENUM: + dofuncenum(); + break; case tPUBLIC: /* This can be a public function or a public variable; see the comment * above (for static functions/variables) for details. @@ -2536,6 +2583,185 @@ static void decl_const(int vclass) needtoken(tTERM); } +/* dofuncenum - declare function enumerations + * + */ +static void dofuncenum(void) +{ + cell val; + char *str,*ptr; + char tagname[sNAMEMAX+1]; + constvalue *cur; + funcenum_t *fenum = NULL; + int i; + + /* get the explicit tag (required!) */ + int l = lex(&val,&str); + if (l != tSYMBOL) + { + /* Incomprehensible but it works for now! */ + error(57); + } + + /* This tag can't already exist! */ + cur=tagname_tab.next; + while (cur) + { + if (strcmp(cur->name, str) == 0) + { + /* Another bad one... */ + if (!(cur->value & FUNCTAG)) + { + error(213); + } + break; + } + cur = cur->next; + } + strcpy(tagname, str); + + fenum = funcenums_add(tagname); + + needtoken('{'); + do + { + functag_t func; + if (matchtoken('}')) + { + /* Quick exit */ + lexpush(); + break; + } + memset(&func, 0, sizeof(func)); + func.ret_tag = pc_addtag(NULL); /* Get the return tag */ + l = lex(&val, &str); + if (l == tFORWARD) + { + func.type = uFORWARD; + } else if (l == tPUBLIC) { + func.type = uPUBLIC; + } else { + error(1, "[forward,public]", str); + } + needtoken('('); + do + { + funcarg_t *arg = &(func.args[func.argcount]); + + /* Quick exit */ + if (matchtoken(')')) + { + lexpush(); + break; + } + l = lex(&val, &str); + if (l == '&') + { + if ((arg->ident != iVARIABLE && arg->ident != 0) || arg->tagcount > 0) + { + error(1, "-identifier-", "&"); + } + arg->ident = iREFERENCE; + } else if (l == tCONST) { + if ((arg->ident != iVARIABLE && arg->ident != 0) || arg->tagcount > 0) + { + error(1, "-identifier-", "const"); + } + arg->fconst=TRUE; + } else if (l == tLABEL) { + if (arg->tagcount > 0) + { + error(1, "-identifier-", "-tagname-"); + } + arg->tags[arg->tagcount++] = pc_addtag(str); +#if 0 + while (arg->tagcount < sTAGS_MAX) + { + if (!matchtoken('_') && !needtoken(tSYMBOL)) + { + break; + } + tokeninfo(&val, &ptr); + arg->tags[arg->tagcount++] = pc_addtag(ptr); + if (matchtoken('}')) + { + break; + } + needtoken(','); + } + needtoken(':'); +#endif + l=tLABEL; + } else if (l == tSYMBOL) { + if (func.argcount >= sARGS_MAX) + { + error(45); + } + if (str[0] == PUBLIC_CHAR) + { + error(56, str); + } + if (matchtoken('[')) + { + if (arg->ident == iREFERENCE) + { + error(67, str); + } + do + { + constvalue *enumroot; + cell size; + int ignore_tag; + if (arg->dimcount == sDIMEN_MAX) + { + error(53); + break; + } + size = needsub(&ignore_tag, &enumroot); + arg->dims[arg->dimcount] = size; + arg->dimcount += 1; + } while (matchtoken('[')); + arg->ident=iREFARRAY; + } else if (arg->ident == 0) { + arg->ident = iVARIABLE; + } + + if (matchtoken('=')) + { + needtoken('0'); + arg->ommittable = TRUE; + func.ommittable = TRUE; + } else if (func.ommittable) { + /* :TODO: ERROR HERE! */ + } + func.argcount++; + } else if (l == tELLIPS) { + if (arg->ident == iVARIABLE) + { + error(10); + } + arg->ident = iVARARGS; + func.argcount++; + } else { + error(10); + } + } while (l == '&' || l == tLABEL || l == tCONST || l != tELLIPS && matchtoken(',')); + needtoken(')'); + for (i=0; i=", "<<", ">>>", ">>", "++", "--", "...", "..", "::", "assert", "*begin", "break", "case", "char", "const", "continue", "default", - "defined", "do", "else", "*end", "enum", "exit", "for", "forward", "goto", + "defined", "do", "else", "*end", "enum", "exit", "for", "forward", "funcenum", "goto", "if", "native", "new", "decl", "operator", "public", "return", "sizeof", "sleep", "state", "static", "stock", "switch", "tagof", "*then", "while", "#assert", "#define", "#else", "#elseif", "#emit", "#endif", "#endinput", diff --git a/sourcepawn/compiler/sc3.c b/sourcepawn/compiler/sc3.c index fba92491..4e7e47a6 100644 --- a/sourcepawn/compiler/sc3.c +++ b/sourcepawn/compiler/sc3.c @@ -288,8 +288,178 @@ SC_FUNC int matchtag(int formaltag,int actualtag,int allowcoerce) /* if the formal tag is zero and the actual tag is not "fixed", the actual * tag is "coerced" to zero */ - if (!allowcoerce || formaltag!=0 || (actualtag & FIXEDTAG)!=0) - return FALSE; + if (!allowcoerce || formaltag!=0 || (actualtag & FIXEDTAG)!=0) { + if (formaltag & FUNCTAG) + { + if (actualtag == pc_functag || (formaltag == pc_functag && actualtag & FUNCTAG)) + { + return TRUE; + } else if (actualtag & FUNCTAG) { + constvalue *v = find_tag_byval(actualtag); + int index; + short usage = uPUBLIC; + symbol *sym, *found = NULL; + funcenum_t *e; + functag_t *t; + + if (strncmp(v->name, "$Func", 5) != 0) + { + return FALSE; + } + + /* Now we have to go about looking up each function in this enum. WHICH IS IT. */ + e = funcenums_find_byval(formaltag); + if (!e) + { + return FALSE; + } + + assert(v->name[5] == '@' || v->name[5] == '!'); + + /* Deduce which function type this is */ + if (v->name[5] == '@') + { + usage = uPUBLIC; + } else if (v->name[5] = '!') { + usage = uFORWARD; + } + + index = atoi(&v->name[6]); + + assert(index >= 0); + + /* Find the function, either by public idx or code addr */ + if (usage == uPUBLIC) + { + for (sym=glbtab.next; sym!=NULL; sym=sym->next) { + if (sym->ident==iFUNCTN && (sym->usage & uPUBLIC)!=0 && (sym->vclass == sGLOBAL)) + { + if (index-- == 0) + { + found = sym; + break; + } + } + } + } else if (usage == uFORWARD) { + for (sym=glbtab.next; sym!=NULL; sym=sym->next) { + if (sym->ident==iFUNCTN && (sym->vclass == sGLOBAL)) + { + if (sym->codeaddr == index) + { + found = sym; + break; + } + } + } + } + + if (!found) + { + assert(found); + return FALSE; + } + + /* Wow, we now have: + * 1) The functional enum deduced from formaltag + * 2) The function trying to be shoved in deduced from actualtag + * Now we have to check if it matches any one of the functags inside the enum. + */ + t = e->first; + while (t) + { + int curarg,skip=0,i; + arginfo *func_arg; + funcarg_t *enum_arg; + /* Check return type first. */ + if (t->ret_tag != sym->tag) + { + t = t->next; + continue; + } + /* Check usage */ + if (t->type != usage) + { + t = t->next; + continue; + } + /* Begin iterating arguments */ + for (curarg=0; curargargcount; curarg++) + { + enum_arg = &t->args[curarg]; + /* Check whether we've exhausted our arguments */ + if (sym->dim.arglist[curarg].ident == 0) + { + /* Can we bail out early? */ + if (!enum_arg->ommittable) + { + /* No! */ + skip = 1; + } + break; + } + func_arg = &sym->dim.arglist[curarg]; + /* First check the ident type */ + if (enum_arg->ident != func_arg->ident) + { + skip = 1; + break; + } + /* Next check arrayness */ + if (enum_arg->dimcount != func_arg->numdim) + { + skip = 1; + break; + } + if (enum_arg->dimcount > 0) + { + for (i=0; idimcount; i++) + { + if (enum_arg->dims[i] != func_arg->dim[i]) + { + skip = 1; + break; + } + } + if (skip) + { + break; + } + } + /* Lastly, check the tags */ + if (enum_arg->tagcount != func_arg->numtags) + { + skip = 1; + break; + } + /* They should all be in the same order just for clarity... */ + for (i=0; itagcount; i++) + { + if (enum_arg->tags[i] != func_arg->tags[i]) + { + skip = 1; + break; + } + } + if (skip) + { + break; + } + } + if (!skip) + { + /* Make sure there are no trailing arguments */ + if (sym->dim.arglist[curarg].ident == 0) + { + return TRUE; + } + } + t = t->next; + } + } + } + return FALSE; + } } /* if */ return TRUE; } @@ -1723,24 +1893,35 @@ restart: if (sc_allowproccall) { callfunction(sym,lval1,FALSE); } else { + symbol *oldsym=sym; int n=-1,iter=0; + int usage = ((sym->usage & uPUBLIC) == uPUBLIC) ? uPUBLIC : 0; + cell code_addr=0; for (sym=glbtab.next; sym!=NULL; sym=sym->next) { - if (sym->ident==iFUNCTN - && (sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) + if (sym->ident==iFUNCTN && sym->vclass == sGLOBAL && (!usage || (sym->usage & usage))) { - assert(sym->vclass==sGLOBAL); if (strcmp(sym->name, lval1->sym->name)==0) { n = iter; + code_addr = sym->codeaddr; break; } iter++; } } if (n!=-1) { + char faketag[sNAMEMAX+1]; lval1->sym=NULL; lval1->ident=iCONSTEXPR; - lval1->constval=n; - lval1->tag=pc_addtag("Function"); + /* Generate a quick pseudo-tag! */ + if (usage == uPUBLIC) { + lval1->constval=(n>>1)|(1<<1); + snprintf(faketag, sizeof(faketag)-1, "$Func@%d", n); + } else { + lval1->constval=(code_addr>>1)|(1<<0); + snprintf(faketag, sizeof(faketag)-1, "$Func!%d", code_addr); + } + lval1->tag=pc_addfunctag(faketag); + oldsym->usage |= uREAD; } else { error(76); /* invalid function call, or syntax error */ } /* if */ diff --git a/sourcepawn/compiler/sctracker.c b/sourcepawn/compiler/sctracker.c index b1d312e3..308bd751 100644 --- a/sourcepawn/compiler/sctracker.c +++ b/sourcepawn/compiler/sctracker.c @@ -1,10 +1,94 @@ #include +#include #include #include "sc.h" #include "sctracker.h" memuse_list_t *heapusage = NULL; memuse_list_t *stackusage = NULL; +funcenum_t *firstenum = NULL; +funcenum_t *lastenum = NULL; + +void funcenums_free() +{ + funcenum_t *e, *next; + + e = firstenum; + while (e) + { + functag_t *tag, *nexttag; + tag = e->first; + while (tag) + { + nexttag = tag->next; + free(tag); + tag = nexttag; + } + next = e->next; + free(e); + e = next; + } + + firstenum = NULL; + lastenum = NULL; +} + +funcenum_t *funcenums_find_byval(int value) +{ + funcenum_t *e = firstenum; + + while (e) + { + if (e->value == value) + { + return e; + } + e = e->next; + } + + return NULL; +} + +funcenum_t *funcenums_add(const char *name) +{ + funcenum_t *e = (funcenum_t *)malloc(sizeof(funcenum_t)); + + memset(e, 0, sizeof(funcenum_t)); + + if (firstenum == NULL) + { + firstenum = e; + lastenum = e; + } else { + lastenum->next = e; + lastenum = e; + } + + strcpy(e->name, name); + e->value = pc_addfunctag((char *)name); + + return e; +} + +functag_t *functags_add(funcenum_t *en, functag_t *src) +{ + functag_t *t = (functag_t *)malloc(sizeof(functag_t)); + + memcpy(t, src, sizeof(functag_t)); + + t->next = NULL; + + if (en->first == NULL) + { + en->first = t; + en->last = t; + } else { + en->last->next = t; + en->last = t; + } + + return t; +} /** * Creates a new mem usage tracker entry diff --git a/sourcepawn/compiler/sctracker.h b/sourcepawn/compiler/sctracker.h index 8cb02b72..1e05a2c7 100644 --- a/sourcepawn/compiler/sctracker.h +++ b/sourcepawn/compiler/sctracker.h @@ -16,6 +16,44 @@ typedef struct memuse_list_s { memuse_t *head; /* head of the current list */ } memuse_list_t; +typedef struct funcarg_s +{ + int tagcount; + int tags[sTAGS_MAX]; + int dimcount; + cell dims[sDIMEN_MAX]; + int ident; + int fconst; + int ommittable; +} funcarg_t; + +typedef struct functag_s +{ + int ret_tag; + int type; + int argcount; + int ommittable; + funcarg_t args[sARGS_MAX]; + struct functag_s *next; +} functag_t; + +typedef struct funcenum_s +{ + int value; + char name[sNAMEMAX+1]; + functag_t *first; + functag_t *last; + struct funcenum_s *next; +} funcenum_t; + +/** + * Function enumeration tags + */ +void funcenums_free(); +funcenum_t *funcenums_add(const char *name); +funcenum_t *funcenums_find_byval(int value); +functag_t *functags_add(funcenum_t *en, functag_t *src); + /** * Heap functions */