论动态修改Java枚举类字段的可行方案

在分析Forge的代码时,发现了可以通过反射特殊类达到修改Java枚举类的方案。

EnumHelper

但很不幸,在Java 9之后,sun.reflect.*不复存在,这种方案需要更改为对jdk.internal.reflect.*进行反射,并且解除Modules安全限制。

同时在较新的JDK中,AccessibleObject类中override字段会被隐藏掉,隐藏功能由jdk.internal.reflect.Reflection实现。

也就意味着,如果我们继续使用旧版本,为了做到修改,我们需要引入一个Agent干掉Reflection的隐藏功能,增加一个–add-exports命令来关掉非法反射内部类的报错。

那么,讨论一个不依赖非法反射,做到动态修改枚举类,也不会因JDK版本更新导致失效的方案,是时候提上日程了。

在此选用的是类修改的方法,可以增加一个Agent或者在ClassLoader处增加一个类转换器就可以继续使用。

首先分析枚举类的格式。

1
2
3
public enum TestEnum {
A, B, C, D
}

等效于以下代码(无法编译)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final class TestEnum extends Enum<TestEnum> {
public static final TestEnum A = new TestEnum("A", 0);
public static final TestEnum B = new TestEnum("B", 1);
public static final TestEnum C = new TestEnum("C", 2);
public static final TestEnum D = new TestEnum("D", 3);
private static final TestEnum[] $VALUES;

public static TestEnum[] values() {
return (TestEnum[])$VALUES.clone();
}

public static TestEnum valueOf(String name) {
return (TestEnum)Enum.valueOf(TestEnum.class, name);
}

private TestEnum(String name, int ordinal) {
super(name, ordinal);
}

static {
$VALUES = new TestEnum[]{A, B, C, D};
}
}

问题1,Java的数组不是变长数组,我们需要让其支持变长,或者可以替换数组。

问题2,需要单独实现values()与valueOf(String name)方法。

问题3,需要为构造方法提供一个稳定的生成与入参方式。

问题4,当枚举名比较奇怪时,保证修改后仍为正常。

问题5,自动计算ordinal,而不是让用户去添加。

问题6,name冲突时,达到报错效果。

问题7,支持移除操作。

问题8,削弱在修改类时过度依赖原始类的问题。

问题9,线程安全问题。

问题10, 额外入参支持。

对于问题1和问题2,可以使用其他容器做替代。

为了实现valueOf(String name),字段选用LinkedHashMap。

1
private static final LinkedHashMap<String, TestEnum> $VALUES;

values方法和valueOf方法替换为。

