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

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