Search in sources :

Example 6 with CodeChunk

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

the class GenJsCodeVisitor method generateFunctionBody.

/**
 * Generates the function body.
 */
@CheckReturnValue
protected CodeChunk generateFunctionBody(TemplateNode node) {
    // Type check parameters.
    CodeChunk paramDeclarations = genParamTypeChecks(node);
    SanitizedContentKind kind = node.getContentKind();
    CodeChunk bodyAndReturn;
    if (isComputableAsJsExprsVisitor.exec(node)) {
        // Case 1: The code style is 'concat' and the whole template body can be represented as JS
        // expressions. We specially handle this case because we don't want to generate the variable
        // 'output' at all. We simply concatenate the JS expressions and return the result.
        List<CodeChunk.WithValue> templateBodyChunks = genJsExprsVisitor.exec(node);
        if (kind == null) {
            // The template is not strict. Thus, it may not apply an escaping directive to *every* print
            // command, which means that some of its print commands could produce a number. Thus, there
            // is a danger that a plus operator between two expressions in the list will do numeric
            // addition instead of string concatenation. Furthermore, a non-strict template always needs
            // to return a string, but if there is just one expression in the list, and we return it as
            // is, we may not always produce a string (since an escaping directive may not be getting
            // applied in that expression at all, or a directive might be getting applied that produces
            // SanitizedContent). We thus call a method that makes sure to return an expression that
            // produces a string and is in no danger of using numeric addition when concatenating the
            // expressions in the list.
            bodyAndReturn = return_(CodeChunkUtils.concatChunksForceString(templateBodyChunks));
        } else {
            // The template is strict. Thus, it applies an escaping directive to *every* print command,
            // which means that no print command produces a number, which means that there is no danger
            // of a plus operator between two print commands doing numeric addition instead of string
            // concatenation. And since a strict template needs to return SanitizedContent, it is ok to
            // get an expression that produces SanitizedContent, which is indeed possible with an
            // escaping directive that produces SanitizedContent. Thus, we do not have to be extra
            // careful when concatenating the expressions in the list.
            bodyAndReturn = return_(sanitize(CodeChunkUtils.concatChunks(templateBodyChunks), kind));
        }
    } else {
        // Case 2: Normal case.
        jsCodeBuilder.pushOutputVar("output");
        CodeChunk codeChunk = visitChildrenReturningCodeChunk(node);
        jsCodeBuilder.popOutputVar();
        bodyAndReturn = CodeChunk.statements(codeChunk, return_(sanitize(id("output"), kind)));
    }
    return CodeChunk.statements(paramDeclarations, bodyAndReturn);
}
Also used : CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk) SanitizedContentKind(com.google.template.soy.base.internal.SanitizedContentKind) CheckReturnValue(javax.annotation.CheckReturnValue)

Example 7 with CodeChunk

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

the class GenJsCodeVisitor method genParamTypeChecks.

/**
 * Generate code to verify the runtime types of the input params. Also typecasts the input
 * parameters and assigns them to local variables for use in the template.
 *
 * @param node the template node.
 */
@CheckReturnValue
protected CodeChunk genParamTypeChecks(TemplateNode node) {
    ImmutableList.Builder<CodeChunk> declarations = ImmutableList.builder();
    for (TemplateParam param : node.getAllParams()) {
        if (param.declLoc() != TemplateParam.DeclLoc.HEADER) {
            continue;
        }
        String paramName = param.name();
        SoyType paramType = param.type();
        CodeChunk.Generator generator = templateTranslationContext.codeGenerator();
        CodeChunk.WithValue paramChunk = TranslateExprNodeVisitor.genCodeForParamAccess(paramName, param.isInjected());
        JsType jsType = getJsType(paramType);
        // The opt_param.name value that will be type-tested.
        String paramAlias = genParamAlias(paramName);
        CodeChunk.WithValue coerced = jsType.getValueCoercion(paramChunk, generator);
        if (coerced != null) {
            // since we have coercion logic, dump into a temporary
            paramChunk = generator.declarationBuilder().setRhs(coerced).build().ref();
        }
        // The param value to assign
        CodeChunk.WithValue value;
        Optional<CodeChunk.WithValue> typeAssertion = jsType.getTypeAssertion(paramChunk, generator);
        // The type-cast expression.
        if (typeAssertion.isPresent()) {
            value = SOY_ASSERTS_ASSERT_TYPE.call(typeAssertion.get(), stringLiteral(paramName), paramChunk, stringLiteral(jsType.typeExpr()));
        } else {
            value = paramChunk;
        }
        VariableDeclaration.Builder declarationBuilder = VariableDeclaration.builder(paramAlias).setRhs(value).setGoogRequires(jsType.getGoogRequires());
        declarationBuilder.setJsDoc("/** @type {" + jsType.typeExpr() + "} */");
        VariableDeclaration declaration = declarationBuilder.build();
        declarations.add(declaration);
        templateTranslationContext.soyToJsVariableMappings().put(paramName, id(paramAlias));
    }
    return CodeChunk.statements(declarations.build());
}
Also used : ImmutableList(com.google.common.collect.ImmutableList) CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk) SoyType(com.google.template.soy.types.SoyType) VariableDeclaration(com.google.template.soy.jssrc.dsl.VariableDeclaration) TemplateParam(com.google.template.soy.soytree.defn.TemplateParam) CheckReturnValue(javax.annotation.CheckReturnValue)

Example 8 with CodeChunk

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

the class AssistantForHtmlMsgs method generateMsgGroupCode.

/**
 * Returns a code chunk of idom instructions that output the contents of a translated message as
 * HTML. For example:
 *
 * <pre>
 *   {msg desc="Says hello to a person."}Hello {$name}!{/msg}
 * </pre>
 *
 * compiles to
 *
 * <pre>
 *   /** @desc Says hello to a person. *{@literal /}
 *   var MSG_EXTERNAL_6936162475751860807 = goog.getMsg(
 *       'Hello {$name}!',
 *       {'name': '\u00010\u0001'});
 *   var lastIndex_1153 = 0, partRe_1153 = /\x01\d+\x01/g, match_1153;
 *   do {
 *     match_1153 = partRe_1153.exec(MSG_EXTERNAL_6936162475751860807) || undefined;
 *     incrementalDom.text(goog.string.unescapeEntities(
 *         MSG_EXTERNAL_6936162475751860807.substring(
 *           lastIndex_1153, match_1153 && match_1153.index)));
 *     lastIndex_1153 = partRe_1153.lastIndex;
 *     switch (match_1153 && match_1153[0]) {
 *       case '\u00010\u0001':
 *         var dyn8 = opt_data.name;
 *         if (typeof dyn8 == 'function') dyn8();
 *         else if (dyn8 != null) incrementalDom.text(dyn8);
 *         break;
 *     }
 *   } while (match_1153);
 * </pre>
 *
 * Each interpolated MsgPlaceholderNode (either for HTML tags or for print statements) compiles to
 * a separate {@code case} statement.
 */
CodeChunk generateMsgGroupCode(MsgFallbackGroupNode node) {
    Preconditions.checkState(placeholderNames.isEmpty(), "This class is not reusable.");
    // Non-HTML {msg}s should be extracted into LetContentNodes and handled by jssrc.
    Preconditions.checkArgument(node.getHtmlContext() == HtmlContext.HTML_PCDATA, "AssistantForHtmlMsgs is only for HTML {msg}s.");
    // All of these helper variables must have uniquely-suffixed names because {msg}s can be nested.
    // It'd be nice to move this codegen to a Soy template...
    // The raw translated text, with placeholder placeholders.
    CodeChunk.WithValue translationVar = super.generateMsgGroupVariable(node);
    // If there are no placeholders, we don't need anything special (but we still need to unescape).
    if (placeholderNames.isEmpty()) {
        CodeChunk.WithValue unescape = GOOG_STRING_UNESCAPE_ENTITIES.call(translationVar);
        return INCREMENTAL_DOM_TEXT.call(unescape);
    }
    // it into a fresh variable.
    if (!translationVar.isCheap()) {
        translationVar = translationContext.codeGenerator().declarationBuilder().setRhs(translationVar).build().ref();
    }
    // Declare everything.
    // The mutable (tracking index of last match) regex to find the placeholder placeholders.
    VariableDeclaration regexVar = VariableDeclaration.builder("partRe_" + node.getId()).setRhs(CodeChunk.regexLiteral(PLACEHOLDER_REGEX)).build();
    // The current placeholder from the regex.
    VariableDeclaration matchVar = VariableDeclaration.builder("match_" + node.getId()).build();
    // The index of the end of the previous placeholder, where the next raw text run starts.
    VariableDeclaration lastIndexVar = VariableDeclaration.builder("lastIndex_" + node.getId()).setRhs(CodeChunk.number(0)).build();
    List<CodeChunk> doBody = new ArrayList<>();
    // match_XXX = partRe_XXX.exec(MSG_EXTERNAL_XXX) || undefined;
    doBody.add(matchVar.ref().assign(regexVar.ref().dotAccess("exec").call(translationVar).or(CodeChunk.id("undefined"), translationContext.codeGenerator())));
    // Replace null with undefined.  This is necessary to make substring() treat falsy as an omitted
    // parameter, so that it goes until the end of the string.  Otherwise, the non-numeric parameter
    // would be coerced to zero.
    // Emit the (possibly-empty) run of raw text since the last placeholder, until this placeholder,
    // or until the end of the source string.
    CodeChunk.WithValue endIndex = matchVar.ref().and(matchVar.ref().dotAccess("index"), translationContext.codeGenerator());
    CodeChunk.WithValue unescape = GOOG_STRING_UNESCAPE_ENTITIES.call(translationVar.dotAccess("substring").call(lastIndexVar.ref(), endIndex));
    doBody.add(INCREMENTAL_DOM_TEXT.call(unescape));
    doBody.add(lastIndexVar.ref().assign(regexVar.ref().dotAccess("lastIndex")));
    // switch (match_XXX && match_XXX[0]) { ... }
    SwitchBuilder switchBuilder = CodeChunk.switch_(matchVar.ref().and(matchVar.ref().bracketAccess(CodeChunk.number(0)), translationContext.codeGenerator()));
    for (Map.Entry<String, MsgPlaceholderNode> ph : placeholderNames.entrySet()) {
        switchBuilder.case_(CodeChunk.stringLiteral(ph.getKey()), master.visitForUseByAssistantsAsCodeChunk(ph.getValue()));
    }
    doBody.add(switchBuilder.build());
    return CodeChunk.statements(CodeChunk.statements(translationVar.initialStatements()), regexVar, lastIndexVar, matchVar, DoWhile.builder().setCondition(matchVar.ref()).setBody(CodeChunk.statements(doBody)).build());
}
Also used : CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk) SwitchBuilder(com.google.template.soy.jssrc.dsl.SwitchBuilder) ArrayList(java.util.ArrayList) VariableDeclaration(com.google.template.soy.jssrc.dsl.VariableDeclaration) Map(java.util.Map) MsgPlaceholderNode(com.google.template.soy.soytree.MsgPlaceholderNode)

