@@ -109,6 +109,9 @@ inline PTR_DynamicMethodDesc MethodDesc::AsDynamicMethodDesc()
109109 return dac_cast<PTR_DynamicMethodDesc>(this );
110110}
111111
112+ // NOTE: Not all methods that create IL at runtime are considered dynamic methods. This only returns TRUE
113+ // for methods represented by DynamicMethodDesc. Transient IL (see TryGenerateTransientILImplementation) also
114+ // generates IL at runtime but returns FALSE here.
112115inline bool MethodDesc::IsDynamicMethod ()
113116{
114117 LIMITED_METHOD_CONTRACT;
@@ -121,16 +124,35 @@ inline bool MethodDesc::IsLCGMethod()
121124 return ((mcDynamic == GetClassification ()) && dac_cast<PTR_DynamicMethodDesc>(this )->IsLCGMethod ());
122125}
123126
127+ // NOTE: This method only detects the subset of ILStubs that are internally represented using DynamicMethodDesc.
128+ // There are other methods compiled from IL generated at runtime via ILStubLinker that still return FALSE here.
129+ // See TryGenerateTransientILImplementation.
124130inline bool MethodDesc::IsILStub ()
125131{
126132 WRAPPER_NO_CONTRACT;
127133 return ((mcDynamic == GetClassification ()) && dac_cast<PTR_DynamicMethodDesc>(this )->IsILStub ());
128134}
129135
136+ // This method is intended to identify methods that aren't shown in diagnostic introspection (stacktraces,
137+ // code viewing, stepping, etc). Partly this is a user experience consideration to preserve the
138+ // abstraction users would expect based on source code and assembly contents. Partly it is also a technical
139+ // limitation that many parts of diagnostics don't know how to work with methods that aren't backed by
140+ // metadata and IL in a module.
130141inline bool MethodDesc::IsDiagnosticsHidden ()
131142{
143+ // Although good user experience can be subjective these are guidelines:
144+ // - We don't want stacktraces to show extra frames when the IL only shows a single call was made. If our runtime stackwalker
145+ // is going to include multiple frames pointing at different MethodDescs then all but one of them should be hidden.
146+ // - In most cases when multiple MethodDescs occur for the same IL call, one of them is a MethodDesc that
147+ // compiled original IL defined in the module (for its default code version at least). That is the one we want to show
148+ // and the rest should be hidden.
149+ // - In other cases the user defines methods in their source code but provides no implementation (for
150+ // example interop calls, Array methods, Delegate.Invoke, or UnsafeAccessor). Ideally a filtered stacktrace would include exactly
151+ // one frame for these calls as well but we haven't always done this consistently. For calls that redirect to another managed method users
152+ // tolerate if the runtime-implemented frame is missing because they can still see the managed target method.
153+
132154 WRAPPER_NO_CONTRACT;
133- return IsILStub () || IsAsyncThunkMethod ();
155+ return IsILStub () || IsAsyncThunkMethod () || IsWrapperStub () ;
134156}
135157
136158inline BOOL MethodDesc::IsQCall ()
0 commit comments