2006-10-06
■ プロジェクトぽく

Google Code とか登録したよ!
http://code.google.com/p/ruby-spidermonkey/
本当は数ヶ月前に登録していたのを見つかってしまったので、整備しますた…。MLもGoogel Group で作った。Googleとハテナに依存しすぎです
http://groups.google.com/group/ruby-spidermonkey
最新版は相変わらずこちらに。
http://nazo.yi.org/rubysmjs/ruby-smjs.tar.gz
Google Codeには、最新のソース一式をtarでパックしたもの、とかバイナリとかは置けないのかなぁ…svnで登録すればいいのか…
後はrubygemとして登録したいなぁ
あと spidermonkeyからdomオブジェクトを扱えるようなすんごい仕組みを誰か考えてください(><)
mal_blue2006/10/07 20:58E4X じゃダメ?
kou2006/10/07 23:19gemを作るならRubyForgeに置いた方がよいと思います.
nazoking2006/10/09 02:13うーん、今までE4Xちゃんと使ったことなかったので調べてみます>mal_blue
nazoking2006/10/09 02:13じゃあまあ、gemができてから移転を考えると言うことで(汗)
2006-07-29
■ js_ObjectClass を使わないように変更

http://rubyist.g.hatena.ne.jp/secondlife/20060729/1154157935
うう、エラります。なんでだろう…。
./spidermonkey.so: ./spidermonkey.so: undefined symbol: js_ObjectClass - ./spidermonkey.so (LoadError)
from test.rb:3
およよ…確かに小文字で始まっているものは使ってはいけないのに使っています…
とりあえず js_ObjectClass は使わないようにしました。NULLで普通のObjectクラスが生成されるはず。運がよければ実行できると思います…
// ガベレージコレクタのためのマーカー・ハッシュ cs->store = JS_NewObject( cs->cx, NULL, 0, 0);
運がよければ、というのは、同じくプライベートな感じの js_ObjectOps は使ってるからで…js_ObjectOpsを自分で定義する方法がわからないのです。これを実装しないと、解決したはずの Functionのtypeofがfunctionと出るように の問題が、またぶり返すことに。
あと、どうしてこちらではエラーが出ないんだろう…?
ちなみに make では
gcc -fPIC -Wall -g -fno-strict-aliasing -O2 -fPIC -I. -I/usr/lib/ruby/1.8/i486-linux -I/usr/lib/ruby/1.8/i486-linux -I. -DXP_UNIX -c spidermonkey.c
spidermonkey.c:605: warning: ‘rbsm_class_no_such_method’ defined but not used
gcc -shared -L"/usr/lib" -o spidermonkey.so spidermonkey.o -lruby1.8 -lsmjs -lpthread -ldl -lcrypt -lm -lc
と warning がでました。
ああ、こちらの警告は無視してもらって大丈夫な、未実装の機能のための関数です。
■ js_GetErrorMessageを自前実装

