Skip to content

Commit 87abd3a

Browse files
committed
[GR-23353] Create and use CastToJavaUnsignedLongNode
PullRequest: graalpython/1260
2 parents e42db99 + a082fe3 commit 87abd3a

File tree

3 files changed

+304
-7
lines changed

3 files changed

+304
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
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.PythonBuiltinClassType;
44+
import com.oracle.graal.python.builtins.objects.ints.PInt;
45+
import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
46+
import com.oracle.graal.python.runtime.exception.PException;
47+
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
48+
import com.oracle.graal.python.test.PythonTests;
49+
import com.oracle.truffle.api.Truffle;
50+
import com.oracle.truffle.api.frame.VirtualFrame;
51+
import com.oracle.truffle.api.nodes.RootNode;
52+
import org.junit.Assert;
53+
import org.junit.Before;
54+
import org.junit.Test;
55+
56+
import java.math.BigInteger;
57+
58+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.OverflowError;
59+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
60+
61+
public class CastToJavaUnsignedLongNodeTests {
62+
private static PythonObjectFactory factory = PythonObjectFactory.getUncached();
63+
64+
@Before
65+
public void setUp() {
66+
PythonTests.enterContext();
67+
}
68+
69+
@Test
70+
public void positiveInt() {
71+
Assert.assertEquals(0, castInt(0));
72+
Assert.assertEquals(16, castInt(16));
73+
Assert.assertEquals(Integer.MAX_VALUE, castInt(Integer.MAX_VALUE));
74+
Assert.assertEquals(42, castObject(42));
75+
}
76+
77+
@Test
78+
public void negativeInt() {
79+
expect(OverflowError, () -> castInt(-1));
80+
expect(OverflowError, () -> castInt(Integer.MIN_VALUE));
81+
}
82+
83+
@Test
84+
public void positiveLong() {
85+
Assert.assertEquals(0, castLong(0L));
86+
Assert.assertEquals(0x80000000L, castLong(0x80000000L));
87+
Assert.assertEquals(Long.MAX_VALUE, castLong(Long.MAX_VALUE));
88+
Assert.assertEquals(1234567890123L, castLong(1234567890123L));
89+
}
90+
91+
@Test
92+
public void negativeLong() {
93+
expect(OverflowError, () -> castLong(-1L));
94+
expect(OverflowError, () -> castLong(Long.MIN_VALUE));
95+
}
96+
97+
@Test
98+
public void positiveBigInt() {
99+
Assert.assertEquals(0, castObject(makePInt(0)));
100+
Assert.assertEquals(1234567890123L, castObject(makePInt(1234567890123L)));
101+
Assert.assertEquals(Long.MAX_VALUE, castObject(makePInt(Long.MAX_VALUE)));
102+
Assert.assertEquals(0x8000000000000000L, castObject(makePInt("8000000000000000")));
103+
Assert.assertEquals(0xFFFFFFFFFFFFFFFFL, castObject(makePInt("FFFFFFFFFFFFFFFF")));
104+
}
105+
106+
@Test
107+
public void negativeBigInt() {
108+
expect(OverflowError, () -> castObject(makePInt(-1)));
109+
expect(OverflowError, () -> castObject(makePInt("-10000000000000000")));
110+
}
111+
112+
@Test
113+
public void largeBigInt() {
114+
expect(OverflowError, () -> castObject(makePInt("10000000000000000")));
115+
expect(OverflowError, () -> castObject(makePInt("10000000000000001")));
116+
}
117+
118+
@Test
119+
public void nonInteger() {
120+
expect(TypeError, () -> castObject("123"));
121+
expect(TypeError, () -> castObject(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+
141+
private static long castInt(int arg) {
142+
return (Long) Truffle.getRuntime().createCallTarget(new RootNode(null) {
143+
@Child private CastToJavaUnsignedLongNode castNode = CastToJavaUnsignedLongNode.create();
144+
145+
@Override
146+
public Object execute(VirtualFrame frame) {
147+
return castNode.execute(arg);
148+
}
149+
}).call();
150+
}
151+
152+
private static long castLong(long arg) {
153+
return (Long) Truffle.getRuntime().createCallTarget(new RootNode(null) {
154+
@Child private CastToJavaUnsignedLongNode castNode = CastToJavaUnsignedLongNode.create();
155+
156+
@Override
157+
public Object execute(VirtualFrame frame) {
158+
return castNode.execute(arg);
159+
}
160+
}).call();
161+
}
162+
163+
private static long castObject(Object arg) {
164+
return (Long) Truffle.getRuntime().createCallTarget(new RootNode(null) {
165+
@Child private CastToJavaUnsignedLongNode castNode = CastToJavaUnsignedLongNode.create();
166+
167+
@Override
168+
public Object execute(VirtualFrame frame) {
169+
return castNode.execute(arg);
170+
}
171+
}).call();
172+
}
173+
}

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.Fallback;
51+
import com.oracle.truffle.api.dsl.Specialization;
52+
import com.oracle.truffle.api.dsl.TypeSystemReference;
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+
/**
60+
* Casts a Python {@code int} to Java {@code long}. This method follows the semantics of CPython's
61+
* function {@code PyLong_AsUnsignedLong}:
62+
* <ul>
63+
* <li>raises {@code TypeError} if the argument is not an int</li>
64+
* <li>raises {@code OverflowError} if the argument is negative</li>
65+
* <li>raises {@code OverflowError} if the argument is greater than or equal to 2^64</li>
66+
* </ul>
67+
* Note that since Java {@code long} is signed, the values in the between 2^63 and 2^64-1 are
68+
* returned as negative numbers.
69+
*/
70+
@TypeSystemReference(PythonArithmeticTypes.class)
71+
public abstract class CastToJavaUnsignedLongNode extends PNodeWithContext {
72+
73+
@Child private PRaiseNode raiseNode;
74+
75+
public static CastToJavaUnsignedLongNode create() {
76+
return CastToJavaUnsignedLongNodeGen.create();
77+
}
78+
79+
public abstract long execute(int x);
80+
81+
public abstract long execute(long x);
82+
83+
public abstract long execute(Object x);
84+
85+
@Specialization
86+
long toUnsignedLong(long x) {
87+
checkNegative(x < 0);
88+
return x;
89+
}
90+
91+
@Specialization
92+
long toUnsignedLong(PInt x) {
93+
checkNegative(x.isNegative());
94+
return convertBigInt(x.getValue());
95+
}
96+
97+
@Fallback
98+
long doUnsupported(@SuppressWarnings("unused") Object x) {
99+
throw getRaiseNode().raise(TypeError, ErrorMessages.INTEGER_IS_REQUIRED);
100+
}
101+
102+
private void checkNegative(boolean negative) {
103+
if (negative) {
104+
throw getRaiseNode().raise(OverflowError, ErrorMessages.CANNOT_CONVERT_NEGATIVE_VALUE_TO_UNSIGNED_INT);
105+
}
106+
}
107+
108+
@TruffleBoundary
109+
private long convertBigInt(BigInteger bi) {
110+
if (bi.bitLength() > 64) {
111+
throw getRaiseNode().raise(OverflowError, ErrorMessages.PYTHON_INT_TOO_LARGE_TO_CONV_TO, "unsigned long");
112+
}
113+
return bi.longValue();
114+
}
115+
116+
private PRaiseNode getRaiseNode() {
117+
if (raiseNode == null) {
118+
CompilerDirectives.transferToInterpreterAndInvalidate();
119+
if (isAdoptable()) {
120+
raiseNode = insert(PRaiseNode.create());
121+
} else {
122+
raiseNode = PRaiseNode.getUncached();
123+
}
124+
}
125+
return raiseNode;
126+
}
127+
}

0 commit comments

Comments
 (0)