Skip to content

Commit

Permalink
Merge pull request Agoric#61 from Agoric/new-ertp
Browse files Browse the repository at this point in the history
ERTP with full higher-order composition of smart contracts
  • Loading branch information
erights authored May 31, 2019
2 parents 701732c + 3ae77b4 commit af655cb
Show file tree
Hide file tree
Showing 19 changed files with 2,552 additions and 786 deletions.
199 changes: 199 additions & 0 deletions collections/EMap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// Copyright (C) 2019 Agoric, under Apache License 2.0

import harden from '@agoric/harden';

import { makePrivateName } from './PrivateName';
import { insist } from './insist';

// Maps from EMaps to encapsulated Maps. All lookups from this table
// are only queries. (Except for the one in the FlexMap constructor)
const hiddenEMap = makePrivateName();

// Abstract superclass with query-only methods.
class EMap {
constructor(optIterable = undefined) {
insist(new.target !== EMap)`\
EMap is abstract`;
const newHidden = new Map(optIterable);
hiddenEMap.init(this, newHidden);
}

snapshot() {
// copy
// eslint-disable-next-line no-use-before-define
return new FixedMap(hiddenEMap.get(this));
}

diverge() {
// copy
// eslint-disable-next-line no-use-before-define
return new FlexMap(hiddenEMap.get(this));
}

readOnlyView() {
// eslint-disable-next-line no-use-before-define
const result = new InternalReadOnlyMap();
// Share the hidden map itself, but the readOnlyView only grants
// the ability to query it.
hiddenEMap.init(result, hiddenEMap.get(this));
return result;
}

// Forward query protocol from Map

keys() {
return hiddenEMap.get(this).keys();
}

values() {
return hiddenEMap.get(this).values();
}

entries() {
return hiddenEMap.get(this).entries();
}

[Symbol.iterator]() {
return hiddenEMap.get(this)[Symbol.iterator]();
}

forEach(callback) {
return hiddenEMap.get(this).forEach(callback);
}

get(member) {
return hiddenEMap.get(this).get(member);
}

has(member) {
return hiddenEMap.get(this).has(member);
}

get size() {
return hiddenEMap.get(this).size;
}
}
harden(EMap);

// Guarantees that the map contents is stable.
// TODO: Somehow arrange for this to be pass-by-copy-ish.
class FixedMap extends EMap {
constructor(optIterable = undefined) {
insist(new.target === FixedMap)`\
FixedMap is final`;
super(optIterable);
harden(this);
}

// override
snapshot() {
return this;
}

// override
readOnlyView() {
return this;
}
}
harden(FixedMap);

// Maps from FlexMaps to encapsulated Maps, a subset of
// hiddenEMap. Lookups from this table can mutate.
const hiddenFlexMap = makePrivateName();

// Supports mutation.
class FlexMap extends EMap {
constructor(optIterable = undefined) {
insist(new.target === FlexMap)`\
FlexMap is final`;
super(optIterable);
// Be very scared of the following line, since it looks up on
// hiddenEMap for purposes of enabling mutation. We assume it is
// safe because the `new.target` insist check above ensures this
// constructor is being called as-if directly with `new`. We say
// "as-if" because it might be invoked by `Reflect.construct`, but
// only in an equivalent manner.
hiddenFlexMap.init(this, hiddenEMap.get(this));
harden(this);
}

// Like snapshot() except that this FlexMap loses ownership and
// becomes useless.
takeSnapshot() {
const hiddenMap = hiddenFlexMap.get(this);

// Ideally we'd delete, as we would from a WeakMap. However,
// PrivateName, to emulate class private names, has no delete.
// hiddenFlexMap.delete(this);
// hiddenEMap.delete(this);
hiddenFlexMap.set(this, null);
hiddenEMap.set(this, null);

const result = new FixedMap();
hiddenEMap.init(result, hiddenMap);
return result;
}

// Like diverge() except that this FlexMap loses ownership and
// becomes useless.
takeDiverge() {
const hiddenMap = hiddenFlexMap.get(this);

// Ideally we'd delete, as we would from a WeakMap. However,
// PrivateName, to emulate class private names, has no delete.
// hiddenFlexMap.delete(this);
// hiddenEMap.delete(this);
hiddenFlexMap.set(this, null);
hiddenEMap.set(this, null);

const result = new FlexMap();
hiddenEMap.init(result, hiddenMap);
hiddenFlexMap.init(result, hiddenMap);
return result;
}

// Forward update protocol from Map

set(k, v) {
return hiddenFlexMap.get(this).set(k, v);
}

clear() {
return hiddenFlexMap.get(this).clear();
}

delete(m) {
return hiddenFlexMap.get(this).delete(m);
}
}
harden(FlexMap);

// The constructor for internal use only. The rest of the class is
// available from the pseudo-constructor ReadOnlyMap.
class InternalReadOnlyMap extends EMap {
constructor() {
super();
harden(this);
}

// override
readOnlyView() {
return this;
}
}

// Fake constructor becomes the public identity of the class.
// Guarantee that an instance of ReadOnlyMap does not provide the
// ability to modify.
function ReadOnlyMap() {
insist(new.target === ReadOnlyMap)`\
ReadOnlyMap is final`;
insist(false)`\
Use readOnlyView() to view an existing EMap`;
}
Object.setPrototypeOf(ReadOnlyMap, EMap);
ReadOnlyMap.prototype = InternalReadOnlyMap.prototype;
ReadOnlyMap.prototype.constructor = ReadOnlyMap;
harden(ReadOnlyMap);

