Search in sources :

Example 1 with CallParamNode

use of com.google.template.soy.soytree.CallParamNode 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 CallParamNode

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

the class GenIncrementalDomCodeVisitor method visitCallNode.

@Override
protected void visitCallNode(CallNode node) {
    // expressions, visit them to generate code to define their respective 'param<n>' variables.
    for (CallParamNode child : node.getChildren()) {
        if (child instanceof CallParamContentNode && !isComputableAsJsExprsVisitor.exec(child)) {
            visit(child);
        }
    }
    CodeChunk.WithValue call = genCallCodeUtils.gen(node, templateAliases, templateTranslationContext, errorReporter);
    switch(getJsCodeBuilder().getContentKind()) {
        case ATTRIBUTES:
            getJsCodeBuilder().append(call);
            break;
        case HTML:
            Optional<SanitizedContentKind> kind = templateRegistry.getCallContentKind(node);
            // such as dynamic recompilation.
            if (!kind.isPresent()) {
                call = SOY_IDOM_RENDER_DYNAMIC_CONTENT.call(call);
            } else if (isTextContent(kind.get())) {
                call = generateTextCall(call);
            }
            getJsCodeBuilder().append(call);
            break;
        case JS:
        case URI:
        case TRUSTED_RESOURCE_URI:
        case CSS:
        case TEXT:
            // If the current content kind (due to a let, param or template) is a text-like, simply
            // concatentate the result of the call to the current output variable.
            getJsCodeBuilder().addChunkToOutputVar(call);
            break;
    }
}
Also used : CallParamContentNode(com.google.template.soy.soytree.CallParamContentNode) CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk) CallParamNode(com.google.template.soy.soytree.CallParamNode) SanitizedContentKind(com.google.template.soy.base.internal.SanitizedContentKind)

Example 3 with CallParamNode

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

the class FindIndirectParamsVisitor method visitCalleeHelper.

private void visitCalleeHelper(CallNode caller, TemplateNode callee) {
    // We must not revisit the current template or any templates already in the caller stack.
    if (callee == currTemplate || callerStack.peek().allCallers.contains(callee)) {
        return;
    }
    // Get the set of params that are passed explicitly in this call, but not already passed
    // explicitly in a previous call in the current call path. And then create the new set of
    // allCallParamKeys (reusing the old set if there are no additional call param keys).
    Set<String> prevAllCallParamKeys = callerStack.peek().allCallParamKeys;
    Set<String> additionalCallParamKeys = Sets.newHashSet();
    for (CallParamNode callParamNode : caller.getChildren()) {
        String callParamKey = callParamNode.getKey().identifier();
        if (!prevAllCallParamKeys.contains(callParamKey)) {
            additionalCallParamKeys.add(callParamKey);
        }
    }
    Set<String> newAllCallParamKeys;
    if (!additionalCallParamKeys.isEmpty()) {
        newAllCallParamKeys = Sets.newHashSet(prevAllCallParamKeys);
        newAllCallParamKeys.addAll(additionalCallParamKeys);
    } else {
        newAllCallParamKeys = prevAllCallParamKeys;
    }
    // Ensure we don't visit the same call situation more than once.
    CallSituation currCallSituation = new CallSituation(callee, newAllCallParamKeys);
    if (visitedCallSituations.contains(currCallSituation)) {
        return;
    }
    visitedCallSituations.add(currCallSituation);
    // Add caller frame.
    if (currNewAllCallers == null) {
        currNewAllCallers = Sets.newHashSet(callerStack.peek().allCallers);
        currNewAllCallers.add(currTemplate);
    }
    CallerFrame callerFrame = new CallerFrame(currTemplate, currNewAllCallers, newAllCallParamKeys);
    callerStack.push(callerFrame);
    // Visit the callee.
    visit(callee);
    // Remove caller frame and restore previous values of currTemplate and currNewAllCallers.
    CallerFrame poppedCallerFrame = callerStack.pop();
    if (poppedCallerFrame != callerFrame) {
        throw new AssertionError();
    }
    currTemplate = callerFrame.caller;
    currNewAllCallers = callerFrame.allCallers;
}
Also used : CallParamNode(com.google.template.soy.soytree.CallParamNode)

Example 4 with CallParamNode

use of com.google.template.soy.soytree.CallParamNode 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 5 with CallParamNode

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

the class GenJsCodeVisitor method visitCallNode.

/**
 * Example:
 *
 * <pre>
 *   {call some.func data="all" /}
 *   {call some.func data="$boo.foo" /}
 *   {call some.func}
 *     {param goo: 88 /}
 *   {/call}
 *   {call some.func data="$boo"}
 *     {param goo}
 *       Hello {$name}
 *     {/param}
 *   {/call}
 * </pre>
 *
 * might generate
 *
 * <pre>
 *   output += some.func(opt_data);
 *   output += some.func(opt_data.boo.foo);
 *   output += some.func({goo: 88});
 *   output += some.func(soy.$$assignDefaults({goo: 'Hello ' + opt_data.name}, opt_data.boo);
 * </pre>
 */
@Override
protected void visitCallNode(CallNode node) {
    // expressions, visit them to generate code to define their respective 'param<n>' variables.
    for (CallParamNode child : node.getChildren()) {
        if (child instanceof CallParamContentNode && !isComputableAsJsExprsVisitor.exec(child)) {
            visit(child);
        }
    }
    // Add the call's result to the current output var.
    CodeChunk.WithValue call = genCallCodeUtils.gen(node, templateAliases, templateTranslationContext, errorReporter);
    jsCodeBuilder.addChunkToOutputVar(call);
}
Also used : CallParamContentNode(com.google.template.soy.soytree.CallParamContentNode) CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk) CallParamNode(com.google.template.soy.soytree.CallParamNode)

Aggregations

CallParamNode (com.google.template.soy.soytree.CallParamNode)8 CallParamContentNode (com.google.template.soy.soytree.CallParamContentNode)7 CodeChunk (com.google.template.soy.jssrc.dsl.CodeChunk)4 CallParamValueNode (com.google.template.soy.soytree.CallParamValueNode)4 ImmutableList (com.google.common.collect.ImmutableList)2 SanitizedContentKind (com.google.template.soy.base.internal.SanitizedContentKind)2 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 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 SoyPrintDirective (com.google.template.soy.shared.restricted.SoyPrintDirective)1 CallNode (com.google.template.soy.soytree.CallNode)1 MsgHtmlTagNode (com.google.template.soy.soytree.MsgHtmlTagNode)1