Skip to content

Commit 477fe1f

Browse files
committed
fix: handling related to delegated closures
1 parent 0eaa411 commit 477fe1f

19 files changed

Lines changed: 534 additions & 251 deletions

src/OTAPI.UnifiedServerProcess/Constants.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public static class Modifiers
3131
}
3232
public static class Patching
3333
{
34-
public const string ConvertedFieldInSingletonSuffix = "_ReusedField";
34+
public const string ConvertedFieldInSingletonSuffix = "_ReusedField_Placeholder";
3535
}
3636
}
3737
}

src/OTAPI.UnifiedServerProcess/Core/Analysis/DataModels/MemberAccess/CollectionElementLayer.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public static bool IsStoreElementMethod(TypeInheritanceGraph graph, MethodRefere
7575
}
7676

7777
LazyInit(caller.Module);
78-
var inheritancesTypes = graph.GetInheritancesTypes(resolvedStoreMethod.DeclaringType);
78+
var inheritancesTypes = graph.GetInheritanceTypes(resolvedStoreMethod.DeclaringType);
7979

8080
if (inheritancesTypes.ContainsKey(IDictionaryType.FullName) && (resolvedStoreMethod.Name is "Add" || resolvedStoreMethod.Name is "TryAddModifications")) {
8181
// 0: key, 1: value
@@ -133,7 +133,7 @@ public static bool IsLoadElementMethod(TypeInheritanceGraph graph, MethodReferen
133133
}
134134

135135
LazyInit(caller.Module);
136-
var inheritancesTypes = graph.GetInheritancesTypes(resolvedLoadMethod.DeclaringType);
136+
var inheritancesTypes = graph.GetInheritanceTypes(resolvedLoadMethod.DeclaringType);
137137

138138
if (inheritancesTypes.ContainsKey(IDictionaryType.FullName) && resolvedLoadMethod.Name is "TryGetValue") {
139139
// 0: key, 1: out value
@@ -177,7 +177,7 @@ public static bool IsModificationMethod(TypeInheritanceGraph graph, MethodRefere
177177
var resolvedModifyMethod = modifyMethod.Resolve();
178178
LazyInit(caller.Module);
179179

180-
var inheritancesTypes = graph.GetInheritancesTypes(resolvedModifyMethod.DeclaringType);
180+
var inheritancesTypes = graph.GetInheritanceTypes(resolvedModifyMethod.DeclaringType);
181181

182182
if (resolvedModifyMethod.Name is "set_Item" && resolvedModifyMethod.IsSpecialName) {
183183
return true;

src/OTAPI.UnifiedServerProcess/Core/Analysis/DataModels/MemberAccess/EnumeratorLayer.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ static TypeReference GetEnumerableType(ModuleDefinition module) {
2020
return enumerableType ??= module.ImportReference(typeof(IEnumerable<>));
2121
}
2222
public static bool IsEnumerator(TypeInheritanceGraph graph, TypeDefinition type) {
23-
var inheritance = graph.GetInheritancesTypes(type);
23+
var inheritance = graph.GetInheritanceTypes(type);
2424
if (inheritance.Count == 0) {
2525
return false;
2626
}
@@ -43,7 +43,7 @@ public static bool IsGetEnumeratorMethod(TypeInheritanceGraph graph, MethodRefer
4343
if (declaringType is null) {
4444
return false;
4545
}
46-
var inheritance = graph.GetInheritancesTypes(declaringType);
46+
var inheritance = graph.GetInheritanceTypes(declaringType);
4747
if (inheritance.Count == 0) {
4848
return false;
4949
}
@@ -66,7 +66,7 @@ public static bool IsGetCurrentMethod(TypeInheritanceGraph graph, MethodReferenc
6666
if (declaringType is null) {
6767
return false;
6868
}
69-
var inheritance = graph.GetInheritancesTypes(declaringType);
69+
var inheritance = graph.GetInheritanceTypes(declaringType);
7070
if (inheritance.Count == 0) {
7171
return false;
7272
}

src/OTAPI.UnifiedServerProcess/Core/Analysis/TypeInheritanceGraph.cs

Lines changed: 155 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,193 @@
11
using Mono.Cecil;
22
using OTAPI.UnifiedServerProcess.Extensions;
3+
using System;
4+
using System.Collections;
35
using System.Collections.Generic;
46

57
namespace OTAPI.UnifiedServerProcess.Core.Analysis
68
{
7-
public class TypeInheritanceGraph
9+
public sealed class TypeInheritanceGraph
810
{
911
public TypeInheritanceGraph(ModuleDefinition module) {
10-
TypeInheritanceChains = [];
11-
foreach (var type in GetTypesInInheritanceOrder(module)) {
12-
GetInheritancesTypes(type);
13-
}
12+
_typeInheritanceChains = new(StringComparer.Ordinal);
13+
_directDerivedTypes = new(StringComparer.Ordinal);
14+
_derivedTypeTrees = new(StringComparer.Ordinal);
15+
16+
// Precompute upward inheritance chains (base types + interfaces) for fast lookups.
17+
foreach (var type in GetTypesInInheritanceOrder(module))
18+
GetInheritanceTypes(type);
19+
20+
// Build a "base -> direct derived types" index (classes only) for descendant tree queries.
21+
foreach (var type in module.GetTypes())
22+
IndexDerivedType(type);
23+
24+
// Stabilize output order.
25+
foreach (var list in _directDerivedTypes.Values)
26+
list.Sort((a, b) => StringComparer.Ordinal.Compare(a.FullName, b.FullName));
1427
}
15-
public Dictionary<string, TypeDefinition> GetInheritancesTypes(TypeDefinition type) {
16-
if (TypeInheritanceChains.TryGetValue(type.FullName, out var result)) {
17-
return result;
18-
}
19-
var types = new Dictionary<string, TypeDefinition>();
28+
29+
/// <summary>
30+
/// Gets all upward reachable types for <paramref name="type"/> (itself + base types + implemented interfaces).
31+
/// Cached per type full name.
32+
/// </summary>
33+
public Dictionary<string, TypeDefinition> GetInheritanceTypes(TypeDefinition type) {
34+
35+
if (_typeInheritanceChains.TryGetValue(type.FullName, out var cached))
36+
return cached;
37+
38+
var types = new Dictionary<string, TypeDefinition>(StringComparer.Ordinal);
2039
var currentType = type;
40+
2141
while (currentType != null) {
2242
types.TryAdd(currentType.FullName, currentType);
2343

24-
foreach (var interfaceType in currentType.Interfaces) {
44+
foreach (var iface in currentType.Interfaces) {
45+
var resolved = iface.InterfaceType.TryResolve();
46+
if (resolved is null) continue;
47+
types.TryAdd(resolved.FullName, resolved);
48+
}
2549

26-
var resolvedInterfaceType = interfaceType.InterfaceType.TryResolve();
50+
currentType = currentType.BaseType?.TryResolve();
51+
}
2752

28-
if (resolvedInterfaceType is null) continue;
53+
_typeInheritanceChains.Add(type.FullName, types);
54+
return types;
55+
}
2956

30-
types.TryAdd(resolvedInterfaceType.FullName, resolvedInterfaceType);
31-
}
32-
currentType = currentType.BaseType?.TryResolve();
57+
/// <summary>
58+
/// Builds a derived-type tree rooted at <paramref name="root"/> (root included).
59+
/// Only supports class roots; interface roots are rejected to guarantee a tree.
60+
/// </summary>
61+
public TypeTreeNode GetDerivedTypeTree(TypeDefinition root) {
62+
if (root is null) throw new ArgumentNullException(nameof(root));
63+
if (root.IsInterface)
64+
throw new NotSupportedException("Interface roots are not supported because implementations form a DAG, not a tree.");
65+
66+
return BuildDerivedTypeTree(root);
67+
}
68+
69+
private void IndexDerivedType(TypeDefinition type) {
70+
// Exclude interfaces so System.Object won't incorrectly "own" all interfaces as children.
71+
if (type is null || type.IsInterface) return;
72+
73+
var baseType = type.BaseType?.TryResolve();
74+
if (baseType is null) return;
75+
76+
if (!_directDerivedTypes.TryGetValue(baseType.FullName, out var list)) {
77+
list = new List<TypeDefinition>();
78+
_directDerivedTypes.Add(baseType.FullName, list);
79+
}
80+
81+
list.Add(type);
82+
}
83+
84+
private TypeTreeNode BuildDerivedTypeTree(TypeDefinition root) {
85+
if (_derivedTypeTrees.TryGetValue(root.FullName, out var cached))
86+
return cached;
87+
88+
var children = new List<TypeTreeNode>();
89+
90+
if (_directDerivedTypes.TryGetValue(root.FullName, out var directChildren)) {
91+
foreach (var child in directChildren)
92+
children.Add(BuildDerivedTypeTree(child));
3393
}
34-
result = types;
35-
TypeInheritanceChains.Add(type.FullName, result);
36-
return result;
94+
95+
var node = new TypeTreeNode(root, children);
96+
_derivedTypeTrees.Add(root.FullName, node);
97+
return node;
3798
}
3899

39-
readonly Dictionary<string, Dictionary<string, TypeDefinition>> TypeInheritanceChains;
100+
private readonly Dictionary<string, Dictionary<string, TypeDefinition>> _typeInheritanceChains;
101+
private readonly Dictionary<string, List<TypeDefinition>> _directDerivedTypes;
102+
private readonly Dictionary<string, TypeTreeNode> _derivedTypeTrees;
103+
40104
private static List<TypeDefinition> GetTypesInInheritanceOrder(ModuleDefinition module) {
41-
var allTypes = new HashSet<TypeDefinition>();
105+
var visited = new HashSet<TypeDefinition>();
42106
var sorted = new List<TypeDefinition>();
43107

44-
foreach (var type in module.GetTypes()) {
45-
VisitType(type, allTypes, sorted);
46-
}
108+
foreach (var type in module.GetTypes())
109+
VisitType(type, visited, sorted);
47110

48111
return sorted;
49112
}
50113

51-
private static void VisitType(
52-
TypeDefinition type,
53-
HashSet<TypeDefinition> visited,
54-
List<TypeDefinition> sorted) {
114+
private static void VisitType(TypeDefinition type, HashSet<TypeDefinition> visited, List<TypeDefinition> sorted) {
55115
if (type is null || visited.Contains(type)) return;
56116

57117
var baseType = type.BaseType?.TryResolve();
58-
if (baseType != null && !visited.Contains(baseType)) {
118+
if (baseType != null && !visited.Contains(baseType))
59119
VisitType(baseType, visited, sorted);
60-
}
61120

62121
foreach (var iface in type.Interfaces) {
63-
var interfaceType = iface.InterfaceType.TryResolve();
64-
if (interfaceType is null) {
65-
continue;
66-
}
67-
VisitType(interfaceType, visited, sorted);
122+
var ifaceType = iface.InterfaceType.TryResolve();
123+
if (ifaceType is null) continue;
124+
VisitType(ifaceType, visited, sorted);
68125
}
69126

70-
if (visited.Add(type)) {
127+
if (visited.Add(type))
71128
sorted.Add(type);
129+
}
130+
}
131+
132+
public enum TreeTraversal
133+
{
134+
PreOrder,
135+
PostOrder,
136+
BreadthFirst
137+
}
138+
139+
/// <summary>
140+
/// Immutable derived-type tree node.
141+
/// Traversal yields TypeDefinitions in the requested order.
142+
/// </summary>
143+
public sealed class TypeTreeNode : IEnumerable<TypeDefinition>
144+
{
145+
public TypeTreeNode(TypeDefinition type, IReadOnlyList<TypeTreeNode> children) {
146+
Type = type ?? throw new ArgumentNullException(nameof(type));
147+
Children = children ?? Array.Empty<TypeTreeNode>();
148+
}
149+
150+
public TypeDefinition Type { get; }
151+
public IReadOnlyList<TypeTreeNode> Children { get; }
152+
153+
/// <summary>
154+
/// Traverses the tree and yields TypeDefinitions in the chosen order.
155+
/// </summary>
156+
public IEnumerable<TypeDefinition> Traverse(TreeTraversal order = TreeTraversal.PreOrder) => order switch {
157+
TreeTraversal.PreOrder => PreOrder(this),
158+
TreeTraversal.PostOrder => PostOrder(this),
159+
TreeTraversal.BreadthFirst => BreadthFirst(this),
160+
_ => throw new ArgumentOutOfRangeException(nameof(order))
161+
};
162+
163+
// Default enumeration is pre-order.
164+
public IEnumerator<TypeDefinition> GetEnumerator() => Traverse().GetEnumerator();
165+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
166+
167+
private static IEnumerable<TypeDefinition> PreOrder(TypeTreeNode node) {
168+
yield return node.Type;
169+
foreach (var child in node.Children)
170+
foreach (var t in PreOrder(child))
171+
yield return t;
172+
}
173+
174+
private static IEnumerable<TypeDefinition> PostOrder(TypeTreeNode node) {
175+
foreach (var child in node.Children)
176+
foreach (var t in PostOrder(child))
177+
yield return t;
178+
yield return node.Type;
179+
}
180+
181+
private static IEnumerable<TypeDefinition> BreadthFirst(TypeTreeNode node) {
182+
var q = new Queue<TypeTreeNode>();
183+
q.Enqueue(node);
184+
185+
while (q.Count > 0) {
186+
var cur = q.Dequeue();
187+
yield return cur.Type;
188+
189+
foreach (var child in cur.Children)
190+
q.Enqueue(child);
72191
}
73192
}
74193
}

src/OTAPI.UnifiedServerProcess/Core/Patching/DataModels/ClosureData.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class ClosureData
1919
public Instruction? CaptureFieldAssignment;
2020
public MethodDefinition? ContainingMethod;
2121
public VariableDefinition Closure;
22-
public readonly Dictionary<string, MethodDefinition> SupportMethods = [];
22+
public readonly Dictionary<string, MethodDefinition> ProcessedMethods = [];
2323
public bool IsReusedClosure { get; private set; }
2424

2525
private ClosureData(TypeDefinition type, MethodDefinition constructor, VariableDefinition closure, ClosureCaptureData[] captures) {
@@ -61,6 +61,12 @@ public void Apply(TypeDefinition declaringType, MethodDefinition containingMetho
6161
.Single(i => i.OpCode == OpCodes.Newobj && ((MethodReference)i.Operand).DeclaringType.TryResolve()?.FullName == ClosureType.FullName)
6262
.Next
6363
.Next;
64+
closureInitInsertBeforetarget = containingMethod.Body.Instructions
65+
.FirstOrDefault(i =>
66+
i is Instruction { OpCode.Code: Code.Stfld, Operand: FieldReference { Name: "<>4__this" } fr } &&
67+
fr.DeclaringType.Resolve()?.FullName == ClosureType.FullName)
68+
?.Next
69+
?? closureInitInsertBeforetarget;
6470
}
6571

6672
foreach (var capture in Captures) {
@@ -133,8 +139,12 @@ public void ApplyMethod(TypeDefinition declaringType, MethodDefinition containin
133139
if (!IsReusedClosure || !ClosureType.Methods.Any(m => m.GetIdentifier(false) == generatedMethod.GetIdentifier(false))) {
134140
ClosureType.Methods.Add(generatedMethod);
135141
}
136-
SupportMethods.Add(generatedMethod.GetIdentifier(), generatedMethod);
142+
ProcessedMethods.Add(generatedMethod.GetIdentifier(), generatedMethod);
137143
if (!Applied) {
144+
if(Closure.VariableType.FullName == containingMethod.DeclaringType.FullName) {
145+
Applied = true;
146+
return;
147+
}
138148
Apply(declaringType, containingMethod, jumpSites);
139149
}
140150
}

src/OTAPI.UnifiedServerProcess/Core/Patching/DataModels/ContextBoundMethodMap.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,22 @@ public class ContextBoundMethodMap
88
/// <summary>
99
/// Key: original method identifier, Value: converted method
1010
/// </summary>
11-
public Dictionary<string, MethodDefinition> originalToContextBound = [];
11+
public DebugMap originalToContextBound = [];
1212
/// <summary>
1313
/// Only the context-bound methods | Key: converted method identifier, Value: converted method
1414
/// </summary>
15-
public Dictionary<string, MethodDefinition> contextBoundMethods = [];
15+
public DebugMap contextBoundMethods = [];
16+
public class DebugMap : Dictionary<string, MethodDefinition>
17+
{
18+
public new bool TryAdd(string key, MethodDefinition m) {
19+
return base.TryAdd(key, m);
20+
}
21+
public new bool Remove(string key) {
22+
return base.Remove(key);
23+
}
24+
public new void Add(string key, MethodDefinition m) {
25+
base.Add(key, m);
26+
}
27+
}
1628
}
1729
}

0 commit comments

Comments
 (0)