blob: 5e8c460ddfcaedfc7582b846c90e0f54b7f53ac8 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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
3// found in the LICENSE file.
4
5(function(global, utils, extrasUtils) {
6
7"use strict";
8
9%CheckIsBootstrapping();
10
11// -------------------------------------------------------------------
12// Imports
13
14var InternalArray = utils.InternalArray;
15var MakeTypeError;
16var promiseCombinedDeferredSymbol =
17 utils.ImportNow("promise_combined_deferred_symbol");
18var promiseHasHandlerSymbol =
19 utils.ImportNow("promise_has_handler_symbol");
Ben Murdochc5610432016-08-08 18:44:38 +010020var promiseRejectReactionsSymbol =
21 utils.ImportNow("promise_reject_reactions_symbol");
22var promiseFulfillReactionsSymbol =
23 utils.ImportNow("promise_fulfill_reactions_symbol");
Ben Murdoch61f157c2016-09-16 13:49:30 +010024var promiseDeferredReactionsSymbol =
25 utils.ImportNow("promise_deferred_reactions_symbol");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000026var promiseRawSymbol = utils.ImportNow("promise_raw_symbol");
Ben Murdochc5610432016-08-08 18:44:38 +010027var promiseStateSymbol = utils.ImportNow("promise_state_symbol");
28var promiseResultSymbol = utils.ImportNow("promise_result_symbol");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000029var SpeciesConstructor;
Ben Murdoch61f157c2016-09-16 13:49:30 +010030var speciesSymbol = utils.ImportNow("species_symbol");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000031var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
32
33utils.Import(function(from) {
34 MakeTypeError = from.MakeTypeError;
35 SpeciesConstructor = from.SpeciesConstructor;
36});
37
38// -------------------------------------------------------------------
39
Ben Murdochc5610432016-08-08 18:44:38 +010040// [[PromiseState]] values:
41const kPending = 0;
42const kFulfilled = +1;
43const kRejected = -1;
44
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000045var lastMicrotaskId = 0;
46
Ben Murdochc5610432016-08-08 18:44:38 +010047// ES#sec-createresolvingfunctions
48// CreateResolvingFunctions ( promise )
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000049function CreateResolvingFunctions(promise) {
50 var alreadyResolved = false;
51
Ben Murdochc5610432016-08-08 18:44:38 +010052 // ES#sec-promise-resolve-functions
53 // Promise Resolve Functions
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000054 var resolve = value => {
55 if (alreadyResolved === true) return;
56 alreadyResolved = true;
Ben Murdoch61f157c2016-09-16 13:49:30 +010057 ResolvePromise(promise, value);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000058 };
59
Ben Murdochc5610432016-08-08 18:44:38 +010060 // ES#sec-promise-reject-functions
61 // Promise Reject Functions
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000062 var reject = reason => {
63 if (alreadyResolved === true) return;
64 alreadyResolved = true;
Ben Murdochc5610432016-08-08 18:44:38 +010065 RejectPromise(promise, reason);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000066 };
67
68 return {
69 __proto__: null,
70 resolve: resolve,
71 reject: reject
72 };
73}
74
75
Ben Murdochc5610432016-08-08 18:44:38 +010076// ES#sec-promise-executor
77// Promise ( executor )
Ben Murdoch61f157c2016-09-16 13:49:30 +010078var GlobalPromise = function Promise(executor) {
79 if (executor === promiseRawSymbol) {
Ben Murdochda12d292016-06-02 14:46:10 +010080 return %_NewObject(GlobalPromise, new.target);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000081 }
82 if (IS_UNDEFINED(new.target)) throw MakeTypeError(kNotAPromise, this);
Ben Murdoch61f157c2016-09-16 13:49:30 +010083 if (!IS_CALLABLE(executor)) {
84 throw MakeTypeError(kResolverNotAFunction, executor);
Ben Murdochc5610432016-08-08 18:44:38 +010085 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000086
Ben Murdochda12d292016-06-02 14:46:10 +010087 var promise = PromiseInit(%_NewObject(GlobalPromise, new.target));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000088 var callbacks = CreateResolvingFunctions(promise);
Ben Murdoch61f157c2016-09-16 13:49:30 +010089 var debug_is_active = DEBUG_IS_ACTIVE;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000090 try {
Ben Murdoch61f157c2016-09-16 13:49:30 +010091 if (debug_is_active) %DebugPushPromise(promise, Promise);
92 executor(callbacks.resolve, callbacks.reject);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000093 } catch (e) {
94 %_Call(callbacks.reject, UNDEFINED, e);
95 } finally {
Ben Murdoch61f157c2016-09-16 13:49:30 +010096 if (debug_is_active) %DebugPopPromise();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000097 }
98
99 return promise;
100}
101
102// Core functionality.
103
Ben Murdoch61f157c2016-09-16 13:49:30 +0100104function PromiseSet(promise, status, value) {
Ben Murdochc5610432016-08-08 18:44:38 +0100105 SET_PRIVATE(promise, promiseStateSymbol, status);
106 SET_PRIVATE(promise, promiseResultSymbol, value);
Ben Murdoch61f157c2016-09-16 13:49:30 +0100107
108 // There are 3 possible states for the resolve, reject symbols when we add
109 // a new callback --
110 // 1) UNDEFINED -- This is the zero state where there is no callback
111 // registered. When we see this state, we directly attach the callbacks to
112 // the symbol.
113 // 2) !IS_ARRAY -- There is a single callback directly attached to the
114 // symbols. We need to create a new array to store additional callbacks.
115 // 3) IS_ARRAY -- There are multiple callbacks already registered,
116 // therefore we can just push the new callback to the existing array.
117 SET_PRIVATE(promise, promiseFulfillReactionsSymbol, UNDEFINED);
118 SET_PRIVATE(promise, promiseRejectReactionsSymbol, UNDEFINED);
119
120 // There are 2 possible states for this symbol --
121 // 1) UNDEFINED -- This is the zero state, no deferred object is
122 // attached to this symbol. When we want to add a new deferred we
123 // directly attach it to this symbol.
124 // 2) symbol with attached deferred object -- New deferred objects
125 // are not attached to this symbol, but instead they are directly
126 // attached to the resolve, reject callback arrays. At this point,
127 // the deferred symbol's state is stale, and the deferreds should be
128 // read from the reject, resolve callbacks.
129 SET_PRIVATE(promise, promiseDeferredReactionsSymbol, UNDEFINED);
130
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000131 return promise;
132}
133
134function PromiseCreateAndSet(status, value) {
135 var promise = new GlobalPromise(promiseRawSymbol);
136 // If debug is active, notify about the newly created promise first.
Ben Murdochc5610432016-08-08 18:44:38 +0100137 if (DEBUG_IS_ACTIVE) PromiseSet(promise, kPending, UNDEFINED);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000138 return PromiseSet(promise, status, value);
139}
140
141function PromiseInit(promise) {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100142 return PromiseSet(promise, kPending, UNDEFINED);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000143}
144
Ben Murdoch61f157c2016-09-16 13:49:30 +0100145function FulfillPromise(promise, status, value, promiseQueue) {
Ben Murdochc5610432016-08-08 18:44:38 +0100146 if (GET_PRIVATE(promise, promiseStateSymbol) === kPending) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000147 var tasks = GET_PRIVATE(promise, promiseQueue);
Ben Murdoch61f157c2016-09-16 13:49:30 +0100148 if (!IS_UNDEFINED(tasks)) {
149 var tasks = GET_PRIVATE(promise, promiseQueue);
150 var deferreds = GET_PRIVATE(promise, promiseDeferredReactionsSymbol);
151 PromiseEnqueue(value, tasks, deferreds, status);
152 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000153 PromiseSet(promise, status, value);
154 }
155}
156
157function PromiseHandle(value, handler, deferred) {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100158 var debug_is_active = DEBUG_IS_ACTIVE;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000159 try {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100160 if (debug_is_active) %DebugPushPromise(deferred.promise, PromiseHandle);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000161 var result = handler(value);
162 deferred.resolve(result);
163 } catch (exception) {
164 try { deferred.reject(exception); } catch (e) { }
165 } finally {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100166 if (debug_is_active) %DebugPopPromise();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000167 }
168}
169
Ben Murdoch61f157c2016-09-16 13:49:30 +0100170function PromiseEnqueue(value, tasks, deferreds, status) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000171 var id, name, instrumenting = DEBUG_IS_ACTIVE;
172 %EnqueueMicrotask(function() {
173 if (instrumenting) {
174 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
175 }
Ben Murdoch61f157c2016-09-16 13:49:30 +0100176 if (IS_ARRAY(tasks)) {
177 for (var i = 0; i < tasks.length; i += 2) {
178 PromiseHandle(value, tasks[i], tasks[i + 1]);
179 }
180 } else {
181 PromiseHandle(value, tasks, deferreds);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000182 }
183 if (instrumenting) {
184 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
185 }
186 });
187 if (instrumenting) {
188 id = ++lastMicrotaskId;
Ben Murdochc5610432016-08-08 18:44:38 +0100189 name = status === kFulfilled ? "Promise.resolve" : "Promise.reject";
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000190 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
191 }
192}
193
Ben Murdoch61f157c2016-09-16 13:49:30 +0100194function PromiseAttachCallbacks(promise, deferred, onResolve, onReject) {
195 var maybeResolveCallbacks =
196 GET_PRIVATE(promise, promiseFulfillReactionsSymbol);
197 if (IS_UNDEFINED(maybeResolveCallbacks)) {
198 SET_PRIVATE(promise, promiseFulfillReactionsSymbol, onResolve);
199 SET_PRIVATE(promise, promiseRejectReactionsSymbol, onReject);
200 SET_PRIVATE(promise, promiseDeferredReactionsSymbol, deferred);
201 } else if (!IS_ARRAY(maybeResolveCallbacks)) {
202 var resolveCallbacks = new InternalArray();
203 var rejectCallbacks = new InternalArray();
204 var existingDeferred = GET_PRIVATE(promise, promiseDeferredReactionsSymbol);
205
206 resolveCallbacks.push(
207 maybeResolveCallbacks, existingDeferred, onResolve, deferred);
208 rejectCallbacks.push(GET_PRIVATE(promise, promiseRejectReactionsSymbol),
209 existingDeferred,
210 onReject,
211 deferred);
212
213 SET_PRIVATE(promise, promiseFulfillReactionsSymbol, resolveCallbacks);
214 SET_PRIVATE(promise, promiseRejectReactionsSymbol, rejectCallbacks);
215 } else {
216 maybeResolveCallbacks.push(onResolve, deferred);
217 GET_PRIVATE(promise, promiseRejectReactionsSymbol).push(onReject, deferred);
218 }
219}
220
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000221function PromiseIdResolveHandler(x) { return x }
222function PromiseIdRejectHandler(r) { throw r }
223
224function PromiseNopResolver() {}
225
226// -------------------------------------------------------------------
227// Define exported functions.
228
229// For bootstrapper.
230
Ben Murdochc5610432016-08-08 18:44:38 +0100231// ES#sec-ispromise IsPromise ( x )
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000232function IsPromise(x) {
Ben Murdochc5610432016-08-08 18:44:38 +0100233 return IS_RECEIVER(x) && HAS_DEFINED_PRIVATE(x, promiseStateSymbol);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000234}
235
236function PromiseCreate() {
237 return new GlobalPromise(PromiseNopResolver)
238}
239
Ben Murdoch61f157c2016-09-16 13:49:30 +0100240// ES#sec-promise-resolve-functions
241// Promise Resolve Functions, steps 6-13
242function ResolvePromise(promise, resolution) {
243 if (resolution === promise) {
244 return RejectPromise(promise, MakeTypeError(kPromiseCyclic, resolution));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000245 }
Ben Murdoch61f157c2016-09-16 13:49:30 +0100246 if (IS_RECEIVER(resolution)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000247 // 25.4.1.3.2 steps 8-12
248 try {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100249 var then = resolution.then;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000250 } catch (e) {
Ben Murdochc5610432016-08-08 18:44:38 +0100251 return RejectPromise(promise, e);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000252 }
Ben Murdoch61f157c2016-09-16 13:49:30 +0100253
254 // Resolution is a native promise and if it's already resolved or
255 // rejected, shortcircuit the resolution procedure by directly
256 // reusing the value from the promise.
257 if (IsPromise(resolution) && then === PromiseThen) {
258 var thenableState = GET_PRIVATE(resolution, promiseStateSymbol);
259 if (thenableState === kFulfilled) {
260 // This goes inside the if-else to save one symbol lookup in
261 // the slow path.
262 var thenableValue = GET_PRIVATE(resolution, promiseResultSymbol);
263 FulfillPromise(promise, kFulfilled, thenableValue,
264 promiseFulfillReactionsSymbol);
265 SET_PRIVATE(promise, promiseHasHandlerSymbol, true);
266 return;
267 } else if (thenableState === kRejected) {
268 var thenableValue = GET_PRIVATE(resolution, promiseResultSymbol);
269 if (!HAS_DEFINED_PRIVATE(resolution, promiseHasHandlerSymbol)) {
270 // Promise has already been rejected, but had no handler.
271 // Revoke previously triggered reject event.
272 %PromiseRevokeReject(resolution);
273 }
274 RejectPromise(promise, thenableValue);
275 SET_PRIVATE(resolution, promiseHasHandlerSymbol, true);
276 return;
277 }
278 }
279
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000280 if (IS_CALLABLE(then)) {
281 // PromiseResolveThenableJob
Ben Murdoch61f157c2016-09-16 13:49:30 +0100282 var id;
283 var name = "PromiseResolveThenableJob";
284 var instrumenting = DEBUG_IS_ACTIVE;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000285 %EnqueueMicrotask(function() {
286 if (instrumenting) {
287 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
288 }
289 var callbacks = CreateResolvingFunctions(promise);
290 try {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100291 %_Call(then, resolution, callbacks.resolve, callbacks.reject);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000292 } catch (e) {
293 %_Call(callbacks.reject, UNDEFINED, e);
294 }
295 if (instrumenting) {
296 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
297 }
298 });
299 if (instrumenting) {
300 id = ++lastMicrotaskId;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000301 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
302 }
303 return;
304 }
305 }
Ben Murdoch61f157c2016-09-16 13:49:30 +0100306 FulfillPromise(promise, kFulfilled, resolution, promiseFulfillReactionsSymbol);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000307}
308
Ben Murdochc5610432016-08-08 18:44:38 +0100309// ES#sec-rejectpromise
310// RejectPromise ( promise, reason )
Ben Murdoch61f157c2016-09-16 13:49:30 +0100311function RejectPromise(promise, reason) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000312 // Check promise status to confirm that this reject has an effect.
313 // Call runtime for callbacks to the debugger or for unhandled reject.
Ben Murdochc5610432016-08-08 18:44:38 +0100314 if (GET_PRIVATE(promise, promiseStateSymbol) === kPending) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000315 var debug_is_active = DEBUG_IS_ACTIVE;
316 if (debug_is_active ||
317 !HAS_DEFINED_PRIVATE(promise, promiseHasHandlerSymbol)) {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100318 %PromiseRejectEvent(promise, reason, debug_is_active);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000319 }
320 }
Ben Murdoch61f157c2016-09-16 13:49:30 +0100321 FulfillPromise(promise, kRejected, reason, promiseRejectReactionsSymbol)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000322}
323
Ben Murdochc5610432016-08-08 18:44:38 +0100324// ES#sec-newpromisecapability
325// NewPromiseCapability ( C )
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000326function NewPromiseCapability(C) {
327 if (C === GlobalPromise) {
328 // Optimized case, avoid extra closure.
329 var promise = PromiseInit(new GlobalPromise(promiseRawSymbol));
330 var callbacks = CreateResolvingFunctions(promise);
331 return {
332 promise: promise,
333 resolve: callbacks.resolve,
334 reject: callbacks.reject
335 };
336 }
337
338 var result = {promise: UNDEFINED, resolve: UNDEFINED, reject: UNDEFINED };
339 result.promise = new C((resolve, reject) => {
340 if (!IS_UNDEFINED(result.resolve) || !IS_UNDEFINED(result.reject))
341 throw MakeTypeError(kPromiseExecutorAlreadyInvoked);
342 result.resolve = resolve;
343 result.reject = reject;
344 });
345
Ben Murdochda12d292016-06-02 14:46:10 +0100346 if (!IS_CALLABLE(result.resolve) || !IS_CALLABLE(result.reject))
347 throw MakeTypeError(kPromiseNonCallable);
348
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000349 return result;
350}
351
Ben Murdochc5610432016-08-08 18:44:38 +0100352// Unspecified V8-specific legacy function
353function PromiseDefer() {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000354 %IncrementUseCounter(kPromiseDefer);
355 return NewPromiseCapability(this);
356}
357
Ben Murdochc5610432016-08-08 18:44:38 +0100358// Unspecified V8-specific legacy function
359function PromiseAccept(x) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000360 %IncrementUseCounter(kPromiseAccept);
Ben Murdochc5610432016-08-08 18:44:38 +0100361 return %_Call(PromiseResolve, this, x);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000362}
363
Ben Murdochc5610432016-08-08 18:44:38 +0100364// ES#sec-promise.reject
365// Promise.reject ( x )
366function PromiseReject(r) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000367 if (!IS_RECEIVER(this)) {
Ben Murdochc5610432016-08-08 18:44:38 +0100368 throw MakeTypeError(kCalledOnNonObject, PromiseResolve);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000369 }
370 if (this === GlobalPromise) {
371 // Optimized case, avoid extra closure.
Ben Murdochc5610432016-08-08 18:44:38 +0100372 var promise = PromiseCreateAndSet(kRejected, r);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000373 // The debug event for this would always be an uncaught promise reject,
374 // which is usually simply noise. Do not trigger that debug event.
375 %PromiseRejectEvent(promise, r, false);
376 return promise;
377 } else {
378 var promiseCapability = NewPromiseCapability(this);
379 %_Call(promiseCapability.reject, UNDEFINED, r);
380 return promiseCapability.promise;
381 }
382}
383
Ben Murdochc5610432016-08-08 18:44:38 +0100384// Shortcut Promise.reject and Promise.resolve() implementations, used by
385// Async Functions implementation.
386function PromiseCreateRejected(r) {
387 return %_Call(PromiseReject, GlobalPromise, r);
388}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000389
Ben Murdochc5610432016-08-08 18:44:38 +0100390function PromiseCreateResolved(x) {
391 return %_Call(PromiseResolve, GlobalPromise, x);
392}
393
394// ES#sec-promise.prototype.then
395// Promise.prototype.then ( onFulfilled, onRejected )
396// Multi-unwrapped chaining with thenable coercion.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000397function PromiseThen(onResolve, onReject) {
Ben Murdochc5610432016-08-08 18:44:38 +0100398 var status = GET_PRIVATE(this, promiseStateSymbol);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000399 if (IS_UNDEFINED(status)) {
400 throw MakeTypeError(kNotAPromise, this);
401 }
402
403 var constructor = SpeciesConstructor(this, GlobalPromise);
404 onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler;
405 onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler;
406 var deferred = NewPromiseCapability(constructor);
407 switch (status) {
Ben Murdochc5610432016-08-08 18:44:38 +0100408 case kPending:
Ben Murdoch61f157c2016-09-16 13:49:30 +0100409 PromiseAttachCallbacks(this, deferred, onResolve, onReject);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000410 break;
Ben Murdochc5610432016-08-08 18:44:38 +0100411 case kFulfilled:
412 PromiseEnqueue(GET_PRIVATE(this, promiseResultSymbol),
Ben Murdoch61f157c2016-09-16 13:49:30 +0100413 onResolve, deferred, kFulfilled);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000414 break;
Ben Murdochc5610432016-08-08 18:44:38 +0100415 case kRejected:
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000416 if (!HAS_DEFINED_PRIVATE(this, promiseHasHandlerSymbol)) {
417 // Promise has already been rejected, but had no handler.
418 // Revoke previously triggered reject event.
419 %PromiseRevokeReject(this);
420 }
Ben Murdochc5610432016-08-08 18:44:38 +0100421 PromiseEnqueue(GET_PRIVATE(this, promiseResultSymbol),
Ben Murdoch61f157c2016-09-16 13:49:30 +0100422 onReject, deferred, kRejected);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000423 break;
424 }
425 // Mark this promise as having handler.
426 SET_PRIVATE(this, promiseHasHandlerSymbol, true);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000427 return deferred.promise;
428}
429
Ben Murdochc5610432016-08-08 18:44:38 +0100430// Unspecified V8-specific legacy function
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000431// Chain is left around for now as an alias for then
432function PromiseChain(onResolve, onReject) {
433 %IncrementUseCounter(kPromiseChain);
434 return %_Call(PromiseThen, this, onResolve, onReject);
435}
436
Ben Murdochc5610432016-08-08 18:44:38 +0100437// ES#sec-promise.prototype.catch
438// Promise.prototype.catch ( onRejected )
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000439function PromiseCatch(onReject) {
440 return this.then(UNDEFINED, onReject);
441}
442
443// Combinators.
444
Ben Murdochc5610432016-08-08 18:44:38 +0100445// ES#sec-promise.resolve
446// Promise.resolve ( x )
447function PromiseResolve(x) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000448 if (!IS_RECEIVER(this)) {
Ben Murdochc5610432016-08-08 18:44:38 +0100449 throw MakeTypeError(kCalledOnNonObject, PromiseResolve);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000450 }
451 if (IsPromise(x) && x.constructor === this) return x;
452
453 var promiseCapability = NewPromiseCapability(this);
454 var resolveResult = %_Call(promiseCapability.resolve, UNDEFINED, x);
455 return promiseCapability.promise;
456}
457
Ben Murdochc5610432016-08-08 18:44:38 +0100458// ES#sec-promise.all
459// Promise.all ( iterable )
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000460function PromiseAll(iterable) {
461 if (!IS_RECEIVER(this)) {
462 throw MakeTypeError(kCalledOnNonObject, "Promise.all");
463 }
464
465 var deferred = NewPromiseCapability(this);
466 var resolutions = new InternalArray();
467 var count;
468
469 function CreateResolveElementFunction(index, values, promiseCapability) {
470 var alreadyCalled = false;
471 return (x) => {
472 if (alreadyCalled === true) return;
473 alreadyCalled = true;
474 values[index] = x;
475 if (--count === 0) {
476 var valuesArray = [];
477 %MoveArrayContents(values, valuesArray);
478 %_Call(promiseCapability.resolve, UNDEFINED, valuesArray);
479 }
480 };
481 }
482
483 try {
484 var i = 0;
485 count = 1;
486 for (var value of iterable) {
487 var nextPromise = this.resolve(value);
488 ++count;
489 nextPromise.then(
490 CreateResolveElementFunction(i, resolutions, deferred),
491 deferred.reject);
492 SET_PRIVATE(deferred.reject, promiseCombinedDeferredSymbol, deferred);
493 ++i;
494 }
495
496 // 6.d
497 if (--count === 0) {
498 var valuesArray = [];
499 %MoveArrayContents(resolutions, valuesArray);
500 %_Call(deferred.resolve, UNDEFINED, valuesArray);
501 }
502
503 } catch (e) {
504 %_Call(deferred.reject, UNDEFINED, e);
505 }
506 return deferred.promise;
507}
508
Ben Murdochc5610432016-08-08 18:44:38 +0100509// ES#sec-promise.race
510// Promise.race ( iterable )
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000511function PromiseRace(iterable) {
512 if (!IS_RECEIVER(this)) {
513 throw MakeTypeError(kCalledOnNonObject, PromiseRace);
514 }
515
516 var deferred = NewPromiseCapability(this);
517 try {
518 for (var value of iterable) {
519 this.resolve(value).then(deferred.resolve, deferred.reject);
520 SET_PRIVATE(deferred.reject, promiseCombinedDeferredSymbol, deferred);
521 }
522 } catch (e) {
523 deferred.reject(e)
524 }
525 return deferred.promise;
526}
527
528
529// Utility for debugger
530
Ben Murdoch61f157c2016-09-16 13:49:30 +0100531function PromiseHasUserDefinedRejectHandlerCheck(handler, deferred) {
532 if (handler !== PromiseIdRejectHandler) {
533 var combinedDeferred = GET_PRIVATE(handler, promiseCombinedDeferredSymbol);
534 if (IS_UNDEFINED(combinedDeferred)) return true;
535 if (PromiseHasUserDefinedRejectHandlerRecursive(combinedDeferred.promise)) {
536 return true;
537 }
538 } else if (PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise)) {
539 return true;
540 }
541 return false;
542}
543
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000544function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
Ben Murdochc5610432016-08-08 18:44:38 +0100545 var queue = GET_PRIVATE(promise, promiseRejectReactionsSymbol);
Ben Murdoch61f157c2016-09-16 13:49:30 +0100546 var deferreds = GET_PRIVATE(promise, promiseDeferredReactionsSymbol);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000547 if (IS_UNDEFINED(queue)) return false;
Ben Murdoch61f157c2016-09-16 13:49:30 +0100548 if (!IS_ARRAY(queue)) {
549 return PromiseHasUserDefinedRejectHandlerCheck(queue, deferreds);
550 } else {
551 for (var i = 0; i < queue.length; i += 2) {
552 if (PromiseHasUserDefinedRejectHandlerCheck(queue[i], queue[i + 1])) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000553 return true;
554 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000555 }
556 }
557 return false;
558}
559
560// Return whether the promise will be handled by a user-defined reject
561// handler somewhere down the promise chain. For this, we do a depth-first
562// search for a reject handler that's not the default PromiseIdRejectHandler.
563function PromiseHasUserDefinedRejectHandler() {
564 return PromiseHasUserDefinedRejectHandlerRecursive(this);
565};
566
Ben Murdoch61f157c2016-09-16 13:49:30 +0100567
568function PromiseSpecies() {
569 return this;
570}
571
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000572// -------------------------------------------------------------------
573// Install exported functions.
574
575%AddNamedProperty(global, 'Promise', GlobalPromise, DONT_ENUM);
576%AddNamedProperty(GlobalPromise.prototype, toStringTagSymbol, "Promise",
577 DONT_ENUM | READ_ONLY);
578
579utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
Ben Murdochc5610432016-08-08 18:44:38 +0100580 "reject", PromiseReject,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000581 "all", PromiseAll,
582 "race", PromiseRace,
Ben Murdochc5610432016-08-08 18:44:38 +0100583 "resolve", PromiseResolve
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000584]);
585
Ben Murdoch61f157c2016-09-16 13:49:30 +0100586utils.InstallGetter(GlobalPromise, speciesSymbol, PromiseSpecies);
587
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000588utils.InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [
589 "then", PromiseThen,
590 "catch", PromiseCatch
591]);
592
593%InstallToContext([
594 "promise_catch", PromiseCatch,
595 "promise_chain", PromiseChain,
596 "promise_create", PromiseCreate,
597 "promise_has_user_defined_reject_handler", PromiseHasUserDefinedRejectHandler,
Ben Murdochc5610432016-08-08 18:44:38 +0100598 "promise_reject", RejectPromise,
Ben Murdoch61f157c2016-09-16 13:49:30 +0100599 "promise_resolve", ResolvePromise,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000600 "promise_then", PromiseThen,
Ben Murdochc5610432016-08-08 18:44:38 +0100601 "promise_create_rejected", PromiseCreateRejected,
602 "promise_create_resolved", PromiseCreateResolved
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000603]);
604
605// This allows extras to create promises quickly without building extra
606// resolve/reject closures, and allows them to later resolve and reject any
607// promise without having to hold on to those closures forever.
608utils.InstallFunctions(extrasUtils, 0, [
609 "createPromise", PromiseCreate,
Ben Murdoch61f157c2016-09-16 13:49:30 +0100610 "resolvePromise", ResolvePromise,
Ben Murdochc5610432016-08-08 18:44:38 +0100611 "rejectPromise", RejectPromise
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000612]);
613
614// TODO(v8:4567): Allow experimental natives to remove function prototype
Ben Murdochc5610432016-08-08 18:44:38 +0100615[PromiseChain, PromiseDefer, PromiseAccept].forEach(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000616 fn => %FunctionRemovePrototype(fn));
617
618utils.Export(function(to) {
619 to.PromiseChain = PromiseChain;
Ben Murdochc5610432016-08-08 18:44:38 +0100620 to.PromiseDefer = PromiseDefer;
621 to.PromiseAccept = PromiseAccept;
622
623 to.PromiseCreateRejected = PromiseCreateRejected;
624 to.PromiseCreateResolved = PromiseCreateResolved;
625 to.PromiseThen = PromiseThen;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000626});
627
628})