Skip to content
This repository has been archived by the owner on Apr 21, 2023. It is now read-only.

Commit

Permalink
[eclipse/xtext-xtend#1005] Improve linking performance
Browse files Browse the repository at this point in the history
Deep Xbase expression trees could easily lead to a frozen editor due to
the complexity of the type resolution.

By reusing previous computation results and therefore avoiding duplicate
work, the performance could be greatly improved.

Signed-off-by: Sebastian Zarnekow <sebastian.zarnekow@gmail.com>
  • Loading branch information
szarnekow committed Feb 12, 2023
1 parent f259219 commit 1279b6c
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -756,10 +756,9 @@ public void testOverloadedOperators_10() throws Exception {
resolvesTo("(1..2).map[ new java.math.BigInteger(toString) ].reduce[ i1, i2| i1 + i2 ]", "BigInteger");
}

@Ignore("i1 and i2 should become T -> Object thus + maps to String + Object")
@Test
public void testOverloadedOperators_11() throws Exception {
resolvesTo("(1..2).map[ new java.math.BigInteger(toString) ].reduce[ i1, i2 | i1.toString + i2 ]", "String");
resolvesTo("(1..2).map[ new java.math.BigInteger(toString) ].reduce[ i1, i2 | i1.toString + i2 ]", "Comparable<?> & Serializable");
}

@Test
Expand All @@ -772,10 +771,9 @@ public void testOverloadedOperators_12() throws Exception {
"}", "String");
}

@Ignore("i1 and i2 should become T -> Object thus + maps to Object + String")
@Test
public void testOverloadedOperators_13() throws Exception {
resolvesTo("(1..2).map[ new java.math.BigInteger(toString) ].reduce[ i1, i2| i1 + String::valueOf(i2) ]", "String");
resolvesTo("(1..2).map[ new java.math.BigInteger(toString) ].reduce[ i1, i2| i1 + String::valueOf(i2) ]", "Comparable<?> & Serializable");
}

@Test
Expand Down Expand Up @@ -2842,7 +2840,7 @@ public void testFeatureCall_15_m() throws Exception {
"Integer");
}

