615 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			615 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* vim: set ts=8 sts=2 sw=2 tw=99 et: */
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <assert.h>
 | |
| #include <stdarg.h>
 | |
| #include "sc.h"
 | |
| #include "sctracker.h"
 | |
| 
 | |
| memuse_list_t *heapusage = NULL;
 | |
| memuse_list_t *stackusage = NULL;
 | |
| funcenum_t *firstenum = NULL;
 | |
| funcenum_t *lastenum = NULL;
 | |
| pstruct_t *firststruct = NULL;
 | |
| pstruct_t *laststruct = NULL;
 | |
| methodmap_t *methodmap_first = NULL;
 | |
| methodmap_t *methodmap_last = NULL;
 | |
| 
 | |
| structarg_t *pstructs_getarg(pstruct_t *pstruct, const char *member)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   for (i=0; i<pstruct->argcount; i++) {
 | |
|     if (strcmp(pstruct->args[i]->name, member) == 0)
 | |
|       return pstruct->args[i];
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| pstruct_t *pstructs_add(const char *name)
 | |
| {
 | |
|   pstruct_t *p = (pstruct_t *)malloc(sizeof(pstruct_t));
 | |
|   
 | |
|   memset(p, 0, sizeof(pstruct_t));
 | |
|   strcpy(p->name, name);
 | |
|   
 | |
|   if (!firststruct) {
 | |
|     firststruct = p;
 | |
|     laststruct = p;
 | |
|   } else {
 | |
|     laststruct->next = p;
 | |
|     laststruct = p;
 | |
|   }
 | |
| 
 | |
|   return p;
 | |
| }
 | |
| 
 | |
| void pstructs_free()
 | |
| {
 | |
|   pstruct_t *p, *next;
 | |
| 
 | |
|   p = firststruct;
 | |
|   while (p) {
 | |
|     while (p->argcount--)
 | |
|       free(p->args[p->argcount]);
 | |
|     free(p->args);
 | |
|     next = p->next;
 | |
|     free(p);
 | |
|     p = next;
 | |
|   }
 | |
|   firststruct = NULL;
 | |
|   laststruct = NULL;
 | |
| }
 | |
| 
 | |
| pstruct_t *pstructs_find(const char *name)
 | |
| {
 | |
|   pstruct_t *p = firststruct;
 | |
| 
 | |
|   while (p) {
 | |
|     if (strcmp(p->name, name) == 0)
 | |
|       return p;
 | |
|     p = p->next;
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| structarg_t *pstructs_addarg(pstruct_t *pstruct, const structarg_t *arg)
 | |
| {
 | |
|   structarg_t *newarg;
 | |
|   int i;
 | |
| 
 | |
|   for (i=0; i<pstruct->argcount; i++) {
 | |
|     if (strcmp(pstruct->args[i]->name, arg->name) == 0) {
 | |
|       /* Don't allow dup names */
 | |
|       return NULL;
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   newarg = (structarg_t *)malloc(sizeof(structarg_t));
 | |
| 
 | |
|   memcpy(newarg, arg, sizeof(structarg_t));
 | |
| 
 | |
|   if (pstruct->argcount == 0) {
 | |
|     pstruct->args = (structarg_t **)malloc(sizeof(structarg_t *) * 1);
 | |
|   } else {
 | |
|     pstruct->args = (structarg_t **)realloc(
 | |
|               pstruct->args,
 | |
|               sizeof(structarg_t *) * (pstruct->argcount + 1));
 | |
|   }
 | |
| 
 | |
|   newarg->offs = pstruct->argcount * sizeof(cell);
 | |
|   newarg->index = pstruct->argcount;
 | |
|   pstruct->args[pstruct->argcount++] = newarg;
 | |
| 
 | |
|   return newarg;
 | |
| }
 | |
| 
 | |
| 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_by_tag(int tag)
 | |
| {
 | |
|   funcenum_t *e = firstenum;
 | |
| 
 | |
|   while (e) {
 | |
|     if (e->tag == tag)
 | |
|       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) {
 | |
|     firstenum = e;
 | |
|     lastenum = e;
 | |
|   } else {
 | |
|     lastenum->next = e;
 | |
|     lastenum = e;
 | |
|   }
 | |
| 
 | |
|   strcpy(e->name, name);
 | |
|   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));
 | |
|   
 | |
|   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
 | |
|  */
 | |
| void _push_memlist(memuse_list_t **head)
 | |
| {
 | |
|   memuse_list_t *newlist = (memuse_list_t *)malloc(sizeof(memuse_list_t));
 | |
|   if (*head != NULL)
 | |
|   {
 | |
|     newlist->list_id = (*head)->list_id + 1;
 | |
|   } else {
 | |
|     newlist->list_id = 0;
 | |
|   }
 | |
|   newlist->prev = *head;
 | |
|   newlist->head = NULL;
 | |
|   *head = newlist;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Pops a heap list but does not free it.
 | |
|  */
 | |
| memuse_list_t *_pop_save_memlist(memuse_list_t **head)
 | |
| {
 | |
|   memuse_list_t *oldlist = *head;
 | |
|   *head = (*head)->prev;
 | |
|   return oldlist;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Marks a memory usage on a memory list
 | |
|  */
 | |
| int _mark_memlist(memuse_list_t *head, int type, int size)
 | |
| {
 | |
|   memuse_t *use;
 | |
|   if (type==MEMUSE_STATIC && size==0)
 | |
|   {
 | |
|     return 0;
 | |
|   }
 | |
|   use=head->head;
 | |
|   if (use && (type==MEMUSE_STATIC) 
 | |
|       && (use->type == type))
 | |
|   {
 | |
|     use->size += size;
 | |
|   } else {
 | |
|     use=(memuse_t *)malloc(sizeof(memuse_t));
 | |
|     use->type=type;
 | |
|     use->size=size;
 | |
|     use->prev=head->head;
 | |
|     head->head=use;
 | |
|   }
 | |
|   return size;
 | |
| }
 | |
| 
 | |
| void _reset_memlist(memuse_list_t **head)
 | |
| {
 | |
|   memuse_list_t *curlist = *head;
 | |
|   memuse_list_t *tmplist;
 | |
|   while (curlist) {
 | |
|     memuse_t *curuse = curlist->head;
 | |
|     memuse_t *tmpuse;
 | |
|     while (curuse) {
 | |
|       tmpuse = curuse->prev;
 | |
|       free(curuse);
 | |
|       curuse = tmpuse;
 | |
|     }
 | |
|     tmplist = curlist->prev;
 | |
|     free(curlist);
 | |
|     curlist = tmplist;
 | |
|   }
 | |
|   *head = NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Wrapper for pushing the heap list
 | |
|  */
 | |
| void pushheaplist()
 | |
| {
 | |
|   _push_memlist(&heapusage);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Wrapper for popping and saving the heap list
 | |
|  */
 | |
| memuse_list_t *popsaveheaplist()
 | |
| {
 | |
|   return _pop_save_memlist(&heapusage);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Wrapper for marking the heap
 | |
|  */
 | |
| int markheap(int type, int size)
 | |
| {
 | |
|   return _mark_memlist(heapusage, type, size);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Wrapper for pushing the stack list
 | |
|  */
 | |
| void pushstacklist()
 | |
| {
 | |
|   _push_memlist(&stackusage);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Wrapper for marking the stack
 | |
|  */
 | |
| int markstack(int type, int size)
 | |
| {
 | |
|   return _mark_memlist(stackusage, type, size);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Generates code to free all heap allocations on a tracker
 | |
|  */
 | |
| void _heap_freeusage(memuse_list_t *heap, int dofree)
 | |
| {
 | |
|   memuse_t *cur=heap->head;
 | |
|   memuse_t *tmp;
 | |
|   while (cur) {
 | |
|     if (cur->type == MEMUSE_STATIC) {
 | |
|       modheap((-1)*cur->size*sizeof(cell));
 | |
|     } else {
 | |
|       modheap_i();
 | |
|     }
 | |
|     if (dofree) {
 | |
|       tmp=cur->prev;
 | |
|       free(cur);
 | |
|       cur=tmp;
 | |
|     } else {
 | |
|       cur=cur->prev;
 | |
|     }
 | |
|   }
 | |
|   if (dofree)
 | |
|     heap->head=NULL;
 | |
| }
 | |
| 
 | |
| void _stack_genusage(memuse_list_t *stack, int dofree)
 | |
| {
 | |
|   memuse_t *cur = stack->head;
 | |
|   memuse_t *tmp;
 | |
|   while (cur)
 | |
|   {
 | |
|     if (cur->type == MEMUSE_DYNAMIC)
 | |
|     {
 | |
|       /* no idea yet */
 | |
|       assert(0);
 | |
|     } else {
 | |
|       modstk(cur->size * sizeof(cell));
 | |
|     }
 | |
|     if (dofree)
 | |
|     {
 | |
|       tmp = cur->prev;
 | |
|       free(cur);
 | |
|       cur = tmp;
 | |
|     } else {
 | |
|       cur = cur->prev;
 | |
|     }
 | |
|   }
 | |
|   if (dofree)
 | |
|   {
 | |
|     stack->head = NULL;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Pops a heap list and frees it.
 | |
|  */
 | |
| void popheaplist()
 | |
| {
 | |
|   memuse_list_t *oldlist;
 | |
|   assert(heapusage!=NULL);
 | |
| 
 | |
|   _heap_freeusage(heapusage, 1);
 | |
|   assert(heapusage->head==NULL);
 | |
| 
 | |
|   oldlist=heapusage->prev;
 | |
|   free(heapusage);
 | |
|   heapusage=oldlist;
 | |
| }
 | |
| 
 | |
| void genstackfree(int stop_id)
 | |
| {
 | |
|   memuse_list_t *curlist = stackusage;
 | |
|   while (curlist && curlist->list_id > stop_id)
 | |
|   {
 | |
|     _stack_genusage(curlist, 0);
 | |
|     curlist = curlist->prev;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void genheapfree(int stop_id)
 | |
| {
 | |
|   memuse_list_t *curlist = heapusage;
 | |
|   while (curlist && curlist->list_id > stop_id)
 | |
|   {
 | |
|     _heap_freeusage(curlist, 0);
 | |
|     curlist = curlist->prev;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void popstacklist(int codegen)
 | |
| {
 | |
|   memuse_list_t *oldlist;
 | |
|   assert(stackusage != NULL);
 | |
| 
 | |
|   if (codegen)
 | |
|   {
 | |
|     _stack_genusage(stackusage, 1);
 | |
|     assert(stackusage->head==NULL);
 | |
|   } else {
 | |
|     memuse_t *use = stackusage->head;
 | |
|     while (use) {
 | |
|       memuse_t *temp = use->prev;
 | |
|       free(use);
 | |
|       use = temp;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   oldlist = stackusage->prev;
 | |
|   free(stackusage);
 | |
|   stackusage = oldlist;
 | |
| }
 | |
| 
 | |
| void resetstacklist()
 | |
| {
 | |
|   _reset_memlist(&stackusage);
 | |
| }
 | |
| 
 | |
| void resetheaplist()
 | |
| {
 | |
|   _reset_memlist(&heapusage);
 | |
| }
 | |
| 
 | |
| void methodmap_add(methodmap_t *map)
 | |
| {
 | |
|   if (!methodmap_first) {
 | |
|     methodmap_first = map;
 | |
|     methodmap_last = map;
 | |
|   } else {
 | |
|     methodmap_last->next = map;
 | |
|     methodmap_last = map;
 | |
|   }
 | |
| }
 | |
| 
 | |
| methodmap_t *methodmap_find_by_tag(int tag)
 | |
| {
 | |
|   methodmap_t *ptr = methodmap_first;
 | |
|   for (; ptr; ptr = ptr->next) {
 | |
|     if (ptr->tag == tag)
 | |
|       return ptr;
 | |
|   }
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| methodmap_t *methodmap_find_by_name(const char *name)
 | |
| {
 | |
|   int tag = pc_findtag(name);
 | |
|   if (tag == -1)
 | |
|     return NULL;
 | |
|   return methodmap_find_by_tag(tag);
 | |
| }
 | |
| 
 | |
| methodmap_method_t *methodmap_find_method(methodmap_t *map, const char *name)
 | |
| {
 | |
|   size_t i;
 | |
|   for (i = 0; i < map->nummethods; i++) {
 | |
|     if (strcmp(map->methods[i]->name, name) == 0)
 | |
|       return map->methods[i];
 | |
|   }
 | |
|   if (map->parent)
 | |
|     return methodmap_find_method(map->parent, name);
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| void methodmaps_free()
 | |
| {
 | |
|   methodmap_t *ptr = methodmap_first;
 | |
|   while (ptr) {
 | |
|     methodmap_t *next = ptr->next;
 | |
|     for (size_t i = 0; i < ptr->nummethods; i++)
 | |
|       free(ptr->methods[i]);
 | |
|     free(ptr->methods);
 | |
|     free(ptr);
 | |
|     ptr = next;
 | |
|   }
 | |
|   methodmap_first = NULL;
 | |
|   methodmap_last = NULL;
 | |
| }
 | |
| 
 | |
| LayoutSpec deduce_layout_spec_by_tag(int tag)
 | |
| {
 | |
|   symbol *sym;
 | |
|   const char *name;
 | |
|   methodmap_t *map;
 | |
|   if ((map = methodmap_find_by_tag(tag)) != NULL)
 | |
|     return map->spec;
 | |
|   if (tag & FUNCTAG)
 | |
|     return Layout_FuncTag;
 | |
| 
 | |
|   name = pc_tagname(tag);
 | |
|   if (pstructs_find(name))
 | |
|     return Layout_PawnStruct;
 | |
|   if ((sym = findglb(name, sGLOBAL)) != NULL)
 | |
|     return Layout_Enum;
 | |
| 
 | |
|   return Layout_None;
 | |
| }
 | |
| 
 | |
| LayoutSpec deduce_layout_spec_by_name(const char *name)
 | |
| {
 | |
|   symbol *sym;
 | |
|   methodmap_t *map;
 | |
|   int tag = pc_findtag(name);
 | |
|   if (tag != -1 && (tag & FUNCTAG))
 | |
|     return Layout_FuncTag;
 | |
|   if (pstructs_find(name))
 | |
|     return Layout_PawnStruct;
 | |
|   if ((map = methodmap_find_by_name(name)) != NULL)
 | |
|     return map->spec;
 | |
|   if ((sym = findglb(name, sGLOBAL)) != NULL)
 | |
|     return Layout_Enum;
 | |
| 
 | |
|   return Layout_None;
 | |
| }
 | |
| 
 | |
| const char *layout_spec_name(LayoutSpec spec)
 | |
| {
 | |
|   switch (spec) {
 | |
|     case Layout_None:
 | |
|       return "<none>";
 | |
|     case Layout_Enum:
 | |
|       return "enum";
 | |
|     case Layout_FuncTag:
 | |
|       return "functag";
 | |
|     case Layout_PawnStruct:
 | |
|       return "deprecated-struct";
 | |
|     case Layout_MethodMap:
 | |
|       return "methodmap";
 | |
|     case Layout_Class:
 | |
|       return "class";
 | |
|   }
 | |
|   return "<unknown>";
 | |
| }
 | |
| 
 | |
| int can_redef_layout_spec(LayoutSpec def1, LayoutSpec def2)
 | |
| {
 | |
|   // Normalize the ordering, since these checks are symmetrical.
 | |
|   if (def1 > def2) {
 | |
|     LayoutSpec temp = def2;
 | |
|     def2 = def1;
 | |
|     def1 = temp;
 | |
|   }
 | |
| 
 | |
|   switch (def1) {
 | |
|     case Layout_None:
 | |
|       return TRUE;
 | |
|     case Layout_Enum:
 | |
|       if (def2 == Layout_Enum || def2 == Layout_FuncTag)
 | |
|         return TRUE;
 | |
|       return def2 == Layout_MethodMap;
 | |
|     case Layout_FuncTag:
 | |
|       return def2 == Layout_Enum || def2 == Layout_FuncTag;
 | |
|     case Layout_PawnStruct:
 | |
|     case Layout_MethodMap:
 | |
|       return FALSE;
 | |
|     case Layout_Class:
 | |
|       return FALSE;
 | |
|   }
 | |
|   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;
 | |
| }
 |