예제 #1
0
            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));
            }