Skip to content

Commit d0515b6

Browse files
committed
Provide access to LazyConstant via internal fallback module
- introduces "JDK Fallbacks" module for internal use only - module delegates to JDK implementations if available and uses fallbacks if not - the accessors are meant to be temporary friend API with expiration date, only to bridge the gap between lower and upper runtime requirements
1 parent ac407de commit d0515b6

9 files changed

Lines changed: 328 additions & 0 deletions

File tree

.github/workflows/main.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,9 @@ jobs:
284284
if: ${{ matrix.java == '21' }}
285285
run: .github/retry.sh ant $OPTS -f platform/masterfs test
286286

287+
- name: platform/o.n.jdk.fallback
288+
run: ant $OPTS -f platform/o.n.jdk.fallback test
289+
287290
- name: Commit Validation tests
288291
run: .github/retry.sh ant $OPTS -Dcluster.config=$CLUSTER_CONFIG commit-validation
289292

nbbuild/cluster.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ nb.cluster.platform=\
215215
o.n.html.ko4j,\
216216
o.n.html.presenters.spi,\
217217
o.n.html.xhr4j,\
218+
o.n.jdk.fallback,\
218219
o.n.swing.laf.dark,\
219220
o.n.swing.laf.flatlaf,\
220221
o.n.swing.outline,\
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Licensed to the Apache Software Foundation (ASF) under one
5+
or more contributor license agreements. See the NOTICE file
6+
distributed with this work for additional information
7+
regarding copyright ownership. The ASF licenses this file
8+
to you under the Apache License, Version 2.0 (the
9+
"License"); you may not use this file except in compliance
10+
with the License. You may obtain a copy of the License at
11+
12+
http://www.apache.org/licenses/LICENSE-2.0
13+
14+
Unless required by applicable law or agreed to in writing,
15+
software distributed under the License is distributed on an
16+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
KIND, either express or implied. See the License for the
18+
specific language governing permissions and limitations
19+
under the License.
20+
21+
-->
22+
<project basedir="." default="build" name="platform/o.n.jdk.fallback">
23+
<description>Builds, tests, and runs the project org.netbeans.jdk.fallback</description>
24+
<import file="../../nbbuild/templates/projectized.xml"/>
25+
</project>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Manifest-Version: 1.0
2+
AutoUpdate-Show-In-Client: false
3+
AutoUpdate-Essential-Module: true
4+
OpenIDE-Module: o.n.jdk.fallback/0
5+
OpenIDE-Module-Localizing-Bundle: org/netbeans/jdk/fallback/Bundle.properties
6+
OpenIDE-Module-Specification-Version: 0.1
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
javac.compilerargs=-Xlint
19+
javac.release=17
20+
21+
# javadoc.arch=${basedir}/arch.xml
22+
# javadoc.apichanges=${basedir}/apichanges.xml
23+
# javadoc.arch=${basedir}/arch.xml
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Licensed to the Apache Software Foundation (ASF) under one
5+
or more contributor license agreements. See the NOTICE file
6+
distributed with this work for additional information
7+
regarding copyright ownership. The ASF licenses this file
8+
to you under the Apache License, Version 2.0 (the
9+
"License"); you may not use this file except in compliance
10+
with the License. You may obtain a copy of the License at
11+
12+
http://www.apache.org/licenses/LICENSE-2.0
13+
14+
Unless required by applicable law or agreed to in writing,
15+
software distributed under the License is distributed on an
16+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
KIND, either express or implied. See the License for the
18+
specific language governing permissions and limitations
19+
under the License.
20+
21+
-->
22+
<project xmlns="http://www.netbeans.org/ns/project/1">
23+
<type>org.netbeans.modules.apisupport.project</type>
24+
<configuration>
25+
<data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
26+
<code-name-base>o.n.jdk.fallback</code-name-base>
27+
<module-dependencies/>
28+
<test-dependencies>
29+
<test-type>
30+
<name>unit</name>
31+
<test-dependency>
32+
<code-name-base>org.netbeans.libs.junit4</code-name-base>
33+
<compile-dependency/>
34+
</test-dependency>
35+
<test-dependency>
36+
<code-name-base>org.netbeans.modules.nbjunit</code-name-base>
37+
<compile-dependency/>
38+
</test-dependency>
39+
</test-type>
40+
</test-dependencies>
41+
<!-- internal API-->
42+
<friend-packages>
43+
<friend>org.netbeans.modules.maven.indexer</friend>
44+
<package>org.netbeans.jdk.fallback.lang</package>
45+
</friend-packages>
46+
</data>
47+
</configuration>
48+
</project>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
OpenIDE-Module-Name=Simple JDK Fallbacks
19+
OpenIDE-Module-Display-Category=Libraries
20+
OpenIDE-Module-Short-Description=Provides internal, non-permanent and possibly incomplete fallbacks for JDK APIs\
21+
which are useful for NetBeans but not covered by the minimal run requirements yet.
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.netbeans.jdk.fallback.lang;
20+
21+
import java.lang.invoke.MethodHandle;
22+
import java.lang.invoke.MethodHandles;
23+
import java.lang.invoke.MethodType;
24+
import java.util.Objects;
25+
import java.util.function.Supplier;
26+
import java.util.logging.Level;
27+
import java.util.logging.Logger;
28+
29+
/**
30+
* Delegates to JDK's LazyConstant and provides a fallback implementation if not available.
31+
*
32+
* Internal API, may be removed when no longer needed.
33+
*
34+
* @author mbien
35+
*/
36+
public final class NBLazyConstant {
37+
38+
private static final MethodHandle lazyConstantFactory;
39+
40+
static {
41+
Logger log = Logger.getLogger(NBLazyConstant.class.getName());
42+
MethodHandle mh = null;
43+
try {
44+
if (Boolean.getBoolean("nb.jdk.LazyConstant.usefallback")) {
45+
mh = null;
46+
log.log(Level.INFO, "using fallback");
47+
} else if (Runtime.version().feature() >= 26) {
48+
Class<?> entryPoint = Class.forName("java.lang.LazyConstant");
49+
mh = MethodHandles.lookup().findStatic(entryPoint, "of", MethodType.methodType(entryPoint, Supplier.class))
50+
.asType(MethodType.methodType(Supplier.class, Supplier.class));
51+
} else if (Runtime.version().feature() == 25) {
52+
Class<?> entryPoint = Class.forName("java.lang.StableValue");
53+
mh = MethodHandles.lookup().findStatic(entryPoint, "supplier", MethodType.methodType(Supplier.class, Supplier.class));
54+
}
55+
// dryrun - just to be sure
56+
if (mh != null) {
57+
Supplier<?> probe = () -> true;
58+
((Supplier<?>)mh.invokeExact(probe)).get();
59+
}
60+
} catch (Throwable ex) {
61+
mh = null;
62+
log.log(Level.FINE, "using fallback", ex);
63+
}
64+
lazyConstantFactory = mh;
65+
log.log(Level.FINE, () -> "impl=" + String.valueOf(lazyConstantFactory));
66+
}
67+
68+
private NBLazyConstant() {}
69+
70+
/**
71+
* Create a {@link Supplier} for a lazily initializing constant.
72+
* @param computingFunction Factory to create the constant, only called once.
73+
* @return Returns the constant, never null.
74+
*/
75+
@SuppressWarnings("unchecked")
76+
public static <T> Supplier<T> of(Supplier<? extends T> computingFunction) {
77+
Objects.requireNonNull(computingFunction);
78+
if (computingFunction instanceof DoubleCheckedFallback<? extends T> lc) {
79+
return (Supplier<T>) lc;
80+
}
81+
if (lazyConstantFactory != null) {
82+
try {
83+
return (Supplier<T>) lazyConstantFactory.invokeExact(computingFunction);
84+
} catch (RuntimeException ex) {
85+
throw ex;
86+
} catch (Throwable ex) {
87+
throw new RuntimeException(ex);
88+
}
89+
} else {
90+
return new DoubleCheckedFallback<>(computingFunction);
91+
}
92+
}
93+
94+
private static class DoubleCheckedFallback<T> implements Supplier<T> {
95+
96+
private volatile T constant;
97+
private Supplier<? extends T> factory;
98+
99+
private DoubleCheckedFallback(Supplier<? extends T> factory) {
100+
this.factory = factory;
101+
}
102+
103+
@Override
104+
public T get() {
105+
T c = constant;
106+
if (c == null) {
107+
synchronized (this) {
108+
c = constant;
109+
if (c == null) {
110+
c = factory.get();
111+
Objects.requireNonNull(c);
112+
constant = c;
113+
factory = null;
114+
}
115+
}
116+
}
117+
return c;
118+
}
119+
120+
@Override
121+
public String toString() {
122+
return getClass().getSimpleName() + "{factory=" + factory + ", constant=" + constant + '}';
123+
}
124+
}
125+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.netbeans.jdk.fallback.lang;
20+
21+
import java.util.function.Supplier;
22+
import org.junit.Test;
23+
24+
import static org.junit.Assert.*;
25+
import static org.junit.Assume.assumeTrue;
26+
27+
public class NBLazyConstantTest {
28+
29+
@Test
30+
public void testImpl() {
31+
32+
Supplier<Boolean> lazy = NBLazyConstant.of(() -> true);
33+
assertNotNull(lazy);
34+
assertTrue(lazy.get());
35+
36+
String impl;
37+
if (Runtime.version().feature() >= 26) {
38+
impl = "LazyConstant";
39+
} else if (Runtime.version().feature() == 25) {
40+
impl = "StableSupplier";
41+
} else {
42+
impl = "DoubleCheckedFallback";
43+
}
44+
assertTrue(impl + " expected but got " + lazy.getClass(), lazy.getClass().getSimpleName().contains(impl));
45+
}
46+
47+
@Test
48+
public void testConstant() {
49+
Supplier<Double> lazy = NBLazyConstant.of(() -> Math.random());
50+
assertNotNull(lazy);
51+
assertEquals(lazy.get(), lazy.get());
52+
assertEquals(lazy.get(), lazy.get());
53+
assertEquals(lazy.get(), lazy.get());
54+
}
55+
56+
@Test
57+
public void testNPEonNullValue() {
58+
59+
// StableValue allows null, we use the LazyConstant spec
60+
assumeTrue(Runtime.version().feature() != 25);
61+
62+
Supplier<Object> lazy = NBLazyConstant.of(() -> null);
63+
assertNotNull(lazy);
64+
65+
try {
66+
lazy.get();
67+
fail();
68+
} catch (NullPointerException good) {}
69+
70+
try {
71+
lazy.get();
72+
fail();
73+
} catch (NullPointerException stillGood) {}
74+
}
75+
76+
}

0 commit comments

Comments
 (0)