2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2026-03-31 18:38:22 +00:00
This commit is contained in:
2026-03-17 19:10:20 +03:00
parent 3f7e3992c1
commit 8fc07d6350
50 changed files with 443 additions and 434 deletions

View File

@@ -0,0 +1,99 @@
using Esiur.Data;
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace Esiur.Data.Types;
public class ArgumentDef
{
public string Name { get; set; }
public bool Optional { get; set; }
public TRU Type { get; set; }
public ParameterInfo ParameterInfo { get; set; }
public int Index { get; set; }
public Map<string, string> Annotations { get; set; }
public static (uint, ArgumentDef) Parse(byte[] data, uint offset, int index)
{
var optional = (data[offset] & 0x1) == 0x1;
var hasAnnotations = (data[offset++] & 0x2) == 0x2;
var cs = (uint)data[offset++];
var name = data.GetString(offset, cs);
offset += cs;
var (size, type) = TRU.Parse(data, offset);
offset += size;
Map<string, string> annotations = null;
if (hasAnnotations)
{
//var acs = data.GetUInt32(offset, Endian.Little);
//offset += 2;
var (l, a) = Codec.ParseSync(data, offset, null);
// for saftey, Map<string, string> might change in the future
if (a is Map<string, string> ann)
annotations = ann;
cs += l;
}
return (cs + 2 + size, new ArgumentDef()
{
Name = name,
Index = index,
Type = type,
Optional = optional,
Annotations = annotations
});
}
public ArgumentDef()
{
}
public override string ToString()
{
if (Optional)
return $"[{Name}: {Type}]";
else
return $"{Name}: {Type} ";
}
public byte[] Compose()
{
var name = DC.ToBytes(Name);
if (Annotations == null)
{
return new BinaryList()
.AddUInt8(Optional ? (byte)1 : (byte)0)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(Type.Compose())
.ToArray();
}
else
{
var exp = Codec.Compose(Annotations, null, null);
return new BinaryList()
.AddUInt8((byte)(0x2 | (Optional ? 1 : 0)))
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(Type.Compose())
.AddUInt8Array(exp)
.ToArray();
}
}
}

View File

@@ -0,0 +1,32 @@
using Esiur.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Data.Types;
public class AttributeDef : MemberDef
{
public PropertyInfo PropertyInfo
{
get;
set;
}
public static AttributeDef MakeAttributeDef(Type type, PropertyInfo pi, byte index, string name, TypeDef typeDef)
{
return new AttributeDef()
{
Index = index,
Inherited = pi.DeclaringType != type,
Name = name,
PropertyInfo = pi,
Definition = typeDef
};
}
}

View File

@@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using Esiur.Data;
using Esiur.Resource;
namespace Esiur.Data.Types;
public class ConstantDef : MemberDef
{
public object Value { get; set; }
public Map<string, string> Annotations { get; set; }
public TRU ValueType { get; set; }
public FieldInfo FieldInfo { get; set; }
public static (uint, ConstantDef) Parse(byte[] data, uint offset, byte index, bool inherited)
{
var oOffset = offset;
var hasAnnotation = ((data[offset++] & 0x10) == 0x10);
var name = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
var (dts, valueType) = TRU.Parse(data, offset);
offset += dts;
(dts, var value) = Codec.ParseSync(data, offset, Warehouse.Default);
offset += dts;
Map<string, string> annotations = null;
// arguments
if (hasAnnotation) // Annotation ?
{
var (len, anns) = Codec.ParseSync(data, offset, null);
if (anns is Map<string, string> map)
annotations = map;
offset += len;
}
return (offset - oOffset, new ConstantDef()
{
Index = index,
Name = name,
Inherited = inherited,
ValueType = valueType,
Value = value,
Annotations = annotations
});
}
public byte[] Compose()
{
var name = DC.ToBytes(Name);
var hdr = Inherited ? (byte)0x80 : (byte)0;
if (Annotations != null)
{
var exp = Codec.Compose(Annotations, null, null);// DC.ToBytes(Annotation);
hdr |= 0x70;
return new BinaryList()
.AddUInt8(hdr)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ValueType.Compose())
.AddUInt8Array(Codec.Compose(Value, null, null))
.AddInt32(exp.Length)
.AddUInt8Array(exp)
.ToArray();
}
else
{
hdr |= 0x60;
return new BinaryList()
.AddUInt8(hdr)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ValueType.Compose())
.AddUInt8Array(Codec.Compose(Value, null, null))
.ToArray();
}
}
public static ConstantDef MakeConstantDef(Type type, FieldInfo ci, byte index = 0, string customName = null, TypeDef typeDef = null)
{
var annotationAttrs = ci.GetCustomAttributes<AnnotationAttribute>(true);
var valueType = TRU.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 (typeDef?.Type == TypeDefKind.Enum)
value = Convert.ChangeType(value, ci.FieldType.GetEnumUnderlyingType());
Map<string, string> annotations = null;
if (annotationAttrs != null && annotationAttrs.Count() > 0)
{
annotations = new Map<string, string>();
foreach (var attr in annotationAttrs)
annotations.Add(attr.Key, attr.Value);
}
return new ConstantDef()
{
Name = customName,
Index = index,
Inherited = ci.DeclaringType != type,
ValueType = valueType,
Value = value,
FieldInfo = ci,
Annotations = annotations,
};
}
}

View File

@@ -0,0 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data.Types;
internal class CustomEventOccurredEvent
{
}

View File