Example 9 with CodeChunk

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

the class GenIncrementalDomCodeVisitor method visitLetParamContentNode.

/**
 * Generates the content of a {@code let} or {@code param} statement. For HTML and attribute
 * let/param statements, the generated instructions inside the node are wrapped in a function
 * which will be optionally passed to another template and invoked in the correct location. All
 * other kinds of let statements are generated as a simple variable.
 */
private void visitLetParamContentNode(RenderUnitNode node, String generatedVarName) {
    // The html transform step, performed by HTMLTransformVisitor, ensures that
    // we always have a content kind specified.
    checkState(node.getContentKind() != null);
    IncrementalDomCodeBuilder jsCodeBuilder = getJsCodeBuilder();
    SanitizedContentKind prevContentKind = jsCodeBuilder.getContentKind();
    jsCodeBuilder.setContentKind(node.getContentKind());
    CodeChunk definition;
    switch(node.getContentKind()) {
        case HTML:
        case ATTRIBUTES:
            definition = VariableDeclaration.builder(generatedVarName).setRhs(CodeChunk.function(ImmutableList.<String>of(), visitChildrenReturningCodeChunk(node))).build();
            break;
        default:
            // We do our own initialization, so mark it as such.
            String outputVarName = generatedVarName + "_output";
            jsCodeBuilder.pushOutputVar(outputVarName).setOutputVarInited();
            definition = CodeChunk.statements(VariableDeclaration.builder(outputVarName).setRhs(LITERAL_EMPTY_STRING).build(), visitChildrenReturningCodeChunk(node), VariableDeclaration.builder(generatedVarName).setRhs(JsRuntime.sanitizedContentOrdainerFunctionForInternalBlocks(node.getContentKind()).call(id(outputVarName))).build());
            jsCodeBuilder.popOutputVar();
            break;
    }
    jsCodeBuilder.setContentKind(prevContentKind);
    jsCodeBuilder.append(definition);
}
Also used : CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk) SanitizedContentKind(com.google.template.soy.base.internal.SanitizedContentKind)

Example 10 with CodeChunk

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

the class GenIncrementalDomCodeVisitor method visitMsgFallbackGroupNode.

@Override
protected void visitMsgFallbackGroupNode(MsgFallbackGroupNode node) {
    CodeChunk.WithValue msgExpression;
    switch(node.getHtmlContext()) {
        case HTML_PCDATA:
            CodeChunk chunk = new AssistantForHtmlMsgs(this, /* master */
            jsSrcOptions, genCallCodeUtils, isComputableAsJsExprsVisitor, templateAliases, genJsExprsVisitor, templateTranslationContext, errorReporter).generateMsgGroupCode(node);
            getJsCodeBuilder().append(chunk);
            break;
        // must unescape the translations before passing them to the idom APIs.
        case HTML_NORMAL_ATTR_VALUE:
            msgExpression = new AssistantForAttributeMsgs(this, /* master */
            jsSrcOptions, genCallCodeUtils, isComputableAsJsExprsVisitor, templateAliases, genJsExprsVisitor, templateTranslationContext, errorReporter).generateMsgGroupVariable(node);
            getJsCodeBuilder().addChunkToOutputVar(GOOG_STRING_UNESCAPE_ENTITIES.call(msgExpression));
            break;
        default:
            msgExpression = getAssistantForMsgs().generateMsgGroupVariable(node);
            getJsCodeBuilder().addChunkToOutputVar(msgExpression);
            break;
    }
}
Also used : CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk)

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