private static Delegate CreateSetDefaultDelegate( [NotNull] Type delegateType, [NotNull] Type elementType) { Debug.Assert(delegateType != null); Debug.Assert(elementType != null); var method = new DynamicMethod( $"{DynamicMethodPrefix}Set{elementType.Name}Default", typeof(void), new[] { elementType }, elementType.Module); ILGenerator generator = method.GetILGenerator(); // We need to create the switch for each property IEnumerable <PropertyInfo> properties = SerializationHelpers .GetAttributeProperties(elementType) .Select(info => info.Property); foreach (PropertyInfo property in properties) { var defaultValueAttribute = Attribute.GetCustomAttribute(property, typeof(DefaultValueAttribute)) as DefaultValueAttribute; if (defaultValueAttribute is null) { continue; } MethodInfo setMethod = property.GetSetMethod(); if (setMethod is null) { throw new InvalidOperationException($"Property {property.Name} is not settable."); } if (property.PropertyType.IsArray) { throw new NotSupportedException("Default values for array types are not implemented."); } object value = defaultValueAttribute.Value; if (value is null) { throw new NotSupportedException($"Null default value is not supported for property {property.Name}."); } if (value.GetType() != property.PropertyType) { throw new InvalidOperationException($"Invalid default value type for property {property.Name}."); } generator.Emit(OpCodes.Ldarg_0); EmitValue(generator, property, value); EmitCall(generator, setMethod); } generator.Emit(OpCodes.Ret); // Let's bake the method return(method.CreateDelegate(delegateType)); }
private static Delegate CreateWriteDelegate([NotNull] Type nodeType, [NotNull] Type delegateType) { Debug.Assert(nodeType != null); Debug.Assert(delegateType != null); var method = new DynamicMethod( $"{DynamicMethodPrefix}Write{delegateType.Name}_{nodeType.Name}", typeof(void), new[] { typeof(XmlWriter), nodeType }, nodeType.Module); ILGenerator generator = method.GetILGenerator(); foreach (PropertySerializationInfo info in SerializationHelpers.GetAttributeProperties(nodeType)) { EmitWriteProperty(info, generator); } generator.Emit(OpCodes.Ret); // Let's bake the method return(method.CreateDelegate(delegateType)); }
private void WriteAttributeDefinitions([NotNull] string nodeName, [NotNull] Type nodeType) { Debug.Assert(nodeName != null); Debug.Assert(nodeType != null); foreach (PropertySerializationInfo info in SerializationHelpers.GetAttributeProperties(nodeType)) { 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", nodeName); _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 NotImplementedException("Default values for array types are not implemented."); } 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(); } }
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 set method."); } // 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 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), new[] { typeof(XmlReader), typeof(string), elementType }, elementType.Module); ILGenerator generator = method.GetILGenerator(); // Ldarg_0 = reader // Ldarg_1 = namespace URI // Ldarg_2 = element generator.DeclareLocal(typeof(string)); GetXmlAttribute("key"); 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); EmitCall(generator, Metadata.StringEqualsMethod); // 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."); } // element.xxx = 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 EmitCall(generator, readMethod); EmitCall(generator, setMethod); // 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)); #region Local function void GetXmlAttribute(string attributeName) { generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldstr, attributeName); EmitCall(generator, Metadata.GetAttributeMethod); } #endregion }