@@ -0,0 +1,131 @@
//using Esiur.Data;
//using System;
//using System.Collections;
//using System.Collections.Generic;
//using System.Dynamic;
//using System.Linq;
//using System.Text;
//namespace Esiur.Resource.Template;
//public struct TemplateDataType
//{
// public DataType Type { get; set; }
// //public string TypeName { get; set; }
// public TypeTemplate TypeTemplate => TypeGuid == null ? null : Warehouse.GetTemplateByClassId((Guid)TypeGuid);
// public Guid? TypeGuid { get; set; }
// public bool IsNullable { get; set; }
// //public TemplateDataType(DataType type, string typeName)
// //{
// // Type = type;
// // TypeName = typeName;
// //}
// public static TemplateDataType FromType(Type type)
// {
// bool isList = typeof(ICollection).IsAssignableFrom(type);
// var t = type switch
// {
// { IsArray: true } => type.GetElementType(),
// { IsEnum: true } => type.GetEnumUnderlyingType(),
// _ when isList => Codec.GetGenericListType(type),
// (_) => type
// };
// DataType dt = t switch
// {
// _ when t == typeof(bool) => DataType.Bool,
// _ when t == typeof(char) => DataType.Char,
// _ when t == typeof(byte) => DataType.UInt8,
// _ when t == typeof(sbyte) => DataType.Int8,
// _ when t == typeof(short) => DataType.Int16,
// _ when t == typeof(ushort) => DataType.UInt16,
// _ when t == typeof(int) => DataType.Int32,
// _ when t == typeof(uint) => DataType.UInt32,
// _ when t == typeof(long) => DataType.Int64,
// _ when t == typeof(ulong) => DataType.UInt64,
// _ when t == typeof(float) => DataType.Float32,
// _ when t == typeof(double) => DataType.Float64,
// _ when t == typeof(decimal) => DataType.Decimal,
// _ when t == typeof(string) => DataType.String,
// _ when t == typeof(DateTime) => DataType.DateTime,
// _ when t == typeof(IResource) => DataType.Void, // Dynamic resource (unspecified type)
// _ when t == typeof(IRecord) => DataType.Void, // Dynamic record (unspecified type)
// _ when typeof(Structure).IsAssignableFrom(t) || t == typeof(ExpandoObject) => DataType.Structure,
// _ when Codec.ImplementsInterface(t, typeof(IResource)) => DataType.Resource,
// _ when Codec.ImplementsInterface(t, typeof(IRecord)) => DataType.Record,
// _ => DataType.Void
// };
// Guid? typeGuid = null;
// if (dt == DataType.Resource || dt == DataType.Record)
// typeGuid = TypeTemplate.GetTypeGuid(t);
// if (type.IsArray || isList)
// dt = (DataType)((byte)dt | 0x80);
// return new TemplateDataType()
// {
// Type = dt,
// TypeGuid = typeGuid,
// IsNullable = Nullable.GetUnderlyingType(type) != null
// };
// }
// public byte[] Compose()
// {
// if (Type == DataType.Resource ||
// Type == DataType.ResourceArray ||
// Type == DataType.Record ||
// Type == DataType.RecordArray)
// {
// var guid = DC.ToBytes((Guid)TypeGuid);
// if (IsNullable)
// {
// return new BinaryList()
// .AddUInt8((byte)((byte)Type | 0x40))
// .AddUInt8Array(guid).ToArray();
// } else
// {
// return new BinaryList()
// .AddUInt8((byte)Type)
// .AddUInt8Array(guid).ToArray();
// }
// }
// else if (IsNullable)
// return new byte[] { (byte)((byte)Type | 0x40) };
// else
// return new byte[] { (byte)Type };
// }
// public override string ToString() => Type.ToString() + (IsNullable ? "?":"" )
// + TypeTemplate != null ? "<" + TypeTemplate.ClassName + ">" : "";
// public static (uint, TemplateDataType) Parse(byte[] data, uint offset)
// {
// bool isNullable = (data[offset] & 0x40) > 0;
// var type = (DataType)(data[offset++] & 0xBF);
// if (type == DataType.Resource ||
// type == DataType.ResourceArray ||
// type == DataType.Record ||
// type == DataType.RecordArray)
// {
// var guid = data.GetGuid(offset);
// return (17, new TemplateDataType() { Type = type, TypeGuid = guid , IsNullable = isNullable});
// }
// else
// return (1, new TemplateDataType() { Type = type, IsNullable = isNullable });
// }
//}

View File

@@ -0,0 +1,187 @@
using Esiur.Core;
using Esiur.Data;
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Data.Types;
public class EventDef : MemberDef
{
public Map<string, string> Annotations
{
get;
set;
}
public override string ToString()
{
return $"{Name}: {ArgumentType}";
}
public bool Subscribable { get; set; }
public EventInfo EventInfo { get; set; }
public TRU ArgumentType { get; set; }
public static (uint, EventDef) Parse(byte[] data, uint offset, byte index, bool inherited)
{
var oOffset = offset;
var hasAnnotation = ((data[offset] & 0x10) == 0x10);
var subscribable = ((data[offset++] & 0x8) == 0x8);
var name = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
var (dts, argType) = TRU.Parse(data, offset);
offset += dts;
// Annotation ?
Map<string, string> annotations = null;
if (hasAnnotation)
{
var (len, anns) = Codec.ParseSync(data, offset, null);
if (anns is Map<string, string> map)
annotations = map;
offset += len;
}
return (offset - oOffset, new EventDef()
{
Index = index,
Name = name,
Inherited = inherited,
ArgumentType = argType,
Subscribable = subscribable,
Annotations = annotations
});
}
public byte[] Compose()
{
var name = Name.ToBytes();
var hdr = Inherited ? (byte)0x80 : (byte)0;
if (Subscribable)
hdr |= 0x8;
if (Annotations != null)
{
var exp = Codec.Compose(Annotations, null, null); //( DC.ToBytes(Annotation);
hdr |= 0x50;
return new BinaryList()
.AddUInt8(hdr)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ArgumentType.Compose())
.AddInt32(exp.Length)
.AddUInt8Array(exp)
.ToArray();
}
else
hdr |= 0x40;
return new BinaryList()
.AddUInt8(hdr)
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ArgumentType.Compose())
.ToArray();
}
//public EventTemplate(TypeSchema template, byte index, string name, bool inherited, TRU argumentType, Map<string, string> annotations = null, bool subscribable = false)
// : base(template, index, name, inherited)
//{
// this.Annotations = annotations;
// this.Subscribable = subscribable;
// this.ArgumentType = argumentType;
//}
public static EventDef MakeEventDef(Type type, EventInfo ei, byte index, string name, TypeDef schema)
{
if (!ei.EventHandlerType.IsGenericType)
throw new Exception($"Unsupported event handler type in event `{type.Name}.{ei.Name}`");
if (ei.EventHandlerType.GetGenericTypeDefinition() != typeof(ResourceEventHandler<>)
&& ei.EventHandlerType.GetGenericTypeDefinition() != typeof(CustomResourceEventHandler<>))
throw new Exception($"Unsupported event handler type in event `{type.Name}.{ei.Name}`");
var argType = ei.EventHandlerType.GenericTypeArguments[0];
var evtType = TRU.FromType(argType);
if (evtType == null)
throw new Exception($"Unsupported type `{argType}` in event `{type.Name}.{ei.Name}`");
var annotationAttrs = ei.GetCustomAttributes<AnnotationAttribute>(true);
var subscribableAttr = ei.GetCustomAttribute<SubscribableAttribute>(true);
//evtType.Nullable = new NullabilityInfoContext().Create(ei).ReadState is NullabilityState.Nullable;
var nullableAttr = ei.GetCustomAttributes().FirstOrDefault(x => x.GetType().Name == "System.Runtime.CompilerServices.NullableAttribute");// .GetCustomAttribute<NullableAttribute>(true);
var nullableContextAttr = ei.GetCustomAttributes().FirstOrDefault(x => x.GetType().Name == "System.Runtime.CompilerServices.NullableContextAttribute");// ei.GetCustomAttribute<NullableContextAttribute>(true);
var nullableAttrFlags = (nullableAttr?.GetType().GetField("NullableFlags")?.GetValue(nullableAttr) as byte[] ?? new byte[0]).ToList();
var nullableContextAttrFlag = (byte)(nullableContextAttr?.GetType().GetField("Flag")?.GetValue(nullableContextAttr) ?? (byte)0);
//var flags = nullableAttr?.Flags?.ToList() ?? new List<byte>();
//var flags = ((byte[])nullableAttr?.NullableFlags ?? new byte[0]).ToList();
// skip the eventHandler class
if (nullableAttrFlags.Count > 1)
nullableAttrFlags = nullableAttrFlags.Skip(1).ToList();
if (nullableContextAttrFlag == 2)
{
if (nullableAttrFlags.Count == 1)
evtType.SetNotNull(nullableAttrFlags.FirstOrDefault());
else
evtType.SetNotNull(nullableAttrFlags);
}
else
{
if (nullableAttrFlags.Count == 1)
evtType.SetNull(nullableAttrFlags.FirstOrDefault());
else
evtType.SetNull(nullableAttrFlags);
}
Map<string, string> annotations = null;
if (annotationAttrs != null && annotationAttrs.Count() > 0)
{
annotations = new Map<string, string>();
foreach (var attr in annotationAttrs)
annotations.Add(attr.Key, attr.Value);
}
return new EventDef()
{
Name = name,
ArgumentType = evtType,
Index = index,
Inherited = ei.DeclaringType != type,
Annotations = annotations,
EventInfo = ei,
Subscribable = subscribableAttr != null
};
}
}

