Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EventBuilder implementation #95936

Merged
merged 2 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<Compile Include="System\Reflection\Emit\CustomAttributeWrapper.cs" />
<Compile Include="System\Reflection\Emit\AssemblyBuilderImpl.cs" />
<Compile Include="System\Reflection\Emit\EnumBuilderImpl.cs" />
<Compile Include="System\Reflection\Emit\EventBuilderImpl.cs" />
<Compile Include="System\Reflection\Emit\FieldBuilderImpl.cs" />
<Compile Include="System\Reflection\Emit\GenericTypeParameterBuilderImpl.cs" />
<Compile Include="System\Reflection\Emit\ILGeneratorImpl.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Reflection.Metadata;

namespace System.Reflection.Emit
{
internal sealed class EventBuilderImpl : EventBuilder
{
private string _name;
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
private EventAttributes _attributes;
private TypeBuilderImpl _typeBuilder;
private Type _eventType;

internal EventDefinitionHandle _handle;
internal MethodBuilder? _addOnMethod;
internal MethodBuilder? _raiseMethod;
internal MethodBuilder? _removeMethod;
internal HashSet<MethodBuilder>? _otherMethods;
internal List<CustomAttributeWrapper>? _customAttributes;

public EventBuilderImpl(string name, EventAttributes attributes, Type eventType, TypeBuilderImpl typeBuilder)
{
_name = name;
_attributes = attributes;
_typeBuilder = typeBuilder;
_eventType = eventType;
}

internal EventAttributes Attributes => _attributes;
internal string Name => _name;
internal Type EventType => _eventType;

protected override void AddOtherMethodCore(MethodBuilder mdBuilder)
{
ArgumentNullException.ThrowIfNull(mdBuilder);
_typeBuilder.ThrowIfCreated();

_otherMethods ??= new HashSet<MethodBuilder>();
_otherMethods.Add(mdBuilder);
}

protected override void SetAddOnMethodCore(MethodBuilder mdBuilder)
{
ArgumentNullException.ThrowIfNull(mdBuilder);
_typeBuilder.ThrowIfCreated();

_addOnMethod = mdBuilder;
}

protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
{
_typeBuilder.ThrowIfCreated();

if (con.ReflectedType!.FullName == "System.Runtime.CompilerServices.SpecialNameAttribute")
{
_attributes |= EventAttributes.SpecialName;
return;
}

_customAttributes ??= new List<CustomAttributeWrapper>();
_customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute));
}

protected override void SetRaiseMethodCore(MethodBuilder mdBuilder)
{
ArgumentNullException.ThrowIfNull(mdBuilder);
_typeBuilder.ThrowIfCreated();

_raiseMethod = mdBuilder;
}

protected override void SetRemoveOnMethodCore(MethodBuilder mdBuilder)
{
ArgumentNullException.ThrowIfNull(mdBuilder);
_typeBuilder.ThrowIfCreated();

_removeMethod = mdBuilder;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder
private int _nextFieldDefRowId = 1;
private int _nextParameterRowId = 1;
private int _nextPropertyRowId = 1;
private int _nextEventRowId = 1;
private bool _coreTypesFullyPopulated;
private Type?[]? _coreTypes;
private static readonly Type[] s_coreTypes = { typeof(void), typeof(object), typeof(bool), typeof(char), typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int),
Expand Down Expand Up @@ -170,9 +171,10 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder)
}

WriteCustomAttributes(typeBuilder._customAttributes, typeHandle);
WriteProperties(typeBuilder);
WriteFields(typeBuilder);
WriteMethods(typeBuilder._methodDefinitions, genericParams, methodBodyEncoder);
WriteProperties(typeBuilder);
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
WriteEvents(typeBuilder);
}

// Now write all generic parameters in order
Expand All @@ -190,6 +192,44 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder)
}
}

