comparison graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java @ 10597:79041ab43660

Truffle-DSL: API-change: Renamed truffle.api.codegen to truffle.api.dsl for all projects and packages.
author Christian Humer <christian.humer@gmail.com>
date Mon, 01 Jul 2013 20:58:32 +0200
parents
children e93efe3ba5f4
comparison
equal deleted inserted replaced
10596:f43eb2f1bbbc 10597:79041ab43660
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.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 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.node.NodeChildData.*;
37 import com.oracle.truffle.dsl.processor.template.*;
38 import com.oracle.truffle.dsl.processor.template.TemplateMethod.*;
39 import com.oracle.truffle.dsl.processor.typesystem.*;
40
41 public class NodeParser extends TemplateParser<NodeData> {
42
43 public static final List<Class<? extends Annotation>> ANNOTATIONS = Arrays.asList(Generic.class, TypeSystemReference.class, ShortCircuit.class, Specialization.class, SpecializationListener.class,
44 NodeContainer.class, NodeChild.class, NodeChildren.class, NodeId.class);
45
46 private Map<String, NodeData> parsedNodes;
47
48 public NodeParser(ProcessorContext c) {
49 super(c);
50 }
51
52 @Override
53 protected NodeData parse(Element element, AnnotationMirror mirror) {
54 assert element instanceof TypeElement;
55 NodeData node = null;
56 try {
57 parsedNodes = new HashMap<>();
58 node = resolveNode((TypeElement) element);
59 if (Log.DEBUG) {
60 NodeData parsed = parsedNodes.get(Utils.getQualifiedName((TypeElement) element));
61 if (node != null) {
62 String dump = parsed.dump();
63 log.message(Kind.ERROR, null, null, null, dump);
64 }
65 }
66 } finally {
67 parsedNodes = null;
68 }
69
70 return node;
71 }
72
73 @Override
74 protected NodeData filterErrorElements(NodeData model) {
75 for (Iterator<NodeData> iterator = model.getDeclaredNodes().iterator(); iterator.hasNext();) {
76 NodeData node = filterErrorElements(iterator.next());
77 if (node == null) {
78 iterator.remove();
79 }
80 }
81 if (model.hasErrors()) {
82 return null;
83 }
84 return model;
85 }
86
87 @Override
88 public boolean isDelegateToRootDeclaredType() {
89 return true;
90 }
91
92 @Override
93 public Class<? extends Annotation> getAnnotationType() {
94 return null;
95 }
96
97 @Override
98 public List<Class<? extends Annotation>> getTypeDelegatedAnnotationTypes() {
99 return ANNOTATIONS;
100 }
101
102 private NodeData resolveNode(TypeElement rootType) {
103 String typeName = Utils.getQualifiedName(rootType);
104 if (parsedNodes.containsKey(typeName)) {
105 return parsedNodes.get(typeName);
106 }
107
108 List<? extends TypeElement> types = ElementFilter.typesIn(rootType.getEnclosedElements());
109
110 List<NodeData> children = new ArrayList<>();
111 for (TypeElement childElement : types) {
112 NodeData childNode = resolveNode(childElement);
113 if (childNode != null) {
114 children.add(childNode);
115 }
116 }
117
118 NodeData rootNode = parseNode(rootType);
119 if (rootNode == null && children.size() > 0) {
120 rootNode = new NodeData(rootType, rootType.getSimpleName().toString());
121 }
122
123 parsedNodes.put(typeName, rootNode);
124
125 if (rootNode != null) {
126 children.addAll(rootNode.getDeclaredNodes());
127 rootNode.setDeclaredNodes(children);
128 }
129
130 return rootNode;
131 }
132
133 private NodeData parseNode(TypeElement originalTemplateType) {
134 // reloading the type elements is needed for ecj
135 TypeElement templateType = Utils.fromTypeMirror(context.reloadTypeElement(originalTemplateType));
136
137 if (Utils.findAnnotationMirror(processingEnv, originalTemplateType, GeneratedBy.class) != null) {
138 // generated nodes should not get called again.
139 return null;
140 }
141
142 List<TypeElement> lookupTypes = findSuperClasses(new ArrayList<TypeElement>(), templateType);
143 Collections.reverse(lookupTypes);
144
145 AnnotationMirror nodeClass = findFirstAnnotation(lookupTypes, NodeContainer.class);
146 TypeMirror nodeType = null;
147 if (Utils.isAssignable(context, templateType.asType(), context.getTruffleTypes().getNode())) {
148 nodeType = templateType.asType();
149 }
150 if (nodeClass != null) {
151 nodeType = inheritType(nodeClass, "value", nodeType);
152 }
153
154 if (nodeType == null) {
155 return null;
156 }
157
158 Elements elementUtil = context.getEnvironment().getElementUtils();
159 Set<Element> elementSet = new HashSet<>(elementUtil.getAllMembers(templateType));
160 if (!Utils.typeEquals(templateType.asType(), nodeType)) {
161 elementSet.addAll(elementUtil.getAllMembers(Utils.fromTypeMirror(nodeType)));
162
163 List<TypeElement> nodeLookupTypes = findSuperClasses(new ArrayList<TypeElement>(), Utils.fromTypeMirror(nodeType));
164 Collections.reverse(nodeLookupTypes);
165 lookupTypes.addAll(nodeLookupTypes);
166
167 Set<TypeElement> types = new HashSet<>();
168 for (ListIterator<TypeElement> iterator = lookupTypes.listIterator(); iterator.hasNext();) {
169 TypeElement typeElement = iterator.next();
170 if (types.contains(typeElement)) {
171 iterator.remove();
172 } else {
173 types.add(typeElement);
174 }
175 }
176 }
177 List<Element> elements = new ArrayList<>(elementSet);
178
179 NodeData node = parseNodeData(templateType, nodeType, elements, lookupTypes);
180
181 if (node.hasErrors()) {
182 return node; // error sync point
183 }
184
185 parseMethods(node, elements);
186
187 if (node.hasErrors()) {
188 return node;
189 }
190
191 List<NodeData> nodes;
192
193 if (node.isNodeContainer()) {
194 nodes = splitNodeData(node);
195 } else {
196 nodes = new ArrayList<>();
197 nodes.add(node);
198 }
199
200 for (NodeData splittedNode : nodes) {
201 finalizeSpecializations(elements, splittedNode);
202 verifyNode(splittedNode, elements);
203 splittedNode.setPolymorphicSpecializations(createPolymorphicSpecializations(splittedNode));
204 assignShortCircuitsToSpecializations(splittedNode);
205 }
206
207 if (node.isNodeContainer()) {
208 node.setDeclaredNodes(nodes);
209 node.setSpecializationListeners(new ArrayList<SpecializationListenerData>());
210 node.setSpecializations(new ArrayList<SpecializationData>());
211 }
212 return node;
213 }
214
215 private List<SpecializationData> createPolymorphicSpecializations(NodeData node) {
216 if (!node.needsRewrites(context) || node.getPolymorphicDepth() <= 1) {
217 return Collections.emptyList();
218 }
219
220 Signature genericSignature = node.getGenericSpecialization().getSignature();
221 Set<Signature> signatures = new HashSet<>();
222
223 for (SpecializationData specialization1 : node.getSpecializations()) {
224 Signature signature = specialization1.getSignature();
225
226 for (SpecializationData specialization2 : node.getSpecializations()) {
227 if (specialization1 == specialization2) {
228 continue;
229 }
230 signatures.add(signature.combine(genericSignature, specialization2.getSignature()));
231 }
232 }
233
234 while (true) {
235 List<Signature> newSignatures = new ArrayList<>();
236 for (Signature signature1 : signatures) {
237 for (Signature signature2 : signatures) {
238 if (signature1 == signature2) {
239 continue;
240 }
241 newSignatures.add(signature1.combine(genericSignature, signature2));
242 }
243 }
244 if (!signatures.addAll(newSignatures)) {
245 break;
246 }
247 }
248
249 List<Signature> sortedSignatures = new ArrayList<>(signatures);
250 Collections.sort(sortedSignatures);
251
252 List<SpecializationData> specializations = new ArrayList<>();
253 SpecializationData generic = node.getGenericSpecialization();
254 for (Signature signature : sortedSignatures) {
255 SpecializationData specialization = new SpecializationData(generic, false, false, true);
256 specialization.forceFrame(context.getTruffleTypes().getFrame());
257 specialization.setNode(node);
258 specialization.updateSignature(signature);
259
260 if (specialization.isGenericSpecialization(context)) {
261 specializations.add(0, specialization);
262 } else {
263 specializations.add(specialization);
264 }
265 }
266
267 return specializations;
268 }
269
270 private NodeData parseNodeData(TypeElement templateType, TypeMirror nodeType, List<? extends Element> elements, List<TypeElement> lookupTypes) {
271 NodeData nodeData = new NodeData(templateType, templateType.getSimpleName().toString());
272
273 AnnotationMirror typeSystemMirror = findFirstAnnotation(lookupTypes, TypeSystemReference.class);
274 if (typeSystemMirror == null) {
275 nodeData.addError("No @%s annotation found in type hierarchy of %s.", TypeSystemReference.class.getSimpleName(), Utils.getQualifiedName(nodeType));
276 return nodeData;
277 }
278
279 TypeMirror typeSytemType = Utils.getAnnotationValue(TypeMirror.class, typeSystemMirror, "value");
280 final TypeSystemData typeSystem = (TypeSystemData) context.getTemplate(typeSytemType, true);
281 if (typeSystem == null) {
282 nodeData.addError("The used type system '%s' is invalid or not a Node.", Utils.getQualifiedName(typeSytemType));
283 return nodeData;
284 }
285
286 AnnotationMirror polymorphicMirror = findFirstAnnotation(lookupTypes, PolymorphicLimit.class);
287 if (polymorphicMirror != null) {
288 AnnotationValue limitValue = Utils.getAnnotationValue(polymorphicMirror, "value");
289 int polymorphicLimit = Utils.getAnnotationValue(Integer.class, polymorphicMirror, "value");
290 if (polymorphicLimit < 1) {
291 nodeData.addError(limitValue, "Invalid polymorphic limit %s.", polymorphicLimit);
292 }
293 nodeData.setPolymorphicDepth(polymorphicLimit);
294 }
295
296 List<String> assumptionsList = new ArrayList<>();
297 for (int i = lookupTypes.size() - 1; i >= 0; i--) {
298 TypeElement type = lookupTypes.get(i);
299 AnnotationMirror assumptions = Utils.findAnnotationMirror(context.getEnvironment(), type, NodeAssumptions.class);
300 if (assumptions != null) {
301 List<String> assumptionStrings = Utils.getAnnotationValueList(String.class, assumptions, "value");
302 for (String string : assumptionStrings) {
303 if (assumptionsList.contains(string)) {
304 assumptionsList.remove(string);
305 }
306 assumptionsList.add(string);
307 }
308 }
309 }
310 AnnotationMirror nodeInfoMirror = findFirstAnnotation(lookupTypes, NodeInfo.class);
311 if (nodeInfoMirror != null) {
312 nodeData.setShortName(Utils.getAnnotationValue(String.class, nodeInfoMirror, "shortName"));
313 }
314
315 nodeData.setAssumptions(new ArrayList<>(assumptionsList));
316 nodeData.setNodeType(nodeType);
317 AnnotationMirror nodeContainer = findFirstAnnotation(lookupTypes, NodeContainer.class);
318 nodeData.setNodeContainer(nodeContainer != null);
319 nodeData.setTypeSystem(typeSystem);
320 nodeData.setFields(parseFields(elements));
321 parsedNodes.put(Utils.getQualifiedName(templateType), nodeData);
322 // parseChildren invokes cyclic parsing.
323 nodeData.setChildren(parseChildren(elements, lookupTypes));
324 nodeData.setExecutableTypes(groupExecutableTypes(new ExecutableTypeMethodParser(context, nodeData).parse(elements)));
325
326 return nodeData;
327 }
328
329 private static List<NodeFieldData> parseFields(List<? extends Element> elements) {
330 List<NodeFieldData> fields = new ArrayList<>();
331 for (VariableElement field : ElementFilter.fieldsIn(elements)) {
332 if (field.getModifiers().contains(Modifier.STATIC)) {
333 continue;
334 }
335 if (field.getModifiers().contains(Modifier.PUBLIC) || field.getModifiers().contains(Modifier.PROTECTED)) {
336 fields.add(new NodeFieldData(field));
337 }
338 }
339 return fields;
340 }
341
342 private List<NodeChildData> parseChildren(List<? extends Element> elements, final List<TypeElement> typeHierarchy) {
343 Set<String> shortCircuits = new HashSet<>();
344 for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
345 AnnotationMirror mirror = Utils.findAnnotationMirror(processingEnv, method, ShortCircuit.class);
346 if (mirror != null) {
347 shortCircuits.add(Utils.getAnnotationValue(String.class, mirror, "value"));
348 }
349 }
350 Map<String, TypeMirror> castNodeTypes = new HashMap<>();
351 for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
352 AnnotationMirror mirror = Utils.findAnnotationMirror(processingEnv, method, CreateCast.class);
353 if (mirror != null) {
354 List<String> children = (Utils.getAnnotationValueList(String.class, mirror, "value"));
355 if (children != null) {
356 for (String child : children) {
357 castNodeTypes.put(child, method.getReturnType());
358 }
359 }
360 }
361 }
362
363 List<NodeChildData> parsedChildren = new ArrayList<>();
364 List<TypeElement> typeHierarchyReversed = new ArrayList<>(typeHierarchy);
365 Collections.reverse(typeHierarchyReversed);
366 for (TypeElement type : typeHierarchyReversed) {
367 AnnotationMirror nodeClassMirror = Utils.findAnnotationMirror(processingEnv, type, NodeContainer.class);
368 AnnotationMirror nodeChildrenMirror = Utils.findAnnotationMirror(processingEnv, type, NodeChildren.class);
369
370 TypeMirror nodeClassType = type.getSuperclass();
371 if (!Utils.isAssignable(context, nodeClassType, context.getTruffleTypes().getNode())) {
372 nodeClassType = null;
373 }
374
375 if (nodeClassMirror != null) {
376 nodeClassType = inheritType(nodeClassMirror, "value", nodeClassType);
377 }
378
379 List<AnnotationMirror> children = Utils.collectAnnotations(context, nodeChildrenMirror, "value", type, NodeChild.class);
380 int index = 0;
381 for (AnnotationMirror childMirror : children) {
382 String name = Utils.getAnnotationValue(String.class, childMirror, "value");
383 if (name.equals("")) {
384 name = "child" + index;
385 }
386
387 Cardinality cardinality = Cardinality.ONE;
388
389 TypeMirror childType = inheritType(childMirror, "type", nodeClassType);
390 if (childType.getKind() == TypeKind.ARRAY) {
391 cardinality = Cardinality.MANY;
392 }
393
394 TypeMirror originalChildType = childType;
395 TypeMirror castNodeType = castNodeTypes.get(name);
396 if (castNodeType != null) {
397 childType = castNodeType;
398 }
399
400 Element getter = findGetter(elements, name, childType);
401
402 ExecutionKind kind = ExecutionKind.DEFAULT;
403 if (shortCircuits.contains(name)) {
404 kind = ExecutionKind.SHORT_CIRCUIT;
405 }
406
407 NodeChildData nodeChild = new NodeChildData(type, childMirror, name, childType, originalChildType, getter, cardinality, kind);
408
409 parsedChildren.add(nodeChild);
410
411 verifyNodeChild(nodeChild);
412 if (nodeChild.hasErrors()) {
413 continue;
414 }
415
416 NodeData fieldNodeData = resolveNode(Utils.fromTypeMirror(childType));
417 nodeChild.setNode(fieldNodeData);
418 if (fieldNodeData == null) {
419 nodeChild.addError("Node type '%s' is invalid or not a valid Node.", Utils.getQualifiedName(childType));
420 }
421
422 }
423 index++;
424 }
425
426 List<NodeChildData> filteredChildren = new ArrayList<>();
427 Set<String> encounteredNames = new HashSet<>();
428 for (int i = parsedChildren.size() - 1; i >= 0; i--) {
429 NodeChildData child = parsedChildren.get(i);
430 if (!encounteredNames.contains(child.getName())) {
431 filteredChildren.add(0, child);
432 encounteredNames.add(child.getName());
433 }
434 }
435
436 for (NodeChildData child : filteredChildren) {
437 List<String> executeWithStrings = Utils.getAnnotationValueList(String.class, child.getMessageAnnotation(), "executeWith");
438 AnnotationValue executeWithValue = Utils.getAnnotationValue(child.getMessageAnnotation(), "executeWith");
439 List<NodeChildData> executeWith = new ArrayList<>();
440 for (String executeWithString : executeWithStrings) {
441
442 if (child.getName().equals(executeWithString)) {
443 child.addError(executeWithValue, "The child node '%s' cannot be executed with itself.", executeWithString);
444 continue;
445 }
446
447 NodeChildData found = null;
448 boolean before = true;
449 for (NodeChildData resolveChild : filteredChildren) {
450 if (resolveChild == child) {
451 before = false;
452 continue;
453 }
454 if (resolveChild.getName().equals(executeWithString)) {
455 found = resolveChild;
456 break;
457 }
458 }
459
460 if (found == null) {
461 child.addError(executeWithValue, "The child node '%s' cannot be executed with '%s'. The child node was not found.", child.getName(), executeWithString);
462 continue;
463 } else if (!before) {
464 child.addError(executeWithValue, "The child node '%s' cannot be executed with '%s'. The node %s is executed after the current node.", child.getName(), executeWithString,
465 executeWithString);
466 continue;
467 }
468 executeWith.add(found);
469 }
470 child.setExecuteWith(executeWith);
471 if (child.getNodeData() == null) {
472 continue;
473 }
474
475 List<ExecutableTypeData> types = child.findGenericExecutableTypes(context);
476 if (types.isEmpty()) {
477 child.addError(executeWithValue, "No generic execute method found with %s evaluated arguments for node type %s.", executeWith.size(), Utils.getSimpleName(child.getNodeType()));
478 continue;
479 }
480 }
481
482 return filteredChildren;
483 }
484
485 private void parseMethods(final NodeData node, List<Element> elements) {
486 node.setShortCircuits(new ShortCircuitParser(context, node).parse(elements));
487 node.setSpecializationListeners(new SpecializationListenerParser(context, node).parse(elements));
488 List<SpecializationData> generics = new GenericParser(context, node).parse(elements);
489 List<SpecializationData> specializations = new SpecializationMethodParser(context, node).parse(elements);
490 node.setCasts(new CreateCastParser(context, node).parse(elements));
491
492 List<SpecializationData> allSpecializations = new ArrayList<>();
493 allSpecializations.addAll(generics);
494 allSpecializations.addAll(specializations);
495
496 node.setSpecializations(allSpecializations);
497 }
498
499 private static List<NodeData> splitNodeData(NodeData node) {
500 SortedMap<String, List<SpecializationData>> groupedSpecializations = groupByNodeId(node.getSpecializations());
501 SortedMap<String, List<SpecializationListenerData>> groupedListeners = groupByNodeId(node.getSpecializationListeners());
502 SortedMap<String, List<CreateCastData>> groupedCasts = groupByNodeId(node.getCasts());
503
504 Set<String> ids = new TreeSet<>();
505 ids.addAll(groupedSpecializations.keySet());
506 ids.addAll(groupedListeners.keySet());
507
508 List<NodeData> splitted = new ArrayList<>();
509 for (String id : ids) {
510 List<SpecializationData> specializations = groupedSpecializations.get(id);
511 List<SpecializationListenerData> listeners = groupedListeners.get(id);
512 List<CreateCastData> casts = groupedCasts.get(id);
513
514 if (specializations == null) {
515 specializations = new ArrayList<>();
516 }
517
518 if (listeners == null) {
519 listeners = new ArrayList<>();
520 }
521
522 String nodeId = node.getNodeId();
523 if (nodeId.endsWith("Node") && !nodeId.equals("Node")) {
524 nodeId = nodeId.substring(0, nodeId.length() - 4);
525 }
526 String newNodeId = nodeId + Utils.firstLetterUpperCase(id);
527 NodeData copy = new NodeData(node, id, newNodeId);
528
529 copy.setSpecializations(specializations);
530 copy.setSpecializationListeners(listeners);
531 copy.setCasts(casts);
532
533 splitted.add(copy);
534 }
535
536 node.setSpecializations(new ArrayList<SpecializationData>());
537 node.setSpecializationListeners(new ArrayList<SpecializationListenerData>());
538 node.setCasts(new ArrayList<CreateCastData>());
539
540 return splitted;
541 }
542
543 private void finalizeSpecializations(List<Element> elements, final NodeData node) {
544 List<SpecializationData> specializations = new ArrayList<>(node.getSpecializations());
545
546 if (specializations.isEmpty()) {
547 return;
548 }
549
550 for (SpecializationData specialization : specializations) {
551 matchGuards(elements, specialization);
552 }
553
554 List<SpecializationData> generics = new ArrayList<>();
555 for (SpecializationData spec : specializations) {
556 if (spec.isGeneric()) {
557 generics.add(spec);
558 }
559 }
560
561 if (generics.size() == 1 && specializations.size() == 1) {
562 for (SpecializationData generic : generics) {
563 generic.addError("@%s defined but no @%s.", Generic.class.getSimpleName(), Specialization.class.getSimpleName());
564 }
565 }
566
567 SpecializationData genericSpecialization = null;
568 if (generics.size() > 1) {
569 for (SpecializationData generic : generics) {
570 generic.addError("Only @%s is allowed per operation.", Generic.class.getSimpleName());
571 }
572 return;
573 } else if (generics.size() == 1) {
574 genericSpecialization = generics.get(0);
575 } else if (node.needsRewrites(context)) {
576 SpecializationData specialization = specializations.get(0);
577 GenericParser parser = new GenericParser(context, node);
578 MethodSpec specification = parser.createDefaultMethodSpec(specialization.getMethod(), null, true, null);
579
580 ExecutableTypeData anyGenericReturnType = node.findAnyGenericExecutableType(context, 0);
581 assert anyGenericReturnType != null;
582
583 ActualParameter returnType = new ActualParameter(specification.getReturnType(), anyGenericReturnType.getType(), 0, false);
584 List<ActualParameter> parameters = new ArrayList<>();
585 for (ActualParameter specializationParameter : specialization.getParameters()) {
586 ParameterSpec parameterSpec = specification.findParameterSpec(specializationParameter.getSpecification().getName());
587 NodeChildData child = node.findChild(parameterSpec.getName());
588 TypeData actualType;
589 if (child == null) {
590 actualType = specializationParameter.getTypeSystemType();
591 } else {
592 ExecutableTypeData paramType = child.findAnyGenericExecutableType(context);
593 assert paramType != null;
594 actualType = paramType.getType();
595 }
596
597 if (actualType != null) {
598 parameters.add(new ActualParameter(parameterSpec, actualType, specializationParameter.getIndex(), specializationParameter.isImplicit()));
599 } else {
600 parameters.add(new ActualParameter(parameterSpec, specializationParameter.getType(), specializationParameter.getIndex(), specializationParameter.isImplicit()));
601 }
602 }
603 TemplateMethod genericMethod = new TemplateMethod("Generic", node, specification, null, null, returnType, parameters);
604 genericSpecialization = new SpecializationData(genericMethod, true, false, false);
605
606 specializations.add(genericSpecialization);
607 }
608
609 if (genericSpecialization != null) {
610 for (ActualParameter parameter : genericSpecialization.getReturnTypeAndParameters()) {
611 if (Utils.isObject(parameter.getType())) {
612 continue;
613 }
614 Set<String> types = new HashSet<>();
615 for (SpecializationData specialization : specializations) {
616 ActualParameter actualParameter = specialization.findParameter(parameter.getLocalName());
617 if (actualParameter != null) {
618 types.add(Utils.getQualifiedName(actualParameter.getType()));
619 }
620 }
621 if (types.size() > 1) {
622 genericSpecialization.replaceParameter(parameter.getLocalName(), new ActualParameter(parameter, node.getTypeSystem().getGenericTypeData()));
623 }
624 }
625 TemplateMethod uninializedMethod = new TemplateMethod("Uninitialized", node, genericSpecialization.getSpecification(), null, null, genericSpecialization.getReturnType(),
626 genericSpecialization.getParameters());
627 // should not use messages from generic specialization
628 uninializedMethod.getMessages().clear();
629 specializations.add(new SpecializationData(uninializedMethod, false, true, false));
630 }
631
632 Collections.sort(specializations);
633
634 node.setSpecializations(specializations);
635
636 List<SpecializationData> needsId = new ArrayList<>();
637 for (SpecializationData specialization : specializations) {
638 if (specialization.isGeneric()) {
639 specialization.setId("Generic");
640 } else if (specialization.isUninitialized()) {
641 specialization.setId("Uninitialized");
642 } else {
643 needsId.add(specialization);
644 }
645 }
646
647 // verify specialization parameter length
648 if (verifySpecializationParameters(node)) {
649 List<String> ids = calculateSpecializationIds(needsId);
650 for (int i = 0; i < ids.size(); i++) {
651 needsId.get(i).setId(ids.get(i));
652 }
653 }
654
655 // calculate reachability
656 int specializationCount = 0;
657 boolean reachable = true;
658 for (SpecializationData specialization : specializations) {
659 if (specialization.isUninitialized()) {
660 specialization.setReachable(true);
661 continue;
662 }
663 if (!reachable && specialization.getMethod() != null) {
664 specialization.addError("%s is not reachable.", specialization.isGeneric() ? "Generic" : "Specialization");
665 }
666 specialization.setReachable(reachable);
667 if (!specialization.hasRewrite(context)) {
668 reachable = false;
669 }
670 if (!specialization.isGeneric()) {
671 specializationCount++;
672 }
673 }
674
675 if (node.getPolymorphicDepth() < 0) {
676 node.setPolymorphicDepth(specializationCount - 1);
677 }
678
679 // reduce polymorphicness if generic is not reachable
680 if (node.getGenericSpecialization() != null && !node.getGenericSpecialization().isReachable()) {
681 node.setPolymorphicDepth(1);
682 }
683 }
684
685 private void assignShortCircuitsToSpecializations(NodeData node) {
686 Map<String, List<ShortCircuitData>> groupedShortCircuits = groupShortCircuits(node.getShortCircuits());
687
688 boolean valid = true;
689 for (NodeChildData field : node.filterFields(ExecutionKind.SHORT_CIRCUIT)) {
690 String valueName = field.getName();
691 List<ShortCircuitData> availableCircuits = groupedShortCircuits.get(valueName);
692
693 if (availableCircuits == null || availableCircuits.isEmpty()) {
694 node.addError("@%s method for short cut value '%s' required.", ShortCircuit.class.getSimpleName(), valueName);
695 valid = false;
696 continue;
697 }
698
699 boolean sameMethodName = true;
700 String methodName = availableCircuits.get(0).getMethodName();
701 for (ShortCircuitData circuit : availableCircuits) {
702 if (!circuit.getMethodName().equals(methodName)) {
703 sameMethodName = false;
704 }
705 }
706
707 if (!sameMethodName) {
708 for (ShortCircuitData circuit : availableCircuits) {
709 circuit.addError("All short circuits for short cut value '%s' must have the same method name.", valueName);
710 }
711 valid = false;
712 continue;
713 }
714
715 ShortCircuitData genericCircuit = null;
716 for (ShortCircuitData circuit : availableCircuits) {
717 if (isGenericShortCutMethod(node, circuit)) {
718 genericCircuit = circuit;
719 break;
720 }
721 }
722
723 if (genericCircuit == null) {
724 node.addError("No generic @%s method available for short cut value '%s'.", ShortCircuit.class.getSimpleName(), valueName);
725 valid = false;
726 continue;
727 }
728
729 for (ShortCircuitData circuit : availableCircuits) {
730 if (circuit != genericCircuit) {
731 circuit.setGenericShortCircuitMethod(genericCircuit);
732 }
733 }
734 }
735
736 if (!valid) {
737 return;
738 }
739
740 NodeChildData[] fields = node.filterFields(ExecutionKind.SHORT_CIRCUIT);
741 List<SpecializationData> specializations = new ArrayList<>();
742 specializations.addAll(node.getSpecializations());
743 specializations.addAll(node.getPolymorphicSpecializations());
744
745 for (SpecializationData specialization : specializations) {
746 List<ShortCircuitData> assignedShortCuts = new ArrayList<>(fields.length);
747
748 for (int i = 0; i < fields.length; i++) {
749 List<ShortCircuitData> availableShortCuts = groupedShortCircuits.get(fields[i].getName());
750
751 ShortCircuitData genericShortCircuit = null;
752 ShortCircuitData compatibleShortCircuit = null;
753 for (ShortCircuitData circuit : availableShortCuts) {
754 if (circuit.isGeneric()) {
755 genericShortCircuit = circuit;
756 } else if (circuit.isCompatibleTo(specialization)) {
757 compatibleShortCircuit = circuit;
758 }
759 }
760
761 if (compatibleShortCircuit == null) {
762 compatibleShortCircuit = genericShortCircuit;
763 }
764 assignedShortCuts.add(compatibleShortCircuit);
765 }
766 specialization.setShortCircuits(assignedShortCuts);
767 }
768 }
769
770 private void matchGuards(List<Element> elements, SpecializationData specialization) {
771 if (specialization.getGuardDefinitions().isEmpty()) {
772 specialization.setGuards(Collections.<GuardData> emptyList());
773 return;
774 }
775
776 List<GuardData> foundGuards = new ArrayList<>();
777 List<ExecutableElement> methods = ElementFilter.methodsIn(elements);
778 for (String guardDefinition : specialization.getGuardDefinitions()) {
779 GuardParser parser = new GuardParser(context, specialization, guardDefinition);
780 List<GuardData> guards = parser.parse(methods);
781 if (!guards.isEmpty()) {
782 foundGuards.add(guards.get(0));
783 } else {
784 // error no guard found
785 MethodSpec spec = parser.createSpecification(specialization.getMethod(), null);
786 spec.applyTypeDefinitions("types");
787 specialization.addError("Guard with method name '%s' not found. Expected signature: %n%s", guardDefinition, spec.toSignatureString("guard"));
788 }
789 }
790
791 specialization.setGuards(foundGuards);
792
793 }
794
795 private static List<String> calculateSpecializationIds(List<SpecializationData> specializations) {
796 int lastSize = -1;
797 List<List<String>> signatureChunks = new ArrayList<>();
798 for (SpecializationData other : specializations) {
799 if (other.isUninitialized() || other.isGeneric()) {
800 continue;
801 }
802 List<String> paramIds = new LinkedList<>();
803 paramIds.add(Utils.getTypeId(other.getReturnType().getType()));
804 for (ActualParameter param : other.getParameters()) {
805 if (other.getNode().findChild(param.getSpecification().getName()) == null) {
806 continue;
807 }
808 paramIds.add(Utils.getTypeId(param.getType()));
809 }
810 assert lastSize == -1 || lastSize == paramIds.size();
811 if (lastSize != -1 && lastSize != paramIds.size()) {
812 throw new AssertionError();
813 }
814 signatureChunks.add(paramIds);
815 lastSize = paramIds.size();
816 }
817
818 // reduce id vertically
819 for (int i = 0; i < lastSize; i++) {
820 String prev = null;
821 boolean allSame = true;
822 for (List<String> signature : signatureChunks) {
823 String arg = signature.get(i);
824 if (prev == null) {
825 prev = arg;
826 continue;
827 } else if (!prev.equals(arg)) {
828 allSame = false;
829 break;
830 }
831 prev = arg;
832 }
833
834 if (allSame) {
835 for (List<String> signature : signatureChunks) {
836 signature.remove(i);
837 }
838 lastSize--;
839 }
840 }
841
842 // reduce id horizontally
843 for (List<String> signature : signatureChunks) {
844 if (signature.isEmpty()) {
845 continue;
846 }
847 String prev = null;
848 boolean allSame = true;
849 for (String arg : signature) {
850 if (prev == null) {
851 prev = arg;
852 continue;
853 } else if (!prev.equals(arg)) {
854 allSame = false;
855 break;
856 }
857 prev = arg;
858 }
859
860 if (allSame) {
861 signature.clear();
862 signature.add(prev);
863 }
864 }
865
866 // create signatures
867 List<String> signatures = new ArrayList<>();
868 for (List<String> signatureChunk : signatureChunks) {
869 StringBuilder b = new StringBuilder();
870 if (signatureChunk.isEmpty()) {
871 b.append("Default");
872 } else {
873 for (String s : signatureChunk) {
874 b.append(s);
875 }
876 }
877 signatures.add(b.toString());
878 }
879
880 Map<String, Integer> counts = new HashMap<>();
881 for (String s1 : signatures) {
882 Integer count = counts.get(s1);
883 if (count == null) {
884 count = 0;
885 }
886 count++;
887 counts.put(s1, count);
888 }
889
890 for (String s : counts.keySet()) {
891 int count = counts.get(s);
892 if (count > 1) {
893 int number = 0;
894 for (ListIterator<String> iterator = signatures.listIterator(); iterator.hasNext();) {
895 String s2 = iterator.next();
896 if (s.equals(s2)) {
897 iterator.set(s2 + number);
898 number++;
899 }
900 }
901 }
902 }
903
904 return signatures;
905 }
906
907 private void verifyNode(NodeData nodeData, List<? extends Element> elements) {
908 // verify order is not ambiguous
909 verifySpecializationOrder(nodeData);
910
911 verifyMissingAbstractMethods(nodeData, elements);
912
913 verifyConstructors(nodeData);
914
915 verifyNamingConvention(nodeData.getShortCircuits(), "needs");
916
917 verifySpecializationThrows(nodeData);
918 }
919
920 private static void verifyNodeChild(NodeChildData nodeChild) {
921 if (nodeChild.getNodeType() == null) {
922 nodeChild.addError("No valid node type could be resoleved.");
923 }
924 // FIXME verify node child
925 // FIXME verify node type set
926 }
927
928 private static void verifyMissingAbstractMethods(NodeData nodeData, List<? extends Element> originalElements) {
929 if (!nodeData.needsFactory()) {
930 // missing abstract methods only needs to be implemented
931 // if we need go generate factory for it.
932 return;
933 }
934
935 List<Element> elements = new ArrayList<>(originalElements);
936
937 Set<Element> unusedElements = new HashSet<>(elements);
938 for (TemplateMethod method : nodeData.getAllTemplateMethods()) {
939 unusedElements.remove(method.getMethod());
940 }
941 if (nodeData.getExtensionElements() != null) {
942 unusedElements.removeAll(nodeData.getExtensionElements());
943 }
944
945 for (NodeChildData child : nodeData.getChildren()) {
946 if (child.getAccessElement() != null) {
947 unusedElements.remove(child.getAccessElement());
948 }
949 }
950
951 for (ExecutableElement unusedMethod : ElementFilter.methodsIn(unusedElements)) {
952 if (unusedMethod.getModifiers().contains(Modifier.ABSTRACT)) {
953 nodeData.addError("The type %s must implement the inherited abstract method %s.", Utils.getSimpleName(nodeData.getTemplateType()), Utils.getReadableSignature(unusedMethod));
954 }
955 }
956 }
957
958 private void verifyConstructors(NodeData nodeData) {
959 if (!nodeData.needsRewrites(context)) {
960 // no specialization constructor is needed if the node never rewrites.
961 return;
962 }
963
964 TypeElement type = Utils.fromTypeMirror(nodeData.getNodeType());
965 List<ExecutableElement> constructors = ElementFilter.constructorsIn(type.getEnclosedElements());
966
967 boolean parametersFound = false;
968 for (ExecutableElement constructor : constructors) {
969 if (!constructor.getParameters().isEmpty()) {
970 parametersFound = true;
971 }
972 }
973 if (!parametersFound) {
974 return;
975 }
976 for (ExecutableElement e : constructors) {
977 if (e.getParameters().size() == 1) {
978 TypeMirror firstArg = e.getParameters().get(0).asType();
979 if (Utils.typeEquals(firstArg, nodeData.getNodeType())) {
980 if (e.getModifiers().contains(Modifier.PRIVATE)) {
981 nodeData.addError("The specialization constructor must not be private.");
982 } else if (constructors.size() <= 1) {
983 nodeData.addError("The specialization constructor must not be the only constructor. The definition of an alternative constructor is required.");
984 }
985 return;
986 }
987 }
988 }
989
990 // not found
991 nodeData.addError("Specialization constructor '%s(%s previousNode) { this(...); }' is required.", Utils.getSimpleName(type), Utils.getSimpleName(type));
992 }
993
994 private static boolean verifySpecializationParameters(NodeData nodeData) {
995 boolean valid = true;
996 int args = -1;
997 for (SpecializationData specializationData : nodeData.getSpecializations()) {
998 int signatureArgs = 0;
999 for (ActualParameter param : specializationData.getParameters()) {
1000 if (param.getSpecification().isSignature()) {
1001 signatureArgs++;
1002 }
1003 }
1004 if (args != -1 && args != signatureArgs) {
1005 valid = false;
1006 break;
1007 }
1008 args = signatureArgs;
1009 }
1010 if (!valid) {
1011 for (SpecializationData specialization : nodeData.getSpecializations()) {
1012 specialization.addError("All specializations must have the same number of arguments.");
1013 }
1014 }
1015 return valid;
1016 }
1017
1018 private static void verifySpecializationOrder(NodeData node) {
1019 List<SpecializationData> specializations = node.getSpecializations();
1020 for (int i = 0; i < specializations.size(); i++) {
1021 SpecializationData m1 = specializations.get(i);
1022 for (int j = i + 1; j < specializations.size(); j++) {
1023 SpecializationData m2 = specializations.get(j);
1024 int inferredOrder = m1.compareBySignature(m2);
1025
1026 if (m1.getOrder() != Specialization.DEFAULT_ORDER && m2.getOrder() != Specialization.DEFAULT_ORDER) {
1027 int specOrder = m1.getOrder() - m2.getOrder();
1028 if (specOrder == 0) {
1029 m1.addError("Order value %d used multiple times", m1.getOrder());
1030 m2.addError("Order value %d used multiple times", m1.getOrder());
1031 return;
1032 } else if ((specOrder < 0 && inferredOrder > 0) || (specOrder > 0 && inferredOrder < 0)) {
1033 m1.addError("Explicit order values %d and %d are inconsistent with type lattice ordering.", m1.getOrder(), m2.getOrder());
1034 m2.addError("Explicit order values %d and %d are inconsistent with type lattice ordering.", m1.getOrder(), m2.getOrder());
1035 return;
1036 }
1037 } else if (inferredOrder == 0) {
1038 SpecializationData m = (m1.getOrder() == Specialization.DEFAULT_ORDER ? m1 : m2);
1039 m.addError("Cannot calculate a consistent order for this specialization. Define the order attribute to resolve this.");
1040 return;
1041 }
1042 }
1043 }
1044 }
1045
1046 private static void verifySpecializationThrows(NodeData node) {
1047 Map<String, SpecializationData> specializationMap = new HashMap<>();
1048 for (SpecializationData spec : node.getSpecializations()) {
1049 specializationMap.put(spec.getMethodName(), spec);
1050 }
1051 for (SpecializationData sourceSpecialization : node.getSpecializations()) {
1052 if (sourceSpecialization.getExceptions() != null) {
1053 for (SpecializationThrowsData throwsData : sourceSpecialization.getExceptions()) {
1054 for (SpecializationThrowsData otherThrowsData : sourceSpecialization.getExceptions()) {
1055 if (otherThrowsData != throwsData && Utils.typeEquals(otherThrowsData.getJavaClass(), throwsData.getJavaClass())) {
1056 throwsData.addError("Duplicate exception type.");
1057 }
1058 }
1059 }
1060 }
1061 }
1062 }
1063
1064 private static void verifyNamingConvention(List<? extends TemplateMethod> methods, String prefix) {
1065 for (int i = 0; i < methods.size(); i++) {
1066 TemplateMethod m1 = methods.get(i);
1067 if (m1.getMethodName().length() < 3 || !m1.getMethodName().startsWith(prefix)) {
1068 m1.addError("Naming convention: method name must start with '%s'.", prefix);
1069 }
1070 }
1071 }
1072
1073 private static Map<Integer, List<ExecutableTypeData>> groupExecutableTypes(List<ExecutableTypeData> executableTypes) {
1074 Map<Integer, List<ExecutableTypeData>> groupedTypes = new HashMap<>();
1075 for (ExecutableTypeData type : executableTypes) {
1076 int evaluatedCount = type.getEvaluatedCount();
1077
1078 List<ExecutableTypeData> types = groupedTypes.get(evaluatedCount);
1079 if (types == null) {
1080 types = new ArrayList<>();
1081 groupedTypes.put(evaluatedCount, types);
1082 }
1083 types.add(type);
1084 }
1085
1086 for (List<ExecutableTypeData> types : groupedTypes.values()) {
1087 Collections.sort(types);
1088 }
1089 return groupedTypes;
1090 }
1091
1092 private AnnotationMirror findFirstAnnotation(List<? extends Element> elements, Class<? extends Annotation> annotation) {
1093 for (Element element : elements) {
1094 AnnotationMirror mirror = Utils.findAnnotationMirror(processingEnv, element, annotation);
1095 if (mirror != null) {
1096 return mirror;
1097 }
1098 }
1099 return null;
1100 }
1101
1102 private TypeMirror inheritType(AnnotationMirror annotation, String valueName, TypeMirror parentType) {
1103 TypeMirror inhertNodeType = context.getTruffleTypes().getNode();
1104 TypeMirror value = Utils.getAnnotationValue(TypeMirror.class, annotation, valueName);
1105 if (Utils.typeEquals(inhertNodeType, value)) {
1106 return parentType;
1107 } else {
1108 return value;
1109 }
1110 }
1111
1112 private Element findGetter(List<? extends Element> elements, String variableName, TypeMirror type) {
1113 if (type == null) {
1114 return null;
1115 }
1116 String methodName;
1117 if (Utils.typeEquals(type, context.getType(boolean.class))) {
1118 methodName = "is" + Utils.firstLetterUpperCase(variableName);
1119 } else {
1120 methodName = "get" + Utils.firstLetterUpperCase(variableName);
1121 }
1122
1123 for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
1124 if (method.getSimpleName().toString().equals(methodName) && method.getParameters().size() == 0 && Utils.isAssignable(context, type, method.getReturnType())) {
1125 return method;
1126 }
1127 }
1128 return null;
1129 }
1130
1131 private boolean isGenericShortCutMethod(NodeData node, TemplateMethod method) {
1132 for (ActualParameter parameter : method.getParameters()) {
1133 NodeChildData field = node.findChild(parameter.getSpecification().getName());
1134 if (field == null) {
1135 continue;
1136 }
1137 ExecutableTypeData found = null;
1138 List<ExecutableTypeData> executableElements = field.findGenericExecutableTypes(context);
1139 for (ExecutableTypeData executable : executableElements) {
1140 if (executable.getType().equalsType(parameter.getTypeSystemType())) {
1141 found = executable;
1142 break;
1143 }
1144 }
1145 if (found == null) {
1146 return false;
1147 }
1148 }
1149 return true;
1150 }
1151
1152 private static Map<String, List<ShortCircuitData>> groupShortCircuits(List<ShortCircuitData> shortCircuits) {
1153 Map<String, List<ShortCircuitData>> group = new HashMap<>();
1154 for (ShortCircuitData shortCircuit : shortCircuits) {
1155 List<ShortCircuitData> circuits = group.get(shortCircuit.getValueName());
1156 if (circuits == null) {
1157 circuits = new ArrayList<>();
1158 group.put(shortCircuit.getValueName(), circuits);
1159 }
1160 circuits.add(shortCircuit);
1161 }
1162 return group;
1163 }
1164
1165 private static <M extends TemplateMethod> SortedMap<String, List<M>> groupByNodeId(List<M> methods) {
1166 SortedMap<String, List<M>> grouped = new TreeMap<>();
1167 for (M m : methods) {
1168 List<M> list = grouped.get(m.getId());
1169 if (list == null) {
1170 list = new ArrayList<>();
1171 grouped.put(m.getId(), list);
1172 }
1173 list.add(m);
1174 }
1175 return grouped;
1176 }
1177
1178 private static List<TypeElement> findSuperClasses(List<TypeElement> collection, TypeElement element) {
1179 if (element.getSuperclass() != null) {
1180 TypeElement superElement = Utils.fromTypeMirror(element.getSuperclass());
1181 if (superElement != null) {
1182 findSuperClasses(collection, superElement);
1183 }
1184 }
1185 collection.add(element);
1186 return collection;
1187 }
1188
1189 }