MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add support for yield and generator functions

+1196 -182
+43 -43
examples/results.txt
··· 420 420 es5/this.thrown-function.js: OK 421 421 compat-table/es6/Array.Symbol.species.js: OK 422 422 compat-table/es6/Array.from.array-like.js: OK 423 - compat-table/es6/Array.from.generator.js: failed 423 + compat-table/es6/Array.from.generator.js: OK 424 424 compat-table/es6/Array.from.iterable-instance.js: OK 425 425 compat-table/es6/Array.from.iterable.js: OK 426 426 compat-table/es6/Array.from.iterator-closing.js: OK 427 427 compat-table/es6/Array.from.map.array-like.js: OK 428 - compat-table/es6/Array.from.map.generator.js: failed 428 + compat-table/es6/Array.from.map.generator.js: OK 429 429 compat-table/es6/Array.from.map.iterable-instance.js: OK 430 430 compat-table/es6/Array.from.map.iterable.js: OK 431 431 compat-table/es6/Array.iterator-prototype-chain.js: OK ··· 738 738 compat-table/es6/destructuring-assign.computed-properties.js: OK 739 739 compat-table/es6/destructuring-assign.defaults.js: OK 740 740 compat-table/es6/destructuring-assign.empty-patterns.js: OK 741 - compat-table/es6/destructuring-assign.generator.js: TypeError: not iterable 741 + compat-table/es6/destructuring-assign.generator.js: OK 742 742 compat-table/es6/destructuring-assign.iterable-expression.js: OK 743 743 compat-table/es6/destructuring-assign.iterable-instance.js: OK 744 744 compat-table/es6/destructuring-assign.iterable.js: OK ··· 763 763 compat-table/es6/destructuring-decl.defaults.js: OK 764 764 compat-table/es6/destructuring-decl.for-in.js: OK 765 765 compat-table/es6/destructuring-decl.for-of.js: OK 766 - compat-table/es6/destructuring-decl.generator.js: TypeError: not iterable 766 + compat-table/es6/destructuring-decl.generator.js: OK 767 767 compat-table/es6/destructuring-decl.iterable-instance.js: OK 768 768 compat-table/es6/destructuring-decl.iterable.js: OK 769 769 compat-table/es6/destructuring-decl.iterator-closing.js: OK ··· 787 787 compat-table/es6/destructuring-params.defaults.js: OK 788 788 compat-table/es6/destructuring-params.duplicate-identifier.js: OK 789 789 compat-table/es6/destructuring-params.empty-patterns.js: OK 790 - compat-table/es6/destructuring-params.generator.js: TypeError: not iterable 790 + compat-table/es6/destructuring-params.generator.js: OK 791 791 compat-table/es6/destructuring-params.iterable-instance.js: OK 792 792 compat-table/es6/destructuring-params.iterable.js: OK 793 793 compat-table/es6/destructuring-params.iterator-closing.js: OK ··· 805 805 compat-table/es6/destructuring-params.trailing-comma-object.js: OK 806 806 compat-table/es6/for-of.array.js: OK 807 807 compat-table/es6/for-of.astral-string.js: OK 808 - compat-table/es6/for-of.generator.js: TypeError: not iterable 808 + compat-table/es6/for-of.generator.js: OK 809 809 compat-table/es6/for-of.iterable-instance.js: OK 810 810 compat-table/es6/for-of.iterable.js: OK 811 811 compat-table/es6/for-of.iterator-closing-break.js: OK ··· 813 813 compat-table/es6/for-of.sparse-array.js: OK 814 814 compat-table/es6/for-of.string.js: OK 815 815 compat-table/es6/function.block-level-decl.js: OK 816 - compat-table/es6/generators.basic.js: TypeError: Cannot read properties of undefined (reading 'next') 817 - compat-table/es6/generators.constructor.js: TypeError: Cannot read properties of undefined (reading 'next') 818 - compat-table/es6/generators.expression.js: TypeError: Cannot read properties of undefined (reading 'next') 816 + compat-table/es6/generators.basic.js: OK 817 + compat-table/es6/generators.constructor.js: OK 818 + compat-table/es6/generators.expression.js: OK 819 819 compat-table/es6/generators.no-new-this.js: OK 820 - compat-table/es6/generators.prototype-chain.js: TypeError: Cannot read properties of null (reading 'hasOwnProperty') 821 - compat-table/es6/generators.prototype.js: failed 822 - compat-table/es6/generators.return.js: TypeError: Cannot read properties of undefined (reading 'next') 823 - compat-table/es6/generators.sending.js: TypeError: Cannot read properties of undefined (reading 'next') 824 - compat-table/es6/generators.shorthand.class.js: TypeError: Cannot read properties of undefined (reading 'next') 825 - compat-table/es6/generators.shorthand.computed.class.js: TypeError: Cannot read properties of undefined (reading 'next') 826 - compat-table/es6/generators.shorthand.computed.js: SyntaxError: Unexpected token '}' 827 - compat-table/es6/generators.shorthand.js: SyntaxError: Unexpected token '}' 828 - compat-table/es6/generators.shorthand.no-constructor.js: failed 829 - compat-table/es6/generators.shorthand.string-keyed.js: SyntaxError: Unexpected token '}' 830 - compat-table/es6/generators.this.js: TypeError: Cannot read properties of undefined (reading 'next') 831 - compat-table/es6/generators.throw.js: TypeError: Cannot read properties of undefined (reading 'next') 832 - compat-table/es6/generators.yield-precedence.js: TypeError: Cannot read properties of undefined (reading 'next') 833 - compat-table/es6/generators.yield-star.array.js: TypeError: Cannot read properties of undefined (reading 'next') 834 - compat-table/es6/generators.yield-star.astral-string.js: TypeError: Cannot read properties of undefined (reading 'next') 835 - compat-table/es6/generators.yield-star.generator.js: TypeError: Cannot read properties of undefined (reading 'next') 836 - compat-table/es6/generators.yield-star.iterable-instance.js: TypeError: Cannot read properties of undefined (reading 'next') 837 - compat-table/es6/generators.yield-star.iterable.js: TypeError: Cannot read properties of undefined (reading 'next') 838 - compat-table/es6/generators.yield-star.iterator-closing-throw.js: TypeError: Cannot read properties of undefined (reading 'next') 839 - compat-table/es6/generators.yield-star.iterator-closing.js: TypeError: Cannot read properties of undefined (reading 'next') 840 - compat-table/es6/generators.yield-star.non-iterable-error.js: TypeError: Cannot read properties of undefined (reading 'next') 841 - compat-table/es6/generators.yield-star.sparse-array.js: TypeError: Cannot read properties of undefined (reading 'next') 842 - compat-table/es6/generators.yield-star.string.js: TypeError: Cannot read properties of undefined (reading 'next') 820 + compat-table/es6/generators.prototype-chain.js: OK 821 + compat-table/es6/generators.prototype.js: OK 822 + compat-table/es6/generators.return.js: OK 823 + compat-table/es6/generators.sending.js: OK 824 + compat-table/es6/generators.shorthand.class.js: OK 825 + compat-table/es6/generators.shorthand.computed.class.js: OK 826 + compat-table/es6/generators.shorthand.computed.js: OK 827 + compat-table/es6/generators.shorthand.js: OK 828 + compat-table/es6/generators.shorthand.no-constructor.js: OK 829 + compat-table/es6/generators.shorthand.string-keyed.js: OK 830 + compat-table/es6/generators.this.js: OK 831 + compat-table/es6/generators.throw.js: OK 832 + compat-table/es6/generators.yield-precedence.js: OK 833 + compat-table/es6/generators.yield-star.array.js: OK 834 + compat-table/es6/generators.yield-star.astral-string.js: OK 835 + compat-table/es6/generators.yield-star.generator.js: OK 836 + compat-table/es6/generators.yield-star.iterable-instance.js: OK 837 + compat-table/es6/generators.yield-star.iterable.js: OK 838 + compat-table/es6/generators.yield-star.iterator-closing-throw.js: OK 839 + compat-table/es6/generators.yield-star.iterator-closing.js: OK 840 + compat-table/es6/generators.yield-star.non-iterable-error.js: OK 841 + compat-table/es6/generators.yield-star.sparse-array.js: OK 842 + compat-table/es6/generators.yield-star.string.js: OK 843 843 compat-table/es6/let.basic.js: OK 844 844 compat-table/es6/let.block-scoped.js: OK 845 845 compat-table/es6/let.for-in-shadow.js: OK ··· 941 941 compat-table/es6/misc.Proxy.set.Object.assign.js: OK 942 942 compat-table/es6/misc.RegExp-alter-flags.js: OK 943 943 compat-table/es6/misc.RegExp-prototype-toString-generic.js: OK 944 - compat-table/es6/misc.accessors-no-constructor.js: failed 945 - compat-table/es6/misc.bound-function-prototype.arrow.js: failed 946 - compat-table/es6/misc.bound-function-prototype.class.js: failed 947 - compat-table/es6/misc.bound-function-prototype.function.js: failed 948 - compat-table/es6/misc.bound-function-prototype.generator.js: failed 944 + compat-table/es6/misc.accessors-no-constructor.js: OK 945 + compat-table/es6/misc.bound-function-prototype.arrow.js: OK 946 + compat-table/es6/misc.bound-function-prototype.class.js: OK 947 + compat-table/es6/misc.bound-function-prototype.function.js: OK 948 + compat-table/es6/misc.bound-function-prototype.generator.js: OK 949 949 compat-table/es6/misc.bound-function-prototype.subclass.js: OK 950 950 compat-table/es6/misc.do-while-no-semicolon.js: OK 951 951 compat-table/es6/misc.duplicate-properties-strict.js: OK ··· 979 979 compat-table/es6/spread.array-literal.js: OK 980 980 compat-table/es6/spread.astral-call.js: OK 981 981 compat-table/es6/spread.astral-literal.js: OK 982 - compat-table/es6/spread.generator-call.js: TypeError: not iterable 983 - compat-table/es6/spread.generator-literal.js: TypeError: not iterable 982 + compat-table/es6/spread.generator-call.js: OK 983 + compat-table/es6/spread.generator-literal.js: OK 984 984 compat-table/es6/spread.iterable-call.js: OK 985 985 compat-table/es6/spread.iterable-instance-call.js: OK 986 986 compat-table/es6/spread.iterable-instance-literal.js: OK ··· 1125 1125 compat-table/es2016/exponentiation.unary-negation-error.js: OK 1126 1126 compat-table/es2016/misc.Proxy-Array-includes.js: failed 1127 1127 compat-table/es2016/misc.Proxy-enumerate-removed.js: OK 1128 - compat-table/es2016/misc.generator-no-new.js: failed 1129 - compat-table/es2016/misc.generator-throw-inner.js: TypeError: Cannot read properties of undefined (reading 'next') 1128 + compat-table/es2016/misc.generator-no-new.js: OK 1129 + compat-table/es2016/misc.generator-throw-inner.js: OK 1130 1130 compat-table/es2016/misc.nested-rest-destructuring-decl.js: OK 1131 1131 compat-table/es2016/misc.nested-rest-destructuring-params.js: OK 1132 1132 compat-table/es2016/misc.strict-fn-non-simple-params-error.js: failed ··· 1198 1198 compat-table/es2018/Promise.prototype.finally.js: OK 1199 1199 compat-table/es2018/Promise.prototype.finally.no-change-resolution.js: OK 1200 1200 compat-table/es2018/async-iterators.for-await-of.js: OK 1201 - compat-table/es2018/async-iterators.generators.js: TypeError: undefined is not a function 1201 + compat-table/es2018/async-iterators.generators.js: OK 1202 1202 compat-table/es2018/misc.Proxy-ownKeys-duplicate-keys.js: OK 1203 1203 compat-table/es2018/misc.template-literal-revision.js: OK 1204 1204 compat-table/es2018/object-rest.js: OK ··· 1239 1239 compat-table/es2019/misc.JSON-superset.paragraph-separator.js: OK 1240 1240 compat-table/es2019/misc.optional-catch-binding.await.js: OK 1241 1241 compat-table/es2019/misc.optional-catch-binding.js: OK 1242 - compat-table/es2019/misc.optional-catch-binding.yield.js: TypeError: Cannot read properties of undefined (reading 'next') 1242 + compat-table/es2019/misc.optional-catch-binding.yield.js: OK 1243 1243 compat-table/es2020/BigInt64Array.js: failed 1244 1244 compat-table/es2020/BigInt.asIntN.js: OK 1245 1245 compat-table/es2020/BigInt.asUintN.js: OK
+387
examples/spec/generators.js
··· 1 + import { test, testDeep, summary } from './helpers.js'; 2 + 3 + console.log('Generator Tests\n'); 4 + 5 + function* simple() { 6 + yield 1; 7 + yield 2; 8 + yield 3; 9 + } 10 + 11 + const gen = simple(); 12 + test('generator object typeof', typeof gen, 'object'); 13 + test('generator is iterable', gen[Symbol.iterator](), gen); 14 + 15 + try { 16 + new simple(); 17 + test('new generator function throws', false, true); 18 + } catch { 19 + test('new generator function throws', true, true); 20 + } 21 + 22 + test('first yield', gen.next().value, 1); 23 + test('second yield', gen.next().value, 2); 24 + test('third yield', gen.next().value, 3); 25 + test('done', gen.next().done, true); 26 + 27 + function* withReturn() { 28 + yield 1; 29 + return 'final'; 30 + } 31 + 32 + const gen2 = withReturn(); 33 + test('yield before return', gen2.next().value, 1); 34 + const last = gen2.next(); 35 + test('return value', last.value, 'final'); 36 + test('return done', last.done, true); 37 + 38 + function* counter(start) { 39 + let i = start; 40 + while (true) yield i++; 41 + } 42 + 43 + const c = counter(10); 44 + test('counter 10', c.next().value, 10); 45 + test('counter 11', c.next().value, 11); 46 + test('counter 12', c.next().value, 12); 47 + 48 + function* range(start, end) { 49 + for (let i = start; i <= end; i++) yield i; 50 + } 51 + 52 + const arr = [...range(1, 5)]; 53 + testDeep('spread generator', arr, [1, 2, 3, 4, 5]); 54 + 55 + let forOfSum = 0; 56 + for (const n of range(1, 4)) { 57 + forOfSum += n; 58 + } 59 + test('for-of generator', forOfSum, 10); 60 + 61 + function* twoWay() { 62 + const x = yield 1; 63 + const y = yield x + 1; 64 + yield y + 1; 65 + } 66 + 67 + const tw = twoWay(); 68 + test('two way first', tw.next().value, 1); 69 + test('two way second', tw.next(10).value, 11); 70 + test('two way third', tw.next(20).value, 21); 71 + 72 + function runDynamicBool(source) { 73 + try { 74 + return Function(source)(); 75 + } catch { 76 + return false; 77 + } 78 + } 79 + 80 + function* yieldStarArray() { 81 + yield* [4, 5, 6]; 82 + } 83 + 84 + testDeep('yield* array', [...yieldStarArray()], [4, 5, 6]); 85 + 86 + function* yieldStarString() { 87 + yield* 'a\uD83D\uDCABb'; 88 + } 89 + 90 + testDeep('yield* string keeps code points', [...yieldStarString()], ['a', '\uD83D\uDCAB', 'b']); 91 + 92 + function* yieldStarSparseArray() { 93 + yield* [7, , 9]; 94 + } 95 + 96 + const sparse = yieldStarSparseArray(); 97 + test('yield* sparse array first', sparse.next().value, 7); 98 + test('yield* sparse array hole value', sparse.next().value, undefined); 99 + test('yield* sparse array third', sparse.next().value, 9); 100 + test('yield* sparse array done', sparse.next().done, true); 101 + 102 + function makeIterable(values, methods = {}) { 103 + values = values.slice(); 104 + values.push(undefined); 105 + const iterator = { 106 + next(value) { 107 + if (methods.next) return methods.next.call(this, value); 108 + return { value: values.shift(), done: values.length === 0 }; 109 + }, 110 + return: methods.return, 111 + throw: methods.throw 112 + }; 113 + return { 114 + [Symbol.iterator]() { 115 + return iterator; 116 + } 117 + }; 118 + } 119 + 120 + function* yieldStarIterable() { 121 + yield* makeIterable([10, 11, 12]); 122 + } 123 + 124 + testDeep('yield* generic iterable', [...yieldStarIterable()], [10, 11, 12]); 125 + 126 + function* yieldStarIterableInstance() { 127 + yield* Object.create(makeIterable([13, 14])); 128 + } 129 + 130 + testDeep('yield* iterable through prototype', [...yieldStarIterableInstance()], [13, 14]); 131 + 132 + function* innerGenerator() { 133 + yield 15; 134 + yield 16; 135 + return 17; 136 + } 137 + 138 + function* yieldStarGenerator() { 139 + return yield* innerGenerator(); 140 + } 141 + 142 + const ysg = yieldStarGenerator(); 143 + test('yield* generator first', ysg.next().value, 15); 144 + test('yield* generator second', ysg.next().value, 16); 145 + const ysgDone = ysg.next(); 146 + test('yield* generator return value', ysgDone.value, 17); 147 + test('yield* generator return done', ysgDone.done, true); 148 + 149 + function* yieldStarReceivesSentValues() { 150 + return yield* (function* () { 151 + const received = yield 'ready'; 152 + return received + 1; 153 + })(); 154 + } 155 + 156 + const yss = yieldStarReceivesSentValues(); 157 + test('yield* sent first', yss.next().value, 'ready'); 158 + const yssDone = yss.next(40); 159 + test('yield* sent return value', yssDone.value, 41); 160 + test('yield* sent return done', yssDone.done, true); 161 + 162 + let yieldStarThrowCaught = false; 163 + function* yieldStarThrowForwarding() { 164 + yield* (function* () { 165 + try { 166 + yield 'before throw'; 167 + } catch (e) { 168 + yieldStarThrowCaught = e === 'boom'; 169 + return 'caught'; 170 + } 171 + })(); 172 + yield 'after throw'; 173 + } 174 + 175 + const yst = yieldStarThrowForwarding(); 176 + test('yield* throw forwarding first', yst.next().value, 'before throw'); 177 + test('yield* throw forwarding continues outer', yst.throw('boom').value, 'after throw'); 178 + test('yield* throw forwarding caught by delegate', yieldStarThrowCaught, true); 179 + 180 + let yieldStarClosed = ''; 181 + const closingIter = makeIterable([1, 2, 3], { 182 + return() { 183 + yieldStarClosed += 'a'; 184 + return { done: true }; 185 + } 186 + }); 187 + 188 + function* yieldStarReturnClosing() { 189 + try { 190 + yield* closingIter; 191 + } finally { 192 + yieldStarClosed += 'b'; 193 + } 194 + } 195 + 196 + const ysrc = yieldStarReturnClosing(); 197 + test('yield* return closing first', ysrc.next().value, 1); 198 + ysrc.return('closed'); 199 + test('yield* return calls delegate and finally', yieldStarClosed, 'ab'); 200 + 201 + let yieldStarThrowClosed = false; 202 + const noThrowIter = makeIterable([1, 2], { 203 + throw: undefined, 204 + return() { 205 + yieldStarThrowClosed = true; 206 + return { done: true }; 207 + } 208 + }); 209 + 210 + function* yieldStarThrowClosing() { 211 + try { 212 + yield* noThrowIter; 213 + } catch {} 214 + } 215 + 216 + const ystc = yieldStarThrowClosing(); 217 + test('yield* throw closing first', ystc.next().value, 1); 218 + ystc.throw('close please'); 219 + test('yield* throw without delegate throw closes iterator', yieldStarThrowClosed, true); 220 + 221 + try { 222 + (function* () { 223 + yield* 123; 224 + })().next(); 225 + test('yield* non-iterable throws', false, true); 226 + } catch { 227 + test('yield* non-iterable throws', true, true); 228 + } 229 + 230 + function* catchesThrow() { 231 + try { 232 + yield 'waiting'; 233 + } catch (e) { 234 + return e; 235 + } 236 + } 237 + 238 + const ct = catchesThrow(); 239 + test('generator throw setup', ct.next().value, 'waiting'); 240 + const ctDone = ct.throw('thrown value'); 241 + test('generator throw caught value', ctDone.value, 'thrown value'); 242 + test('generator throw caught done', ctDone.done, true); 243 + 244 + let finallyRan = false; 245 + function* returnRunsFinally() { 246 + try { 247 + yield 'open'; 248 + } finally { 249 + finallyRan = true; 250 + } 251 + } 252 + 253 + const rrf = returnRunsFinally(); 254 + test('generator return setup', rrf.next().value, 'open'); 255 + const rrfDone = rrf.return('done now'); 256 + test('generator return result value', rrfDone.value, 'done now'); 257 + test('generator return result done', rrfDone.done, true); 258 + test('generator return runs finally', finallyRan, true); 259 + 260 + function* protoShapeFn() {} 261 + const protoShapeGen = protoShapeFn(); 262 + const ownGeneratorProto = Object.getPrototypeOf(protoShapeGen); 263 + const sharedGeneratorProto = Object.getPrototypeOf(ownGeneratorProto); 264 + const iteratorProto = Object.getPrototypeOf(sharedGeneratorProto); 265 + test('generator instance uses function prototype', ownGeneratorProto, protoShapeFn.prototype); 266 + test('generator shared prototype has next', sharedGeneratorProto.hasOwnProperty('next'), true); 267 + test('generator Symbol.iterator inherited from iterator prototype', iteratorProto.hasOwnProperty(Symbol.iterator), true); 268 + 269 + test( 270 + 'shorthand object generator method', 271 + runDynamicBool(` 272 + const o = { 273 + *generator() { 274 + yield 5; 275 + yield 6; 276 + } 277 + }; 278 + const it = o.generator(); 279 + return it.next().value === 5 && 280 + it.next().value === 6 && 281 + it.next().done === true; 282 + `), 283 + true 284 + ); 285 + 286 + test( 287 + 'shorthand computed generator method', 288 + runDynamicBool(` 289 + const name = 'generator'; 290 + const o = { 291 + *[name]() { 292 + yield 7; 293 + yield 8; 294 + } 295 + }; 296 + const it = o.generator(); 297 + return it.next().value === 7 && 298 + it.next().value === 8 && 299 + it.next().done === true; 300 + `), 301 + true 302 + ); 303 + 304 + test( 305 + 'shorthand string-keyed generator method', 306 + runDynamicBool(` 307 + const o = { 308 + *"foo bar"() { 309 + yield 9; 310 + yield 10; 311 + } 312 + }; 313 + const it = o['foo bar'](); 314 + return it.next().value === 9 && 315 + it.next().value === 10 && 316 + it.next().done === true; 317 + `), 318 + true 319 + ); 320 + 321 + test( 322 + 'class generator method', 323 + runDynamicBool(` 324 + class C { 325 + *generator() { 326 + yield 11; 327 + yield 12; 328 + } 329 + } 330 + const it = new C().generator(); 331 + return it.next().value === 11 && 332 + it.next().value === 12 && 333 + it.next().done === true; 334 + `), 335 + true 336 + ); 337 + 338 + test( 339 + 'class computed generator method', 340 + runDynamicBool(` 341 + const name = 'generator'; 342 + class C { 343 + *[name]() { 344 + yield 13; 345 + yield 14; 346 + } 347 + } 348 + const it = new C().generator(); 349 + return it.next().value === 13 && 350 + it.next().value === 14 && 351 + it.next().done === true; 352 + `), 353 + true 354 + ); 355 + 356 + test( 357 + 'generator method cannot be constructor', 358 + runDynamicBool(` 359 + const o = { 360 + *generator() { 361 + yield 1; 362 + } 363 + }; 364 + try { 365 + new o.generator(); 366 + return false; 367 + } catch { 368 + return true; 369 + } 370 + `), 371 + true 372 + ); 373 + 374 + test( 375 + 'class generator constructor syntax rejected', 376 + runDynamicBool(` 377 + try { 378 + Function('class Bad { *constructor() { yield 1; } }'); 379 + return false; 380 + } catch { 381 + return true; 382 + } 383 + `), 384 + true 385 + ); 386 + 387 + summary();
-60
examples/spec/generators.js.disabled
··· 1 - import { test, testDeep, summary } from './helpers.js'; 2 - 3 - console.log('Generator Tests\n'); 4 - 5 - function* simple() { 6 - yield 1; 7 - yield 2; 8 - yield 3; 9 - } 10 - 11 - const gen = simple(); 12 - test('first yield', gen.next().value, 1); 13 - test('second yield', gen.next().value, 2); 14 - test('third yield', gen.next().value, 3); 15 - test('done', gen.next().done, true); 16 - 17 - function* withReturn() { 18 - yield 1; 19 - return 'final'; 20 - } 21 - 22 - const gen2 = withReturn(); 23 - test('yield before return', gen2.next().value, 1); 24 - const last = gen2.next(); 25 - test('return value', last.value, 'final'); 26 - test('return done', last.done, true); 27 - 28 - function* counter(start) { 29 - let i = start; 30 - while (true) { 31 - yield i++; 32 - } 33 - } 34 - 35 - const c = counter(10); 36 - test('counter 10', c.next().value, 10); 37 - test('counter 11', c.next().value, 11); 38 - test('counter 12', c.next().value, 12); 39 - 40 - function* range(start, end) { 41 - for (let i = start; i <= end; i++) { 42 - yield i; 43 - } 44 - } 45 - 46 - const arr = [...range(1, 5)]; 47 - testDeep('spread generator', arr, [1, 2, 3, 4, 5]); 48 - 49 - function* twoWay() { 50 - const x = yield 1; 51 - const y = yield x + 1; 52 - yield y + 1; 53 - } 54 - 55 - const tw = twoWay(); 56 - test('two way first', tw.next().value, 1); 57 - test('two way second', tw.next(10).value, 11); 58 - test('two way third', tw.next(20).value, 21); 59 - 60 - summary();
+1
include/ant.h
··· 66 66 ant_value_t js_mknull(void); 67 67 ant_value_t js_mknum(double); 68 68 ant_value_t js_mkpromise(ant_t *js); 69 + ant_value_t js_mkgenerator(ant_t *js); 69 70 70 71 ant_value_t js_getthis(ant_t *); 71 72 void js_setthis(ant_t *, ant_value_t);
+3 -1
include/common.h
··· 15 15 X(SLOT_PROTO) \ 16 16 X(SLOT_FUNC_PROTO) \ 17 17 X(SLOT_ASYNC_PROTO) \ 18 + X(SLOT_GENERATOR_PROTO) \ 18 19 X(SLOT_AUX) \ 19 20 X(SLOT_TARGET_FUNC) \ 20 21 X(SLOT_MODULE_CTX) \ ··· 73 74 X(SLOT_PIPE_ABORT_LISTENER) \ 74 75 X(SLOT_MATCHALL_RX) \ 75 76 X(SLOT_MATCHALL_STR) \ 76 - X(SLOT_MATCHALL_DONE) 77 + X(SLOT_MATCHALL_DONE) \ 78 + X(SLOT_GENERATOR_STATE) 77 79 78 80 #define ANT_DECLARE_INTERNAL_SLOT(name) name, 79 81 typedef enum {
+4 -4
include/gc.h
··· 12 12 13 13 #define GC_HEAP_GROWTH(n) ((n) + (n) / 2) 14 14 15 - #define GC_OBJ_TYPE_MASK (JS_TYPE_FLAG(T_OBJ) \ 16 - | JS_TYPE_FLAG(T_ARR) \ 17 - | JS_TYPE_FLAG(T_PROMISE) \ 18 - | JS_TYPE_FLAG(T_GENERATOR)) 15 + #define GC_OBJ_TYPE_MASK (JS_TPFLG(T_OBJ) \ 16 + | JS_TPFLG(T_ARR) \ 17 + | JS_TPFLG(T_PROMISE) \ 18 + | JS_TPFLG(T_GENERATOR)) 19 19 20 20 typedef struct gc_func_mark_profile { 21 21 bool enabled;
+7 -6
include/internal.h
··· 59 59 #define NANBOX_PREFIX 0xFFF0000000000000ULL 60 60 #define NANBOX_DATA_MASK 0x00007FFFFFFFFFFFULL 61 61 62 - #define JS_ERR_NO_STACK (1 << 8) 63 - #define JS_TYPE_FLAG(t) (1u << (t)) 62 + #define JS_ERR_NO_STACK (1 << 8) 63 + #define JS_TPFLG(t) (1u << (t)) 64 64 65 65 #define MAX_STRINGIFY_DEPTH 64 66 66 #define MAX_PROTO_CHAIN_DEPTH 256 ··· 74 74 #define ROPE_FLATTEN_THRESHOLD (32 * 1024) 75 75 76 76 #define T_EMPTY (NANBOX_PREFIX | ((ant_value_t)T_SENTINEL << NANBOX_TYPE_SHIFT) | 0xDEADULL) 77 - #define T_SPECIAL_OBJECT_MASK (JS_TYPE_FLAG(T_OBJ) | JS_TYPE_FLAG(T_ARR)) 78 - #define T_NEEDS_PROTO_FALLBACK (JS_TYPE_FLAG(T_FUNC) | JS_TYPE_FLAG(T_ARR) | JS_TYPE_FLAG(T_PROMISE)) 79 - #define T_OBJECT_MASK (JS_TYPE_FLAG(T_OBJ) | JS_TYPE_FLAG(T_ARR) | JS_TYPE_FLAG(T_FUNC) | JS_TYPE_FLAG(T_PROMISE)) 80 - #define T_NON_NUMERIC_MASK (JS_TYPE_FLAG(T_STR) | JS_TYPE_FLAG(T_ARR) | JS_TYPE_FLAG(T_FUNC) | JS_TYPE_FLAG(T_CFUNC) | JS_TYPE_FLAG(T_OBJ)) 77 + #define T_SPECIAL_OBJECT_MASK (JS_TPFLG(T_OBJ) | JS_TPFLG(T_ARR)) 78 + #define T_NEEDS_PROTO_FALLBACK (JS_TPFLG(T_FUNC) | JS_TPFLG(T_ARR) | JS_TPFLG(T_PROMISE) | JS_TPFLG(T_GENERATOR)) 79 + #define T_OBJECT_MASK (JS_TPFLG(T_OBJ) | JS_TPFLG(T_ARR) | JS_TPFLG(T_FUNC) | JS_TPFLG(T_PROMISE) | JS_TPFLG(T_GENERATOR)) 80 + #define T_NON_NUMERIC_MASK (JS_TPFLG(T_STR) | JS_TPFLG(T_ARR) | JS_TPFLG(T_FUNC) | JS_TPFLG(T_CFUNC) | JS_TPFLG(T_OBJ) | JS_TPFLG(T_GENERATOR)) 81 81 82 82 #define is_non_numeric(v) ((1u << vtype(v)) & T_NON_NUMERIC_MASK) 83 83 #define is_object_type(v) ((1u << vtype(v)) & T_OBJECT_MASK) ··· 194 194 ant_value_t iterator_proto; 195 195 ant_value_t array_iterator_proto; 196 196 ant_value_t string_iterator_proto; 197 + ant_value_t generator_proto; 197 198 } sym; 198 199 199 200 ant_offset_t max_size;
+6
include/modules/generator.h
··· 1 + #ifndef GENERATOR_H 2 + #define GENERATOR_H 3 + 4 + void init_generator_module(void); 5 + 6 + #endif
+1 -1
include/silver/compiler.h
··· 27 27 28 28 sv_func_t *sv_compile_function( 29 29 ant_t *js, const char *source, 30 - size_t len, bool is_async 30 + size_t len, bool is_async, bool is_generator 31 31 ); 32 32 33 33 sv_func_t *sv_compile_function_with_params(
+27
include/silver/engine.h
··· 202 202 ant_value_t value; 203 203 } sv_completion_t; 204 204 205 + typedef enum { 206 + SV_RESUME_NEXT = 0, 207 + SV_RESUME_THROW = 1, 208 + SV_RESUME_RETURN = 2, 209 + } sv_resume_kind_t; 210 + 205 211 typedef struct 206 212 sv_upvalue sv_upvalue_t; 207 213 ··· 334 340 bool suspended; 335 341 bool suspended_resume_pending; 336 342 bool suspended_resume_is_error; 343 + sv_resume_kind_t suspended_resume_kind; 337 344 338 345 int suspended_entry_fp; 339 346 int suspended_saved_fp; ··· 753 760 ant_value_t this_val, ant_value_t *args, int argc 754 761 ); 755 762 763 + ant_value_t sv_call_generator_closure_dispatch( 764 + sv_vm_t *vm, ant_t *js, sv_closure_t *closure, 765 + ant_value_t callee_func, ant_value_t super_val, 766 + ant_value_t this_val, ant_value_t *args, int argc 767 + ); 768 + 756 769 static inline ant_value_t sv_call_async_closure( 757 770 sv_vm_t *vm, ant_t *js, sv_closure_t *closure, 758 771 ant_value_t callee_func, sv_call_ctx_t *ctx 759 772 ) { 760 773 ant_value_t result = sv_call_async_closure_dispatch( 774 + vm, js, closure, callee_func, 775 + ctx->super_val, ctx->this_val, ctx->args, ctx->argc 776 + ); 777 + sv_call_cleanup(js, ctx); 778 + return result; 779 + } 780 + 781 + static inline ant_value_t sv_call_generator_closure( 782 + sv_vm_t *vm, ant_t *js, sv_closure_t *closure, 783 + ant_value_t callee_func, sv_call_ctx_t *ctx 784 + ) { 785 + ant_value_t result = sv_call_generator_closure_dispatch( 761 786 vm, js, closure, callee_func, 762 787 ctx->super_val, ctx->this_val, ctx->args, ctx->argc 763 788 ); ··· 1009 1034 sv_vm_t *vm, ant_t *js, sv_closure_t *closure, 1010 1035 ant_value_t callee_func, sv_call_ctx_t *ctx, ant_value_t *out_this 1011 1036 ) { 1037 + if (closure->func->is_generator) 1038 + return sv_call_generator_closure(vm, js, closure, callee_func, ctx); 1012 1039 if (closure->func->is_async) 1013 1040 return sv_call_async_closure(vm, js, closure, callee_func, ctx); 1014 1041 #ifdef ANT_JIT
+4 -2
include/silver/opcode.h
··· 195 195 196 196 OP_DEF( AWAIT, 1, 1, 1, none) /* promise -> resolved value */ 197 197 OP_DEF( YIELD, 1, 1, 2, none) /* val -> received */ 198 - OP_DEF( YIELD_STAR, 1, 1, 2, none) /* delegate to sub-iterator */ 199 - OP_DEF( INITIAL_YIELD, 1, 0, 0, none) /* generator entry point */ 198 + OP_DEF( YIELD_STAR_INIT, 3, 1, 0, loc) /* iterable -> delegate locals */ 199 + OP_DEF( YIELD_STAR_NEXT, 3, 1, 2, loc) /* sent -> final value | suspend */ 200 + OP_DEF( YIELD_STAR_THROW, 3, 1, 2, loc) /* thrown -> final value | suspend */ 201 + OP_DEF( YIELD_STAR_RETURN, 3, 1, 2, loc) /* return value -> final value | suspend */ 200 202 OP_DEF( SPREAD, 1, 1, 0, none) /* arr iterable -> arr */ 201 203 202 204 OP_DEF( DEFINE_METHOD, 6, 2, 1, atom_u8) /* obj func -> obj (flags: get/set/static) */
+1 -1
meson/ant.version
··· 1 - 0.9.0 1 + 0.9.1
+82 -22
src/ant.c
··· 456 456 [T_NUM] = "number", [T_BIGINT] = "bigint", [T_STR] = "string", 457 457 [T_SYMBOL] = "symbol", [T_OBJ] = "object", [T_ARR] = "object", 458 458 [T_FUNC] = "function", [T_CFUNC] = "function", [T_CLOSURE] = "closure", 459 - [T_PROMISE] = "object", [T_GENERATOR] = "generator", 459 + [T_PROMISE] = "object", [T_GENERATOR] = "object", 460 460 [T_ERR] = "err", [T_TYPEDARRAY] = "typedarray", 461 461 [T_FFI] = "ffi", [T_NTARG] = "ntarg" 462 462 }; ··· 641 641 642 642 bool js_truthy(ant_t *js, ant_value_t v) { 643 643 static const void *dispatch[] = { 644 - [T_OBJ] = &&l_true, 645 - [T_FUNC] = &&l_true, 646 - [T_CFUNC] = &&l_true, 647 - [T_ARR] = &&l_true, 648 - [T_PROMISE] = &&l_true, 649 - [T_SYMBOL] = &&l_true, 650 - [T_BOOL] = &&l_bool, 651 - [T_STR] = &&l_str, 652 - [T_BIGINT] = &&l_bigint, 653 - [T_NUM] = &&l_num, 644 + [T_OBJ] = &&l_true, 645 + [T_FUNC] = &&l_true, 646 + [T_CFUNC] = &&l_true, 647 + [T_ARR] = &&l_true, 648 + [T_PROMISE] = &&l_true, 649 + [T_GENERATOR] = &&l_true, 650 + [T_SYMBOL] = &&l_true, 651 + [T_BOOL] = &&l_bool, 652 + [T_STR] = &&l_str, 653 + [T_BIGINT] = &&l_bigint, 654 + [T_NUM] = &&l_num, 654 655 }; 655 656 656 657 uint8_t t = vtype(v); ··· 2731 2732 return true; 2732 2733 } 2733 2734 2734 - if (ct == T_OBJ || ct == T_ARR || ct == T_FUNC || ct == T_PROMISE) { 2735 + if (JS_TPFLG(ct) & T_OBJECT_MASK) { 2735 2736 ant_value_t as_obj = js_as_obj(*cur); 2736 2737 ant_value_t proto = get_slot(as_obj, SLOT_PROTO); 2737 2738 ··· 2742 2743 return true; 2743 2744 } 2744 2745 2745 - if (JS_TYPE_FLAG(ct) & T_NEEDS_PROTO_FALLBACK) { 2746 + if (JS_TPFLG(ct) & T_NEEDS_PROTO_FALLBACK) { 2746 2747 ant_value_t fallback = get_prototype_for_type(js, ct); 2747 2748 uint8_t ft = vtype(fallback); 2748 2749 if (ft == T_NULL || ft == T_UNDEF) return false; ··· 3790 3791 3791 3792 ant_object_t *ptr = js_obj_ptr(as_obj); 3792 3793 ant_value_t proto = ptr ? ptr->proto : js_mkundef(); 3793 - if (is_object_type(proto)) return proto; 3794 + if (is_object_type(proto) || vtype(proto) == T_NULL) return proto; 3794 3795 3795 3796 if (t != T_OBJ) return get_prototype_for_type(js, t); 3796 3797 return js_mknull(); ··· 3850 3851 case T_BOOL: return get_ctor_proto(js, "Boolean", 7); 3851 3852 case T_FUNC: return get_ctor_proto(js, "Function", 8); 3852 3853 case T_PROMISE: return get_ctor_proto(js, "Promise", 7); 3854 + case T_GENERATOR: return js->sym.generator_proto; 3853 3855 case T_BIGINT: return get_ctor_proto(js, "BigInt", 6); 3854 3856 case T_SYMBOL: return get_ctor_proto(js, "Symbol", 6); 3855 3857 default: return js_mknull(); ··· 3865 3867 proto_walk_overflow_guard_init(&guard); 3866 3868 3867 3869 while (true) { 3868 - if (t == T_OBJ || t == T_ARR || t == T_FUNC || t == T_PROMISE) { 3870 + if (t == T_OBJ || t == T_ARR || t == T_FUNC || t == T_PROMISE || t == T_GENERATOR) { 3869 3871 ant_value_t as_obj = js_as_obj(cur); 3870 3872 ant_offset_t off = lkp_interned(js, as_obj, key_intern, len); 3871 3873 if (off != 0) return off; ··· 4624 4626 4625 4627 static ant_value_t builtin_function_empty(ant_t *, ant_value_t *, int); 4626 4628 4627 - static ant_value_t build_dynamic_function(ant_t *js, ant_value_t *args, int nargs, bool is_async) { 4629 + static ant_value_t build_dynamic_function(ant_t *js, ant_value_t *args, int nargs, bool is_async, bool is_generator) { 4628 4630 if (nargs == 0) { 4629 4631 ant_value_t func_obj = mkobj(js, 0); 4630 4632 if (is_err(func_obj)) return func_obj; ··· 4634 4636 set_slot(func_obj, SLOT_ASYNC, js_true); 4635 4637 ant_value_t async_proto = get_slot(js_glob(js), SLOT_ASYNC_PROTO); 4636 4638 if (vtype(async_proto) == T_FUNC) js_set_proto_init(func_obj, async_proto); 4639 + } else if (is_generator) { 4640 + ant_value_t generator_proto = get_slot(js_glob(js), SLOT_GENERATOR_PROTO); 4641 + if (vtype(generator_proto) == T_FUNC) js_set_proto_init(func_obj, generator_proto); 4637 4642 } else { 4638 4643 ant_value_t func_proto = get_slot(js_glob(js), SLOT_FUNC_PROTO); 4639 4644 ant_value_t instance_proto = js_instance_proto_from_new_target(js, func_proto); ··· 4685 4690 ant_value_t func_obj = mkobj(js, 0); 4686 4691 if (is_err(func_obj)) { free(code_buf); return func_obj; } 4687 4692 4688 - sv_func_t *compiled = sv_compile_function(js, code_buf, pos, is_async); 4693 + sv_func_t *compiled = sv_compile_function(js, code_buf, pos, is_async, is_generator); 4689 4694 if (!compiled) { 4690 4695 free(code_buf); 4691 4696 return js_mkerr_typed(js, JS_ERR_SYNTAX, "invalid function body"); ··· 4700 4705 4701 4706 size_t params_len = (size_t)(pos - 2) - (size_t)body_len - 2; 4702 4707 const char *async_prefix = is_async ? "async " : ""; 4708 + const char *generator_marker = is_generator ? "*" : ""; 4703 4709 size_t async_len = is_async ? 6 : 0; 4704 - size_t display_len = async_len + 19 + params_len + 5 + (size_t)body_len + 2; 4710 + size_t generator_len = is_generator ? 1 : 0; 4711 + size_t display_len = async_len + 19 + generator_len + params_len + 5 + (size_t)body_len + 2; 4705 4712 char *display = (char *)malloc(display_len + 1); 4706 4713 if (!display) { free(code_buf); return js_mkerr(js, "oom"); } 4707 4714 size_t n = 0; 4708 4715 4709 4716 memcpy(display + n, async_prefix, async_len); n += async_len; 4710 - memcpy(display + n, "function anonymous(", 19); n += 19; 4717 + memcpy(display + n, "function", 8); n += 8; 4718 + memcpy(display + n, generator_marker, generator_len); n += generator_len; 4719 + memcpy(display + n, " anonymous(", 11); n += 11; 4711 4720 memcpy(display + n, code_buf + 1, params_len); n += params_len; 4712 4721 memcpy(display + n, "\n) {\n", 5); n += 5; 4713 4722 memcpy(display + n, code_buf + 1 + params_len + 2, (size_t)body_len); n += (size_t)body_len; ··· 4721 4730 set_slot(func_obj, SLOT_ASYNC, js_true); 4722 4731 ant_value_t async_proto = get_slot(js_glob(js), SLOT_ASYNC_PROTO); 4723 4732 if (vtype(async_proto) == T_FUNC) js_set_proto_init(func_obj, async_proto); 4733 + } else if (is_generator) { 4734 + ant_value_t generator_proto = get_slot(js_glob(js), SLOT_GENERATOR_PROTO); 4735 + if (vtype(generator_proto) == T_FUNC) js_set_proto_init(func_obj, generator_proto); 4724 4736 } else { 4725 4737 ant_value_t func_proto = get_slot(js_glob(js), SLOT_FUNC_PROTO); 4726 4738 ant_value_t instance_proto = js_instance_proto_from_new_target(js, func_proto); ··· 4730 4742 ant_value_t func = mkval(T_FUNC, (uintptr_t)closure); 4731 4743 ant_value_t proto_setup = setup_func_prototype(js, func); 4732 4744 if (is_err(proto_setup)) return proto_setup; 4745 + if (is_generator) { 4746 + ant_value_t prototype = js_get(js, func, "prototype"); 4747 + if (is_object_type(prototype) && is_object_type(js->sym.generator_proto)) 4748 + js_set_proto_wb(js, prototype, js->sym.generator_proto); 4749 + } 4733 4750 4734 4751 return func; 4735 4752 } 4736 4753 4737 4754 static ant_value_t builtin_Function(ant_t *js, ant_value_t *args, int nargs) { 4738 - return build_dynamic_function(js, args, nargs, false); 4755 + return build_dynamic_function(js, args, nargs, false, false); 4739 4756 } 4740 4757 4741 4758 static ant_value_t builtin_AsyncFunction(ant_t *js, ant_value_t *args, int nargs) { 4742 - return build_dynamic_function(js, args, nargs, true); 4759 + return build_dynamic_function(js, args, nargs, true, false); 4760 + } 4761 + 4762 + static ant_value_t builtin_GeneratorFunction(ant_t *js, ant_value_t *args, int nargs) { 4763 + if (nargs == 0) { 4764 + ant_value_t empty = js_mkstr(js, "", 0); 4765 + if (is_err(empty)) return empty; 4766 + return build_dynamic_function(js, &empty, 1, false, true); 4767 + } 4768 + return build_dynamic_function(js, args, nargs, false, true); 4743 4769 } 4744 4770 4745 4771 static ant_value_t builtin_function_empty(ant_t *js, ant_value_t *args, int nargs) { ··· 4976 5002 } 4977 5003 4978 5004 ant_value_t target_proto = get_proto(js, func); 4979 - if (is_object_type(target_proto)) { 5005 + if (is_object_type(target_proto) || vtype(target_proto) == T_NULL) { 4980 5006 js_set_proto_init(bound_func, target_proto); 4981 5007 } else if (vtype(async_slot) == T_BOOL && vdata(async_slot) == 1) { 4982 5008 ant_value_t async_proto = get_slot(js_glob(js), SLOT_ASYNC_PROTO); ··· 10072 10098 return mkval(T_PROMISE, vdata(obj)); 10073 10099 } 10074 10100 10101 + ant_value_t js_mkgenerator(ant_t *js) { 10102 + ant_value_t obj = mkobj(js, 0); 10103 + if (is_err(obj)) return obj; 10104 + 10105 + ant_object_t *ptr = js_obj_ptr(obj); 10106 + if (!ptr) return js_mkerr(js, "invalid generator object"); 10107 + ptr->type_tag = T_GENERATOR; 10108 + 10109 + ant_value_t gen = mkval(T_GENERATOR, vdata(obj)); 10110 + if (is_object_type(js->sym.generator_proto)) { 10111 + js_set_proto_init(gen, js->sym.generator_proto); 10112 + } 10113 + 10114 + return gen; 10115 + } 10116 + 10075 10117 ant_value_t js_promise_then(ant_t *js, ant_value_t promise, ant_value_t on_fulfilled, ant_value_t on_rejected) { 10076 10118 ant_value_t args_then[2] = { on_fulfilled, on_rejected }; 10077 10119 ant_value_t saved_this = js->this_val; ··· 12683 12725 12684 12726 js_setprop(js, async_func_proto_obj, js_mkstr(js, "constructor", 11), async_func_ctor); 12685 12727 js_set_descriptor(js, async_func_proto_obj, "constructor", 11, JS_DESC_W | JS_DESC_C); 12728 + 12729 + ant_value_t generator_func_proto_obj = js_mkobj(js); 12730 + set_proto(js, generator_func_proto_obj, function_proto); 12731 + ant_value_t generator_func_proto = js_obj_to_func(generator_func_proto_obj); 12732 + set_slot(glob, SLOT_GENERATOR_PROTO, generator_func_proto); 12733 + 12734 + ant_value_t generator_func_ctor_obj = mkobj(js, 0); 12735 + set_proto(js, generator_func_ctor_obj, function_proto); 12736 + set_slot(generator_func_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_GeneratorFunction)); 12737 + js_setprop_nonconfigurable(js, generator_func_ctor_obj, "prototype", 9, generator_func_proto); 12738 + js_setprop(js, generator_func_ctor_obj, js->length_str, tov(1.0)); 12739 + js_set_descriptor(js, generator_func_ctor_obj, "length", 6, JS_DESC_C); 12740 + js_setprop(js, generator_func_ctor_obj, ANT_STRING("name"), ANT_STRING("GeneratorFunction")); 12741 + ant_value_t generator_func_ctor = js_obj_to_func(generator_func_ctor_obj); 12742 + 12743 + js_setprop(js, generator_func_proto_obj, js_mkstr(js, "constructor", 11), generator_func_ctor); 12744 + js_set_descriptor(js, generator_func_proto_obj, "constructor", 11, JS_DESC_W | JS_DESC_C); 12686 12745 12687 12746 ant_value_t str_ctor_obj = mkobj(js, 0); 12688 12747 set_proto(js, str_ctor_obj, function_proto); ··· 12986 13045 ant_offset_t sym_off = (ant_offset_t)vdata(sym); 12987 13046 12988 13047 if (vtype(obj) == T_FUNC) obj = js_func_obj(obj); 13048 + else if (is_object_type(obj)) obj = js_as_obj(obj); 12989 13049 if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR) return; 12990 13050 12991 13051 ant_offset_t existing = lkp_sym(js, obj, sym_off);
+2 -2
src/errors.c
··· 788 788 js_set_slot(err_obj, SLOT_ERR_TYPE, js_mknum((double)err_type)); 789 789 790 790 int props_type = vtype(props); 791 - if ((JS_TYPE_FLAG(props_type) & T_SPECIAL_OBJECT_MASK) != 0) { 791 + if ((JS_TPFLG(props_type) & T_SPECIAL_OBJECT_MASK) != 0) { 792 792 js_merge_obj(js, err_obj, props); 793 793 } 794 794 ant_value_t proto = js_get_ctor_proto(js, err_name, err_name_len); 795 795 int proto_type = vtype(proto); 796 - if ((JS_TYPE_FLAG(proto_type) & T_SPECIAL_OBJECT_MASK) != 0) { 796 + if ((JS_TPFLG(proto_type) & T_SPECIAL_OBJECT_MASK) != 0) { 797 797 js_set_proto_init(err_obj, proto); 798 798 } 799 799
+1 -2
src/esm/commonjs.c
··· 146 146 sv_func_t *compiled = sv_compile_function_with_params( 147 147 js, cjs_params, 148 148 (int)(sizeof(cjs_params) / sizeof(cjs_params[0])), 149 - code, code_len, 150 - false 149 + code, code_len, false 151 150 ); 152 151 153 152 if (!compiled) {
+7
src/gc/objects.c
··· 55 55 } 56 56 57 57 static gc_func_mark_profile_t g_gc_func_mark_profile = {0}; 58 + static void gc_mark_coroutine(ant_t *js, coroutine_t *c); 59 + 58 60 static uint32_t g_gc_func_mark_profile_depth = 0; 59 61 static uint64_t g_gc_func_mark_profile_start_ns = 0; 60 62 ··· 229 231 gc_mark_value(js, obj->proto); 230 232 231 233 if (obj->type_tag != T_ARR) gc_mark_value(js, obj->u.data.value); 234 + if (obj->type_tag == T_GENERATOR) { 235 + ant_value_t coro_val = js_get_slot(js_obj_from_ptr(obj), SLOT_CORO); 236 + if (vtype(coro_val) == T_NUM) gc_mark_coroutine(js, (coroutine_t *)(uintptr_t)js_getnum(coro_val)); 237 + } 232 238 233 239 if (obj->type_tag == T_MAP) { 234 240 map_entry_t **head = (map_entry_t **)(uintptr_t)js_getnum(obj->u.data.value); ··· 418 424 gc_mark_value(js, c->yield_value); 419 425 gc_mark_value(js, c->super_val); 420 426 gc_mark_value(js, c->new_target); 427 + if (c->args) for (int i = 0; i < c->nargs; i++) gc_mark_value(js, c->args[i]); 421 428 } 422 429 423 430 static inline void gc_mark_promise_handler(ant_t *js, const promise_handler_t *h) {
+2
src/main.c
··· 66 66 #include "modules/observable.h" 67 67 #include "modules/collections.h" 68 68 #include "modules/iterator.h" 69 + #include "modules/generator.h" 69 70 #include "modules/module.h" 70 71 #include "modules/util.h" 71 72 #include "modules/async_hooks.h" ··· 579 580 580 581 init_symbol_module(); 581 582 init_iterator_module(); 583 + init_generator_module(); 582 584 init_timer_module(); 583 585 init_domexception_module(); 584 586 init_globals_module();
+281
src/modules/generator.c
··· 1 + #include <string.h> 2 + 3 + #include "ant.h" 4 + #include "errors.h" 5 + #include "internal.h" 6 + #include "runtime.h" 7 + #include "silver/engine.h" 8 + #include "silver/vm.h" 9 + #include "sugar.h" 10 + 11 + #include "modules/generator.h" 12 + #include "modules/symbol.h" 13 + 14 + typedef enum { 15 + GEN_SUSPENDED_START = 0, 16 + GEN_SUSPENDED_YIELD = 1, 17 + GEN_EXECUTING = 2, 18 + GEN_COMPLETED = 3, 19 + } generator_state_t; 20 + 21 + static ant_value_t generator_result(ant_t *js, bool done, ant_value_t value) { 22 + ant_value_t result = js_mkobj(js); 23 + js_set(js, result, "done", js_bool(done)); 24 + js_set(js, result, "value", value); 25 + return result; 26 + } 27 + 28 + static generator_state_t generator_state(ant_value_t gen) { 29 + ant_value_t state = js_get_slot(gen, SLOT_GENERATOR_STATE); 30 + if (vtype(state) != T_NUM) return GEN_COMPLETED; 31 + return (generator_state_t)(int)js_getnum(state); 32 + } 33 + 34 + static void generator_set_state(ant_value_t gen, generator_state_t state) { 35 + js_set_slot(gen, SLOT_GENERATOR_STATE, js_mknum((double)state)); 36 + } 37 + 38 + static bool generator_is_async(ant_value_t gen) { 39 + return js_get_slot(gen, SLOT_ASYNC) == js_true; 40 + } 41 + 42 + static ant_value_t generator_async_wrap_result(ant_t *js, ant_value_t result) { 43 + ant_value_t promise = js_mkpromise(js); 44 + if (is_err(promise)) return promise; 45 + 46 + if (is_err(result)) { 47 + ant_value_t reject_value = js->thrown_exists ? js->thrown_value : result; 48 + js->thrown_exists = false; 49 + js->thrown_value = js_mkundef(); 50 + js_reject_promise(js, promise, reject_value); 51 + } else js_resolve_promise(js, promise, result); 52 + 53 + return promise; 54 + } 55 + 56 + static coroutine_t *generator_coro(ant_value_t gen) { 57 + ant_value_t coro_val = js_get_slot(gen, SLOT_CORO); 58 + if (vtype(coro_val) != T_NUM) return NULL; 59 + return (coroutine_t *)(uintptr_t)js_getnum(coro_val); 60 + } 61 + 62 + static void generator_clear_coro(ant_value_t gen, coroutine_t *coro) { 63 + js_set_slot(gen, SLOT_CORO, js_mkundef()); 64 + if (coro) free_coroutine(coro); 65 + } 66 + 67 + static void generator_finalize(ant_t *js, ant_object_t *obj) { 68 + ant_value_t gen = js_obj_from_ptr(obj); 69 + ant_value_t coro_val = js_get_slot(gen, SLOT_CORO); 70 + if (vtype(coro_val) != T_NUM) return; 71 + coroutine_t *coro = (coroutine_t *)(uintptr_t)js_getnum(coro_val); 72 + js_set_slot(gen, SLOT_CORO, js_mkundef()); 73 + free_coroutine(coro); 74 + } 75 + 76 + static ant_value_t generator_resume_kind( 77 + ant_t *js, ant_value_t gen, ant_value_t resume_value, sv_resume_kind_t resume_kind 78 + ) { 79 + coroutine_t *coro = generator_coro(gen); 80 + if (!coro || !coro->sv_vm) { 81 + generator_set_state(gen, GEN_COMPLETED); 82 + if (resume_kind == SV_RESUME_THROW) return js_throw(js, resume_value); 83 + return generator_result(js, true, resume_kind == SV_RESUME_RETURN ? resume_value : js_mkundef()); 84 + } 85 + 86 + generator_state_t state = generator_state(gen); 87 + if (state == GEN_EXECUTING) return js_mkerr_typed( 88 + js, JS_ERR_TYPE, "Generator is already executing" 89 + ); 90 + 91 + if (state == GEN_COMPLETED) { 92 + if (resume_kind == SV_RESUME_THROW) return js_throw(js, resume_value); 93 + return generator_result(js, true, resume_kind == SV_RESUME_RETURN ? resume_value : js_mkundef()); 94 + } 95 + 96 + if (state == GEN_SUSPENDED_START && resume_kind == SV_RESUME_THROW) { 97 + generator_set_state(gen, GEN_COMPLETED); 98 + generator_clear_coro(gen, coro); 99 + return js_throw(js, resume_value); 100 + } 101 + 102 + if (state == GEN_SUSPENDED_START && resume_kind == SV_RESUME_RETURN) { 103 + generator_set_state(gen, GEN_COMPLETED); 104 + generator_clear_coro(gen, coro); 105 + return generator_result(js, true, resume_value); 106 + } 107 + 108 + generator_set_state(gen, GEN_EXECUTING); 109 + coroutine_t *saved_active = js->active_async_coro; 110 + 111 + coro->active_parent = saved_active; 112 + js->active_async_coro = coro; 113 + 114 + ant_value_t result; 115 + if (state == GEN_SUSPENDED_START) { 116 + sv_closure_t *closure = (vtype(coro->async_func) == T_FUNC) ? js_func_closure(coro->async_func) : NULL; 117 + if (!closure || !closure->func) result = js_mkerr(js, "invalid generator function"); 118 + else result = sv_execute_closure_entry( 119 + coro->sv_vm, closure, coro->async_func, 120 + coro->super_val, coro->this_val, coro->args, coro->nargs, NULL 121 + ); 122 + } else { 123 + coro->sv_vm->suspended_resume_value = resume_value; 124 + coro->sv_vm->suspended_resume_is_error = (resume_kind == SV_RESUME_THROW); 125 + coro->sv_vm->suspended_resume_kind = resume_kind; 126 + coro->sv_vm->suspended_resume_pending = true; 127 + result = sv_resume_suspended(coro->sv_vm); 128 + } 129 + 130 + js->active_async_coro = saved_active; 131 + coro->active_parent = NULL; 132 + 133 + if (is_err(result)) { 134 + generator_set_state(gen, GEN_COMPLETED); 135 + generator_clear_coro(gen, coro); 136 + return result; 137 + } 138 + 139 + if (coro->sv_vm && coro->sv_vm->suspended) { 140 + generator_set_state(gen, GEN_SUSPENDED_YIELD); 141 + return generator_result(js, false, result); 142 + } 143 + 144 + generator_set_state(gen, GEN_COMPLETED); 145 + generator_clear_coro(gen, coro); 146 + 147 + return generator_result(js, true, result); 148 + } 149 + 150 + static ant_value_t generator_resume(ant_t *js, ant_value_t gen, ant_value_t resume_value) { 151 + return generator_resume_kind(js, gen, resume_value, SV_RESUME_NEXT); 152 + } 153 + 154 + static ant_value_t generator_next(ant_t *js, ant_value_t *args, int nargs) { 155 + ant_value_t gen = js->this_val; 156 + if (vtype(gen) != T_GENERATOR) 157 + return js_mkerr_typed(js, JS_ERR_TYPE, "Generator.prototype.next called on incompatible receiver"); 158 + 159 + ant_value_t resume_value = nargs > 0 ? args[0] : js_mkundef(); 160 + ant_value_t result = generator_resume(js, gen, resume_value); 161 + 162 + return generator_is_async(gen) ? generator_async_wrap_result(js, result) : result; 163 + } 164 + 165 + static ant_value_t generator_return(ant_t *js, ant_value_t *args, int nargs) { 166 + ant_value_t gen = js->this_val; 167 + if (vtype(gen) != T_GENERATOR) 168 + return js_mkerr_typed(js, JS_ERR_TYPE, "Generator.prototype.return called on incompatible receiver"); 169 + 170 + generator_state_t state = generator_state(gen); 171 + if (state == GEN_EXECUTING) 172 + return js_mkerr_typed(js, JS_ERR_TYPE, "Generator is already executing"); 173 + 174 + ant_value_t value = nargs > 0 ? args[0] : js_mkundef(); 175 + ant_value_t result = generator_resume_kind(js, gen, value, SV_RESUME_RETURN); 176 + 177 + return generator_is_async(gen) ? generator_async_wrap_result(js, result) : result; 178 + } 179 + 180 + static ant_value_t generator_throw(ant_t *js, ant_value_t *args, int nargs) { 181 + ant_value_t gen = js->this_val; 182 + if (vtype(gen) != T_GENERATOR) 183 + return js_mkerr_typed(js, JS_ERR_TYPE, "Generator.prototype.throw called on incompatible receiver"); 184 + 185 + generator_state_t state = generator_state(gen); 186 + if (state == GEN_EXECUTING) 187 + return js_mkerr_typed(js, JS_ERR_TYPE, "Generator is already executing"); 188 + 189 + ant_value_t value = nargs > 0 ? args[0] : js_mkundef(); 190 + ant_value_t result = generator_resume_kind(js, gen, value, SV_RESUME_THROW); 191 + 192 + return generator_is_async(gen) ? generator_async_wrap_result(js, result) : result; 193 + } 194 + 195 + void init_generator_module(void) { 196 + ant_t *js = rt->js; 197 + ant_value_t proto = js_mkobj(js); 198 + 199 + js->sym.generator_proto = proto; 200 + js_set_proto_init(proto, js->sym.iterator_proto); 201 + 202 + js_set(js, proto, "next", js_mkfun(generator_next)); 203 + js_set(js, proto, "return", js_mkfun(generator_return)); 204 + js_set(js, proto, "throw", js_mkfun(generator_throw)); 205 + js_set_sym(js, proto, get_toStringTag_sym(), js_mkstr(js, "Generator", 9)); 206 + } 207 + 208 + ant_value_t sv_call_generator_closure_dispatch( 209 + sv_vm_t *caller_vm, ant_t *js, sv_closure_t *closure, 210 + ant_value_t callee_func, ant_value_t super_val, 211 + ant_value_t this_val, ant_value_t *args, int argc 212 + ) { 213 + if (!closure || !closure->func) return js_mkerr(js, "invalid generator function"); 214 + 215 + sv_vm_t *gen_vm = sv_vm_create(js, SV_VM_ASYNC); 216 + if (!gen_vm) return js_mkerr(js, "out of memory for generator VM"); 217 + 218 + coroutine_t *coro = (coroutine_t *)CORO_MALLOC(sizeof(coroutine_t)); 219 + if (!coro) { 220 + sv_vm_destroy(gen_vm); 221 + return js_mkerr(js, "out of memory for generator"); 222 + } 223 + 224 + ant_value_t *copied_args = NULL; 225 + if (argc > 0 && args) { 226 + copied_args = (ant_value_t *)CORO_MALLOC(sizeof(ant_value_t) * (size_t)argc); 227 + if (!copied_args) { 228 + sv_vm_destroy(gen_vm); 229 + CORO_FREE(coro); 230 + return js_mkerr(js, "out of memory for generator args"); 231 + } 232 + memcpy(copied_args, args, sizeof(ant_value_t) * (size_t)argc); 233 + } 234 + 235 + ant_value_t gen = js_mkgenerator(js); 236 + if (is_err(gen)) { 237 + if (copied_args) CORO_FREE(copied_args); 238 + sv_vm_destroy(gen_vm); 239 + CORO_FREE(coro); 240 + return gen; 241 + } 242 + 243 + ant_value_t instance_proto = js_get(js, callee_func, "prototype"); 244 + if (is_object_type(instance_proto)) js_set_proto_wb(js, gen, instance_proto); 245 + if (closure->func->is_async) { 246 + js_set_slot(gen, SLOT_ASYNC, js_true); 247 + js_set_sym(js, gen, get_asyncIterator_sym(), js_mkfun(sym_this_cb)); 248 + } 249 + 250 + *coro = (coroutine_t){ 251 + .js = js, 252 + .type = CORO_GENERATOR, 253 + .this_val = this_val, 254 + .super_val = super_val, 255 + .new_target = js->new_target, 256 + .awaited_promise = js_mkundef(), 257 + .result = js_mkundef(), 258 + .async_func = callee_func, 259 + .args = copied_args, 260 + .nargs = argc, 261 + .active_parent = NULL, 262 + .is_settled = false, 263 + .is_error = false, 264 + .is_done = false, 265 + .resume_point = 0, 266 + .yield_value = js_mkundef(), 267 + .async_promise = js_mkundef(), 268 + .next = NULL, 269 + .mco = NULL, 270 + .mco_started = false, 271 + .is_ready = false, 272 + .did_suspend = false, 273 + .sv_vm = gen_vm, 274 + }; 275 + 276 + js_set_slot(gen, SLOT_GENERATOR_STATE, js_mknum((double)GEN_SUSPENDED_START)); 277 + js_set_slot(gen, SLOT_CORO, ANT_PTR(coro)); 278 + js_set_finalizer(gen, generator_finalize); 279 + 280 + return gen; 281 + }
+3 -1
src/modules/symbol.c
··· 286 286 js->sym.iterator_proto = js_mkundef(); 287 287 js->sym.array_iterator_proto = js_mkundef(); 288 288 js->sym.string_iterator_proto = js_mkundef(); 289 + js->sym.generator_proto = js_mkundef(); 289 290 290 291 gc_register_root(&js->sym.iterator_proto); 291 292 gc_register_root(&js->sym.array_iterator_proto); 292 293 gc_register_root(&js->sym.string_iterator_proto); 294 + gc_register_root(&js->sym.generator_proto); 293 295 294 296 #define INIT_SYM(name, desc) g_##name = js_mksym_well_known(js, desc); 295 297 WELLKNOWN_SYMBOLS(INIT_SYM) ··· 352 354 mark(js, js->sym.iterator_proto); 353 355 mark(js, js->sym.array_iterator_proto); 354 356 mark(js, js->sym.string_iterator_proto); 357 + mark(js, js->sym.generator_proto); 355 358 356 359 #define GC_SYM(name, _desc) mark(js, g_##name); 357 360 WELLKNOWN_SYMBOLS(GC_SYM) 358 361 #undef GC_SYM 359 362 } 360 -
+39 -1
src/silver/ast.c
··· 844 844 expect(p, TOK_RBRACKET); 845 845 prop->flags |= FN_COMPUTED; 846 846 } 847 + else if (TOK == TOK_MUL) { 848 + prop->flags |= FN_GENERATOR; 849 + CONSUME(); 850 + NEXT(); 851 + 852 + if (TOK == TOK_LBRACKET) { 853 + CONSUME(); 854 + prop->left = parse_assign(p); 855 + expect(p, TOK_RBRACKET); 856 + prop->flags |= FN_COMPUTED; 857 + } else if (TOK == TOK_NUMBER) { 858 + CONSUME(); 859 + prop->left = mk_num(tod(TVAL)); 860 + } else if (TOK == TOK_STRING) { 861 + prop->left = mk(N_STRING); 862 + sv_ast_set_string(prop->left, sv_lexer_str_literal(&p->lx)); 863 + CONSUME(); 864 + } else { 865 + prop->left = mk_ident_from_tok(p); 866 + CONSUME(); 867 + } 868 + 869 + prop->right = parse_func(p); 870 + prop->right->flags |= FN_GENERATOR | FN_METHOD; 871 + sv_ast_list_push(&n->args, prop); 872 + if (NEXT() == TOK_COMMA) CONSUME(); 873 + continue; 874 + } 847 875 else if ( 848 876 (TLEN == 3 && memcmp(tok_str(p), "get", 3) == 0) || 849 877 (TLEN == 3 && memcmp(tok_str(p), "set", 3) == 0)) { ··· 873 901 } 874 902 875 903 prop->right = parse_func(p); 904 + prop->right->flags |= FN_METHOD; 876 905 sv_ast_list_push(&n->args, prop); 877 906 if (NEXT() == TOK_COMMA) CONSUME(); 878 907 continue; ··· 911 940 } 912 941 913 942 prop->right = parse_func(p); 914 - prop->right->flags |= FN_ASYNC; 943 + prop->right->flags |= FN_ASYNC | FN_METHOD; 915 944 if (prop->flags & FN_GENERATOR) 916 945 prop->right->flags |= FN_GENERATOR; 917 946 sv_ast_list_push(&n->args, prop); ··· 946 975 prop->right = parse_assign(p); 947 976 } else if (TOK == TOK_LPAREN) { 948 977 prop->right = parse_func(p); 978 + prop->right->flags |= FN_METHOD; 949 979 } else { 950 980 prop->right = mk_ident(prop->left->str, prop->left->len); 951 981 if (NEXT() == TOK_ASSIGN) { ··· 1365 1395 } 1366 1396 1367 1397 method->flags = flags; 1398 + 1399 + if (!(flags & FN_STATIC) && (flags & FN_GENERATOR) && 1400 + method->left && method->left->type == N_IDENT && 1401 + method->left->len == 11 && 1402 + memcmp(method->left->str, "constructor", 11) == 0) { 1403 + SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "Class constructor may not be a generator"); 1404 + return cls; 1405 + } 1368 1406 1369 1407 if (NEXT() == TOK_LPAREN) { 1370 1408 method->right = parse_func(p);
+36 -9
src/silver/compiler.c
··· 628 628 return idx; 629 629 } 630 630 631 + static int reserve_hidden_locals(sv_compiler_t *c, int count) { 632 + int base = c->local_count; 633 + for (int i = 0; i < count; i++) 634 + add_local(c, "", 0, false, c->scope_depth); 635 + return base; 636 + } 637 + 631 638 static inline bool sv_type_is_known(uint8_t t) { 632 639 return t != SV_TI_UNKNOWN; 633 640 } ··· 1385 1392 } 1386 1393 } 1387 1394 1395 + static void compile_yield_star_expr(sv_compiler_t *c, sv_ast_t *node) { 1396 + if (node->right) compile_expr(c, node->right); 1397 + else emit_op(c, OP_UNDEF); 1398 + 1399 + int base_local = reserve_hidden_locals(c, 4); 1400 + uint16_t base_slot = (uint16_t)(base_local - c->param_locals); 1401 + 1402 + emit_op(c, OP_YIELD_STAR_INIT); 1403 + emit_u16(c, base_slot); 1404 + 1405 + emit_op(c, OP_UNDEF); 1406 + emit_op(c, OP_YIELD_STAR_NEXT); 1407 + emit_u16(c, base_slot); 1408 + } 1409 + 1388 1410 static void compile_expr(sv_compiler_t *c, sv_ast_t *node) { 1389 1411 if (!node) { emit_op(c, OP_UNDEF); return; } 1390 1412 emit_srcpos(c, node); ··· 1540 1562 break; 1541 1563 1542 1564 case N_YIELD: 1543 - if (node->right) compile_expr(c, node->right); 1544 - else emit_op(c, OP_UNDEF); 1545 - emit_op(c, node->flags ? OP_YIELD_STAR : OP_YIELD); 1565 + if (node->flags) compile_yield_star_expr(c, node); 1566 + else { 1567 + if (node->right) compile_expr(c, node->right); 1568 + else emit_op(c, OP_UNDEF); 1569 + emit_op(c, OP_YIELD); 1570 + } 1546 1571 break; 1547 1572 1548 1573 case N_TAGGED_TEMPLATE: { ··· 4853 4878 return func; 4854 4879 } 4855 4880 4856 - sv_func_t *sv_compile_function(ant_t *js, const char *source, size_t len, bool is_async) { 4857 - const char *prefix = is_async ? "(async function" : "(function"; 4881 + sv_func_t *sv_compile_function(ant_t *js, const char *source, size_t len, bool is_async, bool is_generator) { 4882 + const char *prefix = is_async 4883 + ? "(async function" 4884 + : (is_generator ? "(function*" : "(function"); 4885 + 4858 4886 size_t prefix_len = strlen(prefix); 4859 4887 size_t wrapped_len = prefix_len + len + 1; 4860 4888 ··· 4873 4901 sv_ast_t *func_node = NULL; 4874 4902 if (program->args.count > 0) { 4875 4903 sv_ast_t *stmt = program->args.items[0]; 4876 - if (stmt && stmt->type == N_FUNC) 4877 - func_node = stmt; 4878 - else if (stmt && stmt->left && stmt->left->type == N_FUNC) 4879 - func_node = stmt->left; 4904 + if (stmt && stmt->type == N_FUNC) func_node = stmt; 4905 + else if (stmt && stmt->left && stmt->left->type == N_FUNC) func_node = stmt->left; 4880 4906 } 4881 4907 4882 4908 if (!func_node) { free(wrapped); return NULL; } ··· 4959 4985 4960 4986 sv_func_t *func = compile_function_body(&root, &top_fn, SV_COMPILE_SCRIPT); 4961 4987 if (js->thrown_exists || !func) return NULL; 4988 + 4962 4989 return func; 4963 4990 }
+231 -12
src/silver/engine.c
··· 234 234 return js_mkundef(); 235 235 } 236 236 237 + static inline void sv_yield_star_store_state(sv_vm_t *vm, ant_value_t *lp, uint16_t base) { 238 + lp[base + 0] = vm->stack[vm->sp - 3]; 239 + lp[base + 1] = vm->stack[vm->sp - 2]; 240 + lp[base + 2] = vm->stack[vm->sp - 1]; 241 + } 242 + 243 + static inline void sv_yield_star_push_state(sv_vm_t *vm, ant_value_t *lp, uint16_t base) { 244 + vm->stack[vm->sp++] = lp[base + 0]; 245 + vm->stack[vm->sp++] = lp[base + 1]; 246 + vm->stack[vm->sp++] = lp[base + 2]; 247 + } 248 + 249 + static inline void sv_yield_star_clear_state(ant_t *js, ant_value_t *lp, uint16_t base) { 250 + lp[base + 0] = js_mkundef(); 251 + lp[base + 1] = js_mkundef(); 252 + lp[base + 2] = js_mkundef(); 253 + lp[base + 3] = js_false; 254 + } 255 + 256 + static inline ant_value_t sv_yield_star_unpack_result( 257 + ant_t *js, ant_value_t result, ant_value_t *out_value, bool *out_done 258 + ) { 259 + if (!is_object_type(result)) 260 + return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator result is not an object"); 261 + 262 + ant_value_t done = js_mkundef(); 263 + sv_iter_result_unpack(js, result, &done, out_value); 264 + 265 + if (is_err(done)) return done; 266 + if (is_err(*out_value)) return *out_value; 267 + *out_done = js_truthy(js, done); 268 + 269 + return js_mkundef(); 270 + } 271 + 272 + static inline ant_value_t sv_yield_star_call_method( 273 + sv_vm_t *vm, ant_t *js, ant_value_t iterator, 274 + const char *name, ant_value_t arg, ant_value_t *out_value, bool *out_done 275 + ) { 276 + ant_value_t method = js_getprop_fallback(js, iterator, name); 277 + uint8_t mt = vtype(method); 278 + 279 + if (mt != T_FUNC && mt != T_CFUNC) { 280 + *out_value = js_mkundef(); 281 + *out_done = true; 282 + return js_mkundef(); 283 + } 284 + 285 + ant_value_t call_args[1] = { arg }; 286 + ant_value_t result = sv_vm_call(vm, js, method, iterator, call_args, 1, NULL, false); 287 + if (is_err(result)) return result; 288 + 289 + return sv_yield_star_unpack_result(js, result, out_value, out_done); 290 + } 291 + 292 + static inline ant_value_t sv_yield_star_close_iterator( 293 + sv_vm_t *vm, ant_t *js, ant_value_t iterator 294 + ) { 295 + ant_value_t close_value = js_mkundef(); 296 + bool close_done = true; 297 + 298 + return sv_yield_star_call_method( 299 + vm, js, iterator, "return", js_mkundef(), 300 + &close_value, &close_done 301 + ); 302 + } 303 + 304 + static inline ant_value_t sv_yield_star_next( 305 + sv_vm_t *vm, ant_t *js, ant_value_t *lp, uint16_t base, 306 + ant_value_t sent, ant_value_t *out_value, bool *out_done 307 + ) { 308 + int tag = (int)js_getnum(lp[base + 2]); 309 + bool first = js_truthy(js, lp[base + 3]); 310 + lp[base + 3] = js_false; 311 + 312 + if (tag == SV_ITER_GENERIC) { 313 + ant_value_t iterator = lp[base + 0]; 314 + ant_value_t next_method = lp[base + 1]; 315 + 316 + uint8_t nt = vtype(next_method); 317 + if (nt != T_FUNC && nt != T_CFUNC) 318 + return js_mkerr(js, "iterator.next is not a function"); 319 + 320 + ant_value_t call_args[1] = { sent }; 321 + ant_value_t result = sv_vm_call( 322 + vm, js, next_method, iterator, first ? NULL : call_args, first ? 0 : 1, 323 + NULL, false 324 + ); 325 + 326 + if (is_err(result)) return result; 327 + return sv_yield_star_unpack_result(js, result, out_value, out_done); 328 + } 329 + 330 + sv_yield_star_push_state(vm, lp, base); 331 + ant_value_t status = sv_iter_advance(vm, js, 0, out_value, out_done); 332 + if (is_err(status)) return status; 333 + 334 + sv_yield_star_store_state(vm, lp, base); 335 + vm->sp -= 3; 336 + 337 + return js_mkundef(); 338 + } 339 + 340 + static inline ant_value_t sv_yield_star_throw( 341 + sv_vm_t *vm, ant_t *js, ant_value_t *lp, uint16_t base, 342 + ant_value_t thrown, ant_value_t *out_value, bool *out_done 343 + ) { 344 + int tag = (int)js_getnum(lp[base + 2]); 345 + if (tag != SV_ITER_GENERIC) return js_throw(js, thrown); 346 + 347 + ant_value_t iterator = lp[base + 0]; 348 + ant_value_t throw_method = js_getprop_fallback(js, iterator, "throw"); 349 + uint8_t tt = vtype(throw_method); 350 + 351 + if (tt != T_FUNC && tt != T_CFUNC) { 352 + ant_value_t close_status = sv_yield_star_close_iterator(vm, js, iterator); 353 + if (is_err(close_status)) return close_status; 354 + return js_throw(js, thrown); 355 + } 356 + 357 + ant_value_t call_args[1] = { thrown }; 358 + ant_value_t result = sv_vm_call(vm, js, throw_method, iterator, call_args, 1, NULL, false); 359 + if (is_err(result)) return result; 360 + 361 + return sv_yield_star_unpack_result(js, result, out_value, out_done); 362 + } 363 + 364 + static inline ant_value_t sv_yield_star_return( 365 + sv_vm_t *vm, ant_t *js, ant_value_t *lp, uint16_t base, 366 + ant_value_t value, ant_value_t *out_value, bool *out_done 367 + ) { 368 + int tag = (int)js_getnum(lp[base + 2]); 369 + 370 + if (tag != SV_ITER_GENERIC) { 371 + *out_value = value; 372 + *out_done = true; 373 + return js_mkundef(); 374 + } 375 + 376 + return sv_yield_star_call_method(vm, js, lp[base + 0], "return", value, out_value, out_done); 377 + } 378 + 237 379 static inline ant_value_t sv_execute_entry_common( 238 380 sv_vm_t *vm, sv_func_t *func, sv_upvalue_t **upvalues, int upvalue_count, 239 381 ant_value_t callee_func, ant_value_t super_val, ··· 327 469 328 470 ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant_value_t super_val, ant_value_t *args, int argc) { 329 471 ant_t *js = vm->js; 472 + 330 473 bool resuming = vm->suspended && vm->suspended_resume_pending; 331 474 uint8_t *ip = resuming ? NULL : func->code; 332 475 333 - int entry_fp = resuming ? vm->suspended_entry_fp : vm->fp; 476 + int entry_fp = resuming 477 + ? vm->suspended_entry_fp 478 + : vm->fp; 479 + 334 480 ant_value_t vm_result = js_mkundef(); 335 481 ant_value_t suspended_resume_value = js_mkundef(); 482 + sv_resume_kind_t suspended_resume_kind = SV_RESUME_NEXT; 336 483 337 - bool suspended_resume_is_error = false; 338 484 js->vm_exec_depth++; 339 485 340 486 // TODO: shorthand? ··· 356 502 func = frame->func; 357 503 ip = frame->ip; 358 504 suspended_resume_value = vm->suspended_resume_value; 359 - suspended_resume_is_error = vm->suspended_resume_is_error; 505 + suspended_resume_kind = vm->suspended_resume_kind; 360 506 vm->suspended = false; 361 507 vm->suspended_resume_pending = false; 362 508 vm->suspended_resume_is_error = false; 509 + vm->suspended_resume_kind = SV_RESUME_NEXT; 363 510 vm->suspended_resume_value = js_mkundef(); 364 511 } 365 512 ··· 498 645 #define JIT_OSR_BACK_EDGE() ((void)0) 499 646 #endif 500 647 if (resuming) { 501 - if (suspended_resume_is_error) { 648 + bool yield_star_resume = ip && ( 649 + *ip == OP_YIELD_STAR_NEXT || 650 + *ip == OP_YIELD_STAR_THROW || 651 + *ip == OP_YIELD_STAR_RETURN 652 + ); 653 + if (suspended_resume_kind == SV_RESUME_THROW && !yield_star_resume) { 502 654 sv_err = js_throw(js, suspended_resume_value); 503 655 goto sv_throw; 656 + } 657 + if (suspended_resume_kind == SV_RESUME_RETURN && !yield_star_resume) { 658 + vm->stack[vm->sp++] = suspended_resume_value; 659 + goto L_RETURN; 504 660 } 505 661 vm->stack[vm->sp++] = suspended_resume_value; 506 662 } ··· 791 947 if (closure->func != NULL) { 792 948 if (closure->call_flags & (SV_CALL_HAS_BOUND_ARGS | SV_CALL_HAS_SUPER)) 793 949 goto call_fallback; 794 - if (closure->func->is_async) goto call_fallback; 950 + if (closure->func->is_async || closure->func->is_generator) goto call_fallback; 795 951 #ifdef ANT_JIT 796 952 { 797 953 ant_value_t jit_this = ( ··· 880 1036 if (closure->func != NULL) { 881 1037 if (closure->call_flags & (SV_CALL_HAS_BOUND_ARGS | SV_CALL_HAS_SUPER)) 882 1038 goto call_method_fallback; 883 - if (closure->func->is_async) goto call_method_fallback; 1039 + if (closure->func->is_async || closure->func->is_generator) goto call_method_fallback; 884 1040 #ifdef ANT_JIT 885 1041 { 886 1042 ant_value_t jit_this = ( ··· 989 1145 vtype(call_func) == T_FUNC) { 990 1146 sv_closure_t *closure = js_func_closure(call_func); 991 1147 if (closure->func != NULL) { 992 - if (!closure->func->is_async && 1148 + if (!closure->func->is_async && !closure->func->is_generator && 993 1149 !(closure->call_flags & (SV_CALL_HAS_BOUND_ARGS | SV_CALL_HAS_SUPER))) { 994 1150 if (closure->func->is_arrow || vtype(closure->bound_this) != T_UNDEF) 995 1151 tc_this = closure->bound_this; ··· 1018 1174 vtype(call_func) == T_FUNC) { 1019 1175 sv_closure_t *closure = js_func_closure(call_func); 1020 1176 if (closure->func != NULL) { 1021 - if (!closure->func->is_async && 1177 + if (!closure->func->is_async && !closure->func->is_generator && 1022 1178 !(closure->call_flags & (SV_CALL_HAS_BOUND_ARGS | SV_CALL_HAS_SUPER))) { 1023 1179 if (closure->func->is_arrow || vtype(closure->bound_this) != T_UNDEF) 1024 1180 tc_this = closure->bound_this; ··· 1242 1398 NEXT(1); 1243 1399 } 1244 1400 1245 - // TODO: implement 1246 - L_YIELD: { NEXT(1); } 1247 - L_YIELD_STAR: { NEXT(1); } 1248 - L_INITIAL_YIELD: { NEXT(1); } 1401 + L_YIELD: { 1402 + ant_value_t yielded = vm->stack[--vm->sp]; 1403 + coroutine_t *coro = js->active_async_coro; 1404 + if (!coro || coro->type != CORO_GENERATOR) { 1405 + sv_err = js_mkerr(js, "yield can only be used inside generator functions"); 1406 + goto sv_throw; 1407 + } 1408 + coro->yield_value = yielded; 1409 + coro->did_suspend = true; 1410 + vm->suspended = true; 1411 + vm->suspended_entry_fp = entry_fp; 1412 + vm->suspended_saved_fp = entry_fp - 1; 1413 + frame->ip = ip + 1; 1414 + vm_result = yielded; 1415 + goto sv_leave; 1416 + } 1417 + L_YIELD_STAR_INIT: { 1418 + uint16_t base = sv_get_u16(ip + 1); 1419 + ant_value_t iterable = vm->stack[--vm->sp]; 1420 + vm->stack[vm->sp++] = iterable; 1421 + VM_CHECK(sv_op_for_of(vm, js)); 1422 + sv_yield_star_store_state(vm, lp, base); 1423 + vm->sp -= 3; 1424 + lp[base + 3] = js_true; 1425 + NEXT(3); 1426 + } 1427 + 1428 + L_YIELD_STAR_NEXT: 1429 + L_YIELD_STAR_THROW: 1430 + L_YIELD_STAR_RETURN: { 1431 + coroutine_t *coro = js->active_async_coro; 1432 + if (!coro || coro->type != CORO_GENERATOR) { 1433 + sv_err = js_mkerr(js, "yield can only be used inside generator functions"); 1434 + goto sv_throw; 1435 + } 1436 + 1437 + uint16_t base = sv_get_u16(ip + 1); 1438 + ant_value_t resume_value = vm->stack[--vm->sp]; 1439 + ant_value_t yielded = js_mkundef(); 1440 + bool done = false; 1441 + 1442 + if (*ip == OP_YIELD_STAR_THROW || suspended_resume_kind == SV_RESUME_THROW) { 1443 + VM_CHECK(sv_yield_star_throw(vm, js, lp, base, resume_value, &yielded, &done)); 1444 + } else if (*ip == OP_YIELD_STAR_RETURN || suspended_resume_kind == SV_RESUME_RETURN) { 1445 + VM_CHECK(sv_yield_star_return(vm, js, lp, base, resume_value, &yielded, &done)); 1446 + if (done) { 1447 + sv_yield_star_clear_state(js, lp, base); 1448 + vm->stack[vm->sp++] = yielded; 1449 + goto L_RETURN; 1450 + } 1451 + } else VM_CHECK(sv_yield_star_next(vm, js, lp, base, resume_value, &yielded, &done)); 1452 + 1453 + if (done) { 1454 + sv_yield_star_clear_state(js, lp, base); 1455 + vm->stack[vm->sp++] = yielded; 1456 + NEXT(3); 1457 + } 1458 + 1459 + coro->yield_value = yielded; 1460 + coro->did_suspend = true; 1461 + vm->suspended = true; 1462 + vm->suspended_entry_fp = entry_fp; 1463 + vm->suspended_saved_fp = entry_fp - 1; 1464 + frame->ip = ip; 1465 + vm_result = yielded; 1466 + goto sv_leave; 1467 + } 1249 1468 1250 1469 L_SPREAD: { VM_CHECK(sv_op_spread(vm, js)); NEXT(1); } 1251 1470 L_DEFINE_METHOD: { sv_op_define_method(vm, js, func, ip); NEXT(6); }
+9 -4
src/silver/glue.c
··· 244 244 closure->func_obj = func_obj; 245 245 ant_value_t module_ctx = js_module_eval_active_ctx(js); 246 246 247 - js_mark_constructor(func_obj, !child->is_arrow && !child->is_method); 247 + js_mark_constructor(func_obj, !child->is_arrow && !child->is_method && !child->is_generator); 248 248 js_setprop(js, func_obj, js->length_str, tov((double)child->param_count)); 249 249 js_set_descriptor(js, func_obj, "length", 6, JS_DESC_C); 250 250 ··· 252 252 js_set_slot_wb(js, func_obj, SLOT_MODULE_CTX, module_ctx); 253 253 254 254 ant_value_t func_val = mkval(T_FUNC, (uintptr_t)closure); 255 - if (!child->is_arrow && !child->is_method) 256 - sv_setup_function_prototype(js, func_obj, func_val); 257 - 255 + if (!child->is_arrow && !child->is_method) { 256 + ant_value_t parent_proto = child->is_generator ? js->sym.generator_proto : js->sym.object_proto; 257 + sv_setup_function_prototype_with_parent(js, func_obj, func_val, parent_proto); 258 + } 259 + 258 260 if (child->is_async) { 259 261 js_set_slot(func_obj, SLOT_ASYNC, js_true); 260 262 ant_value_t async_proto = js_get_slot(js->global, SLOT_ASYNC_PROTO); 261 263 if (vtype(async_proto) == T_FUNC) js_set_proto_init(func_obj, async_proto); 264 + } else if (child->is_generator) { 265 + ant_value_t generator_proto = js_get_slot(js->global, SLOT_GENERATOR_PROTO); 266 + if (vtype(generator_proto) == T_FUNC) js_set_proto_init(func_obj, generator_proto); 262 267 } else { 263 268 ant_value_t func_proto = js_get_slot(js->global, SLOT_FUNC_PROTO); 264 269 if (vtype(func_proto) == T_FUNC) js_set_proto_init(func_obj, func_proto);
+3 -2
src/silver/ops/async.h
··· 35 35 if ( 36 36 op == OP_AWAIT_ITER_NEXT || 37 37 op == OP_YIELD || 38 - op == OP_YIELD_STAR || 39 - op == OP_INITIAL_YIELD 38 + op == OP_YIELD_STAR_NEXT || 39 + op == OP_YIELD_STAR_THROW || 40 + op == OP_YIELD_STAR_RETURN 40 41 ) return false; 41 42 42 43 int size = sv_op_size[op];
+15 -9
src/silver/ops/upvalues.h
··· 4 4 #include "silver/engine.h" 5 5 #include "descriptors.h" 6 6 7 - static inline ant_value_t sv_setup_function_prototype( 8 - ant_t *js, ant_value_t func_obj, 9 - ant_value_t func_val 7 + static inline ant_value_t sv_setup_function_prototype_with_parent( 8 + ant_t *js, ant_value_t func_obj, 9 + ant_value_t func_val, ant_value_t parent_proto 10 10 ) { 11 11 ant_value_t proto_obj = mkobj(js, 0); 12 12 if (is_err(proto_obj)) return proto_obj; 13 - 14 - ant_value_t object_proto = js->sym.object_proto; 15 - if (vtype(object_proto) == T_OBJ) js_set_proto_init(proto_obj, object_proto); 13 + if (is_object_type(parent_proto)) js_set_proto_init(proto_obj, parent_proto); 16 14 17 15 ant_value_t ctor_key = js_mkstr(js, "constructor", 11); 18 16 if (is_err(ctor_key)) return ctor_key; 17 + 19 18 ant_value_t set_ctor = js_setprop(js, proto_obj, ctor_key, func_val); 20 19 if (is_err(set_ctor)) return set_ctor; 20 + 21 21 js_set_descriptor(js, proto_obj, "constructor", 11, JS_DESC_W | JS_DESC_C); 22 - 23 22 ant_value_t proto_key = js_mkstr(js, "prototype", 9); 24 23 if (is_err(proto_key)) return proto_key; 24 + 25 25 ant_value_t set_proto = js_setprop(js, func_obj, proto_key, proto_obj); 26 26 if (is_err(set_proto)) return set_proto; 27 27 js_set_descriptor(js, func_obj, "prototype", 9, JS_DESC_W); ··· 117 117 closure->func_obj = func_obj; 118 118 ant_value_t module_ctx = js_module_eval_active_ctx(js); 119 119 120 - js_mark_constructor(func_obj, !child->is_arrow && !child->is_method); 120 + js_mark_constructor(func_obj, !child->is_arrow && !child->is_method && !child->is_generator); 121 121 js_setprop(js, func_obj, js->length_str, tov((double)child->param_count)); 122 122 js_set_descriptor(js, func_obj, "length", 6, JS_DESC_C); 123 123 124 124 if (is_object_type(module_ctx)) js_set_slot_wb(js, func_obj, SLOT_MODULE_CTX, module_ctx); 125 - if (!child->is_arrow && !child->is_method) sv_setup_function_prototype(js, func_obj, func_val); 125 + if (!child->is_arrow && !child->is_method) { 126 + ant_value_t parent_proto = child->is_generator ? js->sym.generator_proto : js->sym.object_proto; 127 + sv_setup_function_prototype_with_parent(js, func_obj, func_val, parent_proto); 128 + } 126 129 127 130 if (child->is_async) { 128 131 js_set_slot(func_obj, SLOT_ASYNC, js_true); 129 132 ant_value_t async_proto = js_get_slot(js->global, SLOT_ASYNC_PROTO); 130 133 if (vtype(async_proto) == T_FUNC) js_set_proto_init(func_obj, async_proto); 134 + } else if (child->is_generator) { 135 + ant_value_t generator_proto = js_get_slot(js->global, SLOT_GENERATOR_PROTO); 136 + if (vtype(generator_proto) == T_FUNC) js_set_proto_init(func_obj, generator_proto); 131 137 } else { 132 138 ant_value_t func_proto = js_get_slot(js->global, SLOT_FUNC_PROTO); 133 139 if (vtype(func_proto) == T_FUNC) js_set_proto_init(func_obj, func_proto);
+1
src/sugar.c
··· 101 101 coro->is_ready = false; 102 102 coro->sv_vm->suspended_resume_value = coro->result; 103 103 coro->sv_vm->suspended_resume_is_error = coro->is_error; 104 + coro->sv_vm->suspended_resume_kind = coro->is_error ? SV_RESUME_THROW : SV_RESUME_NEXT; 104 105 coro->sv_vm->suspended_resume_pending = true; 105 106 106 107 coro->active_parent = js->active_async_coro;