|
|
@ -24,10 +24,12 @@ public unsafe class IterActionGenerator |
|
|
|
private static readonly PropertyInfo _handleTargetProp = typeof(GCHandle).GetProperty(nameof(GCHandle.Target))!; |
|
|
|
private static readonly PropertyInfo _handleTargetProp = typeof(GCHandle).GetProperty(nameof(GCHandle.Target))!; |
|
|
|
|
|
|
|
|
|
|
|
private static readonly ConditionalWeakTable<MethodInfo, IterActionGenerator> _cache = new(); |
|
|
|
private static readonly ConditionalWeakTable<MethodInfo, IterActionGenerator> _cache = new(); |
|
|
|
|
|
|
|
private static readonly Dictionary<Type, Action<ILGeneratorWrapper, IArgument<Iterator>>> _globalUniqueParameters = new() { |
|
|
|
|
|
|
|
[typeof(Universe)] = (IL, iter) => { IL.Load(iter, _iteratorUniverseProp); }, |
|
|
|
|
|
|
|
[typeof(TimeSpan)] = (IL, iter) => { IL.Load(iter, _iteratorDeltaTimeProp); }, |
|
|
|
|
|
|
|
}; |
|
|
|
private static readonly Dictionary<Type, Action<ILGeneratorWrapper, IArgument<Iterator>, ILocal<int>>> _uniqueParameters = new() { |
|
|
|
private static readonly Dictionary<Type, Action<ILGeneratorWrapper, IArgument<Iterator>, ILocal<int>>> _uniqueParameters = new() { |
|
|
|
[typeof(Iterator)] = (IL, iter, i) => { IL.Load(iter); }, |
|
|
|
[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); }, |
|
|
|
[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))) |
|
|
|
// if (!Parameters.Any(c => c.IsRequired && (c.Kind != ParamKind.Unique))) |
|
|
|
// throw new ArgumentException($"At least one parameter in {method} is required"); |
|
|
|
// throw new ArgumentException($"At least one parameter in {method} is required"); |
|
|
|
|
|
|
|
|
|
|
|
var terms = new List<Term>(); |
|
|
|
|
|
|
|
var name = "<>Query_" + string.Join("_", Parameters.Select(p => p.UnderlyingType.Name)); |
|
|
|
var name = "<>Query_" + string.Join("_", Parameters.Select(p => p.UnderlyingType.Name)); |
|
|
|
var genMethod = new DynamicMethod(name, null, new[] { typeof(object), typeof(Iterator) }); |
|
|
|
var genMethod = new DynamicMethod(name, null, new[] { typeof(object), typeof(Iterator) }); |
|
|
|
var IL = new ILGeneratorWrapper(genMethod); |
|
|
|
var IL = new ILGeneratorWrapper(genMethod); |
|
|
@ -69,12 +70,32 @@ public unsafe class IterActionGenerator |
|
|
|
var instanceArg = IL.Argument<object?>(0); |
|
|
|
var instanceArg = IL.Argument<object?>(0); |
|
|
|
var iteratorArg = IL.Argument<Iterator>(1); |
|
|
|
var iteratorArg = IL.Argument<Iterator>(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<Term>(); |
|
|
|
|
|
|
|
GeneratedAction = genMethod.CreateDelegate<Action<object?, Iterator>>(); |
|
|
|
|
|
|
|
ReadableString = IL.ToReadableString(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var terms = new List<Term>(); |
|
|
|
var fieldLocals = new ILocal[Parameters.Length]; |
|
|
|
var fieldLocals = new ILocal[Parameters.Length]; |
|
|
|
var tempLocals = new ILocal[Parameters.Length]; |
|
|
|
var tempLocals = new ILocal[Parameters.Length]; |
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < Parameters.Length; i++) { |
|
|
|
for (var i = 0; i < Parameters.Length; i++) { |
|
|
|
var p = Parameters[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. |
|
|
|
// Add an entry to the terms to look for this type. |
|
|
|
terms.Add(new(universe.LookupOrThrow(p.UnderlyingType)) { |
|
|
|
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. |
|
|
|
// If there's any reference type parameters, we need to define a GCHandle local. |
|
|
|
var hasReferenceType = Parameters |
|
|
|
var hasReferenceType = Parameters |
|
|
|
.Where(p => p.Kind != ParamKind.Unique) |
|
|
|
.Where(p => p.Kind > ParamKind.Unique) |
|
|
|
.Any(p => !p.UnderlyingType.IsValueType); |
|
|
|
.Any(p => !p.UnderlyingType.IsValueType); |
|
|
|
var handleLocal = hasReferenceType ? IL.Local<GCHandle>() : null; |
|
|
|
var handleLocal = hasReferenceType ? IL.Local<GCHandle>() : null; |
|
|
|
|
|
|
|
|
|
|
|
using (IL.For(() => IL.Load(iteratorArg, _iteratorCountProp), out var currentLocal)) { |
|
|
|
using (IL.For(() => IL.Load(iteratorArg, _iteratorCountProp), out var currentLocal)) { |
|
|
|
if (!Method.IsStatic) |
|
|
|
if (!Method.IsStatic) IL.Load(instanceArg); |
|
|
|
IL.Load(instanceArg); |
|
|
|
|
|
|
|
for (var i = 0; i < Parameters.Length; i++) { |
|
|
|
for (var i = 0; i < Parameters.Length; i++) { |
|
|
|
var p = Parameters[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()}"); |
|
|
|
IL.Comment($"Unique parameter {p.ParameterType.GetFriendlyName()}"); |
|
|
|
_uniqueParameters[p.ParameterType](IL, iteratorArg, currentLocal); |
|
|
|
_uniqueParameters[p.ParameterType](IL, iteratorArg, currentLocal); |
|
|
|
} else if (p.Kind is ParamKind.Has or ParamKind.Not) { |
|
|
|
} 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.IsArray) throw new ArgumentException("Arrays are not supported\nParameter: " + info); |
|
|
|
if (info.ParameterType.IsPointer) throw new ArgumentException("Pointers 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)) |
|
|
|
if (_uniqueParameters.ContainsKey(info.ParameterType)) |
|
|
|
return new(info, index, ParamKind.Unique, info.ParameterType, info.ParameterType); |
|
|
|
return new(info, index, ParamKind.Unique, info.ParameterType, info.ParameterType); |
|
|
|
|
|
|
|
|
|
|
@ -289,7 +314,9 @@ public unsafe class IterActionGenerator |
|
|
|
|
|
|
|
|
|
|
|
public enum ParamKind |
|
|
|
public enum ParamKind |
|
|
|
{ |
|
|
|
{ |
|
|
|
/// <summary> Parameter is not part of terms, handled uniquely, such as Universe and Entity. </summary> |
|
|
|
/// <summary> Parameter is not part of terms, handled uniquely, such as Universe. </summary> |
|
|
|
|
|
|
|
GlobalUnique, |
|
|
|
|
|
|
|
/// <summary> Parameter is unique per matched entity, such as EntityRef. </summary> |
|
|
|
Unique, |
|
|
|
Unique, |
|
|
|
/// <summary> Passed by value. </summary> |
|
|
|
/// <summary> Passed by value. </summary> |
|
|
|
Normal, |
|
|
|
Normal, |
|
|
|