blob: 76c936d3cb108862ba439f5112d516c86188aa2d [file] [log] [blame]
/*
* Copyright (c) 2018, 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 java.lang.constant;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.List;
import static java.util.Objects.requireNonNull;
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for a
* {@link MethodType}. A {@linkplain MethodTypeDescImpl} corresponds to a
* {@code Constant_MethodType_info} entry in the constant pool of a classfile.
*/
final class MethodTypeDescImpl implements MethodTypeDesc {
private final ClassDesc returnType;
private final ClassDesc[] argTypes;
/**
* Constructs a {@linkplain MethodTypeDesc} with the specified return type
* and parameter types
*
* @param returnType a {@link ClassDesc} describing the return type
* @param argTypes {@link ClassDesc}s describing the parameter types
*/
MethodTypeDescImpl(ClassDesc returnType, ClassDesc[] argTypes) {
this.returnType = requireNonNull(returnType);
this.argTypes = requireNonNull(argTypes);
for (ClassDesc cr : argTypes)
if (cr.isPrimitive() && cr.descriptorString().equals("V"))
throw new IllegalArgumentException("Void parameters not permitted");
}
/**
* Creates a {@linkplain MethodTypeDescImpl} given a method descriptor string.
*
* @param descriptor the method descriptor string
* @return a {@linkplain MethodTypeDescImpl} describing the desired method type
* @throws IllegalArgumentException if the descriptor string is not a valid
* method descriptor
* @jvms 4.3.3 Method Descriptors
*/
static MethodTypeDescImpl ofDescriptor(String descriptor) {
requireNonNull(descriptor);
List<String> types = ConstantUtils.parseMethodDescriptor(descriptor);
ClassDesc[] paramTypes = types.stream().skip(1).map(ClassDesc::ofDescriptor).toArray(ClassDesc[]::new);
return new MethodTypeDescImpl(ClassDesc.ofDescriptor(types.get(0)), paramTypes);
}
@Override
public ClassDesc returnType() {
return returnType;
}
@Override
public int parameterCount() {
return argTypes.length;
}
@Override
public ClassDesc parameterType(int index) {
return argTypes[index];
}
@Override
public List<ClassDesc> parameterList() {
return List.of(argTypes);
}
@Override
public ClassDesc[] parameterArray() {
return argTypes.clone();
}
@Override
public MethodTypeDesc changeReturnType(ClassDesc returnType) {
return MethodTypeDesc.of(returnType, argTypes);
}
@Override
public MethodTypeDesc changeParameterType(int index, ClassDesc paramType) {
ClassDesc[] newArgs = argTypes.clone();
newArgs[index] = paramType;
return MethodTypeDesc.of(returnType, newArgs);
}
@Override
public MethodTypeDesc dropParameterTypes(int start, int end) {
if (start < 0 || start >= argTypes.length || end < 0 || end > argTypes.length || start > end)
throw new IndexOutOfBoundsException();
ClassDesc[] newArgs = new ClassDesc[argTypes.length - (end - start)];
System.arraycopy(argTypes, 0, newArgs, 0, start);
System.arraycopy(argTypes, end, newArgs, start, argTypes.length - end);
return MethodTypeDesc.of(returnType, newArgs);
}
@Override
public MethodTypeDesc insertParameterTypes(int pos, ClassDesc... paramTypes) {
if (pos < 0 || pos > argTypes.length)
throw new IndexOutOfBoundsException(pos);
ClassDesc[] newArgs = new ClassDesc[argTypes.length + paramTypes.length];
System.arraycopy(argTypes, 0, newArgs, 0, pos);
System.arraycopy(paramTypes, 0, newArgs, pos, paramTypes.length);
System.arraycopy(argTypes, pos, newArgs, pos+paramTypes.length, argTypes.length - pos);
return MethodTypeDesc.of(returnType, newArgs);
}
@Override
public MethodType resolveConstantDesc(MethodHandles.Lookup lookup) throws ReflectiveOperationException {
@SuppressWarnings("removal")
MethodType mtype = AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public MethodType run() {
return MethodType.fromMethodDescriptorString(descriptorString(),
lookup.lookupClass().getClassLoader());
}
});
// let's check that the lookup has access to all the types in the method type
lookup.accessClass(mtype.returnType());
for (Class<?> paramType: mtype.parameterArray()) {
lookup.accessClass(paramType);
}
return mtype;
}
/**
* Returns {@code true} if this {@linkplain MethodTypeDescImpl} is
* equal to another {@linkplain MethodTypeDescImpl}. Equality is
* determined by the two descriptors having equal return types and argument
* types.
*
* @param o the {@code MethodTypeDescImpl} to compare to this
* {@code MethodTypeDescImpl}
* @return {@code true} if the specified {@code MethodTypeDescImpl}
* is equal to this {@code MethodTypeDescImpl}.
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MethodTypeDescImpl constant = (MethodTypeDescImpl) o;
return returnType.equals(constant.returnType)
&& Arrays.equals(argTypes, constant.argTypes);
}
@Override
public int hashCode() {
int result = returnType.hashCode();
result = 31 * result + Arrays.hashCode(argTypes);
return result;
}
@Override
public String toString() {
return String.format("MethodTypeDesc[%s]", displayDescriptor());
}
}