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

assert: fix deepEqual similar sets and maps bug #13426

Closed
wants to merge 4 commits into from

Conversation

josephg
Copy link
Contributor

@josephg josephg commented Jun 3, 2017

This fixes a bug where deepEqual and deepStrictEqual would have
incorrect behaviour in sets and maps containing multiple equivalent
keys.

Fixes: #13347
Refs: #12142

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)

assert

This fixes a bug where deepEqual and deepStrictEqual would have
incorrect behaviour in sets and maps containing multiple equivalent
keys.

Fixes: nodejs#13347
Refs: nodejs#12142
@nodejs-github-bot nodejs-github-bot added the assert Issues and PRs related to the assert subsystem. label Jun 3, 2017
Copy link
Contributor

@refack refack left a comment

Choose a reason for hiding this comment

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

LGTM % nits & CI

@@ -187,6 +187,20 @@ assertOnlyDeepEqual(new Map([['a', '1']]), new Map([['a', 1]]));

assertDeepAndStrictEqual(new Set([{}]), new Set([{}]));

// Discussion of these test cases here - https://github.com/nodejs/node/issues/13347
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: just put Ref: https://github.com/nodejs/node/issues/13347

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

lib/assert.js Outdated
// This is a set of the entries in b which have been consumed in our pairwise
// comparison. Initialized lazily so sets which only have value types can
// skip an extra allocation.
let bEntriesUsed = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I'd rather you name it something like usedEntries, since bEntriesUsed sounds like a boolean predicating a need to use .entries()

Copy link
Contributor

Choose a reason for hiding this comment

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

If you choose to accept this nit, rename everywhere else as well...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure - I don't have a strong opinion so I've renamed it.

My concern with usedEntries is that it doesn't make it clear that we're marking entries in set b.

@refack
Copy link
Contributor

refack commented Jun 3, 2017

lib/assert.js Outdated
for (const val1 of a) {
// If the value doesn't exist in the second set by reference, and its an
// object or an array we'll need to go hunting for something thats
// deep-equal to it. Note that this is O(n^2) complexity, and will get
// slower if large, very similar sets / maps are nested inside.
// Unfortunately there's no real way around this.
if (!setHasSimilarElement(b, val1, strict, memo)) {
if (bEntriesUsed == null && typeof val1 === 'object')
Copy link
Contributor

@rmdm rmdm Jun 3, 2017

Choose a reason for hiding this comment

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

Why is it important that a current val1 is an object? And why it should prevent initializing bEntriesUsed "for now" if it is not?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The bug doesn't affect value types because its impossible for two strings, numbers, bools, etc to be deepEqual to one another without being reference-equal. So in the case where the set or map only contains value typed entries, there's no need to allocate an extra set for bEntriesUsed.

Lazily allocating the set when needed is a microoptimization.

Copy link
Contributor

Choose a reason for hiding this comment

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

Then maybe it worth to continue that optimization and checking type of val2 before adding it to set of usedEntries? Because presence of even single object in a set with large number of typed entries would lead to adding them to usedEntries without any need.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe.. but in practice its very rare to mix types in set / map keys. And it still won't matter in strict mode because there's an early return for value types anyway.

Uhhhh but not in non-strict mode. Huh - looks like this also doesn't throw:

assert.deepEqual(new Set([3, '3']), new Set([3, 4]));

fixes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

... Fixed. PR updated so this behaves correctly too.

Copy link
Contributor

@refack refack left a comment

Choose a reason for hiding this comment

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

💯

@refack
Copy link
Contributor

refack commented Jun 3, 2017

@refack
Copy link
Contributor

refack commented Jun 3, 2017

Lint errors:

not ok 27 - /usr/home/iojs/build/workspace/node-test-linter/test/parallel/test-assert-deep.js
  ---
  message: Missing space before value for key 'a'.
  severity: error
  data:
    line: 208
    column: 15
    ruleId: key-spacing
  messages:
    - message: Missing space before value for key 'a'.
      severity: error
      data:
        line: 208
        column: 22
        ruleId: key-spacing
    - message: Missing space before value for key 'a'.
      severity: error
      data:
        line: 208
        column: 29
        ruleId: key-spacing
    - message: Missing space before value for key 'a'.
      severity: error
      data:
        line: 209
        column: 15
        ruleId: key-spacing
    - message: Missing space before value for key 'a'.
      severity: error
      data:
        line: 209
        column: 22
        ruleId: key-spacing
    - message: Missing space before value for key 'a'.
      severity: error
      data:
        line: 209
        column: 29
        ruleId: key-spacing
  ...

@josephg
Copy link
Contributor Author

josephg commented Jun 3, 2017

Oops! Sorry about that. Fixed!

$ make lint
Running JS linter...
./node tools/eslint/bin/eslint.js --cache --rulesdir=tools/eslint-rules --ext=.js,.md \
	  benchmark doc lib test tools
Running C++ linter...
Total errors found: 0

@refack
Copy link
Contributor

refack commented Jun 4, 2017

@refack
Copy link
Contributor

refack commented Jun 4, 2017

Linux CI fail was infrastructure related, but still rerunning: https://ci.nodejs.org/job/node-test-commit-linux/10349/

@josephg
Copy link
Contributor Author

josephg commented Jun 5, 2017

Unless there's any more comments, I think this is ready to merge 👓

@refack refack self-assigned this Jun 5, 2017
@refack
Copy link
Contributor

refack commented Jun 5, 2017

I'll land this later today (let PDT people a last chance to chime in)
/cc @nodejs/testing

refack pushed a commit to refack/node that referenced this pull request Jun 5, 2017
This fixes a bug where deepEqual and deepStrictEqual would have
incorrect behaviour in sets and maps containing multiple equivalent
keys.

PR-URL: nodejs#13426
Fixes: nodejs#13347
Refs: nodejs#12142
Reviewed-By: Refael Ackermann <refack@gmail.com>
@refack
Copy link
Contributor

refack commented Jun 5, 2017

landed in 7cddcc9

@refack refack closed this Jun 5, 2017
@refack
Copy link
Contributor

refack commented Jun 5, 2017

Extra quick sanity of master: https://ci.nodejs.org/job/node-test-commit-linuxone/6404/

@josephg josephg deleted the deepequal-bug branch June 5, 2017 22:40
jasnell pushed a commit that referenced this pull request Jun 7, 2017
This fixes a bug where deepEqual and deepStrictEqual would have
incorrect behaviour in sets and maps containing multiple equivalent
keys.

PR-URL: #13426
Fixes: #13347
Refs: #12142
Reviewed-By: Refael Ackermann <refack@gmail.com>
@gibfahn gibfahn mentioned this pull request Jun 15, 2017
3 tasks
@refack refack removed their assignment Oct 20, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
assert Issues and PRs related to the assert subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

assert: deepEqual of two Sets with different content passes
5 participants