Skip to content

Commit

Permalink
[mono] Fix StackTrace from a dim and Vtable offsets for static interf…
Browse files Browse the repository at this point in the history
…ace method (#60770)

* Fix mono_get_generic_context_from_stack_frame when it's from a dim.

* Using @vargaz fix

* Fixing test case source name

* Fix behavior when the interface had static interface methods.

* Fixing test cases.
  • Loading branch information
thaystg authored Dec 2, 2021
1 parent 252b7c2 commit 5789792
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/mono/mono/metadata/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -1518,6 +1518,8 @@ build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_
* add_imt_builder_entry anyway.
*/
method = mono_class_get_method_by_index (mono_class_get_generic_class (iface)->container_class, method_slot_in_interface);
if (m_method_is_static (method))
continue;
if (mono_method_get_imt_slot (method) != slot_num) {
vt_slot ++;
continue;
Expand Down
11 changes: 9 additions & 2 deletions src/mono/mono/mini/mini-exceptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -840,12 +840,13 @@ mono_get_generic_context_from_stack_frame (MonoJitInfo *ji, gpointer generic_inf

method = jinfo_get_method (ji);
g_assert (method->is_inflated);
if (mono_method_get_context (method)->method_inst) {
if (mono_method_get_context (method)->method_inst || mini_method_is_default_method (method)) {
MonoMethodRuntimeGenericContext *mrgctx = (MonoMethodRuntimeGenericContext *)generic_info;

klass = mrgctx->class_vtable->klass;
context.method_inst = mrgctx->method_inst;
g_assert (context.method_inst);
if (!mini_method_is_default_method (method))
g_assert (context.method_inst);
} else {
MonoVTable *vtable = (MonoVTable *)generic_info;

Expand All @@ -858,6 +859,12 @@ mono_get_generic_context_from_stack_frame (MonoJitInfo *ji, gpointer generic_inf
else
method_container_class = method->klass;

if (mini_method_is_default_method (method)) {
if (mono_class_is_ginst (klass) || mono_class_is_gtd (klass))
context.class_inst = mini_class_get_context (klass)->class_inst;
return context;
}

/* class might refer to a subclass of method's class */
while (!(klass == method->klass || (mono_class_is_ginst (klass) && mono_class_get_generic_class (klass)->container_class == method_container_class))) {
klass = m_class_get_parent (klass);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
using System;
using System.Runtime.CompilerServices;

public interface IPublisher<out TData>
{
event Action<TData> OnPublish;
}

public interface TestItf1<TT>
{
[MethodImpl(MethodImplOptions.NoInlining)]
void TestMethod1(IPublisher<TT> publisher, StackFrame[] expectedFrames)
{
StackFrame.Validate(Environment.StackTrace, expectedFrames);
}

[MethodImpl(MethodImplOptions.NoInlining)]
void TestMethod2(IPublisher<TT> publisher, StackFrame[] expectedFrames)
{
TestMethod3(this, publisher, expectedFrames);
}

[MethodImpl(MethodImplOptions.NoInlining)]
protected static void TestMethod3(TestItf1<TT> subscriber, IPublisher<TT> publisher, StackFrame[] expectedFrames)
{
StackFrame.Validate(Environment.StackTrace, expectedFrames);
}

[MethodImpl(MethodImplOptions.NoInlining)]
void TestMethod4(IPublisher<TT> publisher, StackFrame[] expectedFrames)
{
TestMethod3(this, publisher, expectedFrames);
}

[MethodImpl(MethodImplOptions.NoInlining)]
void TestMethod5(IPublisher<TT> publisher, StackFrame[] expectedFrames)
{
TestMethod3(this, publisher, expectedFrames);
}

[MethodImpl(MethodImplOptions.NoInlining)]
void TestMethod10(IPublisher<TT> publisher, StackFrame[] expectedFrames)
{
TestMethod3(this, publisher, expectedFrames);
}

void TestMethod11(IPublisher<TT> publisher, StackFrame[] expectedFrames);
}

public interface TestItf2<TT> : TestItf1<TT>
{
[MethodImpl(MethodImplOptions.NoInlining)]
void TestItf1<TT>.TestMethod5(IPublisher<TT> publisher, StackFrame[] expectedFrames)
{
TestMethod3(this, publisher, expectedFrames);
}

[MethodImpl(MethodImplOptions.NoInlining)]
void TestItf1<TT>.TestMethod10(IPublisher<TT> publisher, StackFrame[] expectedFrames)
{
TestMethod3(this, publisher, expectedFrames);
}
}

public interface TestItf3<TT> : TestItf1<TT>
{
[MethodImpl(MethodImplOptions.NoInlining)]
void TestMethod6(IPublisher<TT> publisher, StackFrame[] expectedFrames)
{
TestMethod3(this, publisher, expectedFrames);
}

[MethodImpl(MethodImplOptions.NoInlining)]
void TestMethod7(IPublisher<TT> publisher, StackFrame[] expectedFrames)
{
TestMethod8(this, publisher, expectedFrames);
}

[MethodImpl(MethodImplOptions.NoInlining)]
protected static void TestMethod8(TestItf1<TT> subscriber, IPublisher<TT> publisher, StackFrame[] expectedFrames)
{
StackFrame.Validate(Environment.StackTrace, expectedFrames);
}

void TestMethod9(IPublisher<TT> publisher, StackFrame[] expectedFrames);
}

public interface TestItf4<TT> : TestItf3<TT>
{
[MethodImpl(MethodImplOptions.NoInlining)]
void TestItf3<TT>.TestMethod9(IPublisher<TT> publisher, StackFrame[] expectedFrames)
{
TestMethod8(this, publisher, expectedFrames);
}
}

public class ProgramBase<TT> : TestItf4<TT>
{
[MethodImpl(MethodImplOptions.NoInlining)]
public void TestMethod10(IPublisher<TT> publisher, StackFrame[] expectedFrames)
{
TestItf1<TT>.TestMethod3(this, publisher, expectedFrames);
}

[MethodImpl(MethodImplOptions.NoInlining)]
public void TestMethod11(IPublisher<TT> publisher, StackFrame[] expectedFrames)
{
TestItf1<TT>.TestMethod3(this, publisher, expectedFrames);
}
}

public class Program : ProgramBase<InputData>, TestItf2<InputData>
{
static int Main(string[] args)
{
new Program().Start();
return 100;
}

public void Start()
{
var t1 = this as TestItf1<InputData>;
t1.TestMethod1(null, new[] { new StackFrame("TestItf1`1", "TestMethod1") });
t1.TestMethod2(null, new[] { new StackFrame("TestItf1`1", "TestMethod3"), new StackFrame("TestItf1`1", "TestMethod2") });
t1.TestMethod4(null, new[] { new StackFrame("TestItf1`1", "TestMethod3"), new StackFrame("Program", "TestMethod4") });
t1.TestMethod5(null, new[] { new StackFrame("TestItf1`1", "TestMethod3"), new StackFrame(new[] { "TestItf2`1", "TestItf1" }, "TestMethod5") });

var t3 = this as TestItf3<InputData>;
t3.TestMethod6(null, new[] { new StackFrame("TestItf1`1", "TestMethod3"), new StackFrame("TestItf3`1", "TestMethod6") });
t3.TestMethod7(null, new[] { new StackFrame("TestItf3`1", "TestMethod8"), new StackFrame("TestItf3`1", "TestMethod7") });
t3.TestMethod9(null, new[] { new StackFrame("TestItf3`1", "TestMethod8"), new StackFrame(new[] { "TestItf4`1", "TestItf3" }, "TestMethod9") });

t1.TestMethod10(null, new[] { new StackFrame("TestItf1`1", "TestMethod3"), new StackFrame("ProgramBase`1", "TestMethod10") });
t1.TestMethod11(null, new[] { new StackFrame("TestItf1`1", "TestMethod3"), new StackFrame("ProgramBase`1", "TestMethod11") });
}

[MethodImpl(MethodImplOptions.NoInlining)]
public void TestMethod4(IPublisher<InputData> publisher, StackFrame[] expectedFrames)
{
TestItf1<InputData>.TestMethod3(this, publisher, expectedFrames);
}
}

public class InputData
{
public int i;
}

public class StackFrame
{
public string [] ClassName { get; set; }
public string MethodName { get; set; } = string.Empty;

public StackFrame(string [] className, string methodName)
{
ClassName = className;
MethodName = methodName;
}

public StackFrame(string className, string methodName)
{
ClassName = new string[] { className };
MethodName = methodName;
}

public static void Validate(string testStack, StackFrame[] expectedFrames)
{
int index = 1;

string[] lines = testStack.Split(
new string[] { Environment.NewLine },
StringSplitOptions.None
);

//Console.WriteLine(testStack);

foreach (var frame in expectedFrames)
{
var line = lines[index++].Trim();


if (!line.StartsWith($"at {frame.ClassName[0]}") || !line.Contains($".{frame.MethodName}") || (frame.ClassName.Length > 1 && !line.Contains($".{frame.ClassName[1]}")))
{
Console.WriteLine($"Expected {frame.ClassName}.{frame.MethodName} but got {line}");
Console.WriteLine(testStack);
Environment.Exit(1);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputType>Exe</OutputType>
<CLRTestKind>BuildAndRun</CLRTestKind>
<CLRTestPriority>0</CLRTestPriority>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
</Project>

0 comments on commit 5789792

Please sign in to comment.