<commit_before>/*
 * fb-contrib - Auxiliary detectors for Java programs
 * Copyright (C) 2005-2017 <NAME>
 *
 * 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 com.mebigfatguy.fbcontrib.detect;

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

import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.LocalVariable;
import org.apache.bcel.classfile.LocalVariableTable;

import com.mebigfatguy.fbcontrib.utils.BugType;
import com.mebigfatguy.fbcontrib.utils.RegisterUtils;
import com.mebigfatguy.fbcontrib.utils.SignatureBuilder;
import com.mebigfatguy.fbcontrib.utils.SignatureUtils;
import com.mebigfatguy.fbcontrib.utils.TernaryPatcher;
import com.mebigfatguy.fbcontrib.utils.ToString;
import com.mebigfatguy.fbcontrib.utils.UnmodifiableSet;
import com.mebigfatguy.fbcontrib.utils.Values;

import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.BytecodeScanningDetector;
import edu.umd.cs.findbugs.OpcodeStack;
import edu.umd.cs.findbugs.OpcodeStack.CustomUserValue;
import edu.umd.cs.findbugs.ba.ClassContext;

/**
 * looks for allocations of objects using the default constructor in a loop, where the object allocated is never assigned to any object that is used outside the
 * loop. It is possible that this allocation can be done outside the loop to avoid excessive garbage.
 */
@CustomUserValue
public class PossibleConstantAllocationInLoop extends BytecodeScanningDetector {

    private static final Set<String> SYNTHETIC_ALLOCATION_CLASSES = UnmodifiableSet.create(Values.SLASHED_JAVA_LANG_STRINGBUFFER,
            Values.SLASHED_JAVA_LANG_STRINGBUILDER, "java/lang/AssertionError");

    private final BugReporter bugReporter;
    private OpcodeStack stack;
    /** allocation number, info where allocated */
    private Map<Integer, AllocationInfo> allocations;
    /** reg, allocation number */
    private Map<Integer, Integer> storedAllocations;
    private int nextAllocationNumber;
    private List<SwitchInfo> switchInfos;

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

    @Override
    public void visitClassContext(ClassContext classContext) {
        try {
            stack = new OpcodeStack();
            allocations = new HashMap<>();
            storedAllocations = new HashMap<>();
            switchInfos = new ArrayList<>();
            super.visitClassContext(classContext);
        } finally {
            stack = null;
            allocations = null;
            storedAllocations = null;
            switchInfos = null;
        }
    }

