Skip to content

Commit

Permalink
8331187: Optimize MethodTypeDesc and ClassDesc.ofDescriptor for primi…
Browse files Browse the repository at this point in the history
…tive types

Reviewed-by: jvernee, liach
  • Loading branch information
cl4es committed Apr 26, 2024
1 parent 5e2ced4 commit 8bbd725
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 54 deletions.
17 changes: 4 additions & 13 deletions src/java.base/share/classes/java/lang/constant/ClassDesc.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -158,19 +158,10 @@ static ClassDesc of(String packageName, String className) {
* @see ClassDesc#ofInternalName(String)
*/
static ClassDesc ofDescriptor(String descriptor) {
requireNonNull(descriptor);
if (descriptor.isEmpty()) {
throw new IllegalArgumentException(
"not a valid reference type descriptor: " + descriptor);
}
int depth = ConstantUtils.arrayDepth(descriptor);
if (depth > ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) {
throw new IllegalArgumentException(
"Cannot create an array type descriptor with more than " +
ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions");
}
// implicit null-check
return (descriptor.length() == 1)
? new PrimitiveClassDescImpl(descriptor)
? Wrapper.forPrimitiveType(descriptor.charAt(0)).primitiveClassDescriptor()
// will throw IAE on descriptor.length == 0 or if array dimensions too long
: new ReferenceClassDescImpl(descriptor);
}

Expand Down
20 changes: 10 additions & 10 deletions src/java.base/share/classes/java/lang/constant/ConstantDescs.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -236,31 +236,31 @@ private ConstantDescs() { }
CD_Object, CD_Object);

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

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

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

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

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

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

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

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

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

/**
* {@link MethodHandleDesc} representing {@link MethodHandles#classData(Lookup, String, Class) MethodHandles.classData}
Expand Down
21 changes: 15 additions & 6 deletions src/java.base/share/classes/java/lang/constant/ConstantUtils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -24,6 +24,8 @@
*/
package java.lang.constant;

import sun.invoke.util.Wrapper;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
Expand Down Expand Up @@ -195,10 +197,10 @@ static String dropFirstAndLastChar(String s) {
* @return the list of types
* @throws IllegalArgumentException if the descriptor string is not valid
*/
static List<String> parseMethodDescriptor(String descriptor) {
static List<ClassDesc> parseMethodDescriptor(String descriptor) {
int cur = 0, end = descriptor.length();
ArrayList<String> ptypes = new ArrayList<>();
ptypes.add(null); //placeholder for return type
ArrayList<ClassDesc> ptypes = new ArrayList<>();
ptypes.add(null); // placeholder for return type

if (cur >= end || descriptor.charAt(cur) != '(')
throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
Expand All @@ -208,7 +210,7 @@ static List<String> parseMethodDescriptor(String descriptor) {
int len = skipOverFieldSignature(descriptor, cur, end, false);
if (len == 0)
throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
ptypes.add(descriptor.substring(cur, cur + len));
ptypes.add(resolveClassDesc(descriptor, cur, len));
cur += len;
}
if (cur >= end)
Expand All @@ -218,10 +220,17 @@ static List<String> parseMethodDescriptor(String descriptor) {
int rLen = skipOverFieldSignature(descriptor, cur, end, true);
if (rLen == 0 || cur + rLen != end)
throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
ptypes.set(0, descriptor.substring(cur, cur + rLen));
ptypes.set(0, resolveClassDesc(descriptor, cur, rLen));
return ptypes;
}

private static ClassDesc resolveClassDesc(String descriptor, int start, int len) {
if (len == 1) {
return Wrapper.forBasicType(descriptor.charAt(start)).primitiveClassDescriptor();
}
return ClassDesc.ofDescriptor(descriptor.substring(start, start + len));
}

private static final char JVM_SIGNATURE_ARRAY = '[';
private static final char JVM_SIGNATURE_BYTE = 'B';
private static final char JVM_SIGNATURE_CHAR = 'C';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -88,21 +88,19 @@ static MethodTypeDescImpl ofTrusted(ClassDesc returnType, ClassDesc[] trustedArg
* @jvms 4.3.3 Method Descriptors
*/
static MethodTypeDescImpl ofDescriptor(String descriptor) {
requireNonNull(descriptor);

List<String> types = ConstantUtils.parseMethodDescriptor(descriptor);

int paramCount = types.size() - 1;
var paramTypes = paramCount > 0 ? new ClassDesc[paramCount] : ConstantUtils.EMPTY_CLASSDESC;
for (int i = 0; i < paramCount; i++) {
paramTypes[i] = ClassDesc.ofDescriptor(types.get(i + 1));
}

MethodTypeDescImpl result = ofTrusted(ClassDesc.ofDescriptor(types.getFirst()), paramTypes);
// Implicit null-check of descriptor
List<ClassDesc> ptypes = ConstantUtils.parseMethodDescriptor(descriptor);
int args = ptypes.size() - 1;
ClassDesc[] paramTypes = args > 0
? ptypes.subList(1, args + 1).toArray(ConstantUtils.EMPTY_CLASSDESC)
: ConstantUtils.EMPTY_CLASSDESC;

MethodTypeDescImpl result = ofTrusted(ptypes.get(0), paramTypes);
result.cachedDescriptorString = descriptor;
return result;
}


@Override
public ClassDesc returnType() {
return returnType;
Expand Down
33 changes: 20 additions & 13 deletions src/java.base/share/classes/sun/invoke/util/Wrapper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -25,25 +25,27 @@

package sun.invoke.util;

import java.util.Map;
import static sun.invoke.util.Wrapper.NumericClasses.*;
import jdk.internal.vm.annotation.DontInline;

import java.lang.constant.ClassDesc;
import java.lang.constant.ConstantDescs;

public enum Wrapper {
// wrapperType simple primitiveType simple char emptyArray format numericClass superClass
BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0], Format.unsigned( 1), 0, 0),
BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0], Format.unsigned( 1), 0, 0, ConstantDescs.CD_boolean),
// These must be in the order defined for widening primitive conversions in JLS 5.1.2
// Avoid boxing integral types here to defer initialization of internal caches
BYTE ( Byte.class, "Byte", byte.class, "byte", 'B', new byte[0], Format.signed( 8), BYTE_CLASS, BYTE_SUPERCLASSES),
SHORT ( Short.class, "Short", short.class, "short", 'S', new short[0], Format.signed( 16), SHORT_CLASS, SHORT_SUPERCLASSES),
CHAR (Character.class, "Character", char.class, "char", 'C', new char[0], Format.unsigned(16), CHAR_CLASS, CHAR_SUPERCLASSES),
INT ( Integer.class, "Integer", int.class, "int", 'I', new int[0], Format.signed( 32), INT_CLASS, INT_SUPERCLASSES),
LONG ( Long.class, "Long", long.class, "long", 'J', new long[0], Format.signed( 64), LONG_CLASS, LONG_SUPERCLASSES),
FLOAT ( Float.class, "Float", float.class, "float", 'F', new float[0], Format.floating(32), FLOAT_CLASS, FLOAT_SUPERCLASSES),
DOUBLE ( Double.class, "Double", double.class, "double", 'D', new double[0], Format.floating(64), DOUBLE_CLASS, DOUBLE_CLASS),
OBJECT ( Object.class, "Object", Object.class, "Object", 'L', new Object[0], Format.other( 1), 0, 0),
BYTE ( Byte.class, "Byte", byte.class, "byte", 'B', new byte[0], Format.signed( 8), BYTE_CLASS, BYTE_SUPERCLASSES, ConstantDescs.CD_byte),
SHORT ( Short.class, "Short", short.class, "short", 'S', new short[0], Format.signed( 16), SHORT_CLASS, SHORT_SUPERCLASSES, ConstantDescs.CD_short),
CHAR (Character.class, "Character", char.class, "char", 'C', new char[0], Format.unsigned(16), CHAR_CLASS, CHAR_SUPERCLASSES, ConstantDescs.CD_char),
INT ( Integer.class, "Integer", int.class, "int", 'I', new int[0], Format.signed( 32), INT_CLASS, INT_SUPERCLASSES, ConstantDescs.CD_int),
LONG ( Long.class, "Long", long.class, "long", 'J', new long[0], Format.signed( 64), LONG_CLASS, LONG_SUPERCLASSES, ConstantDescs.CD_long),
FLOAT ( Float.class, "Float", float.class, "float", 'F', new float[0], Format.floating(32), FLOAT_CLASS, FLOAT_SUPERCLASSES, ConstantDescs.CD_float),
DOUBLE ( Double.class, "Double", double.class, "double", 'D', new double[0], Format.floating(64), DOUBLE_CLASS, DOUBLE_CLASS, ConstantDescs.CD_double),
OBJECT ( Object.class, "Object", Object.class, "Object", 'L', new Object[0], Format.other( 1), 0, 0, ConstantDescs.CD_Object),
// VOID must be the last type, since it is "assignable" from any other type:
VOID ( Void.class, "Void", void.class, "void", 'V', null, Format.other( 0), 0, 0),
VOID ( Void.class, "Void", void.class, "void", 'V', null, Format.other( 0), 0, 0, ConstantDescs.CD_void),
;

public static final int COUNT = 10;
Expand All @@ -58,8 +60,9 @@ public enum Wrapper {
private final int superClasses;
private final String wrapperSimpleName;
private final String primitiveSimpleName;
private final ClassDesc primitiveTypeDesc;

private Wrapper(Class<?> wtype, String wtypeName, Class<?> ptype, String ptypeName, char tchar, Object emptyArray, int format, int numericClass, int superClasses) {
private Wrapper(Class<?> wtype, String wtypeName, Class<?> ptype, String ptypeName, char tchar, Object emptyArray, int format, int numericClass, int superClasses, ClassDesc primitiveTypeDesc) {
this.wrapperType = wtype;
this.primitiveType = ptype;
this.basicTypeChar = tchar;
Expand All @@ -70,6 +73,7 @@ private Wrapper(Class<?> wtype, String wtypeName, Class<?> ptype, String ptypeNa
this.superClasses = superClasses;
this.wrapperSimpleName = wtypeName;
this.primitiveSimpleName = ptypeName;
this.primitiveTypeDesc = primitiveTypeDesc;
}

/** For debugging, give the details of this wrapper. */
Expand Down Expand Up @@ -376,6 +380,9 @@ public static Wrapper forBasicType(Class<?> type) {
}
}

/** A nominal descriptor of the primitive type */
public ClassDesc primitiveClassDescriptor() { return primitiveTypeDesc; }

/** What is the primitive type wrapped by this wrapper? */
public Class<?> primitiveType() { return primitiveType; }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.lang.constant;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;

import java.lang.constant.ClassDesc;
import java.util.concurrent.TimeUnit;

/**
* Performance of conversion from and to method type descriptor symbols with
* descriptor strings and class descriptor symbols
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 3, time = 2)
@Measurement(iterations = 6, time = 1)
@Fork(1)
@State(Scope.Thread)
public class ClassDescFactories {

@Param({
"Ljava/lang/Object;",
"V",
"I"
})
public String descString;

@Benchmark
public ClassDesc ofDescriptor() {
return ClassDesc.ofDescriptor(descString);
}
}

1 comment on commit 8bbd725

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.