comparison graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.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/node/NodeParser.java@bd28da642eea
children 9f38d222fa6c
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.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.dsl.*;
34 import com.oracle.truffle.api.nodes.*;
35 import com.oracle.truffle.dsl.processor.*;
36 import com.oracle.truffle.dsl.processor.java.*;
37 import com.oracle.truffle.dsl.processor.java.compiler.*;
38 import com.oracle.truffle.dsl.processor.model.*;
39 import com.oracle.truffle.dsl.processor.model.NodeChildData.Cardinality;
40 import com.oracle.truffle.dsl.processor.model.SpecializationData.SpecializationKind;
41 import com.oracle.truffle.dsl.processor.model.TemplateMethod.TypeSignature;
42
43 public class NodeParser extends AbstractParser<NodeData> {
44
45 public static final List<Class<? extends Annotation>> ANNOTATIONS = Arrays.asList(Generic.class, TypeSystemReference.class, ShortCircuit.class, Specialization.class, NodeChild.class,
46 NodeChildren.class);
47
48 private Map<String, NodeData> parsedNodes;
49
50 @Override
51 protected NodeData parse(Element element, AnnotationMirror mirror) {
52 NodeData node = null;
53 try {
54 parsedNodes = new HashMap<>();
55 node = resolveNode((TypeElement) element);
56 if (Log.DEBUG) {
57 NodeData parsed = parsedNodes.get(ElementUtils.getQualifiedName((TypeElement) element));
58 if (node != null) {
59 String dump = parsed.dump();
60 log.message(Kind.ERROR, null, null, null, dump);
61 }
62 }
63 } finally {
64 parsedNodes = null;
65 }
66
67 return node;
68 }
69
70 @Override
71 protected NodeData filterErrorElements(NodeData model) {
72 for (Iterator<NodeData> iterator = model.getEnclosingNodes().iterator(); iterator.hasNext();) {
73 NodeData node = filterErrorElements(iterator.next());
74 if (node == null) {
75 iterator.remove();
76 }
77 }
78 if (model.hasErrors()) {
79 return null;
80 }
81 return model;
82 }
83
84 @Override
85 public boolean isDelegateToRootDeclaredType() {
86 return true;
87 }
88
89 @Override
90 public Class<? extends Annotation> getAnnotationType() {
91 return null;
92 }
93
94 @Override
95 public List<Class<? extends Annotation>> getTypeDelegatedAnnotationTypes() {
96 return ANNOTATIONS;
97 }
98
99 private NodeData resolveNode(TypeElement rootType) {
100 String typeName = ElementUtils.getQualifiedName(rootType);
101 if (parsedNodes.containsKey(typeName)) {
102 return parsedNodes.get(typeName);
103 }
104
105 List<NodeData> enclosedNodes = new ArrayList<>();
106 for (TypeElement enclosedType : ElementFilter.typesIn(rootType.getEnclosedElements())) {
107 NodeData enclosedChild = resolveNode(enclosedType);
108 if (enclosedChild != null) {
109 enclosedNodes.add(enclosedChild);
110 }
111 }
112
113 NodeData node = parseNode(rootType);
114 if (node == null && !enclosedNodes.isEmpty()) {
115 node = new NodeData(context, rootType);
116 }
117
118 if (node != null) {
119 for (NodeData enclosedNode : enclosedNodes) {
120 node.addEnclosedNode(enclosedNode);
121 }
122 }
123
124 parsedNodes.put(typeName, node);
125 return node;
126 }
127
128 private NodeData parseNode(TypeElement originalTemplateType) {
129 // reloading the type elements is needed for ecj
130 TypeElement templateType = ElementUtils.fromTypeMirror(context.reloadTypeElement(originalTemplateType));
131
132 if (ElementUtils.findAnnotationMirror(processingEnv, originalTemplateType, GeneratedBy.class) != null) {
133 // generated nodes should not get called again.
134 return null;
135 }
136
137 List<TypeElement> lookupTypes = collectSuperClasses(new ArrayList<TypeElement>(), templateType);
138 if (!ElementUtils.isAssignable(templateType.asType(), context.getTruffleTypes().getNode())) {
139 return null;
140 }
141 List<? extends Element> elements = CompilerFactory.getCompiler(templateType).getAllMembersInDeclarationOrder(context.getEnvironment(), templateType);
142
143 NodeData node = parseNodeData(templateType, elements, lookupTypes);
144 if (node.hasErrors()) {
145 return node; // error sync point
146 }
147
148 initializeChildren(node);
149
150 node.getSpecializations().addAll(new SpecializationMethodParser(context, node).parse(elements));
151 node.getSpecializations().addAll(new GenericParser(context, node).parse(elements));
152 node.getCasts().addAll(new CreateCastParser(context, node).parse(elements));
153 node.getShortCircuits().addAll(new ShortCircuitParser(context, node).parse(elements));
154
155 if (node.hasErrors()) {
156 return node; // error sync point
157 }
158
159 verifySpecializationSameLength(node);
160 initializeSpecializations(elements, node);
161 initializeShortCircuits(node); // requires specializations and polymorphic specializations
162
163 verifyVisibilities(node);
164 verifyMissingAbstractMethods(node, elements);
165 verifyConstructors(node);
166 verifyNamingConvention(node.getShortCircuits(), "needs");
167 verifySpecializationThrows(node);
168 return node;
169 }
170
171 private NodeData parseNodeData(TypeElement templateType, List<? extends Element> elements, List<TypeElement> typeHierarchy) {
172 AnnotationMirror typeSystemMirror = findFirstAnnotation(typeHierarchy, TypeSystemReference.class);
173 if (typeSystemMirror == null) {
174 NodeData nodeData = new NodeData(context, templateType);
175 nodeData.addError("No @%s annotation found in type hierarchy of %s.", TypeSystemReference.class.getSimpleName(), ElementUtils.getQualifiedName(templateType));
176 return nodeData;
177 }
178
179 TypeMirror typeSystemType = ElementUtils.getAnnotationValue(TypeMirror.class, typeSystemMirror, "value");
180 final TypeSystemData typeSystem = (TypeSystemData) context.getTemplate(typeSystemType, true);
181 if (typeSystem == null) {
182 NodeData nodeData = new NodeData(context, templateType);
183 nodeData.addError("The used type system '%s' is invalid or not a Node.", ElementUtils.getQualifiedName(typeSystemType));
184 return nodeData;
185 }
186
187 List<String> assumptionsList = new ArrayList<>();
188 for (int i = typeHierarchy.size() - 1; i >= 0; i--) {
189 TypeElement type = typeHierarchy.get(i);
190 AnnotationMirror assumptions = ElementUtils.findAnnotationMirror(context.getEnvironment(), type, NodeAssumptions.class);
191 if (assumptions != null) {
192 List<String> assumptionStrings = ElementUtils.getAnnotationValueList(String.class, assumptions, "value");
193 for (String string : assumptionStrings) {
194 if (assumptionsList.contains(string)) {
195 assumptionsList.remove(string);
196 }
197 assumptionsList.add(string);
198 }
199 }
200 }
201 AnnotationMirror nodeInfoMirror = findFirstAnnotation(typeHierarchy, NodeInfo.class);
202 String shortName = null;
203 if (nodeInfoMirror != null) {
204 shortName = ElementUtils.getAnnotationValue(String.class, nodeInfoMirror, "shortName");
205 }
206
207 List<NodeFieldData> fields = parseFields(typeHierarchy, elements);
208 List<NodeChildData> children = parseChildren(typeHierarchy, elements);
209 List<NodeExecutionData> executions = parseExecutions(children, elements);
210
211 NodeData nodeData = new NodeData(context, templateType, shortName, typeSystem, children, executions, fields, assumptionsList);
212 nodeData.setExecutableTypes(groupExecutableTypes(new ExecutableTypeMethodParser(context, nodeData).parse(elements)));
213
214 parsedNodes.put(ElementUtils.getQualifiedName(templateType), nodeData);
215
216 return nodeData;
217 }
218
219 private List<NodeFieldData> parseFields(List<TypeElement> typeHierarchy, List<? extends Element> elements) {
220 Set<String> names = new HashSet<>();
221
222 List<NodeFieldData> fields = new ArrayList<>();
223 for (VariableElement field : ElementFilter.fieldsIn(elements)) {
224 if (field.getModifiers().contains(Modifier.STATIC)) {
225 continue;
226 }
227 if (field.getModifiers().contains(Modifier.PUBLIC) || field.getModifiers().contains(Modifier.PROTECTED)) {
228 String name = field.getSimpleName().toString();
229 fields.add(new NodeFieldData(field, null, field.asType(), name, false));
230 names.add(name);
231 }
232 }
233
234 List<TypeElement> reversedTypeHierarchy = new ArrayList<>(typeHierarchy);
235 Collections.reverse(reversedTypeHierarchy);
236 for (TypeElement typeElement : reversedTypeHierarchy) {
237 AnnotationMirror nodeChildrenMirror = ElementUtils.findAnnotationMirror(processingEnv, typeElement, NodeFields.class);
238 List<AnnotationMirror> children = ElementUtils.collectAnnotations(context, nodeChildrenMirror, "value", typeElement, NodeField.class);
239
240 for (AnnotationMirror mirror : children) {
241 String name = ElementUtils.firstLetterLowerCase(ElementUtils.getAnnotationValue(String.class, mirror, "name"));
242 TypeMirror type = ElementUtils.getAnnotationValue(TypeMirror.class, mirror, "type");
243
244 NodeFieldData field = new NodeFieldData(typeElement, mirror, type, name, true);
245 if (name.isEmpty()) {
246 field.addError(ElementUtils.getAnnotationValue(mirror, "name"), "Field name cannot be empty.");
247 } else if (names.contains(name)) {
248 field.addError(ElementUtils.getAnnotationValue(mirror, "name"), "Duplicate field name '%s'.", name);
249 }
250 names.add(name);
251
252 fields.add(field);
253 }
254 }
255
256 for (NodeFieldData nodeFieldData : fields) {
257 nodeFieldData.setGetter(findGetter(elements, nodeFieldData.getName(), nodeFieldData.getType()));
258 }
259
260 return fields;
261 }
262
263 private List<NodeChildData> parseChildren(final List<TypeElement> typeHierarchy, List<? extends Element> elements) {
264 Set<String> shortCircuits = new HashSet<>();
265 for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
266 AnnotationMirror mirror = ElementUtils.findAnnotationMirror(processingEnv, method, ShortCircuit.class);
267 if (mirror != null) {
268 shortCircuits.add(ElementUtils.getAnnotationValue(String.class, mirror, "value"));
269 }
270 }
271 Map<String, TypeMirror> castNodeTypes = new HashMap<>();
272 for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
273 AnnotationMirror mirror = ElementUtils.findAnnotationMirror(processingEnv, method, CreateCast.class);
274 if (mirror != null) {
275 List<String> children = (ElementUtils.getAnnotationValueList(String.class, mirror, "value"));
276 if (children != null) {
277 for (String child : children) {
278 castNodeTypes.put(child, method.getReturnType());
279 }
280 }
281 }
282 }
283
284 List<NodeChildData> parsedChildren = new ArrayList<>();
285 List<TypeElement> typeHierarchyReversed = new ArrayList<>(typeHierarchy);
286 Collections.reverse(typeHierarchyReversed);
287 for (TypeElement type : typeHierarchyReversed) {
288 AnnotationMirror nodeChildrenMirror = ElementUtils.findAnnotationMirror(processingEnv, type, NodeChildren.class);
289
290 TypeMirror nodeClassType = type.getSuperclass();
291 if (!ElementUtils.isAssignable(nodeClassType, context.getTruffleTypes().getNode())) {
292 nodeClassType = null;
293 }
294
295 List<AnnotationMirror> children = ElementUtils.collectAnnotations(context, nodeChildrenMirror, "value", type, NodeChild.class);
296 int index = 0;
297 for (AnnotationMirror childMirror : children) {
298 String name = ElementUtils.getAnnotationValue(String.class, childMirror, "value");
299 if (name.equals("")) {
300 name = "child" + index;
301 }
302
303 Cardinality cardinality = Cardinality.ONE;
304
305 TypeMirror childType = inheritType(childMirror, "type", nodeClassType);
306 if (childType.getKind() == TypeKind.ARRAY) {
307 cardinality = Cardinality.MANY;
308 }
309
310 TypeMirror originalChildType = childType;
311 TypeMirror castNodeType = castNodeTypes.get(name);
312 if (castNodeType != null) {
313 childType = castNodeType;
314 }
315
316 Element getter = findGetter(elements, name, childType);
317
318 NodeChildData nodeChild = new NodeChildData(type, childMirror, name, childType, originalChildType, getter, cardinality);
319
320 parsedChildren.add(nodeChild);
321
322 if (nodeChild.getNodeType() == null) {
323 nodeChild.addError("No valid node type could be resoleved.");
324 }
325 if (nodeChild.hasErrors()) {
326 continue;
327 }
328
329 index++;
330 }
331 }
332
333 List<NodeChildData> filteredChildren = new ArrayList<>();
334 Set<String> encounteredNames = new HashSet<>();
335 for (int i = parsedChildren.size() - 1; i >= 0; i--) {
336 NodeChildData child = parsedChildren.get(i);
337 if (!encounteredNames.contains(child.getName())) {
338 filteredChildren.add(0, child);
339 encounteredNames.add(child.getName());
340 }
341 }
342
343 for (NodeChildData child : filteredChildren) {
344 List<String> executeWithStrings = ElementUtils.getAnnotationValueList(String.class, child.getMessageAnnotation(), "executeWith");
345 AnnotationValue executeWithValue = ElementUtils.getAnnotationValue(child.getMessageAnnotation(), "executeWith");
346 List<NodeChildData> executeWith = new ArrayList<>();
347 for (String executeWithString : executeWithStrings) {
348
349 if (child.getName().equals(executeWithString)) {
350 child.addError(executeWithValue, "The child node '%s' cannot be executed with itself.", executeWithString);
351 continue;
352 }
353
354 NodeChildData found = null;
355 boolean before = true;
356 for (NodeChildData resolveChild : filteredChildren) {
357 if (resolveChild == child) {
358 before = false;
359 continue;
360 }
361 if (resolveChild.getName().equals(executeWithString)) {
362 found = resolveChild;
363 break;
364 }
365 }
366
367 if (found == null) {
368 child.addError(executeWithValue, "The child node '%s' cannot be executed with '%s'. The child node was not found.", child.getName(), executeWithString);
369 continue;
370 } else if (!before) {
371 child.addError(executeWithValue, "The child node '%s' cannot be executed with '%s'. The node %s is executed after the current node.", child.getName(), executeWithString,
372 executeWithString);
373 continue;
374 }
375 executeWith.add(found);
376 }
377 child.setExecuteWith(executeWith);
378 if (child.getNodeData() == null) {
379 continue;
380 }
381 }
382
383 return filteredChildren;
384 }
385
386 private List<NodeExecutionData> parseExecutions(List<NodeChildData> children, List<? extends Element> elements) {
387 if (children == null) {
388 return null;
389 }
390
391 // pre-parse short circuits
392 Set<String> shortCircuits = new HashSet<>();
393 List<ExecutableElement> methods = ElementFilter.methodsIn(elements);
394 for (ExecutableElement method : methods) {
395 AnnotationMirror mirror = ElementUtils.findAnnotationMirror(processingEnv, method, ShortCircuit.class);
396 if (mirror != null) {
397 shortCircuits.add(ElementUtils.getAnnotationValue(String.class, mirror, "value"));
398 }
399 }
400
401 boolean hasVarArgs = false;
402 int maxSignatureSize = 0;
403 if (!children.isEmpty()) {
404 int lastIndex = children.size() - 1;
405 hasVarArgs = children.get(lastIndex).getCardinality() == Cardinality.MANY;
406 if (hasVarArgs) {
407 maxSignatureSize = lastIndex;
408 } else {
409 maxSignatureSize = children.size();
410 }
411 }
412
413 // pre-parse specializations
414 for (ExecutableElement method : methods) {
415 AnnotationMirror mirror = ElementUtils.findAnnotationMirror(processingEnv, method, Specialization.class);
416 if (mirror == null) {
417 continue;
418 }
419
420 int currentArgumentCount = 0;
421 boolean skipShortCircuit = false;
422 for (VariableElement var : method.getParameters()) {
423 TypeMirror type = var.asType();
424 if (currentArgumentCount == 0) {
425 // skip optionals
426 if (ElementUtils.typeEquals(type, context.getTruffleTypes().getFrame())) {
427 continue;
428 }
429 // TODO skip optional fields?
430 }
431 int childIndex = currentArgumentCount < children.size() ? currentArgumentCount : children.size() - 1;
432 if (childIndex == -1) {
433 continue;
434 }
435 if (!skipShortCircuit) {
436 NodeChildData child = children.get(childIndex);
437 if (shortCircuits.contains(NodeExecutionData.createShortCircuitId(child, currentArgumentCount - childIndex))) {
438 skipShortCircuit = true;
439 continue;
440 }
441 } else {
442 skipShortCircuit = false;
443 }
444
445 currentArgumentCount++;
446 }
447 maxSignatureSize = Math.max(maxSignatureSize, currentArgumentCount);
448 }
449
450 List<NodeExecutionData> executions = new ArrayList<>();
451 for (int i = 0; i < maxSignatureSize; i++) {
452 int childIndex = i;
453 boolean varArg = false;
454 if (childIndex >= children.size() - 1) {
455 if (hasVarArgs) {
456 childIndex = children.size() - 1;
457 varArg = hasVarArgs;
458 } else if (childIndex >= children.size()) {
459 break;
460 }
461 }
462 int varArgsIndex = varArg ? Math.abs(childIndex - i) : -1;
463 NodeChildData child = children.get(childIndex);
464 boolean shortCircuit = shortCircuits.contains(NodeExecutionData.createShortCircuitId(child, varArgsIndex));
465 executions.add(new NodeExecutionData(child, varArgsIndex, shortCircuit));
466 }
467 return executions;
468 }
469
470 private static Map<Integer, List<ExecutableTypeData>> groupExecutableTypes(List<ExecutableTypeData> executableTypes) {
471 Map<Integer, List<ExecutableTypeData>> groupedTypes = new TreeMap<>();
472 for (ExecutableTypeData type : executableTypes) {
473 int evaluatedCount = type.getEvaluatedCount();
474
475 List<ExecutableTypeData> types = groupedTypes.get(evaluatedCount);
476 if (types == null) {
477 types = new ArrayList<>();
478 groupedTypes.put(evaluatedCount, types);
479 }
480 types.add(type);
481 }
482
483 for (List<ExecutableTypeData> types : groupedTypes.values()) {
484 Collections.sort(types);
485 }
486 return groupedTypes;
487 }
488
489 private void initializeChildren(NodeData node) {
490 for (NodeChildData nodeChild : node.getChildren()) {
491 NodeData fieldNodeData = resolveNode(ElementUtils.fromTypeMirror(nodeChild.getNodeType()));
492 nodeChild.setNode(fieldNodeData);
493 if (fieldNodeData == null) {
494 nodeChild.addError("Node type '%s' is invalid or not a valid Node.", ElementUtils.getQualifiedName(nodeChild.getNodeType()));
495 } else if (!ElementUtils.typeEquals(fieldNodeData.getTypeSystem().getTemplateType().asType(), (node.getTypeSystem().getTemplateType().asType()))) {
496 nodeChild.addError("The @%s of the node and the @%s of the @%s does not match. %s != %s. ", TypeSystem.class.getSimpleName(), TypeSystem.class.getSimpleName(),
497 NodeChild.class.getSimpleName(), ElementUtils.getSimpleName(node.getTypeSystem().getTemplateType()),
498 ElementUtils.getSimpleName(fieldNodeData.getTypeSystem().getTemplateType()));
499 }
500 if (fieldNodeData != null) {
501 List<ExecutableTypeData> types = nodeChild.findGenericExecutableTypes(context);
502 if (types.isEmpty()) {
503 AnnotationValue executeWithValue = ElementUtils.getAnnotationValue(nodeChild.getMessageAnnotation(), "executeWith");
504 nodeChild.addError(executeWithValue, "No generic execute method found with %s evaluated arguments for node type %s.", nodeChild.getExecuteWith().size(),
505 ElementUtils.getSimpleName(nodeChild.getNodeType()));
506 }
507 }
508 }
509 }
510
511 private void initializeSpecializations(List<? extends Element> elements, final NodeData node) {
512 if (node.getSpecializations().isEmpty()) {
513 return;
514 }
515
516 initializeGuards(elements, node);
517 initializeGeneric(node);
518 initializeUninitialized(node);
519 initializeOrder(node);
520 initializePolymorphism(node); // requires specializations
521 initializeReachability(node);
522 initializeContains(node);
523
524 if (!node.hasErrors()) {
525 initializeExceptions(node);
526 }
527 resolveContains(node);
528
529 List<SpecializationData> needsId = new ArrayList<>();
530 for (SpecializationData specialization : node.getSpecializations()) {
531 if (specialization.isGeneric()) {
532 specialization.setId("Generic");
533 } else if (specialization.isUninitialized()) {
534 specialization.setId("Uninitialized");
535 } else if (specialization.isPolymorphic()) {
536 specialization.setId("Polymorphic");
537 } else if (specialization.isSpecialized()) {
538 needsId.add(specialization);
539 } else {
540 throw new AssertionError();
541 }
542 }
543
544 // verify specialization parameter length
545 List<String> ids = initializeSpecializationIds(needsId);
546 for (int i = 0; i < ids.size(); i++) {
547 needsId.get(i).setId(ids.get(i));
548 }
549
550 }
551
552 private static void initializeOrder(NodeData node) {
553 List<SpecializationData> specializations = node.getSpecializations();
554 Collections.sort(specializations);
555
556 for (SpecializationData specialization : specializations) {
557 String searchName = specialization.getInsertBeforeName();
558 if (searchName == null || specialization.getMethod() == null) {
559 continue;
560 }
561 SpecializationData found = lookupSpecialization(node, searchName);
562 if (found == null || found.getMethod() == null) {
563 AnnotationValue value = ElementUtils.getAnnotationValue(specialization.getMarkerAnnotation(), "insertBefore");
564 specialization.addError(value, "The referenced specialization '%s' could not be found.", searchName);
565 continue;
566 }
567
568 ExecutableElement currentMethod = specialization.getMethod();
569 ExecutableElement insertBeforeMethod = found.getMethod();
570
571 TypeMirror currentEnclosedType = currentMethod.getEnclosingElement().asType();
572 TypeMirror insertBeforeEnclosedType = insertBeforeMethod.getEnclosingElement().asType();
573
574 if (ElementUtils.typeEquals(currentEnclosedType, insertBeforeEnclosedType) || !ElementUtils.isSubtype(currentEnclosedType, insertBeforeEnclosedType)) {
575 AnnotationValue value = ElementUtils.getAnnotationValue(specialization.getMarkerAnnotation(), "insertBefore");
576 specialization.addError(value, "Specializations can only be inserted before specializations in superclasses.", searchName);
577 continue;
578 }
579
580 specialization.setInsertBefore(found);
581 }
582
583 int endIndex = specializations.size() - 1;
584 for (int i = endIndex; i >= 0; i--) {
585 SpecializationData specialization = specializations.get(i);
586 if (specialization.isGeneric() || specialization.isPolymorphic()) {
587 endIndex--;
588 continue;
589 }
590
591 SpecializationData insertBefore = specialization.getInsertBefore();
592 if (insertBefore != null) {
593 int insertIndex = specializations.indexOf(insertBefore);
594 if (insertIndex < i) {
595 List<SpecializationData> range = new ArrayList<>(specializations.subList(i, endIndex + 1));
596 specializations.removeAll(range);
597 specializations.addAll(insertIndex, range);
598 }
599 }
600 }
601
602 for (int i = 0; i < specializations.size(); i++) {
603 specializations.get(i).setIndex(i);
604 }
605 }
606
607 private static void initializeExceptions(NodeData node) {
608 List<SpecializationData> specializations = node.getSpecializations();
609 for (int i = 0; i < specializations.size(); i++) {
610 SpecializationData cur = specializations.get(i);
611 if (cur.getExceptions().isEmpty()) {
612 continue;
613 }
614 SpecializationData next = i + 1 < specializations.size() ? specializations.get(i + 1) : null;
615
616 if (!cur.isContainedBy(next)) {
617 // error should be able to contain
618 next.addError("This specialiation is not a valid exceptional rewrite target for %s. To fix this make %s compatible to %s or remove the exceptional rewrite.",
619 cur.createReferenceName(), next.createReferenceName(), cur.createReferenceName());
620 continue;
621 }
622 if (!next.getContains().contains(cur)) {
623 next.getContains().add(cur);
624 // TODO resolve transitive contains
625 }
626 }
627
628 for (SpecializationData cur : specializations) {
629 if (cur.getExceptions().isEmpty()) {
630 continue;
631 }
632 for (SpecializationData child : specializations) {
633 if (child != null && child != cur && child.getContains().contains(cur)) {
634 cur.getExcludedBy().add(child);
635 }
636 }
637 }
638 }
639
640 private static void initializeContains(NodeData node) {
641 for (SpecializationData specialization : node.getSpecializations()) {
642 Set<SpecializationData> resolvedSpecializations = specialization.getContains();
643 resolvedSpecializations.clear();
644 Set<String> includeNames = specialization.getContainsNames();
645 for (String includeName : includeNames) {
646 // TODO reduce complexity of this lookup.
647 SpecializationData foundSpecialization = lookupSpecialization(node, includeName);
648
649 if (foundSpecialization == null) {
650 AnnotationValue value = ElementUtils.getAnnotationValue(specialization.getMarkerAnnotation(), "contains");
651 specialization.addError(value, "The referenced specialization '%s' could not be found.", includeName);
652 } else {
653 if (!foundSpecialization.isContainedBy(specialization)) {
654 AnnotationValue value = ElementUtils.getAnnotationValue(specialization.getMarkerAnnotation(), "contains");
655 if (foundSpecialization.compareTo(specialization) > 0) {
656 specialization.addError(value, "The contained specialization '%s' must be declared before the containing specialization.", includeName);
657 } else {
658 specialization.addError(value,
659 "The contained specialization '%s' is not fully compatible. The contained specialization must be strictly more generic than the containing one.",
660 includeName);
661 }
662
663 }
664 resolvedSpecializations.add(foundSpecialization);
665 }
666 }
667 }
668 }
669
670 private void resolveContains(NodeData node) {
671 // flatten transitive includes
672 for (SpecializationData specialization : node.getSpecializations()) {
673 if (specialization.getContains().isEmpty()) {
674 continue;
675 }
676 Set<SpecializationData> foundSpecializations = new HashSet<>();
677 collectIncludes(specialization, foundSpecializations, new HashSet<SpecializationData>());
678 specialization.getContains().addAll(foundSpecializations);
679 }
680 }
681
682 private static SpecializationData lookupSpecialization(NodeData node, String includeName) {
683 SpecializationData foundSpecialization = null;
684 for (SpecializationData searchSpecialization : node.getSpecializations()) {
685 if (searchSpecialization.getMethodName().equals(includeName)) {
686 foundSpecialization = searchSpecialization;
687 break;
688 }
689 }
690 return foundSpecialization;
691 }
692
693 private void collectIncludes(SpecializationData specialization, Set<SpecializationData> found, Set<SpecializationData> visited) {
694 if (visited.contains(specialization)) {
695 // circle found
696 specialization.addError("Circular contained specialization '%s' found.", specialization.createReferenceName());
697 return;
698 }
699 visited.add(specialization);
700
701 for (SpecializationData included : specialization.getContains()) {
702 collectIncludes(included, found, new HashSet<>(visited));
703 found.add(included);
704 }
705 }
706
707 private static void initializeReachability(final NodeData node) {
708 List<SpecializationData> specializations = node.getSpecializations();
709 for (int i = specializations.size() - 1; i >= 0; i--) {
710 SpecializationData current = specializations.get(i);
711 if (current.isPolymorphic()) {
712 current.setReachable(true);
713 continue;
714 }
715
716 List<SpecializationData> shadowedBy = null;
717 for (int j = i - 1; j >= 0; j--) {
718 SpecializationData prev = specializations.get(j);
719 if (prev.isPolymorphic()) {
720 continue;
721 }
722 if (!current.isReachableAfter(prev)) {
723 if (shadowedBy == null) {
724 shadowedBy = new ArrayList<>();
725 }
726 shadowedBy.add(prev);
727 }
728 }
729
730 if (shadowedBy != null) {
731 StringBuilder name = new StringBuilder();
732 String sep = "";
733 for (SpecializationData shadowSpecialization : shadowedBy) {
734 name.append(sep);
735 name.append(shadowSpecialization.createReferenceName());
736 sep = ", ";
737 }
738 current.addError("%s is not reachable. It is shadowed by %s.", current.isGeneric() ? "Generic" : "Specialization", name);
739 }
740 current.setReachable(shadowedBy == null);
741 }
742 }
743
744 private static List<String> initializeSpecializationIds(List<SpecializationData> specializations) {
745 int lastSize = -1;
746 List<List<String>> signatureChunks = new ArrayList<>();
747 for (SpecializationData other : specializations) {
748 if (!other.isSpecialized()) {
749 continue;
750 }
751 List<String> paramIds = new LinkedList<>();
752 paramIds.add(ElementUtils.getTypeId(other.getReturnType().getType()));
753 for (Parameter param : other.getParameters()) {
754 if (param.getSpecification().getExecution() == null) {
755 continue;
756 }
757 paramIds.add(ElementUtils.getTypeId(param.getType()));
758 }
759 assert lastSize == -1 || lastSize == paramIds.size();
760 if (lastSize != -1 && lastSize != paramIds.size()) {
761 throw new AssertionError();
762 }
763 signatureChunks.add(paramIds);
764 lastSize = paramIds.size();
765 }
766
767 // reduce id vertically
768 for (int i = 0; i < lastSize; i++) {
769 String prev = null;
770 boolean allSame = true;
771 for (List<String> signature : signatureChunks) {
772 String arg = signature.get(i);
773 if (prev == null) {
774 prev = arg;
775 continue;
776 } else if (!prev.equals(arg)) {
777 allSame = false;
778 break;
779 }
780 prev = arg;
781 }
782
783 if (allSame) {
784 for (List<String> signature : signatureChunks) {
785 signature.remove(i);
786 }
787 lastSize--;
788 }
789 }
790
791 // reduce id horizontally
792 for (List<String> signature : signatureChunks) {
793 if (signature.isEmpty()) {
794 continue;
795 }
796 String prev = null;
797 boolean allSame = true;
798 for (String arg : signature) {
799 if (prev == null) {
800 prev = arg;
801 continue;
802 } else if (!prev.equals(arg)) {
803 allSame = false;
804 break;
805 }
806 prev = arg;
807 }
808
809 if (allSame) {
810 signature.clear();
811 signature.add(prev);
812 }
813 }
814
815 // create signatures
816 List<String> signatures = new ArrayList<>();
817 for (List<String> signatureChunk : signatureChunks) {
818 StringBuilder b = new StringBuilder();
819 if (signatureChunk.isEmpty()) {
820 b.append("Default");
821 } else {
822 for (String s : signatureChunk) {
823 b.append(s);
824 }
825 }
826 signatures.add(b.toString());
827 }
828
829 Map<String, Integer> counts = new HashMap<>();
830 for (String s1 : signatures) {
831 Integer count = counts.get(s1);
832 if (count == null) {
833 count = 0;
834 }
835 count++;
836 counts.put(s1, count);
837 }
838
839 for (String s : counts.keySet()) {
840 int count = counts.get(s);
841 if (count > 1) {
842 int number = 0;
843 for (ListIterator<String> iterator = signatures.listIterator(); iterator.hasNext();) {
844 String s2 = iterator.next();
845 if (s.equals(s2)) {
846 iterator.set(s2 + number);
847 number++;
848 }
849 }
850 }
851 }
852
853 return signatures;
854 }
855
856 private void initializeGuards(List<? extends Element> elements, NodeData node) {
857 Map<String, List<GuardData>> guards = new HashMap<>();
858 for (SpecializationData specialization : node.getSpecializations()) {
859 for (GuardExpression exp : specialization.getGuards()) {
860 guards.put(exp.getGuardName(), null);
861 }
862 }
863
864 GuardParser parser = new GuardParser(context, node, null, guards.keySet());
865 List<GuardData> resolvedGuards = parser.parse(elements);
866 for (GuardData guard : resolvedGuards) {
867 List<GuardData> groupedGuards = guards.get(guard.getMethodName());
868 if (groupedGuards == null) {
869 groupedGuards = new ArrayList<>();
870 guards.put(guard.getMethodName(), groupedGuards);
871 }
872 groupedGuards.add(guard);
873 }
874
875 for (SpecializationData specialization : node.getSpecializations()) {
876 for (GuardExpression exp : specialization.getGuards()) {
877 resolveGuardExpression(node, specialization, guards, exp);
878 }
879 }
880 }
881
882 private void resolveGuardExpression(NodeData node, TemplateMethod source, Map<String, List<GuardData>> guards, GuardExpression expression) {
883 List<GuardData> availableGuards = guards.get(expression.getGuardName());
884 if (availableGuards == null) {
885 source.addError("No compatible guard with method name '%s' found. Please note that all signature types of the method guard must be declared in the type system.", expression.getGuardName());
886 return;
887 }
888 List<ExecutableElement> guardMethods = new ArrayList<>();
889 for (GuardData guard : availableGuards) {
890 guardMethods.add(guard.getMethod());
891 }
892 GuardParser parser = new GuardParser(context, node, source, new HashSet<>(Arrays.asList(expression.getGuardName())));
893 List<GuardData> matchingGuards = parser.parse(guardMethods);
894 if (!matchingGuards.isEmpty()) {
895 GuardData guard = matchingGuards.get(0);
896 // use the shared instance of the guard data
897 for (GuardData guardData : availableGuards) {
898 if (guardData.getMethod() == guard.getMethod()) {
899 expression.setGuard(guardData);
900 return;
901 }
902 }
903 throw new AssertionError("Should not reach here.");
904 } else {
905 MethodSpec spec = parser.createSpecification(source.getMethod(), source.getMarkerAnnotation());
906 spec.applyTypeDefinitions("types");
907 source.addError("No guard with name '%s' matched the required signature. Expected signature: %n%s", expression.getGuardName(), spec.toSignatureString("guard"));
908 }
909 }
910
911 private void initializeGeneric(final NodeData node) {
912 if (!node.needsRewrites(context)) {
913 return;
914 }
915
916 List<SpecializationData> generics = new ArrayList<>();
917 for (SpecializationData spec : node.getSpecializations()) {
918 if (spec.isGeneric()) {
919 generics.add(spec);
920 }
921 }
922
923 if (generics.size() == 1 && node.getSpecializations().size() == 1) {
924 // TODO this limitation should be lifted
925 for (SpecializationData generic : generics) {
926 generic.addError("@%s defined but no @%s.", Generic.class.getSimpleName(), Specialization.class.getSimpleName());
927 }
928 }
929
930 if (generics.isEmpty()) {
931 node.getSpecializations().add(createGenericSpecialization(node));
932 } else {
933 if (generics.size() > 1) {
934 for (SpecializationData generic : generics) {
935 generic.addError("Only @%s is allowed per operation.", Generic.class.getSimpleName());
936 }
937 }
938 }
939 }
940
941 private SpecializationData createGenericSpecialization(final NodeData node) {
942 GenericParser parser = new GenericParser(context, node);
943 MethodSpec specification = parser.createDefaultMethodSpec(node.getSpecializations().iterator().next().getMethod(), null, true, null);
944
945 List<TypeMirror> parameterTypes = new ArrayList<>();
946 int signatureIndex = 1;
947 for (ParameterSpec spec : specification.getRequired()) {
948 parameterTypes.add(createGenericType(spec, node.getSpecializations(), signatureIndex));
949 if (spec.isSignature()) {
950 signatureIndex++;
951 }
952 }
953
954 TypeMirror returnType = createGenericType(specification.getReturnType(), node.getSpecializations(), 0);
955 SpecializationData generic = parser.create("Generic", TemplateMethod.NO_NATURAL_ORDER, null, null, returnType, parameterTypes);
956 if (generic == null) {
957 throw new RuntimeException("Unable to create generic signature for node " + node.getNodeId() + " with " + parameterTypes + ". Specification " + specification + ".");
958 }
959
960 return generic;
961 }
962
963 private TypeMirror createGenericType(ParameterSpec spec, List<SpecializationData> specializations, int signatureIndex) {
964 NodeExecutionData execution = spec.getExecution();
965 if (execution == null) {
966 if (spec.getAllowedTypes().size() == 1) {
967 return spec.getAllowedTypes().get(0);
968 } else {
969 return ElementUtils.getCommonSuperType(context, spec.getAllowedTypes().toArray(new TypeMirror[0]));
970 }
971 } else {
972 Set<TypeData> types = new HashSet<>();
973 for (SpecializationData specialization : specializations) {
974 types.add(specialization.getTypeSignature().get(signatureIndex));
975 }
976
977 NodeChildData child = execution.getChild();
978 TypeData genericType = null;
979 if (types.size() == 1) {
980 ExecutableTypeData executable = child.findExecutableType(context, types.iterator().next());
981 if (executable != null && (signatureIndex == 0 || !executable.hasUnexpectedValue(context))) {
982 genericType = types.iterator().next();
983 }
984 }
985 if (genericType == null) {
986 genericType = child.findAnyGenericExecutableType(context).getType();
987 }
988 return genericType.getPrimitiveType();
989 }
990 }
991
992 private static void initializeUninitialized(final NodeData node) {
993 SpecializationData generic = node.getGenericSpecialization();
994 if (generic == null) {
995 return;
996 }
997 for (Parameter parameter : generic.getReturnTypeAndParameters()) {
998 if (ElementUtils.isObject(parameter.getType())) {
999 continue;
1000 }
1001 Set<String> types = new HashSet<>();
1002 for (SpecializationData specialization : node.getSpecializations()) {
1003 Parameter actualParameter = specialization.findParameter(parameter.getLocalName());
1004 if (actualParameter != null) {
1005 types.add(ElementUtils.getQualifiedName(actualParameter.getType()));
1006 }
1007 }
1008 if (types.size() > 1) {
1009 generic.replaceParameter(parameter.getLocalName(), new Parameter(parameter, node.getTypeSystem().getGenericTypeData()));
1010 }
1011 }
1012 TemplateMethod uninializedMethod = new TemplateMethod("Uninitialized", -1, node, generic.getSpecification(), null, null, generic.getReturnType(), generic.getParameters());
1013 // should not use messages from generic specialization
1014 uninializedMethod.getMessages().clear();
1015 node.getSpecializations().add(new SpecializationData(node, uninializedMethod, SpecializationKind.UNINITIALIZED));
1016 }
1017
1018 private void initializePolymorphism(NodeData node) {
1019 if (!node.needsRewrites(context)) {
1020 return;
1021 }
1022
1023 SpecializationData generic = node.getGenericSpecialization();
1024
1025 List<TypeData> polymorphicSignature = new ArrayList<>();
1026 List<Parameter> updatePolymorphic = Arrays.asList();
1027 for (Parameter genericParameter : updatePolymorphic) {
1028 if (!genericParameter.getSpecification().isSignature()) {
1029 continue;
1030 }
1031
1032 Set<TypeData> usedTypes = new HashSet<>();
1033 for (SpecializationData specialization : node.getSpecializations()) {
1034 if (!specialization.isSpecialized()) {
1035 continue;
1036 }
1037 Parameter parameter = specialization.findParameter(genericParameter.getLocalName());
1038 if (parameter == null) {
1039 throw new AssertionError("Parameter existed in generic specialization but not in specialized. param = " + genericParameter.getLocalName());
1040 }
1041 usedTypes.add(parameter.getTypeSystemType());
1042 }
1043
1044 TypeData polymorphicType;
1045 if (usedTypes.size() == 1) {
1046 polymorphicType = usedTypes.iterator().next();
1047 } else {
1048 polymorphicType = node.getTypeSystem().getGenericTypeData();
1049 }
1050 polymorphicSignature.add(polymorphicType);
1051 }
1052
1053 SpecializationData polymorphic = new SpecializationData(node, generic, SpecializationKind.POLYMORPHIC);
1054 polymorphic.updateSignature(new TypeSignature(polymorphicSignature));
1055 node.getSpecializations().add(polymorphic);
1056 }
1057
1058 private void initializeShortCircuits(NodeData node) {
1059 Map<String, List<ShortCircuitData>> groupedShortCircuits = groupShortCircuits(node.getShortCircuits());
1060
1061 boolean valid = true;
1062 List<NodeExecutionData> shortCircuitExecutions = new ArrayList<>();
1063 for (NodeExecutionData execution : node.getChildExecutions()) {
1064 if (!execution.isShortCircuit()) {
1065 continue;
1066 }
1067 shortCircuitExecutions.add(execution);
1068 String valueName = execution.getShortCircuitId();
1069 List<ShortCircuitData> availableCircuits = groupedShortCircuits.get(valueName);
1070
1071 if (availableCircuits == null || availableCircuits.isEmpty()) {
1072 node.addError("@%s method for short cut value '%s' required.", ShortCircuit.class.getSimpleName(), valueName);
1073 valid = false;
1074 continue;
1075 }
1076
1077 boolean sameMethodName = true;
1078 String methodName = availableCircuits.get(0).getMethodName();
1079 for (ShortCircuitData circuit : availableCircuits) {
1080 if (!circuit.getMethodName().equals(methodName)) {
1081 sameMethodName = false;
1082 }
1083 }
1084
1085 if (!sameMethodName) {
1086 for (ShortCircuitData circuit : availableCircuits) {
1087 circuit.addError("All short circuits for short cut value '%s' must have the same method name.", valueName);
1088 }
1089 valid = false;
1090 continue;
1091 }
1092
1093 ShortCircuitData genericCircuit = null;
1094 for (ShortCircuitData circuit : availableCircuits) {
1095 if (isGenericShortCutMethod(circuit)) {
1096 genericCircuit = circuit;
1097 break;
1098 }
1099 }
1100
1101 if (genericCircuit == null) {
1102 node.addError("No generic @%s method available for short cut value '%s'.", ShortCircuit.class.getSimpleName(), valueName);
1103 valid = false;
1104 continue;
1105 }
1106
1107 for (ShortCircuitData circuit : availableCircuits) {
1108 if (circuit != genericCircuit) {
1109 circuit.setGenericShortCircuitMethod(genericCircuit);
1110 }
1111 }
1112 }
1113
1114 if (!valid) {
1115 return;
1116 }
1117
1118 List<SpecializationData> specializations = new ArrayList<>();
1119 specializations.addAll(node.getSpecializations());
1120 for (SpecializationData specialization : specializations) {
1121 List<ShortCircuitData> assignedShortCuts = new ArrayList<>(shortCircuitExecutions.size());
1122
1123 for (NodeExecutionData shortCircuit : shortCircuitExecutions) {
1124 List<ShortCircuitData> availableShortCuts = groupedShortCircuits.get(shortCircuit.getShortCircuitId());
1125
1126 ShortCircuitData genericShortCircuit = null;
1127 ShortCircuitData compatibleShortCircuit = null;
1128 for (ShortCircuitData circuit : availableShortCuts) {
1129 if (circuit.isGeneric()) {
1130 genericShortCircuit = circuit;
1131 } else if (circuit.isCompatibleTo(specialization)) {
1132 compatibleShortCircuit = circuit;
1133 }
1134 }
1135
1136 if (compatibleShortCircuit == null) {
1137 compatibleShortCircuit = genericShortCircuit;
1138 }
1139 assignedShortCuts.add(compatibleShortCircuit);
1140 }
1141 specialization.setShortCircuits(assignedShortCuts);
1142 }
1143 }
1144
1145 private boolean isGenericShortCutMethod(ShortCircuitData method) {
1146 for (Parameter parameter : method.getParameters()) {
1147 NodeExecutionData execution = parameter.getSpecification().getExecution();
1148 if (execution == null) {
1149 continue;
1150 }
1151 ExecutableTypeData found = null;
1152 List<ExecutableTypeData> executableElements = execution.getChild().findGenericExecutableTypes(context);
1153 for (ExecutableTypeData executable : executableElements) {
1154 if (executable.getType().equalsType(parameter.getTypeSystemType())) {
1155 found = executable;
1156 break;
1157 }
1158 }
1159 if (found == null) {
1160 return false;
1161 }
1162 }
1163 return true;
1164 }
1165
1166 private static Map<String, List<ShortCircuitData>> groupShortCircuits(List<ShortCircuitData> shortCircuits) {
1167 Map<String, List<ShortCircuitData>> group = new HashMap<>();
1168 for (ShortCircuitData shortCircuit : shortCircuits) {
1169 List<ShortCircuitData> circuits = group.get(shortCircuit.getValueName());
1170 if (circuits == null) {
1171 circuits = new ArrayList<>();
1172 group.put(shortCircuit.getValueName(), circuits);
1173 }
1174 circuits.add(shortCircuit);
1175 }
1176 return group;
1177 }
1178
1179 private static boolean verifySpecializationSameLength(NodeData nodeData) {
1180 int lastArgs = -1;
1181 for (SpecializationData specializationData : nodeData.getSpecializations()) {
1182 int signatureArgs = specializationData.getSignatureSize();
1183 if (lastArgs == signatureArgs) {
1184 continue;
1185 }
1186 if (lastArgs != -1) {
1187 for (SpecializationData specialization : nodeData.getSpecializations()) {
1188 specialization.addError("All specializations must have the same number of arguments.");
1189 }
1190 return false;
1191 } else {
1192 lastArgs = signatureArgs;
1193 }
1194 }
1195 return true;
1196 }
1197
1198 private static void verifyVisibilities(NodeData node) {
1199 if (node.getTemplateType().getModifiers().contains(Modifier.PRIVATE) && node.getSpecializations().size() > 0) {
1200 node.addError("Classes containing a @%s annotation must not be private.", Specialization.class.getSimpleName());
1201 }
1202 }
1203
1204 private static void verifyMissingAbstractMethods(NodeData nodeData, List<? extends Element> originalElements) {
1205 if (!nodeData.needsFactory()) {
1206 // missing abstract methods only needs to be implemented
1207 // if we need go generate factory for it.
1208 return;
1209 }
1210
1211 List<Element> elements = new ArrayList<>(originalElements);
1212 Set<Element> unusedElements = new HashSet<>(elements);
1213 for (TemplateMethod method : nodeData.getAllTemplateMethods()) {
1214 unusedElements.remove(method.getMethod());
1215 }
1216
1217 for (NodeFieldData field : nodeData.getFields()) {
1218 if (field.getGetter() != null) {
1219 unusedElements.remove(field.getGetter());
1220 }
1221 }
1222
1223 for (NodeChildData child : nodeData.getChildren()) {
1224 if (child.getAccessElement() != null) {
1225 unusedElements.remove(child.getAccessElement());
1226 }
1227 }
1228
1229 for (ExecutableElement unusedMethod : ElementFilter.methodsIn(unusedElements)) {
1230 if (unusedMethod.getModifiers().contains(Modifier.ABSTRACT)) {
1231 nodeData.addError("The type %s must implement the inherited abstract method %s.", ElementUtils.getSimpleName(nodeData.getTemplateType()),
1232 ElementUtils.getReadableSignature(unusedMethod));
1233 }
1234 }
1235 }
1236
1237 private static void verifyNamingConvention(List<? extends TemplateMethod> methods, String prefix) {
1238 for (int i = 0; i < methods.size(); i++) {
1239 TemplateMethod m1 = methods.get(i);
1240 if (m1.getMethodName().length() < 3 || !m1.getMethodName().startsWith(prefix)) {
1241 m1.addError("Naming convention: method name must start with '%s'.", prefix);
1242 }
1243 }
1244 }
1245
1246 private static void verifySpecializationThrows(NodeData node) {
1247 Map<String, SpecializationData> specializationMap = new HashMap<>();
1248 for (SpecializationData spec : node.getSpecializations()) {
1249 specializationMap.put(spec.getMethodName(), spec);
1250 }
1251 for (SpecializationData sourceSpecialization : node.getSpecializations()) {
1252 if (sourceSpecialization.getExceptions() != null) {
1253 for (SpecializationThrowsData throwsData : sourceSpecialization.getExceptions()) {
1254 for (SpecializationThrowsData otherThrowsData : sourceSpecialization.getExceptions()) {
1255 if (otherThrowsData != throwsData && ElementUtils.typeEquals(otherThrowsData.getJavaClass(), throwsData.getJavaClass())) {
1256 throwsData.addError("Duplicate exception type.");
1257 }
1258 }
1259 }
1260 }
1261 }
1262 }
1263
1264 private void verifyConstructors(NodeData nodeData) {
1265 if (!nodeData.needsRewrites(context)) {
1266 // no specialization constructor is needed if the node never rewrites.
1267 return;
1268 }
1269
1270 TypeElement type = ElementUtils.fromTypeMirror(nodeData.getNodeType());
1271 List<ExecutableElement> constructors = ElementFilter.constructorsIn(type.getEnclosedElements());
1272
1273 boolean parametersFound = false;
1274 for (ExecutableElement constructor : constructors) {
1275 if (!constructor.getParameters().isEmpty() && !isSourceSectionConstructor(context, constructor)) {
1276 parametersFound = true;
1277 }
1278 }
1279 if (!parametersFound) {
1280 return;
1281 }
1282 for (ExecutableElement e : constructors) {
1283 if (e.getParameters().size() == 1) {
1284 TypeMirror firstArg = e.getParameters().get(0).asType();
1285 if (ElementUtils.typeEquals(firstArg, nodeData.getNodeType())) {
1286 if (e.getModifiers().contains(Modifier.PRIVATE)) {
1287 nodeData.addError("The specialization constructor must not be private.");
1288 } else if (constructors.size() <= 1) {
1289 nodeData.addError("The specialization constructor must not be the only constructor. The definition of an alternative constructor is required.");
1290 }
1291 return;
1292 }
1293 }
1294 }
1295
1296 // not found
1297 nodeData.addError("Specialization constructor '%s(%s previousNode) { this(...); }' is required.", ElementUtils.getSimpleName(type), ElementUtils.getSimpleName(type));
1298 }
1299
1300 public static boolean isSourceSectionConstructor(ProcessorContext context, ExecutableElement constructor) {
1301 return constructor.getParameters().size() == 1 && ElementUtils.typeEquals(constructor.getParameters().get(0).asType(), context.getTruffleTypes().getSourceSection());
1302 }
1303
1304 private AnnotationMirror findFirstAnnotation(List<? extends Element> elements, Class<? extends Annotation> annotation) {
1305 for (Element element : elements) {
1306 AnnotationMirror mirror = ElementUtils.findAnnotationMirror(processingEnv, element, annotation);
1307 if (mirror != null) {
1308 return mirror;
1309 }
1310 }
1311 return null;
1312 }
1313
1314 private TypeMirror inheritType(AnnotationMirror annotation, String valueName, TypeMirror parentType) {
1315 TypeMirror inhertNodeType = context.getTruffleTypes().getNode();
1316 TypeMirror value = ElementUtils.getAnnotationValue(TypeMirror.class, annotation, valueName);
1317 if (ElementUtils.typeEquals(inhertNodeType, value)) {
1318 return parentType;
1319 } else {
1320 return value;
1321 }
1322 }
1323
1324 private ExecutableElement findGetter(List<? extends Element> elements, String variableName, TypeMirror type) {
1325 if (type == null) {
1326 return null;
1327 }
1328 String methodName;
1329 if (ElementUtils.typeEquals(type, context.getType(boolean.class))) {
1330 methodName = "is" + ElementUtils.firstLetterUpperCase(variableName);
1331 } else {
1332 methodName = "get" + ElementUtils.firstLetterUpperCase(variableName);
1333 }
1334
1335 for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
1336 if (method.getSimpleName().toString().equals(methodName) && method.getParameters().size() == 0 && ElementUtils.isAssignable(type, method.getReturnType())) {
1337 return method;
1338 }
1339 }
1340 return null;
1341 }
1342
1343 private static List<TypeElement> collectSuperClasses(List<TypeElement> collection, TypeElement element) {
1344 if (element != null) {
1345 collection.add(element);
1346 if (element.getSuperclass() != null) {
1347 collectSuperClasses(collection, ElementUtils.fromTypeMirror(element.getSuperclass()));
1348 }
1349 }
1350 return collection;
1351 }
1352
1353 }