Search in sources :

Example 1 with CodeChunk

use of com.google.template.soy.jssrc.dsl.CodeChunk in project closure-templates by google.

the class GenCallCodeUtils method gen.

/**
 * Generates the JS expression for 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 generated calls might be the following:
 * <pre>
 *   some.func(opt_data)
 *   some.func(opt_data.boo.foo)
 *   some.func({goo: opt_data.moo})
 *   some.func(soy.$$assignDefaults({goo: 'Blah'}, opt_data.boo))
 *   some.func({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 call.
 */
public CodeChunk.WithValue gen(CallNode callNode, TemplateAliases templateAliases, TranslationContext translationContext, ErrorReporter errorReporter) {
    // Build the JS CodeChunk for the callee's name.
    CodeChunk.WithValue callee;
    if (callNode instanceof CallBasicNode) {
        // Case 1: Basic call.
        // TODO(lukes): add the logic for the goog.require here.  The simplest strategy requires a
        // TemplateRegistry to detect external templates.
        callee = CodeChunk.dottedIdNoRequire(templateAliases.get(((CallBasicNode) callNode).getCalleeName()));
    } else {
        // Case 2: Delegate call.
        CallDelegateNode callDelegateNode = (CallDelegateNode) callNode;
        CodeChunk.WithValue calleeId = JsRuntime.SOY_GET_DELTEMPLATE_ID.call(stringLiteral(delTemplateNamer.getDelegateName(callDelegateNode)));
        ExprRootNode variantSoyExpr = callDelegateNode.getDelCalleeVariantExpr();
        CodeChunk.WithValue variant;
        if (variantSoyExpr == null) {
            // Case 2a: Delegate call with empty variant.
            variant = LITERAL_EMPTY_STRING;
        } else {
            // Case 2b: Delegate call with variant expression.
            variant = new TranslateExprNodeVisitor(translationContext, errorReporter).exec(variantSoyExpr);
        }
        callee = SOY_GET_DELEGATE_FN.call(calleeId, variant, callDelegateNode.allowEmptyDefault() ? LITERAL_TRUE : LITERAL_FALSE);
    }
    // Generate the data object to pass to callee
    CodeChunk.WithValue objToPass = genObjToPass(callNode, templateAliases, translationContext, errorReporter);
    // Generate the main call expression.
    CodeChunk.WithValue call = callee.call(objToPass, JsRuntime.OPT_IJ_DATA);
    if (callNode.getEscapingDirectives().isEmpty()) {
        return call;
    }
    // Apply escaping directives as necessary.
    // 
    // The print directive system continues to use JsExpr, as it is a publicly available API and
    // migrating it to CodeChunk would be a major change. Therefore, we convert our CodeChunks
    // to JsExpr and back here.
    JsExpr callResult = call.singleExprOrName();
    RequiresCollector.IntoImmutableSet collector = new RequiresCollector.IntoImmutableSet();
    call.collectRequires(collector);
    for (SoyPrintDirective directive : callNode.getEscapingDirectives()) {
        Preconditions.checkState(directive instanceof SoyJsSrcPrintDirective, "Contextual autoescaping produced a bogus directive: %s", directive.getName());
        callResult = ((SoyJsSrcPrintDirective) directive).applyForJsSrc(callResult, ImmutableList.<JsExpr>of());
        if (directive instanceof SoyLibraryAssistedJsSrcPrintDirective) {
            for (String name : ((SoyLibraryAssistedJsSrcPrintDirective) directive).getRequiredJsLibNames()) {
                collector.add(GoogRequire.create(name));
            }
        }
    }
    return fromExpr(callResult, collector.get()).withInitialStatements(call.initialStatements());
}
Also used : JsExpr(com.google.template.soy.jssrc.restricted.JsExpr) CallBasicNode(com.google.template.soy.soytree.CallBasicNode) RequiresCollector(com.google.template.soy.jssrc.dsl.CodeChunk.RequiresCollector) CallDelegateNode(com.google.template.soy.soytree.CallDelegateNode) ExprRootNode(com.google.template.soy.exprtree.ExprRootNode) CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk) SoyPrintDirective(com.google.template.soy.shared.restricted.SoyPrintDirective) SoyJsSrcPrintDirective(com.google.template.soy.jssrc.restricted.SoyJsSrcPrintDirective) SoyLibraryAssistedJsSrcPrintDirective(com.google.template.soy.jssrc.restricted.SoyLibraryAssistedJsSrcPrintDirective)

Example 2 with CodeChunk

use of com.google.template.soy.jssrc.dsl.CodeChunk in project closure-templates by google.

the class GenJsCodeVisitorAssistantForMsgs method generateMsgGroupVariable.

/**
 * Returns a code chunk representing a variable declaration for an {@link MsgFallbackGroupNode}
 * that contains fallback(s).
 */
