Skip to content

Commit 03f937c

Browse files
committed
avoid raising exceptions in PyObjectLookupAttr for common cases
1 parent 668d508 commit 03f937c

File tree

1 file changed

+131
-14
lines changed

1 file changed

+131
-14
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectLookupAttr.java

Lines changed: 131 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,27 @@
4242

4343
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
4444
import com.oracle.graal.python.builtins.objects.PNone;
45+
import com.oracle.graal.python.builtins.objects.function.BuiltinMethodDescriptor;
46+
import com.oracle.graal.python.builtins.objects.module.ModuleBuiltinsFactory;
47+
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory;
48+
import com.oracle.graal.python.builtins.objects.type.PythonManagedClass;
4549
import com.oracle.graal.python.builtins.objects.type.SpecialMethodSlot;
46-
import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetFixedAttributeNode;
50+
import com.oracle.graal.python.builtins.objects.type.TypeBuiltinsFactory;
51+
import com.oracle.graal.python.nodes.PGuards;
52+
import com.oracle.graal.python.nodes.SpecialMethodNames;
53+
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
54+
import com.oracle.graal.python.nodes.attributes.LookupInheritedAttributeNode;
55+
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
56+
import com.oracle.graal.python.nodes.call.CallNode;
4757
import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode;
58+
import com.oracle.graal.python.nodes.call.special.CallTernaryMethodNode;
4859
import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodSlotNode;
4960
import com.oracle.graal.python.nodes.object.GetClassNode;
5061
import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
5162
import com.oracle.graal.python.runtime.exception.PException;
63+
import com.oracle.truffle.api.dsl.Bind;
5264
import com.oracle.truffle.api.dsl.Cached;
65+
import com.oracle.truffle.api.dsl.Cached.Shared;
5366
import com.oracle.truffle.api.dsl.GenerateUncached;
5467
import com.oracle.truffle.api.dsl.ImportStatic;
5568
import com.oracle.truffle.api.dsl.Specialization;
@@ -66,31 +79,135 @@
6679
* doesn't exist.
6780
*/
6881
@GenerateUncached
69-
@ImportStatic(SpecialMethodSlot.class)
82+
@ImportStatic({SpecialMethodSlot.class, SpecialMethodNames.class, PGuards.class})
83+
7084
public abstract class PyObjectLookupAttr extends Node {
85+
private static final BuiltinMethodDescriptor OBJ_GET_ATTRIBUTE = BuiltinMethodDescriptor.get(ObjectBuiltinsFactory.GetAttributeNodeFactory.getInstance(), PythonBuiltinClassType.PythonObject);
86+
private static final BuiltinMethodDescriptor MODULE_GET_ATTRIBUTE = BuiltinMethodDescriptor.get(ModuleBuiltinsFactory.ModuleGetattritbuteNodeFactory.getInstance(), PythonBuiltinClassType.PythonModule);
87+
private static final BuiltinMethodDescriptor TYPE_GET_ATTRIBUTE = BuiltinMethodDescriptor.get(TypeBuiltinsFactory.GetattributeNodeFactory.getInstance(), PythonBuiltinClassType.PythonClass);
88+
7189
public abstract Object execute(Frame frame, Object receiver, Object name);
7290

73-
@Specialization(guards = "name == cachedName", limit = "1")
74-
static Object getFixedAttr(VirtualFrame frame, Object receiver, @SuppressWarnings("unused") String name,
75-
@SuppressWarnings("unused") @Cached("name") String cachedName,
76-
@Cached("create(name)") GetFixedAttributeNode getAttrNode,
77-
@Cached IsBuiltinClassProfile errorProfile) {
78-
try {
79-
return getAttrNode.execute(frame, receiver);
80-
} catch (PException e) {
81-
e.expect(PythonBuiltinClassType.AttributeError, errorProfile);
82-
return PNone.NO_VALUE;
91+
protected static boolean hasNoGetattr(Object lazyClass) {
92+
Object slotValue = null;
93+
if (lazyClass instanceof PythonBuiltinClassType) {
94+
slotValue = SpecialMethodSlot.GetAttr.getValue((PythonBuiltinClassType) lazyClass);
95+
} else if (lazyClass instanceof PythonManagedClass) {
96+
slotValue = SpecialMethodSlot.GetAttr.getValue((PythonManagedClass) lazyClass);
97+
}
98+
return slotValue == PNone.NO_VALUE;
99+
}
100+
101+
protected static boolean getAttributeIs(Object lazyClass, BuiltinMethodDescriptor expected) {
102+
Object slotValue = null;
103+
if (lazyClass instanceof PythonBuiltinClassType) {
104+
slotValue = SpecialMethodSlot.GetAttribute.getValue((PythonBuiltinClassType) lazyClass);
105+
} else if (lazyClass instanceof PythonManagedClass) {
106+
slotValue = SpecialMethodSlot.GetAttribute.getValue((PythonManagedClass) lazyClass);
107+
}
108+
return slotValue == expected;
109+
}
110+
111+
protected static boolean isObjectGetAttribute(Object lazyClass) {
112+
return getAttributeIs(lazyClass, OBJ_GET_ATTRIBUTE);
113+
}
114+
115+
protected static boolean isModuleGetAttribute(Object lazyClass) {
116+
return getAttributeIs(lazyClass, MODULE_GET_ATTRIBUTE);
117+
}
118+
119+
protected static boolean isTypeGetAttribute(Object lazyClass) {
120+
return getAttributeIs(lazyClass, TYPE_GET_ATTRIBUTE);
121+
}
122+
123+
// simple version that needs no calls and only reads from the object directly
124+
@SuppressWarnings("unused")
125+
@Specialization(guards = {"isObjectGetAttribute(type)", "hasNoGetattr(type)", "name == cachedName", "isNoValue(descr)"})
126+
static final Object doBuiltinObject(VirtualFrame frame, Object object, String name,
127+
@Cached("name") String cachedName,
128+
@Cached GetClassNode getClass,
129+
@Bind("getClass.execute(object)") Object type,
130+
@Cached("create(name)") LookupAttributeInMRONode lookupName,
131+
@Bind("lookupName.execute(type)") Object descr,
132+
@Cached ReadAttributeFromObjectNode readNode) {
133+
return readNode.execute(object, cachedName);
134+
}
135+
136+
// simple version that needs no calls and only reads from the object directly. the only
137+
// difference for module.__getattribute__ over object.__getattribute__ is that it looks for a
138+
// module-level __getattr__ as well
139+
@SuppressWarnings("unused")
140+
@Specialization(guards = {"isModuleGetAttribute(type)", "hasNoGetattr(type)", "name == cachedName", "isNoValue(descr)"}, limit = "1")
141+
static final Object doBuiltinModule(VirtualFrame frame, Object object, String name,
142+
@Cached("name") String cachedName,
143+
@Cached GetClassNode getClass,
144+
@Bind("getClass.execute(object)") Object type,
145+
@Cached("create(name)") LookupAttributeInMRONode lookupName,
146+
@Bind("lookupName.execute(type)") Object descr,
147+
@Cached ReadAttributeFromObjectNode readNode,
148+
@Cached ReadAttributeFromObjectNode readGetattr,
149+
@Shared("errorProfile") @Cached IsBuiltinClassProfile errorProfile,
150+
@Cached CallNode callGetattr) {
151+
Object value = readNode.execute(object, cachedName);
152+
if (value == PNone.NO_VALUE) {
153+
Object getAttr = readGetattr.execute(object, SpecialMethodNames.__GETATTR__);
154+
if (getAttr != PNone.NO_VALUE) {
155+
try {
156+
return callGetattr.execute(frame, getAttr, name);
157+
} catch (PException e) {
158+
e.expect(PythonBuiltinClassType.AttributeError, errorProfile);
159+
return PNone.NO_VALUE;
160+
}
161+
} else {
162+
return PNone.NO_VALUE;
163+
}
164+
} else {
165+
return value;
83166
}
84167
}
85168

86-
@Specialization(replaces = "getFixedAttr")
169+
// simple version that needs no calls and only reads from the object directly. the only
170+
// difference for type.__getattribute__ over object.__getattribute__ is that it looks for a
171+
// __get__ method on the value and invokes it if it is callable.
172+
@SuppressWarnings("unused")
173+
@Specialization(guards = {"isTypeGetAttribute(type)", "hasNoGetattr(type)", "name == cachedName", "isNoValue(descr)"}, limit = "1")
174+
static final Object doBuiltinType(VirtualFrame frame, Object object, String name,
175+
@Cached("name") String cachedName,
176+
@Cached GetClassNode getClass,
177+
@Bind("getClass.execute(object)") Object type,
178+
@Cached("create(name)") LookupAttributeInMRONode lookupName,
179+
@Bind("lookupName.execute(type)") Object descr,
180+
@Cached ReadAttributeFromObjectNode readNode,
181+
@Cached ReadAttributeFromObjectNode readGetattr,
182+
@Cached("create(__GET__)") LookupInheritedAttributeNode lookupValueGet,
183+
@Cached CallTernaryMethodNode invokeValueGet,
184+
@Shared("errorProfile") @Cached IsBuiltinClassProfile errorProfile,
185+
@Cached CallNode callGetattr) {
186+
Object value = readNode.execute(object, cachedName);
187+
if (value != PNone.NO_VALUE) {
188+
Object valueGet = lookupValueGet.execute(value);
189+
if (valueGet == PNone.NO_VALUE) {
190+
return value;
191+
} else if (PGuards.isCallable(valueGet)) {
192+
try {
193+
return invokeValueGet.execute(frame, valueGet, value, PNone.NONE, object);
194+
} catch (PException e) {
195+
e.expect(PythonBuiltinClassType.AttributeError, errorProfile);
196+
return PNone.NO_VALUE;
197+
}
198+
}
199+
}
200+
return PNone.NO_VALUE;
201+
}
202+
203+
@Specialization(replaces = {"doBuiltinObject", "doBuiltinModule", "doBuiltinType"})
87204
static Object getDynamicAttr(Frame frame, Object receiver, Object name,
88205
@Cached GetClassNode getClass,
89206
@Cached(parameters = "GetAttribute") LookupSpecialMethodSlotNode lookupGetattribute,
90207
@Cached(parameters = "GetAttr") LookupSpecialMethodSlotNode lookupGetattr,
91208
@Cached CallBinaryMethodNode callGetattribute,
92209
@Cached CallBinaryMethodNode callGetattr,
93-
@Cached IsBuiltinClassProfile errorProfile) {
210+
@Shared("errorProfile") @Cached IsBuiltinClassProfile errorProfile) {
94211
Object type = getClass.execute(receiver);
95212
Object getattribute = lookupGetattribute.execute(frame, type, receiver);
96213
try {

0 commit comments

Comments
 (0)