Skip to content

Commit 7050fbb

Browse files
Avoid keeping parsed method parameter attributes in memory
* Keep only the raw bytes, which are more compact * Fix issues when an argument does not have a name * Remove index < 0 checks when index is known to be positive
1 parent a7b97f3 commit 7050fbb

9 files changed

Lines changed: 92 additions & 59 deletions

File tree

espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaMethod.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,8 @@ public Parameter[] getParameters() {
241241
Parameter[] result = new Parameter[size];
242242
for (int i = 0; i < size; i++) {
243243
Value parameter = table.getArrayElement(i);
244-
String name = parameter.getMember("name").asString();
244+
Value nameValue = parameter.getMember("name");
245+
String name = nameValue.isNull() ? null : nameValue.asString();
245246
int modifiers = parameter.getMember("modifiers").asInt();
246247
result[i] = new Parameter(name, modifiers, this, i);
247248
}

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,17 +1400,13 @@ private SignatureAttribute parseSignatureAttribute(Symbol<Name> name) throws Val
14001400
}
14011401

14021402
private MethodParametersAttribute parseMethodParameters(Symbol<Name> name) {
1403+
int startPosition = stream.getPosition();
14031404
int entryCount = stream.readU1();
14041405
if (entryCount == 0) {
14051406
return MethodParametersAttribute.EMPTY;
14061407
}
1407-
MethodParametersAttribute.Entry[] entries = new MethodParametersAttribute.Entry[entryCount];
1408-
for (int i = 0; i < entryCount; i++) {
1409-
int nameIndex = stream.readU2();
1410-
int accessFlags = stream.readU2();
1411-
entries[i] = new MethodParametersAttribute.Entry(nameIndex, accessFlags);
1412-
}
1413-
return new MethodParametersAttribute(name, entries);
1408+
stream.skip(entryCount * 4);
1409+
return new MethodParametersAttribute(name, stream.getByteRange(startPosition, stream.getPosition() - startPosition));
14141410
}
14151411

