diff --git a/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java b/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java index 2fd9d3c..f060fbd 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java @@ -1,10 +1,11 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code.cfg; import org.jetbrains.java.decompiler.code.*; import org.jetbrains.java.decompiler.code.interpreter.InstructionImpact; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper; +import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.gen.DataPoint; @@ -96,9 +97,9 @@ public class ControlFlowGraph implements CodeConstants { return buf.toString(); } - public void inlineJsr(StructMethod mt) { + public void inlineJsr(StructClass cl, StructMethod mt) { processJsr(); - removeJsr(mt); + removeJsr(cl, mt); removeMarkers(); @@ -668,8 +669,8 @@ public class ControlFlowGraph implements CodeConstants { } } - private void removeJsr(StructMethod mt) { - removeJsrInstructions(mt.getClassStruct().getPool(), first, DataPoint.getInitialDataPoint(mt)); + private void removeJsr(StructClass cl, StructMethod mt) { + removeJsrInstructions(cl.getPool(), first, DataPoint.getInitialDataPoint(mt)); } private static void removeJsrInstructions(ConstantPool pool, BasicBlock block, DataPoint data) { diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index bfd46f2..3da67c6 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -41,7 +41,7 @@ public class ClassWriter { InitializerProcessor.extractInitializers(wrapper); if (node.type == ClassNode.CLASS_ROOT && - !cl.isVersionGE_1_5() && + !cl.isVersion5() && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) { ClassReference14Processor.processClassReferences(node); } @@ -249,10 +249,11 @@ public class ClassWriter { } } - boolean isModuleInfo = cl.hasModifier(CodeConstants.ACC_MODULE) && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); - - if (isModuleInfo) { - writeModuleInfoBody(buffer, cl); + if (cl.hasModifier(CodeConstants.ACC_MODULE)) { + StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); + if (moduleAttribute != null) { + writeModuleInfoBody(buffer, moduleAttribute); + } } buffer.appendIndent(indent).append('}'); @@ -268,111 +269,61 @@ public class ClassWriter { DecompilerContext.getLogger().endWriteClass(); } - private static boolean isSyntheticRecordMethod(StructMethod mt, TextBuffer code) { - if (mt.getClassStruct().getRecordComponents() == null) return false; - String name = mt.getName(); - String descriptor = mt.getDescriptor(); - if (name.equals("equals") && descriptor.equals("(Ljava/lang/Object;)Z") || - name.equals("hashCode") && descriptor.equals("()I") || - name.equals("toString") && descriptor.equals("()Ljava/lang/String;")) { - if (code.countLines() == 1) { - String str = code.toString().trim(); - return str.startsWith("return this." + name + "(this"); + private static boolean isSyntheticRecordMethod(StructClass cl, StructMethod mt, TextBuffer code) { + if (cl.getRecordComponents() != null) { + String name = mt.getName(), descriptor = mt.getDescriptor(); + //noinspection SpellCheckingInspection + if (name.equals("equals") && descriptor.equals("(Ljava/lang/Object;)Z") || + name.equals("hashCode") && descriptor.equals("()I") || + name.equals("toString") && descriptor.equals("()Ljava/lang/String;")) { + if (code.countLines() == 1) { + String str = code.toString().trim(); + return str.startsWith("return this." + name + "(this"); + } } } return false; } - private void writeModuleInfoBody(TextBuffer buffer, StructClass cl) { - StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); - + private static void writeModuleInfoBody(TextBuffer buffer, StructModuleAttribute moduleAttribute) { for (StructModuleAttribute.RequiresEntry requires : moduleAttribute.requires) { - String moduleName = requires.moduleName.replace('/', '.'); - - buffer.appendIndent(1) - .append("requires ") - .append(moduleName) - .append(';') - .appendLineSeparator(); + buffer.appendIndent(1).append("requires ").append(requires.moduleName.replace('/', '.')).append(';').appendLineSeparator(); } for (StructModuleAttribute.ExportsEntry exports : moduleAttribute.exports) { - String packageName = exports.packageName.replace('/', '.'); - - buffer.appendIndent(1).append("exports ").append(packageName); + buffer.appendIndent(1).append("exports ").append(exports.packageName.replace('/', '.')); List exportToModules = exports.exportToModules; if (exportToModules.size() > 0) { buffer.append(" to").appendLineSeparator(); - - int lastIndex = exportToModules.size() - 1; - for (int i = 0; i < exportToModules.size(); i++) { - String moduleName = exportToModules.get(i).replace('/', '.'); - char separator = i == lastIndex ? ';' : ','; - - buffer.appendIndent(2) - .append(moduleName) - .append(separator) - .appendLineSeparator(); - } - } else { - buffer.append(';').appendLineSeparator(); + appendFQClassNames(buffer, exportToModules); } + + buffer.append(';').appendLineSeparator(); } for (StructModuleAttribute.OpensEntry opens : moduleAttribute.opens) { - String packageName = opens.packageName.replace('/', '.'); - - buffer.appendIndent(1).append("opens ").append(packageName); + buffer.appendIndent(1).append("opens ").append(opens.packageName.replace('/', '.')); List opensToModules = opens.opensToModules; if (opensToModules.size() > 0) { buffer.append(" to").appendLineSeparator(); - - int lastIndex = opensToModules.size() - 1; - for (int i = 0; i < opensToModules.size(); i++) { - String moduleName = opensToModules.get(i).replace('/', '.'); - char separator = i == lastIndex ? ';' : ','; - - buffer.appendIndent(2) - .append(moduleName) - .append(separator) - .appendLineSeparator(); - } - } else { - buffer.append(';').appendLineSeparator(); + appendFQClassNames(buffer, opensToModules); } + + buffer.append(';').appendLineSeparator(); } for (String uses : moduleAttribute.uses) { - String className = ExprProcessor.buildJavaClassName(uses); - - buffer.appendIndent(1) - .append("uses ") - .append(className) - .append(';') - .appendLineSeparator(); + buffer.appendIndent(1).append("uses ").append(ExprProcessor.buildJavaClassName(uses)).append(';').appendLineSeparator(); } for (StructModuleAttribute.ProvidesEntry provides : moduleAttribute.provides) { - String interfaceName = ExprProcessor.buildJavaClassName(provides.interfaceName); - - buffer.appendIndent(1) - .append("provides ") - .append(interfaceName) - .append(" with") - .appendLineSeparator(); - - int lastIndex = provides.implementationNames.size() - 1; - for (int i = 0; i < provides.implementationNames.size(); i++) { - String className = ExprProcessor.buildJavaClassName(provides.implementationNames.get(i)); - char separator = i == lastIndex ? ';' : ','; - - buffer.appendIndent(2) - .append(className) - .append(separator) - .appendLineSeparator(); - } + buffer.appendIndent(1).append("provides ").append(ExprProcessor.buildJavaClassName(provides.interfaceName)).append(" with").appendLineSeparator(); + @SuppressWarnings({"SSBasedInspection", "RedundantSuppression"}) List javaNames = + provides.implementationNames.stream().map(ExprProcessor::buildJavaClassName).collect(Collectors.toList()); + appendFQClassNames(buffer, javaNames); + buffer.append(';').appendLineSeparator(); } } @@ -519,11 +470,11 @@ public class ClassWriter { buffer.append('{').appendLineSeparator(); } - private boolean isVarArgRecord(StructClass cl) { + private static boolean isVarArgRecord(StructClass cl) { String canonicalConstructorDescriptor = cl.getRecordComponents().stream().map(c -> c.getDescriptor()).collect(Collectors.joining("", "(", ")V")); - StructMethod ctor = cl.getMethod(CodeConstants.INIT_NAME, canonicalConstructorDescriptor); - return ctor != null && ctor.hasModifier(CodeConstants.ACC_VARARGS); + StructMethod init = cl.getMethod(CodeConstants.INIT_NAME, canonicalConstructorDescriptor); + return init != null && init.hasModifier(CodeConstants.ACC_VARARGS); } private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) { @@ -553,15 +504,9 @@ public class ClassWriter { appendModifiers(buffer, fd.getAccessFlags(), FIELD_ALLOWED, isInterface, FIELD_EXCLUDED); } - VarType fieldType = new VarType(fd.getDescriptor(), false); - - GenericFieldDescriptor descriptor = null; - if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = fd.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE); - if (attr != null) { - descriptor = GenericMain.parseFieldSignature(attr.getSignature()); - } - } + Map.Entry fieldTypeData = getFieldTypeData(fd); + VarType fieldType = fieldTypeData.getKey(); + GenericFieldDescriptor descriptor = fieldTypeData.getValue(); if (!isEnum) { if (descriptor != null) { @@ -619,15 +564,9 @@ public class ClassWriter { private static void recordComponentToJava(StructRecordComponent cd, TextBuffer buffer, boolean varArgComponent) { appendAnnotations(buffer, -1, cd, TypeAnnotation.FIELD); - VarType fieldType = new VarType(cd.getDescriptor(), false); - - GenericFieldDescriptor descriptor = null; - if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = cd.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE); - if (attr != null) { - descriptor = GenericMain.parseFieldSignature(attr.getSignature()); - } - } + Map.Entry fieldTypeData = getFieldTypeData(cd); + VarType fieldType = fieldTypeData.getKey(); + GenericFieldDescriptor descriptor = fieldTypeData.getValue(); if (descriptor != null) { buffer.append(GenericMain.getGenericCastTypeName(varArgComponent ? descriptor.type.decreaseArrayDim() : descriptor.type)); @@ -770,7 +709,7 @@ public class ClassWriter { boolean isAnnotation = cl.hasModifier(CodeConstants.ACC_ANNOTATION); boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); boolean isDeprecated = mt.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED); - boolean clinit = false, init = false, dinit = false; + boolean clInit = false, init = false, dInit = false; MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); @@ -815,7 +754,7 @@ public class ClassWriter { if (CodeConstants.INIT_NAME.equals(name)) { if (node.type == ClassNode.CLASS_ANONYMOUS) { name = ""; - dinit = true; + dInit = true; } else { name = node.simpleName; @@ -824,7 +763,7 @@ public class ClassWriter { } else if (CodeConstants.CLINIT_NAME.equals(name)) { name = ""; - clinit = true; + clInit = true; } GenericMethodDescriptor descriptor = null; @@ -853,7 +792,7 @@ public class ClassWriter { boolean throwsExceptions = false; int paramCount = 0; - if (!clinit && !dinit) { + if (!clInit && !dInit) { boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC); if (descriptor != null && !descriptor.typeParameters.isEmpty()) { @@ -992,7 +931,7 @@ public class ClassWriter { buffer.appendLineSeparator(); } else { - if (!clinit && !dinit) { + if (!clInit && !dInit) { buffer.append(' '); } @@ -1008,8 +947,8 @@ public class ClassWriter { BytecodeMappingTracer codeTracer = new BytecodeMappingTracer(tracer.getCurrentSourceLine()); TextBuffer code = root.toJava(indent + 1, codeTracer); - hideMethod = (code.length() == 0) && (clinit || dinit || hideConstructor(node, init, throwsExceptions, paramCount, flags)) - || isSyntheticRecordMethod(mt, code); + hideMethod = code.length() == 0 && (clInit || dInit || hideConstructor(node, init, throwsExceptions, paramCount, flags)) || + isSyntheticRecordMethod(cl, mt, code); buffer.append(code); @@ -1049,7 +988,6 @@ public class ClassWriter { } private static boolean hideConstructor(ClassNode node, boolean init, boolean throwsExceptions, int paramCount, int methodAccessFlags) { - if (!init || throwsExceptions || paramCount > 0 || !DecompilerContext.getOption(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR)) { return false; } @@ -1057,11 +995,11 @@ public class ClassWriter { ClassWrapper wrapper = node.getWrapper(); StructClass cl = wrapper.getClassStruct(); - int classAccesFlags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access; + int classAccessFlags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access; boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); // default constructor requires same accessibility flags. Exception: enum constructor which is always private - if(!isEnum && ((classAccesFlags & ACCESSIBILITY_FLAGS) != (methodAccessFlags & ACCESSIBILITY_FLAGS))) { + if(!isEnum && ((classAccessFlags & ACCESSIBILITY_FLAGS) != (methodAccessFlags & ACCESSIBILITY_FLAGS))) { return false; } @@ -1077,6 +1015,20 @@ public class ClassWriter { return true; } + private static Map.Entry getFieldTypeData(StructField fd) { + VarType fieldType = new VarType(fd.getDescriptor(), false); + + GenericFieldDescriptor descriptor = null; + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute attr = fd.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE); + if (attr != null) { + descriptor = GenericMain.parseFieldSignature(attr.getSignature()); + } + } + + return new AbstractMap.SimpleImmutableEntry<>(fieldType, descriptor); + } + private static void appendDeprecation(TextBuffer buffer, int indent) { buffer.appendIndent(indent).append("/** @deprecated */").appendLineSeparator(); } @@ -1135,11 +1087,11 @@ public class ClassWriter { buffer.appendIndent(indent).append("// $FF: ").append(comment).appendLineSeparator(); } - private static final StructGeneralAttribute.Key[] ANNOTATION_ATTRIBUTES = { + private static final StructGeneralAttribute.Key[] ANNOTATION_ATTRIBUTES = { StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS}; - private static final StructGeneralAttribute.Key[] PARAMETER_ANNOTATION_ATTRIBUTES = { + private static final StructGeneralAttribute.Key[] PARAMETER_ANNOTATION_ATTRIBUTES = { StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS}; - private static final StructGeneralAttribute.Key[] TYPE_ANNOTATION_ATTRIBUTES = { + private static final StructGeneralAttribute.Key[] TYPE_ANNOTATION_ATTRIBUTES = { StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS}; private static void appendAnnotations(TextBuffer buffer, int indent, StructMember mb, int targetType) { @@ -1283,4 +1235,14 @@ public class ClassWriter { buffer.append('>'); } -} \ No newline at end of file + + private static void appendFQClassNames(TextBuffer buffer, List names) { + for (int i = 0; i < names.size(); i++) { + String name = names.get(i); + buffer.appendIndent(2).append(name); + if (i < names.size() - 1) { + buffer.append(',').appendLineSeparator(); + } + } + } +} diff --git a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java index 1ee22f2..d05c839 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -245,7 +245,7 @@ public class ClassesProcessor implements CodeConstants { } try { - mt.expandData(); + mt.expandData(enclosingCl); InstructionSequence seq = mt.getInstructionSequence(); if (seq != null) { diff --git a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java index ca63ad5..d0b73c0 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.rels; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -59,10 +59,10 @@ public class ClassWrapper { try { if (mt.containsCode()) { if (maxSec == 0 || testMode) { - root = MethodProcessorRunnable.codeToJava(mt, md, varProc); + root = MethodProcessorRunnable.codeToJava(classStruct, mt, md, varProc); } else { - MethodProcessorRunnable mtProc = new MethodProcessorRunnable(mt, md, varProc, DecompilerContext.getCurrentContext()); + MethodProcessorRunnable mtProc = new MethodProcessorRunnable(classStruct, mt, md, varProc, DecompilerContext.getCurrentContext()); Thread mtThread = new Thread(mtProc, "Java decompiler"); long stopAt = System.currentTimeMillis() + maxSec * 1000L; diff --git a/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java index fd0f261..2ad5b20 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.rels; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -33,17 +33,16 @@ public class LambdaProcessor { ClassesProcessor clProcessor = DecompilerContext.getClassProcessor(); StructClass cl = node.classStruct; - if (cl.getBytecodeVersion() < CodeConstants.BYTECODE_JAVA_8) { // lambda beginning with Java 8 + if (!cl.isVersion8()) { // lambda beginning with Java 8 return; } - StructBootstrapMethodsAttribute bootstrap = - cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS); + StructBootstrapMethodsAttribute bootstrap = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS); if (bootstrap == null || bootstrap.getMethodsNumber() == 0) { return; // no bootstrap constants in pool } - BitSet lambda_methods = new BitSet(); + BitSet lambdaMethods = new BitSet(); // find lambda bootstrap constants for (int i = 0; i < bootstrap.getMethodsNumber(); ++i) { @@ -52,11 +51,11 @@ public class LambdaProcessor { // FIXME: extend for Eclipse etc. at some point if (JAVAC_LAMBDA_CLASS.equals(method_ref.classname) && (JAVAC_LAMBDA_METHOD.equals(method_ref.elementname) || JAVAC_LAMBDA_ALT_METHOD.equals(method_ref.elementname))) { - lambda_methods.set(i); + lambdaMethods.set(i); } } - if (lambda_methods.isEmpty()) { + if (lambdaMethods.isEmpty()) { return; // no lambda bootstrap constant found } @@ -64,7 +63,7 @@ public class LambdaProcessor { // iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes. for (StructMethod mt : cl.getMethods()) { - mt.expandData(); + mt.expandData(cl); InstructionSequence seq = mt.getInstructionSequence(); if (seq != null && seq.length() > 0) { @@ -76,7 +75,7 @@ public class LambdaProcessor { if (instr.opcode == CodeConstants.opc_invokedynamic) { LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.operand(0)); - if (lambda_methods.get(invoke_dynamic.index1)) { // lambda invocation found + if (lambdaMethods.get(invoke_dynamic.index1)) { // lambda invocation found List bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1); MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor); @@ -123,4 +122,4 @@ public class LambdaProcessor { // FIXME: mixed hierarchy? } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index 726d747..d9de3d5 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.rels; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -22,6 +22,7 @@ import java.io.IOException; public class MethodProcessorRunnable implements Runnable { public final Object lock = new Object(); + private final StructClass klass; private final StructMethod method; private final MethodDescriptor methodDescriptor; private final VarProcessor varProc; @@ -31,10 +32,12 @@ public class MethodProcessorRunnable implements Runnable { private volatile Throwable error; private volatile boolean finished = false; - public MethodProcessorRunnable(StructMethod method, + public MethodProcessorRunnable(StructClass klass, + StructMethod method, MethodDescriptor methodDescriptor, VarProcessor varProc, DecompilerContext parentContext) { + this.klass = klass; this.method = method; this.methodDescriptor = methodDescriptor; this.varProc = varProc; @@ -48,7 +51,7 @@ public class MethodProcessorRunnable implements Runnable { try { DecompilerContext.setCurrentContext(parentContext); - root = codeToJava(method, methodDescriptor, varProc); + root = codeToJava(klass, method, methodDescriptor, varProc); } catch (Throwable t) { error = t; @@ -63,17 +66,15 @@ public class MethodProcessorRunnable implements Runnable { } } - public static RootStatement codeToJava(StructMethod mt, MethodDescriptor md, VarProcessor varProc) throws IOException { - StructClass cl = mt.getClassStruct(); - + public static RootStatement codeToJava(StructClass cl, StructMethod mt, MethodDescriptor md, VarProcessor varProc) throws IOException { boolean isInitializer = CodeConstants.CLINIT_NAME.equals(mt.getName()); // for now static initializer only - mt.expandData(); + mt.expandData(cl); InstructionSequence seq = mt.getInstructionSequence(); ControlFlowGraph graph = new ControlFlowGraph(seq); DeadCodeHelper.removeDeadBlocks(graph); - graph.inlineJsr(mt); + graph.inlineJsr(cl, mt); // TODO: move to the start, before jsr inlining DeadCodeHelper.connectDummyExitBlock(graph); @@ -110,13 +111,13 @@ public class MethodProcessorRunnable implements Runnable { if (!ExceptionDeobfuscator.handleMultipleEntryExceptionRanges(graph)) { DecompilerContext.getLogger().writeMessage("Found multiple entry exception ranges which could not be splitted", IFernflowerLogger.Severity.WARN); } - ExceptionDeobfuscator.insertDummyExceptionHandlerBlocks(graph, cl.getBytecodeVersion()); + ExceptionDeobfuscator.insertDummyExceptionHandlerBlocks(graph, mt.getBytecodeVersion()); } RootStatement root = DomHelper.parseGraph(graph); FinallyProcessor fProc = new FinallyProcessor(md, varProc); - while (fProc.iterateGraph(mt, root, graph)) { + while (fProc.iterateGraph(cl, mt, root, graph)) { root = DomHelper.parseGraph(graph); } @@ -200,4 +201,4 @@ public class MethodProcessorRunnable implements Runnable { public boolean isFinished() { return finished; } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java index 53a9b68..9f52895 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -26,6 +26,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; +import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; @@ -46,12 +47,8 @@ public class FinallyProcessor { varProcessor = varProc; } - public boolean iterateGraph(StructMethod mt, RootStatement root, ControlFlowGraph graph) { - return processStatementEx(mt, root, graph); - } - - private boolean processStatementEx(StructMethod mt, RootStatement root, ControlFlowGraph graph) { - int bytecode_version = mt.getClassStruct().getBytecodeVersion(); + public boolean iterateGraph(StructClass cl, StructMethod mt, RootStatement root, ControlFlowGraph graph) { + int bytecodeVersion = mt.getBytecodeVersion(); LinkedList stack = new LinkedList<>(); stack.add(root); @@ -77,22 +74,20 @@ public class FinallyProcessor { fin.setMonitor(var == null ? null : new VarExprent(var, VarType.VARTYPE_INT, varProcessor)); } else { - Record inf = getFinallyInformation(mt, root, fin); + Record inf = getFinallyInformation(cl, mt, root, fin); if (inf == null) { // inconsistent finally catchallBlockIDs.put(handler.id, null); } else { - if (DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) { finallyBlockIDs.put(handler.id, null); } else { + int varIndex = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); + insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varIndex, inf, bytecodeVersion); - int varindex = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); - insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf, bytecode_version); - - finallyBlockIDs.put(handler.id, varindex); + finallyBlockIDs.put(handler.id, varIndex); } DeadCodeHelper.removeDeadBlocks(graph); // e.g. multiple return blocks after a nested finally @@ -120,7 +115,7 @@ public class FinallyProcessor { } } - private Record getFinallyInformation(StructMethod mt, RootStatement root, CatchAllStatement fstat) { + private Record getFinallyInformation(StructClass cl, StructMethod mt, RootStatement root, CatchAllStatement fstat) { Map mapLast = new HashMap<>(); BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead(); @@ -138,7 +133,7 @@ public class FinallyProcessor { } ExprProcessor proc = new ExprProcessor(methodDescriptor, varProcessor); - proc.processStatement(root, mt.getClassStruct()); + proc.processStatement(root, cl); SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); ssa.splitVariables(root, mt); @@ -977,4 +972,4 @@ public class FinallyProcessor { } } } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index f30082e..d0a2260 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.exps; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -30,7 +30,6 @@ import java.util.ArrayList; import java.util.List; public class VarExprent extends Exprent { - public static final int STACK_BASE = 10000; public static final String VAR_NAMELESS_ENCLOSURE = ""; @@ -122,7 +121,7 @@ public class VarExprent extends Exprent { Integer origIndex = processor.getVarOriginalIndex(index); if (origIndex != null) { String name = attr.getName(origIndex, visibleOffset); - if (name != null && TextUtil.isValidIdentifier(name, method.getClassStruct().getBytecodeVersion())) { + if (name != null && TextUtil.isValidIdentifier(name, method.getBytecodeVersion())) { return name; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index 9999b50..8d766e0 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.vars; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; @@ -55,7 +55,7 @@ public class VarProcessor { Integer index = mapOriginalVarIndices.get(pair.var); if (index != null) { String debugName = mapDebugVarNames.get(index); - if (debugName != null && TextUtil.isValidIdentifier(debugName, method.getClassStruct().getBytecodeVersion())) { + if (debugName != null && TextUtil.isValidIdentifier(debugName, method.getBytecodeVersion())) { name = debugName; } } @@ -72,11 +72,7 @@ public class VarProcessor { } public Integer getVarOriginalIndex(int index) { - if (varVersions == null) { - return null; - } - - return varVersions.getMapOriginalVarIndices().get(index); + return varVersions == null ? null : varVersions.getMapOriginalVarIndices().get(index); } public void refreshVarNames(VarNamesCollector vc) { @@ -125,4 +121,4 @@ public class VarProcessor { public Set getExternalVars() { return externalVars; } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java index ef580de..c77cfaa 100644 --- a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java +++ b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java @@ -1,4 +1,4 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.main.DecompilerContext; @@ -65,7 +65,7 @@ public class ContextUnit { StructClass newCl; try (DataInputFullStream in = loader.getClassStream(oldName)) { - newCl = new StructClass(in, cl.isOwn(), loader); + newCl = StructClass.create(in, cl.isOwn(), loader); } lstClasses.add(newCl); diff --git a/src/org/jetbrains/java/decompiler/struct/StructClass.java b/src/org/jetbrains/java/decompiler/struct/StructClass.java index 7a46954..134813e 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructClass.java +++ b/src/org/jetbrains/java/decompiler/struct/StructClass.java @@ -1,4 +1,4 @@ -// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -13,6 +13,7 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.io.IOException; import java.util.List; +import java.util.Map; /* class_file { @@ -35,71 +36,86 @@ import java.util.List; } */ public class StructClass extends StructMember { - - public final String qualifiedName; - public final PrimitiveConstant superClass; - - private final boolean own; - private final LazyLoader loader; - private final int minorVersion; - private final int majorVersion; - private final int[] interfaces; - private final String[] interfaceNames; - private final VBStyleCollection fields; - private final VBStyleCollection methods; - - private ConstantPool pool; - - public StructClass(byte[] bytes, boolean own, LazyLoader loader) throws IOException { - this(new DataInputFullStream(bytes), own, loader); - } - - public StructClass(DataInputFullStream in, boolean own, LazyLoader loader) throws IOException { - this.own = own; - this.loader = loader; - + public static StructClass create(DataInputFullStream in, boolean own, LazyLoader loader) throws IOException { in.discard(4); + int minorVersion = in.readUnsignedShort(); + int majorVersion = in.readUnsignedShort(); + int bytecodeVersion = Math.max(majorVersion, CodeConstants.BYTECODE_JAVA_LE_4); - minorVersion = in.readUnsignedShort(); - majorVersion = in.readUnsignedShort(); + ConstantPool pool = new ConstantPool(in); - pool = new ConstantPool(in); - - accessFlags = in.readUnsignedShort(); + int accessFlags = in.readUnsignedShort(); int thisClassIdx = in.readUnsignedShort(); int superClassIdx = in.readUnsignedShort(); - qualifiedName = pool.getPrimitiveConstant(thisClassIdx).getString(); - superClass = pool.getPrimitiveConstant(superClassIdx); + String qualifiedName = pool.getPrimitiveConstant(thisClassIdx).getString(); + PrimitiveConstant superClass = pool.getPrimitiveConstant(superClassIdx); - // interfaces int length = in.readUnsignedShort(); - interfaces = new int[length]; - interfaceNames = new String[length]; + int[] interfaces = new int[length]; + String[] interfaceNames = new String[length]; for (int i = 0; i < length; i++) { interfaces[i] = in.readUnsignedShort(); interfaceNames[i] = pool.getPrimitiveConstant(interfaces[i]).getString(); } - // fields length = in.readUnsignedShort(); - fields = new VBStyleCollection<>(length); + VBStyleCollectionfields = new VBStyleCollection<>(length); for (int i = 0; i < length; i++) { - StructField field = new StructField(in, this); + StructField field = StructField.create(in, pool, qualifiedName); fields.addWithKey(field, InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor())); } - // methods length = in.readUnsignedShort(); - methods = new VBStyleCollection<>(length); + VBStyleCollectionmethods = new VBStyleCollection<>(length); for (int i = 0; i < length; i++) { - StructMethod method = new StructMethod(in, this); + StructMethod method = StructMethod.create(in, pool, qualifiedName, bytecodeVersion, own); methods.addWithKey(method, InterpreterUtil.makeUniqueKey(method.getName(), method.getDescriptor())); } - // attributes - attributes = readAttributes(in, pool); + Map attributes = readAttributes(in, pool); + + StructClass cl = new StructClass( + accessFlags, attributes, qualifiedName, superClass, own, loader, minorVersion, majorVersion, interfaces, interfaceNames, fields, methods); + if (loader == null) cl.pool = pool; + return cl; + } + + public final String qualifiedName; + public final PrimitiveConstant superClass; + private final boolean own; + private final LazyLoader loader; + private final int minorVersion; + private final int majorVersion; + private final int[] interfaces; + private final String[] interfaceNames; + private final VBStyleCollection fields; + private final VBStyleCollection methods; + + private ConstantPool pool; - releaseResources(); + private StructClass(int accessFlags, + Map attributes, + String qualifiedName, + PrimitiveConstant superClass, + boolean own, + LazyLoader loader, + int minorVersion, + int majorVersion, + int[] interfaces, + String[] interfaceNames, + VBStyleCollection fields, + VBStyleCollection methods) { + super(accessFlags, attributes); + this.qualifiedName = qualifiedName; + this.superClass = superClass; + this.own = own; + this.loader = loader; + this.minorVersion = minorVersion; + this.majorVersion = majorVersion; + this.interfaces = interfaces; + this.interfaceNames = interfaceNames; + this.fields = fields; + this.methods = methods; } public boolean hasField(String name, String descriptor) { @@ -168,17 +184,13 @@ public class StructClass extends StructMember { return loader; } - public boolean isVersionGE_1_5() { + public boolean isVersion5() { return (majorVersion > CodeConstants.BYTECODE_JAVA_LE_4 || (majorVersion == CodeConstants.BYTECODE_JAVA_LE_4 && minorVersion > 0)); // FIXME: check second condition } - public boolean isVersionGE_1_7() { - return (majorVersion >= CodeConstants.BYTECODE_JAVA_7); - } - - public int getBytecodeVersion() { - return Math.max(majorVersion, CodeConstants.BYTECODE_JAVA_LE_4); + public boolean isVersion8() { + return majorVersion >= CodeConstants.BYTECODE_JAVA_8; } @Override diff --git a/src/org/jetbrains/java/decompiler/struct/StructContext.java b/src/org/jetbrains/java/decompiler/struct/StructContext.java index 6d07096..61486aa 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructContext.java +++ b/src/org/jetbrains/java/decompiler/struct/StructContext.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.main.DecompilerContext; @@ -17,7 +17,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; public class StructContext { - private final IResultSaver saver; private final IDecompiledData decompiledData; private final LazyLoader loader; @@ -106,7 +105,7 @@ public class StructContext { if (filename.endsWith(".class")) { try (DataInputFullStream in = loader.getClassStream(file.getAbsolutePath(), null)) { - StructClass cl = new StructClass(in, isOwn, loader); + StructClass cl = StructClass.create(in, isOwn, loader); classes.put(cl.qualifiedName, cl); unit.addClass(cl, filename); loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), null)); @@ -141,7 +140,7 @@ public class StructContext { if (!entry.isDirectory()) { if (name.endsWith(".class")) { byte[] bytes = InterpreterUtil.getBytes(archive, entry); - StructClass cl = new StructClass(bytes, isOwn, loader); + StructClass cl = StructClass.create(new DataInputFullStream(bytes), isOwn, loader); classes.put(cl.qualifiedName, cl); unit.addClass(cl, name); loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), name)); @@ -160,4 +159,4 @@ public class StructContext { public Map getClasses() { return classes; } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/StructField.java b/src/org/jetbrains/java/decompiler/struct/StructField.java index 6c85ca4..cfa64d2 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructField.java +++ b/src/org/jetbrains/java/decompiler/struct/StructField.java @@ -1,10 +1,12 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.util.DataInputFullStream; import java.io.IOException; +import java.util.Map; /* field_info { @@ -16,29 +18,32 @@ import java.io.IOException; } */ public class StructField extends StructMember { + public static StructField create(DataInputFullStream in, ConstantPool pool, String clQualifiedName) throws IOException { + int accessFlags = in.readUnsignedShort(); + int nameIndex = in.readUnsignedShort(); + int descriptorIndex = in.readUnsignedShort(); - private final String name; - private final String descriptor; + String[] values = pool.getClassElement(ConstantPool.FIELD, clQualifiedName, nameIndex, descriptorIndex); + Map attributes = readAttributes(in, pool); - public StructField(DataInputFullStream in, StructClass clStruct) throws IOException { - accessFlags = in.readUnsignedShort(); - int nameIndex = in.readUnsignedShort(); - int descriptorIndex = in.readUnsignedShort(); + return new StructField(accessFlags, attributes, values[0], values[1]); + } - ConstantPool pool = clStruct.getPool(); - String[] values = pool.getClassElement(ConstantPool.FIELD, clStruct.qualifiedName, nameIndex, descriptorIndex); - name = values[0]; - descriptor = values[1]; + private final String name; + private final String descriptor; - attributes = readAttributes(in, pool); + protected StructField(int accessFlags, Map attributes, String name, String descriptor) { + super(accessFlags, attributes); + this.name = name; + this.descriptor = descriptor; } - public String getName() { + public final String getName() { return name; } - public String getDescriptor() { + public final String getDescriptor() { return descriptor; } diff --git a/src/org/jetbrains/java/decompiler/struct/StructMember.java b/src/org/jetbrains/java/decompiler/struct/StructMember.java index a5b87f1..920e7f9 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructMember.java +++ b/src/org/jetbrains/java/decompiler/struct/StructMember.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -12,23 +12,26 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -public class StructMember { - - protected int accessFlags; - protected Map attributes; +public abstract class StructMember { + private final int accessFlags; + private final Map attributes; + protected StructMember(int accessFlags, Map attributes) { + this.accessFlags = accessFlags; + this.attributes = attributes; + } public int getAccessFlags() { return accessFlags; } public T getAttribute(StructGeneralAttribute.Key attribute) { - //noinspection unchecked - return (T)attributes.get(attribute.getName()); + @SuppressWarnings("unchecked") T t = (T)attributes.get(attribute.name); + return t; } public boolean hasAttribute(StructGeneralAttribute.Key attribute) { - return attributes.containsKey(attribute.getName()); + return attributes.containsKey(attribute.name); } public boolean hasModifier(int modifier) { @@ -39,45 +42,37 @@ public class StructMember { return hasModifier(CodeConstants.ACC_SYNTHETIC) || hasAttribute(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC); } - protected Map readAttributes(DataInputFullStream in, ConstantPool pool) throws IOException { + public static Map readAttributes(DataInputFullStream in, ConstantPool pool) throws IOException { int length = in.readUnsignedShort(); - Map attributes = new HashMap<>(length); + for (int i = 0; i < length; i++) { int nameIndex = in.readUnsignedShort(); String name = pool.getPrimitiveConstant(nameIndex).getString(); - StructGeneralAttribute attribute = readAttribute(in, pool, name); - - if (attribute != null) { - if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE.getName().equals(name) && attributes.containsKey(name)) { + StructGeneralAttribute attribute = StructGeneralAttribute.createAttribute(name); + int attLength = in.readInt(); + if (attribute == null) { + in.discard(attLength); + } + else { + attribute.initContent(in, pool); + if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE.name.equals(name) && attributes.containsKey(name)) { // merge all variable tables StructLocalVariableTableAttribute table = (StructLocalVariableTableAttribute)attributes.get(name); table.add((StructLocalVariableTableAttribute)attribute); } - else if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.getName().equals(name) && attributes.containsKey(name)) { + else if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.name.equals(name) && attributes.containsKey(name)) { // merge all variable tables StructLocalVariableTypeTableAttribute table = (StructLocalVariableTypeTableAttribute)attributes.get(name); table.add((StructLocalVariableTypeTableAttribute)attribute); } else { - attributes.put(attribute.getName(), attribute); + attributes.put(name, attribute); } } } return attributes; } - - protected StructGeneralAttribute readAttribute(DataInputFullStream in, ConstantPool pool, String name) throws IOException { - StructGeneralAttribute attribute = StructGeneralAttribute.createAttribute(name); - int length = in.readInt(); - if (attribute == null) { - in.discard(length); - } - else { - attribute.initContent(in, pool); - } - return attribute; - } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/StructMethod.java b/src/org/jetbrains/java/decompiler/struct/StructMethod.java index 859805c..a715593 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructMethod.java +++ b/src/org/jetbrains/java/decompiler/struct/StructMethod.java @@ -1,7 +1,8 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.code.*; +import org.jetbrains.java.decompiler.struct.attr.StructCodeAttribute; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; @@ -25,80 +26,66 @@ import static org.jetbrains.java.decompiler.code.CodeConstants.*; } */ public class StructMethod extends StructMember { + public static StructMethod create(DataInputFullStream in, ConstantPool pool, String clQualifiedName, int bytecodeVersion, boolean own) throws IOException { + int accessFlags = in.readUnsignedShort(); + int nameIndex = in.readUnsignedShort(); + int descriptorIndex = in.readUnsignedShort(); + + String[] values = pool.getClassElement(ConstantPool.METHOD, clQualifiedName, nameIndex, descriptorIndex); + + Map attributes = readAttributes(in, pool); + StructCodeAttribute code = (StructCodeAttribute)attributes.remove(StructGeneralAttribute.ATTRIBUTE_CODE.name); + if (code != null) { + attributes.putAll(code.codeAttributes); + } + + return new StructMethod(accessFlags, attributes, values[0], values[1], bytecodeVersion, own ? code : null); + } + private static final int[] opr_iconst = {-1, 0, 1, 2, 3, 4, 5}; private static final int[] opr_loadstore = {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}; private static final int[] opcs_load = {opc_iload, opc_lload, opc_fload, opc_dload, opc_aload}; private static final int[] opcs_store = {opc_istore, opc_lstore, opc_fstore, opc_dstore, opc_astore}; - private final StructClass classStruct; private final String name; private final String descriptor; - - private boolean containsCode = false; - private int localVariables = 0; - private int codeLength = 0; - private int codeFullLength = 0; - private InstructionSequence seq; + private final int bytecodeVersion; + private final int localVariables; + private final int codeLength; + private final int codeFullLength; + private InstructionSequence seq = null; private boolean expanded = false; - private Map codeAttributes; - - public StructMethod(DataInputFullStream in, StructClass clStruct) throws IOException { - classStruct = clStruct; - accessFlags = in.readUnsignedShort(); - int nameIndex = in.readUnsignedShort(); - int descriptorIndex = in.readUnsignedShort(); - - ConstantPool pool = clStruct.getPool(); - String[] values = pool.getClassElement(ConstantPool.METHOD, clStruct.qualifiedName, nameIndex, descriptorIndex); - name = values[0]; - descriptor = values[1]; - - attributes = readAttributes(in, pool); - if (codeAttributes != null) { - attributes.putAll(codeAttributes); - codeAttributes = null; + private StructMethod(int accessFlags, + Map attributes, + String name, + String descriptor, + int bytecodeVersion, + StructCodeAttribute code) { + super(accessFlags, attributes); + this.name = name; + this.descriptor = descriptor; + this.bytecodeVersion = bytecodeVersion; + if (code != null) { + this.localVariables = code.localVariables; + this.codeLength = code.codeLength; + this.codeFullLength = code.codeFullLength; } - } - - @Override - protected StructGeneralAttribute readAttribute(DataInputFullStream in, ConstantPool pool, String name) throws IOException { - if (StructGeneralAttribute.ATTRIBUTE_CODE.getName().equals(name)) { - if (!classStruct.isOwn()) { - // skip code in foreign classes - in.discard(8); - in.discard(in.readInt()); - in.discard(8 * in.readUnsignedShort()); - } - else { - containsCode = true; - in.discard(6); - localVariables = in.readUnsignedShort(); - codeLength = in.readInt(); - in.discard(codeLength); - int excLength = in.readUnsignedShort(); - in.discard(excLength * 8); - codeFullLength = codeLength + excLength * 8 + 2; - } - - codeAttributes = readAttributes(in, pool); - - return null; + else { + this.localVariables = this.codeLength = this.codeFullLength = -1; } - - return super.readAttribute(in, pool, name); } - public void expandData() throws IOException { - if (containsCode && !expanded) { - byte[] code = classStruct.getLoader().loadBytecode(this, codeFullLength); + public void expandData(StructClass classStruct) throws IOException { + if (codeLength >= 0 && !expanded) { + byte[] code = classStruct.getLoader().loadBytecode(classStruct, this, codeFullLength); seq = parseBytecode(new DataInputFullStream(code), codeLength, classStruct.getPool()); expanded = true; } } public void releaseResources() { - if (containsCode && expanded) { + if (codeLength >= 0 && expanded) { seq = null; expanded = false; } @@ -108,10 +95,7 @@ public class StructMethod extends StructMember { private InstructionSequence parseBytecode(DataInputFullStream in, int length, ConstantPool pool) throws IOException { VBStyleCollection instructions = new VBStyleCollection<>(); - int bytecode_version = classStruct.getBytecodeVersion(); - for (int i = 0; i < length; ) { - int offset = i; int opcode = in.readUnsignedByte(); @@ -197,7 +181,7 @@ public class StructMethod extends StructMember { } break; case opc_invokedynamic: - if (classStruct.isVersionGE_1_7()) { // instruction unused in Java 6 and before + if (bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) { // instruction unused in Java 6 and before operands.add(in.readUnsignedShort()); in.discard(2); group = GROUP_INVOCATION; @@ -313,7 +297,7 @@ public class StructMethod extends StructMember { } } - Instruction instr = Instruction.create(opcode, wide, group, bytecode_version, ops); + Instruction instr = Instruction.create(opcode, wide, group, bytecodeVersion, ops); instructions.addWithKey(instr, offset); @@ -355,10 +339,6 @@ public class StructMethod extends StructMember { return seq; } - public StructClass getClassStruct() { - return classStruct; - } - public String getName() { return name; } @@ -367,8 +347,12 @@ public class StructMethod extends StructMember { return descriptor; } + public int getBytecodeVersion() { + return bytecodeVersion; + } + public boolean containsCode() { - return containsCode; + return codeLength >= 0; } public int getLocalVariables() { @@ -387,4 +371,4 @@ public class StructMethod extends StructMember { public String toString() { return name; } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java b/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java index 830078c..e50983d 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java +++ b/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java @@ -1,11 +1,13 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.util.DataInputFullStream; import java.io.IOException; +import java.util.Map; /* record_component_info { @@ -15,33 +17,20 @@ import java.io.IOException; attribute_info attributes[attributes_count]; } */ -public class StructRecordComponent extends StructMember { - - private final String name; - private final String descriptor; - - - public StructRecordComponent(DataInputFullStream in, ConstantPool pool) throws IOException { - accessFlags = 0; +public class StructRecordComponent extends StructField { + public static StructRecordComponent create(DataInputFullStream in, ConstantPool pool) throws IOException { int nameIndex = in.readUnsignedShort(); int descriptorIndex = in.readUnsignedShort(); - name = ((PrimitiveConstant)pool.getConstant(nameIndex)).getString(); - descriptor = ((PrimitiveConstant)pool.getConstant(descriptorIndex)).getString(); + String name = ((PrimitiveConstant)pool.getConstant(nameIndex)).getString(); + String descriptor = ((PrimitiveConstant)pool.getConstant(descriptorIndex)).getString(); - attributes = readAttributes(in, pool); - } - - public String getName() { - return name; - } + Map attributes = readAttributes(in, pool); - public String getDescriptor() { - return descriptor; + return new StructRecordComponent(0, attributes, name, descriptor); } - @Override - public String toString() { - return name; + private StructRecordComponent(int flags, Map attributes, String name, String descriptor) { + super(flags, attributes, name, descriptor); } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java new file mode 100644 index 0000000..87f02be --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java @@ -0,0 +1,43 @@ +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.struct.attr; + +import org.jetbrains.java.decompiler.struct.StructMember; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; + +import java.io.IOException; +import java.util.Map; + +/* + u2 max_stack; + u2 max_locals; + u4 code_length; + u1 code[]; + u2 exception_table_length; + exception_table[] { + u2 start_pc; + u2 end_pc; + u2 handler_pc; + u2 catch_type; + }; + u2 attributes_count; + attribute_info attributes[]; +*/ +public class StructCodeAttribute extends StructGeneralAttribute { + public int localVariables = 0; + public int codeLength = 0; + public int codeFullLength = 0; + public Map codeAttributes; + + @Override + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { + data.discard(2); + localVariables = data.readUnsignedShort(); + codeLength = data.readInt(); + data.discard(codeLength); + int excLength = data.readUnsignedShort(); + data.discard(excLength * 8); + codeFullLength = codeLength + excLength * 8 + 2; + codeAttributes = StructMember.readAttributes(data, pool); + } +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java index 041e266..6239ee4 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java @@ -14,7 +14,7 @@ import java.io.IOException; } */ public class StructGeneralAttribute { - public static final Key ATTRIBUTE_CODE = new Key<>("Code"); + public static final Key ATTRIBUTE_CODE = new Key<>("Code"); public static final Key ATTRIBUTE_INNER_CLASSES = new Key<>("InnerClasses"); public static final Key ATTRIBUTE_SIGNATURE = new Key<>("Signature"); public static final Key ATTRIBUTE_ANNOTATION_DEFAULT = new Key<>("AnnotationDefault"); @@ -37,86 +37,74 @@ public class StructGeneralAttribute { public static final Key ATTRIBUTE_MODULE = new Key<>("Module"); public static final Key ATTRIBUTE_RECORD = new Key<>("Record"); + @SuppressWarnings("unused") public static class Key { - private final String name; + public final String name; public Key(String name) { this.name = name; } - - public String getName() { - return name; - } } - private String name; - public static StructGeneralAttribute createAttribute(String name) { - StructGeneralAttribute attr; - - if (ATTRIBUTE_INNER_CLASSES.getName().equals(name)) { - attr = new StructInnerClassesAttribute(); + if (ATTRIBUTE_CODE.name.equals(name)) { + return new StructCodeAttribute(); } - else if (ATTRIBUTE_CONSTANT_VALUE.getName().equals(name)) { - attr = new StructConstantValueAttribute(); + else if (ATTRIBUTE_INNER_CLASSES.name.equals(name)) { + return new StructInnerClassesAttribute(); } - else if (ATTRIBUTE_SIGNATURE.getName().equals(name)) { - attr = new StructGenericSignatureAttribute(); + else if (ATTRIBUTE_CONSTANT_VALUE.name.equals(name)) { + return new StructConstantValueAttribute(); } - else if (ATTRIBUTE_ANNOTATION_DEFAULT.getName().equals(name)) { - attr = new StructAnnDefaultAttribute(); + else if (ATTRIBUTE_SIGNATURE.name.equals(name)) { + return new StructGenericSignatureAttribute(); } - else if (ATTRIBUTE_EXCEPTIONS.getName().equals(name)) { - attr = new StructExceptionsAttribute(); + else if (ATTRIBUTE_ANNOTATION_DEFAULT.name.equals(name)) { + return new StructAnnDefaultAttribute(); } - else if (ATTRIBUTE_ENCLOSING_METHOD.getName().equals(name)) { - attr = new StructEnclosingMethodAttribute(); + else if (ATTRIBUTE_EXCEPTIONS.name.equals(name)) { + return new StructExceptionsAttribute(); } - else if (ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.getName().equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.getName().equals(name)) { - attr = new StructAnnotationAttribute(); + else if (ATTRIBUTE_ENCLOSING_METHOD.name.equals(name)) { + return new StructEnclosingMethodAttribute(); } - else if (ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.getName().equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.getName().equals(name)) { - attr = new StructAnnotationParameterAttribute(); + else if (ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.name.equals(name)) { + return new StructAnnotationAttribute(); } - else if (ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.getName().equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.getName().equals(name)) { - attr = new StructTypeAnnotationAttribute(); + else if (ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.name.equals(name)) { + return new StructAnnotationParameterAttribute(); } - else if (ATTRIBUTE_LOCAL_VARIABLE_TABLE.getName().equals(name)) { - attr = new StructLocalVariableTableAttribute(); + else if (ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.name.equals(name)) { + return new StructTypeAnnotationAttribute(); } - else if (ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.getName().equals(name)) { - attr = new StructLocalVariableTypeTableAttribute(); + else if (ATTRIBUTE_LOCAL_VARIABLE_TABLE.name.equals(name)) { + return new StructLocalVariableTableAttribute(); } - else if (ATTRIBUTE_BOOTSTRAP_METHODS.getName().equals(name)) { - attr = new StructBootstrapMethodsAttribute(); + else if (ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.name.equals(name)) { + return new StructLocalVariableTypeTableAttribute(); } - else if (ATTRIBUTE_SYNTHETIC.getName().equals(name) || ATTRIBUTE_DEPRECATED.getName().equals(name)) { - attr = new StructGeneralAttribute(); + else if (ATTRIBUTE_BOOTSTRAP_METHODS.name.equals(name)) { + return new StructBootstrapMethodsAttribute(); } - else if (ATTRIBUTE_LINE_NUMBER_TABLE.getName().equals(name)) { - attr = new StructLineNumberTableAttribute(); + else if (ATTRIBUTE_SYNTHETIC.name.equals(name) || ATTRIBUTE_DEPRECATED.name.equals(name)) { + return new StructGeneralAttribute(); } - else if (ATTRIBUTE_METHOD_PARAMETERS.getName().equals(name)) { - attr = new StructMethodParametersAttribute(); + else if (ATTRIBUTE_LINE_NUMBER_TABLE.name.equals(name)) { + return new StructLineNumberTableAttribute(); } - else if (ATTRIBUTE_MODULE.getName().equals(name)) { - attr = new StructModuleAttribute(); + else if (ATTRIBUTE_METHOD_PARAMETERS.name.equals(name)) { + return new StructMethodParametersAttribute(); } - else if (ATTRIBUTE_RECORD.getName().equals(name)) { - attr = new StructRecordAttribute(); + else if (ATTRIBUTE_MODULE.name.equals(name)) { + return new StructModuleAttribute(); + } + else if (ATTRIBUTE_RECORD.name.equals(name)) { + return new StructRecordAttribute(); } else { - // unsupported attribute - return null; + return null; // unsupported attribute } - - attr.name = name; - return attr; } public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { } - - public String getName() { - return name; - } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java index 23c0486..ba2005b 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java @@ -23,7 +23,6 @@ public class StructModuleAttribute extends StructGeneralAttribute { @Override public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { int moduleNameIndex = data.readUnsignedShort(); - this.moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); this.moduleFlags = data.readUnsignedShort(); @@ -41,147 +40,87 @@ public class StructModuleAttribute extends StructGeneralAttribute { public List readRequires(DataInputFullStream data, ConstantPool pool) throws IOException { int requiresCount = data.readUnsignedShort(); - - if (requiresCount <= 0) { - return Collections.emptyList(); - } + if (requiresCount <= 0) return Collections.emptyList(); List requires = new ArrayList<>(requiresCount); for (int i = 0; i < requiresCount; i++) { int moduleNameIndex = data.readUnsignedShort(); int moduleFlags = data.readUnsignedShort(); int versionIndex = data.readUnsignedShort(); - String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); String version = versionIndex == 0 ? null : pool.getPrimitiveConstant(versionIndex).getString(); - requires.add(new RequiresEntry(moduleName, moduleFlags, version)); } - return requires; } - private List readExports(DataInputFullStream data, ConstantPool pool) throws IOException { + private static List readExports(DataInputFullStream data, ConstantPool pool) throws IOException { int exportsCount = data.readUnsignedShort(); - - if (exportsCount <= 0) { - return Collections.emptyList(); - } + if (exportsCount <= 0) return Collections.emptyList(); List exports = new ArrayList<>(exportsCount); - for (int i = 0; i < exportsCount; i++) { int packageNameIndex = data.readUnsignedShort(); int exportsFlags = data.readUnsignedShort(); - int exportsToCount = data.readUnsignedShort(); - - List exportsToModules; - if (exportsToCount > 0) { - exportsToModules = new ArrayList<>(exportsToCount); - - for (int j = 0; j < exportsToCount; j++) { - int moduleNameIndex = data.readUnsignedShort(); - String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); - - exportsToModules.add(moduleName); - } - } else { - exportsToModules = Collections.emptyList(); - } - + List exportsToModules = readStringList(data, pool); String packageName = pool.getPrimitiveConstant(packageNameIndex).getString(); - exports.add(new ExportsEntry(packageName, exportsFlags, exportsToModules)); } - return exports; } - private List readOpens(DataInputFullStream data, ConstantPool pool) throws IOException { + private static List readOpens(DataInputFullStream data, ConstantPool pool) throws IOException { int opensCount = data.readUnsignedShort(); - - if (opensCount <= 0) { - return Collections.emptyList(); - } + if (opensCount <= 0) return Collections.emptyList(); List opens = new ArrayList<>(opensCount); - for (int i = 0; i < opensCount; i++) { int packageNameIndex = data.readUnsignedShort(); int opensFlags = data.readUnsignedShort(); - int opensToCount = data.readUnsignedShort(); - - List opensToModules; - if (opensToCount > 0) { - opensToModules = new ArrayList<>(opensToCount); - - for (int j = 0; j < opensToCount; j++) { - int moduleNameIndex = data.readUnsignedShort(); - String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); - - opensToModules.add(moduleName); - } - } else { - opensToModules = Collections.emptyList(); - } - + List opensToModules = readStringList(data, pool); String packageName = pool.getPrimitiveConstant(packageNameIndex).getString(); - opens.add(new OpensEntry(packageName, opensFlags, opensToModules)); } - return opens; } - private List readUses(DataInputFullStream data, ConstantPool pool) throws IOException { - int usesCount = data.readUnsignedShort(); - if (usesCount <= 0) { - return Collections.emptyList(); - } - - List uses = new ArrayList<>(usesCount); - for (int i = 0; i < usesCount; i++) { - int classNameIndex = data.readUnsignedShort(); - String className = pool.getPrimitiveConstant(classNameIndex).getString(); - - uses.add(className); - } - - return uses; + private static List readUses(DataInputFullStream data, ConstantPool pool) throws IOException { + return readStringList(data, pool); } - private List readProvides(DataInputFullStream data, ConstantPool pool) throws IOException { + private static List readProvides(DataInputFullStream data, ConstantPool pool) throws IOException { int providesCount = data.readUnsignedShort(); - if (providesCount <= 0) { - return Collections.emptyList(); - } + if (providesCount <= 0) return Collections.emptyList(); List provides = new ArrayList<>(providesCount); for (int i = 0; i < providesCount; i++) { int interfaceNameIndex = data.readUnsignedShort(); String interfaceName = pool.getPrimitiveConstant(interfaceNameIndex).getString(); - - // Always nonzero - int providesWithCount = data.readUnsignedShort(); - List implementationNames = new ArrayList<>(providesWithCount); - - for (int j = 0; j < providesWithCount; j++) { - int classNameIndex = data.readUnsignedShort(); - String className = pool.getPrimitiveConstant(classNameIndex).getString(); - - implementationNames.add(className); - } - + List implementationNames = readStringList(data, pool); provides.add(new ProvidesEntry(interfaceName, implementationNames)); } - return provides; } + private static List readStringList(DataInputFullStream data, ConstantPool pool) throws IOException { + int count = data.readUnsignedShort(); + if (count <= 0) { + return Collections.emptyList(); + } + else { + List strings = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + int index = data.readUnsignedShort(); + strings.add(pool.getPrimitiveConstant(index).getString()); + } + return strings; + } + } + public static final class RequiresEntry { - public String moduleName; - public int moduleFlags; - public String moduleVersion; + public final String moduleName; + public final int moduleFlags; + public final String moduleVersion; public RequiresEntry(String moduleName, int moduleFlags, String moduleVersion) { this.moduleName = moduleName; @@ -191,9 +130,9 @@ public class StructModuleAttribute extends StructGeneralAttribute { } public static final class ExportsEntry { - public String packageName; - public int exportsFlags; - public List exportToModules; + public final String packageName; + public final int exportsFlags; + public final List exportToModules; public ExportsEntry(String packageName, int exportsFlags, List exportToModules) { this.packageName = packageName; @@ -203,9 +142,9 @@ public class StructModuleAttribute extends StructGeneralAttribute { } public static final class OpensEntry { - public String packageName; - public int opensFlags; - public List opensToModules; + public final String packageName; + public final int opensFlags; + public final List opensToModules; public OpensEntry(String packageName, int exportsFlags, List exportToModules) { this.packageName = packageName; @@ -215,13 +154,12 @@ public class StructModuleAttribute extends StructGeneralAttribute { } public static final class ProvidesEntry { - public String interfaceName; - public List implementationNames; + public final String interfaceName; + public final List implementationNames; public ProvidesEntry(String interfaceName, List implementationNames) { this.interfaceName = interfaceName; this.implementationNames = implementationNames; } } - -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java index fdbb271..24c6152 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java @@ -1,4 +1,4 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.struct.StructRecordComponent; @@ -22,12 +22,11 @@ public class StructRecordAttribute extends StructGeneralAttribute { List components; @Override - public void initContent(DataInputFullStream data, - ConstantPool pool) throws IOException { + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { int componentCount = data.readUnsignedShort(); StructRecordComponent[] components = new StructRecordComponent[componentCount]; for (int i = 0; i < componentCount; i++) { - components[i] = new StructRecordComponent(data, pool); + components[i] = StructRecordComponent.create(data, pool); } this.components = Arrays.asList(components); } diff --git a/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java b/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java index d90ba1d..3280038 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java @@ -212,18 +212,12 @@ public class ConstantPool implements NewClassNameBuilder { String newName = interceptor.getName(vt.value); if (newName != null) { StringBuilder buffer = new StringBuilder(); - if (vt.arrayDim > 0) { - for (int i = 0; i < vt.arrayDim; i++) { - buffer.append('['); - } - - buffer.append('L').append(newName).append(';'); + buffer.append("[".repeat(vt.arrayDim)).append('L').append(newName).append(';'); } else { buffer.append(newName); } - return buffer.toString(); } @@ -238,4 +232,4 @@ public class ConstantPool implements NewClassNameBuilder { return MethodDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this); } } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java b/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java index 63c0f8e..8b6105d 100644 --- a/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java +++ b/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java @@ -1,7 +1,8 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.lazy; import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; +import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; @@ -19,20 +20,20 @@ public class LazyLoader { this.provider = provider; } - public void addClassLink(String classname, Link link) { - mapClassLinks.put(classname, link); + public void addClassLink(String className, Link link) { + mapClassLinks.put(className, link); } - public void removeClassLink(String classname) { - mapClassLinks.remove(classname); + public void removeClassLink(String className) { + mapClassLinks.remove(className); } - public Link getClassLink(String classname) { - return mapClassLinks.get(classname); + public Link getClassLink(String className) { + return mapClassLinks.get(className); } - public ConstantPool loadPool(String classname) { - try (DataInputFullStream in = getClassStream(classname)) { + public ConstantPool loadPool(String className) { + try (DataInputFullStream in = getClassStream(className)) { if (in != null) { in.discard(8); return new ConstantPool(in); @@ -45,14 +46,14 @@ public class LazyLoader { } } - public byte[] loadBytecode(StructMethod mt, int codeFullLength) { - String className = mt.getClassStruct().qualifiedName; + public byte[] loadBytecode(StructClass classStruct, StructMethod mt, int codeFullLength) { + String className = classStruct.qualifiedName; try (DataInputFullStream in = getClassStream(className)) { if (in != null) { in.discard(8); - ConstantPool pool = mt.getClassStruct().getPool(); + ConstantPool pool = classStruct.getPool(); if (pool == null) { pool = new ConstantPool(in); } @@ -90,7 +91,7 @@ public class LazyLoader { for (int j = 0; j < attrSize; j++) { int attrNameIndex = in.readUnsignedShort(); String attrName = pool.getPrimitiveConstant(attrNameIndex).getString(); - if (!StructGeneralAttribute.ATTRIBUTE_CODE.getName().equals(attrName)) { + if (!StructGeneralAttribute.ATTRIBUTE_CODE.name.equals(attrName)) { in.discard(in.readInt()); continue; } @@ -138,4 +139,4 @@ public class LazyLoader { this.internalPath = internalPath; } } -} \ No newline at end of file +} diff --git a/testData/classes/java9/module-info.class b/testData/classes/java9/module-info.class index 59005e4..7f4d090 100644 Binary files a/testData/classes/java9/module-info.class and b/testData/classes/java9/module-info.class differ diff --git a/testData/results/module-info.dec b/testData/results/module-info.dec index c45d666..73de684 100644 --- a/testData/results/module-info.dec +++ b/testData/results/module-info.dec @@ -1,13 +1,14 @@ +@Deprecated module sample.module { requires java.base; - exports test; - exports test2 to + exports sample.pkg1; + exports sample.pkg2 to java.base; - opens test; - opens test2 to + opens sample.pkg1; + opens sample.pkg2 to java.base; uses java.util.spi.ToolProvider; - provides test.TestService with - test.TestServiceImpl; + provides sample.pkg1.TestService with + sample.pkg1.TestServiceImpl; } diff --git a/testData/src/java9/sample.module/TestClass.java b/testData/src/java9/sample.module/TestClass.java new file mode 100644 index 0000000..0a858dd --- /dev/null +++ b/testData/src/java9/sample.module/TestClass.java @@ -0,0 +1,3 @@ +package sample.pkg2; + +public class TestClass {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/TestService.java b/testData/src/java9/sample.module/TestService.java new file mode 100644 index 0000000..d73510f --- /dev/null +++ b/testData/src/java9/sample.module/TestService.java @@ -0,0 +1,3 @@ +package sample.pkg1; + +public interface TestService {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/TestServiceImpl.java b/testData/src/java9/sample.module/TestServiceImpl.java new file mode 100644 index 0000000..0463d99 --- /dev/null +++ b/testData/src/java9/sample.module/TestServiceImpl.java @@ -0,0 +1,3 @@ +package sample.pkg1; + +public class TestServiceImpl implements TestService {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/module-info.java b/testData/src/java9/sample.module/module-info.java index ea71a46..82d5266 100644 --- a/testData/src/java9/sample.module/module-info.java +++ b/testData/src/java9/sample.module/module-info.java @@ -1,15 +1,16 @@ +@Deprecated module sample.module { requires java.base; uses java.util.spi.ToolProvider; - provides test.TestService with test.TestServiceImpl; + provides sample.pkg1.TestService with sample.pkg1.TestServiceImpl; - exports test; + exports sample.pkg1; - exports test2 to java.base; + exports sample.pkg2 to java.base; - opens test; + opens sample.pkg1; - opens test2 to java.base; -} \ No newline at end of file + opens sample.pkg2 to java.base; +} diff --git a/testData/src/java9/sample.module/test/TestService.java b/testData/src/java9/sample.module/test/TestService.java deleted file mode 100644 index c2a6cd7..0000000 --- a/testData/src/java9/sample.module/test/TestService.java +++ /dev/null @@ -1,3 +0,0 @@ -package test; - -public interface TestService {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/test/TestServiceImpl.java b/testData/src/java9/sample.module/test/TestServiceImpl.java deleted file mode 100644 index 9a153e8..0000000 --- a/testData/src/java9/sample.module/test/TestServiceImpl.java +++ /dev/null @@ -1,3 +0,0 @@ -package test; - -public class TestServiceImpl implements TestService {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/test2/TestClass.java b/testData/src/java9/sample.module/test2/TestClass.java deleted file mode 100644 index 202153e..0000000 --- a/testData/src/java9/sample.module/test2/TestClass.java +++ /dev/null @@ -1,3 +0,0 @@ -package test2; - -public class TestClass {} \ No newline at end of file