From 076e4393f25bf1ad1ff1bd2853153e2b595dd90b Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 28 Aug 2014 21:34:14 +0400 Subject: java-decompiler: post-import cleanup (formatting and copyright) --- .../java/decompiler/main/rels/ClassWrapper.java | 380 ++-- .../java/decompiler/main/rels/LambdaProcessor.java | 247 +-- .../main/rels/MethodProcessorThread.java | 488 +++-- .../java/decompiler/main/rels/MethodWrapper.java | 86 +- .../decompiler/main/rels/NestedClassProcessor.java | 1992 ++++++++++---------- .../decompiler/main/rels/NestedMemberAccess.java | 869 +++++---- 6 files changed, 2039 insertions(+), 2023 deletions(-) (limited to 'src/org/jetbrains/java/decompiler/main/rels') 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 hideMembers = new HashSet(); - - private VBStyleCollection staticFieldInitializers = new VBStyleCollection(); - - private VBStyleCollection dynamicFieldInitializers = new VBStyleCollection(); - - private VBStyleCollection methods = new VBStyleCollection(); - - - 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 setFieldNames = new HashSet(); - 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 getMethods() { - return methods; - } - - public HashSet getHideMembers() { - return hideMembers; - } - - public VBStyleCollection getStaticFieldInitializers() { - return staticFieldInitializers; - } - - public VBStyleCollection getDynamicFieldInitializers() { - return dynamicFieldInitializers; - } + private StructClass classStruct; + + private HashSet hideMembers = new HashSet(); + + private VBStyleCollection staticFieldInitializers = new VBStyleCollection(); + + private VBStyleCollection dynamicFieldInitializers = new VBStyleCollection(); + + private VBStyleCollection methods = new VBStyleCollection(); + + + 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 setFieldNames = new HashSet(); + 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 getMethods() { + return methods; + } + + public HashSet getHideMembers() { + return hideMembers; + } + + public VBStyleCollection getStaticFieldInitializers() { + return staticFieldInitializers; + } + + public VBStyleCollection 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 lambda_methods = new HashSet(); - - // 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 mapMethodsLambda = new HashMap(); - - // 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 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 lambda_methods = new HashSet(); + + // 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 mapMethodsLambda = new HashMap(); + + // 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 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 = "".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()); - - 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 = "".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()); + + 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 signatureFields; - - public boolean decompiledWithErrors; - - public HashSet setOuterVarNames = new HashSet(); - - 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 signatureFields; + + public boolean decompiledWithErrors; + + public HashSet setOuterVarNames = new HashSet(); + + 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 mapNewNames = new HashMap(); - - graph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - - List 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 setNewOuterNames = new HashSet(mapNewNames.values()); - setNewOuterNames.removeAll(meth.setOuterVarNames); - - meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); - meth.setOuterVarNames.addAll(setNewOuterNames); - - for(Entry entr : mapNewNames.entrySet()) { - meth.varproc.setVarName(entr.getKey(), entr.getValue()); - } - - } - - private void checkNotFoundClasses(ClassNode root, ClassNode node) { - - List lstChildren = new ArrayList(node.nested); - - for(ClassNode child : lstChildren) { - - if((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_ANONYMOUS) && child.enclosingMethod == null) { - - Set 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 setEnclosing = child.enclosingClasses; - - LinkedList stack = new LinkedList(); - 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>> mapVarMasks = new HashMap>>(); - - 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> 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>> mapVarFieldPairs = new HashMap>>(); - - 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 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 mask = mapVarMasks.get(refclname).get(constr.getStringDescriptor()); - - if(!mapVarFieldPairs.containsKey(refclname)) { - mapVarFieldPairs.put(refclname, new HashMap>()); - } - - List lstTemp = new ArrayList(); - - for(int i=0;i pairmask = mapVarFieldPairs.get(refclname).get(constr.getStringDescriptor()); - - if(pairmask == null) { - pairmask = lstTemp; - } else { - for(int i=0;i>> entcl : mapVarMasks.entrySet()) { - - ClassNode nestedNode = node.getClassNode(entcl.getKey()); - - // intersection - List intrPairMask = null; - // merge referenced constructors - if(mapVarFieldPairs.containsKey(entcl.getKey())) { - for(List mask : mapVarFieldPairs.get(entcl.getKey()).values()) { - if(intrPairMask == null) { - intrPairMask = new ArrayList(mask); - } else { - mergeListSignatures(intrPairMask, mask, false); - } - } - } - - List intrMask = null; - // merge all constructors - for(List mask : entcl.getValue().values()) { - if(intrMask == null) { - intrMask = new ArrayList(mask); - } else { - mergeListSignatures(intrMask, mask, false); - } - } - - if(intrPairMask == null) { // member or local and never instantiated - intrPairMask = new ArrayList(intrMask); - - boolean found = false; - - for(int i=0;i 0) { - nestedNode.mapFieldsToVars.put(pair.keyfield, pair.varpaar); - } - } - - // set resulting constructor signatures - for(Entry> entmt : entcl.getValue().entrySet()) { - mergeListSignatures(entmt.getValue(), intrPairMask, false); - - MethodWrapper meth = nestedNode.wrapper.getMethodWrapper("", entmt.getKey()); - meth.signatureFields = new ArrayList(); - - 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 mapNewNames = new HashMap(); - // local var types - HashMap mapNewTypes = new HashMap(); - - final HashMap mapParamsToNewVars = new HashMap(); - 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 mapFieldsToNewVars = new HashMap(); - - for(ClassNode clnode = child; clnode != null; clnode = clnode.parent) { - - for(Entry 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 setNewOuterNames = new HashSet(mapNewNames.values()); - setNewOuterNames.removeAll(meth.setOuterVarNames); - - meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); - meth.setOuterVarNames.addAll(setNewOuterNames); - - for(Entry 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 && "".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> getMaskLocalVars(ClassWrapper wrapper) { - - HashMap> mapMasks = new HashMap>(); - - StructClass cl = wrapper.getClassStruct(); - - // iterate over constructors - for(StructMethod mt: cl.getMethods()) { - if("".equals(mt.getName())) { - - MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); - - MethodWrapper meth = wrapper.getMethodWrapper("", mt.getDescriptor()); - DirectGraph graph = meth.getOrBuildGraph(); - - if(graph != null) { // something gone wrong, should not be null - List fields = new ArrayList(); - - int varindex = 1; - for(int i=0;i first, List 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 setStats = new HashSet(); - 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 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 setStats) { - - LinkedList stack = new LinkedList(); - 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 setStats) { - - List condlst = new ArrayList(); - 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 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 mapNewNames = new HashMap(); + + graph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + + List 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 setNewOuterNames = new HashSet(mapNewNames.values()); + setNewOuterNames.removeAll(meth.setOuterVarNames); + + meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); + meth.setOuterVarNames.addAll(setNewOuterNames); + + for (Entry entr : mapNewNames.entrySet()) { + meth.varproc.setVarName(entr.getKey(), entr.getValue()); + } + } + + private void checkNotFoundClasses(ClassNode root, ClassNode node) { + + List lstChildren = new ArrayList(node.nested); + + for (ClassNode child : lstChildren) { + + if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_ANONYMOUS) && child.enclosingMethod == null) { + + Set 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 setEnclosing = child.enclosingClasses; + + LinkedList stack = new LinkedList(); + 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>> mapVarMasks = new HashMap>>(); + + 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> 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>> mapVarFieldPairs = + new HashMap>>(); + + 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 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 mask = mapVarMasks.get(refclname).get(constr.getStringDescriptor()); + + if (!mapVarFieldPairs.containsKey(refclname)) { + mapVarFieldPairs.put(refclname, new HashMap>()); + } + + List lstTemp = new ArrayList(); + + 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 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>> entcl : mapVarMasks.entrySet()) { + + ClassNode nestedNode = node.getClassNode(entcl.getKey()); + + // intersection + List intrPairMask = null; + // merge referenced constructors + if (mapVarFieldPairs.containsKey(entcl.getKey())) { + for (List mask : mapVarFieldPairs.get(entcl.getKey()).values()) { + if (intrPairMask == null) { + intrPairMask = new ArrayList(mask); + } + else { + mergeListSignatures(intrPairMask, mask, false); + } + } + } + + List intrMask = null; + // merge all constructors + for (List mask : entcl.getValue().values()) { + if (intrMask == null) { + intrMask = new ArrayList(mask); + } + else { + mergeListSignatures(intrMask, mask, false); + } + } + + if (intrPairMask == null) { // member or local and never instantiated + intrPairMask = new ArrayList(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> entmt : entcl.getValue().entrySet()) { + mergeListSignatures(entmt.getValue(), intrPairMask, false); + + MethodWrapper meth = nestedNode.wrapper.getMethodWrapper("", entmt.getKey()); + meth.signatureFields = new ArrayList(); + + 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 mapNewNames = new HashMap(); + // local var types + HashMap mapNewTypes = new HashMap(); + + final HashMap mapParamsToNewVars = new HashMap(); + 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 mapFieldsToNewVars = new HashMap(); + + for (ClassNode clnode = child; clnode != null; clnode = clnode.parent) { + + for (Entry 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 setNewOuterNames = new HashSet(mapNewNames.values()); + setNewOuterNames.removeAll(meth.setOuterVarNames); + + meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); + meth.setOuterVarNames.addAll(setNewOuterNames); + + for (Entry 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 && "".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> getMaskLocalVars(ClassWrapper wrapper) { + + HashMap> mapMasks = new HashMap>(); + + StructClass cl = wrapper.getClassStruct(); + + // iterate over constructors + for (StructMethod mt : cl.getMethods()) { + if ("".equals(mt.getName())) { + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + MethodWrapper meth = wrapper.getMethodWrapper("", mt.getDescriptor()); + DirectGraph graph = meth.getOrBuildGraph(); + + if (graph != null) { // something gone wrong, should not be null + List fields = new ArrayList(); + + 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 first, List 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 setStats = new HashSet(); + 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 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 setStats) { + + LinkedList stack = new LinkedList(); + 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 setStats) { + + List condlst = new ArrayList(); + 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 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 mapMethodType = new HashMap(); - - - 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 setVisited = new HashSet(); - LinkedList stack = new LinkedList(); - 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 mapMethodType = new HashMap(); + + + 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 setVisited = new HashSet(); + LinkedList stack = new LinkedList(); + 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; + } } -- cgit v1.2.3