|
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;
|
|
127 | 129 | import com.oracle.graal.python.nodes.ErrorMessages;
|
128 | 130 | import com.oracle.graal.python.nodes.GraalPythonTranslationErrorNode;
|
129 | 131 | import com.oracle.graal.python.nodes.PGuards;
|
| 132 | +import com.oracle.graal.python.nodes.PNodeWithRaise; |
130 | 133 | import com.oracle.graal.python.nodes.PRaiseNode;
|
131 | 134 | import com.oracle.graal.python.nodes.PRootNode;
|
| 135 | +import com.oracle.graal.python.nodes.SpecialAttributeNames; |
132 | 136 | import com.oracle.graal.python.nodes.SpecialMethodNames;
|
133 | 137 | import com.oracle.graal.python.nodes.argument.ReadArgumentNode;
|
134 | 138 | import com.oracle.graal.python.nodes.attributes.DeleteAttributeNode;
|
135 | 139 | import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetAnyAttributeNode;
|
136 | 140 | import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetFixedAttributeNode;
|
137 | 141 | import com.oracle.graal.python.nodes.attributes.HasInheritedAttributeNode;
|
| 142 | +import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode; |
138 | 143 | import com.oracle.graal.python.nodes.attributes.LookupInheritedAttributeNode;
|
139 | 144 | import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
|
140 | 145 | import com.oracle.graal.python.nodes.attributes.SetAttributeNode;
|
141 | 146 | import com.oracle.graal.python.nodes.builtins.ListNodes;
|
142 | 147 | import com.oracle.graal.python.nodes.builtins.ListNodes.ConstructListNode;
|
143 | 148 | import com.oracle.graal.python.nodes.call.CallNode;
|
144 | 149 | import com.oracle.graal.python.nodes.call.GenericInvokeNode;
|
| 150 | +import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode; |
| 151 | +import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode; |
| 152 | +import com.oracle.graal.python.nodes.call.special.CallVarargsMethodNode; |
145 | 153 | import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
|
146 | 154 | import com.oracle.graal.python.nodes.call.special.LookupAndCallTernaryNode;
|
147 | 155 | import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
|
|
160 | 168 | import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
|
161 | 169 | import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
|
162 | 170 | import com.oracle.graal.python.nodes.function.builtins.PythonUnaryClinicBuiltinNode;
|
| 171 | +import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode; |
163 | 172 | import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider;
|
164 | 173 | import com.oracle.graal.python.nodes.object.GetClassNode;
|
165 | 174 | import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
|
| 175 | +import com.oracle.graal.python.nodes.subscript.SetItemNode; |
166 | 176 | import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
|
167 | 177 | import com.oracle.graal.python.nodes.util.CannotCastException;
|
168 | 178 | import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode;
|
|
174 | 184 | import com.oracle.graal.python.runtime.PythonParser.ParserMode;
|
175 | 185 | import com.oracle.graal.python.runtime.exception.PException;
|
176 | 186 | import com.oracle.graal.python.runtime.exception.PythonErrorType;
|
| 187 | +import com.oracle.graal.python.runtime.object.PythonObjectFactory; |
| 188 | +import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; |
177 | 189 | import com.oracle.graal.python.util.CharsetMapping;
|
178 | 190 | import com.oracle.graal.python.util.PythonUtils;
|
179 | 191 | import com.oracle.graal.python.util.Supplier;
|
|
183 | 195 | import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
184 | 196 | import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
185 | 197 | import com.oracle.truffle.api.RootCallTarget;
|
| 198 | +import com.oracle.truffle.api.TruffleLanguage.ContextReference; |
186 | 199 | import com.oracle.truffle.api.TruffleLanguage.LanguageReference;
|
187 | 200 | import com.oracle.truffle.api.debug.Debugger;
|
188 | 201 | import com.oracle.truffle.api.dsl.Cached;
|
189 | 202 | import com.oracle.truffle.api.dsl.Cached.Shared;
|
| 203 | +import com.oracle.truffle.api.dsl.CachedContext; |
190 | 204 | import com.oracle.truffle.api.dsl.Fallback;
|
191 | 205 | import com.oracle.truffle.api.dsl.GenerateNodeFactory;
|
192 | 206 | import com.oracle.truffle.api.dsl.ImportStatic;
|
@@ -1939,4 +1953,207 @@ protected ArgumentClinicProvider getArgumentClinic() {
|
1939 | 1953 | return BuiltinFunctionsClinicProviders.OpenNodeClinicProviderGen.INSTANCE;
|
1940 | 1954 | }
|
1941 | 1955 | }
|
| 1956 | + |
| 1957 | + @ImportStatic(SpecialMethodNames.class) |
| 1958 | + abstract static class UpdateBasesNode extends PNodeWithRaise { |
| 1959 | + |
| 1960 | + abstract PTuple execute(VirtualFrame frame, PTuple bases, Object[] arguments, int nargs); |
| 1961 | + |
| 1962 | + @Specialization |
| 1963 | + PTuple update(VirtualFrame frame, PTuple bases, Object[] arguments, int nargs, |
| 1964 | + @Cached PythonObjectFactory factory, |
| 1965 | + @CachedLibrary(limit = "3") InteropLibrary interop, |
| 1966 | + @Cached GetClassNode getMroClass, |
| 1967 | + @Cached(parameters = "__MRO_ENTRIES__") LookupAttributeInMRONode getMroEntries, |
| 1968 | + @Cached CallBinaryMethodNode callMroEntries) { |
| 1969 | + ArrayList<Object> newBases = null; |
| 1970 | + for (int i = 0; i < nargs; i++) { |
| 1971 | + Object base = arguments[i]; |
| 1972 | + if (PGuards.isClass(base, interop)) { |
| 1973 | + if (newBases != null) { |
| 1974 | + // If we already have made a replacement, then we append every normal base, |
| 1975 | + // otherwise just skip it. |
| 1976 | + newBases.add(base); |
| 1977 | + } |
| 1978 | + continue; |
| 1979 | + } |
| 1980 | + |
| 1981 | + Object meth = getMroEntries.execute(getMroClass.execute(base)); |
| 1982 | + if (PGuards.isNoValue(meth)) { |
| 1983 | + if (newBases != null) { |
| 1984 | + newBases.add(base); |
| 1985 | + } |
| 1986 | + continue; |
| 1987 | + } |
| 1988 | + Object newBase = callMroEntries.executeObject(frame, meth, base, bases); |
| 1989 | + if (newBase == null) { |
| 1990 | + // error |
| 1991 | + return null; |
| 1992 | + } |
| 1993 | + if (!PGuards.isPTuple(newBase)) { |
| 1994 | + throw raise(PythonErrorType.TypeError, "__mro_entries__ must return a tuple"); |
| 1995 | + } |
| 1996 | + PTuple newBaseTuple = (PTuple) newBase; |
| 1997 | + if (newBases == null) { |
| 1998 | + // If this is a first successful replacement, create new_bases list and copy |
| 1999 | + // previously encountered bases. |
| 2000 | + newBases = new ArrayList<>(); |
| 2001 | + for (int j = 0; j < i; j++) { |
| 2002 | + newBases.add(arguments[j]); |
| 2003 | + } |
| 2004 | + } |
| 2005 | + SequenceStorage storage = newBaseTuple.getSequenceStorage(); |
| 2006 | + for (int j = 0; j < storage.length(); j++) { |
| 2007 | + newBases.add(storage.getItemNormalized(j)); |
| 2008 | + } |
| 2009 | + } |
| 2010 | + if (newBases == null) { |
| 2011 | + return bases; |
| 2012 | + } |
| 2013 | + return factory.createTuple(newBases.toArray()); |
| 2014 | + } |
| 2015 | + } |
| 2016 | + |
| 2017 | + abstract static class CalculateMetaclassNode extends PNodeWithRaise { |
| 2018 | + |
| 2019 | + abstract Object execute(Object metatype, PTuple bases); |
| 2020 | + |
| 2021 | + /* Determine the most derived metatype. */ |
| 2022 | + @Specialization |
| 2023 | + Object calculate(Object metatype, PTuple bases, |
| 2024 | + @Cached GetClassNode getClass, |
| 2025 | + @Cached IsSubtypeNode isSubType, |
| 2026 | + @Cached IsSubtypeNode isSubTypeReverse) { |
| 2027 | + /* |
| 2028 | + * Determine the proper metatype to deal with this, and check for metatype conflicts |
| 2029 | + * while we're at it. Note that if some other metatype wins to contract, it's possible |
| 2030 | + * that its instances are not types. |
| 2031 | + */ |
| 2032 | + |
| 2033 | + SequenceStorage storage = bases.getSequenceStorage(); |
| 2034 | + int nbases = storage.length(); |
| 2035 | + Object winner = metatype; |
| 2036 | + for (int i = 0; i < nbases; i++) { |
| 2037 | + Object tmp = storage.getItemNormalized(i); |
| 2038 | + Object tmpType = getClass.execute(tmp); |
| 2039 | + if (isSubType.execute(winner, tmpType)) { |
| 2040 | + // nothing to do |
| 2041 | + } else if (isSubTypeReverse.execute(tmpType, winner)) { |
| 2042 | + winner = tmpType; |
| 2043 | + } else { |
| 2044 | + throw raise(PythonErrorType.TypeError, ErrorMessages.METACLASS_CONFLICT); |
| 2045 | + } |
| 2046 | + } |
| 2047 | + return winner; |
| 2048 | + } |
| 2049 | + } |
| 2050 | + |
| 2051 | + @Builtin(name = BuiltinNames.__BUILD_CLASS__, minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true) |
| 2052 | + @GenerateNodeFactory |
| 2053 | + public abstract static class BuildClassNode extends PythonVarargsBuiltinNode { |
| 2054 | + @Child private com.oracle.graal.python.nodes.call.CallNode callNode = com.oracle.graal.python.nodes.call.CallNode.create(); |
| 2055 | + |
| 2056 | + @Specialization |
| 2057 | + protected Object doItNonFunction(VirtualFrame frame, Object function, Object[] arguments, PKeyword[] keywords, |
| 2058 | + @CachedContext(PythonLanguage.class) ContextReference<PythonContext> contextRef, |
| 2059 | + @CachedLibrary(limit = "3") InteropLibrary interop, |
| 2060 | + @Cached CastToJavaStringNode castToString, |
| 2061 | + @Cached PythonObjectFactory factory, |
| 2062 | + @Cached CalculateMetaclassNode calculateMetaClass, |
| 2063 | + @Cached(parameters = "__PREPARE__") LookupAttributeInMRONode getPrepare, |
| 2064 | + @Cached GetClassNode getGetItemClass, |
| 2065 | + @Cached(parameters = "__GETITEM__") LookupAttributeInMRONode getGetItem, |
| 2066 | + @Cached CallVarargsMethodNode callPrep, |
| 2067 | + @Cached CallVarargsMethodNode callType, |
| 2068 | + @Cached CallUnaryMethodNode callBody, |
| 2069 | + @Cached UpdateBasesNode update, |
| 2070 | + @Cached SetItemNode setOrigBases, |
| 2071 | + @Cached GetClassNode getClass) { |
| 2072 | + |
| 2073 | + boolean isClass = false; |
| 2074 | + |
| 2075 | + if (arguments.length < 1) { |
| 2076 | + throw raise(PythonErrorType.TypeError, "__build_class__: not enough arguments"); |
| 2077 | + } |
| 2078 | + |
| 2079 | + if (!PGuards.isFunction(function)) { |
| 2080 | + throw raise(PythonErrorType.TypeError, "__build_class__: func must be a function"); |
| 2081 | + } |
| 2082 | + String name; |
| 2083 | + try { |
| 2084 | + name = castToString.execute(arguments[0]); |
| 2085 | + } catch (CannotCastException e) { |
| 2086 | + throw raise(PythonErrorType.TypeError, "__build_class__: name is not a string"); |
| 2087 | + } |
| 2088 | + |
| 2089 | + Object[] basesArray = Arrays.copyOfRange(arguments, 1, arguments.length); |
| 2090 | + PTuple origBases = factory.createTuple(basesArray); |
| 2091 | + |
| 2092 | + PTuple bases = update.execute(frame, origBases, basesArray, basesArray.length); |
| 2093 | + |
| 2094 | + Object meta = null; |
| 2095 | + PKeyword[] mkw = keywords; |
| 2096 | + if (keywords.length > 0) { |
| 2097 | + for (int i = 0; i < keywords.length; i++) { |
| 2098 | + if ("metaclass".equals(keywords[i].getName())) { |
| 2099 | + meta = keywords[i].getValue(); |
| 2100 | + mkw = new PKeyword[keywords.length - 1]; |
| 2101 | + System.arraycopy(keywords, 0, mkw, 0, i); |
| 2102 | + System.arraycopy(keywords, i + 1, mkw, i, mkw.length - i); |
| 2103 | + |
| 2104 | + // metaclass is explicitly given, check if it's indeed a class |
| 2105 | + isClass = PGuards.isClass(meta, interop); |
| 2106 | + break; |
| 2107 | + } |
| 2108 | + } |
| 2109 | + } |
| 2110 | + if (meta == null) { |
| 2111 | + // if there are no bases, use type: |
| 2112 | + if (bases.getSequenceStorage().length() == 0) { |
| 2113 | + meta = contextRef.get().getCore().lookupType(PythonBuiltinClassType.PythonClass); |
| 2114 | + } else { |
| 2115 | + // else get the type of the first base |
| 2116 | + meta = getClass.execute(bases.getSequenceStorage().getItemNormalized(0)); |
| 2117 | + } |
| 2118 | + isClass = true; // meta is really a class |
| 2119 | + } |
| 2120 | + |
| 2121 | + if (isClass) { |
| 2122 | + // meta is really a class, so check for a more derived metaclass, or possible |
| 2123 | + // metaclass conflicts: |
| 2124 | + meta = calculateMetaClass.execute(meta, bases); |
| 2125 | + } |
| 2126 | + // else: meta is not a class, so we cannot do the metaclass calculation, so we will |
| 2127 | + // use the explicitly given object as it is |
| 2128 | + Object prep = getPrepare.execute(meta); |
| 2129 | + Object ns; |
| 2130 | + if (PGuards.isNoValue(prep)) { |
| 2131 | + ns = factory.createDict(); |
| 2132 | + } else { |
| 2133 | + if (PGuards.isFunction(prep)) { |
| 2134 | + ns = callPrep.execute(frame, prep, new Object[]{name, bases}, mkw); |
| 2135 | + } else { |
| 2136 | + ns = callPrep.execute(frame, prep, new Object[]{meta, name, bases}, mkw); |
| 2137 | + } |
| 2138 | + } |
| 2139 | + if (PGuards.isNoValue(getGetItem.execute(getGetItemClass.execute(ns)))) { |
| 2140 | + if (isClass) { |
| 2141 | + throw raise(PythonErrorType.TypeError, "%p.__prepare__() must return a mapping, not %p", meta, ns); |
| 2142 | + } else { |
| 2143 | + throw raise(PythonErrorType.TypeError, "<metaclass>.__prepare__() must return a mapping, not %p", ns); |
| 2144 | + } |
| 2145 | + } |
| 2146 | + callBody.executeObject(frame, function, ns); |
| 2147 | + if (bases != origBases) { |
| 2148 | + setOrigBases.executeWith(frame, ns, SpecialAttributeNames.__ORIG_BASES__, origBases); |
| 2149 | + } |
| 2150 | + Object cls = callType.execute(frame, meta, new Object[]{name, bases, ns}, mkw); |
| 2151 | + |
| 2152 | + /* |
| 2153 | + * We could check here and throw "__class__ not set defining..." errors. |
| 2154 | + */ |
| 2155 | + |
| 2156 | + return cls; |
| 2157 | + } |
| 2158 | + } |
1942 | 2159 | }
|
0 commit comments