comparison graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeParser.java @ 7502:6343a09b2ec1

Codegen operation generation is inferred from the node type hierarchy.
author Christian Humer <christian.humer@gmail.com>
date Fri, 18 Jan 2013 13:28:12 +0100
parents
children d295ab2902fb
comparison
equal deleted inserted replaced
7497:0f8c6dbf68be 7502:6343a09b2ec1
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.codegen.processor.node;
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
32 import com.oracle.truffle.api.codegen.*;
33 import com.oracle.truffle.api.nodes.Node.Child;
34 import com.oracle.truffle.api.nodes.Node.Children;
35 import com.oracle.truffle.codegen.processor.*;
36 import com.oracle.truffle.codegen.processor.ast.*;
37 import com.oracle.truffle.codegen.processor.node.NodeFieldData.ExecutionKind;
38 import com.oracle.truffle.codegen.processor.node.NodeFieldData.FieldKind;
39 import com.oracle.truffle.codegen.processor.template.*;
40 import com.oracle.truffle.codegen.processor.typesystem.*;
41
42 public class NodeParser extends TemplateParser<NodeData>{
43
44 private static final List<Class<? extends Annotation>> annotations = Arrays.asList(
45 Generic.class, GuardCheck.class, TypeSystemReference.class, ShortCircuit.class, Specialization.class,
46 SpecializationGuard.class, SpecializationListener.class, SpecializationThrows.class);
47
48 private static final boolean DEBUG = false;
49
50 private Map<String, NodeData> parsedNodes;
51 private TypeElement originalType;
52
53 public NodeParser(ProcessorContext c) {
54 super(c);
55 }
56
57 @Override
58 protected NodeData parse(Element element, AnnotationMirror mirror) {
59 assert element instanceof TypeElement;
60 try {
61 parsedNodes = new HashMap<>();
62 originalType = (TypeElement) element;
63
64 return parseInnerClassHierarchy((TypeElement) element);
65 } finally {
66 if (DEBUG) {
67 NodeData parsed = parsedNodes.get(Utils.getQualifiedName(originalType));
68 if (parsed != null) {
69 String dump = parsed.dump();
70 log.error("Node parsed: %s", dump);
71 System.out.println("Parsed: " + dump);
72 }
73 }
74 parsedNodes = null;
75 originalType = null;
76 }
77 }
78
79 @Override
80 public boolean isDelegateToRootDeclaredType() {
81 return true;
82 }
83
84 private NodeData parseInnerClassHierarchy(TypeElement rootType) {
85 List<? extends TypeElement> types = ElementFilter.typesIn(rootType.getEnclosedElements());
86 List<NodeData> children = new ArrayList<>();
87 for (TypeElement childElement : types) {
88 NodeData childNode = parseInnerClassHierarchy(childElement);
89 if (childNode != null) {
90 children.add(childNode);
91 }
92 }
93 NodeData rootNode = resolveNode(rootType);
94 if (rootNode == null && children.size() > 0) {
95 rootNode = new NodeData(rootType, null);
96 }
97 if (rootNode != null) {
98 rootNode.setDeclaredChildren(children);
99 }
100 return rootNode;
101 }
102
103 private NodeData resolveNode(TypeElement currentType) {
104 String typeName = Utils.getQualifiedName(currentType);
105 if (!parsedNodes.containsKey(typeName)) {
106 NodeData node = parseNode(currentType);
107 if (node != null) {
108 parsedNodes.put(typeName, node);
109 }
110 return node;
111 }
112 return parsedNodes.get(typeName);
113 }
114
115 private NodeData parseNode(TypeElement type) {
116 if (Utils.findAnnotationMirror(processingEnv, type, GeneratedBy.class) != null) {
117 // generated nodes get called again.
118 return null;
119 }
120 if (!Utils.isAssignable(type.asType(), context.getTruffleTypes().getNode())) {
121 return null; // not a node
122 }
123
124 List<Element> elements = new ArrayList<>(context.getEnvironment().getElementUtils().getAllMembers(type));
125 List<TypeElement> typeHierarchy = findSuperClasses(new ArrayList<TypeElement>(), type);
126 Collections.reverse(typeHierarchy);
127
128 AnnotationMirror typeSystemMirror = findFirstAnnotation(typeHierarchy, TypeSystemReference.class);
129 if (typeSystemMirror == null) {
130 log.error(originalType, "No @%s annotation found in type hierarchy.", TypeSystemReference.class.getSimpleName());
131 return null;
132 }
133
134 TypeMirror typeSytemType = Utils.getAnnotationValueType(typeSystemMirror, "value");
135 final TypeSystemData typeSystem = (TypeSystemData) context.getTemplate(typeSytemType, true);
136 if (typeSystem == null) {
137 log.error(originalType, "The used type system '%s' is invalid.", Utils.getQualifiedName(typeSytemType));
138 return null;
139 }
140
141 NodeData nodeData = new NodeData(type, typeSystem);
142
143 nodeData.setExtensionElements(getExtensionParser().parseAll(type, elements));
144 if (nodeData.getExtensionElements() != null) {
145 elements.addAll(nodeData.getExtensionElements());
146 }
147
148 List<ExecutableTypeData> executableTypes = filterExecutableTypes(new ExecutableTypeMethodParser(context, nodeData).parse(elements));
149
150 nodeData.setExecutableTypes(executableTypes.toArray(new ExecutableTypeData[executableTypes.size()]));
151
152 parsedNodes.put(Utils.getQualifiedName(type), nodeData); // node fields will resolve node types, to avoid endless loops
153
154 NodeFieldData[] fields = parseFields(nodeData, elements, typeHierarchy);
155 if (fields == null) {
156 return null;
157 }
158 nodeData.setFields(fields);
159
160
161 List<SpecializationData> genericSpecializations = new GenericParser(context, nodeData).parse(elements);
162 List<GuardData> guards = new GuardParser(context, nodeData, nodeData.getTypeSystem()).parse(elements);
163 nodeData.setGuards(guards.toArray(new GuardData[guards.size()]));
164
165 SpecializationMethodParser specializationParser = new SpecializationMethodParser(context, nodeData);
166 List<SpecializationData> specializations = specializationParser.parse(elements);
167 List<ShortCircuitData> shortCircuits = new ShortCircuitParser(context, nodeData).parse(elements);
168 List<TemplateMethod> listeners = new SpecializationListenerParser(context, nodeData).parse(elements);
169
170 if (specializations == null || genericSpecializations == null || shortCircuits == null || listeners == null || guards == null) {
171 return null;
172 }
173
174 SpecializationData genericSpecialization = null;
175 if (genericSpecializations.size() > 1) {
176 for (SpecializationData generic : genericSpecializations) {
177 log.error(generic.getMethod(), "Only one method with @%s is allowed per operation.", Generic.class.getSimpleName());
178 }
179 return null;
180 } else if (genericSpecializations.size() == 1) {
181 genericSpecialization = genericSpecializations.get(0);
182 }
183
184 if (specializations.size() > 1 && genericSpecialization == null) {
185 log.error(originalType, "Need a @%s method.", Generic.class.getSimpleName());
186 return null;
187 }
188
189 Collections.sort(specializations, new Comparator<SpecializationData>() {
190 @Override
191 public int compare(SpecializationData o1, SpecializationData o2) {
192 return compareSpecialization(typeSystem, o1, o2);
193 }
194 });
195
196 List<SpecializationData> allSpecializations = new ArrayList<>(specializations);
197 if (genericSpecialization != null) {
198 allSpecializations.add(genericSpecialization);
199 CodeExecutableElement uninitializedMethod = new CodeExecutableElement(Utils.modifiers(Modifier.PUBLIC), context.getType(void.class), "doUninitialized");
200 TemplateMethod uninializedMethod = new TemplateMethod(nodeData, genericSpecialization.getSpecification(), uninitializedMethod,
201 genericSpecialization.getMarkerAnnotation(), genericSpecialization.getReturnType(), genericSpecialization.getParameters());
202 allSpecializations.add(0, new SpecializationData(uninializedMethod, false, true));
203 }
204
205 for (SpecializationData specialization : allSpecializations) {
206 specialization.setNode(nodeData);
207 }
208
209 // verify order is not ambiguous
210 if (!verifySpecializationOrder(typeSystem, specializations)) {
211 return null;
212 }
213
214 nodeData.setSpecializations(allSpecializations.toArray(new SpecializationData[allSpecializations.size()]));
215 nodeData.setSpecializationListeners(listeners.toArray(new TemplateMethod[listeners.size()]));
216
217 if (!verifyMissingAbstractMethods(nodeData, elements)) {
218 return null;
219 }
220
221 if (!assignShortCircuitsToSpecializations(nodeData, allSpecializations, shortCircuits)) {
222 return null;
223 }
224
225 if (!verifyConstructors(nodeData)) {
226 return null;
227 }
228
229 if (!verifyNamingConvention(specializations, "do")) {
230 return null;
231 }
232
233 if (!verifyNamesUnique(specializations)) {
234 return null;
235 }
236
237 if (!verifyNamingConvention(shortCircuits, "needs")) {
238 return null;
239 }
240
241 if (!verifySpecializationThrows(typeSystem, specializations)) {
242 return null;
243 }
244
245 return nodeData;
246 }
247
248 private boolean verifyMissingAbstractMethods(NodeData nodeData, List<Element> elements) {
249 if (nodeData.needsFactory()) {
250 // missing abstract methods only needs to be implemented
251 // if we need go generate factory for it.
252 return true;
253 }
254
255 Set<Element> unusedElements = new HashSet<>(elements);
256 for (TemplateMethod method : nodeData.getAllTemplateMethods()) {
257 unusedElements.remove(method.getMethod());
258 }
259 if (nodeData.getExtensionElements() != null) {
260 unusedElements.removeAll(nodeData.getExtensionElements());
261 }
262
263 boolean valid = true;
264 for (ExecutableElement unusedMethod : ElementFilter.methodsIn(unusedElements)) {
265 if (unusedMethod.getModifiers().contains(Modifier.ABSTRACT)) {
266 context.getLog().error(nodeData.getTemplateType(), "The type %s must implement the inherited abstract method %s.", Utils.getSimpleName(nodeData.getTemplateType()), Utils.getReadableSignature(unusedMethod));
267 valid = false;
268 }
269 }
270
271 return valid;
272 }
273
274 private boolean verifyConstructors(NodeData nodeData) {
275 TypeElement type = Utils.fromTypeMirror(nodeData.getNodeType());
276 if (!nodeData.needsRewrites(context)) {
277 // no specialization constructor is needed if the node never rewrites.
278 return true;
279 }
280
281 List<ExecutableElement> constructors = ElementFilter.constructorsIn(type.getEnclosedElements());
282 for (ExecutableElement e : constructors) {
283 if (e.getParameters().size() == 1) {
284 TypeMirror firstArg = e.getParameters().get(0).asType();
285 if (Utils.typeEquals(firstArg, nodeData.getNodeType())) {
286 if (e.getModifiers().contains(Modifier.PRIVATE)) {
287 context.getLog().error(e, "The specialization constructor must not be private.");
288 return false;
289 } else if (constructors.size() <= 1) {
290 context.getLog().error(e, "The specialization constructor must not be the only constructor. The definition of an alternative constructor is required.");
291 return false;
292 }
293 return true;
294 }
295 }
296 }
297
298 // not found
299 context.getLog().error(type, "Specialization constructor '%s(%s previousNode) { this(...); }' is required.", Utils.getSimpleName(type), Utils.getSimpleName(type));
300 return false;
301 }
302
303 private static List<ExecutableTypeData> filterExecutableTypes(List<ExecutableTypeData> executableTypes) {
304 List<ExecutableTypeData> filteredExecutableTypes = new ArrayList<>();
305 for (ExecutableTypeData t1 : executableTypes) {
306 boolean add = true;
307 for (ExecutableTypeData t2 : executableTypes) {
308 if (t1 == t2) {
309 continue;
310 }
311 if (Utils.typeEquals(t1.getType().getPrimitiveType(), t2.getType().getPrimitiveType())) {
312 if (t1.isFinal() && !t2.isFinal()) {
313 add = false;
314 }
315 }
316 }
317 if (add) {
318 filteredExecutableTypes.add(t1);
319 }
320 }
321 return filteredExecutableTypes;
322 }
323
324 private AnnotationMirror findFirstAnnotation(List<? extends Element> elements, Class<? extends Annotation> annotation) {
325 for (Element element : elements) {
326 AnnotationMirror mirror = Utils.findAnnotationMirror(processingEnv, element, annotation);
327 if (mirror != null) {
328 return mirror;
329 }
330 }
331 return null;
332 }
333
334 private NodeFieldData[] parseFields(NodeData nodeData, List<? extends Element> elements, final List<TypeElement> typeHierarchy) {
335 AnnotationMirror executionOrderMirror = findFirstAnnotation(typeHierarchy, ExecuteChildren.class);
336 List<String> executionDefinition = null;
337 if (executionOrderMirror != null) {
338 executionDefinition = new ArrayList<>();
339 for (Object object : Utils.getAnnotationValueList(executionOrderMirror, "value")) {
340 executionDefinition.add((String) object);
341 }
342 }
343
344 Set<String> shortCircuits = new HashSet<>();
345 for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
346 AnnotationMirror mirror = Utils.findAnnotationMirror(processingEnv, method, ShortCircuit.class);
347 if (mirror != null) {
348 shortCircuits.add(Utils.getAnnotationValueString(mirror, "value"));
349 }
350 }
351
352 boolean valid = true;
353
354 List<NodeFieldData> fields = new ArrayList<>();
355 for (VariableElement var : ElementFilter.fieldsIn(elements)) {
356 if (var.getModifiers().contains(Modifier.STATIC)) {
357 continue;
358 }
359
360 if (executionDefinition != null) {
361 if (!executionDefinition.contains(var.getSimpleName().toString())) {
362 continue;
363 }
364 }
365
366 NodeFieldData field = parseField(nodeData, var, shortCircuits);
367 if (field != null) {
368 if (field.getExecutionKind() != ExecutionKind.IGNORE) {
369 fields.add(field);
370 }
371 } else {
372 valid = false;
373 }
374 }
375
376 //TODO parse getters
377 if (!valid) {
378 return null;
379 }
380
381 NodeFieldData[] fieldArray = fields.toArray(new NodeFieldData[fields.size()]);
382 sortByExecutionOrder(fieldArray, executionDefinition == null ? Collections.<String>emptyList() : executionDefinition, typeHierarchy);
383 return fieldArray;
384 }
385
386 private NodeFieldData parseField(NodeData parentNodeData, VariableElement var, Set<String> foundShortCircuits) {
387 AnnotationMirror childMirror = Utils.findAnnotationMirror(processingEnv, var, Child.class);
388 AnnotationMirror childrenMirror = Utils.findAnnotationMirror(processingEnv, var, Children.class);
389
390 FieldKind kind;
391
392 ExecutionKind execution;
393 if (foundShortCircuits.contains(var.getSimpleName().toString())) {
394 execution = ExecutionKind.SHORT_CIRCUIT;
395 } else {
396 execution = ExecutionKind.DEFAULT;
397 }
398
399 AnnotationMirror mirror;
400 TypeMirror nodeType;
401
402 if (childMirror != null) {
403 mirror = childMirror;
404 nodeType = var.asType();
405 kind = FieldKind.CHILD;
406 } else if (childrenMirror != null) {
407 mirror = childrenMirror;
408 nodeType = getComponentType(var.asType());
409 kind = FieldKind.CHILDREN;
410 } else {
411 mirror = null;
412 nodeType = null;
413 kind = FieldKind.FIELD;
414 execution = ExecutionKind.IGNORE;
415 }
416
417 NodeData fieldNodeData = null;
418 if (nodeType != null) {
419 fieldNodeData = resolveNode(Utils.fromTypeMirror(nodeType));
420 Element errorElement = Utils.typeEquals(parentNodeData.getTemplateType().asType(), var.getEnclosingElement().asType()) ? var : parentNodeData.getTemplateType();
421
422 if (fieldNodeData == null) {
423 //TODO redirect errors from resolve.
424 context.getLog().error(errorElement, "Node type '%s' is invalid.", Utils.getQualifiedName(nodeType));
425 return null;
426 } else if (fieldNodeData.findGenericExecutableType(context) == null) {
427 context.getLog().error(errorElement, "No executable generic type found for node '%s'.", Utils.getQualifiedName(nodeType));
428 return null;
429 }
430 }
431
432 //TODO correct handling of access elements
433 if (var.getModifiers().contains(Modifier.PRIVATE) && Utils.typeEquals(var.getEnclosingElement().asType(), parentNodeData.getTemplateType().asType())) {
434 execution = ExecutionKind.IGNORE;
435 }
436
437 return new NodeFieldData(fieldNodeData, var, findAccessElement(var), mirror, kind, execution);
438 }
439
440 private Element findAccessElement(VariableElement variableElement) {
441 Element enclosed = variableElement.getEnclosingElement();
442 if (!enclosed.getKind().isClass()) {
443 throw new IllegalArgumentException("Field must be enclosed in a class.");
444 }
445
446 String methodName;
447 if (Utils.typeEquals(variableElement.asType(), context.getType(boolean.class))) {
448 methodName = "is" + Utils.firstLetterUpperCase(variableElement.getSimpleName().toString());
449 } else {
450 methodName = "get" + Utils.firstLetterUpperCase(variableElement.getSimpleName().toString());
451 }
452
453 ExecutableElement getter = null;
454 for (ExecutableElement method : ElementFilter.methodsIn(enclosed.getEnclosedElements())) {
455 if (method.getSimpleName().toString().equals(methodName)
456 && method.getParameters().size() == 0
457 && !Utils.typeEquals(method.getReturnType(), context.getType(void.class))) {
458 getter = method;
459 break;
460 }
461 }
462
463 if (getter != null) {
464 return getter;
465 } else {
466 return variableElement;
467 }
468 }
469
470 private static void sortByExecutionOrder(NodeFieldData[] fields, final List<String> executionOrder, final List<TypeElement> typeHierarchy) {
471 Arrays.sort(fields, new Comparator<NodeFieldData>() {
472 @Override
473 public int compare(NodeFieldData o1, NodeFieldData o2) {
474 // sort by execution order
475 int index1 = executionOrder.indexOf(o1.getName());
476 int index2 = executionOrder.indexOf(o2.getName());
477 if (index1 == -1 || index2 == -1) {
478 // sort by type hierarchy
479 index1 = typeHierarchy.indexOf(o1.getFieldElement().getEnclosingElement());
480 index2 = typeHierarchy.indexOf(o2.getFieldElement().getEnclosingElement());
481
482 // finally sort by name (will emit warning)
483 if (index1 == -1 || index2 == -1) {
484 return o1.getName().compareTo(o2.getName());
485 }
486 }
487 return index1 - index2;
488 }
489 });
490 }
491
492 private boolean assignShortCircuitsToSpecializations(NodeData nodeData,
493 List<SpecializationData> specializations,
494 List<ShortCircuitData> shortCircuits) {
495
496 Map<String, List<ShortCircuitData>> groupedShortCircuits = groupShortCircuits(shortCircuits);
497
498 boolean valid = true;
499
500 for (NodeFieldData field : nodeData.filterFields(null, ExecutionKind.SHORT_CIRCUIT)) {
501 String valueName = field.getName();
502 List<ShortCircuitData> availableCircuits = groupedShortCircuits.get(valueName);
503
504 if (availableCircuits == null || availableCircuits.isEmpty()) {
505 log.error(nodeData.getTemplateType(),
506 "@%s method for short cut value '%s' required.",
507 ShortCircuit.class.getSimpleName(), valueName);
508 valid = false;
509 continue;
510 }
511
512
513 boolean sameMethodName = true;
514 String methodName = availableCircuits.get(0).getMethodName();
515 for (ShortCircuitData circuit : availableCircuits) {
516 if (!circuit.getMethodName().equals(methodName)) {
517 sameMethodName = false;
518 }
519 }
520
521 if (!sameMethodName) {
522 for (ShortCircuitData circuit : availableCircuits) {
523 log.error(circuit.getMethod(), circuit.getMarkerAnnotation(), "All short circuits for short cut value '%s' must have the same method name.", valueName);
524 }
525 valid = false;
526 continue;
527 }
528
529 ShortCircuitData genericCircuit = null;
530 for (ShortCircuitData circuit : availableCircuits) {
531 if (isGenericShortCutMethod(nodeData, circuit)) {
532 genericCircuit = circuit;
533 break;
534 }
535 }
536
537 if (genericCircuit == null) {
538 log.error(nodeData.getTemplateType(),
539 "No generic @%s method available for short cut value '%s'.", ShortCircuit.class.getSimpleName(), valueName);
540 valid = false;
541 continue;
542 }
543
544 for (ShortCircuitData circuit : availableCircuits) {
545 if (circuit != genericCircuit) {
546 circuit.setGenericShortCircuitMethod(genericCircuit);
547 }
548 }
549 }
550
551 if (!valid) {
552 return valid;
553 }
554
555 NodeFieldData[] fields = nodeData.filterFields(null, ExecutionKind.SHORT_CIRCUIT);
556 for (SpecializationData specialization : specializations) {
557 ShortCircuitData[] assignedShortCuts = new ShortCircuitData[fields.length];
558
559 for (int i = 0; i < fields.length; i++) {
560 List<ShortCircuitData> availableShortCuts = groupedShortCircuits.get(fields[i].getName());
561
562 ShortCircuitData genericShortCircuit = null;
563 for (ShortCircuitData circuit : availableShortCuts) {
564 if (circuit.isGeneric()) {
565 genericShortCircuit = circuit;
566 } else if (circuit.isCompatibleTo(specialization)) {
567 assignedShortCuts[i] = circuit;
568 }
569 }
570
571 if (assignedShortCuts[i] == null) {
572 assignedShortCuts[i] = genericShortCircuit;
573 }
574 }
575 specialization.setShortCircuits(assignedShortCuts);
576 }
577 return true;
578 }
579
580 private boolean verifyNamingConvention(List<? extends TemplateMethod> methods, String prefix) {
581 boolean valid = true;
582 for (int i = 0; i < methods.size(); i++) {
583 TemplateMethod m1 = methods.get(i);
584 if (m1.getMethodName().length() < 3 || !m1.getMethodName().startsWith(prefix)) {
585 log.error(m1.getMethod(), m1.getMarkerAnnotation(), "Naming convention: method name must start with '%s'.", prefix);
586 valid = false;
587 }
588 }
589 return valid;
590 }
591
592 private boolean verifyNamesUnique(List<? extends TemplateMethod> methods) {
593 boolean valid = true;
594 for (int i = 0; i < methods.size(); i++) {
595 TemplateMethod m1 = methods.get(i);
596 for (int j = i + 1; j < methods.size(); j++) {
597 TemplateMethod m2 = methods.get(j);
598
599 if (m1.getMethodName().equalsIgnoreCase(m2.getMethodName())) {
600 log.error(m1.getMethod(), m1.getMarkerAnnotation(), "Method name '%s' used multiple times", m1.getMethodName());
601 log.error(m2.getMethod(), m2.getMarkerAnnotation(), "Method name '%s' used multiple times", m1.getMethodName());
602 return false;
603 }
604 }
605 }
606 return valid;
607 }
608
609
610 private static boolean isGenericShortCutMethod(NodeData node, TemplateMethod method) {
611 for (NodeFieldData field : node.getFields()) {
612 ActualParameter parameter = method.findParameter(field.getName());
613 if (parameter == null) {
614 continue;
615 }
616 if (!Utils.typeEquals(node.getTypeSystem().getGenericType(), parameter.getActualType())) {
617 return false;
618 }
619 }
620 return true;
621 }
622
623 private static Map<String, List<ShortCircuitData>> groupShortCircuits(List<ShortCircuitData> shortCircuits) {
624 Map<String, List<ShortCircuitData>> group = new HashMap<>();
625 for (ShortCircuitData shortCircuit : shortCircuits) {
626 List<ShortCircuitData> circuits = group.get(shortCircuit.getValueName());
627 if (circuits == null) {
628 circuits = new ArrayList<>();
629 group.put(shortCircuit.getValueName(), circuits);
630 }
631 circuits.add(shortCircuit);
632 }
633 return group;
634 }
635
636
637 private TypeMirror getComponentType(TypeMirror type) {
638 if (type instanceof ArrayType) {
639 return getComponentType(((ArrayType) type).getComponentType());
640 }
641 return type;
642 }
643
644 private static List<TypeElement> findSuperClasses(List<TypeElement> collection, TypeElement element) {
645 if (element.getSuperclass() != null) {
646 TypeElement superElement = Utils.fromTypeMirror(element.getSuperclass());
647 if (superElement != null) {
648 findSuperClasses(collection, superElement);
649 }
650 }
651 collection.add(element);
652 return collection;
653 }
654
655
656 private boolean verifySpecializationOrder(TypeSystemData typeSystem, List<SpecializationData> specializations) {
657 for (int i = 0; i < specializations.size(); i++) {
658 SpecializationData m1 = specializations.get(i);
659 for (int j = i + 1; j < specializations.size(); j++) {
660 SpecializationData m2 = specializations.get(j);
661 int inferredOrder = compareSpecializationWithoutOrder(typeSystem, m1, m2);
662
663 if (m1.getOrder() != Specialization.DEFAULT_ORDER && m2.getOrder() != Specialization.DEFAULT_ORDER) {
664 int specOrder = m1.getOrder() - m2.getOrder();
665 if (specOrder == 0) {
666 log.error(m1.getMethod(), m1.getMarkerAnnotation(), "Order value %d used multiple times", m1.getOrder());
667 log.error(m2.getMethod(), m2.getMarkerAnnotation(), "Order value %d used multiple times", m1.getOrder());
668 return false;
669 } else if ((specOrder < 0 && inferredOrder > 0) || (specOrder > 0 && inferredOrder < 0)) {
670 log.error(m1.getMethod(), m1.getMarkerAnnotation(), "Explicit order values %d and %d are inconsistent with type lattice ordering.", m1.getOrder(), m2.getOrder());
671 log.error(m2.getMethod(), m2.getMarkerAnnotation(), "Explicit order values %d and %d are inconsistent with type lattice ordering.", m1.getOrder(), m2.getOrder());
672 return false;
673 }
674 } else if (inferredOrder == 0) {
675 SpecializationData m = (m1.getOrder() == Specialization.DEFAULT_ORDER ? m1 : m2);
676 log.error(m.getMethod(), m.getMarkerAnnotation(), "Cannot calculate a consistent order for this specialization. Define the order attribute to resolve this.");
677 return false;
678 }
679 }
680 }
681 return true;
682 }
683
684
685 private boolean verifySpecializationThrows(TypeSystemData typeSystem, List<SpecializationData> specializations) {
686 Map<String, SpecializationData> specializationMap = new HashMap<>();
687 for (SpecializationData spec : specializations) {
688 specializationMap.put(spec.getMethodName(), spec);
689 }
690 boolean valid = true;
691 for (SpecializationData sourceSpecialization : specializations) {
692 if (sourceSpecialization.getExceptions() != null) {
693 for (SpecializationThrowsData throwsData : sourceSpecialization.getExceptions()) {
694 SpecializationData targetSpecialization = specializationMap.get(throwsData.getTransitionToName());
695 AnnotationValue value = Utils.getAnnotationValue(throwsData.getAnnotationMirror(), "transitionTo");
696
697 if (targetSpecialization != null) {
698 log.error("Specialization throws current %s target %s compare %s.", sourceSpecialization.getMethodName(), targetSpecialization.getMethodName(), compareSpecialization(typeSystem, sourceSpecialization, targetSpecialization));
699 }
700
701 if (targetSpecialization == null) {
702 log.error(throwsData.getSpecialization().getMethod(), throwsData.getAnnotationMirror(), value,
703 "Specialization with name '%s' not found.", throwsData.getTransitionToName());
704 valid = false;
705 } else if (compareSpecialization(typeSystem, sourceSpecialization, targetSpecialization) >= 0) {
706 log.error(throwsData.getSpecialization().getMethod(), throwsData.getAnnotationMirror(), value,
707 "The order of the target specializalization must be higher than the source specialization.", throwsData.getTransitionToName());
708 valid = false;
709 }
710
711 for (SpecializationThrowsData otherThrowsData : sourceSpecialization.getExceptions()) {
712 if (otherThrowsData != throwsData
713 && Utils.typeEquals(otherThrowsData.getJavaClass(), throwsData.getJavaClass())) {
714 AnnotationValue javaClassValue = Utils.getAnnotationValue(throwsData.getAnnotationMirror(), "javaClass");
715 log.error(throwsData.getSpecialization().getMethod(), throwsData.getAnnotationMirror(), javaClassValue,
716 "Duplicate exception type.", throwsData.getTransitionToName());
717 valid = false;
718 }
719 }
720 }
721 }
722 }
723 return valid;
724 }
725
726
727 private static int compareSpecialization(TypeSystemData typeSystem, SpecializationData m1, SpecializationData m2) {
728 if (m1 == m2) {
729 return 0;
730 }
731 int result = compareSpecializationWithoutOrder(typeSystem, m1, m2);
732 if (result == 0) {
733 if (m1.getOrder() != Specialization.DEFAULT_ORDER && m2.getOrder() != Specialization.DEFAULT_ORDER) {
734 return m1.getOrder() - m2.getOrder();
735 }
736 }
737 return result;
738 }
739
740 private static int compareSpecializationWithoutOrder(TypeSystemData typeSystem, SpecializationData m1, SpecializationData m2) {
741 if (m1.getSpecification() != m2.getSpecification()) {
742 throw new UnsupportedOperationException("Cannot compare two specializations with different specifications.");
743 }
744
745 int result = compareActualParameter(typeSystem, m1.getReturnType(), m2.getReturnType());
746
747 for (ParameterSpec spec : m1.getSpecification().getParameters()) {
748 ActualParameter p1 = m1.findParameter(spec);
749 ActualParameter p2 = m2.findParameter(spec);
750
751 if (p1 != null && p2 != null && !Utils.typeEquals(p1.getActualType(), p2.getActualType())) {
752 int typeResult = compareActualParameter(typeSystem, p1, p2);
753 if (result == 0) {
754 result = typeResult;
755 } else if (Math.signum(result) != Math.signum(typeResult)) {
756 // We cannot define an order.
757 return 0;
758 }
759 }
760 }
761 return result;
762 }
763
764 private static int compareActualParameter(TypeSystemData typeSystem, ActualParameter p1, ActualParameter p2) {
765 int index1 = typeSystem.findType(p1.getActualType());
766 int index2 = typeSystem.findType(p2.getActualType());
767
768 assert index1 != index2;
769 assert !(index1 == -1 ^ index2 == -1);
770
771 return index1 - index2;
772 }
773
774
775 @Override
776 public Class< ? extends Annotation> getAnnotationType() {
777 return null;
778 }
779
780 @Override
781 public List<Class< ? extends Annotation>> getTypeDelegatedAnnotationTypes() {
782 return annotations;
783 }
784
785 }