|
40 | 40 | */ |
41 | 41 | package com.oracle.graal.python.processor; |
42 | 42 |
|
43 | | -import static java.lang.String.format; |
44 | | - |
45 | 43 | import java.util.Arrays; |
| 44 | +import java.util.Collections; |
46 | 45 | import java.util.HashSet; |
47 | 46 | import java.util.List; |
48 | 47 | import java.util.Set; |
|
55 | 54 | import com.oracle.graal.python.annotations.ArgumentClinic.PrimitiveType; |
56 | 55 |
|
57 | 56 | public class ArgumentClinicModel { |
58 | | - static final class BuiltinConvertor { |
59 | | - public static final String CLINIC_PACKAGE = "com.oracle.graal.python.nodes.function.builtins.clinic"; |
60 | | - |
61 | | - public static String getCodeSnippet(ArgumentClinic annotation, BuiltinAnnotation builtin) { |
62 | | - switch (annotation.conversion()) { |
63 | | - case Boolean: |
64 | | - return format("JavaBooleanConvertorNodeGen.create(%s)", annotation.defaultValue()); |
65 | | - case String: |
66 | | - String defaultVal = annotation.defaultValue(); |
67 | | - if (defaultVal.isEmpty()) { |
68 | | - return format("JavaStringConvertorNodeGen.create(\"%s\")", builtin.name); |
69 | | - } else { |
70 | | - return format("JavaStringConvertorWithDefaultValueNodeGen.create(\"%s\", %s, %s)", builtin.name, defaultVal, annotation.useDefaultForNone()); |
71 | | - } |
72 | | - case Int: |
73 | | - return format("JavaIntConversionNodeGen.create(%s, %s)", annotation.defaultValue(), annotation.useDefaultForNone()); |
74 | | - case CodePoint: |
75 | | - return format("CodePointConversionNodeGen.create(\"%s\", %s, %s)", builtin.name, annotation.defaultValue(), annotation.useDefaultForNone()); |
76 | | - case Buffer: |
77 | | - return "BufferConversionNodeGen.create()"; |
78 | | - case Index: |
79 | | - return format("IndexConversionNodeGen.create(%s, %s)", annotation.defaultValue(), annotation.useDefaultForNone()); |
80 | | - case None: |
81 | | - return format("DefaultValueNode.create(%s, %s)", annotation.defaultValue(), annotation.useDefaultForNone()); |
82 | | - default: |
83 | | - throw new IllegalArgumentException(annotation.conversion().toString()); |
84 | | - } |
85 | | - } |
86 | | - |
87 | | - public static void addImports(ArgumentClinic annotation, Set<String> imports) { |
88 | | - // We may add imports for some other prominent constants |
89 | | - // Another possibility is to introduce something like ArgumentClinicImportStatic |
90 | | - // annotation, or piggy-back on Truffle DSL's ImportStatic annotation, but then we can |
91 | | - // also use fully qualified names in such rare cases |
92 | | - if (annotation.defaultValue().startsWith("PNone.")) { |
93 | | - imports.add("com.oracle.graal.python.builtins.objects.PNone"); |
94 | | - } |
95 | | - if (annotation.conversion() != ClinicConversion.None || (annotation.customConversion().isEmpty() && !annotation.defaultValue().isEmpty())) { |
96 | | - imports.add(CLINIC_PACKAGE + '.' + getConvertorImport(annotation)); |
97 | | - } |
98 | | - } |
99 | | - |
100 | | - private static String getConvertorImport(ArgumentClinic annotation) { |
101 | | - switch (annotation.conversion()) { |
102 | | - case Boolean: |
103 | | - return "JavaBooleanConvertorNodeGen"; |
104 | | - case String: |
105 | | - return annotation.defaultValue().isEmpty() ? "JavaStringConvertorNodeGen" : "JavaStringConvertorWithDefaultValueNodeGen"; |
106 | | - case Int: |
107 | | - return "JavaIntConversionNodeGen"; |
108 | | - case Index: |
109 | | - return "IndexConversionNodeGen"; |
110 | | - case CodePoint: |
111 | | - return "CodePointConversionNodeGen"; |
112 | | - case Buffer: |
113 | | - return "BufferConversionNodeGen"; |
114 | | - case None: |
115 | | - return "DefaultValueNode"; |
116 | | - default: |
117 | | - throw new IllegalArgumentException(annotation.conversion().toString()); |
118 | | - } |
119 | | - } |
120 | | - |
121 | | - public static PrimitiveType[] getAcceptedPrimitiveTypes(ArgumentClinic annotation) { |
122 | | - switch (annotation.conversion()) { |
123 | | - case Boolean: |
124 | | - return new PrimitiveType[]{PrimitiveType.Boolean}; |
125 | | - case String: |
126 | | - case CodePoint: |
127 | | - case Buffer: |
128 | | - return new PrimitiveType[0]; |
129 | | - case Int: |
130 | | - case Index: |
131 | | - return new PrimitiveType[]{PrimitiveType.Int}; |
132 | | - case None: |
133 | | - return new PrimitiveType[]{PrimitiveType.Boolean, PrimitiveType.Int, PrimitiveType.Long, PrimitiveType.Double}; |
134 | | - default: |
135 | | - throw new IllegalArgumentException(annotation.conversion().toString()); |
136 | | - } |
137 | | - } |
138 | | - } |
139 | 57 |
|
140 | 58 | /** |
141 | 59 | * Mirrors the data of the {@code Builtin} annotation, which cannot be in the "annotations" |
142 | 60 | * project because of its dependence on other GraalPython runtime classes. |
143 | 61 | */ |
144 | | - static final class BuiltinAnnotation { |
| 62 | + public static final class BuiltinAnnotation { |
145 | 63 | public final String name; |
146 | 64 | public final String[] argumentNames; |
147 | 65 |
|
@@ -188,38 +106,73 @@ static final class ArgumentClinicData { |
188 | 106 | public final int index; |
189 | 107 | public final Set<PrimitiveType> acceptedPrimitiveTypes; |
190 | 108 | public final String castNodeFactory; |
| 109 | + public final Set<String> imports; |
191 | 110 |
|
192 | | - public ArgumentClinicData(ArgumentClinic annotation, int index, Set<PrimitiveType> acceptedPrimitiveTypes, String castNodeFactory) { |
| 111 | + private ArgumentClinicData(ArgumentClinic annotation, int index, Set<PrimitiveType> acceptedPrimitiveTypes, String castNodeFactory, Set<String> imports) { |
193 | 112 | this.annotation = annotation; |
194 | 113 | this.index = index; |
195 | 114 | this.acceptedPrimitiveTypes = acceptedPrimitiveTypes; |
196 | 115 | this.castNodeFactory = castNodeFactory; |
| 116 | + this.imports = imports; |
197 | 117 | } |
198 | 118 |
|
199 | | - public boolean isClinicArgument() { |
200 | | - return annotation != null; |
| 119 | + private static ConverterFactory getFactory(ArgumentClinic annotation, TypeElement type, ConverterFactory factory) throws ProcessingError { |
| 120 | + if (factory == null && annotation.args().length != 0) { |
| 121 | + throw new ProcessingError(type, "No conversionClass specified but arguments were provided"); |
| 122 | + } |
| 123 | + if (factory != null) { |
| 124 | + return factory; |
| 125 | + } |
| 126 | + if (annotation.conversion() == ClinicConversion.None && annotation.defaultValue().isEmpty()) { |
| 127 | + throw new ProcessingError(type, "ArgumentClinic annotation must declare either builtin conversion or custom conversion."); |
| 128 | + } |
| 129 | + return ConverterFactory.getBuiltin(annotation); |
201 | 130 | } |
202 | 131 |
|
203 | | - public static ArgumentClinicData create(ArgumentClinic annotation, TypeElement type, BuiltinAnnotation builtinAnnotation, int index) throws ProcessingError { |
| 132 | + public static ArgumentClinicData create(ArgumentClinic annotation, TypeElement type, BuiltinAnnotation builtinAnnotation, int index, ConverterFactory annotationFactory) |
| 133 | + throws ProcessingError { |
204 | 134 | if (annotation == null) { |
205 | | - return new ArgumentClinicData(null, index, new HashSet<>(Arrays.asList(PrimitiveType.values())), null); |
| 135 | + return new ArgumentClinicData(null, index, new HashSet<>(Arrays.asList(PrimitiveType.values())), null, Collections.emptySet()); |
| 136 | + } |
| 137 | + ConverterFactory factory = getFactory(annotation, type, annotationFactory); |
| 138 | + if (annotation.args().length != factory.extraParamCount) { |
| 139 | + throw new ProcessingError(type, "Conversion %s.%s expects %d arguments", factory.fullClassName, factory.methodName, factory.extraParamCount); |
206 | 140 | } |
207 | 141 |
|
208 | | - PrimitiveType[] acceptedPrimitives = new PrimitiveType[0]; |
209 | | - String castNodeFactory; |
210 | | - if (annotation.customConversion().isEmpty()) { |
211 | | - if (annotation.conversion() == ClinicConversion.None && annotation.defaultValue().isEmpty()) { |
212 | | - throw new ProcessingError(type, "ArgumentClinic annotation must declare either builtin conversion or custom conversion."); |
| 142 | + String[] args = new String[factory.params.length]; |
| 143 | + int extraParamIndex = 0; |
| 144 | + for (int i = 0; i < args.length; ++i) { |
| 145 | + switch (factory.params[i]) { |
| 146 | + case BuiltinName: |
| 147 | + args[i] = String.format("\"%s\"", builtinAnnotation.name); |
| 148 | + break; |
| 149 | + case ArgumentIndex: |
| 150 | + args[i] = String.valueOf(index); |
| 151 | + break; |
| 152 | + case ArgumentName: |
| 153 | + args[i] = String.format("\"%s\"", builtinAnnotation.argumentNames[index]); |
| 154 | + break; |
| 155 | + case DefaultValue: |
| 156 | + args[i] = annotation.defaultValue(); |
| 157 | + break; |
| 158 | + case UseDefaultForNone: |
| 159 | + args[i] = String.valueOf(annotation.useDefaultForNone()); |
| 160 | + break; |
| 161 | + case Extra: |
| 162 | + args[i] = annotation.args()[extraParamIndex++]; |
| 163 | + break; |
| 164 | + default: |
| 165 | + throw new IllegalStateException("Unsupported ClinicArgument: " + factory.params[i]); |
213 | 166 | } |
214 | | - castNodeFactory = BuiltinConvertor.getCodeSnippet(annotation, builtinAnnotation); |
215 | | - acceptedPrimitives = BuiltinConvertor.getAcceptedPrimitiveTypes(annotation); |
216 | | - } else { |
217 | | - castNodeFactory = type.getQualifiedName().toString() + '.' + annotation.customConversion() + "()"; |
218 | 167 | } |
219 | | - if (annotation.shortCircuitPrimitive().length > 0) { |
220 | | - acceptedPrimitives = annotation.shortCircuitPrimitive(); |
| 168 | + String castNodeFactory = String.format("%s.%s(%s)", factory.className, factory.methodName, String.join(", ", args)); |
| 169 | + Set<String> imports = new HashSet<>(); |
| 170 | + imports.add(factory.fullClassName); |
| 171 | + if (annotation.defaultValue().startsWith("PNone.")) { |
| 172 | + imports.add("com.oracle.graal.python.builtins.objects.PNone"); |
221 | 173 | } |
222 | | - return new ArgumentClinicData(annotation, index, new HashSet<>(Arrays.asList(acceptedPrimitives)), castNodeFactory); |
| 174 | + |
| 175 | + return new ArgumentClinicData(annotation, index, new HashSet<>(Arrays.asList(factory.acceptedPrimitiveTypes)), castNodeFactory, imports); |
223 | 176 | } |
224 | 177 | } |
225 | 178 | } |
0 commit comments