diff options
Diffstat (limited to 'java/com')
59 files changed, 1115 insertions, 408 deletions
diff --git a/java/com/google/common/escape/SourceCodeEscapers.java b/java/com/google/common/escape/SourceCodeEscapers.java index 4a1aa99..c0f9d6b 100644 --- a/java/com/google/common/escape/SourceCodeEscapers.java +++ b/java/com/google/common/escape/SourceCodeEscapers.java @@ -22,7 +22,7 @@ import java.util.Map; /** * A factory for Escaper instances used to escape strings for safe use in Java. * - * <p>This is a subset of source code escapers that are in the process of being open-sources as part + * <p>This is a subset of source code escapers that are in the process of being open-sourced as part * of guava, see: https://github.com/google/guava/issues/1620 */ // TODO(cushon): migrate to the guava version once it is open-sourced, and delete this @@ -43,8 +43,8 @@ public final class SourceCodeEscapers { * safely be included in either a Java character literal or string literal. This is the preferred * way to escape Java characters for use in String or character literals. * - * <p>See: <a href= "http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#101089" - * >The Java Language Specification</a> for more details. + * <p>See: <a href="https://docs.oracle.com/javase/specs/jls/se14/html/jls-3.html#jls-3.10.6" >The + * Java Language Specification</a> for more details. */ public static CharEscaper javaCharEscaper() { return JAVA_CHAR_ESCAPER; @@ -66,7 +66,7 @@ public final class SourceCodeEscapers { } // This escaper does not produce octal escape sequences. See: - // http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#101089 + // https://docs.oracle.com/javase/specs/jls/se14/html/jls-3.html#jls-3.10.6 // "Octal escapes are provided for compatibility with C, but can express // only Unicode values \u0000 through \u00FF, so Unicode escapes are // usually preferred." diff --git a/java/com/google/turbine/binder/Binder.java b/java/com/google/turbine/binder/Binder.java index 0e3f41f..6c828b3 100644 --- a/java/com/google/turbine/binder/Binder.java +++ b/java/com/google/turbine/binder/Binder.java @@ -54,6 +54,7 @@ import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.FieldSymbol; import com.google.turbine.binder.sym.ModuleSymbol; import com.google.turbine.diag.SourceFile; +import com.google.turbine.diag.TurbineDiagnostic; import com.google.turbine.diag.TurbineError; import com.google.turbine.diag.TurbineError.ErrorKind; import com.google.turbine.diag.TurbineLog; @@ -68,7 +69,7 @@ import java.util.Optional; import javax.annotation.processing.Processor; /** The entry point for analysis. */ -public class Binder { +public final class Binder { /** Binds symbols and types to the given compilation units. */ public static BindingResult bind( @@ -87,19 +88,28 @@ public class Binder { ClassPath bootclasspath, Optional<String> moduleVersion) { TurbineLog log = new TurbineLog(); - BindingResult br = - bind( - log, - units, - /* generatedSources= */ ImmutableMap.of(), - /* generatedClasses= */ ImmutableMap.of(), - classpath, - bootclasspath, - moduleVersion); - if (!processorInfo.processors().isEmpty() && !units.isEmpty()) { + BindingResult br; + try { br = - Processing.process( - log, units, classpath, processorInfo, bootclasspath, br, moduleVersion); + bind( + log, + units, + /* generatedSources= */ ImmutableMap.of(), + /* generatedClasses= */ ImmutableMap.of(), + classpath, + bootclasspath, + moduleVersion); + if (!processorInfo.processors().isEmpty() && !units.isEmpty()) { + br = + Processing.process( + log, units, classpath, processorInfo, bootclasspath, br, moduleVersion); + } + } catch (TurbineError turbineError) { + throw new TurbineError( + ImmutableList.<TurbineDiagnostic>builder() + .addAll(log.diagnostics()) + .addAll(turbineError.diagnostics()) + .build()); } log.maybeThrow(); return br; @@ -540,4 +550,6 @@ public class Binder { units, modules, classPathEnv, tli, generatedSources, generatedClasses, statistics); } } + + private Binder() {} } diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java index a2f045a..ae82b4f 100644 --- a/java/com/google/turbine/binder/CanonicalTypeBinder.java +++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java @@ -38,19 +38,15 @@ import java.util.Map; /** * Canonicalizes all qualified types in a {@link SourceTypeBoundClass} using {@link Canonicalize}. */ -public class CanonicalTypeBinder { +public final class CanonicalTypeBinder { static SourceTypeBoundClass bind( ClassSymbol sym, SourceTypeBoundClass base, Env<ClassSymbol, TypeBoundClass> env) { - ClassTy superClassType = null; - if (base.superClassType() != null && base.superClassType().tyKind() == TyKind.CLASS_TY) { + Type superClassType = base.superClassType(); + if (superClassType != null && superClassType.tyKind() == TyKind.CLASS_TY) { superClassType = Canonicalize.canonicalizeClassTy( - base.source(), - base.decl().position(), - env, - base.owner(), - (ClassTy) base.superClassType()); + base.source(), base.decl().position(), env, base.owner(), (ClassTy) superClassType); } ImmutableList.Builder<Type> interfaceTypes = ImmutableList.builder(); for (Type i : base.interfaceTypes()) { @@ -133,9 +129,7 @@ public class CanonicalTypeBinder { base.defaultValue(), base.decl(), base.annotations(), - base.receiver() != null - ? param(source, base.decl().position(), env, sym, base.receiver()) - : null)); + base.receiver() != null ? param(source, pos, env, sym, base.receiver()) : null)); } return result.build(); } @@ -181,4 +175,6 @@ public class CanonicalTypeBinder { } return result.build(); } + + private CanonicalTypeBinder() {} } diff --git a/java/com/google/turbine/binder/ClassPathBinder.java b/java/com/google/turbine/binder/ClassPathBinder.java index 8aead80..1825c23 100644 --- a/java/com/google/turbine/binder/ClassPathBinder.java +++ b/java/com/google/turbine/binder/ClassPathBinder.java @@ -38,7 +38,7 @@ import java.util.LinkedHashMap; import java.util.Map; /** Sets up an environment for symbols on the classpath. */ -public class ClassPathBinder { +public final class ClassPathBinder { /** * The prefix for repackaged transitive dependencies; see {@link @@ -148,4 +148,6 @@ public class ClassPathBinder { } }); } + + private ClassPathBinder() {} } diff --git a/java/com/google/turbine/binder/CompUnitPreprocessor.java b/java/com/google/turbine/binder/CompUnitPreprocessor.java index ed70e88..9e9a0bb 100644 --- a/java/com/google/turbine/binder/CompUnitPreprocessor.java +++ b/java/com/google/turbine/binder/CompUnitPreprocessor.java @@ -45,7 +45,7 @@ import java.util.Set; * Processes compilation units before binding, creating symbols for type declarations and desugaring * access modifiers. */ -public class CompUnitPreprocessor { +public final class CompUnitPreprocessor { /** A pre-processed compilation unit. */ public static class PreprocessedCompUnit { @@ -222,4 +222,6 @@ public class CompUnitPreprocessor { TurbineTyKind.INTERFACE, /* javadoc= */ null); } + + private CompUnitPreprocessor() {} } diff --git a/java/com/google/turbine/binder/ConstBinder.java b/java/com/google/turbine/binder/ConstBinder.java index 3a41e94..8511183 100644 --- a/java/com/google/turbine/binder/ConstBinder.java +++ b/java/com/google/turbine/binder/ConstBinder.java @@ -16,6 +16,8 @@ package com.google.turbine.binder; +import static java.util.Objects.requireNonNull; + import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -196,6 +198,9 @@ public class ConstBinder { private static RetentionPolicy bindRetention(AnnoInfo annotation) { Const value = annotation.values().get("value"); + if (value == null) { + return null; + } if (value.kind() != Kind.ENUM_CONSTANT) { return null; } @@ -208,7 +213,8 @@ public class ConstBinder { private static ImmutableSet<TurbineElementType> bindTarget(AnnoInfo annotation) { ImmutableSet.Builder<TurbineElementType> result = ImmutableSet.builder(); - Const val = annotation.values().get("value"); + // requireNonNull is safe because java.lang.annotation.Target declares `value`. + Const val = requireNonNull(annotation.values().get("value")); switch (val.kind()) { case ARRAY: for (Const element : ((ArrayInitValue) val).elements()) { @@ -227,7 +233,8 @@ public class ConstBinder { } private static ClassSymbol bindRepeatable(AnnoInfo annotation) { - Const value = annotation.values().get("value"); + // requireNonNull is safe because java.lang.annotation.Repeatable declares `value`. + Const value = requireNonNull(annotation.values().get("value")); if (value.kind() != Kind.CLASS_LITERAL) { return null; } @@ -268,11 +275,12 @@ public class ConstBinder { if ((base.access() & TurbineFlag.ACC_FINAL) == 0) { return null; } - switch (base.type().tyKind()) { + Type type = base.type(); + switch (type.tyKind()) { case PRIM_TY: break; case CLASS_TY: - if (((Type.ClassTy) base.type()).sym().equals(ClassSymbol.STRING)) { + if (((Type.ClassTy) type).sym().equals(ClassSymbol.STRING)) { break; } // falls through @@ -280,8 +288,11 @@ public class ConstBinder { return null; } Value value = constantEnv.get(base.sym()); - if (value != null) { - value = (Value) ConstEvaluator.cast(base.type(), value); + if (value == null) { + return null; + } + if (type.tyKind().equals(TyKind.PRIM_TY)) { + value = ConstEvaluator.coerce(value, ((Type.PrimTy) type).primkind()); } return value; } diff --git a/java/com/google/turbine/binder/ConstEvaluator.java b/java/com/google/turbine/binder/ConstEvaluator.java index 9d5f042..bef98a7 100644 --- a/java/com/google/turbine/binder/ConstEvaluator.java +++ b/java/com/google/turbine/binder/ConstEvaluator.java @@ -47,6 +47,7 @@ import com.google.turbine.model.Const.ConstCastError; import com.google.turbine.model.Const.Value; import com.google.turbine.model.TurbineConstantTypeKind; import com.google.turbine.model.TurbineFlag; +import com.google.turbine.model.TurbineTyKind; import com.google.turbine.tree.Tree; import com.google.turbine.tree.Tree.ArrayInit; import com.google.turbine.tree.Tree.Binary; @@ -126,22 +127,13 @@ public strictfp class ConstEvaluator { } switch (a.constantTypeKind()) { case CHAR: - return new Const.CharValue(((com.google.turbine.model.Const.CharValue) a).value()); case INT: - return new Const.IntValue(((com.google.turbine.model.Const.IntValue) a).value()); case LONG: - return new Const.LongValue(((com.google.turbine.model.Const.LongValue) a).value()); case FLOAT: - return new Const.FloatValue(((com.google.turbine.model.Const.FloatValue) a).value()); case DOUBLE: - return new Const.DoubleValue( - ((com.google.turbine.model.Const.DoubleValue) a).value()); case BOOLEAN: - return new Const.BooleanValue( - ((com.google.turbine.model.Const.BooleanValue) a).value()); case STRING: - return new Const.StringValue( - ((com.google.turbine.model.Const.StringValue) a).value()); + return a; case SHORT: case BYTE: case NULL: @@ -318,20 +310,24 @@ public strictfp class ConstEvaluator { } /** Casts the value to the given type. */ - static Const cast(Type ty, Const value) { + private Const cast(int position, Type ty, Const value) { checkNotNull(value); switch (ty.tyKind()) { case CLASS_TY: case TY_VAR: return value; case PRIM_TY: + if (!value.kind().equals(Const.Kind.PRIMITIVE)) { + throw error(position, ErrorKind.EXPRESSION_ERROR); + } return coerce((Const.Value) value, ((Type.PrimTy) ty).primkind()); default: throw new AssertionError(ty.tyKind()); } } - private static Const.Value coerce(Const.Value value, TurbineConstantTypeKind kind) { + /** Casts the constant value to the given type. */ + static Const.Value coerce(Const.Value value, TurbineConstantTypeKind kind) { switch (kind) { case BOOLEAN: return value.asBoolean(); @@ -925,12 +921,16 @@ public strictfp class ConstEvaluator { if (info.sym() == null) { return info; } - - Map<String, Type> template = new LinkedHashMap<>(); TypeBoundClass annoClass = env.get(info.sym()); + if (annoClass.kind() != TurbineTyKind.ANNOTATION) { + // we've already reported an error for non-annotation symbols used as annotations, + // skip error handling for annotation arguments + return info; + } + Map<String, MethodInfo> template = new LinkedHashMap<>(); if (annoClass != null) { for (MethodInfo method : annoClass.methods()) { - template.put(method.name(), method.returnType()); + template.put(method.name(), method); } } @@ -947,20 +947,28 @@ public strictfp class ConstEvaluator { key = "value"; expr = arg; } - Type ty = template.get(key); - if (ty == null) { - throw error( + MethodInfo methodInfo = template.remove(key); + if (methodInfo == null) { + log.error( arg.position(), ErrorKind.CANNOT_RESOLVE, String.format("element %s() in %s", key, info.sym())); + continue; } - Const value = evalAnnotationValue(expr, ty); + Const value = evalAnnotationValue(expr, methodInfo.returnType()); if (value == null) { - throw error(expr.position(), ErrorKind.EXPRESSION_ERROR); + log.error(expr.position(), ErrorKind.EXPRESSION_ERROR); + continue; } Const existing = values.put(key, value); if (existing != null) { - throw error(arg.position(), ErrorKind.INVALID_ANNOTATION_ARGUMENT); + log.error(arg.position(), ErrorKind.INVALID_ANNOTATION_ARGUMENT); + continue; + } + } + for (MethodInfo methodInfo : template.values()) { + if (!methodInfo.hasDefaultValue()) { + log.error(info.tree().position(), ErrorKind.MISSING_ANNOTATION_ARGUMENT, methodInfo.name()); } } return info.withValues(ImmutableMap.copyOf(values)); @@ -969,8 +977,9 @@ public strictfp class ConstEvaluator { private TurbineAnnotationValue evalAnno(Tree.Anno t) { LookupResult result = scope.lookup(new LookupKey(t.name())); if (result == null) { - throw error( + log.error( t.name().get(0).position(), ErrorKind.CANNOT_RESOLVE, Joiner.on(".").join(t.name())); + return null; } ClassSymbol sym = (ClassSymbol) result.sym(); for (Ident name : result.remaining()) { @@ -982,6 +991,9 @@ public strictfp class ConstEvaluator { if (sym == null) { return null; } + if (env.get(sym).kind() != TurbineTyKind.ANNOTATION) { + log.error(t.position(), ErrorKind.NOT_AN_ANNOTATION, sym); + } AnnoInfo annoInfo = evaluateAnnotation(new AnnoInfo(source, sym, t, ImmutableMap.of())); return new TurbineAnnotationValue(annoInfo); } @@ -1004,7 +1016,8 @@ public strictfp class ConstEvaluator { } Const value = eval(tree); if (value == null) { - throw error(tree.position(), ErrorKind.EXPRESSION_ERROR); + log.error(tree.position(), ErrorKind.EXPRESSION_ERROR); + return null; } switch (ty.tyKind()) { case PRIM_TY: @@ -1024,7 +1037,7 @@ public strictfp class ConstEvaluator { : ImmutableList.of(value); ImmutableList.Builder<Const> coerced = ImmutableList.builder(); for (Const element : elements) { - coerced.add(cast(elementType, element)); + coerced.add(cast(tree.position(), elementType, element)); } return new Const.ArrayInitValue(coerced.build()); } @@ -1043,7 +1056,7 @@ public strictfp class ConstEvaluator { if (value == null || value.kind() != Const.Kind.PRIMITIVE) { return null; } - return (Const.Value) cast(type, value); + return (Const.Value) cast(expression.position(), type, value); } catch (TurbineError error) { for (TurbineDiagnostic diagnostic : error.diagnostics()) { switch (diagnostic.kind()) { diff --git a/java/com/google/turbine/binder/CtSymClassBinder.java b/java/com/google/turbine/binder/CtSymClassBinder.java index a6f1b3d..1d7ece7 100644 --- a/java/com/google/turbine/binder/CtSymClassBinder.java +++ b/java/com/google/turbine/binder/CtSymClassBinder.java @@ -16,11 +16,15 @@ package com.google.turbine.binder; +import static com.google.common.base.Ascii.toUpperCase; import static com.google.common.base.StandardSystemProperty.JAVA_HOME; +import static java.util.Objects.requireNonNull; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; +import com.google.common.primitives.Ints; import com.google.turbine.binder.bound.ModuleInfo; import com.google.turbine.binder.bytecode.BytecodeBinder; import com.google.turbine.binder.bytecode.BytecodeBoundClass; @@ -32,6 +36,7 @@ import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.ModuleSymbol; import com.google.turbine.zip.Zip; import java.io.IOException; +import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -40,12 +45,13 @@ import java.util.Map; import org.checkerframework.checker.nullness.qual.Nullable; /** Constructs a platform {@link ClassPath} from the current JDK's ct.sym file. */ -public class CtSymClassBinder { +public final class CtSymClassBinder { @Nullable public static ClassPath bind(String version) throws IOException { - Path javaHome = Paths.get(JAVA_HOME.value()); - Path ctSym = javaHome.resolve("lib/ct.sym"); + String javaHome = JAVA_HOME.value(); + requireNonNull(javaHome, "attempted to use --release, but JAVA_HOME is not set"); + Path ctSym = Paths.get(javaHome).resolve("lib/ct.sym"); if (!Files.exists(ctSym)) { throw new IllegalStateException("lib/ct.sym does not exist in " + javaHome); } @@ -59,7 +65,9 @@ public class CtSymClassBinder { } }; // ct.sym contains directories whose names are the concatentation of a list of target versions - // (e.g. 789) and which contain interface class files with a .sig extension. + // formatted as a single character 0-9 or A-Z (e.g. 789A) and which contain interface class + // files with a .sig extension. + String releaseString = formatReleaseVersion(version); for (Zip.Entry ze : new Zip.ZipIterable(ctSym)) { String name = ze.name(); if (!name.endsWith(".sig")) { @@ -70,10 +78,13 @@ public class CtSymClassBinder { continue; } // check if the directory matches the desired release - // TODO(cushon): what happens when version numbers contain more than one digit? - if (!ze.name().substring(0, idx).contains(version)) { + if (!ze.name().substring(0, idx).contains(releaseString)) { continue; } + if (isAtLeastJDK12()) { + // JDK >= 12 includes the module name as a prefix + idx = name.indexOf('/', idx + 1); + } if (name.substring(name.lastIndexOf('/') + 1).equals("module-info.sig")) { ModuleInfo moduleInfo = BytecodeBinder.bindModuleInfo(name, toByteArrayOrDie(ze)); modules.put(new ModuleSymbol(moduleInfo.name()), moduleInfo); @@ -122,4 +133,28 @@ public class CtSymClassBinder { } }); } + + @VisibleForTesting + static String formatReleaseVersion(String version) { + Integer n = Ints.tryParse(version); + if (n == null || n <= 4 || n >= 36) { + throw new IllegalArgumentException("invalid release version: " + version); + } + return toUpperCase(Integer.toString(n, 36)); + } + + private static boolean isAtLeastJDK12() { + int major; + try { + Method versionMethod = Runtime.class.getMethod("version"); + Object version = versionMethod.invoke(null); + major = (int) version.getClass().getMethod("major").invoke(version); + } catch (ReflectiveOperationException e) { + // `Runtime.version()` was added in JDK 9 + return false; + } + return major >= 12; + } + + private CtSymClassBinder() {} } diff --git a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java index 7e3fbda..c5de8c1 100644 --- a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java +++ b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java @@ -65,7 +65,7 @@ import java.util.Map; * constant binding is done, read the {@code @Target} meta-annotation for each ambiguous annotation, * and move it to the appropriate location. */ -public class DisambiguateTypeAnnotations { +public final class DisambiguateTypeAnnotations { public static SourceTypeBoundClass bind( SourceTypeBoundClass base, Env<ClassSymbol, TypeBoundClass> env) { return new SourceTypeBoundClass( @@ -317,4 +317,6 @@ public class DisambiguateTypeAnnotations { } return false; } + + private DisambiguateTypeAnnotations() {} } diff --git a/java/com/google/turbine/binder/FileManagerClassBinder.java b/java/com/google/turbine/binder/FileManagerClassBinder.java new file mode 100644 index 0000000..42a8162 --- /dev/null +++ b/java/com/google/turbine/binder/FileManagerClassBinder.java @@ -0,0 +1,235 @@ +/* + * Copyright 2020 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.turbine.binder; + +import com.google.common.base.Joiner; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableMap; +import com.google.common.io.ByteStreams; +import com.google.turbine.binder.bound.ModuleInfo; +import com.google.turbine.binder.bytecode.BytecodeBoundClass; +import com.google.turbine.binder.env.Env; +import com.google.turbine.binder.env.SimpleEnv; +import com.google.turbine.binder.lookup.LookupKey; +import com.google.turbine.binder.lookup.LookupResult; +import com.google.turbine.binder.lookup.PackageScope; +import com.google.turbine.binder.lookup.Scope; +import com.google.turbine.binder.lookup.TopLevelIndex; +import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.binder.sym.ModuleSymbol; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import javax.tools.FileObject; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Binds a {@link StandardJavaFileManager} to an {@link ClassPath}. This can be used to share a + * filemanager (and associated IO costs) between turbine and javac when running both in the same + * process. + */ +public final class FileManagerClassBinder { + + public static ClassPath adapt(StandardJavaFileManager fileManager, StandardLocation location) { + PackageLookup packageLookup = new PackageLookup(fileManager, location); + Env<ClassSymbol, BytecodeBoundClass> env = + new Env<ClassSymbol, BytecodeBoundClass>() { + @Override + public BytecodeBoundClass get(ClassSymbol sym) { + return packageLookup.getPackage(this, sym.packageName()).get(sym); + } + }; + SimpleEnv<ModuleSymbol, ModuleInfo> moduleEnv = new SimpleEnv<>(ImmutableMap.of()); + TopLevelIndex tli = new FileManagerTopLevelIndex(env, packageLookup); + return new ClassPath() { + @Override + public Env<ClassSymbol, BytecodeBoundClass> env() { + return env; + } + + @Override + public Env<ModuleSymbol, ModuleInfo> moduleEnv() { + return moduleEnv; + } + + @Override + public TopLevelIndex index() { + return tli; + } + + @Override + public Supplier<byte[]> resource(String path) { + return packageLookup.resource(path); + } + }; + } + + private static class PackageLookup { + + private final Map<String, Map<ClassSymbol, BytecodeBoundClass>> packages = new HashMap<>(); + private final StandardJavaFileManager fileManager; + private final StandardLocation location; + + private PackageLookup(StandardJavaFileManager fileManager, StandardLocation location) { + this.fileManager = fileManager; + this.location = location; + } + + private ImmutableMap<ClassSymbol, BytecodeBoundClass> listPackage( + Env<ClassSymbol, BytecodeBoundClass> env, String packageName) throws IOException { + Map<ClassSymbol, BytecodeBoundClass> result = new HashMap<>(); + for (JavaFileObject jfo : + fileManager.list( + location, + packageName.replace('/', '.'), + EnumSet.of(JavaFileObject.Kind.CLASS), + false)) { + String binaryName = fileManager.inferBinaryName(location, jfo); + ClassSymbol sym = new ClassSymbol(binaryName.replace('.', '/')); + result.putIfAbsent( + sym, + new BytecodeBoundClass( + sym, + new Supplier<byte[]>() { + @Override + public byte[] get() { + try { + return ByteStreams.toByteArray(jfo.openInputStream()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + }, + env, + /* jarFile= */ null)); + } + return ImmutableMap.copyOf(result); + } + + private Map<ClassSymbol, BytecodeBoundClass> getPackage( + Env<ClassSymbol, BytecodeBoundClass> env, String key) { + return packages.computeIfAbsent( + key, + k -> { + try { + return listPackage(env, key); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + + public Supplier<byte[]> resource(String resource) { + String dir; + String name; + int idx = resource.lastIndexOf('/'); + if (idx != -1) { + dir = resource.substring(0, idx + 1); + name = resource.substring(idx + 1, resource.length()); + } else { + dir = ""; + name = resource; + } + FileObject fileObject; + try { + fileObject = fileManager.getFileForInput(location, dir, name); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + if (fileObject == null) { + return null; + } + return new Supplier<byte[]>() { + @Override + public byte[] get() { + try { + return ByteStreams.toByteArray(fileObject.openInputStream()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + }; + } + } + + private static class FileManagerTopLevelIndex implements TopLevelIndex { + private final Env<ClassSymbol, BytecodeBoundClass> env; + private final PackageLookup packageLookup; + + public FileManagerTopLevelIndex( + Env<ClassSymbol, BytecodeBoundClass> env, PackageLookup packageLookup) { + this.env = env; + this.packageLookup = packageLookup; + } + + @Override + public Scope scope() { + return new Scope() { + @Override + public @Nullable LookupResult lookup(LookupKey lookupKey) { + for (int i = lookupKey.simpleNames().size(); i > 0; i--) { + String p = Joiner.on('/').join(lookupKey.simpleNames().subList(0, i)); + ClassSymbol sym = new ClassSymbol(p); + BytecodeBoundClass r = env.get(sym); + if (r != null) { + return new LookupResult( + sym, + new LookupKey( + lookupKey.simpleNames().subList(i - 1, lookupKey.simpleNames().size()))); + } + } + return null; + } + }; + } + + @Override + public PackageScope lookupPackage(Iterable<String> names) { + String packageName = Joiner.on('/').join(names); + Map<ClassSymbol, BytecodeBoundClass> pkg = packageLookup.getPackage(env, packageName); + if (pkg.isEmpty()) { + return null; + } + return new PackageScope() { + @Override + public Iterable<ClassSymbol> classes() { + return pkg.keySet(); + } + + @Override + public @Nullable LookupResult lookup(LookupKey lookupKey) { + String className = lookupKey.first().value(); + if (!packageName.isEmpty()) { + className = packageName + "/" + className; + } + ClassSymbol sym = new ClassSymbol(className); + if (!pkg.containsKey(sym)) { + return null; + } + return new LookupResult(sym, lookupKey); + } + }; + } + } + + private FileManagerClassBinder() {} +} diff --git a/java/com/google/turbine/binder/ModuleBinder.java b/java/com/google/turbine/binder/ModuleBinder.java index 748ff39..04ce81d 100644 --- a/java/com/google/turbine/binder/ModuleBinder.java +++ b/java/com/google/turbine/binder/ModuleBinder.java @@ -216,11 +216,12 @@ public class ModuleBinder { } ClassSymbol sym = (ClassSymbol) result.sym(); for (Tree.Ident name : result.remaining()) { - sym = Resolve.resolve(env, /* origin= */ null, sym, name); - if (sym == null) { + ClassSymbol next = Resolve.resolve(env, /* origin= */ null, sym, name); + if (next == null) { throw error( ErrorKind.SYMBOL_NOT_FOUND, pos, new ClassSymbol(sym.binaryName() + '$' + name)); } + sym = next; } return sym; } diff --git a/java/com/google/turbine/binder/Processing.java b/java/com/google/turbine/binder/Processing.java index ecdf195..16407aa 100644 --- a/java/com/google/turbine/binder/Processing.java +++ b/java/com/google/turbine/binder/Processing.java @@ -16,7 +16,10 @@ package com.google.turbine.binder; +import static java.util.Objects.requireNonNull; + import com.google.auto.value.AutoValue; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Stopwatch; @@ -27,6 +30,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Sets; +import com.google.common.primitives.Ints; import com.google.turbine.binder.Binder.BindingResult; import com.google.turbine.binder.Binder.Statistics; import com.google.turbine.binder.bound.SourceTypeBoundClass; @@ -56,7 +60,6 @@ import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; @@ -76,6 +79,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; /** Top level annotation processing logic, see also {@link Binder}. */ public class Processing { + @Nullable static BindingResult process( TurbineLog log, final ImmutableList<CompUnit> initialSources, @@ -131,19 +135,13 @@ public class Processing { try (Timers.Timer unused = timers.start(processor)) { processor.init(processingEnv); } catch (Throwable t) { - reportProcessorCrash(log, processor, t); + logProcessorCrash(log, processor, t); + return null; } } - Map<Processor, Pattern> wanted = new HashMap<>(); - for (Processor processor : processorInfo.processors()) { - List<String> patterns = new ArrayList<>(); - for (String supportedAnnotationType : processor.getSupportedAnnotationTypes()) { - // TODO(b/139026291): this handling of getSupportedAnnotationTypes isn't correct - patterns.add(supportedAnnotationType.replace("*", ".*")); - } - wanted.put(processor, Pattern.compile(Joiner.on('|').join(patterns))); - } + ImmutableMap<Processor, SupportedAnnotationTypes> wanted = + initializeSupportedAnnotationTypes(processorInfo); Set<ClassSymbol> allSymbols = new HashSet<>(); @@ -163,12 +161,14 @@ public class Processing { } ImmutableSetMultimap<ClassSymbol, Symbol> allAnnotations = getAllAnnotations(env, syms); TurbineRoundEnvironment roundEnv = null; - for (Processor processor : processorInfo.processors()) { + for (Map.Entry<Processor, SupportedAnnotationTypes> e : wanted.entrySet()) { + Processor processor = e.getKey(); + SupportedAnnotationTypes supportedAnnotationTypes = e.getValue(); Set<TypeElement> annotations = new HashSet<>(); - Pattern pattern = wanted.get(processor); - boolean run = toRun.contains(processor); + boolean run = supportedAnnotationTypes.everything() || toRun.contains(processor); for (ClassSymbol a : allAnnotations.keys()) { - if (pattern.matcher(a.toString()).matches()) { + if (supportedAnnotationTypes.everything() + || supportedAnnotationTypes.pattern().matcher(a.toString()).matches()) { annotations.add(factory.typeElement(a)); run = true; } @@ -184,7 +184,8 @@ public class Processing { // TODO(cushon): consider disallowing this, or reporting a diagnostic processor.process(annotations, roundEnv); } catch (Throwable t) { - reportProcessorCrash(log, processor, t); + logProcessorCrash(log, processor, t); + return null; } } } @@ -197,7 +198,7 @@ public class Processing { } errorRaised = log.errorRaised(); if (errorRaised) { - log.maybeThrow(); + break; } log.clear(); result = @@ -228,7 +229,8 @@ public class Processing { try (Timers.Timer unused = timers.start(processor)) { processor.process(ImmutableSet.of(), roundEnv); } catch (Throwable t) { - reportProcessorCrash(log, processor, t); + logProcessorCrash(log, processor, t); + return null; } } @@ -249,7 +251,9 @@ public class Processing { classpath, bootclasspath, moduleVersion); - log.maybeThrow(); + if (log.anyErrors()) { + return null; + } } if (!filer.generatedClasses().isEmpty()) { @@ -267,13 +271,44 @@ public class Processing { return result; } - private static void reportProcessorCrash(TurbineLog log, Processor processor, Throwable t) { + private static ImmutableMap<Processor, SupportedAnnotationTypes> + initializeSupportedAnnotationTypes(ProcessorInfo processorInfo) { + ImmutableMap.Builder<Processor, SupportedAnnotationTypes> result = ImmutableMap.builder(); + for (Processor processor : processorInfo.processors()) { + result.put(processor, SupportedAnnotationTypes.create(processor)); + } + return result.build(); + } + + @AutoValue + abstract static class SupportedAnnotationTypes { + + abstract boolean everything(); + + abstract Pattern pattern(); + + static SupportedAnnotationTypes create(Processor processor) { + List<String> patterns = new ArrayList<>(); + boolean everything = false; + for (String supportedAnnotationType : processor.getSupportedAnnotationTypes()) { + if (supportedAnnotationType.equals("*")) { + everything = true; + } else { + // TODO(b/139026291): this handling of getSupportedAnnotationTypes isn't correct + patterns.add(supportedAnnotationType); + } + } + return new AutoValue_Processing_SupportedAnnotationTypes( + everything, Pattern.compile(Joiner.on('|').join(patterns))); + } + } + + private static void logProcessorCrash(TurbineLog log, Processor processor, Throwable t) { log.diagnostic( Diagnostic.Kind.ERROR, String.format( "An exception occurred in %s:\n%s", processor.getClass().getCanonicalName(), Throwables.getStackTraceAsString(t))); - log.maybeThrow(); } /** Returns a map from annotations present in the compilation to the annotated elements. */ @@ -313,7 +348,7 @@ public class Processing { } // TODO(cushon): consider memoizing this (or isAnnotationInherited) if they show up in profiles - private static Set<ClassSymbol> inheritedAnnotations( + private static ImmutableSet<ClassSymbol> inheritedAnnotations( Set<ClassSymbol> seen, ClassSymbol sym, Env<ClassSymbol, TypeBoundClass> env) { ImmutableSet.Builder<ClassSymbol> result = ImmutableSet.builder(); ClassSymbol curr = sym; @@ -360,87 +395,110 @@ public class Processing { public static ProcessorInfo initializeProcessors( ImmutableList<String> javacopts, - ImmutableList<String> processorPath, ImmutableSet<String> processorNames, - ImmutableSet<String> builtinProcessors) - throws MalformedURLException { - ClassLoader processorLoader = null; + ClassLoader processorLoader) { + if (processorNames.isEmpty() || javacopts.contains("-proc:none")) { + return ProcessorInfo.empty(); + } + ImmutableList<Processor> processors = instantiateProcessors(processorNames, processorLoader); + ImmutableMap<String, String> processorOptions = processorOptions(javacopts); + SourceVersion sourceVersion = parseSourceVersion(javacopts); + return ProcessorInfo.create(processors, processorLoader, processorOptions, sourceVersion); + } + + private static ImmutableList<Processor> instantiateProcessors( + ImmutableSet<String> processorNames, ClassLoader processorLoader) { ImmutableList.Builder<Processor> processors = ImmutableList.builder(); - ImmutableMap<String, String> processorOptions; - if (!processorNames.isEmpty() && !javacopts.contains("-proc:none")) { - if (!processorPath.isEmpty()) { - processorLoader = - new URLClassLoader( - toUrls(processorPath), - new ClassLoader(getPlatformClassLoader()) { - @Override - protected Class<?> findClass(String name) throws ClassNotFoundException { - if (name.startsWith("com.sun.source.") - || name.startsWith("com.sun.tools.") - || name.startsWith("com.google.common.collect.") - || name.startsWith("com.google.common.base.") - || name.startsWith("com.google.common.graph.") - || name.startsWith("com.google.devtools.build.buildjar.javac.statistics.") - || name.startsWith("dagger.model.") - || name.startsWith("dagger.spi.") - || name.equals("com.google.turbine.processing.TurbineProcessingEnvironment") - || builtinProcessors.contains(name)) { - return Class.forName(name); - } - throw new ClassNotFoundException(name); - } - }); - } else { - processorLoader = Processing.class.getClassLoader(); - } - for (String processor : processorNames) { - try { - Class<? extends Processor> clazz = - Class.forName(processor, false, processorLoader).asSubclass(Processor.class); - processors.add(clazz.getConstructor().newInstance()); - } catch (ReflectiveOperationException e) { - throw new LinkageError(e.getMessage(), e); - } + for (String processor : processorNames) { + try { + Class<? extends Processor> clazz = + Class.forName(processor, false, processorLoader).asSubclass(Processor.class); + processors.add(clazz.getConstructor().newInstance()); + } catch (ReflectiveOperationException e) { + throw new LinkageError(e.getMessage(), e); } - processorOptions = processorOptions(javacopts); - } else { - processorOptions = ImmutableMap.of(); } + return processors.build(); + } + + public static ClassLoader processorLoader( + ImmutableList<String> processorPath, ImmutableSet<String> builtinProcessors) + throws MalformedURLException { + if (processorPath.isEmpty()) { + return Processing.class.getClassLoader(); + } + return new URLClassLoader( + toUrls(processorPath), + new ClassLoader(getPlatformClassLoader()) { + @Override + protected Class<?> findClass(String name) throws ClassNotFoundException { + if (name.equals("com.google.turbine.processing.TurbineProcessingEnvironment")) { + return Class.forName(name); + } + if (!builtinProcessors.isEmpty()) { + if (name.startsWith("com.sun.source.") + || name.startsWith("com.sun.tools.") + || name.startsWith("com.google.common.collect.") + || name.startsWith("com.google.common.base.") + || name.startsWith("com.google.common.graph.") + || name.startsWith("com.google.devtools.build.buildjar.javac.statistics.") + || name.startsWith("dagger.model.") + || name.startsWith("dagger.spi.") + || builtinProcessors.contains(name)) { + return Class.forName(name); + } + } + throw new ClassNotFoundException(name); + } + }); + } + + @VisibleForTesting + static SourceVersion parseSourceVersion(ImmutableList<String> javacopts) { SourceVersion sourceVersion = SourceVersion.latestSupported(); Iterator<String> it = javacopts.iterator(); while (it.hasNext()) { String option = it.next(); switch (option) { - case "-target": - if (it.hasNext()) { - String value = it.next(); - switch (value) { - case "5": - case "1.5": - sourceVersion = SourceVersion.RELEASE_5; - break; - case "6": - case "1.6": - sourceVersion = SourceVersion.RELEASE_6; - break; - case "7": - case "1.7": - sourceVersion = SourceVersion.RELEASE_7; - break; - case "8": - sourceVersion = SourceVersion.RELEASE_8; - break; - default: - break; - } + case "-source": + if (!it.hasNext()) { + throw new IllegalArgumentException("-source requires an argument"); } + sourceVersion = parseSourceVersion(it.next()); break; default: break; } } - return ProcessorInfo.create( - processors.build(), processorLoader, processorOptions, sourceVersion); + return sourceVersion; + } + + private static SourceVersion parseSourceVersion(String value) { + boolean hasPrefix = value.startsWith("1."); + Integer version = Ints.tryParse(hasPrefix ? value.substring("1.".length()) : value); + if (!isValidSourceVersion(version, hasPrefix)) { + throw new IllegalArgumentException("invalid -source version: " + value); + } + try { + return SourceVersion.valueOf("RELEASE_" + version); + } catch (IllegalArgumentException unused) { + throw new IllegalArgumentException("invalid -source version: " + value); + } + } + + private static boolean isValidSourceVersion(Integer version, boolean hasPrefix) { + if (version == null) { + return false; + } + if (version < 5) { + // the earliest source version supported by JDK 8 is Java 5 + return false; + } + if (hasPrefix && version > 10) { + // javac supports legacy `1.*` version numbers for source versions up to Java 10 + return false; + } + return true; } private static URL[] toUrls(ImmutableList<String> processorPath) throws MalformedURLException { @@ -548,9 +606,12 @@ public class Processing { ImmutableMap<String, Duration> build() { ImmutableMap.Builder<String, Duration> result = ImmutableMap.builder(); for (Map.Entry<Class<?>, Stopwatch> e : processorTimers.entrySet()) { - result.put(e.getKey().getCanonicalName(), e.getValue().elapsed()); + // requireNonNull is safe, barring bizarre processor implementations (e.g., anonymous class) + result.put(requireNonNull(e.getKey().getCanonicalName()), e.getValue().elapsed()); } return result.build(); } } + + private Processing() {} } diff --git a/java/com/google/turbine/binder/Resolve.java b/java/com/google/turbine/binder/Resolve.java index 28a8be3..66e1036 100644 --- a/java/com/google/turbine/binder/Resolve.java +++ b/java/com/google/turbine/binder/Resolve.java @@ -33,7 +33,7 @@ import java.util.Objects; import java.util.Set; /** Qualified name resolution. */ -public class Resolve { +public final class Resolve { /** * Performs JLS 6.5.5.2 qualified type name resolution of a type with the given simple name, @@ -213,4 +213,6 @@ public class Resolve { } throw new AssertionError(visibility); } + + private Resolve() {} } diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java index 7b01856..a28acd9 100644 --- a/java/com/google/turbine/binder/TypeBinder.java +++ b/java/com/google/turbine/binder/TypeBinder.java @@ -16,6 +16,8 @@ package com.google.turbine.binder; +import static java.util.Objects.requireNonNull; + import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -54,6 +56,7 @@ import com.google.turbine.tree.TurbineModifier; import com.google.turbine.type.AnnoInfo; import com.google.turbine.type.Type; import com.google.turbine.type.Type.IntersectionTy; +import com.google.turbine.types.Deannotate; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashSet; @@ -394,7 +397,8 @@ public class TypeBinder { ImmutableList<Tree.TyParam> trees, CompoundScope scope, Map<String, TyVarSymbol> symbols) { ImmutableMap.Builder<TyVarSymbol, TyVarInfo> result = ImmutableMap.builder(); for (Tree.TyParam tree : trees) { - TyVarSymbol sym = symbols.get(tree.name().value()); + // `symbols` is constructed to guarantee the requireNonNull call is safe. + TyVarSymbol sym = requireNonNull(symbols.get(tree.name().value())); ImmutableList.Builder<Type> bounds = ImmutableList.builder(); for (Tree bound : tree.bounds()) { bounds.add(bindTy(scope, bound)); @@ -493,6 +497,9 @@ public class TypeBinder { == 0) { access |= TurbineFlag.ACC_ABSTRACT; } + if ((access & TurbineFlag.ACC_FINAL) == TurbineFlag.ACC_FINAL) { + log.error(t.position(), ErrorKind.UNEXPECTED_MODIFIER, TurbineModifier.FINAL); + } break; case ENUM: if (name.equals("<init>")) { @@ -618,7 +625,14 @@ public class TypeBinder { case WILD_TY: return bindWildTy(scope, (Tree.WildTy) ty); default: - return bindTy(scope, ty); + Type result = bindTy(scope, ty); + if (result.tyKind().equals(Type.TyKind.PRIM_TY)) { + // Omit type annotations when printing the type in the diagnostic, since they're + // irrelevant and could be invalid if there were deferred errors. + // TODO(cushon): consider ensuring this is done for all diagnostics that mention types + log.error(ty.position(), ErrorKind.UNEXPECTED_TYPE, Deannotate.deannotate(result)); + } + return result; } } diff --git a/java/com/google/turbine/binder/bound/AnnotationMetadata.java b/java/com/google/turbine/binder/bound/AnnotationMetadata.java index 31860b6..a4d3037 100644 --- a/java/com/google/turbine/binder/bound/AnnotationMetadata.java +++ b/java/com/google/turbine/binder/bound/AnnotationMetadata.java @@ -25,7 +25,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.EnumSet; /** - * Annotation metadata, e.g. from {@link @java.lang.annotation.Target}, {@link + * Annotation metadata, e.g. from {@link java.lang.annotation.Target}, {@link * java.lang.annotation.Retention}, and {@link java.lang.annotation.Repeatable}. */ public class AnnotationMetadata { diff --git a/java/com/google/turbine/binder/bound/HeaderBoundClass.java b/java/com/google/turbine/binder/bound/HeaderBoundClass.java index 14807bb..7aeb3d8 100644 --- a/java/com/google/turbine/binder/bound/HeaderBoundClass.java +++ b/java/com/google/turbine/binder/bound/HeaderBoundClass.java @@ -30,5 +30,5 @@ public interface HeaderBoundClass extends BoundClass { ImmutableList<ClassSymbol> interfaces(); /** Declared type parameters. */ - public ImmutableMap<String, TyVarSymbol> typeParameters(); + ImmutableMap<String, TyVarSymbol> typeParameters(); } diff --git a/java/com/google/turbine/binder/bound/TypeBoundClass.java b/java/com/google/turbine/binder/bound/TypeBoundClass.java index e8933ac..99d15bb 100644 --- a/java/com/google/turbine/binder/bound/TypeBoundClass.java +++ b/java/com/google/turbine/binder/bound/TypeBoundClass.java @@ -51,7 +51,7 @@ public interface TypeBoundClass extends HeaderBoundClass { ImmutableList<MethodInfo> methods(); /** - * Annotation metadata, e.g. from {@link @java.lang.annotation.Target}, {@link + * Annotation metadata, e.g. from {@link java.lang.annotation.Target}, {@link * java.lang.annotation.Retention}, and {@link java.lang.annotation.Repeatable}. */ AnnotationMetadata annotationMetadata(); @@ -229,6 +229,14 @@ public interface TypeBoundClass extends HeaderBoundClass { return defaultValue; } + /** + * Returns true for annotation members with a default value. The default value may not have been + * bound yet, in which case {@link #defaultValue} may still return {@code null}. + */ + public boolean hasDefaultValue() { + return decl() != null ? decl().defaultValue().isPresent() : defaultValue() != null; + } + /** The declaration. */ public MethDecl decl() { return decl; diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java index 66d4cf0..0f4bac1 100644 --- a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java +++ b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java @@ -49,7 +49,7 @@ import java.util.function.Function; import java.util.function.Supplier; /** Bind {@link Type}s from bytecode. */ -public class BytecodeBinder { +public final class BytecodeBinder { static Type.ClassTy bindClassTy(Sig.ClassTySig sig, Function<String, TyVarSymbol> scope) { StringBuilder sb = new StringBuilder(sig.pkg()); @@ -212,4 +212,6 @@ public class BytecodeBinder { /* uses= */ ImmutableList.of(), /* provides= */ ImmutableList.of()); } + + private BytecodeBinder() {} } diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java index b992643..82cefc1 100644 --- a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java +++ b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java @@ -18,6 +18,7 @@ package com.google.turbine.binder.bytecode; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Verify.verify; +import static java.util.Objects.requireNonNull; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; @@ -25,8 +26,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.turbine.binder.bound.AnnotationMetadata; -import com.google.turbine.binder.bound.BoundClass; -import com.google.turbine.binder.bound.HeaderBoundClass; import com.google.turbine.binder.bound.TypeBoundClass; import com.google.turbine.binder.env.Env; import com.google.turbine.binder.sym.ClassSymbol; @@ -69,18 +68,18 @@ import org.checkerframework.checker.nullness.qual.Nullable; * resolved and canonicalized so there are no cycles. The laziness also minimizes the amount of work * done on the classpath. */ -public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBoundClass { +public class BytecodeBoundClass implements TypeBoundClass { private final ClassSymbol sym; private final Env<ClassSymbol, BytecodeBoundClass> env; private final Supplier<ClassFile> classFile; - private final String jarFile; + private final @Nullable String jarFile; public BytecodeBoundClass( ClassSymbol sym, Supplier<byte[]> bytes, Env<ClassSymbol, BytecodeBoundClass> env, - String jarFile) { + @Nullable String jarFile) { this.sym = sym; this.env = env; this.jarFile = jarFile; @@ -124,11 +123,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return kind.get(); } - private final Supplier<ClassSymbol> owner = + private final Supplier<@Nullable ClassSymbol> owner = Suppliers.memoize( - new Supplier<ClassSymbol>() { + new Supplier<@Nullable ClassSymbol>() { @Override - public ClassSymbol get() { + public @Nullable ClassSymbol get() { for (ClassFile.InnerClass inner : classFile.get().innerClasses()) { if (sym.binaryName().equals(inner.innerClass())) { return new ClassSymbol(inner.outerClass()); @@ -188,11 +187,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return access.get(); } - private final Supplier<ClassSig> sig = + private final Supplier<@Nullable ClassSig> sig = Suppliers.memoize( - new Supplier<ClassSig>() { + new Supplier<@Nullable ClassSig>() { @Override - public ClassSig get() { + public @Nullable ClassSig get() { String signature = classFile.get().signature(); if (signature == null) { return null; @@ -223,11 +222,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return tyParams.get(); } - private final Supplier<ClassSymbol> superclass = + private final Supplier<@Nullable ClassSymbol> superclass = Suppliers.memoize( - new Supplier<ClassSymbol>() { + new Supplier<@Nullable ClassSymbol>() { @Override - public ClassSymbol get() { + public @Nullable ClassSymbol get() { String superclass = classFile.get().superName(); if (superclass == null) { return null; @@ -237,7 +236,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou }); @Override - public ClassSymbol superclass() { + public @Nullable ClassSymbol superclass() { return superclass.get(); } @@ -259,11 +258,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return interfaces.get(); } - private final Supplier<ClassTy> superClassType = + private final Supplier<@Nullable ClassTy> superClassType = Suppliers.memoize( - new Supplier<ClassTy>() { + new Supplier<@Nullable ClassTy>() { @Override - public ClassTy get() { + public @Nullable ClassTy get() { if (superclass() == null) { return null; } @@ -276,7 +275,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou }); @Override - public ClassTy superClassType() { + public @Nullable ClassTy superClassType() { return superClassType.get(); } @@ -319,7 +318,8 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou ImmutableMap.Builder<TyVarSymbol, TyVarInfo> tparams = ImmutableMap.builder(); Function<String, TyVarSymbol> scope = makeScope(env, sym, typeParameters()); for (Sig.TyParamSig p : sig.get().tyParams()) { - tparams.put(typeParameters().get(p.name()), bindTyParam(p, scope)); + // typeParameters() is constructed to guarantee the requireNonNull call is safe. + tparams.put(requireNonNull(typeParameters().get(p.name())), bindTyParam(p, scope)); } return tparams.build(); } @@ -380,14 +380,19 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou public ImmutableList<MethodInfo> get() { ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder(); int idx = 0; - for (ClassFile.MethodInfo m : classFile.get().methods()) { - methods.add(bindMethod(idx++, m)); + ClassFile cf = classFile.get(); + for (ClassFile.MethodInfo m : cf.methods()) { + if (m.name().equals("<clinit>")) { + // Don't bother reading class initializers, which we don't need + continue; + } + methods.add(bindMethod(cf, idx++, m)); } return methods.build(); } }); - private MethodInfo bindMethod(int methodIdx, ClassFile.MethodInfo m) { + private MethodInfo bindMethod(ClassFile classFile, int methodIdx, ClassFile.MethodInfo m) { MethodSymbol methodSymbol = new MethodSymbol(methodIdx, sym, m.name()); Sig.MethodSig sig = new SigParser(firstNonNull(m.signature(), m.descriptor())).parseMethodSig(); @@ -405,7 +410,8 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou ImmutableMap.Builder<TyVarSymbol, TyVarInfo> tparams = ImmutableMap.builder(); Function<String, TyVarSymbol> scope = makeScope(env, sym, tyParams); for (Sig.TyParamSig p : sig.tyParams()) { - tparams.put(tyParams.get(p.name()), bindTyParam(p, scope)); + // tyParams is constructed to guarantee the requireNonNull call is safe. + tparams.put(requireNonNull(tyParams.get(p.name())), bindTyParam(p, scope)); } tyParamTypes = tparams.build(); } @@ -460,13 +466,19 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou ImmutableList<AnnoInfo> annotations = BytecodeBinder.bindAnnotations(m.annotations()); + int access = m.access(); + if (((classFile.access() & TurbineFlag.ACC_INTERFACE) == TurbineFlag.ACC_INTERFACE) + && (access & (TurbineFlag.ACC_ABSTRACT | TurbineFlag.ACC_STATIC)) == 0) { + access |= TurbineFlag.ACC_DEFAULT; + } + return new MethodInfo( methodSymbol, tyParamTypes, ret, formals.build(), exceptions.build(), - m.access(), + access, defaultValue, /* decl= */ null, annotations, @@ -478,11 +490,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return methods.get(); } - private final Supplier<AnnotationMetadata> annotationMetadata = + private final Supplier<@Nullable AnnotationMetadata> annotationMetadata = Suppliers.memoize( - new Supplier<AnnotationMetadata>() { + new Supplier<@Nullable AnnotationMetadata>() { @Override - public AnnotationMetadata get() { + public @Nullable AnnotationMetadata get() { if ((access() & TurbineFlag.ACC_ANNOTATION) != TurbineFlag.ACC_ANNOTATION) { return null; } @@ -508,8 +520,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou } }); - private static RetentionPolicy bindRetention(AnnotationInfo annotation) { + private static @Nullable RetentionPolicy bindRetention(AnnotationInfo annotation) { ElementValue val = annotation.elementValuePairs().get("value"); + if (val == null) { + return null; + } if (val.kind() != Kind.ENUM) { return null; } @@ -523,6 +538,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou private static ImmutableSet<TurbineElementType> bindTarget(AnnotationInfo annotation) { ImmutableSet.Builder<TurbineElementType> result = ImmutableSet.builder(); ElementValue val = annotation.elementValuePairs().get("value"); + requireNonNull(val); switch (val.kind()) { case ARRAY: for (ElementValue element : ((ArrayValue) val).elements()) { @@ -547,8 +563,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou } } - private static ClassSymbol bindRepeatable(AnnotationInfo annotation) { + private static @Nullable ClassSymbol bindRepeatable(AnnotationInfo annotation) { ElementValue val = annotation.elementValuePairs().get("value"); + if (val == null) { + return null; + } switch (val.kind()) { case CLASS: String className = ((ConstTurbineClassValue) val).className(); @@ -560,7 +579,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou } @Override - public AnnotationMetadata annotationMetadata() { + public @Nullable AnnotationMetadata annotationMetadata() { return annotationMetadata.get(); } @@ -611,7 +630,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou } /** The jar file the symbol was loaded from. */ - public String jarFile() { + public @Nullable String jarFile() { + String transitiveJar = classFile.get().transitiveJar(); + if (transitiveJar != null) { + return transitiveJar; + } return jarFile; } diff --git a/java/com/google/turbine/binder/bytecode/package-info.java b/java/com/google/turbine/binder/bytecode/package-info.java new file mode 100644 index 0000000..23c59f0 --- /dev/null +++ b/java/com/google/turbine/binder/bytecode/package-info.java @@ -0,0 +1,17 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.turbine.binder.bytecode; diff --git a/java/com/google/turbine/binder/env/Env.java b/java/com/google/turbine/binder/env/Env.java index 6ee38a4..a78d3e6 100644 --- a/java/com/google/turbine/binder/env/Env.java +++ b/java/com/google/turbine/binder/env/Env.java @@ -20,10 +20,10 @@ import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.Symbol; /** - * An environment that maps {@link Symbols} {@code S} to bound nodes {@code V}. + * An environment that maps {@link Symbol}s {@code S} to bound nodes {@code V}. * - * <p>For example, {@link BoundClass} represents superclasses as a {@link ClassSymbol}, which only - * contains the binary name of the type. To get the {@link BoundClass} for that supertype, an {@code + * <p>For example, {@code BoundClass} represents superclasses as a {@link ClassSymbol}, which only + * contains the binary name of the type. To get the {@code BoundClass} for that supertype, an {@code * Env<BoundClass>} is used. * * <p>The indirection through env makes it possible to represent a graph with cycles using immutable diff --git a/java/com/google/turbine/binder/env/LazyEnv.java b/java/com/google/turbine/binder/env/LazyEnv.java index 9e8afd5..a9c3bd1 100644 --- a/java/com/google/turbine/binder/env/LazyEnv.java +++ b/java/com/google/turbine/binder/env/LazyEnv.java @@ -27,17 +27,17 @@ import java.util.Map; * An env that permits an analysis pass to access information about symbols from the current pass, * recursively. Cycles are detected, and result in an {@link LazyBindingError} being thrown. * - * <p>This is used primarily for resolving the supertype hierarchy in {@link HierarchyBinder}. The - * supertype hierarchy forms a directed acyclic graph, and {@link HierarchyBinder} needs to process + * <p>This is used primarily for resolving the supertype hierarchy in {@code HierarchyBinder}. The + * supertype hierarchy forms a directed acyclic graph, and {@code HierarchyBinder} needs to process * classes in a topological sort order of that graph. Unfortuntately, we can't produce a suitable * sort order until the graph exists. * * @param <T> the interface type of the bound node {@link V}, shared by any underlying environments. - * @param <V> a specific implementation of {@code T}. For example, during hierarchy binding {@link + * @param <V> a specific implementation of {@code T}. For example, during hierarchy binding {@code * SourceHeaderBoundClass} nodes are being completed from the sources being compiled, and the - * analysis of a given symbol may require looking up {@link HeaderBoundClass} nodes that will - * either be backed by other {@link SourceHeaderBoundClass} nodes or {@link BytecodeBoundClass} - * nodes. So the phase uses an {@link LazyEnv<HeaderBoundClass, SourceHeaderBoundClass>}. + * analysis of a given symbol may require looking up {@code HeaderBoundClass} nodes that will + * either be backed by other {@code SourceHeaderBoundClass} nodes or {@code BytecodeBoundClass} + * nodes. So the phase uses an {@code LazyEnv<HeaderBoundClass, SourceHeaderBoundClass>}. */ public class LazyEnv<S extends Symbol, T, V extends T> implements Env<S, V> { diff --git a/java/com/google/turbine/bytecode/AnnotationWriter.java b/java/com/google/turbine/bytecode/AnnotationWriter.java index b547971..34d6262 100644 --- a/java/com/google/turbine/bytecode/AnnotationWriter.java +++ b/java/com/google/turbine/bytecode/AnnotationWriter.java @@ -34,7 +34,7 @@ import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.TypeParameterBou import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.TypeParameterTarget; import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.TypePath; import com.google.turbine.model.Const.Value; -import java.util.Map.Entry; +import java.util.Map; /** Writes an {@link AnnotationInfo} to a class file. */ public class AnnotationWriter { @@ -50,7 +50,7 @@ public class AnnotationWriter { public void writeAnnotation(AnnotationInfo annotation) { output.writeShort(pool.utf8(annotation.typeName())); output.writeShort(annotation.elementValuePairs().size()); - for (Entry<String, ElementValue> entry : annotation.elementValuePairs().entrySet()) { + for (Map.Entry<String, ElementValue> entry : annotation.elementValuePairs().entrySet()) { output.writeShort(pool.utf8(entry.getKey())); writeElementValue(entry.getValue()); } diff --git a/java/com/google/turbine/bytecode/Attribute.java b/java/com/google/turbine/bytecode/Attribute.java index 29efb60..7b415a7 100644 --- a/java/com/google/turbine/bytecode/Attribute.java +++ b/java/com/google/turbine/bytecode/Attribute.java @@ -41,7 +41,8 @@ interface Attribute { RUNTIME_VISIBLE_TYPE_ANNOTATIONS("RuntimeVisibleTypeAnnotations"), RUNTIME_INVISIBLE_TYPE_ANNOTATIONS("RuntimeInvisibleTypeAnnotations"), METHOD_PARAMETERS("MethodParameters"), - MODULE("Module"); + MODULE("Module"), + TURBINE_TRANSITIVE_JAR("TurbineTransitiveJar"); private final String signature; @@ -309,4 +310,19 @@ interface Attribute { return module; } } + + /** A custom attribute for recording the original jar of repackaged transitive classes. */ + class TurbineTransitiveJar implements Attribute { + + final String transitiveJar; + + public TurbineTransitiveJar(String transitiveJar) { + this.transitiveJar = transitiveJar; + } + + @Override + public Kind kind() { + return Kind.TURBINE_TRANSITIVE_JAR; + } + } } diff --git a/java/com/google/turbine/bytecode/AttributeWriter.java b/java/com/google/turbine/bytecode/AttributeWriter.java index c5ffd16..84ca55f 100644 --- a/java/com/google/turbine/bytecode/AttributeWriter.java +++ b/java/com/google/turbine/bytecode/AttributeWriter.java @@ -24,6 +24,7 @@ import com.google.turbine.bytecode.Attribute.ExceptionsAttribute; import com.google.turbine.bytecode.Attribute.InnerClasses; import com.google.turbine.bytecode.Attribute.MethodParameters; import com.google.turbine.bytecode.Attribute.Signature; +import com.google.turbine.bytecode.Attribute.TurbineTransitiveJar; import com.google.turbine.bytecode.Attribute.TypeAnnotations; import com.google.turbine.bytecode.ClassFile.AnnotationInfo; import com.google.turbine.bytecode.ClassFile.MethodInfo.ParameterInfo; @@ -87,6 +88,9 @@ public class AttributeWriter { case MODULE: writeModule((Attribute.Module) attribute); break; + case TURBINE_TRANSITIVE_JAR: + writeTurbineTransitiveJar((Attribute.TurbineTransitiveJar) attribute); + break; } } @@ -266,4 +270,10 @@ public class AttributeWriter { output.writeInt(data.length); output.write(data); } + + private void writeTurbineTransitiveJar(TurbineTransitiveJar attribute) { + output.writeShort(pool.utf8(attribute.kind().signature())); + output.writeInt(2); + output.writeShort(pool.utf8(attribute.transitiveJar)); + } } diff --git a/java/com/google/turbine/bytecode/ClassFile.java b/java/com/google/turbine/bytecode/ClassFile.java index 8ee2aac..e979edc 100644 --- a/java/com/google/turbine/bytecode/ClassFile.java +++ b/java/com/google/turbine/bytecode/ClassFile.java @@ -42,6 +42,7 @@ public class ClassFile { private final List<InnerClass> innerClasses; private final ImmutableList<TypeAnnotationInfo> typeAnnotations; @Nullable private final ModuleInfo module; + @Nullable private final String transitiveJar; public ClassFile( int access, @@ -54,7 +55,8 @@ public class ClassFile { List<AnnotationInfo> annotations, List<InnerClass> innerClasses, ImmutableList<TypeAnnotationInfo> typeAnnotations, - @Nullable ModuleInfo module) { + @Nullable ModuleInfo module, + @Nullable String transitiveJar) { this.access = access; this.name = name; this.signature = signature; @@ -66,6 +68,7 @@ public class ClassFile { this.innerClasses = innerClasses; this.typeAnnotations = typeAnnotations; this.module = module; + this.transitiveJar = transitiveJar; } /** Class access and property flags. */ @@ -124,6 +127,12 @@ public class ClassFile { return module; } + /** The original jar of a repackaged transitive class. */ + @Nullable + public String transitiveJar() { + return transitiveJar; + } + /** The contents of a JVMS §4.5 field_info structure. */ public static class FieldInfo { diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java index 9c79b42..ac8b1e1 100644 --- a/java/com/google/turbine/bytecode/ClassReader.java +++ b/java/com/google/turbine/bytecode/ClassReader.java @@ -106,6 +106,7 @@ public class ClassReader { List<ClassFile.InnerClass> innerclasses = ImmutableList.of(); ImmutableList.Builder<ClassFile.AnnotationInfo> annotations = ImmutableList.builder(); ClassFile.ModuleInfo module = null; + String transitiveJar = null; int attributesCount = reader.u2(); for (int j = 0; j < attributesCount; j++) { int attributeNameIndex = reader.u2(); @@ -124,6 +125,9 @@ public class ClassReader { case "Module": module = readModule(constantPool); break; + case "TurbineTransitiveJar": + transitiveJar = readTurbineTransitiveJar(constantPool); + break; default: reader.skip(reader.u4()); break; @@ -141,7 +145,8 @@ public class ClassReader { annotations.build(), innerclasses, ImmutableList.of(), - module); + module, + transitiveJar); } /** Reads a JVMS 4.7.9 Signature attribute. */ @@ -509,4 +514,9 @@ public class ClassReader { } return fields; } + + private String readTurbineTransitiveJar(ConstantPoolReader constantPool) { + reader.u4(); // length + return constantPool.utf8(reader.u2()); + } } diff --git a/java/com/google/turbine/bytecode/ClassWriter.java b/java/com/google/turbine/bytecode/ClassWriter.java index c3490ca..de975f2 100644 --- a/java/com/google/turbine/bytecode/ClassWriter.java +++ b/java/com/google/turbine/bytecode/ClassWriter.java @@ -27,7 +27,7 @@ import com.google.turbine.model.Const.Value; import java.util.List; /** Class file writing. */ -public class ClassWriter { +public final class ClassWriter { private static final int MAGIC = 0xcafebabe; private static final int MINOR_VERSION = 0; @@ -124,4 +124,6 @@ public class ClassWriter { result.write(body.toByteArray()); return result.toByteArray(); } + + private ClassWriter() {} } diff --git a/java/com/google/turbine/bytecode/LowerAttributes.java b/java/com/google/turbine/bytecode/LowerAttributes.java index 67ef2b4..5ae42af 100644 --- a/java/com/google/turbine/bytecode/LowerAttributes.java +++ b/java/com/google/turbine/bytecode/LowerAttributes.java @@ -29,7 +29,7 @@ import java.util.ArrayList; import java.util.List; /** Lower information in {@link ClassFile} structures to attributes. */ -public class LowerAttributes { +public final class LowerAttributes { /** Collects the {@link Attribute}s for a {@link ClassFile}. */ static List<Attribute> classAttributes(ClassFile classfile) { @@ -45,6 +45,9 @@ public class LowerAttributes { if (classfile.module() != null) { attributes.add(new Attribute.Module(classfile.module())); } + if (classfile.transitiveJar() != null) { + attributes.add(new Attribute.TurbineTransitiveJar(classfile.transitiveJar())); + } return attributes; } @@ -146,4 +149,6 @@ public class LowerAttributes { attributes.add(new Attribute.RuntimeInvisibleParameterAnnotations(invisibles)); } } + + private LowerAttributes() {} } diff --git a/java/com/google/turbine/bytecode/sig/Sig.java b/java/com/google/turbine/bytecode/sig/Sig.java index e85740f..f759269 100644 --- a/java/com/google/turbine/bytecode/sig/Sig.java +++ b/java/com/google/turbine/bytecode/sig/Sig.java @@ -21,7 +21,7 @@ import com.google.turbine.model.TurbineConstantTypeKind; import org.checkerframework.checker.nullness.qual.Nullable; /** JVMS 4.7.9.1 signatures. */ -public class Sig { +public final class Sig { /** A JVMS 4.7.9.1 ClassSignature. */ public static class ClassSig { @@ -343,4 +343,6 @@ public class Sig { return exceptions; } } + + private Sig() {} } diff --git a/java/com/google/turbine/deps/Dependencies.java b/java/com/google/turbine/deps/Dependencies.java index 92193e8..ef1eea9 100644 --- a/java/com/google/turbine/deps/Dependencies.java +++ b/java/com/google/turbine/deps/Dependencies.java @@ -51,7 +51,7 @@ import java.util.Optional; import java.util.Set; /** Support for Bazel jdeps dependency output. */ -public class Dependencies { +public final class Dependencies { /** Creates a jdeps proto for the current compilation. */ public static DepsProto.Dependencies collectDeps( Optional<String> targetLabel, ClassPath bootclasspath, BindingResult bound, Lowered lowered) { @@ -219,4 +219,6 @@ public class Dependencies { // preserve the order of entries in the transitive classpath return Collections2.filter(transitiveClasspath, Predicates.in(reduced)); } + + private Dependencies() {} } diff --git a/java/com/google/turbine/deps/Transitive.java b/java/com/google/turbine/deps/Transitive.java index 8b0d44d..75d23f6 100644 --- a/java/com/google/turbine/deps/Transitive.java +++ b/java/com/google/turbine/deps/Transitive.java @@ -33,13 +33,14 @@ import com.google.turbine.bytecode.ClassWriter; import com.google.turbine.model.TurbineFlag; import java.util.LinkedHashSet; import java.util.Set; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Collects the minimal compile-time API for symbols in the supertype closure of compiled classes. * This allows header compilations to be performed against a classpath containing only direct * dependencies and no transitive dependencies. */ -public class Transitive { +public final class Transitive { public static ImmutableMap<String, byte[]> collectDeps( ClassPath bootClassPath, BindingResult bound) { @@ -54,7 +55,8 @@ public class Transitive { // don't export symbols loaded from the bootclasspath continue; } - transitive.put(sym.binaryName(), ClassWriter.writeClass(trimClass(info.classFile()))); + transitive.put( + sym.binaryName(), ClassWriter.writeClass(trimClass(info.classFile(), info.jarFile()))); } return transitive.build(); } @@ -62,7 +64,7 @@ public class Transitive { /** * Removes information from repackaged classes that will not be needed by upstream compilations. */ - public static ClassFile trimClass(ClassFile cf) { + public static ClassFile trimClass(ClassFile cf, @Nullable String jarFile) { // drop non-constant fields ImmutableList.Builder<FieldInfo> fields = ImmutableList.builder(); for (FieldInfo f : cf.fields()) { @@ -80,6 +82,12 @@ public class Transitive { innerClasses.add(i); } } + // Include the original jar file name when repackaging transitive deps. If the same transitive + // dep is repackaged more than once, keep the original name. + String transitiveJar = cf.transitiveJar(); + if (transitiveJar == null) { + transitiveJar = jarFile; + } return new ClassFile( cf.access(), cf.name(), @@ -96,7 +104,8 @@ public class Transitive { cf.annotations(), innerClasses.build(), cf.typeAnnotations(), - /* module= */ null); + /* module= */ null, + /* transitiveJar = */ transitiveJar); } private static Set<ClassSymbol> superClosure(BindingResult bound) { @@ -134,4 +143,6 @@ public class Transitive { addSuperTypes(closure, env, i); } } + + private Transitive() {} } diff --git a/java/com/google/turbine/diag/LineMap.java b/java/com/google/turbine/diag/LineMap.java index 7a39aba..37d055b 100644 --- a/java/com/google/turbine/diag/LineMap.java +++ b/java/com/google/turbine/diag/LineMap.java @@ -17,6 +17,7 @@ package com.google.turbine.diag; import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; import com.google.common.collect.ImmutableRangeMap; import com.google.common.collect.Range; @@ -64,19 +65,22 @@ public class LineMap { /** The zero-indexed column number of the given source position. */ public int column(int position) { checkArgument(0 <= position && position < source.length(), "%s", position); - return position - lines.getEntry(position).getKey().lowerEndpoint(); + // requireNonNull is safe because `lines` covers the whole file length. + return position - requireNonNull(lines.getEntry(position)).getKey().lowerEndpoint(); } /** The one-indexed line number of the given source position. */ public int lineNumber(int position) { checkArgument(0 <= position && position < source.length(), "%s", position); - return lines.get(position); + // requireNonNull is safe because `lines` covers the whole file length. + return requireNonNull(lines.get(position)); } /** The one-indexed line of the given source position. */ public String line(int position) { checkArgument(0 <= position && position < source.length(), "%s", position); - Range<Integer> range = lines.getEntry(position).getKey(); + // requireNonNull is safe because `lines` covers the whole file length. + Range<Integer> range = requireNonNull(lines.getEntry(position)).getKey(); return source.substring(range.lowerEndpoint(), range.upperEndpoint()); } } diff --git a/java/com/google/turbine/diag/TurbineDiagnostic.java b/java/com/google/turbine/diag/TurbineDiagnostic.java index ccbaa7f..ed04a5d 100644 --- a/java/com/google/turbine/diag/TurbineDiagnostic.java +++ b/java/com/google/turbine/diag/TurbineDiagnostic.java @@ -64,6 +64,10 @@ public class TurbineDiagnostic { return severity; } + boolean isError() { + return severity.equals(Diagnostic.Kind.ERROR); + } + /** The diagnostic message. */ public String diagnostic() { StringBuilder sb = new StringBuilder(path()); @@ -71,7 +75,7 @@ public class TurbineDiagnostic { sb.append(':').append(line()); } sb.append(": error: "); - sb.append(message().trim()).append(System.lineSeparator()); + sb.append(message()).append(System.lineSeparator()); if (line() != -1 && column() != -1) { sb.append(CharMatcher.breakingWhitespace().trimTrailingFrom(source.lineMap().line(position))) .append(System.lineSeparator()); diff --git a/java/com/google/turbine/diag/TurbineError.java b/java/com/google/turbine/diag/TurbineError.java index 39244b5..f3ebf08 100644 --- a/java/com/google/turbine/diag/TurbineError.java +++ b/java/com/google/turbine/diag/TurbineError.java @@ -26,15 +26,17 @@ public class TurbineError extends Error { /** A diagnostic kind. */ public enum ErrorKind { - UNEXPECTED_INPUT("unexpected input: %c"), + UNEXPECTED_INPUT("unexpected input: %s"), UNEXPECTED_IDENTIFIER("unexpected identifier '%s'"), UNEXPECTED_EOF("unexpected end of input"), UNTERMINATED_STRING("unterminated string literal"), UNTERMINATED_CHARACTER_LITERAL("unterminated char literal"), + UNPAIRED_SURROGATE("unpaired surrogate 0x%x"), UNTERMINATED_EXPRESSION("unterminated expression, expected ';' not found"), INVALID_UNICODE("illegal unicode escape"), EMPTY_CHARACTER_LITERAL("empty char literal"), EXPECTED_TOKEN("expected token %s"), + EXTENDS_AFTER_IMPLEMENTS("'extends' must come before 'implements'"), INVALID_LITERAL("invalid literal: %s"), UNEXPECTED_TYPE_PARAMETER("unexpected type parameter %s"), SYMBOL_NOT_FOUND("symbol not found %s"), @@ -42,6 +44,7 @@ public class TurbineError extends Error { TYPE_PARAMETER_QUALIFIER("type parameter used as type qualifier"), UNEXPECTED_TOKEN("unexpected token: %s"), INVALID_ANNOTATION_ARGUMENT("invalid annotation argument"), + MISSING_ANNOTATION_ARGUMENT("missing required annotation argument: %s"), CANNOT_RESOLVE("could not resolve %s"), EXPRESSION_ERROR("could not evaluate constant expression"), OPERAND_TYPE("bad operand type %s"), @@ -51,6 +54,8 @@ public class TurbineError extends Error { DUPLICATE_DECLARATION("duplicate declaration of %s"), BAD_MODULE_INFO("unexpected declaration found in module-info"), UNCLOSED_COMMENT("unclosed comment"), + UNEXPECTED_TYPE("unexpected type %s"), + UNEXPECTED_MODIFIER("unexpected modifier: %s"), PROC("%s"); private final String message; diff --git a/java/com/google/turbine/diag/TurbineLog.java b/java/com/google/turbine/diag/TurbineLog.java index b336e25..565b9ea 100644 --- a/java/com/google/turbine/diag/TurbineLog.java +++ b/java/com/google/turbine/diag/TurbineLog.java @@ -18,7 +18,6 @@ package com.google.turbine.diag; import com.google.common.collect.ImmutableList; import com.google.turbine.diag.TurbineError.ErrorKind; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import javax.tools.Diagnostic; @@ -26,20 +25,24 @@ import javax.tools.Diagnostic; /** A log that collects diagnostics. */ public class TurbineLog { - private final Set<TurbineDiagnostic> errors = new LinkedHashSet<>(); + private final Set<TurbineDiagnostic> diagnostics = new LinkedHashSet<>(); public TurbineLogWithSource withSource(SourceFile source) { return new TurbineLogWithSource(source); } + public ImmutableList<TurbineDiagnostic> diagnostics() { + return ImmutableList.copyOf(diagnostics); + } + public void maybeThrow() { if (anyErrors()) { - throw new TurbineError(ImmutableList.copyOf(errors)); + throw new TurbineError(diagnostics()); } } - private boolean anyErrors() { - for (TurbineDiagnostic error : errors) { + public boolean anyErrors() { + for (TurbineDiagnostic error : diagnostics) { if (error.severity().equals(Diagnostic.Kind.ERROR)) { return true; } @@ -55,7 +58,7 @@ public class TurbineLog { * code generated in later processing rounds. */ public boolean errorRaised() { - for (TurbineDiagnostic error : errors) { + for (TurbineDiagnostic error : diagnostics) { if (error.kind().equals(ErrorKind.PROC) && error.severity().equals(Diagnostic.Kind.ERROR)) { return true; } @@ -65,17 +68,12 @@ public class TurbineLog { /** Reset the log between annotation processing rounds. */ public void clear() { - Iterator<TurbineDiagnostic> it = errors.iterator(); - while (it.hasNext()) { - if (it.next().severity().equals(Diagnostic.Kind.ERROR)) { - it.remove(); - } - } + diagnostics.removeIf(TurbineDiagnostic::isError); } /** Reports an annotation processing diagnostic with no position information. */ public void diagnostic(Diagnostic.Kind severity, String message) { - errors.add(TurbineDiagnostic.format(severity, ErrorKind.PROC, message)); + diagnostics.add(TurbineDiagnostic.format(severity, ErrorKind.PROC, message)); } /** A log for a specific source file. */ @@ -88,7 +86,7 @@ public class TurbineLog { } public void diagnostic(Diagnostic.Kind severity, int position, ErrorKind kind, Object... args) { - errors.add(TurbineDiagnostic.format(severity, source, position, kind, args)); + diagnostics.add(TurbineDiagnostic.format(severity, source, position, kind, args)); } public void error(int position, ErrorKind kind, Object... args) { diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java index 0f7bb90..971bbe4 100644 --- a/java/com/google/turbine/lower/Lower.java +++ b/java/com/google/turbine/lower/Lower.java @@ -185,7 +185,8 @@ public class Lower { annotations, innerClasses.build(), /* typeAnnotations= */ ImmutableList.of(), - moduleInfo); + moduleInfo, + /* transitiveJar= */ null); symbols.addAll(sig.classes); return ClassWriter.writeClass(classfile); } @@ -279,7 +280,8 @@ public class Lower { annotations, inners, typeAnnotations, - /* module= */ null); + /* module= */ null, + /* transitiveJar= */ null); symbols.addAll(sig.classes); diff --git a/java/com/google/turbine/lower/LowerSignature.java b/java/com/google/turbine/lower/LowerSignature.java index 13a7b9f..a08c7e8 100644 --- a/java/com/google/turbine/lower/LowerSignature.java +++ b/java/com/google/turbine/lower/LowerSignature.java @@ -128,15 +128,13 @@ public class LowerSignature { * unnecessary. */ public String methodSignature( - Env<ClassSymbol, TypeBoundClass> env, - SourceTypeBoundClass.MethodInfo method, - ClassSymbol sym) { + Env<ClassSymbol, TypeBoundClass> env, TypeBoundClass.MethodInfo method, ClassSymbol sym) { if (!needsMethodSig(sym, env, method)) { return null; } ImmutableList<Sig.TyParamSig> typarams = tyParamSig(method.tyParams(), env); ImmutableList.Builder<Sig.TySig> fparams = ImmutableList.builder(); - for (SourceTypeBoundClass.ParamInfo t : method.parameters()) { + for (TypeBoundClass.ParamInfo t : method.parameters()) { if (t.synthetic()) { continue; } @@ -161,7 +159,7 @@ public class LowerSignature { } private boolean needsMethodSig( - ClassSymbol sym, Env<ClassSymbol, TypeBoundClass> env, SourceTypeBoundClass.MethodInfo m) { + ClassSymbol sym, Env<ClassSymbol, TypeBoundClass> env, TypeBoundClass.MethodInfo m) { if ((env.get(sym).access() & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM && m.name().equals("<init>")) { // JDK-8024694: javac always expects signature attribute for enum constructors @@ -176,7 +174,7 @@ public class LowerSignature { if (m.returnType() != null && needsSig(m.returnType())) { return true; } - for (SourceTypeBoundClass.ParamInfo t : m.parameters()) { + for (TypeBoundClass.ParamInfo t : m.parameters()) { if (t.synthetic()) { continue; } @@ -262,14 +260,14 @@ public class LowerSignature { private ImmutableList<Sig.TyParamSig> tyParamSig( Map<TyVarSymbol, TyVarInfo> px, Env<ClassSymbol, TypeBoundClass> env) { ImmutableList.Builder<Sig.TyParamSig> result = ImmutableList.builder(); - for (Map.Entry<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> entry : px.entrySet()) { + for (Map.Entry<TyVarSymbol, TyVarInfo> entry : px.entrySet()) { result.add(tyParamSig(entry.getKey(), entry.getValue(), env)); } return result.build(); } private Sig.TyParamSig tyParamSig( - TyVarSymbol sym, SourceTypeBoundClass.TyVarInfo info, Env<ClassSymbol, TypeBoundClass> env) { + TyVarSymbol sym, TyVarInfo info, Env<ClassSymbol, TypeBoundClass> env) { String identifier = sym.name(); Sig.TySig cbound = null; diff --git a/java/com/google/turbine/main/Main.java b/java/com/google/turbine/main/Main.java index 1e60ae6..59563b6 100644 --- a/java/com/google/turbine/main/Main.java +++ b/java/com/google/turbine/main/Main.java @@ -70,7 +70,7 @@ import java.util.jar.Manifest; import java.util.zip.ZipEntry; /** Main entry point for the turbine CLI. */ -public class Main { +public final class Main { private static final int BUFFER_SIZE = 65536; @@ -256,9 +256,10 @@ public class Main { ClassPathBinder.bindClasspath(toPaths(classpath)), Processing.initializeProcessors( /* javacopts= */ options.javacOpts(), - /* processorPath= */ options.processorPath(), /* processorNames= */ options.processors(), - /* builtinProcessors= */ options.builtinProcessors()), + Processing.processorLoader( + /* processorPath= */ options.processorPath(), + /* builtinProcessors= */ options.builtinProcessors())), bootclasspath, /* moduleVersion=*/ Optional.empty()); } @@ -332,6 +333,14 @@ public class Main { return; } Path path = Paths.get(options.gensrcOutput().get()); + if (Files.isDirectory(path)) { + for (SourceFile source : generatedSources.values()) { + Path to = path.resolve(source.path()); + Files.createDirectories(to.getParent()); + Files.write(to, source.source().getBytes(UTF_8)); + } + return; + } try (OutputStream os = Files.newOutputStream(path); BufferedOutputStream bos = new BufferedOutputStream(os, BUFFER_SIZE); JarOutputStream jos = new JarOutputStream(bos)) { @@ -349,6 +358,14 @@ public class Main { return; } Path path = Paths.get(options.resourceOutput().get()); + if (Files.isDirectory(path)) { + for (Map.Entry<String, byte[]> resource : generatedResources.entrySet()) { + Path to = path.resolve(resource.getKey()); + Files.createDirectories(to.getParent()); + Files.write(to, resource.getValue()); + } + return; + } try (OutputStream os = Files.newOutputStream(path); BufferedOutputStream bos = new BufferedOutputStream(os, BUFFER_SIZE); JarOutputStream jos = new JarOutputStream(bos)) { @@ -465,4 +482,6 @@ public class Main { } return result.build(); } + + private Main() {} } diff --git a/java/com/google/turbine/model/TurbineFlag.java b/java/com/google/turbine/model/TurbineFlag.java index 48e88e7..c138d46 100644 --- a/java/com/google/turbine/model/TurbineFlag.java +++ b/java/com/google/turbine/model/TurbineFlag.java @@ -22,7 +22,7 @@ package com.google.turbine.model; * <p>See tables 4.1-A, 4.5-A, 4.6-A, and 4.7.6-A in JVMS 4: * https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html */ -public class TurbineFlag { +public final class TurbineFlag { public static final int ACC_PUBLIC = 0x0001; public static final int ACC_PRIVATE = 0x0002; public static final int ACC_PROTECTED = 0x0004; @@ -54,4 +54,6 @@ public class TurbineFlag { /** Synthetic constructors (e.g. of inner classes and enums). */ public static final int ACC_SYNTH_CTOR = 1 << 18; + + private TurbineFlag() {} } diff --git a/java/com/google/turbine/options/TurbineOptions.java b/java/com/google/turbine/options/TurbineOptions.java index 4dcc408..c104c54 100644 --- a/java/com/google/turbine/options/TurbineOptions.java +++ b/java/com/google/turbine/options/TurbineOptions.java @@ -70,17 +70,6 @@ public abstract class TurbineOptions { /** The output jar. */ public abstract Optional<String> output(); - /** - * The output jar. - * - * @deprecated use {@link #output} instead. - */ - @Deprecated - @Nullable - public String outputFile() { - return output().orElse(null); - } - /** Paths to annotation processor artifacts. */ public abstract ImmutableList<String> processorPath(); @@ -160,56 +149,20 @@ public abstract class TurbineOptions { public abstract static class Builder { public abstract Builder setOutput(String output); - /** @deprecated use {@link #setClassPath(ImmutableList)} instead. */ - @Deprecated - public Builder addClassPathEntries(Iterable<String> sources) { - return setClassPath(ImmutableList.copyOf(sources)); - } - public abstract Builder setClassPath(ImmutableList<String> classPath); public abstract Builder setBootClassPath(ImmutableList<String> bootClassPath); - /** @deprecated use {@link #setBootClassPath(ImmutableList)} instead. */ - @Deprecated - public Builder addBootClassPathEntries(Iterable<String> sources) { - return setBootClassPath(ImmutableList.copyOf(sources)); - } - public abstract Builder setRelease(String release); public abstract Builder setSystem(String system); public abstract Builder setSources(ImmutableList<String> sources); - /** @deprecated use {@link #setSources(ImmutableList)} instead. */ - @Deprecated - public Builder addSources(Iterable<String> sources) { - return setSources(ImmutableList.copyOf(sources)); - } - - /** @deprecated use {@link #setProcessorPath(ImmutableList)} instead. */ - @Deprecated - public Builder addProcessorPathEntries(Iterable<String> processorPath) { - return setProcessorPath(ImmutableList.copyOf(processorPath)); - } - public abstract Builder setProcessorPath(ImmutableList<String> processorPath); - /** @deprecated use {@link #setProcessors(ImmutableList)} instead. */ - @Deprecated - public Builder addProcessors(Iterable<String> processors) { - return setProcessors(ImmutableList.copyOf(processors)); - } - public abstract Builder setProcessors(ImmutableList<String> processors); - /** @deprecated use {@link #setBuiltinProcessors(ImmutableList)} instead. */ - @Deprecated - public Builder addBuiltinProcessors(Iterable<String> builtinProcessors) { - return setBuiltinProcessors(ImmutableList.copyOf(builtinProcessors)); - } - public abstract Builder setBuiltinProcessors(ImmutableList<String> builtinProcessors); public abstract Builder setSourceJars(ImmutableList<String> sourceJars); @@ -222,12 +175,6 @@ public abstract class TurbineOptions { public abstract Builder setInjectingRuleKind(String injectingRuleKind); - /** @deprecated use {@link #setDepsArtifacts(ImmutableList)} instead. */ - @Deprecated - public Builder addAllDepsArtifacts(Iterable<String> depsArtifacts) { - return setDepsArtifacts(ImmutableList.copyOf(depsArtifacts)); - } - public abstract Builder setDepsArtifacts(ImmutableList<String> depsArtifacts); public abstract Builder setHelp(boolean help); @@ -241,12 +188,6 @@ public abstract class TurbineOptions { public abstract Builder setReducedClasspathMode(ReducedClasspathMode reducedClasspathMode); - /** @deprecated use {@link #setDirectJars(ImmutableList)} instead. */ - @Deprecated - public Builder addDirectJars(Iterable<String> directJars) { - return setDirectJars(ImmutableList.copyOf(directJars)); - } - public abstract Builder setDirectJars(ImmutableList<String> jars); public abstract Builder setProfile(String profile); @@ -261,4 +202,11 @@ 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/options/TurbineOptionsParser.java b/java/com/google/turbine/options/TurbineOptionsParser.java index 17d4bf6..4a8ff16 100644 --- a/java/com/google/turbine/options/TurbineOptionsParser.java +++ b/java/com/google/turbine/options/TurbineOptionsParser.java @@ -30,10 +30,9 @@ import java.nio.file.Paths; import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; -import org.checkerframework.checker.nullness.qual.Nullable; /** A command line options parser for {@link TurbineOptions}. */ -public class TurbineOptionsParser { +public final class TurbineOptionsParser { /** * Parses command line options into {@link TurbineOptions}, expanding any {@code @params} files. @@ -57,17 +56,17 @@ public class TurbineOptionsParser { private static void parse(TurbineOptions.Builder builder, Deque<String> argumentDeque) { while (!argumentDeque.isEmpty()) { - String next = argumentDeque.pollFirst(); + String next = argumentDeque.removeFirst(); switch (next) { case "--output": - builder.setOutput(readOne(argumentDeque)); + builder.setOutput(readOne(next, argumentDeque)); break; case "--source_jars": builder.setSourceJars(readList(argumentDeque)); break; case "--temp_dir": // TODO(cushon): remove this when Bazel no longer passes the flag - readOne(argumentDeque); + readOne(next, argumentDeque); break; case "--processors": builder.setProcessors(readList(argumentDeque)); @@ -85,10 +84,10 @@ public class TurbineOptionsParser { builder.setBootClassPath(readList(argumentDeque)); break; case "--release": - builder.setRelease(readOne(argumentDeque)); + builder.setRelease(readOne(next, argumentDeque)); break; case "--system": - builder.setSystem(readOne(argumentDeque)); + builder.setSystem(readOne(next, argumentDeque)); break; case "--javacopts": { @@ -100,11 +99,12 @@ public class TurbineOptionsParser { case "--sources": builder.setSources(readList(argumentDeque)); break; + case "--output_deps_proto": case "--output_deps": - builder.setOutputDeps(readOne(argumentDeque)); + builder.setOutputDeps(readOne(next, argumentDeque)); break; case "--output_manifest_proto": - builder.setOutputManifest(readOne(argumentDeque)); + builder.setOutputManifest(readOne(next, argumentDeque)); break; case "--direct_dependencies": builder.setDirectJars(readList(argumentDeque)); @@ -113,10 +113,10 @@ public class TurbineOptionsParser { builder.setDepsArtifacts(readList(argumentDeque)); break; case "--target_label": - builder.setTargetLabel(readOne(argumentDeque)); + builder.setTargetLabel(readOne(next, argumentDeque)); break; case "--injecting_rule_kind": - builder.setInjectingRuleKind(readOne(argumentDeque)); + builder.setInjectingRuleKind(readOne(next, argumentDeque)); break; case "--javac_fallback": case "--nojavac_fallback": @@ -129,26 +129,37 @@ public class TurbineOptionsParser { builder.setReducedClasspathMode(ReducedClasspathMode.NONE); break; case "--reduce_classpath_mode": - builder.setReducedClasspathMode(ReducedClasspathMode.valueOf(readOne(argumentDeque))); + builder.setReducedClasspathMode( + ReducedClasspathMode.valueOf(readOne(next, argumentDeque))); break; case "--full_classpath_length": - builder.setFullClasspathLength(Integer.parseInt(readOne(argumentDeque))); + builder.setFullClasspathLength(Integer.parseInt(readOne(next, argumentDeque))); break; case "--reduced_classpath_length": - builder.setReducedClasspathLength(Integer.parseInt(readOne(argumentDeque))); + builder.setReducedClasspathLength(Integer.parseInt(readOne(next, argumentDeque))); break; case "--profile": - builder.setProfile(readOne(argumentDeque)); + builder.setProfile(readOne(next, argumentDeque)); break; + case "--generated_sources_output": case "--gensrc_output": - builder.setGensrcOutput(readOne(argumentDeque)); + builder.setGensrcOutput(readOne(next, argumentDeque)); break; case "--resource_output": - builder.setResourceOutput(readOne(argumentDeque)); + builder.setResourceOutput(readOne(next, argumentDeque)); break; case "--help": builder.setHelp(true); break; + case "--experimental_fix_deps_tool": + case "--strict_java_deps": + case "--native_header_output": + // accepted (and ignored) for compatibility with JavaBuilder command lines + readOne(next, argumentDeque); + break; + case "--compress_jar": + // accepted (and ignored) for compatibility with JavaBuilder command lines + break; default: throw new IllegalArgumentException("unknown option: " + next); } @@ -190,20 +201,22 @@ public class TurbineOptionsParser { } } - /** Returns the value of an option, or {@code null}. */ - @Nullable - private static String readOne(Deque<String> argumentDeque) { - if (argumentDeque.isEmpty() || argumentDeque.peekFirst().startsWith("-")) { - return null; + /** + * Returns the value of an option, or throws {@link IllegalArgumentException} if the value is not + * present. + */ + private static String readOne(String flag, Deque<String> argumentDeque) { + if (argumentDeque.isEmpty() || argumentDeque.getFirst().startsWith("-")) { + throw new IllegalArgumentException("missing required argument for: " + flag); } - return argumentDeque.pollFirst(); + return argumentDeque.removeFirst(); } /** Returns a list of option values. */ private static ImmutableList<String> readList(Deque<String> argumentDeque) { ImmutableList.Builder<String> result = ImmutableList.builder(); - while (!argumentDeque.isEmpty() && !argumentDeque.peekFirst().startsWith("--")) { - result.add(argumentDeque.pollFirst()); + while (!argumentDeque.isEmpty() && !argumentDeque.getFirst().startsWith("--")) { + result.add(argumentDeque.removeFirst()); } return result.build(); } @@ -215,7 +228,7 @@ public class TurbineOptionsParser { private static ImmutableList<String> readJavacopts(Deque<String> argumentDeque) { ImmutableList.Builder<String> result = ImmutableList.builder(); while (!argumentDeque.isEmpty()) { - String arg = argumentDeque.pollFirst(); + String arg = argumentDeque.removeFirst(); if (arg.equals("--")) { return result.build(); } @@ -237,4 +250,6 @@ public class TurbineOptionsParser { } } } + + private TurbineOptionsParser() {} } diff --git a/java/com/google/turbine/options/package-info.java b/java/com/google/turbine/options/package-info.java new file mode 100644 index 0000000..9c12bf8 --- /dev/null +++ b/java/com/google/turbine/options/package-info.java @@ -0,0 +1,17 @@ +/* + * Copyright 2021 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.turbine.options; diff --git a/java/com/google/turbine/parse/ConstExpressionParser.java b/java/com/google/turbine/parse/ConstExpressionParser.java index e49d51c..ba51814 100644 --- a/java/com/google/turbine/parse/ConstExpressionParser.java +++ b/java/com/google/turbine/parse/ConstExpressionParser.java @@ -506,12 +506,15 @@ public class ConstExpressionParser { return term1; } eat(); - if (op == TurbineOperatorKind.TERNARY) { - term1 = ternary(term1); - } else if (op == TurbineOperatorKind.ASSIGN) { - term1 = assign(term1, op); - } else { - term1 = new Tree.Binary(position, term1, expression(op.prec()), op); + switch (op) { + case TERNARY: + term1 = ternary(term1); + break; + case ASSIGN: + term1 = assign(term1, op); + break; + default: + term1 = new Tree.Binary(position, term1, expression(op.prec()), op); } if (term1 == null) { return null; @@ -568,6 +571,7 @@ public class ConstExpressionParser { throw new AssertionError(); } eat(); + int pos = position; Tree.ConstVarName constVarName = (Tree.ConstVarName) qualIdent(); if (constVarName == null) { return null; @@ -577,10 +581,10 @@ public class ConstExpressionParser { if (token == Token.LPAREN) { eat(); while (token != Token.RPAREN) { - int pos = position; + int argPos = position; Tree.Expression expression = expression(); if (expression == null) { - throw TurbineError.format(lexer.source(), pos, ErrorKind.INVALID_ANNOTATION_ARGUMENT); + throw TurbineError.format(lexer.source(), argPos, ErrorKind.INVALID_ANNOTATION_ARGUMENT); } args.add(expression); if (token != Token.COMMA) { @@ -592,11 +596,19 @@ public class ConstExpressionParser { eat(); } } - return new Tree.AnnoExpr(position, new Tree.Anno(position, name, args.build())); + return new Tree.AnnoExpr(pos, new Tree.Anno(pos, name, args.build())); } @CheckReturnValue private TurbineError error(ErrorKind kind, Object... args) { return TurbineError.format(lexer.source(), lexer.position(), kind, args); } + + public int f() { + return helper(1, 2); + } + + private int helper(int x, int y) { + return x + y; + } } diff --git a/java/com/google/turbine/parse/Parser.java b/java/com/google/turbine/parse/Parser.java index 4a090b3..af1eabf 100644 --- a/java/com/google/turbine/parse/Parser.java +++ b/java/com/google/turbine/parse/Parser.java @@ -519,7 +519,15 @@ public class Parser { interfaces.add(classty()); } while (maybe(Token.COMMA)); } - eat(Token.LBRACE); + switch (token) { + case LBRACE: + next(); + break; + case EXTENDS: + throw error(ErrorKind.EXTENDS_AFTER_IMPLEMENTS); + default: + throw error(ErrorKind.EXPECTED_TOKEN, Token.LBRACE); + } ImmutableList<Tree> members = classMembers(); eat(Token.RBRACE); return new TyDecl( @@ -748,7 +756,9 @@ public class Parser { } if (token == Token.DOT) { next(); - // TODO(cushon): is this cast OK? + if (!result.kind().equals(Kind.CLASS_TY)) { + throw error(token); + } result = classty((ClassTy) result); } result = maybeDims(maybeAnnos(), result); diff --git a/java/com/google/turbine/parse/StreamLexer.java b/java/com/google/turbine/parse/StreamLexer.java index 2e20c26..991b5fd 100644 --- a/java/com/google/turbine/parse/StreamLexer.java +++ b/java/com/google/turbine/parse/StreamLexer.java @@ -29,7 +29,7 @@ public class StreamLexer implements Lexer { private final UnicodeEscapePreprocessor reader; /** The current input character. */ - private char ch; + private int ch; /** The start position of the current token. */ private int position; @@ -353,7 +353,7 @@ public class StreamLexer implements Lexer { eat(); return Token.ELLIPSIS; } else { - throw error(ErrorKind.UNEXPECTED_INPUT, ch); + throw inputError(); } } case '0': @@ -384,7 +384,7 @@ public class StreamLexer implements Lexer { case '\'': throw error(ErrorKind.EMPTY_CHARACTER_LITERAL); default: - value = ch; + value = (char) ch; eat(); } if (ch == '\'') { @@ -419,7 +419,7 @@ public class StreamLexer implements Lexer { } // falls through default: - sb.append(ch); + sb.appendCodePoint(ch); eat(); continue STRING; } @@ -430,7 +430,7 @@ public class StreamLexer implements Lexer { // TODO(cushon): the style guide disallows non-ascii identifiers return identifier(); } - throw error(ErrorKind.UNEXPECTED_INPUT, ch); + throw inputError(); } } } @@ -511,7 +511,7 @@ public class StreamLexer implements Lexer { } } default: - throw error(ErrorKind.UNEXPECTED_INPUT, ch); + throw inputError(); } } @@ -623,7 +623,7 @@ public class StreamLexer implements Lexer { eat(); break; default: - throw error(ErrorKind.UNEXPECTED_INPUT, ch); + throw inputError(); } OUTER: while (true) { @@ -658,7 +658,7 @@ public class StreamLexer implements Lexer { case '9': continue OUTER; default: - throw error(ErrorKind.UNEXPECTED_INPUT, ch); + throw inputError(); } } case 'A': @@ -695,7 +695,7 @@ public class StreamLexer implements Lexer { if ('0' <= ch && ch <= '9') { eat(); } else { - throw error(ErrorKind.UNEXPECTED_INPUT, ch); + throw inputError(); } OUTER: while (true) { @@ -707,7 +707,7 @@ public class StreamLexer implements Lexer { if ('0' <= ch && ch <= '9') { continue OUTER; } else { - throw error(ErrorKind.UNEXPECTED_INPUT, ch); + throw inputError(); } case '0': case '1': @@ -746,7 +746,7 @@ public class StreamLexer implements Lexer { eat(); break; default: - throw error(ErrorKind.UNEXPECTED_INPUT, ch); + throw inputError(); } OUTER: while (true) { @@ -760,7 +760,7 @@ public class StreamLexer implements Lexer { case '1': continue OUTER; default: - throw error(ErrorKind.UNEXPECTED_INPUT, ch); + throw inputError(); } case '0': case '1': @@ -798,7 +798,7 @@ public class StreamLexer implements Lexer { eat(); break; default: - throw error(ErrorKind.UNEXPECTED_INPUT, ch); + throw inputError(); } OUTER: while (true) { @@ -818,7 +818,7 @@ public class StreamLexer implements Lexer { case '7': continue OUTER; default: - throw error(ErrorKind.UNEXPECTED_INPUT, ch); + throw inputError(); } case '0': case '1': @@ -992,7 +992,7 @@ public class StreamLexer implements Lexer { } case '/': // handled with comments - throw error(ErrorKind.UNEXPECTED_INPUT, ch); + throw inputError(); case '%': eat(); @@ -1011,7 +1011,7 @@ public class StreamLexer implements Lexer { return Token.XOR; } default: - throw error(ErrorKind.UNEXPECTED_INPUT, ch); + throw inputError(); } } @@ -1141,6 +1141,12 @@ public class StreamLexer implements Lexer { } } + private TurbineError inputError() { + return error( + ErrorKind.UNEXPECTED_INPUT, + Character.isBmpCodePoint(ch) ? Character.toString((char) ch) : String.format("U+%X", ch)); + } + private TurbineError error(ErrorKind kind, Object... args) { return TurbineError.format(reader.source(), reader.position(), kind, args); } diff --git a/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java b/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java index 3f38561..4146ca5 100644 --- a/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java +++ b/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java @@ -30,7 +30,7 @@ public class UnicodeEscapePreprocessor { private final String input; private int idx = 0; - private char ch; + private int ch; private boolean evenLeadingSlashes = true; public UnicodeEscapePreprocessor(SourceFile source) { @@ -49,7 +49,7 @@ public class UnicodeEscapePreprocessor { } /** Returns the next unescaped Unicode input character. */ - public char next() { + public int next() { eat(); if (ch == '\\' && evenLeadingSlashes) { unicodeEscape(); @@ -88,7 +88,7 @@ public class UnicodeEscapePreprocessor { } /** Consumes a hex digit. */ - private int hexDigit(char d) { + private int hexDigit(int d) { switch (d) { case '0': case '1': @@ -130,8 +130,20 @@ public class UnicodeEscapePreprocessor { * it terminates the input avoids some bounds checks in the lexer. */ private void eat() { - ch = done() ? ASCII_SUB : input.charAt(idx); + char hi = done() ? ASCII_SUB : input.charAt(idx); idx++; + if (!Character.isHighSurrogate(hi)) { + ch = hi; + return; + } + if (done()) { + throw error(ErrorKind.UNPAIRED_SURROGATE, (int) hi); + } + char lo = input.charAt(idx++); + if (!Character.isLowSurrogate(lo)) { + throw error(ErrorKind.UNPAIRED_SURROGATE, (int) hi); + } + ch = Character.toCodePoint(hi, lo); } public SourceFile source() { diff --git a/java/com/google/turbine/parse/VariableInitializerParser.java b/java/com/google/turbine/parse/VariableInitializerParser.java index 4ad9272..7f4d40e 100644 --- a/java/com/google/turbine/parse/VariableInitializerParser.java +++ b/java/com/google/turbine/parse/VariableInitializerParser.java @@ -40,10 +40,10 @@ import java.util.List; * <p>That handles everything except multi-variable declarations (int x = 1, y = 2;), which in * hindsight were probably a mistake. Multi-variable declarations contain a list of name and * initializer pairs separated by commas. The initializer expressions may also contain commas, so - * it's non-trivial to split on initializer boundaries. For example, consider `int x = a < b, c = - * d;`. We can't tell looking at the prefix `a < b, c` whether that's a less-than expression - * followed by another initializer, or the start of a generic type: `a<b, c>.foo()`. Distinguishing - * between these cases requires arbitrary lookahead. + * it's non-trivial to split on initializer boundaries. For example, consider {@code int x = a < b, + * c = d;}. We can't tell looking at the prefix {@code a < b, c} whether that's a less-than + * expression followed by another initializer, or the start of a generic type: {@code a<b, c>.foo(}. + * Distinguishing between these cases requires arbitrary lookahead. * * <p>The preprocessor seems to be operationally correct. It's possible there are edge cases that it * doesn't handle, but it's extremely rare for compile-time constant multi-variable declarations to @@ -330,6 +330,8 @@ public class VariableInitializerParser { depth--; next(); break; + case EOF: + throw error(ErrorKind.UNEXPECTED_EOF); default: next(); break; diff --git a/java/com/google/turbine/processing/TurbineAnnotationMirror.java b/java/com/google/turbine/processing/TurbineAnnotationMirror.java index 5ea3de1..df3bd19 100644 --- a/java/com/google/turbine/processing/TurbineAnnotationMirror.java +++ b/java/com/google/turbine/processing/TurbineAnnotationMirror.java @@ -18,6 +18,7 @@ package com.google.turbine.processing; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Iterables.getLast; +import static java.util.Objects.requireNonNull; import com.google.common.base.Joiner; import com.google.common.base.Supplier; @@ -115,8 +116,13 @@ class TurbineAnnotationMirror implements TurbineAnnotationValueMirror, Annotatio ImmutableMap.Builder<ExecutableElement, AnnotationValue> result = ImmutableMap.builder(); for (Map.Entry<String, Const> value : anno.values().entrySet()) { + // requireNonNull is safe because `elements` contains an entry for every method. + // Any element values pairs without a corresponding method in the annotation + // definition are weeded out in ConstEvaluator.evaluateAnnotation, and don't + // appear in the AnnoInfo. + MethodInfo methodInfo = requireNonNull(elements.get().get(value.getKey())); result.put( - factory.executableElement(elements.get().get(value.getKey()).sym()), + factory.executableElement(methodInfo.sym()), annotationValue(factory, value.getValue())); } return result.build(); diff --git a/java/com/google/turbine/processing/TurbineAnnotationProxy.java b/java/com/google/turbine/processing/TurbineAnnotationProxy.java index c39f310..967ead9 100644 --- a/java/com/google/turbine/processing/TurbineAnnotationProxy.java +++ b/java/com/google/turbine/processing/TurbineAnnotationProxy.java @@ -17,6 +17,7 @@ package com.google.turbine.processing; import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; import com.google.turbine.binder.bound.EnumConstantValue; import com.google.turbine.binder.bound.TurbineAnnotationValue; @@ -131,14 +132,15 @@ class TurbineAnnotationProxy implements InvocationHandler { private static Object constArrayValue( Class<?> returnType, ModelFactory factory, ClassLoader loader, ArrayInitValue value) { - if (returnType.getComponentType().equals(Class.class)) { + Class<?> componentType = requireNonNull(returnType.getComponentType()); + if (componentType.equals(Class.class)) { List<TypeMirror> result = new ArrayList<>(); for (Const element : value.elements()) { result.add(factory.asTypeMirror(((TurbineClassValue) element).type())); } throw new MirroredTypesException(result); } - Object result = Array.newInstance(returnType.getComponentType(), value.elements().size()); + Object result = Array.newInstance(componentType, value.elements().size()); int idx = 0; for (Const element : value.elements()) { Object v = constValue(returnType, factory, loader, element); diff --git a/java/com/google/turbine/processing/TurbineElement.java b/java/com/google/turbine/processing/TurbineElement.java index c22a442..f4f1675 100644 --- a/java/com/google/turbine/processing/TurbineElement.java +++ b/java/com/google/turbine/processing/TurbineElement.java @@ -46,7 +46,7 @@ import com.google.turbine.diag.TurbineError.ErrorKind; import com.google.turbine.model.Const; import com.google.turbine.model.Const.ArrayInitValue; import com.google.turbine.model.TurbineFlag; -import com.google.turbine.model.TurbineTyKind; +import com.google.turbine.tree.Tree; import com.google.turbine.tree.Tree.MethDecl; import com.google.turbine.tree.Tree.TyDecl; import com.google.turbine.tree.Tree.VarDecl; @@ -158,7 +158,8 @@ public abstract class TurbineElement implements Element { continue; } if (anno.sym().equals(metadata.repeatable())) { - ArrayInitValue arrayValue = (ArrayInitValue) anno.values().get("value"); + // requireNonNull is safe because java.lang.annotation.Repeatable declares `value`. + ArrayInitValue arrayValue = (ArrayInitValue) requireNonNull(anno.values().get("value")); for (Const element : arrayValue.elements()) { result.add( TurbineAnnotationProxy.create( @@ -262,11 +263,16 @@ public abstract class TurbineElement implements Element { return factory.asTypeMirror(info.superClassType()); } if (info instanceof SourceTypeBoundClass) { - // support simple name for stuff that doesn't exist + // support simple names for stuff that doesn't exist TyDecl decl = ((SourceTypeBoundClass) info).decl(); if (decl.xtnds().isPresent()) { - return factory.asTypeMirror( - ErrorTy.create(decl.xtnds().get().name().value())); + ArrayDeque<Tree.Ident> flat = new ArrayDeque<>(); + for (Tree.ClassTy curr = decl.xtnds().get(); + curr != null; + curr = curr.base().orElse(null)) { + flat.addFirst(curr.name()); + } + return factory.asTypeMirror(ErrorTy.create(flat)); } } return factory.noType(); @@ -785,18 +791,12 @@ public abstract class TurbineElement implements Element { @Override public ElementKind getKind() { - return info().name().equals("<init>") ? ElementKind.CONSTRUCTOR : ElementKind.METHOD; + return sym.name().equals("<init>") ? ElementKind.CONSTRUCTOR : ElementKind.METHOD; } @Override public Set<Modifier> getModifiers() { - int access = info().access(); - if (factory.getSymbol(info().sym().owner()).kind() == TurbineTyKind.INTERFACE) { - if ((access & (TurbineFlag.ACC_ABSTRACT | TurbineFlag.ACC_STATIC)) == 0) { - access |= TurbineFlag.ACC_DEFAULT; - } - } - return asModifierSet(ModifierOwner.METHOD, access); + return asModifierSet(ModifierOwner.METHOD, info().access()); } @Override diff --git a/java/com/google/turbine/processing/TurbineElements.java b/java/com/google/turbine/processing/TurbineElements.java index 9da210e..7ede6e3 100644 --- a/java/com/google/turbine/processing/TurbineElements.java +++ b/java/com/google/turbine/processing/TurbineElements.java @@ -131,7 +131,7 @@ public class TurbineElements implements Elements { if (!(element instanceof TurbineElement)) { throw new IllegalArgumentException(element.toString()); } - for (AnnoInfo a : ((TurbineTypeElement) element).annos()) { + for (AnnoInfo a : ((TurbineElement) element).annos()) { if (a.sym().equals(ClassSymbol.DEPRECATED)) { return true; } @@ -265,8 +265,8 @@ public class TurbineElements implements Elements { } /** - * Returns true if an element with the given {@code visibility} and located in package {@from} is - * visible to elements in package {@code to}. + * Returns true if an element with the given {@code visibility} and located in package {@code + * from} is visible to elements in package {@code to}. */ private static boolean isVisible( PackageSymbol from, PackageSymbol to, TurbineVisibility visibility) { diff --git a/java/com/google/turbine/processing/TurbineFiler.java b/java/com/google/turbine/processing/TurbineFiler.java index 186eb7f..45cdc22 100644 --- a/java/com/google/turbine/processing/TurbineFiler.java +++ b/java/com/google/turbine/processing/TurbineFiler.java @@ -18,6 +18,7 @@ package com.google.turbine.processing; 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; @@ -361,7 +362,7 @@ public class TurbineFiler implements Filer { @Override public URI toUri() { try { - return loader.getResource(path).toURI(); + return requireNonNull(loader.getResource(path)).toURI(); } catch (URISyntaxException e) { throw new AssertionError(e); } diff --git a/java/com/google/turbine/processing/TurbineProcessingEnvironment.java b/java/com/google/turbine/processing/TurbineProcessingEnvironment.java index 726d075..8b44e75 100644 --- a/java/com/google/turbine/processing/TurbineProcessingEnvironment.java +++ b/java/com/google/turbine/processing/TurbineProcessingEnvironment.java @@ -26,7 +26,7 @@ import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import org.checkerframework.checker.nullness.qual.Nullable; -/** Turbine's {@link ProcessingEnvironment). */ +/** Turbine's {@link ProcessingEnvironment}. */ public class TurbineProcessingEnvironment implements ProcessingEnvironment { private final Filer filer; diff --git a/java/com/google/turbine/processing/TurbineTypes.java b/java/com/google/turbine/processing/TurbineTypes.java index f65f921..7d2e6c0 100644 --- a/java/com/google/turbine/processing/TurbineTypes.java +++ b/java/com/google/turbine/processing/TurbineTypes.java @@ -825,7 +825,7 @@ public class TurbineTypes implements Types { @Override public List<? extends TypeMirror> directSupertypes(TypeMirror m) { - return factory.asTypeMirrors(directSupertypes(asTurbineType(m))); + return factory.asTypeMirrors(deannotate(directSupertypes(asTurbineType(m)))); } public ImmutableList<Type> directSupertypes(Type t) { @@ -882,7 +882,12 @@ public class TurbineTypes implements Types { @Override public TypeMirror erasure(TypeMirror typeMirror) { - return factory.asTypeMirror(erasure(asTurbineType(typeMirror))); + Type t = erasure(asTurbineType(typeMirror)); + if (t.tyKind() == TyKind.CLASS_TY) { + // bug-parity with javac + t = deannotate(t); + } + return factory.asTypeMirror(t); } private Type erasure(Type type) { @@ -896,6 +901,50 @@ public class TurbineTypes implements Types { }); } + /** + * Remove some type annotation metadata for bug-compatibility with javac, which does this + * inconsistently (see https://bugs.openjdk.java.net/browse/JDK-8042981). + */ + private static Type deannotate(Type ty) { + switch (ty.tyKind()) { + case CLASS_TY: + return deannotateClassTy((Type.ClassTy) ty); + case ARRAY_TY: + return deannotateArrayTy((Type.ArrayTy) ty); + case TY_VAR: + case INTERSECTION_TY: + case WILD_TY: + case METHOD_TY: + case PRIM_TY: + case VOID_TY: + case ERROR_TY: + case NONE_TY: + return ty; + } + throw new AssertionError(ty.tyKind()); + } + + private static ImmutableList<Type> deannotate(ImmutableList<Type> types) { + ImmutableList.Builder<Type> result = ImmutableList.builder(); + for (Type type : types) { + result.add(deannotate(type)); + } + return result.build(); + } + + private static Type.ArrayTy deannotateArrayTy(Type.ArrayTy ty) { + return ArrayTy.create(deannotate(ty.elementType()), /* annos= */ ImmutableList.of()); + } + + public static Type.ClassTy deannotateClassTy(Type.ClassTy ty) { + ImmutableList.Builder<Type.ClassTy.SimpleClassTy> classes = ImmutableList.builder(); + for (Type.ClassTy.SimpleClassTy c : ty.classes()) { + classes.add( + SimpleClassTy.create(c.sym(), deannotate(c.targs()), /* annos= */ ImmutableList.of())); + } + return ClassTy.create(classes.build()); + } + @Override public TypeElement boxedClass(PrimitiveType p) { return factory.typeElement(boxedClass(((PrimTy) asTurbineType(p)).primkind())); diff --git a/java/com/google/turbine/type/Type.java b/java/com/google/turbine/type/Type.java index daba2ae..bdddc6c 100644 --- a/java/com/google/turbine/type/Type.java +++ b/java/com/google/turbine/type/Type.java @@ -246,11 +246,14 @@ public interface Type { @Override public final String toString() { StringBuilder sb = new StringBuilder(); - for (AnnoInfo anno : annos()) { - sb.append(anno); + sb.append(elementType()); + if (!annos().isEmpty()) { sb.append(' '); + for (AnnoInfo anno : annos()) { + sb.append(anno); + sb.append(' '); + } } - sb.append(elementType()); sb.append("[]"); return sb.toString(); } diff --git a/java/com/google/turbine/types/Deannotate.java b/java/com/google/turbine/types/Deannotate.java new file mode 100644 index 0000000..1edb11f --- /dev/null +++ b/java/com/google/turbine/types/Deannotate.java @@ -0,0 +1,88 @@ +/* + * Copyright 2021 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.turbine.types; + +import com.google.common.collect.ImmutableList; +import com.google.turbine.type.Type; + +/** Removes type annotation metadata. */ +public class Deannotate { + public static Type deannotate(Type ty) { + switch (ty.tyKind()) { + case CLASS_TY: + return deannotateClassTy((Type.ClassTy) ty); + case ARRAY_TY: + return Type.ArrayTy.create( + deannotate(((Type.ArrayTy) ty).elementType()), ImmutableList.of()); + case INTERSECTION_TY: + return Type.IntersectionTy.create(deannotate(((Type.IntersectionTy) ty).bounds())); + case WILD_TY: + return deannotateWildTy((Type.WildTy) ty); + case METHOD_TY: + return deannotateMethodTy((Type.MethodTy) ty); + case PRIM_TY: + return Type.PrimTy.create(((Type.PrimTy) ty).primkind(), ImmutableList.of()); + case TY_VAR: + return Type.TyVar.create(((Type.TyVar) ty).sym(), ImmutableList.of()); + case VOID_TY: + case ERROR_TY: + case NONE_TY: + return ty; + } + throw new AssertionError(ty.tyKind()); + } + + private static ImmutableList<Type> deannotate(ImmutableList<Type> types) { + ImmutableList.Builder<Type> result = ImmutableList.builder(); + for (Type type : types) { + result.add(deannotate(type)); + } + return result.build(); + } + + public static Type.ClassTy deannotateClassTy(Type.ClassTy ty) { + ImmutableList.Builder<Type.ClassTy.SimpleClassTy> classes = ImmutableList.builder(); + for (Type.ClassTy.SimpleClassTy c : ty.classes()) { + classes.add( + Type.ClassTy.SimpleClassTy.create(c.sym(), deannotate(c.targs()), ImmutableList.of())); + } + return Type.ClassTy.create(classes.build()); + } + + private static Type deannotateWildTy(Type.WildTy ty) { + switch (ty.boundKind()) { + case NONE: + return Type.WildUnboundedTy.create(ImmutableList.of()); + case LOWER: + return Type.WildLowerBoundedTy.create(ty.bound(), ImmutableList.of()); + case UPPER: + return Type.WildUpperBoundedTy.create(ty.bound(), ImmutableList.of()); + } + throw new AssertionError(ty.boundKind()); + } + + private static Type deannotateMethodTy(Type.MethodTy ty) { + return Type.MethodTy.create( + ty.tyParams(), + deannotate(ty.returnType()), + ty.receiverType() != null ? deannotate(ty.receiverType()) : null, + deannotate(ty.parameters()), + deannotate(ty.thrown())); + } + + private Deannotate() {} +} diff --git a/java/com/google/turbine/types/Erasure.java b/java/com/google/turbine/types/Erasure.java index 9042897..4b6fbc1 100644 --- a/java/com/google/turbine/types/Erasure.java +++ b/java/com/google/turbine/types/Erasure.java @@ -19,7 +19,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.SourceTypeBoundClass; import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.type.Type; @@ -32,8 +31,8 @@ import com.google.turbine.type.Type.TyVar; import com.google.turbine.type.Type.WildTy; /** Generic type erasure. */ -public class Erasure { - public static Type erase(Type ty, Function<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tenv) { +public final class Erasure { + public static Type erase(Type ty, Function<TyVarSymbol, TyVarInfo> tenv) { switch (ty.tyKind()) { case CLASS_TY: return eraseClassTy((Type.ClassTy) ty); @@ -70,14 +69,12 @@ public class Erasure { return ty.bounds().isEmpty() ? ClassTy.OBJECT : erase(ty.bounds().get(0), tenv); } - private static Type eraseTyVar( - TyVar ty, Function<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tenv) { - SourceTypeBoundClass.TyVarInfo info = tenv.apply(ty.sym()); + private static Type eraseTyVar(TyVar ty, Function<TyVarSymbol, TyVarInfo> tenv) { + TyVarInfo info = tenv.apply(ty.sym()); return erase(info.upperBound(), tenv); } - private static Type.ArrayTy eraseArrayTy( - Type.ArrayTy ty, Function<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tenv) { + private static Type.ArrayTy eraseArrayTy(Type.ArrayTy ty, Function<TyVarSymbol, TyVarInfo> tenv) { return ArrayTy.create(erase(ty.elementType(), tenv), ty.annos()); } @@ -112,4 +109,6 @@ public class Erasure { erase(ty.parameters(), tenv), erase(ty.thrown(), tenv)); } + + private Erasure() {} } diff --git a/java/com/google/turbine/zip/Zip.java b/java/com/google/turbine/zip/Zip.java index 48d4697..fa0f0e0 100644 --- a/java/com/google/turbine/zip/Zip.java +++ b/java/com/google/turbine/zip/Zip.java @@ -71,7 +71,7 @@ import java.util.zip.ZipException; * header is present only if ENDTOT in EOCD header is 0xFFFF. * </ul> */ -public class Zip { +public final class Zip { static final int ZIP64_ENDSIG = 0x06064b50; @@ -335,4 +335,6 @@ public class Zip { && (buf.get(index + 2) == i) && (buf.get(index + 3) == j); } + + private Zip() {} } |