Search in sources :

Example 1 with AppendableAndOptions

use of com.google.template.soy.jbcsrc.restricted.SoyJbcSrcPrintDirective.Streamable.AppendableAndOptions in project closure-templates by google.

the class SoyNodeCompiler method renderCallNode.

/**
 * Renders a {@link com.google.template.soy.jbcsrc.shared.CompiledTemplate} incrementally.
 *
 * <p>Similar to {@link #renderIncrementally(Expression, List, Label)}, we need to:
 *
 * <ul>
 *   <li>Stash the CompiledTemplate in a field {@code $currentCallee}, so that if we detach
 *       halfway through rendering we don't lose the value. Note, we could use the scope/variable
 *       system of {@link TemplateVariableManager} to manage this value, but we know there will
 *       only ever be 1 live at a time, so we can just manage the single special field ourselves.
 *   <li>Either apply all the streaming autoescapers to the current appendable and, stash it in
 *       the {@code $currentAppendable} field for the same reasons as above, or call {@link
 *       JbcSrcRuntime#applyEscapers} to apply non-streaming print directives.
 *   <li>Invoke {@link com.google.template.soy.jbcsrc.shared.CompiledTemplate#render} with the
 *       standard detach logic.
 *   <li>Clear the two fields once rendering is complete.
 * </ul>
 *
 * @param parametersReattachPoint The label where execution should resume if we need to detach
 *     while calculating parameters.
 * @param node The call node
 * @param calleeExpression The expression that resolves to a constructed instance of the template
 * @return A statement rendering the template.
 */
private Statement renderCallNode(Label parametersReattachPoint, CallNode node, Expression calleeExpression) {
    Statement initAppendable = Statement.NULL_STATEMENT;
    Statement clearAppendable = Statement.NULL_STATEMENT;
    Expression appendable;
    FieldRef currentCalleeField = variables.getCurrentCalleeField();
    // CallDelegateNodes because there is no guarantee that we can tell what the kind is.
    if (!areAllPrintDirectivesStreamable(node)) {
        calleeExpression = MethodRef.RUNTIME_APPLY_ESCAPERS.invoke(calleeExpression, getEscapingDirectivesList(node));
        appendable = appendableExpression;
    } else {
        AppendableAndOptions wrappedAppendable = applyStreamingEscapingDirectives(node.getEscapingDirectives(), appendableExpression, parameterLookup.getRenderContext(), variables);
        FieldRef currentAppendableField = variables.getCurrentAppendable();
        initAppendable = currentAppendableField.putInstanceField(thisVar, wrappedAppendable.appendable());
        appendable = currentAppendableField.accessor(thisVar);
        clearAppendable = currentAppendableField.putInstanceField(thisVar, constantNull(LOGGING_ADVISING_APPENDABLE_TYPE));
        if (wrappedAppendable.closeable()) {
            // make sure to call close before clearing
            clearAppendable = Statement.concat(// LoggingAdvisingAppendable
            currentAppendableField.accessor(thisVar).checkedCast(BytecodeUtils.CLOSEABLE_TYPE).invokeVoid(MethodRef.CLOSEABLE_CLOSE), clearAppendable);
        }
    }
    Statement initCallee = currentCalleeField.putInstanceField(thisVar, calleeExpression).labelStart(parametersReattachPoint);
    Expression callRender = currentCalleeField.accessor(thisVar).invoke(MethodRef.COMPILED_TEMPLATE_RENDER, appendable, parameterLookup.getRenderContext());
    Statement callCallee = detachState.detachForRender(callRender);
    Statement clearCallee = currentCalleeField.putInstanceField(thisVar, BytecodeUtils.constantNull(COMPILED_TEMPLATE_TYPE));
    return Statement.concat(initAppendable, initCallee, callCallee, clearCallee, clearAppendable);
}
Also used : FieldRef(com.google.template.soy.jbcsrc.restricted.FieldRef) SoyExpression(com.google.template.soy.jbcsrc.restricted.SoyExpression) Expression(com.google.template.soy.jbcsrc.restricted.Expression) Statement(com.google.template.soy.jbcsrc.restricted.Statement) AppendableAndOptions(com.google.template.soy.jbcsrc.restricted.SoyJbcSrcPrintDirective.Streamable.AppendableAndOptions)

Example 2 with AppendableAndOptions

use of com.google.template.soy.jbcsrc.restricted.SoyJbcSrcPrintDirective.Streamable.AppendableAndOptions in project closure-templates by google.

the class SoyNodeCompiler method renderIncrementally.

