using System; using System.Collections.Generic; using TLang.BytecodeCompiler; using TLang.Common; namespace TLang.VM { public class ChunkExecuter { public string ClassName {get;set;}=""; public ChunkExecuter(TVMFile file,Chunk chunk,TLangEnvironment env) { File =file; Chunk = chunk; Environment = env.GetSubEnvironment(); } public TLangEnvironment Environment {get;set;} public TVMFile File {get;} public Chunk Chunk {get;} public int IP {get;set;}=0; public Stack Stack {get;}=new Stack(); public TObject Execute(params TObject[] args) { for(int i = 0;i< args.Length;i++) { if(i= File.Chunks.Count) throw new IndexOutOfRangeException($"No chunk with id {closureId}"); Stack.Push(new TClosure(File,File.Chunks[closureId],Environment){ClassName = ClassName}); break; case Instruction.PUSH_STRING: Stack.Push(new TString(ReadString())); break; case Instruction.PUSH_VARIABLE_VALUE: { var res=EnsurePop(); Stack.Push(Environment.GetObject(res.Value)); } break; case Instruction.PUSH_FIELD_VALUE: { var res= EnsurePop(); var symbol = Stack.Pop(); Stack.Push(GetFieldValue(symbol,res.Value)); } break; case Instruction.POP_FIELD_VALUE: { var val = Stack.Pop(); var res= EnsurePop(); var symbol = Stack.Pop(); SetFieldValue(symbol, res.Value, val); } break; case Instruction.POP_VARIABLE_VALUE: { var val = Stack.Pop(); var res=EnsurePop(); Environment.SetObject(res.Value,val); } break; case Instruction.NOP: break; case Instruction.PUSH_CHAR: Stack.Push(new TChar((char)Chunk.Code[IP++])); break; case Instruction.PUSH_ARRAY_VALUE: { var expr = Stack.Pop(); var symbol = Stack.Pop(); Stack.Push(GetArrayValue(symbol,expr)); } break; case Instruction.POP_ARRAY_VALUE: { var val = Stack.Pop(); var expr = Stack.Pop(); var symbol = Stack.Pop(); SetArrayValue(symbol,expr,val); } break; case Instruction.PUSH_UNDEFINED: Stack.Push(new TUndefined()); break; case Instruction.PUSH_NULL: Stack.Push(new TNull()); break; case Instruction.PUSH_TRUE: Stack.Push(new TBool(true)); break; case Instruction.PUSH_FALSE: Stack.Push(new TBool(false)); break; case Instruction.CALL_FUNC: { var argC = (int)EnsurePop().Value; TObject[] argV = new TObject[argC]; for(int i = argC;i>0;i--) { argV[i-1] = Stack.Pop(); } var name = EnsurePop(); var callable=Environment.GetObject(name.Value) as TCallable; if(callable != null) { Stack.Push(callable.Execute(argV)); }else{ Stack.Push(new TUndefined()); } } break; case Instruction.CALL_METHOD: { var argC = (int)EnsurePop().Value; TObject[] argV = new TObject[argC]; for(int i = argC;i>0;i--) { argV[i-1] = Stack.Pop(); } var name = EnsurePop(); var symbol = Stack.Pop(); Stack.Push(ExecuteMethod(symbol,name.Value,argV)); } break; case Instruction.JMP: IP=ReadInt(); break; case Instruction.JMPC: { int jmpPos = ReadInt(); if(Stack.Pop().True) IP = jmpPos; } break; case Instruction.LNOT: { var res=Stack.Pop(); Stack.Push(LNot(res)); } break; case Instruction.POP: Stack.Pop(); break; } } if(Stack.Count > 0) return Stack.Pop(); return new TNull(); } private TObject SetArrayValue(TObject symbol,TObject expr,TObject value) { var classMethod = symbol as TClassInstance; var dict = symbol as TDictionary; var array = symbol as TArray; if(array != null) { var index = expr as TNumber; if(index != null && ((int)index.Value) < array.Items.Count) { array.Items[(int)index.Value] = value; return new TUndefined(); } } if(classMethod != null) { if(classMethod.MethodExists(ClassName,"iset")) { return classMethod.CallMethod(ClassName,"iset",expr,value); } var index = expr as TString; if(index != null) { classMethod.SetField(ClassName,index.Value,value); return new TUndefined(); } } if(dict != null) { if(dict.HasValue("iset")) { var iget = dict.GetValue("iset") as TCallable; if(iget != null) { return iget.Execute(expr,value); } var index = expr as TString; if(index != null) dict.SetValue(index.Value,value); } } return new TUndefined(); } private TObject GetArrayValue(TObject symbol, TObject expr) { var classMethod = symbol as TClassInstance; var dict = symbol as TDictionary; var str = symbol as TString; var array = symbol as TArray; if(str != null) { var index = expr as TNumber; if(index != null && ((int)index.Value) < str.Value.Length) { return new TChar(str.Value[(int)index.Value]); } } if(array != null) { var index = expr as TNumber; if(index != null && ((int)index.Value) < array.Items.Count) { return array.Items[(int)index.Value]; } } if(classMethod != null) { if(classMethod.MethodExists(ClassName,"iget")) { return classMethod.CallMethod(ClassName,"iget",expr); } var index = expr as TString; if(index != null) { return classMethod.GetField(ClassName,index.Value); } } if(dict != null) { if(dict.HasValue("iget")) { var iget = dict.GetValue("iget") as TCallable; if(iget != null) { return iget.Execute(expr); } var index = expr as TString; if(index != null) return dict.GetValue(index.Value); } } return new TUndefined(); } private TObject LNot(TObject res) { var classMethod = res as TClassInstance; var dict = res as TDictionary; if(classMethod != null) { return classMethod.CallMethod(ClassName,"lnot",res); } if(dict != null) { var callable= dict.GetValue("lnot") as TCallable; if(callable != null) { return callable.Execute(res); } } return new TBool(!res.True); } private TObject Negative(TObject object1) { var left = object1 as TNumber; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null) { return new TNumber(-(left.Value)); } if(lcls != null && lcls.MethodExists(ClassName,"neg")) { return lcls.CallMethod(ClassName,"neg"); } if(ldict != null) { var callable =ldict.GetValue("neg") as TCallable; if(callable != null) return callable.Execute(); } return new TNumber(0); } private TObject BNot(TObject object1) { var left = object1 as TNumber; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null) { return new TNumber(~((long)left.Value)); } if(lcls != null && lcls.MethodExists(ClassName,"band")) { return lcls.CallMethod(ClassName,"bnot"); } if(ldict != null) { var callable =ldict.GetValue("bnot") as TCallable; if(callable != null) return callable.Execute(); } return new TNumber(0); } private TObject GetFieldValue(TObject symbol, string name) { var classField = symbol as TClassInstance; var dict = symbol as TDictionary; var tArray = symbol as TArray; var tString = symbol as TString; if(tArray != null) { if(name == "count") { return new TNumber(tArray.Items.Count); } if(name == "empty") { return new TBool(!tArray.True); } if(name == "ittr") { return TDictionary.FromIEnumerator(tArray.Items.GetEnumerator()); } } if(tString != null) { if(name == "count") { return new TNumber(tString.Value.Length); } if(name == "empty") { return new TBool(tString.Value.Length == 0); } if(name == "ittr") { return TDictionary.FromIEnumerator(tString.GetObjects().GetEnumerator()); } } if(classField != null) { if(classField.MethodExists(ClassName,$"get{name}")) { return classField.CallMethod(ClassName,$"get{name}"); } return classField.GetField(ClassName,name); } if(dict != null) { var callable= dict.GetValue($"get{name}") as TCallable; if(callable != null) { return callable.Execute(); } else { return dict.GetValue(name); } } return new TUndefined(); } public void SetFieldValue(TObject symbol, string name,TObject newValue) { var classField = symbol as TClassInstance; var dict = symbol as TDictionary; if(classField != null) { if(classField.MethodExists(ClassName,$"set{name}")) { classField.CallMethod(ClassName,$"set{name}",newValue); } else { classField.SetField(ClassName,name,newValue); } } if(dict != null) { var callable= dict.GetValue($"set{name}") as TCallable; if(callable != null) { callable.Execute(newValue); } else { dict.SetValue(name,newValue); } } } private TObject ExecuteMethod(TObject symbol, string name, params TObject[] argV) { if(name == "toString" && argV.Length == 0) return new TString(symbol.ToString()); var classMethod = symbol as TClassInstance; var number = symbol as TNumber; var dict = symbol as TDictionary; var tArray = symbol as TArray; var tString = symbol as TString; if(tString != null) { if(name == "count") { return new TNumber(tString.Value.Length); } if(name == "empty") { return new TBool(tString.Value.Length == 0); } var strP=Environment.GetObject("String") as TDictionary; if(strP != null) { var func = strP.GetValue(name) as TCallable; if(func != null) { List arguments = new List(); arguments.Add(symbol); arguments.AddRange(argV); return func.Execute(arguments.ToArray()); } } } if(classMethod != null) { return classMethod.CallMethod(ClassName,name,argV); } if(dict != null) { var callable= dict.GetValue(name) as TCallable; if(callable != null) { return callable.Execute(argV); } } if(tArray != null) { if(name == "add") { if(argV.Length > 0) { tArray.Add(argV[0]); } return tArray; } if(name == "remove") { if(argV.Length > 0) { tArray.Remove(argV[0]); } return tArray; } if(name == "removeat") { if(argV.Length > 0) { var num = argV[0] as TNumber; if(num != null) { tArray.Items.RemoveAt((int)num.Value); } } return tArray; } if(name == "clear") { tArray.Items.Clear(); return tArray; } if(name == "count") { return new TNumber(tArray.Items.Count); } if(name == "empty") { return new TBool(!tArray.True); } var strP=Environment.GetObject("Array") as TDictionary; if(strP != null) { var func = strP.GetValue(name) as TCallable; if(func != null) { List arguments = new List(); arguments.Add(symbol); arguments.AddRange(argV); return func.Execute(arguments.ToArray()); } } } if(number != null) { if(name == "floor") { return new TNumber(Math.Floor(number.Value)); } if(name == "ceil") { return new TNumber(Math.Ceiling(number.Value)); } if(name == "round") { if(argV.Length > 0) { var n = argV[0] as TNumber; if(n !=null) return new TNumber(Math.Round(number.Value,(int)n.Value)); } return new TNumber(Math.Round(number.Value)); } if(name == "toChar") { return new TChar((char)(int)number.Value); } var strP=Environment.GetObject("Number") as TDictionary; if(strP != null) { var func = strP.GetValue(name) as TCallable; if(func != null) { List arguments = new List(); arguments.Add(symbol); arguments.AddRange(argV); return func.Execute(arguments.ToArray()); } } return new TNull(); } return new TUndefined(); } private T EnsurePop() where T : TObject { var item = Stack.Pop() as T; if(item == null) throw new Exception("Pop failed"); return item; } private double ReadDouble() { if(IP + 8 <= Chunk.Code.Length) { byte[] data =new byte[8]; Array.Copy(Chunk.Code,IP,data,0,8); IP += 8; if(BitConverter.IsLittleEndian) Array.Reverse(data); return BitConverter.ToDouble(data,0); } throw new Exception("Malformed binary"); } private int ReadInt() { if(IP + 4 <= Chunk.Code.Length) { byte[] data =new byte[4]; Array.Copy(Chunk.Code,IP,data,0,4); IP += 4; if(BitConverter.IsLittleEndian) Array.Reverse(data); return BitConverter.ToInt32(data,0); } throw new Exception("Malformed binary"); } private string ReadString() { int strLen = ReadInt(); if(IP + strLen <= Chunk.Code.Length) { var str=System.Text.Encoding.UTF8.GetString(Chunk.Code,IP,strLen); IP += strLen; return str; } throw new Exception("Malformed binary"); } private TObject BAnd(TObject object2, TObject object1) { var left = object1 as TNumber; var right = object2 as TNumber; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null && right != null) { return new TNumber((long)left.Value & (long)right.Value); } if(lcls != null && lcls.MethodExists(ClassName,"band")) { return lcls.CallMethod(ClassName,"band",object2); } if(ldict != null) { var callable =ldict.GetValue("band") as TCallable; if(callable != null) return callable.Execute(object2); } return new TNumber(0); } private TObject BOr(TObject object2, TObject object1) { var left = object1 as TNumber; var right = object2 as TNumber; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null && right != null) { return new TNumber((long)left.Value | (long)right.Value); } if(lcls != null && lcls.MethodExists(ClassName,"bor")) { return lcls.CallMethod(ClassName,"bor",object2); } if(ldict != null) { var callable =ldict.GetValue("bor") as TCallable; if(callable != null) return callable.Execute(object2); } return new TNumber(0); } private TObject NEq(TObject object2, TObject object1) { var left = object1 as TNumber; var right = object2 as TNumber; var lStr = object1 as TString; var rStr = object2 as TString; var lChr = object1 as TChar; var rChr = object2 as TChar; var lBool = object1 as TBool; var rBool = object2 as TBool; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null && right != null) { return new TBool(left.Value != right.Value); } if(lStr != null && rStr != null) { return new TBool(lStr.Value != lStr.Value); } if(lChr != null && rChr != null) { return new TBool(lChr.Value != lChr.Value); } if(lBool != null && rBool != null) { return new TBool(lBool.Value != lBool.Value); } if(lcls != null && lcls.MethodExists(ClassName,"neq")) { return lcls.CallMethod(ClassName,"neq",object2); } if(ldict != null) { var callable =ldict.GetValue("neq") as TCallable; if(callable != null) return callable.Execute(object2); } return new TBool(object1 != object2); } private TObject Eq(TObject object2, TObject object1) { var left = object1 as TNumber; var right = object2 as TNumber; var lStr = object1 as TString; var rStr = object2 as TString; var lChr = object1 as TChar; var rChr = object2 as TChar; var lBool = object1 as TBool; var rBool = object2 as TBool; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null && right != null) { return new TBool(left.Value == right.Value); } if(lStr != null && rStr != null) { return new TBool(lStr.Value == lStr.Value); } if(lChr != null && rChr != null) { return new TBool(lChr.Value == lChr.Value); } if(lBool != null && rBool != null) { return new TBool(lBool.Value == lBool.Value); } if(lcls != null && lcls.MethodExists(ClassName,"eq")) { return lcls.CallMethod(ClassName,"eq",object2); } if(ldict != null) { var callable =ldict.GetValue("eq") as TCallable; if(callable != null) return callable.Execute(object2); } return new TBool(object1 == object2); } private TObject RShift(TObject object2, TObject object1) { var left = object1 as TNumber; var right = object2 as TNumber; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null && right != null) { return new TNumber((long)left.Value >> (int)right.Value); } if(lcls != null && lcls.MethodExists(ClassName,"rs")) { return lcls.CallMethod(ClassName,"rs",object2); } if(ldict != null) { var callable =ldict.GetValue("rs") as TCallable; if(callable != null) return callable.Execute(object2); } return new TNumber(0); } private TObject LShift(TObject object2, TObject object1) { var left = object1 as TNumber; var right = object2 as TNumber; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null && right != null) { return new TNumber((long)left.Value << (int)right.Value); } if(lcls != null && lcls.MethodExists(ClassName,"ls")) { return lcls.CallMethod(ClassName,"ls",object2); } if(ldict != null) { var callable =ldict.GetValue("ls") as TCallable; if(callable != null) return callable.Execute(object2); } return new TNumber(0); } private TObject XOr(TObject object2, TObject object1) { var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(lcls != null && lcls.MethodExists(ClassName,"lte")) { return lcls.CallMethod(ClassName,"lte",object2); } if(ldict != null) { var callable =ldict.GetValue("lte") as TCallable; if(callable != null) return callable.Execute(object2); } return new TBool(object1.True ^ object2.True); } private TObject LOr(TObject object2, TObject object1) { var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(lcls != null && lcls.MethodExists(ClassName,"lor")) { return lcls.CallMethod(ClassName,"lor",object2); } if(ldict != null) { var callable =ldict.GetValue("lor") as TCallable; if(callable != null) return callable.Execute(object2); } return new TBool(object1.True || object2.True); } private TObject LAnd(TObject object2, TObject object1) { var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(lcls != null && lcls.MethodExists(ClassName,"land")) { return lcls.CallMethod(ClassName,"land",object2); } if(ldict != null) { var callable =ldict.GetValue("land") as TCallable; if(callable != null) return callable.Execute(object2); } return new TBool(object1.True && object2.True); } private TObject Pow(TObject object1, TObject object2) { var left = object1 as TNumber; var right = object2 as TNumber; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null && right != null) { return new TNumber(Math.Pow(left.Value,right.Value)); } if(lcls != null && lcls.MethodExists(ClassName,"pow")) { return lcls.CallMethod(ClassName,"pow",object2); } if(ldict != null) { var callable =ldict.GetValue("pow") as TCallable; if(callable != null) return callable.Execute(object2); } return new TNumber(0); } private TObject Mod(TObject object1, TObject object2) { var left = object1 as TNumber; var right = object2 as TNumber; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null && right != null) { return new TNumber(left.Value % right.Value); } if(lcls != null && lcls.MethodExists(ClassName,"mod")) { return lcls.CallMethod(ClassName,"mod",object2); } if(ldict != null) { var callable =ldict.GetValue("mod") as TCallable; if(callable != null) return callable.Execute(object2); } return new TNumber(0); } private TObject Divide(TObject object1, TObject object2) { var left = object1 as TNumber; var right = object2 as TNumber; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null && right != null) { return new TNumber(left.Value / right.Value); } if(lcls != null && lcls.MethodExists(ClassName,"div")) { return lcls.CallMethod(ClassName,"div",object2); } if(ldict != null) { var callable =ldict.GetValue("div") as TCallable; if(callable != null) return callable.Execute(object2); } return new TNumber(0); } private TObject Times(TObject object1, TObject object2) { var left = object1 as TNumber; var right = object2 as TNumber; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null && right != null) { return new TNumber(left.Value + right.Value); } if(lcls != null && lcls.MethodExists(ClassName,"times")) { return lcls.CallMethod(ClassName,"times",object2); } if(ldict != null) { var callable =ldict.GetValue("times") as TCallable; if(callable != null) return callable.Execute(object2); } return new TNumber(0); } private TObject Sub(TObject object2, TObject object1) { var left = object1 as TNumber; var right = object2 as TNumber; var lStr = object1 as TString; var rStr = object2 as TString; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; var lArray = object1 as TArray; if(left != null && right != null) { return new TNumber(left.Value - right.Value); } if(lcls != null && lcls.MethodExists(ClassName,"sub")) { return lcls.CallMethod(ClassName,"sub",object2); } if(ldict != null) { var callable =ldict.GetValue("sub") as TCallable; if(callable != null) return callable.Execute(object2); } if(lArray != null) { lArray.Remove(object2); return lArray; } return new TNumber(0); } private TObject GTE(TObject object2, TObject object1) { var left = object1 as TNumber; var right = object2 as TNumber; var lStr = object1 as TString; var rStr = object2 as TString; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null && right != null) { return new TBool(left.Value >= right.Value); } if(lcls != null && lcls.MethodExists(ClassName,"gte")) { return lcls.CallMethod(ClassName,"gte",object2); } if(ldict != null) { var callable =ldict.GetValue("gte") as TCallable; if(callable != null) return callable.Execute(object2); } return new TNumber(0); } private TObject GT(TObject object2, TObject object1) { var left = object1 as TNumber; var right = object2 as TNumber; var lStr = object1 as TString; var rStr = object2 as TString; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null && right != null) { return new TBool(left.Value > right.Value); } if(lcls != null && lcls.MethodExists(ClassName,"gt")) { return lcls.CallMethod(ClassName,"gt",object2); } if(ldict != null) { var callable =ldict.GetValue("gt") as TCallable; if(callable != null) return callable.Execute(object2); } return new TNumber(0); } private TObject LTE(TObject object2, TObject object1) { var left = object1 as TNumber; var right = object2 as TNumber; var lStr = object1 as TString; var rStr = object2 as TString; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null && right != null) { return new TBool(left.Value <= right.Value); } if(lcls != null && lcls.MethodExists(ClassName,"lte")) { return lcls.CallMethod(ClassName,"lte",object2); } if(ldict != null) { var callable =ldict.GetValue("lte") as TCallable; if(callable != null) return callable.Execute(object2); } return new TNumber(0); } private TObject LT(TObject object2, TObject object1) { var left = object1 as TNumber; var right = object2 as TNumber; var lStr = object1 as TString; var rStr = object2 as TString; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; if(left != null && right != null) { return new TBool(left.Value < right.Value); } if(lcls != null && lcls.MethodExists(ClassName,"lt")) { return lcls.CallMethod(ClassName,"lt",object2); } if(ldict != null) { var callable =ldict.GetValue("lt") as TCallable; if(callable != null) return callable.Execute(object2); } return new TNumber(0); } private TObject Add(TObject object2, TObject object1) { var left = object1 as TNumber; var right = object2 as TNumber; var lStr = object1 as TString; var rStr = object2 as TString; var lcls = object1 as TClassInstance; var ldict = object1 as TDictionary; var lArray = object1 as TArray; if(left != null && right != null) { return new TNumber(left.Value + right.Value); } if(lStr != null && rStr != null) { return new TString(lStr.Value + rStr.Value); } if(lcls != null && lcls.MethodExists(ClassName,"add")) { return lcls.CallMethod(ClassName,"add",object2); } if(ldict != null) { var callable =ldict.GetValue("add") as TCallable; if(callable != null) return callable.Execute(object2); } if(lArray != null) { lArray.Add(object2); return lArray; } return new TNumber(0); } } }