トップ 最新 追記 RSS feed

継続にっき

2004|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|12|
2006|01|03|05|06|08|09|10|12|
2007|01|02|03|05|07|12|
2008|10|
2009|01|05|12|
2010|04|05|11|
2011|01|09|12|
2012|02|03|05|09|12|
2013|02|03|
2014|05|09|
2015|12|
2017|09|

2007-12-02 (Sun)

))) JScript(WSH 5.6)小ネタ

車輪の再発明のような気もするけれど。

JavaScriptフレームワーク prototype.jsを使う

次のようなWSFファイルを作成する。

<job id="sample">
  <script language="JScript" src="pre_prototype.js"/>
  <script language="JScript" src="prototype.js"/>
  <script language="JScript" src="post_prototype.js"/>
  <script language="JScript">
    (3).times(function(i) {WScript.Echo(i)});
  </script>
</job>
実装

prototype.jsはブラウザで動かすことを前提とした作りになっているために、そのままではWSHでロードすることができない。 適当にダミーオブジェクトを定義する必要あり(pre_prototype.js)。

// For prototype.js version 1.6.0
var document = {
    createElement:  function() {return {appendChild: function(){}}},
    createTextNode: function() {return {}},
    createEvent:    function() {return {__proto__: {}}},
    write:          function() {return {}},
    getElementById: function() {return {}}
};
var window = {};
var navigator = {appVersion: ""};
var Element = {};

また、オートメーションオブジェクトにtoStringプロパティが定義されておらず Object.inspectの呼び出しに失敗するので、該当関数を再定義してみる(post_prototype.js)。

Object.extend(Object, {
    inspect: function(object) {
        try {
            if (object === undefined) return 'undefined';
            if (object === null) return 'null';
            return object.inspect ? object.inspect() :
                                   (object.toString ? object.toString() : "#<UnknownObject:" + object + ">");
        } catch (e) {
            if (e instanceof RangeError) return '...';
            throw e;
        }
    }
});

Railsのbreakpoint(ruby-breakpoint)もどき

ruby-breakpointはコード中に"breakpoint"と書いておくと実行時にその環境でreplに突入してくれるというもの。 オリジナルは複数行にわたる入力やdRubyを用いたリモートアクセスもサポートするが、そこまで考えなければ簡単に似たようなものが用意できる。

(function(i) {eval(breakpoint)})(0);

実行結果。

> i + 1
1
実装
var breakpoint =
    'for(;;) {' +
    '    WScript.StdOut.Write("> ");' +
    '    if (WScript.StdIn.AtEndOfStream) break;' +
    '    var expr = WScript.StdIn.ReadLine();' +
    '    try {' +
    '        WScript.StdOut.WriteLine(Object.inspect(eval(expr)));' +
    '    }' +
    '    catch (e) {' +
    '        WScript.StdOut.WriteLine("Error Code: " + e.number);' +
    '        WScript.StdOut.WriteLine(e.description);' +
    '    }' +
    '}';

例外発生時にスタックトレース取得

例外をキャッチしてスタックトレースを付与する関数でラップする。

function func1(i, j) {
    (function () { func2("foo") })([]);
}

function func2(i, j) {
    throw new Error("ERROR");
}
func2 = throwsInformationalError(func2);


var Test = Class.create({
    func3: throwsInformationalError(function(i, j) {
        this.func4("bar");
    }, "func3"),

    func4: throwsInformationalError(function(i, j) {
        throw new Error("ERROR");
    }, "func4")
});

それぞれ呼び出したときの実行結果。

func1();

 test.wsf(149, 10) Microsoft JScript 実行時エラー: ERROR
 --- Stack Trace ---
 func2('foo')
 [anonymous]([])
 func1()


(new Test()).func3()

 test.wsf(149, 10) Microsoft JScript 実行時エラー: ERROR
 --- Stack Trace ---
 func4('bar')
 func3()
実装
Object.extend(Function.prototype, {
    getName: function () {
        if (this.name) return this.name;
        var regmatch = this.toString().match(/^function\s*(\w+)/);
        return regmatch ? regmatch[1] : '[anonymous]';
    }
});

function throwsInformationalError(fn, fn_name) {
    function _throwsInformationalError() {
        try {
            return fn.apply(this, arguments);
        }
        catch (e) {
            if (! e.informational) {
                var stack = [];
                for (var i = arguments.callee; i; i = i.caller) {
                    var args = [];
                    for (var j = 0; j < i.arguments.length; j++) {
                        args.push(Object.inspect(i.arguments[j]));
                    }
                    var _fn = (i == arguments.callee) ? fn : i;
                    if (_fn.getName() != "_throwsInformationalError") {
                        stack.push(_fn.getName() +"(" +  args.join(", ") + ")");
                    }
                }
                e.description += "\n--- Stack Trace ---\n" + stack.join("\n");
                e.informational = true;
            }
            throw e;
        }
    };
    if (fn_name) {
        fn.name = fn_name;
    }
    return _throwsInformationalError;
}
  • util.js(breakpointとセット。ロードにはprototype.jsが必要)

foreach

JScriptのfor...in文とVBScriptのFor Each...Next文は言語仕様的には似たようなものだけど、 その実装には決定的な違いがある。その差を埋めるためにEnumeratorが用意されてはいるけれど、 それでもなおカバーしきれないオブジェクトが存在する。ADSIによる属性アクセスなどがそれ。

> var o = GetObject("LDAP://DC=example,DC=com")
#<UnknownObject:>
> o.objectClass
[object Object]
> for (var i in o.objectClass) {WScript.Echo(i)}
undefined
> new Enumerator(o.objectClass)
オブジェクトがコレクションではありません。

ここで各要素にアクセスする方法を調べてみるも、MicrosoftのサンプルはVBScriptでFor Each...Nextしているものばかり。 しょうがないのでVBScript側でFor Each...Next文相当のプロシージャを用意しておいてWSFファイルで読み込んで利用する。 *1

Sub foreach(obj, fn)
  Dim i
  For Each i In obj
    Call fn(i)
  Next
End Sub

JScript側からはこのように。

> foreach(o.objectClass, function(i) {WScript.Echo(i)})
top
domain
domainDNS
undefined

*1 ただ、この例ならばo.objectClass.toArray()とやればアクセス可能。


2004|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|12|
2006|01|03|05|06|08|09|10|12|
2007|01|02|03|05|07|12|
2008|10|
2009|01|05|12|
2010|04|05|11|
2011|01|09|12|
2012|02|03|05|09|12|
2013|02|03|
2014|05|09|
2015|12|
2017|09|
トップ 最新 追記 RSS feed