Search in sources :

Example 1 with CallParamValueNode

use of com.google.template.soy.soytree.CallParamValueNode in project closure-templates by google.

the class GenCallCodeUtils method genObjToPass.

/**
 * Generates the JS expression for the object to pass in a given call.
 *
 * <p> Important: If there are CallParamContentNode children whose contents are not computable as
 * JS expressions, then this function assumes that, elsewhere, code has been generated to define
 * their respective 'param&lt;n&gt;' temporary variables.
 *
 * <p> Here are five example calls:
 * <pre>
 *   {call some.func data="all" /}
 *   {call some.func data="$boo.foo" /}
 *   {call some.func}
 *     {param goo = $moo /}
 *   {/call}
 *   {call some.func data="$boo"}
 *     {param goo}Blah{/param}
 *   {/call}
 *   {call some.func}
 *     {param goo}
 *       {for $i in range(3)}{$i}{/for}
 *     {/param}
 *   {/call}
 * </pre>
 * Their respective objects to pass might be the following:
 * <pre>
 *   opt_data
 *   opt_data.boo.foo
 *   {goo: opt_data.moo}
 *   soy.$$assignDefaults({goo: 'Blah'}, opt_data.boo)
 *   {goo: param65}
 * </pre>
 * Note that in the last case, the param content is not computable as JS expressions, so we assume
 * that code has been generated to define the temporary variable 'param&lt;n&gt;'.
 *
 * @param callNode The call to generate code for.
 * @param templateAliases A mapping of fully qualified calls to a variable in scope.
 * @return The JS expression for the object to pass in the call.
 */
private CodeChunk.WithValue genObjToPass(CallNode callNode, TemplateAliases templateAliases, TranslationContext translationContext, ErrorReporter errorReporter) {
    // ------ Generate the expression for the original data to pass ------
    CodeChunk.WithValue dataToPass;
    if (callNode.isPassingAllData()) {
        dataToPass = JsRuntime.OPT_DATA;
    } else if (callNode.isPassingData()) {
        dataToPass = new TranslateExprNodeVisitor(translationContext, errorReporter).exec(callNode.getDataExpr());
    } else {
        dataToPass = LITERAL_NULL;
    }
    // ------ Case 1: No additional params ------
    if (callNode.numChildren() == 0) {
        return dataToPass;
    }
    // ------ Build an object literal containing the additional params ------
    ImmutableList.Builder<CodeChunk.WithValue> keys = ImmutableList.builder();
    ImmutableList.Builder<CodeChunk.WithValue> values = ImmutableList.builder();
    for (CallParamNode child : callNode.getChildren()) {
        keys.add(id(child.getKey().identifier()));
        if (child instanceof CallParamValueNode) {
            CallParamValueNode cpvn = (CallParamValueNode) child;
            CodeChunk.WithValue value = new TranslateExprNodeVisitor(translationContext, errorReporter).exec(cpvn.getExpr());
            values.add(value);
        } else {
            CallParamContentNode cpcn = (CallParamContentNode) child;
            CodeChunk.WithValue content;
            if (isComputableAsJsExprsVisitor.exec(cpcn)) {
                List<CodeChunk.WithValue> chunks = genJsExprsVisitorFactory.create(translationContext, templateAliases, errorReporter).exec(cpcn);
                content = CodeChunkUtils.concatChunksForceString(chunks);
            } else {
                // This is a param with content that cannot be represented as JS expressions, so we assume
                // that code has been generated to define the temporary variable 'param<n>'.
                content = id("param" + cpcn.getId());
            }
            content = maybeWrapContent(translationContext.codeGenerator(), cpcn, content);
            values.add(content);
        }
    }
    CodeChunk.WithValue params = CodeChunk.mapLiteral(keys.build(), values.build());
    // ------ Cases 2 and 3: Additional params with and without original data to pass ------
    if (callNode.isPassingData()) {
        CodeChunk.WithValue allData = SOY_ASSIGN_DEFAULTS.call(params, dataToPass);
        return allData;
    } else {
        return params;
    }
}
Also used : CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk) CallParamContentNode(com.google.template.soy.soytree.CallParamContentNode) ImmutableList(com.google.common.collect.ImmutableList) CallParamNode(com.google.template.soy.soytree.CallParamNode) CallParamValueNode(com.google.template.soy.soytree.CallParamValueNode)

Example 2 with CallParamValueNode

use of com.google.template.soy.soytree.CallParamValueNode in project closure-templates by google.

the class RenderVisitor method visitCallNodeHelper.

