internal void AppendSingle(ReadOnlySpan <T> previousBuffer, T val) { if (CurrentMode == Mode.CopyOnNextAppend || CurrentMode == Mode.InPlace) { SwitchToCopy(previousBuffer); } switch (CurrentMode) { case Mode.Uninitialized: CurrentMode = Mode.Copy; ResizeCopy(1); Copy[0] = val; StartIndex = 0; Length = 1; break; case Mode.Copy: if (Copy.Length == Length) { ResizeCopy(Copy.Length * 2); } Copy[Length] = val; Length++; break; default: Throw.ImpossibleException($"Unexpected {nameof(Mode)}: {CurrentMode}"); break; } }
DynamicRowConverter IElseSupporting <DynamicRowConverter> .Clone(ImmutableArray <DynamicRowConverter> newFallbacks, NullHandling?_, NullHandling?__) { switch (Mode) { case BackingMode.Constructor: { if (ConstructorForObject.HasValue) { return(new DynamicRowConverter(ConstructorForObject.Value, newFallbacks)); } if (ConstructorTakingParams.HasValue) { return(new DynamicRowConverter(ConstructorTakingParams.Value, ParameterTypes.Value, ColumnsForParameters.Value, newFallbacks)); } if (EmptyConstructor.HasValue) { return(new DynamicRowConverter(EmptyConstructor.Value, Setters.Value, ColumnsForSetters.Value, newFallbacks)); } } break; case BackingMode.Delegate: return(new DynamicRowConverter(TargetType, Delegate.Value, newFallbacks)); case BackingMode.Method: return(new DynamicRowConverter(TargetType, Method.Value, newFallbacks)); } Throw.ImpossibleException($"Unexpected {nameof(BackingMode)}: {Mode}"); return(default);
internal static T NonNullValue <T>(T?toCheck) where T : struct { if (toCheck == null) { Throw.ImpossibleException("Expected non-null value, but found null"); } return(toCheck.Value); }
private static TypeInfo?GetPairedType( Type forType, #pragma warning disable CS0618 // This is obsolete to prevent clients from using them, but they are fine for us. GeneratedSourceVersionAttribute.GeneratedTypeKind forMode #pragma warning restore CS0618 ) { var inAssembly = forType.Assembly; var candidateTypes = inAssembly.GetTypes(); TypeInfo?ret = null; foreach (var tRaw in candidateTypes) { var t = tRaw.GetTypeInfo(); #pragma warning disable CS0618 // This is obsolete to prevent clients from using them, but they are fine for us. var attr = t.GetCustomAttribute <GeneratedSourceVersionAttribute>(); #pragma warning restore CS0618 if (attr is not object) { continue; } var meantForType = attr.ForType.GetTypeInfo(); if (meantForType != forType) { continue; } var candidateMode = attr.Kind; if (candidateMode != forMode) { continue; } var version = attr.Version; if (version != CURRENT_CESIL_VERSION) { Throw.ImpossibleException($"Found a generated type ({t}) with an unexpected version ({version}), suggesting the generated source does not match the version ({CURRENT_CESIL_VERSION}) of Cesil in use."); } if (ret != null) { Throw.ImpossibleException($"Found multiple generated types for {forType}"); } ret = t; } return(ret); }
internal readonly bool TryLookup(string key, out int value) { switch (Mode) { case Algorithm.AdaptiveRadixTrie: return(TryLookupAdaptiveRadixTrie(key, out value)); case Algorithm.BinarySearch: return(TryLookupBinarySearch(key, out value)); default: value = 0; Throw.ImpossibleException($"Unexpected {nameof(Algorithm)}: {Mode}"); return(default); } }
protected internal ReadWithCommentResultType EndOfData() { var res = StateMachine.EndOfData(); switch (res) { case ReaderStateMachine.AdvanceResult.Skip_Character: // nothing to be done! return(ReadWithCommentResultType.NoValue); case ReaderStateMachine.AdvanceResult.Append_CarriageReturnAndCurrentCharacter: case ReaderStateMachine.AdvanceResult.Append_Character: Throw.ImpossibleException($"Attempted to append end of data with {nameof(ReaderStateMachine.Advance)} = {res}", Configuration.Options); return(default); case ReaderStateMachine.AdvanceResult.Append_CarriageReturnAndEndComment: Partial.AppendCarriageReturn(ReadOnlySpan <char> .Empty); return(ReadWithCommentResultType.HasComment); case ReaderStateMachine.AdvanceResult.Finished_Unescaped_Value: PushPendingCharactersToValue(false); return(ReadWithCommentResultType.HasValue); case ReaderStateMachine.AdvanceResult.Finished_Escaped_Value: PushPendingCharactersToValue(true); return(ReadWithCommentResultType.HasValue); case ReaderStateMachine.AdvanceResult.Finished_LastValueUnescaped_Record: if (Partial.PendingCharsCount > 0) { PushPendingCharactersToValue(false); } return(ReadWithCommentResultType.HasValue); case ReaderStateMachine.AdvanceResult.Finished_LastValueEscaped_Record: if (Partial.PendingCharsCount > 0) { PushPendingCharactersToValue(true); } return(ReadWithCommentResultType.HasValue); case ReaderStateMachine.AdvanceResult.Finished_Comment: return(ReadWithCommentResultType.HasComment); default: return(HandleUncommonAdvanceResults(res, null)); } }
private static byte CreateMask() { var values = Enum.GetValues(typeof(T)); byte ret = 0; for (var i = 0; i < values.Length; i++) { var o = values.GetValue(i); if (o == null) { Throw.ImpossibleException("Shouldn't be possible"); } ret |= (byte)o; } return(ret); }
// only call this with the buffer we're actively reading from internal void Append(ReadOnlySpan <T> fromBuffer, int index, int length) { // we figured out that we can't be in place on // the other buffer... but that doesn't actually // matter until we next try to append. // // which is now, so copy everything out of the span // into our local buffer and switch the mode // to plain old Copy mode if (CurrentMode == Mode.CopyOnNextAppend) { SwitchToCopy(fromBuffer); } switch (CurrentMode) { case Mode.Uninitialized: CurrentMode = Mode.InPlace; StartIndex = index; Length = length; break; case Mode.Copy: var desiredSize = Length + length; if (desiredSize >= Copy.Length) { ResizeCopy(desiredSize * 2); } fromBuffer.Slice(index, length).CopyTo(Copy.Slice(Length)); Length += length; break; case Mode.InPlace: Length += length; break; default: Throw.ImpossibleException($"Unexpected {nameof(Mode)}: {CurrentMode}"); break; } }
internal void AssertNotPoisoned <T>(IBoundConfiguration <T> self) { if (Poison != null) { switch (Poison.Value) { case PoisonType.Cancelled: Throw.InvalidOperationException("Object is in an invalid state, a previous operation was canceled"); return; case PoisonType.Exception: Throw.InvalidOperationException("Object is in an invalid state, a previous operation raised an exception"); return; default: Throw.ImpossibleException($"Unexpected {nameof(PoisonType)}: {Poison}", self); return; } } }
internal void Skipped() { // this means we can't jut blindly copy of out the backing buffer if // another append comes through, so change state accordingly switch (CurrentMode) { case Mode.Uninitialized: case Mode.CopyOnNextAppend: case Mode.Copy: // nothing to do break; case Mode.InPlace: CurrentMode = Mode.CopyOnNextAppend; break; default: Throw.ImpossibleException($"Unexpected {nameof(Mode)}: {CurrentMode}"); break; } }
internal SurrogateTypeDescriber(ITypeDescriber typeDescriber, ITypeDescriber fallback, SurrogateTypeDescriberFallbackBehavior fallbackBehavior, ImmutableDictionary <TypeInfo, TypeInfo> surrogateTypes) { SurrogateTypes = surrogateTypes; TypeDescriber = typeDescriber; FallbackDescriber = fallback; switch (fallbackBehavior) { case SurrogateTypeDescriberFallbackBehavior.Throw: ThrowOnNoRegisteredSurrogate = true; break; case SurrogateTypeDescriberFallbackBehavior.UseFallback: ThrowOnNoRegisteredSurrogate = false; break; default: Throw.ImpossibleException($"Unexpected {nameof(SurrogateTypeDescriberFallbackBehavior)}: {fallbackBehavior}"); break; } }
internal ReadOnlyMemory <T> AsMemory(ReadOnlyMemory <T> fromBufferMem) { switch (CurrentMode) { case Mode.CopyOnNextAppend: case Mode.InPlace: // we're able to just read out of the buffer, no intermediate copy required return(fromBufferMem.Slice(StartIndex, Length)); case Mode.Copy: // whelp, we had to make a copy... better return it return(CopyOwner.Value.Memory.Slice(StartIndex, Length)); case Mode.Uninitialized: return(ReadOnlyMemory <T> .Empty); default: Throw.ImpossibleException($"Unexpected {nameof(Mode)}: {CurrentMode}"); return(default); } }
public InstanceProvider?GetInstanceProvider(TypeInfo forType) { var paired = GetPairedType(forType, DESERIALIZER_KIND); if (paired == null) { return(null); } var instanceMtd = paired.GetMethodNonNull("__InstanceProvider", PublicStatic); #pragma warning disable CS0618 // This obsolete to prevent clients from using them, but they are fine for us. var forConstructorAttrs = instanceMtd.GetCustomAttributes <ConstructorInstanceProviderAttribute>(); #pragma warning restore CS0618 if (forConstructorAttrs.Any()) { var consOnTypes = forConstructorAttrs.Select(x => x.ForType).Distinct().ToImmutableArray(); if (consOnTypes.Length > 1) { Throw.ImpossibleException($"Generated type {paired} (for {forType}) claims multiple constructors for an InstanceProvider."); } var consOnType = consOnTypes.Single(); var consParams = forConstructorAttrs.OrderBy(x => x.ParameterIndex).Select(i => i.ParameterType).ToArray(); var cons = consOnType.GetConstructor(AllInstance, null, consParams, null); if (cons == null) { Throw.ImpossibleException($"Generated type {paired} (for {forType}) claims a constructor for an InstanceProvider that could not be found."); } return(InstanceProvider.ForConstructorWithParametersInner(cons, paired)); } return(InstanceProvider.ForMethodInner(instanceMtd, paired)); }
private static NullHandling DetermineNullabilityImpl <T>(T member, IEnumerable <CustomAttributeData> memberAttributes, IEnumerable <CustomAttributeData>?contextAttributes, TypeInfo memberType) { // from: https://github.com/dotnet/roslyn/blob/3182fd79a7790188d4facfc3fa2da22c598895f2/docs/features/nullable-metadata.md // nullable annotations are either on the member itself, or on the declaring type // if on the member, it's a [Nullable] // if on the type, it's a [NullableContext] // // Nullable can take either a byte or a byte[], NullableContext always takes a byte // // we only ever care about the first byte (even if we have an array) // because that refers to the "root" // // bytes are as follows: // 0 - null oblivious // 1 - non-null // 2 - allow-null // // the absense of a nullable annotation is equivalent to null oblivious // // null oblivious maps to whatever is "natural" that is: // - reference types allow null // - nullable value types allow null // - value types forbid null // need to de-ref member type, because the "ref-ness" of it doesn't matter // for nullability purposes var effectiveMemberType = memberType; while (effectiveMemberType.IsByRef) { effectiveMemberType = effectiveMemberType.GetElementTypeNonNull(); } // value types can only have meaningful nullable annotations // for their generic parameters, which means we never care // about it's annotations // // since ints and whatnot are pretty common, this shortcut // can save some real time on type describing. if (effectiveMemberType.IsValueType) { if (effectiveMemberType.IsNullableValueType(out _)) { return(NullHandling.AllowNull); } return(NullHandling.CannotBeNull); } // check for explicit attributes foreach (var attr in memberAttributes) { if (attr.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute") { var arg = attr.ConstructorArguments[0]; var argType = arg.ArgumentType.GetTypeInfo(); var val = arg.Value; if (val == null) { Throw.ImpossibleException($@"NullableAttribute with null first argument (on member {member}), this should not be possible"); return(default);
// create a delegate that will format the given value (pulled from a getter or a field) into // a buffer, subject to shouldSerialize being null or returning true // and return true if it was able to do so internal static ColumnWriterDelegate Create(TypeInfo type, Options options, Formatter formatter, NonNull<ShouldSerialize> shouldSerialize, Getter getter, bool emitDefaultValue) { var p1 = Expressions.Parameter_Object; var p2 = Expressions.Parameter_WriteContext_ByRef; var p3 = Expressions.Parameter_IBufferWriterOfChar; var statements = new List<Expression>(); var p1AsType = Expression.Convert(p1, type); var l1 = Expression.Variable(type, "l1"); var assignToL1 = Expression.Assign(l1, p1AsType); statements.Add(assignToL1); var end = Expression.Label(Types.Bool, "end"); var returnTrue = Expression.Label("return-true"); if (shouldSerialize.HasValue) { var ss = shouldSerialize.Value; if (ss.TakesNullability == NullHandling.ForbidNull && ss.Takes.Value.AllowsNullLikeValue()) { var takes = ss.Takes.Value; var checkExp = Utils.MakeNullHandlingCheckExpression(takes, l1, $"{ss} does not accept null rows, but was given one at runtime"); statements.Add(checkExp); } var callShouldSerialize = ss.MakeExpression(l1, p2); var shouldntSerialize = Expression.Not(callShouldSerialize); var jumpToEnd = Expression.Goto(returnTrue); var jumpToEndIfShouldntSerialize = Expression.IfThen(shouldntSerialize, jumpToEnd); statements.Add(jumpToEndIfShouldntSerialize); } var columnType = getter.Returns; var l2 = Expression.Variable(columnType, "l2"); var getExp = getter.MakeExpression(l1, p2); var assignToL2 = Expression.Assign(l2, getExp); statements.Add(assignToL2); if (getter.ReturnsNullability == NullHandling.ForbidNull && columnType.AllowsNullLikeValue()) { // do we need to check that a null didn't happen at runtime? var checkExp = Utils.MakeNullHandlingCheckExpression(getter.Returns, l2, $"{getter} was forbidden from return null values, but did return one at runtime"); statements.Add(checkExp); } if (!emitDefaultValue) { ConstantExpression defValue; if (columnType.IsValueType) { defValue = Expression.Constant(Activator.CreateInstance(columnType)); } else { defValue = Expression.Constant(null, columnType); } Expression isDefault; // intentionally letting GetMethod return null here if (!columnType.IsValueType || columnType.IsEnum || columnType.IsPrimitive || columnType.GetMethod("op_Equality") != null) { isDefault = Expression.Equal(l2, defValue); } else { var equatableI = Types.IEquatable.MakeGenericType(columnType).GetTypeInfo(); if (columnType.GetInterfaces().Any(i => i == equatableI)) { var equals = equatableI.GetMethodNonNull(nameof(IEquatable<object>.Equals)); var map = columnType.GetInterfaceMap(equatableI); MethodInfo? equalsTyped = null; for (var j = 0; j < map.InterfaceMethods.Length; j++) { if (map.InterfaceMethods[j] == equals) { equalsTyped = map.TargetMethods[j]; isDefault = Expression.Call(l2, equalsTyped, defValue); goto done; } } Throw.ImpossibleException($"Could not find typed {nameof(IEquatable<object>.Equals)} method, which shouldn't be possible", options); return default; } else { var eqsUntyped = columnType.GetMethodNonNull(nameof(object.Equals)); var defAsObject = Expression.Convert(defValue, Types.Object); isDefault = Expression.Call(l2, eqsUntyped, defAsObject); } } done: var ifIsDefaultReturnTrue = Expression.IfThen(isDefault, Expression.Goto(returnTrue)); statements.Add(ifIsDefaultReturnTrue); } if (formatter.TakesNullability == NullHandling.ForbidNull && formatter.Takes.AllowsNullLikeValue()) { // make sure formatter invariant hasn't been violated var checkExp = Utils.MakeNullHandlingCheckExpression(formatter.Takes, l2, $"{formatter} does not take null values, but received one at runtime"); statements.Add(checkExp); } var callFormatter = formatter.MakeExpression(l2, p2, p3); statements.Add(Expression.Goto(end, callFormatter)); statements.Add(Expression.Label(returnTrue)); statements.Add(Expression.Goto(end, Expressions.Constant_True)); statements.Add(Expression.Label(end, Expressions.Constant_False)); var block = Expression.Block(new[] { l1, l2 }, statements); var del = Expression.Lambda<ColumnWriterDelegate>(block, p1, p2, p3); var compiled = del.Compile(); return compiled; }
private ReadWithCommentResultType ProcessBuffer(int bufferLen, out int unprocessedCharacters) { var buffSpan = Buffer.Buffer.Span; ReaderStateMachine.AdvanceResult?inBatchableResult = null; var consistentResultSince = -1; for (var i = 0; i < bufferLen; i++) { var c = buffSpan[i]; var res = StateMachine.Advance(c, false); var advanceIBy = 0; // we only see this _if_ there are multiple characters in the separator, which is rare if (res == ReaderStateMachine.AdvanceResult.LookAhead_MultiCharacterSeparator) { var valSepLen = Configuration.ValueSeparatorMemory.Length; // do we have enough in the buffer to look ahead? var canCheckForSeparator = bufferLen - i >= valSepLen; if (canCheckForSeparator) { var shouldMatch = buffSpan.Slice(i, valSepLen); var eq = Utils.AreEqual(shouldMatch, Configuration.ValueSeparatorMemory.Span); if (eq) { // treat it like a value separator res = StateMachine.AdvanceValueSeparator(); // advance further to the last character in the separator advanceIBy = valSepLen - 1; } else { res = StateMachine.Advance(c, true); } } else { // we don't have enough in the buffer... so deal with any running batches and ask for more if (inBatchableResult != null) { switch (inBatchableResult.Value) { case ReaderStateMachine.AdvanceResult.Skip_Character: // there's no distinction between skipping several characters and skipping one // so this doesn't need the length Partial.SkipCharacter(); break; case ReaderStateMachine.AdvanceResult.Append_Character: var length = i - consistentResultSince; Partial.AppendCharacters(buffSpan, consistentResultSince, length); break; default: unprocessedCharacters = default; Throw.ImpossibleException($"Unexpected {nameof(ReaderStateMachine.AdvanceResult)}: {inBatchableResult.Value}", Configuration.Options); return(default); } } // we need to keep everything unprocessedCharacters = bufferLen - i; return(ReadWithCommentResultType.NoValue); } } var handledUpTo = i; // we _might_ need to modify i a bit if we just processed the fallout from a multi-char value separator i += advanceIBy; // try and batch skips and appends // to save time on copying AND on // basically pointless method calls if (inBatchableResult != null) { if (res == inBatchableResult) { continue; } else { switch (inBatchableResult.Value) { case ReaderStateMachine.AdvanceResult.Skip_Character: // there's no distinction between skipping several characters and skipping one // so this doesn't need the length Partial.SkipCharacter(); break; case ReaderStateMachine.AdvanceResult.Append_Character: var length = handledUpTo - consistentResultSince; Partial.AppendCharacters(buffSpan, consistentResultSince, length); break; default: unprocessedCharacters = default; Throw.ImpossibleException($"Unexpected {nameof(ReaderStateMachine.AdvanceResult)}: {inBatchableResult.Value}", Configuration.Options); return(default); } inBatchableResult = null; consistentResultSince = -1; // fall through into the switch to handle the current character } } // inBatchableResult is always null here // because if it's NOT null we either continue (if res == inBatchableResult), // thereby not hitting this point, or set it to null (if res != inBatchableResult) // this means we don't need to handle the inBatchableResult != null cases in // the following switch switch (res) { case ReaderStateMachine.AdvanceResult.Skip_Character: inBatchableResult = ReaderStateMachine.AdvanceResult.Skip_Character; consistentResultSince = i; continue; case ReaderStateMachine.AdvanceResult.Append_Character: inBatchableResult = ReaderStateMachine.AdvanceResult.Append_Character; consistentResultSince = i; continue; case ReaderStateMachine.AdvanceResult.Append_CarriageReturnAndCurrentCharacter: Partial.AppendCarriageReturn(buffSpan); Partial.AppendCharacters(buffSpan, i, 1); break; case ReaderStateMachine.AdvanceResult.Append_ValueSeparator: Partial.AppendCharactersFromDifferentBuffer(buffSpan, Configuration.ValueSeparatorMemory.Span); break; case ReaderStateMachine.AdvanceResult.Append_CarriageReturnAndValueSeparator: Partial.AppendCarriageReturn(buffSpan); Partial.AppendCharactersFromDifferentBuffer(buffSpan, Configuration.ValueSeparatorMemory.Span); break; // cannot reach ReaderStateMachine.AdvanceResult.Append_CarriageReturnAndEndComment, because that only happens // when the data ENDs case ReaderStateMachine.AdvanceResult.Finished_Unescaped_Value: PushPendingCharactersToValue(false); break; case ReaderStateMachine.AdvanceResult.Finished_Escaped_Value: PushPendingCharactersToValue(true); break; case ReaderStateMachine.AdvanceResult.Finished_LastValueUnescaped_Record: if (Partial.PendingCharsCount > 0) { PushPendingCharactersToValue(false); } unprocessedCharacters = bufferLen - i - 1; return(ReadWithCommentResultType.HasValue); case ReaderStateMachine.AdvanceResult.Finished_LastValueEscaped_Record: if (Partial.PendingCharsCount > 0) { PushPendingCharactersToValue(true); } unprocessedCharacters = bufferLen - i - 1; return(ReadWithCommentResultType.HasValue); case ReaderStateMachine.AdvanceResult.Finished_Comment: unprocessedCharacters = bufferLen - i - 1; return(ReadWithCommentResultType.HasComment); default: HandleUncommonAdvanceResults(res, c); break; } } // handle any batch that was still pending if (inBatchableResult != null) { switch (inBatchableResult.Value) { case ReaderStateMachine.AdvanceResult.Skip_Character: // there's no distinction between skipping several characters and skipping one // so this doesn't need the length Partial.SkipCharacter(); break; case ReaderStateMachine.AdvanceResult.Append_Character: // we read all the up to the end, so length needs to include the last character var length = bufferLen - consistentResultSince; Partial.AppendCharacters(buffSpan, consistentResultSince, length); break; default: unprocessedCharacters = default; Throw.ImpossibleException($"Unexpected {nameof(ReaderStateMachine.AdvanceResult)}: {inBatchableResult.Value}", Configuration.Options); return(default); } } unprocessedCharacters = 0; return(ReadWithCommentResultType.NoValue); }
/// <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()); }