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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// The lifecycle of a `foreign … gives view` — spec §14E.1, §14E.3.
//
// **Its own module, and that is a size decision rather than a tidiness
// one.** A DOM-owning foreign is the one construct here that a program
// can go its whole life without writing, and §16.3.1 promises a bundle
// ships nothing it does not use. Left in `dom.js` these bytes were
// downloaded by every page ever served, including one with no FFI in it,
// which is a fixed cost paid for an optional feature. `Bundle::runtime`
// already computes a transitive import closure — `rpc.js`, `store.js` and
// `wire.js` are linked only when the split finds a crossing or a durable
// key — so this is that existing mechanism applied once more, and not a
// new exemption from the size gate. `zdc-bench` charges this file to the
// programs that link it and to no others, and a test pins both halves of
// that: a null program must not reach it, and a program with a `gives
// view` foreign must.
//
// Nothing here touches the DOM. The node is handed in — the template
// already carries it — so this module needs the reactivity core and
// nothing else, which is why it does not import `dom.js` and why linking
// it costs a program `signal.js` it already had.
//
// **The contract is checked here because here is the only place that can
// see it (#239).** `mount(node, props) -> { update(props), destroy() }` is
// a shape no type in the language describes, so `from "three" as "Scene"`
// compiles — a `foreign` declaration gives the compiler nothing to check
// it against — and used to fail on the first render with an engine
// `TypeError` raised inside this file, naming a local the reader never
// wrote and no part of the declaration that caused it.
//
// It is not cheap: the check nearly trebles this file, almost all of it
// the refusals' own prose, and the module is downloaded whole by every
// program that writes one of these. BENCHMARKS.md charges it there and
// records what it did to the margin, because a size argument that quietly
// stops applying to the file it was made about is worse than the bytes.
// What it buys is the sentence that turns a trace through a runtime into
// the name of a declaration to open.
import from './signal.js';
/** The contract, spelled once and quoted verbatim in every refusal. */
const CONTRACT = 'mount(node, props) -> { update(props), destroy() }';
/** The claim both refusals of the imported binding open with. */
const NOT_A_MOUNT = 'gives a view, so what its `as` clause names must be a mount function; ';
/**
* Hand an element to a `foreign … gives view` (§14E.1, §14E.3).
*
* `node` is a `<div>` the template already carries, so a foreign is a
* static-markup hole bound like an attribute rather than an anchor pair
* like `each`, keeping it inside §16.2 R2's cloning model. `props` is a
* thunk giving a plain object, one property per `takes` argument in
* order, read inside an effect. `declared` is the declaration's own name
* in the program, carried here for no reason but the refusals: nothing
* else in this file reads it, and without it a breach of the contract can
* only be reported against a runtime the reader did not write.
*
* Reactivity is `update`, never re-invocation: re-running `create` would
* rebuild whatever the module owns — a WebGL context, an animation — on
* every write, the failure this form prevents. Nothing crosses back, and
* the handle's *types* are still asserted rather than verified (§14E.4) —
* what is checked here is that there is a handle with the two methods,
* which is the part that has an answer at mount.
*/
export
/**
* Call `create` and return its handle, or refuse in the declaration's name.
*
* Checked at mount and nowhere afterwards. A handle that answered once
* cannot stop answering — the module would have to replace its own return
* value, which it no longer holds — so re-checking on every write would
* charge every signal write for a mistake that can only be made once.
*/
/**
* Throw the one shape of refusal this file has: claim, contract, repair.
*
* One function rather than three literals so that the declaration is named
* the same way every time and the spec reference cannot drift between
* them — a reader who has seen one of these has seen all three.
*
* `repair` carries no closing full stop: this adds one after the spec
* reference, which is the sentence's real end.
*/
/** Whether a handle is one: an object carrying both halves of the contract. */
/** What was imported, as a noun phrase: `a number`, `an object`, `null`. */
/** What mounting produced, as the tail of "this …". */
/**
* Whether `fn` is a class rather than an ordinary function.
*
* ECMAScript's own distinction rather than a guess at source text: a class
* constructor's `prototype` is non-writable, an ordinary function's is
* writable, and a method or arrow has none at all. Reading the descriptor
* separates the three without `Function.prototype.toString`, which a
* minifier, a bound function and a native class can each make lie.
*
* A class transpiled down to a plain function — what a bundler targeting
* ES5 emits — is not detectable here and does not need to be: it is
* callable, so it is called, and it is the handle check that refuses it.
*/