Search in sources :

Example 1 with CodeBuilder

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

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

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

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

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

the class DetachState method detachForRender.

/**
 * Generate detach logic for calls.
 *
 * <p>Calls are a little different due to a desire to minimize the cost of detaches. We assume
 * that if a given call site detaches once, it is more likely to detach multiple times. So we
 * generate code that looks like:
 *
 * <pre>{@code
 * RenderResult initialResult = template.render(appendable, renderContext);
 * if (!initialResult.isDone()) {
 *   // save all fields
 *   state = REATTACH_RENDER;
 *   return initialResult;
 * } else {
 *   goto END;
 * }
 * REATTACH_RENDER:
 * // restore nothing!
 * RenderResult secondResult = template.render(appendable, renderContext);
 * if (!secondResult.isDone()) {
 *   // saveFields
 *   state = REATTACH_RENDER;
 *   return secondResult;
 * } else {
 *   // restore all fields
 *   goto END;
 * }
 * END:
 * }</pre>
 *
 * <p>With this technique we save re-running the save-restore logic for multiple detaches from the
 * same call site. This should be especially useful for top level templates.
 *
 * @param callRender an Expression that can generate code to call the render method, should be
 *     safe to generate more than once.
 */
Statement detachForRender(final Expression callRender) {
    checkArgument(callRender.resultType().equals(RENDER_RESULT_TYPE));
    final Label reattachRender = new Label();
    final SaveRestoreState saveRestoreState = variables.saveRestoreState();
    // We pass NULL statement for the restore logic since we handle that ourselves below
    int state = addState(reattachRender, Statement.NULL_STATEMENT);
    final Statement saveState = stateField.putInstanceField(thisExpr, BytecodeUtils.constant(state));
    return new Statement() {

        @Override
        protected void doGen(CodeBuilder adapter) {
            // Legend: RR = RenderResult, Z = boolean
            // Stack: RR
            callRender.gen(adapter);
            // Stack: RR, RR
            adapter.dup();
            // Stack: RR, Z
            MethodRef.RENDER_RESULT_IS_DONE.invokeUnchecked(adapter);
            // if isDone goto Done
            Label end = new Label();
            // Stack: RR
            adapter.ifZCmp(Opcodes.IFNE, end);
            saveRestoreState.save().gen(adapter);
            saveState.gen(adapter);
            adapter.returnValue();
            adapter.mark(reattachRender);
            // Stack: RR
            callRender.gen(adapter);
            // Stack: RR, RR
            adapter.dup();
            // Stack: RR, Z
            MethodRef.RENDER_RESULT_IS_DONE.invokeUnchecked(adapter);
            // if isDone goto restore
            Label restore = new Label();
            // Stack: RR
            adapter.ifZCmp(Opcodes.IFNE, restore);
            // no need to save or restore anything
            adapter.returnValue();
            // Stack: RR
            adapter.mark(restore);
            saveRestoreState.restore().gen(adapter);
            // Stack: RR
            adapter.mark(end);
            // Stack:
            adapter.pop();
        }
    };
}
Also used : 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)

Aggregations

CodeBuilder (com.google.template.soy.jbcsrc.restricted.CodeBuilder)13 Statement (com.google.template.soy.jbcsrc.restricted.Statement)13 Label (org.objectweb.asm.Label)11 Expression (com.google.template.soy.jbcsrc.restricted.Expression)8 LocalVariable (com.google.template.soy.jbcsrc.restricted.LocalVariable)6 ArrayList (java.util.ArrayList)4 SoyExpression (com.google.template.soy.jbcsrc.restricted.SoyExpression)3 SaveRestoreState (com.google.template.soy.jbcsrc.TemplateVariableManager.SaveRestoreState)2 Scope (com.google.template.soy.jbcsrc.TemplateVariableManager.Scope)2 Variable (com.google.template.soy.jbcsrc.TemplateVariableManager.Variable)2 Statement.returnExpression (com.google.template.soy.jbcsrc.restricted.Statement.returnExpression)2 CompiledMethodBody (com.google.template.soy.jbcsrc.SoyNodeCompiler.CompiledMethodBody)1 ClassData (com.google.template.soy.jbcsrc.internal.ClassData)1 SoyClassWriter (com.google.template.soy.jbcsrc.internal.SoyClassWriter)1 AppendableAndOptions (com.google.template.soy.jbcsrc.restricted.SoyJbcSrcPrintDirective.Streamable.AppendableAndOptions)1 TypeInfo (com.google.template.soy.jbcsrc.restricted.TypeInfo)1 RangeArgs (com.google.template.soy.shared.RangeArgs)1 ForNonemptyNode (com.google.template.soy.soytree.ForNonemptyNode)1 TemplateNode (com.google.template.soy.soytree.TemplateNode)1 TemplateParam (com.google.template.soy.soytree.defn.TemplateParam)1