protected override bool EmitInternal(NewArrayExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { var il = context.Il; Type elementType = node.Type.GetElementType(); il.Ldc_I4(node.Expressions.Count); // stack: [length] il.Newarr(elementType); // stack: [new type[length]] for (int index = 0; index < node.Expressions.Count; index++) { var expression = node.Expressions[index]; il.Dup(); il.Ldc_I4(index); if (elementType.IsValueType && !elementType.IsPrimitive) { il.Ldelema(elementType); context.EmitLoadArguments(expression); il.Stobj(elementType); } else { context.EmitLoadArguments(expression); il.Stelem(elementType); } } resultType = node.Type; return(false); }
protected override bool EmitInternal(MemberInitExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { ExpressionEmittersCollection.Emit(node.NewExpression, context, out resultType); // stack: [new obj(args)] GroboIL il = context.Il; if (!node.Type.IsValueType) { foreach (MemberAssignment assignment in node.Bindings) { il.Dup(); context.EmitLoadArguments(assignment.Expression); context.EmitMemberAssign(node.Type, assignment.Member); } } else { using (var temp = context.DeclareLocal(node.Type)) { il.Stloc(temp); il.Ldloca(temp); foreach (MemberAssignment assignment in node.Bindings) { il.Dup(); context.EmitLoadArguments(assignment.Expression); context.EmitMemberAssign(node.Type, assignment.Member); } // todo или il.Ldobj()? il.Pop(); il.Ldloc(temp); } } return(false); }
protected override bool EmitInternal(ListInitExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { var il = context.Il; var result = ExpressionEmittersCollection.Emit(node.NewExpression, context, returnDefaultValueLabel, out resultType); if (!resultType.IsValueType) { foreach (var initializer in node.Initializers) { il.Dup(); context.EmitLoadArguments(initializer.Arguments.ToArray()); il.Call(initializer.AddMethod, resultType); } } else { if (node.Initializers.Count > 0) { using (var temp = context.DeclareLocal(resultType)) { il.Stloc(temp); foreach (var initializer in node.Initializers) { il.Ldloca(temp); context.EmitLoadArguments(initializer.Arguments.ToArray()); il.Call(initializer.AddMethod, resultType); } il.Ldloc(temp); } } } return(result); }
protected override bool EmitInternal(InvocationExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { bool result; if (node.Expression.NodeType != ExpressionType.Lambda) { Type delegateType; result = ExpressionEmittersCollection.Emit(node.Expression, context, returnDefaultValueLabel, ResultType.Value, extend, out delegateType); context.EmitLoadArguments(node.Arguments.ToArray()); var invokeMethod = delegateType.GetMethod("Invoke", node.Arguments.Select(argument => argument.Type).ToArray()); context.Il.Call(invokeMethod, delegateType); } else { result = false; var lambda = (LambdaExpression)node.Expression; Type[] constantTypes; var compiledLambda = LambdaExpressionEmitter.CompileAndLoadConstants(lambda, context, out constantTypes); context.EmitLoadArguments(node.Arguments.ToArray()); context.LoadCompiledLambdaPointer(compiledLambda); context.Il.Calli(CallingConventions.Standard, lambda.ReturnType, constantTypes.Concat(lambda.Parameters.Select(parameter => parameter.Type)).ToArray()); } resultType = node.Type; return(result); }
protected override bool EmitInternal(NewArrayExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { var il = context.Il; if (node.Expressions.Count != 1) { context.EmitLoadArguments(node.Expressions.ToArray()); il.Newobj(node.Type.GetConstructor(node.Expressions.Select(exp => exp.Type).ToArray())); } else { GroboIL.Label lengthIsNullLabel = context.CanReturn ? il.DefineLabel("lengthIsNull") : null; Type lengthType; var labelUsed = ExpressionEmittersCollection.Emit(node.Expressions.Single(), context, lengthIsNullLabel, out lengthType); if (lengthType != typeof(int)) { throw new InvalidOperationException("Cannot create an array with length of type '" + lengthType + "'"); } if (labelUsed && context.CanReturn) { var lengthIsNotNullLabel = il.DefineLabel("lengthIsNotNull"); il.Br(lengthIsNotNullLabel); context.MarkLabelAndSurroundWithSP(lengthIsNullLabel); il.Pop(); il.Ldc_I4(0); context.MarkLabelAndSurroundWithSP(lengthIsNotNullLabel); } il.Newarr(node.Type.GetElementType()); } resultType = node.Type; return(false); }
protected override bool EmitInternal(NewExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { context.EmitLoadArguments(node.Arguments.ToArray()); // note ich: баг решарпера // ReSharper disable ConditionIsAlwaysTrueOrFalse // ReSharper disable HeuristicUnreachableCode GroboIL il = context.Il; if (node.Constructor != null) { il.Newobj(node.Constructor); } else { if (node.Type.IsValueType) { using (var temp = context.DeclareLocal(node.Type)) { il.Ldloca(temp); il.Initobj(node.Type); il.Ldloc(temp); } } else { throw new InvalidOperationException("Missing constructor for type '" + node.Type + "'"); } } resultType = node.Type; // ReSharper restore ConditionIsAlwaysTrueOrFalse // ReSharper restore HeuristicUnreachableCode return(false); }
protected override bool EmitInternal(TryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { var il = context.Il; il.BeginExceptionBlock(); returnDefaultValueLabel = context.CanReturn ? il.DefineLabel("returnDefaultValue") : null; bool returnDefaultValueLabelUsed = ExpressionEmittersCollection.Emit(node.Body, context, returnDefaultValueLabel, whatReturn, extend, out resultType); EmittingContext.LocalHolder retValue = null; var doneLabel = il.DefineLabel("done"); if (resultType == typeof(void)) { if (returnDefaultValueLabelUsed) { context.MarkLabelAndSurroundWithSP(returnDefaultValueLabel); } il.Leave(doneLabel); } else { retValue = context.DeclareLocal(resultType); il.Stloc(retValue); il.Leave(doneLabel); if (returnDefaultValueLabelUsed) { context.MarkLabelAndSurroundWithSP(returnDefaultValueLabel); if (resultType.IsValueType) { il.Ldloca(retValue); il.Initobj(resultType); } else { il.Ldnull(); il.Stloc(retValue); } il.Leave(doneLabel); } } foreach (var catchBlock in node.Handlers) { bool disposeVariable = false; var variable = catchBlock.Variable; if (catchBlock.Filter == null) { il.BeginCatchBlock(catchBlock.Test); if (variable == null) { il.Pop(); } else { // todo вызвать ф-цию из AssignExpressionEmitter var index = Array.IndexOf(context.Parameters, variable); if (index >= 0) { il.Starg(index); } else { GroboIL.Local local; if (!context.VariablesToLocals.TryGetValue(variable, out local)) { local = context.DeclareLocal(variable.Type); context.VariablesToLocals.Add(variable, local); context.Variables.Push(variable); disposeVariable = true; } il.Stloc(local); } } } else { il.BeginExceptFilterBlock(); il.Isinst(catchBlock.Test); il.Dup(); var rightTypeLabel = il.DefineLabel("rightType"); il.Brtrue(rightTypeLabel); il.Pop(); il.Ldc_I4(0); var endFilterLabel = il.DefineLabel("endFilter"); il.Br(endFilterLabel); context.MarkLabelAndSurroundWithSP(rightTypeLabel); if (variable == null) { il.Pop(); } else { // todo вызвать ф-цию из AssignExpressionEmitter var index = Array.IndexOf(context.Parameters, variable); if (index >= 0) { il.Starg(index); } else { GroboIL.Local local; if (!context.VariablesToLocals.TryGetValue(variable, out local)) { local = string.IsNullOrEmpty(variable.Name) ? context.Il.DeclareLocal(variable.Type) : context.Il.DeclareLocal(variable.Type, variable.Name, appendUniquePrefix: false); if (context.DebugInfoGenerator != null) { local.SetLocalSymInfo(local.Name); } context.VariablesToLocals.Add(variable, local); context.Variables.Push(variable); disposeVariable = true; } il.Stloc(local); } } GroboIL.Label returnFalseLabel = context.CanReturn ? il.DefineLabel("returnFalse") : null; Type filterResultType; bool returnFalseLabelUsed = ExpressionEmittersCollection.Emit(catchBlock.Filter, context, returnFalseLabel, out filterResultType); if (returnFalseLabelUsed) { il.Br(endFilterLabel); context.MarkLabelAndSurroundWithSP(returnFalseLabel); il.Pop(); il.Ldc_I4(0); } context.MarkLabelAndSurroundWithSP(endFilterLabel); il.BeginCatchBlock(null); il.Pop(); } context.EmitLoadArguments(catchBlock.Body); if (catchBlock.Body.Type != typeof(void)) { il.Stloc(retValue); } if (disposeVariable) { context.VariablesToLocals.Remove(variable); context.Variables.Pop(); } } if (node.Fault != null) { il.BeginFaultBlock(); EmitExpressionAsVoid(node.Fault, context); } if (node.Finally != null) { il.BeginFinallyBlock(); EmitExpressionAsVoid(node.Finally, context); } il.EndExceptionBlock(); context.MarkLabelAndSurroundWithSP(doneLabel); if (retValue != null) { il.Ldloc(retValue); retValue.Dispose(); } return(false); }
protected override bool EmitInternal(SwitchExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { GroboIL il = context.Il; var defaultLabel = il.DefineLabel("default"); var caseLabels = new GroboIL.Label[node.Cases.Count]; GroboIL.Label switchValueIsNullLabel = null; for (int index = 0; index < node.Cases.Count; index++) { caseLabels[index] = il.DefineLabel("case#" + index); } context.EmitLoadArguments(node.SwitchValue); using (var switchValue = context.DeclareLocal(node.SwitchValue.Type)) { il.Stloc(switchValue); Tuple <int, int, int> switchCase; if (context.ParsedLambda.ParsedSwitches.TryGetValue(node, out switchCase)) { // use simplified hashtable to locate the proper case var labels = new List <GroboIL.Label>(); for (int index = 0; index < node.Cases.Count; index++) { foreach (var testValue in node.Cases[index].TestValues) { if (((ConstantExpression)testValue).Value != null) { labels.Add(caseLabels[index]); } else { switchValueIsNullLabel = caseLabels[index]; } } } if (switchValueIsNullLabel != null) { if (!node.SwitchValue.Type.IsNullable()) { il.Ldloc(switchValue); } else { il.Ldloca(switchValue); context.EmitHasValueAccess(node.SwitchValue.Type); } il.Brfalse(switchValueIsNullLabel); } EmittingContext.LocalHolder pureSwitchValue = switchValue; if (node.SwitchValue.Type.IsNullable()) { pureSwitchValue = context.DeclareLocal(node.SwitchValue.Type.GetGenericArguments()[0]); il.Ldloca(switchValue); context.EmitValueAccess(node.SwitchValue.Type); il.Stloc(pureSwitchValue); } Type temp; ExpressionEmittersCollection.Emit(context.ParsedLambda.ConstantsBuilder.MakeAccess(context.ParsedLambda.ConstantsParameter, switchCase.Item1), context, out temp); var type = node.SwitchValue.Type.IsNullable() ? node.SwitchValue.Type.GetGenericArguments()[0] : node.SwitchValue.Type; var typeCode = Type.GetTypeCode(type); switch (typeCode) { case TypeCode.Byte: case TypeCode.Char: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.SByte: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: il.Ldloc(pureSwitchValue); break; default: if (type.IsValueType) { il.Ldloca(pureSwitchValue); } else { il.Ldloc(pureSwitchValue); } il.Call(typeof(object).GetMethod("GetHashCode"), type); break; } using (var index = context.DeclareLocal(typeof(int))) { if (typeCode == TypeCode.Int64 || typeCode == TypeCode.UInt64) { il.Ldc_I8(switchCase.Item3); } else { il.Ldc_I4(switchCase.Item3); } il.Rem(true); if (typeCode == TypeCode.Int64 || typeCode == TypeCode.UInt64) { il.Conv <int>(); } il.Stloc(index); il.Ldloc(index); il.Ldelem(type); il.Ldloc(pureSwitchValue); if (node.Comparison != null) { il.Call(node.Comparison); } else { il.Ceq(); } il.Brfalse(defaultLabel); ExpressionEmittersCollection.Emit(context.ParsedLambda.ConstantsBuilder.MakeAccess(context.ParsedLambda.ConstantsParameter, switchCase.Item2), context, out temp); il.Ldloc(index); il.Ldelem(typeof(int)); il.Switch(labels.ToArray()); } if (pureSwitchValue != switchValue) { pureSwitchValue.Dispose(); } } else { // use a number of if/else branches to locate the proper case EmittingContext.LocalHolder pureSwitchValue = switchValue; EmittingContext.LocalHolder switchValueIsNull = null; if (node.SwitchValue.Type.IsNullable()) { pureSwitchValue = context.DeclareLocal(node.SwitchValue.Type.GetGenericArguments()[0]); switchValueIsNull = context.DeclareLocal(typeof(bool)); il.Ldloca(switchValue); il.Dup(); context.EmitValueAccess(node.SwitchValue.Type); il.Stloc(pureSwitchValue); context.EmitHasValueAccess(node.SwitchValue.Type); il.Stloc(switchValueIsNull); } for (int index = 0; index < node.Cases.Count; index++) { var caSe = node.Cases[index]; var label = caseLabels[index]; foreach (var testValue in caSe.TestValues) { context.EmitLoadArguments(testValue); GroboIL.Label elseLabel = null; if (testValue.Type.IsNullable()) { elseLabel = il.DefineLabel("else"); using (var temp = context.DeclareLocal(testValue.Type)) { il.Stloc(temp); il.Ldloca(temp); context.EmitHasValueAccess(testValue.Type); if (switchValueIsNull != null) { il.Ldloc(switchValueIsNull); il.Or(); il.Brfalse(label); il.Ldloca(temp); context.EmitHasValueAccess(testValue.Type); il.Ldloc(switchValueIsNull); il.And(); } il.Brfalse(elseLabel); il.Ldloca(temp); context.EmitValueAccess(testValue.Type); } } il.Ldloc(pureSwitchValue); if (node.Comparison != null) { il.Call(node.Comparison); } else { il.Ceq(); } il.Brtrue(label); if (elseLabel != null) { context.MarkLabelAndSurroundWithSP(elseLabel); } } } } } context.MarkLabelAndSurroundWithSP(defaultLabel); var doneLabel = il.DefineLabel("done"); context.EmitLoadArguments(node.DefaultBody); il.Br(doneLabel); for (int index = 0; index < node.Cases.Count; ++index) { context.MarkLabelAndSurroundWithSP(caseLabels[index]); context.EmitLoadArguments(node.Cases[index].Body); if (index < node.Cases.Count - 1) { il.Br(doneLabel); } } context.MarkLabelAndSurroundWithSP(doneLabel); resultType = node.Type; return(false); }
private static void EmitAssign(AssigneeKind assigneeKind, Expression node, EmittingContext context, object[] arguments, EmittingContext.LocalHolder value) { var il = context.Il; switch (assigneeKind) { case AssigneeKind.Parameter: il.Ldloc(value); var index = Array.IndexOf(context.Parameters, node); if (index >= 0) { il.Starg(index); } else { GroboIL.Local variable; if (context.VariablesToLocals.TryGetValue((ParameterExpression)node, out variable)) { il.Stloc(variable); } else { throw new InvalidOperationException("Unknown parameter " + node); } } break; case AssigneeKind.SimpleArray: il.Ldloc(value); il.Stind(node.Type); break; case AssigneeKind.InstanceField: case AssigneeKind.StaticField: il.Ldloc(value); il.Stfld((FieldInfo)((MemberExpression)node).Member); break; case AssigneeKind.InstanceProperty: case AssigneeKind.StaticProperty: il.Ldloc(value); var memberExpression = (MemberExpression)node; il.Call(((PropertyInfo)memberExpression.Member).GetSetMethod(context.SkipVisibility), memberExpression.Expression == null ? null : memberExpression.Expression.Type); break; case AssigneeKind.IndexedProperty: { var indexExpression = (IndexExpression)node; if (arguments == null) { context.EmitLoadArguments(indexExpression.Arguments.ToArray()); } else { foreach (var argument in arguments) { if (argument is Expression) { context.EmitLoadArguments((Expression)argument); } else { var local = (EmittingContext.LocalHolder)argument; il.Ldloc(local); local.Dispose(); } } } il.Ldloc(value); MethodInfo setter = indexExpression.Indexer.GetSetMethod(context.SkipVisibility); if (setter == null) { throw new MissingMethodException(indexExpression.Indexer.ReflectedType.ToString(), "set_" + indexExpression.Indexer.Name); } context.Il.Call(setter, indexExpression.Object.Type); } break; case AssigneeKind.MultiDimensionalArray: { var indexExpression = (IndexExpression)node; Type arrayType = indexExpression.Object.Type; if (!arrayType.IsArray) { throw new InvalidOperationException("An array expected"); } int rank = arrayType.GetArrayRank(); if (rank != indexExpression.Arguments.Count) { throw new InvalidOperationException("Incorrect number of indeces '" + indexExpression.Arguments.Count + "' provided to access an array with rank '" + rank + "'"); } Type indexType = indexExpression.Arguments.First().Type; if (indexType != typeof(int)) { throw new InvalidOperationException("Indexing array with an index of type '" + indexType + "' is not allowed"); } if (arguments == null) { context.EmitLoadArguments(indexExpression.Arguments.ToArray()); } else { foreach (var argument in arguments) { if (argument is Expression) { context.EmitLoadArguments((Expression)argument); } else { var local = (EmittingContext.LocalHolder)argument; il.Ldloc(local); local.Dispose(); } } } il.Ldloc(value); MethodInfo setMethod = arrayType.GetMethod("Set"); if (setMethod == null) { throw new MissingMethodException(arrayType.ToString(), "Set"); } context.Il.Call(setMethod, arrayType); } break; } }
private static object[] EmitAccess(AssigneeKind assigneeKind, Expression node, EmittingContext context) { object[] arguments = null; var il = context.Il; switch (assigneeKind) { case AssigneeKind.Parameter: var index = Array.IndexOf(context.Parameters, node); if (index >= 0) { il.Ldarg(index); } else { GroboIL.Local variable; if (context.VariablesToLocals.TryGetValue((ParameterExpression)node, out variable)) { il.Ldloc(variable); } else { throw new InvalidOperationException("Unknown parameter " + node); } } break; case AssigneeKind.InstanceField: case AssigneeKind.StaticField: il.Ldfld((FieldInfo)((MemberExpression)node).Member); break; case AssigneeKind.InstanceProperty: case AssigneeKind.StaticProperty: var memberExpression = (MemberExpression)node; il.Call(((PropertyInfo)memberExpression.Member).GetGetMethod(context.SkipVisibility), memberExpression.Expression == null ? null : memberExpression.Expression.Type); break; case AssigneeKind.SimpleArray: il.Ldind(node.Type); break; case AssigneeKind.IndexedProperty: { var indexExpression = (IndexExpression)node; var args = new List <object>(); foreach (var argument in indexExpression.Arguments) { context.EmitLoadArguments(argument); if (argument.NodeType == ExpressionType.Constant || (argument.NodeType == ExpressionType.MemberAccess && ((MemberExpression)argument).Member.MemberType == MemberTypes.Field && ((FieldInfo)((MemberExpression)argument).Member).IsStatic)) { args.Add(argument); } else { var local = context.DeclareLocal(argument.Type); args.Add(local); il.Stloc(local); il.Ldloc(local); } } arguments = args.ToArray(); MethodInfo getter = indexExpression.Indexer.GetGetMethod(context.SkipVisibility); if (getter == null) { throw new MissingMethodException(indexExpression.Indexer.ReflectedType.ToString(), "get_" + indexExpression.Indexer.Name); } context.Il.Call(getter, indexExpression.Object.Type); } break; case AssigneeKind.MultiDimensionalArray: { var indexExpression = (IndexExpression)node; Type arrayType = indexExpression.Object.Type; if (!arrayType.IsArray) { throw new InvalidOperationException("An array expected"); } int rank = arrayType.GetArrayRank(); if (rank != indexExpression.Arguments.Count) { throw new InvalidOperationException("Incorrect number of indeces '" + indexExpression.Arguments.Count + "' provided to access an array with rank '" + rank + "'"); } Type indexType = indexExpression.Arguments.First().Type; if (indexType != typeof(int)) { throw new InvalidOperationException("Indexing array with an index of type '" + indexType + "' is not allowed"); } var args = new List <object>(); foreach (var argument in indexExpression.Arguments) { context.EmitLoadArguments(argument); if (argument.NodeType == ExpressionType.Constant || (argument.NodeType == ExpressionType.MemberAccess && ((MemberExpression)argument).Member.MemberType == MemberTypes.Field && ((FieldInfo)((MemberExpression)argument).Member).IsStatic)) { args.Add(argument); } else { var local = context.DeclareLocal(argument.Type); args.Add(local); il.Stloc(local); il.Ldloc(local); } } arguments = args.ToArray(); MethodInfo getMethod = arrayType.GetMethod("Get"); if (getMethod == null) { throw new MissingMethodException(arrayType.ToString(), "Get"); } context.Il.Call(getMethod, arrayType); } break; } return(arguments); }
protected override bool EmitInternal(BinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { var il = context.Il; var left = node.Left; var right = node.Right; bool result = false; Type assigneeType; AssigneeKind assigneeKind; bool checkNullReferences = context.Options.HasFlag(CompilerOptions.CheckNullReferences); extend |= context.Options.HasFlag(CompilerOptions.ExtendOnAssign); GroboIL.Label assigneeIsNullLabel = null; bool assigneeIsNullLabelUsed = false; switch (left.NodeType) { case ExpressionType.Parameter: assigneeType = null; assigneeKind = AssigneeKind.Parameter; checkNullReferences = false; break; case ExpressionType.MemberAccess: var memberExpression = (MemberExpression)left; if (memberExpression.Expression == null) { assigneeType = null; assigneeKind = memberExpression.Member is FieldInfo ? AssigneeKind.StaticField : AssigneeKind.StaticProperty; checkNullReferences = false; } else { bool closureAssign = memberExpression.Expression == context.ParsedLambda.ClosureParameter || memberExpression.Expression.Type.IsStaticClosure(); checkNullReferences &= !closureAssign; if (node.NodeType != ExpressionType.Assign && context.CanReturn) { result |= ExpressionEmittersCollection.Emit(memberExpression.Expression, context, returnDefaultValueLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType); } else { assigneeIsNullLabel = !closureAssign && context.CanReturn ? il.DefineLabel("assigneeIsNull") : null; assigneeIsNullLabelUsed = ExpressionEmittersCollection.Emit(memberExpression.Expression, context, assigneeIsNullLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType); } assigneeKind = memberExpression.Member is FieldInfo ? AssigneeKind.InstanceField : AssigneeKind.InstanceProperty; } break; case ExpressionType.Index: var indexExpression = (IndexExpression)left; if (indexExpression.Object == null) { throw new InvalidOperationException("Indexing of null object is invalid"); } if ((indexExpression.Object.Type.IsArray && indexExpression.Object.Type.GetArrayRank() == 1) || indexExpression.Object.Type.IsList()) { if (node.NodeType != ExpressionType.Assign && context.CanReturn) { result |= ArrayIndexExpressionEmitter.Emit(indexExpression.Object, indexExpression.Arguments.Single(), context, returnDefaultValueLabel, ResultType.ByRefAll, extend, out assigneeType); checkNullReferences = false; } else { assigneeIsNullLabel = context.CanReturn ? il.DefineLabel("assigneeIsNull") : null; assigneeIsNullLabelUsed = ArrayIndexExpressionEmitter.Emit(indexExpression.Object, indexExpression.Arguments.Single(), context, assigneeIsNullLabel, ResultType.ByRefAll, extend, out assigneeType); } assigneeKind = AssigneeKind.SimpleArray; } else { if (node.NodeType != ExpressionType.Assign && context.CanReturn) { result |= ExpressionEmittersCollection.Emit(indexExpression.Object, context, returnDefaultValueLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType); } else { assigneeIsNullLabel = context.CanReturn ? il.DefineLabel("assigneeIsNull") : null; assigneeIsNullLabelUsed = ExpressionEmittersCollection.Emit(indexExpression.Object, context, assigneeIsNullLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType); } assigneeKind = indexExpression.Indexer != null ? AssigneeKind.IndexedProperty : AssigneeKind.MultiDimensionalArray; } break; default: throw new InvalidOperationException("Unable to assign to an expression of type '" + left.NodeType + "'"); } if (assigneeType != null && assigneeType.IsValueType) { using (var temp = context.DeclareLocal(assigneeType)) { il.Stloc(temp); il.Ldloca(temp); } assigneeType = assigneeType.MakeByRefType(); } if (node.NodeType == ExpressionType.Assign) { if (!checkNullReferences) { if (whatReturn == ResultType.Void) { EmitAssign(assigneeKind, left, context, null, right); } else { if (assigneeKind == AssigneeKind.Parameter) { EmitAssign(assigneeKind, left, context, null, right); EmitAccess(assigneeKind, left, context); } else { context.EmitLoadArguments(right); using (var value = context.DeclareLocal(right.Type)) { il.Stloc(value); EmitAssign(assigneeKind, left, context, null, value); il.Ldloc(value); } } } } else { if (whatReturn == ResultType.Void) { il.Dup(); var skipAssigneeLabel = il.DefineLabel("skipAssignee"); il.Brfalse(skipAssigneeLabel); EmitAssign(assigneeKind, left, context, null, right); var returnLabel = il.DefineLabel("return"); il.Br(returnLabel); context.MarkLabelAndSurroundWithSP(skipAssigneeLabel); il.Pop(); context.MarkLabelAndSurroundWithSP(returnLabel); } else { // load value var rightIsNullLabel = context.CanReturn ? il.DefineLabel("rightIsNull") : null; Type valueType; bool labelUsed = ExpressionEmittersCollection.Emit(right, context, rightIsNullLabel, out valueType); // stack: [address, value] if (right.Type == typeof(bool) && valueType == typeof(bool?)) { context.ConvertFromNullableBoolToBool(); } if (labelUsed && context.CanReturn) { context.EmitReturnDefaultValue(right.Type, rightIsNullLabel, il.DefineLabel("rightIsNotNull")); } using (var value = context.DeclareLocal(right.Type)) { il.Stloc(value); il.Dup(); var skipAssigneeLabel = il.DefineLabel("skipAssignee"); il.Brfalse(skipAssigneeLabel); EmitAssign(assigneeKind, left, context, null, value); var returnValueLabel = il.DefineLabel("returnValue"); il.Br(returnValueLabel); context.MarkLabelAndSurroundWithSP(skipAssigneeLabel); il.Pop(); context.MarkLabelAndSurroundWithSP(returnValueLabel); il.Ldloc(value); } } } } else { if (checkNullReferences) { il.Dup(); il.Brfalse(returnDefaultValueLabel); result = true; } if (assigneeType != null) { il.Dup(); } object[] arguments = EmitAccess(assigneeKind, left, context); context.EmitLoadArguments(right); context.EmitArithmeticOperation(GetOp(node.NodeType), left.Type, left.Type, right.Type, node.Method); if (whatReturn == ResultType.Void) { EmitAssign(assigneeKind, left, context, arguments); } else { if (assigneeKind == AssigneeKind.Parameter) { EmitAssign(assigneeKind, left, context, arguments); EmitAccess(assigneeKind, left, context); } else { using (var temp = context.DeclareLocal(left.Type)) { il.Stloc(temp); EmitAssign(assigneeKind, left, context, arguments, temp); il.Ldloc(temp); } } } } resultType = whatReturn == ResultType.Void ? typeof(void) : left.Type; if (assigneeIsNullLabelUsed) { context.EmitReturnDefaultValue(resultType, assigneeIsNullLabel, il.DefineLabel("assigneeIsNotNull")); } return(result); }
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); }