| // Inferno utils/cc/dpchk.c | |
| // http://code.google.com/p/inferno-os/source/browse/utils/cc/dpchk.c | |
| // | |
| // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. | |
| // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) | |
| // Portions Copyright © 1997-1999 Vita Nuova Limited | |
| // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) | |
| // Portions Copyright © 2004,2006 Bruce Ellis | |
| // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) | |
| // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others | |
| // Portions Copyright © 2009 The Go Authors. All rights reserved. | |
| // | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy | |
| // of this software and associated documentation files (the "Software"), to deal | |
| // in the Software without restriction, including without limitation the rights | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| // copies of the Software, and to permit persons to whom the Software is | |
| // furnished to do so, subject to the following conditions: | |
| // | |
| // The above copyright notice and this permission notice shall be included in | |
| // all copies or substantial portions of the Software. | |
| // | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| // THE SOFTWARE. | |
| #include <u.h> | |
| #include "cc.h" | |
| #include "y.tab.h" | |
| enum | |
| { | |
| Fnone = 0, | |
| Fl, | |
| Fvl, | |
| Fignor, | |
| Fstar, | |
| Fadj, | |
| Fverb = 10, | |
| }; | |
| typedef struct Tprot Tprot; | |
| struct Tprot | |
| { | |
| Type* type; | |
| Bits flag; | |
| Tprot* link; | |
| }; | |
| typedef struct Tname Tname; | |
| struct Tname | |
| { | |
| char* name; | |
| int param; | |
| int count; | |
| Tname* link; | |
| Tprot* prot; | |
| }; | |
| static Type* indchar; | |
| static uchar flagbits[512]; | |
| static char* lastfmt; | |
| static int lastadj; | |
| static int lastverb; | |
| static int nstar; | |
| static Tprot* tprot; | |
| static Tname* tname; | |
| void | |
| argflag(int c, int v) | |
| { | |
| switch(v) { | |
| case Fignor: | |
| case Fstar: | |
| case Fl: | |
| case Fvl: | |
| flagbits[c] = v; | |
| break; | |
| case Fverb: | |
| flagbits[c] = lastverb; | |
| /*print("flag-v %c %d\n", c, lastadj);*/ | |
| lastverb++; | |
| break; | |
| case Fadj: | |
| flagbits[c] = lastadj; | |
| /*print("flag-l %c %d\n", c, lastadj);*/ | |
| lastadj++; | |
| break; | |
| } | |
| } | |
| Bits | |
| getflag(char *s) | |
| { | |
| Bits flag; | |
| int f; | |
| Fmt fmt; | |
| Rune c; | |
| flag = zbits; | |
| nstar = 0; | |
| fmtstrinit(&fmt); | |
| for(;;) { | |
| s += chartorune(&c, s); | |
| if(c == 0 || c >= nelem(flagbits)) | |
| break; | |
| fmtrune(&fmt, c); | |
| f = flagbits[c]; | |
| switch(f) { | |
| case Fnone: | |
| argflag(c, Fverb); | |
| f = flagbits[c]; | |
| break; | |
| case Fstar: | |
| nstar++; | |
| case Fignor: | |
| continue; | |
| case Fl: | |
| if(bset(flag, Fl)) | |
| flag = bor(flag, blsh(Fvl)); | |
| } | |
| flag = bor(flag, blsh(f)); | |
| if(f >= Fverb) | |
| break; | |
| } | |
| free(lastfmt); | |
| lastfmt = fmtstrflush(&fmt); | |
| return flag; | |
| } | |
| static void | |
| newprot(Sym *m, Type *t, char *s, Tprot **prot) | |
| { | |
| Bits flag; | |
| Tprot *l; | |
| if(t == T) { | |
| warn(Z, "%s: newprot: type not defined", m->name); | |
| return; | |
| } | |
| flag = getflag(s); | |
| for(l=*prot; l; l=l->link) | |
| if(beq(flag, l->flag) && sametype(t, l->type)) | |
| return; | |
| l = alloc(sizeof(*l)); | |
| l->type = t; | |
| l->flag = flag; | |
| l->link = *prot; | |
| *prot = l; | |
| } | |
| static Tname* | |
| newname(char *s, int p, int count) | |
| { | |
| Tname *l; | |
| for(l=tname; l; l=l->link) | |
| if(strcmp(l->name, s) == 0) { | |
| if(p >= 0 && l->param != p) | |
| yyerror("vargck %s already defined\n", s); | |
| return l; | |
| } | |
| if(p < 0) | |
| return nil; | |
| l = alloc(sizeof(*l)); | |
| l->name = s; | |
| l->param = p; | |
| l->link = tname; | |
| l->count = count; | |
| tname = l; | |
| return l; | |
| } | |
| void | |
| arginit(void) | |
| { | |
| int i; | |
| /* debug['F'] = 1;*/ | |
| /* debug['w'] = 1;*/ | |
| lastadj = Fadj; | |
| lastverb = Fverb; | |
| indchar = typ(TIND, types[TCHAR]); | |
| memset(flagbits, Fnone, sizeof(flagbits)); | |
| for(i='0'; i<='9'; i++) | |
| argflag(i, Fignor); | |
| argflag('.', Fignor); | |
| argflag('#', Fignor); | |
| argflag('u', Fignor); | |
| argflag('h', Fignor); | |
| argflag('+', Fignor); | |
| argflag('-', Fignor); | |
| argflag('*', Fstar); | |
| argflag('l', Fl); | |
| argflag('o', Fverb); | |
| flagbits['x'] = flagbits['o']; | |
| flagbits['X'] = flagbits['o']; | |
| } | |
| static char* | |
| getquoted(void) | |
| { | |
| int c; | |
| Rune r; | |
| Fmt fmt; | |
| c = getnsc(); | |
| if(c != '"') | |
| return nil; | |
| fmtstrinit(&fmt); | |
| for(;;) { | |
| r = getr(); | |
| if(r == '\n') { | |
| free(fmtstrflush(&fmt)); | |
| return nil; | |
| } | |
| if(r == '"') | |
| break; | |
| fmtrune(&fmt, r); | |
| } | |
| free(lastfmt); | |
| lastfmt = fmtstrflush(&fmt); | |
| return strdup(lastfmt); | |
| } | |
| void | |
| pragvararg(void) | |
| { | |
| Sym *s; | |
| int n, c; | |
| char *t; | |
| Type *ty; | |
| Tname *l; | |
| if(!debug['F']) | |
| goto out; | |
| s = getsym(); | |
| if(s && strcmp(s->name, "argpos") == 0) | |
| goto ckpos; | |
| if(s && strcmp(s->name, "type") == 0) | |
| goto cktype; | |
| if(s && strcmp(s->name, "flag") == 0) | |
| goto ckflag; | |
| if(s && strcmp(s->name, "countpos") == 0) | |
| goto ckcount; | |
| yyerror("syntax in #pragma varargck"); | |
| goto out; | |
| ckpos: | |
| /*#pragma varargck argpos warn 2*/ | |
| s = getsym(); | |
| if(s == S) | |
| goto bad; | |
| n = getnsn(); | |
| if(n < 0) | |
| goto bad; | |
| newname(s->name, n, 0); | |
| goto out; | |
| ckcount: | |
| /*#pragma varargck countpos name 2*/ | |
| s = getsym(); | |
| if(s == S) | |
| goto bad; | |
| n = getnsn(); | |
| if(n < 0) | |
| goto bad; | |
| newname(s->name, 0, n); | |
| goto out; | |
| ckflag: | |
| /*#pragma varargck flag 'c'*/ | |
| c = getnsc(); | |
| if(c != '\'') | |
| goto bad; | |
| c = getr(); | |
| if(c == '\\') | |
| c = getr(); | |
| else if(c == '\'') | |
| goto bad; | |
| if(c == '\n') | |
| goto bad; | |
| if(getc() != '\'') | |
| goto bad; | |
| argflag(c, Fignor); | |
| goto out; | |
| cktype: | |
| c = getnsc(); | |
| unget(c); | |
| if(c != '"') { | |
| /*#pragma varargck type name int*/ | |
| s = getsym(); | |
| if(s == S) | |
| goto bad; | |
| l = newname(s->name, -1, -1); | |
| s = getsym(); | |
| if(s == S) | |
| goto bad; | |
| ty = s->type; | |
| while((c = getnsc()) == '*') | |
| ty = typ(TIND, ty); | |
| unget(c); | |
| newprot(s, ty, "a", &l->prot); | |
| goto out; | |
| } | |
| /*#pragma varargck type O int*/ | |
| t = getquoted(); | |
| if(t == nil) | |
| goto bad; | |
| s = getsym(); | |
| if(s == S) | |
| goto bad; | |
| ty = s->type; | |
| while((c = getnsc()) == '*') | |
| ty = typ(TIND, ty); | |
| unget(c); | |
| newprot(s, ty, t, &tprot); | |
| goto out; | |
| bad: | |
| yyerror("syntax in #pragma varargck"); | |
| out: | |
| while(getnsc() != '\n') | |
| ; | |
| } | |
| Node* | |
| nextarg(Node *n, Node **a) | |
| { | |
| if(n == Z) { | |
| *a = Z; | |
| return Z; | |
| } | |
| if(n->op == OLIST) { | |
| *a = n->left; | |
| return n->right; | |
| } | |
| *a = n; | |
| return Z; | |
| } | |
| void | |
| checkargs(Node *nn, char *s, int pos) | |
| { | |
| Node *a, *n; | |
| Bits flag; | |
| Tprot *l; | |
| if(!debug['F']) | |
| return; | |
| n = nn; | |
| for(;;) { | |
| s = strchr(s, '%'); | |
| if(s == 0) { | |
| nextarg(n, &a); | |
| if(a != Z) | |
| warn(nn, "more arguments than format %T", | |
| a->type); | |
| return; | |
| } | |
| s++; | |
| flag = getflag(s); | |
| while(nstar > 0) { | |
| n = nextarg(n, &a); | |
| pos++; | |
| nstar--; | |
| if(a == Z) { | |
| warn(nn, "more format than arguments %s", | |
| lastfmt); | |
| return; | |
| } | |
| if(a->type == T) | |
| continue; | |
| if(!sametype(types[TINT], a->type) && | |
| !sametype(types[TUINT], a->type)) | |
| warn(nn, "format mismatch '*' in %s %T, arg %d", | |
| lastfmt, a->type, pos); | |
| } | |
| for(l=tprot; l; l=l->link) | |
| if(sametype(types[TVOID], l->type)) { | |
| if(beq(flag, l->flag)) { | |
| s++; | |
| goto loop; | |
| } | |
| } | |
| n = nextarg(n, &a); | |
| pos++; | |
| if(a == Z) { | |
| warn(nn, "more format than arguments %s", | |
| lastfmt); | |
| return; | |
| } | |
| if(a->type == 0) | |
| continue; | |
| for(l=tprot; l; l=l->link) | |
| if(sametype(a->type, l->type)) { | |
| /*print("checking %T/%ux %T/%ux\n", a->type, flag.b[0], l->type, l->flag.b[0]);*/ | |
| if(beq(flag, l->flag)) | |
| goto loop; | |
| } | |
| warn(nn, "format mismatch %s %T, arg %d", lastfmt, a->type, pos); | |
| loop:; | |
| } | |
| } | |
| void | |
| dpcheck(Node *n) | |
| { | |
| char *s; | |
| Node *a, *b; | |
| Tname *l; | |
| Tprot *tl; | |
| int i, j; | |
| if(n == Z) | |
| return; | |
| b = n->left; | |
| if(b == Z || b->op != ONAME) | |
| return; | |
| s = b->sym->name; | |
| for(l=tname; l; l=l->link) | |
| if(strcmp(s, l->name) == 0) | |
| break; | |
| if(l == 0) | |
| return; | |
| if(l->count > 0) { | |
| // fetch count, then check remaining length | |
| i = l->count; | |
| a = nil; | |
| b = n->right; | |
| while(i > 0) { | |
| b = nextarg(b, &a); | |
| i--; | |
| } | |
| if(a == Z) { | |
| diag(n, "can't find count arg"); | |
| return; | |
| } | |
| if(a->op != OCONST || !typechl[a->type->etype]) { | |
| diag(n, "count is invalid constant"); | |
| return; | |
| } | |
| j = a->vconst; | |
| i = 0; | |
| while(b != Z) { | |
| b = nextarg(b, &a); | |
| i++; | |
| } | |
| if(i != j) | |
| diag(n, "found %d argument%s after count %d", i, i == 1 ? "" : "s", j); | |
| } | |
| if(l->prot != nil) { | |
| // check that all arguments after param or count | |
| // are listed in type list. | |
| i = l->count; | |
| if(i == 0) | |
| i = l->param; | |
| if(i == 0) | |
| return; | |
| a = nil; | |
| b = n->right; | |
| while(i > 0) { | |
| b = nextarg(b, &a); | |
| i--; | |
| } | |
| if(a == Z) { | |
| diag(n, "can't find count/param arg"); | |
| return; | |
| } | |
| while(b != Z) { | |
| b = nextarg(b, &a); | |
| for(tl=l->prot; tl; tl=tl->link) | |
| if(sametype(a->type, tl->type)) | |
| break; | |
| if(tl == nil) | |
| diag(a, "invalid type %T in call to %s", a->type, s); | |
| } | |
| } | |
| if(l->param <= 0) | |
| return; | |
| i = l->param; | |
| a = nil; | |
| b = n->right; | |
| while(i > 0) { | |
| b = nextarg(b, &a); | |
| i--; | |
| } | |
| if(a == Z) { | |
| diag(n, "can't find format arg"); | |
| return; | |
| } | |
| if(!sametype(indchar, a->type)) { | |
| diag(n, "format arg type %T", a->type); | |
| return; | |
| } | |
| if(a->op != OADDR || a->left->op != ONAME || a->left->sym != symstring) { | |
| /* warn(n, "format arg not constant string");*/ | |
| return; | |
| } | |
| s = a->left->cstring; | |
| checkargs(b, s, l->param); | |
| } | |
| void | |
| pragpack(void) | |
| { | |
| Sym *s; | |
| packflg = 0; | |
| s = getsym(); | |
| if(s) { | |
| packflg = atoi(s->name+1); | |
| if(strcmp(s->name, "on") == 0 || | |
| strcmp(s->name, "yes") == 0) | |
| packflg = 1; | |
| } | |
| while(getnsc() != '\n') | |
| ; | |
| if(debug['f']) | |
| if(packflg) | |
| print("%4d: pack %d\n", lineno, packflg); | |
| else | |
| print("%4d: pack off\n", lineno); | |
| } | |
| void | |
| pragfpround(void) | |
| { | |
| Sym *s; | |
| fproundflg = 0; | |
| s = getsym(); | |
| if(s) { | |
| fproundflg = atoi(s->name+1); | |
| if(strcmp(s->name, "on") == 0 || | |
| strcmp(s->name, "yes") == 0) | |
| fproundflg = 1; | |
| } | |
| while(getnsc() != '\n') | |
| ; | |
| if(debug['f']) | |
| if(fproundflg) | |
| print("%4d: fproundflg %d\n", lineno, fproundflg); | |
| else | |
| print("%4d: fproundflg off\n", lineno); | |
| } | |
| void | |
| pragtextflag(void) | |
| { | |
| Sym *s; | |
| textflag = 0; | |
| s = getsym(); | |
| textflag = 7; | |
| if(s) | |
| textflag = atoi(s->name+1); | |
| while(getnsc() != '\n') | |
| ; | |
| if(debug['f']) | |
| print("%4d: textflag %d\n", lineno, textflag); | |
| } | |
| void | |
| pragincomplete(void) | |
| { | |
| Sym *s; | |
| Type *t; | |
| int istag, w, et; | |
| istag = 0; | |
| s = getsym(); | |
| if(s == nil) | |
| goto out; | |
| et = 0; | |
| w = s->lexical; | |
| if(w == LSTRUCT) | |
| et = TSTRUCT; | |
| else if(w == LUNION) | |
| et = TUNION; | |
| if(et != 0){ | |
| s = getsym(); | |
| if(s == nil){ | |
| yyerror("missing struct/union tag in pragma incomplete"); | |
| goto out; | |
| } | |
| if(s->lexical != LNAME && s->lexical != LTYPE){ | |
| yyerror("invalid struct/union tag: %s", s->name); | |
| goto out; | |
| } | |
| dotag(s, et, 0); | |
| istag = 1; | |
| }else if(strcmp(s->name, "_off_") == 0){ | |
| debug['T'] = 0; | |
| goto out; | |
| }else if(strcmp(s->name, "_on_") == 0){ | |
| debug['T'] = 1; | |
| goto out; | |
| } | |
| t = s->type; | |
| if(istag) | |
| t = s->suetag; | |
| if(t == T) | |
| yyerror("unknown type %s in pragma incomplete", s->name); | |
| else if(!typesu[t->etype]) | |
| yyerror("not struct/union type in pragma incomplete: %s", s->name); | |
| else | |
| t->garb |= GINCOMPLETE; | |
| out: | |
| while(getnsc() != '\n') | |
| ; | |
| if(debug['f']) | |
| print("%s incomplete\n", s->name); | |
| } | |
| Sym* | |
| getimpsym(void) | |
| { | |
| int c; | |
| char *cp; | |
| c = getnsc(); | |
| if(isspace(c) || c == '"') { | |
| unget(c); | |
| return S; | |
| } | |
| for(cp = symb;;) { | |
| if(cp <= symb+NSYMB-4) | |
| *cp++ = c; | |
| c = getc(); | |
| if(c > 0 && !isspace(c) && c != '"') | |
| continue; | |
| unget(c); | |
| break; | |
| } | |
| *cp = 0; | |
| if(cp > symb+NSYMB-4) | |
| yyerror("symbol too large: %s", symb); | |
| return lookup(); | |
| } | |
| void | |
| pragdynimport(void) | |
| { | |
| Sym *local, *remote; | |
| char *path; | |
| Dynimp *f; | |
| local = getimpsym(); | |
| if(local == nil) | |
| goto err; | |
| remote = getimpsym(); | |
| if(remote == nil) | |
| goto err; | |
| path = getquoted(); | |
| if(path == nil) | |
| goto err; | |
| if(ndynimp%32 == 0) | |
| dynimp = realloc(dynimp, (ndynimp+32)*sizeof dynimp[0]); | |
| f = &dynimp[ndynimp++]; | |
| f->local = local->name; | |
| f->remote = remote->name; | |
| f->path = path; | |
| goto out; | |
| err: | |
| yyerror("usage: #pragma dynimport local remote \"path\""); | |
| out: | |
| while(getnsc() != '\n') | |
| ; | |
| } | |
| void | |
| pragdynexport(void) | |
| { | |
| Sym *local, *remote; | |
| Dynexp *f; | |
| local = getsym(); | |
| if(local == nil) | |
| goto err; | |
| remote = getsym(); | |
| if(remote == nil) | |
| goto err; | |
| if(ndynexp%32 == 0) | |
| dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]); | |
| f = &dynexp[ndynexp++]; | |
| f->local = local->name; | |
| f->remote = remote->name; | |
| goto out; | |
| err: | |
| yyerror("usage: #pragma dynexport local remote"); | |
| out: | |
| while(getnsc() != '\n') | |
| ; | |
| } |