Bug 1862693 - Use 1-origin column number in debugger API. r=iain,ochameau,devtools-reviewers

This includes the following API:
  * Debugger.Object.prototype.createSource
     * startColumn field of the parameter object
  * Debugger.Script.prototype.startColumn
     * return value
  * Debugger.Script.prototype.getPossibleBreakpoints
     * minColumn and maxColumn property of the query
     * columnNumber property of the returned array elements
  * Debugger.Script.prototype.getOffsetMetadata
     * columnNumber property of the returned array elements
  * Debugger.Script.prototype.getOffsetLocation
     * columnNumber property of the returned array elements
  * Debugger.Script.prototype.getAllColumnOffsets
     * columnNumber property of the returned array elements
  * Debugger.Script.prototype.getOffsetsCoverage
     * columnNumber property of the returned array elements
  * Debugger.Source.prototype.startColumn
     * return value

This patch modifies DevTools code to convert the column number from/to 1-origin,
while keep using 0-origin on their side.

One exception is the WASM's column number, which had been using 1-origin 1.
Each consumer in DevTools handles the WASM case, and the code can be removed
once DevTools internal also switches to 1-origin column number.

The other exception is to use 1-based column number in logCustomFormatterError,
which is folded from bug 1864783 patch.

Differential Revision: https://phabricator.services.mozilla.com/D193270
This commit is contained in:
Tooru Fujisawa
2023-11-22 12:31:30 +00:00
parent 7f27df46b7
commit 61764d0533
33 changed files with 226 additions and 172 deletions

View File

