예제 #1
0
        internal VariableSlotAllocator?TryCreateVariableSlotAllocator(EmitBaseline baseline, Compilation compilation, IMethodSymbolInternal method, IMethodSymbolInternal topLevelMethod, DiagnosticBag diagnostics)
        {
            // Top-level methods are always included in the semantic edit list. Lambda methods are not.
            if (!mappedMethods.TryGetValue(topLevelMethod, out var mappedMethod))
            {
                return(null);
            }

            // TODO (bug https://github.com/dotnet/roslyn/issues/2504):
            // Handle cases when the previous method doesn't exist.

            if (!TryGetMethodHandle(baseline, (Cci.IMethodDefinition)method.GetCciAdapter(), out var previousHandle))
            {
                // Unrecognized method. Must have been added in the current compilation.
                return(null);
            }

            ImmutableArray <EncLocalInfo> previousLocals;
            IReadOnlyDictionary <EncHoistedLocalInfo, int>?         hoistedLocalMap = null;
            IReadOnlyDictionary <Cci.ITypeReference, int>?          awaiterMap      = null;
            IReadOnlyDictionary <int, KeyValuePair <DebugId, int> >?lambdaMap       = null;
            IReadOnlyDictionary <int, DebugId>?closureMap           = null;
            IReadOnlyDictionary <int, int>?    stateMachineStateMap = null;
            int?firstUnusedIncreasingStateMachineState = null;
            int?firstUnusedDecreasingStateMachineState = null;

            int           hoistedLocalSlotCount = 0;
            int           awaiterSlotCount      = 0;
            string?       stateMachineTypeName  = null;
            SymbolMatcher symbolMap;

            int     methodIndex = MetadataTokens.GetRowNumber(previousHandle);
            DebugId methodId;

            // Check if method has changed previously. If so, we already have a map.
            if (baseline.AddedOrChangedMethods.TryGetValue(methodIndex, out var addedOrChangedMethod))
            {
                methodId = addedOrChangedMethod.MethodId;

                MakeLambdaAndClosureMaps(addedOrChangedMethod.LambdaDebugInfo, addedOrChangedMethod.ClosureDebugInfo, out lambdaMap, out closureMap);
                MakeStateMachineStateMap(addedOrChangedMethod.StateMachineStates.States, out stateMachineStateMap);

                firstUnusedIncreasingStateMachineState = addedOrChangedMethod.StateMachineStates.FirstUnusedIncreasingStateMachineState;
                firstUnusedDecreasingStateMachineState = addedOrChangedMethod.StateMachineStates.FirstUnusedDecreasingStateMachineState;

                if (addedOrChangedMethod.StateMachineTypeName != null)
                {
                    // method is async/iterator kickoff method
                    GetStateMachineFieldMapFromPreviousCompilation(
                        addedOrChangedMethod.StateMachineHoistedLocalSlotsOpt,
                        addedOrChangedMethod.StateMachineAwaiterSlotsOpt,
                        out hoistedLocalMap,
                        out awaiterMap);

                    hoistedLocalSlotCount = addedOrChangedMethod.StateMachineHoistedLocalSlotsOpt.Length;
                    awaiterSlotCount      = addedOrChangedMethod.StateMachineAwaiterSlotsOpt.Length;

                    // Kickoff method has no interesting locals on its own.
                    // We use the EnC method debug information for hoisted locals.
                    previousLocals = ImmutableArray <EncLocalInfo> .Empty;

                    stateMachineTypeName = addedOrChangedMethod.StateMachineTypeName;
                }
                else
                {
                    previousLocals = addedOrChangedMethod.Locals;
                }

                // All types that AddedOrChangedMethodInfo refers to have been mapped to the previous generation.
                // Therefore we don't need to fall back to metadata if we don't find the type reference, like we do in DefinitionMap.MapReference.
                symbolMap = MapToPreviousSymbolMatcher;
            }
            else
            {
                // Method has not changed since initial generation. Generate a map
                // using the local names provided with the initial metadata.
                EditAndContinueMethodDebugInformation debugInfo;
                StandaloneSignatureHandle             localSignature;
                try
                {
                    debugInfo      = baseline.DebugInformationProvider(previousHandle);
                    localSignature = baseline.LocalSignatureProvider(previousHandle);
                }
                catch (Exception e) when(e is InvalidDataException || e is IOException)
                {
                    diagnostics.Add(MessageProvider.CreateDiagnostic(
                                        MessageProvider.ERR_InvalidDebugInfo,
                                        method.Locations.First(),
                                        method,
                                        MetadataTokens.GetToken(previousHandle),
                                        method.ContainingAssembly
                                        ));

                    return(null);
                }

                methodId = new DebugId(debugInfo.MethodOrdinal, 0);

                if (!debugInfo.Lambdas.IsDefaultOrEmpty)
                {
                    MakeLambdaAndClosureMaps(debugInfo.Lambdas, debugInfo.Closures, out lambdaMap, out closureMap);
                }

                MakeStateMachineStateMap(debugInfo.StateMachineStates, out stateMachineStateMap);

                if (!debugInfo.StateMachineStates.IsDefaultOrEmpty)
                {
                    firstUnusedIncreasingStateMachineState = debugInfo.StateMachineStates.Max(s => s.StateNumber) + 1;
                    firstUnusedDecreasingStateMachineState = debugInfo.StateMachineStates.Min(s => s.StateNumber) - 1;
                }

                ITypeSymbolInternal?stateMachineType = TryGetStateMachineType(previousHandle);
                if (stateMachineType != null)
                {
                    // method is async/iterator kickoff method
                    var localSlotDebugInfo = debugInfo.LocalSlots.NullToEmpty();
                    GetStateMachineFieldMapFromMetadata(stateMachineType, localSlotDebugInfo, out hoistedLocalMap, out awaiterMap, out awaiterSlotCount);
                    hoistedLocalSlotCount = localSlotDebugInfo.Length;

                    // Kickoff method has no interesting locals on its own.
                    // We use the EnC method debug information for hoisted locals.
                    previousLocals = ImmutableArray <EncLocalInfo> .Empty;

                    stateMachineTypeName = stateMachineType.Name;
                }
                else
                {
                    // If the current method is async/iterator then either the previous method wasn't declared as async/iterator and it's updated to be one,
                    // or it was but is not marked by the corresponding state machine attribute because it was missing in the compilation.
                    // In the later case we need to report an error since we don't known how to map to the previous state machine.

                    // The IDE already checked that the attribute type is present in the base compilation, but didn't validate that it is well-formed.
                    // We don't have the base compilation to directly query for the attribute, only the source compilation.
                    // But since constructor signatures can't be updated during EnC we can just check the current compilation.

                    if (method.IsAsync)
                    {
                        if (compilation.CommonGetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_AsyncStateMachineAttribute__ctor) == null)
                        {
                            ReportMissingStateMachineAttribute(diagnostics, method, AttributeDescription.AsyncStateMachineAttribute.FullName);
                            return(null);
                        }
                    }
                    else if (method.IsIterator)
                    {
                        if (compilation.CommonGetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_IteratorStateMachineAttribute__ctor) == null)
                        {
                            ReportMissingStateMachineAttribute(diagnostics, method, AttributeDescription.IteratorStateMachineAttribute.FullName);
                            return(null);
                        }
                    }

                    try
                    {
                        previousLocals = localSignature.IsNil ? ImmutableArray <EncLocalInfo> .Empty :
                                         GetLocalSlotMapFromMetadata(localSignature, debugInfo);
                    }
                    catch (Exception e) when(e is UnsupportedSignatureContent || e is BadImageFormatException || e is IOException)
                    {
                        diagnostics.Add(MessageProvider.CreateDiagnostic(
                                            MessageProvider.ERR_InvalidDebugInfo,
                                            method.Locations.First(),
                                            method,
                                            MetadataTokens.GetToken(localSignature),
                                            method.ContainingAssembly
                                            ));

                        return(null);
                    }
                }

                symbolMap = MapToMetadataSymbolMatcher;
            }

            return(new EncVariableSlotAllocator(
                       symbolMap,
                       mappedMethod.SyntaxMap,
                       mappedMethod.PreviousMethod,
                       methodId,
                       previousLocals,
                       lambdaMap,
                       closureMap,
                       stateMachineTypeName,
                       hoistedLocalSlotCount,
                       hoistedLocalMap,
                       awaiterSlotCount,
                       awaiterMap,
                       stateMachineStateMap,
                       firstUnusedIncreasingStateMachineState,
                       firstUnusedDecreasingStateMachineState,
                       GetLambdaSyntaxFacts()));
        }