All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.qbicc.plugin.patcher.ClassPatchInfo Maven / Gradle / Ivy

package org.qbicc.plugin.patcher;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import io.smallrye.common.constraint.Assert;
import org.qbicc.type.annotation.Annotation;
import org.qbicc.type.definition.MethodBodyFactory;
import org.qbicc.type.descriptor.MethodDescriptor;
import org.qbicc.type.descriptor.TypeDescriptor;

final class ClassPatchInfo {

    private static final byte[] EMPTY_DIGEST = new byte[32];

    static final ClassPatchInfo EMPTY = new ClassPatchInfo(0);

    private boolean committed;
    private Map annotatedFields;
    private Map replacedFields;
    private List injectedFields;
    private Map deletedFields;
    private Map runtimeInitFields;
    private Map annotatedConstructors;
    private Map replacedConstructors;
    private List injectedConstructors;
    private Map deletedConstructors;
    private Map> annotatedMethods;
    private Map> replacedMethods;
    private List injectedMethods;
    private Map> deletedMethods;
    private InitializerPatchInfo replacedInitializer;
    private boolean deletedInitializer;
    private Map> methodBodyReplacedMethods;
    private List addedClassAnnotations;
    private byte[] digest;

    ClassPatchInfo() {
        annotatedFields = Map.of();
        replacedFields = Map.of();
        injectedFields = List.of();
        deletedFields = Map.of();
        runtimeInitFields = Map.of();
        annotatedConstructors = Map.of();
        replacedConstructors = Map.of();
        injectedConstructors = List.of();
        deletedConstructors = Map.of();
        annotatedMethods = Map.of();
        replacedMethods = Map.of();
        injectedMethods = List.of();
        deletedMethods = Map.of();
        methodBodyReplacedMethods = Map.of();
        addedClassAnnotations = List.of();
        digest = EMPTY_DIGEST;
    }

    ClassPatchInfo(int ignored) {
        this();
        committed = true;
    }

    ClassPatchInfo(final String internalName) {
        this();
    }

    void commit() {
        assert Thread.holdsLock(this);
        committed = true;
    }

    byte[] getDigest() {
        return digest;
    }

    // remove

    FieldDeleteInfo getDeletedFieldInfo(final String name, final TypeDescriptor descriptor) {
        assert Thread.holdsLock(this);
        FieldDeleteInfo info = deletedFields.get(name);
        return info == null ? null : info.getDescriptor().equals(descriptor) ? info : null;
    }

    ConstructorDeleteInfo getDeletedConstructorInfo(final MethodDescriptor descriptor) {
        assert Thread.holdsLock(this);
        return deletedConstructors.get(descriptor);
    }

    MethodDeleteInfo getDeletedMethodInfo(final String name, final MethodDescriptor descriptor) {
        assert Thread.holdsLock(this);
        return deletedMethods.getOrDefault(name, Map.of()).get(descriptor);
    }

    boolean isDeletedInitializer() {
        assert Thread.holdsLock(this);
        return deletedInitializer;
    }

    // replace

    FieldPatchInfo getReplacementFieldInfo(final String fieldName, TypeDescriptor descriptor) {
        FieldPatchInfo fieldPatchInfo = replacedFields.get(fieldName);
        return fieldPatchInfo != null && fieldPatchInfo.getDescriptor().equals(descriptor) ? fieldPatchInfo : null;
    }

    ConstructorPatchInfo getReplacementConstructorInfo(final MethodDescriptor descriptor) {
        assert Thread.holdsLock(this);
        return replacedConstructors.get(descriptor);
    }

    MethodPatchInfo getReplacementMethodInfo(final String name, final MethodDescriptor descriptor) {
        assert Thread.holdsLock(this);
        return replacedMethods.getOrDefault(name, Map.of()).get(descriptor);
    }

    MethodBodyPatchInfo getReplacementMethodBodyInfo(final String name, final MethodDescriptor descriptor) {
        assert Thread.holdsLock(this);
        return methodBodyReplacedMethods.getOrDefault(name, Map.of()).get(descriptor);
    }

    InitializerPatchInfo getReplacementInitializerInfo() {
        assert Thread.holdsLock(this);
        return replacedInitializer;
    }

    List getAddedClassAnnotations() {
        assert Thread.holdsLock(this);
        return addedClassAnnotations;
    }

    // add

    List getInjectedFields() {
        assert Thread.holdsLock(this);
        return injectedFields;
    }

