Skip to content

Commit ebd7e2e

Browse files
committed
feat(E): new shortcuts to avoid full chaining
The shortcuts all generate Promises, and are: E.G(x).vname := get vname E.D(x).vname := delete vname E.S(x).vname(val) := set vname to val E.M(x).method(...args) := invoke method(...args) E.M(x)(...args) := invoke anonymous function(...args) Now E(x) is shorthand for E.M(x)
1 parent 758e298 commit ebd7e2e

File tree

3 files changed

+121
-66
lines changed

3 files changed

+121
-66
lines changed

src/E.js

+91-58
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,21 @@ if (typeof globalThis === 'undefined') {
99

1010
const harden = (globalThis.SES && globalThis.SES.harden) || Object.freeze;
1111

12+
const readOnlyProxy = {
13+
set(_target, _prop, _value) {
14+
return false;
15+
},
16+
isExtensible(_target) {
17+
return false;
18+
},
19+
setPrototypeOf(_target, _value) {
20+
return false;
21+
},
22+
deleteProperty(_target, _prop) {
23+
return false;
24+
},
25+
};
26+
1227
/**
1328
* A Proxy handler for E(x).
1429
*
@@ -17,6 +32,7 @@ const harden = (globalThis.SES && globalThis.SES.harden) || Object.freeze;
1732
*/
1833
function EProxyHandler(x, HandledPromise) {
1934
return harden({
35+
...readOnlyProxy,
2036
get(_target, p, _receiver) {
2137
if (`${p}` !== p) {
2238
return undefined;
@@ -28,14 +44,8 @@ function EProxyHandler(x, HandledPromise) {
2844
// #95 for details.
2945
return (...args) => harden(HandledPromise.applyMethod(x, p, args));
3046
},
31-
deleteProperty(_target, p) {
32-
return harden(HandledPromise.delete(x, p));
33-
},
34-
set(_target, p, value, _receiver) {
35-
return harden(HandledPromise.set(x, p, value));
36-
},
3747
apply(_target, _thisArg, argArray = []) {
38-
return harden(HandledPromise.apply(x, argArray));
48+
return harden(HandledPromise.applyFunction(x, argArray));
3949
},
4050
has(_target, _p) {
4151
// We just pretend everything exists.
@@ -50,74 +60,97 @@ export default function makeE(HandledPromise) {
5060
return harden(new Proxy({}, handler));
5161
}
5262

53-
const EChain = x => {
54-
return harden({
63+
const makeEGetterProxy = (x, wrap = o => o) =>
64+
new Proxy(
65+
Object.create(null),
66+
{
67+
...readOnlyProxy,
68+
has(_target, _prop) {
69+
return true;
70+
},
71+
get(_target, prop) {
72+
return wrap(HandledPromise.get(x, prop));
73+
},
74+
},
75+
);
76+
77+
78+
const makeEDeleterProxy = (x, wrap = o => o) =>
79+
new Proxy(
80+
Object.create(null),
81+
{
82+
...readOnlyProxy,
83+
has(_target, _prop) {
84+
return true;
85+
},
86+
get(_target, prop) {
87+
return wrap(HandledPromise.delete(x, prop));
88+
},
89+
},
90+
);
91+
92+
const makeESetterProxy = (x, wrap = o => o) =>
93+
new Proxy(
94+
Object.create(null),
95+
{
96+
...readOnlyProxy,
97+
has(_target, _prop) {
98+
return true;
99+
},
100+
get(_target, prop) {
101+
return harden(value =>
102+
wrap(HandledPromise.set(x, prop, value)),
103+
);
104+
},
105+
},
106+
);
107+
108+
const makeEMethodProxy = (x, wrap = o => o) =>
109+
new Proxy(
110+
(..._args) => {},
111+
{
112+
...readOnlyProxy,
113+
has(_target, _prop) {
114+
return true;
115+
},
116+
get(_target, prop) {
117+
return harden((...args) =>
118+
wrap(HandledPromise.applyMethod(x, prop, args)),
119+
);
120+
},
121+
apply(_target, _thisArg, args = []) {
122+
return wrap(HandledPromise.applyFunction(x, args));
123+
},
124+
});
125+
126+
E.G = makeEGetterProxy;
127+
E.D = makeEDeleterProxy;
128+
E.S = makeESetterProxy;
129+
E.M = makeEMethodProxy;
130+
131+
const EChain = x =>
132+
harden({
55133
get G() {
56134
// Return getter.
57-
return new Proxy(
58-
{ EChain: 'getter' },
59-
{
60-
has(_target, _prop) {
61-
return true;
62-
},
63-
get(_target, prop) {
64-
return EChain(HandledPromise.get(x, prop));
65-
},
66-
},
67-
);
135+
return makeEGetterProxy(x, EChain);
68136
},
69137
get D() {
70138
// Return deleter.
71-
return new Proxy(
72-
{ EChain: 'deleter' },
73-
{
74-
has(_target, _prop) {
75-
return true;
76-
},
77-
get(_target, prop) {
78-
return EChain(HandledPromise.delete(x, prop));
79-
},
80-
},
81-
);
139+
return makeEDeleterProxy(x, EChain);
82140
},
83141
get S() {
84142
// Return setter.
85-
return new Proxy(
86-
{ EChain: 'setter' },
87-
{
88-
has(_target, _prop) {
89-
return true;
90-
},
91-
get(_target, prop) {
92-
return harden(value =>
93-
EChain(HandledPromise.set(x, prop, value)),
94-
);
95-
},
96-
},
97-
);
143+
return makeESetterProxy(x, EChain);
98144
},
99145
get M() {
100146
// Return method-caller.
101-
return new Proxy((..._args) => {}, {
102-
has(_target, _prop) {
103-
return true;
104-
},
105-
get(_target, prop) {
106-
return harden((...args) =>
107-
EChain(HandledPromise.applyMethod(x, prop, args)),
108-
);
109-
},
110-
apply(_target, _thisArg, args = []) {
111-
return EChain(HandledPromise.applyFunction(x, args));
112-
},
113-
});
147+
return makeEMethodProxy(x, EChain);
114148
},
115149
get P() {
116150
// Return as promise.
117151
return Promise.resolve(x);
118152
},
119153
});
120-
};
121154

122155
E.C = EChain;
123156
return harden(E);

src/index.js

-8
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,6 @@ export function makeHandledPromise(EPromise) {
5252
deleteSendOnly(target, key) {
5353
EPromise.resolve(target).delete(key);
5454
},
55-
// TODO: Remove when making HandledPromise a constructor
56-
// to avoid conflict with Function.prototype.apply
57-
apply(target, args) {
58-
return EPromise.resolve(target).post(undefined, args);
59-
},
60-
applySendOnly(target, args) {
61-
EPromise.resolve(target).post(undefined, args);
62-
},
6355
applyFunction(target, args) {
6456
return EPromise.resolve(target).post(undefined, args);
6557
},

test/test-e.js

+30
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,36 @@ test('E method calls', async t => {
1818
}
1919
});
2020

21+
test('E.* shortcuts', async t => {
22+
try {
23+
const x = {
24+
name: 'buddy',
25+
val: 123,
26+
y: Object.freeze({
27+
val2: 456,
28+
name2: 'holly',
29+
fn: n => 2 * n,
30+
}),
31+
hello(greeting) {
32+
return `${greeting}, ${this.name}!`;
33+
},
34+
};
35+
t.equal(await E.M(x).hello('Hello'), 'Hello, buddy!', 'method call works');
36+
t.equal(await E.M(await E.G(await E.G(x).y).fn)(4), 8, 'anonymous method works');
37+
t.equal(await E.G(x).val, 123, 'property get');
38+
t.equal(await E.S(x).val(999), 999, 'property set');
39+
t.equal(x.val, 999, 'property set works');
40+
t.equal(await E.D(x).val, true, 'property delete');
41+
t.equal(x.val, undefined, 'delete worked');
42+
await t.rejects(E.D(await E.G(x).y).val2, TypeError, 'property delete fails');
43+
t.equal(x.y.val2, 456, 'delete failed');
44+
} catch (e) {
45+
t.isNot(e, e, 'unexpected exception');
46+
} finally {
47+
t.end();
48+
}
49+
});
50+
2151
test('E.C chains', async t => {
2252
try {
2353
const x = {

0 commit comments

Comments
 (0)