From 3fb9d4db1cb7d0d0c83f4a564c797b48d78e15fc Mon Sep 17 00:00:00 2001 From: Deeptanshu Lal <143280861+Deeptanshuu@users.noreply.github.com> Date: Wed, 15 Jul 2026 04:04:08 +0530 Subject: [PATCH] [Tools] Fix ShaderDump reflection invoke against new optional parameters (#166) TryCompileVertexShader gained an optional scalarRegisterBufferIndex parameter (#156), and reflection Invoke does not apply C# default parameter values, so ShaderDump crashed with TargetParameterCountException. Pad trailing optional parameters with Type.Missing under BindingFlags.OptionalParamBinding so the declared defaults are used; only a new required parameter now needs a tool update, and that fails with a named error instead of a crash. Verified: all five programs behave as expected (exit 0), all eight emitted blobs pass spirv-val --target-env vulkan1.3. Co-authored-by: Claude Fable 5 --- tools/SharpEmu.Tools.ShaderDump/Program.cs | 39 +++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/tools/SharpEmu.Tools.ShaderDump/Program.cs b/tools/SharpEmu.Tools.ShaderDump/Program.cs index edab6844..47696348 100644 --- a/tools/SharpEmu.Tools.ShaderDump/Program.cs +++ b/tools/SharpEmu.Tools.ShaderDump/Program.cs @@ -201,8 +201,8 @@ foreach (var (name, expectTranslate, words) in testPrograms) null, null)!; - object?[] compileArgs = [state, evaluation, null, null, 0, -1, 0]; - if ((bool)tryCompile.Invoke(null, compileArgs)!) + var compileArgs = PadWithDefaults(tryCompile, [state, evaluation, null, null]); + if ((bool)tryCompile.Invoke(null, BindingFlags.OptionalParamBinding, null, compileArgs, null)!) { var shader = compileArgs[2]!; var spirv = (byte[])shader.GetType().GetProperty("Spirv")!.GetValue(shader)!; @@ -216,8 +216,8 @@ foreach (var (name, expectTranslate, words) in testPrograms) Console.WriteLine($"[{name}] emit: FAILED ({compileArgs[3]})"); } - object?[] computeArgs = [state, evaluation, 1u, 1u, 1u, null, null]; - if ((bool)tryCompileCompute.Invoke(null, computeArgs)!) + var computeArgs = PadWithDefaults(tryCompileCompute, [state, evaluation, 1u, 1u, 1u, null, null]); + if ((bool)tryCompileCompute.Invoke(null, BindingFlags.OptionalParamBinding, null, computeArgs, null)!) { var shader = computeArgs[5]!; var spirv = (byte[])shader.GetType().GetProperty("Spirv")!.GetValue(shader)!; @@ -237,6 +237,37 @@ Console.WriteLine(failures == 0 : $"RESULT: {failures} unexpected outcome(s)"); Environment.ExitCode = failures == 0 ? 0 : 1; +// Reflection Invoke does not apply C# default parameter values, so a newly +// added optional parameter on a translator entry point would otherwise throw +// TargetParameterCountException. Type.Missing + OptionalParamBinding lets the +// runtime substitute the declared defaults; only a new *required* parameter +// should force a tool update. +static object?[] PadWithDefaults(MethodInfo method, object?[] arguments) +{ + var parameters = method.GetParameters(); + if (arguments.Length > parameters.Length) + { + throw new InvalidOperationException( + $"{method.DeclaringType?.Name}.{method.Name} takes fewer parameters than the tool supplies"); + } + + var padded = new object?[parameters.Length]; + arguments.CopyTo(padded, 0); + for (var i = arguments.Length; i < padded.Length; i++) + { + if (!parameters[i].IsOptional) + { + throw new InvalidOperationException( + $"{method.DeclaringType?.Name}.{method.Name} gained a required parameter " + + $"'{parameters[i].Name}' — the tool needs updating"); + } + + padded[i] = Type.Missing; + } + + return padded; +} + internal sealed class FakeMemory : ICpuMemory { private readonly List<(ulong Base, byte[] Data)> _regions = [];