iterator-close.js (6613B)
1 // |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue 2 3 var BUGNUMBER = 1180306; 4 var summary = 'Promise.{all,race} should close iterator on error'; 5 6 print(BUGNUMBER + ": " + summary); 7 8 function test(ctor, props, { nextVal=undefined, 9 nextThrowVal=undefined, 10 modifier=undefined, 11 rejectReason=undefined, 12 rejectType=undefined, 13 closed=true }) { 14 function getIterable() { 15 let iterable = { 16 closed: false, 17 [Symbol.iterator]() { 18 let iterator = { 19 first: true, 20 next() { 21 if (this.first) { 22 this.first = false; 23 if (nextThrowVal) 24 throw nextThrowVal; 25 return nextVal; 26 } 27 return { value: undefined, done: true }; 28 }, 29 return() { 30 iterable.closed = true; 31 return {}; 32 } 33 }; 34 if (modifier) 35 modifier(iterator, iterable); 36 37 return iterator; 38 } 39 }; 40 return iterable; 41 } 42 for (let prop of props) { 43 let iterable = getIterable(); 44 let e; 45 ctor[prop](iterable).catch(e_ => { e = e_; }); 46 drainJobQueue(); 47 if(rejectType) 48 assertEq(e instanceof rejectType, true); 49 else 50 assertEq(e, rejectReason); 51 assertEq(iterable.closed, closed); 52 } 53 } 54 55 // == Error cases with close == 56 57 // ES 2017 draft 25.4.4.1.1 step 6.i. 58 // ES 2017 draft 25.4.4.3.1 step 3.h. 59 class MyPromiseStaticResolveGetterThrows extends Promise { 60 static get resolve() { 61 throw "static resolve getter throws"; 62 } 63 }; 64 test(MyPromiseStaticResolveGetterThrows, ["all", "race"], { 65 nextVal: { value: Promise.resolve(1), done: false }, 66 rejectReason: "static resolve getter throws", 67 closed: false, 68 }); 69 70 class MyPromiseStaticResolveThrows extends Promise { 71 static resolve() { 72 throw "static resolve throws"; 73 } 74 }; 75 test(MyPromiseStaticResolveThrows, ["all", "race"], { 76 nextVal: { value: Promise.resolve(1), done: false }, 77 rejectReason: "static resolve throws", 78 closed: true, 79 }); 80 81 // ES 2017 draft 25.4.4.1.1 step 6.q. 82 // ES 2017 draft 25.4.4.3.1 step 3.i. 83 class MyPromiseThenGetterThrows extends Promise { 84 static resolve() { 85 return { 86 get then() { 87 throw "then getter throws"; 88 } 89 }; 90 } 91 }; 92 test(MyPromiseThenGetterThrows, ["all", "race"], { 93 nextVal: { value: Promise.resolve(1), done: false }, 94 rejectReason: "then getter throws", 95 closed: true, 96 }); 97 98 class MyPromiseThenThrows extends Promise { 99 static resolve() { 100 return { 101 then() { 102 throw "then throws"; 103 } 104 }; 105 } 106 }; 107 test(MyPromiseThenThrows, ["all", "race"], { 108 nextVal: { value: Promise.resolve(1), done: false }, 109 rejectReason: "then throws", 110 closed: true, 111 }); 112 113 // ES 2017 draft 7.4.6 step 3. 114 // if GetMethod fails, the thrown value should be used. 115 test(MyPromiseThenThrows, ["all", "race"], { 116 nextVal: { value: Promise.resolve(1), done: false }, 117 modifier: (iterator, iterable) => { 118 Object.defineProperty(iterator, "return", { 119 get: function() { 120 iterable.closed = true; 121 throw "return getter throws"; 122 } 123 }); 124 }, 125 rejectReason: "then throws", 126 closed: true, 127 }); 128 test(MyPromiseThenThrows, ["all", "race"], { 129 nextVal: { value: Promise.resolve(1), done: false }, 130 modifier: (iterator, iterable) => { 131 Object.defineProperty(iterator, "return", { 132 get: function() { 133 iterable.closed = true; 134 return "non object"; 135 } 136 }); 137 }, 138 rejectReason: "then throws", 139 closed: true, 140 }); 141 test(MyPromiseThenThrows, ["all", "race"], { 142 nextVal: { value: Promise.resolve(1), done: false }, 143 modifier: (iterator, iterable) => { 144 Object.defineProperty(iterator, "return", { 145 get: function() { 146 iterable.closed = true; 147 // Non callable. 148 return {}; 149 } 150 }); 151 }, 152 rejectReason: "then throws", 153 closed: true, 154 }); 155 156 // ES 2017 draft 7.4.6 steps 6. 157 // if return method throws, the thrown value should be ignored. 158 test(MyPromiseThenThrows, ["all", "race"], { 159 nextVal: { value: Promise.resolve(1), done: false }, 160 modifier: (iterator, iterable) => { 161 iterator.return = function() { 162 iterable.closed = true; 163 throw "return throws"; 164 }; 165 }, 166 rejectReason: "then throws", 167 closed: true, 168 }); 169 170 test(MyPromiseThenThrows, ["all", "race"], { 171 nextVal: { value: Promise.resolve(1), done: false }, 172 modifier: (iterator, iterable) => { 173 iterator.return = function() { 174 iterable.closed = true; 175 return "non object"; 176 }; 177 }, 178 rejectReason: "then throws", 179 closed: true, 180 }); 181 182 // == Error cases without close == 183 184 // ES 2017 draft 25.4.4.1.1 step 6.a. 185 test(Promise, ["all", "race"], { 186 nextThrowVal: "next throws", 187 rejectReason: "next throws", 188 closed: false, 189 }); 190 191 test(Promise, ["all", "race"], { 192 nextVal: { value: {}, get done() { throw "done getter throws"; } }, 193 rejectReason: "done getter throws", 194 closed: false, 195 }); 196 197 // ES 2017 draft 25.4.4.1.1 step 6.e. 198 test(Promise, ["all", "race"], { 199 nextVal: { get value() { throw "value getter throws"; }, done: false }, 200 rejectReason: "value getter throws", 201 closed: false, 202 }); 203 204 // ES 2017 draft 25.4.4.1.1 step 6.d.iii.2. 205 let first = true; 206 class MyPromiseResolveThrows extends Promise { 207 constructor(executer) { 208 if (first) { 209 first = false; 210 super((resolve, reject) => { 211 executer(() => { 212 throw "resolve throws"; 213 }, reject); 214 }); 215 return; 216 } 217 super(executer); 218 } 219 }; 220 test(MyPromiseResolveThrows, ["all"], { 221 nextVal: { value: undefined, done: true }, 222 rejectReason: "resolve throws", 223 closed: false, 224 }); 225 226 // == Successful cases == 227 228 test(Promise, ["all", "race"], { 229 nextVal: { value: Promise.resolve(1), done: false }, 230 closed: false, 231 }); 232 233 if (typeof reportCompare === 'function') 234 reportCompare(true, true);