| 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 var $promiseCreate; | 5 var $promiseCreate; |
| 6 var $promiseResolve; | 6 var $promiseResolve; |
| 7 var $promiseReject; | 7 var $promiseReject; |
| 8 var $promiseChain; | 8 var $promiseChain; |
| 9 var $promiseCatch; | 9 var $promiseCatch; |
| 10 var $promiseThen; | 10 var $promiseThen; |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 98 %_CallFunction(x, deferred.resolve, deferred.reject, then); | 98 %_CallFunction(x, deferred.resolve, deferred.reject, then); |
| 99 } catch(r) { | 99 } catch(r) { |
| 100 deferred.reject(r); | 100 deferred.reject(r); |
| 101 } | 101 } |
| 102 return deferred.promise; | 102 return deferred.promise; |
| 103 } | 103 } |
| 104 } | 104 } |
| 105 return x; | 105 return x; |
| 106 } | 106 } |
| 107 | 107 |
| 108 function PromiseHandle(value, handler, deferred) { | 108 function PromiseHandle(value, handler, deferred, thenable) { |
| 109 try { | 109 try { |
| 110 %DebugPushPromise(deferred.promise, PromiseHandle); | 110 %DebugPushPromise(deferred.promise, PromiseHandle); |
| 111 DEBUG_PREPARE_STEP_IN_IF_STEPPING(handler); | 111 DEBUG_PREPARE_STEP_IN_IF_STEPPING(handler); |
| 112 var result = handler(value); | 112 var result = handler(value); |
| 113 if (result === deferred.promise) | 113 if (result === deferred.promise) { |
| 114 throw MakeTypeError(kPromiseCyclic, result); | 114 throw MakeTypeError(kPromiseCyclic, result); |
| 115 else if (IsPromise(result)) | 115 } else if (IsPromise(result) && thenable) { |
| 116 var then = result.then; |
| 117 if (IS_SPEC_FUNCTION(then)) { |
| 118 PromiseCallThenInSeparateTask(result, deferred, then); |
| 119 } else { |
| 120 deferred.resolve(result); |
| 121 } |
| 122 } else if (IsPromise(result)) { |
| 116 %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain); | 123 %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain); |
| 117 else | 124 } else { |
| 118 deferred.resolve(result); | 125 deferred.resolve(result); |
| 126 } |
| 119 } catch (exception) { | 127 } catch (exception) { |
| 120 try { deferred.reject(exception); } catch (e) { } | 128 try { deferred.reject(exception); } catch (e) { } |
| 121 } finally { | 129 } finally { |
| 122 %DebugPopPromise(); | 130 %DebugPopPromise(); |
| 123 } | 131 } |
| 124 } | 132 } |
| 125 | 133 |
| 134 function PromiseCallThenInSeparateTask(result, deferred, then) { |
| 135 var id, name, instrumenting = DEBUG_IS_ACTIVE; |
| 136 %EnqueueMicrotask(function() { |
| 137 if (instrumenting) { |
| 138 %DebugAsyncTaskEvent({ type: "willCall", id: id, name: name }); |
| 139 } |
| 140 try { |
| 141 %_CallFunction(result, deferred.resolve, deferred.reject, then); |
| 142 } catch(exception) { |
| 143 try { deferred.reject(exception); } catch (e) { } |
| 144 } |
| 145 if (instrumenting) { |
| 146 %DebugAsyncTaskEvent({ type: "didCall", id: id, name: name }); |
| 147 } |
| 148 }); |
| 149 if (instrumenting) { |
| 150 id = ++lastMicrotaskId; |
| 151 name = "Promise.prototype.then"; |
| 152 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); |
| 153 } |
| 154 } |
| 155 |
| 126 function PromiseEnqueue(value, tasks, status) { | 156 function PromiseEnqueue(value, tasks, status) { |
| 127 var id, name, instrumenting = DEBUG_IS_ACTIVE; | 157 var id, name, instrumenting = DEBUG_IS_ACTIVE; |
| 128 %EnqueueMicrotask(function() { | 158 %EnqueueMicrotask(function() { |
| 129 if (instrumenting) { | 159 if (instrumenting) { |
| 130 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); | 160 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); |
| 131 } | 161 } |
| 132 for (var i = 0; i < tasks.length; i += 2) { | 162 for (var i = 0; i < tasks.length; i += 3) { |
| 133 PromiseHandle(value, tasks[i], tasks[i + 1]) | 163 // tasks[i: i + 2] consists of the reaction handler, the associated |
| 164 // deferred object and whether the thenable handling is required. |
| 165 PromiseHandle(value, tasks[i], tasks[i + 1], tasks[i + 2]) |
| 134 } | 166 } |
| 135 if (instrumenting) { | 167 if (instrumenting) { |
| 136 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); | 168 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); |
| 137 } | 169 } |
| 138 }); | 170 }); |
| 139 if (instrumenting) { | 171 if (instrumenting) { |
| 140 id = ++lastMicrotaskId; | 172 id = ++lastMicrotaskId; |
| 141 name = status > 0 ? "Promise.resolve" : "Promise.reject"; | 173 name = status > 0 ? "Promise.resolve" : "Promise.reject"; |
| 142 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); | 174 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); |
| 143 } | 175 } |
| 144 } | 176 } |
| 145 | 177 |
| 178 function PromiseChainInternal(onResolve, onReject, thenable) { |
| 179 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; |
| 180 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; |
| 181 var deferred = %_CallFunction(this.constructor, PromiseDeferred); |
| 182 switch (GET_PRIVATE(this, promiseStatus)) { |
| 183 case UNDEFINED: |
| 184 throw MakeTypeError(kNotAPromise, this); |
| 185 case 0: // Pending |
| 186 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred, thenable); |
| 187 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred, thenable); |
| 188 break; |
| 189 case +1: // Resolved |
| 190 PromiseEnqueue(GET_PRIVATE(this, promiseValue), |
| 191 [onResolve, deferred, thenable], |
| 192 +1); |
| 193 break; |
| 194 case -1: // Rejected |
| 195 if (!HAS_DEFINED_PRIVATE(this, promiseHasHandler)) { |
| 196 // Promise has already been rejected, but had no handler. |
| 197 // Revoke previously triggered reject event. |
| 198 %PromiseRevokeReject(this); |
| 199 } |
| 200 PromiseEnqueue(GET_PRIVATE(this, promiseValue), |
| 201 [onReject, deferred, thenable], |
| 202 -1); |
| 203 break; |
| 204 } |
| 205 // Mark this promise as having handler. |
| 206 SET_PRIVATE(this, promiseHasHandler, true); |
| 207 if (DEBUG_IS_ACTIVE) { |
| 208 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); |
| 209 } |
| 210 return deferred.promise; |
| 211 } |
| 212 |
| 146 function PromiseIdResolveHandler(x) { return x } | 213 function PromiseIdResolveHandler(x) { return x } |
| 147 function PromiseIdRejectHandler(r) { throw r } | 214 function PromiseIdRejectHandler(r) { throw r } |
| 148 | 215 |
| 149 function PromiseNopResolver() {} | 216 function PromiseNopResolver() {} |
| 150 | 217 |
| 151 // ------------------------------------------------------------------- | 218 // ------------------------------------------------------------------- |
| 152 // Define exported functions. | 219 // Define exported functions. |
| 153 | 220 |
| 154 // For bootstrapper. | 221 // For bootstrapper. |
| 155 | 222 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 217 %PromiseRejectEvent(promise, r, false); | 284 %PromiseRejectEvent(promise, r, false); |
| 218 } else { | 285 } else { |
| 219 promise = new this(function(resolve, reject) { reject(r) }); | 286 promise = new this(function(resolve, reject) { reject(r) }); |
| 220 } | 287 } |
| 221 return promise; | 288 return promise; |
| 222 } | 289 } |
| 223 | 290 |
| 224 // Simple chaining. | 291 // Simple chaining. |
| 225 | 292 |
| 226 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap | 293 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap |
| 227 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; | 294 return %_CallFunction(this, onResolve, onReject, false, |
| 228 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; | 295 PromiseChainInternal); |
| 229 var deferred = %_CallFunction(this.constructor, PromiseDeferred); | |
| 230 switch (GET_PRIVATE(this, promiseStatus)) { | |
| 231 case UNDEFINED: | |
| 232 throw MakeTypeError(kNotAPromise, this); | |
| 233 case 0: // Pending | |
| 234 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred); | |
| 235 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred); | |
| 236 break; | |
| 237 case +1: // Resolved | |
| 238 PromiseEnqueue(GET_PRIVATE(this, promiseValue), | |
| 239 [onResolve, deferred], | |
| 240 +1); | |
| 241 break; | |
| 242 case -1: // Rejected | |
| 243 if (!HAS_DEFINED_PRIVATE(this, promiseHasHandler)) { | |
| 244 // Promise has already been rejected, but had no handler. | |
| 245 // Revoke previously triggered reject event. | |
| 246 %PromiseRevokeReject(this); | |
| 247 } | |
| 248 PromiseEnqueue(GET_PRIVATE(this, promiseValue), | |
| 249 [onReject, deferred], | |
| 250 -1); | |
| 251 break; | |
| 252 } | |
| 253 // Mark this promise as having handler. | |
| 254 SET_PRIVATE(this, promiseHasHandler, true); | |
| 255 if (DEBUG_IS_ACTIVE) { | |
| 256 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); | |
| 257 } | |
| 258 return deferred.promise; | |
| 259 } | 296 } |
| 260 | 297 |
| 261 function PromiseCatch(onReject) { | 298 function PromiseCatch(onReject) { |
| 262 return this.then(UNDEFINED, onReject); | 299 return this.then(UNDEFINED, onReject); |
| 263 } | 300 } |
| 264 | 301 |
| 265 // Multi-unwrapped chaining with thenable coercion. | 302 // Multi-unwrapped chaining with thenable coercion. |
| 266 | 303 |
| 267 function PromiseThen(onResolve, onReject) { | 304 function PromiseThen(onResolve, onReject) { |
| 268 onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve | 305 onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve |
| (...skipping 10 matching lines...) Expand all Loading... |
| 279 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject); | 316 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject); |
| 280 return onReject(MakeTypeError(kPromiseCyclic, x)); | 317 return onReject(MakeTypeError(kPromiseCyclic, x)); |
| 281 } else if (IsPromise(x)) { | 318 } else if (IsPromise(x)) { |
| 282 return x.then(onResolve, onReject); | 319 return x.then(onResolve, onReject); |
| 283 } else { | 320 } else { |
| 284 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve); | 321 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve); |
| 285 return onResolve(x); | 322 return onResolve(x); |
| 286 } | 323 } |
| 287 }, | 324 }, |
| 288 onReject, | 325 onReject, |
| 289 PromiseChain | 326 true, |
| 327 PromiseChainInternal |
| 290 ); | 328 ); |
| 291 } | 329 } |
| 292 | 330 |
| 293 // Combinators. | 331 // Combinators. |
| 294 | 332 |
| 295 function PromiseCast(x) { | 333 function PromiseCast(x) { |
| 296 // TODO(rossberg): cannot do better until we support @@create. | 334 // TODO(rossberg): cannot do better until we support @@create. |
| 297 return IsPromise(x) ? x : new this(function(resolve) { resolve(x) }); | 335 return IsPromise(x) ? x : new this(function(resolve) { resolve(x) }); |
| 298 } | 336 } |
| 299 | 337 |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 341 } | 379 } |
| 342 return deferred.promise; | 380 return deferred.promise; |
| 343 } | 381 } |
| 344 | 382 |
| 345 | 383 |
| 346 // Utility for debugger | 384 // Utility for debugger |
| 347 | 385 |
| 348 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { | 386 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { |
| 349 var queue = GET_PRIVATE(promise, promiseOnReject); | 387 var queue = GET_PRIVATE(promise, promiseOnReject); |
| 350 if (IS_UNDEFINED(queue)) return false; | 388 if (IS_UNDEFINED(queue)) return false; |
| 351 for (var i = 0; i < queue.length; i += 2) { | 389 for (var i = 0; i < queue.length; i += 3) { |
| 352 if (queue[i] != PromiseIdRejectHandler) return true; | 390 if (queue[i] != PromiseIdRejectHandler) return true; |
| 353 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) { | 391 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) { |
| 354 return true; | 392 return true; |
| 355 } | 393 } |
| 356 } | 394 } |
| 357 return false; | 395 return false; |
| 358 } | 396 } |
| 359 | 397 |
| 360 // Return whether the promise will be handled by a user-defined reject | 398 // Return whether the promise will be handled by a user-defined reject |
| 361 // handler somewhere down the promise chain. For this, we do a depth-first | 399 // handler somewhere down the promise chain. For this, we do a depth-first |
| (...skipping 28 matching lines...) Expand all Loading... |
| 390 $promiseResolve = PromiseResolve; | 428 $promiseResolve = PromiseResolve; |
| 391 $promiseReject = PromiseReject; | 429 $promiseReject = PromiseReject; |
| 392 $promiseChain = PromiseChain; | 430 $promiseChain = PromiseChain; |
| 393 $promiseCatch = PromiseCatch; | 431 $promiseCatch = PromiseCatch; |
| 394 $promiseThen = PromiseThen; | 432 $promiseThen = PromiseThen; |
| 395 $promiseHasUserDefinedRejectHandler = PromiseHasUserDefinedRejectHandler; | 433 $promiseHasUserDefinedRejectHandler = PromiseHasUserDefinedRejectHandler; |
| 396 $promiseStatus = promiseStatus; | 434 $promiseStatus = promiseStatus; |
| 397 $promiseValue = promiseValue; | 435 $promiseValue = promiseValue; |
| 398 | 436 |
| 399 }) | 437 }) |
| OLD | NEW |