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