-
Notifications
You must be signed in to change notification settings - Fork 850
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
Add native iterators for Map/List/Collections #890
Conversation
Thanks! I agree that it makes sense to do this. I think we should talk about what we want to enable, however. It looks like this PR enables the pre-ES6 iterator behavior, so that one can do a "for each" over a collection. I also think that we should support the "Symbol.iterator" property on imported collections so that ES6 users can iterate in other ways and so that Java collections can work in other places that work with ES6 iterators, like Array.from for example. We do this now using a Symbol property that matches SymbolKey.ITERATOR (this works even if the user is not in ES6 mode and doesn't have the Symbol global property defined). It also looks like we already support that in NativeJavaMap and NativeJavaObject for Iterable objects. Could you look and see whether we could make that approach more generic instead? |
Do you want to make Nashorn's code work with Rhino? I don't really want to use syntax that are outside the ECMA262 specification, and would like to deprecate in the future if possible. |
Thanks for the feedback. First I have to admit that I didn't know that there were differences between ES6 and pre-ES6. I also did not know that the "for each" style is nashorn specific. I agreee that this should be deprecated. (At least I'll try to avoid it in our code)
Yes, you're right, there is already support for ES6 Symbol.iterator in NativeJavaObject https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/NativeJavaObject.java#L105 and NativeJavaMap. The NativeJavaMap iterator will work well with var js = {};
var java = new java.util.Map();
for (var key in XX) // currently works for js and java the same way (if keys are Strings) - it calls 'getIds'
for each (var valuein XX) // currently works for js and java the same way (but is not ECMA specific)
for (var entry of XX) // works well for java-maps. Iterates over entries, but doesn't work on javascript objects, because they defne no iterator (which can be fixed in JS)
Object.values(XX) // this would also help us in many use cases, but this is currently not implemented. |
Just to clear things up, js> var a = [1,2,3]
js> a.property = 'prop'
prop
js> for each (var value in a) print(value)
1
2
3
prop https://developer.mozilla.org/en-US/docs/Archive/Web/JavaScript/for_each...in There is a lot of e4x code that would break if This is how NativeIterator is meant to be used: js> // this works because NativeArray implements java.util.Collection
js> var arrayList = new java.util.ArrayList(['foo', 'bar', 'baz']);
js> // Iterator() can wrap any java.lang.Iterable or java.util.Iterator
js> for (var element in Iterator(arrayList)) print(element);
foo
bar
baz Would it be easier to just use a js function for your use case? Something like function getValues(objOrMap) {
if (objOrMap instanceof java.util.Map) {
return Array.from(objOrMap.values());
}
else {
return Object.keys(objOrMap).map(key => objOrMap[key]);
}
}
js> o = {a: 'one', b: 'two'}
[object Object]
js> m = new java.util.HashMap(o)
{a=one, b=two}
js> getValues(o)
one,two
js> getValues(m)
one,two |
I read your last comment again, and it seems like using This seems to work: Object.prototype[Symbol.iterator] = function* () {
var entries = Object.keys(this).map(k => [k, this[k]]);
for (var i = 0; i < entries.length; i++) {
yield entries[i];
}
} |
@tonygermano Yes, handling java.util.Map is a bit tricky, especially if keys are not string. I am experimenting with a keyTranslation as you can see in this commit: FOCONIS@d02ac0b#diff-fab16163e35411a29842a0afdd4592dbf45391d96ede85a2ac737744d10ef51b The current implementation will detect ambigous keys: FOCONIS@d02ac0b#diff-46400a21b05dae3795ff41f2ba08f98e64bcf0633a8b0d19b7c038f6e0ecb7e7R38 I still need some time to investigate what is the best solution, but using I have also other PRs open (e.g. Object.values) which may cover some of my use cases. So ignore this PR for a while (you can also close it, if you want, I can create a new one, after my other PRs are merged) |
…tors # Conflicts: # src/org/mozilla/javascript/NativeIterator.java
@@ -88,6 +85,9 @@ public boolean has(Symbol key, Scriptable start) { | |||
|
|||
@Override | |||
public Object get(String name, Scriptable start) { | |||
if (name.equals(NativeIterator.ITERATOR_PROPERTY_NAME) && javaObject instanceof Iterable) { | |||
return es5Iterator; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the hint and I ask for your indulgence when I try to submit one or the other nonsensical change ;)
I have to admit that I don't know the specification in detail and have only even rudimentarily read it so far.
In particular, I have trouble telling the difference between value[Symbol.iterator]
and value["__iterator__"]
. Especially finding the correct documentation
As far I understand now, the __iterator__
is a very old thing (https://web.archive.org/web/20061203042508/http://developer.mozilla.org:80/en/docs/New_in_JavaScript_1.7%23Iterators) and is used when looping with for...in
or for each ... in
(for each in is also deprecated/not recommended to use, as I remember from other comments)
The Symbol.iterator
was introduced with ES5 and works completely different than the above one. It is used in for..of
loops.
Rhino is a JavaScript engine.
... and here we are trying to use java objects in javascript (which isn't specified yet, AFAIK)
So, what this PR enables is to iterate over any java.util.Iterable
with for...in
/ for each ... in
.
and now, while writing these lines, the penny dropped...
- It doesn't make much sense to use
for...in
with ajava.util.HashSet
as you might get only the IDs, but you cannot access them byhashSet[id]
- The
for each ... in
is deprecated and Rhino specific. It can be easily replaced byfor ... of
So the advantage of this PR is, that you can use the (deprecated) for each
on iterable objects.
Unfortunately, we have used "for each" in our code and also the patch in this PR, so the clean way is to migrate our js code to for...of
.
Thanks again
Roland
Closing this PR. Use |
This is a roll up of #827 and #830
I want to discuss, if it makes sense to implement
__iterator__
on Java Map/Iterable objects.This will allow to iterate with
for each
over any iterable java object. (e.g. HashSets)It will also improve performance on NativeJavaMap and NativeJavaList, as a call to getIds() (https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/NativeJavaMap.java#L117 and https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/NativeJavaList.java#L90) is not neccessary.
On the other hand, it is no longer possible, to modify the list in the for loop (you may get a concurrentModificationException)
So, we should decide, if this PR is worth to merge, maybe I can also add a contextFlag for this feature.