Skip to content

Commit 36fd368

Browse files
committed
Fix: Add super class inheritance checks in PyObjectLookupAttrNode.
Previously the specializations for `BuiltinType` and `BuiltinTypeType` only checked for inheritance via metaclasses / excluded the possibility of inheritance via metaclasses. As classes can also inherit attributes from their superclasses this needed be handled, too.
1 parent f69a982 commit 36fd368

File tree

2 files changed

+97
-12
lines changed

2 files changed

+97
-12
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (c) 2019, 2021, 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+
42+
package com.oracle.graal.python.nodes.util;
43+
44+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
45+
import com.oracle.graal.python.builtins.objects.PNone;
46+
import com.oracle.graal.python.lib.PyObjectLookupAttr;
47+
import com.oracle.truffle.api.frame.VirtualFrame;
48+
import com.oracle.truffle.api.nodes.Node;
49+
import com.oracle.truffle.api.nodes.RootNode;
50+
import org.junit.After;
51+
import org.junit.Assert;
52+
import org.junit.Before;
53+
import org.junit.Test;
54+
55+
import com.oracle.graal.python.test.PythonTests;
56+
57+
public class PyObjectLookupAttrTests {
58+
59+
@Before
60+
public void setUp() {
61+
PythonTests.enterContext();
62+
}
63+
64+
@After
65+
public void tearDown() {
66+
PythonTests.closeContext();
67+
}
68+
69+
// Regression test to ensure that super class attributes are evaluated for BuiltinClassType
70+
// attribute lookup.
71+
// Booleans super class PInt defines "real" which we expect to find in an attribute lookup.
72+
@Test
73+
public void lookupThroughMRO() {
74+
Object v = new RootNode(null) {
75+
@Node.Child private PyObjectLookupAttr lookupNode = PyObjectLookupAttr.create();
76+
77+
@Override
78+
public Object execute(VirtualFrame frame) {
79+
return lookupNode.execute(frame, PythonBuiltinClassType.Boolean, "real");
80+
}
81+
}.getCallTarget().call();
82+
Assert.assertNotSame(PNone.NO_VALUE, v);
83+
}
84+
}

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

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -174,15 +174,16 @@ static final Object doBuiltinModule(VirtualFrame frame, Object object, String na
174174
}
175175
}
176176

177-
// simple version that needs no calls and only reads from the object directly. the only
178-
// difference for type.__getattribute__ over object.__getattribute__ is that it looks for a
179-
// __get__ method on the value and invokes it if it is callable.
177+
// If the class of an object is "type", the object must be a class and as "type" is the base
178+
// metaclass, which defines only certain type slots, it can not have inherited other
179+
// attributes via metaclass inheritance. For all non-type-slot attributes it therefore
180+
// suffices to only check for inheritance via super classes.
180181
@SuppressWarnings("unused")
181182
@Specialization(guards = {"isTypeGetAttribute(type)", "isBuiltinTypeType(type)", "!isTypeSlot(name)"}, limit = "1")
182183
static final Object doBuiltinTypeType(VirtualFrame frame, Object object, String name,
183184
@Cached GetClassNode getClass,
184185
@Bind("getClass.execute(object)") Object type,
185-
@Cached ReadAttributeFromObjectNode readNode,
186+
@Cached LookupAttributeInMRONode.Dynamic readNode,
186187
@Cached ConditionProfile valueFound,
187188
@Cached("create(Get)") LookupInheritedSlotNode lookupValueGet,
188189
@Cached ConditionProfile noGetMethod,
@@ -205,24 +206,24 @@ static final Object doBuiltinTypeType(VirtualFrame frame, Object object, String
205206
return PNone.NO_VALUE;
206207
}
207208

208-
// simple version that needs no calls and only reads from the object directly. the only
209-
// difference for type.__getattribute__ over object.__getattribute__ is that it looks for a
210-
// __get__ method on the value and invokes it if it is callable.
209+
// simple version that only reads attributes from (super) class inheritance and the object
210+
// itself. the only difference for type.__getattribute__ over object.__getattribute__
211+
// is that it looks for a __get__ method on the value and invokes it if it is callable.
211212
@SuppressWarnings("unused")
212-
@Specialization(guards = {"isTypeGetAttribute(type)", "hasNoGetAttr(type)", "name == cachedName", "isNoValue(descr)"}, limit = "1", replaces = "doBuiltinTypeType")
213+
@Specialization(guards = {"isTypeGetAttribute(type)", "hasNoGetAttr(type)", "name == cachedName", "isNoValue(metaClassDescr)"}, replaces = "doBuiltinTypeType", limit = "1")
213214
static final Object doBuiltinType(VirtualFrame frame, Object object, String name,
214215
@Cached("name") String cachedName,
215216
@Cached GetClassNode getClass,
216217
@Bind("getClass.execute(object)") Object type,
217-
@Cached("create(name)") LookupAttributeInMRONode lookupName,
218-
@Bind("lookupName.execute(type)") Object descr,
219-
@Cached ReadAttributeFromObjectNode readNode,
218+
@Cached("create(name)") LookupAttributeInMRONode lookupInMetaclassHierachy,
219+
@Bind("lookupInMetaclassHierachy.execute(type)") Object metaClassDescr,
220+
@Cached("create(name)") LookupAttributeInMRONode readNode,
220221
@Cached ConditionProfile valueFound,
221222
@Cached("create(Get)") LookupInheritedSlotNode lookupValueGet,
222223
@Cached ConditionProfile noGetMethod,
223224
@Cached CallTernaryMethodNode invokeValueGet,
224225
@Shared("errorProfile") @Cached IsBuiltinClassProfile errorProfile) {
225-
Object value = readNode.execute(object, cachedName);
226+
Object value = readNode.execute(object);
226227
if (valueFound.profile(value != PNone.NO_VALUE)) {
227228
Object valueGet = lookupValueGet.execute(value);
228229
if (noGetMethod.profile(valueGet == PNone.NO_VALUE)) {

0 commit comments

Comments
 (0)