Skip to content

Commit 58007ca

Browse files
committed
Don't fake a dict in thread local
1 parent 2009e34 commit 58007ca

File tree

3 files changed

+357
-50
lines changed

3 files changed

+357
-50
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/PThreadLocal.java

Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,11 @@
4040
*/
4141
package com.oracle.graal.python.builtins.objects.thread;
4242

43-
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
4443
import com.oracle.graal.python.builtins.objects.dict.PDict;
4544
import com.oracle.graal.python.builtins.objects.function.PKeyword;
4645
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
47-
import com.oracle.graal.python.builtins.objects.object.PythonObjectLibrary;
48-
import com.oracle.graal.python.nodes.ErrorMessages;
49-
import com.oracle.graal.python.nodes.PRaiseNode;
50-
import com.oracle.graal.python.nodes.SpecialMethodNames;
51-
import com.oracle.graal.python.nodes.call.CallNode;
52-
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
53-
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
54-
import com.oracle.truffle.api.dsl.Cached;
55-
import com.oracle.truffle.api.dsl.Cached.Shared;
56-
import com.oracle.truffle.api.library.CachedLibrary;
57-
import com.oracle.truffle.api.library.ExportLibrary;
58-
import com.oracle.truffle.api.library.ExportMessage;
5946
import com.oracle.truffle.api.object.Shape;
6047

