// Copyright 2017 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package starlark_debugging;

option java_package = "com.google.devtools.build.lib.starlarkdebugging";
option java_outer_classname = "StarlarkDebuggingProtos";

// A request sent by the debug client to the debug server.
message DebugRequest {
  // A number (intended to be sequentially generated by the client) that
  // identifies the request. The response sent by the server will contain the
  // same sequence number so that the client can synchronize its activity if
  // desired.
  int64 sequence_number = 1;

  // The payload describes the type of the request and its arguments, if any.
  oneof payload {
    SetBreakpointsRequest set_breakpoints = 101;
    ContinueExecutionRequest continue_execution = 102;
    EvaluateRequest evaluate = 103;
    ListFramesRequest list_frames = 104;
    StartDebuggingRequest start_debugging = 105;
    PauseThreadRequest pause_thread = 106;
    GetChildrenRequest get_children = 107;
  }
}

// A request to update the breakpoints used by the debug server.
message SetBreakpointsRequest {
  // The breakpoints that describe where the debug server should pause
  // evaluation.
  repeated Breakpoint breakpoint = 1;
}

// A request to continue execution on a paused or stepping thread. (A stepping
// thread is a thread that is running as the result of a previous
// ContinueExecutionRequest with non-NONE stepping.)
//
// A paused thread will be resumed with the given stepping, unless thread_id is
// 0. A stepping thread will continue to run with its stepping condition
// removed, as if it were already paused.
message ContinueExecutionRequest {
  // The identifier of the thread to continue. The thread must be paused or
  // stepping.
  //
  // If this field is not set (i.e., zero), then all threads will be continued
  // without stepping; the stepping field in this message will be ignored. This
  // is typically used when the debugger disconnects from the server.

  int64 thread_id = 1;

  // Describes the stepping behavior to use when continuing execution.
  Stepping stepping = 2;
}

// A request to evaluate a Starlark statement in a thread's current environment.
message EvaluateRequest {
  // The identifier of the thread in whose execution context the expression
  // should be evaluated.
  int64 thread_id = 1;

  // The Starlark statement to evaluate.
  string statement = 2;
}

// A request to list the stack frames of a thread.
message ListFramesRequest {
  // The identifier of the thread whose stack frames should be listed.
  int64 thread_id = 1;
}

// A request to begin the debugging session. Starlark execution will block until
// this request is made, to allow initial setup after the connection is
// established (e.g. setting breakpoints).
message StartDebuggingRequest {}

// A request to pause execution of a thread, or all threads.
message PauseThreadRequest {
  // The identifier of the thread to be paused.
  //
  // If not set (i.e. zero), all current Starlark threads will be paused, and
  // until a ContinueExecutionRequest is sent, any future Starlark threads will
  // also start off paused.
  int64 thread_id = 1;
}

// A request to list the children of a previously-communicated Value, such as
// its elements (for a list or dictionary), its fields (for a struct), and so
// forth.
message GetChildrenRequest {
  // The identifier of the relevant thread.
  int64 thread_id = 1;

  // The identifier of the value for which children are being requested. If the
  // value has no children, an empty list will be returned in
  // GetChildrenResponse.
  int64 value_id = 2;
}

// There are two kinds of events: "responses", which correspond to a
// DebugRequest sent by the client, and other asynchronous events that may be
// sent by the server to notify the client of activity in the Starlark code
// being debugged.
message DebugEvent {
  // If non-zero, this event is a response to a DebugRequest with the same
  // sequence number.
  int64 sequence_number = 1;

  // The payload describes the type of event and any additional information
  // about the event.
  oneof payload {
    Error error = 99;

    SetBreakpointsResponse set_breakpoints = 101;
    ContinueExecutionResponse continue_execution = 102;
    EvaluateResponse evaluate = 103;
    ListFramesResponse list_frames = 104;
    StartDebuggingResponse start_debugging = 105;
    PauseThreadResponse pause_thread = 106;
    GetChildrenResponse get_children = 107;

    ThreadPausedEvent thread_paused = 1001;
    ThreadContinuedEvent thread_continued = 1002;
  }
}

// A response that indicates that an error occurred while handling a debugging
// request.
message Error {
  // A message describing the error that occurred.
  string message = 1;
}

// The response to a SetBreakpointsRequest.
message SetBreakpointsResponse {}

// The response to a ContinueExecutionRequest.
message ContinueExecutionResponse {}

// The response to an EvaluateRequest.
message EvaluateResponse {
  // The result of evaluating a statement.
  Value result = 1;
}

// The response to a ListFramesRequest.
message ListFramesResponse {
  // The list of stack frames. The first element in the list represents the
  // topmost frame (that is, the current innermost function).
  repeated Frame frame = 1;
}

// The response to a StartDebuggingRequest.
message StartDebuggingResponse {}

