Skip to content

Commit 2a01d9f

Browse files
committed
Split interface and implementation of SCC
1 parent 93cca54 commit 2a01d9f

File tree

2 files changed

+141
-119
lines changed

2 files changed

+141
-119
lines changed
Lines changed: 19 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
package jdk.internal.access;
22

3-
import jdk.internal.misc.Unsafe;
4-
import jdk.internal.vm.annotation.ForceInline;
5-
import jdk.internal.vm.annotation.Stable;
6-
73
import java.util.Objects;
84
import java.util.Set;
9-
import java.util.StringJoiner;
105

116
// This class is required to be able to be used very early in the boot sequence.
127
// Because of this, it does not use reflection, MethodHandles, or ImmutableCollections
@@ -15,20 +10,23 @@
1510
* <p>
1611
* The lookup of components can be eligible for constant folding if the stable component
1712
* container is a VM constant (e.g., is declared as a `static final` field) and the
18-
* lookup key is a constant (e.g., a class literal)..
13+
* lookup key is a constant (e.g., a class literal).
14+
* <p>
15+
* Except if otherwise specified, all methods throw a {@linkplain NullPointerException}
16+
* if a {@code null} parameter is provided.
1917
*
2018
* @param <T> The common type of the components. The type can be {@linkplain Object} if
2119
* there is no common super type for the components.
2220
*/
23-
public sealed interface StableComponentContainer<T> {
21+
public sealed interface StableComponentContainer<T> permits StableComponentContainerImpl {
2422

2523
/**
2624
* {@return the associated component for the provided {@code type}}
2725
*
2826
* @param type to use as lookup
2927
* @param <C> component type
3028
* @throws IllegalArgumentException if the provided {@code type} was not specified
31-
* at construction.
29+
* {@linkplain StableComponentContainer#of(Set) at construction}.
3230
*/
3331
<C extends T> C get(Class<C> type);
3432

@@ -41,7 +39,7 @@ public sealed interface StableComponentContainer<T> {
4139
* (nullable)
4240
* @param <C> component type
4341
* @throws IllegalArgumentException if the provided {@code type} was not specified
44-
* at construction.
42+
* {@linkplain StableComponentContainer#of(Set) at construction}.
4543
*/
4644
<C extends T> C orElse(Class<C> type, C other);
4745

@@ -51,118 +49,29 @@ public sealed interface StableComponentContainer<T> {
5149
*
5250
* @param type to use as lookup
5351
* @throws IllegalArgumentException if the provided {@code type} was not specified
54-
* at construction.
52+
* {@linkplain StableComponentContainer#of(Set) at construction}.
5553
*/
5654
boolean isInitialized(Class<? extends T> type);
5755

5856
/**
5957
* Associates the provided {@code type} with the provided {@code component}
6058
*
6159
* @param type to use as lookup
60+
* @throws IllegalArgumentException if the provided {@code type} was not specified
61+
* {@linkplain StableComponentContainer#of(Set) at construction}.
6262
* @throws IllegalStateException if the provided {@code type} was already associated
6363
* with a component
6464
*/
6565
<C extends T> void set(Class<C> type, C component);
6666

67-
record StableComponentContainerImpl<T>(@Stable Object[] table) implements StableComponentContainer<T> {
68-
69-
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
70-
71-
@ForceInline
72-
public <C extends T> C get(Class<C> type) {
73-
return type.cast(componentRaw(type));
74-
}
75-
76-
public boolean isInitialized(Class<? extends T> type) {
77-
return componentRaw(type) != null;
78-
}
79-
80-
@Override
81-
public <C extends T> C orElse(Class<C> type, C other) {
82-
final Object componentRaw = componentRaw(type);
83-
return componentRaw == null ? other : type.cast(componentRaw);
84-
}
85-
86-
@ForceInline
87-
private Object componentRaw(Class<?> type) {
88-
Objects.requireNonNull(type);
89-
final int probe = probeOrThrow(type);
90-
return UNSAFE.getReferenceAcquire(table, nextOffset(offsetFor(probe)));
91-
}
92-
93-
public <C extends T> void set(Class<C> type, C component) {
94-
// Implicit null check of both `type` and `component`
95-
if (!type.isInstance(component)) {
96-
throw new IllegalArgumentException();
97-
}
98-
final int probe = probeOrThrow(type);
99-
if (!UNSAFE.compareAndSetReference(table, nextOffset(offsetFor(probe)), null, component)) {
100-
throw new IllegalStateException("The component is already initialized: " + type.getName());
101-
}
102-
}
103-
104-
@Override
105-
public String toString() {
106-
return "StableComponentContainer" + associations(true);
107-
}
108-
109-
private String associations(boolean showValues) {
110-
final StringJoiner sj = new StringJoiner(", ");
111-
for (int i = 0; i < table.length; i+=2) {
112-
final Class<?> type = (Class<?>) table[i];
113-
if (type != null) {
114-
if (showValues) {
115-
final Object component = UNSAFE.getReferenceAcquire(table, nextOffset(offsetFor(i)));
116-
sj.add(type.getName() + (component != null ? "=" + component : ""));
117-
} else {
118-
sj.add(type.toString());
119-
}
120-
}
121-
}
122-
return "{" + sj + "}";
123-
}
124-
125-
@ForceInline
126-
private int probeOrThrow(Class<?> type) {
127-
final int probe = probe(table, type);
128-
if (probe < 0) {
129-
throw new IllegalArgumentException("The type '" + type.getName() + "' is outside the allowed input types: " + associations(false));
130-
}
131-
return probe;
132-
}
133-
134-
@ForceInline
135-
private long offsetFor(int index) {
136-
return Unsafe.ARRAY_OBJECT_BASE_OFFSET + (long) index * Unsafe.ARRAY_OBJECT_INDEX_SCALE;
137-
}
138-
139-
@ForceInline
140-
private long nextOffset(long offset) {
141-
return offset + Unsafe.ARRAY_OBJECT_INDEX_SCALE;
142-
}
143-
}
144-
145-
// returns index at which the probe key is present; or if absent,
146-
// (-i - 1) where i is location where element should be inserted.
147-
@ForceInline
148-
private static int probe(Object[] table, Object pk) {
149-
int idx = Math.floorMod(pk.hashCode(), table.length >> 1) << 1;
150-
while (true) {
151-
Object ek = table[idx];
152-
if (ek == null) {
153-
return -idx - 1;
154-
} else if (pk.equals(ek)) {
155-
return idx;
156-
} else if ((idx += 2) == table.length) {
157-
idx = 0;
158-
}
159-
}
160-
}
161-
162-
private static int availableIndex(int probe) {
163-
return -probe - 1;
164-
}
165-
67+
/**
68+
* {@return a new stable component container that can associate any of the provided
69+
* {@code types} to components}
70+
*
71+
* @param types that can be used to associate to components
72+
* @param <T> the common type of the components. The type can be {@linkplain Object}
73+
* if there is no common super type for the components.
74+
*/
16675
static <T> StableComponentContainer<T> of(Set<Class<? extends T>> types) {
16776
// TOC TOU protection and
16877
// implicit null check of `types` and explicit null check on all its elements
@@ -171,16 +80,7 @@ static <T> StableComponentContainer<T> of(Set<Class<? extends T>> types) {
17180
for (Object type : types) {
17281
inputs[idx++] = Objects.requireNonNull(type);
17382
}
174-
175-
// Prepopulate all the keys upfront
176-
final Object[] table = new Object[inputs.length << 2];
177-
for (Object type : inputs) {
178-
final int probe = probe(table, type);
179-
assert probe < 0;
180-
final int keyIndex = availableIndex(probe);
181-
table[keyIndex] = type;
182-
}
183-
return new StableComponentContainerImpl<>(table);
83+
return StableComponentContainerImpl.of(inputs);
18484
}
18585

18686
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package jdk.internal.access;
2+
3+
import jdk.internal.misc.Unsafe;
4+
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
5+
import jdk.internal.vm.annotation.ForceInline;
6+
import jdk.internal.vm.annotation.Stable;
7+
8+
import java.util.Objects;
9+
import java.util.StringJoiner;
10+
11+
@AOTSafeClassInitializer
12+
record StableComponentContainerImpl<T>(@Stable Object[] table) implements StableComponentContainer<T> {
13+
14+
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
15+
16+
@ForceInline
17+
public <C extends T> C get(Class<C> type) {
18+
return type.cast(componentRaw(type));
19+
}
20+
21+
public boolean isInitialized(Class<? extends T> type) {
22+
return componentRaw(type) != null;
23+
}
24+
25+
@Override
26+
public <C extends T> C orElse(Class<C> type, C other) {
27+
final Object componentRaw = componentRaw(type);
28+
return componentRaw == null ? other : type.cast(componentRaw);
29+
}
30+
31+
@ForceInline
32+
private Object componentRaw(Class<?> type) {
33+
Objects.requireNonNull(type);
34+
final int probe = probeOrThrow(type);
35+
return UNSAFE.getReferenceAcquire(table, nextOffset(offsetFor(probe)));
36+
}
37+
38+
public <C extends T> void set(Class<C> type, C component) {
39+
// Implicit null check of both `type` and `component`
40+
if (!type.isInstance(component)) {
41+
throw new IllegalArgumentException();
42+
}
43+
final int probe = probeOrThrow(type);
44+
if (!UNSAFE.compareAndSetReference(table, nextOffset(offsetFor(probe)), null, component)) {
45+
throw new IllegalStateException("The component is already initialized: " + type.getName());
46+
}
47+
}
48+
49+
@Override
50+
public String toString() {
51+
return "StableComponentContainer" + associations(true);
52+
}
53+
54+
private String associations(boolean showValues) {
55+
final StringJoiner sj = new StringJoiner(", ");
56+
for (int i = 0; i < table.length; i += 2) {
57+
final Class<?> type = (Class<?>) table[i];
58+
if (type != null) {
59+
if (showValues) {
60+
final Object component = UNSAFE.getReferenceAcquire(table, nextOffset(offsetFor(i)));
61+
sj.add(type.getName() + (component != null ? "=" + component : ""));
62+
} else {
63+
sj.add(type.toString());
64+
}
65+
}
66+
}
67+
return "{" + sj + "}";
68+
}
69+
70+
@ForceInline
71+
private int probeOrThrow(Class<?> type) {
72+
final int probe = probe(table, type);
73+
if (probe < 0) {
74+
throw new IllegalArgumentException("The type '" + type.getName() + "' is outside the allowed input types: " + associations(false));
75+
}
76+
return probe;
77+
}
78+
79+
@ForceInline
80+
private long offsetFor(int index) {
81+
return Unsafe.ARRAY_OBJECT_BASE_OFFSET + (long) index * Unsafe.ARRAY_OBJECT_INDEX_SCALE;
82+
}
83+
84+
@ForceInline
85+
private long nextOffset(long offset) {
86+
return offset + Unsafe.ARRAY_OBJECT_INDEX_SCALE;
87+
}
88+
89+
// returns index at which the probe key is present; or if absent,
90+
// (-i - 1) where i is location where element should be inserted.
91+
@ForceInline
92+
private static int probe(Object[] table, Object pk) {
93+
int idx = Math.floorMod(pk.hashCode(), table.length >> 1) << 1;
94+
while (true) {
95+
Object ek = table[idx];
96+
if (ek == null) {
97+
return -idx - 1;
98+
} else if (pk.equals(ek)) {
99+
return idx;
100+
} else if ((idx += 2) == table.length) {
101+
idx = 0;
102+
}
103+
}
104+
}
105+
106+
private static int availableIndex(int probe) {
107+
return -probe - 1;
108+
}
109+
110+
static <T> StableComponentContainer<T> of(Object[] inputs) {
111+
// Prepopulate all the keys upfront
112+
final Object[] table = new Object[inputs.length << 2];
113+
for (Object type : inputs) {
114+
final int probe = probe(table, type);
115+
assert probe < 0;
116+
final int keyIndex = availableIndex(probe);
117+
table[keyIndex] = type;
118+
}
119+
return new StableComponentContainerImpl<>(table);
120+
}
121+
122+
}

0 commit comments

Comments
 (0)