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/FingerPrint.java
41161 views
1
/*
2
* Copyright (c) 2016, 2018, 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 jdk.internal.org.objectweb.asm.*;
29
30
import java.io.IOException;
31
import java.io.InputStream;
32
import java.security.MessageDigest;
33
import java.security.NoSuchAlgorithmException;
34
import java.util.HashMap;
35
import java.util.HashSet;
36
import java.util.Map;
37
import java.util.Set;
38
39
/**
40
* A FingerPrint is an abstract representation of a JarFile entry that contains
41
* information to determine if the entry represents a class or a
42
* resource, and whether two entries are identical. If the FingerPrint represents
43
* a class, it also contains information to (1) describe the public API;
44
* (2) compare the public API of this class with another class; (3) determine
45
* whether or not it's a nested class and, if so, the name of the associated
46
* outer class; and (4) for an canonically ordered set of classes determine
47
* if the class versions are compatible. A set of classes is canonically
48
* ordered if the classes in the set have the same name, and the base class
49
* precedes the versioned classes and if each versioned class with version
50
* {@code n} precedes classes with versions {@code > n} for all versions
51
* {@code n}.
52
*/
53
final class FingerPrint {
54
private static final MessageDigest MD;
55
56
private final String basename;
57
private final String entryName;
58
private final int mrversion;
59
60
private final byte[] sha1;
61
private final ClassAttributes attrs;
62
private final boolean isClassEntry;
63
64
static {
65
try {
66
MD = MessageDigest.getInstance("SHA-1");
67
} catch (NoSuchAlgorithmException x) {
68
// log big problem?
69
throw new RuntimeException(x);
70
}
71
}
72
73
public FingerPrint(String basename, String entryName, int mrversion, byte[] bytes)
74
throws IOException {
75
this.basename = basename;
76
this.entryName = entryName;
77
this.mrversion = mrversion;
78
if (isCafeBabe(bytes)) {
79
isClassEntry = true;
80
sha1 = sha1(bytes, 8); // skip magic number and major/minor version
81
attrs = getClassAttributes(bytes);
82
} else {
83
isClassEntry = false;
84
sha1 = null;
85
attrs = null;
86
}
87
}
88
89
public boolean isClass() {
90
return isClassEntry;
91
}
92
93
public boolean isNestedClass() {
94
return attrs.nestedClass;
95
}
96
97
public boolean isPublicClass() {
98
return attrs.publicClass;
99
}
100
101
public boolean isIdentical(FingerPrint that) {
102
if (that == null) return false;
103
if (this == that) return true;
104
return isEqual(this.sha1, that.sha1);
105
}
106
107
public boolean isCompatibleVersion(FingerPrint that) {
108
return attrs.version >= that.attrs.version;
109
}
110
111
public boolean isSameAPI(FingerPrint that) {
112
if (that == null) return false;
113
return attrs.equals(that.attrs);
114
}
115
116
public String basename() {
117
return basename;
118
}
119
120
public String entryName() {
121
return entryName;
122
}
123
124
public String className() {
125
return attrs.name;
126
}
127
128
public int mrversion() {
129
return mrversion;
130
}
131
132
public String outerClassName() {
133
return attrs.outerClassName;
134
}
135
136
private byte[] sha1(byte[] entry) {
137
MD.update(entry);
138
return MD.digest();
139
}
140
141
private byte[] sha1(byte[] entry, int offset) {
142
MD.update(entry, offset, entry.length - offset);
143
return MD.digest();
144
}
145
146
private boolean isEqual(byte[] sha1_1, byte[] sha1_2) {
147
return MessageDigest.isEqual(sha1_1, sha1_2);
148
}
149
150
private static final byte[] cafeBabe = {(byte)0xca, (byte)0xfe, (byte)0xba, (byte)0xbe};
151
152
private boolean isCafeBabe(byte[] bytes) {
153
if (bytes.length < 4) return false;
154
for (int i = 0; i < 4; i++) {
155
if (bytes[i] != cafeBabe[i]) {
156
return false;
157
}
158
}
159
return true;
160
}
161
162
private ClassAttributes getClassAttributes(byte[] bytes) {
163
ClassReader rdr = new ClassReader(bytes);
164
ClassAttributes attrs = new ClassAttributes();
165
rdr.accept(attrs, 0);
166
return attrs;
167
}
168
169
private static final class Field {
170
private final int access;
171
private final String name;
172
private final String desc;
173
174
Field(int access, String name, String desc) {
175
this.access = access;
176
this.name = name;
177
this.desc = desc;
178
}
179
180
@Override
181
public boolean equals(Object that) {
182
if (that == null) return false;
183
if (this == that) return true;
184
if (!(that instanceof Field)) return false;
185
Field field = (Field)that;
186
return (access == field.access) && name.equals(field.name)
187
&& desc.equals(field.desc);
188
}
189
190
@Override
191
public int hashCode() {
192
int result = 17;
193
result = 37 * result + access;
194
result = 37 * result + name.hashCode();
195
result = 37 * result + desc.hashCode();
196
return result;
197
}
198
}
199
200
private static final class Method {
201
private final int access;
202
private final String name;
203
private final String desc;
204
private final Set<String> exceptions;
205
206
Method(int access, String name, String desc, Set<String> exceptions) {
207
this.access = access;
208
this.name = name;
209
this.desc = desc;
210
this.exceptions = exceptions;
211
}
212
213
@Override
214
public boolean equals(Object that) {
215
if (that == null) return false;
216
if (this == that) return true;
217
if (!(that instanceof Method)) return false;
218
Method method = (Method)that;
219
return (access == method.access) && name.equals(method.name)
220
&& desc.equals(method.desc)
221
&& exceptions.equals(method.exceptions);
222
}
223
224
@Override
225
public int hashCode() {
226
int result = 17;
227
result = 37 * result + access;
228
result = 37 * result + name.hashCode();
229
result = 37 * result + desc.hashCode();
230
result = 37 * result + exceptions.hashCode();
231
return result;
232
}
233
}
234
235
private static final class ClassAttributes extends ClassVisitor {
236
private String name;
237
private String outerClassName;
238
private String superName;
239
private int version;
240
private int access;
241
private boolean publicClass;
242
private boolean nestedClass;
243
private final Set<Field> fields = new HashSet<>();
244
private final Set<Method> methods = new HashSet<>();
245
246
public ClassAttributes() {
247
super(Opcodes.ASM7);
248
}
249
250
private boolean isPublic(int access) {
251
return ((access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC)
252
|| ((access & Opcodes.ACC_PROTECTED) == Opcodes.ACC_PROTECTED);
253
}
254
255
@Override
256
public void visit(int version, int access, String name, String signature,
257
String superName, String[] interfaces) {
258
this.version = version;
259
this.access = access;
260
this.name = name;
261
this.nestedClass = name.contains("$");
262
this.superName = superName;
263
this.publicClass = isPublic(access);
264
}
265
266
@Override
267
public void visitOuterClass(String owner, String name, String desc) {
268
if (!this.nestedClass) return;
269
this.outerClassName = owner;
270
}
271
272
@Override
273
public void visitInnerClass(String name, String outerName, String innerName,
274
int access) {
275
if (!this.nestedClass) return;
276
if (outerName == null) return;
277
if (!this.name.equals(name)) return;
278
if (this.outerClassName == null) this.outerClassName = outerName;
279
}
280
281
@Override
282
public FieldVisitor visitField(int access, String name, String desc,
283
String signature, Object value) {
284
if (isPublic(access)) {
285
fields.add(new Field(access, name, desc));
286
}
287
return null;
288
}
289
290
@Override
291
public MethodVisitor visitMethod(int access, String name, String desc,
292
String signature, String[] exceptions) {
293
if (isPublic(access)) {
294
Set<String> exceptionSet = new HashSet<>();
295
if (exceptions != null) {
296
for (String e : exceptions) {
297
exceptionSet.add(e);
298
}
299
}
300
// treat type descriptor as a proxy for signature because signature
301
// is usually null, need to strip off the return type though
302
int n;
303
if (desc != null && (n = desc.lastIndexOf(')')) != -1) {
304
desc = desc.substring(0, n + 1);
305
methods.add(new Method(access, name, desc, exceptionSet));
306
}
307
}
308
return null;
309
}
310
311
@Override
312
public void visitEnd() {
313
this.nestedClass = this.outerClassName != null;
314
}
315
316
@Override
317
public boolean equals(Object that) {
318
if (that == null) return false;
319
if (this == that) return true;
320
if (!(that instanceof ClassAttributes)) return false;
321
ClassAttributes clsAttrs = (ClassAttributes)that;
322
boolean superNameOkay = superName != null
323
? superName.equals(clsAttrs.superName) : true;
324
return access == clsAttrs.access
325
&& superNameOkay
326
&& fields.equals(clsAttrs.fields)
327
&& methods.equals(clsAttrs.methods);
328
}
329
330
@Override
331
public int hashCode() {
332
int result = 17;
333
result = 37 * result + access;
334
result = 37 * result + superName != null ? superName.hashCode() : 0;
335
result = 37 * result + fields.hashCode();
336
result = 37 * result + methods.hashCode();
337
return result;
338
}
339
}
340
}
341
342