2010/07/14

Recent entries from same category

  1. xtag と angular1 を足して2で割った感じに使える薄い JavaScript ライブラリ「sj.js」
  2. Web Component を簡単に作れる JavaScript ライブラリ「X-Tag」
  3. twitter.bat
  4. jQueryプラグインとして動作するGithub Badge作ってみた。
  5. XSLとjQuery/HTMLだけで作る、amazon最速検索

JScript.NETはMicrosoftが.NET Framework向けに拡張したJavasSriptで、Common Language Runtime(CLR)上で動作するJavaScript実装です。
CLR上ではC#、VB.NET等が動作しますが、JScript.NETは言語がJavaScriptという事もあって実はC#やVB.NET等と同等に使えない機能がいくらかあります。
JavaScriptは動的言語です。オブジェクトにプロパティを生やしてメソッドにしたり、prototypeを弄ったり、eval()で関数を生成したりも出来ます。つまりコンパイルするとは言えど、型が動的に変えられる言語です。
その為、.NET Frameworkの機能の一つであるDelegateが使えません。.NET FrameworkのThreadはDelegateという関数型拘束によりスレッドを安全に呼び出せる様になっています。この関数型拘束に緩い拘束なJavaScript(JScript)の関数を渡す事は出来ないのです。
MSDNでもJScriptのDelegate利用制限が書かれています。
ThreadStart デリゲート

[JScript] JScript では、.NET Framework のデリゲートを利用することができます。ただし、独自に定義することはできません。

http://msdn.microsoft.com/ja-jp/library/system.threading.threadstart(VS.71).aspx
Delegate.CreateDelegate()というメソッドも存在しますが、こちらもJScriptからは利用出来ません。
じゃぁDelegateを引数に取るThreadはJScriptから作れないじゃないか!そう思っておられる方が殆どだと思います。

いえ、出来ます。

import System
import System.Reflection
import System.CodeDom.Compiler
import System.Threading
import Microsoft.CSharp

/**
 * createThread()
 * @param   f   thread function
 * @param   a   argument passing to function
 * @return  thread object
 */
var createThread = (function() {
    var source = [
        "using System.Threading;",
        "public class ThreadInvoker {",
        "  private Microsoft.JScript.Closure func;",
        "  private object arg = null;",
        "  public ThreadInvoker(Microsoft.JScript.Closure func, object arg) { this.func = func; this.arg = arg; }",
        "  private void invokerThread() { func.Invoke(null, new object[]{this.arg}); }",
        "  public Thread createInvoker() { return new Thread(invokerThread); }",
        "}",
    ].join("\n");

    var cp = CodeDomProvider.CreateProvider("CSharp");
    var cps = new CompilerParameters();
    cps.ReferencedAssemblies.Add("Microsoft.JScript.dll");
    cps.GenerateInMemory = true;
    var cr = cp.CompileAssemblyFromSource(cps, source);
    var asm = cr.CompiledAssembly;
    return function(f, a) {
        var tt : Type = asm.GetType("ThreadInvoker");
        var args = new System.Object[2];
        args[0] = f;
        args[1] = a;
        return tt.InvokeMember(
            "createInvoker",
            BindingFlags.InvokeMethod,
            null,
            Activator.CreateInstance(tt, args),
            null);
    };
})();
C#上で書かれたスレッドインボーカをメモリ内でコンパイルして、それにJScriptの関数、正しくはクロージャを渡します。
ここから先はCLRなので、どうとでも出来るはずでC#からMicrosoft.JScript.Closureのメソッドを叩く事も出来ます。
これを使えば
var th = createThread(function(v) {
    for (var n = 1; n <= 6; n++) {
        Thread.Sleep(200);
        print("thread" + n + " " + v);
    }
}, "foo");

th.Start();

Thread.Sleep(200);
print("main1");
th.Suspend();
Thread.Sleep(200);
print("main2");
Thread.Sleep(200);
print("main3");
th.Resume();
こんなコードで thread1 foo
main1
main2
main3
thread2 foo
thread3 foo
thread4 foo
thread5 foo
thread6 foo
こんな動作をさせる事も出来ます。JavaScript(JScript)でスレッドが使えると色々とやりたくなってきます。JScriptでサーバアプリなんて事も簡単に出来そうですね。
ちなみに、このcreateThread()を使うと、ウェブサーバなんかも書けたりします。試しに書いてみたらコードの少なさ(createThreadは除いて)に少し驚きました。JScript.NET & C# ばんざい!
import System
import System.Reflection
import System.CodeDom.Compiler
import System.Threading
import Microsoft.CSharp

/**
 * createThread()
 * @param   f   thread function
 * @param   a   argument passing to function
 * @return  thread object
 */
var createThread = (function() {
    var source = [
        "using System.Threading;",
        "public class ThreadInvoker {",
        "  private Microsoft.JScript.Closure func;",
        "  private object arg = null;",
        "  public ThreadInvoker(Microsoft.JScript.Closure func, object arg) { this.func = func; this.arg = arg; }",
        "  private void invokerThread() { func.Invoke(null, new object[]{this.arg}); }",
        "  public Thread createInvoker() { return new Thread(invokerThread); }",
        "}",
    ].join("\n");

    var cp = CodeDomProvider.CreateProvider("CSharp");
    var cps = new CompilerParameters();
    cps.ReferencedAssemblies.Add("Microsoft.JScript.dll");
    cps.GenerateInMemory = true;
    var cr = cp.CompileAssemblyFromSource(cps, source);
    var asm = cr.CompiledAssembly;
    return function(f, a) {
        var tt : Type = asm.GetType("ThreadInvoker");
        var args = new System.Object[2];
        args[0] = f;
        args[1] = a;
        return tt.InvokeMember(
            "createInvoker",
            BindingFlags.InvokeMethod,
            null,
            Activator.CreateInstance(tt, args),
            null);
    };
})();

import System.Net
import System.Net.Sockets

var listener = new System.Net.Sockets.TcpListener(IPAddress.Parse("127.0.0.1"), 8080);
listener.Start();

while (true) {
    createThread(function(client) {
        var stream = client.GetStream();
        var buf = new System.Byte[client.ReceiveBufferSize];
        var bytesRead = stream.Read(buf, 0, buf.length);
        var str = [
            "HTTP/1.0 200 OK",
            "Content-Type: text/html",
            "",
            "<html><body>",
            "<b>hello world</b>: " + (new Date).toString(),
            "</body></html>",
        ].join("\r\n");
        var bytes = System.Text.Encoding.Default.GetBytes(str);
        stream.Write(bytes, 0, bytes.length);
        stream.Close();
        client.Close();
    }, listener.AcceptTcpClient()).Start();
}
マルチスレッドウェブサーバです。カッコイー!
Posted at by | Edit