Merge pull request #134 from alliedmodders/calli
Simplify how functags work.
This commit is contained in:
		
						commit
						91f0f1001b
					
				| @ -964,9 +964,10 @@ SC_VDECL jmp_buf errbuf;      /* target of longjmp() on a fatal error */ | ||||
| #endif | ||||
| 
 | ||||
| #if defined WIN32 | ||||
| #if !defined snprintf | ||||
| #define snprintf _snprintf | ||||
| #endif | ||||
| # if !defined snprintf | ||||
| #  define snprintf _snprintf | ||||
| #  define vsnprintf _vsnprintf | ||||
| # endif | ||||
| #endif | ||||
| 
 | ||||
| #endif /* SC_SKIP_VDECL */ | ||||
|  | ||||
| @ -705,7 +705,7 @@ int pc_addtag_flags(char *name, int flags) | ||||
|   constvalue *ptr; | ||||
|   int last,tag; | ||||
| 
 | ||||
|   assert(strchr(name,':')==NULL); /* colon should already have been stripped */ | ||||
|   assert((flags & FUNCTAG) || strchr(name,':')==NULL); /* colon should already have been stripped */ | ||||
|   last=0; | ||||
|   ptr=tagname_tab.next; | ||||
|   while (ptr!=NULL) { | ||||
| @ -4201,7 +4201,7 @@ static void parse_function_type(functag_t *type) | ||||
|   needtoken(tFUNCTION); | ||||
| 
 | ||||
|   type->ret_tag = parse_new_typename(NULL); | ||||
|   type->type = uPUBLIC; | ||||
|   type->usage = uPUBLIC; | ||||
| 
 | ||||
|   needtoken('('); | ||||
| 
 | ||||
| @ -4370,13 +4370,13 @@ static void dofuncenum(int listmode) | ||||
|        * rather than the constant value.  And even if we could, we'd have to change the assembler recognize that. | ||||
|        */ | ||||
|       if (l == tPUBLIC) { | ||||
|         func.type = uPUBLIC; | ||||
|         func.usage = uPUBLIC; | ||||
|       } else { | ||||
|         error(1, "-public-", str); | ||||
|       } | ||||
|     } else { | ||||
|       func.ret_tag = newStyleTag; | ||||
|       func.type = uPUBLIC; | ||||
|       func.usage = uPUBLIC; | ||||
|     } | ||||
|     needtoken('('); | ||||
|     do { | ||||
|  | ||||
| @ -413,15 +413,81 @@ static int matchobjecttags(int formaltag, int actualtag, int flags) | ||||
|   return obj_typeerror(133, formaltag, actualtag); | ||||
| } | ||||
| 
 | ||||
| static int matchreturntag(functag_t *t, symbol *sym) | ||||
| static int matchreturntag(const functag_t *formal, const functag_t *actual) | ||||
| { | ||||
|   if (t->ret_tag == sym->tag) | ||||
|     return TRUE; | ||||
|   if (t->ret_tag == pc_tag_void && (sym->tag == 0 && !(sym->usage & uRETVALUE))) | ||||
|   if (formal->ret_tag == actual->ret_tag) | ||||
|     return TRUE; | ||||
|   if (formal->ret_tag == pc_tag_void) { | ||||
|     if (actual->ret_tag == 0 && !(actual->usage & uRETVALUE)) | ||||
|       return TRUE; | ||||
|   } | ||||
|   return FALSE; | ||||
| } | ||||
| 
 | ||||
| static int funcarg_compare(const funcarg_t *formal, const funcarg_t *actual) | ||||
| { | ||||
|   // Check type.
 | ||||
|   if (actual->ident != formal->ident) | ||||
|     return FALSE; | ||||
| 
 | ||||
|   // Check rank.
 | ||||
|   if (actual->dimcount != formal->dimcount) | ||||
|     return FALSE; | ||||
| 
 | ||||
|   // Check arity.
 | ||||
|   for (int i = 0; i < formal->dimcount; i++) { | ||||
|     if (actual->dims[i] != formal->dims[i]) | ||||
|       return FALSE; | ||||
|   } | ||||
| 
 | ||||
|   // Check tags.
 | ||||
|   if (actual->tagcount != formal->tagcount) | ||||
|     return FALSE; | ||||
|   for (int i = 0; i < formal->tagcount; i++) { | ||||
|     // Note we invert the order we pass things to matchtag() here. If the
 | ||||
|     // typedef specifies base type X, and the function specifies derived
 | ||||
|     // type Y, we want this to type since such an assignment is valid.
 | ||||
|     // 
 | ||||
|     // Most programming languages do not subtype arguments like this. We do
 | ||||
|     // it in SourcePawn to preserve compatibility during the Transitional
 | ||||
|     // Syntax effort.
 | ||||
|     int actual_tag = actual->tags[i]; | ||||
|     int formal_tag = formal->tags[i]; | ||||
|     if (!matchtag(actual_tag, formal_tag, MATCHTAG_SILENT|MATCHTAG_COERCE)) | ||||
|       return FALSE; | ||||
|   } | ||||
| 
 | ||||
|   return TRUE; | ||||
| } | ||||
| 
 | ||||