/**
 * Renders a {@link SoyValueProvider} incrementally via {@link SoyValueProvider#renderAndResolve}
 *
 * <p>The strategy is to:
 *
 * <ul>
 *   <li>Stash the SoyValueProvider in a field {@code $currentRenderee}, so that if we detach
 *       halfway through rendering we don't lose the value. Note, we could use the scope/variable
 *       system of {@link TemplateVariableManager} to manage this value, but we know there will
 *       only ever be 1 live at a time, so we can just manage the single special field ourselves.
 *   <li>Apply all the streaming autoescapers to the current appendable. Also, stash it in the
 *       {@code $currentAppendable} field for the same reasons as above.
 *   <li>Invoke {@link SoyValueProvider#renderAndResolve} with the standard detach logic.
 *   <li>Clear the two fields once rendering is complete.
 * </ul>
 *
 * <p>TODO(lukes): if the expression is a param, then this is kind of silly since it looks like
 *
 * <pre>{@code
 * SoyValueProvider localParam = this.param;
 * this.currentRenderee = localParam;
 * SoyValueProvider localRenderee = this.currentRenderee;
 * localRenderee.renderAndResolve();
 * }</pre>
 *
 * <p>In this case we could elide the currentRenderee altogether if we knew the soyValueProvider
 * expression was just a field read... And this is the _common_case for .renderAndResolve calls.
 * to actually do this we could add a mechanism similar to the SaveStrategy enum for expressions,
 * kind of like {@link Expression#isCheap()} which isn't that useful in practice.
 *
 * @param soyValueProvider The value to render incrementally
 * @param directives The streaming print directives applied to the expression
 * @param reattachPoint The point where execution should resume if the soyValueProvider detaches
 *     while being evaluated.
 * @return a statement for the full render.
 */
private Statement renderIncrementally(Expression soyValueProvider, List<PrintDirectiveNode> directives, Label reattachPoint) {
    // In this case we want to render the SoyValueProvider via renderAndResolve which will
    // enable incremental rendering of parameters for lazy transclusions!
    // This actually ends up looking a lot like how calls work so we use the same strategy.
    FieldRef currentRendereeField = variables.getCurrentRenderee();
    Statement initRenderee = currentRendereeField.putInstanceField(thisVar, soyValueProvider).labelStart(reattachPoint);
    Statement clearRenderee = currentRendereeField.putInstanceField(thisVar, constantNull(SOY_VALUE_PROVIDER_TYPE));
    // TODO(lukes): we should have similar logic for calls and message escaping
    Statement initAppendable = Statement.NULL_STATEMENT;
    Statement clearAppendable = Statement.NULL_STATEMENT;
    Expression appendable = appendableExpression;
    if (!directives.isEmpty()) {
        Label printDirectiveArgumentReattachPoint = new Label();
        AppendableAndOptions wrappedAppendable = applyStreamingPrintDirectives(directives, appendable, exprCompiler.asBasicCompiler(printDirectiveArgumentReattachPoint), parameterLookup.getRenderContext(), variables);
        FieldRef currentAppendableField = variables.getCurrentAppendable();
        initAppendable = currentAppendableField.putInstanceField(thisVar, wrappedAppendable.appendable()).labelStart(printDirectiveArgumentReattachPoint);
        appendable = currentAppendableField.accessor(thisVar);
        clearAppendable = currentAppendableField.putInstanceField(thisVar, constantNull(LOGGING_ADVISING_APPENDABLE_TYPE));
        if (wrappedAppendable.closeable()) {
            // make sure to call close before clearing
            clearAppendable = Statement.concat(// LoggingAdvisingAppendable
            currentAppendableField.accessor(thisVar).checkedCast(BytecodeUtils.CLOSEABLE_TYPE).invokeVoid(MethodRef.CLOSEABLE_CLOSE), clearAppendable);
        }
    }
    Expression callRenderAndResolve = currentRendereeField.accessor(thisVar).invoke(MethodRef.SOY_VALUE_PROVIDER_RENDER_AND_RESOLVE, appendable, // TODO(lukes): pass a real value here when we have expression use analysis.
    constant(false));
    Statement doCall = detachState.detachForRender(callRenderAndResolve);
    return Statement.concat(initRenderee, initAppendable, doCall, clearAppendable, clearRenderee);
}
Also used : FieldRef(com.google.template.soy.jbcsrc.restricted.FieldRef) SoyExpression(com.google.template.soy.jbcsrc.restricted.SoyExpression) Expression(com.google.template.soy.jbcsrc.restricted.Expression) Statement(com.google.template.soy.jbcsrc.restricted.Statement) AppendableAndOptions(com.google.template.soy.jbcsrc.restricted.SoyJbcSrcPrintDirective.Streamable.AppendableAndOptions) Label(org.objectweb.asm.Label)

