2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2025-05-06 11:32:59 +00:00

DynamicMethod Nullable Events

This commit is contained in:
Ahmed Zamil 2022-04-01 15:47:19 +03:00
parent 20fcaba518
commit 2809d389bd
7 changed files with 293 additions and 234 deletions

View File

@ -0,0 +1,27 @@
namespace System.Runtime.CompilerServices
{
[AttributeUsage(
AttributeTargets.Class |
AttributeTargets.Event |
AttributeTargets.Field |
AttributeTargets.GenericParameter |
AttributeTargets.Parameter |
AttributeTargets.Property |
AttributeTargets.ReturnValue,
AllowMultiple = false,
Inherited = false)]
public sealed class NullableAttribute : Attribute
{
public readonly byte[] Flags;
public readonly byte Flag;
public NullableAttribute(byte flag)
{
Flag = flag;// new byte[] { flag };
}
public NullableAttribute(byte[] flags)
{
Flags = flags;
}
}
}

View File

@ -0,0 +1,19 @@
namespace System.Runtime.CompilerServices
{
[System.AttributeUsage(
AttributeTargets.Class |
AttributeTargets.Delegate |
AttributeTargets.Interface |
AttributeTargets.Method |
AttributeTargets.Struct,
AllowMultiple = false,
Inherited = false)]
public sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte flag)
{
Flag = flag;
}
}
}

View File

@ -89,14 +89,20 @@ namespace Esiur.Data
public RepresentationType?[] SubTypes = new RepresentationType[3];
public static RepresentationType? FromType(Type type)
public static RepresentationType? FromType(Type type, bool forceNullable = false)
{
var nullType = System.Nullable.GetUnderlyingType(type);
var nullable = false;
if (nullType != null) {
type = nullType;
nullable = true;
var nullable = forceNullable;
if (!forceNullable)
{
var nullType = System.Nullable.GetUnderlyingType(type);
if (nullType != null)
{
type = nullType;
nullable = true;
}
}
if (type.IsGenericType)

View File

@ -72,6 +72,7 @@
<PackageReference Include="System.Net.NetworkInformation" Version="4.3.0" />
<PackageReference Include="System.Net.Security" Version="4.3.2" />
<PackageReference Include="System.Reflection.Emit" Version="4.7.0" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
<PackageReference Include="System.Text.Json" Version="6.0.1" GeneratePathProperty="true" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />

View File

@ -15,6 +15,7 @@ using Esiur.Proxy;
using Esiur.Core;
using System.Text.Json;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection.Emit;
namespace Esiur.Resource;
@ -607,6 +608,27 @@ public class Instance
}
}
internal void EmitResourceEventByIndex(byte eventIndex, object value)
{
IResource res;
if (this.resource.TryGetTarget(out res))
{
var eventTemplate = template.GetEventTemplateByIndex(eventIndex);
EventOccurred?.Invoke(new EventOccurredInfo(res, eventTemplate, value));
}
}
internal void EmitCustomResourceEventByIndex(object issuer, Func<Session, bool> receivers, byte eventIndex, object value)
{
IResource res;
if (this.resource.TryGetTarget(out res))
{
var eventTemplate = template.GetEventTemplateByIndex(eventIndex);
CustomEventOccurred?.Invoke(new CustomEventOccurredInfo(res, eventTemplate, receivers, issuer, value));
}
}
/// <summary>
/// Get the value of a given property by name.
/// </summary>
@ -844,6 +866,13 @@ public class Instance
/// </summary>
public AutoList<IPermissionsManager, Instance> Managers => managers;
public void CallMeTest(Instance ins, int? val) =>
ins.EmitResourceEventByIndex(201, val);
public void CallMeTest2(Instance instance, object issuer, Func<Session, bool> receivers, int? val) =>
instance.EmitCustomResourceEventByIndex(issuer, receivers, 201, val);
/// <summary>
/// Create new instance.
/// </summary>
@ -893,10 +922,11 @@ public class Instance
#endif
var emitEventByIndexMethod = GetType().GetMethod("EmitResourceEventByIndex", BindingFlags.Instance | BindingFlags.NonPublic);
var emitCustomEventByIndexMethod = GetType().GetMethod("EmitCustomResourceEventByIndex", BindingFlags.Instance | BindingFlags.NonPublic);
foreach (var evt in template.Events)
{
//if (evt.EventHandlerType != typeof(ResourceEventHandler))
// continue;
if (evt.EventInfo == null)
continue;
@ -906,21 +936,51 @@ public class Instance
if (eventGenericType == typeof(ResourceEventHandler<>))
{
// var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true);
// if (ca.Length == 0)
// continue;
var dm = new DynamicMethod("_", null,
new Type[] {typeof(Instance), evt.EventInfo.EventHandlerType.GenericTypeArguments[0] },
typeof(Instance).Module, true);
ResourceEventHandler<object> proxyDelegate = (args) => EmitResourceEvent(evt, args);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldc_I4, (int)evt.Index);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Box, evt.EventInfo.EventHandlerType.GenericTypeArguments[0]);
il.Emit(OpCodes.Callvirt, emitEventByIndexMethod);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ret);
var proxyDelegate= dm.CreateDelegate(evt.EventInfo.EventHandlerType, this);
//ResourceEventHandler<object> proxyDelegate = new ResourceEventHandler<object>((args) => EmitResourceEvent(evt, args));
evt.EventInfo.AddEventHandler(resource, proxyDelegate);
}
else if (eventGenericType == typeof(CustomResourceEventHandler<>))
{
//var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true);
//if (ca.Length == 0)
// continue;
var dm = new DynamicMethod("_", null,
new Type[] { typeof(Instance), typeof(object), typeof(Func<Session, bool>),
evt.EventInfo.EventHandlerType.GenericTypeArguments[0] },
typeof(Instance).Module, true);
CustomResourceEventHandler<object> proxyDelegate = (issuer, receivers, args) => EmitCustomResourceEvent(issuer, receivers, evt, args);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Ldc_I4, (int)evt.Index);
il.Emit(OpCodes.Ldarg_3);
il.Emit(OpCodes.Box, evt.EventInfo.EventHandlerType.GenericTypeArguments[0]);
il.Emit(OpCodes.Callvirt, emitCustomEventByIndexMethod);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ret);
var proxyDelegate = dm.CreateDelegate(evt.EventInfo.EventHandlerType, this);
//CustomResourceEventHandler<object> proxyDelegate = (issuer, receivers, args) => EmitCustomResourceEvent(issuer, receivers, evt, args);
evt.EventInfo.AddEventHandler(resource, proxyDelegate);
}

