練炭ブログ

X680x0、Irvine、DMonkey、Proxomitron などの情報を扱ってます。

DMonkey: StringBuffer における NUL 文字の検索

コメントなし»

StringBuffer.indexOf において、String.fromCharCode (0) で NUL 文字を検索することが出来ます。

var sb = new StringBuffer ('abc');
sb [1] = 0;

var s = String.fromCharCode (0);
alert (sb.indexOf (s));   // 1

// これらの方法では検索できない
alert (sb.indexOf (0));   // -1
alert (sb.indexOf (""));   // -1
alert (sb.indexOf ("\x00"));   // -1

NUL の後ろに文字が続いていても考慮されます。

var sb = new StringBuffer ('a*b*c');
sb [1] = sb [3] = 0;

var s = String.fromCharCode (0) + 'c';
alert (sb.indexOf (s));   // 3

ちなみに StringBuffer を文字列で初期化した場合、データ末尾に NUL 文字は付きません。また length を増やした場合は 0 で埋められます。

var sb = new StringBuffer ('abc');
var s = String.fromCharCode (0);

alert (sb.indexOf (s));   // -1
++sb.length;
alert (sb.indexOf (s));   // 3

DMonkey: 数値文字列の大小比較は数値に変換されて比較される

コメントなし»

数値文字列同士を < > で比較すると、number 型に変換されてから数値として比較されます。

Array.sort() を関数を指定せずに呼び出した場合も内部で同じように比較されているようです。

alert ('5' < '10');   // true alert ('9e1' < '1e2');   // true alert ('10' < '0xf');   // true alert ('1E2' < '1e2');   // false alert ('1E2z' < '1e2z');   // true (辞書順) alert ([ '-1', '0', '+1' ].sort ()); // -1,0,+1

DMonkey: "\n" は CR LF の2バイト

コメントなし»

DMonkey で文字列リテラルの "\n" は CR LF の2バイトに変換されます。

var s = "\n";
alert (s.length);   // 2

var t = '';
for (var i = 0; i < s.length; ++i) {   t += s.charCodeAt (i) + ' '; } alert (t);   // 13 10

正規表現リテラルの \n も CR LF になるようです。

alert ("a\nz".match (/a\nz/));   // a\nz

ファイルの書き出し時などに復帰改行のつもりで "\r\n" と書くと、CR CR LF となってしまうので注意が必要です。

DMonkey: オブジェクト内関数からプロパティに this なしでアクセスできる

コメントなし»

オブジェクトのプロパティに代入した関数を呼び出すと、関数内から this を付けずにオブジェクト内のプロパティを読み書きできてしまいます。

var o1 = { a: 'b', f: function () { alert (a); } };
o1.f ();

var o2 = { a: 'b' };
o2.f = function () { alert (a); },
o2.f ();

JavaScript ではいずれも未定義エラーになりますが、DMonkey では 'b' が表示されます。
(2番目の例から、クロージャとして動作しているのではなく this なしでオブジェクト内のプロパティにアクセスしていることが分かります。)

例えば

var Util = {
  sleep: function (ms) {
    println ('Zzz...');
    sleep (ms); // Global.sleep を呼ぶつもり
  }
};
Util.sleep (1);

のように書くと、sleep ()Util.sleep が再帰呼び出しされてしまいスタックオーバーフローのエラーになります。
(Irvine で実行すると強制終了するので注意して下さい)。

この場合は Global.sleep () と書けば期待通りの動作になります。

なお、冒頭のコードの関数呼び出し部分を o1 ['f'] ();o2 ['f'] (); に変えると、インスタンス['メソッド名']() で this が Global になる挙動の影響により、DMonkey でも未定義エラーになります。

DMonkey: import の動作

コメントなし»
alert (1);

というコードを Irvine の scripts フォルダに test.dms として保存して、

var sl = new Strings ();
sl.add ('alert (2);');
sl.saveToFile ('(Irvineのパス)\scripts\test.dms');

alert (3);
import test.*;