Example 3 with AppendableAndOptions

use of com.google.template.soy.jbcsrc.restricted.SoyJbcSrcPrintDirective.Streamable.AppendableAndOptions in project closure-templates by google.

the class MsgCompiler method handleTranslationWithPlaceholders.

/**
 * Handles a complex message with placeholders.
 */
private Statement handleTranslationWithPlaceholders(MsgNode msg, ImmutableList<SoyPrintDirective> escapingDirectives, Expression soyMsgParts, Expression locale, ImmutableList<SoyMsgPart> parts) {
    // We need to render placeholders into a buffer and then pack them into a map to pass to
    // Runtime.renderSoyMsgWithPlaceholders.
    Expression placeholderMap = variables.getMsgPlaceholderMapField().accessor(thisVar);
    Map<String, Statement> placeholderNameToPutStatement = new LinkedHashMap<>();
    putPlaceholdersIntoMap(placeholderMap, msg, parts, placeholderNameToPutStatement);
    // sanity check
    checkState(!placeholderNameToPutStatement.isEmpty());
    variables.setMsgPlaceholderMapMinSize(placeholderNameToPutStatement.size());
    Statement populateMap = Statement.concat(placeholderNameToPutStatement.values());
    Statement clearMap = placeholderMap.invokeVoid(MethodRef.LINKED_HASH_MAP_CLEAR);
    Statement render;
    if (areAllPrintDirectivesStreamable(escapingDirectives)) {
        // No need to save/restore since rendering a message doesn't detach.  All detaching for data
        // should have already happened as part of constructing the placholder map.
        AppendableAndOptions wrappedAppendable = applyStreamingEscapingDirectives(escapingDirectives, appendableExpression, parameterLookup.getRenderContext(), variables);
        Statement initAppendable = Statement.NULL_STATEMENT;
        Statement clearAppendable = Statement.NULL_STATEMENT;
        Expression appendableExpression = wrappedAppendable.appendable();
        if (wrappedAppendable.closeable()) {
            Scope scope = variables.enterScope();
            Variable appendableVar = scope.createTemporary("msg_appendable", wrappedAppendable.appendable());
            initAppendable = appendableVar.initializer();
            appendableExpression = appendableVar.local();
            clearAppendable = Statement.concat(appendableVar.local().checkedCast(BytecodeUtils.CLOSEABLE_TYPE).invokeVoid(MethodRef.CLOSEABLE_CLOSE), scope.exitScope());
        }
        render = Statement.concat(initAppendable, MethodRef.RUNTIME_RENDER_SOY_MSG_PARTS_WITH_PLACEHOLDERS.invokeVoid(soyMsgParts, locale, placeholderMap, appendableExpression), clearAppendable);
    } else {
        // render into the handy buffer we already have!
        Statement renderToBuffer = MethodRef.RUNTIME_RENDER_SOY_MSG_PARTS_WITH_PLACEHOLDERS.invokeVoid(soyMsgParts, locale, placeholderMap, tempBuffer());
        // N.B. the type here is always 'string'
        SoyExpression value = SoyExpression.forString(tempBuffer().invoke(MethodRef.ADVISING_STRING_BUILDER_GET_AND_CLEAR));
        for (SoyPrintDirective directive : escapingDirectives) {
            value = parameterLookup.getRenderContext().applyPrintDirective(directive, value);
        }
        render = Statement.concat(renderToBuffer, appendableExpression.appendString(value.coerceToString()).toStatement());
    }
    return Statement.concat(populateMap, render, clearMap);
}
Also used : SoyExpression(com.google.template.soy.jbcsrc.restricted.SoyExpression) Variable(com.google.template.soy.jbcsrc.TemplateVariableManager.Variable) Scope(com.google.template.soy.jbcsrc.TemplateVariableManager.Scope) SoyExpression(com.google.template.soy.jbcsrc.restricted.SoyExpression) Expression(com.google.template.soy.jbcsrc.restricted.Expression) SoyPrintDirective(com.google.template.soy.shared.restricted.SoyPrintDirective) Statement(com.google.template.soy.jbcsrc.restricted.Statement) AppendableAndOptions(com.google.template.soy.jbcsrc.restricted.SoyJbcSrcPrintDirective.Streamable.AppendableAndOptions) LinkedHashMap(java.util.LinkedHashMap)

Example 4 with AppendableAndOptions

use of com.google.template.soy.jbcsrc.restricted.SoyJbcSrcPrintDirective.Streamable.AppendableAndOptions in project closure-templates by google.

