private static void detectOuterClass(ClassInstance cls, ClassNode cn) { if (cn.outerClass != null) { addOuterClass(cls, cn.outerClass, true); } else if (cn.outerMethod != null) { throw new UnsupportedOperationException(); } else { // determine outer class by outer$inner name pattern for (InnerClassNode icn : cn.innerClasses) { if (icn.name.equals(cn.name)) { addOuterClass(cls, icn.outerName, true); return; } } int pos; if ((pos = cn.name.lastIndexOf('$')) > 0 && pos < cn.name.length() - 1) { addOuterClass(cls, cn.name.substring(0, pos), false); } } }
@Override public RequestMeta getMeta(RequestRegistry registry, Item rawItem) { if (!(rawItem instanceof AsmItem)) throw new UnsupportedOperationException(getClass().getName() + " only supports AsmItem"); AsmItem item = (AsmItem) rawItem; ClassNode node = item.getResult(); noPkgPrivate(node); Collection<Reference<AsmItem>> sgrefs = new HashSet<>(); for (InnerClassNode icn : node.innerClasses) { addNullableRef(sgrefs, new AsmInnerClassReference(item, icn, AsmInnerClassReference.RT_INNER_NAME, false)); addNullableRef(sgrefs, new AsmInnerClassReference(item, icn, AsmInnerClassReference.RT_OUTER_NAME, false)); } addNullableRef(sgrefs, new AsmOuterClassReference(item, node, false)); return new RequestMeta(rawItem, null, null, sgrefs, Collections.emptyList(), Collections.emptyList()); }
/** * Modifies a {@link ClassNode} to clear final flags and rewrite byte code. */ @SuppressWarnings("unchecked") private void modifyClass(ClassNode classNode) { // Make the class not final. classNode.access &= ~Opcodes.ACC_FINAL; List<MethodNode> methodNodes = classNode.methods; for (MethodNode methodNode : methodNodes) { methodNode.access &= ~Opcodes.ACC_FINAL; fixMethodBody(methodNode, classNode); } List<FieldNode> fieldNodes = classNode.fields; for (FieldNode fieldNode : fieldNodes) { // Make public instance fields non-final. This is needed e.g. to "mock" SyncResult.stats. if ((fieldNode.access & Opcodes.ACC_PUBLIC) != 0 && (fieldNode.access & Opcodes.ACC_STATIC) == 0) { fieldNode.access &= ~Opcodes.ACC_FINAL; } } List<InnerClassNode> innerClasses = classNode.innerClasses; for (InnerClassNode innerClassNode : innerClasses) { innerClassNode.access &= ~Opcodes.ACC_FINAL; } }
private boolean shouldAddInnerClass(InnerClassNode innerClass) { if (innerClass.innerName != null && innerClass.outerName != null) { return innerClass.name.equals(mClassNode.name + "$" + innerClass.innerName); } else if (innerClass.outerName == null) { int lastDollarLocation = innerClass.name.lastIndexOf("$"); String classNameWithoutInnerClass = innerClass.name.substring(0, lastDollarLocation); String innerClassName = innerClass.name.substring(lastDollarLocation + 1); if (innerClass.innerName == null) { return classNameWithoutInnerClass.equals(mClassNode.name) && innerClassName.matches("[0-9]+$"); } else { return classNameWithoutInnerClass.equals(mClassNode.name) && innerClassName.matches("[0-9]+.+$"); } } return false; }
public ClassNode transform(Type superClassType) { log.info("Transforming blocking method: " + classNode.name + "." + originalAsyncMethod.name + originalAsyncMethod.desc); //removeAsyncAnnotation(originalAsyncMethod); // Create InnerClassNode for anoymous class String asyncTaskClassName = createInnerClassName(classNode); innerClassesOf(classNode).add(new InnerClassNode(asyncTaskClassName, null, null, 0)); // Create accessor methods createAccessMethodsForAsyncMethod(); // Create ClassNode for anonymous class ClassNode asyncTaskClassNode = createAnonymousClass(asyncTaskClassName, superClassType); // Replace original method MethodNode replacementAsyncMethodNode = createReplacementAsyncMethod(asyncTaskClassName); List<MethodNode> methods = methodsOf(classNode); methods.set(methods.indexOf(originalAsyncMethod), replacementAsyncMethodNode); //System.out.println(BytecodeTraceUtil.toString(classNode)); return asyncTaskClassNode; }
void process(final ClassNode cn) { final JsonStruct json = this.ops.getJson(cn.name); if (json == null) return; final JsonStruct.EnclosingMethod enc = json.enclosingMethod; if (cn.outerClass == null && enc != null) { cn.outerClass = enc.owner; cn.outerMethod = enc.name; cn.outerMethodDesc = enc.desc; } final List<String> icns = new ArrayList<String>(); for (final InnerClassNode icn : cn.innerClasses) icns.add(icn.name); if (json.innerClasses != null) for (final JsonStruct.InnerClass i : json.innerClasses) { if (icns.contains(i.inner_class)) continue; int acc = i.getAccess(); final int accm = this.ops.getAccessClass(i.inner_class); if (accm > -1) acc = AccessTransformer.getAccessFlags(acc, accm); cn.innerClasses.add(new InnerClassNode(i.inner_class, i.outer_class, i.inner_name, acc)); } }
@Override public void visitEnd() { innerClasses.sort( (o1, o2) -> { // Enclosing classes and member classes should come first, with their order preserved boolean o1IsEnclosingOrMember = isEnclosingOrMember(o1); boolean o2IsEnclosingOrMember = isEnclosingOrMember(o2); if (o1IsEnclosingOrMember && o2IsEnclosingOrMember) { // Preserve order among these return 0; } else if (o1IsEnclosingOrMember) { return -1; } else if (o2IsEnclosingOrMember) { return 1; } // References to other classes get sorted. return o1.name.compareTo(o2.name); }); for (InnerClassNode innerClass : innerClasses) { innerClass.accept(cv); } super.visitEnd(); }
@SuppressWarnings("unchecked") private void analyzeMethodForLocalDeadCode(String dir, String className, ClassNode classNode, MethodNode methodNode) throws IOException, XMLStreamException { final LocalVariablesAnalyzer localVariablesAnalyzer = Factory .createLocalVariablesAnalyzer(methodNode); final Set<LocalVariableNode> localVariables = localVariablesAnalyzer.analyzeMethod(); if (localVariables.isEmpty()) { return; } // on exclue éventuellement les variables avec les mêmes filtres que les méthodes for (final Iterator<LocalVariableNode> it = localVariables.iterator(); it.hasNext();) { final LocalVariableNode localVariable = it.next(); if (parameters.isMethodExcluded(localVariable.name)) { it.remove(); } } // s'il reste des variables on regarde s'il y a des classes internes à la méthode for (final InnerClassNode innerClassNode : (List<InnerClassNode>) classNode.innerClasses) { if (innerClassNode.outerName != null && !innerClassNode.outerName.equals(classNode.name)) { // des classes internes n'ont parfois pas la même classe externe ??? // (on ignore car la classe interne n'est alors pas forcément dans le même répertoire) continue; } final ClassNode innerClass = new ClassNode(); final ClassReader innerClassReader = createClassReader(dir, innerClassNode.name); innerClassReader.accept(innerClass, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); localVariablesAnalyzer.analyzeInnerClass(innerClass); if (localVariables.isEmpty()) { // si toutes les variables ont été utilisées, inutile de continuer à lire les classes internes break; } } if (!localVariables.isEmpty()) { report.reportDeadLocalVariables(className, methodNode, localVariables); suspectCount += localVariables.size(); } }
/** Apply a standard sort order to attributes. */ private static void sortAttributes(ClassNode n) { n.innerClasses.sort( Comparator.comparing((InnerClassNode x) -> x.name) .thenComparing(x -> x.outerName) .thenComparing(x -> x.innerName) .thenComparing(x -> x.access)); sortAnnotations(n.visibleAnnotations); sortAnnotations(n.invisibleAnnotations); sortTypeAnnotations(n.visibleTypeAnnotations); sortTypeAnnotations(n.invisibleTypeAnnotations); for (MethodNode m : n.methods) { sortParameterAnnotations(m.visibleParameterAnnotations); sortParameterAnnotations(m.invisibleParameterAnnotations); sortAnnotations(m.visibleAnnotations); sortAnnotations(m.invisibleAnnotations); sortTypeAnnotations(m.visibleTypeAnnotations); sortTypeAnnotations(m.invisibleTypeAnnotations); } for (FieldNode f : n.fields) { sortAnnotations(f.visibleAnnotations); sortAnnotations(f.invisibleAnnotations); sortAnnotations(f.visibleAnnotations); sortAnnotations(f.invisibleAnnotations); sortTypeAnnotations(f.visibleTypeAnnotations); sortTypeAnnotations(f.invisibleTypeAnnotations); } }
/** * For each preserved InnerClass attribute, keep any information about transitive enclosing * classes of the inner class. */ private static void addInnerChain( Map<String, InnerClassNode> infos, List<InnerClassNode> used, String i) { while (infos.containsKey(i)) { InnerClassNode info = infos.get(i); used.add(info); i = info.outerName; } }
public byte[] dump(ClassNode node, boolean autoAdd) { if (node.innerClasses != null) ((List<InnerClassNode>) node.innerClasses).parallelStream().filter(in -> in.innerName != null).forEach(in -> { if (in.innerName.indexOf('/') != -1) in.innerName = in.innerName.substring(in.innerName.lastIndexOf('/') + 1); // Stringer }); ClassWriter writer = new CustomClassWriter(ClassWriter.COMPUTE_FRAMES); try { try { node.accept(writer); } catch(RuntimeException e) { if (e instanceof NoClassInPathException) { NoClassInPathException ex = (NoClassInPathException) e; System.out.println("Error: " + ex.getMessage() + " could not be found while writing " + node.name + ". Using COMPUTE_MAXS"); writer = new CustomClassWriter(ClassWriter.COMPUTE_MAXS); node.accept(writer); } else if (e.getMessage() != null) { if (e.getMessage().contains("JSR/RET")) { System.out.println("ClassNode contained JSR/RET so COMPUTE_MAXS instead"); writer = new CustomClassWriter(ClassWriter.COMPUTE_MAXS); node.accept(writer); } else throw e; } else throw e; } byte[] classBytes = writer.toByteArray(); if (autoAdd) files.add(new ClassFile(node.name.replaceAll("\\.", "/") + ".class", classBytes)); return classBytes; } catch(Throwable t) { System.out.println("Error occurred while writing " + node.name + ". This class will not be written. Exception: " + t.getMessage()); } return null; }
/** * Decompiles inner classes * @param innerClasses list of {@link InnerClassNode} */ private void appendInnerClasses(List innerClasses) { for (Object object : innerClasses) { InnerClassNode innerClass = (InnerClassNode) object; if (shouldAddInnerClass(innerClass)) { try { String innerClassName = mPackage.isEmpty() ? innerClass.name : innerClass.name.replace(mPackage, ""); children.add(Disassembler.getInstance().decompileInnerClass(innerClassName, this)); } catch (DecompilerException e) { children.add(new Statement(new PrimaryExpression(wrapInComment("Classfile of inner class " + innerClass.name + " was not found"),DataType.UNKNOWN), 0, this)); } } } }
private static InnerClassNode getInnerClass(ClassNode classNode, String innerClassName) { for (InnerClassNode icn : innerClassesOf(classNode)) { if (innerClassName.equals(icn.name)) { return icn; } } return null; }
void updateMap(ClassNode cls) { log.debug("processing {}", cls.name); if (cls.outerClass != null) { outerClassNames.put(cls.name, cls.outerClass); } if (cls.innerClasses != null) { for (Object obj : cls.innerClasses) { InnerClassNode node = (InnerClassNode) obj; if (node.outerName != null) { outerClassNames.put(node.name, node.outerName); } } } if (cls.visibleAnnotations == null) { return; } String reloadableDesc = Type.getDescriptor(Reloadable.class); String nonReloadeableDesc = Type.getDescriptor(NonReloadable.class); for (Object o : cls.visibleAnnotations) { AnnotationNode a = (AnnotationNode) o; if (reloadableDesc.equals(a.desc)) { log.debug("class {} is reloadable", cls.name); isReloadableMap.put(cls.name, true); } else { if (nonReloadeableDesc.equals(a.desc)) { log.debug("class {} is NOT reloadable", cls.name); isReloadableMap.put(cls.name, false); } } } }
@SuppressWarnings("unchecked") private List<Object> kotlinLambdas(final ClassLoader loader, final ClassNode owner) { List<Object> result = new ArrayList<>(); List<InnerClassNode> innerClasses = owner.innerClasses; for (InnerClassNode innerClass : innerClasses) { ClassNode innerNode = loadClass(innerClass.name); result.addAll(kotlinLambda(loader, innerNode)); } return result; }
@Override public void visitInnerClass(String name, String outerName, String innerName, int access) { if (name.equals(className) && outerName != null) { // If this class is an inner class, its outer class is considered referenced automatically addReferencedClassName(outerName); } innerClasses.put(name, new InnerClassNode(name, outerName, innerName, access)); super.visitInnerClass(name, outerName, innerName, access); }
@Override public void visitEnd() { // If we reference inner classes, we must also reference their outer class(es). Set<String> newSet = new HashSet<>(); for (String referencedClassName : referencedClassNames) { newSet.add(referencedClassName); InnerClassNode innerClassNode = innerClasses.get(referencedClassName); while (innerClassNode != null) { newSet.add(innerClassNode.name); innerClassNode = innerClasses.get(innerClassNode.outerName); } } referencedClassNames = newSet; super.visitEnd(); }
private static boolean isAnonymousOrLocalOrSyntheticClass(ClassNode node) { if ((node.access & Opcodes.ACC_SYNTHETIC) == Opcodes.ACC_SYNTHETIC) { return true; } InnerClassNode innerClass = getInnerClassMetadata(node); while (innerClass != null) { if (innerClass.outerName == null) { return true; } innerClass = getInnerClassMetadata(node, innerClass.outerName); } return false; }
@Nullable private static InnerClassNode getInnerClassMetadata(ClassNode node, String className) { for (InnerClassNode innerClass : node.innerClasses) { if (innerClass.name.equals(className)) { return innerClass; } } return null; }
private boolean isEnclosingOrMember(InnerClassNode innerClass) { if (className.equals(innerClass.name)) { // Self! return true; } if (className.equals(innerClass.outerName)) { // Member class return true; } // Enclosing class return className.startsWith(innerClass.name + "$"); }
private void noPkgPrivate(ClassNode node) { node.access = noPkgPrivate(node.access); for (MethodNode mn : node.methods) mn.access = noPkgPrivate(mn.access); for (FieldNode fn : node.fields) fn.access = noPkgPrivate(fn.access); for (InnerClassNode icn : node.innerClasses) icn.access = noPkgPrivate(icn.access); }
public AsmInnerClassReference(AsmItem source, InnerClassNode node, int reftype, boolean isStrong) { super(source, isStrong); this.innerClassNode = node; this.reftype = reftype; if (DEBUG) System.err.println("<AsmInnerClassReference> node=" + node + ", reftype=" + reftype); }
/** * Remove InnerClass attributes that are no longer needed after member pruning. This requires * visiting all descriptors and signatures in the bytecode to find references to inner classes. */ private static void removeUnusedInnerClassAttributes( Map<String, InnerClassNode> infos, ClassNode n) { Set<String> types = new HashSet<>(); { types.add(n.name); collectTypesFromSignature(types, n.signature); if (n.superName != null) { types.add(n.superName); } types.addAll(n.interfaces); addTypesInAnnotations(types, n.visibleAnnotations); addTypesInAnnotations(types, n.invisibleAnnotations); addTypesInTypeAnnotations(types, n.visibleTypeAnnotations); addTypesInTypeAnnotations(types, n.invisibleTypeAnnotations); } for (MethodNode m : n.methods) { collectTypesFromSignature(types, m.desc); collectTypesFromSignature(types, m.signature); types.addAll(m.exceptions); addTypesInAnnotations(types, m.visibleAnnotations); addTypesInAnnotations(types, m.invisibleAnnotations); addTypesInTypeAnnotations(types, m.visibleTypeAnnotations); addTypesInTypeAnnotations(types, m.invisibleTypeAnnotations); addTypesFromParameterAnnotations(types, m.visibleParameterAnnotations); addTypesFromParameterAnnotations(types, m.invisibleParameterAnnotations); collectTypesFromAnnotationValue(types, m.annotationDefault); } for (FieldNode f : n.fields) { collectTypesFromSignature(types, f.desc); collectTypesFromSignature(types, f.signature); addTypesInAnnotations(types, f.visibleAnnotations); addTypesInAnnotations(types, f.invisibleAnnotations); addTypesInTypeAnnotations(types, f.visibleTypeAnnotations); addTypesInTypeAnnotations(types, f.invisibleTypeAnnotations); } List<InnerClassNode> used = new ArrayList<>(); for (InnerClassNode i : n.innerClasses) { if (i.outerName != null && i.outerName.equals(n.name)) { // keep InnerClass attributes for any member classes used.add(i); } else if (types.contains(i.name)) { // otherwise, keep InnerClass attributes that were referenced in class or member signatures addInnerChain(infos, used, i.name); } } addInnerChain(infos, used, n.name); n.innerClasses = used; }
private Map<File,ClassWrapper> parseClasses(Iterator<Object> inputs) throws IOException { Map<File,ClassWrapper> results = new HashMap<File,ClassWrapper>(); while (inputs.hasNext()) { // Let user specify either kind of class name or a // file name. Object item = inputs.next(); ClassWrapper klass; File filename; if (item instanceof File) { // Load class from file. if (verbose) System.err.println("[reading file " + item + "]"); klass = getClass((File) item); filename = new File(klass.name); } else { // Load class given the class name. String className = ((String) item).replace('.', '/'); if (verbose) System.err.println("[reading class " + className + "]"); // Use the name the user specified, even if it is // different from the ultimate class name. filename = new File(className); klass = getClass(className); } results.put(filename, klass); parsed.add(item.toString()); // Check to see if there are inner classes to also parse Iterator<?> innerClasses = klass.innerClasses.iterator(); HashSet<Object> innerNames = new HashSet<Object>(); while (innerClasses.hasNext()) { String innerName = ((InnerClassNode) innerClasses.next()).name; if (!parsed.contains(innerName)) innerNames.add(innerName); } results.putAll(parseClasses(innerNames.iterator())); } return results; }
private void storeInnerClassesNames(List innerClasses) { for (Object innerClass : innerClasses) { saveInnerClassName((InnerClassNode)innerClass); } }
private void saveInnerClassName(InnerClassNode innerClass) { if (innerClass.innerName != null && innerClass.outerName != null) { Util.addInnerClassName(innerClass.name, innerClass.innerName); } }
@SuppressWarnings("unchecked") static List<InnerClassNode> innerClassesOf(ClassNode classNode) { return null == classNode.innerClasses ? Collections.<InnerClassNode> emptyList() : (List<InnerClassNode>) classNode.innerClasses; }
public static boolean canUse(ClassNode cn) { if ((cn.access & Opcodes.ACC_PRIVATE) == Opcodes.ACC_PRIVATE) { logger.debug(cn.name + " is private, ignoring it"); return false; } if (ANONYMOUS_MATCHER1.matcher(cn.name).matches()) { logger.debug(cn.name + " looks like an anonymous class, ignoring it"); return false; } if (ANONYMOUS_MATCHER2.matcher(cn.name).matches()) { logger.debug(cn.name + " looks like an anonymous class, ignoring it"); return false; } // TODO: Handle Deprecated if (cn.name.startsWith("junit")) return false; // If the SUT is not in the default package, then // we cannot import classes that are in the default // package /* if ((cn.access & Opcodes.ACC_PUBLIC) != Opcodes.ACC_PUBLIC) { String nameWithDots = cn.name.replace('/', '.'); String packageName = ClassUtils.getPackageName(nameWithDots); if (!packageName.equals(Properties.CLASS_PREFIX)) { return false; } } */ // ASM has some problem with the access of inner classes // so we check if the inner class name is the current class name // and if so, check if the inner class is actually accessible @SuppressWarnings("unchecked") List<InnerClassNode> in = cn.innerClasses; for (InnerClassNode inc : in) { if (cn.name.equals(inc.name)) { // logger.debug("ASM weird behaviour: Inner class equals class: " + inc.name); if ((inc.access & Opcodes.ACC_PRIVATE) == Opcodes.ACC_PRIVATE) { return false; } /* if ((inc.access & Opcodes.ACC_PUBLIC) != Opcodes.ACC_PUBLIC) { String nameWithDots = inc.name.replace('/', '.'); String packageName = ClassUtils.getPackageName(nameWithDots); if (!packageName.equals(Properties.CLASS_PREFIX)) { return false; } } */ logger.debug("Can use inner class: " + inc.name); return true; } } logger.debug(cn.name + " is accessible"); return true; }
@SuppressWarnings("unchecked") public void transform(ClassNode cn) { System.out.println("cn.name: " + cn.name); /** * Attributes */ List<Attribute> allAttributes = cn.attrs; if (allAttributes != null) { for (Attribute attr : allAttributes) { System.out.println(" attr.type: " + attr.type + ", attr.toString: " + attr.toString()); } } /** * Fields */ List<FieldNode> allFields = cn.fields; for (FieldNode field : allFields) { System.out.println(" field.name: " + field.name); } /** * InnerClasses */ List<InnerClassNode> allInnerClassNode = cn.innerClasses; for (InnerClassNode innerClass : allInnerClassNode) { System.out.println(" innerClass.innerName: " + innerClass.innerName + ", innerClass.name: " + innerClass.name + ", innerClass.outerName: " + innerClass.outerName); } /** * Interfaces */ /** * Methods */ List<MethodNode> allMethods = cn.methods; for (MethodNode mn : allMethods) { // Constructor || Destructor if ("<init>".equals(mn.name) || "<clinit>".equals(mn.name)) continue; // method without lines (instructions) if (mn.instructions.size() == 0) continue; // ///////////////////////////////////// System.out.println(" mn.name: " + mn.name); Iterator<AbstractInsnNode> j = mn.instructions.iterator(); while (j.hasNext()) { AbstractInsnNode abs_ins = j.next(); System.out.println(" abs_ins.getOpcode(): " + abs_ins.getOpcode()); } } }
private InnerClassNode innerClass(ClassNode cn, int n) { return cn.innerClasses.get(n); }
protected static PrefixedStringBuilder decompile(PrefixedStringBuilder sb, ArrayList<String> decompiledClasses, String containerName, ClassNode cn) { ArrayList<String> unableToDecompile = new ArrayList<String>(); decompiledClasses.add(cn.name); sb.append(getAccessString(cn.access)); sb.append(" "); sb.append(cn.name); if (cn.superName != null && !cn.superName.equals("java/lang/Object")) { sb.append(" extends "); sb.append(cn.superName); } int amountOfInterfaces = cn.interfaces.size(); if (amountOfInterfaces > 0) { sb.append(" implements "); sb.append(cn.interfaces.get(0)); if (amountOfInterfaces > 1) { // sb.append(","); } for (int i = 1; i < amountOfInterfaces; i++) { sb.append(", "); sb.append(cn.interfaces.get(i)); } } sb.append(" {"); sb.append(BytecodeViewer.nl); for (FieldNode fn : (List<FieldNode>) cn.fields) { sb.append(BytecodeViewer.nl); sb.append(" "); FieldNodeDecompiler.decompile(sb, fn); } if (cn.fields.size() > 0) { sb.append(BytecodeViewer.nl); } for (MethodNode mn : (List<MethodNode>) cn.methods) { sb.append(BytecodeViewer.nl); MethodNodeDecompiler.decompile(sb, mn, cn); } for (Object o : cn.innerClasses) { InnerClassNode innerClassNode = (InnerClassNode) o; String innerClassName = innerClassNode.name; if ((innerClassName != null) && !decompiledClasses.contains(innerClassName)) { decompiledClasses.add(innerClassName); ClassNode cn1 = BytecodeViewer.getClassNode(containerName, innerClassName); if (cn1 != null) { sb.appendPrefix(" "); sb.append(BytecodeViewer.nl + BytecodeViewer.nl); sb = decompile(sb, decompiledClasses, containerName,cn1); sb.trimPrefix(5); sb.append(BytecodeViewer.nl); } else { unableToDecompile.add(innerClassName); } } } if (!unableToDecompile.isEmpty()) { sb.append("//the following inner classes couldn't be decompiled: "); for (String s : unableToDecompile) { sb.append(s); sb.append(" "); } sb.append(BytecodeViewer.nl); } sb.append("}"); // System.out.println("Wrote end for " + cn.name + // " with prefix length: " + sb.prefix.length()); return sb; }
private Map<File,ClassWrapper> parseClasses(Iterator<Object> inputs) throws IOException { Map<File,ClassWrapper> results = new HashMap<File,ClassWrapper>(); while (inputs.hasNext()) { // Let user specify either kind of class name or a // file name. Object item = inputs.next(); ClassWrapper klass; File filename; if (item instanceof File) { // Load class from file. if (verbose) System.err.println("[reading file " + item + "]"); klass = getClass((File) item); filename = new File(klass.name); } else { // Load class given the class name. String className = ((String) item).replace('.', '/'); if (verbose) System.err.println("[reading class " + className + "]"); // Use the name the user specified, even if it is // different from the ultimate class name. filename = new File(className); klass = getClass(className); } results.put(filename, klass); parsed.add(item.toString()); if (! (item instanceof File)) { // Check to see if there are inner classes to also parse Iterator<?> innerClasses = klass.innerClasses.iterator(); HashSet<Object> innerNames = new HashSet<Object>(); while (innerClasses.hasNext()) { String innerName = ((InnerClassNode) innerClasses.next()).name; if (!parsed.contains(innerName)) innerNames.add(innerName); } results.putAll(parseClasses(innerNames.iterator())); } } return results; }
@Nullable private static InnerClassNode getInnerClassMetadata(ClassNode node) { String name = node.name; return getInnerClassMetadata(node, name); }
@Override public void visitInnerClass(String name, String outerName, String innerName, int access) { innerClasses.add(new InnerClassNode(name, outerName, innerName, access)); }