private CodeChunk.WithValue generateMsgGroupVariable(MsgFallbackGroupNode node, String tmpVarName) {
    checkState(node.numChildren() == 2);
    // Generate the goog.getMsg calls for all children.
    GoogMsgCodeGenInfo primaryCodeGenInfo = genGoogGetMsgCallHelper(buildGoogMsgVarNameHelper(node.getChild(0)), node.getChild(0));
    GoogMsgCodeGenInfo fallbackCodeGenInfo = genGoogGetMsgCallHelper(buildGoogMsgVarNameHelper(node.getChild(1)), node.getChild(1));
    // Declare a temporary variable to hold the getMsgWithFallback() call so that we can apply any
    // MessageFormats from any of the fallbacks.  This is also the variable name that we return to
    // the caller.
    CodeChunk.WithValue selectedMsg = VariableDeclaration.builder(tmpVarName).setRhs(CodeChunk.dottedIdNoRequire("goog.getMsgWithFallback").call(primaryCodeGenInfo.googMsgVar, fallbackCodeGenInfo.googMsgVar)).build().ref();
    // We use id() here instead of using the corresponding code chunks because the stupid
    // jscodebuilder system causes us to regenerate the msg vars multiple times because it doesn't
    // detect that they were already generated.
    // TODO(b/33382980): clean this up
    CodeChunk.WithValue isPrimaryMsgInUse = CodeChunk.id(tmpVarName).doubleEquals(CodeChunk.id(primaryCodeGenInfo.googMsgVarName));
    translationContext.soyToJsVariableMappings().setIsPrimaryMsgInUse(node, isPrimaryMsgInUse);
    if (primaryCodeGenInfo.placeholders == null && fallbackCodeGenInfo.placeholders == null) {
        // all placeholders have already been substituted, just return
        return selectedMsg;
    }
    // Generate the goog.i18n.MessageFormat calls for child plural/select messages (if any), each
    // wrapped in an if-block that will only execute if that child is the chosen message.
    CodeChunk condition;
    if (primaryCodeGenInfo.placeholders != null) {
        ConditionalBuilder builder = CodeChunk.ifStatement(selectedMsg.doubleEquals(primaryCodeGenInfo.googMsgVar), selectedMsg.assign(getMessageFormatCall(primaryCodeGenInfo)));
        if (fallbackCodeGenInfo.placeholders != null) {
            builder.else_(selectedMsg.assign(getMessageFormatCall(fallbackCodeGenInfo)));
        }
        condition = builder.build();
    } else {
        condition = CodeChunk.ifStatement(selectedMsg.doubleEquals(fallbackCodeGenInfo.googMsgVar), selectedMsg.assign(getMessageFormatCall(fallbackCodeGenInfo))).build();
    }
    return CodeChunk.id(tmpVarName).withInitialStatement(condition);
}
Also used : CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk) ConditionalBuilder(com.google.template.soy.jssrc.dsl.ConditionalBuilder)

Example 3 with CodeChunk

use of com.google.template.soy.jssrc.dsl.CodeChunk in project closure-templates by google.

the class GenJsCodeVisitor method handleForeachLoop.

/**
 * Example:
 *
 * <pre>
 *   {for $foo in $boo.foos}
 *     ...
 *   {/for}
 * </pre>
 *
 * might generate
 *
 * <pre>
 *   for (var foo2Index = 0; foo2Index &lt; foo2ListLen; foo2Index++) {
 *     var foo2Data = foo2List[foo2Index];
 *     ...
 *   }
 * </pre>
 */
private CodeChunk handleForeachLoop(ForNonemptyNode node, CodeChunk.WithValue limit, Function<CodeChunk.WithValue, CodeChunk.WithValue> getDataItemFunction) {
    // Build some local variable names.
    String varName = node.getVarName();
    String varPrefix = varName + node.getForNodeId();
    // TODO(user): A more consistent pattern for local variable management.
    String loopIndexName = varPrefix + "Index";
    String dataName = varPrefix + "Data";
    CodeChunk.WithValue loopIndex = id(loopIndexName);
    VariableDeclaration data = VariableDeclaration.builder(dataName).setRhs(getDataItemFunction.apply(loopIndex)).build();
    // Populate the local var translations with the translations from this node.
    templateTranslationContext.soyToJsVariableMappings().put(varName, id(dataName)).put(varName + "__isFirst", loopIndex.doubleEquals(number(0))).put(varName + "__isLast", loopIndex.doubleEquals(limit.minus(number(1)))).put(varName + "__index", loopIndex);
    // Generate the loop body.
    CodeChunk foreachBody = CodeChunk.statements(data, visitChildrenReturningCodeChunk(node));
    // Create the entire for block.
    return forLoop(loopIndexName, limit, foreachBody);
}
Also used : CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk) VariableDeclaration(com.google.template.soy.jssrc.dsl.VariableDeclaration)

