[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.
    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.
    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
    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;
 	ExpectErr(tl, ID);
 	t0 = tl->t;
+	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;
+	}
 	if (sym != NULL) {
 		vcc_AddCall(tl, t0, sym);

More information about the varnish-commit mailing list