Compare commits

...

2 Commits

Author SHA1 Message Date
Eric Lafortune
0ff5df070c Added support for sealed classes, through permitted subclasses attributes. 2020-09-13 17:09:02 +02:00
Eric Lafortune
e5cf8ddde5 Added support for Java 14 and 15, including record attributes. 2020-09-06 23:54:08 +02:00
17 changed files with 319 additions and 35 deletions

View File

@@ -22,7 +22,7 @@ package proguard.obfuscate;
import proguard.classfile.*;
import proguard.classfile.attribute.*;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.attribute.visitor.*;
import proguard.classfile.visitor.*;
import proguard.util.Processable;
@@ -39,7 +39,8 @@ import java.util.Arrays;
public class AttributeShrinker
implements ClassVisitor,
MemberVisitor,
AttributeVisitor
AttributeVisitor,
RecordComponentInfoVisitor
{
// Implementations for ClassVisitor.
@@ -83,6 +84,13 @@ implements ClassVisitor,
public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
public void visitRecordAttribute(Clazz clazz, RecordAttribute recordAttribute)
{
// Compact any attributes of the components.
recordAttribute.componentsAccept(clazz, this);
}
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
{
// Compact the attributes array.
@@ -92,6 +100,17 @@ implements ClassVisitor,
}
// Implementations for RecordComponentInfoVisitor.
public void visitRecordComponentInfo(Clazz clazz, RecordComponentInfo recordComponentInfo)
{
// Compact the attributes array.
recordComponentInfo.u2attributesCount =
shrinkArray(recordComponentInfo.attributes,
recordComponentInfo.u2attributesCount);
}
// Small utility methods.
/**

View File

@@ -102,6 +102,12 @@ implements AttributeVisitor
}
public void visitPermittedSubclassesAttribute(Clazz clazz, PermittedSubclassesAttribute permittedSubclassesAttribute)
{
attributeVisitor.visitPermittedSubclassesAttribute(clazz, permittedSubclassesAttribute);
}
public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute)
{
attributeVisitor.visitModuleAttribute(clazz, moduleAttribute);

View File

@@ -44,9 +44,12 @@ import proguard.util.ArrayUtil;
*/
public class TargetClassChanger
implements ClassVisitor,
// Implementation interfaces.
ConstantVisitor,
MemberVisitor,
AttributeVisitor,
RecordComponentInfoVisitor,
LocalVariableInfoVisitor,
LocalVariableTypeInfoVisitor,
AnnotationVisitor,
@@ -305,6 +308,13 @@ implements ClassVisitor,
public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
public void visitRecordAttribute(Clazz clazz, RecordAttribute recordAttribute)
{
// Fix the record component attributes.
recordAttribute.componentsAccept(clazz, this);
}
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
{
// Change the references of the attributes.
@@ -354,6 +364,18 @@ implements ClassVisitor,
}
// Implementations for RecordComponentInfoVisitor.
public void visitRecordComponentInfo(Clazz clazz, RecordComponentInfo recordComponentInfo)
{
// Don't change the referenced field; it's still the original one
// in this class.
// Change the references of the attributes.
recordComponentInfo.attributesAccept(clazz, this);
}
// Implementations for LocalVariableInfoVisitor.
public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)

View File

