| 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, thenable) { | 108 function PromiseHandle(value, handler, deferred) { |
| 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) && thenable) { | 115 else if (IsPromise(result)) |
| 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)) { | |
| 123 %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain); | 116 %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain); |
| 124 } else { | 117 else |
| 125 deferred.resolve(result); | 118 deferred.resolve(result); |
| 126 } | |
| 127 } catch (exception) { | 119 } catch (exception) { |
| 128 try { deferred.reject(exception); } catch (e) { } | 120 try { deferred.reject(exception); } catch (e) { } |
| 129 } finally { | 121 } finally { |
| 130 %DebugPopPromise(); | 122 %DebugPopPromise(); |
| 131 } | 123 } |
| 132 } | 124 } |
| 133 | 125 |
| 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 | |
| 156 function PromiseEnqueue(value, tasks, status) { | 126 function PromiseEnqueue(value, tasks, status) { |
| 157 var id, name, instrumenting = DEBUG_IS_ACTIVE; | 127 var id, name, instrumenting = DEBUG_IS_ACTIVE; |
| 158 %EnqueueMicrotask(function() { | 128 %EnqueueMicrotask(function() { |
| 159 if (instrumenting) { | 129 if (instrumenting) { |
| 160 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); | 130 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); |
| 161 } | 131 } |
| 162 for (var i = 0; i < tasks.length; i += 3) { | 132 for (var i = 0; i < tasks.length; i += 2) { |
| 163 // tasks[i: i + 2] consists of the reaction handler, the associated | 133 PromiseHandle(value, tasks[i], tasks[i + 1]) |
| 164 // deferred object and whether the thenable handling is required. | |
| 165 PromiseHandle(value, tasks[i], tasks[i + 1], tasks[i + 2]) | |
| 166 } | 134 } |
| 167 if (instrumenting) { | 135 if (instrumenting) { |
| 168 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); | 136 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); |
| 169 } | 137 } |
| 170 }); | 138 }); |
| 171 if (instrumenting) { | 139 if (instrumenting) { |
| 172 id = ++lastMicrotaskId; | 140 id = ++lastMicrotaskId; |
| 173 name = status > 0 ? "Promise.resolve" : "Promise.reject"; | 141 name = status > 0 ? "Promise.resolve" : "Promise.reject"; |
| 174 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); | 142 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); |
| 175 } | 143 } |
| 176 } | 144 } |
| 177 | 145 |
| 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 | |
| 213 function PromiseIdResolveHandler(x) { return x } | 146 function PromiseIdResolveHandler(x) { return x } |
| 214 function PromiseIdRejectHandler(r) { throw r } | 147 function PromiseIdRejectHandler(r) { throw r } |
| 215 | 148 |
| 216 function PromiseNopResolver() {} | 149 function PromiseNopResolver() {} |
| 217 | 150 |
| 218 // ------------------------------------------------------------------- | 151 // ------------------------------------------------------------------- |
| 219 // Define exported functions. | 152 // Define exported functions. |
| 220 | 153 |
| 221 // For bootstrapper. | 154 // For bootstrapper. |
| 222 | 155 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 284 %PromiseRejectEvent(promise, r, false); | 217 %PromiseRejectEvent(promise, r, false); |
| 285 } else { | 218 } else { |
| 286 promise = new this(function(resolve, reject) { reject(r) }); | 219 promise = new this(function(resolve, reject) { reject(r) }); |
| 287 } | 220 } |
| 288 return promise; | 221 return promise; |
| 289 } | 222 } |
| 290 | 223 |
| 291 // Simple chaining. | 224 // Simple chaining. |
| 292 | 225 |
| 293 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap | 226 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap |
| 294 return %_CallFunction(this, onResolve, onReject, false, | 227 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; |
| 295 PromiseChainInternal); | 228 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; |
| 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; |
| 296 } | 259 } |
| 297 | 260 |
| 298 function PromiseCatch(onReject) { | 261 function PromiseCatch(onReject) { |
| 299 return this.then(UNDEFINED, onReject); | 262 return this.then(UNDEFINED, onReject); |
| 300 } | 263 } |
| 301 | 264 |
| 302 // Multi-unwrapped chaining with thenable coercion. | 265 // Multi-unwrapped chaining with thenable coercion. |
| 303 | 266 |
| 304 function PromiseThen(onResolve, onReject) { | 267 function PromiseThen(onResolve, onReject) { |
| 305 onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve | 268 onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve |
| (...skipping 10 matching lines...) Expand all Loading... |
| 316 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject); | 279 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject); |
| 317 return onReject(MakeTypeError(kPromiseCyclic, x)); | 280 return onReject(MakeTypeError(kPromiseCyclic, x)); |
| 318 } else if (IsPromise(x)) { | 281 } else if (IsPromise(x)) { |
| 319 return x.then(onResolve, onReject); | 282 return x.then(onResolve, onReject); |
| 320 } else { | 283 } else { |
| 321 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve); | 284 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve); |
| 322 return onResolve(x); | 285 return onResolve(x); |
| 323 } | 286 } |
| 324 }, | 287 }, |
| 325 onReject, | 288 onReject, |
| 326 true, | 289 PromiseChain |
| 327 PromiseChainInternal | |
| 328 ); | 290 ); |
| 329 } | 291 } |
| 330 | 292 |
| 331 // Combinators. | 293 // Combinators. |
| 332 | 294 |
| 333 function PromiseCast(x) { | 295 function PromiseCast(x) { |
| 334 // TODO(rossberg): cannot do better until we support @@create. | 296 // TODO(rossberg): cannot do better until we support @@create. |
| 335 return IsPromise(x) ? x : new this(function(resolve) { resolve(x) }); | 297 return IsPromise(x) ? x : new this(function(resolve) { resolve(x) }); |
| 336 } | 298 } |
| 337 | 299 |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 379 } | 341 } |
| 380 return deferred.promise; | 342 return deferred.promise; |
| 381 } | 343 } |
| 382 | 344 |
| 383 | 345 |
| 384 // Utility for debugger | 346 // Utility for debugger |
| 385 | 347 |
| 386 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { | 348 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { |
| 387 var queue = GET_PRIVATE(promise, promiseOnReject); | 349 var queue = GET_PRIVATE(promise, promiseOnReject); |
| 388 if (IS_UNDEFINED(queue)) return false; | 350 if (IS_UNDEFINED(queue)) return false; |
| 389 for (var i = 0; i < queue.length; i += 3) { | 351 for (var i = 0; i < queue.length; i += 2) { |
| 390 if (queue[i] != PromiseIdRejectHandler) return true; | 352 if (queue[i] != PromiseIdRejectHandler) return true; |
| 391 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) { | 353 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) { |
| 392 return true; | 354 return true; |
| 393 } | 355 } |
| 394 } | 356 } |
| 395 return false; | 357 return false; |
| 396 } | 358 } |
| 397 | 359 |
| 398 // Return whether the promise will be handled by a user-defined reject | 360 // Return whether the promise will be handled by a user-defined reject |
| 399 // handler somewhere down the promise chain. For this, we do a depth-first | 361 // handler somewhere down the promise chain. For this, we do a depth-first |
| (...skipping 28 matching lines...) Expand all Loading... |
| 428 $promiseResolve = PromiseResolve; | 390 $promiseResolve = PromiseResolve; |
| 429 $promiseReject = PromiseReject; | 391 $promiseReject = PromiseReject; |
| 430 $promiseChain = PromiseChain; | 392 $promiseChain = PromiseChain; |
| 431 $promiseCatch = PromiseCatch; | 393 $promiseCatch = PromiseCatch; |
| 432 $promiseThen = PromiseThen; | 394 $promiseThen = PromiseThen; |
| 433 $promiseHasUserDefinedRejectHandler = PromiseHasUserDefinedRejectHandler; | 395 $promiseHasUserDefinedRejectHandler = PromiseHasUserDefinedRejectHandler; |
| 434 $promiseStatus = promiseStatus; | 396 $promiseStatus = promiseStatus; |
| 435 $promiseValue = promiseValue; | 397 $promiseValue = promiseValue; |
| 436 | 398 |
| 437 }) | 399 }) |
| OLD | NEW |