private MsgTuple ParamsToTuple(IEnumerable <RValue> args, CoalescedFrame frame)
        {
            var tuple = new MsgTuple();

            foreach (var arg in args)
            {
                if (arg is ConstantValue)
                {
                    tuple.Column.Add(ConstantToTypedValue(arg as ConstantValue));
                }
                else
                {
                    if (frame != null)
                    {
                        tuple.Column.Add(VariableToTypedValue(arg as LocalVar, frame));
                    }
                    else
                    {
                        throw new RequestFailedException("Local variables cannot be referenced without a stack frame");
                    }
                }
            }

            return(tuple);
        }
        public void Evaluate(DAPRequest request, string expression, CoalescedFrame frame, bool allowMutation)
        {
            var stmt = Parse(expression);

            if (stmt == null)
            {
                DAP.SendReply(request, "Syntax error. Type \"help\" for usage.");
                return;
            }

            if (stmt.Params == null)
            {
                EvaluateName(request, stmt.Name, allowMutation);
            }
            else
            {
                EvaluateCall(request, stmt, frame, allowMutation);
            }
        }
        private MsgTypedValue VariableToTypedValue(LocalVar lvar, CoalescedFrame frame)
        {
            // TODO - lvar.Type?
            if (lvar.Name == "_")
            {
                var tv = new MsgTypedValue();
                tv.TypeId = (UInt32)Value.Type.None;
                return(tv);
            }
            else
            {
                var frameVar = frame.Variables.FirstOrDefault(v => v.Name == lvar.Name);
                if (frameVar == null)
                {
                    throw new RequestFailedException($"Variable does not exist: \"{lvar.Name}\"");
                }

                return(frameVar.TypedValue);
            }
        }
Example #4
0
        private CoalescedFrame MsgFrameToLocal(MsgFrame frame)
        {
            var outFrame = new CoalescedFrame();

            outFrame.Name = Formatter.GetFrameDebugName(frame);

            if (frame.Type == MsgFrame.Types.FrameType.GoalInitAction ||
                frame.Type == MsgFrame.Types.FrameType.GoalExitAction)
            {
                var goal = DebugInfo.Goals[frame.GoalId];
                outFrame.File = goal.Path;
                if (frame.Type == MsgFrame.Types.FrameType.GoalInitAction)
                {
                    outFrame.Line = (int)goal.InitActions[(int)frame.ActionIndex].Line;
                }
                else
                {
                    outFrame.Line = (int)goal.ExitActions[(int)frame.ActionIndex].Line;
                }
            }
            else if (frame.NodeId != 0)
            {
                var node = DebugInfo.Nodes[frame.NodeId];
                if (node.RuleId != 0)
                {
                    var rule = DebugInfo.Rules[node.RuleId];
                    var goal = DebugInfo.Goals[rule.GoalId];
                    outFrame.File = goal.Path;

                    if (frame.Type == MsgFrame.Types.FrameType.Pushdown &&
                        node.Type == Node.Type.Rule)
                    {
                        outFrame.Line = (int)rule.ActionsStartLine;
                    }
                    else if (frame.Type == MsgFrame.Types.FrameType.RuleAction)
                    {
                        outFrame.Line = (int)rule.Actions[(int)frame.ActionIndex].Line;
                    }
                    else
                    {
                        outFrame.Line = node.Line;
                    }
                }
            }

            outFrame.Variables = TupleToVariables(frame);
            outFrame.Frame     = frame;

            if (outFrame.File != null &&
                ModUuid != null)
            {
                var modRe = new Regex(".*\\.pak:/Mods/.*/Story/RawFiles/Goals/(.*)\\.txt");
                var match = modRe.Match(outFrame.File);
                if (match.Success)
                {
                    outFrame.File = "divinity:/" + ModUuid + "/" + match.Groups[1].Value + ".divgoal";
                }
            }

            return(outFrame);
        }
