diff options
Diffstat (limited to 'java/com/google')
-rw-r--r-- | java/com/google/turbine/binder/ClassPathBinder.java | 2 | ||||
-rw-r--r-- | java/com/google/turbine/binder/CompUnitPreprocessor.java | 16 | ||||
-rw-r--r-- | java/com/google/turbine/binder/HierarchyBinder.java | 11 | ||||
-rw-r--r-- | java/com/google/turbine/binder/Processing.java | 2 | ||||
-rw-r--r-- | java/com/google/turbine/binder/TypeBinder.java | 318 | ||||
-rw-r--r-- | java/com/google/turbine/bytecode/ClassReader.java | 5 | ||||
-rw-r--r-- | java/com/google/turbine/diag/TurbineError.java | 2 | ||||
-rw-r--r-- | java/com/google/turbine/lower/Lower.java | 80 | ||||
-rw-r--r-- | java/com/google/turbine/main/Main.java | 44 | ||||
-rw-r--r-- | java/com/google/turbine/options/TurbineOptions.java | 10 | ||||
-rw-r--r-- | java/com/google/turbine/parse/StreamLexer.java | 8 | ||||
-rw-r--r-- | java/com/google/turbine/processing/TurbineFiler.java | 2 | ||||
-rw-r--r-- | java/com/google/turbine/processing/TurbineTypes.java | 2 | ||||
-rw-r--r-- | java/com/google/turbine/types/Erasure.java | 2 |
14 files changed, 343 insertions, 161 deletions
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 { |