private static BoundExpression InvokeGetMethod(MethodSymbol method, SyntaxNode syntax, string name) { var argument = new BoundLiteral( syntax, Microsoft.CodeAnalysis.ConstantValue.Create(name), method.Parameters[0].Type); return(BoundCall.Synthesized( syntax, receiverOpt: null, method: method, arguments: ImmutableArray.Create <BoundExpression>(argument))); }
private BoundNode RewriteDelegateOperation(BoundBinaryOperator node, SpecialMember member) { Debug.Assert(node != null); var method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(member); // UNDONE: Handle the bizarre error case where we don't have the expected methods. Debug.Assert(method != null); BoundExpression call = BoundCall.SynthesizedCall(null, method, node.Left, node.Right); BoundExpression result = method.ReturnType.SpecialType == SpecialType.System_Delegate ? BoundConversion.SynthesizedConversion(call, ConversionKind.ExplicitReference, node.Type) : call; return(Visit(result)); }
private static void RewriteLocalDeclaration( CSharpCompilation compilation, EENamedTypeSymbol container, HashSet <LocalSymbol> declaredLocals, ArrayBuilder <BoundStatement> statements, BoundLocalDeclaration node) { Debug.Assert(node.ArgumentsOpt.IsDefault); var local = node.LocalSymbol; var syntax = node.Syntax; declaredLocals.Add(local); var typeType = compilation.GetWellKnownType(WellKnownType.System_Type); var stringType = compilation.GetSpecialType(SpecialType.System_String); var guidConstructor = (MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Guid__ctor); // CreateVariable(Type type, string name) var method = PlaceholderLocalSymbol.GetIntrinsicMethod(compilation, ExpressionCompilerConstants.CreateVariableMethodName); var type = new BoundTypeOfOperator(syntax, new BoundTypeExpression(syntax, aliasOpt: null, type: local.Type), null, typeType); var name = new BoundLiteral(syntax, ConstantValue.Create(local.Name), stringType); bool hasCustomTypeInfoPayload; var customTypeInfoPayload = GetCustomTypeInfoPayload(local, syntax, compilation, out hasCustomTypeInfoPayload); var customTypeInfoPayloadId = GetCustomTypeInfoPayloadId(syntax, guidConstructor, hasCustomTypeInfoPayload); var call = BoundCall.Synthesized( syntax, receiverOpt: null, method: method, arguments: ImmutableArray.Create(type, name, customTypeInfoPayloadId, customTypeInfoPayload)); statements.Add(new BoundExpressionStatement(syntax, call)); var initializer = node.InitializerOpt; if (initializer != null) { // Generate assignment to local. The assignment will // be rewritten in PlaceholderLocalRewriter. var assignment = new BoundAssignmentOperator( syntax, new BoundLocal(syntax, local, constantValueOpt: null, type: local.Type), initializer, RefKind.None, local.Type); statements.Add(new BoundExpressionStatement(syntax, assignment)); } }
internal override BoundExpression RewriteLocal(CSharpCompilation compilation, EENamedTypeSymbol container, CSharpSyntaxNode syntax, DiagnosticBag diagnostics) { var method = GetIntrinsicMethod(compilation, ExpressionCompilerConstants.GetReturnValueMethodName); var argument = new BoundLiteral( syntax, Microsoft.CodeAnalysis.ConstantValue.Create(_index), method.Parameters[0].Type); var call = BoundCall.Synthesized( syntax, receiverOpt: null, method: method, arguments: ImmutableArray.Create <BoundExpression>(argument)); return(ConvertToLocalType(compilation, call, this.Type, diagnostics)); }
internal override BoundExpression RewriteLocal(CSharpCompilation compilation, EENamedTypeSymbol container, SyntaxNode syntax, DiagnosticBag diagnostics) { var method = GetIntrinsicMethod(compilation, ExpressionCompilerConstants.GetObjectAtAddressMethodName); var argument = new BoundLiteral( syntax, Microsoft.CodeAnalysis.ConstantValue.Create(_address), method.Parameters[0].Type.TypeSymbol); var call = BoundCall.Synthesized( syntax, receiverOpt: null, method: method, arguments: ImmutableArray.Create <BoundExpression>(argument)); Debug.Assert(call.Type == this.Type.TypeSymbol); return(call); }
private BoundNode RewriteStringEquality(BoundBinaryOperator node, SpecialMember member) { Debug.Assert(node != null); Debug.Assert(node.ConstantValueOpt == null); if (node.Left.ConstantValue == ConstantValue.Null || node.Right.ConstantValue == ConstantValue.Null) { return(base.VisitBinaryOperator(node)); } var method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(member); Debug.Assert(method != null); return(Visit(BoundCall.SynthesizedCall(null, method, node.Left, node.Right))); }
private BoundNode RewriteNumericToDecimalConversion(SyntaxNode syntaxNode, BoundExpression operand, TypeSymbol typeFrom) { SpecialMember member; switch (typeFrom.SpecialType) { case SpecialType.System_Char: member = SpecialMember.System_Decimal__op_Implicit_FromChar; break; case SpecialType.System_SByte: member = SpecialMember.System_Decimal__op_Implicit_FromSByte; break; case SpecialType.System_Byte: member = SpecialMember.System_Decimal__op_Implicit_FromByte; break; case SpecialType.System_Int16: member = SpecialMember.System_Decimal__op_Implicit_FromInt16; break; case SpecialType.System_UInt16: member = SpecialMember.System_Decimal__op_Implicit_FromUInt16; break; case SpecialType.System_Int32: member = SpecialMember.System_Decimal__op_Implicit_FromInt32; break; case SpecialType.System_UInt32: member = SpecialMember.System_Decimal__op_Implicit_FromUInt32; break; case SpecialType.System_Int64: member = SpecialMember.System_Decimal__op_Implicit_FromInt64; break; case SpecialType.System_UInt64: member = SpecialMember.System_Decimal__op_Implicit_FromUInt64; break; case SpecialType.System_Single: member = SpecialMember.System_Decimal__op_Explicit_FromSingle; break; case SpecialType.System_Double: member = SpecialMember.System_Decimal__op_Explicit_FromDouble; break; default: Debug.Assert(false); // Cannot reach here return(null); } // call the method var method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(member); Debug.Assert(method != null); // Should have been checked during Warnings pass return(BoundCall.Synthesized( syntaxNode, null, method, operand)); }
internal AsyncForwardEntryPoint(CSharpCompilation compilation, NamedTypeSymbol containingType, MethodSymbol userMain) : base(containingType) { // There should be no way for a userMain to be passed in unless it already passed the // parameter checks for determining entrypoint validity. Debug.Assert(userMain.ParameterCount == 0 || userMain.ParameterCount == 1); UserMain = userMain; _userMainReturnTypeSyntax = userMain.ExtractReturnTypeSyntax(); var binder = compilation.GetBinder(_userMainReturnTypeSyntax); _parameters = SynthesizedParameterSymbol.DeriveParameters(userMain, this); var arguments = Parameters.SelectAsArray((p, s) => (BoundExpression) new BoundParameter(s, p, p.Type), _userMainReturnTypeSyntax); // Main(args) or Main() BoundCall userMainInvocation = new BoundCall( syntax: _userMainReturnTypeSyntax, receiverOpt: null, method: userMain, arguments: arguments, argumentNamesOpt: default(ImmutableArray <string>), argumentRefKindsOpt: default(ImmutableArray <RefKind>), isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: default(ImmutableArray <int>), resultKind: LookupResultKind.Viable, binderOpt: binder, type: userMain.ReturnType) { WasCompilerGenerated = true }; // The diagnostics that would be produced here will already have been captured and returned. var droppedBag = DiagnosticBag.GetInstance(); var success = binder.GetAwaitableExpressionInfo(userMainInvocation, out _getAwaiterGetResultCall !, _userMainReturnTypeSyntax, droppedBag); droppedBag.Free(); Debug.Assert( ReturnType.IsVoidType() || ReturnType.SpecialType == SpecialType.System_Int32); }
internal override BoundExpression RewriteLocal(CSharpCompilation compilation, EENamedTypeSymbol container, CSharpSyntaxNode syntax) { Debug.Assert(this.Name == this.Name.ToLowerInvariant()); var method = container.GetOrAddSynthesizedMethod( this.Name, (c, n, s) => { var returnType = compilation.GetWellKnownType(WellKnownType.System_Exception); return(new PlaceholderMethodSymbol( c, s, n, returnType, m => ImmutableArray <ParameterSymbol> .Empty)); }); var call = BoundCall.Synthesized(syntax, receiverOpt: null, method: method); return(ConvertToLocalType(compilation, call, this.Type)); }
public override BoundNode VisitPropertyAccess(BoundPropertyAccess node) { // Avoid rewriting if node has errors since the accessor may not exist. if (node.HasErrors) { return(base.VisitPropertyAccess(node)); } // Rewrite property access into call to getter. var property = node.PropertySymbol.GetBaseProperty(); var getMethod = property.GetMethod; Debug.Assert(getMethod != null); Debug.Assert(getMethod.Parameters.Count == 0); Debug.Assert(!getMethod.IsOverride); var rewrittenReceiver = (BoundExpression)Visit(node.ReceiverOpt); return(BoundCall.SynthesizedCall(rewrittenReceiver, getMethod)); }
/// <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 static BoundExpression SynthesizeCall(SyntaxNode syntax, BoundExpression receiver, MethodSymbol method, Conversion receiverConversion, TypeSymbol convertedReceiverType) { if (receiver.Type.TypeKind == TypeKind.Struct && method.ContainingType.TypeKind == TypeKind.Interface) { Debug.Assert(receiverConversion.IsBoxing); // 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() return(BoundCall.Synthesized( syntax: syntax, receiverOpt: SynthesizeConversion( syntax: syntax, operand: receiver, conversion: receiverConversion, type: convertedReceiverType), method: method)); } }
public override BoundNode VisitCall(BoundCall node) { var rewrittenMethodSymbol = VisitMethodSymbol(node.Method); var rewrittenReceiver = (BoundExpression)this.Visit(node.ReceiverOpt); var rewrittenArguments = (ImmutableArray <BoundExpression>) this.VisitList(node.Arguments); var rewrittenType = this.VisitType(node.Type); Debug.Assert( rewrittenMethodSymbol.IsMetadataVirtual() == node.Method.IsMetadataVirtual() ); // If the original receiver was a base access and it was rewritten, // change the method to point to the wrapper method if ( BaseReferenceInReceiverWasRewritten(node.ReceiverOpt, rewrittenReceiver) && node.Method.IsMetadataVirtual() ) { rewrittenMethodSymbol = GetMethodWrapperForBaseNonVirtualCall( rewrittenMethodSymbol, node.Syntax ); } return(node.Update( rewrittenReceiver, rewrittenMethodSymbol, rewrittenArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.DefaultArguments, node.ResultKind, rewrittenType )); }
internal void Parse(BoundCall boundCall) { base.Parse(boundCall); this.Method = boundCall.Method; if (boundCall.ReceiverOpt != null && boundCall.ReceiverOpt.Kind != BoundKind.ConditionalReceiver) { this.ReceiverOpt = Deserialize(boundCall.ReceiverOpt) as Expression; // special case to avoid calling (*xxx).method() and replace with xxx->method(); var pointerIndirectionOperator = this.ReceiverOpt as PointerIndirectionOperator; if (pointerIndirectionOperator != null) { this.ReceiverOpt = pointerIndirectionOperator.Operand; this.ReceiverOpt.IsReference = true; } } foreach (var expression in boundCall.Arguments) { var argument = Deserialize(expression) as Expression; Debug.Assert(argument != null); this._arguments.Add(argument); } }
public override object VisitCall(BoundCall node, object unused) { // If the method being called is a partial method without a definition, or is a conditional method // whose condition is not true, then the call has no effect and it is ignored for the purposes of // definite assignment analysis. That means that subexpressions (including lambdas) are not even // analyzed. if (node.MethodSymbol.CallsAreOmitted(this.compilation)) { return(null); } VisitExpression(node.Receiver); foreach (var arg in node.Arguments) { if (arg.Kind == BoundKind.MakeRefOperator) { Unimplemented(arg, "ref argument"); // TODO: if it is an "out" reference, bind as an lvalue } VisitExpression(arg); } return(null); }
internal void Parse(BoundCall boundCall) { base.Parse(boundCall); this.SpecialCaseCreateInstanceNewObjectReplacement = boundCall.Method.IsCreateInstaneNewReplacement(); this.Method = this.SpecialCaseCreateInstanceNewObjectReplacement ? GenerateNativeCreateInstanceMethod(boundCall.Method) : boundCall.Method; if (boundCall.ReceiverOpt != null) { this.ReceiverOpt = Deserialize(boundCall.ReceiverOpt) as Expression; // special case to avoid calling (*xxx).method() and replace with xxx->method(); var pointerIndirectionOperator = this.ReceiverOpt as PointerIndirectionOperator; if (pointerIndirectionOperator != null) { this.ReceiverOpt = pointerIndirectionOperator.Operand; this.ReceiverOpt.IsReference = true; } } foreach (var expression in boundCall.Arguments) { var argument = Deserialize(expression) as Expression; Debug.Assert(argument != null); this._arguments.Add(argument); } }
private static void CreateLocal(CSharpCompilation compilation, HashSet <LocalSymbol> declaredLocals, ArrayBuilder <BoundStatement> statements, LocalSymbol local, SyntaxNode syntax) { declaredLocals.Add(local); var typeType = compilation.GetWellKnownType(WellKnownType.System_Type); var stringType = compilation.GetSpecialType(SpecialType.System_String); var guidConstructor = (MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Guid__ctor); // CreateVariable(Type type, string name) var method = PlaceholderLocalSymbol.GetIntrinsicMethod(compilation, ExpressionCompilerConstants.CreateVariableMethodName); var type = new BoundTypeOfOperator(syntax, new BoundTypeExpression(syntax, aliasOpt: null, type: local.Type), null, typeType); var name = new BoundLiteral(syntax, ConstantValue.Create(local.Name), stringType); bool hasCustomTypeInfoPayload; var customTypeInfoPayload = GetCustomTypeInfoPayload(local, syntax, compilation, out hasCustomTypeInfoPayload); var customTypeInfoPayloadId = GetCustomTypeInfoPayloadId(syntax, guidConstructor, hasCustomTypeInfoPayload); var call = BoundCall.Synthesized( syntax, receiverOpt: null, method: method, arguments: ImmutableArray.Create(type, name, customTypeInfoPayloadId, customTypeInfoPayload)); statements.Add(new BoundExpressionStatement(syntax, call)); }
// private static T <Factory>(object[] submissionArray) // { // var submission = new Submission#N(submissionArray); // return submission.<Initialize>(); // } internal override BoundBlock CreateBody() { var syntax = this.GetSyntax(); var ctor = _containingType.GetScriptConstructor(); Debug.Assert(ctor.ParameterCount == 1); var initializer = _containingType.GetScriptInitializer(); Debug.Assert(initializer.ParameterCount == 0); var submissionArrayParameter = new BoundParameter(syntax, _parameters[0]) { WasCompilerGenerated = true }; var submissionLocal = new BoundLocal( syntax, new SynthesizedLocal(this, _containingType, SynthesizedLocalKind.LoweringTemp), null, _containingType) { WasCompilerGenerated = true }; // var submission = new Submission#N(submissionArray); var submissionAssignment = new BoundExpressionStatement( syntax, new BoundAssignmentOperator( syntax, submissionLocal, new BoundObjectCreationExpression( syntax, ctor, ImmutableArray.Create <BoundExpression>(submissionArrayParameter), default(ImmutableArray <string>), default(ImmutableArray <RefKind>), false, default(ImmutableArray <int>), null, null, _containingType) { WasCompilerGenerated = true }, _containingType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // return submission.<Initialize>(); var initializeResult = new BoundCall( syntax, submissionLocal, initializer, ImmutableArray <BoundExpression> .Empty, default(ImmutableArray <string>), default(ImmutableArray <RefKind>), isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: default(ImmutableArray <int>), resultKind: LookupResultKind.Viable, type: initializer.ReturnType) { WasCompilerGenerated = true }; Debug.Assert(initializeResult.Type == _returnType); var returnStatement = new BoundReturnStatement( syntax, initializeResult) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, ImmutableArray.Create <LocalSymbol>(submissionLocal.LocalSymbol), ImmutableArray.Create <BoundStatement>(submissionAssignment, returnStatement)) { WasCompilerGenerated = true }); }
public override BoundNode VisitCall(BoundCall node) { var receiver = node.ReceiverOpt; // matches or a bit stronger than EmitReceiverRef // if there are any doubts that receiver is a ref type, // assume we will need an address (that will prevent scheduling of receiver). if (!node.Method.IsStatic) { receiver = VisitCallReceiver(receiver); } else { // TODO: for some reason receiver could be not null even if method is static... // it seems wrong, ignore for now. _counter += 1; receiver = null; } MethodSymbol method = node.Method; var rewrittenArguments = VisitArguments(node.Arguments, method.Parameters); return node.Update(receiver, method, rewrittenArguments); }
public override BoundNode VisitCall(BoundCall node) { var rewrittenMethodSymbol = VisitMethodSymbol(node.Method); var rewrittenReceiver = (BoundExpression)this.Visit(node.ReceiverOpt); var rewrittenArguments = (ImmutableArray<BoundExpression>)this.VisitList(node.Arguments); var rewrittenType = this.VisitType(node.Type); Debug.Assert(rewrittenMethodSymbol.IsMetadataVirtual() == node.Method.IsMetadataVirtual()); // If the original receiver was a base access and it was rewritten, // change the method to point to the wrapper method if (BaseReferenceInReceiverWasRewritten(node.ReceiverOpt, rewrittenReceiver) && node.Method.IsMetadataVirtual()) { rewrittenMethodSymbol = GetMethodWrapperForBaseNonVirtualCall(rewrittenMethodSymbol, node.Syntax); } return node.Update( rewrittenReceiver, rewrittenMethodSymbol, rewrittenArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.ResultKind, rewrittenType); }
// Calls are treated as having side effects, but properties and // indexers are not. (Since this visitor is run on the bound tree // before lowering, properties are not represented as calls.) public override BoundNode VisitCall(BoundCall node) { return this.SetMayHaveSideEffects(); }
// Calls are treated as having side effects, but properties and // indexers are not. (Since this visitor is run on the bound tree // before lowering, properties are not represented as calls.) public override BoundNode VisitCall(BoundCall node) { return(this.SetMayHaveSideEffects()); }
public override BoundNode VisitCall(BoundCall node) { Debug.Assert(node != null); // Avoid rewriting if node has errors since one or more // of the arguments may not be the correct rvalue/lvalue. if (node.HasErrors) { return node; } // Start by rewriting the arguments and receiver: var rewrittenReceiver = (BoundExpression)Visit(node.ReceiverOpt); var rewrittenArguments = VisitList(node.Arguments); var method = node.Method; // If the mapping from arguments to parameters is perfectly in order, no complex rewriting is needed. if (node.ArgsToParamsOpt.IsNull && !node.Expanded) { return node.Update(rewrittenReceiver, method, rewrittenArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.Type); } var argumentRefKinds = node.ArgumentRefKindsOpt; ReadOnlyArray<LocalSymbol> temps; RewriteArguments(method, node.Expanded, node.ArgsToParamsOpt, ref argumentRefKinds, ref rewrittenArguments, out temps); var delegateNode = node as BoundDelegateCall; if (temps.IsNullOrEmpty) { return (delegateNode != null) ? delegateNode.Update( rewrittenReceiver, method, rewrittenArguments, ReadOnlyArray<string>.Null, argumentRefKinds, false, ReadOnlyArray<int>.Null, node.Type) : node.Update( rewrittenReceiver, method, rewrittenArguments, ReadOnlyArray<string>.Null, argumentRefKinds, false, ReadOnlyArray<int>.Null, node.Type); } else { return new BoundSequence( null, null, temps, ReadOnlyArray<BoundExpression>.Empty, (delegateNode != null) ? new BoundDelegateCall( node.Syntax, node.SyntaxTree, rewrittenReceiver, method, rewrittenArguments, ReadOnlyArray<string>.Null, argumentRefKinds, false, ReadOnlyArray<int>.Null, node.Type) : new BoundCall( node.Syntax, node.SyntaxTree, rewrittenReceiver, method, rewrittenArguments, ReadOnlyArray<string>.Null, argumentRefKinds, false, ReadOnlyArray<int>.Null, node.Type), node.Type ); } }
public virtual void VisitCall(BoundCall node) { DefaultVisit(node); }
/// <summary> /// Lower a foreach loop that will enumerate a collection using an enumerator. /// /// E e = ((C)(x)).GetEnumerator() /// try { /// while (e.MoveNext()) { /// V v = (V)(T)e.Current; /// // body /// } /// } /// finally { /// // clean up e /// } /// </summary> private BoundStatement RewriteEnumeratorForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; ForEachEnumeratorInfo enumeratorInfo = node.EnumeratorInfoOpt; Debug.Assert(enumeratorInfo != null); BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); TypeSymbol enumeratorType = enumeratorInfo.GetEnumeratorMethod.ReturnType; TypeSymbol elementType = enumeratorInfo.ElementType; // E e LocalSymbol enumeratorVar = new TempLocalSymbol(enumeratorType, RefKind.None, this.containingMethod); // Reference to e. BoundLocal boundEnumeratorVar = MakeBoundLocal(forEachSyntax, enumeratorVar, enumeratorType); // ((C)(x)).GetEnumerator() or (x).GetEnumerator(); BoundExpression enumeratorVarInitValue = SynthesizeCall(forEachSyntax, rewrittenExpression, enumeratorInfo.GetEnumeratorMethod, enumeratorInfo.CollectionConversion, enumeratorInfo.CollectionType); // E e = ((C)(x)).GetEnumerator(); BoundStatement enumeratorVarDecl = MakeLocalDeclaration(forEachSyntax, enumeratorVar, enumeratorVarInitValue); AddForEachExpressionSequencePoint(forEachSyntax, ref enumeratorVarDecl); // V v LocalSymbol iterationVar = node.IterationVariable; //(V)(T)e.Current BoundExpression iterationVarAssignValue = SynthesizeConversion( syntax: forEachSyntax, operand: SynthesizeConversion( syntax: forEachSyntax, operand: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundEnumeratorVar, method: enumeratorInfo.CurrentPropertyGetter), conversion: enumeratorInfo.CurrentConversion, type: elementType), conversion: node.ElementConversion, type: iterationVar.Type); // V v = (V)(T)e.Current; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarAssignValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // while (e.MoveNext()) { // V v = (V)(T)e.Current; // /* node.Body */ // } BoundStatement whileLoop = RewriteWhileStatement( syntax: forEachSyntax, rewrittenCondition: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundEnumeratorVar, method: enumeratorInfo.MoveNextMethod), conditionSequencePointSpan: forEachSyntax.InKeyword.Span, rewrittenBody: new BoundBlock(rewrittenBody.Syntax, statements: ReadOnlyArray <BoundStatement> .CreateFrom(iterationVarDecl, rewrittenBody), localsOpt: ReadOnlyArray <LocalSymbol> .CreateFrom(iterationVar)), breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: false); BoundStatement result; if (enumeratorInfo.DisposeMethodOpt != null) { BoundBlock finallyBlockOpt; var idisposableTypeSymbol = enumeratorInfo.DisposeMethodOpt.ContainingType; var conversions = new TypeConversions(this.containingMethod.ContainingAssembly.CorLibrary); if (conversions.ClassifyImplicitConversion(enumeratorType, idisposableTypeSymbol).IsImplicit) { Debug.Assert(enumeratorInfo.DisposeMethodOpt != null); Conversion receiverConversion = enumeratorType.IsStructType() ? Conversion.Boxing : Conversion.ImplicitReference; // ((IDisposable)e).Dispose(); or e.Dispose(); BoundStatement disposeCall = new BoundExpressionStatement(forEachSyntax, expression: SynthesizeCall(forEachSyntax, boundEnumeratorVar, enumeratorInfo.DisposeMethodOpt, receiverConversion, idisposableTypeSymbol)); BoundStatement disposeStmt; if (enumeratorType.IsValueType) { // No way for the struct to be nullable and disposable. Debug.Assert(((TypeSymbol)enumeratorType.OriginalDefinition).SpecialType != SpecialType.System_Nullable_T); // For non-nullable structs, no null check is required. disposeStmt = disposeCall; } else { // NB: cast to object missing from spec. Needed to ignore user-defined operators and box type parameters. // if ((object)e != null) ((IDisposable)e).Dispose(); disposeStmt = RewriteIfStatement( syntax: forEachSyntax, rewrittenCondition: new BoundBinaryOperator(forEachSyntax, operatorKind: BinaryOperatorKind.NotEqual, left: SynthesizeConversion( syntax: forEachSyntax, operand: boundEnumeratorVar, conversion: enumeratorInfo.EnumeratorConversion, type: this.compilation.GetSpecialType(SpecialType.System_Object)), right: new BoundLiteral(forEachSyntax, constantValueOpt: ConstantValue.Null, type: null), constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: this.compilation.GetSpecialType(SpecialType.System_Boolean)), rewrittenConsequence: disposeCall, rewrittenAlternativeOpt: null, hasErrors: false); } finallyBlockOpt = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray <LocalSymbol> .Null, statements: ReadOnlyArray <BoundStatement> .CreateFrom(disposeStmt)); } else { Debug.Assert(!enumeratorType.IsSealed); // IDisposable d LocalSymbol disposableVar = new TempLocalSymbol(idisposableTypeSymbol, RefKind.None, this.containingMethod); // Reference to d. BoundLocal boundDisposableVar = MakeBoundLocal(forEachSyntax, disposableVar, idisposableTypeSymbol); BoundTypeExpression boundIDisposableTypeExpr = new BoundTypeExpression(forEachSyntax, type: idisposableTypeSymbol); // e as IDisposable BoundExpression disposableVarInitValue = new BoundAsOperator(forEachSyntax, operand: boundEnumeratorVar, targetType: boundIDisposableTypeExpr, conversion: Conversion.ExplicitReference, // Explicit so the emitter won't optimize it away. type: idisposableTypeSymbol); // IDisposable d = e as IDisposable; BoundStatement disposableVarDecl = MakeLocalDeclaration(forEachSyntax, disposableVar, disposableVarInitValue); // if (d != null) d.Dispose(); BoundStatement ifStmt = RewriteIfStatement( syntax: forEachSyntax, rewrittenCondition: new BoundBinaryOperator(forEachSyntax, operatorKind: BinaryOperatorKind.NotEqual, // reference equality left: boundDisposableVar, right: new BoundLiteral(forEachSyntax, constantValueOpt: ConstantValue.Null, type: null), constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: this.compilation.GetSpecialType(SpecialType.System_Boolean)), rewrittenConsequence: new BoundExpressionStatement(forEachSyntax, expression: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundDisposableVar, method: enumeratorInfo.DisposeMethodOpt)), rewrittenAlternativeOpt: null, hasErrors: false); // IDisposable d = e as IDisposable; // if (d != null) d.Dispose(); finallyBlockOpt = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray <LocalSymbol> .CreateFrom(disposableVar), statements: ReadOnlyArray <BoundStatement> .CreateFrom(disposableVarDecl, ifStmt)); } // try { // while (e.MoveNext()) { // V v = (V)(T)e.Current; // /* loop body */ // } // } // finally { // /* dispose of e */ // } BoundStatement tryFinally = new BoundTryStatement(forEachSyntax, tryBlock: new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray <LocalSymbol> .Empty, statements: ReadOnlyArray <BoundStatement> .CreateFrom(whileLoop)), catchBlocks: ReadOnlyArray <BoundCatchBlock> .Empty, finallyBlockOpt: finallyBlockOpt); // E e = ((C)(x)).GetEnumerator(); // try { // /* as above */ result = new BoundBlock( syntax: forEachSyntax, localsOpt: ReadOnlyArray <LocalSymbol> .CreateFrom(enumeratorVar), statements: ReadOnlyArray <BoundStatement> .CreateFrom(enumeratorVarDecl, tryFinally)); } else { // E e = ((C)(x)).GetEnumerator(); // while (e.MoveNext()) { // V v = (V)(T)e.Current; // /* loop body */ // } result = new BoundBlock( syntax: forEachSyntax, localsOpt: ReadOnlyArray <LocalSymbol> .CreateFrom(enumeratorVar), statements: ReadOnlyArray <BoundStatement> .CreateFrom(enumeratorVarDecl, whileLoop)); } AddForEachKeywordSequencePoint(forEachSyntax, ref result); return(result); }
private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; int rank = arrayType.Rank; Debug.Assert(rank > 1); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[...] a LocalSymbol arrayVar = new TempLocalSymbol(arrayType, RefKind.None, containingMethod); // A[...] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // Reference to a. BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // int p_0, p_1, ... LocalSymbol[] positionVar = new LocalSymbol[rank]; BoundLocal[] boundPositionVar = new BoundLocal[rank]; for (int dimension = 0; dimension < rank; dimension++) { positionVar[dimension] = new TempLocalSymbol(intType, RefKind.None, containingMethod); boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType); } // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p_0, p_1, ...] BoundExpression iterationVarInitValue = SynthesizeConversion( syntax: forEachSyntax, operand: new BoundArrayAccess(forEachSyntax, expression: boundArrayVar, indices: ReadOnlyArray <BoundExpression> .CreateFrom((BoundExpression[])boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, type: iterationVarType); // V v = (V)a[p_0, p_1, ...]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement innermostLoopBody = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray <LocalSymbol> .CreateFrom(iterationVar), statements: ReadOnlyArray <BoundStatement> .CreateFrom(iterationVarDecl, rewrittenBody)); // Values we'll use every iteration MethodSymbol getLowerBoundMethod = (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_Array__GetLowerBound); MethodSymbol getUpperBoundMethod = (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_Array__GetUpperBound); // work from most-nested to least-nested // for (A[...] a = /*node.Expression*/; int p_0 = a.GetLowerBound(0); p_0 <= a.GetUpperBound(0); p_0 = p_0 + 1) // for (int p_1 = a.GetLowerBound(0); p_1 <= a.GetUpperBound(0); p_1 = p_1 + 1) // ... // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement forLoop = null; for (int dimension = rank - 1; dimension >= 0; dimension--) { ReadOnlyArray <BoundExpression> dimensionArgument = ReadOnlyArray <BoundExpression> .CreateFrom( new BoundLiteral(forEachSyntax, constantValueOpt : ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type : intType)); // a.GetLowerBound(/*dimension*/) BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument); // a.GetUpperBound(/*dimension*/) //CONSIDER: dev10 creates a temp for each dimension's upper bound BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument); // int p_/*dimension*/ = a.GetLowerBound(/*dimension*/); BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound); ReadOnlyArray <LocalSymbol> locals; BoundStatement initializer; GeneratedLabelSymbol breakLabel; if (dimension == 0) { // outermost for-loop locals = ReadOnlyArray <LocalSymbol> .CreateFrom(arrayVar, positionVar[dimension]); initializer = new BoundStatementList(forEachSyntax, statements: ReadOnlyArray <BoundStatement> .CreateFrom(arrayVarDecl, positionVarDecl)); breakLabel = node.BreakLabel; // i.e. the one that break statements will jump to } else { locals = ReadOnlyArray <LocalSymbol> .CreateFrom(positionVar[dimension]); initializer = positionVarDecl; breakLabel = new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused } // p_/*dimension*/ <= a.GetUpperBound(/*dimension*/) //NB: OrEqual BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThanOrEqual, left: boundPositionVar[dimension], right: currentDimensionUpperBound, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p_/*dimension*/ = p_/*dimension*/ + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType); BoundStatement body; GeneratedLabelSymbol continueLabel; if (forLoop == null) { // innermost for-loop body = innermostLoopBody; continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to } else { body = forLoop; continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused } forLoop = RewriteForStatement( node.Syntax, locals, initializer, exitCondition, forEachSyntax.InKeyword, positionIncrement, body, breakLabel, continueLabel, node.HasErrors); } Debug.Assert(forLoop != null); AddForEachExpressionSequencePoint(forEachSyntax, ref forLoop); return(forLoop); }
/// <summary> /// Lower a foreach loop that will enumerate the characters of a string. /// /// string s = x; /// for (int p = 0; p < s.Length; p = p + 1) { /// V v = (V)s.Chars[p]; /// // body /// } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring string's /// implementation of IEnumerable and just indexing into its characters. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteStringForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); TypeSymbol stringType = collectionExpression.Type; Debug.Assert(stringType.SpecialType == SpecialType.System_String); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // string s; LocalSymbol stringVar = new TempLocalSymbol(stringType, RefKind.None, containingMethod); // int p; LocalSymbol positionVar = new TempLocalSymbol(intType, RefKind.None, containingMethod); // Reference to s. BoundLocal boundStringVar = MakeBoundLocal(forEachSyntax, stringVar, stringType); // Reference to p. BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType); // string s = /*expr*/; BoundStatement stringVarDecl = MakeLocalDeclaration(forEachSyntax, stringVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref stringVarDecl); // int p = 0; BoundStatement positionVariableDecl = MakeLocalDeclaration(forEachSyntax, positionVar, new BoundLiteral(forEachSyntax, ConstantValue.ConstantValueZero.Int32, intType)); // string s = /*node.Expression*/; int p = 0; BoundStatement initializer = new BoundStatementList(forEachSyntax, statements: ReadOnlyArray <BoundStatement> .CreateFrom(stringVarDecl, positionVariableDecl)); BoundExpression stringLength = BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundStringVar, method: (MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_String__Length), arguments: ReadOnlyArray <BoundExpression> .Empty); // p < s.Length BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThan, left: boundPositionVar, right: stringLength, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p = p + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType); LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; Debug.Assert(node.ElementConversion.Exists); // (V)s.Chars[p] BoundExpression iterationVarInitValue = SynthesizeConversion( syntax: forEachSyntax, operand: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundStringVar, method: (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_String__Chars), arguments: ReadOnlyArray <BoundExpression> .CreateFrom(boundPositionVar)), conversion: node.ElementConversion, type: iterationVarType); // V v = (V)s.Chars[p]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)s.Chars[p]; /*node.Body*/ } BoundStatement loopBody = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray <LocalSymbol> .CreateFrom(iterationVar), statements: ReadOnlyArray <BoundStatement> .CreateFrom(iterationVarDecl, rewrittenBody)); // for (string s = /*node.Expression*/, int p = 0; p < s.Length; p = p + 1) { // V v = (V)s.Chars[p]; // /*node.Body*/ // } BoundStatement result = RewriteForStatement( syntax: forEachSyntax, locals: ReadOnlyArray <LocalSymbol> .CreateFrom(stringVar, positionVar), rewrittenInitializer: initializer, rewrittenCondition: exitCondition, conditionSyntax: forEachSyntax.InKeyword, rewrittenIncrement: positionIncrement, rewrittenBody: loopBody, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: node.HasErrors); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return(result); }
public override BoundNode VisitCall(BoundCall node) { var receiver = node.ReceiverOpt; // matches or a bit stronger than EmitReceiverRef // if there are any doubts that receiver is a ref type, // assume we will need an address (that will prevent scheduling of receiver). if (!node.Method.IsStatic) { var receiverType = receiver.Type; ExprContext context; if (receiverType.IsReferenceType) { if (receiverType.IsTypeParameter()) { // type param receiver that we statically know is a reference will be boxed context = ExprContext.Box; } else { // reference receivers will be used as values context = ExprContext.Value; } } else { // everything else will get an address taken context = ExprContext.Address; } receiver = VisitExpression(receiver, context); } else { // TODO: for some reason receiver could be not null even if method is static... // it seems wrong, ignore for now. this.counter += 1; receiver = null; } MethodSymbol method = node.Method; var rewrittenArguments = VisitArguments(node.Arguments, method.Parameters); return node.Update(receiver, method, rewrittenArguments); }
// private static T <Factory>(object[] submissionArray) // { // var submission = new Submission#N(submissionArray); // return submission.<Initialize>(); // } internal override BoundBlock CreateBody() { var syntax = this.GetSyntax(); var ctor = _containingType.GetScriptConstructor(); Debug.Assert(ctor.ParameterCount == 1); var initializer = _containingType.GetScriptInitializer(); Debug.Assert(initializer.ParameterCount == 0); var submissionArrayParameter = new BoundParameter(syntax, _parameters[0]) { WasCompilerGenerated = true }; var submissionLocal = new BoundLocal( syntax, new SynthesizedLocal(this, _containingType, SynthesizedLocalKind.LoweringTemp), null, _containingType) { WasCompilerGenerated = true }; // var submission = new Submission#N(submissionArray); var submissionAssignment = new BoundExpressionStatement( syntax, new BoundAssignmentOperator( syntax, submissionLocal, new BoundObjectCreationExpression( syntax, ctor, ImmutableArray.Create<BoundExpression>(submissionArrayParameter), default(ImmutableArray<string>), default(ImmutableArray<RefKind>), false, default(ImmutableArray<int>), null, null, _containingType) { WasCompilerGenerated = true }, _containingType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // return submission.<Initialize>(); BoundExpression initializeResult = new BoundCall( syntax, submissionLocal, initializer, ImmutableArray<BoundExpression>.Empty, default(ImmutableArray<string>), default(ImmutableArray<RefKind>), isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: default(ImmutableArray<int>), resultKind: LookupResultKind.Viable, type: initializer.ReturnType) { WasCompilerGenerated = true }; if (initializeResult.Type.IsStructType() && (_returnType.SpecialType == SpecialType.System_Object)) { initializeResult = new BoundConversion(syntax, initializeResult, Conversion.Boxing, false, true, ConstantValue.NotAvailable, _returnType) { WasCompilerGenerated = true }; } var returnStatement = new BoundReturnStatement( syntax, initializeResult) { WasCompilerGenerated = true }; return new BoundBlock(syntax, ImmutableArray.Create<LocalSymbol>(submissionLocal.LocalSymbol), ImmutableArray.Create<BoundStatement>(submissionAssignment, returnStatement)) { WasCompilerGenerated = true }; }
private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bool used) { // Avoid rewriting if node has errors since at least // one of the operands is invalid. if (node.HasErrors) { return(node); } var propertyAccessor = node.Left as BoundPropertyAccess; if (propertyAccessor == null) { return((BoundExpression)base.VisitAssignmentOperator(node)); } // Rewrite property assignment into call to setter. var property = propertyAccessor.PropertySymbol.GetBaseProperty(); var setMethod = property.SetMethod; Debug.Assert(setMethod != null); Debug.Assert(setMethod.Parameters.Count == 1); Debug.Assert(!setMethod.IsOverride); var rewrittenReceiver = (BoundExpression)Visit(propertyAccessor.ReceiverOpt); var rewrittenArgument = (BoundExpression)Visit(node.Right); 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. var exprType = rewrittenArgument.Type; var tempSymbol = new TempLocalSymbol(exprType, RefKind.None, containingSymbol); var tempLocal = new BoundLocal(null, null, tempSymbol, null, exprType); var saveTemp = new BoundAssignmentOperator( null, null, tempLocal, rewrittenArgument, exprType); var call = BoundCall.SynthesizedCall( rewrittenReceiver, setMethod, saveTemp); return(new BoundSequence( node.Syntax, node.SyntaxTree, ReadOnlyArray <LocalSymbol> .CreateFrom(tempSymbol), ReadOnlyArray <BoundExpression> .CreateFrom(call), tempLocal, exprType)); } else { return(BoundCall.SynthesizedCall( rewrittenReceiver, setMethod, rewrittenArgument)); } }
public override BoundNode VisitCall(BoundCall node) { Debug.Assert(node != null); // Avoid rewriting if node has errors since one or more // of the arguments may not be the correct rvalue/lvalue. if (node.HasErrors) { return(node); } // Start by rewriting the arguments and receiver: var rewrittenReceiver = (BoundExpression)Visit(node.ReceiverOpt); var rewrittenArguments = VisitList(node.Arguments); var method = node.Method; // If the mapping from arguments to parameters is perfectly in order, no complex rewriting is needed. if (node.ArgsToParamsOpt.IsNull && !node.Expanded) { return(node.Update(rewrittenReceiver, method, rewrittenArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.Type)); } var argumentRefKinds = node.ArgumentRefKindsOpt; ReadOnlyArray <LocalSymbol> temps; RewriteArguments(method, node.Expanded, node.ArgsToParamsOpt, ref argumentRefKinds, ref rewrittenArguments, out temps); var delegateNode = node as BoundDelegateCall; if (temps.IsNullOrEmpty) { return((delegateNode != null) ? delegateNode.Update( rewrittenReceiver, method, rewrittenArguments, ReadOnlyArray <string> .Null, argumentRefKinds, false, ReadOnlyArray <int> .Null, node.Type) : node.Update( rewrittenReceiver, method, rewrittenArguments, ReadOnlyArray <string> .Null, argumentRefKinds, false, ReadOnlyArray <int> .Null, node.Type)); } else { return(new BoundSequence( null, null, temps, ReadOnlyArray <BoundExpression> .Empty, (delegateNode != null) ? new BoundDelegateCall( node.Syntax, node.SyntaxTree, rewrittenReceiver, method, rewrittenArguments, ReadOnlyArray <string> .Null, argumentRefKinds, false, ReadOnlyArray <int> .Null, node.Type) : new BoundCall( node.Syntax, node.SyntaxTree, rewrittenReceiver, method, rewrittenArguments, ReadOnlyArray <string> .Null, argumentRefKinds, false, ReadOnlyArray <int> .Null, node.Type), node.Type )); } }
private void EmitCallExpression(BoundCall call, UseKind useKind) { var method = call.Method; var receiver = call.ReceiverOpt; LocalDefinition tempOpt = null; // Calls to the default struct constructor are emitted as initobj, rather than call. // NOTE: constructor invocations are represented as BoundObjectCreationExpressions, // rather than BoundCalls. This is why we can be confident that if we see a call to a // constructor, it has this very specific form. if (method.IsDefaultValueTypeConstructor()) { Debug.Assert(method.IsImplicitlyDeclared); Debug.Assert(method.ContainingType == receiver.Type); Debug.Assert(receiver.Kind == BoundKind.ThisReference); tempOpt = EmitReceiverRef(receiver); _builder.EmitOpCode(ILOpCode.Initobj); // initobj <MyStruct> EmitSymbolToken(method.ContainingType, call.Syntax); FreeOptTemp(tempOpt); return; } var arguments = call.Arguments; CallKind callKind; if (method.IsStatic) { callKind = CallKind.Call; } else { var receiverType = receiver.Type; if (receiverType.IsVerifierReference()) { tempOpt = EmitReceiverRef(receiver, isAccessConstrained: false); // In some cases CanUseCallOnRefTypeReceiver returns true which means that // null check is unnecessary and we can use "call" if (receiver.SuppressVirtualCalls || (!method.IsMetadataVirtual() && CanUseCallOnRefTypeReceiver(receiver))) { callKind = CallKind.Call; } else { callKind = CallKind.CallVirt; } } else if (receiverType.IsVerifierValue()) { NamedTypeSymbol methodContainingType = method.ContainingType; if (methodContainingType.IsVerifierValue() && MayUseCallForStructMethod(method)) { // NOTE: this should be either a method which overrides some abstract method or // does not override anything (with few exceptions, see MayUseCallForStructMethod); // otherwise we should not use direct 'call' and must use constrained call; // calling a method defined in a value type Debug.Assert(receiverType == methodContainingType); tempOpt = EmitReceiverRef(receiver); callKind = CallKind.Call; } else { if (method.IsMetadataVirtual()) { // When calling a method that is virtual in metadata on a struct receiver, // we use a constrained virtual call. If possible, it will skip boxing. tempOpt = EmitReceiverRef(receiver, isAccessConstrained: true); callKind = CallKind.ConstrainedCallVirt; } else { // calling a method defined in a base class. EmitExpression(receiver, used: true); EmitBox(receiverType, receiver.Syntax); callKind = CallKind.Call; } } } else { // receiver is generic and method must come from the base or an interface or a generic constraint // if the receiver is actually a value type it would need to be boxed. // let .constrained sort this out. callKind = receiverType.IsReferenceType && !IsRef(receiver) ? CallKind.CallVirt : CallKind.ConstrainedCallVirt; tempOpt = EmitReceiverRef(receiver, isAccessConstrained: callKind == CallKind.ConstrainedCallVirt); } } // When emitting a callvirt to a virtual method we always emit the method info of the // method that first declared the virtual method, not the method info of an // overriding method. It would be a subtle breaking change to change that rule; // see bug 6156 for details. MethodSymbol actualMethodTargetedByTheCall = method; if (method.IsOverride && callKind != CallKind.Call) { actualMethodTargetedByTheCall = method.GetConstructedLeastOverriddenMethod(_method.ContainingType); } if (callKind == CallKind.ConstrainedCallVirt && actualMethodTargetedByTheCall.ContainingType.IsValueType) { // special case for overridden methods like ToString(...) called on // value types: if the original method used in emit cannot use callvirt in this // case, change it to Call. callKind = CallKind.Call; } // Devirtualizing of calls to effectively sealed methods. if (callKind == CallKind.CallVirt) { // NOTE: we check that we call method in same module just to be sure // that it cannot be recompiled as not final and make our call not verifiable. // such change by adversarial user would arguably be a compat break, but better be safe... // In reality we would typically have one method calling another method in the same class (one GetEnumerator calling another). // Other scenarios are uncommon since base class cannot be sealed and // referring to a derived type in a different module is not an easy thing to do. if (IsThisReceiver(receiver) && actualMethodTargetedByTheCall.ContainingType.IsSealed && (object)actualMethodTargetedByTheCall.ContainingModule == (object)_method.ContainingModule) { // special case for target is in a sealed class and "this" receiver. Debug.Assert(receiver.Type.IsVerifierReference()); callKind = CallKind.Call; } // NOTE: we do not check that we call method in same module. // Because of the "GetOriginalConstructedOverriddenMethod" above, the actual target // can only be final when it is "newslot virtual final". // In such case Dev11 emits "call" and we will just replicate the behavior. (see DevDiv: 546853 ) else if (actualMethodTargetedByTheCall.IsMetadataFinal && CanUseCallOnRefTypeReceiver(receiver)) { // special case for calling 'final' virtual method on reference receiver Debug.Assert(receiver.Type.IsVerifierReference()); callKind = CallKind.Call; } } EmitArguments(arguments, method.Parameters); int stackBehavior = GetCallStackBehavior(call); switch (callKind) { case CallKind.Call: _builder.EmitOpCode(ILOpCode.Call, stackBehavior); break; case CallKind.CallVirt: _builder.EmitOpCode(ILOpCode.Callvirt, stackBehavior); break; case CallKind.ConstrainedCallVirt: _builder.EmitOpCode(ILOpCode.Constrained); EmitSymbolToken(receiver.Type, receiver.Syntax); _builder.EmitOpCode(ILOpCode.Callvirt, stackBehavior); break; } EmitSymbolToken(actualMethodTargetedByTheCall, call.Syntax, actualMethodTargetedByTheCall.IsVararg ? (BoundArgListOperator)call.Arguments[call.Arguments.Length - 1] : null); if (!method.ReturnsVoid) { EmitPopIfUnused(useKind != UseKind.Unused); } else if (_ilEmitStyle == ILEmitStyle.Debug) { // The only void methods with usable return values are constructors and we represent those // as BoundObjectCreationExpressions, not BoundCalls. Debug.Assert(useKind == UseKind.Unused, "Using the return value of a void method."); Debug.Assert(_method.GenerateDebugInfo, "Implied by this.emitSequencePoints"); // DevDiv #15135. When a method like System.Diagnostics.Debugger.Break() is called, the // debugger sees an event indicating that a user break (vs a breakpoint) has occurred. // When this happens, it uses ICorDebugILFrame.GetIP(out uint, out CorDebugMappingResult) // to determine the current instruction pointer. This method returns the instruction // *after* the call. The source location is then given by the last sequence point before // or on this instruction. As a result, if the instruction after the call has its own // sequence point, then that sequence point will be used to determine the source location // and the debugging experience will be disrupted. The easiest way to ensure that the next // instruction does not have a sequence point is to insert a nop. Obviously, we only do this // if debugging is enabled and optimization is disabled. // From ILGENREC::genCall: // We want to generate a NOP after CALL opcodes that end a statement so the debugger // has better stepping behavior // CONSIDER: In the native compiler, there's an additional restriction on when this nop is // inserted. It is quite complicated, but it basically seems to say that, if we thought // we could omit the temp-and-copy for a struct construction and it turned out that we // couldn't (perhaps because the assigned local was captured by a lambda), and if we're // not using the result of the constructor call (how can this even happen?), then we don't // want to insert the nop. Since the consequence of not implementing this complicated logic // is an extra nop in debug code, this is likely not a priority. // CONSIDER: The native compiler also checks !(tree->flags & EXF_NODEBUGINFO). We don't have // this mutable bit on our bound nodes, so we can't exactly match the behavior. We might be // able to approximate the native behavior by inspecting call.WasCompilerGenerated, but it is // not in a reliable state after lowering. _builder.EmitOpCode(ILOpCode.Nop); } if (useKind == UseKind.UsedAsValue && method.RefKind != RefKind.None) { EmitLoadIndirect(method.ReturnType, call.Syntax); } else if (useKind == UseKind.UsedAsAddress) { Debug.Assert(method.RefKind != RefKind.None); } FreeOptTemp(tempOpt); }
private static int GetCallStackBehavior(BoundCall call) { int stack = 0; if (!call.Method.ReturnsVoid) { // The call puts the return value on the stack. stack += 1; } if (!call.Method.IsStatic) { // The call pops the receiver off the stack. stack -= 1; } if (call.Method.IsVararg) { // The call pops all the arguments, fixed and variadic. int fixedArgCount = call.Arguments.Length - 1; int varArgCount = ((BoundArgListOperator)call.Arguments[fixedArgCount]).Arguments.Length; stack -= fixedArgCount; stack -= varArgCount; } else { // The call pops all the arguments. stack -= call.Arguments.Length; } return stack; }
private BoundValueType EmitCall(BoundCall node) { // Emit the arguments to the call. EmitExpression(node.Method); IL.Emit(OpCodes.Castclass, typeof(JsObject)); _scope.EmitLoad(SpecialLocal.Runtime); EmitBox(EmitExpression(node.Target)); return EmitMethodArgumentsAndGenerics( _objectExecute, node.Arguments, node.Generics ); }