| // Copyright (c) 2024, 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 static com.android.tools.r8.keepanno.ast.KeepTypePattern.fromDescriptor; |
| |
| import com.android.tools.r8.keepanno.ast.KeepClassItemPattern; |
| import com.android.tools.r8.keepanno.ast.KeepEdgeMetaInfo; |
| import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern; |
| import com.android.tools.r8.keepanno.ast.KeepFieldPattern; |
| import com.android.tools.r8.keepanno.ast.KeepFieldTypePattern; |
| import com.android.tools.r8.keepanno.ast.KeepItemPattern; |
| import com.android.tools.r8.keepanno.ast.KeepMemberItemPattern; |
| import com.android.tools.r8.keepanno.ast.KeepMemberPattern; |
| import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern; |
| import com.android.tools.r8.keepanno.ast.KeepMethodParametersPattern; |
| import com.android.tools.r8.keepanno.ast.KeepMethodPattern; |
| import com.android.tools.r8.keepanno.ast.KeepMethodReturnTypePattern; |
| import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern; |
| import com.android.tools.r8.keepanno.ast.ParsingContext; |
| import org.objectweb.asm.Type; |
| |
| public class ContextDescriptor { |
| |
| public static ContextDescriptor parse(String descriptor, ParsingContext parsingContext) { |
| int classDescriptorEnd = descriptor.indexOf(';') + 1; |
| if (classDescriptorEnd <= 0) { |
| throw parsingContext.error("Invalid descriptor: " + descriptor); |
| } |
| String classDescriptor = descriptor.substring(0, classDescriptorEnd); |
| if (classDescriptorEnd == descriptor.length()) { |
| return new ContextDescriptor(classDescriptor); |
| } |
| int memberNameEnd = descriptor.indexOf('(', classDescriptorEnd); |
| if (memberNameEnd < 0) { |
| memberNameEnd = descriptor.indexOf(':', classDescriptorEnd); |
| } |
| if (memberNameEnd < 0) { |
| throw parsingContext.error("Invalid descriptor: " + descriptor); |
| } |
| String memberName = descriptor.substring(classDescriptorEnd, memberNameEnd); |
| String memberDescriptor = descriptor.substring(memberNameEnd); |
| return new ContextDescriptor(classDescriptor, memberName, memberDescriptor); |
| } |
| |
| private final String classDescriptor; |
| private final String memberName; |
| private final String memberDescriptor; |
| |
| private ContextDescriptor(String classDescriptor) { |
| this(classDescriptor, null, null); |
| } |
| |
| private ContextDescriptor(String classDescriptor, String memberName, String memberDescriptor) { |
| this.classDescriptor = classDescriptor; |
| this.memberName = memberName; |
| this.memberDescriptor = memberDescriptor; |
| } |
| |
| public boolean isClassContext() { |
| return memberDescriptor == null; |
| } |
| |
| public boolean isMethodContext() { |
| return memberDescriptor != null && memberDescriptor.charAt(0) == '('; |
| } |
| |
| public boolean isFieldContext() { |
| return memberDescriptor != null && memberDescriptor.charAt(0) == ':'; |
| } |
| |
| public String getClassDescriptor() { |
| return classDescriptor; |
| } |
| |
| public String getMemberName() { |
| return memberName; |
| } |
| |
| public String getMethodDescriptor() { |
| assert isMethodContext(); |
| return memberDescriptor; |
| } |
| |
| public String getFieldType() { |
| assert isFieldContext(); |
| return memberDescriptor.substring(1); |
| } |
| |
| public KeepEdgeMetaInfo.Builder applyToMetadata(KeepEdgeMetaInfo.Builder metaInfoBuilder) { |
| if (isClassContext()) { |
| metaInfoBuilder.setContextFromClassDescriptor(getClassDescriptor()); |
| } else if (isMethodContext()) { |
| metaInfoBuilder.setContextFromMethodDescriptor( |
| getClassDescriptor(), getMemberName(), getMethodDescriptor()); |
| } else { |
| assert isFieldContext(); |
| metaInfoBuilder.setContextFromFieldDescriptor( |
| getClassDescriptor(), getMemberName(), getFieldType()); |
| } |
| return metaInfoBuilder; |
| } |
| |
| public KeepItemPattern toItemPattern() { |
| KeepQualifiedClassNamePattern className = |
| KeepQualifiedClassNamePattern.exactFromDescriptor(getClassDescriptor()); |
| KeepClassItemPattern classItem = |
| KeepClassItemPattern.builder().setClassNamePattern(className).build(); |
| if (isClassContext()) { |
| return classItem; |
| } |
| KeepMemberPattern memberPattern; |
| if (isMethodContext()) { |
| memberPattern = createMethodPatternFromDescriptor(); |
| } else { |
| assert isFieldContext(); |
| memberPattern = createFieldPatternFromDescriptor(); |
| } |
| return KeepMemberItemPattern.builder() |
| .setClassReference(classItem.toClassItemReference()) |
| .setMemberPattern(memberPattern) |
| .build(); |
| } |
| |
| private KeepFieldPattern createFieldPatternFromDescriptor() { |
| return KeepFieldPattern.builder() |
| .setNamePattern(KeepFieldNamePattern.exact(getMemberName())) |
| .setTypePattern(KeepFieldTypePattern.fromType(fromDescriptor(getFieldType()))) |
| .build(); |
| } |
| |
| private KeepMethodPattern createMethodPatternFromDescriptor() { |
| Type methodType = Type.getMethodType(getMethodDescriptor()); |
| |
| String returnTypeDescriptor = methodType.getReturnType().getDescriptor(); |
| KeepMethodReturnTypePattern returnType = |
| returnTypeDescriptor.equals("V") |
| ? KeepMethodReturnTypePattern.voidType() |
| : KeepMethodReturnTypePattern.fromType(fromDescriptor(returnTypeDescriptor)); |
| |
| KeepMethodParametersPattern.Builder paramBuilder = KeepMethodParametersPattern.builder(); |
| for (Type argumentType : methodType.getArgumentTypes()) { |
| paramBuilder.addParameterTypePattern(fromDescriptor(argumentType.getDescriptor())); |
| } |
| |
| return KeepMethodPattern.builder() |
| .setNamePattern(KeepMethodNamePattern.exact(getMemberName())) |
| .setReturnTypePattern(returnType) |
| .setParametersPattern(paramBuilder.build()) |
| .build(); |
| } |
| } |