Skip to content

Commit ded3084

Browse files
committed
fix: prepare for symbol flip
1 parent cd45c09 commit ded3084

File tree

23 files changed

+274
-143
lines changed

23 files changed

+274
-143
lines changed

packages/SwingSet/test/promises.test.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// @ts-nocheck
2+
// eslint-disable-next-line import/order
23
import { test } from '../tools/prepare-test-env-ava.js';
34

4-
// eslint-disable-next-line import/order
5+
import { passableSymbolForName } from '@agoric/internal';
6+
import { testFullOrderEQ } from '@agoric/internal/tools/ava-full-order-eq.js';
57
import { kser, kslot, kunser } from '@agoric/kmarshal';
68
import {
79
buildVatController,
@@ -259,7 +261,7 @@ test('local promises are rejected by vat upgrade', async t => {
259261
return awaitRun(kpid);
260262
};
261263

262-
const S = Symbol.for('passable');
264+
const S = passableSymbolForName('passable');
263265
const watcher = await messageToVat('bootstrap', 'createVat', {
264266
name: 'watcher',
265267
bundleCapName: 'watcher',
@@ -268,7 +270,7 @@ test('local promises are rejected by vat upgrade', async t => {
268270
await messageToObject(watcher, 'watchLocalPromise', 'fulfilled', S);
269271
await messageToObject(watcher, 'watchLocalPromise', 'rejected', undefined, S);
270272
const v1Settlements = await messageToObject(watcher, 'getSettlements');
271-
t.deepEqual(v1Settlements, {
273+
testFullOrderEQ(t, v1Settlements, {
272274
fulfilled: { status: 'fulfilled', value: S },
273275
rejected: { status: 'rejected', reason: S },
274276
});
@@ -277,7 +279,7 @@ test('local promises are rejected by vat upgrade', async t => {
277279
bundleCapName: 'watcher',
278280
});
279281
const v2Settlements = await messageToObject(watcher, 'getSettlements');
280-
t.deepEqual(v2Settlements, {
282+
testFullOrderEQ(t, v2Settlements, {
281283
fulfilled: { status: 'fulfilled', value: S },
282284
rejected: { status: 'rejected', reason: S },
283285
orphaned: {

packages/cosmic-swingset/src/helpers/bufferedStorage.js

Lines changed: 46 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { assert, Fail } from '@endo/errors';
2+
import { unpassableSymbolForName } from '@agoric/internal';
23

34
// XXX Do these "StorageAPI" functions belong in their own package?
45

@@ -302,8 +303,8 @@ export function makeBufferedStorage(kvStore, listeners = {}) {
302303
export const makeReadCachingStorage = kvStore => {
303304
// In addition to the wrapping write buffer, keep a simple cache of
304305
// read values for has and get.
305-
const deleted = Symbol('deleted');
306-
const undef = Symbol('undefined');
306+
const deleted = unpassableSymbolForName('deleted');
307+
const undef = unpassableSymbolForName('undefined');
307308
/** @typedef {(typeof deleted) | (typeof undef)} ReadCacheSentinel */
308309
/** @type {Map<string, T | ReadCacheSentinel>} */
309310
let cache;
@@ -312,50 +313,51 @@ export const makeReadCachingStorage = kvStore => {
312313
}
313314
resetCache();
314315

315-
/** @type {KVStore<T>} */
316-
const storage = harden({
317-
has(key) {
318-
const value = cache.get(key);
319-
if (value !== undefined) {
320-
return value !== deleted;
321-
} else {
322-
const ret = kvStore.has(key);
323-
if (!ret) {
324-
cache.set(key, deleted);
316+
const storage = /** @type {KVStore<T>} */ (
317+
harden({
318+
has(key) {
319+
const value = cache.get(key);
320+
if (value !== undefined) {
321+
return value !== deleted;
322+
} else {
323+
const ret = kvStore.has(key);
324+
if (!ret) {
325+
cache.set(key, deleted);
326+
}
327+
return ret;
328+
}
329+
},
330+
get(key) {
331+
let value = cache.get(key);
332+
if (value !== undefined) {
333+
return value === deleted || value === undef ? undefined : value;
325334
}
326-
return ret;
327-
}
328-
},
329-
get(key) {
330-
let value = cache.get(key);
331-
if (value !== undefined) {
332-
return value === deleted || value === undef ? undefined : value;
333-
}
334335

335-
// Fetch the value and cache it until the next commit or abort.
336-
value = kvStore.get(key);
337-
cache.set(key, value === undefined ? undef : value);
338-
return value;
339-
},
340-
set(key, value) {
341-
// Set the value and cache it until the next commit or abort (which is
342-
// expected immediately, since the buffered wrapper only calls set
343-
// *during* a commit).
344-
// `undefined` is a valid value technically different than deletion,
345-
// depending on how the underlying store does its serialization
346-
cache.set(key, value === undefined ? undef : value);
347-
kvStore.set(key, value);
348-
},
349-
delete(key) {
350-
// Delete the value and cache the deletion until next commit or abort.
351-
// Deletion results in `undefined` on `get`, but `false` on `has`
352-
cache.set(key, deleted);
353-
kvStore.delete(key);
354-
},
355-
getNextKey(_previousKey) {
356-
throw Error('not implemented');
357-
},
358-
});
336+
// Fetch the value and cache it until the next commit or abort.
337+
value = kvStore.get(key);
338+
cache.set(key, value === undefined ? undef : value);
339+
return value;
340+
},
341+
set(key, value) {
342+
// Set the value and cache it until the next commit or abort (which is
343+
// expected immediately, since the buffered wrapper only calls set
344+
// *during* a commit).
345+
// `undefined` is a valid value technically different than deletion,
346+
// depending on how the underlying store does its serialization
347+
cache.set(key, value === undefined ? undef : value);
348+
kvStore.set(key, value);
349+
},
350+
delete(key) {
351+
// Delete the value and cache the deletion until next commit or abort.
352+
// Deletion results in `undefined` on `get`, but `false` on `has`
353+
cache.set(key, deleted);
354+
kvStore.delete(key);
355+
},
356+
getNextKey(_previousKey) {
357+
throw Error('not implemented');
358+
},
359+
})
360+
);
359361
const {
360362
kvStore: buffered,
361363
commit,

packages/cosmic-swingset/test/export-storage.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import type { TestFn } from 'ava';
22
import anyTest from 'ava';
3+
4+
import { unpassableSymbolForName } from '@agoric/internal';
35
import { exportStorage } from '../src/export-storage.js';
46

57
const test = anyTest as TestFn;
68

79
// Export this binding when used by other modules.
8-
const TestDataKey = Symbol('TestDataKey');
10+
const TestDataKey = unpassableSymbolForName('TestDataKey');
911

1012
// Create a local alias, then
1113
// - regex replace '\._\b' with '[_]'

packages/import-manager/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
"url": "https://github.com/Agoric/agoric-sdk/issues"
3030
},
3131
"homepage": "https://github.com/Agoric/agoric-sdk#readme",
32+
"dependencies": {
33+
"@agoric/internal": "^0.3.2"
34+
},
3235
"devDependencies": {
3336
"@agoric/swingset-vat": "^0.32.2",
3437
"ava": "^5.3.0",

packages/import-manager/test/unitTests/badImports.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
// Copyright (C) 2019 Agoric, under Apache License 2.0
22

3+
import { unpassableSymbolForName } from '@agoric/internal';
34
import { importManager } from '../../src/importManager.js';
45
import { listIsEmpty, numIsEmpty } from './valueOps.js';
56

67
function makeBadImportManager() {
78
const mgr = importManager();
89
const obj = { numIsEmpty };
9-
const fooSym = Symbol('foo');
10+
const fooSym = unpassableSymbolForName('foo');
1011
obj[fooSym] = listIsEmpty;
1112
return mgr.addExports(obj);
1213
}

packages/inter-protocol/test/vaultFactory/driver.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { AmountMath, makeIssuerKit } from '@agoric/ertp';
2-
import { allValues, makeTracer, NonNullish, objectMap } from '@agoric/internal';
2+
import {
3+
allValues,
4+
makeTracer,
5+
NonNullish,
6+
objectMap,
7+
unpassableSymbolForName,
8+
} from '@agoric/internal';
39
import { makeNotifierFromSubscriber } from '@agoric/notifier';
410
import { unsafeMakeBundleCache } from '@agoric/swingset-vat/tools/bundleTool.js';
511
import {
@@ -38,7 +44,11 @@ import {
3844

3945
const trace = makeTracer('VFDriver');
4046

41-
export const AT_NEXT = Symbol('AT_NEXT');
47+
// TODO What does it mean to type something to the typeof itself?
48+
// That was the inferred type when the right hand side was `Symbol('AT_NEXT')`
49+
export const AT_NEXT = /** @type {typeof AT_NEXT} */ (
50+
unpassableSymbolForName('AT_NEXT')
51+
);
4252

4353
export const BASIS_POINTS = 10000n;
4454

packages/internal/src/ses-utils.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { objectMap } from '@endo/common/object-map.js';
99
import { objectMetaMap } from '@endo/common/object-meta-map.js';
1010
import { fromUniqueEntries } from '@endo/common/from-unique-entries.js';
1111
import { q, Fail, makeError, annotateError, X } from '@endo/errors';
12+
import { passableSymbolForName as psfn } from '@endo/pass-style';
1213
import { deeplyFulfilled, isObject } from '@endo/marshal';
1314
import { makePromiseKit } from '@endo/promise-kit';
1415
import { makeQueue } from '@endo/stream';
@@ -420,3 +421,29 @@ export const synchronizedTee = (sourceStream, readerCount) => {
420421
void pullNext();
421422
return readers;
422423
};
424+
425+
/**
426+
* An adapter to help transition to flip which symbols are passable. In the endo
427+
* release that's current as of this writing, the real `passableSymbolForName`
428+
* returns `symbol|undefined`.
429+
*
430+
* @param {string} name
431+
* @returns {symbol}
432+
*/
433+
export const passableSymbolForName = name => /** @type {symbol} */ (psfn(name));
434+
435+
const areRegisteredSymbolsPassable =
436+
// TODO Symbol should not be a restricted global for @agoric/internal
437+
// eslint-disable-next-line no-restricted-globals
438+
Symbol.keyFor(passableSymbolForName('x')) === 'x';
439+
440+
/**
441+
* An adapter to help transition to flip which symbols are passable.
442+
*
443+
* @param {string} name
444+
* @returns {symbol}
445+
*/
446+
export const unpassableSymbolForName = name =>
447+
// TODO Symbol should not be a restricted global for @agoric/internal
448+
// eslint-disable-next-line no-restricted-globals
449+
areRegisteredSymbolsPassable ? Symbol(name) : Symbol.for(name);

packages/internal/test/callback.test.js

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import test from 'ava';
44
import { Far } from '@endo/far';
55
import { makeHeapZone } from '@agoric/base-zone/heap.js';
66
import * as cb from '../src/callback.js';
7+
import {
8+
passableSymbolForName,
9+
unpassableSymbolForName,
10+
} from '../src/ses-utils.js';
711

812
/** @import {Callback, SyncCallback} from '../src/types.js' */
913

@@ -50,7 +54,11 @@ test('near function callbacks', t => {
5054
});
5155

5256
test('near method callbacks', t => {
53-
const m2 = Symbol.for('m2');
57+
const m2 = passableSymbolForName('m2');
58+
// TODO This currently has a symbol-named method. If it passes, it is only
59+
// because `o` was not made remotable. Should this test case me deleted,
60+
// or should `m2` and its uses be deleted, or should `m2` be converted to
61+
// `'m2'`?
5462
const o = {
5563
/**
5664
* @param {number} a
@@ -121,7 +129,6 @@ test('near method callbacks', t => {
121129
});
122130

123131
test('far method callbacks', async t => {
124-
const m2 = Symbol.for('m2');
125132
const o = Far('MyObject', {
126133
/**
127134
* @param {number} a
@@ -139,7 +146,7 @@ test('far method callbacks', async t => {
139146
* @param {string} c
140147
* @returns {Promise<string>}
141148
*/
142-
[m2]: async (a, b, c) => {
149+
m2: async (a, b, c) => {
143150
return `${a + b}${c}`;
144151
},
145152
});
@@ -153,8 +160,8 @@ test('far method callbacks', async t => {
153160
t.is(await p2r, '19go');
154161

155162
/** @type {Callback<(c: string) => Promise<string>>} */
156-
const cbp3 = cb.makeMethodCallback(Promise.resolve(o), m2, 9, 10);
157-
t.like(cbp3, { methodName: m2, bound: [9, 10] });
163+
const cbp3 = cb.makeMethodCallback(Promise.resolve(o), 'm2', 9, 10);
164+
t.like(cbp3, { methodName: 'm2', bound: [9, 10] });
158165
t.assert(cbp3.target instanceof Promise);
159166
const p3r = cb.callE(cbp3, 'go');
160167
t.assert(p3r instanceof Promise);
@@ -236,7 +243,13 @@ test('isCallback', t => {
236243
'manual method',
237244
);
238245
t.true(
239-
cb.isCallback({ target: {}, methodName: Symbol.for('foo'), bound: [] }),
246+
cb.isCallback({
247+
target: {},
248+
// TODO if this passes with a symbol-named method, it is because
249+
// some relevant object was not made into a remotable.
250+
methodName: passableSymbolForName('foo'),
251+
bound: [],
252+
}),
240253
'manual symbol-keyed method',
241254
);
242255

@@ -249,7 +262,11 @@ test('isCallback', t => {
249262
'non-object target',
250263
);
251264
t.false(
252-
cb.isCallback({ target: {}, methodName: Symbol('foo'), bound: [] }),
265+
cb.isCallback({
266+
target: {},
267+
methodName: unpassableSymbolForName('foo'),
268+
bound: [],
269+
}),
253270
'unique symbol method name',
254271
);
255272
t.false(cb.isCallback({ target: {}, bound: {} }), 'non-array bound args');

packages/internal/test/snapshots/exports.test.js.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,13 @@ Generated by [AVA](https://avajs.dev).
5252
'objectMap',
5353
'objectMapMutable',
5454
'objectMetaMap',
55+
'passableSymbolForName',
5556
'provideLazyMap',
5657
'pureDataMarshaller',
5758
'synchronizedTee',
5859
'typedEntries',
5960
'typedMap',
61+
'unpassableSymbolForName',
6062
'unprefixedProperties',
6163
'untilTrue',
6264
'whileTrue',
Binary file not shown.

packages/internal/test/storage-test-utils.test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
makeFakeStorageKit,
99
slotStringUnserialize,
1010
} from '../src/storage-test-utils.js';
11+
import { unpassableSymbolForName } from '../src/ses-utils.js';
1112

1213
test('makeFakeStorageKit', async t => {
1314
const rootPath = 'root';
@@ -29,7 +30,7 @@ test('makeFakeStorageKit', async t => {
2930
boolean: true,
3031
null: null,
3132
undefined,
32-
symbol: Symbol('foo'),
33+
symbol: unpassableSymbolForName('foo'),
3334
array: ['foo'],
3435
object: {
3536
toString() {

packages/internal/test/utils.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
forever,
1313
deeplyFulfilledObject,
1414
synchronizedTee,
15+
unpassableSymbolForName,
1516
} from '../src/ses-utils.js';
1617

1718
/** @import {Permit, Attenuated} from '../src/types.js'; */
@@ -334,7 +335,7 @@ const { value: arbShallow } = fc.letrec(tie => ({
334335
]),
335336
// @ts-expect-error TS2345 function signature
336337
async (t, { specimen, permit }) => {
337-
const tag = Symbol('transformed');
338+
const tag = unpassableSymbolForName('transformed');
338339

339340
let mutationCallCount = 0;
340341
const mutatedAttenuation = attenuate(specimen, permit, obj => {
@@ -527,7 +528,7 @@ test('makeMeasureSeconds', async t => {
527528
const mockNow = () => times.shift();
528529
const measureSeconds = makeMeasureSeconds(mockNow);
529530

530-
const unique = Symbol('unique');
531+
const unique = unpassableSymbolForName('unique');
531532
const output = await measureSeconds(async () => unique);
532533
t.deepEqual(output, { result: unique, duration: 1.0005 });
533534
t.deepEqual(times, [NaN]);

0 commit comments

Comments
 (0)