diff options
Diffstat (limited to 'src/org/jetbrains/java/decompiler/main')
26 files changed, 5635 insertions, 5572 deletions
diff --git a/src/org/jetbrains/java/decompiler/main/AssertProcessor.java b/src/org/jetbrains/java/decompiler/main/AssertProcessor.java index 86e5c59..265ca5c 100644 --- a/src/org/jetbrains/java/decompiler/main/AssertProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/AssertProcessor.java @@ -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) { - - ClassWrapper wrapper = node.wrapper; - - StructField field = findAssertionField(node); - - if(field != null) { - - 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); - - for(StructField fd: wrapper.getClassStruct().getFields()) { - - String keyField = InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()); - - // initializer exists - if(wrapper.getStaticFieldInitializers().containsKey(keyField)) { - - 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; - } + + private static final VarType CLASS_ASSERTION_ERROR = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/AssertionError"); + + public static void buildAssertions(ClassNode node) { + + ClassWrapper wrapper = node.wrapper; + + StructField field = findAssertionField(node); + + if (field != null) { + + 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); + + for (StructField fd : wrapper.getClassStruct().getFields()) { + + String keyField = InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()); + + // initializer exists + if (wrapper.getStaticFieldInitializers().containsKey(keyField)) { + + 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; + } } diff --git a/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java b/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java index cdad6ca..411789c 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java @@ -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); - - InvocationExprent constr = new InvocationExprent(); - constr.setName("<init>"); - constr.setClassname("java/lang/NoClassDefFoundError"); - constr.setStringDescriptor("()V"); - constr.setFunctype(InvocationExprent.TYP_INIT); - constr.setDescriptor(MethodDescriptor.parseDescriptor("()V")); - - NewExprent newexpr = new NewExprent(new VarType(CodeConstants.TYPE_OBJECT,0,"java/lang/NoClassDefFoundError"), new ArrayList<Exprent>()); - newexpr.setConstructor(constr); - - InvocationExprent invcause = new InvocationExprent(); - invcause.setName("initCause"); - invcause.setClassname("java/lang/NoClassDefFoundError"); - invcause.setStringDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); - invcause.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;")); - invcause.setInstance(newexpr); - invcause.setLstParameters(Arrays.asList(new Exprent[] {new VarExprent(2, new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"), null)})); - - handlerexprent = new ExitExprent(ExitExprent.EXIT_THROW, - invcause, - null); - } - - - public void processClassReferences(ClassNode node) { - - ClassWrapper wrapper = node.wrapper; - -// int major_version = wrapper.getClassStruct().major_version; -// int minor_version = wrapper.getClassStruct().minor_version; -// -// if(major_version > 48 || (major_version == 48 && minor_version > 0)) { -// // version 1.5 or above -// return; -// } - - if(wrapper.getClassStruct().isVersionGE_1_5()) { - // version 1.5 or above - return; - } - - // find the synthetic method Class class$(String) if present - HashMap<ClassWrapper, MethodWrapper> mapClassMeths = new HashMap<ClassWrapper, MethodWrapper>(); - 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; - } + + public ExitExprent bodyexprent; + + public ExitExprent handlerexprent; + + + public ClassReference14Processor() { + + InvocationExprent invfor = new InvocationExprent(); + invfor.setName("forName"); + invfor.setClassname("java/lang/Class"); + invfor.setStringDescriptor("(Ljava/lang/String;)Ljava/lang/Class;"); + invfor.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/String;)Ljava/lang/Class;")); + invfor.setStatic(true); + invfor.setLstParameters(Arrays.asList(new Exprent[]{new VarExprent(0, VarType.VARTYPE_STRING, null)})); + + bodyexprent = new ExitExprent(ExitExprent.EXIT_RETURN, + invfor, + VarType.VARTYPE_CLASS); + + InvocationExprent constr = new InvocationExprent(); + constr.setName("<init>"); + constr.setClassname("java/lang/NoClassDefFoundError"); + constr.setStringDescriptor("()V"); + constr.setFunctype(InvocationExprent.TYP_INIT); + constr.setDescriptor(MethodDescriptor.parseDescriptor("()V")); + + NewExprent newexpr = + new NewExprent(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/NoClassDefFoundError"), new ArrayList<Exprent>()); + newexpr.setConstructor(constr); + + InvocationExprent invcause = new InvocationExprent(); + invcause.setName("initCause"); + invcause.setClassname("java/lang/NoClassDefFoundError"); + invcause.setStringDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); + invcause.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;")); + invcause.setInstance(newexpr); + invcause.setLstParameters( + Arrays.asList(new Exprent[]{new VarExprent(2, new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"), null)})); + + handlerexprent = new ExitExprent(ExitExprent.EXIT_THROW, + invcause, + null); + } + + + public void processClassReferences(ClassNode node) { + + ClassWrapper wrapper = node.wrapper; + + // int major_version = wrapper.getClassStruct().major_version; + // int minor_version = wrapper.getClassStruct().minor_version; + // + // if(major_version > 48 || (major_version == 48 && minor_version > 0)) { + // // version 1.5 or above + // return; + // } + + if (wrapper.getClassStruct().isVersionGE_1_5()) { + // version 1.5 or above + return; + } + + // find the synthetic method Class class$(String) if present + HashMap<ClassWrapper, MethodWrapper> mapClassMeths = new HashMap<ClassWrapper, MethodWrapper>(); + 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; + } } diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index c14fe79..3c8afab 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -1,27 +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.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -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.IFernflowerLogger; @@ -40,1068 +33,1106 @@ import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.StructMethod; -import org.jetbrains.java.decompiler.struct.attr.StructAnnDefaultAttribute; -import org.jetbrains.java.decompiler.struct.attr.StructAnnotationAttribute; -import org.jetbrains.java.decompiler.struct.attr.StructAnnotationParameterAttribute; -import org.jetbrains.java.decompiler.struct.attr.StructConstantValueAttribute; -import org.jetbrains.java.decompiler.struct.attr.StructExceptionsAttribute; -import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; -import org.jetbrains.java.decompiler.struct.attr.StructGenericSignatureAttribute; +import org.jetbrains.java.decompiler.struct.attr.*; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; -import org.jetbrains.java.decompiler.struct.gen.generics.GenericClassDescriptor; -import org.jetbrains.java.decompiler.struct.gen.generics.GenericFieldDescriptor; -import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; -import org.jetbrains.java.decompiler.struct.gen.generics.GenericMethodDescriptor; -import org.jetbrains.java.decompiler.struct.gen.generics.GenericType; +import org.jetbrains.java.decompiler.struct.gen.generics.*; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + public class ClassWriter { - - private static final int[] modval_class = new int[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, - CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_STRICT}; - - private static final String[] modstr_class = new String[] {"public ", "protected ", "private ", "abstract ", "static ", "final ", "strictfp "}; - - private static final int[] modval_field = new int[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, - CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_TRANSIENT, CodeConstants.ACC_VOLATILE}; - - private static final String[] modstr_field = new String[] {"public ", "protected ", "private ", "static ", "final ", "transient ", "volatile "}; - - private static final int[] modval_meth = new int[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, - CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_SYNCHRONIZED, - CodeConstants.ACC_NATIVE, CodeConstants.ACC_STRICT}; - - private static final String[] modstr_meth = new String[] {"public ", "protected ", "private ", "abstract ", "static ", "final ", "synchronized ", "native ", "strictfp "}; - - private static final HashSet<Integer> mod_notinterface = new HashSet<Integer>(Arrays.asList(new Integer[] {CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC})); - private static final HashSet<Integer> mod_notinterface_fields = new HashSet<Integer>(Arrays.asList(new Integer[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL})); - private static final HashSet<Integer> mod_notinterface_meth = new HashSet<Integer>(Arrays.asList(new Integer[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_ABSTRACT})); - - private ClassReference14Processor ref14processor; - - private PoolInterceptor interceptor; - - public ClassWriter() { - ref14processor = new ClassReference14Processor(); - interceptor = DecompilerContext.getPoolInterceptor(); - } - - - private void invokeProcessors(ClassNode node) { - - ClassWrapper wrapper = node.wrapper; - StructClass cl = wrapper.getClassStruct(); - - InitializerProcessor.extractInitializers(wrapper); - - if(node.type == ClassNode.CLASS_ROOT && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) { - ref14processor.processClassReferences(node); - } - - if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (cl.access_flags & CodeConstants.ACC_ENUM) != 0) { - EnumProcessor.clearEnum(wrapper); - } - - if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ASSERTIONS)) { - AssertProcessor.buildAssertions(node); - } - - } - - public void classLambdaToJava(ClassNode node, BufferedWriter writer, Exprent method_object, int indent) throws IOException { - - // get the class node with the content method - ClassNode node_content = node; - while(node_content != null && node_content.type == ClassNode.CLASS_LAMBDA) { - node_content = node_content.parent; - } - - if(node_content == null) { - return; - } - - boolean lambda_to_anonymous = DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS); - - ClassNode nodeold = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); - DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, node); - - ClassWrapper wrapper = node_content.wrapper; - StructClass cl = wrapper.getClassStruct(); - - DecompilerContext.getLogger().startWriteClass(node.simpleName); - - if(node.lambda_information.is_method_reference) { - - if(!node.lambda_information.is_content_method_static && method_object != null) { // reference to a virtual method - writer.write(method_object.toJava(indent)); - } else { // reference to a static method - writer.write(ExprProcessor.getCastTypeName(new VarType(node.lambda_information.content_class_name, false))); - } - - writer.write("::"); - writer.write(node.lambda_information.content_method_name); - - writer.flush(); - - } else { - - // lambda method - StructMethod mt = cl.getMethod(node.lambda_information.content_method_key); - MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); - - MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node.lambda_information.content_method_descriptor); - MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node.lambda_information.method_descriptor); - - if(!lambda_to_anonymous) { // lambda parameters '() ->' - - StringBuilder buff = new StringBuilder("("); - - boolean firstpar = true; - int index = node.lambda_information.is_content_method_static ? 0 : 1;; - - int start_index = md_content.params.length - md_lambda.params.length; - - for(int i=0;i<md_content.params.length;i++) { - - if(i >= start_index) { - - if(!firstpar) { - buff.append(", "); - } - - String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); - buff.append(parname==null ? "param"+index : parname); // null iff decompiled with errors - - firstpar = false; - } - - index+=md_content.params[i].stack_size; - } - buff.append(") ->"); - - writer.write(buff.toString()); - } - - StringWriter strwriter = new StringWriter(); - BufferedWriter bufstrwriter = new BufferedWriter(strwriter); - - if(lambda_to_anonymous) { - methodLambdaToJava(node, node_content, mt, bufstrwriter, indent+1, false); - } else { - methodLambdaToJava(node, node_content, mt, bufstrwriter, indent, true); - } - - bufstrwriter.flush(); - - // closing up class definition - writer.write(" {"); - writer.write(DecompilerContext.getNewLineSeparator()); - - writer.write(strwriter.toString()); - - writer.write(InterpreterUtil.getIndentString(indent)); - writer.write("}"); - writer.flush(); - } - - DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, nodeold); - - DecompilerContext.getLogger().endWriteClass(); - } - - public void classToJava(ClassNode node, BufferedWriter writer, int indent) throws IOException { - - ClassWrapper wrapper = node.wrapper; - StructClass cl = wrapper.getClassStruct(); - - ClassNode nodeold = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); - DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, node); - - // last minute processing - invokeProcessors(node); - - DecompilerContext.getLogger().startWriteClass(cl.qualifiedName); - - writeClassDefinition(node, writer, indent); - - // methods - StringWriter strwriter = new StringWriter(); - BufferedWriter bufstrwriter = new BufferedWriter(strwriter); - - boolean firstmt = true; - boolean mthidden = false; - - for(StructMethod mt : cl.getMethods()) { - - int flags = mt.getAccessFlags(); - - boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); - boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; - - if((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) && - (!isBridge || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_BRIDGE)) && - !wrapper.getHideMembers().contains(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()))) { - if(!mthidden && (!firstmt || node.type != ClassNode.CLASS_ANONYMOUS)) { - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - firstmt = false; - } - - mthidden = !methodToJava(node, mt, bufstrwriter, indent+1); - } - } - bufstrwriter.flush(); - - StringWriter strwriter1 = new StringWriter(); - BufferedWriter bufstrwriter1 = new BufferedWriter(strwriter1); - - int fields_count = 0; - - boolean enumfields = false; - - // fields - for(StructField fd: cl.getFields()) { - int flags = fd.access_flags; - boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic"); - if((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) - && !wrapper.getHideMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()))) { - - boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; - if(isEnum) { - if(enumfields) { - bufstrwriter1.write(","); - bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); - } else { - enumfields = true; - } - } else { - if(enumfields) { - bufstrwriter1.write(";"); - bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); - enumfields = false; - } - } - - fieldToJava(wrapper, cl, fd, bufstrwriter1, indent+1); - fields_count++; - } - } - - if(enumfields) { - bufstrwriter1.write(";"); - bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); - } - - bufstrwriter1.flush(); - - if(fields_count > 0) { - writer.write(DecompilerContext.getNewLineSeparator()); - writer.write(strwriter1.toString()); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - - // methods - writer.write(strwriter.toString()); - - // member classes - for(ClassNode inner : node.nested) { - if(inner.type == ClassNode.CLASS_MEMBER) { - StructClass innercl = inner.classStruct; - - boolean isSynthetic = ((inner.access | innercl.access_flags) & CodeConstants.ACC_SYNTHETIC) != 0 || innercl.getAttributes().containsKey("Synthetic"); - if((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) - && !wrapper.getHideMembers().contains(innercl.qualifiedName)) { - writer.write(DecompilerContext.getNewLineSeparator()); - classToJava(inner, writer, indent+1); - } - } - } - - writer.write(InterpreterUtil.getIndentString(indent)); - writer.write("}"); - if(node.type != ClassNode.CLASS_ANONYMOUS) { - writer.write(DecompilerContext.getNewLineSeparator()); - } - writer.flush(); - - DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, nodeold); - - DecompilerContext.getLogger().endWriteClass(); - } - - private void writeClassDefinition(ClassNode node, BufferedWriter writer, int indent) throws IOException { - - if(node.type == ClassNode.CLASS_ANONYMOUS) { - writer.write(" {"); - writer.write(DecompilerContext.getNewLineSeparator()); - } else { - - String indstr = InterpreterUtil.getIndentString(indent); - - ClassWrapper wrapper = node.wrapper; - StructClass cl = wrapper.getClassStruct(); - - int flags = node.type == ClassNode.CLASS_ROOT?cl.access_flags:node.access; - boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0; - boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0; - boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; - - boolean isDeprecated = cl.getAttributes().containsKey("Deprecated"); - - if(interceptor != null) { - String oldname = interceptor.getOldName(cl.qualifiedName); - if(oldname != null) { - writer.write(indstr); - writer.write("// $FF: renamed from: "+getDescriptorPrintOut(oldname, 0)); - writer.write(DecompilerContext.getNewLineSeparator()); - } - } - - if (isDeprecated) { - writer.write(indstr); - writer.write("/** @deprecated */"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - // class annotations - List<AnnotationExprent> lstAnn = getAllAnnotations(cl.getAttributes()); - for(AnnotationExprent annexpr : lstAnn) { - writer.write(annexpr.toJava(indent)); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic"); - - if(isSynthetic) { - writer.write(indstr); - writer.write("// $FF: synthetic class"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - writer.write(indstr); - - if(isEnum) { - // remove abstract and final flags (JLS 8.9 Enums) - flags &=~CodeConstants.ACC_ABSTRACT; - flags &=~CodeConstants.ACC_FINAL; - } - - for(int i=0;i<modval_class.length;i++) { - if(!isInterface || !mod_notinterface.contains(modval_class[i])) { - if((flags & modval_class[i]) != 0) { - writer.write(modstr_class[i]); - } - } - } - - if(isEnum) { - writer.write("enum "); - }else if(isInterface) { - if(isAnnotation) { - writer.write("@"); - } - writer.write("interface "); - } else { - writer.write("class "); - } - - GenericClassDescriptor descriptor = null; - if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)cl.getAttributes().getWithKey("Signature"); - if(attr != null) { - descriptor = GenericMain.parseClassSignature(attr.getSignature()); - } - } - - writer.write(node.simpleName); - if(descriptor != null && !descriptor.fparameters.isEmpty()) { - writer.write("<"); - for(int i=0;i<descriptor.fparameters.size();i++) { - if(i>0) { - writer.write(", "); - } - writer.write(descriptor.fparameters.get(i)); - - List<GenericType> lstBounds = descriptor.fbounds.get(i); + + private static final int[] modval_class = new int[]{CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, + CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_STRICT}; + + private static final String[] modstr_class = + new String[]{"public ", "protected ", "private ", "abstract ", "static ", "final ", "strictfp "}; + + private static final int[] modval_field = new int[]{CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, + CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_TRANSIENT, CodeConstants.ACC_VOLATILE}; + + private static final String[] modstr_field = + new String[]{"public ", "protected ", "private ", "static ", "final ", "transient ", "volatile "}; + + private static final int[] modval_meth = new int[]{CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, + CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_SYNCHRONIZED, + CodeConstants.ACC_NATIVE, CodeConstants.ACC_STRICT}; + + private static final String[] modstr_meth = + new String[]{"public ", "protected ", "private ", "abstract ", "static ", "final ", "synchronized ", "native ", "strictfp "}; + + private static final HashSet<Integer> mod_notinterface = + new HashSet<Integer>(Arrays.asList(new Integer[]{CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC})); + private static final HashSet<Integer> mod_notinterface_fields = + new HashSet<Integer>(Arrays.asList(new Integer[]{CodeConstants.ACC_PUBLIC, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL})); + private static final HashSet<Integer> mod_notinterface_meth = + new HashSet<Integer>(Arrays.asList(new Integer[]{CodeConstants.ACC_PUBLIC, CodeConstants.ACC_ABSTRACT})); + + private ClassReference14Processor ref14processor; + + private PoolInterceptor interceptor; + + public ClassWriter() { + ref14processor = new ClassReference14Processor(); + interceptor = DecompilerContext.getPoolInterceptor(); + } + + + private void invokeProcessors(ClassNode node) { + + ClassWrapper wrapper = node.wrapper; + StructClass cl = wrapper.getClassStruct(); + + InitializerProcessor.extractInitializers(wrapper); + + if (node.type == ClassNode.CLASS_ROOT && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) { + ref14processor.processClassReferences(node); + } + + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (cl.access_flags & CodeConstants.ACC_ENUM) != 0) { + EnumProcessor.clearEnum(wrapper); + } + + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ASSERTIONS)) { + AssertProcessor.buildAssertions(node); + } + } + + public void classLambdaToJava(ClassNode node, BufferedWriter writer, Exprent method_object, int indent) throws IOException { + + // get the class node with the content method + ClassNode node_content = node; + while (node_content != null && node_content.type == ClassNode.CLASS_LAMBDA) { + node_content = node_content.parent; + } + + if (node_content == null) { + return; + } + + boolean lambda_to_anonymous = DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS); + + ClassNode nodeold = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, node); + + ClassWrapper wrapper = node_content.wrapper; + StructClass cl = wrapper.getClassStruct(); + + DecompilerContext.getLogger().startWriteClass(node.simpleName); + + if (node.lambda_information.is_method_reference) { + + if (!node.lambda_information.is_content_method_static && method_object != null) { // reference to a virtual method + writer.write(method_object.toJava(indent)); + } + else { // reference to a static method + writer.write(ExprProcessor.getCastTypeName(new VarType(node.lambda_information.content_class_name, false))); + } + + writer.write("::"); + writer.write(node.lambda_information.content_method_name); + + writer.flush(); + } + else { + + // lambda method + StructMethod mt = cl.getMethod(node.lambda_information.content_method_key); + MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); + + MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node.lambda_information.content_method_descriptor); + MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node.lambda_information.method_descriptor); + + if (!lambda_to_anonymous) { // lambda parameters '() ->' + + StringBuilder buff = new StringBuilder("("); + + boolean firstpar = true; + int index = node.lambda_information.is_content_method_static ? 0 : 1; + ; + + int start_index = md_content.params.length - md_lambda.params.length; + + for (int i = 0; i < md_content.params.length; i++) { + + if (i >= start_index) { + + if (!firstpar) { + buff.append(", "); + } + + String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); + buff.append(parname == null ? "param" + index : parname); // null iff decompiled with errors + + firstpar = false; + } + + index += md_content.params[i].stack_size; + } + buff.append(") ->"); + + writer.write(buff.toString()); + } + + StringWriter strwriter = new StringWriter(); + BufferedWriter bufstrwriter = new BufferedWriter(strwriter); + + if (lambda_to_anonymous) { + methodLambdaToJava(node, node_content, mt, bufstrwriter, indent + 1, false); + } + else { + methodLambdaToJava(node, node_content, mt, bufstrwriter, indent, true); + } + + bufstrwriter.flush(); + + // closing up class definition + writer.write(" {"); + writer.write(DecompilerContext.getNewLineSeparator()); + + writer.write(strwriter.toString()); + + writer.write(InterpreterUtil.getIndentString(indent)); + writer.write("}"); + writer.flush(); + } + + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, nodeold); + + DecompilerContext.getLogger().endWriteClass(); + } + + public void classToJava(ClassNode node, BufferedWriter writer, int indent) throws IOException { + + ClassWrapper wrapper = node.wrapper; + StructClass cl = wrapper.getClassStruct(); + + ClassNode nodeold = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, node); + + // last minute processing + invokeProcessors(node); + + DecompilerContext.getLogger().startWriteClass(cl.qualifiedName); + + writeClassDefinition(node, writer, indent); + + // methods + StringWriter strwriter = new StringWriter(); + BufferedWriter bufstrwriter = new BufferedWriter(strwriter); + + boolean firstmt = true; + boolean mthidden = false; + + for (StructMethod mt : cl.getMethods()) { + + int flags = mt.getAccessFlags(); + + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); + boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; + + if ((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) && + (!isBridge || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_BRIDGE)) && + !wrapper.getHideMembers().contains(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()))) { + if (!mthidden && (!firstmt || node.type != ClassNode.CLASS_ANONYMOUS)) { + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + firstmt = false; + } + + mthidden = !methodToJava(node, mt, bufstrwriter, indent + 1); + } + } + bufstrwriter.flush(); + + StringWriter strwriter1 = new StringWriter(); + BufferedWriter bufstrwriter1 = new BufferedWriter(strwriter1); + + int fields_count = 0; + + boolean enumfields = false; + + // fields + for (StructField fd : cl.getFields()) { + int flags = fd.access_flags; + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic"); + if ((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) + && !wrapper.getHideMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()))) { + + boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; + if (isEnum) { + if (enumfields) { + bufstrwriter1.write(","); + bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); + } + else { + enumfields = true; + } + } + else { + if (enumfields) { + bufstrwriter1.write(";"); + bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); + enumfields = false; + } + } + + fieldToJava(wrapper, cl, fd, bufstrwriter1, indent + 1); + fields_count++; + } + } + + if (enumfields) { + bufstrwriter1.write(";"); + bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); + } + + bufstrwriter1.flush(); + + if (fields_count > 0) { + writer.write(DecompilerContext.getNewLineSeparator()); + writer.write(strwriter1.toString()); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + + // methods + writer.write(strwriter.toString()); + + // member classes + for (ClassNode inner : node.nested) { + if (inner.type == ClassNode.CLASS_MEMBER) { + StructClass innercl = inner.classStruct; + + boolean isSynthetic = + ((inner.access | innercl.access_flags) & CodeConstants.ACC_SYNTHETIC) != 0 || innercl.getAttributes().containsKey("Synthetic"); + if ((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) + && !wrapper.getHideMembers().contains(innercl.qualifiedName)) { + writer.write(DecompilerContext.getNewLineSeparator()); + classToJava(inner, writer, indent + 1); + } + } + } + + writer.write(InterpreterUtil.getIndentString(indent)); + writer.write("}"); + if (node.type != ClassNode.CLASS_ANONYMOUS) { + writer.write(DecompilerContext.getNewLineSeparator()); + } + writer.flush(); + + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, nodeold); + + DecompilerContext.getLogger().endWriteClass(); + } + + private void writeClassDefinition(ClassNode node, BufferedWriter writer, int indent) throws IOException { + + if (node.type == ClassNode.CLASS_ANONYMOUS) { + writer.write(" {"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + else { + + String indstr = InterpreterUtil.getIndentString(indent); + + ClassWrapper wrapper = node.wrapper; + StructClass cl = wrapper.getClassStruct(); + + int flags = node.type == ClassNode.CLASS_ROOT ? cl.access_flags : node.access; + boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0; + boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0; + boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; + + boolean isDeprecated = cl.getAttributes().containsKey("Deprecated"); + + if (interceptor != null) { + String oldname = interceptor.getOldName(cl.qualifiedName); + if (oldname != null) { + writer.write(indstr); + writer.write("// $FF: renamed from: " + getDescriptorPrintOut(oldname, 0)); + writer.write(DecompilerContext.getNewLineSeparator()); + } + } + + if (isDeprecated) { + writer.write(indstr); + writer.write("/** @deprecated */"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + // class annotations + List<AnnotationExprent> lstAnn = getAllAnnotations(cl.getAttributes()); + for (AnnotationExprent annexpr : lstAnn) { + writer.write(annexpr.toJava(indent)); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic"); + + if (isSynthetic) { + writer.write(indstr); + writer.write("// $FF: synthetic class"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + writer.write(indstr); + + if (isEnum) { + // remove abstract and final flags (JLS 8.9 Enums) + flags &= ~CodeConstants.ACC_ABSTRACT; + flags &= ~CodeConstants.ACC_FINAL; + } + + for (int i = 0; i < modval_class.length; i++) { + if (!isInterface || !mod_notinterface.contains(modval_class[i])) { + if ((flags & modval_class[i]) != 0) { + writer.write(modstr_class[i]); + } + } + } + + if (isEnum) { + writer.write("enum "); + } + else if (isInterface) { + if (isAnnotation) { + writer.write("@"); + } + writer.write("interface "); + } + else { + writer.write("class "); + } + + GenericClassDescriptor descriptor = null; + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)cl.getAttributes().getWithKey("Signature"); + if (attr != null) { + descriptor = GenericMain.parseClassSignature(attr.getSignature()); + } + } + + writer.write(node.simpleName); + if (descriptor != null && !descriptor.fparameters.isEmpty()) { + writer.write("<"); + for (int i = 0; i < descriptor.fparameters.size(); i++) { + if (i > 0) { + writer.write(", "); + } + writer.write(descriptor.fparameters.get(i)); + + List<GenericType> lstBounds = descriptor.fbounds.get(i); if (lstBounds.size() > 1 || !"java/lang/Object".equals(lstBounds.get(0).value)) { writer.write(" extends "); writer.write(GenericMain.getGenericCastTypeName(lstBounds.get(0))); - for(int j=1;j<lstBounds.size();j++) { + for (int j = 1; j < lstBounds.size(); j++) { writer.write(" & " + GenericMain.getGenericCastTypeName(lstBounds.get(j))); } } - } - writer.write(">"); - } - writer.write(" "); - - if(!isEnum && !isInterface && cl.superClass != null) { - VarType supertype = new VarType(cl.superClass.getString(), true); - if(!VarType.VARTYPE_OBJECT.equals(supertype)) { - writer.write("extends "); - if(descriptor != null) { - writer.write(GenericMain.getGenericCastTypeName(descriptor.superclass)); - } else { - writer.write(ExprProcessor.getCastTypeName(supertype)); - } - writer.write(" "); - } - } - - if(!isAnnotation) { - int[] interfaces = cl.getInterfaces(); - if(interfaces.length > 0) { - writer.write(isInterface?"extends ":"implements "); - for(int i=0;i<interfaces.length;i++) { - if(i>0) { - writer.write(", "); - } - if(descriptor != null) { - writer.write(GenericMain.getGenericCastTypeName(descriptor.superinterfaces.get(i))); - } else { - writer.write(ExprProcessor.getCastTypeName(new VarType(cl.getInterface(i), true))); - } - } - writer.write(" "); - } - } - - writer.write("{"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - } - - private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, BufferedWriter writer, int indent) throws IOException { - - String indstr = InterpreterUtil.getIndentString(indent); - - boolean isInterface = (cl.access_flags & CodeConstants.ACC_INTERFACE) != 0; - int flags = fd.access_flags; - - if(interceptor != null) { - String oldname = interceptor.getOldName(cl.qualifiedName+" "+fd.getName()+" "+fd.getDescriptor()); - if(oldname != null) { - String[] element = oldname.split(" "); - - writer.write(indstr); - writer.write("// $FF: renamed from: "+element[1]+" "+getDescriptorPrintOut(element[2], 1)); - writer.write(DecompilerContext.getNewLineSeparator()); - } - } - - boolean isDeprecated = fd.getAttributes().containsKey("Deprecated"); - - if (isDeprecated) { - writer.write(indstr); - writer.write("/** @deprecated */"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - // field annotations - List<AnnotationExprent> lstAnn = getAllAnnotations(fd.getAttributes()); - for(AnnotationExprent annexpr : lstAnn) { - writer.write(annexpr.toJava(indent)); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic"); - boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; - - if(isSynthetic) { - writer.write(indstr); - writer.write("// $FF: synthetic field"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - writer.write(indstr); - - if(!isEnum) { - for(int i=0;i<modval_field.length;i++) { - if(!isInterface || !mod_notinterface_fields.contains(modval_field[i])) { - if((flags & modval_field[i]) != 0) { - writer.write(modstr_field[i]); - } - } - } - } - - VarType fieldType = new VarType(fd.getDescriptor(), false); - - GenericFieldDescriptor descriptor = null; - if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)fd.getAttributes().getWithKey("Signature"); - if(attr != null) { - descriptor = GenericMain.parseFieldSignature(attr.getSignature()); - } - } - - if(!isEnum) { - if(descriptor != null) { - writer.write(GenericMain.getGenericCastTypeName(descriptor.type)); - } else { - writer.write(ExprProcessor.getCastTypeName(fieldType)); - } - writer.write(" "); - } - - writer.write(fd.getName()); - - Exprent initializer; - if((flags & CodeConstants.ACC_STATIC) != 0) { - initializer = wrapper.getStaticFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); - } else { - initializer = wrapper.getDynamicFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); - } - - if(initializer != null) { - if(isEnum && initializer.type == Exprent.EXPRENT_NEW) { - NewExprent nexpr = (NewExprent)initializer; - nexpr.setEnumconst(true); - writer.write(nexpr.toJava(indent)); - } else { - writer.write(" = "); - writer.write(initializer.toJava(indent)); - } - } else if((flags & CodeConstants.ACC_FINAL) != 0 && (flags & CodeConstants.ACC_STATIC) != 0) { - StructConstantValueAttribute attr = (StructConstantValueAttribute)fd.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_CONSTANT_VALUE); - if(attr != null) { - PrimitiveConstant cnst = cl.getPool().getPrimitiveConstant(attr.getIndex()); - writer.write(" = "); - writer.write(new ConstExprent(fieldType, cnst.value).toJava(indent)); - } - } - - if(!isEnum) { - writer.write(";"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - } - - public boolean methodLambdaToJava(ClassNode node_lambda, ClassNode node_content, StructMethod mt, BufferedWriter writer, int indent, boolean code_only) throws IOException { - - ClassWrapper wrapper = node_content.wrapper; - - MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); - - MethodWrapper methold = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); - DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, meth); - - String indstr = InterpreterUtil.getIndentString(indent); - - String method_name = node_lambda.lambda_information.method_name; - MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node_lambda.lambda_information.content_method_descriptor); - MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node_lambda.lambda_information.method_descriptor); - - StringWriter strwriter = new StringWriter(); - BufferedWriter bufstrwriter = new BufferedWriter(strwriter); - - if(!code_only) { - bufstrwriter.write(indstr); - bufstrwriter.write("public "); - bufstrwriter.write(method_name); - bufstrwriter.write("("); - - boolean firstpar = true; - int index = node_lambda.lambda_information.is_content_method_static ? 0 : 1;; - - int start_index = md_content.params.length - md_lambda.params.length; - - for(int i=0;i<md_content.params.length;i++) { - - if(i >= start_index) { - - if(!firstpar) { - bufstrwriter.write(", "); - } - - VarType partype = md_content.params[i].copy(); - - String strpartype = ExprProcessor.getCastTypeName(partype); - if(ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { - strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); - } - - bufstrwriter.write(strpartype); - bufstrwriter.write(" "); - - String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); - bufstrwriter.write(parname==null?"param"+index:parname); // null iff decompiled with errors - - firstpar = false; - } - - index+=md_content.params[i].stack_size; - } - - bufstrwriter.write(")"); - bufstrwriter.write(" "); - bufstrwriter.write("{"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; - - if(root != null && !meth.decompiledWithErrors) { // check for existence - try { - String code = root.toJava(indent+1); - bufstrwriter.write(code); - } catch(Throwable ex) { - DecompilerContext.getLogger().writeMessage("Method "+mt.getName()+" "+mt.getDescriptor()+" couldn't be written.", ex); - meth.decompiledWithErrors = true; - } - } - - if(meth.decompiledWithErrors) { - bufstrwriter.write(InterpreterUtil.getIndentString(indent+1)); - bufstrwriter.write("// $FF: Couldn't be decompiled"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - if(!code_only) { - bufstrwriter.write(indstr+"}"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - bufstrwriter.flush(); - - writer.write(strwriter.toString()); - - DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methold); - - return true; - } - - public boolean methodToJava(ClassNode node, StructMethod mt, BufferedWriter writer, int indent) throws IOException { - - ClassWrapper wrapper = node.wrapper; - StructClass cl = wrapper.getClassStruct(); - - MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); - - MethodWrapper methold = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); - DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, meth); - - boolean isInterface = (cl.access_flags & CodeConstants.ACC_INTERFACE) != 0; - boolean isAnnotation = (cl.access_flags & CodeConstants.ACC_ANNOTATION) != 0; - boolean isEnum = (cl.access_flags & CodeConstants.ACC_ENUM) != 0 && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); - boolean isDeprecated = mt.getAttributes().containsKey("Deprecated"); - - String indstr = InterpreterUtil.getIndentString(indent); - boolean clinit = false, init = false, dinit = false; - - MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); - - StringWriter strwriter = new StringWriter(); - BufferedWriter bufstrwriter = new BufferedWriter(strwriter); - - int flags = mt.getAccessFlags(); - if((flags & CodeConstants.ACC_NATIVE) != 0) { - flags &= ~CodeConstants.ACC_STRICT; // compiler bug: a strictfp class sets all methods to strictfp - } - - if("<clinit>".equals(mt.getName())) { - flags &= CodeConstants.ACC_STATIC; // ingnore all modifiers except 'static' in a static initializer - } - - if(interceptor != null) { - String oldname = interceptor.getOldName(cl.qualifiedName+" "+mt.getName()+" "+mt.getDescriptor()); - if(oldname != null) { - String[] element = oldname.split(" "); - - bufstrwriter.write(indstr); - bufstrwriter.write("// $FF: renamed from: "+element[1]+" "+getDescriptorPrintOut(element[2], 2)); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - } - - if (isDeprecated) { - writer.write(indstr); - writer.write("/** @deprecated */"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - // method annotations - List<AnnotationExprent> lstAnn = getAllAnnotations(mt.getAttributes()); - for(AnnotationExprent annexpr : lstAnn) { - bufstrwriter.write(annexpr.toJava(indent)); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); - boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; - - if(isSynthetic) { - bufstrwriter.write(indstr); - bufstrwriter.write("// $FF: synthetic method"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - if(isBridge) { - bufstrwriter.write(indstr); - bufstrwriter.write("// $FF: bridge method"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - bufstrwriter.write(indstr); - for(int i=0;i<modval_meth.length;i++) { - if(!isInterface || !mod_notinterface_meth.contains(modval_meth[i])) { - if((flags & modval_meth[i]) != 0) { - bufstrwriter.write(modstr_meth[i]); - } - } - } - - // 'default' modifier (Java 8) - if(isInterface && mt.containsCode()) { - bufstrwriter.write("default "); - } - - String name = mt.getName(); - if ("<init>".equals(name)) { - if (node.type == ClassNode.CLASS_ANONYMOUS) { - name = ""; - dinit = true; - } else { - name = node.simpleName; - init = true; - } - } else if ("<clinit>".equals(name)) { - name = ""; - clinit = true; - } - - GenericMethodDescriptor descriptor = null; - if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)mt.getAttributes().getWithKey("Signature"); - if(attr != null) { - descriptor = GenericMain.parseMethodSignature(attr.getSignature()); + } + writer.write(">"); + } + writer.write(" "); + + if (!isEnum && !isInterface && cl.superClass != null) { + VarType supertype = new VarType(cl.superClass.getString(), true); + if (!VarType.VARTYPE_OBJECT.equals(supertype)) { + writer.write("extends "); + if (descriptor != null) { + writer.write(GenericMain.getGenericCastTypeName(descriptor.superclass)); + } + else { + writer.write(ExprProcessor.getCastTypeName(supertype)); + } + writer.write(" "); + } + } + + if (!isAnnotation) { + int[] interfaces = cl.getInterfaces(); + if (interfaces.length > 0) { + writer.write(isInterface ? "extends " : "implements "); + for (int i = 0; i < interfaces.length; i++) { + if (i > 0) { + writer.write(", "); + } + if (descriptor != null) { + writer.write(GenericMain.getGenericCastTypeName(descriptor.superinterfaces.get(i))); + } + else { + writer.write(ExprProcessor.getCastTypeName(new VarType(cl.getInterface(i), true))); + } + } + writer.write(" "); + } + } + + writer.write("{"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + } + + private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, BufferedWriter writer, int indent) throws IOException { + + String indstr = InterpreterUtil.getIndentString(indent); + + boolean isInterface = (cl.access_flags & CodeConstants.ACC_INTERFACE) != 0; + int flags = fd.access_flags; + + if (interceptor != null) { + String oldname = interceptor.getOldName(cl.qualifiedName + " " + fd.getName() + " " + fd.getDescriptor()); + if (oldname != null) { + String[] element = oldname.split(" "); + + writer.write(indstr); + writer.write("// $FF: renamed from: " + element[1] + " " + getDescriptorPrintOut(element[2], 1)); + writer.write(DecompilerContext.getNewLineSeparator()); + } + } + + boolean isDeprecated = fd.getAttributes().containsKey("Deprecated"); + + if (isDeprecated) { + writer.write(indstr); + writer.write("/** @deprecated */"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + // field annotations + List<AnnotationExprent> lstAnn = getAllAnnotations(fd.getAttributes()); + for (AnnotationExprent annexpr : lstAnn) { + writer.write(annexpr.toJava(indent)); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic"); + boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; + + if (isSynthetic) { + writer.write(indstr); + writer.write("// $FF: synthetic field"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + writer.write(indstr); + + if (!isEnum) { + for (int i = 0; i < modval_field.length; i++) { + if (!isInterface || !mod_notinterface_fields.contains(modval_field[i])) { + if ((flags & modval_field[i]) != 0) { + writer.write(modstr_field[i]); + } + } + } + } + + VarType fieldType = new VarType(fd.getDescriptor(), false); + + GenericFieldDescriptor descriptor = null; + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)fd.getAttributes().getWithKey("Signature"); + if (attr != null) { + descriptor = GenericMain.parseFieldSignature(attr.getSignature()); + } + } + + if (!isEnum) { + if (descriptor != null) { + writer.write(GenericMain.getGenericCastTypeName(descriptor.type)); + } + else { + writer.write(ExprProcessor.getCastTypeName(fieldType)); + } + writer.write(" "); + } + + writer.write(fd.getName()); + + Exprent initializer; + if ((flags & CodeConstants.ACC_STATIC) != 0) { + initializer = wrapper.getStaticFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); + } + else { + initializer = wrapper.getDynamicFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); + } + + if (initializer != null) { + if (isEnum && initializer.type == Exprent.EXPRENT_NEW) { + NewExprent nexpr = (NewExprent)initializer; + nexpr.setEnumconst(true); + writer.write(nexpr.toJava(indent)); + } + else { + writer.write(" = "); + writer.write(initializer.toJava(indent)); + } + } + else if ((flags & CodeConstants.ACC_FINAL) != 0 && (flags & CodeConstants.ACC_STATIC) != 0) { + StructConstantValueAttribute attr = + (StructConstantValueAttribute)fd.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_CONSTANT_VALUE); + if (attr != null) { + PrimitiveConstant cnst = cl.getPool().getPrimitiveConstant(attr.getIndex()); + writer.write(" = "); + writer.write(new ConstExprent(fieldType, cnst.value).toJava(indent)); + } + } + + if (!isEnum) { + writer.write(";"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + } + + public boolean methodLambdaToJava(ClassNode node_lambda, + ClassNode node_content, + StructMethod mt, + BufferedWriter writer, + int indent, + boolean code_only) throws IOException { + + ClassWrapper wrapper = node_content.wrapper; + + MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); + + MethodWrapper methold = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, meth); + + String indstr = InterpreterUtil.getIndentString(indent); + + String method_name = node_lambda.lambda_information.method_name; + MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node_lambda.lambda_information.content_method_descriptor); + MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node_lambda.lambda_information.method_descriptor); + + StringWriter strwriter = new StringWriter(); + BufferedWriter bufstrwriter = new BufferedWriter(strwriter); + + if (!code_only) { + bufstrwriter.write(indstr); + bufstrwriter.write("public "); + bufstrwriter.write(method_name); + bufstrwriter.write("("); + + boolean firstpar = true; + int index = node_lambda.lambda_information.is_content_method_static ? 0 : 1; + ; + + int start_index = md_content.params.length - md_lambda.params.length; + + for (int i = 0; i < md_content.params.length; i++) { + + if (i >= start_index) { + + if (!firstpar) { + bufstrwriter.write(", "); + } + + VarType partype = md_content.params[i].copy(); + + String strpartype = ExprProcessor.getCastTypeName(partype); + if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && + DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { + strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); + } + + bufstrwriter.write(strpartype); + bufstrwriter.write(" "); + + String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); + bufstrwriter.write(parname == null ? "param" + index : parname); // null iff decompiled with errors + + firstpar = false; + } + + index += md_content.params[i].stack_size; + } + + bufstrwriter.write(")"); + bufstrwriter.write(" "); + bufstrwriter.write("{"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; + + if (root != null && !meth.decompiledWithErrors) { // check for existence + try { + String code = root.toJava(indent + 1); + bufstrwriter.write(code); + } + catch (Throwable ex) { + DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex); + meth.decompiledWithErrors = true; + } + } + + if (meth.decompiledWithErrors) { + bufstrwriter.write(InterpreterUtil.getIndentString(indent + 1)); + bufstrwriter.write("// $FF: Couldn't be decompiled"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + if (!code_only) { + bufstrwriter.write(indstr + "}"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + bufstrwriter.flush(); + + writer.write(strwriter.toString()); + + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methold); + + return true; + } + + public boolean methodToJava(ClassNode node, StructMethod mt, BufferedWriter writer, int indent) throws IOException { + + ClassWrapper wrapper = node.wrapper; + StructClass cl = wrapper.getClassStruct(); + + MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); + + MethodWrapper methold = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, meth); + + boolean isInterface = (cl.access_flags & CodeConstants.ACC_INTERFACE) != 0; + boolean isAnnotation = (cl.access_flags & CodeConstants.ACC_ANNOTATION) != 0; + boolean isEnum = (cl.access_flags & CodeConstants.ACC_ENUM) != 0 && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); + boolean isDeprecated = mt.getAttributes().containsKey("Deprecated"); + + String indstr = InterpreterUtil.getIndentString(indent); + boolean clinit = false, init = false, dinit = false; + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + StringWriter strwriter = new StringWriter(); + BufferedWriter bufstrwriter = new BufferedWriter(strwriter); + + int flags = mt.getAccessFlags(); + if ((flags & CodeConstants.ACC_NATIVE) != 0) { + flags &= ~CodeConstants.ACC_STRICT; // compiler bug: a strictfp class sets all methods to strictfp + } + + if ("<clinit>".equals(mt.getName())) { + flags &= CodeConstants.ACC_STATIC; // ingnore all modifiers except 'static' in a static initializer + } + + if (interceptor != null) { + String oldname = interceptor.getOldName(cl.qualifiedName + " " + mt.getName() + " " + mt.getDescriptor()); + if (oldname != null) { + String[] element = oldname.split(" "); + + bufstrwriter.write(indstr); + bufstrwriter.write("// $FF: renamed from: " + element[1] + " " + getDescriptorPrintOut(element[2], 2)); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + } + + if (isDeprecated) { + writer.write(indstr); + writer.write("/** @deprecated */"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + // method annotations + List<AnnotationExprent> lstAnn = getAllAnnotations(mt.getAttributes()); + for (AnnotationExprent annexpr : lstAnn) { + bufstrwriter.write(annexpr.toJava(indent)); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); + boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; + + if (isSynthetic) { + bufstrwriter.write(indstr); + bufstrwriter.write("// $FF: synthetic method"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + if (isBridge) { + bufstrwriter.write(indstr); + bufstrwriter.write("// $FF: bridge method"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + bufstrwriter.write(indstr); + for (int i = 0; i < modval_meth.length; i++) { + if (!isInterface || !mod_notinterface_meth.contains(modval_meth[i])) { + if ((flags & modval_meth[i]) != 0) { + bufstrwriter.write(modstr_meth[i]); + } + } + } + + // 'default' modifier (Java 8) + if (isInterface && mt.containsCode()) { + bufstrwriter.write("default "); + } + + String name = mt.getName(); + if ("<init>".equals(name)) { + if (node.type == ClassNode.CLASS_ANONYMOUS) { + name = ""; + dinit = true; + } + else { + name = node.simpleName; + init = true; + } + } + else if ("<clinit>".equals(name)) { + name = ""; + clinit = true; + } + + GenericMethodDescriptor descriptor = null; + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)mt.getAttributes().getWithKey("Signature"); + if (attr != null) { + descriptor = GenericMain.parseMethodSignature(attr.getSignature()); int actualParams = md.params.length; - if(isEnum && init) actualParams -= 2; - if(actualParams != descriptor.params.size()) { - DecompilerContext.getLogger().writeMessage("Inconsistent generic signature in method "+mt.getName()+" "+mt.getDescriptor(), IFernflowerLogger.WARNING); - descriptor = null; - } - } - } - - boolean throwsExceptions = false; - - int param_count_explicit = 0; - - if(!clinit && !dinit) { - - boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; - - // formal type parameters - if(descriptor != null && !descriptor.fparameters.isEmpty()) { - bufstrwriter.write("<"); - for(int i=0;i<descriptor.fparameters.size();i++) { - if(i>0) { - bufstrwriter.write(", "); - } - bufstrwriter.write(descriptor.fparameters.get(i)); - - List<GenericType> lstBounds = descriptor.fbounds.get(i); - if (lstBounds.size() > 1 || !"java/lang/Object".equals(lstBounds.get(0).value)) { - bufstrwriter.write(" extends "); - bufstrwriter.write(GenericMain.getGenericCastTypeName(lstBounds.get(0))); - - for(int j = 1; j < lstBounds.size(); j++) { - bufstrwriter.write(" & " + GenericMain.getGenericCastTypeName(lstBounds.get(j))); - } - } - } - bufstrwriter.write("> "); - } - - if(!init) { - if(descriptor != null) { - bufstrwriter.write(GenericMain.getGenericCastTypeName(descriptor.ret)); - } else { - bufstrwriter.write(ExprProcessor.getCastTypeName(md.ret)); - } - bufstrwriter.write(" "); - } - - bufstrwriter.write(name); - bufstrwriter.write("("); - - // parameter annotations - List<List<AnnotationExprent>> lstParAnn = getAllParameterAnnotations(mt.getAttributes()); - - List<VarVersionPaar> signFields = meth.signatureFields; - - // compute last visible parameter - int lastparam_index = -1; - for(int i=0;i<md.params.length;i++) { - if(signFields == null || signFields.get(i) == null) { - lastparam_index = i; - } - } - - boolean firstpar = true; - int index = isEnum && init ? 3 : thisvar ? 1 : 0; - int start = isEnum && init && descriptor == null ? 2 : 0; - int params = descriptor == null ? md.params.length : descriptor.params.size(); - for(int i = start; i < params; i++) { - if (signFields == null || signFields.get(i) == null) { - - if(!firstpar) { - bufstrwriter.write(", "); - } - - if(lstParAnn.size() > param_count_explicit) { - List<AnnotationExprent> annotations = lstParAnn.get(param_count_explicit); - for(int j=0;j<annotations.size();j++) { - AnnotationExprent annexpr = annotations.get(j); - if(annexpr.getAnnotationType() == AnnotationExprent.ANNOTATION_NORMAL) { - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - bufstrwriter.write(annexpr.toJava(indent+1)); - } else { - bufstrwriter.write(annexpr.toJava(0)); - } - bufstrwriter.write(" "); - } - } - - if(meth.varproc.getVarFinal(new VarVersionPaar(index, 0)) == VarTypeProcessor.VAR_FINALEXPLICIT) { - bufstrwriter.write("final "); - } - - - if(descriptor != null) { - GenericType partype = descriptor.params.get(i); - - boolean isVarArgs = (i == lastparam_index && (mt.getAccessFlags() & CodeConstants.ACC_VARARGS) != 0 - && partype.arraydim > 0); - - if(isVarArgs) { - partype.arraydim--; - } - - String strpartype = GenericMain.getGenericCastTypeName(partype); - if(ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && - DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { - strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); - } - - bufstrwriter.write(strpartype); - - if(isVarArgs) { - bufstrwriter.write(" ..."); - } - - } else { - VarType partype = md.params[i].copy(); - - boolean isVarArgs = (i == lastparam_index && (mt.getAccessFlags() & CodeConstants.ACC_VARARGS) != 0 - && partype.arraydim > 0); - - if(isVarArgs) { - partype.decArrayDim(); - } - - String strpartype = ExprProcessor.getCastTypeName(partype); - if(ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && - DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { - strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); - } - - bufstrwriter.write(strpartype); - - if(isVarArgs) { - bufstrwriter.write(" ..."); - } - } - - bufstrwriter.write(" "); - String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); - bufstrwriter.write(parname==null?"param"+index:parname); // null iff decompiled with errors - firstpar = false; - param_count_explicit++; - } - - index+=md.params[i].stack_size; - } - - bufstrwriter.write(")"); - - StructExceptionsAttribute attr = (StructExceptionsAttribute)mt.getAttributes().getWithKey("Exceptions"); - if((descriptor!=null && !descriptor.exceptions.isEmpty()) || attr != null) { - throwsExceptions = true; - bufstrwriter.write(" throws "); - - for(int i=0;i<attr.getThrowsExceptions().size();i++) { - if(i>0) { - bufstrwriter.write(", "); - } - if(descriptor!=null && !descriptor.exceptions.isEmpty()) { - bufstrwriter.write(GenericMain.getGenericCastTypeName(descriptor.exceptions.get(i))); - } else { - VarType exctype = new VarType(attr.getExcClassname(i, cl.getPool()), true); - bufstrwriter.write(ExprProcessor.getCastTypeName(exctype)); - } - } - } - } - - boolean hidemethod = false; - - if((flags & (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_NATIVE)) != 0) { // native or abstract method (explicit or interface) - if(isAnnotation) { - StructAnnDefaultAttribute attr = (StructAnnDefaultAttribute)mt.getAttributes().getWithKey("AnnotationDefault"); - if(attr != null) { - bufstrwriter.write(" default "); - bufstrwriter.write(attr.getDefaultValue().toJava(indent+1)); - } - } - - bufstrwriter.write(";"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } else { - if(!clinit && !dinit) { - bufstrwriter.write(" "); - } - bufstrwriter.write("{"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - - RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; - - if(root != null && !meth.decompiledWithErrors) { // check for existence - try { - String code = root.toJava(indent+1); - - boolean singleinit = false; - if(init && param_count_explicit == 0 && !throwsExceptions && DecompilerContext.getOption(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR)) { - int init_counter = 0; - for(MethodWrapper mth : wrapper.getMethods()) { - if("<init>".equals(mth.methodStruct.getName())) { - init_counter++; - } - } - singleinit = (init_counter == 1); - } - - hidemethod = (clinit || dinit || singleinit) && code.length() == 0; - - bufstrwriter.write(code); - } catch(Throwable ex) { - DecompilerContext.getLogger().writeMessage("Method "+mt.getName()+" "+mt.getDescriptor()+" couldn't be written.", ex); - meth.decompiledWithErrors = true; - } - } - - if(meth.decompiledWithErrors) { - bufstrwriter.write(InterpreterUtil.getIndentString(indent+1)); - bufstrwriter.write("// $FF: Couldn't be decompiled"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - bufstrwriter.write(indstr+"}"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - bufstrwriter.flush(); - - if(!hidemethod) { - writer.write(strwriter.toString()); - } - - DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methold); - - return !hidemethod; - } - - private List<AnnotationExprent> getAllAnnotations(VBStyleCollection<StructGeneralAttribute, String> attributes) { - - String[] annattrnames = new String[] {StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, - StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS}; - - List<AnnotationExprent> lst = new ArrayList<AnnotationExprent>(); - - for(String attrname : annattrnames) { - StructAnnotationAttribute attr = (StructAnnotationAttribute)attributes.getWithKey(attrname); - if(attr != null) { - lst.addAll(attr.getAnnotations()); - } - } - - return lst; - } - - private List<List<AnnotationExprent>> getAllParameterAnnotations(VBStyleCollection<StructGeneralAttribute, String> attributes) { - - String[] annattrnames = new String[] {StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, - StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS}; - - List<List<AnnotationExprent>> ret = new ArrayList<List<AnnotationExprent>>(); - - for(String attrname : annattrnames) { - StructAnnotationParameterAttribute attr = (StructAnnotationParameterAttribute)attributes.getWithKey(attrname); - if(attr != null) { - for(int i=0;i<attr.getParamAnnotations().size();i++) { - List<AnnotationExprent> lst = new ArrayList<AnnotationExprent>(); - boolean isnew = (ret.size()<=i); - - if(!isnew) { - lst = ret.get(i); - } - lst.addAll(attr.getParamAnnotations().get(i)); - - if(isnew) { - ret.add(lst); - } else { - ret.set(i, lst); - } - } - } - } - - return ret; - } - - private String getDescriptorPrintOut(String descriptor, int element) { - - switch(element) { - case 0: // class - return ExprProcessor.buildJavaClassName(descriptor); - case 1: // field - return getTypePrintOut(FieldDescriptor.parseDescriptor(descriptor).type); - case 2: // method - default: - MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor); - - StringBuilder buffer = new StringBuilder("("); - - boolean first = true; - for(VarType partype : md.params) { - if(first) { - first = false; - } else { - buffer.append(", "); - } - buffer.append(getTypePrintOut(partype)); - } - buffer.append(") "); - buffer.append(getTypePrintOut(md.ret)); - - return buffer.toString(); - } - } - - private String getTypePrintOut(VarType type) { - String strtype = ExprProcessor.getCastTypeName(type, false); - if(ExprProcessor.UNDEFINED_TYPE_STRING.equals(strtype) && - DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { - strtype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT, false); - } - return strtype; - } + if (isEnum && init) actualParams -= 2; + if (actualParams != descriptor.params.size()) { + DecompilerContext.getLogger() + .writeMessage("Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor(), IFernflowerLogger.WARNING); + descriptor = null; + } + } + } + + boolean throwsExceptions = false; + + int param_count_explicit = 0; + + if (!clinit && !dinit) { + + boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; + + // formal type parameters + if (descriptor != null && !descriptor.fparameters.isEmpty()) { + bufstrwriter.write("<"); + for (int i = 0; i < descriptor.fparameters.size(); i++) { + if (i > 0) { + bufstrwriter.write(", "); + } + bufstrwriter.write(descriptor.fparameters.get(i)); + + List<GenericType> lstBounds = descriptor.fbounds.get(i); + if (lstBounds.size() > 1 || !"java/lang/Object".equals(lstBounds.get(0).value)) { + bufstrwriter.write(" extends "); + bufstrwriter.write(GenericMain.getGenericCastTypeName(lstBounds.get(0))); + + for (int j = 1; j < lstBounds.size(); j++) { + bufstrwriter.write(" & " + GenericMain.getGenericCastTypeName(lstBounds.get(j))); + } + } + } + bufstrwriter.write("> "); + } + + if (!init) { + if (descriptor != null) { + bufstrwriter.write(GenericMain.getGenericCastTypeName(descriptor.ret)); + } + else { + bufstrwriter.write(ExprProcessor.getCastTypeName(md.ret)); + } + bufstrwriter.write(" "); + } + + bufstrwriter.write(name); + bufstrwriter.write("("); + + // parameter annotations + List<List<AnnotationExprent>> lstParAnn = getAllParameterAnnotations(mt.getAttributes()); + + List<VarVersionPaar> signFields = meth.signatureFields; + + // compute last visible parameter + int lastparam_index = -1; + for (int i = 0; i < md.params.length; i++) { + if (signFields == null || signFields.get(i) == null) { + lastparam_index = i; + } + } + + boolean firstpar = true; + int index = isEnum && init ? 3 : thisvar ? 1 : 0; + int start = isEnum && init && descriptor == null ? 2 : 0; + int params = descriptor == null ? md.params.length : descriptor.params.size(); + for (int i = start; i < params; i++) { + if (signFields == null || signFields.get(i) == null) { + + if (!firstpar) { + bufstrwriter.write(", "); + } + + if (lstParAnn.size() > param_count_explicit) { + List<AnnotationExprent> annotations = lstParAnn.get(param_count_explicit); + for (int j = 0; j < annotations.size(); j++) { + AnnotationExprent annexpr = annotations.get(j); + if (annexpr.getAnnotationType() == AnnotationExprent.ANNOTATION_NORMAL) { + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + bufstrwriter.write(annexpr.toJava(indent + 1)); + } + else { + bufstrwriter.write(annexpr.toJava(0)); + } + bufstrwriter.write(" "); + } + } + + if (meth.varproc.getVarFinal(new VarVersionPaar(index, 0)) == VarTypeProcessor.VAR_FINALEXPLICIT) { + bufstrwriter.write("final "); + } + + + if (descriptor != null) { + GenericType partype = descriptor.params.get(i); + + boolean isVarArgs = (i == lastparam_index && (mt.getAccessFlags() & CodeConstants.ACC_VARARGS) != 0 + && partype.arraydim > 0); + + if (isVarArgs) { + partype.arraydim--; + } + + String strpartype = GenericMain.getGenericCastTypeName(partype); + if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && + DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { + strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); + } + + bufstrwriter.write(strpartype); + + if (isVarArgs) { + bufstrwriter.write(" ..."); + } + } + else { + VarType partype = md.params[i].copy(); + + boolean isVarArgs = (i == lastparam_index && (mt.getAccessFlags() & CodeConstants.ACC_VARARGS) != 0 + && partype.arraydim > 0); + + if (isVarArgs) { + partype.decArrayDim(); + } + + String strpartype = ExprProcessor.getCastTypeName(partype); + if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && + DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { + strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); + } + + bufstrwriter.write(strpartype); + + if (isVarArgs) { + bufstrwriter.write(" ..."); + } + } + + bufstrwriter.write(" "); + String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); + bufstrwriter.write(parname == null ? "param" + index : parname); // null iff decompiled with errors + firstpar = false; + param_count_explicit++; + } + + index += md.params[i].stack_size; + } + + bufstrwriter.write(")"); + + StructExceptionsAttribute attr = (StructExceptionsAttribute)mt.getAttributes().getWithKey("Exceptions"); + if ((descriptor != null && !descriptor.exceptions.isEmpty()) || attr != null) { + throwsExceptions = true; + bufstrwriter.write(" throws "); + + for (int i = 0; i < attr.getThrowsExceptions().size(); i++) { + if (i > 0) { + bufstrwriter.write(", "); + } + if (descriptor != null && !descriptor.exceptions.isEmpty()) { + bufstrwriter.write(GenericMain.getGenericCastTypeName(descriptor.exceptions.get(i))); + } + else { + VarType exctype = new VarType(attr.getExcClassname(i, cl.getPool()), true); + bufstrwriter.write(ExprProcessor.getCastTypeName(exctype)); + } + } + } + } + + boolean hidemethod = false; + + if ((flags & (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_NATIVE)) != 0) { // native or abstract method (explicit or interface) + if (isAnnotation) { + StructAnnDefaultAttribute attr = (StructAnnDefaultAttribute)mt.getAttributes().getWithKey("AnnotationDefault"); + if (attr != null) { + bufstrwriter.write(" default "); + bufstrwriter.write(attr.getDefaultValue().toJava(indent + 1)); + } + } + + bufstrwriter.write(";"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + else { + if (!clinit && !dinit) { + bufstrwriter.write(" "); + } + bufstrwriter.write("{"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + + RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; + + if (root != null && !meth.decompiledWithErrors) { // check for existence + try { + String code = root.toJava(indent + 1); + + boolean singleinit = false; + if (init && + param_count_explicit == 0 && + !throwsExceptions && + DecompilerContext.getOption(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR)) { + int init_counter = 0; + for (MethodWrapper mth : wrapper.getMethods()) { + if ("<init>".equals(mth.methodStruct.getName())) { + init_counter++; + } + } + singleinit = (init_counter == 1); + } + + hidemethod = (clinit || dinit || singleinit) && code.length() == 0; + + bufstrwriter.write(code); + } + catch (Throwable ex) { + DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex); + meth.decompiledWithErrors = true; + } + } + + if (meth.decompiledWithErrors) { + bufstrwriter.write(InterpreterUtil.getIndentString(indent + 1)); + bufstrwriter.write("// $FF: Couldn't be decompiled"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + bufstrwriter.write(indstr + "}"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + bufstrwriter.flush(); + + if (!hidemethod) { + writer.write(strwriter.toString()); + } + + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methold); + + return !hidemethod; + } + + private List<AnnotationExprent> getAllAnnotations(VBStyleCollection<StructGeneralAttribute, String> attributes) { + + String[] annattrnames = new String[]{StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, + StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS}; + + List<AnnotationExprent> lst = new ArrayList<AnnotationExprent>(); + + for (String attrname : annattrnames) { + StructAnnotationAttribute attr = (StructAnnotationAttribute)attributes.getWithKey(attrname); + if (attr != null) { + lst.addAll(attr.getAnnotations()); + } + } + + return lst; + } + + private List<List<AnnotationExprent>> getAllParameterAnnotations(VBStyleCollection<StructGeneralAttribute, String> attributes) { + + String[] annattrnames = new String[]{StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, + StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS}; + + List<List<AnnotationExprent>> ret = new ArrayList<List<AnnotationExprent>>(); + + for (String attrname : annattrnames) { + StructAnnotationParameterAttribute attr = (StructAnnotationParameterAttribute)attributes.getWithKey(attrname); + if (attr != null) { + for (int i = 0; i < attr.getParamAnnotations().size(); i++) { + List<AnnotationExprent> lst = new ArrayList<AnnotationExprent>(); + boolean isnew = (ret.size() <= i); + + if (!isnew) { + lst = ret.get(i); + } + lst.addAll(attr.getParamAnnotations().get(i)); + + if (isnew) { + ret.add(lst); + } + else { + ret.set(i, lst); + } + } + } + } + + return ret; + } + + private String getDescriptorPrintOut(String descriptor, int element) { + + switch (element) { + case 0: // class + return ExprProcessor.buildJavaClassName(descriptor); + case 1: // field + return getTypePrintOut(FieldDescriptor.parseDescriptor(descriptor).type); + case 2: // method + default: + MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor); + + StringBuilder buffer = new StringBuilder("("); + + boolean first = true; + for (VarType partype : md.params) { + if (first) { + first = false; + } + else { + buffer.append(", "); + } + buffer.append(getTypePrintOut(partype)); + } + buffer.append(") "); + buffer.append(getTypePrintOut(md.ret)); + + return buffer.toString(); + } + } + + private String getTypePrintOut(VarType type) { + String strtype = ExprProcessor.getCastTypeName(type, false); + if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(strtype) && + DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { + strtype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT, false); + } + return strtype; + } } diff --git a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java index e0e144f..155dee0 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -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) { - - 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 - - String innername = strentry[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) { + 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) { + + 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 + + String innername = strentry[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; - } - - 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); - } - - 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; - } - } + + 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; + } + + 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); + } + + 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; + } + } } diff --git a/src/org/jetbrains/java/decompiler/main/DecompilerContext.java b/src/org/jetbrains/java/decompiler/main/DecompilerContext.java index fc721b7..cb4bbc8 100644 --- a/src/org/jetbrains/java/decompiler/main/DecompilerContext.java +++ b/src/org/jetbrains/java/decompiler/main/DecompilerContext.java @@ -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; - - private IFernflowerLogger logger; - - - private DecompilerContext(HashMap<String, Object> properties) { - this.properties.putAll(properties); - } - - public static void initContext(HashMap<String, Object> propertiesCustom) { - - 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"); - - mapDefault.put(IFernflowerPreferences.USE_DEBUG_VARNAMES, "1"); - mapDefault.put(IFernflowerPreferences.MAX_PROCESSING_METHOD, "0"); - - mapDefault.put(IFernflowerPreferences.REMOVE_EMPTY_RANGES, "1"); - - mapDefault.put(IFernflowerPreferences.NEW_LINE_SEPARATOR, "0"); - mapDefault.put(IFernflowerPreferences.INDENT_STRING, " "); - - mapDefault.put(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION, "1"); - - if(propertiesCustom != null) { - mapDefault.putAll(propertiesCustom); - } - - 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 ; - } + + 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; + + private IFernflowerLogger logger; + + + private DecompilerContext(HashMap<String, Object> properties) { + this.properties.putAll(properties); + } + + public static void initContext(HashMap<String, Object> propertiesCustom) { + + 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"); + + mapDefault.put(IFernflowerPreferences.USE_DEBUG_VARNAMES, "1"); + mapDefault.put(IFernflowerPreferences.MAX_PROCESSING_METHOD, "0"); + + mapDefault.put(IFernflowerPreferences.REMOVE_EMPTY_RANGES, "1"); + + mapDefault.put(IFernflowerPreferences.NEW_LINE_SEPARATOR, "0"); + mapDefault.put(IFernflowerPreferences.INDENT_STRING, " "); + + mapDefault.put(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION, "1"); + + if (propertiesCustom != null) { + mapDefault.putAll(propertiesCustom); + } + + 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; + } } diff --git a/src/org/jetbrains/java/decompiler/main/EnumProcessor.java b/src/org/jetbrains/java/decompiler/main/EnumProcessor.java index 7537596..e991827 100644 --- a/src/org/jetbrains/java/decompiler/main/EnumProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/EnumProcessor.java @@ -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) { - - 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); + 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); } - } - } - } - - - - } - - 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; - } - + } + } + } + + // 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); + } + } + } + } + } + + 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; + } } diff --git a/src/org/jetbrains/java/decompiler/main/Fernflower.java b/src/org/jetbrains/java/decompiler/main/Fernflower.java index 021ab1a..4365ca1 100644 --- a/src/org/jetbrains/java/decompiler/main/Fernflower.java +++ b/src/org/jetbrains/java/decompiler/main/Fernflower.java @@ -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) { - - StructContext context = new StructContext(saver, this, new LazyLoader(provider)); - - 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 static final String version = "v0.8.4"; + + private StructContext structcontext; + + private ClassesProcessor clprocessor; + + public Fernflower(IBytecodeProvider provider, IDecompilatSaver saver, + HashMap<String, Object> propertiesCustom) { + + StructContext context = new StructContext(saver, this, new LazyLoader(provider)); + + 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 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; - } - + 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 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; + } } diff --git a/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java b/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java index ff6233c..c738163 100644 --- a/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java @@ -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; - } - - - 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; - } + 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; + } + + + 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; + } } diff --git a/src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java b/src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java index dcc1968..04d50a7 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java @@ -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; + + private int[] values = new int[]{1, 1, 1}; + + public void setCounter(int counter, int value) { + values[counter] = value; + } - public int getCounter(int counter) { - return values[counter]; - } + public int getCounter(int counter) { + return values[counter]; + } - public int getCounterAndIncrement(int counter) { - return values[counter]++; - } - + public int getCounterAndIncrement(int counter) { + return values[counter]++; + } } diff --git a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java index cdc6529..f45bf27 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java @@ -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 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.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.main.DecompilerContext; import org.jetbrains.java.decompiler.struct.StructContext; +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.*; +import java.util.Map.Entry; + 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 += "/"; - } - } - - 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; - } - - + 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 += "/"; + } + } + + 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; + } } diff --git a/src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java b/src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java index 5fd33c0..dfde971 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java @@ -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() {} - - public VarNamesCollector(HashSet<String> setNames) { - usedNames.addAll(setNames); - } - - 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; - } - + private HashSet<String> usedNames = new HashSet<String>(); + + public VarNamesCollector() { + } + + public VarNamesCollector(HashSet<String> setNames) { + usedNames.addAll(setNames); + } + + 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; + } } diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java index 408c430..995f8c2 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java @@ -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 HashMap<String, HashSet<String>> mapArchiveEntries = new HashMap<String, HashSet<String>>(); - - public ConsoleDecompiler() { - this(null); - } - - 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) { - - 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; - - if(arg.startsWith("-e=")) { - lstLibraries.add(arg.substring(3)); - } else { - lstSources.add(arg); - } - } - } - - if(lstSources.isEmpty()) { - printHelp(); - } else { - ConsoleDecompiler decompiler = new ConsoleDecompiler( - new PrintStreamLogger(IFernflowerLogger.INFO, System.out), - mapOptions); - - for(String source : lstSources) { - decompiler.addSpace(new File(source), true); - } - - 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); - - out.flush(); - out.close(); - - } catch(IOException ex) { - ex.printStackTrace(); - } - } - - + private File root; + + private Fernflower fernflower; + + private HashMap<String, ZipOutputStream> mapArchiveStreams = new HashMap<String, ZipOutputStream>(); + + private HashMap<String, HashSet<String>> mapArchiveEntries = new HashMap<String, HashSet<String>>(); + + public ConsoleDecompiler() { + this(null); + } + + 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) { + + 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; + + if (arg.startsWith("-e=")) { + lstLibraries.add(arg.substring(3)); + } + else { + lstSources.add(arg); + } + } + } + + if (lstSources.isEmpty()) { + printHelp(); + } + else { + ConsoleDecompiler decompiler = new ConsoleDecompiler( + new PrintStreamLogger(IFernflowerLogger.INFO, System.out), + mapOptions); + + for (String source : lstSources) { + decompiler.addSpace(new File(source), true); + } + + 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); + + out.flush(); + out.close(); + } + catch (IOException ex) { + ex.printStackTrace(); + } + } } diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/IdeDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/IdeDecompiler.java index 07dd0f0..dab59e9 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/IdeDecompiler.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/IdeDecompiler.java @@ -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(); } } - } diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/WebDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/WebDecompiler.java index 5f20c01..06e4c6f 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/WebDecompiler.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/WebDecompiler.java @@ -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); - } - - @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; - } - + 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); + } + + @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; + } } diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/helper/PrintStreamLogger.java b/src/org/jetbrains/java/decompiler/main/decompiler/helper/PrintStreamLogger.java index 6d03b34..3a6c7e8 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/helper/PrintStreamLogger.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/helper/PrintStreamLogger.java @@ -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); - } - } - - public void writeMessage(String message, Throwable t) { - t.printStackTrace(stream); - writeMessage(message, ERROR); - } - - public void startClass(String classname) { - stream.println(InterpreterUtil.getIndentString(indent++)+"Processing class "+classname+" ..."); - } - - 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; - } + 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); + } + } + + public void writeMessage(String message, Throwable t) { + t.printStackTrace(stream); + writeMessage(message, ERROR); + } + + public void startClass(String classname) { + stream.println(InterpreterUtil.getIndentString(indent++) + "Processing class " + classname + " ..."); + } + + 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; + } } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java b/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java index 6e13e51..7ff43fc 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java @@ -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); } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IDecompilatSaver.java b/src/org/jetbrains/java/decompiler/main/extern/IDecompilatSaver.java index 855f95c..0270b5e 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IDecompilatSaver.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IDecompilatSaver.java @@ -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 saveFile(String path, String filename, String content); - public void saveFolder(String path); - - public void saveClassFile(String path, String qualifiedName, String entryName, String content); + public void createArchive(String path, String archivename, Manifest manifest); - public void saveFile(String path, String filename, String content); - - public void createArchive(String path, String archivename, Manifest manifest); + public void saveClassEntry(String path, String archivename, String qualifiedName, String entryName, String content); - 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 saveEntry(String path, String archivename, String entryName, String content); + public void copyEntry(String source, String destpath, String archivename, String entry); - public void copyEntry(String source, String destpath, String archivename, String entry); - - public void closeArchive(String path, String archivename); - + public void closeArchive(String path, String archivename); } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java index eb1427c..c80039d 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java @@ -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 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 void writeMessage(String message, Throwable t); - - public void startClass(String classname); - - public void endClass(); - - public void startWriteClass(String classname); - - public void endWriteClass(); - - public void startMethod(String method); - - public void endMethod(); - - public int getSeverity(); - - public void setSeverity(int severity); + 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 void writeMessage(String message, Throwable t); + + public void startClass(String classname); + + public void endClass(); + + public void startWriteClass(String classname); + + public void endWriteClass(); + + public void startMethod(String method); + + public void endMethod(); + + public int getSeverity(); + + public void setSeverity(int severity); } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java index 9f19127..2fcc5e1 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java @@ -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 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 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 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"; } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java b/src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java index f379da6..88b1ca4 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java @@ -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); } diff --git a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java index dcbeaea..f0e8591 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java @@ -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 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); - - // 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); - - 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; - } + private StructClass classStruct; + + private HashSet<String> hideMembers = new HashSet<String>(); + + private VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<Exprent, String>(); + + 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); + // 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); + + 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; + } } diff --git a/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java index bf5f72f..3d42bda 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java @@ -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 { - - for(ClassNode child : node.nested) { - processClass(child); - } - - if(node.nested.isEmpty()) { - hasLambda(node); - } - } - - public boolean hasLambda(ClassNode node) throws IOException { - - 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 - } - - 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; - } - + 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 { + + for (ClassNode child : node.nested) { + processClass(child); + } + + if (node.nested.isEmpty()) { + hasLambda(node); + } + } + + public boolean hasLambda(ClassNode node) throws IOException { + + 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 + } + + 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; + } } diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java index c3c180b..038e057 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java @@ -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() { - - DecompilerContext.setCurrentContext(parentContext); - - 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()); - - -// if(mt.getName().endsWith("_getActiveServers")) { -// System.out.println(); -// } - - //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern1.dot"), true); - - DeadCodeHelper.removeDeadBlocks(graph); - graph.inlineJsr(mt); - -// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern4.dot"), true); - - // TODO: move to the start, before jsr inlining - DeadCodeHelper.connectDummyExitBlock(graph); - - DeadCodeHelper.removeGotos(graph); - ExceptionDeobfuscator.removeCircularRanges(graph); - //DeadCodeHelper.removeCircularRanges(graph); - - -// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); - - ExceptionDeobfuscator.restorePopRanges(graph); - - 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); - -// ExceptionDeobfuscator.restorePopRanges(graph); - ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph); - - DeadCodeHelper.mergeBasicBlocks(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()); - - //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; - } - + 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() { + + DecompilerContext.setCurrentContext(parentContext); + + 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()); + + + // if(mt.getName().endsWith("_getActiveServers")) { + // System.out.println(); + // } + + //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern1.dot"), true); + + DeadCodeHelper.removeDeadBlocks(graph); + graph.inlineJsr(mt); + + // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern4.dot"), true); + + // TODO: move to the start, before jsr inlining + DeadCodeHelper.connectDummyExitBlock(graph); + + DeadCodeHelper.removeGotos(graph); + ExceptionDeobfuscator.removeCircularRanges(graph); + //DeadCodeHelper.removeCircularRanges(graph); + + + // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); + + ExceptionDeobfuscator.restorePopRanges(graph); + + 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); + + // ExceptionDeobfuscator.restorePopRanges(graph); + ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph); + + DeadCodeHelper.mergeBasicBlocks(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()); + + //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; + } } diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java index 4f8d6f8..d36b8bc 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java @@ -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; + } } diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java index 1c962ec..9dba114 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java @@ -1,41 +1,28 @@ /* - * 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.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.Map.Entry; - 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.IFernflowerLogger; 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.ConstExprent; -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.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.sforms.DirectNode; import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; @@ -51,970 +38,995 @@ import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.*; +import java.util.Map.Entry; + public class NestedClassProcessor { - - - public void processClass(ClassNode root, ClassNode node) { - - // hide synthetic lambda content methods - if(node.type == ClassNode.CLASS_LAMBDA && !node.lambda_information.is_method_reference) { - ClassNode node_content = DecompilerContext.getClassprocessor().getMapRootClasses().get(node.classStruct.qualifiedName); - if(node_content != null && node_content.wrapper != null) { - node_content.wrapper.getHideMembers().add(node.lambda_information.content_method_key); - } - } - - if(node.nested.isEmpty()) { - return; - } - - if(node.type != ClassNode.CLASS_LAMBDA) { - - computeLocalVarsAndDefinitions(node); - - // for each local or anonymous class ensure not empty enclosing method - checkNotFoundClasses(root, node); - } - - int nameless = 0, synthetics = 0; - for(ClassNode child : node.nested) { - // ensure not-empty class name - if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_MEMBER) && child.simpleName == null) { - StructClass cl = child.classStruct; - if (((child.access | cl.access_flags) & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic")) { - child.simpleName = "SyntheticClass_" + (++synthetics); - } else { - DecompilerContext.getLogger().writeMessage("Nameless local or member class " + cl.qualifiedName + "!", IFernflowerLogger.WARNING); - child.simpleName = "NamelessClass_" + (++nameless); - } - } - } - - for(ClassNode child : node.nested) { - if(child.type == ClassNode.CLASS_LAMBDA) { - setLambdaVars(node, child); - } else { - if(child.type != ClassNode.CLASS_MEMBER || (child.access & CodeConstants.ACC_STATIC) == 0) { - insertLocalVars(node, child); - - if(child.type == ClassNode.CLASS_LOCAL) { - setLocalClassDefinition(node.wrapper.getMethods().getWithKey(child.enclosingMethod), child); - } - } - } - } - - for(ClassNode child : node.nested) { - processClass(root, child); - } - - } - - private void setLambdaVars(ClassNode parent, ClassNode child) { - - if(child.lambda_information.is_method_reference) { // method reference, no code and no parameters - return; - } - - final MethodWrapper meth = parent.wrapper.getMethods().getWithKey(child.lambda_information.content_method_key); - final MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); - - MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(child.lambda_information.method_descriptor); - final MethodDescriptor md_content = MethodDescriptor.parseDescriptor(child.lambda_information.content_method_descriptor); - - final int vars_count = md_content.params.length - md_lambda.params.length; -// if(vars_count < 0) { // should not happen, but just in case... -// vars_count = 0; -// } - - final boolean is_static_lambda_content = child.lambda_information.is_content_method_static; - - final String parent_class_name = parent.wrapper.getClassStruct().qualifiedName; - final String lambda_class_name = child.simpleName; - - final VarType lambda_class_type = new VarType(lambda_class_name, true); - - // this pointer - if(!is_static_lambda_content && DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) { - meth.varproc.getThisvars().put(new VarVersionPaar(0, 0), parent_class_name); - meth.varproc.setVarName(new VarVersionPaar(0, 0), parent.simpleName + ".this"); - } - - // local variables - DirectGraph graph = encmeth.getOrBuildGraph(); - - final HashMap<VarVersionPaar, String> mapNewNames = new HashMap<VarVersionPaar, String>(); - - graph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - - List<Exprent> lst = exprent.getAllExprents(true); - lst.add(exprent); - - for(Exprent expr: lst) { - - if(expr.type == Exprent.EXPRENT_NEW) { - NewExprent new_expr = (NewExprent)expr; - if(new_expr.isLambda() && lambda_class_type.equals(new_expr.getNewtype())) { - - InvocationExprent inv_dynamic = new_expr.getConstructor(); - - int param_index = is_static_lambda_content ? 0 : 1;; - int varindex = is_static_lambda_content ? 0 : 1; - - for(int i = 0; i < vars_count; ++i) { - - Exprent param = inv_dynamic.getLstParameters().get(param_index + i); - - if(param.type == Exprent.EXPRENT_VAR) { - VarVersionPaar enc_varpaar = new VarVersionPaar((VarExprent)param); - String enc_varname = encmeth.varproc.getVarName(enc_varpaar); - - //meth.varproc.setVarName(new VarVersionPaar(varindex, 0), enc_varname); - mapNewNames.put(new VarVersionPaar(varindex, 0), enc_varname); - } - - varindex+=md_content.params[i].stack_size; - } - - } - } - } - - return 0; - } - }); - - // update names of local variables - HashSet<String> setNewOuterNames = new HashSet<String>(mapNewNames.values()); - setNewOuterNames.removeAll(meth.setOuterVarNames); - - meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); - meth.setOuterVarNames.addAll(setNewOuterNames); - - for(Entry<VarVersionPaar, String> entr : mapNewNames.entrySet()) { - meth.varproc.setVarName(entr.getKey(), entr.getValue()); - } - - } - - private void checkNotFoundClasses(ClassNode root, ClassNode node) { - - List<ClassNode> lstChildren = new ArrayList<ClassNode>(node.nested); - - for(ClassNode child : lstChildren) { - - if((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_ANONYMOUS) && child.enclosingMethod == null) { - - Set<String> setEnclosing = child.enclosingClasses; - - if(setEnclosing.size() == 1) { - StructEnclosingMethodAttribute attr = (StructEnclosingMethodAttribute)child.classStruct.getAttributes().getWithKey("EnclosingMethod"); - if(attr != null && attr.getMethodName() != null) { - if(node.classStruct.qualifiedName.equals(attr.getClassname()) && - node.classStruct.getMethod(attr.getMethodName(), attr.getMethodDescriptor()) != null) { - child.enclosingMethod = InterpreterUtil.makeUniqueKey(attr.getMethodName(), attr.getMethodDescriptor()); - continue; - } - } - } - - node.nested.remove(child); - child.parent = null; - setEnclosing.remove(node.classStruct.qualifiedName); - - boolean hasEnclosing = !setEnclosing.isEmpty(); - if(hasEnclosing) { - hasEnclosing = insertNestedClass(root, child); - } - - if(!hasEnclosing) { - if(child.type == ClassNode.CLASS_ANONYMOUS) { - DecompilerContext.getLogger().writeMessage("Unreferenced anonymous class "+child.classStruct.qualifiedName+"!", IFernflowerLogger.WARNING); - } else if(child.type == ClassNode.CLASS_LOCAL) { - DecompilerContext.getLogger().writeMessage("Unreferenced local class "+child.classStruct.qualifiedName+"!", IFernflowerLogger.WARNING); - } - } - } - } - } - - private boolean insertNestedClass(ClassNode root, ClassNode child) { - - Set<String> setEnclosing = child.enclosingClasses; - - LinkedList<ClassNode> stack = new LinkedList<ClassNode>(); - stack.add(root); - - while(!stack.isEmpty()) { - - ClassNode node = stack.removeFirst(); - - if(setEnclosing.contains(node.classStruct.qualifiedName)) { - node.nested.add(child); - child.parent = node; - - return true; - } - - // note: ordered list - stack.addAll(node.nested); - } - - return false; - } - - - private void computeLocalVarsAndDefinitions(final ClassNode node) { - - // local var masks - // class name, constructor descriptor, field mask - final HashMap<String, HashMap<String, List<VarFieldPair>>> mapVarMasks = new HashMap<String, HashMap<String, List<VarFieldPair>>>(); - - int cltypes = 0; - - for(ClassNode nd: node.nested) { - if(nd.type != ClassNode.CLASS_LAMBDA) { - if((nd.access & CodeConstants.ACC_STATIC) == 0 && (nd.access & CodeConstants.ACC_INTERFACE) == 0) { - - cltypes |= nd.type; - - HashMap<String, List<VarFieldPair>> mask = getMaskLocalVars(nd.wrapper); - if(mask.isEmpty()) { - DecompilerContext.getLogger().writeMessage("Nested class "+nd.classStruct.qualifiedName+" has no constructor!", IFernflowerLogger.WARNING); - } else { - mapVarMasks.put(nd.classStruct.qualifiedName, mask); - } - } - } - } - - // local var masks - final HashMap<String, HashMap<String, List<VarFieldPair>>> mapVarFieldPairs = new HashMap<String, HashMap<String, List<VarFieldPair>>>(); - - if(cltypes != ClassNode.CLASS_MEMBER) { - - // iterate enclosing class - for(final MethodWrapper meth: node.wrapper.getMethods()) { - - if(meth.root != null) { // neither abstract, nor native - DirectGraph graph = meth.getOrBuildGraph(); - - graph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - List<Exprent> lst = exprent.getAllExprents(true); - lst.add(exprent); - - for(Exprent expr: lst) { - - if(expr.type == Exprent.EXPRENT_NEW) { - InvocationExprent constr = ((NewExprent)expr).getConstructor(); - - if(constr != null && mapVarMasks.containsKey(constr.getClassname())) { // non-static inner class constructor - - String refclname = constr.getClassname(); - - ClassNode nestedClassNode = node.getClassNode(refclname); - - if(nestedClassNode.type != ClassNode.CLASS_MEMBER) { - - List<VarFieldPair> mask = mapVarMasks.get(refclname).get(constr.getStringDescriptor()); - - if(!mapVarFieldPairs.containsKey(refclname)) { - mapVarFieldPairs.put(refclname, new HashMap<String, List<VarFieldPair>>()); - } - - List<VarFieldPair> lstTemp = new ArrayList<VarFieldPair>(); - - for(int i=0;i<mask.size();i++) { - Exprent param = constr.getLstParameters().get(i); - VarFieldPair pair = null; - - if(param.type == Exprent.EXPRENT_VAR && mask.get(i) != null) { - VarVersionPaar varpaar = new VarVersionPaar((VarExprent)param); - - // FIXME: final flags of variables are wrong! Correct the entire final functionality. -// if(meth.varproc.getVarFinal(varpaar) != VarTypeProcessor.VAR_NONFINAL) { - pair = new VarFieldPair(mask.get(i).keyfield, varpaar); -// } - } - - lstTemp.add(pair); - } - - List<VarFieldPair> pairmask = mapVarFieldPairs.get(refclname).get(constr.getStringDescriptor()); - - if(pairmask == null) { - pairmask = lstTemp; - } else { - for(int i=0;i<pairmask.size();i++) { - if(!InterpreterUtil.equalObjects(pairmask.get(i), lstTemp.get(i))) { - pairmask.set(i, null); - } - } - } - - mapVarFieldPairs.get(refclname).put(constr.getStringDescriptor(), pairmask); - nestedClassNode.enclosingMethod = InterpreterUtil.makeUniqueKey(meth.methodStruct.getName(), meth.methodStruct.getDescriptor()); - } - } - } - - } - return 0; - } - }); - } - } - } - - // merge var masks - for(Entry<String, HashMap<String, List<VarFieldPair>>> entcl : mapVarMasks.entrySet()) { - - ClassNode nestedNode = node.getClassNode(entcl.getKey()); - - // intersection - List<VarFieldPair> intrPairMask = null; - // merge referenced constructors - if(mapVarFieldPairs.containsKey(entcl.getKey())) { - for(List<VarFieldPair> mask : mapVarFieldPairs.get(entcl.getKey()).values()) { - if(intrPairMask == null) { - intrPairMask = new ArrayList<VarFieldPair>(mask); - } else { - mergeListSignatures(intrPairMask, mask, false); - } - } - } - - List<VarFieldPair> intrMask = null; - // merge all constructors - for(List<VarFieldPair> mask : entcl.getValue().values()) { - if(intrMask == null) { - intrMask = new ArrayList<VarFieldPair>(mask); - } else { - mergeListSignatures(intrMask, mask, false); - } - } - - if(intrPairMask == null) { // member or local and never instantiated - intrPairMask = new ArrayList<VarFieldPair>(intrMask); - - boolean found = false; - - for(int i=0;i<intrPairMask.size();i++) { - if(intrPairMask.get(i) != null) { - if(found) { - intrPairMask.set(i, null); - } - found = true; - } - } - } - - mergeListSignatures(intrPairMask, intrMask, true); - - for(int i=0;i<intrPairMask.size();i++) { - VarFieldPair pair = intrPairMask.get(i); - if(pair != null && pair.keyfield.length() > 0) { - nestedNode.mapFieldsToVars.put(pair.keyfield, pair.varpaar); - } - } - - // set resulting constructor signatures - for(Entry<String, List<VarFieldPair>> entmt : entcl.getValue().entrySet()) { - mergeListSignatures(entmt.getValue(), intrPairMask, false); - - MethodWrapper meth = nestedNode.wrapper.getMethodWrapper("<init>", entmt.getKey()); - meth.signatureFields = new ArrayList<VarVersionPaar>(); - - for(VarFieldPair pair : entmt.getValue()) { - meth.signatureFields.add(pair==null?null:pair.varpaar); - } - } - - } - - } - - private void insertLocalVars(final ClassNode parent, final ClassNode child) { - - // enclosing method, is null iff member class - MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); - - // iterate all child methods - for(final MethodWrapper meth : child.wrapper.getMethods()) { - - if(meth.root != null) { // neither abstract nor native - - // local var names - HashMap<VarVersionPaar, String> mapNewNames = new HashMap<VarVersionPaar, String>(); - // local var types - HashMap<VarVersionPaar, VarType> mapNewTypes = new HashMap<VarVersionPaar, VarType>(); - - final HashMap<Integer, VarVersionPaar> mapParamsToNewVars = new HashMap<Integer, VarVersionPaar>(); - if(meth.signatureFields != null) { - int index = 0; - int varindex = 1; - MethodDescriptor md = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor()); - - for(VarVersionPaar paar : meth.signatureFields) { - if(paar != null) { - VarVersionPaar newvar = new VarVersionPaar(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); - - mapParamsToNewVars.put(varindex, newvar); - - String varname = null; - VarType vartype = null; - - if(child.type != ClassNode.CLASS_MEMBER) { - varname = encmeth.varproc.getVarName(paar); - vartype = encmeth.varproc.getVarType(paar); - - encmeth.varproc.setVarFinal(paar, VarTypeProcessor.VAR_FINALEXPLICIT); - } - - if(paar.var == -1 || "this".equals(varname)) { - if(parent.simpleName == null) { - // anonymous enclosing class, no access to this - varname = VarExprent.VAR_NAMELESS_ENCLOSURE; - } else { - varname = parent.simpleName+".this"; - } - meth.varproc.getThisvars().put(newvar, parent.classStruct.qualifiedName); - } - - mapNewNames.put(newvar, varname); - mapNewTypes.put(newvar, vartype); - } - varindex+=md.params[index++].stack_size; - } - } - - // new vars - final HashMap<String, VarVersionPaar> mapFieldsToNewVars = new HashMap<String, VarVersionPaar>(); - - for(ClassNode clnode = child; clnode != null; clnode = clnode.parent) { - - for(Entry<String, VarVersionPaar> entr : clnode.mapFieldsToVars.entrySet()) { - VarVersionPaar newvar = new VarVersionPaar(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); - - mapFieldsToNewVars.put(InterpreterUtil.makeUniqueKey(clnode.classStruct.qualifiedName, entr.getKey()), newvar); - - String varname = null; - VarType vartype = null; - - if(clnode.type != ClassNode.CLASS_MEMBER) { - - MethodWrapper enclosing_method = clnode.parent.wrapper.getMethods().getWithKey(clnode.enclosingMethod); - - varname = enclosing_method.varproc.getVarName(entr.getValue()); - vartype = enclosing_method.varproc.getVarType(entr.getValue()); - - enclosing_method.varproc.setVarFinal(entr.getValue(), VarTypeProcessor.VAR_FINALEXPLICIT); - } - - if(entr.getValue().var == -1 || "this".equals(varname)) { - if(clnode.parent.simpleName == null) { - // anonymous enclosing class, no access to this - varname = VarExprent.VAR_NAMELESS_ENCLOSURE; - } else { - varname = clnode.parent.simpleName+".this"; - } - meth.varproc.getThisvars().put(newvar, clnode.parent.classStruct.qualifiedName); - } - - mapNewNames.put(newvar, varname); - mapNewTypes.put(newvar, vartype); - - // hide synthetic field - if(clnode == child) { // fields higher up the chain were already handled with their classes - StructField fd = child.classStruct.getFields().getWithKey(entr.getKey()); - child.wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); - } - } - } - - HashSet<String> setNewOuterNames = new HashSet<String>(mapNewNames.values()); - setNewOuterNames.removeAll(meth.setOuterVarNames); - - meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); - meth.setOuterVarNames.addAll(setNewOuterNames); - - for(Entry<VarVersionPaar, String> entr : mapNewNames.entrySet()) { - VarVersionPaar varpaar = entr.getKey(); - VarType vartype = mapNewTypes.get(varpaar); - - meth.varproc.setVarName(varpaar, entr.getValue()); - if(vartype != null) { - meth.varproc.setVarType(varpaar, vartype); - } - } - - DirectGraph graph = meth.getOrBuildGraph(); - - graph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - - if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asexpr = (AssignmentExprent)exprent; - if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { - FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); - - if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && // process this class only - mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(child.classStruct.qualifiedName, - InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString)))) { - return 2; - } - - //if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && - // mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString))) { - // return 2; - //} - } - } - - if(child.type == ClassNode.CLASS_ANONYMOUS && "<init>".equals(meth.methodStruct.getName()) - && exprent.type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent invexpr = (InvocationExprent)exprent; - if(invexpr.getFunctype() == InvocationExprent.TYP_INIT) { - // invocation of the super constructor in an anonymous class - child.superInvocation = invexpr; // FIXME: save original names of parameters - return 2; - } - } - - replaceExprent(exprent); - - return 0; - } - - private Exprent replaceExprent(Exprent exprent) { - - if(exprent.type == Exprent.EXPRENT_VAR) { - int varindex = ((VarExprent)exprent).getIndex(); - if(mapParamsToNewVars.containsKey(varindex)) { - VarVersionPaar newvar = mapParamsToNewVars.get(varindex); - meth.varproc.getExternvars().add(newvar); - return new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); - } - } else if(exprent.type == Exprent.EXPRENT_FIELD) { - FieldExprent fexpr = (FieldExprent)exprent; - - String keyField = InterpreterUtil.makeUniqueKey(fexpr.getClassname(), InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString)); - - if(mapFieldsToNewVars.containsKey(keyField)) { - //if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && - // mapFieldsToNewVars.containsKey(keyField)) { - VarVersionPaar newvar = mapFieldsToNewVars.get(keyField); - meth.varproc.getExternvars().add(newvar); - return new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); - } - } - - boolean replaced = true; - while(replaced) { - replaced = false; - - for(Exprent expr: exprent.getAllExprents()) { - Exprent retexpr = replaceExprent(expr); - if(retexpr != null) { - exprent.replaceExprent(expr, retexpr); - replaced = true; - break; - } - } - } - - return null; - } - }); - - } - } - - } - - private HashMap<String, List<VarFieldPair>> getMaskLocalVars(ClassWrapper wrapper) { - - HashMap<String, List<VarFieldPair>> mapMasks = new HashMap<String, List<VarFieldPair>>(); - - StructClass cl = wrapper.getClassStruct(); - - // iterate over constructors - for(StructMethod mt: cl.getMethods()) { - if("<init>".equals(mt.getName())) { - - MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); - - MethodWrapper meth = wrapper.getMethodWrapper("<init>", mt.getDescriptor()); - DirectGraph graph = meth.getOrBuildGraph(); - - if(graph != null) { // something gone wrong, should not be null - List<VarFieldPair> fields = new ArrayList<VarFieldPair>(); - - int varindex = 1; - for(int i=0;i<md.params.length;i++) { // no static methods allowed - String keyField = getEnclosingVarField(cl, meth, graph, varindex); - fields.add(keyField==null?null:new VarFieldPair(keyField, new VarVersionPaar(-1, 0))); // TODO: null? - varindex+=md.params[i].stack_size; - } - mapMasks.put(mt.getDescriptor(), fields); - } - } - } - - return mapMasks; - } - - private String getEnclosingVarField(StructClass cl, MethodWrapper meth, DirectGraph graph, final int index) { - - String field = ""; - - // parameter variable final - if(meth.varproc.getVarFinal(new VarVersionPaar(index, 0)) == VarTypeProcessor.VAR_NONFINAL) { - return null; - } - - boolean notsynth = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); - - // no loop at the begin - DirectNode firstnode = graph.first; - if(firstnode.preds.isEmpty()) { - // assignment to a final synthetic field? - for(Exprent exprent: firstnode.exprents) { - if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asexpr = (AssignmentExprent)exprent; - if(asexpr.getRight().type == Exprent.EXPRENT_VAR && ((VarExprent)asexpr.getRight()).getIndex() == index) { - if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { - - FieldExprent left = (FieldExprent)asexpr.getLeft(); - StructField fd = cl.getField(left.getName(), left.getDescriptor().descriptorString); - - if(fd != null) { // local (== not inherited) field - if(cl.qualifiedName.equals(left.getClassname()) && - (fd.access_flags & CodeConstants.ACC_FINAL) != 0 && - ((fd.access_flags & CodeConstants.ACC_SYNTHETIC) != 0 || - fd.getAttributes().containsKey("Synthetic") || - (notsynth && (fd.access_flags & CodeConstants.ACC_PRIVATE) != 0))) { - field = InterpreterUtil.makeUniqueKey(left.getName(), left.getDescriptor().descriptorString); - break; - } - } - } - } - } - } - } - - return field; - } - - private void mergeListSignatures(List<VarFieldPair> first, List<VarFieldPair> second, boolean both) { - - int i=1; - for(;;) { - if(first.size() <= i || second.size() <= i) { - break; - } - - VarFieldPair fobj = first.get(first.size() - i); - VarFieldPair sobj = second.get(second.size() - i); - - boolean eq = false; - if(fobj == null || sobj == null) { - eq = (fobj == sobj); - } else { - eq = true; - if(fobj.keyfield.length()==0) { - fobj.keyfield = sobj.keyfield; - } else if(sobj.keyfield.length() == 0) { - if(both) { - sobj.keyfield = fobj.keyfield; - } - } else { - eq = fobj.keyfield.equals(sobj.keyfield); - } - } - - if(!eq) { - first.set(first.size() - i, null); - if(both) { - second.set(second.size() - i, null); - } - } else { - if(fobj != null) { - if(fobj.varpaar.var == -1) { - fobj.varpaar = sobj.varpaar; - } else { - sobj.varpaar = fobj.varpaar; - } - } - } - i++; - } - - for(int j=1;j<=first.size()-i;j++) { - first.set(j, null); - } - - if(both) { - for(int j=1;j<=second.size()-i;j++) { - second.set(j, null); - } - } - - // first - if(first.isEmpty()) { - if(!second.isEmpty() && both) { - second.set(0, null); - } - } else if(second.isEmpty()) { - first.set(0, null); - } else { - VarFieldPair fobj = first.get(0); - VarFieldPair sobj = second.get(0); - - boolean eq = false; - if(fobj == null || sobj == null) { - eq = (fobj == sobj); - } else { - eq = true; - if(fobj.keyfield.length()==0) { - fobj.keyfield = sobj.keyfield; - } else if(sobj.keyfield.length() == 0) { - if(both) { - sobj.keyfield = fobj.keyfield; - } - } else { - eq = fobj.keyfield.equals(sobj.keyfield); - } - } - - if(!eq) { - first.set(0, null); - if(both) { - second.set(0, null); - } - } else if(fobj != null) { - if(fobj.varpaar.var == -1) { - fobj.varpaar = sobj.varpaar; - } else { - sobj.varpaar = fobj.varpaar; - } - } - } - - } - - - private void setLocalClassDefinition(MethodWrapper meth, ClassNode node) { - - RootStatement root = meth.root; - - HashSet<Statement> setStats = new HashSet<Statement>(); - VarType classtype = new VarType(node.classStruct.qualifiedName, true); - - Statement stdef = getDefStatement(root, classtype, setStats); - if(stdef == null) { - // unreferenced local class - stdef = root.getFirst(); - } - - Statement first = findFirstBlock(stdef, setStats); - - List<Exprent> lst; - if(first == null) { - lst = stdef.getVarDefinitions(); - } else if(first.getExprents() == null) { - lst = first.getVarDefinitions(); - } else { - lst = first.getExprents(); - } - - - int addindex = 0; - for(Exprent expr: lst) { - if(searchForClass(expr, classtype)) { - break; - } - addindex++; - } - - VarExprent var = new VarExprent(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), - classtype, meth.varproc); - var.setDefinition(true); - var.setClassdef(true); - - lst.add(addindex, var); - - } - - - - private Statement findFirstBlock(Statement stat, HashSet<Statement> setStats) { - - LinkedList<Statement> stack = new LinkedList<Statement>(); - stack.add(stat); - - while(!stack.isEmpty()) { - Statement st = stack.remove(0); - - if(stack.isEmpty() || setStats.contains(st)) { - - if(st.isLabeled() && !stack.isEmpty()) { - return st; - } - - if(st.getExprents() != null) { - return st; - } else { - stack.clear(); - - switch(st.type) { - case Statement.TYPE_SEQUENCE: - stack.addAll(0, st.getStats()); - break; - case Statement.TYPE_IF: - case Statement.TYPE_ROOT: - case Statement.TYPE_SWITCH: - case Statement.TYPE_SYNCRONIZED: - stack.add(st.getFirst()); - break; - default: - return st; - } - } - } - } - - return null; - } - - - private Statement getDefStatement(Statement stat, VarType classtype, HashSet<Statement> setStats) { - - List<Exprent> condlst = new ArrayList<Exprent>(); - Statement retstat = null; - - if(stat.getExprents() == null) { - int counter = 0; - - for(Object obj: stat.getSequentialObjects()) { - if(obj instanceof Statement) { - Statement st = (Statement)obj; - - Statement stTemp = getDefStatement(st, classtype, setStats); - - if(stTemp != null) { - if(counter == 1) { - retstat = stat; - break; - } - retstat = stTemp; - counter++; - } - - if(st.type == DoStatement.TYPE_DO) { - DoStatement dost = (DoStatement)st; - - condlst.addAll(dost.getInitExprentList()); - condlst.addAll(dost.getConditionExprentList()); - } - - } else if(obj instanceof Exprent) { - condlst.add((Exprent)obj); - } - } - } else { - condlst = stat.getExprents(); - } - - if(retstat != stat) { - for(Exprent exprent : condlst) { - if(exprent!=null && searchForClass(exprent, classtype)) { - retstat = stat; - break; - } - } - } - - if(retstat != null) { - setStats.add(stat); - } - - return retstat; - } - - private boolean searchForClass(Exprent exprent, VarType classtype) { - - List<Exprent> lst = exprent.getAllExprents(true); - lst.add(exprent); - - String classname = classtype.value; - - for(Exprent expr : lst) { - - boolean res = false; - - switch(expr.type) { - case Exprent.EXPRENT_CONST: - ConstExprent cexpr = (ConstExprent)expr; - res = (VarType.VARTYPE_CLASS.equals(cexpr.getConsttype()) && classname.equals(cexpr.getValue()) || - classtype.equals(cexpr.getConsttype())); - break; - case Exprent.EXPRENT_FIELD: - res = classname.equals(((FieldExprent)expr).getClassname()); - break; - case Exprent.EXPRENT_INVOCATION: - res = classname.equals(((InvocationExprent)expr).getClassname()); - break; - case Exprent.EXPRENT_NEW: - VarType newType = expr.getExprType(); - res = newType.type == CodeConstants.TYPE_OBJECT && classname.equals(newType.value); - break; - case Exprent.EXPRENT_VAR: - VarExprent vexpr = (VarExprent)expr; - if(vexpr.isDefinition()) { - VarType vtype = vexpr.getVartype(); - if(classtype.equals(vtype) || (vtype.arraydim > 0 && classtype.value.equals(vtype.value))) { - res = true; - } - } - } - - if(res) { - return true; - } - } - - return false; - } - - - private class VarFieldPair { - - public String keyfield = ""; - public VarVersionPaar varpaar; - - public VarFieldPair(String field, VarVersionPaar varpaar) { - this.keyfield = field; - this.varpaar = varpaar; - } - - @Override - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof VarFieldPair)) return false; - - VarFieldPair pair = (VarFieldPair)o; - return keyfield.equals(pair.keyfield) && varpaar.equals(pair.varpaar); - } - - @Override - public int hashCode() { - return keyfield.hashCode()+varpaar.hashCode(); - } - - } - + + + public void processClass(ClassNode root, ClassNode node) { + + // hide synthetic lambda content methods + if (node.type == ClassNode.CLASS_LAMBDA && !node.lambda_information.is_method_reference) { + ClassNode node_content = DecompilerContext.getClassprocessor().getMapRootClasses().get(node.classStruct.qualifiedName); + if (node_content != null && node_content.wrapper != null) { + node_content.wrapper.getHideMembers().add(node.lambda_information.content_method_key); + } + } + + if (node.nested.isEmpty()) { + return; + } + + if (node.type != ClassNode.CLASS_LAMBDA) { + + computeLocalVarsAndDefinitions(node); + + // for each local or anonymous class ensure not empty enclosing method + checkNotFoundClasses(root, node); + } + + int nameless = 0, synthetics = 0; + for (ClassNode child : node.nested) { + // ensure not-empty class name + if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_MEMBER) && child.simpleName == null) { + StructClass cl = child.classStruct; + if (((child.access | cl.access_flags) & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic")) { + child.simpleName = "SyntheticClass_" + (++synthetics); + } + else { + DecompilerContext.getLogger().writeMessage("Nameless local or member class " + cl.qualifiedName + "!", IFernflowerLogger.WARNING); + child.simpleName = "NamelessClass_" + (++nameless); + } + } + } + + for (ClassNode child : node.nested) { + if (child.type == ClassNode.CLASS_LAMBDA) { + setLambdaVars(node, child); + } + else { + if (child.type != ClassNode.CLASS_MEMBER || (child.access & CodeConstants.ACC_STATIC) == 0) { + insertLocalVars(node, child); + + if (child.type == ClassNode.CLASS_LOCAL) { + setLocalClassDefinition(node.wrapper.getMethods().getWithKey(child.enclosingMethod), child); + } + } + } + } + + for (ClassNode child : node.nested) { + processClass(root, child); + } + } + + private void setLambdaVars(ClassNode parent, ClassNode child) { + + if (child.lambda_information.is_method_reference) { // method reference, no code and no parameters + return; + } + + final MethodWrapper meth = parent.wrapper.getMethods().getWithKey(child.lambda_information.content_method_key); + final MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); + + MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(child.lambda_information.method_descriptor); + final MethodDescriptor md_content = MethodDescriptor.parseDescriptor(child.lambda_information.content_method_descriptor); + + final int vars_count = md_content.params.length - md_lambda.params.length; + // if(vars_count < 0) { // should not happen, but just in case... + // vars_count = 0; + // } + + final boolean is_static_lambda_content = child.lambda_information.is_content_method_static; + + final String parent_class_name = parent.wrapper.getClassStruct().qualifiedName; + final String lambda_class_name = child.simpleName; + + final VarType lambda_class_type = new VarType(lambda_class_name, true); + + // this pointer + if (!is_static_lambda_content && DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) { + meth.varproc.getThisvars().put(new VarVersionPaar(0, 0), parent_class_name); + meth.varproc.setVarName(new VarVersionPaar(0, 0), parent.simpleName + ".this"); + } + + // local variables + DirectGraph graph = encmeth.getOrBuildGraph(); + + final HashMap<VarVersionPaar, String> mapNewNames = new HashMap<VarVersionPaar, String>(); + + graph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + + if (expr.type == Exprent.EXPRENT_NEW) { + NewExprent new_expr = (NewExprent)expr; + if (new_expr.isLambda() && lambda_class_type.equals(new_expr.getNewtype())) { + + InvocationExprent inv_dynamic = new_expr.getConstructor(); + + int param_index = is_static_lambda_content ? 0 : 1; + ; + int varindex = is_static_lambda_content ? 0 : 1; + + for (int i = 0; i < vars_count; ++i) { + + Exprent param = inv_dynamic.getLstParameters().get(param_index + i); + + if (param.type == Exprent.EXPRENT_VAR) { + VarVersionPaar enc_varpaar = new VarVersionPaar((VarExprent)param); + String enc_varname = encmeth.varproc.getVarName(enc_varpaar); + + //meth.varproc.setVarName(new VarVersionPaar(varindex, 0), enc_varname); + mapNewNames.put(new VarVersionPaar(varindex, 0), enc_varname); + } + + varindex += md_content.params[i].stack_size; + } + } + } + } + + return 0; + } + }); + + // update names of local variables + HashSet<String> setNewOuterNames = new HashSet<String>(mapNewNames.values()); + setNewOuterNames.removeAll(meth.setOuterVarNames); + + meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); + meth.setOuterVarNames.addAll(setNewOuterNames); + + for (Entry<VarVersionPaar, String> entr : mapNewNames.entrySet()) { + meth.varproc.setVarName(entr.getKey(), entr.getValue()); + } + } + + private void checkNotFoundClasses(ClassNode root, ClassNode node) { + + List<ClassNode> lstChildren = new ArrayList<ClassNode>(node.nested); + + for (ClassNode child : lstChildren) { + + if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_ANONYMOUS) && child.enclosingMethod == null) { + + Set<String> setEnclosing = child.enclosingClasses; + + if (setEnclosing.size() == 1) { + StructEnclosingMethodAttribute attr = + (StructEnclosingMethodAttribute)child.classStruct.getAttributes().getWithKey("EnclosingMethod"); + if (attr != null && attr.getMethodName() != null) { + if (node.classStruct.qualifiedName.equals(attr.getClassname()) && + node.classStruct.getMethod(attr.getMethodName(), attr.getMethodDescriptor()) != null) { + child.enclosingMethod = InterpreterUtil.makeUniqueKey(attr.getMethodName(), attr.getMethodDescriptor()); + continue; + } + } + } + + node.nested.remove(child); + child.parent = null; + setEnclosing.remove(node.classStruct.qualifiedName); + + boolean hasEnclosing = !setEnclosing.isEmpty(); + if (hasEnclosing) { + hasEnclosing = insertNestedClass(root, child); + } + + if (!hasEnclosing) { + if (child.type == ClassNode.CLASS_ANONYMOUS) { + DecompilerContext.getLogger() + .writeMessage("Unreferenced anonymous class " + child.classStruct.qualifiedName + "!", IFernflowerLogger.WARNING); + } + else if (child.type == ClassNode.CLASS_LOCAL) { + DecompilerContext.getLogger() + .writeMessage("Unreferenced local class " + child.classStruct.qualifiedName + "!", IFernflowerLogger.WARNING); + } + } + } + } + } + + private boolean insertNestedClass(ClassNode root, ClassNode child) { + + Set<String> setEnclosing = child.enclosingClasses; + + LinkedList<ClassNode> stack = new LinkedList<ClassNode>(); + stack.add(root); + + while (!stack.isEmpty()) { + + ClassNode node = stack.removeFirst(); + + if (setEnclosing.contains(node.classStruct.qualifiedName)) { + node.nested.add(child); + child.parent = node; + + return true; + } + + // note: ordered list + stack.addAll(node.nested); + } + + return false; + } + + + private void computeLocalVarsAndDefinitions(final ClassNode node) { + + // local var masks + // class name, constructor descriptor, field mask + final HashMap<String, HashMap<String, List<VarFieldPair>>> mapVarMasks = new HashMap<String, HashMap<String, List<VarFieldPair>>>(); + + int cltypes = 0; + + for (ClassNode nd : node.nested) { + if (nd.type != ClassNode.CLASS_LAMBDA) { + if ((nd.access & CodeConstants.ACC_STATIC) == 0 && (nd.access & CodeConstants.ACC_INTERFACE) == 0) { + + cltypes |= nd.type; + + HashMap<String, List<VarFieldPair>> mask = getMaskLocalVars(nd.wrapper); + if (mask.isEmpty()) { + DecompilerContext.getLogger() + .writeMessage("Nested class " + nd.classStruct.qualifiedName + " has no constructor!", IFernflowerLogger.WARNING); + } + else { + mapVarMasks.put(nd.classStruct.qualifiedName, mask); + } + } + } + } + + // local var masks + final HashMap<String, HashMap<String, List<VarFieldPair>>> mapVarFieldPairs = + new HashMap<String, HashMap<String, List<VarFieldPair>>>(); + + if (cltypes != ClassNode.CLASS_MEMBER) { + + // iterate enclosing class + for (final MethodWrapper meth : node.wrapper.getMethods()) { + + if (meth.root != null) { // neither abstract, nor native + DirectGraph graph = meth.getOrBuildGraph(); + + graph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + + if (expr.type == Exprent.EXPRENT_NEW) { + InvocationExprent constr = ((NewExprent)expr).getConstructor(); + + if (constr != null && mapVarMasks.containsKey(constr.getClassname())) { // non-static inner class constructor + + String refclname = constr.getClassname(); + + ClassNode nestedClassNode = node.getClassNode(refclname); + + if (nestedClassNode.type != ClassNode.CLASS_MEMBER) { + + List<VarFieldPair> mask = mapVarMasks.get(refclname).get(constr.getStringDescriptor()); + + if (!mapVarFieldPairs.containsKey(refclname)) { + mapVarFieldPairs.put(refclname, new HashMap<String, List<VarFieldPair>>()); + } + + List<VarFieldPair> lstTemp = new ArrayList<VarFieldPair>(); + + for (int i = 0; i < mask.size(); i++) { + Exprent param = constr.getLstParameters().get(i); + VarFieldPair pair = null; + + if (param.type == Exprent.EXPRENT_VAR && mask.get(i) != null) { + VarVersionPaar varpaar = new VarVersionPaar((VarExprent)param); + + // FIXME: final flags of variables are wrong! Correct the entire final functionality. + // if(meth.varproc.getVarFinal(varpaar) != VarTypeProcessor.VAR_NONFINAL) { + pair = new VarFieldPair(mask.get(i).keyfield, varpaar); + // } + } + + lstTemp.add(pair); + } + + List<VarFieldPair> pairmask = mapVarFieldPairs.get(refclname).get(constr.getStringDescriptor()); + + if (pairmask == null) { + pairmask = lstTemp; + } + else { + for (int i = 0; i < pairmask.size(); i++) { + if (!InterpreterUtil.equalObjects(pairmask.get(i), lstTemp.get(i))) { + pairmask.set(i, null); + } + } + } + + mapVarFieldPairs.get(refclname).put(constr.getStringDescriptor(), pairmask); + nestedClassNode.enclosingMethod = + InterpreterUtil.makeUniqueKey(meth.methodStruct.getName(), meth.methodStruct.getDescriptor()); + } + } + } + } + return 0; + } + }); + } + } + } + + // merge var masks + for (Entry<String, HashMap<String, List<VarFieldPair>>> entcl : mapVarMasks.entrySet()) { + + ClassNode nestedNode = node.getClassNode(entcl.getKey()); + + // intersection + List<VarFieldPair> intrPairMask = null; + // merge referenced constructors + if (mapVarFieldPairs.containsKey(entcl.getKey())) { + for (List<VarFieldPair> mask : mapVarFieldPairs.get(entcl.getKey()).values()) { + if (intrPairMask == null) { + intrPairMask = new ArrayList<VarFieldPair>(mask); + } + else { + mergeListSignatures(intrPairMask, mask, false); + } + } + } + + List<VarFieldPair> intrMask = null; + // merge all constructors + for (List<VarFieldPair> mask : entcl.getValue().values()) { + if (intrMask == null) { + intrMask = new ArrayList<VarFieldPair>(mask); + } + else { + mergeListSignatures(intrMask, mask, false); + } + } + + if (intrPairMask == null) { // member or local and never instantiated + intrPairMask = new ArrayList<VarFieldPair>(intrMask); + + boolean found = false; + + for (int i = 0; i < intrPairMask.size(); i++) { + if (intrPairMask.get(i) != null) { + if (found) { + intrPairMask.set(i, null); + } + found = true; + } + } + } + + mergeListSignatures(intrPairMask, intrMask, true); + + for (int i = 0; i < intrPairMask.size(); i++) { + VarFieldPair pair = intrPairMask.get(i); + if (pair != null && pair.keyfield.length() > 0) { + nestedNode.mapFieldsToVars.put(pair.keyfield, pair.varpaar); + } + } + + // set resulting constructor signatures + for (Entry<String, List<VarFieldPair>> entmt : entcl.getValue().entrySet()) { + mergeListSignatures(entmt.getValue(), intrPairMask, false); + + MethodWrapper meth = nestedNode.wrapper.getMethodWrapper("<init>", entmt.getKey()); + meth.signatureFields = new ArrayList<VarVersionPaar>(); + + for (VarFieldPair pair : entmt.getValue()) { + meth.signatureFields.add(pair == null ? null : pair.varpaar); + } + } + } + } + + private void insertLocalVars(final ClassNode parent, final ClassNode child) { + + // enclosing method, is null iff member class + MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); + + // iterate all child methods + for (final MethodWrapper meth : child.wrapper.getMethods()) { + + if (meth.root != null) { // neither abstract nor native + + // local var names + HashMap<VarVersionPaar, String> mapNewNames = new HashMap<VarVersionPaar, String>(); + // local var types + HashMap<VarVersionPaar, VarType> mapNewTypes = new HashMap<VarVersionPaar, VarType>(); + + final HashMap<Integer, VarVersionPaar> mapParamsToNewVars = new HashMap<Integer, VarVersionPaar>(); + if (meth.signatureFields != null) { + int index = 0; + int varindex = 1; + MethodDescriptor md = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor()); + + for (VarVersionPaar paar : meth.signatureFields) { + if (paar != null) { + VarVersionPaar newvar = new VarVersionPaar(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); + + mapParamsToNewVars.put(varindex, newvar); + + String varname = null; + VarType vartype = null; + + if (child.type != ClassNode.CLASS_MEMBER) { + varname = encmeth.varproc.getVarName(paar); + vartype = encmeth.varproc.getVarType(paar); + + encmeth.varproc.setVarFinal(paar, VarTypeProcessor.VAR_FINALEXPLICIT); + } + + if (paar.var == -1 || "this".equals(varname)) { + if (parent.simpleName == null) { + // anonymous enclosing class, no access to this + varname = VarExprent.VAR_NAMELESS_ENCLOSURE; + } + else { + varname = parent.simpleName + ".this"; + } + meth.varproc.getThisvars().put(newvar, parent.classStruct.qualifiedName); + } + + mapNewNames.put(newvar, varname); + mapNewTypes.put(newvar, vartype); + } + varindex += md.params[index++].stack_size; + } + } + + // new vars + final HashMap<String, VarVersionPaar> mapFieldsToNewVars = new HashMap<String, VarVersionPaar>(); + + for (ClassNode clnode = child; clnode != null; clnode = clnode.parent) { + + for (Entry<String, VarVersionPaar> entr : clnode.mapFieldsToVars.entrySet()) { + VarVersionPaar newvar = new VarVersionPaar(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); + + mapFieldsToNewVars.put(InterpreterUtil.makeUniqueKey(clnode.classStruct.qualifiedName, entr.getKey()), newvar); + + String varname = null; + VarType vartype = null; + + if (clnode.type != ClassNode.CLASS_MEMBER) { + + MethodWrapper enclosing_method = clnode.parent.wrapper.getMethods().getWithKey(clnode.enclosingMethod); + + varname = enclosing_method.varproc.getVarName(entr.getValue()); + vartype = enclosing_method.varproc.getVarType(entr.getValue()); + + enclosing_method.varproc.setVarFinal(entr.getValue(), VarTypeProcessor.VAR_FINALEXPLICIT); + } + + if (entr.getValue().var == -1 || "this".equals(varname)) { + if (clnode.parent.simpleName == null) { + // anonymous enclosing class, no access to this + varname = VarExprent.VAR_NAMELESS_ENCLOSURE; + } + else { + varname = clnode.parent.simpleName + ".this"; + } + meth.varproc.getThisvars().put(newvar, clnode.parent.classStruct.qualifiedName); + } + + mapNewNames.put(newvar, varname); + mapNewTypes.put(newvar, vartype); + + // hide synthetic field + if (clnode == child) { // fields higher up the chain were already handled with their classes + StructField fd = child.classStruct.getFields().getWithKey(entr.getKey()); + child.wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); + } + } + } + + HashSet<String> setNewOuterNames = new HashSet<String>(mapNewNames.values()); + setNewOuterNames.removeAll(meth.setOuterVarNames); + + meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); + meth.setOuterVarNames.addAll(setNewOuterNames); + + for (Entry<VarVersionPaar, String> entr : mapNewNames.entrySet()) { + VarVersionPaar varpaar = entr.getKey(); + VarType vartype = mapNewTypes.get(varpaar); + + meth.varproc.setVarName(varpaar, entr.getValue()); + if (vartype != null) { + meth.varproc.setVarType(varpaar, vartype); + } + } + + DirectGraph graph = meth.getOrBuildGraph(); + + graph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asexpr = (AssignmentExprent)exprent; + if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { + FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); + + if (fexpr.getClassname().equals(child.classStruct.qualifiedName) && // process this class only + mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(child.classStruct.qualifiedName, + InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr + .getDescriptor().descriptorString)))) { + return 2; + } + + //if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && + // mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString))) { + // return 2; + //} + } + } + + if (child.type == ClassNode.CLASS_ANONYMOUS && "<init>".equals(meth.methodStruct.getName()) + && exprent.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent invexpr = (InvocationExprent)exprent; + if (invexpr.getFunctype() == InvocationExprent.TYP_INIT) { + // invocation of the super constructor in an anonymous class + child.superInvocation = invexpr; // FIXME: save original names of parameters + return 2; + } + } + + replaceExprent(exprent); + + return 0; + } + + private Exprent replaceExprent(Exprent exprent) { + + if (exprent.type == Exprent.EXPRENT_VAR) { + int varindex = ((VarExprent)exprent).getIndex(); + if (mapParamsToNewVars.containsKey(varindex)) { + VarVersionPaar newvar = mapParamsToNewVars.get(varindex); + meth.varproc.getExternvars().add(newvar); + return new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); + } + } + else if (exprent.type == Exprent.EXPRENT_FIELD) { + FieldExprent fexpr = (FieldExprent)exprent; + + String keyField = InterpreterUtil.makeUniqueKey(fexpr.getClassname(), InterpreterUtil + .makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString)); + + if (mapFieldsToNewVars.containsKey(keyField)) { + //if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && + // mapFieldsToNewVars.containsKey(keyField)) { + VarVersionPaar newvar = mapFieldsToNewVars.get(keyField); + meth.varproc.getExternvars().add(newvar); + return new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); + } + } + + boolean replaced = true; + while (replaced) { + replaced = false; + + for (Exprent expr : exprent.getAllExprents()) { + Exprent retexpr = replaceExprent(expr); + if (retexpr != null) { + exprent.replaceExprent(expr, retexpr); + replaced = true; + break; + } + } + } + + return null; + } + }); + } + } + } + + private HashMap<String, List<VarFieldPair>> getMaskLocalVars(ClassWrapper wrapper) { + + HashMap<String, List<VarFieldPair>> mapMasks = new HashMap<String, List<VarFieldPair>>(); + + StructClass cl = wrapper.getClassStruct(); + + // iterate over constructors + for (StructMethod mt : cl.getMethods()) { + if ("<init>".equals(mt.getName())) { + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + MethodWrapper meth = wrapper.getMethodWrapper("<init>", mt.getDescriptor()); + DirectGraph graph = meth.getOrBuildGraph(); + + if (graph != null) { // something gone wrong, should not be null + List<VarFieldPair> fields = new ArrayList<VarFieldPair>(); + + int varindex = 1; + for (int i = 0; i < md.params.length; i++) { // no static methods allowed + String keyField = getEnclosingVarField(cl, meth, graph, varindex); + fields.add(keyField == null ? null : new VarFieldPair(keyField, new VarVersionPaar(-1, 0))); // TODO: null? + varindex += md.params[i].stack_size; + } + mapMasks.put(mt.getDescriptor(), fields); + } + } + } + + return mapMasks; + } + + private String getEnclosingVarField(StructClass cl, MethodWrapper meth, DirectGraph graph, final int index) { + + String field = ""; + + // parameter variable final + if (meth.varproc.getVarFinal(new VarVersionPaar(index, 0)) == VarTypeProcessor.VAR_NONFINAL) { + return null; + } + + boolean notsynth = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); + + // no loop at the begin + DirectNode firstnode = graph.first; + if (firstnode.preds.isEmpty()) { + // assignment to a final synthetic field? + for (Exprent exprent : firstnode.exprents) { + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asexpr = (AssignmentExprent)exprent; + if (asexpr.getRight().type == Exprent.EXPRENT_VAR && ((VarExprent)asexpr.getRight()).getIndex() == index) { + if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { + + FieldExprent left = (FieldExprent)asexpr.getLeft(); + StructField fd = cl.getField(left.getName(), left.getDescriptor().descriptorString); + + if (fd != null) { // local (== not inherited) field + if (cl.qualifiedName.equals(left.getClassname()) && + (fd.access_flags & CodeConstants.ACC_FINAL) != 0 && + ((fd.access_flags & CodeConstants.ACC_SYNTHETIC) != 0 || + fd.getAttributes().containsKey("Synthetic") || + (notsynth && (fd.access_flags & CodeConstants.ACC_PRIVATE) != 0))) { + field = InterpreterUtil.makeUniqueKey(left.getName(), left.getDescriptor().descriptorString); + break; + } + } + } + } + } + } + } + + return field; + } + + private void mergeListSignatures(List<VarFieldPair> first, List<VarFieldPair> second, boolean both) { + + int i = 1; + for (; ; ) { + if (first.size() <= i || second.size() <= i) { + break; + } + + VarFieldPair fobj = first.get(first.size() - i); + VarFieldPair sobj = second.get(second.size() - i); + + boolean eq = false; + if (fobj == null || sobj == null) { + eq = (fobj == sobj); + } + else { + eq = true; + if (fobj.keyfield.length() == 0) { + fobj.keyfield = sobj.keyfield; + } + else if (sobj.keyfield.length() == 0) { + if (both) { + sobj.keyfield = fobj.keyfield; + } + } + else { + eq = fobj.keyfield.equals(sobj.keyfield); + } + } + + if (!eq) { + first.set(first.size() - i, null); + if (both) { + second.set(second.size() - i, null); + } + } + else { + if (fobj != null) { + if (fobj.varpaar.var == -1) { + fobj.varpaar = sobj.varpaar; + } + else { + sobj.varpaar = fobj.varpaar; + } + } + } + i++; + } + + for (int j = 1; j <= first.size() - i; j++) { + first.set(j, null); + } + + if (both) { + for (int j = 1; j <= second.size() - i; j++) { + second.set(j, null); + } + } + + // first + if (first.isEmpty()) { + if (!second.isEmpty() && both) { + second.set(0, null); + } + } + else if (second.isEmpty()) { + first.set(0, null); + } + else { + VarFieldPair fobj = first.get(0); + VarFieldPair sobj = second.get(0); + + boolean eq = false; + if (fobj == null || sobj == null) { + eq = (fobj == sobj); + } + else { + eq = true; + if (fobj.keyfield.length() == 0) { + fobj.keyfield = sobj.keyfield; + } + else if (sobj.keyfield.length() == 0) { + if (both) { + sobj.keyfield = fobj.keyfield; + } + } + else { + eq = fobj.keyfield.equals(sobj.keyfield); + } + } + + if (!eq) { + first.set(0, null); + if (both) { + second.set(0, null); + } + } + else if (fobj != null) { + if (fobj.varpaar.var == -1) { + fobj.varpaar = sobj.varpaar; + } + else { + sobj.varpaar = fobj.varpaar; + } + } + } + } + + + private void setLocalClassDefinition(MethodWrapper meth, ClassNode node) { + + RootStatement root = meth.root; + + HashSet<Statement> setStats = new HashSet<Statement>(); + VarType classtype = new VarType(node.classStruct.qualifiedName, true); + + Statement stdef = getDefStatement(root, classtype, setStats); + if (stdef == null) { + // unreferenced local class + stdef = root.getFirst(); + } + + Statement first = findFirstBlock(stdef, setStats); + + List<Exprent> lst; + if (first == null) { + lst = stdef.getVarDefinitions(); + } + else if (first.getExprents() == null) { + lst = first.getVarDefinitions(); + } + else { + lst = first.getExprents(); + } + + + int addindex = 0; + for (Exprent expr : lst) { + if (searchForClass(expr, classtype)) { + break; + } + addindex++; + } + + VarExprent var = new VarExprent(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), + classtype, meth.varproc); + var.setDefinition(true); + var.setClassdef(true); + + lst.add(addindex, var); + } + + + private Statement findFirstBlock(Statement stat, HashSet<Statement> setStats) { + + LinkedList<Statement> stack = new LinkedList<Statement>(); + stack.add(stat); + + while (!stack.isEmpty()) { + Statement st = stack.remove(0); + + if (stack.isEmpty() || setStats.contains(st)) { + + if (st.isLabeled() && !stack.isEmpty()) { + return st; + } + + if (st.getExprents() != null) { + return st; + } + else { + stack.clear(); + + switch (st.type) { + case Statement.TYPE_SEQUENCE: + stack.addAll(0, st.getStats()); + break; + case Statement.TYPE_IF: + case Statement.TYPE_ROOT: + case Statement.TYPE_SWITCH: + case Statement.TYPE_SYNCRONIZED: + stack.add(st.getFirst()); + break; + default: + return st; + } + } + } + } + + return null; + } + + + private Statement getDefStatement(Statement stat, VarType classtype, HashSet<Statement> setStats) { + + List<Exprent> condlst = new ArrayList<Exprent>(); + Statement retstat = null; + + if (stat.getExprents() == null) { + int counter = 0; + + for (Object obj : stat.getSequentialObjects()) { + if (obj instanceof Statement) { + Statement st = (Statement)obj; + + Statement stTemp = getDefStatement(st, classtype, setStats); + + if (stTemp != null) { + if (counter == 1) { + retstat = stat; + break; + } + retstat = stTemp; + counter++; + } + + if (st.type == DoStatement.TYPE_DO) { + DoStatement dost = (DoStatement)st; + + condlst.addAll(dost.getInitExprentList()); + condlst.addAll(dost.getConditionExprentList()); + } + } + else if (obj instanceof Exprent) { + condlst.add((Exprent)obj); + } + } + } + else { + condlst = stat.getExprents(); + } + + if (retstat != stat) { + for (Exprent exprent : condlst) { + if (exprent != null && searchForClass(exprent, classtype)) { + retstat = stat; + break; + } + } + } + + if (retstat != null) { + setStats.add(stat); + } + + return retstat; + } + + private boolean searchForClass(Exprent exprent, VarType classtype) { + + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + String classname = classtype.value; + + for (Exprent expr : lst) { + + boolean res = false; + + switch (expr.type) { + case Exprent.EXPRENT_CONST: + ConstExprent cexpr = (ConstExprent)expr; + res = (VarType.VARTYPE_CLASS.equals(cexpr.getConsttype()) && classname.equals(cexpr.getValue()) || + classtype.equals(cexpr.getConsttype())); + break; + case Exprent.EXPRENT_FIELD: + res = classname.equals(((FieldExprent)expr).getClassname()); + break; + case Exprent.EXPRENT_INVOCATION: + res = classname.equals(((InvocationExprent)expr).getClassname()); + break; + case Exprent.EXPRENT_NEW: + VarType newType = expr.getExprType(); + res = newType.type == CodeConstants.TYPE_OBJECT && classname.equals(newType.value); + break; + case Exprent.EXPRENT_VAR: + VarExprent vexpr = (VarExprent)expr; + if (vexpr.isDefinition()) { + VarType vtype = vexpr.getVartype(); + if (classtype.equals(vtype) || (vtype.arraydim > 0 && classtype.value.equals(vtype.value))) { + res = true; + } + } + } + + if (res) { + return true; + } + } + + return false; + } + + + private class VarFieldPair { + + public String keyfield = ""; + public VarVersionPaar varpaar; + + public VarFieldPair(String field, VarVersionPaar varpaar) { + this.keyfield = field; + this.varpaar = varpaar; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof VarFieldPair)) return false; + + VarFieldPair pair = (VarFieldPair)o; + return keyfield.equals(pair.keyfield) && varpaar.equals(pair.varpaar); + } + + @Override + public int hashCode() { + return keyfield.hashCode() + varpaar.hashCode(); + } + } } diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java b/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java index 3eda632..e81a69e 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java @@ -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; - } - - 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) { - - 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; - } - - + + 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; + } + + 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) { + + 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; + } } |