ASM 重复方法
ASM duplicate methods
我正在尝试删除所有 System#exit 调用,它似乎可以正常工作,但我最终遇到了重复的方法。
我的代码如下所示:
JarInputStream jis = new JarInputStream(new FileInputStream(new File(input)));
JarOutputStream jos = new JarOutputStream(new FileOutputStream(new File(output)));
byte[] buffer = new byte[4096];
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int size;
while ((size = jis.read(buffer)) != -1) {
bos.write(buffer, 0, size);
}
bos.close();
jos.putNextEntry(new JarEntry(entry.getName()));
if (entry.getName().endsWith(".class")) {
ClassReader cr = new ClassReader(bos.toByteArray());
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassTransformer cm = new ClassTransformer(cw);
ClassNode cn = new ClassNode();
cr.accept(cn, 0);
System.out.println("Analyzing bytecode in class " + cn.name);
List<MethodNode> methods = cn.methods;
for (MethodNode method : methods) {
System.out.println("Analyzing bytecode in method " + method.name);
String[] exceptions = (String[]) method.exceptions.toArray(new String[method.exceptions.size()]);
cm.visitMethod(method.access, method.name, method.desc, method.signature, exceptions);
}
cr.accept(cm, ClassReader.EXPAND_FRAMES);
jos.write(cw.toByteArray());
} else {
jos.write(bos.toByteArray());
}
}
jos.close();
jis.close();
ClassTransformer 和 MethodTransformer 都是自定义的 类。如果你想让我也提供它们,请告诉我。
您应该熟悉 Visitor pattern。
声明
cr.accept(cm, ClassReader.EXPAND_FRAMES);
已经完成了全部工作。 ClassReader
将在指定的 ClassTransformer
上调用所有 visit…
方法,这将在遵循此 API 的标准编程模型时对 ClassWriter
进行所有必要的委托.
换句话说,整个区块
ClassNode cn = new ClassNode();
cr.accept(cn, 0);
System.out.println("Analyzing bytecode in class " + cn.name);
List<MethodNode> methods = cn.methods;
for (MethodNode method : methods) {
System.out.println("Analyzing bytecode in method " + method.name);
String[] exceptions = (String[]) method.exceptions.toArray(new String[method.exceptions.size()]);
cm.visitMethod(method.access, method.name, method.desc, method.signature, exceptions);
}
已过时,因为它循环遍历所有方法并为每个方法调用 visitMethod
除了已经完成整个工作的语句之外,所以每个方法两次都不足为奇。
所以只需删除该代码块。
嗯,当然,你可以保留打印语句...
顺便说一句,您可以通过简单地将 JarInputStream
传递给 ClassReader(InputStream)
构造函数来进一步简化您的代码,而无需先复制到 ByteArrayOutputStream
。
您的代码的清理版本如下所示:
try(JarInputStream jis = new JarInputStream(new FileInputStream(input));
JarOutputStream jos = new JarOutputStream(new FileOutputStream(output)) ) {
JarEntry entry;
while((entry = jis.getNextJarEntry()) != null) {
jos.putNextEntry(new JarEntry(entry.getName()));
if (entry.getName().endsWith(".class")) {
ClassReader cr = new ClassReader(jis);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
ClassTransformer cm = new ClassTransformer(cw);
System.out.println("Analyzing bytecode in class " + cr.getClassName());
cr.accept(cm, ClassReader.EXPAND_FRAMES);
jos.write(cw.toByteArray());
} else {
jis.transferTo(jos); // Java 9
/* before Java 9:
byte[] buffer = new byte[8192];
for(int read; (read = jis.read(buffer, 0, buffer.length)) >= 0; )
jos.write(buffer, 0, read);
*/
}
}
}
请注意,我还将 ClassReader
传递给了 ClassWriter
的构造函数。对于像这样的用例,只进行了很小的更改,这允许优化代码生成。
我正在尝试删除所有 System#exit 调用,它似乎可以正常工作,但我最终遇到了重复的方法。
我的代码如下所示:
JarInputStream jis = new JarInputStream(new FileInputStream(new File(input)));
JarOutputStream jos = new JarOutputStream(new FileOutputStream(new File(output)));
byte[] buffer = new byte[4096];
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int size;
while ((size = jis.read(buffer)) != -1) {
bos.write(buffer, 0, size);
}
bos.close();
jos.putNextEntry(new JarEntry(entry.getName()));
if (entry.getName().endsWith(".class")) {
ClassReader cr = new ClassReader(bos.toByteArray());
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassTransformer cm = new ClassTransformer(cw);
ClassNode cn = new ClassNode();
cr.accept(cn, 0);
System.out.println("Analyzing bytecode in class " + cn.name);
List<MethodNode> methods = cn.methods;
for (MethodNode method : methods) {
System.out.println("Analyzing bytecode in method " + method.name);
String[] exceptions = (String[]) method.exceptions.toArray(new String[method.exceptions.size()]);
cm.visitMethod(method.access, method.name, method.desc, method.signature, exceptions);
}
cr.accept(cm, ClassReader.EXPAND_FRAMES);
jos.write(cw.toByteArray());
} else {
jos.write(bos.toByteArray());
}
}
jos.close();
jis.close();
ClassTransformer 和 MethodTransformer 都是自定义的 类。如果你想让我也提供它们,请告诉我。
您应该熟悉 Visitor pattern。
声明
cr.accept(cm, ClassReader.EXPAND_FRAMES);
已经完成了全部工作。 ClassReader
将在指定的 ClassTransformer
上调用所有 visit…
方法,这将在遵循此 API 的标准编程模型时对 ClassWriter
进行所有必要的委托.
换句话说,整个区块
ClassNode cn = new ClassNode();
cr.accept(cn, 0);
System.out.println("Analyzing bytecode in class " + cn.name);
List<MethodNode> methods = cn.methods;
for (MethodNode method : methods) {
System.out.println("Analyzing bytecode in method " + method.name);
String[] exceptions = (String[]) method.exceptions.toArray(new String[method.exceptions.size()]);
cm.visitMethod(method.access, method.name, method.desc, method.signature, exceptions);
}
已过时,因为它循环遍历所有方法并为每个方法调用 visitMethod
除了已经完成整个工作的语句之外,所以每个方法两次都不足为奇。
所以只需删除该代码块。
嗯,当然,你可以保留打印语句...
顺便说一句,您可以通过简单地将 JarInputStream
传递给 ClassReader(InputStream)
构造函数来进一步简化您的代码,而无需先复制到 ByteArrayOutputStream
。
您的代码的清理版本如下所示:
try(JarInputStream jis = new JarInputStream(new FileInputStream(input));
JarOutputStream jos = new JarOutputStream(new FileOutputStream(output)) ) {
JarEntry entry;
while((entry = jis.getNextJarEntry()) != null) {
jos.putNextEntry(new JarEntry(entry.getName()));
if (entry.getName().endsWith(".class")) {
ClassReader cr = new ClassReader(jis);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
ClassTransformer cm = new ClassTransformer(cw);
System.out.println("Analyzing bytecode in class " + cr.getClassName());
cr.accept(cm, ClassReader.EXPAND_FRAMES);
jos.write(cw.toByteArray());
} else {
jis.transferTo(jos); // Java 9
/* before Java 9:
byte[] buffer = new byte[8192];
for(int read; (read = jis.read(buffer, 0, buffer.length)) >= 0; )
jos.write(buffer, 0, read);
*/
}
}
}
请注意,我还将 ClassReader
传递给了 ClassWriter
的构造函数。对于像这样的用例,只进行了很小的更改,这允许优化代码生成。