]> git.saurik.com Git - apple/javascriptcore.git/blob - builtins/Operations.Promise.js
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / builtins / Operations.Promise.js
1 /*
2 * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 function isPromise(promise)
27 {
28 "use strict";
29
30 return @isObject(promise) && !!promise.@promiseState;
31 }
32
33 function newPromiseReaction(capability, handler)
34 {
35 "use strict";
36
37 return {
38 @capabilities: capability,
39 @handler: handler
40 };
41 }
42
43 function newPromiseDeferred()
44 {
45 "use strict";
46
47 return @newPromiseCapability(@Promise);
48 }
49
50 function newPromiseCapability(constructor)
51 {
52 "use strict";
53
54 // FIXME: Check isConstructor(constructor).
55 if (typeof constructor !== "function")
56 throw new @TypeError("promise capability requires a constructor function");
57
58 var promiseCapability = {
59 @promise: undefined,
60 @resolve: undefined,
61 @reject: undefined
62 };
63
64 function executor(resolve, reject)
65 {
66 if (promiseCapability.@resolve !== undefined)
67 throw new @TypeError("resolve function is already set");
68 if (promiseCapability.@reject !== undefined)
69 throw new @TypeError("reject function is already set");
70
71 promiseCapability.@resolve = resolve;
72 promiseCapability.@reject = reject;
73 }
74
75 var promise = new constructor(executor);
76
77 if (typeof promiseCapability.@resolve !== "function")
78 throw new @TypeError("executor did not take a resolve function");
79
80 if (typeof promiseCapability.@reject !== "function")
81 throw new @TypeError("executor did not take a reject function");
82
83 promiseCapability.@promise = promise;
84
85 return promiseCapability;
86 }
87
88 function triggerPromiseReactions(reactions, argument)
89 {
90 "use strict";
91
92 for (var index = 0, length = reactions.length; index < length; ++index)
93 @enqueueJob(@promiseReactionJob, [reactions[index], argument]);
94 }
95
96 function rejectPromise(promise, reason)
97 {
98 "use strict";
99
100 var reactions = promise.@promiseRejectReactions;
101 promise.@promiseResult = reason;
102 promise.@promiseFulfillReactions = undefined;
103 promise.@promiseRejectReactions = undefined;
104 promise.@promiseState = @promiseRejected;
105 @triggerPromiseReactions(reactions, reason);
106 }
107
108 function fulfillPromise(promise, value)
109 {
110 "use strict";
111
112 var reactions = promise.@promiseFulfillReactions;
113 promise.@promiseResult = value;
114 promise.@promiseFulfillReactions = undefined;
115 promise.@promiseRejectReactions = undefined;
116 promise.@promiseState = @promiseFulfilled;
117 @triggerPromiseReactions(reactions, value);
118 }
119
120 function createResolvingFunctions(promise)
121 {
122 "use strict";
123
124 var alreadyResolved = false;
125
126 var resolve = function (resolution) {
127 if (alreadyResolved)
128 return undefined;
129 alreadyResolved = true;
130
131 if (resolution === promise)
132 return @rejectPromise(promise, new @TypeError("Resolve a promise with itself"));
133
134 if (!@isObject(resolution))
135 return @fulfillPromise(promise, resolution);
136
137 var then;
138 try {
139 then = resolution.then;
140 } catch (error) {
141 return @rejectPromise(promise, error);
142 }
143
144 if (typeof then !== 'function')
145 return @fulfillPromise(promise, resolution);
146
147 @enqueueJob(@promiseResolveThenableJob, [promise, resolution, then]);
148
149 return undefined;
150 };
151
152 var reject = function (reason) {
153 if (alreadyResolved)
154 return undefined;
155 alreadyResolved = true;
156
157 return @rejectPromise(promise, reason);
158 };
159
160 return {
161 @resolve: resolve,
162 @reject: reject
163 };
164 }
165
166 function promiseReactionJob(reaction, argument)
167 {
168 "use strict";
169
170 var promiseCapability = reaction.@capabilities;
171
172 var result;
173 try {
174 result = reaction.@handler.@call(undefined, argument);
175 } catch (error) {
176 return promiseCapability.@reject.@call(undefined, error);
177 }
178
179 return promiseCapability.@resolve.@call(undefined, result);
180 }
181
182 function promiseResolveThenableJob(promiseToResolve, thenable, then)
183 {
184 "use strict";
185
186 var resolvingFunctions = @createResolvingFunctions(promiseToResolve);
187
188 try {
189 return then.@call(thenable, resolvingFunctions.@resolve, resolvingFunctions.@reject);
190 } catch (error) {
191 return resolvingFunctions.@reject.@call(undefined, error);
192 }
193 }
194
195 function initializePromise(executor)
196 {
197 "use strict";
198
199 if (typeof executor !== 'function')
200 throw new @TypeError("Promise constructor takes a function argument");
201
202 this.@promiseState = @promisePending;
203 this.@promiseFulfillReactions = [];
204 this.@promiseRejectReactions = [];
205
206 var resolvingFunctions = @createResolvingFunctions(this);
207 try {
208 executor(resolvingFunctions.@resolve, resolvingFunctions.@reject);
209 } catch (error) {
210 return resolvingFunctions.@reject.@call(undefined, error);
211 }
212
213 return this;
214 }