Mercurial > hg > graal-compiler
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 } |