aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiam Miller-Cushon <cushon@google.com>2023-05-31 15:14:49 -0700
committerJavac Team <javac-team+copybara@google.com>2023-05-31 15:15:34 -0700
commit0902ef6a3fb7204e5780f984d7a9a1b4e220e957 (patch)
treefd041fb5a5ae7caee5fa748d7f2157a8e3817be6
parent02736f5e00b3256691f3a4dc009af0a582724d07 (diff)
downloadturbine-0902ef6a3fb7204e5780f984d7a9a1b4e220e957.tar.gz
Add a debug option to enable emitting private fields
By default, turbine doesn't emit private fields in class files, because they don't affect downstream compilations. This makes it possible to emit private fields by setting a javac debug flag. PiperOrigin-RevId: 536836786
-rw-r--r--java/com/google/turbine/lower/Lower.java78
-rw-r--r--java/com/google/turbine/main/Main.java8
-rw-r--r--javatests/com/google/turbine/deps/DependenciesTest.java5
-rw-r--r--javatests/com/google/turbine/lower/IntegrationTestSupport.java4
-rw-r--r--javatests/com/google/turbine/lower/LowerTest.java92
5 files changed, 150 insertions, 37 deletions
diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java
index b79cf2b..80d8128 100644
--- a/java/com/google/turbine/lower/Lower.java
+++ b/java/com/google/turbine/lower/Lower.java
@@ -21,6 +21,7 @@ import static com.google.turbine.binder.DisambiguateTypeAnnotations.groupRepeate
import static java.lang.Math.max;
import static java.util.Objects.requireNonNull;
+import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -92,30 +93,53 @@ import org.jspecify.nullness.Nullable;
/** Lowering from bound classes to bytecode. */
public class Lower {
- /** The lowered compilation output. */
- public static class Lowered {
- private final ImmutableMap<String, byte[]> bytes;
- private final ImmutableSet<ClassSymbol> symbols;
+ /** Lowering options. */
+ @AutoValue
+ public abstract static class LowerOptions {
+
+ public abstract LanguageVersion languageVersion();
+
+ public abstract boolean emitPrivateFields();
- public Lowered(ImmutableMap<String, byte[]> bytes, ImmutableSet<ClassSymbol> symbols) {
- this.bytes = bytes;
- this.symbols = symbols;
+ public static LowerOptions createDefault() {
+ return builder().build();
}
- /** Returns the bytecode for classes in the compilation. */
- public ImmutableMap<String, byte[]> bytes() {
- return bytes;
+ public static Builder builder() {
+ return new AutoValue_Lower_LowerOptions.Builder()
+ .languageVersion(LanguageVersion.createDefault())
+ .emitPrivateFields(false);
}
+ /** Builder for {@link LowerOptions}. */
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder languageVersion(LanguageVersion languageVersion);
+
+ public abstract Builder emitPrivateFields(boolean emitPrivateFields);
+
+ public abstract LowerOptions build();
+ }
+ }
+
+ /** The lowered compilation output. */
+ @AutoValue
+ public abstract static class Lowered {
+ /** Returns the bytecode for classes in the compilation. */
+ public abstract ImmutableMap<String, byte[]> bytes();
+
/** Returns the set of all referenced symbols in the compilation. */
- public ImmutableSet<ClassSymbol> symbols() {
- return symbols;
+ public abstract ImmutableSet<ClassSymbol> symbols();
+
+ public static Lowered create(
+ ImmutableMap<String, byte[]> bytes, ImmutableSet<ClassSymbol> symbols) {
+ return new AutoValue_Lower_Lowered(bytes, symbols);
}
}
/** Lowers all given classes to bytecode. */
public static Lowered lowerAll(
- LanguageVersion languageVersion,
+ LowerOptions options,
ImmutableMap<ClassSymbol, SourceTypeBoundClass> units,
ImmutableList<SourceModuleInfo> modules,
Env<ClassSymbol, BytecodeBoundClass> classpath) {
@@ -124,9 +148,11 @@ public class Lower {
ImmutableMap.Builder<String, byte[]> result = ImmutableMap.builder();
Set<ClassSymbol> symbols = new LinkedHashSet<>();
// Output Java 8 bytecode at minimum, for type annotations
- int majorVersion = max(languageVersion.majorVersion(), 52);
+ int majorVersion = max(options.languageVersion().majorVersion(), 52);
for (ClassSymbol sym : units.keySet()) {
- result.put(sym.binaryName(), lower(units.get(sym), env, sym, symbols, majorVersion));
+ result.put(
+ sym.binaryName(),
+ lower(units.get(sym), env, sym, symbols, majorVersion, options.emitPrivateFields()));
}
if (modules.size() == 1) {
// single module mode: the module-info.class file is at the root
@@ -140,17 +166,18 @@ public class Lower {
lower(module, env, symbols, majorVersion));
}
}
- return new Lowered(result.buildOrThrow(), ImmutableSet.copyOf(symbols));
+ return Lowered.create(result.buildOrThrow(), ImmutableSet.copyOf(symbols));
}
/** Lowers a class to bytecode. */
- public static byte[] lower(
+ private static byte[] lower(
SourceTypeBoundClass info,
Env<ClassSymbol, TypeBoundClass> env,
ClassSymbol sym,
Set<ClassSymbol> symbols,
- int majorVersion) {
- return new Lower(env).lower(info, sym, symbols, majorVersion);
+ int majorVersion,
+ boolean emitPrivateFields) {
+ return new Lower(env).lower(info, sym, symbols, majorVersion, emitPrivateFields);
}
private static byte[] lower(
@@ -251,7 +278,11 @@ public class Lower {
}
private byte[] lower(
- SourceTypeBoundClass info, ClassSymbol sym, Set<ClassSymbol> symbols, int majorVersion) {
+ SourceTypeBoundClass info,
+ ClassSymbol sym,
+ Set<ClassSymbol> symbols,
+ int majorVersion,
+ boolean emitPrivateFields) {
int access = classAccess(info);
String name = sig.descriptor(sym);
String signature = sig.classSignature(info, env);
@@ -286,8 +317,7 @@ public class Lower {
ImmutableList.Builder<ClassFile.FieldInfo> fields = ImmutableList.builder();
for (FieldInfo f : info.fields()) {
- if ((f.access() & TurbineFlag.ACC_PRIVATE) == TurbineFlag.ACC_PRIVATE) {
- // TODO(cushon): drop private members earlier?
+ if (!emitPrivateFields && (f.access() & TurbineFlag.ACC_PRIVATE) == TurbineFlag.ACC_PRIVATE) {
continue;
}
fields.add(lowerField(f));
@@ -568,7 +598,9 @@ public class Lower {
private final Map<TyVarSymbol, TyVarInfo> tyParams;
- /** @param tyParams the initial lookup scope, e.g. a method's formal type parameters. */
+ /**
+ * @param tyParams the initial lookup scope, e.g. a method's formal type parameters.
+ */
public TyVarEnv(Map<TyVarSymbol, TyVarInfo> tyParams) {
this.tyParams = tyParams;
}
diff --git a/java/com/google/turbine/main/Main.java b/java/com/google/turbine/main/Main.java
index 9db97f4..387b01c 100644
--- a/java/com/google/turbine/main/Main.java
+++ b/java/com/google/turbine/main/Main.java
@@ -195,7 +195,13 @@ public final class Main {
// TODO(cushon): parallelize
Lowered lowered =
Lower.lowerAll(
- options.languageVersion(), bound.units(), bound.modules(), bound.classPathEnv());
+ Lower.LowerOptions.builder()
+ .languageVersion(options.languageVersion())
+ .emitPrivateFields(options.javacOpts().contains("-XDturbine.emitPrivateFields"))
+ .build(),
+ bound.units(),
+ bound.modules(),
+ bound.classPathEnv());
if (options.outputDeps().isPresent()) {
DepsProto.Dependencies deps =
diff --git a/javatests/com/google/turbine/deps/DependenciesTest.java b/javatests/com/google/turbine/deps/DependenciesTest.java
index ba905db..2164a9f 100644
--- a/javatests/com/google/turbine/deps/DependenciesTest.java
+++ b/javatests/com/google/turbine/deps/DependenciesTest.java
@@ -29,7 +29,6 @@ import com.google.turbine.diag.SourceFile;
import com.google.turbine.lower.IntegrationTestSupport;
import com.google.turbine.lower.Lower;
import com.google.turbine.lower.Lower.Lowered;
-import com.google.turbine.options.LanguageVersion;
import com.google.turbine.parse.Parser;
import com.google.turbine.proto.DepsProto;
import com.google.turbine.testing.TestClassPaths;
@@ -105,11 +104,11 @@ public class DependenciesTest {
units.build(),
ClassPathBinder.bindClasspath(classpath),
TestClassPaths.TURBINE_BOOTCLASSPATH,
- /* moduleVersion=*/ Optional.empty());
+ /* moduleVersion= */ Optional.empty());
Lowered lowered =
Lower.lowerAll(
- LanguageVersion.createDefault(),
+ Lower.LowerOptions.createDefault(),
bound.units(),
bound.modules(),
bound.classPathEnv());
diff --git a/javatests/com/google/turbine/lower/IntegrationTestSupport.java b/javatests/com/google/turbine/lower/IntegrationTestSupport.java
index a3bbffc..6527a03 100644
--- a/javatests/com/google/turbine/lower/IntegrationTestSupport.java
+++ b/javatests/com/google/turbine/lower/IntegrationTestSupport.java
@@ -497,7 +497,9 @@ public final class IntegrationTestSupport {
throws IOException {
BindingResult bound = turbineAnalysis(input, classpath, bootClassPath, moduleVersion);
return Lower.lowerAll(
- LanguageVersion.fromJavacopts(javacopts),
+ Lower.LowerOptions.builder()
+ .languageVersion(LanguageVersion.fromJavacopts(javacopts))
+ .build(),
bound.units(),
bound.modules(),
bound.classPathEnv())
diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java
index f04ef27..2de4650 100644
--- a/javatests/com/google/turbine/lower/LowerTest.java
+++ b/javatests/com/google/turbine/lower/LowerTest.java
@@ -233,7 +233,7 @@ public class LowerTest {
Map<String, byte[]> bytes =
Lower.lowerAll(
- LanguageVersion.createDefault(),
+ Lower.LowerOptions.createDefault(),
ImmutableMap.of(
new ClassSymbol("test/Test"), c, new ClassSymbol("test/Test$Inner"), i),
ImmutableList.of(),
@@ -261,10 +261,10 @@ public class LowerTest {
"}"))),
ClassPathBinder.bindClasspath(ImmutableList.of()),
TURBINE_BOOTCLASSPATH,
- /* moduleVersion=*/ Optional.empty());
+ /* moduleVersion= */ Optional.empty());
Map<String, byte[]> lowered =
Lower.lowerAll(
- LanguageVersion.createDefault(),
+ Lower.LowerOptions.createDefault(),
bound.units(),
bound.modules(),
bound.classPathEnv())
@@ -341,10 +341,10 @@ public class LowerTest {
"}"))),
ClassPathBinder.bindClasspath(ImmutableList.of()),
TURBINE_BOOTCLASSPATH,
- /* moduleVersion=*/ Optional.empty());
+ /* moduleVersion= */ Optional.empty());
Map<String, byte[]> lowered =
Lower.lowerAll(
- LanguageVersion.createDefault(),
+ Lower.LowerOptions.createDefault(),
bound.units(),
bound.modules(),
bound.classPathEnv())
@@ -424,10 +424,10 @@ public class LowerTest {
ImmutableList.of(Parser.parse("@Deprecated class Test {}")),
ClassPathBinder.bindClasspath(ImmutableList.of()),
TURBINE_BOOTCLASSPATH,
- /* moduleVersion=*/ Optional.empty());
+ /* moduleVersion= */ Optional.empty());
Map<String, byte[]> lowered =
Lower.lowerAll(
- LanguageVersion.createDefault(),
+ Lower.LowerOptions.createDefault(),
bound.units(),
bound.modules(),
bound.classPathEnv())
@@ -650,10 +650,14 @@ public class LowerTest {
ImmutableList.of(Parser.parse("class Test {}")),
ClassPathBinder.bindClasspath(ImmutableList.of()),
TURBINE_BOOTCLASSPATH,
- /* moduleVersion=*/ Optional.empty());
+ /* moduleVersion= */ Optional.empty());
Map<String, byte[]> lowered =
Lower.lowerAll(
- LanguageVersion.fromJavacopts(ImmutableList.of("-source", "7", "-target", "7")),
+ Lower.LowerOptions.builder()
+ .languageVersion(
+ LanguageVersion.fromJavacopts(
+ ImmutableList.of("-source", "7", "-target", "7")))
+ .build(),
bound.units(),
bound.modules(),
bound.classPathEnv())
@@ -677,6 +681,76 @@ public class LowerTest {
assertThat(major[0]).isEqualTo(Opcodes.V1_8);
}
+ @Test
+ public void privateFields() throws Exception {
+ BindingResult bound =
+ Binder.bind(
+ ImmutableList.of(
+ Parser.parse(
+ "class Test {\n" //
+ + " private int x;\n"
+ + " int y;\n"
+ + "}")),
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ TURBINE_BOOTCLASSPATH,
+ /* moduleVersion= */ Optional.empty());
+ ImmutableMap<String, byte[]> lowered =
+ Lower.lowerAll(
+ Lower.LowerOptions.builder().emitPrivateFields(true).build(),
+ bound.units(),
+ bound.modules(),
+ bound.classPathEnv())
+ .bytes();
+ List<String> fields = new ArrayList<>();
+ new ClassReader(lowered.get("Test"))
+ .accept(
+ new ClassVisitor(Opcodes.ASM9) {
+ @Override
+ public FieldVisitor visitField(
+ int access, String name, String descriptor, String signature, Object value) {
+ fields.add(name);
+ return null;
+ }
+ },
+ 0);
+ assertThat(fields).containsExactly("x", "y");
+ }
+
+ @Test
+ public void noPrivateFields() throws Exception {
+ BindingResult bound =
+ Binder.bind(
+ ImmutableList.of(
+ Parser.parse(
+ "class Test {\n" //
+ + " private int x;\n"
+ + " int y;\n"
+ + "}")),
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ TURBINE_BOOTCLASSPATH,
+ /* moduleVersion= */ Optional.empty());
+ ImmutableMap<String, byte[]> lowered =
+ Lower.lowerAll(
+ Lower.LowerOptions.createDefault(),
+ bound.units(),
+ bound.modules(),
+ bound.classPathEnv())
+ .bytes();
+ List<String> fields = new ArrayList<>();
+ new ClassReader(lowered.get("Test"))
+ .accept(
+ new ClassVisitor(Opcodes.ASM9) {
+ @Override
+ public FieldVisitor visitField(
+ int access, String name, String descriptor, String signature, Object value) {
+ fields.add(name);
+ return null;
+ }
+ },
+ 0);
+ assertThat(fields).containsExactly("y");
+ }
+
static String lines(String... lines) {
return Joiner.on(System.lineSeparator()).join(lines);
}