| // Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| package com.android.tools.r8.keepanno.asm; |
| |
| import com.android.tools.r8.keepanno.annotations.KeepConstants.Edge; |
| import com.android.tools.r8.keepanno.annotations.KeepConstants.Target; |
| import com.android.tools.r8.keepanno.ast.KeepConsequences; |
| import com.android.tools.r8.keepanno.ast.KeepEdge; |
| import com.android.tools.r8.keepanno.ast.KeepEdgeException; |
| import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern.KeepFieldNameExactPattern; |
| import com.android.tools.r8.keepanno.ast.KeepFieldPattern; |
| import com.android.tools.r8.keepanno.ast.KeepItemPattern; |
| import com.android.tools.r8.keepanno.ast.KeepMemberPattern; |
| import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern.KeepMethodNameExactPattern; |
| import com.android.tools.r8.keepanno.ast.KeepMethodPattern; |
| import com.android.tools.r8.keepanno.ast.KeepPreconditions; |
| import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern; |
| import com.android.tools.r8.keepanno.utils.Unimplemented; |
| import java.util.function.BiFunction; |
| import org.objectweb.asm.AnnotationVisitor; |
| import org.objectweb.asm.ClassVisitor; |
| import org.objectweb.asm.Opcodes; |
| import org.objectweb.asm.Type; |
| |
| public class KeepEdgeWriter implements Opcodes { |
| |
| public static void writeEdge(KeepEdge edge, ClassVisitor visitor) { |
| writeEdge(edge, visitor::visitAnnotation); |
| } |
| |
| public static void writeEdge( |
| KeepEdge edge, BiFunction<String, Boolean, AnnotationVisitor> getVisitor) { |
| new KeepEdgeWriter().writeEdge(edge, getVisitor.apply(Edge.DESCRIPTOR, false)); |
| } |
| |
| private void writeEdge(KeepEdge edge, AnnotationVisitor visitor) { |
| writePreconditions(visitor, edge.getPreconditions()); |
| writeConsequences(visitor, edge.getConsequences()); |
| visitor.visitEnd(); |
| } |
| |
| private void writePreconditions(AnnotationVisitor visitor, KeepPreconditions preconditions) { |
| if (preconditions.isAlways()) { |
| return; |
| } |
| throw new Unimplemented(); |
| } |
| |
| private void writeConsequences(AnnotationVisitor visitor, KeepConsequences consequences) { |
| assert !consequences.isEmpty(); |
| String ignoredArrayValueName = null; |
| AnnotationVisitor arrayVisitor = visitor.visitArray(Edge.consequences); |
| consequences.forEachTarget( |
| target -> { |
| AnnotationVisitor targetVisitor = |
| arrayVisitor.visitAnnotation(ignoredArrayValueName, Target.DESCRIPTOR); |
| // No options imply keep all. |
| if (!target.getOptions().isKeepAll()) { |
| throw new Unimplemented(); |
| } |
| KeepItemPattern item = target.getItem(); |
| if (item.isAny()) { |
| throw new Unimplemented(); |
| } |
| KeepQualifiedClassNamePattern namePattern = item.getClassNamePattern(); |
| if (namePattern.isExact()) { |
| Type typeConstant = Type.getType(namePattern.getExactDescriptor()); |
| targetVisitor.visit(Target.classConstant, typeConstant); |
| } else { |
| throw new Unimplemented(); |
| } |
| if (!item.getExtendsPattern().isAny()) { |
| throw new Unimplemented(); |
| } |
| writeMember(item.getMemberPattern(), targetVisitor); |
| targetVisitor.visitEnd(); |
| }); |
| arrayVisitor.visitEnd(); |
| } |
| |
| private void writeMember(KeepMemberPattern memberPattern, AnnotationVisitor targetVisitor) { |
| if (memberPattern.isNone()) { |
| // Default is "no methods". |
| return; |
| } |
| if (memberPattern.isAll()) { |
| throw new Unimplemented(); |
| } |
| if (memberPattern.isMethod()) { |
| writeMethod(memberPattern.asMethod(), targetVisitor); |
| } else if (memberPattern.isField()) { |
| writeField(memberPattern.asField(), targetVisitor); |
| } else { |
| throw new KeepEdgeException("Unexpected member pattern: " + memberPattern); |
| } |
| } |
| |
| private void writeField(KeepFieldPattern field, AnnotationVisitor targetVisitor) { |
| KeepFieldNameExactPattern exactFieldName = field.getNamePattern().asExact(); |
| if (exactFieldName != null) { |
| targetVisitor.visit(Target.fieldName, exactFieldName.getName()); |
| } else { |
| throw new Unimplemented(); |
| } |
| if (!field.getAccessPattern().isAny()) { |
| throw new Unimplemented(); |
| } |
| if (!field.getTypePattern().isAny()) { |
| throw new Unimplemented(); |
| } |
| } |
| |
| private void writeMethod(KeepMethodPattern method, AnnotationVisitor targetVisitor) { |
| KeepMethodNameExactPattern exactMethodName = method.getNamePattern().asExact(); |
| if (exactMethodName != null) { |
| targetVisitor.visit(Target.methodName, exactMethodName.getName()); |
| } else { |
| throw new Unimplemented(); |
| } |
| if (!method.getAccessPattern().isAny()) { |
| throw new Unimplemented(); |
| } |
| if (!method.getReturnTypePattern().isAny()) { |
| if (exactMethodName != null |
| && (exactMethodName.getName().equals("<init>") |
| || exactMethodName.getName().equals("<clinit>")) |
| && method.getReturnTypePattern().isVoid()) { |
| // constructors have implicit void return. |
| } else { |
| throw new Unimplemented(); |
| } |
| } |
| if (!method.getParametersPattern().isAny()) { |
| throw new Unimplemented(); |
| } |
| } |
| } |