14161412
private ExceptionsAttribute parseExceptions(Symbol<Name> name) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,7 @@ public boolean isSame(int thisIndex, int otherIndex, ConstantPool otherPool) {
901901
return false;
902902
}
903903
return switch (tag) {
904-
case INVALID -> false; // TODO(peterssen): Check other is also invalid?
904+
case INVALID -> true;
905905
case UTF8 -> utf8At(thisIndex) == otherPool.utf8At(otherIndex);
906906
case INTEGER -> intAt(thisIndex) == otherPool.intAt(otherIndex);
907907
case FLOAT -> Float.floatToRawIntBits(floatAt(thisIndex)) == Float.floatToRawIntBits(otherPool.floatAt(otherIndex));

espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/attributes/MethodParametersAttribute.java

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,15 @@ public final class MethodParametersAttribute extends Attribute {
3333

3434
public static final Symbol<Name> NAME = ParserNames.MethodParameters;
3535

36-
public static final MethodParametersAttribute EMPTY = new MethodParametersAttribute(NAME, Entry.EMPTY_ARRAY);
37-
38-
public Entry[] getEntries() {
39-
return entries;
40-
}
36+
public static final MethodParametersAttribute EMPTY = new MethodParametersAttribute(NAME, new byte[]{0});
4137

4238
public static final class Entry {
43-
44-
public static final Entry[] EMPTY_ARRAY = new Entry[0];
45-
4639
private final int nameIndex;
4740
private final int accessFlags;
4841

4942
public Entry(int nameIndex, int accessFlags) {
43+
assert nameIndex >= 0;
44+
assert accessFlags >= 0;
5045
this.nameIndex = nameIndex;
5146
this.accessFlags = accessFlags;
5247
}
@@ -64,36 +59,76 @@ public boolean isSame(Entry otherEntry, ConstantPool thisPool, ConstantPool othe
6459
}
6560
}
6661

67-
private final Entry[] entries;
68-
69-
public MethodParametersAttribute(Symbol<Name> name, Entry[] entries) {
62+
/**
63+
* Raw {@code MethodParameters} attribute contents as stored in the class file:
64+
* <ul>
65+
* <li>the first byte is the parameter count</li>
66+
* <li>each entry is encoded as {@code u2 name_index, u2 access_flags}</li>
67+
* </ul>
68+
* A {@code name_index} of {@code 0} denotes an unnamed parameter.
69+
*/
70+
private final byte[] data;
71+
72+
public MethodParametersAttribute(Symbol<Name> name, byte[] data) {
7073
assert name == NAME;
71-
this.entries = entries;
74+
assert data.length > 0;
75+
assert Byte.toUnsignedInt(data[0]) * 4 == data.length - 1;
76+
this.data = data;
77+
}
78+
79+
/**
80+
* Returns the number of {@code MethodParameters} entries stored in this attribute.
81+
*/
82+
public int entryCount() {
83+
return Byte.toUnsignedInt(data[0]);
84+
}
85+
86+
/**
87+
* Decodes and returns the entry at {@code index}.
88+
*/
89+
public Entry entryAt(int index) {
90+
if (index < 0 || index >= entryCount()) {
91+
throw new IndexOutOfBoundsException("index " + index + " out of bounds for list of size " + entryCount() + ".");
92+
}
93+
int offset = 1 + index * 4;
94+
return new Entry(readU2(data, offset), readU2(data, offset + 2));
7295
}
7396

7497
@Override
7598
public boolean isSame(Attribute other, ConstantPool thisPool, ConstantPool otherPool) {
76-
if (!super.isSame(other, thisPool, otherPool)) {
99+
if (this == other) {
100+
return true;
101+
}
102+
if (other == null || getClass() != other.getClass() || getName() != other.getName()) {
77103
return false;
78104
}
79105
MethodParametersAttribute that = (MethodParametersAttribute) other;
80-
return entriesSameAs(that.entries, thisPool, otherPool);
106+
return entriesSameAs(that, thisPool, otherPool);
81107
}
82108

83-
private boolean entriesSameAs(Entry[] otherEntries, ConstantPool thisPool, ConstantPool otherPool) {
84-
if (entries.length != otherEntries.length) {
109+
private boolean entriesSameAs(MethodParametersAttribute other, ConstantPool thisPool, ConstantPool otherPool) {
110+
if (entryCount() != other.entryCount()) {
85111
return false;
86112
}
87-
for (int i = 0; i < entries.length; i++) {
88-
if (!entries[i].isSame(otherEntries[i], thisPool, otherPool)) {
113+
for (int i = 0; i < entryCount(); i++) {
114+
if (!entryAt(i).isSame(other.entryAt(i), thisPool, otherPool)) {
89115
return false;
90116
}
91117
}
92118
return true;
93119
}
94120

121+
@Override
122+
public byte[] getData() {
123+
return data;
124+
}
125+
95126
@Override
96127
public Symbol<Name> getName() {
97128
return NAME;
98129
}
130+
131+
private static int readU2(byte[] data, int offset) {
132+
return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
133+
}
99134
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Method.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2423,12 +2423,13 @@ static Object parameters(Method receiver, @SuppressWarnings("unused") String mem
24232423
if (methodParameters == null) {
24242424
return StaticObject.NULL;
24252425
}
2426-
MethodParametersAttribute.Entry[] entries = methodParameters.getEntries();
2427-
ParameterInteropWrapper[] parameters = new ParameterInteropWrapper[entries.length];
2426+
int entryCount = methodParameters.entryCount();
2427+
ParameterInteropWrapper[] parameters = new ParameterInteropWrapper[entryCount];
24282428
ConstantPool constantPool = receiver.getConstantPool();
2429-
for (int i = 0; i < entries.length; i++) {
2430-
MethodParametersAttribute.Entry entry = entries[i];
2431-
parameters[i] = new ParameterInteropWrapper(constantPool.utf8At(entry.getNameIndex()), entry.getAccessFlags());
2429+
for (int i = 0; i < entryCount; i++) {
2430+
MethodParametersAttribute.Entry entry = methodParameters.entryAt(i);
2431+
Symbol<?> name = entry.getNameIndex() != 0 ? constantPool.utf8At(entry.getNameIndex()) : null;
2432+
parameters[i] = new ParameterInteropWrapper(name, entry.getAccessFlags());
24322433
}
24332434
return new KeysArray<>(parameters);
24342435
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/ParameterInteropWrapper.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.oracle.truffle.api.library.ExportMessage;
3535
import com.oracle.truffle.espresso.classfile.descriptors.Symbol;
3636
import com.oracle.truffle.espresso.impl.KeysArray;
37+
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
3738

3839
/**
3940
* {@linkplain InteropLibrary Interop} object used to carry parameter metadata (name and modifiers)
@@ -68,8 +69,8 @@ abstract static class ReadMember {
6869
static final String MODIFIERS = "modifiers";
6970

7071
@Specialization(guards = "NAME.equals(member)")
71-
static String getName(ParameterInteropWrapper receiver, @SuppressWarnings("unused") String member) {
72-
return receiver.name.toString();
72+
static Object getName(ParameterInteropWrapper receiver, @SuppressWarnings("unused") String member) {
73+
return receiver.name == null ? StaticObject.NULL : receiver.name.toString();
7374
}
7475

7576
@Specialization(guards = "MODIFIERS.equals(member)")

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/SubstitutionScope.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,10 @@ private String[] fetchNames() {
103103
return new String[0];
104104
}
105105
// verify parameter attribute first
106-
MethodParametersAttribute.Entry[] entries = methodParameters.getEntries();
106+
int entryCount = methodParameters.entryCount();
107107
int cpLength = method.getConstantPool().length();
108-
for (MethodParametersAttribute.Entry entry : entries) {
108+
for (int i = 0; i < entryCount; i++) {
109+
MethodParametersAttribute.Entry entry = methodParameters.entryAt(i);
109110
int nameIndex = entry.getNameIndex();
110111
if (nameIndex < 0 || nameIndex >= cpLength) {
111112
return new String[0];
@@ -114,9 +115,9 @@ private String[] fetchNames() {
114115
return new String[0];
115116
}
116117
}
117-
String[] result = new String[entries.length];
118-
for (int i = 0; i < entries.length; i++) {
119-
MethodParametersAttribute.Entry entry = entries[i];
118+
String[] result = new String[entryCount];
119+
for (int i = 0; i < entryCount; i++) {
120+
MethodParametersAttribute.Entry entry = methodParameters.entryAt(i);
120121
// For a 0 index, give an empty name.
121122
String name;
122123
if (entry.getNameIndex() != 0) {

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/VM.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3528,19 +3528,20 @@ public Integer visitFrame(FrameInstance frameInstance) {
35283528
}
35293529
// Verify first.
35303530
/*
3531-
* If number of entries in ParametersAttribute is inconsistent with actual parameters from
3532-
* the signature, it will be caught in guest java code.
3531+
* If the number of entries in ParametersAttribute is inconsistent with the actual
3532+
* parameters from the signature, it will be caught in guest java code.
35333533
*/
3534-
int cpLength = method.getConstantPool().length();
3535-
for (MethodParametersAttribute.Entry entry : methodParameters.getEntries()) {
3534+
ConstantPool constantPool = method.getConstantPool();
3535+
int constantPoolLength = constantPool.length();
3536+
for (int i = 0; i < methodParameters.entryCount(); i++) {
3537+
MethodParametersAttribute.Entry entry = methodParameters.entryAt(i);
35363538
int nameIndex = entry.getNameIndex();
3537-
if (nameIndex < 0 || nameIndex >= cpLength) {
3539+
if (nameIndex >= constantPoolLength) {
35383540
profiler.profile(0);
35393541
throw meta.throwExceptionWithMessage(meta.java_lang_IllegalArgumentException, "Constant pool index out of bounds");
35403542
}
3541-
if (nameIndex != 0 && method.getConstantPool().tagAt(nameIndex) != ConstantPool.Tag.UTF8) {
3542-
profiler.profile(1);
3543-
throw meta.throwExceptionWithMessage(meta.java_lang_IllegalArgumentException, "Wrong type at constant pool index");
3543+
if (nameIndex != 0) {
3544+
checkTag(constantPool, nameIndex, ConstantPool.Tag.UTF8, meta, profiler);
35443545
}
35453546
}
35463547

@@ -3554,15 +3555,15 @@ public Integer visitFrame(FrameInstance frameInstance) {
35543555
/* index */ Types._int));
35553556

35563557
// Use attribute's number of parameters.
3557-
return meta.java_lang_reflect_Parameter.allocateReferenceArray(methodParameters.getEntries().length, new IntFunction<StaticObject>() {
3558+
return meta.java_lang_reflect_Parameter.allocateReferenceArray(methodParameters.entryCount(), new IntFunction<StaticObject>() {
35583559
@Override
35593560
public StaticObject apply(int index) {
3560-
MethodParametersAttribute.Entry entry = methodParameters.getEntries()[index];
3561+
MethodParametersAttribute.Entry entry = methodParameters.entryAt(index);
35613562
StaticObject instance = meta.java_lang_reflect_Parameter.allocateInstance(getContext());
35623563
// For a 0 index, give an empty name.
35633564
StaticObject guestName;
35643565
if (entry.getNameIndex() != 0) {
3565-
guestName = meta.toGuestString(method.getConstantPool().utf8At(entry.getNameIndex(), "parameter name").toString());
3566+
guestName = meta.toGuestString(constantPool.utf8At(entry.getNameIndex(), "parameter name").toString());
35663567
} else {
35673568
guestName = getJavaVersion().java9OrLater() ? StaticObject.NULL : meta.toGuestString("");
35683569
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Executable.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,27 +104,24 @@ private RuntimeLoadedExecutableParameterHelper() {
104104
}
105105

106106
static Parameter[] asReflectParameters(Executable executable, MethodParametersAttribute methodParameters, DynamicHub declaringClass) {
107-
MethodParametersAttribute.Entry[] entries = methodParameters.getEntries();
108107
var constantPool = CremaSupport.singleton().getConstantPool(declaringClass);
109-
Parameter[] parameters = new Parameter[entries.length];
108+
int entryCount = methodParameters.entryCount();
109+
Parameter[] parameters = new Parameter[entryCount];
110110
int constantPoolLength = constantPool.length();
111-
for (int i = 0; i < entries.length; i++) {
112-
MethodParametersAttribute.Entry entry = entries[i];
111+
for (int i = 0; i < entryCount; i++) {
112+
MethodParametersAttribute.Entry entry = methodParameters.entryAt(i);
113113
int nameIndex = entry.getNameIndex();
114-
int modifiers = entry.getAccessFlags();
115-
116-
if (nameIndex < 0 || nameIndex >= constantPoolLength) {
114+
if (nameIndex >= constantPoolLength) {
117115
throw new IllegalArgumentException("Constant pool index out of bounds");
118116
}
119-
120117
String name = null;
121118
if (nameIndex != 0) {
122119
if (constantPool.tagAt(nameIndex) != Tag.UTF8) {
123120
throw new IllegalArgumentException("Wrong type at constant pool index");
124121
}
125122
name = constantPool.utf8At(nameIndex, "parameter name").toString();
126123
}
127-
parameters[i] = ReflectionObjectFactory.newParameter(executable, i, name, modifiers);
124+
parameters[i] = ReflectionObjectFactory.newParameter(executable, i, name, entry.getAccessFlags());
128125
}
129126
return parameters;
130127
}

0 commit comments

Comments
 (0)