Skip to content

Commit 6d2744b

Browse files
committed
[GR-18002] Implement a TruffleLibrary for hashing storages
PullRequest: graalpython/807
2 parents 75413dd + b7a5519 commit 6d2744b

File tree

99 files changed

+4300
-4486
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+4300
-4486
lines changed
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
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.test.builtin;
42+
43+
import static com.oracle.graal.python.test.PythonTests.assertPrints;
44+
45+
import org.junit.Test;
46+
47+
public class HashingTest {
48+
49+
@Test
50+
public void mappingproxyTest() {
51+
String source = "_mappingproxy = type(type.__dict__)\n" +
52+
"d = {\"a\": 1, \"b\": 2, \"c\": 3}\n" +
53+
"mp = _mappingproxy(d)\n" +
54+
"assert len(mp) == 3\n" +
55+
"assert d.keys() == {'a', 'b', 'c'}\n" +
56+
"assert mp.keys() == d.keys()\n" +
57+
"assert len(mp.keys()) == 3, \"keys view has invalid length\"\n" +
58+
"assert set(mp.keys()) == {'a', 'b', 'c'}, \"keys view invalid\"\n" +
59+
"assert len(mp.values()) == 3, \"values view has invalid length\"\n" +
60+
"assert set(mp.values()) == {1, 2, 3}, \"values view invalid\"\n" +
61+
"assert len(mp.items()) == 3, \"items view has invalid length\"\n" +
62+
"assert set(mp.items()) == {('a', 1), ('b', 2), ('c', 3)}, \"items view invalid\"\n";
63+
assertPrints("", source);
64+
}
65+
66+
@Test
67+
public void customMappingObjectTest() {
68+
String source = "class CustomMappingObject:\n" +
69+
" def __init__(self, keys, values):\n" +
70+
" self._keys = keys\n" +
71+
" self._values = values\n" +
72+
" def __getitem__(self, k):\n" +
73+
" for i in range(len(self._keys)):\n" +
74+
" if k == self._keys[i]:\n" +
75+
" return self._values[i]\n" +
76+
" raise KeyError\n" +
77+
" def __setitem__(self, k, v):\n" +
78+
" for i in range(len(self._keys)):\n" +
79+
" if k == self._keys[i]:\n" +
80+
" self._values[i] = v\n" +
81+
" return v\n" +
82+
" raise KeyError\n" +
83+
" def keys(self):\n" +
84+
" return set(self._keys)\n" +
85+
" def values(self):\n" +
86+
" return self._values\n" +
87+
" def items(self):\n" +
88+
" return {(self._keys[i], self._values[i]) for i in range(len(self._keys))}\n" +
89+
" def __len__(self):\n" +
90+
" return len(self._keys)\n" +
91+
"_mappingproxy = type(type.__dict__)\n" +
92+
"mp_list = _mappingproxy(CustomMappingObject([\"a\", \"b\", \"c\"], [1, 2, 3]))\n" +
93+
"assert mp_list.keys() == {\"a\", \"b\", \"c\"}\n" +
94+
"";
95+
assertPrints("", source);
96+
}
97+
98+
@Test
99+
public void dictViewTest() {
100+
String source = "d = dict()\n" +
101+
"d['a'] = 1\n" +
102+
"d['b'] = 2\n" +
103+
"d['c'] = 3\n" +
104+
"assert len(d) == 3\n" +
105+
"assert len(d.keys()) == 3, \"keys view has invalid length\"\n" +
106+
"assert set(d.keys()) == {'a', 'b', 'c'}, \"keys view invalid\"\n" +
107+
"assert len(d.values()) == 3, \"values view has invalid length\"\n" +
108+
"assert set(d.values()) == {1, 2, 3}, \"values view invalid\"\n" +
109+
"assert len(d.items()) == 3, \"items view has invalid length\"\n" +
110+
"assert set(d.items()) == {('a', 1), ('b', 2), ('c', 3)}, \"items view invalid\"\n" +
111+
"";
112+
assertPrints("", source);
113+
}
114+
115+
@Test
116+
public void dictEqualTest1() {
117+
String source = "d = dict.fromkeys(['a', 'b', 'c'])\n" +
118+
"assert len(d) == 3\n" +
119+
"assert set(d.keys()) == {'a', 'b', 'c'}\n" +
120+
"assert set(d.values()) == {None}\n" +
121+
"d = dict.fromkeys(['a', 'b', 'c'], 1)\n" +
122+
"assert len(d) == 3\n" +
123+
"assert set(d.keys()) == {'a', 'b', 'c'}\n" +
124+
"assert set(d.values()) == {1}\n" +
125+
"d = dict.fromkeys(['a', 'b', 'c'], 1.0)\n" +
126+
"assert len(d) == 3\n" +
127+
"assert set(d.keys()) == {'a', 'b', 'c'}\n" +
128+
"assert set(d.values()) == {1.0}\n" +
129+
"d = dict.fromkeys(['a', 'b', 'c'], 'd')\n" +
130+
"assert len(d) == 3\n" +
131+
"assert set(d.keys()) == {'a', 'b', 'c'}\n" +
132+
"assert set(d.values()) == {'d'}\n" +
133+
"d = dict.fromkeys(['a', 'b', 'c'], None)\n" +
134+
"assert len(d) == 3\n" +
135+
"assert set(d.keys()) == {'a', 'b', 'c'}\n" +
136+
"assert set(d.values()) == {None}\n" +
137+
"o = object()\n" +
138+
"d = dict.fromkeys(['a', 'b', 'c'], o)\n" +
139+
"assert len(d) == 3\n" +
140+
"assert set(d.keys()) == {'a', 'b', 'c'}\n" +
141+
"assert set(d.values()) == {o}\n" +
142+
"";
143+
assertPrints("", source);
144+
}
145+
146+
@Test
147+
public void dictEqualTest2() {
148+
String source = "d = dict(a=1, b=2, c=3)\n" +
149+
"def assert_raises(err, fn, *args, **kwargs):\n" +
150+
" raised = False\n" +
151+
" try:\n" +
152+
" fn(*args, **kwargs)\n" +
153+
" except err:\n" +
154+
" raised = True\n" +
155+
" assert raised\n" +
156+
"assert len(d) == 3\n" +
157+
"assert set(d.keys()) == {'a', 'b', 'c'}\n" +
158+
"d = dict.fromkeys(['a', 'b', 'c'])\n" +
159+
"assert len(d) == 3\n" +
160+
"assert set(d.keys()) == {'a', 'b', 'c'}\n" +
161+
"assert list(d.values()) == [None, None, None]\n" +
162+
"d = dict.fromkeys(['a', 'b', 'c'], 1)\n" +
163+
"assert len(d) == 3\n" +
164+
"assert set(d.keys()) == {'a', 'b', 'c'}\n" +
165+
"assert list(d.values()) == [1, 1, 1]\n" +
166+
"assert_raises(TypeError, dict.fromkeys, 10)\n" +
167+
"";
168+
assertPrints("", source);
169+
}
170+
171+
@Test
172+
public void dictEqualTest3() {
173+
String source = "key_set = {'a', 'b', 'c', 'd'}\n" +
174+
"\n" +
175+
"class CustomMappingObject:\n" +
176+
" def __init__(self, keys):\n" +
177+
" self.__keys = keys\n" +
178+
"\n" +
179+
" def keys(self):\n" +
180+
" return self.__keys\n" +
181+
"\n" +
182+
" def __getitem__(self, key):\n" +
183+
" if key in self.__keys:\n" +
184+
" return ord(key)\n" +
185+
" raise KeyError(key)\n" +
186+
"\n" +
187+
" def __len__(self):\n" +
188+
" return len(self.keys)\n" +
189+
"\n" +
190+
"d = dict(CustomMappingObject(key_set))\n" +
191+
"assert len(d) == 4, \"invalid length, expected 4 but was %d\" % len(d)\n" +
192+
"assert set(d.keys()) == key_set, \"unexpected keys: %s\" % str(d.keys())\n" +
193+
"assert set(d.values()) == {97, 98, 99, 100}, \"unexpected values: %s\" % str(d.values())\n" +
194+
"";
195+
assertPrints("", source);
196+
}
197+
198+
}

