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, util: *DeepEqual() handles ArrayBuffers #22266

Closed
wants to merge 2 commits into from
Closed

assert, util: *DeepEqual() handles ArrayBuffers #22266

wants to merge 2 commits into from

Conversation

calebsander
Copy link
Contributor

Previously, all ArrayBuffers and SharedArrayBuffers were considered equal in assert.deepEqual(), assert.deepStrictEqual(), and util.isDeepStrictEqual(). Now, ArrayBuffers and SharedArrayBuffers must have the same byte lengths and contents to be considered equal. In loose mode, an ArrayBuffer is considered equal to a SharedArrayBuffer if they have the same contents, whereas in strict mode, the buffers must be both ArrayBuffers or both SharedArrayBuffers.

Here are 2 examples, both of which succeeded before but now throw:

assert.deepEqual(new Uint8Array([1, 2, 3]).buffer, new Uint8Array([4, 5, 6]).buffer)
assert.deepStrictEqual(new ArrayBuffer(3), new ArrayBuffer(4))
Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines

@nodejs-github-bot nodejs-github-bot added the util Issues and PRs related to the built-in util module. label Aug 11, 2018
@benjamingr benjamingr added the semver-major PRs that contain breaking changes and should be released in the next major version. label Aug 11, 2018
@benjamingr
Copy link
Member

Hey, thank you so much for contributing to Node! We appreciate it.

Have you seen this PR: https://github.com/nodejs/node/pull/22258/files it reworks deepStrictEqual and does this: https://github.com/nodejs/node/pull/22258/files#diff-f4c4bd6d97fd57913c4fc72e673578d6R148

cc @BridgeAR

@calebsander
Copy link
Contributor Author

calebsander commented Aug 11, 2018

No, I actually hadn't seen that PR. Funny coincidence that I made this one the same day. Seems like they shouldn't have any conflicts.

@@ -46,6 +46,10 @@ function isObjectOrArrayTag(tag) {
return tag === '[object Array]' || tag === '[object Object]';
}

function isArrayBuffer(tag) {
return tag === '[object ArrayBuffer]' || tag === '[object SharedArrayBuffer]';
}
Copy link
Member

Choose a reason for hiding this comment

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

Please use util.types.isAnyArrayBuffer instead. This value could be manipulated by using the Symbol.toStringTag.

@@ -116,6 +120,11 @@ function strictDeepEqual(val1, val2, memos) {
// if they both only contain numeric keys, we don't need to exam further
return keyCheck(val1, val2, true, memos, val1.length,
val2.length);
} else if (isArrayBuffer(val1Tag)) {
if (!areSimilarTypedArrays(new Uint8Array(val1),
new Uint8Array(val2), 300)) {
Copy link
Member

Choose a reason for hiding this comment

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

Please change this to if (val1.byteLength !== val2.byteLength || compare(new Uint8Array(val1), new Uint8Array(val2)) !== 0) {. Otherwise the value is converted twice.

@@ -170,6 +179,12 @@ function looseDeepEqual(val1, val2, memos) {
} else if (isArguments(val1Tag) || isArguments(val2Tag)) {
return false;
}
if (isArrayBuffer(val1Tag) && isArrayBuffer(val2Tag)) {
if (!areSimilarTypedArrays(new Uint8Array(val1),
new Uint8Array(val2), 300)) {

This comment was marked as resolved.

This comment was marked as resolved.

This comment was marked as resolved.

[new ArrayBuffer(2), new SharedArrayBuffer(3)],
[
new Uint8Array(new ArrayBuffer(3)).fill(1).buffer,
new Uint8Array(new SharedArrayBuffer(3)).fill(2).buffer
Copy link
Member

Choose a reason for hiding this comment

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

This should already fail because of the different type in strict mode. I would therefore change the test to fill(1) on both sides. In that case it would probably pass in loose mode.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is already a test for that under looseEqualArrayPairs. Here I'm testing that buffers of different types with the same length but different contents are neither loosely nor strictly equal.

@BridgeAR
Copy link
Member

This is great! Thanks a lot for the addition. Just left a few comments.

I am still uncertain how it's possible to have a better error output tough.

@calebsander
Copy link
Contributor Author

Yeah, I was also wondering whether the diff message could be improved. I think it is currently inferred based on the output of util.inspect(), so improving the message would require a better output from util.inspect() on ArrayBuffers.

@BridgeAR
Copy link
Member

BridgeAR commented Aug 11, 2018

@calebsander yes, it does use util.inspect(). That is also very detailed in inspecting values. However, in this case it fails badly. I wonder if it makes sense to actually show a Uint8Array representation of an ArrayBuffer next to it. That way the difference would be clear but AFAIK the original type can not be reconstructed and that might confuse people.

@calebsander
Copy link
Contributor Author

calebsander commented Aug 11, 2018

When passing an ArrayBuffer to util.inspect(), it is just raw binary data, much like a Node Buffer; it seems perfectly reasonable to list its bytes like util.inspect() does for a Buffer. Compare:

util.inspect(Buffer.from([1, 2, 3])) // <Buffer 01 02 03>
util.inspect(new Uint8Array([1, 2, 3]).buffer) // ArrayBuffer { byteLength: 3 }

I think it would make sense for the result to look something like ArrayBuffer { byteLength: 3 } [ 1, 2, 3 ], to parallel the result of util.inspect() on a Uint8Array. Regardless, I would put this in a separate PR.

Copy link
Member

@benjamingr benjamingr left a comment

Choose a reason for hiding this comment

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

I like this and the technical changes. Ping @nodejs/tsc since this is a breaking change.

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

LGTM

@benjamingr benjamingr added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Aug 12, 2018
@benjamingr
Copy link
Member

benjamingr commented Aug 12, 2018

@BridgeAR
Copy link
Member

@calebsander would you be so kind and rebase? In that case the PR should be ready.

Previously, all ArrayBuffers were considered equal in assert.deepEqual()
and assert.deepStrictEqual().
Now, ArrayBuffers and SharedArrayBuffers must have the same byte lengths
and contents to be considered equal.
In loose mode, an ArrayBuffer is considered equal to a SharedArrayBuffer
if they have the same contents, whereas in strict mode, the buffers must
be both ArrayBuffers or both SharedArrayBuffers.
@calebsander
Copy link
Contributor Author

Done

@BridgeAR
Copy link
Member

@BridgeAR
Copy link
Member

BridgeAR commented Aug 20, 2018

Landed in 439b75b

@calebsander thanks a lot for your contribution and congratulations on your first commit to Node.js! 🎉

BridgeAR pushed a commit to BridgeAR/node that referenced this pull request Aug 20, 2018
Previously, all ArrayBuffers were considered equal in assert.deepEqual()
and assert.deepStrictEqual().
Now, ArrayBuffers and SharedArrayBuffers must have the same byte lengths
and contents to be considered equal.
In loose mode, an ArrayBuffer is considered equal to a SharedArrayBuffer
if they have the same contents, whereas in strict mode, the buffers must
be both ArrayBuffers or both SharedArrayBuffers.

PR-URL: nodejs#22266
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: James M Snell <jasnell@gmail.com>
@targos targos closed this Aug 20, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author ready PRs that have at least one approval, no pending requests for changes, and a CI started. 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.

8 participants