// bind the instance provider static InstanceProviderDelegate <TRow> MakeInstanceProviderDelegate(InstanceProvider instanceProvider) { var rowType = typeof(TRow).GetTypeInfo(); var ctxParam = Expressions.Parameter_ReadContext_ByRef; var outParam = Expression.Parameter(rowType.MakeByRefType().GetTypeInfo()); var callInstanceProvider = instanceProvider.MakeExpression(rowType, ctxParam, outParam); Expression <InstanceProviderDelegate <TRow> > lambda; if (instanceProvider.ConstructsNullability == NullHandling.ForbidNull && instanceProvider.ConstructsType.AllowsNullLikeValue()) { // need to inject extra logic to make sure the runtime value doesn't violate the ForbidNull handling var resParam = Expressions.Variable_Bool; var assignRes = Expression.Assign(resParam, callInstanceProvider); var checkExp = Utils.MakeNullHandlingCheckExpression(instanceProvider.ConstructsType, outParam, $"{instanceProvider} was forbidden from producing null values, but did produce one at runtime"); var ifExp = Expression.IfThen(resParam, checkExp); var body = Expression.Block(new[] { resParam }, assignRes, ifExp, resParam); lambda = Expression.Lambda <InstanceProviderDelegate <TRow> >(body, ctxParam, outParam); } else { // simple pass through lambda = Expression.Lambda <InstanceProviderDelegate <TRow> >(callInstanceProvider, ctxParam, outParam); } var del = lambda.Compile(); return(del); }
// 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; }