blob: 0e07bf4299713eeffa57f945b4e17aaefbb1add0 [file] [log] [blame]
// Copyright (c) 2023, 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.desugar;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
import com.google.common.collect.ObjectArrays;
import java.nio.file.Path;
import java.util.function.Consumer;
import org.junit.Assume;
import org.junit.rules.TemporaryFolder;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
public class LibraryFilesHelper implements Opcodes {
public static Path[] getJdk9LibraryFiles(TemporaryFolder temp) throws Exception {
// TODO(b/270105162): We should be able to run this on windows.
Assume.assumeFalse(ToolHelper.isWindows());
// TODO(b/180553597): Add JDK-9 runtime jar instead. As a temporary solution we use the JDK-8
// runtime with additional stubs.
// We use jdk-8 for compilation because in jdk-9 and higher we would need to deal with the
// module patching logic.
Path generatedJar = temp.newFolder().toPath().resolve("stubs.jar");
ZipBuilder builder = ZipBuilder.builder(generatedJar);
addStringConcatFactory(builder);
addVarHandle(builder);
builder.build();
return new Path[] {generatedJar, ToolHelper.getJava8RuntimeJar()};
}
public static Path[] getJdk11LibraryFiles(TemporaryFolder temp) throws Exception {
Assume.assumeFalse(ToolHelper.isWindows());
// TODO(b/180553597): Add JDK-11 runtime jar instead. As a temporary solution we use the JDK-8
// runtime with additional stubs.
return getJdk9LibraryFiles(temp);
}
public static Path[] getJdk15LibraryFiles(TemporaryFolder temp) throws Exception {
// TODO(b/270105162): We should be able to run this on windows.
Assume.assumeFalse(ToolHelper.isWindows());
// TODO(b/169645628): Add JDK-15 runtime jar instead. As a temporary solution we use the jdk 8
// runtime with additional stubs.
Path generatedJar = temp.newFolder().toPath().resolve("stubs.jar");
ZipBuilder builder = ZipBuilder.builder(generatedJar);
addRecord(builder);
addRecordComponent(builder);
addObjectsMethod(builder);
addTypeDescriptor(builder);
builder.build();
return ObjectArrays.concat(getJdk9LibraryFiles(temp), new Path[] {generatedJar}, Path.class);
}
// Generates a class file for:
// <pre>
// public class ObjectMethods {}
// </pre>
private static void addObjectsMethod(ZipBuilder builder) throws Exception {
addClassToZipBuilder(
builder, ACC_PUBLIC, "java/lang/runtime/ObjectMethods", ConsumerUtils.emptyConsumer());
}
// Generates a class file for:
// <pre>
// public interface TypeDescriptor {
// String descriptorString();
// }
// </pre>
private static void addTypeDescriptor(ZipBuilder builder) throws Exception {
addClassToZipBuilder(
builder,
ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT,
"java/lang/invoke/TypeDescriptor",
methodAdder ->
methodAdder.add(
ACC_PUBLIC | ACC_ABSTRACT,
"descriptorString",
methodDescriptor(false, String.class)));
}
// Generates a class file for:
// <pre>
// public class StringConcatFactory {}
// </pre>
private static void addStringConcatFactory(ZipBuilder builder) throws Exception {
addClassToZipBuilder(
builder, ACC_PUBLIC, "java/lang/invoke/StringConcatFactory", ConsumerUtils.emptyConsumer());
}
// Generates a class file for:
// <pre>
// public class VarHandle {}
// </pre>
private static void addVarHandle(ZipBuilder builder) throws Exception {
addClassToZipBuilder(
builder, ACC_PUBLIC, "java/lang/invoke/VarHandle", ConsumerUtils.emptyConsumer());
}
// Generates a class file for:
// <pre>
// public abstract class Record {
// protected Record() {}
//
// public abstract boolean equals(Object obj);
//
// public abstract int hashCode();
//
// public abstract String toString();
// }
// </pre>
private static void addRecord(ZipBuilder builder) throws Exception {
addClassToZipBuilder(
builder,
ACC_PUBLIC | ACC_ABSTRACT,
"java/lang/Record",
methodAdder -> {
methodAdder.add(ACC_PROTECTED, "<init>", "()V");
methodAdder.add(
ACC_PUBLIC | ACC_ABSTRACT,
"equals",
methodDescriptor(false, Object.class, boolean.class));
methodAdder.add(
ACC_PUBLIC | ACC_ABSTRACT, "hashCode", methodDescriptor(false, int.class));
methodAdder.add(
ACC_PUBLIC | ACC_ABSTRACT, "toString", methodDescriptor(false, String.class));
});
}
// Generates a class file for:
// <pre>
// public class RecordComponent {}
// </pre>
private static void addRecordComponent(ZipBuilder builder) throws Exception {
addClassToZipBuilder(
builder, ACC_PUBLIC, "java/lang/reflect/RecordComponent", ConsumerUtils.emptyConsumer());
}
// Generates a class file for:
// <pre>
// public interface Supplier {
// Object get();
// }
// </pre>
public static byte[] getSupplier() {
return createClass(
ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT,
"java/util/function/Supplier",
methodAdder ->
methodAdder.add(
ACC_PUBLIC | ACC_ABSTRACT, "get", methodDescriptor(false, Object.class)));
}
private static void addClassToZipBuilder(
ZipBuilder builder, int access, String binaryName, Consumer<MethodAdder> consumer)
throws Exception {
builder.addBytes(binaryName + ".class", createClass(access, binaryName, consumer));
}
@FunctionalInterface
public interface MethodAdder {
void add(int access, String name, String descriptor);
}
public static String descriptor(Class<?> clazz) {
return DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName());
}
public static String methodDescriptor(boolean isVoid, Class<?>... clazz) {
StringBuilder sb = new StringBuilder();
sb.append("(");
for (int i = 0; i < clazz.length - (isVoid ? 0 : 1); i++) {
sb.append(descriptor(clazz[i]));
}
sb.append(")");
if (!isVoid) {
sb.append(descriptor(clazz[clazz.length - 1]));
}
return sb.toString();
}
public static byte[] createClass(int access, String binaryName, Consumer<MethodAdder> consumer) {
ClassWriter cw = new ClassWriter(0);
cw.visit(V1_8, access, binaryName, null, "java/lang/Object", null);
consumer.accept(
(access1, name, descriptor1) -> cw.visitMethod(access1, name, descriptor1, null, null));
cw.visitEnd();
return cw.toByteArray();
}
}