Skip to content

Commit af56822

Browse files
committed
[GR-60110] Implement Crema runtime constant pool.
PullRequest: graal/21537
2 parents d757310 + f4334b3 commit af56822

File tree

13 files changed

+1031
-122
lines changed

13 files changed

+1031
-122
lines changed

espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ParserConstantPool.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@
2424
*/
2525
package com.oracle.truffle.espresso.classfile;
2626

27+
import java.nio.ByteBuffer;
2728
import java.util.Arrays;
29+
import java.util.function.Function;
2830

2931
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
32+
import com.oracle.truffle.espresso.classfile.descriptors.ModifiedUTF8;
3033
import com.oracle.truffle.espresso.classfile.descriptors.Symbol;
3134

3235
public final class ParserConstantPool extends ConstantPool {
@@ -67,4 +70,69 @@ public ParserConstantPool patchForHiddenClass(int thisKlassIndex, Symbol<?> newN
6770
return new ParserConstantPool(newTags, newEntries, newSymbols, majorVersion, minorVersion);
6871
}
6972

73+
/**
74+
* Creates a precise dump of the symbolic CP information, unlike {@link #toRawBytes()}, which
75+
* may not be precisely reversible. The {@link ParserConstantPool} can be de-serialized with
76+
* {@link #fromBytesForSerialization(byte[], Symbolify)}.
77+
*/
78+
public byte[] toBytesForSerialization() {
79+
int byteSize = 0;
80+
byteSize += Integer.BYTES; // majorVersion
81+
byteSize += Integer.BYTES; // minorVersion
82+
byteSize += Integer.BYTES; // length
83+
assert tags.length == entries.length;
84+
byteSize += tags.length; // tags
85+
byteSize += entries.length * Integer.BYTES; // entries
86+
byteSize += Integer.BYTES; // symbols.length
87+
for (Symbol<?> symbol : symbols) {
88+
byteSize += Integer.BYTES; // sym.length
89+
byteSize += symbol.length(); // sym.bytes
90+
}
91+
byte[] bytes = new byte[byteSize];
92+
ByteBuffer bb = ByteBuffer.wrap(bytes);
93+
bb.putInt(majorVersion);
94+
bb.putInt(minorVersion);
95+
bb.putInt(tags.length);
96+
bb.put(tags);
97+
for (int entry : entries) {
98+
bb.putInt(entry);
99+
}
100+
bb.putInt(symbols.length);
101+
for (Symbol<?> symbol : symbols) {
102+
bb.putInt(symbol.length());
103+
symbol.writeTo(bb);
104+
}
105+
assert !bb.hasRemaining();
106+
return bytes;
107+
}
108+
109+
@FunctionalInterface
110+
public interface Symbolify<T extends ModifiedUTF8> extends Function<byte[], Symbol<T>> {
111+
}
112+
113+
/**
114+
* Recovers a symbolic CP information, from bytes produces by {@link #toRawBytes()}.
115+
*/
116+
public static ParserConstantPool fromBytesForSerialization(byte[] bytes, Symbolify<? extends ModifiedUTF8> symbolify) {
117+
ByteBuffer bb = ByteBuffer.wrap(bytes);
118+
int majorVersion = bb.getInt();
119+
int minorVersion = bb.getInt();
120+
int length = bb.getInt();
121+
byte[] tags = new byte[length];
122+
bb.get(tags);
123+
int[] entries = new int[length];
124+
for (int i = 0; i < length; i++) {
125+
entries[i] = bb.getInt();
126+
}
127+
int symbolsLength = bb.getInt();
128+
Symbol<?>[] symbols = new Symbol<?>[symbolsLength];
129+
for (int i = 0; i < symbolsLength; i++) {
130+
int symbolLength = bb.getInt();
131+
byte[] symbolBytes = new byte[symbolLength];
132+
bb.get(symbolBytes);
133+
symbols[i] = symbolify.apply(symbolBytes);
134+
}
135+
assert !bb.hasRemaining();
136+
return new ParserConstantPool(tags, entries, symbols, majorVersion, minorVersion);
137+
}
70138
}

espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/descriptors/SignatureSymbols.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,17 @@ public Symbol<Signature> getOrCreateValidSignature(ByteSequence signatureBytes)
105105
return getOrCreateValidSignature(signatureBytes, false);
106106
}
107107

108+
/**
109+
* Creates or retrieves a valid method signature symbol from a String.
110+
*
111+
* @return The signature Symbol if valid, null otherwise
112+
*
113+
* @see Validation#validSignatureDescriptor(ByteSequence)
114+
*/
115+
public Symbol<Signature> getOrCreateValidSignature(String signatureString) {
116+
return getOrCreateValidSignature(ByteSequence.create(signatureString), false);
117+
}
118+
108119
/**
109120
* Creates or retrieves a valid method signature symbol from a byte sequence.
110121
*

espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/meta/RuntimeAccess.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@
3232
* Provides access to some VM-specific capabilities, such as throwing exceptions, or obtaining the
3333
* implementor's supported {@link JavaVersion}.
3434
*
35+
* <h2 id="simpleFormat">Simple message format</h2>
36+
* <p>
37+
* A strict subset of {@link String#format(String, Object...)} that <b>ONLY</b> supports:
38+
* <ul>
39+
* <li>"%s" -> {@link Object#toString()}</li>
40+
* <li>"%n" -> {@link System#lineSeparator()}</li>
41+
* <li>"%%" -> "%"</li> The number of arguments and modifiers must match exactly.
42+
* </ul>
43+
*
3544
* @param <C> The class providing access to the VM-side java {@link Class}.
3645
* @param <M> The class providing access to the VM-side java {@link java.lang.reflect.Method}.
3746
* @param <F> The class providing access to the VM-side java {@link java.lang.reflect.Field}.
@@ -47,13 +56,13 @@ public interface RuntimeAccess<C extends TypeAccess<C, M, F>, M extends MethodAc
4756
* is given by the passed {@link ErrorType}.
4857
* <p>
4958
* The caller provides an error message that can be constructed using
50-
* {@code String.format(Locale.ENGLISH, messageFormat, args)}.
59+
* <a href="#simpleFormat">Simple message format</a>
5160
*/
5261
RuntimeException throwError(ErrorType error, String messageFormat, Object... args);
5362

5463
/**
5564
* If {@code error} is an exception that can be thrown by {@link #throwError}, returns the
56-
* correspondin {@link ErrorType}. Returns null otherwise.
65+
* corresponding {@link ErrorType}. Returns null otherwise.
5766
*/
5867
ErrorType getErrorType(Throwable error);
5968

@@ -81,15 +90,15 @@ public interface RuntimeAccess<C extends TypeAccess<C, M, F>, M extends MethodAc
8190
* aborted.
8291
* <p>
8392
* The caller provides an error message that can be constructed using
84-
* {@code String.format(Locale.ENGLISH, messageFormat, args)}.
93+
* <a href="#simpleFormat">Simple message format</a>
8594
*/
8695
RuntimeException fatal(String messageFormat, Object... args);
8796

8897
/**
8998
* Signals that an unexpected exception was seen and that the current operation must be aborted.
9099
* <p>
91100
* The caller provides the unexpected exception and an error message that can be constructed
92-
* using {@code String.format(Locale.ENGLISH, messageFormat, args)}.
101+
* using <a href="#simpleFormat">Simple message format</a>
93102
*/
94103
RuntimeException fatal(Throwable t, String messageFormat, Object... args);
95104
}

substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java

Lines changed: 120 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,34 @@
3333

3434
import com.oracle.svm.core.heap.UnknownObjectField;
3535
import com.oracle.svm.core.util.VMError;
36+
import com.oracle.svm.espresso.classfile.ConstantPool;
3637
import com.oracle.svm.espresso.classfile.ParserConstantPool;
37-
import com.oracle.svm.espresso.classfile.descriptors.Symbol;
3838
import com.oracle.svm.interpreter.metadata.serialization.VisibleForSerialization;
3939

