Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/jdk.jartool/share/classes/sun/tools/jar/Validator.java
41161 views
1
/*
2
* Copyright (c) 2017, 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 sun.tools.jar;
27
28
import java.io.File;
29
import java.io.IOException;
30
import java.io.InputStream;
31
import java.lang.module.ModuleDescriptor;
32
import java.lang.module.ModuleDescriptor.Exports;
33
import java.lang.module.ModuleDescriptor.Opens;
34
import java.lang.module.ModuleDescriptor.Provides;
35
import java.lang.module.ModuleDescriptor.Requires;
36
import java.util.Collections;
37
import java.util.HashMap;
38
import java.util.HashSet;
39
import java.util.List;
40
import java.util.Map;
41
import java.util.Set;
42
import java.util.TreeMap;
43
import java.util.function.Function;
44
import java.util.stream.Collectors;
45
import java.util.zip.ZipEntry;
46
import java.util.zip.ZipFile;
47
48
import static java.util.jar.JarFile.MANIFEST_NAME;
49
import static sun.tools.jar.Main.VERSIONS_DIR;
50
import static sun.tools.jar.Main.VERSIONS_DIR_LENGTH;
51
import static sun.tools.jar.Main.MODULE_INFO;
52
import static sun.tools.jar.Main.getMsg;
53
import static sun.tools.jar.Main.formatMsg;
54
import static sun.tools.jar.Main.formatMsg2;
55
import static sun.tools.jar.Main.toBinaryName;
56
57
final class Validator {
58
59
private final Map<String,FingerPrint> classes = new HashMap<>();
60
private final Main main;
61
private final ZipFile zf;
62
private boolean isValid = true;
63
private Set<String> concealedPkgs = Collections.emptySet();
64
private ModuleDescriptor md;
65
private String mdName;
66
67
private Validator(Main main, ZipFile zf) {
68
this.main = main;
69
this.zf = zf;
70
checkModuleDescriptor(MODULE_INFO);
71
}
72
73
static boolean validate(Main main, ZipFile zf) throws IOException {
74
return new Validator(main, zf).validate();
75
}
76
77
private boolean validate() {
78
try {
79
zf.stream()
80
.filter(e -> e.getName().endsWith(".class"))
81
.map(this::getFingerPrint)
82
.filter(FingerPrint::isClass) // skip any non-class entry
83
.collect(Collectors.groupingBy(
84
FingerPrint::mrversion,
85
TreeMap::new,
86
Collectors.toMap(FingerPrint::className,
87
Function.identity(),
88
this::sameNameFingerPrint)))
89
.forEach((version, entries) -> {
90
if (version == 0)
91
validateBase(entries);
92
else
93
validateVersioned(entries);
94
});
95
} catch (InvalidJarException e) {
96
errorAndInvalid(e.getMessage());
97
}
98
return isValid;
99
}
100
101
static class InvalidJarException extends RuntimeException {
102
private static final long serialVersionUID = -3642329147299217726L;
103
InvalidJarException(String msg) {
104
super(msg);
105
}
106
}
107
108
private FingerPrint sameNameFingerPrint(FingerPrint fp1, FingerPrint fp2) {
109
checkClassName(fp1);
110
checkClassName(fp2);
111
// entries/classes with same name, return fp2 for now ?
112
return fp2;
113
}
114
115
private FingerPrint getFingerPrint(ZipEntry ze) {
116
// figure out the version and basename from the ZipEntry
117
String ename = ze.getName();
118
String bname = ename;
119
int version = 0;
120
121
if (ename.startsWith(VERSIONS_DIR)) {
122
int n = ename.indexOf("/", VERSIONS_DIR_LENGTH);
123
if (n == -1) {
124
throw new InvalidJarException(
125
formatMsg("error.validator.version.notnumber", ename));
126
}
127
try {
128
version = Integer.parseInt(ename, VERSIONS_DIR_LENGTH, n, 10);
129
} catch (NumberFormatException x) {
130
throw new InvalidJarException(
131
formatMsg("error.validator.version.notnumber", ename));
132
}
133
if (n == ename.length()) {
134
throw new InvalidJarException(
135
formatMsg("error.validator.entryname.tooshort", ename));
136
}
137
bname = ename.substring(n + 1);
138
}
139
140
// return the cooresponding fingerprint entry
141
try (InputStream is = zf.getInputStream(ze)) {
142
return new FingerPrint(bname, ename, version, is.readAllBytes());
143
} catch (IOException x) {
144
throw new InvalidJarException(x.getMessage());
145
}
146
}
147
148
/*
149
* Validates (a) if there is any isolated nested class, and (b) if the
150
* class name in class file (by asm) matches the entry's basename.
151
*/
152
public void validateBase(Map<String, FingerPrint> fps) {
153
fps.values().forEach( fp -> {
154
if (!checkClassName(fp)) {
155
return;
156
}
157
if (fp.isNestedClass()) {
158
checkNestedClass(fp, fps);
159
}
160
classes.put(fp.className(), fp);
161
});
162
}
163
164
public void validateVersioned(Map<String, FingerPrint> fps) {
165
166
fps.values().forEach( fp -> {
167
168
// validate the versioned module-info
169
if (MODULE_INFO.equals(fp.basename())) {
170
checkModuleDescriptor(fp.entryName());
171
return;
172
}
173
// process a versioned entry, look for previous entry with same name
174
FingerPrint matchFp = classes.get(fp.className());
175
if (matchFp == null) {
176
// no match found
177
if (fp.isNestedClass()) {
178
checkNestedClass(fp, fps);
179
return;
180
}
181
if (fp.isPublicClass()) {
182
if (!isConcealed(fp.className())) {
183
errorAndInvalid(formatMsg("error.validator.new.public.class",
184
fp.entryName()));
185
return;
186
}
187
// entry is a public class entry in a concealed package
188
warn(formatMsg("warn.validator.concealed.public.class",
189
fp.entryName()));
190
}
191
classes.put(fp.className(), fp);
192
return;
193
}
194
195
// are the two classes/resources identical?
196
if (fp.isIdentical(matchFp)) {
197
warn(formatMsg("warn.validator.identical.entry", fp.entryName()));
198
return; // it's okay, just takes up room
199
}
200
201
// ok, not identical, check for compatible class version and api
202
if (fp.isNestedClass()) {
203
checkNestedClass(fp, fps);
204
return; // fall through, need check nested public class??
205
}
206
if (!fp.isCompatibleVersion(matchFp)) {
207
errorAndInvalid(formatMsg("error.validator.incompatible.class.version",
208
fp.entryName()));
209
return;
210
}
211
if (!fp.isSameAPI(matchFp)) {
212
errorAndInvalid(formatMsg("error.validator.different.api",
213
fp.entryName()));
214
return;
215
}
216
if (!checkClassName(fp)) {
217
return;
218
}
219
classes.put(fp.className(), fp);
220
221
return;
222
});
223
}
224
225
/*
226
* Checks whether or not the given versioned module descriptor's attributes
227
* are valid when compared against the root/base module descriptor.
228
*
229
* A versioned module descriptor must be identical to the root/base module
230
* descriptor, with two exceptions:
231
* - A versioned descriptor can have different non-public `requires`
232
* clauses of platform ( `java.*` and `jdk.*` ) modules, and
233
* - A versioned descriptor can have different `uses` clauses, even of
234
* service types defined outside of the platform modules.
235
*/
236
private void checkModuleDescriptor(String miName) {
237
ZipEntry ze = zf.getEntry(miName);
238
if (ze != null) {
239
try (InputStream jis = zf.getInputStream(ze)) {
240
ModuleDescriptor md = ModuleDescriptor.read(jis);
241
// Initialize the base md if it's not yet. A "base" md can be either the
242
// root module-info.class or the first versioned module-info.class
243
ModuleDescriptor base = this.md;
244
245
if (base == null) {
246
concealedPkgs = new HashSet<>(md.packages());
247
md.exports().stream().map(Exports::source).forEach(concealedPkgs::remove);
248
md.opens().stream().map(Opens::source).forEach(concealedPkgs::remove);
249
// must have the implementation class of the services it 'provides'.
250
if (md.provides().stream().map(Provides::providers)
251
.flatMap(List::stream)
252
.filter(p -> zf.getEntry(toBinaryName(p)) == null)
253
.peek(p -> error(formatMsg("error.missing.provider", p)))
254
.count() != 0) {
255
isValid = false;
256
return;
257
}
258
this.md = md;
259
this.mdName = miName;
260
return;
261
}
262
263
if (!base.name().equals(md.name())) {
264
errorAndInvalid(getMsg("error.validator.info.name.notequal"));
265
}
266
if (!base.requires().equals(md.requires())) {
267
Set<Requires> baseRequires = base.requires();
268
for (Requires r : md.requires()) {
269
if (baseRequires.contains(r))
270
continue;
271
if (r.modifiers().contains(Requires.Modifier.TRANSITIVE)) {
272
errorAndInvalid(getMsg("error.validator.info.requires.transitive"));
273
} else if (!isPlatformModule(r.name())) {
274
errorAndInvalid(getMsg("error.validator.info.requires.added"));
275
}
276
}
277
for (Requires r : baseRequires) {
278
Set<Requires> mdRequires = md.requires();
279
if (mdRequires.contains(r))
280
continue;
281
if (!isPlatformModule(r.name())) {
282
errorAndInvalid(getMsg("error.validator.info.requires.dropped"));
283
}
284
}
285
}
286
if (!base.exports().equals(md.exports())) {
287
errorAndInvalid(getMsg("error.validator.info.exports.notequal"));
288
}
289
if (!base.opens().equals(md.opens())) {
290
errorAndInvalid(getMsg("error.validator.info.opens.notequal"));
291
}
292
if (!base.provides().equals(md.provides())) {
293
errorAndInvalid(getMsg("error.validator.info.provides.notequal"));
294
}
295
if (!base.mainClass().equals(md.mainClass())) {
296
errorAndInvalid(formatMsg("error.validator.info.manclass.notequal",
297
ze.getName()));
298
}
299
if (!base.version().equals(md.version())) {
300
errorAndInvalid(formatMsg("error.validator.info.version.notequal",
301
ze.getName()));
302
}
303
} catch (Exception x) {
304
errorAndInvalid(x.getMessage() + " : " + miName);
305
}
306
}
307
}
308
309
private boolean checkClassName(FingerPrint fp) {
310
if (fp.className().equals(className(fp.basename()))) {
311
return true;
312
}
313
error(formatMsg2("error.validator.names.mismatch",
314
fp.entryName(), fp.className().replace("/", ".")));
315
return isValid = false;
316
}
317
318
private boolean checkNestedClass(FingerPrint fp, Map<String, FingerPrint> outerClasses) {
319
if (outerClasses.containsKey(fp.outerClassName())) {
320
return true;
321
}
322
// outer class was not available
323
324
error(formatMsg("error.validator.isolated.nested.class", fp.entryName()));
325
return isValid = false;
326
}
327
328
private boolean isConcealed(String className) {
329
if (concealedPkgs.isEmpty()) {
330
return false;
331
}
332
int idx = className.lastIndexOf('/');
333
String pkgName = idx != -1 ? className.substring(0, idx).replace('/', '.') : "";
334
return concealedPkgs.contains(pkgName);
335
}
336
337
private static boolean isPlatformModule(String name) {
338
return name.startsWith("java.") || name.startsWith("jdk.");
339
}
340
341
private static String className(String entryName) {
342
return entryName.endsWith(".class") ? entryName.substring(0, entryName.length() - 6) : null;
343
}
344
345
private void error(String msg) {
346
main.error(msg);
347
}
348
349
private void errorAndInvalid(String msg) {
350
main.error(msg);
351
isValid = false;
352
}
353
354
private void warn(String msg) {
355
main.warn(msg);
356
}
357
}
358
359