blob: 27ea6c9018c4b4edd13659e09ec08860a4efa70b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2018 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.qvtd.compiler.internal.qvtb2qvts;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.Parameter;
import org.eclipse.ocl.pivot.utilities.NameUtil;
import org.eclipse.qvtd.pivot.qvtbase.utilities.QVTbaseLibraryHelper;
import org.eclipse.qvtd.pivot.qvtschedule.Edge;
import org.eclipse.qvtd.pivot.qvtschedule.NavigationEdge;
import org.eclipse.qvtd.pivot.qvtschedule.Node;
import org.eclipse.qvtd.pivot.qvtschedule.Node.Utility;
import org.eclipse.qvtd.pivot.qvtschedule.OperationParameterEdge;
import org.eclipse.qvtd.pivot.qvtschedule.Region;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleUtil;
import org.eclipse.qvtd.runtime.utilities.QVTruntimeUtil;
import com.google.common.collect.Sets;
/**
* UtilityAnalysis is a helper that assigns a Utility value to each Region node.
*
* FIXME a QVTrelationUtilityAnalysis might be simpler than this QVTc chain version.
*/
public class UtilityAnalysis
{
public static void assignUtilities(@NonNull ScheduleManager scheduleManager, @NonNull Region region) {
new UtilityAnalysis(scheduleManager, region).assignUtilities();
}
protected final @NonNull ScheduleManager scheduleManager;
protected final @NonNull Region region;
protected final @NonNull QVTbaseLibraryHelper qvtbaseLibraryHelper;
private /*@LazyNonNull*/ @Nullable List<@NonNull Node> stronglyMatchedNodes = null;
private /*@LazyNonNull*/ @Nullable List<@NonNull Node> unconditionalNodes = null;
private /*@LazyNonNull*/ @Nullable List<@NonNull Node> conditionalNodes = null;
// private /*@LazyNonNull*/ @Nullable List<@NonNull Node> dependencyNodes = null;
private /*@LazyNonNull*/ @Nullable List<@NonNull Node> deadNodes = null;
protected UtilityAnalysis(@NonNull ScheduleManager scheduleManager, @NonNull Region region) {
this.scheduleManager = scheduleManager;
this.region = region;
this.qvtbaseLibraryHelper = scheduleManager.getQVTbaseLibraryHelper();
}
protected void assignUtilities() { // FIXME remove assertions after 1-Jan-2017
Iterable<@NonNull Node> headNodes = QVTscheduleUtil.getHeadNodes(region);
Node dispatchNode = computeDispatchNode();
Set<@NonNull Node> traceNodes = computeTraceNodes(headNodes);
Set<@NonNull Node> stronglyMatchedNodes = computeStronglyMatchedNodes(headNodes);
Set<@NonNull Node> unconditionalNodes = computeUnconditionalNodes(headNodes);
Set<@NonNull Node> conditionalNodes = computeConditionalNodes(unconditionalNodes);
// Set<@NonNull Node> dependencyNodes = computeDependencyNodes(headNodes);
Set<@NonNull Node> deadNodes = null;
//
for (@NonNull Node node : QVTscheduleUtil.getOwnedNodes(region)) {
Node.Utility oldUtility = node.basicGetUtility();
if (dispatchNode == node) {
node.setUtility(Node.Utility.DISPATCH);
}
else if (traceNodes.contains(node)) {
node.setUtility(Node.Utility.TRACE);
}
else if (stronglyMatchedNodes.contains(node)) {
node.setUtility(Node.Utility.STRONGLY_MATCHED);
//FIXME assert unconditionalNodes.contains(node);
if (!unconditionalNodes.contains(node)) {
QVTruntimeUtil.errPrintln(node + " is not unconditional in " + this);
}
// assert !dependencyNodes.contains(node);
}
else if (unconditionalNodes.contains(node) && !node.isDependency()) {
node.setUtility(Node.Utility.WEAKLY_MATCHED);
// assert !dependencyNodes.contains(node);
}
else if (conditionalNodes.contains(node)) {
node.setUtility(Node.Utility.CONDITIONAL);
// assert !dependencyNodes.contains(node);
}
else if (node.isDependency()) {
node.setUtility(Node.Utility.DEPENDENCY);
}
else if (node.isComposed()) {
node.setUtility(Node.Utility.COMPOSED);
}
else {
System.out.println("Dead node in " + this + " : " + node);
if (deadNodes == null) {
deadNodes = new HashSet<>();
}
deadNodes.add(node);
node.setUtility(Node.Utility.DEAD);
toString();
}
Node.Utility newUtility = node.getUtility();
if (oldUtility != null) {
assert oldUtility == newUtility;
}
}
if (deadNodes != null) {
this.deadNodes = new ArrayList<>(deadNodes);
Collections.sort(this.deadNodes, NameUtil.NAMEABLE_COMPARATOR);
}
/* for (@NonNull Node node : getNodes()) {
boolean isMatched = node.isMatched();
boolean isUnconditional = node.isUnconditional();
boolean isRequired = node.isRequired();
boolean isPattern = node.isPattern();
if (isMatched != (isUnconditional && isPattern)) {
System.out.println("Inconsistently isMatched in " + this + " : " + node);
}
} */
}
private boolean canBeStronglyMatched(@NonNull Node node) {
if (node.isPattern()) {
return true;
}
if (node.isConstant()) {
return true;
}
return false;
}
private boolean canBeUnconditional(@NonNull Node node) {
if (node.isIterator()) {
return false;
}
if (node.isOperation()) {
return true;
}
if (node.isPattern()) {
return true;
}
if (node.isSuccess()) {
return true;
}
return false;
}
/**
* Any node with an edge to an unconditional node that is not itself unconditional must be conditional.
*/
private @NonNull Set<@NonNull Node> computeConditionalNodes(@NonNull Set<@NonNull Node> unconditionalNodes) {
Set<@NonNull Node> conditionalNodes = new HashSet<>();
Set<@NonNull Node> moreNodes = unconditionalNodes;
while (moreNodes.size() > 0) {
Set<@NonNull Node> moreMoreNodes = new HashSet<>();
for (@NonNull Node node : moreNodes) {
for (@NonNull Edge incomingEdge : QVTscheduleUtil.getIncomingEdges(node)) {
Node sourceNode = incomingEdge.getEdgeSource();
if (!unconditionalNodes.contains(sourceNode) && conditionalNodes.add(sourceNode)) {
moreMoreNodes.add(sourceNode);
}
}
for (@NonNull Edge outgoingEdge : QVTscheduleUtil.getOutgoingEdges(node)) {
Node targetNode = outgoingEdge.getEdgeTarget();
if (!unconditionalNodes.contains(targetNode) && conditionalNodes.add(targetNode)) {
moreMoreNodes.add(targetNode);
}
}
}
if (moreMoreNodes.size() <= 0) {
break;
}
moreNodes = moreMoreNodes;
}
this.conditionalNodes = new ArrayList<>(conditionalNodes);
Collections.sort(this.conditionalNodes, NameUtil.NAMEABLE_COMPARATOR);
return conditionalNodes;
}
private @Nullable Node computeDispatchNode() {
Node dispatchNode = null;
for (@NonNull Node node : QVTscheduleUtil.getOwnedNodes(region)) {
if (node.isPredicated()) {
if (node.basicGetUtility() == Utility.DISPATCH) {
assert dispatchNode == null; // No double dispatch
dispatchNode = node;
}
}
}
return dispatchNode;
}
private @NonNull Set<@NonNull Node> computeStronglyMatchedNodes(@NonNull Iterable<@NonNull Node> headNodes) {
Set<@NonNull Node> stronglyMatchedNodes = new HashSet<>();
for (@NonNull Node headNode : headNodes) {
if (!headNode.isDependency()) {
stronglyMatchedNodes.add(headNode);
}
}
// for (@NonNull Node node : QVTscheduleUtil.getOwnedNodes(region)) {
// if (node.isConstant()) {
// stronglyMatchedNodes.add(node);
// }
// }
Set<@NonNull Node> moreStronglyMatchedNodes = new HashSet<>(stronglyMatchedNodes);
while (moreStronglyMatchedNodes.size() > 0) {
Set<@NonNull Node> moreMoreNodes = new HashSet<>();
for (@NonNull Node sourceNode : moreStronglyMatchedNodes) {
for (@NonNull Edge edge : QVTscheduleUtil.getOutgoingEdges(sourceNode)) {
if (edge.isNavigation()) {
NavigationEdge navigationEdge = (NavigationEdge)edge;
Node targetNode = edge.getEdgeTarget();
if (canBeStronglyMatched(targetNode)) {
if (targetNode.isNullLiteral() || QVTscheduleUtil.getReferredProperty(navigationEdge).isIsRequired()) {
if (stronglyMatchedNodes.add(targetNode)) {
moreMoreNodes.add(targetNode);
}
}
}
}
else {
// SharedEdge
}
}
}
if (moreMoreNodes.size() <= 0) {
break;
}
moreStronglyMatchedNodes = moreMoreNodes;
}
this.stronglyMatchedNodes = new ArrayList<>(stronglyMatchedNodes);
Collections.sort(this.stronglyMatchedNodes, NameUtil.NAMEABLE_COMPARATOR);
return stronglyMatchedNodes;
}
private Set<@NonNull Node> computeTraceNodes(@NonNull Iterable<@NonNull Node> headNodes) {
Set<@NonNull Node> traceNodes = new HashSet<>();
for (@NonNull Node node : QVTscheduleUtil.getOwnedNodes(region)) {
if (node.basicGetUtility() == Utility.TRACE) {
traceNodes.add(node);
}
}
if (traceNodes.isEmpty()) { // A QVTc / legacy usage - deduce trace node(s)
for (@NonNull Node node : QVTscheduleUtil.getOwnedNodes(region)) {
if (node.isRealized() && scheduleManager.isMiddle(node)) {
traceNodes.add(node);
}
}
}
return traceNodes;
}
private @NonNull Set<@NonNull Node> computeUnconditionalNodes(@NonNull Iterable<@NonNull Node> headNodes) {
@NonNull Set<@NonNull Node> unconditionalNodes = Sets.newHashSet(headNodes);
// Iterables.addAll(unconditionalNodes, region.getNewNodes());
for (@NonNull Node node : QVTscheduleUtil.getOwnedNodes(region)) {
if (node.isNew() || node.isConstant()) {
unconditionalNodes.add(node);
}
}
for (@NonNull Edge edge : QVTscheduleUtil.getOwnedEdges(region)) {
if (edge.isRealized() && edge.isNavigation() && !edge.isSecondary()) {
Node sourceNode = edge.getEdgeSource();
assert canBeUnconditional(sourceNode);
unconditionalNodes.add(sourceNode);
Node targetNode = edge.getEdgeTarget();
assert canBeUnconditional(targetNode);
unconditionalNodes.add(targetNode);
}
}
Set<@NonNull Node> moreUnconditionalNodes = new HashSet<>(unconditionalNodes);
while (moreUnconditionalNodes.size() > 0) {
Set<@NonNull Node> moreMoreNodes = new HashSet<>();
for (@NonNull Node node : moreUnconditionalNodes) {
for (@NonNull Edge incomingEdge : QVTscheduleUtil.getIncomingEdges(node)) {
assert !incomingEdge.isCast();
Node sourceNode = incomingEdge.getEdgeSource();
if (!canBeUnconditional(sourceNode)) {}
else if (incomingEdge.isComputation()) {
if (!isConditionalEdge(incomingEdge)) {
if (unconditionalNodes.add(sourceNode)) {
moreMoreNodes.add(sourceNode);
}
}
// if is <<then>>
// gather <<then>> visibilities
// gather <<else>> visibilities
// intersection <<then>>/<<else>> is unconditional
}
else if (incomingEdge.isNavigation()) { // Unconditional target has unconditional source
if (unconditionalNodes.add(sourceNode)) {
moreMoreNodes.add(sourceNode);
}
}
else if (incomingEdge.isShared()) {}
else {
System.out.println("Unsupported incoming edge in " + this + " : " + incomingEdge);
}
}
for (@NonNull Edge outgoingEdge : QVTscheduleUtil.getOutgoingEdges(node)) {
assert !outgoingEdge.isCast();
Node targetNode = outgoingEdge.getEdgeTarget();
if (!canBeUnconditional(targetNode)) {}
else if (outgoingEdge.isComputation()) {}
else if (outgoingEdge.isDependency()) {}
else if (outgoingEdge.isNavigation()) {
if (targetNode.isNullLiteral()) {
if (unconditionalNodes.add(targetNode)) {
moreMoreNodes.add(targetNode);
}
}
else if (node.isRequired() && QVTscheduleUtil.getReferredProperty((NavigationEdge)outgoingEdge).isIsRequired()) {
if (unconditionalNodes.add(targetNode)) {
moreMoreNodes.add(targetNode);
}
}
}
else if (outgoingEdge.isShared()) {
if (unconditionalNodes.add(targetNode)) {
moreMoreNodes.add(targetNode);
}
}
else {
System.out.println("Unsupported outgoing edge in " + this + " : " + outgoingEdge);
}
}
}
if (moreMoreNodes.size() <= 0) {
break;
}
moreUnconditionalNodes = moreMoreNodes;
}
this.unconditionalNodes = new ArrayList<>(unconditionalNodes);
Collections.sort(this.unconditionalNodes, NameUtil.NAMEABLE_COMPARATOR);
return unconditionalNodes;
}
private boolean isConditionalEdge(@NonNull Edge edge) {
if (edge instanceof OperationParameterEdge) {
OperationParameterEdge operationParameterEdge = (OperationParameterEdge)edge;
Parameter parameter = operationParameterEdge.getReferredParameter();
if (parameter == qvtbaseLibraryHelper.getIfThenParameter()) {
return true;
}
else if (parameter == qvtbaseLibraryHelper.getIfElseParameter()) {
return true;
}
else if (parameter == qvtbaseLibraryHelper.getLoopBodyParameter()) {
return true;
}
}
return false;
}
@Override
public String toString() {
return region.toString();
}
}