Skip to content

Commit d6910bc

Browse files
committed
[GR-28698] Replace python-based __build_class__ with Java version.
PullRequest: graalpython/1689
2 parents 2272d90 + 686062d commit d6910bc

File tree

10 files changed

+278
-193
lines changed

10 files changed

+278
-193
lines changed

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,6 @@ private static String[] initializeCoreFiles() {
273273
"code",
274274
"_io",
275275
"_frozen_importlib",
276-
"classes",
277276
"__graalpython__",
278277
"_weakref",
279278
"itertools",

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2577,22 +2577,23 @@ private static HiddenKey createTypeKey(String name) {
25772577
private void addDictDescrAttribute(PythonAbstractClass[] basesArray, PythonClass pythonClass) {
25782578
// Note: we need to avoid MRO lookup of __dict__ using slots because they are not
25792579
// initialized yet
2580-
if ((!hasPythonClassBases(basesArray) &&
2581-
LookupAttributeInMRONode.lookupSlowPath(pythonClass, __DICT__) == PNone.NO_VALUE) ||
2582-
basesHaveSlots(basesArray)) {
2583-
Builtin dictBuiltin = ObjectBuiltins.DictNode.class.getAnnotation(Builtin.class);
2580+
if ((!hasPythonClassBases(basesArray) && LookupAttributeInMRONode.lookupSlowPath(pythonClass, __DICT__) == PNone.NO_VALUE) || basesHaveSlots(basesArray)) {
25842581
RootCallTarget callTarget = PythonLanguage.getCurrent().createCachedCallTarget(
2585-
l -> new BuiltinFunctionRootNode(l, dictBuiltin, new StandaloneBuiltinFactory<PythonBinaryBuiltinNode>(DictNodeGen.create()), true), dictBuiltin,
2586-
StandaloneBuiltinFactory.class);
2582+
l -> {
2583+
Builtin dictBuiltin = ObjectBuiltins.DictNode.class.getAnnotation(Builtin.class);
2584+
return new BuiltinFunctionRootNode(l, dictBuiltin, new StandaloneBuiltinFactory<PythonBinaryBuiltinNode>(DictNodeGen.create()), true);
2585+
}, ObjectBuiltins.DictNode.class, StandaloneBuiltinFactory.class);
25872586
setAttribute(__DICT__, callTarget, pythonClass);
25882587
}
25892588
}
25902589

25912590
@TruffleBoundary
25922591
private void addWeakrefDescrAttribute(PythonClass pythonClass) {
2593-
Builtin builtin = GetWeakRefsNode.class.getAnnotation(Builtin.class);
25942592
RootCallTarget callTarget = PythonLanguage.getCurrent().createCachedCallTarget(
2595-
l -> new BuiltinFunctionRootNode(l, builtin, WeakRefModuleBuiltinsFactory.GetWeakRefsNodeFactory.getInstance(), true), builtin, WeakRefModuleBuiltinsFactory.class);
2593+
l -> {
2594+
Builtin builtin = GetWeakRefsNode.class.getAnnotation(Builtin.class);
2595+
return new BuiltinFunctionRootNode(l, builtin, WeakRefModuleBuiltinsFactory.GetWeakRefsNodeFactory.getInstance(), true);
2596+
}, GetWeakRefsNode.class, WeakRefModuleBuiltinsFactory.class);
25962597
setAttribute(__WEAKREF__, callTarget, pythonClass);
25972598
}
25982599

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

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@
7979
import java.math.BigInteger;
8080
import java.nio.charset.Charset;
8181
import java.nio.charset.CodingErrorAction;
82+
import java.util.ArrayList;
83+
import java.util.Arrays;
8284
import java.util.List;
8385

8486
import com.oracle.graal.python.PythonFileDetector;
@@ -121,27 +123,35 @@
121123
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
122124
import com.oracle.graal.python.builtins.objects.type.TypeBuiltins;
123125
import com.oracle.graal.python.builtins.objects.type.TypeNodes;
126+
import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsTypeNode;
124127
import com.oracle.graal.python.lib.PyNumberAsSizeNode;
125128
import com.oracle.graal.python.lib.PyNumberIndexNode;
126129
import com.oracle.graal.python.nodes.BuiltinNames;
127130
import com.oracle.graal.python.nodes.ErrorMessages;
128131
import com.oracle.graal.python.nodes.GraalPythonTranslationErrorNode;
129132
import com.oracle.graal.python.nodes.PGuards;
133+
import com.oracle.graal.python.nodes.PNodeWithRaise;
130134
import com.oracle.graal.python.nodes.PRaiseNode;
131135
import com.oracle.graal.python.nodes.PRootNode;
136+
import com.oracle.graal.python.nodes.SpecialAttributeNames;
132137
import com.oracle.graal.python.nodes.SpecialMethodNames;
133138
import com.oracle.graal.python.nodes.argument.ReadArgumentNode;
134139
import com.oracle.graal.python.nodes.attributes.DeleteAttributeNode;
140+
import com.oracle.graal.python.nodes.attributes.GetAttributeNode;
135141
import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetAnyAttributeNode;
136142
import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetFixedAttributeNode;
137143
import com.oracle.graal.python.nodes.attributes.HasInheritedAttributeNode;
144+
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
138145
import com.oracle.graal.python.nodes.attributes.LookupInheritedAttributeNode;
139146
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
140147
import com.oracle.graal.python.nodes.attributes.SetAttributeNode;
141148
import com.oracle.graal.python.nodes.builtins.ListNodes;
142149
import com.oracle.graal.python.nodes.builtins.ListNodes.ConstructListNode;
143150
import com.oracle.graal.python.nodes.call.CallNode;
144151
import com.oracle.graal.python.nodes.call.GenericInvokeNode;
152+
import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode;
153+
import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode;
154+
import com.oracle.graal.python.nodes.call.special.CallVarargsMethodNode;
145155
import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
146156
import com.oracle.graal.python.nodes.call.special.LookupAndCallTernaryNode;
147157
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
@@ -160,9 +170,11 @@
160170
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
161171
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
162172
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryClinicBuiltinNode;
173+
import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode;
163174
import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider;
164175
import com.oracle.graal.python.nodes.object.GetClassNode;
165176
import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
177+
import com.oracle.graal.python.nodes.subscript.SetItemNode;
166178
import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
167179
import com.oracle.graal.python.nodes.util.CannotCastException;
168180
import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode;
@@ -174,15 +186,19 @@
174186
import com.oracle.graal.python.runtime.PythonParser.ParserMode;
175187
import com.oracle.graal.python.runtime.exception.PException;
176188
import com.oracle.graal.python.runtime.exception.PythonErrorType;
189+
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
190+
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
177191
import com.oracle.graal.python.util.CharsetMapping;
178192
import com.oracle.graal.python.util.PythonUtils;
179193
import com.oracle.graal.python.util.Supplier;
180194
import com.oracle.truffle.api.Assumption;
181195
import com.oracle.truffle.api.CallTarget;
196+
import com.oracle.truffle.api.CompilerAsserts;
182197
import com.oracle.truffle.api.CompilerDirectives;
183198
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
184199
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
185200
import com.oracle.truffle.api.RootCallTarget;
201+
import com.oracle.truffle.api.TruffleLanguage.Env;
186202
import com.oracle.truffle.api.TruffleLanguage.LanguageReference;
187203
import com.oracle.truffle.api.debug.Debugger;
188204
import com.oracle.truffle.api.dsl.Cached;
@@ -1939,4 +1955,223 @@ protected ArgumentClinicProvider getArgumentClinic() {
19391955
return BuiltinFunctionsClinicProviders.OpenNodeClinicProviderGen.INSTANCE;
19401956
}
19411957
}
1958+
1959+
@ImportStatic(SpecialMethodNames.class)
1960+
abstract static class UpdateBasesNode extends PNodeWithRaise {
1961+
1962+
abstract PTuple execute(PTuple bases, Object[] arguments, int nargs);
1963+
1964+
@Specialization
1965+
PTuple update(PTuple bases, Object[] arguments, int nargs,
1966+
@Cached PythonObjectFactory factory,
1967+
@Cached GetClassNode getMroClass,
1968+
@Cached(parameters = "__MRO_ENTRIES__") LookupAttributeInMRONode getMroEntries,
1969+
@Cached CallBinaryMethodNode callMroEntries) {
1970+
CompilerAsserts.neverPartOfCompilation();
1971+
ArrayList<Object> newBases = null;
1972+
for (int i = 0; i < nargs; i++) {
1973+
Object base = arguments[i];
1974+
if (IsTypeNode.getUncached().execute(base)) {
1975+
if (newBases != null) {
1976+
// If we already have made a replacement, then we append every normal base,
1977+
// otherwise just skip it.
1978+
newBases.add(base);
1979+
}
1980+
continue;
1981+
}
1982+
1983+
Object meth = getMroEntries.execute(getMroClass.execute(base));
1984+
if (PGuards.isNoValue(meth)) {
1985+
if (newBases != null) {
1986+
newBases.add(base);
1987+
}
1988+
continue;
1989+
}
1990+
Object newBase = callMroEntries.executeObject(null, meth, base, bases);
1991+
if (newBase == null) {
1992+
// error
1993+
return null;
1994+
}
1995+
if (!PGuards.isPTuple(newBase)) {
1996+
throw raise(PythonErrorType.TypeError, "__mro_entries__ must return a tuple");
1997+
}
1998+
PTuple newBaseTuple = (PTuple) newBase;
1999+
if (newBases == null) {
2000+
// If this is a first successful replacement, create new_bases list and copy
2001+
// previously encountered bases.
2002+
newBases = new ArrayList<>();
2003+
for (int j = 0; j < i; j++) {
2004+
newBases.add(arguments[j]);
2005+
}
2006+
}
2007+
SequenceStorage storage = newBaseTuple.getSequenceStorage();
2008+
for (int j = 0; j < storage.length(); j++) {
2009+
newBases.add(storage.getItemNormalized(j));
2010+
}
2011+
}
2012+
if (newBases == null) {
2013+
return bases;
2014+
}
2015+
return factory.createTuple(newBases.toArray());
2016+
}
2017+
}
2018+
2019+
abstract static class CalculateMetaclassNode extends PNodeWithRaise {
2020+
2021+
abstract Object execute(Object metatype, PTuple bases);
2022+
2023+
/* Determine the most derived metatype. */
2024+
@Specialization
2025+
Object calculate(Object metatype, PTuple bases,
2026+
@Cached GetClassNode getClass,
2027+
@Cached IsSubtypeNode isSubType,
2028+
@Cached IsSubtypeNode isSubTypeReverse) {
2029+
CompilerAsserts.neverPartOfCompilation();
2030+
/*
2031+
* Determine the proper metatype to deal with this, and check for metatype conflicts
2032+
* while we're at it. Note that if some other metatype wins to contract, it's possible
2033+
* that its instances are not types.
2034+
*/
2035+
2036+
SequenceStorage storage = bases.getSequenceStorage();
2037+
int nbases = storage.length();
2038+
Object winner = metatype;
2039+
for (int i = 0; i < nbases; i++) {
2040+
Object tmp = storage.getItemNormalized(i);
2041+
Object tmpType = getClass.execute(tmp);
2042+
if (isSubType.execute(winner, tmpType)) {
2043+
// nothing to do
2044+
} else if (isSubTypeReverse.execute(tmpType, winner)) {
2045+
winner = tmpType;
2046+
} else {
2047+
throw raise(PythonErrorType.TypeError, ErrorMessages.METACLASS_CONFLICT);
2048+
}
2049+
}
2050+
return winner;
2051+
}
2052+
}
2053+
2054+
@Builtin(name = BuiltinNames.__BUILD_CLASS__, minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true)
2055+
@GenerateNodeFactory
2056+
public abstract static class BuildClassNode extends PythonVarargsBuiltinNode {
2057+
@TruffleBoundary
2058+
private static Object buildJavaClass(Object func, String name, Object base) {
2059+
Object module = PythonLanguage.getContext().getCore().lookupBuiltinModule(BuiltinNames.__GRAALPYTHON__);
2060+
Object buildFunction = PythonObjectLibrary.getUncached().lookupAttribute(module, null, "build_java_class");
2061+
return CallNode.getUncached().execute(buildFunction, func, name, base);
2062+
}
2063+
2064+
@Specialization
2065+
protected Object doItNonFunction(VirtualFrame frame, Object function, Object[] arguments, PKeyword[] keywords,
2066+
@Cached PythonObjectFactory factory,
2067+
@Cached CalculateMetaclassNode calculateMetaClass,
2068+
@Cached("create(__PREPARE__)") GetAttributeNode getPrepare,
2069+
@Cached(parameters = "__GETITEM__") LookupAttributeInMRONode getGetItem,
2070+
@Cached GetClassNode getGetItemClass,
2071+
@Cached CallVarargsMethodNode callPrep,
2072+
@Cached CallVarargsMethodNode callType,
2073+
@Cached CallUnaryMethodNode callBody,
2074+
@Cached UpdateBasesNode update,
2075+
@Cached SetItemNode setOrigBases,
2076+
@Cached GetClassNode getClass,
2077+
@Cached IsBuiltinClassProfile noAttributeProfile) {
2078+
2079+
if (arguments.length < 1) {
2080+
throw raise(PythonErrorType.TypeError, "__build_class__: not enough arguments");
2081+
}
2082+
2083+
if (!PGuards.isFunction(function)) {
2084+
throw raise(PythonErrorType.TypeError, "__build_class__: func must be a function");
2085+
}
2086+
String name;
2087+
try {
2088+
name = CastToJavaStringNode.getUncached().execute(arguments[0]);
2089+
} catch (CannotCastException e) {
2090+
throw raise(PythonErrorType.TypeError, "__build_class__: name is not a string");
2091+
}
2092+
2093+
Object[] basesArray = Arrays.copyOfRange(arguments, 1, arguments.length);
2094+
PTuple origBases = factory.createTuple(basesArray);
2095+
2096+
Env env = PythonLanguage.getContext().getEnv();
2097+
if (arguments.length == 2 && env.isHostObject(arguments[1]) && env.asHostObject(arguments[1]) instanceof Class<?>) {
2098+
// we want to subclass a Java class
2099+
return buildJavaClass(function, name, arguments[1]);
2100+
}
2101+
2102+
class InitializeBuildClass {
2103+
boolean isClass;
2104+
Object meta;
2105+
PKeyword[] mkw;
2106+
PTuple bases;
2107+
2108+
@TruffleBoundary
2109+
InitializeBuildClass() {
2110+
2111+
bases = update.execute(origBases, basesArray, basesArray.length);
2112+
2113+
mkw = keywords;
2114+
for (int i = 0; i < keywords.length; i++) {
2115+
if ("metaclass".equals(keywords[i].getName())) {
2116+
meta = keywords[i].getValue();
2117+
mkw = new PKeyword[keywords.length - 1];
2118+
2119+
PythonUtils.arraycopy(keywords, 0, mkw, 0, i);
2120+
PythonUtils.arraycopy(keywords, i + 1, mkw, i, mkw.length - i);
2121+
2122+
// metaclass is explicitly given, check if it's indeed a class
2123+
isClass = IsTypeNode.getUncached().execute(meta);
2124+
break;
2125+
}
2126+
}
2127+
if (meta == null) {
2128+
// if there are no bases, use type:
2129+
if (bases.getSequenceStorage().length() == 0) {
2130+
meta = PythonLanguage.getContext().getCore().lookupType(PythonBuiltinClassType.PythonClass);
2131+
} else {
2132+
// else get the type of the first base
2133+
meta = getClass.execute(bases.getSequenceStorage().getItemNormalized(0));
2134+
}
2135+
isClass = true; // meta is really a class
2136+
}
2137+
if (isClass) {
2138+
// meta is really a class, so check for a more derived metaclass, or
2139+
// possible
2140+
// metaclass conflicts:
2141+
meta = calculateMetaClass.execute(meta, bases);
2142+
}
2143+
// else: meta is not a class, so we cannot do the metaclass calculation, so we
2144+
// will use the explicitly given object as it is
2145+
}
2146+
}
2147+
InitializeBuildClass init = new InitializeBuildClass();
2148+
2149+
Object ns;
2150+
try {
2151+
Object prep = getPrepare.executeObject(frame, init.meta);
2152+
ns = callPrep.execute(frame, prep, new Object[]{name, init.bases}, init.mkw);
2153+
} catch (PException p) {
2154+
p.expectAttributeError(noAttributeProfile);
2155+
ns = factory.createDict();
2156+
}
2157+
if (PGuards.isNoValue(getGetItem.execute(getGetItemClass.execute(ns)))) {
2158+
if (init.isClass) {
2159+
throw raise(PythonErrorType.TypeError, "%N.__prepare__() must return a mapping, not %p", init.meta, ns);
2160+
} else {
2161+
throw raise(PythonErrorType.TypeError, "<metaclass>.__prepare__() must return a mapping, not %p", ns);
2162+
}
2163+
}
2164+
callBody.executeObject(frame, function, ns);
2165+
if (init.bases != origBases) {
2166+
setOrigBases.executeWith(frame, ns, SpecialAttributeNames.__ORIG_BASES__, origBases);
2167+
}
2168+
Object cls = callType.execute(frame, init.meta, new Object[]{name, init.bases, ns}, init.mkw);
2169+
2170+
/*
2171+
* We could check here and throw "__class__ not set defining..." errors.
2172+
*/
2173+
2174+
return cls;
2175+
}
2176+
}
19422177
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringUtils.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,11 @@ public static String strip(String str, String chars, StripKind stripKind) {
213213
return str.substring(i, j);
214214
}
215215

