in src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs [845:1201]
private void CompileMethod(
MethodSymbol methodSymbol,
int methodOrdinal,
ref Binder.ProcessedFieldInitializers processedInitializers,
SynthesizedSubmissionFields previousSubmissionFields,
TypeCompilationState compilationState)
{
_cancellationToken.ThrowIfCancellationRequested();
SourceMemberMethodSymbol sourceMethod = methodSymbol as SourceMemberMethodSymbol;
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
if (_moduleBeingBuiltOpt == null && (object)sourceMethod != null)
{
var cachedDiagnostics = sourceMethod.Diagnostics;
if (!cachedDiagnostics.IsDefault)
{
_diagnostics.AddRange(cachedDiagnostics);
return;
}
}
ImportChain oldImportChain = compilationState.CurrentImportChain;
// In order to avoid generating code for methods with errors, we create a diagnostic bag just for this method.
DiagnosticBag diagsForCurrentMethod = DiagnosticBag.GetInstance();
try
{
// if synthesized method returns its body in lowered form
if (methodSymbol.SynthesizesLoweredBoundBody)
{
if (_moduleBeingBuiltOpt != null)
{
methodSymbol.GenerateMethodBody(compilationState, diagsForCurrentMethod);
_diagnostics.AddRange(diagsForCurrentMethod);
}
return;
}
// no need to emit the default ctor, we are not emitting those
if (methodSymbol.IsDefaultValueTypeConstructor())
{
return;
}
bool includeInitializersInBody = false;
BoundBlock body;
bool originalBodyNested = false;
// initializers that have been analyzed but not yet lowered.
BoundStatementList analyzedInitializers = null;
ImportChain importChain = null;
var hasTrailingExpression = false;
if (methodSymbol.IsScriptConstructor)
{
body = new BoundBlock(methodSymbol.GetNonNullSyntaxNode(), ImmutableArray<LocalSymbol>.Empty, ImmutableArray<BoundStatement>.Empty) { WasCompilerGenerated = true };
}
else if (methodSymbol.IsScriptInitializer)
{
// rewrite top-level statements and script variable declarations to a list of statements and assignments, respectively:
var initializerStatements = InitializerRewriter.RewriteScriptInitializer(processedInitializers.BoundInitializers, (SynthesizedInteractiveInitializerMethod)methodSymbol, out hasTrailingExpression);
// the lowered script initializers should not be treated as initializers anymore but as a method body:
body = BoundBlock.SynthesizedNoLocals(initializerStatements.Syntax, initializerStatements.Statements);
var unusedDiagnostics = DiagnosticBag.GetInstance();
DataFlowPass.Analyze(_compilation, methodSymbol, initializerStatements, unusedDiagnostics, requireOutParamsAssigned: false);
DiagnosticsPass.IssueDiagnostics(_compilation, initializerStatements, unusedDiagnostics, methodSymbol);
unusedDiagnostics.Free();
}
else
{
// Do not emit initializers if we are invoking another constructor of this class.
includeInitializersInBody = !processedInitializers.BoundInitializers.IsDefaultOrEmpty &&
!HasThisConstructorInitializer(methodSymbol);
body = BindMethodBody(methodSymbol, compilationState, diagsForCurrentMethod, out importChain, out originalBodyNested);
// 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.RewriteConstructor(processedInitializers.BoundInitializers, methodSymbol);
processedInitializers.HasErrors = processedInitializers.HasErrors || analyzedInitializers.HasAnyErrors;
if (body != null && ((methodSymbol.ContainingType.IsStructType() && !methodSymbol.IsImplicitConstructor) || _emitTestCoverageData))
{
if (_emitTestCoverageData && methodSymbol.IsImplicitConstructor)
{
// Flow analysis over the initializers is necessary in order to find assignments to fields.
// Bodies of implicit constructors do not get flow analysis later, so the initializers
// are analyzed here.
DataFlowPass.Analyze(_compilation, methodSymbol, analyzedInitializers, diagsForCurrentMethod, requireOutParamsAssigned: false);
}
// In order to get correct diagnostics, we need to analyze initializers and the body together.
body = body.Update(body.Locals, body.LocalFunctions, body.Statements.Insert(0, analyzedInitializers));
includeInitializersInBody = false;
analyzedInitializers = null;
}
else
{
// 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);
}
}
}
#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 ((methodSymbol.MethodKind == MethodKind.Constructor || methodSymbol.MethodKind == MethodKind.StaticConstructor) &&
methodSymbol.IsImplicitlyDeclared && body == null)
{
// There was no body to bind, so we didn't get anything from BindMethodBody.
Debug.Assert(importChain == null);
}
// Either there were no field initializers or we grabbed debug imports from the first one.
Debug.Assert(processedInitializers.BoundInitializers.IsDefaultOrEmpty || processedInitializers.FirstImportChain != null);
#endif
importChain = importChain ?? processedInitializers.FirstImportChain;
// Associate these debug imports with all methods generated from this one.
compilationState.CurrentImportChain = importChain;
if (body != null)
{
DiagnosticsPass.IssueDiagnostics(_compilation, body, diagsForCurrentMethod, methodSymbol);
}
BoundBlock flowAnalyzedBody = null;
if (body != null)
{
flowAnalyzedBody = FlowAnalysisPass.Rewrite(methodSymbol, body, diagsForCurrentMethod, hasTrailingExpression: hasTrailingExpression, originalBodyNested: originalBodyNested);
}
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.UnguardedAddBoundTreeForStandaloneSyntax(syntax, body);
}
return semanticModel;
});
_compilation.EventQueue.TryEnqueue(new SymbolDeclaredCompilationEvent(_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 (_moduleBeingBuiltOpt == null || hasErrors)
{
_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()
ImmutableArray<SourceSpan> dynamicAnalysisSpans = ImmutableArray<SourceSpan>.Empty;
bool hasBody = flowAnalyzedBody != null;
VariableSlotAllocator lazyVariableSlotAllocator = null;
StateMachineTypeSymbol stateMachineTypeOpt = null;
var lambdaDebugInfoBuilder = ArrayBuilder<LambdaDebugInfo>.GetInstance();
var closureDebugInfoBuilder = ArrayBuilder<ClosureDebugInfo>.GetInstance();
BoundStatement loweredBodyOpt = null;
try
{
if (hasBody)
{
loweredBodyOpt = LowerBodyOrInitializer(
methodSymbol,
methodOrdinal,
flowAnalyzedBody,
previousSubmissionFields,
compilationState,
_emitTestCoverageData,
_debugDocumentProvider,
ref dynamicAnalysisSpans,
diagsForCurrentMethod,
ref lazyVariableSlotAllocator,
lambdaDebugInfoBuilder,
closureDebugInfoBuilder,
out stateMachineTypeOpt);
Debug.Assert(loweredBodyOpt != null);
}
else
{
loweredBodyOpt = null;
}
hasErrors = hasErrors || (hasBody && loweredBodyOpt.HasErrors) || diagsForCurrentMethod.HasAnyErrors();
SetGlobalErrorIfTrue(hasErrors);
// don't emit if the resulting method would contain initializers with errors
if (!hasErrors && (hasBody || includeInitializersInBody))
{
Debug.Assert(!methodSymbol.IsImplicitInstanceConstructor || !methodSymbol.ContainingType.IsStructType());
// 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(loweredBodyOpt, methodSymbol, previousSubmissionFields, _compilation);
}
else
{
boundStatements = ImmutableArray<BoundStatement>.Empty;
if (analyzedInitializers != null)
{
// For dynamic analysis, field initializers are instrumented as part of constructors,
// and so are never instrumented here.
Debug.Assert(!_emitTestCoverageData);
StateMachineTypeSymbol initializerStateMachineTypeOpt;
BoundStatement lowered = LowerBodyOrInitializer(
methodSymbol,
methodOrdinal,
analyzedInitializers,
previousSubmissionFields,
compilationState,
_emitTestCoverageData,
_debugDocumentProvider,
ref dynamicAnalysisSpans,
diagsForCurrentMethod,
ref lazyVariableSlotAllocator,
lambdaDebugInfoBuilder,
closureDebugInfoBuilder,
out initializerStateMachineTypeOpt);
processedInitializers.LoweredInitializers = lowered;
// initializers can't produce state machines
Debug.Assert((object)initializerStateMachineTypeOpt == null);
Debug.Assert(!hasErrors);
hasErrors = lowered.HasAnyErrors || diagsForCurrentMethod.HasAnyErrors();
SetGlobalErrorIfTrue(hasErrors);
if (hasErrors)
{
_diagnostics.AddRange(diagsForCurrentMethod);
return;
}
// Only do the cast if we haven't returned with some error diagnostics.
// Otherwise, `lowered` might have been a BoundBadStatement.
processedInitializers.LoweredInitializers = (BoundStatementList)lowered;
}
// initializers for global code have already been included in the body
if (includeInitializersInBody)
{
if (processedInitializers.LoweredInitializers.Kind == BoundKind.StatementList)
{
BoundStatementList lowered = (BoundStatementList)processedInitializers.LoweredInitializers;
boundStatements = boundStatements.Concat(lowered.Statements);
}
else
{
boundStatements = boundStatements.Add(processedInitializers.LoweredInitializers);
}
}
if (hasBody)
{
boundStatements = boundStatements.Concat(ImmutableArray.Create(loweredBodyOpt));
}
}
CSharpSyntaxNode syntax = methodSymbol.GetNonNullSyntaxNode();
var boundBody = BoundStatementList.Synthesized(syntax, boundStatements);
var emittedBody = GenerateMethodBody(
_moduleBeingBuiltOpt,
methodSymbol,
methodOrdinal,
boundBody,
lambdaDebugInfoBuilder.ToImmutable(),
closureDebugInfoBuilder.ToImmutable(),
stateMachineTypeOpt,
lazyVariableSlotAllocator,
diagsForCurrentMethod,
_debugDocumentProvider,
importChain,
_emittingPdb,
_emitTestCoverageData,
dynamicAnalysisSpans);
_moduleBeingBuiltOpt.SetMethodBody(methodSymbol.PartialDefinitionPart ?? methodSymbol, emittedBody);
}
_diagnostics.AddRange(diagsForCurrentMethod);
}
finally
{
lambdaDebugInfoBuilder.Free();
closureDebugInfoBuilder.Free();
}
}
finally
{
diagsForCurrentMethod.Free();
compilationState.CurrentImportChain = oldImportChain;
}
}