-
-
Notifications
You must be signed in to change notification settings - Fork 12
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Error: Unhandled exception was throw while running the generator ExampleSourceGeneratorSystem.Reflection.TargetInvocationException: An ReflectionTypeLoadException exception was thrown while invoking Example.ExampleSourceGenerator+<>c.<OnInitialize>b__1_1 ---> System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
at System.Reflection.Assembly.GetTypes()
at L5Sharp.Core.LogixSerializer.Introspect(Assembly assembly)
at L5Sharp.Core.LogixSerializer.Scan()
at L5Sharp.Core.LogixSerializer.<>c.<.cctor>b__17_0()
at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()
at L5Sharp.Core.LogixSerializer.Deserialize(XElement element)
at L5Sharp.Core.LogixSerializer.Deserialize[TElement](XElement element)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
at Example.ExampleSourceGenerator.<>c.<OnInitialize>b__1_1(SgfSourceProductionContext spc, AdditionalText file) in
...
--- End of inner exception stack trace ---
Was trying to use this library in an IncrementalGenerator to generate files for the tags in the l5x and am facing this error. Seems the use of reflection in this library prevents it from being used in source code generators in netstandard 2.0. Would love if source generators were also made compatible with this library. So that intellisense can also be used with the created files from this library.
Hacked this together (I am sure there is a better solution):
/// <summary>
/// Initializes LogixSharp types for use in source generators or other compile-time scenarios.
/// Call this before using any L5Sharp.Core functionality in source generators.
/// </summary>
public static class LogixTypeInitializer
{
private static bool _initialized;
/// <summary>
/// Initializes all L5Sharp types. This should be called in source generators before using the library.
/// </summary>
public static void Initialize()
{
if (_initialized) return;
_initialized = true;
InitializeEnums();
InitializeParsableTypes();
InitializeSerializableTypes();
InitializeDataTypes();
}
/// <summary>
/// Initializes all enum types found in the assembly.
/// </summary>
private static void InitializeEnums()
{
var baseType = typeof(LogixEnum);
var assembly = baseType.Assembly;
var enumTypes = assembly.GetTypes()
.Where(t => baseType.IsAssignableFrom(t) && t is { IsClass: true, IsAbstract: false })
.ToList();
foreach (var type in enumTypes)
{
try
{
if (type.IsGenericType) continue;
var getAllMethod = type.GetMethod("All", BindingFlags.Public | BindingFlags.Static);
if (getAllMethod != null)
{
var result = getAllMethod.Invoke(null, null);
if (result is IEnumerable<LogixEnum> enumValues)
{
LogixEnum.RegisterEnumType(type, () => enumValues.ToArray());
}
}
}
catch
{
// Skip types that can't be initialized
}
}
}
/// <summary>
/// Initializes all types that implement ILogixParsable.
/// </summary>
private static void InitializeParsableTypes()
{
var assembly = typeof(LogixParser).Assembly;
var parsableTypes = assembly.GetTypes()
.Where(t => !t.IsAbstract && !t.IsGenericType)
.Where(IsLogixParsable)
.ToList();
foreach (var type in parsableTypes)
{
try
{
LogixParser.RegisterParsableType(type);
}
catch
{
// Skip types that can't be registered
}
}
}
/// <summary>
/// Checks if a type implements ILogixParsable interface.
/// </summary>
private static bool IsLogixParsable(Type type)
{
return type.GetInterfaces().Any(i =>
i.IsGenericType
&& i.GetGenericTypeDefinition() == typeof(ILogixParsable<>)
&& i.GetGenericArguments().All(a => !a.IsGenericParameter)
);
}
/// <summary>
/// Initializes all serializable LogixElement types.
/// </summary>
private static void InitializeSerializableTypes()
{
var baseType = typeof(LogixElement);
var assembly = baseType.Assembly;
var elementTypes = assembly.GetTypes()
.Where(t => baseType.IsAssignableFrom(t)
&& t is { IsAbstract: false, IsPublic: true }
&& !ExcludedTypes.Contains(t))
.ToList();
foreach (var type in elementTypes)
{
try
{
var hasXElementConstructor = type.GetConstructor([typeof(XElement)]) != null;
if (hasXElementConstructor)
{
var isDataType = typeof(LogixData).IsAssignableFrom(type);
LogixSerializer.RegisterType(type, isDataType); // Reflection handles this automatically
}
}
catch
{
// Skip types that can't be registered
}
}
}
/// <summary>
/// Initializes all LogixData types.
/// </summary>
private static void InitializeDataTypes()
{
var baseType = typeof(LogixData);
var assembly = baseType.Assembly;
var dataTypes = assembly.GetTypes()
.Where(t => baseType.IsAssignableFrom(t)
&& t is { IsAbstract: false, IsPublic: true }
&& !ExcludedDataTypes.Contains(t))
.ToList();
foreach (var type in dataTypes)
{
try
{
var parameterlessConstructor = type.GetConstructor(Type.EmptyTypes);
if (parameterlessConstructor != null)
{
LogixData.RegisterDataType(type, () => (LogixData)Activator.CreateInstance(type)!); // Reflection handles this automatically
}
}
catch
{
// Skip types that can't be registered
}
}
}
While each type then would need to be kept track of:
/// <summary>
/// Registry for manually registered enum types (for source generator compatibility).
/// </summary>
private static readonly Dictionary<Type, Func<LogixEnum[]>> _registeredEnumTypes = new();
private static readonly object _lockObject = new();
/// <summary>
/// a global enum cache for all enumeration types defined in the assembly.
/// </summary>
private static readonly Lazy<Dictionary<Type, LogixEnum[]>> Enums = new(AllOptions,
LazyThreadSafetyMode.ExecutionAndPublication);
/// <summary>
/// Register an enum type with a function that returns all its values. Used for source generator compatibility.
/// </summary>
public static void RegisterEnumType(Type type, Func<LogixEnum[]> getValues)
{
lock (_lockObject)
{
_registeredEnumTypes[type] = getValues;
}
}
Thanks for your consideration for this addition!
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request