Skip to content

Commit

Permalink
Reduce memory footprint of class info model population
Browse files Browse the repository at this point in the history
  • Loading branch information
Col-E committed Jan 5, 2025
1 parent 61373a4 commit 529061b
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,7 @@ private List<MethodMember> mapMethods(@Nullable Iterable<DexEncodedMethod> metho
String sig = m.getSignature().toString();
int access = m.getAccessFlags().getAsCfAccessFlags();
List<String> thrownTypes = Collections.emptyList();
List<LocalVariable> variables = Collections.emptyList();
BasicMethodMember method = new BasicMethodMember(name, desc, sig, access, thrownTypes, variables);
BasicMethodMember method = new BasicMethodMember(name, desc, sig, access, thrownTypes);
for (AnnotationInfo anno : mapAnnos(m.annotations())) method.addAnnotation(anno);
return method;
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,44 @@

import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import org.objectweb.asm.*;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.TypePath;
import software.coley.recaf.info.BasicInnerClassInfo;
import software.coley.recaf.info.BasicJvmClassInfo;
import software.coley.recaf.info.InnerClassInfo;
import software.coley.recaf.info.JvmClassInfo;
import software.coley.recaf.info.annotation.*;
import software.coley.recaf.info.member.*;
import software.coley.recaf.info.annotation.AnnotationElement;
import software.coley.recaf.info.annotation.AnnotationInfo;
import software.coley.recaf.info.annotation.BasicAnnotationElement;
import software.coley.recaf.info.annotation.BasicAnnotationEnumReference;
import software.coley.recaf.info.annotation.BasicAnnotationInfo;
import software.coley.recaf.info.annotation.TypeAnnotationInfo;
import software.coley.recaf.info.member.BasicFieldMember;
import software.coley.recaf.info.member.BasicLocalVariable;
import software.coley.recaf.info.member.BasicMethodMember;
import software.coley.recaf.info.member.FieldMember;
import software.coley.recaf.info.member.LocalVariable;
import software.coley.recaf.info.member.MethodMember;
import software.coley.recaf.info.properties.builtin.UnknownAttributesProperty;
import software.coley.recaf.util.MultiMap;

import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Consumer;

import static software.coley.recaf.RecafConstants.getAsmVersion;
Expand Down Expand Up @@ -241,12 +268,12 @@ protected void verify() {
* @see MethodBuilderAdapter
*/
private class ClassBuilderAdapter extends ClassVisitor {
private final List<AnnotationInfo> annotations = new ArrayList<>();
private final List<TypeAnnotationInfo> typeAnnotations = new ArrayList<>();
private final List<InnerClassInfo> innerClasses = new ArrayList<>();
private final List<FieldMember> fields = new ArrayList<>();
private final List<MethodMember> methods = new ArrayList<>();
private final List<Attribute> classCustomAttributes = new ArrayList<>();
private List<AnnotationInfo> annotations;
private List<TypeAnnotationInfo> typeAnnotations;
private List<InnerClassInfo> innerClasses;
private List<FieldMember> fields;
private List<MethodMember> methods;
private List<Attribute> classCustomAttributes;
private final MultiMap<String, Attribute, List<Attribute>> fieldCustomAttributes;
private final MultiMap<String, Attribute, List<Attribute>> methodCustomAttributes;

Expand Down Expand Up @@ -283,11 +310,15 @@ public void visitOuterClass(String owner, String name, String descriptor) {

@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
if (annotations == null)
annotations = new ArrayList<>();
return new AnnotationBuilderAdapter(visible, descriptor, annotations::add);
}

@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
if (typeAnnotations == null)
typeAnnotations = new ArrayList<>();
return new AnnotationBuilderAdapter(visible, descriptor,
anno -> typeAnnotations.add(anno.withTypeInfo(typeRef, typePath)));
}
Expand All @@ -300,6 +331,8 @@ public void visitInnerClass(String name, String outerName, String innerName, int
String currentClassName = getName();

// Add the inner data
if (innerClasses == null)
innerClasses = new ArrayList<>();
innerClasses.add(new BasicInnerClassInfo(currentClassName, name, outerName, innerName, access));

// If the local 'name' is the current class name, then we are visiting an inner class entry
Expand All @@ -318,6 +351,8 @@ public void visitInnerClass(String name, String outerName, String innerName, int

@Override
public void visitAttribute(Attribute attribute) {
if (classCustomAttributes == null)
classCustomAttributes = new ArrayList<>();
classCustomAttributes.add(attribute);
super.visitAttribute(attribute);
}
Expand All @@ -334,6 +369,8 @@ public void visitAttribute(Attribute attribute) {

@Override
public void visitEnd() {
if (fields == null)
fields = new ArrayList<>();
fields.add(getFieldMember());
}
};
Expand All @@ -352,6 +389,8 @@ public void visitAttribute(Attribute attribute) {
@Override
public void visitEnd() {
super.visitEnd();
if (methods == null)
methods = new ArrayList<>();
methods.add(getMethodMember());
}
};
Expand All @@ -360,22 +399,26 @@ public void visitEnd() {
@Override
public void visitEnd() {
super.visitEnd();
if (!annotations.isEmpty()) {
if (annotations != null)
withAnnotations(annotations);
}
withFields(fields);
withMethods(methods);
withInnerClasses(innerClasses);
withAnnotations(annotations);
withTypeAnnotations(typeAnnotations);
if (fields != null)
withFields(fields);
if (methods != null)
withMethods(methods);
if (innerClasses != null)
withInnerClasses(innerClasses);
if (annotations != null)
withAnnotations(annotations);
if (typeAnnotations != null)
withTypeAnnotations(typeAnnotations);
}

/**
* @return {@code true} when any custom attributes were found.
*/
public boolean hasCustomAttributes() {
return !classCustomAttributes.isEmpty() ||
!fieldCustomAttributes.isEmpty() ||
return (classCustomAttributes != null && !classCustomAttributes.isEmpty()) ||
(!fieldCustomAttributes.isEmpty()) ||
!methodCustomAttributes.isEmpty();
}

Expand All @@ -385,9 +428,10 @@ public boolean hasCustomAttributes() {
@Nonnull
public Collection<String> getCustomAttributeNames() {
Set<String> names = new TreeSet<>();
classCustomAttributes.stream()
.map(a -> a.type)
.forEach(names::add);
if (classCustomAttributes != null)
classCustomAttributes.stream()
.map(a -> a.type)
.forEach(names::add);
fieldCustomAttributes.values()
.map(a -> a.type)
.forEach(names::add);
Expand Down Expand Up @@ -427,18 +471,17 @@ public BasicFieldMember getFieldMember() {
private static class MethodBuilderAdapter extends MethodVisitor {
private final BasicMethodMember methodMember;
private final Type methodDescriptor;
private final List<LocalVariable> parameters;
private List<LocalVariable> parameters;
private int parameterIndex;
private int parameterSlot;

public MethodBuilderAdapter(int access, String name, String descriptor,
String signature, String[] exceptions) {
super(getAsmVersion());
List<String> exceptionList = exceptions == null ? Collections.emptyList() : Arrays.asList(exceptions);
methodMember = new BasicMethodMember(name, descriptor, signature, access, exceptionList, new ArrayList<>());
methodMember = new BasicMethodMember(name, descriptor, signature, access, exceptionList);
methodDescriptor = Type.getMethodType(descriptor);
parameterSlot = methodMember.hasStaticModifier() ? 0 : 1;
parameters = new ArrayList<>(methodDescriptor.getArgumentCount());
}

@Override
Expand Down Expand Up @@ -467,8 +510,11 @@ public void visitParameter(String name, int access) {
Type argumentType = argumentTypes[parameterIndex];

// Only add when we have a name for the parameter.
if (name != null)
if (name != null) {
if (parameters == null)
parameters = new ArrayList<>(methodDescriptor.getArgumentCount());
parameters.add(new BasicLocalVariable(parameterSlot, name, argumentType.getDescriptor(), null));
}

parameterIndex++;
parameterSlot += argumentType.getSize();
Expand All @@ -491,9 +537,10 @@ public void visitEnd() {
// provided variables for those indices. This assists in providing variable models for abstract methods.
// This only works when a 'MethodParameters' attribute is present on the method. The javac compiler
// emits this when passing '-parameters'.
for (LocalVariable parameter : parameters)
if (methodMember.getLocalVariable(parameter.getIndex()) == null)
methodMember.addLocalVariable(parameter);
if (parameters != null)
for (LocalVariable parameter : parameters)
if (methodMember.getLocalVariable(parameter.getIndex()) == null)
methodMember.addLocalVariable(parameter);
}

@Nonnull
Expand Down Expand Up @@ -598,4 +645,4 @@ public void visitEnum(String name, String descriptor, String value) {
elements.put(name, new BasicAnnotationElement(name, enumRef));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import software.coley.recaf.info.properties.PropertyContainer;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

Expand All @@ -19,13 +20,13 @@
* @author Matt Coley
*/
public abstract class BasicMember implements ClassMember {
private final PropertyContainer properties = new BasicPropertyContainer();
private final List<AnnotationInfo> annotations = new ArrayList<>();
private final List<TypeAnnotationInfo> typeAnnotations = new ArrayList<>();
private final String name;
private final String desc;
private final String signature;
private final int access;
private PropertyContainer properties;
private List<AnnotationInfo> annotations;
private List<TypeAnnotationInfo> typeAnnotations;
private ClassInfo declaringClass;

protected BasicMember(@Nonnull String name, @Nonnull String desc, @Nullable String signature, int access) {
Expand All @@ -43,6 +44,8 @@ protected BasicMember(@Nonnull String name, @Nonnull String desc, @Nullable Stri
* Annotation to add.
*/
public void addAnnotation(@Nonnull AnnotationInfo annotation) {
if (annotations == null)
annotations = new ArrayList<>(2);
annotations.add(annotation);
}

Expand All @@ -54,6 +57,8 @@ public void addAnnotation(@Nonnull AnnotationInfo annotation) {
* Annotation to add.
*/
public void addTypeAnnotation(@Nonnull TypeAnnotationInfo typeAnnotation) {
if (typeAnnotations == null)
typeAnnotations = new ArrayList<>(2);
typeAnnotations.add(typeAnnotation);
}

Expand Down Expand Up @@ -97,28 +102,37 @@ public String getSignature() {
@Nonnull
@Override
public List<AnnotationInfo> getAnnotations() {
if (annotations == null)
return Collections.emptyList();
return annotations;
}

@Nonnull
@Override
public List<TypeAnnotationInfo> getTypeAnnotations() {
if (typeAnnotations == null)
return Collections.emptyList();
return typeAnnotations;
}

@Override
public <V> void setProperty(Property<V> property) {
if (properties == null)
properties = new BasicPropertyContainer();
properties.setProperty(property);
}

@Override
public void removeProperty(String key) {
properties.removeProperty(key);
if (properties != null)
properties.removeProperty(key);
}

@Nonnull
@Override
public Map<String, Property<?>> getProperties() {
if (properties == null)
return Collections.emptyMap();
return properties.getProperties();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import jakarta.annotation.Nullable;
import software.coley.recaf.info.annotation.AnnotationElement;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

Expand All @@ -14,7 +16,7 @@
*/
public class BasicMethodMember extends BasicMember implements MethodMember {
private final List<String> thrownTypes;
private final List<LocalVariable> variables;
private List<LocalVariable> variables;
private AnnotationElement annotationDefault;

/**
Expand All @@ -28,26 +30,26 @@ public class BasicMethodMember extends BasicMember implements MethodMember {
* Method access modifiers.
* @param thrownTypes
* Method's thrown exceptions.
* @param variables
* Method's local variables.
*/
public BasicMethodMember(@Nonnull String name, @Nonnull String desc, @Nullable String signature, int access,
@Nonnull List<String> thrownTypes, @Nonnull List<LocalVariable> variables) {
@Nonnull List<String> thrownTypes) {
super(name, desc, signature, access);
this.thrownTypes = thrownTypes;
this.variables = variables;
}

/**
* @param variable
* Variable to add.
*/
public void addLocalVariable(@Nonnull LocalVariable variable) {
if (variables == null)
variables = new ArrayList<>();
variables.add(variable);
}

/**
* @param annotationDefault Element value to set.
* @param annotationDefault
* Element value to set.
*/
public void setAnnotationDefault(@Nonnull AnnotationElement annotationDefault) {
this.annotationDefault = annotationDefault;
Expand All @@ -62,6 +64,8 @@ public List<String> getThrownTypes() {
@Nonnull
@Override
public List<LocalVariable> getLocalVariables() {
if (variables == null)
return Collections.emptyList();
return variables;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class BasicPropertyContainer implements PropertyContainer {
* Container with empty map.
*/
public BasicPropertyContainer() {
this(Collections.emptyMap());
this(null);
}

/**
Expand Down
Loading

0 comments on commit 529061b

Please sign in to comment.