Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java
41161 views
1
/*
2
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
package com.sun.tools.jdeps;
26
27
import static com.sun.tools.jdeps.Module.*;
28
import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
29
import static java.util.stream.Collectors.*;
30
31
import com.sun.tools.classfile.AccessFlags;
32
import com.sun.tools.classfile.ClassFile;
33
import com.sun.tools.classfile.ConstantPoolException;
34
import com.sun.tools.classfile.Dependencies;
35
import com.sun.tools.classfile.Dependencies.ClassFileError;
36
import com.sun.tools.classfile.Dependency;
37
import com.sun.tools.classfile.Dependency.Location;
38
39
import java.io.IOException;
40
import java.io.UncheckedIOException;
41
import java.nio.file.Paths;
42
import java.util.Collections;
43
import java.util.Deque;
44
import java.util.HashMap;
45
import java.util.HashSet;
46
import java.util.Map;
47
import java.util.Optional;
48
import java.util.Set;
49
import java.util.concurrent.Callable;
50
import java.util.concurrent.ConcurrentHashMap;
51
import java.util.concurrent.ConcurrentLinkedDeque;
52
import java.util.concurrent.ExecutionException;
53
import java.util.concurrent.ExecutorService;
54
import java.util.concurrent.Executors;
55
import java.util.concurrent.FutureTask;
56
import java.util.stream.Stream;
57
58
/**
59
* Parses class files and finds dependences
60
*/
61
class DependencyFinder {
62
private static Finder API_FINDER = new Finder(true);
63
private static Finder CLASS_FINDER = new Finder(false);
64
65
private final JdepsConfiguration configuration;
66
private final JdepsFilter filter;
67
68
private final Map<Finder, Deque<Archive>> parsedArchives = new ConcurrentHashMap<>();
69
private final Map<Location, Archive> parsedClasses = new ConcurrentHashMap<>();
70
71
private final ExecutorService pool = Executors.newFixedThreadPool(2);
72
private final Deque<FutureTask<Set<Location>>> tasks = new ConcurrentLinkedDeque<>();
73
74
DependencyFinder(JdepsConfiguration configuration,
75
JdepsFilter filter) {
76
this.configuration = configuration;
77
this.filter = filter;
78
this.parsedArchives.put(API_FINDER, new ConcurrentLinkedDeque<>());
79
this.parsedArchives.put(CLASS_FINDER, new ConcurrentLinkedDeque<>());
80
}
81
82
Map<Location, Archive> locationToArchive() {
83
return parsedClasses;
84
}
85
86
/**
87
* Returns the modules of all dependencies found
88
*/
89
Stream<Archive> getDependences(Archive source) {
90
return source.getDependencies()
91
.map(this::locationToArchive)
92
.filter(a -> a != source);
93
}
94
95
/**
96
* Returns the location to archive map; or NOT_FOUND.
97
*
98
* Location represents a parsed class.
99
*/
100
Archive locationToArchive(Location location) {
101
return parsedClasses.containsKey(location)
102
? parsedClasses.get(location)
103
: configuration.findClass(location).orElse(NOT_FOUND);
104
}
105
106
/**
107
* Returns a map from an archive to its required archives
108
*/
109
Map<Archive, Set<Archive>> dependences() {
110
Map<Archive, Set<Archive>> map = new HashMap<>();
111
parsedArchives.values().stream()
112
.flatMap(Deque::stream)
113
.filter(a -> !a.isEmpty())
114
.forEach(source -> {
115
Set<Archive> deps = getDependences(source).collect(toSet());
116
if (!deps.isEmpty()) {
117
map.put(source, deps);
118
}
119
});
120
return map;
121
}
122
123
boolean isParsed(Location location) {
124
return parsedClasses.containsKey(location);
125
}
126
127
/**
128
* Parses all class files from the given archive stream and returns
129
* all target locations.
130
*/
131
public Set<Location> parse(Stream<? extends Archive> archiveStream) {
132
archiveStream.forEach(archive -> parse(archive, CLASS_FINDER));
133
return waitForTasksCompleted();
134
}
135
136
/**
137
* Parses the exported API class files from the given archive stream and
138
* returns all target locations.
139
*/
140
public Set<Location> parseExportedAPIs(Stream<? extends Archive> archiveStream) {
141
archiveStream.forEach(archive -> parse(archive, API_FINDER));
142
return waitForTasksCompleted();
143
}
144
145
/**
146
* Parses the named class from the given archive and
147
* returns all target locations the named class references.
148
*/
149
public Set<Location> parse(Archive archive, String name) {
150
try {
151
return parse(archive, CLASS_FINDER, name);
152
} catch (IOException e) {
153
throw new UncheckedIOException(e);
154
}
155
}
156
157
/**
158
* Parses the exported API of the named class from the given archive and
159
* returns all target locations the named class references.
160
*/
161
public Set<Location> parseExportedAPIs(Archive archive, String name)
162
{
163
try {
164
return parse(archive, API_FINDER, name);
165
} catch (IOException e) {
166
throw new UncheckedIOException(e);
167
}
168
}
169
170
private Optional<FutureTask<Set<Location>>> parse(Archive archive, Finder finder) {
171
if (parsedArchives.get(finder).contains(archive))
172
return Optional.empty();
173
174
parsedArchives.get(finder).add(archive);
175
176
trace("parsing %s %s%n", archive.getName(), archive.getPathName());
177
FutureTask<Set<Location>> task = new FutureTask<>(() -> {
178
Set<Location> targets = new HashSet<>();
179
for (ClassFile cf : archive.reader().getClassFiles()) {
180
if (cf.access_flags.is(AccessFlags.ACC_MODULE))
181
continue;
182
183
String classFileName;
184
try {
185
classFileName = cf.getName();
186
} catch (ConstantPoolException e) {
187
throw new ClassFileError(e);
188
}
189
190
// filter source class/archive
191
String cn = classFileName.replace('/', '.');
192
if (!finder.accept(archive, cn, cf.access_flags))
193
continue;
194
195
// tests if this class matches the -include
196
if (!filter.matches(cn))
197
continue;
198
199
for (Dependency d : finder.findDependencies(cf)) {
200
if (filter.accepts(d)) {
201
archive.addClass(d.getOrigin(), d.getTarget());
202
targets.add(d.getTarget());
203
} else {
204
// ensure that the parsed class is added the archive
205
archive.addClass(d.getOrigin());
206
}
207
parsedClasses.putIfAbsent(d.getOrigin(), archive);
208
}
209
}
210
return targets;
211
});
212
tasks.add(task);
213
pool.submit(task);
214
return Optional.of(task);
215
}
216
217
private Set<Location> parse(Archive archive, Finder finder, String name)
218
throws IOException
219
{
220
ClassFile cf = archive.reader().getClassFile(name);
221
if (cf == null) {
222
throw new IllegalArgumentException(archive.getName() +
223
" does not contain " + name);
224
}
225
226
if (cf.access_flags.is(AccessFlags.ACC_MODULE))
227
return Collections.emptySet();
228
229
Set<Location> targets = new HashSet<>();
230
String cn;
231
try {
232
cn = cf.getName().replace('/', '.');
233
} catch (ConstantPoolException e) {
234
throw new Dependencies.ClassFileError(e);
235
}
236
237
if (!finder.accept(archive, cn, cf.access_flags))
238
return targets;
239
240
// tests if this class matches the -include
241
if (!filter.matches(cn))
242
return targets;
243
244
// skip checking filter.matches
245
for (Dependency d : finder.findDependencies(cf)) {
246
if (filter.accepts(d)) {
247
targets.add(d.getTarget());
248
archive.addClass(d.getOrigin(), d.getTarget());
249
} else {
250
// ensure that the parsed class is added the archive
251
archive.addClass(d.getOrigin());
252
}
253
parsedClasses.putIfAbsent(d.getOrigin(), archive);
254
}
255
return targets;
256
}
257
258
/*
259
* Waits until all submitted tasks are completed.
260
*/
261
private Set<Location> waitForTasksCompleted() {
262
try {
263
Set<Location> targets = new HashSet<>();
264
FutureTask<Set<Location>> task;
265
while ((task = tasks.poll()) != null) {
266
// wait for completion
267
targets.addAll(task.get());
268
}
269
return targets;
270
} catch (InterruptedException|ExecutionException e) {
271
throw new Error(e);
272
}
273
}
274
275
/*
276
* Shutdown the executor service.
277
*/
278
void shutdown() {
279
pool.shutdown();
280
}
281
282
private interface SourceFilter {
283
boolean accept(Archive archive, String cn, AccessFlags accessFlags);
284
}
285
286
private static class Finder implements Dependency.Finder, SourceFilter {
287
private final Dependency.Finder finder;
288
private final boolean apiOnly;
289
Finder(boolean apiOnly) {
290
this.apiOnly = apiOnly;
291
this.finder = apiOnly
292
? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
293
: Dependencies.getClassDependencyFinder();
294
295
}
296
297
@Override
298
public boolean accept(Archive archive, String cn, AccessFlags accessFlags) {
299
int i = cn.lastIndexOf('.');
300
String pn = i > 0 ? cn.substring(0, i) : "";
301
302
// if -apionly is specified, analyze only exported and public types
303
// All packages are exported in unnamed module.
304
return apiOnly ? archive.getModule().isExported(pn) &&
305
accessFlags.is(AccessFlags.ACC_PUBLIC)
306
: true;
307
}
308
309
@Override
310
public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
311
return finder.findDependencies(classfile);
312
}
313
}
314
}
315
316