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

com.google.javascript.jscomp.NameReferenceGraph 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: v20250528
Show newest version
/*
 * Copyright 2009 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;

import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.javascript.jscomp.DefinitionsRemover.AssignmentDefinition;
import com.google.javascript.jscomp.DefinitionsRemover.Definition;
import com.google.javascript.jscomp.DefinitionsRemover.NamedFunctionDefinition;
import com.google.javascript.jscomp.graph.GraphNode;
import com.google.javascript.jscomp.graph.LinkedDirectedGraph;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.JSTypeNative;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * A graph represents all the referencing of global names in the program. In
 * other words, it is a call and variable-name graph.
 *
 * 

The NameReferenceGraph G for a program P is a directed graph G = (V, E) * where: * *

V ({@link Name}) represents all global names in P and E = (v, v'), v and * v' in V ({@link Reference} represents a reference use or definition from the * name v to v' in P. * *

There are two core results we are trying to compute. The first being able * to precisely identify the function body at any given call site with * {@link #getReferencesAt(Node)}. * *

The second result come directly from the previous one. The directed edge * provides us with dependency information. If A->B, B might be needed (in this * module) if A is needed (in this module). The converse of the this result is * more useful. B is not needed if A is not needed. * */ class NameReferenceGraph extends LinkedDirectedGraph implements DefinitionProvider { // This is the key result of the name graph. Given a node in the AST, this map // will give us the Reference edges. For example a CALL node will map to a // list of possible call edge destinations. private final Multimap referenceMap = HashMultimap.create(); // Given a qualified name, provides the Name object. private Map nameMap = new HashMap<>(); // The following are some implicit nodes of the graph. // If we have a call site that we absolutely have no idea what variable it // it calls or reference, we'd point it to UNKNOWN. final Name unknown; // Represents the "main" global block as well as externs. final Name main; // The implicit "window" object. final Name window; final AbstractCompiler compiler; public NameReferenceGraph(AbstractCompiler compiler) { super(true, true); this.compiler = compiler; // Initialize builtins. unknown = new Name("{UNKNOWN}", true); unknown.isAliased = true; unknown.type = compiler.getTypeIRegistry().getNativeType(JSTypeNative.NO_TYPE); this.createNode(unknown); main = new Name("{Global Main}", true); this.createNode(main); window = new Name("window", true); this.createNode(window); } public Name defineNameIfNotExists(String name, boolean isExtern) { Name symbol = null; if (nameMap.containsKey(name)) { // This is a re-declaration. symbol = nameMap.get(name); } else { symbol = new Name(name, isExtern); nameMap.put(name, symbol); createNode(symbol); } return symbol; } /** * Retrieves a list of all possible Names that this site is referring to. */ public List getReferencesAt(Node site) { Preconditions.checkArgument( site.isGetProp() || site.isName()); List result = new ArrayList<>(); result.addAll(referenceMap.get(site)); return result; } @Override public Collection getDefinitionsReferencedAt(Node useSite) { List nameRefs = getReferencesAt(useSite); if (nameRefs.isEmpty()) { return null; } List result = new ArrayList<>(); for (Name nameRef : nameRefs) { List decls = nameRef.getDeclarations(); if (!decls.isEmpty()) { result.addAll(decls); } } if (!result.isEmpty()) { return result; } else { return null; } } public Name getSymbol(String name) { return nameMap.get(name); } @Override public GraphNode createNode(Name value) { nameMap.put(value.qName, value); return super.createNode(value); } @Override public void connect(Name src, Reference ref, Name dest) { super.connect(src, ref, dest); referenceMap.put(ref.site, dest); } /** * Represents function or variable names that can be referenced globally. */ class Name { // Full name private final String qName; private JSType type; // A list (re)declarations private List declarations = new LinkedList<>(); final boolean isExtern; private boolean isExported = false; private boolean isAliased = false; // Function invocations that use ".call" and ".apply" syntax may prevent // several of the possible optimizations. We keep track of all functions // invoked in this way so those passes can exclude them. // Ex: // some_func.call(some_obj, 1, 2 , 3); // The name graph does not currently recognize this as a call to some_func. // This Set is meant to keep track of such occurrence until the name graph // becomes aware of those cases. private boolean exposedToCallOrApply = false; public Name(String qName, boolean isExtern) { this.qName = qName; this.isExtern = isExtern; int lastDot = qName.lastIndexOf('.'); String name = (lastDot == -1) ? qName : qName.substring(lastDot + 1); this.isExported = compiler.getCodingConvention().isExported(name); this.type = compiler.getTypeIRegistry().getNativeType(JSTypeNative.UNKNOWN_TYPE); } public JSType getType() { return type; } public void setType(JSType type) { this.type = type; } public List getDeclarations() { return declarations; } public void addAssignmentDeclaration(Node node) { declarations.add(new AssignmentDefinition(node, isExtern)); } public void addFunctionDeclaration(Node node) { declarations.add(new NamedFunctionDefinition(node, isExtern)); } public void markExported() { this.isExported = true; } public boolean isExported() { return isExported; } /** * @return {@code} True if this name has been dereferenced. Removing from * the program or the module is no longer safe unless further analysis * can prove otherwise. */ public boolean isAliased() { return isAliased; } public void setAliased(boolean isAliased) { this.isAliased = isAliased; } public String getQualifiedName() { return qName; } /** * @return The short property name of this object if it is a property, else * {@code null}. */ public String getPropertyName() { int lastIndexOfDot = qName.lastIndexOf('.'); if (lastIndexOfDot == -1) { return null; } else { return qName.substring(lastIndexOfDot + 1); } } public boolean exposedToCallOrApply() { return exposedToCallOrApply; } public void markExposedToCallOrApply() { exposedToCallOrApply = true; } @Override public String toString() { return qName + " : " + type; } @Override public int hashCode() { return qName.hashCode(); } } /** * A reference site for a function or a variable reference. It can be a * reference use or an assignment to that name. */ static class Reference { // The node that references the name. public final Node site; public Reference(Node site) { this.site = site; } /** * Get accessor for retrieving the actual node corresponding to the * reference. * * @return node representing the access/reference/call site */ public Node getSite() { return site; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy