1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14package lockedregioncodeinjection;
15
16import java.util.ArrayList;
17import java.util.List;
18import org.objectweb.asm.Type;
19import org.objectweb.asm.tree.AbstractInsnNode;
20import org.objectweb.asm.tree.MethodInsnNode;
21import org.objectweb.asm.tree.analysis.AnalyzerException;
22import org.objectweb.asm.tree.analysis.BasicInterpreter;
23import org.objectweb.asm.tree.analysis.BasicValue;
24
25/**
26 * A simple dataflow analysis to determine if the operands on the stack must be one of target lock
27 * class type.
28 */
29public class LockTargetStateAnalysis extends BasicInterpreter {
30
31    private final List<LockTarget> targetLocks;
32
33    public LockTargetStateAnalysis(List<LockTarget> targetLocks) {
34        this.targetLocks = targetLocks;
35    }
36
37    @Override
38    public BasicValue naryOperation(AbstractInsnNode inst, @SuppressWarnings("rawtypes") List args)
39            throws AnalyzerException {
40        // We target the return type of any invocation.
41
42        @SuppressWarnings("unchecked")
43        BasicValue base = super.naryOperation(inst, args);
44        if (!(inst instanceof MethodInsnNode)) {
45            return base;
46        }
47
48        MethodInsnNode invoke = (MethodInsnNode) inst;
49        Type returnType = Type.getReturnType(invoke.desc);
50        if (returnType.equals(Type.VOID_TYPE)) {
51            return base;
52        }
53
54        List<LockTarget> types = new ArrayList<>();
55
56        for (LockTarget target : targetLocks) {
57            if (returnType.getDescriptor().equals(target.getTargetDesc())) {
58                types.add(target);
59            }
60        }
61
62        return new LockTargetState(base.getType(), types);
63    }
64
65    @Override
66    public BasicValue newValue(Type type) {
67        BasicValue base = super.newValue(type);
68        List<LockTarget> types = new ArrayList<>();
69
70        if (type == null) {
71            return base;
72        }
73        for (LockTarget target : targetLocks) {
74            if (type.getDescriptor().equals(target.getTargetDesc())) {
75                types.add(target);
76            }
77        }
78
79        if (types.isEmpty()) {
80            return base;
81        }
82
83        return new LockTargetState(base.getType(), types);
84    }
85
86    @Override
87    public BasicValue merge(BasicValue v1, BasicValue v2) {
88        BasicValue base = super.merge(v1, v2);
89
90        if (!(v1 instanceof LockTargetState)) {
91            return base;
92        }
93        if (!(v2 instanceof LockTargetState)) {
94            return base;
95        }
96
97        LockTargetState state1 = (LockTargetState) v1;
98        LockTargetState state2 = (LockTargetState) v2;
99
100        List<LockTarget> newList = new ArrayList<>(state1.getTargets());
101        for (LockTarget otherTarget : state2.getTargets()) {
102            if (!newList.contains(otherTarget)) {
103                newList.add(otherTarget);
104            }
105        }
106
107        return new LockTargetState(base.getType(), newList);
108    }
109}
110