Fix IcedAssembler immediate overflow and unwrap invoke exceptions
- TryBind wraps Convert.ChangeType in TryChangeType so an immediate that overflows a candidate parameter type returns false (letting a wider overload be tried) instead of throwing OverflowException out of assembly. Verified: "mov eax, 4294967295" and "mov eax, -2147483649" no longer crash. - Immediate now carries a boxed long OR ulong; TryParseImmediate parses decimal values above long.MaxValue via a ulong fallback, and hex via ulong. Previously such literals were rejected at parse time. - Unwrap TargetInvocationException from method.Invoke so callers see the real Iced failure, not the reflection wrapper. Tests: 231 passing, 4 skipped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -129,7 +129,16 @@ public sealed class IcedAssembler : IAssembler
|
||||
|
||||
if (TryBind(parameters, operands, out object?[]? boundArgs))
|
||||
{
|
||||
method.Invoke(assembler, boundArgs);
|
||||
try
|
||||
{
|
||||
method.Invoke(assembler, boundArgs);
|
||||
}
|
||||
catch (TargetInvocationException ex) when (ex.InnerException is not null)
|
||||
{
|
||||
// Surface the real Iced failure rather than the reflection wrapper.
|
||||
throw ex.InnerException;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -151,7 +160,16 @@ public sealed class IcedAssembler : IAssembler
|
||||
switch (operand)
|
||||
{
|
||||
case Immediate imm when IsNumeric(paramType):
|
||||
args[i] = Convert.ChangeType(imm.Value, paramType, CultureInfo.InvariantCulture);
|
||||
// An immediate that overflows this parameter's type means this overload
|
||||
// is the wrong width; return false so a wider overload can be tried
|
||||
// instead of crashing the whole assembly.
|
||||
if (!TryChangeType(imm.Value, paramType, out object? converted))
|
||||
{
|
||||
boundArgs = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
args[i] = converted;
|
||||
break;
|
||||
|
||||
case not null when paramType.IsInstanceOfType(operand):
|
||||
@@ -178,28 +196,63 @@ public sealed class IcedAssembler : IAssembler
|
||||
if (labels.TryGetValue(token, out Label label))
|
||||
return label;
|
||||
|
||||
if (TryParseImmediate(token, out long value))
|
||||
return new Immediate(value);
|
||||
if (TryParseImmediate(token, out object? value))
|
||||
return new Immediate(value!);
|
||||
|
||||
throw new NotSupportedException(
|
||||
$"Unrecognized operand '{token}' (expected a register, an immediate, or a label).");
|
||||
}
|
||||
|
||||
private static bool TryParseImmediate(string token, out long value)
|
||||
// Parses an immediate as the narrowest of long/ulong that holds it, boxed. Storing the
|
||||
// widest representation lets TryChangeType later narrow it to whatever integer parameter
|
||||
// the chosen overload expects — and reject (rather than crash on) values that do not fit.
|
||||
private static bool TryParseImmediate(string token, out object? value)
|
||||
{
|
||||
value = null;
|
||||
bool negative = token.StartsWith('-');
|
||||
string body = negative ? token[1..] : token;
|
||||
|
||||
bool ok;
|
||||
if (body.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
|
||||
ok = long.TryParse(body[2..], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out value);
|
||||
{
|
||||
if (!ulong.TryParse(body[2..], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong hex))
|
||||
return false;
|
||||
|
||||
value = negative ? -(long)hex : hex;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (negative)
|
||||
{
|
||||
if (!long.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out long signed))
|
||||
return false;
|
||||
|
||||
value = signed;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Non-negative decimal: prefer long, fall back to ulong for values above long.MaxValue.
|
||||
if (long.TryParse(body, NumberStyles.Integer, CultureInfo.InvariantCulture, out long asLong))
|
||||
value = asLong;
|
||||
else if (ulong.TryParse(body, NumberStyles.Integer, CultureInfo.InvariantCulture, out ulong asULong))
|
||||
value = asULong;
|
||||
else
|
||||
ok = long.TryParse(body, NumberStyles.Integer, CultureInfo.InvariantCulture, out value);
|
||||
return false;
|
||||
|
||||
if (ok && negative)
|
||||
value = -value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return ok;
|
||||
private static bool TryChangeType(object value, Type targetType, out object? result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex) when (ex is OverflowException or InvalidCastException or FormatException)
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsNumeric(Type type) => Type.GetTypeCode(type) is
|
||||
@@ -266,9 +319,10 @@ public sealed class IcedAssembler : IAssembler
|
||||
return map;
|
||||
}
|
||||
|
||||
// A parsed immediate, distinguished from register/label operands so binding can widen
|
||||
// or narrow it to whichever integer parameter type the chosen overload expects.
|
||||
private readonly record struct Immediate(long Value);
|
||||
// A parsed immediate (boxed long or ulong), distinguished from register/label operands
|
||||
// so binding can narrow it to whichever integer parameter type the chosen overload
|
||||
// expects — or reject it when it does not fit.
|
||||
private readonly record struct Immediate(object Value);
|
||||
|
||||
private sealed class ByteListCodeWriter : CodeWriter
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user