use of com.google.template.soy.jbcsrc.TemplateVariableManager.Scope 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.TemplateVariableManager.Scope 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.TemplateVariableManager.Scope in project closure-templates by google.
the class MsgCompiler method handleTranslationWithPlaceholders.
/**
* Handles a complex message with placeholders.
*/
private Statement handleTranslationWithPlaceholders(MsgNode msg, ImmutableList<SoyPrintDirective> escapingDirectives, Expression soyMsgParts, Expression locale, ImmutableList<SoyMsgPart> parts) {
// We need to render placeholders into a buffer and then pack them into a map to pass to
// Runtime.renderSoyMsgWithPlaceholders.
Expression placeholderMap = variables.getMsgPlaceholderMapField().accessor(thisVar);
Map<String, Statement> placeholderNameToPutStatement = new LinkedHashMap<>();
putPlaceholdersIntoMap(placeholderMap, msg, parts, placeholderNameToPutStatement);
// sanity check
checkState(!placeholderNameToPutStatement.isEmpty());
variables.setMsgPlaceholderMapMinSize(placeholderNameToPutStatement.size());
Statement populateMap = Statement.concat(placeholderNameToPutStatement.values());
Statement clearMap = placeholderMap.invokeVoid(MethodRef.LINKED_HASH_MAP_CLEAR);
Statement render;
if (areAllPrintDirectivesStreamable(escapingDirectives)) {
// No need to save/restore since rendering a message doesn't detach. All detaching for data
// should have already happened as part of constructing the placholder map.
AppendableAndOptions wrappedAppendable = applyStreamingEscapingDirectives(escapingDirectives, appendableExpression, parameterLookup.getRenderContext(), variables);
Statement initAppendable = Statement.NULL_STATEMENT;
Statement clearAppendable = Statement.NULL_STATEMENT;
Expression appendableExpression = wrappedAppendable.appendable();
if (wrappedAppendable.closeable()) {
Scope scope = variables.enterScope();
Variable appendableVar = scope.createTemporary("msg_appendable", wrappedAppendable.appendable());
initAppendable = appendableVar.initializer();
appendableExpression = appendableVar.local();
clearAppendable = Statement.concat(appendableVar.local().checkedCast(BytecodeUtils.CLOSEABLE_TYPE).invokeVoid(MethodRef.CLOSEABLE_CLOSE), scope.exitScope());
}
render = Statement.concat(initAppendable, MethodRef.RUNTIME_RENDER_SOY_MSG_PARTS_WITH_PLACEHOLDERS.invokeVoid(soyMsgParts, locale, placeholderMap, appendableExpression), clearAppendable);
} else {
// render into the handy buffer we already have!
Statement renderToBuffer = MethodRef.RUNTIME_RENDER_SOY_MSG_PARTS_WITH_PLACEHOLDERS.invokeVoid(soyMsgParts, locale, placeholderMap, tempBuffer());
// N.B. the type here is always 'string'
SoyExpression value = SoyExpression.forString(tempBuffer().invoke(MethodRef.ADVISING_STRING_BUILDER_GET_AND_CLEAR));
for (SoyPrintDirective directive : escapingDirectives) {
value = parameterLookup.getRenderContext().applyPrintDirective(directive, value);
}
render = Statement.concat(renderToBuffer, appendableExpression.appendString(value.coerceToString()).toStatement());
}
return Statement.concat(populateMap, render, clearMap);
}
use of com.google.template.soy.jbcsrc.TemplateVariableManager.Scope in project closure-templates by google.
the class PrintDirectives method applyStreamingPrintDirectivesTo.
private static AppendableAndOptions applyStreamingPrintDirectivesTo(List<DirectiveWithArgs> directivesToApply, Expression appendable, JbcSrcPluginContext context, TemplateVariableManager variableManager) {
final List<LocalVariable> closeables = new ArrayList<>();
final List<Variable> appendableVars = new ArrayList<>();
Scope scope = variableManager.enterScope();
AppendableAndOptions prev = AppendableAndOptions.create(appendable);
Variable prevVar = scope.createTemporary("tmp_appendable", appendable);
appendableVars.add(prevVar);
// appendable with the last directive first. so iterate in reverse order.
for (DirectiveWithArgs directiveToApply : Lists.reverse(directivesToApply)) {
AppendableAndOptions curr = directiveToApply.apply(context, prevVar.local());
Variable currVar = scope.createTemporary("tmp_appendable", curr.appendable());
appendableVars.add(currVar);
if (curr.closeable()) {
closeables.add(currVar.local());
}
prev = curr;
prevVar = currVar;
}
// Check if we need to apply a wrapper to make sure close propagates to all the right places
// this is necessary if there are multiple closeable wrappers.
final Expression appendableExpression;
final boolean closeable;
if (closeables.isEmpty()) {
appendableExpression = prev.appendable();
closeable = false;
} else if (closeables.size() == 1 && prev.closeable()) {
// there is exactly one closeable and it is first, we don't need a wrapper
appendableExpression = prev.appendable();
closeable = true;
} else {
// there is either more than one closeable, or it is not the first one, so we need a wrapper
// We need to reverse the list of closeables so that we close them in the correct order. for
// example, given '|foo|bar' we will first wrap the delegate with bar and then with foo but we
// need to close foo first.
appendableExpression = RUNTIME_PROPAGATE_CLOSE.invoke(Iterables.getLast(appendableVars).local(), BytecodeUtils.asImmutableList(Lists.reverse(closeables)));
closeable = true;
}
final Statement exitScope = scope.exitScope();
Expression result = new Expression(appendableExpression.resultType()) {
@Override
protected void doGen(CodeBuilder adapter) {
for (Variable var : appendableVars) {
var.initializer().gen(adapter);
}
appendableExpression.gen(adapter);
exitScope.gen(adapter);
}
};
if (closeable) {
return AppendableAndOptions.createCloseable(result);
} else {
return AppendableAndOptions.create(result);
}
}
use of com.google.template.soy.jbcsrc.TemplateVariableManager.Scope in project closure-templates by google.
the class SoyNodeCompiler method visitChildrenInNewScope.
private Statement visitChildrenInNewScope(BlockNode node) {
Scope prev = currentScope;
currentScope = variables.enterScope();
List<Statement> children = visitChildren(node);
Statement leave = currentScope.exitScope();
children.add(leave);
currentScope = prev;
return Statement.concat(children);
}
Aggregations