private static Delegate CreateWriteDelegate([NotNull] Type elementType, [NotNull] Type delegateType) { Debug.Assert(elementType != null); Debug.Assert(delegateType != null); var method = new DynamicMethod( $"{DynamicMethodPrefix}Write{delegateType.Name}_{elementType.Name}", typeof(void), new[] { typeof(XmlWriter), elementType }, elementType.Module); ILGenerator generator = method.GetILGenerator(); foreach (PropertySerializationInfo info in SerializationHelpers.GetAttributeProperties(elementType)) { EmitWriteProperty(info, generator); } generator.Emit(OpCodes.Ret); // Let's bake the method return(method.CreateDelegate(delegateType)); }
private static Delegate CreateReadDelegate( [NotNull] Type delegateType, [NotNull] Type elementType) { Debug.Assert(delegateType != null); Debug.Assert(elementType != null); var method = new DynamicMethod( $"{DynamicMethodPrefix}Read{elementType.Name}", typeof(void), // reader, namespaceUri new[] { typeof(XmlReader), typeof(string), elementType }, elementType.Module); ILGenerator generator = method.GetILGenerator(); generator.DeclareLocal(typeof(string)); generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldstr, "key"); generator.EmitCall(OpCodes.Callvirt, Metadata.GetAttributeMethod, null); generator.Emit(OpCodes.Stloc_0); // We need to create the switch for each property Label next = generator.DefineLabel(); Label @return = generator.DefineLabel(); bool first = true; foreach (PropertySerializationInfo info in SerializationHelpers.GetAttributeProperties(elementType)) { PropertyInfo property = info.Property; if (!first) { generator.MarkLabel(next); next = generator.DefineLabel(); } first = false; generator.Emit(OpCodes.Ldloc_0); generator.Emit(OpCodes.Ldstr, info.Name); generator.EmitCall(OpCodes.Call, Metadata.StringEqualsMethod, null); // If false jump to next generator.Emit(OpCodes.Brfalse, next); // Do our stuff if (!Metadata.TryGetReadContentMethod(property.PropertyType, out MethodInfo readMethod)) { throw new NotSupportedException($"Property {property.Name} has a non supported type."); } // Do we have a set method? MethodInfo setMethod = property.GetSetMethod(); if (setMethod is null) { throw new InvalidOperationException($"Property {property.DeclaringType}.{property.Name} has no setter."); } // reader.ReadXXX generator.Emit(OpCodes.Ldarg_2); // element generator.Emit(OpCodes.Ldarg_0); // reader generator.Emit(OpCodes.Ldstr, "data"); generator.Emit(OpCodes.Ldarg_1); // namespace URI // When writing scalar values we call member methods of XmlReader, while for array values // we call our own static methods. These two types of methods seem to need different OpCode. generator.EmitCall( readMethod.DeclaringType == typeof(XmlReaderExtensions) ? OpCodes.Call : OpCodes.Callvirt, readMethod, null); generator.EmitCall(OpCodes.Callvirt, setMethod, null); // Jump to do while generator.Emit(OpCodes.Br, @return); } // We don't know this parameter.. we throw generator.MarkLabel(next); generator.Emit(OpCodes.Ldloc_0); generator.Emit(OpCodes.Newobj, Metadata.ArgumentExceptionCtor); generator.Emit(OpCodes.Throw); generator.MarkLabel(@return); generator.Emit(OpCodes.Ret); // Let's bake the method return(method.CreateDelegate(delegateType)); }
private void WriteAttributeDefinitions([NotNull] string elementName, [NotNull] Type elementType) { Debug.Assert(elementName != null); Debug.Assert(elementType != null); foreach (PropertySerializationInfo info in SerializationHelpers.GetAttributeProperties(elementType)) { PropertyInfo property = info.Property; string name = info.Name; Type propertyType = property.PropertyType; // <key id="d1" for="edge" attr.name="weight" attr.type="double"/> _writer.WriteStartElement("key", GraphMLXmlResolver.GraphMLNamespace); _writer.WriteAttributeString(IdAttribute, name); _writer.WriteAttributeString("for", elementName); _writer.WriteAttributeString("attr.name", name); string typeCodeStr; try { typeCodeStr = ConstructTypeCode(propertyType); } catch (NotSupportedException) { throw new NotSupportedException( $"Property type {property.DeclaringType}.{property.Name} not supported by the GraphML schema."); } _writer.WriteAttributeString("attr.type", typeCodeStr); // <default>...</default> if (info.TryGetDefaultValue(out object defaultValue)) { _writer.WriteStartElement("default"); Type defaultValueType = defaultValue.GetType(); switch (Type.GetTypeCode(defaultValueType)) { case TypeCode.Boolean: _writer.WriteString(XmlConvert.ToString((bool)defaultValue)); break; case TypeCode.Int32: _writer.WriteString(XmlConvert.ToString((int)defaultValue)); break; case TypeCode.Int64: _writer.WriteString(XmlConvert.ToString((long)defaultValue)); break; case TypeCode.Single: _writer.WriteString(XmlConvert.ToString((float)defaultValue)); break; case TypeCode.Double: _writer.WriteString(XmlConvert.ToString((double)defaultValue)); break; case TypeCode.String: _writer.WriteString((string)defaultValue); break; case TypeCode.Object: if (defaultValueType.IsArray) { throw new NotSupportedException("Default values for array types are not supported."); } throw new NotSupportedException( $"Property type {property.DeclaringType}.{property.Name} not supported by the GraphML schema."); default: throw new NotSupportedException( $"Property type {property.DeclaringType}.{property.Name} not supported by the GraphML schema."); } _writer.WriteEndElement(); } _writer.WriteEndElement(); } }