Path: blob/master/src/java.base/share/classes/jdk/internal/module/ModuleHashesBuilder.java
41159 views
/*1* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package jdk.internal.module;2627import java.io.PrintStream;28import java.lang.module.Configuration;29import java.lang.module.ModuleReference;30import java.lang.module.ResolvedModule;31import java.util.ArrayDeque;32import java.util.Collections;33import java.util.Deque;34import java.util.HashMap;35import java.util.TreeMap;36import java.util.HashSet;37import java.util.Map;38import java.util.Set;39import java.util.function.Consumer;40import java.util.stream.Stream;41import static java.util.stream.Collectors.*;4243/**44* A Builder to compute ModuleHashes from a given configuration45*/46public class ModuleHashesBuilder {47private final Configuration configuration;48private final Set<String> hashModuleCandidates;4950/**51* Constructs a ModuleHashesBuilder that finds the packaged modules52* from the location of ModuleReference found from the given Configuration.53*54* @param config Configuration for building module hashes55* @param modules the candidate modules to be hashed56*/57public ModuleHashesBuilder(Configuration config, Set<String> modules) {58this.configuration = config;59this.hashModuleCandidates = modules;60}6162/**63* Returns a map of a module M to ModuleHashes for the modules64* that depend upon M directly or indirectly.65*66* The key for each entry in the returned map is a module M that has67* no outgoing edges to any of the candidate modules to be hashed68* i.e. M is a leaf node in a connected subgraph containing M and69* other candidate modules from the module graph filtering70* the outgoing edges from M to non-candidate modules.71*/72public Map<String, ModuleHashes> computeHashes(Set<String> roots) {73// build a graph containing the packaged modules and74// its transitive dependences matching --hash-modules75Graph.Builder<String> builder = new Graph.Builder<>();76Deque<ResolvedModule> todo = new ArrayDeque<>(configuration.modules());77Set<ResolvedModule> visited = new HashSet<>();78ResolvedModule rm;79while ((rm = todo.poll()) != null) {80if (visited.add(rm)) {81builder.addNode(rm.name());82for (ResolvedModule dm : rm.reads()) {83if (!visited.contains(dm)) {84todo.push(dm);85}86builder.addEdge(rm.name(), dm.name());87}88}89}9091// each node in a transposed graph is a matching packaged module92// in which the hash of the modules that depend upon it is recorded93Graph<String> transposedGraph = builder.build().transpose();9495// traverse the modules in topological order that will identify96// the modules to record the hashes - it is the first matching97// module and has not been hashed during the traversal.98Set<String> mods = new HashSet<>();99Map<String, ModuleHashes> hashes = new TreeMap<>();100builder.build()101.orderedNodes()102.filter(mn -> roots.contains(mn) && !mods.contains(mn))103.forEach(mn -> {104// Compute hashes of the modules that depend on mn directly and105// indirectly excluding itself.106Set<String> ns = transposedGraph.dfs(mn)107.stream()108.filter(n -> !n.equals(mn) && hashModuleCandidates.contains(n))109.collect(toSet());110mods.add(mn);111mods.addAll(ns);112113if (!ns.isEmpty()) {114Set<ModuleReference> mrefs = ns.stream()115.map(name -> configuration.findModule(name)116.orElseThrow(InternalError::new))117.map(ResolvedModule::reference)118.collect(toSet());119hashes.put(mn, ModuleHashes.generate(mrefs, "SHA-256"));120}121});122return hashes;123}124125/*126* Utility class127*/128static class Graph<T> {129private final Set<T> nodes;130private final Map<T, Set<T>> edges;131132public Graph(Set<T> nodes, Map<T, Set<T>> edges) {133this.nodes = Collections.unmodifiableSet(nodes);134this.edges = Collections.unmodifiableMap(edges);135}136137public Set<T> nodes() {138return nodes;139}140141public Map<T, Set<T>> edges() {142return edges;143}144145public Set<T> adjacentNodes(T u) {146return edges.get(u);147}148149public boolean contains(T u) {150return nodes.contains(u);151}152153/**154* Returns nodes sorted in topological order.155*/156public Stream<T> orderedNodes() {157TopoSorter<T> sorter = new TopoSorter<>(this);158return sorter.result.stream();159}160161/**162* Traverses this graph and performs the given action in topological order.163*/164public void ordered(Consumer<T> action) {165TopoSorter<T> sorter = new TopoSorter<>(this);166sorter.ordered(action);167}168169/**170* Traverses this graph and performs the given action in reverse topological order.171*/172public void reverse(Consumer<T> action) {173TopoSorter<T> sorter = new TopoSorter<>(this);174sorter.reverse(action);175}176177/**178* Returns a transposed graph from this graph.179*/180public Graph<T> transpose() {181Builder<T> builder = new Builder<>();182nodes.forEach(builder::addNode);183// reverse edges184edges.keySet().forEach(u -> {185edges.get(u).forEach(v -> builder.addEdge(v, u));186});187return builder.build();188}189190/**191* Returns all nodes reachable from the given root.192*/193public Set<T> dfs(T root) {194return dfs(Set.of(root));195}196197/**198* Returns all nodes reachable from the given set of roots.199*/200public Set<T> dfs(Set<T> roots) {201ArrayDeque<T> todo = new ArrayDeque<>(roots);202Set<T> visited = new HashSet<>();203T u;204while ((u = todo.poll()) != null) {205if (visited.add(u) && contains(u)) {206adjacentNodes(u).stream()207.filter(v -> !visited.contains(v))208.forEach(todo::push);209}210}211return visited;212}213214public void printGraph(PrintStream out) {215out.println("graph for " + nodes);216nodes217.forEach(u -> adjacentNodes(u)218.forEach(v -> out.format(" %s -> %s%n", u, v)));219}220221static class Builder<T> {222final Set<T> nodes = new HashSet<>();223final Map<T, Set<T>> edges = new HashMap<>();224225public void addNode(T node) {226if (nodes.add(node)) {227edges.computeIfAbsent(node, _e -> new HashSet<>());228}229}230231public void addEdge(T u, T v) {232addNode(u);233addNode(v);234edges.get(u).add(v);235}236237public Graph<T> build() {238return new Graph<T>(nodes, edges);239}240}241}242243/**244* Topological sort245*/246private static class TopoSorter<T> {247final Deque<T> result = new ArrayDeque<>();248final Graph<T> graph;249250TopoSorter(Graph<T> graph) {251this.graph = graph;252sort();253}254255public void ordered(Consumer<T> action) {256result.forEach(action);257}258259public void reverse(Consumer<T> action) {260result.descendingIterator().forEachRemaining(action);261}262263private void sort() {264Set<T> visited = new HashSet<>();265Deque<T> stack = new ArrayDeque<>();266graph.nodes.forEach(node -> visit(node, visited, stack));267}268269private Set<T> children(T node) {270return graph.edges().get(node);271}272273private void visit(T node, Set<T> visited, Deque<T> stack) {274if (visited.add(node)) {275stack.push(node);276children(node).forEach(child -> visit(child, visited, stack));277stack.pop();278result.addLast(node);279}280else if (stack.contains(node)) {281throw new IllegalArgumentException(282"Cycle detected: " + node + " -> " + children(node));283}284}285}286}287288289