Skip to content

Commit e0f2b36

Browse files
committed
[GR-21522] Implement _pickle EE.
PullRequest: graalpython/1538
2 parents 3b64523 + ed7810c commit e0f2b36

File tree

22 files changed

+806
-72
lines changed

22 files changed

+806
-72
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@
3636
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
39-
import sys
4039
import struct
40+
import sys
41+
4142

4243
def assert_raises(err, fn, *args, **kwargs):
4344
raised = False
@@ -116,7 +117,7 @@ def test_pack_unpack():
116117
for align in alignment:
117118
_fmt = align + fmt
118119
result = struct.pack(_fmt, *vals)
119-
assert vals == struct.unpack(_fmt, result)
120+
assert vals == struct.unpack(_fmt, result), "{} != struct.unpack({}, {})".format(vals, _fmt, result)
120121
assert struct.calcsize(_fmt) == len(result), "calcsize('{}')={} != len({})={}".format(
121122
_fmt, struct.calcsize(_fmt), result, len(result))
122123

@@ -169,6 +170,7 @@ def test_pack_large_long():
169170
assert struct.pack(fmt, 18446744073709551615) == b'\xff\xff\xff\xff\xff\xff\xff\xff'
170171
assert struct.unpack(fmt, b'\xff\xff\xff\xff\xff\xff\xff\xff') == (18446744073709551615,)
171172

