Ejemplo n.º 1
0
        void UpdateLocalScopes(Instruction removedInstruction, Instruction existingInstruction)
        {
            var debug_info = method.debug_info;

            if (debug_info == null)
            {
                return;
            }

            // Local scopes store start/end pair of "instruction offsets". Instruction offset can be either resolved, in which case it
            // has a reference to Instruction, or unresolved in which case it stores numerical offset (instruction offset in the body).
            // Typically local scopes loaded from PE/PDB files will be resolved, but it's not a requirement.
            // Each instruction has its own offset, which is populated on load, but never updated (this would be pretty expensive to do).
            // Instructions created during the editting will typically have offset 0 (so incorrect).
            // Local scopes created during editing will also likely be resolved (so no numerical offsets).
            // So while local scopes which are unresolved are relatively rare if they appear, manipulating them based
            // on the offsets allone is pretty hard (since we can't rely on correct offsets of instructions).
            // On the other hand resolved local scopes are easy to maintain, since they point to instructions and thus inserting
            // instructions is basically a no-op and removing instructions is as easy as changing the pointer.
            // For this reason the algorithm here is:
            //  - First make sure that all instruction offsets are resolved - if not - resolve them
            //     - First time this will be relatively expensinve as it will walk the entire method body to convert offsets to instruction pointers
            //       Almost all local scopes are stored in the "right" order (sequentially per start offsets), so the code uses a simple one-item
            //       cache instruction<->offset to avoid walking instructions multiple times (that would only happen for scopes which are out of order).
            //     - Subsequent calls should be cheap as it will only walk all local scopes without doing anything
            //     - If there was an edit on local scope which makes some of them unresolved, the cost is proportional
            //  - Then update as necessary by manipulaitng instruction references alone

            InstructionOffsetCache cache = new InstructionOffsetCache()
            {
                Offset      = 0,
                Index       = 0,
                Instruction = items [0]
            };

            UpdateLocalScope(debug_info.Scope, removedInstruction, existingInstruction, ref cache);
        }
Ejemplo n.º 2
0
        InstructionOffset ResolveInstructionOffset(InstructionOffset inputOffset, ref InstructionOffsetCache cache)
        {
            if (inputOffset.IsResolved)
            {
                return(inputOffset);
            }

            int offset = inputOffset.Offset;

            if (cache.Offset == offset)
            {
                return(new InstructionOffset(cache.Instruction));
            }

            if (cache.Offset > offset)
            {
                // This should be rare - we're resolving offset pointing to a place before the current cache position
                // resolve by walking the instructions from start and don't cache the result.
                int size = 0;
                for (int i = 0; i < items.Length; i++)
                {
                    if (size == offset)
                    {
                        return(new InstructionOffset(items [i]));
                    }

                    if (size > offset)
                    {
                        return(new InstructionOffset(items [i - 1]));
                    }

                    size += items [i].GetSize();
                }

                // Offset is larger than the size of the body - so it points after the end
                return(new InstructionOffset());
            }
            else
            {
                // The offset points after the current cache position - so continue counting and update the cache
                int size = cache.Offset;
                for (int i = cache.Index; i < items.Length; i++)
                {
                    cache.Index       = i;
                    cache.Offset      = size;
                    cache.Instruction = items [i];

                    if (cache.Offset == offset)
                    {
                        return(new InstructionOffset(cache.Instruction));
                    }

                    if (cache.Offset > offset)
                    {
                        return(new InstructionOffset(items [i - 1]));
                    }

                    size += items [i].GetSize();
                }

                return(new InstructionOffset());
            }
        }
Ejemplo n.º 3
0
        void UpdateLocalScope(ScopeDebugInformation scope, Instruction removedInstruction, Instruction existingInstruction, ref InstructionOffsetCache cache)
        {
            if (scope == null)
            {
                return;
            }

            if (!scope.Start.IsResolved)
            {
                scope.Start = ResolveInstructionOffset(scope.Start, ref cache);
            }

            if (!scope.Start.IsEndOfMethod && scope.Start.ResolvedInstruction == removedInstruction)
            {
                scope.Start = new InstructionOffset(existingInstruction);
            }

            if (scope.HasScopes)
            {
                foreach (var subScope in scope.Scopes)
                {
                    UpdateLocalScope(subScope, removedInstruction, existingInstruction, ref cache);
                }
            }

            if (!scope.End.IsResolved)
            {
                scope.End = ResolveInstructionOffset(scope.End, ref cache);
            }

            if (!scope.End.IsEndOfMethod && scope.End.ResolvedInstruction == removedInstruction)
            {
                scope.End = new InstructionOffset(existingInstruction);
            }
        }
Ejemplo n.º 4
0
        InstructionOffset ResolveInstructionOffset(InstructionOffset inputOffset, ref InstructionOffsetCache cache)
        {
            if (inputOffset.IsResolved)
            {
                return(inputOffset);
            }

            int offset = inputOffset.Offset;

            if (cache.Offset == offset)
            {
                return(new InstructionOffset(cache.Instruction));
            }

            if (cache.Offset > offset)
            {
                // This should be rare - we're resolving offset pointing to a place before the current cache position
                // resolve by walking the instructions from start and don't cache the result.
                int size = 0;
                for (int i = 0; i < items.Length; i++)
                {
                    // The array can be larger than the actual size, in which case its padded with nulls at the end
                    // so when we reach null, treat it as an end of the IL.
                    if (items [i] == null)
                    {
                        return(new InstructionOffset(i == 0 ? items [0] : items [i - 1]));
                    }

                    if (size == offset)
                    {
                        return(new InstructionOffset(items [i]));
                    }

                    if (size > offset)
                    {
                        return(new InstructionOffset(i == 0 ? items [0] : items [i - 1]));
                    }

                    size += items [i].GetSize();
                }

                // Offset is larger than the size of the body - so it points after the end
                return(new InstructionOffset());
            }
            else
            {
                // The offset points after the current cache position - so continue counting and update the cache
                int size = cache.Offset;
                for (int i = cache.Index; i < items.Length; i++)
                {
                    cache.Index  = i;
                    cache.Offset = size;

                    var item = items [i];

                    // Allow for trailing null values in the case of
                    // instructions.Size < instructions.Capacity
                    if (item == null)
                    {
                        return(new InstructionOffset(i == 0 ? items [0] : items [i - 1]));
                    }

                    cache.Instruction = item;

                    if (cache.Offset == offset)
                    {
                        return(new InstructionOffset(cache.Instruction));
                    }

                    if (cache.Offset > offset)
                    {
                        return(new InstructionOffset(i == 0 ? items [0] : items [i - 1]));
                    }

                    size += item.GetSize();
                }

                return(new InstructionOffset());
            }
        }