4242
4343import com .oracle .graal .python .builtins .PythonBuiltinClassType ;
4444import 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 ;
4549import 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 ;
4757import com .oracle .graal .python .nodes .call .special .CallBinaryMethodNode ;
58+ import com .oracle .graal .python .nodes .call .special .CallTernaryMethodNode ;
4859import com .oracle .graal .python .nodes .call .special .LookupSpecialMethodSlotNode ;
4960import com .oracle .graal .python .nodes .object .GetClassNode ;
5061import com .oracle .graal .python .nodes .object .IsBuiltinClassProfile ;
5162import com .oracle .graal .python .runtime .exception .PException ;
63+ import com .oracle .truffle .api .dsl .Bind ;
5264import com .oracle .truffle .api .dsl .Cached ;
65+ import com .oracle .truffle .api .dsl .Cached .Shared ;
5366import com .oracle .truffle .api .dsl .GenerateUncached ;
5467import com .oracle .truffle .api .dsl .ImportStatic ;
5568import com .oracle .truffle .api .dsl .Specialization ;
5669import com .oracle .truffle .api .frame .Frame ;
5770import com .oracle .truffle .api .frame .VirtualFrame ;
5871import com .oracle .truffle .api .nodes .Node ;
72+ import com .oracle .truffle .api .profiles .ConditionProfile ;
5973
6074/**
6175 * Equivalent to use for the various PyObject_LookupAttr* functions available in CPython. Note that
6680 * doesn't exist.
6781 */
6882@ GenerateUncached
69- @ ImportStatic (SpecialMethodSlot .class )
83+ @ ImportStatic ({SpecialMethodSlot .class , SpecialMethodNames .class , PGuards .class })
84+
7085public abstract class PyObjectLookupAttr extends Node {
86+ private static final BuiltinMethodDescriptor OBJ_GET_ATTRIBUTE = BuiltinMethodDescriptor .get (ObjectBuiltinsFactory .GetAttributeNodeFactory .getInstance (), PythonBuiltinClassType .PythonObject );
87+ private static final BuiltinMethodDescriptor MODULE_GET_ATTRIBUTE = BuiltinMethodDescriptor .get (ModuleBuiltinsFactory .ModuleGetattritbuteNodeFactory .getInstance (),
88+ PythonBuiltinClassType .PythonModule );
89+ private static final BuiltinMethodDescriptor TYPE_GET_ATTRIBUTE = BuiltinMethodDescriptor .get (TypeBuiltinsFactory .GetattributeNodeFactory .getInstance (), PythonBuiltinClassType .PythonClass );
90+
7191 public abstract Object execute (Frame frame , Object receiver , Object name );
7292
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 ;
93+ protected static boolean hasNoGetattr (Object lazyClass ) {
94+ Object slotValue = null ;
95+ if (lazyClass instanceof PythonBuiltinClassType ) {
96+ slotValue = SpecialMethodSlot .GetAttr .getValue ((PythonBuiltinClassType ) lazyClass );
97+ } else if (lazyClass instanceof PythonManagedClass ) {
98+ slotValue = SpecialMethodSlot .GetAttr .getValue ((PythonManagedClass ) lazyClass );
99+ }
100+ return slotValue == PNone .NO_VALUE ;
101+ }
102+
103+ protected static boolean getAttributeIs (Object lazyClass , BuiltinMethodDescriptor expected ) {
104+ Object slotValue = null ;
105+ if (lazyClass instanceof PythonBuiltinClassType ) {
106+ slotValue = SpecialMethodSlot .GetAttribute .getValue ((PythonBuiltinClassType ) lazyClass );
107+ } else if (lazyClass instanceof PythonManagedClass ) {
108+ slotValue = SpecialMethodSlot .GetAttribute .getValue ((PythonManagedClass ) lazyClass );
109+ }
110+ return slotValue == expected ;
111+ }
112+
113+ protected static boolean isObjectGetAttribute (Object lazyClass ) {
114+ return getAttributeIs (lazyClass , OBJ_GET_ATTRIBUTE );
115+ }
116+
117+ protected static boolean isModuleGetAttribute (Object lazyClass ) {
118+ return getAttributeIs (lazyClass , MODULE_GET_ATTRIBUTE );
119+ }
120+
121+ protected static boolean isTypeGetAttribute (Object lazyClass ) {
122+ return getAttributeIs (lazyClass , TYPE_GET_ATTRIBUTE );
123+ }
124+
125+ // simple version that needs no calls and only reads from the object directly
126+ @ SuppressWarnings ("unused" )
127+ @ Specialization (guards = {"isObjectGetAttribute(type)" , "hasNoGetattr(type)" , "name == cachedName" , "isNoValue(descr)" })
128+ static final Object doBuiltinObject (VirtualFrame frame , Object object , String name ,
129+ @ Cached ("name" ) String cachedName ,
130+ @ Cached GetClassNode getClass ,
131+ @ Bind ("getClass.execute(object)" ) Object type ,
132+ @ Cached ("create(name)" ) LookupAttributeInMRONode lookupName ,
133+ @ Bind ("lookupName.execute(type)" ) Object descr ,
134+ @ Cached ReadAttributeFromObjectNode readNode ) {
135+ return readNode .execute (object , cachedName );
136+ }
137+
138+ // simple version that needs no calls and only reads from the object directly. the only
139+ // difference for module.__getattribute__ over object.__getattribute__ is that it looks for a
140+ // module-level __getattr__ as well
141+ @ SuppressWarnings ("unused" )
142+ @ Specialization (guards = {"isModuleGetAttribute(type)" , "hasNoGetattr(type)" , "name == cachedName" , "isNoValue(descr)" }, limit = "1" )
143+ static final Object doBuiltinModule (VirtualFrame frame , Object object , String name ,
144+ @ Cached ("name" ) String cachedName ,
145+ @ Cached GetClassNode getClass ,
146+ @ Bind ("getClass.execute(object)" ) Object type ,
147+ @ Cached ("create(name)" ) LookupAttributeInMRONode lookupName ,
148+ @ Bind ("lookupName.execute(type)" ) Object descr ,
149+ @ Cached ReadAttributeFromObjectNode readNode ,
150+ @ Cached ReadAttributeFromObjectNode readGetattr ,
151+ @ Shared ("errorProfile" ) @ Cached IsBuiltinClassProfile errorProfile ,
152+ @ Cached ConditionProfile noValueFound ,
153+ @ Cached CallNode callGetattr ) {
154+ Object value = readNode .execute (object , cachedName );
155+ if (noValueFound .profile (value == PNone .NO_VALUE )) {
156+ Object getAttr = readGetattr .execute (object , SpecialMethodNames .__GETATTR__ );
157+ if (getAttr != PNone .NO_VALUE ) {
158+ // (tfel): I'm not profiling this, since modules with __getattr__ are kind of rare
159+ try {
160+ return callGetattr .execute (frame , getAttr , name );
161+ } catch (PException e ) {
162+ e .expect (PythonBuiltinClassType .AttributeError , errorProfile );
163+ return PNone .NO_VALUE ;
164+ }
165+ } else {
166+ return PNone .NO_VALUE ;
167+ }
168+ } else {
169+ return value ;
83170 }
84171 }
85172
86- @ Specialization (replaces = "getFixedAttr" )
173+ // simple version that needs no calls and only reads from the object directly. the only
174+ // difference for type.__getattribute__ over object.__getattribute__ is that it looks for a
175+ // __get__ method on the value and invokes it if it is callable.
176+ @ SuppressWarnings ("unused" )
177+ @ Specialization (guards = {"isTypeGetAttribute(type)" , "hasNoGetattr(type)" , "name == cachedName" , "isNoValue(descr)" }, limit = "1" )
178+ static final Object doBuiltinType (VirtualFrame frame , Object object , String name ,
179+ @ Cached ("name" ) String cachedName ,
180+ @ Cached GetClassNode getClass ,
181+ @ Bind ("getClass.execute(object)" ) Object type ,
182+ @ Cached ("create(name)" ) LookupAttributeInMRONode lookupName ,
183+ @ Bind ("lookupName.execute(type)" ) Object descr ,
184+ @ Cached ReadAttributeFromObjectNode readNode ,
185+ @ Cached ConditionProfile valueFound ,
186+ @ Cached ("create(__GET__)" ) LookupInheritedAttributeNode lookupValueGet ,
187+ @ Cached ConditionProfile noGetMethod ,
188+ @ Cached CallTernaryMethodNode invokeValueGet ,
189+ @ Shared ("errorProfile" ) @ Cached IsBuiltinClassProfile errorProfile ) {
190+ Object value = readNode .execute (object , cachedName );
191+ if (valueFound .profile (value != PNone .NO_VALUE )) {
192+ Object valueGet = lookupValueGet .execute (value );
193+ if (noGetMethod .profile (valueGet == PNone .NO_VALUE )) {
194+ return value ;
195+ } else if (PGuards .isCallable (valueGet )) {
196+ try {
197+ return invokeValueGet .execute (frame , valueGet , value , PNone .NONE , object );
198+ } catch (PException e ) {
199+ e .expect (PythonBuiltinClassType .AttributeError , errorProfile );
200+ return PNone .NO_VALUE ;
201+ }
202+ }
203+ }
204+ return PNone .NO_VALUE ;
205+ }
206+
207+ @ Specialization (replaces = {"doBuiltinObject" , "doBuiltinModule" , "doBuiltinType" })
87208 static Object getDynamicAttr (Frame frame , Object receiver , Object name ,
88209 @ Cached GetClassNode getClass ,
89210 @ Cached (parameters = "GetAttribute" ) LookupSpecialMethodSlotNode lookupGetattribute ,
90211 @ Cached (parameters = "GetAttr" ) LookupSpecialMethodSlotNode lookupGetattr ,
91212 @ Cached CallBinaryMethodNode callGetattribute ,
92213 @ Cached CallBinaryMethodNode callGetattr ,
93- @ Cached IsBuiltinClassProfile errorProfile ) {
214+ @ Shared ( "errorProfile" ) @ Cached IsBuiltinClassProfile errorProfile ) {
94215 Object type = getClass .execute (receiver );
95216 Object getattribute = lookupGetattribute .execute (frame , type , receiver );
96217 try {
@@ -101,7 +222,7 @@ static Object getDynamicAttr(Frame frame, Object receiver, Object name,
101222 Object getattr = lookupGetattr .execute (frame , type , receiver );
102223 if (getattr != PNone .NO_VALUE ) {
103224 try {
104- return callGetattr .executeObject (frame , getattr , name );
225+ return callGetattr .executeObject (frame , getattr , receiver , name );
105226 } catch (PException e ) {
106227 e .expect (PythonBuiltinClassType .AttributeError , errorProfile );
107228 }
0 commit comments