Search in sources :

Example 6 with PointsToSetInternal

use of soot.jimple.spark.sets.PointsToSetInternal in project soot by Sable.

the class GeomPointsTo method transformToCIResult.

/**
 * For many applications, they only need the context insensitive points-to result.
 * We provide a way to transfer our result back to SPARK.
 * After the transformation, we discard the context sensitive points-to information.
 * Therefore, if context sensitive queries are needed in future, please call ddSolve() for queried pointers first.
 */
public void transformToCIResult() {
    for (IVarAbstraction pn : pointers) {
        if (pn.getRepresentative() != pn)
            continue;
        Node node = pn.getWrappedNode();
        node.discardP2Set();
        PointsToSetInternal ptSet = node.makeP2Set();
        for (AllocNode obj : pn.get_all_points_to_objects()) {
            ptSet.add(obj);
        }
        pn.deleteAll();
    }
    hasTransformed = true;
}
Also used : AllocNode(soot.jimple.spark.pag.AllocNode) PointsToSetInternal(soot.jimple.spark.sets.PointsToSetInternal) FieldRefNode(soot.jimple.spark.pag.FieldRefNode) ContextVarNode(soot.jimple.spark.pag.ContextVarNode) LocalVarNode(soot.jimple.spark.pag.LocalVarNode) Node(soot.jimple.spark.pag.Node) VarNode(soot.jimple.spark.pag.VarNode) AllocNode(soot.jimple.spark.pag.AllocNode)

Example 7 with PointsToSetInternal

use of soot.jimple.spark.sets.PointsToSetInternal in project soot by Sable.

the class DemandCSPointsTo method findContextsForAllocs.

protected AllocAndContextSet findContextsForAllocs(final VarAndContext varAndContext, PointsToSetInternal locs) {
    if (contextForAllocsStack.contains(varAndContext)) {
        // recursion; check depth
        // we're fine for x = x.next
        int oldIndex = contextForAllocsStack.indexOf(varAndContext);
        if (oldIndex != contextForAllocsStack.size() - 1) {
            if (recursionDepth == -1) {
                recursionDepth = oldIndex + 1;
                if (DEBUG) {
                    debugPrint("RECURSION depth = " + recursionDepth);
                }
            } else if (contextForAllocsStack.size() - oldIndex > 5) {
                // just give up
                throw new TerminateEarlyException();
            }
        }
    }
    contextForAllocsStack.push(varAndContext);
    final AllocAndContextSet ret = new AllocAndContextSet();
    final PointsToSetInternal realLocs = checkContextsForAllocsCache(varAndContext, ret, locs);
    if (realLocs.isEmpty()) {
        if (DEBUG) {
            debugPrint("cached result " + ret);
        }
        contextForAllocsStack.pop();
        return ret;
    }
    nesting++;
    if (DEBUG) {
        debugPrint("finding alloc contexts for " + varAndContext);
    }
    try {
        final Set<VarAndContext> marked = new HashSet<VarAndContext>();
        final Stack<VarAndContext> worklist = new Stack<VarAndContext>();
        final Propagator<VarAndContext> p = new Propagator<VarAndContext>(marked, worklist);
        p.prop(varAndContext);
        IncomingEdgeHandler edgeHandler = new IncomingEdgeHandler() {

            @Override
            public void handleAlloc(AllocNode allocNode, VarAndContext origVarAndContext) {
                if (realLocs.contains(allocNode)) {
                    if (DEBUG) {
                        debugPrint("found alloc " + allocNode);
                    }
                    ret.add(new AllocAndContext(allocNode, origVarAndContext.context));
                }
            }

            @Override
            public void handleMatchSrc(final VarNode matchSrc, PointsToSetInternal intersection, VarNode loadBase, VarNode storeBase, VarAndContext origVarAndContext, SparkField field, boolean refine) {
                if (DEBUG) {
                    debugPrint("handling src " + matchSrc);
                    debugPrint("intersection " + intersection);
                }
                if (!refine) {
                    p.prop(new VarAndContext(matchSrc, EMPTY_CALLSTACK));
                    return;
                }
                AllocAndContextSet allocContexts = findContextsForAllocs(new VarAndContext(loadBase, origVarAndContext.context), intersection);
                if (DEBUG) {
                    debugPrint("alloc contexts " + allocContexts);
                }
                for (AllocAndContext allocAndContext : allocContexts) {
                    if (DEBUG) {
                        debugPrint("alloc and context " + allocAndContext);
                    }
                    CallingContextSet matchSrcContexts;
                    if (fieldCheckHeuristic.validFromBothEnds(field)) {
                        matchSrcContexts = findUpContextsForVar(allocAndContext, new VarContextAndUp(storeBase, EMPTY_CALLSTACK, EMPTY_CALLSTACK));
                    } else {
                        matchSrcContexts = findVarContextsFromAlloc(allocAndContext, storeBase);
                    }
                    for (ImmutableStack<Integer> matchSrcContext : matchSrcContexts) {
                        // ret
                        // .add(new Pair<AllocNode,
                        // ImmutableStack<Integer>>(
                        // (AllocNode) n,
                        // matchSrcContext));
                        // ret.addAll(findContextsForAllocs(matchSrc,
                        // matchSrcContext, locs));
                        p.prop(new VarAndContext(matchSrc, matchSrcContext));
                    }
                }
            }

            @Override
            Object getResult() {
                return ret;
            }

            @Override
            void handleAssignSrc(VarAndContext newVarAndContext, VarAndContext origVarAndContext, AssignEdge assignEdge) {
                p.prop(newVarAndContext);
            }

            @Override
            boolean shouldHandleSrc(VarNode src) {
                return realLocs.hasNonEmptyIntersection(src.getP2Set());
            }
        };
        processIncomingEdges(edgeHandler, worklist);
        // update the cache
        if (recursionDepth != -1) {
            // if we're beyond recursion, don't cache anything
            if (contextForAllocsStack.size() > recursionDepth) {
                if (DEBUG) {
                    debugPrint("REMOVING " + varAndContext);
                    debugPrint(contextForAllocsStack.toString());
                }
                contextsForAllocsCache.remove(varAndContext);
            } else {
                assert contextForAllocsStack.size() == recursionDepth : recursionDepth + " " + contextForAllocsStack;
                recursionDepth = -1;
                if (contextsForAllocsCache.containsKey(varAndContext)) {
                    contextsForAllocsCache.get(varAndContext).getO2().addAll(ret);
                } else {
                    PointsToSetInternal storedSet = new HybridPointsToSet(locs.getType(), pag);
                    storedSet.addAll(locs, null);
                    contextsForAllocsCache.put(varAndContext, new Pair<PointsToSetInternal, AllocAndContextSet>(storedSet, ret));
                }
            }
        } else {
            if (contextsForAllocsCache.containsKey(varAndContext)) {
                contextsForAllocsCache.get(varAndContext).getO2().addAll(ret);
            } else {
                PointsToSetInternal storedSet = new HybridPointsToSet(locs.getType(), pag);
                storedSet.addAll(locs, null);
                contextsForAllocsCache.put(varAndContext, new Pair<PointsToSetInternal, AllocAndContextSet>(storedSet, ret));
            }
        }
        nesting--;
        return ret;
    } catch (CallSiteException e) {
        contextsForAllocsCache.remove(varAndContext);
        throw e;
    } finally {
        contextForAllocsStack.pop();
    }
}
Also used : GlobalVarNode(soot.jimple.spark.pag.GlobalVarNode) LocalVarNode(soot.jimple.spark.pag.LocalVarNode) VarNode(soot.jimple.spark.pag.VarNode) PointsToSetInternal(soot.jimple.spark.sets.PointsToSetInternal) SparkField(soot.jimple.spark.pag.SparkField) AssignEdge(soot.jimple.spark.ondemand.pautil.AssignEdge) HybridPointsToSet(soot.jimple.spark.sets.HybridPointsToSet) Stack(soot.jimple.spark.ondemand.genericutil.Stack) ImmutableStack(soot.jimple.spark.ondemand.genericutil.ImmutableStack) AllocNode(soot.jimple.spark.pag.AllocNode) Propagator(soot.jimple.spark.ondemand.genericutil.Propagator) HashSet(java.util.HashSet)

Example 8 with PointsToSetInternal

use of soot.jimple.spark.sets.PointsToSetInternal in project soot by Sable.

the class DemandCSPointsTo method findUpContextsForVar.

