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

edu.umd.cs.findbugs.detect.InitializationChain Maven / Gradle / Ivy

The newest version!
/*
 * FindBugs - Find bugs in Java programs
 * Copyright (C) 2003,2004 University of Maryland
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs.detect;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import org.apache.bcel.Const;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;

import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.BytecodeScanningDetector;
import edu.umd.cs.findbugs.FieldAnnotation;
import edu.umd.cs.findbugs.MethodAnnotation;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.ba.XField;
import edu.umd.cs.findbugs.ba.XMethod;

public class InitializationChain extends BytecodeScanningDetector {
    Set requires = new TreeSet<>();

    Map> classRequires = new TreeMap<>();



    private final BugReporter bugReporter;

    private final Map> staticFieldsRead = new HashMap<>();
    private final Set staticFieldsReadInAnyConstructor = new HashSet<>();
    private Set fieldsReadInThisConstructor = new HashSet<>();

    private final Set constructorsInvokedInStaticInitializer = new HashSet<>();
    private final List invocationInfo = new ArrayList<>();
    private final Set warningGiven = new HashSet<>();

    private InvocationInfo lastInvocation;

    static class InvocationInfo {
        public InvocationInfo(XMethod constructor, int pc) {
            this.constructor = constructor;
            this.pc = pc;
        }

        XMethod constructor;
        int pc;
        XField field;
    }

    private static final boolean DEBUG = SystemProperties.getBoolean("ic.debug");

    public InitializationChain(BugReporter bugReporter) {
        this.bugReporter = bugReporter;
    }

    @Override
    protected Iterable getMethodVisitOrder(JavaClass obj) {
        ArrayList visitOrder = new ArrayList<>();
        Method staticInitializer = null;
        for (Method m : obj.getMethods()) {
            String name = m.getName();
            if (Const.STATIC_INITIALIZER_NAME.equals(name)) {
                staticInitializer = m;
            } else if (Const.CONSTRUCTOR_NAME.equals(name)) {
                visitOrder.add(m);
            }

        }
        if (staticInitializer != null) {
            visitOrder.add(staticInitializer);
        }
        return visitOrder;
    }


    @Override
    public void visit(Code obj) {
        fieldsReadInThisConstructor = new HashSet<>();
        super.visit(obj);
        staticFieldsRead.put(getXMethod(), fieldsReadInThisConstructor);
        requires.remove(getDottedClassName());
        if ("java.lang.System".equals(getDottedClassName())) {
            requires.add("java.io.FileInputStream");
            requires.add("java.io.FileOutputStream");
            requires.add("java.io.BufferedInputStream");
            requires.add("java.io.BufferedOutputStream");
            requires.add("java.io.PrintStream");
        }
        if (!requires.isEmpty()) {
            classRequires.put(getDottedClassName(), requires);
            requires = new TreeSet<>();
        }
    }

    @Override
    public void visitAfter(JavaClass obj) {

        staticFieldsRead.clear();

        staticFieldsReadInAnyConstructor.clear();
        fieldsReadInThisConstructor.clear();

        constructorsInvokedInStaticInitializer.clear();
        invocationInfo.clear();
        lastInvocation = null;

    }

    @Override
    public void sawOpcode(int seen) {
        InvocationInfo prev = lastInvocation;
        lastInvocation = null;
        if (Const.CONSTRUCTOR_NAME.equals(getMethodName())) {
            if (seen == Const.GETSTATIC && getClassConstantOperand().equals(getClassName())) {
                staticFieldsReadInAnyConstructor.add(getXFieldOperand());
                fieldsReadInThisConstructor.add(getXFieldOperand());
            }
            return;
        }

        if (seen == Const.INVOKESPECIAL && Const.CONSTRUCTOR_NAME.equals(getNameConstantOperand()) && getClassConstantOperand().equals(
                getClassName())) {

            XMethod m = getXMethodOperand();
            Set read = staticFieldsRead.get(m);
            if (constructorsInvokedInStaticInitializer.add(m) && read != null && !read.isEmpty()) {
                lastInvocation = new InvocationInfo(m, getPC());
                invocationInfo.add(lastInvocation);

            }

        }
        if (seen == Const.PUTSTATIC && getClassConstantOperand().equals(getClassName())) {
            XField f = getXFieldOperand();
            if (prev != null) {
                prev.field = f;
            }
            if (staticFieldsReadInAnyConstructor.contains(f) && !warningGiven.contains(f)) {
                for (InvocationInfo i : invocationInfo) {
                    Set fields = staticFieldsRead.get(i.constructor);
                    if (fields != null && fields.contains(f)) {
                        warningGiven.add(f);
                        BugInstance bug = new BugInstance(this, "SI_INSTANCE_BEFORE_FINALS_ASSIGNED", NORMAL_PRIORITY).addClassAndMethod(this);
                        if (i.field != null) {
                            bug.addField(i.field).describe(FieldAnnotation.STORED_ROLE);
                        }
                        bug.addMethod(i.constructor).describe(MethodAnnotation.METHOD_CONSTRUCTOR);
                        bug.addReferencedField(this).describe(FieldAnnotation.VALUE_OF_ROLE).addSourceLine(this, i.pc);
                        bugReporter.reportBug(bug);
                        break;

                    }
                }
            }

        } else if (seen == Const.PUTSTATIC || seen == Const.GETSTATIC || seen == Const.INVOKESTATIC || seen == Const.NEW) {
            if (getPC() + 6 < codeBytes.length) {
                requires.add(getDottedClassConstantOperand());
            }
        }
    }

    public void compute() {
        Set allClasses = classRequires.keySet();
        Set emptyClasses = new TreeSet<>();
        for (String c : allClasses) {
            Set needs = classRequires.get(c);
            needs.retainAll(allClasses);
            Set extra = new TreeSet<>();
            for (String need : needs) {
                extra.addAll(classRequires.get(need));
            }
            needs.addAll(extra);
            needs.retainAll(allClasses);
            classRequires.put(c, needs);
            if (needs.isEmpty()) {
                emptyClasses.add(c);
            }
        }
        for (String c : emptyClasses) {
            classRequires.remove(c);
        }
    }

    @Override
    public void report() {

        if (DEBUG) {
            System.out.println("Finishing computation");
        }
        compute();
        compute();
        compute();
        compute();
        compute();
        compute();
        compute();
        compute();

        for (Entry> entry : classRequires.entrySet()) {
            String c = entry.getKey();
            if (DEBUG) {
                System.out.println("Class " + c + " requires:");
            }
            for (String needs : entry.getValue()) {
                if (DEBUG) {
                    System.out.println("  " + needs);
                }
                Set s = classRequires.get(needs);
                if (s != null && s.contains(c) && c.compareTo(needs) < 0) {
                    bugReporter.reportBug(new BugInstance(this, "IC_INIT_CIRCULARITY", NORMAL_PRIORITY).addClass(c).addClass(
                            needs));
                }
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy