前的三章已经为我们介绍了tamarin和开源的flex-sdk的一些基本操作,本章我们将要更加深入地了解avmshell和asc编译器。 1、深入tamarin项目
到目前为止,我们仔细看看我们的tamarin-tracing的目录结构,可能我们从来都没深入了解也注意到他里面的文件:
|-- build // make后的编译版本
|-- Makefile |
|-- core |
|-- extensions |
|-- nanojit |
|-- pcre |
|-- platform |
|-- shell // 前三章,我们一直在这里用./avmshell和java -jar asc.jar进行示例
|-- space
|-- tamarin-tracing // tamarin源码工程
|-- axscript
|-- build
|-- configure
|-- configure.py
|-- core // 核心文件夹
|-- esc // esc的编译器,相对于asc
|-- extensions
|-- localization
|-- manifest.mk
|-- nanojit
|-- pcre
|-- platform // 平台文件夹,win的项目文件在这里可以找到
|-- shell // shell文件夹
|-- space // mmgc项目文件夹
|-- test
|-- utils // 工具文件夹
`-- zlib
结构大体如上,其中 tamarin-tracing/core是程序的核心项目部分,主要构建builtin.abc部分,也有资料写作global.abc
tamarin-tracing/shell是shell项目部分,主要构建shell.abc,也有资料写作toplevel.abc
关于global.abc和toplevel.abc的构建请参考redtamarin项目(http://code.google.com/p/redtamarin/)
这是另一个不同于官方的tamarin实现。
2、定制我们自己的tamarin
大家是不是有一些头晕,tamarin就tamarin吧,怎么还有个redtamerin? 这是另一套完全不同的项目吗?
其实,关于tamarin项目的结果,只是最终生成了avmshell这个可以运行虚拟机的shell。而shell究竟内容如何,主要靠core和shell这两大块。
而core是核心,里面基本是雷打不动的代码,比较偏重虚拟机内的方法,如AS3的内建方法,而shell则是偏重于本地代码的实现,即不同平台上的shell的实现,类似AIR。
两部分合在一起,构成了tamarin的avmshell。
那么,我们完全可以自己制作一个属于自己的shell,或自己的Integrated Runtime环境,就像之前说的Yahoo!Widgets。
Goole Code上实现的叫redtamarin,我们大可以自己作一个bluetamarin。或者My Integrated Runtime 或叫 MIR,以区别AIR。这是完全可行的。
redtamarin在global.as中选用了
include "core\/builtin.as"
include "core\/Math.as"
include "core\/ErrorConstants.as"
include "core\/Error.as"
include "core\/RegExp.as"
include "core\/Date.as"
include "core\/XML.as"
编译成global.abc来代替官方的 tamarin-tracing/core下的builtin_full.abc 在toplevel.as中,用 include "shell\\toplevel.as" include "shell\\Domain.as" include "shell\\StringBuilder.as" include "shell\\ByteArray.as" include "shell\\IntArray.as" include "shell\\UIntArray.as" include "shell\\DoubleArray.as" include "shell\\FloatArray.as" include "shell\\ShortArray.as" include "shell\\UShortArray.as" include "shell\\Dictionary.as" include "shell\\Endian.as" include "shell\\Java.as" //RedTamarin include "shell\\standard.Errors.as" include "shell\\standard.Operations.as" include "shell\\standard.as"
编译成toplevel.abc代替了官方 tamarin-tracing/shell下的shell_full.abc,如此而已。
3、创建自己的build版本,定制avmshell 如果真要按照redtamarin这样定制下来,你会发现,需要编译的地方很多,出错的地方更多。可能大家会觉得没有足够的知识和耐心是不能作到的。
实际上呢,redtamarin已经是个趋向与成品的东西了,就是说它中间经历过的编译过程和产生的文件,事实上都被删掉了,只留下了最终的结果,所以你按照redtamarin编译是没法成功的。
其实我们没有必要从头来定制,core和shell的大部分代码我们都是很需要的,我们只需要在上面加上我们需要的就可以了,而tamarin-tracing也提供了编译和定制的工具。 我们看 tamarin-tracing/core下面有一个builtin.py,这个文件可以编译一些头文件和abc文件或声明内容。
1)它调用了forth语言编译器fc(在 tamarin-tracing/utils下),生成fpu和soft fp部分,关于forth语言,这里就不再赘述。
2)它使用asc.jar下的macromedia.asc.embedding.ScriptCompiler而不是macromedia.asc.embedding.Main(java -jar asc.jar默认的是执行Main)。
这个ScriptCompiler既可以编译abc又可以生成cpp和h文件,从而在make时进一步编译生成avmshell。
同理在tamarin-tracing/shell下也有shell.py,这个文件编译shell相关的abc和头文件或声明内容。 (注意,在运行这两个python脚本后生成的builtin_full.h和shell_full.h有BUG,稍后我们会详细说明这个问题)
我们通过修改源文件,并使用builtin.py和shell.py来快速地进行cpp和h文件的生成,然后新建空文件夹configure,再通过make就可以构建我们自己的avmshell了。
4、初步实践 首先,我们在build/shell的目录下(还是我们经常测试的那个目录)制作testShell.as:
import avmplus.*;
System.alert("test alert");
编译java-jar asc.jar testShell.as testShell.abc, 100 bytes written
运行./avmshell testShell.abc 结果输出
TypeError: Error #1006: alert is not a function.
at global$init() avmshell运行提示并没有定义这个alert方法。
事实上,shell里也没有这个alert方法。我们可以定义一个。在tamarin-tracing/shell/shell.as中
package avmplus {
public class System {
public native static function exit(status:int):void
public native static function getAvmplusVersion():String
public native static function debugger():void
public native static function isDebugger():Boolean
public native static function getTimer():uint
public native static function readLine():String
// 加入这个方法
public static function alert(text:String):void {
// 方法体,自己定义
write(text);
}
然后编译这个shell的头文件 tamarin-tracing/shell/shell.py
输出 Building Full AS3 shell: 80361 Files: 18 Time: 2614ms
BEFORE 80361
AFTER 46010
SAVED 34351 43%
Building Min AS3 shell: 6504 Files: 6 Time: 836ms
BEFORE 6504
AFTER 3281
SAVED 3223 50%
然后新建一个build2目录去configure & make,
mkdir build2 cd build2 ../tamarin-tracing/configure --enable-shell --enable-debugger make 你会发现,编译错误,这是个BUG。
解决方法是,修改shell_full.h,把所有以AVMPLUS_NATIVE_METHOD_DECL(Atom开头的宏命令都替换成AVMPLUS_NATIVE_METHOD_DECL(BoxReturnType
比如AVMPLUS_NATIVE_METHOD_DECL(Atom, avmplus_Domain_private__load)替换为AVMPLUS_NATIVE_METHOD_DECL(BoxReturnType, avmplus_Domain_private__load)
好了,再make,就可以编译了然后,我们就在build2/shell下,得到了一个新的avmshell 我们有两个不同avmshell了,两个不同的shell环境,想不想搞几个不同的avmshell发行版呢,或者自己作一个特色的AIR出来?
这时候,我们可以针对两个build环境分别测试:在build/shell下用./avmshell testShell.abc 结果输出 TypeError: Error #1006: alert is not a function.
at global$init()
在build2/shell下用./avmshell testShell.abc
结果输出 test alert 有些环境下的tamarin-tracing可能输出不出来结果,不过没关系用
./avmshell -Dastrace 1 testShell.abc
结果输出
87 AVMINF: MTHD global$init ()
88 AVMINF: MTHD Object$$cinit ()
88 AVMINF: MTHD Class$$cinit ()
90 AVMINF: MTHD Function$$cinit ()
90 AVMINF: MTHD Object$/private::_hideproto ()
160 AVMINF: MTHD Object$/private::init ()
161 AVMINF: MTHD Object$/private::_hideproto ()
161 AVMINF: MTHD private::MethodClosure$$cinit ()
164 AVMINF: MTHD Namespace$$cinit ()
164 AVMINF: MTHD Object$/private::_hideproto ()
165 AVMINF: MTHD QName$$cinit ()
165 AVMINF: MTHD Object$/private::_hideproto ()
166 AVMINF: MTHD Boolean$$cinit ()
166 AVMINF: MTHD Object$/private::_hideproto ()
171 AVMINF: MTHD Number$$cinit ()
181 AVMINF: MTHD Object$/private::_hideproto ()
183 AVMINF: MTHD int$$cinit ()
184 AVMINF: MTHD Object$/private::_hideproto ()
186 AVMINF: MTHD uint$$cinit ()
206 AVMINF: MTHD Object$/private::_hideproto ()
218 AVMINF: MTHD String$$cinit ()
219 AVMINF: MTHD Object$/private::_hideproto ()
232 AVMINF: MTHD Array$$cinit ()
233 AVMINF: MTHD Object$/private::_hideproto ()
236 AVMINF: MTHD private::FieldName$$cinit ()
236 AVMINF: MTHD private::StackFrame$$cinit ()
237 AVMINF: MTHD private::ArraySort$$cinit ()
286 AVMINF: MTHD global$init ()
286 AVMINF: MTHD global$init ()
286 AVMINF: MTHD avmplus::Domain$$cinit ()
286 AVMINF: MTHD global/private::getfiles ()
286 AVMINF: MTHD global$init ()
287 AVMINF: MTHD avmplus::System$$cinit ()
287 AVMINF: MTHD avmplus::System$/private::getArgv ()
287 AVMINF: MTHD avmplus::File$$cinit ()
287 AVMINF: MTHD flash.system::Capabilities$$cinit ()
287 AVMINF: MTHD Array/get length ()
287 AVMINF: MTHD Array/http://adobe.com/AS3/2006/builtin::shift ()
287 AVMINF: MTHD Array$/private::_shift ()
287 AVMINF: MTHD Array/get length ()
287 AVMINF: MTHD Array/set length ()
287 AVMINF: MTHD Array/private::_set_length ()
287 AVMINF: MTHD Array/http://adobe.com/AS3/2006/builtin::push ()
287 AVMINF: MTHD Array$/private::_push ()
287 AVMINF: MTHD Array/get length ()
287 AVMINF: MTHD Array/get length ()
287 AVMINF: MTHD Array/set length ()
287 AVMINF: MTHD Array/private::_set_length ()
288 AVMINF: MTHD Array/get length ()
288 AVMINF: MTHD Array/get length ()
288 AVMINF: MTHD Array/get length ()
288 AVMINF: MTHD avmplus::Domain/load ()
288 AVMINF: MTHD avmplus::Domain/private::_readAndLoad ()
288 AVMINF: MTHD global$init ()
288 AVMINF: MTHD flash.utils::ByteArray$$cinit ()
288 AVMINF: MTHD flash.utils::ByteArray$/readFile ()
288 AVMINF: MTHD global/_checkNull ()
288 AVMINF: MTHD flash.utils::ByteArray$iinit ()
288 AVMINF: MTHD Object$iinit ()
289 AVMINF: MTHD global$init ()
289 AVMINF: MTHD avmplus::System$/alert ()
289 AVMINF: MTHD avmplus::System$/write ()
289 AVMINF: MTHD global/_checkNull () testShell
289 AVMINF: MTHD Array/get length ()
看这一行289 AVMINF: MTHD avmplus::System$/alert (),说明alert已经执行,并调用了
289 AVMINF: MTHD avmplus::System$/write ()
好了,今天的内容到此结束。 今天主要稍微简单地深入了一下tamarin的各部分构成,以及如何定制自己的avmshell。 接下来,我们要深入了解native这个本地代码关键字以及如何让AVM2虚拟机来执行外部的本地代码。