Skip to content

Commit 3aeb673

Browse files
wenshaoliach
authored andcommitted
8338532: Speed up the ClassFile API MethodTypeDesc#ofDescriptor
Reviewed-by: redestad, liach
1 parent 918cf11 commit 3aeb673

File tree

11 files changed

+202
-111
lines changed

11 files changed

+202
-111
lines changed

src/java.base/share/classes/java/lang/constant/ClassDesc.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import static jdk.internal.constant.ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS;
3737
import static jdk.internal.constant.ConstantUtils.arrayDepth;
3838
import static jdk.internal.constant.ConstantUtils.binaryToInternal;
39+
import static jdk.internal.constant.ConstantUtils.forPrimitiveType;
3940
import static jdk.internal.constant.ConstantUtils.internalToBinary;
4041
import static jdk.internal.constant.ConstantUtils.validateBinaryClassName;
4142
import static jdk.internal.constant.ConstantUtils.validateInternalClassName;
@@ -164,7 +165,7 @@ static ClassDesc of(String packageName, String className) {
164165
static ClassDesc ofDescriptor(String descriptor) {
165166
// implicit null-check
166167
return (descriptor.length() == 1)
167-
? Wrapper.forPrimitiveType(descriptor.charAt(0)).basicClassDescriptor()
168+
? forPrimitiveType(descriptor, 0)
168169
// will throw IAE on descriptor.length == 0 or if array dimensions too long
169170
: ReferenceClassDescImpl.of(descriptor);
170171
}

src/java.base/share/classes/java/lang/constant/ConstantDescs.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -240,31 +240,31 @@ private ConstantDescs() { }
240240
CD_Object, CD_Object);
241241

242242
/** {@link ClassDesc} representing the primitive type {@code int} */
243-
public static final ClassDesc CD_int = new PrimitiveClassDescImpl("I");
243+
public static final ClassDesc CD_int = PrimitiveClassDescImpl.CD_int;
244244

245245
/** {@link ClassDesc} representing the primitive type {@code long} */
246-
public static final ClassDesc CD_long = new PrimitiveClassDescImpl("J");
246+
public static final ClassDesc CD_long = PrimitiveClassDescImpl.CD_long;
247247

248248
/** {@link ClassDesc} representing the primitive type {@code float} */
249-
public static final ClassDesc CD_float = new PrimitiveClassDescImpl("F");
249+
public static final ClassDesc CD_float = PrimitiveClassDescImpl.CD_float;
250250

251251
/** {@link ClassDesc} representing the primitive type {@code double} */
252-
public static final ClassDesc CD_double = new PrimitiveClassDescImpl("D");
252+
public static final ClassDesc CD_double = PrimitiveClassDescImpl.CD_double;
253253

254254
/** {@link ClassDesc} representing the primitive type {@code short} */
255-
public static final ClassDesc CD_short = new PrimitiveClassDescImpl("S");
255+
public static final ClassDesc CD_short = PrimitiveClassDescImpl.CD_short;
256256

257257
/** {@link ClassDesc} representing the primitive type {@code byte} */
258-
public static final ClassDesc CD_byte = new PrimitiveClassDescImpl("B");
258+
public static final ClassDesc CD_byte = PrimitiveClassDescImpl.CD_byte;
259259

260260
/** {@link ClassDesc} representing the primitive type {@code char} */
261-
public static final ClassDesc CD_char = new PrimitiveClassDescImpl("C");
261+
public static final ClassDesc CD_char = PrimitiveClassDescImpl.CD_char;
262262

263263
/** {@link ClassDesc} representing the primitive type {@code boolean} */
264-
public static final ClassDesc CD_boolean = new PrimitiveClassDescImpl("Z");
264+
public static final ClassDesc CD_boolean = PrimitiveClassDescImpl.CD_boolean;
265265

266266
/** {@link ClassDesc} representing the primitive type {@code void} */
267-
public static final ClassDesc CD_void = new PrimitiveClassDescImpl("V");
267+
public static final ClassDesc CD_void = PrimitiveClassDescImpl.CD_void;
268268

