ASM学习系列(一):引言

1.1 动机

程序分析、程序生成和程序转换都是非常有用的技术,可在许多应用环境下使用:
? 程序分析,既可能只是简单的语法分析(syntaxic parsing),也可能是完整的语义分析 (sematic analysis),可用于查找应用程序中的潜在 bug、检测未被用到的代码、对代码 实施逆向工程,等等。
? 程序生成,在编译器中使用。这些编译器不仅包括传统编译器,还包括用于分布式程序 设计的 stub 编译器或 skeleton 编译器,以及 JIT(即时)编译器,等等。
? 程序转换可,用于优化或混淆(obfuscate)程序、向应用程序中插入调试或性能监视代 码,用于面向方面的程序设计,等等。

所有这些技术都可针对任意程序设计语言使用,但对于不同语言,其使用的难易程度可能会 有所不同。对于 Java 语言,它们可用于 Java 源代码或编译后的 Java 类。在使用经过编译的类时, 其好处之一显然就是不需要源代码。因此,程序转换可用于任何应用程序,既包括保密的源代码, 也包含商业应用程序。使用已编译类的另一个好处是,有可能在运行时,在马上就要将类加载到 Java 虚拟机之前,对类进行分析、生成或转换(在运行时生成和编译源代码也可以,但其速度很 慢,而且需要一个完整的 Java 编译器)。其好处是,诸如 stub 编译器或方面编织器等工具对用户 变为透明。

由于程序分析、生成和转换技术的用途众多,所以人们针对许多语言实现了许多用于分析、 生成和转换程序的工具,这些语言中就包括 Java 在内。ASM 就是为 Java 语言设计的工具之一, 用于进行运行时(也是脱机的)类生成与转换。于是,人们设计了 ASM1库,用于处理经过编译 的 Java 类。这个库的设计使其尽可能保持快速和小型化。对于那些在运行时使用 ASM 进行动态 类生成或转换的应用程序来说,尽可能?高库的运行速度是非常重要的,这样可以保证这些应用 程序的速度不致下降过多。而保持 ASM 库的小型化也非常重要,一方面是为了在内存有限的环 境中使用,另一方面,也为了避免使那些使用 ASM 的小型应用程序或库增大过多。

ASM 并不是惟一可生成和转换已编译 Java 类的工具,但它是最新、最高效的工具之一,可 从 http://asm.objectweb.org 下载。其主要优点如下:
1 ASM 的名字没有任何含义:它只是引用 C 语言中的__asm__关键字,这个关键字允许执行一些用汇编 语言编写的函数。

? 有一个简单的模块API,设计完善、使用方便。 ? 文档齐全,拥有一个相关的Eclipse插件。
? 支持最新的 Java 版本——Java 7。
? 小而快、非常可靠。
? 拥有庞大的用户社区,可以为新用户?供支持。 ? 源许可开放,几乎允许任意使用。

1.2 概述

1.2.1 范围

ASM 库的目的是生成、转换和分析以字节数组表示的已编译 Java 类(它们在磁盘中的存储 和在 Java 虚拟机中的加载都采用这种字节数组形式)。为此,ASM ?供了一些工具,使用高于 字节级别的概念来读写和转换这种字节数组,这些概念包括数值常数、字符串、Java 标识符、Java 类型、Java 类结构元素,等等。注意,ASM 库的范围严格限制于类的读、写、转换和分析。具 体来说,类的加载过程就超出了它的范围之外。

1.2.2 模型

ASM 库?供了两个用于生成和转换已编译类的 API,一个是核心 API,以基于事件的形式
来表示类,另一个是树 API,以基于对象的形式来表示类。
在采用基于事件的模型时,类是用一系列事件来表示的,每个事件表示类的一个元素,比如 它的一个标头、一个字段、一个方法声明、一条指令,等等。基于事件的 API 定义了一组可能 事件,以及这些事件必须遵循的发生顺序,还?供了一个类分析器,为每个被分析元素生成一个 事件,还?供一个类写入器,由这些事件的序列生成经过编译的类。
而在采用基于对象的模型时,类用一个对象树表示,每个对象表示类的一部分,比如类本身、 一个字段、一个方法、一条指令,等等,每个对象都有一些引用,指向表示其组成部分的对象。 基于对象的 API ?供了一种方法,可以将表示一个类的事件序列转换为表示同一个类的对象树, 也可以反过来,将对象树表示为等价的事件序列。换言之,基于对象的 API 构建在基于事件的 API 之上。
这两个 API 可以与“用于 XML 的简单 API”(Simple API for XML,SAX)和用于 XML 文 档的“文档对象模型(Document Object Model,DOM)API”相比较:基于事件的API类似于 SAX,而基于对象的 API 类似于 DOM。基于对象的 API 构建在基于事件的 API 之上,类似于 DOM 可在 SAX 的上层?供。
ASM 之所以要?供两个 API,是因为没有哪种 API 是最佳的。实际上,每个 API 都有自己 的优缺点:

