練炭ブログ

萌え壁紙、Irvine、DMonkey、Proxomitron などの情報を扱ってます。

Irvine/DMonkey: 関数クロージャよりクラスのメソッドが優先される場合がある

コメントなし»
class Test {
  function a () { alert ('class method'); }

  function test () {
    function a () { alert ('local func'); }
    function b () { a (); }

    a ();   // local func
    b ();   // class method
  }
}

(new Test ()).test ();

これはハマる。

Dorothy2 用のスクリプトを作る時は、呼び出し元のクラスで定義されている関数名と重複しないようにしなければなりません(変数名も)。

特に設定プログラム(Dorothy2\setting\*.set)は、Dorothy2set.dms で定義されている class SettingForm に多数の関数が含まれているので注意が必要です。

DMonkey: 名前付き関数式はグローバルスコープに対する関数宣言として動作する

コメントなし»

ECMAScript で名前付き関数式(Named Function Expression: NFE)を使うと、その関数の内部でのみ関数名を参照できます。

一方、DMonkey における NFE はグローバルスコープに対する関数宣言として動作します(同じ関数どころか、どこからでも参照できてしまいます)。

function foo () {
  var a = function bar () { alert ('baz'); };
}

foo ();
bar ();   // baz

トップレベルで普通に関数や変数宣言をした場合と同じ扱いになるようです。Global オブジェクトに入るわけではないので、alert (Global.hasKey ('bar')); は false です。

これはつまり関数内で NFE を使ってしまうと外部にまで晒されるということですが、逆に、あえて NFE を使うことで関数内からグローバル関数を定義することが出来る、とも言えます。

以下のように prototype への読み書きも出来るので、オブジェクトを定義することも出来ます。

(function () {
  void function Foo () { };
  Foo.prototype.bar = function () { alert ('baz'); };
}) ();

var x = new Foo ();
x.bar ();   // baz

最初のサンプルコードでは代入式という形で関数式を記述しましたが、今度は頭に void、末尾に ; を着ける方法をとっています。他に、true && function Foo () { }; といったパターンでも動作するので、関数式であればなんでもOKっぽいです。

なお、関数内からグローバルスコープでオブジェクトを定義したい場合、普通に考えると以下のようなコードになると思いますが、DMoneky ではエラーで動作しません。

(function () {
  Foo = function () { };
  Foo.prototype.bar = function () { alert ('baz'); };
}) ();
var x = new Foo ();
x.bar ();   // Exception: ENameError() => bar

(function () {
  function x () { }
  x.prototype.bar = function () { alert ('baz'); };
  Foo2 = x;
}) ();
var x2 = new Foo2 ();
x2.bar ();   // Exception: ENameError() => bar

DMonkey: 文字列を一文字ずつに分割する方法

コメントなし»

DMonkey: String.split() の動作

DMonkey では 'string'.split ('') で分割できないので

'string'.split (/()/).slice (1, -1)

というのを考えてみましたが、少しマシな方法がありました。

'string'.match (/./g) || [ ]

配列の要素が最低でも一つ欲しければ末尾を || [ '' ] に。

また、文字列が空の可能性がなければ || [ ] は省略可。

DMonkey: String.split() の動作

コメントなし»

DMonkey の String.split() と ECMAScript/JavaScript/JScript のそれとは、動作がかなり違う。

--
第1引数に文字列を渡すと、正規表現に変換されて処理される。文字列そのもので分割することは出来ない。
'1a.2a-3'.split ('a.')[ '1', '2', '3' ]

. ? * \ といった正規表現でメタキャラクタとして扱われる文字で分割する時は注意する。

内部的には
'string'.split ('pattern')'string'.split (new RegExp ('pattern'))
のように取り扱われているもよう。

--
第2引数、第3引数もあるっぽいけど詳細不明。

--
空文字列で分割すると空配列が返ってくる
'string'.split ('')[ ]

JavaScript のように一文字ずつ分割したい場合は

'string'.split (/()/).slice (1, -1)

とする。ただし正規表現を使っているのでマルチバイト文字が入っていると化ける。

# () の意味はないが // と書くとコメント記法と扱われてしまうので不可。
# /()/ の代わりに '()' でも可。
# じゃあ '' でいいじゃん?ってことになるが何故か出来ない(ふりだしに戻る)。