1
2
3
4
5
6
7
8
9
10
11
12
public static TestEnum[] values() {
Collection<TestEnum> enumCollection = $VALUES.values();
Object[] arrays = enumCollection.toArray();
TestEnum[] enums = Arrays.copyOf(o, o.length, TestEnum[].class);
return enums;
}
public static TestEnum valueOf(String name) {
if(!$VALUES.containsKey(Objects.requireNonNull(name, "Name is null")) {
throw new IllegalArgumentException("No enum constant TestEnum." + name);
}
return $VALUES.get(name);
}

然后,对<clinit>方法进行改造,以下是Javac生成的等效代码。

1
2
3
4
5
6
7
static void <clinit>(){
A = new TestEnum("A", 0);
B = new TestEnum("B", 1);
C = new TestEnum("C", 2);
D = new TestEnum("D", 3);
$VALUES = new TestEnum[]{A, B, C, D};
}

我们需要阻止$VALUES的写入,并将数据源以HashMap的格式复制到$VALUES。

拦截对TestEnum.$VALUES的putstatic命令,改成使用新格式的。

1
2
3
4
5
6
7
8
9
10
11
static void <clinit>(){
A = new TestEnum("A", 0);
B = new TestEnum("B", 1);
C = new TestEnum("C", 2);
D = new TestEnum("D", 3);
TestEnum[] value = new TestEnum[]{A, B, C, D};
$VALUE = new HashMap<String, TestEnum>();
for(TestEnum enums:value){
$VALUE.put(enums.name(), enums);
}
}

在此,我们支持了枚举类动态增加枚举的前置工作。
现在,增加一个静态的new方法,绕开反射不能调用枚举类构造函数的限制。

1
2
3
4
5
public synchronized TestEnum addEnum(String name, int ordinal) {
TestEnum enumObject = new TestEnum(name, ordinal);
$VALUES.put(name, enumObject);
return enumObject;
}

我们可以测试能否动态增加枚举了。

1 TestEnum

1
2
3
public enum TestEnum {
A, B, C, D
}

2 Main

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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
import org.objectweb.asm.*;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;

import java.util.*;

public class Main {
@SuppressWarnings("all")
public static void main(String[] args) throws Exception {
ClassReader cr = new ClassReader(ClassLoader.getSystemResourceAsStream("TestEnum.class"));
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
//拦截Field与Method
cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
// 可以不去除Enum标识,但Idea反编译时会隐藏values和valueOf方法
super.visit(version, access - Opcodes.ACC_ENUM, name, signature, superName, interfaces);
}

@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
if (name.equals("$VALUES")) {
return null;
}
return super.visitField(access, name, descriptor, signature, value);
}

@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
if (name.equals("values")) {
return null;
}
if (name.equals("valueOf")) {
return null;
}
if (name.equals("<clinit>")) {
// 拦截以下代码中赋值给$VALUES的指令
// A = new TestEnum("A", 0);
// B = new TestEnum("B", 1);
// C = new TestEnum("C", 2);
// D = new TestEnum("D", 3);
// $VALUES = new TestEnum[]{A, B, C, D};
// 替换为
// TestEnum[] VALUES = new TestEnum[]{A, B, C, D}
// $VALUES = new LinkedHashMap<>();
// for(TestEnum testEnum:VALUES) {
// $VALUES.put(testEnum.name(), testEnum);
// }
return new GeneratorAdapter(Opcodes.ASM5, super.visitMethod(access, name, descriptor, signature, exceptions), access, name, descriptor) {
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
if (opcode == Opcodes.PUTSTATIC && name.equals("$VALUES") && descriptor.startsWith("[")) {
int local = newLocal(Type.getType(descriptor));
int i = newLocal(Type.INT_TYPE);
int max = newLocal(Type.INT_TYPE);
Label labelStart = newLabel();
Label labelEnd = newLabel();
dup();
storeLocal(local);
push(0);
storeLocal(i);
arrayLength();
storeLocal(max);
newInstance(Type.getType(LinkedHashMap.class));
dup();
invokeConstructor(Type.getType(LinkedHashMap.class), new Method("<init>", "()V"));
putStatic(Type.getType(TestEnum.class), "$VALUES", Type.getType(LinkedHashMap.class));
visitLabel(labelStart);
loadLocal(i);
loadLocal(max);
ifICmp(Opcodes.IFGE, labelEnd);
getStatic(Type.getType(TestEnum.class), "$VALUES", Type.getType(LinkedHashMap.class));
loadLocal(local);
loadLocal(i);
arrayLoad(Type.getType(TestEnum.class));
dup();
invokeVirtual(Type.getType(TestEnum.class), new Method("name", "()Ljava/lang/String;"));
swap();
invokeVirtual(Type.getType(LinkedHashMap.class), new Method("put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"));
pop();
iinc(i, 1);
goTo(labelStart);
visitLabel(labelEnd);
} else {
super.visitFieldInsn(opcode, owner, name, descriptor);
}
}
};
}
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
}, ClassReader.EXPAND_FRAMES);
//注入新Field
//等效代码
// private static final LinkedHashMap<String, TestEnum> $VALUES;
cw.visitField(Opcodes.ACC_FINAL | Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE, "$VALUES", "Ljava/util/LinkedHashMap;", null, null).visitEnd();
//新的values方法
//等效代码
// public static TestEnum[] values() {
// Object[] valueArray = $VALUES.values().toArray();
// return Arrays.copyOf(valueArray, valueArray.length, TestEnum[].class);
// }
//
MethodVisitor methodVisitor = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "values", "()[LTestEnum;", null, null);
GeneratorAdapter ga = new GeneratorAdapter(methodVisitor, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "values", "()[LTestEnum;");
ga.getStatic(Type.getType(TestEnum.class), "$VALUES", Type.getType(LinkedHashMap.class));
ga.invokeVirtual(Type.getType(LinkedHashMap.class), new Method("values", "()Ljava/util/Collection;"));
ga.invokeInterface(Type.getType(Collection.class), new Method("toArray", "()[Ljava/lang/Object;"));
ga.dup();
ga.arrayLength();
ga.push(Type.getType(TestEnum[].class));
ga.invokeStatic(Type.getType(Arrays.class), new Method("copyOf", "([Ljava/lang/Object;ILjava/lang/Class;)[Ljava/lang/Object;"));
ga.checkCast(Type.getType(TestEnum[].class));
ga.returnValue();
ga.endMethod();
//新的valueOf方法
//等效代码
// public static TestEnum valueOf(String name) {
// if ($VALUES.containsKey(Objects.requireNonNull(name, "Name is null"))) {
// return $VALUES.get(name);
// } else {
// throw new IllegalArgumentException("No enum constant TestEnum." + name);
// }
// }
//
methodVisitor = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "valueOf", "(Ljava/lang/String;)LTestEnum;", null, null);
ga = new GeneratorAdapter(methodVisitor, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "valueOf", "(Ljava/lang/String;)LTestEnum;");
ga.getStatic(Type.getType(TestEnum.class), "$VALUES", Type.getType(LinkedHashMap.class));
ga.loadArg(0);
ga.push("Name is null");
ga.invokeStatic(Type.getType(Objects.class), new Method("requireNonNull", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;"));
ga.invokeVirtual(Type.getType(LinkedHashMap.class), new Method("containsKey", "(Ljava/lang/Object;)Z"));
Label label = ga.newLabel();
ga.ifZCmp(Opcodes.IFEQ, label);
ga.getStatic(Type.getType(TestEnum.class), "$VALUES", Type.getType(LinkedHashMap.class));
ga.loadArg(0);
ga.invokeVirtual(Type.getType(LinkedHashMap.class), new Method("get", "(Ljava/lang/Object;)Ljava/lang/Object;"));
ga.checkCast(Type.getType(TestEnum.class));
ga.returnValue();
ga.visitLabel(label);
ga.newInstance(Type.getType(IllegalArgumentException.class));
ga.dup();
ga.newInstance(Type.getType(StringBuilder.class));
ga.dup();
ga.push("No enum constant " + TestEnum.class.getName() + ".");
ga.invokeConstructor(Type.getType(StringBuilder.class), new Method("<init>", "(Ljava/lang/String;)V"));
ga.loadArg(0);
ga.invokeVirtual(Type.getType(StringBuilder.class), new Method("append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"));
ga.invokeVirtual(Type.getType(StringBuilder.class), new Method("toString", "()Ljava/lang/String;"));
ga.invokeConstructor(Type.getType(IllegalArgumentException.class), new Method("<init>", "(Ljava/lang/String;)V"));
ga.throwException();
ga.endMethod();
//新的addEnum方法
//等效代码
// public static void addEnum(String name, String ordinal){
// TestEnum target = new TestEnum(name, ordinal);
// $VALUES.put(name, target);
// return target;
// }
methodVisitor = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNCHRONIZED, "addEnum", "(Ljava/lang/String;I)LTestEnum;", null, null);
ga = new GeneratorAdapter(methodVisitor, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNCHRONIZED, "addEnum", "(Ljava/lang/String;I)LTestEnum;");
ga.newInstance(Type.getType(TestEnum.class));
ga.dup();
ga.loadArg(0);
ga.loadArg(1);
ga.invokeConstructor(Type.getType(TestEnum.class), new Method("<init>", "(Ljava/lang/String;I)V"));
int local = ga.newLocal(Type.getType(TestEnum.class));
ga.storeLocal(local);
ga.getStatic(Type.getType(TestEnum.class), "$VALUES", Type.getType(LinkedHashMap.class));
ga.loadArg(0);
ga.loadLocal(local);
ga.invokeVirtual(Type.getType(LinkedHashMap.class), new Method("put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"));
ga.pop();
ga.loadLocal(local);
ga.returnValue();
ga.endMethod();
//定义类
Class<? extends Enum> testEnums = (Class<? extends Enum>) new ClassLoader().pDefineClass("TestEnum", cw.toByteArray());
//获取所有的值
//等同于TestEnum.value()
System.out.println(Arrays.toString((Object[]) testEnums.getMethod("values").invoke(null)));
//增加一条枚举
//等同于TestEnum.addEnum("AA", 99);
System.out.println(testEnums.getMethod("addEnum", String.class, int.class).invoke(null, "AA", 99));
//再次获取所有的值
System.out.println(Arrays.toString((Object[]) testEnums.getMethod("values").invoke(null)));
}

