抽象机模式
(已发表在《程序员》2003年第1期上)
撰文/Julio García-Martín Miguel Sutil-Martín
Universidad Politécnica de Madrid[1]
翻译/马维达
摘要
由于现代高级编程语言和现有硬件之间的差异日渐增大,常常有必要引入中间语言、并在原始硬件之上构建抽象机。本论文描述了抽象机(Abstract Machine),一种对抽象机的本质特性进行捕捉的结构型模式;这些特性给出了对抽象机的定义。该模式将抽象机的静态特性和动态特性作为分离的组件进行描述,并且还考虑了指令集和这些指令的语义,以及此模式的其他一些重要组件。
意图
为抽象机的设计定义通用模板。该模式捕捉在抽象机之下的本质特性(也就是,数据区、程序、指令集,等等),将它们的相互分离的、松耦合的组件封装进结构模式中。此外,本论文还提供了抽象机的各组件进行交互的协作结构。
别名
虚拟机(Virtual Machine)、抽象状态机(Abstract State Machine)。
动机
在今天,Java是计算机科学中的一个时髦话语。尽管Java是一种用于分布式和GUI应用开发的面向对象语言,但几乎每天它都在增加新的非常有用的编程特性和工具。作为一项事实,在Java的成功中,虚拟机技术的使用有着极为重大的意义[2]。众所周知,Java虚拟机(Java Virtual Machine,JVM)是基于软件的抽象机,可在不同的微处理器机器上工作(也就是,独立于硬件)。JVM的设计者必须遵从JVM的规范,进行必要的处理,将JVM虚拟环境桥接进具体的操作系统和微处理器中。这个在虚拟环境之后的“桥梁”允许软件开发者“一次编程,到处运行”(Write Once,Run Anywhere)[1],因为不管底层的微处理器是什么,JVM都必须根据JVM的标准规范以同样的方式工作。
由于现代高级编程语言和现有硬件之间的差异日渐增大,常常有必要引入中间语言、并在原始硬件之上构建抽象机。但是,在许多情况下,差异是如此之大,以致于我们难以看出源语言是怎样与中间语言相关联的,或者,难以看出中间语言是怎样与硬件相关联的。遗憾的是,在许多情况下,抽象机仅仅被表示为一些寄存器、内存区和机器指令集的集合,只有很少的、或没有与源语言的对应性。
图1 抽象机的例子:Abstract-WAM [3]
该项工作的目标是双重的:a)首先,为抽象机的设计发明一套方法学;其次,b)使用此方法学来描述一个基于模式的构架,用于设计编程语言编译器。对这样的实现技术(也就是,抽象机)的使用允许将编译任务规划为渐进的优化过程,这样,就可以根据中间的抽象机之间的关系来一步一步获得高性能特性。通过此过程还保持了一个抽象机与下一个抽象机之间的同一性。每个编译步骤都明确地生成一些新特性和变动,并将其增加到全局编译过程中(见图2)。
* 也可以是“定义”
图2 通过虚拟机渐进优化进行的编译过程
该过程给出了更为抽象和系统的构造编译器的方法。此外,它还促进了对编译过程的理解,并简化了对先前的设计进行优化和复用的任务。一个(原型)编译器或多或少有可能作为抽象机设计的边际效应自动导出。
适用性
抽象机模式适用于下面的任意一种情形:
l 当我们想要定义抽象机时。该模式提供了编写高级规范的通用骨架,允许程序员将注意力更多地集中在定义抽象机的各组件上,而不是去担心它们的合并(这可以由此模式“免费”提供)。所编写的规范可以是形式化或非形式化的。
l 当我们想要通过使用抽象机技术来编译语言时。抽象机提供了适当的构架来描述通过中间语言的渐进优化进行的编译过程(也就是,原型式开发)。在这样的过程中,各中间语言(也就是,抽象机)之间的关系定义了整个编译过程。
l 当我们想要定义通过应用抽象机获得的编译器时。这是对上面的两种情况进行结合的结果。
l 当我们想要测试同一指令的不同语义时。可以为同一指令定义不同的语义,同样,也可以获得同一语义的不同实现。
l 当我们想要测试同一抽象机的不同指令集时。可以为同一抽象机定义不同的指令集。而且,编译过程的渐进优化意味着每个中间的抽象机都定义了比前一抽象机所管理的指令集更为优化的指令集。
l 当我们想要对我们的程序的执行进行模拟时。程序模拟将可视化工具和调试工具强制性地结合进程序中。很容易将这些查看组件和其他特性视为抽象机模式的参与者。特别地,与可视化或调试有关的执行代码可以通过增强抽象机的指令来定义。
结构
抽象机模式的结构如图3所示。
图3 抽象机模式(结构)
参与者
抽象机可抽象地定义为两个部分的联合:(i)静态部分,由与状态(state)有关的组件组成,(ii)动态部分,确定与抽象机的行为(behavior)相关联的要素。抽象机模式将两个部分的参与者组织和定位为它的结构的组件。
抽象机的状态由以下组件组成:
1. Abstract-Machine Factory(抽象机工厂):它为创建Abstract DataArea和Abstract Program的操作声明接口。
2. Abstract DataArea(抽象数据区):它为数据区对象类型声明接口,用以配置抽象机。它声明两种抽象操作:
l Init操作,用以确定数据区的初始配置,以及
l Stop操作,确定抽象机的执行是否已结束。如果结束条件不依赖于数据区,Stop操作就返回true。
3. Concrete DataArea(具体数据区):它定义具体的数据区对象。具体数据区可以是简单对象或复杂对象结构(对象容器)。该组件必须提供Abstract DataArea接口的实现。
4. Abstract Program(抽象程序):它为汇编程序声明通用接口。它的定义必须处理一系列指令(Abstract Instruction)及一个指令集(Abstract Instruction Set)。此外,它还声明了四种抽象操作:
l Init操作,用以确定汇编程序的初始配置。
l Stop操作,用以确定抽象机是否到达它的最终阶段。如果终止条件不依赖于任何程序配置,Stop操作返回true。
l LoadProg操作负责构造抽象机程序的汇编指令。该操作从输入流中读取文本表示,并将每条指令翻译为Concrete Instruction对象。
l CurrentInst操作返回要由抽象机执行的指令。
5. Concrete Program(具体程序):它定义具体的程序对象。它被定义为具体指令集和一些程序计数器的集合。它必须实现在Abstract Program中定义的Init、Stop和CurrentInst操作。
6. Abstract Instruction Set(抽象指令集):它声明用以表示一组抽象指令的通用接口。
7. Concrete Instruction Set(具体指令集):它通过Concrete Instruction对象来定义一组对象。具体指令集与具体数据区及具体程序相联系。它必须实现Abstract Instruction Set的各操作。
8. Abstract Instruction(抽象指令):它声明用以表示抽象机指令的通用接口。
9. Concrete Instruction(具体指令):它定义具体的指令对象。具体指令直接与具体数据区和具体程序相联系。它必须实现Abstract Instruction的各操作。
在抽象机的行为这一方面,它依赖于在静态组件之上工作的一些操作。这些操作是抽象机状态的定义的一部分,它们负责描述在执行过程中抽象机所到达的不同状态。这些操作描述如下:
1. Abstract-Machine State(抽象机状态):<