use of com.google.template.soy.jbcsrc.restricted.CodeBuilder 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.CodeBuilder 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.CodeBuilder 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.restricted.CodeBuilder in project closure-templates by google.
the class TemplateCompiler method generateConstructor.
/**
* Generate a public constructor that assigns our final field and checks for missing required
* params.
*
* <p>This constructor is called by the generate factory classes.
*
* @param fieldInitializers additional statements to initialize fields (other than params)
*/
private void generateConstructor(Statement fieldInitializers) {
final Label start = new Label();
final Label end = new Label();
final LocalVariable thisVar = createThisVar(template.typeInfo(), start, end);
final LocalVariable paramsVar = createLocal("params", 1, SOY_RECORD_TYPE, start, end);
final LocalVariable ijVar = createLocal("ij", 2, SOY_RECORD_TYPE, start, end);
final List<Statement> assignments = new ArrayList<>();
// for other fields needed by the compiler.
assignments.add(fieldInitializers);
assignments.add(paramsField.putInstanceField(thisVar, paramsVar));
assignments.add(ijField.putInstanceField(thisVar, ijVar));
for (TemplateParam param : template.node().getAllParams()) {
Expression paramProvider = getParam(paramsVar, ijVar, param);
assignments.add(paramFields.get(param.name()).putInstanceField(thisVar, paramProvider));
}
Statement constructorBody = new Statement() {
@Override
protected void doGen(CodeBuilder ga) {
ga.mark(start);
// call super()
thisVar.gen(ga);
ga.invokeConstructor(OBJECT.type(), NULLARY_INIT);
for (Statement assignment : assignments) {
assignment.gen(ga);
}
ga.visitInsn(Opcodes.RETURN);
ga.visitLabel(end);
thisVar.tableEntry(ga);
paramsVar.tableEntry(ga);
ijVar.tableEntry(ga);
}
};
constructorBody.writeMethod(Opcodes.ACC_PUBLIC, template.constructor().method(), writer);
}
use of com.google.template.soy.jbcsrc.restricted.CodeBuilder in project closure-templates by google.
the class TemplateCompiler method generateRenderMethod.
private Statement generateRenderMethod() {
final Label start = new Label();
final Label end = new Label();
final LocalVariable thisVar = createThisVar(template.typeInfo(), start, end);
final LocalVariable appendableVar = createLocal("appendable", 1, LOGGING_ADVISING_APPENDABLE_TYPE, start, end).asNonNullable();
final LocalVariable contextVar = createLocal("context", 2, RENDER_CONTEXT_TYPE, start, end).asNonNullable();
final TemplateVariableManager variableSet = new TemplateVariableManager(fieldNames, template.typeInfo(), thisVar, template.renderMethod().method());
TemplateNode node = template.node();
TemplateVariables variables = new TemplateVariables(variableSet, thisVar, new RenderContextExpression(contextVar));
final CompiledMethodBody methodBody = SoyNodeCompiler.create(registry, innerClasses, stateField, thisVar, AppendableExpression.forLocal(appendableVar), variableSet, variables).compile(node);
final Statement returnDone = Statement.returnExpression(MethodRef.RENDER_RESULT_DONE.invoke());
new Statement() {
@Override
protected void doGen(CodeBuilder adapter) {
adapter.mark(start);
methodBody.body().gen(adapter);
adapter.mark(end);
returnDone.gen(adapter);
thisVar.tableEntry(adapter);
appendableVar.tableEntry(adapter);
contextVar.tableEntry(adapter);
variableSet.generateTableEntries(adapter);
}
}.writeIOExceptionMethod(Opcodes.ACC_PUBLIC, template.renderMethod().method(), writer);
writer.setNumDetachStates(methodBody.numberOfDetachStates());
variableSet.defineStaticFields(writer);
return variableSet.defineFields(writer);
}
Aggregations