Skip to content

Commit

Permalink
Separated memoization from signature matching
Browse files Browse the repository at this point in the history
  • Loading branch information
oowekyala committed Aug 4, 2017
1 parent 909f989 commit 152dddf
Show file tree
Hide file tree
Showing 22 changed files with 271 additions and 170 deletions.
Expand Up @@ -63,7 +63,7 @@ public double computeWithResultOption(MetricKey<O> key, T node, boolean force, M
List<Double> values = new ArrayList<>();
for (O op : ops) {
if (key.supports(op)) {
MetricMemoizer<O> opStats = stats.getOperationStats(op.getQualifiedName());
MetricMemoizer<O> opStats = stats.getOperationMemoizer(op.getQualifiedName());
double val = this.computeForOperation(key, op, force, version, opStats);
if (val != Double.NaN) {
values.add(val);
Expand Down
Expand Up @@ -30,9 +30,9 @@ public abstract class AbstractMetricsFacade<T extends QualifiableNode, O extends


/**
* Gets the language-specific project mirror.
* Gets the language-specific project memoizer.
*
* @return The project mirror
* @return The project memoizer
*/
protected abstract ProjectMemoizer<T, O> getLanguageSpecificProjectMemoizer();

Expand All @@ -56,7 +56,7 @@ public double computeForType(MetricKey<T> key, T node, MetricVersion version) {
}

MetricVersion safeVersion = (version == null) ? Version.STANDARD : version;
MetricMemoizer<T> memoizer = getLanguageSpecificProjectMemoizer().getClassStats(node.getQualifiedName());
MetricMemoizer<T> memoizer = getLanguageSpecificProjectMemoizer().getClassMemoizer(node.getQualifiedName());

return memoizer == null ? Double.NaN
: getLanguageSpecificComputer().computeForType(key, node, false, safeVersion, memoizer);
Expand All @@ -82,7 +82,7 @@ public double computeForOperation(MetricKey<O> key, O node,
}

MetricVersion safeVersion = (version == null) ? Version.STANDARD : version;
MetricMemoizer<O> memoizer = getLanguageSpecificProjectMemoizer().getOperationStats(node.getQualifiedName());
MetricMemoizer<O> memoizer = getLanguageSpecificProjectMemoizer().getOperationMemoizer(node.getQualifiedName());

return memoizer == null ? Double.NaN
: getLanguageSpecificComputer().computeForOperation(key, node, false,
Expand Down
Expand Up @@ -10,13 +10,13 @@
import net.sourceforge.pmd.lang.ast.Node;

/**
* Base class for metric memoizers.
* Basic implementation of a metric memoizer.
*
* @param <N> Type of node on which the memoized metric can be computed
*
* @author Clément Fournier
*/
public abstract class AbstractMetricMemoizer<N extends Node> implements MetricMemoizer<N> {
public class BasicMetricMemoizer<N extends Node> implements MetricMemoizer<N> {


private final Map<ParameterizedMetricKey<N>, Double> memo = new HashMap<>();
Expand Down
@@ -0,0 +1,50 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/

package net.sourceforge.pmd.lang.metrics;

import java.util.HashMap;
import java.util.Map;

import net.sourceforge.pmd.lang.ast.QualifiableNode;
import net.sourceforge.pmd.lang.ast.QualifiedName;

/**
* Simple implementation of a project memoizer. Memoizers are accessible in constant time, provided the QualifiedName's
* hashCode is well distributed.
*
* @param <T> Type of type declaration nodes of the language
* @param <O> Type of operation declaration nodes of the language
*
* @author Clément Fournier
*/
public class BasicProjectMemoizer<T extends QualifiableNode, O extends QualifiableNode> implements ProjectMemoizer<T, O> {

private Map<QualifiedName, MetricMemoizer<T>> classes = new HashMap<>();
private Map<QualifiedName, MetricMemoizer<O>> operations = new HashMap<>();


@Override
public void addClassMemoizer(QualifiedName qname) {
classes.put(qname, new BasicMetricMemoizer<T>());
}


@Override
public void addOperationMemoizer(QualifiedName qname) {
operations.put(qname, new BasicMetricMemoizer<O>());
}


@Override
public MetricMemoizer<O> getOperationMemoizer(QualifiedName qname) {
return operations.get(qname);
}


@Override
public MetricMemoizer<T> getClassMemoizer(QualifiedName qname) {
return classes.get(qname);
}
}
Expand Up @@ -7,7 +7,8 @@
import net.sourceforge.pmd.lang.ast.Node;

/**
* Umbrella marker interface for metrics.
* Object computing a metric on a node. Metric objects are be stateless, which means that instances of the same
* metric are all equal.
*
* @param <N> Type of nodes the metric can be computed on
*
Expand Down
Expand Up @@ -7,7 +7,8 @@
import net.sourceforge.pmd.lang.ast.Node;

/**
* Objects capable of memoizing metrics for a specific type of node, see eg ClassStats in the Java framework.
* Objects capable of memoizing metrics for a specific type of node. A default implementation is provided, see {@link
* BasicMetricMemoizer}.
*
* @param <N> Type of node on which the memoized metric can be computed
*
Expand Down
Expand Up @@ -8,18 +8,8 @@
import net.sourceforge.pmd.lang.ast.QualifiedName;

/**
* Object storing the memoizers of the analysed project, like PackageStats for Java. If retrieving eg an operation stats
* is expensive, consider implementing a cache.
*
* <p>Language specific implementations should implement some signature matching utilities for metrics to use. The
* details of how the mirror and its subcomponents are built must be kept out of the interfaces and visible only to the
* language specific metric package. Metric implementations, even standard ones, should not have access to it.
*
* <p>While classes and operations are widespread and vary little in form across (class based at least) object-oriented
* languages, the structure of a project is very language specific. For example, while an intuitive way to represent a
* Java project is with a package tree, Apex has no package system. We consider here that there's no point providing
* base implementations, so we use an interface. You could even implement it with a bunch of maps, which may yield good
* results! // TODO:cf investigate that
* Object storing the memoizers of the analysed project. This object should ideally be kept separate from the
* SignatureMatcher if there is one. A base implementation is available, see {@link BasicProjectMemoizer}.
*
* @param <T> Type of type declaration nodes of the language
* @param <O> Type of operation declaration nodes of the language
Expand All @@ -35,7 +25,7 @@ public interface ProjectMemoizer<T extends QualifiableNode, O extends Qualifiabl
*
* @return The correct memoizer, or null if it wasn't found
*/
MetricMemoizer<O> getOperationStats(QualifiedName qname);
MetricMemoizer<O> getOperationMemoizer(QualifiedName qname);


/**
Expand All @@ -45,6 +35,23 @@ public interface ProjectMemoizer<T extends QualifiableNode, O extends Qualifiabl
*
* @return The correct memoizer, or null if it wasn't found
*/
MetricMemoizer<T> getClassStats(QualifiedName qname);
MetricMemoizer<T> getClassMemoizer(QualifiedName qname);


/**
* Adds a memoizer for the class identified by this qualified name.
*
* @param qname The qualified name of the class
*/
void addClassMemoizer(QualifiedName qname);


/**
* Adds a memoizer for the operation identified by this qualified name.
*
* @param qname The qualified name of the operations
*/
void addOperationMemoizer(QualifiedName qname);


}
Expand Up @@ -11,7 +11,7 @@
import net.sourceforge.pmd.lang.ast.QualifiedName;

/**
* Represents Qualified Names for use within the java project mirror.
* Represents Qualified Names for use within the java metrics framework.
*/
public final class JavaQualifiedName implements QualifiedName {

Expand Down
Expand Up @@ -38,7 +38,17 @@ protected List<JavaQualifiedName> findAllCalls(ASTMethodOrConstructorDeclaration
* @return A signature matcher
*/
protected static JavaSignatureMatcher getSignatureMatcher() {
return JavaMetrics.getTopLevelPackageStats();
return JavaMetrics.getFacade().getTopLevelPackageStats();
}

@Override
public final boolean equals(Object o) {
return o != null && o.getClass() == this.getClass();
}

@Override
public final int hashCode() {
return getClass().hashCode();
}

}
Expand Up @@ -9,13 +9,11 @@
import java.util.Map;
import java.util.Set;

import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.JavaQualifiedName;
import net.sourceforge.pmd.lang.java.metrics.signature.JavaFieldSigMask;
import net.sourceforge.pmd.lang.java.metrics.signature.JavaFieldSignature;
import net.sourceforge.pmd.lang.java.metrics.signature.JavaOperationSigMask;
import net.sourceforge.pmd.lang.java.metrics.signature.JavaOperationSignature;
import net.sourceforge.pmd.lang.metrics.AbstractMetricMemoizer;

/**
* Statistics about a class, enum, interface, or annotation. Stores information about the contained members and their
Expand All @@ -28,7 +26,7 @@
*
* @author Clément Fournier
*/
/* default */ class ClassStats extends AbstractMetricMemoizer<ASTAnyTypeDeclaration> {
/* default */ class ClassStats {

private Map<JavaOperationSignature, Map<String, OperationStats>> operations = new HashMap<>();
private Map<JavaFieldSignature, Set<String>> fields = new HashMap<>();
Expand Down
Expand Up @@ -29,12 +29,12 @@ private JavaMetrics() { // Cannot be instantiated


/**
* Returns the project mirror of the analysed project.
* Returns the underlying façade.
*
* @return The project mirror
* @return The underlying façade instance
*/
static PackageStats getTopLevelPackageStats() {
return FACADE.getLanguageSpecificProjectMemoizer();
static JavaMetricsFacade getFacade() {
return FACADE;
}


Expand Down
Expand Up @@ -17,6 +17,7 @@
class JavaMetricsFacade extends AbstractMetricsFacade<ASTAnyTypeDeclaration, ASTMethodOrConstructorDeclaration> {

private final PackageStats topLevelPackageStats = new PackageStats();
private final JavaProjectMemoizer memoizer = new JavaProjectMemoizer();


/** Resets the entire data structure. Used for tests. */
Expand All @@ -25,12 +26,22 @@ void reset() {
}


@Override
public PackageStats getLanguageSpecificProjectMemoizer() {
/**
* Gets the top level package stats of this façade.
*
* @return The top level package stats
*/
PackageStats getTopLevelPackageStats() {
return topLevelPackageStats;
}


@Override
public JavaProjectMemoizer getLanguageSpecificProjectMemoizer() {
return memoizer;
}


@Override
protected MetricsComputer<ASTAnyTypeDeclaration, ASTMethodOrConstructorDeclaration> getLanguageSpecificComputer() {
return JavaMetricsComputer.INSTANCE;
Expand Down
Expand Up @@ -10,6 +10,7 @@
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorReducedAdapter;
import net.sourceforge.pmd.lang.metrics.ProjectMemoizer;

/**
* Visitor for the metrics framework, that fills a {@link PackageStats} object with the signatures of operations and
Expand All @@ -19,12 +20,22 @@
*/
class JavaMetricsVisitor extends JavaParserVisitorReducedAdapter {

private Stack<ClassStats> stack = new Stack<>();
private final Stack<ClassStats> stack = new Stack<>();
private final PackageStats toplevel;
private final ProjectMemoizer<ASTAnyTypeDeclaration, ASTMethodOrConstructorDeclaration> memoizer;


JavaMetricsVisitor(PackageStats toplevel, ProjectMemoizer<ASTAnyTypeDeclaration, ASTMethodOrConstructorDeclaration> memoizer) {
this.toplevel = toplevel;
this.memoizer = memoizer;
}


@Override
public Object visit(ASTAnyTypeDeclaration node, Object data) {
stack.push(((PackageStats) data).getClassStats(node.getQualifiedName(), true));
memoizer.addClassMemoizer(node.getQualifiedName());

stack.push(toplevel.getClassStats(node.getQualifiedName(), true));
super.visit(node, data);
stack.pop();

Expand All @@ -34,6 +45,8 @@ public Object visit(ASTAnyTypeDeclaration node, Object data) {

@Override
public Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
memoizer.addOperationMemoizer(node.getQualifiedName());

stack.peek().addOperation(node.getQualifiedName().getOperation(), node.getSignature());
return super.visit(node, data);
}
Expand Down
Expand Up @@ -15,8 +15,10 @@
public class JavaMetricsVisitorFacade extends JavaParserVisitorAdapter {

public void initializeWith(ASTCompilationUnit rootNode) {
JavaMetricsVisitor visitor = new JavaMetricsVisitor();
rootNode.jjtAccept(visitor, JavaMetrics.getTopLevelPackageStats());
JavaMetricsFacade facade = JavaMetrics.getFacade();
JavaMetricsVisitor visitor = new JavaMetricsVisitor(facade.getTopLevelPackageStats(),
facade.getLanguageSpecificProjectMemoizer());
rootNode.jjtAccept(visitor, null);
}

}
Expand Up @@ -6,13 +6,13 @@

import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.metrics.ProjectMemoizer;
import net.sourceforge.pmd.lang.metrics.BasicProjectMemoizer;

/**
* Shorthand for a project mirror parameterized with Java-specific node types.
* Shorthand for a project memoizer parameterized with Java-specific node types.
*
* @author Clément Fournier
*/
interface JavaProjectMemoizer extends ProjectMemoizer<ASTAnyTypeDeclaration, ASTMethodOrConstructorDeclaration> {
class JavaProjectMemoizer extends BasicProjectMemoizer<ASTAnyTypeDeclaration, ASTMethodOrConstructorDeclaration> {

}
Expand Up @@ -9,7 +9,7 @@
import net.sourceforge.pmd.lang.java.metrics.signature.JavaOperationSigMask;

/**
* Gathers the methods that the Java project mirror should make available to metrics during the computation.
* Gathers the methods that PackageStats should make available to metrics during the computation.
*
* @author Clément Fournier
*/
Expand Down
Expand Up @@ -4,15 +4,12 @@

package net.sourceforge.pmd.lang.java.metrics;

import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.metrics.AbstractMetricMemoizer;

/**
* Statistics for an operation. Keeps a map of all memoized metrics results.
*
* @author Clément Fournier
*/
class OperationStats extends AbstractMetricMemoizer<ASTMethodOrConstructorDeclaration> {
class OperationStats {

private final String name;

Expand Down

0 comments on commit 152dddf

Please sign in to comment.