From 6fbcefbe0f19f58d153b2ba059ed28d6ed04b5e4 Mon Sep 17 00:00:00 2001 From: Marcelo Lv Cabral Date: Sat, 31 Oct 2026 18:29:21 -0700 Subject: [PATCH] fix(core): make ValueKind.Int32 coercion follow the device integer rule (#1271) tryCoerce built Integer values with new Int32(value), so a NaN Float/Double stayed NaN and neighbouring built-in methods read the same argument differently from the Dynamic + toIntegerArg ones. Use toArrayIndex so Float/Double are truncated (NaN as 0) and a LongInteger keeps its low 32 bits. Co-Authored-By: Claude Sonnet 5.5 --- src/core/brsTypes/Coercion.ts | 5 +++-- test/brsTypes/TypeCoercion.test.js | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/core/brsTypes/Coercion.ts b/src/core/brsTypes/Coercion.ts index b0c074752..f5cbd7477 100644 --- a/src/core/brsTypes/Coercion.ts +++ b/src/core/brsTypes/Coercion.ts @@ -53,11 +53,12 @@ export function tryCoerce(value: BrsType, target: ValueKind): BrsType | undefine return tryCoerce(value.unbox(), target); } - // numeric types can be cast between each other + // numeric types can be cast between each other; `Integer` follows `toArrayIndex` (the device rule shared with + // `toIntegerArg`): `Float`/`Double` are truncated with NaN as 0, a `LongInteger` keeps its low 32 bits if (isNumberKind(target) && isBrsNumber(value)) { switch (target) { case ValueKind.Int32: - return new Int32(value.getValue()); + return new Int32(toArrayIndex(value) ?? 0); case ValueKind.Int64: return new Int64(value.getValue()); case ValueKind.Float: diff --git a/test/brsTypes/TypeCoercion.test.js b/test/brsTypes/TypeCoercion.test.js index 384ffb7d1..b5f1e72d9 100644 --- a/test/brsTypes/TypeCoercion.test.js +++ b/test/brsTypes/TypeCoercion.test.js @@ -97,6 +97,10 @@ describe("type coercion", () => { ["float", new Float(3.14159), new Int32(3)], ["double", new Double(2.71828), new Int32(2)], ["longinteger", new Int64(2147483647119), new Int32(-881)], + ["negative float", new Float(-1.9), new Int32(-1)], + ["negative fraction", new Double(-0.5), new Int32(0)], + ["NaN float", new Float(Number.NaN), new Int32(0)], + ["NaN double", new Double(Number.NaN), new Int32(0)], ])("returns integer for %s target", (_type, input, output) => { expect(tryCoerce(input, ValueKind.Int32)).toEqual(output); expect(tryCoerce(input.box(), ValueKind.Int32)).toEqual(output);