KLab若手エンジニアの これなぁに?

KLab株式会社の若手エンジニアによる技術ブログです。phpやRubyなどの汎用LLやJavaScript/actionScript等のクライアントサイドの内容、MySQL等のデータベース、その他フレームワークまで幅広く面白い情報を発信します。

ActionScript

brainf**kでつくる携帯Flash

みなさまこんにちは。takada-atです。 携帯サイト開発の大きな障害となるFlashLiteの制限を突破する画期的なツールを開発しました! 簡単に紹介すると、現在DoCoMo, au, Softbankの携帯電話の一番多くで動作するFlashのバージョンはFlashLite1.1だと言われています。 FlashLite1.1で動作するActionScript(ActionScript1相当)は、配列が使えない、関数が使えないという非常に制限されたものです。外部との通信やファイル容量にもかなり制限がくわえられてしまいます。 そこで、brainf**kのような使いやすい言語(笑うところ)で、FlashLite1.1向けの開発ができれば、携帯Flash開発の生産性を格段に高められるはずだと考えました。brainf**kについて知らない方はwikipediaの記事などをごらんください。 http://ja.wikipedia.org/wiki/Brainfuck 今回開発したbf2flasm は brainf**kをSWF(FlashLite1.1対応)に変換します。 これによってbrainf**kによる携帯Flash開発が可能となりました! (ちょっと嘘。アウトプットが文字列の表示だけなので、できることはかなり制限されます) 以下がHello, worldを表示するbranf**kコードの実行結果です。 (ちゃんと携帯実機でも動作します)。


■動作原理 SWFのアセンブリ / ディスアセンブリができる flasmというツールを使用しています。 http://www.nowrap.de/flasm.html bf2flasm.py というスクリプトで、brainf**kのコードを、flasmのアセンブラ言語に変換し、flasmを使って、アセンブラコードをSWFに埋め込みます。動作原理を図にしてみました。


以下がHello, world!と表示するbrainf**kのコードです。

+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+.

以下がそれを flasmアセンブラ(FlashPlayerの機械語に対応するもの)に変換したものです。長いのでpastebinに貼ります。 http://pastebin.com/rNp8vkb3 このツールは brainf**kコードを flasmが理解できるアセンブラに変換するまでを担当します。いわば brainf**kをフロントエンド、SWFをバックエンドとするコンパイラだと思ってもらえば問題ないかと思います(もっとも brainf**k とflasm アセンブラだとあまり機能が変わらないので、コンパイラというよりはトランスレーターと言いたくなりますが)。 ただし、brainf**kの機能の内、「標準入力から1文字受けとる」というのはFlashでは実現が難しかったため、実装していません。 ■ダウンロード http://lab.klab.org/young/wp-content/uploads/data/code/flasm.tar.gz ■使い方 flasmのインストールが必要です。

$ python -V
Python 2.5.5
$ cat hello.bf
+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+.
$ python bf2flasm.py hello.bf > hello.flm
$ flasm -a hello.flm
hello.flm successfully assembled to testinout.swf, 3922 bytes

ActionScript開発のコツ: flex-config の利用

Flexを利用したActionScript開発では、デバッグがボトルネックになることがしばしばあります。 プリントデバッグを利用するのも一苦労です。 私が開発中によく利用している方法をご紹介します。 ActionScriptで手軽に関数を利用するには無名パッケージでグローバルに関数を定義し、それをソースパスにふくめるのが便利です。 私は、以下で紹介されているlog関数などの便利関数を開発中のみソースパスに含めています。 http://subtech.g.hatena.ne.jp/secondlife/20070219/1171872801 この方法のメリットは以下の通り
  • 無名パッケージだからいちいちパッケージをimportしなくていい。
  • クラスじゃなくて関数だからインスタンスをつくる必要もない(ActionScriptでは、ファイル名と関数名を同じにすることでグローバル関数を定義できます)。
