Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Side by Side Diff: src/promise.js

Issue 622783002: Make PromiseRejectCallback fire after end-of-turn. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/heap/heap.h ('k') | src/runtime/runtime.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 "use strict"; 5 "use strict";
6 6
7 // This file relies on the fact that the following declaration has been made 7 // This file relies on the fact that the following declaration has been made
8 // in runtime.js: 8 // in runtime.js:
9 // var $Object = global.Object 9 // var $Object = global.Object
10 // var $WeakMap = global.WeakMap 10 // var $WeakMap = global.WeakMap
11 11
12 // For bootstrapper. 12 // For bootstrapper.
13 13
14 var IsPromise; 14 var IsPromise;
15 var PromiseCreate; 15 var PromiseCreate;
16 var PromiseResolve; 16 var PromiseResolve;
17 var PromiseReject; 17 var PromiseReject;
18 var PromiseChain; 18 var PromiseChain;
19 var PromiseCatch; 19 var PromiseCatch;
20 var PromiseThen; 20 var PromiseThen;
21 var PromiseHasRejectHandler; 21 var PromiseScheduleUnhandledRejectMessage;
22 var PromiseScheduleRevokeRejectMessage;
23 var PromiseDispatchUnhandledRejectMessages;
22 var PromiseHasUserDefinedRejectHandler; 24 var PromiseHasUserDefinedRejectHandler;
23 25
24 // mirror-debugger.js currently uses builtins.promiseStatus. It would be nice 26 // mirror-debugger.js currently uses builtins.promiseStatus. It would be nice
25 // if we could move these property names into the closure below. 27 // if we could move these property names into the closure below.
26 // TODO(jkummerow/rossberg/yangguo): Find a better solution. 28 // TODO(jkummerow/rossberg/yangguo): Find a better solution.
27 29
28 // Status values: 0 = pending, +1 = resolved, -1 = rejected 30 // Status values: 0 = pending, +1 = resolved, -1 = rejected
29 var promiseStatus = GLOBAL_PRIVATE("Promise#status"); 31 var promiseStatus = GLOBAL_PRIVATE("Promise#status");
30 var promiseValue = GLOBAL_PRIVATE("Promise#value"); 32 var promiseValue = GLOBAL_PRIVATE("Promise#value");
31 var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve"); 33 var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve");
32 var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject"); 34 var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject");
33 var promiseRaw = GLOBAL_PRIVATE("Promise#raw"); 35 var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
34 var promiseHasHandler = %PromiseHasHandlerSymbol(); 36 var promiseHasHandler = GLOBAL_PRIVATE("Promise#hasHandler");
37 // Enqueued promise message: true = revoke reject, false = unhandled reject
38 var promiseMessage = GLOBAL_PRIVATE("Promise#message");
35 var lastMicrotaskId = 0; 39 var lastMicrotaskId = 0;
40 var promiseMicrotasksCount = 0;
41 var enqueuedPromiseMessages;
36 42
37 43
38 (function() { 44 (function() {
39 45
40 var $Promise = function Promise(resolver) { 46 var $Promise = function Promise(resolver) {
41 if (resolver === promiseRaw) return; 47 if (resolver === promiseRaw) return;
42 if (!%_IsConstructCall()) throw MakeTypeError('not_a_promise', [this]); 48 if (!%_IsConstructCall()) throw MakeTypeError('not_a_promise', [this]);
43 if (!IS_SPEC_FUNCTION(resolver)) 49 if (!IS_SPEC_FUNCTION(resolver))
44 throw MakeTypeError('resolver_not_a_function', [resolver]); 50 throw MakeTypeError('resolver_not_a_function', [resolver]);
45 var promise = PromiseInit(this); 51 var promise = PromiseInit(this);
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
112 deferred.resolve(result); 118 deferred.resolve(result);
113 } catch (exception) { 119 } catch (exception) {
114 try { deferred.reject(exception); } catch (e) { } 120 try { deferred.reject(exception); } catch (e) { }
115 } finally { 121 } finally {
116 %DebugPopPromise(); 122 %DebugPopPromise();
117 } 123 }
118 } 124 }
119 125
120 function PromiseEnqueue(value, tasks, status) { 126 function PromiseEnqueue(value, tasks, status) {
121 var id, name, instrumenting = DEBUG_IS_ACTIVE; 127 var id, name, instrumenting = DEBUG_IS_ACTIVE;
128 ++promiseMicrotasksCount;
122 %EnqueueMicrotask(function() { 129 %EnqueueMicrotask(function() {
123 if (instrumenting) { 130 if (instrumenting) {
124 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); 131 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
125 } 132 }
126 for (var i = 0; i < tasks.length; i += 2) { 133 for (var i = 0; i < tasks.length; i += 2) {
127 PromiseHandle(value, tasks[i], tasks[i + 1]) 134 PromiseHandle(value, tasks[i], tasks[i + 1]);
128 } 135 }
129 if (instrumenting) { 136 if (instrumenting) {
130 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); 137 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
131 } 138 }
139 --promiseMicrotasksCount;
132 }); 140 });
133 if (instrumenting) { 141 if (instrumenting) {
134 id = ++lastMicrotaskId; 142 id = ++lastMicrotaskId;
135 name = status > 0 ? "Promise.resolve" : "Promise.reject"; 143 name = status > 0 ? "Promise.resolve" : "Promise.reject";
136 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); 144 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
137 } 145 }
138 } 146 }
139 147
140 function PromiseIdResolveHandler(x) { return x } 148 function PromiseIdResolveHandler(x) { return x }
141 function PromiseIdRejectHandler(r) { throw r } 149 function PromiseIdRejectHandler(r) { throw r }
(...skipping 14 matching lines...) Expand all
156 } 164 }
157 165
158 PromiseResolve = function PromiseResolve(promise, x) { 166 PromiseResolve = function PromiseResolve(promise, x) {
159 PromiseDone(promise, +1, x, promiseOnResolve) 167 PromiseDone(promise, +1, x, promiseOnResolve)
160 } 168 }
161 169
162 PromiseReject = function PromiseReject(promise, r) { 170 PromiseReject = function PromiseReject(promise, r) {
163 // Check promise status to confirm that this reject has an effect. 171 // Check promise status to confirm that this reject has an effect.
164 // Call runtime for callbacks to the debugger or for unhandled reject. 172 // Call runtime for callbacks to the debugger or for unhandled reject.
165 if (GET_PRIVATE(promise, promiseStatus) == 0) { 173 if (GET_PRIVATE(promise, promiseStatus) == 0) {
166 var debug_is_active = DEBUG_IS_ACTIVE; 174 if (DEBUG_IS_ACTIVE) {
167 if (debug_is_active || !HAS_DEFINED_PRIVATE(promise, promiseHasHandler)) { 175 %DebugPromiseRejectEvent(promise, r);
168 %PromiseRejectEvent(promise, r, debug_is_active); 176 }
177 if (!HAS_DEFINED_PRIVATE(promise, promiseHasHandler)) {
178 PromiseScheduleUnhandledRejectMessage(promise, r, false);
169 } 179 }
170 } 180 }
171 PromiseDone(promise, -1, r, promiseOnReject) 181 PromiseDone(promise, -1, r, promiseOnReject)
172 } 182 }
173 183
174 // Convenience. 184 // Convenience.
175 185
176 function PromiseDeferred() { 186 function PromiseDeferred() {
177 if (this === $Promise) { 187 if (this === $Promise) {
178 // Optimized case, avoid extra closure. 188 // Optimized case, avoid extra closure.
(...skipping 20 matching lines...) Expand all
199 } else { 209 } else {
200 return new this(function(resolve, reject) { resolve(x) }); 210 return new this(function(resolve, reject) { resolve(x) });
201 } 211 }
202 } 212 }
203 213
204 function PromiseRejected(r) { 214 function PromiseRejected(r) {
205 var promise; 215 var promise;
206 if (this === $Promise) { 216 if (this === $Promise) {
207 // Optimized case, avoid extra closure. 217 // Optimized case, avoid extra closure.
208 promise = PromiseSet(new $Promise(promiseRaw), -1, r); 218 promise = PromiseSet(new $Promise(promiseRaw), -1, r);
209 // The debug event for this would always be an uncaught promise reject, 219 PromiseScheduleUnhandledRejectMessage(promise, r, false);
210 // which is usually simply noise. Do not trigger that debug event.
211 %PromiseRejectEvent(promise, r, false);
212 } else { 220 } else {
213 promise = new this(function(resolve, reject) { reject(r) }); 221 promise = new this(function(resolve, reject) { reject(r) });
214 } 222 }
215 return promise; 223 return promise;
216 } 224 }
217 225
218 // Simple chaining. 226 // Simple chaining.
219 227
220 PromiseChain = function PromiseChain(onResolve, onReject) { // a.k.a. 228 PromiseChain = function PromiseChain(onResolve, onReject) { // a.k.a.
221 // flatMap 229 // flatMap
222 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; 230 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
223 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; 231 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
224 var deferred = %_CallFunction(this.constructor, PromiseDeferred); 232 var deferred = %_CallFunction(this.constructor, PromiseDeferred);
225 switch (GET_PRIVATE(this, promiseStatus)) { 233 switch (GET_PRIVATE(this, promiseStatus)) {
226 case UNDEFINED: 234 case UNDEFINED:
227 throw MakeTypeError('not_a_promise', [this]); 235 throw MakeTypeError('not_a_promise', [this]);
228 case 0: // Pending 236 case 0: // Pending
229 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred); 237 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred);
230 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred); 238 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred);
231 break; 239 break;
232 case +1: // Resolved 240 case +1: // Resolved
233 PromiseEnqueue(GET_PRIVATE(this, promiseValue), 241 PromiseEnqueue(GET_PRIVATE(this, promiseValue),
234 [onResolve, deferred], 242 [onResolve, deferred],
235 +1); 243 +1);
236 break; 244 break;
237 case -1: // Rejected 245 case -1: // Rejected
238 if (!HAS_DEFINED_PRIVATE(this, promiseHasHandler)) { 246 if (!HAS_DEFINED_PRIVATE(this, promiseHasHandler)) {
239 // Promise has already been rejected, but had no handler. 247 // Promise has already been rejected, but had no handler.
240 // Revoke previously triggered reject event. 248 // Revoke previously triggered reject message.
241 %PromiseRevokeReject(this); 249 PromiseScheduleRevokeRejectMessage(this);
242 } 250 }
243 PromiseEnqueue(GET_PRIVATE(this, promiseValue), 251 PromiseEnqueue(GET_PRIVATE(this, promiseValue),
244 [onReject, deferred], 252 [onReject, deferred],
245 -1); 253 -1);
246 break; 254 break;
247 } 255 }
248 // Mark this promise as having handler. 256 // Mark this promise as having handler.
249 SET_PRIVATE(this, promiseHasHandler, true); 257 SET_PRIVATE(this, promiseHasHandler, true);
250 if (DEBUG_IS_ACTIVE) { 258 if (DEBUG_IS_ACTIVE) {
251 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); 259 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this });
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
333 } 341 }
334 } catch (e) { 342 } catch (e) {
335 deferred.reject(e) 343 deferred.reject(e)
336 } 344 }
337 return deferred.promise; 345 return deferred.promise;
338 } 346 }
339 347
340 348
341 // Utility for debugger 349 // Utility for debugger
342 350
351 function PromiseScheduleUnhandledRejectMessage(promise, r, revoke) {
352 if (!enqueuedPromiseMessages) {
353 enqueuedPromiseMessages = new InternalArray;
354 %EnqueueMicrotask(PromiseDispatchUnhandledRejectMessages);
355 }
356 SET_PRIVATE(promise, promiseMessage, revoke);
357 enqueuedPromiseMessages.push(promise, r);
358 }
359
360 function PromiseScheduleRevokeRejectMessage(promise) {
361 if (HAS_DEFINED_PRIVATE(promise, promiseMessage)) {
362 DELETE_PRIVATE(promise, promiseMessage);
363 } else {
364 PromiseScheduleUnhandledRejectMessage(promise, UNDEFINED, true);
365 }
366 }
367
368 function PromiseDispatchUnhandledRejectMessages() {
369 if (promiseMicrotasksCount || %PendingMicrotaskCount()) {
370 %EnqueueMicrotask(PromiseDispatchUnhandledRejectMessages);
371 return;
372 }
373 var queue = enqueuedPromiseMessages;
374 enqueuedPromiseMessages = UNDEFINED;
375 for (var i = 0; i < queue.length; i += 2) {
376 var promise = queue[i];
377 if (HAS_DEFINED_PRIVATE(promise, promiseMessage)) {
378 var revoke = GET_PRIVATE(promise, promiseMessage);
379 DELETE_PRIVATE(promise, promiseMessage);
380 if (revoke) %PromiseRevokeRejectMessage(promise);
381 else %PromiseRejectMessage(promise, queue[i + 1]);
382 }
383 }
384 }
385
343 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { 386 function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
344 var queue = GET_PRIVATE(promise, promiseOnReject); 387 var queue = GET_PRIVATE(promise, promiseOnReject);
345 if (IS_UNDEFINED(queue)) return false; 388 if (IS_UNDEFINED(queue)) return false;
346 for (var i = 0; i < queue.length; i += 2) { 389 for (var i = 0; i < queue.length; i += 2) {
347 if (queue[i] != PromiseIdRejectHandler) return true; 390 if (queue[i] != PromiseIdRejectHandler) return true;
348 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) { 391 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) {
349 return true; 392 return true;
350 } 393 }
351 } 394 }
352 return false; 395 return false;
(...skipping 20 matching lines...) Expand all
373 "race", PromiseOne, 416 "race", PromiseOne,
374 "resolve", PromiseCast 417 "resolve", PromiseCast
375 ]); 418 ]);
376 InstallFunctions($Promise.prototype, DONT_ENUM, [ 419 InstallFunctions($Promise.prototype, DONT_ENUM, [
377 "chain", PromiseChain, 420 "chain", PromiseChain,
378 "then", PromiseThen, 421 "then", PromiseThen,
379 "catch", PromiseCatch 422 "catch", PromiseCatch
380 ]); 423 ]);
381 424
382 })(); 425 })();
OLDNEW
« no previous file with comments | « src/heap/heap.h ('k') | src/runtime/runtime.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698