View File

@@ -0,0 +1,315 @@
using Esiur.Core;
using Esiur.Data;
using Esiur.Net.IIP;
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Data.Types;
public class FunctionDef : MemberDef
{
public Map<string, string> Annotations
{
get;
set;
}
//public bool IsVoid
//{
// get;
// set;
//}
public TRU ReturnType { get; set; }
public bool IsStatic { get; set; }
public ArgumentDef[] Arguments { get; set; }
public MethodInfo MethodInfo
{
get;
set;
}
public static (uint, FunctionDef) Parse(byte[] data, uint offset, byte index, bool inherited)
{
var oOffset = offset;
var isStatic = ((data[offset] & 0x4) == 0x4);
var hasAnnotation = ((data[offset++] & 0x10) == 0x10);
var name = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
// return type
var (rts, returnType) = TRU.Parse(data, offset);
offset += rts;
// arguments count
var argsCount = data[offset++];
List<ArgumentDef> arguments = new();
for (var a = 0; a < argsCount; a++)
{
var (cs, argType) = ArgumentDef.Parse(data, offset, a);
arguments.Add(argType);
offset += cs;
}
Map<string, string> annotations = null;
// arguments
if (hasAnnotation) // Annotation ?
{
var (len, anns) = Codec.ParseSync(data, offset, null);
if (anns is Map<string, string> map)
annotations = map;
offset += len;
}
return (offset - oOffset, new FunctionDef()
{
Index = index,
Name = name,
Arguments = arguments.ToArray(),
IsStatic = isStatic,
Inherited = inherited,
Annotations = annotations,
ReturnType = returnType,
});
}
public byte[] Compose()
{
var name = DC.ToBytes(Name);
var bl = new BinaryList()
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ReturnType.Compose())
.AddUInt8((byte)Arguments.Length);
for (var i = 0; i < Arguments.Length; i++)
bl.AddUInt8Array(Arguments[i].Compose());
if (Annotations != null)
{
var exp = Codec.Compose(Annotations, null, null);// DC.ToBytes(Annotation);
bl.AddUInt8Array(exp);
bl.InsertUInt8(0, (byte)((Inherited ? (byte)0x90 : (byte)0x10) | (IsStatic ? 0x4 : 0)));
}
else
bl.InsertUInt8(0, (byte)((Inherited ? (byte)0x80 : (byte)0x0) | (IsStatic ? 0x4 : 0)));
return bl.ToArray();
}
//public FunctionTemplate(TypeSchema template, byte index, string name, bool inherited, bool isStatic, ArgumentTemplate[] arguments, TRU returnType, Map<string, string> annotations = null)
// : base(template, index, name, inherited)
//{
// this.Arguments = arguments;
// this.ReturnType = returnType;
// this.Annotations = annotations;
// this.IsStatic = isStatic;
//}
public static FunctionDef MakeFunctionDef(Type type, MethodInfo mi, byte index, string name, TypeDef schema)
{
var genericRtType = mi.ReturnType.IsGenericType ? mi.ReturnType.GetGenericTypeDefinition() : null;
TRU rtType;
if (genericRtType == typeof(AsyncReply<>))
{
rtType = TRU.FromType(mi.ReturnType.GetGenericArguments()[0]);
}
else if (genericRtType == typeof(Task<>))
{
rtType = TRU.FromType(mi.ReturnType.GetGenericArguments()[0]);
}
else if (genericRtType == typeof(IEnumerable<>))// || genericRtType == typeof(IAsyncEnumerable<>))
{
// get export
rtType = TRU.FromType(mi.ReturnType.GetGenericArguments()[0]);
}
else
{
if (mi.ReturnType == typeof(Task))
rtType = TRU.FromType(null);
else
rtType = TRU.FromType(mi.ReturnType);
}
if (rtType == null)
throw new Exception($"Unsupported type `{mi.ReturnType}` in method `{type.Name}.{mi.Name}` return");
var annotationAttrs = mi.GetCustomAttributes<AnnotationAttribute>(true);
//var nullabilityInfoContext = new NullabilityInfoContext();
//rtType.Nullable = nullabilityInfoContext.Create(mi.ReturnParameter).WriteState is NullabilityState.Nullable;
var nullableAttr = mi.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableAttribute");
var nullableContextAttr = mi.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableContextAttribute");
var nullableAttrFlags = (nullableAttr?.GetType().GetField("NullableFlags")?.GetValue(nullableAttr) as byte[] ?? new byte[0]).ToList();
var nullableContextAttrFlag = (byte)(nullableContextAttr?.GetType().GetField("Flag")?.GetValue(nullableContextAttr) ?? (byte)0);
//var flags = ((byte[])nullableAttr?.NullableFlags ?? new byte[0]).ToList();
//var rtNullableAttr = mi.ReturnTypeCustomAttributes.GetCustomAttributes(typeof(NullableAttribute), true).FirstOrDefault() as NullableAttribute;
var rtNullableAttr = mi.ReturnTypeCustomAttributes.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableAttribute");
// var rtNullableContextAttr = mi.ReturnTypeCustomAttributes
// .GetCustomAttributes(typeof(NullableContextAttribute), true)
// .FirstOrDefault() as NullableContextAttribute
// ?? nullableContextAttr;
var rtNullableContextAttr = mi.ReturnTypeCustomAttributes
.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().Name == "NullableContextAttribute")
?? nullableContextAttr;
var rtNullableAttrFlags = (rtNullableAttr?.GetType().GetField("NullableFlags")?.GetValue(rtNullableAttr) as byte[] ?? new byte[0]).ToList();
var rtNullableContextAttrFlag = (byte)(rtNullableContextAttr?.GetType().GetField("Flag")?.GetValue(rtNullableContextAttr) ?? (byte)0);
//var rtFlags = rtNullableAttr?.Flags?.ToList() ?? new List<byte>();
//var rtFlags = ((byte[])rtNullableAttr?.NullableFlags ?? new byte[0]).ToList();
if (rtNullableAttrFlags.Count > 0 && genericRtType == typeof(AsyncReply<>))
rtNullableAttrFlags.RemoveAt(0);
if (rtNullableContextAttrFlag == 2)
{
if (rtNullableAttrFlags.Count == 1)
rtType.SetNotNull(rtNullableAttrFlags.FirstOrDefault());
else
rtType.SetNotNull(rtNullableAttrFlags);
}
else
{
if (rtNullableAttrFlags.Count == 1)
rtType.SetNull(rtNullableAttrFlags.FirstOrDefault());
else
rtType.SetNull(rtNullableAttrFlags);
}
var args = mi.GetParameters();
if (args.Length > 0)
{
if (args.Last().ParameterType == typeof(DistributedConnection)
|| args.Last().ParameterType == typeof(InvocationContext))
args = args.Take(args.Count() - 1).ToArray();
}
var arguments = args.Select(x =>
{
var argType = TRU.FromType(x.ParameterType);
if (argType == null)
throw new Exception($"Unsupported type `{x.ParameterType}` in method `{type.Name}.{mi.Name}` parameter `{x.Name}`");
var argAnnotationAttrs = x.GetCustomAttributes<AnnotationAttribute>(true);
var argNullableAttr = x.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableAttribute");
var argNullableContextAttr = x.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableContextAttr");
var argNullableAttrFlags = (argNullableAttr?.GetType().GetField("NullableFlags")?.GetValue(argNullableAttr) as byte[] ?? new byte[0]).ToList();
var argNullableContextAttrFlag = (byte)(argNullableAttr?.GetType().GetField("Flag")?.GetValue(argNullableAttr) ?? (byte)0);
if (argNullableContextAttrFlag == 2)
{
if (argNullableAttrFlags.Count == 1)
argType.SetNotNull(argNullableAttrFlags.FirstOrDefault());
else
argType.SetNotNull(argNullableAttrFlags);
}
else
{
if (argNullableAttrFlags.Count == 1)
argType.SetNull(argNullableAttrFlags.FirstOrDefault());
else
argType.SetNull(argNullableAttrFlags);
}
Map<string, string> argAnn = null;
if (argAnnotationAttrs != null && argAnnotationAttrs.Count() > 0)
{
argAnn = new Map<string, string>();
foreach (var attr in argAnnotationAttrs)
argAnn.Add(attr.Key, attr.Value);
}
return new ArgumentDef()
{
Name = x.Name,
Type = argType,
ParameterInfo = x,
Optional = x.IsOptional,
Annotations = argAnn
};
})
.ToArray();
Map<string, string> annotations = null;
if (annotationAttrs != null && annotationAttrs.Count() > 0)
{
annotations = new Map<string, string>();
foreach (var attr in annotationAttrs)
annotations.Add(attr.Key, attr.Value);
}
else
{
annotations = new Map<string, string>();
annotations.Add("", "(" + String.Join(",",
mi.GetParameters().Where(x => x.ParameterType != typeof(DistributedConnection))
.Select(x => "[" + x.ParameterType.Name + "] " + x.Name)) + ") -> " + mi.ReturnType.Name);
}
return new FunctionDef()
{
Name = name,
Index = index,
Inherited = mi.DeclaringType != type,
IsStatic = mi.IsStatic,
ReturnType = rtType,
Arguments = arguments,
MethodInfo = mi,
Annotations = annotations
};
}
public override string ToString()
{
//return = "(" + String.Join(",", mi.GetParameters().Where(x => x.ParameterType != typeof(DistributedConnection)).Select(x => "[" + x.ParameterType.Name + "] " + x.Name)) + ") -> " + mi.ReturnType.Name;
return $"{ReturnType} {Name}({string.Join(", ", Arguments.Select(a => a.ToString()))})";
}
}

