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

WIP: Passable collections #587

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions packages/marshal/marshal.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,17 +156,6 @@ 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 a symbol, whether registered or not.
Expand Down
7 changes: 4 additions & 3 deletions packages/same-structure/src/sameStructure.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import harden from '@agoric/harden';
import { sameValueZero, passStyleOf } from '@agoric/marshal';
import { passStyleOf } from '@agoric/marshal';
import { assert, details, openDetail } from '@agoric/assert';
import { sameKey } from '../../store/src/store';

// Shim of Object.fromEntries from
// https://github.com/tc39/proposal-object-from-entries/blob/master/polyfill.js
Expand Down Expand Up @@ -114,7 +115,7 @@ function sameStructure(left, right) {
case 'symbol':
case 'bigint':
case 'presence': {
return sameValueZero(left, right);
return sameKey(left, right);
}
case 'copyRecord':
case 'copyArray': {
Expand Down Expand Up @@ -193,7 +194,7 @@ function mustBeSameStructureInternal(left, right, message, path) {
case 'symbol':
case 'bigint':
case 'presence': {
if (!sameValueZero(left, right)) {
if (!sameKey(left, right)) {
complain('different');
}
break;
Expand Down
5 changes: 3 additions & 2 deletions packages/store/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"scripts": {
"build": "exit 0",
"test": "exit 0",
"test": "tape -r esm test/**/test*.js",
"lint-fix": "eslint --fix '**/*.js'",
"lint-check": "eslint '**/*.js'",
"lint-fix-jessie": "eslint -c '.eslintrc-jessie.js' --fix '**/*.js'",
Expand All @@ -29,7 +29,8 @@
"homepage": "https://github.com/Agoric/agoric-sdk#readme",
"dependencies": {
"@agoric/harden": "^0.0.4",
"@agoric/assert": "^0.0.1"
"@agoric/assert": "^0.0.1",
"@agoric/weak-store": "^0.0.1"
},
"devDependencies": {
"esm": "^3.2.25",
Expand Down
121 changes: 96 additions & 25 deletions packages/store/src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,114 @@

import harden from '@agoric/harden';
import { assert, details, openDetail } from '@agoric/assert';

// `sameKey` is the equality comparison used by JavaScript's Map and Set
// abstractions to compare their keys. Its internal JavaScript spec name
// is "SameValueZero". It forms a proper equivalence class that is less
// precise than `Object.is`. Unlike `===`, `sameKey` is an equivalence
// class because `sameKey(NaN, NaN)`. `sameKey` is more precise than
// `Object.is` because `!sameKey(-0, 0)`.
//
// The simple `makeStore` exported by this module makes stores that
// also use `sameKey` equality to compare their keys.
//
// Marshal serializes -0 as zero, so the semantics of our distributed
// object system also does not distinguish 0 from -0.
//
// `sameValueZero` is the EcmaScript spec name for this equality comparison.
// Unlike `===`, `sameKey` is an equivalence class, since
export const sameKey = (a, b) => a === b || Object.is(a, b);

/**
* Distinguishes between adding a new key (init) and updating or
* referencing a key (get, set, delete).
*
* `init` is only allowed if the key does not already exist. `Get`,
* `init` is only allowed if the key does not already exist. `get`,
* `set` and `delete` are only allowed if the key does already exist.
* For any of these methods, an expression like `store.get`,
* extracting the `get` method from a store using dot (`.`), extracts
* a function that is effectively bound to that store, which can be
* passed and used elsewhere.
*
* @param {string} keyName - the column name for the key
*/
function makeStore(keyName = 'key') {
const store = new Map();
const assertKeyDoesNotExist = key =>
const map = new Map();
const assertKeyNotBound = key =>
assert(
!store.has(key),
!map.has(key),
details`${openDetail(keyName)} already registered: ${key}`,
);
const assertKeyExists = key =>
assert(store.has(key), details`${openDetail(keyName)} not found: ${key}`);
const assertKeyBound = key =>
assert(map.has(key), details`${openDetail(keyName)} not found: ${key}`);

// /////// Methods /////////

const has = key => map.has(key);
const init = (key, value) => {
assertKeyNotBound(key);
map.set(key, value);
};
const get = key => {
assertKeyBound(key);
return map.get(key);
};
const set = (key, value) => {
assertKeyBound(key);
map.set(key, value);
};
const deleteIt = key => {
assertKeyBound(key);
map.delete(key);
};
const keys = () => [...map.keys()];
const values = () => [...map.values()];
const entries = () => [...map.entries()];

const diverge = () => {
const result = makeStore(keyName);
for (const [k, v] of entries()) {
result.init(k, v);
}
return result;
};

// eslint-disable-next-line no-use-before-define
const readOnly = () => readOnlyStore;

const snapshot = () => {
const snapshotStore = harden({
...diverge().readOnly(),
snapshot: () => snapshotStore,
});
return snapshotStore;
};

const readOnlyStore = harden({
diverge,
readOnly,
snapshot,
has,
get,
keys,
values,
entries,
[Symbol.iterator]: entries,
});

return harden({
has: key => store.has(key),
init: (key, value) => {
assertKeyDoesNotExist(key);
store.set(key, value);
},
get: key => {
assertKeyExists(key);
return store.get(key);
},
set: (key, value) => {
assertKeyExists(key);
store.set(key, value);
},
delete: key => {
assertKeyExists(key);
store.delete(key);
},
keys: () => Array.from(store.keys()),
values: () => Array.from(store.values()),
diverge,
readOnly,
snapshot,
has,
init,
get,
set,
delete: deleteIt,
keys,
values,
entries,
[Symbol.iterator]: entries,
});
}
harden(makeStore);
Expand Down
Loading