protected override void Visit(TagHelperChunk chunk)
        {
            // We only want to setup tag helper manager fields if there are tag helpers, and only once
            if (!_foundTagHelpers)
            {
                _foundTagHelpers = true;

                // We want to hide declared TagHelper fields so they cannot be stepped over via a debugger.
                Writer.WriteLineHiddenDirective();

                // Runtime fields aren't useful during design time.
                if (!Context.Host.DesignTimeMode)
                {
                    // Need to disable the warning "X is assigned to but never used." for the value buffer since
                    // whether it's used depends on how a TagHelper is used.
                    Writer.WritePragma("warning disable 0414");
                    WritePrivateField(
                        _tagHelperContext.TagHelperContentTypeName,
                        CSharpTagHelperCodeRenderer.StringValueBufferVariableName,
                        value: null);
                    Writer.WritePragma("warning restore 0414");

                    WritePrivateField(
                        _tagHelperContext.ExecutionContextTypeName,
                        CSharpTagHelperCodeRenderer.ExecutionContextVariableName,
                        value: null);

                    WritePrivateField(
                        _tagHelperContext.RunnerTypeName,
                        CSharpTagHelperCodeRenderer.RunnerVariableName,
                        value: null);

                    WritePrivateField(
                        _tagHelperContext.ScopeManagerTypeName,
                        CSharpTagHelperCodeRenderer.ScopeManagerVariableName,
                        value: null);
                }
            }

            foreach (var descriptor in chunk.Descriptors)
            {
                if (!_declaredTagHelpers.Contains(descriptor.TypeName))
                {
                    _declaredTagHelpers.Add(descriptor.TypeName);

                    WritePrivateField(
                        descriptor.TypeName,
                        CSharpTagHelperCodeRenderer.GetVariableName(descriptor),
                        value: null);
                }
            }

            if (!Context.Host.DesignTimeMode)
            {
                PreAllocateUnboundTagHelperAttributes(chunk);
            }

            // We need to dive deeper to ensure we pick up any nested tag helpers.
            Accept(chunk.Children);
        }
Пример #2
0
        private static bool CanPreallocateBoundAttribute(
            IEnumerable <TagHelperAttributeDescriptor> associatedAttributeDescriptors,
            TagHelperAttributeTracker attribute)
        {
            // If the attribute value is a Dynamic value, it cannot be preallocated.
            if (CSharpTagHelperCodeRenderer.IsDynamicAttributeValue(attribute.Value))
            {
                return(false);
            }

            // Only attributes that are associated with string typed properties can be preallocated.
            var attributeName       = attribute.Name;
            var allStringProperties = associatedAttributeDescriptors
                                      .All(attributeDescriptor => attributeDescriptor.IsStringProperty);

            return(allStringProperties);
        }
