f8g

TomblooのexecuteWSHでオブジェクトのやりとり

TomblooのexecuteWSHはこういう仕様になってます。

/**
 * Windows上でWSHを実行する。
 * スクリプト内でWScript.echoなどで出力された文字列も返り値に含まれる。
 * 
 * @param {Function} func WSHスクリプト。
 * @param {Array} args WSHスクリプトの引数。 
 * @param {Boolean} async 非同期で実行するか。 
 * @return {String} WSHスクリプトの実行結果。
 */

第1引数でfunctionを渡し、渡したfunctionの引数にargsが入ってくる、という感じです。argsをWSH側に渡す際、それぞれの値をunevalしているので、objectでも渡せるという仕組み。つまり、WSHFirefoxJavaScript、どちらでも同じオブジェクトを共有しているような、スクリプトエンジンの境目を意識させないプログラミングが可能になってます。格好いいですね。
ただし、WSH側から返ってくる値は全て文字列ってのが悲しい。多少、無理矢理にでもWSH側から返ってくるオブジェクトをそのまま使えるようにしたい。

JSONライブラリをロードさせる

そもそもWSHJSONをサポートしてないのが問題なので、外からライブラリを持ってきて読み込ませます。
使う → http://www.json.org/js.html
TomblooではMochiKitがロードされてるのでそれを使おうかと思ったんですが、JSON関係だけを読み込ませるだけでもなかなか面倒そうだったんで、もっとシンプルなやつを選びました。ネイティブのprototypeに書き込みまくってるけど気にしない。

addAround(this, "executeWSH", function(proceed, args){
	// http://www.json.org/json2.js
	var jsonLib = (function () {if (!this.JSON) {JSON = {};}(function () { function f(n) {return n < 10 ? "0" + n : n;} if (typeof Date.prototype.toJSON !== "function") {Date.prototype.toJSON = function (key) {return this.getUTCFullYear() + "-" + f(this.getUTCMonth() + 1) + "-" + f(this.getUTCDate()) + "T" + f(this.getUTCHours()) + ":" + f(this.getUTCMinutes()) + ":" + f(this.getUTCSeconds()) + "Z";};String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function (key) {return this.valueOf();};}var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, gap, indent, meta = {'\b': "\\b", '\t': "\\t", '\n': "\\n", '\f': "\\f", '\r': "\\r", '"': "\\\"", '\\': "\\\\"}, rep; function quote(string) {escapable.lastIndex = 0;return escapable.test(string) ? "\"" + string.replace(escapable, function (a) {var c = meta[a];return typeof c === "string" ? c : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);}) + "\"" : "\"" + string + "\"";} function str(key, holder) {var i, k, v, length, mind = gap, partial, value = holder[key];if (value && typeof value === "object" && typeof value.toJSON === "function") {value = value.toJSON(key);}if (typeof rep === "function") {value = rep.call(holder, key, value);}switch (typeof value) {case "string":return quote(value);case "number":return isFinite(value) ? String(value) : "null";case "boolean":case "null":return String(value);case "object":if (!value) {return "null";}gap += indent;partial = [];if (Object.prototype.toString.apply(value) === "[object Array]") {length = value.length;for (i = 0; i < length; i += 1) {partial[i] = str(i, value) || "null";}v = partial.length === 0 ? "[]" : gap ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" : "[" + partial.join(",") + "]";gap = mind;return v;}if (rep && typeof rep === "object") {length = rep.length;for (i = 0; i < length; i += 1) {k = rep[i];if (typeof k === "string") {v = str(k, value);if (v) {partial.push(quote(k) + (gap ? ": " : ":") + v);}}}} else {for (k in value) {if (Object.hasOwnProperty.call(value, k)) {v = str(k, value);if (v) {partial.push(quote(k) + (gap ? ": " : ":") + v);}}}}v = partial.length === 0 ? "{}" : gap ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" : "{" + partial.join(",") + "}";gap = mind;return v;default:;}} if (typeof JSON.stringify !== "function") {JSON.stringify = function (value, replacer, space) {var i;gap = "";indent = "";if (typeof space === "number") {for (i = 0; i < space; i += 1) {indent += " ";}} else if (typeof space === "string") {indent = space;}rep = replacer;if (replacer && typeof replacer !== "function" && (typeof replacer !== "object" || typeof replacer.length !== "number")) {throw new Error("JSON.stringify");}return str("", {'': value});};}if (typeof JSON.parse !== "function") {JSON.parse = function (text, reviver) {var j; function walk(holder, key) {var k, v, value = holder[key];if (value && typeof value === "object") {for (k in value) {if (Object.hasOwnProperty.call(value, k)) {v = walk(value, k);if (v !== undefined) {value[k] = v;} else {delete value[k];}}}}return reviver.call(holder, key, value);} cx.lastIndex = 0;if (cx.test(text)) {text = text.replace(cx, function (a) {return "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);});}if (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]").replace(/(?:^|:|,)(?:\s*\[)+/g, ""))) {j = eval("(" + text + ")");return typeof reviver === "function" ? walk({'': j}, "") : j;}throw new SyntaxError("JSON.parse");};}}());});

	var _args = [
		function(){
			try{
				var args = Array.apply(null, arguments);
				args.pop()();
				return JSON.stringify(args.pop().apply(null, args));
			}
			catch(e){
				return e.description;
			}
		},

		args[1].slice()
	];

	_args[1].push(args[0], jsonLib);

	var result = new Function("return " + proceed(_args))();

	return result;
});
var r = executeWSH(function(a, b, c){
	return {p: (a + b + c).toString()};
}, [1,2,3]);

log(r);

WSH.Echoを使ったりする時も、ちゃんとJSON形式を意識しないとダメとか制限が出てきますが、使い方を限定すれば使いやすくなるかも。