早速エラーレポートありがとうございます
http://rubyist.g.hatena.ne.jp/secondlife/20060727/1153976440
と思ったら spidermonkey.so をロードすると
./spidermonkey.so: ./spidermonkey.so: undefined symbol: js_GetErrorMessage - ./spidermonkey.so (LoadError)
と言われて落ちる罠。むーん。
spidermonkey は 1.5rc6a-2 なら、僕が使ってるのと同じようなものだしなぁ…どのコードで違いが出ているのか不思議です。その部分を自前で実装してみました。
typedef enum RBSMErrNum { #define MSG_DEF(name, number, count, exception, format) \ name = number, MSG_DEF(RBSMMSG_INCOMPATIBLE_PROTO, 8, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}") MSG_DEF(RBSMMSG_NOT_FUNCTION, 22, 1, JSEXN_TYPEERR, "{0} is not a function") #undef MSG_DEF RBSMErr_Limit #undef MSGDEF } RBSMErrNum; JSErrorFormatString rbsm_ErrorFormatString[2] = { #define MSG_DEF(name, number, count, exception, format) MSG_DEF(JSMSG_INCOMPATIBLE_PROTO, 8, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}") MSG_DEF(JSMSG_NOT_FUNCTION, 22, 1, JSEXN_TYPEERR, "{0} is not a function") #undef MSG_DEF }; static const JSErrorFormatString * rbsm_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber){ return &rbsm_ErrorFormatString[errorNumber]; }
使い方がよくわからないままにほぼコピペで実装してみたので、間違ってるかも…まあ、とりあえずテストは通っています。あとテストコードは削除(コメントアウト)したつもり。
また、この変更によって jscntxt.h を include しなくてもよくなったため、js.msg、 jsopcode.tblが必要なくなりました。多分。
とりあえず、お試しくださいm(..)m
kouはじめまして,このプロジェクトに興味があります.
Google Codeにプロジェクトを作成していらっしゃるようですが,Google Codeのリポジトリでソースを公開する予定はありますか?そうなったら開発状況を追いやすくなるので嬉しいです.
secondlife↑キタコレ!!!
nazokingおお、どうもありがとうございます 予定はあるけど使い方がよくわからないので放置してあり(汗) コード自体はここからダウンロードできるものから変わりないです
nazoking現状をアップロードしました 共同開発してもらえたりしますか!?
nazokingうーん、GoogleCodeって何か不便だにゃあ…
kou気になったところがあったらパッチを送ると思います.
ところで,MLはありますか?
kourbsm.msgがないと怒られてしまいます.
kou手元のDebian sid環境ではsmjs/jsXXX.hは使えなくて,xulrunner-jsなどpkg-configでビルド環境を取得するようになっていました.
ということで,pkg-configでビルド環境が取得できるならそちらを優先するパッチです.
http://pub.cozmixng.org/~kou/diff/ruby-smjs-pkg-config.diff
# pkg-config.rbはRuby/GLib2からいただいてきました.
secondlifegoogle group かなんかでさくっと ML 作ったりするとうれしいかも…。>のぞきさん
2006-07-24
■ [進捗]Functionのtypeofがfunctionと出るように!

やっとSpiderMonkey 1.7 pre-release 1 でもこのテストが通るようになった。
def test_function_type^ cx = SpiderMonkey::Context.new x = cx.eval("x={jsfunc:function(){return 3}}") x.function( "func1" ){|*arg| nil } assert_equal "function", cx.evaluate("typeof( x.jsfunc)") assert_equal "function", cx.evaluate("typeof x.func1") end
今までは、Ruby から定義した関数のtypeofが 'object'になっていたのだ( でも、たとえば上のテストコード中で設定している func1 はjavascript中から呼べる)
方法としては、RubyFunctionClassのgetObjectOptがNULL(デフォルトを使う)だったを定義してやって、js_ObjectOps をコピーしたものを渡してやるとうまくいった。js_ObjectOpsのコピーは Init_SpiderMonkey中で行った。
■ [進捗]Rubyオブジェクトに対するhoge= でのメソッドコールが可能に

def test_js_set_rubyprop cx = SpiderMonkey::Context.new dog = Struct.new("Dog", :name, :age) x=dog.new x.name="hoge" assert_equal 'hoge', x.name cx.set_property( "x", x ); assert_equal "hoge", cx.eval("x.name") assert_equal 'fuga', cx.eval("x.name='fuga'") assert_equal 'fuga', x.name end
2006-07-22
■ 進捗

お久しぶりです! ってもう一ヶ月か!
最近もずっと作成は続けてます。こんなのが動く感じになりました
cx = SpiderMonkey::Context.new cx.set_property( "Time", Time ); script ='Time.at(100).strftime("%Y/%m/%d")' assert_equal eval(script), cx.eval( script ), script
えー、何をしているかというと、spidermonkeyに RubyのTimeオブジェクトを食わせ、javascript中からそのメソッドのメソッドを実行してみた結果と、実際のRubyの結果が一致していることを試しているわけです。
Rubyの hoge= の様なメソッドで値を設定するには、 jsobj['hoge=']( huga ) の要に呼べば!(汗)
逆の方はほとんどできていませんが、method_missing で実装できそうな気がするので、「RubyからJavaScriptオブジェクトのメソッドを呼ぶ」ためには、長いメソッド名が必要になります。set_property のような。
関数の相互呼び出しや例外の受け渡しも一応できているつもりです。
def test_deffunc_exception cx = SpiderMonkey::Context.new x = cx.eval("x={}") pr = x.function( "func1" ){|*arg| raise TESTException.new("test") } assert_raise( TESTException ){ pr.call(); } assert_raise( TESTException ){ cx.eval("x.func1()") } assert_raise( TESTException ){ x.call_function("func1") } assert_nothing_thrown { cx.eval(" try{ x.func1(); }catch(e){} ") } assert_equal "test", cx.eval(" try{ x.func1(); }catch(e){e.toString()} ") assert_equal TESTException, cx.eval(" y=null; try{ x.func1(); }catch(e){ y=e['class']; } y") end
こんな感じですね。
each、each_with_index も実装しました。
def test_each_hash cx = SpiderMonkey::Context.new x = cx.eval("x={a:1, b:2}") r = [] x.each{|a| r << a } assert_equal [1,2], r r2 = [] for i in x r2 << i end assert_equal [1,2], r2 end
eachが実装されているのでEnumerableクラスをincludeすれば色々使える関数が増えます、が、method_missingでjavascriptの関数・プロパティーを呼び出させようと考えているので、色々かち合ってまずそうな予感、がするのでそれはしていません。
目下の問題点は
- プリミティブ値のeachができない( for(..in..)の構文がそれっぽくなるようにしたいので、Stringのeachはできるようにしたい… )
- cvs版のSpiderMonkey(1.7-pre)だとRubyで定義した関数に typeof をすると object と判定されてしまう( 1.5-pre だと 'function'が返ってくる
です。
一応使えるはず
一応それっぽく使えるはずなので、色々使ってみて要望ください m(..)m
http://nazo.yi.org/rubysmjs/ruby-smjs.tar.gz
とりあえず、落ちるのは最低限最優先でなおしたいと思いますので、落ちたら落ちたぞーとコメントください。というか落ちるテストを書いて、ついでに直すパッチも書いてくれると最高です
2006-06-29
■ 細かい変更

やっぱり気持ち悪かったので、JS_EvaluateScriptを呼び出す部分を Value#new からContext#eval に移した。
// jsval から SpiderMonkey::Value を作成 VALUE rb_smjs_value_new_jsval(VALUE context, jsval value){ VALUE retval; retval = rb_funcall(cJSValue, rb_intern("new"), 1, context ); rb_smjs_value_set_jsval( retval, value ); return retval; } // code を実行し、SpiderMonkey::Valueオブジェクトを返す VALUE rb_smjs_context_eval(VALUE self, VALUE code){ sSMJS_Context *cs; Data_Get_Struct(self, sSMJS_Context, cs); SafeStringValue(code); char *source = StringValuePtr(code); jsval value; char *filename = "[null]"; uintN lineno = 1; JSBool ok = JS_EvaluateScript( cs->cx, cs->globalObj, source, strlen(source), filename, lineno, &value); if (!ok) rb_smjs_raise_ruby( cs->cx ); // Rubyでラップしたオブジェクトを返す return rb_smjs_value_new_jsval( self, value ); }
rb_smjs_raise_rubyは、「Rubyコールバック関数が呼ばれたときに発生した最後の例外値の処理」の関数で、機能説明を省いたところだが、こうなっている。
// 最後に発生したエラーの情報 struct sSMJS_Error{ char msg[BUFSIZ]; int status; JSErrorReport report; }SMJS_LastError; void rb_smjs_raise_ruby( JSContext *cx ){ if( SMJS_LastError.status != 0 ){ int stat = SMJS_LastError.status; SMJS_LastError.status = 0; rb_jump_tag( stat ); }else{ char msg[BUFSIZ]; snprintf(msg,BUFSIZ,"%s:%d:%s", (SMJS_LastError.report.filename ? SMJS_LastError.report.filename : "NULL"), SMJS_LastError.report.lineno, SMJS_LastError.msg ); rb_raise(eJSEvalError, msg ); } }
これを見るとわかるかと思うが、以前やったエラーメッセージの取得も、変更した。以前はerrorreporter の中で rb_raise していたが、今回の変更で SMJS_LastError.msg と SMJS_LastEr
ror.report に内容をセットするだけにして、実際に rb_raise するのは rb_smjs_raise_ruby の中だけにした。
■ SpiderMonkey::eval("{x:1,y:2}")がエラーになる

http://rubyist.g.hatena.ne.jp/nazoking/20060617#1150564493
これもなぜか x= の部分がないと値が帰ってこないのだが…
これって、SpiderMonkey 1.5 だとブロック構文とラベル(識別子)だと思われているみたい。ジャヴァスクリプターには忘れられがちだけれどJavaScriptにはラベルというものがあって
http://www.tohoho-web.com/js/statement.htm#stLabel
label1: for (i = 0; i < 10; i++) { for (j = 0; j < 10; j++) { if (func(i, j) { break label1; } } }
こんな感じでネストしたループから一気に抜けるために用意されている。これと間違われているのだ。だから、
js> {x:1, y:2}
typein:1: SyntaxError: invalid label:
typein:1: {x:1, y:2}
typein:1: .......^
あははー。Ruby/SpiderMonkey のようなもので使う場合は面倒な仕様だなぁ…

