/// <summary> /// Lower a block of code by performing local rewritings. /// </summary> public static BoundStatement Rewrite( CSharpCompilation compilation, MethodSymbol method, int methodOrdinal, NamedTypeSymbol containingType, BoundStatement statement, TypeCompilationState compilationState, SynthesizedSubmissionFields previousSubmissionFields, bool allowOmissionOfConditionalCalls, DiagnosticBag diagnostics, out bool sawLambdas, out bool sawAwaitInExceptionHandler) { Debug.Assert(statement != null); Debug.Assert(compilationState != null); try { var factory = new SyntheticBoundNodeFactory(method, statement.Syntax, compilationState, diagnostics); var localRewriter = new LocalRewriter(compilation, method, methodOrdinal, containingType, factory, previousSubmissionFields, allowOmissionOfConditionalCalls, diagnostics); var loweredStatement = (BoundStatement)localRewriter.Visit(statement); sawLambdas = localRewriter.sawLambdas; sawAwaitInExceptionHandler = localRewriter.sawAwaitInExceptionHandler; var block = loweredStatement as BoundBlock; var result = (block == null) ? loweredStatement : InsertPrologueSequencePoint(block, method); return(result); } catch (SyntheticBoundNodeFactory.MissingPredefinedMember ex) { diagnostics.Add(ex.Diagnostic); sawLambdas = sawAwaitInExceptionHandler = false; return(new BoundBadStatement(statement.Syntax, ImmutableArray.Create <BoundNode>(statement), hasErrors: true)); } }
private LocalRewriter( CSharpCompilation compilation, MethodSymbol containingMethod, int containingMethodOrdinal, BoundStatement rootStatement, NamedTypeSymbol containingType, SyntheticBoundNodeFactory factory, SynthesizedSubmissionFields previousSubmissionFields, bool allowOmissionOfConditionalCalls, DiagnosticBag diagnostics, Instrumenter instrumenter) { _compilation = compilation; _factory = factory; _factory.CurrentMethod = containingMethod; Debug.Assert(factory.CurrentType == (containingType ?? containingMethod.ContainingType)); _dynamicFactory = new LoweredDynamicOperationFactory(factory, containingMethodOrdinal); _previousSubmissionFields = previousSubmissionFields; _allowOmissionOfConditionalCalls = allowOmissionOfConditionalCalls; _diagnostics = diagnostics; Debug.Assert(instrumenter != null); _instrumenter = instrumenter; _rootStatement = rootStatement; }
private LocalRewriter( CSharpCompilation compilation, MethodSymbol containingMethod, int containingMethodOrdinal, BoundStatement rootStatement, NamedTypeSymbol?containingType, SyntheticBoundNodeFactory factory, SynthesizedSubmissionFields previousSubmissionFields, bool allowOmissionOfConditionalCalls, DiagnosticBag diagnostics, Instrumenter instrumenter) { _compilation = compilation; _factory = factory; _factory.CurrentFunction = containingMethod; Debug.Assert(TypeSymbol.Equals(factory.CurrentType, (containingType ?? containingMethod.ContainingType), TypeCompareKind.ConsiderEverything2)); _dynamicFactory = new LoweredDynamicOperationFactory(factory, containingMethodOrdinal); _previousSubmissionFields = previousSubmissionFields; _allowOmissionOfConditionalCalls = allowOmissionOfConditionalCalls; _diagnostics = diagnostics; Debug.Assert(instrumenter != null); #if DEBUG // Ensure that only expected kinds of instrumenters are in use _ = RemoveDynamicAnalysisInjectors(instrumenter); #endif _instrumenter = instrumenter; _rootStatement = rootStatement; }
/// <summary> /// Lower a block of code by performing local rewritings. /// </summary> public static BoundStatement Rewrite( bool generateDebugInfo, MethodSymbol containingSymbol, NamedTypeSymbol containingType, BoundStatement statement, TypeCompilationState compilationState, DiagnosticBag diagnostics, SynthesizedSubmissionFields previousSubmissionFields, out bool sawLambdas, out bool sawDynamicOperations, out bool sawAwaitInExceptionHandler) { Debug.Assert(statement != null); Debug.Assert(compilationState != null); try { var compilation = containingType.DeclaringCompilation; var factory = new SyntheticBoundNodeFactory(containingSymbol, statement.Syntax, compilationState, diagnostics); var localRewriter = new LocalRewriter(generateDebugInfo, containingSymbol, containingType, factory, previousSubmissionFields, compilation, diagnostics); var loweredStatement = (BoundStatement)localRewriter.Visit(statement); sawLambdas = localRewriter.sawLambdas; sawAwaitInExceptionHandler = localRewriter.sawAwaitInExceptionHandler; sawDynamicOperations = localRewriter.dynamicFactory.GeneratedDynamicOperations; var block = loweredStatement as BoundBlock; var result = (block == null) ? loweredStatement : InsertPrologueSequencePoint(block, containingSymbol); return(result); } catch (SyntheticBoundNodeFactory.MissingPredefinedMember ex) { diagnostics.Add(ex.Diagnostic); sawLambdas = sawDynamicOperations = sawAwaitInExceptionHandler = false; return(new BoundBadStatement(statement.Syntax, ImmutableArray.Create <BoundNode>(statement), hasErrors: true)); } }
internal static ImmutableArray <BoundStatement> ConstructScriptConstructorBody( BoundStatement loweredBody, MethodSymbol constructor, SynthesizedSubmissionFields previousSubmissionFields, CSharpCompilation compilation) { // Script field initializers have to be emitted after the call to the base constructor because they can refer to "this" instance. // // Unlike regular field initializers, initializers of global script variables can access "this" instance. // If the base class had a constructor that initializes its state a global variable would access partially initialized object. // For this reason Script class must always derive directly from a class that has no state (System.Object). SyntaxNode syntax = loweredBody.Syntax; // base constructor call: Debug.Assert((object)constructor.ContainingType.BaseTypeNoUseSiteDiagnostics == null || constructor.ContainingType.BaseTypeNoUseSiteDiagnostics.SpecialType == SpecialType.System_Object); var objectType = constructor.ContainingAssembly.GetSpecialType(SpecialType.System_Object); BoundExpression receiver = new BoundThisReference(syntax, constructor.ContainingType) { WasCompilerGenerated = true }; BoundStatement baseConstructorCall = new BoundExpressionStatement(syntax, new BoundCall(syntax, receiverOpt: receiver, method: objectType.InstanceConstructors[0], arguments: ImmutableArray <BoundExpression> .Empty, argumentNamesOpt: ImmutableArray <string> .Empty, argumentRefKindsOpt: ImmutableArray <RefKind> .Empty, isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: ImmutableArray <int> .Empty, defaultArguments: BitVector.Empty, resultKind: LookupResultKind.Viable, binderOpt: null, type: objectType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; var statements = ArrayBuilder <BoundStatement> .GetInstance(); statements.Add(baseConstructorCall); if (constructor.IsSubmissionConstructor) { // submission initialization: MakeSubmissionInitialization(statements, syntax, constructor, previousSubmissionFields, compilation); } statements.Add(loweredBody); return(statements.ToImmutableAndFree()); }
/// <summary> /// Lower a block of code by performing local rewritings. /// </summary> public static BoundStatement Rewrite( CSharpCompilation compilation, MethodSymbol method, int methodOrdinal, NamedTypeSymbol containingType, BoundStatement statement, TypeCompilationState compilationState, SynthesizedSubmissionFields previousSubmissionFields, bool allowOmissionOfConditionalCalls, bool instrumentForDynamicAnalysis, ref ImmutableArray <SourceSpan> dynamicAnalysisSpans, DebugDocumentProvider debugDocumentProvider, DiagnosticBag diagnostics, out bool sawLambdas, out bool sawLocalFunctions, out bool sawAwaitInExceptionHandler, // @MattWindsor91 (Concept-C# 2017) // // Sending concept witnesses to hoist to method compiler. // TODO: need to work out better way to do this out SmallDictionary <TypeSymbol, LocalSymbol> conceptWitnessesToHoist) { Debug.Assert(statement != null); Debug.Assert(compilationState != null); try { var factory = new SyntheticBoundNodeFactory(method, statement.Syntax, compilationState, diagnostics); DynamicAnalysisInjector dynamicInstrumenter = instrumentForDynamicAnalysis ? DynamicAnalysisInjector.TryCreate(method, statement, factory, diagnostics, debugDocumentProvider, Instrumenter.NoOp) : null; // We don’t want IL to differ based upon whether we write the PDB to a file/stream or not. // Presence of sequence points in the tree affects final IL, therefore, we always generate them. var localRewriter = new LocalRewriter(compilation, method, methodOrdinal, statement, containingType, factory, previousSubmissionFields, allowOmissionOfConditionalCalls, diagnostics, dynamicInstrumenter != null ? new DebugInfoInjector(dynamicInstrumenter) : DebugInfoInjector.Singleton); var loweredStatement = (BoundStatement)localRewriter.Visit(statement); sawLambdas = localRewriter._sawLambdas; sawLocalFunctions = localRewriter._sawLocalFunctions; sawAwaitInExceptionHandler = localRewriter._sawAwaitInExceptionHandler; if (dynamicInstrumenter != null) { dynamicAnalysisSpans = dynamicInstrumenter.DynamicAnalysisSpans; } conceptWitnessesToHoist = localRewriter._conceptWitnessesToHoist; // @MattWindsor91 (Concept-C# 2017) return(loweredStatement); } catch (SyntheticBoundNodeFactory.MissingPredefinedMember ex) { diagnostics.Add(ex.Diagnostic); sawLambdas = sawLocalFunctions = sawAwaitInExceptionHandler = false; conceptWitnessesToHoist = null; // @MattWindsor91 (Concept-C# 2017) return(new BoundBadStatement(statement.Syntax, ImmutableArray.Create <BoundNode>(statement), hasErrors: true)); } }
/// <summary> /// Lower a block of code by performing local rewritings. /// </summary> public static BoundStatement Rewrite( CSharpCompilation compilation, MethodSymbol method, int methodOrdinal, NamedTypeSymbol containingType, BoundStatement statement, TypeCompilationState compilationState, SynthesizedSubmissionFields previousSubmissionFields, bool allowOmissionOfConditionalCalls, bool instrumentForDynamicAnalysis, ref ImmutableArray <SourceSpan> dynamicAnalysisSpans, DebugDocumentProvider debugDocumentProvider, BindingDiagnosticBag diagnostics, out bool sawLambdas, out bool sawLocalFunctions, out bool sawAwaitInExceptionHandler) { Debug.Assert(statement != null); Debug.Assert(compilationState != null); try { var factory = new SyntheticBoundNodeFactory(method, statement.Syntax, compilationState, diagnostics); DynamicAnalysisInjector?dynamicInstrumenter = instrumentForDynamicAnalysis ? DynamicAnalysisInjector.TryCreate(method, statement, factory, diagnostics, debugDocumentProvider, Instrumenter.NoOp) : null; // We don’t want IL to differ based upon whether we write the PDB to a file/stream or not. // Presence of sequence points in the tree affects final IL, therefore, we always generate them. var localRewriter = new LocalRewriter(compilation, method, methodOrdinal, statement, containingType, factory, previousSubmissionFields, allowOmissionOfConditionalCalls, diagnostics, dynamicInstrumenter != null ? new DebugInfoInjector(dynamicInstrumenter) : DebugInfoInjector.Singleton); statement.CheckLocalsDefined(); var loweredStatement = localRewriter.VisitStatement(statement); Debug.Assert(loweredStatement is { }); loweredStatement.CheckLocalsDefined(); sawLambdas = localRewriter._sawLambdas; sawLocalFunctions = localRewriter._availableLocalFunctionOrdinal != 0; sawAwaitInExceptionHandler = localRewriter._sawAwaitInExceptionHandler; if (localRewriter._needsSpilling && !loweredStatement.HasErrors) { // Move spill sequences to a top-level statement. This handles "lifting" await and the switch expression. var spilledStatement = SpillSequenceSpiller.Rewrite(loweredStatement, method, compilationState, diagnostics); spilledStatement.CheckLocalsDefined(); loweredStatement = spilledStatement; } if (dynamicInstrumenter != null) { dynamicAnalysisSpans = dynamicInstrumenter.DynamicAnalysisSpans; } #if DEBUG LocalRewritingValidator.Validate(loweredStatement); localRewriter.AssertNoPlaceholderReplacements(); #endif return(loweredStatement); }
private LocalRewriter(bool generateDebugInfo, MethodSymbol containingMethod, NamedTypeSymbol containingType, SyntheticBoundNodeFactory factory, SynthesizedSubmissionFields previousSubmissionFields, CSharpCompilation compilation, DiagnosticBag diagnostics) { this.generateDebugInfo = generateDebugInfo && containingMethod.GenerateDebugInfo; this.compilation = compilation; this.factory = factory; this.factory.CurrentMethod = containingMethod; Debug.Assert(factory.CurrentClass == (containingType ?? containingMethod.ContainingType)); this.dynamicFactory = new LoweredDynamicOperationFactory(factory); this.previousSubmissionFields = previousSubmissionFields; this.diagnostics = diagnostics; }
internal static ImmutableArray<BoundStatement> ConstructScriptConstructorBody( BoundStatement loweredBody, MethodSymbol constructor, SynthesizedSubmissionFields previousSubmissionFields, CSharpCompilation compilation) { // Script field initializers have to be emitted after the call to the base constructor because they can refer to "this" instance. // // Unlike regular field initializers, initializers of global script variables can access "this" instance. // If the base class had a constructor that initializes its state a global variable would access partially initialized object. // For this reason Script class must always derive directly from a class that has no state (System.Object). CSharpSyntaxNode syntax = loweredBody.Syntax; // base constructor call: Debug.Assert((object)constructor.ContainingType.BaseTypeNoUseSiteDiagnostics == null || constructor.ContainingType.BaseTypeNoUseSiteDiagnostics.SpecialType == SpecialType.System_Object); var objectType = constructor.ContainingAssembly.GetSpecialType(SpecialType.System_Object); BoundExpression receiver = new BoundThisReference(syntax, constructor.ContainingType) { WasCompilerGenerated = true }; BoundStatement baseConstructorCall = new BoundExpressionStatement(syntax, new BoundCall(syntax, receiverOpt: receiver, method: objectType.InstanceConstructors[0], arguments: ImmutableArray<BoundExpression>.Empty, argumentNamesOpt: ImmutableArray<string>.Empty, argumentRefKindsOpt: ImmutableArray<RefKind>.Empty, isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: ImmutableArray<int>.Empty, resultKind: LookupResultKind.Viable, type: objectType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; var statements = ArrayBuilder<BoundStatement>.GetInstance(); statements.Add(baseConstructorCall); if (constructor.IsSubmissionConstructor) { // submission initialization: MakeSubmissionInitialization(statements, syntax, constructor, previousSubmissionFields, compilation); } statements.Add(loweredBody); return statements.ToImmutableAndFree(); }
internal static BoundStatement LowerStatement( bool generateDebugInfo, MethodSymbol method, BoundStatement body, SynthesizedSubmissionFields previousSubmissionFields, TypeCompilationState compilationState, DiagnosticBag diagnostics) { return(LowerStatement( generateDebugInfo, method.ContainingType, method.ThisParameter, method, body, previousSubmissionFields, compilationState, diagnostics)); }
private LocalRewriter( CSharpCompilation compilation, MethodSymbol containingMethod, int containingMethodOrdinal, NamedTypeSymbol containingType, SyntheticBoundNodeFactory factory, SynthesizedSubmissionFields previousSubmissionFields, bool allowOmissionOfConditionalCalls, DiagnosticBag diagnostics) { this.compilation = compilation; this.factory = factory; this.factory.CurrentMethod = containingMethod; Debug.Assert(factory.CurrentType == (containingType ?? containingMethod.ContainingType)); this.dynamicFactory = new LoweredDynamicOperationFactory(factory, containingMethodOrdinal); this.previousSubmissionFields = previousSubmissionFields; this.allowOmissionOfConditionalCalls = allowOmissionOfConditionalCalls; this.diagnostics = diagnostics; }
/// <summary> /// Generates a submission initialization part of a Script type constructor that represents an interactive submission. /// </summary> /// <remarks> /// The constructor takes a parameter of type Roslyn.Scripting.Session - the session reference. /// It adds the object being constructed into the session by calling Microsoft.CSharp.RuntimeHelpers.SessionHelpers.SetSubmission, /// and retrieves strongly typed references on all previous submission script classes whose members are referenced by this submission. /// The references are stored to fields of the submission (<paramref name="synthesizedFields"/>). /// </remarks> private static ImmutableArray <BoundStatement> MakeSubmissionInitialization(CSharpSyntaxNode syntax, MethodSymbol submissionConstructor, SynthesizedSubmissionFields synthesizedFields, CSharpCompilation compilation) { Debug.Assert(submissionConstructor.ParameterCount == 2); var statements = new List <BoundStatement>(2 + synthesizedFields.Count); var submissionArrayReference = new BoundParameter(syntax, submissionConstructor.Parameters[0]) { WasCompilerGenerated = true }; var intType = compilation.GetSpecialType(SpecialType.System_Int32); var objectType = compilation.GetSpecialType(SpecialType.System_Object); var thisReference = new BoundThisReference(syntax, submissionConstructor.ContainingType) { WasCompilerGenerated = true }; var slotIndex = compilation.GetSubmissionSlotIndex(); Debug.Assert(slotIndex >= 0); // <submission_array>[<slot_index] = this; statements.Add(new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundArrayAccess(syntax, submissionArrayReference, ImmutableArray.Create <BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(slotIndex), intType) { WasCompilerGenerated = true }), objectType) { WasCompilerGenerated = true }, thisReference, RefKind.None, thisReference.Type) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }); var hostObjectField = synthesizedFields.GetHostObjectField(); if ((object)hostObjectField != null) { // <host_object> = (<host_object_type>)<submission_array>[0] statements.Add( new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, hostObjectField, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, new BoundArrayAccess(syntax, submissionArrayReference, ImmutableArray.Create <BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(0), intType) { WasCompilerGenerated = true }), objectType), Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, hostObjectField.Type ), hostObjectField.Type) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }); } foreach (var field in synthesizedFields.FieldSymbols) { var targetScriptType = (ImplicitNamedTypeSymbol)field.Type; var targetSubmissionIndex = targetScriptType.DeclaringCompilation.GetSubmissionSlotIndex(); Debug.Assert(targetSubmissionIndex >= 0); // this.<field> = (<target_script_type>)<submission_array>[<target_submission_index>]; statements.Add( new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, new BoundArrayAccess(syntax, submissionArrayReference, ImmutableArray.Create <BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(targetSubmissionIndex), intType) { WasCompilerGenerated = true }), objectType) { WasCompilerGenerated = true }, Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, targetScriptType ), targetScriptType ) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }); } return(statements.AsImmutableOrNull()); }
internal static BoundStatement LowerStatement( bool generateDebugInfo, MethodSymbol method, BoundStatement body, SynthesizedSubmissionFields previousSubmissionFields, TypeCompilationState compilationState, DiagnosticBag diagnostics) { if (body.HasErrors) { return body; } bool sawLambdas; bool sawDynamicOperations; bool sawAwaitInExceptionHandler; var loweredBody = LocalRewriter.Rewrite( method.DeclaringCompilation, generateDebugInfo, method, method.ContainingType, body, compilationState, diagnostics, previousSubmissionFields, out sawLambdas, out sawDynamicOperations, out sawAwaitInExceptionHandler); if (sawDynamicOperations && compilationState.ModuleBuilder.IsENCDelta) { // Dynamic operations are not supported in ENC. var location = method.Locations[0]; diagnostics.Add(new CSDiagnosticInfo(ErrorCode.ERR_EnCNoDynamicOperation), location); } if (loweredBody.HasErrors) { return loweredBody; } if (sawAwaitInExceptionHandler) { // If we have awaits in handlers, we need to // replace handlers with synthetic ones which can be consumed by async rewriter. // The reason why this rewrite happens before the lambda rewrite // is that we may need access to exception locals and it would be fairly hard to do // if these locals are captured into closures (possibly nested ones). Debug.Assert(method.IteratorElementType == null); loweredBody = AsyncHandlerRewriter.Rewrite( generateDebugInfo, method, method.ContainingType, loweredBody, compilationState, diagnostics); } if (loweredBody.HasErrors) { return loweredBody; } BoundStatement bodyWithoutLambdas = loweredBody; if (sawLambdas) { LambdaRewriter.Analysis lambdaAnalysis = LambdaRewriter.Analysis.Analyze(loweredBody, method, out sawLambdas); if (sawLambdas) { bodyWithoutLambdas = LambdaRewriter.Rewrite(loweredBody, method.ContainingType, method.ThisParameter, method, compilationState, diagnostics, lambdaAnalysis, generateDebugInfo); } } if (bodyWithoutLambdas.HasErrors) { return bodyWithoutLambdas; } BoundStatement bodyWithoutIterators = IteratorRewriter.Rewrite(bodyWithoutLambdas, method, compilationState, diagnostics, generateDebugInfo); if (bodyWithoutIterators.HasErrors) { return bodyWithoutIterators; } BoundStatement bodyWithoutAsync = AsyncRewriter2.Rewrite(bodyWithoutIterators, method, compilationState, diagnostics, generateDebugInfo); return bodyWithoutAsync; }
/// <summary> /// Generates a submission initialization part of a Script type constructor that represents an interactive submission. /// </summary> /// <remarks> /// The constructor takes a parameter of type Roslyn.Scripting.Session - the session reference. /// It adds the object being constructed into the session by calling Microsoft.CSharp.RuntimeHelpers.SessionHelpers.SetSubmission, /// and retrieves strongly typed references on all previous submission script classes whose members are referenced by this submission. /// The references are stored to fields of the submission (<paramref name="synthesizedFields"/>). /// </remarks> private static ImmutableArray <BoundStatement> MakeSubmissionInitialization(CSharpSyntaxNode syntax, MethodSymbol submissionConstructor, SynthesizedSubmissionFields synthesizedFields, CSharpCompilation compilation) { Debug.Assert(submissionConstructor.ParameterCount == 2); BoundStatement[] result = new BoundStatement[1 + synthesizedFields.Count]; var sessionReference = new BoundParameter(syntax, submissionConstructor.Parameters[0]) { WasCompilerGenerated = true }; var submissionGetter = (MethodSymbol)compilation.GetWellKnownTypeMember( WellKnownMember.Microsoft_CSharp_RuntimeHelpers_SessionHelpers__GetSubmission ); var submissionAdder = (MethodSymbol)compilation.GetWellKnownTypeMember( WellKnownMember.Microsoft_CSharp_RuntimeHelpers_SessionHelpers__SetSubmission ); // TODO: report missing adder/getter Debug.Assert((object)submissionAdder != null && (object)submissionGetter != null); var intType = compilation.GetSpecialType(SpecialType.System_Int32); var thisReference = new BoundThisReference(syntax, submissionConstructor.ContainingType) { WasCompilerGenerated = true }; int i = 0; // hostObject = (THostObject)SessionHelpers.SetSubmission(<session>, <slot index>, this); var slotIndex = compilation.GetSubmissionSlotIndex(); Debug.Assert(slotIndex >= 0); BoundExpression setSubmission = BoundCall.Synthesized(syntax, null, submissionAdder, sessionReference, new BoundLiteral(syntax, ConstantValue.Create(slotIndex), intType) { WasCompilerGenerated = true }, thisReference ); var hostObjectField = synthesizedFields.GetHostObjectField(); if ((object)hostObjectField != null) { setSubmission = new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, hostObjectField, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, setSubmission, Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, hostObjectField.Type ), hostObjectField.Type ) { WasCompilerGenerated = true }; } result[i++] = new BoundExpressionStatement(syntax, setSubmission) { WasCompilerGenerated = true }; foreach (var field in synthesizedFields.FieldSymbols) { var targetScriptClass = (ImplicitNamedTypeSymbol)field.Type; var targetSubmissionId = targetScriptClass.DeclaringCompilation.GetSubmissionSlotIndex(); Debug.Assert(targetSubmissionId >= 0); // this.<field> = (<FieldType>)SessionHelpers.GetSubmission(<session>, <i>); result[i++] = new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, BoundCall.Synthesized(syntax, null, submissionGetter, sessionReference, new BoundLiteral(syntax, ConstantValue.Create(targetSubmissionId), intType) { WasCompilerGenerated = true }), Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, targetScriptClass ), targetScriptClass ) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; } Debug.Assert(i == result.Length); return(result.AsImmutableOrNull()); }
internal static BoundStatement LowerStatement( bool generateDebugInfo, MethodSymbol method, BoundStatement body, SynthesizedSubmissionFields previousSubmissionFields, TypeCompilationState compilationState, DiagnosticBag diagnostics) { return LowerStatement( generateDebugInfo, method.ContainingType, method.ThisParameter, method, body, previousSubmissionFields, compilationState, diagnostics); }
/// <summary> /// Generates a submission initialization part of a Script type constructor that represents an interactive submission. /// </summary> /// <remarks> /// The constructor takes a parameter of type Roslyn.Scripting.Session - the session reference. /// It adds the object being constructed into the session by calling Microsoft.CSharp.RuntimeHelpers.SessionHelpers.SetSubmission, /// and retrieves strongly typed references on all previous submission script classes whose members are referenced by this submission. /// The references are stored to fields of the submission (<paramref name="synthesizedFields"/>). /// </remarks> private static ImmutableArray<BoundStatement> MakeSubmissionInitialization(CSharpSyntaxNode syntax, MethodSymbol submissionConstructor, SynthesizedSubmissionFields synthesizedFields, CSharpCompilation compilation) { Debug.Assert(submissionConstructor.ParameterCount == 2); BoundStatement[] result = new BoundStatement[1 + synthesizedFields.Count]; var sessionReference = new BoundParameter(syntax, submissionConstructor.Parameters[0]) { WasCompilerGenerated = true }; var submissionGetter = (MethodSymbol)compilation.GetWellKnownTypeMember( WellKnownMember.Microsoft_CSharp_RuntimeHelpers_SessionHelpers__GetSubmission ); var submissionAdder = (MethodSymbol)compilation.GetWellKnownTypeMember( WellKnownMember.Microsoft_CSharp_RuntimeHelpers_SessionHelpers__SetSubmission ); // TODO: report missing adder/getter Debug.Assert((object)submissionAdder != null && (object)submissionGetter != null); var intType = compilation.GetSpecialType(SpecialType.System_Int32); var thisReference = new BoundThisReference(syntax, submissionConstructor.ContainingType) { WasCompilerGenerated = true }; int i = 0; // hostObject = (THostObject)SessionHelpers.SetSubmission(<session>, <slot index>, this); var slotIndex = compilation.GetSubmissionSlotIndex(); Debug.Assert(slotIndex >= 0); BoundExpression setSubmission = BoundCall.Synthesized(syntax, null, submissionAdder, sessionReference, new BoundLiteral(syntax, ConstantValue.Create(slotIndex), intType) { WasCompilerGenerated = true }, thisReference ); var hostObjectField = synthesizedFields.GetHostObjectField(); if ((object)hostObjectField != null) { setSubmission = new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, hostObjectField, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, setSubmission, Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, hostObjectField.Type ), hostObjectField.Type ) { WasCompilerGenerated = true }; } result[i++] = new BoundExpressionStatement(syntax, setSubmission) { WasCompilerGenerated = true }; foreach (var field in synthesizedFields.FieldSymbols) { var targetScriptClass = (ImplicitNamedTypeSymbol)field.Type; var targetSubmissionId = targetScriptClass.DeclaringCompilation.GetSubmissionSlotIndex(); Debug.Assert(targetSubmissionId >= 0); // this.<field> = (<FieldType>)SessionHelpers.GetSubmission(<session>, <i>); result[i++] = new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, BoundCall.Synthesized(syntax, null, submissionGetter, sessionReference, new BoundLiteral(syntax, ConstantValue.Create(targetSubmissionId), intType) { WasCompilerGenerated = true }), Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, targetScriptClass ), targetScriptClass ) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; } Debug.Assert(i == result.Length); return result.AsImmutableOrNull(); }
internal static BoundStatement LowerStatement( bool generateDebugInfo, MethodSymbol method, BoundStatement body, SynthesizedSubmissionFields previousSubmissionFields, TypeCompilationState compilationState, DiagnosticBag diagnostics) { if (body.HasErrors) { return(body); } bool sawLambdas; bool sawDynamicOperations; bool sawAwaitInExceptionHandler; var loweredBody = LocalRewriter.Rewrite( method.DeclaringCompilation, generateDebugInfo, method, method.ContainingType, body, compilationState, diagnostics, previousSubmissionFields, out sawLambdas, out sawDynamicOperations, out sawAwaitInExceptionHandler); if (sawDynamicOperations && compilationState.ModuleBuilder.IsENCDelta) { // Dynamic operations are not supported in ENC. var location = method.Locations[0]; diagnostics.Add(new CSDiagnosticInfo(ErrorCode.ERR_EnCNoDynamicOperation), location); } if (loweredBody.HasErrors) { return(loweredBody); } if (sawAwaitInExceptionHandler) { // If we have awaits in handlers, we need to // replace handlers with synthetic ones which can be consumed by async rewriter. // The reason why this rewrite happens before the lambda rewrite // is that we may need access to exception locals and it would be fairly hard to do // if these locals are captured into closures (possibly nested ones). Debug.Assert(method.IteratorElementType == null); loweredBody = AsyncHandlerRewriter.Rewrite( generateDebugInfo, method, method.ContainingType, loweredBody, compilationState, diagnostics); } if (loweredBody.HasErrors) { return(loweredBody); } BoundStatement bodyWithoutLambdas = loweredBody; if (sawLambdas) { LambdaRewriter.Analysis lambdaAnalysis = LambdaRewriter.Analysis.Analyze(loweredBody, method, out sawLambdas); if (sawLambdas) { bodyWithoutLambdas = LambdaRewriter.Rewrite(loweredBody, method.ContainingType, method.ThisParameter, method, compilationState, diagnostics, lambdaAnalysis, generateDebugInfo); } } if (bodyWithoutLambdas.HasErrors) { return(bodyWithoutLambdas); } BoundStatement bodyWithoutIterators = IteratorRewriter.Rewrite(bodyWithoutLambdas, method, compilationState, diagnostics, generateDebugInfo); if (bodyWithoutIterators.HasErrors) { return(bodyWithoutIterators); } BoundStatement bodyWithoutAsync = AsyncRewriter2.Rewrite(bodyWithoutIterators, method, compilationState, diagnostics, generateDebugInfo); return(bodyWithoutAsync); }
/// <summary> /// Generates a submission initialization part of a Script type constructor that represents an interactive submission. /// </summary> /// <remarks> /// The constructor takes a parameter of type Microsoft.CodeAnalysis.Scripting.Session - the session reference. /// It adds the object being constructed into the session by calling Microsoft.CSharp.RuntimeHelpers.SessionHelpers.SetSubmission, /// and retrieves strongly typed references on all previous submission script classes whose members are referenced by this submission. /// The references are stored to fields of the submission (<paramref name="synthesizedFields"/>). /// </remarks> private static void MakeSubmissionInitialization( ArrayBuilder<BoundStatement> statements, CSharpSyntaxNode syntax, MethodSymbol submissionConstructor, SynthesizedSubmissionFields synthesizedFields, CSharpCompilation compilation) { Debug.Assert(submissionConstructor.ParameterCount == 1); var submissionArrayReference = new BoundParameter(syntax, submissionConstructor.Parameters[0]) { WasCompilerGenerated = true }; var intType = compilation.GetSpecialType(SpecialType.System_Int32); var objectType = compilation.GetSpecialType(SpecialType.System_Object); var thisReference = new BoundThisReference(syntax, submissionConstructor.ContainingType) { WasCompilerGenerated = true }; var slotIndex = compilation.GetSubmissionSlotIndex(); Debug.Assert(slotIndex >= 0); // <submission_array>[<slot_index] = this; statements.Add(new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundArrayAccess(syntax, submissionArrayReference, ImmutableArray.Create<BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(slotIndex), intType) { WasCompilerGenerated = true }), objectType) { WasCompilerGenerated = true }, thisReference, RefKind.None, thisReference.Type) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }); var hostObjectField = synthesizedFields.GetHostObjectField(); if ((object)hostObjectField != null) { // <host_object> = (<host_object_type>)<submission_array>[0] statements.Add( new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, hostObjectField, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, new BoundArrayAccess(syntax, submissionArrayReference, ImmutableArray.Create<BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(0), intType) { WasCompilerGenerated = true }), objectType), Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, hostObjectField.Type ), hostObjectField.Type) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }); } foreach (var field in synthesizedFields.FieldSymbols) { var targetScriptType = (ImplicitNamedTypeSymbol)field.Type; var targetSubmissionIndex = targetScriptType.DeclaringCompilation.GetSubmissionSlotIndex(); Debug.Assert(targetSubmissionIndex >= 0); // this.<field> = (<target_script_type>)<submission_array>[<target_submission_index>]; statements.Add( new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable) { WasCompilerGenerated = true }, BoundConversion.Synthesized(syntax, new BoundArrayAccess(syntax, submissionArrayReference, ImmutableArray.Create<BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(targetSubmissionIndex), intType) { WasCompilerGenerated = true }), objectType) { WasCompilerGenerated = true }, Conversion.ExplicitReference, false, true, ConstantValue.NotAvailable, targetScriptType ), targetScriptType ) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }); } }
//TODO: it might be nice to make this a static method on Compiler private void CompileMethod( MethodSymbol methodSymbol, ref ProcessedFieldInitializers processedInitializers, SynthesizedSubmissionFields previousSubmissionFields, TypeCompilationState compilationState) { cancellationToken.ThrowIfCancellationRequested(); SourceMethodSymbol sourceMethod = methodSymbol as SourceMethodSymbol; if (methodSymbol.IsAbstract) { if ((object)sourceMethod != null) { bool diagsWritten; sourceMethod.SetDiagnostics(ImmutableArray <Diagnostic> .Empty, out diagsWritten); if (diagsWritten && !methodSymbol.IsImplicitlyDeclared && compilation.EventQueue != null) { compilation.SymbolDeclaredEvent(methodSymbol); } } return; } // get cached diagnostics if not building and we have 'em bool calculateDiagnosticsOnly = moduleBeingBuilt == null; if (calculateDiagnosticsOnly && ((object)sourceMethod != null)) { var cachedDiagnostics = sourceMethod.Diagnostics; if (!cachedDiagnostics.IsDefault) { this.diagnostics.AddRange(cachedDiagnostics); return; } } ConsList <Imports> oldDebugImports = compilationState.CurrentDebugImports; // In order to avoid generating code for methods with errors, we create a diagnostic bag just for this method. DiagnosticBag diagsForCurrentMethod = DiagnosticBag.GetInstance(); try { bool includeInitializersInBody; BoundBlock body; // if synthesized method returns its body in lowered form if (methodSymbol.SynthesizesLoweredBoundBody) { if (moduleBeingBuilt != null) { methodSymbol.GenerateMethodBody(compilationState, diagsForCurrentMethod); this.diagnostics.AddRange(diagsForCurrentMethod); } return; } //EDMAURER initializers that have been analyzed but not yet lowered. BoundStatementList analyzedInitializers = null; ConsList <Imports> debugImports; if (methodSymbol.IsScriptConstructor) { // rewrite top-level statements and script variable declarations to a list of statements and assignments, respectively: BoundStatementList initializerStatements = InitializerRewriter.Rewrite(processedInitializers.BoundInitializers, methodSymbol); // the lowered script initializers should not be treated as initializers anymore but as a method body: body = new BoundBlock(initializerStatements.Syntax, ImmutableArray <LocalSymbol> .Empty, initializerStatements.Statements) { WasCompilerGenerated = true }; includeInitializersInBody = false; debugImports = null; } else { // do not emit initializers if we are invoking another constructor of this class: includeInitializersInBody = !processedInitializers.BoundInitializers.IsDefaultOrEmpty && !HasThisConstructorInitializer(methodSymbol); // lower initializers just once. the lowered tree will be reused when emitting all constructors // with field initializers. Once lowered, these initializers will be stashed in processedInitializers.LoweredInitializers // (see later in this method). Don't bother lowering _now_ if this particular ctor won't have the initializers // appended to its body. if (includeInitializersInBody && processedInitializers.LoweredInitializers == null) { analyzedInitializers = InitializerRewriter.Rewrite(processedInitializers.BoundInitializers, methodSymbol); processedInitializers.HasErrors = processedInitializers.HasErrors || analyzedInitializers.HasAnyErrors; // These analyses check for diagnostics in lambdas. // Control flow analysis and implicit return insertion are unnecessary. DataFlowPass.Analyze(compilation, methodSymbol, analyzedInitializers, diagsForCurrentMethod, requireOutParamsAssigned: false); DiagnosticsPass.IssueDiagnostics(compilation, analyzedInitializers, diagsForCurrentMethod, methodSymbol); } body = Compiler.BindMethodBody(methodSymbol, diagsForCurrentMethod, this.generateDebugInfo, out debugImports); } #if DEBUG // If the method is a synthesized static or instance constructor, then debugImports will be null and we will use the value // from the first field initializer. if (this.generateDebugInfo) { if ((methodSymbol.MethodKind == MethodKind.Constructor || methodSymbol.MethodKind == MethodKind.StaticConstructor) && methodSymbol.IsImplicitlyDeclared) { // There was no body to bind, so we didn't get anything from Compiler.BindMethodBody. Debug.Assert(debugImports == null); // Either there were no field initializers or we grabbed debug imports from the first one. Debug.Assert(processedInitializers.BoundInitializers.IsDefaultOrEmpty || processedInitializers.FirstDebugImports != null); } } #endif debugImports = debugImports ?? processedInitializers.FirstDebugImports; // Associate these debug imports with all methods generated from this one. compilationState.CurrentDebugImports = debugImports; if (body != null && methodSymbol is SourceMethodSymbol) { // TODO: Do we need to issue warnings for non-SourceMethodSymbol methods, like synthesized ctors? DiagnosticsPass.IssueDiagnostics(compilation, body, diagsForCurrentMethod, methodSymbol); } BoundBlock flowAnalyzedBody = null; if (body != null) { flowAnalyzedBody = FlowAnalysisPass.Rewrite(methodSymbol, body, diagsForCurrentMethod); } bool hasErrors = hasDeclarationErrors || diagsForCurrentMethod.HasAnyErrors() || processedInitializers.HasErrors; // Record whether or not the bound tree for the lowered method body (including any initializers) contained any // errors (note: errors, not diagnostics). SetGlobalErrorIfTrue(hasErrors); bool diagsWritten = false; var actualDiagnostics = diagsForCurrentMethod.ToReadOnly(); if (sourceMethod != null) { actualDiagnostics = sourceMethod.SetDiagnostics(actualDiagnostics, out diagsWritten); } if (diagsWritten && !methodSymbol.IsImplicitlyDeclared && compilation.EventQueue != null) { var lazySemanticModel = body == null ? null : new Lazy <SemanticModel>(() => { var syntax = body.Syntax; var semanticModel = (CSharpSemanticModel)compilation.GetSemanticModel(syntax.SyntaxTree); var memberModel = semanticModel.GetMemberModel(syntax); if (memberModel != null) { memberModel.AddBoundTreeForStandaloneSyntax(syntax, body); } return(semanticModel); }); compilation.EventQueue.Enqueue(new CompilationEvent.SymbolDeclared(compilation, methodSymbol, lazySemanticModel)); } // Don't lower if we're not emitting or if there were errors. // Methods that had binding errors are considered too broken to be lowered reliably. if (calculateDiagnosticsOnly || hasErrors) { this.diagnostics.AddRange(actualDiagnostics); return; } // ############################ // LOWERING AND EMIT // Any errors generated below here are considered Emit diagnostics // and will not be reported to callers Compilation.GetDiagnostics() BoundStatement loweredBody = (flowAnalyzedBody == null) ? null : Compiler.LowerStatement(this.generateDebugInfo, methodSymbol, flowAnalyzedBody, previousSubmissionFields, compilationState, diagsForCurrentMethod); bool hasBody = loweredBody != null; hasErrors = hasErrors || (hasBody && loweredBody.HasErrors) || diagsForCurrentMethod.HasAnyErrors(); SetGlobalErrorIfTrue(hasErrors); // don't emit if the resulting method would contain initializers with errors if (!hasErrors && (hasBody || includeInitializersInBody)) { // Fields must be initialized before constructor initializer (which is the first statement of the analyzed body, if specified), // so that the initialization occurs before any method overridden by the declaring class can be invoked from the base constructor // and access the fields. ImmutableArray <BoundStatement> boundStatements; if (methodSymbol.IsScriptConstructor) { boundStatements = MethodBodySynthesizer.ConstructScriptConstructorBody(loweredBody, methodSymbol, previousSubmissionFields, compilation); } else { boundStatements = ImmutableArray <BoundStatement> .Empty; if (analyzedInitializers != null) { processedInitializers.LoweredInitializers = (BoundStatementList)Compiler.LowerStatement( this.generateDebugInfo, methodSymbol, analyzedInitializers, previousSubmissionFields, compilationState, diagsForCurrentMethod); Debug.Assert(!hasErrors); hasErrors = processedInitializers.LoweredInitializers.HasAnyErrors || diagsForCurrentMethod.HasAnyErrors(); SetGlobalErrorIfTrue(hasErrors); if (hasErrors) { this.diagnostics.AddRange(diagsForCurrentMethod); return; } } // initializers for global code have already been included in the body if (includeInitializersInBody) { //TODO: rewrite any BoundThis and BoundBase nodes in the initializers to have the correct ThisParameter symbol if (compilation.Options.Optimize) { // TODO: this part may conflict with InitializerRewriter.Rewrite in how it handles // the first field initializer (see 'if (i == 0)'...) which seems suspicious ArrayBuilder <BoundStatement> statements = ArrayBuilder <BoundStatement> .GetInstance(); statements.AddRange(boundStatements); bool anyNonDefault = false; foreach (var initializer in processedInitializers.LoweredInitializers.Statements) { if (ShouldOptimizeOutInitializer(initializer)) { if (methodSymbol.IsStatic) { // NOTE: Dev11 removes static initializers if ONLY all of them are optimized out statements.Add(initializer); } } else { statements.Add(initializer); anyNonDefault = true; } } if (anyNonDefault) { boundStatements = statements.ToImmutableAndFree(); } else { statements.Free(); } } else { boundStatements = boundStatements.Concat(processedInitializers.LoweredInitializers.Statements); } } if (hasBody) { boundStatements = boundStatements.Concat(ImmutableArray.Create(loweredBody)); } } CSharpSyntaxNode syntax = methodSymbol.GetNonNullSyntaxNode(); var boundBody = BoundStatementList.Synthesized(syntax, boundStatements); var emittedBody = Compiler.GenerateMethodBody( compilationState, methodSymbol, boundBody, diagsForCurrentMethod, optimize, debugDocumentProvider, GetNamespaceScopes(methodSymbol, debugImports)); moduleBeingBuilt.SetMethodBody(methodSymbol, emittedBody); } this.diagnostics.AddRange(diagsForCurrentMethod); } finally { diagsForCurrentMethod.Free(); compilationState.CurrentDebugImports = oldDebugImports; } }