// for IntelliJ
@SuppressWarnings("ConstantConditions")
private void visitCallNodeHelper(CallNode node, TemplateNode callee) {
    // ------ Build the call data. ------
    SoyRecord dataToPass;
    if (node.isPassingAllData()) {
        dataToPass = data;
    } else if (node.isPassingData()) {
        SoyValue dataRefValue = eval(node.getDataExpr(), node);
        if (!(dataRefValue instanceof SoyRecord)) {
            throw RenderException.create("In 'call' command " + node.toSourceString() + ", the data reference does not resolve to a SoyRecord.").addStackTraceElement(node);
        }
        dataToPass = (SoyRecord) dataRefValue;
    } else {
        dataToPass = null;
    }
    SoyRecord callData;
    int numChildren = node.numChildren();
    if (numChildren == 0) {
        // --- Cases 1 and 2: Not passing params. ---
        if (dataToPass == null) {
            // Case 1: Not passing data and not passing params.
            callData = ParamStore.EMPTY_INSTANCE;
        } else {
            // Case 2: Passing data and not passing params.
            callData = dataToPass;
        }
    } else {
        // --- Cases 3 and 4: Passing params. ---
        ParamStore mutableCallData;
        if (dataToPass == null) {
            // Case 3: Not passing data and passing params.
            mutableCallData = new BasicParamStore(numChildren);
        } else {
            // Case 4: Passing data and passing params.
            mutableCallData = new AugmentedParamStore(dataToPass, numChildren);
        }
        for (CallParamNode child : node.getChildren()) {
            if (child instanceof CallParamValueNode) {
                mutableCallData.setField(child.getKey().identifier(), lazyEval(((CallParamValueNode) child).getExpr(), child));
            } else if (child instanceof CallParamContentNode) {
                mutableCallData.setField(child.getKey().identifier(), renderRenderUnitNode((CallParamContentNode) child));
            } else {
                throw new AssertionError();
            }
        }
        callData = mutableCallData;
    }
    if (node.getEscapingDirectives().isEmpty()) {
        // No escaping at the call site -- render directly into the output buffer.
        RenderVisitor rv = this.createHelperInstance(currOutputBuf, callData);
        try {
            rv.renderTemplate(callee, node.getParamsToRuntimeCheck(callee));
        } catch (RenderException re) {
            // this template call.
            throw re.addStackTraceElement(node);
        }
    } else {
        // Escaping the call site's result, such as at a strict template boundary.
        // TODO: Some optimization is needed here before Strict Soy can be widely used:
        // - Only create this temporary buffer when contexts mismatch. We could run a pre-pass that
        // eliminates escaping directives when all callers are known.
        // - Instead of creating a temporary buffer and copying, wrap with an escaping StringBuilder.
        StringBuilder calleeBuilder = new StringBuilder();
        RenderVisitor rv = this.createHelperInstance(calleeBuilder, callData);
        try {
            rv.renderTemplate(callee, node.getParamsToRuntimeCheck(callee));
        } catch (RenderException re) {
            // this template call.
            throw re.addStackTraceElement(node);
        }
        ContentKind calleeKind = fromSanitizedContentKind(callee.getContentKind());
        SoyValue resultData = calleeKind != null ? UnsafeSanitizedContentOrdainer.ordainAsSafe(calleeBuilder.toString(), calleeKind) : StringData.forValue(calleeBuilder.toString());
        for (SoyPrintDirective directive : node.getEscapingDirectives()) {
            resultData = applyDirective(directive, resultData, ImmutableList.<SoyValue>of(), node);
        }
        append(currOutputBuf, resultData, node);
    }
}
Also used : SoyRecord(com.google.template.soy.data.SoyRecord) CallParamNode(com.google.template.soy.soytree.CallParamNode) CallParamValueNode(com.google.template.soy.soytree.CallParamValueNode) SoyValue(com.google.template.soy.data.SoyValue) BasicParamStore(com.google.template.soy.data.internal.BasicParamStore) AugmentedParamStore(com.google.template.soy.data.internal.AugmentedParamStore) CallParamContentNode(com.google.template.soy.soytree.CallParamContentNode) SoyPrintDirective(com.google.template.soy.shared.restricted.SoyPrintDirective) ParamStore(com.google.template.soy.data.internal.ParamStore) AugmentedParamStore(com.google.template.soy.data.internal.AugmentedParamStore) BasicParamStore(com.google.template.soy.data.internal.BasicParamStore) ContentKind(com.google.template.soy.data.SanitizedContent.ContentKind) SanitizedContentKind(com.google.template.soy.base.internal.SanitizedContentKind)

