51
51
import static com .oracle .graal .python .builtins .objects .cext .capi .transitions .ArgDescriptor .Py_hash_t ;
52
52
import static com .oracle .graal .python .builtins .objects .cext .capi .transitions .ArgDescriptor .Py_ssize_t ;
53
53
import static com .oracle .graal .python .builtins .objects .cext .capi .transitions .ArgDescriptor .Void ;
54
+ import static com .oracle .graal .python .builtins .objects .common .HashingStorageNodes .HashingStorageIterator ;
54
55
import static com .oracle .graal .python .nodes .ErrorMessages .BAD_ARG_TO_INTERNAL_FUNC_WAS_S_P ;
55
56
import static com .oracle .graal .python .nodes .ErrorMessages .HASH_MISMATCH ;
56
57
import static com .oracle .graal .python .nodes .ErrorMessages .OBJ_P_HAS_NO_ATTR_S ;
68
69
import com .oracle .graal .python .builtins .modules .cext .PythonCextBuiltins .PromoteBorrowedValue ;
69
70
import com .oracle .graal .python .builtins .objects .PNone ;
70
71
import com .oracle .graal .python .builtins .objects .cext .capi .PythonNativePointer ;
72
+ import com .oracle .graal .python .builtins .objects .common .EconomicMapStorage ;
71
73
import com .oracle .graal .python .builtins .objects .common .HashingCollectionNodes .SetItemNode ;
72
74
import com .oracle .graal .python .builtins .objects .common .HashingStorage ;
73
- import com .oracle .graal .python .builtins .objects .common .HashingStorageNodes ;
74
75
import com .oracle .graal .python .builtins .objects .common .HashingStorageNodes .HashingStorageCopy ;
75
76
import com .oracle .graal .python .builtins .objects .common .HashingStorageNodes .HashingStorageGetItem ;
76
77
import com .oracle .graal .python .builtins .objects .common .HashingStorageNodes .HashingStorageGetItemWithHash ;
94
95
import com .oracle .graal .python .lib .PyObjectGetAttr ;
95
96
import com .oracle .graal .python .lib .PyObjectHashNode ;
96
97
import com .oracle .graal .python .lib .PyObjectLookupAttr ;
97
- import com .oracle .graal .python .lib .PyObjectSizeNode ;
98
98
import com .oracle .graal .python .nodes .builtins .ListNodes .ConstructListNode ;
99
99
import com .oracle .graal .python .nodes .call .CallNode ;
100
- import com .oracle .graal .python .nodes .classes .IsSubtypeNode ;
101
- import com .oracle .graal .python .nodes .object .InlinedGetClassNode ;
102
100
import com .oracle .graal .python .nodes .util .CastToJavaLongExactNode ;
103
101
import com .oracle .graal .python .runtime .exception .PException ;
104
102
import com .oracle .graal .python .runtime .sequence .storage .SequenceStorage ;
108
106
import com .oracle .truffle .api .dsl .Specialization ;
109
107
import com .oracle .truffle .api .nodes .Node ;
110
108
import com .oracle .truffle .api .profiles .BranchProfile ;
109
+ import com .oracle .truffle .api .profiles .InlinedBranchProfile ;
111
110
import com .oracle .truffle .api .profiles .LoopConditionProfile ;
112
111
113
112
public final class PythonCextDictBuiltins {
@@ -124,67 +123,93 @@ Object run() {
124
123
@ CApiBuiltin (ret = PyObjectTransfer , args = {PyObject , Py_ssize_t }, call = Ignored )
125
124
abstract static class PyTruffleDict_Next extends CApiBinaryBuiltinNode {
126
125
127
- @ Specialization ( guards = "pos < size(dict, sizeNode)" , limit = "1" )
126
+ @ Specialization
128
127
Object run (PDict dict , long pos ,
129
128
@ Bind ("this" ) Node inliningTarget ,
130
- @ SuppressWarnings ("unused" ) @ Cached PyObjectSizeNode sizeNode ,
129
+ @ Cached InlinedBranchProfile needsRewriteProfile ,
130
+ @ Cached InlinedBranchProfile economicMapProfile ,
131
+ @ Cached HashingStorageLen lenNode ,
131
132
@ Cached HashingStorageGetIterator getIterator ,
132
133
@ Cached HashingStorageIteratorNext itNext ,
133
134
@ Cached HashingStorageIteratorKey itKey ,
134
135
@ Cached HashingStorageIteratorValue itValue ,
135
136
@ Cached HashingStorageIteratorKeyHash itKeyHash ,
136
137
@ Cached PromoteBorrowedValue promoteKeyNode ,
137
138
@ Cached PromoteBorrowedValue promoteValueNode ,
138
- @ Cached SetItemNode setItemNode ,
139
- @ Cached LoopConditionProfile loopProfile ) {
139
+ @ Cached HashingStorageSetItem setItem ) {
140
+ /*
141
+ * We need to promote primitive values and strings to object types for borrowing to work
142
+ * correctly. This is very hard to do mid-iteration, so we do all the promotion for the
143
+ * whole dict at once in the first call (which is required to start with position 0). In
144
+ * order to not violate the ordering, we construct a completely new storage.
145
+ */
146
+ if (pos == 0 ) {
147
+ HashingStorage storage = dict .getDictStorage ();
148
+ int len = lenNode .execute (storage );
149
+ if (len > 0 ) {
150
+ boolean needsRewrite = false ;
151
+ if (storage instanceof EconomicMapStorage ) {
152
+ economicMapProfile .enter (inliningTarget );
153
+ HashingStorageIterator it = getIterator .execute (storage );
154
+ while (itNext .execute (storage , it )) {
155
+ if (promoteKeyNode .execute (itKey .execute (storage , it )) != null || promoteValueNode .execute (itValue .execute (storage , it )) != null ) {
156
+ needsRewrite = true ;
157
+ break ;
158
+ }
159
+ }
160
+ } else {
161
+ /*
162
+ * Other storages always have string keys or have complex iterators, just
163
+ * convert them to economic map
164
+ */
165
+ needsRewrite = true ;
166
+ }
167
+ if (needsRewrite ) {
168
+ needsRewriteProfile .enter (inliningTarget );
169
+ EconomicMapStorage newStorage = EconomicMapStorage .create (len );
170
+ HashingStorageIterator it = getIterator .execute (storage );
171
+ while (itNext .execute (storage , it )) {
172
+ Object key = itKey .execute (storage , it );
173
+ Object value = itValue .execute (storage , it );
174
+ Object promotedKey = promoteKeyNode .execute (key );
175
+ if (promotedKey != null ) {
176
+ key = promotedKey ;
177
+ }
178
+ Object promotedValue = promoteValueNode .execute (value );
179
+ if (promotedValue != null ) {
180
+ value = promotedValue ;
181
+ }
182
+ setItem .execute (null , newStorage , key , value );
183
+ }
184
+ dict .setDictStorage (newStorage );
185
+ }
186
+ }
187
+ }
140
188
141
189
HashingStorage storage = dict .getDictStorage ();
142
- HashingStorageNodes .HashingStorageIterator it = getIterator .execute (storage );
143
- loopProfile .profileCounted (pos );
144
- for (int i = 0 ; loopProfile .inject (i <= pos ); i ++) {
145
- if (!itNext .execute (storage , it )) {
146
- return getNativeNull ();
147
- }
190
+ HashingStorageIterator it = getIterator .execute (storage );
191
+ /*
192
+ * The iterator index starts from -1, but pos starts from 0, so we subtract 1 here and
193
+ * add it back later when computing new pos.
194
+ */
195
+ it .setState ((int ) pos - 1 );
196
+ boolean hasNext = itNext .execute (storage , it );
197
+ if (!hasNext ) {
198
+ return getNativeNull ();
148
199
}
149
200
Object key = itKey .execute (storage , it );
150
201
Object value = itValue .execute (storage , it );
151
- Object promotedKey = promoteKeyNode .execute (key );
152
- Object promotedValue = promoteValueNode .execute (value );
153
- if (promotedKey != null ) {
154
- key = promotedKey ;
155
- // TODO: replace key with promoted value (also, re-enable
156
- // 'test_capi.py::test_dict_iteration' once fixed)
157
- }
158
- if (promotedValue != null ) {
159
- setItemNode .execute (null , inliningTarget , dict , key , value = promotedValue );
160
- }
161
- return factory ().createTuple (new Object []{key , value , itKeyHash .execute (storage , it )});
202
+ assert promoteKeyNode .execute (key ) == null ;
203
+ assert promoteValueNode .execute (value ) == null ;
204
+ long hash = itKeyHash .execute (storage , it );
205
+ int newPos = it .getState () + 1 ;
206
+ return factory ().createTuple (new Object []{key , value , hash , newPos });
162
207
}
163
208
164
- @ Specialization (guards = "isGreaterPosOrNative(inliningTarget, pos, dict, sizeNode, getClassNode, isSubtypeNode)" , limit = "1" )
165
- Object run (@ SuppressWarnings ("unused" ) Object dict , @ SuppressWarnings ("unused" ) long pos ,
166
- @ SuppressWarnings ("unused" ) @ Bind ("this" ) Node inliningTarget ,
167
- @ SuppressWarnings ("unused" ) @ Cached PyObjectSizeNode sizeNode ,
168
- @ SuppressWarnings ("unused" ) @ Cached InlinedGetClassNode getClassNode ,
169
- @ SuppressWarnings ("unused" ) @ Cached IsSubtypeNode isSubtypeNode ) {
209
+ @ Fallback
210
+ Object run (@ SuppressWarnings ("unused" ) Object dict , @ SuppressWarnings ("unused" ) Object pos ) {
170
211
return getNativeNull ();
171
212
}
172
-
173
- protected boolean isGreaterPosOrNative (Node inliningTarget , long pos , Object obj , PyObjectSizeNode sizeNode , InlinedGetClassNode getClassNode , IsSubtypeNode isSubtypeNode ) {
174
- return (isDict (obj ) && pos >= size (obj , sizeNode )) || (!isDict (obj ) && !isDictSubtype (inliningTarget , obj , getClassNode , isSubtypeNode ));
175
- }
176
-
177
- protected boolean isDict (Object obj ) {
178
- return obj instanceof PDict ;
179
- }
180
-
181
- protected int size (Object dict , PyObjectSizeNode sizeNode ) {
182
- return sizeNode .execute (null , dict );
183
- }
184
-
185
- protected boolean isDictSubtype (Node inliningTarget , Object obj , InlinedGetClassNode getClassNode , IsSubtypeNode isSubtypeNode ) {
186
- return isSubtypeNode .execute (getClassNode .execute (inliningTarget , obj ), PythonBuiltinClassType .PDict );
187
- }
188
213
}
189
214
190
215
@ CApiBuiltin (ret = PyObjectTransfer , args = {PyObject , PyObject , PyObject }, call = Direct )
@@ -465,7 +490,7 @@ static int merge(PDict a, PDict b, @SuppressWarnings("unused") int override,
465
490
@ Cached HashingStorageSetItemWithHash setAItem ,
466
491
@ Cached LoopConditionProfile loopProfile ) {
467
492
HashingStorage bStorage = b .getDictStorage ();
468
- HashingStorageNodes . HashingStorageIterator bIt = getBIter .execute (bStorage );
493
+ HashingStorageIterator bIt = getBIter .execute (bStorage );
469
494
HashingStorage aStorage = a .getDictStorage ();
470
495
while (loopProfile .profile (itBNext .execute (bStorage , bIt ))) {
471
496
Object key = itBKey .execute (bStorage , bIt );
0 commit comments