| static int functag_compare(const functag_t *formal, const functag_t *actual) | ||||
| { | ||||
|   // Check return types.
 | ||||
|   if (!matchreturntag(formal, actual)) | ||||
|     return FALSE; | ||||
| 
 | ||||
|   // Make sure there are no trailing arguments.
 | ||||
|   if (actual->argcount > formal->argcount) | ||||
|     return FALSE; | ||||
| 
 | ||||
|   // Check arguments.
 | ||||
|   for (int i = 0; i < formal->argcount; i++) { | ||||
|     const funcarg_t *formal_arg = &formal->args[i]; | ||||
| 
 | ||||
|     if (i >= actual->argcount) { | ||||
|       if (formal_arg->ommittable) | ||||
|         return TRUE; | ||||
|       return FALSE; | ||||
|     } | ||||
| 
 | ||||
|     const funcarg_t *actual_arg = &actual->args[i]; | ||||
|     if (!funcarg_compare(formal_arg, actual_arg)) | ||||
|       return FALSE; | ||||
|   } | ||||
| 
 | ||||
|   return TRUE; | ||||
| } | ||||
| 
 | ||||
| static int matchfunctags(int formaltag, int actualtag) | ||||
| { | ||||
|   if (formaltag == pc_functag && (actualtag & FUNCTAG)) | ||||
| @ -433,135 +499,17 @@ static int matchfunctags(int formaltag, int actualtag) | ||||
|   if (!(actualtag & FUNCTAG)) | ||||
|     return FALSE; | ||||
| 
 | ||||
|   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) | ||||
|   functag_t *actual = functag_find_intrinsic(actualtag); | ||||
|   if (!actual) | ||||
|     return FALSE; | ||||
| 
 | ||||
|   /* Now we have to go about looking up each function in this enum.  WHICH IS IT. */ | ||||
|   e = funcenums_find_byval(formaltag); | ||||
|   funcenum_t *e = funcenums_find_by_tag(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 = uSTOCK; | ||||
|   } | ||||
| 
 | ||||
