private static BlockExpression BlockCore(Type type, ReadOnlyCollection <ParameterExpression> variables, ReadOnlyCollection <Expression> expressions) { RequiresCanRead(expressions, nameof(expressions)); ValidateVariables(variables, nameof(variables)); if (type != null) { if (expressions.Count == 0) { if (type != typeof(void)) { throw Error.ArgumentTypesMustMatch(); } return(new ScopeWithType(variables, expressions, type)); } Expression last = expressions.Last(); if (type != typeof(void)) { if (!TypeUtils.AreReferenceAssignable(type, last.Type)) { throw Error.ArgumentTypesMustMatch(); } } if (!TypeUtils.AreEquivalent(type, last.Type)) { return(new ScopeWithType(variables, expressions, type)); } } switch (expressions.Count) { case 0: return(new ScopeWithType(variables, expressions, typeof(void))); case 1: return(new Scope1(variables, expressions[0])); default: return(new ScopeN(variables, expressions)); } }
// Value types must stay as the same type, otherwise it's now a // different operation, e.g. adding two doubles vs adding two ints. private static void ValidateChildType(Type before, Type after, string methodName) { if (before.GetTypeInfo().IsValueType) { if (TypeUtils.AreEquivalent(before, after)) { // types are the same value type return; } } else if (!after.GetTypeInfo().IsValueType) { // both are reference types return; } // Otherwise, it's an invalid type change. throw Error.MustRewriteChildToSameType(before, after, methodName); }
/// <summary> /// Creates a <see cref="CatchBlock"/> representing a catch statement with the specified elements. /// </summary> /// <param name="type">The <see cref="Type"/> of <see cref="Exception"/> this <see cref="CatchBlock"/> will handle.</param> /// <param name="variable">A <see cref="ParameterExpression"/> representing a reference to the <see cref="Exception"/> object caught by this handler.</param> /// <param name="body">The body of the catch statement.</param> /// <param name="filter">The body of the <see cref="Exception"/> filter.</param> /// <returns>The created <see cref="CatchBlock"/>.</returns> /// <remarks><paramref name="type"/> must be non-null and match the type of <paramref name="variable"/> (if it is supplied).</remarks> public static CatchBlock MakeCatchBlock(Type type, ParameterExpression variable, Expression body, Expression filter) { ContractUtils.RequiresNotNull(type, "type"); ContractUtils.Requires(variable == null || TypeUtils.AreEquivalent(variable.Type, type), "variable"); if (variable != null && variable.IsByRef) { throw Error.VariableMustNotBeByRef(variable, variable.Type); } RequiresCanRead(body, "body"); if (filter != null) { RequiresCanRead(filter, "filter"); if (filter.Type != typeof(bool)) { throw Error.ArgumentMustBeBoolean(); } } return(new CatchBlock(type, variable, body, filter)); }
private void AddressOf(IndexExpression node, Type type) { if (!TypeUtils.AreEquivalent(type, node.Type) || node.Indexer != null) { EmitExpressionAddress(node, type); return; } if (node.ArgumentCount == 1) { EmitExpression(node.Object); EmitExpression(node.GetArgument(0)); _ilg.Emit(OpCodes.Ldelema, node.Type); } else { var address = node.Object.Type.GetMethod("Address", BindingFlags.Public | BindingFlags.Instance); EmitMethodCall(node.Object, address, node); } }
/// <summary> /// If custom type is provided, all branches must be reference assignable to the result type. /// If no custom type is provided, all branches must have the same type - resultType. /// </summary> private static void ValidateSwitchCaseType(Expression @case, bool customType, Type resultType, string parameterName) { if (customType) { if (resultType != typeof(void)) { if (!TypeUtils.AreReferenceAssignable(resultType, @case.Type)) { throw new ArgumentException(Strings.ArgumentTypesMustMatch, parameterName); } } } else { if (!TypeUtils.AreEquivalent(resultType, @case.Type)) { throw new ArgumentException(Strings.AllCaseBodiesMustHaveSameType, parameterName); } } }
// Emits the address of the expression, returning the write back if necessary // // For properties, we want to write back into the property if it's // passed byref. private WriteBack?EmitAddressWriteBack(Expression node, Type type) { var labelScopeChangeInfo = GetLabelScopeChangeInfo(true, _labelBlock, node); if (labelScopeChangeInfo.HasValue) { _labelBlock = new LabelScopeInfo(labelScopeChangeInfo.Value.parent, labelScopeChangeInfo.Value.kind); DefineBlockLabels(labelScopeChangeInfo.Value.nodes); } WriteBack?result = null; if (TypeUtils.AreEquivalent(type, node.Type)) { switch (node.NodeType) { case ExpressionType.MemberAccess: result = AddressOfWriteBack((MemberExpression)node); break; case ExpressionType.Index: result = AddressOfWriteBack((IndexExpression)node); break; default: break; } } if (result == null) { EmitAddress(node, type, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart); } if (labelScopeChangeInfo.HasValue) { _labelBlock = labelScopeChangeInfo.Value.parent; } return(result); }
/// <summary> /// Creates a <see cref="ConditionalExpression" />. /// </summary> /// <param name="test">An <see cref="Expression" /> to set the <see cref="ConditionalExpression.Test" /> property equal to.</param> /// <param name="ifTrue"> /// An <see cref="Expression" /> to set the <see cref="ConditionalExpression.IfTrue" /> property equal /// to. /// </param> /// <param name="ifFalse"> /// An <see cref="Expression" /> to set the <see cref="ConditionalExpression.IfFalse" /> property /// equal to. /// </param> /// <returns> /// A <see cref="ConditionalExpression" /> that has the <see cref="NodeType" /> property equal to /// <see cref="ExpressionType.Conditional" /> and the <see cref="ConditionalExpression.Test" />, /// <see cref="ConditionalExpression.IfTrue" />, /// and <see cref="ConditionalExpression.IfFalse" /> properties set to the specified values. /// </returns> public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse) { ContractUtils.RequiresNotNull(test, nameof(test)); ContractUtils.RequiresNotNull(ifTrue, nameof(ifTrue)); ContractUtils.RequiresNotNull(ifFalse, nameof(ifFalse)); ExpressionUtils.RequiresCanRead(test, nameof(test)); ExpressionUtils.RequiresCanRead(ifTrue, nameof(ifTrue)); ExpressionUtils.RequiresCanRead(ifFalse, nameof(ifFalse)); if (test.Type != typeof(bool)) { throw new ArgumentException("Argument must be boolean", nameof(test)); } if (!TypeUtils.AreEquivalent(ifTrue.Type, ifFalse.Type)) { throw new ArgumentException("Argument types do not match"); } return(ConditionalExpression.Make(test, ifTrue, ifFalse, ifTrue.Type)); }
internal static MethodInfo FindConversionOperator(MethodInfo[] methods, Type typeFrom, Type typeTo, bool implicitOnly) { foreach (MethodInfo mi in methods) { if (mi.Name != "op_Implicit" && (implicitOnly || mi.Name != "op_Explicit")) { continue; } if (!TypeUtils.AreEquivalent(mi.ReturnType, typeTo)) { continue; } ParameterInfo[] pis = mi.GetParametersCached(); if (!TypeUtils.AreEquivalent(pis[0].ParameterType, typeFrom)) { continue; } return(mi); } return(null); }
private static void ValidateCustomBinaryAssign(CSharpExpressionType binaryType, Expression left, Expression right, ref MethodInfo method, LambdaExpression leftConversion, LambdaExpression finalConversion) { var leftType = left.Type; var rightType = right.Type; if (leftConversion != null) { leftType = ValidateConversion(binaryType, leftType, leftConversion); } // NB: Just leverage LINQ to do the dirty work to check everything. Note that this assumes that the // left conversion performed widening if that's required for the underlying operation. However, // the use of FunctionalOp below will widen the right operand to match the left operand's type, // which is what we do during Reduce as well (see remarks in FunctionalOp). This could produce // mysterious error messages. (TODO: Review what's most appropriate here.) // // NB: We can't have it check the final conversion, because it doesn't allow these without the use of // a custom method, so we check that ourselves further down. var leftDummy = Expression.Parameter(leftType, "__left"); var rightDummy = Expression.Parameter(rightType, "__right"); var functionalOp = FunctionalOp(binaryType, leftDummy, rightDummy, method); if (method == null) { method = functionalOp.Method; } var resultType = functionalOp.Type; if (finalConversion != null) { resultType = ValidateConversion(binaryType, resultType, finalConversion); } if (!TypeUtils.AreEquivalent(resultType, left.Type)) { throw Error.InvalidCompoundAssignmentWithOperands(binaryType, left.Type, right.Type); } }
/// <summary> /// Creates a <see cref="BlockExpression"/> that contains the given variables and expressions. /// </summary> /// <param name="type">The result type of the block.</param> /// <param name="variables">The variables in the block.</param> /// <param name="expressions">The expressions in the block.</param> /// <returns>The created <see cref="BlockExpression"/>.</returns> public static BlockExpression Block(Type type, IEnumerable <ParameterExpression> variables, IEnumerable <Expression> expressions) { ContractUtils.RequiresNotNull(type, "type"); ContractUtils.RequiresNotNull(expressions, "expressions"); var expressionList = expressions.ToReadOnly(); var variableList = variables.ToReadOnly(); ContractUtils.RequiresNotEmpty(expressionList, "expressions"); RequiresCanRead(expressionList, "expressions"); ValidateVariables(variableList, "variables"); Expression last = expressionList.Last(); if (type != typeof(void)) { if (!TypeUtils.AreReferenceAssignable(type, last.Type)) { throw Error.ArgumentTypesMustMatch(); } } if (!TypeUtils.AreEquivalent(type, last.Type)) { return(new ScopeWithType(variableList, expressionList, type)); } else { if (expressionList.Count == 1) { return(new Scope1(variableList, expressionList[0])); } else { return(new ScopeN(variableList, expressionList)); } } }
private void EmitExpressionAsType(Expression node, Type type, CompilationFlags flags) { if (type == typeof(void)) { EmitExpressionAsVoid(node, flags); } else { // if the node is emitted as a different type, CastClass IL is emitted at the end, // should not emit with tail calls. if (!TypeUtils.AreEquivalent(node.Type, type)) { EmitExpression(node); Debug.Assert(TypeUtils.AreReferenceAssignable(type, node.Type)); _ilg.Emit(OpCodes.Castclass, type); } else { // emit the node with the flags and emit expression start EmitExpression(node, UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart)); } } }
internal static MethodInfo GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType, bool implicitOnly) { // check for implicit coercions first Type nnExprType = TypeUtils.GetNonNullableType(convertFrom); Type nnConvType = TypeUtils.GetNonNullableType(convertToType); // try exact match on types MethodInfo[] eMethods = nnExprType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo method = FindConversionOperator(eMethods, convertFrom, convertToType, implicitOnly); if (method != null) { return(method); } MethodInfo[] cMethods = nnConvType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); method = FindConversionOperator(cMethods, convertFrom, convertToType, implicitOnly); if (method != null) { return(method); } // try lifted conversion if (!TypeUtils.AreEquivalent(nnExprType, convertFrom) || !TypeUtils.AreEquivalent(nnConvType, convertToType)) { method = FindConversionOperator(eMethods, nnExprType, nnConvType, implicitOnly); if (method == null) { method = FindConversionOperator(cMethods, nnExprType, nnConvType, implicitOnly); } if (method != null) { return(method); } } return(null); }
/// <summary> /// Creates a <see cref="CatchBlock"/> representing a catch statement with the specified elements. /// </summary> /// <param name="type">The <see cref="Type"/> of <see cref="Exception"/> this <see cref="CatchBlock"/> will handle.</param> /// <param name="variable">A <see cref="ParameterExpression"/> representing a reference to the <see cref="Exception"/> object caught by this handler.</param> /// <param name="body">The body of the catch statement.</param> /// <param name="filter">The body of the <see cref="Exception"/> filter.</param> /// <returns>The created <see cref="CatchBlock"/>.</returns> /// <remarks><paramref name="type"/> must be non-null and match the type of <paramref name="variable"/> (if it is supplied).</remarks> public static CatchBlock MakeCatchBlock(Type type, ParameterExpression?variable, Expression body, Expression?filter) { ContractUtils.RequiresNotNull(type, nameof(type)); ContractUtils.Requires(variable == null || TypeUtils.AreEquivalent(variable.Type, type), nameof(variable)); if (variable == null) { TypeUtils.ValidateType(type, nameof(type)); } else if (variable.IsByRef) { throw Error.VariableMustNotBeByRef(variable, variable.Type, nameof(variable)); } ExpressionUtils.RequiresCanRead(body, nameof(body)); if (filter != null) { ExpressionUtils.RequiresCanRead(filter, nameof(filter)); if (filter.Type != typeof(bool)) { throw Error.ArgumentMustBeBoolean(nameof(filter)); } } return(new CatchBlock(type, variable, body, filter)); }
private static void ValidateNewArgs(ConstructorInfo constructor, ref ReadOnlyCollection <Expression> arguments, ref ReadOnlyCollection <MemberInfo> members) { ParameterInfo[] pis; if ((pis = constructor.GetParametersCached()).Length > 0) { if (arguments.Count != pis.Length) { throw Error.IncorrectNumberOfConstructorArguments(); } if (arguments.Count != members.Count) { throw Error.IncorrectNumberOfArgumentsForMembers(); } Expression[]? newArguments = null; MemberInfo[]? newMembers = null; for (int i = 0, n = arguments.Count; i < n; i++) { Expression arg = arguments[i]; ExpressionUtils.RequiresCanRead(arg, nameof(arguments), i); MemberInfo member = members[i]; ContractUtils.RequiresNotNull(member, nameof(members), i); if (!TypeUtils.AreEquivalent(member.DeclaringType, constructor.DeclaringType)) { throw Error.ArgumentMemberNotDeclOnType(member.Name, constructor.DeclaringType !.Name, nameof(members), i); } Type memberType; ValidateAnonymousTypeMember(ref member, out memberType, nameof(members), i); if (!TypeUtils.AreReferenceAssignable(memberType, arg.Type)) { if (!TryQuote(memberType, ref arg)) { throw Error.ArgumentTypeDoesNotMatchMember(arg.Type, memberType, nameof(arguments), i); } } ParameterInfo pi = pis[i]; Type pType = pi.ParameterType; if (pType.IsByRef) { pType = pType.GetElementType() !; } if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) { if (!TryQuote(pType, ref arg)) { throw Error.ExpressionTypeDoesNotMatchConstructorParameter(arg.Type, pType, nameof(arguments), i); } } if (newArguments == null && arg != arguments[i]) { newArguments = new Expression[arguments.Count]; for (int j = 0; j < i; j++) { newArguments[j] = arguments[j]; } } if (newArguments != null) { newArguments[i] = arg; } if (newMembers == null && member != members[i]) { newMembers = new MemberInfo[members.Count]; for (int j = 0; j < i; j++) { newMembers[j] = members[j]; } } if (newMembers != null) { newMembers[i] = member; } } if (newArguments != null) { arguments = new TrueReadOnlyCollection <Expression>(newArguments); } if (newMembers != null) { members = new TrueReadOnlyCollection <MemberInfo>(newMembers); } } else if (arguments != null && arguments.Count > 0) { throw Error.IncorrectNumberOfConstructorArguments(); } else if (members != null && members.Count > 0) { throw Error.IncorrectNumberOfMembersForGivenConstructor(); } }
public override bool Equals(object obj) { return(obj is TypeRestriction other && other._expression == _expression && TypeUtils.AreEquivalent(other._type, _type)); }
/// <summary> /// Returns our Expression converted to DynamicObject /// </summary> private Expression GetLimitedSelf() { // Convert to DynamicObject rather than LimitType, because // the limit type might be non-public. return(TypeUtils.AreEquivalent(Expression.Type, typeof(DynamicObject)) ? Expression : Expression.Convert(Expression, typeof(DynamicObject))); }
// Tries to emit switch as a jmp table private bool TryEmitSwitchInstruction(SwitchExpression node, CompilationFlags flags) { // If we have a comparison, bail if (node.Comparison != null) { return(false); } // Make sure the switch value type and the right side type // are types we can optimize Type type = node.SwitchValue.Type; if (!CanOptimizeSwitchType(type) || !TypeUtils.AreEquivalent(type, node.Cases[0].TestValues[0].Type)) { return(false); } // Make sure all test values are constant, or we can't emit the // jump table. if (!node.Cases.All(c => c.TestValues.All(t => t is ConstantExpression))) { return(false); } // // We can emit the optimized switch, let's do it. // // Build target labels, collect keys. var labels = new Label[node.Cases.Count]; var isGoto = new bool[node.Cases.Count]; var uniqueKeys = new HashSet <decimal>(); var keys = new List <SwitchLabel>(); for (int i = 0; i < node.Cases.Count; i++) { DefineSwitchCaseLabel(node.Cases[i], out labels[i], out isGoto[i]); foreach (ConstantExpression test in node.Cases[i].TestValues) { // Guaranteed to work thanks to CanOptimizeSwitchType. // // Use decimal because it can hold Int64 or UInt64 without // precision loss or signed/unsigned conversions. decimal key = ConvertSwitchValue(test.Value); // Only add each key once. If it appears twice, it's // allowed, but can't be reached. if (uniqueKeys.Add(key)) { keys.Add(new SwitchLabel(key, test.Value, labels[i])); } } } // Sort the keys, and group them into buckets. keys.Sort((x, y) => Math.Sign(x.Key - y.Key)); var buckets = new List <List <SwitchLabel> >(); foreach (var key in keys) { AddToBuckets(buckets, key); } // Emit the switchValue LocalBuilder value = GetLocal(node.SwitchValue.Type); EmitExpression(node.SwitchValue); _ilg.Emit(OpCodes.Stloc, value); // Create end label, and default label if needed Label end = _ilg.DefineLabel(); Label @default = (node.DefaultBody == null) ? end : _ilg.DefineLabel(); // Emit the switch var info = new SwitchInfo(node, value, @default); EmitSwitchBuckets(info, buckets, 0, buckets.Count - 1); // Emit the case bodies and default EmitSwitchCases(node, labels, isGoto, @default, end, flags); FreeLocal(value); return(true); }
private static void ValidateNewArgs(ConstructorInfo constructor, ref Expression[] arguments, ref ReadOnlyCollectionEx <MemberInfo> members) { ParameterInfo[] pis = constructor.GetParameters(); if (pis.Length > 0) { if (arguments.Length != pis.Length) { throw new ArgumentException("Incorrect number of arguments for constructor"); } if (arguments.Length != members.Count) { throw new ArgumentException("Incorrect number of arguments for the given members "); } Expression[]? newArguments = null; MemberInfo[]? newMembers = null; for (int i = 0, n = arguments.Length; i < n; i++) { var arg = arguments[i]; ContractUtils.RequiresNotNull(arg, nameof(arguments), i); ExpressionUtils.RequiresCanRead(arg, nameof(arguments), i); var member = members[i]; ContractUtils.RequiresNotNull(member, nameof(members), i); if (!TypeUtils.AreEquivalent(member.DeclaringType, constructor.DeclaringType)) { throw new ArgumentException($" The member '{member.Name}' is not declared on type '{constructor.DeclaringType?.Name}' being created", i >= 0 ? $"{nameof(members)}[{i}]" : nameof(members)); } ValidateAnonymousTypeMember(ref member, out var memberType, nameof(members), i); if (!memberType.IsReferenceAssignableFromInternal(arg.Type) && !TryQuote(memberType, ref arg)) { throw new ArgumentException($" Argument type '{arg.Type}' does not match the corresponding member type '{memberType}'", i >= 0 ? $"{nameof(arguments)}[{i}]" : nameof(arguments)); } var pi = pis[i]; var pType = pi.ParameterType; if (pType.IsByRef) { pType = pType.GetElementType(); } if (!pType.IsReferenceAssignableFromInternal(arg.Type) && !TryQuote(pType, ref arg)) { throw new ArgumentException($"Expression of type '{arg.Type}' cannot be used for constructor parameter of type '{pType}'", i >= 0 ? $"{nameof(arguments)}[{i}]" : nameof(arguments)); } if (newArguments == null && arg != arguments[i]) { newArguments = new Expression[arguments.Length]; for (var j = 0; j < i; j++) { newArguments[j] = arguments[j]; } } if (newArguments != null) { newArguments[i] = arg; } if (newMembers == null && member != members[i]) { newMembers = new MemberInfo[members.Count]; for (var j = 0; j < i; j++) { newMembers[j] = members[j]; } } if (newMembers != null) { newMembers[i] = member; } } if (newArguments != null) { arguments = newArguments; } if (newMembers != null) { members = ReadOnlyCollectionEx.Create(newMembers); } } else if (arguments?.Length > 0) { throw new ArgumentException("Incorrect number of arguments for constructor"); } else if (members?.Count > 0) { throw new ArgumentException("Incorrect number of members for constructor"); } }
/// <summary> /// Creates a <see cref="SwitchExpression"/>. /// </summary> /// <param name="type">The result type of the switch.</param> /// <param name="switchValue">The value to be tested against each case.</param> /// <param name="defaultBody">The result of the switch if no cases are matched.</param> /// <param name="comparison">The equality comparison method to use.</param> /// <param name="cases">The valid cases for this switch.</param> /// <returns>The created <see cref="SwitchExpression"/>.</returns> public static SwitchExpression Switch(Type type, Expression switchValue, Expression defaultBody, MethodInfo comparison, IEnumerable <SwitchCase> cases) { RequiresCanRead(switchValue, "switchValue"); if (switchValue.Type == typeof(void)) { throw Error.ArgumentCannotBeOfTypeVoid(); } var caseList = cases.ToReadOnly(); ContractUtils.RequiresNotEmpty(caseList, "cases"); ContractUtils.RequiresNotNullItems(caseList, "cases"); // Type of the result. Either provided, or it is type of the branches. Type resultType = type ?? caseList[0].Body.Type; bool customType = type != null; if (comparison != null) { var pms = comparison.GetParametersCached(); if (pms.Length != 2) { throw Error.IncorrectNumberOfMethodCallArguments(comparison); } // Validate that the switch value's type matches the comparison method's // left hand side parameter type. var leftParam = pms[0]; bool liftedCall = false; if (!ParameterIsAssignable(leftParam, switchValue.Type)) { liftedCall = ParameterIsAssignable(leftParam, switchValue.Type.GetNonNullableType()); if (!liftedCall) { throw Error.SwitchValueTypeDoesNotMatchComparisonMethodParameter(switchValue.Type, leftParam.ParameterType); } } var rightParam = pms[1]; foreach (var c in caseList) { ContractUtils.RequiresNotNull(c, "cases"); ValidateSwitchCaseType(c.Body, customType, resultType, "cases"); for (int i = 0; i < c.TestValues.Count; i++) { // When a comparison method is provided, test values can have different type but have to // be reference assignable to the right hand side parameter of the method. Type rightOperandType = c.TestValues[i].Type; if (liftedCall) { if (!rightOperandType.IsNullableType()) { throw Error.TestValueTypeDoesNotMatchComparisonMethodParameter(rightOperandType, rightParam.ParameterType); } rightOperandType = rightOperandType.GetNonNullableType(); } if (!ParameterIsAssignable(rightParam, rightOperandType)) { throw Error.TestValueTypeDoesNotMatchComparisonMethodParameter(rightOperandType, rightParam.ParameterType); } } } } else { // When comparison method is not present, all the test values must have // the same type. Use the first test value's type as the baseline. var firstTestValue = caseList[0].TestValues[0]; foreach (var c in caseList) { ContractUtils.RequiresNotNull(c, "cases"); ValidateSwitchCaseType(c.Body, customType, resultType, "cases"); // When no comparison method is provided, require all test values to have the same type. for (int i = 0; i < c.TestValues.Count; i++) { if (!TypeUtils.AreEquivalent(firstTestValue.Type, c.TestValues[i].Type)) { throw new ArgumentException(Strings.AllTestValuesMustHaveSameType, "cases"); } } } // Now we need to validate that switchValue.Type and testValueType // make sense in an Equal node. Fortunately, Equal throws a // reasonable error, so just call it. var equal = Equal(switchValue, firstTestValue, false, comparison); // Get the comparison function from equals node. comparison = equal.Method; } if (defaultBody == null) { if (resultType != typeof(void)) { throw Error.DefaultBodyMustBeSupplied(); } } else { ValidateSwitchCaseType(defaultBody, customType, resultType, "defaultBody"); } // if we have a non-boolean userdefined equals, we don't want it. if (comparison != null && comparison.ReturnType != typeof(bool)) { throw Error.EqualityMustReturnBoolean(comparison); } return(new SwitchExpression(resultType, switchValue, defaultBody, comparison, caseList)); }
private void EmitUnaryOperator(ExpressionType op, Type operandType, Type resultType) { bool operandIsNullable = TypeUtils.IsNullableType(operandType); if (op == ExpressionType.ArrayLength) { _ilg.Emit(OpCodes.Ldlen); return; } if (operandIsNullable) { switch (op) { case ExpressionType.Not: { if (operandType != typeof(bool?)) { goto case ExpressionType.Negate; } Label labEnd = _ilg.DefineLabel(); LocalBuilder loc = GetLocal(operandType); // store values (reverse order since they are already on the stack) _ilg.Emit(OpCodes.Stloc, loc); // test for null _ilg.Emit(OpCodes.Ldloca, loc); _ilg.EmitHasValue(operandType); _ilg.Emit(OpCodes.Brfalse_S, labEnd); // do op on non-null value _ilg.Emit(OpCodes.Ldloca, loc); _ilg.EmitGetValueOrDefault(operandType); Type nnOperandType = TypeUtils.GetNonNullableType(operandType); EmitUnaryOperator(op, nnOperandType, typeof(bool)); // construct result ConstructorInfo ci = resultType.GetConstructor(ArrayOfType_Bool); _ilg.Emit(OpCodes.Newobj, ci); _ilg.Emit(OpCodes.Stloc, loc); _ilg.MarkLabel(labEnd); _ilg.Emit(OpCodes.Ldloc, loc); FreeLocal(loc); return; } case ExpressionType.UnaryPlus: case ExpressionType.NegateChecked: case ExpressionType.Negate: case ExpressionType.Increment: case ExpressionType.Decrement: case ExpressionType.OnesComplement: case ExpressionType.IsFalse: case ExpressionType.IsTrue: { Debug.Assert(TypeUtils.AreEquivalent(operandType, resultType)); Label labIfNull = _ilg.DefineLabel(); Label labEnd = _ilg.DefineLabel(); LocalBuilder loc = GetLocal(operandType); // check for null _ilg.Emit(OpCodes.Stloc, loc); _ilg.Emit(OpCodes.Ldloca, loc); _ilg.EmitHasValue(operandType); _ilg.Emit(OpCodes.Brfalse_S, labIfNull); // apply operator to non-null value _ilg.Emit(OpCodes.Ldloca, loc); _ilg.EmitGetValueOrDefault(operandType); Type nnOperandType = TypeUtils.GetNonNullableType(resultType); EmitUnaryOperator(op, nnOperandType, nnOperandType); // construct result ConstructorInfo ci = resultType.GetConstructor(new Type[] { nnOperandType }); _ilg.Emit(OpCodes.Newobj, ci); _ilg.Emit(OpCodes.Stloc, loc); _ilg.Emit(OpCodes.Br_S, labEnd); // if null then create a default one _ilg.MarkLabel(labIfNull); _ilg.Emit(OpCodes.Ldloca, loc); _ilg.Emit(OpCodes.Initobj, resultType); _ilg.MarkLabel(labEnd); _ilg.Emit(OpCodes.Ldloc, loc); FreeLocal(loc); return; } case ExpressionType.TypeAs: _ilg.Emit(OpCodes.Box, operandType); _ilg.Emit(OpCodes.Isinst, resultType); if (TypeUtils.IsNullableType(resultType)) { _ilg.Emit(OpCodes.Unbox_Any, resultType); } return; default: throw Error.UnhandledUnary(op); } } else { switch (op) { case ExpressionType.Not: if (operandType == typeof(bool)) { _ilg.Emit(OpCodes.Ldc_I4_0); _ilg.Emit(OpCodes.Ceq); } else { _ilg.Emit(OpCodes.Not); } break; case ExpressionType.OnesComplement: _ilg.Emit(OpCodes.Not); break; case ExpressionType.IsFalse: _ilg.Emit(OpCodes.Ldc_I4_0); _ilg.Emit(OpCodes.Ceq); // Not an arithmetic operation -> no conversion return; case ExpressionType.IsTrue: _ilg.Emit(OpCodes.Ldc_I4_1); _ilg.Emit(OpCodes.Ceq); // Not an arithmetic operation -> no conversion return; case ExpressionType.UnaryPlus: _ilg.Emit(OpCodes.Nop); break; case ExpressionType.Negate: case ExpressionType.NegateChecked: _ilg.Emit(OpCodes.Neg); break; case ExpressionType.TypeAs: if (operandType.GetTypeInfo().IsValueType) { _ilg.Emit(OpCodes.Box, operandType); } _ilg.Emit(OpCodes.Isinst, resultType); if (TypeUtils.IsNullableType(resultType)) { _ilg.Emit(OpCodes.Unbox_Any, resultType); } // Not an arithmetic operation -> no conversion return; case ExpressionType.Increment: EmitConstantOne(resultType); _ilg.Emit(OpCodes.Add); break; case ExpressionType.Decrement: EmitConstantOne(resultType); _ilg.Emit(OpCodes.Sub); break; default: throw Error.UnhandledUnary(op); } EmitConvertArithmeticResult(op, resultType); } }
private void EmitLift(ExpressionType nodeType, Type resultType, MethodCallExpression mc, ParameterExpression[] paramList, Expression[] argList) { Debug.Assert(TypeUtils.AreEquivalent(resultType.GetNonNullableType(), mc.Type.GetNonNullableType())); switch (nodeType) { default: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: { Label exit = _ilg.DefineLabel(); Label exitNull = _ilg.DefineLabel(); LocalBuilder anyNull = GetLocal(typeof(bool)); for (int i = 0, n = paramList.Length; i < n; i++) { ParameterExpression v = paramList[i]; Expression arg = argList[i]; if (arg.Type.IsNullableType()) { _scope.AddLocal(this, v); EmitAddress(arg, arg.Type); _ilg.Emit(OpCodes.Dup); _ilg.EmitHasValue(arg.Type); _ilg.Emit(OpCodes.Ldc_I4_0); _ilg.Emit(OpCodes.Ceq); _ilg.Emit(OpCodes.Stloc, anyNull); _ilg.EmitGetValueOrDefault(arg.Type); _scope.EmitSet(v); } else { _scope.AddLocal(this, v); EmitExpression(arg); if (!arg.Type.IsValueType) { _ilg.Emit(OpCodes.Dup); _ilg.Emit(OpCodes.Ldnull); _ilg.Emit(OpCodes.Ceq); _ilg.Emit(OpCodes.Stloc, anyNull); } _scope.EmitSet(v); } _ilg.Emit(OpCodes.Ldloc, anyNull); _ilg.Emit(OpCodes.Brtrue, exitNull); } EmitMethodCallExpression(mc); if (resultType.IsNullableType() && !TypeUtils.AreEquivalent(resultType, mc.Type)) { ConstructorInfo ci = resultType.GetConstructor(new Type[] { mc.Type }); _ilg.Emit(OpCodes.Newobj, ci); } _ilg.Emit(OpCodes.Br_S, exit); _ilg.MarkLabel(exitNull); if (TypeUtils.AreEquivalent(resultType, mc.Type.GetNullableType())) { if (resultType.IsValueType) { LocalBuilder result = GetLocal(resultType); _ilg.Emit(OpCodes.Ldloca, result); _ilg.Emit(OpCodes.Initobj, resultType); _ilg.Emit(OpCodes.Ldloc, result); FreeLocal(result); } else { _ilg.Emit(OpCodes.Ldnull); } } else { Debug.Assert(nodeType == ExpressionType.LessThan || nodeType == ExpressionType.LessThanOrEqual || nodeType == ExpressionType.GreaterThan || nodeType == ExpressionType.GreaterThanOrEqual); _ilg.Emit(OpCodes.Ldc_I4_0); } _ilg.MarkLabel(exit); FreeLocal(anyNull); return; } case ExpressionType.Equal: case ExpressionType.NotEqual: { if (TypeUtils.AreEquivalent(resultType, mc.Type.GetNullableType())) { goto default; } Label exit = _ilg.DefineLabel(); Label exitAllNull = _ilg.DefineLabel(); Label exitAnyNull = _ilg.DefineLabel(); LocalBuilder anyNull = GetLocal(typeof(bool)); LocalBuilder allNull = GetLocal(typeof(bool)); _ilg.Emit(OpCodes.Ldc_I4_0); _ilg.Emit(OpCodes.Stloc, anyNull); _ilg.Emit(OpCodes.Ldc_I4_1); _ilg.Emit(OpCodes.Stloc, allNull); for (int i = 0, n = paramList.Length; i < n; i++) { ParameterExpression v = paramList[i]; Expression arg = argList[i]; _scope.AddLocal(this, v); if (arg.Type.IsNullableType()) { EmitAddress(arg, arg.Type); _ilg.Emit(OpCodes.Dup); _ilg.EmitHasValue(arg.Type); _ilg.Emit(OpCodes.Ldc_I4_0); _ilg.Emit(OpCodes.Ceq); _ilg.Emit(OpCodes.Dup); _ilg.Emit(OpCodes.Ldloc, anyNull); _ilg.Emit(OpCodes.Or); _ilg.Emit(OpCodes.Stloc, anyNull); _ilg.Emit(OpCodes.Ldloc, allNull); _ilg.Emit(OpCodes.And); _ilg.Emit(OpCodes.Stloc, allNull); _ilg.EmitGetValueOrDefault(arg.Type); } else { EmitExpression(arg); if (!arg.Type.IsValueType) { _ilg.Emit(OpCodes.Dup); _ilg.Emit(OpCodes.Ldnull); _ilg.Emit(OpCodes.Ceq); _ilg.Emit(OpCodes.Dup); _ilg.Emit(OpCodes.Ldloc, anyNull); _ilg.Emit(OpCodes.Or); _ilg.Emit(OpCodes.Stloc, anyNull); _ilg.Emit(OpCodes.Ldloc, allNull); _ilg.Emit(OpCodes.And); _ilg.Emit(OpCodes.Stloc, allNull); } else { _ilg.Emit(OpCodes.Ldc_I4_0); _ilg.Emit(OpCodes.Stloc, allNull); } } _scope.EmitSet(v); } _ilg.Emit(OpCodes.Ldloc, allNull); _ilg.Emit(OpCodes.Brtrue, exitAllNull); _ilg.Emit(OpCodes.Ldloc, anyNull); _ilg.Emit(OpCodes.Brtrue, exitAnyNull); EmitMethodCallExpression(mc); if (resultType.IsNullableType() && !TypeUtils.AreEquivalent(resultType, mc.Type)) { ConstructorInfo ci = resultType.GetConstructor(new Type[] { mc.Type }); _ilg.Emit(OpCodes.Newobj, ci); } _ilg.Emit(OpCodes.Br_S, exit); _ilg.MarkLabel(exitAllNull); _ilg.EmitPrimitive(nodeType == ExpressionType.Equal); _ilg.Emit(OpCodes.Br_S, exit); _ilg.MarkLabel(exitAnyNull); _ilg.EmitPrimitive(nodeType == ExpressionType.NotEqual); _ilg.MarkLabel(exit); FreeLocal(anyNull); FreeLocal(allNull); return; } } }
public override bool Equals(object obj) { BindingRestrictions.TypeRestriction restriction = obj as BindingRestrictions.TypeRestriction; return (((restriction != null) && TypeUtils.AreEquivalent(restriction._type, this._type)) && (restriction._expression == this._expression)); }
private void EmitConvert(UnaryExpression node, CompilationFlags flags) { if (node.Method != null) { // User-defined conversions are only lifted if both source and // destination types are value types. The C# compiler gets this wrong. // In C#, if you have an implicit conversion from int->MyClass and you // "lift" the conversion to int?->MyClass then a null int? goes to a // null MyClass. This is contrary to the specification, which states // that the correct behaviour is to unwrap the int?, throw an exception // if it is null, and then call the conversion. // // We cannot fix this in C# but there is no reason why we need to // propagate this behavior into the expression tree API. Unfortunately // this means that when the C# compiler generates the lambda // (int? i)=>(MyClass)i, we will get different results for converting // that lambda to a delegate directly and converting that lambda to // an expression tree and then compiling it. We can live with this // discrepancy however. if (node.IsLifted && (!node.Type.GetTypeInfo().IsValueType || !node.Operand.Type.GetTypeInfo().IsValueType)) { ParameterInfo[] pis = node.Method.GetParametersCached(); Debug.Assert(pis != null && pis.Length == 1); Type paramType = pis[0].ParameterType; if (paramType.IsByRef) { paramType = paramType.GetElementType(); } UnaryExpression e = Expression.Convert( Expression.Call( node.Method, Expression.Convert(node.Operand, pis[0].ParameterType) ), node.Type ); EmitConvert(e, flags); } else { EmitUnaryMethod(node, flags); } } else if (node.Type == typeof(void)) { EmitExpressionAsVoid(node.Operand, flags); } else { if (TypeUtils.AreEquivalent(node.Operand.Type, node.Type)) { EmitExpression(node.Operand, flags); } else { // A conversion is emitted after emitting the operand, no tail call is emitted EmitExpression(node.Operand); _ilg.EmitConvertToType(node.Operand.Type, node.Type, node.NodeType == ExpressionType.ConvertChecked); } } }
/// <summary> /// Returns our Expression converted to our known LimitType /// </summary> private Expression GetLimitedSelf() { return(TypeUtils.AreEquivalent(Expression.Type, LimitType) ? Expression : Expression.Convert(Expression, LimitType)); }
private void EmitLift(ExpressionType nodeType, Type resultType, MethodCallExpression mc, ParameterExpression[] paramList, Expression[] argList) { Debug.Assert(TypeUtils.AreEquivalent(resultType.GetNonNullable(), mc.Type.GetNonNullable())); switch (nodeType) { case ExpressionType.Equal: case ExpressionType.NotEqual: if (TypeUtils.AreEquivalent(resultType, mc.Type.GetNullable())) { goto default; } EmitLiftEqualityComparison(nodeType, resultType, mc, paramList, argList); return; default: var exit = IL.DefineLabel(); var exitNull = IL.DefineLabel(); var anyNull = GetLocal(typeof(bool)); for (int i = 0, n = paramList.Length; i < n; i++) { var v = paramList[i]; var arg = argList[i]; if (arg.Type.IsNullable()) { _scope.AddLocal(this, v); EmitAddress(arg, arg.Type); IL.Emit(OpCodes.Dup); IL.EmitHasValue(arg.Type); IL.Emit(OpCodes.Ldc_I4_0); IL.Emit(OpCodes.Ceq); IL.Emit(OpCodes.Stloc, anyNull); IL.EmitGetValueOrDefault(arg.Type); _scope.EmitSet(v); } else { _scope.AddLocal(this, v); EmitExpression(arg); if (!arg.Type.IsValueType) { IL.Emit(OpCodes.Dup); IL.Emit(OpCodes.Ldnull); IL.Emit(OpCodes.Ceq); IL.Emit(OpCodes.Stloc, anyNull); } _scope.EmitSet(v); } IL.Emit(OpCodes.Ldloc, anyNull); IL.Emit(OpCodes.Brtrue, exitNull); } EmitMethodCallExpression(mc); if (resultType.IsNullable() && !TypeUtils.AreEquivalent(resultType, mc.Type)) { var ci = resultType.GetConstructor(new[] { mc.Type }); IL.Emit(OpCodes.Newobj, ci); } IL.Emit(OpCodes.Br_S, exit); IL.MarkLabel(exitNull); if (TypeUtils.AreEquivalent(resultType, mc.Type.GetNullable())) { if (resultType.IsValueType) { var result = GetLocal(resultType); IL.Emit(OpCodes.Ldloca, result); IL.Emit(OpCodes.Initobj, resultType); IL.Emit(OpCodes.Ldloc, result); FreeLocal(result); } else { IL.Emit(OpCodes.Ldnull); } } else { Debug.Assert ( nodeType == ExpressionType.LessThan || nodeType == ExpressionType.LessThanOrEqual || nodeType == ExpressionType.GreaterThan || nodeType == ExpressionType.GreaterThanOrEqual ); IL.Emit(OpCodes.Ldc_I4_0); } IL.MarkLabel(exit); FreeLocal(anyNull); return; } }
private void EmitLiftEqualityComparison(ExpressionType nodeType, Type resultType, MethodCallExpression mc, ParameterExpression[] paramList, Expression[] argList) { var exit = IL.DefineLabel(); var exitAllNull = IL.DefineLabel(); var exitAnyNull = IL.DefineLabel(); var anyNull = GetLocal(typeof(bool)); var allNull = GetLocal(typeof(bool)); IL.Emit(OpCodes.Ldc_I4_0); IL.Emit(OpCodes.Stloc, anyNull); IL.Emit(OpCodes.Ldc_I4_1); IL.Emit(OpCodes.Stloc, allNull); for (int i = 0, n = paramList.Length; i < n; i++) { var v = paramList[i]; var arg = argList[i]; _scope.AddLocal(this, v); if (arg.Type.IsNullable()) { EmitAddress(arg, arg.Type); IL.Emit(OpCodes.Dup); IL.EmitHasValue(arg.Type); IL.Emit(OpCodes.Ldc_I4_0); IL.Emit(OpCodes.Ceq); IL.Emit(OpCodes.Dup); IL.Emit(OpCodes.Ldloc, anyNull); IL.Emit(OpCodes.Or); IL.Emit(OpCodes.Stloc, anyNull); IL.Emit(OpCodes.Ldloc, allNull); IL.Emit(OpCodes.And); IL.Emit(OpCodes.Stloc, allNull); IL.EmitGetValueOrDefault(arg.Type); } else { EmitExpression(arg); if (!arg.Type.IsValueType) { IL.Emit(OpCodes.Dup); IL.Emit(OpCodes.Ldnull); IL.Emit(OpCodes.Ceq); IL.Emit(OpCodes.Dup); IL.Emit(OpCodes.Ldloc, anyNull); IL.Emit(OpCodes.Or); IL.Emit(OpCodes.Stloc, anyNull); IL.Emit(OpCodes.Ldloc, allNull); IL.Emit(OpCodes.And); IL.Emit(OpCodes.Stloc, allNull); } else { IL.Emit(OpCodes.Ldc_I4_0); IL.Emit(OpCodes.Stloc, allNull); } } _scope.EmitSet(v); } IL.Emit(OpCodes.Ldloc, allNull); IL.Emit(OpCodes.Brtrue, exitAllNull); IL.Emit(OpCodes.Ldloc, anyNull); IL.Emit(OpCodes.Brtrue, exitAnyNull); EmitMethodCallExpression(mc); if (resultType.IsNullable() && !TypeUtils.AreEquivalent(resultType, mc.Type)) { var ci = resultType.GetConstructor(new[] { mc.Type }); IL.Emit(OpCodes.Newobj, ci); } IL.Emit(OpCodes.Br_S, exit); IL.MarkLabel(exitAllNull); IL.EmitPrimitive(nodeType == ExpressionType.Equal); IL.Emit(OpCodes.Br_S, exit); IL.MarkLabel(exitAnyNull); IL.EmitPrimitive(nodeType == ExpressionType.NotEqual); IL.MarkLabel(exit); FreeLocal(anyNull); FreeLocal(allNull); }
private void EmitLiftedRelational(ExpressionType op, Type leftType, Type rightType, Type resultType, bool liftedToNull) { Debug.Assert(leftType.IsNullableType()); Label shortCircuit = _ilg.DefineLabel(); LocalBuilder locLeft = GetLocal(leftType); LocalBuilder locRight = GetLocal(rightType); // store values (reverse order since they are already on the stack) _ilg.Emit(OpCodes.Stloc, locRight); _ilg.Emit(OpCodes.Stloc, locLeft); if (op == ExpressionType.Equal) { // test for both null -> true _ilg.Emit(OpCodes.Ldloca, locLeft); _ilg.EmitHasValue(leftType); _ilg.Emit(OpCodes.Ldc_I4_0); _ilg.Emit(OpCodes.Ceq); _ilg.Emit(OpCodes.Ldloca, locRight); _ilg.EmitHasValue(rightType); _ilg.Emit(OpCodes.Ldc_I4_0); _ilg.Emit(OpCodes.Ceq); _ilg.Emit(OpCodes.And); _ilg.Emit(OpCodes.Dup); _ilg.Emit(OpCodes.Brtrue_S, shortCircuit); _ilg.Emit(OpCodes.Pop); // test for either is null -> false _ilg.Emit(OpCodes.Ldloca, locLeft); _ilg.EmitHasValue(leftType); _ilg.Emit(OpCodes.Ldloca, locRight); _ilg.EmitHasValue(rightType); _ilg.Emit(OpCodes.And); _ilg.Emit(OpCodes.Dup); _ilg.Emit(OpCodes.Brfalse_S, shortCircuit); _ilg.Emit(OpCodes.Pop); } else if (op == ExpressionType.NotEqual) { // test for both null -> false _ilg.Emit(OpCodes.Ldloca, locLeft); _ilg.EmitHasValue(leftType); _ilg.Emit(OpCodes.Ldloca, locRight); _ilg.EmitHasValue(rightType); _ilg.Emit(OpCodes.Or); _ilg.Emit(OpCodes.Dup); _ilg.Emit(OpCodes.Brfalse_S, shortCircuit); _ilg.Emit(OpCodes.Pop); // test for either is null -> true _ilg.Emit(OpCodes.Ldloca, locLeft); _ilg.EmitHasValue(leftType); _ilg.Emit(OpCodes.Ldc_I4_0); _ilg.Emit(OpCodes.Ceq); _ilg.Emit(OpCodes.Ldloca, locRight); _ilg.EmitHasValue(rightType); _ilg.Emit(OpCodes.Ldc_I4_0); _ilg.Emit(OpCodes.Ceq); _ilg.Emit(OpCodes.Or); _ilg.Emit(OpCodes.Dup); _ilg.Emit(OpCodes.Brtrue_S, shortCircuit); _ilg.Emit(OpCodes.Pop); } else { // test for either is null -> false _ilg.Emit(OpCodes.Ldloca, locLeft); _ilg.EmitHasValue(leftType); _ilg.Emit(OpCodes.Ldloca, locRight); _ilg.EmitHasValue(rightType); _ilg.Emit(OpCodes.And); _ilg.Emit(OpCodes.Dup); _ilg.Emit(OpCodes.Brfalse_S, shortCircuit); _ilg.Emit(OpCodes.Pop); } // do op on values _ilg.Emit(OpCodes.Ldloca, locLeft); _ilg.EmitGetValueOrDefault(leftType); _ilg.Emit(OpCodes.Ldloca, locRight); _ilg.EmitGetValueOrDefault(rightType); //RELEASING locLeft locRight FreeLocal(locLeft); FreeLocal(locRight); EmitBinaryOperator( op, leftType.GetNonNullableType(), rightType.GetNonNullableType(), resultType.GetNonNullableType(), liftedToNull: false ); if (!liftedToNull) { _ilg.MarkLabel(shortCircuit); } if (!TypeUtils.AreEquivalent(resultType, resultType.GetNonNullableType())) { _ilg.EmitConvertToType(resultType.GetNonNullableType(), resultType, isChecked: true); } if (liftedToNull) { Label labEnd = _ilg.DefineLabel(); _ilg.Emit(OpCodes.Br, labEnd); _ilg.MarkLabel(shortCircuit); _ilg.Emit(OpCodes.Pop); _ilg.Emit(OpCodes.Ldnull); _ilg.Emit(OpCodes.Unbox_Any, resultType); _ilg.MarkLabel(labEnd); } }
private void EmitNullableCoalesce(BinaryExpression b) { Debug.Assert(b.Method == null); LocalBuilder loc = GetLocal(b.Left.Type); Label labIfNull = _ilg.DefineLabel(); Label labEnd = _ilg.DefineLabel(); EmitExpression(b.Left); _ilg.Emit(OpCodes.Stloc, loc); _ilg.Emit(OpCodes.Ldloca, loc); _ilg.EmitHasValue(b.Left.Type); _ilg.Emit(OpCodes.Brfalse, labIfNull); Type nnLeftType = b.Left.Type.GetNonNullableType(); if (b.Conversion != null) { Debug.Assert(b.Conversion.ParameterCount == 1); ParameterExpression p = b.Conversion.GetParameter(0); Debug.Assert(p.Type.IsAssignableFrom(b.Left.Type) || p.Type.IsAssignableFrom(nnLeftType)); // emit the delegate instance EmitLambdaExpression(b.Conversion); // emit argument if (!p.Type.IsAssignableFrom(b.Left.Type)) { _ilg.Emit(OpCodes.Ldloca, loc); _ilg.EmitGetValueOrDefault(b.Left.Type); } else { _ilg.Emit(OpCodes.Ldloc, loc); } // emit call to invoke _ilg.Emit(OpCodes.Callvirt, b.Conversion.Type.GetMethod("Invoke")); } else if (!TypeUtils.AreEquivalent(b.Type, nnLeftType)) { _ilg.Emit(OpCodes.Ldloca, loc); _ilg.EmitGetValueOrDefault(b.Left.Type); _ilg.EmitConvertToType(nnLeftType, b.Type, isChecked: true); } else { _ilg.Emit(OpCodes.Ldloca, loc); _ilg.EmitGetValueOrDefault(b.Left.Type); } FreeLocal(loc); _ilg.Emit(OpCodes.Br, labEnd); _ilg.MarkLabel(labIfNull); EmitExpression(b.Right); if (!TypeUtils.AreEquivalent(b.Right.Type, b.Type)) { _ilg.EmitConvertToType(b.Right.Type, b.Type, isChecked: true); } _ilg.MarkLabel(labEnd); }
public override bool Equals(object obj) { var other = obj as TypeRestriction; return(other != null && TypeUtils.AreEquivalent(other._type, _type) && other._expression == _expression); }
private void EmitUnaryOperator(ExpressionType op, Type operandType, Type resultType) { var operandIsNullable = operandType.IsNullable(); if (op == ExpressionType.ArrayLength) { IL.Emit(OpCodes.Ldlen); return; } if (operandIsNullable) { switch (op) { case ExpressionType.UnaryPlus: return; case ExpressionType.TypeAs: if (operandType == resultType) { return; } IL.Emit(OpCodes.Box, operandType); IL.Emit(OpCodes.Isinst, resultType); if (resultType.IsNullable()) { IL.Emit(OpCodes.Unbox_Any, resultType); } return; default: Debug.Assert(TypeUtils.AreEquivalent(operandType, resultType)); var labIfNull = IL.DefineLabel(); var labEnd = IL.DefineLabel(); var loc = GetLocal(operandType); // check for null IL.Emit(OpCodes.Stloc, loc); IL.Emit(OpCodes.Ldloca, loc); IL.EmitHasValue(operandType); IL.Emit(OpCodes.Brfalse_S, labIfNull); // apply operator to non-null value IL.Emit(OpCodes.Ldloca, loc); IL.EmitGetValueOrDefault(operandType); var nnOperandType = resultType.GetNonNullable(); EmitUnaryOperator(op, nnOperandType, nnOperandType); // construct result var ci = resultType.GetConstructor(new[] { nnOperandType }); // ReSharper disable once AssignNullToNotNullAttribute IL.Emit(OpCodes.Newobj, ci); IL.Emit(OpCodes.Br_S, labEnd); // if null then push back on stack. IL.MarkLabel(labIfNull); IL.Emit(OpCodes.Ldloc, loc); FreeLocal(loc); IL.MarkLabel(labEnd); return; } } switch (op) { case ExpressionType.Not: if (operandType == typeof(bool)) { IL.Emit(OpCodes.Ldc_I4_0); IL.Emit(OpCodes.Ceq); return; } goto case ExpressionType.OnesComplement; case ExpressionType.OnesComplement: IL.Emit(OpCodes.Not); if (!operandType.IsUnsigned()) { // Guaranteed to fit within result type: no conversion return; } break; case ExpressionType.IsFalse: IL.Emit(OpCodes.Ldc_I4_0); IL.Emit(OpCodes.Ceq); // Not an arithmetic operation -> no conversion return; case ExpressionType.IsTrue: IL.Emit(OpCodes.Ldc_I4_1); IL.Emit(OpCodes.Ceq); // Not an arithmetic operation -> no conversion return; case ExpressionType.UnaryPlus: // Guaranteed to fit within result type: no conversion return; case ExpressionType.Negate: case ExpressionType.NegateChecked: IL.Emit(OpCodes.Neg); // Guaranteed to fit within result type: no conversion // (integer NegateChecked was rewritten to 0 - operand and doesn't hit here). return; case ExpressionType.TypeAs: if (operandType == resultType) { return; } if (operandType.IsValueType) { IL.Emit(OpCodes.Box, operandType); } IL.Emit(OpCodes.Isinst, resultType); if (resultType.IsNullable()) { IL.Emit(OpCodes.Unbox_Any, resultType); } // Not an arithmetic operation -> no conversion return; case ExpressionType.Increment: EmitConstantOne(resultType); IL.Emit(OpCodes.Add); break; case ExpressionType.Decrement: EmitConstantOne(resultType); IL.Emit(OpCodes.Sub); break; default: break; } EmitConvertArithmeticResult(op, resultType); }