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

Side by Side Diff: src/promise.js

Issue 292173011: Harden a few builtins (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: rebased Created 6 years, 7 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/objects.cc ('k') | src/uri.js » ('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 13
13 var $Promise = function Promise(resolver) { 14 var IsPromise;
14 if (resolver === promiseRaw) return; 15 var PromiseCreate;
15 if (!%_IsConstructCall()) throw MakeTypeError('not_a_promise', [this]); 16 var PromiseResolve;
16 if (!IS_SPEC_FUNCTION(resolver)) 17 var PromiseReject;
17 throw MakeTypeError('resolver_not_a_function', [resolver]); 18 var PromiseChain;
18 var promise = PromiseInit(this); 19 var PromiseCatch;
19 try {
20 %DebugPromiseHandlePrologue(function() { return promise });
21 resolver(function(x) { PromiseResolve(promise, x) },
22 function(r) { PromiseReject(promise, r) });
23 } catch (e) {
24 PromiseReject(promise, e);
25 } finally {
26 %DebugPromiseHandleEpilogue();
27 }
28 }
29 20
30 21 // mirror-debugger.js currently uses builtins.promiseStatus. It would be nice
31 //------------------------------------------------------------------- 22 // if we could move these property names into the closure below.
32 23 // TODO(jkummerow/rossberg/yangguo): Find a better solution.
33 // Core functionality.
34 24
35 // Status values: 0 = pending, +1 = resolved, -1 = rejected 25 // Status values: 0 = pending, +1 = resolved, -1 = rejected
36 var promiseStatus = GLOBAL_PRIVATE("Promise#status"); 26 var promiseStatus = GLOBAL_PRIVATE("Promise#status");
37 var promiseValue = GLOBAL_PRIVATE("Promise#value"); 27 var promiseValue = GLOBAL_PRIVATE("Promise#value");
38 var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve"); 28 var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve");
39 var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject"); 29 var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject");
40 var promiseRaw = GLOBAL_PRIVATE("Promise#raw"); 30 var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
41 31
42 function IsPromise(x) { 32 (function() {
43 return IS_SPEC_OBJECT(x) && %HasLocalProperty(x, promiseStatus); 33
44 } 34 var $Promise = function Promise(resolver) {
45 35 if (resolver === promiseRaw) return;
46 function PromiseSet(promise, status, value, onResolve, onReject) { 36 if (!%_IsConstructCall()) throw MakeTypeError('not_a_promise', [this]);
47 SET_PRIVATE(promise, promiseStatus, status); 37 if (!IS_SPEC_FUNCTION(resolver))
48 SET_PRIVATE(promise, promiseValue, value); 38 throw MakeTypeError('resolver_not_a_function', [resolver]);
49 SET_PRIVATE(promise, promiseOnResolve, onResolve); 39 var promise = PromiseInit(this);
50 SET_PRIVATE(promise, promiseOnReject, onReject); 40 try {
51 return promise; 41 %DebugPromiseHandlePrologue(function() { return promise });
52 } 42 resolver(function(x) { PromiseResolve(promise, x) },
53 43 function(r) { PromiseReject(promise, r) });
54 function PromiseInit(promise) { 44 } catch (e) {
55 return PromiseSet(promise, 0, UNDEFINED, new InternalArray, new InternalArray) 45 PromiseReject(promise, e);
56 } 46 } finally {
57
58 function PromiseDone(promise, status, value, promiseQueue) {
59 if (GET_PRIVATE(promise, promiseStatus) === 0) {
60 PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue));
61 PromiseSet(promise, status, value);
62 }
63 }
64
65 function PromiseResolve(promise, x) {
66 PromiseDone(promise, +1, x, promiseOnResolve)
67 }
68
69 function PromiseReject(promise, r) {
70 PromiseDone(promise, -1, r, promiseOnReject)
71 }
72
73
74 // For API.
75
76 function PromiseNopResolver() {}
77
78 function PromiseCreate() {
79 return new $Promise(PromiseNopResolver)
80 }
81
82
83 // Convenience.
84
85 function PromiseDeferred() {
86 if (this === $Promise) {
87 // Optimized case, avoid extra closure.
88 var promise = PromiseInit(new $Promise(promiseRaw));
89 return {
90 promise: promise,
91 resolve: function(x) { PromiseResolve(promise, x) },
92 reject: function(r) { PromiseReject(promise, r) }
93 };
94 } else {
95 var result = {};
96 result.promise = new this(function(resolve, reject) {
97 result.resolve = resolve;
98 result.reject = reject;
99 })
100 return result;
101 }
102 }
103
104 function PromiseResolved(x) {
105 if (this === $Promise) {
106 // Optimized case, avoid extra closure.
107 return PromiseSet(new $Promise(promiseRaw), +1, x);
108 } else {
109 return new this(function(resolve, reject) { resolve(x) });
110 }
111 }
112
113 function PromiseRejected(r) {
114 if (this === $Promise) {
115 // Optimized case, avoid extra closure.
116 return PromiseSet(new $Promise(promiseRaw), -1, r);
117 } else {
118 return new this(function(resolve, reject) { reject(r) });
119 }
120 }
121
122
123 // Simple chaining.
124
125 function PromiseIdResolveHandler(x) { return x }
126 function PromiseIdRejectHandler(r) { throw r }
127
128 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap
129 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
130 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
131 var deferred = %_CallFunction(this.constructor, PromiseDeferred);
132 switch (GET_PRIVATE(this, promiseStatus)) {
133 case UNDEFINED:
134 throw MakeTypeError('not_a_promise', [this]);
135 case 0: // Pending
136 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred);
137 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred);
138 break;
139 case +1: // Resolved
140 PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onResolve, deferred]);
141 break;
142 case -1: // Rejected
143 PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onReject, deferred]);
144 break;
145 }
146 return deferred.promise;
147 }
148
149 function PromiseCatch(onReject) {
150 return this.then(UNDEFINED, onReject);
151 }
152
153 function PromiseEnqueue(value, tasks) {
154 %EnqueueMicrotask(function() {
155 for (var i = 0; i < tasks.length; i += 2) {
156 PromiseHandle(value, tasks[i], tasks[i + 1])
157 }
158 });
159 }
160
161 function PromiseHandle(value, handler, deferred) {
162 try {
163 %DebugPromiseHandlePrologue(
164 function() {
165 var queue = GET_PRIVATE(deferred.promise, promiseOnReject);
166 return (queue && queue.length == 0) ? deferred.promise : UNDEFINED;
167 });
168 var result = handler(value);
169 if (result === deferred.promise)
170 throw MakeTypeError('promise_cyclic', [result]);
171 else if (IsPromise(result))
172 %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain);
173 else
174 deferred.resolve(result);
175 } catch (exception) {
176 try {
177 %DebugPromiseHandlePrologue(function() { return deferred.promise });
178 deferred.reject(exception);
179 } catch (e) { } finally {
180 %DebugPromiseHandleEpilogue(); 47 %DebugPromiseHandleEpilogue();
181 } 48 }
182 } finally { 49 }
183 %DebugPromiseHandleEpilogue(); 50
184 } 51 // Core functionality.
185 } 52
186 53 function PromiseSet(promise, status, value, onResolve, onReject) {
187 54 SET_PRIVATE(promise, promiseStatus, status);
188 // Multi-unwrapped chaining with thenable coercion. 55 SET_PRIVATE(promise, promiseValue, value);
189 56 SET_PRIVATE(promise, promiseOnResolve, onResolve);
190 function PromiseThen(onResolve, onReject) { 57 SET_PRIVATE(promise, promiseOnReject, onReject);
191 onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve : PromiseIdResolveHandler; 58 return promise;
192 onReject = IS_SPEC_FUNCTION(onReject) ? onReject : PromiseIdRejectHandler; 59 }
193 var that = this; 60
194 var constructor = this.constructor; 61 function PromiseInit(promise) {
195 return %_CallFunction( 62 return PromiseSet(promise, 0, UNDEFINED, new InternalArray,
196 this, 63 new InternalArray)
197 function(x) { 64 }
198 x = PromiseCoerce(constructor, x); 65
199 return x === that ? onReject(MakeTypeError('promise_cyclic', [x])) : 66 function PromiseDone(promise, status, value, promiseQueue) {
200 IsPromise(x) ? x.then(onResolve, onReject) : onResolve(x); 67 if (GET_PRIVATE(promise, promiseStatus) === 0) {
201 }, 68 PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue));
202 onReject, 69 PromiseSet(promise, status, value);
203 PromiseChain 70 }
204 ); 71 }
205 } 72
206 73 function PromiseCoerce(constructor, x) {
207 function PromiseCoerce(constructor, x) { 74 if (!IsPromise(x) && IS_SPEC_OBJECT(x)) {
208 if (!IsPromise(x) && IS_SPEC_OBJECT(x)) { 75 var then;
209 var then;
210 try {
211 then = x.then;
212 } catch(r) {
213 return %_CallFunction(constructor, r, PromiseRejected);
214 }
215 if (IS_SPEC_FUNCTION(then)) {
216 var deferred = %_CallFunction(constructor, PromiseDeferred);
217 try { 76 try {
218 %_CallFunction(x, deferred.resolve, deferred.reject, then); 77 then = x.then;
219 } catch(r) { 78 } catch(r) {
220 deferred.reject(r); 79 return %_CallFunction(constructor, r, PromiseRejected);
221 } 80 }
81 if (IS_SPEC_FUNCTION(then)) {
82 var deferred = %_CallFunction(constructor, PromiseDeferred);
83 try {
84 %_CallFunction(x, deferred.resolve, deferred.reject, then);
85 } catch(r) {
86 deferred.reject(r);
87 }
88 return deferred.promise;
89 }
90 }
91 return x;
92 }
93
94 function PromiseHandle(value, handler, deferred) {
95 try {
96 %DebugPromiseHandlePrologue(
97 function() {
98 var queue = GET_PRIVATE(deferred.promise, promiseOnReject);
99 return (queue && queue.length == 0) ? deferred.promise : UNDEFINED;
100 });
101 var result = handler(value);
102 if (result === deferred.promise)
103 throw MakeTypeError('promise_cyclic', [result]);
104 else if (IsPromise(result))
105 %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain);
106 else
107 deferred.resolve(result);
108 } catch (exception) {
109 try {
110 %DebugPromiseHandlePrologue(function() { return deferred.promise });
111 deferred.reject(exception);
112 } catch (e) { } finally {
113 %DebugPromiseHandleEpilogue();
114 }
115 } finally {
116 %DebugPromiseHandleEpilogue();
117 }
118 }
119
120 function PromiseEnqueue(value, tasks) {
121 %EnqueueMicrotask(function() {
122 for (var i = 0; i < tasks.length; i += 2) {
123 PromiseHandle(value, tasks[i], tasks[i + 1])
124 }
125 });
126 }
127
128 function PromiseIdResolveHandler(x) { return x }
129 function PromiseIdRejectHandler(r) { throw r }
130
131 function PromiseNopResolver() {}
132
133 // -------------------------------------------------------------------
134 // Define exported functions.
135
136 // For bootstrapper.
137
138 IsPromise = function IsPromise(x) {
139 return IS_SPEC_OBJECT(x) && %HasLocalProperty(x, promiseStatus);
140 }
141
142 PromiseCreate = function PromiseCreate() {
143 return new $Promise(PromiseNopResolver)
144 }
145
146 PromiseResolve = function PromiseResolve(promise, x) {
147 PromiseDone(promise, +1, x, promiseOnResolve)
148 }
149
150 PromiseReject = function PromiseReject(promise, r) {
151 PromiseDone(promise, -1, r, promiseOnReject)
152 }
153
154 // Convenience.
155
156 function PromiseDeferred() {
157 if (this === $Promise) {
158 // Optimized case, avoid extra closure.
159 var promise = PromiseInit(new $Promise(promiseRaw));
160 return {
161 promise: promise,
162 resolve: function(x) { PromiseResolve(promise, x) },
163 reject: function(r) { PromiseReject(promise, r) }
164 };
165 } else {
166 var result = {};
167 result.promise = new this(function(resolve, reject) {
168 result.resolve = resolve;
169 result.reject = reject;
170 })
171 return result;
172 }
173 }
174
175 function PromiseResolved(x) {
176 if (this === $Promise) {
177 // Optimized case, avoid extra closure.
178 return PromiseSet(new $Promise(promiseRaw), +1, x);
179 } else {
180 return new this(function(resolve, reject) { resolve(x) });
181 }
182 }
183
184 function PromiseRejected(r) {
185 if (this === $Promise) {
186 // Optimized case, avoid extra closure.
187 return PromiseSet(new $Promise(promiseRaw), -1, r);
188 } else {
189 return new this(function(resolve, reject) { reject(r) });
190 }
191 }
192
193 // Simple chaining.
194
195 PromiseChain = function PromiseChain(onResolve, onReject) { // a.k.a.
196 // flatMap
197 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
198 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
199 var deferred = %_CallFunction(this.constructor, PromiseDeferred);
200 switch (GET_PRIVATE(this, promiseStatus)) {
201 case UNDEFINED:
202 throw MakeTypeError('not_a_promise', [this]);
203 case 0: // Pending
204 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred);
205 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred);
206 break;
207 case +1: // Resolved
208 PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onResolve, deferred]);
209 break;
210 case -1: // Rejected
211 PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onReject, deferred]);
212 break;
213 }
214 return deferred.promise;
215 }
216
217 PromiseCatch = function PromiseCatch(onReject) {
218 return this.then(UNDEFINED, onReject);
219 }
220
221 // Multi-unwrapped chaining with thenable coercion.
222
223 function PromiseThen(onResolve, onReject) {
224 onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve
225 : PromiseIdResolveHandler;
226 onReject = IS_SPEC_FUNCTION(onReject) ? onReject
227 : PromiseIdRejectHandler;
228 var that = this;
229 var constructor = this.constructor;
230 return %_CallFunction(
231 this,
232 function(x) {
233 x = PromiseCoerce(constructor, x);
234 return x === that ? onReject(MakeTypeError('promise_cyclic', [x])) :
235 IsPromise(x) ? x.then(onResolve, onReject) : onResolve(x);
236 },
237 onReject,
238 PromiseChain
239 );
240 }
241
242 // Combinators.
243
244 function PromiseCast(x) {
245 // TODO(rossberg): cannot do better until we support @@create.
246 return IsPromise(x) ? x : new this(function(resolve) { resolve(x) });
247 }
248
249 function PromiseAll(values) {
250 var deferred = %_CallFunction(this, PromiseDeferred);
251 var resolutions = [];
252 if (!%_IsArray(values)) {
253 deferred.reject(MakeTypeError('invalid_argument'));
222 return deferred.promise; 254 return deferred.promise;
223 } 255 }
224 } 256 try {
225 return x; 257 var count = values.length;
226 } 258 if (count === 0) {
227 259 deferred.resolve(resolutions);
228 260 } else {
229 // Combinators. 261 for (var i = 0; i < values.length; ++i) {
230 262 this.resolve(values[i]).then(
231 function PromiseCast(x) { 263 function(i, x) {
232 // TODO(rossberg): cannot do better until we support @@create. 264 resolutions[i] = x;
233 return IsPromise(x) ? x : new this(function(resolve) { resolve(x) }); 265 if (--count === 0) deferred.resolve(resolutions);
234 } 266 }.bind(UNDEFINED, i), // TODO(rossberg): use let loop once
235 267 // available
236 function PromiseAll(values) { 268 function(r) { deferred.reject(r) }
237 var deferred = %_CallFunction(this, PromiseDeferred); 269 );
238 var resolutions = []; 270 }
239 if (!%_IsArray(values)) { 271 }
240 deferred.reject(MakeTypeError('invalid_argument')); 272 } catch (e) {
273 deferred.reject(e)
274 }
241 return deferred.promise; 275 return deferred.promise;
242 } 276 }
243 try { 277
244 var count = values.length; 278 function PromiseOne(values) {
245 if (count === 0) { 279 var deferred = %_CallFunction(this, PromiseDeferred);
246 deferred.resolve(resolutions); 280 if (!%_IsArray(values)) {
247 } else { 281 deferred.reject(MakeTypeError('invalid_argument'));
282 return deferred.promise;
283 }
284 try {
248 for (var i = 0; i < values.length; ++i) { 285 for (var i = 0; i < values.length; ++i) {
249 this.resolve(values[i]).then( 286 this.resolve(values[i]).then(
250 function(i, x) { 287 function(x) { deferred.resolve(x) },
251 resolutions[i] = x;
252 if (--count === 0) deferred.resolve(resolutions);
253 }.bind(UNDEFINED, i), // TODO(rossberg): use let loop once available
254 function(r) { deferred.reject(r) } 288 function(r) { deferred.reject(r) }
255 ); 289 );
256 } 290 }
257 } 291 } catch (e) {
258 } catch (e) { 292 deferred.reject(e)
259 deferred.reject(e) 293 }
260 }
261 return deferred.promise;
262 }
263
264 function PromiseOne(values) {
265 var deferred = %_CallFunction(this, PromiseDeferred);
266 if (!%_IsArray(values)) {
267 deferred.reject(MakeTypeError('invalid_argument'));
268 return deferred.promise; 294 return deferred.promise;
269 } 295 }
270 try { 296
271 for (var i = 0; i < values.length; ++i) { 297 // -------------------------------------------------------------------
272 this.resolve(values[i]).then( 298 // Install exported functions.
273 function(x) { deferred.resolve(x) }, 299
274 function(r) { deferred.reject(r) }
275 );
276 }
277 } catch (e) {
278 deferred.reject(e)
279 }
280 return deferred.promise;
281 }
282
283 //-------------------------------------------------------------------
284
285 function SetUpPromise() {
286 %CheckIsBootstrapping(); 300 %CheckIsBootstrapping();
287 %SetProperty(global, 'Promise', $Promise, DONT_ENUM); 301 %SetProperty(global, 'Promise', $Promise, DONT_ENUM);
288 InstallFunctions($Promise, DONT_ENUM, [ 302 InstallFunctions($Promise, DONT_ENUM, [
289 "defer", PromiseDeferred, 303 "defer", PromiseDeferred,
290 "accept", PromiseResolved, 304 "accept", PromiseResolved,
291 "reject", PromiseRejected, 305 "reject", PromiseRejected,
292 "all", PromiseAll, 306 "all", PromiseAll,
293 "race", PromiseOne, 307 "race", PromiseOne,
294 "resolve", PromiseCast 308 "resolve", PromiseCast
295 ]); 309 ]);
296 InstallFunctions($Promise.prototype, DONT_ENUM, [ 310 InstallFunctions($Promise.prototype, DONT_ENUM, [
297 "chain", PromiseChain, 311 "chain", PromiseChain,
298 "then", PromiseThen, 312 "then", PromiseThen,
299 "catch", PromiseCatch 313 "catch", PromiseCatch
300 ]); 314 ]);
301 }
302 315
303 SetUpPromise(); 316 })();
OLDNEW
« no previous file with comments | « src/objects.cc ('k') | src/uri.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698