From 05fa036623ce2cd72af1d8d696961cfefeb0d0f4 Mon Sep 17 00:00:00 2001 From: Cody <6558800+Bl3nd@users.noreply.github.com> Date: Mon, 7 Oct 2024 21:35:43 -0600 Subject: [PATCH 01/12] Add/Re-add field parsing when scope is from a parameter or variable. - Added 0.1s wait between opening class and registering "Go-To" caret event, hopefully fixes text area not scrolling to caret. - Added/Re-added ability to open a field declaration in its owner class if the scope was a parameter or variable. --- .../gui/components/actions/GoToAction.java | 42 +++++++++++++++++-- .../parser/visitors/FieldAccessParser.java | 28 +++++++++---- .../parser/visitors/MyVoidVisitor.java | 9 ++-- .../parser/visitors/ParserUtil.java | 23 +++++----- 4 files changed, 77 insertions(+), 25 deletions(-) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java index 23b5409e6..74bb16f7a 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java @@ -185,10 +185,34 @@ private ClassFileContainer openClass(String lexeme, boolean field, boolean metho if (field) { String className = container.getClassForField(lexeme); - BytecodeViewer.viewer.workPane.addClassResource(resourceContainer, className + ".class"); - ClassViewer activeResource = (ClassViewer) BytecodeViewer.viewer.workPane.getActiveResource(); - HashMap classFiles = BytecodeViewer.viewer.workPane.classFiles; - return wait(classFiles, activeResource); + + // If the field we want to go to wasn't an expression like Class.field. For example param.field or + // variable.field + if (className.isEmpty()) + { + ClassFieldLocation classFieldLocation = container.getFieldLocationsFor(lexeme).get(0); + className = classFieldLocation.owner; + ClassReferenceLocation classReferenceLocation = + container.getClassReferenceLocationsFor(className).get(0); + if (classReferenceLocation == null) + return null; + + String packagePath = classReferenceLocation.packagePath; + + if (packagePath.startsWith("java") || packagePath.startsWith("javax") || packagePath.startsWith("com.sun")) + return null; + + if (!packagePath.isEmpty()) + className = packagePath + "/" + className; + } + + if (resourceContainer.resourceClasses.containsKey(className)) + { + BytecodeViewer.viewer.workPane.addClassResource(resourceContainer, className + ".class"); + ClassViewer activeResource = (ClassViewer) BytecodeViewer.viewer.workPane.getActiveResource(); + HashMap classFiles = BytecodeViewer.viewer.workPane.classFiles; + return wait(classFiles, activeResource); + } } else if (method) { @@ -354,6 +378,16 @@ private ClassFileContainer wait(HashMap classFiles, private void moveCursor(int line, int columnStart) { + // Wait for 100ms so we make sure there is enough time between loading the class and registering cursor movement + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + throw new RuntimeException(e); + } + for (int i = 0; i < 3; i++) { BytecodeViewPanel panel = ((ClassViewer) BytecodeViewer.viewer.workPane.getActiveResource()).getPanel(i); diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java index 0faa2d4f2..5b5b4deea 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java @@ -1,6 +1,7 @@ package the.bytecode.club.bytecodeviewer.resources.classcontainer.parser.visitors; import com.github.javaparser.Range; +import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.expr.*; import com.github.javaparser.resolution.UnsolvedSymbolException; import the.bytecode.club.bytecodeviewer.resources.classcontainer.ClassFileContainer; @@ -16,7 +17,7 @@ class FieldAccessParser { - static void parse(ClassFileContainer container, FieldAccessExpr expr) + static void parse(ClassFileContainer container, FieldAccessExpr expr, CallableDeclaration method) { Range fieldRange = Objects.requireNonNull(expr.getTokenRange().orElse(null)).getEnd().getRange().orElse(null); if (fieldRange == null) @@ -26,7 +27,7 @@ static void parse(ClassFileContainer container, FieldAccessExpr expr) Expression scope = expr.getScope(); - // Ex. Clazz.field -> Clazz + // Ex. Clazz.field -> Clazz or c.field -> c if (scope instanceof NameExpr) { NameExpr nameExpr = (NameExpr) scope; @@ -38,7 +39,11 @@ static void parse(ClassFileContainer container, FieldAccessExpr expr) try { - putClassResolvedValues(container, expr, nameExpr, scopeValue, fieldValue); + // Scope + putResolvedValues(container, "reference", method, nameExpr, scopeValue); + + // Field + putFieldResolvedValues(container, expr, nameExpr, fieldValue); } catch (UnsolvedSymbolException ignore) { @@ -58,7 +63,8 @@ else if (scope instanceof ThisExpr) try { putFieldResolvedValues(container, expr, thisExpr, fieldValue); - } catch (UnsolvedSymbolException e) + } + catch (UnsolvedSymbolException e) { printException(expr, e); } @@ -69,7 +75,8 @@ else if (scope instanceof EnclosedExpr) try { putFieldResolvedValues(container, expr, enclosedExpr, fieldValue); - } catch (UnsolvedSymbolException e) + } + catch (UnsolvedSymbolException e) { printException(expr, e); } @@ -104,7 +111,9 @@ static void parseStatic(ClassFileContainer container, FieldAccessExpr expr) try { putClassResolvedValues(container, expr, nameExpr, scopeValue, fieldValue); - } catch (UnsolvedSymbolException e) { + } + catch (UnsolvedSymbolException e) + { printException(expr, e); } } @@ -112,9 +121,12 @@ static void parseStatic(ClassFileContainer container, FieldAccessExpr expr) else if (scope instanceof ThisExpr) { ThisExpr thisExpr = (ThisExpr) scope; - try { + try + { putFieldResolvedValues(container, expr, thisExpr, fieldValue); - } catch (UnsolvedSymbolException e) { + } + catch (UnsolvedSymbolException e) + { printException(expr, e); } } diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java index de7f2dbc4..162ae6bed 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java @@ -532,10 +532,13 @@ public void visit(FieldAccessExpr n, Object arg) try { InitializerDeclaration initializer = findInitializerForExpression(n, this.compilationUnit); + CallableDeclaration method = findMethodForExpression(n, this.compilationUnit); + if (method == null) + method = findConstructorForExpression(n, this.compilationUnit); - if (initializer == null) - FieldAccessParser.parse(classFileContainer, n); - else + if (method != null) + FieldAccessParser.parse(classFileContainer, n, method); + else if (initializer != null) FieldAccessParser.parseStatic(classFileContainer, n); } catch (Exception e) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParserUtil.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParserUtil.java index 265410b84..b257885a9 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParserUtil.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParserUtil.java @@ -56,27 +56,30 @@ public Value(SimpleName simpleName, Range range) * @param resolveExpr The {@code NameExpr} * @param value The value */ - static void putResolvedValues(ClassFileContainer container, String decRef, CallableDeclaration method, - NameExpr resolveExpr, Value value) + static boolean putResolvedValues(ClassFileContainer container, String decRef, CallableDeclaration method, + NameExpr resolveExpr, Value value) { ResolvedValueDeclaration vd = resolveExpr.resolve(); if (vd.isField()) { container.putField(value.name, new ClassFieldLocation(getOwner(container), decRef, value.line, value.columnStart, value.columnEnd + 1)); + return true; } else if (vd.isVariable()) { - container.putLocalVariable(value.name, new ClassLocalVariableLocation(getOwner(container) - , getMethod(method), decRef, value.line, value.columnStart, - value.columnEnd + 1)); + container.putLocalVariable(value.name, new ClassLocalVariableLocation(getOwner(container), + getMethod(method), decRef, value.line, value.columnStart, value.columnEnd + 1)); + return true; } else if (vd.isParameter()) { - container.putParameter(value.name, new ClassParameterLocation(getOwner(container), - getMethod(method), decRef, value.line, value.columnStart, - value.columnEnd + 1)); + container.putParameter(value.name, new ClassParameterLocation(getOwner(container), getMethod(method), + decRef, value.line, value.columnStart, value.columnEnd + 1)); + return true; } + + return false; } /** @@ -96,8 +99,8 @@ static void putResolvedValues(ClassFileContainer container, String decRef, NameE } else if (vd.isVariable()) { - container.putLocalVariable(value.name, new ClassLocalVariableLocation(getOwner(container) - , "static", decRef, value.line, value.columnStart, value.columnEnd + 1)); + container.putLocalVariable(value.name, new ClassLocalVariableLocation(getOwner(container), "static", + decRef, value.line, value.columnStart, value.columnEnd + 1)); } else if (vd.isParameter()) { From 73e96313c0591aad080b0d3b2222db17ab83520f Mon Sep 17 00:00:00 2001 From: Cody <6558800+Bl3nd@users.noreply.github.com> Date: Thu, 10 Oct 2024 17:11:31 -0600 Subject: [PATCH 02/12] Add one more field and method parsing cases - Added 'field1.field2' where field2 is parsed also - Added '::method' calls --- .../parser/visitors/FieldAccessParser.java | 33 +++++++++++++++++ .../parser/visitors/MyVoidVisitor.java | 35 +++++++++++++++---- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java index 5b5b4deea..1f37b4012 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java @@ -4,7 +4,10 @@ import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.expr.*; import com.github.javaparser.resolution.UnsolvedSymbolException; +import com.github.javaparser.resolution.types.ResolvedType; import the.bytecode.club.bytecodeviewer.resources.classcontainer.ClassFileContainer; +import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassFieldLocation; +import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassReferenceLocation; import java.util.Objects; @@ -81,6 +84,36 @@ else if (scope instanceof EnclosedExpr) printException(expr, e); } } + else + { + try + { + // If the scope is something like 'this.field1' and similar + ResolvedType resolvedType = expr.getScope().calculateResolvedType(); + if (!resolvedType.isReferenceType()) + return; + + String qualifiedName = resolvedType.asReferenceType().getQualifiedName(); + String className = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1); + // If the class is not registered as a reference yet + if (container.getClassReferenceLocationsFor(className) == null) + { + String packageName = ""; + if (qualifiedName.contains(".")) + packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf('.')).replace('.', '/'); + + // For this purpose, we do not care about its line, columnStart or columnEnd + container.putClassReference(className, new ClassReferenceLocation(getOwner(container), packageName, + fieldValue.name, "reference", -1, -1, -1)); + } + + container.putField(fieldValue.name, new ClassFieldLocation(className, "reference", fieldValue.line, fieldValue.columnStart, fieldValue.columnEnd + 1)); + } + catch (Exception e) + { + printException(expr, e); + } + } } static void parseStatic(ClassFileContainer container, FieldAccessExpr expr) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java index 162ae6bed..d9e8a703b 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java @@ -294,8 +294,8 @@ public void visit(ClassOrInterfaceDeclaration n, Object arg) ResolvedReferenceTypeDeclaration resolve = n.resolve(); this.classFileContainer.putClassReference(resolve.getName(), - new ClassReferenceLocation(getOwner(classFileContainer), - resolve.getPackageName(), "", "declaration", value.line, value.columnStart, value.columnEnd + 1)); + new ClassReferenceLocation(getOwner(classFileContainer), + resolve.getPackageName(), "", "declaration", value.line, value.columnStart, value.columnEnd + 1)); } catch (Exception e) { @@ -344,8 +344,8 @@ public void visit(ClassOrInterfaceType n, Object arg) packagePath = qualifiedName.substring(0, qualifiedName.lastIndexOf('.')).replace('.', '/'); this.classFileContainer.putClassReference(classValue.name, - new ClassReferenceLocation(getOwner(classFileContainer), - packagePath, "", "reference", classValue.line, classValue.columnStart, classValue.columnEnd + 1)); + new ClassReferenceLocation(getOwner(classFileContainer), + packagePath, "", "reference", classValue.line, classValue.columnStart, classValue.columnEnd + 1)); } catch (Exception e) { @@ -470,7 +470,8 @@ public void visit(EnumDeclaration n, Object arg) int line = range.begin.line; int columnStart = range.begin.column; int columnEnd = range.end.column; - this.classFileContainer.putField(name, new ClassFieldLocation(getOwner(classFileContainer), "declaration", line, columnStart, columnEnd + 1)); + this.classFileContainer.putField(name, new ClassFieldLocation(getOwner(classFileContainer), "declaration" + , line, columnStart, columnEnd + 1)); }); } @@ -899,7 +900,29 @@ public void visit(MethodDeclaration n, Object arg) public void visit(MethodReferenceExpr n, Object arg) { super.visit(n, arg); - if (DEBUG) System.err.println("MethodReferenceExpr"); + try + { + ResolvedMethodDeclaration resolve = n.resolve(); + String signature = resolve.getQualifiedSignature(); + String parameters = ""; + if (resolve.getNumberOfParams() != 0) + parameters = signature.substring(signature.indexOf('(') + 1, signature.lastIndexOf(')')); + + Range methodRange = + Objects.requireNonNull(n.getTokenRange().orElse(null)).getEnd().getRange().orElse(null); + if (methodRange == null) + return; + + String methodName = n.getIdentifier(); + + String className = resolve.getClassName(); + classFileContainer.putMethod(methodName, new ClassMethodLocation(className, signature, parameters, + "reference", methodRange.begin.line, methodRange.begin.column, methodRange.end.column + 1)); + } + catch (Exception e) + { + printException(n, e); + } } From b0af9079789b091492bd12b92e17ab60054d3ae1 Mon Sep 17 00:00:00 2001 From: Cody <6558800+Bl3nd@users.noreply.github.com> Date: Thu, 10 Oct 2024 20:35:08 -0600 Subject: [PATCH 03/12] Catch and print any exception during the parse sequence. --- .../resources/classcontainer/ClassFileContainer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/ClassFileContainer.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/ClassFileContainer.java index 989d8778a..f70beeebd 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/ClassFileContainer.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/ClassFileContainer.java @@ -75,9 +75,9 @@ public boolean parse() return true; } } - catch (IOException e) + catch (Throwable e) { - throw new RuntimeException(e); + System.err.println("Failed to parse " + this.getName() + ": " + e.getMessage()); } return false; From c4ae849003e8212f9407691802d6f75c9a3be52d Mon Sep 17 00:00:00 2001 From: Cody <6558800+Bl3nd@users.noreply.github.com> Date: Sat, 12 Oct 2024 16:00:36 -0600 Subject: [PATCH 04/12] Fix clicking on ErrorStrip brings cursor to the top no matter where you click --- .../club/bytecodeviewer/gui/components/MyErrorStripe.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/MyErrorStripe.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/MyErrorStripe.java index 5f9deff71..58928c844 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/MyErrorStripe.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/MyErrorStripe.java @@ -86,7 +86,7 @@ private int yToLine(int y) if (y < h) { float at = y / (float) h; - line = Math.round((Math.max(lineCount, linesPerVisibleRect) - 1) * at); + line = Math.round((float) (Math.max(lineCount, linesPerVisibleRect) - 1) * at); } return line; @@ -199,7 +199,6 @@ public void removeNotify() private class Listener extends MouseAdapter { - private final Rectangle r = new Rectangle(); @Override public void mouseClicked(@NotNull MouseEvent e) @@ -219,7 +218,7 @@ public void mouseClicked(@NotNull MouseEvent e) { try { - int offset = textArea.getLineOfOffset(line); + int offset = textArea.getLineStartOffset(line); textArea.setCaretPosition(offset); RSyntaxUtilities.selectAndPossiblyCenter(textArea, new DocumentRange(offset, offset), false); } From 789cedadaab4d0f1fd62f9540a59685935972261 Mon Sep 17 00:00:00 2001 From: Cody <6558800+Bl3nd@users.noreply.github.com> Date: Sat, 12 Oct 2024 23:47:49 -0600 Subject: [PATCH 05/12] A few parser things --- .../parser/visitors/FieldAccessParser.java | 2 +- .../parser/visitors/MyVoidVisitor.java | 35 ++++++++++++++++--- .../parser/visitors/ParserUtil.java | 12 +++++-- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java index 1f37b4012..e8d4d78db 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java @@ -103,7 +103,7 @@ else if (scope instanceof EnclosedExpr) packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf('.')).replace('.', '/'); // For this purpose, we do not care about its line, columnStart or columnEnd - container.putClassReference(className, new ClassReferenceLocation(getOwner(container), packageName, + container.putClassReference(className, new ClassReferenceLocation(className, packageName, fieldValue.name, "reference", -1, -1, -1)); } diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java index d9e8a703b..d4177ba14 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java @@ -12,6 +12,7 @@ import com.github.javaparser.ast.type.TypeParameter; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.declarations.ResolvedConstructorDeclaration; +import com.github.javaparser.resolution.declarations.ResolvedEnumDeclaration; import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration; import com.github.javaparser.resolution.types.ResolvedReferenceType; @@ -294,7 +295,7 @@ public void visit(ClassOrInterfaceDeclaration n, Object arg) ResolvedReferenceTypeDeclaration resolve = n.resolve(); this.classFileContainer.putClassReference(resolve.getName(), - new ClassReferenceLocation(getOwner(classFileContainer), + new ClassReferenceLocation(resolve.getName(), resolve.getPackageName(), "", "declaration", value.line, value.columnStart, value.columnEnd + 1)); } catch (Exception e) @@ -343,9 +344,22 @@ public void visit(ClassOrInterfaceType n, Object arg) if (qualifiedName.contains(".")) packagePath = qualifiedName.substring(0, qualifiedName.lastIndexOf('.')).replace('.', '/'); - this.classFileContainer.putClassReference(classValue.name, - new ClassReferenceLocation(getOwner(classFileContainer), - packagePath, "", "reference", classValue.line, classValue.columnStart, classValue.columnEnd + 1)); + ClassOrInterfaceType classScope = n.getScope().orElse(null); + if (classScope == null) + { + this.classFileContainer.putClassReference(classValue.name, + new ClassReferenceLocation(classValue.name, + packagePath, "", "reference", classValue.line, classValue.columnStart, + classValue.columnEnd + 1)); + } + else + { + packagePath = packagePath.substring(0, packagePath.lastIndexOf("/")); + + this.classFileContainer.putClassReference(classValue.name, + new ClassReferenceLocation(classScope.getNameAsString(), packagePath, "", "reference", + classValue.line, classValue.columnStart, classValue.columnEnd + 1)); + } } catch (Exception e) { @@ -459,6 +473,19 @@ public void visit(DoStmt n, Object arg) public void visit(EnumDeclaration n, Object arg) { super.visit(n, arg); + + ResolvedEnumDeclaration resolve = n.resolve(); + + Range enumClassNameRange = n.getName().getRange().orElse(null); + if (enumClassNameRange == null) + return; + + Value enumClassValue = new Value(n.getName(), enumClassNameRange); + + this.classFileContainer.putClassReference(enumClassValue.name, + new ClassReferenceLocation(getOwner(classFileContainer), resolve.getPackageName(), "", "declaration", + enumClassValue.line, enumClassValue.columnStart, enumClassValue.columnEnd + 1)); + n.getEntries().forEach(entry -> { SimpleName simpleName = entry.getName(); diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParserUtil.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParserUtil.java index b257885a9..20d3a0dc3 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParserUtil.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParserUtil.java @@ -14,6 +14,7 @@ import com.github.javaparser.ast.stmt.Statement; import com.github.javaparser.ast.stmt.TryStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration; import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import com.github.javaparser.resolution.types.ResolvedReferenceType; import com.github.javaparser.resolution.types.ResolvedType; @@ -143,9 +144,14 @@ static void putClassResolvedValues(ClassFileContainer container, Expression visi if (qualifiedName.contains(".")) packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf('.')).replace('.', '/'); - container.putClassReference(className, new ClassReferenceLocation(ParserUtil.getOwner(container), packageName + ResolvedReferenceTypeDeclaration resolvedReferenceTypeDeclaration = resolvedType.asReferenceType().getTypeDeclaration().orElse(null); + assert resolvedReferenceTypeDeclaration != null; + if (resolvedReferenceTypeDeclaration.getClassName().contains(".")) + packageName = packageName.substring(0, packageName.lastIndexOf('/')); + + container.putClassReference(className, new ClassReferenceLocation(className, packageName , fieldValue.name, "reference", scopeValue.line, scopeValue.columnStart, scopeValue.columnEnd + 1)); - container.putField(fieldValue.name, new ClassFieldLocation(scopeValue.name, "reference", fieldValue.line, + container.putField(fieldValue.name, new ClassFieldLocation(className, "reference", fieldValue.line, fieldValue.columnStart, fieldValue.columnEnd + 1)); } @@ -174,7 +180,7 @@ static void putClassResolvedValues(ClassFileContainer container, Expression visi if (qualifiedName.contains(".")) packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf('.')).replace('.', '/'); - container.putClassReference(className, new ClassReferenceLocation(ParserUtil.getOwner(container), packageName + container.putClassReference(className, new ClassReferenceLocation(className, packageName , "", "reference", scopeValue.line, scopeValue.columnStart, scopeValue.columnEnd + 1)); } From 164843a824988600e8dd042875685d5a441c825c Mon Sep 17 00:00:00 2001 From: Cody <6558800+Bl3nd@users.noreply.github.com> Date: Sat, 12 Oct 2024 23:48:45 -0600 Subject: [PATCH 06/12] Update GoToAction --- .../gui/components/actions/GoToAction.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java index 74bb16f7a..ff728c7ea 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java @@ -53,7 +53,7 @@ public void actionPerformed(ActionEvent e) // Open the class that is associated with the field's owner. if (!field.owner.equals(container.getName())) { - open(textArea, false, true, false); + find(textArea, false, true, false); return; } @@ -136,7 +136,7 @@ public void actionPerformed(ActionEvent e) } }); - open(textArea, false, false, true); + find(textArea, false, false, true); } } })); @@ -166,7 +166,7 @@ public void actionPerformed(ActionEvent e) }); // Should not really do anything when the class is already open - open(textArea, true, false, false); + find(textArea, true, false, false); } } })); @@ -184,7 +184,9 @@ private ClassFileContainer openClass(String lexeme, boolean field, boolean metho if (field) { + ClassFieldLocation fieldLocation = container.getFieldLocationsFor(lexeme).get(0); String className = container.getClassForField(lexeme); + ClassReferenceLocation referenceLocation = container.getClassReferenceLocationsFor(fieldLocation.owner).get(0); // If the field we want to go to wasn't an expression like Class.field. For example param.field or // variable.field @@ -203,7 +205,12 @@ private ClassFileContainer openClass(String lexeme, boolean field, boolean metho return null; if (!packagePath.isEmpty()) - className = packagePath + "/" + className; + className = packagePath + "/" + className.substring(className.lastIndexOf('/') + 1); + } + + if (!fieldLocation.owner.equals(referenceLocation.owner)) + { + className = referenceLocation.packagePath + "/" + referenceLocation.owner; } if (resourceContainer.resourceClasses.containsKey(className)) @@ -261,6 +268,11 @@ else if (method) resourceName = packagePath + "/" + lexeme; } + if (!classReferenceLocation.owner.equals(container.getName())) + { + resourceName = packagePath + "/" + classReferenceLocation.owner; + } + if (resourceContainer.resourceClasses.containsKey(resourceName)) { BytecodeViewer.viewer.workPane.addClassResource(resourceContainer, resourceName + ".class"); @@ -273,7 +285,7 @@ else if (method) return null; } - private void open(RSyntaxTextArea textArea, boolean isClass, boolean isField, boolean isMethod) + private void find(RSyntaxTextArea textArea, boolean isClass, boolean isField, boolean isMethod) { Thread thread = new Thread(() -> { From 0c47d5ce8b4d42f2a34a69a131753de2a82d929f Mon Sep 17 00:00:00 2001 From: Cody <6558800+Bl3nd@users.noreply.github.com> Date: Sun, 13 Oct 2024 19:04:03 -0600 Subject: [PATCH 07/12] Allow FernFlower to know of any inner classes (if it's enabled) so the class can be decompiled correctly, and we can parse it. --- .../impl/FernFlowerDecompiler.java | 118 +++++++++++++++++- 1 file changed, 113 insertions(+), 5 deletions(-) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/FernFlowerDecompiler.java b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/FernFlowerDecompiler.java index b16b9c95d..3c2398eac 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/FernFlowerDecompiler.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/FernFlowerDecompiler.java @@ -22,17 +22,23 @@ import org.apache.commons.lang3.ArrayUtils; import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler; import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InnerClassNode; import the.bytecode.club.bytecodeviewer.BytecodeViewer; import the.bytecode.club.bytecodeviewer.Constants; +import the.bytecode.club.bytecodeviewer.api.ASMUtil; import the.bytecode.club.bytecodeviewer.api.ExceptionUI; import the.bytecode.club.bytecodeviewer.decompilers.AbstractDecompiler; import the.bytecode.club.bytecodeviewer.resources.ExternalResources; +import the.bytecode.club.bytecodeviewer.resources.ResourceContainer; import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings; import the.bytecode.club.bytecodeviewer.util.ExceptionUtils; import the.bytecode.club.bytecodeviewer.util.ProcessUtils; import the.bytecode.club.bytecodeviewer.util.TempFile; import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import static the.bytecode.club.bytecodeviewer.Constants.*; import static the.bytecode.club.bytecodeviewer.translation.TranslatedStrings.*; @@ -51,12 +57,61 @@ public FernFlowerDecompiler() super("FernFlower Decompiler", "fernflower"); } + private String[] inners; + private final List innerFiles = new ArrayList<>(); @Override public String decompileClassNode(ClassNode cn, byte[] bytes) { TempFile tempFile = null; String exception; + List innerClasses = cn.innerClasses; + List innerTempFiles = new ArrayList<>(); + AtomicReference innerTempFile = new AtomicReference<>(); + if (BytecodeViewer.viewer.din.isSelected()) + { + inners = new String[innerClasses.size()]; + for (int i = 0; i < innerClasses.size(); i++) + { + if (innerClasses.get(i).outerName != null && innerClasses.get(i).outerName.equals(cn.name)) + { + inners[i] = innerClasses.get(i).name; + } + else if (innerClasses.get(i).outerName == null) + { + String name = innerClasses.get(i).name; + name = name.substring(name.lastIndexOf('/') + 1); + if (name.contains(cn.name.substring(cn.name.lastIndexOf('/') + 1))) + { + inners[i] = innerClasses.get(i).name; + } + } + } + + for (ResourceContainer container :BytecodeViewer.resourceContainers.values()) { + container.resourceClasses.forEach((s, classNode) -> { + for (String innerClassName : inners) { + if (s.equals(innerClassName)) { + innerTempFile.set(TempFile.createTemporaryFile(true, ".class")); + File tempInputClassFile2 = innerTempFile.get().getFile(); + try (FileOutputStream fos = new FileOutputStream(tempInputClassFile2)) { + fos.write(ASMUtil.nodeToBytes(classNode)); + } + catch (IOException e) + { + throw new RuntimeException(e); + } finally + { + innerFiles.add(tempInputClassFile2); + innerTempFile.get().markAsCreatedFile(tempInputClassFile2); + innerTempFiles.add(innerTempFile.get()); + } + } + } + }); + } + } + try { //create the temporary files @@ -80,12 +135,17 @@ public String decompileClassNode(ClassNode cn, byte[] bytes) { ExternalResources.getSingleton().getJavaCommand(true), "-jar", ExternalResources.getSingleton().findLibrary("fernflower") - }, generateMainMethod(tempInputClassFile.getAbsolutePath(), tempFile.getParent().getAbsolutePath()) + }, generateMainMethod(tempInputClassFile.getAbsolutePath(), "", tempFile.getParent().getAbsolutePath()) ), false); } else { - org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler.main(generateMainMethod(tempInputClassFile.getAbsolutePath(), new File(TEMP_DIRECTORY).getAbsolutePath())); + List strings = generate(tempInputClassFile.getAbsolutePath(), + new File(TEMP_DIRECTORY).getAbsolutePath()); + + String[] args = strings.toArray(new String[0]); + + org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler.main(args); } //if rename is enabled the file name will be the actual class name @@ -112,6 +172,21 @@ public String decompileClassNode(ClassNode cn, byte[] bytes) //cleanup temp files if(tempFile != null) tempFile.cleanup(); + + if (innerTempFile.get() != null) + innerTempFile.get().cleanup(); + + for (TempFile file : innerTempFiles) + { + file.cleanup(); + File file1 = new File(TEMP_DIRECTORY + file.getUniqueName() + ".java"); + if (file1.exists()) + { + file1.delete(); + } + } + + innerFiles.clear(); } return FERNFLOWER + " " + ERROR + "! " + ExceptionUI.SEND_STACKTRACE_TO + NL + NL @@ -127,7 +202,7 @@ public void decompileToZip(String sourceJar, String zipName) try { - ConsoleDecompiler.main(generateMainMethod(tempInputJarFile.getAbsolutePath(), TEMP_DIRECTORY + "./temp/")); + ConsoleDecompiler.main(generateMainMethod(tempInputJarFile.getAbsolutePath(), "", TEMP_DIRECTORY + "./temp/")); } catch (StackOverflowError | Exception ignored) { @@ -140,7 +215,40 @@ public void decompileToZip(String sourceJar, String zipName) } - private String[] generateMainMethod(String className, String folder) + private List generate(String className, String folder) + { + List strings = new ArrayList<>(); + strings.add("-rbr=" + ffOnValue(BytecodeViewer.viewer.rbr.isSelected())); + strings.add("-rsy=" + ffOnValue(BytecodeViewer.viewer.rsy.isSelected())); + strings.add("-din=" + ffOnValue(BytecodeViewer.viewer.din.isSelected())); + strings.add("-dc4=" + ffOnValue(BytecodeViewer.viewer.dc4.isSelected())); + strings.add("-das=" + ffOnValue(BytecodeViewer.viewer.das.isSelected())); + strings.add("-hes=" + ffOnValue(BytecodeViewer.viewer.hes.isSelected())); + strings.add("-hdc=" + ffOnValue(BytecodeViewer.viewer.hdc.isSelected())); + strings.add("-dgs=" + ffOnValue(BytecodeViewer.viewer.dgs.isSelected())); + strings.add("-ner=" + ffOnValue(BytecodeViewer.viewer.ner.isSelected())); + strings.add("-den=" + ffOnValue(BytecodeViewer.viewer.den.isSelected())); + strings.add("-rgn=" + ffOnValue(BytecodeViewer.viewer.rgn.isSelected())); + strings.add("-bto=" + ffOnValue(BytecodeViewer.viewer.bto.isSelected())); + strings.add("-nns=" + ffOnValue(BytecodeViewer.viewer.nns.isSelected())); + strings.add("-uto=" + ffOnValue(BytecodeViewer.viewer.uto.isSelected())); + strings.add("-udv=" + ffOnValue(BytecodeViewer.viewer.udv.isSelected())); + strings.add("-rer=" + ffOnValue(BytecodeViewer.viewer.rer.isSelected())); + strings.add("-fdi=" + ffOnValue(BytecodeViewer.viewer.fdi.isSelected())); + strings.add("-asc=" + ffOnValue(BytecodeViewer.viewer.asc.isSelected())); + strings.add("-ren=" + ffOnValue(BytecodeViewer.viewer.ren.isSelected())); + strings.add(className); + if (BytecodeViewer.viewer.din.isSelected()) + { + for (File file : innerFiles) + strings.add(file.getAbsolutePath()); + } + + strings.add(folder); + return strings; + } + + private String[] generateMainMethod(String className, String test, String folder) { return new String[] { @@ -163,7 +271,7 @@ private String[] generateMainMethod(String className, String folder) "-fdi=" + ffOnValue(BytecodeViewer.viewer.fdi.isSelected()), "-asc=" + ffOnValue(BytecodeViewer.viewer.asc.isSelected()), "-ren=" + ffOnValue(BytecodeViewer.viewer.ren.isSelected()), - className, folder + className, test, folder }; } From 5291651ff771a6d0129c8d2e546add6a4f6a79ac Mon Sep 17 00:00:00 2001 From: Cody <6558800+Bl3nd@users.noreply.github.com> Date: Wed, 16 Oct 2024 19:05:41 -0600 Subject: [PATCH 08/12] Add parsing for things that are not in a method or class declaration. --- .../parser/visitors/FieldAccessParser.java | 58 ++++++++++++++++++- .../parser/visitors/MyVoidVisitor.java | 12 ++++ .../parser/visitors/ParameterParser.java | 15 ++++- .../parser/visitors/ParserUtil.java | 42 ++++++++++++-- 4 files changed, 119 insertions(+), 8 deletions(-) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java index e8d4d78db..188e63a31 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java @@ -4,9 +4,12 @@ import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.expr.*; import com.github.javaparser.resolution.UnsolvedSymbolException; +import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import com.github.javaparser.resolution.types.ResolvedType; import the.bytecode.club.bytecodeviewer.resources.classcontainer.ClassFileContainer; import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassFieldLocation; +import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassLocalVariableLocation; +import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassParameterLocation; import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassReferenceLocation; import java.util.Objects; @@ -20,6 +23,58 @@ class FieldAccessParser { + /** + * Solve a field that is accessed through a lambda and not within a method or constructor + * + * @param container The {@link ClassFileContainer} + * @param expr The {@link FieldAccessExpr} + * @param className The class name of the class that is accessing the field + */ + static void parse(ClassFileContainer container, FieldAccessExpr expr, String className) + { + Range fieldRange = Objects.requireNonNull(expr.getTokenRange().orElse(null)).getEnd().getRange().orElse(null); + if (fieldRange == null) + return; + + Value fieldValue = new Value(expr.getName(), fieldRange); + + Expression scope = expr.getScope(); + if (scope instanceof NameExpr) + { + NameExpr nameExpr = (NameExpr) scope; + Range scopeRange = nameExpr.getRange().orElse(null); + if (scopeRange == null) + return; + + Value scopeValue = new Value(nameExpr.getName(), scopeRange); + try + { + ResolvedValueDeclaration vd = nameExpr.resolve(); + if (vd.isField()) + { + container.putField(scopeValue.name, new ClassFieldLocation(getOwner(container), "reference", + scopeValue.line, scopeValue.columnStart, scopeValue.columnEnd + 1)); + } + else if (vd.isVariable()) + { + container.putLocalVariable(scopeValue.name, new ClassLocalVariableLocation(getOwner(container), + className, "reference", scopeValue.line, scopeValue.columnStart, scopeValue.columnEnd + 1)); + } + else if (vd.isParameter()) + { + container.putParameter(scopeValue.name, new ClassParameterLocation(getOwner(container), className, + "reference", scopeValue.line, scopeValue.columnStart, scopeValue.columnEnd + 1)); + } + + putFieldResolvedValues(container, expr, nameExpr, fieldValue); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + } + static void parse(ClassFileContainer container, FieldAccessExpr expr, CallableDeclaration method) { Range fieldRange = Objects.requireNonNull(expr.getTokenRange().orElse(null)).getEnd().getRange().orElse(null); @@ -107,7 +162,8 @@ else if (scope instanceof EnclosedExpr) fieldValue.name, "reference", -1, -1, -1)); } - container.putField(fieldValue.name, new ClassFieldLocation(className, "reference", fieldValue.line, fieldValue.columnStart, fieldValue.columnEnd + 1)); + container.putField(fieldValue.name, new ClassFieldLocation(className, "reference", fieldValue.line, + fieldValue.columnStart, fieldValue.columnEnd + 1)); } catch (Exception e) { diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java index d4177ba14..ba45e323d 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/MyVoidVisitor.java @@ -560,6 +560,7 @@ public void visit(FieldAccessExpr n, Object arg) try { InitializerDeclaration initializer = findInitializerForExpression(n, this.compilationUnit); + ClassOrInterfaceDeclaration classOrInterfaceDeclaration = findClassOrInterfaceForExpression(n, this.compilationUnit); CallableDeclaration method = findMethodForExpression(n, this.compilationUnit); if (method == null) method = findConstructorForExpression(n, this.compilationUnit); @@ -568,6 +569,8 @@ public void visit(FieldAccessExpr n, Object arg) FieldAccessParser.parse(classFileContainer, n, method); else if (initializer != null) FieldAccessParser.parseStatic(classFileContainer, n); + else if (classOrInterfaceDeclaration != null) + FieldAccessParser.parse(classFileContainer, n, classOrInterfaceDeclaration.getNameAsString()); } catch (Exception e) { @@ -836,12 +839,17 @@ public void visit(MethodCallExpr n, Object arg) { CallableDeclaration method = findMethodForExpression(n, this.compilationUnit); InitializerDeclaration staticInitializer = null; + ClassOrInterfaceDeclaration classOrInterfaceDeclaration = null; if (method == null) { method = findConstructorForExpression(n, this.compilationUnit); if (method == null) { staticInitializer = findInitializerForExpression(n, this.compilationUnit); + if (staticInitializer == null) + { + classOrInterfaceDeclaration = findClassOrInterfaceForExpression(n, this.compilationUnit); + } } } @@ -870,6 +878,10 @@ else if (staticInitializer != null) { MethodCallParser.parseStatic(classFileContainer, n); } + else if (classOrInterfaceDeclaration != null) + { + MethodCallParser.parse(classFileContainer, n, null); + } } catch (Exception e) { diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParameterParser.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParameterParser.java index 729b8dac9..264422374 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParameterParser.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParameterParser.java @@ -2,7 +2,9 @@ import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.Parameter; +import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.SimpleName; import the.bytecode.club.bytecodeviewer.resources.classcontainer.ClassFileContainer; @@ -23,14 +25,21 @@ public static void parse(CompilationUnit compilationUnit, Parameter p, ClassFile String methodName = findMethodOwnerFor(compilationUnit, node); if (methodName == null) { - System.err.println("Parameter - Method not found"); - return; + ClassOrInterfaceDeclaration classOrInterfaceForExpression = findClassOrInterfaceForExpression((Expression) node, compilationUnit); + if (classOrInterfaceForExpression == null) + { + System.err.println("Parameter - Method not found"); + return; + } + + methodName = classOrInterfaceForExpression.getNameAsString(); } SimpleName name = p.getName(); + String finalMethodName = methodName; name.getRange().ifPresent(range -> { Value parameter = new Value(name, range); - putParameter(container, parameter, methodName, "declaration"); + putParameter(container, parameter, finalMethodName, "declaration"); }); } } diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParserUtil.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParserUtil.java index 20d3a0dc3..f8c565f5d 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParserUtil.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/ParserUtil.java @@ -3,10 +3,7 @@ import com.github.javaparser.Range; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.body.CallableDeclaration; -import com.github.javaparser.ast.body.ConstructorDeclaration; -import com.github.javaparser.ast.body.InitializerDeclaration; -import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.expr.SimpleName; @@ -196,8 +193,16 @@ static void putFieldResolvedValues(ClassFileContainer container, Expression visi Expression resolveExpr, Value fieldValue) { ResolvedType resolvedType = visitedExpr.getSymbolResolver().calculateType(resolveExpr); + if (resolvedType.isConstraint()) + { + resolvedType = resolvedType.asConstraintType().getBound(); + } + if (!resolvedType.isReferenceType()) + { return; + } + String qualifiedName = resolvedType.asReferenceType().getQualifiedName(); String className = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1); @@ -523,4 +528,33 @@ public void visit(InitializerDeclaration n, Void arg) return null; } + + static ClassOrInterfaceDeclaration findClassOrInterfaceForExpression(Expression expression, CompilationUnit cu) + { + final boolean[] contains = {false}; + final ClassOrInterfaceDeclaration[] classOrInterfaceDeclaration = {null}; + cu.accept(new VoidVisitorAdapter() + { + @Override + public void visit(ClassOrInterfaceDeclaration n, Void arg) + { + super.visit(n, arg); + if (contains[0]) + return; + + n.getMembers().forEach(member -> { + if (member.containsWithinRange(expression)) + { + contains[0] = true; + classOrInterfaceDeclaration[0] = n; + } + }); + } + }, null); + + if (contains[0]) + return classOrInterfaceDeclaration[0]; + + return null; + } } From a5f31530649fde69e9c825ba18871a739f926230 Mon Sep 17 00:00:00 2001 From: Cody <6558800+Bl3nd@users.noreply.github.com> Date: Wed, 16 Oct 2024 19:09:00 -0600 Subject: [PATCH 09/12] Fix JDGUI not finding/decompiling inner classes. --- .../decompilers/impl/JDGUIDecompiler.java | 52 ++++++++++++++++++- .../decompilers/jdgui/DirectoryLoader.java | 5 +- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/JDGUIDecompiler.java b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/JDGUIDecompiler.java index 4395bc91a..e3ac9948a 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/JDGUIDecompiler.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/JDGUIDecompiler.java @@ -19,20 +19,26 @@ package the.bytecode.club.bytecodeviewer.decompilers.impl; import com.konloch.disklib.DiskReader; +import org.apache.commons.io.FilenameUtils; import org.jd.core.v1.ClassFileToJavaSourceDecompiler; import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InnerClassNode; +import the.bytecode.club.bytecodeviewer.BytecodeViewer; import the.bytecode.club.bytecodeviewer.Constants; +import the.bytecode.club.bytecodeviewer.api.ASMUtil; import the.bytecode.club.bytecodeviewer.api.ExceptionUI; import the.bytecode.club.bytecodeviewer.decompilers.AbstractDecompiler; import the.bytecode.club.bytecodeviewer.decompilers.jdgui.CommonPreferences; import the.bytecode.club.bytecodeviewer.decompilers.jdgui.DirectoryLoader; import the.bytecode.club.bytecodeviewer.decompilers.jdgui.JDGUIClassFileUtil; import the.bytecode.club.bytecodeviewer.decompilers.jdgui.PlainTextPrinter; +import the.bytecode.club.bytecodeviewer.resources.ResourceContainer; import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings; import the.bytecode.club.bytecodeviewer.util.ExceptionUtils; import the.bytecode.club.bytecodeviewer.util.TempFile; import java.io.*; +import java.util.List; import static the.bytecode.club.bytecodeviewer.Constants.FS; import static the.bytecode.club.bytecodeviewer.Constants.NL; @@ -53,12 +59,32 @@ public JDGUIDecompiler() super("JD-GUI Decompiler", "jdgui"); } + private String[] inners; @Override public String decompileClassNode(ClassNode cn, byte[] bytes) { TempFile tempFile = null; String exception; + List innerClasses = cn.innerClasses; + inners = new String[innerClasses.size()]; + for (int i = 0; i < innerClasses.size(); i++) + { + if (innerClasses.get(i).outerName != null && innerClasses.get(i).outerName.equals(cn.name)) + { + inners[i] = innerClasses.get(i).name; + } + else if (innerClasses.get(i).outerName == null) + { + String name = innerClasses.get(i).name; + name = name.substring(name.lastIndexOf('/') + 1); + if (name.contains(cn.name.substring(cn.name.lastIndexOf('/') + 1))) + { + inners[i] = innerClasses.get(i).name; + } + } + } + try { //create the temporary files @@ -75,9 +101,33 @@ public String decompileClassNode(ClassNode cn, byte[] bytes) fos.write(bytes); } + // create the inner class temp files + File innerTempFile; + for (ResourceContainer container : BytecodeViewer.resourceContainers.values()) + { + for (String s : container.resourceClasses.keySet()) + { + for (String innerClassName : inners) + { + if (s.equals(innerClassName)) + { + ClassNode cn2 = container.resourceClasses.get(innerClassName); + tempFile.setUniqueName(cn2.name); + innerTempFile = tempFile.createFileFromExtension(false, false, ".class"); + try (FileOutputStream fos = new FileOutputStream(innerTempFile)) + { + fos.write(ASMUtil.nodeToBytes(cn2)); + } + } + } + } + } + String pathToClass = tempClassFile.getAbsolutePath().replace('/', File.separatorChar).replace('\\', File.separatorChar); String directoryPath = JDGUIClassFileUtil.ExtractDirectoryPath(pathToClass); - String internalPath = JDGUIClassFileUtil.ExtractInternalPath(directoryPath, pathToClass); + String internalPath = FilenameUtils.removeExtension(JDGUIClassFileUtil.ExtractInternalPath(directoryPath, + pathToClass)); + CommonPreferences preferences = new CommonPreferences() { diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/jdgui/DirectoryLoader.java b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/jdgui/DirectoryLoader.java index 882b56521..ba5a64ba4 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/jdgui/DirectoryLoader.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/jdgui/DirectoryLoader.java @@ -46,6 +46,9 @@ public DirectoryLoader(File file) throws LoaderException @Override public byte[] load(String internalPath) throws LoaderException { + if (!internalPath.endsWith(".class")) + internalPath = internalPath + ".class"; + File file = new File(this.codebase, internalPath); try (FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis)) @@ -61,7 +64,7 @@ public byte[] load(String internalPath) throws LoaderException @Override public boolean canLoad(String internalPath) { - File file = new File(this.codebase, internalPath); + File file = new File(this.codebase, internalPath + ".class"); return file.exists() && file.isFile(); } } From 756c82334f7e465c97f77bf77c59c8b81f53a475 Mon Sep 17 00:00:00 2001 From: Cody <6558800+Bl3nd@users.noreply.github.com> Date: Thu, 17 Oct 2024 21:10:14 -0600 Subject: [PATCH 10/12] [JDGUI] Make sure we do not add the opened class into the inner classes array. --- .../bytecodeviewer/decompilers/impl/JDGUIDecompiler.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/JDGUIDecompiler.java b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/JDGUIDecompiler.java index e3ac9948a..78951852b 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/JDGUIDecompiler.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/JDGUIDecompiler.java @@ -59,7 +59,6 @@ public JDGUIDecompiler() super("JD-GUI Decompiler", "jdgui"); } - private String[] inners; @Override public String decompileClassNode(ClassNode cn, byte[] bytes) { @@ -67,9 +66,12 @@ public String decompileClassNode(ClassNode cn, byte[] bytes) String exception; List innerClasses = cn.innerClasses; - inners = new String[innerClasses.size()]; + String[] inners = new String[innerClasses.size()]; for (int i = 0; i < innerClasses.size(); i++) { + if (innerClasses.get(i).name.equals(cn.name)) + break; + if (innerClasses.get(i).outerName != null && innerClasses.get(i).outerName.equals(cn.name)) { inners[i] = innerClasses.get(i).name; @@ -79,9 +81,7 @@ else if (innerClasses.get(i).outerName == null) String name = innerClasses.get(i).name; name = name.substring(name.lastIndexOf('/') + 1); if (name.contains(cn.name.substring(cn.name.lastIndexOf('/') + 1))) - { inners[i] = innerClasses.get(i).name; - } } } From 00340ecd1c0b6b0683303d2658871f73c5d110d3 Mon Sep 17 00:00:00 2001 From: Cody <6558800+Bl3nd@users.noreply.github.com> Date: Thu, 17 Oct 2024 21:22:13 -0600 Subject: [PATCH 11/12] Remove test field on FernFlowerDecompiler generateMainMethod --- .../impl/FernFlowerDecompiler.java | 68 ++++++++++--------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/FernFlowerDecompiler.java b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/FernFlowerDecompiler.java index 3c2398eac..71ca66b93 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/FernFlowerDecompiler.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/FernFlowerDecompiler.java @@ -59,6 +59,7 @@ public FernFlowerDecompiler() private String[] inners; private final List innerFiles = new ArrayList<>(); + @Override public String decompileClassNode(ClassNode cn, byte[] bytes) { @@ -88,19 +89,24 @@ else if (innerClasses.get(i).outerName == null) } } - for (ResourceContainer container :BytecodeViewer.resourceContainers.values()) { + for (ResourceContainer container : BytecodeViewer.resourceContainers.values()) + { container.resourceClasses.forEach((s, classNode) -> { - for (String innerClassName : inners) { - if (s.equals(innerClassName)) { + for (String innerClassName : inners) + { + if (s.equals(innerClassName)) + { innerTempFile.set(TempFile.createTemporaryFile(true, ".class")); File tempInputClassFile2 = innerTempFile.get().getFile(); - try (FileOutputStream fos = new FileOutputStream(tempInputClassFile2)) { + try (FileOutputStream fos = new FileOutputStream(tempInputClassFile2)) + { fos.write(ASMUtil.nodeToBytes(classNode)); } catch (IOException e) { throw new RuntimeException(e); - } finally + } + finally { innerFiles.add(tempInputClassFile2); innerTempFile.get().markAsCreatedFile(tempInputClassFile2); @@ -135,7 +141,7 @@ else if (innerClasses.get(i).outerName == null) { ExternalResources.getSingleton().getJavaCommand(true), "-jar", ExternalResources.getSingleton().findLibrary("fernflower") - }, generateMainMethod(tempInputClassFile.getAbsolutePath(), "", tempFile.getParent().getAbsolutePath()) + }, generateMainMethod(tempInputClassFile.getAbsolutePath(), tempFile.getParent().getAbsolutePath()) ), false); } else @@ -170,7 +176,7 @@ else if (innerClasses.get(i).outerName == null) finally { //cleanup temp files - if(tempFile != null) + if (tempFile != null) tempFile.cleanup(); if (innerTempFile.get() != null) @@ -202,7 +208,7 @@ public void decompileToZip(String sourceJar, String zipName) try { - ConsoleDecompiler.main(generateMainMethod(tempInputJarFile.getAbsolutePath(), "", TEMP_DIRECTORY + "./temp/")); + ConsoleDecompiler.main(generateMainMethod(tempInputJarFile.getAbsolutePath(), TEMP_DIRECTORY + "./temp/")); } catch (StackOverflowError | Exception ignored) { @@ -248,31 +254,31 @@ private List generate(String className, String folder) return strings; } - private String[] generateMainMethod(String className, String test, String folder) + private String[] generateMainMethod(String className, String folder) { return new String[] - { - "-rbr=" + ffOnValue(BytecodeViewer.viewer.rbr.isSelected()), - "-rsy=" + ffOnValue(BytecodeViewer.viewer.rsy.isSelected()), - "-din=" + ffOnValue(BytecodeViewer.viewer.din.isSelected()), - "-dc4=" + ffOnValue(BytecodeViewer.viewer.dc4.isSelected()), - "-das=" + ffOnValue(BytecodeViewer.viewer.das.isSelected()), - "-hes=" + ffOnValue(BytecodeViewer.viewer.hes.isSelected()), - "-hdc=" + ffOnValue(BytecodeViewer.viewer.hdc.isSelected()), - "-dgs=" + ffOnValue(BytecodeViewer.viewer.dgs.isSelected()), - "-ner=" + ffOnValue(BytecodeViewer.viewer.ner.isSelected()), - "-den=" + ffOnValue(BytecodeViewer.viewer.den.isSelected()), - "-rgn=" + ffOnValue(BytecodeViewer.viewer.rgn.isSelected()), - "-bto=" + ffOnValue(BytecodeViewer.viewer.bto.isSelected()), - "-nns=" + ffOnValue(BytecodeViewer.viewer.nns.isSelected()), - "-uto=" + ffOnValue(BytecodeViewer.viewer.uto.isSelected()), - "-udv=" + ffOnValue(BytecodeViewer.viewer.udv.isSelected()), - "-rer=" + ffOnValue(BytecodeViewer.viewer.rer.isSelected()), - "-fdi=" + ffOnValue(BytecodeViewer.viewer.fdi.isSelected()), - "-asc=" + ffOnValue(BytecodeViewer.viewer.asc.isSelected()), - "-ren=" + ffOnValue(BytecodeViewer.viewer.ren.isSelected()), - className, test, folder - }; + { + "-rbr=" + ffOnValue(BytecodeViewer.viewer.rbr.isSelected()), + "-rsy=" + ffOnValue(BytecodeViewer.viewer.rsy.isSelected()), + "-din=" + ffOnValue(BytecodeViewer.viewer.din.isSelected()), + "-dc4=" + ffOnValue(BytecodeViewer.viewer.dc4.isSelected()), + "-das=" + ffOnValue(BytecodeViewer.viewer.das.isSelected()), + "-hes=" + ffOnValue(BytecodeViewer.viewer.hes.isSelected()), + "-hdc=" + ffOnValue(BytecodeViewer.viewer.hdc.isSelected()), + "-dgs=" + ffOnValue(BytecodeViewer.viewer.dgs.isSelected()), + "-ner=" + ffOnValue(BytecodeViewer.viewer.ner.isSelected()), + "-den=" + ffOnValue(BytecodeViewer.viewer.den.isSelected()), + "-rgn=" + ffOnValue(BytecodeViewer.viewer.rgn.isSelected()), + "-bto=" + ffOnValue(BytecodeViewer.viewer.bto.isSelected()), + "-nns=" + ffOnValue(BytecodeViewer.viewer.nns.isSelected()), + "-uto=" + ffOnValue(BytecodeViewer.viewer.uto.isSelected()), + "-udv=" + ffOnValue(BytecodeViewer.viewer.udv.isSelected()), + "-rer=" + ffOnValue(BytecodeViewer.viewer.rer.isSelected()), + "-fdi=" + ffOnValue(BytecodeViewer.viewer.fdi.isSelected()), + "-asc=" + ffOnValue(BytecodeViewer.viewer.asc.isSelected()), + "-ren=" + ffOnValue(BytecodeViewer.viewer.ren.isSelected()), + className, folder + }; } private String ffOnValue(boolean b) From 9356ebfcaa509b63a1be6fc4cc8129d7ea4335d7 Mon Sep 17 00:00:00 2001 From: Cody <6558800+Bl3nd@users.noreply.github.com> Date: Thu, 17 Oct 2024 21:27:26 -0600 Subject: [PATCH 12/12] Print error message if an exception is thrown for FieldAccessParser --- .../classcontainer/parser/visitors/FieldAccessParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java index 188e63a31..98a6918ae 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/visitors/FieldAccessParser.java @@ -70,7 +70,7 @@ else if (vd.isParameter()) } catch (Exception e) { - throw new RuntimeException(e); + printException(expr, e); } } }