Exemplo n.º 1
0
        // Currently the same for design time and runtime
        public void WriteComponentTypeInferenceMethod(CodeRenderingContext context, ComponentTypeInferenceMethodIntermediateNode node)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

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

            // This is ugly because CodeWriter doesn't allow us to erase, but we need to comma-delimit. So we have to
            // materizalize something can iterate, or use string.Join. We'll need this multiple times, so materializing
            // it.
            var parameters = GetParameterDeclarations();

            // This is really similar to the code in WriteComponentAttribute and WriteComponentChildContent - except simpler because
            // attributes and child contents look like variables.
            //
            // Looks like:
            //
            //  public static void CreateFoo_0<T1, T2>(RenderTreeBuilder builder, int seq, int __seq0, T1 __arg0, int __seq1, global::System.Collections.Generic.List<T2> __arg1, int __seq2, string __arg2)
            //  {
            //      builder.OpenComponent<Foo<T1, T2>>();
            //      builder.AddAttribute(__seq0, "Attr0", __arg0);
            //      builder.AddAttribute(__seq1, "Attr1", __arg1);
            //      builder.AddAttribute(__seq2, "Attr2", __arg2);
            //      builder.CloseComponent();
            //  }
            //
            // As a special case, we need to generate a thunk for captures in this block instead of using
            // them verbatim.
            //
            // The problem is that RenderTreeBuilder wants an Action<object>. The caller can't write the type
            // name if it contains generics, and we can't write the variable they want to assign to.
            var writer = context.CodeWriter;

            writer.Write("public static void ");
            writer.Write(node.MethodName);

            writer.Write("<");
            writer.Write(string.Join(", ", node.Component.Component.GetTypeParameters().Select(a => a.Name)));
            writer.Write(">");

            writer.Write("(");
            writer.Write("global::");
            writer.Write(ComponentsApi.RenderTreeBuilder.FullTypeName);
            writer.Write(" builder");
            writer.Write(", ");
            writer.Write("int seq");

            if (parameters.Count > 0)
            {
                writer.Write(", ");
            }

            for (var i = 0; i < parameters.Count; i++)
            {
                writer.Write("int ");
                writer.Write(parameters[i].seqName);

                writer.Write(", ");
                writer.Write(parameters[i].typeName);
                writer.Write(" ");
                writer.Write(parameters[i].parameterName);

                if (i < parameters.Count - 1)
                {
                    writer.Write(", ");
                }
            }

            writer.Write(")");
            writer.WriteLine();

            writer.WriteLine("{");

            // builder.OpenComponent<TComponent>(42);
            context.CodeWriter.Write("builder");
            context.CodeWriter.Write(".");
            context.CodeWriter.Write(ComponentsApi.RenderTreeBuilder.OpenComponent);
            context.CodeWriter.Write("<");
            context.CodeWriter.Write(node.Component.TypeName);
            context.CodeWriter.Write(">(");
            context.CodeWriter.Write("seq");
            context.CodeWriter.Write(");");
            context.CodeWriter.WriteLine();

            var index = 0;

            foreach (var attribute in node.Component.Attributes)
            {
                context.CodeWriter.WriteStartInstanceMethodInvocation("builder", ComponentsApi.RenderTreeBuilder.AddAttribute);
                context.CodeWriter.Write(parameters[index].seqName);
                context.CodeWriter.Write(", ");

                context.CodeWriter.Write($"\"{attribute.AttributeName}\"");
                context.CodeWriter.Write(", ");

                context.CodeWriter.Write(parameters[index].parameterName);
                context.CodeWriter.WriteEndMethodInvocation();

                index++;
            }

            foreach (var childContent in node.Component.ChildContents)
            {
                context.CodeWriter.WriteStartInstanceMethodInvocation("builder", ComponentsApi.RenderTreeBuilder.AddAttribute);
                context.CodeWriter.Write(parameters[index].seqName);
                context.CodeWriter.Write(", ");

                context.CodeWriter.Write($"\"{childContent.AttributeName}\"");
                context.CodeWriter.Write(", ");

                context.CodeWriter.Write(parameters[index].parameterName);
                context.CodeWriter.WriteEndMethodInvocation();

                index++;
            }

            foreach (var capture in node.Component.Captures)
            {
                context.CodeWriter.WriteStartInstanceMethodInvocation("builder", capture.IsComponentCapture ? ComponentsApi.RenderTreeBuilder.AddComponentReferenceCapture : ComponentsApi.RenderTreeBuilder.AddElementReferenceCapture);
                context.CodeWriter.Write(parameters[index].seqName);
                context.CodeWriter.Write(", ");

                var cast = capture.IsComponentCapture ? $"({capture.ComponentCaptureTypeName})" : string.Empty;
                context.CodeWriter.Write($"(__value) => {{ {parameters[index].parameterName}({cast}__value); }}");
                context.CodeWriter.WriteEndMethodInvocation();

                index++;
            }

            context.CodeWriter.WriteInstanceMethodInvocation("builder", ComponentsApi.RenderTreeBuilder.CloseComponent);

            writer.WriteLine("}");

            List <(string seqName, string typeName, string parameterName)> GetParameterDeclarations()
            {
                var p = new List <(string seqName, string typeName, string parameterName)>();

                foreach (var attribute in node.Component.Attributes)
                {
                    p.Add(($"__seq{p.Count}", attribute.TypeName, $"__arg{p.Count}"));
                }

                foreach (var childContent in node.Component.ChildContents)
                {
                    p.Add(($"__seq{p.Count}", childContent.TypeName, $"__arg{p.Count}"));
                }

                foreach (var capture in node.Component.Captures)
                {
                    p.Add(($"__seq{p.Count}", capture.TypeName, $"__arg{p.Count}"));
                }

                return(p);
            }
        }
