comparison graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/TemplateMethodParser.java @ 16759:23415229349b

Truffle-DSL: new package structure.
author Christian Humer <christian.humer@gmail.com>
date Mon, 11 Aug 2014 15:57:14 +0200
parents graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/TemplateMethodParser.java@bd28da642eea
children
comparison
equal deleted inserted replaced
16758:c5f8eeb3cbc8 16759:23415229349b
1 /*
2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package com.oracle.truffle.dsl.processor.model;
24
25 import static com.oracle.truffle.dsl.processor.java.ElementUtils.*;
26
27 import java.lang.annotation.*;
28 import java.util.*;
29
30 import javax.lang.model.element.*;
31 import javax.lang.model.type.*;
32 import javax.lang.model.util.*;
33
34 import com.oracle.truffle.dsl.processor.*;
35 import com.oracle.truffle.dsl.processor.java.*;
36
37 public abstract class TemplateMethodParser<T extends Template, E extends TemplateMethod> {
38
39 private final ProcessorContext context;
40
41 protected final T template;
42
43 private boolean emitErrors = true;
44 private boolean parseNullOnError = false;
45 private boolean useVarArgs = false;
46
47 public TemplateMethodParser(ProcessorContext context, T template) {
48 this.template = template;
49 this.context = context;
50 }
51
52 protected void setUseVarArgs(boolean useVarArgs) {
53 this.useVarArgs = useVarArgs;
54 }
55
56 public boolean isUseVarArgs() {
57 return useVarArgs;
58 }
59
60 public boolean isEmitErrors() {
61 return emitErrors;
62 }
63
64 public void setParseNullOnError(boolean nullOnError) {
65 this.parseNullOnError = nullOnError;
66 }
67
68 public boolean isParseNullOnError() {
69 return parseNullOnError;
70 }
71
72 public void setEmitErrors(boolean emitErrors) {
73 this.emitErrors = emitErrors;
74 }
75
76 public ProcessorContext getContext() {
77 return context;
78 }
79
80 public TypeSystemData getTypeSystem() {
81 return template.getTypeSystem();
82 }
83
84 public abstract MethodSpec createSpecification(ExecutableElement method, AnnotationMirror mirror);
85
86 public abstract E create(TemplateMethod method, boolean invalid);
87
88 public abstract boolean isParsable(ExecutableElement method);
89
90 public Class<? extends Annotation> getAnnotationType() {
91 return null;
92 }
93
94 public final List<E> parse(List<? extends Element> elements) {
95 List<ExecutableElement> methods = new ArrayList<>();
96 methods.addAll(ElementFilter.methodsIn(elements));
97
98 List<E> parsedMethods = new ArrayList<>();
99 boolean valid = true;
100 int naturalOrder = 0;
101 for (ExecutableElement method : methods) {
102 if (!isParsable(method)) {
103 continue;
104 }
105
106 Class<? extends Annotation> annotationType = getAnnotationType();
107 AnnotationMirror mirror = null;
108 if (annotationType != null) {
109 mirror = ElementUtils.findAnnotationMirror(getContext().getEnvironment(), method, annotationType);
110 }
111
112 E parsedMethod = parse(naturalOrder, method, mirror);
113
114 if (method.getModifiers().contains(Modifier.PRIVATE) && emitErrors) {
115 parsedMethod.addError("Method annotated with @%s must not be private.", getAnnotationType().getSimpleName());
116 parsedMethods.add(parsedMethod);
117 valid = false;
118 continue;
119 }
120
121 if (parsedMethod != null) {
122 parsedMethods.add(parsedMethod);
123 } else {
124 valid = false;
125 }
126 naturalOrder++;
127 }
128 Collections.sort(parsedMethods);
129
130 if (!valid && parseNullOnError) {
131 return null;
132 }
133 return parsedMethods;
134 }
135
136 private E parse(int naturalOrder, ExecutableElement method, AnnotationMirror annotation) {
137 MethodSpec methodSpecification = createSpecification(method, annotation);
138 if (methodSpecification == null) {
139 return null;
140 }
141
142 methodSpecification.applyTypeDefinitions("types");
143
144 String id = method.getSimpleName().toString();
145 TypeMirror returnType = method.getReturnType();
146 List<TypeMirror> parameterTypes = new ArrayList<>();
147 for (VariableElement var : method.getParameters()) {
148 parameterTypes.add(var.asType());
149 }
150
151 return parseImpl(methodSpecification, naturalOrder, id, method, annotation, returnType, parameterTypes);
152 }
153
154 private E parseImpl(MethodSpec methodSpecification, int naturalOrder, String id, ExecutableElement method, AnnotationMirror annotation, TypeMirror returnType, List<TypeMirror> parameterTypes) {
155 ParameterSpec returnTypeSpec = methodSpecification.getReturnType();
156 Parameter returnTypeMirror = matchParameter(returnTypeSpec, returnType, -1, -1);
157 if (returnTypeMirror == null) {
158 if (emitErrors) {
159 E invalidMethod = create(new TemplateMethod(id, naturalOrder, template, methodSpecification, method, annotation, returnTypeMirror, Collections.<Parameter> emptyList()), true);
160 String expectedReturnType = returnTypeSpec.toSignatureString(true);
161 String actualReturnType = ElementUtils.getSimpleName(returnType);
162
163 String message = String.format("The provided return type \"%s\" does not match expected return type \"%s\".\nExpected signature: \n %s", actualReturnType, expectedReturnType,
164 methodSpecification.toSignatureString(method.getSimpleName().toString()));
165 invalidMethod.addError(message);
166 return invalidMethod;
167 } else {
168 return null;
169 }
170 }
171
172 List<Parameter> parameters = parseParameters(methodSpecification, parameterTypes, isUseVarArgs() && method != null ? method.isVarArgs() : false);
173 if (parameters == null) {
174 if (isEmitErrors() && method != null) {
175 E invalidMethod = create(new TemplateMethod(id, naturalOrder, template, methodSpecification, method, annotation, returnTypeMirror, Collections.<Parameter> emptyList()), true);
176 String message = String.format("Method signature %s does not match to the expected signature: \n%s", createActualSignature(method),
177 methodSpecification.toSignatureString(method.getSimpleName().toString()));
178 invalidMethod.addError(message);
179 return invalidMethod;
180 } else {
181 return null;
182 }
183 }
184
185 return create(new TemplateMethod(id, naturalOrder, template, methodSpecification, method, annotation, returnTypeMirror, parameters), false);
186 }
187
188 private static String createActualSignature(ExecutableElement method) {
189 StringBuilder b = new StringBuilder("(");
190 String sep = "";
191 if (method != null) {
192 for (VariableElement var : method.getParameters()) {
193 b.append(sep);
194 b.append(ElementUtils.getSimpleName(var.asType()));
195 sep = ", ";
196 }
197 }
198 b.append(")");
199 return b.toString();
200 }
201
202 /*
203 * Parameter parsing tries to parse required arguments starting from offset 0 with increasing
204 * offset until it finds a signature end that matches the required specification. If there is no
205 * end matching the required arguments, parsing fails. Parameters prior to the parsed required
206 * ones are cut and used to parse the optional parameters.
207 */
208 private List<Parameter> parseParameters(MethodSpec spec, List<TypeMirror> parameterTypes, boolean varArgs) {
209 List<Parameter> parsedRequired = null;
210 int offset = 0;
211 for (; offset <= parameterTypes.size(); offset++) {
212 List<TypeMirror> parameters = new ArrayList<>();
213 parameters.addAll(parameterTypes.subList(offset, parameterTypes.size()));
214 parsedRequired = parseParametersRequired(spec, parameters, varArgs);
215 if (parsedRequired != null) {
216 break;
217 }
218 }
219
220 if (parsedRequired == null) {
221 return null;
222 }
223
224 if (parsedRequired.isEmpty() && offset == 0) {
225 offset = parameterTypes.size();
226 }
227 List<TypeMirror> potentialOptionals = parameterTypes.subList(0, offset);
228 List<Parameter> parsedOptionals = parseParametersOptional(spec, potentialOptionals);
229 if (parsedOptionals == null) {
230 return null;
231 }
232
233 List<Parameter> finalParameters = new ArrayList<>();
234 finalParameters.addAll(parsedOptionals);
235 finalParameters.addAll(parsedRequired);
236 return finalParameters;
237 }
238
239 private List<Parameter> parseParametersOptional(MethodSpec spec, List<TypeMirror> types) {
240 List<Parameter> parsedParams = new ArrayList<>();
241
242 int typeStartIndex = 0;
243 List<ParameterSpec> specifications = spec.getOptional();
244 outer: for (int specIndex = 0; specIndex < specifications.size(); specIndex++) {
245 ParameterSpec specification = specifications.get(specIndex);
246 for (int typeIndex = typeStartIndex; typeIndex < types.size(); typeIndex++) {
247 TypeMirror actualType = types.get(typeIndex);
248 Parameter optionalParam = matchParameter(specification, actualType, -1, -1);
249 if (optionalParam != null) {
250 parsedParams.add(optionalParam);
251 typeStartIndex = typeIndex + 1;
252 continue outer;
253 }
254 }
255 }
256
257 if (typeStartIndex < types.size()) {
258 // not enough types found
259 return null;
260 }
261 return parsedParams;
262 }
263
264 private List<Parameter> parseParametersRequired(MethodSpec spec, List<TypeMirror> types, boolean typeVarArgs) {
265 List<Parameter> parsedParams = new ArrayList<>();
266 List<ParameterSpec> specifications = spec.getRequired();
267 boolean specVarArgs = spec.isVariableRequiredParameters();
268 int typeIndex = 0;
269 int specificationIndex = 0;
270
271 ParameterSpec specification;
272 while ((specification = nextSpecification(specifications, specificationIndex, specVarArgs)) != null) {
273 TypeMirror actualType = nextActualType(types, typeIndex, typeVarArgs);
274 if (actualType == null) {
275 if (spec.isIgnoreAdditionalSpecifications()) {
276 break;
277 }
278 return null;
279 }
280
281 int typeVarArgsIndex = typeVarArgs ? typeIndex - types.size() + 1 : -1;
282 int specVarArgsIndex = specVarArgs ? specificationIndex - specifications.size() + 1 : -1;
283
284 if (typeVarArgsIndex >= 0 && specVarArgsIndex >= 0) {
285 // both specifications and types have a variable number of arguments
286 // we would get into an endless loop if we would continue
287 break;
288 }
289
290 Parameter resolvedParameter = matchParameter(specification, actualType, specVarArgsIndex, typeVarArgsIndex);
291 if (resolvedParameter == null) {
292 return null;
293 }
294 parsedParams.add(resolvedParameter);
295 typeIndex++;
296 specificationIndex++;
297 }
298
299 if (typeIndex < types.size()) {
300 // additional types available
301 if (spec.isIgnoreAdditionalParameters()) {
302 return parsedParams;
303 } else {
304 return null;
305 }
306 }
307
308 return parsedParams;
309 }
310
311 private static ParameterSpec nextSpecification(List<ParameterSpec> specifications, int specIndex, boolean varArgs) {
312 if (varArgs && specIndex >= specifications.size() - 1 && !specifications.isEmpty()) {
313 return specifications.get(specifications.size() - 1);
314 } else if (specIndex < specifications.size()) {
315 return specifications.get(specIndex);
316 } else {
317 return null;
318 }
319 }
320
321 private static TypeMirror nextActualType(List<TypeMirror> types, int typeIndex, boolean varArgs) {
322 if (varArgs && typeIndex >= types.size() - 1 && !types.isEmpty()) {
323 // unpack varargs array argument
324 TypeMirror actualType = types.get(types.size() - 1);
325 if (actualType.getKind() == TypeKind.ARRAY) {
326 actualType = ((ArrayType) actualType).getComponentType();
327 }
328 return actualType;
329 } else if (typeIndex < types.size()) {
330 return types.get(typeIndex);
331 } else {
332 return null;
333 }
334 }
335
336 private Parameter matchParameter(ParameterSpec specification, TypeMirror mirror, int specificationIndex, int varArgsIndex) {
337 TypeMirror resolvedType = mirror;
338 if (hasError(resolvedType)) {
339 return null;
340 }
341
342 if (!specification.matches(resolvedType)) {
343 return null;
344 }
345
346 TypeData resolvedTypeData = getTypeSystem().findTypeData(resolvedType);
347 if (resolvedTypeData != null) {
348 return new Parameter(specification, resolvedTypeData, specificationIndex, varArgsIndex);
349 } else {
350 return new Parameter(specification, resolvedType, specificationIndex, varArgsIndex);
351 }
352 }
353
354 public final E create(String id, int naturalOrder, ExecutableElement methodMetadata, AnnotationMirror mirror, TypeMirror returnType, List<TypeMirror> parameterTypes) {
355 return parseImpl(createSpecification(methodMetadata, mirror), naturalOrder, id, methodMetadata, mirror, returnType, parameterTypes);
356 }
357 }