C#<->JScript: невидимый массив?
У меня есть немного сложная программа, которая дает мне эту, по-видимому, фантомную ошибку...
Я начну объяснять с помощью этого небольшого примера программы, которую я настроил, которая может бросить мое прекрасное исключение для всех, кто ее запускает.
<!-- language: c# -->
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace so_redux {
static class Program {
[STAThread]
static void Main(){
CS2JS _class=new CS2JS();
}
}//class Program
class CS2JS{
public CS2JS(){
Func<String,Object> js_eval=initJS();
Object cs_ic=initCS();
string xc;
object res;
cs_ic.GetType().GetMethod("init").Invoke(cs_ic,null);
AppDomain.CurrentDomain.SetData("trespasser",cs_ic);
xc=@"function edata(fieldname:String,ival:String):Object{
var sob=System.AppDomain.CurrentDomain.GetData('trespasser');
var v1=sob.GetType().GetField(fieldname).GetValue(sob);
function HASH(s1:String,s2:String):Object{
var q1=sob.GetType().GetField(s1).GetValue(sob);
return q1.ITEM(s2);
}
var v2=v1.ITEM(ival);
return eval(v2);
}
edata('HT','foo');";
res=js_eval(xc);
// var xx;xx=new Hashtable();xx['sda']='1';eval(xx['sda']); OK
}
Func<String,Object> initJS(){
System.CodeDom.Compiler.CodeDomProvider jcc;
System.CodeDom.Compiler.CompilerParameters jcp;
System.CodeDom.Compiler.CompilerResults jcr;
System.Reflection.Assembly jas;
object jis;
string code;
Type t_ii;
code=@"@set @debug(off)
import System;
import System.Collections;
import System.Collections.Generic;
package internal_namespace{class internal_class{
public function internal_method(_code:String):Object{
return eval(_code);
}
}
}";
jcc=Microsoft.JScript.JScriptCodeProvider.CreateProvider("JScript");
jcp=new System.CodeDom.Compiler.CompilerParameters();
jcp.CompilerOptions="/fast-";
jcp.GenerateExecutable=false;
jcp.GenerateInMemory=true;
jcp.IncludeDebugInformation=false;
jcp.ReferencedAssemblies.Add("System.dll");
jcp.ReferencedAssemblies.Add("System.Core.dll");
jcp.WarningLevel=4;
jcr=jcc.CompileAssemblyFromSource(jcp,code);
jas=jcr.CompiledAssembly;
jis=jas.CreateInstance("internal_namespace.internal_class");
t_ii=jas.GetType("internal_namespace.internal_class");
return (s1)=>t_ii.InvokeMember("internal_method",System.Reflection.BindingFlags.InvokeMethod,null,jis,new object[]{s1});
}//initJS
Object initCS(){
var v1= Microsoft.CSharp.CSharpCodeProvider.CreateProvider("CSharp");
System.CodeDom.Compiler.CompilerParameters v2=new System.CodeDom.Compiler.CompilerParameters();
v2.GenerateExecutable=false;
v2.GenerateInMemory=true;
v2.IncludeDebugInformation=false;
v2.WarningLevel=4;
v2.ReferencedAssemblies.Add("System.dll");
v2.ReferencedAssemblies.Add("System.Core.dll");
string _code="public Hashtable2 HT;"+
"public void init(){"+
"HT=new Hashtable2();"+
"HT[\"foo\"]=\"1\";"+
"HT[\"bar\"]=\"HASH('HT','foo')\";"+
"}";
var v3="using System;using System.Collections;using System.Collections.Generic;"+
"namespace internal_namespace{public class Hashtable2:Hashtable{"+
"public Hashtable2():base(){} public Hashtable2(int N):base(N){}"+
"public Object ITEM(Object K){return this[K];} }"+
"[Serializable]public class internal_class{"+
"public internal_class(){}"+
_code+
"\n}}";
var v4=v1.CompileAssemblyFromSource(v2,v3);
var v5=v4.CompiledAssembly;
var v6=v5.GetType("internal_namespace.internal_class");
var v7=v5.CreateInstance("internal_namespace.internal_class");
return v7;
}//initCS
}//class CS2JS
}//namespace so_redux
Исключение, которое выдается, является "индексом за пределами" и генерируется из JScript. Эта проблема? Дело в том, что нет массива!
Что делает этот код: сначала инициализируется интерпретатор JScript путем компиляции во время выполнения класса, который "экспортирует" eval (можно было бы сделать dll, но в этом случае я этого не сделал).
Затем компилируется сборка C#, которая "экспортирует" некоторый пользовательский код (переменная _code в initCS изначально загружается путем чтения текстового файла).
После инициализации вновь скомпилированного класса (вызова init()) мне нужно, чтобы две сборки (JScript и C#) взаимодействовали, поэтому мне нужно передать данные между ними, и я подумал об использовании AppDomain.
Примечание: в сборке C# Hashtable2 определен, потому что я поместил туда метод ITEM, который можно использовать в качестве альтернативы общему свойству this[]: таким образом отладка проще (например, при показе святого MessageBox при доступе к значениям)).
Итак, я передаю класс, созданный в initCS, в JScript, и JScript читает внутреннюю Hashtable (HT). Что мне нужно сделать, это оценить данные в Hashtable, потому что он должен быть в состоянии динамически изменять себя.
Все работает нормально, если я получаю строку, не взятую из Hashtable, - в тот момент, когда я беру все, что находится в Hashtable, и передаю ее в eval, происходят исключения. Внимание: строки абсолютно одинаковы (даже сравнивая их с Equals) и они работают, разница только в том, откуда они берутся.
Когда функция JScript edata обнаруживает строку, взятую из Hashtable, JScript говорит "индекс за пределами", но, как я уже говорил, я не вижу там никакого массива... (и, возможно, проблема именно в этом, не знаю),
Я признаю, что у меня есть свои ограничения в JScript, поэтому, если кто-нибудь сможет помочь, чтобы понять, что происходит WTF, я был бы очень счастлив.
1 ответ
Временное решение / обходной путь, разделяющий функцию JScript:
xc=@"function odata(fieldname:String,ival:String):Object{
var sob=System.AppDomain.CurrentDomain.GetData('trespasser');
var v1=sob.GetType().GetField(fieldname).GetValue(sob);
return v1.ITEM(ival);
}
odata('HT','bar');";
res=js_eval(xc);
xc=@"function HASH(s1:String,s2:String):Object{
var sob=System.AppDomain.CurrentDomain.GetData('trespasser');
var q1=sob.GetType().GetField(s1).GetValue(sob);
return q1.ITEM(s2);
}
eval("+res.ToString()+");";
res=js_eval(xc);
но если кто-то действительно понял, почему это неправильно в первом примере, пожалуйста, объясните мне!