java-decompiler: post-import cleanup (formatting and copyright)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user