static public int iter(IntPtr l) { object o = checkObj(l, 1); if (o is IEnumerable) { IEnumerable e = o as IEnumerable; var einfo = new EnumerableInfo { Enumerable = e }; einfo.Reset(); pushValue(l, true); pushLightObject(l, einfo); LuaDLL.lua_pushcclosure(l, _iter, 1); return(2); } else { var einfo = new EnumerableInfo { Enumerable = o }; einfo.Reset(); if (null == einfo.Enumerator) { return(error(l, "passed in object isn't enumerable")); } pushValue(l, true); pushLightObject(l, einfo); LuaDLL.lua_pushcclosure(l, _iter, 1); return(2); } }
public void FindMethods() { var sb = new StringBuilder(); foreach (var type in new[] { typeof(List <int>), typeof(IEnumerable <int>), typeof(ExplicitEnumerableBadCount), typeof(ExplicitReadOnlyCollection), typeof(ExplicitReadOnlyCollectionImplicitCount), typeof(ExplicitReadOnlyCollectionPublicEnumerator), typeof(ICollection <short>), typeof(IReadOnlyCollection <ushort>), typeof(IReadOnlyList <long>), typeof(IList <ulong>), this.GetType(), // throw in something non enumerable }) { var methods = EnumerableInfo.FindMethods(type); sb.AppendLine("# " + SchemaTest.HumanName(type)); DescribeMethod(sb, () => methods.GetEnumerator); DescribeMethod(sb, () => methods.MoveNext); DescribeMethod(sb, () => methods.get_Current); DescribeMethod(sb, () => methods.get_Count); DescribeMethod(sb, () => methods.Dispose); sb.AppendLine(); } ApprovalTests.Approvals.Verify(sb.ToString()); }
private void GenerateILForPushingEnumeratorOntoWriteStack(int depth, EnumerableInfo enumerableInfo) { switch (enumerableInfo.EnumeratorCategory) { case EnumeratorCategory.Array: case EnumeratorCategory.List: // _enumerableContextX = -1; _ilg.Emit(OpCodes.Ldarg_0); _ilg.Emit(OpCodes.Ldc_I4_M1); _ilg.Emit(OpCodes.Stfld, enumerableInfo.EnumerableContextField); break; case EnumeratorCategory.ValueTypeEnumerator: // _enumerableContextX = _writeStackX.GetEnumerator(); _ilg.Emit(OpCodes.Ldarg_0); _ilg.Emit(OpCodes.Ldarg_0); _ilg.Emit(OpCodes.Ldfld, GetWriteStackFieldAtDepth(depth)); _ilg.Emit(OpCodes.Call, enumerableInfo.GetEnumeratorMethod); // struct method cannot be virtual _ilg.Emit(OpCodes.Stfld, enumerableInfo.EnumerableContextField); break; case EnumeratorCategory.ReferenceTypeEnumerator: // _writeStackX = _writeStackX.GetEnumerator(); _ilg.Emit(OpCodes.Ldarg_0); _ilg.Emit(OpCodes.Ldarg_0); _ilg.Emit(OpCodes.Ldfld, GetWriteStackFieldAtDepth(depth)); _ilg.Emit(OpCodes.Callvirt, enumerableInfo.GetEnumeratorMethod); _ilg.Emit(OpCodes.Stfld, GetWriteStackFieldAtDepth(depth)); break; } }
static Expression HandleEnumerable(EnumerableInfo enumerableInfo, Expression enumerable, Func <Expression, Expression> body) { var enumeratorType = enumerableInfo.GetEnumerator.ReturnType; var enumeratorInfo = enumerableInfo.EnumeratorInfo; var enumeratorVariable = Variable(enumeratorType, "enumerator"); return(Block( new[] { enumeratorVariable }, Assign(enumeratorVariable, CallGetEnumerator(enumerableInfo, enumerable)), enumeratorInfo switch { { Dispose: null } => NotDisposable(enumeratorInfo, enumeratorVariable, body),
private void GenerateILForStartEnumerableWhile(int depth, EnumerableInfo enumerableInfo, Label bottomOfIterationLoop) { switch (enumerableInfo.EnumeratorCategory) { case EnumeratorCategory.Array: case EnumeratorCategory.List: // _enumerableContextX++ _ilg.Emit(OpCodes.Ldarg_0); _ilg.Emit(OpCodes.Ldarg_0); _ilg.Emit(OpCodes.Ldfld, enumerableInfo.EnumerableContextField); _ilg.Emit(OpCodes.Ldc_I4_1); _ilg.Emit(OpCodes.Add); _ilg.Emit(OpCodes.Stfld, enumerableInfo.EnumerableContextField); // while (_enumerableContextX < _writeStackX.Length/Count) _ilg.Emit(OpCodes.Ldarg_0); _ilg.Emit(OpCodes.Ldfld, enumerableInfo.EnumerableContextField); _ilg.Emit(OpCodes.Ldarg_0); _ilg.Emit(OpCodes.Ldfld, GetWriteStackFieldAtDepth(depth)); if (enumerableInfo.EnumeratorCategory == EnumeratorCategory.Array) { _ilg.Emit(OpCodes.Ldlen); } else { EmitCallVirtIfNeeded(_ilg, enumerableInfo.GetCountMethod); } _ilg.Emit(OpCodes.Bge, bottomOfIterationLoop); break; case EnumeratorCategory.ValueTypeEnumerator: // while (_enumerableContextX.MoveNext()) { _ilg.Emit(OpCodes.Ldarg_0); _ilg.Emit(OpCodes.Ldflda, enumerableInfo.EnumerableContextField); MethodInfo moveNextInterfaceMethod = typeof(IEnumerator).GetMethod("MoveNext", new Type[] { }); MethodInfo moveNextMethod = GetInterfaceMethodImplementation(enumerableInfo.EnumeratorType, moveNextInterfaceMethod); _ilg.Emit(OpCodes.Call, moveNextMethod); _ilg.Emit(OpCodes.Brfalse, bottomOfIterationLoop); break; case EnumeratorCategory.ReferenceTypeEnumerator: // while (_writeStackX.MoveNext()) { _ilg.Emit(OpCodes.Ldarg_0); _ilg.Emit(OpCodes.Ldfld, GetWriteStackFieldAtDepth(depth)); // MoveNext is defined in IEnumerator _ilg.Emit(OpCodes.Callvirt, typeof(IEnumerator).GetMethod("MoveNext", new Type[] { })); _ilg.Emit(OpCodes.Brfalse, bottomOfIterationLoop); break; } }
/// <summary> /// Creates a <see cref="System.Linq.Expressions.Expression"/> with behaviour similar to the <c>foreach</c> statement in C#. /// </summary> /// <param name="enumerableInfo">The information returned by a call to <see cref="NetFabric.Reflection.TypeExtensions.IsEnumerable(Type, out EnumerableInfo)"/>.</param> /// <param name="enumerable">An <see cref="System.Linq.Expressions.Expression"/> that defines the enumerator.</param> /// <param name="body"> /// A <see cref="System.Func<Expression, Expression>"/> that returns the body, given an /// <see cref="System.Linq.Expressions.Expression"/> that defines the item on each loop iteration. /// </param> /// <returns>The created <see cref="System.Linq.Expressions.Expression"/>.</returns> /// <remarks> /// <p> /// The created <see cref="System.Linq.Expressions.Expression"/> depends on if the object defined in <paramref name="enumerable"/> /// is an <c>interface</c>, <c>class</c>, <c>struct</c>, <c>ref struct</c>, and if is disposable. /// </p> /// </remarks> public static Expression ForEach(EnumerableInfo enumerableInfo, Expression enumerable, Func <Expression, Expression> body) { var enumerableType = enumerable.Type; if (enumerableType.IsArray) { return(HandleArray(enumerable, body)); } if (enumerableType.FullName is not null && (enumerableType.FullName.StartsWith("System.ReadOnlySpan`1") || enumerableType.FullName.StartsWith("System.Span`1"))) { return(HandleSpan(enumerable, body)); } return(HandleEnumerable(enumerableInfo, enumerable, body));
static public int iter(IntPtr l) { object o = checkObj(l, 1); if (o is IEnumerable) { IEnumerable e = o as IEnumerable; IEnumerator iter = e.GetEnumerator(); var einfo = new EnumerableInfo { Enumerable = e, Enumerator = iter }; pushValue(l, true); pushLightObject(l, einfo); LuaDLL.lua_pushcclosure(l, _iter, 1); return(2); } return(error(l, "passed in object isn't enumerable")); }
private void GenerateILForIEnumerable(Type type, int depth) { // First argument: Utf8JsonWriter writer // Second argument: IEnumerable<T> enumerable Type enumerateeType = GetIEnumerableGenericType(type); EnumerableInfo enumerableInfo = new EnumerableInfo(ref this, type, enumerateeType); // writer.WriteStartArray(); _ilg.Emit(OpCodes.Ldarg_1); _ilg.Emit(OpCodes.Call, _writeStartArray); GenerateILForPushingEnumeratorOntoWriteStack(depth, enumerableInfo); Label topOfIterationLoop = _ilg.DefineLabel(); Label bottomOfIterationLoop = _ilg.DefineLabel(); _ilg.MarkLabel(topOfIterationLoop); // while (...) { GenerateILForStartEnumerableWhile(depth, enumerableInfo, bottomOfIterationLoop); Action pushNextElementOntoStack = GeneratePushNextElementOntoStackLambda(depth, enumerateeType, enumerableInfo); JsonConverter converter = GetConverterFromOptions(_options, enumerateeType); if (converter == null) { GenerateILForNestedType(enumerateeType, depth + 1, pushNextElementOntoStack); } else { GenerateILForCallingConverter(enumerateeType, pushNextElementOntoStack, converter); } // } _ilg.Emit(OpCodes.Br, topOfIterationLoop); _ilg.MarkLabel(bottomOfIterationLoop); // writer.WriteEndArray(); _ilg.Emit(OpCodes.Ldarg_1); _ilg.Emit(OpCodes.Call, _writeEndArray); }
internal EnumerableNullableReferenceTypeAssertions(TActual actual, EnumerableInfo enumerableInfo) : base(actual) => EnumerableInfo = enumerableInfo;
protected static void AssertIsEnumerable <TActual, TActualItem>(TActual actual, out EnumerableInfo enumerableInfo) { var actualType = typeof(TActual); if (actualType == typeof(TActualItem[])) // convert TActualItem[] to IList<TActualItem> { actualType = typeof(IList <>).MakeGenericType(typeof(TActualItem)); } if (actualType.IsEnumerable(out var temp, out var errors)) { enumerableInfo = temp; var actualItemType = enumerableInfo.EnumeratorInfo.Current.PropertyType; if (actualItemType.IsByRef) { if (!actualItemType.IsAssignableTo(typeof(TActualItem).MakeByRefType())) { throw new ActualAssertionException <TActual>(actual, $"Expected to be an enumerable of '{typeof(TActualItem)}' but found an enumerable of '{actualItemType}'."); } } else { if (!actualItemType.IsAssignableTo(typeof(TActualItem))) { throw new ActualAssertionException <TActual>(actual, $"Expected to be an enumerable of '{typeof(TActualItem)}' but found an enumerable of '{actualItemType}'."); } } } else { if (errors.HasFlag(Errors.MissingGetEnumerator)) { throw new ActualAssertionException <TActual>(actual, $"Expected to be an enumerable but it's missing a valid 'GetEnumerator' method."); } if (errors.HasFlag(Errors.MissingCurrent)) { throw new ActualAssertionException <TActual>(actual, $"Expected to be an enumerator but it's missing a valid 'Current' property."); } if (errors.HasFlag(Errors.MissingMoveNext)) { throw new ActualAssertionException <TActual>(actual, $"Expected to be an enumerator but it's missing a valid 'MoveNext' method."); } enumerableInfo = default !;
internal EnumerableValueTypeAssertions(TActual actual, EnumerableInfo enumerableInfo) : base(actual) => EnumerableInfo = enumerableInfo;
private Action GeneratePushNextElementOntoStackLambda(int depth, Type enumerateeType, EnumerableInfo enumerableInfo) { ILGenerator ilg = _ilg; FieldBuilder currentWriteStack = GetWriteStackFieldAtDepth(depth); FieldBuilder enumerableContextField = enumerableInfo.EnumerableContextField; MethodInfo getCurrentMethod; switch (enumerableInfo.EnumeratorCategory) { case EnumeratorCategory.Array: return(() => { // _writeStackX[_enumerableContextX] ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldfld, currentWriteStack); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldfld, enumerableContextField); ilg.Emit(OpCodes.Ldelem, enumerateeType); }); case EnumeratorCategory.List: getCurrentMethod = enumerableInfo.IndexAccessorMethod; return(() => { // _writeStackX[_enumerableContextX] ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldfld, currentWriteStack); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldfld, enumerableContextField); EmitCallVirtIfNeeded(ilg, getCurrentMethod); }); case EnumeratorCategory.ValueTypeEnumerator: getCurrentMethod = enumerableInfo.GetCurrentMethod; return(() => { // _enumerableContextX.Current ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldflda, enumerableContextField); ilg.Emit(OpCodes.Call, getCurrentMethod); }); default: // Supress compiler error case EnumeratorCategory.ReferenceTypeEnumerator: getCurrentMethod = enumerableInfo.GetCurrentMethod; return(() => { // _writeStackX.Current ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldfld, currentWriteStack); ilg.Emit(OpCodes.Callvirt, getCurrentMethod); }); } }
private void GenerateILForIDictionary(Type type, int depth) { // First argument: Utf8JsonWriter writer // Second argument: IDictionary<string, T> dictionary Type valueType = GetIDictionaryGenericType(type)?.Value; Type keyValuePairType = typeof(KeyValuePair <,>).MakeGenericType(typeof(string), valueType); EnumerableInfo enumerableInfo = new EnumerableInfo(ref this, type, keyValuePairType); // writer.WriteStartObject(); _ilg.Emit(OpCodes.Ldarg_1); _ilg.Emit(OpCodes.Call, _writeStartObject); GenerateILForPushingEnumeratorOntoWriteStack(depth, enumerableInfo); Label topOfIterationLoop = _ilg.DefineLabel(); Label bottomOfIterationLoop = _ilg.DefineLabel(); _ilg.MarkLabel(topOfIterationLoop); // while (...) { GenerateILForStartEnumerableWhile(depth, enumerableInfo, bottomOfIterationLoop); Action pushNextKeyValuePairOntoStack = GeneratePushNextElementOntoStackLambda(depth, keyValuePairType, enumerableInfo); // KeyValuePair<string, T> kvp = _writeStackX.Current; LocalBuilder kvpLocal = _ilg.DeclareLocal(keyValuePairType); pushNextKeyValuePairOntoStack(); _ilg.Emit(OpCodes.Stloc, kvpLocal); // writer.WritePropertyName(DictionaryKeyPolicyField.ConvertName(Namekvp.Key)) _ilg.Emit(OpCodes.Ldarg_1); if (_dictionaryKeyPolicy != null) { _ilg.Emit(OpCodes.Ldsfld, _dictionaryKeyPolicyField); } _ilg.Emit(OpCodes.Ldloca, kvpLocal); _ilg.Emit(OpCodes.Call, keyValuePairType.GetProperty("Key").GetMethod); if (_dictionaryKeyPolicy != null) { _ilg.Emit(OpCodes.Callvirt, typeof(JsonNamingPolicy).GetMethod("ConvertName", new Type[] { typeof(string) })); } _ilg.Emit(OpCodes.Call, _writePropertyNameWithString); JsonConverter converter = GetConverterFromOptions(_options, valueType); ILGenerator ilg = _ilg; Action pushValueOntoStack = () => { // kvp.Value ilg.Emit(OpCodes.Ldloca, kvpLocal); ilg.Emit(OpCodes.Call, keyValuePairType.GetProperty("Value").GetMethod); }; if (converter == null) { GenerateILForNestedType(valueType, depth + 1, pushValueOntoStack); } else { GenerateILForCallingConverter(valueType, pushValueOntoStack, converter); } // } _ilg.Emit(OpCodes.Br, topOfIterationLoop); _ilg.MarkLabel(bottomOfIterationLoop); // writer.WriteEndObject(); _ilg.Emit(OpCodes.Ldarg_1); _ilg.Emit(OpCodes.Call, _writeEndObject); }