| 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 "use strict"; | 5 "use strict"; |
| 6 | 6 |
| 7 // This file relies on the fact that the following declaration has been made | 7 // This file relies on the fact that the following declaration has been made |
| 8 // in runtime.js: | 8 // in runtime.js: |
| 9 // var $Object = global.Object | 9 // var $Object = global.Object |
| 10 // var $WeakMap = global.WeakMap | 10 // var $WeakMap = global.WeakMap |
| 11 | 11 |
| 12 // For bootstrapper. | 12 // For bootstrapper. |
| 13 | 13 |
| 14 var IsPromise; | 14 var IsPromise; |
| 15 var PromiseCreate; | 15 var PromiseCreate; |
| 16 var PromiseResolve; | 16 var PromiseResolve; |
| 17 var PromiseReject; | 17 var PromiseReject; |
| 18 var PromiseChain; | 18 var PromiseChain; |
| 19 var PromiseCatch; | 19 var PromiseCatch; |
| 20 var PromiseThen; | 20 var PromiseThen; |
| 21 var PromiseHasRejectHandler; | 21 var PromiseScheduleUnhandledRejectMessage; |
| 22 var PromiseScheduleRevokeRejectMessage; |
| 23 var PromiseDispatchUnhandledRejectMessages; |
| 22 var PromiseHasUserDefinedRejectHandler; | 24 var PromiseHasUserDefinedRejectHandler; |
| 23 | 25 |
| 24 // mirror-debugger.js currently uses builtins.promiseStatus. It would be nice | 26 // mirror-debugger.js currently uses builtins.promiseStatus. It would be nice |
| 25 // if we could move these property names into the closure below. | 27 // if we could move these property names into the closure below. |
| 26 // TODO(jkummerow/rossberg/yangguo): Find a better solution. | 28 // TODO(jkummerow/rossberg/yangguo): Find a better solution. |
| 27 | 29 |
| 28 // Status values: 0 = pending, +1 = resolved, -1 = rejected | 30 // Status values: 0 = pending, +1 = resolved, -1 = rejected |
| 29 var promiseStatus = GLOBAL_PRIVATE("Promise#status"); | 31 var promiseStatus = GLOBAL_PRIVATE("Promise#status"); |
| 30 var promiseValue = GLOBAL_PRIVATE("Promise#value"); | 32 var promiseValue = GLOBAL_PRIVATE("Promise#value"); |
| 31 var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve"); | 33 var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve"); |
| 32 var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject"); | 34 var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject"); |
| 33 var promiseRaw = GLOBAL_PRIVATE("Promise#raw"); | 35 var promiseRaw = GLOBAL_PRIVATE("Promise#raw"); |
| 34 var promiseHasHandler = %PromiseHasHandlerSymbol(); | 36 var promiseHasHandler = GLOBAL_PRIVATE("Promise#hasHandler"); |
| 37 // Enqueued promise message: true = revoke reject, false = unhandled reject |
| 38 var promiseMessage = GLOBAL_PRIVATE("Promise#message"); |
| 35 var lastMicrotaskId = 0; | 39 var lastMicrotaskId = 0; |
| 40 var promiseMicrotasksCount = 0; |
| 41 var enqueuedPromiseMessages; |
| 36 | 42 |
| 37 | 43 |
| 38 (function() { | 44 (function() { |
| 39 | 45 |
| 40 var $Promise = function Promise(resolver) { | 46 var $Promise = function Promise(resolver) { |
| 41 if (resolver === promiseRaw) return; | 47 if (resolver === promiseRaw) return; |
| 42 if (!%_IsConstructCall()) throw MakeTypeError('not_a_promise', [this]); | 48 if (!%_IsConstructCall()) throw MakeTypeError('not_a_promise', [this]); |
| 43 if (!IS_SPEC_FUNCTION(resolver)) | 49 if (!IS_SPEC_FUNCTION(resolver)) |
| 44 throw MakeTypeError('resolver_not_a_function', [resolver]); | 50 throw MakeTypeError('resolver_not_a_function', [resolver]); |
| 45 var promise = PromiseInit(this); | 51 var promise = PromiseInit(this); |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 112 deferred.resolve(result); | 118 deferred.resolve(result); |
| 113 } catch (exception) { | 119 } catch (exception) { |
| 114 try { deferred.reject(exception); } catch (e) { } | 120 try { deferred.reject(exception); } catch (e) { } |
| 115 } finally { | 121 } finally { |
| 116 %DebugPopPromise(); | 122 %DebugPopPromise(); |
| 117 } | 123 } |
| 118 } | 124 } |
| 119 | 125 |
| 120 function PromiseEnqueue(value, tasks, status) { | 126 function PromiseEnqueue(value, tasks, status) { |
| 121 var id, name, instrumenting = DEBUG_IS_ACTIVE; | 127 var id, name, instrumenting = DEBUG_IS_ACTIVE; |
| 128 ++promiseMicrotasksCount; |
| 122 %EnqueueMicrotask(function() { | 129 %EnqueueMicrotask(function() { |
| 123 if (instrumenting) { | 130 if (instrumenting) { |
| 124 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); | 131 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); |
| 125 } | 132 } |
| 126 for (var i = 0; i < tasks.length; i += 2) { | 133 for (var i = 0; i < tasks.length; i += 2) { |
| 127 PromiseHandle(value, tasks[i], tasks[i + 1]) | 134 PromiseHandle(value, tasks[i], tasks[i + 1]); |
| 128 } | 135 } |
| 129 if (instrumenting) { | 136 if (instrumenting) { |
| 130 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); | 137 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); |
| 131 } | 138 } |
| 139 --promiseMicrotasksCount; |
| 132 }); | 140 }); |
| 133 if (instrumenting) { | 141 if (instrumenting) { |
| 134 id = ++lastMicrotaskId; | 142 id = ++lastMicrotaskId; |
| 135 name = status > 0 ? "Promise.resolve" : "Promise.reject"; | 143 name = status > 0 ? "Promise.resolve" : "Promise.reject"; |
| 136 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); | 144 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); |
| 137 } | 145 } |
| 138 } | 146 } |
| 139 | 147 |
| 140 function PromiseIdResolveHandler(x) { return x } | 148 function PromiseIdResolveHandler(x) { return x } |
| 141 function PromiseIdRejectHandler(r) { throw r } | 149 function PromiseIdRejectHandler(r) { throw r } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 156 } | 164 } |
| 157 | 165 |
| 158 PromiseResolve = function PromiseResolve(promise, x) { | 166 PromiseResolve = function PromiseResolve(promise, x) { |
| 159 PromiseDone(promise, +1, x, promiseOnResolve) | 167 PromiseDone(promise, +1, x, promiseOnResolve) |
| 160 } | 168 } |
| 161 | 169 |
| 162 PromiseReject = function PromiseReject(promise, r) { | 170 PromiseReject = function PromiseReject(promise, r) { |
| 163 // Check promise status to confirm that this reject has an effect. | 171 // Check promise status to confirm that this reject has an effect. |
| 164 // Call runtime for callbacks to the debugger or for unhandled reject. | 172 // Call runtime for callbacks to the debugger or for unhandled reject. |
| 165 if (GET_PRIVATE(promise, promiseStatus) == 0) { | 173 if (GET_PRIVATE(promise, promiseStatus) == 0) { |
| 166 var debug_is_active = DEBUG_IS_ACTIVE; | 174 if (DEBUG_IS_ACTIVE) { |
| 167 if (debug_is_active || !HAS_DEFINED_PRIVATE(promise, promiseHasHandler)) { | 175 %DebugPromiseRejectEvent(promise, r); |
| 168 %PromiseRejectEvent(promise, r, debug_is_active); | 176 } |
| 177 if (!HAS_DEFINED_PRIVATE(promise, promiseHasHandler)) { |
| 178 PromiseScheduleUnhandledRejectMessage(promise, r, false); |
| 169 } | 179 } |
| 170 } | 180 } |
| 171 PromiseDone(promise, -1, r, promiseOnReject) | 181 PromiseDone(promise, -1, r, promiseOnReject) |
| 172 } | 182 } |
| 173 | 183 |
| 174 // Convenience. | 184 // Convenience. |
| 175 | 185 |
| 176 function PromiseDeferred() { | 186 function PromiseDeferred() { |
| 177 if (this === $Promise) { | 187 if (this === $Promise) { |
| 178 // Optimized case, avoid extra closure. | 188 // Optimized case, avoid extra closure. |
| (...skipping 20 matching lines...) Expand all Loading... |
| 199 } else { | 209 } else { |
| 200 return new this(function(resolve, reject) { resolve(x) }); | 210 return new this(function(resolve, reject) { resolve(x) }); |
| 201 } | 211 } |
| 202 } | 212 } |
| 203 | 213 |
| 204 function PromiseRejected(r) { | 214 function PromiseRejected(r) { |
| 205 var promise; | 215 var promise; |
| 206 if (this === $Promise) { | 216 if (this === $Promise) { |
| 207 // Optimized case, avoid extra closure. | 217 // Optimized case, avoid extra closure. |
| 208 promise = PromiseSet(new $Promise(promiseRaw), -1, r); | 218 promise = PromiseSet(new $Promise(promiseRaw), -1, r); |
| 209 // The debug event for this would always be an uncaught promise reject, | 219 PromiseScheduleUnhandledRejectMessage(promise, r, false); |
| 210 // which is usually simply noise. Do not trigger that debug event. | |
| 211 %PromiseRejectEvent(promise, r, false); | |
| 212 } else { | 220 } else { |
| 213 promise = new this(function(resolve, reject) { reject(r) }); | 221 promise = new this(function(resolve, reject) { reject(r) }); |
| 214 } | 222 } |
| 215 return promise; | 223 return promise; |
| 216 } | 224 } |
| 217 | 225 |
| 218 // Simple chaining. | 226 // Simple chaining. |
| 219 | 227 |
| 220 PromiseChain = function PromiseChain(onResolve, onReject) { // a.k.a. | 228 PromiseChain = function PromiseChain(onResolve, onReject) { // a.k.a. |
| 221 // flatMap | 229 // flatMap |
| 222 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; | 230 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; |
| 223 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; | 231 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; |
| 224 var deferred = %_CallFunction(this.constructor, PromiseDeferred); | 232 var deferred = %_CallFunction(this.constructor, PromiseDeferred); |
| 225 switch (GET_PRIVATE(this, promiseStatus)) { | 233 switch (GET_PRIVATE(this, promiseStatus)) { |
| 226 case UNDEFINED: | 234 case UNDEFINED: |
| 227 throw MakeTypeError('not_a_promise', [this]); | 235 throw MakeTypeError('not_a_promise', [this]); |
| 228 case 0: // Pending | 236 case 0: // Pending |
| 229 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred); | 237 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred); |
| 230 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred); | 238 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred); |
| 231 break; | 239 break; |
| 232 case +1: // Resolved | 240 case +1: // Resolved |
| 233 PromiseEnqueue(GET_PRIVATE(this, promiseValue), | 241 PromiseEnqueue(GET_PRIVATE(this, promiseValue), |
| 234 [onResolve, deferred], | 242 [onResolve, deferred], |
| 235 +1); | 243 +1); |
| 236 break; | 244 break; |
| 237 case -1: // Rejected | 245 case -1: // Rejected |
| 238 if (!HAS_DEFINED_PRIVATE(this, promiseHasHandler)) { | 246 if (!HAS_DEFINED_PRIVATE(this, promiseHasHandler)) { |
| 239 // Promise has already been rejected, but had no handler. | 247 // Promise has already been rejected, but had no handler. |
| 240 // Revoke previously triggered reject event. | 248 // Revoke previously triggered reject message. |
| 241 %PromiseRevokeReject(this); | 249 PromiseScheduleRevokeRejectMessage(this); |
| 242 } | 250 } |
| 243 PromiseEnqueue(GET_PRIVATE(this, promiseValue), | 251 PromiseEnqueue(GET_PRIVATE(this, promiseValue), |
| 244 [onReject, deferred], | 252 [onReject, deferred], |
| 245 -1); | 253 -1); |
| 246 break; | 254 break; |
| 247 } | 255 } |
| 248 // Mark this promise as having handler. | 256 // Mark this promise as having handler. |
| 249 SET_PRIVATE(this, promiseHasHandler, true); | 257 SET_PRIVATE(this, promiseHasHandler, true); |
| 250 if (DEBUG_IS_ACTIVE) { | 258 if (DEBUG_IS_ACTIVE) { |
| 251 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); | 259 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 333 } | 341 } |
| 334 } catch (e) { | 342 } catch (e) { |
| 335 deferred.reject(e) | 343 deferred.reject(e) |
| 336 } | 344 } |
| 337 return deferred.promise; | 345 return deferred.promise; |
| 338 } | 346 } |
| 339 | 347 |
| 340 | 348 |
| 341 // Utility for debugger | 349 // Utility for debugger |
| 342 | 350 |
| 351 function PromiseScheduleUnhandledRejectMessage(promise, r, revoke) { |
| 352 if (!enqueuedPromiseMessages) { |
| 353 enqueuedPromiseMessages = new InternalArray; |
| 354 %EnqueueMicrotask(PromiseDispatchUnhandledRejectMessages); |
| 355 } |
| 356 SET_PRIVATE(promise, promiseMessage, revoke); |
| 357 enqueuedPromiseMessages.push(promise, r); |
| 358 } |
| 359 |
| 360 function PromiseScheduleRevokeRejectMessage(promise) { |
| 361 if (HAS_DEFINED_PRIVATE(promise, promiseMessage)) { |
| 362 DELETE_PRIVATE(promise, promiseMessage); |
| 363 } else { |
| 364 PromiseScheduleUnhandledRejectMessage(promise, UNDEFINED, true); |
| 365 } |
| 366 } |
| 367 |
| 368 function PromiseDispatchUnhandledRejectMessages() { |
| 369 if (promiseMicrotasksCount || %PendingMicrotaskCount()) { |
| 370 %EnqueueMicrotask(PromiseDispatchUnhandledRejectMessages); |
| 371 return; |
| 372 } |
| 373 var queue = enqueuedPromiseMessages; |
| 374 enqueuedPromiseMessages = UNDEFINED; |
| 375 for (var i = 0; i < queue.length; i += 2) { |
| 376 var promise = queue[i]; |
| 377 if (HAS_DEFINED_PRIVATE(promise, promiseMessage)) { |
| 378 var revoke = GET_PRIVATE(promise, promiseMessage); |
| 379 DELETE_PRIVATE(promise, promiseMessage); |
| 380 if (revoke) %PromiseRevokeRejectMessage(promise); |
| 381 else %PromiseRejectMessage(promise, queue[i + 1]); |
| 382 } |
| 383 } |
| 384 } |
| 385 |
| 343 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { | 386 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { |
| 344 var queue = GET_PRIVATE(promise, promiseOnReject); | 387 var queue = GET_PRIVATE(promise, promiseOnReject); |
| 345 if (IS_UNDEFINED(queue)) return false; | 388 if (IS_UNDEFINED(queue)) return false; |
| 346 for (var i = 0; i < queue.length; i += 2) { | 389 for (var i = 0; i < queue.length; i += 2) { |
| 347 if (queue[i] != PromiseIdRejectHandler) return true; | 390 if (queue[i] != PromiseIdRejectHandler) return true; |
| 348 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) { | 391 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) { |
| 349 return true; | 392 return true; |
| 350 } | 393 } |
| 351 } | 394 } |
| 352 return false; | 395 return false; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 373 "race", PromiseOne, | 416 "race", PromiseOne, |
| 374 "resolve", PromiseCast | 417 "resolve", PromiseCast |
| 375 ]); | 418 ]); |
| 376 InstallFunctions($Promise.prototype, DONT_ENUM, [ | 419 InstallFunctions($Promise.prototype, DONT_ENUM, [ |
| 377 "chain", PromiseChain, | 420 "chain", PromiseChain, |
| 378 "then", PromiseThen, | 421 "then", PromiseThen, |
| 379 "catch", PromiseCatch | 422 "catch", PromiseCatch |
| 380 ]); | 423 ]); |
| 381 | 424 |
| 382 })(); | 425 })(); |
| OLD | NEW |