@@ -46,6 +46,7 @@ public class ClassShrinker
implements ClassVisitor,
MemberVisitor,
AttributeVisitor,
RecordComponentInfoVisitor,
AnnotationVisitor,
ElementValueVisitor
{
@@ -53,7 +54,7 @@ implements ClassVisitor,
private int[] constantIndexMap = new int[ClassEstimates.TYPICAL_CONSTANT_POOL_SIZE];
private int[] bootstrapMethodIndexMap = new int[ClassEstimates.TYPICAL_CONSTANT_POOL_SIZE];
private final MyNestmemberShrinker nestMemberShrinker = new MyNestmemberShrinker();
private final MyAttributeShrinker attributeShrinker = new MyAttributeShrinker();
private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper();
private final BootstrapMethodRemapper bootstrapMethodRemapper = new BootstrapMethodRemapper();
private final MySignatureCleaner signatureCleaner = new MySignatureCleaner();
@@ -126,8 +127,8 @@ implements ClassVisitor,
.visitProgramClass(programClass);
}
// Shrink the arrays for nest members.
programClass.attributesAccept(nestMemberShrinker);
// Shrink the arrays for nest members and permitted subclasses.
programClass.attributesAccept(attributeShrinker);
// Shrink the constant pool, also setting up an index map.
int newConstantPoolCount =
@@ -233,6 +234,18 @@ implements ClassVisitor,
}
public void visitRecordAttribute(Clazz clazz, RecordAttribute recordAttribute)
{
// Shrink the array of RecordComponentInfo objects.
recordAttribute.u2componentsCount =
shrinkArray(recordAttribute.components,
recordAttribute.u2componentsCount);
// Shrink the attributes of the remaining components.
recordAttribute.componentsAccept(clazz, this);
}
public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
{
// Shrink the array of InnerClassesInfo objects.
@@ -318,6 +331,20 @@ implements ClassVisitor,
}
// Implementations for RecordComponentInfoVisitor.
public void visitRecordComponentInfo(Clazz clazz, RecordComponentInfo recordComponentInfo)
{
// Shrink the attributes array.
recordComponentInfo.u2attributesCount =
shrinkArray(recordComponentInfo.attributes,
recordComponentInfo.u2attributesCount);
// Shrink the remaining attributes.
recordComponentInfo.attributesAccept(clazz, this);
}
// Implementations for AnnotationVisitor.
public void visitAnnotation(Clazz clazz, Annotation annotation)
@@ -334,9 +361,10 @@ implements ClassVisitor,
/**
* This AttributeVisitor shrinks the nest members in the nest member
* attributes and the permitted subclasses in the permitted subclasses
* attributes that it visits.
*/
private class MyNestmemberShrinker
private class MyAttributeShrinker
implements AttributeVisitor
{
public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
@@ -351,6 +379,17 @@ implements ClassVisitor,
nestMembersAttribute.u2classes,
nestMembersAttribute.u2classesCount);
}
public void visitPermittedSubclassesAttribute(Clazz clazz, PermittedSubclassesAttribute permittedSubclassesAttribute)
{
// Shrink the array of nest member indices.
// We must do this before the corresponding constants are remapped.
permittedSubclassesAttribute.u2classesCount =
shrinkConstantIndexArray(((ProgramClass)clazz).constantPool,
permittedSubclassesAttribute.u2classes,
permittedSubclassesAttribute.u2classesCount);
}
}

View File

