/
LaunchClassLoaderUtil.java
184 lines (165 loc) · 6.74 KB
/
LaunchClassLoaderUtil.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/*
* This file is part of Mixin, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.spongepowered.asm.service.mojang;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import net.minecraft.launchwrapper.LaunchClassLoader;
/**
* Utility class for reflecting into {@link LaunchClassLoader}. We <b>do not
* write</b> anything of the classloader fields, but we need to be able to read
* them to perform some validation tasks, and insert entries for mixin "classes"
* into the invalid classes set.
*/
final class LaunchClassLoaderUtil {
private static final String CACHED_CLASSES_FIELD = "cachedClasses";
private static final String INVALID_CLASSES_FIELD = "invalidClasses";
private static final String CLASS_LOADER_EXCEPTIONS_FIELD = "classLoaderExceptions";
private static final String TRANSFORMER_EXCEPTIONS_FIELD = "transformerExceptions";
/**
* ClassLoader for this util
*/
private final LaunchClassLoader classLoader;
// Reflected fields
private final Map<String, Class<?>> cachedClasses;
private final Set<String> invalidClasses;
private final Set<String> classLoaderExceptions;
private final Set<String> transformerExceptions;
/**
* Singleton, use factory to get an instance
*
* @param classLoader class loader
*/
LaunchClassLoaderUtil(LaunchClassLoader classLoader) {
this.classLoader = classLoader;
this.cachedClasses = LaunchClassLoaderUtil.<Map<String, Class<?>>>getField(classLoader, LaunchClassLoaderUtil.CACHED_CLASSES_FIELD);
this.invalidClasses = LaunchClassLoaderUtil.<Set<String>>getField(classLoader, LaunchClassLoaderUtil.INVALID_CLASSES_FIELD);
this.classLoaderExceptions = LaunchClassLoaderUtil.<Set<String>>getField(classLoader, LaunchClassLoaderUtil.CLASS_LOADER_EXCEPTIONS_FIELD);
this.transformerExceptions = LaunchClassLoaderUtil.<Set<String>>getField(classLoader, LaunchClassLoaderUtil.TRANSFORMER_EXCEPTIONS_FIELD);
}
/**
* Get the classloader
*/
LaunchClassLoader getClassLoader() {
return this.classLoader;
}
/**
* Get whether a class name exists in the cache (indicating it was loaded
* via the inner loader
*
* @param name class name
* @return true if the class name exists in the cache
*/
boolean isClassLoaded(String name) {
return this.cachedClasses.containsKey(name);
}
/**
* Get whether the specified name or transformedName exist in either of the
* exclusion lists
*
* @param name class name
* @param transformedName transformed class name
* @return true if either exclusion list contains either of the names
*/
boolean isClassExcluded(String name, String transformedName) {
return this.isClassClassLoaderExcluded(name, transformedName) || this.isClassTransformerExcluded(name, transformedName);
}
/**
* Get whether the specified name or transformedName exist in the
* classloader exclusion list
*
* @param name class name
* @param transformedName transformed class name
* @return true if the classloader exclusion list contains either of the
* names
*/
boolean isClassClassLoaderExcluded(String name, String transformedName) {
for (final String exception : this.getClassLoaderExceptions()) {
if ((transformedName != null && transformedName.startsWith(exception)) || name.startsWith(exception)) {
return true;
}
}
return false;
}
/**
* Get whether the specified name or transformedName exist in the
* transformer exclusion list
*
* @param name class name
* @param transformedName transformed class name
* @return true if the transformer exclusion list contains either of the
* names
*/
boolean isClassTransformerExcluded(String name, String transformedName) {
for (final String exception : this.getTransformerExceptions()) {
if ((transformedName != null && transformedName.startsWith(exception)) || name.startsWith(exception)) {
return true;
}
}
return false;
}
/**
* Stuff a class name directly into the invalidClasses set, this prevents
* the loader from classloading the named class. This is used by the mixin
* processor to prevent classloading of mixin classes
*
* @param name class name
*/
void registerInvalidClass(String name) {
if (this.invalidClasses != null) {
this.invalidClasses.add(name);
}
}
/**
* Get the classloader exclusions from the target classloader
*/
Set<String> getClassLoaderExceptions() {
if (this.classLoaderExceptions != null) {
return this.classLoaderExceptions;
}
return Collections.<String>emptySet();
}
/**
* Get the transformer exclusions from the target classloader
*/
Set<String> getTransformerExceptions() {
if (this.transformerExceptions != null) {
return this.transformerExceptions;
}
return Collections.<String>emptySet();
}
@SuppressWarnings("unchecked")
private static <T> T getField(LaunchClassLoader classLoader, String fieldName) {
try {
Field field = LaunchClassLoader.class.getDeclaredField(fieldName);
field.setAccessible(true);
return (T)field.get(classLoader);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}