use of com.google.template.soy.jbcsrc.restricted.Expression 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.Expression in project closure-templates by google.
the class SoyNodeCompiler method visitCallDelegateNode.
/**
* Given this delcall: {@code {delcall foo.bar variant="$expr" allowemptydefault="true"}}
*
* <p>Generate code that looks like:
*
* <pre>{@code
* renderContext.getDeltemplate("foo.bar", <variant-expression>, true)
* .create(<prepareParameters>, ijParams)
* .render(appendable, renderContext)
*
* }</pre>
*
* <p>We share logic with {@link #visitCallBasicNode(CallBasicNode)} around the actual calling
* convention (setting up detaches, storing the template in a field). As well as the logic for
* preparing the data record. The only interesting part of delcalls is calculating the {@code
* variant} and the fact that we have to invoke the {@link RenderContext} runtime to do the
* deltemplate lookup.
*/
@Override
protected Statement visitCallDelegateNode(CallDelegateNode node) {
Label reattachPoint = new Label();
Expression variantExpr;
if (node.getDelCalleeVariantExpr() == null) {
variantExpr = constant("");
} else {
variantExpr = exprCompiler.compile(node.getDelCalleeVariantExpr(), reattachPoint).coerceToString();
}
Expression calleeExpression = parameterLookup.getRenderContext().getDeltemplate(node.getDelCalleeName(), variantExpr, node.allowEmptyDefault(), prepareParamsHelper(node, reattachPoint), parameterLookup.getIjRecord());
return renderCallNode(reattachPoint, node, calleeExpression);
}
use of com.google.template.soy.jbcsrc.restricted.Expression 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);
}
};
}
use of com.google.template.soy.jbcsrc.restricted.Expression 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);
}
};
}
use of com.google.template.soy.jbcsrc.restricted.Expression in project closure-templates by google.
the class TemplateVariableManager method defineFields.
// TODO(lukes): consider moving all these optional 'one per template' fields to a different object
// for management.
/**
* Defines all the fields necessary for the registered variables.
*
* @return a statement to initialize the fields
*/
@CheckReturnValue
Statement defineFields(ClassVisitor writer) {
List<Statement> initializers = new ArrayList<>();
for (Variable var : allVariables) {
var.maybeDefineField(writer);
}
if (currentCalleeField != null) {
currentCalleeField.defineField(writer);
}
if (currentRendereeField != null) {
currentRendereeField.defineField(writer);
}
if (currentAppendable != null) {
currentAppendable.defineField(writer);
}
if (tempBufferField != null) {
tempBufferField.defineField(writer);
// If a template needs a temp buffer then we initialize it eagerly in the template constructor
// this may be wasteful in the case that the buffer is only used on certain call paths, but
// if it turns out to be expensive, this could always be solved by an author by refactoring
// their templates (e.g. extract the conditional logic into another template)
final Expression newStringBuilder = MethodRef.LOGGING_ADVISING_APPENDABLE_BUFFERING.invoke();
initializers.add(new Statement() {
@Override
protected void doGen(CodeBuilder adapter) {
adapter.loadThis();
newStringBuilder.gen(adapter);
tempBufferField.putUnchecked(adapter);
}
});
}
if (msgPlaceholderMapField != null) {
msgPlaceholderMapField.defineField(writer);
// same comment as above about eager initialization.
final Expression newHashMap = ConstructorRef.LINKED_HASH_MAP_SIZE.construct(constant(msgPlaceholderMapInitialSize));
initializers.add(new Statement() {
@Override
protected void doGen(CodeBuilder adapter) {
adapter.loadThis();
newHashMap.gen(adapter);
msgPlaceholderMapField.putUnchecked(adapter);
}
});
}
return Statement.concat(initializers);
}
Aggregations