@@ -857,15 +857,6 @@ implements ClassVisitor,
}
public void visitSourceDebugExtensionAttribute(Clazz clazz,
SourceDebugExtensionAttribute sourceDebugExtensionAttribute)
{
markAsUsed(sourceDebugExtensionAttribute);
markConstant(clazz, sourceDebugExtensionAttribute.u2attributeNameIndex);
}
public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute)
{
// Don't mark the attribute and its name here. We may mark it in
@@ -891,6 +882,26 @@ implements ClassVisitor,
}
public void visitSourceDebugExtensionAttribute(Clazz clazz, SourceDebugExtensionAttribute sourceDebugExtensionAttribute)
{
markAsUsed(sourceDebugExtensionAttribute);
markConstant(clazz, sourceDebugExtensionAttribute.u2attributeNameIndex);
}
public void visitRecordAttribute(Clazz clazz, RecordAttribute recordAttribute)
{
markAsUsed(recordAttribute);
markConstant(clazz, recordAttribute.u2attributeNameIndex);
// Don't mark the components yet. We may mark them later, in
// RecordComponentUsageMarker.
//recordAttribute.componentsAccept(clazz, this);
}
public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
{
// Don't mark the attribute and its name yet. We may mark it later, in
@@ -938,6 +949,19 @@ implements ClassVisitor,
}
public void visitPermittedSubclassesAttribute(Clazz clazz, PermittedSubclassesAttribute permittedSubclassesAttribute)
{
// Don't mark the attribute and its contents yet. We may mark it later,
// in NestUsageMarker.
//markAsUsed(permittedSubclassesAttribute);
//markConstant(clazz, permittedSubclassesAttribute.u2attributeNameIndex);
// Mark the nest member entries.
//permittedSubclassesAttribute.memberClassConstantsAccept(clazz, this);
}
public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute)
{
markAsUsed(moduleAttribute);

View File

@@ -28,8 +28,8 @@ import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.visitor.ClassVisitor;
/**
* This AttributeVisitor marks all necessary nest host attributes and nest
* members attributes that it visits.
* This AttributeVisitor marks all necessary nest host attributes, nest
* members attributes, and permitted subclasses attributes that it visits.
*
* @see UsageMarker
*
@@ -82,14 +82,14 @@ implements AttributeVisitor,
public void visitNestMembersAttribute(Clazz clazz, NestMembersAttribute nestMembersAttribute)
{
// Mark the necessary inner classes information.
// Mark the necessary nest member information.
attributeUsed = false;
nestMembersAttribute.memberClassConstantsAccept(clazz, this);
if (attributeUsed)
{
// We got a positive used flag, so the nest members class is being used.
// Mark this attribute as being used as well.
// We got a positive used flag, so at least one of the nest members
// is being used. Mark this attribute as being used as well.
classUsageMarker.markAsUsed(nestMembersAttribute);
markConstant(clazz, nestMembersAttribute.u2attributeNameIndex);
@@ -97,6 +97,24 @@ implements AttributeVisitor,
}
public void visitPermittedSubclassesAttribute(Clazz clazz, PermittedSubclassesAttribute permittedSubclassesAttribute)
{
// Mark the necessary permitted subclasses information.
attributeUsed = false;
permittedSubclassesAttribute.permittedSubclassConstantsAccept(clazz, this);
if (attributeUsed)
{
// We got a positive used flag, so at least one of the permitted
// subclasses class is being used. Mark this attribute as being
// used as well.
classUsageMarker.markAsUsed(permittedSubclassesAttribute);
markConstant(clazz, permittedSubclassesAttribute.u2attributeNameIndex);
}
}
// Implementations for ConstantVisitor.
public void visitClassConstant(Clazz clazz, ClassConstant classConstant)

View File

@@ -0,0 +1,108 @@
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2020 Guardsquare NV
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program 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 for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package proguard.shrink;
import proguard.classfile.*;
import proguard.classfile.attribute.*;
import proguard.classfile.attribute.visitor.*;
import proguard.classfile.constant.*;
import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.visitor.*;
/**
* This RecordComponentInfoVisitor marks all record components that
* it visits and whose corresponding fields have been marked before.
*
* @see ClassUsageMarker
*
* @author Eric Lafortune
*/
public class RecordComponentUsageMarker
implements RecordComponentInfoVisitor,
MemberVisitor,
ConstantVisitor
{
private final ClassUsageMarker classUsageMarker;
// Field acting as a return parameter.
private boolean fieldUsed;
/**
* Creates a new InnerUsageMarker.
* @param classUsageMarker the marker to mark and check the classes and
* class members.
*/
public RecordComponentUsageMarker(ClassUsageMarker classUsageMarker)
{
this.classUsageMarker = classUsageMarker;
}
// Implementations for RecordComponentInfoVisitor.
public void visitRecordComponentInfo(Clazz clazz, RecordComponentInfo recordComponentInfo)
{
// Is the field that corresponds to the component used?
fieldUsed = false;
recordComponentInfo.referencedFieldAccept(clazz, this);
if (fieldUsed)
{
// Mark the component.
classUsageMarker.markAsUsed(recordComponentInfo);
// Mark its name and descriptor.
markConstant(clazz, recordComponentInfo.u2nameIndex);
markConstant(clazz, recordComponentInfo.u2descriptorIndex);
// Mark its attributes.
recordComponentInfo.attributesAccept(clazz, classUsageMarker);
}
}
// Implementations for MemberVisitor.
public void visitProgramField(ProgramClass programClass, ProgramField programField)
{
fieldUsed = classUsageMarker.isUsed(programField);
}
// Implementations for ConstantVisitor.
public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
{
classUsageMarker.markAsUsed(utf8Constant);
}
// Small utility methods.
/**
* Marks the given constant pool entry of the given class. This includes
* visiting any other referenced constant pool entries.
*/
private void markConstant(Clazz clazz, int index)
{
clazz.constantPoolEntryAccept(index, this);
}
}

View File

@@ -138,6 +138,12 @@ public class UsageMarker
new KotlinModuleUsageMarker(simpleUsageMarker))));
}
// Mark the record metadata.
programClassPool.classesAccept(
new AllAttributeVisitor(
new AllRecordComponentInfoVisitor(
new RecordComponentUsageMarker(classUsageMarker))));
// Check if the Gson optimization is enabled.
StringMatcher filter = configuration.optimizations != null ?
new ListParser(new NameParser()).parse(configuration.optimizations) :

