mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2026-03-31 18:38:22 +00:00
Renaming
This commit is contained in:
99
Esiur/Data/Types/ArgumentDef.cs
Normal file
99
Esiur/Data/Types/ArgumentDef.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Esiur/Data/Types/AttributeDef.cs
Normal file
32
Esiur/Data/Types/AttributeDef.cs
Normal 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
|
||||
};
|
||||
}
|
||||
}
|
||||
138
Esiur/Data/Types/ConstantDef.cs
Normal file
138
Esiur/Data/Types/ConstantDef.cs
Normal 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,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
9
Esiur/Data/Types/CustomEventOccurredEvent.cs
Normal file
9
Esiur/Data/Types/CustomEventOccurredEvent.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data.Types;
|
||||
|
||||
internal class CustomEventOccurredEvent
|
||||
{
|
||||
}
|
||||
131
Esiur/Data/Types/DefinitionDataType.cs
Normal file
131
Esiur/Data/Types/DefinitionDataType.cs
Normal 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 });
|
||||
// }
|
||||
//}
|
||||
187
Esiur/Data/Types/EventDef.cs
Normal file
187
Esiur/Data/Types/EventDef.cs
Normal 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
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
315
Esiur/Data/Types/FunctionDef.cs
Normal file
315
Esiur/Data/Types/FunctionDef.cs
Normal 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()))})";
|
||||
}
|
||||
|
||||
}
|
||||
90
Esiur/Data/Types/MemberData.cs
Normal file
90
Esiur/Data/Types/MemberData.cs
Normal 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;
|
||||
//}
|
||||
}
|
||||
|
||||
32
Esiur/Data/Types/MemberDef.cs
Normal file
32
Esiur/Data/Types/MemberDef.cs
Normal 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);
|
||||
//}
|
||||
}
|
||||
|
||||
307
Esiur/Data/Types/PropertyDef.cs
Normal file
307
Esiur/Data/Types/PropertyDef.cs
Normal 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
782
Esiur/Data/Types/TypeDef.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
13
Esiur/Data/Types/TypeDefKind.cs
Normal file
13
Esiur/Data/Types/TypeDefKind.cs
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user