61-
@ExportLibrary(PythonObjectLibrary.class)
6248
public final class PThreadLocal extends PythonBuiltinObject {
6349
private final ThreadLocal<PDict> threadLocalDict;
6450
private final Object[] args;
@@ -71,36 +57,15 @@ public PThreadLocal(Object cls, Shape instanceShape, Object[] args, PKeyword[] k
7157
this.keywords = keywords;
7258
}
7359

74-
@ExportMessage
75-
@SuppressWarnings("static-method")
76-
boolean hasDict() {
77-
return true;
60+
public ThreadLocal<PDict> getThreadLocalDict() {
61+
return threadLocalDict;
7862
}
7963

80-
@ExportMessage
81-
@TruffleBoundary
82-
PDict getDict(@CachedLibrary("this") PythonObjectLibrary lib,
83-
@Cached CallNode callNode,
84-
@Cached PythonObjectFactory factory) {
85-
PDict dict = threadLocalDict.get();
86-
if (dict == null) {
87-
dict = factory.createDict();
88-
threadLocalDict.set(dict);
89-
Object initMethod = lib.lookupAttribute(this, null, SpecialMethodNames.__INIT__);
90-
callNode.execute(initMethod, args, keywords);
91-
}
92-
return dict;
64+
public Object[] getArgs() {
65+
return args;
9366
}
9467

95-
@ExportMessage
96-
@SuppressWarnings({"static-method", "unused"})
97-
void setDict(PDict value, @Shared("raise") @Cached PRaiseNode raise) {
98-
throw raise.raise(PythonBuiltinClassType.AttributeError, ErrorMessages.ATTR_S_READONLY, "__dict__");
99-
}
100-
101-
@ExportMessage
102-
@SuppressWarnings("static-method")
103-
void deleteDict(@Shared("raise") @Cached PRaiseNode raise) {
104-
throw raise.raise(PythonBuiltinClassType.AttributeError, ErrorMessages.ATTR_S_READONLY, "__dict__");
68+
public PKeyword[] getKeywords() {
69+
return keywords;
10570
}
10671
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java

Lines changed: 278 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,47 @@
4040
*/
4141
package com.oracle.graal.python.builtins.objects.thread;
4242

43+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__DICT__;
44+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__DELATTR__;
45+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GETATTRIBUTE__;
46+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__INIT__;
47+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__SETATTR__;
48+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.AttributeError;
49+
4350
import java.util.List;
4451

4552
import com.oracle.graal.python.builtins.Builtin;
4653
import com.oracle.graal.python.builtins.CoreFunctions;
4754
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
4855
import com.oracle.graal.python.builtins.PythonBuiltins;
4956
import com.oracle.graal.python.builtins.objects.PNone;
57+
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
58+
import com.oracle.graal.python.builtins.objects.common.HashingStorageLibrary;
5059
import com.oracle.graal.python.builtins.objects.dict.PDict;
51-
import com.oracle.graal.python.builtins.objects.object.PythonObjectLibrary;
52-
import com.oracle.graal.python.nodes.SpecialAttributeNames;
53-
import com.oracle.graal.python.nodes.SpecialMethodNames;
60+
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins;
61+
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory;
62+
import com.oracle.graal.python.builtins.objects.type.SpecialMethodSlot;
63+
import com.oracle.graal.python.nodes.ErrorMessages;
64+
import com.oracle.graal.python.nodes.PGuards;
65+
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
66+
import com.oracle.graal.python.nodes.attributes.LookupCallableSlotInMRONode;
67+
import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode;
68+
import com.oracle.graal.python.nodes.call.special.CallTernaryMethodNode;
5469
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
70+
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
71+
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
5572
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
73+
import com.oracle.graal.python.nodes.object.GetClassNode;
74+
import com.oracle.graal.python.nodes.util.CannotCastException;
75+
import com.oracle.graal.python.nodes.util.CastToJavaStringNode;
76+
import com.oracle.truffle.api.CompilerDirectives;
77+
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
78+
import com.oracle.truffle.api.dsl.Cached;
5679
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
80+
import com.oracle.truffle.api.dsl.ImportStatic;
5781
import com.oracle.truffle.api.dsl.NodeFactory;
5882
import com.oracle.truffle.api.dsl.Specialization;
83+
import com.oracle.truffle.api.frame.VirtualFrame;
5984
import com.oracle.truffle.api.library.CachedLibrary;
6085

6186
@CoreFunctions(extendClasses = PythonBuiltinClassType.PThreadLocal)
@@ -65,7 +90,7 @@ protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFa
6590
return ThreadLocalBuiltinsFactory.getFactories();
6691
}
6792

68-
@Builtin(name = SpecialMethodNames.__INIT__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 1)
93+
@Builtin(name = __INIT__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 1)
6994
@GenerateNodeFactory
7095
abstract static class InitNode extends PythonUnaryBuiltinNode {
7196
@Specialization
@@ -74,13 +99,257 @@ PNone repr(@SuppressWarnings("unused") PThreadLocal self) {
7499
}
75100
}
76101

77-
@Builtin(name = SpecialAttributeNames.__DICT__, minNumOfPositionalArgs = 1, isGetter = true)
102+
@Builtin(name = __DICT__, minNumOfPositionalArgs = 1, isGetter = true)
78103
@GenerateNodeFactory
79104
abstract static class DictNode extends PythonUnaryBuiltinNode {
80-
@Specialization(limit = "1")
81-
PDict repr(PThreadLocal self,
82-
@CachedLibrary("self") PythonObjectLibrary lib) {
83-
return lib.getDict(self);
105+
@Specialization
106+
PDict repr(VirtualFrame frame, PThreadLocal self,
107+
@Cached ThreadLocalNodes.GetThreadLocalDict getThreadLocalDict) {
108+
return getThreadLocalDict.execute(frame, self);
109+
}
110+
}
111+
112+
@ImportStatic(PGuards.class)
113+
@Builtin(name = __GETATTRIBUTE__, minNumOfPositionalArgs = 2)
114+
@GenerateNodeFactory
115+
public abstract static class GetAttributeNode extends PythonBinaryBuiltinNode {
116+
@Child private LookupCallableSlotInMRONode lookupGetNode;
117+
@Child private LookupCallableSlotInMRONode lookupSetNode;
118+
@Child private LookupCallableSlotInMRONode lookupDeleteNode;
119+
@Child private CallTernaryMethodNode dispatchGet;
120+
@Child private HashingStorageLibrary hlib;
121+
@Child private GetClassNode getDescClassNode;
122+
123+
@Specialization
124+
protected Object doIt(VirtualFrame frame, PThreadLocal object, Object keyObj,
125+
@Cached ThreadLocalNodes.GetThreadLocalDict getThreadLocalDict,
126+
@Cached LookupAttributeInMRONode.Dynamic lookup,
127+
@Cached GetClassNode getClassNode,
128+
@Cached CastToJavaStringNode castKeyToStringNode) {
129+
PDict localDict = getThreadLocalDict.execute(frame, object);
130+
String key;
131+
try {
132+
key = castKeyToStringNode.execute(keyObj);
133+
} catch (CannotCastException e) {
134+
throw raise(PythonBuiltinClassType.TypeError, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj);
135+
}
136+
137+
Object type = getClassNode.execute(object);
138+
Object descr = lookup.execute(type, key);
139+
Object dataDescClass = null;
140+
boolean hasDescr = descr != PNone.NO_VALUE;
141+
if (hasDescr) {
142+
dataDescClass = getDescClass(descr);
143+
Object delete = PNone.NO_VALUE;
144+
Object set = lookupSet(dataDescClass);
145+
if (set == PNone.NO_VALUE) {
146+
delete = lookupDelete(dataDescClass);
147+
}
148+
if (set != PNone.NO_VALUE || delete != PNone.NO_VALUE) {
149+
Object get = lookupGet(dataDescClass);
150+
if (PGuards.isCallableOrDescriptor(get)) {
151+
// Only override if __get__ is defined, too, for compatibility with CPython.
152+
return dispatch(frame, object, type, descr, get);
153+
}
154+
}
155+
}
156+
Object value = readAttribute(localDict, key);
157+
if (value != null) {
158+
return value;
159+
}
160+
if (hasDescr) {
161+
Object get = lookupGet(dataDescClass);
162+
if (get == PNone.NO_VALUE) {
163+
return descr;
164+
} else if (PGuards.isCallableOrDescriptor(get)) {
165+
return dispatch(frame, object, type, descr, get);
166+
}
167+
}
168+
throw raise(AttributeError, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key);
169+
}
170+
171+
private Object readAttribute(PDict object, Object key) {
172+
if (hlib == null) {
173+
CompilerDirectives.transferToInterpreterAndInvalidate();
174+
hlib = insert(HashingStorageLibrary.getFactory().createDispatched(3));
175+
}
176+
return hlib.getItem(object.getDictStorage(), key);
177+
}
178+
179+
private Object dispatch(VirtualFrame frame, Object object, Object type, Object descr, Object get) {
180+
if (dispatchGet == null) {
181+
CompilerDirectives.transferToInterpreterAndInvalidate();
182+
dispatchGet = insert(CallTernaryMethodNode.create());
183+
}
184+
return dispatchGet.execute(frame, get, descr, object, type);
185+
}
186+
187+
private Object getDescClass(Object desc) {
188+
if (getDescClassNode == null) {
189+
CompilerDirectives.transferToInterpreterAndInvalidate();
190+
getDescClassNode = insert(GetClassNode.create());
191+
}
192+
return getDescClassNode.execute(desc);
193+
}
194+
195+
private Object lookupGet(Object dataDescClass) {
196+
if (lookupGetNode == null) {
197+
CompilerDirectives.transferToInterpreterAndInvalidate();
198+
lookupGetNode = insert(LookupCallableSlotInMRONode.create(SpecialMethodSlot.Get));
199+
}
200+
return lookupGetNode.execute(dataDescClass);
201+
}
202+
203+
private Object lookupDelete(Object dataDescClass) {
204+
if (lookupDeleteNode == null) {
205+
CompilerDirectives.transferToInterpreterAndInvalidate();
206+
lookupDeleteNode = insert(LookupCallableSlotInMRONode.create(SpecialMethodSlot.Delete));
207+
}
208+
return lookupDeleteNode.execute(dataDescClass);
209+
}
210+
211+
private Object lookupSet(Object dataDescClass) {
212+
if (lookupSetNode == null) {
213+
CompilerDirectives.transferToInterpreterAndInvalidate();
214+
lookupSetNode = insert(LookupCallableSlotInMRONode.create(SpecialMethodSlot.Set));
215+
}
216+
return lookupSetNode.execute(dataDescClass);
217+
}
218+
219+
public static ObjectBuiltins.GetAttributeNode create() {
220+
return ObjectBuiltinsFactory.GetAttributeNodeFactory.create();
221+
}
222+
}
223+
224+
@ImportStatic(PGuards.class)
225+
@Builtin(name = __SETATTR__, minNumOfPositionalArgs = 3)
226+
@GenerateNodeFactory
227+
public abstract static class SetattrNode extends PythonTernaryBuiltinNode {
228+
@Child private GetClassNode getDescClassNode;
229+
@Child private LookupCallableSlotInMRONode lookupSetNode;
230+
@Child private CallTernaryMethodNode callSetNode;
231+
@Child private HashingStorageLibrary hlib;
232+
@CompilationFinal private boolean changedStorage;
233+
234+
public abstract PNone execute(VirtualFrame frame, Object object, String key, Object value);
235+
236+
@Specialization
237+
protected PNone doStringKey(VirtualFrame frame, PThreadLocal object, String keyObject, Object value,
238+
@Cached CastToJavaStringNode castKeyToStringNode,
239+
@Cached ThreadLocalNodes.GetThreadLocalDict getThreadLocalDict,
240+
@Cached GetClassNode getClassNode,
241+
@Cached LookupAttributeInMRONode.Dynamic getExisting) {
242+
String key;
243+
try {
244+
key = castKeyToStringNode.execute(keyObject);
245+
} catch (CannotCastException e) {
246+
throw raise(PythonBuiltinClassType.TypeError, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObject);
247+
}
248+
PDict localDict = getThreadLocalDict.execute(frame, object);
249+
Object type = getClassNode.execute(object);
250+
Object descr = getExisting.execute(type, key);
251+
if (descr != PNone.NO_VALUE) {
252+
Object dataDescClass = getDescClass(descr);
253+
Object set = ensureLookupSetNode().execute(dataDescClass);
254+
if (PGuards.isCallableOrDescriptor(set)) {
255+
ensureCallSetNode().execute(frame, set, descr, object, value);
256+
return PNone.NONE;
257+
}
258+
}
259+
writeAttribute(localDict, key, value);
260+
return PNone.NONE;
261+
}
262+
263+
private Object getDescClass(Object desc) {
264+
if (getDescClassNode == null) {
265+
CompilerDirectives.transferToInterpreterAndInvalidate();
266+
getDescClassNode = insert(GetClassNode.create());
267+
}
268+
return getDescClassNode.execute(desc);
269+
}
270+
271+
private LookupCallableSlotInMRONode ensureLookupSetNode() {
272+
if (lookupSetNode == null) {
273+
CompilerDirectives.transferToInterpreterAndInvalidate();
274+
lookupSetNode = insert(LookupCallableSlotInMRONode.create(SpecialMethodSlot.Set));
275+
}
276+
return lookupSetNode;
277+
}
278+
279+
private CallTernaryMethodNode ensureCallSetNode() {
280+
if (callSetNode == null) {
281+
CompilerDirectives.transferToInterpreterAndInvalidate();
282+
callSetNode = insert(CallTernaryMethodNode.create());
283+
}
284+
return callSetNode;
285+
}
286+
287+
private void writeAttribute(PDict dict, Object key, Object value) {
288+
if (hlib == null) {
289+
hlib = insert(HashingStorageLibrary.getFactory().createDispatched(3));
290+
}
291+
HashingStorage storage = dict.getDictStorage();
292+
HashingStorage newStorage = hlib.setItem(storage, key, value);
293+
if (storage != newStorage) {
294+
if (!changedStorage) {
295+
CompilerDirectives.transferToInterpreterAndInvalidate();
296+
changedStorage = true;
297+
}
298+
dict.setDictStorage(newStorage);
299+
}
300+
}
301+
}
302+
303+
@Builtin(name = __DELATTR__, minNumOfPositionalArgs = 2)
304+
@GenerateNodeFactory
305+
public abstract static class DelattrNode extends PythonBinaryBuiltinNode {
306+
@Child private GetClassNode getDescClassNode;
307+
308+
@Specialization
309+
protected PNone doIt(VirtualFrame frame, PThreadLocal object, Object keyObj,
310+
@Cached ThreadLocalNodes.GetThreadLocalDict getThreadLocalDict,
311+
@Cached LookupAttributeInMRONode.Dynamic getExisting,
312+
@Cached GetClassNode getClassNode,
313+
@Cached("create(__DELETE__)") LookupAttributeInMRONode lookupDeleteNode,
314+
@Cached CallBinaryMethodNode callDelete,
315+
@CachedLibrary(limit = "3") HashingStorageLibrary hlib,
316+
@Cached CastToJavaStringNode castKeyToStringNode) {
317+
String key;
318+
try {
319+
key = castKeyToStringNode.execute(keyObj);
320+
} catch (CannotCastException e) {
321+
throw raise(PythonBuiltinClassType.TypeError, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj);
322+
}
323+
PDict localDict = getThreadLocalDict.execute(frame, object);
324+
Object type = getClassNode.execute(object);
325+
Object descr = getExisting.execute(type, key);
326+
if (descr != PNone.NO_VALUE) {
327+
Object dataDescClass = getDescClass(descr);
328+
Object delete = lookupDeleteNode.execute(dataDescClass);
329+
if (PGuards.isCallable(delete)) {
330+
callDelete.executeObject(frame, delete, descr, object);
331+
return PNone.NONE;
332+
}
333+
}
334+
Object currentValue = hlib.getItem(localDict.getDictStorage(), key);
335+
if (currentValue != null) {
336+
HashingStorage storage = hlib.delItem(localDict.getDictStorage(), key);
337+
localDict.setDictStorage(storage);
338+
return PNone.NONE;
339+
}
340+
if (descr != PNone.NO_VALUE) {
341+
throw raise(AttributeError, ErrorMessages.ATTR_S_READONLY, key);
342+
} else {
343+
throw raise(AttributeError, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key);
344+
}
345+
}
346+
347+
private Object getDescClass(Object desc) {
348+
if (getDescClassNode == null) {
349+
CompilerDirectives.transferToInterpreterAndInvalidate();
350+
getDescClassNode = insert(GetClassNode.create());
351+
}
352+
return getDescClassNode.execute(desc);
84353
}
85354
}
86355
}

0 commit comments

Comments
 (0)