Search in sources :

Example 11 with Expression

use of com.google.template.soy.jbcsrc.restricted.Expression 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 12 with Expression

use of com.google.template.soy.jbcsrc.restricted.Expression in project closure-templates by google.

the class SoyNodeCompiler method visitCallDelegateNode.

/**
 * Given this delcall: {@code {delcall foo.bar variant="$expr" allowemptydefault="true"}}
 *
 * <p>Generate code that looks like:
 *
 * <pre>{@code
 * renderContext.getDeltemplate("foo.bar", <variant-expression>, true)
 *     .create(<prepareParameters>, ijParams)
 *     .render(appendable, renderContext)
 *
 * }</pre>
 *
 * <p>We share logic with {@link #visitCallBasicNode(CallBasicNode)} around the actual calling
 * convention (setting up detaches, storing the template in a field). As well as the logic for
 * preparing the data record. The only interesting part of delcalls is calculating the {@code
 * variant} and the fact that we have to invoke the {@link RenderContext} runtime to do the
 * deltemplate lookup.
 */
@Override
protected Statement visitCallDelegateNode(CallDelegateNode node) {
    Label reattachPoint = new Label();
    Expression variantExpr;
    if (node.getDelCalleeVariantExpr() == null) {
        variantExpr = constant("");
    } else {
        variantExpr = exprCompiler.compile(node.getDelCalleeVariantExpr(), reattachPoint).coerceToString();
    }
    Expression calleeExpression = parameterLookup.getRenderContext().getDeltemplate(node.getDelCalleeName(), variantExpr, node.allowEmptyDefault(), prepareParamsHelper(node, reattachPoint), parameterLookup.getIjRecord());
    return renderCallNode(reattachPoint, node, calleeExpression);
}
Also used : SoyExpression(com.google.template.soy.jbcsrc.restricted.SoyExpression) Expression(com.google.template.soy.jbcsrc.restricted.Expression) Label(org.objectweb.asm.Label)

Example 13 with Expression

use of com.google.template.soy.jbcsrc.restricted.Expression in project closure-templates by google.

the class DetachState method generateReattachTable.

/**
 * Returns a statement that generates the reattach jump table.
 *
 * <p>Note: This statement should be the <em>first</em> statement in any detachable method.
 */
Statement generateReattachTable() {
    final Expression readField = stateField.accessor(thisExpr);
    final Statement defaultCase = Statement.throwExpression(MethodRef.RUNTIME_UNEXPECTED_STATE_ERROR.invoke(readField));
    return new Statement() {

        @Override
        protected void doGen(final CodeBuilder adapter) {
            int[] keys = new int[reattaches.size()];
            for (int i = 0; i < keys.length; i++) {
                keys[i] = i;
            }
            readField.gen(adapter);
            // Generate a switch table.  Note, while it might be preferable to just 'goto state', Java
            // doesn't allow computable gotos (probably because it makes verification impossible).  So
            // instead we emulate that with a jump table.  And anyway we still need to execute 'restore'
            // logic to repopulate the local variable tables, so the 'case' statements are a natural
            // place for that logic to live.
            adapter.tableSwitch(keys, new TableSwitchGenerator() {

                @Override
                public void generateCase(int key, Label end) {
                    if (key == 0) {
                        // State 0 is special, it means initial state, so we just jump to the very end
                        adapter.goTo(end);
                        return;
                    }
                    ReattachState reattachState = reattaches.get(key);
                    // restore and jump!
                    reattachState.restoreStatement().gen(adapter);
                    adapter.goTo(reattachState.reattachPoint());
                }

                @Override
                public void generateDefault() {
                    defaultCase.gen(adapter);
                }
            }, // there are no 'holes' meaning that it is compact in the bytecode.
            true);
        }
    };
}
Also used : Expression(com.google.template.soy.jbcsrc.restricted.Expression) Statement.returnExpression(com.google.template.soy.jbcsrc.restricted.Statement.returnExpression) TableSwitchGenerator(org.objectweb.asm.commons.TableSwitchGenerator) Statement(com.google.template.soy.jbcsrc.restricted.Statement) Label(org.objectweb.asm.Label) CodeBuilder(com.google.template.soy.jbcsrc.restricted.CodeBuilder)

Example 14 with Expression

use of com.google.template.soy.jbcsrc.restricted.Expression in project closure-templates by google.

the class DetachState method detachLimited.

/**
 * Returns a Statement that will conditionally detach if the given {@link AdvisingAppendable} has
 * been {@link AdvisingAppendable#softLimitReached() output limited}.
 */