では、これらのソースコードを開発中のみ読み込むようにするにはどうすればいいでしょう? 方法は簡単で、デバッグ用のXMLを用意します。Flexではビルド時の設定をxmlファイルで指定できるのでそれを利用します。 以下をdebug-config.xmlという名前で保存。開発中は、こちらの設定ファイルを読み込むようにします。 source-pathを設定しているのは、便利関数をオートロードするため。 debug を true に設定することでエラーメッセージにソースコードの行数も載るようになります。
<flex-config>
  <compiler>
    <debug>true</debug>
    <source-path append="true">
      <path-element>./</path-element>
    </source-path>
  </compiler>
</flex-config>
mxmlcのオプションに以下を指定し、上のファイルをコンパイル時に読み込みます。
-load-config+=/path/to/debug-config.xml
私はrascutというビルド用ツールを使っているのですが、rascutを使用する場合、rascutは以下のようにします。こちらのオプションを rascut起動用のスクリプトに書き込んでおき、デバッグ用xmlを読み込まないスクリプトと読み込むスクリプトを、「開発中ビルド用」「製品版ビルド用」で使いわけています。 -start-rascut-dev.bat
rascut -c"-load-config+=/path/to/debug-config.xml" some.mxml
以下、デバッグ中にロードする便利関数を紹介します。まずは先にあげたlog関数です。 log関数の他にも以下のような関数を利用しています。 (先ほどの、debug-config.xmlと同じディレクトリにおけば、これらの関数もオートロードされるようになります。ファイル名と関数名を同じにする必要があるので気をつけてください)。 -dump.as dump関数。オブジェクトを文字列にダンプする関数。ObjectUtilクラスを利用しているだけですが、いちいちパッケージをimportする必要がないのでデバッグ中に重宝します。
package {
    import mx.utils.ObjectUtil;

    public function dump(a:*):String {
        return ObjectUtil.toString(a);
    }
}
-alert.as alert関数。アラートウィンドウをどこからでも使えるようにする関数。
package {
    import mx.controls.Alert;
    public function alert(...r):void {
        var a:* = r[0];
        if(a is String)
            Alert.show(a);
        else
            Alert.show(dump(a));
    }
}

JavaScript/ActionScriptで相互に会話

明けましておめでとうございます!! ども、amo-kです。 2009年最初の投稿は、JavaScript / ActionScript についての内容です。 個人的にはRIA大好物ですので、この辺りは興味深いジャンルです。 では、本題に入ります。 JavaScript と ActionScript で相互に会話ができることを知った。 ...ということは、 ブラウザ操作をトリガーにFlashを操作、 Flash操作をトリガーにブラウザを操作できるということだ。 これは面白い!! ※厳密には、FlashもブラウザのFlashプレイヤで動作している訳だがそこはおいておこう。 という訳で早速動作確認がてら、簡単なjs, as(Flex用mxml)を書き相互会話サンプルアプリを作ってみた。 サンプルアプリ概要
1.JavaScript(白色部分)からActionScriptの関数を実行 サンプルアプリ上部のボタン「js2as」クリックで ActionScript側の関数を実行、実行時刻をFlash上に表示。 2.ActionScript(黒色部分)からJavaScriptの関数を実行 サンプルアプリ下部のボタン「as2js」クリックで JavaScript側の関数を実行、実行時刻をブラウザ上に表示。 ※勿論いずれも何度押してもかまわない。


処理概要
上記サンプルアプリは、flash.externalパッケージのExternalInterfaceクラスを利用している。 JavaScriptからActionScriptの関数を実行するには、ExternalInterface.addCallback()メソッドを利用する。このメソッドでは、JavaScriptから呼び出すActionScript関数を定義する。 また、ActionScriptからJavaScriptの関数を実行するにはExternalInterface.call()メソッドを利用する。このメソッドでは、ActionScriptからJavaScriptの関数名を指定する。 サンプルソース ブラウザ側のサンプルソースコードは、サンプルアプリ白色部分を右クリックしてソースを確認できますので サンプルソースはFlash側のみ記載します。
import flash.external.ExternalInterface;
import mx.controls.*

// as2jsボタンクリック時実行
private function callFunction():void {
    if (ExternalInterface.available) {
        try {
            ExternalInterface.call("as2js");
        }
        catch(e: Error) {
            Alert.show("error : " + e);
        }
    }
}

