Skip to content

Commit 8ab6454

Browse files
committed
Refactor ForeignNumber and handle all int, bool and float methods
1 parent 550e14d commit 8ab6454

File tree

8 files changed

+513
-427
lines changed

8 files changed

+513
-427
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_interop.py

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
3939

40+
import math
4041
import os
4142
import types
4243
import unittest
@@ -1254,6 +1255,10 @@ def test_foreign_number(self):
12541255
def wrap(obj):
12551256
return __graalpython__.foreign_wrapper(obj)
12561257

1258+
def assertValueAndType(actual, expected):
1259+
self.assertEqual(expected, actual)
1260+
self.assertEqual(type(expected), type(actual))
1261+
12571262
n = wrap(42)
12581263
self.assertEqual(type(n).mro(), [polyglot.ForeignNumber, polyglot.ForeignObject, object])
12591264
assert repr(n) == '42', repr(n)
@@ -1265,17 +1270,64 @@ def wrap(obj):
12651270
assert wrap(2) * wrap(3) == 6
12661271
assert wrap(7) / wrap(2) == 3.5
12671272
assert wrap(7) // wrap(2) == 3
1273+
assert wrap(8) % wrap(3) == 2
1274+
assert wrap(2) ** wrap(3) == 8
1275+
assert wrap(1) << wrap(3) == 8
1276+
assert wrap(8) >> wrap(2) == 2
1277+
1278+
# 1 and not 1.0 is unfortunate but interop does not give us a way to know if a non-primitive/wrapped 3.0 is integral or floating point
1279+
assertValueAndType(wrap(3.0) // wrap(2.0), 1)
1280+
assertValueAndType(wrap(3.0) // 2.0, 1.0)
1281+
assertValueAndType(3.0 // wrap(2.0), 1.0)
1282+
1283+
assertValueAndType(wrap(3) - 2.0, 1.0)
1284+
assertValueAndType(3.0 - wrap(2), 1.0)
12681285

12691286
assert wrap(0b1110) & wrap(0b0111) == 0b0110
12701287
assert wrap(0b1110) | wrap(0b0111) == 0b1111
12711288
assert wrap(0b1110) ^ wrap(0b0111) == 0b1001
12721289

1273-
# TODO test ~invert and more
1290+
assert wrap((1 << 65) - 2) & wrap(0b111) == 0b110
1291+
assert wrap((1 << 65) - 2) | wrap(0b111) == ((1 << 65) - 1)
1292+
assert wrap((1 << 65) - 2) ^ wrap(0b1) == ((1 << 65) - 1)
1293+
1294+
assert wrap(42).as_integer_ratio() == (42, 1)
1295+
assert wrap(0b1010).bit_count() == 2
1296+
assert wrap(0b1010).bit_length() == 4
1297+
assert wrap(42).conjugate() == 42
1298+
assert wrap(42).is_integer()
1299+
assert wrap(42.0).is_integer()
1300+
assert not wrap(42.5).is_integer()
1301+
assert wrap(42.0).to_bytes() == b"*"
1302+
1303+
assert ~wrap(42) == -43
1304+
assert -wrap(42) == -42
1305+
assert +wrap(42) == 42
1306+
1307+
assertValueAndType(abs(wrap(-2)), 2)
1308+
assertValueAndType(float(wrap(2)), 2.0)
1309+
assertValueAndType(int(wrap(2.3)), 2)
1310+
assertValueAndType(math.floor(wrap(2.3)), 2)
1311+
assertValueAndType(math.ceil(wrap(2.3)), 3)
1312+
assertValueAndType(math.trunc(wrap(-2.3)), -2)
1313+
assertValueAndType(round(wrap(2.3)), 2)
1314+
1315+
missing_int_methods = set(dir(int)) - set(dir(type(wrap(1))))
1316+
missing_int_methods = [m for m in missing_int_methods if m.startswith('_') and m != '__getnewargs__']
1317+
self.assertEqual([], missing_int_methods)
1318+
1319+
missing_float_methods = set(dir(float)) - set(dir(type(wrap(1.2))))
1320+
missing_float_methods = [m for m in missing_float_methods if m.startswith('_') and m not in ('__getnewargs__', '__getformat__')]
1321+
self.assertEqual([], missing_float_methods)
12741322

12751323
def test_foreign_boolean(self):
12761324
def wrap(obj):
12771325
return __graalpython__.foreign_wrapper(obj)
12781326

1327+
def assertValueAndType(actual, expected):
1328+
self.assertEqual(expected, actual)
1329+
self.assertEqual(type(expected), type(actual))
1330+
12791331
self.assertEqual(type(wrap(True)).mro(), [polyglot.ForeignBoolean, polyglot.ForeignNumber, polyglot.ForeignObject, object])
12801332
assert repr(wrap(True)) == 'True'
12811333
assert repr(wrap(False)) == 'False'
@@ -1287,13 +1339,13 @@ def wrap(obj):
12871339
assert bool(wrap(True)) is True
12881340
assert bool(wrap(False)) is False
12891341

1290-
assert wrap(True) + wrap(2) == 3
1291-
assert wrap(False) + wrap(2) == 2
1292-
assert wrap(2) + wrap(True) == 3
1293-
assert wrap(2) + wrap(False) == 2
1342+
assertValueAndType(wrap(True) + wrap(2), 3)
1343+
assertValueAndType(wrap(False) + wrap(2), 2)
1344+
assertValueAndType(wrap(2) + wrap(True), 3)
1345+
assertValueAndType(wrap(2) + wrap(False), 2)
12941346

1295-
assert wrap(True) - wrap(2) == -1
1296-
assert wrap(2) - wrap(True) == 1
1347+
assertValueAndType(wrap(True) - wrap(2), -1)
1348+
assertValueAndType(wrap(2) - wrap(True), 1)
12971349

12981350
assert wrap(True) & wrap(True) is True
12991351
assert wrap(True) & wrap(False) is False
@@ -1304,7 +1356,15 @@ def wrap(obj):
13041356
assert wrap(True) ^ wrap(False) is True
13051357
assert wrap(False) ^ wrap(False) is False
13061358

1307-
# TODO ~invert
1359+
assertValueAndType(~wrap(True), -2)
1360+
assertValueAndType(~wrap(False), -1)
1361+
1362+
assertValueAndType(float(wrap(True)), 1.0)
1363+
assertValueAndType(int(wrap(True)), 1)
1364+
1365+
missing_bool_methods = set(dir(bool)) - set(dir(type(wrap(True))))
1366+
missing_bool_methods = [m for m in missing_bool_methods if m.startswith('_') and m != '__getnewargs__']
1367+
self.assertEqual([], missing_bool_methods)
13081368

13091369
def test_foreign_repl(self):
13101370
from java.util.logging import LogRecord

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignBooleanBuiltins.java

Lines changed: 2 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,7 @@
3636
import com.oracle.graal.python.builtins.objects.type.TpSlots;
3737
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry.NbBoolBuiltinNode;
3838
import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode;
39-
import com.oracle.graal.python.nodes.expression.BinaryArithmetic.BitAndNode;
40-
import com.oracle.graal.python.nodes.expression.BinaryArithmetic.BitOrNode;
41-
import com.oracle.graal.python.nodes.expression.BinaryArithmetic.BitXorNode;
4239
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
43-
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
4440
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
4541
import com.oracle.graal.python.runtime.GilNode;
4642
import com.oracle.truffle.api.CompilerDirectives;
@@ -58,15 +54,9 @@
5854

5955
import java.util.List;
6056

61-
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___AND__;
6257
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___INDEX__;
63-
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___OR__;
64-
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___RAND__;
6558
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___REPR__;
66-
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___ROR__;
67-
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___RXOR__;
6859
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___STR__;
69-
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___XOR__;
7060

7161
/*
7262
* This class handles foreign booleans and reimplements the methods of Python bool.
@@ -109,7 +99,7 @@ static boolean bool(Object receiver,
10999
@GenerateNodeFactory
110100
abstract static class IndexNode extends PythonUnaryBuiltinNode {
111101
@Specialization(limit = "3")
112-
protected static Object doIt(Object object,
102+
protected static int doIt(Object object,
113103
@CachedLibrary("object") InteropLibrary lib,
114104
@Cached GilNode gil) {
115105
gil.release(true);
@@ -126,6 +116,7 @@ protected static Object doIt(Object object,
126116
}
127117

128118
@Builtin(name = J___STR__, minNumOfPositionalArgs = 1)
119+
@Builtin(name = J___REPR__, minNumOfPositionalArgs = 1)
129120
@GenerateNodeFactory
130121
abstract static class StrNode extends PythonUnaryBuiltinNode {
131122
@Specialization
@@ -149,81 +140,4 @@ Object str(VirtualFrame frame, Object object,
149140
return strNode.execute(frame, inliningTarget, value);
150141
}
151142
}
152-
153-
@Builtin(name = J___REPR__, minNumOfPositionalArgs = 1)
154-
@GenerateNodeFactory
155-
abstract static class ReprNode extends StrNode {
156-
}
157-
158-
@Builtin(name = J___RAND__, minNumOfPositionalArgs = 2)
159-
@Builtin(name = J___AND__, minNumOfPositionalArgs = 2)
160-
@GenerateNodeFactory
161-
abstract static class AndNode extends PythonBinaryBuiltinNode {
162-
@Specialization(limit = "3")
163-
protected static Object op(VirtualFrame frame, Object left, Object right,
164-
@Cached BitAndNode andNode,
165-
@CachedLibrary("left") InteropLibrary lib,
166-
@Cached GilNode gil) {
167-
try {
168-
boolean leftBoolean;
169-
gil.release(true);
170-
try {
171-
leftBoolean = lib.asBoolean(left);
172-
} finally {
173-
gil.acquire();
174-
}
175-
return andNode.executeObject(frame, leftBoolean, right);
176-
} catch (UnsupportedMessageException e) {
177-
throw CompilerDirectives.shouldNotReachHere();
178-
}
179-
}
180-
}
181-
182-
@Builtin(name = J___ROR__, minNumOfPositionalArgs = 2)
183-
@Builtin(name = J___OR__, minNumOfPositionalArgs = 2)
184-
@GenerateNodeFactory
185-
abstract static class OrNode extends PythonBinaryBuiltinNode {
186-
@Specialization(limit = "3")
187-
protected static Object op(VirtualFrame frame, Object left, Object right,
188-
@Cached BitOrNode orNode,
189-
@CachedLibrary("left") InteropLibrary lib,
190-
@Cached GilNode gil) {
191-
try {
192-
boolean leftBoolean;
193-
gil.release(true);
194-
try {
195-
leftBoolean = lib.asBoolean(left);
196-
} finally {
197-
gil.acquire();
198-
}
199-
return orNode.executeObject(frame, leftBoolean, right);
200-
} catch (UnsupportedMessageException e) {
201-
throw CompilerDirectives.shouldNotReachHere();
202-
}
203-
}
204-
}
205-
206-
@Builtin(name = J___RXOR__, minNumOfPositionalArgs = 2)
207-
@Builtin(name = J___XOR__, minNumOfPositionalArgs = 2)
208-
@GenerateNodeFactory
209-
abstract static class XorNode extends PythonBinaryBuiltinNode {
210-
@Specialization(limit = "3")
211-
protected static Object op(VirtualFrame frame, Object left, Object right,
212-
@Cached BitXorNode xorNode,
213-
@CachedLibrary("left") InteropLibrary lib,
214-
@Cached GilNode gil) {
215-
try {
216-
boolean leftBoolean;
217-
gil.release(true);
218-
try {
219-
leftBoolean = lib.asBoolean(left);
220-
} finally {
221-
gil.acquire();
222-
}
223-
return xorNode.executeObject(frame, leftBoolean, right);
224-
} catch (UnsupportedMessageException e) {
225-
throw CompilerDirectives.shouldNotReachHere();
226-
}
227-
}
228-
}
229143
}

0 commit comments

Comments
 (0)