2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2026-06-13 14:38:43 +00:00

TypeDefGenerator

This commit is contained in:
2026-06-08 23:25:49 +03:00
parent 0939f71464
commit 2bdd5d5022
23 changed files with 348 additions and 43 deletions
@@ -124,6 +124,9 @@ public class RemoteTypeDef:TypeDef
}
}
// try to get proxy type
od._proxyType = connection.Instance?.Warehouse?.TryGetProxyType(od.Kind, od.Domain, od.Name);
return od;
}
}
+1 -1
View File
@@ -736,7 +736,7 @@ public partial class EpConnection : NetworkConnection, IStore
}
SendAuthHeaders(EpAuthPacketMethod.SessionEstablished, localHeaders);
_session.Authenticated = true;
_session.LocalIdentity = null;
_session.RemoteIdentity = null;
+20 -5
View File
@@ -271,9 +271,13 @@ public static class TypeDefGenerator
// generate info class
var mainClassName = typeDefs.FirstOrDefault().Name?.Split('.');
var mainNamespace = mainClassName != null ? string.Join(".", mainClassName.Take(mainClassName.Length - 1)) : "Esiur";
var typesFile = @"using System;
namespace Esiur {
public static class Generated {
namespace " + mainNamespace + @" {
public static class Initialization {
public static Type[] Resources {get;} = new Type[] { " +
string.Join(",", typeDefs.Where(x => x.Kind == TypeDefKind.Resource).Select(x => $"typeof({x.Name})"))
+ @" };
@@ -282,11 +286,22 @@ public static class TypeDefGenerator
+ @" };
public static Type[] Enums { get; } = new Type[] { " +
string.Join(",", typeDefs.Where(x => x.Kind == TypeDefKind.Enum).Select(x => $"typeof({x.Name})"))
+ @" };" +
"\r\n } \r\n}";
+ @" };
public static void RegisterTypes(Warehouse warehouse)
{
foreach(var type in Resources)
warehouse.RegisterProxyType(type);
foreach(var type in Records)
warehouse.RegisterProxyType(type);
foreach(var type in Enums)
warehouse.RegisterProxyType(type);
}
\r\n } \r\n}";
File.WriteAllText(dstDir.FullName + Path.DirectorySeparatorChar + "Esiur.g.cs", typesFile);
File.WriteAllText(dstDir.FullName + Path.DirectorySeparatorChar + "Initialization.g.cs", typesFile);
return dstDir.FullName;
+188 -4
View File
@@ -1,19 +1,203 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
namespace Esiur.Resource
{
[AttributeUsage(AttributeTargets.Enum | AttributeTargets.Class, Inherited = false)]
public class RemoteAttribute:Attribute
public class RemoteAttribute : Attribute
{
public string Domain { get; private set; }
public string[] Domains { get; private set; }
public string FullName { get; private set; }
public RemoteAttribute(string domain, string fullName)
static readonly Regex StrictIPv4 = new(
@"^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$",
RegexOptions.Compiled | RegexOptions.CultureInvariant);
static readonly Regex IPv4Like = new(
@"^\d+(?:\.\d+){3}$",
RegexOptions.Compiled | RegexOptions.CultureInvariant);
static readonly Regex HostName = new(
@"^(?=.{1,253}\.?$)(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)*[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.?$",
RegexOptions.Compiled | RegexOptions.CultureInvariant);
public bool IsValidFullName()
{
Domain = domain;
return IsValidQualifiedClassName(FullName, true, false); ;
}
private static readonly HashSet<string> ReservedKeywords = new HashSet<string>(
new[]
{
"abstract", "as", "base", "bool", "break", "byte", "case", "catch",
"char", "checked", "class", "const", "continue", "decimal", "default",
"delegate", "do", "double", "else", "enum", "event", "explicit",
"extern", "false", "finally", "fixed", "float", "for", "foreach",
"goto", "if", "implicit", "in", "int", "interface", "internal",
"is", "lock", "long", "namespace", "new", "null", "object",
"operator", "out", "override", "params", "private", "protected",
"public", "readonly", "ref", "return", "sbyte", "sealed", "short",
"sizeof", "stackalloc", "static", "string", "struct", "switch",
"this", "throw", "true", "try", "typeof", "uint", "ulong",
"unchecked", "unsafe", "ushort", "using", "virtual", "void",
"volatile", "while"
},
StringComparer.Ordinal);
public RemoteAttribute(string fullName, params string[] domains)
{
Domains = domains;
FullName = fullName;
}
// @TODO: support wildcard records
public bool AreValidDomains()
{
foreach(var domain in Domains)
{
if (!IsValidDomain(domain))
return false;
}
return true;
}
private bool IsValidDomain(string domain)
{
if (string.IsNullOrWhiteSpace(domain))
return false;
string s = domain.Trim();
// Accept URI-style IPv6 literals, e.g. [::1]
if (s.Length > 2 && s[0] == '[' && s[s.Length - 1] == ']')
s = s.Substring(1, s.Length - 2);
// Strict IPv4 only: 0.0.0.0 to 255.255.255.255
if (StrictIPv4.IsMatch(s))
return true;
// Reject IPv4-looking strings that failed strict IPv4,
// e.g. 999.999.999.999
if (IPv4Like.IsMatch(s))
return false;
IPAddress ip;
// IPv6
if (IPAddress.TryParse(s, out ip) &&
ip.AddressFamily == AddressFamily.InterNetworkV6)
return true;
// Hostname or domain
return HostName.IsMatch(s);
}
public static bool IsValidQualifiedClassName(string value, bool requireNamespace, bool allowVerbatimIdentifiers)
{
if (string.IsNullOrWhiteSpace(value))
return false;
string s = value.Trim();
if (s.Length == 0)
return false;
// Reject whitespace inside the name
for (int i = 0; i < s.Length; i++)
{
if (char.IsWhiteSpace(s[i]))
return false;
}
// Optional source-style prefix: global::Namespace.Type
if (s.StartsWith("global::", StringComparison.Ordinal))
s = s.Substring("global::".Length);
string[] parts = s.Split('.');
if (requireNamespace && parts.Length < 2)
return false;
for (int i = 0; i < parts.Length; i++)
{
if (!IsValidIdentifier(parts[i], allowVerbatimIdentifiers))
return false;
}
return true;
}
private static bool IsValidIdentifier(string identifier, bool allowVerbatimIdentifier)
{
if (string.IsNullOrEmpty(identifier))
return false;
string id = identifier;
if (id[0] == '@')
{
if (!allowVerbatimIdentifier)
return false;
id = id.Substring(1);
if (id.Length == 0)
return false;
}
if (!IsIdentifierStartCharacter(id[0]))
return false;
for (int i = 1; i < id.Length; i++)
{
if (!IsIdentifierPartCharacter(id[i]))
return false;
}
// Reject reserved keywords unless written as @keyword
if (identifier[0] != '@' && ReservedKeywords.Contains(id))
return false;
return true;
}
private static bool IsIdentifierStartCharacter(char ch)
{
if (ch == '_')
return true;
UnicodeCategory category = CharUnicodeInfo.GetUnicodeCategory(ch);
return category == UnicodeCategory.UppercaseLetter ||
category == UnicodeCategory.LowercaseLetter ||
category == UnicodeCategory.TitlecaseLetter ||
category == UnicodeCategory.ModifierLetter ||
category == UnicodeCategory.OtherLetter ||
category == UnicodeCategory.LetterNumber;
}
private static bool IsIdentifierPartCharacter(char ch)
{
if (IsIdentifierStartCharacter(ch))
return true;
UnicodeCategory category = CharUnicodeInfo.GetUnicodeCategory(ch);
return category == UnicodeCategory.DecimalDigitNumber ||
category == UnicodeCategory.ConnectorPunctuation ||
category == UnicodeCategory.NonSpacingMark ||
category == UnicodeCategory.SpacingCombiningMark ||
category == UnicodeCategory.Format;
}
}
}
+93 -9
View File
@@ -82,8 +82,8 @@ public class Warehouse
KeyList<string, KeyList<ulong, RemoteTypeDef>> _remoteTypeDefs
= new KeyList<string, KeyList<ulong, RemoteTypeDef>>();
//KeyList<string, KeyList<TypeDefKind, KeyList<uint, RemoteTypeDef>>> _remoteTypeDefs
// = new KeyList<string, KeyList<TypeDefKind, KeyList<uint, RemoteTypeDef>>>();
// Domain -> Kind -> Type Name -> Proxy Type
KeyList<string, KeyList<TypeDefKind, KeyList<string, Type>>> _proxyTypeDefs = new();
Map<string, IAuthenticationProvider> _authenticationProviders = new Map<string, IAuthenticationProvider>();
@@ -579,14 +579,98 @@ public class Warehouse
}
public void IsProxyType(Type type)
public Type TryGetProxyType(TypeDefKind kind, string domain, string name)
{
if (!_proxyTypeDefs.ContainsKey(domain))
return null;
if (!_proxyTypeDefs[domain].ContainsKey(kind))
return null;
if (!_proxyTypeDefs[domain][kind].ContainsKey(name))
return null;
return _proxyTypeDefs[domain][kind][name];
}
public Type GetProxyType(TypeDefKind kind, string domain, string name)
{
if (!_proxyTypeDefs.ContainsKey(domain))
throw new Exception($"No proxy types registered for domain {domain}.");
if (!_proxyTypeDefs[domain].ContainsKey(kind))
throw new Exception($"No proxy types registered for kind {kind} in domain {domain}.");
if (!_proxyTypeDefs[domain][kind].ContainsKey(name))
throw new Exception($"No proxy type registered with name {name} for kind {kind} in domain {domain}.");
return _proxyTypeDefs[domain][kind][name];
}
public void RegisterProxyType(Type type)
{
// make sure the type has remote attribute
var remoteAttr = type.GetCustomAttribute<RemoteAttribute>();
if (remoteAttr == null)
throw new Exception("Proxy type must have Remote attribute.");
//@TODO should add this check t the RemoteAttribute class and use it here, but for now, we will just check the domain and full name format here.
if (!remoteAttr.AreValidDomains())
throw new Exception("Invalid domain in Remote attribute.");
if (!remoteAttr.IsValidFullName())
throw new Exception("Invalid full name in Remote attribute.");
// make sure the type implements IResource or IRecord
if (Codec.ImplementsInterface(type, typeof(IRecord)))
{
foreach (var domain in remoteAttr.Domains)
{
if (!_proxyTypeDefs.ContainsKey(domain))
_proxyTypeDefs.Add(domain, new KeyList<TypeDefKind, KeyList<string, Type>>());
if (!_proxyTypeDefs[domain].ContainsKey(TypeDefKind.Record))
_proxyTypeDefs[domain][TypeDefKind.Record] = new KeyList<string, Type>();
_proxyTypeDefs[domain][TypeDefKind.Record][remoteAttr.FullName] = type;
}
}
else if (Codec.InheritsClass(type, typeof(EpResource)))
{
foreach (var domain in remoteAttr.Domains)
{
if (!_proxyTypeDefs.ContainsKey(domain))
_proxyTypeDefs.Add(domain, new KeyList<TypeDefKind, KeyList<string, Type>>());
if (!_proxyTypeDefs[domain].ContainsKey(TypeDefKind.Resource))
_proxyTypeDefs[domain][TypeDefKind.Resource] = new KeyList<string, Type>();
_proxyTypeDefs[domain][TypeDefKind.Resource][remoteAttr.FullName] = type;
}
}
else if (type.IsEnum)
{
foreach (var domain in remoteAttr.Domains)
{
if (!_proxyTypeDefs.ContainsKey(domain))
_proxyTypeDefs.Add(domain, new KeyList<TypeDefKind, KeyList<string, Type>>());
if (!_proxyTypeDefs[domain].ContainsKey(TypeDefKind.Enum))
_proxyTypeDefs[domain][TypeDefKind.Enum] = new KeyList<string, Type>();
_proxyTypeDefs[domain][TypeDefKind.Enum][remoteAttr.FullName] = type;
}
}
else
{
throw new Exception("Proxy type must implement IResource or IRecord or be an enum.");
}
}
/// <summary>
@@ -759,14 +843,14 @@ public class Warehouse
return _remoteTypeDefs[domain].Values.FirstOrDefault(x => x.Name == typeName);
}
public TypeDef GetRemoteTypeDefByType(Type type)
{
var remoteAttr = type.GetCustomAttribute<RemoteAttribute>();
//public TypeDef GetRemoteTypeDefByType(Type type)
//{
// var remoteAttr = type.GetCustomAttribute<RemoteAttribute>();
if (remoteAttr == null) return null;
// if (remoteAttr == null) return null;
return GetRemoteTypeDefByName(remoteAttr.Domain, remoteAttr.FullName);
}
// return GetRemoteTypeDefByName(remoteAttr.Domain, remoteAttr.FullName);
//}
/// <summary>
/// Get a TypeDef by type name . If not in the warehouse, a new TypeDef is created and added to the warehouse.