    List getInjectedConstructors() {
        assert Thread.holdsLock(this);
        return injectedConstructors;
    }

    List getInjectedMethods() {
        assert Thread.holdsLock(this);
        return injectedMethods;
    }

    // modify
    RuntimeInitializerPatchInfo getRuntimeInitFieldInfo(final String fieldName, TypeDescriptor descriptor) {
        RuntimeInitializerPatchInfo rtInitPatchInfo = runtimeInitFields.get(fieldName);
        return rtInitPatchInfo != null && rtInitPatchInfo.getDescriptor().equals(descriptor) ? rtInitPatchInfo : null;
    }

    FieldPatchInfo getAnnotatedFieldInfo(final String fieldName, TypeDescriptor descriptor) {
        FieldPatchInfo fieldPatchInfo = annotatedFields.get(fieldName);
        return fieldPatchInfo != null && fieldPatchInfo.getDescriptor().equals(descriptor) ? fieldPatchInfo : null;
    }

    ConstructorPatchInfo getAnnotatedConstructorInfo(final MethodDescriptor descriptor) {
        assert Thread.holdsLock(this);
        return annotatedConstructors.get(descriptor);
    }

    MethodPatchInfo getAnnotatedMethodInfo(final String name, final MethodDescriptor descriptor) {
        assert Thread.holdsLock(this);
        return annotatedMethods.getOrDefault(name, Map.of()).get(descriptor);
    }

    // Registration methods

    void addField(final FieldPatchInfo fieldPatchInfo) {
        assert Thread.holdsLock(this);
        checkCommitted();
        for (FieldPatchInfo injectedField : injectedFields) {
            if (injectedField.getName().equals(fieldPatchInfo.getName())) {
                // ignore
                return;
            }
        }
        injectedFields = listWith(injectedFields, fieldPatchInfo);
    }

    void deleteField(final String name, final TypeDescriptor descriptor, String internalName, Annotation annotation) {
        assert Thread.holdsLock(this);
        checkCommitted();
        deletedFields = mapWith(deletedFields, name, new FieldDeleteInfo(internalName, descriptor, name, annotation));
    }

    void replaceField(final FieldPatchInfo fieldPatchInfo) {
        assert Thread.holdsLock(this);
        checkCommitted();
        final String name = fieldPatchInfo.getName();
        replacedFields = mapWith(replacedFields, name, fieldPatchInfo);
    }

    void annotateField(final FieldPatchInfo fieldPatchInfo) {
        assert  Thread.holdsLock(this);
        checkCommitted();
        final String name = fieldPatchInfo.getName();
        annotatedFields = mapWith(annotatedFields, name, fieldPatchInfo);
    }

    void runtimeInitField(final RuntimeInitializerPatchInfo runtimeInitPatchInfo) {
        assert Thread.holdsLock(this);
        checkCommitted();
        final String name = runtimeInitPatchInfo.getName();
        runtimeInitFields = mapWith(runtimeInitFields, name, runtimeInitPatchInfo);
    }

    void addConstructor(final ConstructorPatchInfo constructorPatchInfo) {
        assert Thread.holdsLock(this);
        checkCommitted();
        for (ConstructorPatchInfo injectedConstructor : injectedConstructors) {
            if (injectedConstructor.getDescriptor().equals(constructorPatchInfo.getDescriptor())) {
                // ignore
                return;
            }
        }
        injectedConstructors = listWith(injectedConstructors, constructorPatchInfo);
    }

    void deleteConstructor(final MethodDescriptor descriptor, String internalName, Annotation annotation) {
        assert Thread.holdsLock(this);
        checkCommitted();
        deletedConstructors = mapWith(deletedConstructors, descriptor, new ConstructorDeleteInfo(internalName, descriptor, annotation));
    }

    void replaceConstructor(final ConstructorPatchInfo constructorPatchInfo) {
        assert Thread.holdsLock(this);
        checkCommitted();
        final MethodDescriptor descriptor = constructorPatchInfo.getDescriptor();
        replacedConstructors = mapWith(replacedConstructors, descriptor, constructorPatchInfo);
    }

    void annotateConstructor(final ConstructorPatchInfo constructorPatchInfo) {
        assert Thread.holdsLock(this);
        checkCommitted();
        final MethodDescriptor descriptor = constructorPatchInfo.getDescriptor();
        annotatedConstructors = mapWith(annotatedConstructors, descriptor, constructorPatchInfo);
    }