Statement detachLimited(AppendableExpression appendable) {
    if (!appendable.supportsSoftLimiting()) {
        return appendable.toStatement();
    }
    final Label reattachPoint = new Label();
    final SaveRestoreState saveRestoreState = variables.saveRestoreState();
    Statement restore = saveRestoreState.restore();
    int state = addState(reattachPoint, restore);
    final Expression isSoftLimited = appendable.softLimitReached();
    final Statement returnLimited = returnExpression(MethodRef.RENDER_RESULT_LIMITED.invoke());
    final Statement saveState = stateField.putInstanceField(thisExpr, BytecodeUtils.constant(state));
    return new Statement() {

        @Override
        protected void doGen(CodeBuilder adapter) {
            isSoftLimited.gen(adapter);
            // if !softLimited
            adapter.ifZCmp(Opcodes.IFEQ, reattachPoint);
            // ok we were limited, save state and return
            // save locals
            saveRestoreState.save().gen(adapter);
            // save the state field
            saveState.gen(adapter);
            returnLimited.gen(adapter);
            // Note, the reattach point for 'limited' is _after_ the check.  That means we do not
            // recheck the limit state.  So if a caller calls us back without freeing any buffer we
            // will print more before checking again.  This is fine, because our caller is breaking the
            // contract.
            adapter.mark(reattachPoint);
        }
    };
}
Also used : Expression(com.google.template.soy.jbcsrc.restricted.Expression) Statement.returnExpression(com.google.template.soy.jbcsrc.restricted.Statement.returnExpression) SaveRestoreState(com.google.template.soy.jbcsrc.TemplateVariableManager.SaveRestoreState) Statement(com.google.template.soy.jbcsrc.restricted.Statement) Label(org.objectweb.asm.Label) CodeBuilder(com.google.template.soy.jbcsrc.restricted.CodeBuilder)

Example 15 with Expression

use of com.google.template.soy.jbcsrc.restricted.Expression in project closure-templates by google.

the class TemplateVariableManager method defineFields.

// TODO(lukes): consider moving all these optional 'one per template' fields to a different object
// for management.
/**
 * Defines all the fields necessary for the registered variables.
 *
 * @return a statement to initialize the fields
 */
@CheckReturnValue
Statement defineFields(ClassVisitor writer) {
    List<Statement> initializers = new ArrayList<>();
    for (Variable var : allVariables) {
        var.maybeDefineField(writer);
    }
    if (currentCalleeField != null) {
        currentCalleeField.defineField(writer);
    }
    if (currentRendereeField != null) {
        currentRendereeField.defineField(writer);
    }
    if (currentAppendable != null) {
        currentAppendable.defineField(writer);
    }
    if (tempBufferField != null) {
        tempBufferField.defineField(writer);
        // If a template needs a temp buffer then we initialize it eagerly in the template constructor
        // this may be wasteful in the case that the buffer is only used on certain call paths, but
        // if it turns out to be expensive, this could always be solved by an author by refactoring
        // their templates (e.g. extract the conditional logic into another template)
        final Expression newStringBuilder = MethodRef.LOGGING_ADVISING_APPENDABLE_BUFFERING.invoke();
        initializers.add(new Statement() {

            @Override
            protected void doGen(CodeBuilder adapter) {
                adapter.loadThis();
                newStringBuilder.gen(adapter);
                tempBufferField.putUnchecked(adapter);
            }
        });
    }
    if (msgPlaceholderMapField != null) {
        msgPlaceholderMapField.defineField(writer);
        // same comment as above about eager initialization.
        final Expression newHashMap = ConstructorRef.LINKED_HASH_MAP_SIZE.construct(constant(msgPlaceholderMapInitialSize));
        initializers.add(new Statement() {

            @Override
            protected void doGen(CodeBuilder adapter) {
                adapter.loadThis();
                newHashMap.gen(adapter);
                msgPlaceholderMapField.putUnchecked(adapter);
            }
        });
    }
    return Statement.concat(initializers);
}
Also used : LocalVariable(com.google.template.soy.jbcsrc.restricted.LocalVariable) Expression(com.google.template.soy.jbcsrc.restricted.Expression) Statement(com.google.template.soy.jbcsrc.restricted.Statement) ArrayList(java.util.ArrayList) CodeBuilder(com.google.template.soy.jbcsrc.restricted.CodeBuilder) CheckReturnValue(javax.annotation.CheckReturnValue)

Aggregations

Expression (com.google.template.soy.jbcsrc.restricted.Expression)32 SoyExpression (com.google.template.soy.jbcsrc.restricted.SoyExpression)26 Statement (com.google.template.soy.jbcsrc.restricted.Statement)15 Label (org.objectweb.asm.Label)15 CodeBuilder (com.google.template.soy.jbcsrc.restricted.CodeBuilder)8 ArrayList (java.util.ArrayList)8 Statement.returnExpression (com.google.template.soy.jbcsrc.restricted.Statement.returnExpression)5 Scope (com.google.template.soy.jbcsrc.TemplateVariableManager.Scope)4 Variable (com.google.template.soy.jbcsrc.TemplateVariableManager.Variable)4 LocalVariable (com.google.template.soy.jbcsrc.restricted.LocalVariable)4 AppendableAndOptions (com.google.template.soy.jbcsrc.restricted.SoyJbcSrcPrintDirective.Streamable.AppendableAndOptions)4 ImmutableList (com.google.common.collect.ImmutableList)2 ExprRootNode (com.google.template.soy.exprtree.ExprRootNode)2 SoyClassWriter (com.google.template.soy.jbcsrc.internal.SoyClassWriter)2 FieldRef (com.google.template.soy.jbcsrc.restricted.FieldRef)2 TypeInfo (com.google.template.soy.jbcsrc.restricted.TypeInfo)2 RangeArgs (com.google.template.soy.shared.RangeArgs)2 SoyPrintDirective (com.google.template.soy.shared.restricted.SoyPrintDirective)2 ForNonemptyNode (com.google.template.soy.soytree.ForNonemptyNode)2 LinkedHashMap (java.util.LinkedHashMap)2