private void WriteEvents(TypeBuilderImpl typeBuilder)
{
if (typeBuilder._eventDefinitions.Count == 0)
{
return;
}

AddEventMap(typeBuilder._handle, typeBuilder._firstEventToken);
foreach (EventBuilderImpl eventBuilder in typeBuilder._eventDefinitions)
{
EventDefinitionHandle eventHandle = AddEventDefinition(eventBuilder, GetTypeHandle(eventBuilder.EventType));
WriteCustomAttributes(eventBuilder._customAttributes, eventHandle);

if (eventBuilder._addOnMethod is MethodBuilderImpl aMb)
{
AddMethodSemantics(eventHandle, MethodSemanticsAttributes.Adder, aMb._handle);
}

if (eventBuilder._raiseMethod is MethodBuilderImpl rMb)
{
AddMethodSemantics(eventHandle, MethodSemanticsAttributes.Raiser, rMb._handle);
}

if (eventBuilder._removeMethod is MethodBuilderImpl remMb)
{
AddMethodSemantics(eventHandle, MethodSemanticsAttributes.Remover, remMb._handle);
}

if (eventBuilder._otherMethods != null)
{
foreach (MethodBuilderImpl method in eventBuilder._otherMethods)
{
AddMethodSemantics(eventHandle, MethodSemanticsAttributes.Other, method._handle);
}
}
}
}

private void WriteProperties(TypeBuilderImpl typeBuilder)
{
if (typeBuilder._propertyDefinitions.Count == 0)
Expand Down Expand Up @@ -252,15 +292,25 @@ private void PopulatePropertyDefinitionHandles(List<PropertyBuilderImpl> propert
}
}

private void PopulateEventDefinitionHandles(List<EventBuilderImpl> eventDefinitions)
{
foreach (EventBuilderImpl eventBuilder in eventDefinitions)
{
eventBuilder._handle = MetadataTokens.EventDefinitionHandle(_nextEventRowId++);
}
}

internal void PopulateTypeAndItsMembersTokens(TypeBuilderImpl typeBuilder)
{
typeBuilder._handle = MetadataTokens.TypeDefinitionHandle(++_nextTypeDefRowId);
typeBuilder._firstMethodToken = _nextMethodDefRowId;
typeBuilder._firstFieldToken = _nextFieldDefRowId;
typeBuilder._firstPropertyToken = _nextPropertyRowId;
typeBuilder._firstEventToken = _nextEventRowId;
PopulateMethodDefinitionHandles(typeBuilder._methodDefinitions);
PopulateFieldDefinitionHandles(typeBuilder._fieldDefinitions);
PopulatePropertyDefinitionHandles(typeBuilder._propertyDefinitions);
PopulateEventDefinitionHandles(typeBuilder._eventDefinitions);
}

private void WriteMethods(List<MethodBuilderImpl> methods, List<GenericTypeParameterBuilderImpl> genericParams, MethodBodyStreamEncoder methodBodyEncoder)
Expand Down Expand Up @@ -528,6 +578,17 @@ private PropertyDefinitionHandle AddPropertyDefinition(PropertyBuilderImpl prope
name: _metadataBuilder.GetOrAddString(property.Name),
signature: _metadataBuilder.GetOrAddBlob(signature));

private EventDefinitionHandle AddEventDefinition(EventBuilderImpl eventBuilder, EntityHandle eventType) =>
_metadataBuilder.AddEvent(
attributes: eventBuilder.Attributes,
name: _metadataBuilder.GetOrAddString(eventBuilder.Name),
type: eventType);

private void AddEventMap(TypeDefinitionHandle typeHandle, int firstEventToken) =>
_metadataBuilder.AddEventMap(
declaringType: typeHandle,
eventList: MetadataTokens.EventDefinitionHandle(firstEventToken));

