aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/java/proguard/proguard5.3.3/src/proguard/classfile/util/DynamicClassReferenceInitializer.java
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/java/proguard/proguard5.3.3/src/proguard/classfile/util/DynamicClassReferenceInitializer.java')
-rw-r--r--third_party/java/proguard/proguard5.3.3/src/proguard/classfile/util/DynamicClassReferenceInitializer.java486
1 files changed, 486 insertions, 0 deletions
diff --git a/third_party/java/proguard/proguard5.3.3/src/proguard/classfile/util/DynamicClassReferenceInitializer.java b/third_party/java/proguard/proguard5.3.3/src/proguard/classfile/util/DynamicClassReferenceInitializer.java
new file mode 100644
index 0000000000..c7e1c9b0d1
--- /dev/null
+++ b/third_party/java/proguard/proguard5.3.3/src/proguard/classfile/util/DynamicClassReferenceInitializer.java
@@ -0,0 +1,486 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ * of Java bytecode.
+ *
+ * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare
+ *
+ * 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.classfile.util;
+
+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.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.util.StringMatcher;
+
+/**
+ * This InstructionVisitor initializes any constant <code>Class.forName</code> or
+ * <code>.class</code> references of all classes it visits. More specifically,
+ * it fills out the references of string constant pool entries that refer to a
+ * class in the program class pool or in the library class pool.
+ * <p>
+ * It optionally prints notes if on usage of
+ * <code>(SomeClass)Class.forName(variable).newInstance()</code>.
+ * <p>
+ * The class hierarchy must be initialized before using this visitor.
+ *
+ * @see ClassSuperHierarchyInitializer
+ *
+ * @author Eric Lafortune
+ */
+public class DynamicClassReferenceInitializer
+extends SimplifiedVisitor
+implements InstructionVisitor,
+ ConstantVisitor,
+ AttributeVisitor
+{
+ public static final int X = InstructionSequenceMatcher.X;
+ public static final int Y = InstructionSequenceMatcher.Y;
+ public static final int Z = InstructionSequenceMatcher.Z;
+
+ public static final int A = InstructionSequenceMatcher.A;
+ public static final int B = InstructionSequenceMatcher.B;
+ public static final int C = InstructionSequenceMatcher.C;
+ public static final int D = InstructionSequenceMatcher.D;
+
+
+ private final Constant[] CLASS_FOR_NAME_CONSTANTS = new Constant[]
+ {
+ // 0
+ new MethodrefConstant(1, 2, null, null),
+ new ClassConstant(3, null),
+ new NameAndTypeConstant(4, 5),
+ new Utf8Constant(ClassConstants.NAME_JAVA_LANG_CLASS),
+ new Utf8Constant(ClassConstants.METHOD_NAME_CLASS_FOR_NAME),
+ new Utf8Constant(ClassConstants.METHOD_TYPE_CLASS_FOR_NAME),
+
+ // 6
+ new MethodrefConstant(1, 7, null, null),
+ new NameAndTypeConstant(8, 9),
+ new Utf8Constant(ClassConstants.METHOD_NAME_NEW_INSTANCE),
+ new Utf8Constant(ClassConstants.METHOD_TYPE_NEW_INSTANCE),
+
+ // 10
+ new MethodrefConstant(1, 11, null, null),
+ new NameAndTypeConstant(12, 13),
+ new Utf8Constant(ClassConstants.METHOD_NAME_CLASS_GET_COMPONENT_TYPE),
+ new Utf8Constant(ClassConstants.METHOD_TYPE_CLASS_GET_COMPONENT_TYPE),
+ };
+
+ // Class.forName("SomeClass").
+ private final Instruction[] CONSTANT_CLASS_FOR_NAME_INSTRUCTIONS = new Instruction[]
+ {
+ new ConstantInstruction(InstructionConstants.OP_LDC, X),
+ new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
+ };
+
+ // (SomeClass)Class.forName(someName).newInstance().
+ private final Instruction[] CLASS_FOR_NAME_CAST_INSTRUCTIONS = new Instruction[]
+ {
+ new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
+ new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 6),
+ new ConstantInstruction(InstructionConstants.OP_CHECKCAST, X),
+ };
+
+
+// private Constant[] DOT_CLASS_JAVAC_CONSTANTS = new Constant[]
+// {
+// new MethodrefConstant(A, 1, null, null),
+// new NameAndTypeConstant(2, 3),
+// new Utf8Constant(ClassConstants.METHOD_NAME_DOT_CLASS_JAVAC),
+// new Utf8Constant(ClassConstants.METHOD_TYPE_DOT_CLASS_JAVAC),
+// };
+
+ private final Constant[] DOT_CLASS_JAVAC_CONSTANTS = new Constant[]
+ {
+ new MethodrefConstant(A, 1, null, null),
+ new NameAndTypeConstant(B, 2),
+ new Utf8Constant(ClassConstants.METHOD_TYPE_DOT_CLASS_JAVAC),
+ };
+
+ // SomeClass.class = class$("SomeClass") (javac).
+ private final Instruction[] DOT_CLASS_JAVAC_INSTRUCTIONS = new Instruction[]
+ {
+ new ConstantInstruction(InstructionConstants.OP_LDC, X),
+ new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
+ };
+
+
+// private Constant[] DOT_CLASS_JIKES_CONSTANTS = new Constant[]
+// {
+// new MethodrefConstant(A, 1, null, null),
+// new NameAndTypeConstant(2, 3),
+// new Utf8Constant(ClassConstants.METHOD_NAME_DOT_CLASS_JIKES),
+// new Utf8Constant(ClassConstants.METHOD_TYPE_DOT_CLASS_JIKES),
+// };
+
+ private final Constant[] DOT_CLASS_JIKES_CONSTANTS = new Constant[]
+ {
+ new MethodrefConstant(A, 1, null, null),
+ new NameAndTypeConstant(B, 2),
+ new Utf8Constant(ClassConstants.METHOD_TYPE_DOT_CLASS_JIKES),
+ };
+
+ // SomeClass.class = class("SomeClass", false) (jikes).
+ private final Instruction[] DOT_CLASS_JIKES_INSTRUCTIONS = new Instruction[]
+ {
+ new ConstantInstruction(InstructionConstants.OP_LDC, X),
+ new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+ new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
+ };
+
+ // return Class.forName(v0).
+ private final Instruction[] DOT_CLASS_JAVAC_IMPLEMENTATION_INSTRUCTIONS = new Instruction[]
+ {
+ new VariableInstruction(InstructionConstants.OP_ALOAD_0),
+ new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
+ new SimpleInstruction(InstructionConstants.OP_ARETURN),
+ };
+
+ // return Class.forName(v0), if (!v1) .getComponentType().
+ private final Instruction[] DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS = new Instruction[]
+ {
+ new VariableInstruction(InstructionConstants.OP_ALOAD_0),
+ new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
+ new VariableInstruction(InstructionConstants.OP_ALOAD_1),
+ new BranchInstruction(InstructionConstants.OP_IFNE, +6),
+ new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 10),
+ new SimpleInstruction(InstructionConstants.OP_ARETURN),
+ };
+
+ // return Class.forName(v0).getComponentType().
+ private final Instruction[] DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS2 = new Instruction[]
+ {
+ new VariableInstruction(InstructionConstants.OP_ALOAD_0),
+ new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
+ new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 10),
+ new SimpleInstruction(InstructionConstants.OP_ARETURN),
+ };
+
+
+ private final ClassPool programClassPool;
+ private final ClassPool libraryClassPool;
+ private final WarningPrinter missingNotePrinter;
+ private final WarningPrinter dependencyWarningPrinter;
+ private final WarningPrinter notePrinter;
+ private final StringMatcher noteExceptionMatcher;
+
+
+ private final InstructionSequenceMatcher constantClassForNameMatcher =
+ new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
+ CONSTANT_CLASS_FOR_NAME_INSTRUCTIONS);
+
+ private final InstructionSequenceMatcher classForNameCastMatcher =
+ new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
+ CLASS_FOR_NAME_CAST_INSTRUCTIONS);
+
+ private final InstructionSequenceMatcher dotClassJavacMatcher =
+ new InstructionSequenceMatcher(DOT_CLASS_JAVAC_CONSTANTS,
+ DOT_CLASS_JAVAC_INSTRUCTIONS);
+
+ private final InstructionSequenceMatcher dotClassJikesMatcher =
+ new InstructionSequenceMatcher(DOT_CLASS_JIKES_CONSTANTS,
+ DOT_CLASS_JIKES_INSTRUCTIONS);
+
+ private final InstructionSequenceMatcher dotClassJavacImplementationMatcher =
+ new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
+ DOT_CLASS_JAVAC_IMPLEMENTATION_INSTRUCTIONS);
+
+ private final InstructionSequenceMatcher dotClassJikesImplementationMatcher =
+ new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
+ DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS);
+
+ private final InstructionSequenceMatcher dotClassJikesImplementationMatcher2 =
+ new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
+ DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS2);
+
+
+ // A field acting as a return variable for the visitors.
+ private boolean isClassForNameInvocation;
+
+
+ /**
+ * Creates a new DynamicClassReferenceInitializer that optionally prints
+ * warnings and notes, with optional class specifications for which never
+ * to print notes.
+ */
+ public DynamicClassReferenceInitializer(ClassPool programClassPool,
+ ClassPool libraryClassPool,
+ WarningPrinter missingNotePrinter,
+ WarningPrinter dependencyWarningPrinter,
+ WarningPrinter notePrinter,
+ StringMatcher noteExceptionMatcher)
+ {
+ this.programClassPool = programClassPool;
+ this.libraryClassPool = libraryClassPool;
+ this.missingNotePrinter = missingNotePrinter;
+ this.dependencyWarningPrinter = dependencyWarningPrinter;
+ this.notePrinter = notePrinter;
+ this.noteExceptionMatcher = noteExceptionMatcher;
+ }
+
+
+ // Implementations for InstructionVisitor.
+
+ public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
+ {
+ // Try to match the (SomeClass)Class.forName(someName).newInstance()
+ // construct. Apply this matcher first, so the next matcher can still
+ // reset it after the first instruction.
+ instruction.accept(clazz, method, codeAttribute, offset,
+ classForNameCastMatcher);
+
+ // Did we find a match?
+ if (classForNameCastMatcher.isMatching())
+ {
+ // Print out a note about the construct.
+ clazz.constantPoolEntryAccept(classForNameCastMatcher.matchedConstantIndex(X), this);
+ }
+
+ // Try to match the Class.forName("SomeClass") construct.
+ instruction.accept(clazz, method, codeAttribute, offset,
+ constantClassForNameMatcher);
+
+ // Did we find a match?
+ if (constantClassForNameMatcher.isMatching())
+ {
+ // Fill out the matched string constant.
+ clazz.constantPoolEntryAccept(constantClassForNameMatcher.matchedConstantIndex(X), this);
+
+ // Don't look for the dynamic construct.
+ classForNameCastMatcher.reset();
+ }
+
+ // Try to match the javac .class construct.
+ instruction.accept(clazz, method, codeAttribute, offset,
+ dotClassJavacMatcher);
+
+ // Did we find a match?
+ if (dotClassJavacMatcher.isMatching() &&
+ isDotClassMethodref(clazz, dotClassJavacMatcher.matchedConstantIndex(0)))
+ {
+ // Fill out the matched string constant.
+ clazz.constantPoolEntryAccept(dotClassJavacMatcher.matchedConstantIndex(X), this);
+ }
+
+ // Try to match the jikes .class construct.
+ instruction.accept(clazz, method, codeAttribute, offset,
+ dotClassJikesMatcher);
+
+ // Did we find a match?
+ if (dotClassJikesMatcher.isMatching() &&
+ isDotClassMethodref(clazz, dotClassJikesMatcher.matchedConstantIndex(0)))
+ {
+ // Fill out the matched string constant.
+ clazz.constantPoolEntryAccept(dotClassJikesMatcher.matchedConstantIndex(X), this);
+ }
+ }
+
+
+ // Implementations for ConstantVisitor.
+
+ /**
+ * Fills out the link to the referenced class.
+ */
+ public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+ {
+ // Save a reference to the corresponding class.
+ String externalClassName = stringConstant.getString(clazz);
+ String internalClassName = ClassUtil.internalClassName(
+ ClassUtil.externalBaseType(externalClassName));
+
+ stringConstant.referencedClass = findClass(clazz.getName(), internalClassName);
+ }
+
+
+ /**
+ * Prints out a note about the class cast to this class, if applicable.
+ */
+ public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+ {
+ // Print out a note about the class cast.
+ if (noteExceptionMatcher == null ||
+ !noteExceptionMatcher.matches(classConstant.getName(clazz)))
+ {
+ notePrinter.print(clazz.getName(),
+ classConstant.getName(clazz),
+ "Note: " +
+ ClassUtil.externalClassName(clazz.getName()) +
+ " calls '(" +
+ ClassUtil.externalClassName(classConstant.getName(clazz)) +
+ ")Class.forName(variable).newInstance()'");
+ }
+ }
+
+
+ /**
+ * Checks whether the referenced method is a .class method.
+ */
+ public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
+ {
+ String methodType = methodrefConstant.getType(clazz);
+
+ // Do the method's class and type match?
+ if (methodType.equals(ClassConstants.METHOD_TYPE_DOT_CLASS_JAVAC) ||
+ methodType.equals(ClassConstants.METHOD_TYPE_DOT_CLASS_JIKES))
+ {
+ String methodName = methodrefConstant.getName(clazz);
+
+ // Does the method's name match one of the special names?
+ isClassForNameInvocation =
+ methodName.equals(ClassConstants.METHOD_NAME_DOT_CLASS_JAVAC) ||
+ methodName.equals(ClassConstants.METHOD_NAME_DOT_CLASS_JIKES);
+
+ if (isClassForNameInvocation)
+ {
+ return;
+ }
+
+ String className = methodrefConstant.getClassName(clazz);
+
+ // Note that we look for the class by name, since the referenced
+ // class has not been initialized yet.
+ Clazz referencedClass = programClassPool.getClass(className);
+ if (referencedClass != null)
+ {
+ // Check if the code of the referenced method is .class code.
+ // Note that we look for the method by name and type, since the
+ // referenced method has not been initialized yet.
+ referencedClass.methodAccept(methodName,
+ methodType,
+ new AllAttributeVisitor(this));
+ }
+ }
+ }
+
+
+ // Implementations for AttributeVisitor.
+
+ public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+ {
+ // Check whether this is class$(String), as generated by javac, or
+ // class(String, boolean), as generated by jikes, or an optimized
+ // version.
+ isClassForNameInvocation =
+ isDotClassMethodCode(clazz, method, codeAttribute,
+ dotClassJavacImplementationMatcher, 5) ||
+ isDotClassMethodCode(clazz, method, codeAttribute,
+ dotClassJikesImplementationMatcher, 12) ||
+ isDotClassMethodCode(clazz, method, codeAttribute,
+ dotClassJikesImplementationMatcher2, 8);
+ }
+
+
+ // Small utility methods.
+
+ /**
+ * Returns whether the given method reference corresponds to a .class
+ * method, as generated by javac or by jikes.
+ */
+ private boolean isDotClassMethodref(Clazz clazz, int methodrefConstantIndex)
+ {
+ isClassForNameInvocation = false;
+
+ // Check if the code of the referenced method is .class code.
+ clazz.constantPoolEntryAccept(methodrefConstantIndex, this);
+
+ return isClassForNameInvocation;
+ }
+
+
+ /**
+ * Returns whether the first whether the first instructions of the
+ * given code attribute match with the given instruction matcher.
+ */
+ private boolean isDotClassMethodCode(Clazz clazz,
+ Method method,
+ CodeAttribute codeAttribute,
+ InstructionSequenceMatcher codeMatcher,
+ int codeLength)
+ {
+ // Check the minimum code length.
+ if (codeAttribute.u4codeLength < codeLength)
+ {
+ return false;
+ }
+
+ // Check the actual instructions.
+ codeMatcher.reset();
+ codeAttribute.instructionsAccept(clazz, method, 0, codeLength, codeMatcher);
+ return codeMatcher.isMatching();
+ }
+
+
+ /**
+ * Returns the class with the given name, either for the program class pool
+ * or from the library class pool, or <code>null</code> if it can't be found.
+ */
+ private Clazz findClass(String referencingClassName, String name)
+ {
+ // Is it an array type?
+ if (ClassUtil.isInternalArrayType(name))
+ {
+ // Ignore any primitive array types.
+ if (!ClassUtil.isInternalClassType(name))
+ {
+ return null;
+ }
+
+ // Strip the array part.
+ name = ClassUtil.internalClassNameFromClassType(name);
+ }
+
+ // First look for the class in the program class pool.
+ Clazz clazz = programClassPool.getClass(name);
+
+ // Otherwise look for the class in the library class pool.
+ if (clazz == null)
+ {
+ clazz = libraryClassPool.getClass(name);
+
+ if (clazz == null &&
+ missingNotePrinter != null)
+ {
+ // We didn't find the superclass or interface. Print a note.
+ missingNotePrinter.print(referencingClassName,
+ name,
+ "Note: " +
+ ClassUtil.externalClassName(referencingClassName) +
+ ": can't find dynamically referenced class " +
+ ClassUtil.externalClassName(name));
+ }
+ }
+ else if (dependencyWarningPrinter != null)
+ {
+ // The superclass or interface was found in the program class pool.
+ // Print a warning.
+ dependencyWarningPrinter.print(referencingClassName,
+ name,
+ "Warning: library class " +
+ ClassUtil.externalClassName(referencingClassName) +
+ " depends dynamically on program class " +
+ ClassUtil.externalClassName(name));
+ }
+
+ return clazz;
+ }
+}