Skip to content

Commit 550f811

Browse files
authored
Emit MetadataUpdateOriginalTypeAttribute (#63169)
1 parent b2693e6 commit 550f811

File tree

9 files changed

+215
-78
lines changed

9 files changed

+215
-78
lines changed

src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using Microsoft.CodeAnalysis.CSharp.Emit;
1515
using Microsoft.CodeAnalysis.CSharp.Syntax;
1616
using Microsoft.CodeAnalysis.PooledObjects;
17+
using Microsoft.CodeAnalysis.Symbols;
1718
using Roslyn.Utilities;
1819

1920
namespace Microsoft.CodeAnalysis.CSharp.Symbols
@@ -1606,6 +1607,26 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r
16061607
ref attributes,
16071608
compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_RequiredMemberAttribute__ctor));
16081609
}
1610+
1611+
// Add MetadataUpdateOriginalTypeAttribute when a reloadable type is emitted to EnC delta
1612+
if (moduleBuilder.EncSymbolChanges?.IsReplaced(((ISymbolInternal)this).GetISymbol()) == true)
1613+
{
1614+
// Note that we use this source named type symbol in the attribute argument (of System.Type).
1615+
// We do not have access to the original symbol from this compilation. However, System.Type
1616+
// is encoded in the attribute as a string containing a fully qualified type name.
1617+
// The name of the current type symbol as provided by ISymbol.Name is the same as the name of
1618+
// the original type symbol that is being replaced by this type symbol.
1619+
// The "#{generation}" suffix is appended to the TypeDef name in the metadata writer,
1620+
// but not to the attribute value.
1621+
var originalType = this;
1622+
1623+
AddSynthesizedAttribute(
1624+
ref attributes,
1625+
compilation.TrySynthesizeAttribute(
1626+
WellKnownMember.System_Runtime_CompilerServices_MetadataUpdateOriginalTypeAttribute__ctor,
1627+
ImmutableArray.Create(new TypedConstant(compilation.GetWellKnownType(WellKnownType.System_Type), TypedConstantKind.Type, originalType)),
1628+
isOptionalUse: true));
1629+
}
16091630
}
16101631

16111632
#endregion

src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.cs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,27 +145,48 @@ internal static EntityHandle Handle(int rowNumber, TableIndex table)
145145
internal static bool IsDefinition(HandleKind kind)
146146
=> kind is not (HandleKind.AssemblyReference or HandleKind.ModuleReference or HandleKind.TypeReference or HandleKind.MemberReference or HandleKind.TypeSpecification or HandleKind.MethodSpecification);
147147

148+
/// <summary>
149+
/// Checks that the EncLog contains specified rows.
150+
/// Any default values in the expected <paramref name="rows"/> are ignored to facilitate conditional code.
151+
/// </summary>
148152
internal static void CheckEncLog(MetadataReader reader, params EditAndContinueLogEntry[] rows)
149153
{
150-
AssertEx.Equal(rows, reader.GetEditAndContinueLogEntries(), itemInspector: EncLogRowToString);
154+
AssertEx.Equal(
155+
rows.Where(r => r.Handle != default),
156+
reader.GetEditAndContinueLogEntries(), itemInspector: EncLogRowToString);
151157
}
152158

153159
/// <summary>
154160
/// Checks that the EncLog contains specified definition rows. References are ignored as they are usually not interesting to validate. They are emitted as needed.
161+
/// Any default values in the expected <paramref name="rows"/> are ignored to facilitate conditional code.
155162
/// </summary>
156163
internal static void CheckEncLogDefinitions(MetadataReader reader, params EditAndContinueLogEntry[] rows)
157164
{
158-
AssertEx.Equal(rows, reader.GetEditAndContinueLogEntries().Where(e => IsDefinition(e.Handle.Kind)), itemInspector: EncLogRowToString);
165+
AssertEx.Equal(
166+
rows.Where(r => r.Handle != default),
167+
reader.GetEditAndContinueLogEntries().Where(e => IsDefinition(e.Handle.Kind)), itemInspector: EncLogRowToString);
159168
}
160169

170+
/// <summary>
171+
/// Checks that the EncMap contains specified handles.
172+
/// Any default values in the expected <paramref name="handles"/> are ignored to facilitate conditional code.
173+
/// </summary>
161174
internal static void CheckEncMap(MetadataReader reader, params EntityHandle[] handles)
162175
{
163-
AssertEx.Equal(handles, reader.GetEditAndContinueMapEntries(), itemInspector: EncMapRowToString);
176+
AssertEx.Equal(
177+
handles.Where(h => h != default),
178+
reader.GetEditAndContinueMapEntries(), itemInspector: EncMapRowToString);
164179
}
165180

181+
/// <summary>
182+
/// Checks that the EncMap contains specified definition handles. References are ignored as they are usually not interesting to validate. They are emitted as needed.
183+
/// Any default values in the expected <paramref name="handles"/> are ignored to facilitate conditional code.
184+
/// </summary>
166185
internal static void CheckEncMapDefinitions(MetadataReader reader, params EntityHandle[] handles)
167186
{
168-
AssertEx.Equal(handles, reader.GetEditAndContinueMapEntries().Where(e => IsDefinition(e.Kind)), itemInspector: EncMapRowToString);
187+
AssertEx.Equal(
188+
handles.Where(h => h != default),
189+
reader.GetEditAndContinueMapEntries().Where(e => IsDefinition(e.Kind)), itemInspector: EncMapRowToString);
169190
}
170191

171192
internal static void CheckAttributes(MetadataReader reader, params CustomAttributeRow[] rows)
@@ -203,7 +224,7 @@ private static void CheckNames<THandle>(
203224
Func<THandle, Handle> toHandle,
204225
string[] expectedNames)
205226
{
206-
var aggregator = new MetadataAggregator(readers[0], readers.Skip(1).ToArray());
227+
var aggregator = GetAggregator(readers);
207228

208229
AssertEx.Equal(expectedNames, entityHandles.Select(handle =>
209230
{
@@ -215,6 +236,27 @@ private static void CheckNames<THandle>(
215236
}));
216237
}
217238

239+
public static void CheckBlobValue(IList<MetadataReader> readers, BlobHandle valueHandle, byte[] expectedValue)
240+
{
241+
var aggregator = GetAggregator(readers);
242+
243+
var genHandle = (BlobHandle)aggregator.GetGenerationHandle(valueHandle, out int blobGeneration);
244+
var attributeData = readers[blobGeneration].GetBlobBytes(genHandle);
245+
AssertEx.Equal(expectedValue, attributeData);
246+
}
247+
248+
public static void CheckStringValue(IList<MetadataReader> readers, StringHandle valueHandle, string expectedValue)
249+
{
250+
var aggregator = GetAggregator(readers);
251+
252+
var genHandle = (StringHandle)aggregator.GetGenerationHandle(valueHandle, out int blobGeneration);
253+
var attributeData = readers[blobGeneration].GetString(genHandle);
254+
AssertEx.Equal(expectedValue, attributeData);
255+
}
256+
257+
public static MetadataAggregator GetAggregator(IList<MetadataReader> readers)
258+
=> new MetadataAggregator(readers[0], readers.Skip(1).ToArray());
259+
218260
internal static string EncLogRowToString(EditAndContinueLogEntry row)
219261
{
220262
TableIndex tableIndex;

0 commit comments

Comments
 (0)