Example 3 with CallParamValueNode

use of com.google.template.soy.soytree.CallParamValueNode in project closure-templates by google.

the class GenPyCallExprVisitor method genObjToPass.

/**
 * Generates the Python expression for the object to pass in a given call. This expression will be
 * a combination of passed data and additional content params. If both are passed, they'll be
 * combined into one dictionary.
 *
 * @param callNode The call to generate code for.
 * @return The Python expression for the object to pass in the call.
 */
public String genObjToPass(CallNode callNode) {
    TranslateToPyExprVisitor translator = new TranslateToPyExprVisitor(localVarStack, errorReporter);
    // Generate the expression for the original data to pass.
    String dataToPass;
    if (callNode.isPassingAllData()) {
        dataToPass = "data";
    } else if (callNode.isPassingData()) {
        dataToPass = translator.exec(callNode.getDataExpr()).getText();
    } else {
        dataToPass = "{}";
    }
    // Case 1: No additional params.
    if (callNode.numChildren() == 0) {
        return dataToPass;
    }
    // Build an object literal containing the additional params.
    Map<PyExpr, PyExpr> additionalParams = new LinkedHashMap<>();
    for (CallParamNode child : callNode.getChildren()) {
        PyExpr key = new PyStringExpr("'" + child.getKey().identifier() + "'");
        if (child instanceof CallParamValueNode) {
            CallParamValueNode cpvn = (CallParamValueNode) child;
            additionalParams.put(key, translator.exec(cpvn.getExpr()));
        } else {
            CallParamContentNode cpcn = (CallParamContentNode) child;
            PyExpr valuePyExpr;
            if (isComputableAsPyExprVisitor.exec(cpcn)) {
                valuePyExpr = PyExprUtils.concatPyExprs(genPyExprsVisitorFactory.create(localVarStack, errorReporter).exec(cpcn));
            } else {
                // This is a param with content that cannot be represented as Python expressions, so we
                // assume that code has been generated to define the temporary variable 'param<n>'.
                String paramExpr = "param" + cpcn.getId();
                // The param can be assumed to be a list at this point since it was created as an output
                // variable.
                valuePyExpr = new PyListExpr(paramExpr, Integer.MAX_VALUE);
            }
            // Param content nodes require a content kind in strict autoescaping, so the content must be
            // wrapped as SanitizedContent.
            valuePyExpr = InternalPyExprUtils.wrapAsSanitizedContent(cpcn.getContentKind(), valuePyExpr.toPyString());
            additionalParams.put(key, valuePyExpr);
        }
    }
    PyExpr additionalParamsExpr = PyExprUtils.convertMapToPyExpr(additionalParams);
    // Cases 2 and 3: Additional params with and without original data to pass.
    if (callNode.isPassingData()) {
        // make a shallow copy so we don't accidentally modify the param
        dataToPass = "dict(" + dataToPass + ")";
        return "runtime.merge_into_dict(" + dataToPass + ", " + additionalParamsExpr.getText() + ")";
    } else {
        return additionalParamsExpr.getText();
    }
}
Also used : PyExpr(com.google.template.soy.pysrc.restricted.PyExpr) CallParamContentNode(com.google.template.soy.soytree.CallParamContentNode) CallParamNode(com.google.template.soy.soytree.CallParamNode) PyListExpr(com.google.template.soy.pysrc.restricted.PyListExpr) CallParamValueNode(com.google.template.soy.soytree.CallParamValueNode) PyStringExpr(com.google.template.soy.pysrc.restricted.PyStringExpr) LinkedHashMap(java.util.LinkedHashMap)

Example 4 with CallParamValueNode

use of com.google.template.soy.soytree.CallParamValueNode in project closure-templates by google.

the class TemplateParserTest method testParseBasicCallStmt.

