Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions rust/contracts/rust-core-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2114,6 +2114,16 @@ deduplicates by reference and offset, keeps the set in the session, and sends DA
session and never persist or replay it: an address is only valid for one run of
one process.

`debug.setBreakpointsMuted { sessionId, muted }` mutes or unmutes every
breakpoint stored for one session and returns the session update. While muted,
Core keeps the stored source, exception, function, instruction and data sets and
keeps accepting replacements, but sends each kind empty, so the adapter stops on
none of them. Toggling resends, after `initialized`, only the kinds that hold an
enabled entry (source paths one by one); repeating the current state sends
nothing. A child session starts muted when its parent is. Clients call it for
every session, including one they start while muted, right after
`debug.createSession` and before its first breakpoint set.

`debug.execute` accepts an optional `granularity` of `statement`, `line`, or
`instruction` on `next`, `stepIn`, `stepOut`, and `stepBack`. Core forwards it as
the DAP `granularity` argument only when the adapter advertised
Expand Down Expand Up @@ -3337,6 +3347,23 @@ module selector. Generated fingerprints include both project inputs and the
detector revision; either changing marks the detected layer stale and requires
regeneration.

`runConfig.decideTitleAction { action, trigger, currentFileChosen, settled, configuration? }`
decides what a press on the title bar's Run or Debug button does. `action` is
`run` or `debug`; `trigger` is `press`, or `settled` when the host asks again for
a press it kept. `settled` says the configurations are read (detection answered,
no model is loading and the configurations were resolved since). `configuration`
describes the configuration the title bar shows, absent when the project has
none: `{ kind: "unsupported" }` (it does not start through a build provider),
`{ kind: "instanceMissing" }`, `{ kind: "modelNotLoaded", failed }` or
`{ kind: "modelLoaded", debuggable }` (its run declares a debug adapter). It
returns `{ kind }`: `currentFile`, `wait` (keep the press until the
configurations are read), `loadModel` (load the instance's model and keep the
press), `unsupported`, `targetMissing`, `modelFailed`, `run`, `debug` or
`notDebuggable`. A model whose last load failed is loaded again only on a press,
so a settled answer that failed again ends with `modelFailed` instead of looping.
Hosts own the loads, the kept press and the notices.
Cases: `rust/fixtures/run-configuration/title-action-v1.json`.

`runConfig.selectContextConfiguration` accepts a provider-supplied `sourcePath`
and opaque `symbol`, a `preferredId`, platform path-comparison policy, and an
ordered list of effective candidate IDs, identities, disabled state, and
Expand Down Expand Up @@ -5635,6 +5662,26 @@ message is returned. Transport failures are never treated as a boundary.
Addresses are never parsed or offset by Core or hosts. Hosts keep the viewport,
scroll anchor and rendering. Cases: `rust/fixtures/debug/disassembly-paging-v1.json`.

### Memory window

