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> /// 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)); }
/// <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_Rune) { return(_factory.StringLiteral(cv.RuneValue.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) { 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)); } }
private BoundExpression MakePropertyGetAccess( SyntaxNode syntax, BoundExpression rewrittenReceiver, PropertySymbol property, ImmutableArray <BoundExpression> rewrittenArguments, MethodSymbol getMethodOpt = null, BoundPropertyAccess oldNodeOpt = null) { var getMethod = getMethodOpt ?? property.GetOwnOrInheritedGetMethod(); Debug.Assert((object)getMethod != null); Debug.Assert(getMethod.ParameterCount == rewrittenArguments.Length); Debug.Assert(((object)getMethodOpt == null) || ReferenceEquals(getMethod, getMethodOpt)); return(BoundCall.Synthesized( syntax, rewrittenReceiver, getMethod, rewrittenArguments)); }
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, 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, binderOpt: null, type: typeParameter); return(createInstanceCall); }
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 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)); }
internal static BoundBlock ConstructDestructorBody(MethodSymbol method, BoundBlock block) { var syntax = block.Syntax; Debug.Assert(method.MethodKind == MethodKind.Destructor); Debug.Assert(syntax.Kind() == SyntaxKind.Block || syntax.Kind() == SyntaxKind.ArrowExpressionClause); // If this is a destructor and a base type has a Finalize method (see GetBaseTypeFinalizeMethod for exact // requirements), then we need to call that method in a finally block. Otherwise, just return block as-is. // NOTE: the Finalize method need not be a destructor or be overridden by the current method. MethodSymbol baseTypeFinalize = GetBaseTypeFinalizeMethod(method); if ((object)baseTypeFinalize != null) { BoundStatement baseFinalizeCall = new BoundExpressionStatement( syntax, BoundCall.Synthesized( syntax, new BoundBaseReference( syntax, method.ContainingType) { WasCompilerGenerated = true }, baseTypeFinalize)) { WasCompilerGenerated = true }; if (syntax.Kind() == SyntaxKind.Block) { //sequence point to mimic Dev10 baseFinalizeCall = new BoundSequencePointWithSpan( syntax, baseFinalizeCall, ((BlockSyntax)syntax).CloseBraceToken.Span); } return(new BoundBlock( syntax, ImmutableArray <LocalSymbol> .Empty, ImmutableArray.Create <BoundStatement>( new BoundTryStatement( syntax, block, ImmutableArray <BoundCatchBlock> .Empty, new BoundBlock( syntax, ImmutableArray <LocalSymbol> .Empty, ImmutableArray.Create <BoundStatement>( baseFinalizeCall) ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true }))); } return(block); }
/// <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.TypeSymbol; 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, refKind: RefKind.None, 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(BoundBlock.SynthesizedNoLocals(syntax, @return)); } Binder.ReportUseSiteDiagnostics(updateMethod, diagnostics, syntax); BoundThisReference fieldReceiver = eventSymbol.IsStatic ? null : new BoundThisReference(syntax, thisParameter.Type.TypeSymbol) { 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)), conversion: Conversion.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(BoundBlock.SynthesizedNoLocals(syntax, statements: ImmutableArray.Create <BoundStatement>( eventUpdate, @return))); } 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, TypeSymbolWithAnnotations.Create(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)), conversion: Conversion.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 MakePropertyAssignment( SyntaxNode 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, invokedAsExtensionMethod: false, 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.TypeSymbol)); } } }
/// <summary> /// Lowers a lock statement to a try-finally block that calls Monitor.Enter and Monitor.Exit /// before and after the body, respectively. /// </summary> public override BoundNode VisitLockStatement(BoundLockStatement node) { LockStatementSyntax lockSyntax = (LockStatementSyntax)node.Syntax; BoundExpression rewrittenArgument = VisitExpression(node.Argument); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); TypeSymbol argumentType = rewrittenArgument.Type; if ((object)argumentType == null) { // This isn't particularly elegant, but hopefully locking on null is // not very common. Debug.Assert(rewrittenArgument.ConstantValue == ConstantValue.Null); argumentType = _compilation.GetSpecialType(SpecialType.System_Object); rewrittenArgument = MakeLiteral( rewrittenArgument.Syntax, rewrittenArgument.ConstantValue, argumentType); //need to have a non-null type here for TempHelpers.StoreToTemp. } BoundAssignmentOperator assignmentToLockTemp; BoundLocal boundLockTemp = _factory.StoreToTemp(rewrittenArgument, out assignmentToLockTemp, syntaxOpt: lockSyntax, kind: SynthesizedLocalKind.Lock); BoundStatement boundLockTempInit = new BoundExpressionStatement(lockSyntax, assignmentToLockTemp); BoundExpression exitCallExpr; MethodSymbol exitMethod; if (TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Exit, out exitMethod)) { exitCallExpr = BoundCall.Synthesized( lockSyntax, null, exitMethod, boundLockTemp); } else { exitCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundExpression>(boundLockTemp), ErrorTypeSymbol.UnknownResultType); } BoundStatement exitCall = new BoundExpressionStatement(lockSyntax, exitCallExpr); MethodSymbol enterMethod; if ((TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter2, out enterMethod, isOptional: true) || TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter, out enterMethod)) && // If we didn't find the overload introduced in .NET 4.0, then use the older one. enterMethod.ParameterCount == 2) { // C# 4.0+ version // L $lock = `argument`; // sequence point // bool $lockTaken = false; // try // { // Monitor.Enter($lock, ref $lockTaken); // `body` // sequence point // } // finally // { // hidden sequence point // if ($lockTaken) Monitor.Exit($lock); // } TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); BoundAssignmentOperator assignmentToLockTakenTemp; BoundLocal boundLockTakenTemp = _factory.StoreToTemp( MakeLiteral(rewrittenArgument.Syntax, ConstantValue.False, boolType), store: out assignmentToLockTakenTemp, syntaxOpt: lockSyntax, kind: SynthesizedLocalKind.LockTaken); BoundStatement boundLockTakenTempInit = new BoundExpressionStatement(lockSyntax, assignmentToLockTakenTemp); BoundStatement enterCall = new BoundExpressionStatement( lockSyntax, BoundCall.Synthesized( lockSyntax, null, enterMethod, boundLockTemp, boundLockTakenTemp)); exitCall = RewriteIfStatement( lockSyntax, boundLockTakenTemp, exitCall, null, node.HasErrors); return(new BoundBlock( lockSyntax, ImmutableArray.Create(boundLockTemp.LocalSymbol, boundLockTakenTemp.LocalSymbol), ImmutableArray.Create( InstrumentLockTargetCapture(node, boundLockTempInit), boundLockTakenTempInit, new BoundTryStatement( lockSyntax, BoundBlock.SynthesizedNoLocals(lockSyntax, ImmutableArray.Create( enterCall, rewrittenBody)), ImmutableArray <BoundCatchBlock> .Empty, BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall))))); } else { // Pre-4.0 version // L $lock = `argument`; // sequence point // Monitor.Enter($lock); // NB: before try-finally so we don't Exit if an exception prevents us from acquiring the lock. // try // { // `body` // sequence point // } // finally // { // Monitor.Exit($lock); // hidden sequence point // } BoundExpression enterCallExpr; if ((object)enterMethod != null) { Debug.Assert(enterMethod.ParameterCount == 1); enterCallExpr = BoundCall.Synthesized( lockSyntax, null, enterMethod, boundLockTemp); } else { enterCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundExpression>(boundLockTemp), ErrorTypeSymbol.UnknownResultType); } BoundStatement enterCall = new BoundExpressionStatement( lockSyntax, enterCallExpr); return(new BoundBlock( lockSyntax, ImmutableArray.Create(boundLockTemp.LocalSymbol), ImmutableArray.Create( InstrumentLockTargetCapture(node, boundLockTempInit), enterCall, new BoundTryStatement( lockSyntax, BoundBlock.SynthesizedNoLocals(lockSyntax, rewrittenBody), ImmutableArray <BoundCatchBlock> .Empty, BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall))))); } }
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 BoundDefaultExpression(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, isRef: false); // 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)); }
/// <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.TypeSymbol; 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.TypeSymbol), 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[0]); // 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, RefKind.None, 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, RefKind.None, expressionOpt: null); return(BoundBlock.SynthesizedNoLocals(syntax, callStatement, returnStatement)); } }
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].TypeSymbol; if (isLifted) { type = _compilation.GetSpecialType(SpecialType.core_Option_T).Construct(type); Debug.Assert(TypeSymbol.Equals(node.MethodOpt.ParameterTypes[0].TypeSymbol, node.MethodOpt.ReturnType.TypeSymbol, TypeCompareKind.ConsiderEverything2)); } 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, null, userDefinedCall); // default(S?) BoundExpression alternative = new BoundDefaultExpression(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, isRef: false); // 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)); }
private BoundStatement RewriteUsingStatementTryFinally(SyntaxNode syntax, BoundBlock tryBlock, BoundLocal local, SyntaxToken awaitKeywordOpt, AwaitableInfo awaitOpt, MethodSymbol methodOpt) { // 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. // // In the case of using-await statement, we'll use "IAsyncDisposable" instead of "IDisposable", "await DisposeAsync()" instead of "Dispose()" // // 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. Debug.Assert((awaitKeywordOpt == default) == (awaitOpt == default(AwaitableInfo))); BoundExpression disposedExpression; bool isNullableValueType = local.Type.IsNullableType(); if (isNullableValueType) { MethodSymbol getValueOrDefault = UnsafeGetNullableMethod(syntax, local.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); // local.GetValueOrDefault() disposedExpression = BoundCall.Synthesized(syntax, local, getValueOrDefault); } else { // local disposedExpression = local; } BoundExpression disposeCall = GenerateDisposeCall(syntax, disposedExpression, methodOpt, awaitOpt, awaitKeywordOpt); // local.Dispose(); or await variant BoundStatement disposeStatement = new BoundExpressionStatement(syntax, disposeCall); BoundExpression ifCondition; if (isNullableValueType) { // local.HasValue ifCondition = MakeNullableHasValue(syntax, local); } else if (local.Type.IsValueType) { ifCondition = null; } else { // local != null ifCondition = MakeNullCheck(syntax, local, BinaryOperatorKind.NotEqual); } BoundStatement finallyStatement; if (ifCondition == null) { // local.Dispose(); or await variant finallyStatement = disposeStatement; } else { // if (local != null) local.Dispose(); // or // if (local.HasValue) local.GetValueOrDefault().Dispose(); // or // await variants finallyStatement = RewriteIfStatement( syntax: syntax, rewrittenCondition: ifCondition, rewrittenConsequence: disposeStatement, rewrittenAlternativeOpt: null, hasErrors: false); } // try { ... } finally { if (local != null) local.Dispose(); } // or // nullable or await variants BoundStatement tryFinally = new BoundTryStatement( syntax: syntax, tryBlock: tryBlock, catchBlocks: ImmutableArray <BoundCatchBlock> .Empty, finallyBlockOpt: BoundBlock.SynthesizedNoLocals(syntax, finallyStatement)); return(tryFinally); }
private BoundExpression MakeNullCoalescingOperator( SyntaxNode syntax, BoundExpression rewrittenLeft, BoundExpression rewrittenRight, Conversion leftConversion, BoundNullCoalescingOperatorResultKind resultKind, TypeSymbol rewrittenResultType) { Debug.Assert(rewrittenLeft != null); Debug.Assert(rewrittenRight != null); Debug.Assert(leftConversion.IsValid); Debug.Assert((object)rewrittenResultType != null); Debug.Assert(rewrittenRight.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); var isUnconstrainedTypeParameter = (object)rewrittenLeft.Type != null && !rewrittenLeft.Type.IsReferenceType && !rewrittenLeft.Type.IsValueType; // first we can make a small optimization: // If left is a constant then we already know whether it is null or not. If it is null then we // can simply generate "right". If it is not null then we can simply generate // MakeConversion(left). This does not hold when the left is an unconstrained type parameter: at runtime, // it can be either left or right depending on the runtime type of T if (!isUnconstrainedTypeParameter) { if (rewrittenLeft.IsDefaultValue()) { return(rewrittenRight); } if (rewrittenLeft.ConstantValue != null) { Debug.Assert(!rewrittenLeft.ConstantValue.IsNull); return(GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType)); } } // string concatenation is never null. // interpolated string lowering may introduce redundant null coalescing, which we have to remove. if (IsStringConcat(rewrittenLeft)) { return(GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType)); } // if left conversion is intrinsic implicit (always succeeds) and results in a reference type // we can apply conversion before doing the null check that allows for a more efficient IL emit. if (rewrittenLeft.Type.IsReferenceType && leftConversion.IsImplicit && !leftConversion.IsUserDefined) { if (!leftConversion.IsIdentity) { rewrittenLeft = MakeConversionNode(rewrittenLeft.Syntax, rewrittenLeft, leftConversion, rewrittenResultType, @checked: false); } return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, Conversion.Identity, resultKind, rewrittenResultType)); } if (leftConversion.IsIdentity || leftConversion.Kind == ConversionKind.ExplicitNullable) { var conditionalAccess = rewrittenLeft as BoundLoweredConditionalAccess; if (conditionalAccess != null && (conditionalAccess.WhenNullOpt == null || NullableNeverHasValue(conditionalAccess.WhenNullOpt))) { var notNullAccess = NullableAlwaysHasValue(conditionalAccess.WhenNotNull); if (notNullAccess != null) { var whenNullOpt = rewrittenRight; if (whenNullOpt.Type.IsNullableType()) { notNullAccess = conditionalAccess.WhenNotNull; } if (whenNullOpt.IsDefaultValue()) { whenNullOpt = null; } return(conditionalAccess.Update( conditionalAccess.Receiver, conditionalAccess.HasValueMethodOpt, whenNotNull: notNullAccess, whenNullOpt: whenNullOpt, id: conditionalAccess.Id, type: rewrittenResultType )); } } } // Optimize left ?? right to left.GetValueOrDefault() when left is T? and right is the default value of T if (rewrittenLeft.Type.IsNullableType() && RemoveIdentityConversions(rewrittenRight).IsDefaultValue() && rewrittenRight.Type.Equals(rewrittenLeft.Type.GetNullableUnderlyingType(), TypeCompareKind.AllIgnoreOptions) && TryGetNullableMethod(rewrittenLeft.Syntax, rewrittenLeft.Type, SpecialMember.System_Nullable_T_GetValueOrDefault, out MethodSymbol getValueOrDefault)) { return(BoundCall.Synthesized(rewrittenLeft.Syntax, rewrittenLeft, getValueOrDefault)); } // We lower left ?? right to // // var temp = left; // (temp != null) ? MakeConversion(temp) : right // BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(rewrittenLeft, out tempAssignment); // temp != null BoundExpression nullCheck = MakeNullCheck(syntax, boundTemp, BinaryOperatorKind.NotEqual); // MakeConversion(temp, rewrittenResultType) BoundExpression convertedLeft = GetConvertedLeftForNullCoalescingOperator(boundTemp, leftConversion, rewrittenResultType); Debug.Assert(convertedLeft.HasErrors || convertedLeft.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); // (temp != null) ? MakeConversion(temp, LeftConversion) : RightOperand BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: nullCheck, rewrittenConsequence: convertedLeft, rewrittenAlternative: rewrittenRight, constantValueOpt: null, rewrittenType: rewrittenResultType, isRef: false); Debug.Assert(conditionalExpression.ConstantValue == null); // we shouldn't have hit this else case otherwise Debug.Assert(conditionalExpression.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); return(new BoundSequence( syntax: syntax, locals: ImmutableArray.Create(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), value: conditionalExpression, type: rewrittenResultType)); }
private BoundExpression MakeEventAccess( SyntaxNode syntax, BoundExpression rewrittenReceiver, EventSymbol eventSymbol, ConstantValue constantValueOpt, LookupResultKind resultKind, TypeSymbol type) { Debug.Assert(eventSymbol.HasAssociatedField); FieldSymbol fieldSymbol = eventSymbol.AssociatedField; Debug.Assert((object)fieldSymbol != null); if (!eventSymbol.IsWindowsRuntimeEvent) { return(MakeFieldAccess(syntax, rewrittenReceiver, fieldSymbol, constantValueOpt, resultKind, type)); } NamedTypeSymbol fieldType = (NamedTypeSymbol)fieldSymbol.Type.TypeSymbol; Debug.Assert(fieldType.Name == "EventRegistrationTokenTable"); // _tokenTable BoundFieldAccess fieldAccess = new BoundFieldAccess( syntax, fieldSymbol.IsStatic ? null : rewrittenReceiver, fieldSymbol, constantValueOpt: null) { WasCompilerGenerated = true }; BoundExpression getOrCreateCall; MethodSymbol getOrCreateMethod; if (TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationTokenTable_T__GetOrCreateEventRegistrationTokenTable, out getOrCreateMethod)) { getOrCreateMethod = getOrCreateMethod.AsMember(fieldType); // EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable) getOrCreateCall = BoundCall.Synthesized( syntax, receiverOpt: null, method: getOrCreateMethod, arg0: fieldAccess); } else { getOrCreateCall = new BoundBadExpression(syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundExpression>(fieldAccess), ErrorTypeSymbol.UnknownResultType); } PropertySymbol invocationListProperty; if (TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationTokenTable_T__InvocationList, out invocationListProperty)) { MethodSymbol invocationListAccessor = invocationListProperty.GetMethod; if ((object)invocationListAccessor == null) { string accessorName = SourcePropertyAccessorSymbol.GetAccessorName(invocationListProperty.Name, getNotSet: true, isWinMdOutput: invocationListProperty.IsCompilationOutputWinMdObj()); _diagnostics.Add(new CSDiagnosticInfo(ErrorCode.ERR_MissingPredefinedMember, invocationListProperty.ContainingType, accessorName), syntax.Location); } else { invocationListAccessor = invocationListAccessor.AsMember(fieldType); return(_factory.Call(getOrCreateCall, invocationListAccessor)); } } return(new BoundBadExpression(syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create(getOrCreateCall), ErrorTypeSymbol.UnknownResultType)); }