public void SetKey(VMObject key, VMObject obj) { Dictionary <VMObject, VMObject> children; // NOTE: here we need to instantiate the key as new object // otherwise keeping the key in a register allows modifications to it that also affect the dictionary keys var temp = new VMObject(); temp.Copy(key); key = temp; if (this.Type == VMType.Struct) { children = GetChildren(); } else if (this.Type == VMType.None) { this.Type = VMType.Struct; children = new Dictionary <VMObject, VMObject>(); this.Data = children; this._localSize = 0; } else { throw new Exception($"Invalid cast from {this.Type} to struct"); } var result = new VMObject(); children[key] = result; result.Copy(obj); }
public static VMObject FromObject(object obj) { var type = GetVMType(obj.GetType()); Throw.If(type == VMType.None, "not a valid object"); var result = new VMObject(); switch (type) { case VMType.Bool: result.SetValue((bool)obj); break; case VMType.Bytes: result.SetValue((byte[])obj, VMType.Bytes); break; case VMType.String: result.SetValue((string)obj); break; case VMType.Number: result.SetValue((BigInteger)obj); break; case VMType.Enum: result.SetValue((Enum)obj); break; case VMType.Object: result.SetValue(obj); break; default: return(null); } return(result); }
internal void Copy(VMObject other) { if (other == null || other.Type == VMType.None) { this.Type = VMType.None; this.Data = null; return; } this.Type = other.Type; if (other.Type == VMType.Struct) { var children = new Dictionary <VMObject, VMObject>(); var otherChildren = other.GetChildren(); foreach (var key in otherChildren.Keys) { var temp = new VMObject(); temp.Copy(otherChildren[key]); children[key] = temp; } this.Data = children; } else { this.Data = other.Data; /*var temp = other.Data; * _data = new byte[temp.Length]; * Array.Copy(temp, _data, _data.Length);*/ } }
public static VMObject CastTo(VMObject srcObj, VMType type) { if (srcObj.Type == type) { var result = new VMObject(); result.Copy(srcObj); return(result); } switch (type) { case VMType.None: return(new VMObject()); case VMType.String: { var result = new VMObject(); result.SetValue(srcObj.AsString()); // TODO does this work for all types? return(result); } case VMType.Timestamp: { var result = new VMObject(); result.SetValue(srcObj.AsTimestamp()); // TODO does this work for all types? return(result); } case VMType.Bool: { var result = new VMObject(); result.SetValue(srcObj.AsBool()); // TODO does this work for all types? return(result); } case VMType.Bytes: { var result = new VMObject(); result.SetValue(srcObj.AsByteArray()); // TODO does this work for all types? return(result); } case VMType.Number: { var result = new VMObject(); result.SetValue(srcObj.AsNumber()); // TODO does this work for all types? return(result); } case VMType.Struct: switch (srcObj.Type) { case VMType.Object: return(CastViaReflection(srcObj.Data, 0)); default: throw new Exception($"invalid cast: {srcObj.Type} to {type}"); } default: throw new Exception($"invalid cast: {srcObj.Type} to {type}"); } }
public static VMObject FromBytes(byte[] bytes) { var result = new VMObject(); result.UnserializeData(bytes); return(result); }
public void UnserializeData(BinaryReader reader) { this.Type = (VMType)reader.ReadByte(); switch (this.Type) { case VMType.Bool: this.Data = Serialization.Unserialize <bool>(reader); break; case VMType.Bytes: this.Data = Serialization.Unserialize <byte[]>(reader); break; case VMType.Number: this.Data = Serialization.Unserialize <BigInteger>(reader); break; case VMType.Timestamp: this.Data = Serialization.Unserialize <Timestamp>(reader); break; case VMType.String: this.Data = Serialization.Unserialize <string>(reader); break; case VMType.Struct: var childCount = reader.ReadVarInt(); var children = new Dictionary <VMObject, VMObject>(); while (childCount > 0) { var key = new VMObject(); key.UnserializeData(reader); var val = new VMObject(); val.UnserializeData(reader); children[key] = val; childCount--; } this.Data = children; break; // NOTE object type information is lost during serialization, so we reconstruct it as byte array case VMType.Object: this.Type = VMType.Bytes; this.Data = reader.ReadByteArray(); break; case VMType.Enum: // TODO default: throw new Exception($"invalid unserialize: type {this.Type}"); } }
public static VMObject FromBytes(byte[] bytes) { using (var stream = new MemoryStream(bytes)) { using (var reader = new BinaryReader(stream)) { var result = new VMObject(); result.UnserializeData(reader); return(result); } } }
public ExecutionFrame(VirtualMachine VM, uint offset, ExecutionContext context, int registerCount) { this.VM = VM; this.Offset = offset; this.Context = context; Registers = new VMObject[registerCount]; for (int i = 0; i < registerCount; i++) { Registers[i] = new VMObject(); } }
public object ToStruct(Type structType) { Throw.If(Type != VMType.Struct, "not a valid source struct"); Throw.If(!structType.IsStructOrClass(), "not a valid destination struct"); var dict = this.GetChildren(); var fields = structType.GetFields(); var result = Activator.CreateInstance(structType); object boxed = result; foreach (var field in fields) { var key = VMObject.FromObject(field.Name); Throw.If(!dict.ContainsKey(key), "field not present in source struct: " + field.Name); var val = dict[key].ToObject(field.FieldType); // here we check if the types mismatch // in case of getting a byte[] instead of an object, we try unserializing the bytes in a different approach // NOTE this should not be necessary often, but is already getting into black magic territory... if (val != null && field.FieldType != typeof(byte[]) && val.GetType() == typeof(byte[])) { if (typeof(ISerializable).IsAssignableFrom(field.FieldType)) { var temp = (ISerializable)Activator.CreateInstance(field.FieldType); var bytes = (byte[])val; using (var stream = new MemoryStream(bytes)) { using (var reader = new BinaryReader(stream)) { temp.UnserializeData(reader); } } val = temp; } } // HACK allows treating uints as enums, without this it is impossible to transform between C# objects and VM objects if (field.FieldType.IsEnum && !val.GetType().IsEnum) { val = Enum.Parse(field.FieldType, val.ToString()); } field.SetValue(boxed, val); } return(boxed); }
public VMObject GetKey(VMObject key) { if (this.Type != VMType.Struct) { throw new Exception($"Invalid cast: expected struct, got {this.Type}"); } var children = GetChildren(); if (children.ContainsKey(key)) { return(children[key]); } return(new VMObject()); }
public static VMObject FromObject(object obj) { var objType = obj.GetType(); var type = GetVMType(objType); Throw.If(type == VMType.None, "not a valid object"); var result = new VMObject(); switch (type) { case VMType.Bool: result.SetValue((bool)obj); break; case VMType.Bytes: result.SetValue((byte[])obj, VMType.Bytes); break; case VMType.String: result.SetValue((string)obj); break; case VMType.Number: if (obj.GetType() == typeof(int)) { obj = new BigInteger((int)obj); // HACK } result.SetValue((BigInteger)obj); break; case VMType.Enum: result.SetValue((Enum)obj); break; case VMType.Object: result.SetValue(obj); break; case VMType.Timestamp: if (obj.GetType() == typeof(uint)) { obj = new Timestamp((uint)obj); // HACK } result.SetValue((Timestamp)obj); break; default: return(null); } return(result); }
public void SetKey(string key, VMObject obj) { Dictionary <string, VMObject> children; if (this.Type == VMType.Struct) { children = GetChildren(); } else { this.Type = VMType.Struct; children = new Dictionary <string, VMObject>(); this.Data = children; this._localSize = 0; } var result = new VMObject(); children[key] = result; result.Copy(obj); }
public object ToStruct(Type structType) { Throw.If(Type != VMType.Struct, "not a valid source struct"); Throw.If(!structType.IsStructOrClass(), "not a valid destination struct"); var dict = this.GetChildren(); var fields = structType.GetFields(); var result = Activator.CreateInstance(structType); object boxed = result; foreach (var field in fields) { var key = VMObject.FromObject(field.Name); Throw.If(!dict.ContainsKey(key), "field not present in source struct: " + field.Name); var val = dict[key].ToObject(field.FieldType); field.SetValue(boxed, val); } return(boxed); }
public void Step(ExecutionFrame frame, Stack <VMObject> stack) { try { var opcode = (Opcode)Read8(); frame.VM.ValidateOpcode(opcode); switch (opcode) { case Opcode.NOP: { break; } // args: byte src_reg, byte dest_reg case Opcode.MOVE: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); frame.Registers[dst] = frame.Registers[src]; break; } // args: byte src_reg, byte dest_reg case Opcode.COPY: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); frame.Registers[dst].Copy(frame.Registers[src]); break; } // args: byte dst_reg, byte type, var length, var data_bytes case Opcode.LOAD: { var dst = Read8(); var type = (VMType)Read8(); var len = (int)ReadVar(0xFFFF); Expect(dst < frame.Registers.Length); var bytes = ReadBytes(len); frame.Registers[dst].SetValue(bytes, type); break; } // args: byte src_reg, dst_reg, byte type case Opcode.CAST: { var src = Read8(); var dst = Read8(); var type = (VMType)Read8(); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); var val = frame.Registers[src]; val = VMObject.CastTo(val, type); frame.Registers[dst] = val; break; } // args: byte src_reg case Opcode.PUSH: { var src = Read8(); Expect(src < frame.Registers.Length); var val = frame.Registers[src]; var temp = new VMObject(); temp.Copy(val); stack.Push(temp); break; } // args: byte dest_reg case Opcode.POP: { var dst = Read8(); Expect(stack.Count > 0); Expect(dst < frame.Registers.Length); frame.Registers[dst] = stack.Pop(); break; } // args: byte src_reg, byte dest_reg case Opcode.SWAP: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); var temp = frame.Registers[src]; frame.Registers[src] = frame.Registers[dst]; frame.Registers[dst] = temp; break; } // args: ushort offset, byte regCount case Opcode.CALL: { var count = Read8(); var ofs = Read16(); Expect(ofs < this.Script.Length); Expect(count >= 1); Expect(count <= VirtualMachine.MaxRegisterCount); frame.VM.PushFrame(this, InstructionPointer, count); InstructionPointer = ofs; break; } // args: byte srcReg case Opcode.EXTCALL: using (var m = new ProfileMarker("EXTCALL")) { var src = Read8(); Expect(src < frame.Registers.Length); var method = frame.Registers[src].AsString(); var state = frame.VM.ExecuteInterop(method); if (state != ExecutionState.Running) { throw new VMException(frame.VM, "VM extcall failed: " + method); } break; } // args: ushort offset, byte src_reg // NOTE: JMP only has offset arg, not the rest case Opcode.JMP: case Opcode.JMPIF: case Opcode.JMPNOT: { bool shouldJump; if (opcode == Opcode.JMP) { shouldJump = true; } else { var src = Read8(); Expect(src < frame.Registers.Length); shouldJump = frame.Registers[src].AsBool(); if (opcode == Opcode.JMPNOT) { shouldJump = !shouldJump; } } var newPos = (short)Read16(); Expect(newPos >= 0); Expect(newPos < this.Script.Length); if (shouldJump) { InstructionPointer = (uint)newPos; } break; } // args: var length, var bytes case Opcode.THROW: { var len = (int)ReadVar(1024); if (len > 0) { var bytes = ReadBytes(len); } SetState(ExecutionState.Fault); return; } // args: none case Opcode.RET: { if (frame.VM.frames.Count > 1) { var temp = frame.VM.PeekFrame(); if (temp.Context == this) { InstructionPointer = frame.VM.PopFrame(); //Expect(InstructionPointer == this.Script.Length); } else { SetState(ExecutionState.Halt); } } else { SetState(ExecutionState.Halt); } return; } // args: byte src_a_reg, byte src_b_reg, byte dest_reg case Opcode.CAT: { var srcA = Read8(); var srcB = Read8(); var dst = Read8(); Expect(srcA < frame.Registers.Length); Expect(srcB < frame.Registers.Length); Expect(dst < frame.Registers.Length); var A = frame.Registers[srcA]; var B = frame.Registers[srcB]; if (!A.IsEmpty) { if (B.IsEmpty) { frame.Registers[dst].Copy(A); } else { var bytesA = A.AsByteArray(); var bytesB = B.AsByteArray(); var result = new byte[bytesA.Length + bytesB.Length]; Array.Copy(bytesA, result, bytesA.Length); Array.Copy(bytesB, 0, result, bytesA.Length, bytesB.Length); Expect(A.Type == B.Type); VMType type = A.Type; frame.Registers[dst].SetValue(result, type); } } else { if (B.IsEmpty) { frame.Registers[dst] = new VMObject(); } else { frame.Registers[dst].Copy(B); } } break; } case Opcode.SUBSTR: { throw new NotImplementedException(); } // args: byte src_reg, byte dest_reg, var length case Opcode.LEFT: { var src = Read8(); var dst = Read8(); var len = (int)ReadVar(0xFFFF); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); var src_array = frame.Registers[src].AsByteArray(); Expect(len <= src_array.Length); var result = new byte[len]; Array.Copy(src_array, result, len); frame.Registers[dst].SetValue(result, VMType.Bytes); break; } // args: byte src_reg, byte dest_reg, byte length case Opcode.RIGHT: { var src = Read8(); var dst = Read8(); var len = (int)ReadVar(0xFFFF); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); var src_array = frame.Registers[src].AsByteArray(); Expect(len <= src_array.Length); var ofs = src_array.Length - len; var result = new byte[len]; Array.Copy(src_array, ofs, result, 0, len); frame.Registers[dst].SetValue(result, VMType.Bytes); break; } // args: byte src_reg, byte dest_reg case Opcode.SIZE: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); var src_array = frame.Registers[src].AsByteArray(); frame.Registers[dst].SetValue(src_array.Length); break; } // args: byte src_reg, byte dest_reg case Opcode.COUNT: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); var val = frame.Registers[src]; int count; switch (val.Type) { case VMType.Struct: { var children = val.GetChildren(); count = children.Count; break; } default: count = 1; break; } frame.Registers[dst].SetValue(count); break; } // args: byte src_reg, byte dest_reg case Opcode.NOT: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); var val = frame.Registers[src].AsBool(); frame.Registers[dst].SetValue(!val); break; } // args: byte src_a_reg, byte src_b_reg, byte dest_reg case Opcode.AND: case Opcode.OR: case Opcode.XOR: { var srcA = Read8(); var srcB = Read8(); var dst = Read8(); Expect(srcA < frame.Registers.Length); Expect(srcB < frame.Registers.Length); Expect(dst < frame.Registers.Length); var a = frame.Registers[srcA].AsBool(); var b = frame.Registers[srcB].AsBool(); bool result; switch (opcode) { case Opcode.AND: result = (a && b); break; case Opcode.OR: result = (a || b); break; case Opcode.XOR: result = (a ^ b); break; default: { SetState(ExecutionState.Fault); return; } } frame.Registers[dst].SetValue(result); break; } // args: byte src_a_reg, byte src_b_reg, byte dest_reg case Opcode.EQUAL: { var srcA = Read8(); var srcB = Read8(); var dst = Read8(); Expect(srcA < frame.Registers.Length); Expect(srcB < frame.Registers.Length); Expect(dst < frame.Registers.Length); var a = frame.Registers[srcA]; var b = frame.Registers[srcB]; var result = a.Equals(b); frame.Registers[dst].SetValue(result); break; } // args: byte src_a_reg, byte src_b_reg, byte dest_reg case Opcode.LT: case Opcode.GT: case Opcode.LTE: case Opcode.GTE: { var srcA = Read8(); var srcB = Read8(); var dst = Read8(); Expect(srcA < frame.Registers.Length); Expect(srcB < frame.Registers.Length); Expect(dst < frame.Registers.Length); var a = frame.Registers[srcA].AsNumber(); var b = frame.Registers[srcB].AsNumber(); bool result; switch (opcode) { case Opcode.LT: result = (a < b); break; case Opcode.GT: result = (a > b); break; case Opcode.LTE: result = (a <= b); break; case Opcode.GTE: result = (a >= b); break; default: { SetState(ExecutionState.Fault); return; } } frame.Registers[dst].SetValue(result); break; } // args: byte reg case Opcode.INC: { var dst = Read8(); Expect(dst < frame.Registers.Length); var val = frame.Registers[dst].AsNumber(); frame.Registers[dst].SetValue(val + 1); break; } // args: byte reg case Opcode.DEC: { var dst = Read8(); Expect(dst < frame.Registers.Length); var val = frame.Registers[dst].AsNumber(); frame.Registers[dst].SetValue(val - 1); break; } // args: byte src_reg, byte dest_reg case Opcode.SIGN: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); var val = frame.Registers[src].AsNumber(); if (val == 0) { frame.Registers[dst].SetValue(BigInteger.Zero); } else { frame.Registers[dst].SetValue(val < 0 ? -1 : 1); } break; } // args: byte src_reg, byte dest_reg case Opcode.NEGATE: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); var val = frame.Registers[src].AsNumber(); frame.Registers[dst].SetValue(-val); break; } // args: byte src_reg, byte dest_reg case Opcode.ABS: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); var val = frame.Registers[src].AsNumber(); frame.Registers[dst].SetValue(val < 0 ? -val : val); break; } // args: byte src_a_reg, byte src_b_reg, byte dest_reg case Opcode.ADD: case Opcode.SUB: case Opcode.MUL: case Opcode.DIV: case Opcode.MOD: case Opcode.SHR: case Opcode.SHL: case Opcode.MIN: case Opcode.MAX: { var srcA = Read8(); var srcB = Read8(); var dst = Read8(); Expect(srcA < frame.Registers.Length); Expect(srcB < frame.Registers.Length); Expect(dst < frame.Registers.Length); var a = frame.Registers[srcA].AsNumber(); var b = frame.Registers[srcB].AsNumber(); BigInteger result; switch (opcode) { case Opcode.ADD: result = a + b; break; case Opcode.SUB: result = a - b; break; case Opcode.MUL: result = a * b; break; case Opcode.DIV: result = a / b; break; case Opcode.MOD: result = a % b; break; case Opcode.SHR: result = a >> (int)b; break; case Opcode.SHL: result = a << (int)b; break; case Opcode.MIN: result = a < b ? a : b; break; case Opcode.MAX: result = a > b ? a : b; break; default: { SetState(ExecutionState.Fault); return; } } frame.Registers[dst].SetValue(result); break; } // args: byte src_reg, byte dest_reg, byte key case Opcode.PUT: { var src = Read8(); var dst = Read8(); var keyReg = Read8(); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); Expect(keyReg < frame.Registers.Length); var key = frame.Registers[keyReg]; var value = frame.Registers[src]; frame.Registers[dst].SetKey(key, value); break; } // args: byte src_reg, byte dest_reg, byte key case Opcode.GET: { var src = Read8(); var dst = Read8(); var keyReg = Read8(); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); Expect(keyReg < frame.Registers.Length); var key = frame.Registers[keyReg]; var val = frame.Registers[src].GetKey(key); frame.Registers[dst] = val; break; } // args: byte dest_reg case Opcode.THIS: { var dst = Read8(); Expect(dst < frame.Registers.Length); frame.Registers[dst].SetValue(this); break; } // args: byte dest_reg, var key case Opcode.CTX: using (var m = new ProfileMarker("CTX")) { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length); Expect(dst < frame.Registers.Length); var contextName = frame.Registers[src].AsString(); ExecutionContext context = frame.VM.FindContext(contextName); if (context == null) { throw new VMException(frame.VM, $"VM ctx instruction failed: could not find context with name '{contextName}'"); } frame.Registers[dst].SetValue(context); break; } // args: byte src_reg case Opcode.SWITCH: using (var m = new ProfileMarker("SWITCH")) { var src = Read8(); Expect(src < frame.Registers.Length); var context = frame.Registers[src].AsInterop <ExecutionContext>(); _state = frame.VM.SwitchContext(context, InstructionPointer); if (_state == ExecutionState.Halt) { _state = ExecutionState.Running; frame.VM.PopFrame(); } else { throw new VMException(frame.VM, $"VM switch instruction failed: execution state did not halt"); } break; } default: { throw new VMException(frame.VM, $"Unknown VM opcode: {(int)opcode}"); } } } catch (Exception ex) { if (ex is TargetInvocationException) { ex = ((TargetInvocationException)ex).InnerException; } Trace.WriteLine(ex.ToString()); SetState(ExecutionState.Fault); if (!(ex is VMException)) { ex = new VMException(frame.VM, ex.Message); } if (frame.VM.ThrowOnFault) // enable this when debugging difficult stuff in the VM, should not be activated for production code { throw ex; } } }
public void Step(ref ExecutionFrame frame, Stack <VMObject> stack) { try { opcode = (Opcode)Read8(); frame.VM.ValidateOpcode(opcode); switch (opcode) { case Opcode.NOP: { break; } // args: byte src_reg, byte dest_reg case Opcode.MOVE: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); frame.Registers[dst] = frame.Registers[src]; frame.Registers[src] = new VMObject(); break; } // args: byte src_reg, byte dest_reg case Opcode.COPY: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); frame.Registers[dst].Copy(frame.Registers[src]); break; } // args: byte dst_reg, byte type, var length, var data_bytes case Opcode.LOAD: { var dst = Read8(); var type = (VMType)Read8(); var len = (int)ReadVar(0xFFFF); Expect(dst < frame.Registers.Length, "invalid dst register"); var bytes = ReadBytes(len); frame.Registers[dst].SetValue(bytes, type); break; } // args: byte src_reg, dst_reg, byte type case Opcode.CAST: { var src = Read8(); var dst = Read8(); var type = (VMType)Read8(); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var val = frame.Registers[src]; val = VMObject.CastTo(val, type); frame.Registers[dst] = val; break; } // args: byte src_reg case Opcode.PUSH: { var src = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); var val = frame.Registers[src]; var temp = new VMObject(); temp.Copy(val); stack.Push(temp); break; } // args: byte dest_reg case Opcode.POP: { var dst = Read8(); Expect(stack.Count > 0, "stack is empty"); Expect(dst < frame.Registers.Length, "invalid dst register"); frame.Registers[dst] = stack.Pop(); break; } // args: byte src_reg, byte dest_reg case Opcode.SWAP: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var temp = frame.Registers[src]; frame.Registers[src] = frame.Registers[dst]; frame.Registers[dst] = temp; break; } // args: ushort offset, byte regCount case Opcode.CALL: { var count = Read8(); var ofs = Read16(); Expect(ofs < this.Script.Length, "invalid jump offset"); Expect(count >= 1, "at least 1 register required"); Expect(count <= VirtualMachine.MaxRegisterCount, "invalid register allocs"); frame.VM.PushFrame(this, InstructionPointer, count); frame = frame.VM.CurrentFrame; InstructionPointer = ofs; break; } // args: byte srcReg case Opcode.EXTCALL: using (var m = new ProfileMarker("EXTCALL")) { var src = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); var method = frame.Registers[src].AsString(); var state = frame.VM.ExecuteInterop(method); if (state != ExecutionState.Running) { throw new VMException(frame.VM, "VM extcall failed: " + method); } break; } // args: ushort offset, byte src_reg // NOTE: JMP only has offset arg, not the rest case Opcode.JMP: case Opcode.JMPIF: case Opcode.JMPNOT: { bool shouldJump; if (opcode == Opcode.JMP) { shouldJump = true; } else { var src = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); shouldJump = frame.Registers[src].AsBool(); if (opcode == Opcode.JMPNOT) { shouldJump = !shouldJump; } } var newPos = (short)Read16(); Expect(newPos >= 0, "jump offset can't be negative value"); Expect(newPos < this.Script.Length, "trying to jump outside of script bounds"); if (shouldJump) { InstructionPointer = (uint)newPos; } break; } // args: var length, var bytes case Opcode.THROW: { var src = Read8(); Expect(src < frame.Registers.Length, "invalid exception register"); var exception = frame.Registers[src]; var exceptionMessage = exception.AsString(); throw new VMException(frame.VM, exceptionMessage); } // args: none case Opcode.RET: { if (frame.VM.frames.Count > 1) { var temp = frame.VM.PeekFrame(); if (temp.Context.Name == this.Name) { InstructionPointer = frame.VM.PopFrame(); frame = frame.VM.CurrentFrame; } else { SetState(ExecutionState.Halt); } } else { SetState(ExecutionState.Halt); } return; } // args: byte src_a_reg, byte src_b_reg, byte dest_reg case Opcode.CAT: { var srcA = Read8(); var srcB = Read8(); var dst = Read8(); Expect(srcA < frame.Registers.Length, "invalid srcA register"); Expect(srcB < frame.Registers.Length, "invalid srcB register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var A = frame.Registers[srcA]; var B = frame.Registers[srcB]; if (!A.IsEmpty) { if (B.IsEmpty) { frame.Registers[dst].Copy(A); } else { if (A.Type != B.Type) { throw new VMException(frame.VM, "Invalid cast during concat opcode"); } var bytesA = A.AsByteArray(); var bytesB = B.AsByteArray(); var result = new byte[bytesA.Length + bytesB.Length]; Array.Copy(bytesA, result, bytesA.Length); Array.Copy(bytesB, 0, result, bytesA.Length, bytesB.Length); VMType type = A.Type; frame.Registers[dst].SetValue(result, type); } } else { if (B.IsEmpty) { frame.Registers[dst] = new VMObject(); } else { frame.Registers[dst].Copy(B); } } break; } case Opcode.SUBSTR: { throw new NotImplementedException(); } // args: byte src_reg, byte dest_reg, var length case Opcode.LEFT: { var src = Read8(); var dst = Read8(); var len = (int)ReadVar(0xFFFF); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var src_array = frame.Registers[src].AsByteArray(); Expect(len <= src_array.Length, "invalid length"); var result = new byte[len]; Array.Copy(src_array, result, len); frame.Registers[dst].SetValue(result, VMType.Bytes); break; } // args: byte src_reg, byte dest_reg, byte length case Opcode.RIGHT: { var src = Read8(); var dst = Read8(); var len = (int)ReadVar(0xFFFF); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var src_array = frame.Registers[src].AsByteArray(); Expect(len <= src_array.Length, "invalid length register"); var ofs = src_array.Length - len; var result = new byte[len]; Array.Copy(src_array, ofs, result, 0, len); frame.Registers[dst].SetValue(result, VMType.Bytes); break; } // args: byte src_reg, byte dest_reg case Opcode.SIZE: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); int size; var src_val = frame.Registers[src]; switch (src_val.Type) { case VMType.String: size = src_val.AsString().Length; break; case VMType.Timestamp: case VMType.Number: case VMType.Enum: case VMType.Bool: size = 1; break; case VMType.None: size = 0; break; default: var src_array = src_val.AsByteArray(); size = src_array.Length; break; } frame.Registers[dst].SetValue(size); break; } // args: byte src_reg, byte dest_reg case Opcode.COUNT: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var val = frame.Registers[src]; int count; switch (val.Type) { case VMType.Struct: { var children = val.GetChildren(); count = children.Count; break; } default: count = 1; break; } frame.Registers[dst].SetValue(count); break; } // args: byte src_reg, byte dest_reg case Opcode.NOT: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var val = frame.Registers[src].AsBool(); frame.Registers[dst].SetValue(!val); break; } // args: byte src_a_reg, byte src_b_reg, byte dest_reg case Opcode.AND: case Opcode.OR: case Opcode.XOR: { var srcA = Read8(); var srcB = Read8(); var dst = Read8(); Expect(srcA < frame.Registers.Length, "invalid srcA register"); Expect(srcB < frame.Registers.Length, "invalid srcB register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var valA = frame.Registers[srcA]; var valB = frame.Registers[srcB]; switch (valA.Type) { case VMType.Bool: { Expect(valB.Type == VMType.Bool, $"expected {valA.Type} for logical op"); var a = valA.AsBool(); var b = valB.AsBool(); bool result; switch (opcode) { case Opcode.AND: result = (a && b); break; case Opcode.OR: result = (a || b); break; case Opcode.XOR: result = (a ^ b); break; default: { SetState(ExecutionState.Fault); return; } } frame.Registers[dst].SetValue(result); break; } case VMType.Enum: { Expect(valB.Type == VMType.Enum, $"expected {valA.Type} for flag op"); var numA = valA.AsNumber(); var numB = valB.AsNumber(); Expect(numA.GetBitLength() <= 32, "too many bits"); Expect(numB.GetBitLength() <= 32, "too many bits"); var a = (uint)numA; var b = (uint)numB; if (opcode != Opcode.AND) { SetState(ExecutionState.Fault); } bool result = (a & b) != 0; frame.Registers[dst].SetValue(result); break; } case VMType.Number: { Expect(valB.Type == VMType.Number, $"expected {valA.Type} for logical op"); var numA = valA.AsNumber(); var numB = valB.AsNumber(); Expect(numA.GetBitLength() <= 64, "too many bits"); Expect(numB.GetBitLength() <= 64, "too many bits"); var a = (long)numA; var b = (long)numB; BigInteger result; switch (opcode) { case Opcode.AND: result = (a & b); break; case Opcode.OR: result = (a | b); break; case Opcode.XOR: result = (a ^ b); break; default: { SetState(ExecutionState.Fault); return; } } frame.Registers[dst].SetValue(result); break; } default: throw new VMException(frame.VM, "logical op unsupported for type " + valA.Type); } break; } // args: byte src_a_reg, byte src_b_reg, byte dest_reg case Opcode.EQUAL: { var srcA = Read8(); var srcB = Read8(); var dst = Read8(); Expect(srcA < frame.Registers.Length, "invalid srcA register"); Expect(srcB < frame.Registers.Length, "invalid srcB register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var a = frame.Registers[srcA]; var b = frame.Registers[srcB]; var result = a.Equals(b); frame.Registers[dst].SetValue(result); break; } // args: byte src_a_reg, byte src_b_reg, byte dest_reg case Opcode.LT: case Opcode.GT: case Opcode.LTE: case Opcode.GTE: { var srcA = Read8(); var srcB = Read8(); var dst = Read8(); Expect(srcA < frame.Registers.Length, "invalid srcA register"); Expect(srcB < frame.Registers.Length, "invalid srcB register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var a = frame.Registers[srcA].AsNumber(); var b = frame.Registers[srcB].AsNumber(); bool result; switch (opcode) { case Opcode.LT: result = (a < b); break; case Opcode.GT: result = (a > b); break; case Opcode.LTE: result = (a <= b); break; case Opcode.GTE: result = (a >= b); break; default: { SetState(ExecutionState.Fault); return; } } frame.Registers[dst].SetValue(result); break; } // args: byte reg case Opcode.INC: { var dst = Read8(); Expect(dst < frame.Registers.Length, "invalid dst register"); var val = frame.Registers[dst].AsNumber(); frame.Registers[dst].SetValue(val + 1); break; } // args: byte reg case Opcode.DEC: { var dst = Read8(); Expect(dst < frame.Registers.Length, "invalid dst register"); var val = frame.Registers[dst].AsNumber(); frame.Registers[dst].SetValue(val - 1); break; } // args: byte src_reg, byte dest_reg case Opcode.SIGN: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var val = frame.Registers[src].AsNumber(); if (val == 0) { frame.Registers[dst].SetValue(BigInteger.Zero); } else { frame.Registers[dst].SetValue(val < 0 ? -1 : 1); } break; } // args: byte src_reg, byte dest_reg case Opcode.NEGATE: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var val = frame.Registers[src].AsNumber(); frame.Registers[dst].SetValue(-val); break; } // args: byte src_reg, byte dest_reg case Opcode.ABS: { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var val = frame.Registers[src].AsNumber(); frame.Registers[dst].SetValue(val < 0 ? -val : val); break; } // args: byte src_a_reg, byte src_b_reg, byte dest_reg case Opcode.ADD: case Opcode.SUB: case Opcode.MUL: case Opcode.DIV: case Opcode.MOD: case Opcode.SHR: case Opcode.SHL: case Opcode.MIN: case Opcode.MAX: case Opcode.POW: { var srcA = Read8(); var srcB = Read8(); var dst = Read8(); Expect(srcA < frame.Registers.Length, "invalid srcA register"); Expect(srcB < frame.Registers.Length, "invalid srcB register"); Expect(dst < frame.Registers.Length, "invalid dst register"); if (opcode == Opcode.ADD && frame.Registers[srcA].Type == VMType.String) { Expect(frame.Registers[srcB].Type == VMType.String, "invalid string as right operand"); var a = frame.Registers[srcA].AsString(); var b = frame.Registers[srcB].AsString(); var result = a + b; frame.Registers[dst].SetValue(result); } else { var a = frame.Registers[srcA].AsNumber(); var b = frame.Registers[srcB].AsNumber(); BigInteger result; switch (opcode) { case Opcode.ADD: result = a + b; break; case Opcode.SUB: result = a - b; break; case Opcode.MUL: result = a * b; break; case Opcode.DIV: result = a / b; break; case Opcode.MOD: result = a % b; break; case Opcode.SHR: result = a >> (int)b; break; case Opcode.SHL: result = a << (int)b; break; case Opcode.MIN: result = a < b ? a : b; break; case Opcode.MAX: result = a > b ? a : b; break; case Opcode.POW: result = BigInteger.Pow(a, b); break; default: { SetState(ExecutionState.Fault); return; } } frame.Registers[dst].SetValue(result); } break; } // args: byte src_reg, byte dest_reg, byte key case Opcode.PUT: { var src = Read8(); var dst = Read8(); var keyReg = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); Expect(keyReg < frame.Registers.Length, "invalid key register"); var key = frame.Registers[keyReg]; Throw.If(key.Type == VMType.None, "invalid key type"); var value = frame.Registers[src]; frame.Registers[dst].SetKey(key, value); break; } // args: byte src_reg, byte dest_reg, byte key case Opcode.GET: { var src = Read8(); var dst = Read8(); var keyReg = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); Expect(keyReg < frame.Registers.Length, "invalid key register"); var key = frame.Registers[keyReg]; Throw.If(key.Type == VMType.None, "invalid key type"); var val = frame.Registers[src].GetKey(key); frame.Registers[dst] = val; break; } // args: byte dest_reg case Opcode.CLEAR: { var dst = Read8(); Expect(dst < frame.Registers.Length, "invalid dst register"); frame.Registers[dst] = new VMObject(); break; } // args: byte dest_reg, var key case Opcode.CTX: using (var m = new ProfileMarker("CTX")) { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var contextName = frame.Registers[src].AsString(); ExecutionContext context = frame.VM.FindContext(contextName); if (context == null) { throw new VMException(frame.VM, $"VM ctx instruction failed: could not find context with name '{contextName}'"); } frame.Registers[dst].SetValue(context); break; } // args: byte src_reg case Opcode.SWITCH: using (var m = new ProfileMarker("SWITCH")) { var src = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); var context = frame.Registers[src].AsInterop <ExecutionContext>(); _state = frame.VM.SwitchContext(context, InstructionPointer); if (_state == ExecutionState.Halt) { _state = ExecutionState.Running; frame.VM.PopFrame(); } else { throw new VMException(frame.VM, $"VM switch instruction failed: execution state did not halt"); } break; } // args: byte src_reg dst_reg case Opcode.UNPACK: using (var m = new ProfileMarker("SWITCH")) { var src = Read8(); var dst = Read8(); Expect(src < frame.Registers.Length, "invalid src register"); Expect(dst < frame.Registers.Length, "invalid dst register"); var bytes = frame.Registers[src].AsByteArray(); frame.Registers[dst] = VMObject.FromBytes(bytes); break; } case Opcode.DEBUG: { break; // put here a breakpoint for debugging } default: { throw new VMException(frame.VM, $"Unknown VM opcode: {(int)opcode}"); } } } catch (Exception ex) { ex = ex.ExpandInnerExceptions(); Trace.WriteLine(ex.ToString()); SetState(ExecutionState.Fault); if (!(ex is VMException)) { ex = new VMException(frame.VM, ex.Message); } throw ex; } }
public void SetKey(BigInteger key, VMObject obj) { SetKey(key.ToString(), obj); }
public static VMObject CastTo(VMObject srcObj, VMType type) { if (srcObj.Type == type) { var result = new VMObject(); result.Copy(srcObj); return(result); } switch (type) { case VMType.None: return(new VMObject()); case VMType.String: { var result = new VMObject(); result.SetValue(srcObj.AsString()); // TODO does this work for all types? return(result); } case VMType.Timestamp: { var result = new VMObject(); result.SetValue(srcObj.AsTimestamp()); // TODO does this work for all types? return(result); } case VMType.Bool: // TODO move this stuff to AsBool() switch (srcObj.Type) { case VMType.Number: { var result = new VMObject(); result.SetValue(srcObj.AsNumber() != 0); return(result); } case VMType.String: { var result = new VMObject(); result.SetValue(!(((string)srcObj.Data).Equals("false", StringComparison.OrdinalIgnoreCase))); return(result); } default: throw new Exception($"invalid cast: {srcObj.Type} to {type}"); } case VMType.Bytes: switch (srcObj.Type) { case VMType.Bool: { var result = new VMObject(); result.SetValue(new byte[] { (byte)(srcObj.AsBool() ? 1 : 0) }, VMType.Bytes); return(result); } case VMType.String: { var result = new VMObject(); result.SetValue(Encoding.UTF8.GetBytes((string)srcObj.Data), VMType.Bytes); return(result); } case VMType.Number: { var result = new VMObject(); result.SetValue(((BigInteger)srcObj.Data).ToSignedByteArray(), VMType.Bytes); return(result); } default: throw new Exception($"invalid cast: {srcObj.Type} to {type}"); } case VMType.Number: switch (srcObj.Type) { case VMType.Bool: { var result = new VMObject(); result.SetValue(srcObj.AsBool() ? 1 : 0); return(result); } case VMType.String: { var result = new VMObject(); result.SetValue(BigInteger.Parse((string)srcObj.Data)); return(result); } case VMType.Bytes: { var result = new VMObject(); result.SetValue(BigInteger.FromSignedArray((byte[])srcObj.Data)); return(result); } default: throw new Exception($"invalid cast: {srcObj.Type} to {type}"); } case VMType.Struct: switch (srcObj.Type) { case VMType.Object: return(CastViaReflection(srcObj.Data, 0)); default: throw new Exception($"invalid cast: {srcObj.Type} to {type}"); } default: throw new Exception($"invalid cast: {srcObj.Type} to {type}"); } }
// this does the opposite of ToStruct(), takes a InteropObject and converts it to a VM.Struct private static VMObject CastViaReflection(object srcObj, int level) { var srcType = srcObj.GetType(); if (srcType.IsArray) { var children = new Dictionary <VMObject, VMObject>(); var array = (Array)srcObj; for (int i = 0; i < array.Length; i++) { var val = array.GetValue(i); var key = new VMObject(); key.SetValue(i); var vmVal = CastViaReflection(val, level + 1); children[key] = vmVal; } var result = new VMObject(); result.SetValue(children); return(result); } else { var targetType = VMObject.GetVMType(srcType); VMObject result; bool isKnownType = typeof(BigInteger) == srcType || typeof(Timestamp) == srcType || typeof(ISerializable).IsAssignableFrom(srcType); if (srcType.IsStructOrClass() && !isKnownType) { var children = new Dictionary <VMObject, VMObject>(); var fields = srcType.GetFields(); if (fields.Length > 0) { foreach (var field in fields) { var key = new VMObject(); key.SetValue(field.Name); var val = field.GetValue(srcObj); var vmVal = CastViaReflection(val, level + 1); children[key] = vmVal; } result = new VMObject(); result.SetValue(children); return(result); } } result = VMObject.FromObject(srcObj); if (result != null) { return(result); } throw new Exception($"invalid cast: Interop.{srcType.Name} to vm object"); } }
// this does the opposite of ToStruct(), takes a InteropObject and converts it to a VM.Struct private static VMObject CastViaReflection(object srcObj, int level) { var srcType = srcObj.GetType(); if (srcType.IsArray) { var children = new Dictionary <VMObject, VMObject>(); var array = (Array)srcObj; for (int i = 0; i < array.Length; i++) { var val = array.GetValue(i); var key = new VMObject(); key.SetValue(i); var vmVal = CastViaReflection(val, level + 1); children[key] = vmVal; } var result = new VMObject(); result.SetValue(children); return(result); } else { VMObject result; if (level == 0 && srcType.IsStructOrClass()) { var children = new Dictionary <VMObject, VMObject>(); var fields = srcType.GetFields(); if (fields.Length > 0) { foreach (var field in fields) { var key = new VMObject(); key.SetValue(field.Name); var val = field.GetValue(srcObj); var vmVal = CastViaReflection(val, level + 1); children[key] = vmVal; } result = new VMObject(); result.SetValue(children); return(result); } else { throw new Exception("Invalid cast, no fields available"); } } result = VMObject.FromObject(srcObj); if (result != null) { return(result); } throw new Exception($"invalid cast: Interop.{srcType.Name} to vm object"); } }