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;
}
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);
}
}
};
}
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);
}
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);
}
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);
}
Aggregations