Skip to content

Fix duplicate types in generated assemblies on Unity 6.4+#266

Open
acan1980728690 wants to merge 2 commits into
BepInEx:masterfrom
acan1980728690:fix/pass79-duplicate-types
Open

Fix duplicate types in generated assemblies on Unity 6.4+#266
acan1980728690 wants to merge 2 commits into
BepInEx:masterfrom
acan1980728690:fix/pass79-duplicate-types

Conversation

@acan1980728690

Copy link
Copy Markdown

The game crashed on version 6000.4.7. The content of ErrorLog.log is as follows

Unhandled exception. System.IO.FileNotFoundException:
Could not load file or assembly 'UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'

Cause

The real error was BadImageFormatException: Duplicate type with name '__O' — the FileNotFoundException was just the surface. Pass79UnstripTypes was producing a UnityEngine.CoreModule.dll with duplicate types, which CoreCLR refused to load. Three bugs in the pass caused this:

  1. Types like <>O were named <>O in one path and __O in another, creating duplicates of the same logical type.
  2. Nested types were looked up by simple name only, so types already in the output were missed and re-created.
  3. Nested types whose parent was never emitted were incorrectly promoted to top-level, producing multiple top-level __O and __c.

Fix

  • Use unified type name conversion (MakeValidInSource) for all unstripped types
  • Look up types by FullName consistently instead of simple name
  • Skip nested types when parent type is not in the output assembly
  • Add null guard for Resolve() in HasNonBlittableFields recursion

- Use unified type name conversion (MakeValidInSource) for all unstripped types
- Look up types by FullName consistently instead of simple name
- Skip nested types when parent type is not in the output assembly
- Add null guard for Resolve() in HasNonBlittableFields recursion
Comment thread Il2CppInterop.Generator/Passes/Pass79UnstripTypes.cs Outdated
Comment thread Il2CppInterop.Generator/Passes/Pass79UnstripTypes.cs Outdated
Comment on lines +139 to +144
if (fieldDefinition.Signature.FieldType.Namespace?.StartsWith("System") ?? false)
{
var resolved = fieldDefinition.Signature.FieldType.Resolve();
if (resolved != null && HasNonBlittableFields(resolved))
return true;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is cursed, but it's not your fault. I see no reason for:

  • A System namespace check
  • Resolving a TypeSignature to a TypeDefinition, which removes the type arguments if the signature is a generic instance type

I'm not asking you to change anything, except maybe returning true for null resolved; I'm just saying that the preexisting code is extremely flawed.

Comment thread Il2CppInterop.Generator/Passes/Pass79UnstripTypes.cs Outdated
var convertedTypeName = GetConvertedUnityTypeName(processedAssembly.GlobalContext, unityType);

// If the parent type does not exist in the rewritten assembly, this nested type cannot be emitted safely.
// Promoting it to a top-level type creates orphan compiler-generated types such as __O/__c.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is so cursed that we were doing this before.

Comment thread Il2CppInterop.Generator/Passes/Pass79UnstripTypes.cs Outdated
@ds5678

ds5678 commented May 29, 2026

Copy link
Copy Markdown
Collaborator

Note that this shouldn't be an issue in v2 (#238)

Co-authored-by: Jeremy Pritts <49847914+ds5678@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants