summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/analysis/Analyzer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/cuchaz/enigma/analysis/Analyzer.java')
-rw-r--r--src/cuchaz/enigma/analysis/Analyzer.java252
1 files changed, 252 insertions, 0 deletions
diff --git a/src/cuchaz/enigma/analysis/Analyzer.java b/src/cuchaz/enigma/analysis/Analyzer.java
new file mode 100644
index 0000000..1cdabe7
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/Analyzer.java
@@ -0,0 +1,252 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12
13import java.io.IOException;
14import java.util.Arrays;
15
16import javax.tools.JavaCompiler;
17import javax.tools.StandardJavaFileManager;
18import javax.tools.ToolProvider;
19
20import jsyntaxpane.Token;
21import jsyntaxpane.TokenType;
22
23import com.sun.source.tree.ArrayTypeTree;
24import com.sun.source.tree.ClassTree;
25import com.sun.source.tree.CompilationUnitTree;
26import com.sun.source.tree.IdentifierTree;
27import com.sun.source.tree.MethodTree;
28import com.sun.source.tree.PrimitiveTypeTree;
29import com.sun.source.tree.Tree;
30import com.sun.source.tree.Tree.Kind;
31import com.sun.source.tree.VariableTree;
32import com.sun.source.util.JavacTask;
33import com.sun.source.util.TreeScanner;
34import com.sun.source.util.Trees;
35
36import cuchaz.enigma.mapping.ArgumentEntry;
37import cuchaz.enigma.mapping.ClassEntry;
38import cuchaz.enigma.mapping.FieldEntry;
39import cuchaz.enigma.mapping.MethodEntry;
40
41class TreeVisitor extends TreeScanner<CompilationUnitTree, SourcedAst>
42{
43 private SourceIndex m_index;
44
45 public TreeVisitor( SourceIndex index )
46 {
47 m_index = index;
48 }
49
50 @Override
51 public CompilationUnitTree visitClass( ClassTree classTree, SourcedAst ast )
52 {
53 ClassEntry classEntry = indexClass( classTree, ast );
54
55 // look at the class members
56 for( Tree memberTree : classTree.getMembers() )
57 {
58 if( memberTree.getKind() == Kind.VARIABLE )
59 {
60 indexField( (VariableTree)memberTree, ast, classEntry );
61 }
62 else if( memberTree.getKind() == Kind.METHOD )
63 {
64 MethodTree methodTree = (MethodTree)memberTree;
65 MethodEntry methodEntry = indexMethod( methodTree, ast, classEntry );
66
67 // look at method arguments
68 int argNum = 0;
69 for( VariableTree variableTree : methodTree.getParameters() )
70 {
71 indexArgument( variableTree, ast, methodEntry, argNum++ );
72 }
73 }
74 }
75
76 return super.visitClass( classTree, ast );
77 }
78
79 private ClassEntry indexClass( ClassTree classTree, SourcedAst ast )
80 {
81 // build the entry
82 ClassEntry entry = new ClassEntry( ast.getFullClassName( classTree.getSimpleName().toString() ) );
83
84 // lex the source at this tree node
85 for( Token token : new Lexer( ast.getSource( classTree ).toString() ) )
86 {
87 // scan until we get the first identifier
88 if( token.type == TokenType.IDENTIFIER )
89 {
90 m_index.add( entry, offsetToken( token, ast.getStart( classTree ) ) );
91 break;
92 }
93 }
94
95 return entry;
96 }
97
98 private FieldEntry indexField( VariableTree variableTree, SourcedAst ast, ClassEntry classEntry )
99 {
100 // build the entry
101 FieldEntry entry = new FieldEntry( classEntry, variableTree.getName().toString() );
102
103 // lex the source at this tree node
104 Lexer lexer = new Lexer( ast.getSource( variableTree ).toString() );
105 for( Token token : lexer )
106 {
107 // scan until we find an identifier that matches the field name
108 if( token.type == TokenType.IDENTIFIER && lexer.getText( token ).equals( entry.getName() ) )
109 {
110 m_index.add( entry, offsetToken( token, ast.getStart( variableTree ) ) );
111 break;
112 }
113 }
114
115 return entry;
116 }
117
118 private MethodEntry indexMethod( MethodTree methodTree, SourcedAst ast, ClassEntry classEntry )
119 {
120 // build the entry
121 StringBuilder signature = new StringBuilder();
122 signature.append( "(" );
123 for( VariableTree variableTree : methodTree.getParameters() )
124 {
125 signature.append( toJvmType( variableTree.getType(), ast ) );
126 }
127 signature.append( ")" );
128 if( methodTree.getReturnType() != null )
129 {
130 signature.append( toJvmType( methodTree.getReturnType(), ast ) );
131 }
132 else
133 {
134 signature.append( "V" );
135 }
136 MethodEntry entry = new MethodEntry( classEntry, methodTree.getName().toString(), signature.toString() );
137
138 // lex the source at this tree node
139 Lexer lexer = new Lexer( ast.getSource( methodTree ).toString() );
140 for( Token token : lexer )
141 {
142 // scan until we find an identifier that matches the method name
143 if( token.type == TokenType.IDENTIFIER && lexer.getText( token ).equals( entry.getName() ) )
144 {
145 m_index.add( entry, offsetToken( token, ast.getStart( methodTree ) ) );
146 break;
147 }
148 }
149
150 return entry;
151 }
152
153 private void indexArgument( VariableTree variableTree, SourcedAst ast, MethodEntry methodEntry, int index )
154 {
155 System.out.println( "\tFound argument: " + variableTree.getName() );
156
157 // build the entry
158 ArgumentEntry entry = new ArgumentEntry( methodEntry, index, variableTree.getName().toString() );
159
160 // lex the source at this tree node
161 Lexer lexer = new Lexer( ast.getSource( variableTree ).toString() );
162 for( Token token : lexer )
163 {
164 // scan until we find an identifier that matches the variable name
165 if( token.type == TokenType.IDENTIFIER && lexer.getText( token ).equals( entry.getName() ) )
166 {
167 m_index.add( entry, offsetToken( token, ast.getStart( variableTree ) ) );
168 break;
169 }
170 }
171 }
172
173 private Token offsetToken( Token in, int offset )
174 {
175 return new Token( in.type, in.start + offset, in.length );
176 }
177
178 private String toJvmType( Tree tree, SourcedAst ast )
179 {
180 switch( tree.getKind() )
181 {
182 case PRIMITIVE_TYPE:
183 {
184 PrimitiveTypeTree primitiveTypeTree = (PrimitiveTypeTree)tree;
185 switch( primitiveTypeTree.getPrimitiveTypeKind() )
186 {
187 case BOOLEAN: return "Z";
188 case BYTE: return "B";
189 case CHAR: return "C";
190 case DOUBLE: return "D";
191 case FLOAT: return "F";
192 case INT: return "I";
193 case LONG: return "J";
194 case SHORT: return "S";
195 case VOID: return "V";
196
197 default:
198 throw new Error( "Unsupported primitive type: " + primitiveTypeTree.getPrimitiveTypeKind() );
199 }
200 }
201
202 case IDENTIFIER:
203 {
204 IdentifierTree identifierTree = (IdentifierTree)tree;
205 String className = identifierTree.getName().toString();
206 className = ast.getFullClassName( className );
207 return "L" + className.replace( ".", "/" ) + ";";
208 }
209
210 case ARRAY_TYPE:
211 {
212 ArrayTypeTree arrayTree = (ArrayTypeTree)tree;
213 return "[" + toJvmType( arrayTree.getType(), ast );
214 }
215
216
217 default:
218 throw new Error( "Unsupported type kind: " + tree.getKind() );
219 }
220 }
221}
222
223public class Analyzer
224{
225 public static SourceIndex analyze( String className, String source )
226 {
227 SourceIndex index = new SourceIndex();
228 SourcedAst ast = getAst( className, source );
229 ast.visit( new TreeVisitor( index ) );
230 return index;
231 }
232
233 private static SourcedAst getAst( String className, String source )
234 {
235 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
236 StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null );
237 JavaSourceFromString unit = new JavaSourceFromString( className, source );
238 JavacTask task = (JavacTask)compiler.getTask( null, fileManager, null, null, null, Arrays.asList( unit ) );
239
240 try
241 {
242 return new SourcedAst(
243 task.parse().iterator().next(),
244 Trees.instance( task )
245 );
246 }
247 catch( IOException ex )
248 {
249 throw new Error( ex );
250 }
251 }
252}