Skip to content

Commit 4ca036a

Browse files
committed
polyfill: sort string keys, equality fix
1 parent 9a47438 commit 4ca036a

File tree

3 files changed

+47
-11
lines changed

3 files changed

+47
-11
lines changed

README.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,6 @@ c.d === d; // true
137137
Object.is(c.zero, -0); // true
138138
```
139139

140-
The key order matches the argument used to construct it ([#1](https://github.com/acutmore/proposal-composites/issues/1)).
141-
142-
```js
143-
const c = Composite({ z: true, x: true, y: true });
144-
Object.keys(c); // ["z", "x", "y"]
145-
```
146-
147140
### What are the equality semantics?
148141

149142
Two composites are equal only if they have the same prototype ([#5](https://github.com/acutmore/proposal-composites/issues/5)) and their properties form the same set of key-value pairs.

polyfill/composite.test.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ await test("key order", () => {
5151
[s3]: 0,
5252
});
5353
const keys = Reflect.ownKeys(c);
54-
assert.deepStrictEqual(keys, ["0", "10", "b", "a", s1, sB, s2, sA, s3]);
54+
assert.deepStrictEqual(keys, ["0", "10", "a", "b", s1, sB, s2, sA, s3]);
5555
});
5656
await test(".equal non-composite equal", () => {
5757
assert(Composite.equal(-0, 0));
@@ -82,7 +82,12 @@ await test(".equal composites symbol props equal", () => {
8282
const s2 = Symbol();
8383
const c1 = Composite({ [s1]: 1, [s2]: 2 });
8484
assert(Composite.equal(c1, Composite({ [s1]: 1, [s2]: 2 })));
85-
assert(Composite.equal(c1, Composite({ [s2]: 2, [s1]: 1 })));
85+
86+
const c2 = Composite({ [s2]: 2, [s1]: 1 });
87+
assert(Composite.equal(c1, c2));
88+
89+
assert.deepStrictEqual(Reflect.ownKeys(c1), [s1, s2]);
90+
assert.deepStrictEqual(Reflect.ownKeys(c2), [s2, s1]);
8691
});
8792
await test(".equal composites symbol props not-equal", () => {
8893
const s1 = Symbol();
@@ -142,3 +147,19 @@ await test(".equal composites interesting decimal numbers", () => {
142147
assert(c2 !== c3, "c2 and c3 should not be the same object");
143148
assert(Composite.equal(c2, c3), "c2 and c3 should be equal");
144149
});
150+
await test(".equal composites with polluted Object.prototype", () => {
151+
(Object.prototype as any)["pollution"] = true;
152+
try {
153+
const c1 = Composite({ pollution: true });
154+
const c2 = Composite({ other: true });
155+
assert(!Composite.equal(c1, c2), "c1 and c2 should not be equal");
156+
} finally {
157+
delete (Object.prototype as any)["pollution"];
158+
}
159+
});
160+
161+
await test(".equal composites with different key order", () => {
162+
const c1 = Composite({ a: true, b: true });
163+
const c2 = Composite({ b: true, a: true });
164+
assert(Composite.equal(c1, c2), "c1 and c2 should not be equal");
165+
});

polyfill/composite.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
1-
import { assert, sameValueZero } from "./internal/utils.ts";
2-
import { ownKeys, apply, freeze, setAdd, setHas, Set, setPrototypeOf, objectPrototype } from "./internal/originals.ts";
1+
import { assert, EMPTY, sameValueZero } from "./internal/utils.ts";
2+
import {
3+
ownKeys,
4+
apply,
5+
freeze,
6+
setAdd,
7+
setHas,
8+
Set,
9+
setPrototypeOf,
10+
objectPrototype,
11+
sort,
12+
} from "./internal/originals.ts";
313
import { __Composite__, objectIsComposite, maybeGetCompositeHash, setHash } from "./internal/composite-class.ts";
414

515
export type Composite = __Composite__;
@@ -13,8 +23,19 @@ export function Composite(arg: object): Composite {
1323
}
1424
const argKeys = ownKeys(arg);
1525
const c = new __Composite__();
26+
const stringKeys: string[] = [];
1627
for (let i = 0; i < argKeys.length; i++) {
1728
let k = argKeys[i];
29+
if (typeof k === "string") {
30+
stringKeys[stringKeys.length] = k;
31+
} else {
32+
DEV: assert(typeof k === "symbol");
33+
(c as any)[k] = (arg as any)[k];
34+
}
35+
}
36+
apply(sort, stringKeys, EMPTY);
37+
for (let i = 0; i < stringKeys.length; i++) {
38+
let k = stringKeys[i];
1839
(c as any)[k] = (arg as any)[k];
1940
}
2041
setPrototypeOf(c, objectPrototype);
@@ -55,6 +76,7 @@ export function compositeEqual(a: unknown, b: unknown): boolean {
5576
const aKey = aKeys[i];
5677
const bKey = bKeys[i];
5778
if (typeof aKey !== typeof bKey) {
79+
// Different ratios of strings and symbols
5880
return false;
5981
}
6082
if (typeof aKey === "symbol") {

0 commit comments

Comments
 (0)