Mercurial > hg > truffle
comparison truffle/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java @ 21951:9c8c0937da41
Moving all sources into truffle subdirectory
author | Jaroslav Tulach <jaroslav.tulach@oracle.com> |
---|---|
date | Wed, 17 Jun 2015 10:58:08 +0200 |
parents | graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java@fdf55f1ffc59 |
children | dc83cc1f94f2 |
comparison
equal
deleted
inserted
replaced
21950:2a5011c7e641 | 21951:9c8c0937da41 |
---|---|
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 java.lang.annotation.*; | |
26 import java.util.*; | |
27 | |
28 import javax.lang.model.element.*; | |
29 import javax.lang.model.type.*; | |
30 import javax.lang.model.util.*; | |
31 import javax.tools.Diagnostic.Kind; | |
32 | |
33 import com.oracle.truffle.api.*; | |
34 import com.oracle.truffle.api.dsl.*; | |
35 import com.oracle.truffle.api.dsl.internal.*; | |
36 import com.oracle.truffle.api.frame.*; | |
37 import com.oracle.truffle.api.nodes.*; | |
38 import com.oracle.truffle.dsl.processor.*; | |
39 import com.oracle.truffle.dsl.processor.expression.*; | |
40 import com.oracle.truffle.dsl.processor.java.*; | |
41 import com.oracle.truffle.dsl.processor.java.compiler.*; | |
42 import com.oracle.truffle.dsl.processor.java.model.CodeTypeMirror.ArrayCodeTypeMirror; | |
43 import com.oracle.truffle.dsl.processor.java.model.*; | |
44 import com.oracle.truffle.dsl.processor.model.*; | |
45 import com.oracle.truffle.dsl.processor.model.NodeChildData.Cardinality; | |
46 import com.oracle.truffle.dsl.processor.model.SpecializationData.SpecializationKind; | |
47 | |
48 @DSLOptions | |
49 public class NodeParser extends AbstractParser<NodeData> { | |
50 | |
51 public static final List<Class<? extends Annotation>> ANNOTATIONS = Arrays.asList(Fallback.class, TypeSystemReference.class, ShortCircuit.class, Specialization.class, NodeChild.class, | |
52 NodeChildren.class); | |
53 | |
54 @Override | |
55 protected NodeData parse(Element element, AnnotationMirror mirror) { | |
56 NodeData node = parseRootType((TypeElement) element); | |
57 if (Log.isDebug() && node != null) { | |
58 String dump = node.dump(); | |
59 log.message(Kind.ERROR, null, null, null, dump); | |
60 } | |
61 return node; | |
62 } | |
63 | |
64 @Override | |
65 protected NodeData filterErrorElements(NodeData model) { | |
66 for (Iterator<NodeData> iterator = model.getEnclosingNodes().iterator(); iterator.hasNext();) { | |
67 NodeData node = filterErrorElements(iterator.next()); | |
68 if (node == null) { | |
69 iterator.remove(); | |
70 } | |
71 } | |
72 if (model.hasErrors()) { | |
73 return null; | |
74 } | |
75 return model; | |
76 } | |
77 | |
78 @Override | |
79 public boolean isDelegateToRootDeclaredType() { | |
80 return true; | |
81 } | |
82 | |
83 @Override | |
84 public Class<? extends Annotation> getAnnotationType() { | |
85 return null; | |
86 } | |
87 | |
88 @Override | |
89 public List<Class<? extends Annotation>> getTypeDelegatedAnnotationTypes() { | |
90 return ANNOTATIONS; | |
91 } | |
92 | |
93 private NodeData parseRootType(TypeElement rootType) { | |
94 List<NodeData> enclosedNodes = new ArrayList<>(); | |
95 for (TypeElement enclosedType : ElementFilter.typesIn(rootType.getEnclosedElements())) { | |
96 NodeData enclosedChild = parseRootType(enclosedType); | |
97 if (enclosedChild != null) { | |
98 enclosedNodes.add(enclosedChild); | |
99 } | |
100 } | |
101 NodeData node; | |
102 try { | |
103 node = parseNode(rootType); | |
104 } catch (CompileErrorException e) { | |
105 throw e; | |
106 } catch (Throwable e) { | |
107 RuntimeException e2 = new RuntimeException(String.format("Parsing of Node %s failed.", ElementUtils.getQualifiedName(rootType))); | |
108 e2.addSuppressed(e); | |
109 throw e2; | |
110 } | |
111 if (node == null && !enclosedNodes.isEmpty()) { | |
112 node = new NodeData(context, rootType); | |
113 } | |
114 | |
115 if (node != null) { | |
116 for (NodeData enclosedNode : enclosedNodes) { | |
117 node.addEnclosedNode(enclosedNode); | |
118 } | |
119 } | |
120 return node; | |
121 } | |
122 | |
123 private NodeData parseNode(TypeElement originalTemplateType) { | |
124 // reloading the type elements is needed for ecj | |
125 TypeElement templateType = ElementUtils.fromTypeMirror(context.reloadTypeElement(originalTemplateType)); | |
126 | |
127 if (ElementUtils.findAnnotationMirror(processingEnv, originalTemplateType, GeneratedBy.class) != null) { | |
128 // generated nodes should not get called again. | |
129 return null; | |
130 } | |
131 | |
132 if (!ElementUtils.isAssignable(templateType.asType(), context.getTruffleTypes().getNode())) { | |
133 return null; | |
134 } | |
135 | |
136 List<TypeElement> lookupTypes = collectSuperClasses(new ArrayList<TypeElement>(), templateType); | |
137 List<Element> members = loadMembers(templateType); | |
138 // ensure the processed element has at least one @Specialization annotation. | |
139 if (!containsSpecializations(members)) { | |
140 return null; | |
141 } | |
142 | |
143 NodeData node = parseNodeData(templateType, lookupTypes); | |
144 if (node.hasErrors()) { | |
145 return node; | |
146 } | |
147 | |
148 node.getFields().addAll(parseFields(lookupTypes, members)); | |
149 node.getChildren().addAll(parseChildren(lookupTypes, members)); | |
150 node.getChildExecutions().addAll(parseExecutions(node.getFields(), node.getChildren(), members)); | |
151 node.getExecutableTypes().addAll(parseExecutableTypeData(node, members, node.getSignatureSize(), context.getFrameTypes(), false)); | |
152 | |
153 initializeExecutableTypes(node); | |
154 initializeImportGuards(node, lookupTypes, members); | |
155 initializeChildren(node); | |
156 | |
157 if (node.hasErrors()) { | |
158 return node; // error sync point | |
159 } | |
160 | |
161 if (node.hasErrors()) { | |
162 return node; // error sync point | |
163 } | |
164 | |
165 node.getSpecializations().addAll(new SpecializationMethodParser(context, node).parse(members)); | |
166 node.getSpecializations().addAll(new FallbackParser(context, node).parse(members)); | |
167 node.getCasts().addAll(new CreateCastParser(context, node).parse(members)); | |
168 node.getShortCircuits().addAll(new ShortCircuitParser(context, node).parse(members)); | |
169 | |
170 if (node.hasErrors()) { | |
171 return node; // error sync point | |
172 } | |
173 initializeSpecializations(members, node); | |
174 initializeExecutableTypeHierarchy(node); | |
175 | |
176 verifySpecializationSameLength(node); | |
177 initializeShortCircuits(node); // requires specializations and polymorphic specializations | |
178 | |
179 verifyVisibilities(node); | |
180 verifyMissingAbstractMethods(node, members); | |
181 verifyConstructors(node); | |
182 verifyNamingConvention(node.getShortCircuits(), "needs"); | |
183 verifySpecializationThrows(node); | |
184 return node; | |
185 } | |
186 | |
187 private static void initializeExecutableTypeHierarchy(NodeData node) { | |
188 SpecializationData polymorphic = node.getPolymorphicSpecialization(); | |
189 if (polymorphic != null) { | |
190 boolean polymorphicSignatureFound = false; | |
191 List<TypeMirror> dynamicTypes = polymorphic.getDynamicTypes(); | |
192 TypeMirror frame = null; | |
193 if (polymorphic.getFrame() != null) { | |
194 frame = dynamicTypes.remove(0); | |
195 } | |
196 | |
197 ExecutableTypeData polymorphicType = new ExecutableTypeData(node, polymorphic.getReturnType().getType(), "execute", frame, dynamicTypes); | |
198 String genericName = ExecutableTypeData.createName(polymorphicType) + "_"; | |
199 polymorphicType.setUniqueName(genericName); | |
200 | |
201 for (ExecutableTypeData type : node.getExecutableTypes()) { | |
202 if (polymorphicType.sameSignature(type)) { | |
203 polymorphicSignatureFound = true; | |
204 break; | |
205 } | |
206 } | |
207 | |
208 if (!polymorphicSignatureFound) { | |
209 node.getExecutableTypes().add(polymorphicType); | |
210 } | |
211 } | |
212 | |
213 List<ExecutableTypeData> rootTypes = buildExecutableHierarchy(node); | |
214 List<ExecutableTypeData> additionalAbstractRootTypes = new ArrayList<>(); | |
215 for (int i = 1; i < rootTypes.size(); i++) { | |
216 ExecutableTypeData rootType = rootTypes.get(i); | |
217 if (rootType.isAbstract()) { | |
218 // cannot implemement root | |
219 additionalAbstractRootTypes.add(rootType); | |
220 } else { | |
221 node.getExecutableTypes().remove(rootType); | |
222 } | |
223 } | |
224 if (!additionalAbstractRootTypes.isEmpty()) { | |
225 node.addError("Incompatible abstract execute methods found %s.", additionalAbstractRootTypes); | |
226 } | |
227 | |
228 namesUnique(node.getExecutableTypes()); | |
229 | |
230 } | |
231 | |
232 private static List<ExecutableTypeData> buildExecutableHierarchy(NodeData node) { | |
233 List<ExecutableTypeData> executes = node.getExecutableTypes(); | |
234 if (executes.isEmpty()) { | |
235 return Collections.emptyList(); | |
236 } | |
237 List<ExecutableTypeData> hierarchyExecutes = new ArrayList<>(executes); | |
238 Collections.sort(hierarchyExecutes); | |
239 ExecutableTypeData parent = hierarchyExecutes.get(0); | |
240 ListIterator<ExecutableTypeData> executesIterator = hierarchyExecutes.listIterator(1); | |
241 buildExecutableHierarchy(node, parent, executesIterator); | |
242 return hierarchyExecutes; | |
243 } | |
244 | |
245 private static void buildExecutableHierarchy(NodeData node, ExecutableTypeData parent, ListIterator<ExecutableTypeData> executesIterator) { | |
246 while (executesIterator.hasNext()) { | |
247 ExecutableTypeData other = executesIterator.next(); | |
248 if (other.canDelegateTo(parent)) { | |
249 parent.addDelegatedFrom(other); | |
250 executesIterator.remove(); | |
251 } | |
252 } | |
253 for (int i = 1; i < parent.getDelegatedFrom().size(); i++) { | |
254 buildExecutableHierarchy(node, parent.getDelegatedFrom().get(i - 1), parent.getDelegatedFrom().listIterator(i)); | |
255 } | |
256 } | |
257 | |
258 private List<Element> loadMembers(TypeElement templateType) { | |
259 List<Element> members = new ArrayList<>(CompilerFactory.getCompiler(templateType).getAllMembersInDeclarationOrder(context.getEnvironment(), templateType)); | |
260 | |
261 return members; | |
262 } | |
263 | |
264 private boolean containsSpecializations(List<Element> elements) { | |
265 boolean foundSpecialization = false; | |
266 for (ExecutableElement method : ElementFilter.methodsIn(elements)) { | |
267 if (ElementUtils.findAnnotationMirror(processingEnv, method, Specialization.class) != null) { | |
268 foundSpecialization = true; | |
269 break; | |
270 } | |
271 } | |
272 return foundSpecialization; | |
273 } | |
274 | |
275 private void initializeImportGuards(NodeData node, List<TypeElement> lookupTypes, List<Element> elements) { | |
276 for (TypeElement lookupType : lookupTypes) { | |
277 AnnotationMirror importAnnotation = ElementUtils.findAnnotationMirror(processingEnv, lookupType, ImportStatic.class); | |
278 if (importAnnotation == null) { | |
279 continue; | |
280 } | |
281 AnnotationValue importClassesValue = ElementUtils.getAnnotationValue(importAnnotation, "value"); | |
282 List<TypeMirror> importClasses = ElementUtils.getAnnotationValueList(TypeMirror.class, importAnnotation, "value"); | |
283 if (importClasses.isEmpty()) { | |
284 node.addError(importAnnotation, importClassesValue, "At least import guard classes must be specified."); | |
285 continue; | |
286 } | |
287 for (TypeMirror importGuardClass : importClasses) { | |
288 if (importGuardClass.getKind() != TypeKind.DECLARED) { | |
289 node.addError(importAnnotation, importClassesValue, "The specified import guard class '%s' is not a declared type.", ElementUtils.getQualifiedName(importGuardClass)); | |
290 continue; | |
291 } | |
292 | |
293 TypeElement typeElement = ElementUtils.fromTypeMirror(importGuardClass); | |
294 if (typeElement.getEnclosingElement().getKind().isClass() && !typeElement.getModifiers().contains(Modifier.PUBLIC)) { | |
295 node.addError(importAnnotation, importClassesValue, "The specified import guard class '%s' must be public.", ElementUtils.getQualifiedName(importGuardClass)); | |
296 continue; | |
297 } | |
298 elements.addAll(importPublicStaticMembers(typeElement, false)); | |
299 } | |
300 } | |
301 } | |
302 | |
303 private List<? extends Element> importPublicStaticMembers(TypeElement importGuardClass, boolean includeConstructors) { | |
304 // hack to reload type is necessary for incremental compiling in eclipse. | |
305 // otherwise methods inside of import guard types are just not found. | |
306 TypeElement typeElement = ElementUtils.fromTypeMirror(context.reloadType(importGuardClass.asType())); | |
307 | |
308 List<Element> members = new ArrayList<>(); | |
309 for (Element importElement : processingEnv.getElementUtils().getAllMembers(typeElement)) { | |
310 if (!importElement.getModifiers().contains(Modifier.PUBLIC)) { | |
311 continue; | |
312 } | |
313 | |
314 if (includeConstructors && importElement.getKind() == ElementKind.CONSTRUCTOR) { | |
315 members.add(importElement); | |
316 } | |
317 | |
318 if (!importElement.getModifiers().contains(Modifier.STATIC)) { | |
319 continue; | |
320 } | |
321 | |
322 ElementKind kind = importElement.getKind(); | |
323 if (kind.isField() || kind == ElementKind.METHOD) { | |
324 members.add(importElement); | |
325 } | |
326 } | |
327 return members; | |
328 } | |
329 | |
330 private NodeData parseNodeData(TypeElement templateType, List<TypeElement> typeHierarchy) { | |
331 AnnotationMirror typeSystemMirror = findFirstAnnotation(typeHierarchy, TypeSystemReference.class); | |
332 TypeSystemData typeSystem = null; | |
333 if (typeSystemMirror != null) { | |
334 TypeMirror typeSystemType = ElementUtils.getAnnotationValue(TypeMirror.class, typeSystemMirror, "value"); | |
335 typeSystem = (TypeSystemData) context.getTemplate(typeSystemType, true); | |
336 if (typeSystem == null) { | |
337 NodeData nodeData = new NodeData(context, templateType); | |
338 nodeData.addError("The used type system '%s' is invalid. Fix errors in the type system first.", ElementUtils.getQualifiedName(typeSystemType)); | |
339 return nodeData; | |
340 } | |
341 } else { | |
342 // default dummy type system | |
343 typeSystem = new TypeSystemData(context, templateType, null, NodeParser.class.getAnnotation(DSLOptions.class), true); | |
344 } | |
345 AnnotationMirror nodeInfoMirror = findFirstAnnotation(typeHierarchy, NodeInfo.class); | |
346 String shortName = null; | |
347 if (nodeInfoMirror != null) { | |
348 shortName = ElementUtils.getAnnotationValue(String.class, nodeInfoMirror, "shortName"); | |
349 } | |
350 boolean useNodeFactory = findFirstAnnotation(typeHierarchy, GenerateNodeFactory.class) != null; | |
351 return new NodeData(context, templateType, shortName, typeSystem, useNodeFactory); | |
352 | |
353 } | |
354 | |
355 private List<NodeFieldData> parseFields(List<TypeElement> typeHierarchy, List<? extends Element> elements) { | |
356 Set<String> names = new HashSet<>(); | |
357 | |
358 List<NodeFieldData> fields = new ArrayList<>(); | |
359 for (VariableElement field : ElementFilter.fieldsIn(elements)) { | |
360 if (field.getModifiers().contains(Modifier.STATIC)) { | |
361 continue; | |
362 } | |
363 if (field.getModifiers().contains(Modifier.PUBLIC) || field.getModifiers().contains(Modifier.PROTECTED)) { | |
364 String name = field.getSimpleName().toString(); | |
365 fields.add(new NodeFieldData(field, null, field, false)); | |
366 names.add(name); | |
367 } | |
368 } | |
369 | |
370 List<TypeElement> reversedTypeHierarchy = new ArrayList<>(typeHierarchy); | |
371 Collections.reverse(reversedTypeHierarchy); | |
372 for (TypeElement typeElement : reversedTypeHierarchy) { | |
373 AnnotationMirror nodeChildrenMirror = ElementUtils.findAnnotationMirror(processingEnv, typeElement, NodeFields.class); | |
374 List<AnnotationMirror> children = ElementUtils.collectAnnotations(context, nodeChildrenMirror, "value", typeElement, NodeField.class); | |
375 | |
376 for (AnnotationMirror mirror : children) { | |
377 String name = ElementUtils.firstLetterLowerCase(ElementUtils.getAnnotationValue(String.class, mirror, "name")); | |
378 TypeMirror type = ElementUtils.getAnnotationValue(TypeMirror.class, mirror, "type"); | |
379 | |
380 NodeFieldData field = new NodeFieldData(typeElement, mirror, new CodeVariableElement(type, name), true); | |
381 if (name.isEmpty()) { | |
382 field.addError(ElementUtils.getAnnotationValue(mirror, "name"), "Field name cannot be empty."); | |
383 } else if (names.contains(name)) { | |
384 field.addError(ElementUtils.getAnnotationValue(mirror, "name"), "Duplicate field name '%s'.", name); | |
385 } | |
386 names.add(name); | |
387 | |
388 fields.add(field); | |
389 } | |
390 } | |
391 | |
392 for (NodeFieldData nodeFieldData : fields) { | |
393 nodeFieldData.setGetter(findGetter(elements, nodeFieldData.getName(), nodeFieldData.getType())); | |
394 } | |
395 | |
396 return fields; | |
397 } | |
398 | |
399 private List<NodeChildData> parseChildren(final List<TypeElement> typeHierarchy, List<? extends Element> elements) { | |
400 Set<String> shortCircuits = new HashSet<>(); | |
401 for (ExecutableElement method : ElementFilter.methodsIn(elements)) { | |
402 AnnotationMirror mirror = ElementUtils.findAnnotationMirror(processingEnv, method, ShortCircuit.class); | |
403 if (mirror != null) { | |
404 shortCircuits.add(ElementUtils.getAnnotationValue(String.class, mirror, "value")); | |
405 } | |
406 } | |
407 Map<String, TypeMirror> castNodeTypes = new HashMap<>(); | |
408 for (ExecutableElement method : ElementFilter.methodsIn(elements)) { | |
409 AnnotationMirror mirror = ElementUtils.findAnnotationMirror(processingEnv, method, CreateCast.class); | |
410 if (mirror != null) { | |
411 List<String> children = (ElementUtils.getAnnotationValueList(String.class, mirror, "value")); | |
412 if (children != null) { | |
413 for (String child : children) { | |
414 castNodeTypes.put(child, method.getReturnType()); | |
415 } | |
416 } | |
417 } | |
418 } | |
419 | |
420 List<NodeChildData> parsedChildren = new ArrayList<>(); | |
421 List<TypeElement> typeHierarchyReversed = new ArrayList<>(typeHierarchy); | |
422 Collections.reverse(typeHierarchyReversed); | |
423 for (TypeElement type : typeHierarchyReversed) { | |
424 AnnotationMirror nodeChildrenMirror = ElementUtils.findAnnotationMirror(processingEnv, type, NodeChildren.class); | |
425 | |
426 TypeMirror nodeClassType = type.getSuperclass(); | |
427 if (!ElementUtils.isAssignable(nodeClassType, context.getTruffleTypes().getNode())) { | |
428 nodeClassType = null; | |
429 } | |
430 | |
431 List<AnnotationMirror> children = ElementUtils.collectAnnotations(context, nodeChildrenMirror, "value", type, NodeChild.class); | |
432 int index = 0; | |
433 for (AnnotationMirror childMirror : children) { | |
434 String name = ElementUtils.getAnnotationValue(String.class, childMirror, "value"); | |
435 if (name.equals("")) { | |
436 name = "child" + index; | |
437 } | |
438 | |
439 Cardinality cardinality = Cardinality.ONE; | |
440 | |
441 TypeMirror childType = inheritType(childMirror, "type", nodeClassType); | |
442 if (childType.getKind() == TypeKind.ARRAY) { | |
443 cardinality = Cardinality.MANY; | |
444 } | |
445 | |
446 TypeMirror originalChildType = childType; | |
447 TypeMirror castNodeType = castNodeTypes.get(name); | |
448 if (castNodeType != null) { | |
449 childType = castNodeType; | |
450 } | |
451 | |
452 Element getter = findGetter(elements, name, childType); | |
453 NodeChildData nodeChild = new NodeChildData(type, childMirror, name, childType, originalChildType, getter, cardinality); | |
454 | |
455 parsedChildren.add(nodeChild); | |
456 | |
457 if (nodeChild.getNodeType() == null) { | |
458 nodeChild.addError("No valid node type could be resoleved."); | |
459 } | |
460 if (nodeChild.hasErrors()) { | |
461 continue; | |
462 } | |
463 | |
464 index++; | |
465 } | |
466 } | |
467 | |
468 List<NodeChildData> filteredChildren = new ArrayList<>(); | |
469 Set<String> encounteredNames = new HashSet<>(); | |
470 for (int i = parsedChildren.size() - 1; i >= 0; i--) { | |
471 NodeChildData child = parsedChildren.get(i); | |
472 if (!encounteredNames.contains(child.getName())) { | |
473 filteredChildren.add(0, child); | |
474 encounteredNames.add(child.getName()); | |
475 } | |
476 } | |
477 | |
478 return filteredChildren; | |
479 } | |
480 | |
481 private List<NodeExecutionData> parseExecutions(List<NodeFieldData> fields, List<NodeChildData> children, List<? extends Element> elements) { | |
482 // pre-parse short circuits | |
483 Set<String> shortCircuits = new HashSet<>(); | |
484 List<ExecutableElement> methods = ElementFilter.methodsIn(elements); | |
485 for (ExecutableElement method : methods) { | |
486 AnnotationMirror mirror = ElementUtils.findAnnotationMirror(processingEnv, method, ShortCircuit.class); | |
487 if (mirror != null) { | |
488 shortCircuits.add(ElementUtils.getAnnotationValue(String.class, mirror, "value")); | |
489 } | |
490 } | |
491 | |
492 boolean hasVarArgs = false; | |
493 int maxSignatureSize = 0; | |
494 if (!children.isEmpty()) { | |
495 int lastIndex = children.size() - 1; | |
496 hasVarArgs = children.get(lastIndex).getCardinality() == Cardinality.MANY; | |
497 if (hasVarArgs) { | |
498 maxSignatureSize = lastIndex; | |
499 } else { | |
500 maxSignatureSize = children.size(); | |
501 } | |
502 } | |
503 | |
504 List<NodeFieldData> nonGetterFields = new ArrayList<>(); | |
505 for (NodeFieldData field : fields) { | |
506 if (field.getGetter() == null && field.isGenerated()) { | |
507 nonGetterFields.add(field); | |
508 } | |
509 } | |
510 | |
511 TypeMirror cacheAnnotation = context.getType(Cached.class); | |
512 List<TypeMirror> frameTypes = context.getFrameTypes(); | |
513 // pre-parse specializations to find signature size | |
514 for (ExecutableElement method : methods) { | |
515 AnnotationMirror mirror = ElementUtils.findAnnotationMirror(processingEnv, method, Specialization.class); | |
516 if (mirror == null) { | |
517 continue; | |
518 } | |
519 int currentArgumentIndex = 0; | |
520 boolean skipShortCircuit = false; | |
521 parameter: for (VariableElement var : method.getParameters()) { | |
522 if (skipShortCircuit) { | |
523 skipShortCircuit = false; | |
524 continue parameter; | |
525 } | |
526 | |
527 TypeMirror type = var.asType(); | |
528 if (currentArgumentIndex == 0) { | |
529 // skip optionals | |
530 for (TypeMirror frameType : frameTypes) { | |
531 if (ElementUtils.typeEquals(type, frameType)) { | |
532 continue parameter; | |
533 } | |
534 } | |
535 } | |
536 | |
537 if (currentArgumentIndex < nonGetterFields.size()) { | |
538 for (NodeFieldData field : nonGetterFields) { | |
539 if (ElementUtils.typeEquals(var.asType(), field.getType())) { | |
540 continue parameter; | |
541 } | |
542 } | |
543 } | |
544 | |
545 if (ElementUtils.findAnnotationMirror(var.getAnnotationMirrors(), cacheAnnotation) != null) { | |
546 continue parameter; | |
547 } | |
548 | |
549 int childIndex = currentArgumentIndex < children.size() ? currentArgumentIndex : children.size() - 1; | |
550 if (childIndex != -1) { | |
551 NodeChildData child = children.get(childIndex); | |
552 if (shortCircuits.contains(NodeExecutionData.createIndexedName(child, currentArgumentIndex - childIndex))) { | |
553 skipShortCircuit = true; | |
554 } | |
555 } | |
556 | |
557 currentArgumentIndex++; | |
558 } | |
559 maxSignatureSize = Math.max(maxSignatureSize, currentArgumentIndex); | |
560 } | |
561 | |
562 List<NodeExecutionData> executions = new ArrayList<>(); | |
563 for (int i = 0; i < maxSignatureSize; i++) { | |
564 boolean varArgParameter = false; | |
565 int childIndex = i; | |
566 if (i >= children.size() - 1) { | |
567 if (hasVarArgs) { | |
568 varArgParameter = hasVarArgs; | |
569 childIndex = Math.min(i, children.size() - 1); | |
570 } else if (i >= children.size()) { | |
571 childIndex = -1; | |
572 } | |
573 } | |
574 int varArgsIndex = -1; | |
575 boolean shortCircuit = false; | |
576 NodeChildData child = null; | |
577 if (childIndex != -1) { | |
578 varArgsIndex = varArgParameter ? Math.abs(childIndex - i) : -1; | |
579 child = children.get(childIndex); | |
580 shortCircuit = shortCircuits.contains(NodeExecutionData.createIndexedName(child, varArgsIndex)); | |
581 } | |
582 executions.add(new NodeExecutionData(child, i, varArgsIndex, shortCircuit)); | |
583 } | |
584 return executions; | |
585 } | |
586 | |
587 private List<ExecutableTypeData> parseExecutableTypeData(NodeData node, List<? extends Element> elements, int signatureSize, List<TypeMirror> frameTypes, boolean includeFinals) { | |
588 List<ExecutableTypeData> typeData = new ArrayList<>(); | |
589 for (ExecutableElement method : ElementFilter.methodsIn(elements)) { | |
590 Set<Modifier> modifiers = method.getModifiers(); | |
591 if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.STATIC)) { | |
592 continue; | |
593 } | |
594 if (!includeFinals && modifiers.contains(Modifier.FINAL)) { | |
595 continue; | |
596 } | |
597 | |
598 if (!method.getSimpleName().toString().startsWith("execute")) { | |
599 continue; | |
600 } | |
601 if (ElementUtils.findAnnotationMirror(context.getEnvironment(), method, Specialization.class) != null) { | |
602 continue; | |
603 } | |
604 | |
605 ExecutableTypeData executableType = new ExecutableTypeData(node, method, signatureSize, context.getFrameTypes()); | |
606 | |
607 if (executableType.getFrameParameter() != null) { | |
608 boolean supportedType = false; | |
609 for (TypeMirror type : frameTypes) { | |
610 if (ElementUtils.isAssignable(type, executableType.getFrameParameter())) { | |
611 supportedType = true; | |
612 break; | |
613 } | |
614 } | |
615 if (!supportedType) { | |
616 continue; | |
617 } | |
618 } | |
619 | |
620 typeData.add(executableType); | |
621 } | |
622 | |
623 namesUnique(typeData); | |
624 | |
625 return typeData; | |
626 } | |
627 | |
628 private static void namesUnique(List<ExecutableTypeData> typeData) { | |
629 List<String> names = new ArrayList<>(); | |
630 for (ExecutableTypeData type : typeData) { | |
631 names.add(type.getUniqueName()); | |
632 } | |
633 while (renameDuplicateIds(names)) { | |
634 // fix point | |
635 } | |
636 | |
637 for (int i = 0; i < typeData.size(); i++) { | |
638 typeData.get(i).setUniqueName(names.get(i)); | |
639 } | |
640 } | |
641 | |
642 private void initializeExecutableTypes(NodeData node) { | |
643 List<ExecutableTypeData> allExecutes = node.getExecutableTypes(); | |
644 | |
645 Set<String> inconsistentFrameTypes = new HashSet<>(); | |
646 TypeMirror frameType = null; | |
647 for (ExecutableTypeData execute : allExecutes) { | |
648 | |
649 TypeMirror frame = execute.getFrameParameter(); | |
650 TypeMirror resolvedFrameType; | |
651 if (frame != null) { | |
652 resolvedFrameType = frame; | |
653 if (frameType == null) { | |
654 frameType = resolvedFrameType; | |
655 } else if (!ElementUtils.typeEquals(frameType, resolvedFrameType)) { | |
656 // found inconsistent frame types | |
657 inconsistentFrameTypes.add(ElementUtils.getSimpleName(frameType)); | |
658 inconsistentFrameTypes.add(ElementUtils.getSimpleName(resolvedFrameType)); | |
659 } | |
660 } | |
661 } | |
662 if (!inconsistentFrameTypes.isEmpty()) { | |
663 // ensure they are sorted somehow | |
664 List<String> inconsistentFrameTypesList = new ArrayList<>(inconsistentFrameTypes); | |
665 Collections.sort(inconsistentFrameTypesList); | |
666 node.addError("Invalid inconsistent frame types %s found for the declared execute methods. The frame type must be identical for all execute methods.", inconsistentFrameTypesList); | |
667 } | |
668 if (frameType == null) { | |
669 frameType = context.getType(void.class); | |
670 } | |
671 | |
672 node.setFrameType(frameType); | |
673 | |
674 boolean genericFound = false; | |
675 for (ExecutableTypeData type : node.getExecutableTypes()) { | |
676 if (!type.hasUnexpectedValue(context)) { | |
677 genericFound = true; | |
678 break; | |
679 } | |
680 } | |
681 | |
682 // no generic executes | |
683 if (!genericFound) { | |
684 node.addError("No accessible and overridable generic execute method found. Generic execute methods usually have the " | |
685 + "signature 'public abstract {Type} execute(VirtualFrame)' and must not throw any checked exceptions."); | |
686 } | |
687 | |
688 int nodeChildDeclarations = 0; | |
689 int nodeChildDeclarationsRequired = 0; | |
690 List<NodeExecutionData> executions = node.getChildExecutions(); | |
691 for (NodeExecutionData execution : executions) { | |
692 if (execution.getChild() == null) { | |
693 nodeChildDeclarationsRequired = execution.getIndex() + 1; | |
694 } else { | |
695 nodeChildDeclarations++; | |
696 } | |
697 } | |
698 | |
699 List<String> requireNodeChildDeclarations = new ArrayList<>(); | |
700 for (ExecutableTypeData type : allExecutes) { | |
701 if (type.getEvaluatedCount() < nodeChildDeclarationsRequired) { | |
702 requireNodeChildDeclarations.add(ElementUtils.createReferenceName(type.getMethod())); | |
703 } | |
704 } | |
705 | |
706 if (!requireNodeChildDeclarations.isEmpty()) { | |
707 node.addError("Not enough child node declarations found. Please annotate the node class with addtional @NodeChild annotations or remove all execute methods that do not provide all evaluated values. " | |
708 + "The following execute methods do not provide all evaluated values for the expected signature size %s: %s.", executions.size(), requireNodeChildDeclarations); | |
709 } | |
710 | |
711 if (nodeChildDeclarations > 0 && executions.size() == node.getMinimalEvaluatedParameters()) { | |
712 for (NodeChildData child : node.getChildren()) { | |
713 child.addError("Unnecessary @NodeChild declaration. All evaluated child values are provided as parameters in execute methods."); | |
714 } | |
715 } | |
716 | |
717 } | |
718 | |
719 private void initializeChildren(NodeData node) { | |
720 initializeExecuteWith(node); | |
721 | |
722 for (NodeChildData child : node.getChildren()) { | |
723 TypeMirror nodeType = child.getNodeType(); | |
724 NodeData fieldNodeData = parseChildNodeData(node, child, ElementUtils.fromTypeMirror(nodeType)); | |
725 | |
726 child.setNode(fieldNodeData); | |
727 if (fieldNodeData == null || fieldNodeData.hasErrors()) { | |
728 child.addError("Node type '%s' is invalid or not a subclass of Node.", ElementUtils.getQualifiedName(nodeType)); | |
729 } else { | |
730 List<ExecutableTypeData> types = child.findGenericExecutableTypes(context); | |
731 if (types.isEmpty()) { | |
732 AnnotationValue executeWithValue = ElementUtils.getAnnotationValue(child.getMessageAnnotation(), "executeWith"); | |
733 child.addError(executeWithValue, "No generic execute method found with %s evaluated arguments for node type %s and frame types %s.", child.getExecuteWith().size(), | |
734 ElementUtils.getSimpleName(nodeType), ElementUtils.getUniqueIdentifiers(createAllowedChildFrameTypes(node))); | |
735 } | |
736 } | |
737 } | |
738 } | |
739 | |
740 private static void initializeExecuteWith(NodeData node) { | |
741 for (NodeChildData child : node.getChildren()) { | |
742 List<String> executeWithStrings = ElementUtils.getAnnotationValueList(String.class, child.getMessageAnnotation(), "executeWith"); | |
743 AnnotationValue executeWithValue = ElementUtils.getAnnotationValue(child.getMessageAnnotation(), "executeWith"); | |
744 List<NodeExecutionData> executeWith = new ArrayList<>(); | |
745 for (String executeWithString : executeWithStrings) { | |
746 if (child.getName().equals(executeWithString)) { | |
747 child.addError(executeWithValue, "The child node '%s' cannot be executed with itself.", executeWithString); | |
748 continue; | |
749 } | |
750 NodeExecutionData found = null; | |
751 boolean before = true; | |
752 for (NodeExecutionData resolveChild : node.getChildExecutions()) { | |
753 if (resolveChild.getChild() == child) { | |
754 before = false; | |
755 continue; | |
756 } | |
757 if (resolveChild.getIndexedName().equals(executeWithString)) { | |
758 found = resolveChild; | |
759 break; | |
760 } | |
761 } | |
762 | |
763 if (found == null) { | |
764 child.addError(executeWithValue, "The child node '%s' cannot be executed with '%s'. The child node was not found.", child.getName(), executeWithString); | |
765 continue; | |
766 } else if (!before) { | |
767 child.addError(executeWithValue, "The child node '%s' cannot be executed with '%s'. The node %s is executed after the current node.", child.getName(), executeWithString, | |
768 executeWithString); | |
769 continue; | |
770 } | |
771 executeWith.add(found); | |
772 } | |
773 child.setExecuteWith(executeWith); | |
774 } | |
775 } | |
776 | |
777 private NodeData parseChildNodeData(NodeData parentNode, NodeChildData child, TypeElement originalTemplateType) { | |
778 TypeElement templateType = ElementUtils.fromTypeMirror(context.reloadTypeElement(originalTemplateType)); | |
779 | |
780 if (ElementUtils.findAnnotationMirror(processingEnv, originalTemplateType, GeneratedBy.class) != null) { | |
781 // generated nodes should not get called again. | |
782 return null; | |
783 } | |
784 | |
785 if (!ElementUtils.isAssignable(templateType.asType(), context.getTruffleTypes().getNode())) { | |
786 return null; | |
787 } | |
788 | |
789 List<TypeElement> lookupTypes = collectSuperClasses(new ArrayList<TypeElement>(), templateType); | |
790 | |
791 // Declaration order is not required for child nodes. | |
792 List<? extends Element> members = processingEnv.getElementUtils().getAllMembers(templateType); | |
793 NodeData node = parseNodeData(templateType, lookupTypes); | |
794 if (node.hasErrors()) { | |
795 return node; | |
796 } | |
797 List<TypeMirror> frameTypes = Collections.emptyList(); | |
798 if (parentNode.getFrameType() != null) { | |
799 frameTypes = Arrays.asList(parentNode.getFrameType()); | |
800 } | |
801 node.getExecutableTypes().addAll(parseExecutableTypeData(node, members, child.getExecuteWith().size(), frameTypes, true)); | |
802 node.setFrameType(parentNode.getFrameType()); | |
803 return node; | |
804 } | |
805 | |
806 private List<TypeMirror> createAllowedChildFrameTypes(NodeData parentNode) { | |
807 List<TypeMirror> allowedFrameTypes = new ArrayList<>(); | |
808 for (TypeMirror frameType : context.getFrameTypes()) { | |
809 if (ElementUtils.isAssignable(parentNode.getFrameType(), frameType)) { | |
810 allowedFrameTypes.add(frameType); | |
811 } | |
812 } | |
813 return allowedFrameTypes; | |
814 } | |
815 | |
816 private void initializeSpecializations(List<? extends Element> elements, final NodeData node) { | |
817 if (node.getSpecializations().isEmpty()) { | |
818 return; | |
819 } | |
820 | |
821 initializeExpressions(elements, node); | |
822 | |
823 if (node.hasErrors()) { | |
824 return; | |
825 } | |
826 | |
827 initializeGeneric(node); | |
828 initializeUninitialized(node); | |
829 initializeOrder(node); | |
830 initializePolymorphism(node); // requires specializations | |
831 initializeReachability(node); | |
832 initializeContains(node); | |
833 resolveContains(node); | |
834 | |
835 List<SpecializationData> specializations = node.getSpecializations(); | |
836 for (SpecializationData cur : specializations) { | |
837 for (SpecializationData contained : cur.getContains()) { | |
838 if (contained != cur) { | |
839 contained.getExcludedBy().add(cur); | |
840 } | |
841 } | |
842 } | |
843 | |
844 initializeSpecializationIdsWithMethodNames(node.getSpecializations()); | |
845 } | |
846 | |
847 private static void initializeOrder(NodeData node) { | |
848 List<SpecializationData> specializations = node.getSpecializations(); | |
849 Collections.sort(specializations); | |
850 | |
851 for (SpecializationData specialization : specializations) { | |
852 String searchName = specialization.getInsertBeforeName(); | |
853 if (searchName == null || specialization.getMethod() == null) { | |
854 continue; | |
855 } | |
856 SpecializationData found = lookupSpecialization(node, searchName); | |
857 if (found == null || found.getMethod() == null) { | |
858 AnnotationValue value = ElementUtils.getAnnotationValue(specialization.getMarkerAnnotation(), "insertBefore"); | |
859 specialization.addError(value, "The referenced specialization '%s' could not be found.", searchName); | |
860 continue; | |
861 } | |
862 | |
863 ExecutableElement currentMethod = specialization.getMethod(); | |
864 ExecutableElement insertBeforeMethod = found.getMethod(); | |
865 | |
866 TypeMirror currentEnclosedType = currentMethod.getEnclosingElement().asType(); | |
867 TypeMirror insertBeforeEnclosedType = insertBeforeMethod.getEnclosingElement().asType(); | |
868 | |
869 if (ElementUtils.typeEquals(currentEnclosedType, insertBeforeEnclosedType) || !ElementUtils.isSubtype(currentEnclosedType, insertBeforeEnclosedType)) { | |
870 AnnotationValue value = ElementUtils.getAnnotationValue(specialization.getMarkerAnnotation(), "insertBefore"); | |
871 specialization.addError(value, "Specializations can only be inserted before specializations in superclasses.", searchName); | |
872 continue; | |
873 } | |
874 | |
875 specialization.setInsertBefore(found); | |
876 } | |
877 | |
878 for (int i = 0; i < specializations.size(); i++) { | |
879 SpecializationData specialization = specializations.get(i); | |
880 SpecializationData insertBefore = specialization.getInsertBefore(); | |
881 if (insertBefore != null) { | |
882 int insertIndex = specializations.indexOf(insertBefore); | |
883 if (insertIndex < i) { | |
884 specializations.remove(i); | |
885 specializations.add(insertIndex, specialization); | |
886 } | |
887 } | |
888 } | |
889 | |
890 for (int i = 0; i < specializations.size(); i++) { | |
891 specializations.get(i).setIndex(i); | |
892 } | |
893 } | |
894 | |
895 private static void initializeContains(NodeData node) { | |
896 for (SpecializationData specialization : node.getSpecializations()) { | |
897 Set<SpecializationData> resolvedSpecializations = specialization.getContains(); | |
898 resolvedSpecializations.clear(); | |
899 Set<String> includeNames = specialization.getContainsNames(); | |
900 for (String includeName : includeNames) { | |
901 // TODO reduce complexity of this lookup. | |
902 SpecializationData foundSpecialization = lookupSpecialization(node, includeName); | |
903 | |
904 if (foundSpecialization == null) { | |
905 AnnotationValue value = ElementUtils.getAnnotationValue(specialization.getMarkerAnnotation(), "contains"); | |
906 specialization.addError(value, "The referenced specialization '%s' could not be found.", includeName); | |
907 } else { | |
908 if (foundSpecialization.compareTo(specialization) > 0) { | |
909 AnnotationValue value = ElementUtils.getAnnotationValue(specialization.getMarkerAnnotation(), "contains"); | |
910 if (foundSpecialization.compareTo(specialization) > 0) { | |
911 specialization.addError(value, "The contained specialization '%s' must be declared before the containing specialization.", includeName); | |
912 } | |
913 | |
914 } | |
915 resolvedSpecializations.add(foundSpecialization); | |
916 } | |
917 } | |
918 } | |
919 } | |
920 | |
921 private void resolveContains(NodeData node) { | |
922 // flatten transitive includes | |
923 for (SpecializationData specialization : node.getSpecializations()) { | |
924 if (specialization.getContains().isEmpty()) { | |
925 continue; | |
926 } | |
927 Set<SpecializationData> foundSpecializations = new HashSet<>(); | |
928 collectIncludes(specialization, foundSpecializations, new HashSet<SpecializationData>()); | |
929 specialization.getContains().addAll(foundSpecializations); | |
930 } | |
931 } | |
932 | |
933 private static SpecializationData lookupSpecialization(NodeData node, String includeName) { | |
934 SpecializationData foundSpecialization = null; | |
935 for (SpecializationData searchSpecialization : node.getSpecializations()) { | |
936 if (searchSpecialization.getMethodName().equals(includeName)) { | |
937 foundSpecialization = searchSpecialization; | |
938 break; | |
939 } | |
940 } | |
941 return foundSpecialization; | |
942 } | |
943 | |
944 private void collectIncludes(SpecializationData specialization, Set<SpecializationData> found, Set<SpecializationData> visited) { | |
945 if (visited.contains(specialization)) { | |
946 // circle found | |
947 specialization.addError("Circular contained specialization '%s' found.", specialization.createReferenceName()); | |
948 return; | |
949 } | |
950 visited.add(specialization); | |
951 | |
952 for (SpecializationData included : specialization.getContains()) { | |
953 collectIncludes(included, found, new HashSet<>(visited)); | |
954 found.add(included); | |
955 } | |
956 } | |
957 | |
958 private static void initializeReachability(final NodeData node) { | |
959 List<SpecializationData> specializations = node.getSpecializations(); | |
960 for (int i = specializations.size() - 1; i >= 0; i--) { | |
961 SpecializationData current = specializations.get(i); | |
962 if (current.isPolymorphic()) { | |
963 current.setReachable(true); | |
964 continue; | |
965 } | |
966 | |
967 List<SpecializationData> shadowedBy = null; | |
968 for (int j = i - 1; j >= 0; j--) { | |
969 SpecializationData prev = specializations.get(j); | |
970 if (prev.isPolymorphic()) { | |
971 continue; | |
972 } | |
973 if (!current.isReachableAfter(prev)) { | |
974 if (shadowedBy == null) { | |
975 shadowedBy = new ArrayList<>(); | |
976 } | |
977 shadowedBy.add(prev); | |
978 } | |
979 } | |
980 | |
981 if (shadowedBy != null) { | |
982 StringBuilder name = new StringBuilder(); | |
983 String sep = ""; | |
984 for (SpecializationData shadowSpecialization : shadowedBy) { | |
985 name.append(sep); | |
986 name.append(shadowSpecialization.createReferenceName()); | |
987 sep = ", "; | |
988 } | |
989 current.addError("%s is not reachable. It is shadowed by %s.", current.isFallback() ? "Generic" : "Specialization", name); | |
990 } | |
991 current.setReachable(shadowedBy == null); | |
992 } | |
993 } | |
994 | |
995 private static void initializeSpecializationIdsWithMethodNames(List<SpecializationData> specializations) { | |
996 List<String> signatures = new ArrayList<>(); | |
997 for (SpecializationData specialization : specializations) { | |
998 if (specialization.isFallback()) { | |
999 signatures.add("Fallback"); | |
1000 } else if (specialization.isUninitialized()) { | |
1001 signatures.add("Uninitialized"); | |
1002 } else if (specialization.isPolymorphic()) { | |
1003 signatures.add("Polymorphic"); | |
1004 } else { | |
1005 String name = specialization.getMethodName(); | |
1006 | |
1007 // hack for name clashes with BaseNode. | |
1008 if (name.equalsIgnoreCase("base")) { | |
1009 name = name + "0"; | |
1010 } else if (name.startsWith("do")) { | |
1011 String filteredDo = name.substring(2, name.length()); | |
1012 if (!filteredDo.isEmpty() && Character.isJavaIdentifierStart(filteredDo.charAt(0))) { | |
1013 name = filteredDo; | |
1014 } | |
1015 } | |
1016 signatures.add(ElementUtils.firstLetterUpperCase(name)); | |
1017 } | |
1018 } | |
1019 | |
1020 while (renameDuplicateIds(signatures)) { | |
1021 // fix point | |
1022 } | |
1023 | |
1024 for (int i = 0; i < specializations.size(); i++) { | |
1025 specializations.get(i).setId(signatures.get(i)); | |
1026 } | |
1027 } | |
1028 | |
1029 private static boolean renameDuplicateIds(List<String> signatures) { | |
1030 boolean changed = false; | |
1031 Map<String, Integer> counts = new HashMap<>(); | |
1032 for (String s1 : signatures) { | |
1033 Integer count = counts.get(s1.toLowerCase()); | |
1034 if (count == null) { | |
1035 count = 0; | |
1036 } | |
1037 count++; | |
1038 counts.put(s1.toLowerCase(), count); | |
1039 } | |
1040 | |
1041 for (String s : counts.keySet()) { | |
1042 int count = counts.get(s); | |
1043 if (count > 1) { | |
1044 changed = true; | |
1045 int number = 0; | |
1046 for (ListIterator<String> iterator = signatures.listIterator(); iterator.hasNext();) { | |
1047 String s2 = iterator.next(); | |
1048 if (s.equalsIgnoreCase(s2)) { | |
1049 iterator.set(s2 + number); | |
1050 number++; | |
1051 } | |
1052 } | |
1053 } | |
1054 } | |
1055 return changed; | |
1056 } | |
1057 | |
1058 private void initializeExpressions(List<? extends Element> elements, NodeData node) { | |
1059 List<Element> members = filterNotAccessibleElements(node.getTemplateType(), elements); | |
1060 | |
1061 List<VariableElement> fields = new ArrayList<>(); | |
1062 for (NodeFieldData field : node.getFields()) { | |
1063 fields.add(field.getVariable()); | |
1064 } | |
1065 | |
1066 for (SpecializationData specialization : node.getSpecializations()) { | |
1067 if (specialization.getMethod() == null) { | |
1068 continue; | |
1069 } | |
1070 | |
1071 List<Element> specializationMembers = new ArrayList<>(members.size() + specialization.getParameters().size() + fields.size()); | |
1072 for (Parameter p : specialization.getParameters()) { | |
1073 specializationMembers.add(p.getVariableElement()); | |
1074 } | |
1075 specializationMembers.addAll(fields); | |
1076 specializationMembers.addAll(members); | |
1077 DSLExpressionResolver resolver = new DSLExpressionResolver(context, specializationMembers); | |
1078 | |
1079 initializeCaches(specialization, resolver); | |
1080 initializeGuards(specialization, resolver); | |
1081 if (specialization.hasErrors()) { | |
1082 continue; | |
1083 } | |
1084 initializeLimit(specialization, resolver); | |
1085 initializeAssumptions(specialization, resolver); | |
1086 } | |
1087 } | |
1088 | |
1089 private void initializeAssumptions(SpecializationData specialization, DSLExpressionResolver resolver) { | |
1090 final DeclaredType assumptionType = context.getDeclaredType(Assumption.class); | |
1091 final TypeMirror assumptionArrayType = new ArrayCodeTypeMirror(assumptionType); | |
1092 final List<String> assumptionDefinitions = ElementUtils.getAnnotationValueList(String.class, specialization.getMarkerAnnotation(), "assumptions"); | |
1093 List<AssumptionExpression> assumptionExpressions = new ArrayList<>(); | |
1094 int assumptionId = 0; | |
1095 for (String assumption : assumptionDefinitions) { | |
1096 AssumptionExpression assumptionExpression; | |
1097 DSLExpression expression = null; | |
1098 try { | |
1099 expression = DSLExpression.parse(assumption); | |
1100 expression.accept(resolver); | |
1101 assumptionExpression = new AssumptionExpression(specialization, expression, "assumption" + assumptionId); | |
1102 if (!ElementUtils.isAssignable(expression.getResolvedType(), assumptionType) && !ElementUtils.isAssignable(expression.getResolvedType(), assumptionArrayType)) { | |
1103 assumptionExpression.addError("Incompatible return type %s. Assumptions must be assignable to %s or %s.", ElementUtils.getSimpleName(expression.getResolvedType()), | |
1104 ElementUtils.getSimpleName(assumptionType), ElementUtils.getSimpleName(assumptionArrayType)); | |
1105 } | |
1106 if (specialization.isDynamicParameterBound(expression)) { | |
1107 specialization.addError("Assumption expressions must not bind dynamic parameter values."); | |
1108 } | |
1109 } catch (InvalidExpressionException e) { | |
1110 assumptionExpression = new AssumptionExpression(specialization, null, "assumption" + assumptionId); | |
1111 assumptionExpression.addError("Error parsing expression '%s': %s", assumption, e.getMessage()); | |
1112 } | |
1113 assumptionExpressions.add(assumptionExpression); | |
1114 } | |
1115 specialization.setAssumptionExpressions(assumptionExpressions); | |
1116 } | |
1117 | |
1118 private void initializeLimit(SpecializationData specialization, DSLExpressionResolver resolver) { | |
1119 AnnotationValue annotationValue = ElementUtils.getAnnotationValue(specialization.getMessageAnnotation(), "limit"); | |
1120 | |
1121 String limitValue; | |
1122 if (annotationValue == null) { | |
1123 limitValue = ""; | |
1124 } else { | |
1125 limitValue = (String) annotationValue.getValue(); | |
1126 } | |
1127 if (limitValue.isEmpty()) { | |
1128 limitValue = "3"; | |
1129 } else if (!specialization.hasMultipleInstances()) { | |
1130 specialization.addWarning(annotationValue, "The limit expression has no effect. Multiple specialization instantiations are impossible for this specialization."); | |
1131 return; | |
1132 } | |
1133 | |
1134 TypeMirror expectedType = context.getType(int.class); | |
1135 try { | |
1136 DSLExpression expression = DSLExpression.parse(limitValue); | |
1137 expression.accept(resolver); | |
1138 if (!ElementUtils.typeEquals(expression.getResolvedType(), expectedType)) { | |
1139 specialization.addError(annotationValue, "Incompatible return type %s. Limit expressions must return %s.", ElementUtils.getSimpleName(expression.getResolvedType()), | |
1140 ElementUtils.getSimpleName(expectedType)); | |
1141 } | |
1142 if (specialization.isDynamicParameterBound(expression)) { | |
1143 specialization.addError(annotationValue, "Limit expressions must not bind dynamic parameter values."); | |
1144 } | |
1145 | |
1146 specialization.setLimitExpression(expression); | |
1147 } catch (InvalidExpressionException e) { | |
1148 specialization.addError(annotationValue, "Error parsing expression '%s': %s", limitValue, e.getMessage()); | |
1149 } | |
1150 } | |
1151 | |
1152 private void initializeCaches(SpecializationData specialization, DSLExpressionResolver resolver) { | |
1153 TypeMirror cacheMirror = context.getType(Cached.class); | |
1154 List<CacheExpression> expressions = new ArrayList<>(); | |
1155 for (Parameter parameter : specialization.getParameters()) { | |
1156 AnnotationMirror annotationMirror = ElementUtils.findAnnotationMirror(parameter.getVariableElement().getAnnotationMirrors(), cacheMirror); | |
1157 if (annotationMirror != null) { | |
1158 String initializer = ElementUtils.getAnnotationValue(String.class, annotationMirror, "value"); | |
1159 | |
1160 TypeMirror parameterType = parameter.getType(); | |
1161 | |
1162 DSLExpressionResolver localResolver = resolver; | |
1163 if (parameterType.getKind() == TypeKind.DECLARED) { | |
1164 localResolver = localResolver.copy(importPublicStaticMembers(ElementUtils.fromTypeMirror(parameterType), true)); | |
1165 } | |
1166 | |
1167 CacheExpression cacheExpression; | |
1168 DSLExpression expression = null; | |
1169 try { | |
1170 expression = DSLExpression.parse(initializer); | |
1171 expression.accept(localResolver); | |
1172 cacheExpression = new CacheExpression(parameter, annotationMirror, expression); | |
1173 if (!ElementUtils.typeEquals(expression.getResolvedType(), parameter.getType())) { | |
1174 cacheExpression.addError("Incompatible return type %s. The expression type must be equal to the parameter type %s.", ElementUtils.getSimpleName(expression.getResolvedType()), | |
1175 ElementUtils.getSimpleName(parameter.getType())); | |
1176 } | |
1177 } catch (InvalidExpressionException e) { | |
1178 cacheExpression = new CacheExpression(parameter, annotationMirror, null); | |
1179 cacheExpression.addError("Error parsing expression '%s': %s", initializer, e.getMessage()); | |
1180 } | |
1181 expressions.add(cacheExpression); | |
1182 } | |
1183 } | |
1184 specialization.setCaches(expressions); | |
1185 | |
1186 if (specialization.hasErrors()) { | |
1187 return; | |
1188 } | |
1189 | |
1190 // verify that cache expressions are bound in the correct order. | |
1191 for (int i = 0; i < expressions.size(); i++) { | |
1192 CacheExpression currentExpression = expressions.get(i); | |
1193 Set<VariableElement> boundVariables = currentExpression.getExpression().findBoundVariableElements(); | |
1194 for (int j = i + 1; j < expressions.size(); j++) { | |
1195 CacheExpression boundExpression = expressions.get(j); | |
1196 if (boundVariables.contains(boundExpression.getParameter().getVariableElement())) { | |
1197 currentExpression.addError("The initializer expression of parameter '%s' binds unitialized parameter '%s. Reorder the parameters to resolve the problem.", | |
1198 currentExpression.getParameter().getLocalName(), boundExpression.getParameter().getLocalName()); | |
1199 break; | |
1200 } | |
1201 } | |
1202 } | |
1203 } | |
1204 | |
1205 private void initializeGuards(SpecializationData specialization, DSLExpressionResolver resolver) { | |
1206 final TypeMirror booleanType = context.getType(boolean.class); | |
1207 List<String> guardDefinitions = ElementUtils.getAnnotationValueList(String.class, specialization.getMarkerAnnotation(), "guards"); | |
1208 List<GuardExpression> guardExpressions = new ArrayList<>(); | |
1209 for (String guard : guardDefinitions) { | |
1210 GuardExpression guardExpression; | |
1211 DSLExpression expression = null; | |
1212 try { | |
1213 expression = DSLExpression.parse(guard); | |
1214 expression.accept(resolver); | |
1215 guardExpression = new GuardExpression(specialization, expression); | |
1216 if (!ElementUtils.typeEquals(expression.getResolvedType(), booleanType)) { | |
1217 guardExpression.addError("Incompatible return type %s. Guards must return %s.", ElementUtils.getSimpleName(expression.getResolvedType()), ElementUtils.getSimpleName(booleanType)); | |
1218 } | |
1219 } catch (InvalidExpressionException e) { | |
1220 guardExpression = new GuardExpression(specialization, null); | |
1221 guardExpression.addError("Error parsing expression '%s': %s", guard, e.getMessage()); | |
1222 } | |
1223 guardExpressions.add(guardExpression); | |
1224 } | |
1225 specialization.setGuards(guardExpressions); | |
1226 } | |
1227 | |
1228 private static List<Element> filterNotAccessibleElements(TypeElement templateType, List<? extends Element> elements) { | |
1229 String packageName = ElementUtils.getPackageName(templateType); | |
1230 List<Element> filteredElements = new ArrayList<>(elements); | |
1231 for (Element element : elements) { | |
1232 Modifier visibility = ElementUtils.getVisibility(element.getModifiers()); | |
1233 if (visibility == Modifier.PRIVATE) { | |
1234 continue; | |
1235 } else if (visibility == null) { | |
1236 String elementPackageName = ElementUtils.getPackageName(element.getEnclosingElement().asType()); | |
1237 if (!Objects.equals(packageName, elementPackageName) && !elementPackageName.equals("java.lang")) { | |
1238 continue; | |
1239 } | |
1240 } | |
1241 | |
1242 filteredElements.add(element); | |
1243 } | |
1244 return filteredElements; | |
1245 } | |
1246 | |
1247 private void initializeGeneric(final NodeData node) { | |
1248 List<SpecializationData> generics = new ArrayList<>(); | |
1249 for (SpecializationData spec : node.getSpecializations()) { | |
1250 if (spec.isFallback()) { | |
1251 generics.add(spec); | |
1252 } | |
1253 } | |
1254 | |
1255 if (generics.size() == 1 && node.getSpecializations().size() == 1) { | |
1256 // TODO this limitation should be lifted | |
1257 for (SpecializationData generic : generics) { | |
1258 generic.addError("@%s defined but no @%s.", Fallback.class.getSimpleName(), Specialization.class.getSimpleName()); | |
1259 } | |
1260 } | |
1261 | |
1262 if (generics.isEmpty()) { | |
1263 node.getSpecializations().add(createGenericSpecialization(node)); | |
1264 } else { | |
1265 if (generics.size() > 1) { | |
1266 for (SpecializationData generic : generics) { | |
1267 generic.addError("Only one @%s is allowed per operation.", Fallback.class.getSimpleName()); | |
1268 } | |
1269 } | |
1270 } | |
1271 } | |
1272 | |
1273 private SpecializationData createGenericSpecialization(final NodeData node) { | |
1274 FallbackParser parser = new FallbackParser(context, node); | |
1275 MethodSpec specification = parser.createDefaultMethodSpec(node.getSpecializations().iterator().next().getMethod(), null, true, null); | |
1276 | |
1277 List<VariableElement> parameterTypes = new ArrayList<>(); | |
1278 int signatureIndex = 1; | |
1279 for (ParameterSpec spec : specification.getRequired()) { | |
1280 parameterTypes.add(new CodeVariableElement(createGenericType(node, spec), "arg" + signatureIndex)); | |
1281 if (spec.isSignature()) { | |
1282 signatureIndex++; | |
1283 } | |
1284 } | |
1285 | |
1286 TypeMirror returnType = createGenericType(node, specification.getReturnType()); | |
1287 SpecializationData generic = parser.create("Generic", TemplateMethod.NO_NATURAL_ORDER, null, null, returnType, parameterTypes); | |
1288 if (generic == null) { | |
1289 throw new RuntimeException("Unable to create generic signature for node " + node.getNodeId() + " with " + parameterTypes + ". Specification " + specification + "."); | |
1290 } | |
1291 | |
1292 return generic; | |
1293 } | |
1294 | |
1295 private TypeMirror createGenericType(NodeData node, ParameterSpec spec) { | |
1296 NodeExecutionData execution = spec.getExecution(); | |
1297 Collection<TypeMirror> allowedTypes; | |
1298 if (execution == null) { | |
1299 allowedTypes = spec.getAllowedTypes(); | |
1300 } else { | |
1301 allowedTypes = Arrays.asList(node.getGenericType(execution)); | |
1302 } | |
1303 if (allowedTypes.size() == 1) { | |
1304 return allowedTypes.iterator().next(); | |
1305 } else { | |
1306 return ElementUtils.getCommonSuperType(context, allowedTypes); | |
1307 } | |
1308 } | |
1309 | |
1310 private static void initializeUninitialized(final NodeData node) { | |
1311 SpecializationData generic = node.getGenericSpecialization(); | |
1312 if (generic == null) { | |
1313 return; | |
1314 } | |
1315 TemplateMethod uninializedMethod = new TemplateMethod("Uninitialized", -1, node, generic.getSpecification(), null, null, generic.getReturnType(), generic.getParameters()); | |
1316 // should not use messages from generic specialization | |
1317 uninializedMethod.getMessages().clear(); | |
1318 node.getSpecializations().add(new SpecializationData(node, uninializedMethod, SpecializationKind.UNINITIALIZED)); | |
1319 } | |
1320 | |
1321 private void initializePolymorphism(NodeData node) { | |
1322 if (!node.needsRewrites(context)) { | |
1323 return; | |
1324 } | |
1325 | |
1326 SpecializationData generic = node.getGenericSpecialization(); | |
1327 List<VariableElement> types = new ArrayList<>(); | |
1328 | |
1329 Collection<TypeMirror> frameTypes = new HashSet<>(); | |
1330 for (SpecializationData specialization : node.getSpecializations()) { | |
1331 if (specialization.getFrame() != null) { | |
1332 frameTypes.add(specialization.getFrame().getType()); | |
1333 } | |
1334 } | |
1335 if (node.supportsFrame()) { | |
1336 frameTypes.add(node.getFrameType()); | |
1337 } | |
1338 | |
1339 if (!frameTypes.isEmpty()) { | |
1340 frameTypes = ElementUtils.uniqueSortedTypes(frameTypes, false); | |
1341 TypeMirror frameType; | |
1342 if (frameTypes.size() == 1) { | |
1343 frameType = frameTypes.iterator().next(); | |
1344 } else { | |
1345 frameType = context.getType(Frame.class); | |
1346 } | |
1347 types.add(new CodeVariableElement(frameType, TemplateMethod.FRAME_NAME)); | |
1348 } | |
1349 | |
1350 TypeMirror returnType = null; | |
1351 int index = 0; | |
1352 for (Parameter genericParameter : generic.getReturnTypeAndParameters()) { | |
1353 TypeMirror polymorphicType; | |
1354 if (genericParameter.getLocalName().equals(TemplateMethod.FRAME_NAME)) { | |
1355 continue; | |
1356 } | |
1357 boolean isReturnParameter = genericParameter == generic.getReturnType(); | |
1358 if (!genericParameter.getSpecification().isSignature()) { | |
1359 polymorphicType = genericParameter.getType(); | |
1360 } else { | |
1361 Collection<TypeMirror> usedTypes = new HashSet<>(); | |
1362 for (SpecializationData specialization : node.getSpecializations()) { | |
1363 if (specialization.isUninitialized()) { | |
1364 continue; | |
1365 } | |
1366 Parameter parameter = specialization.findParameter(genericParameter.getLocalName()); | |
1367 if (parameter == specialization.getReturnType() && specialization.isFallback() && specialization.getMethod() == null) { | |
1368 continue; | |
1369 } | |
1370 if (parameter == null) { | |
1371 throw new AssertionError("Parameter existed in generic specialization but not in specialized. param = " + genericParameter.getLocalName()); | |
1372 } | |
1373 usedTypes.add(parameter.getType()); | |
1374 } | |
1375 usedTypes = ElementUtils.uniqueSortedTypes(usedTypes, false); | |
1376 | |
1377 if (usedTypes.size() == 1) { | |
1378 polymorphicType = usedTypes.iterator().next(); | |
1379 } else { | |
1380 polymorphicType = ElementUtils.getCommonSuperType(context, usedTypes); | |
1381 } | |
1382 | |
1383 NodeExecutionData execution = genericParameter.getSpecification().getExecution(); | |
1384 if (execution != null && !ElementUtils.isSubtypeBoxed(context, polymorphicType, node.getGenericType(execution))) { | |
1385 throw new AssertionError(String.format("Polymorphic types %s not compatible to generic type %s.", polymorphicType, node.getGenericType(execution))); | |
1386 } | |
1387 | |
1388 } | |
1389 if (isReturnParameter) { | |
1390 returnType = polymorphicType; | |
1391 } else { | |
1392 types.add(new CodeVariableElement(polymorphicType, "param" + index)); | |
1393 } | |
1394 index++; | |
1395 } | |
1396 | |
1397 SpecializationMethodParser parser = new SpecializationMethodParser(context, node); | |
1398 SpecializationData polymorphic = parser.create("Polymorphic", TemplateMethod.NO_NATURAL_ORDER, null, null, returnType, types); | |
1399 if (polymorphic == null) { | |
1400 throw new AssertionError("Failed to parse polymorphic signature. " + parser.createDefaultMethodSpec(null, null, false, null) + " Types: " + returnType + " - " + types); | |
1401 } | |
1402 | |
1403 polymorphic.setKind(SpecializationKind.POLYMORPHIC); | |
1404 node.getSpecializations().add(polymorphic); | |
1405 } | |
1406 | |
1407 private void initializeShortCircuits(NodeData node) { | |
1408 Map<String, List<ShortCircuitData>> groupedShortCircuits = groupShortCircuits(node.getShortCircuits()); | |
1409 | |
1410 boolean valid = true; | |
1411 List<NodeExecutionData> shortCircuitExecutions = new ArrayList<>(); | |
1412 for (NodeExecutionData execution : node.getChildExecutions()) { | |
1413 if (!execution.isShortCircuit()) { | |
1414 continue; | |
1415 } | |
1416 shortCircuitExecutions.add(execution); | |
1417 String valueName = execution.getIndexedName(); | |
1418 List<ShortCircuitData> availableCircuits = groupedShortCircuits.get(valueName); | |
1419 | |
1420 if (availableCircuits == null || availableCircuits.isEmpty()) { | |
1421 node.addError("@%s method for short cut value '%s' required.", ShortCircuit.class.getSimpleName(), valueName); | |
1422 valid = false; | |
1423 continue; | |
1424 } | |
1425 | |
1426 boolean sameMethodName = true; | |
1427 String methodName = availableCircuits.get(0).getMethodName(); | |
1428 for (ShortCircuitData circuit : availableCircuits) { | |
1429 if (!circuit.getMethodName().equals(methodName)) { | |
1430 sameMethodName = false; | |
1431 } | |
1432 } | |
1433 | |
1434 if (!sameMethodName) { | |
1435 for (ShortCircuitData circuit : availableCircuits) { | |
1436 circuit.addError("All short circuits for short cut value '%s' must have the same method name.", valueName); | |
1437 } | |
1438 valid = false; | |
1439 continue; | |
1440 } | |
1441 | |
1442 ShortCircuitData genericCircuit = null; | |
1443 for (ShortCircuitData circuit : availableCircuits) { | |
1444 if (isGenericShortCutMethod(circuit)) { | |
1445 genericCircuit = circuit; | |
1446 break; | |
1447 } | |
1448 } | |
1449 | |
1450 if (genericCircuit == null) { | |
1451 node.addError("No generic @%s method available for short cut value '%s'.", ShortCircuit.class.getSimpleName(), valueName); | |
1452 valid = false; | |
1453 continue; | |
1454 } | |
1455 | |
1456 for (ShortCircuitData circuit : availableCircuits) { | |
1457 if (circuit != genericCircuit) { | |
1458 circuit.setGenericShortCircuitMethod(genericCircuit); | |
1459 } | |
1460 } | |
1461 } | |
1462 | |
1463 if (!valid) { | |
1464 return; | |
1465 } | |
1466 | |
1467 List<SpecializationData> specializations = new ArrayList<>(); | |
1468 specializations.addAll(node.getSpecializations()); | |
1469 for (SpecializationData specialization : specializations) { | |
1470 List<ShortCircuitData> assignedShortCuts = new ArrayList<>(shortCircuitExecutions.size()); | |
1471 | |
1472 for (NodeExecutionData shortCircuit : shortCircuitExecutions) { | |
1473 List<ShortCircuitData> availableShortCuts = groupedShortCircuits.get(shortCircuit.getIndexedName()); | |
1474 | |
1475 ShortCircuitData genericShortCircuit = null; | |
1476 ShortCircuitData compatibleShortCircuit = null; | |
1477 for (ShortCircuitData circuit : availableShortCuts) { | |
1478 if (circuit.isGeneric()) { | |
1479 genericShortCircuit = circuit; | |
1480 } else if (circuit.isCompatibleTo(specialization)) { | |
1481 compatibleShortCircuit = circuit; | |
1482 } | |
1483 } | |
1484 | |
1485 if (compatibleShortCircuit == null) { | |
1486 compatibleShortCircuit = genericShortCircuit; | |
1487 } | |
1488 assignedShortCuts.add(compatibleShortCircuit); | |
1489 } | |
1490 specialization.setShortCircuits(assignedShortCuts); | |
1491 } | |
1492 } | |
1493 | |
1494 private boolean isGenericShortCutMethod(ShortCircuitData method) { | |
1495 for (Parameter parameter : method.getParameters()) { | |
1496 NodeExecutionData execution = parameter.getSpecification().getExecution(); | |
1497 if (execution == null) { | |
1498 continue; | |
1499 } | |
1500 ExecutableTypeData found = null; | |
1501 List<ExecutableTypeData> executableElements = execution.getChild().findGenericExecutableTypes(context); | |
1502 for (ExecutableTypeData executable : executableElements) { | |
1503 if (ElementUtils.typeEquals(executable.getReturnType(), parameter.getType())) { | |
1504 found = executable; | |
1505 break; | |
1506 } | |
1507 } | |
1508 if (found == null) { | |
1509 return false; | |
1510 } | |
1511 } | |
1512 return true; | |
1513 } | |
1514 | |
1515 private static Map<String, List<ShortCircuitData>> groupShortCircuits(List<ShortCircuitData> shortCircuits) { | |
1516 Map<String, List<ShortCircuitData>> group = new HashMap<>(); | |
1517 for (ShortCircuitData shortCircuit : shortCircuits) { | |
1518 List<ShortCircuitData> circuits = group.get(shortCircuit.getValueName()); | |
1519 if (circuits == null) { | |
1520 circuits = new ArrayList<>(); | |
1521 group.put(shortCircuit.getValueName(), circuits); | |
1522 } | |
1523 circuits.add(shortCircuit); | |
1524 } | |
1525 return group; | |
1526 } | |
1527 | |
1528 private static boolean verifySpecializationSameLength(NodeData nodeData) { | |
1529 int lastArgs = -1; | |
1530 for (SpecializationData specializationData : nodeData.getSpecializations()) { | |
1531 int signatureArgs = specializationData.getSignatureSize(); | |
1532 if (lastArgs == signatureArgs) { | |
1533 continue; | |
1534 } | |
1535 if (lastArgs != -1) { | |
1536 for (SpecializationData specialization : nodeData.getSpecializations()) { | |
1537 specialization.addError("All specializations must have the same number of arguments."); | |
1538 } | |
1539 return false; | |
1540 } else { | |
1541 lastArgs = signatureArgs; | |
1542 } | |
1543 } | |
1544 return true; | |
1545 } | |
1546 | |
1547 private static void verifyVisibilities(NodeData node) { | |
1548 if (node.getTemplateType().getModifiers().contains(Modifier.PRIVATE) && node.getSpecializations().size() > 0) { | |
1549 node.addError("Classes containing a @%s annotation must not be private.", Specialization.class.getSimpleName()); | |
1550 } | |
1551 } | |
1552 | |
1553 private static void verifyMissingAbstractMethods(NodeData nodeData, List<? extends Element> originalElements) { | |
1554 if (!nodeData.needsFactory()) { | |
1555 // missing abstract methods only needs to be implemented | |
1556 // if we need go generate factory for it. | |
1557 return; | |
1558 } | |
1559 | |
1560 List<Element> elements = new ArrayList<>(originalElements); | |
1561 Set<Element> unusedElements = new HashSet<>(elements); | |
1562 for (ExecutableElement method : nodeData.getAllTemplateMethods()) { | |
1563 unusedElements.remove(method); | |
1564 } | |
1565 | |
1566 for (NodeFieldData field : nodeData.getFields()) { | |
1567 if (field.getGetter() != null) { | |
1568 unusedElements.remove(field.getGetter()); | |
1569 } | |
1570 } | |
1571 | |
1572 for (NodeChildData child : nodeData.getChildren()) { | |
1573 if (child.getAccessElement() != null) { | |
1574 unusedElements.remove(child.getAccessElement()); | |
1575 } | |
1576 } | |
1577 | |
1578 for (ExecutableElement unusedMethod : ElementFilter.methodsIn(unusedElements)) { | |
1579 if (unusedMethod.getModifiers().contains(Modifier.ABSTRACT)) { | |
1580 nodeData.addError("The type %s must implement the inherited abstract method %s.", ElementUtils.getSimpleName(nodeData.getTemplateType()), | |
1581 ElementUtils.getReadableSignature(unusedMethod)); | |
1582 } | |
1583 } | |
1584 } | |
1585 | |
1586 private static void verifyNamingConvention(List<? extends TemplateMethod> methods, String prefix) { | |
1587 for (int i = 0; i < methods.size(); i++) { | |
1588 TemplateMethod m1 = methods.get(i); | |
1589 if (m1.getMethodName().length() < 3 || !m1.getMethodName().startsWith(prefix)) { | |
1590 m1.addError("Naming convention: method name must start with '%s'.", prefix); | |
1591 } | |
1592 } | |
1593 } | |
1594 | |
1595 private static void verifySpecializationThrows(NodeData node) { | |
1596 Map<String, SpecializationData> specializationMap = new HashMap<>(); | |
1597 for (SpecializationData spec : node.getSpecializations()) { | |
1598 specializationMap.put(spec.getMethodName(), spec); | |
1599 } | |
1600 for (SpecializationData sourceSpecialization : node.getSpecializations()) { | |
1601 if (sourceSpecialization.getExceptions() != null) { | |
1602 for (SpecializationThrowsData throwsData : sourceSpecialization.getExceptions()) { | |
1603 for (SpecializationThrowsData otherThrowsData : sourceSpecialization.getExceptions()) { | |
1604 if (otherThrowsData != throwsData && ElementUtils.typeEquals(otherThrowsData.getJavaClass(), throwsData.getJavaClass())) { | |
1605 throwsData.addError("Duplicate exception type."); | |
1606 } | |
1607 } | |
1608 } | |
1609 } | |
1610 } | |
1611 } | |
1612 | |
1613 private static void verifyConstructors(NodeData nodeData) { | |
1614 List<ExecutableElement> constructors = ElementFilter.constructorsIn(nodeData.getTemplateType().getEnclosedElements()); | |
1615 if (constructors.isEmpty()) { | |
1616 return; | |
1617 } | |
1618 | |
1619 boolean oneNonPrivate = false; | |
1620 for (ExecutableElement constructor : constructors) { | |
1621 if (ElementUtils.getVisibility(constructor.getModifiers()) != Modifier.PRIVATE) { | |
1622 oneNonPrivate = true; | |
1623 break; | |
1624 } | |
1625 } | |
1626 if (!oneNonPrivate && !nodeData.getTemplateType().getModifiers().contains(Modifier.PRIVATE)) { | |
1627 nodeData.addError("At least one constructor must be non-private."); | |
1628 } | |
1629 } | |
1630 | |
1631 private AnnotationMirror findFirstAnnotation(List<? extends Element> elements, Class<? extends Annotation> annotation) { | |
1632 for (Element element : elements) { | |
1633 AnnotationMirror mirror = ElementUtils.findAnnotationMirror(processingEnv, element, annotation); | |
1634 if (mirror != null) { | |
1635 return mirror; | |
1636 } | |
1637 } | |
1638 return null; | |
1639 } | |
1640 | |
1641 private TypeMirror inheritType(AnnotationMirror annotation, String valueName, TypeMirror parentType) { | |
1642 TypeMirror inhertNodeType = context.getTruffleTypes().getNode(); | |
1643 TypeMirror value = ElementUtils.getAnnotationValue(TypeMirror.class, annotation, valueName); | |
1644 if (ElementUtils.typeEquals(inhertNodeType, value)) { | |
1645 return parentType; | |
1646 } else { | |
1647 return value; | |
1648 } | |
1649 } | |
1650 | |
1651 private ExecutableElement findGetter(List<? extends Element> elements, String variableName, TypeMirror type) { | |
1652 if (type == null) { | |
1653 return null; | |
1654 } | |
1655 String methodName; | |
1656 if (ElementUtils.typeEquals(type, context.getType(boolean.class))) { | |
1657 methodName = "is" + ElementUtils.firstLetterUpperCase(variableName); | |
1658 } else { | |
1659 methodName = "get" + ElementUtils.firstLetterUpperCase(variableName); | |
1660 } | |
1661 | |
1662 for (ExecutableElement method : ElementFilter.methodsIn(elements)) { | |
1663 if (method.getSimpleName().toString().equals(methodName) && method.getParameters().size() == 0 && ElementUtils.isAssignable(type, method.getReturnType())) { | |
1664 return method; | |
1665 } | |
1666 } | |
1667 return null; | |
1668 } | |
1669 | |
1670 private static List<TypeElement> collectSuperClasses(List<TypeElement> collection, TypeElement element) { | |
1671 if (element != null) { | |
1672 collection.add(element); | |
1673 if (element.getSuperclass() != null) { | |
1674 collectSuperClasses(collection, ElementUtils.fromTypeMirror(element.getSuperclass())); | |
1675 } | |
1676 } | |
1677 return collection; | |
1678 } | |
1679 | |
1680 } |