を実行すると、3 → 1 と表示されます。

よって、import で指定されたファイルはスクリプトが実行されるより前(恐らくバイトコードへのコンパイル時)に読み込まれ、import が記述された位置で実行されるということになります。

なお、iffunction などのブロックの中に記述すると SyntaxError になるので、トップレベルでしか使用できません。

DMonkey: 関数は .apply() と .call() が使えて、arguments.callee もある

コメントなし»

実は関数に対しては .apply() と .call() が使えるようで、
DMonkey: インスタンス['メソッド名']() で this が Global になる
の例は

a ['p'].apply (a);   // this is Foo
a ['p'].call (a);   // this is Foo

とすれば期待通りの結果を得られました。

この例だと元のメソッドが引数を取らないので違いがありませんが、ECMAScript と同様に
.apply() は引数の配列を受け取り、.call() は最初の引数の後ろに , で並べた引数を受け取ります。

実行中の関数(自分自身の関数)を指す arguments.callee もあります。
typeof arguments.callee == 'function' です。

[6月16日 追記] 残念ながら、組み込みオブジェクトのメソッドには使えないようです。ENameError => member error apply/call になります。

DMonkey: インスタンス['メソッド名']() で this が Global になる

コメントなし»
Global.foo = 'this is Global';

function Foo () { }
Foo.prototype.foo = 'this is Foo';
Foo.prototype.p = function () {
  // alert (nameOf (this));
  alert (this.foo);
};

var a = new Foo ();
a.p ();   // this is Foo

a ['p'] ();   // this is Global

インスタンス.メソッド名() による呼び出しでは this がそのインスタンス自体を指しますが、インスタンス['メソッド名']() では this が Global を指してしまいます。

DMonkey: 配列を挟んでオブジェクトを循環参照するとエラーになる

コメントなし»
function Foo () {
  this.a = [ this ];
}

function OnStartThread () {
  var f = new Foo ();
}

上記スクリプトを実行すると、EInvalidPointer: Invalid pointer operation と表示されてダウンロードスレッドがエラー終了してしまいます。

常にエラーになるわけではなく、正常に終了する場合もあります。その場合は再度アイテムを登録したり、Irvine を再起動すると発症するようになったりします。厳密な発生条件は不明で、OS などの環境によっては全く発生しないかもしれません。

OnStartThread() 自体は正常に終了して、スクリプト環境の終了処理内(DMonkey のガベージコレクタとか)でエラーが起きているような感じですが、詳しい状況は不明です。

対処としては配列内に循環参照を入れないようにするしかないと思いますが、データの保存場所が配列でなくてもよいいなら、代わりにオブジェクトを使えば回避できるはずです。

  this.a = { length: 0 };
  this.a [this.a.length++] = this;

テスト用スクリプトファイル
添付ファイル:dmonkey_circref_test.zip

DMonkey: boolean と文字列の比較結果

コメントなし»

true == 'true'true == '1' が真、それ以外は偽('True' とか '2' も偽)。

false == 'false'false == '0' が真、それ以外は偽。

new Boolean ('string') の挙動も調べてマニュアルに書き加えておいた方がいいかも知れない。

DMonkey: 変数と代入結果の比較

2 個のコメント»
var a = 1;
alert (a == (a = 2));
a = 1;
alert ((a = 2) == a);

IE8、Firefox 12、Google Chrome 19 では false, true となりますが、DMonkey では逆で true, false になります。

そもそも ECMAScript においてこの式の結果が保証されているのか、未定義や不定や実装依存だったりするのか詳しい人教えてぷりーず。

仮に ECMAScript で正当な式であっても、DMonkey では結果が違うし規格書もないから調べることすら出来ないしで、DMonkey では怪しげな表現は使わない方がいいですね。

ちなみに JavaScript で数値の文字列を3桁区切りに変換するコードで

while (a != (a = ~));

というパターンが使われているのを見かけました。

見かけたっていうか移植しようとしてこの違いに気がついた訳ですが。