summaryrefslogtreecommitdiffstats
path: root/src/org/jetbrains/java/decompiler/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/jetbrains/java/decompiler/main')
-rw-r--r--src/org/jetbrains/java/decompiler/main/AssertProcessor.java590
-rw-r--r--src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java561
-rw-r--r--src/org/jetbrains/java/decompiler/main/ClassWriter.java2161
-rw-r--r--src/org/jetbrains/java/decompiler/main/ClassesProcessor.java834
-rw-r--r--src/org/jetbrains/java/decompiler/main/DecompilerContext.java359
-rw-r--r--src/org/jetbrains/java/decompiler/main/EnumProcessor.java261
-rw-r--r--src/org/jetbrains/java/decompiler/main/Fernflower.java171
-rw-r--r--src/org/jetbrains/java/decompiler/main/InitializerProcessor.java598
-rw-r--r--src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java50
-rw-r--r--src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java279
-rw-r--r--src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java77
-rw-r--r--src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java608
-rw-r--r--src/org/jetbrains/java/decompiler/main/decompiler/IdeDecompiler.java46
-rw-r--r--src/org/jetbrains/java/decompiler/main/decompiler/WebDecompiler.java124
-rw-r--r--src/org/jetbrains/java/decompiler/main/decompiler/helper/PrintStreamLogger.java141
-rw-r--r--src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java22
-rw-r--r--src/org/jetbrains/java/decompiler/main/extern/IDecompilatSaver.java44
-rw-r--r--src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java89
-rw-r--r--src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java91
-rw-r--r--src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java39
-rw-r--r--src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java380
-rw-r--r--src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java247
-rw-r--r--src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java488
-rw-r--r--src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java86
-rw-r--r--src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java1992
-rw-r--r--src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java869
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;
+ }
}