    @Override
    public void visitCode(Code obj) {
        stack.resetForMethodEntry(this);
        allocations.clear();
        storedAllocations.clear();
        nextAllocationNumber = 1;
        super.visitCode(obj);

        for (AllocationInfo info : allocations.values()) {
            if (info.loopBottom != -1) {
                bugReporter.reportBug(new BugInstance(this, BugType.PCAIL_POSSIBLE_CONSTANT_ALLOCATION_IN_LOOP.name(), NORMAL_PRIORITY).addClass(this)
                        .addMethod(this).addSourceLine(getClassContext(), this, info.allocationPC));
            }
        }
    }

    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "SF_SWITCH_FALLTHROUGH", justification = "This fall-through is deliberate and documented")
    @Override
    public void sawOpcode(int seen) {
        boolean sawAllocation = false;
        Integer sawAllocationNumber = null;

        try {
            stack.precomputation(this);

            switch (seen) {
                case IFEQ:
                case IFNE:
                case IFLT:
                case IFGE:
                case IFGT:
                case IFLE:
                case IF_ICMPEQ:
                case IF_ICMPNE:
                case IF_ICMPLT:
                case IF_ICMPGE:
                case IF_ICMPGT:
                case IF_ICMPLE:
                case IF_ACMPEQ:
                case IF_ACMPNE:
                case IFNULL:
                case IFNONNULL:
                case GOTO:
                case GOTO_W:
                    processBranch();
                break;
                    
                case INVOKESPECIAL:
                    if (Values.CONSTRUCTOR.equals(getNameConstantOperand()) && SignatureBuilder.SIG_VOID_TO_VOID.equals(getSigConstantOperand())) {
                        String clsName = getClassConstantOperand();
                        if (!SYNTHETIC_ALLOCATION_CLASSES.contains(clsName) && switchInfos.isEmpty()) {
                            sawAllocationNumber = Integer.valueOf(nextAllocationNumber);
                            allocations.put(sawAllocationNumber, new AllocationInfo(getPC()));
                            sawAllocation = true;
                        }
                    }
                    //$FALL-THROUGH$

                case INVOKEINTERFACE:
                case INVOKEVIRTUAL:
                case INVOKESTATIC:
                case INVOKEDYNAMIC:
                    String signature = getSigConstantOperand();
                    int numParameters = SignatureUtils.getNumParameters(signature);
                    if (stack.getStackDepth() >= numParameters) {
                        for (int i = 0; i < numParameters; i++) {
                            OpcodeStack.Item item = stack.getStackItem(i);
                            Integer allocation = (Integer) item.getUserValue();
                            if (allocation != null) {
                                allocations.remove(allocation);
                            }
                        }<commit_msg>fix fp with allocations in the second half of a ternary<commit_after>/*
 * fb-contrib - Auxiliary detectors for Java programs
 * Copyright (C) 2005-2017 <NAME>rosius
 *
 * 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 com.mebigfatguy.fbcontrib.detect;

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

import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.LocalVariable;
import org.apache.bcel.classfile.LocalVariableTable;

import com.mebigfatguy.fbcontrib.utils.BugType;
import com.mebigfatguy.fbcontrib.utils.RegisterUtils;
import com.mebigfatguy.fbcontrib.utils.SignatureBuilder;
import com.mebigfatguy.fbcontrib.utils.SignatureUtils;
import com.mebigfatguy.fbcontrib.utils.TernaryPatcher;
import com.mebigfatguy.fbcontrib.utils.ToString;
import com.mebigfatguy.fbcontrib.utils.UnmodifiableSet;
import com.mebigfatguy.fbcontrib.utils.Values;

import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.BytecodeScanningDetector;
import edu.umd.cs.findbugs.OpcodeStack;
import edu.umd.cs.findbugs.OpcodeStack.CustomUserValue;
import edu.umd.cs.findbugs.ba.ClassContext;

/**
 * looks for allocations of objects using the default constructor in a loop, where the object allocated is never assigned to any object that is used outside the
 * loop. It is possible that this allocation can be done outside the loop to avoid excessive garbage.
 */
@CustomUserValue
public class PossibleConstantAllocationInLoop extends BytecodeScanningDetector {

    private static final Set<String> SYNTHETIC_ALLOCATION_CLASSES = UnmodifiableSet.create(Values.SLASHED_JAVA_LANG_STRINGBUFFER,
            Values.SLASHED_JAVA_LANG_STRINGBUILDER, "java/lang/AssertionError");

    private final BugReporter bugReporter;
    private OpcodeStack stack;
    /** allocation number, info where allocated */
    private Map<Integer, AllocationInfo> allocations;
    /** reg, allocation number */
    private Map<Integer, Integer> storedAllocations;
    private int nextAllocationNumber;
    private List<SwitchInfo> switchInfos;
    private int nextTernaryTarget;

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

    @Override
    public void visitClassContext(ClassContext classContext) {
        try {
            stack = new OpcodeStack();
            allocations = new HashMap<>();
            storedAllocations = new HashMap<>();
            switchInfos = new ArrayList<>();
            super.visitClassContext(classContext);
        } finally {
            stack = null;
            allocations = null;
            storedAllocations = null;
            switchInfos = null;
        }
    }

    @Override
    public void visitCode(Code obj) {
        stack.resetForMethodEntry(this);
        allocations.clear();
        storedAllocations.clear();
        nextAllocationNumber = 1;
        nextTernaryTarget = -1;
        super.visitCode(obj);

        for (AllocationInfo info : allocations.values()) {
            if (info.loopBottom != -1) {
                bugReporter.reportBug(new BugInstance(this, BugType.PCAIL_POSSIBLE_CONSTANT_ALLOCATION_IN_LOOP.name(), NORMAL_PRIORITY).addClass(this)
                        .addMethod(this).addSourceLine(getClassContext(), this, info.allocationPC));
            }
        }
    }

    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "SF_SWITCH_FALLTHROUGH", justification = "This fall-through is deliberate and documented")
    @Override
    public void sawOpcode(int seen) {
        boolean sawAllocation = false;
        Integer sawAllocationNumber = null;

        try {
            if ((nextTernaryTarget == getPC()) && (stack.getStackDepth() > 0)) {
                OpcodeStack.Item itm = stack.getStackItem(0);
                sawAllocationNumber = (Integer) itm.getUserValue();
                sawAllocation = sawAllocationNumber != null;
                nextTernaryTarget = -1;
            }

            stack.precomputation(this);

            if ((sawAllocation) && (stack.getStackDepth() > 0)) {
                OpcodeStack.Item itm = stack.getStackItem(0);
                itm.setUserValue(sawAllocationNumber);
                sawAllocationNumber = null;
                sawAllocation = false;
            }

            switch (seen) {
                case GOTO:
                case GOTO_W:
                    if ((getBranchOffset() > 0) && (stack.getStackDepth() > 0)) {
                        // ternary
                        nextTernaryTarget = getBranchTarget();
                    }
                    // $FALL-THROUGH$
                case IFEQ:
                case IFNE:
                case IFLT:
                case IFGE:
                case IFGT:
                case IFLE:
                case IF_ICMPEQ:
                case IF_ICMPNE:
                case IF_ICMPLT:
                case IF_ICMPGE:
                case IF_ICMPGT:
                case IF_ICMPLE:
                case IF_ACMPEQ:
                case IF_ACMPNE:
                case IFNULL:
                case IFNONNULL:

                    processBranch();
                break;

                case INVOKESPECIAL:
                    if (Values.CONSTRUCTOR.equals(getNameConstantOperand()) && SignatureBuilder.SIG_VOID_TO_VOID.equals(getSigConstantOperand())) {
                        String clsName = getClassConstantOperand();
                        if (!SYNTHETIC_ALLOCATION_CLASSES.contains(clsName) && switchInfos.isEmpty()) {
                            sawAllocationNumber = Integer.valueOf(nextAllocationNumber);
                            allocations.put(sawAllocationNumber, new AllocationInfo(getPC()));
                            sawAllocation = true;
                        }
                    }
                    //$FALL-THROUGH$

                case INVOKEINTERFACE:
                case INVOKEVIRTUAL:
                case INVOKESTATIC:
                case INVOKEDYNAMIC:
                    String signature = getSigConstantOperand();
                    int numParameters = SignatureUtils.getNumParameters(signature);
                    if (stack.getStackDepth() >= numParameters) {
                        for (int i = 0; i < numParameters; i++) {
                            OpcodeStack.Item item = stack.getStackItem(i);
                            Integer allocation = (Integer) item.getUserValue();
                            if (allocation != null) {
                                allocations.remove(allocation);
                            }
                        }
{'fraction_non_alphanumeric': 0.07824067353752359, 'fraction_numerical': 0.0044999274205254755, 'mean_word_length': 2.157424381301558, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6081983', 'n_tokens_mistral': 3832, 'n_tokens_neox': 3597, 'n_words': 920}