Example #5
0
        /// <summary>
        /// Merges a node call range into an output stack frame.
        /// </summary>
        private CoalescedFrame MergeFrame(List <CoalescedFrame> range)
        {
            var frame = new CoalescedFrame();

            frame.Frame = range[0].Frame;

            foreach (var node in range)
            {
                // Use last available location/variable data in the range
                if (node.Line != 0)
                {
                    frame.File = node.File;
                    frame.Line = node.Line;
                }

                if (frame.Rule == null && node.Frame.NodeId != 0)
                {
                    var storyNode = DebugInfo.Nodes[node.Frame.NodeId];
                    if (storyNode.RuleId != 0)
                    {
                        var rule = DebugInfo.Rules[storyNode.RuleId];
                        frame.Rule = rule;
                    }
                }

                if (node.Frame.Type == MsgFrame.Types.FrameType.Pushdown ||
                    node.Frame.Type == MsgFrame.Types.FrameType.Insert ||
                    node.Frame.Type == MsgFrame.Types.FrameType.Delete)
                {
                    // Rule variable info is only propagated through Pushdown nodes.
                    // All other nodes either have no variable info at all, or contain
                    // local tuples used for DB insert/delete/query.

                    // We'll keep the variables from Insert/Delete nodes if there are
                    // no better rule candidates, as they show the PROC/DB input tuple.
                    frame.Variables = node.Variables;
                }

                if (node.Frame.Type == MsgFrame.Types.FrameType.Insert ||
                    node.Frame.Type == MsgFrame.Types.FrameType.Delete)
                {
                    // We'll keep the initial argument list that was passed to the PROC/QRY/DB
                    // (from the initial Insert/Delete frame) to display in the call frame name
                    frame.CallArguments = node.Frame.Tuple;
                }
            }

            if (frame.Variables == null)
            {
                frame.Variables = new List <DebugVariable>();
            }

            frame.Name = Formatter.GetFrameName(frame.Frame, frame.CallArguments);

            // Special indicator for backward propagation of database inserts/deletes
            if (range.Count >= 2 &&
                (range[0].Frame.Type == MsgFrame.Types.FrameType.Insert ||
                 range[0].Frame.Type == MsgFrame.Types.FrameType.Delete) &&
                (range[1].Frame.Type == MsgFrame.Types.FrameType.Pushdown ||
                 range[1].Frame.Type == MsgFrame.Types.FrameType.PushdownDelete))
            {
                var pushdownNode = DebugInfo.Nodes[range[1].Frame.NodeId];
                if (range[0].Frame.NodeId != pushdownNode.ParentNodeId)
                {
                    frame.Name = "(Database Propagation) " + frame.Name;
                }
            }

            return(frame);
        }
        public void EvaluateCall(DAPRequest request, Statement stmt, CoalescedFrame frame, bool allowMutation)
        {
            NodeDebugInfo node;
            var           func = new FunctionNameAndArity(stmt.Name, stmt.Params.Count);

            if (!NameToNodeMap.TryGetValue(func, out node))
            {
                DAP.SendReply(request, "Name not found: " + func);
                return;
            }

            var function = DebugInfo.Functions[node.FunctionName];
            var args     = ParamsToTuple(stmt.Params, frame);

            DbgEvaluate.Types.EvalType evalType;
            switch (node.Type)
            {
            case Node.Type.Database:
                if (stmt.Not)
                {
                    evalType = DbgEvaluate.Types.EvalType.Insert;
                }
                else
                {
                    evalType = DbgEvaluate.Types.EvalType.Delete;
                }
                break;

            case Node.Type.Proc:
                if (stmt.Not)
                {
                    throw new RequestFailedException("\"NOT\" statements not supported for PROCs");
                }

                evalType = DbgEvaluate.Types.EvalType.Insert;
                break;

            case Node.Type.DivQuery:
            case Node.Type.InternalQuery:
            case Node.Type.UserQuery:
                if (stmt.Not)
                {
                    throw new RequestFailedException("\"NOT\" statements not supported for QRYs");
                }

                evalType = DbgEvaluate.Types.EvalType.IsValid;
                break;

            default:
                throw new RequestFailedException($"Eval node type not supported: {node.Type}");
            }

            if ((evalType != DbgEvaluate.Types.EvalType.IsValid ||
                 node.Type == Node.Type.UserQuery) &&
                !allowMutation)
            {
                throw new RequestFailedException($"Evaluation could cause game state change");
            }

            UInt32 seq = DbgClient.SendEvaluate(evalType, node.Id, args);

            var argNames = function.Params.Select(arg => arg.Name).ToList();
            var eval     = new PendingExpressionEvaluation
            {
                Request  = request,
                Results  = EvalResults.MakeResults(function.Params.Count, argNames),
                Node     = node,
                Function = function
            };

            PendingEvaluations.Add(seq, eval);
        }