File: System\Xml\Serialization\XmlSerializationWriterILGen.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Xml.Schema;
 
namespace System.Xml.Serialization
{
    [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
    [RequiresDynamicCode(XmlSerializer.AotSerializationWarning)]
    internal sealed class XmlSerializationWriterILGen : XmlSerializationILGen
    {
        internal XmlSerializationWriterILGen(TypeScope[] scopes, string access, string className)
            : base(scopes, access, className)
        {
        }
 
        internal void GenerateBegin()
        {
            this.typeBuilder = CodeGenerator.CreateTypeBuilder(
                ModuleBuilder,
                ClassName,
                TypeAttributes | TypeAttributes.BeforeFieldInit,
                typeof(XmlSerializationWriter),
                Type.EmptyTypes);
 
            foreach (TypeScope scope in Scopes)
            {
                foreach (TypeMapping mapping in scope.TypeMappings)
                {
                    if (mapping is StructMapping || mapping is EnumMapping)
                    {
                        MethodNames.Add(mapping, NextMethodName(mapping.TypeDesc!.Name));
                    }
                }
                ReflectionAwareILGen.WriteReflectionInit(scope);
            }
        }
 
        internal override void GenerateMethod(TypeMapping mapping)
        {
            if (!GeneratedMethods.Add(mapping))
                return;
 
            if (mapping is StructMapping)
            {
                WriteStructMethod((StructMapping)mapping);
            }
            else if (mapping is EnumMapping)
            {
                WriteEnumMethod((EnumMapping)mapping);
            }
        }
 
        internal Type GenerateEnd()
        {
            GenerateReferencedMethods();
            GenerateInitCallbacksMethod();
            this.typeBuilder.DefineDefaultConstructor(
                CodeGenerator.PublicMethodAttributes);
            return this.typeBuilder.CreateType();
        }
 
        internal string? GenerateElement(XmlMapping xmlMapping)
        {
            if (!xmlMapping.IsWriteable)
                return null;
            if (!xmlMapping.GenerateSerializer)
                throw new ArgumentException(SR.XmlInternalError, nameof(xmlMapping));
            if (xmlMapping is XmlTypeMapping)
                return GenerateTypeElement((XmlTypeMapping)xmlMapping);
            else if (xmlMapping is XmlMembersMapping)
                return GenerateMembersElement((XmlMembersMapping)xmlMapping);
            else
                throw new ArgumentException(SR.XmlInternalError, nameof(xmlMapping));
        }
 
        private void GenerateInitCallbacksMethod()
        {
            ilg = new CodeGenerator(this.typeBuilder);
            ilg.BeginMethod(typeof(void), "InitCallbacks", Type.EmptyTypes, Array.Empty<string>(),
                CodeGenerator.ProtectedOverrideMethodAttributes);
            ilg.EndMethod();
        }
 
        private void WriteQualifiedNameElement(string name, string? ns, object? defaultValue, SourceInfo source, bool nullable, TypeMapping mapping)
        {
            bool hasDefault = defaultValue != null && defaultValue != DBNull.Value;
            if (hasDefault)
            {
                throw Globals.NotSupported("XmlQualifiedName DefaultValue not supported.  Fail in WriteValue()");
            }
            List<Type> argTypes = new List<Type>();
            ilg.Ldarg(0);
            ilg.Ldstr(GetCSharpString(name));
            argTypes.Add(typeof(string));
            if (ns != null)
            {
                ilg.Ldstr(GetCSharpString(ns));
                argTypes.Add(typeof(string));
            }
            source.Load(mapping.TypeDesc!.Type!);
            argTypes.Add(mapping.TypeDesc.Type!);
 
            MethodInfo XmlSerializationWriter_WriteXXX = typeof(XmlSerializationWriter).GetMethod(
                 nullable ? ("WriteNullableQualifiedNameLiteral") : "WriteElementQualifiedName",
                 CodeGenerator.InstanceBindingFlags,
                 argTypes.ToArray()
                 )!;
            ilg.Call(XmlSerializationWriter_WriteXXX);
        }
 
        private void WriteEnumValue(EnumMapping mapping, SourceInfo source, out Type returnType)
        {
            string? methodName = ReferenceMapping(mapping);
 
#if DEBUG
            // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
            if (methodName == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorMethod, mapping.TypeDesc!.Name));
#endif
 
            // For enum, its write method (eg. Write1_Gender) could be called multiple times
            // prior to its declaration.
            MethodBuilder methodBuilder = EnsureMethodBuilder(typeBuilder,
                    methodName!,
                    CodeGenerator.PrivateMethodAttributes,
                    typeof(string),
                    new Type[] { mapping.TypeDesc!.Type! });
            ilg.Ldarg(0);
            source.Load(mapping.TypeDesc.Type!);
            ilg.Call(methodBuilder);
            returnType = typeof(string);
        }
 
        private void WritePrimitiveValue(TypeDesc typeDesc, SourceInfo source, out Type returnType)
        {
            if (typeDesc == StringTypeDesc || typeDesc.FormatterName == "String")
            {
                source.Load(typeDesc.Type!);
                returnType = typeDesc.Type!;
            }
            else
            {
                if (!typeDesc.HasCustomFormatter)
                {
                    Type argType = typeDesc.Type!;
                    // No ToString(Byte), compiler used ToString(Int16) instead.
                    if (argType == typeof(byte))
                        argType = typeof(short);
                    // No ToString(UInt16), compiler used ToString(Int32) instead.
                    else if (argType == typeof(ushort))
                        argType = typeof(int);
                    MethodInfo XmlConvert_ToString = typeof(XmlConvert).GetMethod(
                        "ToString",
                        CodeGenerator.StaticBindingFlags,
                        new Type[] { argType }
                        )!;
                    source.Load(typeDesc.Type!);
                    ilg.Call(XmlConvert_ToString);
                    returnType = XmlConvert_ToString.ReturnType;
                }
                else
                {
                    // Only these methods below that is non Static and need to ldarg("this") for Call.
                    BindingFlags bindingFlags = CodeGenerator.StaticBindingFlags;
                    if (typeDesc.FormatterName == "XmlQualifiedName")
                    {
                        bindingFlags = CodeGenerator.InstanceBindingFlags;
                        ilg.Ldarg(0);
                    }
                    MethodInfo FromXXX = typeof(XmlSerializationWriter).GetMethod(
                        $"From{typeDesc.FormatterName}",
                           bindingFlags,
                           new Type[] { typeDesc.Type! }
                           )!;
                    source.Load(typeDesc.Type!);
                    ilg.Call(FromXXX);
                    returnType = FromXXX.ReturnType;
                }
            }
        }
 
        private void WritePrimitive(string method, string name, string? ns, object? defaultValue, SourceInfo source, TypeMapping mapping, bool writeXsiType, bool isElement, bool isNullable)
        {
            TypeDesc typeDesc = mapping.TypeDesc!;
            bool hasDefault = defaultValue != null && defaultValue != DBNull.Value && mapping.TypeDesc!.HasDefaultSupport;
            if (hasDefault)
            {
                if (mapping is EnumMapping)
                {
#if DEBUG
                    // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
                    if (defaultValue!.GetType() != typeof(string)) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, name + " has invalid default type " + defaultValue.GetType().Name));
#endif
 
                    source.Load(mapping.TypeDesc!.Type!);
                    string? enumDefaultValue = null;
                    if (((EnumMapping)mapping).IsFlags)
                    {
                        string[] values = ((string)defaultValue!).Split(null);
                        for (int i = 0; i < values.Length; i++)
                        {
                            if (string.IsNullOrEmpty(values[i]))
                                continue;
                            if (i > 0)
                                enumDefaultValue += ", ";
                            enumDefaultValue += values[i];
                        }
                    }
                    else
                    {
                        enumDefaultValue = (string)defaultValue!;
                    }
                    ilg.Ldc(Enum.Parse(mapping.TypeDesc.Type!, enumDefaultValue!, false));
                    ilg.If(Cmp.NotEqualTo); // " != "
                }
                else
                {
                    WriteCheckDefault(source, defaultValue!, isNullable);
                }
            }
            List<Type> argTypes = new List<Type>();
            ilg.Ldarg(0);
            argTypes.Add(typeof(string));
            ilg.Ldstr(GetCSharpString(name));
            if (ns != null)
            {
                argTypes.Add(typeof(string));
                ilg.Ldstr(GetCSharpString(ns));
            }
            Type argType;
 
            if (mapping is EnumMapping)
            {
                WriteEnumValue((EnumMapping)mapping, source, out argType);
                argTypes.Add(argType);
            }
            else
            {
                WritePrimitiveValue(typeDesc, source, out argType);
                argTypes.Add(argType);
            }
 
            if (writeXsiType)
            {
                argTypes.Add(typeof(XmlQualifiedName));
                ConstructorInfo XmlQualifiedName_ctor = typeof(XmlQualifiedName).GetConstructor(
                    CodeGenerator.InstanceBindingFlags,
                    new Type[] { typeof(string), typeof(string) }
                    )!;
                ilg.Ldstr(GetCSharpString(mapping.TypeName));
                ilg.Ldstr(GetCSharpString(mapping.Namespace));
                ilg.New(XmlQualifiedName_ctor);
            }
 
            MethodInfo XmlSerializationWriter_method = typeof(XmlSerializationWriter).GetMethod(
                   method,
                   CodeGenerator.InstanceBindingFlags,
                   argTypes.ToArray()
                   )!;
            ilg.Call(XmlSerializationWriter_method);
 
