Architecture¶
MonoWeaver/ has three directories, each owning one responsibility. A change that spreads across all of them usually means the layers got confused.
Patterns/ describe the target, find it in a method, rewrite it
CFG/ method body to basic-block graph, simulate the stack per block, report problems
Utils/ Cecil type system, instruction helpers, delegate emission, two containers
Patterns/ — matching and rewriting¶
The full path of one Match:
Cil.Value(lambda)
| PatternExpressionParser: expression tree -> PatternNode
Pattern (ValuePattern / EffectPattern / ConditionPattern)
|
the method IL
| MethodModel: rebuild only the expression dependencies a candidate needs
a symbolic model of the method
| PatternMatcher: line PatternNodes up against model nodes
CilMatchSet<ValueMatch> / ...
| PatternTransforms: pick an operation
RewritePlan
| Apply
the edited method body (rolled back on failure)
| File | Responsibility |
|---|---|
Pattern.cs |
The Cil and P entry points, the three pattern types, and PatternOptions |
PatternExpressionParser.cs |
Translates a C# expression tree into a PatternNode tree |
PatternNodes.cs |
Pattern-side nodes: argument, local, field, call, binary operation, Mark, and so on |
CilExpr.cs / CilSymbols.cs / CilMetadataSpecs.cs |
The symbol form, for when game types are not referenced |
MethodModel.cs |
A small symbolic model of the target method. It does not decompile the whole method; it keeps only the expression dependencies a candidate needs |
TargetExpressionNodes.cs |
Target-side nodes, including shapes that are invisible in C# such as address-taking |
LocalDefinitionIndex.cs |
The set of stores that could reach a given load, used to disambiguate temporaries |
PatternMatcher.cs |
The matching loop, and the decision on whether a condition can be safely rewritten |
CilMatchResults.cs |
CilMatchSet plus the match and capture types |
PatternTransforms.cs |
The user-facing rewrite extension methods |
PatternTransformImplementation.cs |
The actual IL emission behind those operations |
RewritePlan.cs |
The plan, callback result routing, Apply, and rollback |
MatchDiagnostics.cs |
Explanations for a failed match |
Two design choices matter most:
MethodModelis demand-driven, not a full decompiler. It only rebuilds dependencies around candidate positions, so a large method never gets expanded wholesale.- Ambiguity means no match. When
LocalDefinitionIndexfinds that a load could come from several stores, or that the variable had its address taken, the candidate is dropped rather than guessed. This is "rather miss a match than patch the wrong place", made concrete.
CFG/ — verification¶
| File | Responsibility |
|---|---|
ILBasicBlockGraphBuilder.cs |
Method body to basic-block graph, including exception regions |
StackType.cs |
The model of values on the evaluation stack, and the merge rules |
ILMethodVerifier.cs |
The verifier itself, VerifyOptions, and the exception-region model |
ILMethodVerifier.Verify.cs |
Per-instruction stack simulation along every path |
ILMethodVerifier.Diagnostic.cs |
Diagnostic types, deduplication, CfgVerifyException |
The verifier does not care about semantics. It answers one question: would the runtime reject this method when loading it? The diagnostic list is in Verification Failures.
Utils/ — the Cecil layer¶
| File | Responsibility |
|---|---|
CecilTypeSystem*.cs |
Type comparisons: IsSameWith, IsAssignableTo, CanAccess, generic constraints |
CecilHelper*.cs |
The Verify extension, symbol handling, MonoMod label to Cecil instruction conversion |
CecilInstructionHelpers.cs |
Instruction construction and operand handling |
CecilDelegateEmission.cs |
Turns a runtime delegate into an emittable call; a static delegate is lowered to a direct call |
FixSizeDictionary.cs / ListStack.cs |
Containers on the matching and verification hot paths, to avoid repeated allocation |
CecilCompat.cs |
Papers over the API differences between Cecil 0.10 and 0.11+ |
CecilCompat.cs is why the dual-flavor build works at all. Before using a Cecil API in new code, confirm both generations have it; if only one does, add the adaptation here rather than writing #if CECIL_010 in ordinary code.
Adding something¶
- New pattern syntax: parse it in
PatternExpressionParser, add a node inPatternNodes, match it inPatternMatcher, and possibly add the target-side shape toMethodModel. - A new rewrite operation: add the user-facing overloads in
PatternTransforms, emit the IL inPatternTransformImplementation, then confirm theRewritePlanresult-routing semantics still make sense. - A new diagnostic: add the type in
ILMethodVerifier.Diagnostic.csand add a row to the table in Verification Failures. - Whatever you add, run the tests under both
CecilFlavorvalues — see Build and Test.