Hatena::Groupmoz-addon

Ci.nsIZIGOROu

2007-10-20記事書けない

ScriptControl (1)

| 05:59 |  ScriptControl (1) - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  ScriptControl (1) - Ci.nsIZIGOROu

ScriptControlを使うと現在のActiveScriptのコンテキスト中に異なるActiveScriptのコンテキストを混在させる事が出来ます。

分かりやすい例で言えば、ブラウザの中でのwindowオブジェクトはGlobalオブジェクトですが、iframe内のcontentWindowもやはりGlobalオブジェクト。しかしこれらは異なります。

従ってwindow.Objectとiframe.contentWindow.Objectは異なるコンストラクタです。

ScriptControlについても同じ事が言えます。

var sctl = WScript.CreateObject("ScriptControl");
sctl.Language = "JScript";

WScript.Echo(sctl.Modules.Item(1).CodeObject.Object); // [native code]が出る
WScript.Echo(String(Object == sctl.Modules.Item(1).CodeObject.Object)); // false

って感じですね。つまり異なるGlobalオブジェクトになる訳です。

今度は他のGlobalオブジェクト内にコンストラクタを定義した場合、

sctl.Modules.Item(1).AddCode('function TestObject() { this.foo = 1; }');
var testobj;
try {
  testobj = new sctl.Modules.Item(1).CodeObject.TestObject();
}
catch (e) {
  WScript.Echo(e.message);
}

これは「引数の数が一致していません。または不正なプロパティを指定しています。」などと言われて怒られます。

所が、

testobj = sctl.Modules.Item(1).Eval('new TestObject();');

は見事にTestObjectを生成する事が出来ます。

あるいは、

var TestObject = sctl.Modules.Item(1).CodeObject.TestObject;
testobj = new TestObject();

とすれば、普通に実行出来るようです。

sctl.Modules.Item(1).AddCode('TestObject.prototype.getFoo = function() { return this.foo; };');                                        
var TestObject = sctl.Modules.Item(1).CodeObject.TestObject;                                                                           
WScript.Echo((new TestObject).getFoo()); // 1

これは問題無く出来た。まぁ当たり前かw

sctl.Modules.Add("Foo");

で新しいModuleを定義出来るんですけど、この時のAddメソッドの第二引数のオブジェクトが代入出来る事になってるんだけど、これの意味が分からない。

追記1 (2007-10-23T02:36:47+09:00)

id:nanto_viさんに頂いたコメントを折角だから本文に再掲。*1

これは
new sctl.Modules.Item(1).CodeObject.TestObject()
(new sctl.Modules.Item(1)).CodeObject.TestObject()
と解釈されて、sctrl.Modules.Item がコンストラクタではないためですね。
new (sctl.Modules.Item(1).CodeObject.TestObject)()
とすれば無事オブジェクトを生成できます。 id:nanto_viさんのコメント

今確かめたけど、確かに出来ました。すごー。

ところで何故Itemで切れちゃうのかは分からない。w


JScriptで始めるネットワークプログラミング(2) - SctiptXを使ったEchoサーバー

| 05:30 |  JScriptで始めるネットワークプログラミング(2) - SctiptXを使ったEchoサーバー - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  JScriptで始めるネットワークプログラミング(2) - SctiptXを使ったEchoサーバー - Ci.nsIZIGOROu

前回の「 JScriptで始めるネットワークプログラミング - Echoサーバー編 - Ci.nsIZIGOROu - Mozilla 拡張機能勉強会」では、COMのイベントハンドラ登録が余りに気持ち悪いことがご理解頂けたかと思うのですが、ScriptXと言うActiveXをインストールするともう少し綺麗に書けます。

論より証拠。まずはソース。

var EchoServer = function(port) {
  this.socket = WScript.CreateObject("MSWinsock.Winsock");
  this.sink = WScript.CreateObject("ScriptX.Factory").NewEventSink(this.socket, this);
  this.sctl = WScript.CreateObject("ScriptControl");

  this.sink.handler("ConnectionRequest") = this.onConnectRequest;
  this.sink.handler("Connect") = this.onConnect;
  this.sink.handler("DataArrival") = this.onDataArrival;
  this.sink.handler("Error") = this.onError;

  this.sctl.Language = "VBScript";
  this.sctl.AddObject("WScript", WScript);
  this.sctl.AddCode('Function GetData(wsock): Dim brcv: wsock.GetData brcv, vbstring: GetData = brcv: End Function');

  this.socket.Close();
  this.socket.LocalPort = port;
  this.socket.Listen();
};

EchoServer.prototype = {
  run: function() {
    while (true)
      WScript.Sleep(1000);
  },
  GetData: function() {
    return this.sctl.Run('GetData', this.socket);
  },
  onConnectRequest: function(reqID) {
    WScript.Echo("reqID: " + reqID);

    this.ctx.socket.Close();
    this.ctx.socket.Accept(reqID);

    WScript.Echo("Accepted");
  },
  onConnect: function() {
    WScript.Echo("Connect");
  },
  onDataArrival: function(bytesTotal) {
    WScript.Echo("Bytes: " + bytesTotal);
    var rcv = this.ctx.GetData();
    WScript.Echo("Data: " + rcv);
    this.ctx.socket.SendData(rcv);
  },
  onError: function(eNum, eDesc, eScope, eSource, eHelpFile, eHelpContext, eCancel) {
    WScript.Echo("desc: " + eDesc);
    this.ctx.socket.Close();
    WScript.Quit();
  }
};

var server = new EchoServer(7777);
server.run();

ポイントは、ScriptXのNewEventSink()メソッドによって得られるsinkオブジェクトです。

sink.handler(EventName) = handler;

と言う形でイベントハンドラに関数を直接代入出来ます。

注意しなきゃいけないのは、ハンドラ内のthisです。これはsinkオブジェクトそのものを指しています。

従ってNewEventSink()の第二引数にオブジェクトを渡しているのですが、これはハンドラ内ではthis.ctxで取得出来ます。

今はハンドラに関数を直接代入しちゃってますけど、リスナーのようなパターンを使って上手くwrapしてやるといい感じになると考えてます。

*1:いつも助け舟頂いて恐縮です。><

nanto_vinanto_vi2007/10/23 01:14> これは「引数の数が一致していません。または不正なプロパティを指定しています。」などと言われて怒られます。

これは
new sctl.Modules.Item(1).CodeObject.TestObject()

(new sctl.Modules.Item(1)).CodeObject.TestObject()
と解釈されて、sctrl.Modules.Item がコンストラクタではないためですね。
new (sctl.Modules.Item(1).CodeObject.TestObject)()
とすれば無事オブジェクトを生成できます。

ZIGOROuZIGOROu2007/10/23 02:35> id:nanto_viさん

いつもありがとうございます。なるほど、とても納得しました。
実はこれはとあるネタの複線だったりするんで、そう書けるとなると相当楽になりそうです。

nanto_vinanto_vi2007/10/26 23:37> 何故Itemで切れちゃうのかは分からない。

なぜかといわれれば、new演算子の優先順位が関数呼び出しより高いからというのが答えになるでしょうか。でないと new Date().getTime() なども期待したとおり動きませんし。

ZIGOROuZIGOROu2007/10/27 02:42なるほどー。勉強になります。