269269
/**
270270
* {@link MethodHandleDesc} representing {@link MethodHandles#classData(Lookup, String, Class) MethodHandles.classData}

src/java.base/share/classes/java/lang/invoke/ConstantBootstraps.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package java.lang.invoke;
2626

2727
import sun.invoke.util.Wrapper;
28+
import jdk.internal.constant.ConstantUtils;
2829

2930
import static java.lang.invoke.MethodHandleNatives.mapLookupExceptionToError;
3031
import static java.util.Objects.requireNonNull;
@@ -112,7 +113,7 @@ public static Class<?> primitiveClass(MethodHandles.Lookup lookup, String name,
112113
throw new IllegalArgumentException(String.format("not primitive: %s", name));
113114
}
114115

115-
return Wrapper.forPrimitiveType(name.charAt(0)).primitiveType();
116+
return ConstantUtils.forPrimitiveType(name, 0).resolveConstantDesc(lookup);
116117
}
117118

118119
/**

src/java.base/share/classes/jdk/internal/constant/ConstantUtils.java

Lines changed: 45 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import java.util.List;
3636
import java.util.Set;
3737

38+
import static jdk.internal.constant.PrimitiveClassDescImpl.*;
39+
3840
/**
3941
* Helper methods for the implementation of {@code java.lang.constant}.
4042
*/
@@ -269,59 +271,40 @@ public static String dropFirstAndLastChar(String s) {
269271
return s.substring(1, s.length() - 1);
270272
}
271273

