Description
The implementation of EventSource
has a code paths that uses reflection APIs which are not analyzable by the linker, effectively making any usage of EventSource
potentially linker-unsafe. This in turn means that applications using ILLink during publish may end up broken at runtime due to missing assemblies/types/members.
Manifest creation
EventSource.CreateManifestAndDescriptors
uses reflection enumeration methods Type.GetMethods
and Type.GetFields
as well as Type.GetNestedType
to build the manifest description of an event source. If any of these methods/fields/nested types is not referenced directly by the application code linker may decide to remove them, so the manifest produced for a trimmed app might be different from the untrimmed app.
Accessing attributes
Most of the event source code uses EventSource.GetCustomAttributeHelper
to access attributes on types. The way this is implemented is not analyzable by the linker.
attributeType
is not analyzable right now
It comes from a fixed set of typeof
expressions in the call sites, so this should be relatively easy to fix. Those attribute types must be kept as attributes, instantiated and must have all properties. The method will eventually call CreateInstance
on the type and also GetProperty
and then PropertyInfo.SetValue
.
Performs name based scan of attributes
The method takes the attributeType
and does a name based matched to all attributes from the member
. So there's no good way to tell the linker it needs to keep attributes of a certain name (in any assembly). That said it seems this is for reflection-only scenarios. It's unclear if this is necessary in .NET Core - runtime itself doesn't support reflection-only. Maybe MetadataLoadContext
is considered reflection-only. What are the scenarios for this? Does it have to be an enabled code path for runtime event-tracing?
Caching reflection information about types
The entry point to this system seems to be Statics.CreateDefaultTypeInfo
.
This caches lot of reflection information about a specified Type
. Uses GetProperties
and GetCustomAttributes
. Effectively requires the type to have all properties and attributes kept.
Using reflection to get a value of a property
PropertyValue
uses PropertyInfo.GetValue
or the indirect PropertyInfo.GetGetMethod
to access property value. In some cases linker may remove the getter from a property if it is not used directly by any code in the application.
Similarly InvokeTypeInfo
uses PropertyInfo.GetValue
on a "random" property.
NullableTypeInfo
uses reflection
The implementation of NullableTypeInfo
uses reflection calls to create instance of the property value. The code is not analyzable by the linker as the type in question comes as an input. It's possible that with some advanced annotations the code is safe enough, hard to tell. Is it possible to create arbitrary types with this code, or is it really only a limited set of types which is allowed?
Asking for size of an object
EventSource.DecodeObject
calls Marshal.SizeOf
on a System.Type
which is not statically analyzable. Technically this is unsafe as linker can't guarantee that the SizeOf
will return the same values before and after linking (linker may remove unused fields).
Maps creation for enum types
ManifestBuilder.CreateManifestString
uses Type.GetFields
to enumerate all values on an Enum
type. This is not analyzable by the linker currently as there's no way for linker to tell that the types in question are always enums. The code is safe (as linker keeps all fields on enums by default), we just need a way to describe this to linker in some maintainable way.
Similarly ManifestBuilder.GetTypeName
also special cases enums and calls Type.GetFields
on them. Same situation as above; just need to find a way to annotate this correctly.