|
|
|
@ -19,9 +19,9 @@ public unsafe class IterActionGenerator |
|
|
|
|
private static readonly PropertyInfo _iteratorUniverseProp = typeof(Iterator).GetProperty(nameof(Iterator.Universe))!; |
|
|
|
|
private static readonly PropertyInfo _iteratorDeltaTimeProp = typeof(Iterator).GetProperty(nameof(Iterator.DeltaTime))!; |
|
|
|
|
private static readonly PropertyInfo _iteratorCountProp = typeof(Iterator).GetProperty(nameof(Iterator.Count))!; |
|
|
|
|
private static readonly MethodInfo _iteratorEntityMethod = typeof(Iterator).GetMethod(nameof(Iterator.Entity))!; |
|
|
|
|
private static readonly MethodInfo _iteratorFieldMethod = typeof(Iterator).GetMethod(nameof(Iterator.Field))!; |
|
|
|
|
private static readonly MethodInfo _iteratorMaybeFieldMethod = typeof(Iterator).GetMethod(nameof(Iterator.MaybeField))!; |
|
|
|
|
private static readonly MethodInfo _iteratorEntityMethod = typeof(Iterator).GetMethod(nameof(Iterator.Entity))!; |
|
|
|
|
|
|
|
|
|
private static readonly MethodInfo _handleFromIntPtrMethod = typeof(GCHandle).GetMethod(nameof(GCHandle.FromIntPtr))!; |
|
|
|
|
private static readonly PropertyInfo _handleTargetProp = typeof(GCHandle).GetProperty(nameof(GCHandle.Target))!; |
|
|
|
@ -46,21 +46,15 @@ public unsafe class IterActionGenerator |
|
|
|
|
|
|
|
|
|
public void RunWithTryCatch(object? instance, Iterator iter) |
|
|
|
|
{ |
|
|
|
|
try { GeneratedAction(instance, iter); } catch { |
|
|
|
|
Console.Error.WriteLine("Exception occured while running:"); |
|
|
|
|
Console.Error.WriteLine(" " + Method); |
|
|
|
|
Console.Error.WriteLine(); |
|
|
|
|
Console.Error.WriteLine("Method's IL code:"); |
|
|
|
|
Console.Error.WriteLine(ReadableString); |
|
|
|
|
Console.Error.WriteLine(); |
|
|
|
|
throw; |
|
|
|
|
} |
|
|
|
|
try { GeneratedAction(instance, iter); } |
|
|
|
|
catch { Console.Error.WriteLine(ReadableString); throw; } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public IterActionGenerator(Universe universe, MethodInfo method) |
|
|
|
|
{ |
|
|
|
|
Universe = universe; |
|
|
|
|
Method = method; |
|
|
|
|
Universe = universe; |
|
|
|
|
Method = method; |
|
|
|
|
Parameters = method.GetParameters().Select(ParamInfo.Build).ToImmutableArray(); |
|
|
|
|
|
|
|
|
|
var name = "<>Query_" + string.Join("_", method.Name); |
|
|
|
|
var genMethod = new DynamicMethod(name, null, new[] { typeof(object), typeof(Iterator) }); |
|
|
|
@ -70,13 +64,11 @@ public unsafe class IterActionGenerator |
|
|
|
|
var iteratorArg = IL.Argument<Iterator>(1); |
|
|
|
|
|
|
|
|
|
var fieldIndex = 1; |
|
|
|
|
var parameters = new List<(ParamInfo Info, Term? Term, ILocal? FieldLocal, ILocal? TempLocal)>(); |
|
|
|
|
foreach (var info in method.GetParameters()) { |
|
|
|
|
var p = ParamInfo.Build(info); |
|
|
|
|
|
|
|
|
|
var paramData = new List<(ParamInfo Info, Term? Term, ILocal? FieldLocal, ILocal? TempLocal)>(); |
|
|
|
|
foreach (var p in Parameters) { |
|
|
|
|
// If the parameter is unique, we don't create a term for it. |
|
|
|
|
if (p.Kind <= ParamKind.Unique) |
|
|
|
|
{ parameters.Add((p, null, null, null)); continue; } |
|
|
|
|
{ paramData.Add((p, null, null, null)); continue; } |
|
|
|
|
|
|
|
|
|
// Create a term to add to the query. |
|
|
|
|
var term = new Term(universe.LookupOrThrow(p.UnderlyingType)) { |
|
|
|
@ -96,7 +88,7 @@ public unsafe class IterActionGenerator |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
var spanType = typeof(Span<>).MakeGenericType(p.FieldType); |
|
|
|
|
var fieldLocal = IL.Local(spanType, $"{info.Name}Field"); |
|
|
|
|
var fieldLocal = IL.Local(spanType, $"{p.Info.Name}Field"); |
|
|
|
|
var tempLocal = (ILocal?)null; |
|
|
|
|
|
|
|
|
|
switch (p.Kind) { |
|
|
|
@ -104,27 +96,27 @@ public unsafe class IterActionGenerator |
|
|
|
|
if (!p.ParameterType.IsValueType) break; |
|
|
|
|
// If a "has" or "not" parameter is a struct, we require a temporary local that |
|
|
|
|
// we can later load onto the stack when loading the arguments for the action. |
|
|
|
|
IL.Comment($"{info.Name}Temp = default({p.ParameterType});"); |
|
|
|
|
IL.Comment($"{p.Info.Name}Temp = default({p.ParameterType});"); |
|
|
|
|
tempLocal = IL.Local(p.ParameterType); |
|
|
|
|
IL.LoadAddr(tempLocal); |
|
|
|
|
IL.Init(tempLocal.LocalType); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case ParamKind.Nullable: |
|
|
|
|
IL.Comment($"{info.Name}Field = iterator.MaybeField<{p.FieldType.Name}>({fieldIndex})"); |
|
|
|
|
IL.Comment($"{p.Info.Name}Field = iterator.MaybeField<{p.FieldType.Name}>({fieldIndex})"); |
|
|
|
|
IL.Load(iteratorArg); |
|
|
|
|
IL.LoadConst(fieldIndex); |
|
|
|
|
IL.Call(_iteratorMaybeFieldMethod.MakeGenericMethod(p.FieldType)); |
|
|
|
|
IL.Store(fieldLocal); |
|
|
|
|
|
|
|
|
|
IL.Comment($"{info.Name}Temp = default({p.ParameterType});"); |
|
|
|
|
IL.Comment($"{p.Info.Name}Temp = default({p.ParameterType});"); |
|
|
|
|
tempLocal = IL.Local(p.ParameterType); |
|
|
|
|
IL.LoadAddr(tempLocal); |
|
|
|
|
IL.Init(tempLocal.LocalType); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
IL.Comment($"{info.Name}Field = iterator.Field<{p.FieldType.Name}>({fieldIndex})"); |
|
|
|
|
IL.Comment($"{p.Info.Name}Field = iterator.Field<{p.FieldType.Name}>({fieldIndex})"); |
|
|
|
|
IL.Load(iteratorArg); |
|
|
|
|
IL.LoadConst(fieldIndex); |
|
|
|
|
IL.Call(_iteratorFieldMethod.MakeGenericMethod(p.FieldType)); |
|
|
|
@ -132,102 +124,115 @@ public unsafe class IterActionGenerator |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
parameters.Add((p, term, fieldLocal, tempLocal)); |
|
|
|
|
paramData.Add((p, term, fieldLocal, tempLocal)); |
|
|
|
|
fieldIndex++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// If there's any reference type parameters, we need to define a GCHandle local. |
|
|
|
|
var hasReferenceType = parameters |
|
|
|
|
var hasReferenceType = paramData |
|
|
|
|
.Where(p => p.Info.Kind > ParamKind.Unique) |
|
|
|
|
.Any(p => !p.Info.UnderlyingType.IsValueType); |
|
|
|
|
var handleLocal = hasReferenceType ? IL.Local<GCHandle>() : null; |
|
|
|
|
|
|
|
|
|
IDisposable? forLoopBlock = null; |
|
|
|
|
ILocal<int>? forCurrentLocal = null; |
|
|
|
|
// If all parameters are fixed, iterator count will be 0, but since |
|
|
|
|
// the query matched, we want to run the callback at least once. |
|
|
|
|
if (parameters.Any(p => !p.Info.IsFixed)) |
|
|
|
|
forLoopBlock = IL.For(() => IL.Load(iteratorArg, _iteratorCountProp), out forCurrentLocal); |
|
|
|
|
|
|
|
|
|
if (!Method.IsStatic) |
|
|
|
|
IL.Load(instanceArg); |
|
|
|
|
|
|
|
|
|
foreach (var (info, term, fieldLocal, tempLocal) in parameters) { |
|
|
|
|
switch (info.Kind) { |
|
|
|
|
|
|
|
|
|
case ParamKind.GlobalUnique: |
|
|
|
|
IL.Comment($"Global unique parameter {info.ParameterType.GetFriendlyName()}"); |
|
|
|
|
_globalUniqueParameters[info.ParameterType](IL, iteratorArg); |
|
|
|
|
break; |
|
|
|
|
var countLocal = IL.Local<int>("iter_count"); |
|
|
|
|
var indexLocal = IL.Local<int>("iter_index"); |
|
|
|
|
|
|
|
|
|
case ParamKind.Unique: |
|
|
|
|
IL.Comment($"Unique parameter {info.ParameterType.GetFriendlyName()}"); |
|
|
|
|
_uniqueParameters[info.ParameterType](IL, iteratorArg, forCurrentLocal!); |
|
|
|
|
break; |
|
|
|
|
IL.Load(iteratorArg, _iteratorCountProp); |
|
|
|
|
IL.Store(countLocal); |
|
|
|
|
IL.Set(indexLocal, 0); |
|
|
|
|
|
|
|
|
|
case ParamKind.Has or ParamKind.Not: |
|
|
|
|
if (info.ParameterType.IsValueType) |
|
|
|
|
IL.LoadObj(tempLocal!); |
|
|
|
|
else IL.LoadNull(); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
var spanType = typeof(Span<>).MakeGenericType(info.FieldType); |
|
|
|
|
var spanItemMethod = spanType.GetProperty("Item")!.GetMethod!; |
|
|
|
|
var spanLengthMethod = spanType.GetProperty("Length")!.GetMethod!; |
|
|
|
|
|
|
|
|
|
IL.Comment($"Parameter {info.ParameterType.GetFriendlyName()}"); |
|
|
|
|
if (info.IsByRef) { |
|
|
|
|
IL.LoadAddr(fieldLocal!); |
|
|
|
|
if (info.IsFixed) IL.LoadConst(0); |
|
|
|
|
else IL.Load(forCurrentLocal!); |
|
|
|
|
IL.Call(spanItemMethod); |
|
|
|
|
} else if (info.IsRequired) { |
|
|
|
|
IL.LoadAddr(fieldLocal!); |
|
|
|
|
if (info.IsFixed) IL.LoadConst(0); |
|
|
|
|
else IL.Load(forCurrentLocal!); |
|
|
|
|
IL.Call(spanItemMethod); |
|
|
|
|
IL.LoadObj(info.FieldType); |
|
|
|
|
} else { |
|
|
|
|
var elseLabel = IL.DefineLabel(); |
|
|
|
|
var doneLabel = IL.DefineLabel(); |
|
|
|
|
IL.LoadAddr(fieldLocal!); |
|
|
|
|
IL.Call(spanLengthMethod); |
|
|
|
|
IL.GotoIfFalse(elseLabel); |
|
|
|
|
// If all parameters are fixed, iterator count will be 0, but since |
|
|
|
|
// the query matched, we want to run the callback at least once. |
|
|
|
|
IL.Comment("if (iter_count == 0) iter_count = 1;"); |
|
|
|
|
var dontIncrementLabel = IL.DefineLabel(); |
|
|
|
|
IL.Load(countLocal); |
|
|
|
|
IL.GotoIfTrue(dontIncrementLabel); |
|
|
|
|
IL.LoadConst(1); |
|
|
|
|
IL.Store(countLocal); |
|
|
|
|
IL.MarkLabel(dontIncrementLabel); |
|
|
|
|
|
|
|
|
|
IL.While("IteratorLoop", (@continue) => { |
|
|
|
|
IL.GotoIf(@continue, indexLocal, Comparison.LessThan, countLocal); |
|
|
|
|
}, (_, _) => { |
|
|
|
|
if (!Method.IsStatic) |
|
|
|
|
IL.Load(instanceArg); |
|
|
|
|
|
|
|
|
|
foreach (var (info, term, fieldLocal, tempLocal) in paramData) { |
|
|
|
|
switch (info.Kind) { |
|
|
|
|
|
|
|
|
|
case ParamKind.GlobalUnique: |
|
|
|
|
IL.Comment($"Global unique parameter {info.ParameterType.GetFriendlyName()}"); |
|
|
|
|
_globalUniqueParameters[info.ParameterType](IL, iteratorArg); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case ParamKind.Unique: |
|
|
|
|
IL.Comment($"Unique parameter {info.ParameterType.GetFriendlyName()}"); |
|
|
|
|
_uniqueParameters[info.ParameterType](IL, iteratorArg, indexLocal!); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case ParamKind.Has or ParamKind.Not: |
|
|
|
|
if (info.ParameterType.IsValueType) |
|
|
|
|
IL.LoadObj(tempLocal!); |
|
|
|
|
else IL.LoadNull(); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
var spanType = typeof(Span<>).MakeGenericType(info.FieldType); |
|
|
|
|
var spanItemMethod = spanType.GetProperty("Item")!.GetMethod!; |
|
|
|
|
var spanLengthMethod = spanType.GetProperty("Length")!.GetMethod!; |
|
|
|
|
|
|
|
|
|
IL.Comment($"Parameter {info.ParameterType.GetFriendlyName()}"); |
|
|
|
|
if (info.IsByRef) { |
|
|
|
|
IL.LoadAddr(fieldLocal!); |
|
|
|
|
if (info.IsFixed) IL.LoadConst(0); |
|
|
|
|
else IL.Load(forCurrentLocal!); |
|
|
|
|
else IL.Load(indexLocal!); |
|
|
|
|
IL.Call(spanItemMethod); |
|
|
|
|
} else if (info.IsRequired) { |
|
|
|
|
IL.LoadAddr(fieldLocal!); |
|
|
|
|
if (info.IsFixed) IL.LoadConst(0); |
|
|
|
|
else IL.Load(indexLocal!); |
|
|
|
|
IL.Call(spanItemMethod); |
|
|
|
|
IL.LoadObj(info.FieldType); |
|
|
|
|
if (info.Kind == ParamKind.Nullable) |
|
|
|
|
IL.New(info.ParameterType); |
|
|
|
|
IL.Goto(doneLabel); |
|
|
|
|
IL.MarkLabel(elseLabel); |
|
|
|
|
if (info.Kind == ParamKind.Nullable) |
|
|
|
|
IL.LoadObj(tempLocal!); |
|
|
|
|
else IL.LoadNull(); |
|
|
|
|
IL.MarkLabel(doneLabel); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!info.UnderlyingType.IsValueType) { |
|
|
|
|
IL.Comment($"Convert nint to {info.UnderlyingType.GetFriendlyName()}"); |
|
|
|
|
IL.Call(_handleFromIntPtrMethod); |
|
|
|
|
IL.Store(handleLocal!); |
|
|
|
|
IL.LoadAddr(handleLocal!); |
|
|
|
|
IL.Call(_handleTargetProp.GetMethod!); |
|
|
|
|
IL.Cast(info.UnderlyingType); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} else { |
|
|
|
|
var elseLabel = IL.DefineLabel(); |
|
|
|
|
var doneLabel = IL.DefineLabel(); |
|
|
|
|
IL.LoadAddr(fieldLocal!); |
|
|
|
|
IL.Call(spanLengthMethod); |
|
|
|
|
IL.GotoIfFalse(elseLabel); |
|
|
|
|
IL.LoadAddr(fieldLocal!); |
|
|
|
|
if (info.IsFixed) IL.LoadConst(0); |
|
|
|
|
else IL.Load(indexLocal!); |
|
|
|
|
IL.Call(spanItemMethod); |
|
|
|
|
IL.LoadObj(info.FieldType); |
|
|
|
|
if (info.Kind == ParamKind.Nullable) |
|
|
|
|
IL.New(info.ParameterType); |
|
|
|
|
IL.Goto(doneLabel); |
|
|
|
|
IL.MarkLabel(elseLabel); |
|
|
|
|
if (info.Kind == ParamKind.Nullable) |
|
|
|
|
IL.LoadObj(tempLocal!); |
|
|
|
|
else IL.LoadNull(); |
|
|
|
|
IL.MarkLabel(doneLabel); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!info.UnderlyingType.IsValueType) { |
|
|
|
|
IL.Comment($"Convert nint to {info.UnderlyingType.GetFriendlyName()}"); |
|
|
|
|
IL.Call(_handleFromIntPtrMethod); |
|
|
|
|
IL.Store(handleLocal!); |
|
|
|
|
IL.LoadAddr(handleLocal!); |
|
|
|
|
IL.Call(_handleTargetProp.GetMethod!); |
|
|
|
|
IL.Cast(info.UnderlyingType); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
IL.Call(Method); |
|
|
|
|
IL.Call(Method); |
|
|
|
|
|
|
|
|
|
forLoopBlock?.Dispose(); |
|
|
|
|
IL.Increment(indexLocal); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
IL.Return(); |
|
|
|
|
|
|
|
|
|
Parameters = parameters.Select(p => p.Info).ToImmutableList(); |
|
|
|
|
Terms = parameters.Where(p => p.Term != null).Select(p => p.Term!).ToImmutableList(); |
|
|
|
|
Terms = paramData.Where(p => p.Term != null).Select(p => p.Term!).ToImmutableList(); |
|
|
|
|
GeneratedAction = genMethod.CreateDelegate<Action<object?, Iterator>>(); |
|
|
|
|
ReadableString = IL.ToReadableString(); |
|
|
|
|
} |
|
|
|
|