public async Task <IReadOnlyList <DebugStackFrame> > GetStackFramesAsync(CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); await TaskUtilities.SwitchToBackgroundThread(); await InitializeAsync(cancellationToken); var jFrames = await InvokeDebugHelperAsync <JArray>("rtvs:::describe_traceback()", cancellationToken); Trace.Assert(jFrames.All(t => t is JObject), "rtvs:::describe_traceback(): array of objects expected.\n\n" + jFrames); var stackFrames = new List <DebugStackFrame>(); DebugStackFrame lastFrame = null; int i = 0; foreach (JObject jFrame in jFrames) { var fallbackFrame = (_bpHitFrame != null && _bpHitFrame.Index == i) ? _bpHitFrame : null; lastFrame = new DebugStackFrame(this, i, lastFrame, jFrame, fallbackFrame); stackFrames.Add(lastFrame); ++i; } return(stackFrames); }
internal static DebugEvaluationResult Parse(DebugStackFrame stackFrame, string name, JObject json) { var expression = json.Value <string>("expression"); var errorText = json.Value <string>("error"); if (errorText != null) { return(new DebugErrorEvaluationResult(stackFrame, expression, name, errorText)); } var code = json.Value <string>("promise"); if (code != null) { return(new DebugPromiseEvaluationResult(stackFrame, expression, name, code)); } var isActiveBinding = json.Value <bool?>("active_binding"); if (isActiveBinding == true) { return(new DebugActiveBindingEvaluationResult(stackFrame, expression, name)); } return(new DebugValueEvaluationResult(stackFrame, expression, name, json)); }
internal DebugStackFrame(DebugSession session, int index, DebugStackFrame callingFrame, JObject jFrame, DebugStackFrame fallbackFrame = null) { Session = session; Index = index; CallingFrame = callingFrame; FileName = jFrame.Value<string>("filename"); LineNumber = jFrame.Value<int?>("line_number"); Call = jFrame.Value<string>("call"); IsGlobal = jFrame.Value<bool?>("is_global") ?? false; var match = _doTraceRegex.Match(Call); if (match.Success) { FrameKind = DebugStackFrameKind.DoTrace; } if (fallbackFrame != null) { // If we still don't have the filename and line number, use those from the fallback frame. // This happens during breakpoint hit processing after the context is unwound from within // .doTrace back to the function that called it - because we no longer have .doTrace call, // we don't have the file/line information that came from it. But DebugSession will have // stashed it away when it had it, and then pass it as a fallback frame if index matches. FileName = FileName ?? fallbackFrame.FileName; LineNumber = LineNumber ?? fallbackFrame.LineNumber; } }
internal DebugStackFrame(DebugSession session, int index, DebugStackFrame callingFrame, JObject jFrame, DebugStackFrame fallbackFrame = null) { Session = session; Index = index; CallingFrame = callingFrame; FileName = jFrame.Value <string>("filename"); LineNumber = jFrame.Value <int?>("line_number"); Call = jFrame.Value <string>("call"); IsGlobal = jFrame.Value <bool?>("is_global") ?? false; var match = _doTraceRegex.Match(Call); if (match.Success) { FrameKind = DebugStackFrameKind.DoTrace; } if (fallbackFrame != null) { // If we still don't have the filename and line number, use those from the fallback frame. // This happens during breakpoint hit processing after the context is unwound from within // .doTrace back to the function that called it - because we no longer have .doTrace call, // we don't have the file/line information that came from it. But DebugSession will have // stashed it away when it had it, and then pass it as a fallback frame if index matches. FileName = FileName ?? fallbackFrame.FileName; LineNumber = LineNumber ?? fallbackFrame.LineNumber; } }
internal static DebugEvaluationResult Parse(DebugStackFrame stackFrame, string name, JObject json) { var expression = json.Value<string>("expression"); var errorText = json.Value<string>("error"); if (errorText != null) { return new DebugErrorEvaluationResult(stackFrame, expression, name, errorText); } var code = json.Value<string>("promise"); if (code != null) { return new DebugPromiseEvaluationResult(stackFrame, expression, name, code); } var isActiveBinding = json.Value<bool?>("active_binding"); if (isActiveBinding == true) { return new DebugActiveBindingEvaluationResult(stackFrame, expression, name); } return new DebugValueEvaluationResult(stackFrame, expression, name, json); }
public async Task <DebugEvaluationResult> EvaluateAsync( DebugStackFrame stackFrame, string expression, string name, string env, DebugEvaluationResultFields fields, int?reprMaxLength = null, CancellationToken cancellationToken = default(CancellationToken) ) { ThrowIfDisposed(); await TaskUtilities.SwitchToBackgroundThread(); await InitializeAsync(cancellationToken); env = env ?? stackFrame?.SysFrame ?? "NULL"; var code = Invariant($"rtvs:::toJSON(rtvs:::eval_and_describe({expression.ToRStringLiteral()}, {env},, {fields.ToRVector()},, {reprMaxLength}))"); var jEvalResult = await InvokeDebugHelperAsync <JObject>(code, cancellationToken); return(DebugEvaluationResult.Parse(stackFrame, name, jEvalResult)); }
public DebugErrorEvaluationResult(DebugStackFrame stackFrame, string expression, string name, string errorText) : base(stackFrame, expression, name) { ErrorText = errorText; }
internal DebugEvaluationResult(DebugStackFrame stackFrame, string expression, string name) { StackFrame = stackFrame; Expression = expression; Name = name; }
public DebugActiveBindingEvaluationResult(DebugStackFrame stackFrame, string expression, string name) : base(stackFrame, expression, name) { }
public DebugPromiseEvaluationResult(DebugStackFrame stackFrame, string expression, string name, string code) : base(stackFrame, expression, name) { Code = code; }
internal DebugValueEvaluationResult(DebugStackFrame stackFrame, string expression, string name, JObject json) : base(stackFrame, expression, name) { var repr = json["repr"]; if (repr != null) { _reprObj = repr as JObject; if (_reprObj == null) { throw new InvalidDataException(Invariant($"'repr' must be an object in:\n\n{json}")); } } TypeName = json.Value <string>("type"); Length = json.Value <int?>("length"); AttributeCount = json.Value <int?>("attr_count"); SlotCount = json.Value <int?>("slot_count"); NameCount = json.Value <int?>("name_count"); var classes = json.Value <JArray>("classes"); if (classes != null) { Classes = classes.Select(t => t.Value <string>()).ToArray(); } var dim = json.Value <JArray>("dim"); if (dim != null) { Dim = dim.Select(t => t.Value <int>()).ToArray(); } var kind = json.Value <string>("kind"); switch (kind) { case null: case "[[": Kind = DebugValueEvaluationResultKind.UnnamedItem; break; case "$": Kind = DebugValueEvaluationResultKind.NamedItem; break; case "@": Kind = DebugValueEvaluationResultKind.Slot; break; default: throw new InvalidDataException(Invariant($"Invalid kind '{kind}' in:\n\n{json}")); } var flags = json.Value <JArray>("flags")?.Select(v => v.Value <string>()); if (flags != null) { foreach (var flag in flags) { switch (flag) { case "atomic": Flags |= DebugValueEvaluationResultFlags.Atomic; break; case "recursive": Flags |= DebugValueEvaluationResultFlags.Recursive; break; case "has_parent_env": Flags |= DebugValueEvaluationResultFlags.HasParentEnvironment; break; default: throw new InvalidDataException(Invariant($"Unrecognized flag '{flag}' in:\n\n{json}")); } } } }
private async Task ProcessBrowsePromptWorker(IRSessionInteraction inter) { var frames = await GetStackFramesAsync(); // If there's .doTrace(rtvs:::breakpoint) anywhere on the stack, we're inside the internal machinery // that triggered Browse> prompt when hitting a breakpoint. We need to step out of it until we're // back at the frame where the breakpoint was actually set, so that those internal frames do not show // on the call stack, and further stepping does not try to step through them. // Since browserSetDebug-based step out is not reliable in the presence of loops, we'll just keep // stepping over with "n" until we're all the way out. Every step will trigger a new prompt, and // we will come back to this method again. var doTraceFrame = frames.FirstOrDefault(frame => frame.FrameKind == DebugStackFrameKind.DoTrace); if (doTraceFrame != null) { // Save the .doTrace frame so that we can report file / line number info correctly later, once we're fully stepped out. // TODO: remove this hack when injected breakpoints get proper source info (#570). _bpHitFrame = doTraceFrame; await inter.RespondAsync(Invariant($"n\n")); return; } IReadOnlyCollection <DebugBreakpoint> breakpointsHit = null; var lastFrame = frames.LastOrDefault(); if (lastFrame != null) { // Report breakpoints first, so that by the time step completion is reported, all actions associated // with breakpoints (e.g. printing messages for tracepoints) have already been completed. if (lastFrame.FileName != null && lastFrame.LineNumber != null) { var location = new DebugBreakpointLocation(lastFrame.FileName, lastFrame.LineNumber.Value); DebugBreakpoint bp; if (_breakpoints.TryGetValue(location, out bp)) { bp.RaiseBreakpointHit(); breakpointsHit = Enumerable.Repeat(bp, bp.UseCount).ToArray(); } } } bool isStepCompleted = false; if (_stepTcs != null) { var stepTcs = _stepTcs; _stepTcs = null; stepTcs.TrySetResult(breakpointsHit == null || breakpointsHit.Count == 0); isStepCompleted = true; } EventHandler <DebugBrowseEventArgs> browse; lock (_browseLock) { browse = _browse; } var eventArgs = new DebugBrowseEventArgs(inter.Contexts, isStepCompleted, breakpointsHit); _currentBrowseEventArgs = eventArgs; browse?.Invoke(this, eventArgs); }
private void InterruptBreakpointHitProcessing() { _bpHitFrame = null; }
internal DebugValueEvaluationResult(DebugStackFrame stackFrame, string expression, string name, JObject json) : base(stackFrame, expression, name) { var repr = json["repr"]; if (repr != null) { _reprObj = repr as JObject; if (_reprObj == null) { throw new InvalidDataException(Invariant($"'repr' must be an object in:\n\n{json}")); } } TypeName = json.Value<string>("type"); Length = json.Value<int?>("length"); AttributeCount = json.Value<int?>("attr_count"); SlotCount = json.Value<int?>("slot_count"); NameCount = json.Value<int?>("name_count"); var classes = json.Value<JArray>("classes"); if (classes != null) { Classes = classes.Select(t => t.Value<string>()).ToArray(); } var dim = json.Value<JArray>("dim"); if (dim != null) { Dim = dim.Select(t => t.Value<int>()).ToArray(); } var kind = json.Value<string>("kind"); switch (kind) { case null: case "[[": Kind = DebugValueEvaluationResultKind.UnnamedItem; break; case "$": Kind = DebugValueEvaluationResultKind.NamedItem; break; case "@": Kind = DebugValueEvaluationResultKind.Slot; break; default: throw new InvalidDataException(Invariant($"Invalid kind '{kind}' in:\n\n{json}")); } var flags = json.Value<JArray>("flags")?.Select(v => v.Value<string>()); if (flags != null) { foreach (var flag in flags) { switch (flag) { case "atomic": Flags |= DebugValueEvaluationResultFlags.Atomic; break; case "recursive": Flags |= DebugValueEvaluationResultFlags.Recursive; break; case "has_parent_env": Flags |= DebugValueEvaluationResultFlags.HasParentEnvironment; break; default: throw new InvalidDataException(Invariant($"Unrecognized flag '{flag}' in:\n\n{json}")); } } } }