use of com.google.template.soy.shared.restricted.SoyPrintDirective in project closure-templates by google.
the class GenCallCodeUtils method gen.
/**
* Generates the JS expression for a given call.
*
* <p> Important: If there are CallParamContentNode children whose contents are not computable as
* JS expressions, then this function assumes that, elsewhere, code has been generated to define
* their respective 'param<n>' temporary variables.
*
* <p> Here are five example calls:
* <pre>
* {call some.func data="all" /}
* {call some.func data="$boo.foo" /}
* {call some.func}
* {param goo = $moo /}
* {/call}
* {call some.func data="$boo"}
* {param goo}Blah{/param}
* {/call}
* {call some.func}
* {param goo}
* {for $i in range(3)}{$i}{/for}
* {/param}
* {/call}
* </pre>
* Their respective generated calls might be the following:
* <pre>
* some.func(opt_data)
* some.func(opt_data.boo.foo)
* some.func({goo: opt_data.moo})
* some.func(soy.$$assignDefaults({goo: 'Blah'}, opt_data.boo))
* some.func({goo: param65})
* </pre>
* Note that in the last case, the param content is not computable as JS expressions, so we assume
* that code has been generated to define the temporary variable 'param<n>'.
*
* @param callNode The call to generate code for.
* @param templateAliases A mapping of fully qualified calls to a variable in scope.
* @return The JS expression for the call.
*/
public CodeChunk.WithValue gen(CallNode callNode, TemplateAliases templateAliases, TranslationContext translationContext, ErrorReporter errorReporter) {
// Build the JS CodeChunk for the callee's name.
CodeChunk.WithValue callee;
if (callNode instanceof CallBasicNode) {
// Case 1: Basic call.
// TODO(lukes): add the logic for the goog.require here. The simplest strategy requires a
// TemplateRegistry to detect external templates.
callee = CodeChunk.dottedIdNoRequire(templateAliases.get(((CallBasicNode) callNode).getCalleeName()));
} else {
// Case 2: Delegate call.
CallDelegateNode callDelegateNode = (CallDelegateNode) callNode;
CodeChunk.WithValue calleeId = JsRuntime.SOY_GET_DELTEMPLATE_ID.call(stringLiteral(delTemplateNamer.getDelegateName(callDelegateNode)));
ExprRootNode variantSoyExpr = callDelegateNode.getDelCalleeVariantExpr();
CodeChunk.WithValue variant;
if (variantSoyExpr == null) {
// Case 2a: Delegate call with empty variant.
variant = LITERAL_EMPTY_STRING;
} else {
// Case 2b: Delegate call with variant expression.
variant = new TranslateExprNodeVisitor(translationContext, errorReporter).exec(variantSoyExpr);
}
callee = SOY_GET_DELEGATE_FN.call(calleeId, variant, callDelegateNode.allowEmptyDefault() ? LITERAL_TRUE : LITERAL_FALSE);
}
// Generate the data object to pass to callee
CodeChunk.WithValue objToPass = genObjToPass(callNode, templateAliases, translationContext, errorReporter);
// Generate the main call expression.
CodeChunk.WithValue call = callee.call(objToPass, JsRuntime.OPT_IJ_DATA);
if (callNode.getEscapingDirectives().isEmpty()) {
return call;
}
// Apply escaping directives as necessary.
//
// The print directive system continues to use JsExpr, as it is a publicly available API and
// migrating it to CodeChunk would be a major change. Therefore, we convert our CodeChunks
// to JsExpr and back here.
JsExpr callResult = call.singleExprOrName();
RequiresCollector.IntoImmutableSet collector = new RequiresCollector.IntoImmutableSet();
call.collectRequires(collector);
for (SoyPrintDirective directive : callNode.getEscapingDirectives()) {
Preconditions.checkState(directive instanceof SoyJsSrcPrintDirective, "Contextual autoescaping produced a bogus directive: %s", directive.getName());
callResult = ((SoyJsSrcPrintDirective) directive).applyForJsSrc(callResult, ImmutableList.<JsExpr>of());
if (directive instanceof SoyLibraryAssistedJsSrcPrintDirective) {
for (String name : ((SoyLibraryAssistedJsSrcPrintDirective) directive).getRequiredJsLibNames()) {
collector.add(GoogRequire.create(name));
}
}
}
return fromExpr(callResult, collector.get()).withInitialStatements(call.initialStatements());
}
use of com.google.template.soy.shared.restricted.SoyPrintDirective in project closure-templates by google.
the class RemoveUnnecessaryEscapingDirectives method filterEscapingDirectives.
private static ImmutableList<SoyPrintDirective> filterEscapingDirectives(ImmutableList<SoyPrintDirective> escapingDirectives) {
for (int i = 0; i < escapingDirectives.size(); i++) {
SoyPrintDirective directive = escapingDirectives.get(i);
if (canSkip(directive)) {
ImmutableList.Builder<SoyPrintDirective> builder = ImmutableList.builder();
builder.addAll(escapingDirectives.subList(0, i));
for (; i < escapingDirectives.size(); i++) {
directive = escapingDirectives.get(i);
if (!canSkip(directive)) {
builder.add(directive);
}
}
return builder.build();
}
}
return escapingDirectives;
}
use of com.google.template.soy.shared.restricted.SoyPrintDirective in project closure-templates by google.
the class GenPyExprsVisitor method visitPrintNode.
/**
* Visiting a print node accomplishes 3 basic tasks. It loads data, it performs any operations
* needed, and it executes the appropriate print directives.
*
* <p>TODO(dcphillips): Add support for local variables once LetNode are supported.
*
* <p>Example:
*
* <pre>
* {$boo |changeNewlineToBr}
* {$goo + 5}
* </pre>
*
* might generate
*
* <pre>
* sanitize.change_newline_to_br(data.get('boo'))
* data.get('goo') + 5
* </pre>
*/
@Override
protected void visitPrintNode(PrintNode node) {
TranslateToPyExprVisitor translator = new TranslateToPyExprVisitor(localVarExprs, errorReporter);
PyExpr pyExpr = translator.exec(node.getExpr());
// Process directives.
for (PrintDirectiveNode directiveNode : node.getChildren()) {
// Get directive.
SoyPrintDirective directive = directiveNode.getPrintDirective();
if (!(directive instanceof SoyPySrcPrintDirective)) {
errorReporter.report(directiveNode.getSourceLocation(), UNKNOWN_SOY_PY_SRC_PRINT_DIRECTIVE, directiveNode.getName());
continue;
}
// Get directive args.
List<ExprRootNode> args = directiveNode.getArgs();
// Translate directive args.
List<PyExpr> argsPyExprs = new ArrayList<>(args.size());
for (ExprRootNode arg : args) {
argsPyExprs.add(translator.exec(arg));
}
// Apply directive.
pyExpr = ((SoyPySrcPrintDirective) directive).applyForPySrc(pyExpr, argsPyExprs);
}
pyExprs.add(pyExpr);
}
use of com.google.template.soy.shared.restricted.SoyPrintDirective in project closure-templates by google.
the class RenderVisitor method visitMsgFallbackGroupNode.
@Override
protected void visitMsgFallbackGroupNode(MsgFallbackGroupNode node) {
if (assistantForMsgs == null) {
assistantForMsgs = new RenderVisitorAssistantForMsgs(this, msgBundle);
}
if (!node.getEscapingDirectives().isEmpty()) {
// The entire message needs to be escaped, so we need to render to a temporary buffer.
// Fortunately, for most messages (in HTML context) this is unnecessary.
pushOutputBuf(new StringBuilder());
}
assistantForMsgs.visitForUseByMaster(node);
if (!node.getEscapingDirectives().isEmpty()) {
// Escape the entire message with the required directives.
SoyValue wholeMsg = StringData.forValue(popOutputBuf().toString());
for (SoyPrintDirective directive : node.getEscapingDirectives()) {
wholeMsg = applyDirective(directive, wholeMsg, ImmutableList.<SoyValue>of(), node);
}
append(currOutputBuf, wholeMsg.stringValue());
}
}
use of com.google.template.soy.shared.restricted.SoyPrintDirective in project closure-templates by google.
the class RenderVisitor method visitCallNodeHelper.
// for IntelliJ
@SuppressWarnings("ConstantConditions")
private void visitCallNodeHelper(CallNode node, TemplateNode callee) {
// ------ Build the call data. ------
SoyRecord dataToPass;
if (node.isPassingAllData()) {
dataToPass = data;
} else if (node.isPassingData()) {
SoyValue dataRefValue = eval(node.getDataExpr(), node);
if (!(dataRefValue instanceof SoyRecord)) {
throw RenderException.create("In 'call' command " + node.toSourceString() + ", the data reference does not resolve to a SoyRecord.").addStackTraceElement(node);
}
dataToPass = (SoyRecord) dataRefValue;
} else {
dataToPass = null;
}
SoyRecord callData;
int numChildren = node.numChildren();
if (numChildren == 0) {
// --- Cases 1 and 2: Not passing params. ---
if (dataToPass == null) {
// Case 1: Not passing data and not passing params.
callData = ParamStore.EMPTY_INSTANCE;
} else {
// Case 2: Passing data and not passing params.
callData = dataToPass;
}
} else {
// --- Cases 3 and 4: Passing params. ---
ParamStore mutableCallData;
if (dataToPass == null) {
// Case 3: Not passing data and passing params.
mutableCallData = new BasicParamStore(numChildren);
} else {
// Case 4: Passing data and passing params.
mutableCallData = new AugmentedParamStore(dataToPass, numChildren);
}
for (CallParamNode child : node.getChildren()) {
if (child instanceof CallParamValueNode) {
mutableCallData.setField(child.getKey().identifier(), lazyEval(((CallParamValueNode) child).getExpr(), child));
} else if (child instanceof CallParamContentNode) {
mutableCallData.setField(child.getKey().identifier(), renderRenderUnitNode((CallParamContentNode) child));
} else {
throw new AssertionError();
}
}
callData = mutableCallData;
}
if (node.getEscapingDirectives().isEmpty()) {
// No escaping at the call site -- render directly into the output buffer.
RenderVisitor rv = this.createHelperInstance(currOutputBuf, callData);
try {
rv.renderTemplate(callee, node.getParamsToRuntimeCheck(callee));
} catch (RenderException re) {
// this template call.
throw re.addStackTraceElement(node);
}
} else {
// Escaping the call site's result, such as at a strict template boundary.
// TODO: Some optimization is needed here before Strict Soy can be widely used:
// - Only create this temporary buffer when contexts mismatch. We could run a pre-pass that
// eliminates escaping directives when all callers are known.
// - Instead of creating a temporary buffer and copying, wrap with an escaping StringBuilder.
StringBuilder calleeBuilder = new StringBuilder();
RenderVisitor rv = this.createHelperInstance(calleeBuilder, callData);
try {
rv.renderTemplate(callee, node.getParamsToRuntimeCheck(callee));
} catch (RenderException re) {
// this template call.
throw re.addStackTraceElement(node);
}
ContentKind calleeKind = fromSanitizedContentKind(callee.getContentKind());
SoyValue resultData = calleeKind != null ? UnsafeSanitizedContentOrdainer.ordainAsSafe(calleeBuilder.toString(), calleeKind) : StringData.forValue(calleeBuilder.toString());
for (SoyPrintDirective directive : node.getEscapingDirectives()) {
resultData = applyDirective(directive, resultData, ImmutableList.<SoyValue>of(), node);
}
append(currOutputBuf, resultData, node);
}
}
Aggregations