private static DeserializableMember Map(TypeInfo ontoType, DeserializableMember member) { MethodInfo?resetOnType = null; if (member.Reset.HasValue) { var surrogateResetWrapper = member.Reset.Value; if (surrogateResetWrapper.Mode != BackingMode.Method) { Throw.InvalidOperationException($"Cannot map reset {surrogateResetWrapper} onto {ontoType}, reset isn't backed by a method"); } var surrogateReset = surrogateResetWrapper.Method.Value; var surrogateResetBinding = GetEquivalentFlagsFor(surrogateReset.IsPublic, surrogateReset.IsStatic); Type[] resetTakesTypes; if (surrogateResetWrapper.TakesContext) { resetTakesTypes = new[] { Types.ReadContext.MakeByRefType() }; } else { resetTakesTypes = Type.EmptyTypes; } // intentionally letting this be null resetOnType = ontoType.GetMethod(surrogateReset.Name, surrogateResetBinding, null, resetTakesTypes, null); if (resetOnType == null) { Throw.InvalidOperationException($"No equivalent to {resetOnType} found on {ontoType}"); } } var surrogateSetterWrapper = member.Setter; switch (surrogateSetterWrapper.Mode) { case BackingMode.Field: { var surrogateField = surrogateSetterWrapper.Field.Value; var surrogateFieldBinding = GetEquivalentFlagsFor(surrogateField.IsPublic, surrogateField.IsStatic); // intentionally allowing null here var fieldOnType = ontoType.GetField(surrogateField.Name, surrogateFieldBinding); if (fieldOnType == null) { Throw.InvalidOperationException($"No equivalent to {surrogateField} found on {ontoType}"); } if (fieldOnType.FieldType != surrogateField.FieldType) { Throw.InvalidOperationException($"Field {fieldOnType} type ({fieldOnType.FieldType}) does not match surrogate field {surrogateField} type ({surrogateField.FieldType})"); } var required = GetEquivalentRequiredFor(member.IsRequired); return(DeserializableMember.CreateInner(ontoType, member.Name, (Setter?)fieldOnType, member.Parser, required, (Reset?)resetOnType, null)); } case BackingMode.Method: { var surrogateSetter = surrogateSetterWrapper.Method.Value; var surrogateSetterBinding = GetEquivalentFlagsFor(surrogateSetter.IsPublic, surrogateSetter.IsStatic); // intentionally letting this be null var setterOnType = ontoType.GetMethod(surrogateSetter.Name, surrogateSetterBinding); if (setterOnType == null) { Throw.InvalidOperationException($"No equivalent to {surrogateSetter} found on {ontoType}"); } var paramsOnType = setterOnType.GetParameters(); var paramsOnSurrogate = surrogateSetter.GetParameters(); if (paramsOnType.Length != paramsOnSurrogate.Length) { Throw.InvalidOperationException($"Parameters for {setterOnType} do not match parameters for {surrogateSetter}"); } for (var i = 0; i < paramsOnType.Length; i++) { var pOnType = paramsOnType[i]; var pOnSurrogate = paramsOnSurrogate[i]; if (pOnType.ParameterType != pOnSurrogate.ParameterType) { Throw.InvalidOperationException($"Parameter #{(i + 1)} on {setterOnType} does not match same parameter on {surrogateSetter}"); } } var required = GetEquivalentRequiredFor(member.IsRequired); return(DeserializableMember.CreateInner(ontoType, member.Name, (Setter?)setterOnType, member.Parser, required, (Reset?)resetOnType, null)); } case BackingMode.Delegate: Throw.InvalidOperationException($"Cannot map setter {surrogateSetterWrapper} onto {ontoType}, setter is backed by a delegate"); return(default); default: Throw.InvalidOperationException($"Unexpected {nameof(BackingMode)}: {surrogateSetterWrapper.Mode}"); return(default); } }
/// <summary> /// Enumerate members which will be deserialized for the given type. /// /// Note that these members are generated ahead of time with a source gneerator, /// and cannot be changed at runtime. /// </summary> public IEnumerable <DeserializableMember> EnumerateMembersToDeserialize(TypeInfo forType) { var paired = GetPairedType(forType, DESERIALIZER_KIND); if (paired == null) { return(Enumerable.Empty <DeserializableMember>()); } var colNamesProp = paired.GetPropertyNonNull("__ColumnNames", PublicStatic); var colNames = (ImmutableArray <string>)colNamesProp.GetValueNonNull(null); var ret = ImmutableArray.CreateBuilder <DeserializableMember>(colNames.Length); ParameterInfo[]? consPs = null; for (var i = 0; i < colNames.Length; i++) { var name = colNames[i]; var colReaderName = $"__Column_{i}"; var colReaderMtd = paired.GetMethodNonNull(colReaderName, PublicInstance); #pragma warning disable CS0618 // These are obsolete to prevent clients from using them, but they are fine for us. var isRequired = colReaderMtd.GetCustomAttribute <IsRequiredAttribute>() != null; var setterBackedByParameter = colReaderMtd.GetCustomAttribute <SetterBackedByConstructorParameterAttribute>(); var setterIsInitOnly = colReaderMtd.GetCustomAttribute <SetterBackedByInitOnlyPropertyAttribute>(); #pragma warning restore CS0618 Setter setter; if (setterBackedByParameter == null && setterIsInitOnly == null) { // directly a method var setterName = $"__Column_{i}_Setter"; var setterMtd = paired.GetMethodNonNull(setterName, PublicStatic); setter = Setter.ForMethod(setterMtd); } else if (setterBackedByParameter != null) { // parameter to constructor if (consPs == null) { var ip = GetInstanceProvider(forType); ip = Utils.NonNull(ip); var cons = ip.Constructor.Value; consPs = cons.GetParameters(); } var consParameterIndex = setterBackedByParameter.Index; if (consParameterIndex < 0 || consParameterIndex >= consPs.Length) { Throw.ImpossibleException($"Setter for column {i} claims to be backed by constructor parameter, but its position is out of bounds (index={consParameterIndex})"); } var p = consPs[setterBackedByParameter.Index]; setter = Setter.ForConstructorParameter(p); } else { // init only property var initOnly = Utils.NonNull(setterIsInitOnly); var prop = forType.GetProperty(initOnly.PropertyName, initOnly.BindingFlags); if (prop == null) { Throw.ImpossibleException($"Setter for column {i} claims to be backed by init-only property {initOnly.PropertyName} with bindings ({initOnly.BindingFlags}), but it could not be found"); } setter = Setter.ForProperty(prop); } var resetMethodName = $"__Column_{i}_Reset"; var resetMtd = paired.GetMethod(resetMethodName, PublicStatic); var reset = (Reset?)resetMtd; var parserMethodName = $"__Column_{i}_Parser"; var parserMtd = paired.GetMethod(parserMethodName, PublicStatic); Parser?parser; if (parserMtd != null) { parser = Parser.ForMethod(parserMtd); } else { parser = Utils.NonNull(Parser.GetDefault(setter.Takes)); } ret.Add(DeserializableMember.CreateInner(forType, name, setter, parser, isRequired ? MemberRequired.Yes : MemberRequired.No, reset, paired)); } return(ret.ToImmutable()); }