8168444: (jdeprscan) improper handling of primitives and primitive array types

Reviewed-by: psandoz, jjg
This commit is contained in:
Stuart Marks 2017-04-25 16:14:35 -07:00
parent 4a269851c9
commit d6bf03fe8e
4 changed files with 262 additions and 36 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2017, 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
@ -97,21 +97,70 @@ public class Scan {
finder = f;
}
Pattern typePattern = Pattern.compile("\\[*L(.*);");
// "flattens" an array type name to its component type
// and a reference type "Lpkg/pkg/pkg/name;" to its base name
// "pkg/pkg/pkg/name".
// TODO: deal with primitive types
String flatten(String typeName) {
Matcher matcher = typePattern.matcher(typeName);
/**
* Given a descriptor type, extracts and returns the class name from it, if any.
* These types are obtained from field descriptors (JVMS 4.3.2) and method
* descriptors (JVMS 4.3.3). They have one of the following forms:
*
* I // or any other primitive, or V for void
* [I // array of primitives, including multi-dimensional
* Lname; // the named class
* [Lname; // array whose component is the named class (also multi-d)
*
* This method extracts and returns the class name, or returns empty for primitives, void,
* or array of primitives.
*
* Returns nullable reference instead of Optional because downstream
* processing can throw checked exceptions.
*
* @param descType the type from a descriptor
* @return the extracted class name, or null
*/
String nameFromDescType(String descType) {
Matcher matcher = descTypePattern.matcher(descType);
if (matcher.matches()) {
return matcher.group(1);
} else {
return typeName;
return null;
}
}
Pattern descTypePattern = Pattern.compile("\\[*L(.*);");
/**
* Given a ref type name, extracts and returns the class name from it, if any.
* Ref type names are obtained from a Class_info structure (JVMS 4.4.1) and from
* Fieldref_info, Methodref_info, and InterfaceMethodref_info structures (JVMS 4.4.2).
* They represent named classes or array classes mentioned by name, and they
* represent class or interface types that have the referenced field or method
* as a member. They have one of the following forms:
*
* [I // array of primitives, including multi-dimensional
* name // the named class
* [Lname; // array whose component is the named class (also multi-d)
*
* Notably, a plain class name doesn't have the L prefix and ; suffix, and
* primitives and void do not occur.
*
* Returns nullable reference instead of Optional because downstream
* processing can throw checked exceptions.
*
* @param refType a reference type name
* @return the extracted class name, or null
*/
String nameFromRefType(String refType) {
Matcher matcher = refTypePattern.matcher(refType);
if (matcher.matches()) {
return matcher.group(1);
} else if (refType.startsWith("[")) {
return null;
} else {
return refType;
}
}
Pattern refTypePattern = Pattern.compile("\\[+L(.*);");
String typeKind(ClassFile cf) {
AccessFlags flags = cf.access_flags;
if (flags.is(ACC_ENUM)) {
@ -381,10 +430,12 @@ public class Scan {
*/
void checkClasses(ClassFile cf, CPEntries entries) throws ConstantPoolException {
for (ConstantPool.CONSTANT_Class_info ci : entries.classes) {
String className = ci.getName();
DeprData dd = db.getTypeDeprecated(flatten(className));
if (dd != null) {
printType("scan.out.usesclass", cf, className, dd.isForRemoval());
String name = nameFromRefType(ci.getName());
if (name != null) {
DeprData dd = db.getTypeDeprecated(name);
if (dd != null) {
printType("scan.out.usesclass", cf, name, dd.isForRemoval());
}
}
}
}
@ -393,8 +444,8 @@ public class Scan {
* Checks methods referred to from the constant pool.
*
* @param cf the ClassFile of this class
* @param nti the NameAndType_info from a MethodRef or InterfaceMethodRef entry
* @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
*/
@ -404,10 +455,13 @@ public class Scan {
String msgKey) throws ConstantPoolException {
String name = nti.getName();
String type = nti.getType();
clname = resolveMember(cf, flatten(clname), name, type, true, true);
DeprData dd = db.getMethodDeprecated(clname, name, type);
if (dd != null) {
printMethod(msgKey, cf, clname, name, type, dd.isForRemoval());
clname = nameFromRefType(clname);
if (clname != null) {
clname = resolveMember(cf, clname, name, type, true, true);
DeprData dd = db.getMethodDeprecated(clname, name, type);
if (dd != null) {
printMethod(msgKey, cf, clname, name, type, dd.isForRemoval());
}
}
}
@ -419,15 +473,17 @@ public class Scan {
*/
void checkFieldRef(ClassFile cf,
ConstantPool.CONSTANT_Fieldref_info fri) throws ConstantPoolException {
String clname = fri.getClassName();
String clname = nameFromRefType(fri.getClassName());
CONSTANT_NameAndType_info nti = fri.getNameAndTypeInfo();
String name = nti.getName();
String type = nti.getType();
clname = resolveMember(cf, flatten(clname), name, type, false, true);
DeprData dd = db.getFieldDeprecated(clname, name);
if (dd != null) {
printField("scan.out.usesfield", cf, clname, name, dd.isForRemoval());
if (clname != null) {
clname = resolveMember(cf, clname, name, type, false, true);
DeprData dd = db.getFieldDeprecated(clname, name);
if (dd != null) {
printField("scan.out.usesfield", cf, clname, name, dd.isForRemoval());
}
}
}
@ -439,10 +495,12 @@ public class Scan {
*/
void checkFields(ClassFile cf) throws ConstantPoolException {
for (Field f : cf.fields) {
String type = cf.constant_pool.getUTF8Value(f.descriptor.index);
DeprData dd = db.getTypeDeprecated(flatten(type));
if (dd != null) {
printHasField(cf, f.getName(cf.constant_pool), type, dd.isForRemoval());
String type = nameFromDescType(cf.constant_pool.getUTF8Value(f.descriptor.index));
if (type != null) {
DeprData dd = db.getTypeDeprecated(type);
if (dd != null) {
printHasField(cf, f.getName(cf.constant_pool), type, dd.isForRemoval());
}
}
}
}
@ -461,16 +519,21 @@ public class Scan {
DeprData dd;
for (String parm : sig.getParameters()) {
dd = db.getTypeDeprecated(flatten(parm));
if (dd != null) {
printHasMethodParmType(cf, mname, parm, dd.isForRemoval());
parm = nameFromDescType(parm);
if (parm != null) {
dd = db.getTypeDeprecated(parm);
if (dd != null) {
printHasMethodParmType(cf, mname, parm, dd.isForRemoval());
}
}
}
String ret = sig.getReturnType();
dd = db.getTypeDeprecated(flatten(ret));
if (dd != null) {
printHasMethodRetType(cf, mname, ret, dd.isForRemoval());
String ret = nameFromDescType(sig.getReturnType());
if (ret != null) {
dd = db.getTypeDeprecated(ret);
if (dd != null) {
printHasMethodRetType(cf, mname, ret, dd.isForRemoval());
}
}
// check overrides

View File

@ -1,4 +1,4 @@
#jdepr 1
#jdepr1
METHOD,jdk/deprcases/members/ExampleAnnotation,name()Ljava/lang/String;,,false
FIELD,jdk/deprcases/members/ExampleClass,field1,,false
FIELD,jdk/deprcases/members/ExampleClass,field2,,false

Can't render this file because it has a wrong number of fields in line 2.

View File

@ -0,0 +1,18 @@
#jdepr1
CLASS,V,,,false
CLASS,Z,,,false
CLASS,B,,,false
CLASS,S,,,false
CLASS,C,,,false
CLASS,I,,,false
CLASS,J,,,false
CLASS,F,,,false
CLASS,D,,,false
CLASS,[Z,,,false
CLASS,[B,,,false
CLASS,[S,,,false
CLASS,[C,,,false
CLASS,[I,,,false
CLASS,[J,,,false
CLASS,[F,,,false
CLASS,[D,,,false
1 #jdepr1
2 CLASS,V,,,false
3 CLASS,Z,,,false
4 CLASS,B,,,false
5 CLASS,S,,,false
6 CLASS,C,,,false
7 CLASS,I,,,false
8 CLASS,J,,,false
9 CLASS,F,,,false
10 CLASS,D,,,false
11 CLASS,[Z,,,false
12 CLASS,[B,,,false
13 CLASS,[S,,,false
14 CLASS,[C,,,false
15 CLASS,[I,,,false
16 CLASS,[J,,,false
17 CLASS,[F,,,false
18 CLASS,[D,,,false

View File

@ -0,0 +1,145 @@
/*
* Copyright (c) 2017, 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
* @bug 8168444
* @summary Test of jdeprscan handling of primitives and primitive arrays.
* @modules jdk.jdeps/com.sun.tools.jdeprscan
* @build jdk.jdeprscan.TestPrims
* @run testng jdk.jdeprscan.TestPrims
*/
package jdk.jdeprscan;
import com.sun.tools.jdeprscan.Main;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.regex.Pattern;
import org.testng.annotations.Test;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
public class TestPrims {
@Test
public void test() throws IOException {
final String TESTSRC = System.getProperty("test.src");
final String TESTCLASSPATH = System.getProperty("test.class.path");
String CSV_FILE = TESTSRC + File.separator + "TestPrims.csv";
ByteArrayOutputStream outBaos = new ByteArrayOutputStream();
ByteArrayOutputStream errBaos = new ByteArrayOutputStream();
boolean mainResult;
try (PrintStream out = new PrintStream(outBaos, false, "UTF-8");
PrintStream err = new PrintStream(errBaos, false, "UTF-8")) {
mainResult = Main.call(
out, err,
"--class-path", TESTCLASSPATH,
"--Xload-csv", CSV_FILE,
"jdk.jdeprscan.TestPrims$Usage");
// assertion is checked below after output is dumped
}
byte[] outBytes = outBaos.toByteArray();
byte[] errBytes = errBaos.toByteArray();
ByteArrayInputStream outbais = new ByteArrayInputStream(outBytes);
ByteArrayInputStream errbais = new ByteArrayInputStream(errBytes);
System.out.println("--- stdout ---");
outbais.transferTo(System.out);
System.out.println("--- end stdout ---");
System.out.println("--- stderr ---");
errbais.transferTo(System.out);
System.out.println("--- end stderr ---");
String outString = new String(outBytes, "UTF-8");
String errString = new String(errBytes, "UTF-8");
// matches message "class <classname> uses deprecated class [I"
boolean outMatch = Pattern.compile("^class ").matcher(outString).find();
// matches message "error: cannot find class [I"
boolean errMatch = Pattern.compile("^error: ").matcher(errString).find();
if (!mainResult) {
System.out.println("FAIL: Main.call returned false");
}
if (outMatch) {
System.out.println("FAIL: stdout contains unexpected error message");
}
if (errMatch) {
System.out.println("FAIL: stderr contains unexpected error message");
}
assertTrue(mainResult && !outMatch && !errMatch);
}
static class Usage {
void prims(boolean z, byte b, short s, char c,
int i, long j, float f, double d) { }
void primsArrays(boolean[] z, byte[] b, short[] s, char[] c,
int[] i, long[] j, float[] f, double[] d) { }
boolean zfield;
byte bfield;
short sfield;
char cfield;
int ifield;
long jfield;
float ffield;
double dfield;
boolean[] azfield;
byte[] abfield;
short[] asfield;
char[] acfield;
int[] aifield;
long[] ajfield;
float[] affield;
double[] adfield;
Object[] clones() {
return new Object[] {
azfield.clone(),
abfield.clone(),
asfield.clone(),
acfield.clone(),
aifield.clone(),
ajfield.clone(),
affield.clone(),
adfield.clone()
};
}
}
}