Skip to content

Commit

Permalink
ClrEvent
Browse files Browse the repository at this point in the history
- runtime respects `event` members and provides them as a PHP property with special type "Pchp.Core.ClrEvent<TDelegate>"
- `ClrEvent` has method `add()`
  • Loading branch information
jakubmisek committed May 6, 2024
1 parent 9b9f741 commit 57bab37
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 1 deletion.
104 changes: 104 additions & 0 deletions src/Peachpie.Runtime/ClrEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Pchp.Core.Reflection;

namespace Pchp.Core
{
public class ClrEvent<TDelegate> : IPhpCallable where TDelegate : Delegate
{
sealed class Hook : IDisposable
{
public Hook(object target, EventInfo eventInfo, TDelegate callback)
{
EventInfo = eventInfo ?? throw new ArgumentNullException(nameof(eventInfo));
Target = target ?? throw new ArgumentNullException(nameof(target));
Callback = callback ?? throw new ArgumentNullException(nameof(callback));
}

[PhpHidden]
public EventInfo EventInfo { get; }

[PhpHidden]
public object Target { get; }

[PhpHidden]
public TDelegate Callback { get; }

/// <summary>Alias to <see cref="Dispose"/></summary>
public void Close() => Dispose();

/// <summary>Alias to <see cref="Dispose"/></summary>
public void Remove() => Dispose();

public void Dispose()
{
EventInfo.RemoveEventHandler(Target, Callback);
}
}

[PhpHidden]
public EventInfo EventInfo { get; }

[PhpHidden]
public object Target { get; }

/// <summary>
/// The event field name.
/// </summary>
public string name => EventInfo.Name;

/// <summary>
/// Reference to the owning object instance.
/// </summary>
public string @class => EventInfo.DeclaringType.GetPhpTypeInfo().Name;

internal ClrEvent(Context ctx, object target, EventInfo eventInfo)
{
this.Target = target;
this.EventInfo = eventInfo ?? throw new ArgumentNullException(nameof(eventInfo));
}

public IDisposable/*!*/add(TDelegate callback)
{
if (callback == null)
{
throw new ArgumentNullException(nameof(callback));
}

EventInfo.AddEventHandler(Target, callback);
return new Hook(Target, EventInfo, callback);
}

public void remove(TDelegate callback)
{
EventInfo.RemoveEventHandler(Target, callback);

throw new NotSupportedException();
}

#region IPhpCallable

PhpValue IPhpCallable.Invoke(Context ctx, params PhpValue[] arguments)
{
//TODO: check caller, invoke EventInfo.RaiseMethod?.Invoke(Target, arguments)
throw new NotSupportedException();
}

PhpValue IPhpCallable.ToPhpValue() => PhpValue.FromClr(this);

//public PhpValue __invoke([ImportValue(ImportValueAttribute.ValueSpec.CallerClass)] RuntimeTypeHandle caller, params PhpValue[] arguments)
//{
// if (caller.Equals(default(RuntimeTypeHandle)))
// {
// return false;
// }

// return PhpValue.Null;
//}

#endregion
}
}
82 changes: 82 additions & 0 deletions src/Peachpie.Runtime/Reflection/PhpPropertyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,88 @@ public ClrExplicitProperty(PhpTypeInfo tinfo, PropertyInfo property, string expl

#endregion

#region ClrEvent

internal sealed class ClrEvent : PhpPropertyInfo
{
Type ConstructedClrEventType
{
get
{
return _constructedClrEventType ??= typeof(ClrEvent<>).MakeGenericType(Event.EventHandlerType ?? throw new NotSupportedException());
}
}
Type? _constructedClrEventType;

ConstructorInfo ConstructorInfo
=> ConstructedClrEventType.GetConstructor(BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance, null, new[] { typeof(Context), typeof(object), typeof(EventInfo) }, null)
?? throw new InvalidOperationException()
;

public EventInfo Event { get; }

public override MemberInfo? Member => Event;

public ClrEvent(PhpTypeInfo tinfo, EventInfo @event)
: base(tinfo)
{
Event = @event ?? throw new ArgumentNullException(nameof(@event));
}

public override FieldAttributes Attributes
{
get
{
FieldAttributes attrs = 0;

if (Event.AddMethod != null)
{
attrs = (FieldAttributes)(Event.AddMethod.Attributes & (MethodAttributes.MemberAccessMask | MethodAttributes.Static));
}
else
{
attrs = FieldAttributes.Private;
}

return attrs;
}
}

public override bool IsReadOnly => true;

public override bool IsConstant => false;

public override bool IsRuntimeProperty => false;

public override string PropertyName => Event.Name;

public override Type PropertyType => typeof(PhpValue);

public override PhpValue GetValue(Context ctx, object instance)
{
return PhpValue.FromClass(
Activator.CreateInstance(ConstructedClrEventType, ctx, instance, Event)
);
}

public override void SetValue(Context ctx, object instance, PhpValue value) => throw new NotSupportedException();

public override Expression Bind(Expression ctx, Expression target)
{
// new ClrEvent<TDelegate>( ctx, target, Event )
return Expression.New(
ConstructorInfo,
ctx,
Expression.Convert(target, Cache.Types.Object[0]),
Expression.Constant(Event)
);
}

public override IPhpArray EnsureArray(Context ctx, object instance) => throw new NotSupportedException();
}

#endregion

#region RuntimeProperty

internal sealed class RuntimeProperty : PhpPropertyInfo
Expand Down
10 changes: 9 additions & 1 deletion src/Peachpie.Runtime/Reflection/TypeFields.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

Expand Down Expand Up @@ -76,6 +77,11 @@ internal TypeFields(PhpTypeInfo t)
}
}

foreach (var e in tinfo.DeclaredEvents)
{
properties[e.Name] = new PhpPropertyInfo.ClrEvent(t, e);
}

//
_properties = properties.Count != 0 ? properties : EmptyDictionary<string, PhpPropertyInfo>.Singleton;
}
Expand All @@ -92,7 +98,9 @@ static bool IsAllowedField(FieldInfo f)
!ReflectionUtils.IsRuntimeFields(f) &&
!ReflectionUtils.IsContextField(f) &&
!f.IsPhpHidden() &&
!f.FieldType.IsPointer;
!f.FieldType.IsPointer &&
!(f.IsPrivate && f.GetCustomAttribute<CompilerGeneratedAttribute>() != null)
;
}

static bool IsAllowedProperty(PropertyInfo p)
Expand Down

0 comments on commit 57bab37

Please sign in to comment.