Renamed the class CppDeclarations => CppDeclarationGenerator to better reflect
its function and emphasize its statefulness.
Renamed Visit{Type,Method} => Include{Type,Method} to clarify that these methods
include the type or method into the generator state.
Renamed GenerateVisitedTypes => GenerateRemainingTypeDeclarations to clarify
that it outputs *remaining* declarations, i.e. declarations that haven't been
generated yet.
We prefer to have the Outputs directory contain just one file or
namespace per output module, so we'll move all C++-related stuff to a
new CppUtils namespace instead.
Apparently, idc.SetType returns True if the type is set for the first
time, False if the type is updated, and None if the type couldn't be set
(rather differently from what the documentation says).
- Add string contents to names of StringLiteral objects to improve
decompilation output
- Add string constants for metadata version < 19 via an enum (if you can
identify and type il2cpp_codegen_string_literal_from_index correctly,
all enums resolve nicely to their string contents)
- Add types to metadata usages and globals
- Send method definitions & method references through writeMethods
for consistency
This patch fixes TypeInfo.GetVTable so that it specializes the vtable
for generic type instances. Also fix a minor bug in PropertyInfo that
would pass null pointers to GetMethodByDefinition.
The problem with using an end param in IDA is that IDA will quite
stupidly treat the function end as gospel even if it makes no sense
(e.g. a single "function" spanning many MB because there are no symbols
in between). Leaving out end, on the other hand, tells IDA there's a
function at a given starting address but lets IDA figure out the end by
itself, which it usually does correctly.
We want to get types into the IDA output, and to do that we need
accurate types for the Il2Cpp structures. Unfortunately, some crucial
types like Il2CppClass change between versions without any corresponding
metadata changes, meaning that we have to manually identify the version
outside of the Inspector somehow (e.g. by looking at the version number
embedded in Unity asset files). This patch adds header files for *every*
known Unity version from 5.3.0 to 2019.3.8, merging them into version
ranges where header files don't change.
It also adds front-end support for supplying the version number in both
the CLI and GUI. The GUI is given the ability to guess the version
number approximately to reduce the number of choices presented to the
user.
Per the C# language reference
(https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/basic-concepts#hiding-through-inheritance):
> The rules governing operator declarations (Operators) make it
> impossible for a derived class to declare an operator with the same
> signature as an operator in a base class. Thus, operators never hide
> one another.
Therefore, new is not necessary or permitted on operators, even if the
method signatures are identical. This situation can arise if, for
example, an explicit conversion operator from the same target class
exists in both a base class and a derived class.
Bug #1 is that in 32-bit programs, typeDefinitionIndex might be
0xffff_ffff_ffff_ffff instead (32-bit -1 sign-extended to 64 bits), so
we fix that by simply masking to get the low 32 bits.
Bug #2 is that, if the TypeRef points to no generic instance, we return
null, which wasn't being checked for in the IDAPythonScript generator.
Since that's the only time we could get a null type in Types, we simply
remove nulls from the Types collection.
This basically finishes the concrete generics implementation. We can now
enumerate all members of a concrete generic type with full type
substitution implemented.
Also add a simple test to verify that we can obtain the correct type for
a field of a concrete generic type.
Now that we generate methods in instantiated generic types, we were
getting test failures from methods that were being detected as new
methods. In actuality, they weren't new, but they differed only in
generic type parameters from some base type method, and
GetSignatureString ignores generic parameters completely.
This fix eliminates the hacky GetSignatureString and replaces it with
more-or-less proper signature comparison. This even manages to fix an
incorrect test case from Methods.cs (because GetSignatureString was
incorrectly incorporating the return type - when the return type should
not be examined for signature checking).
We adopt roughly the same approach as the C# Reflection API: a
GenericMethodDefinition is a method which has no method parameters
substituted, but lives in a open or closed type with some or all
type parameters substituted. To ensure the uniqueness of the MethodInfo,
we cache by the method type arguments, and also cache generated
DeclaredConstructors/DeclaredMethods in the TypeInfo.
This also enables MakeGenericMethod, albeit in a slightly different form
than the Reflection API: MakeGenericMethod lives in MethodBase, so it's
callable from a constructor (even though in C# constructors cannot be
generic). This slight violation of the spec reduces code duplication, so
it's probably worth it.
Finally, VirtualAddress gets set when populating GenericMethods, and so
it'll work whether or not the methods get cached/generated ahead of
time.
This has been a little TODO for a while, and happily it's easy enough to
implement with TypeRef arrays.
Also implement ImplementedInterfaces for generic type instances via
substitution.
In the C# reflection API, DeclaredNestedTypes on concrete generic types
just returns the nested types of the type definition. Notably, type
parameters are not substituted, because nested types are not guaranteed
to use the same containing type's type parameters.
Including *every* generated type in Types defeats the purpose of the
Types property, which is to list all types directly referred to by the
Il2Cpp metadata. Therefore, we return to the previous implementation:
only list types referred to by DefinitionIndex, ReferenceIndex, and
classIndexIndex.
Now that FromTypeReference uses public Make* methods instead of private
TypeInfo constructors, it doesn't need to be in TypeInfo anymore. Move
it back to Il2CppModel, where it was before.
With this patch, generic parameters in BaseType and method param/return
types are substituted correctly and deeply. Next up will be to apply the
same substitution rules to fields, properties, events, ...
IsGenericMethodDefinition needs the same treatment as
IsGenericTypeDefinition, i.e. it should depend on whether the class is a
definition as opposed to merely checking if the type args are generic
parameters (which could happen in a partially specialized method).
Also, array/ref/pointer types of generic types are considered to have
generic parameters, so correct ContainsGenericParameters accordingly.
In order to select the correct generic type, model.GetGenericMethod is
changed to use Name (which includes generic parameters) instead of
BaseName. The tests for GenericMethodDefinitionInGenericClass* are also
changed to reflect the fact that the chosen methods are fully concrete.
TestGenerics now passes in its entirety.
GenericMethods should contain all MethodSpec-referenced methods,
including those which are non-generic methods inside generic types. This
patch adds those methods, and also implements parameter substitution for
type arguments in parameters & return types.
We're aiming to make TypeInfo instances unique - no two TypeInfo
instances within a given model should refer to the same type. This will
allow us to use simple reference equality for comparing types.
classIndexIndex only indexes the genericInstance, not the actual generic
instance type. Therefore, for example, A<Int32,Int32> and B<Int32,Int32>
have the same classIndexIndex because the generic parameters are the
same, despite being otherwise unrelated.
Instead of TypesByMethodSpecClassIndex, we simply call MakeGenericType
each time, relying on genericTypeInstances to dedup the resulting
instances. This patch thus also adds all of the types from
genericTypeInstances to the Il2CppModel.Types listing.
We use indices into the TypesByReferenceIndex table to defer type lookup
during model construction. However, this won't support fully generic
instances (which might not be in the type table at all). This patch
introduces a new TypeRef abstraction which can either represent a fixed
index or an instantiated generic type.
Also add MakeGenericType, which creates type instances from a generic
type definition and arguments. We will use this later to build properly
fleshed out concrete generic types.