View File

@@ -0,0 +1,90 @@
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace Esiur.Data.Types;
#nullable enable
public class MemberData
{
public MemberInfo Info;
public string Name;
public int Order;
//public bool Inherited;
public MemberData? Parent;
public MemberData? Child;
public byte Index;
public PropertyPermission PropertyPermission;
//public ExportAttribute ExportAttribute;
//public string Name => ExportAttribute?.Name ?? Info.Name;
public MemberData(MemberInfo info, int order)
{
var exportAttr = info.GetCustomAttribute<ExportAttribute>();
if (info is PropertyInfo pi)
{
if (exportAttr != null && exportAttr.Permission.HasValue)
{
if ((exportAttr.Permission == PropertyPermission.Write
|| exportAttr.Permission == PropertyPermission.ReadWrite) && !pi.CanWrite)
{
throw new Exception($"Property '{pi.Name}' does not have a setter, but ExportAttribute specifies it as writable.");
}
if ((exportAttr.Permission == PropertyPermission.Read
|| exportAttr.Permission == PropertyPermission.ReadWrite) && !pi.CanRead)
{
throw new Exception($"Property '{pi.Name}' does not have a getter, but ExportAttribute specifies it as readable.");
}
this.PropertyPermission = exportAttr.Permission.Value;
}
else
{
this.PropertyPermission = (pi.CanRead && pi.CanWrite) ? PropertyPermission.ReadWrite
: pi.CanWrite ? PropertyPermission.Write
: PropertyPermission.Read;
}
}
this.Name = exportAttr?.Name ?? info.Name;
this.Info = info;
this.Order = order;
}
public MemberInfo GetMemberInfo()
{
var rt = Info;
var md = Child;
while (md != null)
{
rt = Info;
md = md.Child;
}
return rt;
}
//public string? GetAnnotation()
//{
// string? rt = null;
// var md = this;
// while (md != null)
// {
// var annotationAttr = md.Info.GetCustomAttribute<AnnotationAttribute>();
// if (annotationAttr != null)
// rt = annotationAttr.Annotation;
// md = md.Child;
// }
// return rt;
//}
}

View File

@@ -0,0 +1,32 @@
using Esiur.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Data.Types;
public class MemberDef
{
public byte Index { get; set; }
public string Name { get; set; }
public bool Inherited { get; set; }
public TypeDef Definition { get; set; }
//public MemberTemplate()
//{
// Template = template;
// Index = index;
// Name = name;
// Inherited = inherited;
//}
public string Fullname => Definition.ClassName + "." + Name;
//public virtual byte[] Compose()
//{
// return DC.ToBytes(Name);
//}
}

View File

@@ -0,0 +1,307 @@
using Esiur.Data;
using Esiur.Net.IIP;
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Data.Types;
public class PropertyDef : MemberDef
{
public Map<string, string> Annotations { get; set; }
public PropertyInfo PropertyInfo
{
get;
set;
}
public TRU ValueType { get; set; }
/*
public bool Serilize
{
get;set;
}
*/
//bool ReadOnly;
//IIPTypes::DataType ReturnType;
public PropertyPermission Permission
{
get;
set;
}
//public bool IsNullable { get; set; }
public bool Recordable
{
get;
set;
}
/*
public PropertyType Mode
{
get;
set;
}*/
//public string ReadAnnotation
//{
// get;
// set;
//}
//public string WriteAnnotation
//{
// get;
// set;
//}
/*
public bool Storable
{
get;
set;
}*/
public override string ToString()
{
return $"{Name}: {ValueType}";
}
public static (uint, PropertyDef) Parse(byte[] data, uint offset, byte index, bool inherited)
{
var oOffset = offset;
var hasAnnotation = ((data[offset] & 0x8) == 0x8);
var recordable = ((data[offset] & 1) == 1);
var permission = (PropertyPermission)((data[offset++] >> 1) & 0x3);
var name = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
var (dts, valueType) = TRU.Parse(data, offset);
offset += dts;
Map<string, string> annotations = null;
// arguments
if (hasAnnotation) // Annotation ?
{
var (len, anns) = Codec.ParseSync(data, offset, null);
if (anns is Map<string, string> map)
annotations = map;
offset += len;
}
return (offset - oOffset, new PropertyDef()
{
Index = index,
Name = name,
Inherited = inherited,
Permission = permission,
Recordable = recordable,
ValueType = valueType,
Annotations = annotations
});
}
public byte[] Compose()
{
var name = DC.ToBytes(Name);
var pv = ((byte)(Permission) << 1) | (Recordable ? 1 : 0);
if (Inherited)
pv |= 0x80;
//if (WriteAnnotation != null && ReadAnnotation != null)
//{
// var rexp = DC.ToBytes(ReadAnnotation);
// var wexp = DC.ToBytes(WriteAnnotation);
// return new BinaryList()
// .AddUInt8((byte)(0x38 | pv))
// .AddUInt8((byte)name.Length)
// .AddUInt8Array(name)
// .AddUInt8Array(ValueType.Compose())
// .AddInt32(wexp.Length)
// .AddUInt8Array(wexp)
// .AddInt32(rexp.Length)
// .AddUInt8Array(rexp)
// .ToArray();
//}
//else if (WriteAnnotation != null)
//{
// var wexp = DC.ToBytes(WriteAnnotation);
// return new BinaryList()
// .AddUInt8((byte)(0x30 | pv))
// .AddUInt8((byte)name.Length)
// .AddUInt8Array(name)
// .AddUInt8Array(ValueType.Compose())
// .AddInt32(wexp.Length)
// .AddUInt8Array(wexp)
// .ToArray();
//}
//else if (ReadAnnotation != null)
//{
// var rexp = DC.ToBytes(ReadAnnotation);
// return new BinaryList()
// .AddUInt8((byte)(0x28 | pv))
// .AddUInt8((byte)name.Length)
// .AddUInt8Array(name)
// .AddUInt8Array(ValueType.Compose())
// .AddInt32(rexp.Length)
// .AddUInt8Array(rexp)
// .ToArray();
//}
if (Annotations != null)
{
var rexp = Codec.Compose(Annotations, null, null);
return new BinaryList()
.AddUInt8((byte)(0x28 | pv))
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ValueType.Compose())
.AddUInt8Array(rexp)
.ToArray();
}
else
{
return new BinaryList()
.AddUInt8((byte)(0x20 | pv))
.AddUInt8((byte)name.Length)
.AddUInt8Array(name)
.AddUInt8Array(ValueType.Compose())
.ToArray();
}
}
//public PropertyTemplate(TypeSchema template, byte index, string name, bool inherited,
// TRU valueType, string readAnnotation = null, string writeAnnotation = null, bool recordable = false)
// : base(template, index, name, inherited)
//{
// this.Recordable = recordable;
// //this.Storage = storage;
// if (readAnnotation != null)
// this.ReadAnnotation = readAnnotation;
// this.WriteAnnotation = writeAnnotation;
// this.ValueType = valueType;
//}
public static PropertyDef MakePropertyDef(Type type, PropertyInfo pi, string name, byte index, PropertyPermission permission, TypeDef schema)
{
var genericPropType = pi.PropertyType.IsGenericType ? pi.PropertyType.GetGenericTypeDefinition() : null;
var propType = genericPropType == typeof(PropertyContext<>) ?
TRU.FromType(pi.PropertyType.GetGenericArguments()[0]) :
TRU.FromType(pi.PropertyType);
if (propType == null)
throw new Exception($"Unsupported type `{pi.PropertyType}` in property `{type.Name}.{pi.Name}`");
var annotationAttrs = pi.GetCustomAttributes<AnnotationAttribute>(true);
var storageAttr = pi.GetCustomAttribute<StorageAttribute>(true);
//var nullabilityContext = new NullabilityInfoContext();
//propType.Nullable = nullabilityContext.Create(pi).ReadState is NullabilityState.Nullable;
var nullableAttr = pi.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableAttribute");
var nullableContextAttr = pi.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableContextAttribute");
var nullableAttrFlags = (nullableAttr?.GetType().GetField("NullableFlags")?.GetValue(nullableAttr) as byte[] ?? new byte[0]).ToList();
var nullableContextAttrFlag = (byte)(nullableContextAttr?.GetType().GetField("Flag")?.GetValue(nullableContextAttr) ?? (byte)0);
//var nullableAttr = pi.GetCustomAttribute<NullableAttribute>(true);
//var flags = ((byte[]) nullableAttr?.NullableFlags ?? new byte[0]).ToList();
if (nullableAttrFlags.Count > 0 && genericPropType == typeof(PropertyContext<>))
nullableAttrFlags.RemoveAt(0);
if (nullableContextAttrFlag == 2)
{
if (nullableAttrFlags.Count == 1)
propType.SetNotNull(nullableAttrFlags.FirstOrDefault());
else
propType.SetNotNull(nullableAttrFlags);
}
else
{
if (nullableAttrFlags.Count == 1)
propType.SetNull(nullableAttrFlags.FirstOrDefault());
else
propType.SetNull(nullableAttrFlags);
}
Map<string, string> annotations = null;
if (annotationAttrs != null && annotationAttrs.Count() > 0)
{
annotations = new Map<string, string>();
foreach (var attr in annotationAttrs)
annotations.Add(attr.Key, attr.Value);
}
else
{
annotations = new Map<string, string>();
annotations.Add("", GetTypeAnnotationName(pi.PropertyType));
}
return new PropertyDef()
{
Name = name,
Index = index,
Inherited = pi.DeclaringType != type,
ValueType = propType,
PropertyInfo = pi,
Recordable = storageAttr == null ? false : storageAttr.Mode == StorageMode.Recordable,
Permission = permission,
Annotations = annotations,
};
//var pt = new PropertyTemplate(TypeSchema, index, customName ?? pi.Name, pi.DeclaringType != type, propType);
//if (storageAttr != null)
// pt.Recordable = storageAttr.Mode == StorageMode.Recordable;
//if (annotationAttr != null)
// pt.ReadAnnotation = annotationAttr.Annotation;
//else
// pt.ReadAnnotation = GetTypeAnnotationName(pi.PropertyType);
//pt.PropertyInfo = pi;
//return pt;
}
public static string GetTypeAnnotationName(Type type)
{
var nullType = Nullable.GetUnderlyingType(type);
if (nullType == null)
return type.Name;
else
return type.Name + "?";
}
}