@SuppressWarnings({ "ConstantConditions" })
@Test
public void testParseBasicCallStmt() throws Exception {
    String templateBody = "{@param too : ?}{@param animals: ?}\n" + "  {call .booTemplate_ /}\n" + "  {call foo.goo.mooTemplate data=\"all\" /}\n" + "  {call .booTemplate_ /}\n" + "  {call .zooTemplate data=\"$animals\"}\n" + "    {param yoo: round($too) /}\n" + "    {param woo kind=\"html\"}poo{/param}\n" + "    {param zoo: 0 /}\n" + "    {param doo kind=\"html\"}doopoo{/param}\n" + "  {/call}\n";
    List<StandaloneNode> nodes = parseTemplateContent(templateBody, FAIL).getChildren();
    assertThat(nodes).hasSize(4);
    CallBasicNode cn0 = (CallBasicNode) nodes.get(0);
    assertEquals("brittle.test.ns.booTemplate_", cn0.getCalleeName());
    assertEquals(".booTemplate_", cn0.getSourceCalleeName());
    assertEquals(false, cn0.isPassingData());
    assertEquals(false, cn0.isPassingAllData());
    assertEquals(null, cn0.getDataExpr());
    assertEquals("XXX", cn0.genBasePhName());
    assertEquals(0, cn0.numChildren());
    CallBasicNode cn1 = (CallBasicNode) nodes.get(1);
    assertEquals("foo.goo.mooTemplate", cn1.getCalleeName());
    assertEquals("foo.goo.mooTemplate", cn1.getSourceCalleeName());
    assertEquals(true, cn1.isPassingData());
    assertEquals(true, cn1.isPassingAllData());
    assertEquals(null, cn1.getDataExpr());
    assertFalse(cn1.genSamenessKey().equals(cn0.genSamenessKey()));
    assertEquals(0, cn1.numChildren());
    CallBasicNode cn2 = (CallBasicNode) nodes.get(2);
    assertEquals("brittle.test.ns.booTemplate_", cn2.getCalleeName());
    assertEquals(".booTemplate_", cn2.getSourceCalleeName());
    assertFalse(cn2.isPassingData());
    assertEquals(false, cn2.isPassingAllData());
    assertEquals(null, cn2.getDataExpr());
    assertEquals("XXX", cn2.genBasePhName());
    assertEquals(0, cn2.numChildren());
    CallBasicNode cn3 = (CallBasicNode) nodes.get(3);
    assertEquals("brittle.test.ns.zooTemplate", cn3.getCalleeName());
    assertEquals(".zooTemplate", cn3.getSourceCalleeName());
    assertEquals(true, cn3.isPassingData());
    assertEquals(false, cn3.isPassingAllData());
    assertTrue(cn3.getDataExpr().getRoot() != null);
    assertEquals("$animals", cn3.getDataExpr().toSourceString());
    assertEquals(4, cn3.numChildren());
    {
        final CallParamValueNode cn4cpvn0 = (CallParamValueNode) cn3.getChild(0);
        assertEquals("yoo", cn4cpvn0.getKey().identifier());
        assertEquals("round($too)", cn4cpvn0.getExpr().toSourceString());
        assertTrue(cn4cpvn0.getExpr().getRoot() instanceof FunctionNode);
    }
    {
        final CallParamContentNode cn4cpcn1 = (CallParamContentNode) cn3.getChild(1);
        assertEquals("woo", cn4cpcn1.getKey().identifier());
        assertEquals(SanitizedContentKind.HTML, cn4cpcn1.getContentKind());
        assertEquals("poo", ((RawTextNode) cn4cpcn1.getChild(0)).getRawText());
    }
    {
        final CallParamValueNode cn4cpvn2 = (CallParamValueNode) cn3.getChild(2);
        assertEquals("zoo", cn4cpvn2.getKey().identifier());
        assertEquals("0", cn4cpvn2.getExpr().toSourceString());
    }
    {
        final CallParamContentNode cn4cpcn3 = (CallParamContentNode) cn3.getChild(3);
        assertEquals("doo", cn4cpcn3.getKey().identifier());
        assertNotNull(cn4cpcn3.getContentKind());
        assertEquals(SanitizedContentKind.HTML, cn4cpcn3.getContentKind());
        assertEquals("doopoo", ((RawTextNode) cn4cpcn3.getChild(0)).getRawText());
    }
}
Also used : StandaloneNode(com.google.template.soy.soytree.SoyNode.StandaloneNode) CallParamContentNode(com.google.template.soy.soytree.CallParamContentNode) FunctionNode(com.google.template.soy.exprtree.FunctionNode) CallParamValueNode(com.google.template.soy.soytree.CallParamValueNode) CallBasicNode(com.google.template.soy.soytree.CallBasicNode) RawTextNode(com.google.template.soy.soytree.RawTextNode) Test(org.junit.Test)

Example 5 with CallParamValueNode

use of com.google.template.soy.soytree.CallParamValueNode in project closure-templates by google.

the class TemplateParserTest method testParseDelegateCallStmt.