? 基于事件的API要快于基于对象的API,所需要的内存也较少,因为它不需要在内存中 创建和存储用于表示类的对象树(SAX 与 DOM 之间也有同样的差异)。
? 但在使用基于事件的 API 时,类转换的实现可能要更难一些,因为在任意给定时刻, 类中只有一个元素可供使用(也就是与当前事件对应的元素),而在使用基于对象的 API 时,可以在内存中获得整个类。
注意,这两个 API 都是仅能同时维护一个类,而且独立于其他类,也就是说,它们不会维 护有关类层级结构的信息,如果类的转换影响到其他类,那其他这些类的修改应当由用户负责完 成。

1.2.3 体系结构

ASM 应用程序拥有一个很强壮的体系结构方面(aspect)。事实上,对于基于事件的 API, 其组织结构是围绕事件生成器(类分析器)、事件使用器(类写入器)和各种预定义的事件筛选 器进行的,在这一结构中可以添加用户定义的生成器、使用器和筛选器。因此,这一 API 的使 用分为两个步骤:
? 将事件生成器、筛选器和使用器组件组装为可能很复杂的体系结构, ? 然后启动事件生成器,以执行生成或转换过程。
基于对象的 API 也有一个体系结构方面:实际上,用于操作类树的类生成器或转换器组件 是可以组成形成的,它们之间的链接代表着转换的顺序。
尽管典型 ASM 应用程序中的大多数组件体系结构都非常简单,但还是可以想象一下类似于 如下所示的复杂体系结构,其中的箭头表示在类分析器、写入器或转换器之间进行的基于事件或 基于对象的通信,在整个链中的任何位置,都可能会在基于事件与基于对象的表示之间进行转换:

1.3 组织形式

ASM 库划分为几个包,以几个 jar 文件的形式进行分发:
? org.objectweb.asm 和 org.objectweb.asm.signature 包定义了基于事件的 API,并?供了类分析器和写入器组件。它们包含在 asm.jar 存档文件中。
? org.objectweb.asm.util 包,位于 asm-util.jar 存档文件中,?供各种基于 核心 API 的工具,可以在开发和调试 ASM 应用程序时使用。
? org.objectweb.asm.commons 包?供了几个很有用的预定义类转换器,它们大多 是基于核心 API 的。这个包包含在 asm-commons.jar 存档文件中。
? org.objectweb.asm.tree 包,位于 asm-tree.jar 存档文件中,定义了基于对 象的 API,并?供了一些工具,用于在基于事件和基于对象的表示方法之间进行转换。
? org.objectweb.asm.tree.analysis 包?供了一个类分析框架和几个预定义的 类分析器,它们以树 API 为基础。这个包包含在 asm-analysis.jar 存档文件中。
本文档分为两部分。第一部分介绍核心 API,即 asm、asm-util 和 asm-commons 存档文 件。第二部分介绍树 API,即 asm-tree 和 asm-analysis 存档文件。每部分至少包含该 API 与类相关的一章内容、该 API 与方法相关的一章内容、该 API 与注释、泛型等相关的一章内容。 每章都会介绍编程接口及相关的工具与预定义组件。所有示例的源代码都可以从 ASM 网站上获 得。
这种组织形式便于循序渐进地介绍类文件特征,但有时需要将同一个 ASM 类的介绍分散到 几节中。因此,建议依次阅读本文档。如需有关 ASM API 的参考手册,请使用 Javadoc。
印刷约定 斜体用于强调句子中的元素。(译者注:中文中一般改为加粗显示) 定宽字体用于表示代码段。
粗体定宽字体用于强调代码元素。 斜体定宽字体用于表示标记和代码中的变量部分。

1.4 致谢

感谢 François Horn 在制作本文档期间?供的宝贵评论,这些意见极大地?升了本文档的 结构和可读性。

你可能感兴趣的:(#,JAVA,------,ASM)