| 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 |