// JavaScriptイベントのコールバック関数定義
private function setCallbackFunction():void {
    if (ExternalInterface.available) {
        try {
            //JavaScript から実行
            ExternalInterface.addCallback("js2as", function(): void {
                var date: Date = new Date();
                var dateTime: String = date.getFullYear().toString();
                dateTime += "-" + (date.getMonth() + 1).toString();
                dateTime += "-" + date.getDate().toString();
                dateTime += " " + date.getHours().toString();
                dateTime += ":" + date.getMinutes().toString();
                dateTime += ":" + date.getSeconds().toString();
                myLabel.text = "JavaScript => ActionScript: " + dateTime;
            });
        }
        catch(e : Error) {
            Alert.show("error : " + e);
        }
    }
}
参考情報

C/C++ のコードを Flash Player 上で動かす Alchemy

たかだです。 先日 Adobe Labs が C/C++ のコードを Flash Player 向けにコンパイルする Alchemy というプロジェクトをプレビューリリースしました。 Alchemy は llvm を利用し、C/C++ のコードを ActionScript3(ActionScript ByteCode ではありません)に変換し、それを SWF または SWC 形式にコンパイルします。 llvm の最適化処理によって、しばしばネイティブの ActionScript より高速に動作するそうです。 新しもの好きのわたしが早速こちらを動かしてみたいと思います。 以下 Alchemy:Documentation:Getting Started を参考に、Windows+Cygwin 環境で Alchemy を動かします。 必要なものは、以下の通りです。
  • Cygwin
  • (Cygwin上で動く)Perl
  • (Cygwin上で動く)zip
  • (Cygwin上で動く)gcc/g++
  • Java
  • Flex 3.2 SDK

Flex のインストール

最初に Flex 3.2 SDK をインストールします。Flex SDK は以下から 3.2 をダウンロードしてください。 (Cygwin および Java などのインストールについては以下では解説しません)。 解凍し、適当なパスに置いてください。 Cygwin 上で Flex SDK に Path を通す必要があります。 ここでは Flex SDK のインストール先を C:/develop/flex_sdk_3/ とします。 以下を .bashrc などに書きくわえてください。

PATH=/cygdrive/c/develop/flex_sdk_3/bin/:$PATH
export PATH
正しくパスが通っていれば、以下のようになるはずです。

$ mxmlc -version
Version 3.2.0 build 3958

Alchemy のインストール

Adobe Labs より「Alchemy Toolkit for Cygwin on Windows」をダウンロードし、適切な場所に解凍しておきます。 (以下では C:/develop/alchemy-cygwin/ に解凍したものとします)。 最初に config というスクリプトを実行します。

$ cd /cygdrive/c/develop/alchemy-cygwin/
$ ./config 
Generating alchemy-setup...
Turning execution bit on for Alchemy binaries...

Add "source /cygdrive/c/develop/alchemy-cygwin/alchemy-setup" to your login script.
  "alc-home" takes you to the Alchemy install folder.
  "alc-on" puts Alchemy gcc toolchain replacements at the front of your path.
  "alc-off" restores original path.
  "alc-util" shows you various Alchemy-related environment vars
You need Flash 10 or AIR 1.5 and the Flex 3.2 SDK installed for testing.
以上により alchemy-setup が生成され、alchemy ツールのいくつかが使えるようになりました。 alchemy-setup というシェルスクリプトを実行することで必要な環境変数を設定し、alc-on などのコマンドを呼び出せるようになります。 ただし、こちらを実行する前に Air Debug Launcher(ADL)のパスを alchemy-setup に追加しておく必要があります。ADL は Flexディレクトリの bin/ 以下にある adl.exe というファイルです。 alchemy-setup を編集し、ADL のパスを追加してください。 (以下は alchemy-setup に書き加えます)。

export ADL=/cygdrive/c/develop/flex_sdk_3/bin/adl.exe
ログイン時に、alchemy-setupが実行されるようにするため、.bashrcを変更します。Adobe Labsのgetting startedには、achacksディレクトリをPATHに追加するように書いてありますが、追加しない方がよいようです。 (alchemy-setupの場所は、設定に応じて変更してください)。 (以下は.bashrcに書き加えます)。

