Skip to content

Commit

Permalink
Refactoring, documentation, etc.
Browse files Browse the repository at this point in the history
  • Loading branch information
Hapstyx committed Nov 29, 2024
1 parent 98111aa commit d2d1a89
Show file tree
Hide file tree
Showing 16 changed files with 600 additions and 591 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package org.tudalgo.algoutils.transform;

import org.tudalgo.algoutils.transform.util.ClassHeader;
import org.tudalgo.algoutils.transform.util.FieldHeader;
import org.tudalgo.algoutils.transform.util.MethodHeader;
import org.tudalgo.algoutils.transform.util.*;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import org.tudalgo.algoutils.transform.util.TransformationContext;

import java.util.Arrays;
import java.util.HashMap;
Expand Down Expand Up @@ -72,7 +69,7 @@ public void visit(int version, int access, String name, String signature, String
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
FieldHeader fieldHeader = new FieldHeader(className, access, name, descriptor, signature);
FieldNode fieldNode = (FieldNode) super.visitField(access & ~ACC_FINAL, name, descriptor, signature, value);
FieldNode fieldNode = (FieldNode) super.visitField(TransformationUtils.transformAccess(access), name, descriptor, signature, value);
fields.put(fieldHeader, fieldNode);
return fieldNode;
}
Expand Down Expand Up @@ -102,7 +99,7 @@ public MethodVisitor visitMethod(int access, String name, String descriptor, Str
* @return a new {@link MethodNode}
*/
private MethodNode getMethodNode(int access, String name, String descriptor, String signature, String[] exceptions) {
return new MethodNode(ASM9, access, name, descriptor, signature, exceptions) {
return new MethodNode(ASM9, TransformationUtils.transformAccess(access), name, descriptor, signature, exceptions) {
@Override
public void visitMethodInsn(int opcodeAndSource, String owner, String name, String descriptor, boolean isInterface) {
MethodHeader methodHeader = new MethodHeader(owner, 0, name, descriptor, null, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
import org.tudalgo.algoutils.student.annotation.ForceSignature;
import org.tudalgo.algoutils.transform.util.MethodHeader;
import org.tudalgo.algoutils.transform.util.TransformationContext;
import org.tudalgo.algoutils.transform.util.TransformationUtils;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type;
import org.sourcegrade.jagr.api.testing.ClassTransformer;
import org.tudalgo.algoutils.tutor.general.SubmissionInfo;

import java.lang.reflect.Executable;
import java.lang.reflect.Method;
Expand Down Expand Up @@ -66,6 +66,7 @@
* @see SubmissionExecutionHandler
* @author Daniel Mangold
*/
@SuppressWarnings("unused")
public class SolutionMergingClassTransformer implements ClassTransformer {

/**
Expand Down Expand Up @@ -100,7 +101,7 @@ private SolutionMergingClassTransformer(Builder builder) {
solutionClasses,
submissionClasses);
((Map<String, List<String>>) builder.configuration.get(Config.SOLUTION_CLASSES)).keySet()
.forEach(className -> solutionClasses.put(className, TransformationUtils.readSolutionClass(transformationContext, className)));
.forEach(className -> solutionClasses.put(className, transformationContext.readSolutionClass(className)));
}

@Override
Expand All @@ -114,7 +115,6 @@ public int getWriterFlags() {
}

@Override
@SuppressWarnings("unchecked")
public void transform(ClassReader reader, ClassWriter writer) {
if (!new JagrExecutionCondition().evaluateExecutionCondition(null).isDisabled()) { // if Jagr is present
try {
Expand All @@ -124,14 +124,11 @@ public void transform(ClassReader reader, ClassWriter writer) {
transformationContext.setSubmissionClassLoader(submissionClassLoader);

if (!readSubmissionClasses) {
Map<String, ?> submissionInfo = new ObjectMapper()
.readValue(submissionClassLoader.getResourceAsStream("submission-info.json"), Map.class);
Set<String> submissionClassNames = ((List<Map<String, ?>>) submissionInfo.get("sourceSets")).stream()
.filter(sourceSet -> sourceSet.get("name").equals("main"))
.findAny()
.map(sourceSet -> ((Map<String, List<String>>) sourceSet.get("files")).get("java"))
.orElseThrow()
Set<String> submissionClassNames = new ObjectMapper()
.readValue(submissionClassLoader.getResourceAsStream("submission-info.json"), SubmissionInfo.class)
.sourceSets()
.stream()
.flatMap(sourceSet -> sourceSet.files().get("java").stream())
.map(submissionClassName -> submissionClassName.replaceAll("\\.java$", ""))
.collect(Collectors.toSet());
transformationContext.setSubmissionClassNames(submissionClassNames);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;

/**
* A class that holds information on a submission class.
* This class will attempt to find a corresponding solution class and map its members
Expand All @@ -24,16 +23,12 @@
public class SubmissionClassInfo extends ClassVisitor {

private final TransformationContext transformationContext;
private final String originalClassName;
private final String computedClassName;
private final Set<Triple<String, Map<FieldHeader, FieldHeader>, Map<MethodHeader, MethodHeader>>> superClassMembers = new HashSet<>();
private final ForceSignatureAnnotationProcessor fsAnnotationProcessor;
private final SolutionClassNode solutionClass;

private String superClass;
private String[] interfaces;
private final Set<Triple<String, Map<FieldHeader, FieldHeader>, Map<MethodHeader, MethodHeader>>> superClassMembers = new HashSet<>();

private ClassHeader submissionClassHeader;
private ClassHeader originalClassHeader;
private ClassHeader computedClassHeader;
private SolutionClassNode solutionClass;

// Mapping of fields in submission => usable fields
private final Map<FieldHeader, FieldHeader> fields = new HashMap<>();
Expand All @@ -47,25 +42,13 @@ public class SubmissionClassInfo extends ClassVisitor {
* Constructs a new {@link SubmissionClassInfo} instance.
*
* @param transformationContext a {@link TransformationContext} object
* @param className the name of the submission class
* @param fsAnnotationProcessor a {@link ForceSignatureAnnotationProcessor} for the submission class
*/
public SubmissionClassInfo(TransformationContext transformationContext,
String className,
ForceSignatureAnnotationProcessor fsAnnotationProcessor) {
super(Opcodes.ASM9);
this.transformationContext = transformationContext;
this.originalClassName = className;
this.fsAnnotationProcessor = fsAnnotationProcessor;

if (fsAnnotationProcessor.classIdentifierIsForced()) {
this.computedClassName = fsAnnotationProcessor.forcedClassIdentifier();
} else {
// If not forced, get the closest matching solution class
this.computedClassName = Optional.ofNullable(transformationContext.getSolutionClassName(originalClassName))
.orElse(originalClassName);
}
this.solutionClass = transformationContext.solutionClasses().get(computedClassName);
}

/**
Expand All @@ -74,7 +57,7 @@ public SubmissionClassInfo(TransformationContext transformationContext,
* @return the original class header
*/
public ClassHeader getOriginalClassHeader() {
return submissionClassHeader;
return originalClassHeader;
}

/**
Expand All @@ -84,8 +67,8 @@ public ClassHeader getOriginalClassHeader() {
*
* @return the computed class name
*/
public String getComputedClassName() {
return computedClassName;
public ClassHeader getComputedClassHeader() {
return computedClassHeader;
}

/**
Expand Down Expand Up @@ -169,20 +152,28 @@ public MethodHeader getComputedSuperClassConstructorHeader(String descriptor) {

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
submissionClassHeader = new ClassHeader(access, name, signature, superName, interfaces);
resolveSuperClassMembers(superClassMembers, this.superClass = superName, this.interfaces = interfaces);
originalClassHeader = new ClassHeader(access, name, signature, superName, interfaces);
String computedClassName;
if (fsAnnotationProcessor.classIdentifierIsForced()) {
computedClassName = fsAnnotationProcessor.forcedClassIdentifier();
} else {
// If not forced, get the closest matching solution class
computedClassName = transformationContext.getSolutionClassName(originalClassHeader.name());
}
solutionClass = transformationContext.getSolutionClass(computedClassName);
computedClassHeader = getSolutionClass().map(SolutionClassNode::getClassHeader).orElse(originalClassHeader);
}

@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
fields.put(new FieldHeader(originalClassName, access, name, descriptor, signature), null);
fields.put(new FieldHeader(originalClassHeader.name(), access, name, descriptor, signature), null);
return null;
}

@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
MethodHeader submissionMethodHeader = new MethodHeader(originalClassName, access, name, descriptor, signature, exceptions);
if ((access & ACC_SYNTHETIC) != 0 && name.startsWith("lambda$")) { // if method is lambda
MethodHeader submissionMethodHeader = new MethodHeader(originalClassHeader.name(), access, name, descriptor, signature, exceptions);
if (TransformationUtils.isLambdaMethod(access, name)) {
methods.put(submissionMethodHeader, submissionMethodHeader);
return null;
}
Expand All @@ -191,73 +182,73 @@ public MethodVisitor visitMethod(int access, String name, String descriptor, Str
return null;
}

public void mapToSolutionClass() {
public void computeMembers() {
SimilarityMapper<FieldHeader> fieldsSimilarityMapper = new SimilarityMapper<>(
fields.keySet(),
solutionClass == null ? Collections.emptyList() : solutionClass.getFields().keySet(),
getSolutionClass().map(solutionClass -> solutionClass.getFields().keySet()).orElseGet(Collections::emptySet),
transformationContext.getSimilarity(),
FieldHeader::name
);
for (FieldHeader submissionFieldHeader : fields.keySet()) {
FieldHeader solutionFieldHeader;
Type fieldType = Type.getType(submissionFieldHeader.descriptor());
FieldHeader fallbackFieldHeader = new FieldHeader(computedClassName,
Supplier<FieldHeader> fallbackFieldHeader = () -> new FieldHeader(computedClassHeader.name(),
submissionFieldHeader.access(),
submissionFieldHeader.name(),
fieldType.getSort() == Type.OBJECT ?
"L" + transformationContext.getComputedName(fieldType.getInternalName()) + ";" :
fieldType.getSort() == Type.ARRAY ?
transformationContext.getComputedName(fieldType.getInternalName()) :
submissionFieldHeader.descriptor(),
transformationContext.toComputedType(Type.getType(submissionFieldHeader.descriptor())).getDescriptor(),
submissionFieldHeader.signature());
FieldHeader solutionFieldHeader;
if (fsAnnotationProcessor.fieldIdentifierIsForced(submissionFieldHeader.name())) {
solutionFieldHeader = fsAnnotationProcessor.forcedFieldHeader(submissionFieldHeader.name());
} else if (solutionClass != null) {
solutionFieldHeader = solutionClass.getFields()
.keySet()
.stream()
.filter(fieldHeader -> fieldHeader.equals(fieldsSimilarityMapper.getBestMatch(submissionFieldHeader)))
.filter(fieldHeader -> fieldsSimilarityMapper.getBestMatch(submissionFieldHeader)
.map(fieldHeader::equals)
.orElse(false))
.findAny()
.orElse(fallbackFieldHeader);
.orElseGet(fallbackFieldHeader);
} else {
solutionFieldHeader = fallbackFieldHeader;
solutionFieldHeader = fallbackFieldHeader.get();
}
fields.put(submissionFieldHeader, solutionFieldHeader);
}

SimilarityMapper<MethodHeader> methodsSimilarityMapper = new SimilarityMapper<>(
methods.keySet(),
solutionClass == null ? Collections.emptyList() : solutionClass.getMethods().keySet(),
getSolutionClass().map(solutionClass -> solutionClass.getMethods().keySet()).orElseGet(Collections::emptySet),
transformationContext.getSimilarity(),
methodHeader -> methodHeader.name() + methodHeader.descriptor()
);
for (MethodHeader submissionMethodHeader : methods.keySet()) {
String submissionMethodName = submissionMethodHeader.name();
String submissionMethodDescriptor = submissionMethodHeader.descriptor();
MethodHeader solutionMethodHeader;
MethodHeader fallbackMethodHeader = new MethodHeader(computedClassName,
Supplier<MethodHeader> fallbackMethodHeader = () -> new MethodHeader(computedClassHeader.name(),
submissionMethodHeader.access(),
submissionMethodHeader.name(),
submissionMethodHeader.descriptor(),
transformationContext.toComputedType(submissionMethodHeader.descriptor()).getDescriptor(),
submissionMethodHeader.signature(),
submissionMethodHeader.exceptions());
MethodHeader solutionMethodHeader;
if (fsAnnotationProcessor.methodSignatureIsForced(submissionMethodName, submissionMethodDescriptor)) {
solutionMethodHeader = fsAnnotationProcessor.forcedMethodHeader(submissionMethodName, submissionMethodDescriptor);
} else if (solutionClass != null) {
solutionMethodHeader = solutionClass.getMethods()
.keySet()
.stream()
.filter(methodHeader -> methodHeader.equals(methodsSimilarityMapper.getBestMatch(submissionMethodHeader)))
.filter(methodHeader -> methodsSimilarityMapper.getBestMatch(submissionMethodHeader)
.map(methodHeader::equals)
.orElse(false))
.findAny()
.orElse(fallbackMethodHeader);
.orElseGet(fallbackMethodHeader);
} else {
solutionMethodHeader = fallbackMethodHeader;
solutionMethodHeader = fallbackMethodHeader.get();
}
methods.put(submissionMethodHeader, solutionMethodHeader);
}

resolveSuperClassMembers(superClassMembers, originalClassHeader.superName(), originalClassHeader.interfaces());
for (Triple<String, Map<FieldHeader, FieldHeader>, Map<MethodHeader, MethodHeader>> triple : superClassMembers) {
if (triple.getFirst().equals(superClass)) {
if (triple.getFirst().equals(originalClassHeader.superName())) {
triple.getThird()
.entrySet()
.stream()
Expand Down Expand Up @@ -314,7 +305,9 @@ private void resolveSuperClassMembers(Set<Triple<String, Map<FieldHeader, FieldH
.stream()
.filter(entry -> !Modifier.isPrivate(entry.getKey().access()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))));
resolveSuperClassMembers(superClassMembers, submissionClassInfo.superClass, submissionClassInfo.interfaces);
resolveSuperClassMembers(superClassMembers,
submissionClassInfo.originalClassHeader.superName(),
submissionClassInfo.originalClassHeader.interfaces());
} else {
try {
Class<?> clazz = Class.forName(className.replace('/', '.'));
Expand Down
Loading

0 comments on commit d2d1a89

Please sign in to comment.