use of com.google.template.soy.soytree.CallParamValueNode in project closure-templates by google.
the class GenCallCodeUtils method genObjToPass.
/**
* Generates the JS expression for the object to pass in 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 objects to pass might be the following:
* <pre>
* opt_data
* opt_data.boo.foo
* {goo: opt_data.moo}
* soy.$$assignDefaults({goo: 'Blah'}, opt_data.boo)
* {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 object to pass in the call.
*/
private CodeChunk.WithValue genObjToPass(CallNode callNode, TemplateAliases templateAliases, TranslationContext translationContext, ErrorReporter errorReporter) {
// ------ Generate the expression for the original data to pass ------
CodeChunk.WithValue dataToPass;
if (callNode.isPassingAllData()) {
dataToPass = JsRuntime.OPT_DATA;
} else if (callNode.isPassingData()) {
dataToPass = new TranslateExprNodeVisitor(translationContext, errorReporter).exec(callNode.getDataExpr());
} else {
dataToPass = LITERAL_NULL;
}
// ------ Case 1: No additional params ------
if (callNode.numChildren() == 0) {
return dataToPass;
}
// ------ Build an object literal containing the additional params ------
ImmutableList.Builder<CodeChunk.WithValue> keys = ImmutableList.builder();
ImmutableList.Builder<CodeChunk.WithValue> values = ImmutableList.builder();
for (CallParamNode child : callNode.getChildren()) {
keys.add(id(child.getKey().identifier()));
if (child instanceof CallParamValueNode) {
CallParamValueNode cpvn = (CallParamValueNode) child;
CodeChunk.WithValue value = new TranslateExprNodeVisitor(translationContext, errorReporter).exec(cpvn.getExpr());
values.add(value);
} else {
CallParamContentNode cpcn = (CallParamContentNode) child;
CodeChunk.WithValue content;
if (isComputableAsJsExprsVisitor.exec(cpcn)) {
List<CodeChunk.WithValue> chunks = genJsExprsVisitorFactory.create(translationContext, templateAliases, errorReporter).exec(cpcn);
content = CodeChunkUtils.concatChunksForceString(chunks);
} else {
// This is a param with content that cannot be represented as JS expressions, so we assume
// that code has been generated to define the temporary variable 'param<n>'.
content = id("param" + cpcn.getId());
}
content = maybeWrapContent(translationContext.codeGenerator(), cpcn, content);
values.add(content);
}
}
CodeChunk.WithValue params = CodeChunk.mapLiteral(keys.build(), values.build());
// ------ Cases 2 and 3: Additional params with and without original data to pass ------
if (callNode.isPassingData()) {
CodeChunk.WithValue allData = SOY_ASSIGN_DEFAULTS.call(params, dataToPass);
return allData;
} else {
return params;
}
}
use of com.google.template.soy.soytree.CallParamValueNode 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);
}
}
use of com.google.template.soy.soytree.CallParamValueNode in project closure-templates by google.
the class GenPyCallExprVisitor method genObjToPass.
/**
* Generates the Python expression for the object to pass in a given call. This expression will be
* a combination of passed data and additional content params. If both are passed, they'll be
* combined into one dictionary.
*
* @param callNode The call to generate code for.
* @return The Python expression for the object to pass in the call.
*/
public String genObjToPass(CallNode callNode) {
TranslateToPyExprVisitor translator = new TranslateToPyExprVisitor(localVarStack, errorReporter);
// Generate the expression for the original data to pass.
String dataToPass;
if (callNode.isPassingAllData()) {
dataToPass = "data";
} else if (callNode.isPassingData()) {
dataToPass = translator.exec(callNode.getDataExpr()).getText();
} else {
dataToPass = "{}";
}
// Case 1: No additional params.
if (callNode.numChildren() == 0) {
return dataToPass;
}
// Build an object literal containing the additional params.
Map<PyExpr, PyExpr> additionalParams = new LinkedHashMap<>();
for (CallParamNode child : callNode.getChildren()) {
PyExpr key = new PyStringExpr("'" + child.getKey().identifier() + "'");
if (child instanceof CallParamValueNode) {
CallParamValueNode cpvn = (CallParamValueNode) child;
additionalParams.put(key, translator.exec(cpvn.getExpr()));
} else {
CallParamContentNode cpcn = (CallParamContentNode) child;
PyExpr valuePyExpr;
if (isComputableAsPyExprVisitor.exec(cpcn)) {
valuePyExpr = PyExprUtils.concatPyExprs(genPyExprsVisitorFactory.create(localVarStack, errorReporter).exec(cpcn));
} else {
// This is a param with content that cannot be represented as Python expressions, so we
// assume that code has been generated to define the temporary variable 'param<n>'.
String paramExpr = "param" + cpcn.getId();
// The param can be assumed to be a list at this point since it was created as an output
// variable.
valuePyExpr = new PyListExpr(paramExpr, Integer.MAX_VALUE);
}
// Param content nodes require a content kind in strict autoescaping, so the content must be
// wrapped as SanitizedContent.
valuePyExpr = InternalPyExprUtils.wrapAsSanitizedContent(cpcn.getContentKind(), valuePyExpr.toPyString());
additionalParams.put(key, valuePyExpr);
}
}
PyExpr additionalParamsExpr = PyExprUtils.convertMapToPyExpr(additionalParams);
// Cases 2 and 3: Additional params with and without original data to pass.
if (callNode.isPassingData()) {
// make a shallow copy so we don't accidentally modify the param
dataToPass = "dict(" + dataToPass + ")";
return "runtime.merge_into_dict(" + dataToPass + ", " + additionalParamsExpr.getText() + ")";
} else {
return additionalParamsExpr.getText();
}
}
use of com.google.template.soy.soytree.CallParamValueNode in project closure-templates by google.
the class TemplateParserTest method testParseBasicCallStmt.
@SuppressWarnings({ "ConstantConditions" })
@Test
public void testParseBasicCallStmt() throws Exception {
String templateBody = "{@param too : ?}{@param animals: ?}\n" + " {call .booTemplate_ /}\n" + " {call foo.goo.mooTemplate data=\"all\" /}\n" + " {call .booTemplate_ /}\n" + " {call .zooTemplate data=\"$animals\"}\n" + " {param yoo: round($too) /}\n" + " {param woo kind=\"html\"}poo{/param}\n" + " {param zoo: 0 /}\n" + " {param doo kind=\"html\"}doopoo{/param}\n" + " {/call}\n";
List<StandaloneNode> nodes = parseTemplateContent(templateBody, FAIL).getChildren();
assertThat(nodes).hasSize(4);
CallBasicNode cn0 = (CallBasicNode) nodes.get(0);
assertEquals("brittle.test.ns.booTemplate_", cn0.getCalleeName());
assertEquals(".booTemplate_", cn0.getSourceCalleeName());
assertEquals(false, cn0.isPassingData());
assertEquals(false, cn0.isPassingAllData());
assertEquals(null, cn0.getDataExpr());
assertEquals("XXX", cn0.genBasePhName());
assertEquals(0, cn0.numChildren());
CallBasicNode cn1 = (CallBasicNode) nodes.get(1);
assertEquals("foo.goo.mooTemplate", cn1.getCalleeName());
assertEquals("foo.goo.mooTemplate", cn1.getSourceCalleeName());
assertEquals(true, cn1.isPassingData());
assertEquals(true, cn1.isPassingAllData());
assertEquals(null, cn1.getDataExpr());
assertFalse(cn1.genSamenessKey().equals(cn0.genSamenessKey()));
assertEquals(0, cn1.numChildren());
CallBasicNode cn2 = (CallBasicNode) nodes.get(2);
assertEquals("brittle.test.ns.booTemplate_", cn2.getCalleeName());
assertEquals(".booTemplate_", cn2.getSourceCalleeName());
assertFalse(cn2.isPassingData());
assertEquals(false, cn2.isPassingAllData());
assertEquals(null, cn2.getDataExpr());
assertEquals("XXX", cn2.genBasePhName());
assertEquals(0, cn2.numChildren());
CallBasicNode cn3 = (CallBasicNode) nodes.get(3);
assertEquals("brittle.test.ns.zooTemplate", cn3.getCalleeName());
assertEquals(".zooTemplate", cn3.getSourceCalleeName());
assertEquals(true, cn3.isPassingData());
assertEquals(false, cn3.isPassingAllData());
assertTrue(cn3.getDataExpr().getRoot() != null);
assertEquals("$animals", cn3.getDataExpr().toSourceString());
assertEquals(4, cn3.numChildren());
{
final CallParamValueNode cn4cpvn0 = (CallParamValueNode) cn3.getChild(0);
assertEquals("yoo", cn4cpvn0.getKey().identifier());
assertEquals("round($too)", cn4cpvn0.getExpr().toSourceString());
assertTrue(cn4cpvn0.getExpr().getRoot() instanceof FunctionNode);
}
{
final CallParamContentNode cn4cpcn1 = (CallParamContentNode) cn3.getChild(1);
assertEquals("woo", cn4cpcn1.getKey().identifier());
assertEquals(SanitizedContentKind.HTML, cn4cpcn1.getContentKind());
assertEquals("poo", ((RawTextNode) cn4cpcn1.getChild(0)).getRawText());
}
{
final CallParamValueNode cn4cpvn2 = (CallParamValueNode) cn3.getChild(2);
assertEquals("zoo", cn4cpvn2.getKey().identifier());
assertEquals("0", cn4cpvn2.getExpr().toSourceString());
}
{
final CallParamContentNode cn4cpcn3 = (CallParamContentNode) cn3.getChild(3);
assertEquals("doo", cn4cpcn3.getKey().identifier());
assertNotNull(cn4cpcn3.getContentKind());
assertEquals(SanitizedContentKind.HTML, cn4cpcn3.getContentKind());
assertEquals("doopoo", ((RawTextNode) cn4cpcn3.getChild(0)).getRawText());
}
}
use of com.google.template.soy.soytree.CallParamValueNode in project closure-templates by google.
the class TemplateParserTest method testParseDelegateCallStmt.
@SuppressWarnings({ "ConstantConditions" })
@Test
public void testParseDelegateCallStmt() throws Exception {
String templateBody = "{@param animals : ?}{@param too : ?}\n" + " {delcall booTemplate /}\n" + " {delcall foo.goo.mooTemplate data=\"all\" /}\n" + " {delcall MySecretFeature.zooTemplate data=\"$animals\"}\n" + " {param yoo: round($too) /}\n" + " {param woo kind=\"html\"}poo{/param}\n" + " {/delcall}\n";
List<StandaloneNode> nodes = parseTemplateContent(templateBody, FAIL).getChildren();
assertEquals(3, nodes.size());
CallDelegateNode cn0 = (CallDelegateNode) nodes.get(0);
assertEquals("booTemplate", cn0.getDelCalleeName());
assertEquals(false, cn0.isPassingData());
assertEquals(false, cn0.isPassingAllData());
assertEquals(null, cn0.getDataExpr());
assertEquals("XXX", cn0.genBasePhName());
assertEquals(0, cn0.numChildren());
CallDelegateNode cn1 = (CallDelegateNode) nodes.get(1);
assertEquals("foo.goo.mooTemplate", cn1.getDelCalleeName());
assertEquals(true, cn1.isPassingData());
assertEquals(true, cn1.isPassingAllData());
assertEquals(null, cn1.getDataExpr());
assertFalse(cn1.genSamenessKey().equals(cn0.genSamenessKey()));
assertEquals(0, cn1.numChildren());
CallDelegateNode cn2 = (CallDelegateNode) nodes.get(2);
assertEquals("MySecretFeature.zooTemplate", cn2.getDelCalleeName());
assertEquals(true, cn2.isPassingData());
assertEquals(false, cn2.isPassingAllData());
assertTrue(cn2.getDataExpr().getRoot() != null);
assertEquals("$animals", cn2.getDataExpr().toSourceString());
assertEquals(2, cn2.numChildren());
CallParamValueNode cn2cpvn0 = (CallParamValueNode) cn2.getChild(0);
assertEquals("yoo", cn2cpvn0.getKey().identifier());
assertEquals("round($too)", cn2cpvn0.getExpr().toSourceString());
assertTrue(cn2cpvn0.getExpr().getRoot() instanceof FunctionNode);
CallParamContentNode cn2cpcn1 = (CallParamContentNode) cn2.getChild(1);
assertEquals("woo", cn2cpcn1.getKey().identifier());
assertEquals("poo", ((RawTextNode) cn2cpcn1.getChild(0)).getRawText());
}
Aggregations