Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/make/src/classes/build/tools/buildmetaindex/BuildMetaIndex.java
32287 views
1
/*
2
* Copyright (c) 2005, 2013, 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
26
package build.tools.buildmetaindex;
27
28
import java.io.*;
29
import java.util.*;
30
import java.util.jar.*;
31
32
/** Constructs a meta-index of the specified jar files. The meta-index
33
contains prefixes of packages contained in these jars, indexed by
34
the jar file name. It is intended to be consumed by the JVM to
35
allow the boot class loader to be made lazier. For example, when
36
class data sharing is enabled, the presence of the meta-index
37
allows the JVM to skip opening rt.jar if all of the dependent
38
classes of the application are in the shared archive. A similar
39
mechanism could be useful at the application level as well, for
40
example to make the extension class loader lazier.
41
42
<p> The contents of the meta-index file for jre/lib look something
43
like this:
44
45
<PRE>
46
% VERSION 2
47
# charsets.jar
48
sun/
49
# jce.jar
50
javax/
51
! jsse.jar
52
sun/
53
com/sun/net/
54
javax/
55
com/sun/security/
56
# management-agent.jar
57
! rt.jar
58
org/w3c/
59
com/sun/image/
60
com/sun/org/
61
com/sun/imageio/
62
com/sun/accessibility/
63
javax/
64
...
65
</PRE>
66
67
<p> It is a current invariant of the code in the JVM which
68
consumes the meta-index that the meta-index indexes only jars in
69
one directory. It is acceptable for jars in that directory to not
70
be mentioned in the meta-index. The meta-index is designed more to
71
be able to perform a quick rejection test of the presence of a
72
particular class in a particular jar file than to be a precise
73
index of the contents of the jar. */
74
75
public class BuildMetaIndex {
76
public static void main(String[] args) throws IOException {
77
/* The correct usage of this class is as following:
78
* java BuildMetaIndex -o <meta-index> <a list of jar files>
79
* So the argument length should be at least 3 and the first argument should
80
* be '-o'.
81
*/
82
if (args.length < 3 ||
83
!args[0].equals("-o")) {
84
printUsage();
85
System.exit(1);
86
}
87
88
try {
89
PrintStream out = new PrintStream(new FileOutputStream(args[1]));
90
out.println("% VERSION 2");
91
out.println("% WARNING: this file is auto-generated; do not edit");
92
out.println("% UNSUPPORTED: this file and its format may change and/or");
93
out.println("% may be removed in a future release");
94
for (int i = 2; i < args.length; i++) {
95
String filename = args[i];
96
JarMetaIndex jmi = new JarMetaIndex(filename);
97
HashSet<String> index = jmi.getMetaIndex();
98
if (index == null) {
99
continue;
100
}
101
/*
102
* meta-index file plays different role in JVM and JDK side.
103
* On the JVM side, meta-index file is used to speed up locating the
104
* class files only while on the JDK side, meta-index file is used to speed
105
* up the resources file and class file.
106
* To help the JVM and JDK code to better utilize the information in meta-index
107
* file, we mark the jar file differently. Here is the current rule we use (See
108
* JarFileKind.getMarkChar() method. )
109
* For jar file containing only class file, we put '!' before the jar file name;
110
* for jar file containing only resources file, we put '@' before the jar file name;
111
* for jar file containing both resources and class file, we put '#' before the jar name.
112
* Notice the fact that every jar file contains at least the manifest file, so when
113
* we say "jar file containing only class file", we don't include that file.
114
*/
115
116
out.println(jmi.getJarFileKind().getMarkerChar() + " " + filename);
117
for (String entry : index) {
118
out.println(entry);
119
}
120
121
}
122
out.flush();
123
out.close();
124
} catch (FileNotFoundException fnfe) {
125
System.err.println("FileNotFoundException occurred");
126
System.exit(2);
127
}
128
}
129
130
private static void printUsage() {
131
String usage =
132
"BuildMetaIndex is used to generate a meta index file for the jar files\n" +
133
"you specified. The following is its usage:\n" +
134
" java BuildMetaIndex -o <the output meta index file> <a list of jar files> \n" +
135
" You can specify *.jar to refer to all the jar files in the current directory";
136
137
System.err.println(usage);
138
}
139
}
140
141
enum JarFileKind {
142
143
CLASSONLY ('!'),
144
RESOURCEONLY ('@'),
145
MIXED ('#');
146
147
private char markerChar;
148
149
JarFileKind(char markerChar) {
150
this.markerChar = markerChar;
151
}
152
153
public char getMarkerChar() {
154
return markerChar;
155
}
156
}
157
158
/*
159
* JarMetaIndex associates the jar file with a set of what so called
160
* "meta-index" of the jar file. Essentially, the meta-index is a list
161
* of class prefixes and the plain files contained in META-INF directory (
162
* not include the manifest file itself). This will help sun.misc.URLClassPath
163
* to quickly locate the resource file and hotspot VM to locate the class file.
164
*
165
*/
166
class JarMetaIndex {
167
private JarFile jar;
168
private volatile HashSet<String> indexSet;
169
170
/*
171
* A hashmap contains a mapping from the prefix string to
172
* a hashset which contains a set of the second level of prefix string.
173
*/
174
private HashMap<String, HashSet<String>> knownPrefixMap = new HashMap<>();
175
176
/**
177
* Special value for the HashSet to indicate that there are classes in
178
* the top-level package.
179
*/
180
private static final String TOP_LEVEL = "TOP";
181
182
/*
183
* A class for mapping package prefixes to the number of
184
* levels of package elements to include.
185
*/
186
static class ExtraLevel {
187
public ExtraLevel(String prefix, int levels) {
188
this.prefix = prefix;
189
this.levels = levels;
190
}
191
String prefix;
192
int levels;
193
}
194
195
/*
196
* A list of the special-cased package names.
197
*/
198
private static ArrayList<ExtraLevel> extraLevels = new ArrayList<>();
199
200
static {
201
// The order of these statements is significant,
202
// since we stop looking after the first match.
203
204
// Need more precise information to disambiguate
205
// (illegal) references from applications to
206
// obsolete backported collections classes in
207
// com/sun/java/util
208
extraLevels.add(new ExtraLevel("com/sun/java/util/", Integer.MAX_VALUE));
209
extraLevels.add(new ExtraLevel("com/sun/java/", 4));
210
// Need more information than just first two package
211
// name elements to determine that classes in
212
// deploy.jar are not in rt.jar
213
extraLevels.add(new ExtraLevel("com/sun/", 3));
214
// Need to make sure things in jfr.jar aren't
215
// confused with other com/oracle/** packages
216
extraLevels.add(new ExtraLevel("com/oracle/jrockit", 3));
217
}
218
219
220
/*
221
* We add maximum 5 second level entries to "sun", "jdk", "java" and
222
* "javax" entries. Tune this parameter to get a balance on the
223
* cold start and footprint.
224
*/
225
private static final int MAX_PKGS_WITH_KNOWN_PREFIX = 5;
226
227
private JarFileKind jarFileKind;
228
229
JarMetaIndex(String fileName) throws IOException {
230
jar = new JarFile(fileName);
231
knownPrefixMap.put("sun", new HashSet<String>());
232
knownPrefixMap.put("jdk", new HashSet<String>());
233
knownPrefixMap.put("java", new HashSet<String>());
234
knownPrefixMap.put("javax", new HashSet<String>());
235
}
236
237
/* Returns a HashSet contains the meta index string. */
238
HashSet<String> getMetaIndex() {
239
if (indexSet == null) {
240
synchronized(this) {
241
if (indexSet == null) {
242
indexSet = new HashSet<>();
243
Enumeration<JarEntry> entries = jar.entries();
244
boolean containsOnlyClass = true;
245
boolean containsOnlyResource = true;
246
while (entries.hasMoreElements()) {
247
JarEntry entry = entries.nextElement();
248
String name = entry.getName();
249
/* We only look at the non-directory entry.
250
MANIFEST file is also skipped. */
251
if (entry.isDirectory()
252
|| name.equals("META-INF/MANIFEST.MF")) {
253
continue;
254
}
255
256
/* Once containsOnlyResource or containsOnlyClass
257
turns to false, no need to check the entry type.
258
*/
259
if (containsOnlyResource || containsOnlyClass) {
260
if (name.endsWith(".class")) {
261
containsOnlyResource = false;
262
} else {
263
containsOnlyClass = false;
264
}
265
}
266
267
/* Add the full-qualified name of plain files under
268
META-INF directory to the indexSet.
269
*/
270
if (name.startsWith("META-INF")) {
271
indexSet.add(name);
272
continue;
273
}
274
275
/* Add the prefix name to the knownPrefixMap if the
276
name starts with any string in the knownPrefix list.
277
*/
278
if (isPrefixKnown(name)) {
279
continue;
280
}
281
282
String[] pkgElements = name.split("/");
283
// Last one is the class name; definitely ignoring that
284
if (pkgElements.length > 2) {
285
String meta = "";
286
287
// Default is 2 levels of package elements
288
int levels = 2;
289
290
// But for some packages we add more elements
291
for(ExtraLevel el : extraLevels) {
292
if (name.startsWith(el.prefix)) {
293
levels = el.levels;
294
break;
295
}
296
}
297
for (int i = 0; i < levels && i < pkgElements.length - 1; i++) {
298
meta += pkgElements[i] + "/";
299
}
300
301
if (!meta.equals("")) {
302
indexSet.add(meta);
303
}
304
}
305
306
} // end of "while" loop;
307
308
// Add the second level package names to the indexSet for
309
// the predefined names such as "sun", "java" and "javax".
310
addKnownPrefix();
311
312
/* Set "jarFileKind" attribute. */
313
if (containsOnlyClass) {
314
jarFileKind = JarFileKind.CLASSONLY;
315
} else if (containsOnlyResource) {
316
jarFileKind = JarFileKind.RESOURCEONLY;
317
} else {
318
jarFileKind = JarFileKind.MIXED;
319
}
320
}
321
}
322
}
323
return indexSet;
324
}
325
326
/*
327
* Checks to see whether the name starts with a string which is in the predefined
328
* list. If it is among one of the predefined prefixes, add it to the knowPrefixMap
329
* and returns true, otherwise, returns false.
330
* Returns true if the name is in a predefined prefix list. Otherwise, returns false.
331
*/
332
boolean isPrefixKnown(String name) {
333
int firstSlashIndex = name.indexOf("/");
334
if (firstSlashIndex == -1) {
335
return false;
336
}
337
338
String firstPkgElement = name.substring(0, firstSlashIndex);
339
HashSet<String> pkgSet = knownPrefixMap.get(firstPkgElement);
340
341
/* The name does not starts with "sun", "java" or "javax". */
342
if (pkgSet == null) {
343
return false;
344
}
345
346
/* Add the second level package name to the corresponding hashset. */
347
int secondSlashIndex = name.indexOf("/", firstSlashIndex+1);
348
if (secondSlashIndex == -1) {
349
pkgSet.add(TOP_LEVEL);
350
} else {
351
String secondPkgElement = name.substring(firstSlashIndex+1, secondSlashIndex);
352
pkgSet.add(secondPkgElement);
353
}
354
355
return true;
356
}
357
358
/*
359
* Adds all the second level package elements for "sun", "java" and "javax"
360
* if the corresponding jar file does not contain more than
361
* MAX_PKGS_WITH_KNOWN_PREFIX such entries.
362
*/
363
void addKnownPrefix() {
364
if (indexSet == null) {
365
return;
366
}
367
368
/* Iterate through the hash map, add the second level package names
369
* to the indexSet if has any.
370
*/
371
for (String key : knownPrefixMap.keySet()) {
372
HashSet<String> pkgSetStartsWithKey = knownPrefixMap.get(key);
373
int setSize = pkgSetStartsWithKey.size();
374
375
if (setSize == 0) {
376
continue;
377
}
378
if (setSize > JarMetaIndex.MAX_PKGS_WITH_KNOWN_PREFIX ||
379
pkgSetStartsWithKey.contains(TOP_LEVEL)) {
380
indexSet.add(key + "/");
381
} else {
382
/* If the set contains less than MAX_PKGS_WITH_KNOWN_PREFIX, add
383
* them to the indexSet of the MetaIndex object.
384
*/
385
for (String secondPkgElement : pkgSetStartsWithKey) {
386
indexSet.add(key + "/" + secondPkgElement);
387
}
388
}
389
} // end the outer "for"
390
}
391
392
JarFileKind getJarFileKind() {
393
// Build meta index if it hasn't.
394
if (indexSet == null) {
395
indexSet = getMetaIndex();
396
}
397
return jarFileKind;
398
}
399
}
400
401