njs icon indicating copy to clipboard operation
njs copied to clipboard

"for of" iteration support.

Open xeioex opened this issue 6 years ago • 8 comments

xeioex avatar Nov 28 '19 12:11 xeioex

example:

function seq(n) {
    var i = 0;

    return {
        [Symbol.iterator]() {
            return {
                next() {
                    if (i == n) {
                        return { done: true };
                    }

                    return { value: i++ };
                }
            };
        }
    }
}

for (var x of seq(10)) {
    console.log(x);
}

drsm avatar Nov 28 '19 14:11 drsm

Here's some PoC: https://gist.github.com/VBart/ce0bad1f15a337eff49f1336311d8381

% build/njs -c "var a = ['a', 'b', 'c']; for (var e of a) console.log(e);"
a
b
c

VBart avatar Nov 29 '19 10:11 VBart

@VBart

Hi!

Actually, there is a huge difference between for of and for in:

function collatz(n) {
    var seq = [n];

    for (var x of seq) {
        if (x == 1) {
            return seq;
        }
        seq.push(x & 1 ? 3 * x + 1 : x >> 1);
    }
}

console.log(collatz(42));

drsm avatar Nov 29 '19 11:11 drsm

@xeioex

Please take a look. Introduced initial iterator support. test262.diff

Practically useless, because iteration will generate O(n) temporary objects. But by introducing some new vmcodes, the O(1) for( of ) can be made.

$ cat collatz.js 
function collatz2(n) {
    var seq = [n];
    var it = seq.values();

    for (var x = it.next(); !x.done; x = it.next()) {
        if (x.value == 1) {
            return seq;
        }
        seq.push(x.value & 1 ? 3 * x.value + 1 : x.value >> 1);
    }
}

console.log(collatz2(42));

$ build/njs collatz.js 
[42,21,64,32,16,8,4,2,1]

drsm avatar Oct 25 '20 15:10 drsm

@drsm

But by introducing some new vmcodes, the O(1) for( of ) can be made.

you are suggesting to hide the returned value right and operate on njs_value_iterator_t?

for (var x of iterable) {

}
MAKE ITER # it = iterable[Symbol.iterator]()
again: ITER NEXT #  it.next()
BODY # ...
ITER CHECK IF DONE # !it.done
GOTO again

So, making the real object, may be avoided.

xeioex avatar Oct 28 '20 13:10 xeioex

you are suggesting to hide the returned value right and operate on njs_value_iterator_t? So, making the real object may be avoided.

Yes. To avoid useless https://tc39.es/ecma262/#sec-createiterresultobject when it is possible.

I plan to do something like this:

# for ($x of $o)
$o # iterable
$i # iterator object
$x # value
ITERATOR # iterator($i $o) 
GOTO check
again: BODY # ...
check: ITERATE #  iterate($x $i)
GOTO again IF $x is valid

function iterator($i, $o) {
    // https://tc39.es/ecma262/#sec-getiterator
    $i = $o[Symbol.iterator]()
}

function iterate($x, $i) {
    if (is_internal_iterator($i)) {
        internal_iterate($x, $i); // ArrayIterator, StringIterator, SetIterator, MapIterator ...
        return;
    }

    // slow path
    // wrap https://tc39.es/ecma262/#sec-iteratorstep
    var o = $i.next();
    if (o.done) {
        set_invalid($x);
    } else {
        set($x, o.value);
    }
}

But abusing NJS_VALUE_INVALID as the end of the sequence will not work:

var a = [1,2,3];
var x;
for (x of a) {}
console.log(x); // x should point to the last valid value
3

Also, we can't operate directly on the iterator state:

var a = [1,2,3,4,5,6];
var it = a.values();
for (var x of it) { it.next(); console.log(x); }

1
3
5

BTW, I'm just trying to figure out how the VM works. So, i'm sure there is a better way :)

Patch updated, added String.prototype[Symbol.iterator]

drsm avatar Oct 28 '20 21:10 drsm

@drsm

Introduced initial iterator support.

Thanks, committed.

xeioex avatar Nov 06 '20 13:11 xeioex

This is the feature is miss the most now because its proper transpilation brings a significant overhead. Most of the time I iterate just over Arrays, so it can be transpiled into a simple and efficient for loop over indexes, but Babel transpiler cannot know that. There’s an option assumeArray to always transpile for-of into basic for loop, but it’s risky because the code will fail in runtime if the value is not an array but a real iterable.

jirutka avatar Nov 25 '20 12:11 jirutka