// The response to a PauseThreadRequest. This is an acknowledgement that the
// request was received. Actual pausing of individual threads happens
// asynchronously, and will be communicated via ThreadPausedEvent(s).
message PauseThreadResponse {}

// The response to a GetChildrenRequest.
message GetChildrenResponse {
  repeated Value children = 1;
}

// An event indicating that a thread was paused during execution.
message ThreadPausedEvent {
  // The thread that was paused.
  PausedThread thread = 1;
}

// An event indicating that a thread has continued execution after being paused.
message ThreadContinuedEvent {
  // The identifier of the thread that continued executing.
  int64 thread_id = 1;
}

// A location where the debug server will pause execution.
message Breakpoint {
  oneof condition {
    // A breakpoint that is triggered when a particular line is reached.
    // Column index will be ignored for breakpoints. The debugger only supports
    // one breakpoint per line. If multiple breakpoints are supplied for a
    // single line, only the last such breakpoint is accepted.
    Location location = 1;
  }
  // An optional condition for the breakpoint. When present, the breakpoint will
  // be triggered iff both the primary condition holds and this expression
  // evaluates to True. It is unspecified how many times this expression will be
  // evaluated, so it should be free of side-effects.
  string expression = 2;
}

// A single frame in a thread's stack trace.
message Frame {
  // The name of the function that this frame represents.
  string function_name = 1;

  // The scopes that contain value bindings accessible in this frame.
  repeated Scope scope = 2;

  // The source location where the frame is currently paused. May not be set in
  // some situations.
  Location location = 3;
}

// A location in Starlark source code.
message Location {
  // The path of the Starlark source file.
  string path = 1;

  // A 1-indexed line number in the file denoted by path.
  uint32 line_number = 2;

  // A 1-indexed column number in the file denoted by path. 0 (/unset) indicates
  // column number is unknown or irrelevant.
  uint32 column_number = 3;
}

// A scope that contains value bindings accessible in a frame.
message Scope {
  // A human-readable name of the scope, such as "global" or "local".
  string name = 1;

  // The variable bindings that are defined in this scope.
  repeated Value binding = 2;
}

// Describes the stepping behavior that should occur when execution of a thread
// is continued.
enum Stepping {
  // Do not step; continue execution until it completes or is paused for some
  // other reason (such as hitting another breakpoint).
  NONE = 0;

  // If the thread is paused on a statement that contains a function call,
  // step into that function. Otherwise, this is the same as OVER.
  INTO = 1;

  // Step over the next statement and any functions that it may call.
  OVER = 2;

  // Continue execution until the current function has been exited and then
  // pause.
  OUT = 3;
}

// Information about a paused Starlark thread.
message PausedThread {
  // The identifier of the thread.
  int64 id = 1;

  // A descriptive name for the thread that can be displayed in the debugger's
  // UI.
  string name = 2;

  PauseReason pause_reason = 3;

  // The location in Starlark code of the next statement or expression that will
  // be executed.
  Location location = 4;

  // An error that occurred while evaluating a breakpoint condition. Present if
  // and only if pause_reason is CONDITIONAL_BREAKPOINT_ERROR.
  Error conditional_breakpoint_error = 5;
}

// The reason why a thread was paused.
enum PauseReason {
  // The debug server hasn't set any reason.
  UNSET = 0;

  // The stepping condition in a ContinueExecutionRequest was hit.
  STEPPING = 1;

  // A PauseThreadRequest was sent with thread_id=0.
  ALL_THREADS_PAUSED = 2;

  // A PauseThreadRequest was sent with thread_id matching this thread.
  PAUSE_THREAD_REQUEST = 3;

  // A breakpoint was hit.
  HIT_BREAKPOINT = 4;

  // An error occurred while evaluating a breakpoint condition.
  CONDITIONAL_BREAKPOINT_ERROR = 5;

  // Debugging just started, and a StartDebuggingRequest has not yet been
  // received and processed.
  INITIALIZING = 6;
}

// The debugger representation of a Starlark value.
message Value {
  // A label that describes this value's location or source in a value
  // hierarchy.
  //
  // For example, in a stack frame, the label would be the name of the variable
  // to which the value is bound. For a value that is an element of a list, its
  // its label would be its subscript, such as "[4]". A value that is a field in
  // a struct would use the field's name as its label, and so forth.
  string label = 1;

  // A string description of the value.
  string description = 2;

  // A string describing the type of the value.
  //
  // This field may be omitted if the value does not correspond to a "real" type
  // as far as the debugging view is concerned; for example, dictionaries will
  // be rendered as sequences of key/value pairs ("entries") but the entries
  // themselves do not have a meaningful type with respect to our rendering.
  string type = 3;

  // Will be false if the value is known to have no children. May sometimes be
  // true if this isn't yet known, in which case GetChildrenResponse#children
  // will be empty.
  bool has_children = 4;

  // An identifier for this value, used to request its children. The same value
  // may be known by multiple ids. Not set for values without children.
  int64 id = 5;
}
