Enable calling a dynamic sub

that is,

	call vmod.returning_sub();

in VCL.

Background:

The existing

	call sub;

is special in that it instantiates "sub", if it does not exist
already, enabling use-before-definition semantics.

The doctrine of this change is to preserve this behaviour and to not
make existing, static calls any less efficient.

Implementation:

To support calling both literal SUBs as well as SUB expressions, we
peek into the symbol table to check if the called expression is a
function, based on the knowledge that, as of now, only functions can
have a non-literal SUB return. We also know that no other expression
can yield a SUB. So if peeking ahead tells us that a function follows,
we call into expression evaluation.

Otherwise, we expect a literal SUB as before.

Null check:

For dynamic SUB calls from vmods via VRT_call(), we require the SUB
argument be non-NULL. But we can not for call: To signal error with a
SUB return, a vmod function needs to be allowed to return NULL,
otherwise it would need to have a dummy return value available for the
VRT_fail() case. So we need to check for NULL in VGC.

Alternatives considered:

- We could make expression evaluation return SUB also for literal SUB
symbols, turning all calls into sub->func(ctx, ...) C statements. I
tried this path and it resulted in a change of behaviour with
cc_command="clang -g -O2 ...": Where, previously, sub code was
inlined, it would now generate an explicit C function call always,
despite the fact that the C function is known at compile time (because
all named VCL_SUB structs are).

So this option was dismissed for violating the doctrine.

- The null check could go to VPI, via a VPI_call(), basically identical
to VRT_call(), but checking for NULL. This option was dismissed because
it would foreclose the requirement to allow for SUB arguments in the
future.
parent 1c5ba7da
......@@ -19,7 +19,7 @@ varnish v1 -vcl {
backend dummy None;
sub vcl_recv {
debug.call(debug.total_recall());
call debug.total_recall();
}
}
......
......@@ -45,10 +45,38 @@ static void v_matchproto_(sym_act_f)
vcc_act_call(struct vcc *tl, struct token *t, struct symbol *sym)
{
struct token *t0;
unsigned u;
(void)t;
ExpectErr(tl, ID);
t0 = tl->t;
sym = VCC_SymbolGet(tl, SYM_MAIN, SYM_NONE, SYMTAB_NOERR, XREF_NONE);
tl->t = t0;
// only SYM_FUNC may evaluate to SUB
if (sym != NULL && sym->kind == SYM_FUNC) {
u = tl->unique++;
Fb(tl, 1, "{\n");
tl->indent += INDENT;
Fb(tl, 2, "VCL_SUB call_%u =\n", u);
tl->indent += INDENT;
vcc_Expr(tl, SUB);
Fb(tl, 2, ";\n\n");
SkipToken(tl, ';');
tl->indent -= INDENT;
Fb(tl, 2, "if (call_%u == NULL) {\n", u);
Fb(tl, 2, " VRT_fail(ctx, \"Tried to call NULL SUB near"
" source %%u line %%u\",\n");
Fb(tl, 2, " VGC_ref[%u].source,\n", tl->cnt);
Fb(tl, 2, " VGC_ref[%u].line);\n", tl->cnt);
Fb(tl, 2, " END_;\n");
Fb(tl, 2, "}\n");
Fb(tl, 2, "call_%u->func(ctx, VSUB_STATIC, NULL);\n", u);
tl->indent -= INDENT;
Fb(tl, 1, "}\n");
return;
}
sym = VCC_SymbolGet(tl, SYM_MAIN, SYM_SUB, SYMTAB_CREATE, XREF_REF);
if (sym != NULL) {
vcc_AddCall(tl, t0, sym);
......
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