public static final class ClassLoader extends java.lang.ClassLoader {
private ClassLoader() {
super(ClassLoader.class.getClassLoader());
}

public Class<?> pDefineClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length, null);
}
}
}

1
2
3
4
$ java -classpath asm-commons-8.0.1.jar;asm-8.0.1.jar;asm-tree-8.0.1.jar;asm-analysis-8.0.1.jar;. Main
[A, B, C, D]
AA
[A, B, C, D, AA]

成功了,但我们还有很多任务要做。

Java Language Specification中,定义了隐式方法values和valueOf,我们可以根据此处的实现数据来获取$VALUE的真实名称。

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
/**
* 获取枚举类的常量列表字段名
*
* @param cr 类读取器
* @return 常量列表字段名
*/
public static String visitValuesName(ClassReader cr) {
// 通过获取values()方法体获取Enum存储常量池名称
AtomicReference<String> valuesName = new AtomicReference<>();
String className = cr.getClassName();
cr.accept(new ClassVisitor(Opcodes.ASM5, null) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
if (name.equals("values") && ("()[L" + className + ";").equals(descriptor)) {
return new MethodVisitor(Opcodes.ASM5, null) {
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
if (opcode == Opcodes.GETSTATIC) {
valuesName.set(name);
}
}
};
}
return null;
}
}, 0);
return valuesName.get();
}