    void addMethod(final MethodPatchInfo methodPatchInfo) {
        assert Thread.holdsLock(this);
        checkCommitted();
        injectedMethods = listWith(injectedMethods, methodPatchInfo);
        for (MethodPatchInfo injectedMethod : injectedMethods) {
            if (injectedMethod.getName().equals(methodPatchInfo.getName()) && injectedMethod.getDescriptor().equals(methodPatchInfo.getDescriptor())) {
                // ignore
                return;
            }
        }
    }

    void deleteMethod(final String name, final MethodDescriptor descriptor, String internalName, Annotation annotation) {
        assert Thread.holdsLock(this);
        checkCommitted();
        deletedMethods = mapWith(deletedMethods, name, mapWith(deletedMethods.getOrDefault(name, Map.of()), descriptor, new MethodDeleteInfo(internalName, name, descriptor, annotation)));
    }

    void replaceMethod(final MethodPatchInfo methodPatchInfo) {
        assert Thread.holdsLock(this);
        checkCommitted();
        final String name = methodPatchInfo.getName();
        final MethodDescriptor descriptor = methodPatchInfo.getDescriptor();
        replacedMethods = mapWith(replacedMethods, name, mapWith(replacedMethods.getOrDefault(name, Map.of()), descriptor, methodPatchInfo));
    }

    void annotateMethod(final MethodPatchInfo methodPatchInfo) {
        assert Thread.holdsLock(this);
        checkCommitted();
        final String name = methodPatchInfo.getName();
        final MethodDescriptor descriptor = methodPatchInfo.getDescriptor();
        annotatedMethods = mapWith(annotatedMethods, name, mapWith(annotatedMethods.getOrDefault(name, Map.of()), descriptor, methodPatchInfo));
    }

    void replaceMethodBody(final String name, final MethodDescriptor descriptor, final MethodBodyFactory methodBodyFactory, final int index) {
        assert Thread.holdsLock(this);
        checkCommitted();
        methodBodyReplacedMethods = mapWith(methodBodyReplacedMethods, name, mapWith(methodBodyReplacedMethods.getOrDefault(name, Map.of()), descriptor, new MethodBodyPatchInfo(methodBodyFactory, index)));
    }

    void deleteInitializer() {
        assert Thread.holdsLock(this);
        deletedInitializer = true;
    }

    void replaceInitializer(final InitializerPatchInfo initializerPatchInfo) {
        assert Thread.holdsLock(this);
        checkCommitted();
        replacedInitializer = initializerPatchInfo;
    }

    void addClassAnnotation(final Annotation annotation) {
        assert Thread.holdsLock(this);
        checkCommitted();
        addedClassAnnotations = listWith(addedClassAnnotations, annotation);
    }

    void setDigest(byte[] digest) {
        Assert.checkNotNullParam("digest", digest);
        checkCommitted();
        this.digest = digest;
    }

    private void checkCommitted() {
        if (committed) {
            throw new IllegalStateException("Class already loaded");
        }
    }

    private  Map mapWith(Map orig, K key, V val) {
        int size = orig.size();
        if (orig instanceof HashMap) {
            // already exploded
            orig.put(key, val);
            return orig;
        } else if (size == 0 || size == 1 && orig.containsKey(key)) {
            return Map.of(key, val);
        } else {
            // explode it
            Map map = new HashMap<>(orig);
            map.put(key, val);
            return map;
        }
    }

    private  Set setWith(Set orig, E elem) {
        int size = orig.size();
        if (orig instanceof HashSet) {
            // already exploded
            orig.add(elem);
            return orig;
        } else if (size == 0 || size == 1 && orig.contains(elem)) {
            return Set.of(elem);
        } else {
            // explode it
            Set set = new HashSet<>(orig);
            set.add(elem);
            return set;
        }
    }

    private  List listWith(List orig, E elem) {
        int size = orig.size();
        if (orig instanceof ArrayList) {
            // already exploded
            orig.add(elem);
            return orig;
        } else if (size == 0) {
            return List.of(elem);
        } else if (size == 1) {
            return List.of(orig.get(0), elem);
        } else if (size == 2) {
            return List.of(orig.get(0), orig.get(1), elem);
        } else {
            // explode it
            List list = new ArrayList<>(orig);
            list.add(elem);
            return list;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy