Search in sources :

Example 6 with Expression

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

the class SoyNodeCompiler method visitPrintNode.

@Override
protected Statement visitPrintNode(PrintNode node) {
    if (node.getExpr().getRoot() instanceof FunctionNode) {
        FunctionNode fn = (FunctionNode) node.getExpr().getRoot();
        if (fn.getSoyFunction() instanceof LoggingFunction) {
            return visitLoggingFunction(node, fn, (LoggingFunction) fn.getSoyFunction());
        }
    }
    // evaluates to a SoyValueProvider.  This will allow us to render incrementally.
    if (areAllPrintDirectivesStreamable(node)) {
        Label reattachPoint = new Label();
        ExprRootNode expr = node.getExpr();
        Optional<Expression> asSoyValueProvider = expressionToSoyValueProviderCompiler.compileAvoidingBoxing(expr, reattachPoint);
        if (asSoyValueProvider.isPresent()) {
            return renderIncrementally(asSoyValueProvider.get(), node.getChildren(), reattachPoint);
        }
    }
    // otherwise we need to apply some non-streaming print directives, or the expression would
    // require boxing to be a print directive (which usually means it is quite trivial).
    Label reattachPoint = new Label();
    SoyExpression value = compilePrintNodeAsExpression(node, reattachPoint);
    // TODO(lukes): call value.render?
    AppendableExpression renderSoyValue = appendableExpression.appendString(value.coerceToString()).labelStart(reattachPoint);
    Statement stmt;
    if (shouldCheckBuffer(node)) {
        stmt = detachState.detachLimited(renderSoyValue);
    } else {
        stmt = renderSoyValue.toStatement();
    }
    return stmt;
}
Also used : SoyExpression(com.google.template.soy.jbcsrc.restricted.SoyExpression) SoyExpression(com.google.template.soy.jbcsrc.restricted.SoyExpression) Expression(com.google.template.soy.jbcsrc.restricted.Expression) Statement(com.google.template.soy.jbcsrc.restricted.Statement) FunctionNode(com.google.template.soy.exprtree.FunctionNode) Label(org.objectweb.asm.Label) LoggingFunction(com.google.template.soy.logging.LoggingFunction) ExprRootNode(com.google.template.soy.exprtree.ExprRootNode)

Example 7 with Expression

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

the class SoyNodeCompiler method visitForNode.

@Override
protected Statement visitForNode(ForNode node) {
    ForNonemptyNode nonEmptyNode = (ForNonemptyNode) node.getChild(0);
    Optional<RangeArgs> exprAsRangeArgs = RangeArgs.createFromNode(node);
    Scope scope = variables.enterScope();
    final Variable indexVar;
    final List<Statement> initializers = new ArrayList<>();
    final Variable sizeVar;
    final Variable itemVar;
    if (exprAsRangeArgs.isPresent()) {
        final CompiledForeachRangeArgs compiledArgs = calculateRangeArgs(node, scope);
        initializers.addAll(compiledArgs.initStatements());
        // The size is just the number of items in the range.  The logic is a little tricky so we
        // implement it in a runtime function: JbcsrcRuntime.rangeLoopLength
        sizeVar = scope.createSynthetic(SyntheticVarName.foreachLoopLength(nonEmptyNode), MethodRef.RUNTIME_RANGE_LOOP_LENGTH.invoke(compiledArgs.start(), compiledArgs.end(), compiledArgs.step()), DERIVED);
        indexVar = scope.createSynthetic(SyntheticVarName.foreachLoopIndex(nonEmptyNode), constant(0), STORE);
        itemVar = scope.create(nonEmptyNode.getVarName(), new Expression(Type.LONG_TYPE, Feature.CHEAP) {

            @Override
            protected void doGen(CodeBuilder adapter) {
                // executes ((long) start + index * step)
                compiledArgs.start().gen(adapter);
                compiledArgs.step().gen(adapter);
                indexVar.local().gen(adapter);
                adapter.visitInsn(Opcodes.IMUL);
                adapter.visitInsn(Opcodes.IADD);
                adapter.cast(Type.INT_TYPE, Type.LONG_TYPE);
            }
        }, DERIVED);
    } else {
        SoyExpression expr = exprCompiler.compile(node.getExpr()).unboxAs(List.class);
        Variable listVar = scope.createSynthetic(SyntheticVarName.foreachLoopList(nonEmptyNode), expr, STORE);
        initializers.add(listVar.initializer());
        sizeVar = scope.createSynthetic(SyntheticVarName.foreachLoopLength(nonEmptyNode), MethodRef.LIST_SIZE.invoke(listVar.local()), DERIVED);
        indexVar = scope.createSynthetic(SyntheticVarName.foreachLoopIndex(nonEmptyNode), constant(0), STORE);
        itemVar = scope.create(nonEmptyNode.getVarName(), MethodRef.LIST_GET.invoke(listVar.local(), indexVar.local()).checkedCast(SOY_VALUE_PROVIDER_TYPE), DERIVED);
    }
    initializers.add(sizeVar.initializer());
    final Statement loopBody = visitChildrenInNewScope(nonEmptyNode);
    final Statement exitScope = scope.exitScope();
    // it important for this to be generated after exitScope is called (or before enterScope)
    final Statement emptyBlock = node.numChildren() == 2 ? visitChildrenInNewScope(node.getChild(1)) : null;
    return new Statement() {

        @Override
        protected void doGen(CodeBuilder adapter) {
            for (Statement initializer : initializers) {
                initializer.gen(adapter);
            }
            sizeVar.local().gen(adapter);
            Label emptyListLabel = new Label();
            adapter.ifZCmp(Opcodes.IFEQ, emptyListLabel);
            indexVar.initializer().gen(adapter);
            Label loopStart = adapter.mark();
            itemVar.initializer().gen(adapter);
            loopBody.gen(adapter);
            // index++
            adapter.iinc(indexVar.local().index(), 1);
            indexVar.local().gen(adapter);
            sizeVar.local().gen(adapter);
            // if index < list.size(), goto loopstart
            adapter.ifICmp(Opcodes.IFLT, loopStart);
            // exit the loop
            exitScope.gen(adapter);
            if (emptyBlock != null) {
                Label skipIfEmptyBlock = new Label();
                adapter.goTo(skipIfEmptyBlock);
                adapter.mark(emptyListLabel);
                emptyBlock.gen(adapter);
                adapter.mark(skipIfEmptyBlock);
            } else {
                adapter.mark(emptyListLabel);
            }
        }
    };
}
Also used : Variable(com.google.template.soy.jbcsrc.TemplateVariableManager.Variable) RangeArgs(com.google.template.soy.shared.RangeArgs) Statement(com.google.template.soy.jbcsrc.restricted.Statement) ArrayList(java.util.ArrayList) Label(org.objectweb.asm.Label) CodeBuilder(com.google.template.soy.jbcsrc.restricted.CodeBuilder) SoyExpression(com.google.template.soy.jbcsrc.restricted.SoyExpression) Scope(com.google.template.soy.jbcsrc.TemplateVariableManager.Scope) SoyExpression(com.google.template.soy.jbcsrc.restricted.SoyExpression) Expression(com.google.template.soy.jbcsrc.restricted.Expression) ForNonemptyNode(com.google.template.soy.soytree.ForNonemptyNode)

Example 8 with Expression

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

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

the class SoyNodeCompiler method visitVeLogNode.

@Override
protected Statement visitVeLogNode(VeLogNode node) {
    final Label restartPoint = new Label();
    final Expression hasLogger = parameterLookup.getRenderContext().hasLogger();
    final boolean hasLogonlyExpression = node.getLogonlyExpression() != null;
    final Expression logonlyExpression = hasLogonlyExpression ? exprCompiler.compile(node.getLogonlyExpression(), restartPoint).unboxAs(boolean.class) : BytecodeUtils.constant(false);
    final Statement enterStatement = appendableExpression.enterLoggableElement(MethodRef.LOG_STATEMENT_CREATE.invoke(BytecodeUtils.constant(node.getLoggingId()), node.getConfigExpression() == null ? BytecodeUtils.constantNull(BytecodeUtils.MESSAGE_TYPE) : exprCompiler.compile(node.getConfigExpression(), restartPoint).unboxAs(Message.class), logonlyExpression)).toStatement();
    final Statement body = Statement.concat(visitChildren(node));
    final Statement exitStatement = appendableExpression.exitLoggableElement().toStatement();
    return new Statement() {

        @Override
        protected void doGen(CodeBuilder cb) {
            Label noLogger = new Label();
            hasLogger.gen(cb);
            cb.ifZCmp(EQ, noLogger);
            enterStatement.gen(cb);
            if (hasLogonlyExpression) {
                Label bodyLabel = new Label();
                cb.goTo(bodyLabel);
                cb.mark(noLogger);
                // if we get here then we have a logonly expression and no logger.
                logonlyExpression.gen(cb);
                cb.ifZCmp(EQ, bodyLabel);
                cb.throwException(BytecodeUtils.ILLEGAL_STATE_EXCEPTION_TYPE, "Cannot set logonly=\"true\" unless there is a logger configured");
                cb.mark(bodyLabel);
            } else {
                cb.mark(noLogger);
            }
            body.gen(cb);
            Label end = new Label();
            hasLogger.gen(cb);
            cb.ifZCmp(EQ, end);
            exitStatement.gen(cb);
            cb.mark(end);
        }
    }.labelStart(restartPoint);
}
Also used : SoyExpression(com.google.template.soy.jbcsrc.restricted.SoyExpression) Expression(com.google.template.soy.jbcsrc.restricted.Expression) Statement(com.google.template.soy.jbcsrc.restricted.Statement) Label(org.objectweb.asm.Label) CodeBuilder(com.google.template.soy.jbcsrc.restricted.CodeBuilder)

