[java decompiler] minor refactoring + cleanup (duplicates; dependencies; test data layout; typos; formatting)

GitOrigin-RevId: 3589e4d8f2dfa8a5096fcf49070bc65ba6734482
master
Roman Shevchenko 4 years ago committed by intellij-monorepo-bot
parent f259b38c72
commit f40b96ebcf
  1. 11
      src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java
  2. 194
      src/org/jetbrains/java/decompiler/main/ClassWriter.java
  3. 4
      src/org/jetbrains/java/decompiler/main/ClassesProcessor.java
  4. 6
      src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java
  5. 17
      src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java
  6. 21
      src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java
  7. 25
      src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java
  8. 5
      src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java
  9. 10
      src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java
  10. 4
      src/org/jetbrains/java/decompiler/struct/ContextUnit.java
  11. 112
      src/org/jetbrains/java/decompiler/struct/StructClass.java
  12. 7
      src/org/jetbrains/java/decompiler/struct/StructContext.java
  13. 33
      src/org/jetbrains/java/decompiler/struct/StructField.java
  14. 51
      src/org/jetbrains/java/decompiler/struct/StructMember.java
  15. 116
      src/org/jetbrains/java/decompiler/struct/StructMethod.java
  16. 33
      src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java
  17. 43
      src/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java
  18. 94
      src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java
  19. 138
      src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java
  20. 7
      src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java
  21. 8
      src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java
  22. 27
      src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java
  23. BIN
      testData/classes/java9/module-info.class
  24. 13
      testData/results/module-info.dec
  25. 3
      testData/src/java9/sample.module/TestClass.java
  26. 3
      testData/src/java9/sample.module/TestService.java
  27. 3
      testData/src/java9/sample.module/TestServiceImpl.java
  28. 11
      testData/src/java9/sample.module/module-info.java
  29. 3
      testData/src/java9/sample.module/test/TestService.java
  30. 3
      testData/src/java9/sample.module/test/TestServiceImpl.java
  31. 3
      testData/src/java9/sample.module/test2/TestClass.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; package org.jetbrains.java.decompiler.code.cfg;
