diff --git a/Clojure/Clojure/Clojure.csproj b/Clojure/Clojure/Clojure.csproj index bf1921a8..93f0b501 100644 --- a/Clojure/Clojure/Clojure.csproj +++ b/Clojure/Clojure/Clojure.csproj @@ -25,7 +25,6 @@ - Never @@ -67,7 +66,7 @@ --> - + %(Filename)$(Extension) diff --git a/Clojure/Clojure/Lib/ClrTypeSpec.cs b/Clojure/Clojure/Lib/ClrTypeSpec.cs index 94f4c12f..fd91daba 100644 --- a/Clojure/Clojure/Lib/ClrTypeSpec.cs +++ b/Clojure/Clojure/Lib/ClrTypeSpec.cs @@ -1,365 +1,811 @@ using System; using System.Collections.Generic; +using System.IO; using System.Reflection; +using System.Text; + +namespace clojure.lang.TypeName; + +// Inspired by similar code in various places +// See https://github.com/mono/mono/blob/master/mcs/class/corlib/System/TypeSpec.cs +// and see http://www.java2s.com/Open-Source/ASP.NET/Library/sixpack-library/SixPack/Reflection/TypeName.cs.htm +// The EBNF for fully-qualified type names is here: http://msdn.microsoft.com/en-us/library/yfsftwz6(v=VS.100).aspx +// I primarily followed the mono version. Modifications have to do with assembly and type resolver defaults and some minor details. +// Also, rather than throwing exceptions for badly formed names, we just return null. Where this is called, generally, an error is not required. +// +// Giving credit where credit is due: please note the following attributions in the mono code: +// +// Author: +// Rodrigo Kumpera +// +// Copyright (C) 2010 Novell, Inc (http://www.novell.com) +// +// Since my initial pull in 2012, Mono made at least seven commits updating this code. +// 2025.09.11 -- bring this code up to date. + + +// A TypeName is wrapper around type names in display form +// (that is, with special characters escaped). +// +// Note that in general if you unescape a type name, you will +// lose information: If the type name's DisplayName is +// Foo\+Bar+Baz (outer class ``Foo+Bar``, inner class Baz) +// unescaping the first plus will give you (outer class Foo, +// inner class Bar, innermost class Baz). +// +// The correct way to take a TypeName apart is to feed its +// DisplayName to TypeSpec.Parse() +// +public interface IClrTypeName : System.IEquatable +{ + string DisplayName { get; } + + // add a nested name under this one. + IClrTypeName NestedName(IClrTypeIdentifier innerName); +} -namespace clojure.lang +// A type identifier is a single component of a type name. +// Unlike a general typename, a type identifier can be be +// converted to internal form without loss of information. +public interface IClrTypeIdentifier : IClrTypeName { - // Inspired by similar code in various places - // See https://github.com/mono/mono/blob/master/mcs/class/corlib/System/TypeSpec.cs - // and see http://www.java2s.com/Open-Source/ASP.NET/Library/sixpack-library/SixPack/Reflection/TypeName.cs.htm - // The EBNF for fully-qualified type names is here: http://msdn.microsoft.com/en-us/library/yfsftwz6(v=VS.100).aspx - // I primarily followed the mono version. Modifications have to do with assembly and type resolver defaults and some minor details. - // Also, rather than throwing exceptions for badly formed names, we just return null. Where this is called, generally, an error is not required. - // - // Giving credit where credit is due: please note the following attributions in the mono code: - // - // Author: - // Rodrigo Kumpera - // - // Copyright (C) 2010 Novell, Inc (http://www.novell.com) + string InternalName { get; } +} - public class ClrArraySpec +internal class ClrTypeNames +{ + internal static IClrTypeName FromDisplay(string displayName) { + return new Display(displayName); + } - #region Data + internal abstract class ATypeName : IClrTypeName + { + public abstract string DisplayName { get; } - private readonly int _dimensions; - private readonly bool _isBound; + public abstract IClrTypeName NestedName(IClrTypeIdentifier innerName); - #endregion + public bool Equals(IClrTypeName other) + { + return other != null && DisplayName == other.DisplayName; + } - #region C-tors + public override int GetHashCode() + { + return DisplayName.GetHashCode(); + } - internal ClrArraySpec(int dimensions, bool bound) + public override bool Equals(object other) { - this._dimensions = dimensions; - this._isBound = bound; + return Equals(other as IClrTypeName); } + } - #endregion - #region Resolving + internal class Display : ATypeName + { + string _displayName; - internal Type Resolve(Type type) + internal Display(string displayName) { - if (_isBound) - return type.MakeArrayType(1); - else if (_dimensions == 1) - return type.MakeArrayType(); - return type.MakeArrayType(_dimensions); + this._displayName = displayName; + } + + public override string DisplayName { get { return _displayName; } } + + public override IClrTypeName NestedName(IClrTypeIdentifier innerName) + { + return new Display(DisplayName + "+" + innerName.DisplayName); } - #endregion } +} - public class ClrTypeSpec +internal class ClrTypeIdentifiers +{ + + internal static IClrTypeIdentifier FromDisplay(string displayName) { - #region Data + return new Display(displayName); + } - string _name; - string _assemblyName; - List _nested; - List _genericParams; - List _arraySpec; - int _pointerLevel; - bool _isByRef; + private class Display : ClrTypeNames.ATypeName, IClrTypeIdentifier + { + string displayName; + string internal_name; //cached - #endregion + internal Display(string displayName) + { + this.displayName = displayName; + internal_name = null; + } - #region Entry point + public override string DisplayName + { + get { return displayName; } + } - public static Type GetTypeFromName(string name) + public string InternalName { - ClrTypeSpec spec = Parse(name); - if (spec == null) - return null; - return spec.Resolve( - assyName => Assembly.Load(assyName), - //(assy, typeName) => assy == null ? RT.classForName(typeName) : assy.GetType(typeName)); <--- this goes into an infinite loop on a non-existent typename - (assy, typeName) => assy == null ? (name.Equals(typeName) ? null : RT.classForName(typeName)) : assy.GetType(typeName)); + get + { + if (internal_name == null) + internal_name = GetInternalName(); + return internal_name; + } + } + + private string GetInternalName() + { + return ClrTypeSpec.UnescapeInternalName(displayName); + } + + public override IClrTypeName NestedName(IClrTypeIdentifier innerName) + { + return ClrTypeNames.FromDisplay(DisplayName + "+" + innerName.DisplayName); + } + } +} + +public interface IClrModifierSpec +{ + Type Resolve(Type type); + StringBuilder Append(StringBuilder sb); +} + +public class ClrArraySpec : IClrModifierSpec +{ + + // dimensions == 1 and bound, or dimensions > 1 and !bound + private readonly int _dimensions; + private readonly bool _isBound; + + public ClrArraySpec(int dimensions, bool bound) + { + this._dimensions = dimensions; + this._isBound = bound; + } + + public override bool Equals(object obj) + { + var o = obj as ClrArraySpec; + if (o == null) + return false; + return o._dimensions == _dimensions && o._isBound == _isBound; + } + + public override int GetHashCode() + { + return 37 * _dimensions.GetHashCode() + IsBound.GetHashCode(); + } + + public Type Resolve(Type type) + { + if (_isBound) + return type.MakeArrayType(1); + else if (_dimensions == 1) + return type.MakeArrayType(); + return type.MakeArrayType(_dimensions); + } + + public StringBuilder Append(StringBuilder sb) + { + if (_isBound) + return sb.Append("[*]"); + return sb.Append('[') + .Append(',', _dimensions - 1) + .Append(']'); + } + + public override string ToString() => Append(new StringBuilder()).ToString(); + + public int Rank => _dimensions; + + public bool IsBound => _isBound; + + +} + +public class ClrPointerSpec : IClrModifierSpec +{ + int pointer_level; + + public ClrPointerSpec(int pointer_level) + { + this.pointer_level = pointer_level; + } + + public override bool Equals(object obj) + { + var o = obj as ClrPointerSpec; + if (o == null) + return false; + return o.pointer_level == pointer_level; + } + + override public int GetHashCode() => pointer_level.GetHashCode(); + + public Type Resolve(Type type) + { + for (int i = 0; i < pointer_level; ++i) + type = type.MakePointerType(); + return type; + } + + public StringBuilder Append(StringBuilder sb) => sb.Append('*', pointer_level); + + public override string ToString() => Append(new StringBuilder()).ToString(); +} + +public class ClrTypeSpec +{ + #region Data + + IClrTypeIdentifier _name; + string _assemblyName; + List _nested; + List _genericParams; + List _modifierSpec; + bool _isByRef; + + string _displayFullname; // cache + + #endregion + + #region Accessors + + public bool HasModifiers => _modifierSpec is not null; + public bool IsNested => _nested is not null && _nested.Count > 0; + public bool IsByRef => _isByRef; + public IClrTypeName Name => _name; + public string AssemblyName => _assemblyName; + + public IEnumerable Nested + { + get + { + if (_nested != null) + return _nested; + else + return Array.Empty(); } + } + + public IEnumerable Modifiers + { + get + { + if (_modifierSpec != null) + return _modifierSpec; + else + return Array.Empty(); + } + } + + public IEnumerable GenericParams + { + get + { + if (_genericParams != null) + return _genericParams; + else + return Array.Empty(); + } + } - #endregion + #endregion - #region Parsing + #region Display name-related - static ClrTypeSpec Parse(string name) + [Flags] + internal enum DisplayNameFormat + { + Default = 0x0, + WANT_ASSEMBLY = 0x1, + NO_MODIFIERS = 0x2, + } + + //#if DEBUG + public override string ToString() + { + return GetDisplayFullName(DisplayNameFormat.WANT_ASSEMBLY); + } + //#endif + + string GetDisplayFullName(DisplayNameFormat flags) + { + bool wantAssembly = (flags & DisplayNameFormat.WANT_ASSEMBLY) != 0; + bool wantModifiers = (flags & DisplayNameFormat.NO_MODIFIERS) == 0; + + var sb = new StringBuilder(_name.DisplayName); + + if (_nested is not null) { - int pos = 0; - ClrTypeSpec spec = Parse(name, ref pos, false, false); - if (spec == null) - return null; // bad parse - if (pos < name.Length) - return null; // ArgumentException ("Count not parse the whole type name", "typeName"); - return spec; + foreach (var n in _nested) + sb.Append('+').Append(n.DisplayName); + } + + if (_genericParams is not null) + { + sb.Append('['); + for (int i = 0; i < _genericParams.Count; ++i) + { + if (i > 0) + sb.Append(", "); + if (_genericParams[i]._assemblyName != null) + sb.Append('[').Append(_genericParams[i].DisplayFullName).Append(']'); + else + sb.Append(_genericParams[i].DisplayFullName); + } + sb.Append(']'); } - static ClrTypeSpec Parse(string name, ref int p, bool isRecursive, bool allowAssyQualName) + if (wantModifiers) + GetModifierString(sb); + + if (_assemblyName != null && wantAssembly) + sb.Append(", ").Append(_assemblyName); + + return sb.ToString(); + } + + internal string ModifierString() => GetModifierString(new StringBuilder()).ToString(); + + private StringBuilder GetModifierString(StringBuilder sb) + { + if (_modifierSpec is not null) { - int pos = p; - int name_start; - bool hasModifiers = false; - ClrTypeSpec spec = new(); + foreach (var md in _modifierSpec) + md.Append(sb); + } - SkipSpace(name, ref pos); + if (_isByRef) + sb.Append('&'); - name_start = pos; + return sb; + } + + internal string DisplayFullName + { + get + { + if (_displayFullname is null) + _displayFullname = GetDisplayFullName(DisplayNameFormat.Default); + return _displayFullname; + } + } + + internal static string EscapeDisplayName(string internalName) + { + // initial capacity = length of internalName. + // Maybe we won't have to escape anything. + var res = new StringBuilder(internalName.Length); + foreach (char c in internalName) + { + switch (c) + { + case '+': + case ',': + case '[': + case ']': + case '*': + case '&': + case '\\': + res.Append('\\').Append(c); + break; + default: + res.Append(c); + break; + } + } + return res.ToString(); + } + internal static string UnescapeInternalName(string displayName) + { + var res = new StringBuilder(displayName.Length); + for (int i = 0; i < displayName.Length; ++i) + { + char c = displayName[i]; + if (c == '\\') + if (++i < displayName.Length) + c = displayName[i]; + res.Append(c); + } + return res.ToString(); + } + + internal static bool NeedsEscaping(string internalName) + { + foreach (char c in internalName) + { + switch (c) + { + case ',': + case '+': + case '*': + case '&': + case '[': + case ']': + case '\\': + return true; + default: + break; + } + } + return false; + } + + #endregion + + #region Parsing support + + void AddName(string type_name) + { + if (_name is null) + { + _name = ParsedTypeIdentifier(type_name); + } + else + { + if (_nested == null) + _nested = new List(); + _nested.Add(ParsedTypeIdentifier(type_name)); + } + } + + void AddModifier(IClrModifierSpec md) + { + if (_modifierSpec is null) + _modifierSpec = new List(); + _modifierSpec.Add(md); + } + + static void SkipSpace(string name, ref int pos) + { + int p = pos; + while (p < name.Length && Char.IsWhiteSpace(name[p])) + ++p; + pos = p; + } + + static void BoundCheck(int idx, string s) + { + if (idx >= s.Length) + throw new ArgumentException("Invalid generic arguments spec", "typeName"); + } + + static IClrTypeIdentifier ParsedTypeIdentifier(string displayName) + { + return ClrTypeIdentifiers.FromDisplay(displayName); + } + + #endregion + + #region Parsing + + public static ClrTypeSpec Parse(string typeName) + { + int pos = 0; + if (typeName == null) + throw new ArgumentNullException("typeName"); + + ClrTypeSpec res = Parse(typeName, ref pos, false, true); + if (pos < typeName.Length) + throw new ArgumentException("Count not parse the whole type name", "typeName"); + return res; + } + + static ClrTypeSpec Parse(string name, ref int p, bool is_recurse, bool allow_aqn) + { + // Invariants: + // - On exit p, is updated to pos the current unconsumed character. + // + // - The callee peeks at but does not consume delimiters following + // recurisve parse (so for a recursive call like the args of "Foo[P,Q]" + // we'll return with p either on ',' or on ']'. If the name was aqn'd + // "Foo[[P,assmblystuff],Q]" on return p with be on the ']' just + // after the "assmblystuff") + // + // - If allow_aqn is True, assembly qualification is optional. + // If allow_aqn is False, assembly qualification is prohibited. + int pos = p; + int name_start; + bool in_modifiers = false; + ClrTypeSpec data = new(); + + SkipSpace(name, ref pos); + + name_start = pos; + + for (; pos < name.Length; ++pos) + { + switch (name[pos]) + { + case '+': + data.AddName(name.Substring(name_start, pos - name_start)); + name_start = pos + 1; + break; + case ',': + case ']': + data.AddName(name.Substring(name_start, pos - name_start)); + name_start = pos + 1; + in_modifiers = true; + if (is_recurse && !allow_aqn) + { + p = pos; + return data; + } + break; + case '&': + case '*': + case '[': + if (name[pos] != '[' && is_recurse) + throw new ArgumentException("Generic argument can't be byref or pointer type", "typeName"); + data.AddName(name.Substring(name_start, pos - name_start)); + name_start = pos + 1; + in_modifiers = true; + break; + case '\\': + pos++; + break; + } + if (in_modifiers) + break; + } + + if (name_start < pos) + data.AddName(name.Substring(name_start, pos - name_start)); + else if (name_start == pos) + data.AddName(String.Empty); + + if (in_modifiers) + { for (; pos < name.Length; ++pos) { + switch (name[pos]) { - case '+': - spec.AddName(name.Substring(name_start, pos - name_start)); - name_start = pos + 1; + case '&': + if (data._isByRef) + throw new ArgumentException("Can't have a byref of a byref", "typeName"); + + data._isByRef = true; + break; + case '*': + if (data._isByRef) + throw new ArgumentException("Can't have a pointer to a byref type", "typeName"); + // take subsequent '*'s too + int pointer_level = 1; + while (pos + 1 < name.Length && name[pos + 1] == '*') + { + ++pos; + ++pointer_level; + } + data.AddModifier(new ClrPointerSpec(pointer_level)); break; case ',': - case ']': - spec.AddName(name.Substring(name_start, pos - name_start)); - name_start = pos + 1; - if (isRecursive && !allowAssyQualName) + if (is_recurse && allow_aqn) + { + int end = pos; + while (end < name.Length && name[end] != ']') + ++end; + if (end >= name.Length) + throw new ArgumentException("Unmatched ']' while parsing generic argument assembly name"); + data._assemblyName = name.Substring(pos + 1, end - pos - 1).Trim(); + p = end; + return data; + } + if (is_recurse) { p = pos; - return spec; + return data; + } + if (allow_aqn) + { + data._assemblyName = name.Substring(pos + 1).Trim(); + pos = name.Length; } - hasModifiers = true; break; - case '&': - case '*': case '[': - if (name[pos] != '[' && isRecursive) - return null; // ArgumentException ("Generic argument can't be byref or pointer type", "typeName"); - spec.AddName(name.Substring(name_start, pos - name_start)); - name_start = pos + 1; - hasModifiers = true; - break; - case '\\': - pos++; - break; - } - if (hasModifiers) - break; - } - - if (name_start < pos) - spec.AddName(name.Substring(name_start, pos - name_start)); - - if (hasModifiers) - { - for (; pos < name.Length; ++pos) - { - - switch (name[pos]) - { - case '&': - if (spec._isByRef) - return null; // ArgumentException ("Can't have a byref of a byref", "typeName") - - spec._isByRef = true; - break; - case '*': - if (spec._isByRef) - return null; // ArgumentException ("Can't have a pointer to a byref type", "typeName"); - ++spec._pointerLevel; - break; - case ',': - if (isRecursive) + if (data._isByRef) + throw new ArgumentException("Byref qualifier must be the last one of a type", "typeName"); + ++pos; + if (pos >= name.Length) + throw new ArgumentException("Invalid array/generic spec", "typeName"); + SkipSpace(name, ref pos); + + if (name[pos] != ',' && name[pos] != '*' && name[pos] != ']') + {//generic args + List args = new(); + if (data.HasModifiers) + throw new ArgumentException("generic args after array spec or pointer type", "typeName"); + + while (pos < name.Length) { - int end = pos; - while (end < name.Length && name[end] != ']') - ++end; - if (end >= name.Length) - return null; // ArgumentException ("Unmatched ']' while parsing generic argument assembly name"); - spec._assemblyName = name.Substring(pos + 1, end - pos - 1).Trim(); - p = end + 1; - return spec; - } - spec._assemblyName = name.Substring(pos + 1).Trim(); - pos = name.Length; - break; - case '[': - if (spec._isByRef) - return null; // ArgumentException ("Byref qualifier must be the last one of a type", "typeName"); - ++pos; - if (pos >= name.Length) - return null; // ArgumentException ("Invalid array/generic spec", "typeName"); - SkipSpace(name, ref pos); - - if (name[pos] != ',' && name[pos] != '*' && name[pos] != ']') - {//generic args - List args = new(); - if (spec.IsArray) - return null; // ArgumentException ("generic args after array spec", "typeName"); - - while (pos < name.Length) + SkipSpace(name, ref pos); + bool aqn = name[pos] == '['; + if (aqn) + ++pos; //skip '[' to the start of the type + args.Add(Parse(name, ref pos, true, aqn)); + BoundCheck(pos, name); + if (aqn) { - SkipSpace(name, ref pos); - bool aqn = name[pos] == '['; - if (aqn) - ++pos; //skip '[' to the start of the type - { - ClrTypeSpec arg = Parse(name, ref pos, true, aqn); - if (arg == null) - return null; // bad generic arg - args.Add(arg); - } - if (pos >= name.Length) - return null; // ArgumentException ("Invalid generic arguments spec", "typeName"); - if (name[pos] == ']') - break; - if (name[pos] == ',') - ++pos; // skip ',' to the start of the next arg + ++pos; else - return null; // ArgumentException ("Invalid generic arguments separator " + name [pos], "typeName") - + throw new ArgumentException("Unclosed assembly-qualified type name at " + name[pos], "typeName"); // Is this possible? AQN Ends with ] pending. + BoundCheck(pos, name); } - if (pos >= name.Length || name[pos] != ']') - return null; // ArgumentException ("Error parsing generic params spec", "typeName"); - spec._genericParams = args; + + if (name[pos] == ']') + break; + if (name[pos] == ',') + ++pos; // skip ',' to the start of the next arg + else + throw new ArgumentException("Invalid generic arguments separator " + name[pos], "typeName"); + } - else - { //array spec - int dimensions = 1; - bool bound = false; - while (pos < name.Length && name[pos] != ']') + if (pos >= name.Length || name[pos] != ']') + throw new ArgumentException("Error parsing generic params spec", "typeName"); + data._genericParams = args; + } + else + { //array spec + int dimensions = 1; + bool bound = false; + while (pos < name.Length && name[pos] != ']') + { + if (name[pos] == '*') { - if (name[pos] == '*') - { - if (bound) - return null; // ArgumentException ("Array spec cannot have 2 bound dimensions", "typeName"); - bound = true; - } - else if (name[pos] != ',') - return null; // ArgumentException ("Invalid character in array spec " + name [pos], "typeName"); - else - ++dimensions; - - ++pos; - SkipSpace(name, ref pos); + if (bound) + throw new ArgumentException("Array spec cannot have 2 bound dimensions", "typeName"); + bound = true; } - if (name[pos] != ']') - return null; // ArgumentException ("Error parsing array spec", "typeName"); - if (dimensions > 1 && bound) - return null; // ArgumentException ("Invalid array spec, multi-dimensional array cannot be bound", "typeName") - spec.AddArray(new ClrArraySpec(dimensions, bound)); - } + else if (name[pos] != ',') + throw new ArgumentException("Invalid character in array spec " + name[pos], "typeName"); + else + ++dimensions; - break; - case ']': - if (isRecursive) - { - p = pos + 1; - return spec; + ++pos; + SkipSpace(name, ref pos); } - return null; // ArgumentException ("Unmatched ']'", "typeName"); - default: - return null; // ArgumentException ("Bad type def, can't handle '" + name [pos]+"'" + " at " + pos, "typeName"); - } + if (pos >= name.Length || name[pos] != ']') + throw new ArgumentException("Error parsing array spec", "typeName"); + if (dimensions > 1 && bound) + throw new ArgumentException("Invalid array spec, multi-dimensional array cannot be bound", "typeName"); + data.AddModifier(new ClrArraySpec(dimensions, bound)); + } + + break; + case ']': + if (is_recurse) + { + p = pos; + return data; + } + throw new ArgumentException("Unmatched ']'", "typeName"); + default: + throw new ArgumentException("Bad type def, can't handle '" + name[pos] + "'" + " at " + pos, "typeName"); } } - - p = pos; - return spec; } + p = pos; + return data; + } - void AddName(string type_name) - { - if (_name == null) - { - _name = type_name; - } - else - { - if (_nested == null) - _nested = new List(); - _nested.Add(type_name); - } - } + #endregion - static void SkipSpace(string name, ref int pos) - { - int p = pos; - while (p < name.Length && Char.IsWhiteSpace(name[p])) - ++p; - pos = p; - } + #region Resolving - bool IsArray - { - get { return _arraySpec != null; } - } + // We have to get rid of all references to StackCrawlMark -- just not something we have access to. - void AddArray(ClrArraySpec array) - { - if (_arraySpec == null) - _arraySpec = new List(); - _arraySpec.Add(array); - } + public Type Resolve( + Func assemblyResolver, + Func typeResolver, + bool throwOnError, + bool ignoreCase /*, ref System.Threading.StackCrawlMark stackMark*/) + { + Assembly asm = null; - #endregion + //if (assemblyResolver == null && typeResolver == null) + // return RuntimeType.GetType(DisplayFullName, throwOnError, ignoreCase, false, ref stackMark); + // We don't have access to RuntimeType, so we just punt. We will always call with one or the other of assemblyResolver or typeResolver. - #region Resolving + if (assemblyResolver is null && typeResolver is null) + throw new ArgumentException("At least one of assemblyResolver or typeResolver must be non-null"); - internal Type Resolve(Func assemblyResolver, Func typeResolver) + if (_assemblyName != null) { - Assembly asm = null; + if (assemblyResolver != null) + asm = assemblyResolver(new AssemblyName(_assemblyName)); + else + asm = Assembly.Load(_assemblyName); - if (_assemblyName != null) + if (asm == null) { - if (assemblyResolver != null) - asm = assemblyResolver(new AssemblyName(_assemblyName)); - else - asm = Assembly.Load(_assemblyName); - - if (asm == null) - return null; + if (throwOnError) + throw new FileNotFoundException("Could not resolve assembly '" + _assemblyName + "'"); + return null; } + } - Type type = typeResolver(asm, _name); - if (type == null) - return null; + Type type = null; + if (typeResolver is not null) + type = typeResolver(asm, _name.DisplayName, ignoreCase); + else + type = asm.GetType(_name.DisplayName, false, ignoreCase); - if (_nested != null) + if (type is null) + { + if (throwOnError) + throw new TypeLoadException("Could not resolve type '" + _name + "'"); + return null; + } + + if (_nested != null) + { + foreach (var n in _nested) { - foreach (var n in _nested) + var tmp = type.GetNestedType(n.DisplayName, BindingFlags.Public | BindingFlags.NonPublic); + if (tmp == null) { - var tmp = type.GetNestedType(n, BindingFlags.Public | BindingFlags.NonPublic); - if (tmp == null) - return null; - type = tmp; + if (throwOnError) + throw new TypeLoadException("Could not resolve type '" + n + "'"); + return null; } + type = tmp; } + } - if (_genericParams != null) + if (_genericParams != null) + { + Type[] args = new Type[_genericParams.Count]; + for (int i = 0; i < args.Length; ++i) { - Type[] args = new Type[_genericParams.Count]; - for (int i = 0; i < args.Length; ++i) + var tmp = _genericParams[i].Resolve(assemblyResolver, typeResolver, throwOnError, ignoreCase /*, ref stackMark */); + if (tmp == null) { - var tmp = _genericParams[i].Resolve(assemblyResolver, typeResolver); - if (tmp == null) - return null; - args[i] = tmp; + if (throwOnError) + throw new TypeLoadException("Could not resolve type '" + _genericParams[i]._name + "'"); + return null; } - type = type.MakeGenericType(args); + args[i] = tmp; } + type = type.MakeGenericType(args); + } - if (_arraySpec != null) - { - foreach (var arr in _arraySpec) - type = arr.Resolve(type); - } + if (_modifierSpec != null) + { + foreach (var md in _modifierSpec) + type = md.Resolve(type); + } - for (int i = 0; i < _pointerLevel; ++i) - type = type.MakePointerType(); + if (_isByRef) + type = type.MakeByRefType(); - if (_isByRef) - type = type.MakeByRefType(); + return type; + } + + #endregion - return type; - } - #endregion + #region Entry point + + public static Type GetTypeFromName(string name) + { + ClrTypeSpec spec = Parse(name); + if (spec == null) + return null; + return spec.Resolve( + assyName => Assembly.Load(assyName), + //(assy, typeName) => assy == null ? RT.classForName(typeName) : assy.GetType(typeName)); <--- this goes into an infinite loop on a non-existent typename + (assy, typeName, ignoreCase) => assy == null ? (name.Equals(typeName) ? null : RT.classForName(typeName)) : assy.GetType(typeName), + false, + false); } + + #endregion + } diff --git a/Clojure/Clojure/Lib/ClrTypeSpec2.cs b/Clojure/Clojure/Lib/ClrTypeSpec2.cs index 1725dd6e..60d36e7e 100644 --- a/Clojure/Clojure/Lib/ClrTypeSpec2.cs +++ b/Clojure/Clojure/Lib/ClrTypeSpec2.cs @@ -1,424 +1,1288 @@ -using clojure.lang.CljCompiler.Ast; -using System; +using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; +using System.Text; + +namespace clojure.lang.TypeName2; + +// Inspired by similar code in various places +// See https://github.com/mono/mono/blob/master/mcs/class/corlib/System/TypeSpec.cs +// and see http://www.java2s.com/Open-Source/ASP.NET/Library/sixpack-library/SixPack/Reflection/TypeName.cs.htm +// The EBNF for fully-qualified type names is here: http://msdn.microsoft.com/en-us/library/yfsftwz6(v=VS.100).aspx +// I primarily followed the mono version. Modifications have to do with assembly and type resolver defaults and some minor details. +// Also, rather than throwing exceptions for badly formed names, we just return null. Where this is called, generally, an error is not required. +// +// Giving credit where credit is due: please note the following attributions in the mono code: +// +// Author: +// Rodrigo Kumpera +// +// Copyright (C) 2010 Novell, Inc (http://www.novell.com) +// +// Since my initial pull in 2012, Mono made at least seven commits updating this code. +// 2025.09.11 -- bring this code up to date. + + +// A TypeName is wrapper around type names in display form +// (that is, with special characters escaped). +// +// Note that in general if you unescape a type name, you will +// lose information: If the type name's DisplayName is +// Foo\+Bar+Baz (outer class ``Foo+Bar``, inner class Baz) +// unescaping the first plus will give you (outer class Foo, +// inner class Bar, innermost class Baz). +// +// The correct way to take a TypeName apart is to feed its +// DisplayName to TypeSpec.Parse() +// +public interface IClrTypeName : IEquatable +{ + string DisplayName { get; } + + int ImplicitGenericCount { get; set; } // used in some places to track generic arity + + + // add a nested name under this one. + //IClrTypeName NestedName(IClrTypeIdentifier innerName); +} -namespace clojure.lang +// A type identifier is a single component of a type name. +// Unlike a general typename, a type identifier can be be +// converted to internal form without loss of information. +public interface IClrTypeIdentifier : IClrTypeName { - // Inspired by similar code in various places - // See https://github.com/mono/mono/blob/master/mcs/class/corlib/System/TypeSpec.cs - // and see http://www.java2s.com/Open-Source/ASP.NET/Library/sixpack-library/SixPack/Reflection/TypeName.cs.htm - // The EBNF for fully-qualified type names is here: http://msdn.microsoft.com/en-us/library/yfsftwz6(v=VS.100).aspx - // I primarily followed the mono version. Modifications have to do with assembly and type resolver defaults and some minor details. - // Also, rather than throwing exceptions for badly formed names, we just return null. Where this is called, generally, an error is not required. - // - // Giving credit where credit is due: please note the following attributions in the mono code: - // - // Author: - // Rodrigo Kumpera - // - // Copyright (C) 2010 Novell, Inc (http://www.novell.com) + string InternalName { get; } +} - class ClrArraySpec2 +internal class ClrTypeNames +{ + internal static IClrTypeName FromDisplay(string displayName) { + return new Display(displayName); + } - #region Data + internal abstract class ATypeName : IClrTypeName + { + public abstract string DisplayName { get; } + public int ImplicitGenericCount { get; set; } = 0; - private readonly int _dimensions; - private readonly bool _isBound; + //public abstract IClrTypeName NestedName(IClrTypeIdentifier innerName); - #endregion + public bool Equals(IClrTypeName other) + { + return other != null && DisplayName == other.DisplayName; + } - #region C-tors + public override int GetHashCode() + { + return DisplayName.GetHashCode(); + } - internal ClrArraySpec2(int dimensions, bool bound) + public override bool Equals(object other) { - this._dimensions = dimensions; - this._isBound = bound; + return Equals(other as IClrTypeName); } + } - #endregion - #region Resolving + internal class Display : ATypeName + { + string _displayName; - internal Type Resolve(Type type) + internal Display(string displayName) { - if (_isBound) - return type.MakeArrayType(1); - else if (_dimensions == 1) - return type.MakeArrayType(); - return type.MakeArrayType(_dimensions); + this._displayName = displayName; } - #endregion - } + public override string DisplayName { get { return _displayName; } } - public class ClrTypeSpec2 - { - #region Data + //public override IClrTypeName NestedName(IClrTypeIdentifier innerName) + //{ + // return new Display(DisplayName + "+" + innerName.DisplayName); + //} + + } +} - string _name; - string _assemblyName; - List _nested; - List _genericParams; - List _arraySpec; - int _pointerLevel; - bool _isByRef; +internal class ClrTypeIdentifiers +{ - #endregion + internal static IClrTypeIdentifier FromDisplay(string displayName) + { + return new Display(displayName); + } - #region Entry point + private class Display : ClrTypeNames.ATypeName, IClrTypeIdentifier + { + string displayName; + string internal_name; //cached - public static Type GetTypeFromName(string name, Namespace ns = null) + internal Display(string displayName) { - ClrTypeSpec2 spec = Parse(name); - if (spec == null) - return null; - return spec.Resolve( - ns, - name, - assyName => Assembly.Load(assyName)); + this.displayName = displayName; + internal_name = null; } - #endregion - - #region Parsing + public override string DisplayName + { + get { return displayName; } + } - static ClrTypeSpec2 Parse(string name) + public string InternalName { - int pos = 0; - ClrTypeSpec2 spec = Parse(name, ref pos, false, false); - if (spec == null) - return null; // bad parse - if (pos < name.Length) - return null; // ArgumentException ("Count not parse the whole type name", "typeName"); - return spec; + get + { + if (internal_name == null) + internal_name = GetInternalName(); + return internal_name; + } } - static ClrTypeSpec2 Parse(string name, ref int p, bool isRecursive, bool allowAssyQualName) + private string GetInternalName() { - int pos = p; - int name_start; - bool hasModifiers = false; - ClrTypeSpec2 spec = new(); + return ClrTypeSpec.UnescapeInternalName(displayName); + } - SkipSpace(name, ref pos); + //public override IClrTypeName NestedName(IClrTypeIdentifier innerName) + //{ + // return ClrTypeNames.FromDisplay(DisplayName + "+" + innerName.DisplayName); + //} + } +} - name_start = pos; +public interface IClrModifierSpec +{ + Type Resolve(Type type); + StringBuilder Append(StringBuilder sb); +} - for (; pos < name.Length; ++pos) - { - switch (name[pos]) - { - case '+': - spec.AddName(name.Substring(name_start, pos - name_start)); - name_start = pos + 1; - break; - case ',': - case ']': - spec.AddName(name.Substring(name_start, pos - name_start)); - name_start = pos + 1; - if (isRecursive && !allowAssyQualName) - { - p = pos; - return spec; - } - hasModifiers = true; - break; - case '&': - case '*': - case '[': - if (name[pos] != '[' && name[pos] != '<' && isRecursive) - return null; // ArgumentException ("Generic argument can't be byref or pointer type", "typeName"); - spec.AddName(name.Substring(name_start, pos - name_start)); - name_start = pos + 1; - hasModifiers = true; - break; - case '\\': - pos++; - break; - } - if (hasModifiers) - break; - } +public class ClrArraySpec : IClrModifierSpec +{ - if (name_start < pos) - spec.AddName(name.Substring(name_start, pos - name_start)); + // dimensions == 1 and bound, or dimensions > 1 and !bound + private readonly int _dimensions; + private readonly bool _isBound; - if (hasModifiers) - { - for (; pos < name.Length; ++pos) - { + public ClrArraySpec(int dimensions, bool bound) + { + this._dimensions = dimensions; + this._isBound = bound; + } - switch (name[pos]) - { - case '&': - if (spec._isByRef) - return null; // ArgumentException ("Can't have a byref of a byref", "typeName") - - spec._isByRef = true; - break; - case '*': - if (spec._isByRef) - return null; // ArgumentException ("Can't have a pointer to a byref type", "typeName"); - ++spec._pointerLevel; - break; - case ',': - if (isRecursive) - { - int end = pos; - while (end < name.Length && name[end] != ']') - ++end; - if (end >= name.Length) - return null; // ArgumentException ("Unmatched ']' while parsing generic argument assembly name"); - spec._assemblyName = name.Substring(pos + 1, end - pos - 1).Trim(); - p = end + 1; - return spec; - } - spec._assemblyName = name.Substring(pos + 1).Trim(); - pos = name.Length; - break; - case '[': - if (spec._isByRef) - return null; // ArgumentException ("Byref qualifier must be the last one of a type", "typeName"); - ++pos; - if (pos >= name.Length) - return null; // ArgumentException ("Invalid array/generic spec", "typeName"); - SkipSpace(name, ref pos); + public override bool Equals(object obj) + { + var o = obj as ClrArraySpec; + if (o == null) + return false; + return o._dimensions == _dimensions && o._isBound == _isBound; + } - if (name[pos] != ',' && name[pos] != '*' && name[pos] != ']') - {//generic args - List args = new(); - if (spec.IsArray) - return null; // ArgumentException ("generic args after array spec", "typeName"); + public override int GetHashCode() + { + return 37 * _dimensions.GetHashCode() + IsBound.GetHashCode(); + } - while (pos < name.Length) - { - SkipSpace(name, ref pos); - bool aqn = name[pos] == '['; - if (aqn) - ++pos; //skip '[' to the start of the type - { - ClrTypeSpec2 arg = Parse(name, ref pos, true, aqn); - if (arg == null) - return null; // bad generic arg - args.Add(arg); - } - if (pos >= name.Length) - return null; // ArgumentException ("Invalid generic arguments spec", "typeName"); + public Type Resolve(Type type) + { + if (_isBound) + return type.MakeArrayType(1); + else if (_dimensions == 1) + return type.MakeArrayType(); + return type.MakeArrayType(_dimensions); + } - if (name[pos] == ']') - break; - if (name[pos] == ',') - ++pos; // skip ',' to the start of the next arg - else - return null; // ArgumentException ("Invalid generic arguments separator " + name [pos], "typeName") + public StringBuilder Append(StringBuilder sb) + { + if (_isBound) + return sb.Append("[*]"); + return sb.Append('[') + .Append(',', _dimensions - 1) + .Append(']'); + } - } - if (pos >= name.Length || name[pos] != ']') - return null; // ArgumentException ("Error parsing generic params spec", "typeName"); - spec._genericParams = args; - } - else - { //array spec - int dimensions = 1; - bool bound = false; - while (pos < name.Length && name[pos] != ']') - { - if (name[pos] == '*') - { - if (bound) - return null; // ArgumentException ("Array spec cannot have 2 bound dimensions", "typeName"); - bound = true; - } - else if (name[pos] != ',') - return null; // ArgumentException ("Invalid character in array spec " + name [pos], "typeName"); - else - ++dimensions; + public override string ToString() => Append(new StringBuilder()).ToString(); - ++pos; - SkipSpace(name, ref pos); - } - if (name[pos] != ']') - return null; // ArgumentException ("Error parsing array spec", "typeName"); - if (dimensions > 1 && bound) - return null; // ArgumentException ("Invalid array spec, multi-dimensional array cannot be bound", "typeName") - spec.AddArray(new ClrArraySpec2(dimensions, bound)); - } + public int Rank => _dimensions; - break; + public bool IsBound => _isBound; - case ']': - if (isRecursive) - { - p = pos + 1; - return spec; - } - return null; // ArgumentException ("Unmatched ']'", "typeName"); - default: - return null; // ArgumentException ("Bad type def, can't handle '" + name [pos]+"'" + " at " + pos, "typeName"); - } - } - } - p = pos; - return spec; +} + +public class ClrPointerSpec : IClrModifierSpec +{ + int pointer_level; + + public ClrPointerSpec(int pointer_level) + { + this.pointer_level = pointer_level; + } + + public override bool Equals(object obj) + { + var o = obj as ClrPointerSpec; + if (o == null) + return false; + return o.pointer_level == pointer_level; + } + + override public int GetHashCode() => pointer_level.GetHashCode(); + + public Type Resolve(Type type) + { + for (int i = 0; i < pointer_level; ++i) + type = type.MakePointerType(); + return type; + } + + public StringBuilder Append(StringBuilder sb) => sb.Append('*', pointer_level); + + public override string ToString() => Append(new StringBuilder()).ToString(); +} + +public class ClrTypeSpec +{ + #region Data + + IClrTypeIdentifier _name; + string _assemblyName; + List _nested; + List _genericParams; + List _modifierSpec; + bool _isByRef; + + string _displayFullname; // cache + + #endregion + + #region Accessors + + public bool HasModifiers => _modifierSpec is not null; + public bool IsNested => _nested is not null && _nested.Count > 0; + public bool IsByRef => _isByRef; + public IClrTypeName Name => _name; + public string AssemblyName => _assemblyName; + + public IEnumerable Nested + { + get + { + if (_nested != null) + return _nested; + else + return Array.Empty(); } + } + public IEnumerable Modifiers + { + get + { + if (_modifierSpec != null) + return _modifierSpec; + else + return Array.Empty(); + } + } - void AddName(string type_name) + public IEnumerable GenericParams + { + get { - if (_name == null) - { - _name = type_name; - } + if (_genericParams != null) + return _genericParams; else + return Array.Empty(); + } + } + + #endregion + + #region Display name-related + + [Flags] + internal enum DisplayNameFormat + { + Default = 0x0, + WANT_ASSEMBLY = 0x1, + NO_MODIFIERS = 0x2, + } + + //#if DEBUG + public override string ToString() + { + return GetDisplayFullName(DisplayNameFormat.WANT_ASSEMBLY); + } + //#endif + + string GetDisplayFullName(DisplayNameFormat flags) + { + bool wantAssembly = (flags & DisplayNameFormat.WANT_ASSEMBLY) != 0; + bool wantModifiers = (flags & DisplayNameFormat.NO_MODIFIERS) == 0; + + var sb = new StringBuilder(_name.DisplayName); + + if (_nested is not null) + { + foreach (var n in _nested) + sb.Append('+').Append(n.DisplayName); + } + + if (_genericParams is not null) + { + sb.Append('['); + for (int i = 0; i < _genericParams.Count; ++i) { - if (_nested == null) - _nested = new List(); - _nested.Add(type_name); + if (i > 0) + sb.Append(", "); + if (_genericParams[i]._assemblyName != null) + sb.Append('[').Append(_genericParams[i].DisplayFullName).Append(']'); + else + sb.Append(_genericParams[i].DisplayFullName); } + sb.Append(']'); + } + + if (wantModifiers) + GetModifierString(sb); + + if (_assemblyName != null && wantAssembly) + sb.Append(", ").Append(_assemblyName); + + return sb.ToString(); + } + + internal string ModifierString() => GetModifierString(new StringBuilder()).ToString(); + + private StringBuilder GetModifierString(StringBuilder sb) + { + if (_modifierSpec is not null) + { + foreach (var md in _modifierSpec) + md.Append(sb); } - static string AppendGenericCountSuffix(string name, int count) + if (_isByRef) + sb.Append('&'); + + return sb; + } + + internal string DisplayFullName + { + get { - return $"{name}`{count}"; + if (_displayFullname is null) + _displayFullname = GetDisplayFullName(DisplayNameFormat.Default); + return _displayFullname; } + } - void AppendNameGenericCountSuffix(int count) + internal static string EscapeDisplayName(string internalName) + { + // initial capacity = length of internalName. + // Maybe we won't have to escape anything. + var res = new StringBuilder(internalName.Length); + foreach (char c in internalName) { - if (_nested is not null) + switch (c) { - var name = _nested.Last(); - _nested.RemoveAt(_nested.Count - 1); - _nested.Add(AppendGenericCountSuffix(name, count)); + case '+': + case ',': + case '[': + case ']': + case '*': + case '&': + case '\\': + res.Append('\\').Append(c); + break; + default: + res.Append(c); + break; } - else + } + return res.ToString(); + } + + internal static string UnescapeInternalName(string displayName) + { + var res = new StringBuilder(displayName.Length); + for (int i = 0; i < displayName.Length; ++i) + { + char c = displayName[i]; + if (c == '\\') + if (++i < displayName.Length) + c = displayName[i]; + res.Append(c); + } + return res.ToString(); + } + + internal static bool NeedsEscaping(string internalName) + { + foreach (char c in internalName) + { + switch (c) { - _name = AppendGenericCountSuffix(_name, count); + case ',': + case '+': + case '*': + case '&': + case '[': + case ']': + case '\\': + return true; + default: + break; } } + return false; + } + + #endregion + + #region Parsing support + + void AddName(string type_name) + { + if (_name is null) + { + _name = ParsedTypeIdentifier(type_name); + } + else + { + if (_nested == null) + _nested = new List(); + _nested.Add(ParsedTypeIdentifier(type_name)); + } + } + + void AddModifier(IClrModifierSpec md) + { + if (_modifierSpec is null) + _modifierSpec = new List(); + _modifierSpec.Add(md); + } + + static void SkipSpace(string name, ref int pos) + { + int p = pos; + while (p < name.Length && Char.IsWhiteSpace(name[p])) + ++p; + pos = p; + } + + static void BoundCheck(int idx, string s) + { + if (idx >= s.Length) + throw new ArgumentException("Invalid generic arguments spec", "typeName"); + } + + static IClrTypeIdentifier ParsedTypeIdentifier(string displayName) + { + return ClrTypeIdentifiers.FromDisplay(displayName); + } + + void SetGenericArgumentCount(int count) + { + if (_name == null) + throw new InvalidOperationException("Type name not set"); + + if (IsNested) + _nested.Last().ImplicitGenericCount = count; + else + _name.ImplicitGenericCount = count; + } + + void MergeNested(ClrTypeSpec nestedSpec) + { + _assemblyName = nestedSpec.AssemblyName; - static void SkipSpace(string name, ref int pos) + // Append all nested names to the current type + if (_nested == null) + _nested = new List(); + _nested.Add(nestedSpec._name); + + if (nestedSpec._nested != null) { - int p = pos; - while (p < name.Length && Char.IsWhiteSpace(name[p])) - ++p; - pos = p; + _nested.AddRange(nestedSpec._nested); } - bool IsArray + // append any generic arguments to the current type + if (nestedSpec._genericParams != null) + { + if (_genericParams == null) + _genericParams = new List(); + _genericParams.AddRange(nestedSpec._genericParams); + } + // append any modifiers to the current type + if (nestedSpec._modifierSpec != null) { - get { return _arraySpec != null; } + if (_modifierSpec == null) + _modifierSpec = new List(); + _modifierSpec.AddRange(nestedSpec._modifierSpec); } - void AddArray(ClrArraySpec2 array) + // byref flag + if (nestedSpec._isByRef) { - if (_arraySpec == null) - _arraySpec = new List(); - _arraySpec.Add(array); + if (_isByRef) + throw new ArgumentException("Can't have a byref of a byref", "typeName"); + _isByRef = true; } + } - #endregion - #region Resolving + #endregion - internal Type Resolve( - Namespace ns, - string originalTypename, - Func assemblyResolver) - { - Assembly asm = null; + #region Parsing - if (_assemblyName != null) - { - if (assemblyResolver != null) - asm = assemblyResolver(new AssemblyName(_assemblyName)); - else - asm = Assembly.Load(_assemblyName); + public static ClrTypeSpec Parse(string typeName) + { + int pos = 0; + if (typeName == null) + throw new ArgumentNullException("typeName"); + + ClrTypeSpec res = Parse(typeName, ref pos, false, true, true); + if (pos < typeName.Length) + throw new ArgumentException("Count not parse the whole type name", "typeName"); + return res; + } - if (asm == null) - return null; + static ClrTypeSpec Parse(string name, ref int p, bool is_recurse, bool allow_aqn, bool junk) + { + // Invariants: + // - On exit p, is updated to pos the current unconsumed character. + // + // - The callee peeks at but does not consume delimiters following + // recurisve parse (so for a recursive call like the args of "Foo[P,Q]" + // we'll return with p either on ',' or on ']'. If the name was aqn'd + // "Foo[[P,assmblystuff],Q]" on return p with be on the ']' just + // after the "assmblystuff") + // + // - If allow_aqn is True, assembly qualification is optional. + // If allow_aqn is False, assembly qualification is prohibited. + // - If is_recurse is True, we are parsing a generic argument. + // If is_recurse is False, we are parsing a top-level type name. + // - If allow_mods is False, we are recursively parsing a nested name just after a generic argument list. + // In this case, we allow modifiers (array, pointer, byref) after the generic argument list. + int pos = p; + + int name_start; + bool in_modifiers = false; + ClrTypeSpec data = new(); + + SkipSpace(name, ref pos); + + name_start = pos; + + for (; pos < name.Length; ++pos) + { + switch (name[pos]) + { + case '+': + data.AddName(name.Substring(name_start, pos - name_start)); + name_start = pos + 1; + break; + case ',': + case ']': + data.AddName(name.Substring(name_start, pos - name_start)); + name_start = pos + 1; + in_modifiers = true; + if (is_recurse && !allow_aqn) + { + p = pos; + return data; + } + break; + case '&': + case '*': + case '[': + if (name[pos] != '[' && is_recurse) + throw new ArgumentException("Generic argument can't be byref or pointer type", "typeName"); + data.AddName(name.Substring(name_start, pos - name_start)); + name_start = pos + 1; + in_modifiers = true; + break; + case '\\': + pos++; + break; } + if (in_modifiers) + break; + } - // if _name is same as originalTypename, then the parse is identical to what we started with. - // Given that ClrTypeSpec2.GetTypeFromName is called from RT.classForName, - // call RT.classForName when _name == originalTypename will set off an infinite recrusion. - - Type type = null; + if (name_start < pos) + data.AddName(name.Substring(name_start, pos - name_start)); + else if (name_start == pos) + data.AddName(String.Empty); - if (asm != null) - type = asm.GetType(_name); - else + if (in_modifiers) + { + for (; pos < name.Length; ++pos) { - type = HostExpr.maybeSpecialTag(Symbol.create(_name)); - // check for aliases in the namespace - if (type is null && ns is not null) + switch (name[pos]) { - type = ns.GetMapping(Symbol.create(_name)) as Type; - } + case '&': + if (data._isByRef) + throw new ArgumentException("Can't have a byref of a byref", "typeName"); + + data._isByRef = true; + break; + case '*': + if (data._isByRef) + throw new ArgumentException("Can't have a pointer to a byref type", "typeName"); + // take subsequent '*'s too + int pointer_level = 1; + while (pos + 1 < name.Length && name[pos + 1] == '*') + { + ++pos; + ++pointer_level; + } + data.AddModifier(new ClrPointerSpec(pointer_level)); + break; + case ',': + if (is_recurse && allow_aqn) + { + int end = pos; + while (end < name.Length && name[end] != ']') + ++end; + if (end >= name.Length) + throw new ArgumentException("Unmatched ']' while parsing generic argument assembly name"); + data._assemblyName = name.Substring(pos + 1, end - pos - 1).Trim(); + p = end; + return data; + } + if (is_recurse) + { + p = pos; + return data; + } + if (allow_aqn) + { + data._assemblyName = name.Substring(pos + 1).Trim(); + pos = name.Length; + } + break; + case '[': + + // We need indefinite lookahead (SkipSpace) to figure out if we have generic arguments or an array spec. + // We cache the current position so we can restore it in case we need to exit (when array spec && ! allow_mods) + + int pos_cache = pos; + + if (data._isByRef) + throw new ArgumentException("Byref qualifier must be the last one of a type", "typeName"); + ++pos; + if (pos >= name.Length) + throw new ArgumentException("Invalid array/generic spec", "typeName"); + SkipSpace(name, ref pos); + + if (name[pos] != ',' && name[pos] != '*' && name[pos] != ']') + {//generic args + List args = new(); + if (data.HasModifiers) + throw new ArgumentException("generic args after array spec or pointer type", "typeName"); + + while (pos < name.Length) + { + SkipSpace(name, ref pos); + bool aqn = name[pos] == '['; + if (aqn) + ++pos; //skip '[' to the start of the type + args.Add(Parse(name, ref pos, true, aqn, true)); + BoundCheck(pos, name); + if (aqn) + { + if (name[pos] == ']') + ++pos; + else + throw new ArgumentException("Unclosed assembly-qualified type name at " + name[pos], "typeName"); // Is this possible? AQN Ends with ] pending. + BoundCheck(pos, name); + } + + if (name[pos] == ']') + break; + if (name[pos] == ',') + ++pos; // skip ',' to the start of the next arg + else + throw new ArgumentException("Invalid generic arguments separator " + name[pos], "typeName"); + + } + if (pos >= name.Length || name[pos] != ']') + throw new ArgumentException("Error parsing generic params spec", "typeName"); + data._genericParams = args; + data.SetGenericArgumentCount(args.Count); + if (pos + 1 < name.Length && name[pos + 1] == '+') + { + // We have a nested type after a generic argument list. (Extension to the original syntax.) + // Recursively parse to pick up the remainder, then merge the results. + pos += 2; // skip "]+" to the start of the nested name + var nested = Parse(name, ref pos, true, false, true); + data.MergeNested(nested); + // We are going to loop and increment pos, but we haven't yet dealt with the character that ended our nested. + // Decrement pos so the loop increment will get us back to this place. + --pos; + } + } + else + { //array spec + + int dimensions = 1; + bool bound = false; + while (pos < name.Length && name[pos] != ']') + { + if (name[pos] == '*') + { + if (bound) + throw new ArgumentException("Array spec cannot have 2 bound dimensions", "typeName"); + bound = true; + } + else if (name[pos] != ',') + throw new ArgumentException("Invalid character in array spec " + name[pos], "typeName"); + else + ++dimensions; - if (type is null && (!_name?.Equals(originalTypename) ?? false)) - type = RT.classForName(_name); + ++pos; + SkipSpace(name, ref pos); + } + if (pos >= name.Length || name[pos] != ']') + throw new ArgumentException("Error parsing array spec", "typeName"); + if (dimensions > 1 && bound) + throw new ArgumentException("Invalid array spec, multi-dimensional array cannot be bound", "typeName"); + data.AddModifier(new ClrArraySpec(dimensions, bound)); + } + + break; + case ']': + if (is_recurse) + { + p = pos; + return data; + } + throw new ArgumentException("Unmatched ']'", "typeName"); + default: + throw new ArgumentException("Bad type def, can't handle '" + name[pos] + "'" + " at " + pos, "typeName"); + } } + } - if (type is null) - // Cannot resolve _name - return null; + p = pos; + return data; + } - if (_nested != null) + #endregion + + #region Resolving + + // We have to get rid of all references to StackCrawlMark -- just not something we have access to. + + public Type Resolve( + Func assemblyResolver, + Func typeResolver, + bool throwOnError, + bool ignoreCase /*, ref System.Threading.StackCrawlMark stackMark*/) + { + Assembly asm = null; + + //if (assemblyResolver == null && typeResolver == null) + // return RuntimeType.GetType(DisplayFullName, throwOnError, ignoreCase, false, ref stackMark); + // We don't have access to RuntimeType, so we just punt. We will always call with one or the other of assemblyResolver or typeResolver. + + if (assemblyResolver is null && typeResolver is null) + throw new ArgumentException("At least one of assemblyResolver or typeResolver must be non-null"); + + if (_assemblyName != null) + { + if (assemblyResolver != null) + asm = assemblyResolver(new AssemblyName(_assemblyName)); + else + asm = Assembly.Load(_assemblyName); + + if (asm == null) { - foreach (var n in _nested) - { - var tmp = type.GetNestedType(n, BindingFlags.Public | BindingFlags.NonPublic); - if (tmp == null) - return null; - type = tmp; - } + if (throwOnError) + throw new FileNotFoundException("Could not resolve assembly '" + _assemblyName + "'"); + return null; } + } - if (_genericParams != null) + Type type = null; + if (typeResolver is not null) + type = typeResolver(asm, _name.DisplayName, ignoreCase); + else + type = asm.GetType(_name.DisplayName, false, ignoreCase); + + if (type is null) + { + if (throwOnError) + throw new TypeLoadException("Could not resolve type '" + _name + "'"); + return null; + } + + if (_nested != null) + { + foreach (var n in _nested) { - Type[] args = new Type[_genericParams.Count]; - for (int i = 0; i < args.Length; ++i) + var tmp = type.GetNestedType(n.DisplayName, BindingFlags.Public | BindingFlags.NonPublic); + if (tmp == null) { - var tmp = _genericParams[i].Resolve(ns, originalTypename, assemblyResolver); - if (tmp == null) - return null; - args[i] = tmp; + if (throwOnError) + throw new TypeLoadException("Could not resolve type '" + n + "'"); + return null; } - type = type.MakeGenericType(args); + type = tmp; } + } - if (_arraySpec != null) + if (_genericParams != null) + { + Type[] args = new Type[_genericParams.Count]; + for (int i = 0; i < args.Length; ++i) { - foreach (var arr in _arraySpec) - type = arr.Resolve(type); + var tmp = _genericParams[i].Resolve(assemblyResolver, typeResolver, throwOnError, ignoreCase /*, ref stackMark */); + if (tmp == null) + { + if (throwOnError) + throw new TypeLoadException("Could not resolve type '" + _genericParams[i]._name + "'"); + return null; + } + args[i] = tmp; } + type = type.MakeGenericType(args); + } - for (int i = 0; i < _pointerLevel; ++i) - type = type.MakePointerType(); - - if (_isByRef) - type = type.MakeByRefType(); - - return type; + if (_modifierSpec != null) + { + foreach (var md in _modifierSpec) + type = md.Resolve(type); } + if (_isByRef) + type = type.MakeByRefType(); - private static Type DefaultTypeResolver(Assembly assembly, string typename, Namespace ns) - { - if (assembly is not null) - assembly.GetType(typename); + return type; + } + #endregion - //(assy, typeName) => assy == null ? (name.Equals(typeName) ? null : RT.classForName(typeName)) : assy.GetType(typeName) - return null; - } + #region Entry point - #endregion + public static Type GetTypeFromName(string name) + { + ClrTypeSpec spec = Parse(name); + if (spec == null) + return null; + return spec.Resolve( + assyName => Assembly.Load(assyName), + //(assy, typeName) => assy == null ? RT.classForName(typeName) : assy.GetType(typeName)); <--- this goes into an infinite loop on a non-existent typename + (assy, typeName, ignoreCase) => assy == null ? (name.Equals(typeName) ? null : RT.classForName(typeName)) : assy.GetType(typeName), + false, + false); } + + #endregion + } + +// private readonly int _dimensions; +// private readonly bool _isBound; + +// #endregion + +// #region C-tors + +// internal ClrArraySpec2(int dimensions, bool bound) +// { +// this._dimensions = dimensions; +// this._isBound = bound; +// } + +// #endregion + +// #region Resolving + +// internal Type Resolve(Type type) +// { +// if (_isBound) +// return type.MakeArrayType(1); +// else if (_dimensions == 1) +// return type.MakeArrayType(); +// return type.MakeArrayType(_dimensions); +// } + +// #endregion +// } + +// public class ClrTypeSpec2 +// { +// #region Data + +// string _name; +// string _assemblyName; +// List _nested; +// List _genericParams; +// List _arraySpec; +// int _pointerLevel; +// bool _isByRef; + +// #endregion + +// #region Entry point + +// public static Type GetTypeFromName(string name, Namespace ns = null) +// { +// ClrTypeSpec2 spec = Parse(name); +// if (spec == null) +// return null; +// return spec.Resolve( +// ns, +// name, +// assyName => Assembly.Load(assyName)); +// } + +// #endregion + +// #region Parsing + +// static ClrTypeSpec2 Parse(string name) +// { +// int pos = 0; +// ClrTypeSpec2 spec = Parse(name, ref pos, false, false); +// if (spec == null) +// return null; // bad parse +// if (pos < name.Length) +// return null; // ArgumentException ("Count not parse the whole type name", "typeName"); +// return spec; +// } + +// static ClrTypeSpec2 Parse(string name, ref int p, bool isRecursive, bool allowAssyQualName) +// { +// int pos = p; +// int name_start; +// bool hasModifiers = false; +// ClrTypeSpec2 spec = new(); + +// SkipSpace(name, ref pos); + +// name_start = pos; + +// for (; pos < name.Length; ++pos) +// { +// switch (name[pos]) +// { +// case '+': +// spec.AddName(name.Substring(name_start, pos - name_start)); +// name_start = pos + 1; +// break; +// case ',': +// case ']': +// spec.AddName(name.Substring(name_start, pos - name_start)); +// name_start = pos + 1; +// if (isRecursive && !allowAssyQualName) +// { +// p = pos; +// return spec; +// } +// hasModifiers = true; +// break; +// case '&': +// case '*': +// case '[': +// if (name[pos] != '[' && name[pos] != '<' && isRecursive) +// return null; // ArgumentException ("Generic argument can't be byref or pointer type", "typeName"); +// spec.AddName(name.Substring(name_start, pos - name_start)); +// name_start = pos + 1; +// hasModifiers = true; +// break; +// case '\\': +// pos++; +// break; +// } +// if (hasModifiers) +// break; +// } + +// if (name_start < pos) +// spec.AddName(name.Substring(name_start, pos - name_start)); + +// if (hasModifiers) +// { +// for (; pos < name.Length; ++pos) +// { + +// switch (name[pos]) +// { +// case '&': +// if (spec._isByRef) +// return null; // ArgumentException ("Can't have a byref of a byref", "typeName") + +// spec._isByRef = true; +// break; +// case '*': +// if (spec._isByRef) +// return null; // ArgumentException ("Can't have a pointer to a byref type", "typeName"); +// ++spec._pointerLevel; +// break; +// case ',': +// if (isRecursive) +// { +// int end = pos; +// while (end < name.Length && name[end] != ']') +// ++end; +// if (end >= name.Length) +// return null; // ArgumentException ("Unmatched ']' while parsing generic argument assembly name"); +// spec._assemblyName = name.Substring(pos + 1, end - pos - 1).Trim(); +// p = end + 1; +// return spec; +// } +// spec._assemblyName = name.Substring(pos + 1).Trim(); +// pos = name.Length; +// break; +// case '[': +// if (spec._isByRef) +// return null; // ArgumentException ("Byref qualifier must be the last one of a type", "typeName"); +// ++pos; +// if (pos >= name.Length) +// return null; // ArgumentException ("Invalid array/generic spec", "typeName"); +// SkipSpace(name, ref pos); + +// if (name[pos] != ',' && name[pos] != '*' && name[pos] != ']') +// {//generic args +// List args = new(); +// if (spec.IsArray) +// return null; // ArgumentException ("generic args after array spec", "typeName"); + +// while (pos < name.Length) +// { +// SkipSpace(name, ref pos); +// bool aqn = name[pos] == '['; +// if (aqn) +// ++pos; //skip '[' to the start of the type +// { +// ClrTypeSpec2 arg = Parse(name, ref pos, true, aqn); +// if (arg == null) +// return null; // bad generic arg +// args.Add(arg); +// } +// if (pos >= name.Length) +// return null; // ArgumentException ("Invalid generic arguments spec", "typeName"); + +// if (name[pos] == ']') +// break; +// if (name[pos] == ',') +// ++pos; // skip ',' to the start of the next arg +// else +// return null; // ArgumentException ("Invalid generic arguments separator " + name [pos], "typeName") + +// } +// if (pos >= name.Length || name[pos] != ']') +// return null; // ArgumentException ("Error parsing generic params spec", "typeName"); +// spec._genericParams = args; +// } +// else +// { //array spec +// int dimensions = 1; +// bool bound = false; +// while (pos < name.Length && name[pos] != ']') +// { +// if (name[pos] == '*') +// { +// if (bound) +// return null; // ArgumentException ("Array spec cannot have 2 bound dimensions", "typeName"); +// bound = true; +// } +// else if (name[pos] != ',') +// return null; // ArgumentException ("Invalid character in array spec " + name [pos], "typeName"); +// else +// ++dimensions; + +// ++pos; +// SkipSpace(name, ref pos); +// } +// if (name[pos] != ']') +// return null; // ArgumentException ("Error parsing array spec", "typeName"); +// if (dimensions > 1 && bound) +// return null; // ArgumentException ("Invalid array spec, multi-dimensional array cannot be bound", "typeName") +// spec.AddArray(new ClrArraySpec2(dimensions, bound)); +// } + +// break; + +// case ']': +// if (isRecursive) +// { +// p = pos + 1; +// return spec; +// } +// return null; // ArgumentException ("Unmatched ']'", "typeName"); +// default: +// return null; // ArgumentException ("Bad type def, can't handle '" + name [pos]+"'" + " at " + pos, "typeName"); +// } +// } +// } + +// p = pos; +// return spec; +// } + + +// void AddName(string type_name) +// { +// if (_name == null) +// { +// _name = type_name; +// } +// else +// { +// if (_nested == null) +// _nested = new List(); +// _nested.Add(type_name); +// } +// } + +// static string AppendGenericCountSuffix(string name, int count) +// { +// return $"{name}`{count}"; +// } + +// void AppendNameGenericCountSuffix(int count) +// { +// if (_nested is not null) +// { +// var name = _nested.Last(); +// _nested.RemoveAt(_nested.Count - 1); +// _nested.Add(AppendGenericCountSuffix(name, count)); +// } +// else +// { +// _name = AppendGenericCountSuffix(_name, count); +// } +// } + +// static void SkipSpace(string name, ref int pos) +// { +// int p = pos; +// while (p < name.Length && Char.IsWhiteSpace(name[p])) +// ++p; +// pos = p; +// } + +// bool IsArray +// { +// get { return _arraySpec != null; } +// } + +// void AddArray(ClrArraySpec2 array) +// { +// if (_arraySpec == null) +// _arraySpec = new List(); +// _arraySpec.Add(array); +// } + +// #endregion + +// #region Resolving + +// internal Type Resolve( +// Namespace ns, +// string originalTypename, +// Func assemblyResolver) +// { +// Assembly asm = null; + +// if (_assemblyName != null) +// { +// if (assemblyResolver != null) +// asm = assemblyResolver(new AssemblyName(_assemblyName)); +// else +// asm = Assembly.Load(_assemblyName); + +// if (asm == null) +// return null; +// } + +// // if _name is same as originalTypename, then the parse is identical to what we started with. +// // Given that ClrTypeSpec2.GetTypeFromName is called from RT.classForName, +// // call RT.classForName when _name == originalTypename will set off an infinite recrusion. + +// Type type = null; + +// if (asm != null) +// type = asm.GetType(_name); +// else +// { +// type = HostExpr.maybeSpecialTag(Symbol.create(_name)); + +// // check for aliases in the namespace +// if (type is null && ns is not null) +// { +// type = ns.GetMapping(Symbol.create(_name)) as Type; +// } + +// if (type is null && (!_name?.Equals(originalTypename) ?? false)) +// type = RT.classForName(_name); +// } + +// if (type is null) +// // Cannot resolve _name +// return null; + +// if (_nested != null) +// { +// foreach (var n in _nested) +// { +// var tmp = type.GetNestedType(n, BindingFlags.Public | BindingFlags.NonPublic); +// if (tmp == null) +// return null; +// type = tmp; +// } +// } + +// if (_genericParams != null) +// { +// Type[] args = new Type[_genericParams.Count]; +// for (int i = 0; i < args.Length; ++i) +// { +// var tmp = _genericParams[i].Resolve(ns, originalTypename, assemblyResolver); +// if (tmp == null) +// return null; +// args[i] = tmp; +// } +// type = type.MakeGenericType(args); +// } + +// if (_arraySpec != null) +// { +// foreach (var arr in _arraySpec) +// type = arr.Resolve(type); +// } + +// for (int i = 0; i < _pointerLevel; ++i) +// type = type.MakePointerType(); + +// if (_isByRef) +// type = type.MakeByRefType(); + +// return type; +// } + + +// private static Type DefaultTypeResolver(Assembly assembly, string typename, Namespace ns) +// { +// if (assembly is not null) +// assembly.GetType(typename); + + + +// //(assy, typeName) => assy == null ? (name.Equals(typeName) ? null : RT.classForName(typeName)) : assy.GetType(typeName) +// return null; +// } + +// #endregion +// } +//} diff --git a/Clojure/Clojure/Lib/RT.cs b/Clojure/Clojure/Lib/RT.cs index 6063ca38..7de8ecb5 100644 --- a/Clojure/Clojure/Lib/RT.cs +++ b/Clojure/Clojure/Lib/RT.cs @@ -10,6 +10,7 @@ using clojure.lang.CljCompiler.Context; using clojure.lang.Runtime; +using clojure.lang.TypeName; using Microsoft.Scripting.Hosting; using System; using System.Collections; diff --git a/Clojure/Csharp.Tests/TypeNameParsingTests.cs b/Clojure/Csharp.Tests/TypeNameParsingTests.cs deleted file mode 100644 index 3fca1633..00000000 --- a/Clojure/Csharp.Tests/TypeNameParsingTests.cs +++ /dev/null @@ -1,155 +0,0 @@ -using clojure.lang; -using NUnit.Framework; -using System; -using System.Collections.Generic; - -namespace Csharp.Tests; - -public class TypeA { } -public class OneG { } -public class TwoG { } -public class GenParent -{ - public class Child - { - public class GrandChild - { - public class GreatGrandChild - { - - } - } - } -} - - -[TestFixture] -public class TypeNameParsingTests -{ - static Namespace _ns; - - [OneTimeSetUp] - public void Setup() - { - RT.Init(); - - _ns = Namespace.findOrCreate(Symbol.intern("Csharp.Tests")); - - _ns.importClass(Symbol.intern("TTypeA"), typeof(TypeA)); - _ns.importClass(Symbol.intern("TOneG"), typeof(OneG<>)); - _ns.importClass(Symbol.intern("TTwoG"), typeof(TwoG<,>)); - _ns.importClass(Symbol.intern("TGenParent"), typeof(GenParent<,>)); - - RT.CurrentNSVar.bindRoot(_ns); - } - - [TestCase("System.String", typeof(string))] - [TestCase("Csharp.Tests.TypeA", typeof(TypeA))] - public void NamespaceQualifiedClassName_ParsesCorrectly(string typename, Type expectedType) - { - var type = RT.classForName(typename); - Assert.That(type, Is.EqualTo(expectedType)); - } - - [TestCase("int", typeof(int))] - [TestCase("long", typeof(long))] - [TestCase("float", typeof(float))] - [TestCase("double", typeof(double))] - [TestCase("bool", typeof(bool))] - [TestCase("char", typeof(char))] - [TestCase("byte", typeof(byte))] - [TestCase("uint", typeof(uint))] - [TestCase("ulong", typeof(ulong))] - [TestCase("ushort", typeof(ushort))] - [TestCase("sbyte", typeof(sbyte))] - [TestCase("ints", typeof(int[]))] - [TestCase("longs", typeof(long[]))] - [TestCase("floats", typeof(float[]))] - [TestCase("doubles", typeof(double[]))] - [TestCase("bools", typeof(bool[]))] - [TestCase("chars", typeof(char[]))] - [TestCase("bytes", typeof(byte[]))] - [TestCase("uints", typeof(uint[]))] - [TestCase("ulongs", typeof(ulong[]))] - [TestCase("ushorts", typeof(ushort[]))] - [TestCase("sbytes", typeof(sbyte[]))] - public void ClojureTypeAlias_ParsesCorrectly(string typename, Type expectedType) - { - var type = RT.classForName(typename); - Assert.That(type, Is.EqualTo(expectedType)); - } - - [TestCase("TTypeA", typeof(TypeA))] - [TestCase("TOneG", typeof(OneG<>))] - [TestCase("TTwoG", typeof(TwoG<,>))] - [TestCase("TGenParent", typeof(GenParent<,>))] - [TestCase("String", typeof(string))] - [TestCase("Int32", typeof(int))] - public void AliasedTypename_ParsesCorrectly(string typename, Type expectedType) - { - var type = RT.classForName(typename); - Assert.That(type, Is.EqualTo(expectedType)); - } - - [TestCase("int[]", typeof(int[]))] - [TestCase("String[]", typeof(string[]))] - [TestCase("TTypeA[]", typeof(TypeA[]))] - [TestCase("Csharp.Tests.TypeA[]", typeof(TypeA[]))] - public void ArrayType_ParsesCorrectly(string typename, Type expectedType) - { - var type = RT.classForName(typename); - Assert.That(type, Is.EqualTo(expectedType)); - } - - - [TestCase("int*", typeof(int))] - [TestCase("String*", typeof(String))] - [TestCase("TTypeA*", typeof(TypeA))] - [TestCase("Csharp.Tests.TypeA*", typeof(TypeA))] - public void PointerType_ParsesCorrectly(string typename, Type expectedType) - { - var type = RT.classForName(typename); - Assert.That(type, Is.EqualTo(expectedType.MakePointerType())); - } - - [TestCase("int&", typeof(int))] - [TestCase("String&", typeof(String))] - [TestCase("TTypeA&", typeof(TypeA))] - [TestCase("Csharp.Tests.TypeA&", typeof(TypeA))] - public void ByRefType_ParsesCorrectly(string typename, Type expectedType) - { - var type = RT.classForName(typename); - Assert.That(type, Is.EqualTo(expectedType.MakeByRefType())); - } - - [TestCase("TOneG[int]", typeof(OneG))] - [TestCase("TOneG[String]", typeof(OneG))] - [TestCase("TTwoG[int,String]", typeof(TwoG))] - public void GenericType_ParsesCorrectly(string typename, Type expectedType) - { - var type = RT.classForName(typename); - Assert.That(type, Is.EqualTo(expectedType)); - } - - [TestCase("System.Collections.Generic.Dictionary`2[System.String, System.Collections.Generic.List`1[System.Int64]]", - typeof(Dictionary>))] - //[TestCase("Csharp.Tests.TwoG`2[System.Int32, Csharp.Tests.OneG`1[System.String]]", typeof(TwoG>))] - - //[TestCase("TTwoG[int, TOneG[String]]", typeof(TwoG>))] - public void GenericType_ParsesCorrectly1(string typename, Type expectedType) - { - var type = RT.classForName(typename); - Assert.That(type, Is.EqualTo(expectedType)); - } - - - [TestCase("TTwoG[int, TOneG[String]]", typeof(TwoG>))] - public void GenericType_ParsesCorrectly2(string typename, Type expectedType) - { - var type = ClrTypeSpec.GetTypeFromName(typename); - Assert.That(type, Is.EqualTo(expectedType)); - } - - -} - diff --git a/Clojure/Csharp.Tests/TypeNameTests/TypeNameParsingTests.cs b/Clojure/Csharp.Tests/TypeNameTests/TypeNameParsingTests.cs new file mode 100644 index 00000000..bfb5b138 --- /dev/null +++ b/Clojure/Csharp.Tests/TypeNameTests/TypeNameParsingTests.cs @@ -0,0 +1,461 @@ +using clojure.lang.TypeName; +using NUnit.Framework; +using System.Collections.Generic; +using System.Linq; + +namespace Csharp.Tests.TypeNameTests; + +public class TypeSpecComparer +{ + IClrTypeIdentifier _name; + string _assemblyName = null; + List _nested = []; + List _genericParams = []; + List _modifiers = []; + bool _isByRef = false; + + class InternalName : IClrTypeIdentifier + { + public string DisplayName { get; init; } + + public InternalName(string name) + { + DisplayName = name; + } + + public bool Equals(IClrTypeName other) + { + return other is not null && other.DisplayName == DisplayName; + } + + string IClrTypeIdentifier.InternalName => throw new System.NotImplementedException(); + + public IClrTypeName NestedName(IClrTypeIdentifier innerName) + { + throw new System.NotImplementedException(); + } + } + + + public bool SameAs(ClrTypeSpec spec) + { + if (spec == null) + return false; + if (!_name.Equals(spec.Name)) + return false; + + if (!string.Equals(_assemblyName, spec.AssemblyName)) + return false; + + if (_isByRef != spec.IsByRef) + return false; + + var nested = spec.Nested.ToList(); + + if (_nested.Count != nested.Count) + return false; + + for (int i = 0; i < _nested.Count; i++) + if (!_nested[i].Equals(nested[i])) + return false; + + var genericParams = spec.GenericParams.ToList(); + + if (_genericParams.Count != genericParams.Count) + return false; + + for (int i = 0; i < _genericParams.Count; i++) + { + if (!_genericParams[i].SameAs(genericParams[i])) + return false; + } + + var modifiers = spec.Modifiers.ToList(); + + if (_modifiers.Count != modifiers.Count) + return false; + + for (int i = 0; i < _modifiers.Count; i++) + if (!_modifiers[i].Equals(modifiers[i])) + return false; + + return true; + } + + public static TypeSpecComparer Create(string name) + { + var cmp = new TypeSpecComparer(); + cmp._name = new InternalName(name); + + return cmp; + } + + public TypeSpecComparer WithAssembly(string assemblyName) + { + _assemblyName = assemblyName; + return this; + } + + public TypeSpecComparer WithNested(params string[] names) + { + _nested = names.Select(n => (IClrTypeIdentifier)new InternalName(n)).ToList(); + return this; + } + + public TypeSpecComparer WithGenericParams(params TypeSpecComparer[] specs) + { + _genericParams = specs.ToList(); + return this; + } + + public TypeSpecComparer WithModifiers(params IClrModifierSpec[] mods) + { + _modifiers = mods.ToList(); + return this; + } + + public TypeSpecComparer SetIsByRef() + { + _isByRef = true; + return this; + } +} + + +[TestFixture] +public class TypeNameParsingTests +{ + [TestCase("A", "A", "#1")] + [TestCase("A.B", "A.B", "#2")] + [TestCase("A\\+B", "A\\+B", "#3")] + public void BasicName_ParsesCorrectly(string typeName, string expect, string idString) + { + var spec = ClrTypeSpec.Parse(typeName); + var cmp = TypeSpecComparer.Create(expect); + Assert.That(cmp.SameAs(spec), Is.True, idString); + } + + [Test] + public void TypeNameStartsWithSpace_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse(" A.B"); + var cmp = TypeSpecComparer.Create("A.B"); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void TypeNameWithSpaceAfterComma_ParsesCorrectly() + { + var cmp = TypeSpecComparer.Create("A") + .WithGenericParams( + TypeSpecComparer.Create("B"), + TypeSpecComparer.Create("C")); + + var spec = ClrTypeSpec.Parse("A[B, C]"); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[B,C]"); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void NestedName_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A+B"); + var cmp = TypeSpecComparer.Create("A").WithNested("B"); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A+B+C"); + cmp = TypeSpecComparer.Create("A").WithNested("B", "C"); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void AssemblyName_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A, MyAssembly"); + var cmp = TypeSpecComparer.Create("A").WithAssembly("MyAssembly"); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A+B, MyAssembly"); + cmp = TypeSpecComparer.Create("A").WithNested("B").WithAssembly("MyAssembly"); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[T][], AssemblyA"); + cmp = TypeSpecComparer.Create("A") + .WithAssembly("AssemblyA") + .WithGenericParams(TypeSpecComparer.Create("T")) + .WithModifiers(new ClrArraySpec(1, false)); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void ArraySpec_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A[]"); + var cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrArraySpec(1, false)); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[,,]"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrArraySpec(3, false)); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[,][]"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrArraySpec(2, false), new ClrArraySpec(1, false)); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[*]"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrArraySpec(1, true)); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void PointerSpec_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A*"); + var cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrPointerSpec(1)); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A**"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrPointerSpec(2)); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A*[]"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrPointerSpec(1), new ClrArraySpec(1, false)); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A*&"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrPointerSpec(1)).SetIsByRef(); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void ByRef_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A&"); + var cmp = TypeSpecComparer.Create("A").SetIsByRef(); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A+B&"); + cmp = TypeSpecComparer.Create("A").WithNested("B").SetIsByRef(); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A*&"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrPointerSpec(1)).SetIsByRef(); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[]&"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrArraySpec(1, false)).SetIsByRef(); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void GenericParams_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A[B]"); + var cmp = TypeSpecComparer.Create("A") + .WithGenericParams( + TypeSpecComparer.Create("B")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[B,C]"); + cmp = TypeSpecComparer.Create("A") + .WithGenericParams( + TypeSpecComparer.Create("B"), + TypeSpecComparer.Create("C")); + + Assert.That(cmp.SameAs(spec), Is.True); + spec = ClrTypeSpec.Parse("A[B+C,D]"); + cmp = TypeSpecComparer.Create("A") + .WithGenericParams( + TypeSpecComparer.Create("B").WithNested("C"), + TypeSpecComparer.Create("D")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[B,C[D]]"); + cmp = TypeSpecComparer.Create("A") + .WithGenericParams( + TypeSpecComparer.Create("B"), + TypeSpecComparer.Create("C") + .WithGenericParams( + TypeSpecComparer.Create("D"))); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[B[C],D[E,F]]"); + cmp = TypeSpecComparer.Create("A") + .WithGenericParams( + TypeSpecComparer.Create("B") + .WithGenericParams( + TypeSpecComparer.Create("C")), + TypeSpecComparer.Create("D") + .WithGenericParams( + TypeSpecComparer.Create("E"), + TypeSpecComparer.Create("F"))); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[B[C,D[E,F]],G]"); + cmp = TypeSpecComparer.Create("A") + .WithGenericParams( + TypeSpecComparer.Create("B") + .WithGenericParams( + TypeSpecComparer.Create("C"), + TypeSpecComparer.Create("D") + .WithGenericParams( + TypeSpecComparer.Create("E"), + TypeSpecComparer.Create("F"))), + TypeSpecComparer.Create("G")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A+B[C,D[E,F]]"); + cmp = TypeSpecComparer.Create("A") + .WithNested("B") + .WithGenericParams( + TypeSpecComparer.Create("C"), + TypeSpecComparer.Create("D") + .WithGenericParams( + TypeSpecComparer.Create("E"), + TypeSpecComparer.Create("F"))); + Assert.That(cmp.SameAs(spec), Is.True); + + + spec = ClrTypeSpec.Parse("A[ [B, AssemblyB], C, [D, AssemblyD]], AssemblyA"); + cmp = TypeSpecComparer.Create("A") + .WithAssembly("AssemblyA") + .WithGenericParams( + TypeSpecComparer.Create("B").WithAssembly("AssemblyB"), + TypeSpecComparer.Create("C"), + TypeSpecComparer.Create("D").WithAssembly("AssemblyD")); + Assert.That(cmp.SameAs(spec), Is.True); + + } + + [Test] + public void GenericArg_CannotBeByRef() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[B&]")); + Assert.That(exn.Message, Does.Contain("Generic argument can't be byref or pointer type")); + } + + [Test] + public void GenericArg_CannotBePointer() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[B*]")); + Assert.That(exn.Message, Does.Contain("Generic argument can't be byref or pointer type")); + + } + + [Test] + public void CannotTakeByRefOfByRef() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A&&")); + Assert.That(exn.Message, Does.Contain("Can't have a byref of a byref")); + + + + } + + [Test] + public void CannotHavePointerAfterByRef() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A&*")); + Assert.That(exn.Message, Does.Contain("Can't have a pointer to a byref type")); + } + + [Test] + public void CannotHaveMissingCloseBracketInGenericArgumentAssemblyName() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[[B, AssemblyB")); + Assert.That(exn.Message, Does.Contain("Unmatched ']' while parsing generic argument assembly name")); + } + + [Test] + public void ByRefQualifierMustBeLast() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A&[]")); + Assert.That(exn.Message, Does.Contain("Byref qualifier must be the last one of a type")); + } + + [Test] + public void MissingCharactersAfterLeftBracketIsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[")); + Assert.That(exn.Message, Does.Contain("Invalid array/generic spec")); + } + + [Test] + public void CannotHaveGenericArgsAfterArrayOrPointer() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[][B]")); + Assert.That(exn.Message, Does.Contain("generic args after array spec or pointer type")); + + exn = Assert.Throws(() => ClrTypeSpec.Parse("A*[B]")); + Assert.That(exn.Message, Does.Contain("generic args after array spec or pointer type")); + } + + [Test] + public void InvalidGenericArgsSeparator_IsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[ [B, AssemblyB ] + C ] ")); + Assert.That(exn.Message, Does.Contain("Invalid generic arguments separator")); + } + + [Test] + public void ErrorParsingGenericParamsSpec_IsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[B,")); + Assert.That(exn.Message, Does.Contain("Error parsing generic params spec")); + } + + [Test] + public void TwoBoundDesignatorsInArraySpec_IsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[**]")); + Assert.That(exn.Message, Does.Contain("Array spec cannot have 2 bound dimensions")); + } + + [Test] + public void InvalidCharacterInArraySpec_IsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[*!]")); + Assert.That(exn.Message, Does.Contain("Invalid character in array spec")); + } + + [Test] + public void ErrorParsingArraySpec_IsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[,")); + Assert.That(exn.Message, Does.Contain("Error parsing array spec")); + } + + [Test] + public void CannotHaveBoundAndDimensionTogetherInArraySpec() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[*,]")); + Assert.That(exn.Message, Does.Contain("Invalid array spec, multi-dimensional array cannot be bound")); + } + + + [Test] + public void UnmatchedRightBracket_IsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[]]")); + Assert.That(exn.Message, Does.Contain("Unmatched ']'")); + } + + + [Test] + public void UnknownCharacterInModifiers_IsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A*!")); + Assert.That(exn.Message, Does.Contain("Bad type def")); + } + + // I don't know how to trigger this error + //[Test] + //public void UnclosedAssemblyQualifiedNameInGenericArg_IsBad() + //{ + // var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[ [B, AssemblyB ] ] ")); + // Assert.That(exn.Message, Does.Contain("Unclosed assembly-qualified type name")); + //} +} + diff --git a/Clojure/Csharp.Tests/TypeNameTests/TypeNameResolvingTests.cs b/Clojure/Csharp.Tests/TypeNameTests/TypeNameResolvingTests.cs new file mode 100644 index 00000000..5ff04a4b --- /dev/null +++ b/Clojure/Csharp.Tests/TypeNameTests/TypeNameResolvingTests.cs @@ -0,0 +1,107 @@ +using clojure.lang.TypeName; +using NUnit.Framework; +using System; + +namespace Csharp.Tests.TypeNameTests; + + +public class Simple { } + +public class Outer +{ + public class Inner { } +} + +public class OneG { } +public class TwoG { } + +public class GenParent +{ + public class Child + { + public class GrandChild + { + public class GreatGrandChild + { + + } + } + } +} + + +[TestFixture] +public class TypeNameResolvingTests +{ + + public static Type TR(string typename, bool throwOnError) => Type.GetType(typename, throwOnError); + + + public static Type Resolve(string typeName) + { + var spec = ClrTypeSpec.Parse(typeName); + return spec?.Resolve(null, (assemblyName, typename, throwOnError) => TR(typename, throwOnError), false, false); + } + + [Test] + public void SimpleClassName_ResolvesCorrectly() + { + Assert.That(Resolve("Csharp.Tests.TypeNameTests.Simple"), Is.EqualTo(typeof(Simple))); + } + + [Test] + public void NestedClassName_ResolvesCorrectly() + { + Assert.That(Resolve("Csharp.Tests.TypeNameTests.Outer+Inner"), Is.EqualTo(typeof(Outer.Inner))); + } + + [Test] + public void GenericClassName_ResolvesCorrectly() + { + Assert.That(Resolve("Csharp.Tests.TypeNameTests.OneG`1[System.String]"), Is.EqualTo(typeof(OneG))); + Assert.That(Resolve("Csharp.Tests.TypeNameTests.TwoG`2[System.String,System.Int32]"), Is.EqualTo(typeof(TwoG))); + Assert.That( + Resolve("Csharp.Tests.TypeNameTests.GenParent`2+Child+GrandChild`1+GreatGrandChild`2[System.String, System.Int32, System.Double, System.String,System.Object]"), + Is.EqualTo(typeof(GenParent.Child.GrandChild.GreatGrandChild))); + } + + [Test] + public void NonExistentType_ResolvesToNull() + { + Assert.That(Resolve("Csharp.Tests.TypeNameTests.NonExistent"), Is.Null); + Assert.That(Resolve("Csharp.Tests.TypeNameTests.Simple+NonExistent"), Is.Null); + Assert.That(Resolve("Csharp.Tests.TypeNameTests.OneG`1[Non.Existent]"), Is.Null); + } + + [Test] + public void PointerType_ResolvesCorrectly() + { + Assert.That(Resolve("System.String*"), Is.EqualTo(typeof(string).MakePointerType())); + Assert.That(Resolve("System.String**"), Is.EqualTo(typeof(string).MakePointerType().MakePointerType())); + } + + [Test] + public void ByRefType_ResolvesCorrectly() + { + Assert.That(Resolve("System.String&"), Is.EqualTo(typeof(string).MakeByRefType())); + Assert.That(Resolve("System.String*&"), Is.EqualTo(typeof(string).MakePointerType().MakeByRefType())); + } + + [Test] + public void ArrayType_ResolvesCorrectly() + { + Assert.That(Resolve("System.String[]"), Is.EqualTo(typeof(string).MakeArrayType())); + Assert.That(Resolve("System.String[*]"), Is.EqualTo(typeof(string).MakeArrayType(1))); + Assert.That(Resolve("System.String[,]"), Is.EqualTo(typeof(string).MakeArrayType(2))); + Assert.That(Resolve("System.String[,,]"), Is.EqualTo(typeof(string).MakeArrayType(3))); + } + + + [Test] + public void Everything_ResolvesCorrectly() + { + Assert.That( + Resolve("Csharp.Tests.TypeNameTests.GenParent`2+Child+GrandChild`1+GreatGrandChild`2[System.String, System.Int32, System.Double, System.String,System.Object][]**&"), + Is.EqualTo(typeof(GenParent.Child.GrandChild.GreatGrandChild).MakeArrayType().MakePointerType().MakePointerType().MakeByRefType())); + } +} diff --git a/Clojure/Csharp.Tests/TypeNameTests2/TypeNameParsingTests2.cs b/Clojure/Csharp.Tests/TypeNameTests2/TypeNameParsingTests2.cs new file mode 100644 index 00000000..a332f7f0 --- /dev/null +++ b/Clojure/Csharp.Tests/TypeNameTests2/TypeNameParsingTests2.cs @@ -0,0 +1,735 @@ +using clojure.lang.TypeName2; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Csharp.Tests.TypeNameTests2; + +public class TypeSpecComparer +{ + IClrTypeIdentifier _name; + string _assemblyName = null; + List _nested = []; + List _genericParams = []; + List _modifiers = []; + bool _isByRef = false; + + class InternalName : IClrTypeIdentifier + { + public string DisplayName { get; init; } + public int ImplicitGenericCount { get; set; } + + public InternalName(string name, int genericCount = 0) + { + DisplayName = name; + ImplicitGenericCount = genericCount; + } + + public bool Equals(IClrTypeName other) + { + return other is not null && other.DisplayName == DisplayName && other.ImplicitGenericCount == ImplicitGenericCount; + } + + string IClrTypeIdentifier.InternalName => throw new System.NotImplementedException(); + + } + + + public bool SameAs(ClrTypeSpec spec) + { + if (spec == null) + return false; + + if (!_name.Equals(spec.Name)) + return false; + + if (!string.Equals(_assemblyName, spec.AssemblyName)) + return false; + + if (_isByRef != spec.IsByRef) + return false; + + var nested = spec.Nested.ToList(); + + if (_nested.Count != nested.Count) + return false; + + for (int i = 0; i < _nested.Count; i++) + if (!_nested[i].Equals(nested[i])) + return false; + + var genericParams = spec.GenericParams.ToList(); + + if (_genericParams.Count != genericParams.Count) + return false; + + for (int i = 0; i < _genericParams.Count; i++) + { + if (!_genericParams[i].SameAs(genericParams[i])) + return false; + } + + var modifiers = spec.Modifiers.ToList(); + + if (_modifiers.Count != modifiers.Count) + return false; + + for (int i = 0; i < _modifiers.Count; i++) + if (!_modifiers[i].Equals(modifiers[i])) + return false; + + return true; + } + + public static TypeSpecComparer Create(string name, int genericCount = 0) + { + var cmp = new TypeSpecComparer(); + cmp._name = new InternalName(name, genericCount); + + return cmp; + } + + public TypeSpecComparer WithAssembly(string assemblyName) + { + _assemblyName = assemblyName; + return this; + } + + public TypeSpecComparer WithNested(params string[] names) + { + _nested = names.Select(n => (IClrTypeIdentifier)new InternalName(n)).ToList(); + return this; + } + + public TypeSpecComparer WithNested(params (string, int)[] namesAndCounts) + { + _nested = namesAndCounts.Select(n => (IClrTypeIdentifier)new InternalName(n.Item1, n.Item2)).ToList(); + return this; + } + + public TypeSpecComparer WithGenericParams(params TypeSpecComparer[] specs) + { + _genericParams = specs.ToList(); + return this; + } + + public TypeSpecComparer WithModifiers(params IClrModifierSpec[] mods) + { + _modifiers = mods.ToList(); + return this; + } + + public TypeSpecComparer SetIsByRef() + { + _isByRef = true; + return this; + } +} + + +[TestFixture] +public class TypeNameParsingTests +{ + [TestCase("A", "A", "#1")] + [TestCase("A.B", "A.B", "#2")] + [TestCase("A\\+B", "A\\+B", "#3")] + public void BasicName_ParsesCorrectly(string typeName, string expect, string idString) + { + var spec = ClrTypeSpec.Parse(typeName); + var cmp = TypeSpecComparer.Create(expect); + Assert.That(cmp.SameAs(spec), Is.True, idString); + } + + [Test] + public void TypeNameStartsWithSpace_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse(" A.B"); + var cmp = TypeSpecComparer.Create("A.B"); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void TypeNameWithSpaceAfterComma_ParsesCorrectly() + { + var cmp = TypeSpecComparer.Create("A", 2) + .WithGenericParams( + TypeSpecComparer.Create("B"), + TypeSpecComparer.Create("C")); + + var spec = ClrTypeSpec.Parse("A[B, C]"); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[B,C]"); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void NestedName_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A+B"); + var cmp = TypeSpecComparer.Create("A").WithNested("B"); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A+B+C"); + cmp = TypeSpecComparer.Create("A").WithNested("B", "C"); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void AssemblyName_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A, MyAssembly"); + var cmp = TypeSpecComparer.Create("A").WithAssembly("MyAssembly"); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A+B, MyAssembly"); + cmp = TypeSpecComparer.Create("A").WithNested("B").WithAssembly("MyAssembly"); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void ArraySpec_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A[]"); + var cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrArraySpec(1, false)); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[,,]"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrArraySpec(3, false)); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[,][]"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrArraySpec(2, false), new ClrArraySpec(1, false)); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[*]"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrArraySpec(1, true)); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void PointerSpec_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A*"); + var cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrPointerSpec(1)); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A**"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrPointerSpec(2)); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A*[]"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrPointerSpec(1), new ClrArraySpec(1, false)); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A*&"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrPointerSpec(1)).SetIsByRef(); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void ByRef_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A&"); + var cmp = TypeSpecComparer.Create("A").SetIsByRef(); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A+B&"); + cmp = TypeSpecComparer.Create("A").WithNested("B").SetIsByRef(); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A*&"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrPointerSpec(1)).SetIsByRef(); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[]&"); + cmp = TypeSpecComparer.Create("A").WithModifiers(new ClrArraySpec(1, false)).SetIsByRef(); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void GenericParams_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A[B]"); + var cmp = TypeSpecComparer.Create("A", 1) + .WithGenericParams( + TypeSpecComparer.Create("B")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[B,C]"); + cmp = TypeSpecComparer.Create("A", 2) + .WithGenericParams( + TypeSpecComparer.Create("B"), + TypeSpecComparer.Create("C")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[B+C,D]"); + cmp = TypeSpecComparer.Create("A", 2) + .WithGenericParams( + TypeSpecComparer.Create("B").WithNested("C"), + TypeSpecComparer.Create("D")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[B,C[D]]"); + cmp = TypeSpecComparer.Create("A", 2) + .WithGenericParams( + TypeSpecComparer.Create("B"), + TypeSpecComparer.Create("C", 1) + .WithGenericParams( + TypeSpecComparer.Create("D"))); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[B[C],D[E,F]]"); + cmp = TypeSpecComparer.Create("A", 2) + .WithGenericParams( + TypeSpecComparer.Create("B", 1) + .WithGenericParams( + TypeSpecComparer.Create("C")), + TypeSpecComparer.Create("D", 2) + .WithGenericParams( + TypeSpecComparer.Create("E"), + TypeSpecComparer.Create("F"))); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[B[C,D[E,F]],G]"); + cmp = TypeSpecComparer.Create("A", 2) + .WithGenericParams( + TypeSpecComparer.Create("B", 2) + .WithGenericParams( + TypeSpecComparer.Create("C"), + TypeSpecComparer.Create("D", 2) + .WithGenericParams( + TypeSpecComparer.Create("E"), + TypeSpecComparer.Create("F"))), + TypeSpecComparer.Create("G")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A+B[C,D[E,F]]"); + cmp = TypeSpecComparer.Create("A") + .WithNested(("B", 2)) + .WithGenericParams( + TypeSpecComparer.Create("C"), + TypeSpecComparer.Create("D", 2) + .WithGenericParams( + TypeSpecComparer.Create("E"), + TypeSpecComparer.Create("F"))); + Assert.That(cmp.SameAs(spec), Is.True); + + + spec = ClrTypeSpec.Parse("A[ [B, AssemblyB], C, [D, AssemblyD]], AssemblyA"); + cmp = TypeSpecComparer.Create("A", 3) + .WithAssembly("AssemblyA") + .WithGenericParams( + TypeSpecComparer.Create("B").WithAssembly("AssemblyB"), + TypeSpecComparer.Create("C"), + TypeSpecComparer.Create("D").WithAssembly("AssemblyD")); + Assert.That(cmp.SameAs(spec), Is.True); + } + + + [Test] + public void SimpleNestedGeneric_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A[T]+B"); + var cmp = TypeSpecComparer.Create("A", 1) + .WithNested("B") + .WithGenericParams( + TypeSpecComparer.Create("T")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A+B[T]"); + cmp = TypeSpecComparer.Create("A") + .WithNested(("B", 1)) + .WithGenericParams( + TypeSpecComparer.Create("T")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A+B[T]+C"); + cmp = TypeSpecComparer.Create("A") + .WithNested(("B", 1), ("C", 0)) + .WithGenericParams( + TypeSpecComparer.Create("T")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A+B+C[T]"); + cmp = TypeSpecComparer.Create("A") + .WithNested(("B", 0), ("C", 1)) + .WithGenericParams( + TypeSpecComparer.Create("T")); + Assert.That(cmp.SameAs(spec), Is.True); + + + spec = ClrTypeSpec.Parse("A+B[T]+C[U,V]"); + cmp = TypeSpecComparer.Create("A") + .WithNested(("B", 1), ("C", 2)) + .WithGenericParams( + TypeSpecComparer.Create("T"), + TypeSpecComparer.Create("U"), + TypeSpecComparer.Create("V")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[T]+B+C+D[U,V]+E"); + cmp = TypeSpecComparer.Create("A", 1) + .WithNested(("B", 0), ("C", 0), ("D", 2), ("E", 0)) + .WithGenericParams( + TypeSpecComparer.Create("T"), + TypeSpecComparer.Create("U"), + TypeSpecComparer.Create("V")); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void NestedGenericProcessesTerminatingCharacter() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[T]+B]")); + Assert.That(exn.Message, Does.Contain("Unmatched ']'")); + } + + + [Test] + public void NestedGenericWithModifiers_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A[T]+B+C+D[U,V]+E[]"); + var cmp = TypeSpecComparer.Create("A", 1) + .WithNested(("B", 0), ("C", 0), ("D", 2), ("E", 0)) + .WithGenericParams( + TypeSpecComparer.Create("T"), + TypeSpecComparer.Create("U"), + TypeSpecComparer.Create("V")) + .WithModifiers(new ClrArraySpec(1, false)); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[T]+B+C+D[U,V]+E[,]**&"); + cmp = TypeSpecComparer.Create("A", 1) + .WithNested(("B", 0), ("C", 0), ("D", 2), ("E", 0)) + .WithGenericParams( + TypeSpecComparer.Create("T"), + TypeSpecComparer.Create("U"), + TypeSpecComparer.Create("V")) + .WithModifiers(new ClrArraySpec(2, false), new ClrPointerSpec(2)) + .SetIsByRef(); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void NestedGenericAsGenericArg_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A[B+C[T]]"); + var cmp = TypeSpecComparer.Create("A", 1) + .WithGenericParams( + TypeSpecComparer.Create("B").WithNested(("C", 1)) + .WithGenericParams( + TypeSpecComparer.Create("T"))); + Assert.That(cmp.SameAs(spec), Is.True); + + + spec = ClrTypeSpec.Parse("A[B+C[T]+D]"); + cmp = TypeSpecComparer.Create("A", 1) + .WithGenericParams( + TypeSpecComparer.Create("B").WithNested(("C", 1), ("D", 0)) + .WithGenericParams( + TypeSpecComparer.Create("T"))); + Assert.That(cmp.SameAs(spec), Is.True); + + + spec = ClrTypeSpec.Parse("A[B+C[T]]+D"); + cmp = TypeSpecComparer.Create("A", 1) + .WithNested("D") + .WithGenericParams( + TypeSpecComparer.Create("B").WithNested(("C", 1)) + .WithGenericParams( + TypeSpecComparer.Create("T"))); + Assert.That(cmp.SameAs(spec), Is.True); + + + spec = ClrTypeSpec.Parse("A[B+C[T],D[U]+E]+F"); + cmp = TypeSpecComparer.Create("A", 2) + .WithNested("F") + .WithGenericParams( + TypeSpecComparer.Create("B") + .WithNested(("C", 1)) + .WithGenericParams(TypeSpecComparer.Create("T")), + TypeSpecComparer.Create("D", 1) + .WithNested("E") + .WithGenericParams(TypeSpecComparer.Create("U"))); + Assert.That(cmp.SameAs(spec), Is.True); + } + + + [Test] + public void NestedGenericWithAssembly_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A[T]+B, AssemblyA"); + var cmp = TypeSpecComparer.Create("A", 1) + .WithAssembly("AssemblyA") + .WithNested("B") + .WithGenericParams( + TypeSpecComparer.Create("T")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A+B[T], AssemblyA"); + cmp = TypeSpecComparer.Create("A") + .WithAssembly("AssemblyA") + .WithNested(("B", 1)) + .WithGenericParams( + TypeSpecComparer.Create("T")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A+B[T]+C, AssemblyA"); + cmp = TypeSpecComparer.Create("A") + .WithAssembly("AssemblyA") + .WithNested(("B", 1), ("C", 0)) + .WithGenericParams( + TypeSpecComparer.Create("T")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A+B+C[T], AssemblyA"); + cmp = TypeSpecComparer.Create("A") + .WithAssembly("AssemblyA") + .WithNested(("B", 0), ("C", 1)) + .WithGenericParams( + TypeSpecComparer.Create("T")); + Assert.That(cmp.SameAs(spec), Is.True); + + + spec = ClrTypeSpec.Parse("A+B[T]+C[U,V], AssemblyA"); + cmp = TypeSpecComparer.Create("A") + .WithAssembly("AssemblyA") + .WithNested(("B", 1), ("C", 2)) + .WithGenericParams( + TypeSpecComparer.Create("T"), + TypeSpecComparer.Create("U"), + TypeSpecComparer.Create("V")); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[T]+B+C+D[U,V]+E, AssemblyA"); + cmp = TypeSpecComparer.Create("A", 1) + .WithAssembly("AssemblyA") + .WithNested(("B", 0), ("C", 0), ("D", 2), ("E", 0)) + .WithGenericParams( + TypeSpecComparer.Create("T"), + TypeSpecComparer.Create("U"), + TypeSpecComparer.Create("V")); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void NestedGenericWithAssemblyWithModifiers_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A[T][], AssemblyA"); + var cmp = TypeSpecComparer.Create("A", 1) + .WithAssembly("AssemblyA") + .WithGenericParams( + TypeSpecComparer.Create("T")) + .WithModifiers(new ClrArraySpec(1, false)); + Assert.That(cmp.SameAs(spec), Is.True); + + + spec = ClrTypeSpec.Parse("A[T]+B+C+D[U,V]+E[], AssemblyA"); + cmp = TypeSpecComparer.Create("A", 1) + .WithAssembly("AssemblyA") + .WithNested(("B", 0), ("C", 0), ("D", 2), ("E", 0)) + .WithGenericParams( + TypeSpecComparer.Create("T"), + TypeSpecComparer.Create("U"), + TypeSpecComparer.Create("V")) + .WithModifiers(new ClrArraySpec(1, false)); + Assert.That(cmp.SameAs(spec), Is.True); + + spec = ClrTypeSpec.Parse("A[T]+B+C+D[U,V]+E[,]**&, AssemblyA"); + cmp = TypeSpecComparer.Create("A", 1) + .WithAssembly("AssemblyA") + .WithNested(("B", 0), ("C", 0), ("D", 2), ("E", 0)) + .WithGenericParams( + TypeSpecComparer.Create("T"), + TypeSpecComparer.Create("U"), + TypeSpecComparer.Create("V")) + .WithModifiers(new ClrArraySpec(2, false), new ClrPointerSpec(2)) + .SetIsByRef(); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void NestedGenericWithAseemblyAsGenericArg_ParsesCorrectly() + { + var spec = ClrTypeSpec.Parse("A[[B+C[T],AssemblyB]], AssemblyA"); + var cmp = TypeSpecComparer.Create("A", 1) + .WithAssembly("AssemblyA") + .WithGenericParams( + TypeSpecComparer.Create("B").WithNested(("C", 1)) + .WithAssembly("AssemblyB") + .WithGenericParams( + TypeSpecComparer.Create("T"))); + Assert.That(cmp.SameAs(spec), Is.True); + + + spec = ClrTypeSpec.Parse("A[[B+C[T]+D, AssemblyB]], AssemblyA"); + cmp = TypeSpecComparer.Create("A", 1) + .WithAssembly("AssemblyA") + .WithGenericParams( + TypeSpecComparer.Create("B").WithNested(("C", 1), ("D", 0)) + .WithAssembly("AssemblyB") + .WithGenericParams( + TypeSpecComparer.Create("T"))); + Assert.That(cmp.SameAs(spec), Is.True); + + + spec = ClrTypeSpec.Parse("A[[B+C[T], AssemblyB]]+D, AssemblyA"); + cmp = TypeSpecComparer.Create("A", 1) + .WithAssembly("AssemblyA") + .WithNested("D") + .WithGenericParams( + TypeSpecComparer.Create("B").WithNested(("C", 1)) + .WithAssembly("AssemblyB") + .WithGenericParams( + TypeSpecComparer.Create("T"))); + Assert.That(cmp.SameAs(spec), Is.True); + + + spec = ClrTypeSpec.Parse("A[[B+C[T], AssemblyB],[D[U]+E, AssemblyD]]+F, AssemblyA"); + cmp = TypeSpecComparer.Create("A", 2) + .WithAssembly("AssemblyA") + .WithNested("F") + .WithGenericParams( + TypeSpecComparer.Create("B") + .WithAssembly("AssemblyB") + .WithNested(("C", 1)) + .WithGenericParams(TypeSpecComparer.Create("T")), + TypeSpecComparer.Create("D", 1) + .WithAssembly("AssemblyD") + .WithNested("E") + .WithGenericParams(TypeSpecComparer.Create("U"))); + Assert.That(cmp.SameAs(spec), Is.True); + } + + [Test] + public void GenericArg_CannotBeByRef() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[B&]")); + Assert.That(exn.Message, Does.Contain("Generic argument can't be byref or pointer type")); + } + + [Test] + public void GenericArg_CannotBePointer() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[B*]")); + Assert.That(exn.Message, Does.Contain("Generic argument can't be byref or pointer type")); + + } + + [Test] + public void CannotTakeByRefOfByRef() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A&&")); + Assert.That(exn.Message, Does.Contain("Can't have a byref of a byref")); + + + + } + + [Test] + public void CannotHavePointerAfterByRef() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A&*")); + Assert.That(exn.Message, Does.Contain("Can't have a pointer to a byref type")); + } + + [Test] + public void CannotHaveMissingCloseBracketInGenericArgumentAssemblyName() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[[B, AssemblyB")); + Assert.That(exn.Message, Does.Contain("Unmatched ']' while parsing generic argument assembly name")); + } + + [Test] + public void ByRefQualifierMustBeLast() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A&[]")); + Assert.That(exn.Message, Does.Contain("Byref qualifier must be the last one of a type")); + } + + [Test] + public void MissingCharactersAfterLeftBracketIsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[")); + Assert.That(exn.Message, Does.Contain("Invalid array/generic spec")); + } + + [Test] + public void CannotHaveGenericArgsAfterArrayOrPointer() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[][B]")); + Assert.That(exn.Message, Does.Contain("generic args after array spec or pointer type")); + + exn = Assert.Throws(() => ClrTypeSpec.Parse("A*[B]")); + Assert.That(exn.Message, Does.Contain("generic args after array spec or pointer type")); + } + + [Test] + public void InvalidGenericArgsSeparator_IsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[ [B, AssemblyB ] + C ] ")); + Assert.That(exn.Message, Does.Contain("Invalid generic arguments separator")); + } + + [Test] + public void ErrorParsingGenericParamsSpec_IsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[B,")); + Assert.That(exn.Message, Does.Contain("Error parsing generic params spec")); + } + + [Test] + public void TwoBoundDesignatorsInArraySpec_IsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[**]")); + Assert.That(exn.Message, Does.Contain("Array spec cannot have 2 bound dimensions")); + } + + [Test] + public void InvalidCharacterInArraySpec_IsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[*!]")); + Assert.That(exn.Message, Does.Contain("Invalid character in array spec")); + } + + [Test] + public void ErrorParsingArraySpec_IsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[,")); + Assert.That(exn.Message, Does.Contain("Error parsing array spec")); + } + + [Test] + public void CannotHaveBoundAndDimensionTogetherInArraySpec() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[*,]")); + Assert.That(exn.Message, Does.Contain("Invalid array spec, multi-dimensional array cannot be bound")); + } + + + [Test] + public void UnmatchedRightBracket_IsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[]]")); + Assert.That(exn.Message, Does.Contain("Unmatched ']'")); + } + + + [Test] + public void UnknownCharacterInModifiers_IsBad() + { + var exn = Assert.Throws(() => ClrTypeSpec.Parse("A*!")); + Assert.That(exn.Message, Does.Contain("Bad type def")); + } + + // I don't know how to trigger this error + //[Test] + //public void UnclosedAssemblyQualifiedNameInGenericArg_IsBad() + //{ + // var exn = Assert.Throws(() => ClrTypeSpec.Parse("A[ [B, AssemblyB ] ] ")); + // Assert.That(exn.Message, Does.Contain("Unclosed assembly-qualified type name")); + //} +} + diff --git a/Clojure/Csharp.Tests/TypeNameTests2/TypeNameResolvingTests2.cs b/Clojure/Csharp.Tests/TypeNameTests2/TypeNameResolvingTests2.cs new file mode 100644 index 00000000..63d345f5 --- /dev/null +++ b/Clojure/Csharp.Tests/TypeNameTests2/TypeNameResolvingTests2.cs @@ -0,0 +1,162 @@ +namespace Csharp.Tests.TypeNameTests2; +internal class TypeNameResolvingTests2 +{ +} + + +//using clojure.lang; +//using NUnit.Framework; +//using System; +//using System.Collections.Generic; + +//namespace Csharp.Tests; + +//public class TypeA { } +//public class OneG { } +//public class TwoG { } +//public class GenParent +//{ +// public class Child +// { +// public class GrandChild +// { +// public class GreatGrandChild +// { + +// } +// } +// } +//} + + +//[TestFixture] +//public class TypeNameParsingTests +//{ +// static Namespace _ns; + +// [OneTimeSetUp] +// public void Setup() +// { +// RT.Init(); + +// _ns = Namespace.findOrCreate(Symbol.intern("Csharp.Tests")); + +// _ns.importClass(Symbol.intern("TTypeA"), typeof(TypeA)); +// _ns.importClass(Symbol.intern("TOneG"), typeof(OneG<>)); +// _ns.importClass(Symbol.intern("TTwoG"), typeof(TwoG<,>)); +// _ns.importClass(Symbol.intern("TGenParent"), typeof(GenParent<,>)); + +// RT.CurrentNSVar.bindRoot(_ns); +// } + +// [TestCase("System.String", typeof(string))] +// [TestCase("Csharp.Tests.TypeA", typeof(TypeA))] +// public void NamespaceQualifiedClassName_ParsesCorrectly(string typename, Type expectedType) +// { +// var type = RT.classForName(typename); +// Assert.That(type, Is.EqualTo(expectedType)); +// } + +// [TestCase("int", typeof(int))] +// [TestCase("long", typeof(long))] +// [TestCase("float", typeof(float))] +// [TestCase("double", typeof(double))] +// [TestCase("bool", typeof(bool))] +// [TestCase("char", typeof(char))] +// [TestCase("byte", typeof(byte))] +// [TestCase("uint", typeof(uint))] +// [TestCase("ulong", typeof(ulong))] +// [TestCase("ushort", typeof(ushort))] +// [TestCase("sbyte", typeof(sbyte))] +// [TestCase("ints", typeof(int[]))] +// [TestCase("longs", typeof(long[]))] +// [TestCase("floats", typeof(float[]))] +// [TestCase("doubles", typeof(double[]))] +// [TestCase("bools", typeof(bool[]))] +// [TestCase("chars", typeof(char[]))] +// [TestCase("bytes", typeof(byte[]))] +// [TestCase("uints", typeof(uint[]))] +// [TestCase("ulongs", typeof(ulong[]))] +// [TestCase("ushorts", typeof(ushort[]))] +// [TestCase("sbytes", typeof(sbyte[]))] +// public void ClojureTypeAlias_ParsesCorrectly(string typename, Type expectedType) +// { +// var type = RT.classForName(typename); +// Assert.That(type, Is.EqualTo(expectedType)); +// } + +// [TestCase("TTypeA", typeof(TypeA))] +// [TestCase("TOneG", typeof(OneG<>))] +// [TestCase("TTwoG", typeof(TwoG<,>))] +// [TestCase("TGenParent", typeof(GenParent<,>))] +// [TestCase("String", typeof(string))] +// [TestCase("Int32", typeof(int))] +// public void AliasedTypename_ParsesCorrectly(string typename, Type expectedType) +// { +// var type = RT.classForName(typename); +// Assert.That(type, Is.EqualTo(expectedType)); +// } + +// [TestCase("int[]", typeof(int[]))] +// [TestCase("String[]", typeof(string[]))] +// [TestCase("TTypeA[]", typeof(TypeA[]))] +// [TestCase("Csharp.Tests.TypeA[]", typeof(TypeA[]))] +// public void ArrayType_ParsesCorrectly(string typename, Type expectedType) +// { +// var type = RT.classForName(typename); +// Assert.That(type, Is.EqualTo(expectedType)); +// } + + +// [TestCase("int*", typeof(int))] +// [TestCase("String*", typeof(String))] +// [TestCase("TTypeA*", typeof(TypeA))] +// [TestCase("Csharp.Tests.TypeA*", typeof(TypeA))] +// public void PointerType_ParsesCorrectly(string typename, Type expectedType) +// { +// var type = RT.classForName(typename); +// Assert.That(type, Is.EqualTo(expectedType.MakePointerType())); +// } + +// [TestCase("int&", typeof(int))] +// [TestCase("String&", typeof(String))] +// [TestCase("TTypeA&", typeof(TypeA))] +// [TestCase("Csharp.Tests.TypeA&", typeof(TypeA))] +// public void ByRefType_ParsesCorrectly(string typename, Type expectedType) +// { +// var type = RT.classForName(typename); +// Assert.That(type, Is.EqualTo(expectedType.MakeByRefType())); +// } + +// [TestCase("TOneG[int]", typeof(OneG))] +// [TestCase("TOneG[String]", typeof(OneG))] +// [TestCase("TTwoG[int,String]", typeof(TwoG))] +// public void GenericType_ParsesCorrectly(string typename, Type expectedType) +// { +// var type = RT.classForName(typename); +// Assert.That(type, Is.EqualTo(expectedType)); +// } + +// [TestCase("System.Collections.Generic.Dictionary`2[System.String, System.Collections.Generic.List`1[System.Int64]]", +// typeof(Dictionary>))] +// //[TestCase("Csharp.Tests.TwoG`2[System.Int32, Csharp.Tests.OneG`1[System.String]]", typeof(TwoG>))] + +// //[TestCase("TTwoG[int, TOneG[String]]", typeof(TwoG>))] +// public void GenericType_ParsesCorrectly1(string typename, Type expectedType) +// { +// var type = RT.classForName(typename); +// Assert.That(type, Is.EqualTo(expectedType)); +// } + +// [TestCase("System.Collections.Generic.Dictionary`2[System.String, System.Collections.Generic.List`1[System.Int64]]", +// typeof(Dictionary>))] +// [TestCase("TTwoG[int, TOneG[String]]", typeof(TwoG>))] +// public void GenericType_ParsesCorrectly2(string typename, Type expectedType) +// { +// var type = ClrTypeSpec.GetTypeFromName(typename); +// Assert.That(type, Is.EqualTo(expectedType)); +// } + + +//} +