8331855: Convert jdk.jdeps jdeprscan and jdeps to use the Classfile API

Reviewed-by: asotona
This commit is contained in:
Chen Liang 2024-05-17 12:26:22 +00:00 committed by Adam Sotona
parent beeffd4671
commit d4c2edf2c9
19 changed files with 416 additions and 869 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 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
@ -25,16 +25,13 @@
package com.sun.tools.jdeprscan.scan;
import java.lang.classfile.ClassModel;
import java.lang.classfile.constantpool.*;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
import java.util.Locale;
import com.sun.tools.classfile.ClassFile;
import com.sun.tools.classfile.ConstantPool;
import static com.sun.tools.classfile.ConstantPool.CPInfo;
/**
* A container for selected constant pool entries. There are currently
* lists that contain the following types of CP entries:
@ -45,15 +42,21 @@ import static com.sun.tools.classfile.ConstantPool.CPInfo;
* - CONSTANT_InterfaceMethodref_info
*/
class CPEntries {
final List<ConstantPool.CONSTANT_Class_info> classes = new ArrayList<>();
final List<ConstantPool.CONSTANT_Fieldref_info> fieldRefs = new ArrayList<>();
final List<ConstantPool.CONSTANT_Methodref_info> methodRefs = new ArrayList<>();
final List<ConstantPool.CONSTANT_InterfaceMethodref_info> intfMethodRefs = new ArrayList<>();
final List<ClassEntry> classes = new ArrayList<>();
final List<FieldRefEntry> fieldRefs = new ArrayList<>();
final List<MethodRefEntry> methodRefs = new ArrayList<>();
final List<InterfaceMethodRefEntry> intfMethodRefs = new ArrayList<>();
public static CPEntries loadFrom(ClassFile cf) {
public static CPEntries loadFrom(ClassModel cf) {
CPEntries entries = new CPEntries();
for (CPInfo cpi : cf.constant_pool.entries()) {
cpi.accept(new CPSelector(), entries);
for (PoolEntry cpi : cf.constantPool()) {
switch (cpi) {
case ClassEntry ce -> entries.classes.add(ce);
case MethodRefEntry mref -> entries.methodRefs.add(mref);
case InterfaceMethodRefEntry imref -> entries.intfMethodRefs.add(imref);
case FieldRefEntry fref -> entries.fieldRefs.add(fref);
default -> {}
}
}
return entries;
}

View File

@ -1,123 +0,0 @@
/*
* Copyright (c) 2016, 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 com.sun.tools.jdeprscan.scan;
import com.sun.tools.classfile.ConstantPool;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Dynamic_info;
/**
* A visitor that selects constant pool entries by type and adds
* them to the given CPEntries object.
*/
class CPSelector implements ConstantPool.Visitor<Void,CPEntries> {
@Override
public Void visitClass(ConstantPool.CONSTANT_Class_info info, CPEntries p) {
p.classes.add(info);
return null;
}
@Override
public Void visitDouble(ConstantPool.CONSTANT_Double_info info, CPEntries p) {
return null;
}
@Override
public Void visitFieldref(ConstantPool.CONSTANT_Fieldref_info info, CPEntries p) {
p.fieldRefs.add(info);
return null;
}
@Override
public Void visitFloat(ConstantPool.CONSTANT_Float_info info, CPEntries p) {
return null;
}
@Override
public Void visitInteger(ConstantPool.CONSTANT_Integer_info info, CPEntries p) {
return null;
}
@Override
public Void visitInterfaceMethodref(ConstantPool.CONSTANT_InterfaceMethodref_info info, CPEntries p) {
p.intfMethodRefs.add(info);
return null;
}
@Override
public Void visitInvokeDynamic(ConstantPool.CONSTANT_InvokeDynamic_info info, CPEntries p) {
return null;
}
public Void visitDynamicConstant(CONSTANT_Dynamic_info info, CPEntries p) {
return null;
}
@Override
public Void visitLong(ConstantPool.CONSTANT_Long_info info, CPEntries p) {
return null;
}
@Override
public Void visitMethodref(ConstantPool.CONSTANT_Methodref_info info, CPEntries p) {
p.methodRefs.add(info);
return null;
}
@Override
public Void visitMethodHandle(ConstantPool.CONSTANT_MethodHandle_info info, CPEntries p) {
return null;
}
@Override
public Void visitMethodType(ConstantPool.CONSTANT_MethodType_info info, CPEntries p) {
return null;
}
@Override
public Void visitModule(ConstantPool.CONSTANT_Module_info info, CPEntries p) {
return null;
}
@Override
public Void visitNameAndType(ConstantPool.CONSTANT_NameAndType_info info, CPEntries p) {
return null;
}
@Override
public Void visitPackage(ConstantPool.CONSTANT_Package_info info, CPEntries p) {
return null;
}
@Override
public Void visitString(ConstantPool.CONSTANT_String_info info, CPEntries p) {
return null;
}
@Override
public Void visitUtf8(ConstantPool.CONSTANT_Utf8_info info, CPEntries p) {
return null;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 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
@ -25,10 +25,9 @@
package com.sun.tools.jdeprscan.scan;
import com.sun.tools.classfile.ClassFile;
import com.sun.tools.classfile.ConstantPoolException;
import java.io.IOException;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
@ -92,9 +91,9 @@ public class ClassFinder {
* @param className the class to search for
* @return a ClassFile instance, or null if not found
*/
public ClassFile find(String className) {
public ClassModel find(String className) {
for (PathEntry pe : list) {
ClassFile cf = pe.find(className);
ClassModel cf = pe.find(className);
if (cf != null) {
return cf;
}
@ -113,7 +112,7 @@ public class ClassFinder {
* @param className the class to search for
* @return a ClassFile instance, or null if not found
*/
ClassFile find(String className);
ClassModel find(String className);
}
/**
@ -127,14 +126,14 @@ public class ClassFinder {
}
@Override
public ClassFile find(String className) {
public ClassModel find(String className) {
JarEntry entry = jarFile.getJarEntry(className + ".class");
if (entry == null) {
return null;
}
try {
return ClassFile.read(jarFile.getInputStream(entry));
} catch (IOException | ConstantPoolException ex) {
return ClassFile.of().parse(jarFile.getInputStream(entry).readAllBytes());
} catch (IOException | IllegalArgumentException ex) {
if (verbose) {
ex.printStackTrace();
}
@ -154,13 +153,13 @@ public class ClassFinder {
}
@Override
public ClassFile find(String className) {
public ClassModel find(String className) {
Path classFileName = dir.resolve(className + ".class");
try {
return ClassFile.read(classFileName);
return ClassFile.of().parse(classFileName);
} catch (NoSuchFileException nsfe) {
// not found, return silently
} catch (IOException | ConstantPoolException ex) {
} catch (IOException | IllegalArgumentException ex) {
if (verbose) {
ex.printStackTrace();
}
@ -181,7 +180,7 @@ public class ClassFinder {
final FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
@Override
public ClassFile find(String className) {
public ClassModel find(String className) {
int end = className.lastIndexOf('/');
if (end < 0) {
return null;
@ -194,13 +193,13 @@ public class ClassFinder {
.filter(Files::exists)
.findFirst();
if (opath.isPresent()) {
return ClassFile.read(opath.get());
return ClassFile.of().parse(opath.get());
} else {
return null;
}
} catch (NoSuchFileException nsfe) {
// not found, return silently
} catch (IOException | ConstantPoolException ex) {
} catch (IOException | IllegalArgumentException ex) {
if (verbose) {
ex.printStackTrace();
}

View File

@ -1,167 +0,0 @@
/*
* Copyright (c) 2016, 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 com.sun.tools.jdeprscan.scan;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Represents a method's signature, that is, its parameter types
* and its return type.
*/
public class MethodSig {
final List<String> parameters;
final String returnType;
/**
* Parses the method descriptor and returns a MethodSig instance.
*
* @param desc the descriptor to parse
* @return the new MethodSig instance
*/
public static MethodSig fromDesc(String desc) {
return parse(desc, 0, desc.length());
}
/**
* Returns this method's return type.
*
* @return the return type
*/
public String getReturnType() {
return returnType;
}
/**
* Returns a list of parameters of this method.
*
* @return the parameter list
*/
public List<String> getParameters() {
return parameters;
}
/**
* Returns a string describing this method.
*
* @return the string description
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("parameters");
if (parameters.isEmpty()) {
sb.append(" none");
} else {
int i = 0;
for (String p : parameters) {
sb.append(String.format(" %d=%s", i++, p));
}
}
sb.append(String.format(" return %s", returnType));
return sb.toString();
}
private MethodSig(List<String> parameters, String returnType) {
this.parameters = Collections.unmodifiableList(parameters);
this.returnType = returnType;
}
private static IllegalArgumentException ex(String desc, int pos) {
return new IllegalArgumentException(String.format(
"illegal descriptor \"%s\" at position %d", desc, pos));
}
private static MethodSig parse(String desc, int start, int end)
throws IllegalArgumentException {
int p = start;
int dims = 0;
boolean inReturnType = false;
String returnType = null;
List<String> parameters = new ArrayList<>();
while (p < end) {
String type;
char ch;
switch (ch = desc.charAt(p)) {
case '(':
p++;
continue;
case ')':
p++;
inReturnType = true;
continue;
case '[':
p++;
dims++;
continue;
case 'B': // byte
case 'C': // char
case 'D': // double
case 'F': // float
case 'I': // int
case 'J': // long
case 'S': // short
case 'Z': // boolean
case 'V': // void
type = Character.toString(ch);
p++;
break;
case 'L':
int sep = desc.indexOf(';', p);
if (sep == -1 || sep >= end)
throw ex(desc, p);
type = desc.substring(p, ++sep);
p = sep;
break;
default:
throw ex(desc, p);
}
StringBuilder sb = new StringBuilder();
for ( ; dims > 0; dims-- )
sb.append("[");
sb.append(type);
if (inReturnType) {
returnType = sb.toString();
} else {
parameters.add(sb.toString());
}
}
if (returnType == null) {
throw ex(desc, end);
}
return new MethodSig(parameters, returnType);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 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
@ -27,6 +27,13 @@ package com.sun.tools.jdeprscan.scan;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.classfile.AccessFlags;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
import java.lang.classfile.constantpool.FieldRefEntry;
import java.lang.classfile.constantpool.NameAndTypeEntry;
import java.lang.constant.MethodTypeDesc;
import java.lang.reflect.AccessFlag;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
@ -41,17 +48,12 @@ import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.sun.tools.classfile.*;
import com.sun.tools.jdeprscan.DeprData;
import com.sun.tools.jdeprscan.DeprDB;
import com.sun.tools.jdeprscan.Messages;
import static com.sun.tools.classfile.AccessFlags.*;
import static com.sun.tools.classfile.ConstantPool.*;
/**
* An object that represents the scanning phase of deprecation usage checking.
* Given a deprecation database, scans the targeted directory hierarchy, jar
@ -85,7 +87,7 @@ public class Scan {
// one, we should instead add a reference to the symbol file for that release instead
// of the current image. The problems are a) it's unclear how to get from a release
// to paths that reference the symbol files, as this might be internal to the file
// manager; and b) the symbol file includes .sig files, not class files, which ClassFile
// manager; and b) the symbol file includes .sig files, not class files, which ClassModel
// might not be able to handle.
f.addJrt();
@ -164,13 +166,13 @@ public class Scan {
Pattern refTypePattern = Pattern.compile("\\[+L(.*);");
String typeKind(ClassFile cf) {
AccessFlags flags = cf.access_flags;
if (flags.is(ACC_ENUM)) {
String typeKind(ClassModel cf) {
AccessFlags flags = cf.flags();
if (flags.has(AccessFlag.ENUM)) {
return "enum";
} else if (flags.is(ACC_ANNOTATION)) {
} else if (flags.has(AccessFlag.ANNOTATION)) {
return "@interface";
} else if (flags.is(ACC_INTERFACE)) {
} else if (flags.has(AccessFlag.INTERFACE)) {
return "interface";
} else {
return "class";
@ -181,44 +183,43 @@ public class Scan {
return Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
}
void printType(String key, ClassFile cf, String cname, boolean r)
throws ConstantPoolException {
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, dep(r)));
void printType(String key, ClassModel cf, String cname, boolean r) {
out.println(Messages.get(key, typeKind(cf), cf.thisClass().asInternalName(), cname, dep(r)));
}
void printMethod(String key, ClassFile cf, String cname, String mname, String rtype,
boolean r) throws ConstantPoolException {
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, mname, rtype, dep(r)));
void printMethod(String key, ClassModel cf, String cname, String mname, String rtype,
boolean r) {
out.println(Messages.get(key, typeKind(cf), cf.thisClass().asInternalName(), cname, mname, rtype, dep(r)));
}
void printField(String key, ClassFile cf, String cname, String fname,
boolean r) throws ConstantPoolException {
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, fname, dep(r)));
void printField(String key, ClassModel cf, String cname, String fname,
boolean r) {
out.println(Messages.get(key, typeKind(cf), cf.thisClass().asInternalName(), cname, fname, dep(r)));
}
void printFieldType(String key, ClassFile cf, String cname, String fname, String type,
boolean r) throws ConstantPoolException {
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, fname, type, dep(r)));
void printFieldType(String key, ClassModel cf, String cname, String fname, String type,
boolean r) {
out.println(Messages.get(key, typeKind(cf), cf.thisClass().asInternalName(), cname, fname, type, dep(r)));
}
void printHasField(ClassFile cf, String fname, String type, boolean r)
throws ConstantPoolException {
out.println(Messages.get("scan.out.hasfield", typeKind(cf), cf.getName(), fname, type, dep(r)));
void printHasField(ClassModel cf, String fname, String type, boolean r)
{
out.println(Messages.get("scan.out.hasfield", typeKind(cf), cf.thisClass().asInternalName(), fname, type, dep(r)));
}
void printHasMethodParmType(ClassFile cf, String mname, String parmType, boolean r)
throws ConstantPoolException {
out.println(Messages.get("scan.out.methodparmtype", typeKind(cf), cf.getName(), mname, parmType, dep(r)));
void printHasMethodParmType(ClassModel cf, String mname, String parmType, boolean r)
{
out.println(Messages.get("scan.out.methodparmtype", typeKind(cf), cf.thisClass().asInternalName(), mname, parmType, dep(r)));
}
void printHasMethodRetType(ClassFile cf, String mname, String retType, boolean r)
throws ConstantPoolException {
out.println(Messages.get("scan.out.methodrettype", typeKind(cf), cf.getName(), mname, retType, dep(r)));
void printHasMethodRetType(ClassModel cf, String mname, String retType, boolean r)
{
out.println(Messages.get("scan.out.methodrettype", typeKind(cf), cf.thisClass().asInternalName(), mname, retType, dep(r)));
}
void printHasOverriddenMethod(ClassFile cf, String overridden, String mname, String desc, boolean r)
throws ConstantPoolException {
out.println(Messages.get("scan.out.methodoverride", typeKind(cf), cf.getName(), overridden,
void printHasOverriddenMethod(ClassModel cf, String overridden, String mname, String desc, boolean r)
{
out.println(Messages.get("scan.out.methodoverride", typeKind(cf), cf.thisClass().asInternalName(), overridden,
mname, desc, dep(r)));
}
@ -253,29 +254,29 @@ public class Scan {
* The checkMethod parameter determines whether this checks for a method
* or for a field.
*
* @param targetClass the ClassFile of the class to search
* @param targetClass the ClassModel of the class to search
* @param targetName the method or field's name
* @param targetDesc the methods descriptor (ignored if checkMethod is false)
* @param checkMethod true if checking for method, false if checking for field
* @return boolean indicating whether the member is present
* @throws ConstantPoolException if a constant pool entry cannot be found
* @ if a constant pool entry cannot be found
*/
boolean isMemberPresent(ClassFile targetClass,
boolean isMemberPresent(ClassModel targetClass,
String targetName,
String targetDesc,
boolean checkMethod)
throws ConstantPoolException {
{
if (checkMethod) {
for (Method m : targetClass.methods) {
String mname = m.getName(targetClass.constant_pool);
String mdesc = targetClass.constant_pool.getUTF8Value(m.descriptor.index);
for (var m : targetClass.methods()) {
String mname = m.methodName().stringValue();
String mdesc = m.methodType().stringValue();
if (targetName.equals(mname) && targetDesc.equals(mdesc)) {
return true;
}
}
} else {
for (Field f : targetClass.fields) {
String fname = f.getName(targetClass.constant_pool);
for (var f : targetClass.fields()) {
String fname = f.fieldName().stringValue();
if (targetName.equals(fname)) {
return true;
}
@ -288,14 +289,12 @@ public class Scan {
* Adds all interfaces from this class to the deque of interfaces.
*
* @param intfs the deque of interfaces
* @param cf the ClassFile of this class
* @throws ConstantPoolException if a constant pool entry cannot be found
* @param cf the ClassModel of this class
* @ if a constant pool entry cannot be found
*/
void addInterfaces(Deque<String> intfs, ClassFile cf)
throws ConstantPoolException {
int count = cf.interfaces.length;
for (int i = 0; i < count; i++) {
intfs.addLast(cf.getInterfaceName(i));
void addInterfaces(Deque<String> intfs, ClassModel cf) {
for (var itf : cf.interfaces()) {
intfs.addLast(itf.asInternalName());
}
}
@ -307,7 +306,7 @@ public class Scan {
*
* TODO: refine error handling
*
* @param cf the ClassFile of this class
* @param cf the ClassModel of this class
* @param startClassName the name of the class at which to start searching
* @param findName the member name to search for
* @param findDesc the method descriptor to search for (ignored for fields)
@ -315,15 +314,15 @@ public class Scan {
* @param checkStartClass true if the start class should be searched, false if
* it should be skipped
* @return the name of the class where the member resolved, or null
* @throws ConstantPoolException if a constant pool entry cannot be found
* @ if a constant pool entry cannot be found
*/
String resolveMember(
ClassFile cf, String startClassName, String findName, String findDesc,
ClassModel cf, String startClassName, String findName, String findDesc,
boolean resolveMethod, boolean checkStartClass)
throws ConstantPoolException {
ClassFile startClass;
{
ClassModel startClass;
if (cf.getName().equals(startClassName)) {
if (cf.thisClass().asInternalName().equals(startClassName)) {
startClass = cf;
} else {
startClass = finder.find(startClassName);
@ -336,7 +335,7 @@ public class Scan {
// follow super_class until it's 0, meaning we've reached Object
// accumulate interfaces of superclasses as we go along
ClassFile curClass = startClass;
ClassModel curClass = startClass;
Deque<String> intfs = new ArrayDeque<>();
while (true) {
if ((checkStartClass || curClass != startClass) &&
@ -344,12 +343,13 @@ public class Scan {
break;
}
if (curClass.super_class == 0) { // reached Object
var superclass = curClass.superclass();
if (superclass.isEmpty()) { // reached Object
curClass = null;
break;
}
String superName = curClass.getSuperclassName();
String superName = superclass.get().asInternalName();
curClass = finder.find(superName);
if (curClass == null) {
errorNoClass(superName);
@ -391,7 +391,7 @@ public class Scan {
return null;
}
} else {
String foundClassName = curClass.getName();
String foundClassName = curClass.thisClass().asInternalName();
return foundClassName;
}
}
@ -399,11 +399,15 @@ public class Scan {
/**
* Checks the superclass of this class.
*
* @param cf the ClassFile of this class
* @throws ConstantPoolException if a constant pool entry cannot be found
* @param cf the ClassModel of this class
* @ if a constant pool entry cannot be found
*/
void checkSuper(ClassFile cf) throws ConstantPoolException {
String sname = cf.getSuperclassName();
void checkSuper(ClassModel cf) {
var superclass = cf.superclass();
if (superclass.isEmpty()) {
return;
}
String sname = superclass.get().asInternalName();
DeprData dd = db.getTypeDeprecated(sname);
if (dd != null) {
printType("scan.out.extends", cf, sname, dd.isForRemoval());
@ -413,13 +417,12 @@ public class Scan {
/**
* Checks the interfaces of this class.
*
* @param cf the ClassFile of this class
* @throws ConstantPoolException if a constant pool entry cannot be found
* @param cf the ClassModel of this class
* @ if a constant pool entry cannot be found
*/
void checkInterfaces(ClassFile cf) throws ConstantPoolException {
int ni = cf.interfaces.length;
for (int i = 0; i < ni; i++) {
String iname = cf.getInterfaceName(i);
void checkInterfaces(ClassModel cf) {
for (var itf : cf.interfaces()) {
String iname = itf.asInternalName();
DeprData dd = db.getTypeDeprecated(iname);
if (dd != null) {
printType("scan.out.implements", cf, iname, dd.isForRemoval());
@ -430,13 +433,13 @@ public class Scan {
/**
* Checks Class_info entries in the constant pool.
*
* @param cf the ClassFile of this class
* @param cf the ClassModel of this class
* @param entries constant pool entries collected from this class
* @throws ConstantPoolException if a constant pool entry cannot be found
* @ if a constant pool entry cannot be found
*/
void checkClasses(ClassFile cf, CPEntries entries) throws ConstantPoolException {
for (ConstantPool.CONSTANT_Class_info ci : entries.classes) {
String name = nameFromRefType(ci.getName());
void checkClasses(ClassModel cf, CPEntries entries) {
for (var ci : entries.classes) {
String name = nameFromRefType(ci.asInternalName());
if (name != null) {
DeprData dd = db.getTypeDeprecated(name);
if (dd != null) {
@ -449,18 +452,18 @@ public class Scan {
/**
* Checks methods referred to from the constant pool.
*
* @param cf the ClassFile of this class
* @param cf the ClassModel of this class
* @param clname the class name
* @param nti the NameAndType_info from a MethodRef or InterfaceMethodRef entry
* @param msgKey message key for localization
* @throws ConstantPoolException if a constant pool entry cannot be found
* @ if a constant pool entry cannot be found
*/
void checkMethodRef(ClassFile cf,
void checkMethodRef(ClassModel cf,
String clname,
CONSTANT_NameAndType_info nti,
String msgKey) throws ConstantPoolException {
String name = nti.getName();
String type = nti.getType();
NameAndTypeEntry nti,
String msgKey) {
String name = nti.name().stringValue();
String type = nti.type().stringValue();
clname = nameFromRefType(clname);
if (clname != null) {
clname = resolveMember(cf, clname, name, type, true, true);
@ -474,15 +477,15 @@ public class Scan {
/**
* Checks fields referred to from the constant pool.
*
* @param cf the ClassFile of this class
* @throws ConstantPoolException if a constant pool entry cannot be found
* @param cf the ClassModel of this class
* @ if a constant pool entry cannot be found
*/
void checkFieldRef(ClassFile cf,
ConstantPool.CONSTANT_Fieldref_info fri) throws ConstantPoolException {
String clname = nameFromRefType(fri.getClassName());
CONSTANT_NameAndType_info nti = fri.getNameAndTypeInfo();
String name = nti.getName();
String type = nti.getType();
void checkFieldRef(ClassModel cf,
FieldRefEntry fri) {
String clname = nameFromRefType(fri.owner().asInternalName());
var nti = fri.nameAndType();
String name = nti.name().stringValue();
String type = nti.type().stringValue();
if (clname != null) {
clname = resolveMember(cf, clname, name, type, false, true);
@ -496,16 +499,16 @@ public class Scan {
/**
* Checks the fields declared in this class.
*
* @param cf the ClassFile of this class
* @throws ConstantPoolException if a constant pool entry cannot be found
* @param cf the ClassModel of this class
* @ if a constant pool entry cannot be found
*/
void checkFields(ClassFile cf) throws ConstantPoolException {
for (Field f : cf.fields) {
String type = nameFromDescType(cf.constant_pool.getUTF8Value(f.descriptor.index));
void checkFields(ClassModel cf) {
for (var f : cf.fields()) {
String type = nameFromDescType(f.fieldType().stringValue());
if (type != null) {
DeprData dd = db.getTypeDeprecated(type);
if (dd != null) {
printHasField(cf, f.getName(cf.constant_pool), type, dd.isForRemoval());
printHasField(cf, f.fieldName().stringValue(), type, dd.isForRemoval());
}
}
}
@ -514,17 +517,18 @@ public class Scan {
/**
* Checks the methods declared in this class.
*
* @param cf the ClassFile object of this class
* @throws ConstantPoolException if a constant pool entry cannot be found
* @param cf the ClassModel object of this class
* @ if a constant pool entry cannot be found
*/
void checkMethods(ClassFile cf) throws ConstantPoolException {
for (Method m : cf.methods) {
String mname = m.getName(cf.constant_pool);
String desc = cf.constant_pool.getUTF8Value(m.descriptor.index);
MethodSig sig = MethodSig.fromDesc(desc);
void checkMethods(ClassModel cf) {
for (var m : cf.methods()) {
String mname = m.methodName().stringValue();
var desc = m.methodType().stringValue();
MethodTypeDesc sig = m.methodTypeSymbol();
DeprData dd;
for (String parm : sig.getParameters()) {
for (var parmDesc : sig.parameterArray()) {
var parm = parmDesc.descriptorString();
parm = nameFromDescType(parm);
if (parm != null) {
dd = db.getTypeDeprecated(parm);
@ -534,7 +538,7 @@ public class Scan {
}
}
String ret = nameFromDescType(sig.getReturnType());
String ret = nameFromDescType(sig.returnType().descriptorString());
if (ret != null) {
dd = db.getTypeDeprecated(ret);
if (dd != null) {
@ -543,7 +547,7 @@ public class Scan {
}
// check overrides
String overridden = resolveMember(cf, cf.getName(), mname, desc, true, false);
String overridden = resolveMember(cf, cf.thisClass().asInternalName(), mname, desc, true, false);
if (overridden != null) {
dd = db.getMethodDeprecated(overridden, mname, desc);
if (dd != null) {
@ -556,12 +560,12 @@ public class Scan {
/**
* Processes a single class file.
*
* @param cf the ClassFile of the class
* @throws ConstantPoolException if a constant pool entry cannot be found
* @param cf the ClassModel of the class
* @ if a constant pool entry cannot be found
*/
void processClass(ClassFile cf) throws ConstantPoolException {
void processClass(ClassModel cf) {
if (verbose) {
out.println(Messages.get("scan.process.class", cf.getName()));
out.println(Messages.get("scan.process.class", cf.thisClass().asInternalName()));
}
CPEntries entries = CPEntries.loadFrom(cf);
@ -570,19 +574,19 @@ public class Scan {
checkInterfaces(cf);
checkClasses(cf, entries);
for (ConstantPool.CONSTANT_Methodref_info mri : entries.methodRefs) {
String clname = mri.getClassName();
CONSTANT_NameAndType_info nti = mri.getNameAndTypeInfo();
for (var mri : entries.methodRefs) {
String clname = mri.owner().asInternalName();
var nti = mri.nameAndType();
checkMethodRef(cf, clname, nti, "scan.out.usesmethod");
}
for (ConstantPool.CONSTANT_InterfaceMethodref_info imri : entries.intfMethodRefs) {
String clname = imri.getClassName();
CONSTANT_NameAndType_info nti = imri.getNameAndTypeInfo();
for (var imri : entries.intfMethodRefs) {
String clname = imri.owner().asInternalName();
var nti = imri.nameAndType();
checkMethodRef(cf, clname, nti, "scan.out.usesintfmethod");
}
for (ConstantPool.CONSTANT_Fieldref_info fri : entries.fieldRefs) {
for (var fri : entries.fieldRefs) {
checkFieldRef(cf, fri);
}
@ -607,13 +611,14 @@ public class Scan {
if (name.endsWith(".class")
&& !name.endsWith("package-info.class")
&& !name.endsWith("module-info.class")) {
processClass(ClassFile.read(jf.getInputStream(entry)));
processClass(ClassFile.of().parse(jf
.getInputStream(entry).readAllBytes()));
}
}
return true;
} catch (NoSuchFileException nsfe) {
errorNoFile(jarname);
} catch (IOException | ConstantPoolException ex) {
} catch (IOException | IllegalArgumentException ex) {
errorException(ex);
}
return false;
@ -640,10 +645,10 @@ public class Scan {
out.println(Messages.get("scan.head.dir", dirname));
for (Path p : classes) {
processClass(ClassFile.read(p));
processClass(ClassFile.of().parse(p));
}
return true;
} catch (IOException | ConstantPoolException ex) {
} catch (IOException | IllegalArgumentException ex) {
errorException(ex);
return false;
}
@ -657,7 +662,7 @@ public class Scan {
*/
public boolean processClassName(String className) {
try {
ClassFile cf = finder.find(className);
ClassModel cf = finder.find(className);
if (cf == null) {
errorNoClass(className);
return false;
@ -665,7 +670,7 @@ public class Scan {
processClass(cf);
return true;
}
} catch (ConstantPoolException ex) {
} catch (IllegalArgumentException ex) {
errorException(ex);
return false;
}
@ -680,12 +685,12 @@ public class Scan {
public boolean processClassFile(String fileName) {
Path path = Paths.get(fileName);
try {
ClassFile cf = ClassFile.read(path);
ClassModel cf = ClassFile.of().parse(path);
processClass(cf);
return true;
} catch (NoSuchFileException nsfe) {
errorNoFile(fileName);
} catch (IOException | ConstantPoolException ex) {
} catch (IOException | IllegalArgumentException ex) {
errorException(ex);
}
return false;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 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
@ -25,7 +25,7 @@
package com.sun.tools.jdeps;
import com.sun.tools.classfile.Dependency.Location;
import com.sun.tools.jdeps.Dependency.Location;
import java.io.BufferedReader;
import java.io.IOException;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 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
@ -25,7 +25,7 @@
package com.sun.tools.jdeps;
import com.sun.tools.classfile.Dependency.Location;
import com.sun.tools.jdeps.Dependency.Location;
import java.io.Closeable;
import java.io.IOException;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 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
@ -25,10 +25,7 @@
package com.sun.tools.jdeps;
import com.sun.tools.classfile.AccessFlags;
import com.sun.tools.classfile.ClassFile;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.Dependencies.ClassFileError;
import com.sun.tools.jdeps.Dependencies.ClassFileError;
import java.io.Closeable;
import java.io.File;
@ -36,13 +33,14 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
@ -54,7 +52,7 @@ import java.util.stream.Stream;
import java.util.zip.ZipFile;
/**
* ClassFileReader reads ClassFile(s) of a given path that can be
* ClassFileReader reads ClassModel(s) of a given path that can be
* a .class file, a directory, or a JAR file.
*/
public class ClassFileReader implements Closeable {
@ -117,10 +115,10 @@ public class ClassFileReader implements Closeable {
}
/**
* Returns the ClassFile matching the given binary name
* Returns the ClassModel matching the given binary name
* or a fully-qualified class name.
*/
public ClassFile getClassFile(String name) throws IOException {
public ClassModel getClassFile(String name) throws IOException {
if (name.indexOf('.') > 0) {
int i = name.lastIndexOf('.');
String pathname = name.replace('.', File.separatorChar) + ".class";
@ -137,31 +135,25 @@ public class ClassFileReader implements Closeable {
return null;
}
public Iterable<ClassFile> getClassFiles() throws IOException {
public Iterable<ClassModel> getClassFiles() throws IOException {
return FileIterator::new;
}
protected ClassFile readClassFile(Path p) throws IOException {
InputStream is = null;
protected ClassModel readClassFile(Path p) throws IOException {
try {
is = Files.newInputStream(p);
return ClassFile.read(is);
} catch (ConstantPoolException e) {
return ClassFile.of().parse(p);
} catch (IllegalArgumentException e) {
throw new ClassFileError(e);
} finally {
if (is != null) {
is.close();
}
}
}
protected Set<String> scan() {
try {
ClassFile cf = ClassFile.read(path);
String name = cf.access_flags.is(AccessFlags.ACC_MODULE)
? "module-info" : cf.getName();
ClassModel cf = ClassFile.of().parse(path);
String name = cf.isModuleInfo()
? "module-info" : cf.thisClass().asInternalName();
return Collections.singleton(name);
} catch (ConstantPoolException|IOException e) {
} catch (IllegalArgumentException|IOException e) {
throw new ClassFileError(e);
}
}
@ -175,7 +167,7 @@ public class ClassFileReader implements Closeable {
public void close() throws IOException {
}
class FileIterator implements Iterator<ClassFile> {
class FileIterator implements Iterator<ClassModel> {
int count;
FileIterator() {
this.count = 0;
@ -184,12 +176,12 @@ public class ClassFileReader implements Closeable {
return count == 0 && baseFileName.endsWith(".class");
}
public ClassFile next() {
public ClassModel next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
try {
ClassFile cf = readClassFile(path);
ClassModel cf = readClassFile(path);
count++;
return cf;
} catch (IOException e) {
@ -228,7 +220,7 @@ public class ClassFileReader implements Closeable {
}
}
public ClassFile getClassFile(String name) throws IOException {
public ClassModel getClassFile(String name) throws IOException {
if (name.indexOf('.') > 0) {
int i = name.lastIndexOf('.');
String pathname = name.replace(".", fsSep) + ".class";
@ -249,12 +241,12 @@ public class ClassFileReader implements Closeable {
return null;
}
public Iterable<ClassFile> getClassFiles() throws IOException {
final Iterator<ClassFile> iter = new DirectoryIterator();
public Iterable<ClassModel> getClassFiles() throws IOException {
final Iterator<ClassModel> iter = new DirectoryIterator();
return () -> iter;
}
class DirectoryIterator implements Iterator<ClassFile> {
class DirectoryIterator implements Iterator<ClassModel> {
private final List<Path> entries;
private int index = 0;
DirectoryIterator() throws IOException {
@ -271,7 +263,7 @@ public class ClassFileReader implements Closeable {
return index != entries.size();
}
public ClassFile next() {
public ClassModel next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
@ -332,7 +324,7 @@ public class ClassFileReader implements Closeable {
}
}
public ClassFile getClassFile(String name) throws IOException {
public ClassModel getClassFile(String name) throws IOException {
if (name.indexOf('.') > 0) {
int i = name.lastIndexOf('.');
String entryName = name.replace('.', '/') + ".class";
@ -353,31 +345,31 @@ public class ClassFileReader implements Closeable {
return null;
}
protected ClassFile readClassFile(JarFile jarfile, JarEntry e) throws IOException {
protected ClassModel readClassFile(JarFile jarfile, JarEntry e) throws IOException {
try (InputStream is = jarfile.getInputStream(e)) {
ClassFile cf = ClassFile.read(is);
ClassModel cf = ClassFile.of().parse(is.readAllBytes());
// exclude module-info.class since this jarFile is on classpath
if (jarfile.isMultiRelease() && !cf.getName().equals("module-info")) {
if (jarfile.isMultiRelease() && !cf.isModuleInfo()) {
VersionHelper.add(jarfile, e, cf);
}
return cf;
} catch (ConstantPoolException ex) {
} catch (IllegalArgumentException ex) {
throw new ClassFileError(ex);
}
}
public Iterable<ClassFile> getClassFiles() throws IOException {
final Iterator<ClassFile> iter = new JarFileIterator(this, jarfile);
public Iterable<ClassModel> getClassFiles() throws IOException {
final Iterator<ClassModel> iter = new JarFileIterator(this, jarfile);
return () -> iter;
}
}
class JarFileIterator implements Iterator<ClassFile> {
class JarFileIterator implements Iterator<ClassModel> {
protected final JarFileReader reader;
protected Iterator<JarEntry> entries;
protected JarFile jf;
protected JarEntry nextEntry;
protected ClassFile cf;
protected ClassModel cf;
JarFileIterator(JarFileReader reader) {
this(reader, null);
}
@ -413,11 +405,11 @@ public class ClassFileReader implements Closeable {
return false;
}
public ClassFile next() {
public ClassModel next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
ClassFile classFile = cf;
ClassModel classFile = cf;
cf = null;
nextEntry = nextEntry();
return classFile;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 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
@ -23,31 +23,20 @@
* questions.
*/
package com.sun.tools.classfile;
package com.sun.tools.jdeps;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.lang.classfile.*;
import java.lang.classfile.constantpool.*;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.lang.reflect.AccessFlag;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import com.sun.tools.classfile.Dependency.Filter;
import com.sun.tools.classfile.Dependency.Finder;
import com.sun.tools.classfile.Dependency.Location;
import com.sun.tools.classfile.Type.ArrayType;
import com.sun.tools.classfile.Type.ClassSigType;
import com.sun.tools.classfile.Type.ClassType;
import com.sun.tools.classfile.Type.MethodType;
import com.sun.tools.classfile.Type.SimpleType;
import com.sun.tools.classfile.Type.TypeParamType;
import com.sun.tools.classfile.Type.WildcardType;
import static com.sun.tools.classfile.ConstantPool.*;
import com.sun.tools.jdeps.Dependency.Filter;
import com.sun.tools.jdeps.Dependency.Finder;
import com.sun.tools.jdeps.Dependency.Location;
/**
* A framework for determining {@link Dependency dependencies} between class files.
@ -107,7 +96,7 @@ public class Dependencies {
* @throws Dependencies.ClassFileNotFoundException if the classfile cannot be
* found
*/
public ClassFile getClassFile(String className)
public ClassModel getClassFile(String className)
throws ClassFileNotFoundException;
}
@ -127,7 +116,7 @@ public class Dependencies {
* @return the default finder
*/
public static Finder getDefaultFinder() {
return new APIDependencyFinder(AccessFlags.ACC_PRIVATE);
return new APIDependencyFinder(ClassFile.ACC_PRIVATE);
}
/**
@ -135,9 +124,9 @@ public class Dependencies {
* These include the superclass, superinterfaces, and classes referenced in
* the declarations of fields and methods. The fields and methods that
* are checked can be limited according to a specified access.
* The access parameter must be one of {@link AccessFlags#ACC_PUBLIC ACC_PUBLIC},
* {@link AccessFlags#ACC_PRIVATE ACC_PRIVATE},
* {@link AccessFlags#ACC_PROTECTED ACC_PROTECTED}, or 0 for
* The access parameter must be one of {@link ClassFile#ACC_PUBLIC ACC_PUBLIC},
* {@link ClassFile#ACC_PRIVATE ACC_PRIVATE},
* {@link ClassFile#ACC_PROTECTED ACC_PROTECTED}, or 0 for
* package private access. Members with greater than or equal accessibility
* to that specified will be searched for dependencies.
* @param access the access of members to be checked
@ -291,7 +280,7 @@ public class Dependencies {
assert (!doneClasses.contains(className));
doneClasses.add(className);
ClassFile cf = classFinder.getClassFile(className);
ClassModel cf = classFinder.getClassFile(className);
// The following code just applies the filter to the dependencies
// followed for the transitive closure.
@ -466,28 +455,25 @@ public class Dependencies {
* This class identifies class names directly or indirectly in the constant pool.
*/
static class ClassDependencyFinder extends BasicDependencyFinder {
public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
public Iterable<? extends Dependency> findDependencies(ClassModel classfile) {
Visitor v = new Visitor(classfile);
for (CPInfo cpInfo: classfile.constant_pool.entries()) {
for (var cpInfo: classfile.constantPool()) {
v.scan(cpInfo);
}
try {
v.addClass(classfile.super_class);
v.addClasses(classfile.interfaces);
v.scan(classfile.attributes);
classfile.superclass().ifPresent(v::addClass);
v.addClasses(classfile.interfaces());
v.scanAttributes(classfile);
for (Field f : classfile.fields) {
v.scan(f.descriptor, f.attributes);
for (var f : classfile.fields()) {
v.scan(f.fieldTypeSymbol());
v.scanAttributes(f);
}
for (Method m : classfile.methods) {
v.scan(m.descriptor, m.attributes);
Exceptions_attribute e =
(Exceptions_attribute)m.attributes.get(Attribute.Exceptions);
if (e != null) {
v.addClasses(e.exception_index_table);
}
for (var m : classfile.methods()) {
v.scan(m.methodTypeSymbol());
v.scanAttributes(m);
}
} catch (ConstantPoolException e) {
} catch (IllegalArgumentException e) {
throw new ClassFileError(e);
}
@ -502,9 +488,9 @@ public class Dependencies {
static class APIDependencyFinder extends BasicDependencyFinder {
APIDependencyFinder(int access) {
switch (access) {
case AccessFlags.ACC_PUBLIC:
case AccessFlags.ACC_PROTECTED:
case AccessFlags.ACC_PRIVATE:
case ClassFile.ACC_PUBLIC:
case ClassFile.ACC_PROTECTED:
case ClassFile.ACC_PRIVATE:
case 0:
showAccess = access;
break;
@ -514,41 +500,40 @@ public class Dependencies {
}
}
public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
public Iterable<? extends Dependency> findDependencies(ClassModel classfile) {
try {
Visitor v = new Visitor(classfile);
v.addClass(classfile.super_class);
v.addClasses(classfile.interfaces);
classfile.superclass().ifPresent(v::addClass);
v.addClasses(classfile.interfaces());
// inner classes?
for (Field f : classfile.fields) {
if (checkAccess(f.access_flags))
v.scan(f.descriptor, f.attributes);
for (var f : classfile.fields()) {
if (checkAccess(f.flags())) {
v.scan(f.fieldTypeSymbol());
v.scanAttributes(f);
}
}
for (Method m : classfile.methods) {
if (checkAccess(m.access_flags)) {
v.scan(m.descriptor, m.attributes);
Exceptions_attribute e =
(Exceptions_attribute) m.attributes.get(Attribute.Exceptions);
if (e != null)
v.addClasses(e.exception_index_table);
for (var m : classfile.methods()) {
if (checkAccess(m.flags())) {
v.scan(m.methodTypeSymbol());
v.scanAttributes(m);
}
}
return v.deps;
} catch (ConstantPoolException e) {
} catch (IllegalArgumentException e) {
throw new ClassFileError(e);
}
}
boolean checkAccess(AccessFlags flags) {
// code copied from javap.Options.checkAccess
boolean isPublic = flags.is(AccessFlags.ACC_PUBLIC);
boolean isProtected = flags.is(AccessFlags.ACC_PROTECTED);
boolean isPrivate = flags.is(AccessFlags.ACC_PRIVATE);
boolean isPublic = flags.has(AccessFlag.PUBLIC);
boolean isProtected = flags.has(AccessFlag.PROTECTED);
boolean isPrivate = flags.has(AccessFlag.PRIVATE);
boolean isPackage = !(isPublic || isProtected || isPrivate);
if ((showAccess == AccessFlags.ACC_PUBLIC) && (isProtected || isPrivate || isPackage))
if ((showAccess == ClassFile.ACC_PUBLIC) && (isProtected || isPrivate || isPackage))
return false;
else if ((showAccess == AccessFlags.ACC_PROTECTED) && (isPrivate || isPackage))
else if ((showAccess == ClassFile.ACC_PROTECTED) && (isPrivate || isPackage))
return false;
else if ((showAccess == 0) && (isPrivate))
return false;
@ -566,238 +551,158 @@ public class Dependencies {
return locations.computeIfAbsent(className, SimpleLocation::new);
}
class Visitor implements ConstantPool.Visitor<Void,Void>, Type.Visitor<Void, Void> {
private ConstantPool constant_pool;
private Location origin;
Set<Dependency> deps;
class Visitor {
private final Location origin;
final Set<Dependency> deps;
Visitor(ClassFile classFile) {
Visitor(ClassModel classFile) {
try {
constant_pool = classFile.constant_pool;
origin = getLocation(classFile.getName());
deps = new HashSet<>();
} catch (ConstantPoolException e) {
origin = getLocation(classFile.thisClass().asInternalName());
} catch (IllegalArgumentException e) {
throw new ClassFileError(e);
}
deps = new HashSet<>();
}
void scan(Descriptor d, Attributes attrs) {
try {
scan(new Signature(d.index).getType(constant_pool));
scan(attrs);
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
private void addDependency(String internalName) {
deps.add(new SimpleDependency(origin, getLocation(internalName)));
}
void scan(CPInfo cpInfo) {
cpInfo.accept(this, null);
private void addClass(ClassEntry ce) throws IllegalArgumentException {
assert ce.name().charAt(0) != '[';
addDependency(ce.asInternalName());
}
void scan(Type t) {
t.accept(this, null);
}
void scan(Attributes attrs) {
try {
Signature_attribute sa = (Signature_attribute)attrs.get(Attribute.Signature);
if (sa != null)
scan(sa.getParsedSignature().getType(constant_pool));
scan((RuntimeVisibleAnnotations_attribute)
attrs.get(Attribute.RuntimeVisibleAnnotations));
scan((RuntimeVisibleParameterAnnotations_attribute)
attrs.get(Attribute.RuntimeVisibleParameterAnnotations));
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
}
private void scan(RuntimeAnnotations_attribute attr) throws ConstantPoolException {
if (attr == null) {
return;
}
for (int i = 0; i < attr.annotations.length; i++) {
int index = attr.annotations[i].type_index;
scan(new Signature(index).getType(constant_pool));
}
}
private void scan(RuntimeParameterAnnotations_attribute attr) throws ConstantPoolException {
if (attr == null) {
return;
}
for (int param = 0; param < attr.parameter_annotations.length; param++) {
for (int i = 0; i < attr.parameter_annotations[param].length; i++) {
int index = attr.parameter_annotations[param][i].type_index;
scan(new Signature(index).getType(constant_pool));
}
}
}
void addClass(int index) throws ConstantPoolException {
if (index != 0) {
String name = constant_pool.getClassInfo(index).getBaseName();
if (name != null)
addDependency(name);
}
}
void addClasses(int[] indices) throws ConstantPoolException {
for (int i: indices)
private void addClasses(Collection<? extends ClassEntry> ces) throws IllegalArgumentException {
for (var i: ces)
addClass(i);
}
private void addDependency(String name) {
deps.add(new SimpleDependency(origin, getLocation(name)));
private void scan(ClassDesc cd) {
while (cd.isArray()) {
cd = cd.componentType();
}
if (cd.isClassOrInterface()) {
var desc = cd.descriptorString();
addDependency(desc.substring(1, desc.length() - 1));
}
}
// ConstantPool.Visitor methods
private void scan(MethodTypeDesc mtd) {
scan(mtd.returnType());
for (int i = 0; i < mtd.parameterCount(); i++) {
scan(mtd.parameterType(i));
}
}
public Void visitClass(CONSTANT_Class_info info, Void p) {
void scanAttributes(AttributedElement attrs) {
try {
if (info.getName().startsWith("["))
new Signature(info.name_index).getType(constant_pool).accept(this, null);
else
addDependency(info.getBaseName());
return null;
} catch (ConstantPoolException e) {
var sa = attrs.findAttribute(Attributes.SIGNATURE).orElse(null);
if (sa != null) {
switch (attrs) {
case ClassModel _ -> scan(sa.asClassSignature());
case MethodModel _ -> scan(sa.asMethodSignature());
default -> scan(sa.asTypeSignature());
}
}
var rvaa = attrs.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS).orElse(null);
if (rvaa != null) {
for (var anno : rvaa.annotations()) {
scan(anno.classSymbol());
}
}
var rvpaa = attrs.findAttribute(Attributes.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS).orElse(null);
if (rvpaa != null) {
for (var parameter : rvpaa.parameterAnnotations()) {
for (var anno : parameter) {
scan(anno.classSymbol());
}
}
}
var exceptions = attrs.findAttribute(Attributes.EXCEPTIONS).orElse(null);
if (exceptions != null) {
for (var e : exceptions.exceptions()) {
addClass(e);
}
}
} catch (IllegalArgumentException e) {
throw new ClassFileError(e);
}
}
public Void visitDouble(CONSTANT_Double_info info, Void p) {
return null;
}
// ConstantPool scanning
public Void visitFieldref(CONSTANT_Fieldref_info info, Void p) {
return visitRef(info, p);
}
public Void visitFloat(CONSTANT_Float_info info, Void p) {
return null;
}
public Void visitInteger(CONSTANT_Integer_info info, Void p) {
return null;
}
public Void visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, Void p) {
return visitRef(info, p);
}
public Void visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, Void p) {
return null;
}
@Override
public Void visitDynamicConstant(CONSTANT_Dynamic_info info, Void aVoid) {
return null;
}
public Void visitLong(CONSTANT_Long_info info, Void p) {
return null;
}
public Void visitMethodHandle(CONSTANT_MethodHandle_info info, Void p) {
return null;
}
public Void visitMethodType(CONSTANT_MethodType_info info, Void p) {
return null;
}
public Void visitMethodref(CONSTANT_Methodref_info info, Void p) {
return visitRef(info, p);
}
public Void visitModule(CONSTANT_Module_info info, Void p) {
return null;
}
public Void visitNameAndType(CONSTANT_NameAndType_info info, Void p) {
void scan(PoolEntry cpInfo) {
try {
new Signature(info.type_index).getType(constant_pool).accept(this, null);
return null;
} catch (ConstantPoolException e) {
switch (cpInfo) {
case ClassEntry clazz -> scan(clazz.asSymbol());
case FieldRefEntry field -> scan(field.owner().asSymbol());
case MethodRefEntry method -> scan(method.owner().asSymbol());
case InterfaceMethodRefEntry interfaceMethod -> scan(interfaceMethod.owner().asSymbol());
case NameAndTypeEntry nat -> {
var desc = nat.type();
if (desc.charAt(0) == '(') {
scan(MethodTypeDesc.ofDescriptor(desc.stringValue()));
} else {
scan(ClassDesc.ofDescriptor(desc.stringValue()));
}
}
default -> {}
}
} catch (IllegalArgumentException e) {
throw new ClassFileError(e);
}
}
public Void visitPackage(CONSTANT_Package_info info, Void p) {
return null;
}
// Signature scanning
public Void visitString(CONSTANT_String_info info, Void p) {
return null;
}
public Void visitUtf8(CONSTANT_Utf8_info info, Void p) {
return null;
}
private Void visitRef(CPRefInfo info, Void p) {
try {
visitClass(info.getClassInfo(), p);
return null;
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
private void scan(MethodSignature sig) {
for (var param : sig.typeParameters()) {
scan(param);
}
for (var param : sig.arguments()) {
scan(param);
}
scan(sig.result());
for (var thrown : sig.throwableSignatures()) {
scan(thrown);
}
}
// Type.Visitor methods
private void findDependencies(Type t) {
if (t != null)
t.accept(this, null);
}
private void findDependencies(List<? extends Type> ts) {
if (ts != null) {
for (Type t: ts)
t.accept(this, null);
private void scan(ClassSignature sig) {
for (var param : sig.typeParameters()) {
scan(param);
}
scan(sig.superclassSignature());
for (var itf : sig.superinterfaceSignatures()) {
scan(itf);
}
}
public Void visitSimpleType(SimpleType type, Void p) {
return null;
private void scan(Signature.TypeParam param) {
param.classBound().ifPresent(this::scan);
for (var itf : param.interfaceBounds()) {
scan(itf);
}
}
public Void visitArrayType(ArrayType type, Void p) {
findDependencies(type.elemType);
return null;
}
public Void visitMethodType(MethodType type, Void p) {
findDependencies(type.paramTypes);
findDependencies(type.returnType);
findDependencies(type.throwsTypes);
findDependencies(type.typeParamTypes);
return null;
}
public Void visitClassSigType(ClassSigType type, Void p) {
findDependencies(type.superclassType);
findDependencies(type.superinterfaceTypes);
return null;
}
public Void visitClassType(ClassType type, Void p) {
findDependencies(type.outerType);
addDependency(type.getBinaryName());
findDependencies(type.typeArgs);
return null;
}
public Void visitTypeParamType(TypeParamType type, Void p) {
findDependencies(type.classBound);
findDependencies(type.interfaceBounds);
return null;
}
public Void visitWildcardType(WildcardType type, Void p) {
findDependencies(type.boundType);
return null;
private void scan(Signature sig) {
switch (sig) {
case Signature.ClassTypeSig ct -> {
ct.outerType().ifPresent(this::scan);
scan(ct.classDesc());
for (var arg : ct.typeArgs()) {
if (arg instanceof Signature.TypeArg.Bounded bounded) {
scan(bounded.boundType());
}
}
}
case Signature.ArrayTypeSig at -> scan(at.componentSignature());
case Signature.BaseTypeSig _, Signature.TypeVarSig _ -> {}
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 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
@ -23,9 +23,11 @@
* questions.
*/
package com.sun.tools.classfile;
package com.sun.tools.jdeps;
import java.lang.classfile.ClassModel;
/**
* A directed relationship between two {@link Dependency.Location Location}s.
* Subtypes of {@code Dependency} may provide additional detail about the dependency.
@ -56,7 +58,7 @@ public interface Dependency {
* @param classfile the class file to be examined
* @return the dependencies located in the given class file.
*/
public Iterable<? extends Dependency> findDependencies(ClassFile classfile);
public Iterable<? extends Dependency> findDependencies(ClassModel classfile);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -28,17 +28,15 @@ import static com.sun.tools.jdeps.Module.*;
import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
import static java.util.stream.Collectors.*;
import com.sun.tools.classfile.AccessFlags;
import com.sun.tools.classfile.ClassFile;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.Dependencies;
import com.sun.tools.classfile.Dependencies.ClassFileError;
import com.sun.tools.classfile.Dependency;
import com.sun.tools.classfile.Dependency.Location;
import com.sun.tools.jdeps.Dependencies.ClassFileError;
import com.sun.tools.jdeps.Dependency.Location;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Paths;
import java.lang.classfile.AccessFlags;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
import java.lang.reflect.AccessFlag;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
@ -46,7 +44,6 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ExecutionException;
@ -176,20 +173,20 @@ class DependencyFinder {
trace("parsing %s %s%n", archive.getName(), archive.getPathName());
FutureTask<Set<Location>> task = new FutureTask<>(() -> {
Set<Location> targets = new HashSet<>();
for (ClassFile cf : archive.reader().getClassFiles()) {
if (cf.access_flags.is(AccessFlags.ACC_MODULE))
for (var cf : archive.reader().getClassFiles()) {
if (cf.isModuleInfo())
continue;
String classFileName;
try {
classFileName = cf.getName();
} catch (ConstantPoolException e) {
classFileName = cf.thisClass().asInternalName();
} catch (IllegalArgumentException e) {
throw new ClassFileError(e);
}
// filter source class/archive
String cn = classFileName.replace('/', '.');
if (!finder.accept(archive, cn, cf.access_flags))
if (!finder.accept(archive, cn, cf.flags()))
continue;
// tests if this class matches the -include
@ -217,24 +214,24 @@ class DependencyFinder {
private Set<Location> parse(Archive archive, Finder finder, String name)
throws IOException
{
ClassFile cf = archive.reader().getClassFile(name);
var cf = archive.reader().getClassFile(name);
if (cf == null) {
throw new IllegalArgumentException(archive.getName() +
" does not contain " + name);
}
if (cf.access_flags.is(AccessFlags.ACC_MODULE))
if (cf.isModuleInfo())
return Collections.emptySet();
Set<Location> targets = new HashSet<>();
String cn;
try {
cn = cf.getName().replace('/', '.');
} catch (ConstantPoolException e) {
cn = cf.thisClass().asInternalName().replace('/', '.');
} catch (IllegalArgumentException e) {
throw new Dependencies.ClassFileError(e);
}
if (!finder.accept(archive, cn, cf.access_flags))
if (!finder.accept(archive, cn, cf.flags()))
return targets;
// tests if this class matches the -include
@ -296,7 +293,7 @@ class DependencyFinder {
Finder(boolean apiOnly) {
this.apiOnly = apiOnly;
this.finder = apiOnly
? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
? Dependencies.getAPIFinder(ClassFile.ACC_PROTECTED)
: Dependencies.getClassDependencyFinder();
}
@ -309,12 +306,12 @@ class DependencyFinder {
// if -apionly is specified, analyze only exported and public types
// All packages are exported in unnamed module.
return apiOnly ? archive.getModule().isExported(pn) &&
accessFlags.is(AccessFlags.ACC_PUBLIC)
accessFlags.has(AccessFlag.PUBLIC)
: true;
}
@Override
public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
public Iterable<? extends Dependency> findDependencies(ClassModel classfile) {
return finder.findDependencies(classfile);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 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
@ -25,10 +25,9 @@
package com.sun.tools.jdeps;
import com.sun.tools.classfile.Dependency.Location;
import com.sun.tools.jdeps.Dependency.Location;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.LinkedHashSet;
import java.util.LinkedList;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 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
@ -28,8 +28,6 @@ package com.sun.tools.jdeps;
import static com.sun.tools.jdeps.Module.trace;
import static java.util.stream.Collectors.*;
import com.sun.tools.classfile.Dependency;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 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
@ -24,9 +24,7 @@
*/
package com.sun.tools.jdeps;
import com.sun.tools.classfile.Dependencies;
import com.sun.tools.classfile.Dependency;
import com.sun.tools.classfile.Dependency.Location;
import com.sun.tools.jdeps.Dependency.Location;
import java.util.HashSet;
import java.util.Set;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 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
@ -29,8 +29,6 @@ import static com.sun.tools.jdeps.Module.*;
import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
import static java.util.stream.Collectors.*;
import com.sun.tools.classfile.Dependency;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.module.ModuleDescriptor;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 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
@ -25,9 +25,7 @@
package com.sun.tools.jdeps;
import com.sun.tools.classfile.ClassFile;
import com.sun.tools.classfile.ConstantPoolException;
import java.lang.classfile.ClassModel;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
@ -44,8 +42,8 @@ public class VersionHelper {
return classname;
}
public static void add(JarFile jarfile, JarEntry e, ClassFile cf)
throws ConstantPoolException
public static void add(JarFile jarfile, JarEntry e, ClassModel cf)
throws IllegalArgumentException
{
String realName = e.getRealName();
if (realName.startsWith(META_INF_VERSIONS)) {
@ -54,7 +52,7 @@ public class VersionHelper {
if (n > 0) {
String version = realName.substring(len, n);
assert (Integer.parseInt(version) > 8);
String name = cf.getName().replace('/', '.');
String name = cf.thisClass().asInternalName().replace('/', '.');
String v = nameToVersion.computeIfAbsent(name, _n -> version);
if (!version.equals(v)) {
throw new MultiReleaseException("err.multirelease.version.associated",

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 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
@ -22,13 +22,16 @@
*/
import java.io.*;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
import java.util.*;
import java.util.regex.Pattern;
import javax.tools.*;
import com.sun.tools.classfile.*;
import com.sun.tools.classfile.Dependencies.*;
import com.sun.tools.classfile.Dependency.Location;
import com.sun.tools.jdeps.Dependencies;
import com.sun.tools.jdeps.Dependencies.*;
import com.sun.tools.jdeps.Dependency;
import com.sun.tools.jdeps.Dependency.Location;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.util.Context;
@ -140,7 +143,7 @@ public class GetDeps {
}
@Override
public ClassFile getClassFile(String className) throws ClassFileNotFoundException {
public ClassModel getClassFile(String className) throws ClassFileNotFoundException {
try {
JavaFileObject fo = fm.getJavaFileForInput(
StandardLocation.CLASS_PATH, className, JavaFileObject.Kind.CLASS);
@ -151,11 +154,11 @@ public class GetDeps {
throw new ClassFileNotFoundException(className);
InputStream in = fo.openInputStream();
try {
return ClassFile.read(in);
return ClassFile.of().parse(in.readAllBytes());
} finally {
in.close();
}
} catch (ConstantPoolException e) {
} catch (IllegalArgumentException e) {
throw new ClassFileNotFoundException(className, e);
} catch (IOException e) {
throw new ClassFileNotFoundException(className, e);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 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
@ -24,9 +24,10 @@
/*
* @test
* @bug 6907575
* @modules jdk.jdeps/com.sun.tools.classfile
* @modules jdk.jdeps/com.sun.tools.jdeps
* jdk.compiler/com.sun.tools.javac.file
* jdk.compiler/com.sun.tools.javac.util
* @enablePreview
* @build GetDeps p.C1
* @run main T6907575
*/

View File

@ -1,61 +0,0 @@
/*
* Copyright (c) 2016, 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.
*/
/*
* @test
* @summary Simple tests for method signature parsing
* @modules jdk.jdeps/com.sun.tools.jdeprscan.scan
* @build TestMethodSig
* @run testng jdk.jdeprscan.TestMethodSig
*/
package jdk.jdeprscan;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static com.sun.tools.jdeprscan.scan.MethodSig.fromDesc;
public class TestMethodSig {
@Test
public void testSimple() {
assertEquals(fromDesc("(Ljava/rmi/RMISecurityManager;)Ljava/lang/Object;").toString(),
"parameters 0=Ljava/rmi/RMISecurityManager; return Ljava/lang/Object;");
}
@Test
public void testMultParamVoidReturn() {
assertEquals(fromDesc("([[IZLjava/lang/String;B[J)V").toString(),
"parameters 0=[[I 1=Z 2=Ljava/lang/String; 3=B 4=[J return V");
}
@Test
public void testNoParams() {
assertEquals(fromDesc("()J").toString(),
"parameters none return J");
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testMissingReturnType() {
fromDesc("(ISJZ)");
}
}