`debug.planMemoryWindow { address }` places the memory view's bounded `readMemory`
window at the address the user typed or the selected frame's instruction pointer.
It returns `{ address, request }`, where `request` is the exact DAP `readMemory`
arguments `{ memoryReference, offset, count }` and `address` is what the address
field shows. An address written as a number (`0x` hexadecimal or decimal, as DAP
writes a `readMemory` reply's `address`) is shown in lowercase `0x` hexadecimal and
read from the 64-byte boundary at or below it, so its byte sits in its row for every
bytes-per-row choice (8, 16, 32 and 64). Any other reference is opaque: it is
trimmed, read from offset 0 and never parsed. Empty, multi-line or overlong
addresses are rejected. `debug.locateMemoryWindow { window, start?, length }`
takes the window unchanged with the reply's `address` and returned byte count and
returns `{ base?, mark? }`: `base` is the first returned byte's address in lowercase
`0x` hexadecimal (rows are labelled with their offset added to it, or with their
offset in the window when it is absent) and `mark` is the index of the byte at the
address read, absent when the reply does not hold it. A window that does not match
its address is rejected. Addresses cross the protocol as strings. Hosts own the
transport, rows, radix and scrolling. Cases: `rust/fixtures/debug/memory-window-v1.json`.

### Windows source-breakpoint relocation

Windows forwards the editor's exact source-preserving edits to
Expand Down
78 changes: 78 additions & 0 deletions rust/fixtures/debug/memory-window-v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"description": "debug.planMemoryWindow places the memory view's readMemory window at an address; debug.locateMemoryWindow finds where each reply's bytes start and which of them is at the address read. A number (0x hexadecimal or decimal) is read from the multiple of 64 at or below it; any other reference is opaque and read from where it points. Addresses are strings because a 64-bit address can exceed the JavaScript safe integer range.",
"scenarios": [
{
"name": "hexadecimal address inside an aligned row",
"address": "0x2010",
"window": {
"address": "0x2010",
"request": { "memoryReference": "0x2010", "offset": -16, "count": 1024 }
},
"replies": [
{ "start": "0x2000", "length": 1024, "expected": { "base": "0x2000", "mark": 16 } },
{ "length": 1024, "expected": { "base": "0x2000", "mark": 16 } },
{ "start": "0x2008", "length": 1024, "expected": { "base": "0x2008", "mark": 8 } },
{ "start": "0x2000", "length": 8, "expected": { "base": "0x2000" } },
{ "start": "0x2020", "length": 1024, "expected": { "base": "0x2020" } }
]
},
{
"name": "decimal address and decimal reply start",
"address": "8208",
"window": {
"address": "0x2010",
"request": { "memoryReference": "0x2010", "offset": -16, "count": 1024 }
},
"replies": [
{ "start": "8192", "length": 1024, "expected": { "base": "0x2000", "mark": 16 } }
]
},
{
"name": "uppercase prefix, padding and surrounding spaces",
"address": " 0X0000ABCD ",
"window": {
"address": "0xabcd",
"request": { "memoryReference": "0xabcd", "offset": -13, "count": 1024 }
},
"replies": [
{ "start": "0x000000000000ABC0", "length": 1024, "expected": { "base": "0xabc0", "mark": 13 } }
]
},
{
"name": "opaque reference is read from where it points",
"address": "frame:3:ip",
"window": {
"address": "frame:3:ip",
"request": { "memoryReference": "frame:3:ip", "offset": 0, "count": 1024 }
},
"replies": [
{ "start": "0x7ff0", "length": 1024, "expected": { "base": "0x7ff0", "mark": 0 } },
{ "length": 1024, "expected": { "mark": 0 } },
{ "length": 0, "expected": {} }
]
},
{
"name": "address zero",
"address": "0",
"window": {
"address": "0x0",
"request": { "memoryReference": "0x0", "offset": 0, "count": 1024 }
},
"replies": [
{ "length": 0, "expected": { "base": "0x0" } },
{ "length": 1024, "expected": { "base": "0x0", "mark": 0 } }
]
},
{
"name": "highest 64-bit address",
"address": "0xffffffffffffffff",
"window": {
"address": "0xffffffffffffffff",
"request": { "memoryReference": "0xffffffffffffffff", "offset": -63, "count": 1024 }
},
"replies": [
{ "start": "0xffffffffffffffc0", "length": 64, "expected": { "base": "0xffffffffffffffc0", "mark": 63 } }
]
}
]
}
217 changes: 217 additions & 0 deletions rust/fixtures/run-configuration/title-action-v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
{
"version": 1,
"expected": { "providerNeutral": true, "failedModelLoadsAgainOnlyOnPress": true },
"description": "runConfig.decideTitleAction: what a press on the title bar's Run or Debug button does, from the facts the host reports. The host keeps a press answered wait or loadModel and asks again with trigger settled once the configurations or the model arrive.",
"cases": [
{
"name": "current file chosen acts at once, even before the configurations are read",
"request": {
"action": "debug",
"trigger": "press",
"currentFileChosen": true,
"settled": false
},
"expected": {
"kind": "currentFile"
}
},
{
"name": "a press before the configurations are read waits",
"request": {
"action": "run",
"trigger": "press",
"currentFileChosen": false,
"settled": false,
"configuration": {
"kind": "modelLoaded",
"debuggable": true
}
},
"expected": {
"kind": "wait"
}
},
{
"name": "an answer that leaves the configurations unread keeps waiting",
"request": {
"action": "run",
"trigger": "settled",
"currentFileChosen": false,
"settled": false
},
"expected": {
"kind": "wait"
}
},
{
"name": "a project without configurations acts on the current file",
"request": {
"action": "run",
"trigger": "press",
"currentFileChosen": false,
"settled": true
},
"expected": {
"kind": "currentFile"
}
},
{
"name": "a configuration that does not start through a build provider",
"request": {
"action": "run",
"trigger": "press",
"currentFileChosen": false,
"settled": true,
"configuration": {
"kind": "unsupported"
}
},
"expected": {
"kind": "unsupported"
}
},
{
"name": "an undetected build instance",
"request": {
"action": "run",
"trigger": "press",
"currentFileChosen": false,
"settled": true,
"configuration": {
"kind": "instanceMissing"
}
},
"expected": {
"kind": "targetMissing"
}
},
{
"name": "an unloaded model is loaded first",
"request": {
"action": "run",
"trigger": "press",
"currentFileChosen": false,
"settled": true,
"configuration": {
"kind": "modelNotLoaded",
"failed": false
}
},
"expected": {
"kind": "loadModel"
}
},
{
"name": "a press loads a model that failed before once more",
"request": {
"action": "run",
"trigger": "press",
"currentFileChosen": false,
"settled": true,
"configuration": {
"kind": "modelNotLoaded",
"failed": true
}
},
"expected": {
"kind": "loadModel"
}
},
{
"name": "a model that fails again ends the waiting press",
"request": {
"action": "run",
"trigger": "settled",
"currentFileChosen": false,
"settled": true,
"configuration": {
"kind": "modelNotLoaded",
"failed": true
}
},
"expected": {
"kind": "modelFailed"
}
},
{
"name": "a model that has not loaded yet after an answer is loaded",
"request": {
"action": "run",
"trigger": "settled",
"currentFileChosen": false,
"settled": true,
"configuration": {
"kind": "modelNotLoaded",
"failed": false
}
},
"expected": {
"kind": "loadModel"
}
},
{
"name": "run with a loaded model",
"request": {
"action": "run",
"trigger": "press",
"currentFileChosen": false,
"settled": true,
"configuration": {
"kind": "modelLoaded",
"debuggable": false
}
},
"expected": {
"kind": "run"
}
},
{
"name": "run once the waited model arrives",
"request": {
"action": "run",
"trigger": "settled",
"currentFileChosen": false,
"settled": true,
"configuration": {
"kind": "modelLoaded",
"debuggable": true
}
},
"expected": {
"kind": "run"
}
},
{
"name": "debug a run that declares a debug adapter",
"request": {
"action": "debug",
"trigger": "press",
"currentFileChosen": false,
"settled": true,
"configuration": {
"kind": "modelLoaded",
"debuggable": true
}
},
"expected": {
"kind": "debug"
}
},
{
"name": "debug a run that declares no debug adapter",
"request": {
"action": "debug",
"trigger": "press",
"currentFileChosen": false,
"settled": true,
"configuration": {
"kind": "modelLoaded",
"debuggable": false
}
},
"expected": {
"kind": "notDebuggable"
}
}
]
}
Loading
Loading