blob: 44e5e634641eeeefaf6577701287d9873044d81c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* SAP AG - initial API and implementation
******************************************************************************/
package org.eclipse.ocl.examples.impactanalyzer.instanceScope.traceback;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.ocl.ecore.LetExp;
import org.eclipse.ocl.ecore.LoopExp;
import org.eclipse.ocl.ecore.OCLExpression;
import org.eclipse.ocl.ecore.Variable;
import org.eclipse.ocl.examples.impactanalyzer.ValueNotFoundException;
import org.eclipse.ocl.examples.impactanalyzer.configuration.ActivationOption;
import org.eclipse.ocl.examples.impactanalyzer.instanceScope.NavigationStep;
import org.eclipse.ocl.examples.impactanalyzer.instanceScope.unusedEvaluation.UnusedEvaluationRequest;
import org.eclipse.ocl.examples.impactanalyzer.instanceScope.unusedEvaluation.UnusedEvaluationRequestFactory;
import org.eclipse.ocl.examples.impactanalyzer.instanceScope.unusedEvaluation.UnusedEvaluationRequestSet;
import org.eclipse.ocl.examples.impactanalyzer.util.AnnotatedEObject;
import org.eclipse.ocl.examples.impactanalyzer.util.OperationCallExpKeyedSet;
import org.eclipse.ocl.examples.impactanalyzer.util.OperationCallExpKeyedSetFactory;
import org.eclipse.ocl.examples.impactanalyzer.util.Tuple.Triple;
/**
* During the navigation of a {@link NavigationStep} tree, starting from an {@link EObject}, navigation steps may be reached
* several times with the same {@link EObject} at hand. To avoid endless recursion and to accelerate the computation, an object of
* this class is passed through an evaluation of a {@link NavigationStep} tree.
* <p>
*
* In addition to remembering traceback navigation results, this cache also stores variable values inferred during the traceback.
* Those can be used for partial evaluations, e.g., when trying to determine if a subexpression may be reached at all based on the
* current variable values.
* <p>
*
* Variable values are valid only in a scope, called a "dynamic scope." While syntactically the variable's static scope is limited
* to a certain {@link OCLExpression}, e.g., the {@link LetExp#getIn() in} expression of a
* <code>let<code> expression for the variable defined by the <code>let</code> expression, the same expression may be evaluated
* several times during the evaluation of some other expression. Each evaluation of the static scope expression represents a
* dynamic scope for the variables whose static scope is being evaluated.
* <p>
*
* When the <code>unused</code> function is trying a partial evaluation and is missing a variable to complete the evaluation, we
* check if the missing variable is a <code>let</code> variable. If not, the <code>unused</code> evaluation request is stored in
* this cache together with the values of all variables known at that time, as well as the unknown variable that caused the
* evaluation to fail. If the missing variable <em>is</em> a <code>let</code> variable, we also try to evaluate the corresponding
* {@link Variable#getInitExpression() initialization expression}. If that fails too, we store a nested <code>unused</code>
* evaluation request which has as its unknown variable the one from the <code>let</code> variable's initialization expression. If
* that variable's value is inferred, the initialization expression is evaluated again. If successful (either deferred or
* immediately), this triggers the inner request which will then have its unknown <code>let</code> variable set to the result of
* evaluating the initialization expression.
* <p>
*
* When during traceback a variable's value is inferred, the <code>unused</code> evaluations that have this variable as their
* unknown variable can be given another try, based on the variable values remembered, as well as the freshly-inferred value for
* the variable whose value was formerly unknown. If the evaluation succeeds and proves that the expression for which
* <code>unused</code> was evaluated is actually not used, the traceback can be stopped immediately and simply return an empty
* set. If the evaluation fails again for another variable with unknown value, the procedure is repeated: the freshly-inferred
* value is added to the known variable values for the <code>unused</code> evaluation request, and the variable whose value now is
* unknown is remembered with the request such that when <em>it's</em> value is inferred by the traceback procedure, the
* <code>unused</code> evaluation can be attempted again.
* <p>
*
* When the traceback function leaves an expression that is the static scope of an unknown variable, the corresponding
* <code>unused</code> evaluation requests are canceled because we cannot hope for the traceback process to ever infer it anymore.
* Which scopes are left when moving from one expression to the next can be a bit tricky. For example, when jumping from an
* iterator variable expression to the {@link LoopExp}'s source expression, all kinds of in-between expressions that are containers to
* the iterator variable expression are left as well. Therefore, the common container of the from/to expressions needs to be
* determined, and all variables whose static scope is any of from, or any of from's direct or transitive containers that
* are still contained (directly or transitively) in the common container of from/to, are considered out of scope.<p>
*
* @author Axel Uhl (D043530)
*
*/
public class TracebackCache {
/**
* Cached values of {@link NavigationStep#navigate(Set, TracebackCache, org.eclipse.emf.common.notify.Notification)} calls,
* keyed by the step and the <code>fromObject</code> from which navigation was computed, caching the prior results.
*/
private final Map<Triple<TracebackStep, AnnotatedEObject, UnusedEvaluationRequestSet>, OperationCallExpKeyedSet> navigateCache;
/**
* Caches the results of
* {@link UnusedEvaluationRequest#evaluate(org.eclipse.ocl.ecore.opposites.OppositeEndFinder, org.eclipse.ocl.examples.impactanalyzer.OCLFactory)
* evaluating} an {@link UnusedEvaluationRequest}. The result may either be a {@link ValueNotFoundException} telling
* for which unknown variable the request failed, or a {@link Boolean} telling if the evaluation caused unusedness
* to be proven.
*/
private final Map<UnusedEvaluationRequest, Object> unusedEvaluationCache;
private final ActivationOption configuration;
private final UnusedEvaluationRequestFactory unusedEvaluationRequestFactory;
private final OperationCallExpKeyedSetFactory operationCallExpKeyedSetFactory;
public TracebackCache(ActivationOption configuration, UnusedEvaluationRequestFactory unusedEvaluationRequestFactory) {
navigateCache = new HashMap<Triple<TracebackStep, AnnotatedEObject, UnusedEvaluationRequestSet>, OperationCallExpKeyedSet>();
unusedEvaluationCache = new HashMap<UnusedEvaluationRequest, Object>();
this.configuration = configuration;
this.unusedEvaluationRequestFactory = unusedEvaluationRequestFactory;
operationCallExpKeyedSetFactory = new OperationCallExpKeyedSetFactory(configuration.isOperationCallSelectionActive());
}
public ActivationOption getConfiguration() {
return configuration;
}
/**
* Looks up a previously {@link #put entered} result for the <code>step</code> where navigation
* started at the <code>from</code> object. If not found, <code>null</code> is returned.
*/
public OperationCallExpKeyedSet get(TracebackStep step, AnnotatedEObject from,
UnusedEvaluationRequestSet pendingUnusedEvaluationRequests) {
OperationCallExpKeyedSet result = navigateCache.get(new Triple<TracebackStep, AnnotatedEObject, UnusedEvaluationRequestSet>(
step, from, pendingUnusedEvaluationRequests));
return result;
}
public void put(TracebackStep step, AnnotatedEObject from, UnusedEvaluationRequestSet pendingUnusedEvaluationRequests,
OperationCallExpKeyedSet result) {
navigateCache.put(new Triple<TracebackStep, AnnotatedEObject, UnusedEvaluationRequestSet>(step, from,
pendingUnusedEvaluationRequests), result);
}
/**
* Caches the results of
* {@link UnusedEvaluationRequest#evaluate(org.eclipse.ocl.ecore.opposites.OppositeEndFinder, org.eclipse.ocl.examples.impactanalyzer.util.OCLFactory)
* evaluating} an {@link UnusedEvaluationRequest}. The result may either be a {@link ValueNotFoundException} telling
* for which unknown variable the request failed, or a {@link Boolean} telling if the evaluation caused unusedness
* to be proven.
*/
public Object getCachedEvaluationResult(UnusedEvaluationRequest request) {
return unusedEvaluationCache.get(request);
}
/**
* Caches the results of
* {@link UnusedEvaluationRequest#evaluate(org.eclipse.ocl.ecore.opposites.OppositeEndFinder, org.eclipse.ocl.examples.impactanalyzer.util.OCLFactory)
* evaluating} an {@link UnusedEvaluationRequest}. The result may either be a {@link ValueNotFoundException} telling
* for which unknown variable the request failed, or a {@link Boolean} telling if the evaluation caused unusedness
* to be proven.
*/
public void cacheEvaluationResult(UnusedEvaluationRequest request, Object evaluationResult) {
unusedEvaluationCache.put(request, evaluationResult);
}
public UnusedEvaluationRequestFactory getUnusedEvaluationRequestFactory() {
return unusedEvaluationRequestFactory;
}
public OperationCallExpKeyedSetFactory getOperationCallExpKeyedSetFactory() {
return operationCallExpKeyedSetFactory;
}
}