nazonoRubyist RubyでJavaScriptのためにC このページをアンテナに追加 RSSフィード

2006-10-06

プロジェクトぽく プロジェクトぽく - nazonoRubyist RubyでJavaScriptのためにC を含むブックマーク はてなブックマーク - プロジェクトぽく - nazonoRubyist RubyでJavaScriptのためにC プロジェクトぽく - nazonoRubyist RubyでJavaScriptのためにC のブックマークコメント

Google Code とか登録したよ!

http://code.google.com/p/ruby-spidermonkey/

本当は数ヶ月前に登録していたのを見つかってしまったので、整備しますた…。MLもGoogel Group で作った。Googleハテナ依存しすぎです

http://groups.google.com/group/ruby-spidermonkey

このグループトピックは 0 件です。

最新版は相変わらずこちらに。

http://nazo.yi.org/rubysmjs/ruby-smjs.tar.gz

Google Codeには、最新のソース一式をtarでパックしたもの、とかバイナリとかは置けないのかなぁ…svnで登録すればいいのか…

後はrubygemとして登録したいなぁ

あと spidermonkeyからdomオブジェクトを扱えるようなすんごい仕組みを誰か考えてください(><

mal_bluemal_blue2006/10/07 20:58E4X じゃダメ?

koukou2006/10/07 23:19gemを作るならRubyForgeに置いた方がよいと思います.

nazokingnazoking2006/10/09 02:13うーん、今までE4Xちゃんと使ったことなかったので調べてみます>mal_blue

nazokingnazoking2006/10/09 02:13じゃあまあ、gemができてから移転を考えると言うことで(汗)

トラックバック - http://rubyist.g.hatena.ne.jp/nazoking/20061006

2006-07-29

js_ObjectClass を使わないように変更 22:04 js_ObjectClass を使わないように変更 - nazonoRubyist RubyでJavaScriptのためにC を含むブックマーク はてなブックマーク - js_ObjectClass を使わないように変更 - nazonoRubyist RubyでJavaScriptのためにC js_ObjectClass を使わないように変更 - nazonoRubyist RubyでJavaScriptのためにC のブックマークコメント

http://rubyist.g.hatena.ne.jp/secondlife/20060729/1154157935

うう、エラります。なんでだろう…。

$ ruby test.rb

./spidermonkey.so: ./spidermonkey.so: undefined symbol: js_ObjectClass - ./spidermonkey.so (LoadError)

from test.rb:3

およよ…確かに小文字で始まっているものは使ってはいけないのに使っています…

  • Extern but library-private function names use a js_ prefix and mixed case, e.g. js_SearchScope.
  • And library-private and static data use underscores, not intercaps (but library-private data do use a js_ prefix).

http://lxr.mozilla.org/mozilla/source/js/src/README.html

とりあえず js_ObjectClass は使わないようにしました。NULLで普通Objectクラスが生成されるはず。運がよければ実行できると思います…

  // ガベレージコレクタのためのマーカー・ハッシュ
  cs->store = JS_NewObject( cs->cx, NULL, 0, 0);

運がよければ、というのは、同じくプライベートな感じの js_ObjectOps は使ってるからで…js_ObjectOpsを自分で定義する方法がわからないのです。これを実装しないと、解決したはずの Functionのtypeofがfunctionと出るように の問題が、またぶり返すことに。

あと、どうしてこちらではエラーが出ないんだろう…?

ちなみに make では

$ 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を自前実装 16:18 js_GetErrorMessageを自前実装 - nazonoRubyist RubyでJavaScriptのためにC を含むブックマーク はてなブックマーク - js_GetErrorMessageを自前実装 - nazonoRubyist RubyでJavaScriptのためにC js_GetErrorMessageを自前実装 - nazonoRubyist RubyでJavaScriptのためにC のブックマークコメント

早速エラーレポートありがとうございます

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

koukou2006/09/27 22:37はじめまして,このプロジェクトに興味があります.
Google Codeにプロジェクトを作成していらっしゃるようですが,Google Codeのリポジトリでソースを公開する予定はありますか?そうなったら開発状況を追いやすくなるので嬉しいです.

secondlifesecondlife2006/10/02 13:13↑キタコレ!!!

nazokingnazoking2006/10/03 01:28おお、どうもありがとうございます 予定はあるけど使い方がよくわからないので放置してあり(汗) コード自体はここからダウンロードできるものから変わりないです

nazokingnazoking2006/10/03 13:46現状をアップロードしました 共同開発してもらえたりしますか!?

nazokingnazoking2006/10/03 13:49うーん、GoogleCodeって何か不便だにゃあ…

koukou2006/10/04 17:34気になったところがあったらパッチを送ると思います.
ところで,MLはありますか?

koukou2006/10/04 23:07rbsm.msgがないと怒られてしまいます.

koukou2006/10/04 23:15手元の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からいただいてきました.

secondlifesecondlife2006/10/06 13:49google group かなんかでさくっと ML 作ったりするとうれしいかも…。>のぞきさん

トラックバック - http://rubyist.g.hatena.ne.jp/nazoking/20060729

2006-07-24

[]Functionのtypeofがfunctionと出るように! 02:04 Functionのtypeofがfunctionと出るように! - nazonoRubyist RubyでJavaScriptのためにC を含むブックマーク はてなブックマーク - Functionのtypeofがfunctionと出るように! - nazonoRubyist RubyでJavaScriptのためにC Functionのtypeofがfunctionと出るように! - nazonoRubyist RubyでJavaScriptのためにC のブックマークコメント

やっと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= でのメソッドコールが可能に 10:39 Rubyオブジェクトに対するhoge= でのメソッドコールが可能に - nazonoRubyist RubyでJavaScriptのためにC を含むブックマーク はてなブックマーク - Rubyオブジェクトに対するhoge= でのメソッドコールが可能に - nazonoRubyist RubyでJavaScriptのためにC Rubyオブジェクトに対するhoge= でのメソッドコールが可能に - nazonoRubyist RubyでJavaScriptのためにC のブックマークコメント

 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
トラックバック - http://rubyist.g.hatena.ne.jp/nazoking/20060724

2006-07-22

進捗 14:01 進捗 - nazonoRubyist RubyでJavaScriptのためにC を含むブックマーク はてなブックマーク - 進捗 - nazonoRubyist RubyでJavaScriptのためにC 進捗 - nazonoRubyist RubyでJavaScriptのためにC のブックマークコメント

お久しぶりです! ってもう一ヶ月か!

最近もずっと作成は続けてます。こんなのが動く感じになりました

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

えー、何をしているかというと、spidermonkeyRubyTimeオブジェクトを食わせ、javascript中からそのメソッドのメソッドを実行してみた結果と、実際のRubyの結果が一致していることを試しているわけです。

Rubyhoge= の様なメソッドで値を設定するには、 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_missingjavascript関数・プロパティーを呼び出させようと考えているので、色々かち合ってまずそうな予感、がするのでそれはしていません。

目下の問題点は

  • プリミティブ値のeachができない( for(..in..)の構文がそれっぽくなるようにしたいので、Stringeachはできるようにしたい… )
  • cvs版のSpiderMonkey(1.7-pre)だとRubyで定義した関数に typeof をすると object と判定されてしまう( 1.5-pre だと 'function'が返ってくる

です。

一応使えるはず

一応それっぽく使えるはずなので、色々使ってみて要望ください m(..)m

http://nazo.yi.org/rubysmjs/ruby-smjs.tar.gz

とりあえず、落ちるのは最低限最優先でなおしたいと思いますので、落ちたら落ちたぞーとコメントください。というか落ちるテストを書いて、ついでに直すパッチも書いてくれると最高です

トラックバック - http://rubyist.g.hatena.ne.jp/nazoking/20060722

2006-06-29

細かい変更 14:51 細かい変更 - nazonoRubyist RubyでJavaScriptのためにC を含むブックマーク はてなブックマーク - 細かい変更 - nazonoRubyist RubyでJavaScriptのためにC 細かい変更 - nazonoRubyist RubyでJavaScriptのためにC のブックマークコメント

やっぱり気持ち悪かったので、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}")がエラーになる 14:51  SpiderMonkey::eval("{x:1,y:2}")がエラーになる - nazonoRubyist RubyでJavaScriptのためにC を含むブックマーク はてなブックマーク -  SpiderMonkey::eval("{x:1,y:2}")がエラーになる - nazonoRubyist RubyでJavaScriptのためにC  SpiderMonkey::eval("{x:1,y:2}")がエラーになる - nazonoRubyist RubyでJavaScriptのためにC のブックマークコメント

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 のようなもので使う場合は面倒な仕様だなぁ…

mal_bluemal_blue2006/07/03 04:01({x:1,y:2}) とカッコをつければオブジェクトが返りますよ。

nazokingnazoking2006/07/03 04:46をを!これは盲点でした

トラックバック - http://rubyist.g.hatena.ne.jp/nazoking/20060629