@@ -10,6 +10,8 @@ stage: 0
1010contributors: Arthur Fiorette, Arlen Beiler
1111</pre>
1212
13+ <emu-biblio href="node_modules/@tc39/ecma262-biblio/biblio.json"></emu-biblio>
14+
1315<emu-intro id="sec-intro">
1416 <h1>Introduction</h1>
1517 <emu-note>
@@ -18,276 +20,117 @@ contributors: Arthur Fiorette, Arlen Beiler
1820 <p>This proposal introduces a `try` operator and `Result` class to JavaScript for improved error handling ergonomics. The `try` operator evaluates an expression within an implicit try-catch block and returns a `Result` instance containing either the successful value or the caught error.</p>
1921</emu-intro>
2022
21- <emu-clause id="sec-result-objects">
22- <h1>Result Objects</h1>
23-
24- <emu-clause id="sec-result-constructor">
25- <h1>The Result Constructor</h1>
26- <p>The Result constructor is the %Result% intrinsic object. When called as a constructor, it creates and initializes a new Result object.</p>
27-
28- <emu-clause id="sec-result">
29- <h1>Result ( _ok_, _error_, _value_ )</h1>
30- <p>When `Result` is called with arguments _ok_, _error_, and _value_, the following steps are taken:</p>
31- <emu-alg>
32- 1. If NewTarget is *undefined*, throw a *TypeError* exception.
33- 1. Let _result_ be ? OrdinaryCreateFromConstructor(NewTarget, *"%Result.prototype%"*, « [[ResultState]], [[ResultValue]], [[ResultError]] »).
34- 1. Let _booleanOk_ be ToBoolean(_ok_).
35- 1. Perform ! CreateDataPropertyOrThrow(_result_, *"ok"*, _booleanOk_).
36- 1. If _booleanOk_ is *true*, then
37- 1. Set _result_.[[ResultState]] to ~success~.
38- 1. Set _result_.[[ResultValue]] to _value_.
39- 1. Perform ! CreateDataPropertyOrThrow(_result_, *"value"*, _value_).
40- 1. Else,
41- 1. Set _result_.[[ResultState]] to ~failure~.
42- 1. Set _result_.[[ResultError]] to _error_.
43- 1. Perform ! CreateDataPropertyOrThrow(_result_, *"error"*, _error_).
44- 1. Return _result_.
45- </emu-alg>
46- </emu-clause>
47- </emu-clause>
48-
49- <emu-clause id="sec-properties-of-the-result-constructor">
50- <h1>Properties of the Result Constructor</h1>
51-
52- <emu-clause id="sec-result.ok">
53- <h1>Result.ok ( _value_ )</h1>
54- <p>When the `Result.ok` method is called with argument _value_, the following steps are taken:</p>
55- <emu-alg>
56- 1. Let _result_ be ? Construct(%Result%, « *true*, *undefined*, _value_ »).
57- 1. Return _result_.
58- </emu-alg>
59- </emu-clause>
60-
61- <emu-clause id="sec-result.error">
62- <h1>Result.error ( _error_ )</h1>
63- <p>When the `Result.error` method is called with argument _error_, the following steps are taken:</p>
64- <emu-alg>
65- 1. Let _result_ be ? Construct(%Result%, « *false*, _error_, *undefined* »).
66- 1. Return _result_.
67- </emu-alg>
68- </emu-clause>
69-
70- <emu-clause id="sec-result.try">
71- <h1>Result.try ( _arg_, ..._args_ )</h1>
72- <p>When the `Result.try` method is called with argument _arg_ and optional arguments _args_, the following steps are taken:</p>
73- <emu-alg>
74- 1. Let _completion_ be Completion(ResultTryImplementation(_arg_, _args_)).
75- 1. If _completion_ is an abrupt completion, then
76- 1. Return ! Call(%Result.error%, %Result%, « _completion_.[[Value]] »).
77- 1. Return _completion_.[[Value]].
78- </emu-alg>
79-
80- <emu-clause id="sec-result-try-implementation" type="abstract operation">
81- <h1>ResultTryImplementation ( _arg_, _args_ )</h1>
82- <dl class="header">
83- </dl>
84- <emu-alg>
85- 1. Let _result_ be *undefined*.
86- 1. If IsCallable(_arg_) is *true*, then
87- 1. Let _callResult_ be Completion(Call(_arg_, *undefined*, _args_)).
88- 1. If _callResult_ is an abrupt completion, then
89- 1. Return ! Call(%Result.error%, %Result%, « _callResult_.[[Value]] »).
90- 1. Set _result_ to _callResult_.[[Value]].
91- 1. Else,
92- 1. Set _result_ to _arg_.
93- 1. If _result_ is an Object and IsPromise(_result_) is *true*, then
94- 1. Let _fulfillmentHandler_ be a new Abstract Closure with parameters (_value_) that captures nothing and performs the following steps when called:
95- 1. Return ! Call(%Result.ok%, %Result%, « _value_ »).
96- 1. Let _rejectionHandler_ be a new Abstract Closure with parameters (_reason_) that captures nothing and performs the following steps when called:
97- 1. Return ! Call(%Result.error%, %Result%, « _reason_ »).
98- 1. Let _onFulfilled_ be CreateBuiltinFunction(_fulfillmentHandler_, 1, *""*, « »).
99- 1. Let _onRejected_ be CreateBuiltinFunction(_rejectionHandler_, 1, *""*, « »).
100- 1. Return PerformPromiseThen(_result_, _onFulfilled_, _onRejected_).
101- 1. Return ! Call(%Result.ok%, %Result%, « _result_ »).
102- </emu-alg>
103- </emu-clause>
104- </emu-clause>
105-
106- <emu-clause id="sec-result.prototype">
107- <h1>Result.prototype</h1>
108- <p>The initial value of `Result.prototype` is the Result prototype object.</p>
109- <p>This property has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *false* }.</p>
110- </emu-clause>
111- </emu-clause>
112-
113- <emu-clause id="sec-properties-of-the-result-prototype-object">
114- <h1>Properties of the Result Prototype Object</h1>
115- <p>The Result prototype object:</p>
116- <ul>
117- <li>is an ordinary object.</li>
118- <li>is not a Result instance and does not have [[ResultState]], [[ResultValue]], or [[ResultError]] internal slots.</li>
119- <li>has a [[Prototype]] internal slot whose value is %Object.prototype%.</li>
120- </ul>
121-
122- <emu-clause id="sec-result.prototype-@@iterator">
123- <h1>Result.prototype [ %Symbol.iterator% ] ( )</h1>
124- <p>When the %Symbol.iterator% method is called, the following steps are taken:</p>
125- <emu-alg>
126- 1. Let _result_ be the *this* value.
127- 1. Return ? CreateResultIterator(_result_).
128- </emu-alg>
129- </emu-clause>
130-
131- <emu-clause id="sec-result.prototype.constructor">
132- <h1>Result.prototype.constructor</h1>
133- <p>The initial value of `Result.prototype.constructor` is %Result%.</p>
134- </emu-clause>
135-
136- <emu-clause id="sec-result.prototype-@@toStringTag">
137- <h1>Result.prototype [ %Symbol.toStringTag% ]</h1>
138- <p>The initial value of the %Symbol.toStringTag% property is the String value *"Result"*.</p>
139- <p>This property has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *true* }.</p>
140- </emu-clause>
141- </emu-clause>
142-
143- <emu-clause id="sec-result-iterator-objects">
144- <h1>Result Iterator Objects</h1>
145- <p>A Result Iterator is an object that represents a specific iteration over a Result instance. There is not a named constructor for Result Iterator objects.</p>
146-
147- <emu-clause id="sec-createresultiterator" type="abstract operation">
148- <h1>
149- CreateResultIterator (
150- _result_: a Result object,
151- ): either a normal completion containing a Result Iterator or a throw completion
152- </h1>
153- <dl class="header">
154- </dl>
155- <emu-alg>
156- 1. Perform ? RequireInternalSlot(_result_, [[ResultState]]).
157- 1. Let _iterator_ be OrdinaryObjectCreate(%ResultIteratorPrototype%, « [[IteratedResult]], [[ResultIteratorNextIndex]] »).
158- 1. Set _iterator_.[[IteratedResult]] to _result_.
159- 1. Set _iterator_.[[ResultIteratorNextIndex]] to 0.
160- 1. Return _iterator_.
161- </emu-alg>
162- </emu-clause>
163-
164- <emu-clause id="sec-%resultiteratorprototype%-object">
165- <h1>The %ResultIteratorPrototype% Object</h1>
166- <p>The %ResultIteratorPrototype% object:</p>
167- <ul>
168- <li>has a [[Prototype]] internal slot whose value is %IteratorPrototype%.</li>
169- <li>is an ordinary object.</li>
170- </ul>
23+ <emu-clause id="sec-assignment-operators">
24+ <h1>Assignment Operators</h1>
25+ <h2>Syntax</h2>
26+ <emu-grammar type="definition">
27+ AssignmentExpression[In, Yield, Await] :
28+ TryExpression[?In, ?Yield, ?Await]
29+ TryExpression[In, Yield, Await] :
30+ `try` [no LineTerminator here] [lookahead != `{`] AssignmentExpression[?In, ?Yield, ?Await]
31+ </emu-grammar>
17132
172- <emu-clause id="sec-%resultiteratorprototype%.next">
173- <h1>%ResultIteratorPrototype%.next ( )</h1>
174- <emu-alg>
175- 1. Let _iterator_ be the *this* value.
176- 1. Perform ? RequireInternalSlot(_iterator_, [[IteratedResult]]).
177- 1. Let _result_ be _iterator_.[[IteratedResult]].
178- 1. Let _index_ be _iterator_.[[ResultIteratorNextIndex]].
179- 1. If _index_ is 0, then
180- 1. Set _iterator_.[[ResultIteratorNextIndex]] to 1.
181- 1. Let _okValue_ be ? Get(_result_, *"ok"*).
182- 1. Return CreateIteratorResultObject(_okValue_, *false*).
183- 1. Else if _index_ is 1, then
184- 1. Set _iterator_.[[ResultIteratorNextIndex]] to 2.
185- 1. If ? HasProperty(_result_, *"error"*) is *true*, then
186- 1. Let _errorValue_ be ? Get(_result_, *"error"*).
187- 1. Return CreateIteratorResultObject(_errorValue_, *false*).
188- 1. Else,
189- 1. Return CreateIteratorResultObject(*undefined*, *false*).
190- 1. Else if _index_ is 2, then
191- 1. Set _iterator_.[[ResultIteratorNextIndex]] to 3.
192- 1. If ? HasProperty(_result_, *"value"*) is *true*, then
193- 1. Let _valueValue_ be ? Get(_result_, *"value"*).
194- 1. Return CreateIteratorResultObject(_valueValue_, *false*).
195- 1. Else,
196- 1. Return CreateIteratorResultObject(*undefined*, *false*).
197- 1. Else,
198- 1. Return CreateIteratorResultObject(*undefined*, *true*).
199- </emu-alg>
200- </emu-clause>
201-
202- <emu-clause id="sec-%resultiteratorprototype%-@@toStringTag">
203- <h1>%ResultIteratorPrototype% [ %Symbol.toStringTag% ]</h1>
204- <p>The initial value of the %Symbol.toStringTag% property is the String value *"Result Iterator"*.</p>
205- <p>This property has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *true* }.</p>
206- </emu-clause>
207- </emu-clause>
208- </emu-clause>
209- </emu-clause>
210-
211- <emu-clause id="sec-try-operator">
212- <h1>The Try Operator</h1>
213-
214- <emu-clause id="sec-try-operator-runtime-semantics">
215- <h1>Runtime Semantics</h1>
216-
217- <emu-clause id="sec-try-operator-static-semantics">
218- <h1>Static Semantics</h1>
219- <emu-grammar>
220- UnaryExpression[Yield, Await] :
221- `try` UnaryExpression[?Yield, ?Await]
222- </emu-grammar>
223- </emu-clause>
224-
225- <emu-clause id="sec-try-operator-evaluation" type="sdo">
226- <h1>Runtime Semantics: Evaluation</h1>
227- <emu-grammar>UnaryExpression : `try` UnaryExpression</emu-grammar>
228- <emu-alg>
229- 1. Let _exprRef_ be Completion(Evaluation of |UnaryExpression|).
230- 1. If _exprRef_ is an abrupt completion, then
231- 1. Return ! Call(%Result.error%, %Result%, « _exprRef_.[[Value]] »).
232- 1. Let _value_ be Completion(GetValue(_exprRef_)).
233- 1. If _value_ is an abrupt completion, then
234- 1. Return ! Call(%Result.error%, %Result%, « _value_.[[Value]] »).
235- 1. Return ! Call(%Result.ok%, %Result%, « _value_.[[Value]] »).
236- </emu-alg>
237- </emu-clause>
238- </emu-clause>
239-
240- <emu-clause id="sec-try-operator-await-interaction">
241- <h1>Interaction with Await</h1>
242- <p>When the try operator is used with await, it captures both synchronous exceptions and asynchronous rejections.</p>
33+ <emu-note>
34+ <p>Placing the `try` operator in assignment expression allows it to protect an entire assignment expression.</p>
35+ </emu-note>
24336
244- <emu-grammar>UnaryExpression : `try` `await` UnaryExpression</emu-grammar>
37+ <emu-clause id="sec-comma-operator-runtime-semantics-evaluation" type="sdo">
38+ <h1>Runtime Semantics: Evaluation</h1>
39+ <emu-grammar>TryExpression : `try` AssignmentExpression</emu-grammar>
24540 <emu-alg>
246- 1. Let _exprRef_ be the result of evaluating |UnaryExpression|.
247- 1. Let _value_ be ? GetValue(_exprRef_).
248- 1. Let _promise_ be ? PromiseResolve(%Promise%, _value_).
249- 1. Let _fulfillmentHandler_ be a new Abstract Closure with parameters (_value_) that captures nothing and performs the following steps when called:
250- 1. Return ! Call(%Result.ok%, %Result%, « _value_ »).
251- 1. Let _rejectionHandler_ be a new Abstract Closure with parameters (_reason_) that captures nothing and performs the following steps when called:
252- 1. Return ! Call(%Result.error%, %Result%, « _reason_ »).
253- 1. Let _promiseFulfill_ be CreateBuiltinFunction(_fulfillmentHandler_, 1, *""*, « »).
254- 1. Let _promiseReject_ be CreateBuiltinFunction(_rejectionHandler_, 1, *""*, « »).
255- 1. Return ? Await(PerformPromiseThen(_promise_, _promiseFulfill_, _promiseReject_)).
41+ 1. Let _A_ be Completion(Evaluation of |Expression|).
42+ 1. If _A_ is an abrupt completion, return ? TryExpressionResult(_A_).
43+ 1. Let _B_ be Completion(GetValue(_A_)).
44+ 1. Return ? TryExpressionResult(_B_).
25645 </emu-alg>
46+ <emu-note>
47+ <p>GetValue must be called even though its value is not used because it may have observable side-effects.</p>
48+ </emu-note>
49+ <emu-note>
50+ <p>This is identical to <emu-xref href="#sec-try-statement-runtime-semantics-evaluation">evaluation of a try statement</emu-xref>.</p>
51+ </emu-note>
25752 </emu-clause>
258- </emu-clause>
25953
260- <emu-clause id="sec-syntax-directed-operations">
261- <h1>Syntax-Directed Operations</h1>
262-
263- <emu-clause id="sec-isvalidsimpleassignmenttarget" type="sdo">
264- <h1>Static Semantics: IsValidSimpleAssignmentTarget</h1>
265- <emu-grammar>UnaryExpression : `try` UnaryExpression</emu-grammar>
54+ <emu-clause id="sec-try-expression-result" type="abstract operation">
55+ <h1>
56+ TryExpressionResult (
57+ _result_: a Completion Record,
58+ ): either a normal completion containing a Result or an abrupt completion
59+ </h1>
60+ <dl class="header">
61+ </dl>
26662 <emu-alg>
267- 1. Return *false*.
63+ 1. If _result_ is a normal completion, return Result.ok(_result_.[[VALUE]]).
64+ 1. If _result_ is a throw completion, return Result.error(_result_.[[VALUE]]).
65+ 1. Return ? _result_.
26866 </emu-alg>
26967 </emu-clause>
68+ <emu-clause id="sec-try-expression-result-ok" type="abstract operation">
69+ <h1>
70+ Result.ok (
71+ _value_: an ECMAScript language value,
72+ ): a Result
73+ </h1>
74+ <dl class="header">
75+ <dt>description</dt>
76+ <dd>It is the abstract equivelant of calling `Result.ok(value)`</dd>
77+ </dl>
78+ </emu-clause>
79+ <emu-clause id="sec-try-expression-result-error" type="abstract operation">
80+ <h1>
81+ Result.error (
82+ _value_: an ECMAScript language value,
83+ ): a Result
84+ </h1>
85+ <dl class="header">
86+ <dt>description</dt>
87+ <dd>It is the abstract equivelant of calling `Result.error(value)`</dd>
88+ </dl>
89+ </emu-clause>
27090</emu-clause>
27191
272- <emu-annex id="sec-grammar-summary">
273- <h1>Grammar Summary</h1>
274-
275- <emu-annex id="sec-expressions">
276- <h1>Expressions</h1>
277-
278- <emu-prodref name="UnaryExpression"></emu-prodref>
279- <emu-grammar>
280- UnaryExpression[Yield, Await] :
281- UpdateExpression[?Yield, ?Await]
282- `delete` UnaryExpression[?Yield, ?Await]
283- `void` UnaryExpression[?Yield, ?Await]
284- `typeof` UnaryExpression[?Yield, ?Await]
285- `+` UnaryExpression[?Yield, ?Await]
286- `-` UnaryExpression[?Yield, ?Await]
287- `~` UnaryExpression[?Yield, ?Await]
288- `!` UnaryExpression[?Yield, ?Await]
289- <ins>`try` UnaryExpression[?Yield, ?Await]</ins>
290- [+Await] AwaitExpression[?Yield]
291- </emu-grammar>
292- </emu-annex>
293- </emu-annex>
92+ <emu-clause id="sec-result-constructor">
93+ <h1>The Result Constructor</h1>
94+ <pre><code class="javascript">
95+ class Result {
96+ constructor(ok, error, value) {
97+ ok = Boolean(ok)
98+ this.ok = ok
99+ if (ok) {
100+ this.value = value
101+ } else {
102+ this.error = error
103+ }
104+ }
105+ *[Symbol.iterator]() {
106+ yield this.ok
107+ yield this.error
108+ yield this.value
109+ }
110+ static ok(value) {
111+ return new Result(true, undefined, value)
112+ }
113+ static error(error) {
114+ return new Result(false, error, undefined)
115+ }
116+ /* a convenience method for user-land, not used by the try operator */
117+ static try(arg, ...args) {
118+ try {
119+ let result;
120+ if (/* IsCallable(arg) */typeof arg === "function") {
121+ result = arg.call(undefined, args)
122+ } else {
123+ result = arg;
124+ }
125+ if (/* IsPromise (result) */result instanceof Promise) {
126+ return result.then(Result.ok, Result.error)
127+ } else {
128+ return Result.ok(result);
129+ }
130+ } catch (e) {
131+ return Result.error(e)
132+ }
133+ }
134+ }
135+ </code></pre>
136+ </emu-clause>
0 commit comments