Fix handling of unnamed inner classes
The InnerClass attributes for anonymous classes have a null class name,
which turbine shouldn't try to read. This fixes a NPE when repackaging
supertypes loaded from implementation jars; turbine itself doesn't write
InnerClass attributes for anonymous classes.
MOE_MIGRATED_REVID=146268069
diff --git a/java/com/google/turbine/bytecode/ClassFile.java b/java/com/google/turbine/bytecode/ClassFile.java
index 6321ec1..2761fec 100644
--- a/java/com/google/turbine/bytecode/ClassFile.java
+++ b/java/com/google/turbine/bytecode/ClassFile.java
@@ -16,6 +16,8 @@
package com.google.turbine.bytecode;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.collect.ImmutableList;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue;
import com.google.turbine.model.Const;
@@ -188,9 +190,9 @@
private final int access;
public InnerClass(String innerClass, String outerClass, String innerName, int access) {
- this.innerClass = innerClass;
- this.outerClass = outerClass;
- this.innerName = innerName;
+ this.innerClass = requireNonNull(innerClass);
+ this.outerClass = requireNonNull(outerClass);
+ this.innerName = requireNonNull(innerName);
this.access = access;
}
diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java
index 45da3a4..b59448a 100644
--- a/java/com/google/turbine/bytecode/ClassReader.java
+++ b/java/com/google/turbine/bytecode/ClassReader.java
@@ -130,7 +130,7 @@
int innerNameIndex = reader.u2();
String innerName = innerNameIndex != 0 ? constantPool.utf8(innerNameIndex) : null;
int innerClassAccessFlags = reader.u2();
- if (thisClass.equals(innerClass) || thisClass.equals(outerClass)) {
+ if (innerName != null && (thisClass.equals(innerClass) || thisClass.equals(outerClass))) {
innerclasses.add(
new ClassFile.InnerClass(innerClass, outerClass, innerName, innerClassAccessFlags));
}
diff --git a/javatests/com/google/turbine/deps/TransitiveTest.java b/javatests/com/google/turbine/deps/TransitiveTest.java
index c6a6eea..d460287 100644
--- a/javatests/com/google/turbine/deps/TransitiveTest.java
+++ b/javatests/com/google/turbine/deps/TransitiveTest.java
@@ -30,6 +30,7 @@
import com.google.turbine.main.Main;
import com.google.turbine.options.TurbineOptions;
import java.io.IOException;
+import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -40,11 +41,14 @@
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
@RunWith(JUnit4.class)
public class TransitiveTest {
@@ -173,4 +177,48 @@
"META-INF/TRANSITIVE/a/A$Anno.class",
"META-INF/TRANSITIVE/a/A$Inner.class");
}
+
+ @Test
+ public void anonymous() throws Exception {
+ Path liba = temporaryFolder.newFolder().toPath().resolve("out.jar");
+ try (OutputStream os = Files.newOutputStream(liba);
+ JarOutputStream jos = new JarOutputStream(os)) {
+ {
+ jos.putNextEntry(new JarEntry("a/A.class"));
+ ClassWriter cw = new ClassWriter(0);
+ cw.visit(52, Opcodes.ACC_SUPER, "a/A", null, "java/lang/Object", null);
+ cw.visitInnerClass("a/A$1", "a/A", null, Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC);
+ cw.visitInnerClass("a/A$I", "a/A", "I", Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC);
+ jos.write(cw.toByteArray());
+ }
+ {
+ jos.putNextEntry(new JarEntry("a/A$1.class"));
+ ClassWriter cw = new ClassWriter(0);
+ cw.visit(52, Opcodes.ACC_SUPER, "a/A$1", null, "java/lang/Object", null);
+ cw.visitInnerClass("a/A$1", "a/A", "I", Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC);
+ jos.write(cw.toByteArray());
+ }
+ {
+ jos.putNextEntry(new JarEntry("a/A$I.class"));
+ ClassWriter cw = new ClassWriter(0);
+ cw.visit(52, Opcodes.ACC_SUPER, "a/A$I", null, "java/lang/Object", null);
+ cw.visitInnerClass("a/A$I", "a/A", "I", Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC);
+ jos.write(cw.toByteArray());
+ }
+ }
+ Path libb =
+ runTurbine(
+ new SourceBuilder()
+ .addSourceLines(
+ "b/B.java", //
+ "package b;",
+ "public class B extends a.A {}")
+ .build(),
+ ImmutableList.of(liba));
+
+ // libb repackages A and any named member types
+ assertThat(readJar(libb).keySet())
+ .containsExactly(
+ "b/B.class", "META-INF/TRANSITIVE/a/A.class", "META-INF/TRANSITIVE/a/A$I.class");
+ }
}