Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/loading/ClassLoadingHelper.java
41161 views
1
/*
2
* Copyright (c) 2014, 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.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
*
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*/
23
package gc.g1.unloading.loading;
24
25
import gc.g1.unloading.ExecutionTask;
26
import gc.g1.unloading.bytecode.*;
27
import gc.g1.unloading.check.Assertion;
28
import gc.g1.unloading.check.ClassAssertion;
29
import gc.g1.unloading.check.PhantomizedAssertion;
30
31
import gc.g1.unloading.check.FinalizedAssertion;
32
import gc.g1.unloading.check.PhantomizationServiceThread;
33
import gc.g1.unloading.check.cleanup.UnusedThreadKiller;
34
import gc.g1.unloading.classloaders.DoItYourselfClassLoader;
35
import gc.g1.unloading.classloaders.FinalizableClassloader;
36
import gc.g1.unloading.classloaders.JNIClassloader;
37
import gc.g1.unloading.classloaders.ReflectionClassloader;
38
import gc.g1.unloading.configuration.ClassloadingMethod;
39
import gc.g1.unloading.configuration.KeepRefMode;
40
import gc.g1.unloading.configuration.TestConfiguration;
41
import gc.g1.unloading.keepref.*;
42
import nsk.share.test.ExecutionController;
43
import sun.hotspot.WhiteBox;
44
45
import java.lang.invoke.MethodHandles;
46
import java.lang.invoke.MethodHandles.Lookup;
47
48
import java.lang.ref.*;
49
import java.lang.reflect.Field;
50
import java.lang.reflect.InvocationTargetException;
51
import java.lang.reflect.Method;
52
import java.util.Collection;
53
import java.util.LinkedList;
54
import java.util.Random;
55
56
/**
57
* This helper performs dirty job: loads classes, instantiate objects, performs redefinition etc...
58
*/
59
public class ClassLoadingHelper {
60
61
private static final int NATIVE_VERBOSITY = 2;
62
63
private static final Object[] NO_CP_PATCHES = new Object[0];
64
65
private static BytecodeFactory bf;
66
67
private ExecutionController executionController;
68
69
private PhantomizationServiceThread phantomizationServiceThread;
70
71
private Random random;
72
73
private TestConfiguration configuration;
74
75
// This should be TRUE for bytecode generators that use ASM to create the bytecodes
76
// instead of creating Java source and then compiling with InMemoryJavaCompiler.
77
private boolean prepend_package = true;
78
79
/**
80
* Constructor that creates instance of helper. All arguments are self-explaining.
81
* @param executionController
82
* @param randomSeed
83
* @param testConfiguration
84
*/
85
public ClassLoadingHelper(ExecutionController executionController,
86
long randomSeed, TestConfiguration testConfiguration) {
87
random = new Random(randomSeed);
88
this.executionController = executionController;
89
this.configuration = testConfiguration;
90
91
phantomizationServiceThread = new PhantomizationServiceThread(executionController);
92
Thread thread = new Thread(phantomizationServiceThread);
93
thread.setDaemon(true);
94
thread.start();
95
96
if (configuration.isInMemoryCompilation() && !configuration.isHumongousClass() && !(configuration.getKeepRefMode() == KeepRefMode.THREAD_ITSELF)) {
97
prepend_package = false;
98
bf = new BytecodeGeneratorFactory(random.nextLong());
99
} else {
100
if (configuration.isHumongousClass()) {
101
bf = new BytecodeMutatorFactory(HumongousTemplateClass.class.getName());
102
} else if (configuration.getKeepRefMode() == KeepRefMode.THREAD_ITSELF) {
103
bf = new BytecodeMutatorFactory(ThreadTemplateClass.class.getName());
104
} else {
105
bf = new BytecodeMutatorFactory();
106
}
107
}
108
}
109
110
/**
111
* Load class that's supposed to live. Method returns collection of assertions to check it will live.
112
* @param className_
113
* @return
114
*/
115
public Collection<Assertion> loadClassThatGonnaLive(String className_) {
116
// if generating the bytecodes using ASM then prepend the package name to
117
// the classname so that, when created as a hidden class, it will be in the
118
// same package as its lookup class.
119
Bytecode kit = bf.createBytecode(prepend_package ?
120
"gc.g1.unloading.loading." + className_ : className_);
121
String className = kit.getClassName();
122
byte[] bytecode = kit.getBytecode();
123
Class<?> clazz = loadClass(className, bytecode);
124
Object object = instantiateObject(clazz);
125
Object referenceToKeep = configuration.getWhatToKeep().decideUponRefToKeep(clazz, clazz.getClassLoader(), object);
126
127
redefineIfNeeded(bytecode, clazz);
128
129
warmUpClassIfNeeded(object);
130
Assertion assertion;
131
assertion = new ClassAssertion(className, true);
132
133
switch (configuration.getKeepRefMode()) {
134
case STRONG_REFERENCE:
135
assertion.keepLink(referenceToKeep);
136
break;
137
case SOFT_REFERENCE:
138
assertion.keepLink(new SoftReference<Object>(referenceToKeep));
139
break;
140
case STATIC_FIELD:
141
RefHolder holder1 = new InStaticFieldHolder();
142
assertion.keepLink(holder1.hold(referenceToKeep));
143
break;
144
case STACK_LOCAL:
145
RefHolder holder2 = new InStackLocalHolder(); // UnusedThreadKiller
146
assertion.keepLink(holder2.hold(referenceToKeep));
147
break;
148
case THREAD_FIELD:
149
RefHolder holder3 = new InThreadFieldHolder(); // UnusedThreadKiller
150
assertion.keepLink(holder3.hold(referenceToKeep));
151
break;
152
case THREAD_ITSELF:
153
Thread objectThread = (Thread) object;
154
objectThread.setDaemon(true);
155
objectThread.start();
156
assertion.keepLink(new UnusedThreadKiller(objectThread.getId())); // UnusedThreadKiller
157
break;
158
case STATIC_FIELD_OF_ROOT_CLASS:
159
RefHolder holder4 = new NullClassloaderHolder(random.nextLong());
160
Object keep = holder4.hold(referenceToKeep);
161
if (keep != null) {
162
assertion.keepLink(keep);
163
}
164
break;
165
case JNI_GLOBAL_REF:
166
JNIGlobalRefHolder holder5 = new JNIGlobalRefHolder();
167
assertion.keepLink(holder5.hold(referenceToKeep));
168
break;
169
case JNI_LOCAL_REF:
170
JNILocalRefHolder holder6 = new JNILocalRefHolder();
171
assertion.keepLink(holder6.hold(referenceToKeep));
172
break;
173
}
174
175
Collection<Assertion> returnValue = new LinkedList<>();
176
returnValue.add(assertion);
177
return returnValue;
178
}
179
180
/**
181
* Load class that's supposed to be unloaded. Method returns collection of assertions to check it will be unloaded.
182
* @param className_
183
* @return
184
*/
185
public Collection<Assertion> loadClassThatGonnaDie(String className_) {
186
Collection<Assertion> returnValue = new LinkedList<>();
187
// if generating the bytecodes using ASM then prepend the package name to
188
// the classname so that, when created as a hidden class, it will be in the
189
// same package as its lookup class.
190
Bytecode kit = bf.createBytecode(prepend_package ?
191
"gc.g1.unloading.loading." + className_ : className_);
192
String className = kit.getClassName();
193
byte[] bytecode = kit.getBytecode();
194
Class<?> clazz = loadClass(className, bytecode);
195
FinalizableClassloader cl = null;
196
if (clazz.getClassLoader() instanceof FinalizableClassloader) {
197
cl = (FinalizableClassloader) clazz.getClassLoader();
198
}
199
Object object = instantiateObject(clazz);
200
Object referenceToKeep = configuration.getWhatToKeep().decideUponRefToKeep(clazz, clazz.getClassLoader(), object);
201
202
redefineIfNeeded(bytecode, clazz);
203
204
warmUpClassIfNeeded(object);
205
Assertion assertion;
206
assertion = new ClassAssertion(className, false);
207
switch (configuration.getReleaseRefMode()) {
208
case WEAK:
209
assertion.keepLink(new WeakReference<Object>(referenceToKeep));
210
break;
211
case PHANTOM:
212
final ReferenceQueue queue = new ReferenceQueue<Object>();
213
assertion.keepLink(new PhantomReference<Object>(referenceToKeep, queue));
214
new Thread(new ReferenceCleaningThread(executionController, queue)).start();
215
break;
216
}
217
returnValue.add(assertion);
218
219
if (cl != null) {
220
// Check that classloader will be finalized
221
FinalizedAssertion finalizedAssertion = new FinalizedAssertion();
222
cl.setFinalizedAssertion(finalizedAssertion);
223
returnValue.add(finalizedAssertion);
224
225
// Check that classloader will be phantomized
226
PhantomizedAssertion phantomizedAssertion = new PhantomizedAssertion();
227
PhantomReference phantomReference = new PhantomReference<Object>(cl, phantomizationServiceThread.getQueue());
228
phantomizationServiceThread.add(phantomReference, phantomizedAssertion);
229
returnValue.add(phantomizedAssertion);
230
}
231
return returnValue;
232
}
233
234
private void redefineIfNeeded(byte[] bytecode, Class<?> clazz) {
235
if (configuration.isRedefineClasses()) {
236
BytecodePatcher.patch(bytecode);
237
makeRedefinition(NATIVE_VERBOSITY, clazz, bytecode);
238
239
// This will call class's method
240
instantiateObject(clazz);
241
}
242
}
243
244
private Class<?> loadClass(String className, byte[] bytecode) {
245
try {
246
switch (configuration.getClassloadingMethod()) {
247
case PLAIN:
248
DoItYourselfClassLoader loader1 = new DoItYourselfClassLoader();
249
return loader1.defineClass(className, bytecode);
250
case REFLECTION:
251
return Class.forName(className, true, new ReflectionClassloader(bytecode, className));
252
case JNI:
253
return JNIClassloader.loadThroughJNI(className, bytecode);
254
case HIDDEN_CLASSLOADER:
255
Lookup lookup = MethodHandles.lookup();
256
return lookup.defineHiddenClass(bytecode, true).lookupClass();
257
}
258
return null;
259
} catch (ClassNotFoundException | IllegalAccessException e) {
260
throw new RuntimeException("Test bug!", e);
261
}
262
}
263
264
private Object instantiateObject(Class<?> clazz) {
265
try {
266
Object object = clazz.newInstance();
267
268
// Call method just for fun
269
for (Method m : clazz.getMethods()) {
270
if (m.getName().equals("main")) {
271
m.invoke(object);
272
}
273
}
274
return object;
275
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
276
throw new RuntimeException("Test bug!", e);
277
}
278
}
279
280
private void warmUpClassIfNeeded(Object object) {
281
if (configuration.getCompilationLevel() < 1 || configuration.getCompilationNumber() == 0) {
282
return;
283
}
284
Method m = null;
285
for (Method method : object.getClass().getMethods()) {
286
if (method.getName().equalsIgnoreCase("methodForCompilation")) {
287
m = method;
288
}
289
}
290
WhiteBox wb = WhiteBox.getWhiteBox();
291
if (!wb.isMethodCompilable(m)) {
292
throw new RuntimeException("Test bug! Method occured to be not compilable. Requires investigation.");
293
}
294
295
for (int i = configuration.getCompilationNumber(); i >= 0 && executionController.continueExecution(); i--) {
296
if (!wb.isMethodCompilable(m, configuration.getCompilationLevel())) {
297
continue;
298
}
299
if (!wb.enqueueMethodForCompilation(m, configuration.getCompilationLevel())) {
300
throw new RuntimeException("Method could not be enqueued for compilation at level " + configuration.getCompilationLevel());
301
}
302
while (!wb.isMethodCompiled(m) && executionController.continueExecution()) {
303
sleep(50);
304
try {
305
m.invoke(object, new Object());
306
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
307
throw new RuntimeException("Something went wrong while compilation", e);
308
}
309
}
310
if (i > 0) {
311
wb.deoptimizeMethod(m);
312
}
313
}
314
}
315
316
native static int makeRedefinition0(int verbose, Class<?> redefClass, byte[] classBytes);
317
318
private static void makeRedefinition(int verbose, Class<?> redefClass, byte[] classBytes) {
319
new LibLoader().hashCode();
320
if (makeRedefinition0(verbose, redefClass, classBytes) != 0) {
321
throw new RuntimeException("Test bug: native method \"makeRedefinition\" return nonzero");
322
}
323
}
324
325
private static void sleep(long millis) {
326
try {
327
Thread.sleep(millis);
328
} catch (InterruptedException e) {
329
throw new RuntimeException("Got InterruptedException while sleeping.", e);
330
}
331
}
332
333
}
334
335
class ReferenceCleaningThread extends ExecutionTask {
336
337
private ReferenceQueue<?> queue;
338
339
public ReferenceCleaningThread(ExecutionController executionController, ReferenceQueue<?> queue) {
340
super(executionController);
341
this.queue = queue;
342
}
343
344
@Override
345
protected void task() throws Exception {
346
Reference<?> ref = queue.remove(100);
347
if (ref != null) {
348
ref.clear();
349
return;
350
}
351
}
352
353
}
354
355