protected CallingContextSet findUpContextsForVar(AllocAndContext allocAndContext, VarContextAndUp varContextAndUp) {
    final AllocNode alloc = allocAndContext.alloc;
    final ImmutableStack<Integer> allocContext = allocAndContext.context;
    CallingContextSet tmpSet = checkUpContextCache(varContextAndUp, allocAndContext);
    if (tmpSet != null) {
        return tmpSet;
    }
    final CallingContextSet ret = new CallingContextSet();
    upContextCache.get(varContextAndUp).put(allocAndContext, ret);
    nesting++;
    if (DEBUG) {
        debugPrint("finding up context for " + varContextAndUp + " to " + alloc + " " + allocContext);
    }
    try {
        final Set<VarAndContext> marked = new HashSet<VarAndContext>();
        final Stack<VarAndContext> worklist = new Stack<VarAndContext>();
        final Propagator<VarAndContext> p = new Propagator<VarAndContext>(marked, worklist);
        p.prop(varContextAndUp);
        class UpContextEdgeHandler extends IncomingEdgeHandler {

            @Override
            public void handleAlloc(AllocNode allocNode, VarAndContext origVarAndContext) {
                VarContextAndUp contextAndUp = (VarContextAndUp) origVarAndContext;
                if (allocNode == alloc) {
                    if (allocContext.topMatches(contextAndUp.context)) {
                        ImmutableStack<Integer> reverse = contextAndUp.upContext.reverse();
                        ImmutableStack<Integer> toAdd = allocContext.popAll(contextAndUp.context).pushAll(reverse);
                        if (DEBUG) {
                            debugPrint("found up context " + toAdd);
                        }
                        ret.add(toAdd);
                    } else if (contextAndUp.context.topMatches(allocContext)) {
                        ImmutableStack<Integer> toAdd = contextAndUp.upContext.reverse();
                        if (DEBUG) {
                            debugPrint("found up context " + toAdd);
                        }
                        ret.add(toAdd);
                    }
                }
            }

            @Override
            public void handleMatchSrc(VarNode matchSrc, PointsToSetInternal intersection, VarNode loadBase, VarNode storeBase, VarAndContext origVarAndContext, SparkField field, boolean refine) {
                VarContextAndUp contextAndUp = (VarContextAndUp) origVarAndContext;
                if (DEBUG) {
                    debugPrint("CHECKING " + alloc);
                }
                PointsToSetInternal tmp = new HybridPointsToSet(alloc.getType(), pag);
                tmp.add(alloc);
                AllocAndContextSet allocContexts = findContextsForAllocs(new VarAndContext(matchSrc, EMPTY_CALLSTACK), tmp);
                // Set allocContexts = Collections.singleton(new Object());
                if (!refine) {
                    if (!allocContexts.isEmpty()) {
                        ret.add(contextAndUp.upContext.reverse());
                    }
                } else {
                    if (!allocContexts.isEmpty()) {
                        for (AllocAndContext t : allocContexts) {
                            ImmutableStack<Integer> discoveredAllocContext = t.context;
                            if (!allocContext.topMatches(discoveredAllocContext)) {
                                continue;
                            }
                            ImmutableStack<Integer> trueAllocContext = allocContext.popAll(discoveredAllocContext);
                            AllocAndContextSet allocAndContexts = findContextsForAllocs(new VarAndContext(storeBase, trueAllocContext), intersection);
                            for (AllocAndContext allocAndContext : allocAndContexts) {
                                // CallingContextSet upContexts;
                                if (fieldCheckHeuristic.validFromBothEnds(field)) {
                                    ret.addAll(findUpContextsForVar(allocAndContext, new VarContextAndUp(loadBase, contextAndUp.context, contextAndUp.upContext)));
                                } else {
                                    CallingContextSet tmpContexts = findVarContextsFromAlloc(allocAndContext, loadBase);
                                    // upContexts = new CallingContextSet();
                                    for (ImmutableStack<Integer> tmpContext : tmpContexts) {
                                        if (tmpContext.topMatches(contextAndUp.context)) {
                                            ImmutableStack<Integer> reverse = contextAndUp.upContext.reverse();
                                            ImmutableStack<Integer> toAdd = tmpContext.popAll(contextAndUp.context).pushAll(reverse);
                                            ret.add(toAdd);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            @Override
            Object getResult() {
                return ret;
            }

            @Override
            void handleAssignSrc(VarAndContext newVarAndContext, VarAndContext origVarAndContext, AssignEdge assignEdge) {
                VarContextAndUp contextAndUp = (VarContextAndUp) origVarAndContext;
                ImmutableStack<Integer> upContext = contextAndUp.upContext;
                ImmutableStack<Integer> newUpContext = upContext;
                if (assignEdge.isParamEdge() && contextAndUp.context.isEmpty()) {
                    if (upContext.size() < ImmutableStack.getMaxSize()) {
                        newUpContext = pushWithRecursionCheck(upContext, assignEdge);
                    }
                    ;
                }
                p.prop(new VarContextAndUp(newVarAndContext.var, newVarAndContext.context, newUpContext));
            }

            @Override
            boolean shouldHandleSrc(VarNode src) {
                if (src instanceof GlobalVarNode) {
                    // // for now, just give up
                    throw new TerminateEarlyException();
                }
                return src.getP2Set().contains(alloc);
            }
        }
        ;
        UpContextEdgeHandler edgeHandler = new UpContextEdgeHandler();
        processIncomingEdges(edgeHandler, worklist);
        nesting--;
        // }
        return ret;
    } catch (CallSiteException e) {
        upContextCache.remove(varContextAndUp);
        throw e;
    }
}
Also used : GlobalVarNode(soot.jimple.spark.pag.GlobalVarNode) SparkField(soot.jimple.spark.pag.SparkField) AssignEdge(soot.jimple.spark.ondemand.pautil.AssignEdge) Propagator(soot.jimple.spark.ondemand.genericutil.Propagator) ImmutableStack(soot.jimple.spark.ondemand.genericutil.ImmutableStack) HashSet(java.util.HashSet) GlobalVarNode(soot.jimple.spark.pag.GlobalVarNode) LocalVarNode(soot.jimple.spark.pag.LocalVarNode) VarNode(soot.jimple.spark.pag.VarNode) PointsToSetInternal(soot.jimple.spark.sets.PointsToSetInternal) HybridPointsToSet(soot.jimple.spark.sets.HybridPointsToSet) Stack(soot.jimple.spark.ondemand.genericutil.Stack) ImmutableStack(soot.jimple.spark.ondemand.genericutil.ImmutableStack) AllocNode(soot.jimple.spark.pag.AllocNode)

Example 9 with PointsToSetInternal

use of soot.jimple.spark.sets.PointsToSetInternal in project soot by Sable.

the class DemandCSPointsTo method getFlowsToHelper.

protected Set<VarNode> getFlowsToHelper(AllocAndContext allocAndContext) {
    Set<VarNode> ret = new ArraySet<VarNode>();
    try {
        HashSet<VarAndContext> marked = new HashSet<VarAndContext>();
        Stack<VarAndContext> worklist = new Stack<VarAndContext>();
        Propagator<VarAndContext> p = new Propagator<VarAndContext>(marked, worklist);
        AllocNode alloc = allocAndContext.alloc;
        ImmutableStack<Integer> allocContext = allocAndContext.context;
        Node[] newBarNodes = pag.allocLookup(alloc);
        for (int i = 0; i < newBarNodes.length; i++) {
            VarNode v = (VarNode) newBarNodes[i];
            ret.add(v);
            p.prop(new VarAndContext(v, allocContext));
        }
        while (!worklist.isEmpty()) {
            incrementNodesTraversed();
            VarAndContext curVarAndContext = worklist.pop();
            if (DEBUG) {
                debugPrint("looking at " + curVarAndContext);
            }
            VarNode curVar = curVarAndContext.var;
            ImmutableStack<Integer> curContext = curVarAndContext.context;
            ret.add(curVar);
            // assign
            Collection<AssignEdge> assignEdges = filterAssigns(curVar, curContext, false, true);
            for (AssignEdge assignEdge : assignEdges) {
                VarNode dst = assignEdge.getDst();
                ImmutableStack<Integer> newContext = curContext;
                if (assignEdge.isReturnEdge()) {
                    if (!curContext.isEmpty()) {
                        if (!callEdgeInSCC(assignEdge)) {
                            assert assignEdge.getCallSite().equals(curContext.peek()) : assignEdge + " " + curContext;
                            newContext = curContext.pop();
                        } else {
                            newContext = popRecursiveCallSites(curContext);
                        }
                    }
                } else if (assignEdge.isParamEdge()) {
                    if (DEBUG)
                        debugPrint("entering call site " + assignEdge.getCallSite());
                    // if (!isRecursive(curContext, assignEdge)) {
                    // newContext = curContext.push(assignEdge
                    // .getCallSite());
                    // }
                    newContext = pushWithRecursionCheck(curContext, assignEdge);
                }
                if (assignEdge.isReturnEdge() && curContext.isEmpty() && csInfo.isVirtCall(assignEdge.getCallSite())) {
                    Set<SootMethod> targets = refineCallSite(assignEdge.getCallSite(), newContext);
                    if (!targets.contains(((LocalVarNode) assignEdge.getDst()).getMethod())) {
                        continue;
                    }
                }
                if (dst instanceof GlobalVarNode) {
                    newContext = EMPTY_CALLSTACK;
                }
                p.prop(new VarAndContext(dst, newContext));
            }
            // putfield_bars
            Set<VarNode> matchTargets = vMatches.vMatchLookup(curVar);
            Node[] pfTargets = pag.storeLookup(curVar);
            for (int i = 0; i < pfTargets.length; i++) {
                FieldRefNode frNode = (FieldRefNode) pfTargets[i];
                final VarNode storeBase = frNode.getBase();
                SparkField field = frNode.getField();
                // FieldRefNode>(curVar, frNode);
                for (Pair<VarNode, VarNode> load : fieldToLoads.get(field)) {
                    final VarNode loadBase = load.getO2();
                    final PointsToSetInternal loadBaseP2Set = loadBase.getP2Set();
                    final PointsToSetInternal storeBaseP2Set = storeBase.getP2Set();
                    final VarNode matchTgt = load.getO1();
                    if (matchTargets.contains(matchTgt)) {
                        if (DEBUG) {
                            debugPrint("match source " + matchTgt);
                        }
                        PointsToSetInternal intersection = SootUtil.constructIntersection(storeBaseP2Set, loadBaseP2Set, pag);
                        boolean checkField = fieldCheckHeuristic.validateMatchesForField(field);
                        if (checkField) {
                            AllocAndContextSet sharedAllocContexts = findContextsForAllocs(new VarAndContext(storeBase, curContext), intersection);
                            for (AllocAndContext curAllocAndContext : sharedAllocContexts) {
                                CallingContextSet upContexts;
                                if (fieldCheckHeuristic.validFromBothEnds(field)) {
                                    upContexts = findUpContextsForVar(curAllocAndContext, new VarContextAndUp(loadBase, EMPTY_CALLSTACK, EMPTY_CALLSTACK));
                                } else {
                                    upContexts = findVarContextsFromAlloc(curAllocAndContext, loadBase);
                                }
                                for (ImmutableStack<Integer> upContext : upContexts) {
                                    p.prop(new VarAndContext(matchTgt, upContext));
                                }
                            }
                        } else {
                            p.prop(new VarAndContext(matchTgt, EMPTY_CALLSTACK));
                        }
                    // h.handleMatchSrc(matchSrc, intersection,
                    // storeBase,
                    // loadBase, varAndContext, checkGetfield);
                    // if (h.terminate())
                    // return;
                    }
                }
            }
        }
        return ret;
    } catch (CallSiteException e) {
        allocAndContextCache.remove(allocAndContext);
        throw e;
    }
}
Also used : GlobalVarNode(soot.jimple.spark.pag.GlobalVarNode) FieldRefNode(soot.jimple.spark.pag.FieldRefNode) GlobalVarNode(soot.jimple.spark.pag.GlobalVarNode) LocalVarNode(soot.jimple.spark.pag.LocalVarNode) Node(soot.jimple.spark.pag.Node) VarNode(soot.jimple.spark.pag.VarNode) AllocNode(soot.jimple.spark.pag.AllocNode) AssignEdge(soot.jimple.spark.ondemand.pautil.AssignEdge) SparkField(soot.jimple.spark.pag.SparkField) Propagator(soot.jimple.spark.ondemand.genericutil.Propagator) HashSet(java.util.HashSet) GlobalVarNode(soot.jimple.spark.pag.GlobalVarNode) LocalVarNode(soot.jimple.spark.pag.LocalVarNode) VarNode(soot.jimple.spark.pag.VarNode) ArraySet(soot.jimple.spark.ondemand.genericutil.ArraySet) PointsToSetInternal(soot.jimple.spark.sets.PointsToSetInternal) Stack(soot.jimple.spark.ondemand.genericutil.Stack) ImmutableStack(soot.jimple.spark.ondemand.genericutil.ImmutableStack) FieldRefNode(soot.jimple.spark.pag.FieldRefNode) AllocNode(soot.jimple.spark.pag.AllocNode) SootMethod(soot.SootMethod) LocalVarNode(soot.jimple.spark.pag.LocalVarNode)

Example 10 with PointsToSetInternal

use of soot.jimple.spark.sets.PointsToSetInternal in project soot by Sable.

the class DemandCSPointsTo method checkContextsForAllocsCache.

protected PointsToSetInternal checkContextsForAllocsCache(VarAndContext varAndContext, AllocAndContextSet ret, PointsToSetInternal locs) {
    PointsToSetInternal retSet = null;
    if (contextsForAllocsCache.containsKey(varAndContext)) {
        for (AllocAndContext allocAndContext : contextsForAllocsCache.get(varAndContext).getO2()) {
            if (locs.contains(allocAndContext.alloc)) {
                ret.add(allocAndContext);
            }
        }
        final PointsToSetInternal oldLocs = contextsForAllocsCache.get(varAndContext).getO1();
        final PointsToSetInternal tmpSet = new HybridPointsToSet(locs.getType(), pag);
        locs.forall(new P2SetVisitor() {

            @Override
            public void visit(Node n) {
                if (!oldLocs.contains(n)) {
                    tmpSet.add(n);
                }
            }
        });
        retSet = tmpSet;
        oldLocs.addAll(tmpSet, null);
    } else {
        PointsToSetInternal storedSet = new HybridPointsToSet(locs.getType(), pag);
        storedSet.addAll(locs, null);
        contextsForAllocsCache.put(varAndContext, new Pair<PointsToSetInternal, AllocAndContextSet>(storedSet, new AllocAndContextSet()));
        retSet = locs;
    }
    return retSet;
}
Also used : PointsToSetInternal(soot.jimple.spark.sets.PointsToSetInternal) FieldRefNode(soot.jimple.spark.pag.FieldRefNode) GlobalVarNode(soot.jimple.spark.pag.GlobalVarNode) LocalVarNode(soot.jimple.spark.pag.LocalVarNode) Node(soot.jimple.spark.pag.Node) VarNode(soot.jimple.spark.pag.VarNode) AllocNode(soot.jimple.spark.pag.AllocNode) HybridPointsToSet(soot.jimple.spark.sets.HybridPointsToSet) P2SetVisitor(soot.jimple.spark.sets.P2SetVisitor)

Aggregations

PointsToSetInternal (soot.jimple.spark.sets.PointsToSetInternal)37 AllocNode (soot.jimple.spark.pag.AllocNode)32 VarNode (soot.jimple.spark.pag.VarNode)28 Node (soot.jimple.spark.pag.Node)25 FieldRefNode (soot.jimple.spark.pag.FieldRefNode)22 LocalVarNode (soot.jimple.spark.pag.LocalVarNode)15 P2SetVisitor (soot.jimple.spark.sets.P2SetVisitor)15 SparkField (soot.jimple.spark.pag.SparkField)12 ClassConstantNode (soot.jimple.spark.pag.ClassConstantNode)9 GlobalVarNode (soot.jimple.spark.pag.GlobalVarNode)9 NewInstanceNode (soot.jimple.spark.pag.NewInstanceNode)9 HashSet (java.util.HashSet)8 AllocDotField (soot.jimple.spark.pag.AllocDotField)8 SootMethod (soot.SootMethod)7 AssignEdge (soot.jimple.spark.ondemand.pautil.AssignEdge)7 Type (soot.Type)5 ImmutableStack (soot.jimple.spark.ondemand.genericutil.ImmutableStack)5 Stack (soot.jimple.spark.ondemand.genericutil.Stack)5 ContextVarNode (soot.jimple.spark.pag.ContextVarNode)5 Propagator (soot.jimple.spark.ondemand.genericutil.Propagator)4