JScriptのevalを使う

JScriptのevalを簡単なパーサーとして使うぐらいは、探せば腐るぐらい見つかるけど、戻り値の型についての記述は意外と少ない。
とりあえず、JScriptからの戻り値をstringにせずに返してもらって、その型を調べてみるコード。

using System;
using System.Text;
using System.Reflection;
using System.CodeDom.Compiler;

namespace SimpleParser
{
  public class SimpleParser
  {
    static void Main(string[] args)
    {
      SimpleParser sp = new SimpleParser();
      foreach(string exp in args)
      {
        object ret = sp.eval(exp);
        Console.WriteLine("{0}: {1}", ret.GetType(), ret.ToString());
      }
    }
    
    object m_evaluator = null;
    Type m_type = null;
    
    public SimpleParser()
    {
      string source = "package Evaluator { class Evaluator { public function Eval(expr : String) { return eval(expr); } } }";

      CodeDomProvider prov = new Microsoft.JScript.JScriptCodeProvider();
      CompilerParameters cparam = new CompilerParameters();
      cparam.GenerateInMemory = true;
      CompilerResults result = prov.CompileAssemblyFromSource(cparam, source);
      Assembly asm = result.CompiledAssembly;
      m_type = asm.GetType("Evaluator.Evaluator");
      m_evaluator = Activator.CreateInstance(m_type);
    }
    
    public object eval(string exp)
    {
      return m_type.InvokeMember("Eval",
        BindingFlags.InvokeMethod,
        null,
        m_evaluator,
        new object[] {exp});
    }
  }
}

このコード(jseval.exe)に対して、いろいろと引数を渡してみる。

>jseval 1+3
System.Int32: 4

>jseval "'hello, javascript!'"
System.String: hello, javascript!

順当すぎる結果。じゃぁ、

>jseval "var v=3;v"
System.Int32: 3

おもしろいのは、

>jseval 11111111111111111
System.Int64: 11111111111111111

自動的に64bit値になっている。調子に乗ってみる。

>jseval 1111111111111111111111111111111111111111111
System.Double: 1.11111111111111E+42

自動的にdoubleになるのか。だとすれば何でもできるな。

>jseval false
System.Boolean: False

ブーリアン値はboolにちゃんとマップされる。

>jseval [1,2,3,4]
Microsoft.JScript.ArrayObject: 1,2,3,4

素敵なことに配列もちゃんと渡ってくる。優秀じゃないか。

>jseval  "var v = {hoge: 1, foo: 2, bar: 3};"
Microsoft.JScript.JSObject: [object Object]

連想配列のパースは面倒かもしれないけど、これはうまくやればうまく使えそう。

調子に乗って、関数を定義してみたら、

>jseval  "function test() {return 5;}"


ハンドルされていない例外: System.Reflection.TargetInvocationException: 呼び出しのターゲットが例外をスローしました。 ---> System.Security.SecurityException: 型 'System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture
=neutral, PublicKeyToken=b77a5c561934e089' のアクセス許可の要求に失敗しました。
   場所 System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
   場所 System.Security.CodeAccessPermission.Demand()
   場所 Microsoft.JScript.JSParser.ParseFunction(FieldAttributes visibilitySpec, Boolean inExpression, Context fncCtx, Boolean isMethod, Boolean isAbstract, Boolean isFinal, Boolean isInterface, CustomAttributeList customAttributes, Call fu
nction)
   場所 Microsoft.JScript.JSParser.ParseFunction(FieldAttributes visibilitySpec, Boolean inExpression, Context fncCtx, Boolean isMethod, Boolean isAbstract, Boolean isFinal, Boolean isInterface, CustomAttributeList customAttributes)
   場所 Microsoft.JScript.JSParser.ParseStatement()
   場所 Microsoft.JScript.JSParser.ParseStatements(Boolean insideEval)
   場所 Microsoft.JScript.Eval.DoEvaluate(Object source, VsaEngine engine, Boolean isUnsafe)
   場所 Evaluator.Evaluator.Eval(String expr)
失敗した操作:
Demand
失敗した最初のアクセス許可の種類:
System.Security.Permissions.SecurityPermission
失敗した最初のアクセス許可:
<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Flags="UnmanagedCode"/>

要求の対象:
<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Flags="UnmanagedCode"/>

与えられたアクセス許可:
<PermissionSet class="System.Security.PermissionSet"
version="1">
<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Flags="Execution"/>
</PermissionSet>

失敗の原因になったメソッド:
System.Object DoEvaluate(System.Object, Microsoft.JScript.Vsa.VsaEngine, Boolean)
   --- 内部例外スタック トレースの終わり ---
   場所 System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
   場所 System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
   場所 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   場所 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   場所 System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
   場所 SimpleParser.SimpleParser.Main(String[] args)

まぁ、さすがにねぇって感じ。