272-
/**
273-
* Parses a method descriptor string, and return a list of field descriptor
274-
* strings, return type first, then parameter types
275-
*
276-
* @param descriptor the descriptor string
277-
* @return the list of types
278-
* @throws IllegalArgumentException if the descriptor string is not valid
279-
*/
280-
public static List<ClassDesc> parseMethodDescriptor(String descriptor) {
281-
int cur = 0, end = descriptor.length();
282-
ArrayList<ClassDesc> ptypes = new ArrayList<>();
283-
ptypes.add(null); // placeholder for return type
284-
285-
if (cur >= end || descriptor.charAt(cur) != '(')
286-
throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
287-
288-
++cur; // skip '('
289-
while (cur < end && descriptor.charAt(cur) != ')') {
290-
int len = skipOverFieldSignature(descriptor, cur, end, false);
291-
if (len == 0)
292-
throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
293-
ptypes.add(resolveClassDesc(descriptor, cur, len));
294-
cur += len;
295-
}
296-
if (cur >= end)
297-
throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
298-
++cur; // skip ')'
299-
300-
int rLen = skipOverFieldSignature(descriptor, cur, end, true);
301-
if (rLen == 0 || cur + rLen != end)
302-
throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
303-
ptypes.set(0, resolveClassDesc(descriptor, cur, rLen));
304-
return ptypes;
274+
public static PrimitiveClassDescImpl forPrimitiveType(String descriptor, int offset) {
275+
return switch (descriptor.charAt(offset)) {
276+
case JVM_SIGNATURE_BYTE -> CD_byte;
277+
case JVM_SIGNATURE_CHAR -> CD_char;
278+
case JVM_SIGNATURE_FLOAT -> CD_float;
279+
case JVM_SIGNATURE_DOUBLE -> CD_double;
280+
case JVM_SIGNATURE_INT -> CD_int;
281+
case JVM_SIGNATURE_LONG -> CD_long;
282+
case JVM_SIGNATURE_SHORT -> CD_short;
283+
case JVM_SIGNATURE_VOID -> CD_void;
284+
case JVM_SIGNATURE_BOOLEAN -> CD_boolean;
285+
default -> throw badMethodDescriptor(descriptor);
286+
};
305287
}
306288

307-
private static ClassDesc resolveClassDesc(String descriptor, int start, int len) {
289+
static ClassDesc resolveClassDesc(String descriptor, int start, int len) {
308290
if (len == 1) {
309-
return Wrapper.forPrimitiveType(descriptor.charAt(start)).basicClassDescriptor();
291+
return forPrimitiveType(descriptor, start);
310292
}
311-
// Pre-verified in parseMethodDescriptor; avoid redundant verification
293+
294+
// Pre-verified in MethodTypeDescImpl#ofDescriptor; avoid redundant verification
312295
return ReferenceClassDescImpl.ofValidated(descriptor.substring(start, start + len));
313296
}
314297

298+
static IllegalArgumentException badMethodDescriptor(String descriptor) {
299+
return new IllegalArgumentException("Bad method descriptor: " + descriptor);
300+
}
301+
315302
private static final char JVM_SIGNATURE_ARRAY = '[';
316303
private static final char JVM_SIGNATURE_BYTE = 'B';
317304
private static final char JVM_SIGNATURE_CHAR = 'C';
318305
private static final char JVM_SIGNATURE_CLASS = 'L';
319-
private static final char JVM_SIGNATURE_ENDCLASS = ';';
320-
private static final char JVM_SIGNATURE_ENUM = 'E';
321306
private static final char JVM_SIGNATURE_FLOAT = 'F';
322307
private static final char JVM_SIGNATURE_DOUBLE = 'D';
323-
private static final char JVM_SIGNATURE_FUNC = '(';
324-
private static final char JVM_SIGNATURE_ENDFUNC = ')';
325308
private static final char JVM_SIGNATURE_INT = 'I';
326309
private static final char JVM_SIGNATURE_LONG = 'J';
327310
private static final char JVM_SIGNATURE_SHORT = 'S';
@@ -334,17 +317,22 @@ private static ClassDesc resolveClassDesc(String descriptor, int start, int len)
334317
* @param descriptor the descriptor string
335318
* @param start the starting index into the string
336319
* @param end the ending index within the string
337-
* @param voidOK is void acceptable?
338320
* @return the length of the descriptor, or 0 if it is not a descriptor
339321
* @throws IllegalArgumentException if the descriptor string is not valid
340322
*/
341-
@SuppressWarnings("fallthrough")
342-
static int skipOverFieldSignature(String descriptor, int start, int end, boolean voidOK) {
323+
static int skipOverFieldSignature(String descriptor, int start, int end) {
343324
int arrayDim = 0;
344325
int index = start;
345-
while (index < end) {
346-
switch (descriptor.charAt(index)) {
347-
case JVM_SIGNATURE_VOID: if (!voidOK) { return 0; }
326+
if (index < end) {
327+
char ch;
328+
while ((ch = descriptor.charAt(index++)) == JVM_SIGNATURE_ARRAY) {
329+
arrayDim++;
330+
}
331+
if (arrayDim > MAX_ARRAY_TYPE_DESC_DIMENSIONS) {
332+
throw maxArrayTypeDescDimensions();
333+
}
334+
335+
switch (ch) {
348336
case JVM_SIGNATURE_BOOLEAN:
349337
case JVM_SIGNATURE_BYTE:
350338
case JVM_SIGNATURE_CHAR:
@@ -353,16 +341,16 @@ static int skipOverFieldSignature(String descriptor, int start, int end, boolean
353341
case JVM_SIGNATURE_FLOAT:
354342
case JVM_SIGNATURE_LONG:
355343
case JVM_SIGNATURE_DOUBLE:
356-
return index - start + 1;
344+
return index - start;
357345
case JVM_SIGNATURE_CLASS:
358346
// state variable for detection of illegal states, such as:
359347
// empty unqualified name, '//', leading '/', or trailing '/'
360348
boolean legal = false;
361-
while (++index < end) {
362-
switch (descriptor.charAt(index)) {
349+
while (index < end) {
350+
switch (descriptor.charAt(index++)) {
363351
case ';' -> {
364352
// illegal state on parser exit indicates empty unqualified name or trailing '/'
365-
return legal ? index - start + 1 : 0;
353+
return legal ? index - start : 0;
366354
}
367355
case '.', '[' -> {
368356
// do not permit '.' or '['
@@ -377,21 +365,17 @@ static int skipOverFieldSignature(String descriptor, int start, int end, boolean
377365
legal = true;
378366
}
379367
}
380-
return 0;
381-
case JVM_SIGNATURE_ARRAY:
382-
arrayDim++;
383-
if (arrayDim > MAX_ARRAY_TYPE_DESC_DIMENSIONS) {
384-
throw new IllegalArgumentException(String.format("Cannot create an array type descriptor with more than %d dimensions",
385-
ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS));
386-
}
387-
// The rest of what's there better be a legal descriptor
388-
index++;
389-
voidOK = false;
390368
break;
391369
default:
392-
return 0;
370+
break;
393371
}
394372
}
395373
return 0;
396374
}
375+
376+
private static IllegalArgumentException maxArrayTypeDescDimensions() {
377+
return new IllegalArgumentException(String.format(
378+
"Cannot create an array type descriptor with more than %d dimensions",
379+
ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS));
380+
}
397381
}

src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
34
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
45
*
56
* This code is free software; you can redistribute it and/or modify it
@@ -27,17 +28,25 @@
2728
import jdk.internal.vm.annotation.Stable;
2829

2930
import java.lang.constant.ClassDesc;
31+
import java.lang.constant.ConstantDescs;
3032
import java.lang.constant.MethodTypeDesc;
3133
import java.lang.invoke.MethodHandles;
3234
import java.lang.invoke.MethodType;
3335
import java.security.AccessController;
3436
import java.security.PrivilegedAction;
37+
import java.util.ArrayList;
3538
import java.util.Arrays;
3639
import java.util.List;
3740
import java.util.Objects;
3841

3942
import static java.util.Objects.requireNonNull;
4043

44+
import static jdk.internal.constant.ConstantUtils.badMethodDescriptor;
45+
import static jdk.internal.constant.ConstantUtils.resolveClassDesc;
46+
import static jdk.internal.constant.ConstantUtils.skipOverFieldSignature;
47+
import static jdk.internal.constant.ConstantUtils.EMPTY_CLASSDESC;
48+
import static jdk.internal.constant.PrimitiveClassDescImpl.CD_void;
49+
4150
/**
4251
* A <a href="package-summary.html#nominal">nominal descriptor</a> for a
4352
* {@link MethodType}. A {@linkplain MethodTypeDescImpl} corresponds to a
@@ -91,7 +100,7 @@ private static ClassDesc validateArgument(ClassDesc arg) {
91100
*/
92101
public static MethodTypeDescImpl ofValidated(ClassDesc returnType, ClassDesc... trustedArgTypes) {
93102
if (trustedArgTypes.length == 0)
94-
return new MethodTypeDescImpl(returnType, ConstantUtils.EMPTY_CLASSDESC);
103+
return new MethodTypeDescImpl(returnType, EMPTY_CLASSDESC);
95104
return new MethodTypeDescImpl(returnType, trustedArgTypes);
96105
}
97106

@@ -105,18 +114,98 @@ public static MethodTypeDescImpl ofValidated(ClassDesc returnType, ClassDesc...
105114
* @jvms 4.3.3 Method Descriptors
106115
*/
107116
public static MethodTypeDescImpl ofDescriptor(String descriptor) {
108-
// Implicit null-check of descriptor
109-
List<ClassDesc> ptypes = ConstantUtils.parseMethodDescriptor(descriptor);
110-
int args = ptypes.size() - 1;
111-
ClassDesc[] paramTypes = args > 0
112-
? ptypes.subList(1, args + 1).toArray(ConstantUtils.EMPTY_CLASSDESC)
113-
: ConstantUtils.EMPTY_CLASSDESC;
114-
115-
MethodTypeDescImpl result = ofValidated(ptypes.get(0), paramTypes);
117+
int length = descriptor.length();
118+
int rightBracket, retTypeLength;
119+
if (descriptor.charAt(0) != '('
120+
|| (rightBracket = (descriptor.charAt(1) == ')' ? 1 : descriptor.lastIndexOf(')'))) <= 0
121+
|| (retTypeLength = length - rightBracket - 1) == 0
122+
|| (retTypeLength != 1 // if retTypeLength == 1, check correctness in resolveClassDesc
123+
&& retTypeLength != skipOverFieldSignature(descriptor, rightBracket + 1, length))
124+
) {
125+
throw badMethodDescriptor(descriptor);
126+
}
127+
128+
var returnType = resolveClassDesc(descriptor, rightBracket + 1, retTypeLength);
129+
if (length == 3 && returnType == CD_void) {
130+
return (MethodTypeDescImpl) ConstantDescs.MTD_void;
131+
}
132+
var paramTypes = paramTypes(descriptor, 1, rightBracket);
133+
var result = new MethodTypeDescImpl(returnType, paramTypes);
116134
result.cachedDescriptorString = descriptor;
117135
return result;
118136
}
119137

138+
private static ClassDesc[] paramTypes(String descriptor, int start, int end) {
139+
if (start == end) {
140+
return EMPTY_CLASSDESC;
141+
}
142+
143+
/*
144+
* If the length of the first 8 parameters is < 256, save them in lengths to avoid ArrayList allocation
145+
* Stop storing for the last parameter (we can compute length), or if too many parameters or too long.
146+
*/
147+
// little endian storage - lowest byte is encoded length 0
148+
long packedLengths = 0;
149+
int packedCount = 0;
150+
int cur = start;
151+
while (cur < end) {
152+
int len = skipOverFieldSignature(descriptor, cur, end);
153+
if (len == 0) {
154+
throw badMethodDescriptor(descriptor);
155+
}
156+
cur += len;
157+
if (len > 0xFF || packedCount >= Long.SIZE / Byte.SIZE || cur == end) {
158+
// Cannot or do not have to pack this item, but is already scanned and valid
159+
break;
160+
}
161+
packedLengths = packedLengths | (((long) len) << (Byte.SIZE * packedCount++));
162+
}
163+
164+
// Invariant: packedCount parameters encoded in packedLengths,
165+
// And another valid parameter pointed by cur
166+
167+
// Recover encoded elements
168+
ClassDesc[] paramTypes = null;
169+
List<ClassDesc> paramTypeList = null;
170+
if (cur == end) {
171+
paramTypes = new ClassDesc[packedCount + 1];
172+
} else {
173+
paramTypeList = new ArrayList<>(32);
174+
}
175+
176+
int last = start;
177+
for (int i = 0; i < packedCount; i++) {
178+
int len = Byte.toUnsignedInt((byte) (packedLengths >> (Byte.SIZE * i)));
179+
var cd = resolveClassDesc(descriptor, last, len);
180+
if (paramTypes != null) {
181+
paramTypes[i] = cd;
182+
} else {
183+
paramTypeList.add(cd);
184+
}
185+
last += len;
186+
}
187+
var lastCd = resolveClassDesc(descriptor, last, cur - last);
188+
189+
if (paramTypes != null) {
190+
paramTypes[packedCount] = lastCd;
191+
return paramTypes;
192+
}
193+
paramTypeList.add(lastCd);
194+
return buildParamTypes(descriptor, cur, end, paramTypeList);
195+
}
196+
197+
// slow path
198+
private static ClassDesc[] buildParamTypes(String descriptor, int cur, int end, List<ClassDesc> list) {
199+
while (cur < end) {
200+
int len = skipOverFieldSignature(descriptor, cur, end);
201+
if (len == 0)
202+
throw badMethodDescriptor(descriptor);
203+
list.add(resolveClassDesc(descriptor, cur, len));
204+
cur += len;
205+
}
206+
207+
return list.toArray(EMPTY_CLASSDESC);
208+
}
120209

121210
@Override
122211
public ClassDesc returnType() {

0 commit comments

Comments
 (0)