View File

@@ -69,15 +69,16 @@ Yes, you can. **ProGuard** itself is distributed under the GPL, but this
doesn't affect the programs that you process. Your code remains yours, and its
license can remain the same.
## Does ProGuard work with Java 2, 5,..., 14? {: #jdk1.4}
## Does ProGuard work with Java 2, 5,..., 15? {: #jdk1.4}
Yes, **ProGuard** supports all JDKs from 1.1 up to and including 14. Java 2
Yes, **ProGuard** supports all JDKs from 1.1 up to and including 15. Java 2
introduced some small differences in the class file format. Java 5 added
attributes for generics and for annotations. Java 6 introduced optional
preverification attributes. Java 7 made preverification obligatory and
introduced support for dynamic languages. Java 8 added more attributes and
default methods. Java 9 added support for modules. Java 11 added dynamic
constants and nest-based access control. Java 14 added records.
constants and nest-based access control. Java 14 added records. Java 15
added sealed classes.
**ProGuard** handles all versions correctly.
## Does ProGuard work with Java Micro Edition? {: #jme}

View File

@@ -47,6 +47,10 @@ attributes. You can keep them with the
: Specifies the name of the source directory from which the class file was
compiled.
`Record` <div>(Java 14 or higher)</div>
: Specifies the components of a record class. Code may access this information
by reflection.
`InnerClasses`
: Specifies the relationship between a class and its inner classes and outer
classes. Other than this and the naming convention with a '\$' separator
@@ -55,6 +59,10 @@ attributes. You can keep them with the
referenced in a compiled library. Code may access this information by
reflection, for instance to derive the simple name of the class.
`PermittedSubclasses` <div>(Java 15 or higher)</div>
: Specifies the allowed extensions or implementations of sealed classes or
interfaces.
`EnclosingMethod`<div>(Java 5 or higher)</div>
: Specifies the method in which the class was defined. Compilers may need this
information to find classes referenced in a compiled library. Code may
@@ -135,7 +143,7 @@ ProGuard automatically keeps the following essential attributes, processing
them as necessary. We're listing them for the sake of completeness:
`ConstantValue`
: Specifies a constant integer, float, class, string, etc.
: Specifies a constant integer, float, class, string, etc.
`Code`
: Specifies the actual bytecode of a method.

View File

