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  */
14 package lockedregioncodeinjection;
15 
16 import java.util.ArrayList;
17 import java.util.List;
18 import org.objectweb.asm.Type;
19 import org.objectweb.asm.tree.AbstractInsnNode;
20 import org.objectweb.asm.tree.MethodInsnNode;
21 import org.objectweb.asm.tree.analysis.AnalyzerException;
22 import org.objectweb.asm.tree.analysis.BasicInterpreter;
23 import 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  */
29 public class LockTargetStateAnalysis extends BasicInterpreter {
30 
31     private final List<LockTarget> targetLocks;
32 
LockTargetStateAnalysis(List<LockTarget> targetLocks)33     public LockTargetStateAnalysis(List<LockTarget> targetLocks) {
34         this.targetLocks = targetLocks;
35     }
36 
37     @Override
naryOperation(AbstractInsnNode inst, @SuppressWarnings(R) List args)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
newValue(Type type)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
merge(BasicValue v1, BasicValue v2)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