-
Notifications
You must be signed in to change notification settings - Fork 0
Methodology and Census Internals
This document details the internal logic of tools/measure-bodies.py and tools/measure-owned.py, the four classifier bugs discovered during testing, and the complete benchmark data for Unity IL2CPP vs Mono builds.
To verify whether a 0.00% survival rate is an IL2CPP characteristic or a decompiler defect, the census scripts were run across four commercial Unity titles spanning both scripting backends and Unity versions from 2017 to Unity 6 (2026).
| Game | Unity Version | Scripting Backend | Extractor Tool | Files | Total Methods | Empty | Stubs | Live Bodies | Live % |
|---|---|---|---|---|---|---|---|---|---|
| House Flipper | 2019.4 | Mono | ilspycmd | 4,528 | 231,243 | 5,120 | 6,448 | 219,675 | 95.00% |
| Car Mechanic Simulator 2018 | 2017.4 | Mono | ilspycmd | 114 | 1,040 | 18 | 55 | 967 | 92.98% |
| MiSide | 2021.3 | IL2CPP | AssetRipper | 1,225 | 4,899 | 3,408 | 1,491 | 0 | 0.00% |
| Data Center | 6000.4 (Unity 6) | IL2CPP | Cpp2IL | 282 | 2,549 | 0 | 2,549 | 0 | 0.00% |
Whole-project numbers flatten studio code and third-party libraries together. In Mono games, middleware libraries (written for distribution) survive at higher rates than game code, inflating the overall score. In IL2CPP games, IL2CPP compiles everything to native code regardless of whether a library is open-source.
measure-owned.py separates studio code from vendor middleware (Steamworks, Rewired, Sirenix, Odin, I2.Loc, etc.):
| Game | Backend | Category | Files | Methods | Live Bodies | Live % |
|---|---|---|---|---|---|---|
| House Flipper | Mono | Studio Owned | 4,210 | 218,860 | 208,216 | 95.14% |
| Mono | Vendor Middleware | 318 | 12,383 | 11,459 | 92.54% | |
| Car Mechanic Simulator 2018 | Mono | Studio Owned | 22 | 130 | 108 | 83.08% |
| Mono | Vendor Middleware | 92 | 910 | 859 | 94.40% | |
| MiSide | IL2CPP | Studio Owned | 781 | 3,309 | 0 | 0.00% |
| IL2CPP | Vendor Middleware | 444 | 1,590 | 0 | 0.00% | |
| Data Center | IL2CPP | Studio Owned | 258 | 2,366 | 0 | 0.00% |
| IL2CPP | Vendor Middleware | 24 | 183 | 0 | 0.00% |
Note: On Car Mechanic Simulator 2018 (Mono), studio code is 83.08% live while bundled middleware is 94.40%. Third-party libraries inflate whole-project averages.
tools/measure-bodies.py uses regex parsing to classify every recovered method into one of three categories:
-
Empty: 0 statements between braces
{}. -
Trivial Stub: Contains only dummy default return values,
throw null;, oroutparameter default assignments. - Real Body: Contains actionable programming logic.
sig = re.compile(
r'^[ \t]*(?:\[[^\]]*\][ \t]*)*'
r'(?:public|private|protected|internal)[ \t]+'
r'(?:(?:static|virtual|override|sealed|abstract|extern|async|new|unsafe)[ \t]+)*'
r'(?![ \t]*(?:class|struct|enum|interface|delegate)\b)'
r'[\w<>\[\],\.\?]+[ \t]+\w+[ \t]*\([^;{]*\)[ \t]*$'
)TRIVIAL_ZERO = (r"(?:0[uUlLfFdDmM]{0,2}|0\.0[fFdDmM]?|'\\0'"
r'|""|string\.Empty|null|default|false|true)')
TRIVIAL_VALUE = (r'(?:default\([^()]*\)'
r'|\((?:[\w\.]+)\)\s*' + TRIVIAL_ZERO +
r'|' + TRIVIAL_ZERO +
r'|new [\w<>\[\],\. ]+\(\))')
TRIVIAL_STMT = re.compile(
r'(?:return(?:\s+' + TRIVIAL_VALUE + r')?'
r'|throw null'
r'|throw new (?:System\.)?NotImplementedException\(\)'
r'|[\w\.\[\]]+\s*=\s*' + TRIVIAL_VALUE + r')\s*;'
)Before reaching the validated 0.00% figure, the headline number was published wrong three times. Every error was caught by opening the decompiled files and reading them by hand rather than trusting script output:
- Cause: Measured a working project directory that already contained hand-written replacement code.
- Correction: Always measure pristine, untouched decompiler exports directly out of AssetRipper or Cpp2IL.
-
Cause: The original classifier only recognized single-statement method bodies. When IL2CPP strips a method with
outparameters, C# decompilers emit stub assignments for every parameter before returning:Because this body contains three statements, single-statement matching misclassified it as real code.public static bool GetAutoResolution(out int width, out int height) { width = default(int); height = default(int); return false; }
-
Correction: Updated
is_trivial(txt)to verify if all statements in the body match stub assignment/return patterns.
-
Cause: The original stub regex matched
0andnull, but missed type-casted zero values such as(IntPtr)0,0uL,0.0, or'\0'. This credited 149 Steamworks P/Invoke stub functions as live code. -
Correction: Expanded
TRIVIAL_ZEROto cover explicitly typed zero literals, unsigned longs, floats, null pointers, and empty strings.
-
Cause:
measure-owned.pyoriginally checked string prefixes usingsegment.startswith(vendor). The entrygog(for GOG Galaxy) matchedGogoGaga.OptimizedRopesAndCables, andpropertiesmatched every assembly'sProperties/AssemblyInfo.cs. -
Correction: Namespace matching was rewritten to enforce dot-separated namespace boundaries (
comps[:len(vc)] == vc).unitynow matchesUnity.Entitiesbut never a game namespace starting with those letters (likeUnityChanFanGame).