| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
| * in compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package lockedregioncodeinjection; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import org.objectweb.asm.Type; |
| import org.objectweb.asm.tree.AbstractInsnNode; |
| import org.objectweb.asm.tree.MethodInsnNode; |
| import org.objectweb.asm.tree.analysis.AnalyzerException; |
| import org.objectweb.asm.tree.analysis.BasicInterpreter; |
| import org.objectweb.asm.tree.analysis.BasicValue; |
| |
| /** |
| * A simple dataflow analysis to determine if the operands on the stack must be one of target lock |
| * class type. |
| */ |
| public class LockTargetStateAnalysis extends BasicInterpreter { |
| |
| private final List<LockTarget> targetLocks; |
| |
| public LockTargetStateAnalysis(List<LockTarget> targetLocks) { |
| this.targetLocks = targetLocks; |
| } |
| |
| @Override |
| public BasicValue naryOperation(AbstractInsnNode inst, @SuppressWarnings("rawtypes") List args) |
| throws AnalyzerException { |
| // We target the return type of any invocation. |
| |
| @SuppressWarnings("unchecked") |
| BasicValue base = super.naryOperation(inst, args); |
| if (!(inst instanceof MethodInsnNode)) { |
| return base; |
| } |
| |
| MethodInsnNode invoke = (MethodInsnNode) inst; |
| Type returnType = Type.getReturnType(invoke.desc); |
| if (returnType.equals(Type.VOID_TYPE)) { |
| return base; |
| } |
| |
| List<LockTarget> types = new ArrayList<>(); |
| |
| for (LockTarget target : targetLocks) { |
| if (returnType.getDescriptor().equals(target.getTargetDesc())) { |
| types.add(target); |
| } |
| } |
| |
| return new LockTargetState(base.getType(), types); |
| } |
| |
| @Override |
| public BasicValue newValue(Type type) { |
| BasicValue base = super.newValue(type); |
| List<LockTarget> types = new ArrayList<>(); |
| |
| if (type == null) { |
| return base; |
| } |
| for (LockTarget target : targetLocks) { |
| if (type.getDescriptor().equals(target.getTargetDesc())) { |
| types.add(target); |
| } |
| } |
| |
| if (types.isEmpty()) { |
| return base; |
| } |
| |
| return new LockTargetState(base.getType(), types); |
| } |
| |
| @Override |
| public BasicValue merge(BasicValue v1, BasicValue v2) { |
| BasicValue base = super.merge(v1, v2); |
| |
| if (!(v1 instanceof LockTargetState)) { |
| return base; |
| } |
| if (!(v2 instanceof LockTargetState)) { |
| return base; |
| } |
| |
| LockTargetState state1 = (LockTargetState) v1; |
| LockTargetState state2 = (LockTargetState) v2; |
| |
| List<LockTarget> newList = new ArrayList<>(state1.getTargets()); |
| for (LockTarget otherTarget : state2.getTargets()) { |
| if (!newList.contains(otherTarget)) { |
| newList.add(otherTarget); |
| } |
| } |
| |
| return new LockTargetState(base.getType(), newList); |
| } |
| } |