AIR / ActionScript

Entries

mxmlc/amxmlc のコンパイルの背景

Date
2007-07-30 (Mon)
Category
AIR / ActionScript

Flex SDK は

  1. 無料 で使えて、
  2. 馴染みのテキストエディタを使ってActionScipt の編集ができて
  3. .swf か .air がコンパイル出来る

というよいものです。2週間ほど触ってみて理解した事などをまとめてみます。

大事なのは以下の三点。

  1. mxmlc は .as を .swf へ変換する、(java で書かかれた)コンパイラ
    • その中で、画面への描画をしたいなら、DisplayObject を extends したクラスを作る必要がある。
    • これは予想だけれども、Display Object と全く同じ signature をもつ別のクラスを作れば、Display Object を extends する必要はないと思う。そのかわり Display Object Container やらいろいろ自分で実装せねばならないけれども。でもこのクラス図が提供する世界観がきらいとか、それではどうにもならない、という人が新しい世界を作れる、というのはすばらしい。
  2. amxmlc は、mxmlc に Air 用のプリプロセスを含めたコンパイラ。Air 用のプリプロセス、とは
    • いくつかの Air 専用メタデータ(アノテーション)を ActionScript 3 の文法内で再現できるようにすること(マクロ展開みたいなもの。でも java のアノテーションも同じように、コンパイル前のプリプロセスでやってるんだよね?)
    • mxml を .as ファイルへ変換する事
  3. 以上の事より、amxmlc が展開する .mxml という xml ファイルは無名パッケージで同名の public class に変換される、ということ。

例えば、Hoge.mxml というファイルで、以下のような内容だったとする

<?xml version="1.0" encoding="utf-8"?> 
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" 
  layout="absolute" 
  backgroundColor="0xFFCC00"
  >
  <mx:Script source="bootstrap.as"/> 
</mx:WindowedApplication> 

これは、

package
{
  import mx.core.WindowedApplication
  public class Hoge extends WindowedApplication
  {
    public var layout:String = "absolute";
    public var backgroundColor:uint = "0xFFCC00";
    /* bootstrap.as の内容がここに挿入される */
    public function Hoge ():void 
    {
    }
  }
}

という内容の Hoge.as ファイルが作成される事。

当初の目的

僕がしたかったのは、mxml を作る人(DreamWeaver なり Flex Builder なり、何らかの Visual Editing Software を使う人。)と、.as のコードを書く人を分けたかった。で、そのために一番簡単なのは、.mxml の root content (上の例で云えば mx:WindowedApplication )に何らかの event トリガを仕込む事。creationComplete を使うのが一般的かしらん。

でも、それだと、コード側が全権掌握できない。というか、creationComplete の前に何かしたい時どうする、とか、creationComplete で発動させたい関数の名前が変わる時いちいち mxml 作る人に話さなきゃいけないとか、そもそも静的にはアクセスできない関数をトリガしたい、とかいろいろ問題がある。で、どうするのか考えて、僕なりに出した結論が以下のような bootstrap.as で、上の Hoge.mxml から呼び出されている内容です。

