Skip to content

Commit 526e404

Browse files
committed
refactor: prepare for non-trapping integrity level
1 parent b23724f commit 526e404

10 files changed

+95
-60
lines changed

packages/captp/src/trap.js

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Lifted mostly from `@endo/eventual-send/src/E.js`.
22

3+
const { freeze } = Object;
4+
35
/**
46
* Default implementation of Trap for near objects.
57
*
@@ -62,11 +64,21 @@ const TrapProxyHandler = (x, trapImpl) => {
6264
*/
6365
export const makeTrap = trapImpl => {
6466
const Trap = x => {
67+
/**
68+
* `freeze` but not `harden` the proxy target so it remains trapping.
69+
* @see https://github.com/tc39/proposal-stabilize
70+
*/
71+
const target = freeze(() => {});
6572
const handler = TrapProxyHandler(x, trapImpl);
66-
return harden(new Proxy(() => {}, handler));
73+
return new Proxy(target, handler);
6774
};
6875

6976
const makeTrapGetterProxy = x => {
77+
/**
78+
* `freeze` but not `harden` the proxy target so it remains trapping.
79+
* @see https://github.com/tc39/proposal-stabilize
80+
*/
81+
const target = freeze(Object.create(null));
7082
const handler = harden({
7183
...baseFreezableProxyHandler,
7284
has(_target, _prop) {
@@ -77,7 +89,7 @@ export const makeTrap = trapImpl => {
7789
return trapImpl.get(x, prop);
7890
},
7991
});
80-
return new Proxy(Object.create(null), handler);
92+
return new Proxy(target, handler);
8193
};
8294
Trap.get = makeTrapGetterProxy;
8395

packages/eventual-send/src/E.js

+15-10
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { trackTurns } from './track-turns.js';
22
import { makeMessageBreakpointTester } from './message-breakpoints.js';
33

44
const { details: X, quote: q, Fail, error: makeError } = assert;
5-
const { assign, create } = Object;
5+
const { assign, create, freeze } = Object;
66

77
/**
88
* @import { HandledPromiseConstructor } from './types.js';
@@ -171,6 +171,16 @@ const makeEGetProxyHandler = (x, HandledPromise) =>
171171
* @param {HandledPromiseConstructor} HandledPromise
172172
*/
173173
const makeE = HandledPromise => {
174+
/**
175+
* `freeze` but not `harden` the proxy target so it remains trapping.
176+
* @see https://github.com/tc39/proposal-stabilize
177+
*/
178+
const funcTarget = freeze(() => {});
179+
/**
180+
* `freeze` but not `harden` the proxy target so it remains trapping.
181+
* @see https://github.com/tc39/proposal-stabilize
182+
*/
183+
const objTarget = freeze(create(null));
174184
return harden(
175185
assign(
176186
/**
@@ -183,7 +193,7 @@ const makeE = HandledPromise => {
183193
* @returns {ECallableOrMethods<RemoteFunctions<T>>} method/function call proxy
184194
*/
185195
// @ts-expect-error XXX typedef
186-
x => harden(new Proxy(() => {}, makeEProxyHandler(x, HandledPromise))),
196+
x => new Proxy(funcTarget, makeEProxyHandler(x, HandledPromise)),
187197
{
188198
/**
189199
* E.get(x) returns a proxy on which you can get arbitrary properties.
@@ -196,11 +206,8 @@ const makeE = HandledPromise => {
196206
* @returns {EGetters<LocalRecord<T>>} property get proxy
197207
* @readonly
198208
*/
199-
get: x =>
200-
// @ts-expect-error XXX typedef
201-
harden(
202-
new Proxy(create(null), makeEGetProxyHandler(x, HandledPromise)),
203-
),
209+
// @ts-expect-error XXX typedef
210+
get: x => new Proxy(objTarget, makeEGetProxyHandler(x, HandledPromise)),
204211

205212
/**
206213
* E.resolve(x) converts x to a handled promise. It is
@@ -224,9 +231,7 @@ const makeE = HandledPromise => {
224231
*/
225232
sendOnly: x =>
226233
// @ts-expect-error XXX typedef
227-
harden(
228-
new Proxy(() => {}, makeESendOnlyProxyHandler(x, HandledPromise)),
229-
),
234+
new Proxy(funcTarget, makeESendOnlyProxyHandler(x, HandledPromise)),
230235

231236
/**
232237
* E.when(x, res, rej) is equivalent to

packages/eventual-send/src/handled-promise.js

+3
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,9 @@ export const makeHandledPromise = () => {
309309
if (proxyOpts) {
310310
const {
311311
handler: proxyHandler,
312+
// The proxy target can be frozen but should not be hardened
313+
// so it remains trapping.
314+
// See https://github.com/tc39/proposal-stabilize
312315
target: proxyTarget,
313316
revokerCallback,
314317
} = proxyOpts;

packages/far/test/marshal-far-function.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ test('Data can contain far functions', t => {
5858
const arrow = Far('arrow', a => a + 1);
5959
t.is(passStyleOf(harden({ x: 8, foo: arrow })), 'copyRecord');
6060
const mightBeMethod = a => a + 1;
61-
t.throws(() => passStyleOf(freeze({ x: 8, foo: mightBeMethod })), {
61+
t.throws(() => passStyleOf(harden({ x: 8, foo: mightBeMethod })), {
6262
message: /Remotables with non-methods like "x" /,
6363
});
6464
});

packages/marshal/src/marshal-stringify.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { makeMarshal } from './marshal.js';
55

66
/** @import {Passable} from '@endo/pass-style' */
77

8+
const { freeze } = Object;
9+
810
/** @type {import('./types.js').ConvertValToSlot<any>} */
911
const doNotConvertValToSlot = val =>
1012
Fail`Marshal's stringify rejects presences and promises ${val}`;
@@ -23,7 +25,12 @@ const badArrayHandler = harden({
2325
},
2426
});
2527

26-
const badArray = harden(new Proxy(harden([]), badArrayHandler));
28+
/**
29+
* `freeze` but not `harden` the proxy target so it remains trapping.
30+
* @see https://github.com/tc39/proposal-stabilize
31+
*/
32+
const arrayTarget = freeze(/** @type {any[]} */ ([]));
33+
const badArray = new Proxy(arrayTarget, badArrayHandler);
2734

2835
const { serialize, unserialize } = makeMarshal(
2936
doNotConvertValToSlot,
@@ -48,7 +55,10 @@ harden(stringify);
4855
*/
4956
const parse = str =>
5057
unserialize(
51-
harden({
58+
// `freeze` but not `harden` since the `badArray` proxy and its target
59+
// must remain trapping.
60+
// See https://github.com/tc39/proposal-stabilize
61+
freeze({
5262
body: str,
5363
slots: badArray,
5464
}),

packages/marshal/test/marshal-far-function.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ test('Data can contain far functions', t => {
6060
const arrow = Far('arrow', a => a + 1);
6161
t.is(passStyleOf(harden({ x: 8, foo: arrow })), 'copyRecord');
6262
const mightBeMethod = a => a + 1;
63-
t.throws(() => passStyleOf(freeze({ x: 8, foo: mightBeMethod })), {
63+
t.throws(() => passStyleOf(harden({ x: 8, foo: mightBeMethod })), {
6464
message: /Remotables with non-methods like "x" /,
6565
});
6666
});

packages/pass-style/test/passStyleOf.test.js

+32-33
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const harden = /** @type {import('ses').Harden & { isFake?: boolean }} */ (
1313
global.harden
1414
);
1515

16-
const { getPrototypeOf, defineProperty } = Object;
16+
const { getPrototypeOf, defineProperty, freeze } = Object;
1717
const { ownKeys } = Reflect;
1818

1919
test('passStyleOf basic success cases', t => {
@@ -193,16 +193,22 @@ test('passStyleOf testing remotables', t => {
193193

194194
const tagRecord1 = harden(makeTagishRecord('Alleged: manually constructed'));
195195
/** @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385 */
196-
const farObj1 = harden({
197-
__proto__: tagRecord1,
198-
});
196+
const farObj1 = harden({ __proto__: tagRecord1 });
199197
t.is(passStyleOf(farObj1), 'remotable');
200198

201199
const tagRecord2 = makeTagishRecord('Alleged: tagRecord not hardened');
202-
/** @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385 */
203-
const farObj2 = Object.freeze({
204-
__proto__: tagRecord2,
205-
});
200+
/**
201+
* TODO In order to run this test before we have explicit support for a
202+
* non-trapping integrity level, we have to `freeze` here but not `harden`.
203+
* However, once we do have that support, and `passStyleOf` checks that
204+
* its argument is also non-trapping, we still need to avoid `harden`
205+
* because that would also hardden `__proto__`. So we will need to
206+
* explicitly make this non-trapping, which we cannot yet express.
207+
* @see https://github.com/tc39/proposal-stabilize
208+
*
209+
* @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385
210+
*/
211+
const farObj2 = freeze({ __proto__: tagRecord2 });
206212
if (harden.isFake) {
207213
t.is(passStyleOf(farObj2), 'remotable');
208214
} else {
@@ -212,39 +218,27 @@ test('passStyleOf testing remotables', t => {
212218
});
213219
}
214220

215-
const tagRecord3 = Object.freeze(
216-
makeTagishRecord('Alleged: both manually frozen'),
217-
);
221+
const tagRecord3 = harden(makeTagishRecord('Alleged: both manually frozen'));
218222
/** @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385 */
219-
const farObj3 = Object.freeze({
220-
__proto__: tagRecord3,
221-
});
223+
const farObj3 = harden({ __proto__: tagRecord3 });
222224
t.is(passStyleOf(farObj3), 'remotable');
223225

224226
const tagRecord4 = harden(makeTagishRecord('Remotable'));
225227
/** @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385 */
226-
const farObj4 = harden({
227-
__proto__: tagRecord4,
228-
});
228+
const farObj4 = harden({ __proto__: tagRecord4 });
229229
t.is(passStyleOf(farObj4), 'remotable');
230230

231231
const tagRecord5 = harden(makeTagishRecord('Not alleging'));
232-
const farObj5 = harden({
233-
__proto__: tagRecord5,
234-
});
232+
const farObj5 = harden({ __proto__: tagRecord5 });
235233
t.throws(() => passStyleOf(farObj5), {
236234
message:
237235
/For now, iface "Not alleging" must be "Remotable" or begin with "Alleged: " or "DebugName: "; unimplemented/,
238236
});
239237

240238
const tagRecord6 = harden(makeTagishRecord('Alleged: manually constructed'));
241-
const farObjProto6 = harden({
242-
__proto__: tagRecord6,
243-
});
239+
const farObjProto6 = harden({ __proto__: tagRecord6 });
244240
/** @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385 */
245-
const farObj6 = harden({
246-
__proto__: farObjProto6,
247-
});
241+
const farObj6 = harden({ __proto__: farObjProto6 });
248242
t.is(passStyleOf(farObj6), 'remotable', 'tagRecord grandproto is accepted');
249243

250244
// Our current agoric-sdk plans for far classes are to create a class-like
@@ -323,12 +317,8 @@ test('passStyleOf testing remotables', t => {
323317
const fauxTagRecordB = harden(
324318
makeTagishRecord('Alleged: manually constructed', harden({})),
325319
);
326-
const farObjProtoB = harden({
327-
__proto__: fauxTagRecordB,
328-
});
329-
const farObjB = harden({
330-
__proto__: farObjProtoB,
331-
});
320+
const farObjProtoB = harden({ __proto__: fauxTagRecordB });
321+
const farObjB = harden({ __proto__: farObjProtoB });
332322
t.throws(() => passStyleOf(farObjB), {
333323
message:
334324
'cannot serialize Remotables with non-methods like "Symbol(passStyle)" in "[Alleged: manually constructed]"',
@@ -387,7 +377,16 @@ test('remotables - safety from the gibson042 attack', t => {
387377
},
388378
);
389379

390-
const makeInput = () => Object.freeze({ __proto__: mercurialProto });
380+
/**
381+
* TODO In order to run this test before we have explicit support for a
382+
* non-trapping integrity level, we have to `freeze` here but not `harden`.
383+
* However, once we do have that support, and `passStyleOf` checks that
384+
* its argument is also non-trapping, we still need to avoid `harden`
385+
* because that would also hardden `__proto__`. So we will need to
386+
* explicitly make this non-trapping, which we cannot yet express.
387+
* @see https://github.com/tc39/proposal-stabilize
388+
*/
389+
const makeInput = () => freeze({ __proto__: mercurialProto });
391390
const input1 = makeInput();
392391
const input2 = makeInput();
393392

packages/ses/src/commons.js

-6
Original file line numberDiff line numberDiff line change
@@ -269,12 +269,6 @@ export const finalizationRegistryUnregister =
269269
export const getConstructorOf = fn =>
270270
reflectGet(getPrototypeOf(fn), 'constructor');
271271

272-
/**
273-
* immutableObject
274-
* An immutable (frozen) empty object that is safe to share.
275-
*/
276-
export const immutableObject = freeze(create(null));
277-
278272
/**
279273
* isObject tests whether a value is an object.
280274
* Today, this is equivalent to:

packages/ses/src/sloppy-globals-scope-terminator.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@ import {
33
create,
44
freeze,
55
getOwnPropertyDescriptors,
6-
immutableObject,
76
reflectSet,
87
} from './commons.js';
98
import {
109
strictScopeTerminatorHandler,
1110
alwaysThrowHandler,
1211
} from './strict-scope-terminator.js';
1312

13+
/**
14+
* `freeze` but not `harden` the proxy target so it remains trapping.
15+
* Thus, it should not be shared outside this module.
16+
* @see https://github.com/tc39/proposal-stabilize
17+
*/
18+
const onlyFrozenObject = freeze(create(null));
19+
1420
/*
1521
* createSloppyGlobalsScopeTerminator()
1622
* strictScopeTerminatorHandler manages a scopeTerminator Proxy which serves as
@@ -45,7 +51,7 @@ export const createSloppyGlobalsScopeTerminator = globalObject => {
4551
);
4652

4753
const sloppyGlobalsScopeTerminator = new Proxy(
48-
immutableObject,
54+
onlyFrozenObject,
4955
sloppyGlobalsScopeTerminatorHandler,
5056
);
5157

packages/ses/src/strict-scope-terminator.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@ import {
77
freeze,
88
getOwnPropertyDescriptors,
99
globalThis,
10-
immutableObject,
1110
} from './commons.js';
1211
import { assert } from './error/assert.js';
1312

1413
const { Fail, quote: q } = assert;
1514

15+
/**
16+
* `freeze` but not `harden` the proxy target so it remains trapping.
17+
* Thus, it should not be shared outside this module.
18+
* @see https://github.com/tc39/proposal-stabilize
19+
*/
20+
const onlyFrozenObject = freeze(create(null));
21+
1622
/**
1723
* alwaysThrowHandler
1824
* This is an object that throws if any property is called. It's used as
@@ -21,7 +27,7 @@ const { Fail, quote: q } = assert;
2127
* create one and share it between all Proxy handlers.
2228
*/
2329
export const alwaysThrowHandler = new Proxy(
24-
immutableObject,
30+
onlyFrozenObject,
2531
freeze({
2632
get(_shadow, prop) {
2733
Fail`Please report unexpected scope handler trap: ${q(String(prop))}`;
@@ -88,6 +94,6 @@ export const strictScopeTerminatorHandler = freeze(
8894
);
8995

9096
export const strictScopeTerminator = new Proxy(
91-
immutableObject,
97+
onlyFrozenObject,
9298
strictScopeTerminatorHandler,
9399
);

0 commit comments

Comments
 (0)