aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYifei Zhang <yfz@google.com>2023-07-25 06:22:44 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-07-25 06:22:44 +0000
commit820217dbd28c436832f767eac170689c9f3caee0 (patch)
tree405bdc36825a61db2a15f10b8775c78010ee0b56
parentd4be1f10b831f4c3774091c74e4b904ec6450c9a (diff)
parent5ff202242e75c1618ee1242e69f39741941c0949 (diff)
downloadturbine-820217dbd28c436832f767eac170689c9f3caee0.tar.gz
Upgrade turbine to 7c64f0447a967d4717adb7b1b40d8bb856f34186 am: 6f7cfa29c3 am: 278edfeffb am: 5ff202242e
Original change: https://android-review.googlesource.com/c/platform/external/turbine/+/2674341 Change-Id: I6be35c08194a231d004d7c64fb4ec11f8da5a1b1 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.github/dependabot.yml6
-rw-r--r--.github/workflows/ci.yml3
-rw-r--r--METADATA12
-rw-r--r--java/com/google/turbine/binder/ClassPathBinder.java2
-rw-r--r--java/com/google/turbine/binder/CompUnitPreprocessor.java16
-rw-r--r--java/com/google/turbine/binder/HierarchyBinder.java11
-rw-r--r--java/com/google/turbine/binder/Processing.java2
-rw-r--r--java/com/google/turbine/binder/TypeBinder.java318
-rw-r--r--java/com/google/turbine/bytecode/ClassReader.java5
-rw-r--r--java/com/google/turbine/diag/TurbineError.java2
-rw-r--r--java/com/google/turbine/lower/Lower.java80
-rw-r--r--java/com/google/turbine/main/Main.java44
-rw-r--r--java/com/google/turbine/options/TurbineOptions.java10
-rw-r--r--java/com/google/turbine/parse/StreamLexer.java8
-rw-r--r--java/com/google/turbine/processing/TurbineFiler.java2
-rw-r--r--java/com/google/turbine/processing/TurbineTypes.java2
-rw-r--r--java/com/google/turbine/types/Erasure.java2
-rw-r--r--javatests/com/google/turbine/binder/BinderErrorTest.java53
-rw-r--r--javatests/com/google/turbine/binder/BinderTest.java2
-rw-r--r--javatests/com/google/turbine/bytecode/ClassReaderTest.java20
-rw-r--r--javatests/com/google/turbine/bytecode/sig/SigIntegrationTest.java2
-rw-r--r--javatests/com/google/turbine/deps/DependenciesTest.java5
-rw-r--r--javatests/com/google/turbine/lower/IntegrationTestSupport.java5
-rw-r--r--javatests/com/google/turbine/lower/LongStringIntegrationTest.java2
-rw-r--r--javatests/com/google/turbine/lower/LowerIntegrationTest.java18
-rw-r--r--javatests/com/google/turbine/lower/LowerTest.java95
-rw-r--r--javatests/com/google/turbine/lower/testdata/package_info.test2
-rw-r--r--javatests/com/google/turbine/lower/testdata/record_ctor.test52
-rw-r--r--javatests/com/google/turbine/lower/testdata/record_tostring.test35
-rw-r--r--javatests/com/google/turbine/main/MainTest.java4
-rw-r--r--javatests/com/google/turbine/parse/LexerTest.java7
-rw-r--r--javatests/com/google/turbine/parse/ParserIntegrationTest.java15
-rw-r--r--javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java10
-rw-r--r--javatests/com/google/turbine/processing/TurbineFilerTest.java2
-rw-r--r--pom.xml2
35 files changed, 635 insertions, 221 deletions
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index daec318..0000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-version: 2
-updates:
- - package-ecosystem: "maven"
- directory: "/"
- schedule:
- interval: "daily"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 54db52c..0bcdf49 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -43,6 +43,9 @@ jobs:
- os: ubuntu-latest
java: 20-ea
experimental: true
+ - os: ubuntu-latest
+ java: 21-ea
+ experimental: true
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
steps:
diff --git a/METADATA b/METADATA
index ad28c65..841d8e6 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update turbine
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
+
name: "turbine"
description: "Turbine is a header compiler for Java."
third_party {
@@ -9,11 +13,11 @@ third_party {
type: GIT
value: "https://github.com/google/turbine"
}
- version: "f42d03f5b18a61a3cdaf2f903e54618771c8797a"
+ version: "7c64f0447a967d4717adb7b1b40d8bb856f34186"
license_type: NOTICE
last_upgrade_date {
- year: 2022
- month: 6
- day: 1
+ year: 2023
+ month: 7
+ day: 24
}
}
diff --git a/java/com/google/turbine/binder/ClassPathBinder.java b/java/com/google/turbine/binder/ClassPathBinder.java
index 1c41e96..57f30cf 100644
--- a/java/com/google/turbine/binder/ClassPathBinder.java
+++ b/java/com/google/turbine/binder/ClassPathBinder.java
@@ -16,7 +16,6 @@
package com.google.turbine.binder;
-import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
@@ -36,6 +35,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.function.Function;
import org.jspecify.nullness.Nullable;
/** Sets up an environment for symbols on the classpath. */
diff --git a/java/com/google/turbine/binder/CompUnitPreprocessor.java b/java/com/google/turbine/binder/CompUnitPreprocessor.java
index 970dc4b..98be898 100644
--- a/java/com/google/turbine/binder/CompUnitPreprocessor.java
+++ b/java/com/google/turbine/binder/CompUnitPreprocessor.java
@@ -36,6 +36,8 @@ import com.google.turbine.tree.Tree.ModDecl;
import com.google.turbine.tree.Tree.PkgDecl;
import com.google.turbine.tree.Tree.TyDecl;
import com.google.turbine.tree.TurbineModifier;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
@@ -105,7 +107,7 @@ public final class CompUnitPreprocessor {
// "While the file could technically contain the source code
// for one or more package-private (default-access) classes,
// it would be very bad form." -- JLS 7.4.1
- if (!unit.pkg().get().annos().isEmpty()) {
+ if (isPackageInfo(unit)) {
decls = Iterables.concat(decls, ImmutableList.of(packageInfoTree(unit.pkg().get())));
}
} else {
@@ -124,6 +126,18 @@ public final class CompUnitPreprocessor {
unit.imports(), types.build(), unit.mod(), unit.source(), packageName);
}
+ private static boolean isPackageInfo(CompUnit unit) {
+ String path = unit.source().path();
+ if (path == null) {
+ return false;
+ }
+ Path fileName = Paths.get(path).getFileName();
+ if (fileName == null) {
+ return false;
+ }
+ return fileName.toString().equals("package-info.java");
+ }
+
private static ImmutableMap<String, ClassSymbol> preprocessChildren(
SourceFile source,
ImmutableList.Builder<SourceBoundClass> types,
diff --git a/java/com/google/turbine/binder/HierarchyBinder.java b/java/com/google/turbine/binder/HierarchyBinder.java
index ac2c840..3117d4e 100644
--- a/java/com/google/turbine/binder/HierarchyBinder.java
+++ b/java/com/google/turbine/binder/HierarchyBinder.java
@@ -34,6 +34,7 @@ import com.google.turbine.model.TurbineTyKind;
import com.google.turbine.tree.Tree;
import com.google.turbine.tree.Tree.ClassTy;
import java.util.ArrayDeque;
+import java.util.LinkedHashMap;
import org.jspecify.nullness.Nullable;
/** Type hierarchy binding. */
@@ -109,13 +110,17 @@ public class HierarchyBinder {
}
}
- ImmutableMap.Builder<String, TyVarSymbol> typeParameters = ImmutableMap.builder();
+ LinkedHashMap<String, TyVarSymbol> typeParameters = new LinkedHashMap<>();
for (Tree.TyParam p : decl.typarams()) {
- typeParameters.put(p.name().value(), new TyVarSymbol(origin, p.name().value()));
+ TyVarSymbol existing =
+ typeParameters.putIfAbsent(p.name().value(), new TyVarSymbol(origin, p.name().value()));
+ if (existing != null) {
+ log.error(p.position(), ErrorKind.DUPLICATE_DECLARATION, p.name());
+ }
}
return new SourceHeaderBoundClass(
- base, superclass, interfaces.build(), typeParameters.buildOrThrow());
+ base, superclass, interfaces.build(), ImmutableMap.copyOf(typeParameters));
}
/**
diff --git a/java/com/google/turbine/binder/Processing.java b/java/com/google/turbine/binder/Processing.java
index 616bf2c..83ee905 100644
--- a/java/com/google/turbine/binder/Processing.java
+++ b/java/com/google/turbine/binder/Processing.java
@@ -19,7 +19,6 @@ package com.google.turbine.binder;
import static java.util.Objects.requireNonNull;
import com.google.auto.value.AutoValue;
-import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Stopwatch;
import com.google.common.base.Supplier;
@@ -66,6 +65,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.function.Function;
import java.util.regex.Pattern;
import javax.annotation.processing.Processor;
import javax.lang.model.SourceVersion;
diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java
index 92d2827..ec579e7 100644
--- a/java/com/google/turbine/binder/TypeBinder.java
+++ b/java/com/google/turbine/binder/TypeBinder.java
@@ -17,6 +17,7 @@
package com.google.turbine.binder;
import static com.google.common.collect.Iterables.getLast;
+import static com.google.common.collect.Iterables.getOnlyElement;
import static java.util.Objects.requireNonNull;
import com.google.common.base.Joiner;
@@ -63,6 +64,7 @@ import com.google.turbine.types.Deannotate;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -204,7 +206,8 @@ public class TypeBinder {
break;
case CLASS:
if (base.decl().xtnds().isPresent()) {
- superClassType = bindClassTy(bindingScope, base.decl().xtnds().get());
+ superClassType =
+ checkClassType(bindingScope, base.decl().xtnds().get(), /* expectInterface= */ false);
} else if (owner.equals(ClassSymbol.OBJECT)) {
// java.lang.Object doesn't have a superclass
superClassType = null;
@@ -226,7 +229,7 @@ public class TypeBinder {
}
for (Tree.ClassTy i : base.decl().impls()) {
- interfaceTypes.add(bindClassTy(bindingScope, i));
+ interfaceTypes.add(checkClassType(bindingScope, i, /* expectInterface= */ true));
}
ImmutableList.Builder<ClassSymbol> permits = ImmutableList.builder();
@@ -248,12 +251,16 @@ public class TypeBinder {
ImmutableList<RecordComponentInfo> components = bindComponents(scope, base.decl().components());
- ImmutableList.Builder<MethodInfo> methods =
- ImmutableList.<MethodInfo>builder()
- .addAll(syntheticMethods(syntheticMethods, components))
- .addAll(bindMethods(scope, base.decl().members(), components));
+ List<MethodInfo> boundMethods = bindMethods(scope, base.decl().members(), components);
+ ImmutableList<MethodInfo> methods;
if (base.kind().equals(TurbineTyKind.RECORD)) {
- methods.addAll(syntheticRecordMethods(syntheticMethods, components));
+ methods = recordMethods(syntheticMethods, components, boundMethods);
+ } else {
+ methods =
+ ImmutableList.<MethodInfo>builder()
+ .addAll(syntheticMethods(syntheticMethods))
+ .addAll(boundMethods)
+ .build();
}
ImmutableList<FieldInfo> fields = bindFields(scope, base.decl().members());
@@ -265,7 +272,7 @@ public class TypeBinder {
typeParameterTypes,
base.access(),
components,
- methods.build(),
+ methods,
fields,
base.owner(),
base.kind(),
@@ -280,6 +287,193 @@ public class TypeBinder {
base.decl());
}
+ private ImmutableList<MethodInfo> recordMethods(
+ SyntheticMethods syntheticMethods,
+ ImmutableList<RecordComponentInfo> components,
+ List<MethodInfo> boundMethods) {
+ List<MethodInfo> boundConstructors = new ArrayList<>();
+ List<MethodInfo> boundNonConstructors = new ArrayList<>();
+ boolean hasToString = false;
+ boolean hasEquals = false;
+ boolean hasHashCode = false;
+ boolean hasPrimaryConstructor = false;
+ for (MethodInfo m : boundMethods) {
+ if (m.name().equals("<init>")) {
+ if (isPrimaryConstructor(m, components)) {
+ hasPrimaryConstructor = true;
+ }
+ boundConstructors.add(m);
+ } else {
+ switch (m.name()) {
+ case "toString":
+ hasToString = m.parameters().isEmpty();
+ break;
+ case "equals":
+ hasEquals =
+ m.parameters().size() == 1
+ && hasSameErasure(getOnlyElement(m.parameters()).type(), Type.ClassTy.OBJECT);
+ break;
+ case "hashCode":
+ hasHashCode = m.parameters().isEmpty();
+ break;
+ default: // fall out
+ }
+ boundNonConstructors.add(m);
+ }
+ }
+ ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder();
+ methods.addAll(boundConstructors);
+ if (!hasPrimaryConstructor) {
+ methods.add(defaultRecordConstructor(syntheticMethods, components));
+ }
+ methods.addAll(boundNonConstructors);
+ if (!hasToString) {
+ MethodSymbol toStringMethod = syntheticMethods.create(owner, "toString");
+ methods.add(
+ new MethodInfo(
+ toStringMethod,
+ ImmutableMap.of(),
+ Type.ClassTy.STRING,
+ ImmutableList.of(),
+ ImmutableList.of(),
+ TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL,
+ null,
+ null,
+ ImmutableList.of(),
+ null));
+ }
+ if (!hasHashCode) {
+ MethodSymbol hashCodeMethod = syntheticMethods.create(owner, "hashCode");
+ methods.add(
+ new MethodInfo(
+ hashCodeMethod,
+ ImmutableMap.of(),
+ Type.PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()),
+ ImmutableList.of(),
+ ImmutableList.of(),
+ TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL,
+ null,
+ null,
+ ImmutableList.of(),
+ null));
+ }
+ if (!hasEquals) {
+ MethodSymbol equalsMethod = syntheticMethods.create(owner, "equals");
+ methods.add(
+ new MethodInfo(
+ equalsMethod,
+ ImmutableMap.of(),
+ Type.PrimTy.create(TurbineConstantTypeKind.BOOLEAN, ImmutableList.of()),
+ ImmutableList.of(
+ new ParamInfo(
+ new ParamSymbol(equalsMethod, "other"),
+ Type.ClassTy.OBJECT,
+ ImmutableList.of(),
+ TurbineFlag.ACC_MANDATED)),
+ ImmutableList.of(),
+ TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL,
+ null,
+ null,
+ ImmutableList.of(),
+ null));
+ }
+ for (RecordComponentInfo c : components) {
+ MethodSymbol componentMethod = syntheticMethods.create(owner, c.name());
+ methods.add(
+ new MethodInfo(
+ componentMethod,
+ ImmutableMap.of(),
+ c.type(),
+ ImmutableList.of(),
+ ImmutableList.of(),
+ TurbineFlag.ACC_PUBLIC,
+ null,
+ null,
+ c.annotations(),
+ null));
+ }
+ return methods.build();
+ }
+
+ private MethodInfo defaultRecordConstructor(
+ SyntheticMethods syntheticMethods, ImmutableList<RecordComponentInfo> components) {
+ MethodSymbol symbol = syntheticMethods.create(owner, "<init>");
+ ImmutableList.Builder<ParamInfo> params = ImmutableList.builder();
+ for (RecordComponentInfo component : components) {
+ params.add(
+ new ParamInfo(
+ new ParamSymbol(symbol, component.name()),
+ component.type(),
+ component.annotations(),
+ component.access()));
+ }
+ return syntheticConstructor(
+ symbol, params.build(), TurbineVisibility.fromAccess(base.access()));
+ }
+
+ private boolean isPrimaryConstructor(
+ MethodInfo m, ImmutableList<RecordComponentInfo> components) {
+ if (m.parameters().size() != components.size()) {
+ return false;
+ }
+ for (int i = 0; i < m.parameters().size(); i++) {
+ if (!hasSameErasure(m.parameters().get(i).type(), components.get(i).type())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean hasSameErasure(Type a, Type b) {
+ switch (a.tyKind()) {
+ case PRIM_TY:
+ return b.tyKind() == Type.TyKind.PRIM_TY
+ && ((Type.PrimTy) a).primkind() == ((Type.PrimTy) b).primkind();
+ case CLASS_TY:
+ return b.tyKind() == Type.TyKind.CLASS_TY
+ && ((Type.ClassTy) a).sym().equals(((Type.ClassTy) b).sym());
+ case ARRAY_TY:
+ return b.tyKind() == Type.TyKind.ARRAY_TY
+ && hasSameErasure(((Type.ArrayTy) a).elementType(), ((Type.ArrayTy) b).elementType());
+ case TY_VAR:
+ return b.tyKind() == Type.TyKind.TY_VAR
+ && ((Type.TyVar) a).sym().equals(((Type.TyVar) b).sym());
+ case ERROR_TY:
+ return false;
+ case WILD_TY:
+ case INTERSECTION_TY:
+ case METHOD_TY:
+ case NONE_TY:
+ case VOID_TY:
+ // fall out: impossible method parameter types
+ }
+ throw new AssertionError(a.tyKind());
+ }
+
+ private Type checkClassType(CompoundScope scope, ClassTy tree, boolean expectInterface) {
+ Type type = bindClassTy(scope, tree);
+ if (type.tyKind().equals(Type.TyKind.ERROR_TY)) {
+ return type;
+ }
+ HeaderBoundClass info = env.getNonNull(((Type.ClassTy) type).sym());
+ boolean isInterface;
+ switch (info.kind()) {
+ case INTERFACE:
+ case ANNOTATION:
+ isInterface = true;
+ break;
+ default:
+ isInterface = false;
+ break;
+ }
+ if (expectInterface != isInterface) {
+ log.error(
+ tree.position(),
+ expectInterface ? ErrorKind.EXPECTED_INTERFACE : ErrorKind.UNEXPECTED_INTERFACE);
+ }
+ return type;
+ }
+
/**
* A generated for synthetic {@link MethodSymbol}s.
*
@@ -315,13 +509,10 @@ public class TypeBinder {
}
/** Collect synthetic and implicit methods, including default constructors and enum methods. */
- ImmutableList<MethodInfo> syntheticMethods(
- SyntheticMethods syntheticMethods, ImmutableList<RecordComponentInfo> components) {
+ ImmutableList<MethodInfo> syntheticMethods(SyntheticMethods syntheticMethods) {
switch (base.kind()) {
case CLASS:
return maybeDefaultConstructor(syntheticMethods);
- case RECORD:
- return maybeDefaultRecordConstructor(syntheticMethods, components);
case ENUM:
return syntheticEnumMethods(syntheticMethods);
default:
@@ -329,25 +520,6 @@ public class TypeBinder {
}
}
- private ImmutableList<MethodInfo> maybeDefaultRecordConstructor(
- SyntheticMethods syntheticMethods, ImmutableList<RecordComponentInfo> components) {
- if (hasConstructor()) {
- return ImmutableList.of();
- }
- MethodSymbol symbol = syntheticMethods.create(owner, "<init>");
- ImmutableList.Builder<ParamInfo> params = ImmutableList.builder();
- for (RecordComponentInfo component : components) {
- params.add(
- new ParamInfo(
- new ParamSymbol(symbol, component.name()),
- component.type(),
- component.annotations(),
- component.access()));
- }
- return ImmutableList.of(
- syntheticConstructor(symbol, params.build(), TurbineVisibility.fromAccess(base.access())));
- }
-
private ImmutableList<MethodInfo> maybeDefaultConstructor(SyntheticMethods syntheticMethods) {
if (hasConstructor()) {
return ImmutableList.of();
@@ -466,71 +638,6 @@ public class TypeBinder {
return methods.build();
}
- private ImmutableList<MethodInfo> syntheticRecordMethods(
- SyntheticMethods syntheticMethods, ImmutableList<RecordComponentInfo> components) {
- ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder();
- MethodSymbol toStringMethod = syntheticMethods.create(owner, "toString");
- methods.add(
- new MethodInfo(
- toStringMethod,
- ImmutableMap.of(),
- Type.ClassTy.STRING,
- ImmutableList.of(),
- ImmutableList.of(),
- TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL,
- null,
- null,
- ImmutableList.of(),
- null));
- MethodSymbol hashCodeMethod = syntheticMethods.create(owner, "hashCode");
- methods.add(
- new MethodInfo(
- hashCodeMethod,
- ImmutableMap.of(),
- Type.PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()),
- ImmutableList.of(),
- ImmutableList.of(),
- TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL,
- null,
- null,
- ImmutableList.of(),
- null));
- MethodSymbol equalsMethod = syntheticMethods.create(owner, "equals");
- methods.add(
- new MethodInfo(
- equalsMethod,
- ImmutableMap.of(),
- Type.PrimTy.create(TurbineConstantTypeKind.BOOLEAN, ImmutableList.of()),
- ImmutableList.of(
- new ParamInfo(
- new ParamSymbol(equalsMethod, "other"),
- Type.ClassTy.OBJECT,
- ImmutableList.of(),
- TurbineFlag.ACC_MANDATED)),
- ImmutableList.of(),
- TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL,
- null,
- null,
- ImmutableList.of(),
- null));
- for (RecordComponentInfo c : components) {
- MethodSymbol componentMethod = syntheticMethods.create(owner, c.name());
- methods.add(
- new MethodInfo(
- componentMethod,
- ImmutableMap.of(),
- c.type(),
- ImmutableList.of(),
- ImmutableList.of(),
- TurbineFlag.ACC_PUBLIC,
- null,
- null,
- c.annotations(),
- null));
- }
- return methods.build();
- }
-
private boolean hasConstructor() {
for (Tree m : base.decl().members()) {
if (m.kind() != Kind.METH_DECL) {
@@ -546,7 +653,7 @@ public class TypeBinder {
/** Bind type parameter types. */
private ImmutableMap<TyVarSymbol, TyVarInfo> bindTyParams(
ImmutableList<Tree.TyParam> trees, CompoundScope scope, Map<String, TyVarSymbol> symbols) {
- ImmutableMap.Builder<TyVarSymbol, TyVarInfo> result = ImmutableMap.builder();
+ LinkedHashMap<TyVarSymbol, TyVarInfo> result = new LinkedHashMap<>();
for (Tree.TyParam tree : trees) {
// `symbols` is constructed to guarantee the requireNonNull call is safe.
TyVarSymbol sym = requireNonNull(symbols.get(tree.name().value()));
@@ -555,12 +662,16 @@ public class TypeBinder {
bounds.add(bindTy(scope, bound));
}
ImmutableList<AnnoInfo> annotations = bindAnnotations(scope, tree.annos());
- result.put(
- sym,
- new TyVarInfo(
- IntersectionTy.create(bounds.build()), /* lowerBound= */ null, annotations));
+ TyVarInfo existing =
+ result.putIfAbsent(
+ sym,
+ new TyVarInfo(
+ IntersectionTy.create(bounds.build()), /* lowerBound= */ null, annotations));
+ if (existing != null) {
+ log.error(tree.position(), ErrorKind.DUPLICATE_DECLARATION, tree.name());
+ }
}
- return result.buildOrThrow();
+ return ImmutableMap.copyOf(result);
}
private List<MethodInfo> bindMethods(
@@ -588,7 +699,8 @@ public class TypeBinder {
for (Tree.TyParam pt : t.typarams()) {
builder.put(pt.name().value(), new TyVarSymbol(sym, pt.name().value()));
}
- typeParameters = builder.buildOrThrow();
+ // errors for duplicates are reported in bindTyParams
+ typeParameters = builder.buildKeepingLast();
}
// type parameters can refer to each other in f-bounds, so update the scope first
diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java
index da35196..e73bc49 100644
--- a/java/com/google/turbine/bytecode/ClassReader.java
+++ b/java/com/google/turbine/bytecode/ClassReader.java
@@ -226,9 +226,10 @@ public class ClassReader {
int unusedLength = reader.u4();
int numParameters = reader.u1();
for (int i = 0; i < numParameters; i++) {
- String name = constantPool.utf8(reader.u2());
+ int nameIndex = reader.u2();
+ String name = nameIndex == 0 ? null : constantPool.utf8(nameIndex);
int access = reader.u2();
- if ((access & (TurbineFlag.ACC_SYNTHETIC | TurbineFlag.ACC_MANDATED)) != 0) {
+ if (name == null || (access & (TurbineFlag.ACC_SYNTHETIC | TurbineFlag.ACC_MANDATED)) != 0) {
// ExecutableElement#getParameters doesn't expect synthetic or mandated
// parameters
continue;
diff --git a/java/com/google/turbine/diag/TurbineError.java b/java/com/google/turbine/diag/TurbineError.java
index f839345..8031fa5 100644
--- a/java/com/google/turbine/diag/TurbineError.java
+++ b/java/com/google/turbine/diag/TurbineError.java
@@ -57,6 +57,8 @@ public class TurbineError extends Error {
BAD_MODULE_INFO("unexpected declaration found in module-info"),
UNCLOSED_COMMENT("unclosed comment"),
UNEXPECTED_TYPE("unexpected type %s"),
+ EXPECTED_INTERFACE("expected interface type"),
+ UNEXPECTED_INTERFACE("unexpected interface type"),
UNEXPECTED_MODIFIER("unexpected modifier: %s"),
PROC("%s");
diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java
index 362316d..80d8128 100644
--- a/java/com/google/turbine/lower/Lower.java
+++ b/java/com/google/turbine/lower/Lower.java
@@ -21,7 +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.common.base.Function;
+import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -87,35 +87,59 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Function;
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 34984a8..c246a7a 100644
--- a/java/com/google/turbine/main/Main.java
+++ b/java/com/google/turbine/main/Main.java
@@ -18,6 +18,7 @@ package com.google.turbine.main;
import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
@@ -195,13 +196,23 @@ 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 =
Dependencies.collectDeps(options.targetLabel(), bootclasspath, bound, lowered);
Path path = Paths.get(options.outputDeps().get());
- Files.createDirectories(path.getParent());
+ /*
+ * TODO: cpovirk - Consider checking outputDeps for validity earlier so that anyone who
+ * `--output_deps=/` or similar will get a proper error instead of NPE.
+ */
+ Files.createDirectories(requireNonNull(path.getParent()));
try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(path))) {
deps.writeTo(os);
}
@@ -267,7 +278,7 @@ public final class Main {
/* processorPath= */ options.processorPath(),
/* builtinProcessors= */ options.builtinProcessors())),
bootclasspath,
- /* moduleVersion=*/ Optional.empty());
+ /* moduleVersion= */ Optional.empty());
}
private static void usage(TurbineOptions options) {
@@ -314,17 +325,24 @@ public final class Main {
/** Parse all source files and source jars. */
// TODO(cushon): parallelize
private static ImmutableList<CompUnit> parseAll(TurbineOptions options) throws IOException {
+ return parseAll(options.sources(), options.sourceJars());
+ }
+
+ static ImmutableList<CompUnit> parseAll(Iterable<String> sources, Iterable<String> sourceJars)
+ throws IOException {
ImmutableList.Builder<CompUnit> units = ImmutableList.builder();
- for (String source : options.sources()) {
+ for (String source : sources) {
Path path = Paths.get(source);
units.add(Parser.parse(new SourceFile(source, MoreFiles.asCharSource(path, UTF_8).read())));
}
- for (String sourceJar : options.sourceJars()) {
- for (Zip.Entry ze : new Zip.ZipIterable(Paths.get(sourceJar))) {
- if (ze.name().endsWith(".java")) {
- String name = ze.name();
- String source = new String(ze.data(), UTF_8);
- units.add(Parser.parse(new SourceFile(name, source)));
+ for (String sourceJar : sourceJars) {
+ try (Zip.ZipIterable iterable = new Zip.ZipIterable(Paths.get(sourceJar))) {
+ for (Zip.Entry ze : iterable) {
+ if (ze.name().endsWith(".java")) {
+ String name = ze.name();
+ String source = new String(ze.data(), UTF_8);
+ units.add(Parser.parse(new SourceFile(name, source)));
+ }
}
}
}
@@ -342,7 +360,8 @@ public final class Main {
if (Files.isDirectory(path)) {
for (SourceFile source : generatedSources.values()) {
Path to = path.resolve(source.path());
- Files.createDirectories(to.getParent());
+ // TODO: cpovirk - Consider checking gensrcOutput, similar to outputDeps.
+ Files.createDirectories(requireNonNull(to.getParent()));
Files.writeString(to, source.source());
}
return;
@@ -367,7 +386,8 @@ public final class Main {
if (Files.isDirectory(path)) {
for (Map.Entry<String, byte[]> resource : generatedResources.entrySet()) {
Path to = path.resolve(resource.getKey());
- Files.createDirectories(to.getParent());
+ // TODO: cpovirk - Consider checking resourceOutput, similar to outputDeps.
+ Files.createDirectories(requireNonNull(to.getParent()));
Files.write(to, resource.getValue());
}
return;
diff --git a/java/com/google/turbine/options/TurbineOptions.java b/java/com/google/turbine/options/TurbineOptions.java
index 5cd9a61..007ab23 100644
--- a/java/com/google/turbine/options/TurbineOptions.java
+++ b/java/com/google/turbine/options/TurbineOptions.java
@@ -19,8 +19,8 @@ package com.google.turbine.options;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.Optional;
-import org.jspecify.nullness.Nullable;
/** Header compilation options. */
@AutoValue
@@ -182,6 +182,7 @@ public abstract class TurbineOptions {
abstract ImmutableList.Builder<String> javacOptsBuilder();
+ @CanIgnoreReturnValue
public Builder addAllJavacOpts(Iterable<String> javacOpts) {
javacOptsBuilder().addAll(javacOpts);
return this;
@@ -203,11 +204,4 @@ public abstract class TurbineOptions {
public abstract TurbineOptions build();
}
-
- // TODO(b/188833569): remove when AutoValue adds @Nullable to Object if its on the classpath
- @Override
- public abstract boolean equals(@Nullable Object other);
-
- @Override
- public abstract int hashCode();
}
diff --git a/java/com/google/turbine/parse/StreamLexer.java b/java/com/google/turbine/parse/StreamLexer.java
index 3d46b90..ed79dd0 100644
--- a/java/com/google/turbine/parse/StreamLexer.java
+++ b/java/com/google/turbine/parse/StreamLexer.java
@@ -75,8 +75,8 @@ public class StreamLexer implements Lexer {
if (result == null) {
return null;
}
- verify(result.endsWith("*/"), result);
- return result.substring(0, result.length() - "*/".length());
+ verify(result.endsWith("*"), result);
+ return result.substring(0, result.length() - "*".length());
}
@Override
@@ -153,16 +153,18 @@ public class StreamLexer implements Lexer {
sawStar = true;
break;
case '/':
- eat();
if (sawStar) {
if (isJavadoc) {
// Save the comment, excluding the leading `/**` and including
// the trailing `/*`. The comment is trimmed and normalized later.
javadoc = stringValue();
+ verify(javadoc.endsWith("*"), javadoc);
}
+ eat();
continue OUTER;
}
sawStar = false;
+ eat();
break;
case ASCII_SUB:
if (reader.done()) {
diff --git a/java/com/google/turbine/processing/TurbineFiler.java b/java/com/google/turbine/processing/TurbineFiler.java
index 8c522ba..bc94870 100644
--- a/java/com/google/turbine/processing/TurbineFiler.java
+++ b/java/com/google/turbine/processing/TurbineFiler.java
@@ -20,7 +20,6 @@ import static com.google.common.base.Preconditions.checkArgument;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
-import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
@@ -44,6 +43,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Function;
import javax.annotation.processing.Filer;
import javax.annotation.processing.FilerException;
import javax.lang.model.element.Element;
diff --git a/java/com/google/turbine/processing/TurbineTypes.java b/java/com/google/turbine/processing/TurbineTypes.java
index 467059c..0b69bc3 100644
--- a/java/com/google/turbine/processing/TurbineTypes.java
+++ b/java/com/google/turbine/processing/TurbineTypes.java
@@ -20,7 +20,6 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Verify.verify;
import static java.util.Objects.requireNonNull;
-import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.bound.TypeBoundClass;
@@ -50,6 +49,7 @@ import com.google.turbine.types.Erasure;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.function.Function;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ArrayType;
diff --git a/java/com/google/turbine/types/Erasure.java b/java/com/google/turbine/types/Erasure.java
index 4b6fbc1..d9c35b3 100644
--- a/java/com/google/turbine/types/Erasure.java
+++ b/java/com/google/turbine/types/Erasure.java
@@ -16,7 +16,6 @@
package com.google.turbine.types;
-import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
@@ -29,6 +28,7 @@ import com.google.turbine.type.Type.IntersectionTy;
import com.google.turbine.type.Type.MethodTy;
import com.google.turbine.type.Type.TyVar;
import com.google.turbine.type.Type.WildTy;
+import java.util.function.Function;
/** Generic type erasure. */
public final class Erasure {
diff --git a/javatests/com/google/turbine/binder/BinderErrorTest.java b/javatests/com/google/turbine/binder/BinderErrorTest.java
index 6766470..a1bea05 100644
--- a/javatests/com/google/turbine/binder/BinderErrorTest.java
+++ b/javatests/com/google/turbine/binder/BinderErrorTest.java
@@ -681,7 +681,9 @@ public class BinderErrorTest {
"class T extends T {}",
},
{
- "<>:1: error: cycle in class hierarchy: T", "class T extends T {}", " ^",
+ "<>:1: error: cycle in class hierarchy: T", //
+ "class T extends T {}",
+ " ^",
},
},
{
@@ -692,6 +694,19 @@ public class BinderErrorTest {
"<>:1: error: cycle in class hierarchy: T",
"class T implements T {}",
" ^",
+ "<>:1: error: expected interface type",
+ "class T implements T {}",
+ " ^",
+ },
+ },
+ {
+ {
+ "interface T extends T {}",
+ },
+ {
+ "<>:1: error: cycle in class hierarchy: T",
+ "interface T extends T {}",
+ " ^",
},
},
{
@@ -768,7 +783,7 @@ public class BinderErrorTest {
"@interface Test {}",
},
{
- "<>:3: error: missing required annotation argument: value",
+ "<>:3: error: missing required annotation argument: value", //
"@Retention",
"^",
},
@@ -958,6 +973,40 @@ public class BinderErrorTest {
" ^",
},
},
+ {
+ {
+ "class C {}", //
+ "interface I {}",
+ "class A extends I implements C {}",
+ "interface B extends C {}",
+ },
+ {
+ "<>:3: error: unexpected interface type",
+ "class A extends I implements C {}",
+ " ^",
+ "<>:3: error: expected interface type",
+ "class A extends I implements C {}",
+ " ^",
+ "<>:4: error: expected interface type",
+ "interface B extends C {}",
+ " ^",
+ },
+ },
+ {
+ {
+ "class T<X, X> {", //
+ " <Y, Y> void f() {}",
+ "}",
+ },
+ {
+ "<>:1: error: duplicate declaration of X",
+ "class T<X, X> {",
+ " ^",
+ "<>:2: error: duplicate declaration of Y",
+ " <Y, Y> void f() {}",
+ " ^",
+ },
+ },
};
return Arrays.asList((Object[][]) testCases);
}
diff --git a/javatests/com/google/turbine/binder/BinderTest.java b/javatests/com/google/turbine/binder/BinderTest.java
index 40387ac..52b769b 100644
--- a/javatests/com/google/turbine/binder/BinderTest.java
+++ b/javatests/com/google/turbine/binder/BinderTest.java
@@ -265,7 +265,7 @@ public class BinderTest {
parseLines(
"import java.lang.annotation.Target;",
"import java.lang.annotation.ElementType;",
- "public class C implements B {",
+ "public class C extends B {",
" @Target(ElementType.TYPE_USE)",
" @interface A {};",
"}"));
diff --git a/javatests/com/google/turbine/bytecode/ClassReaderTest.java b/javatests/com/google/turbine/bytecode/ClassReaderTest.java
index ad5b90d..d7abea5 100644
--- a/javatests/com/google/turbine/bytecode/ClassReaderTest.java
+++ b/javatests/com/google/turbine/bytecode/ClassReaderTest.java
@@ -40,6 +40,7 @@ import org.objectweb.asm.ByteVector;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.ModuleVisitor;
import org.objectweb.asm.Opcodes;
@@ -57,12 +58,16 @@ public class ClassReaderTest {
null,
"java/lang/Object",
null);
- cw.visitMethod(
- Opcodes.ACC_PUBLIC,
- "f",
- "(Ljava/lang/String;)Ljava/lang/String;",
- "<T:Ljava/lang/String;>(TT;)TT;",
- null);
+ MethodVisitor mv =
+ cw.visitMethod(
+ Opcodes.ACC_PUBLIC,
+ "f",
+ "(Ljava/lang/String;)Ljava/lang/String;",
+ "<T:Ljava/lang/String;>(TT;)TT;",
+ null);
+ mv.visitParameter(null, 0); // skip synthetic parameters
+ mv.visitParameter("<no name>", Opcodes.ACC_SYNTHETIC); // skip synthetic parameters
+ mv.visitParameter("parameterName", 42);
cw.visitMethod(
Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
"g",
@@ -92,6 +97,9 @@ public class ClassReaderTest {
assertThat(f.annotations()).isEmpty();
assertThat(f.parameterAnnotations()).isEmpty();
assertThat(f.defaultValue()).isNull();
+ assertThat(f.parameters()).hasSize(1);
+ assertThat(f.parameters().get(0).name()).isEqualTo("parameterName");
+ assertThat(f.parameters().get(0).access()).isEqualTo(42);
ClassFile.MethodInfo g = classFile.methods().get(1);
assertThat(g.access()).isEqualTo(TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_STATIC);
diff --git a/javatests/com/google/turbine/bytecode/sig/SigIntegrationTest.java b/javatests/com/google/turbine/bytecode/sig/SigIntegrationTest.java
index 8602fe5..58c0eff 100644
--- a/javatests/com/google/turbine/bytecode/sig/SigIntegrationTest.java
+++ b/javatests/com/google/turbine/bytecode/sig/SigIntegrationTest.java
@@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
+import org.objectweb.asm.Opcodes;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
@@ -44,7 +45,6 @@ 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;
/**
* Reads all field, class, and method signatures in the bootclasspath, and round-trips them through
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 f20962b..6527a03 100644
--- a/javatests/com/google/turbine/lower/IntegrationTestSupport.java
+++ b/javatests/com/google/turbine/lower/IntegrationTestSupport.java
@@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.io.MoreFiles;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
+import org.objectweb.asm.Opcodes;
import com.google.turbine.binder.Binder;
import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.ClassPath;
@@ -496,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/LongStringIntegrationTest.java b/javatests/com/google/turbine/lower/LongStringIntegrationTest.java
index 7bb61e5..33deaee 100644
--- a/javatests/com/google/turbine/lower/LongStringIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/LongStringIntegrationTest.java
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import org.objectweb.asm.Opcodes;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
@@ -33,7 +34,6 @@ import org.junit.runners.JUnit4;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.Opcodes;
@RunWith(JUnit4.class)
public class LongStringIntegrationTest {
diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
index 94f1d07..6c95d44 100644
--- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
@@ -23,6 +23,7 @@ import static org.junit.Assume.assumeTrue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import java.io.IOError;
import java.io.IOException;
@@ -47,6 +48,8 @@ public class LowerIntegrationTest {
ImmutableMap.of(
"record.test", 16, //
"record2.test", 16,
+ "record_tostring.test", 16,
+ "record_ctor.test", 16,
"sealed.test", 17,
"sealed_nested.test", 17,
"textblock.test", 15);
@@ -269,9 +272,12 @@ public class LowerIntegrationTest {
"receiver_param.test",
"record.test",
"record2.test",
+ "record_ctor.test",
+ "record_tostring.test",
"rek.test",
"samepkg.test",
"sealed.test",
+ "sealed_nested.test",
"self.test",
"semi.test",
// https://bugs.openjdk.java.net/browse/JDK-8054064 ?
@@ -333,8 +339,9 @@ public class LowerIntegrationTest {
"wildcanon.test",
// keep-sorted end
};
- List<Object[]> tests =
- ImmutableList.copyOf(testCases).stream().map(x -> new Object[] {x}).collect(toList());
+ ImmutableSet<String> cases = ImmutableSet.copyOf(testCases);
+ assertThat(cases).containsAtLeastElementsIn(SOURCE_VERSION.keySet());
+ List<Object[]> tests = cases.stream().map(x -> new Object[] {x}).collect(toList());
String testShardIndex = System.getenv("TEST_SHARD_INDEX");
String testTotalShards = System.getenv("TEST_TOTAL_SHARDS");
if (testShardIndex == null || testTotalShards == null) {
@@ -384,7 +391,12 @@ public class LowerIntegrationTest {
int version = SOURCE_VERSION.getOrDefault(test, 8);
assumeTrue(version <= Runtime.version().feature());
ImmutableList<String> javacopts =
- ImmutableList.of("-source", String.valueOf(version), "-target", String.valueOf(version));
+ ImmutableList.of(
+ "-source",
+ String.valueOf(version),
+ "-target",
+ String.valueOf(version),
+ "-Xpkginfo:always");
Map<String, byte[]> expected =
IntegrationTestSupport.runJavac(input.sources, classpathJar, javacopts);
diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java
index a6410db..2de4650 100644
--- a/javatests/com/google/turbine/lower/LowerTest.java
+++ b/javatests/com/google/turbine/lower/LowerTest.java
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertThrows;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import org.objectweb.asm.Opcodes;
import com.google.turbine.binder.Binder;
import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.ClassPathBinder;
@@ -232,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(),
@@ -260,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())
@@ -340,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())
@@ -423,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())
@@ -544,7 +545,7 @@ public class LowerTest {
.put(
"Test.java",
lines(
- "public class Test extends B.BM {", //
+ "public class Test implements B.BM {", //
" I i;",
"}"))
.build();
@@ -649,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())
@@ -676,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);
}
diff --git a/javatests/com/google/turbine/lower/testdata/package_info.test b/javatests/com/google/turbine/lower/testdata/package_info.test
index a2416a4..6b0fc32 100644
--- a/javatests/com/google/turbine/lower/testdata/package_info.test
+++ b/javatests/com/google/turbine/lower/testdata/package_info.test
@@ -10,3 +10,5 @@ package p;
package p;
class Test {}
+=== Empty.java ===
+package p; \ No newline at end of file
diff --git a/javatests/com/google/turbine/lower/testdata/record_ctor.test b/javatests/com/google/turbine/lower/testdata/record_ctor.test
new file mode 100644
index 0000000..a3adc15
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/record_ctor.test
@@ -0,0 +1,52 @@
+=== Records.java ===
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+public class Records {
+ public record A(String value) {
+
+ void one() {}
+
+ public A(String a, String b) {
+ this(a + ", " + b);
+ }
+
+ void two() {}
+ }
+
+ @Target(ElementType.TYPE_USE)
+ @interface N {}
+
+ public record B(String value) {
+
+ void one() {}
+
+ public B(@N String value) {
+ this.value = value;
+ }
+
+ void two() {}
+
+ public B(String a, String b) {
+ this(a + ", " + b);
+ }
+
+ void three() {}
+ }
+
+ class Inner {}
+
+ public record C(Records.Inner value) {
+
+ public C(Records. @N Inner value) {
+ this.value = value;
+ }
+ }
+
+ public record D<T>(T value) {
+
+ public D(T value) {
+ this.value = value;
+ }
+ }
+}
diff --git a/javatests/com/google/turbine/lower/testdata/record_tostring.test b/javatests/com/google/turbine/lower/testdata/record_tostring.test
new file mode 100644
index 0000000..f93187a
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/record_tostring.test
@@ -0,0 +1,35 @@
+=== Records.java ===
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import java.util.Objects;
+
+class Records {
+ public record A() {
+ @Override
+ public String toString() {
+ return "A";
+ }
+ }
+
+ public record B() {
+ @Override
+ public final String toString() {
+ return "B";
+ }
+ }
+
+ public record C() {
+ @Override
+ public final boolean equals(Object o) {
+ return false;
+ }
+ }
+
+ public record D() {
+ @Override
+ public final int hashCode() {
+ return -1;
+ }
+ }
+} \ No newline at end of file
diff --git a/javatests/com/google/turbine/main/MainTest.java b/javatests/com/google/turbine/main/MainTest.java
index c894d9d..f65e6c0 100644
--- a/javatests/com/google/turbine/main/MainTest.java
+++ b/javatests/com/google/turbine/main/MainTest.java
@@ -106,7 +106,7 @@ public class MainTest {
@Test
public void packageInfo() throws IOException {
- Path src = temporaryFolder.newFile("package-info.jar").toPath();
+ Path src = temporaryFolder.newFile("package-info.java").toPath();
MoreFiles.asCharSink(src, UTF_8).write("@Deprecated package test;");
Path output = temporaryFolder.newFile("output.jar").toPath();
@@ -467,7 +467,7 @@ public class MainTest {
@Test
public void classGeneration() throws IOException {
- Path src = temporaryFolder.newFile("package-info.jar").toPath();
+ Path src = temporaryFolder.newFile("package-info.java").toPath();
MoreFiles.asCharSink(src, UTF_8).write("@Deprecated package test;");
File resources = temporaryFolder.newFile("resources.jar");
Main.compile(
diff --git a/javatests/com/google/turbine/parse/LexerTest.java b/javatests/com/google/turbine/parse/LexerTest.java
index bf0b374..6a6fe1c 100644
--- a/javatests/com/google/turbine/parse/LexerTest.java
+++ b/javatests/com/google/turbine/parse/LexerTest.java
@@ -339,6 +339,11 @@ public class LexerTest {
lexerComparisonTest("import pkg\uD800\uDC00.test;");
}
+ @Test
+ public void javadocUnicodeEscape() {
+ lexerComparisonTest("class {/***/\\u007D;");
+ }
+
private void lexerComparisonTest(String s) {
assertThat(lex(s)).containsExactlyElementsIn(JavacLexer.javacLex(s));
}
@@ -349,6 +354,8 @@ public class LexerTest {
Token token;
do {
token = lexer.next();
+ // Just check that javadoc handling doesn't crash
+ String unused = lexer.javadoc();
String tokenString;
switch (token) {
case IDENT:
diff --git a/javatests/com/google/turbine/parse/ParserIntegrationTest.java b/javatests/com/google/turbine/parse/ParserIntegrationTest.java
index c758a74..0981815 100644
--- a/javatests/com/google/turbine/parse/ParserIntegrationTest.java
+++ b/javatests/com/google/turbine/parse/ParserIntegrationTest.java
@@ -20,15 +20,12 @@ import static com.google.common.base.Verify.verifyNotNull;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.google.common.base.Function;
import com.google.common.base.Splitter;
-import com.google.common.collect.Iterables;
import com.google.common.io.CharStreams;
import com.google.turbine.tree.Tree;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,8 +36,8 @@ import org.junit.runners.Parameterized.Parameters;
public class ParserIntegrationTest {
@Parameters(name = "{index}: {0}")
- public static Iterable<Object[]> parameters() {
- String[] tests = {
+ public static String[] parameters() {
+ return new String[] {
"anno1.input",
"anno2.input",
"annodecl1.input",
@@ -79,14 +76,6 @@ public class ParserIntegrationTest {
"record.input",
"sealed.input",
};
- return Iterables.transform(
- Arrays.asList(tests),
- new Function<String, Object[]>() {
- @Override
- public Object[] apply(String input) {
- return new Object[] {input};
- }
- });
}
final String input;
diff --git a/javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java b/javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java
index 11dedbf..bc6d9e6 100644
--- a/javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java
+++ b/javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java
@@ -77,17 +77,17 @@ public class TurbineElementsGetAllMembersTest {
},
{
"=== I.java ===",
- "abstract class I {",
- " abstract Integer f();",
+ "interface I {",
+ " default Integer f() {}",
"}",
"=== J.java ===",
- "interface J extends I {",
- " default Integer f() {",
+ "class J implements I {",
+ " Integer f() {",
" return 42;",
" }",
"}",
"=== Test.java ===", //
- "class Test extends I implements J {",
+ "class Test extends J implements I {",
"}",
},
{
diff --git a/javatests/com/google/turbine/processing/TurbineFilerTest.java b/javatests/com/google/turbine/processing/TurbineFilerTest.java
index 96c325b..f76a08d 100644
--- a/javatests/com/google/turbine/processing/TurbineFilerTest.java
+++ b/javatests/com/google/turbine/processing/TurbineFilerTest.java
@@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertThrows;
-import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.io.CharStreams;
import com.google.turbine.diag.SourceFile;
@@ -34,6 +33,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.function.Function;
import javax.annotation.processing.FilerException;
import javax.lang.model.element.Element;
import javax.tools.FileObject;
diff --git a/pom.xml b/pom.xml
index a2bf088..c96551f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,7 +31,7 @@
<properties>
<asm.version>9.4</asm.version>
- <guava.version>31.0.1-jre</guava.version>
+ <guava.version>31.1-jre</guava.version>
<errorprone.version>2.16</errorprone.version>
<maven-javadoc-plugin.version>3.3.1</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.2.1</maven-source-plugin.version>