private void AddPropertyMap(TypeDefinitionHandle typeHandle, int firstPropertyToken) =>
_metadataBuilder.AddPropertyMap(
declaringType: typeHandle,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal sealed class PropertyBuilderImpl : PropertyBuilder
private PropertyAttributes _attributes;
private MethodInfo? _getMethod;
private MethodInfo? _setMethod;
internal List<MethodInfo>? _otherMethods;
internal HashSet<MethodInfo>? _otherMethods;

internal PropertyDefinitionHandle _handle;
internal List<CustomAttributeWrapper>? _customAttributes;
Expand All @@ -43,7 +43,7 @@ protected override void AddOtherMethodCore(MethodBuilder mdBuilder)
ArgumentNullException.ThrowIfNull(mdBuilder);
_containingType.ThrowIfCreated();

_otherMethods ??= new List<MethodInfo>();
_otherMethods ??= new HashSet<MethodInfo>();
_otherMethods.Add(mdBuilder);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ internal sealed class TypeBuilderImpl : TypeBuilder
internal int _firstFieldToken;
internal int _firstMethodToken;
internal int _firstPropertyToken;
internal int _firstEventToken;
internal readonly List<MethodBuilderImpl> _methodDefinitions = new();
internal readonly List<FieldBuilderImpl> _fieldDefinitions = new();
internal readonly List<ConstructorBuilderImpl> _constructorDefinitions = new();
internal List<Type>? _interfaces;
internal readonly List<PropertyBuilderImpl> _propertyDefinitions = new();
internal readonly List<EventBuilderImpl> _eventDefinitions = new();
internal List<CustomAttributeWrapper>? _customAttributes;

internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes,
Expand Down Expand Up @@ -181,7 +183,15 @@ private ConstructorBuilderImpl DefineDefaultConstructorInternal(MethodAttributes
return constBuilder;
}

protected override EventBuilder DefineEventCore(string name, EventAttributes attributes, Type eventtype) => throw new NotImplementedException();
protected override EventBuilder DefineEventCore(string name, EventAttributes attributes, Type eventtype)
{
ArgumentNullException.ThrowIfNull(eventtype);
ThrowIfCreated();

EventBuilderImpl eventBuilder = new EventBuilderImpl(name, attributes, eventtype, this);
_eventDefinitions.Add(eventBuilder);
return eventBuilder;
}

protected override FieldBuilder DefineFieldCore(string fieldName, Type type, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers, FieldAttributes attributes)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.IO;
using System.Linq;
using Xunit;

namespace System.Reflection.Emit.Tests
{
[ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
public class AssemblySaveEventBuilderTests
{
[Fact]
public void DefineEventAndItsAccessors()
{
using (TempFile file = TempFile.Create())
{
AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod);
EventBuilder eventType = type.DefineEvent("TestEvent", EventAttributes.SpecialName, typeof(int));
MethodBuilder addMethod = type.DefineMethod("AddMethod", MethodAttributes.Public | MethodAttributes.SpecialName);
MethodBuilder addMethod2 = type.DefineMethod("AddMethod2", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(int), Type.EmptyTypes);
MethodBuilder raiseMethod = type.DefineMethod("RaiseMethod", MethodAttributes.Assembly | MethodAttributes.SpecialName, typeof(int), Type.EmptyTypes);
MethodBuilder removeMethod = type.DefineMethod("RemoveMethod", MethodAttributes.Public, typeof(void), Type.EmptyTypes);
MethodBuilder otherMethod = type.DefineMethod("OtherMethod", MethodAttributes.Family, typeof(int), [typeof(int)]);
CustomAttributeBuilder customAttrBuilder = new CustomAttributeBuilder(typeof(IntPropertyAttribute).GetConstructor([typeof(int)]), [9]);
eventType.SetCustomAttribute(customAttrBuilder);
addMethod.GetILGenerator().Emit(OpCodes.Ret);
ILGenerator adderIL = addMethod2.GetILGenerator();
adderIL.Emit(OpCodes.Ldc_I4_1);
adderIL.Emit(OpCodes.Ret);
eventType.SetAddOnMethod(addMethod);
eventType.SetAddOnMethod(addMethod2); // last set wins
ILGenerator raiseIL = raiseMethod.GetILGenerator();
raiseIL.Emit(OpCodes.Ldc_I4_2);
raiseIL.Emit(OpCodes.Ret);
eventType.SetRaiseMethod(raiseMethod);
removeMethod.GetILGenerator().Emit(OpCodes.Ret);
eventType.SetRemoveOnMethod(removeMethod);
ILGenerator otherILGenerator = otherMethod.GetILGenerator();
otherILGenerator.Emit(OpCodes.Ldarg_1);
otherILGenerator.Emit(OpCodes.Ret);
eventType.AddOtherMethod(otherMethod);
type.CreateType();
saveMethod.Invoke(ab, new[] { file.Path });

Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path);
Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType");
EventInfo eventFromDisk = typeFromDisk.GetEvent("TestEvent");
Assert.Equal(addMethod2.Name, eventFromDisk.AddMethod.Name);
Assert.Equal(raiseMethod.Name, eventFromDisk.RaiseMethod.Name);
Assert.Equal(removeMethod.Name, eventFromDisk.RemoveMethod.Name);
Assert.Equal(typeof(int).FullName, eventFromDisk.EventHandlerType.FullName);
Assert.NotNull(typeFromDisk.GetMethod("OtherMethod", BindingFlags.NonPublic | BindingFlags.Instance));
Assert.Equal(EventAttributes.SpecialName, eventFromDisk.Attributes);
IList<CustomAttributeData> caData = eventFromDisk.GetCustomAttributesData();
Assert.Equal(1, caData.Count);
Assert.Equal(typeof(IntPropertyAttribute).FullName, caData[0].AttributeType.FullName);
Assert.Equal(1, caData[0].ConstructorArguments.Count);
Assert.Equal(9, caData[0].ConstructorArguments[0].Value);
}
}

[Fact]
public void Set_NullValue_ThrowsArgumentNullException()
{
AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _);
EventBuilder eventBuilder = type.DefineEvent("TestEvent", EventAttributes.None, typeof(string));

AssertExtensions.Throws<ArgumentNullException>("eventtype", () => type.DefineEvent("EventTypeNull", EventAttributes.None, null));
AssertExtensions.Throws<ArgumentNullException>("mdBuilder", () => eventBuilder.SetRaiseMethod(null));
AssertExtensions.Throws<ArgumentNullException>("mdBuilder", () => eventBuilder.SetRemoveOnMethod(null));
AssertExtensions.Throws<ArgumentNullException>("mdBuilder", () => eventBuilder.SetAddOnMethod(null));
AssertExtensions.Throws<ArgumentNullException>("mdBuilder", () => eventBuilder.AddOtherMethod(null));
AssertExtensions.Throws<ArgumentNullException>("customBuilder", () => eventBuilder.SetCustomAttribute(null));
}

[Fact]
public void Set_WhenTypeAlreadyCreated_ThrowsInvalidOperationException()
{
AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _);
EventBuilder eventBuilder = type.DefineEvent("TestEvent", EventAttributes.None, typeof(int));

MethodBuilder method = type.DefineMethod("TestMethod", MethodAttributes.Public | MethodAttributes.SpecialName, typeof(int), null);
CustomAttributeBuilder customAttrBuilder = new CustomAttributeBuilder(typeof(IntPropertyAttribute).GetConstructor([typeof(int)]), [10]);
type.CreateType();

Assert.Throws<InvalidOperationException>(() => eventBuilder.SetAddOnMethod(method));
Assert.Throws<InvalidOperationException>(() => eventBuilder.SetRaiseMethod(method));
Assert.Throws<InvalidOperationException>(() => eventBuilder.SetRemoveOnMethod(method));
Assert.Throws<InvalidOperationException>(() => eventBuilder.AddOtherMethod(method));
Assert.Throws<InvalidOperationException>(() => eventBuilder.SetCustomAttribute(customAttrBuilder));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
<Compile Include="PersistableAssemblyBuilder\AssemblySaveCustomAttributeTests.cs" />
<Compile Include="PersistableAssemblyBuilder\AssemblySaveConstructorBuilderTests.cs" />
<Compile Include="PersistableAssemblyBuilder\AssemblySaveEnumBuilderTests.cs" />
<Compile Include="PersistableAssemblyBuilder\AssemblySaveEventBuilderTests.cs" />
<Compile Include="PersistableAssemblyBuilder\AssemblySaveILGeneratorTests.cs" />
<Compile Include="PersistableAssemblyBuilder\AssemblySavePropertyBuilderTests.cs" />
<Compile Include="PersistableAssemblyBuilder\AssemblySaveTools.cs" />
Expand Down
Loading