Package: purescript-lua-integers
File: src/Data/Int/Bits.lua
Function: shl / shr / zshr
Class: semantics Severity: medium
All three use math.pow/division and do not implement JS 32-bit shift semantics. (1) shl: a * math.pow(2, b) returns a float and is neither masked to a 5-bit shift count nor wrapped to Int32, so shl 1 31 gives 2147483648.0 (JS 1 << 31 == -2147483648) and shl 1 32 gives 4294967296 (JS 1 << 32 == 1, count taken mod 32). (2) zshr is identical to shr (math.floor(a / 2^b)), but JS >>> is the UNSIGNED right shift: zshr (-8) 1 must be 2147483644, while the Lua returns -4. (3) shr on negative a happens to match arithmetic shift for the cases tried, but neither masks the shift count nor stays in Int32.
Current (Lua):
shl = (function(a) return function(b) return a * math.pow(2, b) end end),
shr = (function(a) return function(b) return math.floor(a / math.pow(2, b)) end end),
zshr = (function(a) return function(b) return math.floor(a / math.pow(2, b)) end end),
Expected: JS 32-bit shifts: shift count taken mod 32, result wrapped to signed Int32 for shl/shr; zshr operates on the unsigned 32-bit value (zshr (-8) 1 == 2147483644). shl 1 31 == -2147483648, shl 1 32 == 1.
Proposed fix:
Implement via an unsigned-32-bit helper: for shl, compute `(a * 2^(b % 32)) % 0x100000000` then reinterpret as signed Int32; for zshr, first map a to unsigned (a + 0x100000000 if a < 0) then `math.floor(u / 2^(b % 32))`; for shr, do an arithmetic shift on the signed value with count masked to 5 bits. A shared signed/unsigned-32 conversion pair removes the duplication.
Found by the FFI audit; reproduced under Lua 5.1.
Package: purescript-lua-integers
File:
src/Data/Int/Bits.luaFunction:
shl / shr / zshrClass: semantics Severity: medium
All three use
math.pow/division and do not implement JS 32-bit shift semantics. (1) shl:a * math.pow(2, b)returns a float and is neither masked to a 5-bit shift count nor wrapped to Int32, soshl 1 31gives 2147483648.0 (JS1 << 31== -2147483648) andshl 1 32gives 4294967296 (JS1 << 32== 1, count taken mod 32). (2) zshr is identical to shr (math.floor(a / 2^b)), but JS>>>is the UNSIGNED right shift:zshr (-8) 1must be 2147483644, while the Lua returns -4. (3) shr on negative a happens to match arithmetic shift for the cases tried, but neither masks the shift count nor stays in Int32.Current (Lua):
Expected: JS 32-bit shifts: shift count taken mod 32, result wrapped to signed Int32 for shl/shr; zshr operates on the unsigned 32-bit value (zshr (-8) 1 == 2147483644). shl 1 31 == -2147483648, shl 1 32 == 1.
Proposed fix:
Found by the FFI audit; reproduced under Lua 5.1.