diff --git a/Esiur/Data/NullableAttribute.cs b/Esiur/Data/NullableAttribute.cs new file mode 100644 index 0000000..ac8fde7 --- /dev/null +++ b/Esiur/Data/NullableAttribute.cs @@ -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; + } + } +} diff --git a/Esiur/Data/NullableContextAttribute.cs b/Esiur/Data/NullableContextAttribute.cs new file mode 100644 index 0000000..2a6b51c --- /dev/null +++ b/Esiur/Data/NullableContextAttribute.cs @@ -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; + } + } +} diff --git a/Esiur/Data/RepresentationType.cs b/Esiur/Data/RepresentationType.cs index c1fb14e..0d355ad 100644 --- a/Esiur/Data/RepresentationType.cs +++ b/Esiur/Data/RepresentationType.cs @@ -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) diff --git a/Esiur/Esiur.csproj b/Esiur/Esiur.csproj index dd27022..d91b6ce 100644 --- a/Esiur/Esiur.csproj +++ b/Esiur/Esiur.csproj @@ -72,6 +72,7 @@ + diff --git a/Esiur/Resource/Instance.cs b/Esiur/Resource/Instance.cs index 7c240c1..b450f44 100644 --- a/Esiur/Resource/Instance.cs +++ b/Esiur/Resource/Instance.cs @@ -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 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)); + } + } + + /// /// Get the value of a given property by name. /// @@ -844,6 +866,13 @@ public class Instance /// public AutoList Managers => managers; + + public void CallMeTest(Instance ins, int? val) => + ins.EmitResourceEventByIndex(201, val); + + public void CallMeTest2(Instance instance, object issuer, Func receivers, int? val) => + instance.EmitCustomResourceEventByIndex(issuer, receivers, 201, val); + /// /// Create new instance. /// @@ -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 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 proxyDelegate = new ResourceEventHandler((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), + evt.EventInfo.EventHandlerType.GenericTypeArguments[0] }, + typeof(Instance).Module, true); - CustomResourceEventHandler 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 proxyDelegate = (issuer, receivers, args) => EmitCustomResourceEvent(issuer, receivers, evt, args); evt.EventInfo.AddEventHandler(resource, proxyDelegate); } diff --git a/Esiur/Resource/ResourceEventHandler.cs b/Esiur/Resource/ResourceEventHandler.cs index b579dd4..c537f50 100644 --- a/Esiur/Resource/ResourceEventHandler.cs +++ b/Esiur/Resource/ResourceEventHandler.cs @@ -33,9 +33,9 @@ using System.Threading.Tasks; namespace Esiur.Resource; -public delegate R DCovariant(); +//public delegate R DCovariant(); -public delegate void ResourceEventHandler(T argument); +public delegate void ResourceEventHandler(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); diff --git a/Esiur/Resource/Template/TypeTemplate.cs b/Esiur/Resource/Template/TypeTemplate.cs index 961dc77..5ca9e73 100644 --- a/Esiur/Resource/Template/TypeTemplate.cs +++ b/Esiur/Resource/Template/TypeTemplate.cs @@ -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() != null); + var addConstant = (FieldInfo ci, PublicAttribute publicAttr) => { + + var annotationAttr = ci.GetCustomAttribute(true); + var nullableAttr = ci.GetCustomAttribute(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(true); + var storageAttr = pi.GetCustomAttribute(true); + var nullableAttr = pi.GetCustomAttribute(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(true); + var listenableAttr = ei.GetCustomAttribute(true); + var nullableAttr = ei.GetCustomAttribute(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(true); + var nullableAttr = mi.GetCustomAttribute(true); + var nullableContextAttr = mi.GetCustomAttribute(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(true); + var xNullableContextAttr = x.GetCustomAttribute(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(true); - var annotationAttr = ci.GetCustomAttribute(true); if (privateAttr != null) continue; + var publicAttr = ci.GetCustomAttribute(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(true); - var storageAttr = pi.GetCustomAttribute(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(true); + addProperty(pi, publicAttr); } else { var attributeAttr = pi.GetCustomAttribute(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(true); if (privateAttr != null) continue; - var annotationAttr = ei.GetCustomAttribute(true); - var listenableAttr = ei.GetCustomAttribute(true); + var publicAttr = ei.GetCustomAttribute(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(true); if (privateAttr != null) continue; - var annotationAttr = mi.GetCustomAttribute(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(true); + addFunction(mi, publicAttr); } } @@ -554,27 +605,13 @@ public class TypeTemplate foreach (var ci in constantsInfo) { var publicAttr = ci.GetCustomAttribute(true); - var annotationAttr = ci.GetCustomAttribute(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(true); - var storageAttr = pi.GetCustomAttribute(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(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(true); - var listenableAttr = ei.GetCustomAttribute(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(true); if (publicAttr == null) continue; - - var annotationAttr = mi.GetCustomAttribute(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