public void WriteSection(CodeRenderingContext context, SectionIntermediateNode node)
        {
            context.CodeWriter
            .WriteStartMethodInvocation(SectionMethodName)
            .Write("\"")
            .Write(node.SectionName)
            .Write("\", ");

            if (context.Options.DesignTime)
            {
                using (context.CodeWriter.BuildAsyncLambda(DefaultWriterName))
                {
                    context.RenderChildren(node);
                }
            }
            else
            {
                using (context.CodeWriter.BuildAsyncLambda())
                {
                    context.RenderChildren(node);
                }
            }

            context.CodeWriter.WriteEndMethodInvocation(endLine: true);
        }
        public void WriteTagHelperBody(CodeRenderingContext context, DefaultTagHelperBodyIntermediateNode node)
        {
            if (context.Parent as TagHelperIntermediateNode == null)
            {
                var message = Resources.FormatIntermediateNodes_InvalidParentNode(node.GetType(), typeof(TagHelperIntermediateNode));
                throw new InvalidOperationException(message);
            }

            if (context.Options.DesignTime)
            {
                context.RenderChildren(node);
            }
            else
            {
                // Call into the tag helper scope manager to start a new tag helper scope.
                // Also capture the value as the current execution context.
                context.CodeWriter
                .WriteStartAssignment(ExecutionContextVariableName)
                .WriteStartInstanceMethodInvocation(
                    ScopeManagerVariableName,
                    ScopeManagerBeginMethodName);

                // Assign a unique ID for this instance of the source HTML tag. This must be unique
                // per call site, e.g. if the tag is on the view twice, there should be two IDs.
                var uniqueId = (string)context.Items[CodeRenderingContext.SuppressUniqueIds];
                if (uniqueId == null)
                {
                    uniqueId = GetDeterministicId(context);
                }

                context.CodeWriter.WriteStringLiteral(node.TagName)
                .WriteParameterSeparator()
                .Write(TagModeTypeName)
                .Write(".")
                .Write(node.TagMode.ToString())
                .WriteParameterSeparator()
                .WriteStringLiteral(uniqueId)
                .WriteParameterSeparator();

                using (context.CodeWriter.BuildAsyncLambda())
                {
                    // We remove and redirect writers so TagHelper authors can retrieve content.
                    context.RenderChildren(node, new RuntimeNodeWriter());
                }

                context.CodeWriter.WriteEndMethodInvocation();
            }
        }
        public void WriteSection(CodeRenderingContext context, SectionIntermediateNode node)
        {
            // Quirk Alert!
            //
            // In 1.0.0 Razor/MVC the define section method took a parameter for a TextWriter
            // that would be used for all of the output in the section. We simplified this API for
            // 2.0.0 of MVC, but our design time codegen still needs to target 1.0.0.
            //
            // So the workaround is MVC 2.0.0 will define these methods with the TextWriter, but
            // that method is never called. We still generate the call *with* the TextWriter for
            // design time, at least until we have multi-targeting.
            var writerName = context.Options.DesignTime ? DefaultWriterName : string.Empty;

            context.CodeWriter
            .WriteStartMethodInvocation(SectionMethodName)
            .Write("\"")
            .Write(node.SectionName)
            .Write("\", ");

            using (context.CodeWriter.BuildAsyncLambda(writerName))
            {
                context.RenderChildren(node);
            }

            context.CodeWriter.WriteEndMethodInvocation(endLine: true);
        }
Beispiel #4
0
        public override void WriteMarkupElement(CodeRenderingContext context, MarkupElementIntermediateNode node)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (node == null)
            {
                throw new ArgumentNullException(nameof(node));
            }

            context.RenderChildren(node);
        }
