0xd4d / dnlib

Reads and writes .NET assemblies and modules
MIT License
2.18k stars 587 forks source link

Sort `ExportedType` table in the same way as the `TypeDef` table #558

Closed ElektroKill closed 6 months ago

ElektroKill commented 6 months ago

Microsoft has modified the specification to state that the table containing ExportedType rows should receive the exact same sorting as the TypeDef table.

See: https://github.com/dotnet/runtime/blob/main/docs/design/specs/Ecma-335-Augments.md#metadata-logical-format

wtfsck commented 6 months ago

I think this only requires fixing AddExportedTypes() and just updating the code var exportedTypes = module.ExportedTypes; to make sure exportedTypes is sorted correctly, similar to what module.GetTypes() already does, see NormalMetadata.GetAllTypeDefs()

ElektroKill commented 6 months ago

Yeah, I thought about just modifying the code in AddExportedTypes to sort the exported types in the order that is defined in the ECMA augments documents but I haven't though of a nice and optimal algorithm for sorting. We can't do what we do for normal types since ExportedType defines its parent type compared to TypeDef which defines all of its nested types making creating the necessary ordering easier. If you have any ideas, lmk! Maybe I'm not thinking of something obvious :D

wtfsck commented 6 months ago

If it's a nested exported type, ExportedType.Implementation is an ExportedType. Just like if a type is nested, TypeDef.DeclaringType is a TypeDef.

It should be possible to build a temporary TypeDef.NestedTypes (for exported types) with a dictionary ExportedType -> List<ExportedType> by just checking if the current exported type (looping over all of them) is a nested type, and if so, just add it to dict[parent].Add(current exported type). Then once we have that dict, we should be able to just use the same algorithm as module.GetTypes(), except that if ExportedType.Implementation is a FileDef or AssemblyRef, just add it to the result (no special ordering required).

foreach et in all-exported-types {
  if et.Implementation is ExportedType parent
    dict[parent].Add(et)
}
// now use the same algo as `module.GetTypes()` but use `dict` instead of checking `NestedTypes` property.
// and if Implementation is not an ExportedType, just add the ExportedType without any ordering constraint.

Haven't tested this but it seems like it could work.