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

com.google.javascript.jscomp.disambiguate.ColorGraphBuilder 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 2020 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.disambiguate;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableSet.toImmutableSet;

import com.google.common.collect.ImmutableSet;
import com.google.javascript.jscomp.colors.Color;
import com.google.javascript.jscomp.colors.ColorRegistry;
import com.google.javascript.jscomp.colors.StandardColors;
import com.google.javascript.jscomp.graph.LinkedDirectedGraph;
import com.google.javascript.jscomp.graph.LinkedDirectedGraph.LinkedDiGraphNode;
import com.google.javascript.jscomp.graph.LowestCommonAncestorFinder;
import java.util.Collection;
import org.jspecify.nullness.Nullable;

/** Builds a graph of the {@link Color}s on the AST from a specified set of seed colors. */
final class ColorGraphBuilder {

  /**
   * The relationship that caused an edge to be created.
   *
   * 

This information is only retained for diagnostics, not correctness. */ enum EdgeReason { ALGEBRAIC, CAN_HOLD } private final ColorRegistry registry; private final ColorGraphNodeFactory nodeFactory; private final LowestCommonAncestorFinder lcaFinder; private final LinkedDiGraphNode topNode; /** * The graph of colors as defined by `holdsInstanceOf`. * *

We use `holdsInstanceOf` rather than `isSupertypeOf` because we only actually care about * edges that were "used" in the program. If instances never flow over an edge at runtime, then * properties also don't need to be tracked across that edge either. Of course, since we don't * track the types of all assignments in a program, many of the edges are `isSupertypeOf` edges * that we include to be conservative. * *

This graph, when fully constructed, is still only an approximation. This is the due to both * memory and time constraints. The following quirks are expected: * *

    *
  • Some colors, such as primitives, will not have nodes. *
  • Transitive edges (shortcut edges for which there exist alternate paths) are kept minimal. *
* *

In practice, this could be declared as taking an {@link EdgeReason} instead of an Object, * but Object is used to indicate that EdgeReasons are only meant for debugging and not any actual * logic in (dis)ambiguation. */ private @Nullable LinkedDirectedGraph colorHoldsInstanceGraph = LinkedDirectedGraph.createWithoutAnnotations(); ColorGraphBuilder( ColorGraphNodeFactory nodeFactory, LowestCommonAncestorFinder.Factory lcaFinderFactory, ColorRegistry registry) { this.registry = registry; this.nodeFactory = nodeFactory; this.lcaFinder = lcaFinderFactory.create(this.colorHoldsInstanceGraph); this.topNode = this.colorHoldsInstanceGraph.createNode( this.nodeFactory.createNode(StandardColors.UNKNOWN)); } public void add(ColorGraphNode flat) { this.addInternal(flat); } public void addAll(Collection flats) { flats.forEach(this::add); } public LinkedDirectedGraph build() { this.colorHoldsInstanceGraph.getNodes().forEach(this::connectUnionWithAncestors); LinkedDirectedGraph temp = this.colorHoldsInstanceGraph; this.colorHoldsInstanceGraph = null; return temp; } /** * During initial lattice construction unions were only given outbound edges. Here we add any * necessary inbound ones. * *

We defer this operation because adding union-to-union and common-supertype-to-union edges, * is a hard problem. Solving it after all other colors are in place makes it easier. */ private void connectUnionWithAncestors(LinkedDiGraphNode unionNode) { ColorGraphNode flatUnion = unionNode.getValue(); if (!flatUnion.getColor().isUnion()) { return; } /** * Connect the LCAs to the union. * *

The union itself will be found in most cases, but since we don't add self-edges, that * won't matter. * *

Some of these edges may pollute the "lattice-ness" of the graph, but all the invariants we * actually care about will be maintained. If disambiguation is too slow and stricter invariants * would help, we could be more careful. */ checkState(!unionNode.getOutEdges().isEmpty()); ImmutableSet graphNodes = flatUnion.getColor().getUnionElements().stream() .map(this.nodeFactory::createNode) .collect(toImmutableSet()); for (ColorGraphNode lca : this.lcaFinder.findAll(graphNodes)) { this.connectSourceToDest( checkNotNull(this.colorHoldsInstanceGraph.getNode(lca)), EdgeReason.ALGEBRAIC, unionNode); } } /** Insert {@code color} and all necessary related colors into the datastructures of this pass. */ private LinkedDiGraphNode addInternal(Color color) { return this.addInternal(this.nodeFactory.createNode(color)); } /** Insert {@code node} and all necessary related colors into the datastructures of this pass. */ private LinkedDiGraphNode addInternal(ColorGraphNode node) { LinkedDiGraphNode flatNode = this.colorHoldsInstanceGraph.getNode(node); if (flatNode != null) { return flatNode; } flatNode = this.colorHoldsInstanceGraph.createNode(node); if (node.getColor().isUnion()) { for (Color alt : node.getColor().getUnionElements()) { this.connectSourceToDest(flatNode, EdgeReason.ALGEBRAIC, this.addInternal(alt)); } return flatNode; } Color color = node.getColor(); ImmutableSet supertypes = this.registry.getDisambiguationSupertypes(color); if (supertypes.isEmpty()) { this.connectSourceToDest(topNode, EdgeReason.ALGEBRAIC, flatNode); } else { for (Color supertype : supertypes) { this.connectSourceToDest(this.addInternal(supertype), EdgeReason.CAN_HOLD, flatNode); } } /** * Add all instance and prototype colors when visiting a constructor. We won't necessarily see * all possible instance colors that exist at runtime during an AST traversal. * *

For example, a subclass constructor may never be explicitly initialized but instead passed * to some function expecting `function(new:Parent)`. See {@link * AmbiguatePropertiesTest#testImplementsAndExtends_respectsUndeclaredProperties()} */ for (Color prototype : color.getPrototypes()) { this.addInternal(prototype); } for (Color instanceColor : color.getInstanceColors()) { this.addInternal(instanceColor); } return flatNode; } private void connectSourceToDest( LinkedDiGraphNode source, EdgeReason reason, LinkedDiGraphNode dest) { if (source.equals(dest) || this.colorHoldsInstanceGraph.isConnectedInDirection(source, (t) -> true, dest)) { return; } this.colorHoldsInstanceGraph.connect(source, reason, dest); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy