use of com.google.template.soy.soytree.CallParamNode 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.CallParamNode in project closure-templates by google.
the class GenIncrementalDomCodeVisitor method visitCallNode.
@Override
protected void visitCallNode(CallNode node) {
// expressions, visit them to generate code to define their respective 'param<n>' variables.
for (CallParamNode child : node.getChildren()) {
if (child instanceof CallParamContentNode && !isComputableAsJsExprsVisitor.exec(child)) {
visit(child);
}
}
CodeChunk.WithValue call = genCallCodeUtils.gen(node, templateAliases, templateTranslationContext, errorReporter);
switch(getJsCodeBuilder().getContentKind()) {
case ATTRIBUTES:
getJsCodeBuilder().append(call);
break;
case HTML:
Optional<SanitizedContentKind> kind = templateRegistry.getCallContentKind(node);
// such as dynamic recompilation.
if (!kind.isPresent()) {
call = SOY_IDOM_RENDER_DYNAMIC_CONTENT.call(call);
} else if (isTextContent(kind.get())) {
call = generateTextCall(call);
}
getJsCodeBuilder().append(call);
break;
case JS:
case URI:
case TRUSTED_RESOURCE_URI:
case CSS:
case TEXT:
// If the current content kind (due to a let, param or template) is a text-like, simply
// concatentate the result of the call to the current output variable.
getJsCodeBuilder().addChunkToOutputVar(call);
break;
}
}
use of com.google.template.soy.soytree.CallParamNode in project closure-templates by google.
the class FindIndirectParamsVisitor method visitCalleeHelper.
private void visitCalleeHelper(CallNode caller, TemplateNode callee) {
// We must not revisit the current template or any templates already in the caller stack.
if (callee == currTemplate || callerStack.peek().allCallers.contains(callee)) {
return;
}
// Get the set of params that are passed explicitly in this call, but not already passed
// explicitly in a previous call in the current call path. And then create the new set of
// allCallParamKeys (reusing the old set if there are no additional call param keys).
Set<String> prevAllCallParamKeys = callerStack.peek().allCallParamKeys;
Set<String> additionalCallParamKeys = Sets.newHashSet();
for (CallParamNode callParamNode : caller.getChildren()) {
String callParamKey = callParamNode.getKey().identifier();
if (!prevAllCallParamKeys.contains(callParamKey)) {
additionalCallParamKeys.add(callParamKey);
}
}
Set<String> newAllCallParamKeys;
if (!additionalCallParamKeys.isEmpty()) {
newAllCallParamKeys = Sets.newHashSet(prevAllCallParamKeys);
newAllCallParamKeys.addAll(additionalCallParamKeys);
} else {
newAllCallParamKeys = prevAllCallParamKeys;
}
// Ensure we don't visit the same call situation more than once.
CallSituation currCallSituation = new CallSituation(callee, newAllCallParamKeys);
if (visitedCallSituations.contains(currCallSituation)) {
return;
}
visitedCallSituations.add(currCallSituation);
// Add caller frame.
if (currNewAllCallers == null) {
currNewAllCallers = Sets.newHashSet(callerStack.peek().allCallers);
currNewAllCallers.add(currTemplate);
}
CallerFrame callerFrame = new CallerFrame(currTemplate, currNewAllCallers, newAllCallParamKeys);
callerStack.push(callerFrame);
// Visit the callee.
visit(callee);
// Remove caller frame and restore previous values of currTemplate and currNewAllCallers.
CallerFrame poppedCallerFrame = callerStack.pop();
if (poppedCallerFrame != callerFrame) {
throw new AssertionError();
}
currTemplate = callerFrame.caller;
currNewAllCallers = callerFrame.allCallers;
}
use of com.google.template.soy.soytree.CallParamNode 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.CallParamNode in project closure-templates by google.
the class GenJsCodeVisitor method visitCallNode.
/**
* Example:
*
* <pre>
* {call some.func data="all" /}
* {call some.func data="$boo.foo" /}
* {call some.func}
* {param goo: 88 /}
* {/call}
* {call some.func data="$boo"}
* {param goo}
* Hello {$name}
* {/param}
* {/call}
* </pre>
*
* might generate
*
* <pre>
* output += some.func(opt_data);
* output += some.func(opt_data.boo.foo);
* output += some.func({goo: 88});
* output += some.func(soy.$$assignDefaults({goo: 'Hello ' + opt_data.name}, opt_data.boo);
* </pre>
*/
@Override
protected void visitCallNode(CallNode node) {
// expressions, visit them to generate code to define their respective 'param<n>' variables.
for (CallParamNode child : node.getChildren()) {
if (child instanceof CallParamContentNode && !isComputableAsJsExprsVisitor.exec(child)) {
visit(child);
}
}
// Add the call's result to the current output var.
CodeChunk.WithValue call = genCallCodeUtils.gen(node, templateAliases, templateTranslationContext, errorReporter);
jsCodeBuilder.addChunkToOutputVar(call);
}
Aggregations