Commit 54e61ba8 authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Overhaul cross reference checks in vcc compiler

Move and isolate cross reference stuff to it's own source file
(vcc_xref.c) and use vcc_ prefix as originally intended.

Also warn about multiple definitions of objects.



git-svn-id: http://www.varnish-cache.org/svn/trunk/varnish-cache@1291 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent d059484e
......@@ -14,6 +14,7 @@ libvcl_la_SOURCES = \
vcc_parse.c \
vcc_fixed_token.c \
vcc_obj.c \
vcc_token.c
vcc_token.c \
vcc_xref.c
libvcl_la_CFLAGS = -include config.h
......@@ -18,7 +18,7 @@
-e763 // Redundant declaration for symbol '...' previously declared
-e737 // Loss of sign in promotion from int to unsigned int
-e737 // Loss of sign in promotion from int to unsigned int
-e715 // Symbol 'arg' (line 43) not referenced
-e818 // Pointer parameter '...' could be declared as pointing to const
......
......@@ -60,7 +60,7 @@ vcc_Cond_Ip(struct var *vp, struct tokenlist *tl)
case '~':
vcc_NextToken(tl);
ExpectErr(tl, ID);
AddRef(tl, tl->t, R_ACL);
vcc_AddRef(tl, tl->t, R_ACL);
Fc(tl, 1, "VRT_acl_match(sp, \"%.*s\", acl_%.*s)\n",
PF(tl->t), PF(tl->t));
vcc_NextToken(tl);
......@@ -87,7 +87,7 @@ vcc_Acl(struct tokenlist *tl)
an = tl->t;
vcc_NextToken(tl);
AddDef(tl, an, R_ACL);
vcc_AddDef(tl, an, R_ACL);
Fh(tl, 0, "static struct vrt_acl acl_%.*s[];\n", PF(an));
Fc(tl, 1, "static struct vrt_acl acl_%.*s[] = {\n", PF(an));
......
......@@ -85,7 +85,7 @@
#include "vrt.h"
#include "libvcl.h"
static struct method method_tab[] = {
struct method method_tab[] = {
#define VCL_RET_MAC(l,U,b,n)
#define VCL_MET_MAC(l,U,m) { "vcl_"#l, m },
#include "vcl_returns.h"
......@@ -237,46 +237,6 @@ EncToken(struct vsb *sb, struct token *t)
EncString(sb, t->dec, NULL, 0);
}
/*--------------------------------------------------------------------
* Keep track of definitions and references
*/
static struct ref *
FindRef(struct tokenlist *tl, struct token *t, enum ref_type type)
{
struct ref *r;
TAILQ_FOREACH(r, &tl->refs, list) {
if (r->type != type)
continue;
if (vcc_Teq(r->name, t))
return (r);
}
r = TlAlloc(tl, sizeof *r);
assert(r != NULL);
r->name = t;
r->type = type;
TAILQ_INSERT_TAIL(&tl->refs, r, list);
return (r);
}
void
AddRef(struct tokenlist *tl, struct token *t, enum ref_type type)
{
FindRef(tl, t, type)->refcnt++;
}
void
AddDef(struct tokenlist *tl, struct token *t, enum ref_type type)
{
struct ref *r;
r = FindRef(tl, t, type);
r->defcnt++;
r->name = t;
}
/*--------------------------------------------------------------------*/
static struct var *
......@@ -333,182 +293,6 @@ FindVar(struct tokenlist *tl, struct token *t, struct var *vl)
return (NULL);
}
/*--------------------------------------------------------------------
* Consistency check
*/
struct proc *
AddProc(struct tokenlist *tl, struct token *t, int def)
{
struct proc *p;
TAILQ_FOREACH(p, &tl->procs, list) {
if (!vcc_Teq(p->name, t))
continue;
if (def)
p->name = t;
return (p);
}
p = TlAlloc(tl, sizeof *p);
assert(p != NULL);
p->name = t;
TAILQ_INIT(&p->calls);
TAILQ_INSERT_TAIL(&tl->procs, p, list);
return (p);
}
void
AddCall(struct tokenlist *tl, struct token *t)
{
struct proccall *pc;
struct proc *p;
p = AddProc(tl, t, 0);
TAILQ_FOREACH(pc, &tl->curproc->calls, list) {
if (pc->p == p)
return;
}
pc = TlAlloc(tl, sizeof *pc);
assert(pc != NULL);
pc->p = p;
pc->t = t;
TAILQ_INSERT_TAIL(&tl->curproc->calls, pc, list);
}
static int
Consist_Decend(struct tokenlist *tl, struct proc *p, unsigned returns)
{
unsigned u;
struct proccall *pc;
if (!p->exists) {
vsb_printf(tl->sb, "Function %.*s does not exist\n",
PF(p->name));
return (1);
}
if (p->active) {
vsb_printf(tl->sb, "Function recurses on\n");
vcc_ErrWhere(tl, p->name);
return (1);
}
u = p->returns & ~returns;
if (u) {
#define VCL_RET_MAC(a, b, c, d) \
if (u & VCL_RET_##b) { \
vsb_printf(tl->sb, "Illegal action \"%s\"\n", #a); \
vcc_ErrWhere(tl, p->returnt[d]); \
}
#include "vcl_returns.h"
#undef VCL_RET_MAC
vsb_printf(tl->sb, "\n...in function \"%.*s\"\n", PF(p->name));
vcc_ErrWhere(tl, p->name);
return (1);
}
p->active = 1;
TAILQ_FOREACH(pc, &p->calls, list) {
if (Consist_Decend(tl, pc->p, returns)) {
vsb_printf(tl->sb, "\n...called from \"%.*s\"\n",
PF(p->name));
vcc_ErrWhere(tl, pc->t);
return (1);
}
}
p->active = 0;
p->called++;
return (0);
}
static int
Consistency(struct tokenlist *tl)
{
struct proc *p;
struct method *m;
TAILQ_FOREACH(p, &tl->procs, list) {
for(m = method_tab; m->name != NULL; m++) {
if (vcc_IdIs(p->name, m->name))
break;
}
if (m->name == NULL)
continue;
if (Consist_Decend(tl, p, m->returns)) {
vsb_printf(tl->sb,
"\n...which is the \"%s\" method\n", m->name);
vsb_printf(tl->sb, "Legal actions are:");
#define VCL_RET_MAC(a, b, c, d) \
if (m->returns & c) \
vsb_printf(tl->sb, " \"%s\"", #a);
#define VCL_RET_MAC_E(a, b, c, d) VCL_RET_MAC(a, b, c, d)
#include "vcl_returns.h"
#undef VCL_RET_MAC
#undef VCL_RET_MAC_E
vsb_printf(tl->sb, "\n");
return (1);
}
}
TAILQ_FOREACH(p, &tl->procs, list) {
if (p->called)
continue;
vsb_printf(tl->sb, "Function unused\n");
vcc_ErrWhere(tl, p->name);
return (1);
}
return (0);
}
/*--------------------------------------------------------------------*/
static int
CheckRefs(struct tokenlist *tl)
{
struct ref *r;
const char *type;
int nerr = 0;
TAILQ_FOREACH(r, &tl->refs, list) {
if (r->defcnt != 0 && r->refcnt != 0)
continue;
nerr++;
switch(r->type) {
case R_FUNC:
type = "function";
break;
case R_ACL:
type = "acl";
break;
case R_BACKEND:
type = "backend";
break;
default:
ErrInternal(tl);
vsb_printf(tl->sb, "Ref ");
vcc_ErrToken(tl, r->name);
vsb_printf(tl->sb, " has unknown type %d\n",
r->type);
continue;
}
if (r->defcnt == 0 && r->name->tok == METHOD) {
vsb_printf(tl->sb,
"No definition for method %.*s\n", PF(r->name));
continue;
}
if (r->defcnt == 0) {
vsb_printf(tl->sb,
"Undefined %s %.*s, first reference:\n",
type, PF(r->name));
vcc_ErrWhere(tl, r->name);
continue;
}
vsb_printf(tl->sb, "Unused %s %.*s, defined:\n",
type, PF(r->name));
vcc_ErrWhere(tl, r->name);
}
return (nerr);
}
/*--------------------------------------------------------------------
* Output the location/profiling table. For each counted token, we
* record source+line+charpos for the first character in the token.
......@@ -939,13 +723,13 @@ vcc_CompileSource(struct vsb *sb, struct source *sp)
if (tl->err)
return (vcc_DestroyTokenList(tl, NULL));
/* Perform consistency checks */
Consistency(tl);
if (tl->err)
/* Check for orphans */
if (vcc_CheckReferences(tl))
return (vcc_DestroyTokenList(tl, NULL));
/* Check for orphans */
if (CheckRefs(tl))
/* Check that all action returns are legal */
vcc_CheckAction(tl);
if (tl->err)
return (vcc_DestroyTokenList(tl, NULL));
Ff(tl, 0, "\tVRT_free_backends(&VCL_conf);\n");
......
......@@ -120,26 +120,10 @@ struct var {
struct method {
const char *name;
unsigned returns;
};
struct proccall {
TAILQ_ENTRY(proccall) list;
struct proc *p;
struct token *t;
};
struct proc {
TAILQ_ENTRY(proc) list;
TAILQ_HEAD(,proccall) calls;
struct token *name;
unsigned returns;
unsigned exists;
unsigned called;
unsigned active;
struct token *returnt[VCL_RET_MAX];
unsigned actions;
};
struct proc;
/*--------------------------------------------------------------------*/
......@@ -149,18 +133,15 @@ void vcc_Acl(struct tokenlist *tl);
void vcc_Cond_Ip(struct var *vp, struct tokenlist *tl);
/* vcc_compile.c */
extern struct method method_tab[];
void Fh(struct tokenlist *tl, int indent, const char *fmt, ...);
void Fc(struct tokenlist *tl, int indent, const char *fmt, ...);
void Fb(struct tokenlist *tl, int indent, const char *fmt, ...);
void Fi(struct tokenlist *tl, int indent, const char *fmt, ...);
void Ff(struct tokenlist *tl, int indent, const char *fmt, ...);
unsigned UintVal(struct tokenlist *tl);
void AddDef(struct tokenlist *tl, struct token *t, enum ref_type type);
void AddRef(struct tokenlist *tl, struct token *t, enum ref_type type);
void EncToken(struct vsb *sb, struct token *t);
struct var *FindVar(struct tokenlist *tl, struct token *t, struct var *vl);
void AddCall(struct tokenlist *tl, struct token *t);
struct proc *AddProc(struct tokenlist *tl, struct token *t, int def);
int IsMethod(struct token *t);
void *TlAlloc(struct tokenlist *tl, unsigned len);
......@@ -183,6 +164,16 @@ void vcc__ErrInternal(struct tokenlist *tl, const char *func, unsigned line);
void vcc_AddToken(struct tokenlist *tl, unsigned tok, const char *b, const char *e);
void vcc_FreeToken(struct token *t);
/* vcc_expr.c */
void vcc_AddDef(struct tokenlist *tl, struct token *t, enum ref_type type);
void vcc_AddRef(struct tokenlist *tl, struct token *t, enum ref_type type);
int vcc_CheckReferences(struct tokenlist *tl);
void vcc_AddCall(struct tokenlist *tl, struct token *t);
struct proc *vcc_AddProc(struct tokenlist *tl, struct token *t);
void vcc_ProcAction(struct proc *p, unsigned action, struct token *t);
int vcc_CheckAction(struct tokenlist *tl);
#define ERRCHK(tl) do { if ((tl)->err) return; } while (0)
#define ErrInternal(tl) vcc__ErrInternal(tl, __func__, __LINE__)
#define Expect(a, b) vcc__Expect(a, b, __LINE__)
......
......@@ -527,8 +527,7 @@ Action(struct tokenlist *tl)
return;
#define VCL_RET_MAC(a,b,c,d) case T_##b: \
Fb(tl, 1, "VRT_done(sp, VCL_RET_%s);\n", #b); \
tl->curproc->returns |= VCL_RET_##b; \
tl->curproc->returnt[d] = at; \
vcc_ProcAction(tl->curproc, d, at); \
return;
#include "vcl_returns.h"
#undef VCL_RET_MAC
......@@ -554,8 +553,8 @@ Action(struct tokenlist *tl)
return;
case T_CALL:
ExpectErr(tl, ID);
AddCall(tl, tl->t);
AddRef(tl, tl->t, R_FUNC);
vcc_AddCall(tl, tl->t);
vcc_AddRef(tl, tl->t, R_FUNC);
Fb(tl, 1, "if (VGC_function_%.*s(sp))\n", PF(tl->t));
Fb(tl, 1, "\treturn (1);\n");
vcc_NextToken(tl);
......@@ -620,7 +619,7 @@ Action(struct tokenlist *tl)
case BACKEND:
if (tl->t->tok == '=') {
vcc_NextToken(tl);
AddRef(tl, tl->t, R_BACKEND);
vcc_AddRef(tl, tl->t, R_BACKEND);
Fb(tl, 0, "VGC_backend_%.*s", PF(tl->t));
vcc_NextToken(tl);
Fb(tl, 0, ");\n");
......@@ -717,9 +716,9 @@ Backend(struct tokenlist *tl)
vcc_NextToken(tl);
ExpectErr(tl, ID);
t_be = tl->t;
AddDef(tl, tl->t, R_BACKEND);
vcc_AddDef(tl, tl->t, R_BACKEND);
if (tl->nbackend == 0)
AddRef(tl, tl->t, R_BACKEND);
vcc_AddRef(tl, tl->t, R_BACKEND);
Fh(tl, 1, "#define VGC_backend_%.*s (VCL_conf.backend[%d])\n",
PF(tl->t), tl->nbackend);
Fc(tl, 0, "\n");
......@@ -837,17 +836,15 @@ Function(struct tokenlist *tl)
assert(m < N_METHODS);
tl->fb = tl->fm[m];
if (tl->mprocs[m] == NULL) {
tl->mprocs[m] = AddProc(tl, tl->t, 1);
tl->mprocs[m]->exists++;
AddDef(tl, tl->t, R_FUNC);
AddRef(tl, tl->t, R_FUNC);
tl->mprocs[m] = vcc_AddProc(tl, tl->t);
vcc_AddDef(tl, tl->t, R_FUNC);
vcc_AddRef(tl, tl->t, R_FUNC);
}
tl->curproc = tl->mprocs[m];
} else {
tl->fb = tl->fc;
tl->curproc = AddProc(tl, tl->t, 1);
tl->curproc->exists++;
AddDef(tl, tl->t, R_FUNC);
tl->curproc = vcc_AddProc(tl, tl->t);
vcc_AddDef(tl, tl->t, R_FUNC);
Fh(tl, 0, "static int VGC_function_%.*s (struct sess *sp);\n",
PF(tl->t));
Fc(tl, 1, "static int\n");
......
/*-
* Copyright (c) 2006 Verdens Gang AS
* Copyright (c) 2006 Linpro AS
* All rights reserved.
*
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
*
* 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 AUTHOR 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 AUTHOR 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.
*
* $Id$
*
* This fine contains code for two cross-reference or consistency checks.
*
* The first check is simply that all functions, acls and backends are
* both defined and referenced. Complaints about referenced but undefined
* or defined but unreferenced objects will be emitted.
*
* The second check recursively decends through function calls to make
* sure that action actions are correct for the methods through which
* they are called.
*/
#include <assert.h>
#include <stdio.h>
#include "vsb.h"
#include "vcc_priv.h"
#include "vcc_compile.h"
/*--------------------------------------------------------------------*/
struct proccall {
TAILQ_ENTRY(proccall) list;
struct proc *p;
struct token *t;
};
struct proc {
TAILQ_ENTRY(proc) list;
TAILQ_HEAD(,proccall) calls;
struct token *name;
unsigned actions;
unsigned exists;
unsigned called;
unsigned active;
struct token *action_tok[VCL_RET_MAX];
};
/*--------------------------------------------------------------------*/
static const char *
vcc_typename(struct tokenlist *tl, const struct ref *r)
{
switch (r->type) {
case R_FUNC: return ("function");
case R_ACL: return ("acl");
case R_BACKEND: return ("backend");
default:
ErrInternal(tl);
vsb_printf(tl->sb, "Ref ");
vcc_ErrToken(tl, r->name);
vsb_printf(tl->sb, " has unknown type %d\n",
r->type);
return "???";
}
}
/*--------------------------------------------------------------------
* Keep track of definitions and references
*/
static struct ref *
vcc_findref(struct tokenlist *tl, struct token *t, enum ref_type type)
{
struct ref *r;
TAILQ_FOREACH(r, &tl->refs, list) {
if (r->type != type)
continue;
if (vcc_Teq(r->name, t))
return (r);
}
r = TlAlloc(tl, sizeof *r);
assert(r != NULL);
r->name = t;
r->type = type;
TAILQ_INSERT_TAIL(&tl->refs, r, list);
return (r);
}
void
vcc_AddRef(struct tokenlist *tl, struct token *t, enum ref_type type)
{
vcc_findref(tl, t, type)->refcnt++;
}
void
vcc_AddDef(struct tokenlist *tl, struct token *t, enum ref_type type)
{
struct ref *r;
const char *tp;
r = vcc_findref(tl, t, type);
if (r->defcnt > 0) {
tp = vcc_typename(tl, r);
vsb_printf(tl->sb, "Multiple definitions of %s \"%.*s\"\n",
tp, PF(t));
vcc_ErrWhere(tl, r->name);
vsb_printf(tl->sb, "...and\n");
vcc_ErrWhere(tl, t);
}
r->defcnt++;
r->name = t;
}
/*--------------------------------------------------------------------*/
int
vcc_CheckReferences(struct tokenlist *tl)
{
struct ref *r;
const char *type;
int nerr = 0;
TAILQ_FOREACH(r, &tl->refs, list) {
if (r->defcnt != 0 && r->refcnt != 0)
continue;
nerr++;
type = vcc_typename(tl, r);
if (r->defcnt == 0 && r->name->tok == METHOD) {
vsb_printf(tl->sb,
"No definition for method %.*s\n", PF(r->name));
continue;
}
if (r->defcnt == 0) {
vsb_printf(tl->sb,
"Undefined %s %.*s, first reference:\n",
type, PF(r->name));
vcc_ErrWhere(tl, r->name);
continue;
}
vsb_printf(tl->sb, "Unused %s %.*s, defined:\n",
type, PF(r->name));
vcc_ErrWhere(tl, r->name);
}
return (nerr);
}
/*--------------------------------------------------------------------
* Returned action checks
*/
static struct proc *
vcc_findproc(struct tokenlist *tl, struct token *t)
{
struct proc *p;
TAILQ_FOREACH(p, &tl->procs, list)
if (vcc_Teq(p->name, t))
return (p);
p = TlAlloc(tl, sizeof *p);
assert(p != NULL);
TAILQ_INIT(&p->calls);
TAILQ_INSERT_TAIL(&tl->procs, p, list);
p->name = t;
return (p);
}
struct proc *
vcc_AddProc(struct tokenlist *tl, struct token *t)
{
struct proc *p;
p = vcc_findproc(tl, t);
p->name = t; /* make sure the name matches the definition */
p->exists++;
return (p);
}
void
vcc_AddCall(struct tokenlist *tl, struct token *t)
{
struct proccall *pc;
struct proc *p;
p = vcc_findproc(tl, t);
pc = TlAlloc(tl, sizeof *pc);
assert(pc != NULL);
pc->p = p;
pc->t = t;
TAILQ_INSERT_TAIL(&tl->curproc->calls, pc, list);
}
void
vcc_ProcAction(struct proc *p, unsigned action, struct token *t)
{
p->actions |= (1 << action);
/* Record the first instance of this action */
if (p->action_tok[action] == NULL)
p->action_tok[action] = t;
}
static int
vcc_CheckActionRecurse(struct tokenlist *tl, struct proc *p, unsigned actions)
{
unsigned u;
struct proccall *pc;
if (!p->exists) {
vsb_printf(tl->sb, "Function %.*s does not exist\n",
PF(p->name));
return (1);
}
if (p->active) {
vsb_printf(tl->sb, "Function recurses on\n");
vcc_ErrWhere(tl, p->name);
return (1);
}
u = p->actions & ~actions;
if (u) {
#define VCL_RET_MAC(a, b, c, d) \
if (u & VCL_RET_##b) { \
vsb_printf(tl->sb, "Illegal action \"%s\"\n", #a); \
vcc_ErrWhere(tl, p->action_tok[d]); \
}
#include "vcl_returns.h"
#undef VCL_RET_MAC
vsb_printf(tl->sb, "\n...in function \"%.*s\"\n", PF(p->name));
vcc_ErrWhere(tl, p->name);
return (1);
}
p->active = 1;
TAILQ_FOREACH(pc, &p->calls, list) {
if (vcc_CheckActionRecurse(tl, pc->p, actions)) {
vsb_printf(tl->sb, "\n...called from \"%.*s\"\n",
PF(p->name));
vcc_ErrWhere(tl, pc->t);
return (1);
}
}
p->active = 0;
p->called++;
return (0);
}
int
vcc_CheckAction(struct tokenlist *tl)
{
struct proc *p;
struct method *m;
int i;
TAILQ_FOREACH(p, &tl->procs, list) {
i = IsMethod(p->name);
if (i < 0)
continue;
m = method_tab + i;
if (vcc_CheckActionRecurse(tl, p, m->actions)) {
vsb_printf(tl->sb,
"\n...which is the \"%s\" method\n", m->name);
vsb_printf(tl->sb, "Legal actions are:");
#define VCL_RET_MAC(a, b, c, d) \
if (m->actions & c) \
vsb_printf(tl->sb, " \"%s\"", #a);
#define VCL_RET_MAC_E(a, b, c, d) VCL_RET_MAC(a, b, c, d)
#include "vcl_returns.h"
#undef VCL_RET_MAC
#undef VCL_RET_MAC_E
vsb_printf(tl->sb, "\n");
return (1);
}
}
TAILQ_FOREACH(p, &tl->procs, list) {
if (p->called)
continue;
vsb_printf(tl->sb, "Function unused\n");
vcc_ErrWhere(tl, p->name);
return (1);
}
return (0);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment