java-decompiler: post-import cleanup (formatting and copyright)

This commit is contained in:
Roman Shevchenko
2014-08-28 21:34:14 +04:00
parent 663631f045
commit 076e4393f2
355 changed files with 38995 additions and 36094 deletions

View File

@@ -1,23 +1,20 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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 java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
@@ -27,295 +24,292 @@ import org.jetbrains.java.decompiler.main.rels.ClassWrapper;
import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
import org.jetbrains.java.decompiler.modules.decompiler.SecondaryFunctionsHelper;
import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
import org.jetbrains.java.decompiler.modules.decompiler.exps.AssertExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent;
import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
import org.jetbrains.java.decompiler.struct.StructField;
import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class AssertProcessor {
private static final VarType CLASS_ASSERTION_ERROR = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/AssertionError");
public static void buildAssertions(ClassNode node) {
private static final VarType CLASS_ASSERTION_ERROR = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/AssertionError");
ClassWrapper wrapper = node.wrapper;
public static void buildAssertions(ClassNode node) {
StructField field = findAssertionField(node);
if(field != null) {
ClassWrapper wrapper = node.wrapper;
String key = InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor());
boolean res = false;
for(MethodWrapper meth : wrapper.getMethods()) {
RootStatement root = meth.root;
if(root != null) {
res |= replaceAssertions(root, wrapper.getClassStruct().qualifiedName, key);
}
}
if(res) {
// hide the helper field
wrapper.getHideMembers().add(key);
}
}
}
private static StructField findAssertionField(ClassNode node) {
ClassWrapper wrapper = node.wrapper;
boolean nosynthflag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
StructField field = findAssertionField(node);
for(StructField fd: wrapper.getClassStruct().getFields()) {
if (field != null) {
String keyField = InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor());
// initializer exists
if(wrapper.getStaticFieldInitializers().containsKey(keyField)) {
String key = InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor());
int flags = fd.access_flags;
boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic");
boolean res = false;
// access flags set
if((flags & CodeConstants.ACC_STATIC) != 0 && (flags & CodeConstants.ACC_FINAL) != 0 &&
(isSynthetic || nosynthflag)) {
for (MethodWrapper meth : wrapper.getMethods()) {
RootStatement root = meth.root;
if (root != null) {
res |= replaceAssertions(root, wrapper.getClassStruct().qualifiedName, key);
}
}
// field type boolean
FieldDescriptor fdescr = FieldDescriptor.parseDescriptor(fd.getDescriptor());
if(VarType.VARTYPE_BOOLEAN.equals(fdescr.type)) {
if (res) {
// hide the helper field
wrapper.getHideMembers().add(key);
}
}
}
Exprent initializer = wrapper.getStaticFieldInitializers().getWithKey(keyField);
if(initializer.type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent fexpr = (FunctionExprent)initializer;
if(fexpr.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT &&
fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent invexpr = (InvocationExprent)fexpr.getLstOperands().get(0);
if(invexpr.getInstance() != null && invexpr.getInstance().type == Exprent.EXPRENT_CONST && "desiredAssertionStatus".equals(invexpr.getName())
&& "java/lang/Class".equals(invexpr.getClassname()) && invexpr.getLstParameters().isEmpty()) {
ConstExprent cexpr = (ConstExprent)invexpr.getInstance();
if(VarType.VARTYPE_CLASS.equals(cexpr.getConsttype())) {
ClassNode nd = node;
while(nd != null) {
if(nd.wrapper.getClassStruct().qualifiedName.equals(cexpr.getValue())) {
break;
}
nd = nd.parent;
}
if(nd != null) { // found enclosing class with the same name
return fd;
}
}
}
}
}
}
}
}
}
return null;
}
private static boolean replaceAssertions(Statement statement, String classname, String key) {
private static StructField findAssertionField(ClassNode node) {
boolean res = false;
for(Statement st : statement.getStats()) {
res |= replaceAssertions(st, classname, key);
}
boolean replaced = true;
while(replaced) {
replaced = false;
for(Statement st : statement.getStats()) {
if(st.type == Statement.TYPE_IF) {
if(replaceAssertion(statement, (IfStatement)st, classname, key)) {
replaced = true;
break;
}
}
}
res |= replaced;
}
return res;
}
private static boolean replaceAssertion(Statement parent, IfStatement stat, String classname, String key) {
Statement ifstat = stat.getIfstat();
InvocationExprent throwError = isAssertionError(ifstat);
if(throwError == null) {
return false;
}
Object[] exprres = getAssertionExprent(stat.getHeadexprent().getCondition().copy(), classname, key);
if(!(Boolean)exprres[1]) {
return false;
}
List<Exprent> lstParams = new ArrayList<Exprent>();
Exprent ascond = null, retcond = null;
if(exprres[0] != null) {
ascond = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT,
Arrays.asList(new Exprent[]{(Exprent)exprres[0]}));
retcond = SecondaryFunctionsHelper.propagateBoolNot(ascond);
}
lstParams.add(retcond==null?ascond:retcond);
if(!throwError.getLstParameters().isEmpty()) {
lstParams.add(throwError.getLstParameters().get(0));
}
AssertExprent asexpr = new AssertExprent(lstParams);
Statement newstat = new BasicBlockStatement(new BasicBlock(
DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
newstat.setExprents(Arrays.asList(new Exprent[] {asexpr}));
ClassWrapper wrapper = node.wrapper;
Statement first = stat.getFirst();
if(stat.iftype == IfStatement.IFTYPE_IFELSE || (first.getExprents() != null &&
!first.getExprents().isEmpty())) {
boolean nosynthflag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
first.removeSuccessor(stat.getIfEdge());
first.removeSuccessor(stat.getElseEdge());
List<Statement> lstStatements = new ArrayList<Statement>();
if(first.getExprents() != null && !first.getExprents().isEmpty()) {
lstStatements.add(first);
}
lstStatements.add(newstat);
if(stat.iftype == IfStatement.IFTYPE_IFELSE) {
lstStatements.add(stat.getElsestat());
}
SequenceStatement sequence = new SequenceStatement(lstStatements);
sequence.setAllParent();
for (StructField fd : wrapper.getClassStruct().getFields()) {
for(int i=0;i<sequence.getStats().size()-1;i++) {
sequence.getStats().get(i).addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR,
sequence.getStats().get(i), sequence.getStats().get(i+1)));
}
if(stat.iftype == IfStatement.IFTYPE_IFELSE) {
Statement ifelse = stat.getElsestat();
List<StatEdge> lstSuccs = ifelse.getAllSuccessorEdges();
if(!lstSuccs.isEmpty()) {
StatEdge endedge = lstSuccs.get(0);
if(endedge.closure == stat) {
sequence.addLabeledEdge(endedge);
}
}
}
newstat = sequence;
}
newstat.getVarDefinitions().addAll(stat.getVarDefinitions());
parent.replaceStatement(stat, newstat);
return true;
}
private static InvocationExprent isAssertionError(Statement stat) {
if(stat == null || stat.getExprents() == null || stat.getExprents().size() !=1) {
return null;
}
String keyField = InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor());
Exprent expr = stat.getExprents().get(0);
if(expr.type == Exprent.EXPRENT_EXIT) {
ExitExprent exexpr = (ExitExprent)expr;
if(exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_NEW) {
NewExprent nexpr = (NewExprent)exexpr.getValue();
if(CLASS_ASSERTION_ERROR.equals(nexpr.getNewtype()) && nexpr.getConstructor() != null) {
return nexpr.getConstructor();
}
}
}
return null;
}
private static Object[] getAssertionExprent(Exprent exprent, String classname, String key) {
if(exprent.type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent fexpr = (FunctionExprent)exprent;
if(fexpr.getFunctype() == FunctionExprent.FUNCTION_CADD) {
for(int i=0;i<2;i++) {
Exprent param = fexpr.getLstOperands().get(i);
if(isAssertionField(param, classname, key)) {
return new Object[] {fexpr.getLstOperands().get(1-i), true};
}
}
for(int i=0;i<2;i++) {
Exprent param = fexpr.getLstOperands().get(i);
Object[] res = getAssertionExprent(param, classname, key);
if((Boolean)res[1]) {
if(param != res[0]) {
fexpr.getLstOperands().set(i, (Exprent)res[0]);
}
return new Object[] {fexpr, true};
}
}
} else if(isAssertionField(fexpr, classname, key)) {
// assert false;
return new Object[] {null, true};
}
}
return new Object[] {exprent, false};
}
private static boolean isAssertionField(Exprent exprent, String classname, String key) {
if(exprent.type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent fparam = (FunctionExprent)exprent;
if(fparam.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT &&
fparam.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) {
FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0);
if(classname.equals(fdparam.getClassname())
&& key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString))) {
return true;
}
}
}
// initializer exists
if (wrapper.getStaticFieldInitializers().containsKey(keyField)) {
return false;
}
int flags = fd.access_flags;
boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic");
// access flags set
if ((flags & CodeConstants.ACC_STATIC) != 0 && (flags & CodeConstants.ACC_FINAL) != 0 &&
(isSynthetic || nosynthflag)) {
// field type boolean
FieldDescriptor fdescr = FieldDescriptor.parseDescriptor(fd.getDescriptor());
if (VarType.VARTYPE_BOOLEAN.equals(fdescr.type)) {
Exprent initializer = wrapper.getStaticFieldInitializers().getWithKey(keyField);
if (initializer.type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent fexpr = (FunctionExprent)initializer;
if (fexpr.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT &&
fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent invexpr = (InvocationExprent)fexpr.getLstOperands().get(0);
if (invexpr.getInstance() != null &&
invexpr.getInstance().type == Exprent.EXPRENT_CONST &&
"desiredAssertionStatus".equals(invexpr.getName())
&&
"java/lang/Class".equals(invexpr.getClassname()) &&
invexpr.getLstParameters().isEmpty()) {
ConstExprent cexpr = (ConstExprent)invexpr.getInstance();
if (VarType.VARTYPE_CLASS.equals(cexpr.getConsttype())) {
ClassNode nd = node;
while (nd != null) {
if (nd.wrapper.getClassStruct().qualifiedName.equals(cexpr.getValue())) {
break;
}
nd = nd.parent;
}
if (nd != null) { // found enclosing class with the same name
return fd;
}
}
}
}
}
}
}
}
}
return null;
}
private static boolean replaceAssertions(Statement statement, String classname, String key) {
boolean res = false;
for (Statement st : statement.getStats()) {
res |= replaceAssertions(st, classname, key);
}
boolean replaced = true;
while (replaced) {
replaced = false;
for (Statement st : statement.getStats()) {
if (st.type == Statement.TYPE_IF) {
if (replaceAssertion(statement, (IfStatement)st, classname, key)) {
replaced = true;
break;
}
}
}
res |= replaced;
}
return res;
}
private static boolean replaceAssertion(Statement parent, IfStatement stat, String classname, String key) {
Statement ifstat = stat.getIfstat();
InvocationExprent throwError = isAssertionError(ifstat);
if (throwError == null) {
return false;
}
Object[] exprres = getAssertionExprent(stat.getHeadexprent().getCondition().copy(), classname, key);
if (!(Boolean)exprres[1]) {
return false;
}
List<Exprent> lstParams = new ArrayList<Exprent>();
Exprent ascond = null, retcond = null;
if (exprres[0] != null) {
ascond = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT,
Arrays.asList(new Exprent[]{(Exprent)exprres[0]}));
retcond = SecondaryFunctionsHelper.propagateBoolNot(ascond);
}
lstParams.add(retcond == null ? ascond : retcond);
if (!throwError.getLstParameters().isEmpty()) {
lstParams.add(throwError.getLstParameters().get(0));
}
AssertExprent asexpr = new AssertExprent(lstParams);
Statement newstat = new BasicBlockStatement(new BasicBlock(
DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
newstat.setExprents(Arrays.asList(new Exprent[]{asexpr}));
Statement first = stat.getFirst();
if (stat.iftype == IfStatement.IFTYPE_IFELSE || (first.getExprents() != null &&
!first.getExprents().isEmpty())) {
first.removeSuccessor(stat.getIfEdge());
first.removeSuccessor(stat.getElseEdge());
List<Statement> lstStatements = new ArrayList<Statement>();
if (first.getExprents() != null && !first.getExprents().isEmpty()) {
lstStatements.add(first);
}
lstStatements.add(newstat);
if (stat.iftype == IfStatement.IFTYPE_IFELSE) {
lstStatements.add(stat.getElsestat());
}
SequenceStatement sequence = new SequenceStatement(lstStatements);
sequence.setAllParent();
for (int i = 0; i < sequence.getStats().size() - 1; i++) {
sequence.getStats().get(i).addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR,
sequence.getStats().get(i), sequence.getStats().get(i + 1)));
}
if (stat.iftype == IfStatement.IFTYPE_IFELSE) {
Statement ifelse = stat.getElsestat();
List<StatEdge> lstSuccs = ifelse.getAllSuccessorEdges();
if (!lstSuccs.isEmpty()) {
StatEdge endedge = lstSuccs.get(0);
if (endedge.closure == stat) {
sequence.addLabeledEdge(endedge);
}
}
}
newstat = sequence;
}
newstat.getVarDefinitions().addAll(stat.getVarDefinitions());
parent.replaceStatement(stat, newstat);
return true;
}
private static InvocationExprent isAssertionError(Statement stat) {
if (stat == null || stat.getExprents() == null || stat.getExprents().size() != 1) {
return null;
}
Exprent expr = stat.getExprents().get(0);
if (expr.type == Exprent.EXPRENT_EXIT) {
ExitExprent exexpr = (ExitExprent)expr;
if (exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_NEW) {
NewExprent nexpr = (NewExprent)exexpr.getValue();
if (CLASS_ASSERTION_ERROR.equals(nexpr.getNewtype()) && nexpr.getConstructor() != null) {
return nexpr.getConstructor();
}
}
}
return null;
}
private static Object[] getAssertionExprent(Exprent exprent, String classname, String key) {
if (exprent.type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent fexpr = (FunctionExprent)exprent;
if (fexpr.getFunctype() == FunctionExprent.FUNCTION_CADD) {
for (int i = 0; i < 2; i++) {
Exprent param = fexpr.getLstOperands().get(i);
if (isAssertionField(param, classname, key)) {
return new Object[]{fexpr.getLstOperands().get(1 - i), true};
}
}
for (int i = 0; i < 2; i++) {
Exprent param = fexpr.getLstOperands().get(i);
Object[] res = getAssertionExprent(param, classname, key);
if ((Boolean)res[1]) {
if (param != res[0]) {
fexpr.getLstOperands().set(i, (Exprent)res[0]);
}
return new Object[]{fexpr, true};
}
}
}
else if (isAssertionField(fexpr, classname, key)) {
// assert false;
return new Object[]{null, true};
}
}
return new Object[]{exprent, false};
}
private static boolean isAssertionField(Exprent exprent, String classname, String key) {
if (exprent.type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent fparam = (FunctionExprent)exprent;
if (fparam.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT &&
fparam.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) {
FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0);
if (classname.equals(fdparam.getClassname())
&& key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString))) {
return true;
}
}
}
return false;
}
}

View File

@@ -1,39 +1,26 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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 java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
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.AssignmentExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
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;
@@ -46,264 +33,272 @@ 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.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
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);
public ExitExprent bodyexprent;
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;
// }
public ExitExprent handlerexprent;
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>();
findClassMethod(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.getHideMembers().add(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));
}
}
}
private 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()) {
public ClassReference14Processor() {
RootStatement root = meth.root;
if(root != null) {
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)}));
DirectGraph graph = meth.getOrBuildGraph();
bodyexprent = new ExitExprent(ExitExprent.EXIT_RETURN,
invfor,
VarType.VARTYPE_CLASS);
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());
}
}
}
}
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"));
// iterate nested classes
for(ClassNode nd : node.nested) {
processClassRec(nd, mapClassMeths, setFound);
}
}
private void findClassMethod(ClassNode node, HashMap<ClassWrapper, MethodWrapper> mapClassMeths) {
boolean nosynthflag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
ClassWrapper wrapper = node.wrapper;
for(MethodWrapper meth : wrapper.getMethods()) {
StructMethod mt = meth.methodStruct;
if(((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic")
|| nosynthflag) &&
mt.getDescriptor().equals("(Ljava/lang/String;)Ljava/lang/Class;") &&
(mt.getAccessFlags() & CodeConstants.ACC_STATIC) != 0) {
RootStatement root = meth.root;
if(root != null) {
if(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))) {
mapClassMeths.put(wrapper, meth);
break;
}
}
}
}
}
}
}
// iterate nested classes
for(ClassNode nd : node.nested) {
findClassMethod(nd, mapClassMeths);
}
}
private boolean replaceInvocations(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) {
boolean res = false;
for(;;) {
NewExprent newexpr =
new NewExprent(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/NoClassDefFoundError"), new ArrayList<Exprent>());
newexpr.setConstructor(constr);
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 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)) {
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)}));
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.access_flags & CodeConstants.ACC_STATIC) != 0 &&
((fd.access_flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic")
|| 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.getHideMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); // hide synthetic field
return ((ConstExprent)invexpr.getLstParameters().get(0)).getValue().toString();
}
}
}
}
}
}
}
}
}
}
}
return 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>();
findClassMethod(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.getHideMembers().add(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));
}
}
}
private 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 findClassMethod(ClassNode node, HashMap<ClassWrapper, MethodWrapper> mapClassMeths) {
boolean nosynthflag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
ClassWrapper wrapper = node.wrapper;
for (MethodWrapper meth : wrapper.getMethods()) {
StructMethod mt = meth.methodStruct;
if (((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic")
|| nosynthflag) &&
mt.getDescriptor().equals("(Ljava/lang/String;)Ljava/lang/Class;") &&
(mt.getAccessFlags() & CodeConstants.ACC_STATIC) != 0) {
RootStatement root = meth.root;
if (root != null) {
if (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))) {
mapClassMeths.put(wrapper, meth);
break;
}
}
}
}
}
}
}
// iterate nested classes
for (ClassNode nd : node.nested) {
findClassMethod(nd, mapClassMeths);
}
}
private boolean replaceInvocations(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) {
boolean res = false;
for (; ; ) {
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 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.access_flags & CodeConstants.ACC_STATIC) != 0 &&
((fd.access_flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic")
|| 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.getHideMembers()
.add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); // hide synthetic field
return ((ConstExprent)invexpr.getLstParameters().get(0)).getValue().toString();
}
}
}
}
}
}
}
}
}
}
}
return null;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +1,20 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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 java.io.BufferedWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.main.collectors.ImportCollector;
@@ -46,242 +34,254 @@ import org.jetbrains.java.decompiler.struct.attr.StructInnerClassesAttribute;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
public class ClassesProcessor {
private HashMap<String, ClassNode> mapRootClasses = new HashMap<String, ClassNode>();
public ClassesProcessor(StructContext context) {
HashMap<String, Object[]> mapInnerClasses = new HashMap<String, Object[]>();
HashMap<String, HashSet<String>> mapNestedClassReferences = new HashMap<String, HashSet<String>>();
HashMap<String, HashSet<String>> mapEnclosingClassReferences = new HashMap<String, HashSet<String>>();
HashMap<String, String> mapNewSimpleNames = new HashMap<String, String>();
boolean bDecompileInner = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_INNER);
// create class nodes
for(StructClass cl: context.getClasses().values()) {
if(cl.isOwn() && !mapRootClasses.containsKey(cl.qualifiedName)) {
if(bDecompileInner) {
StructInnerClassesAttribute inner = (StructInnerClassesAttribute)cl.getAttributes().getWithKey("InnerClasses");
if(inner != null) {
private HashMap<String, ClassNode> mapRootClasses = new HashMap<String, ClassNode>();
for(int i=0;i<inner.getClassentries().size();i++) {
int[] entry = inner.getClassentries().get(i);
String[] strentry = inner.getStringentries().get(i);
Object[] arr = new Object[4]; // arr[0] not used
public ClassesProcessor(StructContext context) {
String innername = strentry[0];
HashMap<String, Object[]> mapInnerClasses = new HashMap<String, Object[]>();
// nested class type
arr[2] = entry[1] == 0?(entry[2]==0?ClassNode.CLASS_ANONYMOUS:ClassNode.CLASS_LOCAL):ClassNode.CLASS_MEMBER;
HashMap<String, HashSet<String>> mapNestedClassReferences = new HashMap<String, HashSet<String>>();
HashMap<String, HashSet<String>> mapEnclosingClassReferences = new HashMap<String, HashSet<String>>();
// original simple name
String simpleName = strentry[2];
String savedName = mapNewSimpleNames.get(innername);
if(savedName != null) {
simpleName = savedName;
} else if(simpleName != null && DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) {
IIdentifierRenamer renamer = DecompilerContext.getPoolInterceptor().getHelper();
if(renamer.toBeRenamed(IIdentifierRenamer.ELEMENT_CLASS, simpleName, null, null)) {
simpleName = renamer.getNextClassname(innername, simpleName);
mapNewSimpleNames.put(innername, simpleName);
}
}
arr[1] = simpleName;
HashMap<String, String> mapNewSimpleNames = new HashMap<String, String>();
// original access flags
arr[3] = entry[3];
boolean bDecompileInner = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_INNER);
// enclosing class
String enclClassName = null;
if(entry[1] != 0) {
enclClassName = strentry[1];
} else {
enclClassName = cl.qualifiedName;
}
// create class nodes
for (StructClass cl : context.getClasses().values()) {
if (cl.isOwn() && !mapRootClasses.containsKey(cl.qualifiedName)) {
if(!innername.equals(enclClassName)) { // self reference
StructClass enclosing_class = context.getClasses().get(enclClassName);
if(enclosing_class != null && enclosing_class.isOwn()) { // own classes only
Object[] arrold = mapInnerClasses.get(innername);
if(arrold == null) {
mapInnerClasses.put(innername, arr);
} else {
if(!InterpreterUtil.equalObjectArrays(arrold, arr)){
DecompilerContext.getLogger().writeMessage("Inconsistent inner class entries for "+innername+"!", IFernflowerLogger.WARNING);
}
}
// reference to the nested class
HashSet<String> set = mapNestedClassReferences.get(enclClassName);
if(set == null) {
mapNestedClassReferences.put(enclClassName, set = new HashSet<String>());
}
set.add(innername);
if (bDecompileInner) {
StructInnerClassesAttribute inner = (StructInnerClassesAttribute)cl.getAttributes().getWithKey("InnerClasses");
if (inner != null) {
// reference to the enclosing class
set = mapEnclosingClassReferences.get(innername);
if(set == null) {
mapEnclosingClassReferences.put(innername, set = new HashSet<String>());
}
set.add(enclClassName);
}
}
}
}
}
ClassNode node = new ClassNode(ClassNode.CLASS_ROOT, cl);
node.access = cl.access_flags;
mapRootClasses.put(cl.qualifiedName, node);
}
}
if(bDecompileInner) {
// connect nested classes
for(Entry<String, ClassNode> ent: mapRootClasses.entrySet()) {
// root class?
if(!mapInnerClasses.containsKey(ent.getKey())) {
HashSet<String> setVisited = new HashSet<String>();
LinkedList<String> stack = new LinkedList<String>();
stack.add(ent.getKey());
setVisited.add(ent.getKey());
while(!stack.isEmpty()) {
String superClass = stack.removeFirst();
ClassNode supernode = mapRootClasses.get(superClass);
for (int i = 0; i < inner.getClassentries().size(); i++) {
HashSet<String> setNestedClasses = mapNestedClassReferences.get(superClass);
if(setNestedClasses != null) {
int[] entry = inner.getClassentries().get(i);
String[] strentry = inner.getStringentries().get(i);
StructClass scl = supernode.classStruct;
StructInnerClassesAttribute inner = (StructInnerClassesAttribute) scl.getAttributes().getWithKey("InnerClasses");
for(int i = 0; i < inner.getStringentries().size(); i++) {
String nestedClass = inner.getStringentries().get(i)[0];
if (!setNestedClasses.contains(nestedClass)) {
continue;
}
if(setVisited.contains(nestedClass)) {
continue;
}
setVisited.add(nestedClass);
ClassNode nestednode = mapRootClasses.get(nestedClass);
if(nestednode == null) {
DecompilerContext.getLogger().writeMessage("Nested class "+nestedClass+" missing!", IFernflowerLogger.WARNING);
continue;
}
Object[] arr = new Object[4]; // arr[0] not used
Object[] arr = mapInnerClasses.get(nestedClass);
if((Integer)arr[2] == ClassNode.CLASS_MEMBER) {
// FIXME: check for consistent naming
}
nestednode.type = (Integer)arr[2];
nestednode.simpleName = (String)arr[1];
nestednode.access = (Integer)arr[3];
String innername = strentry[0];
if(nestednode.type == ClassNode.CLASS_ANONYMOUS) {
StructClass cl = nestednode.classStruct;
// remove static if anonymous class
// a common compiler bug
nestednode.access &= ~CodeConstants.ACC_STATIC;
int[] interfaces = cl.getInterfaces();
if(interfaces.length > 0) {
if(interfaces.length > 1) {
DecompilerContext.getLogger().writeMessage("Inconsistent anonymous class definition: "+cl.qualifiedName, IFernflowerLogger.WARNING);
}
nestednode.anonimousClassType = new VarType(cl.getInterface(0), true);
} else {
nestednode.anonimousClassType = new VarType(cl.superClass.getString(), true);
}
} else if(nestednode.type == ClassNode.CLASS_LOCAL) {
// only abstract and final are permitted
// a common compiler bug
nestednode.access &= (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_FINAL);
}
supernode.nested.add(nestednode);
nestednode.parent = supernode;
nestednode.enclosingClasses.addAll(mapEnclosingClassReferences.get(nestedClass));
stack.add(nestedClass);
}
}
}
}
}
}
}
public void writeClass(StructContext context, StructClass cl, BufferedWriter outwriter) throws IOException {
ClassNode root = mapRootClasses.get(cl.qualifiedName);
if(root.type != ClassNode.CLASS_ROOT) {
return;
}
try {
DecompilerContext.setImpcollector(new ImportCollector(root));
DecompilerContext.setCountercontainer(new CounterContainer());
// lambda processing
LambdaProcessor lambda_proc = new LambdaProcessor();
lambda_proc.processClass(root);
// add simple class names to implicit import
addClassnameToImport(root, DecompilerContext.getImpcollector());
// build wrappers for all nested classes
// that's where the actual processing takes place
initWrappers(root);
NestedClassProcessor nestedproc = new NestedClassProcessor();
nestedproc.processClass(root, root);
NestedMemberAccess nstmember = new NestedMemberAccess();
nstmember.propagateMemberAccess(root);
ClassWriter clwriter = new ClassWriter();
StringWriter strwriter = new StringWriter();
clwriter.classToJava(root, new BufferedWriter(strwriter), 0);
if(DecompilerContext.getOption(IFernflowerPreferences.OUTPUT_COPYRIGHT_COMMENT)) {
outwriter.write("// Decompiled by: Fernflower "+Fernflower.version);
outwriter.write(DecompilerContext.getNewLineSeparator());
outwriter.write("// Date: "+new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(new Date()));
outwriter.write(DecompilerContext.getNewLineSeparator());
outwriter.write("// Copyright: 2008-2010, Stiver");
outwriter.write(DecompilerContext.getNewLineSeparator());
outwriter.write("// Home page: http://www.reversed-java.com");
outwriter.write(DecompilerContext.getNewLineSeparator());
outwriter.write(DecompilerContext.getNewLineSeparator());
}
int index = cl.qualifiedName.lastIndexOf("/");
if(index >= 0) {
// nested class type
arr[2] = entry[1] == 0 ? (entry[2] == 0 ? ClassNode.CLASS_ANONYMOUS : ClassNode.CLASS_LOCAL) : ClassNode.CLASS_MEMBER;
// original simple name
String simpleName = strentry[2];
String savedName = mapNewSimpleNames.get(innername);
if (savedName != null) {
simpleName = savedName;
}
else if (simpleName != null && DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) {
IIdentifierRenamer renamer = DecompilerContext.getPoolInterceptor().getHelper();
if (renamer.toBeRenamed(IIdentifierRenamer.ELEMENT_CLASS, simpleName, null, null)) {
simpleName = renamer.getNextClassname(innername, simpleName);
mapNewSimpleNames.put(innername, simpleName);
}
}
arr[1] = simpleName;
// original access flags
arr[3] = entry[3];
// enclosing class
String enclClassName = null;
if (entry[1] != 0) {
enclClassName = strentry[1];
}
else {
enclClassName = cl.qualifiedName;
}
if (!innername.equals(enclClassName)) { // self reference
StructClass enclosing_class = context.getClasses().get(enclClassName);
if (enclosing_class != null && enclosing_class.isOwn()) { // own classes only
Object[] arrold = mapInnerClasses.get(innername);
if (arrold == null) {
mapInnerClasses.put(innername, arr);
}
else {
if (!InterpreterUtil.equalObjectArrays(arrold, arr)) {
DecompilerContext.getLogger()
.writeMessage("Inconsistent inner class entries for " + innername + "!", IFernflowerLogger.WARNING);
}
}
// reference to the nested class
HashSet<String> set = mapNestedClassReferences.get(enclClassName);
if (set == null) {
mapNestedClassReferences.put(enclClassName, set = new HashSet<String>());
}
set.add(innername);
// reference to the enclosing class
set = mapEnclosingClassReferences.get(innername);
if (set == null) {
mapEnclosingClassReferences.put(innername, set = new HashSet<String>());
}
set.add(enclClassName);
}
}
}
}
}
ClassNode node = new ClassNode(ClassNode.CLASS_ROOT, cl);
node.access = cl.access_flags;
mapRootClasses.put(cl.qualifiedName, node);
}
}
if (bDecompileInner) {
// connect nested classes
for (Entry<String, ClassNode> ent : mapRootClasses.entrySet()) {
// root class?
if (!mapInnerClasses.containsKey(ent.getKey())) {
HashSet<String> setVisited = new HashSet<String>();
LinkedList<String> stack = new LinkedList<String>();
stack.add(ent.getKey());
setVisited.add(ent.getKey());
while (!stack.isEmpty()) {
String superClass = stack.removeFirst();
ClassNode supernode = mapRootClasses.get(superClass);
HashSet<String> setNestedClasses = mapNestedClassReferences.get(superClass);
if (setNestedClasses != null) {
StructClass scl = supernode.classStruct;
StructInnerClassesAttribute inner = (StructInnerClassesAttribute)scl.getAttributes().getWithKey("InnerClasses");
for (int i = 0; i < inner.getStringentries().size(); i++) {
String nestedClass = inner.getStringentries().get(i)[0];
if (!setNestedClasses.contains(nestedClass)) {
continue;
}
if (setVisited.contains(nestedClass)) {
continue;
}
setVisited.add(nestedClass);
ClassNode nestednode = mapRootClasses.get(nestedClass);
if (nestednode == null) {
DecompilerContext.getLogger().writeMessage("Nested class " + nestedClass + " missing!", IFernflowerLogger.WARNING);
continue;
}
Object[] arr = mapInnerClasses.get(nestedClass);
if ((Integer)arr[2] == ClassNode.CLASS_MEMBER) {
// FIXME: check for consistent naming
}
nestednode.type = (Integer)arr[2];
nestednode.simpleName = (String)arr[1];
nestednode.access = (Integer)arr[3];
if (nestednode.type == ClassNode.CLASS_ANONYMOUS) {
StructClass cl = nestednode.classStruct;
// remove static if anonymous class
// a common compiler bug
nestednode.access &= ~CodeConstants.ACC_STATIC;
int[] interfaces = cl.getInterfaces();
if (interfaces.length > 0) {
if (interfaces.length > 1) {
DecompilerContext.getLogger()
.writeMessage("Inconsistent anonymous class definition: " + cl.qualifiedName, IFernflowerLogger.WARNING);
}
nestednode.anonimousClassType = new VarType(cl.getInterface(0), true);
}
else {
nestednode.anonimousClassType = new VarType(cl.superClass.getString(), true);
}
}
else if (nestednode.type == ClassNode.CLASS_LOCAL) {
// only abstract and final are permitted
// a common compiler bug
nestednode.access &= (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_FINAL);
}
supernode.nested.add(nestednode);
nestednode.parent = supernode;
nestednode.enclosingClasses.addAll(mapEnclosingClassReferences.get(nestedClass));
stack.add(nestedClass);
}
}
}
}
}
}
}
public void writeClass(StructContext context, StructClass cl, BufferedWriter outwriter) throws IOException {
ClassNode root = mapRootClasses.get(cl.qualifiedName);
if (root.type != ClassNode.CLASS_ROOT) {
return;
}
try {
DecompilerContext.setImpcollector(new ImportCollector(root));
DecompilerContext.setCountercontainer(new CounterContainer());
// lambda processing
LambdaProcessor lambda_proc = new LambdaProcessor();
lambda_proc.processClass(root);
// add simple class names to implicit import
addClassnameToImport(root, DecompilerContext.getImpcollector());
// build wrappers for all nested classes
// that's where the actual processing takes place
initWrappers(root);
NestedClassProcessor nestedproc = new NestedClassProcessor();
nestedproc.processClass(root, root);
NestedMemberAccess nstmember = new NestedMemberAccess();
nstmember.propagateMemberAccess(root);
ClassWriter clwriter = new ClassWriter();
StringWriter strwriter = new StringWriter();
clwriter.classToJava(root, new BufferedWriter(strwriter), 0);
if (DecompilerContext.getOption(IFernflowerPreferences.OUTPUT_COPYRIGHT_COMMENT)) {
outwriter.write("// Decompiled by: Fernflower " + Fernflower.version);
outwriter.write(DecompilerContext.getNewLineSeparator());
outwriter.write("// Date: " + new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(new Date()));
outwriter.write(DecompilerContext.getNewLineSeparator());
outwriter.write("// Copyright: 2008-2010, Stiver");
outwriter.write(DecompilerContext.getNewLineSeparator());
outwriter.write("// Home page: http://www.reversed-java.com");
outwriter.write(DecompilerContext.getNewLineSeparator());
outwriter.write(DecompilerContext.getNewLineSeparator());
}
int index = cl.qualifiedName.lastIndexOf("/");
if (index >= 0) {
String packageName = cl.qualifiedName.substring(0, index).replace('/', '.');
outwriter.write("package ");
outwriter.write(packageName);
@@ -289,161 +289,169 @@ public class ClassesProcessor {
outwriter.write(DecompilerContext.getNewLineSeparator());
outwriter.write(DecompilerContext.getNewLineSeparator());
}
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, root);
DecompilerContext.getImpcollector().writeImports(outwriter);
outwriter.write(DecompilerContext.getNewLineSeparator());
outwriter.write(strwriter.toString());
outwriter.flush();
} finally {
destroyWrappers(root);
}
}
private void initWrappers(ClassNode node) throws IOException {
if(node.type == ClassNode.CLASS_LAMBDA) {
return;
}
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, root);
ClassWrapper wrapper = new ClassWrapper(node.classStruct);
wrapper.init();
node.wrapper = wrapper;
for(ClassNode nd: node.nested) {
initWrappers(nd);
}
}
private void addClassnameToImport(ClassNode node, ImportCollector imp) {
if(node.simpleName != null && node.simpleName.length() > 0) {
imp.getShortName(node.type == ClassNode.CLASS_ROOT?node.classStruct.qualifiedName:node.simpleName, false);
}
DecompilerContext.getImpcollector().writeImports(outwriter);
outwriter.write(DecompilerContext.getNewLineSeparator());
for(ClassNode nd: node.nested) {
addClassnameToImport(nd, imp);
}
}
private void destroyWrappers(ClassNode node) {
node.wrapper = null;
node.classStruct.releaseResources();
for(ClassNode nd: node.nested) {
destroyWrappers(nd);
}
}
outwriter.write(strwriter.toString());
outwriter.flush();
}
finally {
destroyWrappers(root);
}
}
public HashMap<String, ClassNode> getMapRootClasses() {
return mapRootClasses;
}
public class ClassNode {
public static final int CLASS_ROOT = 0;
public static final int CLASS_MEMBER = 1;
public static final int CLASS_ANONYMOUS = 2;
public static final int CLASS_LOCAL = 4;
public static final int CLASS_LAMBDA = 8;
public int type;
public int access;
public String simpleName;
public StructClass classStruct;
public ClassWrapper wrapper;
public String enclosingMethod;
public InvocationExprent superInvocation;
public HashMap<String, VarVersionPaar> mapFieldsToVars = new HashMap<String, VarVersionPaar>();
public VarType anonimousClassType;
private void initWrappers(ClassNode node) throws IOException {
public List<ClassNode> nested = new ArrayList<ClassNode>();
public Set<String> enclosingClasses = new HashSet<String>();
public ClassNode parent;
public LambdaInformation lambda_information;
public ClassNode(String content_class_name, String content_method_name, String content_method_descriptor, int content_method_invokation_type,
String lambda_class_name, String lambda_method_name, String lambda_method_descriptor, StructClass classStruct) { // lambda class constructor
this.type = CLASS_LAMBDA;
this.classStruct = classStruct; // 'parent' class containing the static function
lambda_information = new LambdaInformation();
lambda_information.class_name = lambda_class_name;
lambda_information.method_name = lambda_method_name;
lambda_information.method_descriptor = lambda_method_descriptor;
lambda_information.content_class_name = content_class_name;
lambda_information.content_method_name = content_method_name;
lambda_information.content_method_descriptor = content_method_descriptor;
lambda_information.content_method_invokation_type = content_method_invokation_type;
lambda_information.content_method_key = InterpreterUtil.makeUniqueKey(lambda_information.content_method_name, lambda_information.content_method_descriptor);
if (node.type == ClassNode.CLASS_LAMBDA) {
return;
}
anonimousClassType = new VarType(lambda_class_name, true);
boolean is_method_reference = (content_class_name != classStruct.qualifiedName);
StructMethod mt = null;
if(!is_method_reference) { // content method in the same class, check synthetic flag
mt = classStruct.getMethod(content_method_name, content_method_descriptor);
is_method_reference = !((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic")); // if not synthetic -> method reference
}
lambda_information.is_method_reference = is_method_reference;
lambda_information.is_content_method_static = (lambda_information.content_method_invokation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic); // FIXME: redundant?
}
public ClassNode(int type, StructClass classStruct) {
this.type = type;
this.classStruct = classStruct;
simpleName = classStruct.qualifiedName.substring(classStruct.qualifiedName.lastIndexOf('/')+1);
}
public ClassNode getClassNode(String qualifiedName) {
for(ClassNode node : nested) {
if(qualifiedName.equals(node.classStruct.qualifiedName)) {
return node;
}
}
return null;
}
ClassWrapper wrapper = new ClassWrapper(node.classStruct);
wrapper.init();
public class LambdaInformation {
public String class_name;
public String method_name;
public String method_descriptor;
public String content_class_name;
public String content_method_name;
public String content_method_descriptor;
public int content_method_invokation_type; // values from CONSTANT_MethodHandle_REF_*
public String content_method_key;
public boolean is_method_reference;
public boolean is_content_method_static;
}
}
node.wrapper = wrapper;
for (ClassNode nd : node.nested) {
initWrappers(nd);
}
}
private void addClassnameToImport(ClassNode node, ImportCollector imp) {
if (node.simpleName != null && node.simpleName.length() > 0) {
imp.getShortName(node.type == ClassNode.CLASS_ROOT ? node.classStruct.qualifiedName : node.simpleName, false);
}
for (ClassNode nd : node.nested) {
addClassnameToImport(nd, imp);
}
}
private void destroyWrappers(ClassNode node) {
node.wrapper = null;
node.classStruct.releaseResources();
for (ClassNode nd : node.nested) {
destroyWrappers(nd);
}
}
public HashMap<String, ClassNode> getMapRootClasses() {
return mapRootClasses;
}
public class ClassNode {
public static final int CLASS_ROOT = 0;
public static final int CLASS_MEMBER = 1;
public static final int CLASS_ANONYMOUS = 2;
public static final int CLASS_LOCAL = 4;
public static final int CLASS_LAMBDA = 8;
public int type;
public int access;
public String simpleName;
public StructClass classStruct;
public ClassWrapper wrapper;
public String enclosingMethod;
public InvocationExprent superInvocation;
public HashMap<String, VarVersionPaar> mapFieldsToVars = new HashMap<String, VarVersionPaar>();
public VarType anonimousClassType;
public List<ClassNode> nested = new ArrayList<ClassNode>();
public Set<String> enclosingClasses = new HashSet<String>();
public ClassNode parent;
public LambdaInformation lambda_information;
public ClassNode(String content_class_name,
String content_method_name,
String content_method_descriptor,
int content_method_invokation_type,
String lambda_class_name,
String lambda_method_name,
String lambda_method_descriptor,
StructClass classStruct) { // lambda class constructor
this.type = CLASS_LAMBDA;
this.classStruct = classStruct; // 'parent' class containing the static function
lambda_information = new LambdaInformation();
lambda_information.class_name = lambda_class_name;
lambda_information.method_name = lambda_method_name;
lambda_information.method_descriptor = lambda_method_descriptor;
lambda_information.content_class_name = content_class_name;
lambda_information.content_method_name = content_method_name;
lambda_information.content_method_descriptor = content_method_descriptor;
lambda_information.content_method_invokation_type = content_method_invokation_type;
lambda_information.content_method_key =
InterpreterUtil.makeUniqueKey(lambda_information.content_method_name, lambda_information.content_method_descriptor);
anonimousClassType = new VarType(lambda_class_name, true);
boolean is_method_reference = (content_class_name != classStruct.qualifiedName);
StructMethod mt = null;
if (!is_method_reference) { // content method in the same class, check synthetic flag
mt = classStruct.getMethod(content_method_name, content_method_descriptor);
is_method_reference = !((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) != 0 ||
mt.getAttributes().containsKey("Synthetic")); // if not synthetic -> method reference
}
lambda_information.is_method_reference = is_method_reference;
lambda_information.is_content_method_static =
(lambda_information.content_method_invokation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic); // FIXME: redundant?
}
public ClassNode(int type, StructClass classStruct) {
this.type = type;
this.classStruct = classStruct;
simpleName = classStruct.qualifiedName.substring(classStruct.qualifiedName.lastIndexOf('/') + 1);
}
public ClassNode getClassNode(String qualifiedName) {
for (ClassNode node : nested) {
if (qualifiedName.equals(node.classStruct.qualifiedName)) {
return node;
}
}
return null;
}
public class LambdaInformation {
public String class_name;
public String method_name;
public String method_descriptor;
public String content_class_name;
public String content_method_name;
public String content_method_descriptor;
public int content_method_invokation_type; // values from CONSTANT_MethodHandle_REF_*
public String content_method_key;
public boolean is_method_reference;
public boolean is_content_method_static;
}
}
}

View File

@@ -1,21 +1,20 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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 java.util.HashMap;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.main.collectors.ImportCollector;
import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
@@ -24,174 +23,176 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor;
import org.jetbrains.java.decompiler.struct.StructContext;
import java.util.HashMap;
public class DecompilerContext {
public static final String CURRENT_CLASS = "CURRENT_CLASS";
public static final String CURRENT_METHOD = "CURRENT_METHOD";
public static final String CURRENT_METHOD_DESCRIPTOR = "CURRENT_METHOD_DESCRIPTOR";
public static final String CURRENT_VAR_PROCESSOR = "CURRENT_VAR_PROCESSOR";
public static final String CURRENT_CLASSNODE = "CURRENT_CLASSNODE";
public static final String CURRENT_METHOD_WRAPPER = "CURRENT_METHOD_WRAPPER";
private static ThreadLocal<DecompilerContext> currentContext = new ThreadLocal<DecompilerContext>();
private HashMap<String, Object> properties = new HashMap<String, Object>();
private StructContext structcontext;
private ImportCollector impcollector;
private VarNamesCollector varncollector;
private CounterContainer countercontainer;
private ClassesProcessor classprocessor;
private PoolInterceptor poolinterceptor;
public static final String CURRENT_CLASS = "CURRENT_CLASS";
public static final String CURRENT_METHOD = "CURRENT_METHOD";
public static final String CURRENT_METHOD_DESCRIPTOR = "CURRENT_METHOD_DESCRIPTOR";
public static final String CURRENT_VAR_PROCESSOR = "CURRENT_VAR_PROCESSOR";
private IFernflowerLogger logger;
private DecompilerContext(HashMap<String, Object> properties) {
this.properties.putAll(properties);
}
public static final String CURRENT_CLASSNODE = "CURRENT_CLASSNODE";
public static final String CURRENT_METHOD_WRAPPER = "CURRENT_METHOD_WRAPPER";
public static void initContext(HashMap<String, Object> propertiesCustom) {
private static ThreadLocal<DecompilerContext> currentContext = new ThreadLocal<DecompilerContext>();
HashMap<String, Object> mapDefault = new HashMap<String, Object>();
// default settings
mapDefault.put(IFernflowerPreferences.DECOMPILE_INNER, "1");
mapDefault.put(IFernflowerPreferences.DECOMPILE_CLASS_1_4, "1");
mapDefault.put(IFernflowerPreferences.DECOMPILE_ASSERTIONS, "1");
mapDefault.put(IFernflowerPreferences.REMOVE_BRIDGE, "1");
mapDefault.put(IFernflowerPreferences.REMOVE_SYNTHETIC, "0");
mapDefault.put(IFernflowerPreferences.HIDE_EMPTY_SUPER, "1");
mapDefault.put(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR, "1");
mapDefault.put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "0");
mapDefault.put(IFernflowerPreferences.OUTPUT_COPYRIGHT_COMMENT, "0");
mapDefault.put(IFernflowerPreferences.NO_EXCEPTIONS_RETURN, "1");
mapDefault.put(IFernflowerPreferences.DECOMPILE_ENUM, "1");
mapDefault.put(IFernflowerPreferences.FINALLY_DEINLINE, "1");
mapDefault.put(IFernflowerPreferences.REMOVE_GETCLASS_NEW, "1");
mapDefault.put(IFernflowerPreferences.LITERALS_AS_IS, "0");
mapDefault.put(IFernflowerPreferences.ASCII_STRING_CHARACTERS, "0");
mapDefault.put(IFernflowerPreferences.BOOLEAN_TRUE_ONE, "1");
mapDefault.put(IFernflowerPreferences.SYNTHETIC_NOT_SET, "1");
mapDefault.put(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT, "1");
private HashMap<String, Object> properties = new HashMap<String, Object>();
mapDefault.put(IFernflowerPreferences.USE_DEBUG_VARNAMES, "1");
mapDefault.put(IFernflowerPreferences.MAX_PROCESSING_METHOD, "0");
private StructContext structcontext;
mapDefault.put(IFernflowerPreferences.REMOVE_EMPTY_RANGES, "1");
mapDefault.put(IFernflowerPreferences.NEW_LINE_SEPARATOR, "0");
mapDefault.put(IFernflowerPreferences.INDENT_STRING, " ");
private ImportCollector impcollector;
mapDefault.put(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION, "1");
if(propertiesCustom != null) {
mapDefault.putAll(propertiesCustom);
}
private VarNamesCollector varncollector;
currentContext.set(new DecompilerContext(mapDefault));
}
public static DecompilerContext getCurrentContext() {
return currentContext.get();
}
public static void setCurrentContext(DecompilerContext context) {
currentContext.set(context);
}
public static Object getProperty(String key) {
return getCurrentContext().properties.get(key);
}
public static void setProperty(String key, Object value) {
getCurrentContext().properties.put(key, value);
}
private CounterContainer countercontainer;
public static boolean getOption(String key) {
return "1".equals(getCurrentContext().properties.get(key));
}
public static ImportCollector getImpcollector() {
return getCurrentContext().impcollector;
}
private ClassesProcessor classprocessor;
public static void setImpcollector(ImportCollector impcollector) {
getCurrentContext().impcollector = impcollector;
}
private PoolInterceptor poolinterceptor;
public static VarNamesCollector getVarncollector() {
return getCurrentContext().varncollector;
}
private IFernflowerLogger logger;
public static void setVarncollector(VarNamesCollector varncollector) {
getCurrentContext().varncollector = varncollector;
}
public static StructContext getStructcontext() {
return getCurrentContext().structcontext;
}
private DecompilerContext(HashMap<String, Object> properties) {
this.properties.putAll(properties);
}
public static void setStructcontext(StructContext structcontext) {
getCurrentContext().structcontext = structcontext;
}
public static void initContext(HashMap<String, Object> propertiesCustom) {
public static CounterContainer getCountercontainer() {
return getCurrentContext().countercontainer;
}
HashMap<String, Object> mapDefault = new HashMap<String, Object>();
public static void setCountercontainer(CounterContainer countercontainer) {
getCurrentContext().countercontainer = countercontainer;
}
// default settings
mapDefault.put(IFernflowerPreferences.DECOMPILE_INNER, "1");
mapDefault.put(IFernflowerPreferences.DECOMPILE_CLASS_1_4, "1");
mapDefault.put(IFernflowerPreferences.DECOMPILE_ASSERTIONS, "1");
mapDefault.put(IFernflowerPreferences.REMOVE_BRIDGE, "1");
mapDefault.put(IFernflowerPreferences.REMOVE_SYNTHETIC, "0");
mapDefault.put(IFernflowerPreferences.HIDE_EMPTY_SUPER, "1");
mapDefault.put(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR, "1");
mapDefault.put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "0");
mapDefault.put(IFernflowerPreferences.OUTPUT_COPYRIGHT_COMMENT, "0");
mapDefault.put(IFernflowerPreferences.NO_EXCEPTIONS_RETURN, "1");
mapDefault.put(IFernflowerPreferences.DECOMPILE_ENUM, "1");
mapDefault.put(IFernflowerPreferences.FINALLY_DEINLINE, "1");
mapDefault.put(IFernflowerPreferences.REMOVE_GETCLASS_NEW, "1");
mapDefault.put(IFernflowerPreferences.LITERALS_AS_IS, "0");
mapDefault.put(IFernflowerPreferences.ASCII_STRING_CHARACTERS, "0");
mapDefault.put(IFernflowerPreferences.BOOLEAN_TRUE_ONE, "1");
mapDefault.put(IFernflowerPreferences.SYNTHETIC_NOT_SET, "1");
mapDefault.put(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT, "1");
public static ClassesProcessor getClassprocessor() {
return getCurrentContext().classprocessor;
}
mapDefault.put(IFernflowerPreferences.USE_DEBUG_VARNAMES, "1");
mapDefault.put(IFernflowerPreferences.MAX_PROCESSING_METHOD, "0");
public static void setClassprocessor(ClassesProcessor classprocessor) {
getCurrentContext().classprocessor = classprocessor;
}
mapDefault.put(IFernflowerPreferences.REMOVE_EMPTY_RANGES, "1");
public static PoolInterceptor getPoolInterceptor() {
return getCurrentContext().poolinterceptor;
}
mapDefault.put(IFernflowerPreferences.NEW_LINE_SEPARATOR, "0");
mapDefault.put(IFernflowerPreferences.INDENT_STRING, " ");
public static void setPoolInterceptor(PoolInterceptor poolinterceptor) {
getCurrentContext().poolinterceptor = poolinterceptor;
}
mapDefault.put(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION, "1");
public static IFernflowerLogger getLogger() {
return getCurrentContext().logger;
}
if (propertiesCustom != null) {
mapDefault.putAll(propertiesCustom);
}
public static void setLogger(IFernflowerLogger logger) {
getCurrentContext().logger = logger;
setLogSeverity();
}
private static void setLogSeverity() {
IFernflowerLogger logger = getCurrentContext().logger;
if(logger != null) {
String severity = (String)getProperty(IFernflowerPreferences.LOG_LEVEL);
if(severity != null) {
Integer iSeverity = IFernflowerLogger.mapLogLevel.get(severity.toUpperCase());
if(iSeverity != null) {
logger.setSeverity(iSeverity);
}
}
}
}
public static String getNewLineSeparator() {
return getOption(IFernflowerPreferences.NEW_LINE_SEPARATOR) ?
IFernflowerPreferences.LINE_SEPARATOR_LIN : IFernflowerPreferences.LINE_SEPARATOR_WIN ;
}
currentContext.set(new DecompilerContext(mapDefault));
}
public static DecompilerContext getCurrentContext() {
return currentContext.get();
}
public static void setCurrentContext(DecompilerContext context) {
currentContext.set(context);
}
public static Object getProperty(String key) {
return getCurrentContext().properties.get(key);
}
public static void setProperty(String key, Object value) {
getCurrentContext().properties.put(key, value);
}
public static boolean getOption(String key) {
return "1".equals(getCurrentContext().properties.get(key));
}
public static ImportCollector getImpcollector() {
return getCurrentContext().impcollector;
}
public static void setImpcollector(ImportCollector impcollector) {
getCurrentContext().impcollector = impcollector;
}
public static VarNamesCollector getVarncollector() {
return getCurrentContext().varncollector;
}
public static void setVarncollector(VarNamesCollector varncollector) {
getCurrentContext().varncollector = varncollector;
}
public static StructContext getStructcontext() {
return getCurrentContext().structcontext;
}
public static void setStructcontext(StructContext structcontext) {
getCurrentContext().structcontext = structcontext;
}
public static CounterContainer getCountercontainer() {
return getCurrentContext().countercontainer;
}
public static void setCountercontainer(CounterContainer countercontainer) {
getCurrentContext().countercontainer = countercontainer;
}
public static ClassesProcessor getClassprocessor() {
return getCurrentContext().classprocessor;
}
public static void setClassprocessor(ClassesProcessor classprocessor) {
getCurrentContext().classprocessor = classprocessor;
}
public static PoolInterceptor getPoolInterceptor() {
return getCurrentContext().poolinterceptor;
}
public static void setPoolInterceptor(PoolInterceptor poolinterceptor) {
getCurrentContext().poolinterceptor = poolinterceptor;
}
public static IFernflowerLogger getLogger() {
return getCurrentContext().logger;
}
public static void setLogger(IFernflowerLogger logger) {
getCurrentContext().logger = logger;
setLogSeverity();
}
private static void setLogSeverity() {
IFernflowerLogger logger = getCurrentContext().logger;
if (logger != null) {
String severity = (String)getProperty(IFernflowerPreferences.LOG_LEVEL);
if (severity != null) {
Integer iSeverity = IFernflowerLogger.mapLogLevel.get(severity.toUpperCase());
if (iSeverity != null) {
logger.setSeverity(iSeverity);
}
}
}
}
public static String getNewLineSeparator() {
return getOption(IFernflowerPreferences.NEW_LINE_SEPARATOR) ?
IFernflowerPreferences.LINE_SEPARATOR_LIN : IFernflowerPreferences.LINE_SEPARATOR_WIN;
}
}

View File

@@ -1,17 +1,18 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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;
@@ -33,127 +34,125 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil;
public class EnumProcessor {
public static void clearEnum(ClassWrapper wrapper) {
public static void clearEnum(ClassWrapper wrapper) {
StructClass cl = wrapper.getClassStruct();
// hide values() and valueOf()
for(StructMethod meth : cl.getMethods()) {
String name = meth.getName();
int flag = 0;
if("values".equals(name)) {
flag = 1;
} else if("valueOf".equals(name)) {
flag = 2;
}
if(flag>0) {
String[] arr = meth.getDescriptor().split("[()]");
String par = arr[1];
if((flag == 1 && par.length() == 0) ||
flag == 2 && "Ljava/lang/String;".equals(par)) {
wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(name, meth.getDescriptor()));
}
}
}
// hide all super invocations
for(MethodWrapper meth : wrapper.getMethods()) {
if("<init>".equals(meth.methodStruct.getName())) {
Statement firstdata = findFirstData(meth.root);
if(firstdata == null || firstdata.getExprents().isEmpty()) {
return;
}
Exprent exprent = firstdata.getExprents().get(0);
if(exprent.type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent invexpr = (InvocationExprent)exprent;
if(isInvocationSuperConstructor(invexpr, meth, wrapper)) {
firstdata.getExprents().remove(0);
}
}
}
}
// hide dummy synthetic fields of enum constants
for(StructField fd: cl.getFields()) {
if((fd.access_flags & CodeConstants.ACC_ENUM) != 0) {
Exprent initializer = wrapper.getStaticFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
if(initializer != null && initializer.type == Exprent.EXPRENT_NEW) {
NewExprent nexpr = (NewExprent)initializer;
if(nexpr.isAnonymous()) {
ClassNode child = DecompilerContext.getClassprocessor().getMapRootClasses().get(nexpr.getNewtype().value);
hideDummyFieldInConstant(child.wrapper);
StructClass cl = wrapper.getClassStruct();
// hide values() and valueOf()
for (StructMethod meth : cl.getMethods()) {
String name = meth.getName();
int flag = 0;
if ("values".equals(name)) {
flag = 1;
}
else if ("valueOf".equals(name)) {
flag = 2;
}
if (flag > 0) {
String[] arr = meth.getDescriptor().split("[()]");
String par = arr[1];
if ((flag == 1 && par.length() == 0) ||
flag == 2 && "Ljava/lang/String;".equals(par)) {
wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(name, meth.getDescriptor()));
}
}
}
// hide all super invocations
for (MethodWrapper meth : wrapper.getMethods()) {
if ("<init>".equals(meth.methodStruct.getName())) {
Statement firstdata = findFirstData(meth.root);
if (firstdata == null || firstdata.getExprents().isEmpty()) {
return;
}
Exprent exprent = firstdata.getExprents().get(0);
if (exprent.type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent invexpr = (InvocationExprent)exprent;
if (isInvocationSuperConstructor(invexpr, meth, wrapper)) {
firstdata.getExprents().remove(0);
}
}
}
}
}
private static void hideDummyFieldInConstant(ClassWrapper wrapper) {
StructClass cl = wrapper.getClassStruct();
for(StructField fd: cl.getFields()) {
if((fd.access_flags & CodeConstants.ACC_SYNTHETIC) != 0) {
FieldDescriptor descr = FieldDescriptor.parseDescriptor(fd.getDescriptor());
VarType ret = descr.type;
if(ret.type == CodeConstants.TYPE_OBJECT && ret.arraydim == 1 && cl.qualifiedName.equals(ret.value)) {
wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
}
}
}
}
// FIXME: move to a util class (see also InitializerProcessor)
private static Statement findFirstData(Statement stat) {
}
}
}
if(stat.getExprents() != null) {
return stat;
} else {
if(stat.isLabeled()) {
return null;
}
switch(stat.type) {
case Statement.TYPE_SEQUENCE:
case Statement.TYPE_IF:
case Statement.TYPE_ROOT:
case Statement.TYPE_SWITCH:
case Statement.TYPE_SYNCRONIZED:
return findFirstData(stat.getFirst());
default:
return null;
}
}
}
// hide dummy synthetic fields of enum constants
for (StructField fd : cl.getFields()) {
if ((fd.access_flags & CodeConstants.ACC_ENUM) != 0) {
Exprent initializer =
wrapper.getStaticFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
if (initializer != null && initializer.type == Exprent.EXPRENT_NEW) {
NewExprent nexpr = (NewExprent)initializer;
if (nexpr.isAnonymous()) {
ClassNode child = DecompilerContext.getClassprocessor().getMapRootClasses().get(nexpr.getNewtype().value);
hideDummyFieldInConstant(child.wrapper);
}
}
}
}
}
// FIXME: move to util class (see also InitializerProcessor)
private static boolean isInvocationSuperConstructor(InvocationExprent inv, MethodWrapper meth, ClassWrapper wrapper) {
if(inv.getFunctype() == InvocationExprent.TYP_INIT) {
if(inv.getInstance().type == Exprent.EXPRENT_VAR) {
VarExprent instvar = (VarExprent)inv.getInstance();
VarVersionPaar varpaar = new VarVersionPaar(instvar);
String classname = meth.varproc.getThisvars().get(varpaar);
if(classname!=null) { // any this instance. TODO: Restrict to current class?
if(!wrapper.getClassStruct().qualifiedName.equals(inv.getClassname())) {
return true;
}
}
}
}
return false;
}
private static void hideDummyFieldInConstant(ClassWrapper wrapper) {
StructClass cl = wrapper.getClassStruct();
for (StructField fd : cl.getFields()) {
if ((fd.access_flags & CodeConstants.ACC_SYNTHETIC) != 0) {
FieldDescriptor descr = FieldDescriptor.parseDescriptor(fd.getDescriptor());
VarType ret = descr.type;
if (ret.type == CodeConstants.TYPE_OBJECT && ret.arraydim == 1 && cl.qualifiedName.equals(ret.value)) {
wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
}
}
}
}
// FIXME: move to a util class (see also InitializerProcessor)
private static Statement findFirstData(Statement stat) {
if (stat.getExprents() != null) {
return stat;
}
else {
if (stat.isLabeled()) {
return null;
}
switch (stat.type) {
case Statement.TYPE_SEQUENCE:
case Statement.TYPE_IF:
case Statement.TYPE_ROOT:
case Statement.TYPE_SWITCH:
case Statement.TYPE_SYNCRONIZED:
return findFirstData(stat.getFirst());
default:
return null;
}
}
}
// FIXME: move to util class (see also InitializerProcessor)
private static boolean isInvocationSuperConstructor(InvocationExprent inv, MethodWrapper meth, ClassWrapper wrapper) {
if (inv.getFunctype() == InvocationExprent.TYP_INIT) {
if (inv.getInstance().type == Exprent.EXPRENT_VAR) {
VarExprent instvar = (VarExprent)inv.getInstance();
VarVersionPaar varpaar = new VarVersionPaar(instvar);
String classname = meth.varproc.getThisvars().get(varpaar);
if (classname != null) { // any this instance. TODO: Restrict to current class?
if (!wrapper.getClassStruct().qualifiedName.equals(inv.getClassname())) {
return true;
}
}
}
}
return false;
}
}

View File

@@ -1,23 +1,20 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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 java.io.BufferedWriter;
import java.io.StringWriter;
import java.util.HashMap;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider;
@@ -29,82 +26,88 @@ import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructContext;
import org.jetbrains.java.decompiler.struct.lazy.LazyLoader;
import java.io.BufferedWriter;
import java.io.StringWriter;
import java.util.HashMap;
public class Fernflower implements IDecompiledData {
public static final String version = "v0.8.4";
private StructContext structcontext;
private ClassesProcessor clprocessor;
public Fernflower(IBytecodeProvider provider, IDecompilatSaver saver,
HashMap<String, Object> propertiesCustom) {
public static final String version = "v0.8.4";
StructContext context = new StructContext(saver, this, new LazyLoader(provider));
structcontext = context;
private StructContext structcontext;
DecompilerContext.initContext(propertiesCustom);
DecompilerContext.setCountercontainer(new CounterContainer());
private ClassesProcessor clprocessor;
}
public Fernflower(IBytecodeProvider provider, IDecompilatSaver saver,
HashMap<String, Object> propertiesCustom) {
public void decompileContext() {
StructContext context = new StructContext(saver, this, new LazyLoader(provider));
if(DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) {
IdentifierConverter ren = new IdentifierConverter();
ren.rename(structcontext);
ren = null;
}
clprocessor = new ClassesProcessor(structcontext);
DecompilerContext.setClassprocessor(clprocessor);
DecompilerContext.setStructcontext(structcontext);
structcontext.saveContext();
}
structcontext = context;
DecompilerContext.initContext(propertiesCustom);
DecompilerContext.setCountercontainer(new CounterContainer());
}
public void decompileContext() {
if (DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) {
IdentifierConverter ren = new IdentifierConverter();
ren.rename(structcontext);
ren = null;
}
clprocessor = new ClassesProcessor(structcontext);
DecompilerContext.setClassprocessor(clprocessor);
DecompilerContext.setStructcontext(structcontext);
structcontext.saveContext();
}
public void clearContext() {
DecompilerContext.setCurrentContext(null);
}
public String getClassEntryName(StructClass cl, String entryname) {
ClassNode node = clprocessor.getMapRootClasses().get(cl.qualifiedName);
if(node.type != ClassNode.CLASS_ROOT) {
return null;
} else {
if(DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) {
String simple_classname = cl.qualifiedName.substring(cl.qualifiedName.lastIndexOf('/')+1);
return entryname.substring(0, entryname.lastIndexOf('/')+1)+simple_classname+".java";
} else {
return entryname.substring(0, entryname.lastIndexOf(".class"))+".java";
}
}
}
public String getClassEntryName(StructClass cl, String entryname) {
public StructContext getStructcontext() {
return structcontext;
}
ClassNode node = clprocessor.getMapRootClasses().get(cl.qualifiedName);
if (node.type != ClassNode.CLASS_ROOT) {
return null;
}
else {
if (DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) {
String simple_classname = cl.qualifiedName.substring(cl.qualifiedName.lastIndexOf('/') + 1);
return entryname.substring(0, entryname.lastIndexOf('/') + 1) + simple_classname + ".java";
}
else {
return entryname.substring(0, entryname.lastIndexOf(".class")) + ".java";
}
}
}
public String getClassContent(StructClass cl) {
String res = null;
try {
StringWriter strwriter = new StringWriter();
clprocessor.writeClass(structcontext, cl, new BufferedWriter(strwriter));
res = strwriter.toString();
} catch(ThreadDeath ex) {
throw ex;
} catch(Throwable ex) {
DecompilerContext.getLogger().writeMessage("Class "+cl.qualifiedName+" couldn't be fully decompiled.", ex);
}
return res;
}
public StructContext getStructcontext() {
return structcontext;
}
public String getClassContent(StructClass cl) {
String res = null;
try {
StringWriter strwriter = new StringWriter();
clprocessor.writeClass(structcontext, cl, new BufferedWriter(strwriter));
res = strwriter.toString();
}
catch (ThreadDeath ex) {
throw ex;
}
catch (Throwable ex) {
DecompilerContext.getLogger().writeMessage("Class " + cl.qualifiedName + " couldn't be fully decompiled.", ex);
}
return res;
}
}

View File

@@ -1,32 +1,26 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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 java.util.ArrayList;
import java.util.List;
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.AssignmentExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
@@ -34,288 +28,296 @@ import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructField;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.util.ArrayList;
import java.util.List;
public class InitializerProcessor {
public static void extractInitializers(ClassWrapper wrapper) {
MethodWrapper meth = wrapper.getMethodWrapper("<clinit>", "()V");
if(meth != null && meth.root != null) { // successfully decompiled static constructor
extractStaticInitializers(wrapper, meth);
}
extractDynamicInitializers(wrapper);
// required e.g. if anonymous class is being decompiled as a standard one.
// This can happen if InnerClasses attributes are erased
liftConstructor(wrapper);
if(DecompilerContext.getOption(IFernflowerPreferences.HIDE_EMPTY_SUPER)) {
hideEmptySuper(wrapper);
}
}
private static void liftConstructor(ClassWrapper wrapper) {
for(MethodWrapper meth : wrapper.getMethods()) {
if("<init>".equals(meth.methodStruct.getName()) && meth.root != null) {
Statement firstdata = findFirstData(meth.root);
if(firstdata == null) {
return;
}
public static void extractInitializers(ClassWrapper wrapper) {
int index = 0;
List<Exprent> lstExprents = firstdata.getExprents();
for(Exprent exprent : lstExprents) {
MethodWrapper meth = wrapper.getMethodWrapper("<clinit>", "()V");
if (meth != null && meth.root != null) { // successfully decompiled static constructor
extractStaticInitializers(wrapper, meth);
}
int action = 0;
if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asexpr = (AssignmentExprent)exprent;
if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fexpr = (FieldExprent)asexpr.getLeft();
if(fexpr.getClassname().equals(wrapper.getClassStruct().qualifiedName)) {
StructField structField = wrapper.getClassStruct().getField(fexpr.getName(), fexpr.getDescriptor().descriptorString);
if(structField != null && (structField.access_flags & CodeConstants.ACC_FINAL) != 0) {
action = 1;
}
}
}
} else if(index > 0 && exprent.type == Exprent.EXPRENT_INVOCATION &&
isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, true)) {
// this() or super()
lstExprents.add(0, lstExprents.remove(index));
action = 2;
}
if(action != 1) {
break;
}
index++;
}
}
}
}
private static void hideEmptySuper(ClassWrapper wrapper) {
for(MethodWrapper meth : wrapper.getMethods()) {
if("<init>".equals(meth.methodStruct.getName()) && meth.root != null) {
Statement firstdata = findFirstData(meth.root);
if(firstdata == null || firstdata.getExprents().isEmpty()) {
return;
}
Exprent exprent = firstdata.getExprents().get(0);
if(exprent.type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent invexpr = (InvocationExprent)exprent;
if(isInvocationInitConstructor(invexpr, meth, wrapper, false) && invexpr.getLstParameters().isEmpty()) {
firstdata.getExprents().remove(0);
}
}
}
}
}
private static void extractStaticInitializers(ClassWrapper wrapper, MethodWrapper meth) {
RootStatement root = meth.root;
StructClass cl = wrapper.getClassStruct();
Statement firstdata = findFirstData(root);
if(firstdata != null) {
while(!firstdata.getExprents().isEmpty()) {
Exprent exprent = firstdata.getExprents().get(0);
boolean found = false;
if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asexpr = (AssignmentExprent)exprent;
if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD) {
FieldExprent fexpr = (FieldExprent)asexpr.getLeft();
if(fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) &&
cl.hasField(fexpr.getName(), fexpr.getDescriptor().descriptorString)) {
if(isExprentIndependent(asexpr.getRight(), meth)) {
extractDynamicInitializers(wrapper);
String keyField = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString);
if(!wrapper.getStaticFieldInitializers().containsKey(keyField)) {
wrapper.getStaticFieldInitializers().addWithKey(asexpr.getRight(), keyField);
firstdata.getExprents().remove(0);
found = true;
}
}
}
}
}
if(!found) {
break;
}
}
}
}
private static void extractDynamicInitializers(ClassWrapper wrapper) {
StructClass cl = wrapper.getClassStruct();
// required e.g. if anonymous class is being decompiled as a standard one.
// This can happen if InnerClasses attributes are erased
liftConstructor(wrapper);
boolean isAnonymous = DecompilerContext.getClassprocessor().getMapRootClasses().get(cl.qualifiedName).type == ClassNode.CLASS_ANONYMOUS;
List<List<Exprent>> lstFirst = new ArrayList<List<Exprent>>();
List<MethodWrapper> lstMethWrappers = new ArrayList<MethodWrapper>();
for(MethodWrapper meth : wrapper.getMethods()) {
if("<init>".equals(meth.methodStruct.getName()) && meth.root != null) { // successfully decompiled constructor
Statement firstdata = findFirstData(meth.root);
if(firstdata == null || firstdata.getExprents().isEmpty()) {
return;
}
lstFirst.add(firstdata.getExprents());
lstMethWrappers.add(meth);
Exprent exprent = firstdata.getExprents().get(0);
if(!isAnonymous) { // FIXME: doesn't make sense
if(exprent.type != Exprent.EXPRENT_INVOCATION || !isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, false)) {
return;
}
}
}
}
if(lstFirst.isEmpty()) {
return;
}
for(;;) {
String fieldWithDescr = null;
Exprent value = null;
for(int i=0; i<lstFirst.size(); i++) {
List<Exprent> lst = lstFirst.get(i);
if(lst.size() < (isAnonymous?1:2)) {
return;
}
Exprent exprent = lst.get(isAnonymous?0:1);
boolean found = false;
if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asexpr = (AssignmentExprent)exprent;
if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD) {
FieldExprent fexpr = (FieldExprent)asexpr.getLeft();
if(!fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) &&
cl.hasField(fexpr.getName(), fexpr.getDescriptor().descriptorString)) { // check for the physical existence of the field. Could be defined in a superclass.
if(isExprentIndependent(asexpr.getRight(), lstMethWrappers.get(i))) {
String fieldKey = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString);
if(fieldWithDescr == null) {
fieldWithDescr = fieldKey;
value = asexpr.getRight();
} else {
if(!fieldWithDescr.equals(fieldKey) ||
!value.equals(asexpr.getRight())) {
return;
}
}
found = true;
}
}
}
}
if(!found) {
return;
}
}
if(!wrapper.getDynamicFieldInitializers().containsKey(fieldWithDescr)) {
wrapper.getDynamicFieldInitializers().addWithKey(value, fieldWithDescr);
for(List<Exprent> lst : lstFirst) {
lst.remove(isAnonymous?0:1);
}
} else {
return;
}
}
}
private static boolean isExprentIndependent(Exprent exprent, MethodWrapper meth) {
List<Exprent> lst = exprent.getAllExprents(true);
lst.add(exprent);
for(Exprent expr : lst) {
switch(expr.type) {
case Exprent.EXPRENT_VAR:
VarVersionPaar varpaar = new VarVersionPaar((VarExprent)expr);
if(!meth.varproc.getExternvars().contains(varpaar)) {
String varname = meth.varproc.getVarName(varpaar);
if(!varname.equals("this") && !varname.endsWith(".this")) { // FIXME: remove direct comparison with strings
return false;
}
}
break;
case Exprent.EXPRENT_FIELD:
return false;
}
}
if (DecompilerContext.getOption(IFernflowerPreferences.HIDE_EMPTY_SUPER)) {
hideEmptySuper(wrapper);
}
}
return true;
}
private static Statement findFirstData(Statement stat) {
if(stat.getExprents() != null) {
return stat;
} else {
if(stat.isLabeled()) { // FIXME: Why??
return null;
}
switch(stat.type) {
case Statement.TYPE_SEQUENCE:
case Statement.TYPE_IF:
case Statement.TYPE_ROOT:
case Statement.TYPE_SWITCH:
case Statement.TYPE_SYNCRONIZED:
return findFirstData(stat.getFirst());
default:
return null;
}
}
}
private static boolean isInvocationInitConstructor(InvocationExprent inv, MethodWrapper meth, ClassWrapper wrapper, boolean withThis) {
if(inv.getFunctype() == InvocationExprent.TYP_INIT) {
if(inv.getInstance().type == Exprent.EXPRENT_VAR) {
VarExprent instvar = (VarExprent)inv.getInstance();
VarVersionPaar varpaar = new VarVersionPaar(instvar);
String classname = meth.varproc.getThisvars().get(varpaar);
if(classname!=null) { // any this instance. TODO: Restrict to current class?
if(withThis || !wrapper.getClassStruct().qualifiedName.equals(inv.getClassname())) {
return true;
}
}
}
}
return false;
}
private static void liftConstructor(ClassWrapper wrapper) {
for (MethodWrapper meth : wrapper.getMethods()) {
if ("<init>".equals(meth.methodStruct.getName()) && meth.root != null) {
Statement firstdata = findFirstData(meth.root);
if (firstdata == null) {
return;
}
int index = 0;
List<Exprent> lstExprents = firstdata.getExprents();
for (Exprent exprent : lstExprents) {
int action = 0;
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asexpr = (AssignmentExprent)exprent;
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fexpr = (FieldExprent)asexpr.getLeft();
if (fexpr.getClassname().equals(wrapper.getClassStruct().qualifiedName)) {
StructField structField = wrapper.getClassStruct().getField(fexpr.getName(), fexpr.getDescriptor().descriptorString);
if (structField != null && (structField.access_flags & CodeConstants.ACC_FINAL) != 0) {
action = 1;
}
}
}
}
else if (index > 0 && exprent.type == Exprent.EXPRENT_INVOCATION &&
isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, true)) {
// this() or super()
lstExprents.add(0, lstExprents.remove(index));
action = 2;
}
if (action != 1) {
break;
}
index++;
}
}
}
}
private static void hideEmptySuper(ClassWrapper wrapper) {
for (MethodWrapper meth : wrapper.getMethods()) {
if ("<init>".equals(meth.methodStruct.getName()) && meth.root != null) {
Statement firstdata = findFirstData(meth.root);
if (firstdata == null || firstdata.getExprents().isEmpty()) {
return;
}
Exprent exprent = firstdata.getExprents().get(0);
if (exprent.type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent invexpr = (InvocationExprent)exprent;
if (isInvocationInitConstructor(invexpr, meth, wrapper, false) && invexpr.getLstParameters().isEmpty()) {
firstdata.getExprents().remove(0);
}
}
}
}
}
private static void extractStaticInitializers(ClassWrapper wrapper, MethodWrapper meth) {
RootStatement root = meth.root;
StructClass cl = wrapper.getClassStruct();
Statement firstdata = findFirstData(root);
if (firstdata != null) {
while (!firstdata.getExprents().isEmpty()) {
Exprent exprent = firstdata.getExprents().get(0);
boolean found = false;
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asexpr = (AssignmentExprent)exprent;
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) {
FieldExprent fexpr = (FieldExprent)asexpr.getLeft();
if (fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) &&
cl.hasField(fexpr.getName(), fexpr.getDescriptor().descriptorString)) {
if (isExprentIndependent(asexpr.getRight(), meth)) {
String keyField = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString);
if (!wrapper.getStaticFieldInitializers().containsKey(keyField)) {
wrapper.getStaticFieldInitializers().addWithKey(asexpr.getRight(), keyField);
firstdata.getExprents().remove(0);
found = true;
}
}
}
}
}
if (!found) {
break;
}
}
}
}
private static void extractDynamicInitializers(ClassWrapper wrapper) {
StructClass cl = wrapper.getClassStruct();
boolean isAnonymous = DecompilerContext.getClassprocessor().getMapRootClasses().get(cl.qualifiedName).type == ClassNode.CLASS_ANONYMOUS;
List<List<Exprent>> lstFirst = new ArrayList<List<Exprent>>();
List<MethodWrapper> lstMethWrappers = new ArrayList<MethodWrapper>();
for (MethodWrapper meth : wrapper.getMethods()) {
if ("<init>".equals(meth.methodStruct.getName()) && meth.root != null) { // successfully decompiled constructor
Statement firstdata = findFirstData(meth.root);
if (firstdata == null || firstdata.getExprents().isEmpty()) {
return;
}
lstFirst.add(firstdata.getExprents());
lstMethWrappers.add(meth);
Exprent exprent = firstdata.getExprents().get(0);
if (!isAnonymous) { // FIXME: doesn't make sense
if (exprent.type != Exprent.EXPRENT_INVOCATION ||
!isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, false)) {
return;
}
}
}
}
if (lstFirst.isEmpty()) {
return;
}
for (; ; ) {
String fieldWithDescr = null;
Exprent value = null;
for (int i = 0; i < lstFirst.size(); i++) {
List<Exprent> lst = lstFirst.get(i);
if (lst.size() < (isAnonymous ? 1 : 2)) {
return;
}
Exprent exprent = lst.get(isAnonymous ? 0 : 1);
boolean found = false;
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asexpr = (AssignmentExprent)exprent;
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) {
FieldExprent fexpr = (FieldExprent)asexpr.getLeft();
if (!fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) &&
cl.hasField(fexpr.getName(), fexpr
.getDescriptor().descriptorString)) { // check for the physical existence of the field. Could be defined in a superclass.
if (isExprentIndependent(asexpr.getRight(), lstMethWrappers.get(i))) {
String fieldKey = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString);
if (fieldWithDescr == null) {
fieldWithDescr = fieldKey;
value = asexpr.getRight();
}
else {
if (!fieldWithDescr.equals(fieldKey) ||
!value.equals(asexpr.getRight())) {
return;
}
}
found = true;
}
}
}
}
if (!found) {
return;
}
}
if (!wrapper.getDynamicFieldInitializers().containsKey(fieldWithDescr)) {
wrapper.getDynamicFieldInitializers().addWithKey(value, fieldWithDescr);
for (List<Exprent> lst : lstFirst) {
lst.remove(isAnonymous ? 0 : 1);
}
}
else {
return;
}
}
}
private static boolean isExprentIndependent(Exprent exprent, MethodWrapper meth) {
List<Exprent> lst = exprent.getAllExprents(true);
lst.add(exprent);
for (Exprent expr : lst) {
switch (expr.type) {
case Exprent.EXPRENT_VAR:
VarVersionPaar varpaar = new VarVersionPaar((VarExprent)expr);
if (!meth.varproc.getExternvars().contains(varpaar)) {
String varname = meth.varproc.getVarName(varpaar);
if (!varname.equals("this") && !varname.endsWith(".this")) { // FIXME: remove direct comparison with strings
return false;
}
}
break;
case Exprent.EXPRENT_FIELD:
return false;
}
}
return true;
}
private static Statement findFirstData(Statement stat) {
if (stat.getExprents() != null) {
return stat;
}
else {
if (stat.isLabeled()) { // FIXME: Why??
return null;
}
switch (stat.type) {
case Statement.TYPE_SEQUENCE:
case Statement.TYPE_IF:
case Statement.TYPE_ROOT:
case Statement.TYPE_SWITCH:
case Statement.TYPE_SYNCRONIZED:
return findFirstData(stat.getFirst());
default:
return null;
}
}
}
private static boolean isInvocationInitConstructor(InvocationExprent inv, MethodWrapper meth, ClassWrapper wrapper, boolean withThis) {
if (inv.getFunctype() == InvocationExprent.TYP_INIT) {
if (inv.getInstance().type == Exprent.EXPRENT_VAR) {
VarExprent instvar = (VarExprent)inv.getInstance();
VarVersionPaar varpaar = new VarVersionPaar(instvar);
String classname = meth.varproc.getThisvars().get(varpaar);
if (classname != null) { // any this instance. TODO: Restrict to current class?
if (withThis || !wrapper.getClassStruct().qualifiedName.equals(inv.getClassname())) {
return true;
}
}
}
}
return false;
}
}

View File

@@ -1,37 +1,37 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.collectors;
public class CounterContainer {
public static final int STATEMENT_COUNTER = 0;
public static final int EXPRENT_COUNTER = 1;
public static final int VAR_COUNTER = 2;
private int[] values = new int[]{1, 1, 1};
public void setCounter(int counter, int value) {
values[counter] = value;
}
public static final int STATEMENT_COUNTER = 0;
public static final int EXPRENT_COUNTER = 1;
public static final int VAR_COUNTER = 2;
public int getCounter(int counter) {
return values[counter];
}
private int[] values = new int[]{1, 1, 1};
public int getCounterAndIncrement(int counter) {
return values[counter]++;
}
public void setCounter(int counter, int value) {
values[counter] = value;
}
public int getCounter(int counter) {
return values[counter];
}
public int getCounterAndIncrement(int counter) {
return values[counter]++;
}
}

View File

@@ -1,157 +1,152 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.collectors;
import org.jetbrains.java.decompiler.main.ClassesProcessor;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.struct.StructContext;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.*;
import java.util.Map.Entry;
import org.jetbrains.java.decompiler.main.ClassesProcessor;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
import org.jetbrains.java.decompiler.struct.StructContext;
public class ImportCollector {
private static final String JAVA_LANG_PACKAGE = "java.lang";
private HashMap<String, String> mapSimpleNames = new HashMap<String, String>();
private HashSet<String> setNotImportedNames = new HashSet<String>();
private String currentPackageSlash = "";
private String currentPackagePoint = "";
public ImportCollector(ClassNode root) {
String clname = root.classStruct.qualifiedName;
int index = clname.lastIndexOf("/");
if(index >= 0) {
currentPackageSlash = clname.substring(0, index);
currentPackagePoint = currentPackageSlash.replace('/', '.');
currentPackageSlash += "/";
}
}
private static final String JAVA_LANG_PACKAGE = "java.lang";
public String getShortName(String fullname) {
return getShortName(fullname, true);
}
public String getShortName(String fullname, boolean imported) {
ClassesProcessor clproc = DecompilerContext.getClassprocessor();
ClassNode node = clproc.getMapRootClasses().get(fullname.replace('.', '/'));
String retname = null;
if(node != null && node.classStruct.isOwn()) {
retname = node.simpleName;
while(node.parent != null && node.type == ClassNode.CLASS_MEMBER) {
retname = node.parent.simpleName+"."+retname;
node = node.parent;
}
if(node.type == ClassNode.CLASS_ROOT) {
fullname = node.classStruct.qualifiedName;
fullname = fullname.replace('/', '.');
} else {
return retname;
}
} else if(node == null || !node.classStruct.isOwn()) {
fullname = fullname.replace('$', '.');
}
String nshort = fullname;
String npackage = "";
int lastpoint = fullname.lastIndexOf(".");
if(lastpoint >= 0) {
nshort = fullname.substring(lastpoint+1);
npackage = fullname.substring(0, lastpoint);
}
StructContext context = DecompilerContext.getStructcontext();
boolean existsDefaultClass = (context.getClass(currentPackageSlash+nshort) != null
&& !npackage.equals(currentPackagePoint)) // current package
|| (context.getClass(nshort) != null); // default package
if(existsDefaultClass ||
(mapSimpleNames.containsKey(nshort) && !npackage.equals(mapSimpleNames.get(nshort)))) {
return fullname;
} else if(!mapSimpleNames.containsKey(nshort)) {
mapSimpleNames.put(nshort, npackage);
if(!imported) {
setNotImportedNames.add(nshort);
}
}
return retname==null?nshort:retname;
}
public void writeImports(BufferedWriter writer) throws IOException {
private HashMap<String, String> mapSimpleNames = new HashMap<String, String>();
for(String s: packImports()) {
writer.write("import ");
writer.write(s);
writer.write(";");
writer.write(DecompilerContext.getNewLineSeparator());
}
}
private List<String> packImports() {
List<Entry<String, String>> lst = new ArrayList<Entry<String, String>>(mapSimpleNames.entrySet());
Collections.sort(lst, new Comparator<Entry<String, String>>() {
public int compare(Entry<String, String> par0, Entry<String, String> par1) {
int res = par0.getValue().compareTo(par1.getValue());
if(res == 0) {
res = par0.getKey().compareTo(par1.getKey());
}
return res;
}
});
List<String> res = new ArrayList<String>();
for(Entry<String, String> ent: lst) {
if(!setNotImportedNames.contains(ent.getKey()) // not the current class or one of the nested ones. Also not the empty package.
&& !JAVA_LANG_PACKAGE.equals(ent.getValue())
&& ent.getValue().length() > 0) {
private HashSet<String> setNotImportedNames = new HashSet<String>();
String imp = ent.getValue()+"."+ent.getKey();
res.add(imp);
}
}
return res;
}
private String currentPackageSlash = "";
private String currentPackagePoint = "";
public ImportCollector(ClassNode root) {
String clname = root.classStruct.qualifiedName;
int index = clname.lastIndexOf("/");
if (index >= 0) {
currentPackageSlash = clname.substring(0, index);
currentPackagePoint = currentPackageSlash.replace('/', '.');
currentPackageSlash += "/";
}
}
public String getShortName(String fullname) {
return getShortName(fullname, true);
}
public String getShortName(String fullname, boolean imported) {
ClassesProcessor clproc = DecompilerContext.getClassprocessor();
ClassNode node = clproc.getMapRootClasses().get(fullname.replace('.', '/'));
String retname = null;
if (node != null && node.classStruct.isOwn()) {
retname = node.simpleName;
while (node.parent != null && node.type == ClassNode.CLASS_MEMBER) {
retname = node.parent.simpleName + "." + retname;
node = node.parent;
}
if (node.type == ClassNode.CLASS_ROOT) {
fullname = node.classStruct.qualifiedName;
fullname = fullname.replace('/', '.');
}
else {
return retname;
}
}
else if (node == null || !node.classStruct.isOwn()) {
fullname = fullname.replace('$', '.');
}
String nshort = fullname;
String npackage = "";
int lastpoint = fullname.lastIndexOf(".");
if (lastpoint >= 0) {
nshort = fullname.substring(lastpoint + 1);
npackage = fullname.substring(0, lastpoint);
}
StructContext context = DecompilerContext.getStructcontext();
boolean existsDefaultClass = (context.getClass(currentPackageSlash + nshort) != null
&& !npackage.equals(currentPackagePoint)) // current package
|| (context.getClass(nshort) != null); // default package
if (existsDefaultClass ||
(mapSimpleNames.containsKey(nshort) && !npackage.equals(mapSimpleNames.get(nshort)))) {
return fullname;
}
else if (!mapSimpleNames.containsKey(nshort)) {
mapSimpleNames.put(nshort, npackage);
if (!imported) {
setNotImportedNames.add(nshort);
}
}
return retname == null ? nshort : retname;
}
public void writeImports(BufferedWriter writer) throws IOException {
for (String s : packImports()) {
writer.write("import ");
writer.write(s);
writer.write(";");
writer.write(DecompilerContext.getNewLineSeparator());
}
}
private List<String> packImports() {
List<Entry<String, String>> lst = new ArrayList<Entry<String, String>>(mapSimpleNames.entrySet());
Collections.sort(lst, new Comparator<Entry<String, String>>() {
public int compare(Entry<String, String> par0, Entry<String, String> par1) {
int res = par0.getValue().compareTo(par1.getValue());
if (res == 0) {
res = par0.getKey().compareTo(par1.getKey());
}
return res;
}
});
List<String> res = new ArrayList<String>();
for (Entry<String, String> ent : lst) {
if (!setNotImportedNames.contains(ent.getKey()) // not the current class or one of the nested ones. Also not the empty package.
&& !JAVA_LANG_PACKAGE.equals(ent.getValue())
&& ent.getValue().length() > 0) {
String imp = ent.getValue() + "." + ent.getKey();
res.add(imp);
}
}
return res;
}
}

View File

@@ -1,50 +1,51 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.collectors;
import java.util.HashSet;
public class VarNamesCollector {
private HashSet<String> usedNames = new HashSet<String>();
public VarNamesCollector() {}
private HashSet<String> usedNames = new HashSet<String>();
public VarNamesCollector(HashSet<String> setNames) {
usedNames.addAll(setNames);
}
public void addName(String value) {
usedNames.add(value);
}
public VarNamesCollector() {
}
public String getFreeName(int index) {
return getFreeName("var"+index);
}
public String getFreeName(String proposition) {
while(usedNames.contains(proposition)) {
proposition+="x";
}
usedNames.add(proposition);
return proposition;
}
public VarNamesCollector(HashSet<String> setNames) {
usedNames.addAll(setNames);
}
public HashSet<String> getUsedNames() {
return usedNames;
}
public void addName(String value) {
usedNames.add(value);
}
public String getFreeName(int index) {
return getFreeName("var" + index);
}
public String getFreeName(String proposition) {
while (usedNames.contains(proposition)) {
proposition += "x";
}
usedNames.add(proposition);
return proposition;
}
public HashSet<String> getUsedNames() {
return usedNames;
}
}

View File

@@ -1,37 +1,20 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.decompiler;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.Fernflower;
import org.jetbrains.java.decompiler.main.decompiler.helper.PrintStreamLogger;
@@ -40,284 +23,301 @@ import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.io.*;
import java.util.*;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class ConsoleDecompiler implements IBytecodeProvider, IDecompilatSaver {
private File root;
private Fernflower fernflower;
private HashMap<String, ZipOutputStream> mapArchiveStreams = new HashMap<String, ZipOutputStream>();
private File root;
private HashMap<String, HashSet<String>> mapArchiveEntries = new HashMap<String, HashSet<String>>();
public ConsoleDecompiler() {
this(null);
}
private Fernflower fernflower;
public ConsoleDecompiler(HashMap<String, Object> propertiesCustom) {
this(new PrintStreamLogger(IFernflowerLogger.WARNING, System.out), propertiesCustom);
}
protected ConsoleDecompiler(IFernflowerLogger logger, HashMap<String, Object> propertiesCustom) {
fernflower = new Fernflower(this, this, propertiesCustom);
DecompilerContext.setLogger(logger);
}
public static void main(String[] args) {
private HashMap<String, ZipOutputStream> mapArchiveStreams = new HashMap<String, ZipOutputStream>();
try {
if(args != null && args.length > 1) {
HashMap<String, Object> mapOptions = new HashMap<String, Object>();
List<String> lstSources = new ArrayList<String>();
List<String> lstLibraries = new ArrayList<String>();
boolean isOption = true;
for(int i = 0; i < args.length - 1; ++i) { // last parameter - destination
String arg = args[i];
if(isOption && arg.startsWith("-") &&
arg.length()>5 && arg.charAt(4) == '=') {
String value = arg.substring(5).toUpperCase();
if("TRUE".equals(value)) {
value = "1";
} else if("FALSE".equals(value)) {
value = "0";
}
mapOptions.put(arg.substring(1, 4), value);
} else {
isOption = false;
private HashMap<String, HashSet<String>> mapArchiveEntries = new HashMap<String, HashSet<String>>();
if(arg.startsWith("-e=")) {
lstLibraries.add(arg.substring(3));
} else {
lstSources.add(arg);
}
}
}
public ConsoleDecompiler() {
this(null);
}
if(lstSources.isEmpty()) {
printHelp();
} else {
ConsoleDecompiler decompiler = new ConsoleDecompiler(
new PrintStreamLogger(IFernflowerLogger.INFO, System.out),
mapOptions);
public ConsoleDecompiler(HashMap<String, Object> propertiesCustom) {
this(new PrintStreamLogger(IFernflowerLogger.WARNING, System.out), propertiesCustom);
}
for(String source : lstSources) {
decompiler.addSpace(new File(source), true);
}
protected ConsoleDecompiler(IFernflowerLogger logger, HashMap<String, Object> propertiesCustom) {
fernflower = new Fernflower(this, this, propertiesCustom);
DecompilerContext.setLogger(logger);
}
for(String library : lstLibraries) {
decompiler.addSpace(new File(library), false);
}
decompiler.decompileContext(new File(args[args.length-1]));
}
} else {
printHelp();
}
} catch(Exception ex) {
ex.printStackTrace();
}
}
private static void printHelp() {
System.out.println("Usage: java ConsoleDecompiler ( -<option>=<value>)* (<source>)+ <destination>");
System.out.println("Example: java ConsoleDecompiler -dgs=true c:\\mysource\\ c:\\my.jar d:\\decompiled\\");
}
public static void main(String[] args) {
public void addSpace(File file, boolean isOwn) throws IOException {
fernflower.getStructcontext().addSpace(file, isOwn);
}
public void decompileContext(File root) {
this.root = root;
fernflower.decompileContext();
}
// *******************************************************************
// Interface IBytecodeProvider
// *******************************************************************
try {
public InputStream getBytecodeStream(String externPath, String internPath) {
if (args != null && args.length > 1) {
try {
File file = new File(externPath);
if(internPath == null) {
return new FileInputStream(file);
} else { // archive file
ZipFile archive = new ZipFile(file);
Enumeration<? extends ZipEntry> en = archive.entries();
while(en.hasMoreElements()) {
ZipEntry entr = en.nextElement();
HashMap<String, Object> mapOptions = new HashMap<String, Object>();
if(entr.getName().equals(internPath)) {
return archive.getInputStream(entr);
}
}
}
} catch(IOException ex) {
ex.printStackTrace();
}
return null;
}
List<String> lstSources = new ArrayList<String>();
List<String> lstLibraries = new ArrayList<String>();
// *******************************************************************
// Interface IDecompilatSaver
// *******************************************************************
private String getAbsolutePath(String path) {
return new File(root, path).getAbsolutePath();
}
private boolean addEntryName(String filename, String entry) {
HashSet<String> set = mapArchiveEntries.get(filename);
if(set == null) {
mapArchiveEntries.put(filename, set = new HashSet<String>());
}
return set.add(entry);
}
public void copyEntry(String source, String destpath, String archivename, String entryName) {
try {
String filename = new File(getAbsolutePath(destpath), archivename).getAbsolutePath();
if(!addEntryName(filename, entryName)) {
DecompilerContext.getLogger().writeMessage("Zip entry already exists: "+
destpath+","+archivename+","+entryName, IFernflowerLogger.WARNING);
return;
}
ZipFile srcarchive = new ZipFile(new File(source));
Enumeration<? extends ZipEntry> en = srcarchive.entries();
while(en.hasMoreElements()) {
ZipEntry entr = en.nextElement();
if(entr.getName().equals(entryName)) {
InputStream in = srcarchive.getInputStream(entr);
ZipOutputStream out = mapArchiveStreams.get(filename);
out.putNextEntry(new ZipEntry(entryName));
InterpreterUtil.copyInputStream(in, out);
in.close();
}
}
srcarchive.close();
} catch(IOException ex) {
DecompilerContext.getLogger().writeMessage("Error copying zip file entry: "+source+","+destpath+","+archivename+","+entryName, IFernflowerLogger.WARNING);
ex.printStackTrace();
}
}
boolean isOption = true;
for (int i = 0; i < args.length - 1; ++i) { // last parameter - destination
String arg = args[i];
public void copyFile(String source, String destpath, String destfilename) {
try {
InterpreterUtil.copyFile(new File(source), new File(destfilename));
} catch(IOException ex) {
ex.printStackTrace();
}
}
if (isOption && arg.startsWith("-") &&
arg.length() > 5 && arg.charAt(4) == '=') {
String value = arg.substring(5).toUpperCase();
if ("TRUE".equals(value)) {
value = "1";
}
else if ("FALSE".equals(value)) {
value = "0";
}
public void saveFile(String path, String filename, String content) {
try {
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(getAbsolutePath(path), filename)), "UTF8"));
out.write(content);
out.flush();
out.close();
} catch(IOException ex) {
ex.printStackTrace();
}
}
public void createArchive(String path, String archivename, Manifest manifest) {
mapOptions.put(arg.substring(1, 4), value);
}
else {
isOption = false;
try {
File file = new File(getAbsolutePath(path), archivename);
file.createNewFile();
ZipOutputStream out;
if(manifest != null) { // jar
out = new JarOutputStream(new FileOutputStream(file), manifest);
} else {
out = new ZipOutputStream(new FileOutputStream(file));
}
mapArchiveStreams.put(file.getAbsolutePath(), out);
} catch(IOException ex) {
ex.printStackTrace();
}
}
if (arg.startsWith("-e=")) {
lstLibraries.add(arg.substring(3));
}
else {
lstSources.add(arg);
}
}
}
public void saveClassEntry(String path, String archivename,
String qualifiedName, String entryName, String content) {
saveEntry(path, archivename, entryName, content);
}
if (lstSources.isEmpty()) {
printHelp();
}
else {
ConsoleDecompiler decompiler = new ConsoleDecompiler(
new PrintStreamLogger(IFernflowerLogger.INFO, System.out),
mapOptions);
public void saveClassFile(String path, String qualifiedName, String entryName, String content) {
saveFile(path, entryName, content);
}
for (String source : lstSources) {
decompiler.addSpace(new File(source), true);
}
public void saveEntry(String path, String archivename, String entryName,
String content) {
try {
String filename = new File(getAbsolutePath(path), archivename).getAbsolutePath();
if(!addEntryName(filename, entryName)) {
DecompilerContext.getLogger().writeMessage("Zip entry already exists: "+
path+","+archivename+","+entryName, IFernflowerLogger.WARNING);
return;
}
ZipOutputStream out = mapArchiveStreams.get(filename);
out.putNextEntry(new ZipEntry(entryName));
if(content != null) {
BufferedWriter outwriter = new BufferedWriter(new OutputStreamWriter(out, "UTF8"));
outwriter.write(content);
outwriter.flush();
}
} catch(IOException ex) {
ex.printStackTrace();
}
}
public void saveFolder(String path) {
File f = new File(getAbsolutePath(path));
f.mkdirs();
}
for (String library : lstLibraries) {
decompiler.addSpace(new File(library), false);
}
decompiler.decompileContext(new File(args[args.length - 1]));
}
}
else {
printHelp();
}
}
catch (Exception ex) {
ex.printStackTrace();
}
}
private static void printHelp() {
System.out.println("Usage: java ConsoleDecompiler ( -<option>=<value>)* (<source>)+ <destination>");
System.out.println("Example: java ConsoleDecompiler -dgs=true c:\\mysource\\ c:\\my.jar d:\\decompiled\\");
}
public void addSpace(File file, boolean isOwn) throws IOException {
fernflower.getStructcontext().addSpace(file, isOwn);
}
public void decompileContext(File root) {
this.root = root;
fernflower.decompileContext();
}
// *******************************************************************
// Interface IBytecodeProvider
// *******************************************************************
public InputStream getBytecodeStream(String externPath, String internPath) {
try {
File file = new File(externPath);
if (internPath == null) {
return new FileInputStream(file);
}
else { // archive file
ZipFile archive = new ZipFile(file);
Enumeration<? extends ZipEntry> en = archive.entries();
while (en.hasMoreElements()) {
ZipEntry entr = en.nextElement();
if (entr.getName().equals(internPath)) {
return archive.getInputStream(entr);
}
}
}
}
catch (IOException ex) {
ex.printStackTrace();
}
return null;
}
// *******************************************************************
// Interface IDecompilatSaver
// *******************************************************************
private String getAbsolutePath(String path) {
return new File(root, path).getAbsolutePath();
}
private boolean addEntryName(String filename, String entry) {
HashSet<String> set = mapArchiveEntries.get(filename);
if (set == null) {
mapArchiveEntries.put(filename, set = new HashSet<String>());
}
return set.add(entry);
}
public void copyEntry(String source, String destpath, String archivename, String entryName) {
try {
String filename = new File(getAbsolutePath(destpath), archivename).getAbsolutePath();
if (!addEntryName(filename, entryName)) {
DecompilerContext.getLogger().writeMessage("Zip entry already exists: " +
destpath + "," + archivename + "," + entryName, IFernflowerLogger.WARNING);
return;
}
ZipFile srcarchive = new ZipFile(new File(source));
Enumeration<? extends ZipEntry> en = srcarchive.entries();
while (en.hasMoreElements()) {
ZipEntry entr = en.nextElement();
if (entr.getName().equals(entryName)) {
InputStream in = srcarchive.getInputStream(entr);
ZipOutputStream out = mapArchiveStreams.get(filename);
out.putNextEntry(new ZipEntry(entryName));
InterpreterUtil.copyInputStream(in, out);
in.close();
}
}
srcarchive.close();
}
catch (IOException ex) {
DecompilerContext.getLogger()
.writeMessage("Error copying zip file entry: " + source + "," + destpath + "," + archivename + "," + entryName,
IFernflowerLogger.WARNING);
ex.printStackTrace();
}
}
public void copyFile(String source, String destpath, String destfilename) {
try {
InterpreterUtil.copyFile(new File(source), new File(destfilename));
}
catch (IOException ex) {
ex.printStackTrace();
}
}
public void saveFile(String path, String filename, String content) {
try {
BufferedWriter out =
new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(getAbsolutePath(path), filename)), "UTF8"));
out.write(content);
out.flush();
out.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
public void createArchive(String path, String archivename, Manifest manifest) {
try {
File file = new File(getAbsolutePath(path), archivename);
file.createNewFile();
ZipOutputStream out;
if (manifest != null) { // jar
out = new JarOutputStream(new FileOutputStream(file), manifest);
}
else {
out = new ZipOutputStream(new FileOutputStream(file));
}
mapArchiveStreams.put(file.getAbsolutePath(), out);
}
catch (IOException ex) {
ex.printStackTrace();
}
}
public void saveClassEntry(String path, String archivename,
String qualifiedName, String entryName, String content) {
saveEntry(path, archivename, entryName, content);
}
public void saveClassFile(String path, String qualifiedName, String entryName, String content) {
saveFile(path, entryName, content);
}
public void saveEntry(String path, String archivename, String entryName,
String content) {
try {
String filename = new File(getAbsolutePath(path), archivename).getAbsolutePath();
if (!addEntryName(filename, entryName)) {
DecompilerContext.getLogger().writeMessage("Zip entry already exists: " +
path + "," + archivename + "," + entryName, IFernflowerLogger.WARNING);
return;
}
ZipOutputStream out = mapArchiveStreams.get(filename);
out.putNextEntry(new ZipEntry(entryName));
if (content != null) {
BufferedWriter outwriter = new BufferedWriter(new OutputStreamWriter(out, "UTF8"));
outwriter.write(content);
outwriter.flush();
}
}
catch (IOException ex) {
ex.printStackTrace();
}
}
public void saveFolder(String path) {
File f = new File(getAbsolutePath(path));
f.mkdirs();
}
public void closeArchive(String path, String archivename) {
try {
String filename = new File(getAbsolutePath(path), archivename).getAbsolutePath();
mapArchiveEntries.remove(filename);
ZipOutputStream out = mapArchiveStreams.remove(filename);
public void closeArchive(String path, String archivename) {
try {
String filename = new File(getAbsolutePath(path), archivename).getAbsolutePath();
out.flush();
out.close();
} catch(IOException ex) {
ex.printStackTrace();
}
}
mapArchiveEntries.remove(filename);
ZipOutputStream out = mapArchiveStreams.remove(filename);
out.flush();
out.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}

View File

@@ -1,43 +1,43 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.decompiler;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.Fernflower;
import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider;
import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
public class IdeDecompiler {
private Fernflower fernflower;
public IdeDecompiler(IBytecodeProvider provider,
private Fernflower fernflower;
public IdeDecompiler(IBytecodeProvider provider,
IDecompilatSaver saver, IFernflowerLogger logger,
HashMap<String, Object> propertiesCustom) {
fernflower = new Fernflower(provider, saver, propertiesCustom);
DecompilerContext.setLogger(logger);
}
fernflower = new Fernflower(provider, saver, propertiesCustom);
DecompilerContext.setLogger(logger);
}
public void addSpace(File file, boolean isOwn) throws IOException {
fernflower.getStructcontext().addSpace(file, isOwn);
@@ -46,9 +46,9 @@ public class IdeDecompiler {
public void decompileContext() {
try {
fernflower.decompileContext();
} finally {
}
finally {
fernflower.clearContext();
}
}
}

View File

@@ -1,78 +1,78 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.decompiler;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
public class WebDecompiler extends ConsoleDecompiler {
private HashMap<String, File> mapInputFilenames = new HashMap<String, File>();
private HashSet<String> setClassFiles = new HashSet<String>();
private File root;
public WebDecompiler(IFernflowerLogger logger, HashMap<String, Object> propertiesCustom) {
super(logger, propertiesCustom);
}
private HashMap<String, File> mapInputFilenames = new HashMap<String, File>();
@Override
public void decompileContext(File root) {
this.root = root;
super.decompileContext(root);
}
@Override
public void copyFile(String source, String destpath, String destfilename) {
super.copyFile(source, destpath, destfilename);
mapInputFilenames.put(destfilename, new File(getAbsolutePath(destpath), destfilename));
}
private HashSet<String> setClassFiles = new HashSet<String>();
@Override
public void saveFile(String path, String filename, String content) {
super.saveFile(path, filename, content);
mapInputFilenames.put(setClassFiles.contains(filename)?
filename.substring(0, filename.lastIndexOf(".java"))+".class":
filename, new File(getAbsolutePath(path), filename));
}
@Override
public void saveClassFile(String path, String qualifiedName, String entryName, String content) {
setClassFiles.add(entryName);
saveFile(path, entryName, content);
}
@Override
public void closeArchive(String path, String archivename) {
super.closeArchive(path, archivename);
mapInputFilenames.put(archivename, new File(getAbsolutePath(path), archivename));
}
private File root;
private String getAbsolutePath(String path) {
return new File(root, path).getAbsolutePath();
}
public WebDecompiler(IFernflowerLogger logger, HashMap<String, Object> propertiesCustom) {
super(logger, propertiesCustom);
}
public HashMap<String, File> getMapInputFilenames() {
return mapInputFilenames;
}
@Override
public void decompileContext(File root) {
this.root = root;
super.decompileContext(root);
}
@Override
public void copyFile(String source, String destpath, String destfilename) {
super.copyFile(source, destpath, destfilename);
mapInputFilenames.put(destfilename, new File(getAbsolutePath(destpath), destfilename));
}
@Override
public void saveFile(String path, String filename, String content) {
super.saveFile(path, filename, content);
mapInputFilenames.put(setClassFiles.contains(filename) ?
filename.substring(0, filename.lastIndexOf(".java")) + ".class" :
filename, new File(getAbsolutePath(path), filename));
}
@Override
public void saveClassFile(String path, String qualifiedName, String entryName, String content) {
setClassFiles.add(entryName);
saveFile(path, entryName, content);
}
@Override
public void closeArchive(String path, String archivename) {
super.closeArchive(path, archivename);
mapInputFilenames.put(archivename, new File(getAbsolutePath(path), archivename));
}
private String getAbsolutePath(String path) {
return new File(root, path).getAbsolutePath();
}
public HashMap<String, File> getMapInputFilenames() {
return mapInputFilenames;
}
}

View File

@@ -1,83 +1,84 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.decompiler.helper;
import java.io.PrintStream;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.io.PrintStream;
public class PrintStreamLogger implements IFernflowerLogger {
private int severity;
private int indent;
private PrintStream stream;
public PrintStreamLogger(int severity, PrintStream stream) {
this.severity = severity;
this.indent = 0;
this.stream = stream;
}
public void writeMessage(String message, int severity) {
if (severity >= this.severity) {
stream.println(InterpreterUtil.getIndentString(indent) + names[severity] + ": " + message);
}
}
private int severity;
public void writeMessage(String message, Throwable t) {
t.printStackTrace(stream);
writeMessage(message, ERROR);
}
private int indent;
public void startClass(String classname) {
stream.println(InterpreterUtil.getIndentString(indent++)+"Processing class "+classname+" ...");
}
private PrintStream stream;
public void endClass() {
stream.println(InterpreterUtil.getIndentString(--indent)+"... proceeded.");
}
public PrintStreamLogger(int severity, PrintStream stream) {
this.severity = severity;
this.indent = 0;
this.stream = stream;
}
public void startWriteClass(String classname) {
stream.println(InterpreterUtil.getIndentString(indent++)+"Writing class "+classname+" ...");
}
public void endWriteClass() {
stream.println(InterpreterUtil.getIndentString(--indent)+"... written.");
}
public void startMethod(String method) {
if(severity <= INFO) {
stream.println(InterpreterUtil.getIndentString(indent)+"Processing method "+method+" ...");
}
}
public void writeMessage(String message, int severity) {
if (severity >= this.severity) {
stream.println(InterpreterUtil.getIndentString(indent) + names[severity] + ": " + message);
}
}
public void endMethod() {
if(severity <= INFO) {
stream.println(InterpreterUtil.getIndentString(indent)+"... proceeded.");
}
}
public void writeMessage(String message, Throwable t) {
t.printStackTrace(stream);
writeMessage(message, ERROR);
}
public int getSeverity() {
return severity;
}
public void startClass(String classname) {
stream.println(InterpreterUtil.getIndentString(indent++) + "Processing class " + classname + " ...");
}
public void setSeverity(int severity) {
this.severity = severity;
}
public void endClass() {
stream.println(InterpreterUtil.getIndentString(--indent) + "... proceeded.");
}
public void startWriteClass(String classname) {
stream.println(InterpreterUtil.getIndentString(indent++) + "Writing class " + classname + " ...");
}
public void endWriteClass() {
stream.println(InterpreterUtil.getIndentString(--indent) + "... written.");
}
public void startMethod(String method) {
if (severity <= INFO) {
stream.println(InterpreterUtil.getIndentString(indent) + "Processing method " + method + " ...");
}
}
public void endMethod() {
if (severity <= INFO) {
stream.println(InterpreterUtil.getIndentString(indent) + "... proceeded.");
}
}
public int getSeverity() {
return severity;
}
public void setSeverity(int severity) {
this.severity = severity;
}
}

View File

@@ -1,23 +1,23 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.extern;
import java.io.InputStream;
public interface IBytecodeProvider {
public InputStream getBytecodeStream(String externPath, String internPath);
public InputStream getBytecodeStream(String externPath, String internPath);
}

View File

@@ -1,39 +1,39 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.extern;
import java.util.jar.Manifest;
public interface IDecompilatSaver {
public void copyFile(String source, String destpath, String destfilename);
public void copyFile(String source, String destpath, String destfilename);
public void saveFolder(String path);
public void saveClassFile(String path, String qualifiedName, String entryName, String content);
public void saveFolder(String path);
public void saveFile(String path, String filename, String content);
public void createArchive(String path, String archivename, Manifest manifest);
public void saveClassFile(String path, String qualifiedName, String entryName, String content);
public void saveClassEntry(String path, String archivename, String qualifiedName, String entryName, String content);
public void saveFile(String path, String filename, String content);
public void saveEntry(String path, String archivename, String entryName, String content);
public void createArchive(String path, String archivename, Manifest manifest);
public void copyEntry(String source, String destpath, String archivename, String entry);
public void closeArchive(String path, String archivename);
public void saveClassEntry(String path, String archivename, String qualifiedName, String entryName, String content);
public void saveEntry(String path, String archivename, String entryName, String content);
public void copyEntry(String source, String destpath, String archivename, String entry);
public void closeArchive(String path, String archivename);
}

View File

@@ -1,56 +1,57 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.extern;
import java.util.HashMap;
public interface IFernflowerLogger {
public static final int TRACE = 1;
public static final int INFO = 2;
public static final int WARNING = 3;
public static final int ERROR = 4;
public static final int IMMEDIATE = 5;
public static final int TRACE = 1;
public static final int INFO = 2;
public static final int WARNING = 3;
public static final int ERROR = 4;
public static final int IMMEDIATE = 5;
public static final HashMap<String, Integer> mapLogLevel = new HashMap<String, Integer>() {{
put("TRACE", 1);
put("INFO", 2);
put("WARN", 3);
put("ERROR", 4);
put("IMME", 5);
}};
public static final String[] names = new String[] {""/*DUMMY ENTRY*/, "TRACE", "INFO", "WARNING", "ERROR", ""/*IMMEDIATE*/};
public void writeMessage(String message, int severity);
public static final HashMap<String, Integer> mapLogLevel = new HashMap<String, Integer>() {{
put("TRACE", 1);
put("INFO", 2);
put("WARN", 3);
put("ERROR", 4);
put("IMME", 5);
}};
public void writeMessage(String message, Throwable t);
public void startClass(String classname);
public static final String[] names = new String[]{""/*DUMMY ENTRY*/, "TRACE", "INFO", "WARNING", "ERROR", ""/*IMMEDIATE*/};
public void endClass();
public void writeMessage(String message, int severity);
public void startWriteClass(String classname);
public void writeMessage(String message, Throwable t);
public void endWriteClass();
public void startMethod(String method);
public void startClass(String classname);
public void endMethod();
public int getSeverity();
public void endClass();
public void setSeverity(int severity);
public void startWriteClass(String classname);
public void endWriteClass();
public void startMethod(String method);
public void endMethod();
public int getSeverity();
public void setSeverity(int severity);
}

View File

@@ -1,57 +1,58 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.extern;
public interface IFernflowerPreferences {
public static final String REMOVE_BRIDGE = "rbr";
public static final String REMOVE_SYNTHETIC = "rsy";
public static final String DECOMPILE_INNER = "din";
public static final String DECOMPILE_CLASS_1_4 = "dc4";
public static final String DECOMPILE_ASSERTIONS = "das";
public static final String HIDE_EMPTY_SUPER = "hes";
public static final String HIDE_DEFAULT_CONSTRUCTOR = "hdc";
public static final String DECOMPILE_GENERIC_SIGNATURES = "dgs";
public static final String OUTPUT_COPYRIGHT_COMMENT = "occ";
public static final String NO_EXCEPTIONS_RETURN = "ner";
public static final String DECOMPILE_ENUM = "den";
public static final String REMOVE_GETCLASS_NEW = "rgn";
public static final String LITERALS_AS_IS = "lit";
public static final String BOOLEAN_TRUE_ONE = "bto";
public static final String SYNTHETIC_NOT_SET = "nns";
public static final String UNDEFINED_PARAM_TYPE_OBJECT = "uto";
public static final String USE_DEBUG_VARNAMES = "udv";
public static final String MAX_PROCESSING_METHOD = "mpm";
public static final String REMOVE_EMPTY_RANGES = "rer";
public static final String ASCII_STRING_CHARACTERS = "asc";
public static final String FINALLY_DEINLINE = "fdi";
public static final String REMOVE_BRIDGE = "rbr";
public static final String REMOVE_SYNTHETIC = "rsy";
public static final String DECOMPILE_INNER = "din";
public static final String DECOMPILE_CLASS_1_4 = "dc4";
public static final String DECOMPILE_ASSERTIONS = "das";
public static final String HIDE_EMPTY_SUPER = "hes";
public static final String HIDE_DEFAULT_CONSTRUCTOR = "hdc";
public static final String DECOMPILE_GENERIC_SIGNATURES = "dgs";
public static final String OUTPUT_COPYRIGHT_COMMENT = "occ";
public static final String NO_EXCEPTIONS_RETURN = "ner";
public static final String DECOMPILE_ENUM = "den";
public static final String REMOVE_GETCLASS_NEW = "rgn";
public static final String LITERALS_AS_IS = "lit";
public static final String BOOLEAN_TRUE_ONE = "bto";
public static final String SYNTHETIC_NOT_SET = "nns";
public static final String UNDEFINED_PARAM_TYPE_OBJECT = "uto";
public static final String USE_DEBUG_VARNAMES = "udv";
public static final String MAX_PROCESSING_METHOD = "mpm";
public static final String REMOVE_EMPTY_RANGES = "rer";
public static final String ASCII_STRING_CHARACTERS = "asc";
public static final String FINALLY_CATCHALL = "FINALLY_CATCHALL";
public static final String FINALLY_SEMAPHOR = "FINALLY_SEMAPHOR";
public static final String FINALLY_DEINLINE = "fdi";
public static final String RENAME_ENTITIES = "ren";
public static final String USER_RENAMER_CLASS = "urc";
public static final String LOG_LEVEL = "log";
public static final String NEW_LINE_SEPARATOR = "nls";
public static final String IDEA_NOT_NULL_ANNOTATION = "inn";
public static final String LAMBDA_TO_ANONYMOUS_CLASS = "lac";
public static final String FINALLY_CATCHALL = "FINALLY_CATCHALL";
public static final String FINALLY_SEMAPHOR = "FINALLY_SEMAPHOR";
public static final String RENAME_ENTITIES = "ren";
public static final String USER_RENAMER_CLASS = "urc";
public static final String LOG_LEVEL = "log";
public static final String NEW_LINE_SEPARATOR = "nls";
public static final String IDEA_NOT_NULL_ANNOTATION = "inn";
public static final String LAMBDA_TO_ANONYMOUS_CLASS = "lac";
public static final String INDENT_STRING = "ind";
public static final String LINE_SEPARATOR_WIN = "\r\n";
public static final String LINE_SEPARATOR_LIN = "\n";
public static final String LINE_SEPARATOR_WIN = "\r\n";
public static final String LINE_SEPARATOR_LIN = "\n";
}

View File

@@ -1,20 +1,35 @@
/*
* 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.extern;
public interface IIdentifierRenamer {
public static final int ELEMENT_CLASS = 1;
public static final int ELEMENT_FIELD = 2;
public static final int ELEMENT_CLASS = 1;
public static final int ELEMENT_METHOD = 3;
public boolean toBeRenamed(int element_type, String classname, String element, String descriptor);
public String getNextClassname(String fullname, String shortname);
public String getNextFieldname(String classname, String field, String descriptor);
public static final int ELEMENT_FIELD = 2;
public String getNextMethodname(String classname, String method, String descriptor);
public static final int ELEMENT_METHOD = 3;
public boolean toBeRenamed(int element_type, String classname, String element, String descriptor);
public String getNextClassname(String fullname, String shortname);
public String getNextFieldname(String classname, String field, String descriptor);
public String getNextMethodname(String classname, String method, String descriptor);
}

View File

@@ -1,22 +1,20 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.rels;
import java.io.IOException;
import java.util.HashSet;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
@@ -36,181 +34,191 @@ import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.io.IOException;
import java.util.HashSet;
public class ClassWrapper {
private StructClass classStruct;
private HashSet<String> hideMembers = new HashSet<String>();
private VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<Exprent, String>();
private StructClass classStruct;
private VBStyleCollection<Exprent, String> dynamicFieldInitializers = new VBStyleCollection<Exprent, String>();
private VBStyleCollection<MethodWrapper, String> methods = new VBStyleCollection<MethodWrapper, String>();
public ClassWrapper(StructClass classStruct) {
this.classStruct = classStruct;
}
@SuppressWarnings("deprecation")
public void init() throws IOException {
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct);
DecompilerContext.getLogger().startClass(classStruct.qualifiedName);
private HashSet<String> hideMembers = new HashSet<String>();
// collect field names
HashSet<String> setFieldNames = new HashSet<String>();
for(StructField fd: classStruct.getFields()) {
setFieldNames.add(fd.getName());
}
for(StructMethod mt: classStruct.getMethods()) {
DecompilerContext.getLogger().startMethod(mt.getName()+" "+mt.getDescriptor());
VarNamesCollector vc = new VarNamesCollector();
DecompilerContext.setVarncollector(vc);
CounterContainer counter = new CounterContainer();
DecompilerContext.setCountercontainer(counter);
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD, mt);
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR, MethodDescriptor.parseDescriptor(mt.getDescriptor()));
VarProcessor varproc = new VarProcessor();
DecompilerContext.setProperty(DecompilerContext.CURRENT_VAR_PROCESSOR, varproc);
Thread mtthread = null;
RootStatement root = null;
boolean isError = false;
try {
if(mt.containsCode()) {
int maxsec = 10 * Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString());
if(maxsec == 0) { // blocking wait
root = MethodProcessorThread.codeToJava(mt, varproc);
} else {
MethodProcessorThread mtproc = new MethodProcessorThread(mt, varproc, DecompilerContext.getCurrentContext());
mtthread = new Thread(mtproc);
private VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<Exprent, String>();
mtthread.start();
private VBStyleCollection<Exprent, String> dynamicFieldInitializers = new VBStyleCollection<Exprent, String>();
int sec = 0;
while(mtthread.isAlive()) {
private VBStyleCollection<MethodWrapper, String> methods = new VBStyleCollection<MethodWrapper, String>();
synchronized(mtproc) {
mtproc.wait(100);
}
if(maxsec > 0 && ++sec > maxsec) {
DecompilerContext.getLogger().writeMessage("Processing time limit ("+maxsec+" sec.) for method " +
mt.getName()+" "+mt.getDescriptor()+ " exceeded, execution interrupted.", IFernflowerLogger.ERROR);
mtthread.stop();
isError = true;
break;
}
}
public ClassWrapper(StructClass classStruct) {
this.classStruct = classStruct;
}
if(!isError) {
if(mtproc.getError() != null) {
throw mtproc.getError();
} else {
root = mtproc.getRoot();
}
}
}
} else {
boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0;
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
int paramcount = 0;
if(thisvar) {
varproc.getThisvars().put(new VarVersionPaar(0,0), classStruct.qualifiedName);
paramcount = 1;
}
paramcount += md.params.length;
int varindex = 0;
for(int i=0;i<paramcount;i++) {
varproc.setVarName(new VarVersionPaar(varindex, 0), vc.getFreeName(varindex));
if(thisvar) {
if(i==0) {
varindex++;
} else {
varindex+=md.params[i-1].stack_size;
}
} else {
varindex+=md.params[i].stack_size;
}
}
}
@SuppressWarnings("deprecation")
public void init() throws IOException {
} catch(ThreadDeath ex) {
try {
if(mtthread != null) {
mtthread.stop();
}
} catch(Throwable ignored) { }
throw ex;
} catch(Throwable ex) {
DecompilerContext.getLogger().writeMessage("Method "+mt.getName()+" "+mt.getDescriptor()+" couldn't be decompiled.", ex);
isError = true;
}
MethodWrapper meth = new MethodWrapper(root, varproc, mt, counter);
meth.decompiledWithErrors = isError;
methods.addWithKey(meth, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct);
// rename vars so that no one has the same name as a field
varproc.refreshVarNames(new VarNamesCollector(setFieldNames));
DecompilerContext.getLogger().startClass(classStruct.qualifiedName);
// if debug information present and should be used
if(DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VARNAMES)) {
StructLocalVariableTableAttribute attr = (StructLocalVariableTableAttribute)mt.getAttributes().getWithKey(
StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE);
if(attr != null) {
varproc.setDebugVarNames(attr.getMapVarNames());
}
}
DecompilerContext.getLogger().endMethod();
}
// collect field names
HashSet<String> setFieldNames = new HashSet<String>();
for (StructField fd : classStruct.getFields()) {
setFieldNames.add(fd.getName());
}
DecompilerContext.getLogger().endClass();
}
public MethodWrapper getMethodWrapper(String name, String descriptor) {
return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor));
}
public StructClass getClassStruct() {
return classStruct;
}
for (StructMethod mt : classStruct.getMethods()) {
public VBStyleCollection<MethodWrapper, String> getMethods() {
return methods;
}
DecompilerContext.getLogger().startMethod(mt.getName() + " " + mt.getDescriptor());
public HashSet<String> getHideMembers() {
return hideMembers;
}
VarNamesCollector vc = new VarNamesCollector();
DecompilerContext.setVarncollector(vc);
public VBStyleCollection<Exprent, String> getStaticFieldInitializers() {
return staticFieldInitializers;
}
CounterContainer counter = new CounterContainer();
DecompilerContext.setCountercontainer(counter);
public VBStyleCollection<Exprent, String> getDynamicFieldInitializers() {
return dynamicFieldInitializers;
}
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD, mt);
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR, MethodDescriptor.parseDescriptor(mt.getDescriptor()));
VarProcessor varproc = new VarProcessor();
DecompilerContext.setProperty(DecompilerContext.CURRENT_VAR_PROCESSOR, varproc);
Thread mtthread = null;
RootStatement root = null;
boolean isError = false;
try {
if (mt.containsCode()) {
int maxsec = 10 * Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString());
if (maxsec == 0) { // blocking wait
root = MethodProcessorThread.codeToJava(mt, varproc);
}
else {
MethodProcessorThread mtproc = new MethodProcessorThread(mt, varproc, DecompilerContext.getCurrentContext());
mtthread = new Thread(mtproc);
mtthread.start();
int sec = 0;
while (mtthread.isAlive()) {
synchronized (mtproc) {
mtproc.wait(100);
}
if (maxsec > 0 && ++sec > maxsec) {
DecompilerContext.getLogger().writeMessage("Processing time limit (" + maxsec + " sec.) for method " +
mt.getName() + " " + mt.getDescriptor() + " exceeded, execution interrupted.",
IFernflowerLogger.ERROR);
mtthread.stop();
isError = true;
break;
}
}
if (!isError) {
if (mtproc.getError() != null) {
throw mtproc.getError();
}
else {
root = mtproc.getRoot();
}
}
}
}
else {
boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0;
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
int paramcount = 0;
if (thisvar) {
varproc.getThisvars().put(new VarVersionPaar(0, 0), classStruct.qualifiedName);
paramcount = 1;
}
paramcount += md.params.length;
int varindex = 0;
for (int i = 0; i < paramcount; i++) {
varproc.setVarName(new VarVersionPaar(varindex, 0), vc.getFreeName(varindex));
if (thisvar) {
if (i == 0) {
varindex++;
}
else {
varindex += md.params[i - 1].stack_size;
}
}
else {
varindex += md.params[i].stack_size;
}
}
}
}
catch (ThreadDeath ex) {
try {
if (mtthread != null) {
mtthread.stop();
}
}
catch (Throwable ignored) {
}
throw ex;
}
catch (Throwable ex) {
DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be decompiled.", ex);
isError = true;
}
MethodWrapper meth = new MethodWrapper(root, varproc, mt, counter);
meth.decompiledWithErrors = isError;
methods.addWithKey(meth, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));
// rename vars so that no one has the same name as a field
varproc.refreshVarNames(new VarNamesCollector(setFieldNames));
// if debug information present and should be used
if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VARNAMES)) {
StructLocalVariableTableAttribute attr = (StructLocalVariableTableAttribute)mt.getAttributes().getWithKey(
StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE);
if (attr != null) {
varproc.setDebugVarNames(attr.getMapVarNames());
}
}
DecompilerContext.getLogger().endMethod();
}
DecompilerContext.getLogger().endClass();
}
public MethodWrapper getMethodWrapper(String name, String descriptor) {
return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor));
}
public StructClass getClassStruct() {
return classStruct;
}
public VBStyleCollection<MethodWrapper, String> getMethods() {
return methods;
}
public HashSet<String> getHideMembers() {
return hideMembers;
}
public VBStyleCollection<Exprent, String> getStaticFieldInitializers() {
return staticFieldInitializers;
}
public VBStyleCollection<Exprent, String> getDynamicFieldInitializers() {
return dynamicFieldInitializers;
}
}

View File

@@ -1,12 +1,20 @@
/*
* 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.rels;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.code.Instruction;
import org.jetbrains.java.decompiler.code.InstructionSequence;
@@ -23,116 +31,121 @@ import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.io.IOException;
import java.util.*;
public class LambdaProcessor {
private static final String JAVAC_LAMBDA_CLASS = "java/lang/invoke/LambdaMetafactory";
private static final String JAVAC_LAMBDA_METHOD = "metafactory";
private static final String JAVAC_LAMBDA_METHOD_DESCRIPTOR = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;";
public void processClass(ClassNode node) throws IOException {
private static final String JAVAC_LAMBDA_CLASS = "java/lang/invoke/LambdaMetafactory";
private static final String JAVAC_LAMBDA_METHOD = "metafactory";
private static final String JAVAC_LAMBDA_METHOD_DESCRIPTOR =
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;";
for(ClassNode child : node.nested) {
processClass(child);
}
if(node.nested.isEmpty()) {
hasLambda(node);
}
}
public void processClass(ClassNode node) throws IOException {
public boolean hasLambda(ClassNode node) throws IOException {
for (ClassNode child : node.nested) {
processClass(child);
}
ClassesProcessor clprocessor = DecompilerContext.getClassprocessor();
StructClass cl = node.classStruct;
if(cl.getBytecodeVersion() < CodeConstants.BYTECODE_JAVA_8) { // lamda beginning with Java 8
return false;
}
StructBootstrapMethodsAttribute bootstrap = (StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
if(bootstrap == null || bootstrap.getMethodsNumber() == 0) {
return false; // no bootstrap constants in pool
}
if (node.nested.isEmpty()) {
hasLambda(node);
}
}
Set<Integer> lambda_methods = new HashSet<Integer>();
// find lambda bootstrap constants
for(int i = 0; i < bootstrap.getMethodsNumber(); ++i) {
LinkConstant method_ref = bootstrap.getMethodReference(i); // method handle
if(JAVAC_LAMBDA_CLASS.equals(method_ref.classname) &&
JAVAC_LAMBDA_METHOD.equals(method_ref.elementname) &&
JAVAC_LAMBDA_METHOD_DESCRIPTOR.equals(method_ref.descriptor)) { // check for javac lambda structure. FIXME: extend for Eclipse etc. at some point
lambda_methods.add(i);
}
}
if(lambda_methods.isEmpty()) {
return false; // no lambda bootstrap constant found
}
Map<String, String> mapMethodsLambda = new HashMap<String, String>();
// iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes.
for(StructMethod mt: cl.getMethods()) {
mt.expandData();
InstructionSequence seq = mt.getInstructionSequence();
if(seq != null && seq.length() > 0) {
int len = seq.length();
for(int i = 0; i < len; ++i) {
Instruction instr = seq.getInstr(i);
if(instr.opcode == CodeConstants.opc_invokedynamic) {
LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.getOperand(0));
if(lambda_methods.contains(invoke_dynamic.index1)) { // lambda invocation found
List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1);
MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor);
public boolean hasLambda(ClassNode node) throws IOException {
String lambda_class_name = md.ret.value;
String lambda_method_name = invoke_dynamic.elementname;
String lambda_method_descriptor = ((PrimitiveConstant)bootstrap_arguments.get(2)).getString(); // method type
LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1);
ClassesProcessor clprocessor = DecompilerContext.getClassprocessor();
StructClass cl = node.classStruct;
ClassNode node_lambda = clprocessor.new ClassNode(content_method_handle.classname, content_method_handle.elementname,
content_method_handle.descriptor, content_method_handle.index1,
lambda_class_name, lambda_method_name, lambda_method_descriptor, cl);
node_lambda.simpleName = cl.qualifiedName + "##Lambda_" + invoke_dynamic.index1 + "_" + invoke_dynamic.index2;
node_lambda.enclosingMethod = InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor());
if (cl.getBytecodeVersion() < CodeConstants.BYTECODE_JAVA_8) { // lamda beginning with Java 8
return false;
}
node.nested.add(node_lambda);
node_lambda.parent = node;
clprocessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda);
mapMethodsLambda.put(node_lambda.lambda_information.content_method_key, node_lambda.simpleName);
}
}
}
}
mt.releaseResources();
}
// build class hierarchy on lambda
for(ClassNode nd : node.nested) {
if(nd.type == ClassNode.CLASS_LAMBDA) {
String parent_class_name = mapMethodsLambda.get(nd.enclosingMethod);
if(parent_class_name != null) {
ClassNode parent_class = clprocessor.getMapRootClasses().get(parent_class_name);
parent_class.nested.add(nd);
nd.parent = parent_class;
}
}
}
// FIXME: mixed hierarchy?
return false;
}
StructBootstrapMethodsAttribute bootstrap =
(StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
if (bootstrap == null || bootstrap.getMethodsNumber() == 0) {
return false; // no bootstrap constants in pool
}
Set<Integer> lambda_methods = new HashSet<Integer>();
// find lambda bootstrap constants
for (int i = 0; i < bootstrap.getMethodsNumber(); ++i) {
LinkConstant method_ref = bootstrap.getMethodReference(i); // method handle
if (JAVAC_LAMBDA_CLASS.equals(method_ref.classname) &&
JAVAC_LAMBDA_METHOD.equals(method_ref.elementname) &&
JAVAC_LAMBDA_METHOD_DESCRIPTOR
.equals(method_ref.descriptor)) { // check for javac lambda structure. FIXME: extend for Eclipse etc. at some point
lambda_methods.add(i);
}
}
if (lambda_methods.isEmpty()) {
return false; // no lambda bootstrap constant found
}
Map<String, String> mapMethodsLambda = new HashMap<String, String>();
// iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes.
for (StructMethod mt : cl.getMethods()) {
mt.expandData();
InstructionSequence seq = mt.getInstructionSequence();
if (seq != null && seq.length() > 0) {
int len = seq.length();
for (int i = 0; i < len; ++i) {
Instruction instr = seq.getInstr(i);
if (instr.opcode == CodeConstants.opc_invokedynamic) {
LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.getOperand(0));
if (lambda_methods.contains(invoke_dynamic.index1)) { // lambda invocation found
List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1);
MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor);
String lambda_class_name = md.ret.value;
String lambda_method_name = invoke_dynamic.elementname;
String lambda_method_descriptor = ((PrimitiveConstant)bootstrap_arguments.get(2)).getString(); // method type
LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1);
ClassNode node_lambda = clprocessor.new ClassNode(content_method_handle.classname, content_method_handle.elementname,
content_method_handle.descriptor, content_method_handle.index1,
lambda_class_name, lambda_method_name, lambda_method_descriptor, cl);
node_lambda.simpleName = cl.qualifiedName + "##Lambda_" + invoke_dynamic.index1 + "_" + invoke_dynamic.index2;
node_lambda.enclosingMethod = InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor());
node.nested.add(node_lambda);
node_lambda.parent = node;
clprocessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda);
mapMethodsLambda.put(node_lambda.lambda_information.content_method_key, node_lambda.simpleName);
}
}
}
}
mt.releaseResources();
}
// build class hierarchy on lambda
for (ClassNode nd : node.nested) {
if (nd.type == ClassNode.CLASS_LAMBDA) {
String parent_class_name = mapMethodsLambda.get(nd.enclosingMethod);
if (parent_class_name != null) {
ClassNode parent_class = clprocessor.getMapRootClasses().get(parent_class_name);
parent_class.nested.add(nd);
nd.parent = parent_class;
}
}
}
// FIXME: mixed hierarchy?
return false;
}
}

View File

@@ -1,21 +1,20 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.rels;
import java.io.IOException;
import org.jetbrains.java.decompiler.code.InstructionSequence;
import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
import org.jetbrains.java.decompiler.main.DecompilerContext;
@@ -23,252 +22,239 @@ import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper;
import org.jetbrains.java.decompiler.modules.decompiler.ClearStructHelper;
import org.jetbrains.java.decompiler.modules.decompiler.DomHelper;
import org.jetbrains.java.decompiler.modules.decompiler.ExitHelper;
import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.FinallyProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.IdeaNotNullHelper;
import org.jetbrains.java.decompiler.modules.decompiler.IfHelper;
import org.jetbrains.java.decompiler.modules.decompiler.InlineSingleBlockHelper;
import org.jetbrains.java.decompiler.modules.decompiler.LabelHelper;
import org.jetbrains.java.decompiler.modules.decompiler.LoopExtractHelper;
import org.jetbrains.java.decompiler.modules.decompiler.MergeHelper;
import org.jetbrains.java.decompiler.modules.decompiler.PPandMMHelper;
import org.jetbrains.java.decompiler.modules.decompiler.SecondaryFunctionsHelper;
import org.jetbrains.java.decompiler.modules.decompiler.SequenceHelper;
import org.jetbrains.java.decompiler.modules.decompiler.StackVarsProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.*;
import org.jetbrains.java.decompiler.modules.decompiler.deobfuscator.ExceptionDeobfuscator;
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod;
import java.io.IOException;
public class MethodProcessorThread implements Runnable {
private StructMethod method;
private VarProcessor varproc;
private DecompilerContext parentContext;
private RootStatement root;
private Throwable error;
public MethodProcessorThread(StructMethod method, VarProcessor varproc,
DecompilerContext parentContext) {
this.method = method;
this.varproc = varproc;
this.parentContext = parentContext;
}
public void run() {
private StructMethod method;
private VarProcessor varproc;
private DecompilerContext parentContext;
DecompilerContext.setCurrentContext(parentContext);
error = null;
root = null;
try {
root = codeToJava(method, varproc);
private RootStatement root;
synchronized(this) {
this.notify();
}
} catch(ThreadDeath ex) {
;
} catch(Throwable ex) {
error = ex;
}
}
public static RootStatement codeToJava(StructMethod mt, VarProcessor varproc) throws IOException {
private Throwable error;
StructClass cl = mt.getClassStruct();
boolean isInitializer = "<clinit>".equals(mt.getName()); // for now static initializer only
mt.expandData();
InstructionSequence seq = mt.getInstructionSequence();
ControlFlowGraph graph = new ControlFlowGraph(seq);
public MethodProcessorThread(StructMethod method, VarProcessor varproc,
DecompilerContext parentContext) {
this.method = method;
this.varproc = varproc;
this.parentContext = parentContext;
}
// System.out.println(graph.toString());
// if(mt.getName().endsWith("_getActiveServers")) {
// System.out.println();
// }
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern1.dot"), true);
DeadCodeHelper.removeDeadBlocks(graph);
graph.inlineJsr(mt);
public void run() {
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern4.dot"), true);
DecompilerContext.setCurrentContext(parentContext);
// TODO: move to the start, before jsr inlining
DeadCodeHelper.connectDummyExitBlock(graph);
DeadCodeHelper.removeGotos(graph);
ExceptionDeobfuscator.removeCircularRanges(graph);
//DeadCodeHelper.removeCircularRanges(graph);
error = null;
root = null;
try {
root = codeToJava(method, varproc);
synchronized (this) {
this.notify();
}
}
catch (ThreadDeath ex) {
;
}
catch (Throwable ex) {
error = ex;
}
}
public static RootStatement codeToJava(StructMethod mt, VarProcessor varproc) throws IOException {
StructClass cl = mt.getClassStruct();
boolean isInitializer = "<clinit>".equals(mt.getName()); // for now static initializer only
mt.expandData();
InstructionSequence seq = mt.getInstructionSequence();
ControlFlowGraph graph = new ControlFlowGraph(seq);
// System.out.println(graph.toString());
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// if(mt.getName().endsWith("_getActiveServers")) {
// System.out.println();
// }
ExceptionDeobfuscator.restorePopRanges(graph);
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern1.dot"), true);
if(DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) {
ExceptionDeobfuscator.removeEmptyRanges(graph);
}
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
if(DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) {
// special case: single return instruction outside of a protected range
DeadCodeHelper.incorporateValueReturns(graph);
}
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
DeadCodeHelper.removeDeadBlocks(graph);
graph.inlineJsr(mt);
// ExceptionDeobfuscator.restorePopRanges(graph);
ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern4.dot"), true);
DeadCodeHelper.mergeBasicBlocks(graph);
// TODO: move to the start, before jsr inlining
DeadCodeHelper.connectDummyExitBlock(graph);
DecompilerContext.getCountercontainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables());
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
//System.out.println(graph.toString());
if(ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) {
DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.WARNING);
}
RootStatement root = DomHelper.parseGraph(graph);
if(!DecompilerContext.getOption(IFernflowerPreferences.FINALLY_CATCHALL)) {
FinallyProcessor fproc = new FinallyProcessor(varproc);
while(fproc.iterateGraph(mt, root, graph)) {
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern2.dot"), true);
//System.out.println(graph.toString());
DeadCodeHelper.removeGotos(graph);
ExceptionDeobfuscator.removeCircularRanges(graph);
//DeadCodeHelper.removeCircularRanges(graph);
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
root = DomHelper.parseGraph(graph);
}
}
// remove synchronized exception handler
// not until now because of comparison between synchronized statements in the finally cycle
DomHelper.removeSynchronizedHandler(root);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// System.out.println(graph.toString());
// LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>());
SequenceHelper.condenseSequences(root);
ClearStructHelper.clearStatements(root);
ExprProcessor proc = new ExprProcessor();
proc.processStatement(root, cl);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// System.out.println(graph.toString());
ExceptionDeobfuscator.restorePopRanges(graph);
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
for(;;) {
StackVarsProcessor stackproc = new StackVarsProcessor();
stackproc.simplifyStackVars(root, mt, cl);
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
varproc.setVarVersions(root);
if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) {
ExceptionDeobfuscator.removeEmptyRanges(graph);
}
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
if(!new PPandMMHelper().findPPandMM(root)) {
break;
}
}
for(;;) {
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
LabelHelper.cleanUpEdges(root);
for(;;) {
MergeHelper.enhanceLoops(root);
if(LoopExtractHelper.extractLoops(root)) {
continue;
}
if(!IfHelper.mergeAllIfs(root)) {
break;
}
}
if(DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) {
if(IdeaNotNullHelper.removeHardcodedChecks(root, mt)) {
SequenceHelper.condenseSequences(root);
StackVarsProcessor stackproc = new StackVarsProcessor();
stackproc.simplifyStackVars(root, mt, cl);
varproc.setVarVersions(root);
}
}
LabelHelper.identifyLabels(root);
if (DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) {
// special case: single return instruction outside of a protected range
DeadCodeHelper.incorporateValueReturns(graph);
}
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
if(InlineSingleBlockHelper.inlineSingleBlocks(root)) {
continue;
}
// initializer may have at most one return point, so no transformation of method exits permitted
if(isInitializer || !ExitHelper.condenseExits(root)) {
break;
}
// FIXME: !!
// if(!EliminateLoopsHelper.eliminateLoops(root)) {
// break;
// }
}
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
ExitHelper.removeRedundantReturns(root);
SecondaryFunctionsHelper.identifySecondaryFunctions(root);
varproc.setVarDefinitions(root);
// must be the last invocation, because it makes the statement structure inconsistent
// FIXME: new edge type needed
LabelHelper.replaceContinueWithBreak(root);
mt.releaseResources();
// ExceptionDeobfuscator.restorePopRanges(graph);
ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph);
// System.out.println("++++++++++++++++++++++/// \r\n"+root.toJava());
return root;
}
DeadCodeHelper.mergeBasicBlocks(graph);
public RootStatement getRoot() {
return root;
}
DecompilerContext.getCountercontainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables());
public Throwable getError() {
return error;
}
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
//System.out.println(graph.toString());
if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) {
DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.WARNING);
}
RootStatement root = DomHelper.parseGraph(graph);
if (!DecompilerContext.getOption(IFernflowerPreferences.FINALLY_CATCHALL)) {
FinallyProcessor fproc = new FinallyProcessor(varproc);
while (fproc.iterateGraph(mt, root, graph)) {
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern2.dot"), true);
//System.out.println(graph.toString());
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
root = DomHelper.parseGraph(graph);
}
}
// remove synchronized exception handler
// not until now because of comparison between synchronized statements in the finally cycle
DomHelper.removeSynchronizedHandler(root);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// System.out.println(graph.toString());
// LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>());
SequenceHelper.condenseSequences(root);
ClearStructHelper.clearStatements(root);
ExprProcessor proc = new ExprProcessor();
proc.processStatement(root, cl);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// System.out.println(graph.toString());
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
for (; ; ) {
StackVarsProcessor stackproc = new StackVarsProcessor();
stackproc.simplifyStackVars(root, mt, cl);
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
varproc.setVarVersions(root);
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
if (!new PPandMMHelper().findPPandMM(root)) {
break;
}
}
for (; ; ) {
LabelHelper.cleanUpEdges(root);
for (; ; ) {
MergeHelper.enhanceLoops(root);
if (LoopExtractHelper.extractLoops(root)) {
continue;
}
if (!IfHelper.mergeAllIfs(root)) {
break;
}
}
if (DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) {
if (IdeaNotNullHelper.removeHardcodedChecks(root, mt)) {
SequenceHelper.condenseSequences(root);
StackVarsProcessor stackproc = new StackVarsProcessor();
stackproc.simplifyStackVars(root, mt, cl);
varproc.setVarVersions(root);
}
}
LabelHelper.identifyLabels(root);
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
if (InlineSingleBlockHelper.inlineSingleBlocks(root)) {
continue;
}
// initializer may have at most one return point, so no transformation of method exits permitted
if (isInitializer || !ExitHelper.condenseExits(root)) {
break;
}
// FIXME: !!
// if(!EliminateLoopsHelper.eliminateLoops(root)) {
// break;
// }
}
ExitHelper.removeRedundantReturns(root);
SecondaryFunctionsHelper.identifySecondaryFunctions(root);
varproc.setVarDefinitions(root);
// must be the last invocation, because it makes the statement structure inconsistent
// FIXME: new edge type needed
LabelHelper.replaceContinueWithBreak(root);
mt.releaseResources();
// System.out.println("++++++++++++++++++++++/// \r\n"+root.toJava());
return root;
}
public RootStatement getRoot() {
return root;
}
public Throwable getError() {
return error;
}
}

View File

@@ -1,22 +1,20 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.rels;
import java.util.HashSet;
import java.util.List;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper;
@@ -25,38 +23,40 @@ import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
import org.jetbrains.java.decompiler.struct.StructMethod;
import java.util.HashSet;
import java.util.List;
public class MethodWrapper {
public RootStatement root;
public VarProcessor varproc;
public StructMethod methodStruct;
public CounterContainer counter;
public DirectGraph graph;
public List<VarVersionPaar> signatureFields;
public boolean decompiledWithErrors;
public HashSet<String> setOuterVarNames = new HashSet<String>();
public MethodWrapper(RootStatement root, VarProcessor varproc, StructMethod methodStruct, CounterContainer counter) {
this.root = root;
this.varproc = varproc;
this.methodStruct = methodStruct;
this.counter = counter;
}
public DirectGraph getOrBuildGraph() {
if(graph == null && root != null) {
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
graph = flatthelper.buildDirectGraph(root);
}
return graph;
}
public RootStatement root;
public VarProcessor varproc;
public StructMethod methodStruct;
public CounterContainer counter;
public DirectGraph graph;
public List<VarVersionPaar> signatureFields;
public boolean decompiledWithErrors;
public HashSet<String> setOuterVarNames = new HashSet<String>();
public MethodWrapper(RootStatement root, VarProcessor varproc, StructMethod methodStruct, CounterContainer counter) {
this.root = root;
this.varproc = varproc;
this.methodStruct = methodStruct;
this.counter = counter;
}
public DirectGraph getOrBuildGraph() {
if (graph == null && root != null) {
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
graph = flatthelper.buildDirectGraph(root);
}
return graph;
}
}

View File

@@ -1,35 +1,27 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* 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
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* 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.rels;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
@@ -37,421 +29,426 @@ import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
public class NestedMemberAccess {
private static final int METHOD_ACCESS_NORMAL = 1;
private static final int METHOD_ACCESS_FIELDGET = 2;
private static final int METHOD_ACCESS_FIELDSET = 3;
private static final int METHOD_ACCESS_METHOD = 4;
private boolean notSetSync;
private HashMap<MethodWrapper, Integer> mapMethodType = new HashMap<MethodWrapper, Integer>();
public void propagateMemberAccess(ClassNode root) {
if(root.nested.isEmpty()) {
return;
}
private static final int METHOD_ACCESS_NORMAL = 1;
private static final int METHOD_ACCESS_FIELDGET = 2;
private static final int METHOD_ACCESS_FIELDSET = 3;
private static final int METHOD_ACCESS_METHOD = 4;
notSetSync = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
computeMethodTypes(root);
eliminateStaticAccess(root);
}
private void computeMethodTypes(ClassNode node) {
if(node.type == ClassNode.CLASS_LAMBDA) {
return;
}
for(ClassNode nd : node.nested) {
computeMethodTypes(nd);
}
for(MethodWrapper meth : node.wrapper.getMethods()) {
computeMethodType(node, meth);
}
}
private void computeMethodType(ClassNode node, MethodWrapper meth) {
private boolean notSetSync;
int type = METHOD_ACCESS_NORMAL;
if(meth.root != null) {
DirectGraph graph = meth.getOrBuildGraph();
int flags = meth.methodStruct.getAccessFlags();
if(((flags & CodeConstants.ACC_SYNTHETIC) != 0 || meth.methodStruct.getAttributes().containsKey("Synthetic") || notSetSync) &&
(flags & CodeConstants.ACC_STATIC) != 0) {
if(graph.nodes.size() == 2) { // incl. dummy exit node
if(graph.first.exprents.size() == 1) {
Exprent exprent = graph.first.exprents.get(0);
MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor());
int parcount = mtdesc.params.length;
Exprent exprCore = exprent;
if(exprent.type == Exprent.EXPRENT_EXIT) {
ExitExprent exexpr = (ExitExprent)exprent;
if(exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {
exprCore = exexpr.getValue();
}
}
switch(exprCore.type) {
case Exprent.EXPRENT_FIELD:
FieldExprent fexpr = (FieldExprent)exprCore;
if((parcount == 1 && !fexpr.isStatic()) ||
(parcount == 0 && fexpr.isStatic())) {
if(fexpr.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if(fexpr.isStatic() || (fexpr.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpr.getInstance()).getIndex() == 0)) {
type = METHOD_ACCESS_FIELDGET;
}
}
}
break;
case Exprent.EXPRENT_VAR: // qualified this
if(parcount == 1) {
// this or final variable
if(((VarExprent)exprCore).getIndex() != 0) {
type = METHOD_ACCESS_FIELDGET;
}
}
break;
case Exprent.EXPRENT_INVOCATION:
type = METHOD_ACCESS_METHOD;
break;
case Exprent.EXPRENT_ASSIGNMENT:
AssignmentExprent asexpr = (AssignmentExprent)exprCore;
if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fexpras = (FieldExprent)asexpr.getLeft();
if((parcount == 2 && !fexpras.isStatic()) ||
(parcount == 1 && fexpras.isStatic())) {
if(fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if(fexpras.isStatic() || (fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {
if(((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
type = METHOD_ACCESS_FIELDSET;
}
}
}
}
}
}
private HashMap<MethodWrapper, Integer> mapMethodType = new HashMap<MethodWrapper, Integer>();
if(type == METHOD_ACCESS_METHOD) { // FIXME: check for private flag of the method
public void propagateMemberAccess(ClassNode root) {
type = METHOD_ACCESS_NORMAL;
if (root.nested.isEmpty()) {
return;
}
InvocationExprent invexpr = (InvocationExprent)exprCore;
notSetSync = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
if((invexpr.isStatic() && invexpr.getLstParameters().size() == parcount) || (!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR
&& ((VarExprent)invexpr.getInstance()).getIndex() == 0 && invexpr.getLstParameters().size() == parcount-1)) {
computeMethodTypes(root);
boolean equalpars = true;
eliminateStaticAccess(root);
}
for(int i=0;i<invexpr.getLstParameters().size();i++) {
Exprent parexpr = invexpr.getLstParameters().get(i);
if(parexpr.type != Exprent.EXPRENT_VAR ||
((VarExprent)parexpr).getIndex() != i + (invexpr.isStatic()?0:1)) {
equalpars = false;
break;
}
}
if(equalpars) {
type = METHOD_ACCESS_METHOD;
}
}
}
} else if(graph.first.exprents.size() == 2) {
Exprent exprentFirst = graph.first.exprents.get(0);
Exprent exprentSecond = graph.first.exprents.get(1);
private void computeMethodTypes(ClassNode node) {
if(exprentFirst.type == Exprent.EXPRENT_ASSIGNMENT &&
exprentSecond.type == Exprent.EXPRENT_EXIT) {
MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor());
int parcount = mtdesc.params.length;
if (node.type == ClassNode.CLASS_LAMBDA) {
return;
}
AssignmentExprent asexpr = (AssignmentExprent)exprentFirst;
if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fexpras = (FieldExprent)asexpr.getLeft();
if((parcount == 2 && !fexpras.isStatic()) ||
(parcount == 1 && fexpras.isStatic())) {
if(fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if(fexpras.isStatic() || (fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {
if(((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
ExitExprent exexpr = (ExitExprent)exprentSecond;
if(exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {
if(exexpr.getValue().type == Exprent.EXPRENT_VAR &&
((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
type = METHOD_ACCESS_FIELDSET;
}
}
}
}
}
}
}
}
}
}
}
}
for (ClassNode nd : node.nested) {
computeMethodTypes(nd);
}
if(type != METHOD_ACCESS_NORMAL) {
mapMethodType.put(meth, type);
} else {
mapMethodType.remove(meth);
}
}
private void eliminateStaticAccess(ClassNode node) {
if(node.type == ClassNode.CLASS_LAMBDA) {
return;
}
for(MethodWrapper meth : node.wrapper.getMethods()) {
if(meth.root != null) {
boolean replaced = false;
DirectGraph graph = meth.getOrBuildGraph();
HashSet<DirectNode> setVisited = new HashSet<DirectNode>();
LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
stack.add(graph.first);
while(!stack.isEmpty()) { // TODO: replace with interface iterator?
DirectNode nd = stack.removeFirst();
if(setVisited.contains(nd)) {
continue;
}
setVisited.add(nd);
for(int i=0;i<nd.exprents.size();i++) {
Exprent exprent = nd.exprents.get(i);
replaced |= replaceInvocations(node, meth, exprent);
if(exprent.type == Exprent.EXPRENT_INVOCATION) {
Exprent ret = replaceAccessExprent(node, meth, (InvocationExprent)exprent);
if(ret != null) {
nd.exprents.set(i, ret);
replaced = true;
}
}
}
for(DirectNode ndx: nd.succs) {
stack.add(ndx);
}
}
if(replaced) {
computeMethodType(node, meth);
}
}
}
for(ClassNode child : node.nested) {
eliminateStaticAccess(child);
}
}
private boolean replaceInvocations(ClassNode caller, MethodWrapper meth, Exprent exprent) {
boolean res = false;
for(Exprent expr : exprent.getAllExprents()) {
res |= replaceInvocations(caller, meth, expr);
}
for(;;) {
boolean found = false;
for(Exprent expr : exprent.getAllExprents()) {
if(expr.type == Exprent.EXPRENT_INVOCATION) {
Exprent newexpr = replaceAccessExprent(caller, meth, (InvocationExprent)expr);
if(newexpr != null) {
exprent.replaceExprent(expr, newexpr);
found = true;
res = true;
break;
}
}
}
if(!found) {
break;
}
}
return res;
}
private boolean sameTree(ClassNode caller, ClassNode callee) {
if(caller.classStruct.qualifiedName.equals(callee.classStruct.qualifiedName)) {
return false;
}
while(caller.parent != null) {
caller = caller.parent;
}
while(callee.parent != null) {
callee = callee.parent;
}
return caller == callee;
}
for (MethodWrapper meth : node.wrapper.getMethods()) {
computeMethodType(node, meth);
}
}
private Exprent replaceAccessExprent(ClassNode caller, MethodWrapper methdest, InvocationExprent invexpr) {
ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(invexpr.getClassname());
MethodWrapper methsource = null;
if(node != null && node.wrapper != null) {
methsource = node.wrapper.getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor());
}
if(methsource == null || !mapMethodType.containsKey(methsource)) {
return null;
}
// if same method, return
if(node.classStruct.qualifiedName.equals(caller.classStruct.qualifiedName) &&
methsource.methodStruct.getName().equals(methdest.methodStruct.getName()) &&
methsource.methodStruct.getDescriptor().equals(methdest.methodStruct.getDescriptor())) {
// no recursive invocations permitted!
return null;
}
int type = mapMethodType.get(methsource);
// // FIXME: impossible case. METHOD_ACCESS_NORMAL is not saved in the map
// if(type == METHOD_ACCESS_NORMAL) {
// return null;
// }
if(!sameTree(caller, node)) {
return null;
}
DirectGraph graph = methsource.getOrBuildGraph();
Exprent source = graph.first.exprents.get(0);
Exprent retexprent = null;
switch(type) {
case METHOD_ACCESS_FIELDGET:
ExitExprent exsource = (ExitExprent)source;
if(exsource.getValue().type == Exprent.EXPRENT_VAR) { // qualified this
VarExprent var = (VarExprent)exsource.getValue();
String varname = methsource.varproc.getVarName(new VarVersionPaar(var));
if(!methdest.setOuterVarNames.contains(varname)) {
VarNamesCollector vnc = new VarNamesCollector();
vnc.addName(varname);
methdest.varproc.refreshVarNames(vnc);
methdest.setOuterVarNames.add(varname);
}
int index = methdest.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER);
VarExprent ret = new VarExprent(index, var.getVartype(), methdest.varproc);
methdest.varproc.setVarName(new VarVersionPaar(index, 0), varname);
retexprent = ret;
} else { // field
FieldExprent ret = (FieldExprent)exsource.getValue().copy();
if(!ret.isStatic()) {
ret.replaceExprent(ret.getInstance(), invexpr.getLstParameters().get(0));
}
retexprent = ret;
}
break;
case METHOD_ACCESS_FIELDSET:
AssignmentExprent ret;
if(source.type == Exprent.EXPRENT_EXIT) {
ExitExprent extex = (ExitExprent)source;
ret = (AssignmentExprent)((AssignmentExprent)extex.getValue()).copy();
} else {
ret = (AssignmentExprent)((AssignmentExprent)source).copy();
}
FieldExprent fexpr = (FieldExprent)ret.getLeft();
if(fexpr.isStatic()) {
ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(0));
} else {
ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(1));
fexpr.replaceExprent(fexpr.getInstance(), invexpr.getLstParameters().get(0));
}
retexprent = ret;
break;
case METHOD_ACCESS_METHOD:
if(source.type == Exprent.EXPRENT_EXIT) {
source = ((ExitExprent)source).getValue();
}
InvocationExprent invret = (InvocationExprent)source.copy();
int index = 0;
if(!invret.isStatic()) {
invret.replaceExprent(invret.getInstance(), invexpr.getLstParameters().get(0));
index = 1;
}
for(int i=0;i<invret.getLstParameters().size();i++) {
invret.replaceExprent(invret.getLstParameters().get(i), invexpr.getLstParameters().get(i + index));
}
retexprent = invret;
}
if(retexprent != null) {
// hide synthetic access method
boolean hide = true;
if(node.type == ClassNode.CLASS_ROOT || (node.access & CodeConstants.ACC_STATIC) != 0) {
StructMethod mt = methsource.methodStruct;
if((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) == 0 && !mt.getAttributes().containsKey("Synthetic")) {
hide = false;
}
}
if(hide) {
node.wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor()));
}
}
return retexprent;
}
private void computeMethodType(ClassNode node, MethodWrapper meth) {
int type = METHOD_ACCESS_NORMAL;
if (meth.root != null) {
DirectGraph graph = meth.getOrBuildGraph();
int flags = meth.methodStruct.getAccessFlags();
if (((flags & CodeConstants.ACC_SYNTHETIC) != 0 || meth.methodStruct.getAttributes().containsKey("Synthetic") || notSetSync) &&
(flags & CodeConstants.ACC_STATIC) != 0) {
if (graph.nodes.size() == 2) { // incl. dummy exit node
if (graph.first.exprents.size() == 1) {
Exprent exprent = graph.first.exprents.get(0);
MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor());
int parcount = mtdesc.params.length;
Exprent exprCore = exprent;
if (exprent.type == Exprent.EXPRENT_EXIT) {
ExitExprent exexpr = (ExitExprent)exprent;
if (exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {
exprCore = exexpr.getValue();
}
}
switch (exprCore.type) {
case Exprent.EXPRENT_FIELD:
FieldExprent fexpr = (FieldExprent)exprCore;
if ((parcount == 1 && !fexpr.isStatic()) ||
(parcount == 0 && fexpr.isStatic())) {
if (fexpr.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if (fexpr.isStatic() ||
(fexpr.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpr.getInstance()).getIndex() == 0)) {
type = METHOD_ACCESS_FIELDGET;
}
}
}
break;
case Exprent.EXPRENT_VAR: // qualified this
if (parcount == 1) {
// this or final variable
if (((VarExprent)exprCore).getIndex() != 0) {
type = METHOD_ACCESS_FIELDGET;
}
}
break;
case Exprent.EXPRENT_INVOCATION:
type = METHOD_ACCESS_METHOD;
break;
case Exprent.EXPRENT_ASSIGNMENT:
AssignmentExprent asexpr = (AssignmentExprent)exprCore;
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fexpras = (FieldExprent)asexpr.getLeft();
if ((parcount == 2 && !fexpras.isStatic()) ||
(parcount == 1 && fexpras.isStatic())) {
if (fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if (fexpras.isStatic() ||
(fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {
if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
type = METHOD_ACCESS_FIELDSET;
}
}
}
}
}
}
if (type == METHOD_ACCESS_METHOD) { // FIXME: check for private flag of the method
type = METHOD_ACCESS_NORMAL;
InvocationExprent invexpr = (InvocationExprent)exprCore;
if ((invexpr.isStatic() && invexpr.getLstParameters().size() == parcount) ||
(!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR
&& ((VarExprent)invexpr.getInstance()).getIndex() == 0 && invexpr.getLstParameters().size() == parcount - 1)) {
boolean equalpars = true;
for (int i = 0; i < invexpr.getLstParameters().size(); i++) {
Exprent parexpr = invexpr.getLstParameters().get(i);
if (parexpr.type != Exprent.EXPRENT_VAR ||
((VarExprent)parexpr).getIndex() != i + (invexpr.isStatic() ? 0 : 1)) {
equalpars = false;
break;
}
}
if (equalpars) {
type = METHOD_ACCESS_METHOD;
}
}
}
}
else if (graph.first.exprents.size() == 2) {
Exprent exprentFirst = graph.first.exprents.get(0);
Exprent exprentSecond = graph.first.exprents.get(1);
if (exprentFirst.type == Exprent.EXPRENT_ASSIGNMENT &&
exprentSecond.type == Exprent.EXPRENT_EXIT) {
MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor());
int parcount = mtdesc.params.length;
AssignmentExprent asexpr = (AssignmentExprent)exprentFirst;
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fexpras = (FieldExprent)asexpr.getLeft();
if ((parcount == 2 && !fexpras.isStatic()) ||
(parcount == 1 && fexpras.isStatic())) {
if (fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if (fexpras.isStatic() ||
(fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {
if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
ExitExprent exexpr = (ExitExprent)exprentSecond;
if (exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {
if (exexpr.getValue().type == Exprent.EXPRENT_VAR &&
((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
type = METHOD_ACCESS_FIELDSET;
}
}
}
}
}
}
}
}
}
}
}
}
if (type != METHOD_ACCESS_NORMAL) {
mapMethodType.put(meth, type);
}
else {
mapMethodType.remove(meth);
}
}
private void eliminateStaticAccess(ClassNode node) {
if (node.type == ClassNode.CLASS_LAMBDA) {
return;
}
for (MethodWrapper meth : node.wrapper.getMethods()) {
if (meth.root != null) {
boolean replaced = false;
DirectGraph graph = meth.getOrBuildGraph();
HashSet<DirectNode> setVisited = new HashSet<DirectNode>();
LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
stack.add(graph.first);
while (!stack.isEmpty()) { // TODO: replace with interface iterator?
DirectNode nd = stack.removeFirst();
if (setVisited.contains(nd)) {
continue;
}
setVisited.add(nd);
for (int i = 0; i < nd.exprents.size(); i++) {
Exprent exprent = nd.exprents.get(i);
replaced |= replaceInvocations(node, meth, exprent);
if (exprent.type == Exprent.EXPRENT_INVOCATION) {
Exprent ret = replaceAccessExprent(node, meth, (InvocationExprent)exprent);
if (ret != null) {
nd.exprents.set(i, ret);
replaced = true;
}
}
}
for (DirectNode ndx : nd.succs) {
stack.add(ndx);
}
}
if (replaced) {
computeMethodType(node, meth);
}
}
}
for (ClassNode child : node.nested) {
eliminateStaticAccess(child);
}
}
private boolean replaceInvocations(ClassNode caller, MethodWrapper meth, Exprent exprent) {
boolean res = false;
for (Exprent expr : exprent.getAllExprents()) {
res |= replaceInvocations(caller, meth, expr);
}
for (; ; ) {
boolean found = false;
for (Exprent expr : exprent.getAllExprents()) {
if (expr.type == Exprent.EXPRENT_INVOCATION) {
Exprent newexpr = replaceAccessExprent(caller, meth, (InvocationExprent)expr);
if (newexpr != null) {
exprent.replaceExprent(expr, newexpr);
found = true;
res = true;
break;
}
}
}
if (!found) {
break;
}
}
return res;
}
private boolean sameTree(ClassNode caller, ClassNode callee) {
if (caller.classStruct.qualifiedName.equals(callee.classStruct.qualifiedName)) {
return false;
}
while (caller.parent != null) {
caller = caller.parent;
}
while (callee.parent != null) {
callee = callee.parent;
}
return caller == callee;
}
private Exprent replaceAccessExprent(ClassNode caller, MethodWrapper methdest, InvocationExprent invexpr) {
ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(invexpr.getClassname());
MethodWrapper methsource = null;
if (node != null && node.wrapper != null) {
methsource = node.wrapper.getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor());
}
if (methsource == null || !mapMethodType.containsKey(methsource)) {
return null;
}
// if same method, return
if (node.classStruct.qualifiedName.equals(caller.classStruct.qualifiedName) &&
methsource.methodStruct.getName().equals(methdest.methodStruct.getName()) &&
methsource.methodStruct.getDescriptor().equals(methdest.methodStruct.getDescriptor())) {
// no recursive invocations permitted!
return null;
}
int type = mapMethodType.get(methsource);
// // FIXME: impossible case. METHOD_ACCESS_NORMAL is not saved in the map
// if(type == METHOD_ACCESS_NORMAL) {
// return null;
// }
if (!sameTree(caller, node)) {
return null;
}
DirectGraph graph = methsource.getOrBuildGraph();
Exprent source = graph.first.exprents.get(0);
Exprent retexprent = null;
switch (type) {
case METHOD_ACCESS_FIELDGET:
ExitExprent exsource = (ExitExprent)source;
if (exsource.getValue().type == Exprent.EXPRENT_VAR) { // qualified this
VarExprent var = (VarExprent)exsource.getValue();
String varname = methsource.varproc.getVarName(new VarVersionPaar(var));
if (!methdest.setOuterVarNames.contains(varname)) {
VarNamesCollector vnc = new VarNamesCollector();
vnc.addName(varname);
methdest.varproc.refreshVarNames(vnc);
methdest.setOuterVarNames.add(varname);
}
int index = methdest.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER);
VarExprent ret = new VarExprent(index, var.getVartype(), methdest.varproc);
methdest.varproc.setVarName(new VarVersionPaar(index, 0), varname);
retexprent = ret;
}
else { // field
FieldExprent ret = (FieldExprent)exsource.getValue().copy();
if (!ret.isStatic()) {
ret.replaceExprent(ret.getInstance(), invexpr.getLstParameters().get(0));
}
retexprent = ret;
}
break;
case METHOD_ACCESS_FIELDSET:
AssignmentExprent ret;
if (source.type == Exprent.EXPRENT_EXIT) {
ExitExprent extex = (ExitExprent)source;
ret = (AssignmentExprent)((AssignmentExprent)extex.getValue()).copy();
}
else {
ret = (AssignmentExprent)((AssignmentExprent)source).copy();
}
FieldExprent fexpr = (FieldExprent)ret.getLeft();
if (fexpr.isStatic()) {
ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(0));
}
else {
ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(1));
fexpr.replaceExprent(fexpr.getInstance(), invexpr.getLstParameters().get(0));
}
retexprent = ret;
break;
case METHOD_ACCESS_METHOD:
if (source.type == Exprent.EXPRENT_EXIT) {
source = ((ExitExprent)source).getValue();
}
InvocationExprent invret = (InvocationExprent)source.copy();
int index = 0;
if (!invret.isStatic()) {
invret.replaceExprent(invret.getInstance(), invexpr.getLstParameters().get(0));
index = 1;
}
for (int i = 0; i < invret.getLstParameters().size(); i++) {
invret.replaceExprent(invret.getLstParameters().get(i), invexpr.getLstParameters().get(i + index));
}
retexprent = invret;
}
if (retexprent != null) {
// hide synthetic access method
boolean hide = true;
if (node.type == ClassNode.CLASS_ROOT || (node.access & CodeConstants.ACC_STATIC) != 0) {
StructMethod mt = methsource.methodStruct;
if ((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) == 0 && !mt.getAttributes().containsKey("Synthetic")) {
hide = false;
}
}
if (hide) {
node.wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor()));
}
}
return retexprent;
}
}