Skip to content

Commit ad47974

Browse files
committed
[GR-31276] Intrinsify parts of importlib
PullRequest: graalpython/1824
2 parents 9a7ecd8 + bf7330e commit ad47974

File tree

16 files changed

+797
-204
lines changed

16 files changed

+797
-204
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
def foo():
41+
from os.path import dirname
42+
43+
44+
def bar():
45+
import enum
46+
47+
48+
def baz():
49+
import test.support
50+
import xml.dom.minidom
51+
52+
53+
def measure(num):
54+
for i in range(num):
55+
import sys
56+
foo()
57+
bar()
58+
baz()
59+
60+
61+
def __benchmark__(num=5):
62+
measure(num)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@
186186
import com.oracle.graal.python.builtins.objects.method.ClassmethodBuiltins;
187187
import com.oracle.graal.python.builtins.objects.method.DecoratedMethodBuiltins;
188188
import com.oracle.graal.python.builtins.objects.method.MethodBuiltins;
189+
import com.oracle.graal.python.builtins.objects.method.PMethod;
189190
import com.oracle.graal.python.builtins.objects.method.StaticmethodBuiltins;
190191
import com.oracle.graal.python.builtins.objects.mmap.MMapBuiltins;
191192
import com.oracle.graal.python.builtins.objects.module.ModuleBuiltins;
@@ -547,6 +548,8 @@ private static PythonBuiltins[] initializeBuiltins(boolean nativeAccessAllowed)
547548
@CompilationFinal private PythonModule builtinsModule;
548549
@CompilationFinal private PythonModule sysModule;
549550
@CompilationFinal private PDict sysModules;
551+
@CompilationFinal private PMethod importFunc;
552+
@CompilationFinal private PythonModule importlib;
550553

551554
@CompilationFinal private PInt pyTrue;
552555
@CompilationFinal private PInt pyFalse;
@@ -666,6 +669,28 @@ public PDict getSysModules() {
666669
return sysModules;
667670
}
668671

672+
public PythonModule getImportlib() {
673+
return importlib;
674+
}
675+
676+
public void registerImportlib(PythonModule mod) {
677+
if (importlib != null) {
678+
throw new IllegalStateException("importlib cannot be registered more than once");
679+
}
680+
importlib = mod;
681+
}
682+
683+
public PMethod getImportFunc() {
684+
return importFunc;
685+
}
686+
687+
public void registerImportFunc(PMethod func) {
688+
if (importFunc != null) {
689+
throw new IllegalStateException("__import__ func cannot be registered more than once");
690+
}
691+
importFunc = func;
692+
}
693+
669694
@Override
670695
@TruffleBoundary
671696
public PException raise(PythonBuiltinClassType type, String format, Object... args) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,4 +708,25 @@ String doit(VirtualFrame frame, @CachedContext(PythonLanguage.class) PythonConte
708708
}
709709
}
710710

711+
@Builtin(name = "register_import_func", minNumOfPositionalArgs = 1)
712+
@GenerateNodeFactory
713+
public abstract static class RegisterImportFunc extends PythonUnaryBuiltinNode {
714+
@Specialization
715+
Object doit(PMethod func,
716+
@CachedContext(PythonLanguage.class) PythonContext ctx) {
717+
ctx.getCore().registerImportFunc(func);
718+
return PNone.NONE;
719+
}
720+
}
721+
722+
@Builtin(name = "register_importlib", minNumOfPositionalArgs = 1)
723+
@GenerateNodeFactory
724+
public abstract static class RegisterImportlib extends PythonUnaryBuiltinNode {
725+
@Specialization
726+
Object doit(PythonModule lib,
727+
@CachedContext(PythonLanguage.class) PythonContext ctx) {
728+
ctx.getCore().registerImportlib(lib);
729+
return PNone.NONE;
730+
}
731+
}
711732
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WarningsModuleBuiltins.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ private PDict getSysDict() {
296296
reportPolymorphicSpecialize();
297297
getDictNode = insert(GetDictNode.create());
298298
}
299-
return (PDict) getDictNode.execute(getContext().getCore().lookupBuiltinModule("sys"));
299+
return getDictNode.execute(getContext().getCore().lookupBuiltinModule("sys"));
300300
}
301301

302302
private Object getGlobalsDict(Object globals) {

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,15 @@
6262
public abstract class PyDictGetItem extends Node {
6363
public abstract Object execute(Frame frame, PDict dict, Object item);
6464

65+
// We never need a frame for reading string keys
6566
@Specialization(limit = "3")
67+
static final Object getString(@SuppressWarnings("unused") PDict dict, String item,
68+
@Bind("dict.getDictStorage()") HashingStorage dictStorage,
69+
@CachedLibrary("dictStorage") HashingStorageLibrary lib) {
70+
return lib.getItem(dictStorage, item);
71+
}
72+
73+
@Specialization(replaces = "getString", limit = "3")
6674
static final Object getItemCached(VirtualFrame frame, @SuppressWarnings("unused") PDict dict, Object item,
6775
@Bind("dict.getDictStorage()") HashingStorage dictStorage,
6876
@Cached ConditionProfile frameCondition,

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,8 @@ public abstract class PyFrameGetBuiltins extends Node {
6060
static PythonModule getBuiltins(@CachedContext(PythonLanguage.class) PythonContext context) {
6161
return context.getCore().getBuiltins();
6262
}
63+
64+
public static PyFrameGetBuiltins getUncached() {
65+
return PyFrameGetBuiltinsNodeGen.getUncached();
66+
}
6367
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ static Object getDynamicAttr(Frame frame, Object receiver, Object name,
8888
e.expect(PythonBuiltinClassType.AttributeError, errorProfile);
8989
Object getattr = lookupGetattr.execute(frame, type, receiver);
9090
if (getattr != PNone.NO_VALUE) {
91-
return callGetattr.executeObject(frame, getattr, name);
91+
return callGetattr.executeObject(frame, getattr, receiver, name);
9292
} else {
9393
throw e;
9494
}

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

Lines changed: 136 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,34 @@
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;
5669
import com.oracle.truffle.api.frame.Frame;
5770
import com.oracle.truffle.api.frame.VirtualFrame;
5871
import 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
@@ -66,31 +80,138 @@
6680
* doesn't exist.
6781
*/
6882
@GenerateUncached
69-
@ImportStatic(SpecialMethodSlot.class)
83+
@ImportStatic({SpecialMethodSlot.class, SpecialMethodNames.class, PGuards.class})
84+
7085
public 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

Comments
 (0)