View File

@ -33,9 +33,9 @@ using System.Threading.Tasks;
namespace Esiur.Resource;
public delegate R DCovariant<out R>();
//public delegate R DCovariant<out R>();
public delegate void ResourceEventHandler<in T>(T argument);
public delegate void ResourceEventHandler<in T>(T argument);//where T : class;
// public delegate void CustomUsersEventHanlder(string[] usernames, params object[] args);
//public delegate void CustomReceiversEventHanlder(DistributedConnection[] connections, params object[] args);
//public delegate void CustomInquirerEventHanlder(object inquirer, params object[] args);

View File

@ -9,6 +9,7 @@ using Esiur.Core;
using System.Security.Cryptography;
using Esiur.Proxy;
using Esiur.Net.IIP;
using System.Runtime.CompilerServices;
namespace Esiur.Resource.Template;
@ -393,9 +394,150 @@ public class TypeTemplate
bool classIsPublic = type.IsEnum || (type.GetCustomAttribute<PublicAttribute>() != null);
var addConstant = (FieldInfo ci, PublicAttribute publicAttr) => {
var annotationAttr = ci.GetCustomAttribute<AnnotationAttribute>(true);
var nullableAttr = ci.GetCustomAttribute<NullableAttribute>(true);
var valueType = RepresentationType.FromType(ci.FieldType);//, nullable != null && nullable.NullableFlags[0] == 2);
if (valueType == null)
throw new Exception($"Unsupported type `{ci.FieldType}` in constant `{type.Name}.{ci.Name}`");
var value = ci.GetValue(null);
if (templateType == TemplateType.Enum)
value = Convert.ChangeType(value, ci.FieldType.GetEnumUnderlyingType());
var ct = new ConstantTemplate(this, (byte)constants.Count, publicAttr?.Name ?? ci.Name, ci.DeclaringType != type, valueType, value, annotationAttr?.Annotation);
constants.Add(ct);
};
var addProperty = (PropertyInfo pi, PublicAttribute publicAttr) =>
{
var annotationAttr = pi.GetCustomAttribute<AnnotationAttribute>(true);
var storageAttr = pi.GetCustomAttribute<StorageAttribute>(true);
var nullableAttr = pi.GetCustomAttribute<NullableAttribute>(true);
var attrType = RepresentationType.FromType(pi.PropertyType, nullableAttr != null && nullableAttr.Flag == 2);
if (attrType == null)
throw new Exception($"Unsupported type `{pi.PropertyType}` in property `{type.Name}.{pi.Name}`");
var pt = new PropertyTemplate(this, (byte)properties.Count, publicAttr?.Name ?? pi.Name, pi.DeclaringType != type, attrType);
if (storageAttr != null)
pt.Recordable = storageAttr.Mode == StorageMode.Recordable;
if (annotationAttr != null)
pt.ReadExpansion = annotationAttr.Annotation;
else
pt.ReadExpansion = GetTypeAnnotationName(pi.PropertyType);
pt.PropertyInfo = pi;
properties.Add(pt);
};
var addEvent = (EventInfo ei, PublicAttribute publicAttr) =>
{
var annotationAttr = ei.GetCustomAttribute<AnnotationAttribute>(true);
var listenableAttr = ei.GetCustomAttribute<ListenableAttribute>(true);
var nullableAttr = ei.GetCustomAttribute<NullableAttribute>(true);
var argIsNull = nullableAttr != null &&
nullableAttr.Flags != null &&
nullableAttr.Flags.Length > 1 &&
nullableAttr.Flags[1] == 2;
var argType = ei.EventHandlerType.GenericTypeArguments[0];
var evtType = RepresentationType.FromType(argType, argIsNull);
if (evtType == null)
throw new Exception($"Unsupported type `{argType}` in event `{type.Name}.{ei.Name}`");
var et = new EventTemplate(this, (byte)events.Count, publicAttr?.Name ?? ei.Name, ei.DeclaringType != type, evtType);
et.EventInfo = ei;
if (annotationAttr != null)
et.Expansion = annotationAttr.Annotation;
if (listenableAttr != null)
et.Listenable = true;
events.Add(et);
};
var addAttribute = (PropertyInfo pi, AttributeAttribute attributeAttr)=>
{
var an = attributeAttr.Name ?? pi.Name;
var at = new AttributeTemplate(this, 0, an, pi.DeclaringType != type);
at.PropertyInfo = pi;
attributes.Add(at);
};
var addFunction = (MethodInfo mi, PublicAttribute publicAttr) =>
{
var annotationAttr = mi.GetCustomAttribute<AnnotationAttribute>(true);
var nullableAttr = mi.GetCustomAttribute<NullableAttribute>(true);
var nullableContextAttr = mi.GetCustomAttribute<NullableContextAttribute>(true);
var contextIsNull = nullableContextAttr != null && nullableContextAttr.Flag == 2;
var returnType = RepresentationType.FromType(mi.ReturnType,
nullableAttr != null ? nullableAttr.Flag == 2 : contextIsNull);
if (returnType == null)
throw new Exception($"Unsupported type `{mi.ReturnType}` in method `{type.Name}.{mi.Name}` return");
var args = mi.GetParameters();
if (args.Length > 0)
{
if (args.Last().ParameterType == typeof(DistributedConnection))
args = args.Take(args.Count() - 1).ToArray();
}
var arguments = args.Select(x =>
{
var xNullableAttr = x.GetCustomAttribute<NullableAttribute>(true);
var xNullableContextAttr = x.GetCustomAttribute<NullableContextAttribute>(true);
var argType = RepresentationType.FromType(x.ParameterType,
xNullableAttr != null ? xNullableAttr.Flag == 2 : contextIsNull);
if (argType == null)
throw new Exception($"Unsupported type `{x.ParameterType}` in method `{type.Name}.{mi.Name}` parameter `{x.Name}`");
return new ArgumentTemplate()
{
Name = x.Name,
Type = argType,
ParameterInfo = x,
Optional = x.IsOptional
};
})
.ToArray();
var fn = publicAttr.Name ?? mi.Name;
var ft = new FunctionTemplate(this, (byte)functions.Count, fn, mi.DeclaringType != type, arguments, returnType);// mi.ReturnType == typeof(void));
if (annotationAttr != null)
ft.Expansion = annotationAttr.Annotation;
else
ft.Expansion = "(" + String.Join(",", mi.GetParameters().Where(x => x.ParameterType != typeof(DistributedConnection)).Select(x => "[" + x.ParameterType.Name + "] " + x.Name)) + ") -> " + mi.ReturnType.Name;
ft.MethodInfo = mi;
functions.Add(ft);
};
byte i = 0;
if (classIsPublic)
{
@ -403,30 +545,15 @@ public class TypeTemplate
foreach (var ci in constantsInfo)
{
var privateAttr = ci.GetCustomAttribute<PrivateAttribute>(true);
var annotationAttr = ci.GetCustomAttribute<AnnotationAttribute>(true);
if (privateAttr != null)
continue;
var publicAttr = ci.GetCustomAttribute<PublicAttribute>(true);
var valueType = RepresentationType.FromType(ci.FieldType);
if (valueType == null)
throw new Exception($"Unsupported type `{ci.FieldType}` in constant `{type.Name}.{ci.Name}`");
var value = ci.GetValue(null);
if (templateType == TemplateType.Enum)
value = Convert.ChangeType(value, ci.FieldType.GetEnumUnderlyingType());
var ct = new ConstantTemplate(this, i++, ci.Name, ci.DeclaringType != type, valueType, value, annotationAttr?.Annotation);
constants.Add(ct);
addConstant(ci, publicAttr);
}
i = 0;
foreach (var pi in propsInfo)
{
@ -434,117 +561,41 @@ public class TypeTemplate
if (privateAttr == null)
{
var annotationAttr = pi.GetCustomAttribute<AnnotationAttribute>(true);
var storageAttr = pi.GetCustomAttribute<StorageAttribute>(true);
var attrType = RepresentationType.FromType(pi.PropertyType);
if (attrType == null)
throw new Exception($"Unsupported type `{pi.PropertyType}` in property `{type.Name}.{pi.Name}`");
var pt = new PropertyTemplate(this, i++, pi.Name, pi.DeclaringType != type, attrType);
if (storageAttr != null)
pt.Recordable = storageAttr.Mode == StorageMode.Recordable;
if (annotationAttr != null)
pt.ReadExpansion = annotationAttr.Annotation;
else
pt.ReadExpansion = GetTypeAnnotationName(pi.PropertyType);
pt.PropertyInfo = pi;
//pt.Serilize = publicAttr.Serialize;
properties.Add(pt);
var publicAttr = pi.GetCustomAttribute<PublicAttribute>(true);
addProperty(pi, publicAttr);
}
else
{
var attributeAttr = pi.GetCustomAttribute<AttributeAttribute>(true);
if (attributeAttr != null)
{
var an = attributeAttr.Name ?? pi.Name;
var at = new AttributeTemplate(this, 0, an, pi.DeclaringType != type);
at.PropertyInfo = pi;
attributes.Add(at);
addAttribute(pi, attributeAttr);
}
}
}
if (templateType == TemplateType.Resource)
{
i = 0;
foreach (var ei in eventsInfo)
{
var privateAttr = ei.GetCustomAttribute<PrivateAttribute>(true);
if (privateAttr != null)
continue;
var annotationAttr = ei.GetCustomAttribute<AnnotationAttribute>(true);
var listenableAttr = ei.GetCustomAttribute<ListenableAttribute>(true);
var publicAttr = ei.GetCustomAttribute<PublicAttribute>(true);
var argType = ei.EventHandlerType.GenericTypeArguments[0];
var evtType = RepresentationType.FromType(argType);
if (evtType == null)
throw new Exception($"Unsupported type `{argType}` in event `{type.Name}.{ei.Name}`");
var et = new EventTemplate(this, i++, ei.Name, ei.DeclaringType != type, evtType);
et.EventInfo = ei;
if (annotationAttr != null)
et.Expansion = annotationAttr.Annotation;
if (listenableAttr != null)
et.Listenable = true;
events.Add(et);
addEvent(ei, publicAttr);
}
i = 0;
foreach (MethodInfo mi in methodsInfo)
{
var privateAttr = mi.GetCustomAttribute<PrivateAttribute>(true);
if (privateAttr != null)
continue;
var annotationAttr = mi.GetCustomAttribute<AnnotationAttribute>(true);
var returnType = RepresentationType.FromType(mi.ReturnType);
if (returnType == null)
throw new Exception($"Unsupported type {mi.ReturnType} in method {type.Name}.{mi.Name} return");
var args = mi.GetParameters();
if (args.Length > 0)
{
if (args.Last().ParameterType == typeof(DistributedConnection))
args = args.Take(args.Count() - 1).ToArray();
}
var arguments = args.Select(x =>
{
var argType = RepresentationType.FromType(x.ParameterType);
if (argType == null)
throw new Exception($"Unsupported type `{x.ParameterType}` in method `{type.Name}.{mi.Name}` parameter `{x.Name}`");
return new ArgumentTemplate()
{
Name = x.Name,
Type = argType,
ParameterInfo = x,
Optional = x.IsOptional
};
}).ToArray();
var ft = new FunctionTemplate(this, i++, mi.Name, mi.DeclaringType != type, arguments, returnType);// mi.ReturnType == typeof(void));
if (annotationAttr != null)
ft.Expansion = annotationAttr.Annotation;
else
ft.Expansion = "(" + String.Join(",", mi.GetParameters().Where(x => x.ParameterType != typeof(DistributedConnection)).Select(x => "[" + x.ParameterType.Name + "] " + x.Name)) + ") -> " + mi.ReturnType.Name;
ft.MethodInfo = mi;
functions.Add(ft);
var publicAttr = mi.GetCustomAttribute<PublicAttribute>(true);
addFunction(mi, publicAttr);
}
}
@ -554,27 +605,13 @@ public class TypeTemplate
foreach (var ci in constantsInfo)
{
var publicAttr = ci.GetCustomAttribute<PublicAttribute>(true);
var annotationAttr = ci.GetCustomAttribute<AnnotationAttribute>(true);
if (publicAttr == null)
continue;
var valueType = RepresentationType.FromType(ci.FieldType);
if (valueType == null)
throw new Exception($"Unsupported type `{ci.FieldType}` in constant `{type.Name}.{ci.Name}`");
var value = ci.GetValue(null);
var ct = new ConstantTemplate(this, i++, ci.Name, ci.DeclaringType != type, valueType, value, annotationAttr?.Annotation);
constants.Add(ct);
addConstant(ci, publicAttr);
}
i = 0;
foreach (var pi in propsInfo)
{
@ -582,45 +619,20 @@ public class TypeTemplate
if (publicAttr != null)
{
var annotationAttr = pi.GetCustomAttribute<AnnotationAttribute>(true);
var storageAttr = pi.GetCustomAttribute<StorageAttribute>(true);
var valueType = RepresentationType.FromType(pi.PropertyType);
if (valueType == null)
throw new Exception($"Unsupported type `{pi.PropertyType}` in property `{type.Name}.{pi.Name}`");
var pn = publicAttr.Name ?? pi.Name;
var pt = new PropertyTemplate(this, i++, pn, pi.DeclaringType != type, valueType);//, rp.ReadExpansion, rp.WriteExpansion, rp.Storage);
if (storageAttr != null)
pt.Recordable = storageAttr.Mode == StorageMode.Recordable;
if (annotationAttr != null)
pt.ReadExpansion = annotationAttr.Annotation;
else
pt.ReadExpansion = GetTypeAnnotationName(pi.PropertyType);
pt.PropertyInfo = pi;
//pt.Serilize = publicAttr.Serialize;
properties.Add(pt);
addProperty(pi, publicAttr);
}
else
{
var attributeAttr = pi.GetCustomAttribute<AttributeAttribute>(true);
if (attributeAttr != null)
{
var pn = attributeAttr.Name ?? pi.Name;
var at = new AttributeTemplate(this, 0, pn, pi.DeclaringType != type);
at.PropertyInfo = pi;
attributes.Add(at);
addAttribute(pi, attributeAttr);
}
}
}
if (templateType == TemplateType.Resource)
{
i = 0;
foreach (var ei in eventsInfo)
{
@ -629,97 +641,32 @@ public class TypeTemplate
if (publicAttr == null)
continue;
var annotationAttr = ei.GetCustomAttribute<AnnotationAttribute>(true);
var listenableAttr = ei.GetCustomAttribute<ListenableAttribute>(true);
var argType = ei.EventHandlerType.GenericTypeArguments[0];
var en = publicAttr.Name ?? ei.Name;
var evtType = RepresentationType.FromType(argType);
if (evtType == null)
throw new Exception($"Unsupported type `{argType}` in event `{type.Name}.{ei.Name}`");
var et = new EventTemplate(this, i++, en, ei.DeclaringType != type, evtType);
et.EventInfo = ei;
if (annotationAttr != null)
et.Expansion = annotationAttr.Annotation;
if (listenableAttr != null)
et.Listenable = true;
events.Add(et);
addEvent(ei, publicAttr);
}
i = 0;
foreach (MethodInfo mi in methodsInfo)
{
var publicAttr = mi.GetCustomAttribute<PublicAttribute>(true);
if (publicAttr == null)
continue;
var annotationAttr = mi.GetCustomAttribute<AnnotationAttribute>(true);
var returnType = RepresentationType.FromType(mi.ReturnType);
if (returnType == null)
throw new Exception($"Unsupported type `{mi.ReturnType}` in method `{type.Name}.{mi.Name}` return");
var args = mi.GetParameters();
if (args.Length > 0)
{
if (args.Last().ParameterType == typeof(DistributedConnection))
args = args.Take(args.Count() - 1).ToArray();
}
var arguments = args.Select(x =>
{
var argType = RepresentationType.FromType(x.ParameterType);
if (argType == null)
throw new Exception($"Unsupported type `{x.ParameterType}` in method `{type.Name}.{mi.Name}` parameter `{x.Name}`");
return new ArgumentTemplate()
{
Name = x.Name,
Type = argType,
ParameterInfo = x,
Optional = x.IsOptional
};
})
.ToArray();
var fn = publicAttr.Name ?? mi.Name;
var ft = new FunctionTemplate(this, i++, fn, mi.DeclaringType != type, arguments, returnType);// mi.ReturnType == typeof(void));
if (annotationAttr != null)
ft.Expansion = annotationAttr.Annotation;
else
ft.Expansion = "(" + String.Join(",", mi.GetParameters().Where(x => x.ParameterType != typeof(DistributedConnection)).Select(x => "[" + x.ParameterType.Name + "] " + x.Name)) + ") -> " + mi.ReturnType.Name;
ft.MethodInfo = mi;
functions.Add(ft);
addFunction(mi, publicAttr);
}
}
}
// append signals
for (i = 0; i < events.Count; i++)
for (var i = 0; i < events.Count; i++)
members.Add(events[i]);
// append slots
for (i = 0; i < functions.Count; i++)
for (var i = 0; i < functions.Count; i++)
members.Add(functions[i]);
// append properties
for (i = 0; i < properties.Count; i++)
for (var i = 0; i < properties.Count; i++)
members.Add(properties[i]);
// append constants
for (i = 0; i < constants.Count; i++)
for (var i = 0; i < constants.Count; i++)
members.Add(constants[i]);
// bake it binarily
@ -729,7 +676,6 @@ public class TypeTemplate
if (HasParent(type))
{
// find the first parent type that implements IResource