public override BoundNode VisitCall(BoundCall node) { if (node.Method.MethodKind == MethodKind.LocalFunction) { BoundExpression receiver; MethodSymbol method; var arguments = node.Arguments; _lambdaRewriter.RemapLocalFunction(node.Syntax, node.Method, out receiver, out method, ref arguments); node = node.Update(receiver, method, arguments); } return base.VisitCall(node); }
private BoundExpression RewriteStringConcatenationFourExprs(SyntaxNode syntax, BoundExpression loweredFirst, BoundExpression loweredSecond, BoundExpression loweredThird, BoundExpression loweredFourth) { Debug.Assert(loweredFirst.HasAnyErrors || loweredFirst.Type.IsStringType()); Debug.Assert(loweredSecond.HasAnyErrors || loweredSecond.Type.IsStringType()); Debug.Assert(loweredThird.HasAnyErrors || loweredThird.Type.IsStringType()); Debug.Assert(loweredFourth.HasAnyErrors || loweredFourth.Type.IsStringType()); var method = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_String__ConcatStringStringStringString); Debug.Assert((object)method != null); return(BoundCall.Synthesized(syntax, null, method, ImmutableArray.Create(loweredFirst, loweredSecond, loweredThird, loweredFourth))); }
/// <summary> /// Strangely enough there is such a thing as unary concatenation and it must be rewritten. /// </summary> private BoundExpression RewriteStringConcatenationOneExpr(SyntaxNode syntax, BoundExpression loweredOperand) { if (loweredOperand.Type.SpecialType == SpecialType.System_String) { // loweredOperand ?? "" return(_factory.Coalesce(loweredOperand, _factory.Literal(""))); } var method = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_String__ConcatObject); Debug.Assert((object)method != null); return((BoundExpression)BoundCall.Synthesized(syntax, null, method, loweredOperand)); }
private BoundExpression RewriteStringConcatenationManyExprs(SyntaxNode syntax, ImmutableArray <BoundExpression> loweredArgs) { Debug.Assert(loweredArgs.Length > 4); Debug.Assert(loweredArgs.All(a => a.HasErrors || a.Type.IsStringType())); var method = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_String__ConcatStringArray); Debug.Assert((object)method != null); var array = _factory.ArrayOrEmpty(_factory.SpecialType(SpecialType.System_String), loweredArgs); return((BoundExpression)BoundCall.Synthesized(syntax, null, method, array)); }
private BoundExpression RewriteStringConcatenationThreeExprs(SyntaxNode syntax, BoundExpression loweredFirst, BoundExpression loweredSecond, BoundExpression loweredThird) { SpecialMember member = (loweredFirst.Type.SpecialType == SpecialType.System_String && loweredSecond.Type.SpecialType == SpecialType.System_String && loweredThird.Type.SpecialType == SpecialType.System_String) ? SpecialMember.System_String__ConcatStringStringString : SpecialMember.System_String__ConcatObjectObjectObject; var method = UnsafeGetSpecialTypeMethod(syntax, member); Debug.Assert((object)method != null); return(BoundCall.Synthesized(syntax, null, method, ImmutableArray.Create(loweredFirst, loweredSecond, loweredThird))); }
/// <summary> /// Checks whether the expression represents a boxing conversion of a special value type. /// If it does, it tries to return a string-based representation instead in order /// to avoid allocations. If it can't, the original expression is returned. /// </summary> private BoundExpression ConvertConcatExprToStringIfPossible(SyntaxNode syntax, BoundExpression expr) { if (expr.Kind == BoundKind.Conversion) { BoundConversion conv = (BoundConversion)expr; if (conv.ConversionKind == ConversionKind.Boxing) { BoundExpression operand = conv.Operand; if (operand != null) { // Is the expression a literal char? If so, we can // simply make it a literal string instead and avoid any // allocations for converting the char to a string at run time. if (operand.Kind == BoundKind.Literal) { ConstantValue cv = ((BoundLiteral)operand).ConstantValue; if (cv != null && cv.SpecialType == SpecialType.System_Char) { return(_factory.StringLiteral(cv.CharValue.ToString())); } } // Can the expression be optimized with a ToString call? // If so, we can synthesize a ToString call to avoid boxing. if (ConcatExprCanBeOptimizedWithToString(operand.Type)) { var toString = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_Object__ToString); var type = (NamedTypeSymbol)operand.Type; var toStringMembers = type.GetMembers(toString.Name); foreach (var member in toStringMembers) { var toStringMethod = member as MethodSymbol; if (toStringMethod.GetLeastOverriddenMethod(type) == (object)toString) { return(BoundCall.Synthesized(syntax, operand, toStringMethod)); } } } } } } // Optimization not possible; just return the original expression. return(expr); }
private BoundExpression VisitCall(BoundCall node) { if (node.IsDelegateCall) { // Generate Expression.Invoke(Receiver, arguments) return(ExprFactory(WellKnownMemberNames.DelegateInvokeName, Visit(node.ReceiverOpt), Expressions(node.Arguments))); } else { // Generate Expression.Call(Receiver, Method, [typeArguments,] arguments) var method = node.Method; return(ExprFactory( "Call", method.IsStatic ? _bound.Null(ExpressionType) : Visit(node.ReceiverOpt), _bound.MethodInfo(method), Expressions(node.Arguments))); } }
public override BoundNode?VisitCall(BoundCall node) { BoundExpression?receiverOpt = (BoundExpression)this.Visit(node.ReceiverOpt); ImmutableArray <BoundExpression> arguments = this.VisitList(node.Arguments); BoundCall updatedNode; if (_updatedNullabilities.TryGetValue(node, out (NullabilityInfo Info, TypeSymbol Type)infoAndType) && _updatedMethodSymbols.TryGetValue(node, out MethodSymbol updatedMethodSymbol)) { updatedNode = node.Update(receiverOpt, updatedMethodSymbol, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.ResultKind, node.BinderOpt, infoAndType.Type); updatedNode.TopLevelNullability = infoAndType.Info; } else { updatedNode = node.Update(receiverOpt, node.Method, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.ResultKind, node.BinderOpt, node.Type); } return(updatedNode); }
public override BoundNode VisitCall(BoundCall node) { if (node.Method.MethodKind == MethodKind.LocalFunction) { BoundExpression receiver; MethodSymbol method; var arguments = node.Arguments; _lambdaRewriter.RemapLocalFunction( node.Syntax, node.Method, out receiver, out method, ref arguments); node = node.Update(receiver, method, arguments); } return(base.VisitCall(node)); }
private BoundExpression MakeNewT(SyntaxNode syntax, TypeParameterSymbol typeParameter) { // "new T()" is rewritten as: "Activator.CreateInstance<T>()". // NOTE: DIFFERENCE FROM DEV12 // Dev12 tried to statically optimize this and would emit default(T) if T happens to be struct // However semantics of "new" in C# requires that parameterless constructor be called // if struct defines one. // Since we cannot know if T has a parameterless constructor statically, // we must call Activator.CreateInstance unconditionally. MethodSymbol method; if ( !this.TryGetWellKnownTypeMember( syntax, WellKnownMember.System_Activator__CreateInstance_T, out method ) ) { return(new BoundDefaultExpression(syntax, type: typeParameter, hasErrors: true)); } Debug.Assert((object)method != null); method = method.Construct(ImmutableArray.Create <TypeSymbol>(typeParameter)); var createInstanceCall = new BoundCall( syntax, null, method, ImmutableArray <BoundExpression> .Empty, default(ImmutableArray <string>), default(ImmutableArray <RefKind>), isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: default(ImmutableArray <int>), defaultArguments: default(BitVector), resultKind: LookupResultKind.Viable, type: typeParameter ); return(createInstanceCall); }
/// <summary> /// Synthesize a no-argument call to a given method, possibly applying a conversion to the receiver. /// /// If the receiver is of struct type and the method is an interface method, then skip the conversion /// and just call the interface method directly - the code generator will detect this and generate a /// constrained virtual call. /// </summary> /// <param name="syntax">A syntax node to attach to the synthesized bound node.</param> /// <param name="receiver">Receiver of method call.</param> /// <param name="method">Method to invoke.</param> /// <param name="receiverConversion">Conversion to be applied to the receiver if not calling an interface method on a struct.</param> /// <param name="convertedReceiverType">Type of the receiver after applying the conversion.</param> /// <returns>A BoundExpression representing the call.</returns> private BoundExpression SynthesizeCall(CSharpSyntaxNode syntax, BoundExpression receiver, MethodSymbol method, Conversion receiverConversion, TypeSymbol convertedReceiverType) { if (!receiver.Type.IsReferenceType && method.ContainingType.IsInterface) { Debug.Assert(receiverConversion.IsImplicit && !receiverConversion.IsUserDefined); // NOTE: The spec says that disposing of a struct enumerator won't cause any // unnecessary boxing to occur. However, Dev10 extends this improvement to the // GetEnumerator call as well. // We're going to let the emitter take care of avoiding the extra boxing. // When it sees an interface call to a struct, it will generate a constrained // virtual call, which will skip boxing, if possible. // CONSIDER: In cases where the struct implicitly implements the interface method // (i.e. with a public method), we could save a few bytes of IL by creating a // BoundCall to the struct method rather than the interface method (so that the // emitter wouldn't need to create a constrained virtual call). It is not clear // what effect this would have on back compat. // NOTE: This call does not correspond to anything that can be written in C# source. // We're invoking the interface method directly on the struct (which may have a private // explicit implementation). The code generator knows how to handle it though. // receiver.InterfaceMethod() return(BoundCall.Synthesized(syntax, receiver, method)); } else { // ((Interface)receiver).InterfaceMethod() Debug.Assert(!receiverConversion.IsNumeric); return(BoundCall.Synthesized( syntax: syntax, receiverOpt: MakeConversion( syntax: syntax, rewrittenOperand: receiver, conversion: receiverConversion, @checked: false, rewrittenType: convertedReceiverType), method: method)); } }
public override BoundNode?VisitCall(BoundCall node) { bool mightMutate = // might be a call to a local function that assigns something node.Method.MethodKind == MethodKind.LocalFunction || // or perhaps we are passing a variable by ref and mutating it that way !node.ArgumentRefKindsOpt.IsDefault; if (mightMutate) { _mightAssignSomething = true; } else { base.VisitCall(node); } return(null); }
private BoundExpression RewriteStringConcatenationManyExprs(SyntaxNode syntax, ImmutableArray <BoundExpression> loweredArgs) { Debug.Assert(loweredArgs.Length > 3); Debug.Assert(loweredArgs.All(a => a.HasErrors || a.Type.SpecialType == SpecialType.System_Object || a.Type.SpecialType == SpecialType.System_String)); bool isObject = false; TypeSymbol elementType = null; foreach (var arg in loweredArgs) { elementType = arg.Type; if (elementType.SpecialType != SpecialType.System_String) { isObject = true; break; } } // Count == 4 is handled differently because there is a Concat method with 4 arguments // for strings, but there is no such method for objects. if (!isObject && loweredArgs.Length == 4) { SpecialMember member = SpecialMember.System_String__ConcatStringStringStringString; var method = UnsafeGetSpecialTypeMethod(syntax, member); Debug.Assert((object)method != null); return((BoundExpression)BoundCall.Synthesized(syntax, null, method, loweredArgs)); } else { SpecialMember member = isObject ? SpecialMember.System_String__ConcatObjectArray : SpecialMember.System_String__ConcatStringArray; var method = UnsafeGetSpecialTypeMethod(syntax, member); Debug.Assert((object)method != null); var array = _factory.ArrayOrEmpty(elementType, loweredArgs); return((BoundExpression)BoundCall.Synthesized(syntax, null, method, array)); } }
public override BoundNode VisitCall(BoundCall node) { bool mightMutate = // might be a call to a local function that assigns something node.Method.MethodKind == MethodKind.LocalFunction || // or perhaps we are passing a variable by ref and mutating it that way, e.g. `int.Parse(..., out x)` !node.ArgumentRefKindsOpt.IsDefault || // or perhaps we are calling a mutating method of a value type MethodMayMutateReceiver(node.ReceiverOpt, node.Method); if (mightMutate) { _mightAssignSomething = true; } else { base.VisitCall(node); } return(null); }
//private MethodSymbol FindUsualObjectCtor() //{ // var objectType = _compilation.GetSpecialType(SpecialType.System_Object); // var ctor = _compilation.UsualType().GetMembers(".ctor").Where(c => c.GetParameterCount() == 1 && c.GetParameterTypes()[0] == objectType).FirstOrDefault(); // return (MethodSymbol) ctor; //} private BoundExpression MemVarFieldAccess(SyntaxNode syntax, XsVariableSymbol property) { var getMethod = property.GetMethod; Debug.Assert((object)getMethod != null); if (property.HasAlias) { var stringType = _compilation.GetSpecialType(SpecialType.System_String); var lit = new BoundLiteral(syntax, ConstantValue.Create(property.Alias), stringType); var arg1 = MakeConversionNode(lit, getMethod.ParameterTypes[0], false); var arg2 = new BoundLiteral(syntax, ConstantValue.Create(property.Name), stringType); return(BoundCall.Synthesized(syntax, null, getMethod, arg1, arg2)); } else { var arg1 = new BoundLiteral(syntax, ConstantValue.Create(property.Name), _compilation.GetSpecialType(SpecialType.System_String)); return(BoundCall.Synthesized(syntax, null, getMethod, arg1)); } }
private BoundExpression MakePropertyGetAccess( SyntaxNode syntax, BoundExpression rewrittenReceiver, PropertySymbol property, ImmutableArray <BoundExpression> rewrittenArguments, MethodSymbol getMethodOpt = null, BoundPropertyAccess oldNodeOpt = null) { #if XSHARP if (property is XsVariableSymbol xsvar) { return(MemVarFieldAccess(syntax, xsvar)); } #endif if (_inExpressionLambda && rewrittenArguments.IsEmpty) { return(oldNodeOpt != null? oldNodeOpt.Update(rewrittenReceiver, property, LookupResultKind.Viable, property.Type) : new BoundPropertyAccess(syntax, rewrittenReceiver, property, LookupResultKind.Viable, property.Type)); } else { var getMethod = getMethodOpt ?? property.GetOwnOrInheritedGetMethod(); #if !XSHARP Debug.Assert((object)getMethod != null); Debug.Assert(getMethod.ParameterCount == rewrittenArguments.Length); Debug.Assert(((object)getMethodOpt == null) || ReferenceEquals(getMethod, getMethodOpt)); #else if (getMethod == null) { _diagnostics.Add(new CSDiagnosticInfo(ErrorCode.ERR_PropertyLacksGet, property.Name), syntax.Location); return(BadExpression(rewrittenReceiver)); } #endif return(BoundCall.Synthesized( syntax, rewrittenReceiver, getMethod, rewrittenArguments)); } }
public static BoundCall ErrorCall( CSharpSyntaxNode node, BoundExpression receiverOpt, MethodSymbol method, ImmutableArray<BoundExpression> arguments, ImmutableArray<string> namedArguments, ImmutableArray<RefKind> refKinds, bool isDelegateCall, bool invokedAsExtensionMethod, ImmutableArray<MethodSymbol> originalMethods, LookupResultKind resultKind) { if (!originalMethods.IsEmpty) resultKind = resultKind.WorseResultKind(LookupResultKind.OverloadResolutionFailure); var call = new BoundCall(node, receiverOpt, method, arguments, namedArguments, refKinds, isDelegateCall: isDelegateCall, expanded: false, invokedAsExtensionMethod: invokedAsExtensionMethod, argsToParamsOpt: default(ImmutableArray<int>), resultKind: resultKind, type: method.ReturnType, hasErrors: true); call.OriginalMethodsOpt = originalMethods; return call; }
private BoundExpression MemVarFieldAssign(SyntaxNode syntax, XsVariableSymbol property, BoundExpression rewrittenRight) { var setMethod = property.SetMethod; if (property.HasAlias) { var stringType = _compilation.GetSpecialType(SpecialType.System_String); var lit = new BoundLiteral(syntax, ConstantValue.Create(property.Alias), stringType); var arg1 = MakeConversionNode(lit, setMethod.ParameterTypes[0], false); var arg2 = new BoundLiteral(syntax, ConstantValue.Create(property.Name), stringType); var arg3 = MakeConversionNode(rewrittenRight, setMethod.ParameterTypes[2], false); return(BoundCall.Synthesized(syntax, null, setMethod, ImmutableArray.Create(arg1, arg2, arg3))); } else { var arg1 = new BoundLiteral(syntax, ConstantValue.Create(property.Name), _compilation.GetSpecialType(SpecialType.System_String)); return(BoundCall.Synthesized(syntax, null, setMethod, arg1, MakeConversionNode(rewrittenRight, _compilation.UsualType(), false))); } }
private BoundExpression MakeCall( CSharpSyntaxNode syntax, BoundExpression rewrittenReceiver, MethodSymbol method, ImmutableArray <BoundExpression> rewrittenArguments, ImmutableArray <RefKind> argumentRefKindsOpt, bool expanded, bool invokedAsExtensionMethod, ImmutableArray <int> argsToParamsOpt, LookupResultKind resultKind, TypeSymbol type, BoundCall nodeOpt = null) { // We have already lowered each argument, but we may need some additional rewriting for the arguments, // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. ImmutableArray <LocalSymbol> temps; rewrittenArguments = MakeArguments(syntax, rewrittenArguments, method, method, expanded, argsToParamsOpt, ref argumentRefKindsOpt, out temps, invokedAsExtensionMethod); return(MakeCall(nodeOpt, syntax, rewrittenReceiver, method, rewrittenArguments, argumentRefKindsOpt, invokedAsExtensionMethod, resultKind, type, temps)); }
public override BoundNode VisitCall(BoundCall node) { BoundExpression receiverOpt = (BoundExpression)this.Visit(node.ReceiverOpt); ReadOnlyArray <BoundExpression> arguments = this.VisitList(node.Arguments); TypeSymbol type = this.VisitType(node.Type); if (!RequiresSpill(arguments) && !RequiresSpill(receiverOpt)) { return(node.Update( receiverOpt, node.Method, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.ResultKind, type)); } var spillBuilder = new SpillBuilder(); var spillResult = SpillExpressionsWithReceiver(receiverOpt, arguments, spillBuilder, node.Method.ParameterRefKinds); var newCall = node.Update( spillResult.Item1, node.Method, spillResult.Item2, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.ResultKind, type); return(spillBuilder.BuildSequenceAndFree(F, newCall)); }
public override BoundNode VisitCall(BoundCall node) { BoundSpillSequence2 ss = null; var arguments = this.VisitExpressionList(ref ss, node.Arguments, node.ArgumentRefKindsOpt); BoundExpression receiver = null; if (ss == null) { receiver = VisitExpression(ref ss, node.ReceiverOpt); } else if (!node.Method.IsStatic) { // spill the receiver if there were await expressions in the arguments var ss2 = new BoundSpillSequence2(); receiver = Spill(ss2, VisitExpression(ref ss2, node.ReceiverOpt), refKind: node.ReceiverOpt.Type.IsReferenceType ? RefKind.None : RefKind.Ref); ss2.IncludeSequence(ss); ss = ss2; } return(UpdateExpression(ss, node.Update(receiver, node.Method, arguments))); }
private BoundExpression MakeLiftedDecimalIncDecOperator(SyntaxNode syntax, BinaryOperatorKind oper, BoundExpression operand) { Debug.Assert(operand.Type.IsNullableType() && operand.Type.GetNullableUnderlyingType().SpecialType == SpecialType.System_Decimal); // This method assumes that operand is already a temporary and so there is no need to copy it again. MethodSymbol method = GetDecimalIncDecOperator(oper); MethodSymbol getValueOrDefault = UnsafeGetNullableMethod(syntax, operand.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); MethodSymbol ctor = UnsafeGetNullableMethod(syntax, operand.Type, SpecialMember.System_Nullable_T__ctor); // x.HasValue BoundExpression condition = MakeNullableHasValue(syntax, operand); // x.GetValueOrDefault() BoundExpression getValueCall = BoundCall.Synthesized(syntax, operand, getValueOrDefault); // op_Inc(x.GetValueOrDefault()) BoundExpression methodCall = BoundCall.Synthesized(syntax, null, method, getValueCall); // new decimal?(op_Inc(x.GetValueOrDefault())) BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, methodCall); // default(decimal?) BoundExpression alternative = new BoundDefaultOperator(syntax, null, operand.Type); // x.HasValue ? new decimal?(op_Inc(x.GetValueOrDefault())) : default(decimal?) return(RewriteConditionalOperator(syntax, condition, consequence, alternative, ConstantValue.NotAvailable, operand.Type)); }
private static BoundCall ReverseLastTwoParameterOrder(BoundCall result) { // The input call has its arguments in the appropriate order for the invocation, but its last // two argument expressions appear in the reverse order from which they appeared in source. // Since we want region analysis to see them in source order, we rewrite the call so that these // two arguments are evaluated in source order. int n = result.Arguments.Length; var arguments = ArrayBuilder <BoundExpression> .GetInstance(); arguments.AddRange(result.Arguments); var lastArgument = arguments[n - 1]; arguments[n - 1] = arguments[n - 2]; arguments[n - 2] = lastArgument; var argsToParams = ArrayBuilder <int> .GetInstance(); argsToParams.AddRange(Enumerable.Range(0, n)); argsToParams[n - 1] = n - 2; argsToParams[n - 2] = n - 1; return(result.Update( result.ReceiverOpt, result.Method, arguments.ToImmutableAndFree(), default(ImmutableArray <string>), default(ImmutableArray <RefKind>), result.IsDelegateCall, result.Expanded, result.InvokedAsExtensionMethod, argsToParams.ToImmutableAndFree(), result.ResultKind, result.BinderOpt, result.Type)); }
public static BoundCall ErrorCall( CSharpSyntaxNode node, BoundExpression receiverOpt, MethodSymbol method, ImmutableArray <BoundExpression> arguments, ImmutableArray <string> namedArguments, ImmutableArray <RefKind> refKinds, bool isDelegateCall, bool invokedAsExtensionMethod, ImmutableArray <MethodSymbol> originalMethods, LookupResultKind resultKind) { if (!originalMethods.IsEmpty) { resultKind = resultKind.WorseResultKind(LookupResultKind.OverloadResolutionFailure); } var call = new BoundCall(node, receiverOpt, method, arguments, namedArguments, refKinds, isDelegateCall: isDelegateCall, expanded: false, invokedAsExtensionMethod: invokedAsExtensionMethod, argsToParamsOpt: default(ImmutableArray <int>), resultKind: resultKind, type: method.ReturnType, hasErrors: true); call.OriginalMethodsOpt = originalMethods; return(call); }
public override BoundNode VisitCall(BoundCall node) { BoundSpillSequence2 ss = null; var arguments = this.VisitExpressionList(ref ss, node.Arguments, node.ArgumentRefKindsOpt); BoundExpression receiver = null; if (ss == null) { receiver = VisitExpression(ref ss, node.ReceiverOpt); } else if (!node.Method.IsStatic) { // spill the receiver if there were await expressions in the arguments var ss2 = new BoundSpillSequence2(); receiver = Spill(ss2, VisitExpression(ref ss2, node.ReceiverOpt), refKind: node.ReceiverOpt.Type.IsReferenceType ? RefKind.None : RefKind.Ref); ss2.IncludeSequence(ss); ss = ss2; } return UpdateExpression(ss, node.Update(receiver, node.Method, arguments)); }
public override BoundNode VisitCall(BoundCall node) { VisitCall(node.Method, null, node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt, node.Expanded, node); CheckReceiverIfField(node.ReceiverOpt); return(base.VisitCall(node)); }
private BoundExpression MakeCall( SyntaxNode syntax, BoundExpression rewrittenReceiver, MethodSymbol method, ImmutableArray<BoundExpression> rewrittenArguments, ImmutableArray<RefKind> argumentRefKindsOpt, bool expanded, bool invokedAsExtensionMethod, ImmutableArray<int> argsToParamsOpt, LookupResultKind resultKind, TypeSymbol type, BoundCall nodeOpt = null) { // We have already lowered each argument, but we may need some additional rewriting for the arguments, // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. ImmutableArray<LocalSymbol> temps; rewrittenArguments = MakeArguments(syntax, rewrittenArguments, method, method, expanded, argsToParamsOpt, ref argumentRefKindsOpt, out temps, invokedAsExtensionMethod); return MakeCall(nodeOpt, syntax, rewrittenReceiver, method, rewrittenArguments, argumentRefKindsOpt, invokedAsExtensionMethod, resultKind, type, temps); }
public override BoundNode VisitCall(BoundCall node) { Debug.Assert(node != null); // Rewrite the receiver BoundExpression rewrittenReceiver = VisitExpression(node.ReceiverOpt); // Rewrite the arguments. // NOTE: We may need additional argument rewriting such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. // NOTE: This is done later by MakeArguments, for now we just lower each argument. var rewrittenArguments = VisitList(node.Arguments); return MakeCall( syntax: node.Syntax, rewrittenReceiver: rewrittenReceiver, method: node.Method, rewrittenArguments: rewrittenArguments, argumentRefKindsOpt: node.ArgumentRefKindsOpt, expanded: node.Expanded, invokedAsExtensionMethod: node.InvokedAsExtensionMethod, argsToParamsOpt: node.ArgsToParamsOpt, resultKind: node.ResultKind, type: node.Type, nodeOpt: node); }
private BoundExpression MakeUserDefinedIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement) { Debug.Assert((object)node.MethodOpt != null); Debug.Assert(node.MethodOpt.ParameterCount == 1); bool isLifted = node.OperatorKind.IsLifted(); bool @checked = node.OperatorKind.IsChecked(); BoundExpression rewrittenArgument = rewrittenValueToIncrement; SyntaxNode syntax = node.Syntax; TypeSymbol type = node.MethodOpt.ParameterTypes[0]; if (isLifted) { type = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(type); Debug.Assert(node.MethodOpt.ParameterTypes[0] == node.MethodOpt.ReturnType); } if (!node.OperandConversion.IsIdentity) { rewrittenArgument = MakeConversionNode( syntax: syntax, rewrittenOperand: rewrittenValueToIncrement, conversion: node.OperandConversion, rewrittenType: type, @checked: @checked); } if (!isLifted) { return(BoundCall.Synthesized(syntax, null, node.MethodOpt, rewrittenArgument)); } // S? temp = operand; // S? r = temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); // Unlike the other unary operators, we do not attempt to optimize nullable user-defined // increment or decrement. The operand is a variable (or property), and so we do not know if // it is always null/never null. BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(rewrittenArgument, out tempAssignment); MethodSymbol getValueOrDefault = UnsafeGetNullableMethod(syntax, type, SpecialMember.System_Nullable_T_GetValueOrDefault); MethodSymbol ctor = UnsafeGetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor); // temp.HasValue BoundExpression condition = MakeNullableHasValue(node.Syntax, boundTemp); // temp.GetValueOrDefault() BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); // op_Increment(temp.GetValueOrDefault()) BoundExpression userDefinedCall = BoundCall.Synthesized(syntax, null, node.MethodOpt, call_GetValueOrDefault); // new S?(op_Increment(temp.GetValueOrDefault())) BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, userDefinedCall); // default(S?) BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); // temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: type); // temp = operand; // temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); return(new BoundSequence( syntax: syntax, locals: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), value: conditionalExpression, type: type)); }
/// <summary> /// Generate a thread-safe accessor for a regular field-like event. /// /// DelegateType tmp0 = _event; //backing field /// DelegateType tmp1; /// DelegateType tmp2; /// do { /// tmp1 = tmp0; /// tmp2 = (DelegateType)Delegate.Combine(tmp1, value); //Remove for -= /// tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); /// } while ((object)tmp0 != (object)tmp1); /// /// Note, if System.Threading.Interlocked.CompareExchange<T> is not available, /// we emit the following code and mark the method Synchronized (unless it is a struct). /// /// _event = (DelegateType)Delegate.Combine(_event, value); //Remove for -= /// /// </summary> internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics) { CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode; TypeSymbol delegateType = eventSymbol.Type; MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod; ParameterSymbol thisParameter = accessor.ThisParameter; TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); SpecialMember updateMethodId = isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove; MethodSymbol updateMethod = (MethodSymbol)compilation.GetSpecialTypeMember(updateMethodId); BoundStatement @return = new BoundReturnStatement(syntax, expressionOpt: null) { WasCompilerGenerated = true }; if (updateMethod == null) { MemberDescriptor memberDescriptor = SpecialMembers.GetDescriptor(updateMethodId); diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.ERR_MissingPredefinedMember, memberDescriptor.DeclaringTypeMetadataName, memberDescriptor.Name), syntax.Location)); return(new BoundBlock(syntax, locals: ImmutableArray <LocalSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>(@return)) { WasCompilerGenerated = true }); } Binder.ReportUseSiteDiagnostics(updateMethod, diagnostics, syntax); BoundThisReference fieldReceiver = eventSymbol.IsStatic ? null : new BoundThisReference(syntax, thisParameter.Type) { WasCompilerGenerated = true }; BoundFieldAccess boundBackingField = new BoundFieldAccess(syntax, receiver: fieldReceiver, fieldSymbol: eventSymbol.AssociatedField, constantValueOpt: null) { WasCompilerGenerated = true }; BoundParameter boundParameter = new BoundParameter(syntax, parameterSymbol: accessor.Parameters[0]) { WasCompilerGenerated = true }; BoundExpression delegateUpdate; MethodSymbol compareExchangeMethod = (MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Threading_Interlocked__CompareExchange_T); if ((object)compareExchangeMethod == null) { // (DelegateType)Delegate.Combine(_event, value) delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>(boundBackingField, boundParameter)), kind: ConversionKind.ExplicitReference, type: delegateType); // _event = (DelegateType)Delegate.Combine(_event, value); BoundStatement eventUpdate = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundBackingField, right: delegateUpdate, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, locals: ImmutableArray <LocalSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>( eventUpdate, @return)) { WasCompilerGenerated = true }); } compareExchangeMethod = compareExchangeMethod.Construct(ImmutableArray.Create <TypeSymbol>(delegateType)); Binder.ReportUseSiteDiagnostics(compareExchangeMethod, diagnostics, syntax); GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop"); const int numTemps = 3; LocalSymbol[] tmps = new LocalSymbol[numTemps]; BoundLocal[] boundTmps = new BoundLocal[numTemps]; for (int i = 0; i < numTemps; i++) { tmps[i] = new SynthesizedLocal(accessor, delegateType, SynthesizedLocalKind.LoweringTemp); boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType); } // tmp0 = _event; BoundStatement tmp0Init = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: boundBackingField, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // LOOP: BoundStatement loopStart = new BoundLabelStatement(syntax, label: loopLabel) { WasCompilerGenerated = true }; // tmp1 = tmp0; BoundStatement tmp1Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[1], right: boundTmps[0], type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // (DelegateType)Delegate.Combine(tmp1, value) delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>(boundTmps[1], boundParameter)), kind: ConversionKind.ExplicitReference, type: delegateType); // tmp2 = (DelegateType)Delegate.Combine(tmp1, value); BoundStatement tmp2Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[2], right: delegateUpdate, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1) BoundExpression compareExchange = BoundCall.Synthesized(syntax, receiverOpt: null, method: compareExchangeMethod, arguments: ImmutableArray.Create <BoundExpression>(boundBackingField, boundTmps[2], boundTmps[1])); // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); BoundStatement tmp0Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: compareExchange, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise BoundExpression loopExitCondition = new BoundBinaryOperator(syntax, operatorKind: BinaryOperatorKind.ObjectEqual, left: boundTmps[0], right: boundTmps[1], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType) { WasCompilerGenerated = true }; // branchfalse (tmp0 == tmp1) LOOP BoundStatement loopEnd = new BoundConditionalGoto(syntax, condition: loopExitCondition, jumpIfTrue: false, label: loopLabel) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, locals: tmps.AsImmutable(), statements: ImmutableArray.Create <BoundStatement>( tmp0Init, loopStart, tmp1Update, tmp2Update, tmp0Update, loopEnd, @return)) { WasCompilerGenerated = true }); }
private BoundExpression VisitCall(BoundCall node) { if (node.IsDelegateCall) { // Generate Expression.Invoke(Receiver, arguments) return ExprFactory(WellKnownMemberNames.DelegateInvokeName, Visit(node.ReceiverOpt), Expressions(node.Arguments)); } else { // Generate Expression.Call(Receiver, Method, [typeArguments,] arguments) var method = node.Method; return ExprFactory( "Call", method.IsStatic ? _bound.Null(ExpressionType) : Visit(node.ReceiverOpt), _bound.MethodInfo(method), Expressions(node.Arguments)); } }
/// <summary> /// If the call represents an extension method invocation with an explicit receiver, return the original /// methods as ReducedExtensionMethodSymbols. Otherwise, return the original methods unchanged. /// </summary> private static ImmutableArray<MethodSymbol> CreateReducedExtensionMethodsFromOriginalsIfNecessary(BoundCall call) { var methods = call.OriginalMethodsOpt; TypeSymbol extensionThisType = null; Debug.Assert(!methods.IsDefault); if (call.InvokedAsExtensionMethod) { // If the call was invoked as an extension method, the receiver // should be non-null and all methods should be extension methods. if (call.ReceiverOpt != null) { extensionThisType = call.ReceiverOpt.Type; } else { extensionThisType = call.Arguments[0].Type; } Debug.Assert((object)extensionThisType != null); } var methodBuilder = ArrayBuilder<MethodSymbol>.GetInstance(); var filteredMethodBuilder = ArrayBuilder<MethodSymbol>.GetInstance(); foreach (var method in FilterOverriddenOrHiddenMethods(methods)) { AddReducedAndFilteredMethodGroupSymbol(methodBuilder, filteredMethodBuilder, method, default(ImmutableArray<TypeSymbol>), extensionThisType); } methodBuilder.Free(); return filteredMethodBuilder.ToImmutableAndFree(); }
private bool CheckIsCallVariable(BoundCall call, CSharpSyntaxNode node, BindValueKind kind, bool checkingReceiver, DiagnosticBag diagnostics) { // A call can only be a variable if it returns by reference. If this is the case, // whether or not it is a valid variable depends on whether or not the call is the // RHS of a return or an assign by reference: // - If call is used in a context demanding ref-returnable reference all of its ref // inputs must be ref-returnable var methodSymbol = call.Method; if (methodSymbol.RefKind != RefKind.None) { if (kind == BindValueKind.RefReturn) { var args = call.Arguments; var argRefKinds = call.ArgumentRefKindsOpt; if (!argRefKinds.IsDefault) { for (var i = 0; i < args.Length; i++) { if (argRefKinds[i] != RefKind.None && !CheckIsVariable(args[i].Syntax, args[i], kind, false, diagnostics)) { var errorCode = checkingReceiver ? ErrorCode.ERR_RefReturnCall2 : ErrorCode.ERR_RefReturnCall; var parameterIndex = call.ArgsToParamsOpt.IsDefault ? i : call.ArgsToParamsOpt[i]; var parameterName = methodSymbol.Parameters[parameterIndex].Name; Error(diagnostics, errorCode, call.Syntax, methodSymbol, parameterName); return false; } } } } return true; } if (checkingReceiver) { // Error is associated with expression, not node which may be distinct. Error(diagnostics, ErrorCode.ERR_ReturnNotLValue, call.Syntax, methodSymbol); } else { Error(diagnostics, GetStandardLvalueError(kind), node); } return false; }
private BoundExpression MakeLiftedUserDefinedConversionConsequence(BoundCall call, TypeSymbol resultType) { if (call.Method.ReturnType.IsNonNullableValueType()) { Debug.Assert(resultType.IsNullableType() && resultType.GetNullableUnderlyingType() == call.Method.ReturnType); MethodSymbol ctor = GetNullableMethod(call.Syntax, resultType, SpecialMember.System_Nullable_T__ctor); return new BoundObjectCreationExpression(call.Syntax, ctor, call); } return call; }
public override BoundNode VisitCall(BoundCall node) { VisitCall(node.Method, null, node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt, node.Expanded, node); CheckReceiverIfField(node.ReceiverOpt); return base.VisitCall(node); }
public override BoundNode VisitCall(BoundCall node) { BoundExpression receiverOpt = (BoundExpression)this.Visit(node.ReceiverOpt); ReadOnlyArray<BoundExpression> arguments = this.VisitList(node.Arguments); TypeSymbol type = this.VisitType(node.Type); if (!RequiresSpill(arguments) && !RequiresSpill(receiverOpt)) { return node.Update( receiverOpt, node.Method, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.ResultKind, type); } var spillBuilder = new SpillBuilder(); var spillResult = SpillExpressionsWithReceiver(receiverOpt, arguments, spillBuilder, node.Method.ParameterRefKinds); var newCall = node.Update( spillResult.Item1, node.Method, spillResult.Item2, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.ResultKind, type); return spillBuilder.BuildSequenceAndFree(F, newCall); }
private BoundExpression MakeNewT(CSharpSyntaxNode syntax, TypeParameterSymbol typeParameter) { // "new T()" is rewritten as: "Activator.CreateInstance<T>()". // NOTE: DIFFERENCE FROM DEV12 // Dev12 tried to statically optimize this and would emit default(T) if T happens to be struct // However semantics of "new" in C# requires that parameterless constructor be called // if struct defines one. // Since we cannot know if T has a parameterless constructor statically, // we must call Activator.CreateInstance unconditionally. MethodSymbol method; if (!this.TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Activator__CreateInstance_T, out method)) { return new BoundDefaultOperator(syntax, null, type: typeParameter, hasErrors: true); } Debug.Assert((object)method != null); method = method.Construct(ImmutableArray.Create<TypeSymbol>(typeParameter)); var createInstanceCall = new BoundCall( syntax, null, method, ImmutableArray<BoundExpression>.Empty, default(ImmutableArray<string>), default(ImmutableArray<RefKind>), isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: default(ImmutableArray<int>), resultKind: LookupResultKind.Viable, type: typeParameter); return createInstanceCall; }
private BoundExpression MakePropertyAssignment( CSharpSyntaxNode syntax, BoundExpression rewrittenReceiver, PropertySymbol property, ImmutableArray <BoundExpression> rewrittenArguments, ImmutableArray <RefKind> argumentRefKindsOpt, bool expanded, ImmutableArray <int> argsToParamsOpt, BoundExpression rewrittenRight, TypeSymbol type, bool used) { // Rewrite property assignment into call to setter. var setMethod = property.GetOwnOrInheritedSetMethod(); if ((object)setMethod == null) { Debug.Assert((property as SourcePropertySymbol)?.IsAutoProperty == true, "only autoproperties can be assignable without having setters"); var backingField = (property as SourcePropertySymbol).BackingField; return(factory.AssignmentExpression( factory.Field(rewrittenReceiver, backingField), rewrittenRight)); } // We have already lowered each argument, but we may need some additional rewriting for the arguments, // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. ImmutableArray <LocalSymbol> argTemps; rewrittenArguments = MakeArguments(syntax, rewrittenArguments, property, setMethod, expanded, argsToParamsOpt, ref argumentRefKindsOpt, out argTemps, enableCallerInfo: ThreeState.True); if (used) { // Save expression value to a temporary before calling the // setter, and restore the temporary after the setter, so the // assignment can be used as an embedded expression. TypeSymbol exprType = rewrittenRight.Type; LocalSymbol rhsTemp = factory.SynthesizedLocal(exprType); BoundExpression boundRhs = new BoundLocal(syntax, rhsTemp, null, exprType); BoundExpression rhsAssignment = new BoundAssignmentOperator( syntax, boundRhs, rewrittenRight, exprType); BoundExpression setterCall = BoundCall.Synthesized( syntax, rewrittenReceiver, setMethod, AppendToPossibleNull(rewrittenArguments, rhsAssignment)); return(new BoundSequence( syntax, AppendToPossibleNull(argTemps, rhsTemp), ImmutableArray.Create(setterCall), boundRhs, type)); } else { BoundCall setterCall = BoundCall.Synthesized( syntax, rewrittenReceiver, setMethod, AppendToPossibleNull(rewrittenArguments, rewrittenRight)); if (argTemps.IsDefaultOrEmpty) { return(setterCall); } else { return(new BoundSequence( syntax, argTemps, ImmutableArray <BoundExpression> .Empty, setterCall, setMethod.ReturnType)); } } }
private BoundStatement RewriteUsingStatementTryFinally(CSharpSyntaxNode syntax, BoundBlock tryBlock, BoundLocal local) { // SPEC: When ResourceType is a non-nullable value type, the expansion is: // SPEC: // SPEC: { // SPEC: ResourceType resource = expr; // SPEC: try { statement; } // SPEC: finally { ((IDisposable)resource).Dispose(); } // SPEC: } // SPEC: // SPEC: Otherwise, when Resource type is a nullable value type or // SPEC: a reference type other than dynamic, the expansion is: // SPEC: // SPEC: { // SPEC: ResourceType resource = expr; // SPEC: try { statement; } // SPEC: finally { if (resource != null) ((IDisposable)resource).Dispose(); } // SPEC: } // SPEC: // SPEC: Otherwise, when ResourceType is dynamic, the expansion is: // SPEC: { // SPEC: dynamic resource = expr; // SPEC: IDisposable d = (IDisposable)resource; // SPEC: try { statement; } // SPEC: finally { if (d != null) d.Dispose(); } // SPEC: } // SPEC: // SPEC: An implementation is permitted to implement a given using statement // SPEC: differently -- for example, for performance reasons -- as long as the // SPEC: behavior is consistent with the above expansion. // // And we do in fact generate the code slightly differently than precisely how it is // described above. // // First: if the type is a non-nullable value type then we do not do the // *boxing conversion* from the resource to IDisposable. Rather, we do // a *constrained virtual call* that elides the boxing if possible. // // Now, you might wonder if that is legal; isn't skipping the boxing producing // an observable difference? Because if the value type is mutable and the Dispose // mutates it, then skipping the boxing means that we are now mutating the original, // not the boxed copy. But this is never observable. Either (1) we have "using(R r = x){}" // and r is out of scope after the finally, so it is not possible to observe the mutation, // or (2) we have "using(x) {}". But that has the semantics of "using(R temp = x){}", // so again, we are not mutating x to begin with; we're always mutating a copy. Therefore // it doesn't matter if we skip making *a copy of the copy*. // // This is what the dev10 compiler does, and we do so as well. // // Second: if the type is a nullable value type then we can similarly elide the boxing. // We can generate // // { // ResourceType resource = expr; // try { statement; } // finally { if (resource.HasValue) resource.GetValueOrDefault().Dispose(); } // } // // Where again we do a constrained virtual call to Dispose, rather than boxing // the value to IDisposable. // // Note that this optimization is *not* what the native compiler does; in this case // the native compiler behavior is to test for HasValue, then *box* and convert // the boxed value to IDisposable. There's no need to do that. // // Third: if we have "using(x)" and x is dynamic then obviously we need not generate // "{ dynamic temp1 = x; IDisposable temp2 = (IDisposable) temp1; ... }". Rather, we elide // the completely unnecessary first temporary. BoundExpression disposedExpression; bool isNullableValueType = local.Type.IsNullableType(); if (isNullableValueType) { MethodSymbol getValueOrDefault = GetNullableMethod(syntax, local.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); // local.GetValueOrDefault() disposedExpression = BoundCall.Synthesized(syntax, local, getValueOrDefault); } else { // local disposedExpression = local; } // local.Dispose() BoundExpression disposeCall; MethodSymbol disposeMethodSymbol; if (TryGetSpecialTypeMember(syntax, SpecialMember.System_IDisposable__Dispose, out disposeMethodSymbol)) { disposeCall = BoundCall.Synthesized(syntax, disposedExpression, disposeMethodSymbol); } else { disposeCall = new BoundBadExpression(syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(disposedExpression), ErrorTypeSymbol.UnknownResultType); } // local.Dispose(); BoundStatement disposeStatement = new BoundExpressionStatement(syntax, disposeCall); BoundExpression ifCondition; if (isNullableValueType) { MethodSymbol hasValue = GetNullableMethod(syntax, local.Type, SpecialMember.System_Nullable_T_get_HasValue); // local.HasValue ifCondition = BoundCall.Synthesized(syntax, local, hasValue); } else if (local.Type.IsValueType) { ifCondition = null; } else { // local != null ifCondition = MakeNullCheck(syntax, local, BinaryOperatorKind.NotEqual); } BoundStatement finallyStatement; if (ifCondition == null) { // local.Dispose(); finallyStatement = disposeStatement; } else { // if (local != null) local.Dispose(); // or // if (local.HasValue) local.GetValueOrDefault().Dispose(); finallyStatement = RewriteIfStatement( syntax: syntax, rewrittenCondition: ifCondition, rewrittenConsequence: disposeStatement, rewrittenAlternativeOpt: null, hasErrors: false); } // try { ... } finally { if (local != null) local.Dispose(); } BoundStatement tryFinally = new BoundTryStatement( syntax: syntax, tryBlock: tryBlock, catchBlocks: ImmutableArray <BoundCatchBlock> .Empty, finallyBlockOpt: BoundBlock.SynthesizedNoLocals(syntax, finallyStatement)); return(tryFinally); }
/// <summary> /// If the call represents an extension method with an explicit receiver, return a /// ReducedExtensionMethodSymbol if it can be constructed. Otherwise, return the /// original call method. /// </summary> private static ImmutableArray<Symbol> CreateReducedExtensionMethodIfPossible(BoundCall call) { var method = call.Method; Debug.Assert((object)method != null); if (call.InvokedAsExtensionMethod && method.IsExtensionMethod && method.MethodKind != MethodKind.ReducedExtension) { Debug.Assert(call.Arguments.Length > 0); BoundExpression receiver = call.Arguments[0]; MethodSymbol reduced = method.ReduceExtensionMethod(receiver.Type); // If the extension method can't be applied to the receiver of the given // type, we should also return the original call method. method = reduced ?? method; } return ImmutableArray.Create<Symbol>(method); }
/// <summary> /// Generate a thread-safe accessor for a WinRT field-like event. /// /// Add: /// return EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).AddEventHandler(value); /// /// Remove: /// EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).RemoveEventHandler(value); /// </summary> internal static BoundBlock ConstructFieldLikeEventAccessorBody_WinRT(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics) { CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode; MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod; Debug.Assert((object)accessor != null); FieldSymbol field = eventSymbol.AssociatedField; Debug.Assert((object)field != null); NamedTypeSymbol fieldType = (NamedTypeSymbol)field.Type; Debug.Assert(fieldType.Name == "EventRegistrationTokenTable"); MethodSymbol getOrCreateMethod = (MethodSymbol)Binder.GetWellKnownTypeMember( compilation, WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationTokenTable_T__GetOrCreateEventRegistrationTokenTable, diagnostics, syntax: syntax); if ((object)getOrCreateMethod == null) { Debug.Assert(diagnostics.HasAnyErrors()); return(null); } getOrCreateMethod = getOrCreateMethod.AsMember(fieldType); WellKnownMember processHandlerMember = isAddMethod ? WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationTokenTable_T__AddEventHandler : WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationTokenTable_T__RemoveEventHandler; MethodSymbol processHandlerMethod = (MethodSymbol)Binder.GetWellKnownTypeMember( compilation, processHandlerMember, diagnostics, syntax: syntax); if ((object)processHandlerMethod == null) { Debug.Assert(diagnostics.HasAnyErrors()); return(null); } processHandlerMethod = processHandlerMethod.AsMember(fieldType); // _tokenTable BoundFieldAccess fieldAccess = new BoundFieldAccess( syntax, field.IsStatic ? null : new BoundThisReference(syntax, accessor.ThisParameter.Type), field, constantValueOpt: null) { WasCompilerGenerated = true }; // EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable) BoundCall getOrCreateCall = BoundCall.Synthesized( syntax, receiverOpt: null, method: getOrCreateMethod, arg0: fieldAccess); // value BoundParameter parameterAccess = new BoundParameter( syntax, accessor.Parameters.Single()); // EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).AddHandler(value) // or RemoveHandler BoundCall processHandlerCall = BoundCall.Synthesized( syntax, receiverOpt: getOrCreateCall, method: processHandlerMethod, arg0: parameterAccess); if (isAddMethod) { // { // return EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).AddHandler(value); // } BoundStatement returnStatement = BoundReturnStatement.Synthesized(syntax, processHandlerCall); return(BoundBlock.SynthesizedNoLocals(syntax, returnStatement)); } else { // { // EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).RemoveHandler(value); // return; // } BoundStatement callStatement = new BoundExpressionStatement(syntax, processHandlerCall); BoundStatement returnStatement = new BoundReturnStatement(syntax, expressionOpt: null); return(BoundBlock.SynthesizedNoLocals(syntax, callStatement, returnStatement)); } }
private BoundExpression MakeNewT(CSharpSyntaxNode syntax, TypeParameterSymbol typeParameter) { // How "new T()" is rewritten depends on whether T is known to be a value // type, a reference type, or neither (see OperatorRewriter::VisitNEWTYVAR). if (typeParameter.IsValueType) { // "new T()" rewritten as: "default(T)". return new BoundDefaultOperator(syntax, type: typeParameter); } // For types not known to be value types, "new T()" requires // Activator.CreateInstance<T>(). MethodSymbol method; if (!this.TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Activator__CreateInstance_T, out method)) { return new BoundDefaultOperator(syntax, null, type: typeParameter, hasErrors: true); } Debug.Assert((object)method != null); method = method.Construct(ImmutableArray.Create<TypeSymbol>(typeParameter)); var createInstanceCall = new BoundCall( syntax, null, method, ImmutableArray<BoundExpression>.Empty, default(ImmutableArray<string>), default(ImmutableArray<RefKind>), isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: default(ImmutableArray<int>), resultKind: LookupResultKind.Viable, type: typeParameter); if (typeParameter.IsReferenceType) { // "new T()" is rewritten as: "Activator.CreateInstance<T>()". return createInstanceCall; } else { // "new T()" is rewritten as: "(null == (object)default(T)) ? Activator.CreateInstance<T>() : default(T)". var defaultT = new BoundDefaultOperator(syntax, type: typeParameter); return new BoundConditionalOperator( syntax, MakeNullCheck( syntax: syntax, rewrittenExpr: MakeConversion( syntax: syntax, rewrittenOperand: defaultT, conversionKind: ConversionKind.Boxing, rewrittenType: this.compilation.GetSpecialType(SpecialType.System_Object), @checked: false), operatorKind: BinaryOperatorKind.Equal), createInstanceCall, defaultT, constantValueOpt: null, type: typeParameter); } }
private BoundExpression LowerLiftedUnaryOperator( UnaryOperatorKind kind, SyntaxNode syntax, MethodSymbol method, BoundExpression loweredOperand, TypeSymbol type) { // First, an optimization. If we know that the operand is always null then // we can simply lower to the alternative. BoundExpression optimized = OptimizeLiftedUnaryOperator(kind, syntax, method, loweredOperand, type); if (optimized != null) { return(optimized); } // We do not know whether the operand is null or non-null, so we generate: // // S? temp = operand; // R? r = temp.HasValue ? // new R?(OP(temp.GetValueOrDefault())) : // default(R?); BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(loweredOperand, out tempAssignment); MethodSymbol getValueOrDefault = UnsafeGetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); // temp.HasValue BoundExpression condition = MakeNullableHasValue(syntax, boundTemp); // temp.GetValueOrDefault() BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); // new R?(temp.GetValueOrDefault()) BoundExpression consequence = GetLiftedUnaryOperatorConsequence(kind, syntax, method, type, call_GetValueOrDefault); // default(R?) BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); // temp.HasValue ? // new R?(OP(temp.GetValueOrDefault())) : // default(R?); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: type); // temp = operand; // temp.HasValue ? // new R?(OP(temp.GetValueOrDefault())) : // default(R?); return(new BoundSequence( syntax: syntax, locals: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), value: conditionalExpression, type: type)); }
private static BoundExpression ExtractCastInvocation(BoundCall invocation) { int index = invocation.InvokedAsExtensionMethod ? 1 : 0; var c1 = invocation.Arguments[index] as BoundConversion; var l1 = c1 != null ? c1.Operand as BoundLambda : null; var r1 = l1 != null ? l1.Body.Statements[0] as BoundReturnStatement : null; var i1 = r1 != null ? r1.ExpressionOpt as BoundCall : null; return i1; }
private BoundExpression MakeUnaryOperator( BoundUnaryOperator oldNode, UnaryOperatorKind kind, SyntaxNode syntax, MethodSymbol method, BoundExpression loweredOperand, TypeSymbol type) { if (kind.IsDynamic()) { Debug.Assert(kind == UnaryOperatorKind.DynamicTrue && type.SpecialType == SpecialType.System_Boolean || type.IsDynamic()); Debug.Assert((object)method == null); // Logical operators on boxed Boolean constants: var constant = UnboxConstant(loweredOperand); if (constant == ConstantValue.True || constant == ConstantValue.False) { if (kind == UnaryOperatorKind.DynamicTrue) { return(_factory.Literal(constant.BooleanValue)); } else if (kind == UnaryOperatorKind.DynamicLogicalNegation) { return(MakeConversionNode(_factory.Literal(!constant.BooleanValue), type, @checked: false)); } } return(_dynamicFactory.MakeDynamicUnaryOperator(kind, loweredOperand, type).ToExpression()); } else if (kind.IsLifted()) { if (!_inExpressionLambda) { return(LowerLiftedUnaryOperator(kind, syntax, method, loweredOperand, type)); } } else if (kind.IsUserDefined()) { Debug.Assert((object)method != null); Debug.Assert(type == method.ReturnType); if (!_inExpressionLambda || kind == UnaryOperatorKind.UserDefinedTrue || kind == UnaryOperatorKind.UserDefinedFalse) { return(BoundCall.Synthesized(syntax, null, method, loweredOperand)); } } else if (kind.Operator() == UnaryOperatorKind.UnaryPlus) { // We do not call the operator even for decimal; we simply optimize it away entirely. return(loweredOperand); } if (kind == UnaryOperatorKind.EnumBitwiseComplement) { var underlyingType = loweredOperand.Type.GetEnumUnderlyingType(); var upconvertSpecialType = Binder.GetEnumPromotedType(underlyingType.SpecialType); var upconvertType = upconvertSpecialType == underlyingType.SpecialType ? underlyingType : _compilation.GetSpecialType(upconvertSpecialType); var newOperand = MakeConversionNode(loweredOperand, upconvertType, false); UnaryOperatorKind newKind = kind.Operator().WithType(upconvertSpecialType); var newNode = (oldNode != null) ? oldNode.Update( newKind, newOperand, oldNode.ConstantValueOpt, method, newOperand.ResultKind, upconvertType) : new BoundUnaryOperator( syntax, newKind, newOperand, null, method, LookupResultKind.Viable, upconvertType); return(MakeConversionNode(newNode.Syntax, newNode, Conversion.ExplicitEnumeration, type, @checked: false)); } if (kind == UnaryOperatorKind.DecimalUnaryMinus) { method = (MethodSymbol)_compilation.Assembly.GetSpecialTypeMember(SpecialMember.System_Decimal__op_UnaryNegation); if (!_inExpressionLambda) { return(BoundCall.Synthesized(syntax, null, method, loweredOperand)); } } return((oldNode != null) ? oldNode.Update(kind, loweredOperand, oldNode.ConstantValueOpt, method, oldNode.ResultKind, type) : new BoundUnaryOperator(syntax, kind, loweredOperand, null, method, LookupResultKind.Viable, type)); }
private static BoundCall ReverseLastTwoParameterOrder(BoundCall result) { // The input call has its arguments in the appropriate order for the invocation, but its last // two argument expressions appear in the reverse order from which they appeared in source. // Since we want region analysis to see them in source order, we rewrite the call so that these // two arguments are evaluated in source order. int n = result.Arguments.Length; var arguments = ArrayBuilder<BoundExpression>.GetInstance(); arguments.AddRange(result.Arguments); var lastArgument = arguments[n - 1]; arguments[n - 1] = arguments[n - 2]; arguments[n - 2] = lastArgument; var argsToParams = ArrayBuilder<int>.GetInstance(); argsToParams.AddRange(Enumerable.Range(0, n)); argsToParams[n - 1] = n - 2; argsToParams[n - 2] = n - 1; return result.Update( result.ReceiverOpt, result.Method, arguments.ToImmutableAndFree(), default(ImmutableArray<string>), default(ImmutableArray<RefKind>), result.IsDelegateCall, result.Expanded, result.InvokedAsExtensionMethod, argsToParams.ToImmutableAndFree(), result.ResultKind, result.Type); }
public override BoundNode VisitCall(BoundCall node) { if (node.Method.MethodKind == MethodKind.LocalFunction) { // Use OriginalDefinition to strip generic type parameters ReferenceVariable(node.Syntax, node.Method.OriginalDefinition); } return base.VisitCall(node); }
private BoundExpression MakeCall( BoundCall node, SyntaxNode syntax, BoundExpression rewrittenReceiver, MethodSymbol method, ImmutableArray<BoundExpression> rewrittenArguments, ImmutableArray<RefKind> argumentRefKinds, bool invokedAsExtensionMethod, LookupResultKind resultKind, TypeSymbol type, ImmutableArray<LocalSymbol> temps = default(ImmutableArray<LocalSymbol>)) { BoundExpression rewrittenBoundCall; if (method.IsStatic && method.ContainingType.IsObjectType() && !_inExpressionLambda && (object)method == (object)_compilation.GetSpecialTypeMember(SpecialMember.System_Object__ReferenceEquals)) { Debug.Assert(rewrittenArguments.Length == 2); // ECMA - 335 // I.8.2.5.1 Identity // ... // Identity is implemented on System.Object via the ReferenceEquals method. rewrittenBoundCall = new BoundBinaryOperator( syntax, BinaryOperatorKind.ObjectEqual, rewrittenArguments[0], rewrittenArguments[1], null, null, resultKind, type); } else if (node == null) { rewrittenBoundCall = new BoundCall( syntax, rewrittenReceiver, method, rewrittenArguments, default(ImmutableArray<string>), argumentRefKinds, isDelegateCall: false, expanded: false, invokedAsExtensionMethod: invokedAsExtensionMethod, argsToParamsOpt: default(ImmutableArray<int>), resultKind: resultKind, type: type); } else { rewrittenBoundCall = node.Update( rewrittenReceiver, method, rewrittenArguments, default(ImmutableArray<string>), argumentRefKinds, node.IsDelegateCall, false, node.InvokedAsExtensionMethod, default(ImmutableArray<int>), node.ResultKind, node.Type); } if (!temps.IsDefaultOrEmpty) { return new BoundSequence( syntax, locals: temps, sideEffects: ImmutableArray<BoundExpression>.Empty, value: rewrittenBoundCall, type: type); } return rewrittenBoundCall; }