use of com.google.template.soy.jbcsrc.restricted.Statement 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);
}
use of com.google.template.soy.jbcsrc.restricted.Statement 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);
}
use of com.google.template.soy.jbcsrc.restricted.Statement in project closure-templates by google.
the class SoyNodeCompiler method compile.
CompiledMethodBody compile(RenderUnitNode node) {
Statement templateBody = visitChildrenInNewScope(node);
// Tag the content with the kind
if (node.getContentKind() != null) {
templateBody = Statement.concat(appendableExpression.setSanitizedContentKind(node.getContentKind()).toStatement(), appendableExpression.setSanitizedContentDirectionality(ContentKind.valueOf(node.getContentKind().name()).getDefaultDir()).toStatement(), templateBody);
}
Statement jumpTable = detachState.generateReattachTable();
return CompiledMethodBody.create(Statement.concat(jumpTable, templateBody), detachState.getNumberOfDetaches());
}
use of com.google.template.soy.jbcsrc.restricted.Statement 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);
}
use of com.google.template.soy.jbcsrc.restricted.Statement in project closure-templates by google.
the class DetachState method createExpressionDetacher.
/**
* Returns a {@link ExpressionDetacher} that can be used to instrument an expression with detach
* reattach logic.
*/
@Override
public ExpressionDetacher createExpressionDetacher(Label reattachPoint) {
SaveRestoreState saveRestoreState = variables.saveRestoreState();
Statement restore = saveRestoreState.restore();
int state = addState(reattachPoint, restore);
Statement saveState = stateField.putInstanceField(thisExpr, BytecodeUtils.constant(state));
return new ExpressionDetacher.BasicDetacher(Statement.concat(saveRestoreState.save(), saveState));
}
Aggregations