3.6 执行InjectUnitTest.java的test()方法查看InjectTest.class结果
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.xyaty.asmdemo;
public class InjectTest {
public InjectTest() {
long var1 = System.currentTimeMillis();
long var3 = System.currentTimeMillis();
System.out.println("execute: " + (var3 - var1) + "ms");
}
public static void main(String[] var0) throws InterruptedException {
long var1 = System.currentTimeMillis();
Thread.sleep(1000L);
long var3 = System.currentTimeMillis();
System.out.println("execute: " + (var3 - var1) + "ms");
}
public void methodA() {
long var1 = System.currentTimeMillis();
System.out.println("methodA");
long var3 = System.currentTimeMillis();
System.out.println("execute: " + (var3 - var1) + "ms");
}
}
发现在每个方法中都加入了
long var1 = System.currentTimeMillis();
和
long var3 = System.currentTimeMillis();
System.out.println("execute: " + (var3 - var1) + "ms");
如果仅仅在main()方法才注入代码,就需要引入自定义注解来标记指定的方法
四、引入自定义注解,标记方法才注入代码
4.1 注解类ASMTest.java,并通过javac编译成ASMTest.class(略)
package com.xyaty.asmdemo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* DESC :
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface ASMTest {
}
4.2 在InjectTest.java的main()方法上加上注解@ASMTest,标记此方法,并通过javac生成class
package com.xyaty.asmdemo;
/**
* DESC :
*/
public class InjectTest {
@ASMTest
public static void main(String[] args) throws InterruptedException {
Thread.sleep(1000);
}
public void methodA() {
System.out.println("methodA");
}
}
4.3 在上述MyMethodVisitor的下列方法中加入注解判断
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
System.out.println("visitAnnotation===>methodName="+getName()+", descriptor="+descriptor);
//如果方法的注解名字是@ASMTest,则给此方法注入代码
if ("Lcom/xyaty/asmdemo/ASMTest;".equals(descriptor)) {
isInject = true;
} else {
isInject = false;
}
return super.visitAnnotation(descriptor, visible);
}
/**
* 进入方法插入内容
*/
@Override
protected void onMethodEnter() {
super.onMethodEnter();
if (!isInject) {
return;
}
}
@Override
protected void onMethodExit(int opcode) {
super.onMethodExit(opcode);
if (!isInject) {
return;
}
}
InjectUnitTest.java完整代码如下:
package com.xyaty.asmdemo;
import org.junit.Test;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AdviceAdapter;
import org.objectweb.asm.commons.Method;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* DESC :
*/
public class InjectUnitTest {
/**
* 单元测试方法,右击test()方法,选择run test()方法即可查看结果
*/
@Test
public void test() {
try {
//读取待插桩的class
FileInputStream fis = new FileInputStream(
new File("src/test/java/com/xyaty/asmdemo/InjectTest.class"));
/**
* 执行分析与插桩
* ClassReader是class字节码的读取与分析引擎
*/
ClassReader classReader = new ClassReader(fis);
// ClassWriter写出器, COMPUTE_FRAMES表示自动计算栈帧和局部变量表的大小
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
/**
* 执行分析,处理结果写入classWriter, EXPAND_FRAMES表示栈图以扩展格式进行访问
* 执行插桩的代码就在MyClassVisitor中实现
*/
classReader.accept(new MyClassVisitor(Opcodes.ASM9, classWriter), ClassReader.EXPAND_FRAMES);
//获得执行了插桩之后的字节码数据
byte[] bytes = classWriter.toByteArray();
// 重新写入InjectTest.class中(也可以写入到其他class中,InjectTest1.class),完成插桩
FileOutputStream fos = new FileOutputStream(
new File("src/test/java/com/xyaty/asmdemo/InjectTest.class"));
fos.write(bytes);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public class MyClassVisitor extends ClassVisitor {
public MyClassVisitor(int api, ClassVisitor classVisitor) {
super(api, classVisitor);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
System.out.println("visitMethod==>name="+name);
/**
* 会输出以下方法:
* visitMethod==>name=<init>
* visitMethod==>name=main
*/
MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
return new MyMethodVisitor(api, methodVisitor, access, name,descriptor);
}
}
/**
* 之所以继承自AdviceAdapter,是因为AdviceAdapter是MethodVisitor的子类,
* AdviceAdapter封装了指令插入方法,更为直观与简单,
* 要使用其中的onMethodEnter和 onMethodExit方法进行字节码插桩,
*
* 继承关系如下:
* AdviceAdapter extends GeneratorAdapter
* GeneratorAdapter extends LocalVariablesSorter
* LocalVariablesSorter extends MethodVisitor
*/
public class MyMethodVisitor extends AdviceAdapter {
long start;
private int startIdentifier;
private boolean isInject = false;//是否注入代码
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
System.out.println("visitAnnotation===>methodName="+getName()+", descriptor="+descriptor);
//如果方法的注解名字是@ASMTest,则给此方法注入代码
if ("Lcom/xyaty/asmdemo/ASMTest;".equals(descriptor)) {
isInject = true;
} else {
isInject = false;
}
return super.visitAnnotation(descriptor, visible);
}
protected MyMethodVisitor(int api, MethodVisitor methodVisitor, int access, String name, String descriptor) {
super(api, methodVisitor, access, name, descriptor);
}
/**
* 进入方法插入内容
*/
@Override
protected void onMethodEnter() {
super.onMethodEnter();
if (!isInject) {
return;
}
// start = System.currentTimeMillis();
/**
* @Type owner 调用哪个类
* @Method method 调用某个类的静态方法(参数name: 方法名字,descriptor:方法中参数和方法返回值类型)
*/
invokeStatic(Type.getType("Ljava/lang/System;"), new Method("currentTimeMillis", "()J"));
//调用newLocal创建一个long类型的变量,返回一个int类型索引identifier
startIdentifier = newLocal(Type.LONG_TYPE);
//保存到本地变量索引中,用一个本地变量接收上一步执行的结果
storeLocal(startIdentifier);
}
/**
* 在方法结尾插入内容
* @param opcode
*/
@Override
protected void onMethodExit(int opcode) {
super.onMethodExit(opcode);
if (!isInject) {
return;
}
// long end = System.currentTimeMillis();
// System.out.println("execute: "+(end - start)+"ms");
invokeStatic(Type.getType("Ljava/lang/System;"), new Method("currentTimeMillis", "()J"));
//调用newLocal创建一个long类型的变量,返回一个int类型索引identifier
int endIdentifier = newLocal(Type.LONG_TYPE);
//保存到本地变量索引中,用一个本地变量接收上一步执行的结果
storeLocal(endIdentifier);
//获取System的静态字段out,类型为PrintStream
getStatic(Type.getType("Ljava/lang/System;"),
"out", Type.getType("Ljava/io/PrintStream;"));
/**
* "execute: "+(end - start)+"ms"实际是内部创建StringBuilder来拼接
* 源码:NEW java/lang/StringBuilder
* 创建一个对象StringBuilder
*/
newInstance(Type.getType("Ljava/lang/StringBuilder;"));
// dup压入栈顶,让下面的INVOKESPECIAL 知道执行谁的构造方法创建StringBuilder
dup();
/**
* 源码:INVOKESPECIAL java/lang/StringBuilder.<init> ()V
* 创建StringBuilder的构造方法,用init来代替
*/
invokeConstructor(Type.getType("Ljava/lang/StringBuilder;"),
new Method("<init>", "()V"));
visitLdcInsn("execute: ");
/**
* 源码:INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
* 调用append方法
*/
invokeVirtual(Type.getType("Ljava/lang/StringBuilder;"),
new Method("append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"));
/**
* 对结束时间和开始时间进行减法操作
* LLOAD 3 先加载结束时间
* LLOAD 1 后加载开始时间
* LSUB 执行减法操作
*/
loadLocal(endIdentifier);
loadLocal(startIdentifier);
//执行减法操作,返回long类型
math(SUB, Type.LONG_TYPE);
/**
* 源码:INVOKEVIRTUAL java/lang/StringBuilder.append (J)Ljava/lang/StringBuilder;
* LDC "ms"
*/
invokeVirtual(Type.getType("Ljava/lang/StringBuilder;"),
new Method("append", "(J)Ljava/lang/StringBuilder;"));
//拼接毫秒
visitLdcInsn("ms");
/**
* 源码:
* INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
* INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
* INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
*/
invokeVirtual(Type.getType("Ljava/lang/StringBuilder;"),
new Method("append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"));
invokeVirtual(Type.getType("Ljava/lang/StringBuilder;"),
new Method("toString", "()Ljava/lang/String;"));
invokeVirtual(Type.getType("Ljava/io/PrintStream;"),
new Method("println", "(Ljava/lang/String;)V"));
}
}
}
再次运行InjectTest.class结果如下,可以看到只有main()方法注入了代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.xyaty.asmdemo;
public class InjectTest {
public InjectTest() {
}
@ASMTest
public static void main(String[] var0) throws InterruptedException {
long var1 = System.currentTimeMillis();
Thread.sleep(1000L);
long var3 = System.currentTimeMillis();
System.out.println("execute: " + (var3 - var1) + "ms");
}
public void methodA() {
System.out.println("methodA");
}
}
到此结束。
参考文章:
https://blog.csdn.net/zenmela2011/article/details/125586333
https://blog.csdn.net/huangbin123/article/details/123322667
我正在使用Ruby,我正在与一个网络端点通信,该端点在发送消息本身之前需要格式化“header”。header中的第一个字段必须是消息长度,它被定义为网络字节顺序中的2二进制字节消息长度。比如我的消息长度是1024。如何将1024表示为二进制双字节? 最佳答案 Ruby(以及Perl和Python等)中字节整理的标准工具是pack和unpack。ruby的packisinArray.您的长度应该是两个字节长,并且按网络字节顺序排列,这听起来像是n格式说明符的工作:n|Integer|16-bitunsigned,network(bi
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visitthehelpcenter.关闭9年前。我正在创建一个Sinatra应用程序,它采用上传的CSV文件并将其内容放入哈希中。当我像这样在我的app.rb中引用这个散列时:hash=extract_values(path_to_filename)我不断收到此错误消息:undefinedmethod`bytesize'forHash:0x007fc5e28f2b90#object_idfile:utils.rblocation:bytesiz
有没有一种方法可以查看ruby中为类分配的内存大小?我构建了一个自定义类,我想知道它在内存中的大小。那么C语言中有没有类似sizeof()的函数呢?我只是想像这样初始化一个新类test=MyClass.new并试图找到一种方法来打印出已分配给内存的类的大小。这在ruby中甚至可能吗? 最佳答案 没有以与C相同的方式计算类大小的语言功能。对象的内存大小取决于实现。这取决于基类对象的实现。估计使用的内存也不简单。例如,如果字符串很短,则可以嵌入到RString结构中,但如果它们很长(NevercreateRubystringsl
同时Ruby1.9wascompilingtobytecode,它无法将预编译的脚本保存到磁盘。我们被告知期待Ruby2toallowsavingcompiledbytecode到磁盘,但我没有听到太多关于它的讨论,也没有看到无数的博客文章描述如何通过编译获得性能,我希望看到它是否真的在Ruby2.x的某个地方实现。AfocusedGooglesearch似乎没有返回任何有用的东西。在2.1(或更早版本)中可以吗?如果没有,这是否仍在路线图上? 最佳答案 有一半可能。从here下载扩展并编译它。需要库iseq.so好的,现在字节码的
在Ruby中计算一个字节是奇校验还是偶校验的最佳方法是什么?我有一个可用的版本:result="AB".to_i(16).to_s(2).count('1').odd?=>true不过,将数字转换为字符串并计算“1”似乎是一种糟糕的计算奇偶校验的方法。有什么更好的方法吗?我希望能够计算3DESkey的奇偶校验。最终,我想将偶数字节转换为奇数。谢谢,丹 最佳答案 除非你拥有的速度不够快,否则请保留它。它清晰简洁,性能比您想象的要好。我们将根据数组查找对所有内容进行基准测试,这是我测试过的最快的方法:ODD_PARITY=[false,
我有两个字符串:a='hànội'b='hànội'当我将它们与a==b进行比较时,它返回false。我检查了字节码:a.bytes=[104,97,204,128,32,110,195,180,204,163,105]b.bytes=[104,195,160,32,110,225,187,153,105]这是什么原因?我该如何修复它以便a==b返回true? 最佳答案 这是Unicodeequivalence的问题.为了比较这些字符串,您需要对它们进行规范化,以便它们都对这些类型的字符使用相同的字节序列。a.unicode_n
我有一个使用Rack::Session::Cookie的Sinatra应用useRack::Session::Cookie,:key=>'my.key',:path=>'/',:expire_after=>3600,#Inseconds:secret=>'something'我在session中有一个地方可以设置数据,我转储了session,大约在erb调用之前有600字节puts"sessionis#{session.inspect}"==>400bytesoftext然后我得到Warning!Rack::Session::Cookiedatasizeexceeds4K.Warnin
我正在尝试编写Ruby代码来检查我发现的特定消息上的椭圆曲线数字签名算法(ECDSA)签名here.问题是我不知道如何将公钥的八位字节字符串转换为OpenSSL::PKey::EC::Point目的。如果我用C写这个,我会把八位字节字符串传递给OpenSSL的o2i_ECPublicKey,它做的事情接近我想要的,实际上被referenceimplementation使用.但是,我搜索了sourcecodeofRuby(MRI)而且它不包含对o2i_ECPublicKey的调用,所以我不知道如何在不编写C扩展的情况下使用Ruby中的该函数。这是十六进制的八位字节字符串。它只是一个0x0
我想获取ruby中数组(项目)内容的字节大小。我这样填充我的数组:@records.eachdo|record|itemstable,:id=>record.id,:lruos=>record.updated_at}end事实上,当我在JSON中序列化它时,我想强制发送这个数组的Content-Length:respond_todo|format|#response['Content-Length']=items.to_s.sizeformat.json{render:json=>{:success=>"OK",:items=>items}}end所以任何这样做的想法都可能很有趣。
我一直在阅读newruby2.0features,发现它会支持字节码导入/导出:Ruby2.0isexpectedtomakeitsimpletosavepre-compiledRubyscriptstobytecoderepresentationsandtothenrunthesedirectly.我已经安装了ruby-2.0.0-p0,但是我没有找到任何关于如何导出字节码的信息(或者一般的关于这方面的文档)。此功能是否已经实现,如果是,我该如何使用它?我也想知道一些细节。YARV字节码是否应该与平台无关?所有的gem都自动包含在字节码中吗? 最佳答案