|   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 == uSTOCK) { | ||||
|     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 (!matchreturntag(t, sym)) { | ||||
|       t = t->next; | ||||
|       continue; | ||||
|     } | ||||
|     /* Check usage */ | ||||
|     if (t->type != usage) { | ||||
|       t = t->next; | ||||
|       continue; | ||||
|     } | ||||
|     /* Begin iterating arguments */ | ||||
|     for (curarg=0; curarg<t->argcount; 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; i<enum_arg->dimcount; 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; i<enum_arg->tagcount; i++) { | ||||
|         if (!matchtag(func_arg->tags[i], enum_arg->tags[i], MATCHTAG_SILENT|MATCHTAG_COERCE)) { | ||||
|           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; | ||||
|   for (functag_t *formal = e->first; formal; formal = formal->next) { | ||||
|     if (functag_compare(formal, actual)) | ||||
|       return TRUE; | ||||
|   } | ||||
| 
 | ||||
|   return FALSE; | ||||
| @ -2281,48 +2229,36 @@ restart: | ||||
|   } /* if */ | ||||
|   if (sym!=NULL && lval1->ident==iFUNCTN) { | ||||
|     assert(sym->ident==iFUNCTN); | ||||
|     if (sc_allowproccall) { | ||||
|       // Note: this is unreachable in SourceMod, we don't support paren-less calls.
 | ||||
|       callfunction(sym,NULL,lval1,FALSE); | ||||
|     } else if ((sym->usage & uNATIVE) != uNATIVE) { | ||||
|       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->vclass == sGLOBAL && (!usage || (sym->usage & usage))) | ||||
|         { | ||||
|           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; | ||||
|         /* Generate a quick pseudo-tag! */ | ||||
|         if (usage == uPUBLIC) { | ||||
|           lval1->constval=(n<<1)|1; | ||||
|           snprintf(faketag, sizeof(faketag)-1, "$Func@%d", n); | ||||
|         } else { | ||||
|           lval1->constval=(code_addr<<1)|0; | ||||
|           snprintf(faketag, sizeof(faketag)-1, "$Func!%d", code_addr); | ||||
|           error(153); | ||||
|         } | ||||
|         lval1->tag=pc_addtag_flags(faketag, FIXEDTAG|FUNCTAG); | ||||
|         oldsym->usage |= uREAD; | ||||
|         sym->usage |= uREAD; | ||||
|       } else { | ||||
|         error(76);                /* invalid function call, or syntax error */ | ||||
|       } /* if */ | ||||
| 
 | ||||
|     if (sym->usage & uNATIVE) { | ||||
|       error(76); | ||||
|       return FALSE; | ||||
|     } else { | ||||
|       error(76);                  /* invalid function call, or syntax error */ | ||||
|     } | ||||
| 
 | ||||
|     int public_index = 0; | ||||
|     symbol *target = NULL; | ||||
|     for (symbol *iter = glbtab.next; iter; iter = iter->next) { | ||||
|       if (iter->ident != iFUNCTN || iter->vclass != sGLOBAL) | ||||
|         continue; | ||||
|       if (strcmp(iter->name, lval1->sym->name) == 0) { | ||||
|         target = iter; | ||||
|         break; | ||||
|       } | ||||
|       if (iter->usage & uPUBLIC) | ||||
|         public_index++; | ||||
|     } | ||||
| 
 | ||||
|     if (!target || !(target->usage & uPUBLIC)) { | ||||
|       error(76); | ||||
|       return FALSE; | ||||
|     } | ||||
| 
 | ||||
|     funcenum_t *fe = funcenum_for_symbol(target); | ||||
|     lval1->sym = NULL; | ||||
|     lval1->ident = iCONSTEXPR; | ||||
|     lval1->constval = (public_index << 1) | 1; | ||||
|     lval1->tag = fe->tag; | ||||
|     target->usage |= uREAD; | ||||
|   } /* if */ | ||||
|   return lvalue; | ||||
| } | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <assert.h> | ||||
| #include <stdarg.h> | ||||
| #include "sc.h" | ||||
| #include "sctracker.h" | ||||
| 
 | ||||
| @ -127,12 +128,12 @@ void funcenums_free() | ||||
|   lastenum = NULL; | ||||
| } | ||||
| 
 | ||||
