2009年2月6日金曜日

FlexSDKのソースを少し覗いてみる

ActionScript3の構文解析をやりたい(正確には構文解析されたオブジェクトを利用したい)と思っているので、しげるんがアドバイスしてくれたASCを触ってみている。とりあえずはWikiにも書かずメモ程度にここに書いておく。

準備

EclipseにSubversiveとq4eを入れてある事が前提。プラグイン等の詳細はwikiの方を参照する。ちなみに、FlexSDKのソースはJava5以上が前提です。

ソースの取得

FlexSDKのリポジトリのURLは"http://opensource.adobe.com/svn/opensource/flex/sdk"。

  1. 構文解析を行ってくれるモジュールは"modules/asc"なのでcheckoutすりゃいいのだが、FlexSDKのサイトを見ると最新の安定板のバージョンは3.2系っぽいので、tagにある"3.2.0.3958"からcheckoutする事にする。
  2. ついでに、swfを扱えそうなモジュールである"modules/swfutils"も"tags/3.2.0.3958"からcheckoutする。
  3. checkoutしただけだと、Eclipse上ではそもそもJavaProjectですらない扱いとなってしまうが、次の手順でq4eを使って簡単にビルドする。

ビルド

q4eを前提に、pom.xmlを書いた。

  1. まず以下のpom.xmlをascプロジェクトのルートフォルダに配置し、パッケージエクスプローラ上でプロジェクトのルートを右クリックし、[Maven2][Use Maven dependency management]を選択する。
    <?xml version="1.0" encoding="UTF-8"?>
    <project>
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.adobe.opensource</groupId>
      <artifactId>asc</artifactId>
      <name>asc</name>
      <version>3.2.0.3958</version>
      <url>http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/modules/asc</url>
      <build>
        <sourceDirectory>src/java</sourceDirectory>
        <outputDirectory>target/classes</outputDirectory>
        <plugins>
          <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
              <source>1.5</source>
              <target>1.5</target>
              <encoding>UTF-8</encoding>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </project>
    ちなみにGroupId,ArtifactID,Versionについては正式なものがわからんので適当にそれっぽく書いた。mavenを使わない開発なんてしたくないし、開発中はこれで十分だと思う。実際に何か作って依存するなら、SDK本体に付属のasc.jarを使えば良い。
  2. これだけで、q4eが勝手にJavaProjectとしてEclipseに認識させてくれた上にコンパイルも終了している(target/classesに出力されている)。
  3. 実際触るには、ソースも欲しいので、パッケージエクスプローラから[Maven2][Execute Goal]を選択して、Goalには"clean source:jar install"を指定する。これを実行すれば、target直下に"asc-3.2.0.3958.jar","asc-3.2.0.3958-sources.jar"がパッケージとして出力される。q4eを使えばこんなにラクチン。
ascだけが必要ならこれだけで良いのだが、swfutilsも気になるのでこっちもビルドする。swfutilsは以下のpom.xmlを使って、ascと同じ手順を実施すればそれだけでOKOK。
<?xml version="1.0" encoding="UTF-8"?>
<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.adobe.opensource</groupId>
  <artifactId>swfutils</artifactId>
  <name>swfutils</name>
  <version>3.2.0.3958</version>
  <url>http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/modules/swfutils</url>
  <build>
    <sourceDirectory>src/java</sourceDirectory>
    <outputDirectory>target/classes</outputDirectory>
    <testSourceDirectory>test/java</testSourceDirectory>
    <testOutputDirectory>target/test-classes</testOutputDirectory>
    <resources>
      <resource>
        <directory>abc</directory>
      </resource>
    </resources>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.5</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.xmlgraphics</groupId>
      <artifactId>batik-svggen</artifactId>
      <version>1.7</version>
    </dependency>
    <dependency>
      <groupId>com.adobe.opensource</groupId>
      <artifactId>asc</artifactId>
      <version>3.2.0.3958</version>
      <type>jar</type>
    </dependency>
  </dependencies>
</project>

使ってみる

とりあえず適当なActionScript3のソースを作成して、解析させてみる。

  1. 適当に自分のプロジェクトを作成して、先にビルドしたascのモジュールかプロジェクトに依存させる。モジュールを依存させる場合はモチロン、ソースの添付もお忘れなく。
  2. Eclipseで実行の構成を作成する。メインクラスは"macromedia.asc.embedding.ScriptCompiler"で、引数として"-d -AS3 -outdir target -import ../asc/abc/toplevel.abc -import ../asc/abc/builtin.abc Hoge.as"を指定する。ここで「../asc/abc/*.abc」を指定しているが、これはascプロジェクト配下にあるabcファイルの事。これでコンパイルが実行され、targetフォルダにコンパイルされたabcが配置される。

構文解析に関するソースを読む

自分の場合は構文解析された結果を利用したいだけなワケだが、これすら大変。ascのソースには以下の特徴がある。

  • 基本的にコメントは一切ない。
  • かなり汚い。バッドプラクティスとしてとても参考になるケースがしばしば。
  • toString()がひどい。クラス名をリテラルで埋め込んであるだけとか。仕方がないからtoString()の中を置き換えてやれ、と思ってやってみると、「toString()の結果をロジックとして利用している箇所がある」ため、メインのロジックに影響を与えてしまい、正常動作しなくなる。
  • 構文ツリーのルートとなるNodeクラス(クラスだよ、インターフェースじゃないよ!)にあった「public static final boolean useDebugToStrings = false;」なる便利そうなフラグを発見して「そりゃデバッグモードがいいだろ」とフラグをtrueにすると、動作しなくなる。おそらくデバッグモードで動作確認されていないのではなかろうか。
とにかくむかつく事が多い。checkstyle使えよfindbugs使えよ!せっかく便利なツールが揃ってるのにさ!

まぁ、そう言ってても進まないので、ソースを読んでみたい人は以下をエントリポイントに読み進めていくのが良いと感じた。

  • ScriptCompiler/BatchCompiler
    これは実際にコンパイルを行う箇所なので、構文ツリーをどのように意味ある状態でアクセスできるように構築していけば良いかの参考になる。だが、常に同じ要素数を持つべき複数のListがそのまま生でフィールドとして扱われているとか、ヒドイので注意が必要。このクラス内でPairというクラスが定義されており、そいつらが前述の「ヒドイList」のインデックスに対応して各Fileの間の参照関係やら何やらを保持している、という事も重要。インデクスで管理て…Compilerではそんなに長期間保持する必要も無いからやっつけ仕事的な組まれ方なのかもしれない。
  • ProgramNode
    ASやabcのファイルと同じ単位でできあがるNode。この中にクラスに対応するNodeが複数ぶらさがって行くカンジ。
  • 普通はDOMを見るときはルートのインターフェースを見るだろ?とか思うかもしれないが、それはおすすめしない。Nodeはダメ。このクラスはコードリーディングの役に立たない(DOMのNodeのサブクラスのソースがいかにヒドイ事になっていそうか、という苦労の覚悟を決めるには役立つ)。
  • ObjectListというListのラッパークラスもあり、Nodeのコンテナとして使用される事が多い。が、何のためにラッパーになっているのか想像できない仕様。
  • 後はObjectValueやらReferenceValue等のValueObjectみたいなヤツら。
肝心の「参照の解決」などはNodeの中に存在しているというより(存在しているが)、上記のScriptCompiler等の中で定義されているいくつかのListで管理されている点に注意が必要。

…まぁ、地味に読んでいくしかないです。

コメントを投稿