use of com.google.template.soy.jbcsrc.restricted.Statement 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.Statement 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.Statement 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();
}
};
}
use of com.google.template.soy.jbcsrc.restricted.Statement 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);
}
use of com.google.template.soy.jbcsrc.restricted.Statement in project closure-templates by google.
the class MsgCompiler method compileMessage.
/**
* Compiles the given {@link MsgNode} to a statement with the given escaping directives applied.
*
* <p>The returned statement must be written to a location with a stack depth of zero, since
* placeholder formatting may require detach logic.
*
* @param partsAndId The computed msg id
* @param msg The msg node
* @param escapingDirectives The set of escaping directives to apply.
*/
Statement compileMessage(MsgPartsAndIds partsAndId, MsgNode msg, ImmutableList<SoyPrintDirective> escapingDirectives) {
Expression soyMsgDefaultParts = compileDefaultMessagePartsConstant(partsAndId);
Expression soyMsgParts = parameterLookup.getRenderContext().getSoyMsgParts(partsAndId.id, soyMsgDefaultParts);
Statement printMsg;
if (msg.isRawTextMsg()) {
// Simplest case, just a static string translation
printMsg = handleBasicTranslation(escapingDirectives, soyMsgParts);
} else {
// String translation + placeholders
printMsg = handleTranslationWithPlaceholders(msg, escapingDirectives, soyMsgParts, parameterLookup.getRenderContext().getULocale(), partsAndId.parts);
}
return Statement.concat(printMsg.withSourceLocation(msg.getSourceLocation()), detachState.detachLimited(appendableExpression));
}
Aggregations