Skip to content

Commit 8c8016e

Browse files
kevinoconnor7copybara-github
authored andcommitted
Prep-work to make java.lang.Iterable implement the JS iterator protocol
PiperOrigin-RevId: 888280380
1 parent 7255073 commit 8c8016e

File tree

63 files changed

+484
-89
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+484
-89
lines changed

jre/java/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ package_sources(
7272
"javaemul/internal/Casts.java", # J2WASM doesn't need it.
7373
"javaemul/internal/ArrayStamper.java", # Unused in Wasm
7474
"javaemul/internal/Comparables.java", # Unused in Wasm
75+
"javaemul/internal/JsIterableHelper.java", # Unused in Wasm
7576
"javaemul/internal/Objects.java", # Unused in Wasm
7677
"javaemul/internal/Ref.java", # Unused in Wasm
7778
],

jre/java/java/lang/Iterable.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
import java.util.Spliterator;
2222
import java.util.Spliterators;
2323
import java.util.function.Consumer;
24+
import javaemul.internal.JsIterableHelper;
25+
import javaemul.internal.JsIterableHelper.JsIterable;
26+
import jsinterop.annotations.JsMethod;
27+
import jsinterop.annotations.JsNonNull;
2428

2529
/**
2630
* Allows an instance of a class implementing this interface to be used in the foreach statement.

jre/java/java/util/Iterator.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,16 @@
1818
import static javaemul.internal.InternalPreconditions.checkNotNull;
1919

2020
import java.util.function.Consumer;
21+
import javaemul.internal.JsIterableHelper;
22+
import javaemul.internal.JsIterableHelper.JsIterator;
2123

2224
/**
2325
* See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html">the official Java
2426
* API doc</a> for details.
2527
*
2628
* @param <T> element type
2729
*/
28-
public interface Iterator<T> {
30+
public interface Iterator<T> extends JsIterator<T> {
2931

3032
boolean hasNext();
3133

@@ -41,4 +43,11 @@ default void forEachRemaining(Consumer<? super T> consumer) {
4143
default void remove() {
4244
throw new UnsupportedOperationException();
4345
}
46+
47+
@Override
48+
default JsIterableHelper.IIterableResult<T> _private_jsNext__() {
49+
return hasNext()
50+
? JsIterableHelper.makeResult(next(), false)
51+
: JsIterableHelper.makeResult((T) null, true);
52+
}
4453
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2026 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package javaemul.internal;
17+
18+
import java.util.Iterator;
19+
import jsinterop.annotations.JsMethod;
20+
import jsinterop.annotations.JsPackage;
21+
import jsinterop.annotations.JsType;
22+
23+
/** A helper class to create JavaScript iterable objects. */
24+
public final class JsIterableHelper {
25+
26+
/** Abstraction for JavaScript Iterable. */
27+
@JsType(isNative = true, name = "Iterable", namespace = JsPackage.GLOBAL)
28+
public interface JsIterable<T> {
29+
@JsMethod(name = "[Symbol.iterator]")
30+
JsIterator<T> _private_jsIterator__();
31+
}
32+
33+
/** Abstraction for JavaScript Iterator. */
34+
@JsType(isNative = true, name = "Iterator", namespace = JsPackage.GLOBAL)
35+
public interface JsIterator<T> {
36+
@JsMethod(name = "next")
37+
IIterableResult<T> _private_jsNext__();
38+
}
39+
40+
/** Abstraction for JavaScript IIterableResult. */
41+
@JsType(isNative = true, namespace = JsPackage.GLOBAL)
42+
public interface IIterableResult<T> {}
43+
44+
@JsMethod
45+
public static native <T> IIterableResult<T> makeResult(T value, boolean done);
46+
47+
public static <T> JsIterable<T> asJsIterable(Iterable<T> iterable) {
48+
return new JsIterableAdapter<T>(iterable);
49+
}
50+
51+
private static final class JsIterableAdapter<T> implements JsIterable<T> {
52+
private final Iterable<T> delegate;
53+
54+
private JsIterableAdapter(Iterable<T> delegate) {
55+
this.delegate = delegate;
56+
}
57+
58+
@Override
59+
public final JsIterableIteratorAdapter<T> _private_jsIterator__() {
60+
return new JsIterableIteratorAdapter<>(delegate.iterator());
61+
}
62+
}
63+
64+
/**
65+
* Adapts a Java {@link Iterator} to also implement the {@link JsIterable} API. This ultimately
66+
* makes this type an {@code IteratorIterable} for JS.
67+
*
68+
* <p>In general JS expects all Iterators to themselves be iterable, see:
69+
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol:~:text=Such%20object%20is,not%20iterators.)
70+
*/
71+
private static final class JsIterableIteratorAdapter<T> implements JsIterator<T>, JsIterable<T> {
72+
private final JsIterator<T> delegate;
73+
74+
private JsIterableIteratorAdapter(JsIterator<T> delegate) {
75+
this.delegate = delegate;
76+
}
77+
78+
@Override
79+
public final IIterableResult<T> _private_jsNext__() {
80+
return delegate._private_jsNext__();
81+
}
82+
83+
@Override
84+
public final JsIterator<T> _private_jsIterator__() {
85+
return this;
86+
}
87+
}
88+
89+
private JsIterableHelper() {}
90+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @param {?T} value
3+
* @param {boolean} done
4+
* @return {!IIterableResult<T>}
5+
* @template T
6+
*/
7+
JsIterableHelper.makeResult = function(value, done) {
8+
return {value: value, done: done};
9+
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2007 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package java.lang;
17+
18+
import static javaemul.internal.InternalPreconditions.checkNotNull;
19+
20+
import java.util.Iterator;
21+
import java.util.Spliterator;
22+
import java.util.Spliterators;
23+
import java.util.function.Consumer;
24+
25+
/**
26+
* Allows an instance of a class implementing this interface to be used in the foreach statement.
27+
* See <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html">the official Java
28+
* API doc</a> for details.
29+
*
30+
* @param <T> type of returned iterator
31+
*/
32+
public interface Iterable<T> {
33+
Iterator<T> iterator();
34+
35+
default void forEach(Consumer<? super T> action) {
36+
checkNotNull(action);
37+
for (T t : this) {
38+
action.accept(t);
39+
}
40+
}
41+
42+
default Spliterator<T> spliterator() {
43+
return Spliterators.spliteratorUnknownSize(iterator(), 0);
44+
}
45+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2007 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package java.util;
17+
18+
import static javaemul.internal.InternalPreconditions.checkNotNull;
19+
20+
import java.util.function.Consumer;
21+
22+
/**
23+
* See <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html">the official Java
24+
* API doc</a> for details.
25+
*
26+
* @param <T> element type
27+
*/
28+
public interface Iterator<T> {
29+
30+
boolean hasNext();
31+
32+
T next();
33+
34+
default void forEachRemaining(Consumer<? super T> consumer) {
35+
checkNotNull(consumer);
36+
while (hasNext()) {
37+
consumer.accept(next());
38+
}
39+
}
40+
41+
default void remove() {
42+
throw new UnsupportedOperationException();
43+
}
44+
}

jre/javatests/com/google/j2cl/jre/JsSuite.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.j2cl.jre.java.lang.JsStringTest;
2020
import com.google.j2cl.jre.java.lang.JsThrowableTest;
2121
import com.google.j2cl.jre.java.util.JsCollectionTest;
22+
import com.google.j2cl.jre.java.util.JsIteratorTest;
2223
import org.junit.runner.RunWith;
2324
import org.junit.runners.Suite;
2425
import org.junit.runners.Suite.SuiteClasses;
@@ -30,5 +31,6 @@
3031
JsThrowableTest.class,
3132
JsStringTest.class,
3233
JsCollectionTest.class,
34+
JsIteratorTest.class,
3335
})
3436
public class JsSuite {}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2026 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.google.j2cl.jre.java.util;
17+
18+
import static org.junit.Assert.assertArrayEquals;
19+
20+
import java.util.ArrayList;
21+
import java.util.Collections;
22+
import java.util.LinkedHashSet;
23+
import javaemul.internal.JsIterableHelper.JsIterable;
24+
import jsinterop.annotations.JsMethod;
25+
import jsinterop.annotations.JsPackage;
26+
import junit.framework.TestCase;
27+
28+
/** Testing JS contract of Iterator interface. */
29+
public class JsIteratorTest extends TestCase {
30+
31+
@JsMethod(namespace = JsPackage.GLOBAL, name = "Array.from")
32+
private static native String[] arrayFrom(JsIterable<String> jsIterable);
33+
}

transpiler/java/com/google/j2cl/transpiler/ast/JsUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public final class JsUtils {
3333
* characters but we disallow those for the time being.
3434
*/
3535
public static boolean isValidJsIdentifier(String name) {
36-
return name.matches(VALID_JS_NAME_REGEX);
36+
return name.equals("[Symbol.iterator]") || name.matches(VALID_JS_NAME_REGEX);
3737
}
3838

3939
/**

0 commit comments

Comments
 (0)