Beispiel #5
0
        public void WriteTemplate(CodeRenderingContext context, TemplateIntermediateNode node)
        {
            const string ItemParameterName  = "item";
            const string TemplateWriterName = "__razor_template_writer";

            context.CodeWriter
            .Write(ItemParameterName).Write(" => ")
            .WriteStartNewObject(TemplateTypeName);

            using (context.CodeWriter.BuildAsyncLambda(TemplateWriterName))
            {
                context.NodeWriter.BeginWriterScope(context, TemplateWriterName);

                context.RenderChildren(node);

                context.NodeWriter.EndWriterScope(context);
            }

            context.CodeWriter.WriteEndMethodInvocation(endLine: false);
        }
        public void WriteTagHelperProperty(CodeRenderingContext context, DefaultTagHelperPropertyIntermediateNode node)
        {
            var tagHelperNode = context.Parent as TagHelperIntermediateNode;

            if (context.Parent == null)
            {
                var message = Resources.FormatIntermediateNodes_InvalidParentNode(node.GetType(), typeof(TagHelperIntermediateNode));
                throw new InvalidOperationException(message);
            }

            if (!context.Options.DesignTime)
            {
                // Ensure that the property we're trying to set has initialized its dictionary bound properties.
                if (node.IsIndexerNameMatch &&
                    object.ReferenceEquals(FindFirstUseOfIndexer(tagHelperNode, node), node))
                {
                    // Throw a reasonable Exception at runtime if the dictionary property is null.
                    context.CodeWriter
                    .Write("if (")
                    .Write(node.FieldName)
                    .Write(".")
                    .Write(node.PropertyName)
                    .WriteLine(" == null)");
                    using (context.CodeWriter.BuildScope())
                    {
                        // System is in Host.NamespaceImports for all MVC scenarios. No need to generate FullName
                        // of InvalidOperationException type.
                        context.CodeWriter
                        .Write("throw ")
                        .WriteStartNewObject(nameof(InvalidOperationException))
                        .WriteStartMethodInvocation(FormatInvalidIndexerAssignmentMethodName)
                        .WriteStringLiteral(node.AttributeName)
                        .WriteParameterSeparator()
                        .WriteStringLiteral(node.TagHelper.GetTypeName())
                        .WriteParameterSeparator()
                        .WriteStringLiteral(node.PropertyName)
                        .WriteEndMethodInvocation(endLine: false) // End of method call
                        .WriteEndMethodInvocation();              // End of new expression / throw statement
                    }
                }
            }

            // If this is not the first use of the attribute value, we need to evaluate the expression and assign it to
            // the tag helper property.
            //
            // Otherwise, the value has already been computed and assigned to another tag helper. We just need to
            // copy from that tag helper to this one.
            //
            // This is important because we can't evaluate the expression twice due to side-effects.
            var firstUseOfAttribute = FindFirstUseOfAttribute(tagHelperNode, node);

            if (!object.ReferenceEquals(firstUseOfAttribute, node))
            {
                // If we get here, this value has already been used. We just need to copy the value.
                context.CodeWriter
                .WriteStartAssignment(GetPropertyAccessor(node))
                .Write(GetPropertyAccessor(firstUseOfAttribute))
                .WriteLine(";");

                return;
            }

            // If we get there, this is the first time seeing this property so we need to evaluate the expression.
            if (node.BoundAttribute.ExpectsStringValue(node.AttributeName))
            {
                if (context.Options.DesignTime)
                {
                    context.RenderChildren(node);

                    context.CodeWriter.WriteStartAssignment(GetPropertyAccessor(node));
                    if (node.Children.Count == 1 && node.Children.First() is HtmlContentIntermediateNode htmlNode)
                    {
                        var content = GetContent(htmlNode);
                        context.CodeWriter.WriteStringLiteral(content);
                    }
                    else
                    {
                        context.CodeWriter.Write("string.Empty");
                    }
                    context.CodeWriter.WriteLine(";");
                }
                else
                {
                    context.CodeWriter.WriteMethodInvocation(BeginWriteTagHelperAttributeMethodName);

                    context.RenderChildren(node, new LiteralRuntimeNodeWriter());

                    context.CodeWriter
                    .WriteStartAssignment(StringValueBufferVariableName)
                    .WriteMethodInvocation(EndWriteTagHelperAttributeMethodName)
                    .WriteStartAssignment(GetPropertyAccessor(node))
                    .Write(StringValueBufferVariableName)
                    .WriteLine(";");
                }
            }
            else
            {
                if (context.Options.DesignTime)
                {
                    var firstMappedChild = node.Children.FirstOrDefault(child => child.Source != null) as IntermediateNode;
                    var valueStart       = firstMappedChild?.Source;

                    using (context.CodeWriter.BuildLinePragma(node.Source, context))
                    {
                        var accessor = GetPropertyAccessor(node);
                        var assignmentPrefixLength = accessor.Length + " = ".Length;
                        if (node.BoundAttribute.IsEnum &&
                            node.Children.Count == 1 &&
                            node.Children.First() is IntermediateToken token &&
                            token.IsCSharp)
                        {
                            assignmentPrefixLength += $"global::{node.BoundAttribute.TypeName}.".Length;

                            if (valueStart != null)
                            {
                                context.CodeWriter.WritePadding(assignmentPrefixLength, node.Source, context);
                            }

                            context.CodeWriter
                            .WriteStartAssignment(accessor)
                            .Write("global::")
                            .Write(node.BoundAttribute.TypeName)
                            .Write(".");
                        }
                        else
                        {
                            if (valueStart != null)
                            {
                                context.CodeWriter.WritePadding(assignmentPrefixLength, node.Source, context);
                            }

                            context.CodeWriter.WriteStartAssignment(GetPropertyAccessor(node));
                        }

                        if (node.Children.Count == 0 &&
                            node.AttributeStructure == AttributeStructure.Minimized &&
                            node.BoundAttribute.ExpectsBooleanValue(node.AttributeName))
                        {
                            // If this is a minimized boolean attribute, set the value to true.
                            context.CodeWriter.Write("true");
                        }
                        else
                        {
                            RenderTagHelperAttributeInline(context, node, node.Source);
                        }

                        context.CodeWriter.WriteLine(";");
                    }
                }
        public void WriteTagHelperHtmlAttribute(CodeRenderingContext context, DefaultTagHelperHtmlAttributeIntermediateNode node)
        {
            if (context.Parent as TagHelperIntermediateNode == null)
            {
                var message = Resources.FormatIntermediateNodes_InvalidParentNode(node.GetType(), typeof(TagHelperIntermediateNode));
                throw new InvalidOperationException(message);
            }

            if (context.Options.DesignTime)
            {
                context.RenderChildren(node);
            }
            else
            {
                var attributeValueStyleParameter = $"{HtmlAttributeValueStyleTypeName}.{node.AttributeStructure}";
                var isConditionalAttributeValue  = node.Children.Any(
                    child => child is CSharpExpressionAttributeValueIntermediateNode || child is CSharpCodeAttributeValueIntermediateNode);

                // All simple text and minimized attributes will be pre-allocated.
                if (isConditionalAttributeValue)
                {
                    // Dynamic attribute value should be run through the conditional attribute removal system. It's
                    // unbound and contains C#.

                    // TagHelper attribute rendering is buffered by default. We do not want to write to the current
                    // writer.
                    var valuePieceCount = node.Children.Count(
                        child =>
                        child is HtmlAttributeValueIntermediateNode ||
                        child is CSharpExpressionAttributeValueIntermediateNode ||
                        child is CSharpCodeAttributeValueIntermediateNode ||
                        child is ExtensionIntermediateNode);

                    context.CodeWriter
                    .WriteStartMethodInvocation(BeginAddHtmlAttributeValuesMethodName)
                    .Write(ExecutionContextVariableName)
                    .WriteParameterSeparator()
                    .WriteStringLiteral(node.AttributeName)
                    .WriteParameterSeparator()
                    .Write(valuePieceCount.ToString(CultureInfo.InvariantCulture))
                    .WriteParameterSeparator()
                    .Write(attributeValueStyleParameter)
                    .WriteEndMethodInvocation();

                    context.RenderChildren(node, new TagHelperHtmlAttributeRuntimeNodeWriter());

                    context.CodeWriter
                    .WriteMethodInvocation(
                        EndAddHtmlAttributeValuesMethodName,
                        ExecutionContextVariableName);
                }
                else
                {
                    // This is a data-* attribute which includes C#. Do not perform the conditional attribute removal or
                    // other special cases used when IsDynamicAttributeValue(). But the attribute must still be buffered to
                    // determine its final value.

                    // Attribute value is not plain text, must be buffered to determine its final value.
                    context.CodeWriter.WriteMethodInvocation(BeginWriteTagHelperAttributeMethodName);

                    // We're building a writing scope around the provided chunks which captures everything written from the
                    // page. Therefore, we do not want to write to any other buffer since we're using the pages buffer to
                    // ensure we capture all content that's written, directly or indirectly.
                    context.RenderChildren(node, new RuntimeNodeWriter());

                    context.CodeWriter
                    .WriteStartAssignment(StringValueBufferVariableName)
                    .WriteMethodInvocation(EndWriteTagHelperAttributeMethodName)
                    .WriteStartInstanceMethodInvocation(
                        ExecutionContextVariableName,
                        ExecutionContextAddHtmlAttributeMethodName)
                    .WriteStringLiteral(node.AttributeName)
                    .WriteParameterSeparator()
                    .WriteStartMethodInvocation(MarkAsHtmlEncodedMethodName)
                    .Write(StringValueBufferVariableName)
                    .WriteEndMethodInvocation(endLine: false)
                    .WriteParameterSeparator()
                    .Write(attributeValueStyleParameter)
                    .WriteEndMethodInvocation();
                }
            }
        }