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