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

pascal.taie.analysis.pta.plugin.assertion.Checkers Maven / Gradle / Ivy

The newest version!
/*
 * Tai-e: A Static Analysis Framework for Java
 *
 * Copyright (C) 2022 Tian Tan 
 * Copyright (C) 2022 Yue Li 
 *
 * This file is part of Tai-e.
 *
 * Tai-e 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 3
 * of the License, or (at your option) any later version.
 *
 * Tai-e 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 Tai-e. If not, see .
 */

package pascal.taie.analysis.pta.plugin.assertion;

import pascal.taie.analysis.pta.core.heap.Obj;
import pascal.taie.analysis.pta.plugin.util.InvokeUtils;
import pascal.taie.ir.IR;
import pascal.taie.ir.exp.IntLiteral;
import pascal.taie.ir.exp.StringLiteral;
import pascal.taie.ir.exp.Var;
import pascal.taie.ir.stmt.Invoke;
import pascal.taie.ir.stmt.StoreArray;
import pascal.taie.language.classes.JMethod;
import pascal.taie.language.type.Type;
import pascal.taie.util.collection.CollectionUtils;
import pascal.taie.util.collection.Maps;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Implementations of {@link Checker}.
 */
enum Checkers {

    NOT_EMPTY("void notEmpty(java.lang.Object[])", (invoke, pta, __, ___) -> {
        List checkVars = getStoredVariables(invoke, 0);
        String assertion = String.format(
                "points-to sets of variables %s are not empty", checkVars);
        Map> failures = Maps.newLinkedHashMap();
        checkVars.forEach(v -> {
            Set pts = pta.getPointsToSet(v);
            if (pts.isEmpty()) {
                failures.put(v, pts);
            }
        });
        return new Result(invoke, assertion, failures);
    }),
    SIZE_EQUALS("void sizeEquals(int,java.lang.Object[])", (invoke, pta, __, ___) -> {
        int size = getInt(InvokeUtils.getVar(invoke, 0));
        List checkVars = getStoredVariables(invoke, 1);
        String assertion = String.format(
                "size of points-to sets of variables %s is %d", checkVars, size);
        Map> failures = Maps.newLinkedHashMap();
        checkVars.forEach(v -> {
            Set pts = pta.getPointsToSet(v);
            if (pts.size() != size) {
                failures.put(v, pts);
            }
        });
        return new Result(invoke, assertion, failures);
    }),
    EQUALS("void equals(java.lang.Object[])", (invoke, pta, __, ___) -> {
        List checkVars = getStoredVariables(invoke, 0);
        String assertion = String.format(
                "points-to sets of variables %s are equal", checkVars);
        Set pts = pta.getPointsToSet(CollectionUtils.getOne(checkVars));
        Map> failures = Maps.newLinkedHashMap();
        if (!checkVars.stream()
                .map(pta::getPointsToSet)
                .allMatch(pts::equals)) {
            checkVars.forEach(v -> failures.put(v, pta.getPointsToSet(v)));
        }
        return new Result(invoke, assertion, failures);
    }),
    CONTAINS("void contains(java.lang.Object,java.lang.Object[])", (invoke, pta, __, ___) -> {
        Var x = InvokeUtils.getVar(invoke, 0);
        List checkVars = getStoredVariables(invoke, 1);
        String assertion = String.format(
                "pt(%s) contains points-to sets of variables %s", x, checkVars);
        Set xPts = pta.getPointsToSet(x);
        Map> failures = Maps.newLinkedHashMap();
        checkVars.forEach(v -> {
            Set vPts = pta.getPointsToSet(v);
            if (!xPts.containsAll(vPts)) {
                failures.put(x, xPts);
                failures.put(v, vPts);
            }
        });
        return new Result(invoke, assertion, failures);
    }),
    INSTANCEOF_IN("void instanceOfIn(java.lang.String,java.lang.Object[])", (invoke, pta, __, typeSystem) -> {
        String typeName = getString(InvokeUtils.getVar(invoke, 0));
        Type expected = typeSystem.getType(typeName);
        List checkVars = getStoredVariables(invoke, 1);
        String assertion = String.format(
                "points-to sets of variables %s has instance of %s", checkVars, expected);
        Map> failures = Maps.newLinkedHashMap();
        checkVars.forEach(v -> {
            Set pts = pta.getPointsToSet(v);
            if (pts.stream()
                    .map(Obj::getType)
                    .noneMatch(actual -> typeSystem.isSubtype(expected, actual))) {
                failures.put(v, pts);
            }
        });
        return new Result(invoke, assertion, failures);
    }),
    HAS_INSTANCEOF("void hasInstanceOf(java.lang.Object,java.lang.String[])", (invoke, pta, __, typeSystem) -> {
        Var x = InvokeUtils.getVar(invoke, 0);
        List expectedTypes = getStoredVariables(invoke, 1)
                .stream()
                .map(v -> typeSystem.getType(getString(v)))
                .toList();
        String assertion = String.format(
                "pt(%s) has instances of %s", x, expectedTypes);
        Set pts = pta.getPointsToSet(x);
        Set actualTypes = pts.stream()
                .map(Obj::getType)
                .collect(Collectors.toUnmodifiableSet());
        Map> failures = Maps.newHybridMap();
        for (Type expected : expectedTypes) {
            if (actualTypes.stream()
                    .noneMatch(actual -> typeSystem.isSubtype(expected, actual))) {
                failures.put(x, pts);
                break;
            }
        }
        return new Result(invoke, assertion, failures);
    }),
    NOT_EQUALS("void notEquals(java.lang.Object,java.lang.Object)", (invoke, pta, __, ___) -> {
        Var x = InvokeUtils.getVar(invoke, 0);
        Var y = InvokeUtils.getVar(invoke, 1);
        String assertion = String.format("pt(%s) != pt(%s)", x, y);
        Map> failures = Maps.newLinkedHashMap();
        Set xPts = pta.getPointsToSet(x), yPts = pta.getPointsToSet(y);
        if (xPts.equals(yPts)) {
            failures.put(x, xPts);
            failures.put(y, yPts);
        }
        return new Result(invoke, assertion, failures);
    }),
    DISJOINT("void disjoint(java.lang.Object,java.lang.Object)", (invoke, pta, __, ___) -> {
        Var x = InvokeUtils.getVar(invoke, 0);
        Var y = InvokeUtils.getVar(invoke, 1);
        String assertion = String.format("pt(%s) ^ pt(%s) = {}", x, y);
        Map> failures = Maps.newLinkedHashMap();
        Set xPts = pta.getPointsToSet(x), yPts = pta.getPointsToSet(y);
        if (!Collections.disjoint(xPts, yPts)) {
            failures.put(x, xPts);
            failures.put(y, yPts);
        }
        return new Result(invoke, assertion, failures);
    }),
    CALLS("void calls(java.lang.String[])", (invoke, pta, hierarchy, ___)  -> {
        Invoke callSite = findCallSiteBefore(invoke);
        List expectedCallees = getStoredVariables(invoke, 0)
                .stream()
                .map(v -> hierarchy.getMethod(getString(v)))
                .toList();
        String assertion = String.format("%s calls %s", callSite, expectedCallees);
        Set actualCallees = pta.getCallGraph().getCalleesOf(callSite);
        Map> failures = actualCallees.containsAll(expectedCallees)
                ? Map.of()
                : Map.of(callSite, actualCallees);
        return new Result(invoke, assertion, failures);
    }),
    CALLS_EXACT("void callsExact(java.lang.String[])", (invoke, pta, hierarchy, ___)  -> {
        Invoke callSite = findCallSiteBefore(invoke);
        Set expectedCallees = getStoredVariables(invoke, 0)
                .stream()
                .map(v -> hierarchy.getMethod(getString(v)))
                .filter(Objects::nonNull)
                .collect(Collectors.toUnmodifiableSet());
        String assertion = String.format("%s calls exactly %s", callSite, expectedCallees);
        Set actualCallees = pta.getCallGraph().getCalleesOf(callSite);
        Map> failures = actualCallees.equals(expectedCallees)
                ? Map.of()
                : Map.of(callSite, actualCallees);
        return new Result(invoke, assertion, failures);
    }),
    REACHABLE("void reachable(java.lang.String[])", (invoke, pta, hierarchy, ___)  -> {
        List reachable = getStoredVariables(invoke, 0)
                .stream()
                .map(v -> hierarchy.getMethod(getString(v)))
                .toList();
        String assertion = String.format("%s are reachable", reachable);
        Map failures = Maps.newLinkedHashMap();
        reachable.forEach(method -> {
            if (!pta.getCallGraph().contains(method)) {
                failures.put(method, "unreachable");
            }
        });
        return new Result(invoke, assertion, failures);
    }),
    ;

    private final String api;

    private final Checker checker;

    Checkers(String subsig, Checker checker) {
        this.api = String.format("<%s: %s>", AssertionChecker.PTA_ASSERT, subsig);
        this.checker = checker;
    }

    String getApi() {
        return api;
    }

    Checker getChecker() {
        return checker;
    }

    private static List getStoredVariables(Invoke invoke, int index) {
        Var array = InvokeUtils.getVar(invoke, index);
        return invoke.getContainer().getIR()
                .stmts()
                .filter(s -> s instanceof StoreArray store
                        && store.getArrayAccess().getBase().equals(array))
                .map(s -> ((StoreArray) s).getRValue())
                .toList();
    }

    private static int getInt(Var var) {
        return ((IntLiteral) var.getConstValue()).getValue();
    }

    private static String getString(Var var) {
        return ((StringLiteral) var.getConstValue()).getString();
    }

    private static Invoke findCallSiteBefore(Invoke invoke) {
        IR ir = invoke.getContainer().getIR();
        for (int i = invoke.getIndex() - 1; i >= 0; --i) {
            if (ir.getStmt(i) instanceof Invoke callSite) {
                return callSite;
            }
        }
        throw new RuntimeException("No call site before " + invoke);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy