internal override bool TryGetPreviousLocals(
            EmitBaseline baseline,
            IMethodSymbol method,
            out ImmutableArray <EncLocalInfo> previousLocals,
            out GetPreviousLocalSlot getPreviousLocalSlot)
        {
            previousLocals       = default(ImmutableArray <EncLocalInfo>);
            getPreviousLocalSlot = NoPreviousLocalSlot;

            MethodHandle handle;

            if (!this.TryGetMethodHandle(baseline, (Cci.IMethodDefinition)method, out handle))
            {
                // Unrecognized method. Must have been added in the current compilation.
                return(false);
            }

            MethodDefinitionEntry methodEntry;

            if (!this.methodMap.TryGetValue(method, out methodEntry))
            {
                // Not part of changeset. No need to preserve locals.
                return(false);
            }

            if (!methodEntry.PreserveLocalVariables)
            {
                // Not necessary to preserve locals.
                return(false);
            }

            var           previousMethod = methodEntry.PreviousMethod;
            var           methodIndex    = (uint)MetadataTokens.GetRowNumber(handle);
            SymbolMatcher map;

            // Check if method has changed previously. If so, we already have a map.
            if (baseline.LocalsForMethodsAddedOrChanged.TryGetValue(methodIndex, out previousLocals))
            {
                map = this.mapToPrevious;
            }
            else
            {
                // Method has not changed since initial generation. Generate a map
                // using the local names provided with the initial metadata.
                var localNames = baseline.LocalNames(methodIndex);
                Debug.Assert(!localNames.IsDefault);

                var localInfo = default(ImmutableArray <MetadataDecoder.LocalInfo>);
                try
                {
                    Debug.Assert(this.module.HasIL);
                    var methodBody = this.module.GetMethodBodyOrThrow(handle);

                    if (!methodBody.LocalSignature.IsNil)
                    {
                        var signatureHandle = this.module.MetadataReader.GetLocalSignature(methodBody.LocalSignature);
                        var signatureReader = this.module.GetMemoryReaderOrThrow(signatureHandle);
                        localInfo = this.metadataDecoder.DecodeLocalSignatureOrThrow(ref signatureReader);
                    }
                    else
                    {
                        localInfo = ImmutableArray <MetadataDecoder.LocalInfo> .Empty;
                    }
                }
                catch (UnsupportedSignatureContent)
                {
                }
                catch (BadImageFormatException)
                {
                }

                if (localInfo.IsDefault)
                {
                    // TODO: Report error that metadata is not supported.
                    return(false);
                }
                else
                {
                    // The signature may have more locals than names if trailing locals are unnamed.
                    // (Locals in the middle of the signature may be unnamed too but since localNames
                    // is indexed by slot, unnamed locals before the last named local will be represented
                    // as null values in the array.)
                    Debug.Assert(localInfo.Length >= localNames.Length);
                    previousLocals = GetLocalSlots(previousMethod, localNames, localInfo);
                    Debug.Assert(previousLocals.Length == localInfo.Length);
                }

                map = this.mapToMetadata;
            }

            // Find declarators in previous method syntax.
            // The locals are indices into this list.
            var previousDeclarators = LocalVariableDeclaratorsCollector.GetDeclarators(previousMethod);

            // Create a map from declarator to declarator offset.
            var previousDeclaratorToOffset = new Dictionary <SyntaxNode, int>();

            for (int offset = 0; offset < previousDeclarators.Length; offset++)
            {
                previousDeclaratorToOffset.Add(previousDeclarators[offset], offset);
            }

            // Create a map from local info to slot.
            var previousLocalInfoToSlot = new Dictionary <EncLocalInfo, int>();

            for (int slot = 0; slot < previousLocals.Length; slot++)
            {
                var localInfo = previousLocals[slot];
                Debug.Assert(!localInfo.IsDefault);
                if (localInfo.IsInvalid)
                {
                    // Unrecognized or deleted local.
                    continue;
                }
                previousLocalInfoToSlot.Add(localInfo, slot);
            }

            var syntaxMap = methodEntry.SyntaxMap;

            if (syntaxMap == null)
            {
                // If there was no syntax map, the syntax structure has not changed,
                // so we can map from current to previous syntax by declarator index.
                Debug.Assert(methodEntry.PreserveLocalVariables);
                // Create a map from declarator to declarator index.
                var currentDeclarators       = LocalVariableDeclaratorsCollector.GetDeclarators(method);
                var currentDeclaratorToIndex = CreateDeclaratorToIndexMap(currentDeclarators);
                syntaxMap = currentSyntax =>
                {
                    var currentIndex = currentDeclaratorToIndex[(CSharpSyntaxNode)currentSyntax];
                    return(previousDeclarators[currentIndex]);
                };
            }

            getPreviousLocalSlot = (object identity, Cci.ITypeReference typeRef, LocalSlotConstraints constraints) =>
            {
                var local      = (LocalSymbol)identity;
                var syntaxRefs = local.DeclaringSyntaxReferences;
                Debug.Assert(!syntaxRefs.IsDefault);

                if (!syntaxRefs.IsDefaultOrEmpty)
                {
                    var currentSyntax  = syntaxRefs[0].GetSyntax();
                    var previousSyntax = (CSharpSyntaxNode)syntaxMap(currentSyntax);
                    if (previousSyntax != null)
                    {
                        int offset;
                        if (previousDeclaratorToOffset.TryGetValue(previousSyntax, out offset))
                        {
                            var previousType = map.MapReference(typeRef);
                            if (previousType != null)
                            {
                                var localKey = new EncLocalInfo(offset, previousType, constraints, (int)local.SynthesizedLocalKind, signature: null);
                                int slot;
                                // Should report a warning if the type of the local has changed
                                // and the previous value will be dropped. (Bug #781309.)
                                if (previousLocalInfoToSlot.TryGetValue(localKey, out slot))
                                {
                                    return(slot);
                                }
                            }
                        }
                    }
                }

                return(-1);
            };

            return(true);
        }
Beispiel #2
0
        internal override VariableSlotAllocator TryCreateVariableSlotAllocator(EmitBaseline baseline, IMethodSymbol method)
        {
            MethodHandle handle;

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

            MethodDefinitionEntry methodEntry;

            if (!this.methodMap.TryGetValue(method, out methodEntry))
            {
                // Not part of changeset. No need to preserve locals.
                return(null);
            }

            if (!methodEntry.PreserveLocalVariables)
            {
                // Not necessary to preserve locals.
                return(null);
            }

            CSharpSymbolMatcher           symbolMap;
            ImmutableArray <EncLocalInfo> previousLocals;

            uint methodIndex = (uint)MetadataTokens.GetRowNumber(handle);

            // Check if method has changed previously. If so, we already have a map.
            if (baseline.LocalsForMethodsAddedOrChanged.TryGetValue(methodIndex, out previousLocals))
            {
                symbolMap = this.mapToPrevious;
            }
            else
            {
                // Method has not changed since initial generation. Generate a map
                // using the local names provided with the initial metadata.
                var localNames = baseline.LocalNames(handle);
                Debug.Assert(!localNames.IsDefault);

                ImmutableArray <MetadataDecoder.LocalInfo> localInfo;
                if (!metadataDecoder.TryGetLocals(handle, out localInfo))
                {
                    // TODO: Report error that metadata is not supported.
                    return(null);
                }

                // The signature may have more locals than names if trailing locals are unnamed.
                // (Locals in the middle of the signature may be unnamed too but since localNames
                // is indexed by slot, unnamed locals before the last named local will be represented
                // as null values in the array.)
                Debug.Assert(localInfo.Length >= localNames.Length);

                previousLocals = GetLocalSlots(methodEntry.PreviousMethod, localNames, localInfo);
                Debug.Assert(previousLocals.Length == localInfo.Length);
                symbolMap = this.mapToMetadata;
            }

            // Find declarators in previous method syntax.
            // The locals are indices into this list.
            var previousDeclarators = LocalVariableDeclaratorsCollector.GetDeclarators(methodEntry.PreviousMethod);

            var syntaxMap = methodEntry.SyntaxMap;

            if (syntaxMap == null)
            {
                // If there was no syntax map, the syntax structure has not changed,
                // so we can map from current to previous syntax by declarator index.
                Debug.Assert(methodEntry.PreserveLocalVariables);

                // Create a map from declarator to declarator index.
                var currentDeclarators       = LocalVariableDeclaratorsCollector.GetDeclarators(method);
                var currentDeclaratorToIndex = CreateDeclaratorToIndexMap(currentDeclarators);

                syntaxMap = currentSyntax =>
                {
                    var currentIndex = currentDeclaratorToIndex[currentSyntax];
                    return(previousDeclarators[currentIndex]);
                };
            }

            return(new EncVariableSlotAllocator(symbolMap, syntaxMap, previousDeclarators, previousLocals));
        }