From 6ecc2ffdbc4040202e72ade67a83ee7be896cec2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Apr 2026 04:17:13 +0000 Subject: [PATCH 01/26] Parse custom attribute value blob for dependencies and rewrite type name strings Implement custom attribute blob parsing in CustomAttributeNode: 1. GetStaticDependencies now decodes the custom attribute value blob and reports dependencies for typeof() arguments, enum types, property setters, and fields referenced in the blob. 2. WriteInternal now re-encodes the blob with type name strings resolved to their definitions, handling cases where trimming drops type forwarders (e.g. re-encoding "System.Object, System.Runtime" to "System.Object, System.Private.CoreLib"). Remove 24 tests from ILTrimExpectedFailures.txt that now pass. Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/f4173cc7-fd45-4a62-9a1c-1a31c06cc85b Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 488 +++++++++++++++++- .../ILTrim.Tests/ILTrimExpectedFailures.txt | 24 - 2 files changed, 485 insertions(+), 27 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 744e5c1539dfb6..bb57d1a0a36c9a 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -1,9 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Reflection.Metadata; +using System.Text; +using Internal.TypeSystem; using Internal.TypeSystem.Ecma; namespace ILCompiler.DependencyAnalysis @@ -61,7 +65,154 @@ public override IEnumerable GetStaticDependencies(NodeFacto if (!customAttribute.Constructor.IsNil) yield return new DependencyListEntry(factory.GetNodeForMethodToken(_module, customAttribute.Constructor), "Custom attribute constructor"); - // TODO: Parse custom attribute value and add dependencies from it + // Parse the custom attribute value blob and add dependencies from it + CustomAttributeValue decodedValue; + try + { + decodedValue = customAttribute.DecodeValue(new CustomAttributeTypeProvider(_module)); + } + catch (Exception) + { + // If decoding fails (bad metadata, unresolvable types, etc.), skip blob analysis. + yield break; + } + + foreach (var entry in GetDependenciesFromCustomAttributeBlob(factory, decodedValue)) + yield return entry; + } + + private IEnumerable GetDependenciesFromCustomAttributeBlob(NodeFactory factory, CustomAttributeValue value) + { + foreach (CustomAttributeTypedArgument fixedArg in value.FixedArguments) + { + foreach (var entry in GetDependenciesFromCustomAttributeArgument(factory, fixedArg.Type, fixedArg.Value)) + yield return entry; + } + + foreach (CustomAttributeNamedArgument namedArg in value.NamedArguments) + { + // Report the type that declares the field/property + // (The constructor's declaring type was already reported above.) + if (namedArg.Kind == CustomAttributeNamedArgumentKind.Property) + { + // We need the setter of the property to be kept + MethodDesc constructor = _module.GetMethod( + _module.MetadataReader.GetCustomAttribute(Handle).Constructor); + if (constructor is not null) + { + foreach (var entry in GetDependenciesFromPropertySetter(factory, constructor.OwningType, namedArg.Name)) + yield return entry; + } + } + else if (namedArg.Kind == CustomAttributeNamedArgumentKind.Field) + { + MethodDesc constructor = _module.GetMethod( + _module.MetadataReader.GetCustomAttribute(Handle).Constructor); + if (constructor is not null) + { + foreach (var entry in GetDependenciesFromField(factory, constructor.OwningType, namedArg.Name)) + yield return entry; + } + } + + foreach (var entry in GetDependenciesFromCustomAttributeArgument(factory, namedArg.Type, namedArg.Value)) + yield return entry; + } + } + + private IEnumerable GetDependenciesFromCustomAttributeArgument(NodeFactory factory, TypeDesc type, object value) + { + if (type is null) + yield break; + + // Report the type itself (e.g. enum types that need to be kept for boxing) + object reflectedType = factory.ReflectedType(type); + if (reflectedType is DependencyAnalysisFramework.DependencyNodeCore typeNode) + yield return new DependencyListEntry(typeNode, "Custom attribute blob"); + + if (type.UnderlyingType.IsPrimitive || type.IsString || value is null) + yield break; + + if (type.IsSzArray) + { + TypeDesc elementType = ((ArrayType)type).ElementType; + if (elementType.UnderlyingType.IsPrimitive || elementType.IsString) + yield break; + + if (value is ImmutableArray> arrayElements) + { + foreach (CustomAttributeTypedArgument element in arrayElements) + { + foreach (var entry in GetDependenciesFromCustomAttributeArgument(factory, element.Type, element.Value)) + yield return entry; + } + } + + yield break; + } + + // typeof() - the value is a TypeDesc + if (value is TypeDesc typeofType) + { + object reflectedTypeofType = factory.ReflectedType(typeofType); + if (reflectedTypeofType is DependencyAnalysisFramework.DependencyNodeCore typeofNode) + yield return new DependencyListEntry(typeofNode, "Custom attribute blob"); + } + } + + private static IEnumerable GetDependenciesFromPropertySetter(NodeFactory factory, TypeDesc attributeType, string propertyName) + { + if (attributeType is not EcmaType ecmaType) + yield break; + + MetadataReader reader = ecmaType.MetadataReader; + TypeDefinition typeDef = reader.GetTypeDefinition(ecmaType.Handle); + + foreach (PropertyDefinitionHandle propDefHandle in typeDef.GetProperties()) + { + PropertyDefinition propDef = reader.GetPropertyDefinition(propDefHandle); + if (reader.StringComparer.Equals(propDef.Name, propertyName)) + { + PropertyAccessors accessors = propDef.GetAccessors(); + if (!accessors.Setter.IsNil) + { + object reflectedMethod = factory.ReflectedMethod(ecmaType.Module.GetMethod(accessors.Setter)); + if (reflectedMethod is DependencyAnalysisFramework.DependencyNodeCore methodNode) + yield return new DependencyListEntry(methodNode, "Custom attribute blob"); + } + + yield break; + } + } + + // Check base type + TypeDesc baseType = attributeType.BaseType; + if (baseType is not null) + { + foreach (var entry in GetDependenciesFromPropertySetter(factory, baseType, propertyName)) + yield return entry; + } + } + + private static IEnumerable GetDependenciesFromField(NodeFactory factory, TypeDesc attributeType, string fieldName) + { + FieldDesc field = attributeType.GetField(Encoding.UTF8.GetBytes(fieldName)); + if (field is not null) + { + object reflectedField = factory.ReflectedField(field); + if (reflectedField is DependencyAnalysisFramework.DependencyNodeCore fieldNode) + yield return new DependencyListEntry(fieldNode, "Custom attribute blob"); + + yield break; + } + + // Check base type + TypeDesc baseType = attributeType.BaseType; + if (baseType is not null) + { + foreach (var entry in GetDependenciesFromField(factory, baseType, fieldName)) + yield return entry; + } } protected override EntityHandle WriteInternal(ModuleWritingContext writeContext) @@ -71,14 +222,345 @@ protected override EntityHandle WriteInternal(ModuleWritingContext writeContext) var builder = writeContext.MetadataBuilder; - // TODO: the value blob might contain references to tokens we need to rewrite - var valueBlob = reader.GetBlobBytes(customAttribute.Value); + // Resolve type name strings in the blob to their definitions. + // Trimming may drop type forwarders, so we re-encode type names + // to point to the assembly where the type is actually defined. + byte[] valueBlob = RewriteCustomAttributeBlob(customAttribute); return builder.AddCustomAttribute(writeContext.TokenMap.MapToken(customAttribute.Parent), writeContext.TokenMap.MapToken(customAttribute.Constructor), builder.GetOrAddBlob(valueBlob)); } + private byte[] RewriteCustomAttributeBlob(CustomAttribute customAttribute) + { + byte[] originalBlob = _module.MetadataReader.GetBlobBytes(customAttribute.Value); + + CustomAttributeValue decodedValue; + try + { + decodedValue = customAttribute.DecodeValue(new CustomAttributeTypeProvider(_module)); + } + catch (Exception) + { + return originalBlob; + } + + // Check if any type name string needs rewriting + var formatter = new CustomAttributeTypeNameFormatter(); + bool needsRewrite = false; + + CheckArgumentsForRewrite(decodedValue, formatter, originalBlob, ref needsRewrite); + + if (!needsRewrite) + return originalBlob; + + // Re-encode the entire blob + return EncodeCustomAttributeBlob(customAttribute, decodedValue, formatter); + } + + private void CheckArgumentsForRewrite( + CustomAttributeValue decodedValue, + CustomAttributeTypeNameFormatter formatter, + byte[] originalBlob, + ref bool needsRewrite) + { + foreach (CustomAttributeTypedArgument fixedArg in decodedValue.FixedArguments) + { + CheckArgumentValueForRewrite(fixedArg.Type, fixedArg.Value, formatter, ref needsRewrite); + if (needsRewrite) return; + } + + foreach (CustomAttributeNamedArgument namedArg in decodedValue.NamedArguments) + { + CheckArgumentValueForRewrite(namedArg.Type, namedArg.Value, formatter, ref needsRewrite); + if (needsRewrite) return; + } + } + + private void CheckArgumentValueForRewrite(TypeDesc type, object value, CustomAttributeTypeNameFormatter formatter, ref bool needsRewrite) + { + if (value is null || type is null) + return; + + if (value is TypeDesc typeofType) + { + // Check if the formatted name differs from what was originally stored + // Any typeof() with a type forwarder reference might need rewriting + needsRewrite = true; + return; + } + + if (type.IsSzArray && value is ImmutableArray> arrayElements) + { + TypeDesc elementType = ((ArrayType)type).ElementType; + if (!elementType.UnderlyingType.IsPrimitive && !elementType.IsString) + { + foreach (CustomAttributeTypedArgument element in arrayElements) + { + CheckArgumentValueForRewrite(element.Type, element.Value, formatter, ref needsRewrite); + if (needsRewrite) return; + } + } + } + } + + private byte[] EncodeCustomAttributeBlob( + CustomAttribute customAttribute, + CustomAttributeValue decodedValue, + CustomAttributeTypeNameFormatter formatter) + { + var blobBuilder = new BlobBuilder(); + + // Prolog (0x0001) + blobBuilder.WriteUInt16(1); + + // Resolve the constructor to get parameter types + MethodDesc constructor; + try + { + constructor = _module.GetMethod(customAttribute.Constructor); + } + catch (Exception) + { + return _module.MetadataReader.GetBlobBytes(customAttribute.Value); + } + + // Write fixed arguments + MethodSignature constructorSig = constructor.Signature; + for (int i = 0; i < decodedValue.FixedArguments.Length; i++) + { + TypeDesc paramType = constructorSig[i]; + WriteArgumentValue(blobBuilder, paramType, decodedValue.FixedArguments[i].Type, decodedValue.FixedArguments[i].Value, formatter, isFixedArg: true); + } + + // NumNamed + blobBuilder.WriteUInt16((ushort)decodedValue.NamedArguments.Length); + + // Write named arguments + foreach (CustomAttributeNamedArgument namedArg in decodedValue.NamedArguments) + { + // Field or property + blobBuilder.WriteByte(namedArg.Kind == CustomAttributeNamedArgumentKind.Field ? (byte)0x53 : (byte)0x54); + + // Field/property type + WriteFieldOrPropType(blobBuilder, namedArg.Type); + + // Name as SerString + WriteSerString(blobBuilder, namedArg.Name); + + // Value + WriteArgumentValue(blobBuilder, namedArg.Type, namedArg.Type, namedArg.Value, formatter, isFixedArg: false); + } + + return blobBuilder.ToArray(); + } + + private void WriteFieldOrPropType(BlobBuilder builder, TypeDesc type) + { + if (type is null) + return; + + // ECMA-335 II.23.3 - FieldOrPropType encoding + if (type.IsEnum) + { + builder.WriteByte(0x55); // ELEMENT_TYPE_ENUM + var formatter = new CustomAttributeTypeNameFormatter(); + WriteSerString(builder, formatter.FormatName(type, true)); + return; + } + + if (type.IsSzArray) + { + builder.WriteByte(0x1D); // ELEMENT_TYPE_SZARRAY + WriteFieldOrPropType(builder, ((ArrayType)type).ElementType); + return; + } + + switch (type.UnderlyingType.Category) + { + case TypeFlags.Boolean: builder.WriteByte(0x02); break; + case TypeFlags.Char: builder.WriteByte(0x03); break; + case TypeFlags.SByte: builder.WriteByte(0x04); break; + case TypeFlags.Byte: builder.WriteByte(0x05); break; + case TypeFlags.Int16: builder.WriteByte(0x06); break; + case TypeFlags.UInt16: builder.WriteByte(0x07); break; + case TypeFlags.Int32: builder.WriteByte(0x08); break; + case TypeFlags.UInt32: builder.WriteByte(0x09); break; + case TypeFlags.Int64: builder.WriteByte(0x0A); break; + case TypeFlags.UInt64: builder.WriteByte(0x0B); break; + case TypeFlags.Single: builder.WriteByte(0x0C); break; + case TypeFlags.Double: builder.WriteByte(0x0D); break; + default: + if (type.IsString) + { + builder.WriteByte(0x0E); // ELEMENT_TYPE_STRING + } + else if (IsSystemType(type)) + { + builder.WriteByte(0x50); // TYPE + } + else if (type.IsObject) + { + builder.WriteByte(0x51); // TAGGED_OBJECT / boxed + } + break; + } + } + + private void WriteArgumentValue(BlobBuilder builder, TypeDesc declaredType, TypeDesc actualType, object value, CustomAttributeTypeNameFormatter formatter, bool isFixedArg) + { + if (declaredType is null) + return; + + // Boxed (object-typed) arguments: write field/prop type tag then the value + if (declaredType.IsObject && !isFixedArg) + { + WriteBoxedArgumentValue(builder, actualType, value, formatter); + return; + } + + if (declaredType.IsObject && isFixedArg) + { + WriteBoxedArgumentValue(builder, actualType, value, formatter); + return; + } + + if (value is null) + { + if (declaredType.IsString) + { + // Null string + builder.WriteByte(0xFF); + return; + } + + if (IsSystemType(declaredType)) + { + // Null type + builder.WriteByte(0xFF); + return; + } + + if (declaredType.IsSzArray) + { + // Null array = 0xFFFFFFFF + builder.WriteUInt32(0xFFFFFFFF); + return; + } + } + + // Enum values + if (declaredType.IsEnum) + { + WritePrimitiveValue(builder, declaredType.UnderlyingType, value); + return; + } + + // Primitive types + if (declaredType.UnderlyingType.IsPrimitive) + { + WritePrimitiveValue(builder, declaredType, value); + return; + } + + // String + if (declaredType.IsString) + { + WriteSerString(builder, (string)value); + return; + } + + // System.Type (typeof) + if (IsSystemType(declaredType)) + { + if (value is TypeDesc typeofType) + { + string typeName = formatter.FormatName(typeofType, true); + WriteSerString(builder, typeName); + } + else + { + builder.WriteByte(0xFF); + } + return; + } + + // SZArray + if (declaredType.IsSzArray) + { + if (value is ImmutableArray> arrayElements) + { + builder.WriteUInt32((uint)arrayElements.Length); + TypeDesc elementType = ((ArrayType)declaredType).ElementType; + foreach (CustomAttributeTypedArgument element in arrayElements) + { + WriteArgumentValue(builder, elementType, element.Type, element.Value, formatter, isFixedArg: true); + } + } + else + { + builder.WriteUInt32(0xFFFFFFFF); + } + return; + } + } + + private void WriteBoxedArgumentValue(BlobBuilder builder, TypeDesc actualType, object value, CustomAttributeTypeNameFormatter formatter) + { + if (actualType is null || value is null) + { + // Null boxed value - this should not normally happen but handle gracefully + builder.WriteByte(0x0E); // string type tag + builder.WriteByte(0xFF); // null + return; + } + + WriteFieldOrPropType(builder, actualType); + WriteArgumentValue(builder, actualType, actualType, value, formatter, isFixedArg: true); + } + + private static void WritePrimitiveValue(BlobBuilder builder, TypeDesc type, object value) + { + switch (type.Category) + { + case TypeFlags.Boolean: builder.WriteBoolean((bool)value); break; + case TypeFlags.Char: builder.WriteUInt16((ushort)(char)value); break; + case TypeFlags.SByte: builder.WriteSByte((sbyte)value); break; + case TypeFlags.Byte: builder.WriteByte((byte)value); break; + case TypeFlags.Int16: builder.WriteInt16((short)value); break; + case TypeFlags.UInt16: builder.WriteUInt16((ushort)value); break; + case TypeFlags.Int32: builder.WriteInt32((int)value); break; + case TypeFlags.UInt32: builder.WriteUInt32((uint)value); break; + case TypeFlags.Int64: builder.WriteInt64((long)value); break; + case TypeFlags.UInt64: builder.WriteUInt64((ulong)value); break; + case TypeFlags.Single: builder.WriteSingle((float)value); break; + case TypeFlags.Double: builder.WriteDouble((double)value); break; + } + } + + private static void WriteSerString(BlobBuilder builder, string value) + { + if (value is null) + { + builder.WriteByte(0xFF); + return; + } + + byte[] bytes = Encoding.UTF8.GetBytes(value); + builder.WriteCompressedInteger(bytes.Length); + builder.WriteBytes(bytes); + } + + private static bool IsSystemType(TypeDesc type) + { + if (type is not MetadataType mdType) + return false; + + return mdType.Name.SequenceEqual("Type"u8) + && mdType.Namespace.SequenceEqual("System"u8); + } + public override string ToString() { // TODO: Need to write a helper to get the name of the type diff --git a/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt b/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt index 4f03cadcadc2be..19ece9bcf720c6 100644 --- a/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt +++ b/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt @@ -1,31 +1,11 @@ Advanced.TypeCheckRemoval Attributes.AssemblyAttributeAccessesMembers Attributes.AssemblyAttributeIsRemovedIfOnlyTypesUsedInAssembly -Attributes.AttributeOnPreservedTypeWithTypeUsedInConstructorIsKept -Attributes.AttributeOnPreservedTypeWithTypeUsedInDifferentNamespaceIsKept -Attributes.AttributeOnPreservedTypeWithTypeUsedInFieldIsKept -Attributes.AttributeOnPreservedTypeWithTypeUsedInPropertySetterIsKept -Attributes.AttributeOnPreservedTypeWithUsedSetter Attributes.BoxedValues Attributes.CoreLibraryAssemblyAttributesAreKept -Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeArrayOnAttributeCtorOnType Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributeCtorOnAssembly -Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributeCtorOnAssemblyOtherTypesInAttributeAssemblyUsed -Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributeCtorOnEvent -Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributeCtorOnMethod -Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributeCtorOnProperty -Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributeCtorOnType Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributeFieldOnAssembly -Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributeFieldOnEvent -Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributeFieldOnMethod -Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributeFieldOnProperty -Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributeFieldOnType Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributePropertyOnAssembly -Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributePropertyOnEvent -Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributePropertyOnMethod -Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributePropertyOnProperty -Attributes.Csc.OnlyTypeUsedInAssemblyIsTypeOnAttributePropertyOnType -Attributes.Csc.OnlyTypeUsedInAssemblyWithReferenceIsTypeOnAttributeCtorOnType Attributes.Debugger.DebuggerDisplayAttributeOnAssemblyUsingTarget Attributes.Debugger.DebuggerDisplayAttributeOnAssemblyUsingTargetOnUnusedType Attributes.Debugger.DebuggerDisplayAttributeOnAssemblyUsingTargetTypeNameInOtherAssembly @@ -71,7 +51,6 @@ Attributes.SecurityAttributesOnUsedMethodAreKept Attributes.SecurityAttributesOnUsedTypeAreKept Attributes.StructLayout.ExplicitClass Attributes.StructLayout.SequentialClass -Attributes.TypeUsedInObjectArrayConstructorArgumentOnAttributeIsKept Attributes.TypeWithDynamicInterfaceCastableImplementationAttributeIsKept Basic.InstantiatedTypeWithOverridesFromObject Basic.UninvokedInterfaceMemberGetsRemoved @@ -105,7 +84,6 @@ DataFlow.CompilerGeneratedTypes DataFlow.CompilerGeneratedTypesNet90 DataFlow.CompilerGeneratedTypesRelease DataFlow.CompilerGeneratedTypesReleaseNet90 -DataFlow.ComplexTypeHandling DataFlow.ConstructorDataFlow DataFlow.DependencyInjectionPattern DataFlow.DynamicDependencyDataflow @@ -384,7 +362,6 @@ Reflection.RuntimeReflectionExtensionsCalls Reflection.TypeHierarchyLibraryModeSuppressions Reflection.TypeHierarchyReflectionWarnings Reflection.TypeMap -Reflection.TypeMapEntryAssembly Reflection.TypeUsedViaReflection Reflection.UnsafeAccessor RequiresCapability.BasicRequires @@ -445,7 +422,6 @@ Symbols.ReferenceWithPortablePdbCopyActionAndSymbolLinkingEnabled TestFramework.VerifyAttributesInAssemblyWorks TestFramework.VerifyAttributesInAssemblyWorksWithStrings TestFramework.VerifyLocalsAreChanged -TypeForwarding.AttributeArgumentForwarded TypeForwarding.AttributeArgumentForwardedWithCopyAction TypeForwarding.AttributeEnumArgumentForwardedCopyUsedWithSweptForwarder TypeForwarding.AttributesScopeUpdated From d600986704e0a2b2068412a780bb3cb11619c0cb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Apr 2026 04:25:17 +0000 Subject: [PATCH 02/26] Address code review feedback: narrow exception types, add type alias, remove unused parameter - Catch TypeSystemException and BadImageFormatException instead of base Exception - Add DependencyNode type alias to reduce verbose fully-qualified type names - Remove unused isFixedArg parameter from WriteArgumentValue - Hoist constructor resolution out of named argument loop - Fix duplicate CustomAttributeTypeNameFormatter allocations Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/f4173cc7-fd45-4a62-9a1c-1a31c06cc85b Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 75 +++++++++---------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index bb57d1a0a36c9a..4c03c3e250a2c8 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -10,6 +10,8 @@ using Internal.TypeSystem; using Internal.TypeSystem.Ecma; +using DependencyNode = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore; + namespace ILCompiler.DependencyAnalysis { /// @@ -71,9 +73,15 @@ public override IEnumerable GetStaticDependencies(NodeFacto { decodedValue = customAttribute.DecodeValue(new CustomAttributeTypeProvider(_module)); } - catch (Exception) + catch (TypeSystemException) + { + // Attribute ctor doesn't resolve, typeof() refers to something that can't be loaded, + // attribute refers to a non-existing field, etc. + yield break; + } + catch (BadImageFormatException) { - // If decoding fails (bad metadata, unresolvable types, etc.), skip blob analysis. + // System.Reflection.Metadata throws BadImageFormatException if the blob is malformed. yield break; } @@ -89,26 +97,20 @@ private IEnumerable GetDependenciesFromCustomAttributeBlob( yield return entry; } + // Resolve the constructor once for all named arguments + MethodDesc constructor = _module.GetMethod( + _module.MetadataReader.GetCustomAttribute(Handle).Constructor); + foreach (CustomAttributeNamedArgument namedArg in value.NamedArguments) { - // Report the type that declares the field/property - // (The constructor's declaring type was already reported above.) - if (namedArg.Kind == CustomAttributeNamedArgumentKind.Property) + if (constructor is not null) { - // We need the setter of the property to be kept - MethodDesc constructor = _module.GetMethod( - _module.MetadataReader.GetCustomAttribute(Handle).Constructor); - if (constructor is not null) + if (namedArg.Kind == CustomAttributeNamedArgumentKind.Property) { foreach (var entry in GetDependenciesFromPropertySetter(factory, constructor.OwningType, namedArg.Name)) yield return entry; } - } - else if (namedArg.Kind == CustomAttributeNamedArgumentKind.Field) - { - MethodDesc constructor = _module.GetMethod( - _module.MetadataReader.GetCustomAttribute(Handle).Constructor); - if (constructor is not null) + else if (namedArg.Kind == CustomAttributeNamedArgumentKind.Field) { foreach (var entry in GetDependenciesFromField(factory, constructor.OwningType, namedArg.Name)) yield return entry; @@ -127,7 +129,7 @@ private IEnumerable GetDependenciesFromCustomAttributeArgum // Report the type itself (e.g. enum types that need to be kept for boxing) object reflectedType = factory.ReflectedType(type); - if (reflectedType is DependencyAnalysisFramework.DependencyNodeCore typeNode) + if (reflectedType is DependencyNode typeNode) yield return new DependencyListEntry(typeNode, "Custom attribute blob"); if (type.UnderlyingType.IsPrimitive || type.IsString || value is null) @@ -155,7 +157,7 @@ private IEnumerable GetDependenciesFromCustomAttributeArgum if (value is TypeDesc typeofType) { object reflectedTypeofType = factory.ReflectedType(typeofType); - if (reflectedTypeofType is DependencyAnalysisFramework.DependencyNodeCore typeofNode) + if (reflectedTypeofType is DependencyNode typeofNode) yield return new DependencyListEntry(typeofNode, "Custom attribute blob"); } } @@ -177,7 +179,7 @@ private static IEnumerable GetDependenciesFromPropertySette if (!accessors.Setter.IsNil) { object reflectedMethod = factory.ReflectedMethod(ecmaType.Module.GetMethod(accessors.Setter)); - if (reflectedMethod is DependencyAnalysisFramework.DependencyNodeCore methodNode) + if (reflectedMethod is DependencyNode methodNode) yield return new DependencyListEntry(methodNode, "Custom attribute blob"); } @@ -200,7 +202,7 @@ private static IEnumerable GetDependenciesFromField(NodeFac if (field is not null) { object reflectedField = factory.ReflectedField(field); - if (reflectedField is DependencyAnalysisFramework.DependencyNodeCore fieldNode) + if (reflectedField is DependencyNode fieldNode) yield return new DependencyListEntry(fieldNode, "Custom attribute blob"); yield break; @@ -241,7 +243,11 @@ private byte[] RewriteCustomAttributeBlob(CustomAttribute customAttribute) { decodedValue = customAttribute.DecodeValue(new CustomAttributeTypeProvider(_module)); } - catch (Exception) + catch (TypeSystemException) + { + return originalBlob; + } + catch (BadImageFormatException) { return originalBlob; } @@ -321,7 +327,7 @@ private byte[] EncodeCustomAttributeBlob( { constructor = _module.GetMethod(customAttribute.Constructor); } - catch (Exception) + catch (TypeSystemException) { return _module.MetadataReader.GetBlobBytes(customAttribute.Value); } @@ -331,7 +337,7 @@ private byte[] EncodeCustomAttributeBlob( for (int i = 0; i < decodedValue.FixedArguments.Length; i++) { TypeDesc paramType = constructorSig[i]; - WriteArgumentValue(blobBuilder, paramType, decodedValue.FixedArguments[i].Type, decodedValue.FixedArguments[i].Value, formatter, isFixedArg: true); + WriteArgumentValue(blobBuilder, paramType, decodedValue.FixedArguments[i].Type, decodedValue.FixedArguments[i].Value, formatter); } // NumNamed @@ -344,19 +350,19 @@ private byte[] EncodeCustomAttributeBlob( blobBuilder.WriteByte(namedArg.Kind == CustomAttributeNamedArgumentKind.Field ? (byte)0x53 : (byte)0x54); // Field/property type - WriteFieldOrPropType(blobBuilder, namedArg.Type); + WriteFieldOrPropType(blobBuilder, namedArg.Type, formatter); // Name as SerString WriteSerString(blobBuilder, namedArg.Name); // Value - WriteArgumentValue(blobBuilder, namedArg.Type, namedArg.Type, namedArg.Value, formatter, isFixedArg: false); + WriteArgumentValue(blobBuilder, namedArg.Type, namedArg.Type, namedArg.Value, formatter); } return blobBuilder.ToArray(); } - private void WriteFieldOrPropType(BlobBuilder builder, TypeDesc type) + private static void WriteFieldOrPropType(BlobBuilder builder, TypeDesc type, CustomAttributeTypeNameFormatter formatter) { if (type is null) return; @@ -365,7 +371,6 @@ private void WriteFieldOrPropType(BlobBuilder builder, TypeDesc type) if (type.IsEnum) { builder.WriteByte(0x55); // ELEMENT_TYPE_ENUM - var formatter = new CustomAttributeTypeNameFormatter(); WriteSerString(builder, formatter.FormatName(type, true)); return; } @@ -373,7 +378,7 @@ private void WriteFieldOrPropType(BlobBuilder builder, TypeDesc type) if (type.IsSzArray) { builder.WriteByte(0x1D); // ELEMENT_TYPE_SZARRAY - WriteFieldOrPropType(builder, ((ArrayType)type).ElementType); + WriteFieldOrPropType(builder, ((ArrayType)type).ElementType, formatter); return; } @@ -408,19 +413,13 @@ private void WriteFieldOrPropType(BlobBuilder builder, TypeDesc type) } } - private void WriteArgumentValue(BlobBuilder builder, TypeDesc declaredType, TypeDesc actualType, object value, CustomAttributeTypeNameFormatter formatter, bool isFixedArg) + private void WriteArgumentValue(BlobBuilder builder, TypeDesc declaredType, TypeDesc actualType, object value, CustomAttributeTypeNameFormatter formatter) { if (declaredType is null) return; // Boxed (object-typed) arguments: write field/prop type tag then the value - if (declaredType.IsObject && !isFixedArg) - { - WriteBoxedArgumentValue(builder, actualType, value, formatter); - return; - } - - if (declaredType.IsObject && isFixedArg) + if (declaredType.IsObject) { WriteBoxedArgumentValue(builder, actualType, value, formatter); return; @@ -495,7 +494,7 @@ private void WriteArgumentValue(BlobBuilder builder, TypeDesc declaredType, Type TypeDesc elementType = ((ArrayType)declaredType).ElementType; foreach (CustomAttributeTypedArgument element in arrayElements) { - WriteArgumentValue(builder, elementType, element.Type, element.Value, formatter, isFixedArg: true); + WriteArgumentValue(builder, elementType, element.Type, element.Value, formatter); } } else @@ -516,8 +515,8 @@ private void WriteBoxedArgumentValue(BlobBuilder builder, TypeDesc actualType, o return; } - WriteFieldOrPropType(builder, actualType); - WriteArgumentValue(builder, actualType, actualType, value, formatter, isFixedArg: true); + WriteFieldOrPropType(builder, actualType, formatter); + WriteArgumentValue(builder, actualType, actualType, value, formatter); } private static void WritePrimitiveValue(BlobBuilder builder, TypeDesc type, object value) From da3465b55b4180c8e1caa416beda2c806991c079 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Apr 2026 09:04:10 +0000 Subject: [PATCH 03/26] Refactor custom attribute encoding to use BlobEncoder APIs and shared BlobBuilder Use BlobEncoder.CustomAttributeSignature with FixedArgumentsEncoder and CustomAttributeNamedArgumentsEncoder instead of manual byte writing. Use writeContext.GetSharedBlobBuilder() following established patterns. Removed byte[] materialization - writes directly to shared BlobBuilder. Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/d078b767-9d76-41e5-9168-8f6651786108 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 296 +++++++----------- 1 file changed, 116 insertions(+), 180 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 4c03c3e250a2c8..0b3006eae76c09 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; using System.Text; using Internal.TypeSystem; @@ -227,17 +228,16 @@ protected override EntityHandle WriteInternal(ModuleWritingContext writeContext) // Resolve type name strings in the blob to their definitions. // Trimming may drop type forwarders, so we re-encode type names // to point to the assembly where the type is actually defined. - byte[] valueBlob = RewriteCustomAttributeBlob(customAttribute); + BlobBuilder blobBuilder = writeContext.GetSharedBlobBuilder(); + RewriteCustomAttributeBlob(customAttribute, blobBuilder); return builder.AddCustomAttribute(writeContext.TokenMap.MapToken(customAttribute.Parent), writeContext.TokenMap.MapToken(customAttribute.Constructor), - builder.GetOrAddBlob(valueBlob)); + builder.GetOrAddBlob(blobBuilder)); } - private byte[] RewriteCustomAttributeBlob(CustomAttribute customAttribute) + private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBuilder blobBuilder) { - byte[] originalBlob = _module.MetadataReader.GetBlobBytes(customAttribute.Value); - CustomAttributeValue decodedValue; try { @@ -245,53 +245,49 @@ private byte[] RewriteCustomAttributeBlob(CustomAttribute customAttribute) } catch (TypeSystemException) { - return originalBlob; + blobBuilder.WriteBytes(_module.MetadataReader.GetBlobBytes(customAttribute.Value)); + return; } catch (BadImageFormatException) { - return originalBlob; + blobBuilder.WriteBytes(_module.MetadataReader.GetBlobBytes(customAttribute.Value)); + return; } - // Check if any type name string needs rewriting - var formatter = new CustomAttributeTypeNameFormatter(); bool needsRewrite = false; - - CheckArgumentsForRewrite(decodedValue, formatter, originalBlob, ref needsRewrite); + CheckArgumentsForRewrite(decodedValue, ref needsRewrite); if (!needsRewrite) - return originalBlob; + { + blobBuilder.WriteBytes(_module.MetadataReader.GetBlobBytes(customAttribute.Value)); + return; + } - // Re-encode the entire blob - return EncodeCustomAttributeBlob(customAttribute, decodedValue, formatter); + EncodeCustomAttributeBlob(customAttribute, decodedValue, blobBuilder); } - private void CheckArgumentsForRewrite( - CustomAttributeValue decodedValue, - CustomAttributeTypeNameFormatter formatter, - byte[] originalBlob, - ref bool needsRewrite) + private static void CheckArgumentsForRewrite(CustomAttributeValue decodedValue, ref bool needsRewrite) { foreach (CustomAttributeTypedArgument fixedArg in decodedValue.FixedArguments) { - CheckArgumentValueForRewrite(fixedArg.Type, fixedArg.Value, formatter, ref needsRewrite); + CheckArgumentValueForRewrite(fixedArg.Type, fixedArg.Value, ref needsRewrite); if (needsRewrite) return; } foreach (CustomAttributeNamedArgument namedArg in decodedValue.NamedArguments) { - CheckArgumentValueForRewrite(namedArg.Type, namedArg.Value, formatter, ref needsRewrite); + CheckArgumentValueForRewrite(namedArg.Type, namedArg.Value, ref needsRewrite); if (needsRewrite) return; } } - private void CheckArgumentValueForRewrite(TypeDesc type, object value, CustomAttributeTypeNameFormatter formatter, ref bool needsRewrite) + private static void CheckArgumentValueForRewrite(TypeDesc type, object value, ref bool needsRewrite) { if (value is null || type is null) return; - if (value is TypeDesc typeofType) + if (value is TypeDesc) { - // Check if the formatted name differs from what was originally stored // Any typeof() with a type forwarder reference might need rewriting needsRewrite = true; return; @@ -304,24 +300,18 @@ private void CheckArgumentValueForRewrite(TypeDesc type, object value, CustomAtt { foreach (CustomAttributeTypedArgument element in arrayElements) { - CheckArgumentValueForRewrite(element.Type, element.Value, formatter, ref needsRewrite); + CheckArgumentValueForRewrite(element.Type, element.Value, ref needsRewrite); if (needsRewrite) return; } } } } - private byte[] EncodeCustomAttributeBlob( + private void EncodeCustomAttributeBlob( CustomAttribute customAttribute, CustomAttributeValue decodedValue, - CustomAttributeTypeNameFormatter formatter) + BlobBuilder blobBuilder) { - var blobBuilder = new BlobBuilder(); - - // Prolog (0x0001) - blobBuilder.WriteUInt16(1); - - // Resolve the constructor to get parameter types MethodDesc constructor; try { @@ -329,226 +319,172 @@ private byte[] EncodeCustomAttributeBlob( } catch (TypeSystemException) { - return _module.MetadataReader.GetBlobBytes(customAttribute.Value); + blobBuilder.WriteBytes(_module.MetadataReader.GetBlobBytes(customAttribute.Value)); + return; } + var formatter = new CustomAttributeTypeNameFormatter(); + var encoder = new BlobEncoder(blobBuilder); + encoder.CustomAttributeSignature(out FixedArgumentsEncoder fixedArgs, out CustomAttributeNamedArgumentsEncoder namedArgs); + // Write fixed arguments MethodSignature constructorSig = constructor.Signature; for (int i = 0; i < decodedValue.FixedArguments.Length; i++) { TypeDesc paramType = constructorSig[i]; - WriteArgumentValue(blobBuilder, paramType, decodedValue.FixedArguments[i].Type, decodedValue.FixedArguments[i].Value, formatter); + WriteLiteralValue(fixedArgs.AddArgument(), paramType, decodedValue.FixedArguments[i].Type, decodedValue.FixedArguments[i].Value, formatter); } - // NumNamed - blobBuilder.WriteUInt16((ushort)decodedValue.NamedArguments.Length); - // Write named arguments + NamedArgumentsEncoder namedArgsEncoder = namedArgs.Count(decodedValue.NamedArguments.Length); foreach (CustomAttributeNamedArgument namedArg in decodedValue.NamedArguments) { - // Field or property - blobBuilder.WriteByte(namedArg.Kind == CustomAttributeNamedArgumentKind.Field ? (byte)0x53 : (byte)0x54); + namedArgsEncoder.AddArgument( + namedArg.Kind == CustomAttributeNamedArgumentKind.Field, + out NamedArgumentTypeEncoder type, + out NameEncoder name, + out LiteralEncoder literal); - // Field/property type - WriteFieldOrPropType(blobBuilder, namedArg.Type, formatter); + EncodeNamedArgumentType(type, namedArg.Type, formatter); + name.Name(namedArg.Name); + WriteLiteralValue(literal, namedArg.Type, namedArg.Type, namedArg.Value, formatter); + } + } - // Name as SerString - WriteSerString(blobBuilder, namedArg.Name); + private static void EncodeNamedArgumentType(NamedArgumentTypeEncoder type, TypeDesc argType, CustomAttributeTypeNameFormatter formatter) + { + if (argType.IsObject) + { + type.Object(); + return; + } - // Value - WriteArgumentValue(blobBuilder, namedArg.Type, namedArg.Type, namedArg.Value, formatter); + if (argType.IsSzArray) + { + TypeDesc elementType = ((ArrayType)argType).ElementType; + if (elementType.IsObject) + { + type.SZArray().ObjectArray(); + } + else + { + EncodeElementType(type.SZArray().ElementType(), elementType, formatter); + } + return; } - return blobBuilder.ToArray(); + EncodeElementType(type.ScalarType(), argType, formatter); } - private static void WriteFieldOrPropType(BlobBuilder builder, TypeDesc type, CustomAttributeTypeNameFormatter formatter) + private static void EncodeElementType(CustomAttributeElementTypeEncoder encoder, TypeDesc type, CustomAttributeTypeNameFormatter formatter) { - if (type is null) - return; - - // ECMA-335 II.23.3 - FieldOrPropType encoding if (type.IsEnum) { - builder.WriteByte(0x55); // ELEMENT_TYPE_ENUM - WriteSerString(builder, formatter.FormatName(type, true)); + encoder.Enum(formatter.FormatName(type, true)); return; } - if (type.IsSzArray) + if (IsSystemType(type)) { - builder.WriteByte(0x1D); // ELEMENT_TYPE_SZARRAY - WriteFieldOrPropType(builder, ((ArrayType)type).ElementType, formatter); + encoder.SystemType(); return; } switch (type.UnderlyingType.Category) { - case TypeFlags.Boolean: builder.WriteByte(0x02); break; - case TypeFlags.Char: builder.WriteByte(0x03); break; - case TypeFlags.SByte: builder.WriteByte(0x04); break; - case TypeFlags.Byte: builder.WriteByte(0x05); break; - case TypeFlags.Int16: builder.WriteByte(0x06); break; - case TypeFlags.UInt16: builder.WriteByte(0x07); break; - case TypeFlags.Int32: builder.WriteByte(0x08); break; - case TypeFlags.UInt32: builder.WriteByte(0x09); break; - case TypeFlags.Int64: builder.WriteByte(0x0A); break; - case TypeFlags.UInt64: builder.WriteByte(0x0B); break; - case TypeFlags.Single: builder.WriteByte(0x0C); break; - case TypeFlags.Double: builder.WriteByte(0x0D); break; + case TypeFlags.Boolean: encoder.Boolean(); break; + case TypeFlags.Char: encoder.Char(); break; + case TypeFlags.SByte: encoder.SByte(); break; + case TypeFlags.Byte: encoder.Byte(); break; + case TypeFlags.Int16: encoder.Int16(); break; + case TypeFlags.UInt16: encoder.UInt16(); break; + case TypeFlags.Int32: encoder.Int32(); break; + case TypeFlags.UInt32: encoder.UInt32(); break; + case TypeFlags.Int64: encoder.Int64(); break; + case TypeFlags.UInt64: encoder.UInt64(); break; + case TypeFlags.Single: encoder.Single(); break; + case TypeFlags.Double: encoder.Double(); break; default: if (type.IsString) - { - builder.WriteByte(0x0E); // ELEMENT_TYPE_STRING - } - else if (IsSystemType(type)) - { - builder.WriteByte(0x50); // TYPE - } - else if (type.IsObject) - { - builder.WriteByte(0x51); // TAGGED_OBJECT / boxed - } + encoder.String(); break; } } - private void WriteArgumentValue(BlobBuilder builder, TypeDesc declaredType, TypeDesc actualType, object value, CustomAttributeTypeNameFormatter formatter) + private void WriteLiteralValue(LiteralEncoder literal, TypeDesc declaredType, TypeDesc actualType, object value, CustomAttributeTypeNameFormatter formatter) { if (declaredType is null) return; - // Boxed (object-typed) arguments: write field/prop type tag then the value + // Boxed (object-typed) arguments: write type tag then the value if (declaredType.IsObject) { - WriteBoxedArgumentValue(builder, actualType, value, formatter); - return; - } - - if (value is null) - { - if (declaredType.IsString) + if (value is null) { - // Null string - builder.WriteByte(0xFF); + literal.TaggedScalar(out CustomAttributeElementTypeEncoder type, out ScalarEncoder scalar); + type.String(); + scalar.Constant(null); return; } - if (IsSystemType(declaredType)) + if (actualType is not null && actualType.IsSzArray) { - // Null type - builder.WriteByte(0xFF); + literal.TaggedVector(out CustomAttributeArrayTypeEncoder arrayType, out VectorEncoder vector); + TypeDesc elementType = ((ArrayType)actualType).ElementType; + if (elementType.IsObject) + arrayType.ObjectArray(); + else + EncodeElementType(arrayType.ElementType(), elementType, formatter); + WriteVectorElements(vector, elementType, value, formatter); return; } - if (declaredType.IsSzArray) - { - // Null array = 0xFFFFFFFF - builder.WriteUInt32(0xFFFFFFFF); - return; - } - } - - // Enum values - if (declaredType.IsEnum) - { - WritePrimitiveValue(builder, declaredType.UnderlyingType, value); + literal.TaggedScalar(out CustomAttributeElementTypeEncoder typeEncoder, out ScalarEncoder scalarEncoder); + EncodeElementType(typeEncoder, actualType, formatter); + WriteScalarValue(scalarEncoder, actualType, value, formatter); return; } - // Primitive types - if (declaredType.UnderlyingType.IsPrimitive) - { - WritePrimitiveValue(builder, declaredType, value); - return; - } - - // String - if (declaredType.IsString) - { - WriteSerString(builder, (string)value); - return; - } - - // System.Type (typeof) - if (IsSystemType(declaredType)) - { - if (value is TypeDesc typeofType) - { - string typeName = formatter.FormatName(typeofType, true); - WriteSerString(builder, typeName); - } - else - { - builder.WriteByte(0xFF); - } - return; - } - - // SZArray + // Array arguments if (declaredType.IsSzArray) { - if (value is ImmutableArray> arrayElements) - { - builder.WriteUInt32((uint)arrayElements.Length); - TypeDesc elementType = ((ArrayType)declaredType).ElementType; - foreach (CustomAttributeTypedArgument element in arrayElements) - { - WriteArgumentValue(builder, elementType, element.Type, element.Value, formatter); - } - } - else + if (value is null) { - builder.WriteUInt32(0xFFFFFFFF); + literal.Scalar().NullArray(); + return; } - return; - } - } - private void WriteBoxedArgumentValue(BlobBuilder builder, TypeDesc actualType, object value, CustomAttributeTypeNameFormatter formatter) - { - if (actualType is null || value is null) - { - // Null boxed value - this should not normally happen but handle gracefully - builder.WriteByte(0x0E); // string type tag - builder.WriteByte(0xFF); // null + TypeDesc elementType = ((ArrayType)declaredType).ElementType; + WriteVectorElements(literal.Vector(), elementType, value, formatter); return; } - WriteFieldOrPropType(builder, actualType, formatter); - WriteArgumentValue(builder, actualType, actualType, value, formatter); + // Scalar value (primitive, enum, string, Type) + WriteScalarValue(literal.Scalar(), declaredType, value, formatter); } - private static void WritePrimitiveValue(BlobBuilder builder, TypeDesc type, object value) + private void WriteVectorElements(VectorEncoder vector, TypeDesc elementType, object value, CustomAttributeTypeNameFormatter formatter) { - switch (type.Category) - { - case TypeFlags.Boolean: builder.WriteBoolean((bool)value); break; - case TypeFlags.Char: builder.WriteUInt16((ushort)(char)value); break; - case TypeFlags.SByte: builder.WriteSByte((sbyte)value); break; - case TypeFlags.Byte: builder.WriteByte((byte)value); break; - case TypeFlags.Int16: builder.WriteInt16((short)value); break; - case TypeFlags.UInt16: builder.WriteUInt16((ushort)value); break; - case TypeFlags.Int32: builder.WriteInt32((int)value); break; - case TypeFlags.UInt32: builder.WriteUInt32((uint)value); break; - case TypeFlags.Int64: builder.WriteInt64((long)value); break; - case TypeFlags.UInt64: builder.WriteUInt64((ulong)value); break; - case TypeFlags.Single: builder.WriteSingle((float)value); break; - case TypeFlags.Double: builder.WriteDouble((double)value); break; + if (value is ImmutableArray> arrayElements) + { + LiteralsEncoder literals = vector.Count(arrayElements.Length); + foreach (CustomAttributeTypedArgument element in arrayElements) + { + WriteLiteralValue(literals.AddLiteral(), elementType, element.Type, element.Value, formatter); + } } } - private static void WriteSerString(BlobBuilder builder, string value) + private static void WriteScalarValue(ScalarEncoder scalar, TypeDesc type, object value, CustomAttributeTypeNameFormatter formatter) { - if (value is null) + if (IsSystemType(type)) { - builder.WriteByte(0xFF); + scalar.SystemType(value is TypeDesc typeofType ? formatter.FormatName(typeofType, true) : null); return; } - byte[] bytes = Encoding.UTF8.GetBytes(value); - builder.WriteCompressedInteger(bytes.Length); - builder.WriteBytes(bytes); + // Primitives, enums (underlying value), strings, null + scalar.Constant(value); } private static bool IsSystemType(TypeDesc type) From 20a8fefc8ffcbe35eb7e9665a4d1164e29369a57 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:08:23 +0000 Subject: [PATCH 04/26] Remove premature rewrite optimization - always re-encode custom attribute blobs Remove CheckArgumentsForRewrite/CheckArgumentValueForRewrite that skipped rewriting when no typeof() references were found. Always rewrite to exercise the encoding logic and surface bugs. Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/46e2e54d-5313-4538-8274-c5cfe241e547 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 50 ------------------- 1 file changed, 50 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 0b3006eae76c09..036d43227bbda0 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -254,59 +254,9 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui return; } - bool needsRewrite = false; - CheckArgumentsForRewrite(decodedValue, ref needsRewrite); - - if (!needsRewrite) - { - blobBuilder.WriteBytes(_module.MetadataReader.GetBlobBytes(customAttribute.Value)); - return; - } - EncodeCustomAttributeBlob(customAttribute, decodedValue, blobBuilder); } - private static void CheckArgumentsForRewrite(CustomAttributeValue decodedValue, ref bool needsRewrite) - { - foreach (CustomAttributeTypedArgument fixedArg in decodedValue.FixedArguments) - { - CheckArgumentValueForRewrite(fixedArg.Type, fixedArg.Value, ref needsRewrite); - if (needsRewrite) return; - } - - foreach (CustomAttributeNamedArgument namedArg in decodedValue.NamedArguments) - { - CheckArgumentValueForRewrite(namedArg.Type, namedArg.Value, ref needsRewrite); - if (needsRewrite) return; - } - } - - private static void CheckArgumentValueForRewrite(TypeDesc type, object value, ref bool needsRewrite) - { - if (value is null || type is null) - return; - - if (value is TypeDesc) - { - // Any typeof() with a type forwarder reference might need rewriting - needsRewrite = true; - return; - } - - if (type.IsSzArray && value is ImmutableArray> arrayElements) - { - TypeDesc elementType = ((ArrayType)type).ElementType; - if (!elementType.UnderlyingType.IsPrimitive && !elementType.IsString) - { - foreach (CustomAttributeTypedArgument element in arrayElements) - { - CheckArgumentValueForRewrite(element.Type, element.Value, ref needsRewrite); - if (needsRewrite) return; - } - } - } - } - private void EncodeCustomAttributeBlob( CustomAttribute customAttribute, CustomAttributeValue decodedValue, From 229cd7f28862a043e4cc379934fa021767e75774 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 11:23:24 +0000 Subject: [PATCH 05/26] Refactor to use if/else chains instead of early returns, switch on type.Category - Replace early returns with cascading if/else if/else in EncodeNamedArgumentType, WriteLiteralValue, and WriteScalarValue. - Switch on type.Category instead of type.UnderlyingType.Category in EncodeElementType, handling IsEnum and System.Type in the default case. Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/683a21bb-4e07-4219-81e8-b0a320967bd3 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 89 ++++++++----------- 1 file changed, 35 insertions(+), 54 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 036d43227bbda0..a63fef18453196 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -306,41 +306,24 @@ private static void EncodeNamedArgumentType(NamedArgumentTypeEncoder type, TypeD if (argType.IsObject) { type.Object(); - return; } - - if (argType.IsSzArray) + else if (argType.IsSzArray) { TypeDesc elementType = ((ArrayType)argType).ElementType; if (elementType.IsObject) - { type.SZArray().ObjectArray(); - } else - { EncodeElementType(type.SZArray().ElementType(), elementType, formatter); - } - return; } - - EncodeElementType(type.ScalarType(), argType, formatter); + else + { + EncodeElementType(type.ScalarType(), argType, formatter); + } } private static void EncodeElementType(CustomAttributeElementTypeEncoder encoder, TypeDesc type, CustomAttributeTypeNameFormatter formatter) { - if (type.IsEnum) - { - encoder.Enum(formatter.FormatName(type, true)); - return; - } - - if (IsSystemType(type)) - { - encoder.SystemType(); - return; - } - - switch (type.UnderlyingType.Category) + switch (type.Category) { case TypeFlags.Boolean: encoder.Boolean(); break; case TypeFlags.Char: encoder.Char(); break; @@ -355,7 +338,11 @@ private static void EncodeElementType(CustomAttributeElementTypeEncoder encoder, case TypeFlags.Single: encoder.Single(); break; case TypeFlags.Double: encoder.Double(); break; default: - if (type.IsString) + if (type.IsEnum) + encoder.Enum(formatter.FormatName(type, true)); + else if (IsSystemType(type)) + encoder.SystemType(); + else if (type.IsString) encoder.String(); break; } @@ -364,20 +351,18 @@ private static void EncodeElementType(CustomAttributeElementTypeEncoder encoder, private void WriteLiteralValue(LiteralEncoder literal, TypeDesc declaredType, TypeDesc actualType, object value, CustomAttributeTypeNameFormatter formatter) { if (declaredType is null) - return; - - // Boxed (object-typed) arguments: write type tag then the value - if (declaredType.IsObject) + { + // Nothing to write + } + else if (declaredType.IsObject) { if (value is null) { literal.TaggedScalar(out CustomAttributeElementTypeEncoder type, out ScalarEncoder scalar); type.String(); scalar.Constant(null); - return; } - - if (actualType is not null && actualType.IsSzArray) + else if (actualType is not null && actualType.IsSzArray) { literal.TaggedVector(out CustomAttributeArrayTypeEncoder arrayType, out VectorEncoder vector); TypeDesc elementType = ((ArrayType)actualType).ElementType; @@ -386,31 +371,31 @@ private void WriteLiteralValue(LiteralEncoder literal, TypeDesc declaredType, Ty else EncodeElementType(arrayType.ElementType(), elementType, formatter); WriteVectorElements(vector, elementType, value, formatter); - return; } - - literal.TaggedScalar(out CustomAttributeElementTypeEncoder typeEncoder, out ScalarEncoder scalarEncoder); - EncodeElementType(typeEncoder, actualType, formatter); - WriteScalarValue(scalarEncoder, actualType, value, formatter); - return; + else + { + literal.TaggedScalar(out CustomAttributeElementTypeEncoder typeEncoder, out ScalarEncoder scalarEncoder); + EncodeElementType(typeEncoder, actualType, formatter); + WriteScalarValue(scalarEncoder, actualType, value, formatter); + } } - - // Array arguments - if (declaredType.IsSzArray) + else if (declaredType.IsSzArray) { if (value is null) { literal.Scalar().NullArray(); - return; } - - TypeDesc elementType = ((ArrayType)declaredType).ElementType; - WriteVectorElements(literal.Vector(), elementType, value, formatter); - return; + else + { + TypeDesc elementType = ((ArrayType)declaredType).ElementType; + WriteVectorElements(literal.Vector(), elementType, value, formatter); + } + } + else + { + // Scalar value (primitive, enum, string, Type) + WriteScalarValue(literal.Scalar(), declaredType, value, formatter); } - - // Scalar value (primitive, enum, string, Type) - WriteScalarValue(literal.Scalar(), declaredType, value, formatter); } private void WriteVectorElements(VectorEncoder vector, TypeDesc elementType, object value, CustomAttributeTypeNameFormatter formatter) @@ -428,13 +413,9 @@ private void WriteVectorElements(VectorEncoder vector, TypeDesc elementType, obj private static void WriteScalarValue(ScalarEncoder scalar, TypeDesc type, object value, CustomAttributeTypeNameFormatter formatter) { if (IsSystemType(type)) - { scalar.SystemType(value is TypeDesc typeofType ? formatter.FormatName(typeofType, true) : null); - return; - } - - // Primitives, enums (underlying value), strings, null - scalar.Constant(value); + else + scalar.Constant(value); } private static bool IsSystemType(TypeDesc type) From 48062a124ff9f1f730bdd4383b84db19b06aa5f2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 01:04:55 +0000 Subject: [PATCH 06/26] Remove Constructor.IsNil check; rewrite dependency methods to use DependencyList with .Add - Remove unnecessary `customAttribute.Constructor.IsNil` guard since custom attributes always have a constructor. - Rewrite GetStaticDependencies to return a DependencyList populated via .Add. - Convert all helper methods (GetDependenciesFromCustomAttributeBlob, GetDependenciesFromCustomAttributeArgument, GetDependenciesFromPropertySetter, GetDependenciesFromField) to accept a DependencyList parameter and .Add directly, eliminating yield return and IEnumerable overhead. Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/f59c4055-f47c-4e90-9a3e-b04855a33de1 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 102 +++++++----------- 1 file changed, 40 insertions(+), 62 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index a63fef18453196..0e7f91ad17d318 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -61,12 +61,13 @@ public static bool IsCustomAttributeForSecurity(EcmaModule module, CustomAttribu public override IEnumerable GetStaticDependencies(NodeFactory factory) { + DependencyList dependencies = new DependencyList(); + CustomAttribute customAttribute = _module.MetadataReader.GetCustomAttribute(Handle); // We decided not to report parent as a dependency because we don't expect custom attributes to be needed outside of their parent references - if (!customAttribute.Constructor.IsNil) - yield return new DependencyListEntry(factory.GetNodeForMethodToken(_module, customAttribute.Constructor), "Custom attribute constructor"); + dependencies.Add(factory.GetNodeForMethodToken(_module, customAttribute.Constructor), "Custom attribute constructor"); // Parse the custom attribute value blob and add dependencies from it CustomAttributeValue decodedValue; @@ -78,24 +79,24 @@ public override IEnumerable GetStaticDependencies(NodeFacto { // Attribute ctor doesn't resolve, typeof() refers to something that can't be loaded, // attribute refers to a non-existing field, etc. - yield break; + return dependencies; } catch (BadImageFormatException) { // System.Reflection.Metadata throws BadImageFormatException if the blob is malformed. - yield break; + return dependencies; } - foreach (var entry in GetDependenciesFromCustomAttributeBlob(factory, decodedValue)) - yield return entry; + GetDependenciesFromCustomAttributeBlob(dependencies, factory, decodedValue); + + return dependencies; } - private IEnumerable GetDependenciesFromCustomAttributeBlob(NodeFactory factory, CustomAttributeValue value) + private void GetDependenciesFromCustomAttributeBlob(DependencyList dependencies, NodeFactory factory, CustomAttributeValue value) { foreach (CustomAttributeTypedArgument fixedArg in value.FixedArguments) { - foreach (var entry in GetDependenciesFromCustomAttributeArgument(factory, fixedArg.Type, fixedArg.Value)) - yield return entry; + GetDependenciesFromCustomAttributeArgument(dependencies, factory, fixedArg.Type, fixedArg.Value); } // Resolve the constructor once for all named arguments @@ -107,66 +108,51 @@ private IEnumerable GetDependenciesFromCustomAttributeBlob( if (constructor is not null) { if (namedArg.Kind == CustomAttributeNamedArgumentKind.Property) - { - foreach (var entry in GetDependenciesFromPropertySetter(factory, constructor.OwningType, namedArg.Name)) - yield return entry; - } + GetDependenciesFromPropertySetter(dependencies, factory, constructor.OwningType, namedArg.Name); else if (namedArg.Kind == CustomAttributeNamedArgumentKind.Field) - { - foreach (var entry in GetDependenciesFromField(factory, constructor.OwningType, namedArg.Name)) - yield return entry; - } + GetDependenciesFromField(dependencies, factory, constructor.OwningType, namedArg.Name); } - foreach (var entry in GetDependenciesFromCustomAttributeArgument(factory, namedArg.Type, namedArg.Value)) - yield return entry; + GetDependenciesFromCustomAttributeArgument(dependencies, factory, namedArg.Type, namedArg.Value); } } - private IEnumerable GetDependenciesFromCustomAttributeArgument(NodeFactory factory, TypeDesc type, object value) + private static void GetDependenciesFromCustomAttributeArgument(DependencyList dependencies, NodeFactory factory, TypeDesc type, object value) { if (type is null) - yield break; + return; // Report the type itself (e.g. enum types that need to be kept for boxing) - object reflectedType = factory.ReflectedType(type); - if (reflectedType is DependencyNode typeNode) - yield return new DependencyListEntry(typeNode, "Custom attribute blob"); + if (factory.ReflectedType(type) is DependencyNode typeNode) + dependencies.Add(typeNode, "Custom attribute blob"); if (type.UnderlyingType.IsPrimitive || type.IsString || value is null) - yield break; + return; if (type.IsSzArray) { TypeDesc elementType = ((ArrayType)type).ElementType; - if (elementType.UnderlyingType.IsPrimitive || elementType.IsString) - yield break; - - if (value is ImmutableArray> arrayElements) + if (!elementType.UnderlyingType.IsPrimitive && !elementType.IsString + && value is ImmutableArray> arrayElements) { foreach (CustomAttributeTypedArgument element in arrayElements) { - foreach (var entry in GetDependenciesFromCustomAttributeArgument(factory, element.Type, element.Value)) - yield return entry; + GetDependenciesFromCustomAttributeArgument(dependencies, factory, element.Type, element.Value); } } - - yield break; } - - // typeof() - the value is a TypeDesc - if (value is TypeDesc typeofType) + else if (value is TypeDesc typeofType) { - object reflectedTypeofType = factory.ReflectedType(typeofType); - if (reflectedTypeofType is DependencyNode typeofNode) - yield return new DependencyListEntry(typeofNode, "Custom attribute blob"); + // typeof() - the value is a TypeDesc + if (factory.ReflectedType(typeofType) is DependencyNode typeofNode) + dependencies.Add(typeofNode, "Custom attribute blob"); } } - private static IEnumerable GetDependenciesFromPropertySetter(NodeFactory factory, TypeDesc attributeType, string propertyName) + private static void GetDependenciesFromPropertySetter(DependencyList dependencies, NodeFactory factory, TypeDesc attributeType, string propertyName) { if (attributeType is not EcmaType ecmaType) - yield break; + return; MetadataReader reader = ecmaType.MetadataReader; TypeDefinition typeDef = reader.GetTypeDefinition(ecmaType.Handle); @@ -177,44 +163,36 @@ private static IEnumerable GetDependenciesFromPropertySette if (reader.StringComparer.Equals(propDef.Name, propertyName)) { PropertyAccessors accessors = propDef.GetAccessors(); - if (!accessors.Setter.IsNil) + if (!accessors.Setter.IsNil + && factory.ReflectedMethod(ecmaType.Module.GetMethod(accessors.Setter)) is DependencyNode methodNode) { - object reflectedMethod = factory.ReflectedMethod(ecmaType.Module.GetMethod(accessors.Setter)); - if (reflectedMethod is DependencyNode methodNode) - yield return new DependencyListEntry(methodNode, "Custom attribute blob"); + dependencies.Add(methodNode, "Custom attribute blob"); } - yield break; + return; } } // Check base type TypeDesc baseType = attributeType.BaseType; if (baseType is not null) - { - foreach (var entry in GetDependenciesFromPropertySetter(factory, baseType, propertyName)) - yield return entry; - } + GetDependenciesFromPropertySetter(dependencies, factory, baseType, propertyName); } - private static IEnumerable GetDependenciesFromField(NodeFactory factory, TypeDesc attributeType, string fieldName) + private static void GetDependenciesFromField(DependencyList dependencies, NodeFactory factory, TypeDesc attributeType, string fieldName) { FieldDesc field = attributeType.GetField(Encoding.UTF8.GetBytes(fieldName)); if (field is not null) { - object reflectedField = factory.ReflectedField(field); - if (reflectedField is DependencyNode fieldNode) - yield return new DependencyListEntry(fieldNode, "Custom attribute blob"); - - yield break; + if (factory.ReflectedField(field) is DependencyNode fieldNode) + dependencies.Add(fieldNode, "Custom attribute blob"); } - - // Check base type - TypeDesc baseType = attributeType.BaseType; - if (baseType is not null) + else { - foreach (var entry in GetDependenciesFromField(factory, baseType, fieldName)) - yield return entry; + // Check base type + TypeDesc baseType = attributeType.BaseType; + if (baseType is not null) + GetDependenciesFromField(dependencies, factory, baseType, fieldName); } } From a102e2081e41807a0af6a30644c064dc5f8751c5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 02:46:08 +0000 Subject: [PATCH 07/26] Collapse catch blocks, inline methods, use TryGetMethod - Collapse separate TypeSystemException/BadImageFormatException catch blocks into single `catch (Exception ex) when (ex is ... or ...)` in both GetStaticDependencies and RewriteCustomAttributeBlob. - Inline GetDependenciesFromCustomAttributeBlob into GetStaticDependencies. - Inline EncodeCustomAttributeBlob into RewriteCustomAttributeBlob. - Use _module.TryGetMethod (GetObject with NotFoundBehavior.ReturnNull) instead of try/catch for constructor resolution. Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/93abc623-385e-4d42-a955-f61dd8eab979 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 48 ++++--------------- 1 file changed, 10 insertions(+), 38 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 0e7f91ad17d318..04977c452e2cb8 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -75,35 +75,22 @@ public override IEnumerable GetStaticDependencies(NodeFacto { decodedValue = customAttribute.DecodeValue(new CustomAttributeTypeProvider(_module)); } - catch (TypeSystemException) + catch (Exception ex) when (ex is TypeSystemException or BadImageFormatException) { // Attribute ctor doesn't resolve, typeof() refers to something that can't be loaded, - // attribute refers to a non-existing field, etc. - return dependencies; - } - catch (BadImageFormatException) - { - // System.Reflection.Metadata throws BadImageFormatException if the blob is malformed. + // attribute refers to a non-existing field, malformed blob, etc. return dependencies; } - GetDependenciesFromCustomAttributeBlob(dependencies, factory, decodedValue); - - return dependencies; - } - - private void GetDependenciesFromCustomAttributeBlob(DependencyList dependencies, NodeFactory factory, CustomAttributeValue value) - { - foreach (CustomAttributeTypedArgument fixedArg in value.FixedArguments) + foreach (CustomAttributeTypedArgument fixedArg in decodedValue.FixedArguments) { GetDependenciesFromCustomAttributeArgument(dependencies, factory, fixedArg.Type, fixedArg.Value); } // Resolve the constructor once for all named arguments - MethodDesc constructor = _module.GetMethod( - _module.MetadataReader.GetCustomAttribute(Handle).Constructor); + MethodDesc constructor = _module.TryGetMethod(customAttribute.Constructor); - foreach (CustomAttributeNamedArgument namedArg in value.NamedArguments) + foreach (CustomAttributeNamedArgument namedArg in decodedValue.NamedArguments) { if (constructor is not null) { @@ -115,6 +102,8 @@ private void GetDependenciesFromCustomAttributeBlob(DependencyList dependencies, GetDependenciesFromCustomAttributeArgument(dependencies, factory, namedArg.Type, namedArg.Value); } + + return dependencies; } private static void GetDependenciesFromCustomAttributeArgument(DependencyList dependencies, NodeFactory factory, TypeDesc type, object value) @@ -221,31 +210,14 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui { decodedValue = customAttribute.DecodeValue(new CustomAttributeTypeProvider(_module)); } - catch (TypeSystemException) + catch (Exception ex) when (ex is TypeSystemException or BadImageFormatException) { blobBuilder.WriteBytes(_module.MetadataReader.GetBlobBytes(customAttribute.Value)); return; } - catch (BadImageFormatException) - { - blobBuilder.WriteBytes(_module.MetadataReader.GetBlobBytes(customAttribute.Value)); - return; - } - - EncodeCustomAttributeBlob(customAttribute, decodedValue, blobBuilder); - } - private void EncodeCustomAttributeBlob( - CustomAttribute customAttribute, - CustomAttributeValue decodedValue, - BlobBuilder blobBuilder) - { - MethodDesc constructor; - try - { - constructor = _module.GetMethod(customAttribute.Constructor); - } - catch (TypeSystemException) + MethodDesc constructor = _module.TryGetMethod(customAttribute.Constructor); + if (constructor is null) { blobBuilder.WriteBytes(_module.MetadataReader.GetBlobBytes(customAttribute.Value)); return; From 45bd9c30e4e1f8576d884d6ff9e7a0ea7f695ae8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 01:07:55 +0000 Subject: [PATCH 08/26] Early out for null constructor and handle generic attributes in GetDependenciesFromPropertySetter Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/b66bedfc-56a2-4b01-a4c0-a16efba5f76e Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 04977c452e2cb8..abcd3189c6f9b6 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -89,16 +89,15 @@ public override IEnumerable GetStaticDependencies(NodeFacto // Resolve the constructor once for all named arguments MethodDesc constructor = _module.TryGetMethod(customAttribute.Constructor); + if (constructor is null) + return dependencies; foreach (CustomAttributeNamedArgument namedArg in decodedValue.NamedArguments) { - if (constructor is not null) - { - if (namedArg.Kind == CustomAttributeNamedArgumentKind.Property) - GetDependenciesFromPropertySetter(dependencies, factory, constructor.OwningType, namedArg.Name); - else if (namedArg.Kind == CustomAttributeNamedArgumentKind.Field) - GetDependenciesFromField(dependencies, factory, constructor.OwningType, namedArg.Name); - } + if (namedArg.Kind == CustomAttributeNamedArgumentKind.Property) + GetDependenciesFromPropertySetter(dependencies, factory, constructor.OwningType, namedArg.Name); + else if (namedArg.Kind == CustomAttributeNamedArgumentKind.Field) + GetDependenciesFromField(dependencies, factory, constructor.OwningType, namedArg.Name); GetDependenciesFromCustomAttributeArgument(dependencies, factory, namedArg.Type, namedArg.Value); } @@ -140,7 +139,7 @@ private static void GetDependenciesFromCustomAttributeArgument(DependencyList de private static void GetDependenciesFromPropertySetter(DependencyList dependencies, NodeFactory factory, TypeDesc attributeType, string propertyName) { - if (attributeType is not EcmaType ecmaType) + if (attributeType.GetTypeDefinition() is not EcmaType ecmaType) return; MetadataReader reader = ecmaType.MetadataReader; From 3982922f4f13e51c842c1e307b1498d6fb72442c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 04:46:48 +0000 Subject: [PATCH 09/26] Remove GenericAttributes from ILTrim expected failures Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/3d6120c4-6eaa-4457-a924-2fccb738ebbe Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt b/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt index 19ece9bcf720c6..9a9789eedf0c37 100644 --- a/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt +++ b/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt @@ -23,7 +23,6 @@ Attributes.Debugger.KeepDebugMembers.DebuggerDisplayAttributeOnGenerics Attributes.Debugger.KeepDebugMembers.DebuggerDisplayAttributeOnType Attributes.Debugger.KeepDebugMembers.DebuggerTypeProxyAttributeOnAssemblyUsingTarget Attributes.Debugger.KeepDebugMembers.DebuggerTypeProxyAttributeOnType -Attributes.GenericAttributes Attributes.IVTUsed Attributes.MarshalAsCustomMarshaler Attributes.MarshalAsCustomMarshalerInterface From bf4544eda45de0e4ceb383d046e575f26814f5f8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 06:32:23 +0000 Subject: [PATCH 10/26] Rewrite custom attribute blob handling to manual BlobReader copy/rewrite Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/53a664e0-dd9f-4c69-9390-18a15c55ef0f Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 321 ++++++++++++------ 1 file changed, 210 insertions(+), 111 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index abcd3189c6f9b6..b55569136387d8 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -204,167 +204,266 @@ protected override EntityHandle WriteInternal(ModuleWritingContext writeContext) private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBuilder blobBuilder) { - CustomAttributeValue decodedValue; + byte[] originalBlob = _module.MetadataReader.GetBlobBytes(customAttribute.Value); + MethodDesc constructor = _module.TryGetMethod(customAttribute.Constructor); + if (constructor is null) + { + blobBuilder.WriteBytes(originalBlob); + return; + } + try { - decodedValue = customAttribute.DecodeValue(new CustomAttributeTypeProvider(_module)); + BlobReader valueReader = _module.MetadataReader.GetBlobReader(customAttribute.Value); + var formatter = new CustomAttributeTypeNameFormatter(); + var typeProvider = new CustomAttributeTypeProvider(_module); + + ushort prolog = valueReader.ReadUInt16(); + if (prolog != 1) + throw new BadImageFormatException(); + + blobBuilder.WriteUInt16(prolog); + + MethodSignature constructorSig = constructor.Signature; + for (int i = 0; i < constructorSig.Length; i++) + { + CopyFixedArgument(ref valueReader, blobBuilder, originalBlob, constructorSig[i], typeProvider, formatter); + } + + ushort namedArgumentCount = valueReader.ReadUInt16(); + blobBuilder.WriteUInt16(namedArgumentCount); + + for (int i = 0; i < namedArgumentCount; i++) + { + CustomAttributeNamedArgumentKind kind = (CustomAttributeNamedArgumentKind)valueReader.ReadByte(); + if (kind != CustomAttributeNamedArgumentKind.Field && kind != CustomAttributeNamedArgumentKind.Property) + throw new BadImageFormatException(); + + blobBuilder.WriteByte((byte)kind); + + CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); + CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, typeProvider, formatter); + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, valueTypeCode, elementValueTypeCode); + } + + if (valueReader.RemainingBytes != 0) + throw new BadImageFormatException(); } catch (Exception ex) when (ex is TypeSystemException or BadImageFormatException) { - blobBuilder.WriteBytes(_module.MetadataReader.GetBlobBytes(customAttribute.Value)); - return; + blobBuilder.Clear(); + blobBuilder.WriteBytes(originalBlob); } + } - MethodDesc constructor = _module.TryGetMethod(customAttribute.Constructor); - if (constructor is null) - { - blobBuilder.WriteBytes(_module.MetadataReader.GetBlobBytes(customAttribute.Value)); - return; - } + private static void CopyRawBytes(ref BlobReader reader, BlobBuilder blobBuilder, byte[] originalBlob, int byteCount) + { + int startOffset = reader.Offset; + reader.Offset = startOffset + byteCount; + blobBuilder.WriteBytes(originalBlob, startOffset, byteCount); + } - var formatter = new CustomAttributeTypeNameFormatter(); - var encoder = new BlobEncoder(blobBuilder); - encoder.CustomAttributeSignature(out FixedArgumentsEncoder fixedArgs, out CustomAttributeNamedArgumentsEncoder namedArgs); + private static void CopySerializedString(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, bool rewriteTypeName, CustomAttributeTypeProvider typeProvider, CustomAttributeTypeNameFormatter formatter) + { + int startOffset = valueReader.Offset; + string? original = valueReader.ReadSerializedString(); + int endOffset = valueReader.Offset; - // Write fixed arguments - MethodSignature constructorSig = constructor.Signature; - for (int i = 0; i < decodedValue.FixedArguments.Length; i++) + if (rewriteTypeName && original is not null) { - TypeDesc paramType = constructorSig[i]; - WriteLiteralValue(fixedArgs.AddArgument(), paramType, decodedValue.FixedArguments[i].Type, decodedValue.FixedArguments[i].Value, formatter); + TypeDesc resolved = typeProvider.GetTypeFromSerializedName(original); + if (resolved is not null) + { + string rewritten = formatter.FormatName(resolved, true); + if (rewritten != original) + { + blobBuilder.WriteSerializedString(rewritten); + return; + } + } } - // Write named arguments - NamedArgumentsEncoder namedArgsEncoder = namedArgs.Count(decodedValue.NamedArguments.Length); - foreach (CustomAttributeNamedArgument namedArg in decodedValue.NamedArguments) - { - namedArgsEncoder.AddArgument( - namedArg.Kind == CustomAttributeNamedArgumentKind.Field, - out NamedArgumentTypeEncoder type, - out NameEncoder name, - out LiteralEncoder literal); + blobBuilder.WriteBytes(originalBlob, startOffset, endOffset - startOffset); + } - EncodeNamedArgumentType(type, namedArg.Type, formatter); - name.Name(namedArg.Name); - WriteLiteralValue(literal, namedArg.Type, namedArg.Type, namedArg.Value, formatter); - } + private static void CopyFixedArgument(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, TypeDesc type, CustomAttributeTypeProvider typeProvider, CustomAttributeTypeNameFormatter formatter) + { + GetFixedArgumentTypeCodes(type, typeProvider, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, valueTypeCode, elementValueTypeCode); } - private static void EncodeNamedArgumentType(NamedArgumentTypeEncoder type, TypeDesc argType, CustomAttributeTypeNameFormatter formatter) + private static void GetFixedArgumentTypeCodes(TypeDesc type, CustomAttributeTypeProvider typeProvider, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) { - if (argType.IsObject) + elementValueTypeCode = default; + + if (type.UnderlyingType.IsPrimitive) { - type.Object(); + valueTypeCode = GetPrimitiveSerializationTypeCode(type); } - else if (argType.IsSzArray) + else if (type.IsString) { - TypeDesc elementType = ((ArrayType)argType).ElementType; - if (elementType.IsObject) - type.SZArray().ObjectArray(); - else - EncodeElementType(type.SZArray().ElementType(), elementType, formatter); + valueTypeCode = SerializationTypeCode.String; + } + else if (IsSystemType(type)) + { + valueTypeCode = SerializationTypeCode.Type; + } + else if (type.IsObject) + { + valueTypeCode = SerializationTypeCode.TaggedObject; + } + else if (type.IsSzArray) + { + valueTypeCode = SerializationTypeCode.SZArray; + GetFixedArgumentTypeCodes(((ArrayType)type).ElementType, typeProvider, out elementValueTypeCode, out _); + } + else if (type.IsEnum) + { + valueTypeCode = (SerializationTypeCode)typeProvider.GetUnderlyingEnumType(type); } else { - EncodeElementType(type.ScalarType(), argType, formatter); + throw new BadImageFormatException(); } } - private static void EncodeElementType(CustomAttributeElementTypeEncoder encoder, TypeDesc type, CustomAttributeTypeNameFormatter formatter) + private static SerializationTypeCode GetPrimitiveSerializationTypeCode(TypeDesc type) { switch (type.Category) { - case TypeFlags.Boolean: encoder.Boolean(); break; - case TypeFlags.Char: encoder.Char(); break; - case TypeFlags.SByte: encoder.SByte(); break; - case TypeFlags.Byte: encoder.Byte(); break; - case TypeFlags.Int16: encoder.Int16(); break; - case TypeFlags.UInt16: encoder.UInt16(); break; - case TypeFlags.Int32: encoder.Int32(); break; - case TypeFlags.UInt32: encoder.UInt32(); break; - case TypeFlags.Int64: encoder.Int64(); break; - case TypeFlags.UInt64: encoder.UInt64(); break; - case TypeFlags.Single: encoder.Single(); break; - case TypeFlags.Double: encoder.Double(); break; - default: - if (type.IsEnum) - encoder.Enum(formatter.FormatName(type, true)); - else if (IsSystemType(type)) - encoder.SystemType(); - else if (type.IsString) - encoder.String(); - break; + case TypeFlags.Boolean: return SerializationTypeCode.Boolean; + case TypeFlags.Char: return SerializationTypeCode.Char; + case TypeFlags.SByte: return SerializationTypeCode.SByte; + case TypeFlags.Byte: return SerializationTypeCode.Byte; + case TypeFlags.Int16: return SerializationTypeCode.Int16; + case TypeFlags.UInt16: return SerializationTypeCode.UInt16; + case TypeFlags.Int32: return SerializationTypeCode.Int32; + case TypeFlags.UInt32: return SerializationTypeCode.UInt32; + case TypeFlags.Int64: return SerializationTypeCode.Int64; + case TypeFlags.UInt64: return SerializationTypeCode.UInt64; + case TypeFlags.Single: return SerializationTypeCode.Single; + case TypeFlags.Double: return SerializationTypeCode.Double; + default: throw new BadImageFormatException(); } } - private void WriteLiteralValue(LiteralEncoder literal, TypeDesc declaredType, TypeDesc actualType, object value, CustomAttributeTypeNameFormatter formatter) + private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, CustomAttributeTypeProvider typeProvider, CustomAttributeTypeNameFormatter formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode, bool isElementType = false) { - if (declaredType is null) + elementValueTypeCode = default; + + SerializationTypeCode typeCode = valueReader.ReadSerializationTypeCode(); + blobBuilder.WriteByte((byte)typeCode); + + if (typeCode == SerializationTypeCode.Boolean + || typeCode == SerializationTypeCode.Char + || typeCode == SerializationTypeCode.SByte + || typeCode == SerializationTypeCode.Byte + || typeCode == SerializationTypeCode.Int16 + || typeCode == SerializationTypeCode.UInt16 + || typeCode == SerializationTypeCode.Int32 + || typeCode == SerializationTypeCode.UInt32 + || typeCode == SerializationTypeCode.Int64 + || typeCode == SerializationTypeCode.UInt64 + || typeCode == SerializationTypeCode.Single + || typeCode == SerializationTypeCode.Double + || typeCode == SerializationTypeCode.String + || typeCode == SerializationTypeCode.Type + || typeCode == SerializationTypeCode.TaggedObject) { - // Nothing to write + valueTypeCode = typeCode; } - else if (declaredType.IsObject) + else if (typeCode == SerializationTypeCode.SZArray) { - if (value is null) - { - literal.TaggedScalar(out CustomAttributeElementTypeEncoder type, out ScalarEncoder scalar); - type.String(); - scalar.Constant(null); - } - else if (actualType is not null && actualType.IsSzArray) - { - literal.TaggedVector(out CustomAttributeArrayTypeEncoder arrayType, out VectorEncoder vector); - TypeDesc elementType = ((ArrayType)actualType).ElementType; - if (elementType.IsObject) - arrayType.ObjectArray(); - else - EncodeElementType(arrayType.ElementType(), elementType, formatter); - WriteVectorElements(vector, elementType, value, formatter); - } - else - { - literal.TaggedScalar(out CustomAttributeElementTypeEncoder typeEncoder, out ScalarEncoder scalarEncoder); - EncodeElementType(typeEncoder, actualType, formatter); - WriteScalarValue(scalarEncoder, actualType, value, formatter); - } + if (isElementType) + throw new BadImageFormatException(); + + CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, out elementValueTypeCode, out _, isElementType: true); + valueTypeCode = SerializationTypeCode.SZArray; } - else if (declaredType.IsSzArray) + else if (typeCode == SerializationTypeCode.Enum) { - if (value is null) - { - literal.Scalar().NullArray(); - } + int startOffset = valueReader.Offset; + string? enumTypeName = valueReader.ReadSerializedString(); + int endOffset = valueReader.Offset; + if (enumTypeName is null) + throw new BadImageFormatException(); + + TypeDesc enumType = typeProvider.GetTypeFromSerializedName(enumTypeName); + if (enumType is null) + throw new BadImageFormatException(); + + string rewritten = formatter.FormatName(enumType, true); + if (rewritten == enumTypeName) + blobBuilder.WriteBytes(originalBlob, startOffset, endOffset - startOffset); else - { - TypeDesc elementType = ((ArrayType)declaredType).ElementType; - WriteVectorElements(literal.Vector(), elementType, value, formatter); - } + blobBuilder.WriteSerializedString(rewritten); + + valueTypeCode = (SerializationTypeCode)typeProvider.GetUnderlyingEnumType(enumType); } else { - // Scalar value (primitive, enum, string, Type) - WriteScalarValue(literal.Scalar(), declaredType, value, formatter); + throw new BadImageFormatException(); } } - private void WriteVectorElements(VectorEncoder vector, TypeDesc elementType, object value, CustomAttributeTypeNameFormatter formatter) + private static void CopySerializedValue(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, CustomAttributeTypeProvider typeProvider, CustomAttributeTypeNameFormatter formatter, SerializationTypeCode valueTypeCode, SerializationTypeCode elementValueTypeCode) { - if (value is ImmutableArray> arrayElements) + if (valueTypeCode == SerializationTypeCode.Boolean + || valueTypeCode == SerializationTypeCode.Byte + || valueTypeCode == SerializationTypeCode.SByte) + { + CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 1); + } + else if (valueTypeCode == SerializationTypeCode.Char + || valueTypeCode == SerializationTypeCode.Int16 + || valueTypeCode == SerializationTypeCode.UInt16) + { + CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 2); + } + else if (valueTypeCode == SerializationTypeCode.Int32 + || valueTypeCode == SerializationTypeCode.UInt32 + || valueTypeCode == SerializationTypeCode.Single) + { + CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 4); + } + else if (valueTypeCode == SerializationTypeCode.Int64 + || valueTypeCode == SerializationTypeCode.UInt64 + || valueTypeCode == SerializationTypeCode.Double) + { + CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 8); + } + else if (valueTypeCode == SerializationTypeCode.String) + { + CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, typeProvider, formatter); + } + else if (valueTypeCode == SerializationTypeCode.Type) + { + CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: true, typeProvider, formatter); + } + else if (valueTypeCode == SerializationTypeCode.SZArray) { - LiteralsEncoder literals = vector.Count(arrayElements.Length); - foreach (CustomAttributeTypedArgument element in arrayElements) + int elementCount = valueReader.ReadInt32(); + blobBuilder.WriteInt32(elementCount); + if (elementCount < -1) + throw new BadImageFormatException(); + + if (elementCount > 0) { - WriteLiteralValue(literals.AddLiteral(), elementType, element.Type, element.Value, formatter); + for (int i = 0; i < elementCount; i++) + { + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, elementValueTypeCode, default); + } } } - } - - private static void WriteScalarValue(ScalarEncoder scalar, TypeDesc type, object value, CustomAttributeTypeNameFormatter formatter) - { - if (IsSystemType(type)) - scalar.SystemType(value is TypeDesc typeofType ? formatter.FormatName(typeofType, true) : null); + else if (valueTypeCode == SerializationTypeCode.TaggedObject) + { + CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, out SerializationTypeCode boxedTypeCode, out SerializationTypeCode boxedElementTypeCode); + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, boxedTypeCode, boxedElementTypeCode); + } else - scalar.Constant(value); + { + throw new BadImageFormatException(); + } } private static bool IsSystemType(TypeDesc type) From e06fb7f2a7f573228eef3974b568114deca9d3e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 08:00:18 +0000 Subject: [PATCH 11/26] Track corrupted custom attributes and short-circuit blob rewrite path Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/e7baf5db-dc38-4019-b7c6-c66d7a5cbb73 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 57 ++++++++----------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index b55569136387d8..334c3faa5588d4 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -20,6 +20,8 @@ namespace ILCompiler.DependencyAnalysis /// public sealed class CustomAttributeNode : TokenBasedNode { + private bool _isCorrupted; + public CustomAttributeNode(EcmaModule module, CustomAttributeHandle handle) : base(module, handle) { @@ -79,6 +81,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto { // Attribute ctor doesn't resolve, typeof() refers to something that can't be loaded, // attribute refers to a non-existing field, malformed blob, etc. + _isCorrupted = true; return dependencies; } @@ -205,6 +208,12 @@ protected override EntityHandle WriteInternal(ModuleWritingContext writeContext) private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBuilder blobBuilder) { byte[] originalBlob = _module.MetadataReader.GetBlobBytes(customAttribute.Value); + if (_isCorrupted) + { + blobBuilder.WriteBytes(originalBlob); + return; + } + MethodDesc constructor = _module.TryGetMethod(customAttribute.Constructor); if (constructor is null) { @@ -218,11 +227,7 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui var formatter = new CustomAttributeTypeNameFormatter(); var typeProvider = new CustomAttributeTypeProvider(_module); - ushort prolog = valueReader.ReadUInt16(); - if (prolog != 1) - throw new BadImageFormatException(); - - blobBuilder.WriteUInt16(prolog); + blobBuilder.WriteUInt16(valueReader.ReadUInt16()); MethodSignature constructorSig = constructor.Signature; for (int i = 0; i < constructorSig.Length; i++) @@ -236,18 +241,12 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui for (int i = 0; i < namedArgumentCount; i++) { CustomAttributeNamedArgumentKind kind = (CustomAttributeNamedArgumentKind)valueReader.ReadByte(); - if (kind != CustomAttributeNamedArgumentKind.Field && kind != CustomAttributeNamedArgumentKind.Property) - throw new BadImageFormatException(); - blobBuilder.WriteByte((byte)kind); CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, typeProvider, formatter); CopySerializedValue(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, valueTypeCode, elementValueTypeCode); } - - if (valueReader.RemainingBytes != 0) - throw new BadImageFormatException(); } catch (Exception ex) when (ex is TypeSystemException or BadImageFormatException) { @@ -323,7 +322,7 @@ private static void GetFixedArgumentTypeCodes(TypeDesc type, CustomAttributeType } else { - throw new BadImageFormatException(); + valueTypeCode = SerializationTypeCode.Invalid; } } @@ -343,7 +342,7 @@ private static SerializationTypeCode GetPrimitiveSerializationTypeCode(TypeDesc case TypeFlags.UInt64: return SerializationTypeCode.UInt64; case TypeFlags.Single: return SerializationTypeCode.Single; case TypeFlags.Double: return SerializationTypeCode.Double; - default: throw new BadImageFormatException(); + default: return SerializationTypeCode.Invalid; } } @@ -374,23 +373,22 @@ private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilde } else if (typeCode == SerializationTypeCode.SZArray) { - if (isElementType) - throw new BadImageFormatException(); - - CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, out elementValueTypeCode, out _, isElementType: true); - valueTypeCode = SerializationTypeCode.SZArray; + if (!isElementType) + { + CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, out elementValueTypeCode, out _, isElementType: true); + valueTypeCode = SerializationTypeCode.SZArray; + } + else + { + valueTypeCode = SerializationTypeCode.Invalid; + } } else if (typeCode == SerializationTypeCode.Enum) { int startOffset = valueReader.Offset; - string? enumTypeName = valueReader.ReadSerializedString(); + string enumTypeName = valueReader.ReadSerializedString()!; int endOffset = valueReader.Offset; - if (enumTypeName is null) - throw new BadImageFormatException(); - - TypeDesc enumType = typeProvider.GetTypeFromSerializedName(enumTypeName); - if (enumType is null) - throw new BadImageFormatException(); + TypeDesc enumType = typeProvider.GetTypeFromSerializedName(enumTypeName)!; string rewritten = formatter.FormatName(enumType, true); if (rewritten == enumTypeName) @@ -402,7 +400,7 @@ private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilde } else { - throw new BadImageFormatException(); + valueTypeCode = SerializationTypeCode.Invalid; } } @@ -444,9 +442,6 @@ private static void CopySerializedValue(ref BlobReader valueReader, BlobBuilder { int elementCount = valueReader.ReadInt32(); blobBuilder.WriteInt32(elementCount); - if (elementCount < -1) - throw new BadImageFormatException(); - if (elementCount > 0) { for (int i = 0; i < elementCount; i++) @@ -460,10 +455,6 @@ private static void CopySerializedValue(ref BlobReader valueReader, BlobBuilder CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, out SerializationTypeCode boxedTypeCode, out SerializationTypeCode boxedElementTypeCode); CopySerializedValue(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, boxedTypeCode, boxedElementTypeCode); } - else - { - throw new BadImageFormatException(); - } } private static bool IsSystemType(TypeDesc type) From 3788c36a2612642a6afba1b05a70e093bfca9667 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 08:02:00 +0000 Subject: [PATCH 12/26] Harden enum type-name rewrite null handling in custom attribute blob parser Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/e7baf5db-dc38-4019-b7c6-c66d7a5cbb73 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 334c3faa5588d4..0c06eb08458481 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -227,6 +227,7 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui var formatter = new CustomAttributeTypeNameFormatter(); var typeProvider = new CustomAttributeTypeProvider(_module); + // The dependency walk already decoded the blob successfully unless `_isCorrupted` is set. blobBuilder.WriteUInt16(valueReader.ReadUInt16()); MethodSignature constructorSig = constructor.Signature; @@ -386,17 +387,25 @@ private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilde else if (typeCode == SerializationTypeCode.Enum) { int startOffset = valueReader.Offset; - string enumTypeName = valueReader.ReadSerializedString()!; + string? enumTypeName = valueReader.ReadSerializedString(); int endOffset = valueReader.Offset; - TypeDesc enumType = typeProvider.GetTypeFromSerializedName(enumTypeName)!; + TypeDesc enumType = enumTypeName is not null ? typeProvider.GetTypeFromSerializedName(enumTypeName) : null; - string rewritten = formatter.FormatName(enumType, true); - if (rewritten == enumTypeName) - blobBuilder.WriteBytes(originalBlob, startOffset, endOffset - startOffset); - else - blobBuilder.WriteSerializedString(rewritten); + if (enumTypeName is not null && enumType is not null) + { + string rewritten = formatter.FormatName(enumType, true); + if (rewritten == enumTypeName) + blobBuilder.WriteBytes(originalBlob, startOffset, endOffset - startOffset); + else + blobBuilder.WriteSerializedString(rewritten); - valueTypeCode = (SerializationTypeCode)typeProvider.GetUnderlyingEnumType(enumType); + valueTypeCode = (SerializationTypeCode)typeProvider.GetUnderlyingEnumType(enumType); + } + else + { + blobBuilder.WriteBytes(originalBlob, startOffset, endOffset - startOffset); + valueTypeCode = SerializationTypeCode.Invalid; + } } else { From 4fa658695fb7dce210ed4f80fa4990f5cda88582 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 08:03:36 +0000 Subject: [PATCH 13/26] Handle invalid sentinel explicitly in custom attribute blob value copying Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/e7baf5db-dc38-4019-b7c6-c66d7a5cbb73 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../DependencyAnalysis/TokenBased/CustomAttributeNode.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 0c06eb08458481..6a8552d41c89b3 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -20,6 +20,8 @@ namespace ILCompiler.DependencyAnalysis /// public sealed class CustomAttributeNode : TokenBasedNode { + // Set when dependency-time custom attribute decoding fails. + // Rewrite then preserves the original blob for this node. private bool _isCorrupted; public CustomAttributeNode(EcmaModule module, CustomAttributeHandle handle) @@ -391,7 +393,7 @@ private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilde int endOffset = valueReader.Offset; TypeDesc enumType = enumTypeName is not null ? typeProvider.GetTypeFromSerializedName(enumTypeName) : null; - if (enumTypeName is not null && enumType is not null) + if (enumType is not null) { string rewritten = formatter.FormatName(enumType, true); if (rewritten == enumTypeName) @@ -415,6 +417,9 @@ private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilde private static void CopySerializedValue(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, CustomAttributeTypeProvider typeProvider, CustomAttributeTypeNameFormatter formatter, SerializationTypeCode valueTypeCode, SerializationTypeCode elementValueTypeCode) { + if (valueTypeCode == SerializationTypeCode.Invalid) + throw new BadImageFormatException(); + if (valueTypeCode == SerializationTypeCode.Boolean || valueTypeCode == SerializationTypeCode.Byte || valueTypeCode == SerializationTypeCode.SByte) From 7ce4e4a2cb81be7e49bd0a44e39bea312f63f787 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 08:04:57 +0000 Subject: [PATCH 14/26] Add descriptive message for invalid custom attribute type code Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/e7baf5db-dc38-4019-b7c6-c66d7a5cbb73 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../DependencyAnalysis/TokenBased/CustomAttributeNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 6a8552d41c89b3..cefa957c5bf47a 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -418,7 +418,7 @@ private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilde private static void CopySerializedValue(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, CustomAttributeTypeProvider typeProvider, CustomAttributeTypeNameFormatter formatter, SerializationTypeCode valueTypeCode, SerializationTypeCode elementValueTypeCode) { if (valueTypeCode == SerializationTypeCode.Invalid) - throw new BadImageFormatException(); + throw new BadImageFormatException("Invalid custom attribute serialization type code."); if (valueTypeCode == SerializationTypeCode.Boolean || valueTypeCode == SerializationTypeCode.Byte From 35271089e83e8d649d23ba64fab705da38461a3a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 08:18:29 +0000 Subject: [PATCH 15/26] Switch custom attribute blob type dispatch to switch statements Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/e83d165b-d5bc-4be9-b1db-5069b96d2c27 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 154 +++++++----------- 1 file changed, 56 insertions(+), 98 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index cefa957c5bf47a..7d11d8172e3aab 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -345,56 +345,28 @@ private static SerializationTypeCode GetPrimitiveSerializationTypeCode(TypeDesc case TypeFlags.UInt64: return SerializationTypeCode.UInt64; case TypeFlags.Single: return SerializationTypeCode.Single; case TypeFlags.Double: return SerializationTypeCode.Double; - default: return SerializationTypeCode.Invalid; + default: throw new BadImageFormatException(); } } - private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, CustomAttributeTypeProvider typeProvider, CustomAttributeTypeNameFormatter formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode, bool isElementType = false) + private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, CustomAttributeTypeProvider typeProvider, CustomAttributeTypeNameFormatter formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) { elementValueTypeCode = default; SerializationTypeCode typeCode = valueReader.ReadSerializationTypeCode(); blobBuilder.WriteByte((byte)typeCode); - if (typeCode == SerializationTypeCode.Boolean - || typeCode == SerializationTypeCode.Char - || typeCode == SerializationTypeCode.SByte - || typeCode == SerializationTypeCode.Byte - || typeCode == SerializationTypeCode.Int16 - || typeCode == SerializationTypeCode.UInt16 - || typeCode == SerializationTypeCode.Int32 - || typeCode == SerializationTypeCode.UInt32 - || typeCode == SerializationTypeCode.Int64 - || typeCode == SerializationTypeCode.UInt64 - || typeCode == SerializationTypeCode.Single - || typeCode == SerializationTypeCode.Double - || typeCode == SerializationTypeCode.String - || typeCode == SerializationTypeCode.Type - || typeCode == SerializationTypeCode.TaggedObject) + switch (typeCode) { - valueTypeCode = typeCode; - } - else if (typeCode == SerializationTypeCode.SZArray) - { - if (!isElementType) - { - CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, out elementValueTypeCode, out _, isElementType: true); + case SerializationTypeCode.SZArray: + CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, out elementValueTypeCode, out _); valueTypeCode = SerializationTypeCode.SZArray; - } - else - { - valueTypeCode = SerializationTypeCode.Invalid; - } - } - else if (typeCode == SerializationTypeCode.Enum) - { - int startOffset = valueReader.Offset; - string? enumTypeName = valueReader.ReadSerializedString(); - int endOffset = valueReader.Offset; - TypeDesc enumType = enumTypeName is not null ? typeProvider.GetTypeFromSerializedName(enumTypeName) : null; - - if (enumType is not null) - { + break; + case SerializationTypeCode.Enum: + int startOffset = valueReader.Offset; + string enumTypeName = valueReader.ReadSerializedString()!; + int endOffset = valueReader.Offset; + TypeDesc enumType = typeProvider.GetTypeFromSerializedName(enumTypeName)!; string rewritten = formatter.FormatName(enumType, true); if (rewritten == enumTypeName) blobBuilder.WriteBytes(originalBlob, startOffset, endOffset - startOffset); @@ -402,72 +374,58 @@ private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilde blobBuilder.WriteSerializedString(rewritten); valueTypeCode = (SerializationTypeCode)typeProvider.GetUnderlyingEnumType(enumType); - } - else - { - blobBuilder.WriteBytes(originalBlob, startOffset, endOffset - startOffset); - valueTypeCode = SerializationTypeCode.Invalid; - } - } - else - { - valueTypeCode = SerializationTypeCode.Invalid; + break; + default: + valueTypeCode = typeCode; + break; } } private static void CopySerializedValue(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, CustomAttributeTypeProvider typeProvider, CustomAttributeTypeNameFormatter formatter, SerializationTypeCode valueTypeCode, SerializationTypeCode elementValueTypeCode) { - if (valueTypeCode == SerializationTypeCode.Invalid) - throw new BadImageFormatException("Invalid custom attribute serialization type code."); - - if (valueTypeCode == SerializationTypeCode.Boolean - || valueTypeCode == SerializationTypeCode.Byte - || valueTypeCode == SerializationTypeCode.SByte) - { - CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 1); - } - else if (valueTypeCode == SerializationTypeCode.Char - || valueTypeCode == SerializationTypeCode.Int16 - || valueTypeCode == SerializationTypeCode.UInt16) - { - CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 2); - } - else if (valueTypeCode == SerializationTypeCode.Int32 - || valueTypeCode == SerializationTypeCode.UInt32 - || valueTypeCode == SerializationTypeCode.Single) - { - CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 4); - } - else if (valueTypeCode == SerializationTypeCode.Int64 - || valueTypeCode == SerializationTypeCode.UInt64 - || valueTypeCode == SerializationTypeCode.Double) - { - CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 8); - } - else if (valueTypeCode == SerializationTypeCode.String) - { - CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, typeProvider, formatter); - } - else if (valueTypeCode == SerializationTypeCode.Type) - { - CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: true, typeProvider, formatter); - } - else if (valueTypeCode == SerializationTypeCode.SZArray) - { - int elementCount = valueReader.ReadInt32(); - blobBuilder.WriteInt32(elementCount); - if (elementCount > 0) - { - for (int i = 0; i < elementCount; i++) + switch (valueTypeCode) + { + case SerializationTypeCode.Boolean: + case SerializationTypeCode.Byte: + case SerializationTypeCode.SByte: + CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 1); + break; + case SerializationTypeCode.Char: + case SerializationTypeCode.Int16: + case SerializationTypeCode.UInt16: + CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 2); + break; + case SerializationTypeCode.Int32: + case SerializationTypeCode.UInt32: + case SerializationTypeCode.Single: + CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 4); + break; + case SerializationTypeCode.Int64: + case SerializationTypeCode.UInt64: + case SerializationTypeCode.Double: + CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 8); + break; + case SerializationTypeCode.String: + CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, typeProvider, formatter); + break; + case SerializationTypeCode.Type: + CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: true, typeProvider, formatter); + break; + case SerializationTypeCode.SZArray: + int elementCount = valueReader.ReadInt32(); + blobBuilder.WriteInt32(elementCount); + if (elementCount > 0) { - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, elementValueTypeCode, default); + for (int i = 0; i < elementCount; i++) + { + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, elementValueTypeCode, default); + } } - } - } - else if (valueTypeCode == SerializationTypeCode.TaggedObject) - { - CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, out SerializationTypeCode boxedTypeCode, out SerializationTypeCode boxedElementTypeCode); - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, boxedTypeCode, boxedElementTypeCode); + break; + case SerializationTypeCode.TaggedObject: + CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, out SerializationTypeCode boxedTypeCode, out SerializationTypeCode boxedElementTypeCode); + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, boxedTypeCode, boxedElementTypeCode); + break; } } From 466a515734cd04a9eefae54ac516a505daa0f2c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 09:39:35 +0000 Subject: [PATCH 16/26] Simplify custom attribute rewrite typing and remove rewrite indirection Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/4fe77ae2-b014-4433-9586-1722a52da603 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 65 ++++++++----------- 1 file changed, 26 insertions(+), 39 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 7d11d8172e3aab..abd426c2f415a3 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -227,7 +227,6 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui { BlobReader valueReader = _module.MetadataReader.GetBlobReader(customAttribute.Value); var formatter = new CustomAttributeTypeNameFormatter(); - var typeProvider = new CustomAttributeTypeProvider(_module); // The dependency walk already decoded the blob successfully unless `_isCorrupted` is set. blobBuilder.WriteUInt16(valueReader.ReadUInt16()); @@ -235,7 +234,7 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui MethodSignature constructorSig = constructor.Signature; for (int i = 0; i < constructorSig.Length; i++) { - CopyFixedArgument(ref valueReader, blobBuilder, originalBlob, constructorSig[i], typeProvider, formatter); + CopyFixedArgument(ref valueReader, blobBuilder, originalBlob, constructorSig[i], _module, formatter); } ushort namedArgumentCount = valueReader.ReadUInt16(); @@ -246,9 +245,9 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui CustomAttributeNamedArgumentKind kind = (CustomAttributeNamedArgumentKind)valueReader.ReadByte(); blobBuilder.WriteByte((byte)kind); - CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); - CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, typeProvider, formatter); - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, valueTypeCode, elementValueTypeCode); + CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, _module, formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); + CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, _module, formatter); + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, _module, formatter, valueTypeCode, elementValueTypeCode); } } catch (Exception ex) when (ex is TypeSystemException or BadImageFormatException) @@ -265,7 +264,7 @@ private static void CopyRawBytes(ref BlobReader reader, BlobBuilder blobBuilder, blobBuilder.WriteBytes(originalBlob, startOffset, byteCount); } - private static void CopySerializedString(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, bool rewriteTypeName, CustomAttributeTypeProvider typeProvider, CustomAttributeTypeNameFormatter formatter) + private static void CopySerializedString(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, bool rewriteTypeName, EcmaModule module, CustomAttributeTypeNameFormatter formatter) { int startOffset = valueReader.Offset; string? original = valueReader.ReadSerializedString(); @@ -273,7 +272,7 @@ private static void CopySerializedString(ref BlobReader valueReader, BlobBuilder if (rewriteTypeName && original is not null) { - TypeDesc resolved = typeProvider.GetTypeFromSerializedName(original); + TypeDesc resolved = module.GetTypeByCustomAttributeTypeName(original); if (resolved is not null) { string rewritten = formatter.FormatName(resolved, true); @@ -288,19 +287,21 @@ private static void CopySerializedString(ref BlobReader valueReader, BlobBuilder blobBuilder.WriteBytes(originalBlob, startOffset, endOffset - startOffset); } - private static void CopyFixedArgument(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, TypeDesc type, CustomAttributeTypeProvider typeProvider, CustomAttributeTypeNameFormatter formatter) + private static void CopyFixedArgument(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, TypeDesc type, EcmaModule module, CustomAttributeTypeNameFormatter formatter) { - GetFixedArgumentTypeCodes(type, typeProvider, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, valueTypeCode, elementValueTypeCode); + GetFixedArgumentTypeCodes(type, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, module, formatter, valueTypeCode, elementValueTypeCode); } - private static void GetFixedArgumentTypeCodes(TypeDesc type, CustomAttributeTypeProvider typeProvider, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) + private static void GetFixedArgumentTypeCodes(TypeDesc type, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) { elementValueTypeCode = default; + valueTypeCode = default; + TypeDesc underlyingType = type.UnderlyingType; - if (type.UnderlyingType.IsPrimitive) + if (underlyingType.IsPrimitive) { - valueTypeCode = GetPrimitiveSerializationTypeCode(type); + valueTypeCode = GetPrimitiveSerializationTypeCode(underlyingType); } else if (type.IsString) { @@ -317,15 +318,7 @@ private static void GetFixedArgumentTypeCodes(TypeDesc type, CustomAttributeType else if (type.IsSzArray) { valueTypeCode = SerializationTypeCode.SZArray; - GetFixedArgumentTypeCodes(((ArrayType)type).ElementType, typeProvider, out elementValueTypeCode, out _); - } - else if (type.IsEnum) - { - valueTypeCode = (SerializationTypeCode)typeProvider.GetUnderlyingEnumType(type); - } - else - { - valueTypeCode = SerializationTypeCode.Invalid; + GetFixedArgumentTypeCodes(((ArrayType)type).ElementType, out elementValueTypeCode, out _); } } @@ -349,7 +342,7 @@ private static SerializationTypeCode GetPrimitiveSerializationTypeCode(TypeDesc } } - private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, CustomAttributeTypeProvider typeProvider, CustomAttributeTypeNameFormatter formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) + private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, EcmaModule module, CustomAttributeTypeNameFormatter formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) { elementValueTypeCode = default; @@ -359,21 +352,15 @@ private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilde switch (typeCode) { case SerializationTypeCode.SZArray: - CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, out elementValueTypeCode, out _); + CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, module, formatter, out elementValueTypeCode, out _); valueTypeCode = SerializationTypeCode.SZArray; break; case SerializationTypeCode.Enum: - int startOffset = valueReader.Offset; string enumTypeName = valueReader.ReadSerializedString()!; - int endOffset = valueReader.Offset; - TypeDesc enumType = typeProvider.GetTypeFromSerializedName(enumTypeName)!; + TypeDesc enumType = module.GetTypeByCustomAttributeTypeName(enumTypeName)!; string rewritten = formatter.FormatName(enumType, true); - if (rewritten == enumTypeName) - blobBuilder.WriteBytes(originalBlob, startOffset, endOffset - startOffset); - else - blobBuilder.WriteSerializedString(rewritten); - - valueTypeCode = (SerializationTypeCode)typeProvider.GetUnderlyingEnumType(enumType); + blobBuilder.WriteSerializedString(rewritten); + valueTypeCode = GetPrimitiveSerializationTypeCode(enumType.UnderlyingType); break; default: valueTypeCode = typeCode; @@ -381,7 +368,7 @@ private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilde } } - private static void CopySerializedValue(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, CustomAttributeTypeProvider typeProvider, CustomAttributeTypeNameFormatter formatter, SerializationTypeCode valueTypeCode, SerializationTypeCode elementValueTypeCode) + private static void CopySerializedValue(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, EcmaModule module, CustomAttributeTypeNameFormatter formatter, SerializationTypeCode valueTypeCode, SerializationTypeCode elementValueTypeCode) { switch (valueTypeCode) { @@ -406,10 +393,10 @@ private static void CopySerializedValue(ref BlobReader valueReader, BlobBuilder CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 8); break; case SerializationTypeCode.String: - CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, typeProvider, formatter); + CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, module, formatter); break; case SerializationTypeCode.Type: - CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: true, typeProvider, formatter); + CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: true, module, formatter); break; case SerializationTypeCode.SZArray: int elementCount = valueReader.ReadInt32(); @@ -418,13 +405,13 @@ private static void CopySerializedValue(ref BlobReader valueReader, BlobBuilder { for (int i = 0; i < elementCount; i++) { - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, elementValueTypeCode, default); + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, module, formatter, elementValueTypeCode, default); } } break; case SerializationTypeCode.TaggedObject: - CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, out SerializationTypeCode boxedTypeCode, out SerializationTypeCode boxedElementTypeCode); - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, typeProvider, formatter, boxedTypeCode, boxedElementTypeCode); + CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, module, formatter, out SerializationTypeCode boxedTypeCode, out SerializationTypeCode boxedElementTypeCode); + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, module, formatter, boxedTypeCode, boxedElementTypeCode); break; } } From d4b6451527395c611c5d30461cc08c71b8ac579e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 11:20:40 +0000 Subject: [PATCH 17/26] Refactor CustomAttributeNode rewrite helpers to instance methods Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/6129cc2c-9d66-4f42-876a-056fcef7f711 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 84 ++++++++----------- 1 file changed, 37 insertions(+), 47 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index abd426c2f415a3..7a138805392b5d 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -234,7 +234,7 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui MethodSignature constructorSig = constructor.Signature; for (int i = 0; i < constructorSig.Length; i++) { - CopyFixedArgument(ref valueReader, blobBuilder, originalBlob, constructorSig[i], _module, formatter); + CopyFixedArgument(ref valueReader, blobBuilder, originalBlob, constructorSig[i], formatter); } ushort namedArgumentCount = valueReader.ReadUInt16(); @@ -245,9 +245,9 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui CustomAttributeNamedArgumentKind kind = (CustomAttributeNamedArgumentKind)valueReader.ReadByte(); blobBuilder.WriteByte((byte)kind); - CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, _module, formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); - CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, _module, formatter); - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, _module, formatter, valueTypeCode, elementValueTypeCode); + CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); + CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, formatter); + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, formatter, valueTypeCode, elementValueTypeCode); } } catch (Exception ex) when (ex is TypeSystemException or BadImageFormatException) @@ -264,7 +264,7 @@ private static void CopyRawBytes(ref BlobReader reader, BlobBuilder blobBuilder, blobBuilder.WriteBytes(originalBlob, startOffset, byteCount); } - private static void CopySerializedString(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, bool rewriteTypeName, EcmaModule module, CustomAttributeTypeNameFormatter formatter) + private void CopySerializedString(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, bool rewriteTypeName, CustomAttributeTypeNameFormatter formatter) { int startOffset = valueReader.Offset; string? original = valueReader.ReadSerializedString(); @@ -272,7 +272,7 @@ private static void CopySerializedString(ref BlobReader valueReader, BlobBuilder if (rewriteTypeName && original is not null) { - TypeDesc resolved = module.GetTypeByCustomAttributeTypeName(original); + TypeDesc resolved = _module.GetTypeByCustomAttributeTypeName(original); if (resolved is not null) { string rewritten = formatter.FormatName(resolved, true); @@ -287,10 +287,10 @@ private static void CopySerializedString(ref BlobReader valueReader, BlobBuilder blobBuilder.WriteBytes(originalBlob, startOffset, endOffset - startOffset); } - private static void CopyFixedArgument(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, TypeDesc type, EcmaModule module, CustomAttributeTypeNameFormatter formatter) + private void CopyFixedArgument(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, TypeDesc type, CustomAttributeTypeNameFormatter formatter) { GetFixedArgumentTypeCodes(type, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, module, formatter, valueTypeCode, elementValueTypeCode); + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, formatter, valueTypeCode, elementValueTypeCode); } private static void GetFixedArgumentTypeCodes(TypeDesc type, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) @@ -323,26 +323,24 @@ private static void GetFixedArgumentTypeCodes(TypeDesc type, out SerializationTy } private static SerializationTypeCode GetPrimitiveSerializationTypeCode(TypeDesc type) - { - switch (type.Category) + => type.Category switch { - case TypeFlags.Boolean: return SerializationTypeCode.Boolean; - case TypeFlags.Char: return SerializationTypeCode.Char; - case TypeFlags.SByte: return SerializationTypeCode.SByte; - case TypeFlags.Byte: return SerializationTypeCode.Byte; - case TypeFlags.Int16: return SerializationTypeCode.Int16; - case TypeFlags.UInt16: return SerializationTypeCode.UInt16; - case TypeFlags.Int32: return SerializationTypeCode.Int32; - case TypeFlags.UInt32: return SerializationTypeCode.UInt32; - case TypeFlags.Int64: return SerializationTypeCode.Int64; - case TypeFlags.UInt64: return SerializationTypeCode.UInt64; - case TypeFlags.Single: return SerializationTypeCode.Single; - case TypeFlags.Double: return SerializationTypeCode.Double; - default: throw new BadImageFormatException(); - } - } - - private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, EcmaModule module, CustomAttributeTypeNameFormatter formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) + TypeFlags.Boolean => SerializationTypeCode.Boolean, + TypeFlags.Char => SerializationTypeCode.Char, + TypeFlags.SByte => SerializationTypeCode.SByte, + TypeFlags.Byte => SerializationTypeCode.Byte, + TypeFlags.Int16 => SerializationTypeCode.Int16, + TypeFlags.UInt16 => SerializationTypeCode.UInt16, + TypeFlags.Int32 => SerializationTypeCode.Int32, + TypeFlags.UInt32 => SerializationTypeCode.UInt32, + TypeFlags.Int64 => SerializationTypeCode.Int64, + TypeFlags.UInt64 => SerializationTypeCode.UInt64, + TypeFlags.Single => SerializationTypeCode.Single, + TypeFlags.Double => SerializationTypeCode.Double, + _ => throw new BadImageFormatException(), + }; + + private void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, CustomAttributeTypeNameFormatter formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) { elementValueTypeCode = default; @@ -352,12 +350,12 @@ private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilde switch (typeCode) { case SerializationTypeCode.SZArray: - CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, module, formatter, out elementValueTypeCode, out _); + CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, formatter, out elementValueTypeCode, out _); valueTypeCode = SerializationTypeCode.SZArray; break; case SerializationTypeCode.Enum: string enumTypeName = valueReader.ReadSerializedString()!; - TypeDesc enumType = module.GetTypeByCustomAttributeTypeName(enumTypeName)!; + TypeDesc enumType = _module.GetTypeByCustomAttributeTypeName(enumTypeName)!; string rewritten = formatter.FormatName(enumType, true); blobBuilder.WriteSerializedString(rewritten); valueTypeCode = GetPrimitiveSerializationTypeCode(enumType.UnderlyingType); @@ -368,35 +366,27 @@ private static void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilde } } - private static void CopySerializedValue(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, EcmaModule module, CustomAttributeTypeNameFormatter formatter, SerializationTypeCode valueTypeCode, SerializationTypeCode elementValueTypeCode) + private void CopySerializedValue(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, CustomAttributeTypeNameFormatter formatter, SerializationTypeCode valueTypeCode, SerializationTypeCode elementValueTypeCode) { switch (valueTypeCode) { - case SerializationTypeCode.Boolean: - case SerializationTypeCode.Byte: - case SerializationTypeCode.SByte: + case SerializationTypeCode.Boolean or SerializationTypeCode.Byte or SerializationTypeCode.SByte: CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 1); break; - case SerializationTypeCode.Char: - case SerializationTypeCode.Int16: - case SerializationTypeCode.UInt16: + case SerializationTypeCode.Char or SerializationTypeCode.Int16 or SerializationTypeCode.UInt16: CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 2); break; - case SerializationTypeCode.Int32: - case SerializationTypeCode.UInt32: - case SerializationTypeCode.Single: + case SerializationTypeCode.Int32 or SerializationTypeCode.UInt32 or SerializationTypeCode.Single: CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 4); break; - case SerializationTypeCode.Int64: - case SerializationTypeCode.UInt64: - case SerializationTypeCode.Double: + case SerializationTypeCode.Int64 or SerializationTypeCode.UInt64 or SerializationTypeCode.Double: CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 8); break; case SerializationTypeCode.String: - CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, module, formatter); + CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, formatter); break; case SerializationTypeCode.Type: - CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: true, module, formatter); + CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: true, formatter); break; case SerializationTypeCode.SZArray: int elementCount = valueReader.ReadInt32(); @@ -405,13 +395,13 @@ private static void CopySerializedValue(ref BlobReader valueReader, BlobBuilder { for (int i = 0; i < elementCount; i++) { - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, module, formatter, elementValueTypeCode, default); + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, formatter, elementValueTypeCode, default); } } break; case SerializationTypeCode.TaggedObject: - CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, module, formatter, out SerializationTypeCode boxedTypeCode, out SerializationTypeCode boxedElementTypeCode); - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, module, formatter, boxedTypeCode, boxedElementTypeCode); + CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, formatter, out SerializationTypeCode boxedTypeCode, out SerializationTypeCode boxedElementTypeCode); + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, formatter, boxedTypeCode, boxedElementTypeCode); break; } } From ef95ab1b36b457ef86b9044a2ca461db0d11ad93 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 11:48:08 +0000 Subject: [PATCH 18/26] Apply follow-up CustomAttributeNode simplifications from review Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/20d7918c-7976-4a3d-882f-8aac8f4ade8b Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 49 +++++-------------- 1 file changed, 13 insertions(+), 36 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 7a138805392b5d..335380b09dd471 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -81,8 +81,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto } catch (Exception ex) when (ex is TypeSystemException or BadImageFormatException) { - // Attribute ctor doesn't resolve, typeof() refers to something that can't be loaded, - // attribute refers to a non-existing field, malformed blob, etc. + // Metadata decode failed. _isCorrupted = true; return dependencies; } @@ -112,9 +111,6 @@ public override IEnumerable GetStaticDependencies(NodeFacto private static void GetDependenciesFromCustomAttributeArgument(DependencyList dependencies, NodeFactory factory, TypeDesc type, object value) { - if (type is null) - return; - // Report the type itself (e.g. enum types that need to be kept for boxing) if (factory.ReflectedType(type) is DependencyNode typeNode) dependencies.Add(typeNode, "Custom attribute blob"); @@ -210,14 +206,7 @@ protected override EntityHandle WriteInternal(ModuleWritingContext writeContext) private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBuilder blobBuilder) { byte[] originalBlob = _module.MetadataReader.GetBlobBytes(customAttribute.Value); - if (_isCorrupted) - { - blobBuilder.WriteBytes(originalBlob); - return; - } - - MethodDesc constructor = _module.TryGetMethod(customAttribute.Constructor); - if (constructor is null) + if (_isCorrupted || _module.TryGetMethod(customAttribute.Constructor) is not MethodDesc constructor) { blobBuilder.WriteBytes(originalBlob); return; @@ -246,7 +235,7 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui blobBuilder.WriteByte((byte)kind); CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); - CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, formatter); + CopySerializedString(ref valueReader, blobBuilder, rewriteTypeName: false, formatter); CopySerializedValue(ref valueReader, blobBuilder, originalBlob, formatter, valueTypeCode, elementValueTypeCode); } } @@ -264,27 +253,17 @@ private static void CopyRawBytes(ref BlobReader reader, BlobBuilder blobBuilder, blobBuilder.WriteBytes(originalBlob, startOffset, byteCount); } - private void CopySerializedString(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, bool rewriteTypeName, CustomAttributeTypeNameFormatter formatter) + private void CopySerializedString(ref BlobReader valueReader, BlobBuilder blobBuilder, bool rewriteTypeName, CustomAttributeTypeNameFormatter formatter) { - int startOffset = valueReader.Offset; - string? original = valueReader.ReadSerializedString(); - int endOffset = valueReader.Offset; + string? s = valueReader.ReadSerializedString(); - if (rewriteTypeName && original is not null) + if (rewriteTypeName && s is not null) { - TypeDesc resolved = _module.GetTypeByCustomAttributeTypeName(original); - if (resolved is not null) - { - string rewritten = formatter.FormatName(resolved, true); - if (rewritten != original) - { - blobBuilder.WriteSerializedString(rewritten); - return; - } - } + TypeDesc resolved = _module.GetTypeByCustomAttributeTypeName(s); + s = formatter.FormatName(resolved, true); } - blobBuilder.WriteBytes(originalBlob, startOffset, endOffset - startOffset); + blobBuilder.WriteSerializedString(s); } private void CopyFixedArgument(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, TypeDesc type, CustomAttributeTypeNameFormatter formatter) @@ -297,11 +276,9 @@ private static void GetFixedArgumentTypeCodes(TypeDesc type, out SerializationTy { elementValueTypeCode = default; valueTypeCode = default; - TypeDesc underlyingType = type.UnderlyingType; - - if (underlyingType.IsPrimitive) + if (type.IsPrimitive || type.IsEnum) { - valueTypeCode = GetPrimitiveSerializationTypeCode(underlyingType); + valueTypeCode = GetPrimitiveSerializationTypeCode(type.UnderlyingType); } else if (type.IsString) { @@ -383,10 +360,10 @@ private void CopySerializedValue(ref BlobReader valueReader, BlobBuilder blobBui CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 8); break; case SerializationTypeCode.String: - CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: false, formatter); + CopySerializedString(ref valueReader, blobBuilder, rewriteTypeName: false, formatter); break; case SerializationTypeCode.Type: - CopySerializedString(ref valueReader, blobBuilder, originalBlob, rewriteTypeName: true, formatter); + CopySerializedString(ref valueReader, blobBuilder, rewriteTypeName: true, formatter); break; case SerializationTypeCode.SZArray: int elementCount = valueReader.ReadInt32(); From 4606c8d710c494834c7c4b54d54ad88fc2c3d24f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 12:24:40 +0000 Subject: [PATCH 19/26] Address remaining CustomAttributeNode review comments Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/f536fc71-012c-4ae8-b43e-0abb314fd4c0 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 335380b09dd471..fb09d8b24ed036 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -284,10 +284,6 @@ private static void GetFixedArgumentTypeCodes(TypeDesc type, out SerializationTy { valueTypeCode = SerializationTypeCode.String; } - else if (IsSystemType(type)) - { - valueTypeCode = SerializationTypeCode.Type; - } else if (type.IsObject) { valueTypeCode = SerializationTypeCode.TaggedObject; @@ -297,6 +293,10 @@ private static void GetFixedArgumentTypeCodes(TypeDesc type, out SerializationTy valueTypeCode = SerializationTypeCode.SZArray; GetFixedArgumentTypeCodes(((ArrayType)type).ElementType, out elementValueTypeCode, out _); } + else + { + valueTypeCode = SerializationTypeCode.Type; + } } private static SerializationTypeCode GetPrimitiveSerializationTypeCode(TypeDesc type) @@ -368,12 +368,9 @@ private void CopySerializedValue(ref BlobReader valueReader, BlobBuilder blobBui case SerializationTypeCode.SZArray: int elementCount = valueReader.ReadInt32(); blobBuilder.WriteInt32(elementCount); - if (elementCount > 0) + for (int i = 0; i < elementCount; i++) { - for (int i = 0; i < elementCount; i++) - { - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, formatter, elementValueTypeCode, default); - } + CopySerializedValue(ref valueReader, blobBuilder, originalBlob, formatter, elementValueTypeCode, default); } break; case SerializationTypeCode.TaggedObject: @@ -383,15 +380,6 @@ private void CopySerializedValue(ref BlobReader valueReader, BlobBuilder blobBui } } - private static bool IsSystemType(TypeDesc type) - { - if (type is not MetadataType mdType) - return false; - - return mdType.Name.SequenceEqual("Type"u8) - && mdType.Namespace.SequenceEqual("System"u8); - } - public override string ToString() { // TODO: Need to write a helper to get the name of the type From 4bb3fae14c29fcc123b100e55f18cb5da7c1be6c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 12:49:24 +0000 Subject: [PATCH 20/26] Remove originalBlob plumbing from attribute rewrite helpers Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/e2e5e7e2-e344-4277-b320-fb781514a11c Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index fb09d8b24ed036..c0a0f1018c8251 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -223,7 +223,7 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui MethodSignature constructorSig = constructor.Signature; for (int i = 0; i < constructorSig.Length; i++) { - CopyFixedArgument(ref valueReader, blobBuilder, originalBlob, constructorSig[i], formatter); + CopyFixedArgument(ref valueReader, blobBuilder, constructorSig[i], formatter); } ushort namedArgumentCount = valueReader.ReadUInt16(); @@ -234,9 +234,9 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui CustomAttributeNamedArgumentKind kind = (CustomAttributeNamedArgumentKind)valueReader.ReadByte(); blobBuilder.WriteByte((byte)kind); - CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); + CopyNamedArgumentType(ref valueReader, blobBuilder, formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); CopySerializedString(ref valueReader, blobBuilder, rewriteTypeName: false, formatter); - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, formatter, valueTypeCode, elementValueTypeCode); + CopySerializedValue(ref valueReader, blobBuilder, formatter, valueTypeCode, elementValueTypeCode); } } catch (Exception ex) when (ex is TypeSystemException or BadImageFormatException) @@ -246,12 +246,9 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui } } - private static void CopyRawBytes(ref BlobReader reader, BlobBuilder blobBuilder, byte[] originalBlob, int byteCount) - { - int startOffset = reader.Offset; - reader.Offset = startOffset + byteCount; - blobBuilder.WriteBytes(originalBlob, startOffset, byteCount); - } + // We can avoid the intermediate array allocation after https://github.com/dotnet/runtime/issues/85280 (or with unsafe code now) + private static void CopyRawBytes(ref BlobReader reader, BlobBuilder blobBuilder, int byteCount) + => blobBuilder.WriteBytes(reader.ReadBytes(byteCount)); private void CopySerializedString(ref BlobReader valueReader, BlobBuilder blobBuilder, bool rewriteTypeName, CustomAttributeTypeNameFormatter formatter) { @@ -266,10 +263,10 @@ private void CopySerializedString(ref BlobReader valueReader, BlobBuilder blobBu blobBuilder.WriteSerializedString(s); } - private void CopyFixedArgument(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, TypeDesc type, CustomAttributeTypeNameFormatter formatter) + private void CopyFixedArgument(ref BlobReader valueReader, BlobBuilder blobBuilder, TypeDesc type, CustomAttributeTypeNameFormatter formatter) { GetFixedArgumentTypeCodes(type, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, formatter, valueTypeCode, elementValueTypeCode); + CopySerializedValue(ref valueReader, blobBuilder, formatter, valueTypeCode, elementValueTypeCode); } private static void GetFixedArgumentTypeCodes(TypeDesc type, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) @@ -317,7 +314,7 @@ private static SerializationTypeCode GetPrimitiveSerializationTypeCode(TypeDesc _ => throw new BadImageFormatException(), }; - private void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, CustomAttributeTypeNameFormatter formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) + private void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobBuilder, CustomAttributeTypeNameFormatter formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) { elementValueTypeCode = default; @@ -327,7 +324,7 @@ private void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobB switch (typeCode) { case SerializationTypeCode.SZArray: - CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, formatter, out elementValueTypeCode, out _); + CopyNamedArgumentType(ref valueReader, blobBuilder, formatter, out elementValueTypeCode, out _); valueTypeCode = SerializationTypeCode.SZArray; break; case SerializationTypeCode.Enum: @@ -343,21 +340,21 @@ private void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobB } } - private void CopySerializedValue(ref BlobReader valueReader, BlobBuilder blobBuilder, byte[] originalBlob, CustomAttributeTypeNameFormatter formatter, SerializationTypeCode valueTypeCode, SerializationTypeCode elementValueTypeCode) + private void CopySerializedValue(ref BlobReader valueReader, BlobBuilder blobBuilder, CustomAttributeTypeNameFormatter formatter, SerializationTypeCode valueTypeCode, SerializationTypeCode elementValueTypeCode) { switch (valueTypeCode) { case SerializationTypeCode.Boolean or SerializationTypeCode.Byte or SerializationTypeCode.SByte: - CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 1); + CopyRawBytes(ref valueReader, blobBuilder, 1); break; case SerializationTypeCode.Char or SerializationTypeCode.Int16 or SerializationTypeCode.UInt16: - CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 2); + CopyRawBytes(ref valueReader, blobBuilder, 2); break; case SerializationTypeCode.Int32 or SerializationTypeCode.UInt32 or SerializationTypeCode.Single: - CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 4); + CopyRawBytes(ref valueReader, blobBuilder, 4); break; case SerializationTypeCode.Int64 or SerializationTypeCode.UInt64 or SerializationTypeCode.Double: - CopyRawBytes(ref valueReader, blobBuilder, originalBlob, 8); + CopyRawBytes(ref valueReader, blobBuilder, 8); break; case SerializationTypeCode.String: CopySerializedString(ref valueReader, blobBuilder, rewriteTypeName: false, formatter); @@ -370,12 +367,12 @@ private void CopySerializedValue(ref BlobReader valueReader, BlobBuilder blobBui blobBuilder.WriteInt32(elementCount); for (int i = 0; i < elementCount; i++) { - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, formatter, elementValueTypeCode, default); + CopySerializedValue(ref valueReader, blobBuilder, formatter, elementValueTypeCode, default); } break; case SerializationTypeCode.TaggedObject: - CopyNamedArgumentType(ref valueReader, blobBuilder, originalBlob, formatter, out SerializationTypeCode boxedTypeCode, out SerializationTypeCode boxedElementTypeCode); - CopySerializedValue(ref valueReader, blobBuilder, originalBlob, formatter, boxedTypeCode, boxedElementTypeCode); + CopyNamedArgumentType(ref valueReader, blobBuilder, formatter, out SerializationTypeCode boxedTypeCode, out SerializationTypeCode boxedElementTypeCode); + CopySerializedValue(ref valueReader, blobBuilder, formatter, boxedTypeCode, boxedElementTypeCode); break; } } From 9c8afd1bd5f0d71607ef5fe2427beceed6184ff5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 13:18:55 +0000 Subject: [PATCH 21/26] Address latest ILTrim CustomAttributeNode review comments Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/47730547-daf1-4d8d-aa61-66dda42e8d3e Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index c0a0f1018c8251..19943073acedd1 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -223,7 +223,8 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui MethodSignature constructorSig = constructor.Signature; for (int i = 0; i < constructorSig.Length; i++) { - CopyFixedArgument(ref valueReader, blobBuilder, constructorSig[i], formatter); + GetFixedArgumentTypeCodes(constructorSig[i], out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); + CopySerializedValue(ref valueReader, blobBuilder, formatter, valueTypeCode, elementValueTypeCode); } ushort namedArgumentCount = valueReader.ReadUInt16(); @@ -263,16 +264,9 @@ private void CopySerializedString(ref BlobReader valueReader, BlobBuilder blobBu blobBuilder.WriteSerializedString(s); } - private void CopyFixedArgument(ref BlobReader valueReader, BlobBuilder blobBuilder, TypeDesc type, CustomAttributeTypeNameFormatter formatter) - { - GetFixedArgumentTypeCodes(type, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); - CopySerializedValue(ref valueReader, blobBuilder, formatter, valueTypeCode, elementValueTypeCode); - } - private static void GetFixedArgumentTypeCodes(TypeDesc type, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) { elementValueTypeCode = default; - valueTypeCode = default; if (type.IsPrimitive || type.IsEnum) { valueTypeCode = GetPrimitiveSerializationTypeCode(type.UnderlyingType); @@ -316,16 +310,14 @@ private static SerializationTypeCode GetPrimitiveSerializationTypeCode(TypeDesc private void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobBuilder, CustomAttributeTypeNameFormatter formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) { + valueTypeCode = valueReader.ReadSerializationTypeCode(); elementValueTypeCode = default; + blobBuilder.WriteByte((byte)valueTypeCode); - SerializationTypeCode typeCode = valueReader.ReadSerializationTypeCode(); - blobBuilder.WriteByte((byte)typeCode); - - switch (typeCode) + switch (valueTypeCode) { case SerializationTypeCode.SZArray: CopyNamedArgumentType(ref valueReader, blobBuilder, formatter, out elementValueTypeCode, out _); - valueTypeCode = SerializationTypeCode.SZArray; break; case SerializationTypeCode.Enum: string enumTypeName = valueReader.ReadSerializedString()!; @@ -334,9 +326,6 @@ private void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobB blobBuilder.WriteSerializedString(rewritten); valueTypeCode = GetPrimitiveSerializationTypeCode(enumType.UnderlyingType); break; - default: - valueTypeCode = typeCode; - break; } } From b64b9ac9df5eca86978a5ee84229123cb2931974 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 14:08:38 +0000 Subject: [PATCH 22/26] Refine ILTrim custom attribute rewrite per latest review comments Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/6f52143b-ce97-4011-8d73-52d18ba9700c Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../TokenBased/CustomAttributeNode.cs | 62 ++++++++----------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 19943073acedd1..1cf2d29449b708 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -205,45 +205,36 @@ protected override EntityHandle WriteInternal(ModuleWritingContext writeContext) private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBuilder blobBuilder) { - byte[] originalBlob = _module.MetadataReader.GetBlobBytes(customAttribute.Value); if (_isCorrupted || _module.TryGetMethod(customAttribute.Constructor) is not MethodDesc constructor) { - blobBuilder.WriteBytes(originalBlob); + blobBuilder.WriteBytes(_module.MetadataReader.GetBlobBytes(customAttribute.Value)); return; } - try - { - BlobReader valueReader = _module.MetadataReader.GetBlobReader(customAttribute.Value); - var formatter = new CustomAttributeTypeNameFormatter(); + BlobReader valueReader = _module.MetadataReader.GetBlobReader(customAttribute.Value); + var formatter = new CustomAttributeTypeNameFormatter(); - // The dependency walk already decoded the blob successfully unless `_isCorrupted` is set. - blobBuilder.WriteUInt16(valueReader.ReadUInt16()); + // The dependency walk already decoded the blob successfully unless `_isCorrupted` is set. + blobBuilder.WriteUInt16(valueReader.ReadUInt16()); - MethodSignature constructorSig = constructor.Signature; - for (int i = 0; i < constructorSig.Length; i++) - { - GetFixedArgumentTypeCodes(constructorSig[i], out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); - CopySerializedValue(ref valueReader, blobBuilder, formatter, valueTypeCode, elementValueTypeCode); - } + MethodSignature constructorSig = constructor.Signature; + for (int i = 0; i < constructorSig.Length; i++) + { + GetFixedArgumentTypeCodes(constructorSig[i], out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); + CopySerializedValue(ref valueReader, blobBuilder, formatter, valueTypeCode, elementValueTypeCode); + } - ushort namedArgumentCount = valueReader.ReadUInt16(); - blobBuilder.WriteUInt16(namedArgumentCount); + ushort namedArgumentCount = valueReader.ReadUInt16(); + blobBuilder.WriteUInt16(namedArgumentCount); - for (int i = 0; i < namedArgumentCount; i++) - { - CustomAttributeNamedArgumentKind kind = (CustomAttributeNamedArgumentKind)valueReader.ReadByte(); - blobBuilder.WriteByte((byte)kind); - - CopyNamedArgumentType(ref valueReader, blobBuilder, formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); - CopySerializedString(ref valueReader, blobBuilder, rewriteTypeName: false, formatter); - CopySerializedValue(ref valueReader, blobBuilder, formatter, valueTypeCode, elementValueTypeCode); - } - } - catch (Exception ex) when (ex is TypeSystemException or BadImageFormatException) + for (int i = 0; i < namedArgumentCount; i++) { - blobBuilder.Clear(); - blobBuilder.WriteBytes(originalBlob); + CustomAttributeNamedArgumentKind kind = (CustomAttributeNamedArgumentKind)valueReader.ReadByte(); + blobBuilder.WriteByte((byte)kind); + + CopyNamedArgumentType(ref valueReader, blobBuilder, formatter, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode); + CopySerializedString(ref valueReader, blobBuilder, rewriteTypeName: false, formatter); + CopySerializedValue(ref valueReader, blobBuilder, formatter, valueTypeCode, elementValueTypeCode); } } @@ -251,17 +242,19 @@ private void RewriteCustomAttributeBlob(CustomAttribute customAttribute, BlobBui private static void CopyRawBytes(ref BlobReader reader, BlobBuilder blobBuilder, int byteCount) => blobBuilder.WriteBytes(reader.ReadBytes(byteCount)); - private void CopySerializedString(ref BlobReader valueReader, BlobBuilder blobBuilder, bool rewriteTypeName, CustomAttributeTypeNameFormatter formatter) + private TypeDesc? CopySerializedString(ref BlobReader valueReader, BlobBuilder blobBuilder, bool rewriteTypeName, CustomAttributeTypeNameFormatter formatter) { string? s = valueReader.ReadSerializedString(); + TypeDesc? resolved = null; - if (rewriteTypeName && s is not null) + if (rewriteTypeName) { - TypeDesc resolved = _module.GetTypeByCustomAttributeTypeName(s); + resolved = _module.GetTypeByCustomAttributeTypeName(s ?? throw new BadImageFormatException("Custom attribute blob contains null type name.")); s = formatter.FormatName(resolved, true); } blobBuilder.WriteSerializedString(s); + return resolved; } private static void GetFixedArgumentTypeCodes(TypeDesc type, out SerializationTypeCode valueTypeCode, out SerializationTypeCode elementValueTypeCode) @@ -320,10 +313,7 @@ private void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobB CopyNamedArgumentType(ref valueReader, blobBuilder, formatter, out elementValueTypeCode, out _); break; case SerializationTypeCode.Enum: - string enumTypeName = valueReader.ReadSerializedString()!; - TypeDesc enumType = _module.GetTypeByCustomAttributeTypeName(enumTypeName)!; - string rewritten = formatter.FormatName(enumType, true); - blobBuilder.WriteSerializedString(rewritten); + TypeDesc enumType = CopySerializedString(ref valueReader, blobBuilder, rewriteTypeName: true, formatter) ?? throw new BadImageFormatException("Custom attribute blob contains null enum type name."); valueTypeCode = GetPrimitiveSerializationTypeCode(enumType.UnderlyingType); break; } From bd085a6075d04ea3c873d8aaa79ce26bdcf52add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Mon, 11 May 2026 14:27:03 -0700 Subject: [PATCH 23/26] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michal Strehovský --- .../DependencyAnalysis/TokenBased/CustomAttributeNode.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 1cf2d29449b708..cd5b280abe6a7b 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -247,9 +247,9 @@ private static void CopyRawBytes(ref BlobReader reader, BlobBuilder blobBuilder, string? s = valueReader.ReadSerializedString(); TypeDesc? resolved = null; - if (rewriteTypeName) + if (rewriteTypeName && s is not null) { - resolved = _module.GetTypeByCustomAttributeTypeName(s ?? throw new BadImageFormatException("Custom attribute blob contains null type name.")); + resolved = _module.GetTypeByCustomAttributeTypeName(s)); s = formatter.FormatName(resolved, true); } @@ -313,7 +313,7 @@ private void CopyNamedArgumentType(ref BlobReader valueReader, BlobBuilder blobB CopyNamedArgumentType(ref valueReader, blobBuilder, formatter, out elementValueTypeCode, out _); break; case SerializationTypeCode.Enum: - TypeDesc enumType = CopySerializedString(ref valueReader, blobBuilder, rewriteTypeName: true, formatter) ?? throw new BadImageFormatException("Custom attribute blob contains null enum type name."); + TypeDesc enumType = CopySerializedString(ref valueReader, blobBuilder, rewriteTypeName: true, formatter); valueTypeCode = GetPrimitiveSerializationTypeCode(enumType.UnderlyingType); break; } From d469dfaa774857a5bc59d81208c90f5ca689f6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 12 May 2026 06:35:37 +0900 Subject: [PATCH 24/26] Fix syntax error in CustomAttributeNode.cs --- .../DependencyAnalysis/TokenBased/CustomAttributeNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index cd5b280abe6a7b..33d4993316e4d3 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -249,7 +249,7 @@ private static void CopyRawBytes(ref BlobReader reader, BlobBuilder blobBuilder, if (rewriteTypeName && s is not null) { - resolved = _module.GetTypeByCustomAttributeTypeName(s)); + resolved = _module.GetTypeByCustomAttributeTypeName(s); s = formatter.FormatName(resolved, true); } From 37d65646657bdc2d118cf9722c21e4350392810c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 06:32:01 +0000 Subject: [PATCH 25/26] ILTrim: use non-throwing type lookup for custom attribute type-name rewrite Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/5aad31ca-3463-4605-90a7-54c2be91deb6 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../DependencyAnalysis/TokenBased/CustomAttributeNode.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 33d4993316e4d3..5098db5851ded5 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -249,8 +249,11 @@ private static void CopyRawBytes(ref BlobReader reader, BlobBuilder blobBuilder, if (rewriteTypeName && s is not null) { - resolved = _module.GetTypeByCustomAttributeTypeName(s); - s = formatter.FormatName(resolved, true); + resolved = _module.GetTypeByCustomAttributeTypeName(s, throwIfNotFound: false); + if (resolved is not null) + { + s = formatter.FormatName(resolved, true); + } } blobBuilder.WriteSerializedString(s); From ae36c3aadcbb796db323e0bf3bf6102ba072474c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Wed, 13 May 2026 15:43:22 +0900 Subject: [PATCH 26/26] Sigh, this path is already validated, this is unnecessary --- .../DependencyAnalysis/TokenBased/CustomAttributeNode.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs index 5098db5851ded5..33d4993316e4d3 100644 --- a/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs +++ b/src/coreclr/tools/ILTrim.Core/DependencyAnalysis/TokenBased/CustomAttributeNode.cs @@ -249,11 +249,8 @@ private static void CopyRawBytes(ref BlobReader reader, BlobBuilder blobBuilder, if (rewriteTypeName && s is not null) { - resolved = _module.GetTypeByCustomAttributeTypeName(s, throwIfNotFound: false); - if (resolved is not null) - { - s = formatter.FormatName(resolved, true); - } + resolved = _module.GetTypeByCustomAttributeTypeName(s); + s = formatter.FormatName(resolved, true); } blobBuilder.WriteSerializedString(s);