@Ignore("slightly too slow")
@IgnoredBySmokeTest("Pointless since the scenario is pretty much the same as above")
@Test
public void testFeatureCall_15_n() throws Exception {
resolvesTo(
Expand Down Expand Up @@ -2892,7 +2890,6 @@ public void testFeatureCall_15_n() throws Exception {
"Integer");
}

@Ignore("too slow")
@Test
public void testFeatureCall_15_n_1() throws Exception {
resolvesTo(
Expand Down Expand Up @@ -2942,7 +2939,6 @@ public void testFeatureCall_15_n_1() throws Exception {
"Integer");
}

@Ignore("way too slow")
@Test
public void testFeatureCall_15_n_2() throws Exception {
resolvesTo(
Expand Down Expand Up @@ -3269,6 +3265,79 @@ public void testFeatureCall_42() throws Exception {
resolvesTo("new testdata.ArrayClient().toStringArray('a', 'b').head", "String");
}

@Test
public void testFeatureCall_43_a() throws Exception {
resolvesTo(
"{\n"
+ " var a = 0\n"
+ " var b = 0\n"
+ " var c = 0\n"
+ " a - (c*(Double.valueOf(10**(b-1)).intValue))\n"
+ "}",
"int");
}

@IgnoredBySmokeTest("Almost the same as testFeatureCall_43_a")
@Test
public void testFeatureCall_43_b() throws Exception {
resolvesTo(
"{\n"
+ " var a = 0\n"
+ " var b = 0\n"
+ " var c = 0\n"
+ " a - ((c*(Double.valueOf(10**(b-1)).intValue)) + (a - (c*(Double.valueOf(10**(b-1)).intValue))))\n"
+ "}",
"int");
}

@IgnoredBySmokeTest("Almost the same as testFeatureCall_43_a")
@Test
public void testFeatureCall_43_c() throws Exception {
resolvesTo(
"{\n"
+ " var a = 0\n"
+ " var b = 0\n"
+ " var c = 0\n"
+ " a - (((c*(Double.valueOf(10**(b-1)).intValue)) + (a - (c*(Double.valueOf(10**(b-1)).intValue)))) + (a - ((c*(Double.valueOf(10**(b-1)).intValue)) + (a - (c*(Double.valueOf(10**(b-1)).intValue))))))\n"
+ "}",
"int");
}

@Test
public void testFeatureCall_44_a() throws Exception {
resolvesTo(
"{\n"
+ " val mu = 0.0\n"
+ " val beta = 0.0\n"
+ " val double L = 0.0\n"
+ " val int a = 0\n"
+ " val int b = 0\n"
+ " val double lambda = 0.0\n"
+ " val double sa = 0.0\n"
+ " val double sb = 0.0\n"
+ " mu * (sa + lambda * (sb - sa) - 0.5 * mu * (a + lambda * (b - a)))\n"
+ "}",
"double");
}

@IgnoredBySmokeTest("Almost the same as testFeatureCall_44_a")
@Test
public void testFeatureCall_44_b() throws Exception {
resolvesTo(
"{\n"
+ " val mu = 0.0\n"
+ " val beta = 0.0\n"
+ " val double L = 0.0\n"
+ " val int a = 0\n"
+ " val int b = 0\n"
+ " val double lambda = 0.0\n"
+ " val double sa = 0.0\n"
+ " val double sb = 0.0\n"
+ " mu * ((sa + lambda * (sb - sa) - 0.5 * mu * (a + lambda * (b - a))) + (mu * (sa + lambda * (sb - sa) - 0.5 * mu * (a + lambda * (b - a)))))\n"
+ "}",
"double");
}

@Test
public void testToList_01() throws Exception {
resolvesTo(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2012, 2017 itemis AG (http://www.itemis.eu) and others.
* Copyright (c) 2012, 2022 itemis AG (http://www.itemis.eu) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
Expand Down Expand Up @@ -39,12 +39,8 @@
*/
public interface IResolvedTypes {

/*
* TODO find a suitable abstraction to represent diagnostics
* It's cumbersome to use Issues since they only know about URIs
* so we probably want to use Diagnostics?
*
* TODO do we really need this on the resolved types API? probably not
/**
* Provides access to all diagnostics that have been added during the type computation so far.
*/
Collection<AbstractDiagnostic> getQueuedDiagnostics();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@
*
* @noimplement This interface is not intended to be implemented by clients.
* @author Sebastian Zarnekow - Initial contribution and API
*
* TODO JavaDoc, toString
*/
public interface ITypeComputationState {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,17 @@
import com.google.common.collect.Lists;

/**
* @author Sebastian Zarnekow - Initial contribution and API
* @author Moritz Eysholdt
* Base implementation of {@link ITypeComputationState}.
*
* TODO JavaDoc
* @author Sebastian Zarnekow - Initial contribution and API
* @author Moritz Eysholdt - Validation for checked exceptions
*/
public abstract class AbstractTypeComputationState implements ITypeComputationState {
protected final ResolvedTypes resolvedTypes;
private IFeatureScopeSession featureScopeSession;
private final DefaultReentrantTypeResolver reentrantTypeResolver;
private List<AbstractTypeExpectation> expectations;

// is this field actually used?
private List<AbstractTypeExpectation> returnExpectations;

protected AbstractTypeComputationState(ResolvedTypes resolvedTypes,
Expand Down Expand Up @@ -137,10 +136,6 @@ protected ExpressionTypeComputationState createExpressionComputationState(XExpre
return new ExpressionTypeComputationState(typeResolution, featureScopeSession, this, expression);
}

/*
* Clients who override this method have to be careful with AbstractPendingLinkingCandidate#computeArgumentTypes where
* a subtype of TypeComputationStateWithExpectation is used.
*/
@Override
public TypeComputationStateWithExpectation withExpectation(/* @Nullable */ LightweightTypeReference expectation) {
return new TypeComputationStateWithExpectation(resolvedTypes, featureScopeSession, this, expectation);
Expand Down Expand Up @@ -326,7 +321,6 @@ public final List<? extends ITypeExpectation> getExpectations() {
return expectations;
}

//TODO not referenced
protected final List<? extends ITypeExpectation> getReturnExpectations() {
if (returnExpectations == null)
returnExpectations = getReturnExpectations(this, false);
Expand Down Expand Up @@ -410,15 +404,59 @@ public LightweightTypeReference getActualType(XExpression expression) {
Iterable<IEObjectDescription> descriptions = reentrantTypeResolver.getScopeProviderAccess().getCandidateDescriptions(
featureCall, XbasePackage.Literals.XABSTRACT_FEATURE_CALL__FEATURE, proxyOrResolved, featureScopeSession, demandResolvedTypes);
List<IFeatureLinkingCandidate> resultList = Lists.newArrayList();
AbstractPendingLinkingCandidate<?> previouslyResolved = resolvedTypes.forwardLinking(featureCall);
for(IEObjectDescription description: descriptions) {
resultList.add(createCandidate(featureCall, demandComputedTypes, toIdentifiableDescription(description)));
IFeatureLinkingCandidate candidate = createCandidate(featureCall, demandComputedTypes, toIdentifiableDescription(description));
// TODO apply to constructors, too
if (matches(previouslyResolved, candidate)) {
return Collections.singletonList(candidate);
}
resultList.add(candidate);
}
if (resultList.isEmpty()) {
resultList.add(new NullFeatureLinkingCandidate(featureCall, this));
}
return resultList;
}

private boolean matches(AbstractPendingLinkingCandidate<?> previouslyResolved, IFeatureLinkingCandidate candidate) {
if (previouslyResolved == null) {
return false;
}
if (!previouslyResolved.getClass().equals(candidate.getClass())) {
return false;
}
AbstractPendingLinkingCandidate<?> casted = (AbstractPendingLinkingCandidate<?>)candidate;
IIdentifiableElementDescription prevDescription = previouslyResolved.description;
IIdentifiableElementDescription description = casted.description;
if (!prevDescription.getShadowingKey().equals(description.getShadowingKey())) {
return false;
}
if (!prevDescription.getElementOrProxy().equals(description.getElementOrProxy())) {
return false;
}
if (!equalTypes(prevDescription.getSyntacticReceiverType(), description.getSyntacticReceiverType())) {
return false;
}
if (!equalTypes(prevDescription.getImplicitReceiverType(), description.getImplicitReceiverType())) {
return false;
}
if (!equalTypes(prevDescription.getImplicitFirstArgumentType(), description.getImplicitFirstArgumentType())) {
return false;
}
return true;
}

private boolean equalTypes(LightweightTypeReference left, LightweightTypeReference right) {
if (left == right) {
return true;
}
if (right == null) {
return false;
}
return left.getUniqueIdentifier().equals(right.getUniqueIdentifier());
}

protected IFeatureLinkingCandidate createResolvedLink(XAbstractFeatureCall featureCall, JvmIdentifiableElement resolvedTo) {
ExpressionAwareStackedResolvedTypes resolvedTypes = this.resolvedTypes.pushTypes(featureCall);
ExpressionTypeComputationState state = createExpressionComputationState(featureCall, resolvedTypes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@

import java.util.List;

import org.eclipse.xtext.xbase.XAbstractFeatureCall;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.scoping.batch.BucketedEObjectDescription;
import org.eclipse.xtext.xbase.scoping.batch.SimpleIdentifiableElementDescription;
import org.eclipse.xtext.xbase.typesystem.computation.IApplicableCandidate;
import org.eclipse.xtext.xbase.typesystem.computation.IFeatureLinkingCandidate;
import org.eclipse.xtext.xbase.typesystem.references.UnboundTypeReference;

/**
Expand All @@ -24,6 +29,13 @@ protected ExpressionAwareStackedResolvedTypes(ResolvedTypes parent, XExpression
super(parent);
this.expression = expression;
}

/**
* @since 2.30
*/
protected XExpression expression() {
return expression;
}

@Override
protected void prepareMergeIntoParent() {
Expand Down Expand Up @@ -55,5 +67,42 @@ protected void tryResolveUnboundReferences() {
}
}
}

@Override
protected void performMergeIntoParent() {
if (canBeForwardResolved()) {
/*
* XExpressions that can be forward-resolved will be kept around in the forwardComputedChildren map.
*
* There are two cases: Either we performed the computation for the first time or we are
* in a subsequent iteration where we used refined results from a previous run.
*/
IApplicableCandidate candidate = basicGetLinkingMap().get(expression);
if (candidate instanceof AbstractPendingLinkingCandidate<?>) {
AbstractPendingLinkingCandidate<?> casted = (AbstractPendingLinkingCandidate<?>) candidate;
if (casted.description instanceof BucketedEObjectDescription || casted.description instanceof SimpleIdentifiableElementDescription) {
forwardLinking().put((XAbstractFeatureCall) expression, casted);
}
}
}
super.performMergeIntoParent();
}

protected boolean canBeForwardResolved() {
if (expression instanceof XAbstractFeatureCall
&& basicGetTypeParameters().isEmpty()
&& basicGetTypeParameterHints().isEmpty()
&& basicGetDeclardTypeParameters() == null
&& basicGetReassignedTypes().isEmpty()
&& basicGetPropagatedTypes().isEmpty()
&& basicGetRefinedTypes().isEmpty()
&& basicGetTypes().isEmpty()) {
IFeatureLinkingCandidate candidate = this.getFeature((XAbstractFeatureCall)expression);
if (candidate != null && candidate.getTypeArguments().isEmpty()) {
return true;
}
}
return false;
}

}
Loading

0 comments on commit 1279b6c

Please sign in to comment.