Search in sources :

Example 1 with SoyRecord

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;
}
Also used : SoyRecord(com.google.template.soy.data.SoyRecord) SoyProtoValue(com.google.template.soy.data.SoyProtoValue) SoyValue(com.google.template.soy.data.SoyValue)

Example 2 with SoyRecord

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);
    }
}
Also used : SoyRecord(com.google.template.soy.data.SoyRecord) CallParamNode(com.google.template.soy.soytree.CallParamNode) CallParamValueNode(com.google.template.soy.soytree.CallParamValueNode) SoyValue(com.google.template.soy.data.SoyValue) BasicParamStore(com.google.template.soy.data.internal.BasicParamStore) AugmentedParamStore(com.google.template.soy.data.internal.AugmentedParamStore) CallParamContentNode(com.google.template.soy.soytree.CallParamContentNode) SoyPrintDirective(com.google.template.soy.shared.restricted.SoyPrintDirective) ParamStore(com.google.template.soy.data.internal.ParamStore) AugmentedParamStore(com.google.template.soy.data.internal.AugmentedParamStore) BasicParamStore(com.google.template.soy.data.internal.BasicParamStore) ContentKind(com.google.template.soy.data.SanitizedContent.ContentKind) SanitizedContentKind(com.google.template.soy.base.internal.SanitizedContentKind)

Example 3 with SoyRecord

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: ");
}
Also used : SoyRecord(com.google.template.soy.data.SoyRecord) AtomicReference(java.util.concurrent.atomic.AtomicReference) AbstractFuture(com.google.common.util.concurrent.AbstractFuture) Test(org.junit.Test)

Example 4 with SoyRecord

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");
}
Also used : SoyRecord(com.google.template.soy.data.SoyRecord) ParseResult(com.google.template.soy.SoyFileSetParser.ParseResult) Test(org.junit.Test)

Example 5 with SoyRecord

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>");
}
Also used : SoyRecord(com.google.template.soy.data.SoyRecord) AtomicReference(java.util.concurrent.atomic.AtomicReference) AbstractFuture(com.google.common.util.concurrent.AbstractFuture) Test(org.junit.Test)

Aggregations

SoyRecord (com.google.template.soy.data.SoyRecord)10 Test (org.junit.Test)8 BufferingAppendable (com.google.template.soy.data.LoggingAdvisingAppendable.BufferingAppendable)3 CompiledTemplate (com.google.template.soy.jbcsrc.shared.CompiledTemplate)3 CompiledTemplates (com.google.template.soy.jbcsrc.shared.CompiledTemplates)3 AbstractFuture (com.google.common.util.concurrent.AbstractFuture)2 ParseResult (com.google.template.soy.SoyFileSetParser.ParseResult)2 SoyValue (com.google.template.soy.data.SoyValue)2 RenderContext (com.google.template.soy.jbcsrc.shared.RenderContext)2 AtomicReference (java.util.concurrent.atomic.AtomicReference)2 SanitizedContentKind (com.google.template.soy.base.internal.SanitizedContentKind)1 ContentKind (com.google.template.soy.data.SanitizedContent.ContentKind)1 SoyDict (com.google.template.soy.data.SoyDict)1 SoyProtoValue (com.google.template.soy.data.SoyProtoValue)1 AugmentedParamStore (com.google.template.soy.data.internal.AugmentedParamStore)1 BasicParamStore (com.google.template.soy.data.internal.BasicParamStore)1 ParamStore (com.google.template.soy.data.internal.ParamStore)1 SoyPrintDirective (com.google.template.soy.shared.restricted.SoyPrintDirective)1 CallParamContentNode (com.google.template.soy.soytree.CallParamContentNode)1 CallParamNode (com.google.template.soy.soytree.CallParamNode)1