graaljs
graaljs copied to clipboard
[Bug]: Using async function to Symbol.iterator results in infinite loop
// input.js
var obj = {
[ Symbol . iterator ]: async ( ) => {
await 0 ;
while ( true ) ;
}
}
for ( { } of obj );
Hello,
I think the input code should throw TypeError
and the while (true)
loop under await 0
should not be executed:

$ js --version
GraalVM JavaScript (GraalVM CE Native 22.2.0)
$ js input.js
// infinite loop
^C
Interestingly, V8 has a similar bug:
$ d8 input.js
input.js:8: TypeError: undefined is not a function
for ( { } of obj );
^
TypeError: undefined is not a function
at input.js:8:7
// infinite loop
^C
but NodeJS terminates well:
$ node input.js
input.js:8
for ( { } of obj );
^
TypeError: undefined is not a function
...
Node.js v18.11.0
Let me explain what is going on here. The provided async
function is invoked to obtain the iterator. There is no special handling for async functions here. So, the returned Promise
(that corresponds to the body of the async function) is being interpreted as the iterator. The promise does not have next
method. Hence, TypeError
occurs (when next
is invoked). Unfortunately, there is still the infinite promise job trying to resolve the promise.
In fact, graal-js
throws the TypeError
but for some technical reasons the js
launcher postpones the output of this error into the console until the promise job queue is empty. This never happens in this case.
You can verify that graal-js
throws the TypeError
if you replace the last line of your test-case by
try {
for ( { } of obj );
} catch (e) {
console.log(e);
}
So, the described behaviour can be considered as a confusing behaviour of js
launcher but it cannot be considered as non-compliant behaviour with respect to ECMAScript specification. The specification does not cover how and when the user is informed about the abrupt completion of the top-level script.
Thank you for your detailed explanation!