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

org.mutabilitydetector.checkers.OldSetterMethodChecker Maven / Gradle / Ivy

There is a newer version: 0.10.6
Show newest version
package org.mutabilitydetector.checkers;

/*
 * #%L
 * MutabilityDetector
 * %%
 * Copyright (C) 2008 - 2014 Graham Allan
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */



import static java.lang.String.format;
import static org.mutabilitydetector.checkers.AccessModifierQuery.method;
import static org.mutabilitydetector.checkers.info.MethodIdentifier.forMethod;
import static org.mutabilitydetector.locations.CodeLocation.ClassLocation.fromInternalName;
import static org.mutabilitydetector.locations.Slashed.slashed;

import org.mutabilitydetector.MutabilityReason;
import org.mutabilitydetector.asmoverride.AsmVerifierFactory;
import org.mutabilitydetector.checkers.VarStack.VarStackSnapshot;
import org.mutabilitydetector.checkers.info.MethodIdentifier;
import org.mutabilitydetector.checkers.info.PrivateMethodInvocationInformation;
import org.mutabilitydetector.locations.CodeLocation.FieldLocation;
import org.mutabilitydetector.locations.Dotted;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.analysis.BasicValue;
import org.objectweb.asm.tree.analysis.Frame;

/**
 * This class checks, for each field, that there is no method available which can change the reference of the field.
 * 
 * The check will pass iff there is no method available to change a reference for ANY field.
 * 
 * @author Graham Allan / Grundlefleck at gmail dot com
 * 
 */
public final class OldSetterMethodChecker extends AsmMutabilityChecker {

    private final PrivateMethodInvocationInformation privateMethodInvocationInfo;
    private final AsmVerifierFactory verifierFactory;

    private OldSetterMethodChecker(PrivateMethodInvocationInformation privateMethodInvocationInfo, 
                                 AsmVerifierFactory verifierFactory) {
        this.privateMethodInvocationInfo = privateMethodInvocationInfo;
        this.verifierFactory = verifierFactory;
    }

    public static OldSetterMethodChecker newSetterMethodChecker(PrivateMethodInvocationInformation privateMethodInvocationInfo, AsmVerifierFactory verifierFactory) {
        return new OldSetterMethodChecker(privateMethodInvocationInfo, verifierFactory);
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        return new SetterAssignmentVisitor(ownerClass,
                access,
                name,
                desc,
                signature,
                exceptions, 
                verifierFactory);
    }

    class SetterAssignmentVisitor extends FieldAssignmentVisitor {

        private final VarStack varStack = new VarStack();

        public SetterAssignmentVisitor(String ownerName,
                int access,
                String name,
                String desc,
                String signature,
                String[] exceptions, 
                AsmVerifierFactory verifierFactory) {
            super(ownerName, access, name, desc, signature, exceptions, verifierFactory);
        }

        @Override
        protected void visitFieldAssignmentFrame(Frame assignmentFrame, FieldInsnNode fieldInsnNode, BasicValue stackValue) {
            if (MethodIs.aConstructor(name) || isInvalidStackValue(stackValue)) { return; }

            if (method(access).isStatic()) {
                detectInStaticMethod(fieldInsnNode);
            } else {
                detectInInstanceMethod(fieldInsnNode);
            }

        }

        private boolean isOnlyCalledFromConstructor() {
            MethodIdentifier methodId = forMethod(slashed(this.owner), name + ":" + desc);
            return privateMethodInvocationInfo.isOnlyCalledFromConstructor(methodId);
        }

        private void detectInStaticMethod(FieldInsnNode fieldInsnNode) {
            String ownerOfReassignedField = fieldInsnNode.owner;
            if (reassignedIsThisType(ownerOfReassignedField) && assignmentIsNotOnAParameter(fieldInsnNode)) {
                setIsImmutableResult(fieldInsnNode.name, Dotted.fromFieldInsnNode(fieldInsnNode));
            }
        }

        private boolean assignmentIsNotOnAParameter(FieldInsnNode fieldInsnNode) {
            /*
             * This is a temporary hack/workaround. It's quite difficult to tell for sure if the owner of the reassigned
             * field is a parameter. But if the type is not included in the parameter list, we can guess it's not
             * (though it still may be).
             */
            return this.desc.contains(fieldInsnNode.owner);
        }

        private boolean reassignedIsThisType(String ownerOfReassignedField) {
            return this.owner.compareTo(ownerOfReassignedField) == 0;
        }

        private void detectInInstanceMethod(FieldInsnNode fieldInsnNode) {
            if (isOnlyCalledFromConstructor()) { return; }

            VarStackSnapshot varStackSnapshot = varStack.next();
            if (varStackSnapshot.thisObjectWasAddedToStack()) {
                // Throwing an NPE, assuming it's mutable for now.
                setIsImmutableResult(fieldInsnNode.name, Dotted.fromFieldInsnNode(fieldInsnNode));
            }
        }

        @Override
        public void visitFieldInsn(int opcode, String fieldsOwner, String fieldName, String fieldDesc) {
            super.visitFieldInsn(opcode, fieldsOwner, fieldName, fieldDesc);
            if (opcode == Opcodes.PUTFIELD) {
                varStack.takeSnapshotOfVarsAtPutfield();
            }
        }

        @Override
        public void visitVarInsn(int opcode, int var) {
            super.visitVarInsn(opcode, var);
            varStack.visitVarInsn(var);
        }

        private void setIsImmutableResult(String fieldName, Dotted fieldType) {
            setResult(format("Field [%s] can be reassigned within method [%s]", fieldName, this.name), 
                      FieldLocation.fieldLocation(fieldName, fromInternalName(owner), fieldType), 
                      MutabilityReason.FIELD_CAN_BE_REASSIGNED);
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy