Skip to content

Commit cc99f7e

Browse files
authored
fix: Patterns and Keys (#4210)
1 parent 2c92c13 commit cc99f7e

22 files changed

+993
-546
lines changed

packages/marshal/index.js

+2-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export {
44
isObject,
55
assertChecker,
66
getTag,
7+
hasOwnPropertyOf,
78
} from './src/helpers/passStyle-helpers.js';
89

910
export { getErrorConstructor, toPassableError } from './src/helpers/error.js';
@@ -17,12 +18,7 @@ export {
1718
passableSymbolForName,
1819
} from './src/helpers/symbol.js';
1920

20-
export {
21-
passStyleOf,
22-
assertPassable,
23-
everyPassableChild,
24-
somePassableChild,
25-
} from './src/passStyleOf.js';
21+
export { passStyleOf, assertPassable } from './src/passStyleOf.js';
2622

2723
export { pureCopy, sameValueZero } from './src/pureCopy.js';
2824
export { deeplyFulfilled } from './src/deeplyFulfilled.js';

packages/marshal/src/assertPassStyleOf.js

+4-12
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,11 @@ import { passStyleOf } from './passStyleOf.js';
44

55
const { details: X, quote: q } = assert;
66

7-
/**
8-
* @typedef {Array} CopyArray
9-
*/
10-
11-
/**
12-
* @typedef {Record<string, Passable>} CopyRecord
13-
*/
14-
157
/**
168
* Check whether the argument is a pass-by-copy array, AKA a "copyArray"
179
* in @agoric/marshal terms
1810
*
19-
* @param {CopyArray} array
11+
* @param {CopyArray<any>} array
2012
* @returns {boolean}
2113
*/
2214
const isCopyArray = array => passStyleOf(array) === 'copyArray';
@@ -26,7 +18,7 @@ harden(isCopyArray);
2618
* Check whether the argument is a pass-by-copy record, AKA a
2719
* "copyRecord" in @agoric/marshal terms
2820
*
29-
* @param {CopyRecord} record
21+
* @param {CopyRecord<any>} record
3022
* @returns {boolean}
3123
*/
3224
const isRecord = record => passStyleOf(record) === 'copyRecord';
@@ -45,7 +37,7 @@ harden(isRemotable);
4537
* Assert that the argument is a pass-by-copy array, AKA a "copyArray"
4638
* in @agoric/marshal terms
4739
*
48-
* @param {CopyArray} array
40+
* @param {CopyArray<any>} array
4941
* @param {string=} optNameOfArray
5042
* @returns {void}
5143
*/
@@ -64,7 +56,7 @@ harden(assertCopyArray);
6456
* Assert that the argument is a pass-by-copy record, or a
6557
* "copyRecord" in @agoric/marshal terms
6658
*
67-
* @param {CopyRecord} record
59+
* @param {CopyRecord<any>} record
6860
* @param {string=} optNameOfRecord
6961
* @returns {void}
7062
*/

packages/marshal/src/helpers/copyArray.js

+1-6
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,6 @@ export const CopyArrayHelper = harden({
4444
TypeError,
4545
);
4646
// Recursively validate that each member is passable.
47-
CopyArrayHelper.every(candidate, v => !!passStyleOfRecur(v));
47+
candidate.every(v => !!passStyleOfRecur(v));
4848
},
49-
50-
every: (passable, fn) =>
51-
// Note that we explicitly call `fn` with only the arguments we want
52-
// to provide.
53-
passable.every((v, i) => fn(v, i)),
5449
});

packages/marshal/src/helpers/copyRecord.js

+1-7
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ const { ownKeys } = Reflect;
1818
const {
1919
getPrototypeOf,
2020
getOwnPropertyDescriptors,
21-
entries,
2221
prototype: objectPrototype,
2322
} = Object;
2423

@@ -62,11 +61,6 @@ export const CopyRecordHelper = harden({
6261
checkNormalProperty(candidate, name, 'string', true, assertChecker);
6362
}
6463
// Recursively validate that each member is passable.
65-
CopyRecordHelper.every(candidate, v => !!passStyleOfRecur(v));
64+
Object.values(candidate).every(v => !!passStyleOfRecur(v));
6665
},
67-
68-
every: (passable, fn) =>
69-
// Note that we explicitly call `fn` with only the arguments we want
70-
// to provide.
71-
entries(passable).every(([k, v]) => fn(v, k)),
7266
});

packages/marshal/src/helpers/error.js

-2
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,6 @@ export const ErrorHelper = harden({
9595
assertValid: candidate => {
9696
ErrorHelper.canBeValid(candidate, assertChecker);
9797
},
98-
99-
every: (_passable, _fn) => true,
10098
});
10199

102100
/**

packages/marshal/src/helpers/internal-types.js

-6
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,4 @@
2727
* @property {(candidate: any,
2828
* passStyleOfRecur: PassStyleOf
2929
* ) => void} assertValid
30-
*
31-
* @property {(passable: Passable,
32-
* fn: (passable: Passable, index: any) => boolean
33-
* ) => boolean} every
34-
* For recuring through the nested passable structure. Like
35-
* `Array.prototype.every`, return `false` to stop early.
3630
*/

packages/marshal/src/helpers/tagged.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ export const TaggedHelper = harden({
5151
checkNormalProperty(candidate, 'payload', 'string', true, assertChecker);
5252

5353
// Recursively validate that each member is passable.
54-
TaggedHelper.every(candidate, v => !!passStyleOfRecur(v));
54+
!!passStyleOfRecur(candidate.payload);
5555
},
56-
57-
every: (passable, fn) => fn(passable.payload, 'payload'),
5856
});

packages/marshal/src/passStyleOf.js

+4-21
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ const { isFrozen } = Object;
2828
* does what it is supposed to do. `makePassStyleOf` is not trying to defend
2929
* itself against malicious helpers, though it does defend against some
3030
* accidents.
31-
* @returns {{passStyleOf: PassStyleOf, HelperTable: any}}
31+
* @returns {PassStyleOf}
3232
*/
33-
const makePassStyleOfKit = passStyleHelpers => {
33+
const makePassStyleOf = passStyleHelpers => {
3434
const HelperTable = {
3535
__proto__: null,
3636
copyArray: undefined,
@@ -179,35 +179,18 @@ const makePassStyleOfKit = passStyleHelpers => {
179179

180180
return passStyleOfRecur(passable);
181181
};
182-
return harden({ passStyleOf, HelperTable });
182+
return harden(passStyleOf);
183183
};
184184

185-
const { passStyleOf, HelperTable } = makePassStyleOfKit([
185+
export const passStyleOf = makePassStyleOf([
186186
CopyArrayHelper,
187187
CopyRecordHelper,
188188
TaggedHelper,
189189
RemotableHelper,
190190
ErrorHelper,
191191
]);
192-
export { passStyleOf };
193192

194193
export const assertPassable = val => {
195194
passStyleOf(val); // throws if val is not a passable
196195
};
197196
harden(assertPassable);
198-
199-
export const everyPassableChild = (passable, fn) => {
200-
const passStyle = passStyleOf(passable);
201-
const helper = HelperTable[passStyle];
202-
if (helper) {
203-
// everyPassable guards .every so that each helper only gets a
204-
// genuine passable of its own flavor.
205-
return helper.every(passable, fn);
206-
}
207-
return true;
208-
};
209-
harden(everyPassableChild);
210-
211-
export const somePassableChild = (passable, fn) =>
212-
!everyPassableChild(passable, (v, i) => !fn(v, i));
213-
harden(somePassableChild);

packages/marshal/src/types.js

+10
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@
9797
* The leaves of a Passable's pass-by-copy superstructure.
9898
*/
9999

100+
/**
101+
* @template T
102+
* @typedef {T[]} CopyArray
103+
*/
104+
105+
/**
106+
* @template T
107+
* @typedef {Record<string, T>} CopyRecord
108+
*/
109+
100110
/**
101111
* @typedef {{
102112
* [PASS_STYLE]: 'tagged',

packages/store/src/index.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@
33
export { isKey, assertKey } from './keys/checkKey.js';
44
export { keyLT, keyLTE, keyEQ, keyGTE, keyGT } from './keys/compareKeys.js';
55

6-
export { makePatternKit } from './patterns/patternMatchers.js';
7-
export { compareRank } from './patterns/rankOrder.js';
6+
export {
7+
M,
8+
isPattern,
9+
assertPattern,
10+
matches,
11+
fit,
12+
} from './patterns/patternMatchers.js';
13+
// export { compareRank } from './patterns/rankOrder.js';
814

915
export { makeScalarWeakSetStore } from './stores/scalarWeakSetStore.js';
1016
export { makeScalarSetStore } from './stores/scalarSetStore.js';

packages/store/src/keys/checkKey.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import {
77
assertChecker,
88
assertPassable,
9-
everyPassableChild,
109
getTag,
1110
isObject,
1211
passStyleOf,
@@ -94,10 +93,13 @@ const checkKeyInternal = (val, check = x => x) => {
9493

9594
const passStyle = passStyleOf(val);
9695
switch (passStyle) {
97-
case 'copyRecord':
96+
case 'copyRecord': {
97+
// A copyRecord is a key iff all its children are keys
98+
return Object.values(val).every(checkIt);
99+
}
98100
case 'copyArray': {
99-
// A copyRecord or copyArray is a key iff all its children are keys
100-
return everyPassableChild(val, checkIt);
101+
// A copyArray is a key iff all its children are keys
102+
return val.every(checkIt);
101103
}
102104
case 'tagged': {
103105
const tag = getTag(val);

packages/store/src/keys/copyMap.js

+75-19
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
makeTagged,
77
passStyleOf,
88
} from '@agoric/marshal';
9-
import { compareRank, sortByRank } from '../patterns/rankOrder.js';
9+
import { compareAntiRank, sortByRank } from '../patterns/rankOrder.js';
1010
import { checkCopySetKeys } from './copySet.js';
1111

1212
// eslint-disable-next-line spaced-comment
@@ -15,7 +15,7 @@ import { checkCopySetKeys } from './copySet.js';
1515
const { details: X } = assert;
1616
const { ownKeys } = Reflect;
1717

18-
/** @type WeakSet<CopyMap> */
18+
/** @type WeakSet<CopyMap<any,any>> */
1919
const copyMapMemo = new WeakSet();
2020

2121
/**
@@ -63,49 +63,105 @@ export const assertCopyMap = m => checkCopyMap(m, assertChecker);
6363
harden(assertCopyMap);
6464

6565
/**
66-
* @param {CopyMap} m
67-
* @param {(v: Passable, i: number) => boolean} fn
68-
* @returns {boolean}
66+
* @template K,V
67+
* @param {CopyMap<K,V>} m
68+
* @returns {K[]}
6969
*/
70-
export const everyCopyMapKey = (m, fn) => {
70+
export const getCopyMapKeys = m => {
7171
assertCopyMap(m);
72-
return m.payload.keys.every((v, i) => fn(v, i));
72+
return m.payload.keys;
7373
};
74-
harden(everyCopyMapKey);
74+
harden(getCopyMapKeys);
7575

7676
/**
77-
* @param {CopyMap} m
78-
* @param {(v: Passable, i: number) => boolean} fn
79-
* @returns {boolean}
77+
* @template K,V
78+
* @param {CopyMap<K,V>} m
79+
* @returns {V[]}
8080
*/
81-
export const everyCopyMapValue = (m, fn) => {
81+
export const getCopyMapValues = m => {
8282
assertCopyMap(m);
83-
return m.payload.values.every((v, i) => fn(v, i));
83+
return m.payload.values;
8484
};
85+
harden(getCopyMapValues);
86+
87+
/**
88+
* @template K,V
89+
* @param {CopyMap<K,V>} m
90+
* @returns {Iterable<[K,V]>}
91+
*/
92+
export const getCopyMapEntries = m => {
93+
assertCopyMap(m);
94+
const {
95+
payload: { keys, values },
96+
} = m;
97+
const { length } = keys;
98+
return harden({
99+
[Symbol.iterator]: () => {
100+
let i = 0;
101+
return harden({
102+
next: () => {
103+
/** @type {IteratorResult<[K,V],void>} */
104+
let result;
105+
if (i < length) {
106+
result = harden({ done: false, value: [keys[i], values[i]] });
107+
i += 1;
108+
return result;
109+
} else {
110+
result = harden({ done: true, value: undefined });
111+
}
112+
return result;
113+
},
114+
});
115+
},
116+
});
117+
};
118+
harden(getCopyMapEntries);
119+
120+
/**
121+
* @template K,V
122+
* @param {CopyMap<K,V>} m
123+
* @param {(key: K, index: number) => boolean} fn
124+
* @returns {boolean}
125+
*/
126+
export const everyCopyMapKey = (m, fn) =>
127+
getCopyMapKeys(m).every((key, index) => fn(key, index));
128+
harden(everyCopyMapKey);
129+
130+
/**
131+
* @template K,V
132+
* @param {CopyMap<K,V>} m
133+
* @param {(value: V, index: number) => boolean} fn
134+
* @returns {boolean}
135+
*/
136+
export const everyCopyMapValue = (m, fn) =>
137+
getCopyMapValues(m).every((value, index) => fn(value, index));
85138
harden(everyCopyMapValue);
86139

87140
/**
88-
* @param {CopyMap} m
89-
* @returns {CopySet}
141+
* @template K,V
142+
* @param {CopyMap<K,V>} m
143+
* @returns {CopySet<K>}
90144
*/
91145
export const copyMapKeySet = m =>
92146
// A copyMap's keys are already in the internal form used by copySets.
93147
makeTagged('copySet', m.payload.keys);
94148
harden(copyMapKeySet);
95149

96150
/**
97-
* @param {Iterable<[Passable, Passable]>} entries
98-
* @returns {CopyMap}
151+
* @template K,V
152+
* @param {Iterable<[K, V]>} entries
153+
* @returns {CopyMap<K,V>}
99154
*/
100155
export const makeCopyMap = entries => {
101156
// This is weird, but reverse rank sorting the entries is a good first step
102157
// for getting the rank sorted keys together with the values
103158
// organized by those keys. Also, among values associated with
104-
// keys in the same equivalence class, those are rank sorted. This
159+
// keys in the same equivalence class, those are rank sorted.
160+
// TODO This
105161
// could solve the copyMap cover issue explained in patternMatchers.js.
106162
// But only if we include this criteria in our validation of copyMaps,
107163
// which we currently do not.
108-
const sortedEntries = [...sortByRank(entries, compareRank)].reverse();
164+
const sortedEntries = sortByRank(entries, compareAntiRank);
109165
const keys = sortedEntries.map(([k, _v]) => k);
110166
const values = sortedEntries.map(([_k, v]) => v);
111167
return makeTagged('copyMap', { keys, values });

0 commit comments

Comments
 (0)