また、少々長くなるが、以下のような方法でも出来る。

String.prototype.split_ = function (s) {
  if (s != '') {
    return this.split (s);
  }

  var result = [ ];
  var len = this.length;
  for (var i = 0; i < len; ++i) {     result.push (this.charAt (i));   }   return result; }; alert ('string'.split_ (''));

--
空文字列を分割すると、空文字列からなる配列が返ってくる
''.split ('x')[ '' ]

これは JavaScript と同じ。ただし空文字列で分割した場合は空配列になる。

--
引数を省略すると undefined が返ってくる。
'string'.split ()undefined

--
/./ で分割すると、文字数 +1 個の空文字列からなる配列が返ってくる
'abc'.split (/./)[ '', '', '', '' ]

これは JavaScript と同じ。ただし JScript(IE の JavaScript)では空配列になる。

--
第1引数で括弧によるキャプチャを使っても戻り値には含まれない。
'a1b2c'.split (/(\d)/)[ 'a', 'b', 'c' ]

ただし JScript(IE の JavaScript)でも戻り値に含まれない。

Irvine/DMonkey: 即時関数パターン

2 個のコメント»

(function(){ ... })()の別の書き方いろいろ - 泥のように

こちらを見て、DMonkey でも JavaScript と同じように動作するのかを調査してみました。

  • Irvine 1.3.1.127 & DMScript 0.3.12
  • 「スクリプトを直接実行」から実行。
var a = "global";

(function(){
  var a = "another paren";
  alert(a); //"another paren"
}());
//OK

+function(){
  var a = "plus";
  alert(a); //plus
}();
// 関数は実行されるがその直後に ENameError(-1)

-function(){
  var a = "minus";
  alert(a); //minus
}();
// 関数は実行されるがその直後に ENameError(-1)

!function(){
  var a = "ex";
  alert(a); //ex
}();
// OK

void function(){
  var a = "void";
  alert(a); //void
}();
// OK

typeof function(){
  var a = "typeof";
  alert(a); //typeof
}();
// OK

new function(){
  var a = "new";
  alert(a); //new
};
// NG

+ 演算子、- 演算子がエラーになります。

上のサンプルコードでは関数が値を返していないので undefined として評価されますが、DMonkey では undefined に対する算術演算がエラーになるためです(JavaScript では +undefined や -undefined が NaN という演算結果になります)。

以下のように、有効な値を返せば動作します。

+function(){
  var a = "plus";
  return a;
}();
// OK

-function(){
  var a = "minus";
  return a;
}();
// OK

new を使う方法は動作しません。new 自体の仕様が JavaScript と違うようです。

次に、そのまま書くパターン。

var b = [ function(){ alert("array"); }() ];
// OK

var b = function(){ alert("bare"); }();
// OK

問題なく動作します。

代入の場合。

res = (function(){return "1";})(); //string の "1"(元のまま)
res = +function(){return "1";}(); //number の 1
res = -function(){return "1";}(); //number の -1
res = !function(){return "1";}(); //false
res = void function(){return "1";}(); //null

どれも値を返しているので動作します。

なお、void にキャストした結果が JavaScript では undefined ですが、DMonkey では null になります(ちなみに typeof null は "object" ではなく "null")。

DMonkey: format 関数

コメントなし»

format()sprintf() は全く同一(別名定義)。

中身は Delphi の Format 関数なので、形式指定子は以下のサイトなどを参考に。
Delphi 6 ローテクTips/Format 形式文字列 - カルト・ドラン

なお DMonkey の format() では引数を配列([ ~ ])に入れず、

format ('%s%s', 'foo', 'bar')

のように直接並べます。

DMonkey: format 関数に数値を渡すとエラーになる問題

コメントなし»

§対象ソフト

  • Irvine 1.3.1.127 & DMScript 0.3.12
  • DMonkey Script Host ver.0.3.9.1

§問題の概要

DMonkey のグローバルオブジェクトの format() 関数において、数値を渡すとエラーが発生する。

テストコード #1

format ('%d', 1+2);

これを実行すると

Exception: EConvertError(1) => Format '%d' invalid or incompatible with argument

というエラーが発生してしまう。%x にしても同じ。

1+21parseInt (1+2) に変更すると、エラーは発生しない。

テストコード #2

//var n = 1; // ok
//var n = parseInt (1 + 2); // ok
var n = 1 + 2; // ng

println ('typeof (n) = ' + typeof (n) + ', n = ' + n);
var s = format ('%d', n);
println (s);

テストコード #3

alert (format ('<%*s>', 10+20, 'test'));

エラーは発生しないが幅指定が無視される。

§原因

未調査。

§対策

parseInt() で数値に変換する。

format ('%d', parseInt (1+2));

§副作用

特に無いと思われる。

Irvine/DMonkey: DMonkey スクリプトから JScript を実行する

コメントなし»

文字列をスクリプトとして実行(MSScriptControl.Scriptcontrol) | 作業日報

ActiveXObject 経由で VBScript、JScript を実行する方法。WSH のスクリプトエンジンを直接呼び出してるっぽいです。

var js = "function main () { var api = new ActiveXObject ('Irvine.api'); api.AddUrl ('http://www.google.co.jp/favicon.ico', 0); return 1; }";

var script = new ActiveXObject ('MSScriptControl.Scriptcontrol');
script.Language = 'JScript';
script.addCode (js);

alert (script.run ('main', {}));

Irvine の「スクリプトの直接実行」でなら動作しますが、Dorothy2 スクリプトからだと EActiveXError エラーになります。

今更な話ですが、もしかして Irvine スクリプトの

※synchronize=1 以外の場合にIrvine.Api/UrlManager/Folder/Itemを使用しないでください

という制限は Irvine.Api 側の仕様ではなくて、DMonkey の ActiveX 機能がメインスレッドでないと使えない仕様によるもの、なのかも知れません(どこまで正しいか不明ですが)。

DMonkey: Date('月 日, 年') が正しく解釈されない問題

コメントなし»

§問題の概要

Irvine のスクリプト機能(DMonkey)において、new Date ('月 日, 年') で日付が正しく解釈されない。

1月(January)を渡した時は内部的にエラーになり現在の日付が返り、2月以降であれば一か月前の日付が返ってしまう。

元ネタ: http://hibari.2ch.net/test/read.cgi/win/1303889299/987n

テストコード

alert (new Date ('Jan 1, 2009'));
alert (new Date ('Feb 1, 2009'));
alert (new Date ('December 31, 1999 23:59:59'));

§原因

Dmonkey の ecma_misc.pas の function DateParse() 内で、EncodeDate() に渡す月データを GetMonth(sl[0]) - 1 として計算しているため。

EncodeDate() は Delphi の標準関数で、月データを 1~12 で受け取る。

§対策

自分でビルドできる場合は、前述のソースの - 1 を削除する(試していないけど多分それでいけるはず)。

ビルド環境がない場合は、バイナリを直接書き換える。

  1. バイナリエディタで irvine.exe を開く。
  2. 16進数で FF 53 0C 8B 45 D0 E8 61 40 FF FF というデータを検索する(1か所だけ見つかるはず)。
  3. その直後の 4890 に書き換える。

Irvine 1.3.1.127 であれば以下のように書き換える。
00099F8F: 48 90

# dec eaxnop に書き換え。

§副作用

特に無いと思われる。

DMonkey: HTTP.length と HTTP.read().length の違い

コメントなし»

Irvine 1.3.1.127 & DMScript 0.3.12

var http = new HTTP ();
http.request ('GET', 'http://www.google.co.jp/favicon.ico');
http.response ();
var data = http.read();

var f = new File('test1234.ico');
f.open ('w');
f.write (data);
f.close ();

alert ("http.length = " + http.length
+ "\nhttp.read() length = " + data.length
+ "\nFile.write() length = " + f.length);

http.read() の返り値(string 型)の .length が小さいことがあるのは、データのバイト数ではなくマルチバイト文字列(Shift_JIS?)として解釈した場合の文字数だからのようです。

http.read().charCodeAt(~) も、マルチバイト文字だった場合 0x100 以上の値を返してきます。

読み込んだデータをバイト単位で読み書きしたい場合は StringBuffer 型に変換すればいいらしいです。