Skip to content

Commit 2e16229

Browse files
committedJan 8, 2024
Add exploded sandbox
1 parent faf1fe0 commit 2e16229

File tree

1 file changed

+315
-0
lines changed

1 file changed

+315
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @summary Verify basic SandboxTest operations
27+
* @modules java.base/jdk.internal.misc
28+
* @modules java.base/jdk.internal.vm.annotation
29+
* @modules java.base/jdk.internal
30+
* @enablePreview
31+
* @run junit SandboxTest
32+
*/
33+
34+
import jdk.internal.ValueBased;
35+
import jdk.internal.misc.Unsafe;
36+
import jdk.internal.vm.annotation.Stable;
37+
import org.junit.jupiter.api.Test;
38+
39+
import java.util.NoSuchElementException;
40+
import java.util.function.Supplier;
41+
42+
import static org.junit.jupiter.api.Assertions.*;
43+
44+
final class SandboxTest {
45+
46+
// Holds a general value
47+
// This is basically a MethodHandle
48+
interface Value<V> extends Supplier<V> {
49+
@Override V get();
50+
V getVolatile();
51+
void cas(V o);
52+
boolean needsFreeze();
53+
54+
static <V> Value<V> ofObject() {
55+
return new StandardValue<>();
56+
}
57+
58+
static Value<Integer> ofInt() {
59+
return new IntValue();
60+
}
61+
}
62+
63+
// Holds an internal byte state
64+
// This is basically a MethodHandle
65+
interface State extends Supplier<Byte> {
66+
byte UNBOUND = 0;
67+
byte NON_NULL = 1;
68+
byte NULL = 2;
69+
byte ERROR = 3;
70+
71+
@Override Byte get();
72+
byte getAsByte();
73+
byte getVolatile();
74+
void cas(byte state);
75+
76+
static State create() {
77+
return new StandardState();
78+
}
79+
}
80+
81+
// This defines a Computed Constant.
82+
// It has no operations. Instead, it is pure data
83+
interface CC<V> {
84+
State state();
85+
Value<V> value();
86+
Supplier<? extends V> supplier();
87+
88+
static <V> CC<V> of(State state,
89+
Value<V> value,
90+
Supplier<? extends V> supplier) {
91+
return new StandardCC<>(state, value, supplier);
92+
}
93+
94+
}
95+
96+
// Operators on CC
97+
98+
static boolean isBound(CC<?> cc) {
99+
byte s = cc.state().getAsByte();
100+
return s == State.NON_NULL || s == State.NULL ||
101+
(s = cc.state().getVolatile()) == State.NON_NULL || s == State.NULL;
102+
}
103+
104+
static <V> V get(CC<V> cc) {
105+
V v = cc.value().get();
106+
if (v != null) {
107+
return v;
108+
}
109+
if (cc.state().getAsByte() == State.NULL) {
110+
return null;
111+
}
112+
return Util.slowPath(cc, null, true);
113+
}
114+
115+
// Tests
116+
117+
@Test
118+
void ofObject() {
119+
CC<Long> cc = CC.of(State.create(), Value.ofObject(), () -> 42L);
120+
assertEquals(42L, (long) get(cc));
121+
assertEquals(42L, (long) get(cc));
122+
}
123+
124+
@Test
125+
void ofIntegerAsObject() {
126+
CC<Integer> cc = CC.of(State.create(), Value.ofObject(), () -> 42);
127+
assertEquals(42, (int) get(cc));
128+
assertEquals(42, (int) get(cc));
129+
}
130+
131+
@Test
132+
void ofIntegerAsInt() {
133+
CC<Integer> cc = CC.of(State.create(), Value.ofInt(), () -> 42);
134+
assertEquals(42, (int) get(cc));
135+
assertEquals(42, (int) get(cc));
136+
}
137+
138+
// Implementations
139+
140+
@ValueBased
141+
public record StandardCC<V>(State state,
142+
Value<V> value,
143+
Supplier<? extends V> supplier) implements SandboxTest.CC<V> {
144+
}
145+
146+
private static class StandardValue<V> implements Value<V> {
147+
148+
private static final long VALUE_OFFSET = Unsafe.getUnsafe().objectFieldOffset(StandardValue.class, "value");
149+
150+
/**
151+
* This field holds a bound lazy value. If != null, a value is bound, otherwise the state
152+
* field needs to be consulted.
153+
* <p>
154+
* This field is accessed indirectly via Unsafe operations
155+
*/
156+
@Stable
157+
private V value;
158+
159+
public V get() {
160+
return value;
161+
}
162+
163+
@SuppressWarnings("unchecked")
164+
public V getVolatile() {
165+
return (V) Unsafe.getUnsafe().getReferenceVolatile(this, VALUE_OFFSET);
166+
}
167+
168+
public void cas(V o) {
169+
if (!Unsafe.getUnsafe().compareAndSetReference(this, VALUE_OFFSET, null, o)) {
170+
throw new InternalError("Value was not null: " + getVolatile());
171+
}
172+
}
173+
174+
@Override
175+
public boolean needsFreeze() {
176+
return true;
177+
}
178+
}
179+
180+
private static class IntValue implements Value<Integer> {
181+
182+
private static final long VALUE_OFFSET = Unsafe.getUnsafe().objectFieldOffset(IntValue.class, "value");
183+
184+
@Stable
185+
int value;
186+
187+
@Override
188+
public Integer get() {
189+
int v = value;
190+
if (v != 0) {
191+
return v;
192+
}
193+
// We do not know what it is...
194+
return null;
195+
}
196+
197+
@Override
198+
public Integer getVolatile() {
199+
int v = Unsafe.getUnsafe().getIntVolatile(this, VALUE_OFFSET);
200+
if (v != 0) {
201+
return v;
202+
}
203+
return null;
204+
}
205+
206+
@Override
207+
public void cas(Integer o) {
208+
System.out.println("cas to " + o);
209+
int val = o;
210+
if (!Unsafe.getUnsafe().compareAndSetInt(this, VALUE_OFFSET, 0, val)) {
211+
throw new InternalError("Value was not zero: " + getVolatile());
212+
}
213+
}
214+
215+
@Override
216+
public boolean needsFreeze() {
217+
return false;
218+
}
219+
}
220+
221+
private static class StandardState implements State {
222+
223+
private static final long STATE_OFFSET = Unsafe.getUnsafe().objectFieldOffset(StandardState.class, "state");
224+
/**
225+
* This non-final state field is used for flagging: 0) if the value is never bound
226+
* (State.UNBOUND) 1) Flagging if a non-null value is bound (State.BOUND_NON_NULL) 2) if the
227+
* value was actually evaluated to null (State.BOUND_NULL) 3) if the initial supplier threw
228+
* an exception (State.ERROR)
229+
* <p>
230+
* This field is accessed indirectly via Unsafe operations
231+
*/
232+
@Stable
233+
private byte state;
234+
235+
@Override
236+
public Byte get() {
237+
return state;
238+
}
239+
240+
@Override
241+
public byte getAsByte() {
242+
return state;
243+
}
244+
245+
@Override
246+
public byte getVolatile() {
247+
return Unsafe.getUnsafe().getByteVolatile(this, STATE_OFFSET);
248+
}
249+
250+
@Override
251+
public void cas(byte state) {
252+
if (!Unsafe.getUnsafe().compareAndSetByte(this, STATE_OFFSET, (byte) 0, state)) {
253+
throw new InternalError("State was not zero: " + getVolatile());
254+
}
255+
}
256+
}
257+
258+
private static final class Util {
259+
260+
private Util() {}
261+
262+
private static <V> V slowPath(CC<V> cc,
263+
V other,
264+
boolean rethrow) {
265+
266+
synchronized (cc.state()) {
267+
// Under synchronization, visibility and atomicy is guaranteed for
268+
// the fields "value" and "state" as they only change within this block.
269+
return switch (cc.state().getAsByte()) {
270+
case State.UNBOUND -> bindValue(cc, rethrow, other);
271+
case State.NON_NULL -> cc.value().get();
272+
case State.NULL -> null;
273+
default -> {
274+
if (rethrow) {
275+
throw new NoSuchElementException("StandardCC: A previous provider threw an exception");
276+
} else {
277+
yield other;
278+
}
279+
}
280+
};
281+
}
282+
}
283+
284+
static private <V> V bindValue(CC<V> cc, boolean rethrow, V other) {
285+
// setBindingVolatile(true);
286+
try {
287+
V v = cc.supplier().get();
288+
if (v == null) {
289+
cc.state().cas(State.NULL);
290+
} else {
291+
cc.value().cas(v);
292+
// Insert a memory barrier for store/store operations
293+
if (cc.value().needsFreeze()) {
294+
// ConstantUtil.freeze();
295+
}
296+
cc.state().cas(State.NON_NULL);
297+
}
298+
return v;
299+
} catch (Throwable e) {
300+
cc.state().cas(State.ERROR);
301+
if (e instanceof Error err) {
302+
// Always rethrow errors
303+
throw err;
304+
}
305+
if (rethrow) {
306+
throw new NoSuchElementException(e);
307+
}
308+
return other;
309+
} finally {
310+
// setBindingVolatile(false);
311+
}
312+
}
313+
}
314+
315+
}

0 commit comments

Comments
 (0)