diff --git a/src/Engine/ProtoCore/AssociativeGraph.cs b/src/Engine/ProtoCore/AssociativeGraph.cs index 201ec7698ca..08be1a8ef96 100644 --- a/src/Engine/ProtoCore/AssociativeGraph.cs +++ b/src/Engine/ProtoCore/AssociativeGraph.cs @@ -17,7 +17,17 @@ public class Utils public static AssociativeGraph.GraphNode GetGraphNodeAtPC(int pc, List graphNodesInScope) { Validity.Assert(graphNodesInScope != null); - return graphNodesInScope.FirstOrDefault(g => g.isActive && g.isDirty && g.updateBlock.startpc == pc); + + for (int i = 0; i < graphNodesInScope.Count; i++) + { + var g = graphNodesInScope[i]; + if (g.isActive && g.isDirty && g.updateBlock.startpc == pc) + { + return g; + } + } + + return null; } /// @@ -29,7 +39,17 @@ public static AssociativeGraph.GraphNode GetGraphNodeAtPC(int pc, List graphNodesInScope) { Validity.Assert(graphNodesInScope != null); - return graphNodesInScope.FirstOrDefault(g => g.isActive && g.isDirty && g.updateBlock.startpc >= pc); + + for (int i = 0; i < graphNodesInScope.Count; i++) + { + var g = graphNodesInScope[i]; + if (g.isActive && g.isDirty && g.updateBlock.startpc >= pc) + { + return g; + } + } + + return null; } /// @@ -659,23 +679,28 @@ public static bool IsGraphNodeRedefined(AssociativeGraph.GraphNode gnode, Associ List redefinedNodes = new List(); if (executingGraphNode != null) { - // Remove this condition when full SSA is enabled - bool isssa = (!executingGraphNode.IsSSANode() && executingGraphNode.DependsOnTempSSA()); - + bool isssa; if (runtimeCore.Options.ExecuteSSA) { isssa = executingGraphNode.IsSSANode(); } + else + { + // Remove this condition when full SSA is enabled + isssa = (!executingGraphNode.IsSSANode() && executingGraphNode.DependsOnTempSSA()); + } + if (!isssa) { + + SymbolNode symbol = executingGraphNode.updateNodeRefList[0].nodeList[0].symbol; + bool isMember = symbol.classScope != Constants.kInvalidIndex + && symbol.functionIndex == Constants.kInvalidIndex; + foreach (AssociativeGraph.GraphNode graphNode in nodesInScope) { bool allowRedefine = true; - SymbolNode symbol = executingGraphNode.updateNodeRefList[0].nodeList[0].symbol; - bool isMember = symbol.classScope != Constants.kInvalidIndex - && symbol.functionIndex == Constants.kInvalidIndex; - if (isMember) { // For member vars, do not allow if not in the same scope @@ -1475,7 +1500,7 @@ public bool IsSSANode() return false; } - var firstNode = updateNodeRefList.First().nodeList.FirstOrDefault(); + var firstNode = updateNodeRefList[0].nodeList[0]; return firstNode != null && firstNode.nodeType == UpdateNodeType.Symbol && firstNode.symbol.isSSATemp; } } diff --git a/src/Engine/ProtoCore/DSASM/DSObject.cs b/src/Engine/ProtoCore/DSASM/DSObject.cs index 5ccab6bb174..dfb22217501 100644 --- a/src/Engine/ProtoCore/DSASM/DSObject.cs +++ b/src/Engine/ProtoCore/DSASM/DSObject.cs @@ -1,4 +1,4 @@ -using System; +using System; using ProtoCore.Properties; using ProtoCore.Runtime; @@ -12,6 +12,12 @@ public DSObject(int size, Heap heap) MetaData = new MetaData { type = (int)PrimitiveType.Pointer }; } + public DSObject(int size, Heap heap, MetaData metaData) + : base(size, heap) + { + MetaData = metaData; + } + public DSObject(StackValue[] values, Heap heap) : base(values, heap) { diff --git a/src/Engine/ProtoCore/DSASM/Executive.cs b/src/Engine/ProtoCore/DSASM/Executive.cs index ecc0a89a0f2..dd9d4603b58 100644 --- a/src/Engine/ProtoCore/DSASM/Executive.cs +++ b/src/Engine/ProtoCore/DSASM/Executive.cs @@ -111,6 +111,17 @@ public Executive(RuntimeCore runtimeCore, bool isFep = false) deferedGraphNodes = new List(); } + public void Reset(bool isFep = false) + { + pc = Constants.kInvalidIndex; + executingBlock = Constants.kInvalidIndex; + RX = StackValue.BuildInvalid(); + TX = StackValue.BuildInvalid(); + fepRun = isFep; + terminate = false; + graphNodesInProgramScope = null; + } + /// /// Cache the graphnodes in scope /// @@ -2308,15 +2319,35 @@ private void Execute(int exeblock, int entry, Language language = Language.NotSp } } + private int cacheBlockID = -1; + private int cacheClassIndex = -1; + private int cachesymbolIndex = -1; + private SymbolNode cachedSymbol = null; + protected SymbolNode GetSymbolNode(int blockId, int classIndex, int symbolIndex) { + if (blockId == cacheBlockID && classIndex == cacheClassIndex && symbolIndex == cachesymbolIndex && cachedSymbol != null) + { + return cachedSymbol; + } + if (Constants.kGlobalScope == classIndex) { - return exe.runtimeSymbols[blockId].symbolList[symbolIndex]; + var symbol = exe.runtimeSymbols[blockId].symbolList[symbolIndex]; + cacheBlockID = blockId; + cacheClassIndex = classIndex; + cachesymbolIndex = symbolIndex; + cachedSymbol = symbol; + return symbol; } else { - return exe.classTable.ClassNodes[classIndex].Symbols.symbolList[symbolIndex]; + var symbol = exe.classTable.ClassNodes[classIndex].Symbols.symbolList[symbolIndex]; + cacheBlockID = blockId; + cacheClassIndex = classIndex; + cachesymbolIndex = symbolIndex; + cachedSymbol = symbol; + return symbol; } } @@ -2438,10 +2469,11 @@ protected StackValue PopTo(int blockId, StackValue op1, StackValue op2, StackVal case AddressType.MemVarIndex: SymbolNode symbol = GetSymbolNode(blockId, op2.ClassIndex, op1.SymbolIndex); - opPrev = rmem.GetSymbolValue(symbol); - rmem.SetSymbolValue(symbol, opVal); - exe.UpdatedSymbols.Add(symbol); + opPrev = rmem.SetSymbolValueAndGetPreviousValue(symbol, opVal); + + exe.UpdatedSymbols.Add(symbol); +#if DEBUG if (IsDebugRun()) { logWatchWindow(blockId, op1.SymbolIndex); @@ -2452,16 +2484,17 @@ protected StackValue PopTo(int blockId, StackValue op1, StackValue op2, StackVal { logWatchWindow(blockId, op1.SymbolIndex); } - +#endif RecordExecutedGraphNode(); break; case AddressType.StaticMemVarIndex: var staticMember = GetSymbolNode(blockId, Constants.kGlobalScope, op1.StaticVariableIndex); - opPrev = rmem.GetSymbolValue(staticMember); - rmem.SetSymbolValue(staticMember, opVal); - exe.UpdatedSymbols.Add(staticMember); + opPrev = rmem.SetSymbolValueAndGetPreviousValue(staticMember, opVal); + + exe.UpdatedSymbols.Add(staticMember); +#if DEBUG if (IsDebugRun()) { logWatchWindow(blockId, op1.StaticVariableIndex); @@ -2469,6 +2502,7 @@ protected StackValue PopTo(int blockId, StackValue op1, StackValue op2, StackVal } logWatchWindow(blockId, op1.StaticVariableIndex); +#endif break; case AddressType.Register: { diff --git a/src/Engine/ProtoCore/DSASM/Heap.cs b/src/Engine/ProtoCore/DSASM/Heap.cs index f825cf35808..07165d3fa1c 100644 --- a/src/Engine/ProtoCore/DSASM/Heap.cs +++ b/src/Engine/ProtoCore/DSASM/Heap.cs @@ -297,8 +297,8 @@ public enum GCMark Black // Objects that are in use (are not candidates for garbage collection) and have had all their references traced. } - private readonly List freeList = new List(); - private readonly List heapElements = new List(); + private readonly List freeList = new List(GC_THRESHOLD); + private readonly List heapElements = new List(GC_THRESHOLD); private HashSet fixedHeapElements = new HashSet(); private readonly StringTable stringTable = new StringTable(); // The expected lifetime of the sweepSet is start of GC to end of GC @@ -402,9 +402,7 @@ public StackValue AllocatePointer(int size, MetaData metadata) { try { - int index = AllocateInternal(size, PrimitiveType.Pointer); - var hpe = heapElements[index]; - hpe.MetaData = metadata; + int index = AllocatePointerInternal(size, metadata); return StackValue.BuildPointer(index, metadata); } catch (OutOfMemoryException) @@ -583,6 +581,11 @@ private int AllocateInternal(int size, PrimitiveType type) return AddHeapElement(hpe); } + private int AllocatePointerInternal(int size, MetaData metaData) + { + return AddHeapElement(new DSObject(size, this, metaData)); + } + private int AllocateInternal(StackValue[] values, PrimitiveType type) { HeapElement hpe = null; diff --git a/src/Engine/ProtoCore/FFI/CLRFFIFunctionPointer.cs b/src/Engine/ProtoCore/FFI/CLRFFIFunctionPointer.cs index 8f0728f6d4e..a56efe7306b 100644 --- a/src/Engine/ProtoCore/FFI/CLRFFIFunctionPointer.cs +++ b/src/Engine/ProtoCore/FFI/CLRFFIFunctionPointer.cs @@ -500,9 +500,24 @@ private string ErrorString(System.Exception ex) return string.Format(Resources.OperationFailType2, ReflectionInfo.DeclaringType.Name, ReflectionInfo.Name, msg); } + private object[] parameters = null; + private FFIParameterInfo[] paraminfos; + private List referencedParameters; + private object missing = Type.Missing; + public override object Execute(ProtoCore.Runtime.Context c, Interpreter dsi, List s) { - var parameters = new List(); + if (parameters == null) + { + parameters = new object[mArgTypes.Length]; + paraminfos = ReflectionInfo.GetParameters(); + referencedParameters = new List(); + } + else + { + referencedParameters.Clear(); + } + if (s == null) s = dsi.runtime.rmem.Stack; @@ -525,9 +540,6 @@ public override object Execute(ProtoCore.Runtime.Context c, Interpreter dsi, Lis return null; //Can't call a method on null object. } - FFIParameterInfo[] paraminfos = ReflectionInfo.GetParameters(); - List referencedParameters = new List(); - for (int i = 0; i < mArgTypes.Length; ++i) { var opArg = s[i]; @@ -536,7 +548,7 @@ public override object Execute(ProtoCore.Runtime.Context c, Interpreter dsi, Lis Type paramType = paraminfos[i].Info.ParameterType; object param = null; if (opArg.IsDefaultArgument) - param = Type.Missing; + param = missing; else param = marshaller.UnMarshal(opArg, c, dsi, paramType); @@ -559,7 +571,7 @@ public override object Execute(ProtoCore.Runtime.Context c, Interpreter dsi, Lis } - parameters.Add(param); + parameters[i] = param; } catch (System.InvalidCastException ex) { @@ -574,7 +586,7 @@ public override object Execute(ProtoCore.Runtime.Context c, Interpreter dsi, Lis } } - var ret = InvokeFunctionPointerNoThrow(c, dsi, thisObject, parameters.Count > 0 ? parameters.ToArray() : null); + var ret = InvokeFunctionPointerNoThrow(c, dsi, thisObject, parameters); if (ReflectionInfo.KeepReferenceThis && thisObject != null) { referencedParameters.Add(s.Last()); diff --git a/src/Engine/ProtoCore/FFI/CLRObjectMarshaler.cs b/src/Engine/ProtoCore/FFI/CLRObjectMarshaler.cs index dc4d87e0f04..d9bc0b883a2 100644 --- a/src/Engine/ProtoCore/FFI/CLRObjectMarshaler.cs +++ b/src/Engine/ProtoCore/FFI/CLRObjectMarshaler.cs @@ -504,12 +504,27 @@ public override object UnMarshal(StackValue dsObject, ProtoCore.Runtime.Context internal static bool IsAssignableFromDictionary(Type expectedCLRType) { - return expectedCLRType == typeof(IDictionary) || - expectedCLRType.GetInterfaces() - .Where(i => i.IsGenericType) - .Select(i => i.GetGenericTypeDefinition()) - .Contains(typeof(IDictionary<,>)) || - expectedCLRType.IsAssignableFrom(typeof(DesignScript.Builtin.Dictionary)); + if (expectedCLRType == typeof(IDictionary)) + return true; + + // Fast path for common generic IDictionary<,> + if (expectedCLRType.IsGenericType && expectedCLRType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + return true; + + // Check interfaces without LINQ + var interfaces = expectedCLRType.GetInterfaces(); + for (int i = 0; i < interfaces.Length; i++) + { + var iface = interfaces[i]; + if (iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + return true; + } + + // Check assignability from DesignScript.Builtin.Dictionary + if (expectedCLRType.IsAssignableFrom(typeof(DesignScript.Builtin.Dictionary))) + return true; + + return false; } private object ToIDictionary(StackValue dsObject, ProtoCore.Runtime.Context context, Interpreter dsi, System.Type expectedType) @@ -709,9 +724,9 @@ public override StackValue Marshal(object obj, ProtoCore.Runtime.Context context return retVal; //5. If it is a StackValue, simply return it. - if (obj is StackValue) + if (obj is StackValue sv) { - return (StackValue)obj; + return sv; } //6. Seems like a new object create a new DS object and bind it. diff --git a/src/Engine/ProtoCore/Lang/CallSite.cs b/src/Engine/ProtoCore/Lang/CallSite.cs index db237faa6c3..beaa2340f1c 100644 --- a/src/Engine/ProtoCore/Lang/CallSite.cs +++ b/src/Engine/ProtoCore/Lang/CallSite.cs @@ -5,6 +5,7 @@ using System.IO; using System.IO.Compression; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using DynamoServices; using Newtonsoft.Json; @@ -1421,8 +1422,10 @@ private StackValue Execute( } else //replicated call { + ReadOnlySpan replicationInstructionsSpan = CollectionsMarshal.AsSpan(replicationInstructions); + c.IsReplicating = true; - ret = ExecWithRISlowPath(functionEndPoints, c, formalParameters, replicationInstructions, stackFrame, runtimeCore, singleRunTraceData, newTraceData); + ret = ExecWithRISlowPath(functionEndPoints, c, formalParameters, replicationInstructionsSpan, stackFrame, runtimeCore, singleRunTraceData, newTraceData); } //Do a trace save here @@ -1456,7 +1459,7 @@ private StackValue ExecWithRISlowPath( List functionEndPoints, Context c, List formalParameters, - List replicationInstructions, + ReadOnlySpan replicationInstructions, StackFrame stackFrame, RuntimeCore runtimeCore, SingleRunTraceData previousTraceData, @@ -1467,7 +1470,7 @@ private StackValue ExecWithRISlowPath( throw new NotImplementedException("Parallel mode disabled: {BF417AD5-9EA9-4292-ABBC-3526FC5A149E}"); //Recursion base case - if (replicationInstructions.Count == 0) + if (replicationInstructions.Length == 0) { return ExecWithZeroRI(functionEndPoints, c, formalParameters, stackFrame, runtimeCore, previousTraceData, newTraceData, finalFunctionEndPoint); } @@ -1490,7 +1493,7 @@ private StackValue ExecWithRISlowPath( List repIndecies = ri.ZipIndecies; //this will hold the heap elements for all the arrays that are going to be replicated over - List parameters = new List(); + List parameters = new List(repIndecies.Count); int retSize; switch (algorithm) @@ -1545,7 +1548,7 @@ private StackValue ExecWithRISlowPath( StackValue[] retSVs = new StackValue[retSize]; SingleRunTraceData retTrace = newTraceData; - retTrace.NestedData = new List(); //this will shadow the SVs as they are created + retTrace.NestedData = new List(retSize); //this will shadow the SVs as they are created //Populate out the size of the list with default values //@TODO:Luke perf optimisation here @@ -1595,7 +1598,7 @@ private StackValue ExecWithRISlowPath( } } - List newRIs = replicationInstructions.GetRange(1, replicationInstructions.Count - 1); + ReadOnlySpan newRIs = replicationInstructions[1..]; SingleRunTraceData cleanRetTrace = new SingleRunTraceData(); @@ -1658,7 +1661,7 @@ private StackValue ExecWithRISlowPath( if (supressArray) { - List newRIs = replicationInstructions.GetRange(1, replicationInstructions.Count - 1); + ReadOnlySpan newRIs = replicationInstructions[1..]; return ExecWithRISlowPath(functionEndPoints, c, newFormalParams, newRIs, stackFrame, runtimeCore, previousTraceData, newTraceData, finalFunctionEndPoint); } @@ -1669,7 +1672,7 @@ private StackValue ExecWithRISlowPath( //It was an array pack the arg with the current value newFormalParams[cartIndex] = array.GetValueFromIndex(i, runtimeCore); - List newRIs = replicationInstructions.GetRange(1, replicationInstructions.Count - 1); + ReadOnlySpan newRIs = replicationInstructions[1..]; SingleRunTraceData lastExecTrace; diff --git a/src/Engine/ProtoCore/Lang/JILFunctionEndPoint.cs b/src/Engine/ProtoCore/Lang/JILFunctionEndPoint.cs index b9eae6bc9cd..3518871e106 100644 --- a/src/Engine/ProtoCore/Lang/JILFunctionEndPoint.cs +++ b/src/Engine/ProtoCore/Lang/JILFunctionEndPoint.cs @@ -1,4 +1,4 @@ - + using System.Collections.Generic; using ProtoCore.DSASM; using ProtoCore.Lang.Replication; @@ -19,6 +19,8 @@ public class JILActivationRecord public class JILFunctionEndPoint : FunctionEndPoint { private readonly JILActivationRecord activation; + private Interpreter mInterpreter; + public JILFunctionEndPoint() { activation = new JILActivationRecord(); @@ -35,14 +37,29 @@ public override bool DoesPredicateMatch(ProtoCore.Runtime.Context c, List formalParameters, ProtoCore.DSASM.StackFrame stackFrame, RuntimeCore runtimeCore) { - ProtoCore.DSASM.Interpreter interpreter = new ProtoCore.DSASM.Interpreter(runtimeCore, true); + if (mInterpreter == null || c.IsImplicitCall) + { + Init(runtimeCore); + } + else + { + mInterpreter.runtime.Reset(true); + } + ProtoCore.DSASM.Executive oldDSASMExec = null; if (runtimeCore.CurrentExecutive != null) { oldDSASMExec = runtimeCore.CurrentExecutive.CurrentDSASMExec; - runtimeCore.CurrentExecutive.CurrentDSASMExec = interpreter.runtime; + runtimeCore.CurrentExecutive.CurrentDSASMExec = mInterpreter.runtime; } try @@ -60,14 +77,14 @@ public override StackValue Execute(ProtoCore.Runtime.Context c, List for (int n = execStateSize - 1; n >= 0; --n) { AssociativeGraph.GraphNode gnode = procedureNode.GraphNodeList[n]; - interpreter.Push(StackValue.BuildBoolean(gnode.isDirty)); + mInterpreter.Push(StackValue.BuildBoolean(gnode.isDirty)); } // Push Params formalParameters.Reverse(); for (int i = 0; i < formalParameters.Count; i++) { - interpreter.Push(formalParameters[i]); + mInterpreter.Push(formalParameters[i]); } StackValue svThisPtr = stackFrame.ThisPtr; @@ -108,7 +125,7 @@ public override StackValue Execute(ProtoCore.Runtime.Context c, List } stackFrame.TX = svCallConvention; - interpreter.runtime.TX = svCallConvention; + mInterpreter.runtime.TX = svCallConvention; // Set SX register stackFrame.BlockIndex = svBlockDecl; @@ -138,7 +155,7 @@ public override StackValue Execute(ProtoCore.Runtime.Context c, List } else { - svRet = interpreter.Run(runtimeCore.RunningBlock, activation.pc, Language.NotSpecified, runtimeCore.Breakpoints); + svRet = mInterpreter.Run(runtimeCore.RunningBlock, activation.pc, Language.NotSpecified, runtimeCore.Breakpoints); runtimeCore.RunningBlock = origRunningBlock; } diff --git a/src/Engine/ProtoCore/Reflection/MirrorData.cs b/src/Engine/ProtoCore/Reflection/MirrorData.cs index 541d9165f95..4785c428aef 100644 --- a/src/Engine/ProtoCore/Reflection/MirrorData.cs +++ b/src/Engine/ProtoCore/Reflection/MirrorData.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.Linq; using Autodesk.DesignScript.Interfaces; +using ProtoCore.AST.AssociativeAST; using ProtoCore.DSASM; using ProtoCore.Utils; @@ -39,11 +40,8 @@ public bool IsFunction // private ProtoCore.Core core; private ProtoCore.RuntimeCore runtimeCore; - - /// - /// - /// - private static GraphicDataProvider dataProvider = new GraphicDataProvider(); + private ProtoCore.DSASM.Interpreter interpreter; + private ProtoFFI.FFIObjectMarshaler marshaler; /// /// Experimental constructor that takes in a core object @@ -70,6 +68,21 @@ public MirrorData(ProtoCore.Core core, ProtoCore.RuntimeCore runtimeCore, StackV svData = sv; } + /// + /// Takes a runtime core object to read runtime data + /// + /// + /// + /// + public MirrorData(ProtoCore.Core core, ProtoCore.RuntimeCore runtimeCore, StackValue sv, Interpreter i, ProtoFFI.FFIObjectMarshaler m) + { + this.core = core; + this.runtimeCore = runtimeCore; + svData = sv; + interpreter = i; + marshaler = m; + } + /// /// Recursively finds all Pointers from the stack value @@ -145,7 +158,20 @@ public IEnumerable GetElements() return null; var array = runtimeCore.Heap.ToHeapObject(svData); - return array.Values.Select(x => new MirrorData(this.core, this.runtimeCore, x)); + + ProtoCore.DSASM.Interpreter interpreter = new ProtoCore.DSASM.Interpreter(runtimeCore, false); + var helper = ProtoFFI.DLLFFIHandler.GetModuleHelper(ProtoFFI.FFILanguage.CSharp); + var marshaler = helper.GetMarshaler(runtimeCore); + var elements = new List(); + + foreach(var item in array.Values) + { + elements.Add(item.IsPointer + ? new MirrorData(this.core, this.runtimeCore, item, interpreter, marshaler) + : new MirrorData(this.core, this.runtimeCore, item)); + } + + return elements; } /// @@ -162,7 +188,7 @@ public IEnumerable GetElements() /// StackValue /// ProtoCore.Core /// System.Object - internal static object GetData(StackValue sv, RuntimeCore runtimeCore) + internal object GetData(StackValue sv, RuntimeCore runtimeCore) { switch (sv.optype) { @@ -177,13 +203,42 @@ internal static object GetData(StackValue sv, RuntimeCore runtimeCore) case AddressType.String: return StringUtils.GetStringValue(sv, runtimeCore); case AddressType.Pointer: - return dataProvider.GetCLRObject(sv, runtimeCore); + return GetCLRObject(sv, runtimeCore); default: break; } return null; } + internal object GetCLRObject(StackValue svData, RuntimeCore runtimeCore) + { + if (null == runtimeCore.DSExecutable.classTable) + return null; + + IList classNodes = runtimeCore.DSExecutable.classTable.ClassNodes; + if (null == classNodes || (classNodes.Count <= 0)) + return null; + + ClassNode classnode = runtimeCore.DSExecutable.classTable.ClassNodes[svData.metaData.type]; + if (!classnode.IsImportedClass) //TODO: look at properties to see if it contains any FFI objects. + return null; + + try + { + if (this.marshaler == null) + { + interpreter = new ProtoCore.DSASM.Interpreter(runtimeCore, false); + var helper = ProtoFFI.DLLFFIHandler.GetModuleHelper(ProtoFFI.FFILanguage.CSharp); + marshaler = helper.GetMarshaler(runtimeCore); + } + return marshaler.UnMarshal(svData, null, interpreter, typeof(object)); + } + catch (System.Exception) + { + return null; + } + } + internal static string PrecisionFormat { get; set; } = "f3"; /// diff --git a/src/Engine/ProtoCore/RuntimeMemory.cs b/src/Engine/ProtoCore/RuntimeMemory.cs index dd3b508bc24..05c7e6f1dbb 100644 --- a/src/Engine/ProtoCore/RuntimeMemory.cs +++ b/src/Engine/ProtoCore/RuntimeMemory.cs @@ -186,6 +186,20 @@ public void SetSymbolValue(SymbolNode symbol, StackValue data) Stack[index] = data; } + /// + /// Set the value for symbol on current stack frame and get the previous item. + /// + /// + /// + /// + public StackValue SetSymbolValueAndGetPreviousValue(SymbolNode symbol, StackValue data) + { + int index = GetStackIndex(symbol, FramePointer); + var prevVal = Stack[index]; + Stack[index] = data; + return prevVal; + } + // TO BE DELETED public int GetStackIndex(int offset) {