source /cygdrive/c/develop/alchemy-cygwin/alchemy-setup
alchemy-cygwin/bin/llvm-stub にシンボリックリンクをはる必要もあるようです

cd bin/
ln -s llvm-stub llvm-stub.exe
以上が終わったら、Cygwinにログインし直し環境変数の設定が更新されるようにします。 (単に source .bashrc を実行するだけでもかまいません)。

C コードのコンパイル

以上までで alc-on / alc-off というコマンドを利用できるようになりました。 alc-on を実行すると gcc コマンドの参照先が alchemyプロジェクトのものに変更され、alc-off で元に戻ります。

$ alc-on
$ which gcc
/cygdrive/c/develop/alchemy-cygwin/achacks/gcc
さっそくサンプルプログラムをコンパイルしましょう。alchemy のディレクトリの中にすでにサンプルプログラムが用意されています。 ここでは、stringecho.c というコードをコンパイルします。 コードを一部だけのぞいてみましょう。以下のようなC言語の関数が定義されています。引数が null かどうかチェックし、AS3_String 型にして返すだけの関数のようです。

static AS3_Val echo(void* self, AS3_Val args)
{
	//initialize string to null
	char* val = NULL;
	
	//parse the arguments. Expect 1.
	//pass in val to hold the first argument, which
	//should be a string
	AS3_ArrayValue( args, "StrType", &val );
	
	//if no argument is specified
	if(val == NULL)
	{
		char* nullString = "null";
		//return the string "null"
		return AS3_String(nullString);
	}
	
	//otherwise, return the string that was passed in
	return AS3_String(val);
}

alc-on の状態で、gcc コマンドを利用し、コンパイルします。

$ cd /cygdrive/c/develop/alchemy-cygwin/samples/stringecho/
$ gcc stringecho.c -Wall -swc -o stringecho.swc
WARNING: While resolving call to function 'main' arguments were dropped!

4328.achacks.swf, 363894 bytes written
frame rate: 60
frame count: 1
69 : 4
72 : 363824
76 : 33
1 : 0
0 : 0
frame rate: 24
frame count: 1
69 : 4
77 : 506
64 : 31
63 : 16
65 : 4
9 : 3
41 : 26
82 : 471
1 : 0
0 : 0
converting to DOS line endings
catalog.xml: done.
  adding: catalog.xml (deflated 75%)
  adding: library.swf (deflated 61%)
$ ls -l
total 149
drwx------+ 2 takada-at ????????         0 Dec  1 10:02 as3
-r-x------+ 1 takada-at ????????       435 Nov 13 10:20 readme.txt
-r-x------+ 1 takada-at ????????      1221 Nov 13 10:20 stringecho.c
-rwxr-xr-x  1 takada-at mkgroup-l-d 144137 Dec  1 11:07 stringecho.swc
SWC ファイルが生成されました! 生成された SWC ファイルはライブラリとして ActionScript コードの中で利用できます。 stringecho.swc は、引数として渡された文字列を、そのまま返すだけの関数を提供しています。 ここでは、以下のような mxml コードを利用して、このライブラリの動作を試してみましょう。以下を、Hello.mxml として保存し、同じディレクトリに stringecho.swc も置きます。

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="main()">
    <mx:Label text="aaa" id="labelone" fontSize="24" />
    <mx:Script><![CDATA[
        public function main():void{
            import cmodule.stringecho.CLibInit;
            var lib:CLibInit = new CLibInit();
            var stringEcho:Object = lib.init();
            labelone.text = stringEcho.echo("Hello!");
        }
    ]]></mx:Script>
</mx:Application>
SWFファイルとしてコンパイルします。target-player の指定を忘れないように注意してください。

$ mxmlc.exe -library-path+=./stringecho.swc Hello.mxml -target-player=10.0.0
設定ファイル "C:\develop\flex_sdk_3\frameworks\flex-config.xml" をロードしています
C:\develop\alchemy-cygwin\samples\stringecho\Hello.swf (461380 bytes)
生成されたSWFファイルをブラウザにドラッグすると...? 期待通り、Helloの文字が表示されました。
 KLab若手エンジニアブログのフッター