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.vcgc.gcc,see.gccと区別できるようにしておく.

まず,GarbageCollectorの Boehm gcを作る.

gcには,nmake用のgc.makがあるので,release(NDEBUG) 版の libgc.dlllibgc.lib, debug(DEBUG)版の libgcd.dlllibgcd.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/Makefilelibsee_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/Makefilenoinst_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.libsee.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.exeshell\test\の.jsファイルを読み込むように起動すると 異常終了してくれる.どこで落ちてるのかを確かめようと -dTEpのオプション付で起動してみると違うところで異常終了.半ば予想していたことなので慌てずに libsee のプロジェクトのデバッグのプロパティで shell.exe と-f..\shell\test\common.js -f..\shell\test\grammer.jsをコマンドとコマンド引数にそれぞれ設定.shell\main.cmainに入ったところにブレークポイントを仕掛けてデバッグを開始.

ステップ実行で流れを追ってくと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)”)からどうぞ