Skip to content

Commit fbc4691

Browse files
minborgmcimadamore
andcommitted
8351565: Implement JEP 502: Stable Values (Preview)
Co-authored-by: Maurizio Cimadamore <[email protected]> Reviewed-by: vklang, jvernee, alanb, liach
1 parent 4c695fa commit fbc4691

30 files changed

+4806
-11
lines changed

src/java.base/share/classes/java/lang/StableValue.java

Lines changed: 757 additions & 0 deletions
Large diffs are not rendered by default.

src/java.base/share/classes/java/util/Collection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
* constructors) but all of the general-purpose {@code Collection}
5959
* implementations in the Java platform libraries comply.
6060
*
61-
* <p>Certain methods are specified to be
61+
* <p><a id="optional-operations"></a>Certain methods are specified to be
6262
* <i>optional</i>. If a collection implementation doesn't implement a
6363
* particular operation, it should define the corresponding method to throw
6464
* {@code UnsupportedOperationException}. Such methods are marked "optional

src/java.base/share/classes/java/util/ImmutableCollections.java

Lines changed: 269 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -36,11 +36,19 @@
3636
import java.util.function.BiFunction;
3737
import java.util.function.Consumer;
3838
import java.util.function.Function;
39+
import java.util.function.IntFunction;
3940
import java.util.function.Predicate;
41+
import java.util.function.Supplier;
4042
import java.util.function.UnaryOperator;
43+
4144
import jdk.internal.access.JavaUtilCollectionAccess;
4245
import jdk.internal.access.SharedSecrets;
46+
import jdk.internal.lang.stable.StableUtil;
47+
import jdk.internal.lang.stable.StableValueImpl;
4348
import jdk.internal.misc.CDS;
49+
import jdk.internal.util.ArraysSupport;
50+
import jdk.internal.util.NullableKeyValueHolder;
51+
import jdk.internal.vm.annotation.ForceInline;
4452
import jdk.internal.vm.annotation.Stable;
4553

4654
/**
@@ -128,6 +136,12 @@ public <E> List<E> listFromTrustedArray(Object[] array) {
128136
public <E> List<E> listFromTrustedArrayNullsAllowed(Object[] array) {
129137
return ImmutableCollections.listFromTrustedArrayNullsAllowed(array);
130138
}
139+
public <E> List<E> stableList(int size, IntFunction<? extends E> mapper) {
140+
return ImmutableCollections.stableList(size, mapper);
141+
}
142+
public <K, V> Map<K, V> stableMap(Set<K> keys, Function<? super K, ? extends V> mapper) {
143+
return new StableMap<>(keys, mapper);
144+
}
131145
});
132146
}
133147
}
@@ -250,6 +264,11 @@ static <E> List<E> listFromTrustedArrayNullsAllowed(Object... input) {
250264
}
251265
}
252266

267+
static <E> List<E> stableList(int size, IntFunction<? extends E> mapper) {
268+
// A lazy list is not Serializable so, we cannot return `List.of()` if size == 0
269+
return new StableList<>(size, mapper);
270+
}
271+
253272
// ---------- List Implementations ----------
254273

255274
@jdk.internal.ValueBased
@@ -448,7 +467,7 @@ static final class SubList<E> extends AbstractImmutableList<E>
448467
private final int size;
449468

450469
private SubList(AbstractImmutableList<E> root, int offset, int size) {
451-
assert root instanceof List12 || root instanceof ListN;
470+
assert root instanceof List12 || root instanceof ListN || root instanceof StableList;
452471
this.root = root;
453472
this.offset = offset;
454473
this.size = size;
@@ -499,7 +518,8 @@ private void rangeCheck(int index) {
499518
}
500519

501520
private boolean allowNulls() {
502-
return root instanceof ListN && ((ListN<?>)root).allowNulls;
521+
return root instanceof ListN<?> listN && listN.allowNulls
522+
|| root instanceof StableList<E>;
503523
}
504524

505525
@Override
@@ -551,6 +571,15 @@ public <T> T[] toArray(T[] a) {
551571
}
552572
return array;
553573
}
574+
575+
@Override
576+
public String toString() {
577+
if (root instanceof StableList<E> stableList) {
578+
return StableUtil.renderElements(root, "StableList", stableList.delegates, offset, size);
579+
} else {
580+
return super.toString();
581+
}
582+
}
554583
}
555584

556585
@jdk.internal.ValueBased
@@ -768,6 +797,116 @@ public int lastIndexOf(Object o) {
768797
}
769798
}
770799

800+
@jdk.internal.ValueBased
801+
static final class StableList<E> extends AbstractImmutableList<E> {
802+
803+
@Stable
804+
private final IntFunction<? extends E> mapper;
805+
@Stable
806+
final StableValueImpl<E>[] delegates;
807+
808+
StableList(int size, IntFunction<? extends E> mapper) {
809+
this.mapper = mapper;
810+
this.delegates = StableUtil.array(size);
811+
}
812+
813+
@Override public boolean isEmpty() { return delegates.length == 0;}
814+
@Override public int size() { return delegates.length; }
815+
@Override public Object[] toArray() { return copyInto(new Object[size()]); }
816+
817+
@ForceInline
818+
@Override
819+
public E get(int i) {
820+
final StableValueImpl<E> delegate;
821+
try {
822+
delegate = delegates[i];
823+
} catch (ArrayIndexOutOfBoundsException aioobe) {
824+
throw new IndexOutOfBoundsException(i);
825+
}
826+
return delegate.orElseSet(new Supplier<E>() {
827+
@Override public E get() { return mapper.apply(i); }});
828+
}
829+
830+
@Override
831+
@SuppressWarnings("unchecked")
832+
public <T> T[] toArray(T[] a) {
833+
final int size = delegates.length;
834+
if (a.length < size) {
835+
// Make a new array of a's runtime type, but my contents:
836+
T[] n = (T[])Array.newInstance(a.getClass().getComponentType(), size);
837+
return copyInto(n);
838+
}
839+
copyInto(a);
840+
if (a.length > size) {
841+
a[size] = null; // null-terminate
842+
}
843+
return a;
844+
}
845+
846+
@Override
847+
public int indexOf(Object o) {
848+
final int size = size();
849+
for (int i = 0; i < size; i++) {
850+
if (Objects.equals(o, get(i))) {
851+
return i;
852+
}
853+
}
854+
return -1;
855+
}
856+
857+
@Override
858+
public int lastIndexOf(Object o) {
859+
for (int i = size() - 1; i >= 0; i--) {
860+
if (Objects.equals(o, get(i))) {
861+
return i;
862+
}
863+
}
864+
return -1;
865+
}
866+
867+
@SuppressWarnings("unchecked")
868+
private <T> T[] copyInto(Object[] a) {
869+
final int len = delegates.length;
870+
for (int i = 0; i < len; i++) {
871+
a[i] = get(i);
872+
}
873+
return (T[]) a;
874+
}
875+
876+
@Override
877+
public List<E> reversed() {
878+
return new StableReverseOrderListView<>(this);
879+
}
880+
881+
@Override
882+
public String toString() {
883+
return StableUtil.renderElements(this, "StableList", delegates);
884+
}
885+
886+
private static final class StableReverseOrderListView<E> extends ReverseOrderListView.Rand<E> {
887+
888+
private StableReverseOrderListView(List<E> base) {
889+
super(base, false);
890+
}
891+
892+
// This method does not evaluate the elements
893+
@Override
894+
public String toString() {
895+
final StableValueImpl<E>[] delegates = ((StableList<E>)base).delegates;
896+
final StableValueImpl<E>[] reversed = ArraysSupport.reverse(
897+
Arrays.copyOf(delegates, delegates.length));
898+
return StableUtil.renderElements(base, "Collection", reversed);
899+
}
900+
901+
@Override
902+
public List<E> reversed() {
903+
return base;
904+
}
905+
906+
}
907+
908+
}
909+
771910
// ---------- Set Implementations ----------
772911

773912
@jdk.internal.ValueBased
@@ -1112,7 +1251,7 @@ public <T> T[] toArray(T[] a) {
11121251
// ---------- Map Implementations ----------
11131252

11141253
// Not a jdk.internal.ValueBased class; disqualified by fields in superclass AbstractMap
1115-
abstract static class AbstractImmutableMap<K,V> extends AbstractMap<K,V> implements Serializable {
1254+
abstract static class AbstractImmutableMap<K,V> extends AbstractMap<K,V> {
11161255
@Override public void clear() { throw uoe(); }
11171256
@Override public V compute(K key, BiFunction<? super K,? super V,? extends V> rf) { throw uoe(); }
11181257
@Override public V computeIfAbsent(K key, Function<? super K,? extends V> mf) { throw uoe(); }
@@ -1143,7 +1282,7 @@ public V getOrDefault(Object key, V defaultValue) {
11431282
}
11441283

11451284
// Not a jdk.internal.ValueBased class; disqualified by fields in superclass AbstractMap
1146-
static final class Map1<K,V> extends AbstractImmutableMap<K,V> {
1285+
static final class Map1<K,V> extends AbstractImmutableMap<K,V> implements Serializable {
11471286
@Stable
11481287
private final K k0;
11491288
@Stable
@@ -1215,7 +1354,7 @@ public void forEach(BiConsumer<? super K, ? super V> action) {
12151354
* @param <V> the value type
12161355
*/
12171356
// Not a jdk.internal.ValueBased class; disqualified by fields in superclass AbstractMap
1218-
static final class MapN<K,V> extends AbstractImmutableMap<K,V> {
1357+
static final class MapN<K,V> extends AbstractImmutableMap<K,V> implements Serializable {
12191358

12201359
@Stable
12211360
final Object[] table; // pairs of key, value
@@ -1405,6 +1544,130 @@ private Object writeReplace() {
14051544
return new CollSer(CollSer.IMM_MAP, array);
14061545
}
14071546
}
1547+
1548+
static final class StableMap<K, V>
1549+
extends AbstractImmutableMap<K, V> {
1550+
1551+
@Stable
1552+
private final Function<? super K, ? extends V> mapper;
1553+
@Stable
1554+
private final Map<K, StableValueImpl<V>> delegate;
1555+
1556+
StableMap(Set<K> keys, Function<? super K, ? extends V> mapper) {
1557+
this.mapper = mapper;
1558+
this.delegate = StableUtil.map(keys);
1559+
}
1560+
1561+
@Override public boolean containsKey(Object o) { return delegate.containsKey(o); }
1562+
@Override public int size() { return delegate.size(); }
1563+
@Override public Set<Map.Entry<K, V>> entrySet() { return new StableMapEntrySet(); }
1564+
1565+
@ForceInline
1566+
@Override
1567+
public V get(Object key) {
1568+
return getOrDefault(key, null);
1569+
}
1570+
1571+
@ForceInline
1572+
@Override
1573+
public V getOrDefault(Object key, V defaultValue) {
1574+
final StableValueImpl<V> stable = delegate.get(key);
1575+
if (stable == null) {
1576+
return defaultValue;
1577+
}
1578+
@SuppressWarnings("unchecked")
1579+
final K k = (K) key;
1580+
return stable.orElseSet(new Supplier<V>() {
1581+
@Override public V get() { return mapper.apply(k); }});
1582+
}
1583+
1584+
@jdk.internal.ValueBased
1585+
final class StableMapEntrySet extends AbstractImmutableSet<Map.Entry<K, V>> {
1586+
1587+
@Stable
1588+
private final Set<Map.Entry<K, StableValueImpl<V>>> delegateEntrySet;
1589+
1590+
StableMapEntrySet() {
1591+
this.delegateEntrySet = delegate.entrySet();
1592+
}
1593+
1594+
@Override public Iterator<Map.Entry<K, V>> iterator() { return new LazyMapIterator(); }
1595+
@Override public int size() { return delegateEntrySet.size(); }
1596+
@Override public int hashCode() { return StableMap.this.hashCode(); }
1597+
1598+
@Override
1599+
public String toString() {
1600+
return StableUtil.renderMappings(this, "StableSet", delegateEntrySet, false);
1601+
}
1602+
1603+
@jdk.internal.ValueBased
1604+
final class LazyMapIterator implements Iterator<Map.Entry<K, V>> {
1605+
1606+
@Stable
1607+
private final Iterator<Map.Entry<K, StableValueImpl<V>>> delegateIterator;
1608+
1609+
LazyMapIterator() {
1610+
this.delegateIterator = delegateEntrySet.iterator();
1611+
}
1612+
1613+
@Override public boolean hasNext() { return delegateIterator.hasNext(); }
1614+
1615+
@Override
1616+
public Entry<K, V> next() {
1617+
final Map.Entry<K, StableValueImpl<V>> inner = delegateIterator.next();
1618+
final K k = inner.getKey();
1619+
return new NullableKeyValueHolder<>(k, inner.getValue().orElseSet(new Supplier<V>() {
1620+
@Override public V get() { return mapper.apply(k); }}));
1621+
}
1622+
1623+
@Override
1624+
public void forEachRemaining(Consumer<? super Map.Entry<K, V>> action) {
1625+
final Consumer<? super Map.Entry<K, StableValueImpl<V>>> innerAction =
1626+
new Consumer<>() {
1627+
@Override
1628+
public void accept(Entry<K, StableValueImpl<V>> inner) {
1629+
final K k = inner.getKey();
1630+
action.accept(new NullableKeyValueHolder<>(k, inner.getValue().orElseSet(new Supplier<V>() {
1631+
@Override public V get() { return mapper.apply(k); }})));
1632+
}
1633+
};
1634+
delegateIterator.forEachRemaining(innerAction);
1635+
}
1636+
}
1637+
}
1638+
1639+
@Override
1640+
public Collection<V> values() {
1641+
return new StableMapValues();
1642+
}
1643+
1644+
final class StableMapValues extends AbstractImmutableCollection<V> {
1645+
@Override public Iterator<V> iterator() { return new ValueIterator(); }
1646+
@Override public int size() { return StableMap.this.size(); }
1647+
@Override public boolean isEmpty() { return StableMap.this.isEmpty();}
1648+
@Override public boolean contains(Object v) { return StableMap.this.containsValue(v); }
1649+
1650+
private static final IntFunction<StableValueImpl<?>[]> GENERATOR = new IntFunction<StableValueImpl<?>[]>() {
1651+
@Override
1652+
public StableValueImpl<?>[] apply(int len) {
1653+
return new StableValueImpl<?>[len];
1654+
}
1655+
};
1656+
1657+
@Override
1658+
public String toString() {
1659+
final StableValueImpl<?>[] values = delegate.values().toArray(GENERATOR);
1660+
return StableUtil.renderElements(StableMap.this, "StableMap", values);
1661+
}
1662+
}
1663+
1664+
@Override
1665+
public String toString() {
1666+
return StableUtil.renderMappings(this, "StableMap", delegate.entrySet(), true);
1667+
}
1668+
1669+
}
1670+
14081671
}
14091672

14101673
// ---------- Serialization Proxy ----------

src/java.base/share/classes/jdk/internal/access/JavaUtilCollectionAccess.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -26,8 +26,14 @@
2626
package jdk.internal.access;
2727

2828
import java.util.List;
29+
import java.util.Map;
30+
import java.util.Set;
31+
import java.util.function.Function;
32+
import java.util.function.IntFunction;
2933

3034
public interface JavaUtilCollectionAccess {
3135
<E> List<E> listFromTrustedArray(Object[] array);
3236
<E> List<E> listFromTrustedArrayNullsAllowed(Object[] array);
37+
<E> List<E> stableList(int size, IntFunction<? extends E> mapper);
38+
<K, V> Map<K, V> stableMap(Set<K> keys, Function<? super K, ? extends V> mapper);
3339
}

0 commit comments

Comments
 (0)