173+
172174
def test_pack_into():
173175
test_string = b'Reykjavik rocks, eow!'
174176
writable_buf = bytearray(b' '*100)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@ public enum PythonBuiltinClassType implements TruffleObject {
162162
LsprofProfiler("Profiler", "_lsprof"),
163163
PStruct("Struct", "_struct"),
164164
PStructUnpackIterator("unpack_iterator", "_struct"),
165+
Pickler("Pickler", "_pickle"),
166+
PicklerMemoProxy("PicklerMemoProxy", "_pickle"),
167+
UnpicklerMemoProxy("UnpicklerMemoProxy", "_pickle"),
168+
Unpickler("Unpickler", "_pickle"),
169+
PickleBuffer("PickleBuffer", "_pickle"),
165170

166171
// bz2
167172
BZ2Compressor("BZ2Compressor", "_bz2"),
@@ -264,6 +269,9 @@ public enum PythonBuiltinClassType implements TruffleObject {
264269
ZLibError("error", "zlib", Flags.EXCEPTION),
265270
LZMAError("LZMAError", "_lzma", Flags.EXCEPTION),
266271
StructError("StructError", "_struct", Flags.EXCEPTION),
272+
PickleError("PickleError", "_pickle", Flags.EXCEPTION),
273+
PicklingError("PicklingError", "_pickle", Flags.EXCEPTION),
274+
UnpicklingError("UnpicklingError", "_pickle", Flags.EXCEPTION),
267275
SocketGAIError("gaierror", "_socket", Flags.EXCEPTION),
268276
SocketHError("herror", "_socket", Flags.EXCEPTION),
269277
SocketTimeout("timeout", "_socket", Flags.EXCEPTION),
@@ -569,6 +577,9 @@ public final Shape getInstanceShape(PythonLanguage lang) {
569577
StructError.base = Exception;
570578
BinasciiError.base = ValueError;
571579
BinasciiIncomplete.base = Exception;
580+
PickleError.base = Exception;
581+
PicklingError.base = PickleError;
582+
UnpicklingError.base = PickleError;
572583

573584
// warnings
574585
Warning.base = Exception;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinConstructors.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,14 @@ public abstract static class IntNode extends PythonTernaryBuiltinNode {
10291029
@Child private LookupAndCallUnaryNode callReprNode;
10301030
@Child private WarnNode warnNode;
10311031

1032+
public final Object executeWith(VirtualFrame frame, Object number) {
1033+
return execute(frame, PythonBuiltinClassType.PInt, number, 10);
1034+
}
1035+
1036+
public final Object executeWith(VirtualFrame frame, Object number, Object base) {
1037+
return execute(frame, PythonBuiltinClassType.PInt, number, base);
1038+
}
1039+
10321040
@TruffleBoundary
10331041
private static Object stringToIntInternal(String num, int base) {
10341042
try {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/CodecsModuleBuiltins.java

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,30 @@ public class CodecsModuleBuiltins extends PythonBuiltins {
112112
public static final String SURROGATEESCAPE = "surrogateescape";
113113
public static final String SURROGATEPASS = "surrogatepass";
114114

115+
static CodingErrorAction convertCodingErrorAction(String errors) {
116+
CodingErrorAction errorAction;
117+
switch (errors) {
118+
// TODO: see [GR-10256] to implement the correct handling mechanics
119+
case IGNORE:
120+
errorAction = CodingErrorAction.IGNORE;
121+
break;
122+
case REPLACE:
123+
case NAMEREPLACE:
124+
errorAction = CodingErrorAction.REPLACE;
125+
break;
126+
case STRICT:
127+
case BACKSLASHREPLACE:
128+
case SURROGATEPASS:
129+
case SURROGATEESCAPE:
130+
case XMLCHARREFREPLACE:
131+
default:
132+
// Everything else will be handled by our Handle nodes
133+
errorAction = CodingErrorAction.REPORT;
134+
break;
135+
}
136+
return errorAction;
137+
}
138+
115139
@Override
116140
protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFactories() {
117141
return CodecsModuleBuiltinsFactory.getFactories();
@@ -432,36 +456,13 @@ public static HandleDecodingErrorNode create() {
432456
}
433457
}
434458

435-
protected static CodingErrorAction convertCodingErrorAction(String errors) {
436-
CodingErrorAction errorAction;
437-
switch (errors) {
438-
// TODO: see [GR-10256] to implement the correct handling mechanics
439-
case IGNORE:
440-
errorAction = CodingErrorAction.IGNORE;
441-
break;
442-
case REPLACE:
443-
case NAMEREPLACE:
444-
errorAction = CodingErrorAction.REPLACE;
445-
break;
446-
case STRICT:
447-
case BACKSLASHREPLACE:
448-
case SURROGATEPASS:
449-
case SURROGATEESCAPE:
450-
case XMLCHARREFREPLACE:
451-
default:
452-
// Everything else will be handled by our Handle nodes
453-
errorAction = CodingErrorAction.REPORT;
454-
break;
455-
}
456-
return errorAction;
457-
}
458-
459459
// _codecs.encode(obj, encoding='utf-8', errors='strict')
460460
@Builtin(name = "__truffle_encode__", minNumOfPositionalArgs = 1, parameterNames = {"obj", "encoding", "errors"})
461461
@ArgumentClinic(name = "encoding", conversion = ArgumentClinic.ClinicConversion.String, defaultValue = "\"utf-8\"", useDefaultForNone = true)
462462
@ArgumentClinic(name = "errors", conversion = ArgumentClinic.ClinicConversion.String, defaultValue = "\"strict\"", useDefaultForNone = true)
463463
@GenerateNodeFactory
464464
public abstract static class CodecsEncodeNode extends PythonTernaryClinicBuiltinNode {
465+
public abstract Object execute(Object str, Object encoding, Object errors);
465466

466467
@Override
467468
protected ArgumentClinicProvider getArgumentClinic() {
@@ -504,7 +505,7 @@ Object encode(Object str, @SuppressWarnings("unused") Object encoding, @Suppress
504505
@ArgumentClinic(name = "errors", conversion = ArgumentClinic.ClinicConversion.String, defaultValue = "\"strict\"", useDefaultForNone = true)
505506
@ArgumentClinic(name = "final", conversion = ArgumentClinic.ClinicConversion.Boolean, defaultValue = "false", useDefaultForNone = true)
506507
@GenerateNodeFactory
507-
abstract static class CodecsDecodeNode extends PythonQuaternaryClinicBuiltinNode {
508+
public abstract static class CodecsDecodeNode extends PythonQuaternaryClinicBuiltinNode {
508509

509510
@Override
510511
protected ArgumentClinicProvider getArgumentClinic() {
@@ -544,7 +545,7 @@ Object decode(Object bytes, @SuppressWarnings("unused") Object encoding, @Suppre
544545
@ArgumentClinic(name = "data", conversion = ArgumentClinic.ClinicConversion.Buffer)
545546
@ArgumentClinic(name = "errors", conversion = ArgumentClinic.ClinicConversion.String, defaultValue = "\"strict\"", useDefaultForNone = true)
546547
@GenerateNodeFactory
547-
abstract static class CodecsEscapeDecodeNode extends PythonBinaryClinicBuiltinNode {
548+
public abstract static class CodecsEscapeDecodeNode extends PythonBinaryClinicBuiltinNode {
548549
enum Errors {
549550
ERR_STRICT,
550551
ERR_IGNORE,
@@ -695,8 +696,7 @@ private static boolean isHexDigit(char digit) {
695696
@Builtin(name = "escape_encode", minNumOfPositionalArgs = 1, parameterNames = {"data", "errors"})
696697
@ArgumentClinic(name = "errors", conversion = ArgumentClinic.ClinicConversion.String, defaultValue = "\"strict\"", useDefaultForNone = true)
697698
@GenerateNodeFactory
698-
abstract static class CodecsEscapeEncodeNode extends PythonBinaryClinicBuiltinNode {
699-
699+
public abstract static class CodecsEscapeEncodeNode extends PythonBinaryClinicBuiltinNode {
700700
@Override
701701
protected ArgumentClinicProvider getArgumentClinic() {
702702
return CodecsModuleBuiltinsClinicProviders.CodecsEscapeEncodeNodeClinicProviderGen.INSTANCE;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@
3333
import java.nio.charset.StandardCharsets;
3434
import java.util.Arrays;
3535

36+
import com.oracle.graal.python.builtins.Python3Core;
3637
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
3738
import com.oracle.graal.python.builtins.objects.object.PythonObjectLibrary;
3839
import com.oracle.graal.python.nodes.ErrorMessages;
3940
import com.oracle.graal.python.nodes.PRaiseNode;
40-
import com.oracle.graal.python.builtins.Python3Core;
4141
import com.oracle.graal.python.runtime.PythonParser.ParserErrorCallback;
4242
import com.oracle.graal.python.util.PythonUtils;
4343
import com.oracle.truffle.api.CompilerAsserts;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatUtils.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@
4848
/**
4949
* Contains helper methods for parsing float numbers in float() and complex() constructors.
5050
*/
51-
public class FloatUtils {
51+
public final class FloatUtils {
52+
private FloatUtils() {
53+
}
5254

5355
public static int skipAsciiWhitespace(String str, int start, int len) {
5456
int offset = start;
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Copyright (c) 2021, 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.builtins.objects.ints;
42+
43+
import static com.oracle.graal.python.nodes.ErrorMessages.TOO_LARGE_TO_CONVERT;
44+
45+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
46+
import com.oracle.graal.python.nodes.PRaiseNode;
47+
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
48+
import com.oracle.graal.python.util.NumericSupport;
49+
import com.oracle.graal.python.util.OverflowException;
50+
import com.oracle.truffle.api.dsl.Cached;
51+
import com.oracle.truffle.api.dsl.Specialization;
52+
import com.oracle.truffle.api.nodes.Node;
53+
54+
/**
55+
* Namespace containing equivalent nodes of {@code _Pylong_XXX} private function from
56+
* {@code longobject.c}
57+
*/
58+
public final class IntNodes {
59+
private IntNodes() {
60+
}
61+
62+
/**
63+
* Equivalent of CPython's {@code _PyLong_Sign}. Return 0 if v is 0, -1 if v < 0, +1 if v > 0.
64+
*/
65+
public abstract static class PyLongSign extends Node {
66+
public abstract int execute(Object value);
67+
68+
@Specialization
69+
static int doInt(int value) {
70+
return Integer.compare(value, 0);
71+
}
72+
73+
@Specialization
74+
static int doLong(long value) {
75+
return Long.compare(value, 0);
76+
}
77+
78+
@Specialization
79+
static int doPInt(PInt value) {
80+
return value.compareTo(0);
81+
}
82+
}
83+
84+
/**
85+
* Equivalent to CPython's {@code _PyLong_NumBits}. Return the number of bits needed to
86+
* represent the absolute value of a long.
87+
*/
88+
public abstract static class PyLongNumBits extends Node {
89+
public abstract int execute(Object value);
90+
91+
@Specialization
92+
static int doInt(int value) {
93+
return Integer.SIZE - Integer.numberOfLeadingZeros(Math.abs(value));
94+
}
95+
96+
@Specialization
97+
static int doLong(long value) {
98+
return Long.SIZE - Long.numberOfLeadingZeros(Math.abs(value));
99+
}
100+
101+
@Specialization
102+
static int doPInt(PInt value) {
103+
return value.bitLength();
104+
}
105+
}
106+
107+
/**
108+
* Equivalent to CPython's {@code _PyLong_AsByteArray}. Convert the least-significant 8*n bits
109+
* of long v to a base-256 integer, stored in array bytes.
110+
*/
111+
public abstract static class PyLongAsByteArray extends Node {
112+
public abstract byte[] execute(Object value, int size, boolean bigEndian);
113+
114+
@Specialization
115+
static byte[] doLong(long value, int size, boolean bigEndian) {
116+
final byte[] bytes = new byte[size];
117+
NumericSupport support = bigEndian ? NumericSupport.bigEndian() : NumericSupport.littleEndian();
118+
support.putLong(bytes, 0, value);
119+
return bytes;
120+
}
121+
122+
@Specialization
123+
static byte[] doPInt(PInt value, int size, boolean bigEndian,
124+
@Cached PRaiseNode raiseNode) {
125+
final byte[] bytes = new byte[size];
126+
NumericSupport support = bigEndian ? NumericSupport.bigEndian() : NumericSupport.littleEndian();
127+
try {
128+
support.putBigInteger(bytes, 0, value.getValue(), size);
129+
} catch (OverflowException oe) {
130+
throw raiseNode.raise(PythonBuiltinClassType.OverflowError, TOO_LARGE_TO_CONVERT, "int");
131+
}
132+
return bytes;
133+
}
134+
}
135+
136+
/**
137+
* Equivalent to CPython's {@code _PyLong_FromByteArray}. View the n unsigned bytes as a binary
138+
* integer in base 256, and return a Python int with the same numeric value.
139+
*/
140+
public abstract static class PyLongFromByteArray extends Node {
141+
public abstract Object execute(byte[] data, boolean bigEndian);
142+
143+
protected boolean fitsInLong(byte[] data) {
144+
return data.length <= Long.BYTES;
145+
}
146+
147+
@Specialization(guards = "fitsInLong(data)")
148+
static Object doLong(byte[] data, boolean bigEndian) {
149+
NumericSupport support = bigEndian ? NumericSupport.bigEndian() : NumericSupport.littleEndian();
150+
return support.getLong(data, 0);
151+
}
152+
153+
@Specialization(guards = "!fitsInLong(data)")
154+
static Object doPInt(byte[] data, boolean bigEndian,
155+
@Cached PythonObjectFactory factory) {
156+
NumericSupport support = bigEndian ? NumericSupport.bigEndian() : NumericSupport.littleEndian();
157+
return factory.createInt(support.getBigInteger(data, 0));
158+
}
159+
}
160+
}

0 commit comments

Comments
 (0)