package com.oracle.truffle.dsl.processor.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import com.oracle.truffle.dsl.processor.ProcessorContext;
import com.oracle.truffle.dsl.processor.java.ElementUtils;
import com.oracle.truffle.dsl.processor.model.NodeChildData.Cardinality;
public class NodeData extends Template implements Comparable<NodeData> {
private final String nodeId;
private final List<NodeData> enclosingNodes = new ArrayList<>();
private NodeData declaringNode;
private final TypeSystemData typeSystem;
private final List<NodeChildData> children;
private final List<NodeExecutionData> childExecutions;
private final List<NodeFieldData> fields;
private ParameterSpec instanceParameterSpec;
private final List<SpecializationData> specializations = new ArrayList<>();
private final List<CreateCastData> casts = new ArrayList<>();
private final List<ExecutableTypeData> executableTypes = new ArrayList<>();
private final NodeExecutionData thisExecution;
private final boolean generateFactory;
private TypeMirror frameType;
private boolean generateIntrospection;
private boolean generateStatistics;
private boolean reportPolymorphism;
private boolean isUncachable;
private boolean isNodeBound;
private boolean generateUncached;
private Set<String> allowedCheckedExceptions;
private Map<CacheExpression, String> sharedCaches = Collections.emptyMap();
public NodeData(ProcessorContext context, TypeElement type, TypeSystemData typeSystem, boolean generateFactory, boolean generateUncached) {
super(context, type, null);
this.nodeId = ElementUtils.getSimpleName(type);
this.typeSystem = typeSystem;
this.fields = new ArrayList<>();
this.children = new ArrayList<>();
this.childExecutions = new ArrayList<>();
this.thisExecution = new NodeExecutionData(new NodeChildData(null, null, "this", getNodeType(), getNodeType(), null, Cardinality.ONE, null), -1, -1);
this.thisExecution.getChild().setNode(this);
this.generateFactory = generateFactory;
this.generateUncached = generateUncached;
}
public void setGenerateStatistics(boolean generateStatistics) {
this.generateStatistics = generateStatistics;
}
public boolean isGenerateStatistics() {
return generateStatistics;
}
public Map<CacheExpression, String> getSharedCaches() {
return sharedCaches;
}
public void setSharedCaches(Map<CacheExpression, String> sharedCaches) {
this.sharedCaches = sharedCaches;
}
public NodeData(ProcessorContext context, TypeElement type) {
this(context, type, null, false, false);
}
public void setNodeBound(boolean isNodeBound) {
this.isNodeBound = isNodeBound;
}
public boolean isNodeBound() {
return isNodeBound;
}
public void setUncachable(boolean uncached) {
this.isUncachable = uncached;
}
public void setGenerateUncached(boolean generateUncached) {
this.generateUncached = generateUncached;
}
public boolean isGenerateUncached() {
return generateUncached;
}
public boolean isUncachable() {
return isUncachable;
}
public boolean isGenerateFactory() {
return generateFactory;
}
public NodeExecutionData getThisExecution() {
return thisExecution;
}
public boolean isGenerateIntrospection() {
return generateIntrospection;
}
public void setGenerateIntrospection(boolean reflectable) {
this.generateIntrospection = reflectable;
}
public boolean isFallbackReachable() {
SpecializationData generic = getGenericSpecialization();
if (generic != null) {
return generic.isReachable();
}
return false;
}
public void setFrameType(TypeMirror frameType) {
this.frameType = frameType;
}
public TypeMirror getFrameType() {
return frameType;
}
public void addEnclosedNode(NodeData node) {
this.enclosingNodes.add(node);
node.declaringNode = this;
}
public List<NodeExecutionData> getChildExecutions() {
return childExecutions;
}
public Set<TypeMirror> findSpecializedTypes(NodeExecutionData execution) {
Set<TypeMirror> foundTypes = new HashSet<>();
for (SpecializationData specialization : getSpecializations()) {
if (!specialization.isSpecialized()) {
continue;
}
List<Parameter> parameters = specialization.findByExecutionData(execution);
for (Parameter parameter : parameters) {
TypeMirror type = parameter.getType();
if (type == null) {
throw new AssertionError();
}
foundTypes.add(type);
}
}
return foundTypes;
}
public Collection<TypeMirror> findSpecializedReturnTypes() {
Set<TypeMirror> foundTypes = new HashSet<>();
for (SpecializationData specialization : getSpecializations()) {
if (!specialization.isSpecialized()) {
continue;
}
foundTypes.add(specialization.getReturnType().getType());
}
return foundTypes;
}
public int getExecutionCount() {
return getChildExecutions().size();
}
public int getSignatureSize() {
return getChildExecutions().size();
}
public boolean isFrameUsedByAnyGuard() {
for (SpecializationData specialization : specializations) {
if (!specialization.isReachable()) {
continue;
}
if (specialization.isFrameUsedByGuard()) {
return true;
}
}
return false;
}
public List<CreateCastData> getCasts() {
return casts;
}
public List<NodeFieldData> getFields() {
return fields;
}
@Override
protected List<MessageContainer> findChildContainers() {
List<MessageContainer> containerChildren = new ArrayList<>();
if (enclosingNodes != null) {
containerChildren.addAll(enclosingNodes);
}
if (typeSystem != null) {
containerChildren.add(typeSystem);
}
if (specializations != null) {
for (MessageContainer specialization : specializations) {
if (specialization.getMessageElement() != null) {
containerChildren.add(specialization);
}
}
}
if (executableTypes != null) {
containerChildren.addAll(getExecutableTypes());
}
if (children != null) {
containerChildren.addAll(children);
}
if (fields != null) {
containerChildren.addAll(fields);
}
if (casts != null) {
containerChildren.addAll(casts);
}
return containerChildren;
}
public ParameterSpec getInstanceParameterSpec() {
return instanceParameterSpec;
}
public void setInstanceParameterSpec(ParameterSpec instanceParameter) {
this.instanceParameterSpec = instanceParameter;
}
public String getNodeId() {
return nodeId;
}
public TypeMirror getNodeType() {
return getTemplateType().asType();
}
public boolean needsFactory() {
if (specializations == null) {
return false;
}
if (getTemplateType().getModifiers().contains(Modifier.PRIVATE)) {
return false;
}
boolean noSpecialization = true;
for (SpecializationData specialization : specializations) {
noSpecialization = noSpecialization && !specialization.isSpecialized();
}
return !noSpecialization;
}
public boolean supportsFrame() {
if (executableTypes != null) {
for (ExecutableTypeData execType : getExecutableTypes(-1)) {
if (execType.getFrameParameter() == null) {
return false;
}
}
}
return true;
}
public NodeExecutionData findExecutionByExpression(String childNameExpression) {
String childName = childNameExpression;
int index = -1;
int start = childName.indexOf('[');
int end = childName.lastIndexOf(']');
if (start != -1 && end != -1 && start < end) {
try {
index = Integer.parseInt(childName.substring(start + 1, end));
childName = childName.substring(0, start);
childName = NodeExecutionData.createName(childName, index);
} catch (NumberFormatException e) {
}
}
for (NodeExecutionData execution : childExecutions) {
if (execution.getName().equals(childName) && (execution.getChildArrayIndex() == -1 || execution.getChildArrayIndex() == index)) {
return execution;
}
}
return null;
}
public List<NodeData> getNodesWithFactories() {
List<NodeData> nodeChildren = new ArrayList<>();
for (NodeData child : getEnclosingNodes()) {
if (child.needsFactory() && child.isGenerateFactory()) {
nodeChildren.add(child);
}
nodeChildren.addAll(child.getNodesWithFactories());
}
return nodeChildren;
}
public NodeData getDeclaringNode() {
return declaringNode;
}
public List<NodeData> getEnclosingNodes() {
return enclosingNodes;
}
public List<ExecutableElement> getAllTemplateMethods() {
List<ExecutableElement> methods = new ArrayList<>();
for (SpecializationData specialization : getSpecializations()) {
methods.add(specialization.getMethod());
}
for (ExecutableTypeData execType : getExecutableTypes()) {
if (execType.getMethod() != null) {
methods.add(execType.getMethod());
}
}
if (getCasts() != null) {
for (CreateCastData castData : getCasts()) {
methods.add(castData.getMethod());
}
}
return methods;
}
public ExecutableTypeData findAnyGenericExecutableType(ProcessorContext context, int evaluatedCount) {
List<ExecutableTypeData> foundTypes = findGenericExecutableTypes(evaluatedCount);
for (ExecutableTypeData type : foundTypes) {
if (context.isType(type.getReturnType(), Object.class)) {
return type;
}
}
for (ExecutableTypeData type : foundTypes) {
if (!context.isType(type.getReturnType(), void.class)) {
return type;
}
}
for (ExecutableTypeData type : foundTypes) {
return type;
}
return null;
}
public List<ExecutableTypeData> getExecutableTypes(int evaluatedCount) {
if (evaluatedCount == -1) {
return executableTypes;
} else {
List<ExecutableTypeData> filteredTypes = new ArrayList<>();
for (ExecutableTypeData type : executableTypes) {
if (type.getEvaluatedCount() == evaluatedCount) {
filteredTypes.add(type);
}
}
return filteredTypes;
}
}
public List<ExecutableTypeData> findGenericExecutableTypes(int evaluatedCount) {
List<ExecutableTypeData> foundTypes = new ArrayList<>();
for (ExecutableTypeData type : getExecutableTypes(evaluatedCount)) {
if (!type.hasUnexpectedValue()) {
foundTypes.add(type);
}
}
return foundTypes;
}
public ExecutableTypeData findExecutableType(TypeMirror primitiveType, int evaluatedCount) {
for (ExecutableTypeData type : getExecutableTypes(evaluatedCount)) {
if (ElementUtils.typeEquals(type.getReturnType(), primitiveType)) {
return type;
}
}
return null;
}
public boolean needsRewrites(ProcessorContext context) {
int count = 0;
for (SpecializationData specialization : getSpecializations()) {
if (specialization.getMethod() == null) {
continue;
}
if (count == 1) {
return true;
}
if (specialization.needsRewrite(context)) {
return true;
}
count++;
}
return false;
}
public SpecializationData getPolymorphicSpecialization() {
for (SpecializationData specialization : specializations) {
if (specialization.isPolymorphic()) {
return specialization;
}
}
return null;
}
public SpecializationData getGenericSpecialization() {
for (SpecializationData specialization : specializations) {
if (specialization.isFallback()) {
return specialization;
}
}
return null;
}
public SpecializationData getUninitializedSpecialization() {
for (SpecializationData specialization : specializations) {
if (specialization.isUninitialized()) {
return specialization;
}
}
return null;
}
public TypeSystemData getTypeSystem() {
return typeSystem;
}
@Override
public String dump() {
return dump(0);
}
private String dump(int level) {
String indent = "";
for (int i = 0; i < level; i++) {
indent += " ";
}
StringBuilder builder = new StringBuilder();
builder.append(String.format("%s%s {", indent, toString()));
dumpProperty(builder, indent, "templateClass", ElementUtils.getQualifiedName(getTemplateType()));
dumpProperty(builder, indent, "typeSystem", getTypeSystem());
dumpProperty(builder, indent, "fields", getChildren());
dumpProperty(builder, indent, "executableTypes", getExecutableTypes());
dumpProperty(builder, indent, "specializations", getSpecializations());
dumpProperty(builder, indent, "casts", getCasts());
dumpProperty(builder, indent, "messages", collectMessages());
if (getEnclosingNodes().size() > 0) {
builder.append(String.format("%n%s children = [", indent));
for (NodeData node : getEnclosingNodes()) {
builder.append("%n");
builder.append(node.dump(level + 1));
}
builder.append(String.format("%n%s ]", indent));
}
builder.append(String.format("%s}", indent));
return builder.toString();
}
private static void dumpProperty(StringBuilder b, String indent, String propertyName, Object value) {
if (value instanceof List) {
List<?> list = (List<?>) value;
if (!list.isEmpty()) {
b.append(String.format("%n%s %s = %s", indent, propertyName, dumpList(indent, (List<?>) value)));
}
} else {
if (value != null) {
b.append(String.format("%n%s %s = %s", indent, propertyName, value));
}
}
}
private static String dumpList(String indent, List<?> array) {
if (array == null) {
return "null";
}
if (array.isEmpty()) {
return "[]";
} else if (array.size() == 1) {
return "[" + array.get(0).toString() + "]";
}
StringBuilder b = new StringBuilder();
b.append("[");
for (Object object : array) {
b.append("%n ");
b.append(indent);
b.append(object);
b.append(", ");
}
b.append("%n ").append(indent).append("]");
return b.toString();
}
public NodeChildData findChild(String name) {
for (NodeChildData field : getChildren()) {
if (field.getName().equals(name)) {
return field;
}
}
return null;
}
public List<NodeChildData> getChildren() {
return children;
}
public Collection<SpecializationData> computeUncachedSpecializations(List<SpecializationData> s) {
Set<SpecializationData> uncached = new LinkedHashSet<>(s);
for (SpecializationData specialization : s) {
uncached.removeAll(specialization.getReplaces());
}
return uncached;
}
public List<SpecializationData> getSpecializations() {
return specializations;
}
public ExecutableTypeData getGenericExecutableType(ExecutableTypeData typeHint) {
ExecutableTypeData polymorphicDelegate = null;
if (typeHint != null) {
polymorphicDelegate = typeHint;
while (polymorphicDelegate.getDelegatedTo() != null && polymorphicDelegate.getEvaluatedCount() != getSignatureSize()) {
polymorphicDelegate = polymorphicDelegate.getDelegatedTo();
}
}
if (polymorphicDelegate == null) {
for (ExecutableTypeData type : getExecutableTypes()) {
if (type.getDelegatedTo() == null && type.getEvaluatedCount() == getSignatureSize()) {
polymorphicDelegate = type;
break;
}
}
}
return polymorphicDelegate;
}
public List<ExecutableTypeData> getExecutableTypes() {
return getExecutableTypes(-1);
}
public int getMinimalEvaluatedParameters() {
int minimalEvaluatedParameters = Integer.MAX_VALUE;
for (ExecutableTypeData type : getExecutableTypes()) {
minimalEvaluatedParameters = Math.min(minimalEvaluatedParameters, type.getEvaluatedCount());
}
return minimalEvaluatedParameters;
}
@Override
public String toString() {
return getClass().getSimpleName() + "[" + getNodeId() + "]";
}
public CreateCastData findCast(String name) {
if (getCasts() != null) {
for (CreateCastData cast : getCasts()) {
if (cast.getChildNames().contains(name)) {
return cast;
}
}
}
return null;
}
public int compareTo(NodeData o) {
return getNodeId().compareTo(o.getNodeId());
}
public TypeMirror getGenericType(NodeExecutionData execution) {
return ElementUtils.getCommonSuperType(getContext(), getGenericTypes(execution));
}
public List<TypeMirror> getGenericTypes(NodeExecutionData execution) {
List<TypeMirror> foundTypes = new ArrayList<>();
if (execution.getChild() != null) {
for (ExecutableTypeData executable : execution.getChild().getNodeData().getExecutableTypes()) {
if (executable.hasUnexpectedValue()) {
continue;
}
foundTypes.add(executable.getReturnType());
}
}
int executionIndex = execution.getIndex();
if (executionIndex >= 0) {
for (ExecutableTypeData typeData : getExecutableTypes()) {
List<TypeMirror> signatureParameters = typeData.getSignatureParameters();
if (executionIndex < signatureParameters.size()) {
TypeMirror genericType = signatureParameters.get(executionIndex);
foundTypes.add(genericType);
}
}
}
return Arrays.asList(ElementUtils.getCommonSuperType(ProcessorContext.getInstance(), foundTypes));
}
public void setReportPolymorphism(boolean report) {
this.reportPolymorphism = report;
}
public boolean isReportPolymorphism() {
return reportPolymorphism;
}
public void setAllowedCheckedExceptions(Set<String> checkedExceptions) {
this.allowedCheckedExceptions = checkedExceptions;
}
public Set<String> getAllowedCheckedExceptions() {
return allowedCheckedExceptions;
}
private final Set<TypeMirror> libraryTypes = new LinkedHashSet<>();
public Set<TypeMirror> getLibraryTypes() {
return libraryTypes;
}
}