[master] 7ec28f1ae Enable calling a dynamic sub
Nils Goroll
nils.goroll at uplex.de
Mon Feb 15 15:03:04 UTC 2021
commit 7ec28f1ae91bd39e6e89beb7dffd4248c5054414
Author: Nils Goroll <nils.goroll at uplex.de>
Date: Tue Feb 2 17:53:44 2021 +0100
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.
Acknowledgements:
reza mentioned the idea of call SUB at one point, and I can not remember
if I had it before or not, so when in doubt, it was his idea.
Dridi helped with ideas on the implementation and spotted a bug.
diff --git a/bin/varnishtest/tests/m00054.vtc b/bin/varnishtest/tests/m00054.vtc
index 87b53f591..3cda1eeb5 100644
--- a/bin/varnishtest/tests/m00054.vtc
+++ b/bin/varnishtest/tests/m00054.vtc
@@ -19,7 +19,7 @@ varnish v1 -vcl {
backend dummy None;
sub vcl_recv {
- debug.call(debug.total_recall());
+ call debug.total_recall();
}
}
diff --git a/lib/libvcc/vcc_action.c b/lib/libvcc/vcc_action.c
index 42d5cf1ae..cc99c8537 100644
--- a/lib/libvcc/vcc_action.c
+++ b/lib/libvcc/vcc_action.c
@@ -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 functions/methods may evaluate to SUB
+ if (sym != NULL && (sym->kind == SYM_FUNC || sym->kind == SYM_METHOD)) {
+ 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);
More information about the varnish-commit
mailing list