import org.jetbrains.java.decompiler.code.*; import org.jetbrains.java.decompiler.code.*;
import org.jetbrains.java.decompiler.code.interpreter.InstructionImpact; import org.jetbrains.java.decompiler.code.interpreter.InstructionImpact;
import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper; 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.StructMethod;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.struct.gen.DataPoint; import org.jetbrains.java.decompiler.struct.gen.DataPoint;
@ -96,9 +97,9 @@ public class ControlFlowGraph implements CodeConstants {
return buf.toString(); return buf.toString();
} }
public void inlineJsr(StructMethod mt) { public void inlineJsr(StructClass cl, StructMethod mt) {
processJsr(); processJsr();
removeJsr(mt); removeJsr(cl, mt);
removeMarkers(); removeMarkers();
@ -668,8 +669,8 @@ public class ControlFlowGraph implements CodeConstants {
} }
} }
private void removeJsr(StructMethod mt) { private void removeJsr(StructClass cl, StructMethod mt) {
removeJsrInstructions(mt.getClassStruct().getPool(), first, DataPoint.getInitialDataPoint(mt)); removeJsrInstructions(cl.getPool(), first, DataPoint.getInitialDataPoint(mt));
} }
private static void removeJsrInstructions(ConstantPool pool, BasicBlock block, DataPoint data) { private static void removeJsrInstructions(ConstantPool pool, BasicBlock block, DataPoint data) {

@ -41,7 +41,7 @@ public class ClassWriter {
InitializerProcessor.extractInitializers(wrapper); InitializerProcessor.extractInitializers(wrapper);
if (node.type == ClassNode.CLASS_ROOT && if (node.type == ClassNode.CLASS_ROOT &&
!cl.isVersionGE_1_5() && !cl.isVersion5() &&
DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) { DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) {
ClassReference14Processor.processClassReferences(node); ClassReference14Processor.processClassReferences(node);
} }
@ -249,10 +249,11 @@ public class ClassWriter {
} }
} }
boolean isModuleInfo = cl.hasModifier(CodeConstants.ACC_MODULE) && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); if (cl.hasModifier(CodeConstants.ACC_MODULE)) {
StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE);
if (isModuleInfo) { if (moduleAttribute != null) {
writeModuleInfoBody(buffer, cl); writeModuleInfoBody(buffer, moduleAttribute);
}
} }
buffer.appendIndent(indent).append('}'); buffer.appendIndent(indent).append('}');
@ -268,111 +269,61 @@ public class ClassWriter {
DecompilerContext.getLogger().endWriteClass(); DecompilerContext.getLogger().endWriteClass();
} }
private static boolean isSyntheticRecordMethod(StructMethod mt, TextBuffer code) { private static boolean isSyntheticRecordMethod(StructClass cl, StructMethod mt, TextBuffer code) {
if (mt.getClassStruct().getRecordComponents() == null) return false; if (cl.getRecordComponents() != null) {
String name = mt.getName(); String name = mt.getName(), descriptor = mt.getDescriptor();
String descriptor = mt.getDescriptor(); //noinspection SpellCheckingInspection
if (name.equals("equals") && descriptor.equals("(Ljava/lang/Object;)Z") || if (name.equals("equals") && descriptor.equals("(Ljava/lang/Object;)Z") ||
name.equals("hashCode") && descriptor.equals("()I") || name.equals("hashCode") && descriptor.equals("()I") ||
name.equals("toString") && descriptor.equals("()Ljava/lang/String;")) { name.equals("toString") && descriptor.equals("()Ljava/lang/String;")) {
if (code.countLines() == 1) { if (code.countLines() == 1) {
String str = code.toString().trim(); String str = code.toString().trim();
return str.startsWith("return this." + name + "<invokedynamic>(this"); return str.startsWith("return this." + name + "<invokedynamic>(this");
}
} }
} }
return false; return false;
} }
private void writeModuleInfoBody(TextBuffer buffer, StructClass cl) { private static void writeModuleInfoBody(TextBuffer buffer, StructModuleAttribute moduleAttribute) {
StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE);
for (StructModuleAttribute.RequiresEntry requires : moduleAttribute.requires) { for (StructModuleAttribute.RequiresEntry requires : moduleAttribute.requires) {
String moduleName = requires.moduleName.replace('/', '.'); buffer.appendIndent(1).append("requires ").append(requires.moduleName.replace('/', '.')).append(';').appendLineSeparator();
buffer.appendIndent(1)
.append("requires ")
.append(moduleName)
.append(';')
.appendLineSeparator();
} }
for (StructModuleAttribute.ExportsEntry exports : moduleAttribute.exports) { for (StructModuleAttribute.ExportsEntry exports : moduleAttribute.exports) {
String packageName = exports.packageName.replace('/', '.'); buffer.appendIndent(1).append("exports ").append(exports.packageName.replace('/', '.'));
buffer.appendIndent(1).append("exports ").append(packageName);
List<String> exportToModules = exports.exportToModules; List<String> exportToModules = exports.exportToModules;
if (exportToModules.size() > 0) { if (exportToModules.size() > 0) {
buffer.append(" to").appendLineSeparator(); buffer.append(" to").appendLineSeparator();
appendFQClassNames(buffer, exportToModules);
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();
} }
buffer.append(';').appendLineSeparator();
} }
for (StructModuleAttribute.OpensEntry opens : moduleAttribute.opens) { for (StructModuleAttribute.OpensEntry opens : moduleAttribute.opens) {
String packageName = opens.packageName.replace('/', '.'); buffer.appendIndent(1).append("opens ").append(opens.packageName.replace('/', '.'));
buffer.appendIndent(1).append("opens ").append(packageName);
List<String> opensToModules = opens.opensToModules; List<String> opensToModules = opens.opensToModules;
if (opensToModules.size() > 0) { if (opensToModules.size() > 0) {
buffer.append(" to").appendLineSeparator(); buffer.append(" to").appendLineSeparator();
appendFQClassNames(buffer, opensToModules);
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();
} }
buffer.append(';').appendLineSeparator();
} }
for (String uses : moduleAttribute.uses) { for (String uses : moduleAttribute.uses) {
String className = ExprProcessor.buildJavaClassName(uses); buffer.appendIndent(1).append("uses ").append(ExprProcessor.buildJavaClassName(uses)).append(';').appendLineSeparator();
buffer.appendIndent(1)
.append("uses ")
.append(className)
.append(';')
.appendLineSeparator();
} }
for (StructModuleAttribute.ProvidesEntry provides : moduleAttribute.provides) { for (StructModuleAttribute.ProvidesEntry provides : moduleAttribute.provides) {
String interfaceName = ExprProcessor.buildJavaClassName(provides.interfaceName); buffer.appendIndent(1).append("provides ").append(ExprProcessor.buildJavaClassName(provides.interfaceName)).append(" with").appendLineSeparator();
@SuppressWarnings({"SSBasedInspection", "RedundantSuppression"}) List<String> javaNames =
buffer.appendIndent(1) provides.implementationNames.stream().map(ExprProcessor::buildJavaClassName).collect(Collectors.toList());
.append("provides ") appendFQClassNames(buffer, javaNames);
.append(interfaceName) buffer.append(';').appendLineSeparator();
.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();
}
} }
} }
@ -519,11 +470,11 @@ public class ClassWriter {
buffer.append('{').appendLineSeparator(); buffer.append('{').appendLineSeparator();
} }
private boolean isVarArgRecord(StructClass cl) { private static boolean isVarArgRecord(StructClass cl) {
String canonicalConstructorDescriptor = String canonicalConstructorDescriptor =
cl.getRecordComponents().stream().map(c -> c.getDescriptor()).collect(Collectors.joining("", "(", ")V")); cl.getRecordComponents().stream().map(c -> c.getDescriptor()).collect(Collectors.joining("", "(", ")V"));
StructMethod ctor = cl.getMethod(CodeConstants.INIT_NAME, canonicalConstructorDescriptor); StructMethod init = cl.getMethod(CodeConstants.INIT_NAME, canonicalConstructorDescriptor);
return ctor != null && ctor.hasModifier(CodeConstants.ACC_VARARGS); return init != null && init.hasModifier(CodeConstants.ACC_VARARGS);
} }
private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) { 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); appendModifiers(buffer, fd.getAccessFlags(), FIELD_ALLOWED, isInterface, FIELD_EXCLUDED);
} }
VarType fieldType = new VarType(fd.getDescriptor(), false); Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(fd);
VarType fieldType = fieldTypeData.getKey();
GenericFieldDescriptor descriptor = null; GenericFieldDescriptor descriptor = fieldTypeData.getValue();
if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
StructGenericSignatureAttribute attr = fd.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE);
if (attr != null) {
descriptor = GenericMain.parseFieldSignature(attr.getSignature());
}
}
if (!isEnum) { if (!isEnum) {
if (descriptor != null) { if (descriptor != null) {
@ -619,15 +564,9 @@ public class ClassWriter {
private static void recordComponentToJava(StructRecordComponent cd, TextBuffer buffer, boolean varArgComponent) { private static void recordComponentToJava(StructRecordComponent cd, TextBuffer buffer, boolean varArgComponent) {
appendAnnotations(buffer, -1, cd, TypeAnnotation.FIELD); appendAnnotations(buffer, -1, cd, TypeAnnotation.FIELD);
VarType fieldType = new VarType(cd.getDescriptor(), false); Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(cd);
VarType fieldType = fieldTypeData.getKey();
GenericFieldDescriptor descriptor = null; GenericFieldDescriptor descriptor = fieldTypeData.getValue();
if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
StructGenericSignatureAttribute attr = cd.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE);
if (attr != null) {
descriptor = GenericMain.parseFieldSignature(attr.getSignature());
}
}
if (descriptor != null) { if (descriptor != null) {
buffer.append(GenericMain.getGenericCastTypeName(varArgComponent ? descriptor.type.decreaseArrayDim() : descriptor.type)); 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 isAnnotation = cl.hasModifier(CodeConstants.ACC_ANNOTATION);
boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
boolean isDeprecated = mt.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED); 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()); MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
@ -815,7 +754,7 @@ public class ClassWriter {
if (CodeConstants.INIT_NAME.equals(name)) { if (CodeConstants.INIT_NAME.equals(name)) {
if (node.type == ClassNode.CLASS_ANONYMOUS) { if (node.type == ClassNode.CLASS_ANONYMOUS) {
name = ""; name = "";
dinit = true; dInit = true;
} }
else { else {
name = node.simpleName; name = node.simpleName;
@ -824,7 +763,7 @@ public class ClassWriter {
} }
else if (CodeConstants.CLINIT_NAME.equals(name)) { else if (CodeConstants.CLINIT_NAME.equals(name)) {
name = ""; name = "";
clinit = true; clInit = true;
} }
GenericMethodDescriptor descriptor = null; GenericMethodDescriptor descriptor = null;
@ -853,7 +792,7 @@ public class ClassWriter {
boolean throwsExceptions = false; boolean throwsExceptions = false;
int paramCount = 0; int paramCount = 0;
if (!clinit && !dinit) { if (!clInit && !dInit) {
boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC); boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC);
if (descriptor != null && !descriptor.typeParameters.isEmpty()) { if (descriptor != null && !descriptor.typeParameters.isEmpty()) {
@ -992,7 +931,7 @@ public class ClassWriter {
buffer.appendLineSeparator(); buffer.appendLineSeparator();
} }
else { else {
if (!clinit && !dinit) { if (!clInit && !dInit) {
buffer.append(' '); buffer.append(' ');
} }
@ -1008,8 +947,8 @@ public class ClassWriter {
BytecodeMappingTracer codeTracer = new BytecodeMappingTracer(tracer.getCurrentSourceLine()); BytecodeMappingTracer codeTracer = new BytecodeMappingTracer(tracer.getCurrentSourceLine());
TextBuffer code = root.toJava(indent + 1, codeTracer); TextBuffer code = root.toJava(indent + 1, codeTracer);
hideMethod = (code.length() == 0) && (clinit || dinit || hideConstructor(node, init, throwsExceptions, paramCount, flags)) hideMethod = code.length() == 0 && (clInit || dInit || hideConstructor(node, init, throwsExceptions, paramCount, flags)) ||
|| isSyntheticRecordMethod(mt, code); isSyntheticRecordMethod(cl, mt, code);
buffer.append(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) { 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)) { if (!init || throwsExceptions || paramCount > 0 || !DecompilerContext.getOption(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR)) {
return false; return false;
} }
@ -1057,11 +995,11 @@ public class ClassWriter {
ClassWrapper wrapper = node.getWrapper(); ClassWrapper wrapper = node.getWrapper();
StructClass cl = wrapper.getClassStruct(); 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); 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 // 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; return false;
} }
@ -1077,6 +1015,20 @@ public class ClassWriter {
return true; return true;
} }
private static Map.Entry<VarType, GenericFieldDescriptor> 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) { private static void appendDeprecation(TextBuffer buffer, int indent) {
buffer.appendIndent(indent).append("/** @deprecated */").appendLineSeparator(); buffer.appendIndent(indent).append("/** @deprecated */").appendLineSeparator();
} }
@ -1135,11 +1087,11 @@ public class ClassWriter {
buffer.appendIndent(indent).append("// $FF: ").append(comment).appendLineSeparator(); 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}; 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}; 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}; StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS};
private static void appendAnnotations(TextBuffer buffer, int indent, StructMember mb, int targetType) { private static void appendAnnotations(TextBuffer buffer, int indent, StructMember mb, int targetType) {
@ -1283,4 +1235,14 @@ public class ClassWriter {
buffer.append('>'); buffer.append('>');
} }
private static void appendFQClassNames(TextBuffer buffer, List<String> 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();
}
}
}
} }

@ -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; package org.jetbrains.java.decompiler.main;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -245,7 +245,7 @@ public class ClassesProcessor implements CodeConstants {
} }
try { try {
mt.expandData(); mt.expandData(enclosingCl);
InstructionSequence seq = mt.getInstructionSequence(); InstructionSequence seq = mt.getInstructionSequence();
if (seq != null) { if (seq != null) {

@ -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; package org.jetbrains.java.decompiler.main.rels;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -59,10 +59,10 @@ public class ClassWrapper {
try { try {
if (mt.containsCode()) { if (mt.containsCode()) {
if (maxSec == 0 || testMode) { if (maxSec == 0 || testMode) {
root = MethodProcessorRunnable.codeToJava(mt, md, varProc); root = MethodProcessorRunnable.codeToJava(classStruct, mt, md, varProc);
} }
else { 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"); Thread mtThread = new Thread(mtProc, "Java decompiler");
long stopAt = System.currentTimeMillis() + maxSec * 1000L; long stopAt = System.currentTimeMillis() + maxSec * 1000L;

@ -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; package org.jetbrains.java.decompiler.main.rels;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -33,17 +33,16 @@ public class LambdaProcessor {
ClassesProcessor clProcessor = DecompilerContext.getClassProcessor(); ClassesProcessor clProcessor = DecompilerContext.getClassProcessor();
StructClass cl = node.classStruct; 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; return;
} }
StructBootstrapMethodsAttribute bootstrap = StructBootstrapMethodsAttribute bootstrap = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
if (bootstrap == null || bootstrap.getMethodsNumber() == 0) { if (bootstrap == null || bootstrap.getMethodsNumber() == 0) {
return; // no bootstrap constants in pool return; // no bootstrap constants in pool
} }
BitSet lambda_methods = new BitSet(); BitSet lambdaMethods = new BitSet();
// find lambda bootstrap constants // find lambda bootstrap constants
for (int i = 0; i < bootstrap.getMethodsNumber(); ++i) { for (int i = 0; i < bootstrap.getMethodsNumber(); ++i) {
@ -52,11 +51,11 @@ public class LambdaProcessor {
// FIXME: extend for Eclipse etc. at some point // FIXME: extend for Eclipse etc. at some point
if (JAVAC_LAMBDA_CLASS.equals(method_ref.classname) && if (JAVAC_LAMBDA_CLASS.equals(method_ref.classname) &&
(JAVAC_LAMBDA_METHOD.equals(method_ref.elementname) || JAVAC_LAMBDA_ALT_METHOD.equals(method_ref.elementname))) { (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 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. // iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes.
for (StructMethod mt : cl.getMethods()) { for (StructMethod mt : cl.getMethods()) {
mt.expandData(); mt.expandData(cl);
InstructionSequence seq = mt.getInstructionSequence(); InstructionSequence seq = mt.getInstructionSequence();
if (seq != null && seq.length() > 0) { if (seq != null && seq.length() > 0) {
@ -76,7 +75,7 @@ public class LambdaProcessor {
if (instr.opcode == CodeConstants.opc_invokedynamic) { if (instr.opcode == CodeConstants.opc_invokedynamic) {
LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.operand(0)); 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<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1); List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1);
MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor); MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor);

@ -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; package org.jetbrains.java.decompiler.main.rels;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -22,6 +22,7 @@ import java.io.IOException;
public class MethodProcessorRunnable implements Runnable { public class MethodProcessorRunnable implements Runnable {
public final Object lock = new Object(); public final Object lock = new Object();
private final StructClass klass;
private final StructMethod method; private final StructMethod method;
private final MethodDescriptor methodDescriptor; private final MethodDescriptor methodDescriptor;
private final VarProcessor varProc; private final VarProcessor varProc;
@ -31,10 +32,12 @@ public class MethodProcessorRunnable implements Runnable {
private volatile Throwable error; private volatile Throwable error;
private volatile boolean finished = false; private volatile boolean finished = false;
public MethodProcessorRunnable(StructMethod method, public MethodProcessorRunnable(StructClass klass,
StructMethod method,
MethodDescriptor methodDescriptor, MethodDescriptor methodDescriptor,
VarProcessor varProc, VarProcessor varProc,
DecompilerContext parentContext) { DecompilerContext parentContext) {
this.klass = klass;
this.method = method; this.method = method;
this.methodDescriptor = methodDescriptor; this.methodDescriptor = methodDescriptor;
this.varProc = varProc; this.varProc = varProc;
@ -48,7 +51,7 @@ public class MethodProcessorRunnable implements Runnable {
try { try {
DecompilerContext.setCurrentContext(parentContext); DecompilerContext.setCurrentContext(parentContext);
root = codeToJava(method, methodDescriptor, varProc); root = codeToJava(klass, method, methodDescriptor, varProc);
} }
catch (Throwable t) { catch (Throwable t) {
error = t; error = t;
@ -63,17 +66,15 @@ public class MethodProcessorRunnable implements Runnable {
} }
} }
public static RootStatement codeToJava(StructMethod mt, MethodDescriptor md, VarProcessor varProc) throws IOException { public static RootStatement codeToJava(StructClass cl, StructMethod mt, MethodDescriptor md, VarProcessor varProc) throws IOException {
StructClass cl = mt.getClassStruct();
boolean isInitializer = CodeConstants.CLINIT_NAME.equals(mt.getName()); // for now static initializer only boolean isInitializer = CodeConstants.CLINIT_NAME.equals(mt.getName()); // for now static initializer only
mt.expandData(); mt.expandData(cl);
InstructionSequence seq = mt.getInstructionSequence(); InstructionSequence seq = mt.getInstructionSequence();
ControlFlowGraph graph = new ControlFlowGraph(seq); ControlFlowGraph graph = new ControlFlowGraph(seq);
DeadCodeHelper.removeDeadBlocks(graph); DeadCodeHelper.removeDeadBlocks(graph);
graph.inlineJsr(mt); graph.inlineJsr(cl, mt);
// TODO: move to the start, before jsr inlining // TODO: move to the start, before jsr inlining
DeadCodeHelper.connectDummyExitBlock(graph); DeadCodeHelper.connectDummyExitBlock(graph);
@ -110,13 +111,13 @@ public class MethodProcessorRunnable implements Runnable {
if (!ExceptionDeobfuscator.handleMultipleEntryExceptionRanges(graph)) { if (!ExceptionDeobfuscator.handleMultipleEntryExceptionRanges(graph)) {
DecompilerContext.getLogger().writeMessage("Found multiple entry exception ranges which could not be splitted", IFernflowerLogger.Severity.WARN); 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); RootStatement root = DomHelper.parseGraph(graph);
FinallyProcessor fProc = new FinallyProcessor(md, varProc); FinallyProcessor fProc = new FinallyProcessor(md, varProc);
while (fProc.iterateGraph(mt, root, graph)) { while (fProc.iterateGraph(cl, mt, root, graph)) {
root = DomHelper.parseGraph(graph); root = DomHelper.parseGraph(graph);
} }

@ -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; package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.code.CodeConstants; 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.stats.Statement;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; 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.StructMethod;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.struct.gen.VarType;
@ -46,12 +47,8 @@ public class FinallyProcessor {
varProcessor = varProc; varProcessor = varProc;
} }
public boolean iterateGraph(StructMethod mt, RootStatement root, ControlFlowGraph graph) { public boolean iterateGraph(StructClass cl, StructMethod mt, RootStatement root, ControlFlowGraph graph) {
return processStatementEx(mt, root, graph); int bytecodeVersion = mt.getBytecodeVersion();
}
private boolean processStatementEx(StructMethod mt, RootStatement root, ControlFlowGraph graph) {
int bytecode_version = mt.getClassStruct().getBytecodeVersion();
LinkedList<Statement> stack = new LinkedList<>(); LinkedList<Statement> stack = new LinkedList<>();
stack.add(root); stack.add(root);
@ -77,22 +74,20 @@ public class FinallyProcessor {
fin.setMonitor(var == null ? null : new VarExprent(var, VarType.VARTYPE_INT, varProcessor)); fin.setMonitor(var == null ? null : new VarExprent(var, VarType.VARTYPE_INT, varProcessor));
} }
else { else {
Record inf = getFinallyInformation(mt, root, fin); Record inf = getFinallyInformation(cl, mt, root, fin);
if (inf == null) { // inconsistent finally if (inf == null) { // inconsistent finally
catchallBlockIDs.put(handler.id, null); catchallBlockIDs.put(handler.id, null);
} }
else { else {
if (DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) { if (DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) {
finallyBlockIDs.put(handler.id, null); finallyBlockIDs.put(handler.id, null);
} }
else { 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); finallyBlockIDs.put(handler.id, varIndex);
insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf, bytecode_version);
finallyBlockIDs.put(handler.id, varindex);
} }
DeadCodeHelper.removeDeadBlocks(graph); // e.g. multiple return blocks after a nested finally 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<BasicBlock, Boolean> mapLast = new HashMap<>(); Map<BasicBlock, Boolean> mapLast = new HashMap<>();
BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead(); BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead();
@ -138,7 +133,7 @@ public class FinallyProcessor {
} }
ExprProcessor proc = new ExprProcessor(methodDescriptor, varProcessor); ExprProcessor proc = new ExprProcessor(methodDescriptor, varProcessor);
proc.processStatement(root, mt.getClassStruct()); proc.processStatement(root, cl);
SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();
ssa.splitVariables(root, mt); ssa.splitVariables(root, mt);

@ -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; package org.jetbrains.java.decompiler.modules.decompiler.exps;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -30,7 +30,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class VarExprent extends Exprent { public class VarExprent extends Exprent {
public static final int STACK_BASE = 10000; public static final int STACK_BASE = 10000;
public static final String VAR_NAMELESS_ENCLOSURE = "<VAR_NAMELESS_ENCLOSURE>"; public static final String VAR_NAMELESS_ENCLOSURE = "<VAR_NAMELESS_ENCLOSURE>";
@ -122,7 +121,7 @@ public class VarExprent extends Exprent {
Integer origIndex = processor.getVarOriginalIndex(index); Integer origIndex = processor.getVarOriginalIndex(index);
if (origIndex != null) { if (origIndex != null) {
String name = attr.getName(origIndex, visibleOffset); 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; return name;
} }
} }

@ -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; package org.jetbrains.java.decompiler.modules.decompiler.vars;
import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
@ -55,7 +55,7 @@ public class VarProcessor {
Integer index = mapOriginalVarIndices.get(pair.var); Integer index = mapOriginalVarIndices.get(pair.var);
if (index != null) { if (index != null) {
String debugName = mapDebugVarNames.get(index); String debugName = mapDebugVarNames.get(index);
if (debugName != null && TextUtil.isValidIdentifier(debugName, method.getClassStruct().getBytecodeVersion())) { if (debugName != null && TextUtil.isValidIdentifier(debugName, method.getBytecodeVersion())) {
name = debugName; name = debugName;
} }
} }
@ -72,11 +72,7 @@ public class VarProcessor {
} }
public Integer getVarOriginalIndex(int index) { public Integer getVarOriginalIndex(int index) {
if (varVersions == null) { return varVersions == null ? null : varVersions.getMapOriginalVarIndices().get(index);
return null;
}
return varVersions.getMapOriginalVarIndices().get(index);
} }
public void refreshVarNames(VarNamesCollector vc) { public void refreshVarNames(VarNamesCollector vc) {

@ -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; package org.jetbrains.java.decompiler.struct;
import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.DecompilerContext;
@ -65,7 +65,7 @@ public class ContextUnit {
StructClass newCl; StructClass newCl;
try (DataInputFullStream in = loader.getClassStream(oldName)) { try (DataInputFullStream in = loader.getClassStream(oldName)) {
newCl = new StructClass(in, cl.isOwn(), loader); newCl = StructClass.create(in, cl.isOwn(), loader);
} }
lstClasses.add(newCl); lstClasses.add(newCl);

@ -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; package org.jetbrains.java.decompiler.struct;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -13,6 +13,7 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map;
/* /*
class_file { class_file {
@ -35,71 +36,86 @@ import java.util.List;
} }
*/ */
public class StructClass extends StructMember { public class StructClass extends StructMember {
public static StructClass create(DataInputFullStream in, boolean own, LazyLoader loader) throws IOException {
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<StructField, String> fields;
private final VBStyleCollection<StructMethod, String> 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;
in.discard(4); in.discard(4);
int minorVersion = in.readUnsignedShort();
int majorVersion = in.readUnsignedShort();
int bytecodeVersion = Math.max(majorVersion, CodeConstants.BYTECODE_JAVA_LE_4);
minorVersion = in.readUnsignedShort(); ConstantPool pool = new ConstantPool(in);
majorVersion = in.readUnsignedShort();
pool = new ConstantPool(in); int accessFlags = in.readUnsignedShort();
accessFlags = in.readUnsignedShort();
int thisClassIdx = in.readUnsignedShort(); int thisClassIdx = in.readUnsignedShort();
int superClassIdx = in.readUnsignedShort(); int superClassIdx = in.readUnsignedShort();
qualifiedName = pool.getPrimitiveConstant(thisClassIdx).getString(); String qualifiedName = pool.getPrimitiveConstant(thisClassIdx).getString();
superClass = pool.getPrimitiveConstant(superClassIdx); PrimitiveConstant superClass = pool.getPrimitiveConstant(superClassIdx);
// interfaces
int length = in.readUnsignedShort(); int length = in.readUnsignedShort();
interfaces = new int[length]; int[] interfaces = new int[length];
interfaceNames = new String[length]; String[] interfaceNames = new String[length];
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
interfaces[i] = in.readUnsignedShort(); interfaces[i] = in.readUnsignedShort();
interfaceNames[i] = pool.getPrimitiveConstant(interfaces[i]).getString(); interfaceNames[i] = pool.getPrimitiveConstant(interfaces[i]).getString();
} }
// fields
length = in.readUnsignedShort(); length = in.readUnsignedShort();
fields = new VBStyleCollection<>(length); VBStyleCollection<StructField, String>fields = new VBStyleCollection<>(length);
for (int i = 0; i < length; i++) { 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())); fields.addWithKey(field, InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor()));
} }
// methods
length = in.readUnsignedShort(); length = in.readUnsignedShort();
methods = new VBStyleCollection<>(length); VBStyleCollection<StructMethod, String>methods = new VBStyleCollection<>(length);
for (int i = 0; i < length; i++) { 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())); methods.addWithKey(method, InterpreterUtil.makeUniqueKey(method.getName(), method.getDescriptor()));
} }
// attributes Map<String, StructGeneralAttribute> attributes = readAttributes(in, pool);
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<StructField, String> fields;
private final VBStyleCollection<StructMethod, String> methods;
private ConstantPool pool;
releaseResources(); private StructClass(int accessFlags,
Map<String, StructGeneralAttribute> attributes,
String qualifiedName,
PrimitiveConstant superClass,
boolean own,
LazyLoader loader,
int minorVersion,
int majorVersion,
int[] interfaces,
String[] interfaceNames,
VBStyleCollection<StructField, String> fields,
VBStyleCollection<StructMethod, String> 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) { public boolean hasField(String name, String descriptor) {
@ -168,17 +184,13 @@ public class StructClass extends StructMember {
return loader; return loader;
} }
public boolean isVersionGE_1_5() { public boolean isVersion5() {
return (majorVersion > CodeConstants.BYTECODE_JAVA_LE_4 || return (majorVersion > CodeConstants.BYTECODE_JAVA_LE_4 ||
(majorVersion == CodeConstants.BYTECODE_JAVA_LE_4 && minorVersion > 0)); // FIXME: check second condition (majorVersion == CodeConstants.BYTECODE_JAVA_LE_4 && minorVersion > 0)); // FIXME: check second condition
} }
public boolean isVersionGE_1_7() { public boolean isVersion8() {
return (majorVersion >= CodeConstants.BYTECODE_JAVA_7); return majorVersion >= CodeConstants.BYTECODE_JAVA_8;
}
public int getBytecodeVersion() {
return Math.max(majorVersion, CodeConstants.BYTECODE_JAVA_LE_4);
} }
@Override @Override

@ -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; package org.jetbrains.java.decompiler.struct;
import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.DecompilerContext;
@ -17,7 +17,6 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
public class StructContext { public class StructContext {
private final IResultSaver saver; private final IResultSaver saver;
private final IDecompiledData decompiledData; private final IDecompiledData decompiledData;
private final LazyLoader loader; private final LazyLoader loader;
@ -106,7 +105,7 @@ public class StructContext {
if (filename.endsWith(".class")) { if (filename.endsWith(".class")) {
try (DataInputFullStream in = loader.getClassStream(file.getAbsolutePath(), null)) { 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); classes.put(cl.qualifiedName, cl);
unit.addClass(cl, filename); unit.addClass(cl, filename);
loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), null)); loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), null));
@ -141,7 +140,7 @@ public class StructContext {
if (!entry.isDirectory()) { if (!entry.isDirectory()) {
if (name.endsWith(".class")) { if (name.endsWith(".class")) {
byte[] bytes = InterpreterUtil.getBytes(archive, entry); 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); classes.put(cl.qualifiedName, cl);
unit.addClass(cl, name); unit.addClass(cl, name);
loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), name)); loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), name));

@ -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; 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.ConstantPool;
import org.jetbrains.java.decompiler.util.DataInputFullStream; import org.jetbrains.java.decompiler.util.DataInputFullStream;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
/* /*
field_info { field_info {
@ -16,29 +18,32 @@ import java.io.IOException;
} }
*/ */
public class StructField extends StructMember { 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; String[] values = pool.getClassElement(ConstantPool.FIELD, clQualifiedName, nameIndex, descriptorIndex);
private final String descriptor;
Map<String, StructGeneralAttribute> attributes = readAttributes(in, pool);
public StructField(DataInputFullStream in, StructClass clStruct) throws IOException { return new StructField(accessFlags, attributes, values[0], values[1]);
accessFlags = in.readUnsignedShort(); }
int nameIndex = in.readUnsignedShort();
int descriptorIndex = in.readUnsignedShort();
ConstantPool pool = clStruct.getPool(); private final String name;
String[] values = pool.getClassElement(ConstantPool.FIELD, clStruct.qualifiedName, nameIndex, descriptorIndex); private final String descriptor;
name = values[0];
descriptor = values[1];
attributes = readAttributes(in, pool); protected StructField(int accessFlags, Map<String, StructGeneralAttribute> attributes, String name, String descriptor) {
super(accessFlags, attributes);
this.name = name;
this.descriptor = descriptor;
} }
public String getName() { public final String getName() {
return name; return name;
} }
public String getDescriptor() { public final String getDescriptor() {
return descriptor; return descriptor;
} }

@ -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; package org.jetbrains.java.decompiler.struct;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -12,23 +12,26 @@ import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class StructMember { public abstract class StructMember {
private final int accessFlags;
protected int accessFlags; private final Map<String, StructGeneralAttribute> attributes;
protected Map<String, StructGeneralAttribute> attributes;
protected StructMember(int accessFlags, Map<String, StructGeneralAttribute> attributes) {
this.accessFlags = accessFlags;
this.attributes = attributes;
}
public int getAccessFlags() { public int getAccessFlags() {
return accessFlags; return accessFlags;
} }
public <T extends StructGeneralAttribute> T getAttribute(StructGeneralAttribute.Key<T> attribute) { public <T extends StructGeneralAttribute> T getAttribute(StructGeneralAttribute.Key<T> attribute) {
//noinspection unchecked @SuppressWarnings("unchecked") T t = (T)attributes.get(attribute.name);
return (T)attributes.get(attribute.getName()); return t;
} }
public boolean hasAttribute(StructGeneralAttribute.Key<?> attribute) { public boolean hasAttribute(StructGeneralAttribute.Key<?> attribute) {
return attributes.containsKey(attribute.getName()); return attributes.containsKey(attribute.name);
} }
public boolean hasModifier(int modifier) { public boolean hasModifier(int modifier) {
@ -39,45 +42,37 @@ public class StructMember {
return hasModifier(CodeConstants.ACC_SYNTHETIC) || hasAttribute(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC); return hasModifier(CodeConstants.ACC_SYNTHETIC) || hasAttribute(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC);
} }
protected Map<String, StructGeneralAttribute> readAttributes(DataInputFullStream in, ConstantPool pool) throws IOException { public static Map<String, StructGeneralAttribute> readAttributes(DataInputFullStream in, ConstantPool pool) throws IOException {
int length = in.readUnsignedShort(); int length = in.readUnsignedShort();
Map<String, StructGeneralAttribute> attributes = new HashMap<>(length); Map<String, StructGeneralAttribute> attributes = new HashMap<>(length);
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
int nameIndex = in.readUnsignedShort(); int nameIndex = in.readUnsignedShort();
String name = pool.getPrimitiveConstant(nameIndex).getString(); String name = pool.getPrimitiveConstant(nameIndex).getString();
StructGeneralAttribute attribute = readAttribute(in, pool, name); StructGeneralAttribute attribute = StructGeneralAttribute.createAttribute(name);
int attLength = in.readInt();
if (attribute != null) { if (attribute == null) {
if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE.getName().equals(name) && attributes.containsKey(name)) { in.discard(attLength);
}
else {
attribute.initContent(in, pool);
if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE.name.equals(name) && attributes.containsKey(name)) {
// merge all variable tables // merge all variable tables
StructLocalVariableTableAttribute table = (StructLocalVariableTableAttribute)attributes.get(name); StructLocalVariableTableAttribute table = (StructLocalVariableTableAttribute)attributes.get(name);
table.add((StructLocalVariableTableAttribute)attribute); 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 // merge all variable tables
StructLocalVariableTypeTableAttribute table = (StructLocalVariableTypeTableAttribute)attributes.get(name); StructLocalVariableTypeTableAttribute table = (StructLocalVariableTypeTableAttribute)attributes.get(name);
table.add((StructLocalVariableTypeTableAttribute)attribute); table.add((StructLocalVariableTypeTableAttribute)attribute);
} }
else { else {
attributes.put(attribute.getName(), attribute); attributes.put(name, attribute);
} }
} }
} }
return attributes; 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;
}
} }

@ -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; package org.jetbrains.java.decompiler.struct;
import org.jetbrains.java.decompiler.code.*; 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.StructGeneralAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute; import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 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 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<String, StructGeneralAttribute> 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_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[] 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_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 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 name;
private final String descriptor; private final String descriptor;
private final int bytecodeVersion;
private boolean containsCode = false; private final int localVariables;
private int localVariables = 0; private final int codeLength;
private int codeLength = 0; private final int codeFullLength;
private int codeFullLength = 0; private InstructionSequence seq = null;
private InstructionSequence seq;
private boolean expanded = false; private boolean expanded = false;
private Map<String, StructGeneralAttribute> codeAttributes;
public StructMethod(DataInputFullStream in, StructClass clStruct) throws IOException {
classStruct = clStruct;
accessFlags = in.readUnsignedShort(); private StructMethod(int accessFlags,
int nameIndex = in.readUnsignedShort(); Map<String, StructGeneralAttribute> attributes,
int descriptorIndex = in.readUnsignedShort(); String name,
String descriptor,
ConstantPool pool = clStruct.getPool(); int bytecodeVersion,
String[] values = pool.getClassElement(ConstantPool.METHOD, clStruct.qualifiedName, nameIndex, descriptorIndex); StructCodeAttribute code) {
name = values[0]; super(accessFlags, attributes);
descriptor = values[1]; this.name = name;
this.descriptor = descriptor;
attributes = readAttributes(in, pool); this.bytecodeVersion = bytecodeVersion;
if (codeAttributes != null) { if (code != null) {
attributes.putAll(codeAttributes); this.localVariables = code.localVariables;
codeAttributes = null; this.codeLength = code.codeLength;
this.codeFullLength = code.codeFullLength;
} }
} else {
this.localVariables = this.codeLength = this.codeFullLength = -1;
@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;
} }
return super.readAttribute(in, pool, name);
} }
public void expandData() throws IOException { public void expandData(StructClass classStruct) throws IOException {
if (containsCode && !expanded) { if (codeLength >= 0 && !expanded) {
byte[] code = classStruct.getLoader().loadBytecode(this, codeFullLength); byte[] code = classStruct.getLoader().loadBytecode(classStruct, this, codeFullLength);
seq = parseBytecode(new DataInputFullStream(code), codeLength, classStruct.getPool()); seq = parseBytecode(new DataInputFullStream(code), codeLength, classStruct.getPool());
expanded = true; expanded = true;
} }
} }
public void releaseResources() { public void releaseResources() {
if (containsCode && expanded) { if (codeLength >= 0 && expanded) {
seq = null; seq = null;
expanded = false; expanded = false;
} }
@ -108,10 +95,7 @@ public class StructMethod extends StructMember {
private InstructionSequence parseBytecode(DataInputFullStream in, int length, ConstantPool pool) throws IOException { private InstructionSequence parseBytecode(DataInputFullStream in, int length, ConstantPool pool) throws IOException {
VBStyleCollection<Instruction, Integer> instructions = new VBStyleCollection<>(); VBStyleCollection<Instruction, Integer> instructions = new VBStyleCollection<>();
int bytecode_version = classStruct.getBytecodeVersion();
for (int i = 0; i < length; ) { for (int i = 0; i < length; ) {
int offset = i; int offset = i;
int opcode = in.readUnsignedByte(); int opcode = in.readUnsignedByte();
@ -197,7 +181,7 @@ public class StructMethod extends StructMember {
} }
break; break;
case opc_invokedynamic: 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()); operands.add(in.readUnsignedShort());
in.discard(2); in.discard(2);
group = GROUP_INVOCATION; 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); instructions.addWithKey(instr, offset);
@ -355,10 +339,6 @@ public class StructMethod extends StructMember {
return seq; return seq;
} }
public StructClass getClassStruct() {
return classStruct;
}
public String getName() { public String getName() {
return name; return name;
} }
@ -367,8 +347,12 @@ public class StructMethod extends StructMember {
return descriptor; return descriptor;
} }
public int getBytecodeVersion() {
return bytecodeVersion;
}
public boolean containsCode() { public boolean containsCode() {
return containsCode; return codeLength >= 0;
} }
public int getLocalVariables() { public int getLocalVariables() {

@ -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; 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.ConstantPool;
import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
import org.jetbrains.java.decompiler.util.DataInputFullStream; import org.jetbrains.java.decompiler.util.DataInputFullStream;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
/* /*
record_component_info { record_component_info {
@ -15,33 +17,20 @@ import java.io.IOException;
attribute_info attributes[attributes_count]; attribute_info attributes[attributes_count];
} }
*/ */
public class StructRecordComponent extends StructMember { public class StructRecordComponent extends StructField {
public static StructRecordComponent create(DataInputFullStream in, ConstantPool pool) throws IOException {
private final String name;
private final String descriptor;
public StructRecordComponent(DataInputFullStream in, ConstantPool pool) throws IOException {
accessFlags = 0;
int nameIndex = in.readUnsignedShort(); int nameIndex = in.readUnsignedShort();
int descriptorIndex = in.readUnsignedShort(); int descriptorIndex = in.readUnsignedShort();
name = ((PrimitiveConstant)pool.getConstant(nameIndex)).getString(); String name = ((PrimitiveConstant)pool.getConstant(nameIndex)).getString();
descriptor = ((PrimitiveConstant)pool.getConstant(descriptorIndex)).getString(); String descriptor = ((PrimitiveConstant)pool.getConstant(descriptorIndex)).getString();
attributes = readAttributes(in, pool); Map<String, StructGeneralAttribute> attributes = readAttributes(in, pool);
}
public String getName() {
return name;
}
public String getDescriptor() { return new StructRecordComponent(0, attributes, name, descriptor);
return descriptor;
} }
@Override private StructRecordComponent(int flags, Map<String, StructGeneralAttribute> attributes, String name, String descriptor) {
public String toString() { super(flags, attributes, name, descriptor);
return name;
} }
} }

@ -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<String, StructGeneralAttribute> 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);
}
}

@ -14,7 +14,7 @@ import java.io.IOException;
} }
*/ */
public class StructGeneralAttribute { public class StructGeneralAttribute {
public static final Key<StructGeneralAttribute> ATTRIBUTE_CODE = new Key<>("Code"); public static final Key<StructCodeAttribute> ATTRIBUTE_CODE = new Key<>("Code");
public static final Key<StructInnerClassesAttribute> ATTRIBUTE_INNER_CLASSES = new Key<>("InnerClasses"); public static final Key<StructInnerClassesAttribute> ATTRIBUTE_INNER_CLASSES = new Key<>("InnerClasses");
public static final Key<StructGenericSignatureAttribute> ATTRIBUTE_SIGNATURE = new Key<>("Signature"); public static final Key<StructGenericSignatureAttribute> ATTRIBUTE_SIGNATURE = new Key<>("Signature");
public static final Key<StructAnnDefaultAttribute> ATTRIBUTE_ANNOTATION_DEFAULT = new Key<>("AnnotationDefault"); public static final Key<StructAnnDefaultAttribute> ATTRIBUTE_ANNOTATION_DEFAULT = new Key<>("AnnotationDefault");
@ -37,86 +37,74 @@ public class StructGeneralAttribute {
public static final Key<StructModuleAttribute> ATTRIBUTE_MODULE = new Key<>("Module"); public static final Key<StructModuleAttribute> ATTRIBUTE_MODULE = new Key<>("Module");
public static final Key<StructRecordAttribute> ATTRIBUTE_RECORD = new Key<>("Record"); public static final Key<StructRecordAttribute> ATTRIBUTE_RECORD = new Key<>("Record");
@SuppressWarnings("unused")
public static class Key<T extends StructGeneralAttribute> { public static class Key<T extends StructGeneralAttribute> {
private final String name; public final String name;
public Key(String name) { public Key(String name) {
this.name = name; this.name = name;
} }
public String getName() {
return name;
}
} }
private String name;
public static StructGeneralAttribute createAttribute(String name) { public static StructGeneralAttribute createAttribute(String name) {
StructGeneralAttribute attr; if (ATTRIBUTE_CODE.name.equals(name)) {
return new StructCodeAttribute();
if (ATTRIBUTE_INNER_CLASSES.getName().equals(name)) {
attr = new StructInnerClassesAttribute();
} }
else if (ATTRIBUTE_CONSTANT_VALUE.getName().equals(name)) { else if (ATTRIBUTE_INNER_CLASSES.name.equals(name)) {
attr = new StructConstantValueAttribute(); return new StructInnerClassesAttribute();
} }
else if (ATTRIBUTE_SIGNATURE.getName().equals(name)) { else if (ATTRIBUTE_CONSTANT_VALUE.name.equals(name)) {
attr = new StructGenericSignatureAttribute(); return new StructConstantValueAttribute();
} }
else if (ATTRIBUTE_ANNOTATION_DEFAULT.getName().equals(name)) { else if (ATTRIBUTE_SIGNATURE.name.equals(name)) {
attr = new StructAnnDefaultAttribute(); return new StructGenericSignatureAttribute();
} }
else if (ATTRIBUTE_EXCEPTIONS.getName().equals(name)) { else if (ATTRIBUTE_ANNOTATION_DEFAULT.name.equals(name)) {
attr = new StructExceptionsAttribute(); return new StructAnnDefaultAttribute();
} }
else if (ATTRIBUTE_ENCLOSING_METHOD.getName().equals(name)) { else if (ATTRIBUTE_EXCEPTIONS.name.equals(name)) {
attr = new StructEnclosingMethodAttribute(); return new StructExceptionsAttribute();
} }
else if (ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.getName().equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.getName().equals(name)) { else if (ATTRIBUTE_ENCLOSING_METHOD.name.equals(name)) {
attr = new StructAnnotationAttribute(); return new StructEnclosingMethodAttribute();
} }
else if (ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.getName().equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.getName().equals(name)) { else if (ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.name.equals(name)) {
attr = new StructAnnotationParameterAttribute(); return new StructAnnotationAttribute();
} }
else if (ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.getName().equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.getName().equals(name)) { else if (ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.name.equals(name)) {
attr = new StructTypeAnnotationAttribute(); return new StructAnnotationParameterAttribute();
} }
else if (ATTRIBUTE_LOCAL_VARIABLE_TABLE.getName().equals(name)) { else if (ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.name.equals(name)) {
attr = new StructLocalVariableTableAttribute(); return new StructTypeAnnotationAttribute();
} }
else if (ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.getName().equals(name)) { else if (ATTRIBUTE_LOCAL_VARIABLE_TABLE.name.equals(name)) {
attr = new StructLocalVariableTypeTableAttribute(); return new StructLocalVariableTableAttribute();
} }
else if (ATTRIBUTE_BOOTSTRAP_METHODS.getName().equals(name)) { else if (ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.name.equals(name)) {
attr = new StructBootstrapMethodsAttribute(); return new StructLocalVariableTypeTableAttribute();
} }
else if (ATTRIBUTE_SYNTHETIC.getName().equals(name) || ATTRIBUTE_DEPRECATED.getName().equals(name)) { else if (ATTRIBUTE_BOOTSTRAP_METHODS.name.equals(name)) {
attr = new StructGeneralAttribute(); return new StructBootstrapMethodsAttribute();
} }
else if (ATTRIBUTE_LINE_NUMBER_TABLE.getName().equals(name)) { else if (ATTRIBUTE_SYNTHETIC.name.equals(name) || ATTRIBUTE_DEPRECATED.name.equals(name)) {
attr = new StructLineNumberTableAttribute(); return new StructGeneralAttribute();
} }
else if (ATTRIBUTE_METHOD_PARAMETERS.getName().equals(name)) { else if (ATTRIBUTE_LINE_NUMBER_TABLE.name.equals(name)) {
attr = new StructMethodParametersAttribute(); return new StructLineNumberTableAttribute();
} }
else if (ATTRIBUTE_MODULE.getName().equals(name)) { else if (ATTRIBUTE_METHOD_PARAMETERS.name.equals(name)) {
attr = new StructModuleAttribute(); return new StructMethodParametersAttribute();
} }
else if (ATTRIBUTE_RECORD.getName().equals(name)) { else if (ATTRIBUTE_MODULE.name.equals(name)) {
attr = new StructRecordAttribute(); return new StructModuleAttribute();
}
else if (ATTRIBUTE_RECORD.name.equals(name)) {
return new StructRecordAttribute();
} }
else { else {
// unsupported attribute return null; // unsupported attribute
return null;
} }
attr.name = name;
return attr;
} }
public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { } public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { }
public String getName() {
return name;
}
} }

@ -23,7 +23,6 @@ public class StructModuleAttribute extends StructGeneralAttribute {
@Override @Override
public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {
int moduleNameIndex = data.readUnsignedShort(); int moduleNameIndex = data.readUnsignedShort();
this.moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); this.moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString();
this.moduleFlags = data.readUnsignedShort(); this.moduleFlags = data.readUnsignedShort();
@ -41,147 +40,87 @@ public class StructModuleAttribute extends StructGeneralAttribute {
public List<RequiresEntry> readRequires(DataInputFullStream data, ConstantPool pool) throws IOException { public List<RequiresEntry> readRequires(DataInputFullStream data, ConstantPool pool) throws IOException {
int requiresCount = data.readUnsignedShort(); int requiresCount = data.readUnsignedShort();
if (requiresCount <= 0) return Collections.emptyList();
if (requiresCount <= 0) {
return Collections.emptyList();
}
List<RequiresEntry> requires = new ArrayList<>(requiresCount); List<RequiresEntry> requires = new ArrayList<>(requiresCount);
for (int i = 0; i < requiresCount; i++) { for (int i = 0; i < requiresCount; i++) {
int moduleNameIndex = data.readUnsignedShort(); int moduleNameIndex = data.readUnsignedShort();
int moduleFlags = data.readUnsignedShort(); int moduleFlags = data.readUnsignedShort();
int versionIndex = data.readUnsignedShort(); int versionIndex = data.readUnsignedShort();
String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString();
String version = versionIndex == 0 ? null : pool.getPrimitiveConstant(versionIndex).getString(); String version = versionIndex == 0 ? null : pool.getPrimitiveConstant(versionIndex).getString();
requires.add(new RequiresEntry(moduleName, moduleFlags, version)); requires.add(new RequiresEntry(moduleName, moduleFlags, version));
} }
return requires; return requires;
} }
private List<ExportsEntry> readExports(DataInputFullStream data, ConstantPool pool) throws IOException { private static List<ExportsEntry> readExports(DataInputFullStream data, ConstantPool pool) throws IOException {
int exportsCount = data.readUnsignedShort(); int exportsCount = data.readUnsignedShort();
if (exportsCount <= 0) return Collections.emptyList();
if (exportsCount <= 0) {
return Collections.emptyList();
}
List<ExportsEntry> exports = new ArrayList<>(exportsCount); List<ExportsEntry> exports = new ArrayList<>(exportsCount);
for (int i = 0; i < exportsCount; i++) { for (int i = 0; i < exportsCount; i++) {
int packageNameIndex = data.readUnsignedShort(); int packageNameIndex = data.readUnsignedShort();
int exportsFlags = data.readUnsignedShort(); int exportsFlags = data.readUnsignedShort();
int exportsToCount = data.readUnsignedShort(); List<String> exportsToModules = readStringList(data, pool);
List<String> 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();
}
String packageName = pool.getPrimitiveConstant(packageNameIndex).getString(); String packageName = pool.getPrimitiveConstant(packageNameIndex).getString();
exports.add(new ExportsEntry(packageName, exportsFlags, exportsToModules)); exports.add(new ExportsEntry(packageName, exportsFlags, exportsToModules));
} }
return exports; return exports;
} }
private List<OpensEntry> readOpens(DataInputFullStream data, ConstantPool pool) throws IOException { private static List<OpensEntry> readOpens(DataInputFullStream data, ConstantPool pool) throws IOException {
int opensCount = data.readUnsignedShort(); int opensCount = data.readUnsignedShort();
if (opensCount <= 0) return Collections.emptyList();
if (opensCount <= 0) {
return Collections.emptyList();
}
List<OpensEntry> opens = new ArrayList<>(opensCount); List<OpensEntry> opens = new ArrayList<>(opensCount);
for (int i = 0; i < opensCount; i++) { for (int i = 0; i < opensCount; i++) {
int packageNameIndex = data.readUnsignedShort(); int packageNameIndex = data.readUnsignedShort();
int opensFlags = data.readUnsignedShort(); int opensFlags = data.readUnsignedShort();
int opensToCount = data.readUnsignedShort(); List<String> opensToModules = readStringList(data, pool);
List<String> 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();
}
String packageName = pool.getPrimitiveConstant(packageNameIndex).getString(); String packageName = pool.getPrimitiveConstant(packageNameIndex).getString();
opens.add(new OpensEntry(packageName, opensFlags, opensToModules)); opens.add(new OpensEntry(packageName, opensFlags, opensToModules));
} }
return opens; return opens;
} }
private List<String> readUses(DataInputFullStream data, ConstantPool pool) throws IOException { private static List<String> readUses(DataInputFullStream data, ConstantPool pool) throws IOException {
int usesCount = data.readUnsignedShort(); return readStringList(data, pool);
if (usesCount <= 0) {
return Collections.emptyList();
}
List<String> 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 List<ProvidesEntry> readProvides(DataInputFullStream data, ConstantPool pool) throws IOException { private static List<ProvidesEntry> readProvides(DataInputFullStream data, ConstantPool pool) throws IOException {
int providesCount = data.readUnsignedShort(); int providesCount = data.readUnsignedShort();
if (providesCount <= 0) { if (providesCount <= 0) return Collections.emptyList();
return Collections.emptyList();
}
List<ProvidesEntry> provides = new ArrayList<>(providesCount); List<ProvidesEntry> provides = new ArrayList<>(providesCount);
for (int i = 0; i < providesCount; i++) { for (int i = 0; i < providesCount; i++) {
int interfaceNameIndex = data.readUnsignedShort(); int interfaceNameIndex = data.readUnsignedShort();
String interfaceName = pool.getPrimitiveConstant(interfaceNameIndex).getString(); String interfaceName = pool.getPrimitiveConstant(interfaceNameIndex).getString();
List<String> implementationNames = readStringList(data, pool);
// Always nonzero
int providesWithCount = data.readUnsignedShort();
List<String> implementationNames = new ArrayList<>(providesWithCount);
for (int j = 0; j < providesWithCount; j++) {
int classNameIndex = data.readUnsignedShort();
String className = pool.getPrimitiveConstant(classNameIndex).getString();
implementationNames.add(className);
}
provides.add(new ProvidesEntry(interfaceName, implementationNames)); provides.add(new ProvidesEntry(interfaceName, implementationNames));
} }
return provides; return provides;
} }
private static List<String> readStringList(DataInputFullStream data, ConstantPool pool) throws IOException {
int count = data.readUnsignedShort();
if (count <= 0) {
return Collections.emptyList();
}
else {
List<String> 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 static final class RequiresEntry {
public String moduleName; public final String moduleName;
public int moduleFlags; public final int moduleFlags;
public String moduleVersion; public final String moduleVersion;
public RequiresEntry(String moduleName, int moduleFlags, String moduleVersion) { public RequiresEntry(String moduleName, int moduleFlags, String moduleVersion) {
this.moduleName = moduleName; this.moduleName = moduleName;
@ -191,9 +130,9 @@ public class StructModuleAttribute extends StructGeneralAttribute {
} }
public static final class ExportsEntry { public static final class ExportsEntry {
public String packageName; public final String packageName;
public int exportsFlags; public final int exportsFlags;
public List<String> exportToModules; public final List<String> exportToModules;
public ExportsEntry(String packageName, int exportsFlags, List<String> exportToModules) { public ExportsEntry(String packageName, int exportsFlags, List<String> exportToModules) {
this.packageName = packageName; this.packageName = packageName;
@ -203,9 +142,9 @@ public class StructModuleAttribute extends StructGeneralAttribute {
} }
public static final class OpensEntry { public static final class OpensEntry {
public String packageName; public final String packageName;
public int opensFlags; public final int opensFlags;
public List<String> opensToModules; public final List<String> opensToModules;
public OpensEntry(String packageName, int exportsFlags, List<String> exportToModules) { public OpensEntry(String packageName, int exportsFlags, List<String> exportToModules) {
this.packageName = packageName; this.packageName = packageName;
@ -215,13 +154,12 @@ public class StructModuleAttribute extends StructGeneralAttribute {
} }
public static final class ProvidesEntry { public static final class ProvidesEntry {
public String interfaceName; public final String interfaceName;
public List<String> implementationNames; public final List<String> implementationNames;
public ProvidesEntry(String interfaceName, List<String> implementationNames) { public ProvidesEntry(String interfaceName, List<String> implementationNames) {
this.interfaceName = interfaceName; this.interfaceName = interfaceName;
this.implementationNames = implementationNames; this.implementationNames = implementationNames;
} }
} }
} }

@ -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; package org.jetbrains.java.decompiler.struct.attr;
import org.jetbrains.java.decompiler.struct.StructRecordComponent; import org.jetbrains.java.decompiler.struct.StructRecordComponent;
@ -22,12 +22,11 @@ public class StructRecordAttribute extends StructGeneralAttribute {
List<StructRecordComponent> components; List<StructRecordComponent> components;
@Override @Override
public void initContent(DataInputFullStream data, public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {
ConstantPool pool) throws IOException {
int componentCount = data.readUnsignedShort(); int componentCount = data.readUnsignedShort();
StructRecordComponent[] components = new StructRecordComponent[componentCount]; StructRecordComponent[] components = new StructRecordComponent[componentCount];
for (int i = 0; i < componentCount; i++) { for (int i = 0; i < componentCount; i++) {
components[i] = new StructRecordComponent(data, pool); components[i] = StructRecordComponent.create(data, pool);
} }
this.components = Arrays.asList(components); this.components = Arrays.asList(components);
} }

@ -212,18 +212,12 @@ public class ConstantPool implements NewClassNameBuilder {
String newName = interceptor.getName(vt.value); String newName = interceptor.getName(vt.value);
if (newName != null) { if (newName != null) {
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
if (vt.arrayDim > 0) { if (vt.arrayDim > 0) {
for (int i = 0; i < vt.arrayDim; i++) { buffer.append("[".repeat(vt.arrayDim)).append('L').append(newName).append(';');
buffer.append('[');
}
buffer.append('L').append(newName).append(';');
} }
else { else {
buffer.append(newName); buffer.append(newName);
} }
return buffer.toString(); return buffer.toString();
} }

@ -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; package org.jetbrains.java.decompiler.struct.lazy;
import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; 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.StructMethod;
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
@ -19,20 +20,20 @@ public class LazyLoader {
this.provider = provider; this.provider = provider;
} }
public void addClassLink(String classname, Link link) { public void addClassLink(String className, Link link) {
mapClassLinks.put(classname, link); mapClassLinks.put(className, link);
} }
public void removeClassLink(String classname) { public void removeClassLink(String className) {
mapClassLinks.remove(classname); mapClassLinks.remove(className);
} }
public Link getClassLink(String classname) { public Link getClassLink(String className) {
return mapClassLinks.get(classname); return mapClassLinks.get(className);
} }
public ConstantPool loadPool(String classname) { public ConstantPool loadPool(String className) {
try (DataInputFullStream in = getClassStream(classname)) { try (DataInputFullStream in = getClassStream(className)) {
if (in != null) { if (in != null) {
in.discard(8); in.discard(8);
return new ConstantPool(in); return new ConstantPool(in);
@ -45,14 +46,14 @@ public class LazyLoader {
} }
} }
public byte[] loadBytecode(StructMethod mt, int codeFullLength) { public byte[] loadBytecode(StructClass classStruct, StructMethod mt, int codeFullLength) {
String className = mt.getClassStruct().qualifiedName; String className = classStruct.qualifiedName;
try (DataInputFullStream in = getClassStream(className)) { try (DataInputFullStream in = getClassStream(className)) {
if (in != null) { if (in != null) {
in.discard(8); in.discard(8);
ConstantPool pool = mt.getClassStruct().getPool(); ConstantPool pool = classStruct.getPool();
if (pool == null) { if (pool == null) {
pool = new ConstantPool(in); pool = new ConstantPool(in);
} }
@ -90,7 +91,7 @@ public class LazyLoader {
for (int j = 0; j < attrSize; j++) { for (int j = 0; j < attrSize; j++) {
int attrNameIndex = in.readUnsignedShort(); int attrNameIndex = in.readUnsignedShort();
String attrName = pool.getPrimitiveConstant(attrNameIndex).getString(); String attrName = pool.getPrimitiveConstant(attrNameIndex).getString();
if (!StructGeneralAttribute.ATTRIBUTE_CODE.getName().equals(attrName)) { if (!StructGeneralAttribute.ATTRIBUTE_CODE.name.equals(attrName)) {
in.discard(in.readInt()); in.discard(in.readInt());
continue; continue;
} }

@ -1,13 +1,14 @@
@Deprecated
module sample.module { module sample.module {
requires java.base; requires java.base;
exports test; exports sample.pkg1;
exports test2 to exports sample.pkg2 to
java.base; java.base;
opens test; opens sample.pkg1;
opens test2 to opens sample.pkg2 to
java.base; java.base;
uses java.util.spi.ToolProvider; uses java.util.spi.ToolProvider;
provides test.TestService with provides sample.pkg1.TestService with
test.TestServiceImpl; sample.pkg1.TestServiceImpl;
} }

@ -0,0 +1,3 @@
package sample.pkg2;
public class TestClass {}

@ -0,0 +1,3 @@
package sample.pkg1;
public interface TestService {}

@ -0,0 +1,3 @@
package sample.pkg1;
public class TestServiceImpl implements TestService {}

@ -1,15 +1,16 @@
@Deprecated
module sample.module { module sample.module {
requires java.base; requires java.base;
uses java.util.spi.ToolProvider; 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; opens sample.pkg2 to java.base;
} }

@ -1,3 +0,0 @@
package test;
public interface TestService {}

@ -1,3 +0,0 @@
package test;
public class TestServiceImpl implements TestService {}

@ -1,3 +0,0 @@
package test2;
public class TestClass {}
Loading…
Cancel
Save