protected override bool EmitInternal(ParameterExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { if (whatReturn == ResultType.Void) { resultType = typeof(void); return(false); } ConstructorInfo constructor = node.Type.GetConstructor(Type.EmptyTypes); extend &= node != context.ParsedLambda.ClosureParameter && node != context.ParsedLambda.ConstantsParameter && !node.Type.IsStaticClosure() && ((node.Type.IsClass && constructor != null) || node.Type.IsArray); int index = Array.IndexOf(context.Parameters, node); if (index >= 0) { if (extend) { context.Il.Ldarg(index); var parameterIsNotNullLabel = context.Il.DefineLabel("parameterIsNotNull"); context.Il.Brtrue(parameterIsNotNullLabel); context.Il.Ldarga(index); context.Create(node.Type); context.Il.Stind(node.Type); context.MarkLabelAndSurroundWithSP(parameterIsNotNullLabel); } switch (whatReturn) { case ResultType.Value: context.Il.Ldarg(index); // stack: [parameter] resultType = node.Type; break; case ResultType.ByRefAll: context.Il.Ldarga(index); // stack: [¶meter] resultType = node.Type.MakeByRefType(); break; case ResultType.ByRefValueTypesOnly: if (node.Type.IsValueType) { context.Il.Ldarga(index); // stack: [¶meter] resultType = node.Type.MakeByRefType(); } else { context.Il.Ldarg(index); // stack: [parameter] resultType = node.Type; } break; default: throw new NotSupportedException("Result type '" + whatReturn + "' is not supported"); } return(false); } GroboIL.Local variable; if (context.VariablesToLocals.TryGetValue(node, out variable)) { if (extend) { context.Il.Ldloc(variable); var parameterIsNotNullLabel = context.Il.DefineLabel("parameterIsNotNull"); context.Il.Brtrue(parameterIsNotNullLabel); context.Create(node.Type); context.Il.Stloc(variable); context.MarkLabelAndSurroundWithSP(parameterIsNotNullLabel); } switch (whatReturn) { case ResultType.Value: context.Il.Ldloc(variable); // stack: [variable] resultType = node.Type; break; case ResultType.ByRefAll: context.Il.Ldloca(variable); // stack: [&variable] resultType = node.Type.MakeByRefType(); break; case ResultType.ByRefValueTypesOnly: if (node.Type.IsValueType) { context.Il.Ldloca(variable); // stack: [&variable] resultType = node.Type.MakeByRefType(); } else { context.Il.Ldloc(variable); // stack: [variable] resultType = node.Type; } break; default: throw new NotSupportedException("Result type '" + whatReturn + "' is not supported"); } return(false); } throw new InvalidOperationException("Unknown parameter " + node); }
protected override bool EmitInternal(IndexExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { if (node.Object == null) { throw new InvalidOperationException("Indexing of null object is invalid"); } if (node.Object.Type.IsArray && node.Object.Type.GetArrayRank() == 1) { return(ExpressionEmittersCollection.Emit(Expression.ArrayIndex(node.Object, node.Arguments.Single()), context, returnDefaultValueLabel, whatReturn, extend, out resultType)); } if (node.Object.Type.IsList()) { return(ArrayIndexExpressionEmitter.Emit(node.Object, node.Arguments.Single(), context, returnDefaultValueLabel, whatReturn, extend, out resultType)); } Type objectType; bool result = ExpressionEmittersCollection.Emit(node.Object, context, returnDefaultValueLabel, ResultType.ByRefValueTypesOnly, extend, out objectType); if (objectType.IsValueType) { using (var temp = context.DeclareLocal(objectType)) { context.Il.Stloc(temp); context.Il.Ldloca(temp); } } if (context.Options.HasFlag(CompilerOptions.CheckNullReferences)) { result |= context.EmitNullChecking(objectType, returnDefaultValueLabel); } if (node.Indexer != null) { context.EmitLoadArguments(node.Arguments.ToArray()); MethodInfo getter = node.Indexer.GetGetMethod(context.SkipVisibility); if (getter == null) { throw new MissingMethodException(node.Indexer.ReflectedType.ToString(), "get_" + node.Indexer.Name); } GroboIL.Label doneLabel = null; if (node.Object.Type.IsDictionary()) { var valueType = node.Object.Type.GetGenericArguments()[1]; var constructor = valueType.GetConstructor(Type.EmptyTypes); extend &= (valueType.IsClass && constructor != null) || valueType.IsArray || valueType.IsValueType || valueType == typeof(string); doneLabel = context.Il.DefineLabel("done"); using (var dict = context.DeclareLocal(node.Object.Type)) using (var key = context.DeclareLocal(node.Arguments.Single().Type)) using (var value = context.DeclareLocal(valueType)) { context.Il.Stloc(key); // key = arg0; stack: [dict] context.Il.Dup(); // stack: [dict, dict] context.Il.Stloc(dict); // stack: [dict] context.Il.Ldloc(key); // stack: [dict, key] context.Il.Ldloca(value); // stack: [dict, key, ref value] var tryGetValueMethod = node.Object.Type.GetMethod("TryGetValue", BindingFlags.Public | BindingFlags.Instance); if (tryGetValueMethod == null) { throw new MissingMethodException(node.Object.Type.Name, "TryGetValue"); } context.Il.Call(tryGetValueMethod, node.Object.Type); // stack: [dict.TryGetValue(key, out value)] var loadResultLabel = context.Il.DefineLabel("loadResult"); context.Il.Brtrue(loadResultLabel); // if(dict.TryGetValue(key, out result)) goto loadResult; stack: [] if (valueType.IsValueType) { context.Il.Ldloca(value); context.Il.Initobj(valueType); // value = default(valueType) context.Il.Ldloc(value); // stack: [default(valueType)] } else if (valueType == typeof(string)) { context.Il.Ldstr(""); } else { context.Create(valueType); } context.Il.Stloc(value); if (extend) { context.Il.Ldloc(dict); // stack: [dict] context.Il.Ldloc(key); // stack: [dict, key] context.Il.Ldloc(value); // stack: [dict, key, value] var setter = node.Indexer.GetSetMethod(context.SkipVisibility); if (setter == null) { throw new MissingMethodException(node.Indexer.ReflectedType.ToString(), "set_" + node.Indexer.Name); } context.Il.Call(setter, node.Object.Type); // dict.set_Item(key, default(valueType)); stack: [] } context.MarkLabelAndSurroundWithSP(loadResultLabel); context.Il.Ldloc(value); } } if (doneLabel != null) { context.MarkLabelAndSurroundWithSP(doneLabel); } else { context.Il.Call(getter, node.Object.Type); } } else { Type arrayType = node.Object.Type; if (!arrayType.IsArray) { throw new InvalidOperationException("An array expected"); } int rank = arrayType.GetArrayRank(); if (rank != node.Arguments.Count) { throw new InvalidOperationException("Incorrect number of indeces '" + node.Arguments.Count + "' provided to access an array with rank '" + rank + "'"); } Type indexType = node.Arguments.First().Type; if (indexType != typeof(int)) { throw new InvalidOperationException("Indexing array with an index of type '" + indexType + "' is not allowed"); } context.EmitLoadArguments(node.Arguments.ToArray()); MethodInfo getMethod = arrayType.GetMethod("Get"); if (getMethod == null) { throw new MissingMethodException(arrayType.ToString(), "Get"); } context.Il.Call(getMethod, arrayType); } resultType = node.Type; return(result); }
public static bool Emit(Expression zarr, Expression zindex, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { var arrayType = zarr.Type; var isArray = arrayType.IsArray; if (!isArray && !arrayType.IsList()) { throw new InvalidOperationException("Unable to perform array index operator to type '" + arrayType + "'"); } var itemType = isArray ? arrayType.GetElementType() : arrayType.GetGenericArguments()[0]; GroboIL il = context.Il; EmittingContext.LocalHolder arrayIndex = null; bool extendArray = extend && (CanAssign(zarr) || !isArray); bool extendArrayElement = extend && itemType.IsClass; var result = false; if (!extendArray) { result |= ExpressionEmittersCollection.Emit(zarr, context, returnDefaultValueLabel, ResultType.Value, extend, out arrayType); // stack: [array] if (context.Options.HasFlag(CompilerOptions.CheckNullReferences)) { result = true; il.Dup(); // stack: [array, array] il.Brfalse(returnDefaultValueLabel); // if(array == null) goto returnDefaultValue; stack: [array] } EmitLoadIndex(zindex, context, arrayType); // stack: [array, arrayIndex] if (context.Options.HasFlag(CompilerOptions.CheckArrayIndexes)) { result = true; arrayIndex = context.DeclareLocal(typeof(int)); il.Stloc(arrayIndex); // arrayIndex = index; stack: [array] il.Dup(); // stack: [array, array] if (isArray) { il.Ldlen(); // stack: [array, array.Length] } else { EmitLoadField(context, arrayType, arrayType.GetField("_size", BindingFlags.Instance | BindingFlags.NonPublic)); } il.Ldloc(arrayIndex); // stack: [array, array.Length, arrayIndex] il.Ble(returnDefaultValueLabel, false); // if(array.Length <= arrayIndex) goto returnDefaultValue; stack: [array] il.Ldloc(arrayIndex); // stack: [array, arrayIndex] il.Ldc_I4(0); // stack: [array, arrayIndex, 0] il.Blt(returnDefaultValueLabel, false); // if(arrayIndex < 0) goto returnDefaultValue; stack: [array] } else if (extendArrayElement || !isArray) { arrayIndex = context.DeclareLocal(typeof(int)); il.Stloc(arrayIndex); // arrayIndex = index; stack: [array] } } else { EmittingContext.LocalHolder arrayOwner = null; switch (zarr.NodeType) { case ExpressionType.Parameter: case ExpressionType.ArrayIndex: case ExpressionType.Index: Type type; ExpressionEmittersCollection.Emit(zarr, context, returnDefaultValueLabel, ResultType.ByRefAll, true, out type); // stack: [ref array] arrayOwner = context.DeclareLocal(type); il.Dup(); // stack: [ref array, ref array] il.Stloc(arrayOwner); // arrayOwner = ref array; stack: [ref array] il.Ldind(zarr.Type); // stack: [array] break; case ExpressionType.MemberAccess: var memberExpression = (MemberExpression)zarr; Type memberType; context.EmitMemberAccess(memberExpression, returnDefaultValueLabel, context.Options.HasFlag(CompilerOptions.CheckNullReferences), true, ResultType.ByRefValueTypesOnly, out memberType, out arrayOwner); // stack: [array] break; default: throw new InvalidOperationException("Cannot extend array for expression with node type '" + zarr.NodeType + "'"); } if (context.Options.HasFlag(CompilerOptions.CheckNullReferences)) { il.Dup(); // stack: [array, array] il.Brfalse(returnDefaultValueLabel); // if(array == null) goto returnDefaultValue; stack: [array] } EmitLoadIndex(zindex, context, arrayType); result = true; arrayIndex = context.DeclareLocal(typeof(int)); il.Stloc(arrayIndex); // arrayIndex = index; stack: [array] il.Ldloc(arrayIndex); // stack: [array, arrayIndex] il.Ldc_I4(0); // stack: [array, arrayIndex, 0] il.Blt(returnDefaultValueLabel, false); // if(arrayIndex < 0) goto returnDefaultValue; stack: [array] il.Dup(); // stack: [array, array] if (isArray) { il.Ldlen(); // stack: [array, array.Length] } else { EmitLoadField(context, arrayType, arrayType.GetField("_size", BindingFlags.Instance | BindingFlags.NonPublic)); } il.Ldloc(arrayIndex); // stack: [array, array.Length, arrayIndex] var bigEnoughLabel = il.DefineLabel("bigEnough"); il.Bgt(bigEnoughLabel, false); // if(array.Length > arrayIndex) goto bigEnough; stack: [array] using (var array = context.DeclareLocal(arrayType)) { il.Stloc(array); // stack: [] if (!isArray) { EnsureCount(context, array, arrayIndex, arrayType); } else { il.Ldloca(array); // stack: [ref array] il.Ldloc(arrayIndex); // stack: [ref array, arrayIndex] il.Ldc_I4(1); // stack: [ref array, arrayIndex, 1] il.Add(); // stack: [ref array, arrayIndex + 1] il.Call(arrayResizeMethod.MakeGenericMethod(arrayType.GetElementType())); // Array.Resize(ref array, 1 + arrayIndex); stack: [] switch (zarr.NodeType) { case ExpressionType.Parameter: case ExpressionType.ArrayIndex: case ExpressionType.Index: il.Ldloc(arrayOwner); // stack: [ref parameter] il.Ldloc(array); // stack: [ref parameter, array] il.Stind(arrayType); // parameter = array; stack: [] break; case ExpressionType.MemberAccess: var memberExpression = (MemberExpression)zarr; if (memberExpression.Expression != null) { il.Ldloc(arrayOwner); } il.Ldloc(array); switch (memberExpression.Member.MemberType) { case MemberTypes.Field: il.Stfld((FieldInfo)memberExpression.Member); break; case MemberTypes.Property: var propertyInfo = (PropertyInfo)memberExpression.Member; var setter = propertyInfo.GetSetMethod(context.SkipVisibility); if (setter == null) { throw new MissingMethodException(propertyInfo.ReflectedType.ToString(), "set_" + propertyInfo.Name); } il.Call(setter, memberExpression.Expression == null ? null : memberExpression.Expression.Type); break; default: throw new NotSupportedException("Member type '" + memberExpression.Member.MemberType + "' is not supported"); } break; default: throw new InvalidOperationException("Unable to assign array to an expression with node type '" + zarr.NodeType); } } il.Ldloc(array); context.MarkLabelAndSurroundWithSP(bigEnoughLabel); } } if (!isArray) { // TODO: это злобно, лист при всех операциях меняет _version, а мы нет EmitLoadField(context, arrayType, arrayType.GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic)); arrayType = itemType.MakeArrayType(); } if (extendArrayElement) { // stack: [array] var constructor = itemType.GetConstructor(Type.EmptyTypes); if (itemType.IsArray || constructor != null) { using (var array = context.DeclareLocal(arrayType)) { il.Dup(); // stack: [array, array] il.Stloc(array); // stack: [array] il.Ldloc(arrayIndex); // stack: [array, arrayIndex] il.Ldelem(itemType); // stack: [array[arrayIndex]] var elementIsNotNullLabel = il.DefineLabel("elementIsNotNull"); il.Brtrue(elementIsNotNullLabel); il.Ldloc(array); il.Ldloc(arrayIndex); context.Create(itemType); il.Stelem(itemType); context.MarkLabelAndSurroundWithSP(elementIsNotNullLabel); il.Ldloc(array); } } } if (arrayIndex != null) { il.Ldloc(arrayIndex); arrayIndex.Dispose(); } switch (whatReturn) { case ResultType.ByRefAll: il.Ldelema(itemType); resultType = itemType.MakeByRefType(); break; case ResultType.ByRefValueTypesOnly: if (itemType.IsValueType) { il.Ldelema(itemType); resultType = itemType.MakeByRefType(); } else { il.Ldelem(itemType); // stack: [array[arrayIndex]] resultType = itemType; } break; default: il.Ldelem(itemType); // stack: [array[arrayIndex]] resultType = itemType; break; } return(result); }