the class PrintDirectives method applyStreamingPrintDirectivesTo.

private static AppendableAndOptions applyStreamingPrintDirectivesTo(List<DirectiveWithArgs> directivesToApply, Expression appendable, JbcSrcPluginContext context, TemplateVariableManager variableManager) {
    final List<LocalVariable> closeables = new ArrayList<>();
    final List<Variable> appendableVars = new ArrayList<>();
    Scope scope = variableManager.enterScope();
    AppendableAndOptions prev = AppendableAndOptions.create(appendable);
    Variable prevVar = scope.createTemporary("tmp_appendable", appendable);
    appendableVars.add(prevVar);
    // appendable with the last directive first. so iterate in reverse order.
    for (DirectiveWithArgs directiveToApply : Lists.reverse(directivesToApply)) {
        AppendableAndOptions curr = directiveToApply.apply(context, prevVar.local());
        Variable currVar = scope.createTemporary("tmp_appendable", curr.appendable());
        appendableVars.add(currVar);
        if (curr.closeable()) {
            closeables.add(currVar.local());
        }
        prev = curr;
        prevVar = currVar;
    }
    // Check if we need to apply a wrapper to make sure close propagates to all the right places
    // this is necessary if there are multiple closeable wrappers.
    final Expression appendableExpression;
    final boolean closeable;
    if (closeables.isEmpty()) {
        appendableExpression = prev.appendable();
        closeable = false;
    } else if (closeables.size() == 1 && prev.closeable()) {
        // there is exactly one closeable and it is first, we don't need a wrapper
        appendableExpression = prev.appendable();
        closeable = true;
    } else {
        // there is either more than one closeable, or it is not the first one, so we need a wrapper
        // We need to reverse the list of closeables so that we close them in the correct order. for
        // example, given '|foo|bar' we will first wrap the delegate with bar and then with foo but we
        // need to close foo first.
        appendableExpression = RUNTIME_PROPAGATE_CLOSE.invoke(Iterables.getLast(appendableVars).local(), BytecodeUtils.asImmutableList(Lists.reverse(closeables)));
        closeable = true;
    }
    final Statement exitScope = scope.exitScope();
    Expression result = new Expression(appendableExpression.resultType()) {

        @Override
        protected void doGen(CodeBuilder adapter) {
            for (Variable var : appendableVars) {
                var.initializer().gen(adapter);
            }
            appendableExpression.gen(adapter);
            exitScope.gen(adapter);
        }
    };
    if (closeable) {
        return AppendableAndOptions.createCloseable(result);
    } else {
        return AppendableAndOptions.create(result);
    }
}
Also used : Variable(com.google.template.soy.jbcsrc.TemplateVariableManager.Variable) LocalVariable(com.google.template.soy.jbcsrc.restricted.LocalVariable) Scope(com.google.template.soy.jbcsrc.TemplateVariableManager.Scope) SoyExpression(com.google.template.soy.jbcsrc.restricted.SoyExpression) Expression(com.google.template.soy.jbcsrc.restricted.Expression) Statement(com.google.template.soy.jbcsrc.restricted.Statement) AppendableAndOptions(com.google.template.soy.jbcsrc.restricted.SoyJbcSrcPrintDirective.Streamable.AppendableAndOptions) LocalVariable(com.google.template.soy.jbcsrc.restricted.LocalVariable) ArrayList(java.util.ArrayList) CodeBuilder(com.google.template.soy.jbcsrc.restricted.CodeBuilder)

Aggregations

Expression (com.google.template.soy.jbcsrc.restricted.Expression)4 SoyExpression (com.google.template.soy.jbcsrc.restricted.SoyExpression)4 AppendableAndOptions (com.google.template.soy.jbcsrc.restricted.SoyJbcSrcPrintDirective.Streamable.AppendableAndOptions)4 Statement (com.google.template.soy.jbcsrc.restricted.Statement)4 Scope (com.google.template.soy.jbcsrc.TemplateVariableManager.Scope)2 Variable (com.google.template.soy.jbcsrc.TemplateVariableManager.Variable)2 FieldRef (com.google.template.soy.jbcsrc.restricted.FieldRef)2 CodeBuilder (com.google.template.soy.jbcsrc.restricted.CodeBuilder)1 LocalVariable (com.google.template.soy.jbcsrc.restricted.LocalVariable)1 SoyPrintDirective (com.google.template.soy.shared.restricted.SoyPrintDirective)1 ArrayList (java.util.ArrayList)1 LinkedHashMap (java.util.LinkedHashMap)1 Label (org.objectweb.asm.Label)1