Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"for of" iteration support. #262

Open
xeioex opened this issue Nov 28, 2019 · 8 comments
Open

"for of" iteration support. #262

xeioex opened this issue Nov 28, 2019 · 8 comments

Comments

@xeioex
Copy link
Contributor

xeioex commented Nov 28, 2019

No description provided.

@drsm
Copy link
Contributor

drsm commented Nov 28, 2019

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);
}

@VBart VBart self-assigned this Nov 29, 2019
@VBart
Copy link
Contributor

VBart commented Nov 29, 2019

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

@drsm
Copy link
Contributor

drsm commented Nov 29, 2019

@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
Copy link
Contributor

drsm commented Oct 25, 2020

@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]

@xeioex
Copy link
Contributor Author

xeioex commented Oct 28, 2020

@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.

@drsm
Copy link
Contributor

drsm commented Oct 28, 2020

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]

@xeioex
Copy link
Contributor Author

xeioex commented Nov 6, 2020

@drsm

Introduced initial iterator support.

Thanks, committed.

@jirutka
Copy link
Contributor

jirutka commented Nov 25, 2020

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.

@VBart VBart removed their assignment Apr 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants