Merge remote-tracking branch 'aosp/upstream-main'
* aosp/upstream-main: (86 commits)
Handle unicode characters that require two UTF-16 code units
Sort test inputs
Update to setup-java v2, and use adopt builds
Address compile errors that would appear when `ImmutableMap` is annotated for nullness in CL 382342656.
Remove obsolete parent per https://github.com/sonatype/oss-parents
Fix handling of repackaged transitive classes in jdeps
Inline a single-use abstract test class
Fix javadoc
Inherit from the sonatype oss parent artifact
Test invalid annotation element values are weeded out
Satisfy the nullness checker.
Use `assertThrows` for expected exception tests
Don't require an argument for `--compress_jar`
Satisfy the nullness checker
Fix NPEs in options parsing
Remove deprecated builders
Rename `master` branch to `main`
Never class-load `TurbineProcessingEnvironment` from the `-processor`
Update Error Prone and maven versions
Update turbine CI JDK versions
...
Bug: 193141629
Test: m checkbuild
Change-Id: If2e91cfa8c0b7d307acceb119b5ac4b195a4a237
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..6313b56
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* text=auto eol=lf
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..87582f0
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,75 @@
+# 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.
+
+name: CI
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ test:
+ name: "JDK ${{ matrix.java }} on ${{ matrix.os }}"
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ ubuntu-latest ]
+ java: [ 16, 11, 8 ]
+ experimental: [ false ]
+ include:
+ # Only test on macos and windows with a single recent JDK to avoid a
+ # combinatorial explosion of test configurations.
+ - os: macos-latest
+ java: 16
+ experimental: false
+ - os: windows-latest
+ java: 16
+ experimental: false
+ - os: ubuntu-latest
+ java: 17-ea
+ experimental: true
+ runs-on: ${{ matrix.os }}
+ continue-on-error: ${{ matrix.experimental }}
+ steps:
+ - name: Cancel previous
+ uses: styfle/cancel-workflow-action@0.8.0
+ with:
+ access_token: ${{ github.token }}
+ - name: 'Check out repository'
+ uses: actions/checkout@v2
+ - name: 'Cache local Maven repository'
+ uses: actions/cache@v2
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-
+ - name: 'Set up JDK ${{ matrix.java }}'
+ uses: actions/setup-java@v2
+ with:
+ java-version: ${{ matrix.java }}
+ distribution: 'adopt'
+ - name: 'Install'
+ shell: bash
+ run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
+ - name: 'Test'
+ shell: bash
+ run: mvn test -B
+ - name: 'Javadoc'
+ shell: bash
+ run: mvn javadoc:aggregate
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 8113f5f..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-language: java
-
-jdk:
- - openjdk8
- - openjdk9
- - openjdk10
- - openjdk11
- - openjdk-ea
-
-matrix:
- allow_failures:
- - jdk: openjdk-ea
-
-# see https://github.com/travis-ci/travis-ci/issues/8408
-before_install:
-- unset _JAVA_OPTIONS
-
-# use travis-ci docker based infrastructure
-sudo: false
-
-cache:
- directories:
- - $HOME/.m2
-
-script:
-- mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
-- mvn test -B
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 44ff736..0000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-os: Visual Studio 2015
-
-environment:
- matrix:
- - JAVA_HOME: C:\Program Files\Java\jdk9
- - JAVA_HOME: C:\Program Files\Java\jdk10
-
-install:
- - ps: |
- Add-Type -AssemblyName System.IO.Compression.FileSystem
- if (!(Test-Path -Path "C:\maven" )) {
- (new-object System.Net.WebClient).DownloadFile(
- 'http://www.us.apache.org/dist/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.zip',
- 'C:\maven-bin.zip'
- )
- [System.IO.Compression.ZipFile]::ExtractToDirectory("C:\maven-bin.zip", "C:\maven")
- }
- - cmd: SET PATH=C:\maven\apache-maven-3.3.9\bin;%JAVA_HOME%\bin;%PATH%
- - cmd: SET MAVEN_OPTS=-XX:MaxPermSize=2g -Xmx4g
- - cmd: SET JAVA_OPTS=-XX:MaxPermSize=2g -Xmx4g
-
-build_script:
- - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
-
-test_script:
- - mvn test -B
-
-cache:
- - C:\maven\
- - C:\Users\appveyor\.m2
-
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 @@
/**
* 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 @@
* 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 @@
}
// 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.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 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 @@
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 @@
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 @@
/**
* 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 @@
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 @@
}
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.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 @@
}
});
}
+
+ 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 @@
* 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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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.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 @@
}
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 @@
}
/** 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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
: 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 @@
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.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 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 @@
}
};
// 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 @@
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 @@
}
});
}
+
+ @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 @@
* 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 @@
}
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 @@
}
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.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.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 @@
/** 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 @@
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 @@
}
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 @@
// 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 @@
}
errorRaised = log.errorRaised();
if (errorRaised) {
- log.maybeThrow();
+ break;
}
log.clear();
result =
@@ -228,7 +229,8 @@
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 @@
classpath,
bootclasspath,
moduleVersion);
- log.maybeThrow();
+ if (log.anyErrors()) {
+ return null;
+ }
}
if (!filer.generatedClasses().isEmpty()) {
@@ -267,13 +271,44 @@
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 @@
}
// 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 static ProcessorInfo initializeProcessors(
ImmutableList<String> javacopts,
- ImmutableList<String> processorPath,
ImmutableSet<String> processorNames,
- ImmutableSet<String> builtinProcessors)
- throws MalformedURLException {
- ClassLoader processorLoader = null;
- 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);
- }
- }
- processorOptions = processorOptions(javacopts);
- } else {
- processorOptions = ImmutableMap.of();
+ 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();
+ 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);
+ }
+ }
+ 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 @@
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.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 @@
}
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.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 @@
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 @@
== 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 @@
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.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 @@
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 @@
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 @@
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.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 @@
/* 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 @@
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.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 @@
* 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 @@
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 @@
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 @@
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 @@
});
@Override
- public ClassSymbol superclass() {
+ public @Nullable ClassSymbol superclass() {
return superclass.get();
}
@@ -259,11 +258,11 @@
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 @@
});
@Override
- public ClassTy superClassType() {
+ public @Nullable ClassTy superClassType() {
return superClassType.get();
}
@@ -319,7 +318,8 @@
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 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 @@
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 @@
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 @@
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 @@
}
});
- 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 @@
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 @@
}
}
- 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 @@
}
@Override
- public AnnotationMetadata annotationMetadata() {
+ public @Nullable AnnotationMetadata annotationMetadata() {
return annotationMetadata.get();
}
@@ -611,7 +630,11 @@
}
/** 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.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 @@
* 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.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 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 @@
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 @@
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.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 @@
case MODULE:
writeModule((Attribute.Module) attribute);
break;
+ case TURBINE_TRANSITIVE_JAR:
+ writeTurbineTransitiveJar((Attribute.TurbineTransitiveJar) attribute);
+ break;
}
}
@@ -266,4 +270,10 @@
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 @@
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 @@
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 @@
this.innerClasses = innerClasses;
this.typeAnnotations = typeAnnotations;
this.module = module;
+ this.transitiveJar = transitiveJar;
}
/** Class access and property flags. */
@@ -124,6 +127,12 @@
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 @@
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 @@
case "Module":
module = readModule(constantPool);
break;
+ case "TurbineTransitiveJar":
+ transitiveJar = readTurbineTransitiveJar(constantPool);
+ break;
default:
reader.skip(reader.u4());
break;
@@ -141,7 +145,8 @@
annotations.build(),
innerclasses,
ImmutableList.of(),
- module);
+ module,
+ transitiveJar);
}
/** Reads a JVMS 4.7.9 Signature attribute. */
@@ -509,4 +514,9 @@
}
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 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 @@
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.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 @@
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 @@
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 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 @@
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.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 @@
// 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.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 @@
// 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 @@
/**
* 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 @@
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 @@
cf.annotations(),
innerClasses.build(),
cf.typeAnnotations(),
- /* module= */ null);
+ /* module= */ null,
+ /* transitiveJar = */ transitiveJar);
}
private static Set<ClassSymbol> superClosure(BindingResult bound) {
@@ -134,4 +143,6 @@
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 @@
/** 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 @@
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 @@
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 @@
/** 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 @@
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 @@
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 @@
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 @@
/** 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 @@
* 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 @@
/** 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 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 @@
annotations,
innerClasses.build(),
/* typeAnnotations= */ ImmutableList.of(),
- moduleInfo);
+ moduleInfo,
+ /* transitiveJar= */ null);
symbols.addAll(sig.classes);
return ClassWriter.writeClass(classfile);
}
@@ -279,7 +280,8 @@
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 @@
* 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 @@
}
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 @@
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 @@
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.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 @@
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 @@
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 @@
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 @@
}
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 @@
* <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 @@
/** 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 @@
/** 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 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 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 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 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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- /** 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 @@
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 @@
}
}
}
+
+ 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 @@
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 @@
throw new AssertionError();
}
eat();
+ int pos = position;
Tree.ConstVarName constVarName = (Tree.ConstVarName) qualIdent();
if (constVarName == null) {
return null;
@@ -577,10 +581,10 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
eat();
return Token.ELLIPSIS;
} else {
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
}
case '0':
@@ -384,7 +384,7 @@
case '\'':
throw error(ErrorKind.EMPTY_CHARACTER_LITERAL);
default:
- value = ch;
+ value = (char) ch;
eat();
}
if (ch == '\'') {
@@ -419,7 +419,7 @@
}
// falls through
default:
- sb.append(ch);
+ sb.appendCodePoint(ch);
eat();
continue STRING;
}
@@ -430,7 +430,7 @@
// TODO(cushon): the style guide disallows non-ascii identifiers
return identifier();
}
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
}
}
@@ -511,7 +511,7 @@
}
}
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
}
@@ -623,7 +623,7 @@
eat();
break;
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
OUTER:
while (true) {
@@ -658,7 +658,7 @@
case '9':
continue OUTER;
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
}
case 'A':
@@ -695,7 +695,7 @@
if ('0' <= ch && ch <= '9') {
eat();
} else {
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
OUTER:
while (true) {
@@ -707,7 +707,7 @@
if ('0' <= ch && ch <= '9') {
continue OUTER;
} else {
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
case '0':
case '1':
@@ -746,7 +746,7 @@
eat();
break;
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
OUTER:
while (true) {
@@ -760,7 +760,7 @@
case '1':
continue OUTER;
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
case '0':
case '1':
@@ -798,7 +798,7 @@
eat();
break;
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
OUTER:
while (true) {
@@ -818,7 +818,7 @@
case '7':
continue OUTER;
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
case '0':
case '1':
@@ -992,7 +992,7 @@
}
case '/':
// handled with comments
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
case '%':
eat();
@@ -1011,7 +1011,7 @@
return Token.XOR;
}
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
}
@@ -1141,6 +1141,12 @@
}
}
+ 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 @@
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 @@
}
/** Returns the next unescaped Unicode input character. */
- public char next() {
+ public int next() {
eat();
if (ch == '\\' && evenLeadingSlashes) {
unicodeEscape();
@@ -88,7 +88,7 @@
}
/** Consumes a hex digit. */
- private int hexDigit(char d) {
+ private int hexDigit(int d) {
switch (d) {
case '0':
case '1':
@@ -130,8 +130,20 @@
* 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 @@
* <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 @@
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 @@
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 @@
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 @@
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.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 @@
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 @@
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 @@
@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 @@
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 @@
}
/**
- * 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 @@
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 @@
@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.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 @@
@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 @@
@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 @@
});
}
+ /**
+ * 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 @@
@Override
public final String toString() {
StringBuilder sb = new StringBuilder();
- for (AnnoInfo anno : annos()) {
- sb.append(anno);
- sb.append(' ');
- }
sb.append(elementType());
+ if (!annos().isEmpty()) {
+ sb.append(' ');
+ for (AnnoInfo anno : annos()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
+ }
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 @@
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.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 @@
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 @@
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 @@
* 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 @@
&& (buf.get(index + 2) == i)
&& (buf.get(index + 3) == j);
}
+
+ private Zip() {}
}
diff --git a/javatests/com/google/turbine/binder/BinderErrorTest.java b/javatests/com/google/turbine/binder/BinderErrorTest.java
index 15b54eb..e6e30cb 100644
--- a/javatests/com/google/turbine/binder/BinderErrorTest.java
+++ b/javatests/com/google/turbine/binder/BinderErrorTest.java
@@ -18,7 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
@@ -93,6 +93,9 @@
"<>:2: error: could not resolve element foo() in Anno", //
"@Anno(foo=100, bar=200) class Test {}",
" ^",
+ "<>:2: error: could not resolve element bar() in Anno", //
+ "@Anno(foo=100, bar=200) class Test {}",
+ " ^",
},
},
{
@@ -558,9 +561,12 @@
"class T {}",
},
{
- "<>:7: error: could not resolve B", //
+ "<>:7: error: could not resolve B",
"@One.A(b = {@B})",
" ^",
+ "<>:7: error: could not evaluate constant expression",
+ "@One.A(b = {@B})",
+ " ^",
},
},
{
@@ -700,6 +706,112 @@
" ^",
},
},
+ {
+ {
+ "import java.util.List;",
+ "class T {", //
+ " List<int> xs = new ArrayList<>();",
+ "}",
+ },
+ {
+ "<>:3: error: unexpected type int", //
+ " List<int> xs = new ArrayList<>();",
+ " ^",
+ },
+ },
+ {
+ {
+ "@interface A {",
+ " int[] xs() default {};",
+ "}",
+ "@A(xs = Object.class)",
+ "class T {",
+ "}",
+ },
+ {
+ "<>:4: error: could not evaluate constant expression",
+ "@A(xs = Object.class)",
+ " ^",
+ },
+ },
+ {
+ {
+ "package foobar;",
+ "import java.lang.annotation.Retention;",
+ "@Retention",
+ "@interface Test {}",
+ },
+ {
+ "<>:3: error: missing required annotation argument: value", //
+ "@Retention",
+ "^",
+ },
+ },
+ {
+ {
+ "interface Test {", //
+ " static final void f() {}",
+ "}",
+ },
+ {
+ "<>:2: error: unexpected modifier: final", //
+ " static final void f() {}",
+ " ^",
+ },
+ },
+ {
+ {
+ "package foobar;",
+ "import java.lang.annotation.Retention;",
+ "@Retention",
+ "@Retention",
+ "@interface Test {}",
+ },
+ {
+ "<>:3: error: missing required annotation argument: value",
+ "@Retention",
+ "^",
+ "<>:4: error: missing required annotation argument: value",
+ "@Retention",
+ "^",
+ "<>:3: error: java.lang.annotation.Retention is not @Repeatable",
+ "@Retention",
+ "^",
+ },
+ },
+ {
+ {
+ "import java.util.List;", //
+ "class Test {",
+ " @interface A {}",
+ " void f(List<@NoSuch int> xs) {}",
+ "}",
+ },
+ {
+ "<>:4: error: could not resolve NoSuch",
+ " void f(List<@NoSuch int> xs) {}",
+ " ^",
+ "<>:4: error: unexpected type int",
+ " void f(List<@NoSuch int> xs) {}",
+ " ^",
+ },
+ },
+ {
+ {
+ "@interface B {}",
+ "@interface A {",
+ " B[] value() default @B;",
+ "}",
+ "interface C {}",
+ "@A(value = @C)",
+ "class T {}",
+ },
+ {
+ "<>:6: error: C is not an annotation", //
+ "@A(value = @C)",
+ " ^",
+ },
+ },
};
return Arrays.asList((Object[][]) testCases);
}
@@ -714,17 +826,18 @@
@Test
public void test() throws Exception {
- try {
- Binder.bind(
- ImmutableList.of(parseLines(source)),
- ClassPathBinder.bindClasspath(ImmutableList.of()),
- TURBINE_BOOTCLASSPATH,
- /* moduleVersion=*/ Optional.empty())
- .units();
- fail(Joiner.on('\n').join(source));
- } catch (TurbineError e) {
- assertThat(e).hasMessageThat().isEqualTo(lines(expected));
- }
+ TurbineError e =
+ assertThrows(
+ Joiner.on('\n').join(source),
+ TurbineError.class,
+ () ->
+ Binder.bind(
+ ImmutableList.of(parseLines(source)),
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ TURBINE_BOOTCLASSPATH,
+ /* moduleVersion=*/ Optional.empty())
+ .units());
+ assertThat(e).hasMessageThat().isEqualTo(lines(expected));
}
@SupportedAnnotationTypes("*")
@@ -744,22 +857,23 @@
// exercise error reporting with annotation enabled, which should be identical
@Test
public void testWithProcessors() throws Exception {
- try {
- Binder.bind(
- ImmutableList.of(parseLines(source)),
- ClassPathBinder.bindClasspath(ImmutableList.of()),
- ProcessorInfo.create(
- ImmutableList.of(new HelloWorldProcessor()),
- /* loader= */ getClass().getClassLoader(),
- /* options= */ ImmutableMap.of(),
- SourceVersion.latestSupported()),
- TURBINE_BOOTCLASSPATH,
- /* moduleVersion=*/ Optional.empty())
- .units();
- fail(Joiner.on('\n').join(source));
- } catch (TurbineError e) {
- assertThat(e).hasMessageThat().isEqualTo(lines(expected));
- }
+ TurbineError e =
+ assertThrows(
+ Joiner.on('\n').join(source),
+ TurbineError.class,
+ () ->
+ Binder.bind(
+ ImmutableList.of(parseLines(source)),
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ ProcessorInfo.create(
+ ImmutableList.of(new HelloWorldProcessor()),
+ /* loader= */ getClass().getClassLoader(),
+ /* options= */ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TURBINE_BOOTCLASSPATH,
+ /* moduleVersion=*/ Optional.empty())
+ .units());
+ assertThat(e).hasMessageThat().isEqualTo(lines(expected));
}
private static CompUnit parseLines(String... lines) {
diff --git a/javatests/com/google/turbine/binder/BinderTest.java b/javatests/com/google/turbine/binder/BinderTest.java
index e238ee0..820fe22 100644
--- a/javatests/com/google/turbine/binder/BinderTest.java
+++ b/javatests/com/google/turbine/binder/BinderTest.java
@@ -19,7 +19,8 @@
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH;
-import static org.junit.Assert.fail;
+import static java.util.Objects.requireNonNull;
+import static org.junit.Assert.assertThrows;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
@@ -84,17 +85,16 @@
new ClassSymbol("a/A$Inner2"),
new ClassSymbol("b/B"));
- SourceTypeBoundClass a = bound.get(new ClassSymbol("a/A"));
+ SourceTypeBoundClass a = getBoundClass(bound, "a/A");
assertThat(a.superclass()).isEqualTo(new ClassSymbol("java/lang/Object"));
assertThat(a.interfaces()).isEmpty();
- assertThat(bound.get(new ClassSymbol("a/A$Inner1")).superclass())
- .isEqualTo(new ClassSymbol("b/B"));
+ assertThat(getBoundClass(bound, "a/A$Inner1").superclass()).isEqualTo(new ClassSymbol("b/B"));
- assertThat(bound.get(new ClassSymbol("a/A$Inner2")).superclass())
+ assertThat(getBoundClass(bound, "a/A$Inner2").superclass())
.isEqualTo(new ClassSymbol("a/A$Inner1"));
- SourceTypeBoundClass b = bound.get(new ClassSymbol("b/B"));
+ SourceTypeBoundClass b = getBoundClass(bound, "b/B");
assertThat(b.superclass()).isEqualTo(new ClassSymbol("a/A"));
}
@@ -129,12 +129,12 @@
new ClassSymbol("b/B"),
new ClassSymbol("b/B$BInner"));
- assertThat(bound.get(new ClassSymbol("b/B")).interfaces())
+ assertThat(getBoundClass(bound, "b/B").interfaces())
.containsExactly(new ClassSymbol("com/i/I"));
- assertThat(bound.get(new ClassSymbol("b/B$BInner")).superclass())
+ assertThat(getBoundClass(bound, "b/B$BInner").superclass())
.isEqualTo(new ClassSymbol("com/i/I$IInner"));
- assertThat(bound.get(new ClassSymbol("b/B$BInner")).interfaces()).isEmpty();
+ assertThat(getBoundClass(bound, "b/B$BInner").interfaces()).isEmpty();
}
@Test
@@ -161,7 +161,7 @@
/* moduleVersion=*/ Optional.empty())
.units();
- assertThat(bound.get(new ClassSymbol("other/Foo")).superclass())
+ assertThat(getBoundClass(bound, "other/Foo").superclass())
.isEqualTo(new ClassSymbol("com/test/Test$Inner"));
}
@@ -182,16 +182,16 @@
" class Inner {}",
"}"));
- try {
- Binder.bind(
- units,
- ClassPathBinder.bindClasspath(ImmutableList.of()),
- TURBINE_BOOTCLASSPATH,
- /* moduleVersion=*/ Optional.empty());
- fail();
- } catch (TurbineError e) {
- assertThat(e).hasMessageThat().contains("cycle in class hierarchy: a.A -> b.B -> a.A");
- }
+ TurbineError e =
+ assertThrows(
+ TurbineError.class,
+ () ->
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ TURBINE_BOOTCLASSPATH,
+ /* moduleVersion=*/ Optional.empty()));
+ assertThat(e).hasMessageThat().contains("cycle in class hierarchy: a.A -> b.B -> a.A");
}
@Test
@@ -211,7 +211,7 @@
/* moduleVersion=*/ Optional.empty())
.units();
- SourceTypeBoundClass a = bound.get(new ClassSymbol("com/test/Annotation"));
+ SourceTypeBoundClass a = getBoundClass(bound, "com/test/Annotation");
assertThat(a.access())
.isEqualTo(
TurbineFlag.ACC_PUBLIC
@@ -240,7 +240,7 @@
/* moduleVersion=*/ Optional.empty())
.units();
- SourceTypeBoundClass a = bound.get(new ClassSymbol("a/A"));
+ SourceTypeBoundClass a = getBoundClass(bound, "a/A");
assertThat(a.interfaces()).containsExactly(new ClassSymbol("java/util/Map$Entry"));
}
@@ -259,7 +259,7 @@
try (OutputStream os = Files.newOutputStream(libJar);
JarOutputStream jos = new JarOutputStream(os)) {
jos.putNextEntry(new JarEntry("B.class"));
- jos.write(lib.get("B"));
+ jos.write(requireNonNull(lib.get("B")));
}
ImmutableList<Tree.CompUnit> units =
@@ -280,7 +280,7 @@
/* moduleVersion=*/ Optional.empty())
.units();
- SourceTypeBoundClass a = bound.get(new ClassSymbol("C$A"));
+ SourceTypeBoundClass a = getBoundClass(bound, "C$A");
assertThat(a.annotationMetadata().target()).containsExactly(TurbineElementType.TYPE_USE);
}
@@ -306,7 +306,7 @@
assertThat(bound.keySet()).containsExactly(new ClassSymbol("a/A"));
- SourceTypeBoundClass a = bound.get(new ClassSymbol("a/A"));
+ SourceTypeBoundClass a = getBoundClass(bound, "a/A");
FieldInfo f = getOnlyElement(a.fields());
assertThat(f.name()).isEqualTo("b");
assertThat(f.value()).isNull();
@@ -315,4 +315,10 @@
private Tree.CompUnit parseLines(String... lines) {
return Parser.parse(Joiner.on('\n').join(lines));
}
+
+ private static SourceTypeBoundClass getBoundClass(
+ Map<ClassSymbol, SourceTypeBoundClass> bound, String name) {
+ // requireNonNull is safe as long as we call this method with classes that exist in our sources.
+ return requireNonNull(bound.get(new ClassSymbol(name)));
+ }
}
diff --git a/javatests/com/google/turbine/binder/ClassPathBinderTest.java b/javatests/com/google/turbine/binder/ClassPathBinderTest.java
index c11d814..6c6bc3e 100644
--- a/javatests/com/google/turbine/binder/ClassPathBinderTest.java
+++ b/javatests/com/google/turbine/binder/ClassPathBinderTest.java
@@ -16,17 +16,22 @@
package com.google.turbine.binder;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.Iterables.getLast;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.collect.MoreCollectors.onlyElement;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH;
+import static com.google.turbine.testing.TestResources.getResourceBytes;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.junit.Assert.fail;
+import static java.util.Locale.ENGLISH;
+import static java.util.Objects.requireNonNull;
+import static org.junit.Assert.assertThrows;
import com.google.common.base.VerifyException;
+import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
-import com.google.common.io.ByteStreams;
import com.google.common.io.MoreFiles;
import com.google.turbine.binder.bound.EnumConstantValue;
import com.google.turbine.binder.bound.TypeBoundClass;
@@ -34,6 +39,7 @@
import com.google.turbine.binder.env.Env;
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.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
@@ -42,40 +48,100 @@
import com.google.turbine.tree.Tree.Ident;
import com.google.turbine.type.AnnoInfo;
import com.google.turbine.type.Type.ClassTy;
-import java.io.IOError;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Arrays;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+import org.junit.runners.Parameterized;
-@RunWith(JUnit4.class)
+@RunWith(Parameterized.class)
public class ClassPathBinderTest {
+ @Parameterized.Parameters
+ public static ImmutableCollection<Object[]> parameters() {
+ Object[] testCases = {
+ TURBINE_BOOTCLASSPATH,
+ FileManagerClassBinder.adapt(
+ ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, ENGLISH, UTF_8),
+ StandardLocation.PLATFORM_CLASS_PATH),
+ };
+ return Arrays.stream(testCases).map(x -> new Object[] {x}).collect(toImmutableList());
+ }
+
+ private final ClassPath classPath;
+
+ public ClassPathBinderTest(ClassPath classPath) {
+ this.classPath = classPath;
+ }
+
@Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+ private static Ident ident(String string) {
+ return new Ident(/* position= */ -1, string);
+ }
+
@Test
- public void classPathLookup() throws IOException {
+ public void classPathLookup() {
- Scope javaLang = TURBINE_BOOTCLASSPATH.index().lookupPackage(ImmutableList.of("java", "lang"));
+ Scope javaLang = classPath.index().lookupPackage(ImmutableList.of("java", "lang"));
- LookupResult result = javaLang.lookup(new LookupKey(ImmutableList.of(new Ident(-1, "String"))));
+ final String string = "String";
+ LookupResult result = javaLang.lookup(new LookupKey(ImmutableList.of(ident(string))));
assertThat(result.remaining()).isEmpty();
assertThat(result.sym()).isEqualTo(new ClassSymbol("java/lang/String"));
- result = javaLang.lookup(new LookupKey(ImmutableList.of(new Ident(-1, "Object"))));
+ result = javaLang.lookup(new LookupKey(ImmutableList.of(ident("Object"))));
assertThat(result.remaining()).isEmpty();
assertThat(result.sym()).isEqualTo(new ClassSymbol("java/lang/Object"));
}
@Test
- public void classPathClasses() throws IOException {
- Env<ClassSymbol, BytecodeBoundClass> env = TURBINE_BOOTCLASSPATH.env();
+ public void packageScope() {
+
+ PackageScope result = classPath.index().lookupPackage(ImmutableList.of("java", "nosuch"));
+ assertThat(result).isNull();
+
+ result = classPath.index().lookupPackage(ImmutableList.of("java", "lang"));
+ assertThat(result.classes()).contains(new ClassSymbol("java/lang/String"));
+
+ assertThat(result.lookup(new LookupKey(ImmutableList.of(ident("NoSuch"))))).isNull();
+ }
+
+ @Test
+ public void scope() {
+ Scope scope = classPath.index().scope();
+ LookupResult result;
+
+ result =
+ scope.lookup(
+ new LookupKey(
+ ImmutableList.of(ident("java"), ident("util"), ident("Map"), ident("Entry"))));
+ assertThat(result.sym()).isEqualTo(new ClassSymbol("java/util/Map"));
+ assertThat(result.remaining().stream().map(Ident::value)).containsExactly("Entry");
+
+ result =
+ scope.lookup(new LookupKey(ImmutableList.of(ident("java"), ident("util"), ident("Map"))));
+ assertThat(result.sym()).isEqualTo(new ClassSymbol("java/util/Map"));
+ assertThat(result.remaining()).isEmpty();
+
+ result =
+ scope.lookup(
+ new LookupKey(ImmutableList.of(ident("java"), ident("util"), ident("NoSuch"))));
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void classPathClasses() {
+ Env<ClassSymbol, BytecodeBoundClass> env = classPath.env();
TypeBoundClass c = env.get(new ClassSymbol("java/util/Map$Entry"));
assertThat(c.owner()).isEqualTo(new ClassSymbol("java/util/Map"));
@@ -96,7 +162,7 @@
@Test
public void interfaces() {
- Env<ClassSymbol, BytecodeBoundClass> env = TURBINE_BOOTCLASSPATH.env();
+ Env<ClassSymbol, BytecodeBoundClass> env = classPath.env();
TypeBoundClass c = env.get(new ClassSymbol("java/lang/annotation/Retention"));
assertThat(c.interfaceTypes()).hasSize(1);
@@ -114,7 +180,7 @@
@Test
public void annotations() {
- Env<ClassSymbol, BytecodeBoundClass> env = TURBINE_BOOTCLASSPATH.env();
+ Env<ClassSymbol, BytecodeBoundClass> env = classPath.env();
TypeBoundClass c = env.get(new ClassSymbol("java/lang/annotation/Retention"));
AnnoInfo anno =
@@ -122,34 +188,25 @@
.filter(a -> a.sym().equals(new ClassSymbol("java/lang/annotation/Retention")))
.collect(onlyElement());
assertThat(anno.values().keySet()).containsExactly("value");
- assertThat(((EnumConstantValue) anno.values().get("value")).sym())
+ // requireNonNull is safe because we checked that the keySet contains `"value"`.
+ assertThat(((EnumConstantValue) requireNonNull(anno.values().get("value"))).sym())
.isEqualTo(
new FieldSymbol(new ClassSymbol("java/lang/annotation/RetentionPolicy"), "RUNTIME"));
}
@Test
public void byteCodeBoundClassName() {
+ Env<ClassSymbol, BytecodeBoundClass> env = classPath.env();
BytecodeBoundClass c =
new BytecodeBoundClass(
new ClassSymbol("java/util/List"),
- () -> {
- try {
- return ByteStreams.toByteArray(
- getClass().getClassLoader().getResourceAsStream("java/util/ArrayList.class"));
- } catch (IOException e) {
- throw new IOError(e);
- }
- },
- null,
+ () -> getResourceBytes(getClass(), "/java/util/ArrayList.class"),
+ env,
null);
- try {
- c.owner();
- fail();
- } catch (VerifyException e) {
- assertThat(e)
- .hasMessageThat()
- .contains("expected class data for java/util/List, saw java/util/ArrayList instead");
- }
+ VerifyException e = assertThrows(VerifyException.class, () -> c.owner());
+ assertThat(e)
+ .hasMessageThat()
+ .contains("expected class data for java/util/List, saw java/util/ArrayList instead");
}
@Test
@@ -157,12 +214,9 @@
Path lib = temporaryFolder.newFile("NOT_A_JAR").toPath();
MoreFiles.asCharSink(lib, UTF_8).write("hello");
- try {
- ClassPathBinder.bindClasspath(ImmutableList.of(lib));
- fail();
- } catch (IOException e) {
- assertThat(e).hasMessageThat().contains("NOT_A_JAR");
- }
+ IOException e =
+ assertThrows(IOException.class, () -> ClassPathBinder.bindClasspath(ImmutableList.of(lib)));
+ assertThat(e).hasMessageThat().contains("NOT_A_JAR");
}
@Test
@@ -178,4 +232,21 @@
assertThat(new String(classPath.resource("foo/bar/hello.txt").get(), UTF_8)).isEqualTo("hello");
assertThat(classPath.resource("foo/bar/Baz.class")).isNull();
}
+
+ @Test
+ public void resourcesFileManager() throws Exception {
+ Path path = temporaryFolder.newFile("tmp.jar").toPath();
+ try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(path))) {
+ jos.putNextEntry(new JarEntry("foo/bar/hello.txt"));
+ jos.write("hello".getBytes(UTF_8));
+ jos.putNextEntry(new JarEntry("foo/bar/Baz.class"));
+ jos.write("goodbye".getBytes(UTF_8));
+ }
+ StandardJavaFileManager fileManager =
+ ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, ENGLISH, UTF_8);
+ fileManager.setLocation(StandardLocation.CLASS_PATH, ImmutableList.of(path.toFile()));
+ ClassPath classPath = FileManagerClassBinder.adapt(fileManager, StandardLocation.CLASS_PATH);
+ assertThat(new String(classPath.resource("foo/bar/hello.txt").get(), UTF_8)).isEqualTo("hello");
+ assertThat(classPath.resource("foo/bar/NoSuch.class")).isNull();
+ }
}
diff --git a/javatests/com/google/turbine/binder/CtSymClassBinderTest.java b/javatests/com/google/turbine/binder/CtSymClassBinderTest.java
new file mode 100644
index 0000000..2da9f4c
--- /dev/null
+++ b/javatests/com/google/turbine/binder/CtSymClassBinderTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class CtSymClassBinderTest {
+ @Test
+ public void formatReleaseVersion() {
+ ImmutableList.of("5", "6", "7", "8", "9")
+ .forEach(x -> assertThat(CtSymClassBinder.formatReleaseVersion(x)).isEqualTo(x));
+ ImmutableMap.of(
+ "10", "A",
+ "11", "B",
+ "12", "C",
+ "35", "Z")
+ .forEach((k, v) -> assertThat(CtSymClassBinder.formatReleaseVersion(k)).isEqualTo(v));
+ ImmutableList.of("4", "36")
+ .forEach(
+ x ->
+ assertThrows(
+ x,
+ IllegalArgumentException.class,
+ () -> CtSymClassBinder.formatReleaseVersion(x)));
+ }
+}
diff --git a/javatests/com/google/turbine/binder/ProcessingTest.java b/javatests/com/google/turbine/binder/ProcessingTest.java
new file mode 100644
index 0000000..b7091e8
--- /dev/null
+++ b/javatests/com/google/turbine/binder/ProcessingTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import com.google.common.collect.ImmutableList;
+import javax.lang.model.SourceVersion;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ProcessingTest {
+ @Test
+ public void parseSourceVersion() {
+ assertThat(Processing.parseSourceVersion(ImmutableList.of()))
+ .isEqualTo(SourceVersion.latestSupported());
+ assertThat(Processing.parseSourceVersion(ImmutableList.of("-source", "8", "-target", "11")))
+ .isEqualTo(SourceVersion.RELEASE_8);
+ assertThat(Processing.parseSourceVersion(ImmutableList.of("-source", "8", "-source", "7")))
+ .isEqualTo(SourceVersion.RELEASE_7);
+ }
+
+ @Test
+ public void withPrefix() {
+ assertThat(Processing.parseSourceVersion(ImmutableList.of("-source", "1.7")))
+ .isEqualTo(SourceVersion.RELEASE_7);
+ assertThat(Processing.parseSourceVersion(ImmutableList.of("-source", "1.8")))
+ .isEqualTo(SourceVersion.RELEASE_8);
+ }
+
+ @Test
+ public void invalidPrefix() {
+ IllegalArgumentException expected =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> Processing.parseSourceVersion(ImmutableList.of("-source", "1.11")));
+ assertThat(expected).hasMessageThat().contains("invalid -source version: 1.11");
+ }
+
+ @Test
+ public void latestSupported() {
+ String latest = SourceVersion.latestSupported().toString();
+ assertThat(latest).startsWith("RELEASE_");
+ latest = latest.substring("RELEASE_".length());
+ assertThat(Processing.parseSourceVersion(ImmutableList.of("-source", latest)))
+ .isEqualTo(SourceVersion.latestSupported());
+ }
+
+ @Test
+ public void missingArgument() {
+ IllegalArgumentException expected =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> Processing.parseSourceVersion(ImmutableList.of("-source")));
+ assertThat(expected).hasMessageThat().contains("-source requires an argument");
+ }
+
+ @Test
+ public void invalidSourceVersion() {
+ IllegalArgumentException expected =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> Processing.parseSourceVersion(ImmutableList.of("-source", "NOSUCH")));
+ assertThat(expected).hasMessageThat().contains("invalid -source version: NOSUCH");
+ }
+}
diff --git a/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java b/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
index 3e841a5..ec2ebbf 100644
--- a/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
+++ b/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
@@ -17,6 +17,7 @@
package com.google.turbine.binder.bytecode;
import static com.google.common.collect.Iterables.getLast;
+import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.collect.MoreCollectors.onlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH;
@@ -31,6 +32,7 @@
import com.google.turbine.binder.env.CompoundEnv;
import com.google.turbine.binder.env.Env;
import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.model.TurbineFlag;
import com.google.turbine.type.Type;
import com.google.turbine.type.Type.ClassTy;
import java.io.IOException;
@@ -40,6 +42,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -81,9 +84,11 @@
.isEqualTo(new ClassSymbol("java/lang/String"));
}
+ @SuppressWarnings({"deprecation", "TypeNameShadowing", "InlineMeSuggester"})
static class HasMethod {
@Deprecated
- <X, Y extends X, Z extends Throwable> X foo(@Deprecated X bar, Y baz) throws IOException, Z {
+ <X, Y extends X, Z extends Throwable> @Nullable X foo(@Deprecated X bar, Y baz)
+ throws IOException, Z {
return null;
}
@@ -175,6 +180,19 @@
assertThat(getBytecodeBoundClass(C.class, B.class, A.class).methods()).hasSize(1);
}
+ interface D {
+ default void f() {}
+ }
+
+ @Test
+ public void defaultMethods() {
+ assertThat(
+ (getOnlyElement(getBytecodeBoundClass(D.class).methods()).access()
+ & TurbineFlag.ACC_DEFAULT)
+ == TurbineFlag.ACC_DEFAULT)
+ .isTrue();
+ }
+
private static byte[] toByteArrayOrDie(InputStream is) {
try {
return ByteStreams.toByteArray(is);
@@ -201,7 +219,7 @@
.append(
new Env<ClassSymbol, BytecodeBoundClass>() {
@Override
- public BytecodeBoundClass get(ClassSymbol sym) {
+ public @Nullable BytecodeBoundClass get(ClassSymbol sym) {
return map.get(sym);
}
});
diff --git a/javatests/com/google/turbine/binder/lookup/TopLevelIndexTest.java b/javatests/com/google/turbine/binder/lookup/TopLevelIndexTest.java
index 022e47c..861bfef 100644
--- a/javatests/com/google/turbine/binder/lookup/TopLevelIndexTest.java
+++ b/javatests/com/google/turbine/binder/lookup/TopLevelIndexTest.java
@@ -18,7 +18,7 @@
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.turbine.binder.sym.ClassSymbol;
@@ -105,15 +105,8 @@
@Test
public void emptyLookup() {
- LookupKey key = lookupKey(ImmutableList.of("java", "util", "List"));
- key = key.rest();
- key = key.rest();
- try {
- key.rest();
- fail("expected exception");
- } catch (NoSuchElementException e) {
- // expected
- }
+ LookupKey key = lookupKey(ImmutableList.of("java", "util", "List")).rest().rest();
+ assertThrows(NoSuchElementException.class, () -> key.rest());
}
private LookupKey lookupKey(ImmutableList<String> names) {
diff --git a/javatests/com/google/turbine/bytecode/ClassReaderTest.java b/javatests/com/google/turbine/bytecode/ClassReaderTest.java
index fb64541..9a9fdb1 100644
--- a/javatests/com/google/turbine/bytecode/ClassReaderTest.java
+++ b/javatests/com/google/turbine/bytecode/ClassReaderTest.java
@@ -18,6 +18,7 @@
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
+import static java.util.Objects.requireNonNull;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
@@ -34,6 +35,8 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ByteVector;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.ModuleVisitor;
@@ -136,7 +139,7 @@
assertThat(annotation.typeName()).isEqualTo("Ljava/lang/annotation/Retention;");
assertThat(annotation.elementValuePairs()).hasSize(1);
assertThat(annotation.elementValuePairs()).containsKey("value");
- ElementValue value = annotation.elementValuePairs().get("value");
+ ElementValue value = requireNonNull(annotation.elementValuePairs().get("value"));
assertThat(value.kind()).isEqualTo(ElementValue.Kind.ENUM);
ElementValue.EnumConstValue enumValue = (ElementValue.EnumConstValue) value;
assertThat(enumValue.typeName()).isEqualTo("Ljava/lang/annotation/RetentionPolicy;");
@@ -335,4 +338,28 @@
assertThat(p2.descriptor()).isEqualTo("p2");
assertThat(p2.implDescriptors()).containsExactly("p2i1", "p2i2", "p2i3");
}
+
+ @Test
+ public void transitiveJar() {
+ ClassWriter cw = new ClassWriter(0);
+ cw.visit(
+ 52,
+ Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SUPER,
+ "Hello",
+ null,
+ "java/lang/Object",
+ null);
+ cw.visitAttribute(
+ new Attribute("TurbineTransitiveJar") {
+ @Override
+ protected ByteVector write(
+ ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) {
+ ByteVector result = new ByteVector();
+ result.putShort(classWriter.newUTF8("path/to/transitive.jar"));
+ return result;
+ }
+ });
+ ClassFile cf = ClassReader.read(null, cw.toByteArray());
+ assertThat(cf.transitiveJar()).isEqualTo("path/to/transitive.jar");
+ }
}
diff --git a/javatests/com/google/turbine/bytecode/ClassWriterTest.java b/javatests/com/google/turbine/bytecode/ClassWriterTest.java
index 71cf356..f488bbe 100644
--- a/javatests/com/google/turbine/bytecode/ClassWriterTest.java
+++ b/javatests/com/google/turbine/bytecode/ClassWriterTest.java
@@ -90,7 +90,8 @@
byte[] original = Files.readAllBytes(out.resolve("test/Test.class"));
byte[] actual = ClassWriter.writeClass(ClassReader.read(null, original));
- assertThat(AsmUtils.textify(original)).isEqualTo(AsmUtils.textify(actual));
+ assertThat(AsmUtils.textify(original, /* skipDebug= */ true))
+ .isEqualTo(AsmUtils.textify(actual, /* skipDebug= */ true));
}
// Test that >Short.MAX_VALUE constants round-trip through the constant pool.
@@ -145,10 +146,12 @@
byte[] inputBytes = cw.toByteArray();
byte[] outputBytes = ClassWriter.writeClass(ClassReader.read("module-info", inputBytes));
- assertThat(AsmUtils.textify(inputBytes)).isEqualTo(AsmUtils.textify(outputBytes));
+ assertThat(AsmUtils.textify(inputBytes, /* skipDebug= */ true))
+ .isEqualTo(AsmUtils.textify(outputBytes, /* skipDebug= */ true));
// test a round trip
outputBytes = ClassWriter.writeClass(ClassReader.read("module-info", outputBytes));
- assertThat(AsmUtils.textify(inputBytes)).isEqualTo(AsmUtils.textify(outputBytes));
+ assertThat(AsmUtils.textify(inputBytes, /* skipDebug= */ true))
+ .isEqualTo(AsmUtils.textify(outputBytes, /* skipDebug= */ true));
}
}
diff --git a/javatests/com/google/turbine/bytecode/sig/SigIntegrationTest.java b/javatests/com/google/turbine/bytecode/sig/SigIntegrationTest.java
index f3ab8e7..8602fe5 100644
--- a/javatests/com/google/turbine/bytecode/sig/SigIntegrationTest.java
+++ b/javatests/com/google/turbine/bytecode/sig/SigIntegrationTest.java
@@ -17,6 +17,7 @@
package com.google.turbine.bytecode.sig;
import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.io.MoreFiles.getFileExtension;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.base.Splitter;
@@ -70,7 +71,7 @@
Stream<Path> stream = Files.walk(jarfs.getPath("/"))) {
stream
.filter(Files::isRegularFile)
- .filter(p -> p.getFileName().toString().endsWith(".class"))
+ .filter(p -> getFileExtension(p).equals("class"))
.forEachOrdered(consumer);
}
}
@@ -80,7 +81,7 @@
Map<String, ?> env = new HashMap<>();
try (FileSystem fileSystem = FileSystems.newFileSystem(URI.create("jrt:/"), env);
Stream<Path> stream = Files.walk(fileSystem.getPath("/modules"))) {
- stream.filter(p -> p.getFileName().toString().endsWith(".class")).forEachOrdered(consumer);
+ stream.filter(p -> getFileExtension(p).equals("class")).forEachOrdered(consumer);
}
}
}
@@ -93,7 +94,7 @@
try {
new ClassReader(Files.newInputStream(path))
.accept(
- new ClassVisitor(Opcodes.ASM7) {
+ new ClassVisitor(Opcodes.ASM9) {
@Override
public void visit(
int version,
diff --git a/javatests/com/google/turbine/deps/AbstractTransitiveTest.java b/javatests/com/google/turbine/deps/AbstractTransitiveTest.java
deleted file mode 100644
index c5b68ff..0000000
--- a/javatests/com/google/turbine/deps/AbstractTransitiveTest.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * 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.deps;
-
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.turbine.testing.TestClassPaths.optionsWithBootclasspath;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.io.ByteStreams;
-import com.google.turbine.bytecode.ClassFile;
-import com.google.turbine.bytecode.ClassFile.InnerClass;
-import com.google.turbine.bytecode.ClassReader;
-import com.google.turbine.main.Main;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.jar.JarOutputStream;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.Opcodes;
-
-public abstract class AbstractTransitiveTest {
-
- protected abstract Path runTurbine(ImmutableList<Path> sources, ImmutableList<Path> classpath)
- throws IOException;
-
- @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
-
- class SourceBuilder {
- private final Path lib;
- private final ImmutableList.Builder<Path> sources = ImmutableList.builder();
-
- SourceBuilder() throws IOException {
- lib = temporaryFolder.newFolder().toPath();
- }
-
- SourceBuilder addSourceLines(String name, String... lines) throws IOException {
- Path path = lib.resolve(name);
- Files.createDirectories(path.getParent());
- Files.write(path, Arrays.asList(lines), UTF_8);
- sources.add(path);
- return this;
- }
-
- ImmutableList<Path> build() {
- return sources.build();
- }
- }
-
- private Map<String, byte[]> readJar(Path libb) throws IOException {
- Map<String, byte[]> jarEntries = new LinkedHashMap<>();
- try (JarFile jf = new JarFile(libb.toFile())) {
- Enumeration<JarEntry> entries = jf.entries();
- while (entries.hasMoreElements()) {
- JarEntry je = entries.nextElement();
- jarEntries.put(je.getName(), ByteStreams.toByteArray(jf.getInputStream(je)));
- }
- }
- return jarEntries;
- }
-
- @Test
- public void transitive() throws Exception {
- Path liba =
- runTurbine(
- new SourceBuilder()
- .addSourceLines(
- "a/A.java",
- "package a;",
- "import java.util.Map;",
- "public class A {",
- " public @interface Anno {",
- " int x() default 42;",
- " }",
- " public static class Inner {}",
- " public static final int CONST = 42;",
- " public int mutable = 42;",
- " public Map.Entry<String, String> f(Map<String, String> m) {",
- " return m.entrySet().iterator().next();",
- " }",
- "}")
- .build(),
- ImmutableList.of());
-
- Path libb =
- runTurbine(
- new SourceBuilder()
- .addSourceLines("b/B.java", "package b;", "public class B extends a.A {}")
- .build(),
- ImmutableList.of(liba));
-
- // libb repackages A, and any member types
- assertThat(readJar(libb).keySet())
- .containsExactly(
- "b/B.class",
- "META-INF/TRANSITIVE/a/A.class",
- "META-INF/TRANSITIVE/a/A$Anno.class",
- "META-INF/TRANSITIVE/a/A$Inner.class");
-
- ClassFile a = ClassReader.read(null, readJar(libb).get("META-INF/TRANSITIVE/a/A.class"));
- // methods and non-constant fields are removed
- assertThat(getOnlyElement(a.fields()).name()).isEqualTo("CONST");
- assertThat(a.methods()).isEmpty();
- assertThat(Iterables.transform(a.innerClasses(), InnerClass::innerClass))
- .containsExactly("a/A$Anno", "a/A$Inner");
-
- // annotation interface methods are preserved
- assertThat(
- ClassReader.read(null, readJar(libb).get("META-INF/TRANSITIVE/a/A$Anno.class"))
- .methods())
- .hasSize(1);
-
- // A class that references members of the transitive supertype A by simple name
- // compiles cleanly against the repackaged version of A.
- // Explicitly use turbine; javac-turbine doesn't support direct-classpath compilations.
-
- Path libc = temporaryFolder.newFolder().toPath().resolve("out.jar");
- ImmutableList<String> sources =
- new SourceBuilder()
- .addSourceLines(
- "c/C.java",
- "package c;",
- "public class C extends b.B {",
- " @Anno(x = 2) static final Inner i; // a.A$Inner ",
- " static final int X = CONST; // a.A#CONST",
- "}")
- .build()
- .stream()
- .map(Path::toString)
- .collect(toImmutableList());
- Main.compile(
- optionsWithBootclasspath()
- .setSources(sources)
- .setClassPath(
- ImmutableList.of(libb).stream().map(Path::toString).collect(toImmutableList()))
- .setOutput(libc.toString())
- .build());
-
- assertThat(readJar(libc).keySet())
- .containsExactly(
- "c/C.class",
- "META-INF/TRANSITIVE/b/B.class",
- "META-INF/TRANSITIVE/a/A.class",
- "META-INF/TRANSITIVE/a/A$Anno.class",
- "META-INF/TRANSITIVE/a/A$Inner.class");
- }
-
- @Test
- public void anonymous() throws Exception {
- Path liba = temporaryFolder.newFolder().toPath().resolve("out.jar");
- try (OutputStream os = Files.newOutputStream(liba);
- JarOutputStream jos = new JarOutputStream(os)) {
- {
- jos.putNextEntry(new JarEntry("a/A.class"));
- ClassWriter cw = new ClassWriter(0);
- cw.visit(52, Opcodes.ACC_SUPER | Opcodes.ACC_PUBLIC, "a/A", null, "java/lang/Object", null);
- cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
- cw.visitInnerClass("a/A$1", "a/A", null, Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC);
- cw.visitInnerClass("a/A$I", "a/A", "I", Opcodes.ACC_STATIC);
- jos.write(cw.toByteArray());
- }
- {
- jos.putNextEntry(new JarEntry("a/A$1.class"));
- ClassWriter cw = new ClassWriter(0);
- cw.visit(
- 52, Opcodes.ACC_SUPER | Opcodes.ACC_PUBLIC, "a/A$1", null, "java/lang/Object", null);
- cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
- cw.visitInnerClass("a/A$1", "a/A", "I", Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC);
- jos.write(cw.toByteArray());
- }
- {
- jos.putNextEntry(new JarEntry("a/A$I.class"));
- ClassWriter cw = new ClassWriter(0);
- cw.visit(
- 52, Opcodes.ACC_SUPER | Opcodes.ACC_PUBLIC, "a/A$I", null, "java/lang/Object", null);
- cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
- cw.visitInnerClass("a/A$I", "a/A", "I", Opcodes.ACC_STATIC);
- jos.write(cw.toByteArray());
- }
- }
- Path libb =
- runTurbine(
- new SourceBuilder()
- .addSourceLines(
- "b/B.java", //
- "package b;",
- "public class B extends a.A {}")
- .build(),
- ImmutableList.of(liba));
-
- // libb repackages A and any named member types
- assertThat(readJar(libb).keySet())
- .containsExactly(
- "b/B.class", "META-INF/TRANSITIVE/a/A.class", "META-INF/TRANSITIVE/a/A$I.class");
- }
-
- @Test
- public void childClass() throws Exception {
- Path liba =
- runTurbine(
- new SourceBuilder()
- .addSourceLines(
- "a/S.java", //
- "package a;",
- "public class S {}")
- .addSourceLines(
- "a/A.java", //
- "package a;",
- "public class A {",
- " public class I extends S {}",
- "}")
- .build(),
- ImmutableList.of());
-
- Path libb =
- runTurbine(
- new SourceBuilder()
- .addSourceLines(
- "b/B.java", //
- "package b;",
- "public class B extends a.A {",
- " class I extends a.A.I {",
- " }",
- "}")
- .build(),
- ImmutableList.of(liba));
-
- assertThat(readJar(libb).keySet())
- .containsExactly(
- "b/B.class",
- "b/B$I.class",
- "META-INF/TRANSITIVE/a/A.class",
- "META-INF/TRANSITIVE/a/A$I.class",
- "META-INF/TRANSITIVE/a/S.class");
- }
-}
diff --git a/javatests/com/google/turbine/deps/TransitiveTest.java b/javatests/com/google/turbine/deps/TransitiveTest.java
index 2c9f807..f08e899 100644
--- a/javatests/com/google/turbine/deps/TransitiveTest.java
+++ b/javatests/com/google/turbine/deps/TransitiveTest.java
@@ -17,20 +17,281 @@
package com.google.turbine.deps;
import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.turbine.testing.TestClassPaths.optionsWithBootclasspath;
+import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.io.ByteStreams;
+import com.google.protobuf.ExtensionRegistry;
+import com.google.turbine.bytecode.ClassFile;
+import com.google.turbine.bytecode.ClassFile.InnerClass;
+import com.google.turbine.bytecode.ClassReader;
import com.google.turbine.main.Main;
+import com.google.turbine.proto.DepsProto;
+import com.google.turbine.proto.DepsProto.Dependency.Kind;
+import java.io.BufferedInputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
@RunWith(JUnit4.class)
-public class TransitiveTest extends AbstractTransitiveTest {
+public class TransitiveTest {
- @Override
- protected Path runTurbine(ImmutableList<Path> sources, ImmutableList<Path> classpath)
+ @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ class SourceBuilder {
+ private final Path lib;
+ private final ImmutableList.Builder<Path> sources = ImmutableList.builder();
+
+ SourceBuilder() throws IOException {
+ lib = temporaryFolder.newFolder().toPath();
+ }
+
+ SourceBuilder addSourceLines(String name, String... lines) throws IOException {
+ Path path = lib.resolve(name);
+ Files.createDirectories(path.getParent());
+ Files.write(path, Arrays.asList(lines), UTF_8);
+ sources.add(path);
+ return this;
+ }
+
+ ImmutableList<Path> build() {
+ return sources.build();
+ }
+ }
+
+ private static Map<String, byte[]> readJar(Path libb) throws IOException {
+ Map<String, byte[]> jarEntries = new LinkedHashMap<>();
+ try (JarFile jf = new JarFile(libb.toFile())) {
+ Enumeration<JarEntry> entries = jf.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry je = entries.nextElement();
+ jarEntries.put(je.getName(), ByteStreams.toByteArray(jf.getInputStream(je)));
+ }
+ }
+ return jarEntries;
+ }
+
+ @Test
+ public void transitive() throws Exception {
+ Path liba =
+ runTurbine(
+ new SourceBuilder()
+ .addSourceLines(
+ "a/A.java",
+ "package a;",
+ "import java.util.Map;",
+ "public class A {",
+ " public @interface Anno {",
+ " int x() default 42;",
+ " }",
+ " public static class Inner {}",
+ " public static final int CONST = 42;",
+ " public int mutable = 42;",
+ " public Map.Entry<String, String> f(Map<String, String> m) {",
+ " return m.entrySet().iterator().next();",
+ " }",
+ "}")
+ .build(),
+ ImmutableList.of());
+
+ Path libb =
+ runTurbine(
+ new SourceBuilder()
+ .addSourceLines("b/B.java", "package b;", "public class B extends a.A {}")
+ .build(),
+ ImmutableList.of(liba));
+
+ // libb repackages A, and any member types
+ assertThat(readJar(libb).keySet())
+ .containsExactly(
+ "b/B.class",
+ "META-INF/TRANSITIVE/a/A.class",
+ "META-INF/TRANSITIVE/a/A$Anno.class",
+ "META-INF/TRANSITIVE/a/A$Inner.class");
+
+ ClassFile a = ClassReader.read(null, readJar(libb).get("META-INF/TRANSITIVE/a/A.class"));
+ // methods and non-constant fields are removed
+ assertThat(getOnlyElement(a.fields()).name()).isEqualTo("CONST");
+ assertThat(a.methods()).isEmpty();
+ assertThat(Iterables.transform(a.innerClasses(), InnerClass::innerClass))
+ .containsExactly("a/A$Anno", "a/A$Inner");
+
+ // annotation interface methods are preserved
+ assertThat(
+ ClassReader.read(null, readJar(libb).get("META-INF/TRANSITIVE/a/A$Anno.class"))
+ .methods())
+ .hasSize(1);
+
+ // When a.A is repackaged as a transitive class in libb, its 'transitive jar' attribute
+ // should record the path to the original liba jar.
+ assertThat(a.transitiveJar()).isEqualTo(liba.toString());
+ // The transitive jar attribute is only set for transitive classes, not e.g. b.B in libb:
+ ClassFile b = ClassReader.read(null, readJar(libb).get("b/B.class"));
+ assertThat(b.transitiveJar()).isNull();
+
+ // A class that references members of the transitive supertype A by simple name
+ // compiles cleanly against the repackaged version of A.
+ // Explicitly use turbine; javac-turbine doesn't support direct-classpath compilations.
+
+ Path libc = temporaryFolder.newFolder().toPath().resolve("out.jar");
+ Path libcDeps = temporaryFolder.newFolder().toPath().resolve("out.jdeps");
+ ImmutableList<String> sources =
+ new SourceBuilder()
+ .addSourceLines(
+ "c/C.java",
+ "package c;",
+ "public class C extends b.B {",
+ " @Anno(x = 2) static final Inner i; // a.A$Inner ",
+ " static final int X = CONST; // a.A#CONST",
+ "}")
+ .build()
+ .stream()
+ .map(Path::toString)
+ .collect(toImmutableList());
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSources(sources)
+ .setClassPath(
+ ImmutableList.of(libb).stream().map(Path::toString).collect(toImmutableList()))
+ .setOutput(libc.toString())
+ .setOutputDeps(libcDeps.toString())
+ .build());
+
+ assertThat(readJar(libc).keySet())
+ .containsExactly(
+ "c/C.class",
+ "META-INF/TRANSITIVE/b/B.class",
+ "META-INF/TRANSITIVE/a/A.class",
+ "META-INF/TRANSITIVE/a/A$Anno.class",
+ "META-INF/TRANSITIVE/a/A$Inner.class");
+
+ // liba is recorded as an explicit dep, even thought it's only present as a transitive class
+ // repackaged in lib
+ assertThat(readDeps(libcDeps))
+ .containsExactly(liba.toString(), Kind.EXPLICIT, libb.toString(), Kind.EXPLICIT);
+ }
+
+ private static ImmutableMap<String, Kind> readDeps(Path libcDeps) throws IOException {
+ DepsProto.Dependencies.Builder deps = DepsProto.Dependencies.newBuilder();
+ try (InputStream is = new BufferedInputStream(Files.newInputStream(libcDeps))) {
+ deps.mergeFrom(is, ExtensionRegistry.getEmptyRegistry());
+ }
+ return deps.getDependencyList().stream()
+ .collect(toImmutableMap(d -> d.getPath(), d -> d.getKind()));
+ }
+
+ @Test
+ public void anonymous() throws Exception {
+ Path liba = temporaryFolder.newFolder().toPath().resolve("out.jar");
+ try (OutputStream os = Files.newOutputStream(liba);
+ JarOutputStream jos = new JarOutputStream(os)) {
+ {
+ jos.putNextEntry(new JarEntry("a/A.class"));
+ ClassWriter cw = new ClassWriter(0);
+ cw.visit(52, Opcodes.ACC_SUPER | Opcodes.ACC_PUBLIC, "a/A", null, "java/lang/Object", null);
+ cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
+ cw.visitInnerClass("a/A$1", "a/A", null, Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC);
+ cw.visitInnerClass("a/A$I", "a/A", "I", Opcodes.ACC_STATIC);
+ jos.write(cw.toByteArray());
+ }
+ {
+ jos.putNextEntry(new JarEntry("a/A$1.class"));
+ ClassWriter cw = new ClassWriter(0);
+ cw.visit(
+ 52, Opcodes.ACC_SUPER | Opcodes.ACC_PUBLIC, "a/A$1", null, "java/lang/Object", null);
+ cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
+ cw.visitInnerClass("a/A$1", "a/A", "I", Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC);
+ jos.write(cw.toByteArray());
+ }
+ {
+ jos.putNextEntry(new JarEntry("a/A$I.class"));
+ ClassWriter cw = new ClassWriter(0);
+ cw.visit(
+ 52, Opcodes.ACC_SUPER | Opcodes.ACC_PUBLIC, "a/A$I", null, "java/lang/Object", null);
+ cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
+ cw.visitInnerClass("a/A$I", "a/A", "I", Opcodes.ACC_STATIC);
+ jos.write(cw.toByteArray());
+ }
+ }
+ Path libb =
+ runTurbine(
+ new SourceBuilder()
+ .addSourceLines(
+ "b/B.java", //
+ "package b;",
+ "public class B extends a.A {}")
+ .build(),
+ ImmutableList.of(liba));
+
+ // libb repackages A and any named member types
+ assertThat(readJar(libb).keySet())
+ .containsExactly(
+ "b/B.class", "META-INF/TRANSITIVE/a/A.class", "META-INF/TRANSITIVE/a/A$I.class");
+ }
+
+ @Test
+ public void childClass() throws Exception {
+ Path liba =
+ runTurbine(
+ new SourceBuilder()
+ .addSourceLines(
+ "a/S.java", //
+ "package a;",
+ "public class S {}")
+ .addSourceLines(
+ "a/A.java", //
+ "package a;",
+ "public class A {",
+ " public class I extends S {}",
+ "}")
+ .build(),
+ ImmutableList.of());
+
+ Path libb =
+ runTurbine(
+ new SourceBuilder()
+ .addSourceLines(
+ "b/B.java", //
+ "package b;",
+ "public class B extends a.A {",
+ " class I extends a.A.I {",
+ " }",
+ "}")
+ .build(),
+ ImmutableList.of(liba));
+
+ assertThat(readJar(libb).keySet())
+ .containsExactly(
+ "b/B.class",
+ "b/B$I.class",
+ "META-INF/TRANSITIVE/a/A.class",
+ "META-INF/TRANSITIVE/a/A$I.class",
+ "META-INF/TRANSITIVE/a/S.class");
+ }
+
+ private Path runTurbine(ImmutableList<Path> sources, ImmutableList<Path> classpath)
throws IOException {
Path out = temporaryFolder.newFolder().toPath().resolve("out.jar");
Main.compile(
diff --git a/javatests/com/google/turbine/lower/IntegrationTestSupport.java b/javatests/com/google/turbine/lower/IntegrationTestSupport.java
index a03473d..744f341 100644
--- a/javatests/com/google/turbine/lower/IntegrationTestSupport.java
+++ b/javatests/com/google/turbine/lower/IntegrationTestSupport.java
@@ -17,8 +17,10 @@
package com.google.turbine.lower;
import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.io.MoreFiles.getFileExtension;
import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
@@ -82,7 +84,7 @@
import org.objectweb.asm.tree.TypeAnnotationNode;
/** Support for bytecode diffing-integration tests. */
-public class IntegrationTestSupport {
+public final class IntegrationTestSupport {
/**
* Normalizes order of members, attributes, and constant pool entries, to allow diffing bytecode.
@@ -410,20 +412,21 @@
final Set<String> classes1 = classes;
new SignatureReader(signature)
.accept(
- new SignatureVisitor(Opcodes.ASM7) {
+ new SignatureVisitor(Opcodes.ASM9) {
private final Set<String> classes = classes1;
// class signatures may contain type arguments that contain class signatures
Deque<List<String>> pieces = new ArrayDeque<>();
@Override
public void visitInnerClassType(String name) {
- pieces.peek().add(name);
+ pieces.element().add(name);
}
@Override
public void visitClassType(String name) {
- pieces.push(new ArrayList<>());
- pieces.peek().add(name);
+ List<String> classType = new ArrayList<>();
+ classType.add(name);
+ pieces.push(classType);
}
@Override
@@ -510,7 +513,7 @@
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs)
throws IOException {
- if (path.getFileName().toString().endsWith(".class")) {
+ if (getFileExtension(path).equals("class")) {
classes.add(path);
}
return FileVisitResult.CONTINUE;
@@ -551,7 +554,9 @@
fileManager.setLocationFromPaths(StandardLocation.CLASS_OUTPUT, ImmutableList.of(out));
fileManager.setLocationFromPaths(StandardLocation.CLASS_PATH, classpath);
fileManager.setLocationFromPaths(StandardLocation.locationFor("MODULE_PATH"), classpath);
- if (inputs.stream().filter(i -> i.getFileName().toString().equals("module-info.java")).count()
+ if (inputs.stream()
+ .filter(i -> requireNonNull(i.getFileName()).toString().equals("module-info.java"))
+ .count()
> 1) {
// multi-module mode
fileManager.setLocationFromPaths(
@@ -578,7 +583,7 @@
na = na.substring(1);
}
sb.append(String.format("=== %s ===\n", na));
- sb.append(AsmUtils.textify(compiled.get(key)));
+ sb.append(AsmUtils.textify(compiled.get(key), /* skipDebug= */ true));
}
return sb.toString();
}
@@ -634,4 +639,6 @@
return new TestInput(sources, classes);
}
}
+
+ private IntegrationTestSupport() {}
}
diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
index 85c3450..ab4e0ee 100644
--- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
@@ -17,12 +17,11 @@
package com.google.turbine.lower;
import static com.google.common.truth.Truth.assertThat;
-import static java.nio.charset.StandardCharsets.UTF_8;
+import static com.google.turbine.testing.TestResources.getResource;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
-import com.google.common.io.ByteStreams;
import java.io.IOError;
import java.io.IOException;
import java.nio.file.Files;
@@ -45,143 +44,30 @@
@Parameters(name = "{index}: {0}")
public static Iterable<Object[]> parameters() {
String[] testCases = {
+ // keep-sorted start
+ "B33513475.test",
+ "B33513475b.test",
+ "B33513475c.test",
+ "B70953542.test",
+ "B8056066.test",
+ "B8056066b.test",
+ "B8075274.test",
+ "B8148131.test",
"abstractenum.test",
"access1.test",
- "anonymous.test",
- "asset.test",
- "outerparam.test",
- "basic_field.test",
- "basic_nested.test",
- "bcp.test",
- "builder.test",
- "byte.test",
- "byte2.test",
- "circ_cvar.test",
- "clash.test",
- "ctorvis.test",
- "cvar_qualified.test",
- "cycle.test",
- "default_fbound.test",
- "default_rawfbound.test",
- "default_simple.test",
- "enum1.test",
- "enumctor.test",
- "enumctor2.test",
- "enumimpl.test",
- "enumingeneric.test",
- "enuminner.test",
- "enumint.test",
- "enumint2.test",
- "enumint3.test",
- "enumint_byte.test",
- "enumint_objectmethod.test",
- "enumint_objectmethod2.test",
- "enumint_objectmethod_raw.test",
- "enuminthacks.test",
- "enumstat.test",
- "erasurebound.test",
- "existingctor.test",
- "extend_inner.test",
- "extends_bound.test",
- "extends_otherbound.test",
- "extendsandimplements.test",
- "extrainnerclass.test",
- "fbound.test",
- "firstcomparator.test",
- "fuse.test",
- "genericarrayfield.test",
- "genericexn.test",
- "genericexn2.test",
- "genericret.test",
- "hierarchy.test",
- "ibound.test",
- "icu.test",
- "icu2.test",
- "importinner.test",
- "innerctor.test",
- "innerenum.test",
- "innerint.test",
- "innerstaticgeneric.test",
- "interfacemem.test",
- "interfaces.test",
- "lexical.test",
- "lexical2.test",
- "lexical4.test",
- "list.test",
- "loopthroughb.test",
- "mapentry.test",
- "member.test",
- "mods.test",
- "morefields.test",
- "moremethods.test",
- "multifield.test",
- "nested.test",
- "nested2.test",
- "one.test",
- "outer.test",
- "packageprivateprotectedinner.test",
- "param_bound.test",
- "privateinner.test",
- "proto.test",
- "proto2.test",
- "qual.test",
- "raw.test",
- "raw2.test",
- "rawfbound.test",
- "rek.test",
- "samepkg.test",
- "self.test",
- "semi.test",
- "simple.test",
- "simplemethod.test",
- "string.test",
- "superabstract.test",
- "supplierfunction.test",
- "tbound.test",
- "typaram.test",
- "tyvarfield.test",
- "useextend.test",
- "vanillaexception.test",
- "varargs.test",
- "wild.test",
- "bytenoncanon.test",
- "canon.test",
- "genericnoncanon.test",
- "genericnoncanon1.test",
- "genericnoncanon10.test",
- "genericnoncanon2.test",
- "genericnoncanon3.test",
- "genericnoncanon4.test",
- "genericnoncanon5.test",
- "genericnoncanon6.test",
- "genericnoncanon8.test",
- "genericnoncanon9.test",
- "genericnoncanon_byte.test",
- "genericnoncanon_method3.test",
- "noncanon.test",
- "rawcanon.test",
- "wildboundcanon.test",
- "wildcanon.test",
+ "anno_const_coerce.test",
+ "anno_const_scope.test",
+ "anno_nested.test",
+ "anno_repeated.test",
+ "anno_self_const.test",
+ "anno_void.test",
"annoconstvis.test",
- "const_byte.test",
- "const_char.test",
- "const_field.test",
- "const_types.test",
- "const_underscore.test",
- "constlevel.test",
- "constpack.test",
- "importconst.test",
- "const.test",
- "const_all.test",
- "const_arith.test",
- "const_conditional.test",
- "const_moreexpr.test",
- "const_multi.test",
- "field_anno.test",
"annotation_bool_default.test",
"annotation_class_default.test",
+ "annotation_clinit.test",
"annotation_declaration.test",
"annotation_enum_default.test",
+ "annotation_scope.test",
"annotations_default.test",
"annouse.test",
"annouse10.test",
@@ -201,116 +87,233 @@
"annouse8.test",
"annouse9.test",
"annovis.test",
- "complex_param_anno.test",
- "enummemberanno.test",
- "innerannodecl.test",
- "source_anno_retention.test",
- "anno_nested.test",
- "nested_member_import.test",
- "nested_member_import_noncanon.test",
- "unary.test",
- "hex_int.test",
- "const_conv.test",
+ "anonymous.test",
+ "array_class_literal.test",
+ "ascii_sub.test",
+ "asset.test",
+ "basic_field.test",
+ "basic_nested.test",
+ "bcp.test",
"bmethod.test",
- "prim_class.test",
- "wild2.test",
- "wild3.test",
- "const_hiding.test",
- "interface_field.test",
- "concat.test",
- "static_type_import.test",
- "non_const.test",
"bounds.test",
- "cast_tail.test",
- "marker.test",
- "interface_method.test",
- "raw_canon.test",
- "float_exponent.test",
"boxed_const.test",
- "package_info.test",
- "import_wild_order.test",
+ "builder.test",
+ "byte.test",
+ "byte2.test",
+ "bytecode_boolean_const.test",
+ "bytenoncanon.test",
+ "c_array.test",
+ "canon.test",
+ "canon_class_header.test",
"canon_recursive.test",
+ "cast_tail.test",
+ "circ_cvar.test",
+ "clash.test",
+ "complex_param_anno.test",
+ "concat.test",
+ "const.test",
+ "const_all.test",
+ "const_arith.test",
+ "const_boxed.test",
+ "const_byte.test",
+ "const_char.test",
+ "const_conditional.test",
+ "const_conv.test",
+ "const_field.test",
+ "const_hiding.test",
+ "const_moreexpr.test",
+ "const_multi.test",
+ "const_nonfinal.test",
+ "const_octal_underscore.test",
+ "const_types.test",
+ "const_underscore.test",
+ "constlevel.test",
+ "constpack.test",
+ "ctor_anno.test",
+ "ctorvis.test",
+ "cvar_qualified.test",
+ "cycle.test",
+ "default_fbound.test",
+ "default_rawfbound.test",
+ "default_simple.test",
+ "deficient_types_classfile.test",
+ "dollar.test",
+ "enum1.test",
+ "enum_abstract.test",
+ "enum_final.test",
+ "enumctor.test",
+ "enumctor2.test",
+ "enumimpl.test",
+ "enumingeneric.test",
+ "enuminner.test",
+ "enumint.test",
+ "enumint2.test",
+ "enumint3.test",
+ "enumint_byte.test",
+ "enumint_objectmethod.test",
+ "enumint_objectmethod2.test",
+ "enumint_objectmethod_raw.test",
+ "enuminthacks.test",
+ "enummemberanno.test",
+ "enumstat.test",
+ "erasurebound.test",
+ "existingctor.test",
+ "extend_inner.test",
+ "extends_bound.test",
+ "extends_otherbound.test",
+ "extendsandimplements.test",
+ "extrainnerclass.test",
+ "fbound.test",
+ "field_anno.test",
+ "firstcomparator.test",
+ "float_exponent.test",
+ "fuse.test",
+ "genericarrayfield.test",
+ "genericexn.test",
+ "genericexn2.test",
+ "genericnoncanon.test",
+ "genericnoncanon1.test",
+ "genericnoncanon10.test",
+ "genericnoncanon2.test",
+ "genericnoncanon3.test",
+ "genericnoncanon4.test",
+ "genericnoncanon5.test",
+ "genericnoncanon6.test",
+ "genericnoncanon8.test",
+ "genericnoncanon9.test",
+ "genericnoncanon_byte.test",
+ "genericnoncanon_method3.test",
+ "genericret.test",
+ "hex_int.test",
+ "hierarchy.test",
+ "ibound.test",
+ "icu.test",
+ "icu2.test",
+ "import_wild_order.test",
+ "importconst.test",
+ "importinner.test",
+ "inner_static.test",
+ "innerannodecl.test",
+ "innerclassanno.test",
+ "innerctor.test",
+ "innerenum.test",
+ "innerint.test",
+ "innerstaticgeneric.test",
+ "interface_field.test",
+ "interface_member_public.test",
+ "interface_method.test",
+ "interfacemem.test",
+ "interfaces.test",
// TODO(cushon): crashes ASM, see:
// https://gitlab.ow2.org/asm/asm/issues/317776
// "canon_array.test",
"java_lang_object.test",
- "visible_package.test",
- "visible_private.test",
- "visible_same_package.test",
- "private_member.test",
- "visible_nested.test",
- "visible_qualified.test",
- "ascii_sub.test",
- "bytecode_boolean_const.test",
- "tyvar_bound.test",
- "type_anno_hello.test",
- "type_anno_array_dims.test",
- "nonconst_unary_expression.test",
- "type_anno_ambiguous.test",
- "type_anno_ambiguous_param.test",
- "unicode.test",
- "annotation_scope.test",
- "visible_package_private_toplevel.test",
- "receiver_param.test",
- "static_member_type_import.test",
- "type_anno_qual.test",
- "array_class_literal.test",
- "underscore_literal.test",
- "c_array.test",
- "type_anno_retention.test",
- "member_import_clash.test",
- "anno_repeated.test",
- "long_expression.test",
- "const_nonfinal.test",
- "enum_abstract.test",
- "deficient_types_classfile.test",
- "ctor_anno.test",
- "anno_const_coerce.test",
- "const_octal_underscore.test",
- "const_boxed.test",
- "interface_member_public.test",
"javadoc_deprecated.test",
- "strictfp.test",
- "type_anno_raw.test",
- "inner_static.test",
- "innerclassanno.test",
- "type_anno_parameter_index.test",
- "anno_const_scope.test",
- "type_anno_ambiguous_qualified.test",
- "type_anno_array_bound.test",
- "type_anno_return.test",
- "type_anno_order.test",
- "canon_class_header.test",
- "type_anno_receiver.test",
- "enum_final.test",
- "dollar.test",
- "typaram_lookup.test",
- "typaram_lookup_enclosing.test",
- "B33513475.test",
- "B33513475b.test",
- "B33513475c.test",
- "noncanon_static_wild.test",
- "B8075274.test",
- "B8148131.test",
- "B8056066.test",
- "B8056066b.test",
- "source_bootclasspath_order.test",
- "anno_self_const.test",
- "type_anno_cstyle_array_dims.test",
- "packagedecl.test",
- "static_member_type_import_recursive.test",
- "B70953542.test",
+ "lexical.test",
+ "lexical2.test",
+ "lexical4.test",
+ "list.test",
+ "local.test",
+ "long_expression.test",
+ "loopthroughb.test",
+ "mapentry.test",
+ "marker.test",
+ "member.test",
+ "member_import_clash.test",
// TODO(cushon): support for source level 9 in integration tests
// "B74332665.test",
"memberimport.test",
- "type_anno_c_array.test",
+ "mods.test",
+ "morefields.test",
+ "moremethods.test",
+ "multifield.test",
+ "nested.test",
+ "nested2.test",
+ "nested_member_import.test",
+ "nested_member_import_noncanon.test",
+ "non_const.test",
+ "noncanon.test",
+ "noncanon_static_wild.test",
+ "nonconst_unary_expression.test",
+ "one.test",
+ "outer.test",
+ "outerparam.test",
+ "package_info.test",
+ "packagedecl.test",
+ "packageprivateprotectedinner.test",
+ "param_bound.test",
+ "prim_class.test",
+ "private_member.test",
+ "privateinner.test",
+ "proto.test",
+ "proto2.test",
+ "qual.test",
+ "raw.test",
+ "raw2.test",
+ "raw_canon.test",
+ "rawcanon.test",
+ "rawfbound.test",
+ "receiver_param.test",
+ "rek.test",
+ "samepkg.test",
+ "self.test",
+ "semi.test",
// https://bugs.openjdk.java.net/browse/JDK-8054064 ?
"shadow_inherited.test",
+ "simple.test",
+ "simplemethod.test",
+ "source_anno_retention.test",
+ "source_bootclasspath_order.test",
"static_final_boxed.test",
- "anno_void.test",
- "tyanno_varargs.test",
+ "static_member_type_import.test",
+ "static_member_type_import_recursive.test",
+ "static_type_import.test",
+ "strictfp.test",
+ "string.test",
+ "superabstract.test",
+ "supplierfunction.test",
+ "tbound.test",
"tyanno_inner.test",
- "local.test",
+ "tyanno_varargs.test",
+ "typaram.test",
+ "typaram_lookup.test",
+ "typaram_lookup_enclosing.test",
+ "type_anno_ambiguous.test",
+ "type_anno_ambiguous_param.test",
+ "type_anno_ambiguous_qualified.test",
+ "type_anno_array_bound.test",
+ "type_anno_array_dims.test",
+ "type_anno_c_array.test",
+ "type_anno_cstyle_array_dims.test",
+ "type_anno_hello.test",
+ "type_anno_order.test",
+ "type_anno_parameter_index.test",
+ "type_anno_qual.test",
+ "type_anno_raw.test",
+ "type_anno_receiver.test",
+ "type_anno_retention.test",
+ "type_anno_return.test",
+ "tyvar_bound.test",
+ "tyvarfield.test",
+ "unary.test",
+ "underscore_literal.test",
+ "unicode.test",
+ "unicode_pkg.test",
+ "useextend.test",
+ "vanillaexception.test",
+ "varargs.test",
+ "visible_nested.test",
+ "visible_package.test",
+ "visible_package_private_toplevel.test",
+ "visible_private.test",
+ "visible_qualified.test",
+ "visible_same_package.test",
+ "wild.test",
+ "wild2.test",
+ "wild3.test",
+ "wildboundcanon.test",
+ "wildcanon.test",
+ // keep-sorted end
};
List<Object[]> tests =
ImmutableList.copyOf(testCases).stream().map(x -> new Object[] {x}).collect(toList());
@@ -344,10 +347,7 @@
public void test() throws Exception {
IntegrationTestSupport.TestInput input =
- IntegrationTestSupport.TestInput.parse(
- new String(
- ByteStreams.toByteArray(getClass().getResourceAsStream("testdata/" + test)),
- UTF_8));
+ IntegrationTestSupport.TestInput.parse(getResource(getClass(), "testdata/" + test));
ImmutableList<Path> classpathJar = ImmutableList.of();
if (!input.classes.isEmpty()) {
diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java
index 8151e81..d74e829 100644
--- a/javatests/com/google/turbine/lower/LowerTest.java
+++ b/javatests/com/google/turbine/lower/LowerTest.java
@@ -18,13 +18,13 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.junit.Assert.fail;
+import static com.google.turbine.testing.TestResources.getResource;
+import static java.util.Objects.requireNonNull;
+import static org.junit.Assert.assertThrows;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.common.io.ByteStreams;
import com.google.turbine.binder.Binder;
import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.ClassPathBinder;
@@ -233,18 +233,10 @@
TURBINE_BOOTCLASSPATH.env())
.bytes();
- assertThat(AsmUtils.textify(bytes.get("test/Test")))
- .isEqualTo(
- new String(
- ByteStreams.toByteArray(
- LowerTest.class.getResourceAsStream("testdata/golden/outer.txt")),
- UTF_8));
- assertThat(AsmUtils.textify(bytes.get("test/Test$Inner")))
- .isEqualTo(
- new String(
- ByteStreams.toByteArray(
- LowerTest.class.getResourceAsStream("testdata/golden/inner.txt")),
- UTF_8));
+ assertThat(AsmUtils.textify(bytes.get("test/Test"), /* skipDebug= */ false))
+ .isEqualTo(getResource(LowerTest.class, "testdata/golden/outer.txt"));
+ assertThat(AsmUtils.textify(bytes.get("test/Test$Inner"), /* skipDebug= */ false))
+ .isEqualTo(getResource(LowerTest.class, "testdata/golden/inner.txt"));
}
@Test
@@ -268,7 +260,7 @@
List<String> attributes = new ArrayList<>();
new ClassReader(lowered.get("Test$Inner$InnerMost"))
.accept(
- new ClassVisitor(Opcodes.ASM7) {
+ new ClassVisitor(Opcodes.ASM9) {
@Override
public void visitInnerClass(
String name, String outerName, String innerName, int access) {
@@ -285,10 +277,7 @@
public void wildArrayElement() throws Exception {
IntegrationTestSupport.TestInput input =
IntegrationTestSupport.TestInput.parse(
- new String(
- ByteStreams.toByteArray(
- getClass().getResourceAsStream("testdata/canon_array.test")),
- UTF_8));
+ getResource(getClass(), "testdata/canon_array.test"));
Map<String, byte[]> actual =
IntegrationTestSupport.runTurbine(input.sources, ImmutableList.of());
@@ -346,11 +335,11 @@
TypePath[] path = new TypePath[1];
new ClassReader(lowered.get("Test"))
.accept(
- new ClassVisitor(Opcodes.ASM7) {
+ new ClassVisitor(Opcodes.ASM9) {
@Override
public FieldVisitor visitField(
int access, String name, String desc, String signature, Object value) {
- return new FieldVisitor(Opcodes.ASM7) {
+ return new FieldVisitor(Opcodes.ASM9) {
@Override
public AnnotationVisitor visitTypeAnnotation(
int typeRef, TypePath typePath, String desc, boolean visible) {
@@ -397,7 +386,7 @@
Map<String, Object> values = new LinkedHashMap<>();
new ClassReader(actual.get("Test"))
.accept(
- new ClassVisitor(Opcodes.ASM7) {
+ new ClassVisitor(Opcodes.ASM9) {
@Override
public FieldVisitor visitField(
int access, String name, String desc, String signature, Object value) {
@@ -424,7 +413,7 @@
int[] acc = {0};
new ClassReader(lowered.get("Test"))
.accept(
- new ClassVisitor(Opcodes.ASM7) {
+ new ClassVisitor(Opcodes.ASM9) {
@Override
public void visit(
int version,
@@ -522,16 +511,11 @@
Path libJar = temporaryFolder.newFile("lib.jar").toPath();
try (OutputStream os = Files.newOutputStream(libJar);
JarOutputStream jos = new JarOutputStream(os)) {
- jos.putNextEntry(new JarEntry("A$M.class"));
- jos.write(lib.get("A$M"));
- jos.putNextEntry(new JarEntry("A$M$I.class"));
- jos.write(lib.get("A$M$I"));
- jos.putNextEntry(new JarEntry("B.class"));
- jos.write(lib.get("B"));
- jos.putNextEntry(new JarEntry("B$BM.class"));
- jos.write(lib.get("B$BM"));
- jos.putNextEntry(new JarEntry("B$BM$BI.class"));
- jos.write(lib.get("B$BM$BI"));
+ write(jos, lib, "A$M");
+ write(jos, lib, "A$M$I");
+ write(jos, lib, "B");
+ write(jos, lib, "B$BM");
+ write(jos, lib, "B$BM$BI");
}
ImmutableMap<String, String> sources =
@@ -544,14 +528,13 @@
"}"))
.build();
- try {
- IntegrationTestSupport.runTurbine(sources, ImmutableList.of(libJar));
- fail();
- } catch (TurbineError error) {
- assertThat(error)
- .hasMessageThat()
- .contains("Test.java: error: could not locate class file for A");
- }
+ TurbineError error =
+ assertThrows(
+ TurbineError.class,
+ () -> IntegrationTestSupport.runTurbine(sources, ImmutableList.of(libJar)));
+ assertThat(error)
+ .hasMessageThat()
+ .contains("Test.java: error: could not locate class file for A");
}
@Test
@@ -579,16 +562,11 @@
Path libJar = temporaryFolder.newFile("lib.jar").toPath();
try (OutputStream os = Files.newOutputStream(libJar);
JarOutputStream jos = new JarOutputStream(os)) {
- jos.putNextEntry(new JarEntry("A$M.class"));
- jos.write(lib.get("A$M"));
- jos.putNextEntry(new JarEntry("A$M$I.class"));
- jos.write(lib.get("A$M$I"));
- jos.putNextEntry(new JarEntry("B.class"));
- jos.write(lib.get("B"));
- jos.putNextEntry(new JarEntry("B$BM.class"));
- jos.write(lib.get("B$BM"));
- jos.putNextEntry(new JarEntry("B$BM$BI.class"));
- jos.write(lib.get("B$BM$BI"));
+ write(jos, lib, "A$M");
+ write(jos, lib, "A$M$I");
+ write(jos, lib, "B");
+ write(jos, lib, "B$BM");
+ write(jos, lib, "B$BM$BI");
}
ImmutableMap<String, String> sources =
@@ -603,18 +581,15 @@
"}"))
.build();
- try {
- IntegrationTestSupport.runTurbine(sources, ImmutableList.of(libJar));
- fail();
- } catch (TurbineError error) {
- assertThat(error)
- .hasMessageThat()
- .contains(
- lines(
- "Test.java:3: error: could not locate class file for A",
- " I i;",
- " ^"));
- }
+ TurbineError error =
+ assertThrows(
+ TurbineError.class,
+ () -> IntegrationTestSupport.runTurbine(sources, ImmutableList.of(libJar)));
+ assertThat(error)
+ .hasMessageThat()
+ .contains(
+ lines(
+ "Test.java:3: error: could not locate class file for A", " I i;", " ^"));
}
// If an element incorrectly has multiple visibility modifiers, pick one, and rely on javac to
@@ -629,7 +604,7 @@
int[] testAccess = {0};
new ClassReader(lowered.get("Test"))
.accept(
- new ClassVisitor(Opcodes.ASM7) {
+ new ClassVisitor(Opcodes.ASM9) {
@Override
public void visit(
int version,
@@ -649,4 +624,9 @@
static String lines(String... lines) {
return Joiner.on(System.lineSeparator()).join(lines);
}
+
+ static void write(JarOutputStream jos, Map<String, byte[]> lib, String name) throws IOException {
+ jos.putNextEntry(new JarEntry(name + ".class"));
+ jos.write(requireNonNull(lib.get(name)));
+ }
}
diff --git a/javatests/com/google/turbine/lower/ModuleIntegrationTest.java b/javatests/com/google/turbine/lower/ModuleIntegrationTest.java
index 03c6fb7..f2c0bbf 100644
--- a/javatests/com/google/turbine/lower/ModuleIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/ModuleIntegrationTest.java
@@ -18,12 +18,11 @@
import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_VERSION;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
-import static java.nio.charset.StandardCharsets.UTF_8;
+import static com.google.turbine.testing.TestResources.getResource;
import static java.util.stream.Collectors.toList;
import static org.junit.Assert.assertEquals;
import com.google.common.collect.ImmutableList;
-import com.google.common.io.ByteStreams;
import com.google.turbine.binder.CtSymClassBinder;
import com.google.turbine.binder.JimageClassBinder;
import java.nio.file.Files;
@@ -68,10 +67,7 @@
}
IntegrationTestSupport.TestInput input =
- IntegrationTestSupport.TestInput.parse(
- new String(
- ByteStreams.toByteArray(getClass().getResourceAsStream("moduletestdata/" + test)),
- UTF_8));
+ IntegrationTestSupport.TestInput.parse(getResource(getClass(), "moduletestdata/" + test));
ImmutableList<Path> classpathJar = ImmutableList.of();
if (!input.classes.isEmpty()) {
diff --git a/javatests/com/google/turbine/lower/testdata/annotation_clinit.test b/javatests/com/google/turbine/lower/testdata/annotation_clinit.test
new file mode 100644
index 0000000..7419ed6
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/annotation_clinit.test
@@ -0,0 +1,18 @@
+%%% pkg/Anno.java %%%
+package pkg;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Anno {
+ String CONSTANT = Anno.class.toString();
+
+ String value() default "";
+}
+
+=== pkg/T.java ===
+package pkg;
+
+@Anno
+class T {}
diff --git a/javatests/com/google/turbine/lower/testdata/unicode_pkg.test b/javatests/com/google/turbine/lower/testdata/unicode_pkg.test
new file mode 100644
index 0000000..85d38d9
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/unicode_pkg.test
@@ -0,0 +1,4 @@
+=== Test.java ===
+package pkg𐀀.test;
+
+class Test {}
diff --git a/javatests/com/google/turbine/main/MainTest.java b/javatests/com/google/turbine/main/MainTest.java
index 5d47632..57940f3 100644
--- a/javatests/com/google/turbine/main/MainTest.java
+++ b/javatests/com/google/turbine/main/MainTest.java
@@ -23,7 +23,8 @@
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.turbine.testing.TestClassPaths.optionsWithBootclasspath;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.junit.Assert.fail;
+import static java.util.Objects.requireNonNull;
+import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -40,8 +41,11 @@
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.io.Writer;
+import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Enumeration;
@@ -88,16 +92,16 @@
}
Path output = temporaryFolder.newFile("output.jar").toPath();
- try {
- Main.compile(
- optionsWithBootclasspath()
- .setSourceJars(ImmutableList.of(sourcesa.toString(), sourcesb.toString()))
- .setOutput(output.toString())
- .build());
- fail();
- } catch (TurbineError e) {
- assertThat(e).hasMessageThat().contains("error: duplicate declaration of Test");
- }
+ TurbineError e =
+ assertThrows(
+ TurbineError.class,
+ () ->
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSourceJars(ImmutableList.of(sourcesa.toString(), sourcesb.toString()))
+ .setOutput(output.toString())
+ .build()));
+ assertThat(e).hasMessageThat().contains("error: duplicate declaration of Test");
}
@Test
@@ -204,8 +208,8 @@
assertThat(entries.map(JarEntry::getName))
.containsAtLeast("META-INF/", "META-INF/MANIFEST.MF");
}
- Manifest manifest = jarFile.getManifest();
- Attributes attributes = manifest.getMainAttributes();
+ Manifest manifest = requireNonNull(jarFile.getManifest());
+ Attributes attributes = requireNonNull(manifest.getMainAttributes());
ImmutableMap<String, ?> entries =
attributes.entrySet().stream()
.collect(toImmutableMap(e -> e.getKey().toString(), Map.Entry::getValue));
@@ -215,12 +219,15 @@
"Manifest-Version", "1.0",
"Target-Label", "//foo:foo",
"Injecting-Rule-Kind", "foo_library");
- assertThat(jarFile.getEntry(JarFile.MANIFEST_NAME).getLastModifiedTime().toInstant())
+ assertThat(
+ requireNonNull(jarFile.getEntry(JarFile.MANIFEST_NAME))
+ .getLastModifiedTime()
+ .toInstant())
.isEqualTo(
LocalDateTime.of(2010, 1, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant());
}
try (JarFile jarFile = new JarFile(gensrcOutput.toFile())) {
- Manifest manifest = jarFile.getManifest();
+ Manifest manifest = requireNonNull(jarFile.getManifest());
Attributes attributes = manifest.getMainAttributes();
ImmutableMap<String, ?> entries =
attributes.entrySet().stream()
@@ -257,16 +264,16 @@
Path output = temporaryFolder.newFile("output.jar").toPath();
- try {
- Main.compile(
- TurbineOptions.builder()
- .setSources(ImmutableList.of(src.toString()))
- .setOutput(output.toString())
- .build());
- fail();
- } catch (IllegalArgumentException expected) {
- assertThat(expected).hasMessageThat().contains("java.lang");
- }
+ IllegalArgumentException expected =
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ Main.compile(
+ TurbineOptions.builder()
+ .setSources(ImmutableList.of(src.toString()))
+ .setOutput(output.toString())
+ .build()));
+ assertThat(expected).hasMessageThat().contains("java.lang");
}
@Test
@@ -274,14 +281,17 @@
Path src = temporaryFolder.newFile("Test.java").toPath();
MoreFiles.asCharSink(src, UTF_8).write("public class Test {}");
- try {
- Main.compile(optionsWithBootclasspath().setSources(ImmutableList.of(src.toString())).build());
- fail();
- } catch (UsageException expected) {
- assertThat(expected)
- .hasMessageThat()
- .contains("at least one of --output, --gensrc_output, or --resource_output is required");
- }
+ UsageException expected =
+ assertThrows(
+ UsageException.class,
+ () ->
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSources(ImmutableList.of(src.toString()))
+ .build()));
+ assertThat(expected)
+ .hasMessageThat()
+ .contains("at least one of --output, --gensrc_output, or --resource_output is required");
}
@Test
@@ -471,4 +481,60 @@
assertThat(entries.map(JarEntry::getName)).containsExactly("g/Gen.class");
}
}
+
+ @Test
+ public void testGensrcDirectoryOutput() throws IOException {
+ Path src = temporaryFolder.newFile("Foo.java").toPath();
+ MoreFiles.asCharSink(src, UTF_8).write("package f; @Deprecated class Foo {}");
+
+ Path output = temporaryFolder.newFile("output.jar").toPath();
+ Path gensrc = temporaryFolder.newFolder("gensrcOutput").toPath();
+
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSources(ImmutableList.of(src.toString()))
+ .setTargetLabel("//foo:foo")
+ .setInjectingRuleKind("foo_library")
+ .setOutput(output.toString())
+ .setGensrcOutput(gensrc.toString())
+ .setProcessors(ImmutableList.of(SourceGeneratingProcessor.class.getName()))
+ .build());
+
+ assertThat(listDirectoryContents(gensrc)).containsExactly(gensrc.resolve("g/Gen.java"));
+ }
+
+ @Test
+ public void testResourceDirectoryOutput() throws IOException {
+ Path src = temporaryFolder.newFile("Foo.java").toPath();
+ MoreFiles.asCharSink(src, UTF_8).write("package f; @Deprecated class Foo {}");
+
+ Path output = temporaryFolder.newFile("output.jar").toPath();
+ Path resources = temporaryFolder.newFolder("resources").toPath();
+
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSources(ImmutableList.of(src.toString()))
+ .setTargetLabel("//foo:foo")
+ .setInjectingRuleKind("foo_library")
+ .setOutput(output.toString())
+ .setResourceOutput(resources.toString())
+ .setProcessors(ImmutableList.of(ClassGeneratingProcessor.class.getName()))
+ .build());
+
+ assertThat(listDirectoryContents(resources)).containsExactly(resources.resolve("g/Gen.class"));
+ }
+
+ private static ImmutableList<Path> listDirectoryContents(Path output) throws IOException {
+ ImmutableList.Builder<Path> paths = ImmutableList.builder();
+ Files.walkFileTree(
+ output,
+ new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
+ paths.add(path);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ return paths.build();
+ }
}
diff --git a/javatests/com/google/turbine/main/ReducedClasspathTest.java b/javatests/com/google/turbine/main/ReducedClasspathTest.java
index d74c640..2810481 100644
--- a/javatests/com/google/turbine/main/ReducedClasspathTest.java
+++ b/javatests/com/google/turbine/main/ReducedClasspathTest.java
@@ -20,7 +20,8 @@
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.turbine.testing.TestClassPaths.optionsWithBootclasspath;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.junit.Assert.fail;
+import static java.util.Objects.requireNonNull;
+import static org.junit.Assert.assertThrows;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
@@ -106,7 +107,7 @@
try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(lib))) {
for (String className : classNames) {
jos.putNextEntry(new JarEntry(className + ".class"));
- jos.write(compiled.get(className));
+ jos.write(requireNonNull(compiled.get(className), className));
}
}
return lib;
@@ -231,19 +232,19 @@
Path output = temporaryFolder.newFile("output.jar").toPath();
- try {
- Main.compile(
- optionsWithBootclasspath()
- .setOutput(output.toString())
- .setSources(ImmutableList.of(src.toString()))
- .setReducedClasspathMode(ReducedClasspathMode.JAVABUILDER_REDUCED)
- .setClassPath(ImmutableList.of(libc.toString()))
- .setDepsArtifacts(ImmutableList.of(libcJdeps.toString()))
- .build());
- fail();
- } catch (TurbineError e) {
- assertThat(e).hasMessageThat().contains("could not resolve I");
- }
+ TurbineError e =
+ assertThrows(
+ TurbineError.class,
+ () ->
+ Main.compile(
+ optionsWithBootclasspath()
+ .setOutput(output.toString())
+ .setSources(ImmutableList.of(src.toString()))
+ .setReducedClasspathMode(ReducedClasspathMode.JAVABUILDER_REDUCED)
+ .setClassPath(ImmutableList.of(libc.toString()))
+ .setDepsArtifacts(ImmutableList.of(libcJdeps.toString()))
+ .build()));
+ assertThat(e).hasMessageThat().contains("could not resolve I");
}
static String lines(String... lines) {
diff --git a/javatests/com/google/turbine/options/TurbineOptionsTest.java b/javatests/com/google/turbine/options/TurbineOptionsTest.java
index d4b468b..5d892c5 100644
--- a/javatests/com/google/turbine/options/TurbineOptionsTest.java
+++ b/javatests/com/google/turbine/options/TurbineOptionsTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableList;
@@ -283,23 +284,23 @@
@Test
public void unknownOption() throws Exception {
- try {
- TurbineOptionsParser.parse(Iterables.concat(BASE_ARGS, Arrays.asList("--nosuch")));
- fail();
- } catch (IllegalArgumentException e) {
- assertThat(e).hasMessageThat().contains("unknown option");
- }
+ IllegalArgumentException e =
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ TurbineOptionsParser.parse(Iterables.concat(BASE_ARGS, Arrays.asList("--nosuch"))));
+ assertThat(e).hasMessageThat().contains("unknown option");
}
@Test
public void unterminatedJavacopts() throws Exception {
- try {
- TurbineOptionsParser.parse(
- Iterables.concat(BASE_ARGS, Arrays.asList("--javacopts", "--release", "8")));
- fail();
- } catch (IllegalArgumentException e) {
- assertThat(e).hasMessageThat().contains("javacopts should be terminated by `--`");
- }
+ IllegalArgumentException e =
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ TurbineOptionsParser.parse(
+ Iterables.concat(BASE_ARGS, Arrays.asList("--javacopts", "--release", "8"))));
+ assertThat(e).hasMessageThat().contains("javacopts should be terminated by `--`");
}
@Test
@@ -348,11 +349,9 @@
@Test
public void invalidUnescape() throws Exception {
String[] lines = {"--sources", "'Foo$Bar.java"};
- try {
- TurbineOptionsParser.parse(Iterables.concat(BASE_ARGS, Arrays.asList(lines)));
- fail();
- } catch (IllegalArgumentException expected) {
- }
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> TurbineOptionsParser.parse(Iterables.concat(BASE_ARGS, Arrays.asList(lines))));
}
@Test
@@ -373,4 +372,37 @@
assertThat(options.reducedClasspathMode()).isEqualTo(mode);
}
}
+
+ @Test
+ public void javaBuilderCompatibility() throws Exception {
+ TurbineOptions options =
+ TurbineOptionsParser.parse(
+ Iterables.concat(
+ BASE_ARGS,
+ ImmutableList.of(
+ "--output_deps_proto",
+ "output_deps.proto",
+ "--generated_sources_output",
+ "generated_sources.jar",
+ "--experimental_fix_deps_tool",
+ "ignored",
+ "--strict_java_deps",
+ "ignored",
+ "--native_header_output",
+ "ignored",
+ "--compress_jar")));
+ assertThat(options.outputDeps()).hasValue("output_deps.proto");
+ assertThat(options.gensrcOutput()).hasValue("generated_sources.jar");
+ }
+
+ @Test
+ public void requiredValue() throws Exception {
+ IllegalArgumentException e =
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ TurbineOptionsParser.parse(
+ Iterables.concat(BASE_ARGS, ImmutableList.of("--output", "--system"))));
+ assertThat(e).hasMessageThat().contains("missing required argument for: --output");
+ }
}
diff --git a/javatests/com/google/turbine/parse/JavacLexer.java b/javatests/com/google/turbine/parse/JavacLexer.java
index d8939f1..6e1a984 100644
--- a/javatests/com/google/turbine/parse/JavacLexer.java
+++ b/javatests/com/google/turbine/parse/JavacLexer.java
@@ -27,7 +27,7 @@
import java.util.List;
/** A javac-based reference lexer. */
-public class JavacLexer {
+public final class JavacLexer {
static List<String> javacLex(final String input) {
Context context = new Context();
@@ -283,4 +283,6 @@
}
return token.kind.toString();
}
+
+ private JavacLexer() {}
}
diff --git a/javatests/com/google/turbine/parse/LexerTest.java b/javatests/com/google/turbine/parse/LexerTest.java
index 8530d52..c3d7804 100644
--- a/javatests/com/google/turbine/parse/LexerTest.java
+++ b/javatests/com/google/turbine/parse/LexerTest.java
@@ -328,6 +328,11 @@
lexerComparisonTest("foo /*/*/ bar");
}
+ @Test
+ public void unicode() {
+ lexerComparisonTest("import pkg\uD800\uDC00.test;");
+ }
+
private void lexerComparisonTest(String s) {
assertThat(lex(s)).containsExactlyElementsIn(JavacLexer.javacLex(s));
}
diff --git a/javatests/com/google/turbine/parse/ParseErrorTest.java b/javatests/com/google/turbine/parse/ParseErrorTest.java
index 6a9ad11..eeb3923 100644
--- a/javatests/com/google/turbine/parse/ParseErrorTest.java
+++ b/javatests/com/google/turbine/parse/ParseErrorTest.java
@@ -17,7 +17,7 @@
package com.google.turbine.parse;
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
import com.google.common.base.Joiner;
import com.google.turbine.diag.SourceFile;
@@ -36,12 +36,8 @@
new StreamLexer(
new UnicodeEscapePreprocessor(new SourceFile("<>", String.valueOf("2147483648"))));
ConstExpressionParser parser = new ConstExpressionParser(lexer, lexer.next());
- try {
- parser.expression();
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e).hasMessageThat().contains("invalid literal");
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> parser.expression());
+ assertThat(e).hasMessageThat().contains("invalid literal");
}
@Test
@@ -50,233 +46,252 @@
new StreamLexer(
new UnicodeEscapePreprocessor(new SourceFile("<>", String.valueOf("0x100000000"))));
ConstExpressionParser parser = new ConstExpressionParser(lexer, lexer.next());
- try {
- parser.expression();
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e).hasMessageThat().contains("invalid literal");
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> parser.expression());
+ assertThat(e).hasMessageThat().contains("invalid literal");
}
@Test
public void unexpectedTopLevel() {
String input = "public static void main(String[] args) {}";
- try {
- Parser.parse(input);
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- lines(
- "<>:1: error: unexpected token: void",
- "public static void main(String[] args) {}",
- " ^"));
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: unexpected token: void",
+ "public static void main(String[] args) {}",
+ " ^"));
}
@Test
public void unexpectedIdentifier() {
String input = "public clas Test {}";
- try {
- Parser.parse(input);
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- lines(
- "<>:1: error: unexpected identifier 'clas'", //
- "public clas Test {}",
- " ^"));
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: unexpected identifier 'clas'", //
+ "public clas Test {}",
+ " ^"));
}
@Test
public void missingTrailingCloseBrace() {
String input = "public class Test {\n\n";
- try {
- Parser.parse(input);
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- lines(
- "<>:2: error: unexpected end of input", //
- "",
- "^"));
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:2: error: unexpected end of input", //
+ "",
+ "^"));
}
@Test
public void annotationArgument() {
String input = "@A(x = System.err.println()) class Test {}\n";
- try {
- Parser.parse(input);
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- lines(
- "<>:1: error: invalid annotation argument", //
- "@A(x = System.err.println()) class Test {}",
- " ^"));
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: invalid annotation argument", //
+ "@A(x = System.err.println()) class Test {}",
+ " ^"));
}
@Test
public void dropParens() {
String input = "enum E { ONE(";
- try {
- Parser.parse(input);
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- lines(
- "<>:1: error: unexpected end of input", //
- "enum E { ONE(",
- " ^"));
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: unexpected end of input", //
+ "enum E { ONE(",
+ " ^"));
}
@Test
public void dropBlocks() {
String input = "class T { Object f = new Object() {";
- try {
- Parser.parse(input);
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- lines(
- "<>:1: error: unexpected end of input", //
- "class T { Object f = new Object() {",
- " ^"));
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: unexpected end of input", //
+ "class T { Object f = new Object() {",
+ " ^"));
}
@Test
public void unterminatedString() {
String input = "class T { String s = \"hello\nworld\"; }";
- try {
- Parser.parse(input);
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- lines(
- "<>:1: error: unterminated string literal", //
- "class T { String s = \"hello",
- " ^"));
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: unterminated string literal", //
+ "class T { String s = \"hello",
+ " ^"));
}
@Test
public void emptyChar() {
String input = "class T { char c = ''; }";
- try {
- Parser.parse(input);
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- lines(
- "<>:1: error: empty char literal", //
- "class T { char c = ''; }",
- " ^"));
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: empty char literal", //
+ "class T { char c = ''; }",
+ " ^"));
}
@Test
public void unterminatedChar() {
String input = "class T { char c = '; }";
- try {
- Parser.parse(input);
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- lines(
- "<>:1: error: unterminated char literal", //
- "class T { char c = '; }",
- " ^"));
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: unterminated char literal", //
+ "class T { char c = '; }",
+ " ^"));
}
@Test
public void unterminatedExpr() {
String input = "class T { String s = hello + world }";
- try {
- Parser.parse(input);
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- lines(
- "<>:1: error: unterminated expression, expected ';' not found", //
- "class T { String s = hello + world }",
- " ^"));
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: unterminated expression, expected ';' not found", //
+ "class T { String s = hello + world }",
+ " ^"));
}
@Test
public void abruptMultivariableDeclaration() {
String input = "class T { int x,; }";
- try {
- Parser.parse(input);
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- lines(
- "<>:1: error: expected token <identifier>", //
- "class T { int x,; }",
- " ^"));
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: expected token <identifier>", //
+ "class T { int x,; }",
+ " ^"));
}
@Test
public void invalidAnnotation() {
String input = "@Foo(x = @E [] x) class T {}";
- try {
- Parser.parse(input);
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- lines(
- "<>:1: error: invalid annotation argument", //
- "@Foo(x = @E [] x) class T {}",
- " ^"));
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: invalid annotation argument", //
+ "@Foo(x = @E [] x) class T {}",
+ " ^"));
}
@Test
public void unclosedComment() {
String input = "/** *\u001a/ class Test {}";
- try {
- Parser.parse(input);
- fail("expected parsing to fail");
- } catch (TurbineError e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- lines(
- "<>:1: error: unclosed comment", //
- "/** *\u001a/ class Test {}",
- "^"));
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: unclosed comment", //
+ "/** *\u001a/ class Test {}",
+ "^"));
+ }
+
+ @Test
+ public void unclosedGenerics() {
+ String input = "enum\te{l;p u@.<@";
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: unexpected end of input", //
+ "enum\te{l;p u@.<@",
+ " ^"));
+ }
+
+ @Test
+ public void arrayDot() {
+ String input = "enum\te{p;ullt[].<~>>>L\0";
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: unexpected token: <", //
+ "enum\te{p;ullt[].<~>>>L\0",
+ " ^"));
+ }
+
+ @Test
+ public void implementsBeforeExtends() {
+ String input = "class T implements A extends B {}";
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: 'extends' must come before 'implements'",
+ "class T implements A extends B {}",
+ " ^"));
+ }
+
+ @Test
+ public void unpairedSurrogate() {
+ String input = "import pkg\uD800.PackageTest;";
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: unpaired surrogate 0xd800",
+ "import pkg\uD800.PackageTest;",
+ " ^"));
+ }
+
+ @Test
+ public void abruptSurrogate() {
+ String input = "import pkg\uD800";
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines("<>:1: error: unpaired surrogate 0xd800", "import pkg\uD800", " ^"));
+ }
+
+ @Test
+ public void unexpectedSurrogate() {
+ String input = "..\uD800\uDC00";
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: unexpected input: U+10000", //
+ "..\uD800\uDC00",
+ " ^"));
}
private static String lines(String... lines) {
diff --git a/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java b/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java
index e3f7b63..b3e09b8 100644
--- a/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java
+++ b/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java
@@ -18,7 +18,7 @@
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
import com.google.turbine.diag.SourceFile;
import com.google.turbine.diag.TurbineError;
@@ -57,19 +57,11 @@
@Test
public void abruptEnd() {
- try {
- readAll("\\u00");
- fail();
- } catch (TurbineError e) {
- assertThat(getOnlyElement(e.diagnostics()).kind()).isEqualTo(ErrorKind.UNEXPECTED_EOF);
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> readAll("\\u00"));
+ assertThat(getOnlyElement(e.diagnostics()).kind()).isEqualTo(ErrorKind.UNEXPECTED_EOF);
- try {
- readAll("\\u");
- fail();
- } catch (TurbineError e) {
- assertThat(getOnlyElement(e.diagnostics()).kind()).isEqualTo(ErrorKind.UNEXPECTED_EOF);
- }
+ e = assertThrows(TurbineError.class, () -> readAll("\\u"));
+ assertThat(getOnlyElement(e.diagnostics()).kind()).isEqualTo(ErrorKind.UNEXPECTED_EOF);
}
@Test
@@ -79,19 +71,16 @@
@Test
public void invalidEscape() {
- try {
- readAll("\\uUUUU");
- fail();
- } catch (TurbineError e) {
- assertThat(getOnlyElement(e.diagnostics()).kind()).isEqualTo(ErrorKind.INVALID_UNICODE);
- }
+ TurbineError e = assertThrows(TurbineError.class, () -> readAll("\\uUUUU"));
+ assertThat(getOnlyElement(e.diagnostics()).kind()).isEqualTo(ErrorKind.INVALID_UNICODE);
}
private List<Character> readAll(String input) {
UnicodeEscapePreprocessor reader = new UnicodeEscapePreprocessor(new SourceFile(null, input));
List<Character> result = new ArrayList<>();
- for (char ch = reader.next(); ch != UnicodeEscapePreprocessor.ASCII_SUB; ch = reader.next()) {
- result.add(ch);
+ for (int ch = reader.next(); ch != UnicodeEscapePreprocessor.ASCII_SUB; ch = reader.next()) {
+ assertThat(Character.isBmpCodePoint(ch)).isTrue();
+ result.add((char) ch);
}
return result;
}
diff --git a/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java b/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java
index e6a59bf..d3b3836 100644
--- a/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java
+++ b/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java
@@ -21,6 +21,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
import com.google.common.base.Joiner;
@@ -231,6 +232,14 @@
"Float",
"Double",
},
+ // type annotations
+ {
+ "@A List<@B Integer>",
+ "@A List",
+ "@A int @B []",
+ "@A List<@A int @B []>",
+ "Map.@A Entry<@B Integer, @C Number>",
+ },
};
List<String> files = new ArrayList<>();
AtomicInteger idx = new AtomicInteger();
@@ -242,6 +251,7 @@
"package p;",
"import java.util.*;",
"import java.io.*;",
+ "import java.lang.annotation.*;",
String.format("abstract class Test%s {", idx.getAndIncrement()),
Streams.mapWithIndex(
Arrays.stream(group), (x, i) -> String.format(" %s f%d;\n", x, i))
@@ -250,6 +260,9 @@
" abstract <V extends List<V>> V g();",
" abstract <W extends ArrayList> W h();",
" abstract <X extends Serializable> X i();",
+ " @Target(ElementType.TYPE_USE) @interface A {}",
+ " @Target(ElementType.TYPE_USE) @interface B {}",
+ " @Target(ElementType.TYPE_USE) @interface C {}",
"}");
String content = sb.toString();
files.add(content);
@@ -397,8 +410,11 @@
ListMultimap<String, TypeMirror> turbineInputs =
MultimapBuilder.linkedHashKeys().arrayListValues().build();
- turbineElements
- .get(name)
+ /*
+ * requireNonNull is safe because `name` is from `javacElements`, which we checked has the
+ * same keys as `turbineElements`.
+ */
+ requireNonNull(turbineElements.get(name))
.getEnclosedElements()
.forEach(e -> getTypes(turbineTypes, e, turbineInputs));
diff --git a/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java b/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java
index ed5af6a..96664d2 100644
--- a/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java
+++ b/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java
@@ -19,9 +19,11 @@
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.MoreCollectors.onlyElement;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
@@ -33,6 +35,7 @@
import com.google.turbine.binder.Processing;
import com.google.turbine.binder.Processing.ProcessorInfo;
import com.google.turbine.diag.SourceFile;
+import com.google.turbine.diag.TurbineDiagnostic;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.lower.IntegrationTestSupport;
import com.google.turbine.parse.Parser;
@@ -45,9 +48,11 @@
import java.util.Optional;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
@@ -73,39 +78,33 @@
}
}
- private static final IntegrationTestSupport.TestInput SOURCES =
- IntegrationTestSupport.TestInput.parse(
- Joiner.on('\n')
- .join(
- "=== Test.java ===", //
- "@Deprecated",
- "class Test extends NoSuch {",
- "}"));
-
@Test
public void crash() throws IOException {
ImmutableList<Tree.CompUnit> units =
- SOURCES.sources.entrySet().stream()
- .map(e -> new SourceFile(e.getKey(), e.getValue()))
- .map(Parser::parse)
- .collect(toImmutableList());
- try {
- Binder.bind(
- units,
- ClassPathBinder.bindClasspath(ImmutableList.of()),
- Processing.ProcessorInfo.create(
- ImmutableList.of(new CrashingProcessor()),
- getClass().getClassLoader(),
- ImmutableMap.of(),
- SourceVersion.latestSupported()),
- TestClassPaths.TURBINE_BOOTCLASSPATH,
- Optional.empty());
- fail();
- } catch (TurbineError e) {
- assertThat(e.diagnostics()).hasSize(2);
- assertThat(e.diagnostics().get(0).message()).contains("could not resolve NoSuch");
- assertThat(e.diagnostics().get(1).message()).contains("crash!");
- }
+ parseUnit(
+ "=== Test.java ===", //
+ "@Deprecated",
+ "class Test extends NoSuch {",
+ "}");
+ TurbineError e =
+ assertThrows(
+ TurbineError.class,
+ () ->
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ Processing.ProcessorInfo.create(
+ ImmutableList.of(new CrashingProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty()));
+ ImmutableList<String> messages =
+ e.diagnostics().stream().map(TurbineDiagnostic::message).collect(toImmutableList());
+ assertThat(messages).hasSize(2);
+ assertThat(messages.get(0)).contains("could not resolve NoSuch");
+ assertThat(messages.get(1)).contains("crash!");
}
@SupportedAnnotationTypes("*")
@@ -142,38 +141,30 @@
@Test
public void warnings() throws IOException {
ImmutableList<Tree.CompUnit> units =
- IntegrationTestSupport.TestInput.parse(
- Joiner.on('\n')
- .join(
- "=== Test.java ===", //
- "@Deprecated",
- "class Test {",
- "}"))
- .sources
- .entrySet()
- .stream()
- .map(e -> new SourceFile(e.getKey(), e.getValue()))
- .map(Parser::parse)
- .collect(toImmutableList());
- try {
- Binder.bind(
- units,
- ClassPathBinder.bindClasspath(ImmutableList.of()),
- Processing.ProcessorInfo.create(
- ImmutableList.of(new WarningProcessor()),
- getClass().getClassLoader(),
- ImmutableMap.of(),
- SourceVersion.latestSupported()),
- TestClassPaths.TURBINE_BOOTCLASSPATH,
- Optional.empty());
- fail();
- } catch (TurbineError e) {
- ImmutableList<String> diags =
- e.diagnostics().stream().map(d -> d.message()).collect(toImmutableList());
- assertThat(diags).hasSize(2);
- assertThat(diags.get(0)).contains("proc warning");
- assertThat(diags.get(1)).contains("proc error");
- }
+ parseUnit(
+ "=== Test.java ===", //
+ "@Deprecated",
+ "class Test {",
+ "}");
+ TurbineError e =
+ assertThrows(
+ TurbineError.class,
+ () ->
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ Processing.ProcessorInfo.create(
+ ImmutableList.of(new WarningProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty()));
+ ImmutableList<String> diags =
+ e.diagnostics().stream().map(d -> d.message()).collect(toImmutableList());
+ assertThat(diags).hasSize(2);
+ assertThat(diags.get(0)).contains("proc warning");
+ assertThat(diags.get(1)).contains("proc error");
}
@SupportedAnnotationTypes("*")
@@ -219,19 +210,11 @@
@Test
public void resources() throws IOException {
ImmutableList<Tree.CompUnit> units =
- IntegrationTestSupport.TestInput.parse(
- Joiner.on('\n')
- .join(
- "=== Test.java ===", //
- "@Deprecated",
- "class Test {",
- "}"))
- .sources
- .entrySet()
- .stream()
- .map(e -> new SourceFile(e.getKey(), e.getValue()))
- .map(Parser::parse)
- .collect(toImmutableList());
+ parseUnit(
+ "=== Test.java ===", //
+ "@Deprecated",
+ "class Test {",
+ "}");
BindingResult bound =
Binder.bind(
units,
@@ -247,34 +230,27 @@
assertThat(bound.generatedSources().keySet()).containsExactly("Gen.java", "source.txt");
assertThat(bound.generatedClasses().keySet()).containsExactly("class.txt");
- assertThat(bound.generatedSources().get("source.txt").source())
+ // The requireNonNull calls are safe because of the keySet checks above.
+ assertThat(requireNonNull(bound.generatedSources().get("source.txt")).source())
.isEqualTo("hello source output");
- assertThat(new String(bound.generatedClasses().get("class.txt"), UTF_8))
+ assertThat(new String(requireNonNull(bound.generatedClasses().get("class.txt")), UTF_8))
.isEqualTo("hello class output");
}
@Test
public void getAllAnnotations() throws IOException {
ImmutableList<Tree.CompUnit> units =
- IntegrationTestSupport.TestInput.parse(
- Joiner.on('\n')
- .join(
- "=== A.java ===", //
- "import java.lang.annotation.Inherited;",
- "@Inherited",
- "@interface A {}",
- "=== B.java ===", //
- "@interface B {}",
- "=== One.java ===", //
- "@A @B class One {}",
- "=== Two.java ===", //
- "class Two extends One {}"))
- .sources
- .entrySet()
- .stream()
- .map(e -> new SourceFile(e.getKey(), e.getValue()))
- .map(Parser::parse)
- .collect(toImmutableList());
+ parseUnit(
+ "=== A.java ===", //
+ "import java.lang.annotation.Inherited;",
+ "@Inherited",
+ "@interface A {}",
+ "=== B.java ===", //
+ "@interface B {}",
+ "=== One.java ===", //
+ "@A @B class One {}",
+ "=== Two.java ===", //
+ "class Two extends One {}");
BindingResult bound =
Binder.bind(
units,
@@ -343,4 +319,308 @@
.collect(joining(", ")));
}
}
+
+ private static void logError(
+ ProcessingEnvironment processingEnv,
+ RoundEnvironment roundEnv,
+ Class<?> processorClass,
+ int round) {
+ processingEnv
+ .getMessager()
+ .printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "%d: %s {errorRaised=%s, processingOver=%s}",
+ round,
+ processorClass.getSimpleName(),
+ roundEnv.errorRaised(),
+ roundEnv.processingOver()));
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class ErrorProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ int round = 0;
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ int round = ++this.round;
+ logError(processingEnv, roundEnv, getClass(), round);
+ String name = "Gen" + round;
+ try (Writer writer = processingEnv.getFiler().createSourceFile(name).openWriter()) {
+ writer.write(String.format("class %s {}", name));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ return false;
+ }
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class FinalRoundErrorProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ int round = 0;
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ int round = ++this.round;
+ if (roundEnv.processingOver()) {
+ logError(processingEnv, roundEnv, getClass(), round);
+ }
+ return false;
+ }
+ }
+
+ @Test
+ public void errorsAndFinalRound() throws IOException {
+ ImmutableList<Tree.CompUnit> units =
+ parseUnit(
+ "=== Test.java ===", //
+ "@Deprecated",
+ "class Test {",
+ "}");
+ TurbineError e =
+ assertThrows(
+ TurbineError.class,
+ () ->
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ Processing.ProcessorInfo.create(
+ ImmutableList.of(new ErrorProcessor(), new FinalRoundErrorProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty()));
+ ImmutableList<String> diags =
+ e.diagnostics().stream().map(d -> d.message()).collect(toImmutableList());
+ assertThat(diags)
+ .containsExactly(
+ "1: ErrorProcessor {errorRaised=false, processingOver=false}",
+ "2: ErrorProcessor {errorRaised=true, processingOver=true}",
+ "2: FinalRoundErrorProcessor {errorRaised=true, processingOver=true}")
+ .inOrder();
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class SuperTypeProcessor extends AbstractProcessor {
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ TypeElement typeElement = processingEnv.getElementUtils().getTypeElement("T");
+ processingEnv
+ .getMessager()
+ .printMessage(
+ Diagnostic.Kind.ERROR,
+ typeElement.getSuperclass()
+ + " "
+ + processingEnv.getTypeUtils().directSupertypes(typeElement.asType()));
+ return false;
+ }
+ }
+
+ @Test
+ public void superType() throws IOException {
+ ImmutableList<Tree.CompUnit> units =
+ parseUnit(
+ "=== T.java ===", //
+ "@Deprecated",
+ "class T extends S {",
+ "}");
+ TurbineError e =
+ assertThrows(
+ TurbineError.class,
+ () ->
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ Processing.ProcessorInfo.create(
+ ImmutableList.of(new SuperTypeProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty()));
+ ImmutableList<String> diags =
+ e.diagnostics().stream().map(d -> d.message()).collect(toImmutableList());
+ assertThat(diags).containsExactly("could not resolve S", "S [S]").inOrder();
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class GenerateAnnotationProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ private boolean first = true;
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (first) {
+ try {
+ JavaFileObject file = processingEnv.getFiler().createSourceFile("A");
+ try (Writer writer = file.openWriter()) {
+ writer.write("@interface A {}");
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ first = false;
+ }
+ return false;
+ }
+ }
+
+ @Test
+ public void generatedAnnotationDefinition() throws IOException {
+ ImmutableList<Tree.CompUnit> units =
+ parseUnit(
+ "=== T.java ===", //
+ "@interface B {",
+ " A value() default @A;",
+ "}",
+ "@B(value = @A)",
+ "class T {",
+ "}");
+ BindingResult bound =
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ ProcessorInfo.create(
+ ImmutableList.of(new GenerateAnnotationProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+ assertThat(bound.generatedSources()).containsKey("A.java");
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class GenerateQualifiedProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ String superType =
+ processingEnv.getElementUtils().getTypeElement("T").getSuperclass().toString();
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, superType);
+ return false;
+ }
+ }
+
+ @Test
+ public void qualifiedErrorType() throws IOException {
+ ImmutableList<Tree.CompUnit> units =
+ parseUnit(
+ "=== T.java ===", //
+ "class T extends G.I {",
+ "}");
+ TurbineError e =
+ assertThrows(
+ TurbineError.class,
+ () ->
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ ProcessorInfo.create(
+ ImmutableList.of(new GenerateQualifiedProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty()));
+ assertThat(
+ e.diagnostics().stream()
+ .filter(d -> d.severity().equals(Diagnostic.Kind.NOTE))
+ .map(d -> d.message()))
+ .containsExactly("G.I");
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class ElementValueInspector extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ TypeElement element = processingEnv.getElementUtils().getTypeElement("T");
+ for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
+ processingEnv
+ .getMessager()
+ .printMessage(
+ Diagnostic.Kind.NOTE,
+ String.format("@Deprecated(%s)", annotationMirror.getElementValues()),
+ element,
+ annotationMirror);
+ }
+ return false;
+ }
+ }
+
+ @Test
+ public void badElementValue() throws IOException {
+ ImmutableList<Tree.CompUnit> units =
+ parseUnit(
+ "=== T.java ===", //
+ "@Deprecated(noSuch = 42) class T {}");
+ TurbineError e =
+ assertThrows(
+ TurbineError.class,
+ () ->
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ ProcessorInfo.create(
+ ImmutableList.of(new ElementValueInspector()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty()));
+ assertThat(
+ e.diagnostics().stream()
+ .filter(d -> d.severity().equals(Diagnostic.Kind.ERROR))
+ .map(d -> d.message()))
+ .containsExactly("could not resolve element noSuch() in java.lang.Deprecated");
+ assertThat(
+ e.diagnostics().stream()
+ .filter(d -> d.severity().equals(Diagnostic.Kind.NOTE))
+ .map(d -> d.message()))
+ .containsExactly("@Deprecated({})");
+ }
+
+ private static ImmutableList<Tree.CompUnit> parseUnit(String... lines) {
+ return IntegrationTestSupport.TestInput.parse(Joiner.on('\n').join(lines))
+ .sources
+ .entrySet()
+ .stream()
+ .map(e -> new SourceFile(e.getKey(), e.getValue()))
+ .map(Parser::parse)
+ .collect(toImmutableList());
+ }
}
diff --git a/javatests/com/google/turbine/processing/TurbineAnnotationProxyTest.java b/javatests/com/google/turbine/processing/TurbineAnnotationProxyTest.java
index d339700..a8c00aa 100644
--- a/javatests/com/google/turbine/processing/TurbineAnnotationProxyTest.java
+++ b/javatests/com/google/turbine/processing/TurbineAnnotationProxyTest.java
@@ -18,11 +18,11 @@
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
+import static com.google.turbine.testing.TestResources.getResourceBytes;
+import static org.junit.Assert.assertThrows;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
-import com.google.common.io.ByteStreams;
import com.google.common.primitives.Ints;
import com.google.common.testing.EqualsTester;
import com.google.turbine.binder.Binder;
@@ -39,7 +39,6 @@
import com.google.turbine.testing.TestClassPaths;
import com.google.turbine.tree.Tree.CompUnit;
import java.io.IOException;
-import java.io.InputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
@@ -161,17 +160,13 @@
assertThat(a.b().value()).isEqualTo(-1);
assertThat(a.e()).isEqualTo(ElementType.PACKAGE);
- try {
- a.c();
- fail();
- } catch (MirroredTypeException e) {
+ {
+ MirroredTypeException e = assertThrows(MirroredTypeException.class, () -> a.c());
assertThat(e.getTypeMirror().getKind()).isEqualTo(TypeKind.DECLARED);
assertThat(getQualifiedName(e.getTypeMirror())).contains("java.lang.String");
}
- try {
- a.cx();
- fail();
- } catch (MirroredTypesException e) {
+ {
+ MirroredTypesException e = assertThrows(MirroredTypesException.class, () -> a.cx());
assertThat(
e.getTypeMirrors().stream().map(m -> getQualifiedName(m)).collect(toImmutableList()))
.containsExactly("java.lang.Integer", "java.lang.Long");
@@ -208,9 +203,7 @@
private static void addClass(JarOutputStream jos, Class<?> clazz) throws IOException {
String entryPath = clazz.getName().replace('.', '/') + ".class";
jos.putNextEntry(new JarEntry(entryPath));
- try (InputStream is = clazz.getClassLoader().getResourceAsStream(entryPath)) {
- ByteStreams.copy(is, jos);
- }
+ jos.write(getResourceBytes(clazz, "/" + entryPath));
}
private static String getQualifiedName(TypeMirror typeMirror) {
diff --git a/javatests/com/google/turbine/processing/TurbineElementsTest.java b/javatests/com/google/turbine/processing/TurbineElementsTest.java
index 770e6f6..281bde4 100644
--- a/javatests/com/google/turbine/processing/TurbineElementsTest.java
+++ b/javatests/com/google/turbine/processing/TurbineElementsTest.java
@@ -20,6 +20,8 @@
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.collect.MoreCollectors.onlyElement;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.common.truth.TruthJUnit.assume;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
@@ -225,6 +227,12 @@
.isFalse();
assertThat(turbineElements.isDeprecated(turbineElements.getTypeElement("One"))).isFalse();
assertThat(turbineElements.isDeprecated(turbineElements.getTypeElement("Test"))).isTrue();
+ for (Element e : turbineElements.getTypeElement("java.lang.Object").getEnclosedElements()) {
+ assume().that(e.getSimpleName().contentEquals("finalize")).isFalse();
+ assertWithMessage(e.getSimpleName().toString())
+ .that(turbineElements.isDeprecated(e))
+ .isFalse();
+ }
}
@Test
diff --git a/javatests/com/google/turbine/processing/TurbineFilerTest.java b/javatests/com/google/turbine/processing/TurbineFilerTest.java
index 40b78ea..d433428 100644
--- a/javatests/com/google/turbine/processing/TurbineFilerTest.java
+++ b/javatests/com/google/turbine/processing/TurbineFilerTest.java
@@ -19,7 +19,7 @@
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
@@ -98,19 +98,13 @@
seen.add("com/foo/Bar.java");
seen.add("com/foo/Baz.class");
- try {
- filer.createSourceFile("com.foo.Bar", (Element[]) null);
- fail();
- } catch (FilerException expected) {
- }
+ assertThrows(
+ FilerException.class, () -> filer.createSourceFile("com.foo.Bar", (Element[]) null));
filer.createSourceFile("com.foo.Baz", (Element[]) null);
filer.createClassFile("com.foo.Bar", (Element[]) null);
- try {
- filer.createClassFile("com.foo.Baz", (Element[]) null);
- fail();
- } catch (FilerException expected) {
- }
+ assertThrows(
+ FilerException.class, () -> filer.createClassFile("com.foo.Baz", (Element[]) null));
}
@Test
@@ -121,11 +115,7 @@
StandardLocation.SOURCE_OUTPUT,
StandardLocation.ANNOTATION_PROCESSOR_PATH,
StandardLocation.CLASS_PATH)) {
- try {
- filer.getResource(location, "", "NoSuch");
- fail();
- } catch (FileNotFoundException expected) {
- }
+ assertThrows(FileNotFoundException.class, () -> filer.getResource(location, "", "NoSuch"));
}
}
diff --git a/javatests/com/google/turbine/processing/TurbineMessagerTest.java b/javatests/com/google/turbine/processing/TurbineMessagerTest.java
index c1e6401..017012c 100644
--- a/javatests/com/google/turbine/processing/TurbineMessagerTest.java
+++ b/javatests/com/google/turbine/processing/TurbineMessagerTest.java
@@ -19,6 +19,7 @@
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
import static java.util.Comparator.comparing;
+import static java.util.Objects.requireNonNull;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
@@ -242,7 +243,7 @@
private static String shortPath(Diagnostic<? extends JavaFileObject> d) {
return d.getSource() != null
- ? Paths.get(d.getSource().getName()).getFileName().toString()
+ ? requireNonNull(Paths.get(d.getSource().getName()).getFileName()).toString()
: "<>";
}
}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesFactoryTest.java b/javatests/com/google/turbine/processing/TurbineTypesFactoryTest.java
index 0f9e6a6..b028a81 100644
--- a/javatests/com/google/turbine/processing/TurbineTypesFactoryTest.java
+++ b/javatests/com/google/turbine/processing/TurbineTypesFactoryTest.java
@@ -17,7 +17,7 @@
package com.google.turbine.processing;
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
@@ -83,11 +83,7 @@
PrimitiveType type = turbineTypes.getPrimitiveType(kind);
assertThat(type.getKind()).isEqualTo(kind);
} else {
- try {
- turbineTypes.getPrimitiveType(kind);
- fail();
- } catch (IllegalArgumentException expected) {
- }
+ assertThrows(IllegalArgumentException.class, () -> turbineTypes.getPrimitiveType(kind));
}
}
}
@@ -167,11 +163,7 @@
public void noType() {
assertThat(turbineTypes.getNoType(TypeKind.VOID).getKind()).isEqualTo(TypeKind.VOID);
assertThat(turbineTypes.getNoType(TypeKind.NONE).getKind()).isEqualTo(TypeKind.NONE);
- try {
- turbineTypes.getNoType(TypeKind.DECLARED);
- fail();
- } catch (IllegalArgumentException expected) {
- }
+ assertThrows(IllegalArgumentException.class, () -> turbineTypes.getNoType(TypeKind.DECLARED));
}
@Test
diff --git a/javatests/com/google/turbine/processing/TurbineTypesUnaryTest.java b/javatests/com/google/turbine/processing/TurbineTypesUnaryTest.java
index eb5ee6c..00eb571 100644
--- a/javatests/com/google/turbine/processing/TurbineTypesUnaryTest.java
+++ b/javatests/com/google/turbine/processing/TurbineTypesUnaryTest.java
@@ -18,10 +18,11 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.TruthJUnit.assume;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
+import com.google.turbine.types.Deannotate;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
@@ -68,12 +69,10 @@
thrown = e;
}
if (thrown != null) {
- try {
- turbineTypes.unboxedType(turbineA).toString();
- fail(String.format("expected unboxedType(`%s`) to throw", turbineA));
- } catch (IllegalArgumentException expected) {
- // expected
- }
+ assertThrows(
+ String.format("expected unboxedType(`%s`) to throw", turbineA),
+ IllegalArgumentException.class,
+ () -> turbineTypes.unboxedType(turbineA).toString());
} else {
String actual = turbineTypes.unboxedType(turbineA).toString();
assertWithMessage("unboxedClass(`%s`) = unboxedClass(`%s`)", javacA, turbineA)
@@ -121,16 +120,8 @@
public void directSupertypesThrows() {
assume().that(UNSUPPORTED_BY_DIRECT_SUPERTYPES).contains(javacA.getKind());
- try {
- javacTypes.directSupertypes(turbineA);
- fail();
- } catch (IllegalArgumentException expected) {
- }
- try {
- turbineTypes.directSupertypes(turbineA);
- fail();
- } catch (IllegalArgumentException expected) {
- }
+ assertThrows(IllegalArgumentException.class, () -> javacTypes.directSupertypes(turbineA));
+ assertThrows(IllegalArgumentException.class, () -> turbineTypes.directSupertypes(turbineA));
}
@Test
@@ -144,4 +135,16 @@
.that(actual)
.isEqualTo(expected);
}
+
+ @Test
+ public void deannotate() {
+ String toString = turbineA.toString();
+ String deannotated =
+ Deannotate.deannotate(((TurbineTypeMirror) turbineA).asTurbineType()).toString();
+ if (toString.contains("@")) {
+ assertWithMessage("deannotate(`%s`) = `%s`", toString, deannotated)
+ .that(deannotated)
+ .doesNotContain("@");
+ }
+ }
}
diff --git a/javatests/com/google/turbine/testing/AsmUtils.java b/javatests/com/google/turbine/testing/AsmUtils.java
index 5b5e102..b7e77bc 100644
--- a/javatests/com/google/turbine/testing/AsmUtils.java
+++ b/javatests/com/google/turbine/testing/AsmUtils.java
@@ -27,14 +27,18 @@
* ASM-based test utilities, in their own class mostly to avoid namespace issues with e.g. {@link
* com.google.turbine.bytecode.ClassReader}.
*/
-public class AsmUtils {
- public static String textify(byte[] bytes) {
+public final class AsmUtils {
+ public static String textify(byte[] bytes, boolean skipDebug) {
Printer textifier = new Textifier();
StringWriter sw = new StringWriter();
new ClassReader(bytes)
.accept(
new TraceClassVisitor(null, textifier, new PrintWriter(sw, true)),
- ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE);
+ ClassReader.SKIP_FRAMES
+ | ClassReader.SKIP_CODE
+ | (skipDebug ? ClassReader.SKIP_DEBUG : 0));
return sw.toString();
}
+
+ private AsmUtils() {}
}
diff --git a/javatests/com/google/turbine/testing/TestClassPaths.java b/javatests/com/google/turbine/testing/TestClassPaths.java
index 93be916..55e8b9e 100644
--- a/javatests/com/google/turbine/testing/TestClassPaths.java
+++ b/javatests/com/google/turbine/testing/TestClassPaths.java
@@ -20,10 +20,9 @@
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Streams;
import com.google.turbine.binder.ClassPath;
import com.google.turbine.binder.ClassPathBinder;
-import com.google.turbine.binder.CtSymClassBinder;
+import com.google.turbine.binder.JimageClassBinder;
import com.google.turbine.options.TurbineOptions;
import java.io.File;
import java.io.IOException;
@@ -33,15 +32,14 @@
import java.nio.file.Paths;
import java.util.Optional;
-public class TestClassPaths {
+public final class TestClassPaths {
private static final Splitter CLASS_PATH_SPLITTER =
Splitter.on(File.pathSeparatorChar).omitEmptyStrings();
- private static final ImmutableList<Path> BOOTCLASSPATH =
- Streams.stream(
- CLASS_PATH_SPLITTER.split(
- Optional.ofNullable(System.getProperty("sun.boot.class.path")).orElse("")))
+ public static final ImmutableList<Path> BOOTCLASSPATH =
+ CLASS_PATH_SPLITTER
+ .splitToStream(Optional.ofNullable(System.getProperty("sun.boot.class.path")).orElse(""))
.map(Paths::get)
.filter(Files::exists)
.collect(toImmutableList());
@@ -53,7 +51,7 @@
if (!BOOTCLASSPATH.isEmpty()) {
return ClassPathBinder.bindClasspath(BOOTCLASSPATH);
}
- return CtSymClassBinder.bind("8");
+ return JimageClassBinder.bindDefault();
} catch (IOException e) {
e.printStackTrace();
throw new UncheckedIOException(e);
@@ -74,4 +72,6 @@
}
return options;
}
+
+ private TestClassPaths() {}
}
diff --git a/javatests/com/google/turbine/testing/TestResources.java b/javatests/com/google/turbine/testing/TestResources.java
new file mode 100644
index 0000000..86c7632
--- /dev/null
+++ b/javatests/com/google/turbine/testing/TestResources.java
@@ -0,0 +1,42 @@
+/*
+ * 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.testing;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+
+public final class TestResources {
+
+ public static String getResource(Class<?> clazz, String resource) {
+ return new String(getResourceBytes(clazz, resource), UTF_8);
+ }
+
+ public static byte[] getResourceBytes(Class<?> clazz, String resource) {
+ try (InputStream is = requireNonNull(clazz.getResourceAsStream(resource), resource)) {
+ return ByteStreams.toByteArray(is);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private TestResources() {}
+}
diff --git a/javatests/com/google/turbine/zip/ZipTest.java b/javatests/com/google/turbine/zip/ZipTest.java
index bfc9cdf..0d49e1a 100644
--- a/javatests/com/google/turbine/zip/ZipTest.java
+++ b/javatests/com/google/turbine/zip/ZipTest.java
@@ -18,7 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableMap;
import com.google.common.hash.Hashing;
@@ -161,11 +161,7 @@
}
Files.write(path, "trailing garbage".getBytes(UTF_8), StandardOpenOption.APPEND);
- try {
- actual(path);
- fail();
- } catch (ZipException e) {
- assertThat(e).hasMessageThat().isEqualTo("zip file comment length was 33, expected 17");
- }
+ ZipException e = assertThrows(ZipException.class, () -> actual(path));
+ assertThat(e).hasMessageThat().isEqualTo("zip file comment length was 33, expected 17");
}
}
diff --git a/pom.xml b/pom.xml
index fa923f4..dae4b70 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,4 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2020 Google Inc.
+
+ 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.
+-->
+
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -7,7 +23,7 @@
<groupId>com.google.turbine</groupId>
<artifactId>turbine</artifactId>
- <version>0.1-SNAPSHOT</version>
+ <version>HEAD-SNAPSHOT</version>
<name>turbine</name>
<description>
@@ -15,9 +31,13 @@
</description>
<properties>
- <asm.version>7.0</asm.version>
+ <asm.version>9.1</asm.version>
<javac.version>9+181-r4173-1</javac.version>
- <guava.version>27.0.1-jre</guava.version>
+ <guava.version>30.0-jre</guava.version>
+ <errorprone.version>2.7.1</errorprone.version>
+ <maven-javadoc-plugin.version>3.1.0</maven-javadoc-plugin.version>
+ <maven-source-plugin.version>3.2.1</maven-source-plugin.version>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
@@ -27,19 +47,20 @@
<version>${guava.version}</version>
</dependency>
<dependency>
- <groupId>com.google.code.findbugs</groupId>
- <artifactId>jsr305</artifactId>
- <version>2.0.1</version>
- </dependency>
- <dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
- <version>2.0.12</version>
+ <version>${errorprone.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.checkerframework</groupId>
+ <artifactId>checker-qual</artifactId>
+ <version>3.9.1</version>
+ <optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
- <version>3.1.0</version>
+ <version>3.10.0</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
@@ -68,31 +89,31 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>4.12</version>
+ <version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
- <version>1.0</version>
+ <version>1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.truth.extensions</groupId>
<artifactId>truth-proto-extension</artifactId>
- <version>1.0</version>
+ <version>1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.truth.extensions</groupId>
<artifactId>truth-java8-extension</artifactId>
- <version>1.0</version>
+ <version>1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.jimfs</groupId>
<artifactId>jimfs</artifactId>
- <version>1.0</version>
+ <version>1.2</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -103,8 +124,8 @@
</dependency>
<dependency>
<groupId>com.google.auto.value</groupId>
- <artifactId>auto-value</artifactId>
- <version>1.5.3</version>
+ <artifactId>auto-value-annotations</artifactId>
+ <version>1.7.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -132,14 +153,38 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>3.6.2</version>
+ <version>3.8.0</version>
<configuration>
- <fork>true</fork>
- <source>1.8</source>
- <target>1.8</target>
+ <source>8</source>
+ <target>8</target>
<encoding>UTF-8</encoding>
- <compilerArgument>-parameters</compilerArgument>
- <testCompilerArgument>-parameters</testCompilerArgument>
+ <fork>true</fork>
+ <compilerArgs>
+ <arg>-parameters</arg>
+ <arg>-XDcompilePolicy=simple</arg>
+ <arg>-Xplugin:ErrorProne</arg>
+ <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
+ <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
+ <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
+ <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
+ <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
+ <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
+ <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
+ <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
+ <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
+ </compilerArgs>
+ <annotationProcessorPaths>
+ <path>
+ <groupId>com.google.errorprone</groupId>
+ <artifactId>error_prone_core</artifactId>
+ <version>${errorprone.version}</version>
+ </path>
+ <path>
+ <groupId>com.google.auto.value</groupId>
+ <artifactId>auto-value</artifactId>
+ <version>1.7.4</version>
+ </path>
+ </annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
@@ -167,7 +212,19 @@
<version>2.19.1</version>
<configuration>
<!-- set heap size to work around http://github.com/travis-ci/travis-ci/issues/3396 -->
- <argLine>-Xmx2g</argLine>
+ <argLine>
+ -Xmx2g
+ --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
+ --add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
+ --add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
+ --add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
+ --add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
+ --add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
+ --add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
+ --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+ --add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
+ --add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
+ </argLine>
</configuration>
</plugin>
<plugin>
@@ -200,9 +257,33 @@
</execution>
</executions>
</plugin>
-
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>3.1.1</version>
+ <configuration>
+ <source>8</source>
+ <detectJavaApiLink>false</detectJavaApiLink>
+ <notimestamp>true</notimestamp>
+ <doctitle>turbine ${project.version} API</doctitle>
+ </configuration>
+ </plugin>
</plugins>
</build>
+
+ <distributionManagement>
+ <snapshotRepository>
+ <id>sonatype-nexus-snapshots</id>
+ <name>Sonatype Nexus Snapshots</name>
+ <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
+ </snapshotRepository>
+ <repository>
+ <id>sonatype-nexus-staging</id>
+ <name>Nexus Release Repository</name>
+ <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
+ </repository>
+ </distributionManagement>
+
<profiles>
<profile>
<id>java-8</id>
@@ -219,6 +300,66 @@
<argLine>-Xbootclasspath/p:${settings.localRepository}/com/google/errorprone/javac/${javac.version}/javac-${javac.version}.jar</argLine>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <fork>true</fork>
+ <compilerArgs>
+ <arg>-parameters</arg>
+ <arg>-XDcompilePolicy=simple</arg>
+ <arg>-Xplugin:ErrorProne</arg>
+ <arg>-J-Xbootclasspath/p:${settings.localRepository}/com/google/errorprone/javac/${javac.version}/javac-${javac.version}.jar</arg>
+ </compilerArgs>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>sonatype-oss-release</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>${maven-source-plugin.version}</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar-no-fork</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>${maven-javadoc-plugin.version}</version>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-gpg-plugin</artifactId>
+ <version>1.6</version>
+ <executions>
+ <execution>
+ <id>sign-artifacts</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>sign</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
</profile>