@@ -27,10 +27,10 @@ export function createLocation({
sourceActor,
sourceActorId: sourceActor?.id,
// `line` is 1-based while `column` is 0-based.
// `line` and `column` are 1-based.
// This data is mostly coming from and driven by
// JSScript::lineno and JSScript::column
// https://searchfox.org/mozilla-central/rev/d81e60336d9f498ad3985491dc17c2b77969ade4/js/src/vm/JSScript.h#1544-1547
// https://searchfox.org/mozilla-central/rev/90dce6b0223b4dc17bb10f1125b44f70951585f9/js/src/vm/JSScript.h#1545-1548
line,
column,
};

View File

@@ -47,7 +47,7 @@ async function testHeaderNotReturningJsonMl(hud) {
info(`Test for "header" not returning JsonML`);
await testCustomFormatting(hud, {
messageText: `Custom formatter failed: devtoolsFormatters[1].header should return an array, got number`,
source: "test-console-custom-formatters-errors.html:19:18",
source: "test-console-custom-formatters-errors.html:19:19",
});
}

View File

@@ -174,7 +174,7 @@ The functions described below may only be called with a ``this`` value referring
The elements of the array are objects, each of which describes a single entry point, and contains the following properties:
- lineNumber: the line number for which offset is an entry point
- columnNumber: the column number for which offset is an entry point
- columnNumber: the 1-based column number for which offset is an entry point
- offset: the bytecode instruction offset of the entry point
@@ -191,10 +191,10 @@ The functions described below may only be called with a ``this`` value referring
.. code-block:: javascript
[{ lineNumber: 0, columnNumber: 0, offset: 0 },
{ lineNumber: 1, columnNumber: 5, offset: 5 },
{ lineNumber: 1, columnNumber: 10, offset: 20 },
{ lineNumber: 3, columnNumber: 4, offset: 10 }]
[{ lineNumber: 0, columnNumber: 1, offset: 0 },
{ lineNumber: 1, columnNumber: 6, offset: 5 },
{ lineNumber: 1, columnNumber: 11, offset: 20 },
{ lineNumber: 3, columnNumber: 5, offset: 10 }]
**If the instance refers to WebAssembly code**, throw a ``TypeError``.
@@ -205,7 +205,7 @@ The functions described below may only be called with a ``this`` value referring
**If the instance refers to a JSScript**, return an object describing the source code location responsible for the bytecode at *offset* in this script. The object has the following properties:
- lineNumber: the line number for which offset is an entry point
- columnNumber: the column number for which offset is an entry point
- columnNumber: the 1-based column number for which offset is an entry point
- isEntryPoint: true if the offset is a column entry point, as would be reported by getAllColumnOffsets(); otherwise false.
@@ -213,7 +213,7 @@ The functions described below may only be called with a ``this`` value referring
**If the instance refers to a JSScript**, return ``null`` or an array which contains information about the coverage of all opcodes. The elements of the array are objects, each of which describes a single opcode, and contains the following properties:
- lineNumber: the line number of the current opcode.
- columnNumber: the column number of the current opcode.
- columnNumber: the 1-based column number of the current opcode.
- offset: the bytecode instruction offset of the current opcode.
- count: the number of times the current opcode got executed.

View File

@@ -238,8 +238,6 @@ The functions described below may only be called with a ``this`` value referring
The scripts ``source`` property must be equal to this value.
``line``
The script must at least partially cover the given source line. If this property is present, the ``url`` property must be present as well.
``column``
The script must include given column on the line given by the ``line`` property. If this property is present, the ``url`` and ``line`` properties must both be present as well.
``innermost``
If this property is present and true, the script must be the innermost script covering the given source location; scripts of enclosing code are omitted.
``global``

View File

@@ -968,8 +968,13 @@ class EventCollector {
if (script) {
const scriptSource = script.source.text;
// NOTE: Debugger.Script.prototype.startColumn is 1-based.
// Convert to 0-based, while keeping the wasm's column (1) as is.
// (bug 1863878)
const columnBase = script.format === "wasm" ? 0 : 1;
line = script.startLine;
column = script.startColumn;
column = script.startColumn - columnBase;
url = script.url;
const actor = this.targetActor.sourcesManager.getOrCreateSourceActor(
script.source

View File

@@ -506,10 +506,15 @@ class NodeActor extends Actor {
return undefined;
}
// NOTE: Debugger.Script.prototype.startColumn is 1-based.
// Convert to 0-based, while keeping the wasm's column (1) as is.
// (bug 1863878)
const columnBase = customElementDO.script.format === "wasm" ? 0 : 1;
return {
url: customElementDO.script.url,
line: customElementDO.script.startLine,
column: customElementDO.script.startColumn,
column: customElementDO.script.startColumn - columnBase,
};
}

View File

@@ -156,10 +156,14 @@ const previewers = {
grip.isGenerator = obj.isGeneratorFunction;
if (obj.script) {
// NOTE: Debugger.Script.prototype.startColumn is 1-based.
// Convert to 0-based, while keeping the wasm's column (1) as is.
// (bug 1863878)
const columnBase = obj.script.format === "wasm" ? 0 : 1;
grip.location = {
url: obj.script.url,
line: obj.script.startLine,
column: obj.script.startColumn,
column: obj.script.startColumn - columnBase,
};
}

View File

@@ -193,6 +193,11 @@ class SourceActor extends Actor {
introductionType = "scriptElement";
}
// NOTE: Debugger.Source.prototype.startColumn is 1-based.
// Convert to 0-based, while keeping the wasm's column (1) as is.
// (bug 1863878)
const columnBase = source.introductionType === "wasm" ? 0 : 1;
return {
actor: this.actorID,
extensionName: this.extensionName,
@@ -206,7 +211,7 @@ class SourceActor extends Actor {
introductionType,
isInlineSource: this._isInlineSource,
sourceStartLine: source.startLine,
sourceStartColumn: source.startColumn,
sourceStartColumn: source.startColumn - columnBase,
sourceLength: source.text?.length,
};
}
@@ -411,10 +416,15 @@ class SourceActor extends Actor {
return false;
}
// NOTE: Debugger.Script.prototype.startColumn is 1-based.
// Convert to 0-based, while keeping the wasm's column (1) as is.
// (bug 1863878)
const columnBase = script.format === "wasm" ? 0 : 1;
if (
script.startLine > endLine ||
script.startLine + lineCount <= startLine ||
(script.startLine == endLine && script.startColumn > endColumn)
(script.startLine == endLine &&
script.startColumn - columnBase > endColumn)
) {
return false;
}
@@ -422,7 +432,7 @@ class SourceActor extends Actor {
if (
lineCount == 1 &&
script.startLine == startLine &&
script.startColumn + script.sourceLength <= startColumn
script.startColumn - columnBase + script.sourceLength <= startColumn
) {
return false;
}
@@ -469,20 +479,25 @@ class SourceActor extends Actor {
end: { line: endLine = Infinity, column: endColumn = Infinity } = {},
} = query || {};
// NOTE: Debugger.Script.prototype.startColumn is 1-based.
// Convert to 0-based, while keeping the wasm's column (1) as is.
// (bug 1863878)
const columnBase = script.format === "wasm" ? 0 : 1;
const offsets = script.getPossibleBreakpoints();
for (const { lineNumber, columnNumber } of offsets) {
if (
lineNumber < startLine ||
(lineNumber === startLine && columnNumber < startColumn) ||
(lineNumber === startLine && columnNumber - columnBase < startColumn) ||
lineNumber > endLine ||
(lineNumber === endLine && columnNumber >= endColumn)
(lineNumber === endLine && columnNumber - columnBase >= endColumn)
) {
continue;
}
positions.push({
line: lineNumber,
column: columnNumber,
column: columnNumber - columnBase,
});
}
}
@@ -610,6 +625,11 @@ class SourceActor extends Actor {
script => !actor.hasScript(script)
);
// NOTE: Debugger.Script.prototype.getPossibleBreakpoints returns
// columnNumber in 1-based.
// The following code uses columnNumber only for comparing against
// other columnNumber, and we don't need to convert to 0-based.
// This is a line breakpoint, so we add a breakpoint on the first
// breakpoint on the line.
const lineMatches = [];
@@ -644,13 +664,19 @@ class SourceActor extends Actor {
);
for (const script of scripts) {
// NOTE: getPossibleBreakpoints's minColumn/maxColumn parameters are
// 1-based.
// Convert to 1-based, while keeping the wasm's column (1) as is.
// (bug 1863878)
const columnBase = script.format === "wasm" ? 0 : 1;
// Check to see if the script contains a breakpoint position at
// this line and column.
const possibleBreakpoint = script
.getPossibleBreakpoints({
line,
minColumn: column,
maxColumn: column + 1,
minColumn: column + columnBase,
maxColumn: column + columnBase + 1,
})
.pop();

View File

@@ -2254,7 +2254,11 @@ class ThreadActor extends Actor {
...content.substring(0, scriptStartOffset).matchAll("\n"),
];
const startLine = 1 + allLineBreaks.length;
// NOTE: Debugger.Source.prototype.startColumn is 1-based.
// Create 1-based column here for the following comparison,
// and also the createSource call below.
const startColumn =
1 +
scriptStartOffset -
(allLineBreaks.length ? allLineBreaks.at(-1).index - 1 : 0);
@@ -2270,6 +2274,7 @@ class ThreadActor extends Actor {
try {
const global = this.dbg.getDebuggees()[0];
// NOTE: Debugger.Object.prototype.createSource takes 1-based column.
this._addSource(
global.createSource({
text,

View File

@@ -149,8 +149,20 @@ class TracerActor extends Actor {
const { lineNumber, columnNumber } = script.getOffsetMetadata(frame.offset);
const url = script.source.url;
// NOTE: Debugger.Script.prototype.getOffsetMetadata returns
// columnNumber in 1-based.
// Convert to 0-based, while keeping the wasm's column (1) as is.
// (bug 1863878)
const columnBase = script.format === "wasm" ? 0 : 1;
// Ignore blackboxed sources
if (this.sourcesManager.isBlackBoxed(url, lineNumber, columnNumber)) {
if (
this.sourcesManager.isBlackBoxed(
url,
lineNumber,
columnNumber - columnBase
)
) {
return false;
}
@@ -185,7 +197,7 @@ class TracerActor extends Actor {
this.throttledConsoleMessages.push({
filename: url,
lineNumber,
columnNumber,
columnNumber: columnNumber - columnBase,
arguments: args,
styles: CONSOLE_ARGS_STYLES,
level: "logTrace",

View File

@@ -228,10 +228,14 @@ class SourcesManager extends EventEmitter {
*/
getScriptOffsetLocation(script, offset) {
const { lineNumber, columnNumber } = script.getOffsetMetadata(offset);
// NOTE: Debugger.Source.prototype.startColumn is 1-based.
// Convert to 0-based, while keeping the wasm's column (1) as is.
// (bug 1863878)
const columnBase = script.format === "wasm" ? 0 : 1;
return new SourceLocation(
this.createSourceActor(script.source),
lineNumber,
columnNumber
columnNumber - columnBase
);
}

View File

@@ -291,6 +291,11 @@ class JavaScriptTracer {
// but if DevTools are closed, stdout is the only way to log the traces.
if (shouldLogToStdout) {
const { script } = frame;
// NOTE: Debugger.Script.prototype.getOffsetMetadata returns
// columnNumber in 1-based.
// Convert to 0-based, while keeping the wasm's column (1) as is.
// (bug 1863878)
const columnBase = script.format === "wasm" ? 0 : 1;
const { lineNumber, columnNumber } = script.getOffsetMetadata(
frame.offset
);
@@ -305,7 +310,9 @@ class JavaScriptTracer {
// Use a special URL, including line and column numbers which Firefox
// interprets as to be opened in the already opened DevTool's debugger
const href = `${script.source.url}:${lineNumber}:${columnNumber}`;
const href = `${script.source.url}:${lineNumber}:${
columnNumber - columnBase
}`;
// Use special characters in order to print working hyperlinks right from the terminal
// See https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda

View File

@@ -1246,6 +1246,9 @@ bool DebuggerObject::CallData::createSource() {
if (!ToUint32(cx, v, &startColumn)) {
return false;
}
if (startColumn == 0) {
startColumn = 1;
}
if (!JS_GetProperty(cx, options, "sourceMapURL", &v)) {
return false;
@@ -1267,8 +1270,7 @@ bool DebuggerObject::CallData::createSource() {
JS::CompileOptions compileOptions(cx);
compileOptions.lineno = startLine;
compileOptions.column =
JS::ColumnNumberOneOrigin::fromZeroOrigin(startColumn);
compileOptions.column = JS::ColumnNumberOneOrigin(startColumn);
if (!JS::StringHasLatin1Chars(url)) {
JS_ReportErrorASCII(cx, "URL must be a narrow string");

View File

@@ -384,7 +384,7 @@ bool DebuggerScript::CallData::getStartColumn() {
return JS::LimitedColumnNumberOneOrigin(
JS::WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin);
});
args.rval().setNumber(column.zeroOriginValue());
args.rval().setNumber(column.oneOriginValue());
return true;
}
@@ -691,7 +691,7 @@ class DebuggerScript::GetPossibleBreakpointsMatcher {
return false;
}
value = NumberValue(colno.zeroOriginValue());
value = NumberValue(colno.oneOriginValue());
if (!DefineDataProperty(cx_, entry, cx_->names().columnNumber, value)) {
return false;
}
@@ -727,12 +727,7 @@ class DebuggerScript::GetPossibleBreakpointsMatcher {
}
bool parseColumnValue(HandleValue value,
JS::LimitedColumnNumberOneOrigin* result) {
uint32_t tmp;
if (!parseIntValueImpl(value, &tmp)) {
return false;
}
*result = JS::LimitedColumnNumberOneOrigin::fromZeroOrigin(tmp);
return true;
return parseIntValueImpl(value, result->addressOfValueForTranscode());
}
bool parseSizeTValue(HandleValue value, size_t* result) {
return parseIntValueImpl(value, result);
@@ -942,16 +937,7 @@ class DebuggerScript::GetPossibleBreakpointsMatcher {
for (uint32_t i = 0; i < offsets.length(); i++) {
uint32_t lineno = offsets[i].lineno;
// FIXME: wasm::ExprLoc::column contains "1". which is "1 in 1-origin",
// but currently the debugger API returns 0-origin column number,
// and the value becomes "0 in 0-origin".
// the existing wasm debug functionality expects the observable
// column number be "1", so it is "1 in 0-origin".
// Once the debugger API is rewritten to use 1-origin, this
// part also needs to be rewritten to directly pass the
// "1 in 1-origin" (bug 1863878).
JS::LimitedColumnNumberOneOrigin column =
JS::LimitedColumnNumberOneOrigin::fromZeroOrigin(offsets[i].column);
JS::LimitedColumnNumberOneOrigin column(offsets[i].column);
size_t offset = offsets[i].offset;
if (!maybeAppendEntry(offset, lineno, column, true)) {
return false;
@@ -1030,7 +1016,7 @@ class DebuggerScript::GetOffsetMetadataMatcher {
return false;
}
value = NumberValue(r.frontColumnNumber().zeroOriginValue());
value = NumberValue(r.frontColumnNumber().oneOriginValue());
if (!DefineDataProperty(cx_, result_, cx_->names().columnNumber, value)) {
return false;
}
@@ -1369,7 +1355,7 @@ class DebuggerScript::GetOffsetLocationMatcher {
return false;
}
value = NumberValue(column.zeroOriginValue());
value = NumberValue(column.oneOriginValue());
if (!DefineDataProperty(cx_, result_, cx_->names().columnNumber, value)) {
return false;
}
@@ -1837,7 +1823,7 @@ class DebuggerScript::GetAllColumnOffsetsMatcher {
return false;
}
value = NumberValue(column.zeroOriginValue());
value = NumberValue(column.oneOriginValue());
if (!DefineDataProperty(cx_, entry, cx_->names().columnNumber, value)) {
return false;
}
@@ -1906,9 +1892,7 @@ class DebuggerScript::GetAllColumnOffsetsMatcher {
for (uint32_t i = 0; i < offsets.length(); i++) {
uint32_t lineno = offsets[i].lineno;
// See the comment in GetPossibleBreakpointsMatcher::parseQuery.
JS::LimitedColumnNumberOneOrigin column =
JS::LimitedColumnNumberOneOrigin::fromZeroOrigin(offsets[i].column);
JS::LimitedColumnNumberOneOrigin column(offsets[i].column);
size_t offset = offsets[i].offset;
if (!appendColumnOffsetEntry(lineno, column, offset)) {
return false;
@@ -2429,8 +2413,7 @@ bool DebuggerScript::CallData::getOffsetsCoverage() {
offsetValue.setNumber(double(offset));
lineNumberValue.setNumber(double(r.frontLineNumber()));
columnNumberValue.setNumber(
double(r.frontColumnNumber().zeroOriginValue()));
columnNumberValue.setNumber(double(r.frontColumnNumber().oneOriginValue()));
countValue.setNumber(double(hits));
// Create a new object with the offset, line number, column number, the

View File

@@ -363,7 +363,7 @@ class DebuggerSourceGetStartColumnMatcher {
bool DebuggerSource::CallData::getStartColumn() {
DebuggerSourceGetStartColumnMatcher matcher;
JS::LimitedColumnNumberOneOrigin column = referent.match(matcher);
args.rval().setNumber(column.zeroOriginValue());
args.rval().setNumber(column.oneOriginValue());
return true;
}

View File

@@ -173,37 +173,36 @@ into the system; the entire string is not a valid URL. For
**If the instance refers to WebAssembly code**, throw a `TypeError`.
### `startLine`
**If the instance refers to a `JSScript`**, the number of the line at
**If the instance refers to a `JSScript`**, the 1-origin number of the line at
which this script's code starts, within the file or document named by
`url`.
### `startColumn`
**If the instance refers to a `JSScript`**, the zero-indexed number of the
column at which this script's code starts, within the file or document
named by `url`. For functions, this is the start of the function's
arguments:
**If the instance refers to a `JSScript`**, the 1-origin column number at
which this script's code starts, within the file or document named by `url`.
For functions, this is the start of the function's arguments:
```js
function f() { ... }
// ^ start (column 10)
// ^ start (column 11)
let g = x => x*x;
// ^ start (column 8)
// ^ start (column 9)
let h = (x) => x*x;
// ^ start (column 8)
// ^ start (column 9)
```
For default class constructors, it is the start of the `class` keyword:
```js
let MyClass = class { };
// ^ start (column 14)
// ^ start (column 15)
```
For scripts from other sources, such as `eval` or the `Function`
constructor, it is typically 0:
```js
let f = new Function(" console.log('hello world');");
// ^ start (column 0, from the string's perspective)
// ^ start (column 1, from the string's perspective)
```
### `lineCount`
**If the instance refers to a `JSScript`**, the number of lines this
**If the instance refers to a `JSScript`**, the 1-origin number of lines this
script's code occupies, within the file or document named by `url`.
### `source`
@@ -232,7 +231,7 @@ by `source`.
**If the instance refers to WebAssembly code**, throw a `TypeError`.
### `mainOffset`
**If the instance refers to a `JSScript`**, the offset of the main
**If the instance refers to a `JSScript`**, the 0-origin offset of the main
entry point of the script, excluding any prologue.
**If the instance refers to WebAssembly code**, throw a `TypeError`.
@@ -267,9 +266,9 @@ children can be reached by walking the tree.
### `getPossibleBreakpoints(query)`
Query for the recommended breakpoint locations available in SpiderMonkey.
Returns a result array of objects with the following properties:
* `offset: number` - The offset the breakpoint.
* `lineNumber: number` - The line number of the breakpoint.
* `columnNumber: number` - The column number of the breakpoint.
* `offset: number` - The 0-origin offset the breakpoint.
* `lineNumber: number` - The 1-origin line number of the breakpoint.
* `columnNumber: number` - The 1-origin column number of the breakpoint.
* `isStepStart: boolean` - True if SpiderMonkey recommends that the
breakpoint be treated as a step location when users of debuggers
step to the next item. This _roughly_ translates to the start of
@@ -278,13 +277,13 @@ Returns a result array of objects with the following properties:
The `query` argument can be used to filter the set of breakpoints.
The `query` object can contain the following properties:
* `minOffset: number` - The inclusive lower bound of `offset` values to include.
* `maxOffset: number` - The exclusive upper bound of `offset` values to include.
* `line: number` - Limit to breakpoints on the given line.
* `minLine: number` - The inclusive lower bound of lines to include.
* `minColumn: number` - The inclusive lower bound of the line/minLine column to include.
* `maxLine: number` - The exclusive upper bound of lines to include.
* `maxColumn: number` - The exclusive upper bound of the line/maxLine column to include.
* `minOffset: number` - The inclusive lower bound of `offset` values to include (0-origin).
* `maxOffset: number` - The exclusive upper bound of `offset` values to include (0-origin).
* `line: number` - Limit to breakpoints on the given line (1-origin).
* `minLine: number` - The inclusive lower bound of lines to include (1-origin).
* `minColumn: number` - The inclusive lower bound of the line/minLine column to include (1-origin).
* `maxLine: number` - The exclusive upper bound of lines to include (1-origin).
* `maxColumn: number` - The exclusive upper bound of the line/maxLine column to include (1-origin).
### `getPossibleBreakpointOffsets(query)`
Query for the recommended breakpoint locations available in SpiderMonkey.
@@ -292,10 +291,10 @@ Identical to getPossibleBreakpoints except this returns an array of `offset`
values instead of offset metadata objects.
### `getOffsetMetadata(offset)`
Get metadata about a given bytecode offset.
Get metadata about a given bytecode offset (0-origin).
Returns an object with the following properties:
* `lineNumber: number` - The line number of the breakpoint.
* `columnNumber: number` - The column number of the breakpoint.
* `lineNumber: number` - The 1-origin line number of the breakpoint.
* `columnNumber: number` - The 1-origin column number of the breakpoint.
* `isBreakpoint: boolean` - True if this offset qualifies as a breakpoint,
defined using the same semantics used for `getPossibleBreakpoints()`.
* `isStepStart: boolean` - True if SpiderMonkey recommends that the
@@ -305,8 +304,8 @@ Returns an object with the following properties:
### `setBreakpoint(offset, handler)`
**If the instance refers to a `JSScript`**, set a breakpoint at the
bytecode instruction at <i>offset</i> in this script, reporting hits to
the `hit` method of <i>handler</i>. If <i>offset</i> is not a valid offset
bytecode instruction at <i>offset</i> (0-origin) in this script, reporting hits
to the `hit` method of <i>handler</i>. If <i>offset</i> is not a valid offset
in this script, throw an error. Also, even if <i>offset</i> is valid offset
in this script, some instructions for engine-internal operation (e.g.
SetAliasedVar in the generator function initialization) don't allow setting
@@ -337,7 +336,7 @@ global's scripts.
### `getBreakpoints([offset])`
**If the instance refers to a `JSScript`**, return an array containing the
handler objects for all the breakpoints set at <i>offset</i> in this
handler objects for all the breakpoints set at <i>offset</i> (0-origin) in this
script. If <i>offset</i> is omitted, return the handlers of all
breakpoints set anywhere in this script. If <i>offset</i> is present, but
not a valid offset in this script, throw an error.
@@ -347,24 +346,24 @@ not a valid offset in this script, throw an error.
### `clearBreakpoint(handler, [offset])`
**If the instance refers to a `JSScript`**, remove all breakpoints set in
this [`Debugger`][debugger-object] instance that use <i>handler</i> as
their handler. If <i>offset</i> is given, remove only those breakpoints
set at <i>offset</i> that use <i>handler</i>; if <i>offset</i> is not a
valid offset in this script, throw an error.
their handler. If <i>offset</i> (0-origin) is given, remove only those
breakpoints set at <i>offset</i> that use <i>handler</i>; if <i>offset</i> is
not a valid offset in this script, throw an error.
Note that, if breakpoints using other handler objects are set at the
same location(s) as <i>handler</i>, they remain in place.
### `clearAllBreakpoints([offset])`
**If the instance refers to a `JSScript`**, remove all breakpoints set in
this script. If <i>offset</i> is present, remove all breakpoints set at
that offset in this script; if <i>offset</i> is not a valid bytecode
this script. If <i>offset</i> (0-origin) is present, remove all breakpoints set
at that offset in this script; if <i>offset</i> is not a valid bytecode
offset in this script, throw an error.
### `getEffectfulOffsets()`
**If the instance refers to a `JSScript`**, return an array
containing the offsets of all bytecodes in the script which can have direct
side effects that are visible outside the currently executing frame. This
includes, for example, operations that set properties or elements on
containing the 0-origin offsets of all bytecodes in the script which can have
direct side effects that are visible outside the currently executing frame.
This includes, for example, operations that set properties or elements on
objects, or that may set names in environments created outside the frame.
This doesn't include some instructions for engine-internal operation (e.g.
@@ -378,11 +377,11 @@ contains information about the coverage of all opcodes. The elements of
the array are objects, each of which describes a single opcode, and
contains the following properties:
* `lineNumber`: the line number of the current opcode.
* `lineNumber`: the 1-origin line number of the current opcode.
* `columnNumber`: the column number of the current opcode.
* `columnNumber`: the 1-origin column number of the current opcode.
* `offset`: the bytecode instruction offset of the current opcode.
* `offset`: the 0-origin bytecode instruction offset of the current opcode.
* `count`: the number of times the current opcode got executed.
@@ -393,8 +392,8 @@ the flag `Debugger.collectCoverageInfo` should be set to `true`.
**If the instance refers to WebAssembly code**, throw a `TypeError`.
### `isInCatchScope([offset])`
**If the instance refers to a `JSScript`**, this is `true` if this offset
falls within the scope of a try block, and `false` otherwise.
**If the instance refers to a `JSScript`**, this is `true` if this 0-origin
offset falls within the scope of a try block, and `false` otherwise.
**If the instance refers to WebAssembly code**, throw a `TypeError`.
@@ -407,7 +406,7 @@ in their results.
#### `getAllOffsets()`
**If the instance refers to a `JSScript`**, return an array <i>L</i>
describing the relationship between bytecode instruction offsets and
describing the relationship between bytecode instruction offsets (0-origin) and
source code positions in this script. <i>L</i> is sparse, and indexed by
source line number. If a source line number <i>line</i> has no code, then
<i>L</i> has no <i>line</i> property. If there is code for <i>line</i>,
@@ -426,20 +425,20 @@ for (i=1; i < 10; i++)
Calling `getAllOffsets()` on that code might yield an array like this:
```js
[[0], [5, 20], , [10]]
[, [0], [16, 75], , [52]]
```
This array indicates that:
* the first line's code starts at offset 0 in the script;
* the `for` statement head has two entry points at offsets 5 and 20 (for
* the `for` statement head has two entry points at offsets 16 and 75 (for
the initialization, which is performed only once, and the loop test,
which is performed at the start of each iteration);
* the third line has no code;
* and the fourth line begins at offset 10.
* and the fourth line begins at offset 52.
**If the instance refers to WebAssembly code**, throw a `TypeError`.
@@ -453,11 +452,11 @@ all offsets that are entry points for each (line, column) pair.
The elements of the array are objects, each of which describes a single
entry point, and contains the following properties:
* lineNumber: the line number for which offset is an entry point
* lineNumber: the 1-origin line number for which offset is an entry point
* columnNumber: the column number for which offset is an entry point
* columnNumber: the 1-origin column number for which offset is an entry point
* offset: the bytecode instruction offset of the entry point
* offset: the 0-origin bytecode instruction offset of the entry point
For example, suppose we have a script for the following source code:
@@ -471,10 +470,12 @@ for (i=1; i < 10; i++)
Calling `getAllColumnOffsets()` on that code might yield an array like this:
```js
[{ lineNumber: 0, columnNumber: 0, offset: 0 },
{ lineNumber: 1, columnNumber: 5, offset: 5 },
{ lineNumber: 1, columnNumber: 10, offset: 20 },
{ lineNumber: 3, columnNumber: 4, offset: 10 }]
[{ lineNumber: 1, columnNumber: 1, offset: 0 },
{ lineNumber: 2, columnNumber: 6, offset: 16 },
{ lineNumber: 2, columnNumber: 11, offset: 28 },
{ lineNumber: 4, columnNumber: 5, offset: 52 },
{ lineNumber: 4, columnNumber: 14, offset: 67 },
{ lineNumber: 2, columnNumber: 19, offset: 75 }]
```
**If the instance refers to WebAssembly code**, throw a `TypeError`.
@@ -482,17 +483,17 @@ Calling `getAllColumnOffsets()` on that code might yield an array like this:
#### `getLineOffsets(line)`
**If the instance refers to a `JSScript`**, return an array of bytecode
instruction offsets representing the entry points to source line
<i>line</i>. If the script contains no executable code at that line, the
array returned is empty.
<i>line</i> (1-origin). If the script contains no executable code at that line,
the array returned is empty.
#### `getOffsetLocation(offset)`
**If the instance refers to a `JSScript`**, return an object describing the
source code location responsible for the bytecode at <i>offset</i> in this
script. The object has the following properties:
* `lineNumber`: the line number for which offset is an entry point
* `lineNumber`: the 1-origin line number for which offset is an entry point
* `columnNumber`: the column number for which offset is an entry point
* `columnNumber`: the 1-origin column number for which offset is an entry point
* `isEntryPoint`: true if the offset is a column entry point, as
would be reported by getAllColumnOffsets(); otherwise false.

View File

@@ -111,9 +111,9 @@ source within the file or URL it was loaded from. This is normally `1`, but
may have another value if the source is part of an HTML document.
### `startColumn`
**If the instance refers to JavaScript source**, the 0-origin start column in
**If the instance refers to JavaScript source**, the 1-origin start column in
UTF-16 code units of the source within the file or URL it was loaded from. This
is normally `0`, but may have another value if the source is part of an HTML
is normally `1`, but may have another value if the source is part of an HTML
document.
### `id`

View File

@@ -398,12 +398,6 @@ instances for all debuggee scripts.
The script must at least partially cover the given source line. If this
property is present, the `url` property must be present as well.
* `column`
The script must include given column on the line given by the `line`property.
If this property is present, the `url` and `line` properties must both be
present as well.
* `innermost`
If this property is present and true, the script must be the innermost

View File

@@ -41,7 +41,7 @@ function assertOffsetColumns(code, expectedBpts, expectedOrdering = null) {
const { script } = debuggeeFn;
for (const offset of script.getAllColumnOffsets()) {
assertEq(offset.lineNumber, 1);
assertEq(offset.columnNumber < execCode.length, true);
assertEq(offset.columnNumber <= execCode.length, true);
bpts.add(offset.columnNumber);
script.setBreakpoint(offset.offset, {
@@ -53,7 +53,7 @@ function assertOffsetColumns(code, expectedBpts, expectedOrdering = null) {
global.f(3);
const actualBpts = Array.from(execCode, (_, i) => {
return bpts.has(i) ? "^" : " ";
return bpts.has(i + 1) ? "^" : " ";
}).join("");
if (actualBpts.trimEnd() !== expectedBpts.trimEnd()) {

View File

@@ -55,7 +55,7 @@ for (text of source.split("\n")) {
// Check lineno/column.
assertEq(getStartLine(className), lineno);
assertEq(getStartColumn(className), column);
assertEq(getStartColumn(className), column + 1);
// Check sourceStart/sourceEnd.
offset = source.indexOf("class " + className)

View File

@@ -55,4 +55,4 @@ C();
D();
new E();
`);
assertEq(g.log.join(","), "A,pop(8:0),B,pop(14:0),C,pop(20:0),D,pop(26:0),E,pop(27:16),pop(34:4)");
assertEq(g.log.join(","), "A,pop(8:1),B,pop(14:1),C,pop(20:1),D,pop(26:1),E,pop(27:17),pop(34:5)");

View File

@@ -8,13 +8,13 @@ let source = gdbg.createSource({
text: "x = 3",
url: "foo.js",
startLine: 3,
startColumn: 42,
startColumn: 43,
sourceMapURL: "sourceMapURL",
isScriptElement: true,
});
assertEq(source.text, "x = 3");
assertEq(source.url, "foo.js");
assertEq(source.startLine, 3);
assertEq(source.startColumn, 42);
assertEq(source.startColumn, 43);
assertEq(source.sourceMapURL, "sourceMapURL");
assertEq(source.introductionType, "inlineScript");

View File

@@ -29,31 +29,31 @@ function onDebuggerStatement(frame) {
assertBPCount({ line: 9 }, 1);
assertBPCount({ line: 10 }, 1);
assertBPCount({ line: 6, minColumn: 7 }, 3);
assertBPCount({ line: 6, maxColumn: 16 }, 3);
assertBPCount({ line: 6, minColumn: 7, maxColumn: 16 }, 2);
assertBPCount({ line: 6, minColumn: 8 }, 3);
assertBPCount({ line: 6, maxColumn: 17 }, 3);
assertBPCount({ line: 6, minColumn: 8, maxColumn: 17 }, 2);
assertBPError({ line: 1, minLine: 1 }, "line", "not allowed alongside 'minLine'/'maxLine'");
assertBPError({ line: 1, maxLine: 1 }, "line", "not allowed alongside 'minLine'/'maxLine'");
assertBPError({ line: "1" }, "line", "not an integer");
assertBPCount({ minLine: 9 }, 2);
assertBPCount({ minLine: 9, minColumn: 0 }, 2);
assertBPCount({ minLine: 9, minColumn: 8 }, 1);
assertBPCount({ minLine: 9, minColumn: 1 }, 2);
assertBPCount({ minLine: 9, minColumn: 9 }, 1);
assertBPError({ minLine: "1" }, "minLine", "not an integer");
assertBPError({ minColumn: 1 }, "minColumn", "not allowed without 'line' or 'minLine'");
assertBPError({ minLine: 1, minColumn: "1" }, "minColumn", "not an integer");
assertBPError({ minColumn: 2 }, "minColumn", "not allowed without 'line' or 'minLine'");
assertBPError({ minLine: 1, minColumn: "2" }, "minColumn", "not an integer");
assertBPCount({ maxLine: 7 }, 5);
assertBPCount({ maxLine: 7, maxColumn: 0 }, 5);
assertBPCount({ maxLine: 7, maxColumn: 8 }, 6);
assertBPCount({ maxLine: 7, maxColumn: 1 }, 5);
assertBPCount({ maxLine: 7, maxColumn: 9 }, 6);
assertBPError({ maxLine: "1" }, "maxLine", "not an integer");
assertBPError({ maxColumn: 1 }, "maxColumn", "not allowed without 'line' or 'maxLine'");
assertBPError({ maxLine: 1, maxColumn: "1" }, "maxColumn", "not an integer");
assertBPError({ maxColumn: 2 }, "maxColumn", "not allowed without 'line' or 'maxLine'");
assertBPError({ maxLine: 1, maxColumn: "2" }, "maxColumn", "not an integer");
assertBPCount({ minLine: 6, maxLine: 8 }, 6);
assertBPCount({ minLine: 6, minColumn: 8, maxLine: 8 }, 5);
assertBPCount({ minLine: 6, maxLine: 8, maxColumn: 8 }, 7);
assertBPCount({ minLine: 6, minColumn: 8, maxLine: 8, maxColumn: 8 }, 6);
assertBPCount({ minLine: 6, minColumn: 9, maxLine: 8 }, 5);
assertBPCount({ minLine: 6, maxLine: 8, maxColumn: 9 }, 7);
assertBPCount({ minLine: 6, minColumn: 9, maxLine: 8, maxColumn: 9 }, 6);
assertBPCount({
minOffset: fScript.getPossibleBreakpoints({ line: 6 })[3].offset,

View File

@@ -379,6 +379,6 @@ function createOffsetLookup(code) {
if (!lineOffsets.hasOwnProperty(line)) {
throw new Error("Unknown line " + line + " column " + column);
}
return lineOffsets[line] + column;
return lineOffsets[line] + column - 1;
};
}

View File

@@ -13,33 +13,33 @@ function test(f, expected) {
g.eval(`
function f1() { }
`);
test(g.f1, 11);
test(g.f1, 12);
g.eval(`
var f2 = function({ a, b, c }, d, e, ...more) { };
`);
test(g.f2, 17);
test(g.f2, 18);
g.eval(`
var f3 = function *() { };
`);
test(g.f3, 19);
test(g.f3, 20);
g.eval(`
var f4 = async function
() { };
`);
test(g.f4, 2);
test(g.f4, 3);
g.eval(`
var f5 = (a, b) => a + b;
`);
test(g.f5, 9);
test(g.f5, 10);
g.eval(`
var f6 = a => a + 1;
`);
test(g.f6, 9);
test(g.f6, 10);
g.eval(`
var MyClass = class {
@@ -47,8 +47,8 @@ var MyClass = class {
};
var myInstance = new MyClass();
`);
test(g.myInstance.method, 10);
test(g.myInstance.constructor, 14);
test(g.myInstance.method, 11);
test(g.myInstance.constructor, 15);
const g2 = newGlobal({newCompartment: true, useWindowProxy: true});
const dbg2 = Debugger(g2);
@@ -60,20 +60,20 @@ function f7() { }
});
const f7w = g2Wrapped.makeDebuggeeValue(g2.f7);
assertEq(f7w.callable, true);
assertEq(f7w.script.startColumn, 11);
assertEq(f7w.script.startColumn, 12);
g.eval(`
function f8() {
return function f8Inner() { }
}
`);
test(g.f8, 11);
test(g.f8(), 27);
test(g.f8, 12);
test(g.f8(), 28);
g.eval(`
var f9 = new Function(\"\");
`);
test(g.f9, 0);
test(g.f9, 1);
let hit = 0;
let column;
@@ -83,19 +83,19 @@ dbg.onDebuggerStatement = function (frame) {
};
g.eval(` debugger;`);
assertEq(column, 0);
assertEq(column, 1);
assertEq(hit, 1);
const location = { fileName: "column.js", lineNumber: 1, columnNumber: 1 };
hit = 0;
g.evaluate(` debugger;`, location);
assertEq(column, 1);
assertEq(column, 2);
assertEq(hit, 1);
g.evaluate(`var f10 = function () { };`, location);
test(g.f10, 20);
test(g.f10, 21);
g.evaluate(`
var f11 = function () { };
`, location);
test(g.f11, 19);
test(g.f11, 20);

View File

@@ -23,7 +23,12 @@ function g() {
}
f();
`, { fileName: "foobar.js", lineNumber: 3, columnNumber: 42 });
`, {
fileName: "foobar.js",
lineNumber: 3,
// NOTE: evaluate function takes 0-origin column number.
columnNumber: 42,
});
let onNewScriptCalls = 0;
dbg.onNewScript = script => { onNewScriptCalls++; };
@@ -34,7 +39,8 @@ assertEq(onNewScriptCalls, 0);
assertEq(reparsedScript.url, "foobar.js");
assertEq(reparsedScript.startLine, 3);
assertEq(reparsedScript.startColumn, 42);
// NOTE: Debugger uses 1-origin column number.
assertEq(reparsedScript.startColumn, 43);
// Test for the same breakpoint positions in the original and reparsed script.
function getBreakpointPositions(script) {

View File

@@ -6,8 +6,9 @@ var gw = dbg.addDebuggee(g);
g.evaluate("function f(x) {}");
var fw = gw.getOwnPropertyDescriptor("f").value;
assertEq(fw.script.source.startLine, 1);
assertEq(fw.script.source.startColumn, 0);
assertEq(fw.script.source.startColumn, 1);
// NOTE: evaluate function takes 0-origin column number.
g.evaluate("function g(x) {}", { lineNumber: 10, columnNumber: 5 });
var gw = gw.getOwnPropertyDescriptor("g").value;
assertEq(gw.script.source.startLine, 10);
assertEq(gw.script.source.startColumn, 5);
assertEq(gw.script.source.startColumn, 6);

View File

@@ -18,7 +18,7 @@ dbg.onExceptionUnwind = function (frame, exc) {
assertEq(frame.older.type, "call");
const { lineNumber, columnNumber } = frame.script.getOffsetMetadata(frame.offset);
assertEq(lineNumber, 3);
assertEq(columnNumber, 4);
assertEq(columnNumber, 5);
const isInCatchScope = frame.script.isInCatchScope(frame.offset);
assertEq(isInCatchScope, false);
@@ -31,7 +31,7 @@ dbg.onExceptionUnwind = function (frame, exc) {
assertEq(older.type, "call");
const { lineNumber, columnNumber } = older.script.getOffsetMetadata(older.offset);
assertEq(lineNumber, 7);
assertEq(columnNumber, 4);
assertEq(columnNumber, 5);
const isInCatchScope = older.script.isInCatchScope(older.offset);
assertEq(isInCatchScope, false);

View File

@@ -18,7 +18,7 @@ dbg.onExceptionUnwind = function (frame, exc) {
assertEq(frame.older.type, "call");
const { lineNumber, columnNumber } = frame.script.getOffsetMetadata(frame.offset);
assertEq(lineNumber, 3);
assertEq(columnNumber, 4);
assertEq(columnNumber, 5);
const isInCatchScope = frame.script.isInCatchScope(frame.offset);
assertEq(isInCatchScope, false);
@@ -31,7 +31,7 @@ dbg.onExceptionUnwind = function (frame, exc) {
assertEq(older.type, "call");
const { lineNumber, columnNumber } = older.script.getOffsetMetadata(older.offset);
assertEq(lineNumber, 8);
assertEq(columnNumber, 6);
assertEq(columnNumber, 7);
const isInCatchScope = older.script.isInCatchScope(older.offset);
assertEq(isInCatchScope, true);

View File

@@ -18,7 +18,7 @@ dbg.onExceptionUnwind = function (frame, exc) {
assertEq(frame.older.type, "call");
const { lineNumber, columnNumber } = frame.script.getOffsetMetadata(frame.offset);
assertEq(lineNumber, 3);
assertEq(columnNumber, 4);
assertEq(columnNumber, 5);
const isInCatchScope = frame.script.isInCatchScope(frame.offset);
assertEq(isInCatchScope, false);
@@ -31,7 +31,7 @@ dbg.onExceptionUnwind = function (frame, exc) {
assertEq(older.type, "call");
const { lineNumber, columnNumber } = older.script.getOffsetMetadata(older.offset);
assertEq(lineNumber, 8);
assertEq(columnNumber, 6);
assertEq(columnNumber, 7);
const isInCatchScope = older.script.isInCatchScope(older.offset);
assertEq(isInCatchScope, true);

View File

@@ -45,8 +45,8 @@ for (var i = 0; i < NumInitializers; ++i) {
// Check the initializer lambda has expected line/column
assertEq(script.startLine, START + 1 + i);
assertEq(script.startColumn, 20);
assertEq(script.startColumn, 21);
// Check that source length matches expectations.
assertEq(script.startColumn + script.sourceLength + ';'.length, lineText.length);
assertEq(script.startColumn + script.sourceLength + ';'.length, lineText.length + 1);
}

View File

@@ -38,7 +38,7 @@ function getScriptSourceExtent(source) {
// to test multi-line sources, this will need to be updated.
for (let script of scripts) {
assertEq(script.startLine, 1);
assertEq(script.startColumn, script.sourceStart);
assertEq(script.startColumn, script.sourceStart + 1);
}
// Map each found script to a source extent string.
@@ -334,4 +334,4 @@ testSourceExtent(` class C { static set #prop(v) { } }`,
// Static Class Blocks
testSourceExtent(` class C { static { 10; } }`,
` ^-------------^ `,
` ^-------------------------`);
` ^-------------------------`);

View File

@@ -51,7 +51,8 @@ CoverageCollector.prototype._getLinesCovered = function () {
this._allCoverage[scriptName] = {};
}
let key = [lineNumber, columnNumber, offset].join("#");
// NOTE: columnNumber is 1-origin.
let key = [lineNumber, columnNumber - 1, offset].join("#");
if (!currentCoverage[scriptName][key]) {
currentCoverage[scriptName][key] = count;
} else {