|
79 | 79 | import java.math.BigInteger;
|
80 | 80 | import java.nio.charset.Charset;
|
81 | 81 | import java.nio.charset.CodingErrorAction;
|
| 82 | +import java.util.ArrayList; |
| 83 | +import java.util.Arrays; |
82 | 84 | import java.util.List;
|
83 | 85 |
|
84 | 86 | import com.oracle.graal.python.PythonFileDetector;
|
|
121 | 123 | import com.oracle.graal.python.builtins.objects.tuple.PTuple;
|
122 | 124 | import com.oracle.graal.python.builtins.objects.type.TypeBuiltins;
|
123 | 125 | import com.oracle.graal.python.builtins.objects.type.TypeNodes;
|
| 126 | +import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsTypeNode; |
124 | 127 | import com.oracle.graal.python.lib.PyNumberAsSizeNode;
|
125 | 128 | import com.oracle.graal.python.lib.PyNumberIndexNode;
|
126 | 129 | import com.oracle.graal.python.nodes.BuiltinNames;
|
127 | 130 | import com.oracle.graal.python.nodes.ErrorMessages;
|
128 | 131 | import com.oracle.graal.python.nodes.GraalPythonTranslationErrorNode;
|
129 | 132 | import com.oracle.graal.python.nodes.PGuards;
|
| 133 | +import com.oracle.graal.python.nodes.PNodeWithRaise; |
130 | 134 | import com.oracle.graal.python.nodes.PRaiseNode;
|
131 | 135 | import com.oracle.graal.python.nodes.PRootNode;
|
| 136 | +import com.oracle.graal.python.nodes.SpecialAttributeNames; |
132 | 137 | import com.oracle.graal.python.nodes.SpecialMethodNames;
|
133 | 138 | import com.oracle.graal.python.nodes.argument.ReadArgumentNode;
|
134 | 139 | import com.oracle.graal.python.nodes.attributes.DeleteAttributeNode;
|
| 140 | +import com.oracle.graal.python.nodes.attributes.GetAttributeNode; |
135 | 141 | import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetAnyAttributeNode;
|
136 | 142 | import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetFixedAttributeNode;
|
137 | 143 | import com.oracle.graal.python.nodes.attributes.HasInheritedAttributeNode;
|
| 144 | +import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode; |
138 | 145 | import com.oracle.graal.python.nodes.attributes.LookupInheritedAttributeNode;
|
139 | 146 | import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
|
140 | 147 | import com.oracle.graal.python.nodes.attributes.SetAttributeNode;
|
141 | 148 | import com.oracle.graal.python.nodes.builtins.ListNodes;
|
142 | 149 | import com.oracle.graal.python.nodes.builtins.ListNodes.ConstructListNode;
|
143 | 150 | import com.oracle.graal.python.nodes.call.CallNode;
|
144 | 151 | 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; |
145 | 155 | import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
|
146 | 156 | import com.oracle.graal.python.nodes.call.special.LookupAndCallTernaryNode;
|
147 | 157 | import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
|
|
160 | 170 | import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
|
161 | 171 | import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
|
162 | 172 | import com.oracle.graal.python.nodes.function.builtins.PythonUnaryClinicBuiltinNode;
|
| 173 | +import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode; |
163 | 174 | import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider;
|
164 | 175 | import com.oracle.graal.python.nodes.object.GetClassNode;
|
165 | 176 | import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
|
| 177 | +import com.oracle.graal.python.nodes.subscript.SetItemNode; |
166 | 178 | import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
|
167 | 179 | import com.oracle.graal.python.nodes.util.CannotCastException;
|
168 | 180 | import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode;
|
|
174 | 186 | import com.oracle.graal.python.runtime.PythonParser.ParserMode;
|
175 | 187 | import com.oracle.graal.python.runtime.exception.PException;
|
176 | 188 | 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; |
177 | 191 | import com.oracle.graal.python.util.CharsetMapping;
|
178 | 192 | import com.oracle.graal.python.util.PythonUtils;
|
179 | 193 | import com.oracle.graal.python.util.Supplier;
|
180 | 194 | import com.oracle.truffle.api.Assumption;
|
181 | 195 | import com.oracle.truffle.api.CallTarget;
|
| 196 | +import com.oracle.truffle.api.CompilerAsserts; |
182 | 197 | import com.oracle.truffle.api.CompilerDirectives;
|
183 | 198 | import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
184 | 199 | import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
185 | 200 | import com.oracle.truffle.api.RootCallTarget;
|
| 201 | +import com.oracle.truffle.api.TruffleLanguage.Env; |
186 | 202 | import com.oracle.truffle.api.TruffleLanguage.LanguageReference;
|
187 | 203 | import com.oracle.truffle.api.debug.Debugger;
|
188 | 204 | import com.oracle.truffle.api.dsl.Cached;
|
@@ -1939,4 +1955,223 @@ protected ArgumentClinicProvider getArgumentClinic() {
|
1939 | 1955 | return BuiltinFunctionsClinicProviders.OpenNodeClinicProviderGen.INSTANCE;
|
1940 | 1956 | }
|
1941 | 1957 | }
|
| 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 | + } |
1942 | 2177 | }
|
0 commit comments