Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

util: change sparse arrays inspection format #11576

Closed
wants to merge 1 commit into from

Conversation

aqrln
Copy link
Contributor

@aqrln aqrln commented Feb 27, 2017

Missing elements in sparse arrays used to be serialized to empty placeholders delimited with commas by util.inspect() and in some cases the result was a syntactically correct representation of a JavaScript array with shorter length than the original one. This PR implements @TimothyGu's suggestion to change the way util.inspect() formats sparse arrays to something similar to how Firefox shows them.

Fixes: #11570

image

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

util

@nodejs-github-bot nodejs-github-bot added the util Issues and PRs related to the built-in util module. label Feb 27, 2017
@aqrln
Copy link
Contributor Author

aqrln commented Feb 27, 2017

Existing tests are updated, but I'll add a couple of new ones.

UPD: already added.

@aqrln aqrln force-pushed the inspect-sparse-arrays branch from 61b0999 to 3fe8d6a Compare February 27, 2017 07:41
lib/util.js Outdated
index++;
}
if (emptyElements > 0) {
output.push(ctx.stylize(`[${emptyElements} empty]`, 'special'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still stand by my opinion of using 'undefined' as the color, though I'm not going to block this PR if that's what others prefer.

Copy link
Contributor Author

@aqrln aqrln Feb 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, let's wait and see what others think about it. If there's no specific preference, I'll change it to 'undefined'.

@@ -832,12 +832,12 @@ checkAlignment(new Map(big_array.map(function(y) { return [y, null]; })));
// Do not backport to v5/v4 unless all of
// https://github.com/nodejs/node/pull/6334 is backported.
{
const x = Array(101);
const x = ','.repeat(100).split(','); // array of 101 empty strings
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new Array(101).fill() might be more semantically correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, thanks.

@aqrln aqrln force-pushed the inspect-sparse-arrays branch 2 times, most recently from b692e86 to 72ecc70 Compare February 27, 2017 10:28
@aqrln
Copy link
Contributor Author

aqrln commented Feb 27, 2017

@TimothyGu I've updated the PR to use the 'undefined' color instead of the 'special' one. It indeed seems better.

@aqrln aqrln force-pushed the inspect-sparse-arrays branch from 72ecc70 to 8993097 Compare February 27, 2017 11:26
@evanlucas
Copy link
Contributor

I'm on the fence on this one...leaning slightly towards keep existing behavior. I'm not sure that this is in fact more readable... going ahead and marking as semver-major though

@evanlucas evanlucas added the semver-major PRs that contain breaking changes and should be released in the next major version. label Feb 27, 2017
@aqrln
Copy link
Contributor Author

aqrln commented Feb 27, 2017

@evanlucas yes, certainly semver-major. An alternative solution that is at least similar to existing behavior yet solves the issue is to add an extra comma if the last element of a sparse array is missing.

@aqrln aqrln force-pushed the inspect-sparse-arrays branch from 8993097 to e923dbb Compare February 27, 2017 12:22
@Fishrock123
Copy link
Contributor

I agree with @evanlucas. I think something a bit more clear would be desired e.g. [3 empty indices skipped]. Not sure that's saving anything then though so...

@jasnell
Copy link
Member

jasnell commented Feb 27, 2017

I don't feel strongly about it one way or another but one fairly compact approach is to use one . for each omitted index... e.g.

[1, 2, {...}, 6, 7] // three index positions skipped
[1, 2, {..}, 5, 6, 7] // two index positions skipped
[1, 2, {..........}, 13, 14] // ten index positions skipped

@aqrln
Copy link
Contributor Author

aqrln commented Feb 27, 2017

@Fishrock123 changed it to be

> new Array(3)
[ [3 uninitialized elements] ]
> [1, 2, , , , 3, 4, 5]
[ 1, 2, [3 uninitialized elements], 3, 4, 5 ]
> [1, 2, , , , 3, 4, 5, , 2]
[ 1,
  2,
  [3 uninitialized elements],
  3,
  4,
  5,
  [1 uninitialized element],
  2 ]

@aqrln
Copy link
Contributor Author

aqrln commented Feb 27, 2017

@TimothyGu looks like this is somewhat controversial 😄 As I already suggested above, a better solution might be to leave the current format with a small addition: if a sparse array doesn't have its last element, just append an extra dangling comma. This way the output of util.format() will not be contradicting JavaScript syntax and #11570 will be fixed, though the other way. What do you think about it?

}
const remaining = value.length - index;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn’t this be value.length - visisbleLength?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it shouldn't. visibleLength is the count of... well, printed elements, i.e., 3 in case of [ 1, [10000 uninitialized elements], 2 ] (maybe the variable name isn't that great, though). remaining, on the other hand, is the count of real array indices which values were not printed.

@aqrln
Copy link
Contributor Author

aqrln commented Feb 27, 2017

@jasnell well, this would be hard to format with respect to inspectOptions.maxArrayLength and inspectOptions.breakLength.

Copy link
Member

@addaleax addaleax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

(edit: +1 to doing this and +1 to “uninitialized”)

@TimothyGu
Copy link
Member

if a sparse array doesn't have its last element, just append an extra dangling comma

No, though it's now "correct", it wouldn't be less confusing.

The goal for the message is to be 1) clear/unambiguous, and 2) concise.

Having an extra dangling comma at the end would indeed be unambiguous in the sense of ES compliance, but it would be confusing considering chances are people might have already gotten used to this quirk in Node.js.

That applies also to the suggestion to use {..} as well: it's simply not beginner-friendly.

At the same time, uninitialized seems to convey the same information as, only in a less concise fashion than empty.

@aqrln
Copy link
Contributor Author

aqrln commented Feb 28, 2017

Having an extra dangling comma at the end would indeed be unambiguous in the sense of ES compliance, but it would be confusing considering chances are people might have already gotten used to this quirk in Node.js.

+1, good point.

So what about the placeholder message? Considering browser examples provided in #11570, I don't really like the way Chromium formats sparse arrays (it isn't less confusing when [undefined] and [undefined × 1] mean two completely different things), the format Firefox uses is more appealing to me, though the word "slots" is rather misleading. Speaking about the message we currently have in this PR, first of all, I think I can change the word element(s) to item(s) because not only it is shorter but it is also the term used in ... N more items message.

Hence the current variants:

  • "empty items"
  • "uninitialized items"

It would be great if anyone has better suggestions. @TimothyGu, @addaleax?

@addaleax
Copy link
Member

I like “uninitialized” because it tells the user how that hole in the array actually came to be – it was simply not initialized to any particular value (except for when somebody does delete arr[i], but I’d say that doesn’t happen very often).

I’d be fine with any of {“empty”, “uninitialized”, “missing”} × {“item(s)”, “element(s)”}, though.

@addaleax
Copy link
Member

addaleax commented Mar 1, 2017

@nodejs/ctc This needs another review from a CTC member. Also, is anybody objecting to the current format ([ 'foo', [1 uninitialized element], 'baz' ])?

@cjihrig
Copy link
Contributor

cjihrig commented Mar 1, 2017

The square bracket use makes it a bit harder to parse. At a quick glance, it looks like a nested array.

@jasnell
Copy link
Member

jasnell commented Mar 1, 2017

I'm good with the [ bracket but a { could work also. Either way I can live with it

@cjihrig
Copy link
Contributor

cjihrig commented Mar 1, 2017

That would read like an object literal. Maybe < and >. Maybe I'm reading into it too much :-)

@aqrln
Copy link
Contributor Author

aqrln commented Mar 1, 2017

@cjihrig, just wanted to suggest it :)

@aqrln
Copy link
Contributor Author

aqrln commented Mar 1, 2017

Also, what about s/element(s)/item(s) to be consistent with an existing "... N more items" message?

@Fishrock123
Copy link
Contributor

I think it is a bit excessively long but I don't see good and descriptive options otherwise.

Well, I suppose empty is probably better than uninitialized?

Copy link
Member

@TimothyGu TimothyGu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM other than the two nits. Thanks for working on this!

assert(!/1 more item/.test(util.inspect(x, {maxArrayLength: 101})));
}

{
const x = new Array(101).fill();
assert(/^\[ ... 101 more items ]$/.test(
util.inspect(x, {maxArrayLength: 0})));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I know the regex is there already, but it doesn't seem to be needed, since assert.strictEqual(util.inspect(x, {maxArrayLength: 0}), '[ ... 101 more items ]') should accomplish the same.

Copy link
Contributor Author

@aqrln aqrln Mar 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to open a new PR and change it for the whole group of tests here so that they look consistent and this PR doesn't modify the tests which are not related to its changes.

Copy link
Contributor Author

@aqrln aqrln Mar 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other that these two and analogous Uint8Array test (line 873), there are tests where a regex can be reduced to a simple .endsWith() check.

util.inspect(x, {maxArrayLength: 0})));
}