Пример #3
0
        private void PreAllocateTagHelperAttributes(TagHelperChunk chunk)
        {
            var boundAttributes = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            for (var i = 0; i < chunk.Attributes.Count; i++)
            {
                var attribute = chunk.Attributes[i];
                var associatedAttributeDescriptors = chunk.Descriptors.SelectMany(descriptor => descriptor.Attributes)
                                                     .Where(attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Name));

                // If there's no descriptors associated or is a repeated attribute with same name as a bound attribute,
                // it is considered as an unbound attribute.
                var isUnBoundAttribute = !associatedAttributeDescriptors.Any() || !boundAttributes.Add(attribute.Name);

                // Perf: We will preallocate TagHelperAttribute for unbound attributes and simple bound string valued attributes.
                if (isUnBoundAttribute || CanPreallocateBoundAttribute(associatedAttributeDescriptors, attribute))
                {
                    string preAllocatedAttributeVariableName = null;

                    if (attribute.ValueStyle == HtmlAttributeValueStyle.Minimized)
                    {
                        Debug.Assert(attribute.Value == null);

                        var preAllocatedAttributeKey = new TagHelperAttributeKey(
                            attribute.Name,
                            value: null,
                            unBoundAttribute: isUnBoundAttribute,
                            valueStyle: attribute.ValueStyle);
                        if (TryCachePreallocatedVariableName(preAllocatedAttributeKey, out preAllocatedAttributeVariableName))
                        {
                            Writer
                            .Write("private static readonly global::")
                            .Write(_tagHelperContext.TagHelperAttributeTypeName)
                            .Write(" ")
                            .Write(preAllocatedAttributeVariableName)
                            .Write(" = ")
                            .WriteStartNewObject("global::" + _tagHelperContext.TagHelperAttributeTypeName)
                            .WriteStringLiteral(attribute.Name)
                            .WriteEndMethodInvocation();
                        }
                    }
                    else
                    {
                        Debug.Assert(attribute.Value != null);

                        string plainText;
                        if (CSharpTagHelperCodeRenderer.TryGetPlainTextValue(attribute.Value, out plainText))
                        {
                            var preAllocatedAttributeKey = new TagHelperAttributeKey(attribute.Name, plainText, isUnBoundAttribute, attribute.ValueStyle);
                            if (TryCachePreallocatedVariableName(preAllocatedAttributeKey, out preAllocatedAttributeVariableName))
                            {
                                Writer
                                .Write("private static readonly global::")
                                .Write(_tagHelperContext.TagHelperAttributeTypeName)
                                .Write(" ")
                                .Write(preAllocatedAttributeVariableName)
                                .Write(" = ")
                                .WriteStartNewObject("global::" + _tagHelperContext.TagHelperAttributeTypeName)
                                .WriteStringLiteral(attribute.Name)
                                .WriteParameterSeparator();

                                if (isUnBoundAttribute)
                                {
                                    // For unbound attributes, we need to create HtmlString.
                                    Writer
                                    .WriteStartNewObject("global::" + _tagHelperContext.EncodedHtmlStringTypeName)
                                    .WriteStringLiteral(plainText)
                                    .WriteEndMethodInvocation(endLine: false);
                                }
                                else
                                {
                                    Writer.WriteStringLiteral(plainText);
                                }

                                Writer
                                .WriteParameterSeparator()
                                .Write($"global::{typeof(HtmlAttributeValueStyle).FullName}.{attribute.ValueStyle}")
                                .WriteEndMethodInvocation();
                            }
                        }
                    }

                    if (preAllocatedAttributeVariableName != null)
                    {
                        chunk.Attributes[i] = new TagHelperAttributeTracker(
                            attribute.Name,
                            new PreallocatedTagHelperAttributeChunk
                        {
                            AttributeVariableAccessor = preAllocatedAttributeVariableName
                        },
                            attribute.ValueStyle);
                    }
                }
            }
        }
        private void PreAllocateUnboundTagHelperAttributes(TagHelperChunk chunk)
        {
            var boundAttributes = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            for (var i = 0; i < chunk.Attributes.Count; i++)
            {
                var attribute = chunk.Attributes[i];
                var hasAssociatedDescriptors = chunk.Descriptors.Any(descriptor =>
                                                                     descriptor.Attributes.Any(attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Key)));

                // If there's no descriptors associated or we're hitting a bound attribute a second time.
                if (!hasAssociatedDescriptors || !boundAttributes.Add(attribute.Key))
                {
                    string preAllocatedAttributeVariableName = null;

                    if (attribute.Value == null)
                    {
                        var preAllocatedAttributeKey = new TagHelperAttributeKey(attribute.Key, value: null);
                        if (TryCachePreallocatedVariableName(preAllocatedAttributeKey, out preAllocatedAttributeVariableName))
                        {
                            Writer
                            .Write("private static readonly global::")
                            .Write(_tagHelperContext.TagHelperAttributeTypeName)
                            .Write(" ")
                            .Write(preAllocatedAttributeVariableName)
                            .Write(" = ")
                            .WriteStartNewObject("global::" + _tagHelperContext.TagHelperAttributeTypeName)
                            .WriteStringLiteral(attribute.Key)
                            .WriteEndMethodInvocation();
                        }
                    }
                    else
                    {
                        string plainText;
                        if (CSharpTagHelperCodeRenderer.TryGetPlainTextValue(attribute.Value, out plainText))
                        {
                            var preAllocatedAttributeKey = new TagHelperAttributeKey(attribute.Key, plainText);
                            if (TryCachePreallocatedVariableName(preAllocatedAttributeKey, out preAllocatedAttributeVariableName))
                            {
                                Writer
                                .Write("private static readonly global::")
                                .Write(_tagHelperContext.TagHelperAttributeTypeName)
                                .Write(" ")
                                .Write(preAllocatedAttributeVariableName)
                                .Write(" = ")
                                .WriteStartNewObject("global::" + _tagHelperContext.TagHelperAttributeTypeName)
                                .WriteStringLiteral(attribute.Key)
                                .WriteParameterSeparator()
                                .WriteStartNewObject("global::" + _tagHelperContext.EncodedHtmlStringTypeName)
                                .WriteStringLiteral(plainText)
                                .WriteEndMethodInvocation(endLine: false)
                                .WriteEndMethodInvocation();
                            }
                        }
                    }

                    if (preAllocatedAttributeVariableName != null)
                    {
                        chunk.Attributes[i] = new KeyValuePair <string, Chunk>(
                            attribute.Key,
                            new PreallocatedTagHelperAttributeChunk
                        {
                            AttributeVariableAccessor = preAllocatedAttributeVariableName
                        });
                    }
                }
            }
        }