comparison graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/MethodSpecParser.java @ 16851:2db61eddcb97

Truffle-DSL: argument syntax support for guards
author Christian Humer <christian.humer@gmail.com>
date Mon, 18 Aug 2014 18:41:16 +0200
parents
children 08aa0372dad4
comparison
equal deleted inserted replaced
16850:d6c002f4d2a9 16851:2db61eddcb97
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.parser;
24
25 import static com.oracle.truffle.dsl.processor.java.ElementUtils.*;
26
27 import java.util.*;
28
29 import javax.lang.model.element.*;
30 import javax.lang.model.type.*;
31
32 import com.oracle.truffle.dsl.processor.java.*;
33 import com.oracle.truffle.dsl.processor.model.*;
34
35 public final class MethodSpecParser {
36
37 private boolean emitErrors = true;
38 private boolean useVarArgs = false;
39
40 private final Template template;
41
42 public MethodSpecParser(Template template) {
43 this.template = template;
44 }
45
46 public Template getTemplate() {
47 return template;
48 }
49
50 public TypeSystemData getTypeSystem() {
51 return template.getTypeSystem();
52 }
53
54 public boolean isEmitErrors() {
55 return emitErrors;
56 }
57
58 public boolean isUseVarArgs() {
59 return useVarArgs;
60 }
61
62 public void setEmitErrors(boolean emitErrors) {
63 this.emitErrors = emitErrors;
64 }
65
66 public void setUseVarArgs(boolean useVarArgs) {
67 this.useVarArgs = useVarArgs;
68 }
69
70 public TemplateMethod parse(MethodSpec methodSpecification, ExecutableElement method, AnnotationMirror annotation, int naturalOrder) {
71 if (methodSpecification == null) {
72 return null;
73 }
74
75 methodSpecification.applyTypeDefinitions("types");
76
77 String id = method.getSimpleName().toString();
78 TypeMirror returnType = method.getReturnType();
79 List<TypeMirror> parameterTypes = new ArrayList<>();
80 for (VariableElement var : method.getParameters()) {
81 parameterTypes.add(var.asType());
82 }
83
84 return parseImpl(methodSpecification, naturalOrder, id, method, annotation, returnType, parameterTypes);
85 }
86
87 public TemplateMethod parseImpl(MethodSpec methodSpecification, int naturalOrder, String id, ExecutableElement method, AnnotationMirror annotation, TypeMirror returnType,
88 List<TypeMirror> parameterTypes) {
89 ParameterSpec returnTypeSpec = methodSpecification.getReturnType();
90 Parameter returnTypeMirror = matchParameter(returnTypeSpec, returnType, -1, -1);
91 if (returnTypeMirror == null) {
92 if (emitErrors) {
93 TemplateMethod invalidMethod = new TemplateMethod(id, naturalOrder, template, methodSpecification, method, annotation, returnTypeMirror, Collections.<Parameter> emptyList());
94 String expectedReturnType = returnTypeSpec.toSignatureString(true);
95 String actualReturnType = ElementUtils.getSimpleName(returnType);
96
97 String message = String.format("The provided return type \"%s\" does not match expected return type \"%s\".\nExpected signature: \n %s", actualReturnType, expectedReturnType,
98 methodSpecification.toSignatureString(method.getSimpleName().toString()));
99 invalidMethod.addError(message);
100 return invalidMethod;
101 } else {
102 return null;
103 }
104 }
105
106 List<Parameter> parameters = parseParameters(methodSpecification, parameterTypes, isUseVarArgs() && method != null ? method.isVarArgs() : false);
107 if (parameters == null) {
108 if (isEmitErrors() && method != null) {
109 TemplateMethod invalidMethod = new TemplateMethod(id, naturalOrder, template, methodSpecification, method, annotation, returnTypeMirror, Collections.<Parameter> emptyList());
110 String message = String.format("Method signature %s does not match to the expected signature: \n%s", createActualSignature(method),
111 methodSpecification.toSignatureString(method.getSimpleName().toString()));
112 invalidMethod.addError(message);
113 return invalidMethod;
114 } else {
115 return null;
116 }
117 }
118
119 return new TemplateMethod(id, naturalOrder, template, methodSpecification, method, annotation, returnTypeMirror, parameters);
120 }
121
122 private static String createActualSignature(ExecutableElement method) {
123 StringBuilder b = new StringBuilder("(");
124 String sep = "";
125 if (method != null) {
126 for (VariableElement var : method.getParameters()) {
127 b.append(sep);
128 b.append(ElementUtils.getSimpleName(var.asType()));
129 sep = ", ";
130 }
131 }
132 b.append(")");
133 return b.toString();
134 }
135
136 /*
137 * Parameter parsing tries to parse required arguments starting from offset 0 with increasing
138 * offset until it finds a signature end that matches the required specification. If there is no
139 * end matching the required arguments, parsing fails. Parameters prior to the parsed required
140 * ones are cut and used to parse the optional parameters.
141 */
142 private List<Parameter> parseParameters(MethodSpec spec, List<TypeMirror> parameterTypes, boolean varArgs) {
143 List<Parameter> parsedRequired = null;
144 int offset = 0;
145 for (; offset <= parameterTypes.size(); offset++) {
146 List<TypeMirror> parameters = new ArrayList<>();
147 parameters.addAll(parameterTypes.subList(offset, parameterTypes.size()));
148 parsedRequired = parseParametersRequired(spec, parameters, varArgs);
149 if (parsedRequired != null) {
150 break;
151 }
152 }
153
154 if (parsedRequired == null) {
155 return null;
156 }
157
158 if (parsedRequired.isEmpty() && offset == 0) {
159 offset = parameterTypes.size();
160 }
161 List<TypeMirror> potentialOptionals = parameterTypes.subList(0, offset);
162 List<Parameter> parsedOptionals = parseParametersOptional(spec, potentialOptionals);
163 if (parsedOptionals == null) {
164 return null;
165 }
166
167 List<Parameter> finalParameters = new ArrayList<>();
168 finalParameters.addAll(parsedOptionals);
169 finalParameters.addAll(parsedRequired);
170 return finalParameters;
171 }
172
173 private List<Parameter> parseParametersOptional(MethodSpec spec, List<TypeMirror> types) {
174 List<Parameter> parsedParams = new ArrayList<>();
175
176 int typeStartIndex = 0;
177 List<ParameterSpec> specifications = spec.getOptional();
178 outer: for (int specIndex = 0; specIndex < specifications.size(); specIndex++) {
179 ParameterSpec specification = specifications.get(specIndex);
180 for (int typeIndex = typeStartIndex; typeIndex < types.size(); typeIndex++) {
181 TypeMirror actualType = types.get(typeIndex);
182 Parameter optionalParam = matchParameter(specification, actualType, -1, -1);
183 if (optionalParam != null) {
184 parsedParams.add(optionalParam);
185 typeStartIndex = typeIndex + 1;
186 continue outer;
187 }
188 }
189 }
190
191 if (typeStartIndex < types.size()) {
192 // not enough types found
193 return null;
194 }
195 return parsedParams;
196 }
197
198 private List<Parameter> parseParametersRequired(MethodSpec spec, List<TypeMirror> types, boolean typeVarArgs) {
199 List<Parameter> parsedParams = new ArrayList<>();
200 List<ParameterSpec> specifications = spec.getRequired();
201 boolean specVarArgs = spec.isVariableRequiredParameters();
202 int typeIndex = 0;
203 int specificationIndex = 0;
204
205 ParameterSpec specification;
206 while ((specification = nextSpecification(specifications, specificationIndex, specVarArgs)) != null) {
207 TypeMirror actualType = nextActualType(types, typeIndex, typeVarArgs);
208 if (actualType == null) {
209 if (spec.isIgnoreAdditionalSpecifications()) {
210 break;
211 }
212 return null;
213 }
214
215 int typeVarArgsIndex = typeVarArgs ? typeIndex - types.size() + 1 : -1;
216 int specVarArgsIndex = specVarArgs ? specificationIndex - specifications.size() + 1 : -1;
217
218 if (typeVarArgsIndex >= 0 && specVarArgsIndex >= 0) {
219 // both specifications and types have a variable number of arguments
220 // we would get into an endless loop if we would continue
221 break;
222 }
223
224 Parameter resolvedParameter = matchParameter(specification, actualType, specVarArgsIndex, typeVarArgsIndex);
225 if (resolvedParameter == null) {
226 return null;
227 }
228 parsedParams.add(resolvedParameter);
229 typeIndex++;
230 specificationIndex++;
231 }
232
233 if (typeIndex < types.size()) {
234 // additional types available
235 if (spec.isIgnoreAdditionalParameters()) {
236 return parsedParams;
237 } else {
238 return null;
239 }
240 }
241
242 return parsedParams;
243 }
244
245 private static ParameterSpec nextSpecification(List<ParameterSpec> specifications, int specIndex, boolean varArgs) {
246 if (varArgs && specIndex >= specifications.size() - 1 && !specifications.isEmpty()) {
247 return specifications.get(specifications.size() - 1);
248 } else if (specIndex < specifications.size()) {
249 return specifications.get(specIndex);
250 } else {
251 return null;
252 }
253 }
254
255 private static TypeMirror nextActualType(List<TypeMirror> types, int typeIndex, boolean varArgs) {
256 if (varArgs && typeIndex >= types.size() - 1 && !types.isEmpty()) {
257 // unpack varargs array argument
258 TypeMirror actualType = types.get(types.size() - 1);
259 if (actualType.getKind() == TypeKind.ARRAY) {
260 actualType = ((ArrayType) actualType).getComponentType();
261 }
262 return actualType;
263 } else if (typeIndex < types.size()) {
264 return types.get(typeIndex);
265 } else {
266 return null;
267 }
268 }
269
270 private Parameter matchParameter(ParameterSpec specification, TypeMirror mirror, int specificationIndex, int varArgsIndex) {
271 TypeMirror resolvedType = mirror;
272 if (hasError(resolvedType)) {
273 return null;
274 }
275
276 if (!specification.matches(resolvedType)) {
277 return null;
278 }
279
280 TypeData resolvedTypeData = getTypeSystem().findTypeData(resolvedType);
281 if (resolvedTypeData != null) {
282 return new Parameter(specification, resolvedTypeData, specificationIndex, varArgsIndex);
283 } else {
284 return new Parameter(specification, resolvedType, specificationIndex, varArgsIndex);
285 }
286 }
287
288 }