use of com.oracle.truffle.dsl.processor.java.ElementUtils.elementEquals in project graal by oracle.
the class ExportsParser method parse.
@Override
protected ExportsData parse(Element element, List<AnnotationMirror> elementMirrors) {
TypeElement type = (TypeElement) element;
ExportsData model = parseExports(type, elementMirrors);
if (model.hasErrors()) {
return model;
}
// set of types that contribute members
Set<TypeElement> declaringTypes = new HashSet<>();
Set<TypeElement> declaringInTemplateTypes = new HashSet<>();
declaringInTemplateTypes.add(type);
for (ExportsLibrary library : model.getExportedLibraries().values()) {
declaringTypes.addAll(library.getDeclaringTypes());
if (library.isDeclaredInTemplate()) {
declaringInTemplateTypes.addAll(library.getDeclaringTypes());
}
}
Element packageElement = ElementUtils.findPackageElement(type);
for (TypeElement currentType : declaringInTemplateTypes) {
if (ElementUtils.elementEquals(currentType, element)) {
// current type always visible
continue;
}
List<Element> foundInvisibleMembers = new ArrayList<>();
List<? extends Element> superTypeMembers = loadMembers(null, currentType);
for (Element superTypeMember : superTypeMembers) {
List<AnnotationMirror> exportedMessages = getRepeatedAnnotation(superTypeMember.getAnnotationMirrors(), types.ExportMessage);
if (!exportedMessages.isEmpty()) {
if (!ElementUtils.isVisible(packageElement, superTypeMember)) {
foundInvisibleMembers.add(superTypeMember);
} else if (superTypeMember.getKind().isClass()) {
for (Element specializationMember : loadMembers(null, (TypeElement) superTypeMember)) {
if (ElementUtils.findAnnotationMirror(specializationMember, types.Specialization) != null && !ElementUtils.isVisible(packageElement, specializationMember)) {
foundInvisibleMembers.add(specializationMember);
}
}
}
}
}
if (!foundInvisibleMembers.isEmpty()) {
StringBuilder b = new StringBuilder();
for (Element invisibleMember : foundInvisibleMembers) {
b.append(System.lineSeparator()).append(" - ");
b.append(ElementUtils.getReadableReference(element, invisibleMember));
}
model.addError("Found invisible exported elements in super type '%s': %s%nIncrease their visibility to resolve this problem.", ElementUtils.getSimpleName(currentType), b.toString());
}
}
if (model.getExportedLibraries().isEmpty()) {
for (Element member : loadMembers(null, type)) {
List<AnnotationMirror> exportedMessageMirrors = getRepeatedAnnotation(member.getAnnotationMirrors(), types.ExportMessage);
if (!exportedMessageMirrors.isEmpty()) {
model.addError(//
"Class declares @%s annotations but does not export any libraries. " + //
"Exported messages cannot be resoved without exported library. " + "Add @%s(MyLibrary.class) to the class ot resolve this.", getSimpleName(types.ExportMessage), getSimpleName(types.ExportLibrary));
return model;
}
}
}
List<? extends Element> members = loadMembers(declaringInTemplateTypes, type);
/*
* First pass: element creation
*/
Map<String, List<Element>> potentiallyMissedOverrides = new LinkedHashMap<>();
List<ExportMessageData> exportedElements = new ArrayList<>();
for (Element member : members) {
List<AnnotationMirror> exportedMessageMirrors = getRepeatedAnnotation(member.getAnnotationMirrors(), types.ExportMessage);
if (exportedMessageMirrors.isEmpty()) {
boolean isMethod = isMethodElement(member);
boolean isNode = isNodeElement(member);
String name = null;
if (isMethod) {
name = member.getSimpleName().toString();
} else if (isNode) {
name = inferNodeMessageName((TypeElement) member);
}
if (isMethod || isNode) {
Element enclosingType = member.getEnclosingElement();
if (elementEquals(model.getTemplateType(), enclosingType)) {
potentiallyMissedOverrides.computeIfAbsent(name, (n) -> new ArrayList<>()).add(member);
}
}
} else {
for (AnnotationMirror exportMessage : exportedMessageMirrors) {
exportedElements.addAll(parseExportedMessage(model, member, exportMessage));
}
}
}
for (ExportMessageData exportedMessage : exportedElements) {
Element member = exportedMessage.getMessageElement();
String messageName = exportedMessage.getResolvedMessage().getName();
Map<String, ExportMessageData> exportedMessages = exportedMessage.getExportsLibrary().getExportedMessages();
ExportMessageData existing = exportedMessages.get(messageName);
if (existing != null) {
Element existingEnclosingElement = existing.getMessageElement().getEnclosingElement();
Element currentEnclosingElement = exportedMessage.getMessageElement().getEnclosingElement();
if (ElementUtils.elementEquals(existingEnclosingElement, currentEnclosingElement)) {
String error = String.format("Duplicate exported library message %s.", messageName);
model.addError(member, error);
model.addError(existing.getMessageElement(), error);
} else if (ElementUtils.isSubtype(currentEnclosingElement.asType(), existingEnclosingElement.asType())) {
// new message is more concrete
exportedMessages.put(messageName, exportedMessage);
existing.setOverriden(true);
} else {
// keep existing exported message
exportedMessage.setOverriden(true);
}
} else {
exportedMessages.put(messageName, exportedMessage);
}
}
/*
* Generate synthetic exports for export delegation.
*/
for (ExportsLibrary exportsLibrary : model.getExportedLibraries().values()) {
if (!exportsLibrary.hasExportDelegation()) {
continue;
}
ExportMessageData accepts = exportsLibrary.getExportedMessages().get("accepts");
if (accepts == null) {
String delegateName = exportsLibrary.getDelegationVariable().getSimpleName().toString();
CodeAnnotationMirror annotation = new CodeAnnotationMirror(types.CachedLibrary);
annotation.setElementValue(ElementUtils.findExecutableElement(types.CachedLibrary, "value"), new CodeAnnotationValue("receiver_." + delegateName));
CodeExecutableElement executable = CodeExecutableElement.clone(ElementUtils.findMethod(types.Library, "accepts"));
executable.changeTypes(exportsLibrary.getReceiverType());
executable.renameArguments("receiver_");
executable.getModifiers().add(Modifier.STATIC);
CodeVariableElement var = new CodeVariableElement(exportsLibrary.getLibrary().getTemplateType().asType(), delegateName);
var.addAnnotationMirror(annotation);
executable.setEnclosingElement(exportsLibrary.getTemplateType());
executable.getParameters().add(var);
LibraryMessage message = null;
for (LibraryMessage libMessage : exportsLibrary.getLibrary().getMethods()) {
if (libMessage.getName().equals("accepts")) {
message = libMessage;
break;
}
}
accepts = new ExportMessageData(exportsLibrary, message, executable, annotation);
exportsLibrary.getExportedMessages().put("accepts", accepts);
exportedElements.add(accepts);
} else {
accepts.addError("Exporting a custom accepts method is currently not supported when export delegation is used in @%s. " + "Remove delegateTo from all exports or remove the accepts export to resolve this.", getSimpleName(types.ExportLibrary));
}
}
for (ExportsLibrary exportsLibrary : model.getExportedLibraries().values()) {
if (!exportsLibrary.isDeclaredInTemplate()) {
for (ExportMessageData message : exportsLibrary.getExportedMessages().values()) {
if (elementEquals(message.getMessageElement().getEnclosingElement(), type)) {
message.addError(//
"The @%s declaration is missing for this exported message. " + "Add @%s(%s.class) to the enclosing class %s to resolve this.", getSimpleName(types.ExportLibrary), getSimpleName(types.ExportLibrary), getSimpleName(exportsLibrary.getLibrary().getTemplateType()), getSimpleName(type));
}
}
}
}
// avoid removal of elements if errors occured.
if (model.hasErrors()) {
return model;
}
for (ExportsLibrary exportsLibrary : model.getExportedLibraries().values()) {
if (exportsLibrary.isBuiltinDefaultExport()) {
// we don't print unused warnings for builtin defaults.
continue;
}
if (exportsLibrary.isDeclaredInTemplate()) {
boolean foundDeclared = false;
for (ExportMessageData message : exportsLibrary.getExportedMessages().values()) {
if (message.isDeclared()) {
foundDeclared = true;
break;
}
}
if (!foundDeclared) {
exportsLibrary.addWarning("Exported library %s does not export any messages and therefore has no effect. Remove the export declaration to resolve this.", getSimpleName(exportsLibrary.getLibrary().getTemplateType()));
}
}
}
// avoid removal of elements if errors occured.
if (model.hasErrors()) {
return model;
}
/*
* filter elements that come from exports not relevant for this class. Remove all export
* declarations not relevant for this type.
*/
Predicate<? super ExportsLibrary> filterpredicate = (library -> {
if (library.isDynamicDispatchTarget()) {
// Implicitly export super's library if dynamic dispatched.
return true;
}
return library.isDeclaredInTemplate();
});
Set<ExportsLibrary> declaredExports = model.getExportedLibraries().values().stream().filter(filterpredicate).collect(Collectors.toSet());
model.getExportedLibraries().values().removeIf((e) -> !declaredExports.contains(e));
exportedElements = exportedElements.stream().filter((e) -> declaredExports.contains(e.getExportsLibrary())).collect(Collectors.toList());
/*
* Third pass: initialize and further parsing that need both method and node to be
* available.
*/
for (ExportsLibrary exportsLibrary : declaredExports) {
// recreate cache for every exports library to not confuse exports configuration
Map<String, NodeData> parsedNodeCache = new HashMap<>();
for (ExportMessageData exportedElement : exportsLibrary.getExportedMessages().values()) {
if (exportedElement.isOverriden()) {
// gets confused.
continue;
}
Element member = exportedElement.getMessageElement();
if (isMethodElement(member)) {
initializeExportedMethod(parsedNodeCache, model, exportedElement);
} else if (isNodeElement(member)) {
initializeExportedNode(parsedNodeCache, exportedElement);
} else {
throw new AssertionError("should not be reachable");
}
}
}
TypeMirror receiverClass = null;
for (Entry<String, ExportsLibrary> entry : model.getExportedLibraries().entrySet()) {
ExportsLibrary exportLib = entry.getValue();
if (exportLib.hasErrors()) {
continue;
}
if (receiverClass == null) {
receiverClass = exportLib.getReceiverType();
} else if (!typeEquals(exportLib.getReceiverType(), receiverClass)) {
exportLib.addError("All receiver classes must match for a declared java type. Found '%s' and '%s'.", getSimpleName(receiverClass), getSimpleName(exportLib.getReceiverType()));
continue;
}
Set<LibraryMessage> missingAbstractMessage = new LinkedHashSet<>();
for (LibraryMessage message : exportLib.getLibrary().getMethods()) {
List<Element> elementsWithSameName = potentiallyMissedOverrides.getOrDefault(message.getName(), Collections.emptyList());
if (!elementsWithSameName.isEmpty()) {
for (Element overridingElement : elementsWithSameName) {
if (ElementUtils.findAnnotationMirror(overridingElement, types.ExportMessage_Ignore) == null) {
exportLib.addError(overridingElement, "The method has the same name '%s' as a message in the exported library %s. Did you forget to export it? " + "Use @%s to export the message, @%s to ignore this warning, rename the method or reduce the visibility of the method to private to resolve this warning.", overridingElement.getSimpleName().toString(), getSimpleName(exportLib.getLibrary().getTemplateType()), types.ExportMessage.asElement().getSimpleName().toString(), types.ExportMessage_Ignore.asElement().getSimpleName().toString());
}
}
}
if (message.isAbstract() && !message.getName().equals("accepts")) {
ExportMessageData exportMessage = exportLib.getExportedMessages().get(message.getName());
if (exportMessage == null || exportMessage.getResolvedMessage() != message) {
boolean isAbstract;
if (!message.getAbstractIfExported().isEmpty()) {
isAbstract = false;
for (LibraryMessage abstractIfExported : message.getAbstractIfExported()) {
if (exportLib.getExportedMessages().containsKey(abstractIfExported.getName())) {
isAbstract = true;
break;
}
}
} else {
isAbstract = !exportLib.hasExportDelegation();
}
if (isAbstract) {
missingAbstractMessage.add(message);
}
}
}
}
if (!missingAbstractMessage.isEmpty()) {
StringBuilder msg = new StringBuilder(String.format("The following message(s) of library %s are abstract and must be exported using:%n", getSimpleName(exportLib.getLibrary().getTemplateType())));
for (LibraryMessage message : missingAbstractMessage) {
msg.append(" ").append(generateExpectedSignature(type, message, exportLib.getExplicitReceiver())).append(" {");
if (!ElementUtils.isVoid(message.getExecutable().getReturnType())) {
msg.append(" return ").append(ElementUtils.defaultValue(message.getExecutable().getReturnType()));
msg.append(";");
}
msg.append(" }%n");
}
exportLib.addError(msg.toString());
}
}
for (ExportsLibrary libraryExports : model.getExportedLibraries().values()) {
List<NodeData> cachedSharedNodes = new ArrayList<>();
List<ExportMessageData> exportedMessages = new ArrayList<>();
for (ExportMessageData export : libraryExports.getExportedMessages().values()) {
if (export.getSpecializedNode() != null) {
cachedSharedNodes.add(export.getSpecializedNode());
exportedMessages.add(export);
}
}
libraryExports.setSharedExpressions(NodeParser.computeSharing(libraryExports.getTemplateType(), cachedSharedNodes, true));
// JDT will otherwise just ignore those messages and not display anything.
for (int i = 0; i < cachedSharedNodes.size(); i++) {
NodeData nodeData = cachedSharedNodes.get(i);
ExportMessageData exportedMessage = exportedMessages.get(i);
if (nodeData.hasErrorsOrWarnings()) {
nodeData.redirectMessagesOnGeneratedElements(exportedMessage);
}
nodeData.setGenerateUncached(false);
}
}
for (ExportMessageData message : exportedElements) {
if (!elementEquals(message.getMessageElement().getEnclosingElement(), model.getTemplateType())) {
message.redirectMessages(message.getExportsLibrary());
}
}
if (isGenerateSlowPathOnly(type)) {
for (ExportsLibrary libraryExports : model.getExportedLibraries().values()) {
for (ExportMessageData export : libraryExports.getExportedMessages().values()) {
if (export.isClass() && export.getSpecializedNode() != null) {
NodeParser.removeFastPathSpecializations(export.getSpecializedNode());
}
}
}
}
return model;
}
Aggregations