export { EMap, FixedMap, FlexMap, ReadOnlyMap };
195 changes: 195 additions & 0 deletions collections/ESet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
// Copyright (C) 2019 Agoric, under Apache License 2.0

import harden from '@agoric/harden';

import { makePrivateName } from './PrivateName';
import { insist } from './insist';

// Maps from ESets to encapsulated Sets. All lookups from this table
// are only queries. (Except for the one in the FlexSet constructor)
const hiddenESet = makePrivateName();

// Abstract superclass with query-only methods.
class ESet {
constructor(optIterable = undefined) {
insist(new.target !== ESet)`\
ESet is abstract`;
const newHidden = new Set(optIterable);
hiddenESet.init(this, newHidden);
}

snapshot() {
// copy
// eslint-disable-next-line no-use-before-define
return new FixedSet(hiddenESet.get(this));
}

diverge() {
// copy
// eslint-disable-next-line no-use-before-define
return new FlexSet(hiddenESet.get(this));
}

readOnlyView() {
// eslint-disable-next-line no-use-before-define
const result = new InternalReadOnlySet();
// Share the hidden set itself, but the readOnlyView only grants
// the ability to query it.
hiddenESet.init(result, hiddenESet.get(this));
return result;
}

// Forward query protocol from Set

keys() {
return hiddenESet.get(this).keys();
}

values() {
return hiddenESet.get(this).values();
}

entries() {
return hiddenESet.get(this).entries();
}

[Symbol.iterator]() {
return hiddenESet.get(this)[Symbol.iterator]();
}

forEach(callback) {
return hiddenESet.get(this).forEach(callback);
}

has(member) {
return hiddenESet.get(this).has(member);
}

get size() {
return hiddenESet.get(this).size;
}
}
harden(ESet);

// Guarantees that the set contents is stable.
// TODO: Somehow arrange for this to be pass-by-copy-ish.
class FixedSet extends ESet {
constructor(optIterable = undefined) {
insist(new.target === FixedSet)`\
FixedSet is final`;
super(optIterable);
harden(this);
}

// override
snapshot() {
return this;
}

// override
readOnlyView() {
return this;
}
}
harden(FixedSet);

// Maps from FlexSets to encapsulated Sets, a subset of
// hiddenESet. Lookups from this table can mutate.
const hiddenFlexSet = makePrivateName();

// Supports mutation.
class FlexSet extends ESet {
constructor(optIterable = undefined) {
insist(new.target === FlexSet)`\
FlexSet is final`;
super(optIterable);
// Be very scared of the following line, since it looks up on
// hiddenESet for purposes of enabling mutation. We assume it is
// safe because the `new.target` insist check above ensures this
// constructor is being called as-if directly with `new`. We say
// "as-if" because it might be invoked by `Reflect.construct`, but
// only in an equivalent manner.
hiddenFlexSet.init(this, hiddenESet.get(this));
harden(this);
}

// Like snapshot() except that this FlexSet loses ownership and
// becomes useless.
takeSnapshot() {
const hiddenSet = hiddenFlexSet.get(this);

// Ideally we'd delete, as we would from a WeakMap. However,
// PrivateName, to emulate class private names, has no delete.
// hiddenFlexSet.delete(this);
// hiddenESet.delete(this);
hiddenFlexSet.set(this, null);
hiddenESet.set(this, null);

const result = new FixedSet();
hiddenESet.init(result, hiddenSet);
return result;
}

// Like diverge() except that this FlexSet loses ownership and
// becomes useless.
takeDiverge() {
const hiddenSet = hiddenFlexSet.get(this);

// Ideally we'd delete, as we would from a WeakMap. However,
// PrivateName, to emulate class private names, has no delete.
// hiddenFlexSet.delete(this);
// hiddenESet.delete(this);
hiddenFlexSet.set(this, null);
hiddenESet.set(this, null);

const result = new FlexSet();
hiddenESet.init(result, hiddenSet);
hiddenFlexSet.init(result, hiddenSet);
return result;
}

// Forward update protocol from Set

add(m) {
return hiddenFlexSet.get(this).add(m);
}

clear() {
return hiddenFlexSet.get(this).clear();
}

delete(m) {
return hiddenFlexSet.get(this).delete(m);
}
}
harden(FlexSet);

// The constructor for internal use only. The rest of the class is
// available from the pseudo-constructor ReadOnlySet.
class InternalReadOnlySet extends ESet {
constructor() {
super();
harden(this);
}

// override
readOnlyView() {
return this;
}
}

// Fake constructor becomes the public identity of the class.
// Guarantee that an instance of ReadOnlySet does not provide the
// ability to modify.
function ReadOnlySet() {
insist(new.target === ReadOnlySet)`\
ReadOnlySet is final`;
insist(false)`\
Use readOnlyView() to view an existing ESet`;
}
Object.setPrototypeOf(ReadOnlySet, ESet);
ReadOnlySet.prototype = InternalReadOnlySet.prototype;
ReadOnlySet.prototype.constructor = ReadOnlySet;
harden(ReadOnlySet);

export { ESet, FixedSet, FlexSet, ReadOnlySet };
Loading

0 comments on commit af655cb

Please sign in to comment.