|
// 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.Security;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Schema;
namespace System.Xml.Serialization
{
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
[RequiresDynamicCode(XmlSerializer.AotSerializationWarning)]
internal sealed partial class XmlSerializationReaderILGen : XmlSerializationILGen
{
private readonly Dictionary<string, string> _idNames = new Dictionary<string, string>();
// Mapping name->id_XXXNN field
private readonly Dictionary<string, FieldBuilder> _idNameFields = new Dictionary<string, FieldBuilder>();
private Dictionary<string, EnumMapping>? _enums;
private int _nextIdNumber;
internal Dictionary<string, EnumMapping> Enums => _enums ??= new Dictionary<string, EnumMapping>();
private static readonly string[] s_checkTypeString = new string[] { "checkType" };
private static readonly Type[] s_boolType = new Type[] { typeof(bool) };
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
[RequiresDynamicCode(XmlSerializer.AotSerializationWarning)]
private sealed class Member
{
private readonly string _source;
private readonly string _arrayName;
private readonly string _arraySource;
private readonly string _choiceArrayName;
private readonly string? _choiceSource;
private readonly string? _choiceArraySource;
private readonly MemberMapping _mapping;
private readonly bool _isArray;
private readonly bool _isList;
private bool _isNullable;
private int _fixupIndex = -1;
private string? _paramsReadSource;
private string? _checkSpecifiedSource;
internal Member(string source, string? arrayName, int i, MemberMapping mapping)
: this(source, null, arrayName, i, mapping, false, null)
{
}
internal Member(string source, string? arrayName, int i, MemberMapping mapping, string? choiceSource)
: this(source, null, arrayName, i, mapping, false, choiceSource)
{
}
internal Member(string source, string? arraySource, string? arrayName, int i, MemberMapping mapping)
: this(source, arraySource, arrayName, i, mapping, false, null)
{
}
internal Member(string source, string? arraySource, string? arrayName, int i, MemberMapping mapping, string? choiceSource)
: this(source, arraySource, arrayName, i, mapping, false, choiceSource)
{
}
internal Member(string source, string? arrayName, int i, MemberMapping mapping, bool multiRef)
: this(source, null, arrayName, i, mapping, multiRef, null)
{
}
internal Member(string source, string? arraySource, string? arrayName, int i, MemberMapping mapping, bool multiRef, string? choiceSource)
{
_source = source;
_arrayName = string.Create(CultureInfo.InvariantCulture, $"{arrayName}_{i}");
_choiceArrayName = $"choice_{_arrayName}";
_choiceSource = choiceSource;
if (mapping.TypeDesc!.IsArrayLike)
{
if (arraySource != null)
_arraySource = arraySource;
else
_arraySource = XmlSerializationReaderILGen.GetArraySource(mapping.TypeDesc, _arrayName, multiRef);
_isArray = mapping.TypeDesc.IsArray;
_isList = !_isArray;
if (mapping.ChoiceIdentifier != null)
{
_choiceArraySource = XmlSerializationReaderILGen.GetArraySource(mapping.TypeDesc, _choiceArrayName, multiRef);
string a = _choiceArrayName;
string c = $"c{a}";
string choiceTypeFullName = mapping.ChoiceIdentifier.Mapping!.TypeDesc!.CSharpName;
string castString = $"({choiceTypeFullName}[])";
string init = $"{a} = {castString}EnsureArrayIndex({a}, {c}, {ReflectionAwareILGen.GetStringForTypeof(choiceTypeFullName)});";
_choiceArraySource = init + ReflectionAwareILGen.GetStringForArrayMember(a, $"{c}++");
}
else
{
_choiceArraySource = _choiceSource;
}
}
else
{
_arraySource = arraySource ?? source;
_choiceArraySource = _choiceSource;
}
_mapping = mapping;
}
internal MemberMapping Mapping
{
get { return _mapping; }
}
internal string Source
{
get { return _source; }
}
internal string ArrayName
{
get { return _arrayName; }
}
internal string ArraySource
{
get { return _arraySource; }
}
internal bool IsList
{
get { return _isList; }
}
internal bool IsArrayLike
{
get { return (_isArray || _isList); }
}
internal bool IsNullable
{
get { return _isNullable; }
set { _isNullable = value; }
}
internal int FixupIndex
{
get { return _fixupIndex; }
set { _fixupIndex = value; }
}
internal string? ParamsReadSource
{
get { return _paramsReadSource; }
set { _paramsReadSource = value; }
}
internal string? CheckSpecifiedSource
{
get { return _checkSpecifiedSource; }
set { _checkSpecifiedSource = value; }
}
internal string? ChoiceSource
{
get { return _choiceSource; }
}
internal string ChoiceArrayName
{
get { return _choiceArrayName; }
}
internal string? ChoiceArraySource
{
get { return _choiceArraySource; }
}
}
internal XmlSerializationReaderILGen(TypeScope[] scopes, string access, string className)
: base(scopes, access, className)
{
}
internal void GenerateBegin()
{
this.typeBuilder = CodeGenerator.CreateTypeBuilder(
ModuleBuilder,
ClassName,
TypeAttributes | TypeAttributes.BeforeFieldInit,
typeof(XmlSerializationReader),
Type.EmptyTypes);
foreach (TypeScope scope in Scopes)
{
foreach (TypeMapping mapping in scope.TypeMappings)
{
if (mapping is StructMapping || mapping is EnumMapping || mapping is NullableMapping)
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);
}
else if (mapping is NullableMapping)
{
WriteNullableMethod((NullableMapping)mapping);
}
}
internal void GenerateEnd()
{
GenerateReferencedMethods();
GenerateInitCallbacksMethod();
ilg = new CodeGenerator(this.typeBuilder);
ilg.BeginMethod(typeof(void), "InitIDs", Type.EmptyTypes, Array.Empty<string>(),
CodeGenerator.ProtectedOverrideMethodAttributes);
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_get_NameTable = typeof(XmlReader).GetMethod(
"get_NameTable",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlNameTable_Add = typeof(XmlNameTable).GetMethod(
"Add",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string) }
)!;
foreach (string id in _idNames.Keys)
{
ilg.Ldarg(0);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_NameTable);
ilg.Ldstr(GetCSharpString(id));
ilg.Call(XmlNameTable_Add);
Debug.Assert(_idNameFields.ContainsKey(id));
ilg.StoreMember(_idNameFields[id]);
}
ilg.EndMethod();
this.typeBuilder.DefineDefaultConstructor(
CodeGenerator.PublicMethodAttributes);
Type readerType = this.typeBuilder.CreateType();
CreatedTypes.Add(readerType.Name, readerType);
}
internal string? GenerateElement(XmlMapping xmlMapping)
{
if (!xmlMapping.IsReadable)
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 WriteIsStartTag(string? name, string? ns)
{
WriteID(name);
WriteID(ns);
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_IsStartElement = typeof(XmlReader).GetMethod(
"IsStartElement",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string), typeof(string) }
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Ldarg(0);
ilg.LoadMember(_idNameFields[name ?? string.Empty]);
ilg.Ldarg(0);
ilg.LoadMember(_idNameFields[ns ?? string.Empty]);
ilg.Call(XmlReader_IsStartElement);
ilg.If();
}
private void WriteUnknownNode(string func, string node, ElementAccessor? e, bool anyIfs)
{
if (anyIfs)
{
ilg.Else();
}
List<Type> argTypes = new List<Type>();
ilg.Ldarg(0);
Debug.Assert(node == "null" || node == "(object)p");
if (node == "null")
ilg.Load(null);
else
{
object pVar = ilg.GetVariable("p");
ilg.Load(pVar);
ilg.ConvertValue(CodeGenerator.GetVariableType(pVar), typeof(object));
}
argTypes.Add(typeof(object));
if (e != null)
{
string? expectedElement = e.Form == XmlSchemaForm.Qualified ? e.Namespace : "";
expectedElement += ":";
expectedElement += e.Name;
ilg.Ldstr(ReflectionAwareILGen.GetCSharpString(expectedElement));
argTypes.Add(typeof(string));
}
MethodInfo method = typeof(XmlSerializationReader).GetMethod(
func,
CodeGenerator.InstanceBindingFlags,
argTypes.ToArray()
)!;
ilg.Call(method);
if (anyIfs)
{
ilg.EndIf();
}
}
private void GenerateInitCallbacksMethod()
{
ilg = new CodeGenerator(this.typeBuilder);
ilg.BeginMethod(typeof(void), "InitCallbacks", Type.EmptyTypes, Array.Empty<string>(),
CodeGenerator.ProtectedOverrideMethodAttributes);
ilg.EndMethod();
}
private string GenerateMembersElement(XmlMembersMapping xmlMembersMapping)
{
return GenerateLiteralMembersElement(xmlMembersMapping);
}
private static string GetChoiceIdentifierSource(MemberMapping[] mappings, MemberMapping member)
{
string? choiceSource = null;
if (member.ChoiceIdentifier != null)
{
for (int j = 0; j < mappings.Length; j++)
{
if (mappings[j].Name == member.ChoiceIdentifier.MemberName)
{
choiceSource = $"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 (choiceSource == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, "Can not find " + member.ChoiceIdentifier.MemberName + " in the members mapping."));
#endif
}
return choiceSource!;
}
private static string GetChoiceIdentifierSource(MemberMapping mapping, string parent)
{
if (mapping.ChoiceIdentifier == null) return "";
CodeIdentifier.CheckValidIdentifier(mapping.ChoiceIdentifier.MemberName);
return ReflectionAwareILGen.GetStringForMember(parent, mapping.ChoiceIdentifier.MemberName);
}
private string GenerateLiteralMembersElement(XmlMembersMapping xmlMembersMapping)
{
ElementAccessor element = xmlMembersMapping.Accessor;
MemberMapping[] mappings = ((MembersMapping)element.Mapping!).Members!;
bool hasWrapperElement = ((MembersMapping)element.Mapping).HasWrapperElement;
string methodName = NextMethodName(element.Name);
ilg = new CodeGenerator(this.typeBuilder);
ilg.BeginMethod(
typeof(object[]),
methodName,
Type.EmptyTypes,
Array.Empty<string>(),
CodeGenerator.PublicMethodAttributes
);
ilg.Load(null);
ilg.Stloc(ilg.ReturnLocal);
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_MoveToContent = typeof(XmlReader).GetMethod(
"MoveToContent",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_MoveToContent);
ilg.Pop();
LocalBuilder localP = ilg.DeclareLocal(typeof(object[]), "p");
ilg.NewArray(typeof(object), mappings.Length);
ilg.Stloc(localP);
InitializeValueTypes("p", mappings);
if (hasWrapperElement)
{
WriteWhileNotLoopStart();
WriteIsStartTag(element.Name, element.Form == XmlSchemaForm.Qualified ? element.Namespace : "");
}
Member? anyText = null;
Member? anyElement = null;
Member? anyAttribute = null;
var membersList = new List<Member>();
var textOrArrayMembersList = new List<Member>();
var attributeMembersList = new List<Member>();
for (int i = 0; i < mappings.Length; i++)
{
MemberMapping mapping = mappings[i];
string source = $"p[{i}]";
string arraySource = source;
if (mapping.Xmlns != null)
{
arraySource = $"(({mapping.TypeDesc!.CSharpName}){source})";
}
string choiceSource = GetChoiceIdentifierSource(mappings, mapping);
Member member = new Member(source, arraySource, "a", i, mapping, choiceSource);
Member anyMember = new Member(source, null, "a", i, mapping, choiceSource);
if (!mapping.IsSequence)
member.ParamsReadSource = $"paramsRead[{i}]";
if (mapping.CheckSpecified == SpecifiedAccessor.ReadWrite)
{
string nameSpecified = $"{mapping.Name}Specified";
for (int j = 0; j < mappings.Length; j++)
{
if (mappings[j].Name == nameSpecified)
{
member.CheckSpecifiedSource = $"p[{j}]";
break;
}
}
}
bool foundAnyElement = false;
if (mapping.Text != null) anyText = anyMember;
if (mapping.Attribute != null && mapping.Attribute.Any)
anyAttribute = anyMember;
if (mapping.Attribute != null || mapping.Xmlns != null)
attributeMembersList.Add(member);
else if (mapping.Text != null)
textOrArrayMembersList.Add(member);
if (!mapping.IsSequence)
{
for (int j = 0; j < mapping.Elements!.Length; j++)
{
if (mapping.Elements[j].Any && mapping.Elements[j].Name.Length == 0)
{
anyElement = anyMember;
if (mapping.Attribute == null && mapping.Text == null)
textOrArrayMembersList.Add(anyMember);
foundAnyElement = true;
break;
}
}
}
if (mapping.Attribute != null || mapping.Text != null || foundAnyElement)
membersList.Add(anyMember);
else if (mapping.TypeDesc!.IsArrayLike && !(mapping.Elements!.Length == 1 && mapping.Elements[0].Mapping is ArrayMapping))
{
membersList.Add(anyMember);
textOrArrayMembersList.Add(anyMember);
}
else
{
if (mapping.TypeDesc.IsArrayLike && !mapping.TypeDesc.IsArray)
member.ParamsReadSource = null; // collection
membersList.Add(member);
}
}
Member[] members = membersList.ToArray();
Member[] textOrArrayMembers = textOrArrayMembersList.ToArray();
if (members.Length > 0 && members[0].Mapping.IsReturnValue)
{
MethodInfo XmlSerializationReader_set_IsReturnValue = typeof(XmlSerializationReader).GetMethod(
"set_IsReturnValue",
CodeGenerator.InstanceBindingFlags,
s_boolType
)!;
ilg.Ldarg(0);
ilg.Ldc(true);
ilg.Call(XmlSerializationReader_set_IsReturnValue);
}
WriteParamsRead(mappings.Length);
if (attributeMembersList.Count > 0)
{
Member[] attributeMembers = attributeMembersList.ToArray();
WriteMemberBegin(attributeMembers);
WriteAttributes(attributeMembers, anyAttribute, "UnknownNode", localP);
WriteMemberEnd(attributeMembers);
MethodInfo XmlReader_MoveToElement = typeof(XmlReader).GetMethod(
"MoveToElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_MoveToElement);
ilg.Pop();
}
WriteMemberBegin(textOrArrayMembers);
if (hasWrapperElement)
{
MethodInfo XmlReader_get_IsEmptyElement = typeof(XmlReader).GetMethod(
"get_IsEmptyElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_IsEmptyElement);
ilg.If();
{
MethodInfo XmlReader_Skip = typeof(XmlReader).GetMethod(
"Skip",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_Skip);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_MoveToContent);
ilg.Pop();
ilg.WhileContinue();
}
ilg.EndIf();
MethodInfo XmlReader_ReadStartElement = typeof(XmlReader).GetMethod(
"ReadStartElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_ReadStartElement);
}
if (IsSequence(members))
{
ilg.Ldc(0);
ilg.Stloc(typeof(int), "state");
}
WriteWhileNotLoopStart();
string unknownNode = $"UnknownNode((object)p, {ExpectedElements(members)});";
WriteMemberElements(members, unknownNode, unknownNode, anyElement, anyText);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_MoveToContent);
ilg.Pop();
WriteWhileLoopEnd();
WriteMemberEnd(textOrArrayMembers);
if (hasWrapperElement)
{
MethodInfo XmlSerializationReader_ReadEndElement = typeof(XmlSerializationReader).GetMethod(
"ReadEndElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_ReadEndElement);
WriteUnknownNode("UnknownNode", "null", element, true);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_MoveToContent);
ilg.Pop();
WriteWhileLoopEnd();
}
ilg.Ldloc(ilg.GetLocal("p"));
ilg.EndMethod();
return methodName;
}
private void InitializeValueTypes(string arrayName, MemberMapping[] mappings)
{
for (int i = 0; i < mappings.Length; i++)
{
if (!mappings[i].TypeDesc!.IsValueType)
continue;
LocalBuilder arrayLoc = ilg.GetLocal(arrayName);
ilg.Ldloc(arrayLoc);
ilg.Ldc(i);
ReflectionAwareILGen.ILGenForCreateInstance(ilg, mappings[i].TypeDesc!.Type!, false, false);
ilg.ConvertValue(mappings[i].TypeDesc!.Type!, typeof(object));
ilg.Stelem(arrayLoc.LocalType.GetElementType()!);
}
}
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(object),
methodName,
Type.EmptyTypes,
Array.Empty<string>(),
CodeGenerator.PublicMethodAttributes
);
LocalBuilder oLoc = ilg.DeclareLocal(typeof(object), "o");
ilg.Load(null);
ilg.Stloc(oLoc);
MemberMapping member = new MemberMapping();
member.TypeDesc = mapping.TypeDesc;
//member.ReadOnly = !mapping.TypeDesc.HasDefaultConstructor;
member.Elements = new ElementAccessor[] { element };
Member[] members = new Member[] { new Member("o", "o", "a", 0, member) };
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_MoveToContent = typeof(XmlReader).GetMethod(
"MoveToContent",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_MoveToContent);
ilg.Pop();
string unknownNode = $"UnknownNode(null, {ExpectedElements(members)});";
WriteMemberElements(members, "throw CreateUnknownNodeException();", unknownNode, element.Any ? members[0] : null, null);
ilg.Ldloc(oLoc);
// for code compat as compiler does
ilg.Stloc(ilg.ReturnLocal);
ilg.Ldloc(ilg.ReturnLocal);
ilg.EndMethod();
return methodName;
}
private string NextMethodName(string name) =>
string.Create(CultureInfo.InvariantCulture, $"Read{++NextMethodNumber}_{CodeIdentifier.MakeValidInternal(name)}");
private string NextIdName(string name) =>
string.Create(CultureInfo.InvariantCulture, $"id{++_nextIdNumber}_{CodeIdentifier.MakeValidInternal(name)}");
private void WritePrimitive(TypeMapping mapping, string source)
{
System.Diagnostics.Debug.Assert(source == "Reader.ReadElementString()" || source == "Reader.ReadString()"
|| source == "false" || source == "Reader.Value" || source == "vals[i]");
if (mapping is EnumMapping)
{
string? enumMethodName = ReferenceMapping(mapping);
if (enumMethodName == null) throw new InvalidOperationException(SR.Format(SR.XmlMissingMethodEnum, mapping.TypeDesc!.Name));
// For enum, its read method (eg. Read1_Gender) could be called multiple times
// prior to its declaration.
MethodBuilder methodBuilder = EnsureMethodBuilder(typeBuilder,
enumMethodName,
CodeGenerator.PrivateMethodAttributes,
mapping.TypeDesc!.Type,
new Type[] { typeof(string) }
);
ilg.Ldarg(0);
if (source == "Reader.ReadElementString()" || source == "Reader.ReadString()")
{
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_ReadXXXString = typeof(XmlReader).GetMethod(
source == "Reader.ReadElementString()" ? "ReadElementContentAsString" : "ReadContentAsString",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_ReadXXXString);
}
else if (source == "Reader.Value")
{
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_get_Value = typeof(XmlReader).GetMethod(
"get_Value",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_Value);
}
else if (source == "vals[i]")
{
LocalBuilder locVals = ilg.GetLocal("vals");
LocalBuilder locI = ilg.GetLocal("i");
ilg.LoadArrayElement(locVals, locI);
}
else if (source == "false")
{
ilg.Ldc(false);
}
else
{
throw Globals.NotSupported($"Unexpected: {source}");
}
ilg.Call(methodBuilder);
}
else if (mapping.TypeDesc == StringTypeDesc)
{
if (source == "Reader.ReadElementString()" || source == "Reader.ReadString()")
{
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_ReadXXXString = typeof(XmlReader).GetMethod(
source == "Reader.ReadElementString()" ? "ReadElementContentAsString" : "ReadContentAsString",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_ReadXXXString);
}
else if (source == "Reader.Value")
{
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_get_Value = typeof(XmlReader).GetMethod(
"get_Value",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_Value);
}
else if (source == "vals[i]")
{
LocalBuilder locVals = ilg.GetLocal("vals");
LocalBuilder locI = ilg.GetLocal("i");
ilg.LoadArrayElement(locVals, locI);
}
else
{
throw Globals.NotSupported($"Unexpected: {source}");
}
}
else if (mapping.TypeDesc!.FormatterName == "String")
{
System.Diagnostics.Debug.Assert(source == "Reader.Value" || source == "Reader.ReadElementString()" || source == "vals[i]");
if (source == "vals[i]")
{
if (mapping.TypeDesc.CollapseWhitespace)
ilg.Ldarg(0);
LocalBuilder locVals = ilg.GetLocal("vals");
LocalBuilder locI = ilg.GetLocal("i");
ilg.LoadArrayElement(locVals, locI);
if (mapping.TypeDesc.CollapseWhitespace)
{
MethodInfo XmlSerializationReader_CollapseWhitespace = typeof(XmlSerializationReader).GetMethod(
"CollapseWhitespace",
CodeGenerator.InstanceBindingFlags,
null,
new Type[] { typeof(string) },
null
)!;
ilg.Call(XmlSerializationReader_CollapseWhitespace);
}
}
else
{
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_method = typeof(XmlReader).GetMethod(
source == "Reader.Value" ? "get_Value" : "ReadElementContentAsString",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
if (mapping.TypeDesc.CollapseWhitespace)
ilg.Ldarg(0);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_method);
if (mapping.TypeDesc.CollapseWhitespace)
{
MethodInfo XmlSerializationReader_CollapseWhitespace = typeof(XmlSerializationReader).GetMethod(
"CollapseWhitespace",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string) }
)!;
ilg.Call(XmlSerializationReader_CollapseWhitespace);
}
}
}
else
{
Type argType = source == "false" ? typeof(bool) : typeof(string);
MethodInfo ToXXX;
if (mapping.TypeDesc.HasCustomFormatter)
{
// Only these methods below that is non Static and need to ldarg("this") for Call.
BindingFlags bindingFlags = CodeGenerator.StaticBindingFlags;
if ((mapping.TypeDesc.FormatterName == "ByteArrayBase64" && source == "false")
|| (mapping.TypeDesc.FormatterName == "ByteArrayHex" && source == "false")
|| (mapping.TypeDesc.FormatterName == "XmlQualifiedName"))
{
bindingFlags = CodeGenerator.InstanceBindingFlags;
ilg.Ldarg(0);
}
ToXXX = typeof(XmlSerializationReader).GetMethod(
$"To{mapping.TypeDesc.FormatterName}",
bindingFlags,
new Type[] { argType }
)!;
}
else
{
ToXXX = typeof(XmlConvert).GetMethod(
$"To{mapping.TypeDesc.FormatterName}",
CodeGenerator.StaticBindingFlags,
new Type[] { argType }
)!;
}
if (source == "Reader.ReadElementString()" || source == "Reader.ReadString()")
{
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_ReadXXXString = typeof(XmlReader).GetMethod(
source == "Reader.ReadElementString()" ? "ReadElementContentAsString" : "ReadContentAsString",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_ReadXXXString);
}
else if (source == "Reader.Value")
{
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_get_Value = typeof(XmlReader).GetMethod(
"get_Value",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_Value);
}
else if (source == "vals[i]")
{
LocalBuilder locVals = ilg.GetLocal("vals");
LocalBuilder locI = ilg.GetLocal("i");
ilg.LoadArrayElement(locVals, locI);
}
else
{
System.Diagnostics.Debug.Assert(source == "false");
ilg.Ldc(false);
}
ilg.Call(ToXXX);
}
}
private string? MakeUnique(EnumMapping mapping, string name)
{
string uniqueName = name;
EnumMapping? m;
if (Enums.TryGetValue(uniqueName, out m))
{
if (m == mapping)
{
// we already have created the hashtable
return null;
}
int i = 0;
while (m != null)
{
i++;
uniqueName = name + i.ToString(CultureInfo.InvariantCulture);
Enums.TryGetValue(uniqueName, out m);
}
}
Enums.Add(uniqueName, mapping);
return uniqueName;
}
private string WriteHashtable(EnumMapping mapping, string typeName, out MethodBuilder? get_TableName)
{
get_TableName = null;
CodeIdentifier.CheckValidIdentifier(typeName);
string? propName = MakeUnique(mapping, $"{typeName}Values");
if (propName == null) return CodeIdentifier.GetCSharpName(typeName);
string memberName = MakeUnique(mapping, $"_{propName}")!;
propName = CodeIdentifier.GetCSharpName(propName);
FieldBuilder fieldBuilder = this.typeBuilder.DefineField(
memberName,
typeof(Hashtable),
FieldAttributes.Private
);
PropertyBuilder propertyBuilder = this.typeBuilder.DefineProperty(
propName,
PropertyAttributes.None,
CallingConventions.HasThis,
typeof(Hashtable),
null, null, null, null, null);
ilg = new CodeGenerator(this.typeBuilder);
ilg.BeginMethod(
typeof(Hashtable),
$"get_{propName}",
Type.EmptyTypes,
Array.Empty<string>(),
MethodAttributes.Assembly | MethodAttributes.HideBySig | MethodAttributes.SpecialName);
ilg.Ldarg(0);
ilg.LoadMember(fieldBuilder);
ilg.Load(null);
ilg.If(Cmp.EqualTo);
ConstructorInfo Hashtable_ctor = typeof(Hashtable).GetConstructor(
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
LocalBuilder hLoc = ilg.DeclareLocal(typeof(Hashtable), "h");
ilg.New(Hashtable_ctor);
ilg.Stloc(hLoc);
ConstantMapping[] constants = mapping.Constants!;
MethodInfo Hashtable_Add = typeof(Hashtable).GetMethod(
"Add",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(object), typeof(object) }
)!;
for (int i = 0; i < constants.Length; i++)
{
ilg.Ldloc(hLoc);
ilg.Ldstr(GetCSharpString(constants[i].XmlName));
ilg.Ldc(Enum.ToObject(mapping.TypeDesc!.Type!, constants[i].Value));
ilg.ConvertValue(mapping.TypeDesc.Type!, typeof(long));
ilg.ConvertValue(typeof(long), typeof(object));
ilg.Call(Hashtable_Add);
}
ilg.Ldarg(0);
ilg.Ldloc(hLoc);
ilg.StoreMember(fieldBuilder);
ilg.EndIf();
ilg.Ldarg(0);
ilg.LoadMember(fieldBuilder);
get_TableName = ilg.EndMethod();
propertyBuilder.SetGetMethod(get_TableName!);
return propName;
}
private void WriteEnumMethod(EnumMapping mapping)
{
MethodBuilder? get_TableName = null;
if (mapping.IsFlags)
WriteHashtable(mapping, mapping.TypeDesc!.Name, out get_TableName);
string? methodName;
MethodNames.TryGetValue(mapping, out methodName);
string fullTypeName = mapping.TypeDesc!.CSharpName;
List<Type> argTypes = new List<Type>();
List<string> argNames = new List<string>();
Type returnType;
Type underlyingType;
returnType = mapping.TypeDesc.Type!;
underlyingType = Enum.GetUnderlyingType(returnType);
argTypes.Add(typeof(string));
argNames.Add("s");
ilg = new CodeGenerator(this.typeBuilder);
ilg.BeginMethod(
returnType,
GetMethodBuilder(methodName!),
argTypes.ToArray(),
argNames.ToArray(),
CodeGenerator.PrivateMethodAttributes);
ConstantMapping[] constants = mapping.Constants!;
if (mapping.IsFlags)
{
{
MethodInfo XmlSerializationReader_ToEnum = typeof(XmlSerializationReader).GetMethod(
"ToEnum",
CodeGenerator.StaticBindingFlags,
new Type[] { typeof(string), typeof(Hashtable), typeof(string) }
)!;
ilg.Ldarg("s");
ilg.Ldarg(0);
Debug.Assert(get_TableName != null);
ilg.Call(get_TableName);
ilg.Ldstr(GetCSharpString(fullTypeName));
ilg.Call(XmlSerializationReader_ToEnum);
// XmlSerializationReader_ToEnum return long!
if (underlyingType != typeof(long))
{
ilg.ConvertValue(typeof(long), underlyingType);
}
ilg.Stloc(ilg.ReturnLocal);
ilg.Br(ilg.ReturnLabel);
}
}
else
{
List<Label> caseLabels = new List<Label>();
List<object> retValues = new List<object>();
Label defaultLabel = ilg.DefineLabel();
Label endSwitchLabel = ilg.DefineLabel();
// This local is necessary; otherwise, it becomes if/else
LocalBuilder localTmp = ilg.GetTempLocal(typeof(string));
ilg.Ldarg("s");
ilg.Stloc(localTmp);
ilg.Ldloc(localTmp);
ilg.Brfalse(defaultLabel);
var cases = new HashSet<string>();
for (int i = 0; i < constants.Length; i++)
{
ConstantMapping c = constants[i];
CodeIdentifier.CheckValidIdentifier(c.Name);
if (cases.Add(c.XmlName))
{
Label caseLabel = ilg.DefineLabel();
ilg.Ldloc(localTmp);
ilg.Ldstr(GetCSharpString(c.XmlName));
MethodInfo String_op_Equality = typeof(string).GetMethod(
"op_Equality",
CodeGenerator.StaticBindingFlags,
new Type[] { typeof(string), typeof(string) }
)!;
ilg.Call(String_op_Equality);
ilg.Brtrue(caseLabel);
caseLabels.Add(caseLabel);
retValues.Add(Enum.ToObject(mapping.TypeDesc.Type!, c.Value));
}
}
ilg.Br(defaultLabel);
// Case bodies
for (int i = 0; i < caseLabels.Count; i++)
{
ilg.MarkLabel(caseLabels[i]);
ilg.Ldc(retValues[i]);
ilg.Stloc(ilg.ReturnLocal);
ilg.Br(ilg.ReturnLabel);
}
MethodInfo XmlSerializationReader_CreateUnknownConstantException = typeof(XmlSerializationReader).GetMethod(
"CreateUnknownConstantException",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string), typeof(Type) }
)!;
// Default body
ilg.MarkLabel(defaultLabel);
ilg.Ldarg(0);
ilg.Ldarg("s");
// typeof(..)
ilg.Ldc(mapping.TypeDesc.Type!);
ilg.Call(XmlSerializationReader_CreateUnknownConstantException);
ilg.Throw();
// End switch
ilg.MarkLabel(endSwitchLabel);
}
ilg.MarkLabel(ilg.ReturnLabel);
ilg.Ldloc(ilg.ReturnLocal);
ilg.EndMethod();
}
private void WriteDerivedTypes(StructMapping mapping, bool isTypedReturn, string returnTypeName)
{
for (StructMapping? derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping)
{
ilg.InitElseIf();
WriteQNameEqual("xsiType", derived.TypeName, derived.Namespace);
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(SR.Format(SR.XmlInternalErrorMethod, derived.TypeDesc!.Name));
#endif
List<Type> argTypes = new List<Type>();
ilg.Ldarg(0);
if (derived.TypeDesc!.IsNullable)
{
ilg.Ldarg("isNullable");
argTypes.Add(typeof(bool));
}
ilg.Ldc(false);
argTypes.Add(typeof(bool));
MethodBuilder methodBuilder = EnsureMethodBuilder(typeBuilder,
methodName!,
CodeGenerator.PrivateMethodAttributes,
derived.TypeDesc.Type,
argTypes.ToArray()
);
ilg.Call(methodBuilder);
ilg.ConvertValue(methodBuilder.ReturnType, ilg.ReturnLocal.LocalType);
ilg.Stloc(ilg.ReturnLocal);
ilg.Br(ilg.ReturnLabel);
WriteDerivedTypes(derived, isTypedReturn, returnTypeName);
}
}
private void WriteEnumAndArrayTypes()
{
foreach (TypeScope scope in Scopes)
{
foreach (Mapping m in scope.TypeMappings)
{
if (m is EnumMapping enumMapping)
{
ilg.InitElseIf();
WriteQNameEqual("xsiType", enumMapping.TypeName, enumMapping.Namespace);
ilg.AndIf();
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_ReadStartElement = typeof(XmlReader).GetMethod(
"ReadStartElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_ReadStartElement);
string? methodName = ReferenceMapping(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 (methodName == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorMethod, enumMapping.TypeDesc!.Name));
#endif
LocalBuilder eLoc = ilg.DeclareOrGetLocal(typeof(object), "e");
MethodBuilder methodBuilder = EnsureMethodBuilder(typeBuilder,
methodName!,
CodeGenerator.PrivateMethodAttributes,
enumMapping.TypeDesc!.Type,
new Type[] { typeof(string) }
);
MethodInfo XmlSerializationReader_CollapseWhitespace = typeof(XmlSerializationReader).GetMethod(
"CollapseWhitespace",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string) }
)!;
MethodInfo XmlReader_ReadString = typeof(XmlReader).GetMethod(
"ReadContentAsString",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Ldarg(0);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_ReadString);
ilg.Call(XmlSerializationReader_CollapseWhitespace);
ilg.Call(methodBuilder);
ilg.ConvertValue(methodBuilder.ReturnType, eLoc.LocalType);
ilg.Stloc(eLoc);
MethodInfo XmlSerializationReader_ReadEndElement = typeof(XmlSerializationReader).GetMethod(
"ReadEndElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_ReadEndElement);
ilg.Ldloc(eLoc);
ilg.Stloc(ilg.ReturnLocal);
ilg.Br(ilg.ReturnLabel);
// Caller own calling ilg.EndIf();
}
else if (m is ArrayMapping arrayMapping)
{
if (arrayMapping.TypeDesc!.HasDefaultConstructor)
{
ilg.InitElseIf();
WriteQNameEqual("xsiType", arrayMapping.TypeName, arrayMapping.Namespace);
ilg.AndIf();
ilg.EnterScope();
MemberMapping memberMapping = new MemberMapping();
memberMapping.TypeDesc = arrayMapping.TypeDesc;
memberMapping.Elements = arrayMapping.Elements;
string aVar = "a";
string zVar = "z";
Member member = new Member(aVar, zVar, 0, memberMapping);
TypeDesc td = arrayMapping.TypeDesc;
LocalBuilder aLoc = ilg.DeclareLocal(arrayMapping.TypeDesc.Type!, aVar);
if (arrayMapping.TypeDesc.IsValueType)
{
ReflectionAwareILGen.ILGenForCreateInstance(ilg, td.Type!, false, false);
}
else
ilg.Load(null);
ilg.Stloc(aLoc);
WriteArray(member.Source, member.ArrayName, arrayMapping, false, false, 0);
ilg.Ldloc(aLoc);
ilg.Stloc(ilg.ReturnLocal);
ilg.Br(ilg.ReturnLabel);
ilg.ExitScope();
// Caller own calling ilg.EndIf();
}
}
}
}
}
private void WriteNullableMethod(NullableMapping nullableMapping)
{
string? methodName;
MethodNames.TryGetValue(nullableMapping, out methodName);
ilg = new CodeGenerator(this.typeBuilder);
ilg.BeginMethod(
nullableMapping.TypeDesc!.Type!,
GetMethodBuilder(methodName!),
s_boolType,
s_checkTypeString,
CodeGenerator.PrivateMethodAttributes);
LocalBuilder oLoc = ilg.DeclareLocal(nullableMapping.TypeDesc.Type!, "o");
ilg.LoadAddress(oLoc);
ilg.InitObj(nullableMapping.TypeDesc.Type!);
MethodInfo XmlSerializationReader_ReadNull = typeof(XmlSerializationReader).GetMethod(
"ReadNull",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_ReadNull);
ilg.If();
{
ilg.Ldloc(oLoc);
ilg.Stloc(ilg.ReturnLocal);
ilg.Br(ilg.ReturnLabel);
}
ilg.EndIf();
ElementAccessor element = new ElementAccessor();
element.Mapping = nullableMapping.BaseMapping;
element.Any = false;
element.IsNullable = nullableMapping.BaseMapping!.TypeDesc!.IsNullable;
WriteElement("o", null, null, element, null, null, false, false, -1, -1);
ilg.Ldloc(oLoc);
ilg.Stloc(ilg.ReturnLocal);
ilg.Br(ilg.ReturnLabel);
ilg.MarkLabel(ilg.ReturnLabel);
ilg.Ldloc(ilg.ReturnLocal);
ilg.EndMethod();
}
private void WriteStructMethod(StructMapping structMapping)
{
WriteLiteralStructMethod(structMapping);
}
private void WriteLiteralStructMethod(StructMapping structMapping)
{
string? methodName;
MethodNames.TryGetValue(structMapping, out methodName);
string typeName = structMapping.TypeDesc!.CSharpName;
ilg = new CodeGenerator(this.typeBuilder);
List<Type> argTypes = new List<Type>();
List<string> argNames = new List<string>();
if (structMapping.TypeDesc.IsNullable)
{
argTypes.Add(typeof(bool));
argNames.Add("isNullable");
}
argTypes.Add(typeof(bool));
argNames.Add("checkType");
ilg.BeginMethod(
structMapping.TypeDesc.Type!,
GetMethodBuilder(methodName!),
argTypes.ToArray(),
argNames.ToArray(),
CodeGenerator.PrivateMethodAttributes);
LocalBuilder locXsiType = ilg.DeclareLocal(typeof(XmlQualifiedName), "xsiType");
LocalBuilder locIsNull = ilg.DeclareLocal(typeof(bool), "isNull");
MethodInfo XmlSerializationReader_GetXsiType = typeof(XmlSerializationReader).GetMethod(
"GetXsiType",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlSerializationReader_ReadNull = typeof(XmlSerializationReader).GetMethod(
"ReadNull",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
Label labelTrue = ilg.DefineLabel();
Label labelEnd = ilg.DefineLabel();
ilg.Ldarg("checkType");
ilg.Brtrue(labelTrue);
ilg.Load(null);
ilg.Br_S(labelEnd);
ilg.MarkLabel(labelTrue);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_GetXsiType);
ilg.MarkLabel(labelEnd);
ilg.Stloc(locXsiType);
ilg.Ldc(false);
ilg.Stloc(locIsNull);
if (structMapping.TypeDesc.IsNullable)
{
ilg.Ldarg("isNullable");
ilg.If();
{
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_ReadNull);
ilg.Stloc(locIsNull);
}
ilg.EndIf();
}
ilg.Ldarg("checkType");
ilg.If(); // if (checkType)
if (structMapping.TypeDesc.IsRoot)
{
ilg.Ldloc(locIsNull);
ilg.If();
ilg.Ldloc(locXsiType);
ilg.Load(null);
ilg.If(Cmp.NotEqualTo);
MethodInfo XmlSerializationReader_ReadTypedNull = typeof(XmlSerializationReader).GetMethod(
"ReadTypedNull",
CodeGenerator.InstanceBindingFlags,
new Type[] { locXsiType.LocalType }
)!;
ilg.Ldarg(0);
ilg.Ldloc(locXsiType);
ilg.Call(XmlSerializationReader_ReadTypedNull);
ilg.Stloc(ilg.ReturnLocal);
ilg.Br(ilg.ReturnLabel);
ilg.Else();
if (structMapping.TypeDesc.IsValueType)
{
throw Globals.NotSupported(SR.Arg_NeverValueType);
}
else
{
ilg.Load(null);
ilg.Stloc(ilg.ReturnLocal);
ilg.Br(ilg.ReturnLabel);
}
ilg.EndIf(); // if (xsiType != null)
ilg.EndIf(); // if (isNull)
}
ilg.Ldloc(typeof(XmlQualifiedName), "xsiType");
ilg.Load(null);
ilg.Ceq();
if (!structMapping.TypeDesc.IsRoot)
{
labelTrue = ilg.DefineLabel();
labelEnd = ilg.DefineLabel();
// xsiType == null
ilg.Brtrue(labelTrue);
WriteQNameEqual("xsiType", structMapping.TypeName, structMapping.Namespace);
// Bool result for WriteQNameEqual is on the stack
ilg.Br_S(labelEnd);
ilg.MarkLabel(labelTrue);
ilg.Ldc(true);
ilg.MarkLabel(labelEnd);
}
ilg.If(); // if (xsiType == null
if (structMapping.TypeDesc.IsRoot)
{
ConstructorInfo XmlQualifiedName_ctor = typeof(XmlQualifiedName).GetConstructor(
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string), typeof(string) }
)!;
MethodInfo XmlSerializationReader_ReadTypedPrimitive = typeof(XmlSerializationReader).GetMethod(
"ReadTypedPrimitive",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(XmlQualifiedName) }
)!;
ilg.Ldarg(0);
ilg.Ldstr(Soap.UrType);
ilg.Ldstr(XmlSchema.Namespace);
ilg.New(XmlQualifiedName_ctor);
ilg.Call(XmlSerializationReader_ReadTypedPrimitive);
ilg.Stloc(ilg.ReturnLocal);
ilg.Br(ilg.ReturnLabel);
}
WriteDerivedTypes(structMapping, !structMapping.TypeDesc.IsRoot, typeName);
if (structMapping.TypeDesc.IsRoot) WriteEnumAndArrayTypes();
ilg.Else(); // if (xsiType == null
if (structMapping.TypeDesc.IsRoot)
{
MethodInfo XmlSerializationReader_ReadTypedPrimitive = typeof(XmlSerializationReader).GetMethod(
"ReadTypedPrimitive",
CodeGenerator.InstanceBindingFlags,
new Type[] { locXsiType.LocalType }
)!;
ilg.Ldarg(0);
ilg.Ldloc(locXsiType);
ilg.Call(XmlSerializationReader_ReadTypedPrimitive);
ilg.Stloc(ilg.ReturnLocal);
ilg.Br(ilg.ReturnLabel);
}
else
{
MethodInfo XmlSerializationReader_CreateUnknownTypeException = typeof(XmlSerializationReader).GetMethod(
"CreateUnknownTypeException",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(XmlQualifiedName) }
)!;
ilg.Ldarg(0);
ilg.Ldloc(locXsiType);
ilg.Call(XmlSerializationReader_CreateUnknownTypeException);
ilg.Throw();
}
ilg.EndIf(); // if (xsiType == null
ilg.EndIf(); // checkType
if (structMapping.TypeDesc.IsNullable)
{
ilg.Ldloc(typeof(bool), "isNull");
ilg.If();
{
ilg.Load(null);
ilg.Stloc(ilg.ReturnLocal);
ilg.Br(ilg.ReturnLabel);
}
ilg.EndIf();
}
if (structMapping.TypeDesc.IsAbstract)
{
MethodInfo XmlSerializationReader_CreateAbstractTypeException = typeof(XmlSerializationReader).GetMethod(
"CreateAbstractTypeException",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string), typeof(string) }
)!;
ilg.Ldarg(0);
ilg.Ldstr(GetCSharpString(structMapping.TypeName));
ilg.Ldstr(GetCSharpString(structMapping.Namespace));
ilg.Call(XmlSerializationReader_CreateAbstractTypeException);
ilg.Throw();
}
else
{
if (structMapping.TypeDesc.Type != null && typeof(XmlSchemaObject).IsAssignableFrom(structMapping.TypeDesc.Type))
{
MethodInfo XmlSerializationReader_set_DecodeName = typeof(XmlSerializationReader).GetMethod(
"set_DecodeName",
CodeGenerator.InstanceBindingFlags,
s_boolType
)!;
ilg.Ldarg(0);
ilg.Ldc(false);
ilg.Call(XmlSerializationReader_set_DecodeName);
}
WriteCreateMapping(structMapping, "o");
LocalBuilder oLoc = ilg.GetLocal("o");
// this method populates the memberInfos dictionary based on the structMapping
MemberMapping[] mappings = TypeScope.GetSettableMembers(structMapping, memberInfos);
Member? anyText = null;
Member? anyElement = null;
Member? anyAttribute = null;
bool isSequence = structMapping.HasExplicitSequence();
var arraysToDeclareList = new List<Member>(mappings.Length);
var arraysToSetList = new List<Member>(mappings.Length);
var allMembersList = new List<Member>(mappings.Length);
for (int i = 0; i < mappings.Length; i++)
{
MemberMapping mapping = mappings[i];
CodeIdentifier.CheckValidIdentifier(mapping.Name);
string source = ReflectionAwareILGen.GetStringForMember("o", mapping.Name);
Member member = new Member(source, "a", i, mapping, GetChoiceIdentifierSource(mapping, "o"));
if (!mapping.IsSequence)
member.ParamsReadSource = $"paramsRead[{i}]";
member.IsNullable = mapping.TypeDesc!.IsNullable;
if (mapping.CheckSpecified == SpecifiedAccessor.ReadWrite)
member.CheckSpecifiedSource = ReflectionAwareILGen.GetStringForMember("o", $"{mapping.Name}Specified");
if (mapping.Text != null)
anyText = member;
if (mapping.Attribute != null && mapping.Attribute.Any)
anyAttribute = member;
if (!isSequence)
{
// find anyElement if present.
for (int j = 0; j < mapping.Elements!.Length; j++)
{
if (mapping.Elements[j].Any && string.IsNullOrEmpty(mapping.Elements[j].Name))
{
anyElement = member;
break;
}
}
}
else if (mapping.IsParticle && !mapping.IsSequence)
{
StructMapping? declaringMapping;
structMapping.FindDeclaringMapping(mapping, out declaringMapping, structMapping.TypeName);
throw new InvalidOperationException(SR.Format(SR.XmlSequenceHierarchy, structMapping.TypeDesc.FullName, mapping.Name, declaringMapping!.TypeDesc!.FullName, "Order"));
}
if (mapping.Attribute == null && mapping.Elements!.Length == 1 && mapping.Elements[0].Mapping is ArrayMapping)
{
Member arrayMember = new Member(source, source, "a", i, mapping, GetChoiceIdentifierSource(mapping, "o"));
arrayMember.CheckSpecifiedSource = member.CheckSpecifiedSource;
allMembersList.Add(arrayMember);
}
else
{
allMembersList.Add(member);
}
if (mapping.TypeDesc.IsArrayLike)
{
arraysToDeclareList.Add(member);
if (mapping.TypeDesc.IsArrayLike && !(mapping.Elements!.Length == 1 && mapping.Elements[0].Mapping is ArrayMapping))
{
member.ParamsReadSource = null; // flat arrays -- don't want to count params read.
if (member != anyText && member != anyElement)
{
arraysToSetList.Add(member);
}
}
else if (!mapping.TypeDesc.IsArray)
{
member.ParamsReadSource = null; // collection
}
}
}
if (anyElement != null) arraysToSetList.Add(anyElement);
if (anyText != null && anyText != anyElement) arraysToSetList.Add(anyText);
Member[] arraysToDeclare = arraysToDeclareList.ToArray();
Member[] arraysToSet = arraysToSetList.ToArray();
Member[] allMembers = allMembersList.ToArray();
WriteMemberBegin(arraysToDeclare);
WriteParamsRead(mappings.Length);
WriteAttributes(allMembers, anyAttribute, "UnknownNode", oLoc);
if (anyAttribute != null)
WriteMemberEnd(arraysToDeclare);
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_MoveToElement = typeof(XmlReader).GetMethod(
"MoveToElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_MoveToElement);
ilg.Pop();
MethodInfo XmlReader_get_IsEmptyElement = typeof(XmlReader).GetMethod(
"get_IsEmptyElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_IsEmptyElement);
ilg.If();
MethodInfo XmlReader_Skip = typeof(XmlReader).GetMethod(
"Skip",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_Skip);
WriteMemberEnd(arraysToSet);
ilg.Ldloc(oLoc);
ilg.Stloc(ilg.ReturnLocal);
ilg.Br(ilg.ReturnLabel);
ilg.EndIf();
MethodInfo XmlReader_ReadStartElement = typeof(XmlReader).GetMethod(
"ReadStartElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_ReadStartElement);
if (IsSequence(allMembers))
{
ilg.Ldc(0);
ilg.Stloc(typeof(int), "state");
}
WriteWhileNotLoopStart();
string unknownNode = $"UnknownNode((object)o, {ExpectedElements(allMembers)});";
WriteMemberElements(allMembers, unknownNode, unknownNode, anyElement, anyText);
MethodInfo XmlReader_MoveToContent = typeof(XmlReader).GetMethod(
"MoveToContent",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_MoveToContent);
ilg.Pop();
WriteWhileLoopEnd();
WriteMemberEnd(arraysToSet);
MethodInfo XmlSerializationReader_ReadEndElement = typeof(XmlSerializationReader).GetMethod(
"ReadEndElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_ReadEndElement);
ilg.Ldloc(structMapping.TypeDesc.Type!, "o");
ilg.Stloc(ilg.ReturnLocal);
}
ilg.MarkLabel(ilg.ReturnLabel);
ilg.Ldloc(ilg.ReturnLocal);
ilg.EndMethod();
}
private void WriteQNameEqual(string source, string? name, string? ns)
{
WriteID(name);
WriteID(ns);
// This api assume the source is local member of XmlQualifiedName type
// It leaves bool result on the stack
MethodInfo XmlQualifiedName_get_Name = typeof(XmlQualifiedName).GetMethod(
"get_Name",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlQualifiedName_get_Namespace = typeof(XmlQualifiedName).GetMethod(
"get_Namespace",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
Label labelEnd = ilg.DefineLabel();
Label labelFalse = ilg.DefineLabel();
LocalBuilder sLoc = ilg.GetLocal(source);
ilg.Ldloc(sLoc);
ilg.Call(XmlQualifiedName_get_Name);
ilg.Ldarg(0);
ilg.LoadMember(_idNameFields[name ?? string.Empty]);
ilg.Bne(labelFalse);
ilg.Ldloc(sLoc);
ilg.Call(XmlQualifiedName_get_Namespace);
ilg.Ldarg(0);
ilg.LoadMember(_idNameFields[ns ?? string.Empty]);
ilg.Ceq();
ilg.Br_S(labelEnd);
ilg.MarkLabel(labelFalse);
ilg.Ldc(false);
ilg.MarkLabel(labelEnd);
}
private void WriteXmlNodeEqual(string source, string name, string? ns)
{
WriteXmlNodeEqual(source, name, ns, true);
}
private void WriteXmlNodeEqual(string source, string name, string? ns, bool doAndIf)
{
bool isNameNullOrEmpty = string.IsNullOrEmpty(name);
if (!isNameNullOrEmpty)
{
WriteID(name);
}
WriteID(ns);
// Only support Reader and XmlSerializationReaderReader only
System.Diagnostics.Debug.Assert(source == "Reader");
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
$"get_{source}",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_get_LocalName = typeof(XmlReader).GetMethod(
"get_LocalName",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_get_NamespaceURI = typeof(XmlReader).GetMethod(
"get_NamespaceURI",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
Label labelFalse = ilg.DefineLabel();
Label labelEnd = ilg.DefineLabel();
if (!isNameNullOrEmpty)
{
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_LocalName);
ilg.Ldarg(0);
ilg.LoadMember(_idNameFields[name ?? string.Empty]);
ilg.Bne(labelFalse);
}
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_NamespaceURI);
ilg.Ldarg(0);
ilg.LoadMember(_idNameFields[ns ?? string.Empty]);
ilg.Ceq();
if (!isNameNullOrEmpty)
{
ilg.Br_S(labelEnd);
ilg.MarkLabel(labelFalse);
ilg.Ldc(false);
ilg.MarkLabel(labelEnd);
}
if (doAndIf)
ilg.AndIf();
}
private void WriteID(string? name)
{
name ??= "";
if (!_idNames.ContainsKey(name))
{
string? idName = NextIdName(name);
_idNames.Add(name, idName);
_idNameFields.Add(name, this.typeBuilder.DefineField(idName, typeof(string), FieldAttributes.Private));
}
}
private void WriteAttributes(Member[] members, Member? anyAttribute, string elseCall, LocalBuilder firstParam)
{
int count = 0;
Member? xmlnsMember = null;
var attributes = new List<AttributeAccessor>();
// Condition do at the end, so C# looks the same
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_MoveToNextAttribute = typeof(XmlReader).GetMethod(
"MoveToNextAttribute",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.WhileBegin();
for (int i = 0; i < members.Length; i++)
{
Member member = (Member)members[i];
if (member.Mapping.Xmlns != null)
{
xmlnsMember = member;
continue;
}
if (member.Mapping.Ignore)
continue;
AttributeAccessor? attribute = member.Mapping.Attribute;
if (attribute == null) continue;
if (attribute.Any) continue;
attributes.Add(attribute);
if (count++ > 0)
ilg.InitElseIf();
else
ilg.InitIf();
if (member.ParamsReadSource != null)
{
ILGenParamsReadSource(member.ParamsReadSource);
ilg.Ldc(false);
ilg.AndIf(Cmp.EqualTo);
}
if (attribute.IsSpecialXmlNamespace)
{
WriteXmlNodeEqual("Reader", attribute.Name, XmlReservedNs.NsXml);
}
else
WriteXmlNodeEqual("Reader", attribute.Name, attribute.Form == XmlSchemaForm.Qualified ? attribute.Namespace : "");
WriteAttribute(member);
}
if (count > 0)
ilg.InitElseIf();
else
ilg.InitIf();
if (xmlnsMember != null)
{
MethodInfo XmlSerializationReader_IsXmlnsAttribute = typeof(XmlSerializationReader).GetMethod(
"IsXmlnsAttribute",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string) }
)!;
MethodInfo XmlReader_get_Name = typeof(XmlReader).GetMethod(
"get_Name",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_get_LocalName = typeof(XmlReader).GetMethod(
"get_LocalName",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_get_Value = typeof(XmlReader).GetMethod(
"get_Value",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_Name);
ilg.Call(XmlSerializationReader_IsXmlnsAttribute);
ilg.Ldc(true);
ilg.AndIf(Cmp.EqualTo);
ILGenLoad(xmlnsMember.Source);
ilg.Load(null);
ilg.If(Cmp.EqualTo);
WriteSourceBegin(xmlnsMember.Source);
ConstructorInfo ctor = xmlnsMember.Mapping.TypeDesc!.Type!.GetConstructor(
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.New(ctor);
WriteSourceEnd(xmlnsMember.Source, xmlnsMember.Mapping.TypeDesc.Type!);
ilg.EndIf(); // if (xmlnsMember.Source == null
Label labelEqual5 = ilg.DefineLabel();
Label labelEndLength = ilg.DefineLabel();
MethodInfo Add = xmlnsMember.Mapping.TypeDesc.Type!.GetMethod(
"Add",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string), typeof(string) }
)!;
MethodInfo String_get_Length = typeof(string).GetMethod(
"get_Length",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ILGenLoad(xmlnsMember.ArraySource, xmlnsMember.Mapping.TypeDesc.Type);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_Name);
ilg.Call(String_get_Length);
ilg.Ldc(5);
ilg.Beq(labelEqual5);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_LocalName);
ilg.Br(labelEndLength);
ilg.MarkLabel(labelEqual5);
ilg.Ldstr(string.Empty);
ilg.MarkLabel(labelEndLength);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_Value);
ilg.Call(Add);
ilg.Else();
}
else
{
MethodInfo XmlSerializationReader_IsXmlnsAttribute = typeof(XmlSerializationReader).GetMethod(
"IsXmlnsAttribute",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string) }
)!;
MethodInfo XmlReader_get_Name = typeof(XmlReader).GetMethod(
"get_Name",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_Name);
ilg.Call(XmlSerializationReader_IsXmlnsAttribute);
ilg.Ldc(false);
ilg.AndIf(Cmp.EqualTo);
}
if (anyAttribute != null)
{
LocalBuilder localAttr = ilg.DeclareOrGetLocal(typeof(XmlAttribute), "attr");
MethodInfo XmlSerializationReader_get_Document = typeof(XmlSerializationReader).GetMethod(
"get_Document",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlDocument_ReadNode = typeof(XmlDocument).GetMethod(
"ReadNode",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(XmlReader) }
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Document);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlDocument_ReadNode);
ilg.ConvertValue(XmlDocument_ReadNode.ReturnType, localAttr.LocalType);
ilg.Stloc(localAttr);
MethodInfo XmlSerializationReader_ParseWsdlArrayType = typeof(XmlSerializationReader).GetMethod(
"ParseWsdlArrayType",
CodeGenerator.InstanceBindingFlags,
new Type[] { localAttr.LocalType }
)!;
ilg.Ldarg(0);
ilg.Ldloc(localAttr);
ilg.Call(XmlSerializationReader_ParseWsdlArrayType);
WriteAttribute(anyAttribute);
}
else
{
List<Type> argTypes = new List<Type>();
ilg.Ldarg(0);
argTypes.Add(typeof(object));
ilg.Ldloc(firstParam);
ilg.ConvertValue(firstParam.LocalType, typeof(object));
if (attributes.Count > 0)
{
string qnames = "";
for (int i = 0; i < attributes.Count; i++)
{
AttributeAccessor attribute = attributes[i];
if (i > 0)
qnames += ", ";
qnames += attribute.IsSpecialXmlNamespace ? XmlReservedNs.NsXml : $"{(attribute.Form == XmlSchemaForm.Qualified ? attribute.Namespace : "")}:{attribute.Name}";
}
argTypes.Add(typeof(string));
ilg.Ldstr(qnames);
}
System.Diagnostics.Debug.Assert(elseCall == "UnknownNode");
MethodInfo elseCallMethod = typeof(XmlSerializationReader).GetMethod(
elseCall,
CodeGenerator.InstanceBindingFlags,
argTypes.ToArray()
)!;
ilg.Call(elseCallMethod);
}
ilg.EndIf();
ilg.WhileBeginCondition();
{
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_MoveToNextAttribute);
}
ilg.WhileEndCondition();
ilg.WhileEnd();
}
private void WriteAttribute(Member member)
{
AttributeAccessor attribute = member.Mapping.Attribute!;
if (attribute.Mapping is SpecialMapping special)
{
if (special.TypeDesc!.Kind == TypeKind.Attribute)
{
WriteSourceBegin(member.ArraySource);
ilg.Ldloc("attr");
WriteSourceEnd(member.ArraySource, member.Mapping.TypeDesc!.IsArrayLike ? member.Mapping.TypeDesc.ArrayElementTypeDesc!.Type! : member.Mapping.TypeDesc.Type!);
}
else if (special.TypeDesc.CanBeAttributeValue)
{
LocalBuilder attrLoc = ilg.GetLocal("attr");
ilg.Ldloc(attrLoc);
// to get code compat
if (attrLoc.LocalType == typeof(XmlAttribute))
{
ilg.Load(null);
ilg.Cne();
}
else
ilg.IsInst(typeof(XmlAttribute));
ilg.If();
WriteSourceBegin(member.ArraySource);
ilg.Ldloc(attrLoc);
ilg.ConvertValue(attrLoc.LocalType, typeof(XmlAttribute));
WriteSourceEnd(member.ArraySource, member.Mapping.TypeDesc!.IsArrayLike ? member.Mapping.TypeDesc.ArrayElementTypeDesc!.Type! : member.Mapping.TypeDesc.Type!);
ilg.EndIf();
}
else
throw new InvalidOperationException(SR.XmlInternalError);
}
else
{
if (attribute.IsList)
{
LocalBuilder locListValues = ilg.DeclareOrGetLocal(typeof(string), "listValues");
LocalBuilder locVals = ilg.DeclareOrGetLocal(typeof(string[]), "vals");
MethodInfo String_Split = typeof(string).GetMethod(
"Split",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(char[]) }
)!;
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_get_Value = typeof(XmlReader).GetMethod(
"get_Value",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_Value);
ilg.Stloc(locListValues);
ilg.Ldloc(locListValues);
ilg.Load(null);
ilg.Call(String_Split);
ilg.Stloc(locVals);
LocalBuilder localI = ilg.DeclareOrGetLocal(typeof(int), "i");
ilg.For(localI, 0, locVals);
string attributeSource = GetArraySource(member.Mapping.TypeDesc!, member.ArrayName);
WriteSourceBegin(attributeSource);
WritePrimitive(attribute.Mapping!, "vals[i]");
WriteSourceEnd(attributeSource, member.Mapping.TypeDesc!.ArrayElementTypeDesc!.Type!);
ilg.EndFor();
}
else
{
WriteSourceBegin(member.ArraySource);
WritePrimitive(attribute.Mapping!, attribute.IsList ? "vals[i]" : "Reader.Value");
WriteSourceEnd(member.ArraySource, member.Mapping.TypeDesc!.IsArrayLike ? member.Mapping.TypeDesc.ArrayElementTypeDesc!.Type! : member.Mapping.TypeDesc.Type!);
}
}
if (member.Mapping.CheckSpecified == SpecifiedAccessor.ReadWrite && member.CheckSpecifiedSource != null && member.CheckSpecifiedSource.Length > 0)
{
ILGenSet(member.CheckSpecifiedSource, true);
}
if (member.ParamsReadSource != null)
{
ILGenParamsReadSource(member.ParamsReadSource, true);
}
}
private void WriteMemberBegin(Member[] members)
{
for (int i = 0; i < members.Length; i++)
{
Member member = (Member)members[i];
if (member.IsArrayLike)
{
string a = member.ArrayName;
string c = $"c{a}";
TypeDesc typeDesc = member.Mapping.TypeDesc!;
if (member.Mapping.TypeDesc!.IsArray)
{
WriteArrayLocalDecl(typeDesc.CSharpName,
a, "null", typeDesc);
ilg.Ldc(0);
ilg.Stloc(typeof(int), c);
if (member.Mapping.ChoiceIdentifier != null)
{
WriteArrayLocalDecl($"{member.Mapping.ChoiceIdentifier.Mapping!.TypeDesc!.CSharpName}[]",
member.ChoiceArrayName, "null",
member.Mapping.ChoiceIdentifier.Mapping.TypeDesc);
ilg.Ldc(0);
ilg.Stloc(typeof(int), $"c{member.ChoiceArrayName}");
}
}
else
{
if (member.Source.EndsWith('(') || member.Source.EndsWith('{'))
{
WriteCreateInstance(a, typeDesc.CannotNew, typeDesc.Type!);
WriteSourceBegin(member.Source);
ilg.Ldloc(ilg.GetLocal(a));
WriteSourceEnd(member.Source, typeDesc.Type!);
}
else
{
if (member.IsList && !member.Mapping.ReadOnly) //&& member.Mapping.TypeDesc.IsNullable) // nullable or not, we are likely to assign null in the next step if we don't do this initialization. So just do this.
{
// we need to new the Collections and ArrayLists
ILGenLoad(member.Source, typeof(object));
ilg.Load(null);
ilg.If(Cmp.EqualTo);
if (!member.Mapping.TypeDesc.HasDefaultConstructor)
{
MethodInfo XmlSerializationReader_CreateReadOnlyCollectionException = typeof(XmlSerializationReader).GetMethod(
"CreateReadOnlyCollectionException",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string) }
)!;
ilg.Ldarg(0);
ilg.Ldstr(GetCSharpString(member.Mapping.TypeDesc.CSharpName));
ilg.Call(XmlSerializationReader_CreateReadOnlyCollectionException);
ilg.Throw();
}
else
{
WriteSourceBegin(member.Source);
ReflectionAwareILGen.ILGenForCreateInstance(ilg, member.Mapping.TypeDesc.Type!, typeDesc.CannotNew, true);
WriteSourceEnd(member.Source, member.Mapping.TypeDesc.Type!);
}
ilg.EndIf(); // if ((object)(member.Source) == null
}
WriteLocalDecl(a, new SourceInfo(member.Source, member.Source, member.Mapping.MemberInfo, member.Mapping.TypeDesc.Type, ilg));
}
}
}
}
}
private static string ExpectedElements(Member[] members)
{
if (IsSequence(members))
return "null";
string qnames = string.Empty;
bool firstElement = true;
for (int i = 0; i < members.Length; i++)
{
Member member = (Member)members[i];
if (member.Mapping.Xmlns != null)
continue;
if (member.Mapping.Ignore)
continue;
if (member.Mapping.IsText || member.Mapping.IsAttribute)
continue;
ElementAccessor[] elements = member.Mapping.Elements!;
for (int j = 0; j < elements.Length; j++)
{
ElementAccessor e = elements[j];
string? ns = e.Form == XmlSchemaForm.Qualified ? e.Namespace : "";
if (e.Any && string.IsNullOrEmpty(e.Name)) continue;
if (!firstElement)
qnames += ", ";
qnames += $"{ns}:{e.Name}";
firstElement = false;
}
}
return ReflectionAwareILGen.GetQuotedCSharpString(qnames);
}
private void WriteMemberElements(Member[] members, string elementElseString, string elseString, Member? anyElement, Member? anyText)
{
if (anyText != null)
{
ilg.Load(null);
ilg.Stloc(typeof(string), "tmp");
}
MethodInfo XmlReader_get_NodeType = typeof(XmlReader).GetMethod(
"get_NodeType",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
int XmlNodeType_Element = 1;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_NodeType);
ilg.Ldc(XmlNodeType_Element);
ilg.If(Cmp.EqualTo);
WriteMemberElementsIf(members, anyElement, elementElseString);
if (anyText != null)
WriteMemberText(anyText);
ilg.Else();
ILGenElseString(elseString);
ilg.EndIf();
}
private void WriteMemberText(Member anyText)
{
ilg.InitElseIf();
Label labelTrue = ilg.DefineLabel();
Label labelEnd = ilg.DefineLabel();
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_get_NodeType = typeof(XmlReader).GetMethod(
"get_NodeType",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_NodeType);
ilg.Ldc(XmlNodeType.Text);
ilg.Ceq();
ilg.Brtrue(labelTrue);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_NodeType);
ilg.Ldc(XmlNodeType.CDATA);
ilg.Ceq();
ilg.Brtrue(labelTrue);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_NodeType);
ilg.Ldc(XmlNodeType.Whitespace);
ilg.Ceq();
ilg.Brtrue(labelTrue);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_NodeType);
ilg.Ldc(XmlNodeType.SignificantWhitespace);
ilg.Ceq();
ilg.Br(labelEnd);
ilg.MarkLabel(labelTrue);
ilg.Ldc(true);
ilg.MarkLabel(labelEnd);
ilg.AndIf();
if (anyText != null)
{
WriteText(anyText);
}
Debug.Assert(anyText != null);
}
private void WriteText(Member member)
{
TextAccessor text = member.Mapping.Text!;
if (text.Mapping is SpecialMapping special)
{
WriteSourceBeginTyped(member.ArraySource);
switch (special.TypeDesc!.Kind)
{
case TypeKind.Node:
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_ReadString = typeof(XmlReader).GetMethod(
"ReadContentAsString",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlSerializationReader_get_Document = typeof(XmlSerializationReader).GetMethod(
"get_Document",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlDocument_CreateTextNode = typeof(XmlDocument).GetMethod(
"CreateTextNode",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string) }
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Document);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_ReadString);
ilg.Call(XmlDocument_CreateTextNode);
break;
default:
throw new InvalidOperationException(SR.XmlInternalError);
}
WriteSourceEnd(member.ArraySource, special.TypeDesc.Type!);
}
else
{
if (member.IsArrayLike)
{
WriteSourceBegin(member.ArraySource);
if (text.Mapping!.TypeDesc!.CollapseWhitespace)
{
ilg.Ldarg(0); // for calling CollapseWhitespace
}
else
{
}
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_ReadString = typeof(XmlReader).GetMethod(
"ReadContentAsString",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_ReadString);
if (text.Mapping.TypeDesc.CollapseWhitespace)
{
MethodInfo XmlSerializationReader_CollapseWhitespace = typeof(XmlSerializationReader).GetMethod(
"CollapseWhitespace",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string) }
)!;
ilg.Call(XmlSerializationReader_CollapseWhitespace);
}
}
else
{
if (text.Mapping!.TypeDesc == StringTypeDesc || text.Mapping.TypeDesc!.FormatterName == "String")
{
LocalBuilder tmpLoc = ilg.GetLocal("tmp");
MethodInfo XmlSerializationReader_ReadString = typeof(XmlSerializationReader).GetMethod(
"ReadString",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string), typeof(bool) }
)!;
ilg.Ldarg(0);
ilg.Ldloc(tmpLoc);
ilg.Ldc(text.Mapping.TypeDesc!.CollapseWhitespace);
ilg.Call(XmlSerializationReader_ReadString);
ilg.Stloc(tmpLoc);
WriteSourceBegin(member.ArraySource);
ilg.Ldloc(tmpLoc);
}
else
{
WriteSourceBegin(member.ArraySource);
WritePrimitive(text.Mapping, "Reader.ReadString()");
}
}
WriteSourceEnd(member.ArraySource, text.Mapping.TypeDesc.Type!);
}
}
private void WriteMemberElementsElse(Member? anyElement, string elementElseString)
{
if (anyElement != null)
{
ElementAccessor[] elements = anyElement.Mapping.Elements!;
for (int i = 0; i < elements.Length; i++)
{
ElementAccessor element = elements[i];
if (element.Any && element.Name.Length == 0)
{
WriteElement(anyElement.ArraySource, anyElement.ArrayName, anyElement.ChoiceArraySource, element, anyElement.Mapping.ChoiceIdentifier, anyElement.Mapping.CheckSpecified == SpecifiedAccessor.ReadWrite ? anyElement.CheckSpecifiedSource : null, false, false, -1, i);
break;
}
}
}
else
{
ILGenElementElseString(elementElseString);
}
}
private static bool IsSequence(Member[] members)
{
for (int i = 0; i < members.Length; i++)
{
if (members[i].Mapping.IsParticle && members[i].Mapping.IsSequence)
return true;
}
return false;
}
private void WriteMemberElementsIf(Member[] members, Member? anyElement, string elementElseString)
{
int count = 0;
bool isSequence = IsSequence(members);
int cases = 0;
for (int i = 0; i < members.Length; i++)
{
Member member = (Member)members[i];
if (member.Mapping.Xmlns != null)
continue;
if (member.Mapping.Ignore)
continue;
if (isSequence && (member.Mapping.IsText || member.Mapping.IsAttribute))
continue;
bool firstElement = true;
ChoiceIdentifierAccessor? choice = member.Mapping.ChoiceIdentifier;
ElementAccessor[] elements = member.Mapping.Elements!;
for (int j = 0; j < elements.Length; j++)
{
ElementAccessor e = elements[j];
string? ns = e.Form == XmlSchemaForm.Qualified ? e.Namespace : "";
if (!isSequence && e.Any && string.IsNullOrEmpty(e.Name)) continue;
if (!firstElement || (!isSequence && count > 0))
{
ilg.InitElseIf();
}
else if (isSequence)
{
if (cases > 0)
ilg.InitElseIf();
else
ilg.InitIf();
ilg.Ldloc("state");
ilg.Ldc(cases);
ilg.AndIf(Cmp.EqualTo);
ilg.InitIf();
}
else
{
ilg.InitIf();
}
count++;
firstElement = false;
if (member.ParamsReadSource != null)
{
ILGenParamsReadSource(member.ParamsReadSource);
ilg.Ldc(false);
ilg.AndIf(Cmp.EqualTo);
}
Label labelTrue = ilg.DefineLabel();
Label labelEnd = ilg.DefineLabel();
if (member.Mapping.IsReturnValue)
{
MethodInfo XmlSerializationReader_get_IsReturnValue = typeof(XmlSerializationReader).GetMethod(
"get_IsReturnValue",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_IsReturnValue);
ilg.Brtrue(labelTrue);
}
if (isSequence && e.Any && e.AnyNamespaces == null)
{
ilg.Ldc(true);
}
else
{
WriteXmlNodeEqual("Reader", e.Name, ns, false);
}
if (member.Mapping.IsReturnValue)
{
ilg.Br_S(labelEnd);
ilg.MarkLabel(labelTrue);
ilg.Ldc(true);
ilg.MarkLabel(labelEnd);
}
ilg.AndIf();
WriteElement(member.ArraySource, member.ArrayName, member.ChoiceArraySource, e, choice, member.Mapping.CheckSpecified == SpecifiedAccessor.ReadWrite ? member.CheckSpecifiedSource : null, member.IsList && member.Mapping.TypeDesc!.IsNullable, member.Mapping.ReadOnly, member.FixupIndex, j);
if (member.Mapping.IsReturnValue)
{
MethodInfo XmlSerializationReader_set_IsReturnValue = typeof(XmlSerializationReader).GetMethod(
"set_IsReturnValue",
CodeGenerator.InstanceBindingFlags,
s_boolType
)!;
ilg.Ldarg(0);
ilg.Ldc(false);
ilg.Call(XmlSerializationReader_set_IsReturnValue);
}
if (member.ParamsReadSource != null)
{
ILGenParamsReadSource(member.ParamsReadSource, true);
}
}
if (isSequence)
{
if (member.IsArrayLike)
{
ilg.Else();
}
else
{
ilg.EndIf();
}
cases++;
ilg.Ldc(cases);
ilg.Stloc(ilg.GetLocal("state"));
if (member.IsArrayLike)
{
ilg.EndIf();
}
}
}
if (count > 0)
{
ilg.Else();
}
WriteMemberElementsElse(anyElement, elementElseString);
if (count > 0)
{
ilg.EndIf();
}
}
private static string GetArraySource(TypeDesc typeDesc, string arrayName)
{
return GetArraySource(typeDesc, arrayName, false);
}
private static string GetArraySource(TypeDesc typeDesc, string arrayName, bool multiRef)
{
string a = arrayName;
string c = $"c{a}";
string init = "";
if (multiRef)
{
init = $"soap = (System.Object[])EnsureArrayIndex(soap, {c}+2, typeof(System.Object)); ";
}
if (typeDesc.IsArray)
{
string arrayTypeFullName = typeDesc.ArrayElementTypeDesc!.CSharpName;
init = $"{init}{a} = ({arrayTypeFullName}[])EnsureArrayIndex({a}, {c}, {ReflectionAwareILGen.GetStringForTypeof(arrayTypeFullName)});";
string arraySource = ReflectionAwareILGen.GetStringForArrayMember(a, $"{c}++");
if (multiRef)
{
init = $"{init} soap[1] = {a};";
init = $"{init} if (ReadReference(out soap[{c}+2])) {arraySource} = null; else ";
}
return $"{init}{arraySource}";
}
else
{
return ReflectionAwareILGen.GetStringForMethod(arrayName, "Add");
}
}
private void WriteMemberEnd(Member[] members)
{
WriteMemberEnd(members, false);
}
private void WriteMemberEnd(Member[] members, bool soapRefs)
{
for (int i = 0; i < members.Length; i++)
{
Member member = (Member)members[i];
if (member.IsArrayLike)
{
TypeDesc typeDesc = member.Mapping.TypeDesc!;
if (typeDesc.IsArray)
{
WriteSourceBegin(member.Source);
Debug.Assert(!soapRefs);
string a = member.ArrayName;
string c = $"c{a}";
MethodInfo XmlSerializationReader_ShrinkArray = typeof(XmlSerializationReader).GetMethod(
"ShrinkArray",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(Array), typeof(int), typeof(Type), typeof(bool) }
)!;
ilg.Ldarg(0);
ilg.Ldloc(ilg.GetLocal(a));
ilg.Ldloc(ilg.GetLocal(c));
ilg.Ldc(typeDesc.ArrayElementTypeDesc!.Type!);
ilg.Ldc(member.IsNullable);
ilg.Call(XmlSerializationReader_ShrinkArray);
ilg.ConvertValue(XmlSerializationReader_ShrinkArray.ReturnType, typeDesc.Type!);
WriteSourceEnd(member.Source, typeDesc.Type!);
if (member.Mapping.ChoiceIdentifier != null)
{
WriteSourceBegin(member.ChoiceSource!);
a = member.ChoiceArrayName;
c = $"c{a}";
ilg.Ldarg(0);
ilg.Ldloc(ilg.GetLocal(a));
ilg.Ldloc(ilg.GetLocal(c));
ilg.Ldc(member.Mapping.ChoiceIdentifier.Mapping!.TypeDesc!.Type!);
ilg.Ldc(member.IsNullable);
ilg.Call(XmlSerializationReader_ShrinkArray);
ilg.ConvertValue(XmlSerializationReader_ShrinkArray.ReturnType, member.Mapping.ChoiceIdentifier.Mapping.TypeDesc.Type!.MakeArrayType());
WriteSourceEnd(member.ChoiceSource!, member.Mapping.ChoiceIdentifier.Mapping.TypeDesc.Type!.MakeArrayType());
}
}
else if (typeDesc.IsValueType)
{
LocalBuilder arrayLoc = ilg.GetLocal(member.ArrayName);
WriteSourceBegin(member.Source);
ilg.Ldloc(arrayLoc);
WriteSourceEnd(member.Source, arrayLoc.LocalType);
}
}
}
}
private void WriteSourceBeginTyped(string source)
{
WriteSourceBegin(source);
}
[GeneratedRegex("(?<locA1>[^ ]+) = .+EnsureArrayIndex[(](?<locA2>[^,]+), (?<locI1>[^,]+),[^;]+;(?<locA3>[^[]+)[[](?<locI2>[^+]+)[+][+][]]")]
private static partial Regex EnsureArrayIndexRegex { get; }
[GeneratedRegex("(?<a>[^[]+)[[](?<ia>.+)[]]")]
private static partial Regex P0Regex { get; }
private void WriteSourceBegin(string source)
{
object? variable;
if (ilg.TryGetVariable(source, out variable))
{
Type varType = CodeGenerator.GetVariableType(variable);
if (CodeGenerator.IsNullableGenericType(varType))
{
// local address to invoke ctor on WriteSourceEnd
ilg.LoadAddress(variable);
}
return;
}
// o.@Field
if (source.StartsWith("o.@", StringComparison.Ordinal))
{
ilg.LdlocAddress(ilg.GetLocal("o"));
return;
}
// a_0_0 = (global::System.Object[])EnsureArrayIndex(a_0_0, ca_0_0, typeof(global::System.Object));a_0_0[ca_0_0++]
Match match = EnsureArrayIndexRegex.Match(source);
if (match.Success)
{
Debug.Assert(match.Groups["locA1"].Value == match.Groups["locA2"].Value);
Debug.Assert(match.Groups["locA1"].Value == match.Groups["locA3"].Value);
Debug.Assert(match.Groups["locI1"].Value == match.Groups["locI2"].Value);
LocalBuilder localA = ilg.GetLocal(match.Groups["locA1"].Value);
LocalBuilder localI = ilg.GetLocal(match.Groups["locI1"].Value);
Type arrayElementType = localA.LocalType.GetElementType()!;
MethodInfo XmlSerializationReader_EnsureArrayIndex = typeof(XmlSerializationReader).GetMethod(
"EnsureArrayIndex",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(Array), typeof(int), typeof(Type) }
)!;
ilg.Ldarg(0);
ilg.Ldloc(localA);
ilg.Ldloc(localI);
ilg.Ldc(arrayElementType);
ilg.Call(XmlSerializationReader_EnsureArrayIndex);
ilg.Castclass(localA.LocalType);
ilg.Stloc(localA);
// a_0[ca_0++]
ilg.Ldloc(localA);
ilg.Ldloc(localI);
ilg.Dup();
ilg.Ldc(1);
ilg.Add();
ilg.Stloc(localI);
if (CodeGenerator.IsNullableGenericType(arrayElementType) || arrayElementType.IsValueType)
{
ilg.Ldelema(arrayElementType);
}
return;
}
//"a_0_0.Add("
if (source.EndsWith(".Add(", StringComparison.Ordinal))
{
int index = source.LastIndexOf(".Add(", StringComparison.Ordinal);
LocalBuilder localA = ilg.GetLocal(source.Substring(0, index));
ilg.LdlocAddress(localA);
return;
}
// p[0]
match = P0Regex.Match(source);
if (match.Success)
{
System.Diagnostics.Debug.Assert(CodeGenerator.GetVariableType(ilg.GetVariable(match.Groups["a"].Value)).IsArray);
ilg.Load(ilg.GetVariable(match.Groups["a"].Value));
ilg.Load(ilg.GetVariable(match.Groups["ia"].Value));
return;
}
throw Globals.NotSupported($"Unexpected: {source}");
}
private void WriteSourceEnd(string source, Type elementType)
{
WriteSourceEnd(source, elementType, elementType);
}
private void WriteSourceEnd(string source, Type elementType, Type stackType)
{
object? variable;
if (ilg.TryGetVariable(source, out variable))
{
Type varType = CodeGenerator.GetVariableType(variable);
if (CodeGenerator.IsNullableGenericType(varType))
{
ilg.Call(varType.GetConstructor(varType.GetGenericArguments())!);
}
else
{
Debug.Assert(elementType != null && variable is LocalBuilder);
ilg.ConvertValue(stackType, elementType);
ilg.ConvertValue(elementType, varType);
ilg.Stloc((LocalBuilder)variable);
}
return;
}
// o.@Field
if (source.StartsWith("o.@", StringComparison.Ordinal))
{
Debug.Assert(memberInfos.ContainsKey(source.Substring(3)));
MemberInfo memInfo = memberInfos[source.Substring(3)];
ilg.ConvertValue(stackType, memInfo is FieldInfo ? ((FieldInfo)memInfo).FieldType : ((PropertyInfo)memInfo).PropertyType);
ilg.StoreMember(memInfo);
return;
}
// a_0_0 = (global::System.Object[])EnsureArrayIndex(a_0_0, ca_0_0, typeof(global::System.Object));a_0_0[ca_0_0++]
Match match = EnsureArrayIndexRegex.Match(source);
if (match.Success)
{
object oVar = ilg.GetVariable(match.Groups["locA1"].Value);
Type arrayElementType = CodeGenerator.GetVariableType(oVar).GetElementType()!;
ilg.ConvertValue(elementType, arrayElementType);
if (CodeGenerator.IsNullableGenericType(arrayElementType) || arrayElementType.IsValueType)
{
ilg.Stobj(arrayElementType);
}
else
{
ilg.Stelem(arrayElementType);
}
return;
}
//"a_0_0.Add("
if (source.EndsWith(".Add(", StringComparison.Ordinal))
{
int index = source.LastIndexOf(".Add(", StringComparison.Ordinal);
LocalBuilder localA = ilg.GetLocal(source.Substring(0, index));
Debug.Assert(!localA.LocalType.IsGenericType || (localA.LocalType.GetGenericArguments().Length == 1 && localA.LocalType.GetGenericArguments()[0].IsAssignableFrom(elementType)));
MethodInfo Add = localA.LocalType.GetMethod(
"Add",
CodeGenerator.InstanceBindingFlags,
new Type[] { elementType }
)!;
Debug.Assert(Add != null);
Type addParameterType = Add.GetParameters()[0].ParameterType;
ilg.ConvertValue(stackType, addParameterType);
ilg.Call(Add);
if (Add.ReturnType != typeof(void))
ilg.Pop();
return;
}
// p[0]
match = P0Regex.Match(source);
if (match.Success)
{
Type varType = CodeGenerator.GetVariableType(ilg.GetVariable(match.Groups["a"].Value));
System.Diagnostics.Debug.Assert(varType.IsArray);
Type varElementType = varType.GetElementType()!;
ilg.ConvertValue(stackType, varElementType);
ilg.Stelem(varElementType);
return;
}
throw Globals.NotSupported($"Unexpected: {source}");
}
private void WriteArray(string source, string? arrayName, ArrayMapping arrayMapping, bool readOnly, bool isNullable, int elementIndex)
{
MethodInfo XmlSerializationReader_ReadNull = typeof(XmlSerializationReader).GetMethod(
"ReadNull",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_ReadNull);
ilg.IfNot(); // if (!ReadNull()) { // EnterScope
ilg.EnterScope();
MemberMapping memberMapping = new MemberMapping();
memberMapping.Elements = arrayMapping.Elements;
memberMapping.TypeDesc = arrayMapping.TypeDesc;
memberMapping.ReadOnly = readOnly;
if (source.StartsWith("o.@", StringComparison.Ordinal))
{
Debug.Assert(memberInfos.ContainsKey(source.Substring(3)));
memberMapping.MemberInfo = memberInfos[source.Substring(3)];
}
Member member = new Member(source, arrayName, elementIndex, memberMapping, false);
member.IsNullable = false; //Note, IsNullable is set to false since null condition (xsi:nil) is already handled by 'ReadNull()'
Member[] members = new Member[] { member };
WriteMemberBegin(members);
Label labelTrue = ilg.DefineLabel();
Label labelEnd = ilg.DefineLabel();
if (readOnly)
{
ilg.Load(ilg.GetVariable(member.ArrayName));
ilg.Load(null);
ilg.Beq(labelTrue);
}
else
{
}
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_get_IsEmptyElement = typeof(XmlReader).GetMethod(
"get_IsEmptyElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_IsEmptyElement);
if (readOnly)
{
ilg.Br_S(labelEnd);
ilg.MarkLabel(labelTrue);
ilg.Ldc(true);
ilg.MarkLabel(labelEnd);
}
ilg.If();
MethodInfo XmlReader_Skip = typeof(XmlReader).GetMethod(
"Skip",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_Skip);
ilg.Else();
MethodInfo XmlReader_ReadStartElement = typeof(XmlReader).GetMethod(
"ReadStartElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_ReadStartElement);
WriteWhileNotLoopStart();
string unknownNode = $"UnknownNode(null, {ExpectedElements(members)});";
WriteMemberElements(members, unknownNode, unknownNode, null, null);
MethodInfo XmlReader_MoveToContent = typeof(XmlReader).GetMethod(
"MoveToContent",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_MoveToContent);
ilg.Pop();
WriteWhileLoopEnd();
MethodInfo XmlSerializationReader_ReadEndElement = typeof(XmlSerializationReader).GetMethod(
"ReadEndElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_ReadEndElement);
ilg.EndIf();
WriteMemberEnd(members, false);
if (isNullable)
{
ilg.ExitScope(); // if(!ReadNull()) { ExitScope
ilg.Else(); // } else { EnterScope
ilg.EnterScope();
member.IsNullable = true;
WriteMemberBegin(members);
WriteMemberEnd(members);
}
ilg.ExitScope(); // if(!ReadNull())/else ExitScope
ilg.EndIf();
}
private void WriteElement(string source, string? arrayName, string? choiceSource, ElementAccessor element, ChoiceIdentifierAccessor? choice, string? checkSpecified, bool checkForNull, bool readOnly, int fixupIndex, int elementIndex)
{
if (checkSpecified != null && checkSpecified.Length > 0)
{
ILGenSet(checkSpecified, true);
}
if (element.Mapping is ArrayMapping)
{
WriteArray(source, arrayName, (ArrayMapping)element.Mapping, readOnly, element.IsNullable, elementIndex);
}
else if (element.Mapping is NullableMapping)
{
string? methodName = ReferenceMapping(element.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, element.Mapping.TypeDesc!.Name));
#endif
WriteSourceBegin(source);
ilg.Ldarg(0);
ilg.Ldc(true);
MethodBuilder methodBuilder = EnsureMethodBuilder(typeBuilder,
methodName!,
CodeGenerator.PrivateMethodAttributes,
// See WriteNullableMethod for different return type logic
element.Mapping.TypeDesc!.Type,
s_boolType
);
ilg.Call(methodBuilder);
WriteSourceEnd(source, element.Mapping.TypeDesc.Type!);
}
else if (element.Mapping is PrimitiveMapping)
{
bool doEndIf = false;
if (element.IsNullable)
{
MethodInfo XmlSerializationReader_ReadNull = typeof(XmlSerializationReader).GetMethod(
"ReadNull",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_ReadNull);
ilg.If();
WriteSourceBegin(source);
if (element.Mapping.TypeDesc!.IsValueType)
{
throw Globals.NotSupported("No such condition. PrimitiveMapping && IsNullable = String, XmlQualifiedName and never IsValueType");
}
else
{
ilg.Load(null);
}
WriteSourceEnd(source, element.Mapping.TypeDesc.Type!);
ilg.Else();
doEndIf = true;
}
if (element.Default != null && element.Default != DBNull.Value && element.Mapping.TypeDesc!.IsValueType)
{
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_get_IsEmptyElement = typeof(XmlReader).GetMethod(
"get_IsEmptyElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_IsEmptyElement);
ilg.If();
MethodInfo XmlReader_Skip = typeof(XmlReader).GetMethod(
"Skip",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_Skip);
ilg.Else();
doEndIf = true;
}
else
{
}
if ((element.Mapping.TypeDesc!.Type == typeof(TimeSpan)) || element.Mapping.TypeDesc!.Type == typeof(DateTimeOffset))
{
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_get_IsEmptyElement = typeof(XmlReader).GetMethod(
"get_IsEmptyElement",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_IsEmptyElement);
ilg.If();
WriteSourceBegin(source);
MethodInfo XmlReader_Skip = typeof(XmlReader).GetMethod(
"Skip",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_Skip);
LocalBuilder tmpLoc = ilg.GetTempLocal(element.Mapping.TypeDesc!.Type);
ilg.Ldloca(tmpLoc);
ilg.InitObj(element.Mapping.TypeDesc!.Type);
ilg.Ldloc(tmpLoc);
WriteSourceEnd(source, element.Mapping.TypeDesc.Type);
ilg.Else();
WriteSourceBegin(source);
WritePrimitive(element.Mapping, "Reader.ReadElementString()");
WriteSourceEnd(source, element.Mapping.TypeDesc.Type);
ilg.EndIf();
}
else
{
WriteSourceBegin(source);
if (element.Mapping.TypeDesc == QnameTypeDesc)
{
MethodInfo XmlSerializationReader_ReadElementQualifiedName = typeof(XmlSerializationReader).GetMethod(
"ReadElementQualifiedName",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_ReadElementQualifiedName);
}
else
{
string readFunc;
switch (element.Mapping.TypeDesc.FormatterName)
{
case "ByteArrayBase64":
case "ByteArrayHex":
readFunc = "false";
break;
default:
readFunc = "Reader.ReadElementString()";
break;
}
WritePrimitive(element.Mapping, readFunc);
}
WriteSourceEnd(source, element.Mapping.TypeDesc.Type!);
}
if (doEndIf)
ilg.EndIf();
}
else if (element.Mapping is StructMapping)
{
TypeMapping mapping = element.Mapping;
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
if (checkForNull)
{
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_Skip = typeof(XmlReader).GetMethod(
"Skip",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldloc(arrayName!);
ilg.Load(null);
ilg.If(Cmp.EqualTo);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_Skip);
ilg.Else();
}
WriteSourceBegin(source);
List<Type> argTypes = new List<Type>();
ilg.Ldarg(0);
if (mapping.TypeDesc!.IsNullable)
{
ilg.Load(element.IsNullable);
argTypes.Add(typeof(bool));
}
ilg.Ldc(true);
argTypes.Add(typeof(bool));
MethodBuilder methodBuilder = EnsureMethodBuilder(typeBuilder,
methodName!,
CodeGenerator.PrivateMethodAttributes,
mapping.TypeDesc.Type,
argTypes.ToArray()
);
ilg.Call(methodBuilder);
WriteSourceEnd(source, mapping.TypeDesc.Type!);
if (checkForNull)
// 'If' begins in checkForNull above
ilg.EndIf();
}
else if (element.Mapping is SpecialMapping special)
{
switch (special.TypeDesc!.Kind)
{
case TypeKind.Node:
bool isDoc = special.TypeDesc.FullName == typeof(XmlDocument).FullName;
WriteSourceBeginTyped(source);
MethodInfo XmlSerializationReader_ReadXmlXXX = typeof(XmlSerializationReader).GetMethod(
isDoc ? "ReadXmlDocument" : "ReadXmlNode",
CodeGenerator.InstanceBindingFlags,
s_boolType
)!;
ilg.Ldarg(0);
ilg.Ldc(element.Any ? false : true);
ilg.Call(XmlSerializationReader_ReadXmlXXX);
// See logic in WriteSourceBeginTyped whether or not to castclass.
if (special.TypeDesc != null)
ilg.Castclass(special.TypeDesc.Type!);
WriteSourceEnd(source, special.TypeDesc!.Type!);
break;
case TypeKind.Serializable:
SerializableMapping sm = (SerializableMapping)element.Mapping;
// check to see if we need to do the derivation
if (sm.DerivedMappings != null)
{
MethodInfo XmlSerializationReader_GetXsiType = typeof(XmlSerializationReader).GetMethod(
"GetXsiType",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
Label labelTrue = ilg.DefineLabel();
Label labelEnd = ilg.DefineLabel();
LocalBuilder tserLoc = ilg.DeclareOrGetLocal(typeof(XmlQualifiedName), "tser");
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_GetXsiType);
ilg.Stloc(tserLoc);
ilg.Ldloc(tserLoc);
ilg.Load(null);
ilg.Ceq();
ilg.Brtrue(labelTrue);
WriteQNameEqual("tser", sm.XsiType!.Name, sm.XsiType.Namespace);
ilg.Br_S(labelEnd);
ilg.MarkLabel(labelTrue);
ilg.Ldc(true);
ilg.MarkLabel(labelEnd);
ilg.If();
}
WriteSourceBeginTyped(source);
bool isWrappedAny = !element.Any && IsWildcard(sm);
MethodInfo XmlSerializationReader_ReadSerializable = typeof(XmlSerializationReader).GetMethod(
"ReadSerializable",
CodeGenerator.InstanceBindingFlags,
isWrappedAny ? new Type[] { typeof(IXmlSerializable), typeof(bool) } : new Type[] { typeof(IXmlSerializable) }
)!;
ilg.Ldarg(0);
ReflectionAwareILGen.ILGenForCreateInstance(ilg, sm.TypeDesc!.Type!, sm.TypeDesc.CannotNew, false);
if (sm.TypeDesc.CannotNew)
ilg.ConvertValue(typeof(object), typeof(IXmlSerializable));
if (isWrappedAny)
ilg.Ldc(true);
ilg.Call(XmlSerializationReader_ReadSerializable);
// See logic in WriteSourceBeginTyped whether or not to castclass.
if (sm.TypeDesc != null)
ilg.ConvertValue(typeof(IXmlSerializable), sm.TypeDesc.Type!);
WriteSourceEnd(source, sm.TypeDesc!.Type!);
if (sm.DerivedMappings != null)
{
WriteDerivedSerializable(sm, sm, source, isWrappedAny);
WriteUnknownNode("UnknownNode", "null", null, true);
}
break;
default:
throw new InvalidOperationException(SR.XmlInternalError);
}
}
else
{
throw new InvalidOperationException(SR.XmlInternalError);
}
if (choice != null)
{
#if DEBUG
// use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
if (choiceSource == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, "need parent for the " + source));
#endif
WriteSourceBegin(choiceSource!);
CodeIdentifier.CheckValidIdentifier(choice.MemberIds![elementIndex]);
ReflectionAwareILGen.ILGenForEnumMember(ilg, choice.Mapping!.TypeDesc!.Type!, choice.MemberIds[elementIndex]);
WriteSourceEnd(choiceSource!, choice.Mapping.TypeDesc.Type!);
}
}
private void WriteDerivedSerializable(SerializableMapping head, SerializableMapping? mapping, string source, bool isWrappedAny)
{
if (mapping == null)
return;
for (SerializableMapping? derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping)
{
Label labelTrue = ilg.DefineLabel();
Label labelEnd = ilg.DefineLabel();
LocalBuilder tserLoc = ilg.GetLocal("tser");
ilg.InitElseIf();
ilg.Ldloc(tserLoc);
ilg.Load(null);
ilg.Ceq();
ilg.Brtrue(labelTrue);
WriteQNameEqual("tser", derived.XsiType!.Name, derived.XsiType.Namespace);
ilg.Br_S(labelEnd);
ilg.MarkLabel(labelTrue);
ilg.Ldc(true);
ilg.MarkLabel(labelEnd);
ilg.AndIf();
if (derived.Type != null)
{
if (head.Type!.IsAssignableFrom(derived.Type))
{
WriteSourceBeginTyped(source);
MethodInfo XmlSerializationReader_ReadSerializable = typeof(XmlSerializationReader).GetMethod(
"ReadSerializable",
CodeGenerator.InstanceBindingFlags,
isWrappedAny ? new Type[] { typeof(IXmlSerializable), typeof(bool) } : new Type[] { typeof(IXmlSerializable) }
)!;
ilg.Ldarg(0);
ReflectionAwareILGen.ILGenForCreateInstance(ilg, derived.TypeDesc!.Type!, derived.TypeDesc.CannotNew, false);
if (derived.TypeDesc.CannotNew)
ilg.ConvertValue(typeof(object), typeof(IXmlSerializable));
if (isWrappedAny)
ilg.Ldc(true);
ilg.Call(XmlSerializationReader_ReadSerializable);
// See logic in WriteSourceBeginTyped whether or not to castclass.
if (head.TypeDesc != null)
ilg.ConvertValue(typeof(IXmlSerializable), head.TypeDesc.Type!);
WriteSourceEnd(source, head.TypeDesc!.Type!);
}
else
{
MethodInfo XmlSerializationReader_CreateBadDerivationException = typeof(XmlSerializationReader).GetMethod(
"CreateBadDerivationException",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string) }
)!;
ilg.Ldarg(0);
ilg.Ldstr(GetCSharpString(derived.XsiType.Name));
ilg.Ldstr(GetCSharpString(derived.XsiType.Namespace));
ilg.Ldstr(GetCSharpString(head.XsiType!.Name));
ilg.Ldstr(GetCSharpString(head.XsiType.Namespace));
ilg.Ldstr(GetCSharpString(derived.Type.FullName));
ilg.Ldstr(GetCSharpString(head.Type.FullName));
ilg.Call(XmlSerializationReader_CreateBadDerivationException);
ilg.Throw();
}
}
else
{
MethodInfo XmlSerializationReader_CreateMissingIXmlSerializableType = typeof(XmlSerializationReader).GetMethod(
"CreateMissingIXmlSerializableType",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string), typeof(string), typeof(string) }
)!;
ilg.Ldarg(0);
ilg.Ldstr(GetCSharpString(derived.XsiType.Name));
ilg.Ldstr(GetCSharpString(derived.XsiType.Namespace));
ilg.Ldstr(GetCSharpString(head.Type!.FullName));
ilg.Call(XmlSerializationReader_CreateMissingIXmlSerializableType);
ilg.Throw();
}
WriteDerivedSerializable(head, derived, source, isWrappedAny);
}
}
private void WriteWhileNotLoopStart()
{
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_MoveToContent = typeof(XmlReader).GetMethod(
"MoveToContent",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_MoveToContent);
ilg.Pop();
ilg.WhileBegin();
}
private void WriteWhileLoopEnd()
{
ilg.WhileBeginCondition();
{
int XmlNodeType_None = 0;
//int XmlNodeType_Element = 1;
int XmlNodeType_EndElement = 15;
MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod(
"get_Reader",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
MethodInfo XmlReader_get_NodeType = typeof(XmlReader).GetMethod(
"get_NodeType",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
Label labelFalse = ilg.DefineLabel();
Label labelEnd = ilg.DefineLabel();
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_NodeType);
ilg.Ldc(XmlNodeType_EndElement);
ilg.Beq(labelFalse);
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_get_Reader);
ilg.Call(XmlReader_get_NodeType);
ilg.Ldc(XmlNodeType_None);
ilg.Cne();
ilg.Br_S(labelEnd);
ilg.MarkLabel(labelFalse);
ilg.Ldc(false);
ilg.MarkLabel(labelEnd);
}
ilg.WhileEndCondition();
ilg.WhileEnd();
}
private void WriteParamsRead(int length)
{
const int StackallocLimit =
#if DEBUG
3; // lower limit in debug to help test both stackalloc and array code paths
#else
32; // arbitrary limit
#endif
LocalBuilder paramsRead = ilg.DeclareLocal(typeof(Span<bool>), "paramsRead");
if (length <= StackallocLimit)
{
ilg.StackallocSpan(typeof(bool), length);
}
else
{
ilg.NewArray(typeof(bool), length);
ilg.New(typeof(Span<bool>).GetConstructor(new[] { typeof(bool[]) })!);
}
ilg.Stloc(paramsRead);
}
private void WriteCreateMapping(TypeMapping mapping, string local)
{
string fullTypeName = mapping.TypeDesc!.CSharpName;
bool ctorInaccessible = mapping.TypeDesc.CannotNew;
LocalBuilder loc = ilg.DeclareLocal(
mapping.TypeDesc.Type!,
local);
if (ctorInaccessible)
{
ilg.BeginExceptionBlock();
}
ReflectionAwareILGen.ILGenForCreateInstance(ilg, mapping.TypeDesc.Type!, mapping.TypeDesc.CannotNew, true);
ilg.Stloc(loc);
if (ctorInaccessible)
{
ilg.Leave();
WriteCatchException(typeof(MissingMethodException));
MethodInfo XmlSerializationReader_CreateInaccessibleConstructorException = typeof(XmlSerializationReader).GetMethod(
"CreateInaccessibleConstructorException",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string) }
)!;
ilg.Ldarg(0);
ilg.Ldstr(GetCSharpString(fullTypeName));
ilg.Call(XmlSerializationReader_CreateInaccessibleConstructorException);
ilg.Throw();
WriteCatchException(typeof(SecurityException));
MethodInfo XmlSerializationReader_CreateCtorHasSecurityException = typeof(XmlSerializationReader).GetMethod(
"CreateCtorHasSecurityException",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(string) }
)!;
ilg.Ldarg(0);
ilg.Ldstr(GetCSharpString(fullTypeName));
ilg.Call(XmlSerializationReader_CreateCtorHasSecurityException);
ilg.Throw();
ilg.EndExceptionBlock();
}
}
private void WriteCatchException(Type exceptionType)
{
ilg.BeginCatchBlock(exceptionType);
ilg.Pop();
}
private void WriteArrayLocalDecl(string typeName, string variableName, string initValue, TypeDesc arrayTypeDesc)
{
ReflectionAwareILGen.WriteArrayLocalDecl(typeName, variableName, new SourceInfo(initValue, initValue, null, arrayTypeDesc.Type, ilg), arrayTypeDesc);
}
private void WriteCreateInstance(string source, bool ctorInaccessible, Type type)
{
ReflectionAwareILGen.WriteCreateInstance(source, ctorInaccessible, type, ilg);
}
private static void WriteLocalDecl(string variableName, SourceInfo initValue)
{
ReflectionAwareILGen.WriteLocalDecl(variableName, initValue);
}
[GeneratedRegex("UnknownNode[(]null, @[\"](?<qnames>[^\"]*)[\"][)];")]
private static partial Regex UnknownNodeNullAnyTypeRegex { get; }
[GeneratedRegex("UnknownNode[(][(]object[)](?<o>[^,]+), @[\"](?<qnames>[^\"]*)[\"][)];")]
private static partial Regex UnknownNodeObjectEmptyRegex { get; }
[GeneratedRegex("UnknownNode[(][(]object[)](?<o>[^,]+), null[)];")]
private static partial Regex UnknownNodeObjectNullRegex { get; }
[GeneratedRegex("UnknownNode[(][(]object[)](?<o>[^)]+)[)];")]
private static partial Regex UnknownNodeObjectRegex { get; }
[GeneratedRegex("paramsRead\\[(?<index>[0-9]+)\\]")]
private static partial Regex ParamsReadRegex { get; }
private void ILGenElseString(string elseString)
{
MethodInfo XmlSerializationReader_UnknownNode1 = typeof(XmlSerializationReader).GetMethod(
"UnknownNode",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(object) }
)!;
MethodInfo XmlSerializationReader_UnknownNode2 = typeof(XmlSerializationReader).GetMethod(
"UnknownNode",
CodeGenerator.InstanceBindingFlags,
new Type[] { typeof(object), typeof(string) }
)!;
// UnknownNode(null, @":anyType");
Match match = UnknownNodeNullAnyTypeRegex.Match(elseString);
if (match.Success)
{
ilg.Ldarg(0);
ilg.Load(null);
ilg.Ldstr(match.Groups["qnames"].Value);
ilg.Call(XmlSerializationReader_UnknownNode2);
return;
}
// UnknownNode((object)o, @"");
match = UnknownNodeObjectEmptyRegex.Match(elseString);
if (match.Success)
{
ilg.Ldarg(0);
LocalBuilder localO = ilg.GetLocal(match.Groups["o"].Value);
ilg.Ldloc(localO);
ilg.ConvertValue(localO.LocalType, typeof(object));
ilg.Ldstr(match.Groups["qnames"].Value);
ilg.Call(XmlSerializationReader_UnknownNode2);
return;
}
// UnknownNode((object)o, null);
match = UnknownNodeObjectNullRegex.Match(elseString);
if (match.Success)
{
ilg.Ldarg(0);
LocalBuilder localO = ilg.GetLocal(match.Groups["o"].Value);
ilg.Ldloc(localO);
ilg.ConvertValue(localO.LocalType, typeof(object));
ilg.Load(null);
ilg.Call(XmlSerializationReader_UnknownNode2);
return;
}
// "UnknownNode((object)o);"
match = UnknownNodeObjectRegex.Match(elseString);
if (match.Success)
{
ilg.Ldarg(0);
LocalBuilder localO = ilg.GetLocal(match.Groups["o"].Value);
ilg.Ldloc(localO);
ilg.ConvertValue(localO.LocalType, typeof(object));
ilg.Call(XmlSerializationReader_UnknownNode1);
return;
}
throw Globals.NotSupported($"Unexpected: {elseString}");
}
private void ILGenParamsReadSource(string paramsReadSource)
{
Match match = ParamsReadRegex.Match(paramsReadSource);
if (match.Success)
{
ilg.Ldloca(ilg.GetLocal("paramsRead"));
ilg.Ldc(int.Parse(match.Groups["index"].Value, CultureInfo.InvariantCulture));
ilg.Call(typeof(Span<bool>).GetMethod("get_Item")!);
ilg.LdindU1();
return;
}
throw Globals.NotSupported($"Unexpected: {paramsReadSource}");
}
private void ILGenParamsReadSource(string paramsReadSource, bool value)
{
Match match = ParamsReadRegex.Match(paramsReadSource);
if (match.Success)
{
ilg.Ldloca(ilg.GetLocal("paramsRead"));
ilg.Ldc(int.Parse(match.Groups["index"].Value, CultureInfo.InvariantCulture));
ilg.Call(typeof(Span<bool>).GetMethod("get_Item")!);
ilg.Ldc(value);
ilg.StindI1();
return;
}
throw Globals.NotSupported($"Unexpected: {paramsReadSource}");
}
private void ILGenElementElseString(string elementElseString)
{
if (elementElseString == "throw CreateUnknownNodeException();")
{
MethodInfo XmlSerializationReader_CreateUnknownNodeException = typeof(XmlSerializationReader).GetMethod(
"CreateUnknownNodeException",
CodeGenerator.InstanceBindingFlags,
Type.EmptyTypes
)!;
ilg.Ldarg(0);
ilg.Call(XmlSerializationReader_CreateUnknownNodeException);
ilg.Throw();
return;
}
if (elementElseString.StartsWith("UnknownNode(", StringComparison.Ordinal))
{
ILGenElseString(elementElseString);
return;
}
throw Globals.NotSupported($"Unexpected: {elementElseString}");
}
private void ILGenSet(string source, object value)
{
WriteSourceBegin(source);
ilg.Load(value);
WriteSourceEnd(source, value == null ? typeof(object) : value.GetType());
}
}
}
|