| funcenum_t *funcenums_find_byval(int value) | ||||
| funcenum_t *funcenums_find_by_tag(int tag) | ||||
| { | ||||
|   funcenum_t *e = firstenum; | ||||
| 
 | ||||
|   while (e) { | ||||
|     if (e->value == value) | ||||
|     if (e->tag == tag) | ||||
|       return e; | ||||
|     e = e->next; | ||||
|   } | ||||
| @ -146,7 +147,7 @@ funcenum_t *funcenums_add(const char *name) | ||||
| 
 | ||||
|   memset(e, 0, sizeof(funcenum_t)); | ||||
| 
 | ||||
|   if (firstenum == NULL) { | ||||
|   if (!firstenum) { | ||||
|     firstenum = e; | ||||
|     lastenum = e; | ||||
|   } else { | ||||
| @ -155,11 +156,57 @@ funcenum_t *funcenums_add(const char *name) | ||||
|   } | ||||
| 
 | ||||
|   strcpy(e->name, name); | ||||
|   e->value = pc_addtag_flags((char *)name, FIXEDTAG|FUNCTAG); | ||||
|   e->tag = pc_addtag_flags((char *)name, FIXEDTAG|FUNCTAG); | ||||
| 
 | ||||
|   return e; | ||||
| } | ||||
| 
 | ||||
| funcenum_t *funcenum_for_symbol(symbol *sym) | ||||
| { | ||||
|   functag_t ft; | ||||
|   memset(&ft, 0, sizeof(ft)); | ||||
| 
 | ||||
|   ft.ret_tag = sym->tag; | ||||
|   ft.usage = uPUBLIC & (sym->usage & uRETVALUE); | ||||
|   ft.argcount = 0; | ||||
|   ft.ommittable = FALSE; | ||||
|   for (arginfo *arg = sym->dim.arglist; arg->ident; arg++) { | ||||
|     funcarg_t *dest = &ft.args[ft.argcount++]; | ||||
| 
 | ||||
|     dest->tagcount = arg->numtags; | ||||
|     memcpy(dest->tags, arg->tags, arg->numtags * sizeof(int)); | ||||
| 
 | ||||
|     dest->dimcount = arg->numdim; | ||||
|     memcpy(dest->dims, arg->dim, arg->numdim * sizeof(int)); | ||||
| 
 | ||||
|     dest->ident = arg->ident; | ||||
|     dest->fconst = !!(arg->usage & uCONST); | ||||
|     dest->ommittable = FALSE; | ||||
|   } | ||||
| 
 | ||||
|   char name[METHOD_NAMEMAX+1]; | ||||
|   UTIL_Format(name, sizeof(name), "::ft:%s:%d:%d", sym->name, sym->addr, sym->codeaddr); | ||||
| 
 | ||||
|   funcenum_t *fe = funcenums_add(name); | ||||
|   functags_add(fe, &ft); | ||||
| 
 | ||||
|   return fe; | ||||
| } | ||||
| 
 | ||||
| // Finds a functag that was created intrinsically.
 | ||||
| functag_t *functag_find_intrinsic(int tag) | ||||
| { | ||||
|   funcenum_t *fe = funcenums_find_by_tag(tag); | ||||
|   if (!fe) | ||||
|     return NULL; | ||||
| 
 | ||||
|   if (strncmp(fe->name, "::ft:", 5) != 0) | ||||
|     return NULL; | ||||
| 
 | ||||
|   assert(fe->first && fe->first == fe->last); | ||||
|   return fe->first; | ||||
| } | ||||
| 
 | ||||
| functag_t *functags_add(funcenum_t *en, functag_t *src) | ||||
| { | ||||
|   functag_t *t = (functag_t *)malloc(sizeof(functag_t)); | ||||
| @ -550,3 +597,18 @@ int can_redef_layout_spec(LayoutSpec def1, LayoutSpec def2) | ||||
|   } | ||||
|   return FALSE; | ||||
| } | ||||
| 
 | ||||
| size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...) | ||||
| { | ||||
|   va_list ap; | ||||
| 
 | ||||
|   va_start(ap, fmt); | ||||
|   size_t len = vsnprintf(buffer, maxlength, fmt, ap); | ||||
|   va_end(ap); | ||||
| 
 | ||||
|   if (len >= maxlength) { | ||||
|     buffer[maxlength - 1] = '\0'; | ||||
|     return maxlength - 1; | ||||
|   } | ||||
|   return len; | ||||
| } | ||||
|  | ||||
| @ -31,7 +31,7 @@ typedef struct funcarg_s | ||||
| typedef struct functag_s | ||||
| { | ||||
|   int ret_tag; | ||||
|   int type; | ||||
|   int usage; | ||||
|   int argcount; | ||||
|   int ommittable; | ||||
|   funcarg_t args[sARGS_MAX]; | ||||
| @ -40,8 +40,8 @@ typedef struct functag_s | ||||
| 
 | ||||
| typedef struct funcenum_s | ||||
| { | ||||
|   int value; | ||||
|   char name[sNAMEMAX+1]; | ||||
|   int tag; | ||||
|   char name[METHOD_NAMEMAX+1]; | ||||
|   functag_t *first; | ||||
|   functag_t *last; | ||||
|   struct funcenum_s *next; | ||||
| @ -116,8 +116,10 @@ structarg_t *pstructs_getarg(pstruct_t *pstruct, const char *member); | ||||
|  */ | ||||
| void funcenums_free(); | ||||
| funcenum_t *funcenums_add(const char *name); | ||||
| funcenum_t *funcenums_find_byval(int value); | ||||
| funcenum_t *funcenums_find_by_tag(int tag); | ||||
| functag_t *functags_add(funcenum_t *en, functag_t *src); | ||||
| funcenum_t *funcenum_for_symbol(symbol *sym); | ||||
| functag_t *functag_find_intrinsic(int tag); | ||||
| 
 | ||||
| /**
 | ||||
|  * Given a name or tag, find any extra weirdness it has associated with it. | ||||
| @ -167,4 +169,6 @@ void methodmaps_free(); | ||||
| extern memuse_list_t *heapusage; | ||||
| extern memuse_list_t *stackusage; | ||||
| 
 | ||||
| size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...); | ||||
| 
 | ||||
| #endif //_INCLUDE_SOURCEPAWN_COMPILER_TRACKER_H_
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user