Add support for emitting finally regions in ILEmitter (dotnet/coreclr#27109)
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>
Fri, 11 Oct 2019 15:16:53 +0000 (17:16 +0200)
committerJan Kotas <jkotas@microsoft.com>
Fri, 11 Oct 2019 15:16:53 +0000 (08:16 -0700)
This will be needed to properly do cleanups in marshalling stubs.

The usage goes like (I couldn't come up with anything better):

```
var b = codeStream.BeginTry();
//...
codeStream.EndTry(b);
codeStream.BeginHandler(b);
// ...
codeStream.EndHandler(b);

emit.NewFinallyRegion(b);
```

Commit migrated from https://github.com/dotnet/coreclr/commit/a7ffbba5a2843e7da03fdffe40ac85b9adfe126e

src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/MethodILDebugView.cs
src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/Stubs/ILEmitter.cs

index 1f5fbcb..e0d5675 100644 (file)
@@ -92,10 +92,45 @@ namespace Internal.IL
                 }
                 sb.AppendLine();
 
-                // TODO: exception regions
+                const string pad = "  ";
+
+                // TODO: pretty exception regions
+                foreach (ILExceptionRegion region in _methodIL.GetExceptionRegions())
+                {
+                    sb.Append(pad);
+                    sb.Append(".try ");
+                    ILDisassembler.AppendOffset(sb, region.TryOffset);
+                    sb.Append(" to ");
+                    ILDisassembler.AppendOffset(sb, region.TryOffset + region.TryLength);
+                                        
+                    switch (region.Kind)
+                    {
+                        case ILExceptionRegionKind.Catch:
+                            sb.Append(" catch ");
+                            disasm.AppendType(sb, (TypeDesc)_methodIL.GetObject(region.ClassToken));
+                            break;
+                        case ILExceptionRegionKind.Fault:
+                            sb.Append(" fault");
+                            break;
+                        case ILExceptionRegionKind.Filter:
+                            sb.Append(" filter ");
+                            ILDisassembler.AppendOffset(sb, region.FilterOffset);
+                            break;
+                        case ILExceptionRegionKind.Finally:
+                            sb.Append(" finally");
+                            break;
+                    }
+
+                    sb.Append(" handler ");
+                    ILDisassembler.AppendOffset(sb, region.HandlerOffset);
+                    sb.Append(" to ");
+                    ILDisassembler.AppendOffset(sb, region.HandlerOffset + region.HandlerLength);
+                    sb.AppendLine();
+                }
+
                 while (disasm.HasNextInstruction)
                 {
-                    sb.Append("  ");
+                    sb.Append(pad);
                     sb.AppendLine(disasm.GetNextInstruction());
                 }
 
index 45627bd..ec272c8 100644 (file)
@@ -14,6 +14,8 @@ namespace Internal.IL.Stubs
 {
     public class ILCodeStream
     {
+        private const int StartOffsetNotSet = -1;
+
         private struct LabelAndOffset
         {
             public readonly ILCodeLabel Label;
@@ -37,10 +39,16 @@ namespace Internal.IL.Stubs
         internal ILCodeStream(ILEmitter emitter)
         {
             _instructions = Array.Empty<byte>();
-            _startOffsetForLinking = -1;
+            _startOffsetForLinking = StartOffsetNotSet;
             _emitter = emitter;
         }
 
+        internal int RelativeToAbsoluteOffset(int relativeOffset)
+        {
+            Debug.Assert(_startOffsetForLinking != StartOffsetNotSet);
+            return _startOffsetForLinking + relativeOffset;
+        }
+
         private void EmitByte(byte b)
         {
             if (_instructions.Length == _length)
@@ -417,6 +425,34 @@ namespace Internal.IL.Stubs
             label.Place(this, _length);
         }
 
+        public void BeginTry(ILExceptionRegionBuilder builder)
+        {
+            Debug.Assert(builder._beginTryStream == null);
+            builder._beginTryStream = this;
+            builder._beginTryOffset = _length;
+        }
+
+        public void EndTry(ILExceptionRegionBuilder builder)
+        {
+            Debug.Assert(builder._endTryStream == null);
+            builder._endTryStream = this;
+            builder._endTryOffset = _length;
+        }
+
+        public void BeginHandler(ILExceptionRegionBuilder builder)
+        {
+            Debug.Assert(builder._beginHandlerStream == null);
+            builder._beginHandlerStream = this;
+            builder._beginHandlerOffset = _length;
+        }
+
+        public void EndHandler(ILExceptionRegionBuilder builder)
+        {
+            Debug.Assert(builder._endHandlerStream == null);
+            builder._endHandlerStream = this;
+            builder._endHandlerOffset = _length;
+        }
+
         internal void PatchLabels()
         {
             for (int i = 0; i < _offsetsNeedingPatching.Count; i++)
@@ -424,7 +460,7 @@ namespace Internal.IL.Stubs
                 LabelAndOffset patch = _offsetsNeedingPatching[i];
 
                 Debug.Assert(patch.Label.IsPlaced);
-                Debug.Assert(_startOffsetForLinking > -1);
+                Debug.Assert(_startOffsetForLinking != StartOffsetNotSet);
 
                 int offset = patch.Offset;
 
@@ -456,6 +492,34 @@ namespace Internal.IL.Stubs
         }
     }
 
+    public class ILExceptionRegionBuilder
+    {
+        internal ILCodeStream _beginTryStream;
+        internal int _beginTryOffset;
+
+        internal ILCodeStream _endTryStream;
+        internal int _endTryOffset;
+
+        internal ILCodeStream _beginHandlerStream;
+        internal int _beginHandlerOffset;
+
+        internal ILCodeStream _endHandlerStream;
+        internal int _endHandlerOffset;
+
+        internal ILExceptionRegionBuilder()
+        {
+        }
+
+        internal int TryOffset => _beginTryStream.RelativeToAbsoluteOffset(_beginTryOffset);
+        internal int TryLength => _endTryStream.RelativeToAbsoluteOffset(_endTryOffset) - TryOffset;
+        internal int HandlerOffset => _beginHandlerStream.RelativeToAbsoluteOffset(_beginHandlerOffset);
+        internal int HandlerLength => _endHandlerStream.RelativeToAbsoluteOffset(_endHandlerOffset) - HandlerOffset;
+        
+        internal bool IsDefined =>
+            _beginTryStream != null && _endTryStream != null
+            && _beginHandlerStream != null && _endHandlerStream != null;
+    }
+
     /// <summary>
     /// Represent a token. Use one of the overloads of <see cref="ILEmitter.NewToken"/>
     /// to create a new token.
@@ -473,12 +537,13 @@ namespace Internal.IL.Stubs
         private readonly LocalVariableDefinition[] _locals;
         private readonly Object[] _tokens;
         private readonly MethodDesc _method;
+        private readonly ILExceptionRegion[] _exceptionRegions;
         private readonly MethodDebugInformation _debugInformation;
 
         private const int MaxStackNotSet = -1;
         private int _maxStack;
 
-        public ILStubMethodIL(MethodDesc owningMethod, byte[] ilBytes, LocalVariableDefinition[] locals, Object[] tokens, MethodDebugInformation debugInfo = null)
+        public ILStubMethodIL(MethodDesc owningMethod, byte[] ilBytes, LocalVariableDefinition[] locals, Object[] tokens, ILExceptionRegion[] exceptionRegions = null, MethodDebugInformation debugInfo = null)
         {
             _ilBytes = ilBytes;
             _locals = locals;
@@ -486,6 +551,10 @@ namespace Internal.IL.Stubs
             _method = owningMethod;
             _maxStack = MaxStackNotSet;
 
+            if (exceptionRegions == null)
+                exceptionRegions = Array.Empty<ILExceptionRegion>();
+            _exceptionRegions = exceptionRegions;
+
             if (debugInfo == null)
                 debugInfo = MethodDebugInformation.None;
             _debugInformation = debugInfo;
@@ -531,7 +600,7 @@ namespace Internal.IL.Stubs
 
         public override ILExceptionRegion[] GetExceptionRegions()
         {
-            return Array.Empty<ILExceptionRegion>();
+            return _exceptionRegions;
         }
         public override bool IsInitLocals
         {
@@ -569,8 +638,7 @@ namespace Internal.IL.Stubs
             get
             {
                 Debug.Assert(IsPlaced);
-                Debug.Assert(_codeStream._startOffsetForLinking >= 0);
-                return _codeStream._startOffsetForLinking + _offsetWithinCodeStream;
+                return _codeStream.RelativeToAbsoluteOffset(_offsetWithinCodeStream);
             }
         }
 
@@ -591,6 +659,7 @@ namespace Internal.IL.Stubs
         private ArrayBuilder<ILCodeStream> _codeStreams;
         private ArrayBuilder<LocalVariableDefinition> _locals;
         private ArrayBuilder<Object> _tokens;
+        private ArrayBuilder<ILExceptionRegionBuilder> _finallyRegions;
 
         public ILEmitter()
         {
@@ -648,6 +717,13 @@ namespace Internal.IL.Stubs
             return newLabel;
         }
 
+        public ILExceptionRegionBuilder NewFinallyRegion()
+        {
+            var region = new ILExceptionRegionBuilder();
+            _finallyRegions.Add(region);
+            return region;
+        }
+
         public MethodIL Link(MethodDesc owningMethod)
         {
             int totalLength = 0;
@@ -694,7 +770,26 @@ namespace Internal.IL.Stubs
                 debugInfo = new EmittedMethodDebugInformation(sequencePoints);
             }
 
-            var result = new ILStubMethodIL(owningMethod, ilInstructions, _locals.ToArray(), _tokens.ToArray(), debugInfo);
+            ILExceptionRegion[] exceptionRegions = null;
+
+            int numberOfExceptionRegions = _finallyRegions.Count;
+            if (numberOfExceptionRegions > 0)
+            {
+                exceptionRegions = new ILExceptionRegion[numberOfExceptionRegions];
+
+                for (int i = 0; i < _finallyRegions.Count; i++)
+                {
+                    ILExceptionRegionBuilder region = _finallyRegions[i];
+
+                    Debug.Assert(region.IsDefined);
+
+                    exceptionRegions[i] = new ILExceptionRegion(ILExceptionRegionKind.Finally,
+                        region.TryOffset, region.TryLength, region.HandlerOffset, region.HandlerLength,
+                        classToken: 0, filterOffset: 0);
+                }
+            }
+
+            var result = new ILStubMethodIL(owningMethod, ilInstructions, _locals.ToArray(), _tokens.ToArray(), exceptionRegions, debugInfo);
             result.CheckStackBalance();
             return result;
         }