同时,对原来的处理方案进行迭代,同时解决其他所有问题。

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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
public static byte[] genEnumModifyClass(byte[] bytes) {
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
String className = cr.getClassName();
String valuesName = visitValuesName(cr);
cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
// 可以不去除Enum标识,但Idea反编译时会隐藏values和valueOf方法
super.visit(version, access - Opcodes.ACC_ENUM, name, signature, superName, interfaces);
}
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
// 拦截原$VALUES字段
if (name.equals(valuesName)) {
return null;
}
return super.visitField(access, name, descriptor, signature, value);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
// 拦截values和valueOf方法
if (name.equals("values")) {
return null;
}
if (name.equals("valueOf")) {
return null;
}
//为static{}注入新指令
if (name.equals("<clinit>")) {
// 拦截以下代码中赋值给$VALUES的指令
// A = new TestEnum("A", 0);
// B = new TestEnum("B", 1);
// C = new TestEnum("C", 2);
// D = new TestEnum("D", 3);
// $VALUES = new TestEnum[]{A, B, C, D};
// 替换为
// $VALUES = new LinkedHashMap<>();
// visitMap(new TestEnum[]{A, B, C, D});
return new MethodVisitor(Opcodes.ASM5, super.visitMethod(access, name, descriptor, signature, exceptions)) {
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
if (opcode == Opcodes.PUTSTATIC && name.equals(valuesName) && descriptor.startsWith("[")) {
mv.visitTypeInsn(Opcodes.NEW, "java/util/LinkedHashMap");
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/util/LinkedHashMap", "<init>", "()V", false);
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/util/Map");
//mv.visitMethodInsn(Opcodes.INVOKESTATIC,"java/util/Collections","synchronizedMap","(Ljava/util/Map;)Ljava/util/Map;",false);
mv.visitFieldInsn(Opcodes.PUTSTATIC, className, valuesName, "Ljava/util/Map;");
mv.visitTypeInsn(Opcodes.NEW, "java/util/concurrent/atomic/AtomicInteger");
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/util/concurrent/atomic/AtomicInteger", "<init>", "()V", false);
mv.visitFieldInsn(Opcodes.PUTSTATIC, className, valuesName + "$ordinal", "Ljava/util/concurrent/atomic/AtomicInteger;");
mv.visitMethodInsn(Opcodes.INVOKESTATIC, className, "visitMap", "([L" + className + ";)V", false);
} else {
super.visitFieldInsn(opcode, owner, name, descriptor);
}
}
@Override
public void visitEnd() {
visitMaxs(0, 0);
super.visitEnd();
}
};
}
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
}, ClassReader.EXPAND_FRAMES);
//注入新Field
//等效代码
// private static final Map<String, TestEnum> $VALUES;
// private static final AtomicInteger $VALUES$ordinal;
cw.visitField(Opcodes.ACC_FINAL | Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE, valuesName, "Ljava/util/Map;", null, null).visitEnd();
cw.visitField(Opcodes.ACC_FINAL | Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE, valuesName + "$ordinal", "Ljava/util/concurrent/atomic/AtomicInteger;", null, null).visitEnd();
//新的values方法
//等效代码
// public static TestEnum[] values() {
// Object[] valueArray = $VALUES.values().toArray();
// return Arrays.copyOf(valueArray, valueArray.length, TestEnum[].class);
// }
//
MethodVisitor methodVisitor = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "values", "()[L" + className + ";", null, null);
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, className, valuesName, "Ljava/util/Map;");
methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "values", "()Ljava/util/Collection;", true);
methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Collection", "toArray", "()[Ljava/lang/Object;", true);
methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitInsn(Opcodes.ARRAYLENGTH);
methodVisitor.visitLdcInsn(Type.getType("[L" + className + ";"));
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "copyOf", "([Ljava/lang/Object;ILjava/lang/Class;)[Ljava/lang/Object;", false);
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, "[L" + className + ";");
methodVisitor.visitInsn(Opcodes.ARETURN);
methodVisitor.visitMaxs(0, 0);
methodVisitor.visitEnd();
//新的valueOf方法
//等效代码
// public static TestEnum valueOf(String name) {
// if ($VALUES.containsKey(Objects.requireNonNull(name, "Name is null"))) {
// return $VALUES.get(name);
// } else {
// throw new IllegalArgumentException("No enum constant TestEnum." + name);
// }
// }
//
methodVisitor = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "valueOf", "(Ljava/lang/String;)L" + className + ";", null, null);
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, className, valuesName, "Ljava/util/Map;");
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitLdcInsn("Name is null");
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Objects", "requireNonNull", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;", false);
methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "containsKey", "(Ljava/lang/Object;)Z", true);
Label label = new Label();
methodVisitor.visitJumpInsn(Opcodes.IFEQ, label);
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, className, valuesName, "Ljava/util/Map;");
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, className);
methodVisitor.visitInsn(Opcodes.ARETURN);
methodVisitor.visitLabel(label);
methodVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalArgumentException");
methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitLdcInsn("No enum constant " + className.replace("/", ".") + ".");
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", false);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "(Ljava/lang/String;)V", false);
methodVisitor.visitInsn(Opcodes.ATHROW);
methodVisitor.visitMaxs(0, 0);
methodVisitor.visitEnd();
//新的addEnum方法
//等效代码
// public static synchronized void addEnum(String name) {
// if($VALUES.containsKey(name)){
// throw new IllegalArgumentException();
// }
// TestEnum target = new TestEnum(name, $VALUES$ordinal.incrementAndGet());
// $VALUES.put(name, target);
// return target;
// }
methodVisitor = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNCHRONIZED, "addEnum", "(Ljava/lang/String;)L" + className + ";", null, null);
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, className, valuesName, "Ljava/util/Map;");
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "containsKey", "(Ljava/lang/Object;)Z", true);
label = new Label();
methodVisitor.visitJumpInsn(Opcodes.IFEQ, label);
methodVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalArgumentException");
methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "()V", false);
methodVisitor.visitInsn(Opcodes.ATHROW);
methodVisitor.visitLabel(label);
methodVisitor.visitTypeInsn(Opcodes.NEW, className);
methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, className, valuesName + "$ordinal", "Ljava/util/concurrent/atomic/AtomicInteger;");
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/concurrent/atomic/AtomicInteger", "incrementAndGet", "()I", false);
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, className, "<init>", "(Ljava/lang/String;I)V", false);
int local = 4;
methodVisitor.visitVarInsn(Opcodes.ASTORE, local);
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, className, valuesName, "Ljava/util/Map;");
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitVarInsn(Opcodes.ALOAD, local);
methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true);
methodVisitor.visitInsn(Opcodes.POP);
methodVisitor.visitVarInsn(Opcodes.ALOAD, local);
methodVisitor.visitInsn(Opcodes.ARETURN);
methodVisitor.visitMaxs(0, 0);
methodVisitor.visitEnd();
//生成visitMap方法
//等效代码
//private static void visitMap(TestEnum[] enums) {
// for(TestEnum test:enums) {
// $VALUES.add(test.name(), test);
// $VALUES$ordinal.set(Math.max($VALUES$ordinal.get()));
// }
// }
//
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, "visitMap", "([L" + className + ";)V", null, null);
int i = 1;
int max = 2;
Label labelStart = new Label();
Label labelEnd = new Label();
mv.visitInsn(Opcodes.ICONST_0);
mv.visitVarInsn(Opcodes.ISTORE, i);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitInsn(Opcodes.ARRAYLENGTH);
mv.visitVarInsn(Opcodes.ISTORE, max);
mv.visitLabel(labelStart);
mv.visitVarInsn(Opcodes.ILOAD, i);
mv.visitVarInsn(Opcodes.ILOAD, max);
mv.visitJumpInsn(Opcodes.IF_ICMPGE, labelEnd);
mv.visitFieldInsn(Opcodes.GETSTATIC, className, valuesName, "Ljava/util/Map;");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ILOAD, i);
mv.visitInsn(Opcodes.AALOAD);
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, "name", "()Ljava/lang/String;", false);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true);
mv.visitInsn(Opcodes.POP);
mv.visitFieldInsn(Opcodes.GETSTATIC, className, valuesName + "$ordinal", "Ljava/util/concurrent/atomic/AtomicInteger;");
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/concurrent/atomic/AtomicInteger", "get", "()I", false);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ILOAD, i);
mv.visitInsn(Opcodes.AALOAD);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, "ordinal", "()I", false);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Math", "max", "(II)I", false);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/concurrent/atomic/AtomicInteger", "set", "(I)V", false);
mv.visitIincInsn(i, 1);
mv.visitJumpInsn(Opcodes.GOTO, labelStart);
mv.visitLabel(labelEnd);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
mv = cw.visitMethod(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, "remove", "(L" + className + ";)V", null, null);
mv.visitFieldInsn(Opcodes.GETSTATIC, className, valuesName, "Ljava/util/Map;");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, "name", "()Ljava/lang/String;", false);
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "remove", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
mv.visitInsn(Opcodes.POP);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
mv = cw.visitMethod(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, "remove", "(Ljava/lang/String;)V", null, null);
mv.visitFieldInsn(Opcodes.GETSTATIC, className, valuesName, "Ljava/util/Map;");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "remove", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
mv.visitInsn(Opcodes.POP);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
return cw.toByteArray();
}