Exemplo n.º 2
0
            private void CreateTypeInferenceMethod(
                DocumentIntermediateNode documentNode,
                ComponentExtensionNode node,
                Dictionary <string, GenericTypeNameRewriter.Binding> bindings)
            {
                var @namespace = documentNode.FindPrimaryNamespace().Content;

                @namespace  = string.IsNullOrEmpty(@namespace) ? "__Blazor" : "__Blazor." + @namespace;
                @namespace += "." + documentNode.FindPrimaryClass().ClassName;

                var typeInferenceNode = new ComponentTypeInferenceMethodIntermediateNode()
                {
                    Bindings  = bindings,
                    Component = node,

                    // Method name is generated and guaranteed not to collide, since it's unique for each
                    // component call site.
                    MethodName   = $"Create{node.TagName}_{_id++}",
                    FullTypeName = @namespace + ".TypeInference",
                };

                node.TypeInferenceNode = typeInferenceNode;

                // Now we need to insert the type inference node into the tree.
                var namespaceNode = documentNode.Children
                                    .OfType <NamespaceDeclarationIntermediateNode>()
                                    .Where(n => n.Annotations.Contains(new KeyValuePair <object, object>(BlazorMetadata.Component.GenericTypedKey, bool.TrueString)))
                                    .FirstOrDefault();

                if (namespaceNode == null)
                {
                    namespaceNode = new NamespaceDeclarationIntermediateNode()
                    {
                        Annotations =
                        {
                            { BlazorMetadata.Component.GenericTypedKey, bool.TrueString },
                        },
                        Content = @namespace,
                    };

                    documentNode.Children.Add(namespaceNode);
                }

                var classNode = namespaceNode.Children
                                .OfType <ClassDeclarationIntermediateNode>()
                                .Where(n => n.ClassName == "TypeInference")
                                .FirstOrDefault();

                if (classNode == null)
                {
                    classNode = new ClassDeclarationIntermediateNode()
                    {
                        ClassName = "TypeInference",
                        Modifiers =
                        {
                            "internal",
                            "static",
                        },
                    };
                    namespaceNode.Children.Add(classNode);
                }

                classNode.Children.Add(typeInferenceNode);
            }