diff --git a/std/cpp/_std/haxe/numeric/Int32Native.hx b/std/cpp/_std/haxe/numeric/Int32Native.hx new file mode 100644 index 00000000000..a3fe93955e5 --- /dev/null +++ b/std/cpp/_std/haxe/numeric/Int32Native.hx @@ -0,0 +1,25 @@ +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package haxe.numeric; + +typedef Int32Native = Int32Direct; diff --git a/std/cpp/_std/haxe/numeric/Int64Native.hx b/std/cpp/_std/haxe/numeric/Int64Native.hx index 7c26585d8c7..ed63c3b232a 100644 --- a/std/cpp/_std/haxe/numeric/Int64Native.hx +++ b/std/cpp/_std/haxe/numeric/Int64Native.hx @@ -98,7 +98,6 @@ private extern class CppInt64Helper { } #end -@:coreApi(check = Off) #if cppia extern #end diff --git a/std/haxe/Int32.hx b/std/haxe/Int32.hx index 615bea1860a..0c348e24831 100644 --- a/std/haxe/Int32.hx +++ b/std/haxe/Int32.hx @@ -22,78 +22,77 @@ package haxe; +import haxe.numeric.Int32Native; + /** - Int32 provides a 32-bit integer with consistent overflow behavior across - all platforms. + A cross-platform signed 32-bit integer with consistent overflow behavior. + + This abstract defines the operator overloads and public API surface. + The actual implementation is in `haxe.numeric.Int32Native`, which can be + shadowed by platform-specific `_std` directories for native support. + + On targets with native 32-bit Int (C++, JVM, HL), Int32 maps directly + to the platform Int with no overhead. On scripting targets (JS, PHP, Python, + Lua), operations are clamped to 32-bit range after each computation. On Neko, + values exceeding 31-bit range are auto-promoted to Float by the VM while + preserving correct 32-bit arithmetic. **/ @:transitive -abstract Int32(Int) from Int to Int { - @:op(-A) private inline function negate():Int32 - return clamp(~this + 1); +abstract Int32(Int32Native) from Int to Int { + private inline function new(x:Int32Native) + this = x; + + @:op(-A) private static inline function neg(x:Int32):Int32 + return Int32Native.neg(x); - @:op(++A) private inline function preIncrement():Int32 - return this = clamp(++this); + @:op(++A) private inline function preIncrement():Int32 { + this = Int32Native.add(this, 1); + return cast this; + } @:op(A++) private inline function postIncrement():Int32 { - var ret = this++; - this = clamp(this); + var ret = this; + this = Int32Native.add(this, 1); return ret; } - @:op(--A) private inline function preDecrement():Int32 - return this = clamp(--this); + @:op(--A) private inline function preDecrement():Int32 { + this = Int32Native.sub(this, 1); + return cast this; + } @:op(A--) private inline function postDecrement():Int32 { - var ret = this--; - this = clamp(this); + var ret = this; + this = Int32Native.sub(this, 1); return ret; } @:op(A + B) private static inline function add(a:Int32, b:Int32):Int32 - return clamp((a : Int) + (b : Int)); + return Int32Native.add(a, b); @:op(A + B) @:commutative private static inline function addInt(a:Int32, b:Int):Int32 - return clamp((a : Int) + (b : Int)); + return Int32Native.add(a, b); @:op(A + B) @:commutative private static function addFloat(a:Int32, b:Float):Float; @:op(A - B) private static inline function sub(a:Int32, b:Int32):Int32 - return clamp((a : Int) - (b : Int)); + return Int32Native.sub(a, b); @:op(A - B) private static inline function subInt(a:Int32, b:Int):Int32 - return clamp((a : Int) - (b : Int)); + return Int32Native.sub(a, b); @:op(A - B) private static inline function intSub(a:Int, b:Int32):Int32 - return clamp((a : Int) - (b : Int)); + return Int32Native.sub(a, b); @:op(A - B) private static function subFloat(a:Int32, b:Float):Float; @:op(A - B) private static function floatSub(a:Float, b:Int32):Float; - #if (js || php || python || lua) - #if js - // on JS we want to try using Math.imul, but we have to assign that function to Int32.mul only once, - // or else V8 will deoptimize it, so we need to be a bit funky with this. - // See https://github.com/HaxeFoundation/haxe/issues/5367 for benchmarks. - @:op(A * B) inline static function mul(a:Int32, b:Int32):Int32 - return _mul(a, b); - - static var _mul:Int32->Int32->Int32 = untyped if (Math.imul != null) - Math.imul - else - function(a:Int32, b:Int32):Int32 return clamp((a : Int) * ((b : Int) & 0xFFFF) + clamp((a : Int) * ((b : Int) >>> 16) << 16)); - #else - @:op(A * B) private static function mul(a:Int32, b:Int32):Int32 - return clamp((a : Int) * ((b : Int) & 0xFFFF) + clamp((a : Int) * ((b : Int) >>> 16) << 16)); - #end + @:op(A * B) private static inline function mul(a:Int32, b:Int32):Int32 + return Int32Native.mul(a, b); @:op(A * B) @:commutative private static inline function mulInt(a:Int32, b:Int):Int32 - return mul(a, b); - #else - @:op(A * B) private static function mul(a:Int32, b:Int32):Int32; - - @:op(A * B) @:commutative private static function mulInt(a:Int32, b:Int):Int32; - #end + return Int32Native.mul(a, b); @:op(A * B) @:commutative private static function mulFloat(a:Int32, b:Float):Float; @@ -169,114 +168,60 @@ abstract Int32(Int) from Int to Int { @:op(A >= B) private static function floatGte(a:Float, b:Int32):Bool; - #if (lua || python || php) @:op(~A) private static inline function complement(a:Int32):Int32 - #if lua return lua.Boot.clampInt32(~a); #else return clamp(~a); #end - #else - @:op(~A) private function complement():Int32; - #end - - @:op(A & B) private static function and(a:Int32, b:Int32):Int32; + return Int32Native.complement(a); - @:op(A & B) @:commutative private static function andInt(a:Int32, b:Int):Int32; + @:op(A & B) private static inline function and(a:Int32, b:Int32):Int32 + return Int32Native.and(a, b); - #if (lua || python || php) - @:op(A | B) private static #if (python || php) inline #end function or(a:Int32, b:Int32):Int32 - return clamp((a : Int) | (b : Int)); + @:op(A & B) @:commutative private static inline function andInt(a:Int32, b:Int):Int32 + return Int32Native.and(a, b); - @:op(A | B) @:commutative private #if (python || php) inline #end static function orInt(a:Int32, b:Int):Int32 - return clamp((a : Int) | b); - #else - @:op(A | B) private static function or(a:Int32, b:Int32):Int32; + @:op(A | B) private static inline function or(a:Int32, b:Int32):Int32 + return Int32Native.or(a, b); - @:op(A | B) @:commutative private static function orInt(a:Int32, b:Int):Int32; - #end + @:op(A | B) @:commutative private static inline function orInt(a:Int32, b:Int):Int32 + return Int32Native.or(a, b); - #if (lua || python || php) - @:op(A ^ B) private static #if (python || php) inline #end function xor(a:Int32, b:Int32):Int32 - return clamp((a : Int) ^ (b : Int)); + @:op(A ^ B) private static inline function xor(a:Int32, b:Int32):Int32 + return Int32Native.xor(a, b); - @:op(A ^ B) @:commutative private static #if (python || php) inline #end function xorInt(a:Int32, b:Int):Int32 - return clamp((a : Int) ^ b); - #else - @:op(A ^ B) private static function xor(a:Int32, b:Int32):Int32; + @:op(A ^ B) @:commutative private static inline function xorInt(a:Int32, b:Int):Int32 + return Int32Native.xor(a, b); - @:op(A ^ B) @:commutative private static function xorInt(a:Int32, b:Int):Int32; - #end + @:op(A >> B) private static inline function shr(a:Int32, b:Int32):Int32 + return Int32Native.shr(a, (b : Int)); - #if (lua || python || php) - @:op(A >> B) private static #if (python || php) inline #end function shr(a:Int32, b:Int32):Int32 - return clamp((a : Int) >> (b : Int)); + @:op(A >> B) private static inline function shrInt(a:Int32, b:Int):Int32 + return Int32Native.shr(a, b); - @:op(A >> B) private static #if (python || php) inline #end function shrInt(a:Int32, b:Int):Int32 - return clamp((a : Int) >> b); + @:op(A >> B) private static inline function intShr(a:Int, b:Int32):Int32 + return Int32Native.shr(a, (b : Int)); - @:op(A >> B) private static #if (python || php) inline #end function intShr(a:Int, b:Int32):Int32 - return clamp(a >> (b : Int)); - #else - @:op(A >> B) private static function shr(a:Int32, b:Int32):Int32; + @:op(A >>> B) private static inline function ushr(a:Int32, b:Int32):Int32 + return Int32Native.ushr(a, (b : Int)); - @:op(A >> B) private static function shrInt(a:Int32, b:Int):Int32; + @:op(A >>> B) private static inline function ushrInt(a:Int32, b:Int):Int32 + return Int32Native.ushr(a, b); - @:op(A >> B) private static function intShr(a:Int, b:Int32):Int32; - #end + @:op(A >>> B) private static inline function intUshr(a:Int, b:Int32):Int32 + return Int32Native.ushr(a, (b : Int)); - @:op(A >>> B) private static function ushr(a:Int32, b:Int32):Int32; - - @:op(A >>> B) private static function ushrInt(a:Int32, b:Int):Int32; - - @:op(A >>> B) private static function intUshr(a:Int, b:Int32):Int32; - - #if (php || python || lua) - // PHP may be 64-bit, so shifts must be clamped @:op(A << B) private static inline function shl(a:Int32, b:Int32):Int32 - return clamp((a : Int) << (b : Int)); + return Int32Native.shl(a, (b : Int)); @:op(A << B) private static inline function shlInt(a:Int32, b:Int):Int32 - return clamp((a : Int) << b); + return Int32Native.shl(a, b); @:op(A << B) private static inline function intShl(a:Int, b:Int32):Int32 - return clamp(a << (b : Int)); - #else - @:op(A << B) private static function shl(a:Int32, b:Int32):Int32; - - @:op(A << B) private static function shlInt(a:Int32, b:Int):Int32; - - @:op(A << B) private static function intShl(a:Int, b:Int32):Int32; - #end + return Int32Native.shl(a, (b : Int)); @:to private inline function toFloat():Float - return this; + return (this : Int); /** Compare `a` and `b` in unsigned mode. **/ - public static function ucompare(a:Int32, b:Int32):Int { - if (a < 0) - return b < 0 ? (~b - ~a) : 1; - return b < 0 ? -1 : (a - b); - } - - #if php - static var extraBits:Int = php.Const.PHP_INT_SIZE * 8 - 32; - #end - - #if !lua - inline - #end - static function clamp(x:Int):Int { - // force to-int conversion on platforms that require it - #if js - return x | 0; - #elseif php - // we might be on 64-bit php, so sign extend from 32-bit - return (x << extraBits) >> extraBits; - #elseif python - return (python.Syntax.code("{0} % {1}", (x + python.Syntax.opPow(2, 31)), python.Syntax.opPow(2, 32)) : Int) - python.Syntax.opPow(2, 31); - #elseif lua - return lua.Boot.clampInt32(x); - #else - return (x); - #end - } + public static inline function ucompare(a:Int32, b:Int32):Int + return Int32Native.ucompare(a, b); } diff --git a/std/haxe/numeric/Int32Direct.hx b/std/haxe/numeric/Int32Direct.hx new file mode 100644 index 00000000000..c8d07cd6017 --- /dev/null +++ b/std/haxe/numeric/Int32Direct.hx @@ -0,0 +1,81 @@ +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package haxe.numeric; + +/** + Shared Int32Native implementation for targets where `Int` is natively + 32-bit (C++, JVM, HashLink). No masking is needed; operations are identity + casts on the underlying type. + + This type is used internally via `typedef Int32Native = Int32Direct` + in the target-specific overrides. +**/ +abstract Int32Direct(Int) from Int to Int { + public static inline function neg(x:Int32Direct):Int32Direct + // Use ~x+1 (two's complement) rather than unary minus. + // On CPPIA, unary minus on Int can return a value wider than 32 bits, + // while bitwise NOT and addition stay within the native 32-bit int range. + return cast(~(x : Int) + 1); + + public static inline function add(a:Int32Direct, b:Int32Direct):Int32Direct + return cast((a : Int) + (b : Int)); + + public static inline function sub(a:Int32Direct, b:Int32Direct):Int32Direct + return cast((a : Int) - (b : Int)); + + public static inline function mul(a:Int32Direct, b:Int32Direct):Int32Direct + return cast((a : Int) * (b : Int)); + + public static inline function complement(a:Int32Direct):Int32Direct + return cast ~(a : Int); + + public static inline function and(a:Int32Direct, b:Int32Direct):Int32Direct + return cast((a : Int) & (b : Int)); + + public static inline function or(a:Int32Direct, b:Int32Direct):Int32Direct + return cast((a : Int) | (b : Int)); + + public static inline function xor(a:Int32Direct, b:Int32Direct):Int32Direct + return cast((a : Int) ^ (b : Int)); + + public static inline function shl(a:Int32Direct, b:Int):Int32Direct + return cast((a : Int) << b); + + public static inline function shr(a:Int32Direct, b:Int):Int32Direct + return cast((a : Int) >> b); + + public static inline function ushr(a:Int32Direct, b:Int):Int32Direct + return cast((a : Int) >>> b); + + public static function ucompare(a:Int32Direct, b:Int32Direct):Int { + if ((a : Int) < 0) + return (b : Int) < 0 ? (~(b : Int) - ~(a : Int)) : 1; + return (b : Int) < 0 ? -1 : ((a : Int) - (b : Int)); + } + + public inline function toFloat():Float + return this; + + public static inline function clamp(x:Int):Int32Direct + return cast x; +} diff --git a/std/haxe/numeric/Int32Native.hx b/std/haxe/numeric/Int32Native.hx new file mode 100644 index 00000000000..189df0f0cbf --- /dev/null +++ b/std/haxe/numeric/Int32Native.hx @@ -0,0 +1,133 @@ +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package haxe.numeric; + +typedef Int32Native = Int32NativeImpl; + +private abstract Int32NativeImpl(Int) from Int to Int { + public static inline function neg(x:Int32Native):Int32Native + return clamp(~(x : Int) + 1); + + public static inline function add(a:Int32Native, b:Int32Native):Int32Native + return clamp((a : Int) + (b : Int)); + + public static inline function sub(a:Int32Native, b:Int32Native):Int32Native + return clamp((a : Int) - (b : Int)); + + #if (js || php || python || lua) + #if js + // On JS we want to try using Math.imul, but we have to assign that function + // to _mul only once, or else V8 will deoptimize it. + // See https://github.com/HaxeFoundation/haxe/issues/5367 for benchmarks. + public static inline function mul(a:Int32Native, b:Int32Native):Int32Native + return _mul(a, b); + + static var _mul:Int32Native->Int32Native->Int32Native = untyped if (Math.imul != null) + Math.imul + else + function(a:Int32Native, b:Int32Native):Int32Native return clamp((a : Int) * ((b : Int) & 0xFFFF) + clamp((a : Int) * ((b : Int) >>> 16) << 16)); + #else + public static function mul(a:Int32Native, b:Int32Native):Int32Native + return clamp((a : Int) * ((b : Int) & 0xFFFF) + clamp((a : Int) * ((b : Int) >>> 16) << 16)); + #end + #else + public static inline function mul(a:Int32Native, b:Int32Native):Int32Native + return clamp((a : Int) * (b : Int)); + #end + + #if (lua || python || php) + public static #if (python || php) inline #end function complement(a:Int32Native):Int32Native + #if lua return lua.Boot.clampInt32(~(a : Int)); #else return clamp(~(a : Int)); #end + #else + public static inline function complement(a:Int32Native):Int32Native + return cast ~(a : Int); + #end + + public static inline function and(a:Int32Native, b:Int32Native):Int32Native + return cast((a : Int) & (b : Int)); + + #if (lua || python || php) + public static #if (python || php) inline #end function or(a:Int32Native, b:Int32Native):Int32Native + return clamp((a : Int) | (b : Int)); + #else + public static inline function or(a:Int32Native, b:Int32Native):Int32Native + return cast((a : Int) | (b : Int)); + #end + + #if (lua || python || php) + public static #if (python || php) inline #end function xor(a:Int32Native, b:Int32Native):Int32Native + return clamp((a : Int) ^ (b : Int)); + #else + public static inline function xor(a:Int32Native, b:Int32Native):Int32Native + return cast((a : Int) ^ (b : Int)); + #end + + #if (php || python || lua) + public static inline function shl(a:Int32Native, b:Int):Int32Native + return clamp((a : Int) << b); + #else + public static inline function shl(a:Int32Native, b:Int):Int32Native + return cast((a : Int) << b); + #end + + #if (lua || python || php) + public static #if (python || php) inline #end function shr(a:Int32Native, b:Int):Int32Native + return clamp((a : Int) >> b); + #else + public static inline function shr(a:Int32Native, b:Int):Int32Native + return cast((a : Int) >> b); + #end + + public static inline function ushr(a:Int32Native, b:Int):Int32Native + return cast((a : Int) >>> b); + + public static function ucompare(a:Int32Native, b:Int32Native):Int { + if ((a : Int) < 0) + return (b : Int) < 0 ? (~(b : Int) - ~(a : Int)) : 1; + return (b : Int) < 0 ? -1 : ((a : Int) - (b : Int)); + } + + public inline function toFloat():Float + return this; + + #if php + static var extraBits:Int = php.Const.PHP_INT_SIZE * 8 - 32; + #end + + #if !lua + inline + #end + public static function clamp(x:Int):Int32Native { + #if js + return cast(x | 0); + #elseif php + return cast((x << extraBits) >> extraBits); + #elseif python + return cast(((python.Syntax.code("{0} % {1}", (x + python.Syntax.opPow(2, 31)), python.Syntax.opPow(2, 32)) : Int) - python.Syntax.opPow(2, 31))); + #elseif lua + return cast lua.Boot.clampInt32(x); + #else + return cast x; + #end + } +} diff --git a/std/hl/_std/haxe/numeric/Int32Native.hx b/std/hl/_std/haxe/numeric/Int32Native.hx new file mode 100644 index 00000000000..a3fe93955e5 --- /dev/null +++ b/std/hl/_std/haxe/numeric/Int32Native.hx @@ -0,0 +1,25 @@ +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package haxe.numeric; + +typedef Int32Native = Int32Direct; diff --git a/std/hl/_std/haxe/numeric/Int64Native.hx b/std/hl/_std/haxe/numeric/Int64Native.hx index a42374695c3..7d3fb69a7a2 100644 --- a/std/hl/_std/haxe/numeric/Int64Native.hx +++ b/std/hl/_std/haxe/numeric/Int64Native.hx @@ -25,7 +25,6 @@ package haxe.numeric; #if (hl_ver >= version("1.12.0") && !hl_legacy32) typedef Int64Native = Int64NativeImpl; -@:coreApi(check = Off) private abstract Int64NativeImpl(hl.I64) from hl.I64 to hl.I64 { static var MASK:hl.I64 = { var v:hl.I64 = 0xFFFF; diff --git a/std/jvm/_std/haxe/numeric/Int32Native.hx b/std/jvm/_std/haxe/numeric/Int32Native.hx new file mode 100644 index 00000000000..a3fe93955e5 --- /dev/null +++ b/std/jvm/_std/haxe/numeric/Int32Native.hx @@ -0,0 +1,25 @@ +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package haxe.numeric; + +typedef Int32Native = Int32Direct; diff --git a/std/jvm/_std/haxe/numeric/Int64Native.hx b/std/jvm/_std/haxe/numeric/Int64Native.hx index 60dfad843f7..71090f8f028 100644 --- a/std/jvm/_std/haxe/numeric/Int64Native.hx +++ b/std/jvm/_std/haxe/numeric/Int64Native.hx @@ -24,7 +24,6 @@ package haxe.numeric; typedef Int64Native = Int64NativeImpl; -@:coreApi(check = Off) private abstract Int64NativeImpl(jvm.Int64) from jvm.Int64 to jvm.Int64 { public var high(get, set):haxe.Int32; diff --git a/tests/unit/src/unit/TestInt32.hx b/tests/unit/src/unit/TestInt32.hx new file mode 100644 index 00000000000..78d0911bef6 --- /dev/null +++ b/tests/unit/src/unit/TestInt32.hx @@ -0,0 +1,264 @@ +package unit; + +import haxe.Int32; + +class TestInt32 extends Test { + // --- Constants --- + static var MAX:Int32 = 0x7fffffff; + static var MIN:Int32 = 0x80000000; + static var ZERO:Int32 = 0; + static var ONE:Int32 = 1; + static var NEG_ONE:Int32 = -1; + + // --- Overflow behavior --- + function testOverflowAdd() { + eq((MAX + ONE : Int32), MIN); + eq((MIN + NEG_ONE : Int32), MAX); + eq((MAX + MIN : Int32), NEG_ONE); + } + + function testOverflowSub() { + eq((MIN - ONE : Int32), MAX); + eq((MAX - MIN : Int32), NEG_ONE); + eq((MIN - 1 : Int32), MAX); + } + + function testOverflowMul() { + eq((MAX * MAX : Int32), ONE); + eq((MAX * 2 : Int32), cast(-2, Int32)); + var minVal:Int32 = MIN; + eq((MAX * minVal : Int32), MIN); + } + + function testOverflowNeg() { + // Two's complement: -MIN overflows back to MIN + eq((-MIN : Int32), MIN); + // Normal negation + eq((-ONE : Int32), NEG_ONE); + eq((-NEG_ONE : Int32), ONE); + eq((-ZERO : Int32), ZERO); + } + + // --- Increment/Decrement --- + function testPreIncrement() { + var a:Int32 = MAX; + eq(++a, MIN); + eq(a, MIN); + } + + function testPostIncrement() { + var a:Int32 = MAX; + eq(a++, MAX); + eq(a, MIN); + } + + function testPreDecrement() { + var a:Int32 = MIN; + eq(--a, MAX); + eq(a, MAX); + } + + function testPostDecrement() { + var a:Int32 = MIN; + eq(a--, MIN); + eq(a, MAX); + } + + // --- Bitwise operations --- + function testComplement() { + eq((~ZERO : Int32), NEG_ONE); + eq((~NEG_ONE : Int32), ZERO); + eq((~MAX : Int32), MIN); + eq((~MIN : Int32), MAX); + } + + function testBitwiseAnd() { + eq((MAX & MIN : Int32), ZERO); + eq((NEG_ONE & MAX : Int32), MAX); + eq((NEG_ONE & MIN : Int32), MIN); + } + + function testBitwiseOr() { + var expected:Int32 = NEG_ONE; + eq((MAX | MIN : Int32), expected); + eq((ZERO | MAX : Int32), MAX); + } + + function testBitwiseXor() { + var expected:Int32 = NEG_ONE; + eq((MAX ^ MIN : Int32), expected); + eq((MAX ^ MAX : Int32), ZERO); + eq((MIN ^ MIN : Int32), ZERO); + } + + // --- Shift operations --- + function testShiftLeft() { + var one:Int32 = 1; + eq((one << 31 : Int32), MIN); + eq((MIN << 1 : Int32), ZERO); + var v:Int32 = 0xFF; + eq((v << 8 : Int32), cast(0xFF00, Int32)); + } + + function testShiftRight() { + eq((MIN >> 1 : Int32), cast(0xc0000000, Int32)); + eq((NEG_ONE >> 1 : Int32), NEG_ONE); // sign extension + eq((MAX >> 1 : Int32), cast(0x3fffffff, Int32)); + } + + function testUnsignedShiftRight() { + eq((MIN >>> 1 : Int32), cast(0x40000000, Int32)); + eq((NEG_ONE >>> 1 : Int32), MAX); + } + + // --- Unsigned comparison --- + function testUcompare() { + // 0 < MAX (unsigned) + t(Int32.ucompare(ZERO, MAX) < 0); + // MAX < MIN (unsigned, since MIN = 0x80000000 is large unsigned) + t(Int32.ucompare(MAX, MIN) < 0); + // MIN < NEG_ONE (unsigned, 0x80000000 < 0xFFFFFFFF) + t(Int32.ucompare(MIN, NEG_ONE) < 0); + // Equal values + eq(Int32.ucompare(MAX, MAX), 0); + eq(Int32.ucompare(MIN, MIN), 0); + eq(Int32.ucompare(ZERO, ZERO), 0); + } + + // --- Comparison operators --- + function testComparison() { + t(MIN < MAX); + t(MAX > MIN); + t(MIN <= MIN); + t(MAX >= MAX); + t(ZERO == ZERO); + t(ONE != ZERO); + } + + // --- Mixed-type operations --- + function testMixedIntOps() { + // Int32 + Int + eq((MAX + 1 : Int32), MIN); + // Int32 - Int + eq((MIN - 1 : Int32), MAX); + // Int - Int32 + eq((0 - ONE : Int32), NEG_ONE); + } + + function testMixedFloatOps() { + // Int32 + Float returns Float + var result:Float = MAX + 0.5; + feq(result, 2147483647.5); + // Int32 * Float returns Float + var result2:Float = ONE * 2.5; + feq(result2, 2.5); + } + + // --- Conversion --- + function testToFloat() { + var f:Float = MAX; + feq(f, 2147483647.0); + var f2:Float = MIN; + feq(f2, -2147483648.0); + } + + function testFromInt() { + var a:Int32 = 42; + eq((a : Int), 42); + var b:Int32 = -42; + eq((b : Int), -42); + } + + // --- Division (returns Float) --- + function testDivision() { + var ten:Int32 = 10; + var three:Int32 = 3; + feq((ten : Float) / (three : Float), 10.0 / 3.0); + } + + // --- Modulus --- + function testModulus() { + var ten:Int32 = 10; + var three:Int32 = 3; + eq((ten % three : Int32), cast(1, Int32)); + var negTen:Int32 = -10; + eq((negTen % three : Int32), cast(-1, Int32)); + } + + // --- Specific regression tests --- + function testTwosComplementOverflow_Issue7491() { + // https://github.com/HaxeFoundation/haxe/pull/7491 + var min:Int32 = MIN; + eq(-min, min); // two's complement overflow + eq(-2147483643, cast(5 + -min, Int)); // order of ops and negate + eq(2147483643, cast(-(5 + min), Int)); // static analyzer issue + } + + // https://github.com/HaxeFoundation/haxe/issues/10780 + // C++ inconsistent overflow behavior for Int32 with multiplication + function testMulOverflow_Issue10780() { + var a:Int32 = 257; + var b:Int32 = 0x01010101; + // 257 * 0x01010101 = 0x102020201 overflows 32-bit to 0x02020201 = 33686017 + eq((a * b : Int), 0x02020201); + } + + // https://github.com/HaxeFoundation/haxe/issues/10995 + // Python: wrong result when XOR with dynamic shift amount + function testXorWithDynamicShift_Issue10995() { + var changeBit = 31; + var result:Int32 = MIN ^ (1 << changeBit); + eq((result : Int), 0); + } + + // https://github.com/HaxeFoundation/haxe/issues/5938 + // Python: |= not clamping result to 32 bits + function testOrEqualsNotClamping_Issue5938() { + var i32:Int32 = 15459750; + var arr = [178, 0, 0]; + var next = 0; + i32 |= (arr[next] << 24); + eq((i32 : Int), -1293163098); + } + + // C++ handles array indexing with Int32 differently due to native type handling + #if !cpp + function testArrayIndexWithInt32() { + var a = [1]; + var next = 0; + + var i32:Int32 = MAX - 1; + i32 |= ((a[next] << 32) | 1); + eq(i32, MAX); + + var i32:Int32 = ((a[next] << 33) | 3); + i32 >>= 1; + eq((i32 : Int), 1); + + var i32:Int32 = 2; + i32 ^= (((a[next] << 32) | 1) : Int32); + eq((i32 : Int), 3); + + var i32:Int32 = 2; + var c = ~(((a[next] << 32) | 1) : Int32); + eq(c, cast(0xfffffffe, Int32)); + } + #end + + // --- Arithmetic identity tests --- + function testArithmeticIdentities() { + var a:Int32 = 12345; + eq((a + ZERO : Int32), a); + eq((a - ZERO : Int32), a); + eq((a * ONE : Int32), a); + eq(a, a); + } + + function testBitwiseIdentities() { + var a:Int32 = 0xDEADBEEF; + eq((a & NEG_ONE : Int32), a); + eq((a | ZERO : Int32), a); + eq((a ^ ZERO : Int32), a); + eq((a ^ a : Int32), ZERO); + } +} diff --git a/tests/unit/src/unit/TestMain.hx b/tests/unit/src/unit/TestMain.hx index 8cc773e8283..e9ed4c285d1 100644 --- a/tests/unit/src/unit/TestMain.hx +++ b/tests/unit/src/unit/TestMain.hx @@ -45,6 +45,7 @@ function main() { new TestMisc(), new TestJson(), new TestResource(), + new TestInt32(), new TestInt64(), new TestReflect(), new TestSerialize(),