| 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  | 
| (...skipping 22 matching lines...) Expand all  Loading... | 
|   33  |   33  | 
|   34 // Status values: 0 = pending, +1 = resolved, -1 = rejected |   34 // Status values: 0 = pending, +1 = resolved, -1 = rejected | 
|   35 var lastMicrotaskId = 0; |   35 var lastMicrotaskId = 0; | 
|   36  |   36  | 
|   37 function CreateResolvingFunctions(promise) { |   37 function CreateResolvingFunctions(promise) { | 
|   38   var alreadyResolved = false; |   38   var alreadyResolved = false; | 
|   39  |   39  | 
|   40   var resolve = function(value) { |   40   var resolve = function(value) { | 
|   41     if (alreadyResolved === true) return; |   41     if (alreadyResolved === true) return; | 
|   42     alreadyResolved = true; |   42     alreadyResolved = true; | 
|   43     if (value === promise) { |  | 
|   44       return PromiseReject(promise, MakeTypeError(kPromiseCyclic, value)); |  | 
|   45     } |  | 
|   46     PromiseResolve(promise, value); |   43     PromiseResolve(promise, value); | 
|   47   }; |   44   }; | 
|   48  |   45  | 
|   49   var reject = function(reason) { |   46   var reject = function(reason) { | 
|   50     if (alreadyResolved === true) return; |   47     if (alreadyResolved === true) return; | 
|   51     alreadyResolved = true; |   48     alreadyResolved = true; | 
|   52     PromiseReject(promise, reason); |   49     PromiseReject(promise, reason); | 
|   53   }; |   50   }; | 
|   54  |   51  | 
|   55   return { |   52   return { | 
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  109 } |  106 } | 
|  110  |  107  | 
|  111 function PromiseDone(promise, status, value, promiseQueue) { |  108 function PromiseDone(promise, status, value, promiseQueue) { | 
|  112   if (GET_PRIVATE(promise, promiseStatusSymbol) === 0) { |  109   if (GET_PRIVATE(promise, promiseStatusSymbol) === 0) { | 
|  113     var tasks = GET_PRIVATE(promise, promiseQueue); |  110     var tasks = GET_PRIVATE(promise, promiseQueue); | 
|  114     if (tasks.length) PromiseEnqueue(value, tasks, status); |  111     if (tasks.length) PromiseEnqueue(value, tasks, status); | 
|  115     PromiseSet(promise, status, value); |  112     PromiseSet(promise, status, value); | 
|  116   } |  113   } | 
|  117 } |  114 } | 
|  118  |  115  | 
|  119 function PromiseCoerce(constructor, x) { |  | 
|  120   if (!IsPromise(x) && IS_SPEC_OBJECT(x)) { |  | 
|  121     var then; |  | 
|  122     try { |  | 
|  123       then = x.then; |  | 
|  124     } catch(r) { |  | 
|  125       return %_Call(PromiseRejected, constructor, r); |  | 
|  126     } |  | 
|  127     if (IS_CALLABLE(then)) { |  | 
|  128       var deferred = NewPromiseCapability(constructor); |  | 
|  129       try { |  | 
|  130         %_Call(then, x, deferred.resolve, deferred.reject); |  | 
|  131       } catch(r) { |  | 
|  132         deferred.reject(r); |  | 
|  133       } |  | 
|  134       return deferred.promise; |  | 
|  135     } |  | 
|  136   } |  | 
|  137   return x; |  | 
|  138 } |  | 
|  139  |  | 
|  140 function PromiseHandle(value, handler, deferred) { |  116 function PromiseHandle(value, handler, deferred) { | 
|  141   try { |  117   try { | 
|  142     %DebugPushPromise(deferred.promise, PromiseHandle); |  118     %DebugPushPromise(deferred.promise, PromiseHandle); | 
|  143     var result = handler(value); |  119     var result = handler(value); | 
|  144     if (result === deferred.promise) |  120     deferred.resolve(result); | 
|  145       throw MakeTypeError(kPromiseCyclic, result); |  | 
|  146     else if (IsPromise(result)) |  | 
|  147       %_Call(PromiseChain, result, deferred.resolve, deferred.reject); |  | 
|  148     else |  | 
|  149       deferred.resolve(result); |  | 
|  150   } catch (exception) { |  121   } catch (exception) { | 
|  151     try { deferred.reject(exception); } catch (e) { } |  122     try { deferred.reject(exception); } catch (e) { } | 
|  152   } finally { |  123   } finally { | 
|  153     %DebugPopPromise(); |  124     %DebugPopPromise(); | 
|  154   } |  125   } | 
|  155 } |  126 } | 
|  156  |  127  | 
|  157 function PromiseEnqueue(value, tasks, status) { |  128 function PromiseEnqueue(value, tasks, status) { | 
|  158   var id, name, instrumenting = DEBUG_IS_ACTIVE; |  129   var id, name, instrumenting = DEBUG_IS_ACTIVE; | 
|  159   %EnqueueMicrotask(function() { |  130   %EnqueueMicrotask(function() { | 
| (...skipping 26 matching lines...) Expand all  Loading... | 
|  186  |  157  | 
|  187 function IsPromise(x) { |  158 function IsPromise(x) { | 
|  188   return IS_SPEC_OBJECT(x) && HAS_DEFINED_PRIVATE(x, promiseStatusSymbol); |  159   return IS_SPEC_OBJECT(x) && HAS_DEFINED_PRIVATE(x, promiseStatusSymbol); | 
|  189 } |  160 } | 
|  190  |  161  | 
|  191 function PromiseCreate() { |  162 function PromiseCreate() { | 
|  192   return new GlobalPromise(PromiseNopResolver) |  163   return new GlobalPromise(PromiseNopResolver) | 
|  193 } |  164 } | 
|  194  |  165  | 
|  195 function PromiseResolve(promise, x) { |  166 function PromiseResolve(promise, x) { | 
|  196   if (GET_PRIVATE(promise, promiseStatusSymbol) === 0) { |  167   if (x === promise) { | 
|  197     if (IS_SPEC_OBJECT(x)) { |  168     return PromiseReject(promise, MakeTypeError(kPromiseCyclic, x)); | 
|  198       // 25.4.1.3.2 steps 8-12 |  169   } | 
|  199       try { |  170   if (IS_SPEC_OBJECT(x)) { | 
|  200         var then = x.then; |  171     // 25.4.1.3.2 steps 8-12 | 
|  201       } catch (e) { |  172     try { | 
|  202         return PromiseReject(promise, e); |  173       var then = x.then; | 
|  203       } |  174     } catch (e) { | 
|  204       if (IS_CALLABLE(then)) { |  175       return PromiseReject(promise, e); | 
|  205         // PromiseResolveThenableJob |  | 
|  206         return %EnqueueMicrotask(function() { |  | 
|  207           try { |  | 
|  208             var callbacks = CreateResolvingFunctions(promise); |  | 
|  209             %_Call(then, x, callbacks.resolve, callbacks.reject); |  | 
|  210           } catch (e) { |  | 
|  211             PromiseReject(promise, e); |  | 
|  212           } |  | 
|  213         }); |  | 
|  214       } |  | 
|  215     } |  176     } | 
|  216     PromiseDone(promise, +1, x, promiseOnResolveSymbol); |  177     if (IS_CALLABLE(then)) { | 
 |  178       // PromiseResolveThenableJob | 
 |  179       return %EnqueueMicrotask(function() { | 
 |  180         try { | 
 |  181           var callbacks = CreateResolvingFunctions(promise); | 
 |  182           %_Call(then, x, callbacks.resolve, callbacks.reject); | 
 |  183         } catch (e) { | 
 |  184           PromiseReject(promise, e); | 
 |  185         } | 
 |  186       }); | 
 |  187     } | 
|  217   } |  188   } | 
 |  189   PromiseDone(promise, +1, x, promiseOnResolveSymbol); | 
|  218 } |  190 } | 
|  219  |  191  | 
|  220 function PromiseReject(promise, r) { |  192 function PromiseReject(promise, r) { | 
|  221   // Check promise status to confirm that this reject has an effect. |  193   // Check promise status to confirm that this reject has an effect. | 
|  222   // Call runtime for callbacks to the debugger or for unhandled reject. |  194   // Call runtime for callbacks to the debugger or for unhandled reject. | 
|  223   if (GET_PRIVATE(promise, promiseStatusSymbol) == 0) { |  195   if (GET_PRIVATE(promise, promiseStatusSymbol) == 0) { | 
|  224     var debug_is_active = DEBUG_IS_ACTIVE; |  196     var debug_is_active = DEBUG_IS_ACTIVE; | 
|  225     if (debug_is_active || |  197     if (debug_is_active || | 
|  226         !HAS_DEFINED_PRIVATE(promise, promiseHasHandlerSymbol)) { |  198         !HAS_DEFINED_PRIVATE(promise, promiseHasHandlerSymbol)) { | 
|  227       %PromiseRejectEvent(promise, r, debug_is_active); |  199       %PromiseRejectEvent(promise, r, debug_is_active); | 
| (...skipping 10 matching lines...) Expand all  Loading... | 
|  238     var promise = PromiseInit(new GlobalPromise(promiseRawSymbol)); |  210     var promise = PromiseInit(new GlobalPromise(promiseRawSymbol)); | 
|  239     var callbacks = CreateResolvingFunctions(promise); |  211     var callbacks = CreateResolvingFunctions(promise); | 
|  240     return { |  212     return { | 
|  241       promise: promise, |  213       promise: promise, | 
|  242       resolve: callbacks.resolve, |  214       resolve: callbacks.resolve, | 
|  243       reject: callbacks.reject |  215       reject: callbacks.reject | 
|  244     }; |  216     }; | 
|  245   } else { |  217   } else { | 
|  246     var result = {promise: UNDEFINED, resolve: UNDEFINED, reject: UNDEFINED }; |  218     var result = {promise: UNDEFINED, resolve: UNDEFINED, reject: UNDEFINED }; | 
|  247     result.promise = new C(function(resolve, reject) { |  219     result.promise = new C(function(resolve, reject) { | 
 |  220       // TODO(littledan): Check for resolve and reject being not undefined | 
|  248       result.resolve = resolve; |  221       result.resolve = resolve; | 
|  249       result.reject = reject; |  222       result.reject = reject; | 
|  250     }); |  223     }); | 
|  251     return result; |  224     return result; | 
|  252   } |  225   } | 
|  253 } |  226 } | 
|  254  |  227  | 
|  255 function PromiseDeferred() { |  228 function PromiseDeferred() { | 
|  256   return NewPromiseCapability(this); |  229   return NewPromiseCapability(this); | 
|  257 } |  230 } | 
|  258  |  231  | 
|  259 function PromiseResolved(x) { |  232 function PromiseResolved(x) { | 
|  260   if (this === GlobalPromise) { |  233   return %_Call(PromiseCast, this, x); | 
|  261     // Optimized case, avoid extra closure. |  | 
|  262     return PromiseCreateAndSet(+1, x); |  | 
|  263   } else { |  | 
|  264     return new this(function(resolve, reject) { resolve(x) }); |  | 
|  265   } |  | 
|  266 } |  234 } | 
|  267  |  235  | 
|  268 function PromiseRejected(r) { |  236 function PromiseRejected(r) { | 
 |  237   if (!IS_SPEC_OBJECT(this)) { | 
 |  238     throw MakeTypeError(kCalledOnNonObject, PromiseRejected); | 
 |  239   } | 
|  269   var promise; |  240   var promise; | 
|  270   if (this === GlobalPromise) { |  241   if (this === GlobalPromise) { | 
|  271     // Optimized case, avoid extra closure. |  242     // Optimized case, avoid extra closure. | 
|  272     promise = PromiseCreateAndSet(-1, r); |  243     promise = PromiseCreateAndSet(-1, r); | 
|  273     // The debug event for this would always be an uncaught promise reject, |  244     // The debug event for this would always be an uncaught promise reject, | 
|  274     // which is usually simply noise. Do not trigger that debug event. |  245     // which is usually simply noise. Do not trigger that debug event. | 
|  275     %PromiseRejectEvent(promise, r, false); |  246     %PromiseRejectEvent(promise, r, false); | 
|  276   } else { |  247   } else { | 
|  277     promise = new this(function(resolve, reject) { reject(r) }); |  248     promise = new this(function(resolve, reject) { reject(r) }); | 
|  278   } |  249   } | 
|  279   return promise; |  250   return promise; | 
|  280 } |  251 } | 
|  281  |  252  | 
|  282 // Simple chaining. |  253 // Multi-unwrapped chaining with thenable coercion. | 
|  283  |  254  | 
|  284 // PromiseChain a.k.a. flatMap |  255 function PromiseThen(onResolve, onReject) { | 
|  285 function PromiseChainInternal(constructor, onResolve, onReject) { |  256   var constructor = this.constructor; | 
|  286   onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; |  257   onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler; | 
|  287   onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; |  258   onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler; | 
|  288   var deferred = NewPromiseCapability(constructor); |  259   var deferred = NewPromiseCapability(constructor); | 
|  289   switch (GET_PRIVATE(this, promiseStatusSymbol)) { |  260   switch (GET_PRIVATE(this, promiseStatusSymbol)) { | 
|  290     case UNDEFINED: |  261     case UNDEFINED: | 
 |  262       // TODO(littledan): The type check should be called before | 
 |  263       // constructing NewPromiseCapability; this is observable when | 
 |  264       // erroneously copying this method to other classes. | 
|  291       throw MakeTypeError(kNotAPromise, this); |  265       throw MakeTypeError(kNotAPromise, this); | 
|  292     case 0:  // Pending |  266     case 0:  // Pending | 
|  293       GET_PRIVATE(this, promiseOnResolveSymbol).push(onResolve, deferred); |  267       GET_PRIVATE(this, promiseOnResolveSymbol).push(onResolve, deferred); | 
|  294       GET_PRIVATE(this, promiseOnRejectSymbol).push(onReject, deferred); |  268       GET_PRIVATE(this, promiseOnRejectSymbol).push(onReject, deferred); | 
|  295       break; |  269       break; | 
|  296     case +1:  // Resolved |  270     case +1:  // Resolved | 
|  297       PromiseEnqueue(GET_PRIVATE(this, promiseValueSymbol), |  271       PromiseEnqueue(GET_PRIVATE(this, promiseValueSymbol), | 
|  298                      [onResolve, deferred], |  272                      [onResolve, deferred], | 
|  299                      +1); |  273                      +1); | 
|  300       break; |  274       break; | 
|  301     case -1:  // Rejected |  275     case -1:  // Rejected | 
|  302       if (!HAS_DEFINED_PRIVATE(this, promiseHasHandlerSymbol)) { |  276       if (!HAS_DEFINED_PRIVATE(this, promiseHasHandlerSymbol)) { | 
|  303         // Promise has already been rejected, but had no handler. |  277         // Promise has already been rejected, but had no handler. | 
|  304         // Revoke previously triggered reject event. |  278         // Revoke previously triggered reject event. | 
|  305         %PromiseRevokeReject(this); |  279         %PromiseRevokeReject(this); | 
|  306       } |  280       } | 
|  307       PromiseEnqueue(GET_PRIVATE(this, promiseValueSymbol), |  281       PromiseEnqueue(GET_PRIVATE(this, promiseValueSymbol), | 
|  308                      [onReject, deferred], |  282                      [onReject, deferred], | 
|  309                      -1); |  283                      -1); | 
|  310       break; |  284       break; | 
|  311   } |  285   } | 
|  312   // Mark this promise as having handler. |  286   // Mark this promise as having handler. | 
|  313   SET_PRIVATE(this, promiseHasHandlerSymbol, true); |  287   SET_PRIVATE(this, promiseHasHandlerSymbol, true); | 
|  314   if (DEBUG_IS_ACTIVE) { |  288   if (DEBUG_IS_ACTIVE) { | 
|  315     %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); |  289     %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); | 
|  316   } |  290   } | 
|  317   return deferred.promise; |  291   return deferred.promise; | 
|  318 } |  292 } | 
|  319  |  293  | 
 |  294 // Chain is left around for now as an alias for then | 
|  320 function PromiseChain(onResolve, onReject) { |  295 function PromiseChain(onResolve, onReject) { | 
|  321   return %_Call(PromiseChainInternal, this, this.constructor, |  296   return %_Call(PromiseThen, this, onResolve, onReject); | 
|  322                 onResolve, onReject); |  | 
|  323 } |  297 } | 
|  324  |  298  | 
|  325 function PromiseCatch(onReject) { |  299 function PromiseCatch(onReject) { | 
|  326   return this.then(UNDEFINED, onReject); |  300   return this.then(UNDEFINED, onReject); | 
|  327 } |  301 } | 
|  328  |  302  | 
|  329 // Multi-unwrapped chaining with thenable coercion. |  | 
|  330  |  | 
|  331 function PromiseThen(onResolve, onReject) { |  | 
|  332   onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler; |  | 
|  333   onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler; |  | 
|  334   var that = this; |  | 
|  335   var constructor = this.constructor; |  | 
|  336   return %_Call( |  | 
|  337     PromiseChainInternal, |  | 
|  338     this, |  | 
|  339     constructor, |  | 
|  340     function(x) { |  | 
|  341       x = PromiseCoerce(constructor, x); |  | 
|  342       if (x === that) { |  | 
|  343         return onReject(MakeTypeError(kPromiseCyclic, x)); |  | 
|  344       } else if (IsPromise(x)) { |  | 
|  345         return x.then(onResolve, onReject); |  | 
|  346       } else { |  | 
|  347         return onResolve(x); |  | 
|  348       } |  | 
|  349     }, |  | 
|  350     onReject |  | 
|  351   ); |  | 
|  352 } |  | 
|  353  |  | 
|  354 // Combinators. |  303 // Combinators. | 
|  355  |  304  | 
|  356 function PromiseCast(x) { |  305 function PromiseCast(x) { | 
 |  306   if (!IS_SPEC_OBJECT(this)) { | 
 |  307     throw MakeTypeError(kCalledOnNonObject, PromiseCast); | 
 |  308   } | 
|  357   if (IsPromise(x) && x.constructor === this) { |  309   if (IsPromise(x) && x.constructor === this) { | 
|  358     return x; |  310     return x; | 
|  359   } else { |  311   } else { | 
|  360     return new this(function(resolve) { resolve(x) }); |  312     return new this(function(resolve, reject) { resolve(x) }); | 
|  361   } |  313   } | 
|  362 } |  314 } | 
|  363  |  315  | 
|  364 function PromiseAll(iterable) { |  316 function PromiseAll(iterable) { | 
|  365   var deferred = NewPromiseCapability(this); |  317   var deferred = NewPromiseCapability(this); | 
|  366   var resolutions = []; |  318   var resolutions = []; | 
|  367   try { |  319   try { | 
|  368     var count = 0; |  320     var count = 0; | 
|  369     var i = 0; |  321     var i = 0; | 
|  370     for (var value of iterable) { |  322     for (var value of iterable) { | 
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  477 [PromiseChain, PromiseDeferred, PromiseResolved].forEach( |  429 [PromiseChain, PromiseDeferred, PromiseResolved].forEach( | 
|  478     fn => %FunctionRemovePrototype(fn)); |  430     fn => %FunctionRemovePrototype(fn)); | 
|  479  |  431  | 
|  480 utils.Export(function(to) { |  432 utils.Export(function(to) { | 
|  481   to.PromiseChain = PromiseChain; |  433   to.PromiseChain = PromiseChain; | 
|  482   to.PromiseDeferred = PromiseDeferred; |  434   to.PromiseDeferred = PromiseDeferred; | 
|  483   to.PromiseResolved = PromiseResolved; |  435   to.PromiseResolved = PromiseResolved; | 
|  484 }); |  436 }); | 
|  485  |  437  | 
|  486 }) |  438 }) | 
| OLD | NEW |