graalpython/com.oracle.graal.python.test/src/tests/cpyext/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -415,7 +415,7 @@ def create_module(self, name=None):
415415
fargs["resultvarlocations"] = ", ".join("&" + arg.rpartition(" ")[2] for arg in fargs["resultvars"])
416416
if "resulttype" not in fargs:
417417
fargs["resulttype"] = "void*"
418-
if len(fargs["resultvarlocations"]):
418+
if len(fargs["resultvarlocations"]) and not fargs["resultvarlocations"].startswith(","):
419419
fargs["resultvarlocations"] = ", " + fargs["resultvarlocations"]
420420
self._insert(fargs, "customcode", "")
421421
super(CPyExtFunctionOutVars, self).create_module(name)

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -597,3 +597,34 @@ def __eq__(self, other):
597597
assert b in d
598598
assert count_hash == 4, count_hash
599599
assert count_eq == 1, count_eq
600+
601+
602+
def test_hash_and_eq_for_dynamic_object_storage():
603+
class MyObject:
604+
def __init__(self, string):
605+
self.string = string
606+
607+
def __eq__(self, other):
608+
return self.string == other
609+
610+
def __hash__(self):
611+
return hash(self.string)
612+
613+
d = {"1": 42}
614+
615+
d2 = MyObject("1").__dict__
616+
d2["1"] = 42
617+
618+
assert MyObject("1") in d
619+
assert d[MyObject("1")] == 42
620+
d[MyObject("1")] = 112
621+
assert d[MyObject("1")] == 112
622+
del d[MyObject("1")]
623+
assert "1" not in d
624+
625+
assert MyObject("1") in d2
626+
assert d2[MyObject("1")] == 42
627+
d2[MyObject("1")] = 112
628+
assert d2[MyObject("1")] == 112
629+
del d2[MyObject("1")]
630+
assert "1" not in d2

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -323,3 +323,10 @@ def test_hashable_frozenset():
323323
d = {key1: 42}
324324
assert hash(key1) == hash(key2)
325325
assert d[key2] == 42
326+
327+
328+
def test_equality():
329+
s1 = {1, 2, 3}
330+
s2 = {1, 3}
331+
assert not s1 == s2
332+
assert not s2 == s1

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -70,3 +70,14 @@ def test_base():
7070
#
7171
# C = type('C', (B, int), {'spam': lambda self: 'spam%s' % self})
7272
# assert C.__base__ == int
73+
74+
75+
def test_namespace_with_non_string_keys():
76+
class MyStr(str):
77+
pass
78+
79+
A = type('A', (), {
80+
MyStr("x"): 42
81+
})
82+
assert A.x == 42
83+
assert any(type(k) == MyStr for k in A.__dict__.keys())

graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_dict.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
*DictTest.test_dictview_set_operations_on_keys
1414
*DictTest.test_empty_presized_dict_in_freelist
1515
*DictTest.test_eq
16-
*DictTest.test_equal_operator_modifying_operand
1716
*DictTest.test_errors_in_view_containment_check
1817
*DictTest.test_fromkeys_operator_modifying_dict_operand
1918
*DictTest.test_fromkeys_operator_modifying_set_operand

graalpython/com.oracle.graal.python.test/testData/goldenFiles/BasicTests/call12.tast

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ ModuleRootNode Name: <module 'call12'> SourceSection: [0,25]`foo(**mydict1, **my
1010
KeywordArgumentsNodeGen SourceSection: None
1111
CompactKeywordsNodeGen SourceSection: None
1212
ExecuteKeywordStarargsNodeGen SourceSection: None
13-
DictConcatNode SourceSection: None
13+
DictConcatNodeGen SourceSection: None
1414
ReadNameNodeGen SourceSection: [6,13]`mydict1`
1515
Identifier: mydict1
1616
ReadNameNodeGen SourceSection: [17,24]`mydict2`

graalpython/com.oracle.graal.python.test/testData/goldenFiles/DictAndSetTests/dict01.tast

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ ModuleRootNode Name: <module 'dict01'> SourceSection: [0,2]`{}`
55
FrameDescriptor: Empty
66
Documentation: None
77
InnerRootNode SourceSection: [0,2]`{}`
8-
DictLiteralNode SourceSection: [0,2]`{}`
8+
DictLiteralNodeGen SourceSection: [0,2]`{}`
99
PythonObjectFactoryNodeGen SourceSection: None

graalpython/com.oracle.graal.python.test/testData/goldenFiles/DictAndSetTests/dict02.tast

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ ModuleRootNode Name: <module 'dict02'> SourceSection: [0,5]`{a:b}`
55
FrameDescriptor: Empty
66
Documentation: None
77
InnerRootNode SourceSection: [0,5]`{a:b}`
8-
DictLiteralNode SourceSection: [0,5]`{a:b}`
8+
DictLiteralNodeGen SourceSection: [0,5]`{a:b}`
99
PythonObjectFactoryNodeGen SourceSection: None
1010
ReadNameNodeGen SourceSection: [1,2]`a`
1111
Identifier: a

graalpython/com.oracle.graal.python.test/testData/goldenFiles/DictAndSetTests/dict03.tast

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ ModuleRootNode Name: <module 'dict03'> SourceSection: [0,6]`{a:b,}`
55
FrameDescriptor: Empty
66
Documentation: None
77
InnerRootNode SourceSection: [0,6]`{a:b,}`
8-
DictLiteralNode SourceSection: [0,6]`{a:b,}`
8+
DictLiteralNodeGen SourceSection: [0,6]`{a:b,}`
99
PythonObjectFactoryNodeGen SourceSection: None
1010
ReadNameNodeGen SourceSection: [1,2]`a`
1111
Identifier: a

0 commit comments

Comments
 (0)