| // 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; |
| } |
| |
| } |