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); }