blob: 3807d2e80a27421d2ea6ce5092e4fc9f3a8294e1 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.main;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.main.rels.ClassWrapper;
import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import org.jetbrains.java.decompiler.struct.StructField;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.util.*;
import java.util.Map.Entry;
public class ClassReference14Processor {
public ExitExprent bodyexprent;
public ExitExprent handlerexprent;
public ClassReference14Processor() {
InvocationExprent invfor = new InvocationExprent();
invfor.setName("forName");
invfor.setClassname("java/lang/Class");
invfor.setStringDescriptor("(Ljava/lang/String;)Ljava/lang/Class;");
invfor.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/String;)Ljava/lang/Class;"));
invfor.setStatic(true);
invfor.setLstParameters(Arrays.asList(new Exprent[]{new VarExprent(0, VarType.VARTYPE_STRING, null)}));
bodyexprent = new ExitExprent(ExitExprent.EXIT_RETURN,
invfor,
VarType.VARTYPE_CLASS);
InvocationExprent constr = new InvocationExprent();
constr.setName("<init>");
constr.setClassname("java/lang/NoClassDefFoundError");
constr.setStringDescriptor("()V");
constr.setFunctype(InvocationExprent.TYP_INIT);
constr.setDescriptor(MethodDescriptor.parseDescriptor("()V"));
NewExprent newexpr =
new NewExprent(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/NoClassDefFoundError"), new ArrayList<Exprent>());
newexpr.setConstructor(constr);
InvocationExprent invcause = new InvocationExprent();
invcause.setName("initCause");
invcause.setClassname("java/lang/NoClassDefFoundError");
invcause.setStringDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
invcause.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;"));
invcause.setInstance(newexpr);
invcause.setLstParameters(
Arrays.asList(new Exprent[]{new VarExprent(2, new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"), null)}));
handlerexprent = new ExitExprent(ExitExprent.EXIT_THROW,
invcause,
null);
}
public void processClassReferences(ClassNode node) {
ClassWrapper wrapper = node.wrapper;
// int major_version = wrapper.getClassStruct().major_version;
// int minor_version = wrapper.getClassStruct().minor_version;
//
// if(major_version > 48 || (major_version == 48 && minor_version > 0)) {
// // version 1.5 or above
// return;
// }
if (wrapper.getClassStruct().isVersionGE_1_5()) {
// version 1.5 or above
return;
}
// find the synthetic method Class class$(String) if present
HashMap<ClassWrapper, MethodWrapper> mapClassMeths = new HashMap<ClassWrapper, MethodWrapper>();
mapClassMethods(node, mapClassMeths);
if (mapClassMeths.isEmpty()) {
return;
}
HashSet<ClassWrapper> setFound = new HashSet<ClassWrapper>();
processClassRec(node, mapClassMeths, setFound);
if (!setFound.isEmpty()) {
for (ClassWrapper wrp : setFound) {
StructMethod mt = mapClassMeths.get(wrp).methodStruct;
wrp.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));
}
}
}
private static void processClassRec(ClassNode node,
final HashMap<ClassWrapper, MethodWrapper> mapClassMeths,
final HashSet<ClassWrapper> setFound) {
final ClassWrapper wrapper = node.wrapper;
// search code
for (MethodWrapper meth : wrapper.getMethods()) {
RootStatement root = meth.root;
if (root != null) {
DirectGraph graph = meth.getOrBuildGraph();
graph.iterateExprents(new DirectGraph.ExprentIterator() {
public int processExprent(Exprent exprent) {
for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) {
if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) {
setFound.add(ent.getKey());
}
}
return 0;
}
});
}
}
// search initializers
for (int j = 0; j < 2; j++) {
VBStyleCollection<Exprent, String> initializers =
j == 0 ? wrapper.getStaticFieldInitializers() : wrapper.getDynamicFieldInitializers();
for (int i = 0; i < initializers.size(); i++) {
for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) {
Exprent exprent = initializers.get(i);
if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) {
setFound.add(ent.getKey());
}
String cl = isClass14Invocation(exprent, ent.getKey(), ent.getValue());
if (cl != null) {
initializers.set(i, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/')));
setFound.add(ent.getKey());
}
}
}
}
// iterate nested classes
for (ClassNode nd : node.nested) {
processClassRec(nd, mapClassMeths, setFound);
}
}
private void mapClassMethods(ClassNode node, Map<ClassWrapper, MethodWrapper> map) {
boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
ClassWrapper wrapper = node.wrapper;
for (MethodWrapper method : wrapper.getMethods()) {
StructMethod mt = method.methodStruct;
if ((noSynthFlag || mt.isSynthetic()) &&
mt.getDescriptor().equals("(Ljava/lang/String;)Ljava/lang/Class;") &&
mt.hasModifier(CodeConstants.ACC_STATIC)) {
RootStatement root = method.root;
if (root != null && root.getFirst().type == Statement.TYPE_TRYCATCH) {
CatchStatement cst = (CatchStatement)root.getFirst();
if (cst.getStats().size() == 2 && cst.getFirst().type == Statement.TYPE_BASICBLOCK &&
cst.getStats().get(1).type == Statement.TYPE_BASICBLOCK &&
cst.getVars().get(0).getVartype().equals(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"))) {
BasicBlockStatement body = (BasicBlockStatement)cst.getFirst();
BasicBlockStatement handler = (BasicBlockStatement)cst.getStats().get(1);
if (body.getExprents().size() == 1 && handler.getExprents().size() == 1) {
if (bodyexprent.equals(body.getExprents().get(0)) &&
handlerexprent.equals(handler.getExprents().get(0))) {
map.put(wrapper, method);
break;
}
}
}
}
}
}
// iterate nested classes
for (ClassNode nd : node.nested) {
mapClassMethods(nd, map);
}
}
private static boolean replaceInvocations(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) {
boolean res = false;
while (true) {
boolean found = false;
for (Exprent expr : exprent.getAllExprents()) {
String cl = isClass14Invocation(expr, wrapper, meth);
if (cl != null) {
exprent.replaceExprent(expr, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/')));
found = true;
res = true;
break;
}
res |= replaceInvocations(expr, wrapper, meth);
}
if (!found) {
break;
}
}
return res;
}
private static String isClass14Invocation(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) {
if (exprent.type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent fexpr = (FunctionExprent)exprent;
if (fexpr.getFunctype() == FunctionExprent.FUNCTION_IIF) {
if (fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent headexpr = (FunctionExprent)fexpr.getLstOperands().get(0);
if (headexpr.getFunctype() == FunctionExprent.FUNCTION_EQ) {
if (headexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD &&
headexpr.getLstOperands().get(1).type == Exprent.EXPRENT_CONST &&
((ConstExprent)headexpr.getLstOperands().get(1)).getConsttype().equals(VarType.VARTYPE_NULL)) {
FieldExprent field = (FieldExprent)headexpr.getLstOperands().get(0);
ClassNode fieldnode = DecompilerContext.getClassProcessor().getMapRootClasses().get(field.getClassname());
if (fieldnode != null && fieldnode.classStruct.qualifiedName.equals(wrapper.getClassStruct().qualifiedName)) { // source class
StructField fd =
wrapper.getClassStruct().getField(field.getName(), field.getDescriptor().descriptorString); // FIXME: can be null! why??
if (fd != null && fd.hasModifier(CodeConstants.ACC_STATIC) &&
(fd.isSynthetic() || DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET))) {
if (fexpr.getLstOperands().get(1).type == Exprent.EXPRENT_ASSIGNMENT && fexpr.getLstOperands().get(2).equals(field)) {
AssignmentExprent asexpr = (AssignmentExprent)fexpr.getLstOperands().get(1);
if (asexpr.getLeft().equals(field) && asexpr.getRight().type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent invexpr = (InvocationExprent)asexpr.getRight();
if (invexpr.getClassname().equals(wrapper.getClassStruct().qualifiedName) &&
invexpr.getName().equals(meth.methodStruct.getName()) &&
invexpr.getStringDescriptor().equals(meth.methodStruct.getDescriptor())) {
if (invexpr.getLstParameters().get(0).type == Exprent.EXPRENT_CONST) {
wrapper.getHiddenMembers()
.add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); // hide synthetic field
return ((ConstExprent)invexpr.getLstParameters().get(0)).getValue().toString();
}
}
}
}
}
}
}
}
}
}
}
return null;
}
}