Skip to content

Commit b24b197

Browse files
committed
Create and use CastToJavaUnsignedLongNode
1 parent e42db99 commit b24b197

File tree

3 files changed

+271
-7
lines changed

3 files changed

+271
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
42+
package com.oracle.graal.python.nodes.util;
43+
44+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
45+
import com.oracle.graal.python.builtins.objects.ints.PInt;
46+
import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
47+
import com.oracle.graal.python.runtime.exception.PException;
48+
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
49+
import com.oracle.graal.python.test.PythonTests;
50+
import org.junit.Assert;
51+
import org.junit.Before;
52+
import org.junit.Test;
53+
54+
import java.math.BigInteger;
55+
56+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.OverflowError;
57+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
58+
59+
public class CastToJavaUnsignedLongNodeTests {
60+
61+
@Before
62+
public void setUp() {
63+
PythonTests.enterContext();
64+
}
65+
66+
private static CastToJavaUnsignedLongNode castNode = CastToJavaUnsignedLongNode.getUncached();
67+
private static PythonObjectFactory factory = PythonObjectFactory.getUncached();
68+
69+
@Test
70+
public void positiveInt() {
71+
Assert.assertEquals(0, castNode.execute(0));
72+
Assert.assertEquals(16, castNode.execute(16));
73+
Assert.assertEquals(Integer.MAX_VALUE, castNode.execute(Integer.MAX_VALUE));
74+
Assert.assertEquals(42, castNode.execute(Integer.valueOf(42)));
75+
}
76+
77+
@Test
78+
public void negativeInt() {
79+
expect(OverflowError, () -> castNode.execute(-1));
80+
expect(OverflowError, () -> castNode.execute(Integer.MIN_VALUE));
81+
}
82+
83+
@Test
84+
public void positiveLong() {
85+
Assert.assertEquals(0, castNode.execute(0L));
86+
Assert.assertEquals(0x80000000L, castNode.execute(0x80000000L));
87+
Assert.assertEquals(Long.MAX_VALUE, castNode.execute(Long.MAX_VALUE));
88+
Assert.assertEquals(1234567890123L, castNode.execute(Long.valueOf(1234567890123L)));
89+
}
90+
91+
@Test
92+
public void negativeLong() {
93+
expect(OverflowError, () -> castNode.execute(-1L));
94+
expect(OverflowError, () -> castNode.execute(Long.MIN_VALUE));
95+
}
96+
97+
@Test
98+
public void positiveBigInt() {
99+
Assert.assertEquals(0, castNode.execute(makePInt(0)));
100+
Assert.assertEquals(1234567890123L, castNode.execute(makePInt(1234567890123L)));
101+
Assert.assertEquals(Long.MAX_VALUE, castNode.execute(makePInt(Long.MAX_VALUE)));
102+
Assert.assertEquals(0x8000000000000000L, castNode.execute(makePInt("8000000000000000")));
103+
Assert.assertEquals(0xFFFFFFFFFFFFFFFFL, castNode.execute(makePInt("FFFFFFFFFFFFFFFF")));
104+
}
105+
106+
@Test
107+
public void negativeBigInt() {
108+
expect(OverflowError, () -> castNode.execute(makePInt(-1)));
109+
expect(OverflowError, () -> castNode.execute(makePInt("-10000000000000000")));
110+
}
111+
112+
@Test
113+
public void largeBigInt() {
114+
expect(OverflowError, () -> castNode.execute(makePInt("10000000000000000")));
115+
expect(OverflowError, () -> castNode.execute(makePInt("10000000000000001")));
116+
}
117+
118+
@Test
119+
public void nonInteger() {
120+
expect(TypeError, () -> castNode.execute("123"));
121+
expect(TypeError, () -> castNode.execute(2.7));
122+
}
123+
124+
private static PInt makePInt(long l) {
125+
return factory.createInt(BigInteger.valueOf(l));
126+
}
127+
128+
private static PInt makePInt(String hexString) {
129+
return factory.createInt(new BigInteger(hexString, 16));
130+
}
131+
132+
private static void expect(PythonBuiltinClassType errorType, Runnable test) {
133+
try {
134+
test.run();
135+
Assert.fail("Expected " + errorType.getName());
136+
} catch (PException e) {
137+
e.expect(errorType, IsBuiltinClassProfile.getUncached());
138+
}
139+
}
140+
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/random/RandomBuiltins.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryClinicBuiltinNode;
6666
import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider;
6767
import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
68+
import com.oracle.graal.python.nodes.util.CastToJavaUnsignedLongNode;
6869
import com.oracle.graal.python.runtime.exception.PythonErrorType;
6970
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
7071
import com.oracle.truffle.api.dsl.Cached;
@@ -76,7 +77,6 @@
7677
import com.oracle.truffle.api.frame.VirtualFrame;
7778
import com.oracle.truffle.api.library.CachedLibrary;
7879

79-
import static com.oracle.graal.python.runtime.exception.PythonErrorType.OverflowError;
8080
import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
8181
import static com.oracle.graal.python.runtime.exception.PythonErrorType.ValueError;
8282

@@ -165,20 +165,17 @@ public abstract static class SetStateNode extends PythonBuiltinNode {
165165
@Specialization
166166
PNone setstate(PRandom random, PTuple tuple,
167167
@Cached GetObjectArrayNode getObjectArrayNode,
168-
@CachedLibrary(limit = "3") PythonObjectLibrary lib) {
168+
@Cached CastToJavaUnsignedLongNode castNode) {
169169
Object[] arr = getObjectArrayNode.execute(tuple);
170170
if (arr.length != PRandom.N + 1) {
171171
throw raise(PythonErrorType.ValueError, ErrorMessages.STATE_VECTOR_INVALID);
172172
}
173173
int[] state = new int[PRandom.N];
174174
for (int i = 0; i < PRandom.N; ++i) {
175-
long l = lib.asJavaLong(arr[i]);
176-
if (l < 0 || l > 0xFFFFFFFFL) {
177-
throw raise(OverflowError, ErrorMessages.PYTHON_INT_TOO_LARGE_TO_CONV_TO, "C unsigned long");
178-
}
175+
long l = castNode.execute(arr[i]);
179176
state[i] = (int) l;
180177
}
181-
long index = lib.asJavaLong(arr[PRandom.N]);
178+
long index = castNode.execute(arr[PRandom.N]);
182179
if (index < 0 || index > PRandom.N) {
183180
throw raise(PythonErrorType.ValueError, ErrorMessages.STATE_VECTOR_INVALID);
184181
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.nodes.util;
42+
43+
import com.oracle.graal.python.builtins.objects.ints.PInt;
44+
import com.oracle.graal.python.nodes.ErrorMessages;
45+
import com.oracle.graal.python.nodes.PNodeWithContext;
46+
import com.oracle.graal.python.nodes.PRaiseNode;
47+
import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
48+
import com.oracle.truffle.api.CompilerDirectives;
49+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
50+
import com.oracle.truffle.api.dsl.Cached;
51+
import com.oracle.truffle.api.dsl.Cached.Shared;
52+
import com.oracle.truffle.api.dsl.Fallback;
53+
import com.oracle.truffle.api.dsl.GenerateUncached;
54+
import com.oracle.truffle.api.dsl.Specialization;
55+
import com.oracle.truffle.api.dsl.TypeSystemReference;
56+
import com.oracle.truffle.api.profiles.ConditionProfile;
57+
58+
import java.math.BigInteger;
59+
60+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.OverflowError;
61+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
62+
63+
/**
64+
* Casts a Python {@code int} to Java {@code long}. This method follows the semantics of CPython's
65+
* function {@code PyLong_AsUnsignedLong}:
66+
* <ul>
67+
* <li>raises {@code TypeError} if the argument is not an int</li>
68+
* <li>raises {@code OverflowError} if the argument is negative</li>
69+
* <li>raises {@code OverflowError} if the argument is greater than or equal to 2^64</li>
70+
* </ul>
71+
* Note that since Java {@code long} is signed, the values in the between 2^63 and 2^64-1 are
72+
* returned as negative numbers.
73+
*/
74+
@GenerateUncached
75+
@TypeSystemReference(PythonArithmeticTypes.class)
76+
public abstract class CastToJavaUnsignedLongNode extends PNodeWithContext {
77+
78+
public static CastToJavaUnsignedLongNode create() {
79+
return CastToJavaUnsignedLongNodeGen.create();
80+
}
81+
82+
public static CastToJavaUnsignedLongNode getUncached() {
83+
return CastToJavaUnsignedLongNodeGen.getUncached();
84+
}
85+
86+
public abstract long execute(int x);
87+
88+
public abstract long execute(long x);
89+
90+
public abstract long execute(Object x);
91+
92+
@Specialization
93+
public long toUnsignedLong(long x,
94+
@Cached @Shared("negative") ConditionProfile negativeProfile) {
95+
checkNegative(x < 0, negativeProfile);
96+
return x;
97+
}
98+
99+
@Specialization
100+
protected long toUnsignedLong(PInt x,
101+
@Cached @Shared("negative") ConditionProfile negativeProfile) {
102+
checkNegative(x.isNegative(), negativeProfile);
103+
return convertBigInt(x.getValue());
104+
}
105+
106+
@Fallback
107+
static long doUnsupported(@SuppressWarnings("unused") Object x) {
108+
CompilerDirectives.transferToInterpreter();
109+
throw PRaiseNode.getUncached().raise(TypeError, ErrorMessages.INTEGER_IS_REQUIRED);
110+
}
111+
112+
private static void checkNegative(boolean negative, ConditionProfile profile) {
113+
if (profile.profile(negative)) {
114+
CompilerDirectives.transferToInterpreter();
115+
throw PRaiseNode.getUncached().raise(OverflowError, ErrorMessages.CANNOT_CONVERT_NEGATIVE_VALUE_TO_UNSIGNED_INT);
116+
}
117+
}
118+
119+
@TruffleBoundary
120+
private static long convertBigInt(BigInteger bi) {
121+
if (bi.bitLength() > 64) {
122+
CompilerDirectives.transferToInterpreter();
123+
throw PRaiseNode.getUncached().raise(OverflowError, ErrorMessages.PYTHON_INT_TOO_LARGE_TO_CONV_TO, "unsigned long");
124+
}
125+
return bi.longValue();
126+
}
127+
}

0 commit comments

Comments
 (0)