{
const x = Array(101);
assert(/^\[ ... 101 more items ]$/.test(
util.inspect(x, {maxArrayLength: 0})));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

@addaleax
Copy link
Member

addaleax commented Mar 4, 2017

@addaleax addaleax added this to the 8.0.0 milestone Mar 4, 2017
@aqrln
Copy link
Contributor Author

aqrln commented Mar 4, 2017

@addaleax I guess CITGM failures aren't related to this patch

Missing elements in sparse arrays used to be serialized to empty
placeholders delimited with commas by util.inspect() and in some cases
the result was a syntactically correct representation of a JavaScript
array with shorter length than the original one. This commit implements
@TimothyGu's suggestion to change the way util.inspect() formats sparse
arrays to something similar to how Firefox shows them.

Fixes: nodejs#11570
@aqrln aqrln force-pushed the inspect-sparse-arrays branch from 2cb034c to fb4fe0d Compare March 7, 2017 17:50
@aqrln
Copy link
Contributor Author

aqrln commented Mar 7, 2017

Squashed the commits into one.

@aqrln
Copy link
Contributor Author

aqrln commented Mar 9, 2017

@addaleax ping

@addaleax
Copy link
Member

addaleax commented Mar 9, 2017

@aqrln Thanks for the ping, I’m busy but I’ll land this later today if nobody beats me to it

@addaleax
Copy link
Member

Landed in ec2f098, thanks for the PR!

@addaleax addaleax closed this Mar 10, 2017
addaleax pushed a commit that referenced this pull request Mar 10, 2017
Missing elements in sparse arrays used to be serialized to empty
placeholders delimited with commas by util.inspect() and in some cases
the result was a syntactically correct representation of a JavaScript
array with shorter length than the original one. This commit implements
@TimothyGu's suggestion to change the way util.inspect() formats sparse
arrays to something similar to how Firefox shows them.

Fixes: #11570
PR-URL: #11576
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Timothy Gu <timothygu99@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
@aqrln aqrln deleted the inspect-sparse-arrays branch March 10, 2017 00:19
@aqrln aqrln mentioned this pull request Mar 10, 2017
2 tasks
aqrln added a commit to aqrln/node that referenced this pull request Mar 10, 2017
* Enclose tests that used to introduce module-level variables into
  their own scopes.
* Replace ES5 anonymous functions with arrow functions where it makes
  sense.
* And make one arrow function a regular function thus fixing a bug in a
  getter inside an object created in "Array with dynamic properties"
  test.  This getter has never been invoked though, so the test hasn't been
  failing.
* Convert snake_case identifiers to camelCase.
* Make some variable names more readable.
* Replace regular expressions in maxArrayLength tests with simple
  assert.strictEquals() and assert(...endsWith()) checks, as suggested
  in <nodejs#11576 (comment)>.
jasnell pushed a commit that referenced this pull request Mar 15, 2017
* Enclose tests that used to introduce module-level variables into
  their own scopes.
* Replace ES5 anonymous functions with arrow functions where it makes
  sense.
* And make one arrow function a regular function thus fixing a bug in a
  getter inside an object created in "Array with dynamic properties"
  test.  This getter has never been invoked though, so the test hasn't been
  failing.
* Convert snake_case identifiers to camelCase.
* Make some variable names more readable.
* Replace regular expressions in maxArrayLength tests with simple
  assert.strictEquals() and assert(...endsWith()) checks, as suggested
  in <#11576 (comment)>.

PR-URL: #11779
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
jungx098 pushed a commit to jungx098/node that referenced this pull request Mar 21, 2017
Missing elements in sparse arrays used to be serialized to empty
placeholders delimited with commas by util.inspect() and in some cases
the result was a syntactically correct representation of a JavaScript
array with shorter length than the original one. This commit implements
@TimothyGu's suggestion to change the way util.inspect() formats sparse
arrays to something similar to how Firefox shows them.

Fixes: nodejs#11570
PR-URL: nodejs#11576
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Timothy Gu <timothygu99@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
jungx098 pushed a commit to jungx098/node that referenced this pull request Mar 21, 2017
* Enclose tests that used to introduce module-level variables into
  their own scopes.
* Replace ES5 anonymous functions with arrow functions where it makes
  sense.
* And make one arrow function a regular function thus fixing a bug in a
  getter inside an object created in "Array with dynamic properties"
  test.  This getter has never been invoked though, so the test hasn't been
  failing.
* Convert snake_case identifiers to camelCase.
* Make some variable names more readable.
* Replace regular expressions in maxArrayLength tests with simple
  assert.strictEquals() and assert(...endsWith()) checks, as suggested
  in <nodejs#11576 (comment)>.

PR-URL: nodejs#11779
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
aqrln added a commit to aqrln/node that referenced this pull request Mar 21, 2017
* Enclose tests that used to introduce module-level variables into
  their own scopes.
* Replace ES5 anonymous functions with arrow functions where it makes
  sense.
* And make one arrow function a regular function thus fixing a bug in a
  getter inside an object created in "Array with dynamic properties"
  test.  This getter has never been invoked though, so the test hasn't been
  failing.
* Convert snake_case identifiers to camelCase.
* Make some variable names more readable.
* Replace regular expressions in maxArrayLength tests with simple
  assert.strictEquals() and assert(...endsWith()) checks, as suggested
  in <nodejs#11576 (comment)>.

PR-URL: nodejs#11779
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
@jasnell jasnell mentioned this pull request Apr 4, 2017
evanlucas pushed a commit that referenced this pull request May 2, 2017
* Enclose tests that used to introduce module-level variables into
  their own scopes.
* Replace ES5 anonymous functions with arrow functions where it makes
  sense.
* And make one arrow function a regular function thus fixing a bug in a
  getter inside an object created in "Array with dynamic properties"
  test.  This getter has never been invoked though, so the test hasn't been
  failing.
* Convert snake_case identifiers to camelCase.
* Make some variable names more readable.
* Replace regular expressions in maxArrayLength tests with simple
  assert.strictEquals() and assert(...endsWith()) checks, as suggested
  in <#11576 (comment)>.

PR-URL: #11779
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver-major PRs that contain breaking changes and should be released in the next major version. util Issues and PRs related to the built-in util module.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Confusing inspection for sparse arrays
9 participants