using System; using System.Linq; using System.Reflection; using System.Reflection.Emit; namespace Utf8Json.Internal.Emit { internal class MetaMember { public string Name { get; private set; } public string MemberName { get; private set; } public bool IsProperty { get { return PropertyInfo != null; } } public bool IsField { get { return FieldInfo != null; } } public bool IsWritable { get; private set; } public bool IsReadable { get; private set; } public Type Type { get; private set; } public FieldInfo FieldInfo { get; private set; } public PropertyInfo PropertyInfo { get; private set; } public MethodInfo ShouldSerializeMethodInfo { get; private set; } MethodInfo getMethod; MethodInfo setMethod; protected MetaMember(Type type, string name, string memberName, bool isWritable, bool isReadable) { this.Name = name; this.MemberName = memberName; this.Type = type; this.IsWritable = isWritable; this.IsReadable = isReadable; } public MetaMember(FieldInfo info, string name, bool allowPrivate) { this.Name = name; this.MemberName = info.Name; this.FieldInfo = info; this.Type = info.FieldType; this.IsReadable = allowPrivate || info.IsPublic; this.IsWritable = allowPrivate || (info.IsPublic && !info.IsInitOnly); this.ShouldSerializeMethodInfo = GetShouldSerialize(info); } public MetaMember(PropertyInfo info, string name, bool allowPrivate) { this.getMethod = info.GetGetMethod(true); this.setMethod = info.GetSetMethod(true); this.Name = name; this.MemberName = info.Name; this.PropertyInfo = info; this.Type = info.PropertyType; this.IsReadable = (getMethod != null) && (allowPrivate || getMethod.IsPublic) && !getMethod.IsStatic; this.IsWritable = (setMethod != null) && (allowPrivate || setMethod.IsPublic) && !setMethod.IsStatic; this.ShouldSerializeMethodInfo = GetShouldSerialize(info); } static MethodInfo GetShouldSerialize(MemberInfo info) { var shouldSerialize = "ShouldSerialize" + info.Name; // public only return info.DeclaringType.GetMethods(BindingFlags.Instance | BindingFlags.Public) .Where(x => x.Name == shouldSerialize && x.ReturnType == typeof(bool) && x.GetParameters().Length == 0) .FirstOrDefault(); } public T GetCustomAttribute(bool inherit) where T : Attribute { if (IsProperty) { return PropertyInfo.GetCustomAttribute(inherit); } else if (FieldInfo != null) { return FieldInfo.GetCustomAttribute(inherit); } else { return null; } } public virtual void EmitLoadValue(ILGenerator il) { if (IsProperty) { il.EmitCall(getMethod); } else { il.Emit(OpCodes.Ldfld, FieldInfo); } } public virtual void EmitStoreValue(ILGenerator il) { if (IsProperty) { il.EmitCall(setMethod); } else { il.Emit(OpCodes.Stfld, FieldInfo); } } } // used for serialize exception... internal class StringConstantValueMetaMember : MetaMember { readonly string constant; public StringConstantValueMetaMember(string name, string constant) : base(typeof(String), name, name, false, true) { this.constant = constant; } public override void EmitLoadValue(ILGenerator il) { il.Emit(OpCodes.Pop); // pop load instance il.Emit(OpCodes.Ldstr, constant); } public override void EmitStoreValue(ILGenerator il) { throw new NotSupportedException(); } } // used for serialize exception... internal class InnerExceptionMetaMember : MetaMember { static readonly MethodInfo getInnerException = ExpressionUtility.GetPropertyInfo((Exception ex) => ex.InnerException).GetGetMethod(); static readonly MethodInfo nongenericSerialize = ExpressionUtility.GetMethodInfo(writer => JsonSerializer.NonGeneric.Serialize(ref writer, default(object), default(IJsonFormatterResolver))); // set after... internal ArgumentField argWriter; internal ArgumentField argValue; internal ArgumentField argResolver; public InnerExceptionMetaMember(string name) : base(typeof(Exception), name, name, false, true) { } public override void EmitLoadValue(ILGenerator il) { il.Emit(OpCodes.Callvirt, getInnerException); } public override void EmitStoreValue(ILGenerator il) { throw new NotSupportedException(); } public void EmitSerializeDirectly(ILGenerator il) { // JsonSerializer.NonGeneric.Serialize(ref writer, value.InnerException, formatterResolver); argWriter.EmitLoad(); argValue.EmitLoad(); il.Emit(OpCodes.Callvirt, getInnerException); argResolver.EmitLoad(); il.EmitCall(nongenericSerialize); } } }