[514590] Revamp pattern match to prioritize checks
diff --git a/plugins/org.eclipse.qvtd.compiler/META-INF/MANIFEST.MF b/plugins/org.eclipse.qvtd.compiler/META-INF/MANIFEST.MF
index 397f303..3f3ed1f 100644
--- a/plugins/org.eclipse.qvtd.compiler/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.qvtd.compiler/META-INF/MANIFEST.MF
@@ -32,6 +32,7 @@
org.eclipse.qvtd.compiler.internal.qvts2qvts.merger,
org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner,
org.eclipse.qvtd.compiler.internal.qvts2qvts.splitter,
+ org.eclipse.qvtd.compiler.internal.qvts2qvts.utilities,
org.eclipse.qvtd.compiler.internal.qvtu2qvtm,
org.eclipse.qvtd.compiler.internal.templates.model,
org.eclipse.qvtd.compiler.internal.utilities
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/AbstractRegion2Mapping.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/AbstractRegion2Mapping.java
index 9773f2b..ed7ac3e 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/AbstractRegion2Mapping.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/AbstractRegion2Mapping.java
@@ -50,6 +50,7 @@
import org.eclipse.qvtd.pivot.qvtimperative.MappingParameter;
import org.eclipse.qvtd.pivot.qvtimperative.MappingParameterBinding;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.QVTimperativeHelper;
+import org.eclipse.qvtd.pivot.qvtschedule.BasicMappingRegion;
import org.eclipse.qvtd.pivot.qvtschedule.Node;
import org.eclipse.qvtd.pivot.qvtschedule.NodeConnection;
import org.eclipse.qvtd.pivot.qvtschedule.Region;
@@ -60,7 +61,9 @@
protected final @NonNull QVTs2QVTiVisitor visitor;
protected final @NonNull QVTimperativeHelper helper;
protected final @NonNull Region region;
+ @SuppressWarnings("unused")private final @NonNull String regionName;
protected final @NonNull Mapping mapping;
+ @SuppressWarnings("unused")private final @NonNull String mappingName;
/**
* Mapping from QVTm expression to Schedule Node.
@@ -86,9 +89,14 @@
this.visitor = visitor;
this.helper = new QVTimperativeHelper(visitor.getEnvironmentFactory());
this.region = region;
- String name = region.getSymbolName();
- assert name != null;
- this.mapping = helper.createMapping(name);
+ this.regionName = RegionUtil.getName(region);
+ String mappingName = region.getSymbolName();
+ assert mappingName != null;
+ this.mapping = helper.createMapping(mappingName);
+ this.mappingName = mappingName;
+ if (region instanceof BasicMappingRegion) {
+ this.mapping.setIsAbstract(((BasicMappingRegion)region).getReferredMapping().isIsAbstract());
+ }
this.names = new HashSet<@NonNull String>(visitor.getReservedNames());
for (@NonNull Node node : RegionUtil.getOwnedNodes(region)) {
for (TypedElement typedElement : node.getTypedElements()) {
@@ -144,6 +152,10 @@
}
}
+ public @NonNull MappingCall createMappingCall(@NonNull List<@NonNull MappingParameterBinding> mappingParameterBindings) {
+ return helper.createMappingCall(getMapping(), mappingParameterBindings);
+ }
+
protected @NonNull CallExp createOclAsTypeCallExp(@NonNull OCLExpression asSource, @NonNull Type asType) {
ScheduleManager scheduleManager = RegionUtil.getScheduleManager(getRegion());
CompleteClass completeClass = scheduleManager.getEnvironmentFactory().getCompleteModel().getCompleteClass(asType);
@@ -151,8 +163,6 @@
return helper.createOperationCallExp(asSource, "oclAsType", asTypeExp);
}
- public abstract void createSchedulingStatements();
-
protected int getCollectionDepth(@NonNull Type type) {
if (type instanceof CollectionType) {
Type elementType = ((CollectionType)type).getElementType();
@@ -279,12 +289,11 @@
return false;
}
+ public abstract void synthesizeCallStatements();
+ public abstract void synthesizeLocalStatements();
+
@Override
public String toString() {
return mapping.toString();
}
-
- public @NonNull MappingCall createMappingCall(@NonNull List<@NonNull MappingParameterBinding> mappingParameterBindings) {
- return helper.createMappingCall(getMapping(), mappingParameterBindings);
- }
}
\ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/BasicRegion2Mapping.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/BasicRegion2Mapping.java
index 9a465c5..0ad9f4b 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/BasicRegion2Mapping.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/BasicRegion2Mapping.java
@@ -13,7 +13,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -30,6 +29,7 @@
import org.eclipse.ocl.pivot.CollectionLiteralPart;
import org.eclipse.ocl.pivot.CollectionRange;
import org.eclipse.ocl.pivot.CollectionType;
+import org.eclipse.ocl.pivot.CompleteClass;
import org.eclipse.ocl.pivot.EnumLiteralExp;
import org.eclipse.ocl.pivot.IfExp;
import org.eclipse.ocl.pivot.IterateExp;
@@ -71,6 +71,7 @@
import org.eclipse.qvtd.compiler.internal.qvtm2qvts.ScheduleManager;
import org.eclipse.qvtd.compiler.internal.qvts2qvts.ClassDatumAnalysis;
import org.eclipse.qvtd.compiler.internal.qvts2qvts.RegionAnalysis;
+import org.eclipse.qvtd.compiler.internal.qvts2qvts.utilities.ReachabilityForest;
import org.eclipse.qvtd.pivot.qvtbase.Function;
import org.eclipse.qvtd.pivot.qvtbase.Transformation;
import org.eclipse.qvtd.pivot.qvtbase.TypedModel;
@@ -105,6 +106,7 @@
import org.eclipse.qvtd.pivot.qvtschedule.Region;
import org.eclipse.qvtd.pivot.qvtschedule.Role;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleConstants;
+import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleUtil;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -117,19 +119,8 @@
* to build a linear edge traversal schedule in which unconditional checks that may fail appear
* as early as possible.
*/
- private class OldEdgeSchedule implements Comparator<@NonNull Edge>
+ private class OldEdgeSchedule
{
- protected final @NonNull Region region;
-
- private @NonNull Map<@NonNull Node, @NonNull Integer> node2depth;
-
- private final @NonNull List<@NonNull Edge> oldUnconditionalEdges;
-
- /**
- * All properties (and their opposites) that need to be checked for readiness before access.
- */
- private final @Nullable Set<@NonNull Property> allCheckedProperties;
-
/**
* The linear execution schedule for edges.
*/
@@ -141,16 +132,12 @@
private final @NonNull Set<@NonNull Edge> scheduledEdges = new HashSet<>();
/**
- * The edges (and their opposites) in edgeSchedule.
+ * The nodes at the ends of the edges in edgeSchedule.
*/
private final @NonNull Set<@NonNull Node> scheduledNodes;
- public OldEdgeSchedule(@NonNull Region region) {
- this.region = region;
- this.node2depth = computeDepths();
- this.oldUnconditionalEdges = computeOldUnconditionalEdges();
- this.allCheckedProperties = computeCheckedProperties();
- this.scheduledNodes = Sets.newHashSet(RegionUtil.getHeadNodes(region));
+ public OldEdgeSchedule() {
+ this.scheduledNodes = Sets.newHashSet(RegionUtil.getHeadNodes(region)); // ?? leaf constants
}
private void addEdge(@NonNull Edge edge) {
@@ -176,21 +163,21 @@
if (scheduledNodes.add(targetNode)) {
Subexpression subexpression = node2subexpression.get(targetNode);
if (subexpression != null) {
- for (@NonNull Node inputNode : subexpression.inputNodes) {
+ for (@NonNull Node inputNode : subexpression.unconditionalInputNodes) {
addNode(inputNode);
}
+ targetNode = subexpression.resultNode;
+ addNode(targetNode);
}
- else {
- Integer targetDepth = node2depth.get(targetNode);
- assert targetDepth != null;
- for (@NonNull Edge edge : RegionUtil.getIncomingEdges(targetNode)) {
- if (edge.isOld()) {
- Node sourceNode = RegionUtil.getSourceNode(edge);
- Integer sourceDepth = node2depth.get(sourceNode);
- assert sourceDepth != null;
- if (sourceDepth < targetDepth) {
- addEdge(edge);
- }
+ Integer targetCost = reachabilityForest.getCost(targetNode);
+ assert targetCost != null;
+ for (@NonNull Edge edge : RegionUtil.getIncomingEdges(targetNode)) {
+ if (edge.isOld() && edge.isUnconditional()) {
+ Node sourceNode = RegionUtil.getSourceNode(edge);
+ Integer sourceCost = reachabilityForest.getCost(sourceNode);
+ assert sourceCost != null;
+ if (sourceCost < targetCost) {
+ addEdge(edge);
}
}
}
@@ -198,15 +185,17 @@
}
public void analyze() {
- Set<@NonNull Edge> checkableEdges = computeCheckables();
- int checkableSize = checkableEdges.size();
+ Set<@NonNull CheckedCondition> checkedConditions = computeCheckedConditions();
+ int checkableSize = checkedConditions.size();
if (checkableSize > 0) {
- List<@NonNull Edge> sortedCheckableEdges = new ArrayList<>(checkableEdges);
+ List<@NonNull CheckedCondition> sortedCheckedConditions = new ArrayList<>(checkedConditions);
if (checkableSize > 1) {
- Collections.sort(sortedCheckableEdges, this);
+ Collections.sort(sortedCheckedConditions);
}
- for (@NonNull Edge checkableEdge : sortedCheckableEdges) {
- addEdge(checkableEdge);
+ for (@NonNull CheckedCondition checkedCondition : sortedCheckedConditions) {
+ for (@NonNull Edge edge : checkedCondition.getEdges()) {
+ addEdge(edge);
+ }
}
}
for (@NonNull Edge edge : oldUnconditionalEdges) {
@@ -218,66 +207,84 @@
// assert new HashSet<>(edgeSchedule).equals(new HashSet<>(oldEdges));
}
- @Override
- public int compare(@NonNull Edge e1, @NonNull Edge e2) {
- Integer d1s = node2depth.get(RegionUtil.getSourceNode(e1));
- Integer d1t = node2depth.get(RegionUtil.getTargetNode(e1));
- Integer d2s = node2depth.get(RegionUtil.getSourceNode(e2));
- Integer d2t = node2depth.get(RegionUtil.getTargetNode(e2));
- assert (d1s != null) && (d1t != null) && (d2s != null) && (d2t != null);
- int d1 = Math.max(d1s, d1t);
- int d2 = Math.max(d2s, d2t);
- if (d1 != d2) {
- return d1 - d2;
- }
- if (d1s != d2s) {
- return d1s - d2s;
- }
- return ClassUtil.safeCompareTo(e1.getDisplayName(), e2.getDisplayName());
- }
-
/**
* Return all old edges that require a check for readiness or consistency.
*/
- private @NonNull Set<@NonNull Edge> computeCheckables() {
+ private @NonNull Set<@NonNull CheckedCondition> computeCheckedConditions() {
Set<@NonNull Property> allCheckedProperties2 = allCheckedProperties;
- Set<@NonNull Edge> checkables = new HashSet<>();
+ Set<@NonNull CheckedCondition> checkedConditions = new HashSet<>();
+ Set<@NonNull NavigableEdge> checkedNavigableEdges = null;
for (@NonNull Edge edge : oldUnconditionalEdges) {
+ CheckedCondition checkedCondition = null;
if (edge instanceof CastEdge) {
- checkables.add(edge);
+ checkedCondition = new CastEdgeCheckedCondition((CastEdge)edge);
}
else if (edge instanceof PredicateEdge) {
- checkables.add(edge);
+ checkedCondition = new PredicateEdgeCheckedCondition((PredicateEdge)edge);
}
else if (edge instanceof NavigableEdge) {
+ NavigableEdge navigableEdge = (NavigableEdge)edge;
if (allCheckedProperties2 != null) {
- Property property = ((NavigableEdge)edge).getProperty();
+ NavigableEdge primaryEdge = RegionUtil.getPrimaryEdge(navigableEdge);
+ Property property = primaryEdge.getProperty();
if (allCheckedProperties2.contains(property)) {
- checkables.add(edge);
+ if (checkedNavigableEdges == null) {
+ checkedNavigableEdges = new HashSet<>();
+ }
+ if (checkedNavigableEdges.add(primaryEdge)) {
+ checkedCondition = new NavigableEdgeCheckedCondition(primaryEdge);
+ }
+ }
+ }
+ if (checkedCondition == null) {
+ Node targetNode = RegionUtil.getTargetNode(edge);
+ if (edge.isPredicated() && targetNode.isConstant()) {
+ checkedCondition = new ConstantTargetCheckedCondition((NavigableEdge)edge);
+ }
+ if ((checkedCondition == null) && edge.isOld()) {
+ Property property = QVTscheduleUtil.getProperty(navigableEdge);
+ CompleteClass edgeTargetCompleteClass = getMetamodelManager().getCompleteModel().getCompleteClass(QVTrelationUtil.getType(property));
+ Node sourceNode = RegionUtil.getSourceNode(edge);
+ Integer sourceCost = reachabilityForest.getCost(sourceNode);
+ Integer targetCost = reachabilityForest.getCost(targetNode);
+ assert (sourceCost != null) && (targetCost != null);
+ if (sourceCost < targetCost) {
+ CompleteClass targetNodeCompleteClass = targetNode.getCompleteClass();
+ if (!edgeTargetCompleteClass.conformsTo(targetNodeCompleteClass)) {
+ checkedCondition = new CastEdgeCheckedCondition(navigableEdge);
+ }
+ }
}
}
}
+ if (checkedCondition != null) {
+ checkedConditions.add(checkedCondition);
+ }
}
//
- // Double input nodes require a consistency check.
+ // Multi-input nodes require a consistency check.
//
for (@NonNull Node node : RegionUtil.getOwnedNodes(region)) {
if (node.isOld() && node.isUnconditional()) {
- Integer targetDepth = node2depth.get(node);
- assert targetDepth != null;
+ Integer targetCost = reachabilityForest.getCost(node);
+ assert targetCost != null;
if (node.isOld()) {
Edge firstEdge = null;
+ MultipleEdgeCheckedCondition checkedCondition = null;
for (@NonNull Edge edge : RegionUtil.getIncomingEdges(node)) {
- if (edge.isOld() && !edge.isExpression()) {
- Integer sourceDepth = node2depth.get(RegionUtil.getSourceNode(edge));
- assert sourceDepth != null;
- if (sourceDepth <= targetDepth) {
+ if (edge.isOld() && !edge.isExpression()) { // FIXME why exclude expression?
+ Integer sourceCost = reachabilityForest.getCost(RegionUtil.getSourceNode(edge));
+ assert sourceCost != null;
+ if (sourceCost <= targetCost) {
if (firstEdge == null) {
firstEdge = edge;
}
+ else if (checkedCondition == null){
+ checkedCondition = new MultipleEdgeCheckedCondition(firstEdge, edge);
+ checkedConditions.add(checkedCondition);
+ }
else {
- checkables.add(firstEdge);
- checkables.add(edge);
+ checkedCondition.addEdge(edge);
}
}
}
@@ -285,94 +292,7 @@
}
}
}
- return checkables;
- }
-
- /**
- * Return all properties (and their opposites) that need checking for readiness prior to access.
- */
- private @Nullable Set<@NonNull Property> computeCheckedProperties() {
- //
- // Better, we would not be pessimistic about input/output typedModel ambiguity in endogeneous
- // mappings, but that incurs many typedModel accuracy issues.
- //
- Set<@NonNull Property> allCheckedProperties = null;
- DomainUsage anyUsage = RegionUtil.getScheduleManager(region).getDomainAnalysis().getAnyUsage();
- for (@NonNull TypedModel qvtmTypedModel : anyUsage.getTypedModels()) {
- Iterable<@NonNull NavigableEdge> checkedEdges = RegionAnalysis.get(region).getCheckedEdges(qvtmTypedModel);
- if (checkedEdges != null) {
- for (NavigableEdge checkedEdge : checkedEdges) {
- Property asProperty = RegionUtil.getProperty(checkedEdge);
- if (allCheckedProperties == null) {
- allCheckedProperties = new HashSet<>();
- }
- allCheckedProperties.add(asProperty);
- Property asOppositeProperty = asProperty.getOpposite();
- if (asOppositeProperty != null) {
- allCheckedProperties.add(asOppositeProperty);
- }
- }
- }
- }
- return allCheckedProperties;
- }
-
- /**
- * Return a map from each old node to the minimum edge distance from a head node.
- * @return
- */
- private @NonNull Map<@NonNull Node, @NonNull Integer> computeDepths() {
- // Successive depth candidate iteration works fine.
- // A recursion has trouble with the forward/reverse edge pairs.
- // A max-depth similarly has trouble with forward/reverse recursion.
- Map<@NonNull Node, @NonNull Integer> node2minDepth = new HashMap<>();
- Set<@NonNull Node> lowerDepthNodes = new HashSet<>();
- Iterable<@NonNull Node> thisDepthNodes = RegionUtil.getHeadNodes(region);
- for (int depth = 0; thisDepthNodes != null; depth++) {
- Set<@NonNull Node> nextDepthNodes = null;
- for (@NonNull Node node : thisDepthNodes) {
- if (!node2minDepth.containsKey(node)) {
- node2minDepth.put(node, depth);
- }
- for (@NonNull Edge edge : RegionUtil.getOutgoingEdges(node)) {
- if (edge.isOld()) {
- Node targetNode = RegionUtil.getTargetNode(edge);
- if (!lowerDepthNodes.contains(targetNode)) {
- if (nextDepthNodes == null) {
- nextDepthNodes = new HashSet<>();
- }
- nextDepthNodes.add(targetNode);
- }
- }
- }
- }
- for (@NonNull Node node : thisDepthNodes) {
- lowerDepthNodes.add(node);
- }
- thisDepthNodes = nextDepthNodes;
- }
- for (@NonNull Node node : RegionUtil.getOwnedNodes(region)) {
- if (node.isOld()) {
- Integer depth = node2minDepth.get(node);
- if (depth == null) {
- assert node.isConstant();
- node2minDepth.put(node, 0);
- }
- }
- }
- return node2minDepth;
- }
-
- private @NonNull List<@NonNull Edge> computeOldUnconditionalEdges() {
- Set<@NonNull Edge> oldEdges = new HashSet<>();
- for (@NonNull Edge edge : RegionUtil.getOwnedEdges(region)) {
- if (edge.isOld() && edge.isUnconditional()) {
- oldEdges.add(edge);
- }
- }
- List<@NonNull Edge> sortedEdges = new ArrayList<>(oldEdges);
- Collections.sort(sortedEdges, this);
- return sortedEdges;
+ return checkedConditions;
}
private void createCastPredicates(@NonNull Node sourceNode, @NonNull VariableDeclaration sourceVariable) {
@@ -400,12 +320,7 @@
}
}
- public @Nullable Set<@NonNull Property> getAllCheckedProperties() {
- return allCheckedProperties;
- }
-
public void synthesize() {
- @SuppressWarnings("unused")String regionName = region.getName();
for (@NonNull Edge edge : edgeSchedule) {
assert edge.isUnconditional();
Node sourceNode = RegionUtil.getSourceNode(edge);
@@ -452,7 +367,7 @@
getSubexpressionDeclaration(targetNode);
}
}
- List<@NonNull Subexpression> subexpressions = new ArrayList<>(node2subexpression.values());
+ List<@NonNull Subexpression> subexpressions = new ArrayList<>(resultNode2subexpression.values());
Collections.sort(subexpressions);
for (@NonNull Subexpression subexpression : subexpressions) {
if (!subexpression.resultNode.isNew() && subexpression.resultNode.isUnconditional()) {
@@ -921,16 +836,29 @@
}
TypedElement oldTypedElement = node.getTypedElements().iterator().next();
assert oldTypedElement != null;
- if ((oldTypedElement instanceof Variable) && (((Variable)oldTypedElement).getOwnedInit() == null)) {
- for (@NonNull Edge edge : RegionUtil.getIncomingEdges(node)) {
- if (edge.isExpression() && QVTscheduleConstants.EQUALS_NAME.equals(edge.getName())) {
- Node edgeSource = edge.getEdgeSource();
- return create(edgeSource); //createBottomVariable(node, helper.createNullLiteralExp()); // FIXME is this possible?
+ if (oldTypedElement instanceof Variable) {
+ Variable variable = (Variable)oldTypedElement;
+ OCLExpression ownedInit = variable.getOwnedInit();
+ if (ownedInit == null) {
+ for (@NonNull Edge edge : RegionUtil.getIncomingEdges(node)) {
+ if (edge.isExpression() && QVTscheduleConstants.EQUALS_NAME.equals(edge.getName())) {
+ Node sourceNode = RegionUtil.getSourceNode(edge);
+ return create(sourceNode); //createBottomVariable(node, helper.createNullLiteralExp()); // FIXME is this possible?
+ }
+ else if (edge.isCast()) {
+ throw new UnsupportedOperationException();
+ //theVariable = createBottomVariable(node, helper.createNullLiteralExp()); // FIXME is this possible?
+ }
+ else if (edge.isNavigation()) {
+ Node sourceNode = RegionUtil.getSourceNode(edge);
+ OCLExpression sourceExpression = create(sourceNode);
+ Property referredProperty = RegionUtil.getProperty((NavigableEdge) edge);
+ return helper.createNavigationCallExp(sourceExpression, referredProperty);
+ }
}
- else if (edge.isCast()) {
- throw new UnsupportedOperationException();
- //theVariable = createBottomVariable(node, helper.createNullLiteralExp()); // FIXME is this possible?
- }
+ }
+ else {
+ return ownedInit.accept(this);
}
}
else {
@@ -948,16 +876,21 @@
private final @NonNull OperationNode resultNode;
/**
- * Externalnodes used within the subexpression
+ * External nodes used within the subexpression
*/
private final @NonNull Set<@NonNull Node> inputNodes = new HashSet<>();
/**
+ * External nodes used within the subexpression
+ */
+ private final @NonNull Set<@NonNull Node> unconditionalInputNodes = new HashSet<>();
+
+ /**
* Nodes within the subexpression
*/
private final @NonNull Set<@NonNull Node> contentNodes = new HashSet<>();
- private @Nullable Integer depth = null;
+ private @Nullable Integer cost = null;
public Subexpression(@NonNull OperationNode resultNode) {
this.resultNode = resultNode;
@@ -971,8 +904,8 @@
@Override
public int compareTo(@NonNull Subexpression e2) {
Subexpression e1 = this;
- int d1 = e1.getDepth();
- int d2 = e2.getDepth();
+ int d1 = e1.getCost();
+ int d2 = e2.getCost();
if (d1 != d2) {
return d1 - d2;
}
@@ -981,26 +914,36 @@
private void computeSubexpressionContent(@NonNull Node node, @NonNull Set<@NonNull OperationNode> subexpressionResults) {
if (contentNodes.add(node)) {
+ node2subexpression.put(node, this);
for (@NonNull Edge edge : RegionUtil.getIncomingEdges(node)) {
// if (edge.isUnconditional()) {
Node sourceNode = edge.getEdgeSource();
if (subexpressionResults.contains(sourceNode)) {
if (sourceNode.isOld() || (!edge.isSecondary() && resultNode.isNew())) {
inputNodes.add(sourceNode);
+ if (edge.isUnconditional() && sourceNode.isUnconditional()) {
+ unconditionalInputNodes.add(sourceNode);
+ }
+ // edge2subexpression.put(edge, this);
}
}
else if (sourceNode.isExpression()) {
+ node2subexpression.put(sourceNode, this);
edge2subexpression.put(edge, this);
computeSubexpressionContent(sourceNode, subexpressionResults);
}
else if (!sourceNode.isUnconditional()) {
+ node2subexpression.put(sourceNode, this);
edge2subexpression.put(edge, this);
computeSubexpressionContent(sourceNode, subexpressionResults);
}
else {
if (sourceNode.isOld() || resultNode.isNew()) {
inputNodes.add(sourceNode);
- edge2subexpression.put(edge, this);
+ if (edge.isUnconditional() && sourceNode.isUnconditional()) {
+ unconditionalInputNodes.add(sourceNode);
+ }
+ // edge2subexpression.put(edge, this);
}
}
// }
@@ -1008,19 +951,23 @@
}
}
- public int getDepth() {
- Integer depth2 = depth;
- if (depth2 == null) {
- int inputDepth = 0;
- for (@NonNull Node inputNode : inputNodes) {
- Subexpression inputSubexpression = node2subexpression.get(inputNode);
- if (inputSubexpression != null) {
- inputDepth = Math.max(inputDepth, inputSubexpression.getDepth()+1);
+ public int getCost() {
+ Integer cost2 = cost;
+ if (cost2 == null) {
+ cost2 = reachabilityForest.getCost(resultNode);
+ if (cost2 == null) { // Can happen for REALIZED results
+ int inputCost = 0;
+ for (@NonNull Node inputNode : inputNodes) {
+ Subexpression inputSubexpression = resultNode2subexpression.get(inputNode);
+ if (inputSubexpression != null) {
+ inputCost = Math.max(inputCost, inputSubexpression.getCost()+1);
+ }
}
+ cost2 = inputCost;
}
- depth = depth2 = inputDepth;
+ cost = cost2;
}
- return depth2;
+ return cost2;
}
@Override
public @NonNull String toString() {
@@ -1029,9 +976,197 @@
}
/**
- * Map from each subexpression result to its contents.
+ * A CheckedCondition identifies a possible micromapping failure mechanism. QVTi synthesis prioritises
+ * easily computed CheckedConditions to minimize the wasted effort in computing failures.
*/
- private final @NonNull Map<@NonNull OperationNode, @NonNull Subexpression> node2subexpression;
+ abstract class CheckedCondition implements Comparable<@NonNull CheckedCondition>
+ {
+ private int weight = -1;
+
+ protected void accumulateNodes(@NonNull Set<@NonNull Node> requiredNodes, @NonNull Edge edge) {
+ requiredNodes.addAll(getPrecedingNodes(RegionUtil.getSourceNode(edge)));
+ requiredNodes.addAll(getPrecedingNodes(RegionUtil.getTargetNode(edge)));
+ }
+
+ @Override
+ public int compareTo(@NonNull CheckedCondition that) {
+ int w1 = this.getWeight();
+ int w2 = that.getWeight();
+ return w1 - w2;
+ }
+
+ protected abstract int computeWeight();
+
+ public abstract @NonNull Iterable<@NonNull Edge> getEdges();
+
+ public int getWeight() {
+ if (weight < 0) {
+ weight = computeWeight();
+ }
+ return weight;
+ }
+
+ @Override
+ public @NonNull String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append(getClass().getSimpleName());
+ s.append("(");
+ boolean isFirst = true;
+ for (@NonNull Edge edge : getEdges()) {
+ if (!isFirst) {
+ s.append(",");
+ }
+ s.append(edge.getDisplayName());
+ isFirst = false;
+ }
+ s.append(")");
+ return s.toString();
+ }
+
+ }
+
+ /**
+ * A CastEdgeCheckedCondition identifies the failure when a source fails to compky with the required target type.
+ */
+ class CastEdgeCheckedCondition extends CheckedCondition
+ {
+ private final @NonNull NavigableEdge castEdge;
+
+ public CastEdgeCheckedCondition(@NonNull NavigableEdge castEdge) {
+ this.castEdge = castEdge;
+ }
+
+ @Override
+ protected int computeWeight() {
+ Set<@NonNull Node> requiredNodes = new HashSet<>();
+ accumulateNodes(requiredNodes, castEdge);
+ return requiredNodes.size();
+ }
+
+ @Override
+ public @NonNull Iterable<@NonNull Edge> getEdges() {
+ return Collections.singletonList(castEdge);
+ }
+ }
+
+ /**
+ * A ConstantTargetCheckedCondition identifies the failure when a computation fails to yield the required constant value.
+ */
+ class ConstantTargetCheckedCondition extends CheckedCondition
+ {
+ private final @NonNull NavigableEdge predicateEdge;
+
+ public ConstantTargetCheckedCondition(@NonNull NavigableEdge predicateEdge) {
+ this.predicateEdge = predicateEdge;
+ }
+
+ @Override
+ protected int computeWeight() {
+ Set<@NonNull Node> requiredNodes = new HashSet<>();
+ requiredNodes.addAll(getPrecedingNodes(RegionUtil.getSourceNode(predicateEdge)));
+ return requiredNodes.size();
+ }
+
+ @Override
+ public @NonNull Iterable<@NonNull Edge> getEdges() {
+ return Collections.singletonList(predicateEdge);
+ }
+ }
+
+ /**
+ * A MultipleEdgeCheckedCondition identifies the failure when two alternate navigation paths yield inconsistent results.
+ */
+ class MultipleEdgeCheckedCondition extends CheckedCondition
+ {
+ private final @NonNull List<@NonNull Edge> edges = new ArrayList<>();
+
+ public MultipleEdgeCheckedCondition(@NonNull Edge firstEdge, @NonNull Edge secondEdge) {
+ addEdge(firstEdge);
+ addEdge(secondEdge);
+ }
+
+ public boolean addEdge(@NonNull Edge edge) {
+ return edges.add(edge);
+ }
+
+ @Override
+ protected int computeWeight() {
+ Set<@NonNull Node> requiredNodes = new HashSet<>();
+ for (@NonNull Edge edge : edges) {
+ accumulateNodes(requiredNodes, edge);
+ }
+ return requiredNodes.size();
+ }
+
+ @Override
+ public @NonNull Iterable<@NonNull Edge> getEdges() {
+ return edges;
+ }
+ }
+
+ /**
+ * A NavigableEdgeCheckedCondition identifies the temporary failure that arises when
+ * observing a property access to a not-yet assigned slot.
+ */
+ class NavigableEdgeCheckedCondition extends CheckedCondition
+ {
+ private final @NonNull NavigableEdge navigableEdge;
+
+ public NavigableEdgeCheckedCondition(@NonNull NavigableEdge navigableEdge) {
+ this.navigableEdge = navigableEdge;
+ }
+
+ @Override
+ protected int computeWeight() {
+ Set<@NonNull Node> requiredNodes = new HashSet<>();
+ accumulateNodes(requiredNodes, navigableEdge);
+ return requiredNodes.size();
+ }
+
+ @Override
+ public @NonNull Iterable<@NonNull Edge> getEdges() {
+ return Collections.singletonList(navigableEdge);
+ }
+ }
+
+ /**
+ * A PredicateEdgeCheckedCondition identifies the failure of a required consistent computation result.
+ */
+ class PredicateEdgeCheckedCondition extends CheckedCondition
+ {
+ private final @NonNull PredicateEdge predicateEdge;
+
+ public PredicateEdgeCheckedCondition(@NonNull PredicateEdge predicateEdge) {
+ this.predicateEdge = predicateEdge;
+ }
+
+ @Override
+ protected int computeWeight() {
+ Set<@NonNull Node> requiredNodes = new HashSet<>();
+ accumulateNodes(requiredNodes, predicateEdge);
+ return requiredNodes.size();
+ }
+
+ @Override
+ public @NonNull Iterable<@NonNull Edge> getEdges() {
+ return Collections.singletonList(predicateEdge);
+ }
+ }
+
+ /**
+ * Lazily computed closure of all preceding nodes, icluding the final node, of each node.
+ */
+ private @Nullable Map<@NonNull Node, @NonNull Set<@NonNull Node>> node2precedingNodeClosure = null;
+
+ /**
+ * Map from each subexpression result node to its contents.
+ */
+ private final @NonNull Map<@NonNull OperationNode, @NonNull Subexpression> resultNode2subexpression;
+
+ /**
+ * Map from each subexpression node to its contents.
+ */
+ private final @NonNull Map<@NonNull Node, @NonNull Subexpression> node2subexpression = new HashMap<>();
/**
* Map from each subexpression edge to its contents.
@@ -1039,6 +1174,18 @@
private final @NonNull Map<@NonNull Edge, @NonNull Subexpression> edge2subexpression = new HashMap<>();
/**
+ * All properties (and their opposites) that need to be checked for readiness before access.
+ */
+ private final @Nullable Set<@NonNull Property> allCheckedProperties;
+
+ /**
+ * The unconditional old edges provide the pattern matching workload.
+ * Once cost sorted the list is a sensible residual scheduling order after higher priority checked edges.
+ * Conditional edges should be scheduled as a consequence of an invoking unconditional edge/node.
+ */
+ private final @NonNull List<@NonNull Edge> oldUnconditionalEdges;
+
+ /**
* The selected head from each head group that is actually passed. Other heads are computed.
*/
private final @NonNull List<@NonNull Node> headNodes = new ArrayList<>();
@@ -1053,27 +1200,70 @@
*/
private final @NonNull Map<@NonNull Node, @NonNull VariableDeclaration> node2variable = new HashMap<>();
+ /**
+ * The mechanisms to reach required nodes.
+ */
+ private final @NonNull ReachabilityForest reachabilityForest;
+
public BasicRegion2Mapping(@NonNull QVTs2QVTiVisitor visitor, @NonNull Region region) {
super(visitor, region);
- @SuppressWarnings("unused")String name = region.getName();
- this.node2subexpression = computeSubexpressions();
-
+ this.reachabilityForest = new ReachabilityForest(getReachabilityRootNodes(), getAvailableNavigableEdges());
+ this.resultNode2subexpression = computeSubexpressions();
//
- // Create the 'local' statements.
+ // Gather the subexpression contents.
//
- createHeadAndGuardNodeVariables(); // BLUE/CYAN guard/append nodes
- OldEdgeSchedule oldSchedule = createNavigablePredicates(); // BLUE/CYAN navigable nodes and edges
- createRealizedVariables(); // GREEN nodes
- createPropertyAssignments(); // GREEN edges
- createAddStatements(); // export to append nodes
- createRealizedIncludesAssignments();
- Set<@NonNull Property> allCheckedProperties = oldSchedule.getAllCheckedProperties();
- if (allCheckedProperties != null) {
- createObservedProperties(allCheckedProperties); // wrap observable clauses around hazardous accesses
+ Set<@NonNull OperationNode> subexpressionResults = resultNode2subexpression.keySet();
+ for (@NonNull Subexpression subexpression : resultNode2subexpression.values()) {
+ subexpression.analyze(subexpressionResults);
}
+ this.allCheckedProperties = computeCheckedProperties();
+ this.oldUnconditionalEdges = computeOldUnconditionalEdges();
+ }
+ private @NonNull Iterable<@NonNull Node> getReachabilityRootNodes() {
//
- // The 'global' statements are created by createSchedulingStatements().
+ // The zero-cost nodes are the head nodes ...
//
+ List<@NonNull Node> zeroCostNodes = Lists.newArrayList(RegionUtil.getHeadNodes(region));
+ //
+ // ... and the no-input constant nodes
+ //
+ for (@NonNull Node node : RegionUtil.getOwnedNodes(region)) {
+ if (node.isExplicitNull() || node.isOperation()) {
+ if (node.isConstant()) {
+ boolean hasNoComputationInputs = true;
+ for (@NonNull Edge edge : RegionUtil.getIncomingEdges(node)) {
+ if (edge.isComputation()) {
+ hasNoComputationInputs = false;
+ break;
+ }
+ }
+ if (hasNoComputationInputs) {
+ zeroCostNodes.add(node);
+ }
+ }
+ }
+ }
+ return zeroCostNodes;
+ }
+ /**
+ * Return the navigable edges that may be used by to locate nodes by this partition.
+ * The default implementation returns all old primary navigable edges
+ * and all already realized navigable edges
+ */
+ protected @NonNull Iterable<@NonNull NavigableEdge> getAvailableNavigableEdges() {
+ Set<@NonNull NavigableEdge> oldEdges = new HashSet<>();
+ for (@NonNull Edge edge : RegionUtil.getOwnedEdges(region)) {
+ if (edge.isOld() && edge.isNavigation() /*&& edge.isUnconditional()*/) {
+ Node sourceNode = RegionUtil.getSourceNode(edge);
+ Node targetNode = RegionUtil.getTargetNode(edge);
+ if (sourceNode.isOld() && targetNode.isOld()) {
+ oldEdges.add((NavigableEdge) edge);
+ }
+ }
+ }
+ // List<@NonNull Edge> sortedEdges = new ArrayList<>(oldEdges);
+ // Collections.sort(sortedEdges, reachabilityForest.getEdgeCostComparator());
+ return oldEdges;
}
public @Nullable VariableDeclaration basicGetVariable(@NonNull Node node) {
@@ -1081,55 +1271,103 @@
}
/**
+ * Return all properties (and their opposites) that need checking for readiness prior to access.
+ */
+ private @Nullable Set<@NonNull Property> computeCheckedProperties() {
+ //
+ // Better, we would not be pessimistic about input/output typedModel ambiguity in endogeneous
+ // mappings, but that incurs many typedModel accuracy issues.
+ //
+ Set<@NonNull Property> allCheckedProperties = null;
+ DomainUsage anyUsage = RegionUtil.getScheduleManager(region).getDomainAnalysis().getAnyUsage();
+ for (@NonNull TypedModel qvtmTypedModel : anyUsage.getTypedModels()) {
+ Iterable<@NonNull NavigableEdge> checkedEdges = RegionAnalysis.get(region).getCheckedEdges(qvtmTypedModel);
+ if (checkedEdges != null) {
+ for (@NonNull NavigableEdge checkedEdge : checkedEdges) {
+ Property asProperty = RegionUtil.getProperty(checkedEdge);
+ if (allCheckedProperties == null) {
+ allCheckedProperties = new HashSet<>();
+ }
+ allCheckedProperties.add(asProperty);
+ Property asOppositeProperty = asProperty.getOpposite();
+ if (asOppositeProperty != null) {
+ allCheckedProperties.add(asOppositeProperty);
+ }
+ }
+ }
+ }
+ return allCheckedProperties;
+ }
+
+ private @NonNull List<@NonNull Edge> computeOldUnconditionalEdges() {
+ Set<@NonNull Edge> oldEdges = new HashSet<>();
+ for (@NonNull Edge edge : RegionUtil.getOwnedEdges(region)) {
+ if (edge.isOld() && edge.isUnconditional()) {
+ Node sourceNode = RegionUtil.getSourceNode(edge);
+ Node targetNode = RegionUtil.getTargetNode(edge);
+ if (sourceNode.isOld() && targetNode.isOld()) {
+ oldEdges.add(edge);
+ }
+ }
+ }
+ List<@NonNull Edge> sortedEdges = new ArrayList<>(oldEdges);
+ Collections.sort(sortedEdges, reachabilityForest.getEdgeCostComparator());
+ return sortedEdges;
+ }
+
+ /**
* Return all subexpressions and their content. A subexpression is an OperationNode whose value is consumed by
* one (or more) PatternNodes or by two (or more) OperationNodes.
*/
private @NonNull Map<@NonNull OperationNode, @NonNull Subexpression> computeSubexpressions() {
- Map<@NonNull OperationNode, @NonNull Subexpression> node2subexpression = new HashMap<>();
+ Map<@NonNull OperationNode, @NonNull Subexpression> resultNode2subexpression = new HashMap<>();
//
// Identify the subexpression result nodes
//
for (@NonNull Node node : RegionUtil.getOwnedNodes(region)) {
if (node.isOperation() && node.isUnconditional()) {
OperationNode resultNode = (OperationNode) node;
- Edge firstEdge = null;
- boolean isSubexpression = false;
- for (@NonNull Edge edge : RegionUtil.getIncomingEdges(node)) {
- if (edge.isNew() && !edge.isSecondary()) {
- Node edgeSource = edge.getSourceNode();
- if (edgeSource.isPattern()) {
- node2subexpression.put(resultNode, new Subexpression(resultNode));
- isSubexpression = true;
- break;
- }
+ boolean isPrimitiveLiteralExp = false;
+ if (node.isConstant()) {
+ TypedElement oldTypedElement = node.getTypedElements().iterator().next();
+ assert oldTypedElement != null;
+ if (oldTypedElement instanceof PrimitiveLiteralExp) {
+ isPrimitiveLiteralExp = true;
}
}
- if (!isSubexpression) {
- for (@NonNull Edge edge : RegionUtil.getOutgoingEdges(node)) {
- Node edgeTarget = edge.getTargetNode();
- if (edgeTarget.isPattern() || edgeTarget.isTrue()) {
- node2subexpression.put(resultNode, new Subexpression(resultNode));
- break;
+ if (!isPrimitiveLiteralExp) {
+ Edge firstEdge = null;
+ boolean isSubexpression = false;
+ for (@NonNull Edge edge : RegionUtil.getIncomingEdges(node)) {
+ if (edge.isNew() && !edge.isSecondary()) {
+ Node edgeSource = edge.getSourceNode();
+ if (edgeSource.isPattern()) {
+ resultNode2subexpression.put(resultNode, new Subexpression(resultNode));
+ isSubexpression = true;
+ break;
+ }
}
- if (firstEdge == null) {
- firstEdge = edge;
- }
- else {
- node2subexpression.put(resultNode, new Subexpression(resultNode));
- break;
+ }
+ if (!isSubexpression) {
+ for (@NonNull Edge edge : RegionUtil.getOutgoingEdges(node)) {
+ Node edgeTarget = edge.getTargetNode();
+ if (edgeTarget.isPattern() || edgeTarget.isTrue()) {
+ resultNode2subexpression.put(resultNode, new Subexpression(resultNode));
+ break;
+ }
+ if (firstEdge == null) {
+ firstEdge = edge;
+ }
+ else {
+ resultNode2subexpression.put(resultNode, new Subexpression(resultNode));
+ break;
+ }
}
}
}
}
}
- //
- // Gather the subexpression contents.
- //
- Set<@NonNull OperationNode> subexpressionResults = node2subexpression.keySet();
- for (@NonNull Subexpression subexpression : node2subexpression.values()) {
- subexpression.analyze(subexpressionResults);
- }
- return node2subexpression;
+ return resultNode2subexpression;
}
/**
@@ -1147,6 +1385,49 @@
}
}
+ private void createCallStatements(@NonNull Map<@NonNull Region, @NonNull Map<@NonNull Node, @NonNull Node>> calls) {
+ List<@NonNull MappingStatement> mappingStatements = new ArrayList<>();
+ Map<@NonNull LoopVariable, @NonNull OCLExpression> loopVariables = null;
+ for (Map.Entry<@NonNull Region, @NonNull Map<@NonNull Node, @NonNull Node>> entry : calls.entrySet()) {
+ @NonNull Region calledRegion = entry.getKey();
+ AbstractRegion2Mapping calledRegion2Mapping = visitor.getRegion2Mapping(calledRegion);
+ List<@NonNull MappingParameterBinding> mappingParameterBindings = new ArrayList<>();
+ for (Map.Entry<@NonNull Node, @NonNull Node> entry2 : entry.getValue().entrySet()) {
+ @NonNull Node sourceNode = entry2.getKey();
+ @NonNull Node targetNode = entry2.getValue();
+ OCLExpression sourceExpression = createVariableExp(sourceNode);
+ Type type = sourceExpression.getType();
+ if (type instanceof CollectionType) {
+ if (loopVariables == null) {
+ loopVariables = new HashMap<>();
+ }
+ Type elementType = ((CollectionType)type).getElementType();
+ assert elementType != null;
+ LoopVariable loopVariable = helper.createLoopVariable(getSafeName("loop" + loopVariables.size()), elementType);//, true, sourceExpression);
+ loopVariables.put(loopVariable, sourceExpression);
+ // sourceExpression = PivotUtil.createVariableExp(loopVariable);
+ VariableDeclaration guardVariable = calledRegion2Mapping.getGuardVariable(targetNode);
+ mappingParameterBindings.add(helper.createLoopParameterBinding((GuardParameter) guardVariable, loopVariable));
+ }
+ else {
+ VariableDeclaration guardVariable = calledRegion2Mapping.getGuardVariable(targetNode);
+ mappingParameterBindings.add(helper.createSimpleParameterBinding((SimpleParameter) guardVariable, sourceExpression));
+ }
+ }
+ Collections.sort(mappingParameterBindings, QVTimperativeUtil.MappingParameterBindingComparator.INSTANCE);
+ MappingStatement mappingCallStatement = calledRegion2Mapping.createMappingCall(mappingParameterBindings);
+ if (loopVariables != null) {
+ for (Map.Entry<@NonNull LoopVariable, @NonNull OCLExpression> loopEntry : loopVariables.entrySet()) {
+ @NonNull LoopVariable loopVariable = loopEntry.getKey();
+ @NonNull OCLExpression loopSource = loopEntry.getValue();
+ mappingCallStatement = helper.createMappingLoop(loopSource, loopVariable, mappingCallStatement);
+ }
+ }
+ mappingStatements.add(mappingCallStatement);
+ }
+ mapping.getOwnedStatements().addAll(mappingStatements);
+ }
+
private @NonNull CheckStatement createCheckStatement(@NonNull OCLExpression booleanExpression) {
CheckStatement checkStatement = helper.createCheckStatement(booleanExpression);
mapping.getOwnedStatements().add(checkStatement);
@@ -1266,83 +1547,46 @@
createAppendParameters();
}
- private void createMappingStatements(@NonNull Map<@NonNull Region, @NonNull Map<@NonNull Node, @NonNull Node>> calls) {
- List<@NonNull MappingStatement> mappingStatements = new ArrayList<>();
- Map<@NonNull LoopVariable, @NonNull OCLExpression> loopVariables = null;
- for (Map.Entry<@NonNull Region, @NonNull Map<@NonNull Node, @NonNull Node>> entry : calls.entrySet()) {
- @NonNull Region calledRegion = entry.getKey();
- AbstractRegion2Mapping calledRegion2Mapping = visitor.getRegion2Mapping(calledRegion);
- List<@NonNull MappingParameterBinding> mappingParameterBindings = new ArrayList<>();
- for (Map.Entry<@NonNull Node, @NonNull Node> entry2 : entry.getValue().entrySet()) {
- @NonNull Node sourceNode = entry2.getKey();
- @NonNull Node targetNode = entry2.getValue();
- OCLExpression sourceExpression = createVariableExp(sourceNode);
- Type type = sourceExpression.getType();
- if (type instanceof CollectionType) {
- if (loopVariables == null) {
- loopVariables = new HashMap<>();
+ private void createObservedProperties() {
+ Set<@NonNull Property> allCheckedProperties2 = allCheckedProperties;
+ if (allCheckedProperties2 != null) {
+ //
+ // Ideally we could install each observed property as it is actually used. But
+ // this needs to be coded in many places.
+ //
+ for (Statement asStatement : mapping.getOwnedStatements()) {
+ if (asStatement instanceof ObservableStatement) {
+ List<Property> observedProperties = ((ObservableStatement)asStatement).getObservedProperties();
+ for (EObject eObject : new TreeIterable(asStatement, false)) {
+ if (eObject instanceof PropertyCallExp) {
+ Property property = PivotUtil.getReferredProperty((PropertyCallExp) eObject);
+ if (allCheckedProperties2.contains(property) && !observedProperties.contains(property)) {
+ observedProperties.add(property);
+ }
+ }
+ else if (eObject instanceof OppositePropertyCallExp) {
+ Property property = PivotUtil.getReferredProperty((NavigationCallExp) eObject).getOpposite();
+ if (allCheckedProperties2.contains(property) && !observedProperties.contains(property)) {
+ observedProperties.add(property);
+ }
+ }
}
- Type elementType = ((CollectionType)type).getElementType();
- assert elementType != null;
- LoopVariable loopVariable = helper.createLoopVariable(getSafeName("loop" + loopVariables.size()), elementType);//, true, sourceExpression);
- loopVariables.put(loopVariable, sourceExpression);
- // sourceExpression = PivotUtil.createVariableExp(loopVariable);
- VariableDeclaration guardVariable = calledRegion2Mapping.getGuardVariable(targetNode);
- mappingParameterBindings.add(helper.createLoopParameterBinding((GuardParameter) guardVariable, loopVariable));
- }
- else {
- VariableDeclaration guardVariable = calledRegion2Mapping.getGuardVariable(targetNode);
- mappingParameterBindings.add(helper.createSimpleParameterBinding((SimpleParameter) guardVariable, sourceExpression));
}
}
- Collections.sort(mappingParameterBindings, QVTimperativeUtil.MappingParameterBindingComparator.INSTANCE);
- MappingStatement mappingCallStatement = calledRegion2Mapping.createMappingCall(mappingParameterBindings);
- if (loopVariables != null) {
- for (Map.Entry<@NonNull LoopVariable, @NonNull OCLExpression> loopEntry : loopVariables.entrySet()) {
- @NonNull LoopVariable loopVariable = loopEntry.getKey();
- @NonNull OCLExpression loopSource = loopEntry.getValue();
- mappingCallStatement = helper.createMappingLoop(loopSource, loopVariable, mappingCallStatement);
- }
- }
- mappingStatements.add(mappingCallStatement);
}
- mapping.getOwnedStatements().addAll(mappingStatements);
}
/**
- * Recurse over the guard nodes and loaded/predicates region and convert the non-guard nodes to unrealized variables
- * and edges to predicates or initializations
+ * Determine a traversal order for the old edges then synthesize the patttern matching statements.
*/
- private @NonNull OldEdgeSchedule createNavigablePredicates() {
- OldEdgeSchedule oldSchedule = new OldEdgeSchedule(region);
- oldSchedule.analyze();
- oldSchedule.synthesize();
- return oldSchedule;
- }
-
- private void createObservedProperties(@NonNull Set<@NonNull Property> allCheckedProperties) {
- //
- // Ideally we could install each observed property as it is actually used. But
- // this needs to be coded in many places.
- //
- for (Statement asStatement : mapping.getOwnedStatements()) {
- if (asStatement instanceof ObservableStatement) {
- List<Property> observedProperties = ((ObservableStatement)asStatement).getObservedProperties();
- for (EObject eObject : new TreeIterable(asStatement, false)) {
- if (eObject instanceof PropertyCallExp) {
- Property property = PivotUtil.getReferredProperty((PropertyCallExp) eObject);
- if (allCheckedProperties.contains(property) && !observedProperties.contains(property)) {
- observedProperties.add(property);
- }
- }
- else if (eObject instanceof OppositePropertyCallExp) {
- Property property = PivotUtil.getReferredProperty((NavigationCallExp) eObject).getOpposite();
- if (allCheckedProperties.contains(property) && !observedProperties.contains(property)) {
- observedProperties.add(property);
- }
- }
- }
- }
+ private void createPatternMatch() {
+ if (mapping.isIsAbstract()) {
+ mapping.getOwnedStatements().add(createCheckStatement(helper.createBooleanLiteralExp(false))); // FIXME add a message
+ }
+ else {
+ OldEdgeSchedule oldSchedule = new OldEdgeSchedule();
+ oldSchedule.analyze();
+ oldSchedule.synthesize();
}
}
@@ -1452,39 +1696,6 @@
}
}
- @Override
- public void createSchedulingStatements() {
- Map<@NonNull Region, @NonNull Map<@NonNull Node, @NonNull Node>> calls = null;
- for (@NonNull Region calledRegion : region.getCallableChildren()) {
- if (calls == null) {
- calls = new HashMap<>();
- }
- Map<@NonNull Node, @NonNull Node> source2target = calls.get(calledRegion);
- if (source2target == null) {
- source2target = new HashMap<>();
- calls.put(calledRegion, source2target);
- }
- AbstractRegion2Mapping calledRegion2Mapping = visitor.getRegion2Mapping(calledRegion);
- for (@NonNull Node calledGuardNode : calledRegion2Mapping.getGuardNodes()) {
- for (@NonNull Node callingNode : calledGuardNode.getPassedBindingSources()) {
- if (callingNode.getOwningRegion() == region) {
- Node oldNode = source2target.put(callingNode, calledGuardNode);
- assert oldNode == null;
- }
- }
- for (@NonNull Node callingNode : calledGuardNode.getUsedBindingSources()) {
- if (callingNode.getOwningRegion() == region) {
- Node oldNode = source2target.put(callingNode, calledGuardNode);
- assert oldNode == null;
- }
- }
- }
- }
- if (calls != null) {
- createMappingStatements(calls);
- }
- }
-
private @NonNull OCLExpression createVariableExp(@NonNull Node node) {
if (node.isExplicitNull()) {
return helper.createNullLiteralExp();
@@ -1507,6 +1718,24 @@
return (MappingParameter) variable;
}
+ public @NonNull Set<@NonNull Node> getPrecedingNodes(@NonNull Node targetNode) {
+ Map<@NonNull Node, @NonNull Set<@NonNull Node>> node2precedingNodeClosure2 = node2precedingNodeClosure;
+ if (node2precedingNodeClosure2 == null) {
+ node2precedingNodeClosure = node2precedingNodeClosure2 = new HashMap<>();
+ }
+ Set<@NonNull Node> precedingNodes = node2precedingNodeClosure2.get(targetNode);
+ if (precedingNodes == null) {
+ precedingNodes = new HashSet<>();
+ node2precedingNodeClosure2.put(targetNode, precedingNodes);
+ precedingNodes.add(targetNode);
+ for (@NonNull Node sourceNode : reachabilityForest.getPredecessors(targetNode)) {
+ precedingNodes.addAll(getPrecedingNodes(sourceNode));
+ }
+ }
+ assert precedingNodes.size() > 0;
+ return precedingNodes;
+ }
+
private @NonNull VariableDeclaration getSubexpressionDeclaration(@NonNull Node node) {
VariableDeclaration variable = node2variable.get(node);
if (variable == null) {
@@ -1535,6 +1764,15 @@
return variable; */
}
+ private boolean isHazardousWrite(@NonNull NavigableEdge edge) {
+ Node sourceNode = edge.getEdgeSource();
+ Property asProperty = edge.getProperty();
+ TypedModel typedModel = RegionUtil.getTypedModel(RegionUtil.getClassDatumAnalysis(sourceNode));
+ RegionAnalysis regionAnalysis = RegionAnalysis.get(region);
+ Iterable<@NonNull NavigableEdge> enforcedEdges = regionAnalysis.getEnforcedEdges(typedModel, asProperty);
+ return enforcedEdges != null;
+ }
+
private boolean isIfExp(@NonNull Node node) {
if (node.isExpression()) {
for (TypedElement typedElement : node.getTypedElements()) {
@@ -1545,24 +1783,6 @@
}
return false;
}
-
- private boolean isHazardousWrite(@NonNull NavigableEdge edge) {
- Node sourceNode = edge.getEdgeSource();
- Property asProperty = edge.getProperty();
- TypedModel typedModel = RegionUtil.getTypedModel(RegionUtil.getClassDatumAnalysis(sourceNode));
- Iterable<@NonNull NavigableEdge> enforcedEdges = RegionAnalysis.get(region).getEnforcedEdges(typedModel);
- if (enforcedEdges != null) {
- Property asOppositeProperty = asProperty.getOpposite();
- for (@NonNull NavigableEdge enforcedEdge : enforcedEdges) {
- Property edgeProperty = enforcedEdge.getProperty();
- if ((edgeProperty == asProperty) || (edgeProperty == asOppositeProperty)) {
- return true;
- }
- }
- }
- return false;
- }
-
@Override
public boolean isInfinite() {
if (region.getRecursionEdges().iterator().hasNext()) { // FIXME unduly pessimistic
@@ -1613,6 +1833,53 @@
}
@Override
+ public void synthesizeCallStatements() {
+ Map<@NonNull Region, @NonNull Map<@NonNull Node, @NonNull Node>> calls = null;
+ for (@NonNull Region calledRegion : region.getCallableChildren()) {
+ if (calls == null) {
+ calls = new HashMap<>();
+ }
+ Map<@NonNull Node, @NonNull Node> source2target = calls.get(calledRegion);
+ if (source2target == null) {
+ source2target = new HashMap<>();
+ calls.put(calledRegion, source2target);
+ }
+ AbstractRegion2Mapping calledRegion2Mapping = visitor.getRegion2Mapping(calledRegion);
+ for (@NonNull Node calledGuardNode : calledRegion2Mapping.getGuardNodes()) {
+ for (@NonNull Node callingNode : calledGuardNode.getPassedBindingSources()) {
+ if (callingNode.getOwningRegion() == region) {
+ Node oldNode = source2target.put(callingNode, calledGuardNode);
+ assert oldNode == null;
+ }
+ }
+ for (@NonNull Node callingNode : calledGuardNode.getUsedBindingSources()) {
+ if (callingNode.getOwningRegion() == region) {
+ Node oldNode = source2target.put(callingNode, calledGuardNode);
+ assert oldNode == null;
+ }
+ }
+ }
+ }
+ if (calls != null) {
+ createCallStatements(calls);
+ }
+ }
+
+ /**
+ * Create all the statements that support the local content of this micromapping.
+ */
+ @Override
+ public void synthesizeLocalStatements() {
+ createHeadAndGuardNodeVariables(); // BLUE/CYAN guard/append nodes
+ createPatternMatch(); // BLUE/CYAN nodes and edges
+ createRealizedVariables(); // GREEN nodes
+ createPropertyAssignments(); // GREEN edges
+ createAddStatements(); // export to append nodes
+ createRealizedIncludesAssignments();
+ createObservedProperties(); // wrap observable clauses around hazardous accesses
+ }
+
+ @Override
public @NonNull String toString() {
return String.valueOf(region);
}
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/NavigationEdgeSorter.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/NavigationEdgeSorter.java
index 6447df8..e03429a 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/NavigationEdgeSorter.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/NavigationEdgeSorter.java
@@ -29,8 +29,8 @@
import com.google.common.collect.Lists;
/**
- * NavigationEdgeSorter supports ordering of a list of NavigationEdges into least-dependent first order thereby ensuring
- * that unrealized variables are created and initualized for accessed realized attribute nodes before they are used.
+ * NavigationEdgeSorter supports ordering of a list of NavigationEdges into least-dependent first order thereby ensuring
+ * that unrealized variables are created and initialized for accessed realized attribute nodes before they are used.
*/
public class NavigationEdgeSorter
{
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/NavigationForestBuilder.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/NavigationForestBuilder.java
deleted file mode 100644
index 1db77cc..0000000
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/NavigationForestBuilder.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2015, 2017 Willink Transformations and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * E.D.Willink - Initial API and implementation
- *******************************************************************************/
-package org.eclipse.qvtd.compiler.internal.qvts2qvti;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.ocl.pivot.utilities.NameUtil;
-import org.eclipse.qvtd.compiler.internal.qvtm2qvts.RegionUtil;
-import org.eclipse.qvtd.pivot.qvtschedule.Edge;
-import org.eclipse.qvtd.pivot.qvtschedule.NavigableEdge;
-import org.eclipse.qvtd.pivot.qvtschedule.Node;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
-/**
- * A NavigationForestBuilder selects a forest of navigable edges to reach each navigable node from its heads.
- */
-class NavigationForestBuilder implements Comparator<@NonNull Edge>
-{
- /**
- * The preferred non-secondary edges to be used in the tree.
- */
- private final @NonNull Set<@NonNull Edge> forwardEdges = new HashSet<>();
-
- /**
- * Edges that have no opposite.
- */
- private final @NonNull Set<@NonNull NavigableEdge> manyToOneEdges = new HashSet<>();
-
- /**
- * The edges that are traversed while locating each node and their depth in the traversal forest, 0 at edge sourced by root.
- */
- private final @NonNull Map<@NonNull Edge, @NonNull Integer> traversedEdge2depth = new HashMap<>();
-
- /**
- * The nodes that are traversed while locating each node and their depth in the traversal forest, 0 at root.
- */
- // private final @NonNull Map<@NonNull Node, @NonNull Integer> traversedNode2depth = new HashMap<>();
-
- /**
- * The incoming edge for each node in the traversal forest, null at a root.
- */
- private final @NonNull Map<@NonNull Node, @Nullable Edge> traversedNode2incomingEdge = new HashMap<>();
-
- /**
- * The nodes that form the traversal forest.
- */
- // private final @NonNull Set<@NonNull Node> navigableNodes = new HashSet<>();
-
- /**
- * The other edges that are traversed while traversing a primary operation input.
- */
- private final @NonNull Set<@NonNull Edge> otherTraversedEdges = new HashSet<>();
-
- /**
- * The edges that are not traversed while locating each node.
- */
- private final @NonNull Set<@NonNull Edge> untraversedEdges = new HashSet<>();
-
- public NavigationForestBuilder(@NonNull Iterable<@NonNull Node> rootNodes, @NonNull Iterable<@NonNull Edge> edges) {
- for (@NonNull Node rootNode : rootNodes) {
- traversedNode2incomingEdge.put(rootNode, null);
- }
- for (@NonNull Edge edge : edges) {
- addEdge(edge);
- }
- //
- // Analyze the descendants of the roots to identify the most simply navigated forest.
- //
- analyze(traversedNode2incomingEdge.keySet());
- //
- // Traverse the attributes too.
- //
- /* for (@NonNull NavigationEdge edge : attributeEdges) {
- Node targetNode = edge.getTarget();
- if (!traversedNode2incomingEdge.containsKey(targetNode)) {
- traversedNode2incomingEdge.put(targetNode, edge);
- }
- else {
- untraversedEdges.add(edge);
- }
- } */
- //
- // Identify the remaining untraversed edges that are not used to reach nodes and so must be reified as predicates to check nodes.
- //
- Set<@NonNull Edge> predicateEdges = Sets.newHashSet(forwardEdges);
- for (@NonNull Edge traversedEdge : getTraversedEdges()) {
- predicateEdges.remove(traversedEdge);
- if (traversedEdge.isNavigation()) {
- NavigableEdge oppositeEdge = ((NavigableEdge)traversedEdge).getOppositeEdge();
- if (oppositeEdge != null) {
- predicateEdges.remove(oppositeEdge);
- }
- }
- }
- predicateEdges.removeAll(otherTraversedEdges);
- untraversedEdges.addAll(predicateEdges);
- }
-
- /**
- * Add a possible edge and end nodes categorizing a navigable edge as forward if it is a sole or primary edge,
- * reverse if it is a secondary edge.
- */
- protected void addEdge(@NonNull Edge edge) {
- if (edge.isRealized()) {}
- else if (edge.isCast()) {}
- else {
- // assert !edge.isExpression();
- // assert !edge.isComputation();
- Node targetNode = edge.getEdgeTarget();
- targetNode = RegionUtil.getCastTarget(targetNode);
- if (!edge.isSecondary()) {
- forwardEdges.add(edge);
- if ((edge.getEdgeSource().isClass()) && (edge instanceof NavigableEdge)) {
- NavigableEdge navigableEdge = (NavigableEdge)edge;
- if (navigableEdge.getOppositeEdge() == null) {
- manyToOneEdges.add((NavigableEdge)edge);
- }
- }
- }
- }
- }
-
- /**
- * Identify the forest from the given roots.
- */
- public void analyze(@NonNull Iterable<@NonNull Node> rootNodes) {
- Set<@NonNull Node> moreNodes = Sets.newHashSet(rootNodes);
- //
- // Advance breadth first, one depth at a time, accumulating all edges that make one stage of progress.
- //
- for (int depth = 0; moreNodes.size() > 0; depth++) {
- //
- // Select the forward edges that make progress.
- //
- Set<@NonNull Node> moreMoreNodes = new HashSet<>();
- for (@NonNull Node sourceNode : moreNodes) {
- // traversedNode2depth.put(sourceNode, depth);
- for (@NonNull Edge forwardEdge : RegionUtil.getOutgoingEdges(sourceNode)) {
- if (forwardEdges.contains(forwardEdge)) {
- Node targetNode = RegionUtil.getCastTarget(forwardEdge.getEdgeTarget());
- if (forwardEdge.isNavigation()) {
- if (!traversedNode2incomingEdge.containsKey(targetNode)) {
- traversedNode2incomingEdge.put(targetNode, forwardEdge);
- moreMoreNodes.add(targetNode);
- traversedEdge2depth.put(forwardEdge, depth);
- }
- }
- else if (forwardEdge.isExpression()) {
- boolean allReady = true;
- for (@NonNull Edge incomingEdge : RegionUtil.getIncomingEdges(targetNode)) {
- if (incomingEdge.isExpression()) {
- Node expressionSourceNode = incomingEdge.getSourceNode();
- if (!traversedNode2incomingEdge.containsKey(expressionSourceNode)) {
- allReady = false;
- break;
- }
- }
- }
- if (allReady) {
- for (@NonNull Edge incomingEdge : RegionUtil.getIncomingEdges(targetNode)) {
- if (incomingEdge.isExpression()) {
- traversedEdge2depth.put(incomingEdge, depth);
- if (incomingEdge != forwardEdge) {
- otherTraversedEdges.add(incomingEdge);
- }
- }
- }
- traversedNode2incomingEdge.put(targetNode, forwardEdge);
- if (!targetNode.getOutgoingEdges().isEmpty()) {
- moreMoreNodes.add(targetNode);
- }
- }
- }
- else {
- assert false;
- }
- }
- }
- }
- if (moreMoreNodes.isEmpty()) {
- //
- // If no forward edge makes progress, select just one backward edge instead.
- //
- for (@NonNull Edge forwardEdge : forwardEdges) { // FIXME maintain reducing list of possibles
- if (forwardEdge instanceof NavigableEdge) {
- NavigableEdge navigableForwardEdge = (NavigableEdge)forwardEdge;
- @Nullable NavigableEdge backwardEdge = navigableForwardEdge.getOppositeEdge();
- if (backwardEdge != null) {
- Node sourceNode = backwardEdge.getEdgeSource();
- if (traversedNode2incomingEdge.containsKey(sourceNode)) {
- Node targetNode = backwardEdge.getEdgeTarget();
- if (!traversedNode2incomingEdge.containsKey(targetNode)) {
- traversedNode2incomingEdge.put(targetNode, backwardEdge);
- moreMoreNodes.add(targetNode);
- traversedEdge2depth.put(backwardEdge, depth);
- break;
- }
- }
- }
- }
- }
- if (moreMoreNodes.isEmpty()) {
- //
- // If no backward edge makes progress, select just one inverse edge instead.
- //
- for (@NonNull NavigableEdge manyToOneEdge : manyToOneEdges) { // FIXME maintain reducing list of possibles
- Node sourceNode = manyToOneEdge.getEdgeTarget();
- if (traversedNode2incomingEdge.containsKey(sourceNode)) {
- Node targetNode = manyToOneEdge.getEdgeSource();
- if (!traversedNode2incomingEdge.containsKey(targetNode)) {
- traversedNode2incomingEdge.put(targetNode, manyToOneEdge);
- moreMoreNodes.add(targetNode);
- traversedEdge2depth.put(manyToOneEdge, depth);
- break;
- }
- }
- }
- }
- }
- moreNodes = moreMoreNodes;
- }
- }
-
- /**
- * Select the shallowest first then alphabetical ordering of the traversal edges of the forest.
- *
- * FIXME prioritize fallible predicate paths
- */
- @Override
- public int compare(@NonNull Edge o1, @NonNull Edge o2) {
- Integer d1 = traversedEdge2depth.get(o1);
- Integer d2 = traversedEdge2depth.get(o2);
- assert (d1 != null) && (d2 != null);
- if (d1 != d2) {
- return d1 - d2;
- }
- String n1 = o1.getDisplayName();
- String n2 = o2.getDisplayName();
- return n1.compareTo(n2);
- }
-
- /**
- * Return a shallowest first then alphabetical ordering of the traversal edges of the forest.
- */
- public @NonNull List<@NonNull Edge> getForestNavigations() {
- List<@NonNull Edge> forestNavigations = Lists.newArrayList(getTraversedEdges());
- forestNavigations.removeAll(otherTraversedEdges);
- Collections.sort(forestNavigations, this);
- return forestNavigations;
- }
-
- /**
- * Return an alphabetical ordering of the residual edges to be checked as predicates.
- */
- public @NonNull List<@NonNull Edge> getGraphPredicates() {
- List<@NonNull Edge> graphPredicateEdges = new ArrayList<>();
- for (@NonNull Edge untraversedEdge : untraversedEdges) {
- graphPredicateEdges.add(untraversedEdge);
- }
- Collections.sort(graphPredicateEdges, NameUtil.NAMEABLE_COMPARATOR);
- return graphPredicateEdges;
- }
-
- protected @NonNull Iterable<@NonNull Edge> getTraversedEdges() {
- return traversedEdge2depth.keySet();
- }
-}
\ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/QVTs2QVTiVisitor.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/QVTs2QVTiVisitor.java
index 79ada5e..0dbe92a 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/QVTs2QVTiVisitor.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/QVTs2QVTiVisitor.java
@@ -194,6 +194,7 @@
else {
region2mapping = new BasicRegion2Mapping(this, region);
}
+ region2mapping.synthesizeLocalStatements();
region2region2mapping.put(region, region2mapping);
qvtiTransformation.getRule().add(region2mapping.getMapping());
region.accept(this);
@@ -456,7 +457,7 @@
for (@NonNull Region region : sortedRegions) {
// if (!region.isConnectionRegion()) {
AbstractRegion2Mapping region2Mapping = getRegion2Mapping(region);
- region2Mapping.createSchedulingStatements();
+ region2Mapping.synthesizeCallStatements();
// }
}
// Mappings are in schedule index order.
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/RootRegion2Mapping.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/RootRegion2Mapping.java
index 753adc7..57386b8 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/RootRegion2Mapping.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/RootRegion2Mapping.java
@@ -66,21 +66,6 @@
public RootRegion2Mapping(@NonNull QVTs2QVTiVisitor visitor, @NonNull LoadingRegion region) {
super(visitor, region);
- //
- // Create domains
- //
- Set<@NonNull ImperativeTypedModel> checkableTypedModels = new HashSet<>();
- for (@NonNull Node node : RegionUtil.getOwnedNodes(region)) {
- ClassDatum classDatum = node.getClassDatum();
- org.eclipse.ocl.pivot.Class type = classDatum.getCompleteClass().getPrimaryClass();
- if (!(type instanceof DataType) && !(type instanceof AnyType) && !(type instanceof VoidType) && !(type instanceof InvalidType)) {
- TypedModel qvtmTypedModel = classDatum.getReferredTypedModel();
- ImperativeTypedModel qvtiTypedModel = visitor.getQVTiTypedModel(qvtmTypedModel);
- if (qvtiTypedModel != null) {
- checkableTypedModels.add(qvtiTypedModel);
- }
- }
- }
}
private @NonNull ConnectionVariable createRootConnectionVariable(@NonNull String name, boolean isStrict, @NonNull Type type, @Nullable OCLExpression initExpression) {
@@ -123,78 +108,6 @@
}
@Override
- public void createSchedulingStatements() {
- // BottomPattern bottomPattern = mapping.getBottomPattern();
- createRootConnectionVariables();
- /* //
- // Cache the children in a middle bottom pattern variable.
- //
- OCLExpression parentExpression = createVariableExp(headNode);
- OCLExpression initExpression = PivotUtil.createNavigationCallExp(parentExpression, property);
- Type asType = initExpression.getType();
- if (!(asType instanceof CollectionType)) {
- initExpression = createOclAsSetCallExp(initExpression);
- asType = initExpression.getType();
- }
- assert asType != null;
- Variable childrenVariable = PivotUtil.createVariable(getSafeName("allChildren"), asType, true, initExpression);
- mapping.getBottomPattern().getVariable().add(childrenVariable); */
- //
- // Create union assignments for connections.
- //
- // if (connection2variable != null) {
- /* Operation asOperation = NameUtil.getNameable(visitor.getStandardLibrary().getCollectionType().getOwnedOperations(), "union");
- for (Node node : region.getNodes()) {
- if (node.isComposed()) {
- for (InterRegionEdge edge : node.getOutgoingPassedBindingEdges()) {
- Node connectionNode = edge.getTarget();
- Region connectionRegion = connectionNode.getRegion();
- Variable connectionVariable = connection2variable.get(connectionRegion);
- if (connectionVariable != null) {
- OCLExpression collection1 = helper.createVariableExp(connectionVariable);
- OCLExpression collection2 = createSelectByKind(node);
- OCLExpression union = createOperationCallExp(collection1, asOperation, collection2);
- VariableAssignment variableAssignment = QVTcoreBaseFactory.eINSTANCE.createVariableAssignment();
- variableAssignment.setTargetVariable(connectionVariable);
- variableAssignment.setValue(union);
- bottomPattern.getAssignment().add(variableAssignment);
- }
- }
- }
- }
- bottomPattern.getVariable().addAll(connection2variable.values());
-// } */
- /* ChainNode chain = visitor.getChain(region);
- for (ChainNode child : chain.getChildren()) {
- Region calledRegion = child.getRegion();
- if (!calledRegion.isConnectionRegion()) {
- mappingStatement = createCalls(mappingStatement, calledRegion);
- }
- else if (Iterables.isEmpty(child.getChildren())) {
-// mappingStatement = createCalls(mappingStatement, calledRegion);
- }
- else {
- assert calledRegion.isConnectionRegion();
- assert !Iterables.isEmpty(child.getChildren());
- for (ChainNode connectionChild : child.getChildren()) {
- Region connectionedRegion = connectionChild.getRegion();
- assert !connectionedRegion.isConnectionRegion();
- mappingStatement = createCalls(mappingStatement, connectionedRegion);
- }
- }
- } */
- List<Statement> ownedStatements = mapping.getOwnedStatements();
- for (@NonNull Region callableRegion : region.getCallableChildren()) {
- if (isInstall(callableRegion)) {
- ownedStatements.add(createInstall(callableRegion));
- }
- else {
- ownedStatements.add(createCall(callableRegion, null));
- }
- }
- }
-
- @Override
protected @NonNull OCLExpression createSelectByKind(@NonNull Node resultNode) {
throw new UnsupportedOperationException();
/*refactored code inlined at call point -- ?? ok, but needs usage analysis
@@ -267,4 +180,95 @@
}
return true;
}
+
+ @Override
+ public void synthesizeCallStatements() {
+ // BottomPattern bottomPattern = mapping.getBottomPattern();
+ createRootConnectionVariables();
+ /* //
+ // Cache the children in a middle bottom pattern variable.
+ //
+ OCLExpression parentExpression = createVariableExp(headNode);
+ OCLExpression initExpression = PivotUtil.createNavigationCallExp(parentExpression, property);
+ Type asType = initExpression.getType();
+ if (!(asType instanceof CollectionType)) {
+ initExpression = createOclAsSetCallExp(initExpression);
+ asType = initExpression.getType();
+ }
+ assert asType != null;
+ Variable childrenVariable = PivotUtil.createVariable(getSafeName("allChildren"), asType, true, initExpression);
+ mapping.getBottomPattern().getVariable().add(childrenVariable); */
+ //
+ // Create union assignments for connections.
+ //
+ // if (connection2variable != null) {
+ /* Operation asOperation = NameUtil.getNameable(visitor.getStandardLibrary().getCollectionType().getOwnedOperations(), "union");
+ for (Node node : region.getNodes()) {
+ if (node.isComposed()) {
+ for (InterRegionEdge edge : node.getOutgoingPassedBindingEdges()) {
+ Node connectionNode = edge.getTarget();
+ Region connectionRegion = connectionNode.getRegion();
+ Variable connectionVariable = connection2variable.get(connectionRegion);
+ if (connectionVariable != null) {
+ OCLExpression collection1 = helper.createVariableExp(connectionVariable);
+ OCLExpression collection2 = createSelectByKind(node);
+ OCLExpression union = createOperationCallExp(collection1, asOperation, collection2);
+ VariableAssignment variableAssignment = QVTcoreBaseFactory.eINSTANCE.createVariableAssignment();
+ variableAssignment.setTargetVariable(connectionVariable);
+ variableAssignment.setValue(union);
+ bottomPattern.getAssignment().add(variableAssignment);
+ }
+ }
+ }
+ }
+ bottomPattern.getVariable().addAll(connection2variable.values());
+ // } */
+ /* ChainNode chain = visitor.getChain(region);
+ for (ChainNode child : chain.getChildren()) {
+ Region calledRegion = child.getRegion();
+ if (!calledRegion.isConnectionRegion()) {
+ mappingStatement = createCalls(mappingStatement, calledRegion);
+ }
+ else if (Iterables.isEmpty(child.getChildren())) {
+ // mappingStatement = createCalls(mappingStatement, calledRegion);
+ }
+ else {
+ assert calledRegion.isConnectionRegion();
+ assert !Iterables.isEmpty(child.getChildren());
+ for (ChainNode connectionChild : child.getChildren()) {
+ Region connectionedRegion = connectionChild.getRegion();
+ assert !connectionedRegion.isConnectionRegion();
+ mappingStatement = createCalls(mappingStatement, connectionedRegion);
+ }
+ }
+ } */
+ List<Statement> ownedStatements = mapping.getOwnedStatements();
+ for (@NonNull Region callableRegion : region.getCallableChildren()) {
+ if (isInstall(callableRegion)) {
+ ownedStatements.add(createInstall(callableRegion));
+ }
+ else {
+ ownedStatements.add(createCall(callableRegion, null));
+ }
+ }
+ }
+
+ @Override
+ public void synthesizeLocalStatements() {
+ //
+ // Create domains
+ //
+ Set<@NonNull ImperativeTypedModel> checkableTypedModels = new HashSet<>();
+ for (@NonNull Node node : RegionUtil.getOwnedNodes(region)) {
+ ClassDatum classDatum = node.getClassDatum();
+ org.eclipse.ocl.pivot.Class type = classDatum.getCompleteClass().getPrimaryClass();
+ if (!(type instanceof DataType) && !(type instanceof AnyType) && !(type instanceof VoidType) && !(type instanceof InvalidType)) {
+ TypedModel qvtmTypedModel = classDatum.getReferredTypedModel();
+ ImperativeTypedModel qvtiTypedModel = visitor.getQVTiTypedModel(qvtmTypedModel);
+ if (qvtiTypedModel != null) {
+ checkableTypedModels.add(qvtiTypedModel);
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/utilities/ReachabilityForest.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/utilities/ReachabilityForest.java
new file mode 100644
index 0000000..fbaa3dc
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/utilities/ReachabilityForest.java
@@ -0,0 +1,320 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2017 Willink Transformations and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * E.D.Willink - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.qvtd.compiler.internal.qvts2qvts.utilities;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.ocl.pivot.utilities.ClassUtil;
+import org.eclipse.qvtd.compiler.internal.qvtm2qvts.RegionUtil;
+import org.eclipse.qvtd.pivot.qvtschedule.Edge;
+import org.eclipse.qvtd.pivot.qvtschedule.NavigableEdge;
+import org.eclipse.qvtd.pivot.qvtschedule.Node;
+import com.google.common.collect.Lists;
+
+/**
+ * The ReachabilityForest comprises a tree of preferred edge paths from each head/leaf-constant root node to the
+ * nodes that can be reached from them. It answers the question: what preceding nodes are necessary to ensure
+ * that a given node is reachable.
+ */
+public class ReachabilityForest
+{
+ private static final int FORWARD_NAVIGATION_COST = 1;
+ private static final int INVERSE_MANY_NAVIGATION_COST = 5;
+ private static final int INVERSE_NAVIGATION_COST = 3;
+ private static final int ITERATOR_COST = 1;
+ private static final int OPERATION_COST = 10;
+ private static final int RESULT_COST = 1;
+
+ /**
+ * The preferred non-secondary edges to be used in the tree.
+ */
+ private final @NonNull Set<@NonNull NavigableEdge> forwardEdges = new HashSet<>();
+
+ /**
+ * Edges that have no opposite.
+ */
+ private final @NonNull Set<@NonNull Edge> manyToOneEdges = new HashSet<>();
+
+ /**
+ * The incoming edge for each node in the traversal forest, null at a root.
+ */
+ private final @NonNull Map<@NonNull Node, @Nullable Edge> node2reachingEdge = new HashMap<>();
+
+ /**
+ * The cost for each node from the root in the traversal forest, 0 at a root. The cost is equal
+ * to the sum of the costs of each edge on the lowest cost path from the root.
+ */
+ private @NonNull Map<@NonNull Node, @NonNull Integer> node2cost = new HashMap<>();
+
+ /**
+ * Lazily created compartor to order nodes lowest cost first.
+ */
+ private @Nullable Comparator<@NonNull Edge> edgeCostComparator = null;
+
+ /**
+ * Lazily created compartor to order nodes lowest cost first.
+ */
+ private @Nullable Comparator<@NonNull Node> nodeCostComparator = null;
+
+
+ /**
+ * Construct the Reachability forest for the specified rootNodes using the availableNavigableEdges to locate
+ * paths to further nodes and also any old computation edges.
+ */
+ public ReachabilityForest(@NonNull Iterable<@NonNull Node> rootNodes, @NonNull Iterable<@NonNull NavigableEdge> availableNavigableEdges) {
+ for (@NonNull Node rootNode : rootNodes) {
+ node2reachingEdge.put(rootNode, null);
+ }
+ for (@NonNull NavigableEdge edge : availableNavigableEdges) {
+ addEdge(edge);
+ }
+ //
+ // Analyze the descendants of the roots to identify the most simply navigated forest.
+ //
+ analyze(node2reachingEdge.keySet());
+ }
+
+ protected void addEdge(@NonNull NavigableEdge edge) {
+ if (!edge.isSecondary()) {
+ forwardEdges.add(edge);
+ if ((edge.getEdgeSource().isClass()) && (edge.getOppositeEdge() == null)) {
+ manyToOneEdges.add(edge);
+ }
+ }
+ }
+
+ /**
+ * Identify the forest from the given roots.
+ */
+ protected void analyze(@NonNull Iterable<@NonNull Node> rootNodes) {
+ List<@Nullable List<@NonNull Node>> costs2nodes = new ArrayList<>();
+ for (@NonNull Node rootNode : rootNodes) {
+ node2cost.put(rootNode, 0);
+ }
+ costs2nodes.add(Lists.newArrayList(rootNodes));
+ //
+ // Advance breadth first, one cost at a time, accumulating all edges that make one stage of progress.
+ //
+ for (int thisCost = 0; thisCost < costs2nodes.size(); thisCost++) {
+ List<@NonNull Node> thisCostNodes = costs2nodes.get(thisCost);
+ if (thisCostNodes != null) {
+ //
+ // Add the forward edges that make progress to moreMoreNodes and remember the
+ // backward and inverse edges in case forward egdes alone are inadequate.
+ //
+ for (@NonNull Node sourceNode : thisCostNodes) {
+ assert node2cost.get(sourceNode) == thisCost;
+ for (@NonNull Edge edge : RegionUtil.getOutgoingEdges(sourceNode)) {
+ Node targetNode = edge.getEdgeTarget();
+ if (!node2reachingEdge.containsKey(targetNode)) {
+ Integer targetCost = node2cost.get(targetNode);
+ assert (targetCost == null) || (thisCost < targetCost);
+ if (edge.isNavigation()) {
+ NavigableEdge navigableEdge = (NavigableEdge) edge;
+ if (forwardEdges.contains(navigableEdge)) {
+ int nextCost = thisCost + FORWARD_NAVIGATION_COST;
+ if ((targetCost == null) || (nextCost < targetCost)) {
+ node2cost.put(targetNode, nextCost);
+ node2reachingEdge.put(targetNode, edge);
+ putCosts(costs2nodes, nextCost, targetNode);
+ }
+ }
+ else {
+ NavigableEdge oppositeEdge = navigableEdge.getOppositeEdge();
+ if (forwardEdges.contains(oppositeEdge)) {
+ int nextCost = thisCost + INVERSE_NAVIGATION_COST;
+ if ((targetCost == null) || (nextCost < targetCost)) {
+ node2cost.put(targetNode, nextCost);
+ node2reachingEdge.put(targetNode, oppositeEdge);
+ putCosts(costs2nodes, nextCost, targetNode);
+ }
+ }
+ else if (manyToOneEdges.contains(oppositeEdge)) {
+ int nextCost = thisCost + INVERSE_MANY_NAVIGATION_COST;
+ if ((targetCost == null) || (nextCost < targetCost)) {
+ node2cost.put(targetNode, nextCost);
+ node2reachingEdge.put(targetNode, oppositeEdge);
+ putCosts(costs2nodes, nextCost, targetNode);
+ }
+ }
+ }
+ }
+ else if (edge.isComputation() /*&& edge.isUnconditional()*/) {
+ int nextCost = thisCost + OPERATION_COST;
+ if (targetNode.isOperation()) {
+ for (@NonNull Edge incomingEdge : RegionUtil.getIncomingEdges(targetNode)) {
+ if (incomingEdge.isOld() && incomingEdge.isComputation()) {
+ Node node = RegionUtil.getSourceNode(incomingEdge);
+ Integer cost = node2cost.get(node);
+ if ((cost == null) || (cost > thisCost)) {
+ nextCost = -1;
+ break;
+ }
+ }
+ }
+ }
+ else if (targetNode.isIterator()) {
+ nextCost = thisCost + ITERATOR_COST;
+ }
+ else {
+ nextCost = thisCost + RESULT_COST;
+ }
+ if (nextCost > 0) {
+ if ((targetCost == null) || (nextCost < targetCost)) {
+ node2cost.put(targetNode, nextCost);
+ node2reachingEdge.put(targetNode, edge);
+ putCosts(costs2nodes, nextCost, targetNode);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public @Nullable Integer getCost(@NonNull Node node) {
+ return node2cost.get(node);
+ }
+
+ /**
+ * Return a comparator to sort edges source cost first then target cost then alphabetically.
+ */
+ public @NonNull Comparator<@NonNull Edge> getEdgeCostComparator() {
+ Comparator<@NonNull Edge> edgeCostComparator2 = edgeCostComparator;
+ if (edgeCostComparator2 == null) {
+ edgeCostComparator = edgeCostComparator2 = new Comparator<@NonNull Edge>()
+ {
+ @Override
+ public int compare(@NonNull Edge e1, @NonNull Edge e2) {
+ Integer d1s = node2cost.get(RegionUtil.getSourceNode(e1));
+ Integer d1t = node2cost.get(RegionUtil.getTargetNode(e1));
+ Integer d2s = node2cost.get(RegionUtil.getSourceNode(e2));
+ Integer d2t = node2cost.get(RegionUtil.getTargetNode(e2));
+ assert (d1s != null) && (d1t != null) && (d2s != null) && (d2t != null);
+ int d1 = Math.max(d1s, d1t);
+ int d2 = Math.max(d2s, d2t);
+ if (d1 != d2) {
+ return d1 - d2;
+ }
+ if (d1s != d2s) {
+ return d1s - d2s;
+ }
+ return ClassUtil.safeCompareTo(e1.getDisplayName(), e2.getDisplayName());
+ }
+ };
+ }
+ return edgeCostComparator2;
+ }
+
+ /**
+ * Return a comparator to sort nodes cost first then alphabetically.
+ */
+ public @NonNull Comparator<@NonNull Node> getNodeCostComparator() {
+ Comparator<@NonNull Node> nodeCostComparator2 = nodeCostComparator;
+ if (nodeCostComparator2 == null) {
+ nodeCostComparator = nodeCostComparator2 = new Comparator<@NonNull Node>()
+ {
+ @Override
+ public int compare(@NonNull Node o1, @NonNull Node o2) {
+ Integer c1 = node2cost.get(o1);
+ Integer c2 = node2cost.get(o2);
+ assert (c1 != null) && (c2 != null);
+ int diff = c1 - c2;
+ if (diff != 0) {
+ return diff;
+ }
+ return ClassUtil.safeCompareTo(o1.getName(), o2.getName());
+ }
+ };
+ }
+ return nodeCostComparator2;
+ }
+
+ public @NonNull Iterable<@NonNull Node> getPredecessors(@NonNull Node targetNode) {
+ Set<@NonNull Node> precedingNodes = new HashSet<@NonNull Node>();
+ getPredecessors(precedingNodes, targetNode);
+ precedingNodes.remove(targetNode);
+ return precedingNodes;
+ }
+ private void getPredecessors(@NonNull Set<@NonNull Node> precedingNodes, @NonNull Node targetNode) {
+ if (precedingNodes.add(targetNode)) {
+ if (targetNode.isOperation()) {
+ for (@NonNull Edge edge : RegionUtil.getIncomingEdges(targetNode)) {
+ // assert edge.isUnconditional();
+ if (edge.isComputation()) {
+ getPredecessors(precedingNodes, edge.getEdgeSource());
+ }
+ }
+ }
+ else {
+ Edge parentEdge = getReachingEdge(targetNode);
+ if (parentEdge != null) {
+ Node sourceNode = parentEdge.getEdgeSource();
+ if (sourceNode == targetNode) { // invert a backward edge
+ sourceNode = parentEdge.getEdgeTarget();
+ }
+ getPredecessors(precedingNodes, sourceNode);
+ }
+ }
+ }
+ }
+
+ /**
+ * Return the edge from the preceding reachable node to the given reachable node. Null if none.
+ */
+ public @Nullable Edge getReachingEdge(@NonNull Node node) {
+ return node2reachingEdge.get(node);
+ }
+
+ private void putCosts(@NonNull List<@Nullable List<@NonNull Node>> costs2nodes, int cost, @NonNull Node node) {
+ while (costs2nodes.size() <= cost) {
+ costs2nodes.add(null);
+ }
+ List<@NonNull Node> nodes = costs2nodes.get(cost);
+ if (nodes == null) {
+ nodes = new ArrayList<>();
+ costs2nodes.set(cost, nodes);
+ }
+ if (!nodes.contains(node)) {
+ nodes.add(node);
+ }
+ }
+
+ @Override
+ public @NonNull String toString() {
+ Comparator<@NonNull Node> nodeComparator = getNodeCostComparator();
+ StringBuilder s = new StringBuilder();
+ List<@NonNull Node> nodes = new ArrayList<>(node2cost.keySet());
+ Collections.sort(nodes, nodeComparator);
+ for (@NonNull Node node : nodes) {
+ if (s.length() > 0) {
+ s.append("\n");
+ }
+ s.append(node2cost.get(node));
+ s.append(": ");
+ s.append(node.getName());
+ s.append(" : ");
+ s.append(node2reachingEdge.get(node));
+ }
+ return s.toString();
+ }
+}
\ No newline at end of file