@SuppressWarnings({ "ConstantConditions" })
@Test
public void testParseDelegateCallStmt() throws Exception {
    String templateBody = "{@param animals : ?}{@param too : ?}\n" + "  {delcall booTemplate /}\n" + "  {delcall foo.goo.mooTemplate data=\"all\" /}\n" + "  {delcall MySecretFeature.zooTemplate data=\"$animals\"}\n" + "    {param yoo: round($too) /}\n" + "    {param woo kind=\"html\"}poo{/param}\n" + "  {/delcall}\n";
    List<StandaloneNode> nodes = parseTemplateContent(templateBody, FAIL).getChildren();
    assertEquals(3, nodes.size());
    CallDelegateNode cn0 = (CallDelegateNode) nodes.get(0);
    assertEquals("booTemplate", cn0.getDelCalleeName());
    assertEquals(false, cn0.isPassingData());
    assertEquals(false, cn0.isPassingAllData());
    assertEquals(null, cn0.getDataExpr());
    assertEquals("XXX", cn0.genBasePhName());
    assertEquals(0, cn0.numChildren());
    CallDelegateNode cn1 = (CallDelegateNode) nodes.get(1);
    assertEquals("foo.goo.mooTemplate", cn1.getDelCalleeName());
    assertEquals(true, cn1.isPassingData());
    assertEquals(true, cn1.isPassingAllData());
    assertEquals(null, cn1.getDataExpr());
    assertFalse(cn1.genSamenessKey().equals(cn0.genSamenessKey()));
    assertEquals(0, cn1.numChildren());
    CallDelegateNode cn2 = (CallDelegateNode) nodes.get(2);
    assertEquals("MySecretFeature.zooTemplate", cn2.getDelCalleeName());
    assertEquals(true, cn2.isPassingData());
    assertEquals(false, cn2.isPassingAllData());
    assertTrue(cn2.getDataExpr().getRoot() != null);
    assertEquals("$animals", cn2.getDataExpr().toSourceString());
    assertEquals(2, cn2.numChildren());
    CallParamValueNode cn2cpvn0 = (CallParamValueNode) cn2.getChild(0);
    assertEquals("yoo", cn2cpvn0.getKey().identifier());
    assertEquals("round($too)", cn2cpvn0.getExpr().toSourceString());
    assertTrue(cn2cpvn0.getExpr().getRoot() instanceof FunctionNode);
    CallParamContentNode cn2cpcn1 = (CallParamContentNode) cn2.getChild(1);
    assertEquals("woo", cn2cpcn1.getKey().identifier());
    assertEquals("poo", ((RawTextNode) cn2cpcn1.getChild(0)).getRawText());
}
Also used : StandaloneNode(com.google.template.soy.soytree.SoyNode.StandaloneNode) CallParamContentNode(com.google.template.soy.soytree.CallParamContentNode) FunctionNode(com.google.template.soy.exprtree.FunctionNode) CallParamValueNode(com.google.template.soy.soytree.CallParamValueNode) CallDelegateNode(com.google.template.soy.soytree.CallDelegateNode) Test(org.junit.Test)

Aggregations

CallParamContentNode (com.google.template.soy.soytree.CallParamContentNode)6 CallParamValueNode (com.google.template.soy.soytree.CallParamValueNode)6 CallParamNode (com.google.template.soy.soytree.CallParamNode)4 FunctionNode (com.google.template.soy.exprtree.FunctionNode)2 StandaloneNode (com.google.template.soy.soytree.SoyNode.StandaloneNode)2 Test (org.junit.Test)2 ImmutableList (com.google.common.collect.ImmutableList)1 SanitizedContentKind (com.google.template.soy.base.internal.SanitizedContentKind)1 ContentKind (com.google.template.soy.data.SanitizedContent.ContentKind)1 SoyRecord (com.google.template.soy.data.SoyRecord)1 SoyValue (com.google.template.soy.data.SoyValue)1 AugmentedParamStore (com.google.template.soy.data.internal.AugmentedParamStore)1 BasicParamStore (com.google.template.soy.data.internal.BasicParamStore)1 ParamStore (com.google.template.soy.data.internal.ParamStore)1 Expression (com.google.template.soy.jbcsrc.restricted.Expression)1 SoyExpression (com.google.template.soy.jbcsrc.restricted.SoyExpression)1 CodeChunk (com.google.template.soy.jssrc.dsl.CodeChunk)1 PyExpr (com.google.template.soy.pysrc.restricted.PyExpr)1 PyListExpr (com.google.template.soy.pysrc.restricted.PyListExpr)1 PyStringExpr (com.google.template.soy.pysrc.restricted.PyStringExpr)1