Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1b920fc
Add HybridCLR runtime compatibility support
SNWCreations Feb 15, 2026
ef6bc78
Improve HybridCLR compatibility and null safety
SNWCreations Feb 21, 2026
852ee07
Add HybridCLR support for Generator and HarmonySupport
SNWCreations Feb 21, 2026
1c5bdac
Add incremental interop generation for HybridCLR hotfix assemblies
SNWCreations Feb 22, 2026
086fd61
Fix incremental mode to properly load existing interop assemblies
SNWCreations Feb 22, 2026
f5b3b52
Fix nullable warnings in incremental mode code
SNWCreations Feb 22, 2026
736e7b7
Add cross-version compatible HybridCLR hooking API
SNWCreations Feb 23, 2026
f110d8c
Fix null reference warning in DeobfuscationMapGenerator
SNWCreations Feb 23, 2026
51e17ed
Merge upstream/master - resolve conflicts with null safety
SNWCreations Mar 3, 2026
5636676
Simplify PrepareMethodForDetour: remove detour address parameter
SNWCreations Mar 27, 2026
d32cad3
Fix relative call/jmp instructions in the copied HybridCLR bridge code.
SNWCreations Apr 6, 2026
f9f2383
Merge branch 'master' into master
SNWCreations Apr 19, 2026
a5eabd0
Fix delegate base type resolution for hotfix-only generation
SNWCreations May 24, 2026
48f507f
Merge remote-tracking branch 'upstream'
SNWCreations May 24, 2026
91e703f
Fix RewriteTypeRef: use name-based lookup for reference assembly types
SNWCreations May 24, 2026
3a7f4a0
Fix CS9342: use .Value to get string from Utf8String for comparison
SNWCreations May 24, 2026
9234844
Use HybridCLRMetadataResolver for source assemblies in incremental mode
SNWCreations May 24, 2026
eabbbc6
Fix type resolution: inject reference assemblies into source resolver
SNWCreations May 24, 2026
8dadfde
Fix HasNativeConstructor: use IsInterpterImpl instead of MethodPointe…
SNWCreations May 24, 2026
3f73654
Guard HasNativeConstructor: only check IsInterpterImpl in HybridCLR r…
SNWCreations May 24, 2026
26f49df
Fix synthetic MethodInfo for HybridCLR delegate dispatch
SNWCreations May 24, 2026
afd8b25
Merge remote-tracking branch 'upstream/master'
SNWCreations Jun 11, 2026
fb0b0f4
Fix HybridCLR bridge RIP-relative relocation
SNWCreations Jun 16, 2026
0354124
Fix HybridCLR generator gating
SNWCreations Jun 16, 2026
951bc9b
Bring back the IsInterpreterMethod from an incorrect refactoring
SNWCreations Jun 21, 2026
6a9f139
Let relocator fail if target out of rel32 range
SNWCreations Jun 21, 2026
9882a7e
Fix mmap call on macOS, still untested but should be more closer to l…
SNWCreations Jun 21, 2026
d2f7e11
Go back using DllImport for mmap declaration because we are on net6
SNWCreations Jun 21, 2026
e4e25bf
Forget to format the exception string for rel32 out-of-range throwing
SNWCreations Jun 21, 2026
e28dce3
Merge upstream/master into SNW master
SNWCreations Jun 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Il2CppInterop.CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
new Option<bool>("--passthrough-names",
"If specified, names will be copied from input assemblies as-is without renaming or deobfuscation."),
new Option<bool>("--no-parallel", "Disable parallel processing when writing assemblies. Use if you encounter stability issues when generating assemblies."),
new Option<bool>("--hybridclr", "Enable HybridCLR-specific metadata compatibility handling."),
new Option<DirectoryInfo>("--existing-interop", "Directory with existing interop assemblies to reference for incremental generation.").ExistingOnly(),
new Option<bool>("--no-skip-existing", "Generate assemblies even when they already exist in --existing-interop."),
};
generateCommand.Description = "Generate wrapper assemblies that can be used to interop with Il2Cpp";
generateCommand.Handler = CommandHandler.Create((GenerateCommandOptions opts) =>
Expand Down Expand Up @@ -169,7 +172,10 @@ internal record GenerateCommandOptions(
string[]? BlacklistAssembly,
Regex? ObfRegex,
bool PassthroughNames,
bool NoParallel
bool NoParallel,
bool HybridCLR,
DirectoryInfo? ExistingInterop,
bool NoSkipExisting
) : BaseCmdOptions(Verbose)
{
public override CmdOptionsResult Build()
Expand All @@ -188,6 +194,9 @@ public override CmdOptionsResult Build()
opts.ObfuscatedNamesRegex = ObfRegex;
opts.PassthroughNames = PassthroughNames;
opts.Parallel = !NoParallel;
opts.IsHybridCLREnvironment = HybridCLR;
opts.ExistingInteropDir = ExistingInterop?.FullName;
opts.SkipExistingAssemblies = !NoSkipExisting;

if (AddPrefixTo is not null && AddPrefixTo.Length > 0)
{
Expand Down
147 changes: 134 additions & 13 deletions Il2CppInterop.Generator/Contexts/AssemblyRewriteContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ public class AssemblyRewriteContext
private readonly Dictionary<TypeDefinition, TypeRewriteContext> myOldTypeMap = new();
public readonly AssemblyDefinition NewAssembly;

#nullable disable
// OriginalAssembly is null for reference-only contexts (loaded from ExistingInteropDir)
public readonly AssemblyDefinition OriginalAssembly;
#nullable enable

public AssemblyRewriteContext(RewriteGlobalContext globalContext, AssemblyDefinition originalAssembly,
public AssemblyRewriteContext(RewriteGlobalContext globalContext, AssemblyDefinition? originalAssembly,
AssemblyDefinition newAssembly)
{
OriginalAssembly = originalAssembly;
Expand Down Expand Up @@ -56,11 +59,66 @@ public void RegisterTypeRewrite(TypeRewriteContext context)
myNameTypeMap[(context.OriginalType ?? context.NewType).FullName] = context;
}

public IMethodDefOrRef RewriteMethodRef(IMethodDefOrRef methodRef)
/// <summary>
/// Registers a type under an alternative name for lookup purposes.
/// Used for reference-only assemblies where Il2Cpp-prefixed types need to be
/// findable by their original names (e.g., "System.Type" for "Il2CppSystem.Type").
/// </summary>
public void RegisterTypeByAlternativeName(string alternativeName, TypeRewriteContext context)
{
var newType = GlobalContext.GetNewTypeForOriginal(methodRef.DeclaringType!.Resolve()!);
var newMethod = newType.GetMethodByOldMethod(methodRef.Resolve()!).NewMethod;
return NewAssembly.ManifestModule!.DefaultImporter.ImportMethod(newMethod);
if (!myNameTypeMap.ContainsKey(alternativeName))
{
myNameTypeMap[alternativeName] = context;
}
}

public IMethodDefOrRef? RewriteMethodRef(IMethodDefOrRef? methodRef)
{
if (methodRef?.DeclaringType == null)
{
if (GlobalContext.Options.IsHybridCLREnvironment)
return null;

throw new ArgumentNullException(nameof(methodRef));
}

var declaringType = methodRef.DeclaringType.Resolve();
if (declaringType == null)
{
if (GlobalContext.Options.IsHybridCLREnvironment)
return null;

throw new($"Could not resolve declaring type {methodRef.DeclaringType.FullName} for method {methodRef.Name}");
}

var newType = GlobalContext.GetNewTypeForOriginal(declaringType);
if (newType == null)
{
if (GlobalContext.Options.IsHybridCLREnvironment)
return null;

throw new($"Could not find rewrite context for declaring type {declaringType.FullName}");
}

var resolvedMethod = methodRef.Resolve();
if (resolvedMethod == null)
{
if (GlobalContext.Options.IsHybridCLREnvironment)
return null;

throw new($"Could not resolve method {methodRef.FullName}");
}

var methodContext = newType.TryGetMethodByOldMethod(resolvedMethod);
if (methodContext == null)
{
if (GlobalContext.Options.IsHybridCLREnvironment)
return null;

throw new($"Could not find rewrite context for method {resolvedMethod.FullName}");
}

return NewAssembly.ManifestModule!.DefaultImporter.ImportMethod(methodContext.NewMethod);
}

public ITypeDefOrRef RewriteTypeRef(ITypeDescriptor typeRef)
Expand Down Expand Up @@ -124,18 +182,81 @@ public TypeSignature RewriteTypeRef(TypeSignature? typeRef)
return Imports.Module.String();

if (typeRef.FullName == "System.Object")
return sourceModule.DefaultImporter.ImportType(GlobalContext.GetAssemblyByName("mscorlib")
.GetTypeByName("System.Object").NewType).ToTypeSignature();
{
var mscorlib = GlobalContext.TryGetAssemblyByName("mscorlib");
if (mscorlib != null)
return sourceModule.DefaultImporter.ImportType(mscorlib.GetTypeByName("System.Object").NewType).ToTypeSignature();
if (!GlobalContext.Options.IsHybridCLREnvironment)
throw new KeyNotFoundException("Required corlib type 'System.Object' was not found.");
return sourceModule.CorLibTypeFactory.Object;
}

if (typeRef.FullName == "System.Attribute")
return sourceModule.DefaultImporter.ImportType(GlobalContext.GetAssemblyByName("mscorlib")
.GetTypeByName("System.Attribute").NewType).ToTypeSignature();
{
var mscorlib = GlobalContext.TryGetAssemblyByName("mscorlib");
if (mscorlib != null)
return sourceModule.DefaultImporter.ImportType(mscorlib.GetTypeByName("System.Attribute").NewType).ToTypeSignature();
if (!GlobalContext.Options.IsHybridCLREnvironment)
throw new KeyNotFoundException("Required corlib type 'System.Attribute' was not found.");
return sourceModule.ImportCorlibReference("System.Attribute");
}

var originalTypeDef = typeRef.Resolve()!;
var targetAssembly = GlobalContext.GetNewAssemblyForOriginal(originalTypeDef.DeclaringModule!.Assembly!);
var target = targetAssembly.GetContextForOriginalType(originalTypeDef).NewType;
var originalTypeDef = typeRef.Resolve();
if (originalTypeDef == null)
{
if (!GlobalContext.Options.IsHybridCLREnvironment)
throw new($"Could not resolve type {typeRef.FullName}");

var mscorlib = GlobalContext.TryGetAssemblyByName("mscorlib");
if (mscorlib != null)
return sourceModule.DefaultImporter.ImportType(mscorlib.GetTypeByName("System.Object").NewType).ToTypeSignature();
return sourceModule.CorLibTypeFactory.Object;
}

var targetAssembly = GlobalContext.GetNewAssemblyForOriginal(originalTypeDef.DeclaringModule?.Assembly);
if (targetAssembly == null)
{
// Not a source assembly — try name-based lookup to find reference (existing interop) assembly.
// The resolved TypeDefinition lives in the raw dependency DLL (e.g., mscorlib),
// but the registered context is for the interop DLL (e.g., Il2Cppmscorlib).
var asmName = originalTypeDef.DeclaringModule?.Assembly?.Name?.Value;
if (asmName != null)
targetAssembly = GlobalContext.TryGetAssemblyByName(asmName);
}
if (targetAssembly == null)
{
if (!GlobalContext.Options.IsHybridCLREnvironment)
throw new KeyNotFoundException(
$"Could not find target assembly for type {originalTypeDef.FullName} from assembly {originalTypeDef.DeclaringModule?.Assembly?.Name}");

var mscorlib = GlobalContext.TryGetAssemblyByName("mscorlib");
if (mscorlib != null)
return sourceModule.DefaultImporter.ImportType(mscorlib.GetTypeByName("System.Object").NewType).ToTypeSignature();
return sourceModule.CorLibTypeFactory.Object;
}

var typeContext = targetAssembly.TryGetContextForOriginalType(originalTypeDef);
if (typeContext == null)
{
// For reference assemblies, the type IS the new type (no original/new distinction).
// Object-identity lookup won't work because originalTypeDef is from the raw dependency DLL,
// not from the interop DLL. Use name-based lookup instead.
var typeName = originalTypeDef.FullName;
typeContext = targetAssembly.TryGetTypeByName(typeName);
}
if (typeContext == null)
{
if (!GlobalContext.Options.IsHybridCLREnvironment)
throw new KeyNotFoundException(
$"Could not find rewrite context for type {originalTypeDef.FullName} in assembly {targetAssembly.NewAssembly.Name}");

var mscorlib = GlobalContext.TryGetAssemblyByName("mscorlib");
if (mscorlib != null)
return sourceModule.DefaultImporter.ImportType(mscorlib.GetTypeByName("System.Object").NewType).ToTypeSignature();
return sourceModule.CorLibTypeFactory.Object;
}

return sourceModule.DefaultImporter.ImportType(target).ToTypeSignature();
return sourceModule.DefaultImporter.ImportType(typeContext.NewType).ToTypeSignature();
}

public TypeRewriteContext GetTypeByName(string name)
Expand Down
Loading
Loading