[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