[serializer/performance] avoid redundant traversal of node model

Signed-off-by: Moritz Eysholdt <moritz.eysholdt@typefox.io>
This commit is contained in:
Moritz Eysholdt 2016-10-07 19:48:37 +02:00 committed by Moritz Eysholdt
parent 40016bd0dd
commit d59869f704
3 changed files with 197 additions and 82 deletions

View file

@ -9,7 +9,6 @@ package org.eclipse.xtext.serializer.sequencer;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -34,9 +33,9 @@ import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IConstra
import org.eclipse.xtext.serializer.analysis.ISemanticSequencerNfaProvider.ISemState;
import org.eclipse.xtext.serializer.analysis.SerializationContext;
import org.eclipse.xtext.serializer.sequencer.ISemanticNodeProvider.INodesForEObjectProvider;
import org.eclipse.xtext.serializer.sequencer.ISemanticNodeProvider.ISemanticNode;
import org.eclipse.xtext.util.EmfFormatter;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Triple;
import org.eclipse.xtext.util.Tuples;
import org.eclipse.xtext.util.formallang.Nfa;
import org.eclipse.xtext.util.formallang.NfaUtil;
@ -101,16 +100,19 @@ public class BacktrackingSemanticSequencer extends AbstractSemanticSequencer {
public class SerializableObject {
protected final EObject eObject;
protected final ISerializationContext context;
protected List<INode>[] nodes;
protected List<ISemanticNode>[] nodes;
protected final ISemanticNode firstNode;
protected boolean[] optional;
protected Map<Pair<AbstractElement, Integer>, Boolean> valid = Maps.newHashMap();
protected Object[] values;
@SuppressWarnings("unchecked")
public SerializableObject(ISerializationContext context, EObject eObject, INodesForEObjectProvider nodeProvider) {
public SerializableObject(ISerializationContext context, EObject eObject,
INodesForEObjectProvider nodeProvider) {
super();
this.eObject = eObject;
this.context = context;
this.firstNode = nodeProvider.getFirstSemanticNode();
EClass clazz = eObject.eClass();
values = new Object[clazz.getFeatureCount()];
nodes = new List[clazz.getFeatureCount()];
@ -119,8 +121,8 @@ public class BacktrackingSemanticSequencer extends AbstractSemanticSequencer {
int featureID = eObject.eClass().getFeatureID(feature);
if (feature.isMany())
switch (transientValues.isListTransient(eObject, feature)) {
case NO:
List<INode> nodes1 = Lists.newArrayList();
case NO:
List<ISemanticNode> nodes1 = Lists.newArrayList();
List<?> values1;
if (feature instanceof EReference && ((EReference) feature).isResolveProxies()) {
values1 = ((InternalEList<?>) eObject.eGet(feature)).basicList();
@ -128,49 +130,49 @@ public class BacktrackingSemanticSequencer extends AbstractSemanticSequencer {
values1 = (List<?>) eObject.eGet(feature);
}
for (int i = 0; i < values1.size(); i++)
nodes1.add(nodeProvider.getNodeForMultiValue(feature, i, i, values1.get(i)));
nodes1.add(nodeProvider.getSemanticNodeForMultiValue(feature, i, i, values1.get(i)));
values[featureID] = values1;
nodes[featureID] = nodes1;
break;
case SOME:
List<INode> nodes2 = Lists.newArrayList();
List<?> values2;
if (feature instanceof EReference && ((EReference) feature).isResolveProxies()) {
values2 = ((InternalEList<?>) eObject.eGet(feature)).basicList();
} else {
values2 = (List<?>) eObject.eGet(feature);
case SOME:
List<ISemanticNode> nodes2 = Lists.newArrayList();
List<?> values2;
if (feature instanceof EReference && ((EReference) feature).isResolveProxies()) {
values2 = ((InternalEList<?>) eObject.eGet(feature)).basicList();
} else {
values2 = (List<?>) eObject.eGet(feature);
}
List<Object> values3 = Lists.newArrayList();
for (int i = 0, j = 0; i < values2.size(); i++)
if (!transientValues.isValueInListTransient(eObject, i, feature)) {
Object value = values2.get(i);
ISemanticNode node = nodeProvider.getSemanticNodeForMultiValue(feature, i, j++, value);
values3.add(value);
nodes2.add(node);
}
List<Object> values3 = Lists.newArrayList();
for (int i = 0, j = 0; i < values2.size(); i++)
if (!transientValues.isValueInListTransient(eObject, i, feature)) {
Object value = values2.get(i);
INode node = nodeProvider.getNodeForMultiValue(feature, i, j++, value);
values3.add(value);
nodes2.add(node);
}
values[featureID] = values3;
nodes[featureID] = nodes2;
break;
case YES:
values[featureID] = INVALID;
values[featureID] = values3;
nodes[featureID] = nodes2;
break;
case YES:
values[featureID] = INVALID;
}
else
switch (transientValues.isValueTransient(eObject, feature)) {
case PREFERABLY:
optional[featureID] = true;
Object value1 = eObject.eGet(feature, false);
values[featureID] = value1;
nodes[featureID] = Collections
.singletonList(nodeProvider.getNodeForSingelValue(feature, value1));
break;
case NO:
Object value2 = eObject.eGet(feature, false);
values[featureID] = value2;
nodes[featureID] = Collections
.singletonList(nodeProvider.getNodeForSingelValue(feature, value2));
break;
case YES:
values[featureID] = INVALID;
case PREFERABLY:
optional[featureID] = true;
Object value1 = eObject.eGet(feature, false);
values[featureID] = value1;
nodes[featureID] = Collections
.singletonList(nodeProvider.getSemanticNodeForSingelValue(feature, value1));
break;
case NO:
Object value2 = eObject.eGet(feature, false);
values[featureID] = value2;
nodes[featureID] = Collections
.singletonList(nodeProvider.getSemanticNodeForSingelValue(feature, value2));
break;
case YES:
values[featureID] = INVALID;
}
}
}
@ -179,8 +181,12 @@ public class BacktrackingSemanticSequencer extends AbstractSemanticSequencer {
return eObject;
}
public INode getNode(int featureID, int index) {
List<INode> featureNodes = nodes[featureID];
public ISemanticNode getFirstNode() {
return firstNode;
}
public ISemanticNode getNode(int featureID, int index) {
List<ISemanticNode> featureNodes = nodes[featureID];
if (featureNodes != null && index >= 0 && index < featureNodes.size())
return featureNodes.get(index);
return null;
@ -234,7 +240,8 @@ public class BacktrackingSemanticSequencer extends AbstractSemanticSequencer {
if (valid.get(key) == Boolean.TRUE)
return true;
INode node = getNode(state.getFeatureID(), index);
ISemanticNode semanticNode = getNode(state.getFeatureID(), index);
INode node = semanticNode == null ? null : semanticNode.getNode();
Multimap<AbstractElement, ISerializationContext> assignments = ArrayListMultimap.create();
for (AbstractElement ele : candidates)
assignments.put(ele, context);
@ -269,7 +276,7 @@ public class BacktrackingSemanticSequencer extends AbstractSemanticSequencer {
protected static class TraceItem {
protected int index;
protected int[] nextIndex;
protected INode node;
protected ISemanticNode node;
protected SerializableObject obj;
protected TraceItem parent;
protected ISemState state;
@ -335,20 +342,22 @@ public class BacktrackingSemanticSequencer extends AbstractSemanticSequencer {
}
public AbstractElement getNextGrammarElement() {
Iterator<Triple<INode, AbstractElement, EObject>> it;
if (obj != null && (parent == null || parent.parent == null))
it = new SemanticNodeIterator(obj.getEObject());
else if (node == null)
return null;
else
it = new SemanticNodeIterator(node);
if (it.hasNext())
return it.next().getSecond();
ISemanticNode sem = null;
if (obj != null && (parent == null || parent.parent == null)) {
sem = obj.getFirstNode();
} else if (node != null) {
sem = node.getFollower();
}
if (sem != null) {
return sem.getGrammarElement();
}
return null;
}
public INode getNode() {
return node;
if (node == null)
return null;
return node.getNode();
}
public SerializableObject getObj() {
@ -483,7 +492,8 @@ public class BacktrackingSemanticSequencer extends AbstractSemanticSequencer {
return r;
}
});
SequenceFeeder feeder = feederProvider.create(context, obj, nodes, masterSequencer, sequenceAcceptor, errorAcceptor);
SequenceFeeder feeder = feederProvider.create(context, obj, nodes, masterSequencer, sequenceAcceptor,
errorAcceptor);
if (trace != null) {
for (TraceItem ti : trace)
if (ti.getState() != null && ti.getState().getFeature() != null)

View file

@ -9,6 +9,7 @@ package org.eclipse.xtext.serializer.sequencer;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.INode;
@ -20,7 +21,21 @@ import com.google.inject.ImplementedBy;
@ImplementedBy(SemanticNodeProvider.class)
public interface ISemanticNodeProvider {
public interface ISemanticNode {
INode getNode();
ISemanticNode getFollower();
AbstractElement getGrammarElement();
}
public interface INodesForEObjectProvider {
ISemanticNode getSemanticNodeForMultiValue(EStructuralFeature feature, int indexInFeature, int indexInNonTransient, Object value);
ISemanticNode getSemanticNodeForSingelValue(EStructuralFeature feature, Object value);
ISemanticNode getFirstSemanticNode();
INode getNodeForMultiValue(EStructuralFeature feature, int indexInFeature, int indexInNonTransient, Object value);
INode getNodeForSingelValue(EStructuralFeature feature, Object value);
@ -37,6 +52,22 @@ public interface ISemanticNodeProvider {
public INode getNodeForSingelValue(EStructuralFeature feature, Object value) {
return null;
}
@Override
public ISemanticNode getSemanticNodeForMultiValue(EStructuralFeature feature, int indexInFeature,
int indexInNonTransient, Object value) {
return null;
}
@Override
public ISemanticNode getSemanticNodeForSingelValue(EStructuralFeature feature, Object value) {
return null;
}
@Override
public ISemanticNode getFirstSemanticNode() {
return null;
}
}
public INodesForEObjectProvider NULL_NODES_PROVIDER = new NullNodesForEObjectProvider();

View file

@ -14,8 +14,10 @@ import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.nodemodel.BidiTreeIterator;
import org.eclipse.xtext.nodemodel.ICompositeNode;
@ -30,11 +32,45 @@ import com.google.common.collect.Maps;
*/
public class SemanticNodeProvider implements ISemanticNodeProvider {
public static class SemanticNode implements ISemanticNode {
private final INode node;
private final AbstractElement grammarElement;
private SemanticNode follower = null;
public SemanticNode(INode node) {
this.node = node;
EObject ge = node.getGrammarElement();
if (ge instanceof CrossReference)
this.grammarElement = ((CrossReference) ge).getTerminal();
else if (ge instanceof AbstractElement)
this.grammarElement = (AbstractElement) ge;
else
this.grammarElement = null;
}
@Override
public INode getNode() {
return node;
}
@Override
public ISemanticNode getFollower() {
return follower;
}
@Override
public AbstractElement getGrammarElement() {
return grammarElement;
}
}
public static class NodesForEObjectProvider implements INodesForEObjectProvider {
protected final Object[] childrenByFeatureIDAndIndex;
protected Map<EObject, INode> childrenBySemanticChild = null;
protected Map<EObject, SemanticNode> childrenBySemanticChild = null;
protected SemanticNode first = null;
protected final ICompositeNode node;
@ -48,22 +84,29 @@ public class SemanticNodeProvider implements ISemanticNodeProvider {
collectNodesForFeatures();
}
protected void add(String featureName, INode child) {
protected SemanticNode add(String featureName, INode child, SemanticNode last) {
if (featureName == null)
return;
return last;
EClass eClass = this.semanticObject.eClass();
EStructuralFeature feature = eClass.getEStructuralFeature(featureName);
if (feature == null)
return;
return last;
SemanticNode sem = new SemanticNode(child);
if (last != null) {
last.follower = sem;
}
if (this.first == null) {
this.first = sem;
}
int id = eClass.getFeatureID(feature);
if (feature.isMany()) {
@SuppressWarnings("unchecked")
List<INode> nodes = (List<INode>) childrenByFeatureIDAndIndex[id];
List<SemanticNode> nodes = (List<SemanticNode>) childrenByFeatureIDAndIndex[id];
if (nodes == null)
childrenByFeatureIDAndIndex[id] = nodes = Lists.<INode> newArrayList();
nodes.add(child);
childrenByFeatureIDAndIndex[id] = nodes = Lists.<SemanticNode>newArrayList();
nodes.add(sem);
} else {
childrenByFeatureIDAndIndex[id] = child;
childrenByFeatureIDAndIndex[id] = sem;
}
if (feature instanceof EReference) {
EReference reference = (EReference) feature;
@ -72,15 +115,18 @@ public class SemanticNodeProvider implements ISemanticNodeProvider {
if (semanitcChild != null) {
if (this.childrenBySemanticChild == null)
this.childrenBySemanticChild = Maps.newHashMap();
this.childrenBySemanticChild.put(semanitcChild, child);
this.childrenBySemanticChild.put(semanitcChild, sem);
}
}
}
return sem;
}
// this implementation should be synonym to
// org.eclipse.xtext.nodemodel.util.NodeModelUtils.findNodesForFeature(EObject, INode, EStructuralFeature)
// this implementation should be synonym to
// org.eclipse.xtext.nodemodel.util.NodeModelUtils.findNodesForFeature(EObject,
// INode, EStructuralFeature)
protected void collectNodesForFeatures() {
SemanticNode last = null;
BidiTreeIterator<INode> iterator = node.getAsTreeIterable().iterator();
while (iterator.hasNext()) {
INode child = iterator.next();
@ -90,11 +136,14 @@ public class SemanticNodeProvider implements ISemanticNodeProvider {
Action action = (Action) grammarElement;
if (child.getSemanticElement() == this.semanticObject) {
child = iterator.next();
add(action.getFeature(), child);
last = add(action.getFeature(), child, last);
} else {
// navigate the action's left side (first child) until we find an assignment (a rule call)
// the assignment will tell us about the feature to which we assigned
// the semantic object that has been created by the action
// navigate the action's left side (first child)
// until we find an assignment (a rule call)
// the assignment will tell us about the feature to
// which we assigned
// the semantic object that has been created by the
// action
INode firstChild = ((ICompositeNode) child).getFirstChild();
while (firstChild.getGrammarElement() instanceof Action) {
firstChild = ((ICompositeNode) firstChild).getFirstChild();
@ -102,13 +151,13 @@ public class SemanticNodeProvider implements ISemanticNodeProvider {
EObject firstChildGrammarElement = firstChild.getGrammarElement();
Assignment assignment = GrammarUtil.containingAssignment(firstChildGrammarElement);
if (assignment != null)
add(assignment.getFeature(), child);
last = add(assignment.getFeature(), child, last);
}
iterator.prune();
} else if (child != node) {
Assignment assignment = GrammarUtil.containingAssignment(grammarElement);
if (assignment != null) {
add(assignment.getFeature(), child);
last = add(assignment.getFeature(), child, last);
iterator.prune();
}
}
@ -117,26 +166,27 @@ public class SemanticNodeProvider implements ISemanticNodeProvider {
}
@Override
public INode getNodeForMultiValue(EStructuralFeature feat, int indexInFeat, int indexInNonTransient, Object val) {
public SemanticNode getSemanticNodeForMultiValue(EStructuralFeature feat, int indexInFeat,
int indexInNonTransient, Object val) {
if (childrenBySemanticChild != null && feat instanceof EReference && ((EReference) feat).isContainment()) {
INode candiadate = this.childrenBySemanticChild.get(val);
SemanticNode candiadate = this.childrenBySemanticChild.get(val);
if (candiadate != null)
return candiadate;
}
Object object = this.childrenByFeatureIDAndIndex[semanticObject.eClass().getFeatureID(feat)];
if (feat.isMany() && object instanceof List<?>) {
@SuppressWarnings("unchecked")
List<INode> nodes = (List<INode>) object;
List<SemanticNode> nodes = (List<SemanticNode>) object;
if (indexInNonTransient >= 0 && indexInNonTransient < nodes.size())
return nodes.get(indexInNonTransient);
} else if (object instanceof INode)
return (INode) object;
} else if (object instanceof SemanticNode)
return (SemanticNode) object;
return null;
}
@Override
public INode getNodeForSingelValue(EStructuralFeature feature, Object value) {
return getNodeForMultiValue(feature, 0, 0, value);
public SemanticNode getSemanticNodeForSingelValue(EStructuralFeature feature, Object value) {
return getSemanticNodeForMultiValue(feature, 0, 0, value);
}
protected EObject getSemanticChild(INode node) {
@ -151,6 +201,29 @@ public class SemanticNodeProvider implements ISemanticNodeProvider {
}
return null;
}
@Override
public INode getNodeForMultiValue(EStructuralFeature feature, int indexInFeature, int indexInNonTransient,
Object value) {
SemanticNode semantic = getSemanticNodeForMultiValue(feature, indexInFeature, indexInNonTransient, value);
if (semantic == null)
return null;
return semantic.getNode();
}
@Override
public INode getNodeForSingelValue(EStructuralFeature feature, Object value) {
SemanticNode semantic = getSemanticNodeForSingelValue(feature, value);
if (semantic == null)
return null;
return semantic.getNode();
}
@Override
public ISemanticNode getFirstSemanticNode() {
return first;
}
}
protected NodesForEObjectProvider createNodesForEObjectProvider(EObject semanticObject, ICompositeNode node) {
@ -158,7 +231,8 @@ public class SemanticNodeProvider implements ISemanticNodeProvider {
}
@Override
public INodesForEObjectProvider getNodesForSemanticObject(EObject semanticObject, ICompositeNode suggestedComposite) {
public INodesForEObjectProvider getNodesForSemanticObject(EObject semanticObject,
ICompositeNode suggestedComposite) {
if (suggestedComposite != null)
return createNodesForEObjectProvider(semanticObject, suggestedComposite);
ICompositeNode actualComposite = NodeModelUtils.findActualNodeFor(semanticObject);