Example 10 with Expression

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

the class SoyNodeCompiler method visitSwitchNode.

@Override
protected Statement visitSwitchNode(SwitchNode node) {
    // A few special cases:
    // 1. only a {default} block.  In this case we can skip all the switch logic and temporaries
    // 2. no children.  Just return the empty statement
    // Note that in both of these cases we do not evalutate (or generate code) for the switch
    // expression.
    List<BlockNode> children = node.getChildren();
    if (children.isEmpty()) {
        return Statement.NULL_STATEMENT;
    }
    if (children.size() == 1 && children.get(0) instanceof SwitchDefaultNode) {
        return visitChildrenInNewScope(children.get(0));
    }
    // otherwise we need to evaluate the switch variable and generate dispatching logic.
    SoyExpression switchVar = exprCompiler.compile(node.getExpr());
    Scope scope = variables.enterScope();
    Variable variable = scope.createSynthetic(SyntheticVarName.forSwitch(node), switchVar, STORE);
    Statement initializer = variable.initializer();
    switchVar = switchVar.withSource(variable.local());
    List<IfBlock> cases = new ArrayList<>();
    Optional<Statement> defaultBlock = Optional.absent();
    for (SoyNode child : children) {
        if (child instanceof SwitchCaseNode) {
            SwitchCaseNode caseNode = (SwitchCaseNode) child;
            Label reattachPoint = new Label();
            List<Expression> comparisons = new ArrayList<>();
            for (ExprRootNode caseExpr : caseNode.getExprList()) {
                comparisons.add(compareSoyEquals(switchVar, exprCompiler.compile(caseExpr, reattachPoint)));
            }
            Expression condition = BytecodeUtils.logicalOr(comparisons).labelStart(reattachPoint);
            Statement block = visitChildrenInNewScope(caseNode);
            cases.add(IfBlock.create(condition, block));
        } else {
            SwitchDefaultNode defaultNode = (SwitchDefaultNode) child;
            defaultBlock = Optional.of(visitChildrenInNewScope(defaultNode));
        }
    }
    Statement exitScope = scope.exitScope();
    // generation that we could maybe use
    return Statement.concat(initializer, ControlFlow.ifElseChain(cases, defaultBlock), exitScope);
}
Also used : BlockNode(com.google.template.soy.soytree.SoyNode.BlockNode) SoyNode(com.google.template.soy.soytree.SoyNode) Variable(com.google.template.soy.jbcsrc.TemplateVariableManager.Variable) Statement(com.google.template.soy.jbcsrc.restricted.Statement) ArrayList(java.util.ArrayList) Label(org.objectweb.asm.Label) ExprRootNode(com.google.template.soy.exprtree.ExprRootNode) SoyExpression(com.google.template.soy.jbcsrc.restricted.SoyExpression) Scope(com.google.template.soy.jbcsrc.TemplateVariableManager.Scope) SoyExpression(com.google.template.soy.jbcsrc.restricted.SoyExpression) Expression(com.google.template.soy.jbcsrc.restricted.Expression) SwitchCaseNode(com.google.template.soy.soytree.SwitchCaseNode) IfBlock(com.google.template.soy.jbcsrc.ControlFlow.IfBlock) SwitchDefaultNode(com.google.template.soy.soytree.SwitchDefaultNode)

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