| // Copyright (c) 2018, 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.kotlin; | |
| import com.android.tools.r8.utils.DescriptorUtils; | |
| import com.android.tools.r8.utils.InternalOptions; | |
| import java.io.ByteArrayOutputStream; | |
| import java.io.IOException; | |
| import java.nio.file.Files; | |
| import java.nio.file.Path; | |
| import java.util.List; | |
| import java.util.jar.JarEntry; | |
| import java.util.jar.JarInputStream; | |
| import org.objectweb.asm.ClassReader; | |
| import org.objectweb.asm.ClassVisitor; | |
| import org.objectweb.asm.FieldVisitor; | |
| import org.objectweb.asm.MethodVisitor; | |
| import org.objectweb.asm.Opcodes; | |
| /** | |
| * Utilities to lookup symbols in a classpath using ASM. | |
| */ | |
| public final class AsmUtils { | |
| private AsmUtils() { | |
| } | |
| public static boolean doesClassExist(List<Path> classpath, String className) { | |
| byte[] classData = loadClassBytesFromClasspath(classpath, className); | |
| return classData != null; | |
| } | |
| public static boolean doesMethodExist(List<Path> classpath, String className, | |
| String methodName, | |
| String methodDescriptor) { | |
| MethodFinder classVisitor = new MethodFinder(methodName, methodDescriptor); | |
| visitClass(classpath, className, classVisitor); | |
| return classVisitor.foundMethod; | |
| } | |
| private static final class MethodFinder extends ClassVisitor { | |
| private final String methodName; | |
| private final String methodDescriptor; | |
| public boolean foundMethod = false; | |
| public MethodFinder(String methodName, String methodDescriptor) { | |
| super(InternalOptions.ASM_VERSION); | |
| this.methodName = methodName; | |
| this.methodDescriptor = methodDescriptor; | |
| } | |
| @Override | |
| public MethodVisitor visitMethod(int access, String name, String desc, String signature, | |
| String[] exceptions) { | |
| if (name.equals(methodName) && desc.equals(methodDescriptor)) { | |
| assert !foundMethod; | |
| foundMethod = true; | |
| } | |
| return null; | |
| } | |
| } | |
| public static boolean doesFieldExist(List<Path> classpath, String className, | |
| String fieldName, | |
| String fieldType) { | |
| FieldFinder classVisitor = new FieldFinder(fieldName, fieldType); | |
| visitClass(classpath, className, classVisitor); | |
| return classVisitor.foundField; | |
| } | |
| private static final class FieldFinder extends ClassVisitor { | |
| private final String fieldName; | |
| private final String fieldDescriptor; | |
| public boolean foundField = false; | |
| public FieldFinder(String fieldName, String fieldType) { | |
| super(InternalOptions.ASM_VERSION); | |
| this.fieldName = fieldName; | |
| this.fieldDescriptor = DescriptorUtils.javaTypeToDescriptor(fieldType); | |
| } | |
| @Override | |
| public FieldVisitor visitField(int access, String name, String desc, String signature, | |
| Object value) { | |
| if (name.equals(fieldName) && desc.equals(fieldDescriptor)) { | |
| assert !foundField; | |
| foundField = true; | |
| } | |
| return null; | |
| } | |
| } | |
| private static void visitClass(List<Path> classpath, String className, | |
| ClassVisitor classVisitor) { | |
| byte[] classData = loadClassBytesFromClasspath(classpath, className); | |
| new ClassReader(classData).accept(classVisitor, | |
| ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); | |
| } | |
| private static byte[] loadClassBytesFromClasspath(List<Path> classpath, String className) { | |
| String classFilename = DescriptorUtils.getPathFromJavaType(className); | |
| for (Path path : classpath) { | |
| if (path.toFile().getPath().endsWith(".jar")) { | |
| try (JarInputStream jarInputStream = new JarInputStream(Files.newInputStream(path))) { | |
| JarEntry jarEntry; | |
| while ((jarEntry = jarInputStream.getNextJarEntry()) != null) { | |
| if (jarEntry.isDirectory()) { | |
| continue; | |
| } | |
| String entryName = jarEntry.getName(); | |
| if (entryName.equals(classFilename)) { | |
| byte[] data = new byte[1024]; | |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
| while (true) { | |
| int bytesRead = jarInputStream.read(data, 0, data.length); | |
| if (bytesRead < 0) { | |
| break; | |
| } | |
| baos.write(data, 0, bytesRead); | |
| } | |
| return baos.toByteArray(); | |
| } | |
| } | |
| } catch (IOException e) { | |
| throw new AssertionError(e); | |
| } | |
| } else if (path.toFile().getPath().endsWith(".class")) { | |
| if (path.toFile().getPath().equals(classFilename)) { | |
| try { | |
| return Files.readAllBytes(path); | |
| } catch (IOException e) { | |
| throw new AssertionError(e); | |
| } | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| } |