OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 (function(global, utils, extrasUtils) { | 5 (function(global, utils, extrasUtils) { |
6 | 6 |
7 "use strict"; | 7 "use strict"; |
8 | 8 |
9 %CheckIsBootstrapping(); | 9 %CheckIsBootstrapping(); |
10 | 10 |
11 // ------------------------------------------------------------------- | 11 // ------------------------------------------------------------------- |
12 // Imports | 12 // Imports |
13 | 13 |
14 var InternalArray = utils.InternalArray; | 14 var InternalArray = utils.InternalArray; |
15 var MakeTypeError; | 15 var MakeTypeError; |
16 var promiseCombinedDeferredSymbol = | 16 var promiseCombinedDeferredSymbol = |
17 utils.ImportNow("promise_combined_deferred_symbol"); | 17 utils.ImportNow("promise_combined_deferred_symbol"); |
18 var promiseHasHandlerSymbol = | 18 var promiseHasHandlerSymbol = |
19 utils.ImportNow("promise_has_handler_symbol"); | 19 utils.ImportNow("promise_has_handler_symbol"); |
20 var promiseRejectReactionsSymbol = | 20 var promiseRejectReactionsSymbol = |
21 utils.ImportNow("promise_reject_reactions_symbol"); | 21 utils.ImportNow("promise_reject_reactions_symbol"); |
22 var promiseFulfillReactionsSymbol = | 22 var promiseFulfillReactionsSymbol = |
23 utils.ImportNow("promise_fulfill_reactions_symbol"); | 23 utils.ImportNow("promise_fulfill_reactions_symbol"); |
| 24 var promiseDeferredReactionsSymbol = |
| 25 utils.ImportNow("promise_deferred_reactions_symbol"); |
24 var promiseRawSymbol = utils.ImportNow("promise_raw_symbol"); | 26 var promiseRawSymbol = utils.ImportNow("promise_raw_symbol"); |
25 var promiseStateSymbol = utils.ImportNow("promise_state_symbol"); | 27 var promiseStateSymbol = utils.ImportNow("promise_state_symbol"); |
26 var promiseResultSymbol = utils.ImportNow("promise_result_symbol"); | 28 var promiseResultSymbol = utils.ImportNow("promise_result_symbol"); |
27 var SpeciesConstructor; | 29 var SpeciesConstructor; |
28 var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol"); | 30 var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol"); |
29 | 31 |
30 utils.Import(function(from) { | 32 utils.Import(function(from) { |
31 MakeTypeError = from.MakeTypeError; | 33 MakeTypeError = from.MakeTypeError; |
32 SpeciesConstructor = from.SpeciesConstructor; | 34 SpeciesConstructor = from.SpeciesConstructor; |
33 }); | 35 }); |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
91 %_Call(callbacks.reject, UNDEFINED, e); | 93 %_Call(callbacks.reject, UNDEFINED, e); |
92 } finally { | 94 } finally { |
93 if (debug_is_active) %DebugPopPromise(); | 95 if (debug_is_active) %DebugPopPromise(); |
94 } | 96 } |
95 | 97 |
96 return promise; | 98 return promise; |
97 } | 99 } |
98 | 100 |
99 // Core functionality. | 101 // Core functionality. |
100 | 102 |
101 function PromiseSet(promise, status, value, onResolve, onReject) { | 103 function PromiseSet(promise, status, value) { |
102 SET_PRIVATE(promise, promiseStateSymbol, status); | 104 SET_PRIVATE(promise, promiseStateSymbol, status); |
103 SET_PRIVATE(promise, promiseResultSymbol, value); | 105 SET_PRIVATE(promise, promiseResultSymbol, value); |
104 SET_PRIVATE(promise, promiseFulfillReactionsSymbol, onResolve); | 106 |
105 SET_PRIVATE(promise, promiseRejectReactionsSymbol, onReject); | 107 // There are 3 possible states for the resolve, reject symbols when we add |
| 108 // a new callback -- |
| 109 // 1) UNDEFINED -- This is the zero state where there is no callback |
| 110 // registered. When we see this state, we directly attach the callbacks to |
| 111 // the symbol. |
| 112 // 2) !IS_ARRAY -- There is a single callback directly attached to the |
| 113 // symbols. We need to create a new array to store additional callbacks. |
| 114 // 3) IS_ARRAY -- There are multiple callbacks already registered, |
| 115 // therefore we can just push the new callback to the existing array. |
| 116 SET_PRIVATE(promise, promiseFulfillReactionsSymbol, UNDEFINED); |
| 117 SET_PRIVATE(promise, promiseRejectReactionsSymbol, UNDEFINED); |
| 118 SET_PRIVATE(promise, promiseDeferredReactionsSymbol, UNDEFINED); |
106 return promise; | 119 return promise; |
107 } | 120 } |
108 | 121 |
109 function PromiseCreateAndSet(status, value) { | 122 function PromiseCreateAndSet(status, value) { |
110 var promise = new GlobalPromise(promiseRawSymbol); | 123 var promise = new GlobalPromise(promiseRawSymbol); |
111 // If debug is active, notify about the newly created promise first. | 124 // If debug is active, notify about the newly created promise first. |
112 if (DEBUG_IS_ACTIVE) PromiseSet(promise, kPending, UNDEFINED); | 125 if (DEBUG_IS_ACTIVE) PromiseSet(promise, kPending, UNDEFINED); |
113 return PromiseSet(promise, status, value); | 126 return PromiseSet(promise, status, value); |
114 } | 127 } |
115 | 128 |
116 function PromiseInit(promise) { | 129 function PromiseInit(promise) { |
117 return PromiseSet( | 130 return PromiseSet( |
118 promise, kPending, UNDEFINED, new InternalArray, new InternalArray) | 131 promise, kPending, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED); |
119 } | 132 } |
120 | 133 |
121 function FulfillPromise(promise, status, value, promiseQueue) { | 134 function FulfillPromise(promise, status, value, promiseQueue) { |
122 if (GET_PRIVATE(promise, promiseStateSymbol) === kPending) { | 135 if (GET_PRIVATE(promise, promiseStateSymbol) === kPending) { |
123 var tasks = GET_PRIVATE(promise, promiseQueue); | 136 var tasks = GET_PRIVATE(promise, promiseQueue); |
124 if (tasks.length) PromiseEnqueue(value, tasks, status); | 137 if (!IS_UNDEFINED(tasks)) { |
| 138 var tasks = GET_PRIVATE(promise, promiseQueue); |
| 139 var deferreds = GET_PRIVATE(promise, promiseDeferredReactionsSymbol); |
| 140 PromiseEnqueue(value, tasks, deferreds, status); |
| 141 } |
125 PromiseSet(promise, status, value); | 142 PromiseSet(promise, status, value); |
126 } | 143 } |
127 } | 144 } |
128 | 145 |
129 function PromiseHandle(value, handler, deferred) { | 146 function PromiseHandle(value, handler, deferred) { |
130 var debug_is_active = DEBUG_IS_ACTIVE; | 147 var debug_is_active = DEBUG_IS_ACTIVE; |
131 try { | 148 try { |
132 if (debug_is_active) %DebugPushPromise(deferred.promise, PromiseHandle); | 149 if (debug_is_active) %DebugPushPromise(deferred.promise, PromiseHandle); |
133 var result = handler(value); | 150 var result = handler(value); |
134 deferred.resolve(result); | 151 deferred.resolve(result); |
135 } catch (exception) { | 152 } catch (exception) { |
136 try { deferred.reject(exception); } catch (e) { } | 153 try { deferred.reject(exception); } catch (e) { } |
137 } finally { | 154 } finally { |
138 if (debug_is_active) %DebugPopPromise(); | 155 if (debug_is_active) %DebugPopPromise(); |
139 } | 156 } |
140 } | 157 } |
141 | 158 |
142 function PromiseEnqueue(value, tasks, status) { | 159 function PromiseEnqueue(value, tasks, deferreds, status) { |
143 var id, name, instrumenting = DEBUG_IS_ACTIVE; | 160 var id, name, instrumenting = DEBUG_IS_ACTIVE; |
144 %EnqueueMicrotask(function() { | 161 %EnqueueMicrotask(function() { |
145 if (instrumenting) { | 162 if (instrumenting) { |
146 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); | 163 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); |
147 } | 164 } |
148 for (var i = 0; i < tasks.length; i += 2) { | 165 if (IS_ARRAY(tasks)) { |
149 PromiseHandle(value, tasks[i], tasks[i + 1]) | 166 for (var i = 0; i < tasks.length; i += 1) { |
| 167 PromiseHandle(value, tasks[i], deferreds[i]); |
| 168 } |
| 169 } else { |
| 170 PromiseHandle(value, tasks, deferreds); |
150 } | 171 } |
151 if (instrumenting) { | 172 if (instrumenting) { |
152 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); | 173 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); |
153 } | 174 } |
154 }); | 175 }); |
155 if (instrumenting) { | 176 if (instrumenting) { |
156 id = ++lastMicrotaskId; | 177 id = ++lastMicrotaskId; |
157 name = status === kFulfilled ? "Promise.resolve" : "Promise.reject"; | 178 name = status === kFulfilled ? "Promise.resolve" : "Promise.reject"; |
158 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); | 179 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); |
159 } | 180 } |
160 } | 181 } |
161 | 182 |
| 183 function PromiseAttachCallbacks(promise, deferred, onResolve, onReject) { |
| 184 var maybeResolveCallbacks = |
| 185 GET_PRIVATE(promise, promiseFulfillReactionsSymbol); |
| 186 if (IS_UNDEFINED(maybeResolveCallbacks)) { |
| 187 SET_PRIVATE(promise, promiseFulfillReactionsSymbol, onResolve); |
| 188 SET_PRIVATE(promise, promiseRejectReactionsSymbol, onReject); |
| 189 SET_PRIVATE(promise, promiseDeferredReactionsSymbol, deferred); |
| 190 } else if (!IS_ARRAY(maybeResolveCallbacks)) { |
| 191 var resolveCallbacks = new InternalArray(); |
| 192 var rejectCallbacks = new InternalArray(); |
| 193 var deferreds = new InternalArray(); |
| 194 |
| 195 resolveCallbacks.push(maybeResolveCallbacks); |
| 196 rejectCallbacks.push(GET_PRIVATE(promise, promiseRejectReactionsSymbol)); |
| 197 deferreds.push(GET_PRIVATE(promise, promiseDeferredReactionsSymbol)); |
| 198 |
| 199 resolveCallbacks.push(onResolve); |
| 200 rejectCallbacks.push(onReject); |
| 201 deferreds.push(deferred); |
| 202 |
| 203 SET_PRIVATE(promise, promiseFulfillReactionsSymbol, resolveCallbacks); |
| 204 SET_PRIVATE(promise, promiseRejectReactionsSymbol, rejectCallbacks); |
| 205 SET_PRIVATE(promise, promiseDeferredReactionsSymbol, deferreds); |
| 206 } else { |
| 207 maybeResolveCallbacks.push(onResolve); |
| 208 GET_PRIVATE(promise, promiseRejectReactionsSymbol).push(onReject); |
| 209 GET_PRIVATE(promise, promiseDeferredReactionsSymbol).push(deferred); |
| 210 } |
| 211 } |
| 212 |
162 function PromiseIdResolveHandler(x) { return x } | 213 function PromiseIdResolveHandler(x) { return x } |
163 function PromiseIdRejectHandler(r) { throw r } | 214 function PromiseIdRejectHandler(r) { throw r } |
164 | 215 |
165 function PromiseNopResolver() {} | 216 function PromiseNopResolver() {} |
166 | 217 |
167 // ------------------------------------------------------------------- | 218 // ------------------------------------------------------------------- |
168 // Define exported functions. | 219 // Define exported functions. |
169 | 220 |
170 // For bootstrapper. | 221 // For bootstrapper. |
171 | 222 |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
312 if (IS_UNDEFINED(status)) { | 363 if (IS_UNDEFINED(status)) { |
313 throw MakeTypeError(kNotAPromise, this); | 364 throw MakeTypeError(kNotAPromise, this); |
314 } | 365 } |
315 | 366 |
316 var constructor = SpeciesConstructor(this, GlobalPromise); | 367 var constructor = SpeciesConstructor(this, GlobalPromise); |
317 onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler; | 368 onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler; |
318 onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler; | 369 onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler; |
319 var deferred = NewPromiseCapability(constructor); | 370 var deferred = NewPromiseCapability(constructor); |
320 switch (status) { | 371 switch (status) { |
321 case kPending: | 372 case kPending: |
322 GET_PRIVATE(this, promiseFulfillReactionsSymbol).push(onResolve, | 373 PromiseAttachCallbacks(this, deferred, onResolve, onReject); |
323 deferred); | |
324 GET_PRIVATE(this, promiseRejectReactionsSymbol).push(onReject, deferred); | |
325 break; | 374 break; |
326 case kFulfilled: | 375 case kFulfilled: |
327 PromiseEnqueue(GET_PRIVATE(this, promiseResultSymbol), | 376 PromiseEnqueue(GET_PRIVATE(this, promiseResultSymbol), |
328 [onResolve, deferred], | 377 onResolve, deferred, kFulfilled); |
329 kFulfilled); | |
330 break; | 378 break; |
331 case kRejected: | 379 case kRejected: |
332 if (!HAS_DEFINED_PRIVATE(this, promiseHasHandlerSymbol)) { | 380 if (!HAS_DEFINED_PRIVATE(this, promiseHasHandlerSymbol)) { |
333 // Promise has already been rejected, but had no handler. | 381 // Promise has already been rejected, but had no handler. |
334 // Revoke previously triggered reject event. | 382 // Revoke previously triggered reject event. |
335 %PromiseRevokeReject(this); | 383 %PromiseRevokeReject(this); |
336 } | 384 } |
337 PromiseEnqueue(GET_PRIVATE(this, promiseResultSymbol), | 385 PromiseEnqueue(GET_PRIVATE(this, promiseResultSymbol), |
338 [onReject, deferred], | 386 onReject, deferred, kRejected); |
339 kRejected); | |
340 break; | 387 break; |
341 } | 388 } |
342 // Mark this promise as having handler. | 389 // Mark this promise as having handler. |
343 SET_PRIVATE(this, promiseHasHandlerSymbol, true); | 390 SET_PRIVATE(this, promiseHasHandlerSymbol, true); |
344 return deferred.promise; | 391 return deferred.promise; |
345 } | 392 } |
346 | 393 |
347 // Unspecified V8-specific legacy function | 394 // Unspecified V8-specific legacy function |
348 // Chain is left around for now as an alias for then | 395 // Chain is left around for now as an alias for then |
349 function PromiseChain(onResolve, onReject) { | 396 function PromiseChain(onResolve, onReject) { |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
438 } | 485 } |
439 } catch (e) { | 486 } catch (e) { |
440 deferred.reject(e) | 487 deferred.reject(e) |
441 } | 488 } |
442 return deferred.promise; | 489 return deferred.promise; |
443 } | 490 } |
444 | 491 |
445 | 492 |
446 // Utility for debugger | 493 // Utility for debugger |
447 | 494 |
| 495 function PromiseHasUserDefinedRejectHandlerCheck(handler, deferred) { |
| 496 if (handler !== PromiseIdRejectHandler) { |
| 497 var combinedDeferred = GET_PRIVATE(handler, promiseCombinedDeferredSymbol); |
| 498 if (IS_UNDEFINED(combinedDeferred)) return true; |
| 499 if (PromiseHasUserDefinedRejectHandlerRecursive(combinedDeferred.promise)) { |
| 500 return true; |
| 501 } |
| 502 } else if (PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise)) { |
| 503 return true; |
| 504 } |
| 505 return false; |
| 506 } |
| 507 |
448 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { | 508 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { |
449 var queue = GET_PRIVATE(promise, promiseRejectReactionsSymbol); | 509 var queue = GET_PRIVATE(promise, promiseRejectReactionsSymbol); |
| 510 var deferreds = GET_PRIVATE(promise, promiseDeferredReactionsSymbol); |
450 if (IS_UNDEFINED(queue)) return false; | 511 if (IS_UNDEFINED(queue)) return false; |
451 for (var i = 0; i < queue.length; i += 2) { | 512 if (!IS_ARRAY(queue)) { |
452 var handler = queue[i]; | 513 return PromiseHasUserDefinedRejectHandlerCheck(queue, deferreds); |
453 if (handler !== PromiseIdRejectHandler) { | 514 } else { |
454 var deferred = GET_PRIVATE(handler, promiseCombinedDeferredSymbol); | 515 for (var i = 0; i < queue.length; i += 1) { |
455 if (IS_UNDEFINED(deferred)) return true; | 516 if (PromiseHasUserDefinedRejectHandlerCheck(queue[i], deferreds[i])) { |
456 if (PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise)) { | |
457 return true; | 517 return true; |
458 } | 518 } |
459 } else if (PromiseHasUserDefinedRejectHandlerRecursive( | |
460 queue[i + 1].promise)) { | |
461 return true; | |
462 } | 519 } |
463 } | 520 } |
464 return false; | 521 return false; |
465 } | 522 } |
466 | 523 |
467 // Return whether the promise will be handled by a user-defined reject | 524 // Return whether the promise will be handled by a user-defined reject |
468 // handler somewhere down the promise chain. For this, we do a depth-first | 525 // handler somewhere down the promise chain. For this, we do a depth-first |
469 // search for a reject handler that's not the default PromiseIdRejectHandler. | 526 // search for a reject handler that's not the default PromiseIdRejectHandler. |
470 function PromiseHasUserDefinedRejectHandler() { | 527 function PromiseHasUserDefinedRejectHandler() { |
471 return PromiseHasUserDefinedRejectHandlerRecursive(this); | 528 return PromiseHasUserDefinedRejectHandlerRecursive(this); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
519 to.PromiseChain = PromiseChain; | 576 to.PromiseChain = PromiseChain; |
520 to.PromiseDefer = PromiseDefer; | 577 to.PromiseDefer = PromiseDefer; |
521 to.PromiseAccept = PromiseAccept; | 578 to.PromiseAccept = PromiseAccept; |
522 | 579 |
523 to.PromiseCreateRejected = PromiseCreateRejected; | 580 to.PromiseCreateRejected = PromiseCreateRejected; |
524 to.PromiseCreateResolved = PromiseCreateResolved; | 581 to.PromiseCreateResolved = PromiseCreateResolved; |
525 to.PromiseThen = PromiseThen; | 582 to.PromiseThen = PromiseThen; |
526 }); | 583 }); |
527 | 584 |
528 }) | 585 }) |
OLD | NEW |