diff --git a/ci/graal/common.json b/ci/graal/common.json index 0abfc23711..f4266e2539 100644 --- a/ci/graal/common.json +++ b/ci/graal/common.json @@ -4,7 +4,7 @@ "Jsonnet files should not include this file directly but use ci/common.jsonnet instead." ], - "mx_version": "7.68.13", + "mx_version": "7.69.0", "COMMENT.jdks": "When adding or removing JDKs keep in sync with JDKs in ci/common.jsonnet", "jdks": { diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_complex.py b/graalpython/com.oracle.graal.python.test/src/tests/test_complex.py index e4e19b88d7..9eb43d378b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_complex.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_complex.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # The Universal Permissive License (UPL), Version 1.0 @@ -187,7 +187,7 @@ def __index__(self): c = complex(CP1(5+5j), 7+7j) assert c == complex(-2, 12) assert type(c) == complex - + c = complex(CP1(5+5j), CP1(7+7j)) assert c == complex(-2, 12) assert type(c) == complex @@ -203,7 +203,7 @@ def __index__(self): c = CP1(CP1(5+5j), 7+7j) assert c == complex(-2, 12) assert type(c) == CP1 - + c = CP1(CP1(5+5j), CP1(7+7j)) assert c == complex(-2, 12) assert type(c) == CP1 @@ -215,7 +215,7 @@ def __index__(self): c = CP1(CP2(5+5j), 7+7j) assert c == complex(-7, 49) assert type(c) == CP1 - + c = CP1(CP2(5+5j), CP2(7+7j)) assert c == complex(-7, 49) assert type(c) == CP1 @@ -227,7 +227,7 @@ def __index__(self): c = complex(CP2(5+5j), 7+7j) assert c == complex(-7, 49) assert type(c) == complex - + c = complex(CP2(5+5j), CP2(7+7j)) assert c == complex(-7, 49) assert type(c) == complex @@ -243,7 +243,7 @@ def __index__(self): c = CP2(CP2(5+5j), 7+7j) assert c == complex(-7, 49) assert type(c) == CP2 - + c = CP2(CP2(5+5j), CP2(7+7j)) assert c == complex(-7, 49) assert type(c) == CP2 @@ -252,6 +252,17 @@ def __index__(self): if sys.version_info >= (3, 8, 0): assert complex(CP4()) == complex(123) + +def test_complex_subclass_without_dunder_complex_uses_fallback(): + class SubComplex(complex): + pass + + value = SubComplex(2 + 3j) + result = complex(value) + + assert result == 2 + 3j + assert type(result) is complex + class ComplexTest(unittest.TestCase): def assertAlmostEqual(self, a, b): @@ -442,7 +453,7 @@ def __complex__(self): return None self.assertEqual(complex(complex0(1j)), 42j) - # TODO we are not able to throw warning now. + # TODO we are not able to throw warning now. # with self.assertWarns(DeprecationWarning): self.assertEqual(complex(complex1(1j)), 2j) self.assertRaises(TypeError, complex, complex2(1j)) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_float.py b/graalpython/com.oracle.graal.python.test/src/tests/test_float.py index 1937d21be9..d6f9f6fd92 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_float.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_float.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # The Universal Permissive License (UPL), Version 1.0 @@ -148,6 +148,23 @@ def __round__(x, n): a = object() assert round(C(), a) == a + def test_round_missing_special_method(self): + class MissingRound: + def __getattr__(self, name): + if name == "__round__": + return lambda *args: 42 + raise AttributeError(name) + + with self.assertRaisesRegex(TypeError, "__round__"): + round(MissingRound()) + + def test_round_returns_notimplemented(self): + class NotImplementedRound: + def __round__(self, *args): + return NotImplemented + + assert round(NotImplementedRound()) is NotImplemented + def test_create(self): class Obj: def __float__(self): diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_int.py b/graalpython/com.oracle.graal.python.test/src/tests/test_int.py index cc3ead0f84..a6934f0e9c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_int.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_int.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # The Universal Permissive License (UPL), Version 1.0 @@ -703,6 +703,13 @@ def __bytes__(self): self.assertEqual(int.from_bytes(mybyteslike(), 'big'), 2580) + def test_from_object_without_bytes_uses_iterable_fallback(self): + class IterableOnly: + def __iter__(self): + return iter([1, 2, 3]) + + self.assertEqual(int.from_bytes(IterableOnly(), 'big'), 0x010203) + def test_from_wrong_byteslike_object(self): class mybyteslike1(): def __bytes__(self): diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_json.py b/graalpython/com.oracle.graal.python.test/src/tests/test_json.py index 6c6d5b1337..b94d05e36a 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_json.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_json.py @@ -59,6 +59,26 @@ class JsonTest(unittest.TestCase): + def test_callable_object_hook(self): + called = [] + class Hook: + def __call__(self, obj): + called.append(True) + return obj + + hook_instance = Hook() + result = json.loads('{"a": 1, "b": 2}', object_hook=hook_instance) + assert len(called) == 1 + assert result == {"a": 1, "b": 2} + + def test_invalid_object_hook(self): + with self.assertRaises(TypeError): + json.loads('{"a": 1}', object_hook="not_a_function") + + def test_invalid_object_pairs_hook(self): + with self.assertRaises(TypeError): + json.loads('{"a": 1}', object_pairs_hook=12345) + def test_dump(self): cwd = os.getcwd() new_file_path = os.path.join(cwd, 'myFile.json') diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_math.py b/graalpython/com.oracle.graal.python.test/src/tests/test_math.py index 3026992f16..c07a896a4b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_math.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_math.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2023, Oracle and/or its affiliates. +# Copyright (c) 2018, 2026, Oracle and/or its affiliates. # Copyright (C) 1996-2017 Python Software Foundation # # Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 @@ -214,7 +214,7 @@ def testAcos(self): class MyFloat2: def __float__(self): return 1.6 - self.assertRaises(ValueError, math.acos, MyFloat2()) + self.assertRaises(ValueError, math.acos, MyFloat2()) class MyFloat3: def __float__(self): @@ -265,7 +265,7 @@ def testSqrt(self): self.assertRaises(ValueError, math.sqrt, -1) self.assertRaises(ValueError, math.sqrt, NINF) self.assertTrue(math.isnan(math.sqrt(NAN))) - + math.sqrt(MyFloat()) math.sqrt(BIG_INT) self.assertRaises(TypeError, math.asin, 'ahoj') @@ -307,7 +307,7 @@ def testLog1p(self): self.assertAlmostEqual(math.log1p(n), math.log1p(float(n))) self.assertRaises(ValueError, math.log1p, -1) self.assertEqual(math.log1p(INF), INF) - + # test of specializations self.ftest('log1p(MyFloat())', math.log1p(MyFloat()), 0.4700036292457356) self.assertRaises(TypeError, math.log1p, 'ahoj') @@ -382,7 +382,7 @@ def testIsinf(self): self.assertFalse(math.isinf(float("nan"))) self.assertFalse(math.isinf(0.)) self.assertFalse(math.isinf(1.)) - + self.assertFalse(math.isinf(True)) self.assertFalse(math.isinf(LONG_INT)) self.assertFalse(math.isinf(BIG_INT)) @@ -484,7 +484,7 @@ def test_basic_copysign(self): self.assertEqual(math.copysign(999999999999999999999.1, 1), 999999999999999999999.1) self.assertRaises(TypeError, math.copysign, 'hello', 1) self.assertRaises(TypeError, math.copysign, 1, 'hello') - + self.assertEqual(math.copysign(MyFloat(), 1), 0.6) self.assertEqual(math.copysign(MyFloat(), -1), -0.6) self.assertEqual(math.copysign(1.2, MyFloat()), 1.2) @@ -679,12 +679,12 @@ def testPow(self): self.assertEqual(math.pow(999999999999999999999999999, 0), 1) self.assertEqual(math.pow(0.0, 999999999999999999999999999), 0) self.assertEqual(math.pow(999999999999999999999999999, 0.0), 1) - + class MyNumber(): def __float__(self): return -2.; self.ftest('MyFloat()**-3.', math.pow(MyNumber(), -3.0), -0.125) - + def testAtan2(self): self.assertRaises(TypeError, math.atan2) self.ftest('atan2(-1, 0)', math.atan2(-1, 0), -math.pi/2) @@ -768,7 +768,7 @@ def testCos(self): self.assertRaises(ValueError, math.cos, INF) self.assertRaises(ValueError, math.cos, NINF) self.assertTrue(math.isnan(math.cos(NAN))) - + #test of specializations self.ftest('cos(BIG_INT)', math.cos(BIG_INT), 0.4145587418469303) self.ftest('cos(MyFloat())', math.cos(MyFloat()), 0.8253356149096783) @@ -786,7 +786,7 @@ def testCosh(self): self.ftest('cosh(MyFloat())', math.cosh(MyFloat()), 1.1854652182422676) self.assertRaises(TypeError, math.cosh, 'ahoj') self.assertRaises(OverflowError, math.cosh, BIG_INT) - + def testSin(self): self.assertRaises(TypeError, math.sin) self.ftest('sin(0)', math.sin(0), 0) @@ -813,7 +813,7 @@ def testSinh(self): self.assertEqual(math.sinh(INF), INF) self.assertEqual(math.sinh(NINF), NINF) self.assertTrue(math.isnan(math.sinh(NAN))) - + # test of specializations self.ftest('sinh(MyFloat())', math.sinh(MyFloat()), 0.6366535821482412) self.assertRaises(TypeError, math.sinh, 'ahoj') @@ -1128,7 +1128,7 @@ def testExp(self): self.assertEqual(math.exp(NINF), 0.) self.assertTrue(math.isnan(math.exp(NAN))) self.assertRaises(OverflowError, math.exp, 1000000) - + # test of specializations self.ftest('exp(MyFloat())', math.exp(MyFloat()), 1.8221188003905089) self.assertRaises(TypeError, math.exp, 'ahoj') @@ -1239,7 +1239,7 @@ class II(int): self.assertRaises(TypeError, math.ldexp, 'Hello', 1000000) self.assertRaises(TypeError, math.ldexp, 1, 'Hello') self.assertEqual(math.ldexp(7589167167882033, -48), 26.962138008038156) - + self.assertRaises(TypeError, math.ldexp, 1, MyIndexable(2)) self.assertRaises(TypeError, math.ldexp, 1, MyInt(2)) self.assertRaises(TypeError, math.ldexp, 1, MyFloat()) @@ -1272,6 +1272,20 @@ class TestNoTrunc(object): self.assertRaises(TypeError, math.trunc, 1, 2) self.assertRaises(TypeError, math.trunc, TestNoTrunc()) + class MissingTrunc: + def __getattr__(self, name): + if name == "__trunc__": + return lambda: 99 + raise AttributeError(name) + + self.assertRaises(TypeError, math.trunc, MissingTrunc()) + + class NotImplementedTrunc: + def __trunc__(self): + return NotImplemented + + self.assertIs(math.trunc(NotImplementedTrunc()), NotImplemented) + def testDegrees(self): self.assertRaises(TypeError, math.degrees) self.ftest('degrees(pi)', math.degrees(math.pi), 180.0) @@ -1325,7 +1339,7 @@ def executeFnTest(self, values, fn, fnName): result = fn(value[0]) expected = value[1] if math.isnan(expected): - self.assertTrue(math.isnan(result), "Test2 fail: {}({}) = {}, but was {}".format(fnName, value[0], expected, result)) + self.assertTrue(math.isnan(result), "Test2 fail: {}({}) = {}, but was {}".format(fnName, value[0], expected, result)) else : if result != expected: if (sys.version_info.major >= 3 and sys.version_info.minor >= 5): @@ -1334,7 +1348,7 @@ def executeFnTest(self, values, fn, fnName): def test_erf(self): erfValues = [(0.0, 0.0), (-0.0, -0.0), (INF, 1.0), (NINF, -1.0), (NAN, NAN), # tiny values - (1e-308, 1.1283791670955125e-308), (5e-324, 4.9406564584124654e-324), + (1e-308, 1.1283791670955125e-308), (5e-324, 4.9406564584124654e-324), (1e-10, 1.1283791670955126e-10), # small integers (1, 0.842700792949715), (2, 0.99532226501895271), (3, 0.99997790950300136), @@ -1356,7 +1370,7 @@ def test_erfc(self): (1e-308, 1.0), (5e-324, 1.0), (1e-10, 0.99999999988716204), # small integers (1, 0.157299207050285), (2, 0.004677734981047268), (3, 2.2090496998585482e-05), - (4, 1.541725790028002e-08), (5, 1.5374597944280341e-12), + (4, 1.541725790028002e-08), (5, 1.5374597944280341e-12), # this number needs to be rounded (6, 2.1519736712498925e-17), (-1, 1.842700792949715), (-2, 1.9953222650189528), (-3, 1.9999779095030015), @@ -1372,7 +1386,7 @@ def test_erfc(self): (-26.8, 2.0), (-27.0, 2.0), (-27.2, 2.0), (-27.4, 2.0), (-27.6, 2.0) ] self.executeFnTest(values, math.erfc, 'math.erfc') - + def test_gamma(self): self.assertRaises(ValueError, math.gamma, 0.) self.assertRaises(ValueError, math.gamma, -0.0) @@ -1400,34 +1414,34 @@ def test_gamma(self): (3.5, 3.323350970447842), (-0.5, -3.5449077018110322), (-1.5, 2.3632718012073544), (-2.5, -0.94530872048294170), (-3.5, 0.27008820585226917), # values near 0 - (0.1, 9.5135076986687306), - (0.01, 99.432585119150602), + (0.1, 9.5135076986687306), + (0.01, 99.432585119150602), (1e-8, 99999999.422784343), - #(1e-16, 10000000000000000), + #(1e-16, 10000000000000000), (1e-30, 9.9999999999999988e+29), (1e-160, 1.0000000000000000e+160), - (1e-308, 1.0000000000000000e+308), + (1e-308, 1.0000000000000000e+308), (5.6e-309, 1.7857142857142848e+308), - (-0.1, -10.686287021193193), - (-0.01, -100.58719796441078), + (-0.1, -10.686287021193193), + (-0.01, -100.58719796441078), (-1e-8, -100000000.57721567), - (-1e-16, -10000000000000000), + (-1e-16, -10000000000000000), (-1e-30, -9.9999999999999988e+29), (-1e-160, -1.0000000000000000e+160), - (-1e-308, -1.0000000000000000e+308), + (-1e-308, -1.0000000000000000e+308), (-5.6e-309, -1.7857142857142848e+308), # values near negative integers - (-0.99999999999999989, -9007199254740992.0), + (-0.99999999999999989, -9007199254740992.0), (-1.0000000000000002, 4503599627370495.5), - (-1.9999999999999998, 2251799813685248.5), + (-1.9999999999999998, 2251799813685248.5), (-2.0000000000000004, -1125899906842623.5), - (-100.00000000000001, -7.5400833348831090e-145), + (-100.00000000000001, -7.5400833348831090e-145), (-99.999999999999986, 7.5400833348840962e-145), # large inputs - (170, 4.2690680090047051e+304), - (171, 7.2574156153079990e+306), + (170, 4.2690680090047051e+304), + (171, 7.2574156153079990e+306), (171.624, 1.7942117599248104e+308), # inputs for which gamma(x) is tiny - (-100.5, -3.3536908198076787e-159), - (-160.5, -5.2555464470078293e-286), + (-100.5, -3.3536908198076787e-159), + (-160.5, -5.2555464470078293e-286), (-170.5, -3.3127395215386074e-308), (-171.5, 1.9316265431711902e-310), (-176.5, -1.1956388629358166e-321), (-177.5, 4.9406564584124654e-324), (-178.5, -0.0), (-179.5, 0.0), (-201.0001, 0.0), (-202.9999, -0.0), (-1000.5, -0.0), @@ -1452,11 +1466,11 @@ def test_lgamma(self): values = [(INF, INF), (-INF, INF), (NAN, NAN), # small positive integers give factorials - (1, 0.0), (2, 0.0), - (3, 0.69314718055994529), - (4, 1.791759469228055), - (5, 3.1780538303479458), - (6, 4.7874917427820458), + (1, 0.0), (2, 0.0), + (3, 0.69314718055994529), + (4, 1.791759469228055), + (5, 3.1780538303479458), + (6, 4.7874917427820458), # half integers (0.5, 0.57236494292470008), (1.5, -0.12078223763524522), diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py b/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py index bd13515e34..5629fd5401 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # The Universal Permissive License (UPL), Version 1.0 @@ -239,6 +239,15 @@ def __fspath__(self): with self.assertRaisesRegex(TypeError, r"expected Wrap.__fspath__\(\) to return str or bytes, not " + type(x).__name__): os.fspath(Wrap(x)) + class MissingFspath: + def __getattr__(self, name): + if name == "__fspath__": + return lambda: "abc" + raise AttributeError(name) + + with self.assertRaisesRegex(TypeError, "expected str, bytes or os.PathLike object"): + os.fspath(MissingFspath()) + def test_path_convertor(self): class C: def __fspath__(self): diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_contextlib.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_contextlib.txt index fbaffcbaf7..6a56ca2d74 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_contextlib.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_contextlib.txt @@ -18,8 +18,7 @@ test.test_contextlib.ContextManagerTestCase.test_contextmanager_trap_yield_after test.test_contextlib.ContextManagerTestCase.test_contextmanager_wrap_runtimeerror @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib.ContextManagerTestCase.test_instance_docstring_given_cm_docstring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib.ContextManagerTestCase.test_keywords @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# GR-73564 - regression in Truffle -!test.test_contextlib.ContextManagerTestCase.test_nokeepref @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_contextlib.ContextManagerTestCase.test_nokeepref @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib.ContextManagerTestCase.test_param_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib.ContextManagerTestCase.test_recursive @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib.FileContextTestCase.testWithOpen @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java index b6810489fd..dded26923c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java @@ -1934,11 +1934,11 @@ static Object round(VirtualFrame frame, Object x, @SuppressWarnings("unused") PN @Bind Node inliningTarget, @Cached("create(T___ROUND__)") LookupAndCallUnaryNode callRound, @Shared @Cached PRaiseNode raiseNode) { - Object result = callRound.executeObject(frame, x); - if (result == PNone.NO_VALUE) { + try { + return callRound.executeObject(frame, x); + } catch (SpecialMethodNotFound e) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.TYPE_DOESNT_DEFINE_METHOD, x, T___ROUND__); } - return result; } @Specialization(guards = "!isPNone(n)") diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathModuleBuiltins.java index a82c6e2b10..fa75890da9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathModuleBuiltins.java @@ -72,6 +72,7 @@ import com.oracle.graal.python.nodes.builtins.TupleNodes; import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; +import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; @@ -2185,11 +2186,11 @@ static Object trunc(VirtualFrame frame, Object obj, @Bind Node inliningTarget, @Cached("create(T___TRUNC__)") LookupAndCallUnaryNode callTrunc, @Cached PRaiseNode raiseNode) { - Object result = callTrunc.executeObject(frame, obj); - if (result == PNone.NO_VALUE) { + try { + return callTrunc.executeObject(frame, obj); + } catch (SpecialMethodNotFound e) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.TYPE_DOESNT_DEFINE_METHOD, obj, "__trunc__"); } - return result; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java index 4d2dee0b49..b24290478b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. * Copyright (c) 2014, Regents of the University of California * * All rights reserved. @@ -100,6 +100,7 @@ import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; +import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -3487,8 +3488,10 @@ PosixFileHandle doGeneric(VirtualFrame frame, Object value, @Bind PythonContext context, @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Exclusive @Cached PRaiseNode raiseNode) { - Object pathObject = callFSPath.executeObject(frame, value); - if (pathObject == PNone.NO_VALUE) { + Object pathObject; + try { + pathObject = callFSPath.executeObject(frame, value); + } catch (SpecialMethodNotFound e) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.S_S_SHOULD_BE_S_NOT_P, functionNameWithColon, argumentName, getAllowedTypes(), value); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java index fab70a3b20..c2b27d5f2e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java @@ -222,7 +222,7 @@ import com.oracle.graal.python.nodes.StringLiterals; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; -import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode.NoAttributeHandler; +import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; import com.oracle.graal.python.nodes.frame.ReadFrameNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; @@ -263,7 +263,6 @@ import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.strings.TruffleString; @CoreFunctions(defineModule = "sys", isEager = true) @@ -995,7 +994,13 @@ static Object doGeneric(VirtualFrame frame, Object object, @SuppressWarnings("un @Shared @Cached PyNumberAsSizeNode asSizeNode, @Cached("createWithError()") LookupAndCallUnaryNode callSizeofNode, @Shared @Cached PRaiseNode raiseNode) { - return checkResult(frame, inliningTarget, asSizeNode, callSizeofNode.executeObject(frame, object), raiseNode); + Object result; + try { + result = callSizeofNode.executeObject(frame, object); + } catch (SpecialMethodNotFound e) { + throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.TYPE_DOESNT_DEFINE_METHOD, object, T___SIZEOF__); + } + return checkResult(frame, inliningTarget, asSizeNode, result, raiseNode); } @Specialization(guards = "!isNoValue(dflt)") @@ -1004,8 +1009,10 @@ static Object doGeneric(VirtualFrame frame, Object object, Object dflt, @Shared @Cached PyNumberAsSizeNode asSizeNode, @Cached("createWithoutError()") LookupAndCallUnaryNode callSizeofNode, @Shared @Cached PRaiseNode raiseNode) { - Object result = callSizeofNode.executeObject(frame, object); - if (result == PNone.NO_VALUE) { + Object result; + try { + result = callSizeofNode.executeObject(frame, object); + } catch (SpecialMethodNotFound e) { return dflt; } return checkResult(frame, inliningTarget, asSizeNode, result, raiseNode); @@ -1021,15 +1028,7 @@ private static Object checkResult(VirtualFrame frame, Node inliningTarget, PyNum @NeverDefault protected LookupAndCallUnaryNode createWithError() { - return LookupAndCallUnaryNode.create(T___SIZEOF__, () -> new NoAttributeHandler() { - private final BranchProfile errorProfile = BranchProfile.create(); - - @Override - public Object execute(Object receiver) { - errorProfile.enter(); - throw PRaiseNode.raiseStatic(this, TypeError, ErrorMessages.TYPE_DOESNT_DEFINE_METHOD, receiver, T___SIZEOF__); - } - }); + return LookupAndCallUnaryNode.create(T___SIZEOF__); } @NeverDefault diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.java index 5bcd916cbb..cae62531f6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.java @@ -6,6 +6,7 @@ package com.oracle.graal.python.builtins.modules.json; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.RecursionError; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; import static com.oracle.graal.python.builtins.objects.str.StringUtils.byteIndexToCodepointIndex; import static com.oracle.graal.python.builtins.objects.str.StringUtils.codepointIndexToByteIndex; import static com.oracle.graal.python.nodes.StringLiterals.T_STRICT; @@ -36,6 +37,7 @@ import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; +import com.oracle.graal.python.lib.PyCallableCheckNode; import com.oracle.graal.python.lib.PyFloatCheckExactNode; import com.oracle.graal.python.lib.PyLongCheckExactNode; import com.oracle.graal.python.lib.PyLongFromUnicodeObject; @@ -59,6 +61,7 @@ import com.oracle.graal.python.runtime.sequence.storage.ObjectSequenceStorage; import com.oracle.graal.python.util.ArrayBuilder; import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.dsl.Bind; @@ -107,6 +110,7 @@ public abstract static class MakeScanner extends PythonBinaryBuiltinNode { public PJSONScanner doNew(VirtualFrame frame, Object cls, Object context, @Bind Node inliningTarget, @Cached PyObjectIsTrueNode castStrict, + @Cached PyCallableCheckNode checkCallable, @Cached TypeNodes.GetInstanceShape getInstanceShape, @Cached PyFloatCheckExactNode pyFloatCheckExactNode, @Cached PyLongCheckExactNode pyLongCheckExactNode) { @@ -119,6 +123,19 @@ public PJSONScanner doNew(VirtualFrame frame, Object cls, Object context, Object parseConstant = getParseConstant.execute(frame, context); Object parseFloat = pyFloatCheckExactNode.execute(inliningTarget, parseFloatProp) ? PNone.NONE : parseFloatProp; Object parseInt = pyLongCheckExactNode.execute(inliningTarget, parseIntProp) ? PNone.NONE : parseIntProp; + /* + * Validate that object_hook and object_pairs_hook, if provided, are callable. This + * mirrors CPython's behavior and prevents ClassCastException when a non‑callable (e.g., + * a string) is supplied. + */ + if (objectHook != PNone.NONE && !checkCallable.execute(inliningTarget, objectHook)) { + CompilerDirectives.transferToInterpreter(); + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_MUST_BE_CALLABLE, "object_hook"); + } + if (objectPairsHook != PNone.NONE && !checkCallable.execute(inliningTarget, objectPairsHook)) { + CompilerDirectives.transferToInterpreter(); + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_MUST_BE_CALLABLE, "object_pairs_hook"); + } return PFactory.createJSONScanner(cls, getInstanceShape.execute(cls), strict, objectHook, objectPairsHook, parseFloat, parseInt, parseConstant); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java index dfb6d10810..0b064abb96 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -103,6 +103,7 @@ import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; +import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode; @@ -617,7 +618,11 @@ private Object callComplex(VirtualFrame frame, Object object) { CompilerDirectives.transferToInterpreterAndInvalidate(); callComplexNode = insert(LookupAndCallUnaryNode.create(T___COMPLEX__)); } - return callComplexNode.executeObject(frame, object); + try { + return callComplexNode.executeObject(frame, object); + } catch (SpecialMethodNotFound e) { + return null; + } } private WarningsModuleBuiltins.WarnNode getWarnNode() { @@ -649,7 +654,7 @@ private PComplex getComplexNumberFromObject(VirtualFrame frame, Object object, N object, "__complex__", "complex", result, "complex"); } return (PComplex) result; - } else if (result != PNone.NO_VALUE) { + } else if (result != null) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.COMPLEX_RETURNED_NON_COMPLEX, result); } if (object instanceof PComplex) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java index 387b08acba..4b07364ac0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -125,6 +125,7 @@ import com.oracle.graal.python.nodes.SpecialMethodNames; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; +import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; @@ -2631,14 +2632,14 @@ static Object fromObject(VirtualFrame frame, Object cl, Object object, TruffleSt throw raiseNode.raise(inliningTarget, PythonErrorType.ValueError, ErrorMessages.BYTEORDER_MUST_BE_LITTLE_OR_BIG); } byte[] bytes; - Object bytesObj = callBytes.executeObject(frame, object); - if (bytesObj != PNone.NO_VALUE) { + try { + Object bytesObj = callBytes.executeObject(frame, object); hasBytesProfile.enter(inliningTarget); if (!(bytesObj instanceof PBytes)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.RETURNED_NONBYTES, T___BYTES__); } bytes = bufferLib.getCopiedByteArray(bytesObj); - } else { + } catch (SpecialMethodNotFound e) { bytes = bytesFromObject.execute(frame, object); } Object result = fromByteArray.execute(inliningTarget, bytes, littleEndian, signed); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectDir.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectDir.java index 93f75625cd..be5bd9f315 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectDir.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectDir.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -42,7 +42,6 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; -import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.list.ListBuiltins; import com.oracle.graal.python.builtins.objects.list.PList; import com.oracle.graal.python.nodes.ErrorMessages; @@ -50,6 +49,7 @@ import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.builtins.ListNodes; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; +import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; @@ -72,8 +72,10 @@ static PList dir(VirtualFrame frame, Node inliningTarget, Object object, @Cached(inline = false) ListNodes.ConstructListNode constructListNode, @Cached(value = "create(T___DIR__)", inline = false) LookupAndCallUnaryNode callDir, @Cached PRaiseNode raiseNode) { - Object result = callDir.executeObject(frame, object); - if (result == PNone.NO_VALUE) { + Object result; + try { + result = callDir.executeObject(frame, object); + } catch (SpecialMethodNotFound e) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.OBJ_DOES_NOT_PROVIDE_DIR); } PList list = constructListNode.execute(frame, result); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java index 1014a90522..ceab1fb2f2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java @@ -1578,4 +1578,6 @@ public abstract class ErrorMessages { public static final TruffleString NO_SUCH_GROUP = tsLiteral("no such group"); public static final TruffleString WEAKLY_REFERENCED_OBJECT_NO_LONGER_EXISTS = tsLiteral("weakly-referenced object no longer exists"); public static final TruffleString WEAKREF_PROXY_REFERENCED_A_NON_ITERATOR_S_OBJECT = tsLiteral("Weakref proxy referenced a non-iterator '%s' object"); + + public static final TruffleString S_MUST_BE_CALLABLE = tsLiteral("%s must be callable"); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java index ff1a55b2f1..4586ad381f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java @@ -315,7 +315,7 @@ boxingEliminationTypes = {int.class}, // tagTreeNodeLibrary = PTagTreeNodeExports.class, // storeBytecodeIndexInFrame = true, // - defaultUncachedThreshold = "5", // + defaultUncachedThreshold = "4", // enableUncachedInterpreter = true) @OperationProxy(PyNumberSubtractNode.class) @OperationProxy(PyNumberTrueDivideNode.class) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallBinaryNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallBinaryNode.java index 9116057b2b..06e3b6a38e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallBinaryNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallBinaryNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -57,6 +57,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.strings.TruffleString; /** @@ -126,9 +127,10 @@ Object callObjectGeneric(VirtualFrame frame, Object left, Object right, @Bind Node inliningTarget, @SuppressWarnings("unused") @Cached("left.getClass()") Class cachedLeftClass, @SuppressWarnings("unused") @Cached("right.getClass()") Class cachedRightClass, + @Exclusive @Cached InlinedBranchProfile notFoundProfile, @Exclusive @Cached GetClassNode getClassNode, @Exclusive @Cached("create(name)") LookupSpecialMethodNode getattr) { - return doCallObject(frame, inliningTarget, left, right, getClassNode, getattr); + return doCallObject(frame, inliningTarget, notFoundProfile, left, right, getClassNode, getattr); } @Specialization(replaces = "callObjectGeneric") @@ -136,15 +138,18 @@ Object callObjectGeneric(VirtualFrame frame, Object left, Object right, @SuppressWarnings("truffle-static-method") Object callObjectMegamorphic(VirtualFrame frame, Object left, Object right, @Bind Node inliningTarget, + @Exclusive @Cached InlinedBranchProfile notFoundProfile, @Exclusive @Cached GetClassNode getClassNode, @Exclusive @Cached("create(name)") LookupSpecialMethodNode getattr) { - return doCallObject(frame, inliningTarget, left, right, getClassNode, getattr); + return doCallObject(frame, inliningTarget, notFoundProfile, left, right, getClassNode, getattr); } - private Object doCallObject(VirtualFrame frame, Node inliningTarget, Object left, Object right, GetClassNode getClassNode, LookupSpecialMethodNode getattr) { + private Object doCallObject(VirtualFrame frame, Node inliningTarget, InlinedBranchProfile notFoundProfile, Object left, Object right, GetClassNode getClassNode, + LookupSpecialMethodNode getattr) { Object leftClass = getClassNode.execute(inliningTarget, left); Object leftCallable = getattr.execute(frame, leftClass, left); if (PGuards.isNoValue(leftCallable)) { + notFoundProfile.enter(inliningTarget); throw SpecialMethodNotFound.INSTANCE; } return ensureDispatch().executeObject(frame, leftCallable, left, right); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallUnaryNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallUnaryNode.java index b666b96a97..7b34b7e4a3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallUnaryNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallUnaryNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -49,8 +49,7 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.PythonOptions; -import com.oracle.graal.python.util.Supplier; -import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; @@ -62,21 +61,15 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.profiles.InlinedConditionProfile; import com.oracle.truffle.api.strings.TruffleString; @ImportStatic(PythonOptions.class) public abstract class LookupAndCallUnaryNode extends UnaryOpNode { - - public abstract static class NoAttributeHandler extends PNodeWithContext { - public abstract Object execute(Object receiver); - } - protected final TruffleString name; - protected final Supplier handlerFactory; - @Child private NoAttributeHandler handler; - public abstract Object executeObject(VirtualFrame frame, Object receiver); + public abstract Object executeObject(VirtualFrame frame, Object receiver) throws SpecialMethodNotFound; @Override public Object execute(VirtualFrame frame, Object receiver) { @@ -85,17 +78,11 @@ public Object execute(VirtualFrame frame, Object receiver) { @NeverDefault public static LookupAndCallUnaryNode create(TruffleString name) { - return LookupAndCallUnaryNodeGen.create(name, null); - } - - @NeverDefault - public static LookupAndCallUnaryNode create(TruffleString name, Supplier handlerFactory) { - return LookupAndCallUnaryNodeGen.create(name, handlerFactory); + return LookupAndCallUnaryNodeGen.create(name); } - LookupAndCallUnaryNode(TruffleString name, Supplier handlerFactory) { + LookupAndCallUnaryNode(TruffleString name) { this.name = name; - this.handlerFactory = handlerFactory; } public TruffleString getMethodName() { @@ -137,41 +124,38 @@ static Object callObjectBuiltin(VirtualFrame frame, Object receiver, Object callObjectGeneric(VirtualFrame frame, Object receiver, @Bind Node inliningTarget, @SuppressWarnings("unused") @Cached("receiver.getClass()") Class cachedClass, + @Shared @Cached InlinedBranchProfile notFoundProfile, @Shared @Cached GetClassNode getClassNode, @Shared @Cached("create(name)") LookupSpecialMethodNode getattr, @Shared @Cached CallUnaryMethodNode dispatchNode) { - return doCallObject(frame, inliningTarget, receiver, getClassNode, getattr, dispatchNode); + return doCallObject(frame, inliningTarget, notFoundProfile, receiver, getClassNode, getattr, dispatchNode); } @Specialization(replaces = "callObjectGeneric") @Megamorphic + @InliningCutoff @SuppressWarnings("truffle-static-method") Object callObjectMegamorphic(VirtualFrame frame, Object receiver, @Bind Node inliningTarget, + @Shared @Cached InlinedBranchProfile notFoundProfile, @Shared @Cached GetClassNode getClassNode, @Shared @Cached("create(name)") LookupSpecialMethodNode getattr, @Shared @Cached CallUnaryMethodNode dispatchNode) { - return doCallObject(frame, inliningTarget, receiver, getClassNode, getattr, dispatchNode); + return doCallObject(frame, inliningTarget, notFoundProfile, receiver, getClassNode, getattr, dispatchNode); } protected Class getObjectClass(Object object) { return object.getClass(); } - private Object doCallObject(VirtualFrame frame, Node inliningTarget, Object receiver, GetClassNode getClassNode, LookupSpecialMethodNode getattr, CallUnaryMethodNode dispatchNode) { + private Object doCallObject(VirtualFrame frame, Node inliningTarget, InlinedBranchProfile notFoundProfile, + Object receiver, GetClassNode getClassNode, LookupSpecialMethodNode getattr, CallUnaryMethodNode dispatchNode) { Object attr = getattr.execute(frame, getClassNode.execute(inliningTarget, receiver), receiver); if (attr == PNone.NO_VALUE) { - if (handlerFactory != null) { - if (handler == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - handler = insert(handlerFactory.get()); - } - return handler.execute(receiver); - } - return PNone.NO_VALUE; - } else { - return dispatchNode.executeObject(frame, attr, receiver); + notFoundProfile.enter(inliningTarget); + throw SpecialMethodNotFound.INSTANCE; } + return dispatchNode.executeObject(frame, attr, receiver); } @GenerateUncached diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CoerceToComplexNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CoerceToComplexNode.java index 848adfa7c2..7ca81a500c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CoerceToComplexNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CoerceToComplexNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -43,7 +43,6 @@ import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; import com.oracle.graal.python.PythonLanguage; -import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.complex.PComplex; import com.oracle.graal.python.lib.PyFloatAsDoubleNode; import com.oracle.graal.python.nodes.ErrorMessages; @@ -51,6 +50,7 @@ import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; +import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; import com.oracle.graal.python.nodes.truffle.PythonIntegerAndFloatTypes; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.dsl.Bind; @@ -94,8 +94,8 @@ static PComplex toComplex(VirtualFrame frame, Node inliningTarget, Object x, @Cached PyFloatAsDoubleNode asDoubleNode, @Bind PythonLanguage language, @Cached PRaiseNode raiseNode) { - Object result = callComplexFunc.executeObject(frame, x); - if (result != PNone.NO_VALUE) { + try { + Object result = callComplexFunc.executeObject(frame, x); if (result instanceof PComplex) { // TODO we need pass here deprecation warning // DeprecationWarning: __complex__ returned non-complex (type %p). @@ -103,10 +103,10 @@ static PComplex toComplex(VirtualFrame frame, Node inliningTarget, Object x, // deprecated, // and may be removed in a future version of Python. return (PComplex) result; - } else { - throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.SHOULD_RETURN, "__complex__", "complex object"); } + throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.SHOULD_RETURN, "__complex__", "complex object"); + } catch (SpecialMethodNotFound e) { + return PFactory.createComplex(language, asDoubleNode.execute(frame, inliningTarget, x), 0); } - return PFactory.createComplex(language, asDoubleNode.execute(frame, inliningTarget, x), 0); } } diff --git a/mx.graalpython/suite.py b/mx.graalpython/suite.py index 455e01b382..04a5def644 100644 --- a/mx.graalpython/suite.py +++ b/mx.graalpython/suite.py @@ -53,7 +53,7 @@ }, { "name": "tools", - "version": "79093aa5f8fc2d015cd486fd1776330381807c53", + "version": "dd827b1abba3aa655f320e9ac61769c6137f8924", "subdir": True, "urls": [ {"url": "https://github.com/oracle/graal", "kind": "git"}, @@ -61,7 +61,7 @@ }, { "name": "regex", - "version": "79093aa5f8fc2d015cd486fd1776330381807c53", + "version": "dd827b1abba3aa655f320e9ac61769c6137f8924", "subdir": True, "urls": [ {"url": "https://github.com/oracle/graal", "kind": "git"},