diff options
Diffstat (limited to 'src/main/java/cuchaz/enigma/source/cfr')
3 files changed, 573 insertions, 0 deletions
diff --git a/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java b/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java new file mode 100644 index 0000000..9e37f16 --- /dev/null +++ b/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java | |||
| @@ -0,0 +1,108 @@ | |||
| 1 | package cuchaz.enigma.source.cfr; | ||
| 2 | |||
| 3 | import com.google.common.io.ByteStreams; | ||
| 4 | import cuchaz.enigma.ClassProvider; | ||
| 5 | import cuchaz.enigma.source.Decompiler; | ||
| 6 | import cuchaz.enigma.source.Source; | ||
| 7 | import cuchaz.enigma.source.SourceSettings; | ||
| 8 | import org.benf.cfr.reader.apiunreleased.ClassFileSource2; | ||
| 9 | import org.benf.cfr.reader.apiunreleased.JarContent; | ||
| 10 | import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair; | ||
| 11 | import org.benf.cfr.reader.entities.ClassFile; | ||
| 12 | import org.benf.cfr.reader.mapping.MappingFactory; | ||
| 13 | import org.benf.cfr.reader.mapping.ObfuscationMapping; | ||
| 14 | import org.benf.cfr.reader.relationship.MemberNameResolver; | ||
| 15 | import org.benf.cfr.reader.state.DCCommonState; | ||
| 16 | import org.benf.cfr.reader.state.TypeUsageCollectingDumper; | ||
| 17 | import org.benf.cfr.reader.util.AnalysisType; | ||
| 18 | import org.benf.cfr.reader.util.CannotLoadClassException; | ||
| 19 | import org.benf.cfr.reader.util.collections.ListFactory; | ||
| 20 | import org.benf.cfr.reader.util.getopt.Options; | ||
| 21 | import org.benf.cfr.reader.util.getopt.OptionsImpl; | ||
| 22 | import org.objectweb.asm.ClassWriter; | ||
| 23 | import org.objectweb.asm.tree.ClassNode; | ||
| 24 | |||
| 25 | import java.io.IOException; | ||
| 26 | import java.io.InputStream; | ||
| 27 | import java.util.Collection; | ||
| 28 | import java.util.HashMap; | ||
| 29 | import java.util.Map; | ||
| 30 | |||
| 31 | |||
| 32 | public class CfrDecompiler implements Decompiler { | ||
| 33 | private final DCCommonState state; | ||
| 34 | |||
| 35 | public CfrDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) { | ||
| 36 | Map<String, String> options = new HashMap<>(); | ||
| 37 | |||
| 38 | state = new DCCommonState(OptionsImpl.getFactory().create(options), new ClassFileSource2() { | ||
| 39 | @Override | ||
| 40 | public JarContent addJarContent(String s, AnalysisType analysisType) { | ||
| 41 | return null; | ||
| 42 | } | ||
| 43 | |||
| 44 | @Override | ||
| 45 | public void informAnalysisRelativePathDetail(String usePath, String classFilePath) { | ||
| 46 | |||
| 47 | } | ||
| 48 | |||
| 49 | @Override | ||
| 50 | public Collection<String> addJar(String jarPath) { | ||
| 51 | return null; | ||
| 52 | } | ||
| 53 | |||
| 54 | @Override | ||
| 55 | public String getPossiblyRenamedPath(String path) { | ||
| 56 | return path; | ||
| 57 | } | ||
| 58 | |||
| 59 | @Override | ||
| 60 | public Pair<byte[], String> getClassFileContent(String path) { | ||
| 61 | ClassNode node = classProvider.getClassNode(path.substring(0, path.lastIndexOf('.'))); | ||
| 62 | |||
| 63 | if (node == null) { | ||
| 64 | try (InputStream classResource = CfrDecompiler.class.getClassLoader().getResourceAsStream(path)) { | ||
| 65 | if (classResource != null) { | ||
| 66 | return new Pair<>(ByteStreams.toByteArray(classResource), path); | ||
| 67 | } | ||
| 68 | } catch (IOException ignored) {} | ||
| 69 | |||
| 70 | return null; | ||
| 71 | } | ||
| 72 | |||
| 73 | ClassWriter cw = new ClassWriter(0); | ||
| 74 | node.accept(cw); | ||
| 75 | return new Pair<>(cw.toByteArray(), path); | ||
| 76 | } | ||
| 77 | }); | ||
| 78 | } | ||
| 79 | |||
| 80 | @Override | ||
| 81 | public Source getSource(String className) { | ||
| 82 | DCCommonState state = this.state; | ||
| 83 | Options options = state.getOptions(); | ||
| 84 | |||
| 85 | ObfuscationMapping mapping = MappingFactory.get(options, state); | ||
| 86 | state = new DCCommonState(state, mapping); | ||
| 87 | ClassFile tree = state.getClassFileMaybePath(className); | ||
| 88 | |||
| 89 | state.configureWith(tree); | ||
| 90 | |||
| 91 | // To make sure we're analysing the cached version | ||
| 92 | try { | ||
| 93 | tree = state.getClassFile(tree.getClassType()); | ||
| 94 | } catch (CannotLoadClassException ignored) {} | ||
| 95 | |||
| 96 | if (options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)) { | ||
| 97 | tree.loadInnerClasses(state); | ||
| 98 | } | ||
| 99 | |||
| 100 | if (options.getOption(OptionsImpl.RENAME_DUP_MEMBERS)) { | ||
| 101 | MemberNameResolver.resolveNames(state, ListFactory.newList(state.getClassCache().getLoadedTypes())); | ||
| 102 | } | ||
| 103 | |||
| 104 | TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree); | ||
| 105 | tree.analyseTop(state, typeUsageCollector); | ||
| 106 | return new CfrSource(tree, state, typeUsageCollector.getRealTypeUsageInformation()); | ||
| 107 | } | ||
| 108 | } | ||
diff --git a/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java b/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java new file mode 100644 index 0000000..d4f2da6 --- /dev/null +++ b/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | package cuchaz.enigma.source.cfr; | ||
| 2 | |||
| 3 | import cuchaz.enigma.source.Source; | ||
| 4 | import cuchaz.enigma.source.SourceIndex; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 6 | import org.benf.cfr.reader.entities.ClassFile; | ||
| 7 | import org.benf.cfr.reader.state.DCCommonState; | ||
| 8 | import org.benf.cfr.reader.state.TypeUsageInformation; | ||
| 9 | |||
| 10 | public class CfrSource implements Source { | ||
| 11 | private final ClassFile tree; | ||
| 12 | private final SourceIndex index; | ||
| 13 | private final String string; | ||
| 14 | |||
| 15 | public CfrSource(ClassFile tree, DCCommonState state, TypeUsageInformation typeUsages) { | ||
| 16 | this.tree = tree; | ||
| 17 | |||
| 18 | EnigmaDumper dumper = new EnigmaDumper(typeUsages); | ||
| 19 | tree.dump(state.getObfuscationMapping().wrap(dumper)); | ||
| 20 | index = dumper.getIndex(); | ||
| 21 | string = dumper.getString(); | ||
| 22 | } | ||
| 23 | |||
| 24 | @Override | ||
| 25 | public String asString() { | ||
| 26 | return string; | ||
| 27 | } | ||
| 28 | |||
| 29 | @Override | ||
| 30 | public Source addJavadocs(EntryRemapper remapper) { | ||
| 31 | return this; // TODO | ||
| 32 | } | ||
| 33 | |||
| 34 | @Override | ||
| 35 | public SourceIndex index() { | ||
| 36 | return index; | ||
| 37 | } | ||
| 38 | } | ||
diff --git a/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java b/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java new file mode 100644 index 0000000..b9cdbea --- /dev/null +++ b/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java | |||
| @@ -0,0 +1,427 @@ | |||
| 1 | package cuchaz.enigma.source.cfr; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.Token; | ||
| 4 | import cuchaz.enigma.source.SourceIndex; | ||
| 5 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 6 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 8 | import org.benf.cfr.reader.bytecode.analysis.types.*; | ||
| 9 | import org.benf.cfr.reader.bytecode.analysis.variables.NamedVariable; | ||
| 10 | import org.benf.cfr.reader.entities.Field; | ||
| 11 | import org.benf.cfr.reader.entities.Method; | ||
| 12 | import org.benf.cfr.reader.mapping.NullMapping; | ||
| 13 | import org.benf.cfr.reader.mapping.ObfuscationMapping; | ||
| 14 | import org.benf.cfr.reader.state.TypeUsageInformation; | ||
| 15 | import org.benf.cfr.reader.util.collections.SetFactory; | ||
| 16 | import org.benf.cfr.reader.util.output.DelegatingDumper; | ||
| 17 | import org.benf.cfr.reader.util.output.Dumpable; | ||
| 18 | import org.benf.cfr.reader.util.output.Dumper; | ||
| 19 | import org.benf.cfr.reader.util.output.TypeContext; | ||
| 20 | |||
| 21 | import java.util.Set; | ||
| 22 | import java.util.stream.Collectors; | ||
| 23 | |||
| 24 | public class EnigmaDumper implements Dumper { | ||
| 25 | private int outputCount = 0; | ||
| 26 | private int indent; | ||
| 27 | private boolean atStart = true; | ||
| 28 | private boolean pendingCR = false; | ||
| 29 | private final StringBuilder sb = new StringBuilder(); | ||
| 30 | private final TypeUsageInformation typeUsageInformation; | ||
| 31 | private final Set<JavaTypeInstance> emitted = SetFactory.newSet(); | ||
| 32 | private final SourceIndex index = new SourceIndex(); | ||
| 33 | private int position; | ||
| 34 | |||
| 35 | |||
| 36 | public EnigmaDumper(TypeUsageInformation typeUsageInformation) { | ||
| 37 | this.typeUsageInformation = typeUsageInformation; | ||
| 38 | } | ||
| 39 | |||
| 40 | private void append(String s) { | ||
| 41 | sb.append(s); | ||
| 42 | position += s.length(); | ||
| 43 | } | ||
| 44 | |||
| 45 | private String getDesc(JavaTypeInstance type) { | ||
| 46 | type = type.getDeGenerifiedType(); | ||
| 47 | |||
| 48 | if (type instanceof JavaRefTypeInstance) { | ||
| 49 | return "L" + type.getRawName().replace('.', '/') + ";"; | ||
| 50 | } | ||
| 51 | |||
| 52 | if (type instanceof JavaArrayTypeInstance) { | ||
| 53 | return "[" + getDesc(((JavaArrayTypeInstance) type).removeAnArrayIndirection()); | ||
| 54 | } | ||
| 55 | |||
| 56 | if (type instanceof RawJavaType) { | ||
| 57 | switch ((RawJavaType) type) { | ||
| 58 | case BOOLEAN: | ||
| 59 | return "Z"; | ||
| 60 | case BYTE: | ||
| 61 | return "B"; | ||
| 62 | case CHAR: | ||
| 63 | return "C"; | ||
| 64 | case SHORT: | ||
| 65 | return "S"; | ||
| 66 | case INT: | ||
| 67 | return "I"; | ||
| 68 | case LONG: | ||
| 69 | return "J"; | ||
| 70 | case FLOAT: | ||
| 71 | return "F"; | ||
| 72 | case DOUBLE: | ||
| 73 | return "D"; | ||
| 74 | case VOID: | ||
| 75 | return "V"; | ||
| 76 | default: | ||
| 77 | throw new AssertionError(); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | throw new AssertionError(); | ||
| 82 | } | ||
| 83 | |||
| 84 | private MethodEntry getMethodEntry(MethodPrototype method) { | ||
| 85 | if (method.getClassType() == null) { | ||
| 86 | return null; | ||
| 87 | } | ||
| 88 | |||
| 89 | MethodDescriptor desc = new MethodDescriptor( | ||
| 90 | method.getArgs().stream().map(type -> new TypeDescriptor(getDesc(type))).collect(Collectors.toList()), | ||
| 91 | new TypeDescriptor(method.getName().equals("<init>") || method.getName().equals("<clinit>") ? "V" : getDesc(method.getReturnType())) | ||
| 92 | ); | ||
| 93 | |||
| 94 | return new MethodEntry(getClassEntry(method.getClassType()), method.getName(), desc); | ||
| 95 | } | ||
| 96 | |||
| 97 | private LocalVariableEntry getParameterEntry(MethodPrototype method, int parameterIndex, String name) { | ||
| 98 | int variableIndex = method.isInstanceMethod() ? 1 : 0; | ||
| 99 | for (int i = 0; i < parameterIndex; i++) { | ||
| 100 | variableIndex += method.getArgs().get(parameterIndex).getStackType().getComputationCategory(); | ||
| 101 | } | ||
| 102 | |||
| 103 | return new LocalVariableEntry(getMethodEntry(method), variableIndex, name, true, null); | ||
| 104 | } | ||
| 105 | |||
| 106 | private FieldEntry getFieldEntry(JavaTypeInstance owner, String name, JavaTypeInstance type) { | ||
| 107 | return new FieldEntry(getClassEntry(owner), name, new TypeDescriptor(getDesc(type))); | ||
| 108 | } | ||
| 109 | |||
| 110 | private ClassEntry getClassEntry(JavaTypeInstance type) { | ||
| 111 | return new ClassEntry(type.getRawName().replace('.', '/')); | ||
| 112 | } | ||
| 113 | |||
| 114 | @Override | ||
| 115 | public Dumper beginBlockComment(boolean inline) { | ||
| 116 | print("/*").newln(); | ||
| 117 | return this; | ||
| 118 | } | ||
| 119 | |||
| 120 | @Override | ||
| 121 | public Dumper endBlockComment() { | ||
| 122 | print(" */").newln(); | ||
| 123 | return this; | ||
| 124 | } | ||
| 125 | |||
| 126 | @Override | ||
| 127 | public Dumper label(String s, boolean inline) { | ||
| 128 | processPendingCR(); | ||
| 129 | append(s); | ||
| 130 | append(":"); | ||
| 131 | return this; | ||
| 132 | } | ||
| 133 | |||
| 134 | @Override | ||
| 135 | public Dumper comment(String s) { | ||
| 136 | append("// "); | ||
| 137 | append(s); | ||
| 138 | append("\n"); | ||
| 139 | return this; | ||
| 140 | } | ||
| 141 | |||
| 142 | @Override | ||
| 143 | public void enqueuePendingCarriageReturn() { | ||
| 144 | pendingCR = true; | ||
| 145 | } | ||
| 146 | |||
| 147 | @Override | ||
| 148 | public Dumper removePendingCarriageReturn() { | ||
| 149 | pendingCR = false; | ||
| 150 | return this; | ||
| 151 | } | ||
| 152 | |||
| 153 | private void processPendingCR() { | ||
| 154 | if (pendingCR) { | ||
| 155 | append("\n"); | ||
| 156 | atStart = true; | ||
| 157 | pendingCR = false; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | @Override | ||
| 162 | public Dumper identifier(String s, Object ref, boolean defines) { | ||
| 163 | return print(s); | ||
| 164 | } | ||
| 165 | |||
| 166 | @Override | ||
| 167 | public Dumper methodName(String name, MethodPrototype method, boolean special, boolean defines) { | ||
| 168 | doIndent(); | ||
| 169 | Token token = new Token(position, position + name.length(), name); | ||
| 170 | Entry<?> entry = getMethodEntry(method); | ||
| 171 | |||
| 172 | if (entry != null) { | ||
| 173 | if (defines) { | ||
| 174 | index.addDeclaration(token, entry); | ||
| 175 | } else { | ||
| 176 | index.addReference(token, entry, null); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | return identifier(name, null, defines); | ||
| 181 | } | ||
| 182 | |||
| 183 | @Override | ||
| 184 | public Dumper parameterName(String name, MethodPrototype method, int index, boolean defines) { | ||
| 185 | doIndent(); | ||
| 186 | Token token = new Token(position, position + name.length(), name); | ||
| 187 | Entry<?> entry = getParameterEntry(method, index, name); | ||
| 188 | |||
| 189 | if (entry != null) { | ||
| 190 | if (defines) { | ||
| 191 | this.index.addDeclaration(token, entry); | ||
| 192 | } else { | ||
| 193 | this.index.addReference(token, entry, null); | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | return identifier(name, null, defines); | ||
| 198 | } | ||
| 199 | |||
| 200 | @Override | ||
| 201 | public Dumper variableName(String name, NamedVariable variable, boolean defines) { | ||
| 202 | return identifier(name, null, defines); | ||
| 203 | } | ||
| 204 | |||
| 205 | @Override | ||
| 206 | public Dumper packageName(JavaRefTypeInstance t) { | ||
| 207 | String s = t.getPackageName(); | ||
| 208 | |||
| 209 | if (!s.isEmpty()) { | ||
| 210 | keyword("package ").print(s).endCodeln().newln(); | ||
| 211 | } | ||
| 212 | |||
| 213 | return this; | ||
| 214 | } | ||
| 215 | |||
| 216 | @Override | ||
| 217 | public Dumper fieldName(String name, Field field, JavaTypeInstance owner, boolean hiddenDeclaration, boolean defines) { | ||
| 218 | doIndent(); | ||
| 219 | Token token = new Token(position, position + name.length(), name); | ||
| 220 | Entry<?> entry = field == null ? null : getFieldEntry(owner, name, field.getJavaTypeInstance()); | ||
| 221 | |||
| 222 | if (entry != null) { | ||
| 223 | if (defines) { | ||
| 224 | index.addDeclaration(token, entry); | ||
| 225 | } else { | ||
| 226 | index.addReference(token, entry, null); | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | identifier(name, null, defines); | ||
| 231 | return this; | ||
| 232 | } | ||
| 233 | |||
| 234 | @Override | ||
| 235 | public Dumper print(String s) { | ||
| 236 | processPendingCR(); | ||
| 237 | doIndent(); | ||
| 238 | append(s); | ||
| 239 | atStart = s.endsWith("\n"); | ||
| 240 | outputCount++; | ||
| 241 | return this; | ||
| 242 | } | ||
| 243 | |||
| 244 | @Override | ||
| 245 | public Dumper print(char c) { | ||
| 246 | return print(String.valueOf(c)); | ||
| 247 | } | ||
| 248 | |||
| 249 | @Override | ||
| 250 | public Dumper newln() { | ||
| 251 | append("\n"); | ||
| 252 | atStart = true; | ||
| 253 | outputCount++; | ||
| 254 | return this; | ||
| 255 | } | ||
| 256 | |||
| 257 | @Override | ||
| 258 | public Dumper endCodeln() { | ||
| 259 | append(";\n"); | ||
| 260 | atStart = true; | ||
| 261 | outputCount++; | ||
| 262 | return this; | ||
| 263 | } | ||
| 264 | |||
| 265 | @Override | ||
| 266 | public Dumper keyword(String s) { | ||
| 267 | print(s); | ||
| 268 | return this; | ||
| 269 | } | ||
| 270 | |||
| 271 | @Override | ||
| 272 | public Dumper operator(String s) { | ||
| 273 | print(s); | ||
| 274 | return this; | ||
| 275 | } | ||
| 276 | |||
| 277 | @Override | ||
| 278 | public Dumper separator(String s) { | ||
| 279 | print(s); | ||
| 280 | return this; | ||
| 281 | } | ||
| 282 | |||
| 283 | @Override | ||
| 284 | public Dumper literal(String s, Object o) { | ||
| 285 | print(s); | ||
| 286 | return this; | ||
| 287 | } | ||
| 288 | |||
| 289 | private void doIndent() { | ||
| 290 | if (!atStart) return; | ||
| 291 | String indents = " "; | ||
| 292 | |||
| 293 | for (int x = 0; x < indent; ++x) { | ||
| 294 | append(indents); | ||
| 295 | } | ||
| 296 | |||
| 297 | atStart = false; | ||
| 298 | } | ||
| 299 | |||
| 300 | @Override | ||
| 301 | public void indent(int diff) { | ||
| 302 | indent += diff; | ||
| 303 | } | ||
| 304 | |||
| 305 | @Override | ||
| 306 | public Dumper dump(Dumpable d) { | ||
| 307 | if (d == null) { | ||
| 308 | keyword("null"); | ||
| 309 | return this; | ||
| 310 | } | ||
| 311 | |||
| 312 | d.dump(this); | ||
| 313 | return this; | ||
| 314 | } | ||
| 315 | |||
| 316 | @Override | ||
| 317 | public TypeUsageInformation getTypeUsageInformation() { | ||
| 318 | return typeUsageInformation; | ||
| 319 | } | ||
| 320 | |||
| 321 | @Override | ||
| 322 | public ObfuscationMapping getObfuscationMapping() { | ||
| 323 | return NullMapping.INSTANCE; | ||
| 324 | } | ||
| 325 | |||
| 326 | @Override | ||
| 327 | public String toString() { | ||
| 328 | return sb.toString(); | ||
| 329 | } | ||
| 330 | |||
| 331 | @Override | ||
| 332 | public void addSummaryError(Method method, String s) {} | ||
| 333 | |||
| 334 | @Override | ||
| 335 | public void close() { | ||
| 336 | } | ||
| 337 | |||
| 338 | @Override | ||
| 339 | public boolean canEmitClass(JavaTypeInstance type) { | ||
| 340 | return emitted.add(type); | ||
| 341 | } | ||
| 342 | |||
| 343 | @Override | ||
| 344 | public int getOutputCount() { | ||
| 345 | return outputCount; | ||
| 346 | } | ||
| 347 | |||
| 348 | @Override | ||
| 349 | public Dumper dump(JavaTypeInstance type) { | ||
| 350 | return dump(type, TypeContext.None, false); | ||
| 351 | } | ||
| 352 | |||
| 353 | @Override | ||
| 354 | public Dumper dump(JavaTypeInstance type, boolean defines) { | ||
| 355 | return dump(type, TypeContext.None, false); | ||
| 356 | } | ||
| 357 | |||
| 358 | @Override | ||
| 359 | public Dumper dump(JavaTypeInstance type, TypeContext context) { | ||
| 360 | return dump(type, context, false); | ||
| 361 | } | ||
| 362 | |||
| 363 | private Dumper dump(JavaTypeInstance type, TypeContext context, boolean defines) { | ||
| 364 | doIndent(); | ||
| 365 | if (type instanceof JavaRefTypeInstance) { | ||
| 366 | int start = position; | ||
| 367 | type.dumpInto(this, typeUsageInformation, TypeContext.None); | ||
| 368 | int end = position; | ||
| 369 | Token token = new Token(start, end, sb.toString().substring(start, end)); | ||
| 370 | |||
| 371 | if (defines) { | ||
| 372 | index.addDeclaration(token, getClassEntry(type)); | ||
| 373 | } else { | ||
| 374 | index.addReference(token, getClassEntry(type), null); | ||
| 375 | } | ||
| 376 | |||
| 377 | return this; | ||
| 378 | } | ||
| 379 | |||
| 380 | type.dumpInto(this, typeUsageInformation, context); | ||
| 381 | return this; | ||
| 382 | } | ||
| 383 | |||
| 384 | @Override | ||
| 385 | public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) { | ||
| 386 | return new WithTypeUsageInformationDumper(this, innerclassTypeUsageInformation); | ||
| 387 | } | ||
| 388 | |||
| 389 | public SourceIndex getIndex() { | ||
| 390 | index.setSource(getString()); | ||
| 391 | return index; | ||
| 392 | } | ||
| 393 | |||
| 394 | public String getString() { | ||
| 395 | return sb.toString(); | ||
| 396 | } | ||
| 397 | |||
| 398 | public static class WithTypeUsageInformationDumper extends DelegatingDumper { | ||
| 399 | private final TypeUsageInformation typeUsageInformation; | ||
| 400 | |||
| 401 | WithTypeUsageInformationDumper(Dumper delegate, TypeUsageInformation typeUsageInformation) { | ||
| 402 | super(delegate); | ||
| 403 | this.typeUsageInformation = typeUsageInformation; | ||
| 404 | } | ||
| 405 | |||
| 406 | @Override | ||
| 407 | public TypeUsageInformation getTypeUsageInformation() { | ||
| 408 | return typeUsageInformation; | ||
| 409 | } | ||
| 410 | |||
| 411 | @Override | ||
| 412 | public Dumper dump(JavaTypeInstance javaTypeInstance) { | ||
| 413 | return dump(javaTypeInstance, TypeContext.None); | ||
| 414 | } | ||
| 415 | |||
| 416 | @Override | ||
| 417 | public Dumper dump(JavaTypeInstance javaTypeInstance, TypeContext typeContext) { | ||
| 418 | javaTypeInstance.dumpInto(this, typeUsageInformation, typeContext); | ||
| 419 | return this; | ||
| 420 | } | ||
| 421 | |||
| 422 | @Override | ||
| 423 | public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) { | ||
| 424 | return new WithTypeUsageInformationDumper(delegate, innerclassTypeUsageInformation); | ||
| 425 | } | ||
| 426 | } | ||
| 427 | } | ||