782
Esiur/Data/Types/TypeDef.cs Normal file
View File

@@ -0,0 +1,782 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Esiur.Misc;
using Esiur.Data;
using Esiur.Core;
using System.Security.Cryptography;
using Esiur.Proxy;
using Esiur.Net.IIP;
using System.Runtime.CompilerServices;
using Esiur.Resource;
namespace Esiur.Data.Types;
//public enum TemplateType
//{
// Resource,
// Record
//}
public class TypeDef
{
protected UUID typeId;
protected UUID? parentId;
public Map<string, string> Annotations { get; set; }
string typeName;
List<FunctionDef> functions = new List<FunctionDef>();
List<EventDef> events = new List<EventDef>();
List<PropertyDef> properties = new List<PropertyDef>();
List<AttributeDef> attributes = new List<AttributeDef>();
List<ConstantDef> constants = new();
int version;
TypeDefKind typeDefKind;
public override string ToString()
{
return typeName;
}
// protected TemplateType
//bool isReady;
protected byte[] content;
public UUID? ParentId => parentId;
public byte[] Content
{
get { return content; }
}
public TypeDefKind Kind => typeDefKind;
public Type DefinedType { get; set; }
public Type ParentDefinedType { get; set; }
//public MemberTemplate GetMemberTemplate(MemberInfo member)
//{
// if (member is MethodInfo)
// return GetFunctionTemplateByName(member.Name);
// else if (member is EventInfo)
// return GetEventTemplateByName(member.Name);
// else if (member is PropertyInfo)
// return GetPropertyTemplateByName(member.Name);
// else
// return null;
//}
public EventDef GetEventDefByName(string eventName)
{
foreach (var i in events)
if (i.Name == eventName)
return i;
return null;
}
public EventDef GetEventDefByIndex(byte index)
{
foreach (var i in events)
if (i.Index == index)
return i;
return null;
}
public FunctionDef GetFunctionDefByName(string functionName)
{
foreach (var i in functions)
if (i.Name == functionName)
return i;
return null;
}
public FunctionDef GetFunctionDefByIndex(byte index)
{
foreach (var i in functions)
if (i.Index == index)
return i;
return null;
}
public PropertyDef GetPropertyDefByIndex(byte index)
{
foreach (var i in properties)
if (i.Index == index)
return i;
return null;
}
public PropertyDef GetPropertyDefByName(string propertyName)
{
foreach (var i in properties)
if (i.Name == propertyName)
return i;
return null;
}
public AttributeDef GetAttributeDef(string attributeName)
{
foreach (var i in attributes)
if (i.Name == attributeName)
return i;
return null;
}
public UUID Id
{
get { return typeId; }
}
public string Name
{
get { return typeName; }
}
//public MemberTemplate[] Methods
//{
// get { return members.ToArray(); }
//}
public FunctionDef[] Functions
{
get { return functions.ToArray(); }
}
public EventDef[] Events
{
get { return events.ToArray(); }
}
public PropertyDef[] Properties
{
get { return properties.ToArray(); }
}
public ConstantDef[] Constants => constants.ToArray();
public TypeDef()
{
}
public static UUID GetTypeUUID(Type type)
{
var attr = type.GetCustomAttribute<ClassIdAttribute>();
if (attr != null)
return attr.ClassId;
var tn = Encoding.UTF8.GetBytes(GetTypeName(type));
var hash = SHA256.Create().ComputeHash(tn).Clip(0, 16);
hash[6] = (byte)((hash[6] & 0xF) | 0x80);
hash[8] = (byte)((hash[8] & 0xF) | 0x80);
var rt = new UUID(hash);
return rt;
}
static Type[] GetDistributedTypes(Type type)
{
if (type.IsArray)
return GetDistributedTypes(type.GetElementType());
else if (type.IsEnum)
return new Type[] { type };
else if (Codec.ImplementsInterface(type, typeof(IRecord))
|| Codec.ImplementsInterface(type, typeof(IResource)))
{
return new Type[] { type };
}
else if (type.IsGenericType)
{
var genericType = type.GetGenericTypeDefinition();
var genericTypeArgs = type.GetGenericArguments();
if (genericType == typeof(List<>)
|| genericType == typeof(PropertyContext<>)
|| genericType == typeof(AsyncReply<>)
|| genericType == typeof(ResourceLink<>))
{
return GetDistributedTypes(genericTypeArgs[0]);
}
else if (genericType == typeof(Tuple<>)
|| genericType == typeof(Map<,>))
{
var rt = new List<Type>();
for (var i = 0; i < genericTypeArgs.Length; i++)
{
var depTypes = GetDistributedTypes(genericTypeArgs[i]);
foreach (var depType in depTypes)
if (!rt.Contains(depType))
rt.Add(depType);
}
return rt.ToArray();
}
}
return new Type[0];
}
public static TypeDef[] GetDependencies(TypeDef schema, Warehouse warehouse)
{
var list = new List<TypeDef>();
// Add self
list.Add(schema);
Action<TypeDef, List<TypeDef>> getDependenciesFunc = null;
getDependenciesFunc = (TypeDef sch, List<TypeDef> bag) =>
{
if (schema.DefinedType == null)
return;
// Add parents
var parentType = sch.ParentDefinedType;
// Get parents
while (parentType != null)
{
var parentTemplate = warehouse.GetTemplateByType(parentType);
if (parentTemplate != null)
{
list.Add(parentTemplate);
parentType = parentTemplate.ParentDefinedType;
}
}
// functions
foreach (var f in sch.functions)
{
var functionReturnTypes = GetDistributedTypes(f.MethodInfo.ReturnType);
//.Select(x => Warehouse.GetTemplateByType(x))
//.Where(x => x != null && !bag.Contains(x))
foreach (var functionReturnType in functionReturnTypes)
{
var functionReturnTemplate = warehouse.GetTemplateByType(functionReturnType);
if (functionReturnTemplate != null)
{
if (!bag.Contains(functionReturnTemplate))
{
list.Add(functionReturnTemplate);
getDependenciesFunc(functionReturnTemplate, bag);
}
}
}
var args = f.MethodInfo.GetParameters();
for (var i = 0; i < args.Length - 1; i++)
{
var fpTypes = GetDistributedTypes(args[i].ParameterType);
foreach (var fpType in fpTypes)
{
var fpt = warehouse.GetTemplateByType(fpType);
if (fpt != null)
{
if (!bag.Contains(fpt))
{
bag.Add(fpt);
getDependenciesFunc(fpt, bag);
}
}
}
}
// skip DistributedConnection argument
if (args.Length > 0)
{
var last = args.Last();
if (last.ParameterType != typeof(DistributedConnection))
{
var fpTypes = GetDistributedTypes(last.ParameterType);
foreach (var fpType in fpTypes)
{
var fpt = warehouse.GetTemplateByType(fpType);
if (fpt != null)
{
if (!bag.Contains(fpt))
{
bag.Add(fpt);
getDependenciesFunc(fpt, bag);
}
}
}
}
}
}
// properties
foreach (var p in sch.properties)
{
var propertyTypes = GetDistributedTypes(p.PropertyInfo.PropertyType);
foreach (var propertyType in propertyTypes)
{
var propertyTemplate = warehouse.GetTemplateByType(propertyType);
if (propertyTemplate != null)
{
if (!bag.Contains(propertyTemplate))
{
bag.Add(propertyTemplate);
getDependenciesFunc(propertyTemplate, bag);
}
}
}
}
// events
foreach (var e in sch.events)
{
var eventTypes = GetDistributedTypes(e.EventInfo.EventHandlerType.GenericTypeArguments[0]);
foreach (var eventType in eventTypes)
{
var eventTemplate = warehouse.GetTemplateByType(eventType);
if (eventTemplate != null)
{
if (!bag.Contains(eventTemplate))
{
bag.Add(eventTemplate);
getDependenciesFunc(eventTemplate, bag);
}
}
}
}
};
getDependenciesFunc(schema, list);
return list.Distinct().ToArray();
}
public static string GetTypeName(Type type, char separator = '.')
{
if (type.IsGenericType)
{
var index = type.Name.IndexOf("`");
var name = $"{type.Namespace}{separator}{((index > -1) ? type.Name.Substring(0, index) : type.Name)}Of";
foreach (var t in type.GenericTypeArguments)
name += GetTypeName(t, '_');
return name;
}
else
return $"{type.Namespace?.Replace('.', separator) ?? "Global"}{separator}{type.Name}";
}
public bool IsWrapper { get; private set; }
public TypeDef(Type type, Warehouse warehouse = null)
{
if (Codec.ImplementsInterface(type, typeof(IResource)))
typeDefKind = TypeDefKind.Resource;
else if (Codec.ImplementsInterface(type, typeof(IRecord)))
typeDefKind = TypeDefKind.Record;
else if (type.IsEnum)
typeDefKind = TypeDefKind.Enum;
else
throw new Exception("Type must implement IResource, IRecord or inherit from DistributedResource.");
IsWrapper = Codec.InheritsClass(type, typeof(DistributedResource));
type = ResourceProxy.GetBaseType(type);
DefinedType = type;
typeName = GetTypeName(type);
// set guid
typeId = GetTypeUUID(type);
if (warehouse != null)
warehouse.RegisterSchema(this);
var hierarchy = GetHierarchy(type);
if (hierarchy.ContainsKey(MemberTypes.Field))
{
foreach (var cd in hierarchy[MemberTypes.Field])
{
constants.Add(ConstantDef.MakeConstantDef
(type, (FieldInfo)cd.GetMemberInfo(), cd.Index, cd.Name, this));
}
}
if (hierarchy.ContainsKey(MemberTypes.Property))
{
foreach (var pd in hierarchy[MemberTypes.Property])
{
properties.Add(PropertyDef.MakePropertyDef
(type, (PropertyInfo)pd.GetMemberInfo(), pd.Name, pd.Index, pd.PropertyPermission, this));
}
}
if (typeDefKind == TypeDefKind.Resource)
{
if (hierarchy.ContainsKey(MemberTypes.Method))
{
foreach (var fd in hierarchy[MemberTypes.Method])
{
functions.Add(FunctionDef.MakeFunctionDef
(type, (MethodInfo)fd.GetMemberInfo(), fd.Index, fd.Name, this));
}
}
if (hierarchy.ContainsKey(MemberTypes.Event))
{
foreach (var ed in hierarchy[MemberTypes.Event])
{
events.Add(EventDef.MakeEventDef
(type, (EventInfo)ed.GetMemberInfo(), ed.Index, ed.Name, this));
}
}
}
// add attributes
var attrs = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.GetCustomAttribute<AttributeAttribute>() != null);
foreach (var attr in attrs)
{
var attrAttr = attr.GetCustomAttribute<AttributeAttribute>();
attributes.Add(AttributeDef
.MakeAttributeDef(type, attr, 0, attrAttr?.Name ?? attr.Name, this));
}
// bake it binarily
var b = new BinaryList();
// find the first parent type that implements IResource
var hasParent = HasParent(type);
var classAnnotations = type.GetCustomAttributes<AnnotationAttribute>(false);
var hasClassAnnotation = (classAnnotations != null) && (classAnnotations.Count() > 0);
var typeNameBytes = DC.ToBytes(typeName);
b.AddUInt8((byte)((hasParent ? 0x80 : 0) | (hasClassAnnotation ? 0x40 : 0x0) | (byte)typeDefKind))
.AddUUID(typeId)
.AddUInt8((byte)typeNameBytes.Length)
.AddUInt8Array(typeNameBytes);
if (hasParent)
{
// find the first parent type that implements IResource
ParentDefinedType = ResourceProxy.GetBaseType(type.BaseType);
var parentId = GetTypeUUID(ParentDefinedType);
b.AddUUID(parentId);
}
if (hasClassAnnotation)
{
Annotations = new Map<string, string>();
foreach (var ann in classAnnotations)
Annotations.Add(ann.Key, ann.Value);
var classAnnotationBytes = Codec.Compose (Annotations, null, null);
b.AddUInt8Array(classAnnotationBytes);
}
b.AddInt32(version)
.AddUInt16((ushort)(functions.Count + properties.Count + events.Count + constants.Count));
foreach (var ft in functions)
b.AddUInt8Array(ft.Compose());
foreach (var pt in properties)
b.AddUInt8Array(pt.Compose());
foreach (var et in events)
b.AddUInt8Array(et.Compose());
foreach (var ct in constants)
b.AddUInt8Array(ct.Compose());
content = b.ToArray();
}
public static bool HasParent(Type type)
{
var parent = type.BaseType;
while (parent != null)
{
if (parent == typeof(Esiur.Resource.Resource)
|| parent == typeof(Record)
|| parent == typeof(EntryPoint))
return false;
if (parent.GetInterfaces().Contains(typeof(IResource))
|| parent.GetInterfaces().Contains(typeof(IRecord)))
return true;
parent = parent.BaseType;
}
return false;
}
public static Dictionary<MemberTypes, List<MemberData>> GetHierarchy(Type type)
{
var members = new List<MemberData>();
var order = 0;
while (type != null)
{
var classIsPublic = type.IsEnum || (type.GetCustomAttribute<ExportAttribute>() != null);
if (classIsPublic)
{
// get public instance members only.
var mis = type.GetMembers(BindingFlags.Public | BindingFlags.Instance
| BindingFlags.DeclaredOnly | BindingFlags.Static)
.Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field
|| x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method)
.Where(x => !(x is FieldInfo c && !c.IsStatic))
.Where(x => x.GetCustomAttribute<IgnoreAttribute>() == null)
.Where(x => x.Name != "Instance")
.Where(x => x.Name != "Trigger")
.Where(x => !(x is MethodInfo m && m.IsSpecialName))
.Where(x => !(x is EventInfo e &&
!(e.EventHandlerType.IsGenericType &&
(e.EventHandlerType.GetGenericTypeDefinition() == typeof(ResourceEventHandler<>)
|| e.EventHandlerType.GetGenericTypeDefinition() == typeof(CustomResourceEventHandler<>))
)
))
.Select(x => new MemberData(
info: x,
order: order
))
.OrderBy(x => x.Name);
members.AddRange(mis.ToArray());
}
else
{
// allow private and public members that are marked with [Export] attribute.
var mis = type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
| BindingFlags.DeclaredOnly | BindingFlags.Static)
.Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field
|| x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method)
.Where(x => !(x is FieldInfo c && !c.IsStatic))
.Where(x => x.GetCustomAttribute<ExportAttribute>() != null)
.Where(x => !(x is MethodInfo m && m.IsSpecialName))
.Select(x => new MemberData(
info: x,
order: order
))
.OrderBy(x => x.Name);
members.AddRange(mis.ToArray());
}
type = type.BaseType;
if (type == null
|| type == typeof(Esiur.Resource.Resource)
|| type == typeof(Record)
|| type == typeof(EntryPoint))
break;
if (type.GetInterfaces().Contains(typeof(IResource))
|| type.GetInterfaces().Contains(typeof(IRecord)))
{
order++;
continue;
}
break;
}
// round 2: check for duplicates
for (var i = 0; i < members.Count; i++)
{
var mi = members[i];
for (var j = i + 1; j < members.Count; j++)
{
var pi = members[j];
if (pi.Info.MemberType != mi.Info.MemberType)
continue;
//if (ci.Info.Name == mi.Info.Name && ci.Order == mi.Order)
// throw new Exception($"Method overload is not supported. Method '{ci.Info.Name}'.");
if (pi.Name == mi.Name)
{
if (pi.Order == mi.Order)
throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'.");
else
{
// @TODO: check for return type and parameters they must match
if (pi.Info.Name != mi.Info.Name)
throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'.");
}
mi.Parent = pi;
pi.Child = mi;
}
}
}
// assign indexes
var groups = members.Where(x => x.Parent == null)
.OrderBy(x => x.Name).OrderByDescending(x => x.Order)
.GroupBy(x => x.Info.MemberType);
foreach (var group in groups)
{
byte index = 0;
foreach (var mi in group)
{
//if (mi.Parent == null)
mi.Index = index++;
}
}
var rt = groups.ToDictionary(g => g.Key, g => g.ToList());
return rt;
}
public static TypeDef Parse(byte[] data)
{
return Parse(data, 0, (uint)data.Length);
}
public static TypeDef Parse(byte[] data, uint offset, uint contentLength)
{
uint ends = offset + contentLength;
uint oOffset = offset;
// start parsing...
var od = new TypeDef();
od.content = data.Clip(offset, contentLength);
var hasParent = (data[offset] & 0x80) > 0;
var hasClassAnnotation = (data[offset] & 0x40) > 0;
od.typeDefKind = (TypeDefKind)(data[offset++] & 0xF);
od.typeId = data.GetUUID(offset);
offset += 16;
od.typeName = data.GetString(offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
if (hasParent)
{
od.parentId = data.GetUUID(offset);
offset += 16;
}
if (hasClassAnnotation)
{
var (len, anns) = Codec.ParseSync(data, offset, null);
if (anns is Map<string, string> annotations)
od.Annotations = annotations;
offset += len;
}
od.version = data.GetInt32(offset, Endian.Little);
offset += 4;
ushort methodsCount = data.GetUInt16(offset, Endian.Little);
offset += 2;
byte functionIndex = 0;
byte propertyIndex = 0;
byte eventIndex = 0;
for (int i = 0; i < methodsCount; i++)
{
var inherited = (data[offset] & 0x80) > 0;
var type = (data[offset] >> 5) & 0x3;
if (type == 0) // function
{
var (len, ft) = FunctionDef.Parse(data, offset, functionIndex++, inherited);
offset += len;
od.functions.Add(ft);
}
else if (type == 1) // property
{
var (len, pt) = PropertyDef.Parse(data, offset, propertyIndex++, inherited);
offset += len;
od.properties.Add(pt);
}
else if (type == 2) // Event
{
var (len, et) = EventDef.Parse(data, offset, propertyIndex++, inherited);
offset += len;
od.events.Add(et);
}
// constant
else if (type == 3)
{
var (len, ct) = ConstantDef.Parse(data, offset, propertyIndex++, inherited);
offset += len;
od.constants.Add(ct);
}
}
return od;
}
public Map<byte, object> CastProperties(Map<string, object> properties)
{
var rt = new Map<byte, object>();
foreach (var kv in properties)
{
var pt = GetPropertyDefByName(kv.Key);
if (pt == null) continue;
rt.Add(pt.Index, kv.Value);
}
return rt;
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data.Types;
public enum TypeDefKind : byte
{
Resource,
Record,
Enum,
Function
}