Ruhunaに組み込むべく Simple ECMAScript Engine のMSVCへの移植をしてみた.
SEEが./configure
でいろいろやってるのでCygwinとVCとを行ったり来たりしながら,どうにかVC上でDLLの形にできたので公開.
なるべく元のソースとヘッダには手を入れない方針でDLLにしたので,実用的じゃない気もするが後学のために.
下準備.GCのDLLを作り,SEEのconfigureをする
MSVC環境(nmake と cl) と Cygwin環境(make と gcc)とを併用するので,GCとSEEは2組用意しておいて,gc.vc,see.vc
と gc.gcc,see.gcc
と区別できるようにしておく.
まず,GarbageCollectorの Boehm gcを作る.
gcには,nmake用のgc.mak
があるので,release(NDEBUG
) 版の libgc.dll
と libgc.lib
, debug(DEBUG
)版の libgcd.dll
と libgcd.lib
を生成するように,gc.vc/gc.mak
を書き換えて(書き換えたgc.mak(gc7.mak)”))
nmake /f "gc7.mak" CFG="gctest - Win32 Release"
nmake /f "gc7.mak" CFG="gctest - Win32 Debug"
で Release と Debug 版それぞれのdllとテストツール(gctest.exe
) がそれぞれのディレクトリの下に出来る.
出来上がったテストツールを実行するとgc.log
というファイルが出来る.
Finalized 6609/6609 objects - finalization is probably ok
とか
Collector appears to work
とか言われて,問題なさそうなのでSEE本体にとりかかる.
SEEは,./configure で必要なものを作ってるので,Cygwinの gc.gcc
のディレクトリでお決まりの
./configure; make; make install
として,Cygwin環境にGCをインストールしておいてから,see.gcc
のディレクトリに移って./configure
を実行し Makefile,config.h
などを作成する.
作成した see.gcc
の中身をそのままsee.vc
に上書きして下準備が終了
VS.netでSEEを作る
SEE には ライブラリの libsee と see-shell と呼ばれる対話型の実行環境があるが,先にライブラリをDLLとして作成する.
VS.netを起動して VisualC++プロジェクトのWin32コンソールプロジェクトを,see.vc\libsee
に作られるように設定.「アプリケーションウィザード」の「アプリケーションの設定」で DLL 空のプロジェクトを指定して「完了」
ここで一度vs.netを終了すると ソリューションファイル(see.vc\libsee\libsee.sln
)が出来てるのでこれを,see.vc\
の直下に移動し,
Project("CLSID") = "libsee","libsee.vcproj","CLSID"
となっているのを
Project("CLSID") = "libsee","libsee\libsee.vcproj","CLSID"
としておく.これは,後々see-shellを作るときのためだ.
その後,移動したソリューションファイルからVS.netを起動し,ソースの追加とパスの設定をしていく.
ソースはsee.gcc/libsee/Makefile
の libsee_la_SOURCES
の近辺から,まずは
cfunction.c debug.c dprint.c dtoa.c enumerate.c error.c function.c
input_file.c input_lookahead.c input_string.c input_utf8.c intern.c
interpreter.c lex.c mem.c native.c no.c obj_Array.c
obj_Boolean.c obj_Date.c obj_Error.c obj_Function.c obj_Global.c obj_Math.c
obj_Number.c obj_Object.c obj_RegExp.c obj_String.c object.c parse.c
regex.c scope.c string.c stringdefs.c system.c tokens.c
try.c unicase.c unicode.c value.c version.c
を追加.ヘッダ類は ソースと同様に see.gcc/libsee/Makefile
の noinst_HEADERS
などから
array.h cfunction.h cfunction_private.h context.h debug.h dprint.h
dtoa.h dtoa_config.h enumerate.h error.h eval.h function.h init.h
input.h intern.h interpreter.h lex.h mem.h native.h nmath.h no.h
object.h parse.h regex.h scope.h see.h string.defs string.h stringdefs.h
stringdefs.inc system.h tokens.h try.h type.h unicase.inc unicode.h unicode.inc
value.h version.h
を追加しておく..
プロジェクトのプロパティの設定
libsee 用のソースには #include <see/array.h>
等と成っているので「追加のインクルードディレクトリ」に$(ProjectDir)..\include\;
を設定.
GCを組み込めるように GC のヘッダ類を gc.vc/include から see.vc/gc にコピーし,「追加のインクルードディレクトリ」に$(ProjectDir)..\gc\
も設定.同様に gc.vc/debug,gc.vc/release
から libgcd.lib,libgc.lib
を see.vc/gc
にコピーして,「追加のライブラリディレクトリ」に$(ProjectDir)..\gc\
を設定し,「追加の依存ファイル」にもそれぞれ設定.
see-shellとの都合上,出力ディレクトリを$(SolutionDir)$(ConfigurationName)
,中間ディレクトリを$(ConfigurationName)
と設定.
config.h
を使うことにしてあるので HAVE_CONFIG_H
を,math.hでの定数類を使うようになってるので _USE_MATH_DEFINES
をプリプロセッサの項に設定して,コンパイラ(orリンカ)オプションの設定はおおむね終了
コンパイルして修正
実際にコンパイルしてみてエラーにならないところまでソースやヘッダに手を入れる.VC7に無い関数(isinf(),rint() etc)類をマクロで置き換えたり,逆にVC7の定義と競合するもの(SEE_strtod)を置き換えたりなどして対応.
DLL化に向けて
Using the Simple ECMAScript Engine#Name Indexを元にExportする関数類をDEFファイルに書き出していく.release(NDEBUG
) 版 と debug(DEBUG
)版で微妙に異なるので,それぞれをlibsee.def
,libseed.def
として作成して,モジュール定義ファイルとしてリンカのオプションに設定.とくに,debug(DEBUG
)版では,debug出力用のフラグ変数が外部からセットできるようになってるので DATA 付のシンボルをEXPORTしておく.
無くても問題ない(と思う)が一応 DllMain
を書いて see.vc\libsee\dllmain.c
としてソースに追加.
特に問題なければビルドしてSEEのDLLが 出来上がる.
see-shellの移植
めでたくDLL化は出来たっぽいが,使えるかどうかは分からないので,see-shellを移植して確かめる.
libseeと同じソリューションに,Win32コンソールプロジェクトとして新しいプロジェクトを(shell.vcporjがsee.vc\shell\
に作られるように)追加.
libseeと同様にプロパティの設定.追加の依存ファイルに libsee(d).lib を追加してDLLを使うようにしておく.
コンパイルは特に問題なく進むがリンクがいくつか「SEE_fooが見つかりません」と失敗するので,そのあたりの関数を libsee(d).def に追加してリンクも問題なく終了.
さあてと,debug\shell.exe
をshell\test\
の.jsファイルを読み込むように起動すると 異常終了してくれる.どこで落ちてるのかを確かめようと -dTEp
のオプション付で起動してみると違うところで異常終了.半ば予想していたことなので慌てずに libsee のプロジェクトのデバッグのプロパティで shell.exe と-f..\shell\test\common.js -f..\shell\test\grammer.js
をコマンドとコマンド引数にそれぞれ設定.shell\main.c
の main
に入ったところにブレークポイントを仕掛けてデバッグを開始.
ステップ実行で流れを追ってくとSEE_input_file
に渡したファイルポインタ(FILE*)がおかしくて fgetc()がEBADF
を返してファイルから何も読めてないまま処理が進んでったことがとりあえずの原因と判明.だが,main.c::run_fileでのSEE_input_fileの呼び出し時のファイルポインタからは問題なく読み出しが出来ている.どうしたものかと 愚繰って見るもコレといってでてこない.「DLLを超えてのファイルポインタの受け渡しは出来ない云々」の書かれていたらしい古いKBの番号らしきポインタがあったがMSDNに該当文書が無いので真偽は不明.が,ほかに[[http://search.acty-net.ne.jp/mfc_search/archive/2003-1/msg00953.html|[mfc 44438] Re: 引数にファイルポインタ含むDLLのエラー]]ってのもあっておそらくこの問題かと推測.プロジェクトのプロパティを見直してランタイム,文字セットを統一して解決.
-dTEp
で落ちていた問題は,libseed.dllがexportした変数へのアクセス違反だったので,”DLL”,“インポート”でMSDNを検索.MSDNのVisual C++ の概念: 機能の追加 インポートとエクスポートにDLL 内に変数をインポートするには,__declspec(dllimport) を必ず使用する必要があります
と原因がそっくり書いてあるのでそのように shell\main.c
を書き換えて解決.
最終的に
shell.exeとlibsee.dllの動くものが出来たので,shell/test/*.js
をそれぞれ動かしてみると全部問題なくPASSしたので公開しておこうかと.
GCもSEEも修正BSDライセンスなのでそのままBSDライセンスで公開.SEE(Win32)”)からどうぞ