// this code works as a bootstrap
import mx.core.Application;
import mx.events.FlexEvent;
import mx.controls.Alert;
public var myvar:int = 0;
public function initilizer (ev:Event):void { //trace("init: Start"); Alert.show(this); trace(this.myvar); //trace("init: End"); }
Shell.shell.addEventListener(InvokeEvent.INVOKE, function(ev:InvokeEvent):void { // because Application.application is not set yet this moment. Application.application.addEventListener(FlexEvent.CREATION_COMPLETE, Application.application.initilizer); });

一番大きなポイントは、イヴェントの発動が2段構えなところでしょうか。何故こんな面倒な事をしているかというと、上で一度説明していますが、amxmlc がコンパイルする時、この bootstrap.as は変換された Hoge.as の一部に挿入されるだけ、だからなんですね。もし

  • Application.application.addEventListener の行を一番外側に持ってこようとすると動かない→実行時にまだその Object は出来てないから。
  • Application.application.addEventListener でリスンさせている関数が Application.application.initilizer という見慣れない完全修飾型になっているのは、呼び出し元によって、default の名前空間(端的に言えば this )が変わるから。これは Javascript をやってる人にはなじみが深いと思うのだけれど、instance method 内で this を使うと、普段はちゃんと動くのだけれど、Event からトリガされると this が切り替わってしまって動かないとか。それと同じ。
  • Shell.shell の InvokeEvent.INVOKE には、closure をあてているのは、意味のない関数に名前のあてるが癪だから。

これがわかるまで、グローバルに配置しておきたい変数宣言を何処におくかとか、名前空間とか、めちゃくちゃ悩みました。もう遅いので後で、自分が使うプロトタイプなどをアップしておきます。

ActionScript 3 版 print_r

Date
2007-07-25 (Wed)
Category
AIR / ActionScript

オールドスクール開発で、print debug はかかせません。そして php で育った僕には、print_r みたいな、データ構造ダンプ関数がかかせません。というわけで作りました。

使い方


import Utils;

trace( Utils.print_r( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ) );

などとすると、

[trace] ---------------------------------------------------------------------
[trace] :Array =>
[trace]   [0]:int => 0
[trace]   [1]:int => 1
[trace]   [2]:int => 2
[trace]   [3]:int => 3
[trace]   [4]:int => 4
[trace]   [5]:int => 5
[trace]   [6]:int => 6
[trace]   [7]:int => 7
[trace]   [8]:int => 8
[trace]   [9]:int => 9
[trace] ---------------------------------------------------------------------

と、帰ってきます。他に toCamelCase / capitalize ができます。

既存の問題

  • コンストラクタ、イヴェントそれにスコープチェインなどは、まずどうやって表示するのがわかりやすいのか、わからない。取得の方法はわかっていて、ソースを見ればだいたいわかると思います。

コード

package
{
  import flash.utils.describeType;
public class Utils {
public static function print_r (o:*, name:String = "", recur:int = 0):String { var result:String = ""; var type:String = typeof(o); var desc:XML = describeType(o);
// meta data of object if (recur == 0) { result += "---------------------------------------------------------------------\n"; } else { for (var i:int = 0; i < recur; ++i) { result += " "; } } if (name) { result += '[' + name + ']'; } result += ':' + desc.@name + ' => ';
// content of object switch (type) { case "boolean": case "number": case "string": result += String(o); break; case "xml": result += o.toXMLString(); break;0 case "object": if (desc.@name == "Object" || desc.@name == "Array") { for (var key:String in o) { result += "\n"; result += print_r(o[key], key, (recur + 1)); } } else { var prop:XML; //trace("UTIL: " + typeof(o)); //trace("UTIL: " + desc.toXMLString()); // -- properties for each (prop in desc.variable) { result += "\n"; result += print_r(o[prop.@name], prop.@name, (recur + 1)); } // -- methods for each (prop in desc.method) { //trace("UTIL: " + prop.toXMLString()); result += "\n"; result += print_r(o[prop.@name], prop.@name, (recur + 1)); } // -- constructor //<constructor> // <parameter index="1" type="flash.net::URLRequest" optional="true"/> //</constructor> // -- inheritance and implements //<implementsInterface type="flash.events::IEventDispatcher"/> //<extendsClass type="flash.events::EventDispatcher"/> //<extendsClass type="Object"/> // -- events and others.. //<metadata name="Event"> // <arg key="name" value="httpStatus"/> // <arg key="type" value="flash.events.HTTPStatusEvent"/> //</metadata> } break; case "function": //trace("UTIL: " + typeof(o)); //trace("UTIL: " + desc.toXMLString()); result += String(o); break; }
if (recur == 0) { result += "\n---------------------------------------------------------------------"; } return result; }
public static function toCamelCase (str:String):String { var r:String = ""; var a:Array = str.split(' '); for (var s:String in a) { r += Utils.capitalize(a[s]); } return r; }
public static function capitalize (str:String):String { var r:String = ""; r += (str.slice(0, 1)).toUpperCase(); r += str.slice(1); return r; }
} }

fdb あるいは Flash Player debug

Date
2007-07-21 (Sat)
Category
AIR / ActionScript

最近 ActionScript をすこし真面目にやるようになりました。もうほとんど Java で、まぁ覚えやすいような、みんなそこまで Java 好きなのか… Design Pattern をやったので、それも近いうちにまとめたいです。

さて、今回は小ネタで。Flash では伝統的に trace という標準出力関数があり、皆よく print debug などに使いまくってるはずですが、最新の Flash Player.app でどうやるかを解説します。

僕の記憶では、昔のバージョンの Debug 版 Flash Player.app (昔は Projector とか呼ばれてたような…)では、フローティングパネルが開いて、そこに出ていたりしたような気がします(嘘かも)。もちろん、Flash CS3 の IDE 内ではそのように動作するし、coding から、build、debug まで一貫した環境で出来て便利そうではあります。

僕のやり方は、Flex SDK を使った OLD SKOOL!! 開発なので(emacs まではまだたどり着いてない)、無料で手に入るものでやりましょう。ActionScript 3 なので最新バージョンの Flash Player が必要です。以下のページよりダウンロード。

Adobe Flash Player - Downloads

種類がありすぎて confusing ですが、Projector content debugger と書いてあるものを platform 毎に選んでください。ちなみに Flex3 SDK に同梱の Flash Player.app は debug ビルドではありません。(っつぅか何処見ても Debug と Release ビルドの違いがわからないのはどうかと思う。)

さてようやく本題です。

そのまま debug ビルドな Flash Player.app を立ち上げて、trace 文を含む swf ファイルを開こうとすると、debug host は何処だとか聞いてきます。どうやら debug 用に別の server daemon が必要なんですね? Projects Open Source Flash をみるといくつか興味深いプロジェクトがいくつかあります。試しましたけど、なんというか、ソースコードに手を加えなきゃ行けなかったり、それは納得いきません。

で、どうするか。実は Flex3 SDK には fdb という超本格的な debugger が付属しています。これと Flash Player を連携させると、trace を拾えます。というか連携させないと拾えません。しかも致命的に使いにくい点もいくつかあります。以下使い方を。

  • fdb の中から、Flash Player を起動させる必要があります。
  • fdb が Flash Player.app を起動するためにのファイルパスを渡してやる必要がありますfdb は空白を含むパスを理解しません。
  • ので、まず最初に Debug built Flash Player.app を適当な名前に rename して、空白文字列を含まないパスに copy します。僕は /usr/local/flash/debug.app とかしました。
  • fdb とタイプすると、interactive shell のようなものが立ち上がります。次に run /usr/local/flash/debug.app などとすると、Flash Player が立ち上がるので、その dock アイコンに swf ファイルをドロップするもよし、メニューからファイルを開くもよし。
  • 通常の terminal shell から、fdb run /usr/local/flash/debug.app などとすると、Debug built Flash Player が立ち上がる所まで行くので、僕はそれを Shell の Alias に登録しました。
  • そしてその開いた Flash Player で swf ファイルを開くと、fdb が最初に実行を休止します。fdb シェルにて continue あるいは c とタイプすると、実行が再開されます。
  • fdb シェル上にて help または h とタイプすると、コマンド一覧が表示されます。
  • 一度 swf ファイルを Flash Player から閉じると、fdb と Debug built Flash Player のコネクションは途絶えます。というわけで最初からやり直し!なのです。
  • また開いたままでしばらくほっとくと、やはりコネクションタイムアウト、とかいって勝手に切られます。

こんな所でしょうか。

最近 TextMate に TextEditor を鞍替えしようかと思っています。Skeleton (App-config.xmlとかの)作成ができて、one key stroke でbuild → debug player の起動まで云ったりしたら便利そう?あぁ、それにしても1行で終わらせるつもりのエントリに1時間もかけてしまった。。

Apollo on Mac OS X: adt コマンドを使った配布パッケージの作成

Date
2007-04-11 (Wed)
Category
AIR / ActionScript

Apollo を Mac OS X で開発するための記録です。今回は Adobe の sample ページにあるような、.air ファイルの配布パッケージの製作をやってみます。

コマンド実行時 (adl とか adt ) にエラーが出る、という場合は 前回の記事 を参考にしてみてください。

Continue reading

Apollo on Mac OS X: Install(完成版)

Date
2007-04-11 (Wed)
Category
AIR / ActionScript

昨日に引き続き、Apollo を Mac OS X で開発するための記録です。今回はインストールを完了して、(あまりに長くなったので)次エントリで Adobe の sample ページにあるような、.air ファイルの配布パッケージの製作をやってみます。

最初に、この本はダウンロードしましょう。

Apollo:Books:Apollo for Adobe Flex Developers Pocket Guide

僕もやってる事はここに書いてある事です。加えて開発環境 (Flex Builder) を買わないで、無料で何処までできるか、というのがこの記事の目的です。

Continue reading

Return to Page Top