Example 4 with CodeChunk

use of com.google.template.soy.jssrc.dsl.CodeChunk in project closure-templates by google.

the class GenJsCodeVisitor method doVisitReturningCodeChunk.

/**
 * Do not use directly; use {@link #visitChildrenReturningCodeChunk} or {@link
 * #visitNodeReturningCodeChunk} instead.
 */
private CodeChunk doVisitReturningCodeChunk(SoyNode node, boolean visitChildren) {
    // Replace jsCodeBuilder with a child JsCodeBuilder.
    JsCodeBuilder original = jsCodeBuilder;
    jsCodeBuilder = createChildJsCodeBuilder();
    // Visit body.
    // set indent to 0 since when rendering the CodeChunk, everything will get re-indented
    jsCodeBuilder.setIndent(0);
    if (visitChildren) {
        visitChildren((ParentSoyNode<?>) node);
    } else {
        visit(node);
    }
    CodeChunk chunk = CodeChunk.treatRawStringAsStatementLegacyOnly(jsCodeBuilder.getCode(), jsCodeBuilder.googRequires());
    jsCodeBuilder = original;
    return chunk;
}
Also used : CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk)

Example 5 with CodeChunk

use of com.google.template.soy.jssrc.dsl.CodeChunk in project closure-templates by google.

the class GenJsCodeVisitor method generateNonExpressionIfNode.

/**
 * Generates the JavaScript code for an {if} block that cannot be done as an expression.
 *
 * <p>TODO(user): Instead of interleaving JsCodeBuilders like this, consider refactoring
 * GenJsCodeVisitor to return CodeChunks for each sub-Template level SoyNode.
 */
protected void generateNonExpressionIfNode(IfNode node) {
    ConditionalBuilder conditional = null;
    for (SoyNode child : node.getChildren()) {
        if (child instanceof IfCondNode) {
            IfCondNode condNode = (IfCondNode) child;
            // Convert predicate.
            CodeChunk.WithValue predicate = translateExpr(condNode.getExpr());
            // Convert body.
            CodeChunk consequent = visitChildrenReturningCodeChunk(condNode);
            // Add if-block to conditional.
            if (conditional == null) {
                conditional = ifStatement(predicate, consequent);
            } else {
                conditional.elseif_(predicate, consequent);
            }
        } else if (child instanceof IfElseNode) {
            // Convert body.
            CodeChunk trailingElse = visitChildrenReturningCodeChunk((IfElseNode) child);
            // Add else-block to conditional.
            conditional.else_(trailingElse);
        } else {
            throw new AssertionError();
        }
    }
    jsCodeBuilder.append(conditional.build());
}
Also used : IfElseNode(com.google.template.soy.soytree.IfElseNode) ParentSoyNode(com.google.template.soy.soytree.SoyNode.ParentSoyNode) SoyNode(com.google.template.soy.soytree.SoyNode) IfCondNode(com.google.template.soy.soytree.IfCondNode) CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk) ConditionalBuilder(com.google.template.soy.jssrc.dsl.ConditionalBuilder)

Aggregations

CodeChunk (com.google.template.soy.jssrc.dsl.CodeChunk)20 ImmutableList (com.google.common.collect.ImmutableList)6 VariableDeclaration (com.google.template.soy.jssrc.dsl.VariableDeclaration)4 SanitizedContentKind (com.google.template.soy.base.internal.SanitizedContentKind)3 ExprNode (com.google.template.soy.exprtree.ExprNode)3 WithValue (com.google.template.soy.jssrc.dsl.CodeChunk.WithValue)3 UniqueNameGenerator (com.google.template.soy.base.internal.UniqueNameGenerator)2 StringNode (com.google.template.soy.exprtree.StringNode)2 ConditionalBuilder (com.google.template.soy.jssrc.dsl.ConditionalBuilder)2 SwitchBuilder (com.google.template.soy.jssrc.dsl.SwitchBuilder)2 SoyPrintDirective (com.google.template.soy.shared.restricted.SoyPrintDirective)2 SoyNode (com.google.template.soy.soytree.SoyNode)2 ParentSoyNode (com.google.template.soy.soytree.SoyNode.ParentSoyNode)2 ArrayList (java.util.ArrayList)2 Map (java.util.Map)2 CheckReturnValue (javax.annotation.CheckReturnValue)2 ImmutableMap (com.google.common.collect.ImmutableMap)1 FieldDescriptor (com.google.protobuf.Descriptors.FieldDescriptor)1 PrimitiveNode (com.google.template.soy.exprtree.ExprNode.PrimitiveNode)1 ExprRootNode (com.google.template.soy.exprtree.ExprRootNode)1