Tamarinとは
Tamarinプロジェクト
Adobe Flash Player 9に組み込まれているAVM2(Adobe ActionScript Vitual Machine)のソースコードがAdobeからMozillaに寄付された。Mozillaはこのコードをオープンソースプロジェクトとしてホストすることになりました。そのプロジェクト名をTamarinといいます。Tamarinプロジェクトの目標は「言語仕様 ECMAScript 第 4 版 (ES4) を、パフォーマンスの高い、オープンソースのコードとして実装すること」とされています。
AVMPlus
Tamarinプロジェクトのソースコードをビルドすると、AVMPlusというVMとガベージコレクタ(MMgc)のライブラリを作り、avmplusというコマンドを手に入れることが出来ます。今回はこのavmplusを使って遊んでみたよ、というお話です。
ビルド
Build Documentation
基本的にはドキュメント通りにやれば簡単にビルドすることができます。また、ドキュメントではFreeBSDがサポートされていませんが、FreeBSDな人はこちらを参考にしてみてください。以降は、Mac OS X 10.5 Leopardにおけるビルド方法になります。
ソースを入手します。hgコマンドはこちらから入手します。
% hg clone http://hg.mozilla.org/tamarin-central tamarin-central
ビルドに必要なものをインストールしておきます。
% sudo port sync % sudo port install libidl % sudo port install autoconf213
XCodeでビルドします。
% cd tamarin-central % xcodebuild -project platform/mac/shell/shell.xcodeproj % cd platform/mac/shell/build/Release % mv shell avmplus
ABC(ActionScript Byte Code)
avmplusはABCファイルフォーマットを実行するコマンドなので、ABCを作成する必要があります。今のところTamarin用のABCを作成出来るコンパイラは、Flex SDKに含まれているASC(ActionScript Compiler)のみなので、これを使います。ASCを使うと、ASをファイルをABCファイルにコンパイルすることが出来ます。
動かしてみる
あらかじめFlex SDKのbinディレクトリにあるasc.jarと、tamarin-central/core/builtin.abcとtamarin-central/shell/toplevel.abcをカレントディレクトリにコピーしておきます。avmplusはFlex SDKのbinディレクトリにでも入れておきましょう。
% echo 'trace("hello, world!")' > hello.as
% java -jar asc.jar -strict -warnings -AS3 -import toplevel.abc -import builtin.abc hello.as
hello.abc, 71 bytes written
% avmplus hello.abc
hello, world!
avmplusのラッパーを作る
asc.jarに渡すオプションは基本的に固定なくせに長いので、これをラップするコマンドを作成します。ついでに、asファイルを食べさせたときはabcを作成してから実行、abcファイルを食べさせたときはすぐ実行というものにします。tamarin-central/core/builtin.abcとtamarin-central/shell/toplevel.abcはFlex SDKのlibディレクトリに置いておきます。オリジナルのavmplusはavmplus.orgとしておきます。
#!/bin/sh
if echo $1 | grep '.as'
then
a=${0%/*};
b=${a%/*};
if [ "$a" != "$b" ]
then
lib=$b/lib;
else
lib='../lib';
fi
java -jar "$lib/asc.jar" -strict -warnings -AS3 -import "$lib/asc/builtin.abc" -import "$lib/asc/toplevel.abc" $1;
file=${1%as}abc;
else
file=$1;
fi
shift;
echo "--------------------------------------------------";
echo "";
avmplus.org $file -- $@;
これでさっきと同じことをやる
% echo 'trace("hello, world!")' > hello.as
% avmplus hello.as
hello.as
hello.abc, 71 bytes written
--------------------------------------------------
hello, world!
avmplusで出来ること
基本的な文法は一通り書けるものの、使えるクラスはごくわずかです。例えば、Flash Player APIである、flash.display.Spriteなどは使えません。ただし、avmplusのソースがあるので、avmplusを拡張することが出来ます。例えば、Spark projectのbeinteractiveさんはGETパラメータやPOSTパラメータを受け取るための拡張を作ったりしています。
GET/POSTというくらいだから、CGIで動かせます。CGIで動かすには、binfmt_miscなどを使って、ABCを直接実行出来るようにしておく必要があります。Mac OS Xの場合は、binfmt_miscのようなものはないので、abcxを使ったり、シェルスクリプトでラップしたりします。
他には、ByteArrayクラスが実装されているのて、ファイルIOが簡単に出来るようになっているので、バイナリでファイルを読み込んでごにょごにょすることで頑張れば色んなフォーマットが扱えます。
avmplusでPNGを生成してみる
PNGEncoderを使って、BitmapDataをPNGファイルに保存する、ということをやってみようと思います。ただし、BitmapDataクラスはSpriteと同じでFlash Player APIなのでavmplusにはありません。しょうがないので必要最低限に実装してみます。おそらく、C++で実装した方が良いのでしょうが、とりあえずASで実装します。
package flash.display
{
public class BitmapData
{
public function BitmapData(width:int, height:int, transparent:Boolean = true, fillColor:uint = 0xFFFFFFFF)
{
_width = width;
_height = height;
_transparent = transparent;
pixels = [];
for (var i:int = 0; i < height; i++)
{
var cols:Array = [];
for (var j:int = 0; j < width; j++)
{
cols.push(fillColor);
}
pixels.push(cols);
}
}
private var pixels:Array;
private var _width:int;
public function get width():int
{
return _width;
}
public function set width(value:int):void
{
_width = value;
}
private var _height:int;
public function get height():int
{
return _height;
}
public function set height(value:int):void
{
_height = value;
}
private var _transparent:Boolean;
public function get transparent():Boolean
{
return _transparent;
}
public function set transparent(value:Boolean):void
{
_transparent = value;
}
public function getPixel(x:int, y:int):uint
{
return pixels[y][x];
}
public function setPixel(x:int, y:int, color:uint):void
{
if (x >= 0 && x <= _width && y >= 0 && y <= _height)
pixels[y][x] = color;
}
public function getPixel32(x:int, y:int):uint
{
return pixels[y][x];
}
public function setPixel32(x:int, y:int, color:uint):void
{
if (x >= 0 && x <= _width && y >= 0 && y <= _height)
pixels[y][x] = color;
}
}
}
続いて、クライアント側のコードです。bitmap.asという名前で保存します。
include 'flash/display/BitmapData.as';
include 'com/adobe/images/PNGEncoder.as';
import com.adobe.images.PNGEncoder;
import flash.utils.ByteArray;
import flash.display.BitmapData;
var img:BitmapData = new BitmapData(300, 300, true, 0xFF000099);
for (var i:int = 100; i < 200; i++)
{
for (var j:int = 50; j < 250; j++)
{
img.setPixel32(j, i, 0xFF990000);
}
}
var byteArray:ByteArray = PNGEncoder.encode(img);
byteArray.writeFile('bitmap.png');
実行するとbitmap.pngというPNGファイルが生成されます。
% avmplus bitmap.as
