use of com.google.template.soy.data.SoyRecord in project closure-templates by google.
the class EvalVisitor method visitNullSafeFieldAccessNode.
private SoyValue visitNullSafeFieldAccessNode(FieldAccessNode fieldAccess) {
SoyValue base = visitNullSafeNodeRecurse(fieldAccess.getBaseExprChild());
// attempting field access on non-SoyRecord
if (!(base instanceof SoyRecord) && !(base instanceof SoyProtoValue)) {
if (base == NullSafetySentinel.INSTANCE) {
// Bail out if base expression failed a null-safety check.
return NullSafetySentinel.INSTANCE;
}
if (fieldAccess.isNullSafe()) {
if (isNullOrUndefinedBase(base)) {
// Return the sentinel value that indicates that a null-safety check failed.
return NullSafetySentinel.INSTANCE;
} else {
throw RenderException.create(String.format("While evaluating \"%s\", encountered non-record just before accessing \"%s\".", fieldAccess.toSourceString(), fieldAccess.getSourceStringSuffix()));
}
}
// TODO: If feasible, find and fix existing instances, then throw RenderException here.
return UndefinedData.INSTANCE;
}
// the base type is possibly nullable, so remove null before testing for being a proto
if (SoyTypes.tryRemoveNull(fieldAccess.getBaseExprChild().getType()).getKind() == Kind.PROTO) {
return ((SoyProtoValue) base).getProtoField(fieldAccess.getFieldName());
}
maybeMarkBadProtoAccess(fieldAccess, base);
// base is a valid SoyRecord: get value
SoyValue value = ((SoyRecord) base).getField(fieldAccess.getFieldName());
// TODO(user): Consider cleaning up the null / NullData inconsistencies.
if (value != null && !TofuTypeChecks.isInstance(fieldAccess.getType(), value)) {
throw RenderException.create(String.format("Expected value of type '%s', but actual type was '%s'.", fieldAccess.getType(), value.getClass().getSimpleName()));
}
return (value != null) ? value : UndefinedData.INSTANCE;
}
use of com.google.template.soy.data.SoyRecord 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.data.SoyRecord in project closure-templates by google.
the class RenderVisitorTest method testDelayedCheckingOfCachingProviders.
@Test
public void testDelayedCheckingOfCachingProviders() {
String soyFileContent = "{namespace ns}\n" + "\n" + "{template .template}\n" + " {@param foo: int}\n" + " Before: {$foo}\n" + "{/template}\n";
final StringBuilder outputSb = new StringBuilder();
final AtomicReference<String> outputAtFutureGetTime = new AtomicReference<>();
AbstractFuture<Integer> fooFuture = new AbstractFuture<Integer>() {
{
set(1);
}
@Override
public Integer get() throws InterruptedException, ExecutionException {
outputAtFutureGetTime.set(outputSb.toString());
return super.get();
}
};
SoyRecord data = SoyValueConverterUtility.newDict("foo", fooFuture);
assertThat(renderTemplateInFile(SoyFileSetParserBuilder.forFileContents(soyFileContent).parse(), "ns.template", data, TEST_IJ_DATA, Predicates.<String>alwaysFalse(), outputSb)).isEqualTo("Before: 1");
assertThat(outputAtFutureGetTime.get()).isEqualTo("Before: ");
}
use of com.google.template.soy.data.SoyRecord in project closure-templates by google.
the class RenderVisitorTest method testRenderDelegateCallWithoutDefault.
@Test
public void testRenderDelegateCallWithoutDefault() throws Exception {
String soyFileContent1a = "{namespace ns1}\n" + "\n" + "/***/\n" + "{template .callerTemplate}\n" + " {delcall myApp.myDelegate allowemptydefault=\"true\"}\n" + " {param boo: 'aaaaaah' /}\n" + " {/delcall}\n" + "{/template}\n";
String soyFileContent1b = "{namespace ns1}\n" + "\n" + "/***/\n" + "{template .callerTemplate}\n" + " {delcall myApp.myDelegate}\n" + " {param boo: 'aaaaaah' /}\n" + " {/delcall}\n" + "{/template}\n";
String soyFileContent2 = "{delpackage SecretFeature}\n" + "{namespace ns2}\n" + "\n" + "/** @param boo */\n" + "{deltemplate myApp.myDelegate}\n" + // implementation in SecretFeature
" 111 {$boo}\n" + "{/deltemplate}\n";
SoyRecord data = SoyValueConverterUtility.newDict();
ParseResult parseResult;
// ------ Test with only file 1a in bundle. ------
parseResult = SoyFileSetParserBuilder.forFileContents(soyFileContent1a).parse();
Predicate<String> activeDelPackageNames = Predicates.alwaysFalse();
assertThat(renderTemplateInFile(parseResult, "ns1.callerTemplate", data, null, activeDelPackageNames)).isEmpty();
activeDelPackageNames = Predicates.equalTo("SecretFeature");
assertThat(renderTemplateInFile(parseResult, "ns1.callerTemplate", data, null, activeDelPackageNames)).isEmpty();
// ------ Test with both files 1a and 2 in bundle. ------
parseResult = SoyFileSetParserBuilder.forFileContents(soyFileContent1a, soyFileContent2).parse();
activeDelPackageNames = Predicates.alwaysFalse();
assertThat(renderTemplateInFile(parseResult, "ns1.callerTemplate", data, null, activeDelPackageNames)).isEmpty();
activeDelPackageNames = Predicates.equalTo("SecretFeature");
assertThat(renderTemplateInFile(parseResult, "ns1.callerTemplate", data, null, activeDelPackageNames)).isEqualTo("111 aaaaaah");
activeDelPackageNames = Predicates.equalTo("NonexistentFeature");
assertThat(renderTemplateInFile(parseResult, "ns1.callerTemplate", data, null, activeDelPackageNames)).isEmpty();
activeDelPackageNames = Predicates.in(ImmutableSet.of("NonexistentFeature", "SecretFeature"));
assertThat(renderTemplateInFile(parseResult, "ns1.callerTemplate", data, null, activeDelPackageNames)).isEqualTo("111 aaaaaah");
// ------ Test with only file 1b in bundle. ------
activeDelPackageNames = Predicates.alwaysFalse();
try {
renderTemplateInFile(SoyFileSetParserBuilder.forFileContents(soyFileContent1b).parse(), "ns1.callerTemplate", data, null, activeDelPackageNames);
fail("expected RenderException");
} catch (RenderException e) {
assertThat(e).hasMessageThat().contains("Found no active impl for delegate call");
}
try {
renderTemplateInFile(SoyFileSetParserBuilder.forFileContents(soyFileContent1b, soyFileContent2).parse(), "ns1.callerTemplate", data, null, activeDelPackageNames);
fail("expected RenderException");
} catch (RenderException e) {
assertThat(e).hasMessageThat().contains("Found no active impl for delegate call");
}
activeDelPackageNames = Predicates.equalTo("SecretFeature");
assertThat(renderTemplateInFile(SoyFileSetParserBuilder.forFileContents(soyFileContent1b, soyFileContent2).parse(), "ns1.callerTemplate", data, null, activeDelPackageNames)).isEqualTo("111 aaaaaah");
}
use of com.google.template.soy.data.SoyRecord in project closure-templates by google.
the class RenderVisitorTest method testStreamLazyParamsToOutputStreamDirectly.
@Test
public void testStreamLazyParamsToOutputStreamDirectly() {
String soyFileContent = Joiner.on("\n").join("{namespace ns}", "", "{template .callee}", " {@param body: html}", " <div>", " {$body}", " </div>", "{/template}", "", "{template .caller}", " {@param future: string}", " {call .callee}", " {param body kind=\"html\"}", " static-content{sp}", " {$future}", " {/param}", " {/call}", "{/template}");
final StringBuilder outputSb = new StringBuilder();
final AtomicReference<String> outputAtFutureGetTime = new AtomicReference<>();
AbstractFuture<String> future = new AbstractFuture<String>() {
{
set("future-content");
}
@Override
public String get() throws InterruptedException, ExecutionException {
outputAtFutureGetTime.set(outputSb.toString());
return super.get();
}
};
SoyRecord data = SoyValueConverterUtility.newDict("future", future);
assertThat(renderTemplateInFile(SoyFileSetParserBuilder.forFileContents(soyFileContent).parse(), "ns.caller", data, TEST_IJ_DATA, Predicates.<String>alwaysFalse(), outputSb)).isEqualTo("<div>static-content future-content</div>");
// we only get the <div>. we used to get the 'static-content' as well but that was only
// because we aren't using the autoescaper.
assertThat(outputAtFutureGetTime.get()).isEqualTo("<div>");
}
Aggregations