use of com.google.template.soy.soytree.TemplateDelegateNode in project closure-templates by google.
the class RenderVisitor method visitCallDelegateNode.
@Override
protected void visitCallDelegateNode(CallDelegateNode node) {
ExprRootNode variantExpr = node.getDelCalleeVariantExpr();
String variant;
if (variantExpr == null) {
variant = "";
} else {
try {
SoyValue variantData = eval(variantExpr, node);
if (variantData instanceof IntegerData) {
// An integer constant is being used as variant. Use the value string representation as
// variant.
variant = String.valueOf(variantData.longValue());
} else {
// Variant is either a StringData or a SanitizedContent. Use the value as a string. If
// the value is not a string, and exception will be thrown.
variant = variantData.stringValue();
}
} catch (SoyDataException e) {
throw RenderException.createWithSource(String.format("Variant expression \"%s\" doesn't evaluate to a valid type " + "(Only string and integer are supported).", variantExpr.toSourceString()), e, node);
}
}
DelTemplateKey delegateKey = DelTemplateKey.create(node.getDelCalleeName(), variant);
TemplateDelegateNode callee;
try {
callee = templateRegistry.selectDelTemplate(delegateKey, activeDelPackageSelector);
} catch (IllegalArgumentException e) {
throw RenderException.createWithSource(e.getMessage(), e, node);
}
if (callee != null) {
visitCallNodeHelper(node, callee);
} else if (node.allowEmptyDefault()) {
// no active delegate implementation, so the call output is empty string
return;
} else {
throw RenderException.createWithSource("Found no active impl for delegate call to \"" + node.getDelCalleeName() + (variant.isEmpty() ? "" : ":" + variant) + "\" (and delcall does not set allowemptydefault=\"true\").", node);
}
}
use of com.google.template.soy.soytree.TemplateDelegateNode in project closure-templates by google.
the class GenJsCodeVisitor method visitTemplateNode.
/**
* Outputs a {@link TemplateNode}, generating the function open and close, along with a a debug
* template name.
*
* <p>If aliasing is not performed (which is always the case for V1 templates), this looks like:
*
* <pre>
* my.namespace.func = function(opt_data, opt_sb) {
* ...
* };
* if (goog.DEBUG) {
* my.namespace.func.soyTemplateName = 'my.namespace.func';
* }
* </pre>
*
* <p>If aliasing is performed, this looks like:
*
* <pre>
* function $func(opt_data, opt_sb) {
* ...
* }
* exports.func = $func;
* if (goog.DEBUG) {
* $func.soyTemplateName = 'my.namespace.func';
* }
* <p>Note that the alias is not exactly the function name as in may conflict with a reserved
* JavaScript identifier.
* </pre>
*/
@Override
protected void visitTemplateNode(TemplateNode node) {
// TODO(lukes): why don't we always do this? even for old style params this would be useful
boolean useStrongTyping = hasStrictParams(node);
String templateName = node.getTemplateName();
String partialName = node.getPartialTemplateName();
String alias;
boolean addToExports = jsSrcOptions.shouldGenerateGoogModules();
// TODO(lukes): does it make sense to add deltempaltes or private templates to exports?
if (addToExports && node instanceof TemplateDelegateNode) {
alias = node.getPartialTemplateName().substring(1);
} else {
alias = templateAliases.get(templateName);
}
// TODO(lukes): reserve all the namespace prefixes that are in scope
// TODO(lukes): use this for all local variable declarations
UniqueNameGenerator nameGenerator = JsSrcNameGenerators.forLocalVariables();
CodeChunk.Generator codeGenerator = CodeChunk.Generator.create(nameGenerator);
templateTranslationContext = TranslationContext.of(SoyToJsVariableMappings.forNewTemplate(), codeGenerator, nameGenerator);
genJsExprsVisitor = genJsExprsVisitorFactory.create(templateTranslationContext, templateAliases, errorReporter);
assistantForMsgs = null;
String paramsRecordType = null;
String jsDoc = null;
// ------ Generate JS Doc. ------
StringBuilder jsDocBuilder = new StringBuilder();
jsDocBuilder.append("/**\n");
jsDocBuilder.append(" * @param {");
if (useStrongTyping) {
paramsRecordType = genParamsRecordType(node);
jsDocBuilder.append(alias).append(".Params");
} else {
jsDocBuilder.append("Object<string, *>=");
}
jsDocBuilder.append("} opt_data\n");
jsDocBuilder.append(" * @param {Object<string, *>=} opt_ijData\n");
jsDocBuilder.append(" * @param {Object<string, *>=} opt_ijData_deprecated\n");
String returnType = getTemplateReturnType(node);
jsDocBuilder.append(" * @return {").append(returnType).append("}\n");
// Sometimes we will throw an error in the middle and the following code is not reachable.
jsDocBuilder.append(" * @suppress {").append("checkTypes|uselessCode").append("}\n");
if (node.getVisibility() == Visibility.PRIVATE) {
jsDocBuilder.append(" * @private\n");
}
jsDocBuilder.append(" */\n");
jsDoc = jsDocBuilder.toString();
ImmutableList.Builder<CodeChunk> bodyStatements = ImmutableList.builder();
bodyStatements.add(CodeChunk.assign("opt_ijData", CodeChunk.id("opt_ijData_deprecated").or(CodeChunk.id("opt_ijData"), codeGenerator)));
// Generate statement to ensure data is defined, if necessary.
if (new ShouldEnsureDataIsDefinedVisitor().exec(node)) {
bodyStatements.add(assign("opt_data", OPT_DATA.or(EMPTY_OBJECT_LITERAL, codeGenerator)));
}
// ------ Generate function body. ------
bodyStatements.add(generateFunctionBody(node));
CodeChunk.WithValue function = CodeChunk.function(// are too brittle.
ImmutableList.of("opt_data", "opt_ijData", "opt_ijData_deprecated"), CodeChunk.statements(bodyStatements.build()));
ImmutableList.Builder<CodeChunk> declarations = ImmutableList.builder();
if (addToExports) {
declarations.add(VariableDeclaration.builder(alias).setRhs(function).build());
declarations.add(assign("exports" + /* partialName starts with a dot */
partialName, id(alias)));
} else {
declarations.add(CodeChunk.assign(alias, function));
}
// ------ Add the @typedef of opt_data. ------
if (paramsRecordType != null) {
// TODO(b/35203585): find a way to represent jsdoc using code chunks
StringBuilder sb = new StringBuilder();
sb.append("/**\n");
sb.append(" * @typedef {").append(paramsRecordType).append("}\n");
sb.append(" */\n");
// TODO(b/35203585): find a way to represent declarations like this in codechunks
sb.append(alias).append(".Params");
declarations.add(CodeChunk.treatRawStringAsStatementLegacyOnly(sb.toString(), ImmutableList.<GoogRequire>of()));
}
// ------ Add the fully qualified template name to the function to use in debug code. ------
declarations.add(ifStatement(GOOG_DEBUG, assign(alias + ".soyTemplateName", stringLiteral(templateName))).build());
// ------ If delegate template, generate a statement to register it. ------
if (node instanceof TemplateDelegateNode) {
TemplateDelegateNode nodeAsDelTemplate = (TemplateDelegateNode) node;
declarations.add(SOY_REGISTER_DELEGATE_FN.call(SOY_GET_DELTEMPLATE_ID.call(stringLiteral(delTemplateNamer.getDelegateName(nodeAsDelTemplate))), stringLiteral(nodeAsDelTemplate.getDelTemplateVariant()), number(nodeAsDelTemplate.getDelPriority().getValue()), dottedIdNoRequire(alias)));
}
// TODO(b/35203585): find a way to represent jsdoc using code chunks
if (jsDoc != null) {
jsCodeBuilder.append(jsDoc);
}
jsCodeBuilder.append(CodeChunk.statements(declarations.build()));
}
use of com.google.template.soy.soytree.TemplateDelegateNode in project closure-templates by google.
the class CheckDelegatesVisitor method checkTemplates.
/**
* Performs checks that only involve templates (uses templateRegistry only).
*/
private void checkTemplates() {
DelTemplateSelector<TemplateDelegateNode> selector = templateRegistry.getDelTemplateSelector();
// content kind, and strict html mode.
for (Collection<TemplateDelegateNode> delTemplateGroup : selector.delTemplateNameToValues().asMap().values()) {
TemplateDelegateNode firstDelTemplate = null;
Set<Equivalence.Wrapper<TemplateParam>> firstRequiredParamSet = null;
SanitizedContentKind firstContentKind = null;
boolean firstStrictHtml = false;
// loop over all members of the deltemplate group.
for (TemplateDelegateNode delTemplate : delTemplateGroup) {
if (firstDelTemplate == null) {
// First template encountered.
firstDelTemplate = delTemplate;
firstRequiredParamSet = getRequiredParamSet(delTemplate);
firstContentKind = delTemplate.getContentKind();
firstStrictHtml = delTemplate.isStrictHtml() && firstContentKind == SanitizedContentKind.HTML;
} else {
// Not first template encountered.
Set<Equivalence.Wrapper<TemplateParam>> currRequiredParamSet = getRequiredParamSet(delTemplate);
if (!currRequiredParamSet.equals(firstRequiredParamSet)) {
errorReporter.report(delTemplate.getSourceLocation(), DELTEMPLATES_WITH_DIFFERENT_PARAM_DECLARATIONS, firstDelTemplate.getDelTemplateName(), firstDelTemplate.getSourceLocation().toString());
}
if (delTemplate.getContentKind() != firstContentKind) {
// TODO: This is only *truly* a requirement if the strict mode deltemplates are
// being called by contextual templates. For a strict-to-strict call, everything
// is escaped at runtime at the call sites. You could imagine delegating between
// either a plain-text or rich-html template. However, most developers will write
// their deltemplates in a parallel manner, and will want to know when the
// templates differ. Plus, requiring them all to be the same early-on will allow
// future optimizations to avoid the run-time checks, so it's better to start out
// as strict as possible and only open up if needed.
errorReporter.report(delTemplate.getSourceLocation(), STRICT_DELTEMPLATES_WITH_DIFFERENT_CONTENT_KIND, String.valueOf(firstContentKind), String.valueOf(delTemplate.getContentKind()), firstDelTemplate.getSourceLocation().toString());
}
// in this pass.
if (delTemplate.isStrictHtml() != firstStrictHtml) {
errorReporter.report(delTemplate.getSourceLocation(), DELTEMPLATES_WITH_DIFFERENT_STRICT_HTML_MODE, firstDelTemplate.getDelTemplateName(), firstDelTemplate.getSourceLocation().toString());
}
}
}
}
}
use of com.google.template.soy.soytree.TemplateDelegateNode in project closure-templates by google.
the class PassManager method doContextualEscaping.
/**
* Runs the autoescaper and returns whether or not new contextual templates have been added.
*/
private boolean doContextualEscaping(SoyFileSetNode soyTree) {
List<TemplateNode> extraTemplates = autoescaper.rewrite(soyTree, errorReporter);
// TODO: Run the redundant template remover here and rename after CL 16642341 is in.
if (!extraTemplates.isEmpty()) {
// TODO: pull out somewhere else. Ideally do the merge as part of the redundant template
// removal.
Map<String, SoyFileNode> containingFile = new HashMap<>();
for (SoyFileNode fileNode : soyTree.getChildren()) {
for (TemplateNode templateNode : fileNode.getChildren()) {
String name = templateNode instanceof TemplateDelegateNode ? ((TemplateDelegateNode) templateNode).getDelTemplateName() : templateNode.getTemplateName();
containingFile.put(DerivedTemplateUtils.getBaseName(name), fileNode);
}
}
for (TemplateNode extraTemplate : extraTemplates) {
String name = extraTemplate instanceof TemplateDelegateNode ? ((TemplateDelegateNode) extraTemplate).getDelTemplateName() : extraTemplate.getTemplateName();
containingFile.get(DerivedTemplateUtils.getBaseName(name)).addChild(extraTemplate);
}
return true;
}
return false;
}
use of com.google.template.soy.soytree.TemplateDelegateNode in project closure-templates by google.
the class FindIndirectParamsVisitor method visitCallDelegateNode.
@Override
protected void visitCallDelegateNode(CallDelegateNode node) {
// Don't forget to visit content within CallParamContentNodes.
visitChildren(node);
// We only want to recurse on calls that pass all data.
if (!node.isPassingAllData()) {
return;
}
// The current Soy file bundle may not contain all the delegate implementations that could
// potentially be used.
mayHaveIndirectParamsInExternalDelCalls = true;
// Visit all the possible callee templates.
ImmutableList<TemplateDelegateNode> potentialCallees = templateRegistry.getDelTemplateSelector().delTemplateNameToValues().get(node.getDelCalleeName());
for (TemplateDelegateNode delCallee : potentialCallees) {
visitCalleeHelper(node, delCallee);
}
}
Aggregations