40-
import jdk.vm.ci.meta.ConstantPool;
4140
import jdk.vm.ci.meta.JavaConstant;
4241
import jdk.vm.ci.meta.JavaField;
4342
import jdk.vm.ci.meta.JavaMethod;
4443
import jdk.vm.ci.meta.JavaType;
4544
import jdk.vm.ci.meta.ResolvedJavaMethod;
4645
import jdk.vm.ci.meta.Signature;
46+
import jdk.vm.ci.meta.UnresolvedJavaField;
47+
import jdk.vm.ci.meta.UnresolvedJavaMethod;
48+
import jdk.vm.ci.meta.UnresolvedJavaType;
4749

48-
public final class InterpreterConstantPool extends com.oracle.svm.espresso.classfile.ConstantPool implements ConstantPool {
50+
/**
51+
* JVMCI's {@link jdk.vm.ci.meta.ConstantPool} is not designed to be used in a performance-sensitive
52+
* bytecode interpreter, so a Espresso-like CP implementation is used instead for performance.
53+
* <p>
54+
* This class doesn't support runtime resolution on purpose, but supports pre-resolved entries
55+
* instead for AOT types.
56+
*/
57+
public class InterpreterConstantPool extends ConstantPool implements jdk.vm.ci.meta.ConstantPool {
58+
59+
final InterpreterResolvedObjectType holder;
60+
final ParserConstantPool parserConstantPool;
4961

50-
private final InterpreterResolvedObjectType holder;
5162
// Assigned after analysis.
52-
@UnknownObjectField(types = Object[].class) private Object[] entries;
63+
@UnknownObjectField(types = Object[].class) protected Object[] cachedEntries;
5364

5465
Object objAt(int cpi) {
5566
if (cpi == 0) {
@@ -60,23 +71,28 @@ Object objAt(int cpi) {
6071
// where an appropriate error should be thrown.
6172
throw VMError.shouldNotReachHere("Cannot resolve CP entry 0");
6273
}
63-
return entries[cpi];
74+
return cachedEntries[cpi];
6475
}
6576

66-
private InterpreterConstantPool(InterpreterResolvedObjectType holder, Object[] entries) {
67-
super(new byte[]{}, new int[]{}, Symbol.EMPTY_ARRAY, 0, 0);
77+
protected InterpreterConstantPool(InterpreterResolvedObjectType holder, ParserConstantPool parserConstantPool, Object[] cachedEntries) {
78+
super(parserConstantPool);
6879
this.holder = MetadataUtil.requireNonNull(holder);
69-
this.entries = MetadataUtil.requireNonNull(entries);
80+
this.parserConstantPool = parserConstantPool;
81+
this.cachedEntries = MetadataUtil.requireNonNull(cachedEntries);
82+
}
83+
84+
protected InterpreterConstantPool(InterpreterResolvedObjectType holder, ParserConstantPool parserConstantPool) {
85+
this(holder, parserConstantPool, new Object[parserConstantPool.length()]);
7086
}
7187

7288
@VisibleForSerialization
73-
public static InterpreterConstantPool create(InterpreterResolvedObjectType holder, Object[] entries) {
74-
return new InterpreterConstantPool(holder, entries);
89+
public static InterpreterConstantPool create(InterpreterResolvedObjectType holder, ParserConstantPool parserConstantPool, Object[] cachedEntries) {
90+
return new InterpreterConstantPool(holder, parserConstantPool, cachedEntries);
7591
}
7692

7793
@Override
7894
public int length() {
79-
return entries.length;
95+
return cachedEntries.length;
8096
}
8197

8298
@Override
@@ -128,25 +144,24 @@ public JavaConstant lookupAppendix(int cpi, int opcode) {
128144

129145
@VisibleForSerialization
130146
@Platforms(Platform.HOSTED_ONLY.class)
131-
public Object[] getEntries() {
132-
return entries;
147+
public Object[] getCachedEntries() {
148+
return cachedEntries;
149+
}
150+
151+
public Object peekCachedEntry(int cpi) {
152+
return cachedEntries[cpi];
133153
}
134154

135155
public InterpreterResolvedObjectType getHolder() {
136156
return holder;
137157
}
138158

139-
// region Unimplemented methods
140-
141159
@Override
142160
public RuntimeException classFormatError(String message) {
143161
throw new ClassFormatError(message);
144162
}
145163

146-
@Override
147-
public ParserConstantPool getParserConstantPool() {
148-
throw VMError.unimplemented("getParserConstantPool");
149-
}
164+
// region Unimplemented methods
150165

151166
@Override
152167
public void loadReferencedType(int cpi, int opcode) {
@@ -169,4 +184,89 @@ public Signature lookupSignature(int cpi) {
169184
}
170185

171186
// endregion Unimplemented methods
187+
188+
@Override
189+
public ParserConstantPool getParserConstantPool() {
190+
return parserConstantPool;
191+
}
192+
193+
protected Object resolve(int cpi, @SuppressWarnings("unused") InterpreterResolvedObjectType accessingClass) {
194+
assert Thread.holdsLock(this);
195+
assert cpi != 0; // guaranteed by the caller
196+
197+
@SuppressWarnings("unused")
198+
Tag tag = tagAt(cpi); // CPI bounds check
199+
200+
Object entry = cachedEntries[cpi];
201+
if (isUnresolved(entry)) {
202+
/*
203+
* Runtime resolution is deliberately unsupported for AOT types (using base
204+
* InterpreterConstantPool). This can be relaxed in the future e.g. by attaching a
205+
* RuntimeInterpreterConstantPool instead.
206+
*/
207+
throw new UnsupportedResolutionException();
208+
}
209+
210+
return entry;
211+
}
212+
213+
public Object resolvedAt(int cpi, InterpreterResolvedObjectType accessingClass) {
214+
Object entry = cachedEntries[cpi];
215+
if (isUnresolved(entry)) {
216+
// TODO(peterssen): GR-68611 Avoid deadlocks when hitting breakpoints (JDWP debugger)
217+
// during class resolution.
218+
/*
219+
* Class resolution can run arbitrary code (not in the to-be resolved class <clinit>
220+
* but) in the user class loaders where it can hit a breakpoint (JDWP debugger), causing
221+
* a deadlock.
222+
*/
223+
synchronized (this) {
224+
entry = cachedEntries[cpi];
225+
if (isUnresolved(entry)) {
226+
cachedEntries[cpi] = entry = resolve(cpi, accessingClass);
227+
}
228+
}
229+
}
230+
231+
assert !isUnresolved(entry);
232+
if (entry instanceof Throwable throwable) {
233+
// Cached exception.
234+
throw uncheckedThrow(throwable);
235+
}
236+
237+
return entry;
238+
}
239+
240+
private static boolean isUnresolved(Object entry) {
241+
return entry == null || entry instanceof UnresolvedJavaType || entry instanceof UnresolvedJavaMethod || entry instanceof UnresolvedJavaField;
242+
}
243+
244+
@SuppressWarnings("unchecked")
245+
private static <T extends Throwable> RuntimeException uncheckedThrow(Throwable t) throws T {
246+
throw (T) t;
247+
}
248+
249+
public InterpreterResolvedJavaField resolvedFieldAt(InterpreterResolvedObjectType accessingKlass, int cpi) {
250+
Object resolvedEntry = resolvedAt(cpi, accessingKlass);
251+
assert resolvedEntry != null;
252+
return (InterpreterResolvedJavaField) resolvedEntry;
253+
}
254+
255+
public InterpreterResolvedJavaMethod resolvedMethodAt(InterpreterResolvedObjectType accessingKlass, int cpi) {
256+
Object resolvedEntry = resolvedAt(cpi, accessingKlass);
257+
assert resolvedEntry != null;
258+
return (InterpreterResolvedJavaMethod) resolvedEntry;
259+
}
260+
261+
public InterpreterResolvedObjectType resolvedTypeAt(InterpreterResolvedObjectType accessingKlass, int cpi) {
262+
Object resolvedEntry = resolvedAt(cpi, accessingKlass);
263+
assert resolvedEntry != null;
264+
return (InterpreterResolvedObjectType) resolvedEntry;
265+
}
266+
267+
public String resolveStringAt(int cpi) {
268+
Object resolvedEntry = resolvedAt(cpi, null);
269+
assert resolvedEntry != null;
270+
return (String) resolvedEntry;
271+
}
172272
}

0 commit comments

Comments
 (0)