-
Notifications
You must be signed in to change notification settings - Fork 75
/
Copy pathmake-far.js
170 lines (154 loc) · 5.63 KB
/
make-far.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// @ts-check
/// <reference types="ses"/>
import { assertChecker, PASS_STYLE } from './helpers/passStyle-helpers.js';
import {
assertIface,
getInterfaceOf,
RemotableHelper,
} from './helpers/remotable.js';
import { pureCopy } from './pureCopy.js';
/** @typedef {import('./types.js').InterfaceSpec} InterfaceSpec */
const { quote: q, details: X } = assert;
const { prototype: functionPrototype } = Function;
const {
getPrototypeOf,
setPrototypeOf,
create,
isFrozen,
prototype: objectPrototype,
} = Object;
/**
* Now that the remotableProto does not provide its own `toString` method,
* ensure it always inherits from something. The original prototype of
* `remotable` if there was one, or `Object.prototype` otherwise.
*
* @param {Object} remotable
* @param {InterfaceSpec} iface
* @returns {Object}
*/
const makeRemotableProto = (remotable, iface) => {
let oldProto = getPrototypeOf(remotable);
if (typeof remotable === 'object') {
if (oldProto === null) {
oldProto = objectPrototype;
}
assert(
oldProto === objectPrototype || oldProto === null,
X`For now, remotables cannot inherit from anything unusual, in ${remotable}`,
);
} else if (typeof remotable === 'function') {
assert(
oldProto !== null,
X`Original function must not inherit from null: ${remotable}`,
);
assert(
oldProto === functionPrototype ||
getPrototypeOf(oldProto) === functionPrototype,
X`Far functions must originally inherit from Function.prototype, in ${remotable}`,
);
} else {
assert.fail(X`unrecognized typeof ${remotable}`);
}
return harden(
create(oldProto, {
[PASS_STYLE]: { value: 'remotable' },
[Symbol.toStringTag]: { value: iface },
}),
);
};
const assertCanBeRemotable = candidate =>
RemotableHelper.canBeValid(candidate, assertChecker);
/**
* Create and register a Remotable. After this, getInterfaceOf(remotable)
* returns iface.
*
* // https://github.com/Agoric/agoric-sdk/issues/804
*
* @param {InterfaceSpec} [iface='Remotable'] The interface specification for
* the remotable. For now, a string iface must be "Remotable" or begin with
* "Alleged: ", to serve as the alleged name. More general ifaces are not yet
* implemented. This is temporary. We include the
* "Alleged" as a reminder that we do not yet have SwingSet or Comms Vat
* support for ensuring this is according to the vat hosting the object.
* Currently, Alice can tell Bob about Carol, where VatA (on Alice's behalf)
* misrepresents Carol's `iface`. VatB and therefore Bob will then see
* Carol's `iface` as misrepresented by VatA.
* @param {undefined} [props=undefined] Currently may only be undefined.
* That plan is that own-properties are copied to the remotable
* @param {any} [remotable={}] The object used as the remotable
* @returns {any} remotable, modified for debuggability
*/
export const Remotable = (
iface = 'Remotable',
props = undefined,
remotable = {},
) => {
assertIface(iface);
iface = pureCopy(harden(iface));
assert(iface);
// TODO: When iface is richer than just string, we need to get the allegedName
// in a different way.
assert(props === undefined, X`Remotable props not yet implemented ${props}`);
// Fail fast: check that the unmodified object is able to become a Remotable.
assertCanBeRemotable(remotable);
// Ensure that the remotable isn't already marked.
assert(
!(PASS_STYLE in remotable),
X`Remotable ${remotable} is already marked as a ${q(
remotable[PASS_STYLE],
)}`,
);
// Ensure that the remotable isn't already frozen.
assert(!isFrozen(remotable), X`Remotable ${remotable} is already frozen`);
const remotableProto = makeRemotableProto(remotable, iface);
// Take a static copy of the enumerable own properties as data properties.
// const propDescs = getOwnPropertyDescriptors({ ...props });
const mutateHardenAndCheck = target => {
// defineProperties(target, propDescs);
setPrototypeOf(target, remotableProto);
harden(target);
assertCanBeRemotable(target);
};
// Fail fast: check a fresh remotable to see if our rules fit.
mutateHardenAndCheck({});
// Actually finish the new remotable.
mutateHardenAndCheck(remotable);
// COMMITTED!
// We're committed, so keep the interface for future reference.
assert(iface !== undefined); // To make TypeScript happy
return remotable;
};
harden(Remotable);
/**
* A concise convenience for the most common `Remotable` use.
*
* @template T
* @param {string} farName This name will be prepended with `Alleged: `
* for now to form the `Remotable` `iface` argument.
* @param {T|undefined} [remotable={}] The object used as the remotable
* @returns {T} remotable, modified for debuggability
*/
export const Far = (farName, remotable = undefined) => {
const r = remotable === undefined ? {} : remotable;
return Remotable(`Alleged: ${farName}`, undefined, r);
};
harden(Far);
/**
* Coerce `func` to a far function that preserves its call behavior.
* If it is already a far function, return it. Otherwise make and return a
* new far function that wraps `func` and forwards calls to it. This
* works even if `func` is already frozen. `ToFarFunction` is to be used
* when the function comes from elsewhere under less control. For functions
* you author in place, better to use `Far` on their function literal directly.
*
* @param {string} farName to be used only if `func` is not already a
* far function.
* @param {(...args: any[]) => any} func
*/
export const ToFarFunction = (farName, func) => {
if (getInterfaceOf(func) !== undefined) {
return func;
}
return Far(farName, (...args) => func(...args));
};
harden(ToFarFunction);