diff --git a/packages/SwingSet/test/test-marshal.js b/packages/SwingSet/test/test-marshal.js index 354019864e8..5fa3fc3f319 100644 --- a/packages/SwingSet/test/test-marshal.js +++ b/packages/SwingSet/test/test-marshal.js @@ -34,7 +34,7 @@ test('serialize static data', t => { body: '{"@qclass":"undefined"}', slots: [], }); - t.deepEqual(ser(-0), { body: '{"@qclass":"-0"}', slots: [] }); +// t.deepEqual(ser(-0), { body: '{"@qclass":"-0"}', slots: [] }); t.deepEqual(ser(NaN), { body: '{"@qclass":"NaN"}', slots: [] }); t.deepEqual(ser(Infinity), { body: '{"@qclass":"Infinity"}', @@ -86,8 +86,8 @@ test('unserialize static data', t => { // JS primitives that aren't natively representable by JSON t.deepEqual(uns('{"@qclass":"undefined"}'), undefined); - t.ok(Object.is(uns('{"@qclass":"-0"}'), -0)); - t.notOk(Object.is(uns('{"@qclass":"-0"}'), 0)); +// t.ok(Object.is(uns('{"@qclass":"-0"}'), -0)); +// t.notOk(Object.is(uns('{"@qclass":"-0"}'), 0)); t.ok(Object.is(uns('{"@qclass":"NaN"}'), NaN)); t.deepEqual(uns('{"@qclass":"Infinity"}'), Infinity); t.deepEqual(uns('{"@qclass":"-Infinity"}'), -Infinity); diff --git a/packages/marshal/marshal.js b/packages/marshal/marshal.js index 462f84a8cb5..59b21df5745 100644 --- a/packages/marshal/marshal.js +++ b/packages/marshal/marshal.js @@ -156,6 +156,17 @@ export function mustPassByPresence(val) { // ok! } +// This is the equality comparison used by JavaScript's Map and Set +// abstractions, where NaN is the same as NaN and -0 is the same as +// 0. Marshal serializes -0 as zero, so the semantics of our distributed +// object system does not distinguish 0 from -0. +// +// `sameValueZero` is the EcmaScript spec name for this equality comparison, +// but TODO we need a better name for the API. +export function sameValueZero(x, y) { + return x === y || Object.is(x, y); +} + // How would val be passed? For primitive values, the answer is // * 'null' for null // * throwing an error for an unregistered symbol @@ -343,7 +354,7 @@ export function makeMarshal( return harden({ [QCLASS]: 'NaN' }); } if (Object.is(val, -0)) { - return harden({ [QCLASS]: '-0' }); + return 0; } if (val === Infinity) { return harden({ [QCLASS]: 'Infinity' }); @@ -478,9 +489,6 @@ export function makeMarshal( case 'undefined': { return undefined; } - case '-0': { - return -0; - } case 'NaN': { return NaN; } diff --git a/packages/marshal/test/test-marshal.js b/packages/marshal/test/test-marshal.js index 23b73b46011..802874aae9f 100644 --- a/packages/marshal/test/test-marshal.js +++ b/packages/marshal/test/test-marshal.js @@ -19,7 +19,7 @@ test('serialize static data', t => { body: '{"@qclass":"undefined"}', slots: [], }); - t.deepEqual(ser(-0), { body: '{"@qclass":"-0"}', slots: [] }); +// t.deepEqual(ser(-0), { body: '{"@qclass":"-0"}', slots: [] }); t.deepEqual(ser(NaN), { body: '{"@qclass":"NaN"}', slots: [] }); t.deepEqual(ser(Infinity), { body: '{"@qclass":"Infinity"}', @@ -75,8 +75,8 @@ test('unserialize static data', t => { // JS primitives that aren't natively representable by JSON t.deepEqual(uns('{"@qclass":"undefined"}'), undefined); - t.ok(Object.is(uns('{"@qclass":"-0"}'), -0)); - t.notOk(Object.is(uns('{"@qclass":"-0"}'), 0)); + //t.ok(Object.is(uns('{"@qclass":"-0"}'), -0)); + //t.notOk(Object.is(uns('{"@qclass":"-0"}'), 0)); t.ok(Object.is(uns('{"@qclass":"NaN"}'), NaN)); t.deepEqual(uns('{"@qclass":"Infinity"}'), Infinity); t.deepEqual(uns('{"@qclass":"-Infinity"}'), -Infinity); diff --git a/packages/same-structure/src/sameStructure.js b/packages/same-structure/src/sameStructure.js index 5f6bd17dc11..23aadf74720 100644 --- a/packages/same-structure/src/sameStructure.js +++ b/packages/same-structure/src/sameStructure.js @@ -1,5 +1,5 @@ import harden from '@agoric/harden'; -import { passStyleOf } from '@agoric/marshal'; +import { sameValueZero, passStyleOf } from '@agoric/marshal'; import { assert, details, openDetail } from '@agoric/assert'; // Shim of Object.fromEntries from @@ -114,7 +114,7 @@ function sameStructure(left, right) { case 'symbol': case 'bigint': case 'presence': { - return Object.is(left, right); + return sameValueZero(left, right); } case 'copyRecord': case 'copyArray': { @@ -193,7 +193,7 @@ function mustBeSameStructureInternal(left, right, message, path) { case 'symbol': case 'bigint': case 'presence': { - if (!Object.is(left, right)) { + if (!sameValueZero(left, right)) { complain('different'); } break;