diff --git a/src/gaemstone/Utility/IL/IterActionGenerator.cs b/src/gaemstone/Utility/IL/IterActionGenerator.cs index 4c82e0f..d68c324 100644 --- a/src/gaemstone/Utility/IL/IterActionGenerator.cs +++ b/src/gaemstone/Utility/IL/IterActionGenerator.cs @@ -24,10 +24,12 @@ public unsafe class IterActionGenerator private static readonly PropertyInfo _handleTargetProp = typeof(GCHandle).GetProperty(nameof(GCHandle.Target))!; private static readonly ConditionalWeakTable _cache = new(); + private static readonly Dictionary>> _globalUniqueParameters = new() { + [typeof(Universe)] = (IL, iter) => { IL.Load(iter, _iteratorUniverseProp); }, + [typeof(TimeSpan)] = (IL, iter) => { IL.Load(iter, _iteratorDeltaTimeProp); }, + }; private static readonly Dictionary, ILocal>> _uniqueParameters = new() { [typeof(Iterator)] = (IL, iter, i) => { IL.Load(iter); }, - [typeof(Universe)] = (IL, iter, i) => { IL.Load(iter, _iteratorUniverseProp); }, - [typeof(TimeSpan)] = (IL, iter, i) => { IL.Load(iter, _iteratorDeltaTimeProp); }, [typeof(EntityRef)] = (IL, iter, i) => { IL.Load(iter); IL.Load(i); IL.Call(_iteratorEntityMethod); }, }; @@ -61,7 +63,6 @@ public unsafe class IterActionGenerator // if (!Parameters.Any(c => c.IsRequired && (c.Kind != ParamKind.Unique))) // throw new ArgumentException($"At least one parameter in {method} is required"); - var terms = new List(); var name = "<>Query_" + string.Join("_", Parameters.Select(p => p.UnderlyingType.Name)); var genMethod = new DynamicMethod(name, null, new[] { typeof(object), typeof(Iterator) }); var IL = new ILGeneratorWrapper(genMethod); @@ -69,12 +70,32 @@ public unsafe class IterActionGenerator var instanceArg = IL.Argument(0); var iteratorArg = IL.Argument(1); + // If parameters only contains global unique paremeters (such as + // Universe or TimeSpan), or is empty, just run the system, since + // without terms it won't match any entities anyway. + if (Parameters.All(p => p.Kind == ParamKind.GlobalUnique)) { + if (!Method.IsStatic) IL.Load(instanceArg); + foreach (var p in Parameters) { + IL.Comment($"Global unique parameter {p.ParameterType.GetFriendlyName()}"); + _globalUniqueParameters[p.ParameterType](IL, iteratorArg); + } + IL.Call(Method); + IL.Return(); + + Terms = Array.Empty(); + GeneratedAction = genMethod.CreateDelegate>(); + ReadableString = IL.ToReadableString(); + + return; + } + + var terms = new List(); var fieldLocals = new ILocal[Parameters.Length]; var tempLocals = new ILocal[Parameters.Length]; for (var i = 0; i < Parameters.Length; i++) { var p = Parameters[i]; - if (p.Kind == ParamKind.Unique) continue; + if (p.Kind <= ParamKind.Unique) continue; // Add an entry to the terms to look for this type. terms.Add(new(universe.LookupOrThrow(p.UnderlyingType)) { @@ -128,16 +149,18 @@ public unsafe class IterActionGenerator // If there's any reference type parameters, we need to define a GCHandle local. var hasReferenceType = Parameters - .Where(p => p.Kind != ParamKind.Unique) + .Where(p => p.Kind > ParamKind.Unique) .Any(p => !p.UnderlyingType.IsValueType); var handleLocal = hasReferenceType ? IL.Local() : null; using (IL.For(() => IL.Load(iteratorArg, _iteratorCountProp), out var currentLocal)) { - if (!Method.IsStatic) - IL.Load(instanceArg); + if (!Method.IsStatic) IL.Load(instanceArg); for (var i = 0; i < Parameters.Length; i++) { var p = Parameters[i]; - if (p.Kind == ParamKind.Unique) { + if (p.Kind == ParamKind.GlobalUnique) { + IL.Comment($"Global unique parameter {p.ParameterType.GetFriendlyName()}"); + _globalUniqueParameters[p.ParameterType](IL, iteratorArg); + } else if (p.Kind == ParamKind.Unique) { IL.Comment($"Unique parameter {p.ParameterType.GetFriendlyName()}"); _uniqueParameters[p.ParameterType](IL, iteratorArg, currentLocal); } else if (p.Kind is ParamKind.Has or ParamKind.Not) { @@ -242,6 +265,8 @@ public unsafe class IterActionGenerator if (info.ParameterType.IsArray) throw new ArgumentException("Arrays are not supported\nParameter: " + info); if (info.ParameterType.IsPointer) throw new ArgumentException("Pointers are not supported\nParameter: " + info); + if (_globalUniqueParameters.ContainsKey(info.ParameterType)) + return new(info, index, ParamKind.GlobalUnique, info.ParameterType, info.ParameterType); if (_uniqueParameters.ContainsKey(info.ParameterType)) return new(info, index, ParamKind.Unique, info.ParameterType, info.ParameterType); @@ -289,7 +314,9 @@ public unsafe class IterActionGenerator public enum ParamKind { - /// Parameter is not part of terms, handled uniquely, such as Universe and Entity. + /// Parameter is not part of terms, handled uniquely, such as Universe. + GlobalUnique, + /// Parameter is unique per matched entity, such as EntityRef. Unique, /// Passed by value. Normal,