From 80922a13eea35e8bcb614f669bf06be0eb36d9f7 Mon Sep 17 00:00:00 2001 From: Esiur Project Date: Sat, 11 Jun 2022 18:50:08 +0300 Subject: [PATCH] Type Annotation --- Esiur/Esiur.csproj | 10 ++--- Esiur/Proxy/TemplateGenerator.cs | 55 +++++++++++++++++++++++++ Esiur/Resource/AnnotationAttribute.cs | 6 ++- Esiur/Resource/Template/TypeTemplate.cs | 52 +++++++++++++++-------- Test/MyResource.cs | 1 + Test/Program.cs | 2 +- 6 files changed, 102 insertions(+), 24 deletions(-) diff --git a/Esiur/Esiur.csproj b/Esiur/Esiur.csproj index 662ed8d..8d4fbad 100644 --- a/Esiur/Esiur.csproj +++ b/Esiur/Esiur.csproj @@ -6,7 +6,7 @@ Ahmed Kh. Zamil http://www.esiur.com true - 2.2.2 + 2.2.3 https://github.com/esiur/esiur-dotnet Ahmed Kh. Zamil @@ -62,21 +62,21 @@ - + - + - + - + diff --git a/Esiur/Proxy/TemplateGenerator.cs b/Esiur/Proxy/TemplateGenerator.cs index ef54093..888de20 100644 --- a/Esiur/Proxy/TemplateGenerator.cs +++ b/Esiur/Proxy/TemplateGenerator.cs @@ -15,6 +15,49 @@ public static class TemplateGenerator { internal static Regex urlRegex = new Regex(@"^(?:([\S]*)://([^/]*)/?)"); + //public static string ToLiteral(string valueTextForCompiler) + //{ + // return SymbolDisplay.FormatLiteral(valueTextForCompiler, false); + //} + + static string ToLiteral(string input) + { + var literal = new StringBuilder(); + literal.Append("\""); + foreach (var c in input) + { + switch (c) + { + case '\"': literal.Append("\\\""); break; + case '\\': literal.Append(@"\\"); break; + case '\0': literal.Append(@"\0"); break; + case '\a': literal.Append(@"\a"); break; + case '\b': literal.Append(@"\b"); break; + case '\f': literal.Append(@"\f"); break; + case '\n': literal.Append(@"\n"); break; + case '\r': literal.Append(@"\r"); break; + case '\t': literal.Append(@"\t"); break; + case '\v': literal.Append(@"\v"); break; + default: + // ASCII printable character + if (c >= 0x20 && c <= 0x7e) + { + literal.Append(c); + // As UTF16 escaped character + } + else + { + literal.Append(@"\u"); + literal.Append(((int)c).ToString("x4")); + } + break; + } + } + literal.Append("\""); + return literal.ToString(); + } + + internal static string GenerateRecord(TypeTemplate template, TypeTemplate[] templates) { var cls = template.ClassName.Split('.'); @@ -22,10 +65,15 @@ public static class TemplateGenerator var nameSpace = string.Join(".", cls.Take(cls.Length - 1)); var className = cls.Last(); + var rt = new StringBuilder(); rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;"); rt.AppendLine($"namespace { nameSpace} {{"); + + if (template.Annotation != null) + rt.AppendLine($"[Annotation({ToLiteral(template.Annotation)})]"); + rt.AppendLine($"[Public] public class {className} : IRecord {{"); @@ -52,6 +100,10 @@ public static class TemplateGenerator rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;"); rt.AppendLine($"namespace { nameSpace} {{"); + + if (template.Annotation != null) + rt.AppendLine($"[Annotation({ToLiteral(template.Annotation)})]"); + rt.AppendLine($"[Public] public enum {className} {{"); rt.AppendLine(String.Join(",\r\n", template.Constants.Select(x => $"{x.Name}={x.Value}"))); @@ -213,6 +265,9 @@ public static class TemplateGenerator rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;"); rt.AppendLine($"namespace { nameSpace} {{"); + if (template.Annotation != null) + rt.AppendLine($"[Annotation({ToLiteral(template.Annotation)})]"); + // extends if (template.ParentId == null) rt.AppendLine($"public class {className} : DistributedResource {{"); diff --git a/Esiur/Resource/AnnotationAttribute.cs b/Esiur/Resource/AnnotationAttribute.cs index 28d477c..2372e8c 100644 --- a/Esiur/Resource/AnnotationAttribute.cs +++ b/Esiur/Resource/AnnotationAttribute.cs @@ -4,7 +4,7 @@ using System.Text; namespace Esiur.Resource; -[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Event)] +[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Event)] public class AnnotationAttribute : Attribute { @@ -13,4 +13,8 @@ public class AnnotationAttribute : Attribute { this.Annotation = annotation; } + public AnnotationAttribute(params string[] annotations) + { + this.Annotation = String.Join("\n", annotations); + } } diff --git a/Esiur/Resource/Template/TypeTemplate.cs b/Esiur/Resource/Template/TypeTemplate.cs index 3d70064..5c4c06f 100644 --- a/Esiur/Resource/Template/TypeTemplate.cs +++ b/Esiur/Resource/Template/TypeTemplate.cs @@ -25,6 +25,8 @@ public class TypeTemplate protected Guid classId; protected Guid? parentId; + public string Annotation { get; set; } + string className; List members = new List(); List functions = new List(); @@ -157,7 +159,6 @@ public class TypeTemplate } - public static Guid GetTypeGuid(Type type) => GetTypeGuid(GetTypeClassName(type)); public static Guid GetTypeGuid(string typeName) @@ -403,6 +404,7 @@ public class TypeTemplate Warehouse.PutTemplate(this); + PropertyInfo[] propsInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);// | BindingFlags.DeclaredOnly); EventInfo[] eventsInfo = type.GetEvents(BindingFlags.Public | BindingFlags.Instance);// | BindingFlags.DeclaredOnly); MethodInfo[] methodsInfo = type.GetMethods(BindingFlags.Public | BindingFlags.Instance); // | BindingFlags.DeclaredOnly); @@ -772,31 +774,37 @@ public class TypeTemplate // find the first parent type that implements IResource + var hasParent = HasParent(type); + var classAnnotation = type.GetCustomAttribute(false); + var hasAnnotation = classAnnotation != null && classAnnotation.Annotation != null; - if (HasParent(type)) + var classNameBytes = DC.ToBytes(className); + + b.AddUInt8((byte)((hasParent ? 0x80 : 0) | (hasAnnotation ? 0x40 : 0x0) | (byte)templateType)) + .AddGuid(classId) + .AddUInt8((byte)classNameBytes.Length) + .AddUInt8Array(classNameBytes); + + if (hasParent) { // find the first parent type that implements IResource var ParentDefinedType = ResourceProxy.GetBaseType(type.BaseType); var parentId = GetTypeGuid(ParentDefinedType); - - b.AddUInt8((byte)(0x80 | (byte)templateType)) - .AddGuid(classId) - .AddUInt8((byte)className.Length) - .AddString(className) - .AddGuid(parentId) - .AddInt32(version) - .AddUInt16((ushort)members.Count); + b.AddGuid(parentId); } - else + + if (hasAnnotation) { - b.AddUInt8((byte)templateType) - .AddGuid(classId) - .AddUInt8((byte)className.Length) - .AddString(className) - .AddInt32(version) - .AddUInt16((ushort)members.Count); + var classAnnotationBytes = DC.ToBytes(classAnnotation.Annotation); + b.AddUInt16((ushort)classAnnotationBytes.Length) + .AddUInt8Array(classAnnotationBytes); + + Annotation = classAnnotation.Annotation; } + b.AddInt32(version) + .AddUInt16((ushort)members.Count); + foreach (var ft in functions) b.AddUInt8Array(ft.Compose()); foreach (var pt in properties) @@ -848,6 +856,8 @@ public class TypeTemplate od.content = data.Clip(offset, contentLength); var hasParent = (data[offset] & 0x80) > 0; + var hasAnnotation = (data[offset] & 0x40) > 0; + od.templateType = (TemplateType)(data[offset++] & 0xF); od.classId = data.GetGuid(offset); @@ -862,6 +872,14 @@ public class TypeTemplate offset += 16; } + if (hasAnnotation) + { + var len = data.GetUInt16(offset, Endian.Little); + offset += 2; + od.Annotation = data.GetString(offset, len); + offset += len; + } + od.version = data.GetInt32(offset, Endian.Little); offset += 4; diff --git a/Test/MyResource.cs b/Test/MyResource.cs index 937e1a1..5b4e5e6 100644 --- a/Test/MyResource.cs +++ b/Test/MyResource.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; namespace Test { [Resource] + [Annotation("A", "B", "C", "D")] public partial class MyResource { [Public] string description; diff --git a/Test/Program.cs b/Test/Program.cs index 6b7ccd4..0507605 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -53,7 +53,7 @@ namespace Test static async Task Main(string[] args) { - + // Create stores to keep objects. var system = await Warehouse.Put("mem", new MemoryStore()); var server = await Warehouse.Put("mem/server", new DistributedServer());