@@ -42,7 +42,7 @@ public CppDeclarationGenerator(AppModel appModel) {
4242 // Configure inheritance style based on binary type; this can be overridden by setting InheritanceStyle in the object initializer
4343 InheritanceStyle = CppCompiler . GuessFromImage ( model . Package . BinaryImage ) ;
4444 }
45-
45+
4646 // C type declaration used to name variables of the given C# type
4747 private static readonly Dictionary < string , string > primitiveTypeMap = new ( )
4848 {
@@ -89,6 +89,7 @@ public void Reset() {
8989 _visitedTypes . Clear ( ) ;
9090 _todoFieldStructs . Clear ( ) ;
9191 _todoTypeStructs . Clear ( ) ;
92+ _todoTypesExceedingNestingLimit . Clear ( ) ;
9293 }
9394
9495 #region Field Struct Generation
@@ -313,12 +314,34 @@ private MethodBase[] GetFilledVTable(TypeInfo ti) {
313314 private readonly HashSet < TypeInfo > _visitedTypes = [ ] ;
314315 private readonly List < TypeInfo > _todoTypeStructs = [ ] ;
315316
317+ private const int MaxGenericNestingDepth = 7 ;
318+ private readonly HashSet < TypeInfo > _todoTypesExceedingNestingLimit = [ ] ;
319+
320+ private static int CalculateTypeDepth ( TypeInfo type , int startingDepth = 0 )
321+ {
322+ while ( type . HasElementType )
323+ {
324+ startingDepth ++ ;
325+ type = type . ElementType ;
326+ }
327+
328+ var depth = startingDepth ;
329+
330+ var args = type . GenericTypeArguments ;
331+ for ( int i = 0 ; i < args . Length ; i ++ )
332+ {
333+ depth = Math . Max ( depth , CalculateTypeDepth ( args [ i ] , startingDepth + 1 ) ) ;
334+ }
335+
336+ return depth ;
337+ }
338+
316339 /// <summary>
317340 /// Include the given type into this generator. This will add the given type and all types it depends on.
318341 /// Call GenerateRemainingTypeDeclarations to produce the actual type declarations afterwards.
319342 /// </summary>
320343 /// <param name="ti"></param>
321- public void IncludeType ( TypeInfo ti )
344+ public void IncludeType ( TypeInfo ti )
322345 {
323346 if ( _visitedTypes . Contains ( ti ) )
324347 return ;
@@ -328,6 +351,23 @@ public void IncludeType(TypeInfo ti)
328351
329352 _visitedTypes . Add ( ti ) ;
330353
354+ // This is my attempt at a solution for a big problem in some games:
355+ // circular generic argument references that cause loops when inflating the types.
356+ // ex. Class<T> -> member Class<T[]> -> instantiates Class<T[]> -> member Class<T[][]> -> instantiates Class<T[][]> -> ...
357+ // or Class<T> -> member Class<SomeType<T>> -> instantiates Class<SomeType<T>> -> member Class<SomeType<SomeType<T>>> -> ...
358+
359+ // The only game (issue #49) that currently encounters this uses the MetaXR Unity SDK,
360+ // which defines OVRTask<T> which has a method that returns OVRTask<T[]>.
361+ // This can also be filtered out by filtering out methods without an address, as they get folded through generic sharing,
362+ // but this is a (hopefully) better solution to the problem.
363+
364+ var typeDepth = CalculateTypeDepth ( ti ) ;
365+ if ( typeDepth > MaxGenericNestingDepth )
366+ {
367+ _todoTypesExceedingNestingLimit . Add ( ti ) ;
368+ return ;
369+ }
370+
331371 if ( ti . IsArray || ti . HasElementType )
332372 {
333373 IncludeType ( ti . ElementType ) ;
@@ -351,17 +391,8 @@ public void IncludeType(TypeInfo ti)
351391
352392 foreach ( var mi in GetFilledVTable ( ti ) )
353393 {
354- // The VirtualAddress: not null constraint is a workaround for a much bigger issue:
355- // circular generic argument references that cause loops when inflating the types.
356- // ex. Class<T> -> member Class<T[]> -> instantiates Class<T[]> -> member Class<T[][]> -> instantiates Class<T[][]> -> ...
357- // or Class<T> -> member Class<SomeType<T>> -> instantiates Class<SomeType<T>> -> member Class<SomeType<SomeType<T>>> -> ...
358394
359- // The only game (issue #49) that currently encounters this uses the MetaXR Unity SDK,
360- // which defines OVRTask<T> which has a method that returns OVRTask<T[]>.
361- // This filters out methods like that, as they get folded through generic sharing
362- // in the actual runtime.
363-
364- if ( mi is { ContainsGenericParameters : false , VirtualAddress : not null } )
395+ if ( mi is { ContainsGenericParameters : false } )
365396 IncludeMethod ( mi ) ;
366397 }
367398
@@ -456,9 +487,20 @@ should display the correct method name (with a computed
456487 var ( cls , statics , vtable ) = GenerateTypeStruct ( ti ) ;
457488 decl . Add ( ( ti , null , cls , null , vtable , statics ) ) ;
458489 }
490+
491+ /*
492+ NOTE: This is currently not required, but if we do need custom types for deeply nested structs
493+ this can be used
494+ foreach (var ti in _todoTypesExceedingNestingLimit)
495+ {
496+ var dummyType = GenerateNestingLimitExceededStruct(ti);
497+ decl.Add((ti, null, dummyType, null, null, null));
498+ }
499+ */
459500
460501 _todoTypeStructs . Clear ( ) ;
461502 _todoFieldStructs . Clear ( ) ;
503+ _todoTypesExceedingNestingLimit . Clear ( ) ;
462504
463505 return decl ;
464506 }
0 commit comments