cc.kave.commons.pointsto.analysis.unification.UnificationAnalysisVisitorContext.java Source code

Java tutorial

Introduction

Here is the source code for cc.kave.commons.pointsto.analysis.unification.UnificationAnalysisVisitorContext.java

Source

/**
 * Copyright 2016 Simon Reu
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package cc.kave.commons.pointsto.analysis.unification;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.jgrapht.alg.util.UnionFind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;

import cc.kave.commons.model.events.completionevents.Context;
import cc.kave.commons.model.naming.codeelements.IMemberName;
import cc.kave.commons.model.naming.codeelements.IMethodName;
import cc.kave.commons.model.naming.codeelements.IParameterName;
import cc.kave.commons.model.naming.codeelements.IPropertyName;
import cc.kave.commons.model.naming.types.ITypeName;
import cc.kave.commons.model.ssts.IReference;
import cc.kave.commons.model.ssts.expressions.IAssignableExpression;
import cc.kave.commons.model.ssts.expressions.assignable.IInvocationExpression;
import cc.kave.commons.model.ssts.expressions.assignable.ILambdaExpression;
import cc.kave.commons.model.ssts.references.IAssignableReference;
import cc.kave.commons.model.ssts.references.IEventReference;
import cc.kave.commons.model.ssts.references.IFieldReference;
import cc.kave.commons.model.ssts.references.IIndexAccessReference;
import cc.kave.commons.model.ssts.references.IMemberReference;
import cc.kave.commons.model.ssts.references.IMethodReference;
import cc.kave.commons.model.ssts.references.IPropertyReference;
import cc.kave.commons.model.ssts.references.IUnknownReference;
import cc.kave.commons.model.ssts.references.IVariableReference;
import cc.kave.commons.model.ssts.statements.IAssignment;
import cc.kave.commons.pointsto.analysis.AbstractLocation;
import cc.kave.commons.pointsto.analysis.references.DistinctFieldReference;
import cc.kave.commons.pointsto.analysis.references.DistinctIndexAccessReference;
import cc.kave.commons.pointsto.analysis.references.DistinctLambdaParameterReference;
import cc.kave.commons.pointsto.analysis.references.DistinctMemberReference;
import cc.kave.commons.pointsto.analysis.references.DistinctMethodParameterReference;
import cc.kave.commons.pointsto.analysis.references.DistinctPropertyParameterReference;
import cc.kave.commons.pointsto.analysis.references.DistinctPropertyReference;
import cc.kave.commons.pointsto.analysis.references.DistinctReference;
import cc.kave.commons.pointsto.analysis.references.DistinctReferenceCreationVisitor;
import cc.kave.commons.pointsto.analysis.unification.identifiers.LocationIdentifier;
import cc.kave.commons.pointsto.analysis.unification.identifiers.LocationIdentifierFactory;
import cc.kave.commons.pointsto.analysis.unification.locations.BottomLocation;
import cc.kave.commons.pointsto.analysis.unification.locations.ExtendedReferenceLocation;
import cc.kave.commons.pointsto.analysis.unification.locations.FunctionLocation;
import cc.kave.commons.pointsto.analysis.unification.locations.Location;
import cc.kave.commons.pointsto.analysis.unification.locations.ReferenceLocation;
import cc.kave.commons.pointsto.analysis.unification.locations.SimpleReferenceLocation;
import cc.kave.commons.pointsto.analysis.utils.LanguageOptions;
import cc.kave.commons.pointsto.analysis.utils.PropertyAsFieldPredicate;
import cc.kave.commons.pointsto.analysis.visitors.DistinctReferenceVisitorContext;
import cc.kave.commons.pointsto.analysis.visitors.ThisReferenceOption;
import cc.kave.commons.pointsto.extraction.DeclarationMapper;
import cc.recommenders.assertions.Asserts;

public class UnificationAnalysisVisitorContext extends DistinctReferenceVisitorContext {

    private static final Logger LOGGER = LoggerFactory.getLogger(UnificationAnalysisVisitorContext.class);

    private LanguageOptions languageOptions = LanguageOptions.getInstance();

    private DistinctReferenceCreationVisitor distinctReferenceCreationVisitor = new DistinctReferenceCreationVisitor();

    private Multimap<SetRepresentative, SetRepresentative> pending = HashMultimap.create();
    private UnionFind<SetRepresentative> unionFind = new UnionFind<>(new HashSet<>());

    private Map<DistinctReference, ReferenceLocation> referenceLocations = new HashMap<>();
    private Map<DistinctMemberReference, SetRepresentative> memberLocations = new HashMap<>();
    private Map<DistinctIndexAccessReference, SetRepresentative> indexAccessLocations = new HashMap<>();

    private IAssignment lastAssignment;
    private IMemberName currentMember;
    private Deque<Pair<ILambdaExpression, ReferenceLocation>> lambdaStack = new ArrayDeque<>();

    private final PropertyAsFieldPredicate treatPropertyAsField;

    private Map<IMemberName, ReferenceLocation> returnLocations = new HashMap<>();

    private DestinationLocationVisitor destLocationVisitor = new DestinationLocationVisitor();
    private SourceLocationVisitor srcLocationVisitor = new SourceLocationVisitor();

    private final LocationIdentifierFactory identifierFactory;

    private Multimap<ReferenceLocation, ReferenceLocation> pendingUnifications = HashMultimap.create();

    public UnificationAnalysisVisitorContext(Context context, LocationIdentifierFactory identifierFactory) {
        super(context, ThisReferenceOption.PER_CONTEXT);
        this.treatPropertyAsField = new PropertyAsFieldPredicate(new DeclarationMapper(context));
        this.identifierFactory = identifierFactory;

        createImplicitLocations(context);
    }

    public Map<DistinctReference, AbstractLocation> getReferenceLocations() {
        int totalReferences = referenceLocations.size() + memberLocations.size();
        Map<DistinctReference, AbstractLocation> result = new HashMap<>(totalReferences);
        Map<SetRepresentative, AbstractLocation> refToAbstractLocation = new HashMap<>(totalReferences);

        for (Map.Entry<DistinctReference, ReferenceLocation> entry : referenceLocations.entrySet()) {
            DistinctReference reference = entry.getKey();
            ReferenceLocation location = entry.getValue();

            SetRepresentative ecr;
            if (reference instanceof DistinctMemberReference) {
                ecr = unionFind.find(location.getSetRepresentative());
            } else {
                Location valueLocation = unionFind
                        .find(location.getLocation(LocationIdentifier.VALUE).getSetRepresentative()).getLocation();
                Location funLocation = location.getLocation(LocationIdentifier.FUNCTION);
                if (funLocation != null) {
                    funLocation = unionFind.find(funLocation.getSetRepresentative()).getLocation();
                }
                ecr = (valueLocation.isBottom() && funLocation != null && !funLocation.isBottom())
                        ? funLocation.getSetRepresentative()
                        : valueLocation.getSetRepresentative();
            }

            AbstractLocation abstractLocation = refToAbstractLocation.get(ecr);
            if (abstractLocation == null) {
                abstractLocation = new AbstractLocation();
                refToAbstractLocation.put(ecr, abstractLocation);
            }

            result.put(reference, abstractLocation);
        }

        for (Map.Entry<? extends DistinctReference, SetRepresentative> entry : Iterables
                .concat(memberLocations.entrySet(), indexAccessLocations.entrySet())) {
            DistinctReference reference = entry.getKey();
            SetRepresentative ecr = unionFind.find(entry.getValue());

            AbstractLocation abstractLocation = refToAbstractLocation.get(ecr);
            if (abstractLocation == null) {
                abstractLocation = new AbstractLocation();
                refToAbstractLocation.put(ecr, abstractLocation);
            }

            result.put(reference, abstractLocation);
        }

        return result;
    }

    private void createImplicitLocations(Context context) {
        DistinctReference thisRef = namesToReferences.get(languageOptions.getThisName());
        allocate(thisRef);
        DistinctReference superRef = namesToReferences.get(languageOptions.getSuperName());

        // let 'this' and 'super' point to the same object
        alias(superRef, thisRef);
    }

    public void finalizeAnalysis() {
        finalizePendingJoins();
        finalizePendingUnifications();
    }

    private void finalizePendingJoins() {
        while (!pending.isEmpty()) {
            Map.Entry<SetRepresentative, SetRepresentative> entry = pending.entries().iterator().next();
            join(entry.getKey(), entry.getValue());
            pending.remove(entry.getKey(), entry.getValue());
        }
    }

    /**
     * Reruns the unification until all lazily added locations have propagated
     * and no more changes are detected.
     * 
     * {@link LocationIdentifier} are added lazily to
     * {@link ExtendedReferenceLocation} instances. If a location is added to an
     * already unified {@link ExtendedReferenceLocation}, the unification has to
     * be applied again to ensure correctness of the result.
     */
    private void finalizePendingUnifications() {
        Deque<Pair<ReferenceLocation, ReferenceLocation>> worklist = new ArrayDeque<>();
        for (Map.Entry<ReferenceLocation, ReferenceLocation> locations : pendingUnifications.entries()) {
            ReferenceLocation refLoc1 = locations.getKey();
            ReferenceLocation refLoc2 = locations.getValue();

            int loc1Identifiers = refLoc1.getIdentifiers().size();
            int loc2Identifiers = refLoc2.getIdentifiers().size();

            if (loc1Identifiers != loc2Identifiers) {
                worklist.addFirst(ImmutablePair.of(refLoc1, refLoc2));
            }
        }

        while (!worklist.isEmpty()) {
            Pair<ReferenceLocation, ReferenceLocation> locations = worklist.removeFirst();
            ReferenceLocation loc1 = locations.getLeft();
            ReferenceLocation loc2 = locations.getRight();

            int previousIdentifiersLoc1 = loc1.getIdentifiers().size();
            int previousIdentifiersLoc2 = loc2.getIdentifiers().size();
            unify(loc1, loc2);

            updateUnificationWorklist(worklist, previousIdentifiersLoc1, loc1, loc2);
            updateUnificationWorklist(worklist, previousIdentifiersLoc2, loc2, loc1);
        }
    }

    private void updateUnificationWorklist(Deque<Pair<ReferenceLocation, ReferenceLocation>> worklist,
            int previousIdentifiersSize, ReferenceLocation location, ReferenceLocation unificationPartner) {
        int newIdentifiersSize = location.getIdentifiers().size();
        if (newIdentifiersSize != previousIdentifiersSize) {
            for (ReferenceLocation dependentLocation : pendingUnifications.get(location)) {
                if (dependentLocation != unificationPartner) {
                    worklist.addFirst(ImmutablePair.of(location, dependentLocation));
                }
            }
        }
    }

    private LocationIdentifier getIdentifierForSimpleRefLoc(ITypeName type) {
        if (type.isDelegateType()) {
            return LocationIdentifier.FUNCTION;
        } else {
            return LocationIdentifier.VALUE;
        }
    }

    private BottomLocation createBottomLocation() {
        SetRepresentative bottomRep = new SetRepresentative();
        unionFind.addElement(bottomRep);
        return new BottomLocation(bottomRep);
    }

    ReferenceLocation createSimpleReferenceLocation() {
        SetRepresentative valueBottomRep = new SetRepresentative();
        SetRepresentative funBottomRep = new SetRepresentative();

        SetRepresentative refRepresentative = new SetRepresentative();
        ReferenceLocation refLocation = new SimpleReferenceLocation(refRepresentative,
                new BottomLocation(valueBottomRep), new BottomLocation(funBottomRep));

        unionFind.addElement(valueBottomRep);
        unionFind.addElement(funBottomRep);
        unionFind.addElement(refRepresentative);
        return refLocation;
    }

    private ReferenceLocation createExtendedReferenceLocation() {
        SetRepresentative setRep = new SetRepresentative();
        unionFind.addElement(setRep);
        return new ExtendedReferenceLocation(setRep);
    }

    private void mergeIdentifiers(ReferenceLocation refLoc1, ReferenceLocation refLoc2) {
        Set<LocationIdentifier> loc1Identifiers = refLoc1.getIdentifiers();
        Set<LocationIdentifier> loc2Identifiers = refLoc2.getIdentifiers();

        {
            Set<LocationIdentifier> missingInLoc1 = new HashSet<>(loc2Identifiers);
            missingInLoc1.removeAll(loc1Identifiers);
            for (LocationIdentifier identifier : missingInLoc1) {
                refLoc1.setLocation(identifier, createBottomLocation());
            }
        }

        {
            Set<LocationIdentifier> missingInLoc2 = new HashSet<>(loc1Identifiers);
            missingInLoc2.removeAll(loc2Identifiers);
            for (LocationIdentifier identifier : missingInLoc2) {
                refLoc2.setLocation(identifier, createBottomLocation());
            }
        }
    }

    private FunctionLocation createFunctionLocation(ILambdaExpression lambdaExpr) {
        SetRepresentative functionRep = new SetRepresentative();
        unionFind.addElement(functionRep);
        return createFunctionLocation(lambdaExpr, functionRep);
    }

    private FunctionLocation createFunctionLocation(ILambdaExpression lambdaExpr, SetRepresentative functionRep) {
        List<IParameterName> lambdaParameters = lambdaExpr.getName().getParameters();
        List<ReferenceLocation> parameterLocations = new ArrayList<>(lambdaParameters.size());

        for (IParameterName formalParameter : lambdaParameters) {
            DistinctReference distRef = new DistinctLambdaParameterReference(formalParameter, lambdaExpr);
            ReferenceLocation formalParameterLocation = getOrCreateLocation(distRef);

            parameterLocations.add(formalParameterLocation);
        }

        Pair<ILambdaExpression, ReferenceLocation> currentLambdaEntry = lambdaStack.getFirst();
        Asserts.assertTrue(lambdaExpr == currentLambdaEntry.getKey());

        return new FunctionLocation(parameterLocations, currentLambdaEntry.getValue(), functionRep);
    }

    private FunctionLocation createFunctionLocation(IMethodName method) {
        SetRepresentative functionRep = new SetRepresentative();
        unionFind.addElement(functionRep);
        return createFunctionLocation(method, functionRep);
    }

    private FunctionLocation createFunctionLocation(IMethodName method, SetRepresentative functionRep) {
        List<IParameterName> methodParameters = method.getParameters();
        List<ReferenceLocation> parameterLocations = new ArrayList<>(methodParameters.size());

        for (IParameterName formalParameter : methodParameters) {
            DistinctMethodParameterReference distRef = new DistinctMethodParameterReference(formalParameter,
                    method);
            ReferenceLocation formalParameterLocation = getOrCreateLocation(distRef);

            parameterLocations.add(formalParameterLocation);
        }

        return new FunctionLocation(parameterLocations, getOrCreateReturnLocation(method), functionRep);
    }

    ReferenceLocation getOrCreateLocation(DistinctReference reference) {
        ReferenceLocation location = referenceLocations.get(reference);

        if (location == null) {
            location = createSimpleReferenceLocation();
            referenceLocations.put(reference, location);
        }

        return location;
    }

    ReferenceLocation getOrCreateReturnLocation(IMemberName member) {
        ReferenceLocation location = returnLocations.get(member);

        if (location == null) {
            location = createSimpleReferenceLocation();
            returnLocations.put(member, location);
        }

        return location;
    }

    private void registerMemberLocation(DistinctMemberReference memberRef, Location location) {
        if (!memberLocations.containsKey(memberRef)) {
            memberLocations.put(memberRef, location.getSetRepresentative());
        }
    }

    private void registerIndexAccessLocation(DistinctIndexAccessReference distRef, Location location) {
        if (!indexAccessLocations.containsKey(distRef)) {
            indexAccessLocations.put(distRef, location.getSetRepresentative());
        }
    }

    private void setLocation(SetRepresentative setRepresentative, Location newLoc) {
        setRepresentative.setLocation(newLoc);
        newLoc.setSetRepresentative(setRepresentative);

        for (SetRepresentative x : pending.get(setRepresentative)) {
            join(setRepresentative, x);
        }
        pending.removeAll(setRepresentative);

    }

    private void cjoin(SetRepresentative rep1, SetRepresentative rep2) {
        if (rep2.getLocation().isBottom()) {
            pending.put(rep2, rep1);
        } else {
            join(rep1, rep2);
        }
    }

    private void join(SetRepresentative rep1, SetRepresentative rep2) {
        rep1 = unionFind.find(rep1);
        rep2 = unionFind.find(rep2);
        if (rep1 == rep2) {
            // prevent indefinite loop when finalizing the set of pending joins
            return;
        }

        Location loc1 = rep1.getLocation();
        Location loc2 = rep2.getLocation();

        unionFind.union(rep1, rep2);
        SetRepresentative unionRep = unionFind.find(rep1);
        loc1.setSetRepresentative(unionRep);
        loc2.setSetRepresentative(unionRep);

        if (loc1.isBottom()) {
            unionRep.setLocation(loc2);
            if (loc2.isBottom()) {
                if (unionRep != rep1) {
                    pending.putAll(unionRep, pending.get(rep1));
                }
                if (unionRep != rep2) {
                    pending.putAll(unionRep, pending.get(rep2));
                }
            } else {
                for (SetRepresentative x : pending.get(rep1)) {
                    join(unionRep, x);
                }
                pending.removeAll(rep1);
            }
        } else {
            unionRep.setLocation(loc1);
            if (loc2.isBottom()) {
                for (SetRepresentative x : pending.get(rep2)) {
                    join(unionRep, x);
                }
                pending.removeAll(rep2);
            } else {
                unify(loc1, loc2);
            }

        }
    }

    private void unify(Location loc1, Location loc2) {
        if (loc1 instanceof ReferenceLocation && loc2 instanceof ReferenceLocation) {
            ReferenceLocation rloc1 = (ReferenceLocation) loc1;
            ReferenceLocation rloc2 = (ReferenceLocation) loc2;
            pendingUnifications.put(rloc1, rloc2);
            pendingUnifications.put(rloc2, rloc1);
            unify(rloc1, rloc2);
        } else if (loc1 instanceof FunctionLocation && loc2 instanceof FunctionLocation) {
            unify((FunctionLocation) loc1, (FunctionLocation) loc2);
        }
    }

    private void unify(ReferenceLocation refLoc1, ReferenceLocation refLoc2) {
        mergeIdentifiers(refLoc1, refLoc2);
        // copy set to list for iteration to avoid concurrent modification
        // exceptions
        List<LocationIdentifier> commonIdentifiers = new ArrayList<>(refLoc1.getIdentifiers());

        for (LocationIdentifier identifier : commonIdentifiers) {
            Location loc1 = refLoc1.getLocation(identifier);
            Location loc2 = refLoc2.getLocation(identifier);

            SetRepresentative rep1 = unionFind.find(loc1.getSetRepresentative());
            SetRepresentative rep2 = unionFind.find(loc2.getSetRepresentative());

            if (rep1 != rep2) {
                join(rep1, rep2);
            }
        }
    }

    private void unify(FunctionLocation funLoc1, FunctionLocation funLoc2) {
        List<ReferenceLocation> fun1Parameters = funLoc1.getParameterLocations();
        List<ReferenceLocation> fun2Parameters = funLoc2.getParameterLocations();

        int minParametersSize = Math.min(fun1Parameters.size(), fun2Parameters.size());
        int maxParametersSize = Math.max(fun1Parameters.size(), fun2Parameters.size());
        for (int i = 0; i < minParametersSize; ++i) {
            unify(fun1Parameters.get(i), fun2Parameters.get(i));
        }

        // function locations have a different number of parameters: copy over
        // the additional ones
        if (minParametersSize != maxParametersSize) {
            List<ReferenceLocation> minParameters = (fun1Parameters.size() == minParametersSize) ? fun1Parameters
                    : fun2Parameters;
            List<ReferenceLocation> maxParameters = (fun1Parameters.size() == maxParametersSize) ? fun1Parameters
                    : fun2Parameters;

            minParameters.addAll(maxParameters.subList(minParametersSize, maxParametersSize));
        }

        unify(funLoc1.getReturnLocation(), funLoc2.getReturnLocation());
    }

    public DistinctReference getDistinctReference(IReference reference) {
        return reference.accept(distinctReferenceCreationVisitor, namesToReferences);
    }

    public ReferenceLocation getLocation(IVariableReference varRef) {
        DistinctReference distRef = varRef.accept(distinctReferenceCreationVisitor, namesToReferences);
        return getOrCreateLocation(distRef);
    }

    @Override
    public void enterMember(IMemberName member) {
        super.enterMember(member);
        this.currentMember = member;
    }

    public void setLastAssignment(IAssignment assignment) {
        lastAssignment = assignment;
    }

    public IAssignableReference getDestinationForExpr(IAssignableExpression expr) {
        if (lastAssignment == null || lastAssignment.getReference() instanceof IUnknownReference
                || lastAssignment.getExpression() != expr) {
            return null;
        } else {
            return lastAssignment.getReference();
        }
    }

    public void enterLambda(ILambdaExpression lambdaExpr) {
        ReferenceLocation lambdaReturnLocation = createSimpleReferenceLocation();
        lambdaStack.addFirst(ImmutablePair.of(lambdaExpr, lambdaReturnLocation));
    }

    public void leaveLambda() {
        lambdaStack.removeFirst();
    }

    public void allocate(IAssignableReference destRef) {
        if (destRef instanceof IUnknownReference) {
            LOGGER.error("Ignoring an allocation due to an unknown reference");
            return;
        }

        ReferenceLocation destLocation = destRef.accept(destLocationVisitor, this);
        allocate(destLocation);
    }

    private void allocate(DistinctReference destRef) {
        allocate(getOrCreateLocation(destRef));
    }

    void allocate(ReferenceLocation destLocation) {
        Location derefDestLocation = unionFind
                .find(destLocation.getLocation(LocationIdentifier.VALUE).getSetRepresentative()).getLocation();

        if (derefDestLocation.isBottom()) {
            ReferenceLocation allocatedLocation = createExtendedReferenceLocation();
            setLocation(derefDestLocation.getSetRepresentative(), allocatedLocation);
            destLocation.setLocation(LocationIdentifier.VALUE, allocatedLocation);
        }
    }

    private Location readDereference(ReferenceLocation destLocation, ReferenceLocation srcLocation,
            LocationIdentifier destIdentifier, LocationIdentifier srcIdentifier) {
        SetRepresentative destDerefRep = unionFind
                .find(destLocation.getLocation(destIdentifier).getSetRepresentative());
        SetRepresentative srcDerefRep = unionFind
                .find(srcLocation.getLocation(LocationIdentifier.VALUE).getSetRepresentative());

        if (srcDerefRep.getLocation().isBottom()) {
            ReferenceLocation srcDerefLoc = new ExtendedReferenceLocation(srcDerefRep);
            Location dereferenceTargetLoc = destDerefRep.getLocation();
            if (dereferenceTargetLoc.isBottom()) {
                dereferenceTargetLoc = new ExtendedReferenceLocation(destDerefRep);
                setLocation(destDerefRep, dereferenceTargetLoc);
                destLocation.setLocation(destIdentifier, dereferenceTargetLoc);
            }
            srcDerefLoc.setLocation(srcIdentifier, dereferenceTargetLoc);
            setLocation(srcDerefRep, srcDerefLoc);
            srcLocation.setLocation(LocationIdentifier.VALUE, srcDerefLoc);

            return destDerefRep.getLocation();
        } else {
            ReferenceLocation srcDerefLocation = (ReferenceLocation) srcDerefRep.getLocation();
            Location dereferenceTargetLoc = srcDerefLocation.getLocation(srcIdentifier);
            if (dereferenceTargetLoc == null) {
                dereferenceTargetLoc = createExtendedReferenceLocation();
                srcDerefLocation.setLocation(srcIdentifier, dereferenceTargetLoc);
            }

            SetRepresentative dereferenceTargetRep = unionFind.find(dereferenceTargetLoc.getSetRepresentative());
            if (destDerefRep != dereferenceTargetRep) {
                cjoin(destDerefRep, dereferenceTargetRep);
            }

            return dereferenceTargetRep.getLocation();
        }
    }

    private Location writeDereference(ReferenceLocation destLocation, ReferenceLocation srcLocation,
            LocationIdentifier destIdentifier, LocationIdentifier srcIdentifier) {
        SetRepresentative destDerefRep = unionFind
                .find(destLocation.getLocation(LocationIdentifier.VALUE).getSetRepresentative());
        SetRepresentative srcDerefRep = unionFind
                .find(srcLocation.getLocation(srcIdentifier).getSetRepresentative());

        if (destDerefRep.getLocation().isBottom()) {
            ReferenceLocation destDerefLoc = new ExtendedReferenceLocation(destDerefRep);
            Location dereferenceTargetLoc = srcDerefRep.getLocation();
            if (dereferenceTargetLoc.isBottom()) {
                dereferenceTargetLoc = new ExtendedReferenceLocation(srcDerefRep);
                setLocation(srcDerefRep, dereferenceTargetLoc);
                srcLocation.setLocation(srcIdentifier, dereferenceTargetLoc);
            }
            destDerefLoc.setLocation(destIdentifier, srcDerefRep.getLocation());
            setLocation(destDerefRep, destDerefLoc);
            destLocation.setLocation(LocationIdentifier.VALUE, destDerefLoc);

            return srcDerefRep.getLocation();
        } else {
            ReferenceLocation destDerefLocation = (ReferenceLocation) destDerefRep.getLocation();
            Location dereferenceTargetLoc = destDerefLocation.getLocation(destIdentifier);
            if (dereferenceTargetLoc == null) {
                dereferenceTargetLoc = createExtendedReferenceLocation();
                destDerefLocation.setLocation(destIdentifier, dereferenceTargetLoc);
            }

            SetRepresentative dereferenceTargetRep = unionFind.find(dereferenceTargetLoc.getSetRepresentative());

            if (dereferenceTargetRep != srcDerefRep) {
                cjoin(dereferenceTargetRep, srcDerefRep);
            }

            return dereferenceTargetRep.getLocation();
        }
    }

    public void alias(IReference destRef, IVariableReference srcRef) {
        DistinctReference srcDistRef = srcRef.accept(distinctReferenceCreationVisitor, namesToReferences);
        ReferenceLocation srcLocation = getOrCreateLocation(srcDistRef);
        ReferenceLocation destLocation = destRef.accept(destLocationVisitor, this);
        alias(destLocation, srcLocation);
    }

    private void alias(DistinctReference dest, DistinctReference src) {
        Asserts.assertNotNull(dest);
        Asserts.assertNotNull(src);
        ReferenceLocation destLocation = getOrCreateLocation(dest);
        ReferenceLocation srcLocation = getOrCreateLocation(src);
        alias(destLocation, srcLocation);
    }

    void alias(ReferenceLocation destLocation, ReferenceLocation srcLocation) {
        mergeIdentifiers(destLocation, srcLocation);
        for (LocationIdentifier identifier : destLocation.getIdentifiers()) {
            alias(destLocation, srcLocation, identifier);
        }
    }

    private void alias(ReferenceLocation destLocation, ReferenceLocation srcLocation,
            LocationIdentifier identifier) {
        SetRepresentative derefDestRep = unionFind
                .find(destLocation.getLocation(identifier).getSetRepresentative());
        SetRepresentative derefSrcRep = unionFind.find(srcLocation.getLocation(identifier).getSetRepresentative());

        if (derefDestRep != derefSrcRep) {
            cjoin(derefDestRep, derefSrcRep);
        }
    }

    public void storeFunction(IReference destRef, ILambdaExpression lambdaExpr) {
        Asserts.assertTrue(lambdaExpr == lambdaStack.getFirst().getKey());

        ReferenceLocation destLocation = destRef.accept(destLocationVisitor, this);
        Location derefDestLocation = unionFind
                .find(destLocation.getLocation(LocationIdentifier.FUNCTION).getSetRepresentative()).getLocation();

        if (derefDestLocation.isBottom()) {
            FunctionLocation functionLocation = createFunctionLocation(lambdaExpr);
            setLocation(derefDestLocation.getSetRepresentative(), functionLocation);
            destLocation.setLocation(LocationIdentifier.FUNCTION, functionLocation);
        } else {
            FunctionLocation functionLocation = ensureProperFunctionLocation(
                    rep -> createFunctionLocation(lambdaExpr, rep), destLocation, derefDestLocation);

            List<IParameterName> lambdaParameters = lambdaExpr.getName().getParameters();
            List<DistinctReference> distLambdaParameters = new ArrayList<>(lambdaParameters.size());
            for (IParameterName lambdaParameter : lambdaParameters) {
                distLambdaParameters.add(new DistinctLambdaParameterReference(lambdaParameter, lambdaExpr));
            }

            ReferenceLocation lambdaReturnLocation = lambdaStack.getFirst().getValue();
            ITypeName lambdaReturnType = lambdaStack.getFirst().getKey().getName().getReturnType();

            updateFunctionLocation(functionLocation, distLambdaParameters, lambdaReturnType, lambdaReturnLocation);
        }

    }

    public void storeFunction(IReference destRef, IMethodReference methodRef) {
        ReferenceLocation destLocation = destRef.accept(destLocationVisitor, this);
        storeFunction(destLocation, methodRef);
    }

    void storeFunction(ReferenceLocation destLocation, IMethodReference methodRef) {
        IMethodName method = methodRef.getMethodName();

        Location derefDestLocation = unionFind
                .find(destLocation.getLocation(LocationIdentifier.FUNCTION).getSetRepresentative()).getLocation();

        if (derefDestLocation.isBottom()) {
            FunctionLocation functionLocation = createFunctionLocation(method);
            setLocation(derefDestLocation.getSetRepresentative(), functionLocation);
            destLocation.setLocation(LocationIdentifier.FUNCTION, functionLocation);
        } else {
            FunctionLocation functionLocation = ensureProperFunctionLocation(
                    rep -> createFunctionLocation(method, rep), destLocation, derefDestLocation);

            List<IParameterName> methodParameters = method.getParameters();
            List<DistinctReference> distMethodParameters = new ArrayList<>(methodParameters.size());
            for (IParameterName parameter : methodParameters) {
                distMethodParameters.add(new DistinctMethodParameterReference(parameter, method));
            }

            updateFunctionLocation(functionLocation, distMethodParameters, method.getReturnType(),
                    getOrCreateReturnLocation(method));
        }
    }

    private void updateFunctionLocation(FunctionLocation functionLocation, List<DistinctReference> newParameters,
            ITypeName newReturnType, ReferenceLocation newReturnLocation) {
        List<ReferenceLocation> funLocParameters = functionLocation.getParameterLocations();
        List<ReferenceLocation> newParameterLocations = new ArrayList<>(newParameters.size());
        for (DistinctReference distParameterRef : newParameters) {
            ReferenceLocation newParameterLocation = getOrCreateLocation(distParameterRef);
            newParameterLocations.add(newParameterLocation);
        }

        // the number of parameters can be different in rare cases
        int commonNumParameters = Math.min(funLocParameters.size(), newParameters.size());
        for (int i = 0; i < commonNumParameters; ++i) {
            DistinctReference distParameterRef = newParameters.get(i);
            ReferenceLocation newParameterLocation = newParameterLocations.get(i);
            LocationIdentifier newParameterIdentifier = getIdentifierForSimpleRefLoc(distParameterRef.getType());
            SetRepresentative derefNewParameterLocationRep = unionFind
                    .find(newParameterLocation.getLocation(newParameterIdentifier).getSetRepresentative());
            SetRepresentative derefFunLocParameterRep = unionFind
                    .find(funLocParameters.get(i).getLocation(newParameterIdentifier).getSetRepresentative());

            if (derefFunLocParameterRep != derefNewParameterLocationRep) {
                join(derefNewParameterLocationRep, derefFunLocParameterRep);
            }
        }
        if (newParameterLocations.size() > funLocParameters.size()) {
            funLocParameters
                    .addAll(newParameterLocations.subList(commonNumParameters, newParameterLocations.size()));
        }

        LocationIdentifier newReturnIdentifier = getIdentifierForSimpleRefLoc(newReturnType);
        SetRepresentative derefNewReturnLocRep = unionFind
                .find(newReturnLocation.getLocation(newReturnIdentifier).getSetRepresentative());
        SetRepresentative derefFunLocReturnRep = unionFind
                .find(functionLocation.getReturnLocation().getLocation(newReturnIdentifier).getSetRepresentative());
        if (derefFunLocReturnRep != derefNewReturnLocRep) {
            join(derefFunLocReturnRep, derefNewReturnLocRep);
        }
    }

    public FunctionLocation invokeDelegate(IInvocationExpression invocation) {
        IMethodName method = invocation.getMethodName();

        DistinctReference receiverRef = invocation.getReference().accept(distinctReferenceCreationVisitor,
                namesToReferences);
        ReferenceLocation receiverLoc = getOrCreateLocation(receiverRef);
        Location derefReceiverLoc = unionFind
                .find(receiverLoc.getLocation(LocationIdentifier.FUNCTION).getSetRepresentative()).getLocation();

        FunctionLocation functionLocation;
        if (derefReceiverLoc.isBottom()) {
            functionLocation = createFunctionLocation(method);
            setLocation(derefReceiverLoc.getSetRepresentative(), functionLocation);
            receiverLoc.setLocation(LocationIdentifier.FUNCTION, functionLocation);
        } else {
            functionLocation = ensureProperFunctionLocation(rep -> createFunctionLocation(method, rep), receiverLoc,
                    derefReceiverLoc);
        }

        return functionLocation;
    }

    private FunctionLocation ensureProperFunctionLocation(
            Function<SetRepresentative, FunctionLocation> funLocCreator, ReferenceLocation location,
            Location derefLocation) {
        if (derefLocation instanceof FunctionLocation) {
            return (FunctionLocation) derefLocation;
        } else {
            // location got initialized by readDereference or writeDereference
            // to an ExtendedRefernceLocation as we
            // cannot simply extract the method name at that stage
            FunctionLocation functionLocation = funLocCreator.apply(derefLocation.getSetRepresentative());
            // swap locations
            derefLocation.getSetRepresentative().setLocation(functionLocation);
            location.setLocation(LocationIdentifier.FUNCTION, functionLocation);

            return functionLocation;
        }
    }

    public List<ReferenceLocation> getMethodParameterLocations(IMethodName method) {
        List<IParameterName> formalParameters = method.getParameters();
        List<ReferenceLocation> parameterLocations = new ArrayList<>(formalParameters.size());
        for (IParameterName parameter : formalParameters) {
            DistinctReference distParameterRef = new DistinctMethodParameterReference(parameter, method);
            parameterLocations.add(getOrCreateLocation(distParameterRef));
        }

        return parameterLocations;
    }

    public ReferenceLocation getMethodReturnLocation(IMethodName method) {
        return getOrCreateReturnLocation(method);
    }

    public void assign(IFieldReference dest, IFieldReference src) {
        assign((DistinctFieldReference) dest.accept(distinctReferenceCreationVisitor, namesToReferences),
                (DistinctFieldReference) src.accept(distinctReferenceCreationVisitor, namesToReferences));
    }

    private void assign(DistinctFieldReference dest, DistinctFieldReference src) {
        ReferenceLocation tempLoc = createSimpleReferenceLocation();
        readMember(tempLoc, src);
        writeMember(dest, tempLoc);
    }

    public void assign(IPropertyReference dest, IPropertyReference src) {
        assign((DistinctPropertyReference) dest.accept(distinctReferenceCreationVisitor, namesToReferences),
                (DistinctPropertyReference) src.accept(distinctReferenceCreationVisitor, namesToReferences));
    }

    private void assign(DistinctPropertyReference dest, DistinctPropertyReference src) {
        ReferenceLocation tempLoc = createSimpleReferenceLocation();
        IPropertyName srcProperty = src.getReference().getPropertyName();
        if (treatPropertyAsField(src.getReference())) {
            readMember(tempLoc, src);
        } else {
            ReferenceLocation returnLocation = getOrCreateReturnLocation(srcProperty);
            alias(tempLoc, returnLocation);
        }

        IPropertyName destProperty = dest.getReference().getPropertyName();
        if (treatPropertyAsField(dest.getReference())) {
            writeMember(dest, tempLoc);
        } else {
            DistinctReference destPropertyParameter = new DistinctPropertyParameterReference(languageOptions,
                    destProperty);
            alias(getOrCreateLocation(destPropertyParameter), tempLoc);
        }
    }

    public void assign(IPropertyReference dest, IFieldReference src) {
        assign((DistinctPropertyReference) dest.accept(distinctReferenceCreationVisitor, namesToReferences),
                (DistinctFieldReference) src.accept(distinctReferenceCreationVisitor, namesToReferences));
    }

    private void assign(DistinctPropertyReference dest, DistinctFieldReference src) {
        ReferenceLocation tempLoc = createSimpleReferenceLocation();
        readMember(tempLoc, src);

        IPropertyName destProperty = dest.getReference().getPropertyName();
        if (treatPropertyAsField(dest.getReference())) {
            writeMember(dest, tempLoc);
        } else {
            DistinctReference destPropertyParameter = new DistinctPropertyParameterReference(languageOptions,
                    destProperty);
            alias(getOrCreateLocation(destPropertyParameter), tempLoc);
        }
    }

    public void assign(IFieldReference dest, IPropertyReference src) {
        assign((DistinctFieldReference) dest.accept(distinctReferenceCreationVisitor, namesToReferences),
                (DistinctPropertyReference) src.accept(distinctReferenceCreationVisitor, namesToReferences));
    }

    private void assign(DistinctFieldReference dest, DistinctPropertyReference src) {
        ReferenceLocation tempLoc = createSimpleReferenceLocation();
        IPropertyName srcProperty = src.getReference().getPropertyName();
        if (treatPropertyAsField(src.getReference())) {
            readMember(tempLoc, src);
        } else {
            ReferenceLocation returnLocation = getOrCreateReturnLocation(srcProperty);
            alias(tempLoc, returnLocation);
        }

        writeMember(dest, tempLoc);
    }

    public void assign(IFieldReference dest, IIndexAccessReference src) {
        assign((DistinctFieldReference) dest.accept(distinctReferenceCreationVisitor, namesToReferences),
                (DistinctIndexAccessReference) src.accept(distinctReferenceCreationVisitor, namesToReferences));
    }

    private void assign(DistinctFieldReference dest, DistinctIndexAccessReference src) {
        ReferenceLocation tempLoc = createSimpleReferenceLocation();
        readArray(tempLoc, src);
        writeMember(dest, tempLoc);
    }

    public void assign(IPropertyReference dest, IIndexAccessReference src) {
        assign((DistinctPropertyReference) dest.accept(distinctReferenceCreationVisitor, namesToReferences),
                (DistinctIndexAccessReference) src.accept(distinctReferenceCreationVisitor, namesToReferences));
    }

    private void assign(DistinctPropertyReference dest, DistinctIndexAccessReference src) {
        ReferenceLocation tempLoc = createSimpleReferenceLocation();
        readArray(tempLoc, src);

        IPropertyName destProperty = dest.getReference().getPropertyName();
        if (treatPropertyAsField(dest.getReference())) {
            writeMember(dest, tempLoc);
        } else {
            DistinctReference destPropertyParameter = new DistinctPropertyParameterReference(languageOptions,
                    destProperty);
            alias(getOrCreateLocation(destPropertyParameter), tempLoc);
        }
    }

    public void assign(IIndexAccessReference dest, IFieldReference src) {
        assign((DistinctIndexAccessReference) dest.accept(distinctReferenceCreationVisitor, namesToReferences),
                (DistinctFieldReference) src.accept(distinctReferenceCreationVisitor, namesToReferences));
    }

    private void assign(DistinctIndexAccessReference dest, DistinctFieldReference src) {
        ReferenceLocation tempLoc = createSimpleReferenceLocation();
        readMember(tempLoc, src);
        writeArray(dest, tempLoc);
    }

    public void assign(IIndexAccessReference dest, IPropertyReference src) {
        assign((DistinctIndexAccessReference) dest.accept(distinctReferenceCreationVisitor, namesToReferences),
                (DistinctPropertyReference) src.accept(distinctReferenceCreationVisitor, namesToReferences));
    }

    private void assign(DistinctIndexAccessReference dest, DistinctPropertyReference src) {
        ReferenceLocation tempLoc = createSimpleReferenceLocation();

        IPropertyName srcProperty = src.getReference().getPropertyName();
        if (treatPropertyAsField(src.getReference())) {
            readMember(tempLoc, src);
        } else {
            ReferenceLocation returnLocation = getOrCreateReturnLocation(srcProperty);
            alias(tempLoc, returnLocation);
        }

        writeArray(dest, tempLoc);
    }

    public void assign(IIndexAccessReference dest, IIndexAccessReference src) {
        DistinctIndexAccessReference distDestRef = (DistinctIndexAccessReference) dest
                .accept(distinctReferenceCreationVisitor, namesToReferences);
        DistinctIndexAccessReference distSrcRef = (DistinctIndexAccessReference) src
                .accept(distinctReferenceCreationVisitor, namesToReferences);
        assign(distDestRef, distSrcRef);
    }

    private void assign(DistinctIndexAccessReference dest, DistinctIndexAccessReference src) {
        ReferenceLocation tempLoc = createSimpleReferenceLocation();
        readArray(tempLoc, src);
        writeArray(dest, tempLoc);
    }

    public void assign(IEventReference dest, IFieldReference src) {
        ReferenceLocation tempLoc = dest.accept(destLocationVisitor, this);
        readMember(tempLoc, src);
    }

    public void assign(IEventReference dest, IPropertyReference src) {
        ReferenceLocation tempLoc = dest.accept(destLocationVisitor, this);
        if (treatPropertyAsField(src)) {
            readMember(tempLoc, src);
        } else {
            ReferenceLocation returnLocation = getOrCreateReturnLocation(src.getPropertyName());
            alias(tempLoc, returnLocation);
        }
    }

    public void assign(IEventReference dest, IEventReference src) {
        ReferenceLocation destLoc = dest.accept(destLocationVisitor, this);
        ReferenceLocation srcLoc = src.accept(srcLocationVisitor, this);
        alias(destLoc, srcLoc);
    }

    public void readField(IVariableReference destRef, IFieldReference fieldRef) {
        readField(destRef.accept(distinctReferenceCreationVisitor, namesToReferences), fieldRef);
    }

    private void readField(DistinctReference destRef, IFieldReference fieldRef) {
        readMember(getOrCreateLocation(destRef), fieldRef);
    }

    private void readMember(ReferenceLocation destLocation, IMemberReference memberRef) {
        DistinctMemberReference distinctMemberRef = (DistinctMemberReference) memberRef
                .accept(distinctReferenceCreationVisitor, namesToReferences);
        readMember(destLocation, distinctMemberRef);
    }

    void readMember(ReferenceLocation destLocation, DistinctMemberReference memberRef) {
        if (memberRef.isStaticMember()) {
            // model static members (fields, properties) as global variables
            alias(destLocation, getOrCreateLocation(memberRef));
        } else {

            ReferenceLocation srcLocation = getOrCreateLocation(memberRef.getBaseReference());
            LocationIdentifier destIdentifier = getIdentifierForSimpleRefLoc(memberRef.getType());
            LocationIdentifier srcIdentifier = identifierFactory.create(memberRef.getReference());
            Location srcDerefLocation = readDereference(destLocation, srcLocation, destIdentifier, srcIdentifier);

            registerMemberLocation(memberRef, srcDerefLocation);
        }
    }

    public void writeField(IFieldReference fieldRef, IVariableReference srcRef) {
        writeField(fieldRef, srcRef.accept(distinctReferenceCreationVisitor, namesToReferences));
    }

    private void writeField(IFieldReference fieldRef, DistinctReference srcRef) {
        writeField((DistinctFieldReference) fieldRef.accept(distinctReferenceCreationVisitor, namesToReferences),
                srcRef);
    }

    private void writeField(DistinctFieldReference fieldRef, DistinctReference srcRef) {
        ReferenceLocation srcLocation = getOrCreateLocation(srcRef);
        writeMember(fieldRef, srcLocation);
    }

    void writeMember(DistinctMemberReference memberRef, ReferenceLocation srcLocation) {
        if (memberRef.isStaticMember()) {
            // model static members (fields, properties) as global variables
            alias(getOrCreateLocation(memberRef), srcLocation);
        } else {

            ReferenceLocation destLocation = getOrCreateLocation(memberRef.getBaseReference());
            LocationIdentifier destIdentifier = identifierFactory.create(memberRef.getReference());
            // we live in a type safe environment (src must be a delegate type
            // as well if the member is one)
            LocationIdentifier srcIdentifier = getIdentifierForSimpleRefLoc(memberRef.getType());
            Location destDerefLocation = writeDereference(destLocation, srcLocation, destIdentifier, srcIdentifier);

            registerMemberLocation(memberRef, destDerefLocation);
        }
    }

    public void readProperty(IVariableReference destRef, IPropertyReference propertyRef) {
        readProperty(destRef.accept(distinctReferenceCreationVisitor, namesToReferences), propertyRef);
    }

    private void readProperty(DistinctReference destRef, IPropertyReference propertyRef) {
        readProperty(destRef, (DistinctPropertyReference) propertyRef.accept(distinctReferenceCreationVisitor,
                namesToReferences));
    }

    private void readProperty(DistinctReference destRef, DistinctPropertyReference propertyRef) {
        ReferenceLocation destLocation = getOrCreateLocation(destRef);
        IPropertyName property = propertyRef.getReference().getPropertyName();
        if (treatPropertyAsField(propertyRef.getReference())) {
            readMember(destLocation, propertyRef);
        } else {
            ReferenceLocation returnLocation = getOrCreateReturnLocation(property);
            alias(destLocation, returnLocation);
        }
    }

    public void writeProperty(IPropertyReference propertyRef, IVariableReference srcRef) {
        writeProperty(propertyRef, srcRef.accept(distinctReferenceCreationVisitor, namesToReferences));
    }

    private void writeProperty(IPropertyReference propertyRef, DistinctReference srcRef) {
        writeProperty(
                (DistinctPropertyReference) propertyRef.accept(distinctReferenceCreationVisitor, namesToReferences),
                srcRef);
    }

    private void writeProperty(DistinctPropertyReference propertyRef, DistinctReference srcRef) {
        IPropertyName property = propertyRef.getReference().getPropertyName();
        if (treatPropertyAsField(propertyRef.getReference())) {
            ReferenceLocation srcLocation = getOrCreateLocation(srcRef);
            writeMember(propertyRef, srcLocation);
        } else {
            // map parameter of the property setter to the source
            alias(new DistinctPropertyParameterReference(languageOptions, property), srcRef);
        }
    }

    public boolean treatPropertyAsField(IPropertyReference propertyRef) {
        return treatPropertyAsField.test(propertyRef.getPropertyName());
    }

    public void readArray(IVariableReference destRef, IIndexAccessReference srcRef) {
        DistinctReference dest = destRef.accept(distinctReferenceCreationVisitor, namesToReferences);
        DistinctIndexAccessReference src = (DistinctIndexAccessReference) srcRef
                .accept(distinctReferenceCreationVisitor, namesToReferences);
        readArray(dest, src);
    }

    private void readArray(DistinctReference destRef, DistinctIndexAccessReference srcRef) {
        ReferenceLocation destLocation = getOrCreateLocation(destRef);
        readArray(destLocation, srcRef);
    }

    void readArray(ReferenceLocation destLocation, DistinctIndexAccessReference srcRef) {
        ReferenceLocation srcLocation = getOrCreateLocation(srcRef.getBaseReference());
        LocationIdentifier destIdentifier = getIdentifierForSimpleRefLoc(srcRef.getType());
        LocationIdentifier srcIdentifier = identifierFactory.create(srcRef.getReference(), srcRef.getType());
        Location srcDerefLocation = readDereference(destLocation, srcLocation, destIdentifier, srcIdentifier);

        registerIndexAccessLocation(srcRef, srcDerefLocation);
    }

    public void writeArray(IIndexAccessReference destRef, IReference srcRef) {
        DistinctIndexAccessReference distDestRef = (DistinctIndexAccessReference) destRef
                .accept(distinctReferenceCreationVisitor, namesToReferences);
        writeArray(distDestRef, srcRef);
    }

    void writeArray(DistinctIndexAccessReference destRef, IReference srcRef) {
        ReferenceLocation srcLocaction = srcRef.accept(srcLocationVisitor, this);
        writeArray(destRef, srcLocaction);
    }

    void writeArray(DistinctIndexAccessReference destRef, ReferenceLocation srcLocation) {
        ReferenceLocation destLocation = getOrCreateLocation(destRef.getBaseReference());
        LocationIdentifier destIdentifier = identifierFactory.create(destRef.getReference(), destRef.getType());
        LocationIdentifier srcIdentifier = getIdentifierForSimpleRefLoc(destRef.getType());
        Location destDerefLocation = writeDereference(destLocation, srcLocation, destIdentifier, srcIdentifier);

        registerIndexAccessLocation(destRef, destDerefLocation);
    }

    public void registerParameterReference(ReferenceLocation parameterLocation,
            IVariableReference actualParameter) {
        alias(parameterLocation, getLocation(actualParameter));
    }

    public void registerParameterReference(ReferenceLocation parameterLocation, IFieldReference actualParameter) {
        readMember(parameterLocation, actualParameter);
    }

    public void registerParameterReference(ReferenceLocation parameterLocation,
            IPropertyReference actualParameter) {
        readMember(parameterLocation, actualParameter);
    }

    public void registerParameterReference(ReferenceLocation parameterLocation, IMethodReference actualParameter) {
        storeFunction(parameterLocation, actualParameter);
    }

    public void registerReturnedReference(IReference ref) {
        if (ref instanceof IUnknownReference) {
            LOGGER.error("Ignoring returning of an unknown reference");
        } else {
            ReferenceLocation returnLocation;
            if (lambdaStack.isEmpty()) {
                returnLocation = getOrCreateReturnLocation(currentMember);
            } else {
                returnLocation = lambdaStack.getFirst().getValue();
            }
            ReferenceLocation srcLocation = ref.accept(srcLocationVisitor, this);

            alias(returnLocation, srcLocation);
        }
    }

    public void storeReturn(IAssignableReference destRef, ReferenceLocation returnLocation) {
        ReferenceLocation destLocation = destRef.accept(destLocationVisitor, this);
        alias(destLocation, returnLocation);
    }
}