summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java
blob: 8accfc7c108492cf9944b35ce8503d33e0e94883 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package cuchaz.enigma.source.procyon.transformers;

import com.strobel.assembler.metadata.BuiltinTypes;
import com.strobel.assembler.metadata.CommonTypeReferences;
import com.strobel.assembler.metadata.Flags;
import com.strobel.assembler.metadata.IGenericInstance;
import com.strobel.assembler.metadata.IMemberDefinition;
import com.strobel.assembler.metadata.JvmType;
import com.strobel.assembler.metadata.MemberReference;
import com.strobel.assembler.metadata.MethodDefinition;
import com.strobel.assembler.metadata.TypeDefinition;
import com.strobel.assembler.metadata.TypeReference;
import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression;
import com.strobel.decompiler.languages.java.ast.AstNode;
import com.strobel.decompiler.languages.java.ast.AstNodeCollection;
import com.strobel.decompiler.languages.java.ast.AstType;
import com.strobel.decompiler.languages.java.ast.CastExpression;
import com.strobel.decompiler.languages.java.ast.ComposedType;
import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
import com.strobel.decompiler.languages.java.ast.Expression;
import com.strobel.decompiler.languages.java.ast.Identifier;
import com.strobel.decompiler.languages.java.ast.InvocationExpression;
import com.strobel.decompiler.languages.java.ast.Keys;
import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression;
import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression;
import com.strobel.decompiler.languages.java.ast.Roles;
import com.strobel.decompiler.languages.java.ast.SimpleType;
import com.strobel.decompiler.languages.java.ast.WildcardType;
import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;

/**
 * Created by Thiakil on 12/07/2018.
 */
public class Java8Generics implements IAstTransform {

	@Override
	public void run(AstNode compilationUnit) {
		compilationUnit.acceptVisitor(new Visitor(), null);
	}

	static class Visitor extends DepthFirstAstVisitor<Void,Void>{

		@Override
		public Void visitInvocationExpression(InvocationExpression node, Void data) {
			super.visitInvocationExpression(node, data);
			if (node.getTarget() instanceof MemberReferenceExpression){
				MemberReferenceExpression referenceExpression = (MemberReferenceExpression) node.getTarget();
				if (referenceExpression.getTypeArguments().stream().map(t->{
					TypeReference tr = t.toTypeReference();
					if (tr.getDeclaringType() != null){//ensure that inner types are resolved so we can get the TypeDefinition below
						TypeReference resolved = tr.resolve();
						if (resolved != null)
							return resolved;
					}
					return tr;
				}).anyMatch(t -> t.isWildcardType() || (t instanceof TypeDefinition && ((TypeDefinition) t).isAnonymous()))) {
					//these are invalid for invocations, let the compiler work it out
					referenceExpression.getTypeArguments().clear();
				} else if (referenceExpression.getTypeArguments().stream().allMatch(t->t.toTypeReference().equals(CommonTypeReferences.Object))){
					//all are <Object>, thereby redundant and/or bad
					referenceExpression.getTypeArguments().clear();
				}
			}
			return null;
		}

		@Override
		public Void visitObjectCreationExpression(ObjectCreationExpression node, Void data) {
			super.visitObjectCreationExpression(node, data);
			AstType type = node.getType();
			if (type instanceof SimpleType && !((SimpleType) type).getTypeArguments().isEmpty()){
				SimpleType simpleType = (SimpleType) type;
				AstNodeCollection<AstType> typeArguments = simpleType.getTypeArguments();
				if (typeArguments.size() == 1 && typeArguments.firstOrNullObject().toTypeReference().equals(CommonTypeReferences.Object)){
					//all are <Object>, thereby redundant and/or bad
					typeArguments.firstOrNullObject().getChildByRole(Roles.IDENTIFIER).replaceWith(Identifier.create(""));
				}
			}
			return null;
		}

		@Override
		public Void visitCastExpression(CastExpression node, Void data) {
			boolean doReplace = false;
			TypeReference typeReference = node.getType().toTypeReference();
			if (typeReference.isArray() && typeReference.getElementType().isGenericType()){
				doReplace = true;
			} else if (typeReference.isGenericType()) {
				Expression target = node.getExpression();
				if (typeReference instanceof IGenericInstance && ((IGenericInstance)typeReference).getTypeArguments().stream().anyMatch(t->t.isWildcardType())){
					doReplace = true;
				} else if (target instanceof InvocationExpression) {
					InvocationExpression invocationExpression = (InvocationExpression)target;
					if (invocationExpression.getTarget() instanceof MemberReferenceExpression && !((MemberReferenceExpression) invocationExpression.getTarget()).getTypeArguments().isEmpty()) {
						((MemberReferenceExpression) invocationExpression.getTarget()).getTypeArguments().clear();
						doReplace = true;
					}
				}
			}
			super.visitCastExpression(node, data);
			if (doReplace){
				node.replaceWith(node.getExpression());
			}
			return null;
		}
	}
}