            if (hasDefault)
            {
                ilg.EndIf();
            }
        }
 
        private void WriteTag(string methodName, string name, string? ns)
        {
            MethodInfo XmlSerializationWriter_Method = typeof(XmlSerializationWriter).GetMethod(
                methodName,
                CodeGenerator.InstanceBindingFlags,
                new Type[] { typeof(string), typeof(string) }
                )!;
            ilg.Ldarg(0);
            ilg.Ldstr(GetCSharpString(name));
            ilg.Ldstr(GetCSharpString(ns));
            ilg.Call(XmlSerializationWriter_Method);
        }
 
        private void WriteTag(string methodName, string name, string? ns, bool writePrefixed)
        {
            MethodInfo XmlSerializationWriter_Method = typeof(XmlSerializationWriter).GetMethod(
                methodName,
                CodeGenerator.InstanceBindingFlags,
                new Type[] { typeof(string), typeof(string), typeof(object), typeof(bool) }
                )!;
            ilg.Ldarg(0);
            ilg.Ldstr(GetCSharpString(name));
            ilg.Ldstr(GetCSharpString(ns));
            ilg.Load(null);
            ilg.Ldc(writePrefixed);
            ilg.Call(XmlSerializationWriter_Method);
        }
 
        private void WriteStartElement(string name, string? ns, bool writePrefixed)
        {
            WriteTag("WriteStartElement", name, ns, writePrefixed);
        }
 
        private void WriteEndElement()
        {
            MethodInfo XmlSerializationWriter_WriteEndElement = typeof(XmlSerializationWriter).GetMethod(
                "WriteEndElement",
                CodeGenerator.InstanceBindingFlags,
                Type.EmptyTypes
                )!;
            ilg.Ldarg(0);
            ilg.Call(XmlSerializationWriter_WriteEndElement);
        }
        private void WriteEndElement(string source)
        {
            MethodInfo XmlSerializationWriter_WriteEndElement = typeof(XmlSerializationWriter).GetMethod(
                "WriteEndElement",
                CodeGenerator.InstanceBindingFlags,
                s_objectType
                )!;
            object oVar = ilg.GetVariable(source);
            ilg.Ldarg(0);
            ilg.Load(oVar);
            ilg.ConvertValue(CodeGenerator.GetVariableType(oVar), typeof(object));
            ilg.Call(XmlSerializationWriter_WriteEndElement);
        }
 
        private void WriteLiteralNullTag(string name, string? ns)
        {
            WriteTag("WriteNullTagLiteral", name, ns);
        }
 
        private void WriteEmptyTag(string name, string? ns)
        {
            WriteTag("WriteEmptyTag", name, ns);
        }
 
        private static readonly string[] s_argNamesP = new string[] { "p" };
        private static readonly string[] s_argNamesO = new string[] { "o" };
        private static readonly Type[] s_objectArrayType = new Type[] { typeof(object[]) };
        private static readonly Type[] s_objectType = new Type[] { typeof(object) };
 
        private string GenerateMembersElement(XmlMembersMapping xmlMembersMapping)
        {
            ElementAccessor element = xmlMembersMapping.Accessor;
            MembersMapping mapping = (MembersMapping)element.Mapping!;
            bool hasWrapperElement = mapping.HasWrapperElement;
            bool writeAccessors = mapping.WriteAccessors;
            string methodName = NextMethodName(element.Name);
            ilg = new CodeGenerator(this.typeBuilder);
            ilg.BeginMethod(
                typeof(void),
                methodName,
                s_objectArrayType,
                s_argNamesP,
                CodeGenerator.PublicMethodAttributes
                );
 
            MethodInfo XmlSerializationWriter_WriteStartDocument = typeof(XmlSerializationWriter).GetMethod(
                "WriteStartDocument",
                CodeGenerator.InstanceBindingFlags,
                Type.EmptyTypes
                )!;
            ilg.Ldarg(0);
            ilg.Call(XmlSerializationWriter_WriteStartDocument);
 
            MethodInfo XmlSerializationWriter_TopLevelElement = typeof(XmlSerializationWriter).GetMethod(
                "TopLevelElement",
                CodeGenerator.InstanceBindingFlags,
                Type.EmptyTypes
                )!;
            ilg.Ldarg(0);
            ilg.Call(XmlSerializationWriter_TopLevelElement);
 
            // in the top-level method add check for the parameters length,
            // because visual basic does not have a concept of an <out> parameter it uses <ByRef> instead
            // so sometime we think that we have more parameters then supplied
            LocalBuilder pLengthLoc = ilg.DeclareLocal(typeof(int), "pLength");
            ilg.Ldarg("p");
            ilg.Ldlen();
            ilg.Stloc(pLengthLoc);
 
            if (hasWrapperElement)
            {
                WriteStartElement(element.Name, (element.Form == XmlSchemaForm.Qualified ? element.Namespace : ""), false);
 
                int xmlnsMember = FindXmlnsIndex(mapping.Members!);
                if (xmlnsMember >= 0)
                {
                    string source = string.Create(CultureInfo.InvariantCulture, $"(({typeof(XmlSerializerNamespaces).FullName})p[{xmlnsMember}])");
 
                    ilg.Ldloc(pLengthLoc);
                    ilg.Ldc(xmlnsMember);
                    ilg.If(Cmp.GreaterThan);
                    WriteNamespaces(source);
                    ilg.EndIf();
                }
 
                for (int i = 0; i < mapping.Members!.Length; i++)
                {
                    MemberMapping member = mapping.Members[i];
 
                    if (member.Attribute != null && !member.Ignore)
                    {
                        SourceInfo source = new SourceInfo($"p[{i}]", null, null, pLengthLoc.LocalType.GetElementType()!, ilg);
 
                        SourceInfo? specifiedSource = null;
                        int specifiedPosition = 0;
                        if (member.CheckSpecified != SpecifiedAccessor.None)
                        {
                            string memberNameSpecified = $"{member.Name}Specified";
                            for (int j = 0; j < mapping.Members.Length; j++)
                            {
                                if (mapping.Members[j].Name == memberNameSpecified)
                                {
                                    specifiedSource = new SourceInfo($"((bool)p[{j}])", null, null, typeof(bool), ilg);
                                    specifiedPosition = j;
                                    break;
                                }
                            }
                        }
 
                        ilg.Ldloc(pLengthLoc);
                        ilg.Ldc(i);
                        ilg.If(Cmp.GreaterThan);
 
                        if (specifiedSource != null)
                        {
                            Label labelTrue = ilg.DefineLabel();
                            Label labelEnd = ilg.DefineLabel();
                            ilg.Ldloc(pLengthLoc);
                            ilg.Ldc(specifiedPosition);
                            ilg.Ble(labelTrue);
                            specifiedSource.Load(typeof(bool));
                            ilg.Br_S(labelEnd);
                            ilg.MarkLabel(labelTrue);
                            ilg.Ldc(true);
                            ilg.MarkLabel(labelEnd);
                            ilg.If();
                        }
 
                        WriteMember(source, member.Attribute, member.TypeDesc!, "p");
 
                        if (specifiedSource != null)
                        {
                            ilg.EndIf();
                        }
 
                        ilg.EndIf();
                    }
                }
            }
 
            for (int i = 0; i < mapping.Members!.Length; i++)
            {
                MemberMapping member = mapping.Members[i];
                if (member.Xmlns != null)
                    continue;
                if (member.Ignore)
                    continue;
 
                SourceInfo? specifiedSource = null;
                int specifiedPosition = 0;
                if (member.CheckSpecified != SpecifiedAccessor.None)
                {
                    string memberNameSpecified = $"{member.Name}Specified";
 
                    for (int j = 0; j < mapping.Members.Length; j++)
                    {
                        if (mapping.Members[j].Name == memberNameSpecified)
                        {
                            specifiedSource = new SourceInfo($"((bool)p[{j}])", null, null, typeof(bool), ilg);
                            specifiedPosition = j;
                            break;
                        }
                    }
                }
 
                ilg.Ldloc(pLengthLoc);
                ilg.Ldc(i);
                ilg.If(Cmp.GreaterThan);
 
                if (specifiedSource != null)
                {
                    Label labelTrue = ilg.DefineLabel();
                    Label labelEnd = ilg.DefineLabel();
                    ilg.Ldloc(pLengthLoc);
                    ilg.Ldc(specifiedPosition);
                    ilg.Ble(labelTrue);
                    specifiedSource.Load(typeof(bool));
                    ilg.Br_S(labelEnd);
                    ilg.MarkLabel(labelTrue);
                    ilg.Ldc(true);
                    ilg.MarkLabel(labelEnd);
                    ilg.If();
                }
 
                string source = $"p[{i}]";
                string? enumSource = null;
                if (member.ChoiceIdentifier != null)
                {
                    for (int j = 0; j < mapping.Members.Length; j++)
                    {
                        if (mapping.Members[j].Name == member.ChoiceIdentifier.MemberName)
                        {
                            enumSource = $"(({mapping.Members[j].TypeDesc!.CSharpName})p[{j}])";
                            break;
                        }
                    }
 
#if DEBUG
                    // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
                    if (enumSource == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, "Can not find " + member.ChoiceIdentifier.MemberName + " in the members mapping."));
#endif
                }
 
                // override writeAccessors choice when we've written a wrapper element
                WriteMember(new SourceInfo(source, source, null, null, ilg), enumSource, member.ElementsSortedByDerivation!, member.Text, member.ChoiceIdentifier, member.TypeDesc!, writeAccessors || hasWrapperElement);
 
                if (specifiedSource != null)
                {
                    ilg.EndIf();
                }
 
                ilg.EndIf();
            }
 
            if (hasWrapperElement)
            {
                WriteEndElement();
            }
            ilg.EndMethod();
            return methodName;
        }
 
        private string GenerateTypeElement(XmlTypeMapping xmlTypeMapping)
        {
            ElementAccessor element = xmlTypeMapping.Accessor;
            TypeMapping mapping = element.Mapping!;
            string methodName = NextMethodName(element.Name);
            ilg = new CodeGenerator(this.typeBuilder);
            ilg.BeginMethod(
                typeof(void),
                methodName,
                s_objectType,
                s_argNamesO,
                CodeGenerator.PublicMethodAttributes
                );
 
            MethodInfo XmlSerializationWriter_WriteStartDocument = typeof(XmlSerializationWriter).GetMethod(
                "WriteStartDocument",
                CodeGenerator.InstanceBindingFlags,
                Type.EmptyTypes
                )!;
            ilg.Ldarg(0);
            ilg.Call(XmlSerializationWriter_WriteStartDocument);
 
            ilg.If(ilg.GetArg("o"), Cmp.EqualTo, null);
            if (element.IsNullable)
            {
                WriteLiteralNullTag(element.Name, (element.Form == XmlSchemaForm.Qualified ? element.Namespace : ""));
            }
            else
                WriteEmptyTag(element.Name, (element.Form == XmlSchemaForm.Qualified ? element.Namespace : ""));
            ilg.GotoMethodEnd();
            ilg.EndIf();
 
            if (!mapping.TypeDesc!.IsValueType && !mapping.TypeDesc.Type!.IsPrimitive)
            {
                MethodInfo XmlSerializationWriter_TopLevelElement = typeof(XmlSerializationWriter).GetMethod(
                      "TopLevelElement",
                      CodeGenerator.InstanceBindingFlags,
                      Type.EmptyTypes
                      )!;
                ilg.Ldarg(0);
                ilg.Call(XmlSerializationWriter_TopLevelElement);
            }
 
            WriteMember(new SourceInfo("o", "o", null, typeof(object), ilg), null, new ElementAccessor[] { element }, null, null, mapping.TypeDesc, true);
 
            ilg.EndMethod();
            return methodName;
        }
 
        private string NextMethodName(string name)
        {
            return string.Create(CultureInfo.InvariantCulture, $"Write{++NextMethodNumber}_{CodeIdentifier.MakeValidInternal(name)}");
        }
 
        private void WriteEnumMethod(EnumMapping mapping)
        {
            string? methodName;
            MethodNames.TryGetValue(mapping, out methodName);
            List<Type> argTypes = new List<Type>();
            List<string> argNames = new List<string>();
            argTypes.Add(mapping.TypeDesc!.Type!);
            argNames.Add("v");
            ilg = new CodeGenerator(this.typeBuilder);
            ilg.BeginMethod(
                typeof(string),
                GetMethodBuilder(methodName!),
                argTypes.ToArray(),
                argNames.ToArray(),
                CodeGenerator.PrivateMethodAttributes);
            LocalBuilder sLoc = ilg.DeclareLocal(typeof(string), "s");
            ilg.Load(null);
            ilg.Stloc(sLoc);
            ConstantMapping[] constants = mapping.Constants!;
 
            if (constants.Length > 0)
            {
                var values = new HashSet<long>();
                List<Label> caseLabels = new List<Label>();
                List<string> retValues = new List<string>();
                Label defaultLabel = ilg.DefineLabel();
                Label endSwitchLabel = ilg.DefineLabel();
                // This local is necessary; otherwise, it becomes if/else
                LocalBuilder localTmp = ilg.DeclareLocal(mapping.TypeDesc.Type!, "localTmp");
                ilg.Ldarg("v");
                ilg.Stloc(localTmp);
                for (int i = 0; i < constants.Length; i++)
                {
                    ConstantMapping c = constants[i];
                    if (values.Add(c.Value))
                    {
                        Label caseLabel = ilg.DefineLabel();
                        ilg.Ldloc(localTmp);
                        ilg.Ldc(Enum.ToObject(mapping.TypeDesc.Type!, c.Value));
                        ilg.Beq(caseLabel);
                        caseLabels.Add(caseLabel);
                        retValues.Add(GetCSharpString(c.XmlName));
                    }
                }
 
 
                if (mapping.IsFlags)
                {
                    ilg.Br(defaultLabel);
                    for (int i = 0; i < caseLabels.Count; i++)
                    {
                        ilg.MarkLabel(caseLabels[i]);
                        ilg.Ldc(retValues[i]);
                        ilg.Stloc(sLoc);
                        ilg.Br(endSwitchLabel);
                    }
                    ilg.MarkLabel(defaultLabel);
                    ReflectionAwareILGen.ILGenForEnumLongValue(ilg, "v");
                    LocalBuilder strArray = ilg.DeclareLocal(typeof(string[]), "strArray");
                    ilg.NewArray(typeof(string), constants.Length);
                    ilg.Stloc(strArray);
                    for (int i = 0; i < constants.Length; i++)
                    {
                        ConstantMapping c = constants[i];
                        ilg.Ldloc(strArray);
                        ilg.Ldc(i);
                        ilg.Ldstr(GetCSharpString(c.XmlName));
                        ilg.Stelem(typeof(string));
                    }
                    ilg.Ldloc(strArray);
                    LocalBuilder longArray = ilg.DeclareLocal(typeof(long[]), "longArray");
                    ilg.NewArray(typeof(long), constants.Length);
                    ilg.Stloc(longArray);
 
                    for (int i = 0; i < constants.Length; i++)
                    {
                        ConstantMapping c = constants[i];
                        ilg.Ldloc(longArray);
                        ilg.Ldc(i);
                        ilg.Ldc(c.Value);
                        ilg.Stelem(typeof(long));
                    }
                    ilg.Ldloc(longArray);
                    ilg.Ldstr(GetCSharpString(mapping.TypeDesc.FullName));
                    MethodInfo XmlSerializationWriter_FromEnum = typeof(XmlSerializationWriter).GetMethod(
                        "FromEnum",
                        CodeGenerator.StaticBindingFlags,
                        new Type[] { typeof(long), typeof(string[]), typeof(long[]), typeof(string) }
                        )!;
                    ilg.Call(XmlSerializationWriter_FromEnum);
                    ilg.Stloc(sLoc);
                    ilg.Br(endSwitchLabel);
                }
                else
                {
                    ilg.Br(defaultLabel);
                    // Case bodies
                    for (int i = 0; i < caseLabels.Count; i++)
                    {
                        ilg.MarkLabel(caseLabels[i]);
                        ilg.Ldc(retValues[i]);
                        ilg.Stloc(sLoc);
                        ilg.Br(endSwitchLabel);
                    }
                    MethodInfo CultureInfo_get_InvariantCulture = typeof(CultureInfo).GetMethod(
                        "get_InvariantCulture",
                        CodeGenerator.StaticBindingFlags,
                        Type.EmptyTypes
                        )!;
                    MethodInfo Int64_ToString = typeof(long).GetMethod(
                        "ToString",
                        CodeGenerator.InstanceBindingFlags,
                        new Type[] { typeof(IFormatProvider) }
                        )!;
                    MethodInfo XmlSerializationWriter_CreateInvalidEnumValueException = typeof(XmlSerializationWriter).GetMethod(
                        "CreateInvalidEnumValueException",
                        CodeGenerator.InstanceBindingFlags,
                        new Type[] { typeof(object), typeof(string) }
                        )!;
                    // Default body
                    ilg.MarkLabel(defaultLabel);
                    ilg.Ldarg(0);
                    ilg.Ldarg("v");
                    ilg.ConvertValue(mapping.TypeDesc.Type!, typeof(long));
                    LocalBuilder numLoc = ilg.DeclareLocal(typeof(long), "num");
                    ilg.Stloc(numLoc);
                    // Invoke method on Value type need address
                    ilg.LdlocAddress(numLoc);
                    ilg.Call(CultureInfo_get_InvariantCulture);
                    ilg.Call(Int64_ToString);
                    ilg.Ldstr(GetCSharpString(mapping.TypeDesc.FullName));
                    ilg.Call(XmlSerializationWriter_CreateInvalidEnumValueException);
                    ilg.Throw();
                }
                ilg.MarkLabel(endSwitchLabel);
            }
            ilg.Ldloc(sLoc);
            ilg.EndMethod();
        }
 
        private void WriteDerivedTypes(StructMapping mapping)
        {
            for (StructMapping? derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping)
            {
                ilg.InitElseIf();
                WriteTypeCompare("t", derived.TypeDesc!.Type!);
                ilg.AndIf();
 
                string? methodName = ReferenceMapping(derived);
 
#if DEBUG
                // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
                if (methodName == null) throw new InvalidOperationException("derived from " + mapping.TypeDesc!.FullName + ", " + SR.Format(SR.XmlInternalErrorMethod, derived.TypeDesc.Name));
#endif
 
                List<Type> argTypes = new List<Type>();
                ilg.Ldarg(0);
                argTypes.Add(typeof(string));
                ilg.Ldarg("n");
                argTypes.Add(typeof(string));
                ilg.Ldarg("ns");
                object oVar = ilg.GetVariable("o");
                Type oType = CodeGenerator.GetVariableType(oVar);
                ilg.Load(oVar);
                ilg.ConvertValue(oType, derived.TypeDesc.Type!);
                argTypes.Add(derived.TypeDesc.Type!);
                if (derived.TypeDesc.IsNullable)
                {
                    argTypes.Add(typeof(bool));
                    ilg.Ldarg("isNullable");
                }
                argTypes.Add(typeof(bool));
                ilg.Ldc(true);
                MethodInfo methodBuilder = EnsureMethodBuilder(typeBuilder,
                    methodName!,
                    CodeGenerator.PrivateMethodAttributes,
                    typeof(void),
                    argTypes.ToArray());
                ilg.Call(methodBuilder);
                ilg.GotoMethodEnd();
 
                WriteDerivedTypes(derived);
            }
        }
 
        private void WriteEnumAndArrayTypes()
        {
            foreach (TypeScope scope in Scopes)
            {
                foreach (Mapping m in scope.TypeMappings)
                {
                    if (m is EnumMapping)
                    {
                        EnumMapping mapping = (EnumMapping)m;
                        ilg.InitElseIf();
                        WriteTypeCompare("t", mapping.TypeDesc!.Type!);
                        // WriteXXXTypeCompare leave bool on the stack
                        ilg.AndIf();
 
                        string? methodName = ReferenceMapping(mapping);
 
#if DEBUG
                        // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
                        if (methodName == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorMethod, mapping.TypeDesc.Name));
#endif
                        MethodInfo XmlSerializationWriter_get_Writer = typeof(XmlSerializationWriter).GetMethod(
                            "get_Writer",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        MethodInfo XmlWriter_WriteStartElement = typeof(XmlWriter).GetMethod(
                            "WriteStartElement",
                            CodeGenerator.InstanceBindingFlags,
                            new Type[] { typeof(string), typeof(string) }
                            )!;
                        ilg.Ldarg(0);
                        ilg.Call(XmlSerializationWriter_get_Writer);
                        ilg.Ldarg("n");
                        ilg.Ldarg("ns");
                        ilg.Call(XmlWriter_WriteStartElement);
                        MethodInfo XmlSerializationWriter_WriteXsiType = typeof(XmlSerializationWriter).GetMethod(
                            "WriteXsiType",
                            CodeGenerator.InstanceBindingFlags,
                            new Type[] { typeof(string), typeof(string) }
                            )!;
                        ilg.Ldarg(0);
                        ilg.Ldstr(GetCSharpString(mapping.TypeName));
                        ilg.Ldstr(GetCSharpString(mapping.Namespace));
                        ilg.Call(XmlSerializationWriter_WriteXsiType);
                        MethodBuilder methodBuilder = EnsureMethodBuilder(typeBuilder,
                            methodName!,
                            CodeGenerator.PrivateMethodAttributes,
                            typeof(string),
                            new Type[] { mapping.TypeDesc.Type! }
                        );
                        MethodInfo XmlWriter_WriteString = typeof(XmlWriter).GetMethod(
                            "WriteString",
                            CodeGenerator.InstanceBindingFlags,
                            new Type[] { typeof(string) }
                            )!;
                        ilg.Ldarg(0);
                        ilg.Call(XmlSerializationWriter_get_Writer);
                        object oVar = ilg.GetVariable("o");
                        ilg.Ldarg(0);
                        ilg.Load(oVar);
                        ilg.ConvertValue(CodeGenerator.GetVariableType(oVar), mapping.TypeDesc.Type!);
                        ilg.Call(methodBuilder);
                        ilg.Call(XmlWriter_WriteString);
                        MethodInfo XmlWriter_WriteEndElement = typeof(XmlWriter).GetMethod(
                            "WriteEndElement",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        ilg.Ldarg(0);
                        ilg.Call(XmlSerializationWriter_get_Writer);
                        ilg.Call(XmlWriter_WriteEndElement);
                        ilg.GotoMethodEnd();
                    }
                    else if (m is ArrayMapping)
                    {
                        ArrayMapping? mapping = m as ArrayMapping;
                        if (mapping == null) continue;
                        ilg.InitElseIf();
                        if (mapping.TypeDesc!.IsArray)
                            WriteArrayTypeCompare("t", mapping.TypeDesc.Type!);
                        else
                            WriteTypeCompare("t", mapping.TypeDesc.Type!);
                        // WriteXXXTypeCompare leave bool on the stack
                        ilg.AndIf();
                        ilg.EnterScope();
 
                        MethodInfo XmlSerializationWriter_get_Writer = typeof(XmlSerializationWriter).GetMethod(
                            "get_Writer",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        MethodInfo XmlWriter_WriteStartElement = typeof(XmlWriter).GetMethod(
                            "WriteStartElement",
                            CodeGenerator.InstanceBindingFlags,
                            new Type[] { typeof(string), typeof(string) }
                            )!;
                        ilg.Ldarg(0);
                        ilg.Call(XmlSerializationWriter_get_Writer);
                        ilg.Ldarg("n");
                        ilg.Ldarg("ns");
                        ilg.Call(XmlWriter_WriteStartElement);
                        MethodInfo XmlSerializationWriter_WriteXsiType = typeof(XmlSerializationWriter).GetMethod(
                            "WriteXsiType",
                            CodeGenerator.InstanceBindingFlags,
                            new Type[] { typeof(string), typeof(string) }
                            )!;
                        ilg.Ldarg(0);
                        ilg.Ldstr(GetCSharpString(mapping.TypeName));
                        ilg.Ldstr(GetCSharpString(mapping.Namespace));
                        ilg.Call(XmlSerializationWriter_WriteXsiType);
 
                        WriteMember(new SourceInfo("o", "o", null, null, ilg), null, mapping.ElementsSortedByDerivation!, null, null, mapping.TypeDesc, true);
 
                        MethodInfo XmlWriter_WriteEndElement = typeof(XmlWriter).GetMethod(
                            "WriteEndElement",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        ilg.Ldarg(0);
                        ilg.Call(XmlSerializationWriter_get_Writer);
                        ilg.Call(XmlWriter_WriteEndElement);
                        ilg.GotoMethodEnd();
                        ilg.ExitScope();
                    }
                }
            }
        }
 
        private void WriteStructMethod(StructMapping mapping)
        {
            string? methodName;
            MethodNames.TryGetValue(mapping, out methodName);
 
            ilg = new CodeGenerator(this.typeBuilder);
            List<Type> argTypes = new List<Type>(5);
            List<string> argNames = new List<string>(5);
            argTypes.Add(typeof(string));
            argNames.Add("n");
            argTypes.Add(typeof(string));
            argNames.Add("ns");
            argTypes.Add(mapping.TypeDesc!.Type!);
            argNames.Add("o");
            if (mapping.TypeDesc.IsNullable)
            {
                argTypes.Add(typeof(bool));
                argNames.Add("isNullable");
            }
            argTypes.Add(typeof(bool));
            argNames.Add("needType");
            ilg.BeginMethod(typeof(void),
                GetMethodBuilder(methodName!),
                argTypes.ToArray(),
                argNames.ToArray(),
                CodeGenerator.PrivateMethodAttributes);
            if (mapping.TypeDesc.IsNullable)
            {
                ilg.If(ilg.GetArg("o"), Cmp.EqualTo, null);
                {
                    ilg.If(ilg.GetArg("isNullable"), Cmp.EqualTo, true);
                    {
                        MethodInfo XmlSerializationWriter_WriteNullTagLiteral = typeof(XmlSerializationWriter).GetMethod(
                                "WriteNullTagLiteral",
                                CodeGenerator.InstanceBindingFlags,
                                new Type[] { typeof(string), typeof(string) }
                                )!;
                        ilg.Ldarg(0);
                        ilg.Ldarg("n");
                        ilg.Ldarg("ns");
                        ilg.Call(XmlSerializationWriter_WriteNullTagLiteral);
                    }
                    ilg.EndIf();
                    ilg.GotoMethodEnd();
                }
                ilg.EndIf();
            }
            ilg.If(ilg.GetArg("needType"), Cmp.NotEqualTo, true); // if (!needType)
 
            LocalBuilder tLoc = ilg.DeclareLocal(typeof(Type), "t");
            MethodInfo Object_GetType = typeof(object).GetMethod(
                    "GetType",
                    CodeGenerator.InstanceBindingFlags,
                    Type.EmptyTypes
                    )!;
            ArgBuilder oArg = ilg.GetArg("o");
            ilg.LdargAddress(oArg);
            ilg.ConvertAddress(oArg.ArgType, typeof(object));
            ilg.Call(Object_GetType);
            ilg.Stloc(tLoc);
            WriteTypeCompare("t", mapping.TypeDesc.Type!);
            // Bool on the stack from WriteTypeCompare.
            ilg.If(); // if (t == typeof(...))
            WriteDerivedTypes(mapping);
            if (mapping.TypeDesc.IsRoot)
                WriteEnumAndArrayTypes();
            ilg.Else();
            if (mapping.TypeDesc.IsRoot)
            {
                MethodInfo XmlSerializationWriter_WriteTypedPrimitive = typeof(XmlSerializationWriter).GetMethod(
                        "WriteTypedPrimitive",
                        CodeGenerator.InstanceBindingFlags,
                        new Type[] { typeof(string), typeof(string), typeof(object), typeof(bool) }
                        )!;
                ilg.Ldarg(0);
                ilg.Ldarg("n");
                ilg.Ldarg("ns");
                ilg.Ldarg("o");
                ilg.Ldc(true);
                ilg.Call(XmlSerializationWriter_WriteTypedPrimitive);
                ilg.GotoMethodEnd();
            }
            else
            {
                MethodInfo XmlSerializationWriter_CreateUnknownTypeException = typeof(XmlSerializationWriter).GetMethod(
                    "CreateUnknownTypeException",
                    CodeGenerator.InstanceBindingFlags,
                    s_objectType
                    )!;
                ilg.Ldarg(0);
                ilg.Ldarg(oArg);
                ilg.ConvertValue(oArg.ArgType, typeof(object));
                ilg.Call(XmlSerializationWriter_CreateUnknownTypeException);
                ilg.Throw();
            }
            ilg.EndIf(); // if (t == typeof(...))
            ilg.EndIf(); // if (!needType)
 
            if (!mapping.TypeDesc.IsAbstract)
            {
                if (mapping.TypeDesc.Type != null && typeof(XmlSchemaObject).IsAssignableFrom(mapping.TypeDesc.Type))
                {
                    MethodInfo XmlSerializationWriter_set_EscapeName = typeof(XmlSerializationWriter).GetMethod(
                        "set_EscapeName",
                        CodeGenerator.InstanceBindingFlags,
                        new Type[] { typeof(bool) }
                        )!;
                    ilg.Ldarg(0);
                    ilg.Ldc(false);
                    ilg.Call(XmlSerializationWriter_set_EscapeName);
                }
 
                string? xmlnsSource = null;
                MemberMapping[] members = TypeScope.GetAllMembers(mapping, memberInfos);
                int xmlnsMember = FindXmlnsIndex(members);
                if (xmlnsMember >= 0)
                {
                    MemberMapping member = members[xmlnsMember];
                    CodeIdentifier.CheckValidIdentifier(member.Name);
                    xmlnsSource = ReflectionAwareILGen.GetStringForMember("o", member.Name);
                }
 
                ilg.Ldarg(0);
                ilg.Ldarg("n");
                ilg.Ldarg("ns");
                ArgBuilder argO = ilg.GetArg("o");
                ilg.Ldarg(argO);
                ilg.ConvertValue(argO.ArgType, typeof(object));
                ilg.Ldc(false);
                if (xmlnsSource == null)
                    ilg.Load(null);
                else
                {
                    System.Diagnostics.Debug.Assert(xmlnsSource.StartsWith("o.@", StringComparison.Ordinal));
                    ILGenLoad(xmlnsSource);
                }
 
                MethodInfo XmlSerializationWriter_WriteStartElement = typeof(XmlSerializationWriter).GetMethod(
                        "WriteStartElement",
                        CodeGenerator.InstanceBindingFlags,
                        new Type[] { typeof(string), typeof(string), typeof(object), typeof(bool), typeof(XmlSerializerNamespaces) }
                        )!;
                ilg.Call(XmlSerializationWriter_WriteStartElement);
                if (!mapping.TypeDesc.IsRoot)
                {
                    ilg.If(ilg.GetArg("needType"), Cmp.EqualTo, true);
                    {
                        MethodInfo XmlSerializationWriter_WriteXsiType = typeof(XmlSerializationWriter).GetMethod(
                                "WriteXsiType",
                                CodeGenerator.InstanceBindingFlags,
                                new Type[] { typeof(string), typeof(string) }
                                )!;
                        ilg.Ldarg(0);
                        ilg.Ldstr(GetCSharpString(mapping.TypeName));
                        ilg.Ldstr(GetCSharpString(mapping.Namespace));
                        ilg.Call(XmlSerializationWriter_WriteXsiType);
                    }
                    ilg.EndIf();
                }
                for (int i = 0; i < members.Length; i++)
                {
                    MemberMapping m = members[i];
                    if (m.Attribute != null)
                    {
                        CodeIdentifier.CheckValidIdentifier(m.Name);
                        if (m.CheckShouldPersist)
                        {
                            ilg.LdargAddress(oArg);
                            ilg.Call(m.CheckShouldPersistMethodInfo!);
                            ilg.If();
                        }
                        if (m.CheckSpecified != SpecifiedAccessor.None)
                        {
                            string memberGet = ReflectionAwareILGen.GetStringForMember("o", $"{m.Name}Specified");
                            ILGenLoad(memberGet);
                            ilg.If();
                        }
                        WriteMember(ReflectionAwareILGen.GetSourceForMember("o", m, ilg), m.Attribute, m.TypeDesc!, "o");
 
                        if (m.CheckSpecified != SpecifiedAccessor.None)
                        {
                            ilg.EndIf();
                        }
                        if (m.CheckShouldPersist)
                        {
                            ilg.EndIf();
                        }
                    }
                }
 
                for (int i = 0; i < members.Length; i++)
                {
                    MemberMapping m = members[i];
                    if (m.Xmlns != null)
                        continue;
                    CodeIdentifier.CheckValidIdentifier(m.Name);
                    bool checkShouldPersist = m.CheckShouldPersist && (m.Elements!.Length > 0 || m.Text != null);
 
                    if (checkShouldPersist)
                    {
                        ilg.LdargAddress(oArg);
                        ilg.Call(m.CheckShouldPersistMethodInfo!);
                        ilg.If();
                    }
                    if (m.CheckSpecified != SpecifiedAccessor.None)
                    {
                        string memberGet = ReflectionAwareILGen.GetStringForMember("o", $"{m.Name}Specified");
                        ILGenLoad(memberGet);
                        ilg.If();
                    }
 
                    string? choiceSource = null;
                    if (m.ChoiceIdentifier != null)
                    {
                        CodeIdentifier.CheckValidIdentifier(m.ChoiceIdentifier.MemberName);
                        choiceSource = ReflectionAwareILGen.GetStringForMember("o", m.ChoiceIdentifier.MemberName);
                    }
 
                    WriteMember(ReflectionAwareILGen.GetSourceForMember("o", m, m.MemberInfo, ilg), choiceSource, m.ElementsSortedByDerivation!, m.Text, m.ChoiceIdentifier, m.TypeDesc!, true);
 
                    if (m.CheckSpecified != SpecifiedAccessor.None)
                    {
                        ilg.EndIf();
                    }
                    if (checkShouldPersist)
                    {
                        ilg.EndIf();
                    }
                }
                WriteEndElement("o");
            }
            ilg.EndMethod();
        }
 
        private bool CanOptimizeWriteListSequence(TypeDesc? listElementTypeDesc)
        {
            // check to see if we can write values of the attribute sequentially
            // currently we have only one data type (XmlQualifiedName) that we can not write "inline",
            // because we need to output xmlns:qx="..." for each of the qnames
 
            return (listElementTypeDesc != null && listElementTypeDesc != QnameTypeDesc);
        }
 
        private void WriteMember(SourceInfo source, AttributeAccessor attribute, TypeDesc memberTypeDesc, string parent)
        {
            if (memberTypeDesc.IsAbstract) return;
            if (memberTypeDesc.IsArrayLike)
            {
                string aVar = $"a{memberTypeDesc.Name}";
                string aiVar = $"ai{memberTypeDesc.Name}";
                string iVar = "i";
                string fullTypeName = memberTypeDesc.CSharpName;
                ilg.EnterScope();
                WriteArrayLocalDecl(fullTypeName, aVar, source, memberTypeDesc);
                if (memberTypeDesc.IsNullable)
                {
                    ilg.Ldloc(memberTypeDesc.Type!, aVar);
                    ilg.Load(null);
                    ilg.If(Cmp.NotEqualTo);
                }
                if (attribute.IsList)
                {
                    if (CanOptimizeWriteListSequence(memberTypeDesc.ArrayElementTypeDesc))
                    {
                        string? ns = attribute.Form == XmlSchemaForm.Qualified ? attribute.Namespace : string.Empty;
                        MethodInfo XmlSerializationWriter_get_Writer = typeof(XmlSerializationWriter).GetMethod(
                            "get_Writer",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        MethodInfo XmlWriter_WriteStartAttribute = typeof(XmlWriter).GetMethod(
                            "WriteStartAttribute",
                            CodeGenerator.InstanceBindingFlags,
                            new Type[] { typeof(string), typeof(string), typeof(string) }
                            )!;
                        ilg.Ldarg(0);
                        ilg.Call(XmlSerializationWriter_get_Writer);
                        ilg.Load(null);
                        ilg.Ldstr(GetCSharpString(attribute.Name));
                        ilg.Ldstr(GetCSharpString(ns));
                        ilg.Call(XmlWriter_WriteStartAttribute);
                    }
                    else
                    {
                        LocalBuilder sbLoc = ilg.DeclareOrGetLocal(typeof(StringBuilder), "sb");
                        ConstructorInfo StringBuilder_ctor = typeof(StringBuilder).GetConstructor(
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        ilg.New(StringBuilder_ctor);
                        ilg.Stloc(sbLoc);
                    }
                }
                TypeDesc arrayElementTypeDesc = memberTypeDesc.ArrayElementTypeDesc!;
 
                if (memberTypeDesc.IsEnumerable)
                {
                    throw Globals.NotSupported("Also fail in IEnumerable member with XmlAttributeAttribute");
                }
                else
                {
                    LocalBuilder localI = ilg.DeclareOrGetLocal(typeof(int), iVar);
                    ilg.For(localI, 0, ilg.GetLocal(aVar));
                    WriteLocalDecl(aiVar, ReflectionAwareILGen.GetStringForArrayMember(aVar, iVar), arrayElementTypeDesc.Type!);
                }
                if (attribute.IsList)
                {
                    string methodName;
                    Type methodType;
                    Type argType;
                    // check to see if we can write values of the attribute sequentially
                    if (CanOptimizeWriteListSequence(memberTypeDesc.ArrayElementTypeDesc))
                    {
                        ilg.Ldloc(iVar);
                        ilg.Ldc(0);
                        ilg.If(Cmp.NotEqualTo);
                        MethodInfo XmlSerializationWriter_get_Writer = typeof(XmlSerializationWriter).GetMethod(
                            "get_Writer",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        MethodInfo XmlWriter_WriteString = typeof(XmlWriter).GetMethod(
                            "WriteString",
                            CodeGenerator.InstanceBindingFlags,
                            new Type[] { typeof(string) }
                            )!;
                        ilg.Ldarg(0);
                        ilg.Call(XmlSerializationWriter_get_Writer);
                        ilg.Ldstr(" ");
                        ilg.Call(XmlWriter_WriteString);
                        ilg.EndIf();
                        ilg.Ldarg(0);
                        methodName = "WriteValue";
                        methodType = typeof(XmlSerializationWriter);
                    }
                    else
                    {
                        MethodInfo StringBuilder_Append = typeof(StringBuilder).GetMethod(
                            "Append",
                            CodeGenerator.InstanceBindingFlags,
                            new Type[] { typeof(string) }
                            )!;
                        ilg.Ldloc(iVar);
                        ilg.Ldc(0);
                        ilg.If(Cmp.NotEqualTo);
                        ilg.Ldloc("sb");
                        ilg.Ldstr(" ");
                        ilg.Call(StringBuilder_Append);
                        ilg.Pop();
                        ilg.EndIf();
                        ilg.Ldloc("sb");
                        methodName = "Append";
                        methodType = typeof(StringBuilder);
                    }
                    if (attribute.Mapping is EnumMapping)
                        WriteEnumValue((EnumMapping)attribute.Mapping, new SourceInfo(aiVar, aiVar, null, arrayElementTypeDesc.Type, ilg), out argType);
                    else
                        WritePrimitiveValue(arrayElementTypeDesc, new SourceInfo(aiVar, aiVar, null, arrayElementTypeDesc.Type, ilg), out argType);
                    MethodInfo method = methodType.GetMethod(
                        methodName,
                        CodeGenerator.InstanceBindingFlags,
                        new Type[] { argType }
                        )!;
                    ilg.Call(method);
                    if (method.ReturnType != typeof(void))
                        ilg.Pop();
                }
                else
                {
                    WriteAttribute(new SourceInfo(aiVar, aiVar, null, null, ilg), attribute, parent);
                }
                if (memberTypeDesc.IsEnumerable)
                    throw Globals.NotSupported("Also fail in whidbey IEnumerable member with XmlAttributeAttribute");
                else
                    ilg.EndFor();
                if (attribute.IsList)
                {
                    // check to see if we can write values of the attribute sequentially
                    if (CanOptimizeWriteListSequence(memberTypeDesc.ArrayElementTypeDesc))
                    {
                        MethodInfo XmlSerializationWriter_get_Writer = typeof(XmlSerializationWriter).GetMethod(
                            "get_Writer",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        MethodInfo XmlWriter_WriteEndAttribute = typeof(XmlWriter).GetMethod(
                            "WriteEndAttribute",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        ilg.Ldarg(0);
                        ilg.Call(XmlSerializationWriter_get_Writer);
                        ilg.Call(XmlWriter_WriteEndAttribute);
                    }
                    else
                    {
                        MethodInfo StringBuilder_get_Length = typeof(StringBuilder).GetMethod(
                            "get_Length",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        ilg.Ldloc("sb");
                        ilg.Call(StringBuilder_get_Length);
                        ilg.Ldc(0);
                        ilg.If(Cmp.NotEqualTo);
 
                        List<Type> argTypes = new List<Type>();
                        ilg.Ldarg(0);
                        ilg.Ldstr(GetCSharpString(attribute.Name));
                        argTypes.Add(typeof(string));
                        string? ns = attribute.Form == XmlSchemaForm.Qualified ? attribute.Namespace : string.Empty;
                        if (ns != null)
                        {
                            ilg.Ldstr(GetCSharpString(ns));
                            argTypes.Add(typeof(string));
                        }
                        MethodInfo Object_ToString = typeof(object).GetMethod(
                            "ToString",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        ilg.Ldloc("sb");
                        ilg.Call(Object_ToString);
                        argTypes.Add(typeof(string));
                        MethodInfo XmlSerializationWriter_WriteAttribute = typeof(XmlSerializationWriter).GetMethod(
                             "WriteAttribute",
                             CodeGenerator.InstanceBindingFlags,
                             argTypes.ToArray()
                             )!;
                        ilg.Call(XmlSerializationWriter_WriteAttribute);
                        ilg.EndIf();
                    }
                }
 
                if (memberTypeDesc.IsNullable)
                {
                    ilg.EndIf();
                }
 
                ilg.ExitScope();
            }
            else
            {
                WriteAttribute(source, attribute, parent);
            }
        }
 
        private void WriteAttribute(SourceInfo source, AttributeAccessor attribute, string parent)
        {
            if (attribute.Mapping is SpecialMapping special)
            {
                if (special.TypeDesc!.Kind == TypeKind.Attribute || special.TypeDesc.CanBeAttributeValue)
                {
                    System.Diagnostics.Debug.Assert(parent == "o" || parent == "p");
                    MethodInfo XmlSerializationWriter_WriteXmlAttribute = typeof(XmlSerializationWriter).GetMethod(
                        "WriteXmlAttribute",
                        CodeGenerator.InstanceBindingFlags,
                        new Type[] { typeof(XmlNode), typeof(object) }
                        )!;
                    ilg.Ldarg(0);
                    ilg.Ldloc(source.Source);
                    ilg.Ldarg(parent);
                    ilg.ConvertValue(ilg.GetArg(parent).ArgType, typeof(object));
                    ilg.Call(XmlSerializationWriter_WriteXmlAttribute);
                }
                else
                    throw new InvalidOperationException(SR.XmlInternalError);
            }
            else
            {
                TypeDesc typeDesc = attribute.Mapping!.TypeDesc!;
                source = source.CastTo(typeDesc);
                WritePrimitive("WriteAttribute", attribute.Name, attribute.Form == XmlSchemaForm.Qualified ? attribute.Namespace : "", GetConvertedDefaultValue(source.Type, attribute.Default), source, attribute.Mapping, false, false, false);
            }
        }
 
        private static object? GetConvertedDefaultValue(
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
            Type? targetType,
            object? rawDefaultValue)
        {
            if (targetType == null)
            {
                return rawDefaultValue;
            }
 
            object? convertedDefaultValue;
            if (!targetType.TryConvertTo(rawDefaultValue, out convertedDefaultValue))
            {
                return rawDefaultValue;
            }
 
            return convertedDefaultValue;
        }
 
        private void WriteMember(SourceInfo source, string? choiceSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc memberTypeDesc, bool writeAccessors)
        {
            if (memberTypeDesc.IsArrayLike &&
                !(elements.Length == 1 && elements[0].Mapping is ArrayMapping))
                WriteArray(source, choiceSource, elements, text, choice, memberTypeDesc);
            else
                // NOTE: Use different variable name to work around reuse same variable name in different scope
                WriteElements(source, choiceSource, elements, text, choice, $"a{memberTypeDesc.Name}", writeAccessors, memberTypeDesc.IsNullable);
        }
 
        private void WriteArray(SourceInfo source, string? choiceSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc arrayTypeDesc)
        {
            if (elements.Length == 0 && text == null) return;
            string arrayTypeName = arrayTypeDesc.CSharpName;
            string aName = $"a{arrayTypeDesc.Name}";
            ilg.EnterScope();
            WriteArrayLocalDecl(arrayTypeName, aName, source, arrayTypeDesc);
            LocalBuilder aLoc = ilg.GetLocal(aName);
            if (arrayTypeDesc.IsNullable)
            {
                ilg.Ldloc(aLoc);
                ilg.Load(null);
                ilg.If(Cmp.NotEqualTo);
            }
 
            string? cName = null;
            if (choice != null)
            {
                string choiceFullName = choice.Mapping!.TypeDesc!.CSharpName;
                SourceInfo choiceSourceInfo = new SourceInfo(choiceSource!, null, choice.MemberInfo, null, ilg);
                cName = $"c{choice.Mapping.TypeDesc.Name}";
                WriteArrayLocalDecl($"{choiceFullName}[]", cName, choiceSourceInfo, choice.Mapping.TypeDesc);
                // write check for the choice identifier array
                Label labelEnd = ilg.DefineLabel();
                Label labelTrue = ilg.DefineLabel();
                LocalBuilder cLoc = ilg.GetLocal(cName);
                ilg.Ldloc(cLoc);
                ilg.Load(null);
                ilg.Beq(labelTrue);
                ilg.Ldloc(cLoc);
                ilg.Ldlen();
                ilg.Ldloc(aLoc);
                ilg.Ldlen();
                ilg.Clt();
                ilg.Br(labelEnd);
                ilg.MarkLabel(labelTrue);
                ilg.Ldc(true);
                ilg.MarkLabel(labelEnd);
                ilg.If();
                MethodInfo XmlSerializationWriter_CreateInvalidChoiceIdentifierValueException = typeof(XmlSerializationWriter).GetMethod(
                    "CreateInvalidChoiceIdentifierValueException",
                    CodeGenerator.InstanceBindingFlags,
                    new Type[] { typeof(string), typeof(string) }
                    )!;
                ilg.Ldarg(0);
                ilg.Ldstr(GetCSharpString(choice.Mapping.TypeDesc.FullName));
                ilg.Ldstr(GetCSharpString(choice.MemberName));
                ilg.Call(XmlSerializationWriter_CreateInvalidChoiceIdentifierValueException);
                ilg.Throw();
                ilg.EndIf();
            }
 
            WriteArrayItems(elements, text, choice, arrayTypeDesc, aName, cName!);
            if (arrayTypeDesc.IsNullable)
            {
                ilg.EndIf();
            }
 
            ilg.ExitScope();
        }
 
        private void WriteArrayItems(ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc arrayTypeDesc, string arrayName, string? choiceName)
        {
            TypeDesc arrayElementTypeDesc = arrayTypeDesc.ArrayElementTypeDesc!;
 
            if (arrayTypeDesc.IsEnumerable)
            {
                LocalBuilder eLoc = ilg.DeclareLocal(typeof(IEnumerator), "e");
                ilg.LoadAddress(ilg.GetVariable(arrayName));
                MethodInfo getEnumeratorMethod;
 
                if (arrayTypeDesc.IsPrivateImplementation)
                {
                    Type typeIEnumerable = typeof(IEnumerable);
 
                    getEnumeratorMethod = typeIEnumerable.GetMethod(
                        "GetEnumerator",
                        CodeGenerator.InstanceBindingFlags,
                        Type.EmptyTypes)!;
 
                    ilg.ConvertValue(arrayTypeDesc.Type!, typeIEnumerable);
                }
                else if (arrayTypeDesc.IsGenericInterface)
                {
                    Type typeIEnumerable = typeof(IEnumerable<>).MakeGenericType(arrayElementTypeDesc.Type!);
 
                    getEnumeratorMethod = typeIEnumerable.GetMethod(
                        "GetEnumerator",
                        CodeGenerator.InstanceBindingFlags,
                        Type.EmptyTypes)!;
 
                    ilg.ConvertValue(arrayTypeDesc.Type!, typeIEnumerable);
                }
                else
                {
                    getEnumeratorMethod = arrayTypeDesc.Type!.GetMethod(
                        "GetEnumerator",
                        Type.EmptyTypes)!;
                }
                ilg.Call(getEnumeratorMethod);
                ilg.ConvertValue(getEnumeratorMethod.ReturnType, typeof(IEnumerator));
                ilg.Stloc(eLoc);
 
                ilg.Ldloc(eLoc);
                ilg.Load(null);
                ilg.If(Cmp.NotEqualTo);
                ilg.WhileBegin();
                string arrayNamePlusA = $"{(arrayName).Replace(arrayTypeDesc.Name, "")}a{arrayElementTypeDesc.Name}";
                string arrayNamePlusI = $"{(arrayName).Replace(arrayTypeDesc.Name, "")}i{arrayElementTypeDesc.Name}";
                WriteLocalDecl(arrayNamePlusI, "e.Current", arrayElementTypeDesc.Type!);
                WriteElements(new SourceInfo(arrayNamePlusI, null, null, arrayElementTypeDesc.Type, ilg), $"{choiceName}i", elements, text, choice, arrayNamePlusA, true, true);
 
                ilg.WhileBeginCondition(); // while (e.MoveNext())
                MethodInfo IEnumerator_MoveNext = typeof(IEnumerator).GetMethod(
                    "MoveNext",
                    CodeGenerator.InstanceBindingFlags,
                    Type.EmptyTypes)!;
                ilg.Ldloc(eLoc);
                ilg.Call(IEnumerator_MoveNext);
                ilg.WhileEndCondition();
                ilg.WhileEnd();
 
                ilg.EndIf(); // if (e != null)
            }
            else
            {
                // Filter out type specific for index (code match reusing local).
                string iPlusArrayName = $"i{(arrayName).Replace(arrayTypeDesc.Name, "")}";
                string arrayNamePlusA = $"{(arrayName).Replace(arrayTypeDesc.Name, "")}a{arrayElementTypeDesc.Name}";
                string arrayNamePlusI = $"{(arrayName).Replace(arrayTypeDesc.Name, "")}i{arrayElementTypeDesc.Name}";
                LocalBuilder localI = ilg.DeclareOrGetLocal(typeof(int), iPlusArrayName);
                ilg.For(localI, 0, ilg.GetLocal(arrayName));
                int count = elements.Length + (text == null ? 0 : 1);
                if (count > 1)
                {
                    WriteLocalDecl(arrayNamePlusI, ReflectionAwareILGen.GetStringForArrayMember(arrayName, iPlusArrayName), arrayElementTypeDesc.Type!);
                    if (choice != null)
                    {
                        WriteLocalDecl($"{choiceName}i", ReflectionAwareILGen.GetStringForArrayMember(choiceName, iPlusArrayName), choice.Mapping!.TypeDesc!.Type!);
                    }
                    WriteElements(new SourceInfo(arrayNamePlusI, null, null, arrayElementTypeDesc.Type, ilg), $"{choiceName}i", elements, text, choice, arrayNamePlusA, true, arrayElementTypeDesc.IsNullable);
                }
                else
                {
                    WriteElements(new SourceInfo(ReflectionAwareILGen.GetStringForArrayMember(arrayName, iPlusArrayName), null, null, arrayElementTypeDesc.Type, ilg), null, elements, text, choice, arrayNamePlusA, true, arrayElementTypeDesc.IsNullable);
                }
                ilg.EndFor();
            }
        }
 
        private void WriteElements(SourceInfo source, string? enumSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, string arrayName, bool writeAccessors, bool isNullable)
        {
            if (elements.Length == 0 && text == null) return;
            if (elements.Length == 1 && text == null)
            {
                TypeDesc td = elements[0].IsUnbounded ? elements[0].Mapping!.TypeDesc!.CreateArrayTypeDesc() : elements[0].Mapping!.TypeDesc!;
                if (!elements[0].Any && !elements[0].Mapping!.TypeDesc!.IsOptionalValue)
                    source = source.CastTo(td);
                WriteElement(source, elements[0], arrayName, writeAccessors);
            }
            else
            {
                bool doEndIf = false;
                if (isNullable && choice == null)
                {
                    source.Load(typeof(object));
                    ilg.Load(null);
                    ilg.If(Cmp.NotEqualTo);
                    doEndIf = true;
                }
                int anyCount = 0;
                var namedAnys = new List<ElementAccessor>();
                ElementAccessor? unnamedAny = null; // can only have one
                bool wroteFirstIf = false;
                string? enumTypeName = choice == null ? null : choice.Mapping!.TypeDesc!.FullName;
 
                for (int i = 0; i < elements.Length; i++)
                {
                    ElementAccessor element = elements[i];
 
                    if (element.Any)
                    {
                        anyCount++;
                        if (element.Name != null && element.Name.Length > 0)
                        {
                            namedAnys.Add(element);
                        }
                        else
                        {
                            unnamedAny ??= element;
                        }
                    }
                    else if (choice != null)
                    {
                        object? enumValue;
                        string enumFullName = $"{enumTypeName}.@{FindChoiceEnumValue(element, (EnumMapping)choice.Mapping!, out enumValue)}";
 
                        if (wroteFirstIf) ilg.InitElseIf();
                        else { wroteFirstIf = true; ilg.InitIf(); }
                        ILGenLoad(enumSource!, choice == null ? null : choice.Mapping!.TypeDesc!.Type);
                        ilg.Load(enumValue);
                        ilg.Ceq();
                        if (isNullable && !element.IsNullable)
                        {
                            Label labelFalse = ilg.DefineLabel();
                            Label labelEnd = ilg.DefineLabel();
                            ilg.Brfalse(labelFalse);
                            source.Load(typeof(object));
                            ilg.Load(null);
                            ilg.Cne();
                            ilg.Br_S(labelEnd);
                            ilg.MarkLabel(labelFalse);
                            ilg.Ldc(false);
                            ilg.MarkLabel(labelEnd);
                        }
                        ilg.AndIf();
 
                        WriteChoiceTypeCheck(source, choice!, enumFullName, element.Mapping!.TypeDesc!);
 
                        SourceInfo castedSource = source.CastTo(element.Mapping.TypeDesc!);
                        WriteElement(element.Any ? source : castedSource, element, arrayName, writeAccessors);
                    }
                    else
                    {
                        TypeDesc td = element.IsUnbounded ? element.Mapping!.TypeDesc!.CreateArrayTypeDesc() : element.Mapping!.TypeDesc!;
                        if (wroteFirstIf) ilg.InitElseIf();
                        else { wroteFirstIf = true; ilg.InitIf(); }
                        WriteInstanceOf(source, td.Type!);
                        // WriteInstanceOf leave bool on the stack
                        ilg.AndIf();
                        SourceInfo castedSource = source.CastTo(td);
                        WriteElement(element.Any ? source : castedSource, element, arrayName, writeAccessors);
                    }
                }
                if (wroteFirstIf)
                {
                    if (anyCount > 0)
                    {
                        // See "else " below
                        if (elements.Length - anyCount > 0)
                        { // NOOP
                        }
                        else ilg.EndIf();
                    }
                }
                if (anyCount > 0)
                {
                    if (elements.Length - anyCount > 0) ilg.InitElseIf();
                    else ilg.InitIf();
 
                    source.Load(typeof(object));
                    ilg.IsInst(typeof(XmlElement));
                    ilg.Load(null);
                    ilg.Cne();
                    ilg.AndIf();
 
                    LocalBuilder elemLoc = ilg.DeclareLocal(typeof(XmlElement), "elem");
                    source.Load(typeof(XmlElement));
                    ilg.Stloc(elemLoc);
 
                    int c = 0;
 
                    foreach (ElementAccessor element in namedAnys)
                    {
                        if (c++ > 0) ilg.InitElseIf();
                        else ilg.InitIf();
 
                        string? enumFullName = null;
 
                        Label labelEnd, labelFalse;
                        if (choice != null)
                        {
                            object? enumValue;
                            enumFullName = $"{enumTypeName}.@{FindChoiceEnumValue(element, (EnumMapping)choice.Mapping!, out enumValue)}";
                            labelFalse = ilg.DefineLabel();
                            labelEnd = ilg.DefineLabel();
                            ILGenLoad(enumSource!, choice == null ? null : choice.Mapping!.TypeDesc!.Type);
                            ilg.Load(enumValue);
                            ilg.Bne(labelFalse);
                            if (isNullable && !element.IsNullable)
                            {
                                source.Load(typeof(object));
                                ilg.Load(null);
                                ilg.Cne();
                            }
                            else
                            {
                                ilg.Ldc(true);
                            }
                            ilg.Br(labelEnd);
                            ilg.MarkLabel(labelFalse);
                            ilg.Ldc(false);
                            ilg.MarkLabel(labelEnd);
                            ilg.AndIf();
                        }
                        labelFalse = ilg.DefineLabel();
                        labelEnd = ilg.DefineLabel();
                        MethodInfo XmlNode_get_Name = typeof(XmlNode).GetMethod(
                            "get_Name",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        MethodInfo XmlNode_get_NamespaceURI = typeof(XmlNode).GetMethod(
                            "get_NamespaceURI",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        ilg.Ldloc(elemLoc);
                        ilg.Call(XmlNode_get_Name);
                        ilg.Ldstr(GetCSharpString(element.Name));
                        MethodInfo String_op_Equality = typeof(string).GetMethod(
                            "op_Equality",
                            CodeGenerator.StaticBindingFlags,
                            new Type[] { typeof(string), typeof(string) }
                            )!;
                        ilg.Call(String_op_Equality);
                        ilg.Brfalse(labelFalse);
                        ilg.Ldloc(elemLoc);
                        ilg.Call(XmlNode_get_NamespaceURI);
                        ilg.Ldstr(GetCSharpString(element.Namespace));
                        ilg.Call(String_op_Equality);
                        ilg.Br(labelEnd);
                        ilg.MarkLabel(labelFalse);
                        ilg.Ldc(false);
                        ilg.MarkLabel(labelEnd);
                        if (choice != null) ilg.If();
                        else ilg.AndIf();
                        WriteElement(new SourceInfo("elem", null, null, elemLoc.LocalType, ilg), element, arrayName, writeAccessors);
 
                        if (choice != null)
                        {
                            ilg.Else();
 
                            MethodInfo XmlSerializationWriter_CreateChoiceIdentifierValueException = typeof(XmlSerializationWriter).GetMethod(
                                "CreateChoiceIdentifierValueException",
                                CodeGenerator.InstanceBindingFlags,
                                new Type[] { typeof(string), typeof(string), typeof(string), typeof(string) }
                                )!;
                            ilg.Ldarg(0);
                            ilg.Ldstr(GetCSharpString(enumFullName));
                            ilg.Ldstr(GetCSharpString(choice.MemberName));
                            ilg.Ldloc(elemLoc);
                            ilg.Call(XmlNode_get_Name);
                            ilg.Ldloc(elemLoc);
                            ilg.Call(XmlNode_get_NamespaceURI);
                            ilg.Call(XmlSerializationWriter_CreateChoiceIdentifierValueException);
                            ilg.Throw();
                            ilg.EndIf();
                        }
                    }
                    if (c > 0)
                    {
                        ilg.Else();
                    }
                    if (unnamedAny != null)
                    {
                        WriteElement(new SourceInfo("elem", null, null, elemLoc.LocalType, ilg), unnamedAny, arrayName, writeAccessors);
                    }
                    else
                    {
                        MethodInfo XmlSerializationWriter_CreateUnknownAnyElementException = typeof(XmlSerializationWriter).GetMethod(
                            "CreateUnknownAnyElementException",
                            CodeGenerator.InstanceBindingFlags,
                            new Type[] { typeof(string), typeof(string) }
                            )!;
                        ilg.Ldarg(0);
                        ilg.Ldloc(elemLoc);
                        MethodInfo XmlNode_get_Name = typeof(XmlNode).GetMethod(
                            "get_Name",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        MethodInfo XmlNode_get_NamespaceURI = typeof(XmlNode).GetMethod(
                            "get_NamespaceURI",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        ilg.Call(XmlNode_get_Name);
                        ilg.Ldloc(elemLoc);
                        ilg.Call(XmlNode_get_NamespaceURI);
                        ilg.Call(XmlSerializationWriter_CreateUnknownAnyElementException);
                        ilg.Throw();
                    }
                    if (c > 0)
                    {
                        ilg.EndIf();
                    }
                }
                if (text != null)
                {
                    if (elements.Length > 0)
                    {
                        ilg.InitElseIf();
                        WriteInstanceOf(source, text.Mapping!.TypeDesc!.Type!);
                        ilg.AndIf();
                        SourceInfo castedSource = source.CastTo(text.Mapping.TypeDesc);
                        WriteText(castedSource, text);
                    }
                    else
                    {
                        SourceInfo castedSource = source.CastTo(text.Mapping!.TypeDesc!);
                        WriteText(castedSource, text);
                    }
                }
                if (elements.Length > 0)
                {
                    if (isNullable)
                    {
                        ilg.InitElseIf();
                        source.Load(null);
                        ilg.Load(null);
                        ilg.AndIf(Cmp.NotEqualTo);
                    }
                    else
                    {
                        ilg.Else();
                    }
 
                    MethodInfo XmlSerializationWriter_CreateUnknownTypeException = typeof(XmlSerializationWriter).GetMethod(
                        "CreateUnknownTypeException",
                        CodeGenerator.InstanceBindingFlags,
                        s_objectType)!;
                    ilg.Ldarg(0);
                    source.Load(typeof(object));
                    ilg.Call(XmlSerializationWriter_CreateUnknownTypeException);
                    ilg.Throw();
 
                    ilg.EndIf();
                }
                // See ilg.If() cond above
                if (doEndIf) // if (isNullable && choice == null)
                    ilg.EndIf();
            }
        }
 
        private void WriteText(SourceInfo source, TextAccessor text)
        {
            if (text.Mapping is PrimitiveMapping primitiveMapping)
            {
                Type argType;
                ilg.Ldarg(0);
                if (text.Mapping is EnumMapping)
                {
                    WriteEnumValue((EnumMapping)text.Mapping, source, out argType);
                }
                else
                {
                    WritePrimitiveValue(primitiveMapping.TypeDesc!, source, out argType);
                }
                MethodInfo XmlSerializationWriter_WriteValue = typeof(XmlSerializationWriter).GetMethod(
                    "WriteValue",
                    CodeGenerator.InstanceBindingFlags,
                    new Type[] { argType }
                    )!;
                ilg.Call(XmlSerializationWriter_WriteValue);
            }
            else if (text.Mapping is SpecialMapping specialMapping)
            {
                switch (specialMapping.TypeDesc!.Kind)
                {
                    case TypeKind.Node:
                        MethodInfo WriteTo = source.Type!.GetMethod(
                            "WriteTo",
                            CodeGenerator.InstanceBindingFlags,
                            new Type[] { typeof(XmlWriter) }
                            )!;
                        MethodInfo XmlSerializationWriter_get_Writer = typeof(XmlSerializationWriter).GetMethod(
                            "get_Writer",
                            CodeGenerator.InstanceBindingFlags,
                            Type.EmptyTypes
                            )!;
                        source.Load(source.Type);
                        ilg.Ldarg(0);
                        ilg.Call(XmlSerializationWriter_get_Writer);
                        ilg.Call(WriteTo);
                        break;
                    default:
                        throw new InvalidOperationException(SR.XmlInternalError);
                }
            }
        }
 
        private void WriteElement(SourceInfo source, ElementAccessor element, string arrayName, bool writeAccessor)
        {
            string name = writeAccessor ? element.Name : element.Mapping!.TypeName!;
            string? ns = element.Any && element.Name.Length == 0 ? null : (element.Form == XmlSchemaForm.Qualified ? (writeAccessor ? element.Namespace : element.Mapping!.Namespace) : "");
            if (element.Mapping is NullableMapping)
            {
                if (source.Type == element.Mapping.TypeDesc!.Type)
                {
                    MethodInfo Nullable_get_HasValue = element.Mapping.TypeDesc.Type!.GetMethod(
                        "get_HasValue",
                        CodeGenerator.InstanceBindingFlags,
                        Type.EmptyTypes
                        )!;
                    source.LoadAddress(element.Mapping.TypeDesc.Type);
                    ilg.Call(Nullable_get_HasValue);
                }
                else
                {
                    source.Load(null);
                    ilg.Load(null);
                    ilg.Cne();
                }
                ilg.If();
                SourceInfo castedSource = source.CastTo(element.Mapping.TypeDesc.BaseTypeDesc!);
                ElementAccessor e = element.Clone();
                e.Mapping = ((NullableMapping)element.Mapping).BaseMapping;
                WriteElement(e.Any ? source : castedSource, e, arrayName, writeAccessor);
                if (element.IsNullable)
                {
                    ilg.Else();
                    WriteLiteralNullTag(element.Name, element.Form == XmlSchemaForm.Qualified ? element.Namespace : "");
                }
                ilg.EndIf();
            }
            else if (element.Mapping is ArrayMapping arrayMapping)
            {
                if (element.IsUnbounded)
                {
                    throw Globals.NotSupported("Unreachable: IsUnbounded is never set true!");
                }
                else
                {
                    ilg.EnterScope();
                    string fullTypeName = arrayMapping.TypeDesc!.CSharpName;
                    WriteArrayLocalDecl(fullTypeName, arrayName, source, arrayMapping.TypeDesc);
                    if (element.IsNullable)
                    {
                        WriteNullCheckBegin(arrayName, element);
                    }
                    else
                    {
                        if (arrayMapping.TypeDesc.IsNullable)
                        {
                            ilg.Ldloc(ilg.GetLocal(arrayName));
                            ilg.Load(null);
                            ilg.If(Cmp.NotEqualTo);
                        }
                    }
                    WriteStartElement(name, ns, false);
                    WriteArrayItems(arrayMapping.ElementsSortedByDerivation!, null, null, arrayMapping.TypeDesc, arrayName, null);
                    WriteEndElement();
                    if (element.IsNullable)
                    {
                        ilg.EndIf();
                    }
                    else
                    {
                        if (arrayMapping.TypeDesc.IsNullable)
                        {
                            ilg.EndIf();
                        }
                    }
                    ilg.ExitScope();
                }
            }
            else if (element.Mapping is EnumMapping)
            {
                WritePrimitive("WriteElementString", name, ns, element.Default, source, element.Mapping, false, true, element.IsNullable);
            }
            else if (element.Mapping is PrimitiveMapping primitiveMapping)
            {
                if (primitiveMapping.TypeDesc == QnameTypeDesc)
                {
                    WriteQualifiedNameElement(name, ns, GetConvertedDefaultValue(source.Type, element.Default), source, element.IsNullable, primitiveMapping);
                }
                else
                {
                    string suffixRaw = primitiveMapping.TypeDesc!.XmlEncodingNotRequired ? "Raw" : "";
                    WritePrimitive(element.IsNullable ? ("WriteNullableStringLiteral" + suffixRaw) : ("WriteElementString" + suffixRaw),
                                   name, ns, GetConvertedDefaultValue(source.Type, element.Default), source, primitiveMapping, false, true, element.IsNullable);
                }
            }
            else if (element.Mapping is StructMapping structMapping)
            {
                string? methodName = ReferenceMapping(structMapping);
 
#if DEBUG
                // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
                if (methodName == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorMethod, structMapping.TypeDesc!.Name));
#endif
                List<Type> argTypes = new List<Type>();
                ilg.Ldarg(0);
                ilg.Ldstr(GetCSharpString(name));
                argTypes.Add(typeof(string));
                ilg.Ldstr(GetCSharpString(ns));
                argTypes.Add(typeof(string));
                source.Load(structMapping.TypeDesc!.Type);
                argTypes.Add(structMapping.TypeDesc.Type!);
                if (structMapping.TypeDesc.IsNullable)
                {
                    ilg.Ldc(element.IsNullable);
                    argTypes.Add(typeof(bool));
                }
                ilg.Ldc(false);
                argTypes.Add(typeof(bool));
                MethodBuilder methodBuilder = EnsureMethodBuilder(typeBuilder,
                    methodName!,
                    CodeGenerator.PrivateMethodAttributes,
                    typeof(void),
                    argTypes.ToArray());
                ilg.Call(methodBuilder);
            }
            else if (element.Mapping is SpecialMapping)
            {
                if (element.Mapping is SerializableMapping)
                {
                    WriteElementCall("WriteSerializable", typeof(IXmlSerializable), source, name, ns, element.IsNullable, !element.Any);
                }
                else
                {
                    // XmlNode, XmlElement
                    Label ifLabel1 = ilg.DefineLabel();
                    Label ifLabel2 = ilg.DefineLabel();
                    source.Load(null);
                    ilg.IsInst(typeof(XmlNode));
                    ilg.Brtrue(ifLabel1);
                    source.Load(null);
                    ilg.Load(null);
                    ilg.Ceq();
                    ilg.Br(ifLabel2);
                    ilg.MarkLabel(ifLabel1);
                    ilg.Ldc(true);
                    ilg.MarkLabel(ifLabel2);
                    ilg.If();
 
                    WriteElementCall("WriteElementLiteral", typeof(XmlNode), source, name, ns, element.IsNullable, element.Any);
 
                    ilg.Else();
 
                    MethodInfo XmlSerializationWriter_CreateInvalidAnyTypeException = typeof(XmlSerializationWriter).GetMethod(
                        "CreateInvalidAnyTypeException",
                        CodeGenerator.InstanceBindingFlags,
                        s_objectType
                        )!;
                    ilg.Ldarg(0);
                    source.Load(null);
                    ilg.Call(XmlSerializationWriter_CreateInvalidAnyTypeException);
                    ilg.Throw();
 
                    ilg.EndIf();
                }
            }
            else
            {
                throw new InvalidOperationException(SR.XmlInternalError);
            }
        }
 
        private void WriteElementCall(string func, Type cast, SourceInfo source, string? name, string? ns, bool isNullable, bool isAny)
        {
            MethodInfo XmlSerializationWriter_func = typeof(XmlSerializationWriter).GetMethod(
                 func,
                 CodeGenerator.InstanceBindingFlags,
                 new Type[] { cast, typeof(string), typeof(string), typeof(bool), typeof(bool) }
                 )!;
            ilg.Ldarg(0);
            source.Load(cast);
            ilg.Ldstr(GetCSharpString(name));
            ilg.Ldstr(GetCSharpString(ns));
            ilg.Ldc(isNullable);
            ilg.Ldc(isAny);
            ilg.Call(XmlSerializationWriter_func);
        }
 
        private void WriteCheckDefault(SourceInfo source, object value, bool isNullable)
        {
            if (value is string && ((string)value).Length == 0)
            {
                // special case for string compare
                Label labelEnd = ilg.DefineLabel();
                Label labelFalse = ilg.DefineLabel();
                Label labelTrue = ilg.DefineLabel();
                source.Load(typeof(string));
                if (isNullable)
                    // check == null with false
                    ilg.Brfalse(labelTrue);
                else
                    //check != null with false
                    ilg.Brfalse(labelFalse);
                MethodInfo String_get_Length = typeof(string).GetMethod(
                    "get_Length",
                    CodeGenerator.InstanceBindingFlags,
                    Type.EmptyTypes
                    )!;
                source.Load(typeof(string));
                ilg.Call(String_get_Length);
                ilg.Ldc(0);
                ilg.Cne();
                ilg.Br(labelEnd);
                if (isNullable)
                {
                    ilg.MarkLabel(labelTrue);
                    ilg.Ldc(true);
                }
                else
                {
                    ilg.MarkLabel(labelFalse);
                    ilg.Ldc(false);
                }
                ilg.MarkLabel(labelEnd);
                ilg.If();
            }
            else
            {
                if (value == null)
                {
                    source.Load(typeof(object));
                    ilg.Load(null);
                    ilg.Cne();
                }
                else if (value.GetType().IsPrimitive)
                {
                    source.Load(null);
                    ilg.Ldc(Convert.ChangeType(value, source.Type!, CultureInfo.InvariantCulture));
                    ilg.Cne();
                }
                else
                {
                    Type valueType = value.GetType();
                    source.Load(valueType);
                    ilg.Ldc(value is string ? GetCSharpString((string)value) : value);
                    MethodInfo op_Inequality = valueType.GetMethod(
                                "op_Inequality",
                                CodeGenerator.StaticBindingFlags,
                                new Type[] { valueType, valueType }
                                )!;
                    if (op_Inequality != null)
                        ilg.Call(op_Inequality);
                    else
                        ilg.Cne();
                }
                ilg.If();
            }
        }
 
        private void WriteChoiceTypeCheck(SourceInfo source, ChoiceIdentifierAccessor choice, string enumName, TypeDesc typeDesc)
        {
            Label labelFalse = ilg.DefineLabel();
            Label labelEnd = ilg.DefineLabel();
            source.Load(typeof(object));
            ilg.Load(null);
            ilg.Beq(labelFalse);
            WriteInstanceOf(source, typeDesc.Type!);
            // Negative
            ilg.Ldc(false);
            ilg.Ceq();
            ilg.Br(labelEnd);
            ilg.MarkLabel(labelFalse);
            ilg.Ldc(false);
            ilg.MarkLabel(labelEnd);
            ilg.If();
            MethodInfo XmlSerializationWriter_CreateMismatchChoiceException = typeof(XmlSerializationWriter).GetMethod(
                "CreateMismatchChoiceException",
                CodeGenerator.InstanceBindingFlags,
                new Type[] { typeof(string), typeof(string), typeof(string) }
                )!;
            ilg.Ldarg(0);
            ilg.Ldstr(GetCSharpString(typeDesc.FullName));
            ilg.Ldstr(GetCSharpString(choice.MemberName));
            ilg.Ldstr(GetCSharpString(enumName));
            ilg.Call(XmlSerializationWriter_CreateMismatchChoiceException);
            ilg.Throw();
            ilg.EndIf();
        }
 
        private void WriteNullCheckBegin(string source, ElementAccessor element)
        {
            LocalBuilder local = ilg.GetLocal(source);
            Debug.Assert(!local.LocalType.IsValueType);
            ilg.Load(local);
            ilg.Load(null);
            ilg.If(Cmp.EqualTo);
            WriteLiteralNullTag(element.Name, element.Form == XmlSchemaForm.Qualified ? element.Namespace : "");
            ilg.Else();
        }
 
        private void WriteNamespaces(string source)
        {
            MethodInfo XmlSerializationWriter_WriteNamespaceDeclarations = typeof(XmlSerializationWriter).GetMethod(
                "WriteNamespaceDeclarations",
                CodeGenerator.InstanceBindingFlags,
                new Type[] { typeof(XmlSerializerNamespaces) }
                )!;
            ilg.Ldarg(0);
            ILGenLoad(source, typeof(XmlSerializerNamespaces));
            ilg.Call(XmlSerializationWriter_WriteNamespaceDeclarations);
        }
 
        private static int FindXmlnsIndex(MemberMapping[] members)
        {
            for (int i = 0; i < members.Length; i++)
            {
                if (members[i].Xmlns == null)
                    continue;
                return i;
            }
            return -1;
        }
 
        private void WriteLocalDecl(string variableName, string initValue, Type type)
        {
            ReflectionAwareILGen.WriteLocalDecl(variableName, new SourceInfo(initValue, initValue, null, type, ilg));
        }
 
        private static void WriteArrayLocalDecl(string typeName, string variableName, SourceInfo initValue, TypeDesc arrayTypeDesc)
        {
            ReflectionAwareILGen.WriteArrayLocalDecl(typeName, variableName, initValue, arrayTypeDesc);
        }
        private void WriteTypeCompare(string variable, Type type)
        {
            ReflectionAwareILGen.WriteTypeCompare(variable, type, ilg);
        }
 
        private void WriteInstanceOf(SourceInfo source, Type type)
        {
            ReflectionAwareILGen.WriteInstanceOf(source, type, ilg);
        }
        private void WriteArrayTypeCompare(string variable, Type arrayType)
        {
            ReflectionAwareILGen.WriteArrayTypeCompare(variable, arrayType, ilg);
        }
 
 
        private static string FindChoiceEnumValue(ElementAccessor element, EnumMapping choiceMapping, out object? eValue)
        {
            string? enumValue = null;
            eValue = null;
 
            for (int i = 0; i < choiceMapping.Constants!.Length; i++)
            {
                string xmlName = choiceMapping.Constants[i].XmlName;
 
                if (element.Any && element.Name.Length == 0)
                {
                    if (xmlName == "##any:")
                    {
                        enumValue = choiceMapping.Constants[i].Name;
                        eValue = Enum.ToObject(choiceMapping.TypeDesc!.Type!, choiceMapping.Constants[i].Value);
                        break;
                    }
                    continue;
                }
                int colon = xmlName.LastIndexOf(':');
                string? choiceNs = colon < 0 ? choiceMapping.Namespace : xmlName.Substring(0, colon);
                string choiceName = colon < 0 ? xmlName : xmlName.Substring(colon + 1);
 
                if (element.Name == choiceName)
                {
                    if ((element.Form == XmlSchemaForm.Unqualified && string.IsNullOrEmpty(choiceNs)) || element.Namespace == choiceNs)
                    {
                        enumValue = choiceMapping.Constants[i].Name;
                        eValue = Enum.ToObject(choiceMapping.TypeDesc!.Type!, choiceMapping.Constants[i].Value);
                        break;
                    }
                }
            }
            if (string.IsNullOrEmpty(enumValue))
            {
                if (element.Any && element.Name.Length == 0)
                {
                    // Type {0} is missing enumeration value '##any' for XmlAnyElementAttribute.
                    throw new InvalidOperationException(SR.Format(SR.XmlChoiceMissingAnyValue, choiceMapping.TypeDesc!.FullName));
                }
                // Type {0} is missing value for '{1}'.
                throw new InvalidOperationException(SR.Format(SR.XmlChoiceMissingValue, choiceMapping.TypeDesc!.FullName, $"{element.Namespace}:{element.Name}", element.Name, element.Namespace));
            }
            CodeIdentifier.CheckValidIdentifier(enumValue);
            return enumValue;
        }
    }
 
    internal sealed class ReflectionAwareILGen
    {
        // reflectionVariables holds mapping between a reflection entity
        // referenced in the generated code (such as TypeInfo,
        // FieldInfo) and the variable which represent the entity (and
        // initialized before).
        // The types of reflection entity and corresponding key is
        // given below.
        // ----------------------------------------------------------------------------------
        // Entity           Key
        // ----------------------------------------------------------------------------------
        // Assembly         assembly.FullName
        // Type             CodeIdentifier.EscapedKeywords(type.FullName)
        // Field            fieldName+":"+CodeIdentifier.EscapedKeywords(containingType.FullName>)
        // Property         propertyName+":"+CodeIdentifier.EscapedKeywords(containingType.FullName)
        // ArrayAccessor    "0:"+CodeIdentifier.EscapedKeywords(typeof(Array).FullName)
        // MyCollectionAccessor     "0:"+CodeIdentifier.EscapedKeywords(typeof(MyCollection).FullName)
        // ----------------------------------------------------------------------------------
        internal ReflectionAwareILGen() { }
 
        [RequiresUnreferencedCode("calls GetTypeDesc")]
        internal static void WriteReflectionInit(TypeScope scope)
        {
            foreach (Type type in scope.Types)
            {
                scope.GetTypeDesc(type);
            }
        }
 
        internal static void ILGenForEnumLongValue(CodeGenerator ilg, string variable)
        {
            ArgBuilder argV = ilg.GetArg(variable);
            ilg.Ldarg(argV);
            ilg.ConvertValue(argV.ArgType, typeof(long));
        }
 
        internal static string GetStringForTypeof(string typeFullName)
        {
            return $"typeof({typeFullName})";
        }
        internal static string GetStringForMember(string obj, string memberName)
        {
            return $"{obj}.@{memberName}";
        }
        internal static SourceInfo GetSourceForMember(string obj, MemberMapping member, CodeGenerator ilg)
        {
            return GetSourceForMember(obj, member, member.MemberInfo, ilg);
        }
        internal static SourceInfo GetSourceForMember(string obj, MemberMapping member, MemberInfo? memberInfo, CodeGenerator ilg)
        {
            return new SourceInfo(GetStringForMember(obj, member.Name), obj, memberInfo, member.TypeDesc!.Type, ilg);
        }
 
        internal static void ILGenForEnumMember(CodeGenerator ilg, Type type, string memberName)
        {
            ilg.Ldc(Enum.Parse(type, memberName, false));
        }
        internal static string GetStringForArrayMember(string? arrayName, string subscript)
        {
            return $"{arrayName}[{subscript}]";
        }
        internal static string GetStringForMethod(string obj, string memberName)
        {
            return $"{obj}.{memberName}(";
        }
 
        [RequiresUnreferencedCode("calls ILGenForCreateInstance")]
        internal static void ILGenForCreateInstance(CodeGenerator ilg, Type type, bool ctorInaccessible, bool cast)
        {
            if (!ctorInaccessible)
            {
                ConstructorInfo ctor = type.GetConstructor(
                       CodeGenerator.InstanceBindingFlags,
                       Type.EmptyTypes
                       )!;
                if (ctor != null)
                    ilg.New(ctor);
                else
                {
                    Debug.Assert(type.IsValueType);
                    LocalBuilder tmpLoc = ilg.GetTempLocal(type);
                    ilg.Ldloca(tmpLoc);
                    ilg.InitObj(type);
                    ilg.Ldloc(tmpLoc);
                }
                return;
            }
            ILGenForCreateInstance(ilg, type, cast ? type : null);
        }
 
        [RequiresUnreferencedCode("calls GetType")]
        internal static void ILGenForCreateInstance(CodeGenerator ilg, Type type, Type? cast)
        {
            // Special case DBNull
            if (type == typeof(DBNull))
            {
                FieldInfo DBNull_Value = type.GetField("Value", CodeGenerator.StaticBindingFlags)!;
                ilg.LoadMember(DBNull_Value);
                return;
            }
 
            // Special case XElement
            // codegen the same as 'internal XElement : this("default") { }'
            if (type.FullName == "System.Xml.Linq.XElement")
            {
                Type? xName = type.Assembly.GetType("System.Xml.Linq.XName");
                if (xName != null)
                {
                    MethodInfo XName_op_Implicit = xName.GetMethod(
                        "op_Implicit",
                        CodeGenerator.StaticBindingFlags,
                        new Type[] { typeof(string) }
                        )!;
                    ConstructorInfo XElement_ctor = type.GetConstructor(
                        CodeGenerator.InstanceBindingFlags,
                        new Type[] { xName }
                        )!;
                    if (XName_op_Implicit != null && XElement_ctor != null)
                    {
                        ilg.Ldstr("default");
                        ilg.Call(XName_op_Implicit);
                        ilg.New(XElement_ctor);
                        return;
                    }
                }
            }
 
            Label labelReturn = ilg.DefineLabel();
            Label labelEndIf = ilg.DefineLabel();
 
            // TypeInfo typeInfo = type.GetTypeInfo();
            // typeInfo not declared explicitly
            ilg.Ldc(type);
            MethodInfo getTypeInfoMehod = typeof(IntrospectionExtensions).GetMethod(
                  "GetTypeInfo",
                  CodeGenerator.StaticBindingFlags,
                  new[] { typeof(Type) }
                  )!;
            ilg.Call(getTypeInfoMehod);
 
            // IEnumerator<ConstructorInfo> e = typeInfo.DeclaredConstructors.GetEnumerator();
            LocalBuilder enumerator = ilg.DeclareLocal(typeof(IEnumerator<>).MakeGenericType(typeof(ConstructorInfo)), "e");
            MethodInfo getDeclaredConstructors = typeof(TypeInfo).GetMethod("get_DeclaredConstructors")!;
            MethodInfo getEnumerator = typeof(IEnumerable<>).MakeGenericType(typeof(ConstructorInfo)).GetMethod("GetEnumerator")!;
            ilg.Call(getDeclaredConstructors);
            ilg.Call(getEnumerator);
            ilg.Stloc(enumerator);
 
            ilg.WhileBegin();
            // ConstructorInfo constructorInfo = e.Current();
            MethodInfo enumeratorCurrent = typeof(IEnumerator).GetMethod("get_Current")!;
            ilg.Ldloc(enumerator);
            ilg.Call(enumeratorCurrent);
            LocalBuilder constructorInfo = ilg.DeclareLocal(typeof(ConstructorInfo), "constructorInfo");
            ilg.Stloc(constructorInfo);
 
            // if (!constructorInfo.IsStatic && constructorInfo.GetParameters.Length() == 0)
            ilg.Ldloc(constructorInfo);
            MethodInfo constructorIsStatic = typeof(ConstructorInfo).GetMethod("get_IsStatic")!;
            ilg.Call(constructorIsStatic);
            ilg.Brtrue(labelEndIf);
            ilg.Ldloc(constructorInfo);
            MethodInfo constructorGetParameters = typeof(ConstructorInfo).GetMethod("GetParameters")!;
            ilg.Call(constructorGetParameters);
            ilg.Ldlen();
            ilg.Ldc(0);
            ilg.Cne();
            ilg.Brtrue(labelEndIf);
 
            // constructorInfo.Invoke(null);
            MethodInfo constructorInvoke = typeof(ConstructorInfo).GetMethod("Invoke", new Type[] { typeof(object[]) })!;
            ilg.Ldloc(constructorInfo);
            ilg.Load(null);
            ilg.Call(constructorInvoke);
            ilg.Br(labelReturn);
 
            ilg.MarkLabel(labelEndIf);
            ilg.WhileBeginCondition(); // while (e.MoveNext())
            MethodInfo IEnumeratorMoveNext = typeof(IEnumerator).GetMethod(
                "MoveNext",
                CodeGenerator.InstanceBindingFlags,
                Type.EmptyTypes)!;
            ilg.Ldloc(enumerator);
            ilg.Call(IEnumeratorMoveNext);
            ilg.WhileEndCondition();
            ilg.WhileEnd();
 
            MethodInfo Activator_CreateInstance = typeof(Activator).GetMethod(
                  "CreateInstance",
                  CodeGenerator.StaticBindingFlags,
                  new Type[] { typeof(Type) }
                  )!;
            ilg.Ldc(type);
            ilg.Call(Activator_CreateInstance);
            ilg.MarkLabel(labelReturn);
            if (cast != null)
                ilg.ConvertValue(Activator_CreateInstance.ReturnType, cast);
        }
 
        [RequiresUnreferencedCode("calls LoadMember")]
        internal static void WriteLocalDecl(string variableName, SourceInfo initValue)
        {
            Type localType = initValue.Type!;
            LocalBuilder localA = initValue.ILG.DeclareOrGetLocal(localType, variableName);
            if (initValue.Source != null)
            {
                if (initValue == "null")
                {
                    initValue.ILG.Load(null);
                }
                else
                {
                    if (initValue.Arg.StartsWith("o.@", StringComparison.Ordinal))
                    {
                        Debug.Assert(initValue.MemberInfo != null);
                        Debug.Assert(initValue.MemberInfo.Name == initValue.Arg.Substring(3));
                        initValue.ILG.LoadMember(initValue.ILG.GetLocal("o"), initValue.MemberInfo);
                    }
                    else if (initValue.Source.EndsWith(']'))
                    {
                        initValue.Load(initValue.Type);
                    }
                    else if (initValue.Source == "fixup.Source" || initValue.Source == "e.Current")
                    {
                        string[] vars = initValue.Source.Split('.');
                        object fixup = initValue.ILG.GetVariable(vars[0]);
                        PropertyInfo propInfo = CodeGenerator.GetVariableType(fixup).GetProperty(vars[1])!;
                        initValue.ILG.LoadMember(fixup, propInfo);
                        initValue.ILG.ConvertValue(propInfo.PropertyType, localA.LocalType);
                    }
                    else
                    {
                        object sVar = initValue.ILG.GetVariable(initValue.Arg);
                        initValue.ILG.Load(sVar);
                        initValue.ILG.ConvertValue(CodeGenerator.GetVariableType(sVar), localA.LocalType);
                    }
                }
                initValue.ILG.Stloc(localA);
            }
        }
 
        [RequiresUnreferencedCode("calls ILGenForCreateInstance")]
        internal static void WriteCreateInstance(string source, bool ctorInaccessible, Type type, CodeGenerator ilg)
        {
            LocalBuilder sLoc = ilg.DeclareOrGetLocal(type, source);
            ILGenForCreateInstance(ilg, type, ctorInaccessible, ctorInaccessible);
            ilg.Stloc(sLoc);
        }
 
        [RequiresUnreferencedCode("calls Load")]
        internal static void WriteInstanceOf(SourceInfo source, Type type, CodeGenerator ilg)
        {
            source.Load(typeof(object));
            ilg.IsInst(type);
            ilg.Load(null);
            ilg.Cne();
        }
 
        [RequiresUnreferencedCode("calls Load")]
        [RequiresDynamicCode(XmlSerializer.AotSerializationWarning)]
        internal static void WriteArrayLocalDecl(string typeName, string variableName, SourceInfo initValue, TypeDesc arrayTypeDesc)
        {
            Debug.Assert(typeName == arrayTypeDesc.CSharpName || typeName == $"{arrayTypeDesc.CSharpName}[]");
            Type localType = (typeName == arrayTypeDesc.CSharpName) ? arrayTypeDesc.Type! : arrayTypeDesc.Type!.MakeArrayType();
            // This may need reused variable to get code compat?
            LocalBuilder local = initValue.ILG.DeclareOrGetLocal(localType, variableName);
            initValue.Load(local.LocalType);
            initValue.ILG.Stloc(local);
        }
        internal static void WriteTypeCompare(string variable, Type type, CodeGenerator ilg)
        {
            Debug.Assert(type != null);
            Debug.Assert(ilg != null);
            ilg.Ldloc(typeof(Type), variable);
            ilg.Ldc(type);
            ilg.Ceq();
        }
        internal static void WriteArrayTypeCompare(string variable, Type arrayType, CodeGenerator ilg)
        {
            Debug.Assert(arrayType != null);
            Debug.Assert(ilg != null);
            ilg.Ldloc(typeof(Type), variable);
            ilg.Ldc(arrayType);
            ilg.Ceq();
        }
 
        [return: NotNullIfNotNull(nameof(value))]
        internal static string? GetQuotedCSharpString(string? value) =>
            value is null ? null :
            $"@\"{GetCSharpString(value)}\"";
 
        [return: NotNullIfNotNull(nameof(value))]
        internal static string? GetCSharpString(string? value)
        {
            if (value == null)
            {
                return null;
            }
 
            // Find the first character to be escaped.
            int i;
            for (i = 0; i < value.Length; i++)
            {
                if (value[i] is < (char)32 or '\"')
                {
                    break;
                }
            }
 
            // If nothing needs to be escaped, return the original string.
            if (i == value.Length)
            {
                return value;
            }
 
            var builder = new ValueStringBuilder(stackalloc char[128]);
 
            // Copy over all text that needn't be escaped.
            builder.Append(value.AsSpan(0, i));
 
            // Process the remainder of the string, escaping each value that needs to be.
            for (; i < value.Length; i++)
            {
                char ch = value[i];
 
                if (ch < 32)
                {
                    if (ch == '\r')
                    {
                        builder.Append("\\r");
                    }
                    else if (ch == '\n')
                    {
                        builder.Append("\\n");
                    }
                    else if (ch == '\t')
                    {
                        builder.Append("\\t");
                    }
                    else
                    {
                        byte b = (byte)ch;
                        builder.Append("\\x");
                        builder.Append(HexConverter.ToCharUpper(b >> 4));
                        builder.Append(HexConverter.ToCharUpper(b));
                    }
                }
                else if (ch == '\"')
                {
                    builder.Append("\"\"");
                }
                else
                {
                    builder.Append(ch);
                }
            }
 
            return builder.ToString();
        }
    }
}