216+
@TruffleBoundary
217+
public static boolean equalsBoundary(String a, String b) {
218+
return a.equals(b);
219+
}
220+
216221
@TruffleBoundary
217222
public static boolean containsNullCharacter(String value) {
218223
return value.indexOf(0) > 0;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1360,7 +1360,12 @@ public abstract static class IsTypeNode extends Node {
13601360
public abstract boolean execute(Object obj);
13611361

13621362
@Specialization
1363-
boolean doManagedClass(@SuppressWarnings("unused") PythonManagedClass obj) {
1363+
boolean doManagedClass(@SuppressWarnings("unused") PythonClass obj) {
1364+
return true;
1365+
}
1366+
1367+
@Specialization
1368+
boolean doManagedClass(@SuppressWarnings("unused") PythonBuiltinClass obj) {
13641369
return true;
13651370
}
13661371

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/SpecialAttributeNames.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,5 @@ public abstract class SpecialAttributeNames {
8383
public static final String __ALL__ = "__all__";
8484
public static final String __FLAGS__ = "__flags__";
8585
public static final String __ABSTRACTMETHODS__ = "__abstractmethods__";
86+
public static final String __ORIG_BASES__ = "__orig_bases__";
8687
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/SpecialMethodNames.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ public abstract class SpecialMethodNames {
159159
public static final String __SETFORMAT__ = "__setformat__";
160160
public static final String __OBJCLASS__ = "__objclass__";
161161
public static final String __ISABSTRACTMETHOD__ = "__isabstractmethod__";
162+
public static final String __MRO_ENTRIES__ = "__mro_entries__";
162163
public static final String KEYS = "keys";
163164
public static final String ITEMS = "items";
164165
public static final String VALUES = "values";

0 commit comments

Comments
 (0)