Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java
41159 views
1
/*
2
* Copyright (c) 2001, 2021, 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 jdk.internal.reflect;
27
28
import java.io.Externalizable;
29
import java.io.ObjectInputStream;
30
import java.io.ObjectOutputStream;
31
import java.io.ObjectStreamClass;
32
import java.io.OptionalDataException;
33
import java.io.Serializable;
34
import java.lang.invoke.MethodHandle;
35
import java.lang.invoke.MethodHandles;
36
import java.lang.reflect.Constructor;
37
import java.lang.reflect.Executable;
38
import java.lang.reflect.Field;
39
import java.lang.reflect.InvocationTargetException;
40
import java.lang.reflect.Method;
41
import java.lang.reflect.Modifier;
42
import java.security.PrivilegedAction;
43
import java.util.Properties;
44
import jdk.internal.access.JavaLangReflectAccess;
45
import jdk.internal.access.SharedSecrets;
46
import jdk.internal.misc.VM;
47
import sun.reflect.misc.ReflectUtil;
48
import sun.security.action.GetPropertyAction;
49
import sun.security.util.SecurityConstants;
50
51
/** <P> The master factory for all reflective objects, both those in
52
java.lang.reflect (Fields, Methods, Constructors) as well as their
53
delegates (FieldAccessors, MethodAccessors, ConstructorAccessors).
54
</P>
55
56
<P> The methods in this class are extremely unsafe and can cause
57
subversion of both the language and the verifier. For this reason,
58
they are all instance methods, and access to the constructor of
59
this factory is guarded by a security check, in similar style to
60
{@link jdk.internal.misc.Unsafe}. </P>
61
*/
62
63
public class ReflectionFactory {
64
65
private static boolean initted = false;
66
private static final ReflectionFactory soleInstance = new ReflectionFactory();
67
68
69
/* Method for static class initializer <clinit>, or null */
70
private static volatile Method hasStaticInitializerMethod;
71
72
//
73
// "Inflation" mechanism. Loading bytecodes to implement
74
// Method.invoke() and Constructor.newInstance() currently costs
75
// 3-4x more than an invocation via native code for the first
76
// invocation (though subsequent invocations have been benchmarked
77
// to be over 20x faster). Unfortunately this cost increases
78
// startup time for certain applications that use reflection
79
// intensively (but only once per class) to bootstrap themselves.
80
// To avoid this penalty we reuse the existing JVM entry points
81
// for the first few invocations of Methods and Constructors and
82
// then switch to the bytecode-based implementations.
83
//
84
// Package-private to be accessible to NativeMethodAccessorImpl
85
// and NativeConstructorAccessorImpl
86
private static boolean noInflation = false;
87
private static int inflationThreshold = 15;
88
89
// true if deserialization constructor checking is disabled
90
private static boolean disableSerialConstructorChecks = false;
91
92
private final JavaLangReflectAccess langReflectAccess;
93
private ReflectionFactory() {
94
this.langReflectAccess = SharedSecrets.getJavaLangReflectAccess();
95
}
96
97
/**
98
* A convenience class for acquiring the capability to instantiate
99
* reflective objects. Use this instead of a raw call to {@link
100
* #getReflectionFactory} in order to avoid being limited by the
101
* permissions of your callers.
102
*
103
* <p>An instance of this class can be used as the argument of
104
* <code>AccessController.doPrivileged</code>.
105
*/
106
public static final class GetReflectionFactoryAction
107
implements PrivilegedAction<ReflectionFactory> {
108
public ReflectionFactory run() {
109
return getReflectionFactory();
110
}
111
}
112
113
/**
114
* Provides the caller with the capability to instantiate reflective
115
* objects.
116
*
117
* <p> First, if there is a security manager, its
118
* <code>checkPermission</code> method is called with a {@link
119
* java.lang.RuntimePermission} with target
120
* <code>"reflectionFactoryAccess"</code>. This may result in a
121
* security exception.
122
*
123
* <p> The returned <code>ReflectionFactory</code> object should be
124
* carefully guarded by the caller, since it can be used to read and
125
* write private data and invoke private methods, as well as to load
126
* unverified bytecodes. It must never be passed to untrusted code.
127
*
128
* @exception SecurityException if a security manager exists and its
129
* <code>checkPermission</code> method doesn't allow
130
* access to the RuntimePermission "reflectionFactoryAccess". */
131
public static ReflectionFactory getReflectionFactory() {
132
@SuppressWarnings("removal")
133
SecurityManager security = System.getSecurityManager();
134
if (security != null) {
135
security.checkPermission(
136
SecurityConstants.REFLECTION_FACTORY_ACCESS_PERMISSION);
137
}
138
return soleInstance;
139
}
140
141
/**
142
* Returns an alternate reflective Method instance for the given method
143
* intended for reflection to invoke, if present.
144
*
145
* A trusted method can define an alternate implementation for a method `foo`
146
* by defining a method named "reflected$foo" that will be invoked
147
* reflectively.
148
*/
149
private static Method findMethodForReflection(Method method) {
150
String altName = "reflected$" + method.getName();
151
try {
152
return method.getDeclaringClass()
153
.getDeclaredMethod(altName, method.getParameterTypes());
154
} catch (NoSuchMethodException ex) {
155
return null;
156
}
157
}
158
159
//--------------------------------------------------------------------------
160
//
161
// Routines used by java.lang.reflect
162
//
163
//
164
165
/*
166
* Note: this routine can cause the declaring class for the field
167
* be initialized and therefore must not be called until the
168
* first get/set of this field.
169
* @param field the field
170
* @param override true if caller has overridden accessibility
171
*/
172
public FieldAccessor newFieldAccessor(Field field, boolean override) {
173
checkInitted();
174
175
Field root = langReflectAccess.getRoot(field);
176
if (root != null) {
177
// FieldAccessor will use the root unless the modifiers have
178
// been overridden
179
if (root.getModifiers() == field.getModifiers() || !override) {
180
field = root;
181
}
182
}
183
boolean isFinal = Modifier.isFinal(field.getModifiers());
184
boolean isReadOnly = isFinal && (!override || langReflectAccess.isTrustedFinalField(field));
185
return UnsafeFieldAccessorFactory.newFieldAccessor(field, isReadOnly);
186
}
187
188
public MethodAccessor newMethodAccessor(Method method) {
189
checkInitted();
190
191
if (Reflection.isCallerSensitive(method)) {
192
Method altMethod = findMethodForReflection(method);
193
if (altMethod != null) {
194
method = altMethod;
195
}
196
}
197
198
// use the root Method that will not cache caller class
199
Method root = langReflectAccess.getRoot(method);
200
if (root != null) {
201
method = root;
202
}
203
204
if (noInflation && !method.getDeclaringClass().isHidden()) {
205
return new MethodAccessorGenerator().
206
generateMethod(method.getDeclaringClass(),
207
method.getName(),
208
method.getParameterTypes(),
209
method.getReturnType(),
210
method.getExceptionTypes(),
211
method.getModifiers());
212
} else {
213
NativeMethodAccessorImpl acc =
214
new NativeMethodAccessorImpl(method);
215
DelegatingMethodAccessorImpl res =
216
new DelegatingMethodAccessorImpl(acc);
217
acc.setParent(res);
218
return res;
219
}
220
}
221
222
public ConstructorAccessor newConstructorAccessor(Constructor<?> c) {
223
checkInitted();
224
225
Class<?> declaringClass = c.getDeclaringClass();
226
if (Modifier.isAbstract(declaringClass.getModifiers())) {
227
return new InstantiationExceptionConstructorAccessorImpl(null);
228
}
229
if (declaringClass == Class.class) {
230
return new InstantiationExceptionConstructorAccessorImpl
231
("Can not instantiate java.lang.Class");
232
}
233
234
// use the root Constructor that will not cache caller class
235
Constructor<?> root = langReflectAccess.getRoot(c);
236
if (root != null) {
237
c = root;
238
}
239
240
// Bootstrapping issue: since we use Class.newInstance() in
241
// the ConstructorAccessor generation process, we have to
242
// break the cycle here.
243
if (Reflection.isSubclassOf(declaringClass,
244
ConstructorAccessorImpl.class)) {
245
return new BootstrapConstructorAccessorImpl(c);
246
}
247
248
if (noInflation && !c.getDeclaringClass().isHidden()) {
249
return new MethodAccessorGenerator().
250
generateConstructor(c.getDeclaringClass(),
251
c.getParameterTypes(),
252
c.getExceptionTypes(),
253
c.getModifiers());
254
} else {
255
NativeConstructorAccessorImpl acc =
256
new NativeConstructorAccessorImpl(c);
257
DelegatingConstructorAccessorImpl res =
258
new DelegatingConstructorAccessorImpl(acc);
259
acc.setParent(res);
260
return res;
261
}
262
}
263
264
//--------------------------------------------------------------------------
265
//
266
// Routines used by java.lang
267
//
268
//
269
270
/** Creates a new java.lang.reflect.Constructor. Access checks as
271
per java.lang.reflect.AccessibleObject are not overridden. */
272
public Constructor<?> newConstructor(Class<?> declaringClass,
273
Class<?>[] parameterTypes,
274
Class<?>[] checkedExceptions,
275
int modifiers,
276
int slot,
277
String signature,
278
byte[] annotations,
279
byte[] parameterAnnotations)
280
{
281
return langReflectAccess.newConstructor(declaringClass,
282
parameterTypes,
283
checkedExceptions,
284
modifiers,
285
slot,
286
signature,
287
annotations,
288
parameterAnnotations);
289
}
290
291
/** Gets the ConstructorAccessor object for a
292
java.lang.reflect.Constructor */
293
public ConstructorAccessor getConstructorAccessor(Constructor<?> c) {
294
return langReflectAccess.getConstructorAccessor(c);
295
}
296
297
/** Sets the ConstructorAccessor object for a
298
java.lang.reflect.Constructor */
299
public void setConstructorAccessor(Constructor<?> c,
300
ConstructorAccessor accessor)
301
{
302
langReflectAccess.setConstructorAccessor(c, accessor);
303
}
304
305
/** Makes a copy of the passed method. The returned method is a
306
"child" of the passed one; see the comments in Method.java for
307
details. */
308
public Method copyMethod(Method arg) {
309
return langReflectAccess.copyMethod(arg);
310
}
311
312
/** Makes a copy of the passed method. The returned method is NOT
313
* a "child" but a "sibling" of the Method in arg. Should only be
314
* used on non-root methods. */
315
public Method leafCopyMethod(Method arg) {
316
return langReflectAccess.leafCopyMethod(arg);
317
}
318
319
320
/** Makes a copy of the passed field. The returned field is a
321
"child" of the passed one; see the comments in Field.java for
322
details. */
323
public Field copyField(Field arg) {
324
return langReflectAccess.copyField(arg);
325
}
326
327
/** Makes a copy of the passed constructor. The returned
328
constructor is a "child" of the passed one; see the comments
329
in Constructor.java for details. */
330
public <T> Constructor<T> copyConstructor(Constructor<T> arg) {
331
return langReflectAccess.copyConstructor(arg);
332
}
333
334
/** Gets the byte[] that encodes TypeAnnotations on an executable.
335
*/
336
public byte[] getExecutableTypeAnnotationBytes(Executable ex) {
337
return langReflectAccess.getExecutableTypeAnnotationBytes(ex);
338
}
339
340
public Class<?>[] getExecutableSharedParameterTypes(Executable ex) {
341
return langReflectAccess.getExecutableSharedParameterTypes(ex);
342
}
343
344
public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller)
345
throws IllegalAccessException, InstantiationException, InvocationTargetException
346
{
347
return langReflectAccess.newInstance(ctor, args, caller);
348
}
349
350
//--------------------------------------------------------------------------
351
//
352
// Routines used by serialization
353
//
354
//
355
356
public final Constructor<?> newConstructorForExternalization(Class<?> cl) {
357
if (!Externalizable.class.isAssignableFrom(cl)) {
358
return null;
359
}
360
try {
361
Constructor<?> cons = cl.getConstructor();
362
cons.setAccessible(true);
363
return cons;
364
} catch (NoSuchMethodException ex) {
365
return null;
366
}
367
}
368
369
public final Constructor<?> newConstructorForSerialization(Class<?> cl,
370
Constructor<?> constructorToCall)
371
{
372
if (constructorToCall.getDeclaringClass() == cl) {
373
constructorToCall.setAccessible(true);
374
return constructorToCall;
375
}
376
return generateConstructor(cl, constructorToCall);
377
}
378
379
/**
380
* Given a class, determines whether its superclass has
381
* any constructors that are accessible from the class.
382
* This is a special purpose method intended to do access
383
* checking for a serializable class and its superclasses
384
* up to, but not including, the first non-serializable
385
* superclass. This also implies that the superclass is
386
* always non-null, because a serializable class must be a
387
* class (not an interface) and Object is not serializable.
388
*
389
* @param cl the class from which access is checked
390
* @return whether the superclass has a constructor accessible from cl
391
*/
392
private boolean superHasAccessibleConstructor(Class<?> cl) {
393
Class<?> superCl = cl.getSuperclass();
394
assert Serializable.class.isAssignableFrom(cl);
395
assert superCl != null;
396
if (packageEquals(cl, superCl)) {
397
// accessible if any non-private constructor is found
398
for (Constructor<?> ctor : superCl.getDeclaredConstructors()) {
399
if ((ctor.getModifiers() & Modifier.PRIVATE) == 0) {
400
return true;
401
}
402
}
403
if (Reflection.areNestMates(cl, superCl)) {
404
return true;
405
}
406
return false;
407
} else {
408
// sanity check to ensure the parent is protected or public
409
if ((superCl.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) {
410
return false;
411
}
412
// accessible if any constructor is protected or public
413
for (Constructor<?> ctor : superCl.getDeclaredConstructors()) {
414
if ((ctor.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) != 0) {
415
return true;
416
}
417
}
418
return false;
419
}
420
}
421
422
/**
423
* Returns a constructor that allocates an instance of cl and that then initializes
424
* the instance by calling the no-arg constructor of its first non-serializable
425
* superclass. This is specified in the Serialization Specification, section 3.1,
426
* in step 11 of the deserialization process. If cl is not serializable, returns
427
* cl's no-arg constructor. If no accessible constructor is found, or if the
428
* class hierarchy is somehow malformed (e.g., a serializable class has no
429
* superclass), null is returned.
430
*
431
* @param cl the class for which a constructor is to be found
432
* @return the generated constructor, or null if none is available
433
*/
434
public final Constructor<?> newConstructorForSerialization(Class<?> cl) {
435
Class<?> initCl = cl;
436
while (Serializable.class.isAssignableFrom(initCl)) {
437
Class<?> prev = initCl;
438
if ((initCl = initCl.getSuperclass()) == null ||
439
(!disableSerialConstructorChecks && !superHasAccessibleConstructor(prev))) {
440
return null;
441
}
442
}
443
Constructor<?> constructorToCall;
444
try {
445
constructorToCall = initCl.getDeclaredConstructor();
446
int mods = constructorToCall.getModifiers();
447
if ((mods & Modifier.PRIVATE) != 0 ||
448
((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 &&
449
!packageEquals(cl, initCl))) {
450
return null;
451
}
452
} catch (NoSuchMethodException ex) {
453
return null;
454
}
455
return generateConstructor(cl, constructorToCall);
456
}
457
458
private final Constructor<?> generateConstructor(Class<?> cl,
459
Constructor<?> constructorToCall) {
460
461
462
ConstructorAccessor acc = new MethodAccessorGenerator().
463
generateSerializationConstructor(cl,
464
constructorToCall.getParameterTypes(),
465
constructorToCall.getExceptionTypes(),
466
constructorToCall.getModifiers(),
467
constructorToCall.getDeclaringClass());
468
Constructor<?> c = newConstructor(constructorToCall.getDeclaringClass(),
469
constructorToCall.getParameterTypes(),
470
constructorToCall.getExceptionTypes(),
471
constructorToCall.getModifiers(),
472
langReflectAccess.
473
getConstructorSlot(constructorToCall),
474
langReflectAccess.
475
getConstructorSignature(constructorToCall),
476
langReflectAccess.
477
getConstructorAnnotations(constructorToCall),
478
langReflectAccess.
479
getConstructorParameterAnnotations(constructorToCall));
480
setConstructorAccessor(c, acc);
481
c.setAccessible(true);
482
return c;
483
}
484
485
public final MethodHandle readObjectForSerialization(Class<?> cl) {
486
return findReadWriteObjectForSerialization(cl, "readObject", ObjectInputStream.class);
487
}
488
489
public final MethodHandle readObjectNoDataForSerialization(Class<?> cl) {
490
return findReadWriteObjectForSerialization(cl, "readObjectNoData", ObjectInputStream.class);
491
}
492
493
public final MethodHandle writeObjectForSerialization(Class<?> cl) {
494
return findReadWriteObjectForSerialization(cl, "writeObject", ObjectOutputStream.class);
495
}
496
497
private final MethodHandle findReadWriteObjectForSerialization(Class<?> cl,
498
String methodName,
499
Class<?> streamClass) {
500
if (!Serializable.class.isAssignableFrom(cl)) {
501
return null;
502
}
503
504
try {
505
Method meth = cl.getDeclaredMethod(methodName, streamClass);
506
int mods = meth.getModifiers();
507
if (meth.getReturnType() != Void.TYPE ||
508
Modifier.isStatic(mods) ||
509
!Modifier.isPrivate(mods)) {
510
return null;
511
}
512
meth.setAccessible(true);
513
return MethodHandles.lookup().unreflect(meth);
514
} catch (NoSuchMethodException ex) {
515
return null;
516
} catch (IllegalAccessException ex1) {
517
throw new InternalError("Error", ex1);
518
}
519
}
520
521
/**
522
* Returns a MethodHandle for {@code writeReplace} on the serializable class
523
* or null if no match found.
524
* @param cl a serializable class
525
* @returnss the {@code writeReplace} MethodHandle or {@code null} if not found
526
*/
527
public final MethodHandle writeReplaceForSerialization(Class<?> cl) {
528
return getReplaceResolveForSerialization(cl, "writeReplace");
529
}
530
531
/**
532
* Returns a MethodHandle for {@code readResolve} on the serializable class
533
* or null if no match found.
534
* @param cl a serializable class
535
* @returns the {@code writeReplace} MethodHandle or {@code null} if not found
536
*/
537
public final MethodHandle readResolveForSerialization(Class<?> cl) {
538
return getReplaceResolveForSerialization(cl, "readResolve");
539
}
540
541
/**
542
* Lookup readResolve or writeReplace on a class with specified
543
* signature constraints.
544
* @param cl a serializable class
545
* @param methodName the method name to find
546
* @returns a MethodHandle for the method or {@code null} if not found or
547
* has the wrong signature.
548
*/
549
private MethodHandle getReplaceResolveForSerialization(Class<?> cl,
550
String methodName) {
551
if (!Serializable.class.isAssignableFrom(cl)) {
552
return null;
553
}
554
555
Class<?> defCl = cl;
556
while (defCl != null) {
557
try {
558
Method m = defCl.getDeclaredMethod(methodName);
559
if (m.getReturnType() != Object.class) {
560
return null;
561
}
562
int mods = m.getModifiers();
563
if (Modifier.isStatic(mods) | Modifier.isAbstract(mods)) {
564
return null;
565
} else if (Modifier.isPublic(mods) | Modifier.isProtected(mods)) {
566
// fall through
567
} else if (Modifier.isPrivate(mods) && (cl != defCl)) {
568
return null;
569
} else if (!packageEquals(cl, defCl)) {
570
return null;
571
}
572
try {
573
// Normal return
574
m.setAccessible(true);
575
return MethodHandles.lookup().unreflect(m);
576
} catch (IllegalAccessException ex0) {
577
// setAccessible should prevent IAE
578
throw new InternalError("Error", ex0);
579
}
580
} catch (NoSuchMethodException ex) {
581
defCl = defCl.getSuperclass();
582
}
583
}
584
return null;
585
}
586
587
/**
588
* Returns true if the given class defines a static initializer method,
589
* false otherwise.
590
*/
591
public final boolean hasStaticInitializerForSerialization(Class<?> cl) {
592
Method m = hasStaticInitializerMethod;
593
if (m == null) {
594
try {
595
m = ObjectStreamClass.class.getDeclaredMethod("hasStaticInitializer",
596
new Class<?>[]{Class.class});
597
m.setAccessible(true);
598
hasStaticInitializerMethod = m;
599
} catch (NoSuchMethodException ex) {
600
throw new InternalError("No such method hasStaticInitializer on "
601
+ ObjectStreamClass.class, ex);
602
}
603
}
604
try {
605
return (Boolean) m.invoke(null, cl);
606
} catch (InvocationTargetException | IllegalAccessException ex) {
607
throw new InternalError("Exception invoking hasStaticInitializer", ex);
608
}
609
}
610
611
/**
612
* Return the accessible constructor for OptionalDataException signaling eof.
613
* @returns the eof constructor for OptionalDataException
614
*/
615
public final Constructor<OptionalDataException> newOptionalDataExceptionForSerialization() {
616
try {
617
Constructor<OptionalDataException> boolCtor =
618
OptionalDataException.class.getDeclaredConstructor(Boolean.TYPE);
619
boolCtor.setAccessible(true);
620
return boolCtor;
621
} catch (NoSuchMethodException ex) {
622
throw new InternalError("Constructor not found", ex);
623
}
624
}
625
626
//--------------------------------------------------------------------------
627
//
628
// Internals only below this point
629
//
630
631
static int inflationThreshold() {
632
return inflationThreshold;
633
}
634
635
/** We have to defer full initialization of this class until after
636
the static initializer is run since java.lang.reflect.Method's
637
static initializer (more properly, that for
638
java.lang.reflect.AccessibleObject) causes this class's to be
639
run, before the system properties are set up. */
640
private static void checkInitted() {
641
if (initted) return;
642
643
// Defer initialization until module system is initialized so as
644
// to avoid inflation and spinning bytecode in unnamed modules
645
// during early startup.
646
if (!VM.isModuleSystemInited()) {
647
return;
648
}
649
650
Properties props = GetPropertyAction.privilegedGetProperties();
651
String val = props.getProperty("sun.reflect.noInflation");
652
if (val != null && val.equals("true")) {
653
noInflation = true;
654
}
655
656
val = props.getProperty("sun.reflect.inflationThreshold");
657
if (val != null) {
658
try {
659
inflationThreshold = Integer.parseInt(val);
660
} catch (NumberFormatException e) {
661
throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", e);
662
}
663
}
664
665
disableSerialConstructorChecks =
666
"true".equals(props.getProperty("jdk.disableSerialConstructorChecks"));
667
668
initted = true;
669
}
670
671
/**
672
* Returns true if classes are defined in the classloader and same package, false
673
* otherwise.
674
* @param cl1 a class
675
* @param cl2 another class
676
* @returns true if the two classes are in the same classloader and package
677
*/
678
private static boolean packageEquals(Class<?> cl1, Class<?> cl2) {
679
assert !cl1.isArray() && !cl2.isArray();
680
681
if (cl1 == cl2) {
682
return true;
683
}
684
685
return cl1.getClassLoader() == cl2.getClassLoader() &&
686
cl1.getPackageName() == cl2.getPackageName();
687
}
688
689
}
690
691