diff options
author | Yifei Zhang <yfz@google.com> | 2023-07-25 07:50:08 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-07-25 07:50:08 +0000 |
commit | f7d94438c8bcdfdbf0d5a2a5e40120d0696e7088 (patch) | |
tree | 405bdc36825a61db2a15f10b8775c78010ee0b56 | |
parent | d4be1f10b831f4c3774091c74e4b904ec6450c9a (diff) | |
parent | ea1cf67d10bfde29a1bb50ba92fd4d3137916e41 (diff) | |
download | turbine-android14-qpr2-s2-release.tar.gz |
Upgrade turbine to 7c64f0447a967d4717adb7b1b40d8bb856f34186 am: 6f7cfa29c3 am: 278edfeffb am: 5ff202242e am: 820217dbd2 am: ea1cf67d10HEADandroid-14.0.0_r37android-14.0.0_r36android-14.0.0_r35android-14.0.0_r34android-14.0.0_r33android-14.0.0_r32android-14.0.0_r31android-14.0.0_r30android-14.0.0_r29mastermainandroid14-qpr2-s5-releaseandroid14-qpr2-s4-releaseandroid14-qpr2-s3-releaseandroid14-qpr2-s2-releaseandroid14-qpr2-s1-releaseandroid14-qpr2-release
Original change: https://android-review.googlesource.com/c/platform/external/turbine/+/2674341
Change-Id: I83c10ceb7d338fd1791d717b833e2c7d589f7704
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
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: @@ -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; @@ -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> |