| 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 $promiseHasUserDefinedRejectHandler; | |
| 6 var $promiseStatus; | |
| 7 var $promiseValue; | |
| 8 | |
| 9 (function(global, utils) { | 5 (function(global, utils) { |
| 10 | 6 |
| 11 "use strict"; | 7 "use strict"; |
| 12 | 8 |
| 13 %CheckIsBootstrapping(); | 9 %CheckIsBootstrapping(); |
| 14 | 10 |
| 15 // ------------------------------------------------------------------- | 11 // ------------------------------------------------------------------- |
| 16 // Imports | 12 // Imports |
| 17 | 13 |
| 18 var InternalArray = utils.InternalArray; | 14 var InternalArray = utils.InternalArray; |
| 15 var PromiseHasHandlerSymbol = |
| 16 utils.GetPrivateSymbol("promise_has_handler_symbol"); |
| 17 var PromiseOnRejectSymbol = utils.GetPrivateSymbol("promise_on_reject_symbol"); |
| 18 var PromiseOnResolveSymbol = |
| 19 utils.GetPrivateSymbol("promise_on_resolve_symbol"); |
| 20 var PromiseRawSymbol = utils.GetPrivateSymbol("promise_raw_symbol"); |
| 21 var PromiseStatusSymbol = utils.GetPrivateSymbol("promise_status_symbol"); |
| 22 var PromiseValueSymbol = utils.GetPrivateSymbol("promise_value_symbol"); |
| 19 | 23 |
| 20 // ------------------------------------------------------------------- | 24 // ------------------------------------------------------------------- |
| 21 | 25 |
| 22 // Status values: 0 = pending, +1 = resolved, -1 = rejected | 26 // Status values: 0 = pending, +1 = resolved, -1 = rejected |
| 23 var promiseStatus = GLOBAL_PRIVATE("Promise#status"); | |
| 24 var promiseValue = GLOBAL_PRIVATE("Promise#value"); | |
| 25 var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve"); | |
| 26 var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject"); | |
| 27 var promiseRaw = GLOBAL_PRIVATE("Promise#raw"); | |
| 28 var promiseHasHandler = %PromiseHasHandlerSymbol(); | |
| 29 var lastMicrotaskId = 0; | 27 var lastMicrotaskId = 0; |
| 30 | 28 |
| 31 var GlobalPromise = function Promise(resolver) { | 29 var GlobalPromise = function Promise(resolver) { |
| 32 if (resolver === promiseRaw) return; | 30 if (resolver === PromiseRawSymbol) return; |
| 33 if (!%_IsConstructCall()) throw MakeTypeError(kNotAPromise, this); | 31 if (!%_IsConstructCall()) throw MakeTypeError(kNotAPromise, this); |
| 34 if (!IS_SPEC_FUNCTION(resolver)) | 32 if (!IS_SPEC_FUNCTION(resolver)) |
| 35 throw MakeTypeError(kResolverNotAFunction, resolver); | 33 throw MakeTypeError(kResolverNotAFunction, resolver); |
| 36 var promise = PromiseInit(this); | 34 var promise = PromiseInit(this); |
| 37 try { | 35 try { |
| 38 %DebugPushPromise(promise, Promise); | 36 %DebugPushPromise(promise, Promise); |
| 39 resolver(function(x) { PromiseResolve(promise, x) }, | 37 resolver(function(x) { PromiseResolve(promise, x) }, |
| 40 function(r) { PromiseReject(promise, r) }); | 38 function(r) { PromiseReject(promise, r) }); |
| 41 } catch (e) { | 39 } catch (e) { |
| 42 PromiseReject(promise, e); | 40 PromiseReject(promise, e); |
| 43 } finally { | 41 } finally { |
| 44 %DebugPopPromise(); | 42 %DebugPopPromise(); |
| 45 } | 43 } |
| 46 } | 44 } |
| 47 | 45 |
| 48 // Core functionality. | 46 // Core functionality. |
| 49 | 47 |
| 50 function PromiseSet(promise, status, value, onResolve, onReject) { | 48 function PromiseSet(promise, status, value, onResolve, onReject) { |
| 51 SET_PRIVATE(promise, promiseStatus, status); | 49 SET_PRIVATE(promise, PromiseStatusSymbol, status); |
| 52 SET_PRIVATE(promise, promiseValue, value); | 50 SET_PRIVATE(promise, PromiseValueSymbol, value); |
| 53 SET_PRIVATE(promise, promiseOnResolve, onResolve); | 51 SET_PRIVATE(promise, PromiseOnResolveSymbol, onResolve); |
| 54 SET_PRIVATE(promise, promiseOnReject, onReject); | 52 SET_PRIVATE(promise, PromiseOnRejectSymbol, onReject); |
| 55 if (DEBUG_IS_ACTIVE) { | 53 if (DEBUG_IS_ACTIVE) { |
| 56 %DebugPromiseEvent({ promise: promise, status: status, value: value }); | 54 %DebugPromiseEvent({ promise: promise, status: status, value: value }); |
| 57 } | 55 } |
| 58 return promise; | 56 return promise; |
| 59 } | 57 } |
| 60 | 58 |
| 61 function PromiseCreateAndSet(status, value) { | 59 function PromiseCreateAndSet(status, value) { |
| 62 var promise = new GlobalPromise(promiseRaw); | 60 var promise = new GlobalPromise(PromiseRawSymbol); |
| 63 // If debug is active, notify about the newly created promise first. | 61 // If debug is active, notify about the newly created promise first. |
| 64 if (DEBUG_IS_ACTIVE) PromiseSet(promise, 0, UNDEFINED); | 62 if (DEBUG_IS_ACTIVE) PromiseSet(promise, 0, UNDEFINED); |
| 65 return PromiseSet(promise, status, value); | 63 return PromiseSet(promise, status, value); |
| 66 } | 64 } |
| 67 | 65 |
| 68 function PromiseInit(promise) { | 66 function PromiseInit(promise) { |
| 69 return PromiseSet( | 67 return PromiseSet( |
| 70 promise, 0, UNDEFINED, new InternalArray, new InternalArray) | 68 promise, 0, UNDEFINED, new InternalArray, new InternalArray) |
| 71 } | 69 } |
| 72 | 70 |
| 73 function PromiseDone(promise, status, value, promiseQueue) { | 71 function PromiseDone(promise, status, value, promiseQueue) { |
| 74 if (GET_PRIVATE(promise, promiseStatus) === 0) { | 72 if (GET_PRIVATE(promise, PromiseStatusSymbol) === 0) { |
| 75 var tasks = GET_PRIVATE(promise, promiseQueue); | 73 var tasks = GET_PRIVATE(promise, promiseQueue); |
| 76 if (tasks.length) PromiseEnqueue(value, tasks, status); | 74 if (tasks.length) PromiseEnqueue(value, tasks, status); |
| 77 PromiseSet(promise, status, value); | 75 PromiseSet(promise, status, value); |
| 78 } | 76 } |
| 79 } | 77 } |
| 80 | 78 |
| 81 function PromiseCoerce(constructor, x) { | 79 function PromiseCoerce(constructor, x) { |
| 82 if (!IsPromise(x) && IS_SPEC_OBJECT(x)) { | 80 if (!IsPromise(x) && IS_SPEC_OBJECT(x)) { |
| 83 var then; | 81 var then; |
| 84 try { | 82 try { |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 141 function PromiseIdRejectHandler(r) { throw r } | 139 function PromiseIdRejectHandler(r) { throw r } |
| 142 | 140 |
| 143 function PromiseNopResolver() {} | 141 function PromiseNopResolver() {} |
| 144 | 142 |
| 145 // ------------------------------------------------------------------- | 143 // ------------------------------------------------------------------- |
| 146 // Define exported functions. | 144 // Define exported functions. |
| 147 | 145 |
| 148 // For bootstrapper. | 146 // For bootstrapper. |
| 149 | 147 |
| 150 function IsPromise(x) { | 148 function IsPromise(x) { |
| 151 return IS_SPEC_OBJECT(x) && HAS_DEFINED_PRIVATE(x, promiseStatus); | 149 return IS_SPEC_OBJECT(x) && HAS_DEFINED_PRIVATE(x, PromiseStatusSymbol); |
| 152 } | 150 } |
| 153 | 151 |
| 154 function PromiseCreate() { | 152 function PromiseCreate() { |
| 155 return new GlobalPromise(PromiseNopResolver) | 153 return new GlobalPromise(PromiseNopResolver) |
| 156 } | 154 } |
| 157 | 155 |
| 158 function PromiseResolve(promise, x) { | 156 function PromiseResolve(promise, x) { |
| 159 PromiseDone(promise, +1, x, promiseOnResolve) | 157 PromiseDone(promise, +1, x, PromiseOnResolveSymbol) |
| 160 } | 158 } |
| 161 | 159 |
| 162 function PromiseReject(promise, r) { | 160 function PromiseReject(promise, r) { |
| 163 // Check promise status to confirm that this reject has an effect. | 161 // Check promise status to confirm that this reject has an effect. |
| 164 // Call runtime for callbacks to the debugger or for unhandled reject. | 162 // Call runtime for callbacks to the debugger or for unhandled reject. |
| 165 if (GET_PRIVATE(promise, promiseStatus) == 0) { | 163 if (GET_PRIVATE(promise, PromiseStatusSymbol) == 0) { |
| 166 var debug_is_active = DEBUG_IS_ACTIVE; | 164 var debug_is_active = DEBUG_IS_ACTIVE; |
| 167 if (debug_is_active || !HAS_DEFINED_PRIVATE(promise, promiseHasHandler)) { | 165 if (debug_is_active || |
| 166 !HAS_DEFINED_PRIVATE(promise, PromiseHasHandlerSymbol)) { |
| 168 %PromiseRejectEvent(promise, r, debug_is_active); | 167 %PromiseRejectEvent(promise, r, debug_is_active); |
| 169 } | 168 } |
| 170 } | 169 } |
| 171 PromiseDone(promise, -1, r, promiseOnReject) | 170 PromiseDone(promise, -1, r, PromiseOnRejectSymbol) |
| 172 } | 171 } |
| 173 | 172 |
| 174 // Convenience. | 173 // Convenience. |
| 175 | 174 |
| 176 function PromiseDeferred() { | 175 function PromiseDeferred() { |
| 177 if (this === GlobalPromise) { | 176 if (this === GlobalPromise) { |
| 178 // Optimized case, avoid extra closure. | 177 // Optimized case, avoid extra closure. |
| 179 var promise = PromiseInit(new GlobalPromise(promiseRaw)); | 178 var promise = PromiseInit(new GlobalPromise(PromiseRawSymbol)); |
| 180 return { | 179 return { |
| 181 promise: promise, | 180 promise: promise, |
| 182 resolve: function(x) { PromiseResolve(promise, x) }, | 181 resolve: function(x) { PromiseResolve(promise, x) }, |
| 183 reject: function(r) { PromiseReject(promise, r) } | 182 reject: function(r) { PromiseReject(promise, r) } |
| 184 }; | 183 }; |
| 185 } else { | 184 } else { |
| 186 var result = {promise: UNDEFINED, reject: UNDEFINED, resolve: UNDEFINED}; | 185 var result = {promise: UNDEFINED, reject: UNDEFINED, resolve: UNDEFINED}; |
| 187 result.promise = new this(function(resolve, reject) { | 186 result.promise = new this(function(resolve, reject) { |
| 188 result.resolve = resolve; | 187 result.resolve = resolve; |
| 189 result.reject = reject; | 188 result.reject = reject; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 214 } | 213 } |
| 215 return promise; | 214 return promise; |
| 216 } | 215 } |
| 217 | 216 |
| 218 // Simple chaining. | 217 // Simple chaining. |
| 219 | 218 |
| 220 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap | 219 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap |
| 221 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; | 220 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; |
| 222 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; | 221 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; |
| 223 var deferred = %_CallFunction(this.constructor, PromiseDeferred); | 222 var deferred = %_CallFunction(this.constructor, PromiseDeferred); |
| 224 switch (GET_PRIVATE(this, promiseStatus)) { | 223 switch (GET_PRIVATE(this, PromiseStatusSymbol)) { |
| 225 case UNDEFINED: | 224 case UNDEFINED: |
| 226 throw MakeTypeError(kNotAPromise, this); | 225 throw MakeTypeError(kNotAPromise, this); |
| 227 case 0: // Pending | 226 case 0: // Pending |
| 228 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred); | 227 GET_PRIVATE(this, PromiseOnResolveSymbol).push(onResolve, deferred); |
| 229 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred); | 228 GET_PRIVATE(this, PromiseOnRejectSymbol).push(onReject, deferred); |
| 230 break; | 229 break; |
| 231 case +1: // Resolved | 230 case +1: // Resolved |
| 232 PromiseEnqueue(GET_PRIVATE(this, promiseValue), | 231 PromiseEnqueue(GET_PRIVATE(this, PromiseValueSymbol), |
| 233 [onResolve, deferred], | 232 [onResolve, deferred], |
| 234 +1); | 233 +1); |
| 235 break; | 234 break; |
| 236 case -1: // Rejected | 235 case -1: // Rejected |
| 237 if (!HAS_DEFINED_PRIVATE(this, promiseHasHandler)) { | 236 if (!HAS_DEFINED_PRIVATE(this, PromiseHasHandlerSymbol)) { |
| 238 // Promise has already been rejected, but had no handler. | 237 // Promise has already been rejected, but had no handler. |
| 239 // Revoke previously triggered reject event. | 238 // Revoke previously triggered reject event. |
| 240 %PromiseRevokeReject(this); | 239 %PromiseRevokeReject(this); |
| 241 } | 240 } |
| 242 PromiseEnqueue(GET_PRIVATE(this, promiseValue), | 241 PromiseEnqueue(GET_PRIVATE(this, PromiseValueSymbol), |
| 243 [onReject, deferred], | 242 [onReject, deferred], |
| 244 -1); | 243 -1); |
| 245 break; | 244 break; |
| 246 } | 245 } |
| 247 // Mark this promise as having handler. | 246 // Mark this promise as having handler. |
| 248 SET_PRIVATE(this, promiseHasHandler, true); | 247 SET_PRIVATE(this, PromiseHasHandlerSymbol, true); |
| 249 if (DEBUG_IS_ACTIVE) { | 248 if (DEBUG_IS_ACTIVE) { |
| 250 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); | 249 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); |
| 251 } | 250 } |
| 252 return deferred.promise; | 251 return deferred.promise; |
| 253 } | 252 } |
| 254 | 253 |
| 255 function PromiseCatch(onReject) { | 254 function PromiseCatch(onReject) { |
| 256 return this.then(UNDEFINED, onReject); | 255 return this.then(UNDEFINED, onReject); |
| 257 } | 256 } |
| 258 | 257 |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 333 } catch (e) { | 332 } catch (e) { |
| 334 deferred.reject(e) | 333 deferred.reject(e) |
| 335 } | 334 } |
| 336 return deferred.promise; | 335 return deferred.promise; |
| 337 } | 336 } |
| 338 | 337 |
| 339 | 338 |
| 340 // Utility for debugger | 339 // Utility for debugger |
| 341 | 340 |
| 342 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { | 341 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { |
| 343 var queue = GET_PRIVATE(promise, promiseOnReject); | 342 var queue = GET_PRIVATE(promise, PromiseOnRejectSymbol); |
| 344 if (IS_UNDEFINED(queue)) return false; | 343 if (IS_UNDEFINED(queue)) return false; |
| 345 for (var i = 0; i < queue.length; i += 2) { | 344 for (var i = 0; i < queue.length; i += 2) { |
| 346 if (queue[i] != PromiseIdRejectHandler) return true; | 345 if (queue[i] != PromiseIdRejectHandler) return true; |
| 347 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) { | 346 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) { |
| 348 return true; | 347 return true; |
| 349 } | 348 } |
| 350 } | 349 } |
| 351 return false; | 350 return false; |
| 352 } | 351 } |
| 353 | 352 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 373 "race", PromiseRace, | 372 "race", PromiseRace, |
| 374 "resolve", PromiseCast | 373 "resolve", PromiseCast |
| 375 ]); | 374 ]); |
| 376 | 375 |
| 377 utils.InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [ | 376 utils.InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [ |
| 378 "chain", PromiseChain, | 377 "chain", PromiseChain, |
| 379 "then", PromiseThen, | 378 "then", PromiseThen, |
| 380 "catch", PromiseCatch | 379 "catch", PromiseCatch |
| 381 ]); | 380 ]); |
| 382 | 381 |
| 383 $promiseStatus = promiseStatus; | |
| 384 $promiseValue = promiseValue; | |
| 385 | |
| 386 utils.ExportToRuntime(function(to) { | 382 utils.ExportToRuntime(function(to) { |
| 387 to.promiseStatus = promiseStatus; | |
| 388 to.promiseValue = promiseValue; | |
| 389 to.PromiseCreate = PromiseCreate; | 383 to.PromiseCreate = PromiseCreate; |
| 390 to.PromiseResolve = PromiseResolve; | 384 to.PromiseResolve = PromiseResolve; |
| 391 to.PromiseReject = PromiseReject; | 385 to.PromiseReject = PromiseReject; |
| 392 to.PromiseChain = PromiseChain; | 386 to.PromiseChain = PromiseChain; |
| 393 to.PromiseCatch = PromiseCatch; | 387 to.PromiseCatch = PromiseCatch; |
| 394 to.PromiseThen = PromiseThen; | 388 to.PromiseThen = PromiseThen; |
| 395 to.PromiseHasUserDefinedRejectHandler = PromiseHasUserDefinedRejectHandler; | 389 to.PromiseHasUserDefinedRejectHandler = PromiseHasUserDefinedRejectHandler; |
| 396 }); | 390 }); |
| 397 | 391 |
| 398 }) | 392 }) |
| OLD | NEW |