All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.graph.Graph Maven / Gradle / Ivy

Go to download

Closure Compiler is a JavaScript optimizing compiler. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls. It is used in many of Google's JavaScript apps, including Gmail, Google Web Search, Google Maps, and Google Docs.

There is a newer version: v20240317
Show newest version
/*
 * Copyright 2008 The Closure Compiler Authors.
 *
 * 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 com.google.javascript.jscomp.graph;

import static com.google.common.base.Preconditions.checkNotNull;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.List;

/**
 * The base generic class for graph-like data structure and algorithms in the compiler.
 *
 * 

Nodes and edges in the graph can store a piece of data that this graph is used to represent. * For example, a variable interference graph might store a variable in the node. This piece of data * can be accessed with {@link GraphNode#getValue} and {@link GraphEdge#getValue}. However, in some * cases wrapping the nodes and/or edges in a custom class may be preferrable, as this additional * piece of data must be explicitly cast down from {@link Annotation} to its true type. * *

Algorithms and analysis can annotate information on the nodes and edges using {@link * GraphNode#getValue} and {@link GraphEdge#getValue}. For example, a graph coloring algorithm can * store the color as an annotation. If multiple analyses are required, it is up to the user of the * analysis to save the annotated solution between passes. * *

We implemented our own graph data structure (as opposed to using com.google.common.graph * ) for three reasons. First, aside from the node's label value, we would like to annotate * information on the nodes and edges. Using a map to annotate would introduce too much overhead * during fix point analysis. Also, com.google.common.graph does not support labeling * of edges. Second, avoiding using an external package limits our dependencies. Third, * com.google.common.graph uses WeakRefs which J2CL does not support. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public abstract class Graph implements AdjacencyGraph { /** * Pseudo typedef for a pair of annotations. Record of an object's * annotation at some state. */ private static final class AnnotationState { private final Annotatable first; private final Annotation second; public AnnotationState(Annotatable annotatable, Annotation annotation) { this.first = annotatable; this.second = annotation; } } /** * Pseudo typedef for {@code ArrayList}. Record of a collection of * objects' annotations at some state. */ private static class GraphAnnotationState extends ArrayList { private static final long serialVersionUID = 1L; public GraphAnnotationState(int size) { super(size); } } /** * Used by {@link #pushNodeAnnotations()} and {@link #popNodeAnnotations()}. */ private Deque nodeAnnotationStack; /** * Used by {@link #pushEdgeAnnotations()} and {@link #popEdgeAnnotations()}. */ private Deque edgeAnnotationStack; /** * Connects two nodes in the graph with an edge. * * @param n1 First node. * @param edge The edge. * @param n2 Second node. */ public abstract void connect(N n1, E edge, N n2); /** * Disconnects two nodes in the graph by removing all edges between them. * * @param n1 First node. * @param n2 Second node. */ public abstract void disconnect(N n1, N n2); /** * Connects two nodes in the graph with an edge if such edge does not already * exists between the nodes. * * @param n1 First node. * @param edge The edge. * @param n2 Second node. */ public final void connectIfNotFound(N n1, E edge, N n2) { if (!isConnected(n1, edge, n2)) { connect(n1, edge, n2); } } /** * Gets a node from the graph given a value. New nodes are created if that * value has not been assigned a graph node. Values equality are compared * using Object.equals. * * @param value The node's value. * @return The corresponding node in the graph. */ public abstract GraphNode createNode(N value); /** Gets an immutable list of all nodes. */ @Override public abstract Collection> getNodes(); @Override public abstract int getNodeCount(); /** Gets an immutable list of all edges. */ public abstract List> getEdges(); /** * Retrieves an edge from the graph. * * @param n1 Node one. * @param n2 Node two. * @return The list of edges between those two values in the graph. */ public abstract List> getEdges(N n1, N n2); /** * Gets the degree of a node. * * @param value The node's value. * @return The degree of the node. */ public abstract int getNodeDegree(N value); @Override public int getWeight(N value) { return getNodeDegree(value); } /** * Gets the neighboring nodes. * * @param value The node's value. * @return A list of neighboring nodes. */ public abstract List> getNeighborNodes(N value); /** * Retrieves any edge from the graph. * * @param n1 Node one. * @param n2 Node two. * @return The first edges between those two values in the graph. null if * there are none. */ public abstract GraphEdge getFirstEdge(N n1, N n2); /** * Checks whether the node exists in the graph ({@link #createNode(Object)} * has been called with that value). * * @param n Node. * @return true if it exist. */ public final boolean hasNode(N n) { return getNode(n) != null; } /** * Checks whether two nodes in the graph are connected. * * @param n1 Node 1. * @param n2 Node 2. * @return true if the two nodes are connected. */ public abstract boolean isConnected(N n1, N n2); /** * Checks whether two nodes in the graph are connected by the given * edge type. * * @param n1 Node 1. * @param e The edge type. * @param n2 Node 2. */ public abstract boolean isConnected(N n1, E e, N n2); /** * Gets the node of the specified type, or throws an * IllegalArgumentException. */ @SuppressWarnings("unchecked") > T getNodeOrFail(N val) { T node = (T) getNode(val); if (node == null) { throw new IllegalArgumentException(val + " does not exist in graph"); } return node; } @Override public final void clearNodeAnnotations() { for (GraphNode n : getNodes()) { n.setAnnotation(null); } } /** Makes each edge's annotation null. */ public final void clearEdgeAnnotations() { for (GraphEdge e : getEdges()) { e.setAnnotation(null); } } /** * Pushes nodes' annotation values. Restored with * {@link #popNodeAnnotations()}. Nodes' annotation values are cleared. */ public final void pushNodeAnnotations() { if (nodeAnnotationStack == null) { nodeAnnotationStack = new ArrayDeque<>(); } pushAnnotations(nodeAnnotationStack, getNodes()); } /** * Restores nodes' annotation values to state before last * {@link #pushNodeAnnotations()}. */ public final void popNodeAnnotations() { checkNotNull(nodeAnnotationStack, "Popping node annotations without pushing."); popAnnotations(nodeAnnotationStack); } /** * Pushes edges' annotation values. Restored with * {@link #popEdgeAnnotations()}. Edges' annotation values are cleared. */ public final void pushEdgeAnnotations() { if (edgeAnnotationStack == null) { edgeAnnotationStack = new ArrayDeque<>(); } pushAnnotations(edgeAnnotationStack, getEdges()); } /** * Restores edges' annotation values to state before last * {@link #pushEdgeAnnotations()}. */ public final void popEdgeAnnotations() { checkNotNull(edgeAnnotationStack, "Popping edge annotations without pushing."); popAnnotations(edgeAnnotationStack); } /** * A generic edge. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public interface GraphEdge extends Annotatable { /** * Retrieves the edge's value. * * @return The value. */ E getValue(); GraphNode getNodeA(); GraphNode getNodeB(); } /** * A simple implementation of SubGraph that calculates adjacency by iterating * over a node's neighbors. */ static class SimpleSubGraph implements SubGraph { private final Graph graph; private final List> nodes = new ArrayList<>(); SimpleSubGraph(Graph graph) { this.graph = graph; } @Override public boolean isIndependentOf(N value) { GraphNode node = graph.getNode(value); for (GraphNode n : nodes) { if (graph.getNeighborNodes(n.getValue()).contains(node)) { return false; } } return true; } @Override public void addNode(N value) { nodes.add(graph.getNodeOrFail(value)); } } /** * Pushes a new list on stack and stores nodes annotations in the new list. * Clears objects' annotations as well. */ private static void pushAnnotations( Deque stack, Collection haveAnnotations) { stack.push(new GraphAnnotationState(haveAnnotations.size())); for (Annotatable h : haveAnnotations) { stack.peek().add(new AnnotationState(h, h.getAnnotation())); h.setAnnotation(null); } } /** * Restores the node annotations on the top of stack and pops stack. */ private static void popAnnotations(Deque stack) { for (AnnotationState as : stack.pop()) { as.first.setAnnotation(as.second); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy