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 |