Skip to content

Commit

Permalink
fix(api): remove many unnecessary methods
Browse files Browse the repository at this point in the history
Remove the experimental E.C chaining proxy.

Remove setting, deleting, has methods.
Fixes #41
  • Loading branch information
michaelfig committed Dec 10, 2019
1 parent 8d10e1c commit cf10dc3
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 283 deletions.
97 changes: 5 additions & 92 deletions packages/eventual-send/src/E.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
/* global globalThis window */
/* global SES */
// eslint-disable-next-line spaced-comment
/// <reference path="index.d.ts" />
// Shim globalThis when we don't have it.
if (typeof globalThis === 'undefined') {
const myGlobal = typeof window === 'undefined' ? global : window;
myGlobal.globalThis = myGlobal;
}

const harden = (globalThis.SES && globalThis.SES.harden) || Object.freeze;
const harden = (typeof SES !== 'undefined' && SES.harden) || Object.freeze;

const readOnlyProxy = {
set(_target, _prop, _value) {
Expand Down Expand Up @@ -57,103 +52,21 @@ function EProxyHandler(x, HandledPromise) {
export default function makeE(HandledPromise) {
function E(x) {
const handler = EProxyHandler(x, HandledPromise);
return harden(new Proxy({}, handler));
return harden(new Proxy(() => {}, handler));
}

const makeEGetterProxy = (x, wrap = o => o) =>
new Proxy(Object.create(null), {
...readOnlyProxy,
has(_target, _prop) {
return true;
},
get(_target, prop) {
return wrap(HandledPromise.get(x, prop));
},
});

const makeEDeleterProxy = (x, wrap = o => o) =>
const makeEGetterProxy = x =>
new Proxy(Object.create(null), {
...readOnlyProxy,
has(_target, _prop) {
return true;
},
get(_target, prop) {
return wrap(HandledPromise.delete(x, prop));
},
});

const makeEHasProxy = (x, wrap = o => o) =>
new Proxy(Object.create(null), {
...readOnlyProxy,
has(_target, _prop) {
return true;
},
get(_target, prop) {
return wrap(HandledPromise.has(x, prop));
},
});

const makeESetterProxy = (x, wrap = o => o) =>
new Proxy(Object.create(null), {
...readOnlyProxy,
has(_target, _prop) {
return true;
},
get(_target, prop) {
return harden(value => wrap(HandledPromise.set(x, prop, value)));
},
});

const makeEMethodProxy = (x, wrap = o => o) =>
new Proxy((..._args) => {}, {
...readOnlyProxy,
has(_target, _prop) {
return true;
},
get(_target, prop) {
return harden((...args) =>
wrap(HandledPromise.applyMethod(x, prop, args)),
);
},
apply(_target, _thisArg, args = []) {
return wrap(HandledPromise.applyFunction(x, args));
return HandledPromise.get(x, prop);
},
});

E.G = o => makeEGetterProxy(o);
E.H = o => makeEHasProxy(o);
E.D = o => makeEDeleterProxy(o);
E.S = o => makeESetterProxy(o);
E.M = o => makeEMethodProxy(o);

const EChain = x =>
harden({
get G() {
// Return getter.
return makeEGetterProxy(x, EChain);
},
get D() {
// Return deleter.
return makeEDeleterProxy(x, EChain);
},
get H() {
// Return has.
return makeEHasProxy(x, EChain);
},
get S() {
// Return setter.
return makeESetterProxy(x, EChain);
},
get M() {
// Return method-caller.
return makeEMethodProxy(x, EChain);
},
get P() {
// Return as promise.
return Promise.resolve(x);
},
});

E.C = EChain;
return harden(E);
}
60 changes: 14 additions & 46 deletions packages/eventual-send/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,47 +21,13 @@ interface HandledPromiseConstructor {

declare const HandledPromise: HandledPromiseConstructor;

interface ESingleMethod<U> {
[prop: Property]: (...args) => U;
interface ESingleMethod {
(...args: unknown[]): Promise<unknown>;
[prop: Property]: (...args) => Promise<unknown>;
}

interface EChain<T = unknown> {
M: EChainMethod<EChain<T>>;
G: EChainGet<EChain<T>>;
S: EChainSet<EChain<T>>;
D: EChainDelete<EChain<boolean>>;
P: Promise<T>;
sendOnly: EChainSendOnly;
}

interface EChainSendOnly {
M: EChainMethod<void>;
G: EChainGet<void>;
S: EChainSet<void>;
D: EChainDelete<void>;
}

interface EChainMethod<U> {
(...args: unknown[]): U;
[prop: Property]: (...args: unknown) => U;
}

interface EChainGet<U> {
[prop: Property]: U;
}

interface EChainSet<U> {
/**
* Eventually set the prop property.
*/
[prop: Property]: (value: unknown) => U;
}

interface EChainDelete<U> {
/**
* Eventually delete the prop property.
*/
[prop: Property]: U is void ? U : EChain<boolean>;
interface ESingleGet {
[prop: Property]: Promise<unknown>;
}

interface EProxy {
Expand All @@ -74,16 +40,18 @@ interface EProxy {
* @param {*} x target for method call
* @returns {ESingleMethod} method call proxy
*/
(x: unknown): ESingleMethod<Promise<unknown>>;
sendOnly: (x: unknown) => ESingleMethod<void>;
(x: unknown): ESingleMethod;
sendOnly: (x: unknown) => ESingleMethod;
/**
* E.C(x) returns a chain where operations are selected by
* uppercase single-letter selectors.
* E.G(x) returns a proxy on which you can get arbitrary properties.
* Each of these properties returns a promise. The property will be
* taken from whatever 'x' designates (or resolves to) in a future turn,
* not this one.
*
* @param {*} x target for first operation
* @returns {EChain}
* @param {*} x target for property get
* @returns {ESingleGet} property get proxy
*/
C(x: unknown): EChain;
G(x: unknown): ESingleGet;
}

export const E: EProxy;
Expand Down
62 changes: 17 additions & 45 deletions packages/eventual-send/src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* global globalThis */
/* global HandledPromise SES globalThis window */

import makeE from './E';

Expand All @@ -8,18 +8,27 @@ import makeE from './E';
// import { HandledPromise, E } from '@agoric/eventual-send';
// ...

// TODO: Maybe rename the global HandledPromise to something only the tildot rewriter uses.
if (!globalThis.HandledPromise) {
// eslint-disable-next-line import/no-mutable-exports
let hp;
if (typeof HandledPromise === 'undefined') {
/* eslint-disable no-use-before-define */
if (typeof globalThis === 'undefined') {
const myGlobal = typeof window === 'undefined' ? global : window;
myGlobal.globalThis = window;
}

// Install the shim as best we can.
maybeExtendPromise(Promise);
globalThis.HandledPromise = makeHandledPromise(Promise);
hp = globalThis.HandledPromise;
/* eslint-enable no-use-before-define */
} else {
hp = HandledPromise;
}

// Provide a handled platform Promise if SES has not run.
export const { HandledPromise } = globalThis;
export const E = makeE(HandledPromise);
export { hp as HandledPromise };
export const E = makeE(hp);

// the following methods (makeHandledPromise and maybeExtendPromise) are part
// of the shim, and will not be exported by the module once the feature
Expand All @@ -28,36 +37,17 @@ export const E = makeE(HandledPromise);
// Create HandledPromise static methods as a bridge from v0.2.4
// to new proposal support (wavy dot's infrastructure).
export function makeHandledPromise(EPromise) {
const harden = (globalThis.SES && globalThis.SES.harden) || Object.freeze;
const harden = (typeof SES !== 'undefined' && SES.harden) || Object.freeze;

// TODO: Use HandledPromise.resolve to store our weakmap, and
// install it on Promise.resolve.
// TODO: Use HandledPromise.resolve to store our weakmap, instead of
// monkey-patching Promise.resolve.
const staticMethods = {
get(target, key) {
return EPromise.resolve(target).get(key);
},
getSendOnly(target, key) {
EPromise.resolve(target).get(key);
},
has(target, key) {
return EPromise.resolve(target).has(key);
},
hasSendOnly(target, key) {
EPromise.resolve(target).has(key);
},
set(target, key, val) {
return EPromise.resolve(target).put(key, val);
},
setSendOnly(target, key, val) {
EPromise.resolve(target).put(key, val);
},
// TODO: Change HandledPromise.delete to HandledPromise.deleteProperty
delete(target, key) {
return EPromise.resolve(target).delete(key);
},
deleteSendOnly(target, key) {
EPromise.resolve(target).delete(key);
},
applyFunction(target, args) {
return EPromise.resolve(target).post(undefined, args);
},
Expand Down Expand Up @@ -161,18 +151,6 @@ export function maybeExtendPromise(Promise) {
return handle(this, 'GET', key);
},

has(key) {
return handle(this, 'HAS', key);
},

put(key, val) {
return handle(this, 'PUT', key, val);
},

delete(key) {
return handle(this, 'DELETE', key);
},

post(optKey, args) {
return handle(this, 'POST', optKey, args);
},
Expand Down Expand Up @@ -261,9 +239,6 @@ export function maybeExtendPromise(Promise) {

unfulfilledHandler = {
GET: makePostponed('get'),
HAS: makePostponed('has'),
PUT: makePostponed('put'),
DELETE: makePostponed('delete'),
POST: makePostponed('post'),
};
}
Expand Down Expand Up @@ -391,9 +366,6 @@ export function maybeExtendPromise(Promise) {

forwardingHandler = {
GET: makeForwarder('GET', (o, key) => o[key]),
HAS: makeForwarder('HAS', (o, key) => key in o),
PUT: makeForwarder('PUT', (o, key, val) => (o[key] = val)),
DELETE: makeForwarder('DELETE', (o, key) => delete o[key]),
POST: makeForwarder('POST', (o, optKey, args) => {
if (optKey === undefined || optKey === null) {
return o(...args);
Expand Down
49 changes: 3 additions & 46 deletions packages/eventual-send/test/test-e.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ test('E method calls', async t => {
}
});

test('E.* shortcuts', async t => {
test('E shortcuts', async t => {
try {
const x = {
name: 'buddy',
Expand All @@ -32,56 +32,13 @@ test('E.* shortcuts', async t => {
return `${greeting}, ${this.name}!`;
},
};
t.equal(await E.M(x).hello('Hello'), 'Hello, buddy!', 'method call works');
t.equal(await E(x).hello('Hello'), 'Hello, buddy!', 'method call works');
t.equal(
await E.M(await E.G(await E.G(x).y).fn)(4),
await E(await E.G(await E.G(x).y).fn)(4),
8,
'anonymous method works',
);
t.equal(await E.G(x).val, 123, 'property get');
t.equal(await E.S(x).val(999), 999, 'property set');
t.equal(x.val, 999, 'property set works');
t.equal(await E.D(x).val, true, 'property delete');
t.equal(x.val, undefined, 'delete worked');
await t.rejects(
E.D(await E.G(x).y).val2,
TypeError,
'property delete fails',
);
t.equal(x.y.val2, 456, 'delete failed');
} catch (e) {
t.isNot(e, e, 'unexpected exception');
} finally {
t.end();
}
});

test('E.C chains', async t => {
try {
const x = {
name: 'buddy',
val: 123,
y: Object.freeze({
val2: 456,
name2: 'holly',
fn: n => 2 * n,
}),
hello(greeting) {
return `${greeting}, ${this.name}!`;
},
};
const xC = E.C(x);
t.equal(await xC.M.hello('Hello').P, 'Hello, buddy!', 'method call works');
t.equal(await xC.G.y.G.fn.M(4).P, 8, 'anonymous method works');
t.equal(await xC.G.val.P, 123, 'property get');
t.equal(await xC.S.val(999).P, 999, 'property set');
t.equal(await xC.H.val.P, true, 'property has');
t.equal(await xC.H.notval.P, false, 'missing property has not');
t.equal(x.val, 999, 'property set works');
t.equal(await xC.D.val.P, true, 'property delete');
t.equal(x.val, undefined, 'delete worked');
await t.rejects(xC.G.y.D.val2.P, TypeError, 'property delete fails');
t.equal(x.y.val2, 456, 'delete failed');
} catch (e) {
t.isNot(e, e, 'unexpected exception');
} finally {
Expand Down
Loading

0 comments on commit cf10dc3

Please sign in to comment.