@@ -347,8 +347,9 @@ still be used as such, for developing code based on its public API.
-keepparameternames
-renamesourcefileattribute SourceFile
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
-keepattributes Signature,Exceptions,*Annotation*,
InnerClasses,PermittedSubclasses,EnclosingMethod,
Deprecated,SourceFile,LineNumberTable
-keepclasseswithmembernames,includedescriptorclasses class * {
native <methods>;
@@ -373,16 +374,26 @@ access in the library. Only if there are any other non-public classes or
methods that are invoked dynamically, they should be specified using
additional [`-keep`](usage.md#keep) options.
The "Signature" attribute is required to be able to access generic types.
The "Exceptions" attribute has to be preserved, so the compiler knows
which exceptions methods may throw.
The various "\*Annotations\*" attributes contain any annotations, which
developers might need to access through reflection.
The "InnerClasses" attribute (or more precisely, its source name part) has to
be preserved too, for any inner classes that can be referenced from outside
the library. The `javac` compiler would be unable to find the inner classes
otherwise.
The "Signature" attribute is required to be able to access generic types when
compiling in JDK 5.0 and higher.
The "PermittedSubclasses" attribute defines sealed classes, which developers
can't extend further.
The "EnclosingMethod" attribute marks classes that are defined inside methods.
The "Deprecated" attribute marks any deprecated classes, fields, or
methods, which may be useful for developers to know.
The [`-keepparameternames`](usage.md#keepparameternames) option keeps the
parameter names in the "LocalVariableTable" and "LocalVariableTypeTable"

View File

@@ -1,3 +1,13 @@
## Version 7.1
| Version| Issue | Module | Explanation
|--------|----------|----------|----------------------------------
| 7.1.x | PGD-0064 | CORE | Added support for Java 14 and 15.
| 7.1.x | PGD-0064 | CORE | Added support for sealed classes (permitted subclasses attributes).
| 7.1.x | PGD-0064 | CORE | Added support for record attributes.
| 7.1.x | DGD-2390 | CORE | Fixed storage and alignment of uncompressed zip entries.
| 7.1.x | DGD-2338 | CORE | Fixed processing of constant boolean arrays.
## Version 7.0 (Jun 2020)
| Version| Issue | Module | Explanation

View File

@@ -21,13 +21,14 @@
<!-- Keep some useful attributes. -->
<keepattribute name="Signature" />
<keepattribute name="Exceptions" />
<keepattribute name="InnerClasses" />
<keepattribute name="Signature" />
<keepattribute name="PermittedSubclasses" />
<keepattribute name="EnclosingMethod" />
<keepattribute name="Deprecated" />
<keepattribute name="SourceFile" />
<keepattribute name="LineNumberTable" />
<keepattribute name="EnclosingMethod" />
<!-- Preserve all public classes, and their public and protected fields
and methods. -->

View File

@@ -53,7 +53,7 @@ task ('proguard', type: proguard.gradle.ProGuardTask) {
printmapping 'out.map'
keepparameternames
renamesourcefileattribute 'SourceFile'
keepattributes 'Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,EnclosingMethod'
keepattributes 'Signature,Exceptions,InnerClasses,PermittedSubclasses,EnclosingMethod,Deprecated,SourceFile,LineNumberTable'
// Preserve all annotations.

View File

@@ -28,8 +28,9 @@
-printmapping out.map
-keepparameternames
-renamesourcefileattribute SourceFile
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
SourceFile,LineNumberTable,EnclosingMethod
-keepattributes Signature,Exceptions,
InnerClasses,PermittedSubclasses,EnclosingMethod,
Deprecated,SourceFile,LineNumberTable
# Preserve all annotations.
@@ -78,7 +79,7 @@
java.lang.Object readResolve();
}
# Your library may contain more items that need to be preserved;
# Your library may contain more items that need to be preserved;
# typically classes that are dynamically created using Class.forName:
# -keep public class com.example.MyClass

View File

@@ -2,5 +2,10 @@ gsonVersion = 2.8.5
kotlinVersion = 1.3.31
kotlinxMetadataVersion = 0.1.0
target = 1.8
# Optionally compile the WTK plugin.
wtkDir = /usr/local/java/wtk2.1
wtkHome = /usr/local/java/wtk
wtkHome = /usr/local/java/wtk
# Optionally set up a composite build with ProGuardCORE.
#proguardCoreDir = ../proguard-core

View File

@@ -11,6 +11,11 @@ pluginManagement {
}
}
if (hasProperty('proguardCoreDir'))
{
includeBuild proguardCoreDir
}
rootProject.name = 'parent'
include 'base'