aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2021-06-17 19:05:22 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2021-06-17 19:05:22 +0000
commita37949be1fd1a3b76c509904dce6ea1d6e2aa047 (patch)
treea7fa8fc6aa0656abac95cb56fbde65706b79e78b
parentc6fb3b925e3e7c9dbf8324105097988c75b69c2b (diff)
parent8a8c1f2648124e7e7612fdc620ee25b5e8e3e902 (diff)
downloadturbine-android12-mainline-mediaprovider-release.tar.gz
Change-Id: Ic3538a2a2d2dfadc03052937544b800cb42c917c
-rw-r--r--.travis.yml4
-rw-r--r--Android.bp25
-rw-r--r--METADATA17
-rw-r--r--NOTICE202
-rwxr-xr-xandroid-annotation-stubs/gen_annotations.sh37
-rw-r--r--android-annotation-stubs/src/org/checkerframework/checker/nullness/compatqual/NullableDecl.java40
-rw-r--r--android-annotation-stubs/src/org/checkerframework/checker/nullness/qual/Nullable.java40
-rw-r--r--android-annotation-stubs/tmpl.java43
-rw-r--r--java/com/google/turbine/binder/Binder.java154
-rw-r--r--java/com/google/turbine/binder/CanonicalTypeBinder.java7
-rw-r--r--java/com/google/turbine/binder/ClassPath.java5
-rw-r--r--java/com/google/turbine/binder/ClassPathBinder.java12
-rw-r--r--java/com/google/turbine/binder/CompUnitPreprocessor.java3
-rw-r--r--java/com/google/turbine/binder/ConstBinder.java33
-rw-r--r--java/com/google/turbine/binder/ConstEvaluator.java257
-rw-r--r--java/com/google/turbine/binder/CtSymClassBinder.java5
-rw-r--r--java/com/google/turbine/binder/DisambiguateTypeAnnotations.java68
-rw-r--r--java/com/google/turbine/binder/HierarchyBinder.java9
-rw-r--r--java/com/google/turbine/binder/JimageClassBinder.java29
-rw-r--r--java/com/google/turbine/binder/ModuleBinder.java22
-rw-r--r--java/com/google/turbine/binder/Processing.java556
-rw-r--r--java/com/google/turbine/binder/Resolve.java21
-rw-r--r--java/com/google/turbine/binder/TypeBinder.java93
-rw-r--r--java/com/google/turbine/binder/bound/AnnotationMetadata.java2
-rw-r--r--java/com/google/turbine/binder/bound/EnumConstantValue.java5
-rw-r--r--java/com/google/turbine/binder/bound/TurbineAnnotationValue.java (renamed from java/com/google/turbine/binder/bound/AnnotationValue.java)31
-rw-r--r--java/com/google/turbine/binder/bound/TypeBoundClass.java68
-rw-r--r--java/com/google/turbine/binder/bytecode/BytecodeBinder.java52
-rw-r--r--java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java47
-rw-r--r--java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java8
-rw-r--r--java/com/google/turbine/binder/lookup/ImportIndex.java3
-rw-r--r--java/com/google/turbine/binder/lookup/PackageScope.java53
-rw-r--r--java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java26
-rw-r--r--java/com/google/turbine/binder/lookup/TopLevelIndex.java3
-rw-r--r--java/com/google/turbine/binder/sym/ClassSymbol.java30
-rw-r--r--java/com/google/turbine/binder/sym/FieldSymbol.java2
-rw-r--r--java/com/google/turbine/binder/sym/MethodSymbol.java11
-rw-r--r--java/com/google/turbine/binder/sym/PackageSymbol.java54
-rw-r--r--java/com/google/turbine/binder/sym/ParamSymbol.java66
-rw-r--r--java/com/google/turbine/binder/sym/Symbol.java4
-rw-r--r--java/com/google/turbine/binder/sym/TyVarSymbol.java2
-rw-r--r--java/com/google/turbine/bytecode/AnnotationWriter.java10
-rw-r--r--java/com/google/turbine/bytecode/AttributeWriter.java2
-rw-r--r--java/com/google/turbine/bytecode/ClassFile.java66
-rw-r--r--java/com/google/turbine/bytecode/ClassReader.java51
-rw-r--r--java/com/google/turbine/bytecode/ClassWriter.java2
-rw-r--r--java/com/google/turbine/bytecode/ConstantPool.java3
-rw-r--r--java/com/google/turbine/bytecode/sig/SigWriter.java4
-rw-r--r--java/com/google/turbine/deps/Dependencies.java72
-rw-r--r--java/com/google/turbine/deps/Transitive.java5
-rw-r--r--java/com/google/turbine/diag/SourceFile.java31
-rw-r--r--java/com/google/turbine/diag/TurbineDiagnostic.java101
-rw-r--r--java/com/google/turbine/diag/TurbineError.java16
-rw-r--r--java/com/google/turbine/diag/TurbineLog.java50
-rw-r--r--java/com/google/turbine/lower/Lower.java30
-rw-r--r--java/com/google/turbine/lower/LowerSignature.java20
-rw-r--r--java/com/google/turbine/main/Main.java289
-rw-r--r--java/com/google/turbine/model/Const.java116
-rw-r--r--java/com/google/turbine/options/TurbineOptions.java374
-rw-r--r--java/com/google/turbine/options/TurbineOptionsParser.java47
-rw-r--r--java/com/google/turbine/parse/ConstExpressionParser.java22
-rw-r--r--java/com/google/turbine/parse/IteratorLexer.java8
-rw-r--r--java/com/google/turbine/parse/Lexer.java3
-rw-r--r--java/com/google/turbine/parse/Parser.java125
-rw-r--r--java/com/google/turbine/parse/StreamLexer.java51
-rw-r--r--java/com/google/turbine/parse/UnicodeEscapePreprocessor.java15
-rw-r--r--java/com/google/turbine/parse/VariableInitializerParser.java11
-rw-r--r--java/com/google/turbine/processing/ClassHierarchy.java170
-rw-r--r--java/com/google/turbine/processing/ModelFactory.java391
-rw-r--r--java/com/google/turbine/processing/TurbineAnnotationMirror.java349
-rw-r--r--java/com/google/turbine/processing/TurbineAnnotationProxy.java176
-rw-r--r--java/com/google/turbine/processing/TurbineAnnotationValueMirror.java25
-rw-r--r--java/com/google/turbine/processing/TurbineElement.java1298
-rw-r--r--java/com/google/turbine/processing/TurbineElements.java360
-rw-r--r--java/com/google/turbine/processing/TurbineFiler.java435
-rw-r--r--java/com/google/turbine/processing/TurbineMessager.java252
-rw-r--r--java/com/google/turbine/processing/TurbineName.java67
-rw-r--r--java/com/google/turbine/processing/TurbineProcessingEnvironment.java105
-rw-r--r--java/com/google/turbine/processing/TurbineRoundEnvironment.java99
-rw-r--r--java/com/google/turbine/processing/TurbineTypeMirror.java770
-rw-r--r--java/com/google/turbine/processing/TurbineTypes.java1132
-rw-r--r--java/com/google/turbine/tree/Pretty.java2
-rw-r--r--java/com/google/turbine/tree/Tree.java37
-rw-r--r--java/com/google/turbine/type/AnnoInfo.java33
-rw-r--r--java/com/google/turbine/type/Type.java302
-rw-r--r--java/com/google/turbine/types/Canonicalize.java16
-rw-r--r--java/com/google/turbine/types/Erasure.java51
-rw-r--r--javatests/com/google/turbine/binder/BinderErrorTest.java176
-rw-r--r--javatests/com/google/turbine/binder/BinderTest.java174
-rw-r--r--javatests/com/google/turbine/binder/ClassPathBinderTest.java17
-rw-r--r--javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java93
-rw-r--r--javatests/com/google/turbine/bytecode/ClassReaderTest.java13
-rw-r--r--javatests/com/google/turbine/bytecode/ClassWriterTest.java3
-rw-r--r--javatests/com/google/turbine/bytecode/sig/SigRegressionTest.java1
-rw-r--r--javatests/com/google/turbine/deps/AbstractTransitiveTest.java22
-rw-r--r--javatests/com/google/turbine/deps/DependenciesTest.java98
-rw-r--r--javatests/com/google/turbine/deps/TransitiveTest.java16
-rw-r--r--javatests/com/google/turbine/lower/IntegrationTestSupport.java138
-rw-r--r--javatests/com/google/turbine/lower/LowerIntegrationTest.java3
-rw-r--r--javatests/com/google/turbine/lower/LowerSignatureTest.java2
-rw-r--r--javatests/com/google/turbine/lower/LowerTest.java29
-rw-r--r--javatests/com/google/turbine/lower/testdata/local.test8
-rw-r--r--javatests/com/google/turbine/lower/testdata/tyanno_inner.test16
-rw-r--r--javatests/com/google/turbine/lower/testdata/tyanno_varargs.test12
-rw-r--r--javatests/com/google/turbine/main/MainTest.java315
-rw-r--r--javatests/com/google/turbine/main/ReducedClasspathTest.java252
-rw-r--r--javatests/com/google/turbine/model/ConstTest.java74
-rw-r--r--javatests/com/google/turbine/options/TurbineOptionsTest.java57
-rw-r--r--javatests/com/google/turbine/parse/CommentParserTest.java78
-rw-r--r--javatests/com/google/turbine/parse/JavacLexer.java3
-rw-r--r--javatests/com/google/turbine/parse/LexerTest.java6
-rw-r--r--javatests/com/google/turbine/parse/ParseErrorTest.java51
-rw-r--r--javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java21
-rw-r--r--javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java46
-rw-r--r--javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java527
-rw-r--r--javatests/com/google/turbine/processing/ProcessingIntegrationTest.java346
-rw-r--r--javatests/com/google/turbine/processing/TurbineAnnotationMirrorTest.java249
-rw-r--r--javatests/com/google/turbine/processing/TurbineAnnotationProxyTest.java219
-rw-r--r--javatests/com/google/turbine/processing/TurbineElementTest.java234
-rw-r--r--javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java293
-rw-r--r--javatests/com/google/turbine/processing/TurbineElementsTest.java353
-rw-r--r--javatests/com/google/turbine/processing/TurbineFilerTest.java172
-rw-r--r--javatests/com/google/turbine/processing/TurbineMessagerTest.java248
-rw-r--r--javatests/com/google/turbine/processing/TurbineNameTest.java54
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypeMirrorTest.java281
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypesContainsTest.java53
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypesFactoryTest.java181
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypesIsAssignableTest.java57
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypesIsSameTypeTest.java42
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypesIsSubsignatureTest.java50
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypesIsSubtypeTest.java57
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypesUnaryTest.java147
-rw-r--r--javatests/com/google/turbine/testing/TestClassPaths.java2
-rw-r--r--javatests/com/google/turbine/type/TypeTest.java69
-rw-r--r--javatests/com/google/turbine/zip/ZipTest.java2
-rw-r--r--pom.xml10
-rw-r--r--proto/deps.proto6
-rw-r--r--proto/manifest.proto40
138 files changed, 13825 insertions, 1334 deletions
diff --git a/.travis.yml b/.travis.yml
index 3ea8885..8113f5f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,8 @@
language: java
jdk:
- - oraclejdk8
- - oraclejdk9
+ - openjdk8
+ - openjdk9
- openjdk10
- openjdk11
- openjdk-ea
diff --git a/Android.bp b/Android.bp
index 8a1af22..f5880c2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,12 +14,30 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["external_turbine_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "external_turbine_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "LICENSE",
+ ],
+}
+
java_library_host {
name: "turbine",
srcs: [
"java/**/*.java",
"proto/**/*.proto",
+ "android-annotation-stubs/src/**/*.java",
],
manifest: "manifest.txt",
@@ -28,8 +46,11 @@ java_library_host {
"guava",
],
- plugins: ["dagger2-auto-value"],
- libs: ["dagger2-auto-value"],
+ plugins: ["auto_value_plugin"],
+ libs: [
+ "auto_value_annotations",
+ "auto_value_memoized_extension_annotations",
+ ],
proto: {
type: "full",
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..e166a49
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,17 @@
+name: "turbine"
+description:
+ "Turbine is a header compiler for Java."
+
+third_party {
+ url {
+ type: HOMEPAGE
+ value: "https://github.com/google/turbine"
+ }
+ url {
+ type: GIT
+ value: "https://github.com/google/turbine"
+ }
+ version: "a963d859dc98108c37a701c1f76c4494fc480198"
+ last_upgrade_date { year: 2020 month: 3 day: 27 }
+ license_type: NOTICE
+}
diff --git a/NOTICE b/NOTICE
deleted file mode 100644
index d645695..0000000
--- a/NOTICE
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- 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.
diff --git a/android-annotation-stubs/gen_annotations.sh b/android-annotation-stubs/gen_annotations.sh
new file mode 100755
index 0000000..1965324
--- /dev/null
+++ b/android-annotation-stubs/gen_annotations.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+declare -A INNER
+declare -A PARAMETER
+declare -A IMPORT
+
+ANNOTATIONS=(
+ org.checkerframework.checker.nullness.qual.Nullable
+ org.checkerframework.checker.nullness.compatqual.NullableDecl
+)
+
+for a in ${ANNOTATIONS[@]}; do
+ package=${a%.*}
+ class=${a##*.}
+ dir=$(dirname $0)/src/${package//.//}
+ file=${class}.java
+ inner=${INNER[$a]}
+ parameter=${PARAMETER[$a]}
+ import=
+
+ if [ -n "${parameter}" ]; then
+ parameter="${parameter} value();"
+ fi
+
+ for i in ${IMPORT[$a]}; do
+ import="${import}import ${i};"
+ done
+
+ mkdir -p ${dir}
+ sed -e"s/__PACKAGE__/${package}/" \
+ -e"s/__CLASS__/${class}/" \
+ -e"s/__INNER__/${inner}/" \
+ -e"s/__PARAMETER__/${parameter}/" \
+ -e"s/__IMPORT__/${import}/" \
+ $(dirname $0)/tmpl.java > ${dir}/${file}
+ google-java-format -i ${dir}/${file}
+done
diff --git a/android-annotation-stubs/src/org/checkerframework/checker/nullness/compatqual/NullableDecl.java b/android-annotation-stubs/src/org/checkerframework/checker/nullness/compatqual/NullableDecl.java
new file mode 100644
index 0000000..2d39b0f
--- /dev/null
+++ b/android-annotation-stubs/src/org/checkerframework/checker/nullness/compatqual/NullableDecl.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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 org.checkerframework.checker.nullness.compatqual;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/* This is an annotation stub to avoid dependencies on annotations that aren't
+ * in the Android platform source tree. */
+
+@Target({
+ ElementType.ANNOTATION_TYPE,
+ ElementType.CONSTRUCTOR,
+ ElementType.FIELD,
+ ElementType.LOCAL_VARIABLE,
+ ElementType.METHOD,
+ ElementType.PACKAGE,
+ ElementType.PARAMETER,
+ ElementType.TYPE,
+ ElementType.TYPE_PARAMETER,
+ ElementType.TYPE_USE
+})
+@Retention(RetentionPolicy.SOURCE)
+public @interface NullableDecl {}
diff --git a/android-annotation-stubs/src/org/checkerframework/checker/nullness/qual/Nullable.java b/android-annotation-stubs/src/org/checkerframework/checker/nullness/qual/Nullable.java
new file mode 100644
index 0000000..276d64c
--- /dev/null
+++ b/android-annotation-stubs/src/org/checkerframework/checker/nullness/qual/Nullable.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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 org.checkerframework.checker.nullness.qual;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/* This is an annotation stub to avoid dependencies on annotations that aren't
+ * in the Android platform source tree. */
+
+@Target({
+ ElementType.ANNOTATION_TYPE,
+ ElementType.CONSTRUCTOR,
+ ElementType.FIELD,
+ ElementType.LOCAL_VARIABLE,
+ ElementType.METHOD,
+ ElementType.PACKAGE,
+ ElementType.PARAMETER,
+ ElementType.TYPE,
+ ElementType.TYPE_PARAMETER,
+ ElementType.TYPE_USE
+})
+@Retention(RetentionPolicy.SOURCE)
+public @interface Nullable {}
diff --git a/android-annotation-stubs/tmpl.java b/android-annotation-stubs/tmpl.java
new file mode 100644
index 0000000..c4df609
--- /dev/null
+++ b/android-annotation-stubs/tmpl.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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 __PACKAGE__;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+__IMPORT__
+
+/* This is an annotation stub to avoid dependencies on annotations that aren't
+ * in the Android platform source tree. */
+
+@Target({
+ ElementType.ANNOTATION_TYPE,
+ ElementType.CONSTRUCTOR,
+ ElementType.FIELD,
+ ElementType.LOCAL_VARIABLE,
+ ElementType.METHOD,
+ ElementType.PACKAGE,
+ ElementType.PARAMETER,
+ ElementType.TYPE,
+ ElementType.TYPE_PARAMETER,
+ ElementType.TYPE_USE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface __CLASS__ {
+ __INNER__
+ __PARAMETER__
+}
diff --git a/java/com/google/turbine/binder/Binder.java b/java/com/google/turbine/binder/Binder.java
index cffe291..0e3f41f 100644
--- a/java/com/google/turbine/binder/Binder.java
+++ b/java/com/google/turbine/binder/Binder.java
@@ -16,11 +16,13 @@
package com.google.turbine.binder;
+import com.google.auto.value.AutoValue;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.turbine.binder.CompUnitPreprocessor.PreprocessedCompUnit;
+import com.google.turbine.binder.Processing.ProcessorInfo;
import com.google.turbine.binder.Resolve.CanonicalResolver;
import com.google.turbine.binder.bound.BoundClass;
import com.google.turbine.binder.bound.HeaderBoundClass;
@@ -51,6 +53,7 @@ import com.google.turbine.binder.lookup.WildImportIndex;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.ModuleSymbol;
+import com.google.turbine.diag.SourceFile;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.diag.TurbineError.ErrorKind;
import com.google.turbine.diag.TurbineLog;
@@ -60,19 +63,56 @@ import com.google.turbine.tree.Tree;
import com.google.turbine.tree.Tree.CompUnit;
import com.google.turbine.tree.Tree.ModDecl;
import com.google.turbine.type.Type;
-import java.util.List;
+import java.time.Duration;
import java.util.Optional;
+import javax.annotation.processing.Processor;
/** The entry point for analysis. */
public class Binder {
/** Binds symbols and types to the given compilation units. */
public static BindingResult bind(
- List<CompUnit> units,
+ ImmutableList<CompUnit> units,
ClassPath classpath,
ClassPath bootclasspath,
Optional<String> moduleVersion) {
+ return bind(units, classpath, Processing.ProcessorInfo.empty(), bootclasspath, moduleVersion);
+ }
+ /** Binds symbols and types to the given compilation units. */
+ public static BindingResult bind(
+ ImmutableList<CompUnit> units,
+ ClassPath classpath,
+ ProcessorInfo processorInfo,
+ 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()) {
+ br =
+ Processing.process(
+ log, units, classpath, processorInfo, bootclasspath, br, moduleVersion);
+ }
+ log.maybeThrow();
+ return br;
+ }
+
+ static BindingResult bind(
+ TurbineLog log,
+ ImmutableList<CompUnit> units,
+ ImmutableMap<String, SourceFile> generatedSources,
+ ImmutableMap<String, byte[]> generatedClasses,
+ ClassPath classpath,
+ ClassPath bootclasspath,
+ Optional<String> moduleVersion) {
ImmutableList<PreprocessedCompUnit> preProcessedUnits = CompUnitPreprocessor.preprocess(units);
SimpleEnv<ClassSymbol, SourceBoundClass> ienv = bindSourceBoundClasses(preProcessedUnits);
@@ -91,8 +131,6 @@ public class Binder {
CompoundEnv<ModuleSymbol, ModuleInfo> classPathModuleEnv =
CompoundEnv.of(classpath.moduleEnv()).append(bootclasspath.moduleEnv());
- TurbineLog log = new TurbineLog();
-
BindPackagesResult bindPackagesResult =
bindPackages(log, ienv, tli, preProcessedUnits, classPathEnv);
@@ -108,11 +146,12 @@ public class Binder {
henv,
CompoundEnv.<ClassSymbol, HeaderBoundClass>of(classPathEnv).append(henv));
- log.maybeThrow();
-
tenv =
constants(
- syms, tenv, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv));
+ syms,
+ tenv,
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv),
+ log);
tenv =
disambiguateTypeAnnotations(
syms, tenv, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv));
@@ -125,13 +164,22 @@ public class Binder {
modules,
CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv),
classPathModuleEnv,
- moduleVersion);
+ moduleVersion,
+ log);
ImmutableMap.Builder<ClassSymbol, SourceTypeBoundClass> result = ImmutableMap.builder();
for (ClassSymbol sym : syms) {
result.put(sym, tenv.get(sym));
}
- return new BindingResult(result.build(), boundModules, classPathEnv);
+
+ return new BindingResult(
+ result.build(),
+ boundModules,
+ classPathEnv,
+ tli,
+ generatedSources,
+ generatedClasses,
+ Statistics.empty());
}
/** Records enclosing declarations of member classes, and group classes by compilation unit. */
@@ -262,7 +310,8 @@ public class Binder {
SimpleEnv<ModuleSymbol, PackageSourceBoundModule> modules,
CompoundEnv<ClassSymbol, TypeBoundClass> env,
CompoundEnv<ModuleSymbol, ModuleInfo> moduleEnv,
- Optional<String> moduleVersion) {
+ Optional<String> moduleVersion,
+ TurbineLog log) {
// Allow resolution of modules in the current compilation. Currently this is only needed for
// version strings in requires directives.
moduleEnv =
@@ -288,7 +337,9 @@ public class Binder {
});
ImmutableList.Builder<SourceModuleInfo> bound = ImmutableList.builder();
for (PackageSourceBoundModule module : modules.asMap().values()) {
- bound.add(ModuleBinder.bind(module, env, moduleEnv, moduleVersion));
+ bound.add(
+ ModuleBinder.bind(
+ module, env, moduleEnv, moduleVersion, log.withSource(module.source())));
}
return bound.build();
}
@@ -296,7 +347,8 @@ public class Binder {
private static Env<ClassSymbol, SourceTypeBoundClass> constants(
ImmutableSet<ClassSymbol> syms,
Env<ClassSymbol, SourceTypeBoundClass> env,
- CompoundEnv<ClassSymbol, TypeBoundClass> baseEnv) {
+ CompoundEnv<ClassSymbol, TypeBoundClass> baseEnv,
+ TurbineLog log) {
// Prepare to lazily evaluate constant fields in each compilation unit.
// The laziness is necessary since constant fields can reference other
@@ -322,7 +374,8 @@ public class Binder {
info.source(),
info.scope(),
env1,
- baseEnv)
+ baseEnv,
+ log.withSource(info.source()))
.evalFieldInitializer(field.decl().init().get(), field.type());
} catch (LazyEnv.LazyBindingError e) {
// fields initializers are allowed to reference the field being initialized,
@@ -342,7 +395,9 @@ public class Binder {
SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder();
for (ClassSymbol sym : syms) {
- builder.put(sym, new ConstBinder(constenv, sym, baseEnv, env.get(sym)).bind());
+ SourceTypeBoundClass base = env.get(sym);
+ builder.put(
+ sym, new ConstBinder(constenv, sym, baseEnv, base, log.withSource(base.source())).bind());
}
return builder.build();
}
@@ -387,19 +442,57 @@ public class Binder {
return builder.build();
}
+ /** Statistics about annotation processing. */
+ @AutoValue
+ public abstract static class Statistics {
+
+ /**
+ * The total elapsed time spent in {@link Processor#init} and {@link Processor#process} across
+ * all rounds for each annotation processor.
+ */
+ public abstract ImmutableMap<String, Duration> processingTime();
+
+ /**
+ * Serialized protos containing processor-specific metrics. Currently only supported for Dagger.
+ */
+ public abstract ImmutableMap<String, byte[]> processorMetrics();
+
+ public static Statistics create(
+ ImmutableMap<String, Duration> processingTime,
+ ImmutableMap<String, byte[]> processorMetrics) {
+ return new AutoValue_Binder_Statistics(processingTime, processorMetrics);
+ }
+
+ public static Statistics empty() {
+ return create(ImmutableMap.of(), ImmutableMap.of());
+ }
+ }
+
/** The result of binding: bound nodes for sources in the compilation, and the classpath. */
public static class BindingResult {
private final ImmutableMap<ClassSymbol, SourceTypeBoundClass> units;
private final ImmutableList<SourceModuleInfo> modules;
private final CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv;
+ private final TopLevelIndex tli;
+ private final ImmutableMap<String, SourceFile> generatedSources;
+ private final ImmutableMap<String, byte[]> generatedClasses;
+ private final Statistics statistics;
public BindingResult(
ImmutableMap<ClassSymbol, SourceTypeBoundClass> units,
ImmutableList<SourceModuleInfo> modules,
- CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv) {
+ CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv,
+ TopLevelIndex tli,
+ ImmutableMap<String, SourceFile> generatedSources,
+ ImmutableMap<String, byte[]> generatedClasses,
+ Statistics statistics) {
this.units = units;
this.modules = modules;
this.classPathEnv = classPathEnv;
+ this.tli = tli;
+ this.generatedSources = generatedSources;
+ this.generatedClasses = generatedClasses;
+ this.statistics = statistics;
}
/** Bound nodes for sources in the compilation. */
@@ -415,5 +508,36 @@ public class Binder {
public CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv() {
return classPathEnv;
}
+
+ public TopLevelIndex tli() {
+ return tli;
+ }
+
+ public ImmutableMap<String, SourceFile> generatedSources() {
+ return generatedSources;
+ }
+
+ public ImmutableMap<String, byte[]> generatedClasses() {
+ return generatedClasses;
+ }
+
+ public Statistics statistics() {
+ return statistics;
+ }
+
+ public BindingResult withGeneratedClasses(ImmutableMap<String, byte[]> generatedClasses) {
+ return new BindingResult(
+ units, modules, classPathEnv, tli, generatedSources, generatedClasses, statistics);
+ }
+
+ public BindingResult withGeneratedSources(ImmutableMap<String, SourceFile> generatedSources) {
+ return new BindingResult(
+ units, modules, classPathEnv, tli, generatedSources, generatedClasses, statistics);
+ }
+
+ public BindingResult withStatistics(Statistics statistics) {
+ return new BindingResult(
+ units, modules, classPathEnv, tli, generatedSources, generatedClasses, statistics);
+ }
}
}
diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java
index 934ec54..a2f045a 100644
--- a/java/com/google/turbine/binder/CanonicalTypeBinder.java
+++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java
@@ -147,8 +147,8 @@ public class CanonicalTypeBinder {
ClassSymbol sym,
ParamInfo base) {
return new ParamInfo(
+ base.sym(),
Canonicalize.canonicalize(source, position, env, sym, base.type()),
- base.name(),
base.annotations(),
base.access());
}
@@ -162,8 +162,9 @@ public class CanonicalTypeBinder {
ImmutableMap.Builder<TyVarSymbol, TyVarInfo> result = ImmutableMap.builder();
for (Map.Entry<TyVarSymbol, TyVarInfo> e : tps.entrySet()) {
TyVarInfo info = e.getValue();
- Type bound = Canonicalize.canonicalize(source, position, env, sym, info.bound());
- result.put(e.getKey(), new TyVarInfo((IntersectionTy) bound, info.annotations()));
+ IntersectionTy upperBound =
+ (IntersectionTy) Canonicalize.canonicalize(source, position, env, sym, info.upperBound());
+ result.put(e.getKey(), new TyVarInfo(upperBound, /* lowerBound= */ null, info.annotations()));
}
return result.build();
}
diff --git a/java/com/google/turbine/binder/ClassPath.java b/java/com/google/turbine/binder/ClassPath.java
index d3461bf..eeea7c5 100644
--- a/java/com/google/turbine/binder/ClassPath.java
+++ b/java/com/google/turbine/binder/ClassPath.java
@@ -16,6 +16,7 @@
package com.google.turbine.binder;
+import com.google.common.base.Supplier;
import com.google.turbine.binder.bound.ModuleInfo;
import com.google.turbine.binder.bytecode.BytecodeBoundClass;
import com.google.turbine.binder.env.Env;
@@ -24,7 +25,7 @@ import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.ModuleSymbol;
/**
- * A compilation classpath, e.g. the user or platform class path. Maybe backed by a search path of
+ * A compilation classpath, e.g. the user or platform class path. May be backed by a search path of
* jar files, or a jrtfs filesystem.
*/
public interface ClassPath {
@@ -36,4 +37,6 @@ public interface ClassPath {
/** The classpath's top level index. */
TopLevelIndex index();
+
+ Supplier<byte[]> resource(String path);
}
diff --git a/java/com/google/turbine/binder/ClassPathBinder.java b/java/com/google/turbine/binder/ClassPathBinder.java
index 5d8db86..8aead80 100644
--- a/java/com/google/turbine/binder/ClassPathBinder.java
+++ b/java/com/google/turbine/binder/ClassPathBinder.java
@@ -53,6 +53,7 @@ public class ClassPathBinder {
Map<ClassSymbol, BytecodeBoundClass> transitive = new LinkedHashMap<>();
Map<ClassSymbol, BytecodeBoundClass> map = new HashMap<>();
Map<ModuleSymbol, ModuleInfo> modules = new HashMap<>();
+ Map<String, Supplier<byte[]>> resources = new HashMap<>();
Env<ClassSymbol, BytecodeBoundClass> benv =
new Env<ClassSymbol, BytecodeBoundClass>() {
@Override
@@ -62,7 +63,7 @@ public class ClassPathBinder {
};
for (Path path : paths) {
try {
- bindJar(path, map, modules, benv, transitive);
+ bindJar(path, map, modules, benv, transitive, resources);
} catch (IOException e) {
throw new IOException("error reading " + path, e);
}
@@ -89,6 +90,11 @@ public class ClassPathBinder {
public TopLevelIndex index() {
return index;
}
+
+ @Override
+ public Supplier<byte[]> resource(String path) {
+ return resources.get(path);
+ }
};
}
@@ -97,12 +103,14 @@ public class ClassPathBinder {
Map<ClassSymbol, BytecodeBoundClass> env,
Map<ModuleSymbol, ModuleInfo> modules,
Env<ClassSymbol, BytecodeBoundClass> benv,
- Map<ClassSymbol, BytecodeBoundClass> transitive)
+ Map<ClassSymbol, BytecodeBoundClass> transitive,
+ Map<String, Supplier<byte[]>> resources)
throws IOException {
// TODO(cushon): don't leak file descriptors
for (Zip.Entry ze : new Zip.ZipIterable(path)) {
String name = ze.name();
if (!name.endsWith(".class")) {
+ resources.put(name, toByteArrayOrDie(ze));
continue;
}
if (name.startsWith(TRANSITIVE_PREFIX)) {
diff --git a/java/com/google/turbine/binder/CompUnitPreprocessor.java b/java/com/google/turbine/binder/CompUnitPreprocessor.java
index 85ff1c0..ed70e88 100644
--- a/java/com/google/turbine/binder/CompUnitPreprocessor.java
+++ b/java/com/google/turbine/binder/CompUnitPreprocessor.java
@@ -219,6 +219,7 @@ public class CompUnitPreprocessor {
Optional.empty(),
ImmutableList.of(),
ImmutableList.of(),
- TurbineTyKind.INTERFACE);
+ TurbineTyKind.INTERFACE,
+ /* javadoc= */ null);
}
}
diff --git a/java/com/google/turbine/binder/ConstBinder.java b/java/com/google/turbine/binder/ConstBinder.java
index 0f989dd..3a41e94 100644
--- a/java/com/google/turbine/binder/ConstBinder.java
+++ b/java/com/google/turbine/binder/ConstBinder.java
@@ -33,6 +33,7 @@ import com.google.turbine.binder.env.Env;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.diag.TurbineLog.TurbineLogWithSource;
import com.google.turbine.model.Const;
import com.google.turbine.model.Const.ArrayInitValue;
import com.google.turbine.model.Const.Kind;
@@ -63,19 +64,29 @@ public class ConstBinder {
private final SourceTypeBoundClass base;
private final CompoundEnv<ClassSymbol, TypeBoundClass> env;
private final ConstEvaluator constEvaluator;
+ private final TurbineLogWithSource log;
public ConstBinder(
Env<FieldSymbol, Value> constantEnv,
ClassSymbol origin,
CompoundEnv<ClassSymbol, TypeBoundClass> env,
- SourceTypeBoundClass base) {
+ SourceTypeBoundClass base,
+ TurbineLogWithSource log) {
this.constantEnv = constantEnv;
this.origin = origin;
this.base = base;
this.env = env;
+ this.log = log;
this.constEvaluator =
new ConstEvaluator(
- origin, origin, base.memberImports(), base.source(), base.scope(), constantEnv, env);
+ origin,
+ origin,
+ base.memberImports(),
+ base.source(),
+ base.scope(),
+ constantEnv,
+ env,
+ log);
}
public SourceTypeBoundClass bind() {
@@ -87,7 +98,8 @@ public class ConstBinder {
base.source(),
base.enclosingScope(),
constantEnv,
- env)
+ env,
+ log)
.evaluateAnnotations(base.annotations());
ImmutableList<TypeBoundClass.FieldInfo> fields = fields(base.fields());
ImmutableList<MethodInfo> methods = bindMethods(base.methods());
@@ -149,7 +161,7 @@ public class ConstBinder {
private ParamInfo bindParameter(ParamInfo base) {
ImmutableList<AnnoInfo> annos = constEvaluator.evaluateAnnotations(base.annotations());
- return new ParamInfo(bindType(base.type()), base.name(), annos, base.access());
+ return new ParamInfo(base.sym(), bindType(base.type()), annos, base.access());
}
static AnnotationMetadata bindAnnotationMetadata(
@@ -161,7 +173,11 @@ public class ConstBinder {
ImmutableSet<TurbineElementType> target = null;
ClassSymbol repeatable = null;
for (AnnoInfo annotation : annotations) {
- switch (annotation.sym().binaryName()) {
+ ClassSymbol sym = annotation.sym();
+ if (sym == null) {
+ continue;
+ }
+ switch (sym.binaryName()) {
case "java/lang/annotation/Retention":
retention = bindRetention(annotation);
break;
@@ -286,7 +302,8 @@ public class ConstBinder {
result.put(
entry.getKey(),
new TyVarInfo(
- (IntersectionTy) bindType(info.bound()),
+ (IntersectionTy) bindType(info.upperBound()),
+ /* lowerBound= */ null,
constEvaluator.evaluateAnnotations(info.annotations())));
}
return result.build();
@@ -318,12 +335,12 @@ public class ConstBinder {
return WildLowerBoundedTy.create(
bindType(wildTy.bound()),
constEvaluator.evaluateAnnotations(wildTy.annotations()));
- default:
- throw new AssertionError(wildTy.boundKind());
}
+ throw new AssertionError(wildTy.boundKind());
}
case PRIM_TY:
case VOID_TY:
+ case ERROR_TY:
return type;
case INTERSECTION_TY:
return IntersectionTy.create(bindTypes(((IntersectionTy) type).bounds()));
diff --git a/java/com/google/turbine/binder/ConstEvaluator.java b/java/com/google/turbine/binder/ConstEvaluator.java
index 0e837a4..9d5f042 100644
--- a/java/com/google/turbine/binder/ConstEvaluator.java
+++ b/java/com/google/turbine/binder/ConstEvaluator.java
@@ -22,8 +22,8 @@ import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
-import com.google.turbine.binder.bound.AnnotationValue;
import com.google.turbine.binder.bound.EnumConstantValue;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
import com.google.turbine.binder.bound.TurbineClassValue;
import com.google.turbine.binder.bound.TypeBoundClass;
import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
@@ -38,8 +38,10 @@ import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.Symbol;
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.TurbineLogWithSource;
import com.google.turbine.model.Const;
import com.google.turbine.model.Const.ConstCastError;
import com.google.turbine.model.Const.Value;
@@ -91,14 +93,17 @@ public strictfp class ConstEvaluator {
private final Scope scope;
+ private final TurbineLogWithSource log;
+
public ConstEvaluator(
ClassSymbol origin,
ClassSymbol owner,
MemberImportIndex memberImports,
SourceFile source,
Scope scope,
- Env<FieldSymbol, Const.Value> values,
- CompoundEnv<ClassSymbol, TypeBoundClass> env) {
+ Env<FieldSymbol, Value> values,
+ CompoundEnv<ClassSymbol, TypeBoundClass> env,
+ TurbineLogWithSource log) {
this.origin = origin;
this.owner = owner;
@@ -107,6 +112,7 @@ public strictfp class ConstEvaluator {
this.values = values;
this.env = env;
this.scope = scope;
+ this.log = log;
}
/** Evaluates the given expression's value. */
@@ -139,9 +145,8 @@ public strictfp class ConstEvaluator {
case SHORT:
case BYTE:
case NULL:
- default:
- throw new AssertionError(a.constantTypeKind());
}
+ throw new AssertionError(a.constantTypeKind());
}
case VOID_TY:
throw new AssertionError(t.kind());
@@ -162,7 +167,7 @@ public strictfp class ConstEvaluator {
case ANNO_EXPR:
return evalAnno(((Tree.AnnoExpr) t).value());
default:
- throw new AssertionError(t.kind());
+ throw error(t.position(), ErrorKind.EXPRESSION_ERROR);
}
}
@@ -178,7 +183,7 @@ public strictfp class ConstEvaluator {
case VOID_TY:
return Type.VOID;
case CLASS_TY:
- return Type.ClassTy.asNonParametricClassTy(resolveClass((ClassTy) type));
+ return resolveClass((ClassTy) type);
case ARR_TY:
return Type.ArrayTy.create(
evalClassLiteralType(((Tree.ArrTy) type).elem()), ImmutableList.of());
@@ -191,19 +196,20 @@ public strictfp class ConstEvaluator {
* Resolves the {@link ClassSymbol} for the given {@link Tree.ClassTy}, with handling for
* non-canonical qualified type names.
*
- * <p>Similar to {@link HierarchyBinder#resolveClass}, except we can't unconditionally consider
+ * <p>Similar to {@code HierarchyBinder#resolveClass}, except we can't unconditionally consider
* members of the current class (e.g. when binding constants inside annotations on that class),
* and when we do want to consider members we can rely on them being in the current scope (it
* isn't completed during the hierarchy phase).
*/
- private ClassSymbol resolveClass(ClassTy classTy) {
+ private Type resolveClass(ClassTy classTy) {
ArrayDeque<Ident> flat = new ArrayDeque<>();
for (ClassTy curr = classTy; curr != null; curr = curr.base().orElse(null)) {
flat.addFirst(curr.name());
}
LookupResult result = scope.lookup(new LookupKey(ImmutableList.copyOf(flat)));
if (result == null) {
- throw error(classTy.position(), ErrorKind.CANNOT_RESOLVE, flat.peekFirst());
+ log.error(classTy.position(), ErrorKind.CANNOT_RESOLVE, flat.peekFirst());
+ return Type.ErrorTy.create(flat);
}
if (result.sym().symKind() != Symbol.Kind.CLASS) {
throw error(classTy.position(), ErrorKind.UNEXPECTED_TYPE_PARAMETER, flat.peekFirst());
@@ -212,7 +218,7 @@ public strictfp class ConstEvaluator {
for (Ident bit : result.remaining()) {
classSym = resolveNext(classTy.position(), classSym, bit);
}
- return classSym;
+ return Type.ClassTy.asNonParametricClassTy(classSym);
}
private ClassSymbol resolveNext(int position, ClassSymbol sym, Ident bit) {
@@ -372,41 +378,41 @@ public strictfp class ConstEvaluator {
}
switch (t.op()) {
case NOT:
- return unaryNegate(expr);
+ return unaryNegate(t.position(), expr);
case BITWISE_COMP:
- return bitwiseComp(expr);
+ return bitwiseComp(t.position(), expr);
case UNARY_PLUS:
- return unaryPlus(expr);
+ return unaryPlus(t.position(), expr);
case NEG:
- return unaryMinus(expr);
+ return unaryMinus(t.position(), expr);
default:
throw new AssertionError(t.op());
}
}
- private Value unaryNegate(Value expr) {
+ private Value unaryNegate(int position, Value expr) {
switch (expr.constantTypeKind()) {
case BOOLEAN:
return new Const.BooleanValue(!expr.asBoolean().value());
default:
- throw new AssertionError(expr.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, expr.constantTypeKind());
}
}
- private Value bitwiseComp(Value expr) {
- expr = promoteUnary(expr);
+ private Value bitwiseComp(int position, Value expr) {
+ expr = promoteUnary(position, expr);
switch (expr.constantTypeKind()) {
case INT:
return new Const.IntValue(~expr.asInteger().value());
case LONG:
return new Const.LongValue(~expr.asLong().value());
default:
- throw new AssertionError(expr.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, expr.constantTypeKind());
}
}
- private Value unaryPlus(Value expr) {
- expr = promoteUnary(expr);
+ private Value unaryPlus(int position, Value expr) {
+ expr = promoteUnary(position, expr);
switch (expr.constantTypeKind()) {
case INT:
return new Const.IntValue(+expr.asInteger().value());
@@ -417,12 +423,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.DoubleValue(+expr.asDouble().value());
default:
- throw new AssertionError(expr.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, expr.constantTypeKind());
}
}
- private Value unaryMinus(Value expr) {
- expr = promoteUnary(expr);
+ private Value unaryMinus(int position, Value expr) {
+ expr = promoteUnary(position, expr);
switch (expr.constantTypeKind()) {
case INT:
return new Const.IntValue(-expr.asInteger().value());
@@ -433,7 +439,7 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.DoubleValue(-expr.asDouble().value());
default:
- throw new AssertionError(expr.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, expr.constantTypeKind());
}
}
@@ -460,12 +466,12 @@ public strictfp class ConstEvaluator {
}
}
- static Const.Value add(Const.Value a, Const.Value b) {
+ private Const.Value add(int position, Const.Value a, Const.Value b) {
if (a.constantTypeKind() == TurbineConstantTypeKind.STRING
|| b.constantTypeKind() == TurbineConstantTypeKind.STRING) {
return new Const.StringValue(a.asString().value() + b.asString().value());
}
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -478,12 +484,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.DoubleValue(a.asDouble().value() + b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value subtract(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value subtract(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -496,12 +502,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.DoubleValue(a.asDouble().value() - b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value mult(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value mult(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -514,12 +520,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.DoubleValue(a.asDouble().value() * b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value divide(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value divide(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -532,12 +538,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.DoubleValue(a.asDouble().value() / b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value mod(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value mod(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -550,17 +556,17 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.DoubleValue(a.asDouble().value() % b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static final int INT_SHIFT_MASK = 0b11111;
+ private static final int INT_SHIFT_MASK = 0b11111;
- static final int LONG_SHIFT_MASK = 0b111111;
+ private static final int LONG_SHIFT_MASK = 0b111111;
- static Const.Value shiftLeft(Const.Value a, Const.Value b) {
- a = promoteUnary(a);
- b = promoteUnary(b);
+ private Const.Value shiftLeft(int position, Const.Value a, Const.Value b) {
+ a = promoteUnary(position, a);
+ b = promoteUnary(position, b);
switch (a.constantTypeKind()) {
case INT:
return new Const.IntValue(
@@ -568,13 +574,13 @@ public strictfp class ConstEvaluator {
case LONG:
return new Const.LongValue(a.asLong().value() << (b.asInteger().value() & LONG_SHIFT_MASK));
default:
- throw new AssertionError(a.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, a.constantTypeKind());
}
}
- static Const.Value shiftRight(Const.Value a, Const.Value b) {
- a = promoteUnary(a);
- b = promoteUnary(b);
+ private Const.Value shiftRight(int position, Const.Value a, Const.Value b) {
+ a = promoteUnary(position, a);
+ b = promoteUnary(position, b);
switch (a.constantTypeKind()) {
case INT:
return new Const.IntValue(
@@ -582,13 +588,13 @@ public strictfp class ConstEvaluator {
case LONG:
return new Const.LongValue(a.asLong().value() >> (b.asInteger().value() & LONG_SHIFT_MASK));
default:
- throw new AssertionError(a.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, a.constantTypeKind());
}
}
- static Const.Value unsignedShiftRight(Const.Value a, Const.Value b) {
- a = promoteUnary(a);
- b = promoteUnary(b);
+ private Const.Value unsignedShiftRight(int position, Const.Value a, Const.Value b) {
+ a = promoteUnary(position, a);
+ b = promoteUnary(position, b);
switch (a.constantTypeKind()) {
case INT:
return new Const.IntValue(
@@ -597,12 +603,12 @@ public strictfp class ConstEvaluator {
return new Const.LongValue(
a.asLong().value() >>> (b.asInteger().value() & LONG_SHIFT_MASK));
default:
- throw new AssertionError(a.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, a.constantTypeKind());
}
}
- static Const.Value lessThan(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value lessThan(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -615,12 +621,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.BooleanValue(a.asDouble().value() < b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value lessThanEqual(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value lessThanEqual(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -633,12 +639,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.BooleanValue(a.asDouble().value() <= b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value greaterThan(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value greaterThan(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -651,12 +657,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.BooleanValue(a.asDouble().value() > b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value greaterThanEqual(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value greaterThanEqual(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -669,11 +675,11 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.BooleanValue(a.asDouble().value() >= b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value equal(Const.Value a, Const.Value b) {
+ private Const.Value equal(int position, Const.Value a, Const.Value b) {
switch (a.constantTypeKind()) {
case STRING:
return new Const.BooleanValue(a.asString().value().equals(b.asString().value()));
@@ -682,7 +688,7 @@ public strictfp class ConstEvaluator {
default:
break;
}
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -695,11 +701,11 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.BooleanValue(a.asDouble().value() == b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value notEqual(Const.Value a, Const.Value b) {
+ private Const.Value notEqual(int position, Const.Value a, Const.Value b) {
switch (a.constantTypeKind()) {
case STRING:
return new Const.BooleanValue(!a.asString().value().equals(b.asString().value()));
@@ -708,7 +714,7 @@ public strictfp class ConstEvaluator {
default:
break;
}
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -721,18 +727,18 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.BooleanValue(a.asDouble().value() != b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value bitwiseAnd(Const.Value a, Const.Value b) {
+ private Const.Value bitwiseAnd(int position, Const.Value a, Const.Value b) {
switch (a.constantTypeKind()) {
case BOOLEAN:
return new Const.BooleanValue(a.asBoolean().value() & b.asBoolean().value());
default:
break;
}
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -741,18 +747,18 @@ public strictfp class ConstEvaluator {
case LONG:
return new Const.LongValue(a.asLong().value() & b.asLong().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value bitwiseOr(Const.Value a, Const.Value b) {
+ private Const.Value bitwiseOr(int position, Const.Value a, Const.Value b) {
switch (a.constantTypeKind()) {
case BOOLEAN:
return new Const.BooleanValue(a.asBoolean().value() | b.asBoolean().value());
default:
break;
}
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -761,18 +767,18 @@ public strictfp class ConstEvaluator {
case LONG:
return new Const.LongValue(a.asLong().value() | b.asLong().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value bitwiseXor(Const.Value a, Const.Value b) {
+ private Const.Value bitwiseXor(int position, Const.Value a, Const.Value b) {
switch (a.constantTypeKind()) {
case BOOLEAN:
return new Const.BooleanValue(a.asBoolean().value() ^ b.asBoolean().value());
default:
break;
}
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -781,7 +787,7 @@ public strictfp class ConstEvaluator {
case LONG:
return new Const.LongValue(a.asLong().value() ^ b.asLong().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
@@ -793,49 +799,49 @@ public strictfp class ConstEvaluator {
}
switch (t.op()) {
case PLUS:
- return add(lhs, rhs);
+ return add(t.position(), lhs, rhs);
case MINUS:
- return subtract(lhs, rhs);
+ return subtract(t.position(), lhs, rhs);
case MULT:
- return mult(lhs, rhs);
+ return mult(t.position(), lhs, rhs);
case DIVIDE:
- return divide(lhs, rhs);
+ return divide(t.position(), lhs, rhs);
case MODULO:
- return mod(lhs, rhs);
+ return mod(t.position(), lhs, rhs);
case SHIFT_LEFT:
- return shiftLeft(lhs, rhs);
+ return shiftLeft(t.position(), lhs, rhs);
case SHIFT_RIGHT:
- return shiftRight(lhs, rhs);
+ return shiftRight(t.position(), lhs, rhs);
case UNSIGNED_SHIFT_RIGHT:
- return unsignedShiftRight(lhs, rhs);
+ return unsignedShiftRight(t.position(), lhs, rhs);
case LESS_THAN:
- return lessThan(lhs, rhs);
+ return lessThan(t.position(), lhs, rhs);
case GREATER_THAN:
- return greaterThan(lhs, rhs);
+ return greaterThan(t.position(), lhs, rhs);
case LESS_THAN_EQ:
- return lessThanEqual(lhs, rhs);
+ return lessThanEqual(t.position(), lhs, rhs);
case GREATER_THAN_EQ:
- return greaterThanEqual(lhs, rhs);
+ return greaterThanEqual(t.position(), lhs, rhs);
case EQUAL:
- return equal(lhs, rhs);
+ return equal(t.position(), lhs, rhs);
case NOT_EQUAL:
- return notEqual(lhs, rhs);
+ return notEqual(t.position(), lhs, rhs);
case AND:
return new Const.BooleanValue(lhs.asBoolean().value() && rhs.asBoolean().value());
case OR:
return new Const.BooleanValue(lhs.asBoolean().value() || rhs.asBoolean().value());
case BITWISE_AND:
- return bitwiseAnd(lhs, rhs);
+ return bitwiseAnd(t.position(), lhs, rhs);
case BITWISE_XOR:
- return bitwiseXor(lhs, rhs);
+ return bitwiseXor(t.position(), lhs, rhs);
case BITWISE_OR:
- return bitwiseOr(lhs, rhs);
+ return bitwiseOr(t.position(), lhs, rhs);
default:
throw new AssertionError(t.op());
}
}
- private static Const.Value promoteUnary(Const.Value v) {
+ private Const.Value promoteUnary(int position, Value v) {
switch (v.constantTypeKind()) {
case CHAR:
case SHORT:
@@ -847,13 +853,13 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return v;
default:
- throw new AssertionError(v.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, v.constantTypeKind());
}
}
- private static TurbineConstantTypeKind promoteBinary(Const.Value a, Const.Value b) {
- a = promoteUnary(a);
- b = promoteUnary(b);
+ private TurbineConstantTypeKind promoteBinary(int position, Const.Value a, Const.Value b) {
+ a = promoteUnary(position, a);
+ b = promoteUnary(position, b);
switch (a.constantTypeKind()) {
case INT:
switch (b.constantTypeKind()) {
@@ -863,7 +869,7 @@ public strictfp class ConstEvaluator {
case FLOAT:
return b.constantTypeKind();
default:
- throw new AssertionError(b.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, b.constantTypeKind());
}
case LONG:
switch (b.constantTypeKind()) {
@@ -874,7 +880,7 @@ public strictfp class ConstEvaluator {
case FLOAT:
return b.constantTypeKind();
default:
- throw new AssertionError(b.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, b.constantTypeKind());
}
case FLOAT:
switch (b.constantTypeKind()) {
@@ -885,7 +891,7 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return TurbineConstantTypeKind.DOUBLE;
default:
- throw new AssertionError(b.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, b.constantTypeKind());
}
case DOUBLE:
switch (b.constantTypeKind()) {
@@ -895,10 +901,10 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return TurbineConstantTypeKind.DOUBLE;
default:
- throw new AssertionError(b.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, b.constantTypeKind());
}
default:
- throw new AssertionError(a.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, a.constantTypeKind());
}
}
@@ -915,9 +921,17 @@ public strictfp class ConstEvaluator {
* expression trees.
*/
AnnoInfo evaluateAnnotation(AnnoInfo info) {
+ // bail if annotation has not been resolved
+ if (info.sym() == null) {
+ return info;
+ }
+
Map<String, Type> template = new LinkedHashMap<>();
- for (MethodInfo method : env.get(info.sym()).methods()) {
- template.put(method.name(), method.returnType());
+ TypeBoundClass annoClass = env.get(info.sym());
+ if (annoClass != null) {
+ for (MethodInfo method : annoClass.methods()) {
+ template.put(method.name(), method.returnType());
+ }
}
Map<String, Const> values = new LinkedHashMap<>();
@@ -952,7 +966,7 @@ public strictfp class ConstEvaluator {
return info.withValues(ImmutableMap.copyOf(values));
}
- private AnnotationValue evalAnno(Tree.Anno t) {
+ private TurbineAnnotationValue evalAnno(Tree.Anno t) {
LookupResult result = scope.lookup(new LookupKey(t.name()));
if (result == null) {
throw error(
@@ -968,8 +982,8 @@ public strictfp class ConstEvaluator {
if (sym == null) {
return null;
}
- AnnoInfo annoInfo = evaluateAnnotation(new AnnoInfo(source, sym, t, null));
- return new AnnotationValue(annoInfo.sym(), annoInfo.values());
+ AnnoInfo annoInfo = evaluateAnnotation(new AnnoInfo(source, sym, t, ImmutableMap.of()));
+ return new TurbineAnnotationValue(annoInfo);
}
private Const.ArrayInitValue evalArrayInit(ArrayInit t) {
@@ -994,6 +1008,9 @@ public strictfp class ConstEvaluator {
}
switch (ty.tyKind()) {
case PRIM_TY:
+ if (!(value instanceof Const.Value)) {
+ throw error(tree.position(), ErrorKind.EXPRESSION_ERROR);
+ }
return coerce((Const.Value) value, ((Type.PrimTy) ty).primkind());
case CLASS_TY:
case TY_VAR:
@@ -1027,7 +1044,17 @@ public strictfp class ConstEvaluator {
return null;
}
return (Const.Value) cast(type, value);
- } catch (TurbineError | ConstCastError error) {
+ } catch (TurbineError error) {
+ for (TurbineDiagnostic diagnostic : error.diagnostics()) {
+ switch (diagnostic.kind()) {
+ case CANNOT_RESOLVE:
+ // assume this wasn't a constant
+ return null;
+ default: // fall out
+ }
+ }
+ throw error;
+ } catch (ConstCastError error) {
return null;
}
}
diff --git a/java/com/google/turbine/binder/CtSymClassBinder.java b/java/com/google/turbine/binder/CtSymClassBinder.java
index 84fa966..a6f1b3d 100644
--- a/java/com/google/turbine/binder/CtSymClassBinder.java
+++ b/java/com/google/turbine/binder/CtSymClassBinder.java
@@ -105,6 +105,11 @@ public class CtSymClassBinder {
public TopLevelIndex index() {
return index;
}
+
+ @Override
+ public Supplier<byte[]> resource(String input) {
+ return null;
+ }
};
}
diff --git a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
index 9ad20d2..7e3fbda 100644
--- a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
+++ b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
@@ -18,13 +18,14 @@ package com.google.turbine.binder;
import static com.google.common.collect.Iterables.getOnlyElement;
-import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
-import com.google.turbine.binder.bound.AnnotationValue;
+import com.google.common.collect.MultimapBuilder;
+import com.google.turbine.binder.bound.AnnotationMetadata;
import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
import com.google.turbine.binder.bound.TypeBoundClass;
import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
@@ -44,7 +45,6 @@ import com.google.turbine.type.Type.PrimTy;
import com.google.turbine.type.Type.TyVar;
import java.util.Collection;
import java.util.Map;
-import java.util.Set;
/**
* Disambiguate annotations on field, parameter, and method return types that could be declaration
@@ -139,7 +139,7 @@ public class DisambiguateTypeAnnotations {
base.type(),
base.annotations(),
declarationAnnotations);
- return new ParamInfo(type, base.name(), declarationAnnotations.build(), base.access());
+ return new ParamInfo(base.sym(), type, declarationAnnotations.build(), base.access());
}
/**
@@ -151,13 +151,13 @@ public class DisambiguateTypeAnnotations {
TurbineElementType declarationTarget,
Type type,
ImmutableList<AnnoInfo> annotations,
- Builder<AnnoInfo> declarationAnnotations) {
+ ImmutableList.Builder<AnnoInfo> declarationAnnotations) {
// desugar @Repeatable annotations before disambiguating: annotation containers may target
// a subset of the types targeted by their element annotation
annotations = groupRepeated(env, annotations);
ImmutableList.Builder<AnnoInfo> typeAnnotations = ImmutableList.builder();
for (AnnoInfo anno : annotations) {
- Set<TurbineElementType> target = env.get(anno.sym()).annotationMetadata().target();
+ ImmutableSet<TurbineElementType> target = getTarget(env, anno);
if (target.contains(TurbineElementType.TYPE_USE)) {
typeAnnotations.add(anno);
}
@@ -168,6 +168,23 @@ public class DisambiguateTypeAnnotations {
return addAnnotationsToType(type, typeAnnotations.build());
}
+ private static ImmutableSet<TurbineElementType> getTarget(
+ Env<ClassSymbol, TypeBoundClass> env, AnnoInfo anno) {
+ ClassSymbol sym = anno.sym();
+ if (sym == null) {
+ return AnnotationMetadata.DEFAULT_TARGETS;
+ }
+ TypeBoundClass info = env.get(sym);
+ if (info == null) {
+ return AnnotationMetadata.DEFAULT_TARGETS;
+ }
+ AnnotationMetadata metadata = info.annotationMetadata();
+ if (metadata == null) {
+ return AnnotationMetadata.DEFAULT_TARGETS;
+ }
+ return metadata.target();
+ }
+
private static ImmutableList<FieldInfo> bindFields(
Env<ClassSymbol, TypeBoundClass> env, ImmutableList<FieldInfo> fields) {
ImmutableList.Builder<FieldInfo> result = ImmutableList.builder();
@@ -218,6 +235,7 @@ public class DisambiguateTypeAnnotations {
TyVar tyVar = (TyVar) type;
return Type.TyVar.create(tyVar.sym(), appendAnnotations(tyVar.annos(), extra));
case VOID_TY:
+ case ERROR_TY:
return type;
case WILD_TY:
throw new AssertionError("unexpected wildcard type outside type argument context");
@@ -242,21 +260,33 @@ public class DisambiguateTypeAnnotations {
*/
public static ImmutableList<AnnoInfo> groupRepeated(
Env<ClassSymbol, TypeBoundClass> env, ImmutableList<AnnoInfo> annotations) {
- Multimap<ClassSymbol, AnnoInfo> repeated = ArrayListMultimap.create();
+ Multimap<ClassSymbol, AnnoInfo> repeated =
+ MultimapBuilder.linkedHashKeys().arrayListValues().build();
+ ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder();
for (AnnoInfo anno : annotations) {
+ if (anno.sym() == null) {
+ result.add(anno);
+ continue;
+ }
repeated.put(anno.sym(), anno);
}
- Builder<AnnoInfo> result = ImmutableList.builder();
for (Map.Entry<ClassSymbol, Collection<AnnoInfo>> entry : repeated.asMap().entrySet()) {
ClassSymbol symbol = entry.getKey();
Collection<AnnoInfo> infos = entry.getValue();
if (infos.size() > 1) {
- Builder<Const> elements = ImmutableList.builder();
+ ImmutableList.Builder<Const> elements = ImmutableList.builder();
for (AnnoInfo element : infos) {
- elements.add(new AnnotationValue(element.sym(), element.values()));
+ elements.add(new TurbineAnnotationValue(element));
}
- ClassSymbol container = env.get(symbol).annotationMetadata().repeatable();
+ TypeBoundClass info = env.get(symbol);
+ if (info == null || info.annotationMetadata() == null) {
+ continue;
+ }
+ ClassSymbol container = info.annotationMetadata().repeatable();
if (container == null) {
+ if (isKotlinRepeatable(info)) {
+ continue;
+ }
AnnoInfo anno = infos.iterator().next();
throw TurbineError.format(
anno.source(), anno.position(), ErrorKind.NONREPEATABLE_ANNOTATION, symbol);
@@ -273,4 +303,18 @@ public class DisambiguateTypeAnnotations {
}
return result.build();
}
+
+ // Work-around for https://youtrack.jetbrains.net/issue/KT-34189.
+ // Kotlin stubs include repeated annotations that are valid in Kotlin (i.e. meta-annotated with
+ // @kotlin.annotation.Repeatable), even though they are invalid Java.
+ // TODO(b/142002426): kill this with fire
+ static boolean isKotlinRepeatable(TypeBoundClass info) {
+ for (AnnoInfo metaAnno : info.annotations()) {
+ if (metaAnno.sym() != null
+ && metaAnno.sym().binaryName().equals("kotlin/annotation/Repeatable")) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/java/com/google/turbine/binder/HierarchyBinder.java b/java/com/google/turbine/binder/HierarchyBinder.java
index 8549d3a..07d255c 100644
--- a/java/com/google/turbine/binder/HierarchyBinder.java
+++ b/java/com/google/turbine/binder/HierarchyBinder.java
@@ -16,6 +16,7 @@
package com.google.turbine.binder;
+import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.bound.HeaderBoundClass;
@@ -68,6 +69,9 @@ public class HierarchyBinder {
ClassSymbol superclass;
if (decl.xtnds().isPresent()) {
superclass = resolveClass(decl.xtnds().get());
+ if (origin.equals(superclass)) {
+ log.error(decl.xtnds().get().position(), ErrorKind.CYCLIC_HIERARCHY, origin);
+ }
} else {
switch (decl.tykind()) {
case ENUM:
@@ -90,6 +94,9 @@ public class HierarchyBinder {
if (result == null) {
continue;
}
+ if (origin.equals(result)) {
+ log.error(i.position(), ErrorKind.CYCLIC_HIERARCHY, origin);
+ }
interfaces.add(result);
}
} else {
@@ -120,7 +127,7 @@ public class HierarchyBinder {
// Resolve the base symbol in the qualified name.
LookupResult result = lookup(ty, new LookupKey(ImmutableList.copyOf(flat)));
if (result == null) {
- log.error(ty.position(), ErrorKind.CANNOT_RESOLVE, ty);
+ log.error(ty.position(), ErrorKind.CANNOT_RESOLVE, Joiner.on('.').join(flat));
return null;
}
// Resolve pieces in the qualified name referring to member types.
diff --git a/java/com/google/turbine/binder/JimageClassBinder.java b/java/com/google/turbine/binder/JimageClassBinder.java
index e3d0865..d11dda1 100644
--- a/java/com/google/turbine/binder/JimageClassBinder.java
+++ b/java/com/google/turbine/binder/JimageClassBinder.java
@@ -33,6 +33,7 @@ import com.google.turbine.binder.bytecode.BytecodeBoundClass;
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.lookup.TopLevelIndex;
import com.google.turbine.binder.sym.ClassSymbol;
@@ -150,7 +151,7 @@ public class JimageClassBinder {
String binaryName = modulePath.relativize(path).toString();
binaryName = binaryName.substring(0, binaryName.length() - ".class".length());
ClassSymbol sym = new ClassSymbol(binaryName);
- packageClassesBySimpleName.put(packageName, simpleName(sym), sym);
+ packageClassesBySimpleName.put(packageName, sym.simpleName(), sym);
JimageClassBinder.this.env.put(
sym, new BytecodeBoundClass(sym, toByteArrayOrDie(path), env, path.toString()));
}
@@ -176,16 +177,6 @@ public class JimageClassBinder {
});
}
- private static String simpleName(ClassSymbol sym) {
- int idx = sym.binaryName().lastIndexOf('/');
- return idx != -1 ? sym.binaryName().substring(idx + 1) : sym.binaryName();
- }
-
- private static String packageName(ClassSymbol sym) {
- int idx = sym.binaryName().lastIndexOf('/');
- return idx != -1 ? sym.binaryName().substring(0, idx) : "";
- }
-
private class JimageTopLevelIndex implements TopLevelIndex {
final Scope topLevelScope =
@@ -222,18 +213,23 @@ public class JimageClassBinder {
}
@Override
- public Scope lookupPackage(ImmutableList<String> name) {
+ public PackageScope lookupPackage(Iterable<String> name) {
String packageName = Joiner.on('/').join(name);
if (!initPackage(packageName)) {
return null;
}
- return new Scope() {
+ return new PackageScope() {
@Nullable
@Override
public LookupResult lookup(LookupKey lookupKey) {
ClassSymbol sym = packageClassesBySimpleName.get(packageName, lookupKey.first().value());
return sym != null ? new LookupResult(sym, lookupKey) : null;
}
+
+ @Override
+ public Iterable<ClassSymbol> classes() {
+ return packageClassesBySimpleName.row(packageName).values();
+ }
};
}
}
@@ -247,7 +243,7 @@ public class JimageClassBinder {
return new Env<ClassSymbol, BytecodeBoundClass>() {
@Override
public BytecodeBoundClass get(ClassSymbol sym) {
- return initPackage(packageName(sym)) ? env.get(sym) : null;
+ return initPackage(sym.packageName()) ? env.get(sym) : null;
}
};
}
@@ -266,5 +262,10 @@ public class JimageClassBinder {
public TopLevelIndex index() {
return index;
}
+
+ @Override
+ public Supplier<byte[]> resource(String input) {
+ return null;
+ }
}
}
diff --git a/java/com/google/turbine/binder/ModuleBinder.java b/java/com/google/turbine/binder/ModuleBinder.java
index afd8770..748ff39 100644
--- a/java/com/google/turbine/binder/ModuleBinder.java
+++ b/java/com/google/turbine/binder/ModuleBinder.java
@@ -40,6 +40,7 @@ import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.ModuleSymbol;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.diag.TurbineError.ErrorKind;
+import com.google.turbine.diag.TurbineLog.TurbineLogWithSource;
import com.google.turbine.model.TurbineFlag;
import com.google.turbine.tree.Tree;
import com.google.turbine.tree.Tree.Ident;
@@ -60,8 +61,9 @@ public class ModuleBinder {
PackageSourceBoundModule module,
CompoundEnv<ClassSymbol, TypeBoundClass> env,
Env<ModuleSymbol, ModuleInfo> moduleEnv,
- Optional<String> moduleVersion) {
- return new ModuleBinder(module, env, moduleEnv, moduleVersion).bind();
+ Optional<String> moduleVersion,
+ TurbineLogWithSource log) {
+ return new ModuleBinder(module, env, moduleEnv, moduleVersion, log).bind();
}
private final PackageSourceBoundModule module;
@@ -69,16 +71,19 @@ public class ModuleBinder {
private final Env<ModuleSymbol, ModuleInfo> moduleEnv;
private final Optional<String> moduleVersion;
private final CompoundScope scope;
+ private final TurbineLogWithSource log;
public ModuleBinder(
PackageSourceBoundModule module,
CompoundEnv<ClassSymbol, TypeBoundClass> env,
Env<ModuleSymbol, ModuleInfo> moduleEnv,
- Optional<String> moduleVersion) {
+ Optional<String> moduleVersion,
+ TurbineLogWithSource log) {
this.module = module;
this.env = env;
this.moduleEnv = moduleEnv;
this.moduleVersion = moduleVersion;
+ this.log = log;
this.scope = module.scope().toScope(Resolve.resolveFunction(env, /* origin= */ null));
}
@@ -92,11 +97,12 @@ public class ModuleBinder {
module.source(),
scope,
/* values= */ new SimpleEnv<>(ImmutableMap.of()),
- env);
+ env,
+ log);
ImmutableList.Builder<AnnoInfo> annoInfos = ImmutableList.builder();
for (Tree.Anno annoTree : module.module().annos()) {
ClassSymbol sym = resolve(annoTree.position(), annoTree.name());
- annoInfos.add(new AnnoInfo(module.source(), sym, annoTree, null));
+ annoInfos.add(new AnnoInfo(module.source(), sym, annoTree, ImmutableMap.of()));
}
ImmutableList<AnnoInfo> annos = constEvaluator.evaluateAnnotations(annoInfos.build());
@@ -130,8 +136,6 @@ public class ModuleBinder {
case PROVIDES:
provides.add(bindProvides((ModProvides) directive));
break;
- default:
- throw new AssertionError(directive.kind());
}
}
if (!requiresJavaBase) {
@@ -181,11 +185,11 @@ public class ModuleBinder {
return new RequireInfo(moduleName, flags, requires != null ? requires.version() : null);
}
- private ExportInfo bindExports(ModExports directive) {
+ private static ExportInfo bindExports(ModExports directive) {
return new ExportInfo(directive.packageName(), directive.moduleNames());
}
- private OpenInfo bindOpens(ModOpens directive) {
+ private static OpenInfo bindOpens(ModOpens directive) {
return new OpenInfo(directive.packageName(), directive.moduleNames());
}
diff --git a/java/com/google/turbine/binder/Processing.java b/java/com/google/turbine/binder/Processing.java
new file mode 100644
index 0000000..ecdf195
--- /dev/null
+++ b/java/com/google/turbine/binder/Processing.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright 2019 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.auto.value.AutoValue;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Supplier;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Sets;
+import com.google.turbine.binder.Binder.BindingResult;
+import com.google.turbine.binder.Binder.Statistics;
+import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.diag.SourceFile;
+import com.google.turbine.diag.TurbineLog;
+import com.google.turbine.parse.Parser;
+import com.google.turbine.processing.ModelFactory;
+import com.google.turbine.processing.TurbineElements;
+import com.google.turbine.processing.TurbineFiler;
+import com.google.turbine.processing.TurbineMessager;
+import com.google.turbine.processing.TurbineProcessingEnvironment;
+import com.google.turbine.processing.TurbineRoundEnvironment;
+import com.google.turbine.processing.TurbineTypes;
+import com.google.turbine.tree.Tree.CompUnit;
+import com.google.turbine.type.AnnoInfo;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.regex.Pattern;
+import javax.annotation.processing.Processor;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/** Top level annotation processing logic, see also {@link Binder}. */
+public class Processing {
+
+ static BindingResult process(
+ TurbineLog log,
+ final ImmutableList<CompUnit> initialSources,
+ final ClassPath classpath,
+ ProcessorInfo processorInfo,
+ ClassPath bootclasspath,
+ BindingResult result,
+ Optional<String> moduleVersion) {
+
+ Set<String> seen = new HashSet<>();
+ for (CompUnit u : initialSources) {
+ if (u.source() != null) {
+ seen.add(u.source().path());
+ }
+ }
+
+ TurbineFiler filer =
+ new TurbineFiler(
+ seen,
+ new Function<String, Supplier<byte[]>>() {
+ @Nullable
+ @Override
+ public Supplier<byte[]> apply(@Nullable String input) {
+ // TODO(cushon): should annotation processors be allowed to generate code with
+ // dependencies between source and bytecode, or vice versa?
+ // Currently generated classes are not available on the classpath when compiling
+ // the compilation sources (including generated sources).
+ return classpath.resource(input);
+ }
+ },
+ processorInfo.loader());
+
+ Env<ClassSymbol, SourceTypeBoundClass> tenv = new SimpleEnv<>(result.units());
+ CompoundEnv<ClassSymbol, TypeBoundClass> env =
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(result.classPathEnv()).append(tenv);
+ ModelFactory factory = new ModelFactory(env, processorInfo.loader(), result.tli());
+
+ Map<String, byte[]> statistics = new LinkedHashMap<>();
+
+ TurbineTypes turbineTypes = new TurbineTypes(factory);
+ TurbineProcessingEnvironment processingEnv =
+ new TurbineProcessingEnvironment(
+ filer,
+ turbineTypes,
+ new TurbineElements(factory, turbineTypes),
+ new TurbineMessager(factory, log),
+ processorInfo.options(),
+ processorInfo.sourceVersion(),
+ processorInfo.loader(),
+ statistics);
+ Timers timers = new Timers();
+ for (Processor processor : processorInfo.processors()) {
+ try (Timers.Timer unused = timers.start(processor)) {
+ processor.init(processingEnv);
+ } catch (Throwable t) {
+ reportProcessorCrash(log, processor, t);
+ }
+ }
+
+ 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)));
+ }
+
+ Set<ClassSymbol> allSymbols = new HashSet<>();
+
+ ImmutableList.Builder<CompUnit> units =
+ ImmutableList.<CompUnit>builder().addAll(initialSources);
+
+ Set<Processor> toRun = new LinkedHashSet<>();
+
+ boolean errorRaised = false;
+
+ while (true) {
+ ImmutableSet<ClassSymbol> syms =
+ Sets.difference(result.units().keySet(), allSymbols).immutableCopy();
+ allSymbols.addAll(syms);
+ if (syms.isEmpty()) {
+ break;
+ }
+ ImmutableSetMultimap<ClassSymbol, Symbol> allAnnotations = getAllAnnotations(env, syms);
+ TurbineRoundEnvironment roundEnv = null;
+ for (Processor processor : processorInfo.processors()) {
+ Set<TypeElement> annotations = new HashSet<>();
+ Pattern pattern = wanted.get(processor);
+ boolean run = toRun.contains(processor);
+ for (ClassSymbol a : allAnnotations.keys()) {
+ if (pattern.matcher(a.toString()).matches()) {
+ annotations.add(factory.typeElement(a));
+ run = true;
+ }
+ }
+ if (run) {
+ toRun.add(processor);
+ if (roundEnv == null) {
+ roundEnv =
+ new TurbineRoundEnvironment(factory, syms, false, errorRaised, allAnnotations);
+ }
+ try (Timers.Timer unused = timers.start(processor)) {
+ // discard the result of Processor#process because 'claiming' annotations is a bad idea
+ // TODO(cushon): consider disallowing this, or reporting a diagnostic
+ processor.process(annotations, roundEnv);
+ } catch (Throwable t) {
+ reportProcessorCrash(log, processor, t);
+ }
+ }
+ }
+ Collection<SourceFile> files = filer.finishRound();
+ if (files.isEmpty()) {
+ break;
+ }
+ for (SourceFile file : files) {
+ units.add(Parser.parse(file));
+ }
+ errorRaised = log.errorRaised();
+ if (errorRaised) {
+ log.maybeThrow();
+ }
+ log.clear();
+ result =
+ Binder.bind(
+ log,
+ units.build(),
+ filer.generatedSources(),
+ filer.generatedClasses(),
+ classpath,
+ bootclasspath,
+ moduleVersion);
+ tenv = new SimpleEnv<>(result.units());
+ env = CompoundEnv.<ClassSymbol, TypeBoundClass>of(result.classPathEnv()).append(tenv);
+ factory.round(env, result.tli());
+ }
+
+ TurbineRoundEnvironment roundEnv = null;
+ for (Processor processor : toRun) {
+ if (roundEnv == null) {
+ roundEnv =
+ new TurbineRoundEnvironment(
+ factory,
+ ImmutableSet.of(),
+ /* processingOver= */ true,
+ errorRaised,
+ ImmutableSetMultimap.of());
+ }
+ try (Timers.Timer unused = timers.start(processor)) {
+ processor.process(ImmutableSet.of(), roundEnv);
+ } catch (Throwable t) {
+ reportProcessorCrash(log, processor, t);
+ }
+ }
+
+ Collection<SourceFile> files = filer.finishRound();
+ if (!files.isEmpty()) {
+ // processors aren't supposed to generate sources on the final processing round, but javac
+ // tolerates it anyway
+ // TODO(cushon): consider disallowing this, or reporting a diagnostic
+ for (SourceFile file : files) {
+ units.add(Parser.parse(file));
+ }
+ result =
+ Binder.bind(
+ log,
+ units.build(),
+ filer.generatedSources(),
+ filer.generatedClasses(),
+ classpath,
+ bootclasspath,
+ moduleVersion);
+ log.maybeThrow();
+ }
+
+ if (!filer.generatedClasses().isEmpty()) {
+ // add any generated class files to the output
+ // TODO(cushon): consider handling generated classes after each round
+ result = result.withGeneratedClasses(filer.generatedClasses());
+ }
+ if (!filer.generatedSources().isEmpty()) {
+ result = result.withGeneratedSources(filer.generatedSources());
+ }
+
+ result =
+ result.withStatistics(Statistics.create(timers.build(), ImmutableMap.copyOf(statistics)));
+
+ return result;
+ }
+
+ private static void reportProcessorCrash(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. */
+ private static ImmutableSetMultimap<ClassSymbol, Symbol> getAllAnnotations(
+ Env<ClassSymbol, TypeBoundClass> env, Iterable<ClassSymbol> syms) {
+ ImmutableSetMultimap.Builder<ClassSymbol, Symbol> result = ImmutableSetMultimap.builder();
+ for (ClassSymbol sym : syms) {
+ TypeBoundClass info = env.get(sym);
+ for (AnnoInfo annoInfo : info.annotations()) {
+ if (sym.simpleName().equals("package-info")) {
+ addAnno(result, annoInfo, sym.owner());
+ } else {
+ addAnno(result, annoInfo, sym);
+ }
+ }
+ for (ClassSymbol inheritedAnno :
+ inheritedAnnotations(new HashSet<>(), info.superclass(), env)) {
+ result.put(inheritedAnno, sym);
+ }
+ for (TypeBoundClass.MethodInfo method : info.methods()) {
+ for (AnnoInfo annoInfo : method.annotations()) {
+ addAnno(result, annoInfo, method.sym());
+ }
+ for (TypeBoundClass.ParamInfo param : method.parameters()) {
+ for (AnnoInfo annoInfo : param.annotations()) {
+ addAnno(result, annoInfo, param.sym());
+ }
+ }
+ }
+ for (FieldInfo field : info.fields()) {
+ for (AnnoInfo annoInfo : field.annotations()) {
+ addAnno(result, annoInfo, field.sym());
+ }
+ }
+ }
+ return result.build();
+ }
+
+ // TODO(cushon): consider memoizing this (or isAnnotationInherited) if they show up in profiles
+ private static Set<ClassSymbol> inheritedAnnotations(
+ Set<ClassSymbol> seen, ClassSymbol sym, Env<ClassSymbol, TypeBoundClass> env) {
+ ImmutableSet.Builder<ClassSymbol> result = ImmutableSet.builder();
+ ClassSymbol curr = sym;
+ while (curr != null && seen.add(curr)) {
+ TypeBoundClass info = env.get(curr);
+ if (info == null) {
+ break;
+ }
+ for (AnnoInfo anno : info.annotations()) {
+ ClassSymbol annoSym = anno.sym();
+ if (annoSym == null) {
+ continue;
+ }
+ if (isAnnotationInherited(env, annoSym)) {
+ result.add(annoSym);
+ }
+ }
+ curr = info.superclass();
+ }
+ return result.build();
+ }
+
+ private static boolean isAnnotationInherited(
+ Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym) {
+ TypeBoundClass annoInfo = env.get(sym);
+ if (annoInfo == null) {
+ return false;
+ }
+ for (AnnoInfo anno : annoInfo.annotations()) {
+ if (Objects.equals(anno.sym(), ClassSymbol.INHERITED)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void addAnno(
+ ImmutableSetMultimap.Builder<ClassSymbol, Symbol> result, AnnoInfo annoInfo, Symbol owner) {
+ ClassSymbol sym = annoInfo.sym();
+ if (sym != null) {
+ result.put(sym, owner);
+ }
+ }
+
+ 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();
+ }
+ 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;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return ProcessorInfo.create(
+ processors.build(), processorLoader, processorOptions, sourceVersion);
+ }
+
+ private static URL[] toUrls(ImmutableList<String> processorPath) throws MalformedURLException {
+ URL[] urls = new URL[processorPath.size()];
+ int i = 0;
+ for (String path : processorPath) {
+ urls[i++] = Paths.get(path).toUri().toURL();
+ }
+ return urls;
+ }
+
+ public static ClassLoader getPlatformClassLoader() {
+ try {
+ return (ClassLoader) ClassLoader.class.getMethod("getPlatformClassLoader").invoke(null);
+ } catch (ReflectiveOperationException e) {
+ // In earlier releases, set 'null' as the parent to delegate to the boot class loader.
+ return null;
+ }
+ }
+
+ private static ImmutableMap<String, String> processorOptions(ImmutableList<String> javacopts) {
+ Map<String, String> result = new LinkedHashMap<>(); // ImmutableMap.Builder rejects duplicates
+ for (String javacopt : javacopts) {
+ if (javacopt.startsWith("-A")) {
+ javacopt = javacopt.substring("-A".length());
+ int idx = javacopt.indexOf('=');
+ String key;
+ String value;
+ if (idx != -1) {
+ key = javacopt.substring(0, idx);
+ value = javacopt.substring(idx + 1);
+ } else {
+ key = javacopt;
+ value = javacopt;
+ }
+ result.put(key, value);
+ }
+ }
+ return ImmutableMap.copyOf(result);
+ }
+
+ /** Information about any annotation processing performed by this compilation. */
+ @AutoValue
+ public abstract static class ProcessorInfo {
+
+ abstract ImmutableList<Processor> processors();
+
+ /**
+ * The classloader to use for annotation processor implementations, and any annotations they
+ * access reflectively.
+ */
+ @Nullable
+ abstract ClassLoader loader();
+
+ /** Command line annotation processing options, passed to javac with {@code -Akey=value}. */
+ abstract ImmutableMap<String, String> options();
+
+ public abstract SourceVersion sourceVersion();
+
+ public static ProcessorInfo create(
+ ImmutableList<Processor> processors,
+ @Nullable ClassLoader loader,
+ ImmutableMap<String, String> options,
+ SourceVersion sourceVersion) {
+ return new AutoValue_Processing_ProcessorInfo(processors, loader, options, sourceVersion);
+ }
+
+ static ProcessorInfo empty() {
+ return create(
+ /* processors= */ ImmutableList.of(),
+ /* loader= */ null,
+ /* options= */ ImmutableMap.of(),
+ /* sourceVersion= */ SourceVersion.latest());
+ }
+ }
+
+ private static class Timers {
+ private final Map<Class<?>, Stopwatch> processorTimers = new LinkedHashMap<>();
+
+ Timer start(Processor processor) {
+ Class<? extends Processor> clazz = processor.getClass();
+ Stopwatch sw = processorTimers.get(clazz);
+ if (sw == null) {
+ sw = Stopwatch.createUnstarted();
+ processorTimers.put(clazz, sw);
+ }
+ sw.start();
+ return new Timer(sw);
+ }
+
+ private static class Timer implements AutoCloseable {
+
+ private final Stopwatch sw;
+
+ public Timer(Stopwatch sw) {
+ this.sw = sw;
+ }
+
+ @Override
+ public void close() {
+ sw.stop();
+ }
+ }
+
+ 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());
+ }
+ return result.build();
+ }
+ }
+}
diff --git a/java/com/google/turbine/binder/Resolve.java b/java/com/google/turbine/binder/Resolve.java
index 0a61844..28a8be3 100644
--- a/java/com/google/turbine/binder/Resolve.java
+++ b/java/com/google/turbine/binder/Resolve.java
@@ -136,12 +136,11 @@ public class Resolve {
return true;
case PROTECTED:
case PACKAGE:
- return Objects.equals(packageName(sym), packagename);
+ return Objects.equals(sym.packageName(), packagename);
case PRIVATE:
return false;
- default:
- throw new AssertionError(visibility);
}
+ throw new AssertionError(visibility);
}
}
@@ -206,24 +205,12 @@ public class Resolve {
case PROTECTED:
return true;
case PACKAGE:
- return Objects.equals(packageName(owner), packageName(origin));
+ return Objects.equals(owner.packageName(), origin.packageName());
case PRIVATE:
// Private members of lexically enclosing declarations are not handled,
// since this visibility check is only used for inherited members.
return owner.equals(origin);
- default:
- throw new AssertionError(visibility);
}
- }
-
- private static String packageName(ClassSymbol sym) {
- if (sym == null) {
- return null;
- }
- int idx = sym.binaryName().lastIndexOf('/');
- if (idx == -1) {
- return null;
- }
- return sym.binaryName().substring(0, idx);
+ throw new AssertionError(visibility);
}
}
diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java
index 8cf71e1..7b01856 100644
--- a/java/com/google/turbine/binder/TypeBinder.java
+++ b/java/com/google/turbine/binder/TypeBinder.java
@@ -34,6 +34,7 @@ import com.google.turbine.binder.lookup.Scope;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
import com.google.turbine.binder.sym.Symbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.diag.TurbineError.ErrorKind;
@@ -266,22 +267,23 @@ public class TypeBinder {
if (hasConstructor()) {
return ImmutableList.of();
}
+ MethodSymbol symbol = new MethodSymbol(-1, owner, "<init>");
ImmutableList<ParamInfo> formals;
if (hasEnclosingInstance(base)) {
- formals = ImmutableList.of(enclosingInstanceParameter());
+ formals = ImmutableList.of(enclosingInstanceParameter(symbol));
} else {
formals = ImmutableList.of();
}
return ImmutableList.of(
- syntheticConstructor(formals, TurbineVisibility.fromAccess(base.access())));
+ syntheticConstructor(symbol, formals, TurbineVisibility.fromAccess(base.access())));
}
private MethodInfo syntheticConstructor(
- ImmutableList<ParamInfo> formals, TurbineVisibility visibility) {
+ MethodSymbol symbol, ImmutableList<ParamInfo> formals, TurbineVisibility visibility) {
int access = visibility.flag();
access |= (base.access() & TurbineFlag.ACC_STRICT);
return new MethodInfo(
- new MethodSymbol(owner, "<init>"),
+ symbol,
ImmutableMap.of(),
Type.VOID,
formals,
@@ -293,7 +295,7 @@ public class TypeBinder {
null);
}
- private ParamInfo enclosingInstanceParameter() {
+ private ParamInfo enclosingInstanceParameter(MethodSymbol owner) {
int access = TurbineFlag.ACC_FINAL;
if ((base.access() & TurbineFlag.ACC_PRIVATE) == TurbineFlag.ACC_PRIVATE) {
access |= TurbineFlag.ACC_SYNTHETIC;
@@ -311,37 +313,40 @@ public class TypeBinder {
sym = info.owner();
}
return new ParamInfo(
+ new ParamSymbol(owner, "this$" + enclosingInstances),
Type.ClassTy.asNonParametricClassTy(base.owner()),
- "this$" + enclosingInstances,
ImmutableList.of(),
access);
}
- private static final ImmutableList<ParamInfo> ENUM_CTOR_PARAMS =
- ImmutableList.of(
- new ParamInfo(
- Type.ClassTy.STRING,
- "$enum$name",
- ImmutableList.of(),
- /*synthetic*/
- TurbineFlag.ACC_SYNTHETIC),
- new ParamInfo(
- Type.PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()),
- "$enum$ordinal",
- ImmutableList.of(),
- /*synthetic*/
- TurbineFlag.ACC_SYNTHETIC));
+ private static ImmutableList<ParamInfo> enumCtorParams(MethodSymbol owner) {
+ return ImmutableList.of(
+ new ParamInfo(
+ new ParamSymbol(owner, "$enum$name"),
+ Type.ClassTy.STRING,
+ ImmutableList.of(),
+ /*synthetic*/
+ TurbineFlag.ACC_SYNTHETIC),
+ new ParamInfo(
+ new ParamSymbol(owner, "$enum$ordinal"),
+ Type.PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()),
+ ImmutableList.of(),
+ /*synthetic*/
+ TurbineFlag.ACC_SYNTHETIC));
+ }
private ImmutableList<MethodInfo> syntheticEnumMethods() {
ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder();
int access = 0;
access |= (base.access() & TurbineFlag.ACC_STRICT);
if (!hasConstructor()) {
- methods.add(syntheticConstructor(ENUM_CTOR_PARAMS, TurbineVisibility.PRIVATE));
+ MethodSymbol symbol = new MethodSymbol(-1, owner, "<init>");
+ methods.add(syntheticConstructor(symbol, enumCtorParams(symbol), TurbineVisibility.PRIVATE));
}
+ MethodSymbol valuesMethod = new MethodSymbol(-2, owner, "values");
methods.add(
new MethodInfo(
- new MethodSymbol(owner, "values"),
+ valuesMethod,
ImmutableMap.of(),
Type.ArrayTy.create(Type.ClassTy.asNonParametricClassTy(owner), ImmutableList.of()),
ImmutableList.of(),
@@ -351,14 +356,18 @@ public class TypeBinder {
null,
ImmutableList.of(),
null));
+ MethodSymbol valueOfMethod = new MethodSymbol(-3, owner, "valueOf");
methods.add(
new MethodInfo(
- new MethodSymbol(owner, "valueOf"),
+ valueOfMethod,
ImmutableMap.of(),
Type.ClassTy.asNonParametricClassTy(owner),
ImmutableList.of(
new ParamInfo(
- Type.ClassTy.STRING, "name", ImmutableList.of(), TurbineFlag.ACC_MANDATED)),
+ new ParamSymbol(valueOfMethod, "name"),
+ Type.ClassTy.STRING,
+ ImmutableList.of(),
+ TurbineFlag.ACC_MANDATED)),
ImmutableList.of(),
access | TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_STATIC,
null,
@@ -387,38 +396,38 @@ public class TypeBinder {
for (Tree.TyParam tree : trees) {
TyVarSymbol sym = symbols.get(tree.name().value());
ImmutableList.Builder<Type> bounds = ImmutableList.builder();
- if (tree.bounds().isEmpty()) {
- bounds.add(Type.ClassTy.OBJECT);
- } else {
- for (Tree bound : tree.bounds()) {
- bounds.add(bindTy(scope, bound));
- }
+ for (Tree bound : tree.bounds()) {
+ bounds.add(bindTy(scope, bound));
}
ImmutableList<AnnoInfo> annotations = bindAnnotations(scope, tree.annos());
- result.put(sym, new TyVarInfo(IntersectionTy.create(bounds.build()), annotations));
+ result.put(
+ sym,
+ new TyVarInfo(
+ IntersectionTy.create(bounds.build()), /* lowerBound= */ null, annotations));
}
return result.build();
}
private List<MethodInfo> bindMethods(CompoundScope scope, ImmutableList<Tree> members) {
List<MethodInfo> methods = new ArrayList<>();
+ int idx = 0;
for (Tree member : members) {
if (member.kind() == Tree.Kind.METH_DECL) {
- methods.add(bindMethod(scope, (Tree.MethDecl) member));
+ methods.add(bindMethod(idx++, scope, (Tree.MethDecl) member));
}
}
return methods;
}
- private MethodInfo bindMethod(CompoundScope scope, Tree.MethDecl t) {
+ private MethodInfo bindMethod(int idx, CompoundScope scope, Tree.MethDecl t) {
- MethodSymbol sym = new MethodSymbol(owner, t.name().value());
+ MethodSymbol sym = new MethodSymbol(idx, owner, t.name().value());
ImmutableMap<String, TyVarSymbol> typeParameters;
{
ImmutableMap.Builder<String, TyVarSymbol> builder = ImmutableMap.builder();
for (Tree.TyParam pt : t.typarams()) {
- builder.put(pt.name().value(), new TyVarSymbol(owner, pt.name().value()));
+ builder.put(pt.name().value(), new TyVarSymbol(sym, pt.name().value()));
}
typeParameters = builder.build();
}
@@ -439,9 +448,9 @@ public class TypeBinder {
String name = t.name().value();
if (name.equals("<init>")) {
if (hasEnclosingInstance(base)) {
- parameters.add(enclosingInstanceParameter());
+ parameters.add(enclosingInstanceParameter(sym));
} else if (base.kind() == TurbineTyKind.ENUM && name.equals("<init>")) {
- parameters.addAll(ENUM_CTOR_PARAMS);
+ parameters.addAll(enumCtorParams(sym));
}
}
ParamInfo receiver = null;
@@ -452,8 +461,8 @@ public class TypeBinder {
}
ParamInfo param =
new ParamInfo(
+ new ParamSymbol(sym, p.name().value()),
bindTy(scope, p.ty()),
- p.name().value(),
bindAnnotations(scope, p.annos()), /*synthetic*/
access);
if (p.name().value().equals("this")) {
@@ -561,7 +570,7 @@ public class TypeBinder {
ImmutableList<Ident> name = tree.name();
LookupResult lookupResult = scope.lookup(new LookupKey(name));
ClassSymbol sym = resolveAnnoSymbol(tree, name, lookupResult);
- result.add(new AnnoInfo(base.source(), sym, tree, null));
+ result.add(new AnnoInfo(base.source(), sym, tree, ImmutableMap.of()));
}
return result.build();
}
@@ -648,7 +657,7 @@ public class TypeBinder {
LookupResult result = scope.lookup(new LookupKey(names));
if (result == null || result.sym() == null) {
log.error(names.get(0).position(), ErrorKind.CANNOT_RESOLVE, Joiner.on('.').join(names));
- return Type.ErrorTy.create();
+ return Type.ErrorTy.create(names);
}
Symbol sym = result.sym();
int annoIdx = flat.size() - result.remaining().size() - 1;
@@ -660,7 +669,7 @@ public class TypeBinder {
case TY_PARAM:
if (!result.remaining().isEmpty()) {
log.error(t.position(), ErrorKind.TYPE_PARAMETER_QUALIFIER);
- return Type.ErrorTy.create();
+ return Type.ErrorTy.create(names);
}
return Type.TyVar.create((TyVarSymbol) sym, annos);
default:
@@ -684,7 +693,7 @@ public class TypeBinder {
Tree.ClassTy curr = flat.get(idx);
sym = resolveNext(sym, curr.name());
if (sym == null) {
- return Type.ErrorTy.create();
+ return Type.ErrorTy.create(bits);
}
annotations = bindAnnotations(scope, curr.annos());
diff --git a/java/com/google/turbine/binder/bound/AnnotationMetadata.java b/java/com/google/turbine/binder/bound/AnnotationMetadata.java
index 9c81a5f..31860b6 100644
--- a/java/com/google/turbine/binder/bound/AnnotationMetadata.java
+++ b/java/com/google/turbine/binder/bound/AnnotationMetadata.java
@@ -30,7 +30,7 @@ import java.util.EnumSet;
*/
public class AnnotationMetadata {
- private static final ImmutableSet<TurbineElementType> DEFAULT_TARGETS = getDefaultElements();
+ public static final ImmutableSet<TurbineElementType> DEFAULT_TARGETS = getDefaultElements();
private static ImmutableSet<TurbineElementType> getDefaultElements() {
EnumSet<TurbineElementType> values = EnumSet.allOf(TurbineElementType.class);
diff --git a/java/com/google/turbine/binder/bound/EnumConstantValue.java b/java/com/google/turbine/binder/bound/EnumConstantValue.java
index 4083ae3..e99c6ed 100644
--- a/java/com/google/turbine/binder/bound/EnumConstantValue.java
+++ b/java/com/google/turbine/binder/bound/EnumConstantValue.java
@@ -46,4 +46,9 @@ public class EnumConstantValue extends Const {
public boolean equals(Object obj) {
return obj instanceof EnumConstantValue && sym().equals(((EnumConstantValue) obj).sym());
}
+
+ @Override
+ public String toString() {
+ return sym.toString();
+ }
}
diff --git a/java/com/google/turbine/binder/bound/AnnotationValue.java b/java/com/google/turbine/binder/bound/TurbineAnnotationValue.java
index fd4ffab..808d603 100644
--- a/java/com/google/turbine/binder/bound/AnnotationValue.java
+++ b/java/com/google/turbine/binder/bound/TurbineAnnotationValue.java
@@ -19,22 +19,20 @@ package com.google.turbine.binder.bound;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.model.Const;
-import java.util.Objects;
+import com.google.turbine.type.AnnoInfo;
/** An annotation literal constant. */
-public class AnnotationValue extends Const {
+public class TurbineAnnotationValue extends Const {
- private final ClassSymbol sym;
- private final ImmutableMap<String, Const> values;
+ private final AnnoInfo info;
- public AnnotationValue(ClassSymbol sym, ImmutableMap<String, Const> values) {
- this.sym = sym;
- this.values = values;
+ public TurbineAnnotationValue(AnnoInfo info) {
+ this.info = info;
}
@Override
public String toString() {
- return String.format("@%s", sym);
+ return info.toString();
}
@Override
@@ -44,25 +42,26 @@ public class AnnotationValue extends Const {
/** The annotation declaration. */
public ClassSymbol sym() {
- return sym;
+ return info.sym();
}
/** The annotation literal's element-value pairs. */
public ImmutableMap<String, Const> values() {
- return values;
+ return info.values();
}
@Override
public int hashCode() {
- return Objects.hash(sym, values);
+ return info.hashCode();
}
@Override
public boolean equals(Object obj) {
- if (!(obj instanceof AnnotationValue)) {
- return false;
- }
- AnnotationValue that = (AnnotationValue) obj;
- return sym().equals(that.sym()) && values().equals(that.values());
+ return obj instanceof TurbineAnnotationValue
+ && info().equals(((TurbineAnnotationValue) obj).info());
+ }
+
+ public AnnoInfo info() {
+ return info;
}
}
diff --git a/java/com/google/turbine/binder/bound/TypeBoundClass.java b/java/com/google/turbine/binder/bound/TypeBoundClass.java
index 350d311..e8933ac 100644
--- a/java/com/google/turbine/binder/bound/TypeBoundClass.java
+++ b/java/com/google/turbine/binder/bound/TypeBoundClass.java
@@ -16,10 +16,12 @@
package com.google.turbine.binder.bound;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.model.Const;
import com.google.turbine.model.TurbineFlag;
@@ -28,6 +30,8 @@ import com.google.turbine.tree.Tree.MethDecl;
import com.google.turbine.type.AnnoInfo;
import com.google.turbine.type.Type;
import com.google.turbine.type.Type.IntersectionTy;
+import com.google.turbine.type.Type.MethodTy;
+import org.checkerframework.checker.nullness.qual.Nullable;
/** A bound node that augments {@link HeaderBoundClass} with type information. */
public interface TypeBoundClass extends HeaderBoundClass {
@@ -57,17 +61,29 @@ public interface TypeBoundClass extends HeaderBoundClass {
/** A type parameter declaration. */
class TyVarInfo {
- private final IntersectionTy bound;
+ private final IntersectionTy upperBound;
+ @Nullable private final Type lowerBound;
private final ImmutableList<AnnoInfo> annotations;
- public TyVarInfo(IntersectionTy bound, ImmutableList<AnnoInfo> annotations) {
- this.bound = bound;
+ public TyVarInfo(
+ IntersectionTy upperBound, @Nullable Type lowerBound, ImmutableList<AnnoInfo> annotations) {
+ this.upperBound = upperBound;
+ if (lowerBound != null) {
+ throw new IllegalArgumentException("TODO(cushon): support lower bounds");
+ }
+ this.lowerBound = lowerBound;
this.annotations = annotations;
}
- /** The bound. */
- public IntersectionTy bound() {
- return bound;
+ /** The upper bound. */
+ public IntersectionTy upperBound() {
+ return upperBound;
+ }
+
+ /** The lower bound. */
+ @Nullable
+ public Type lowerBound() {
+ return lowerBound;
}
/** Type parameter declaration annotations. */
@@ -148,7 +164,7 @@ public interface TypeBoundClass extends HeaderBoundClass {
private final Const defaultValue;
private final MethDecl decl;
private final ImmutableList<AnnoInfo> annotations;
- private final ParamInfo receiver;
+ private final @Nullable ParamInfo receiver;
public MethodInfo(
MethodSymbol sym,
@@ -160,7 +176,7 @@ public interface TypeBoundClass extends HeaderBoundClass {
Const defaultValue,
MethDecl decl,
ImmutableList<AnnoInfo> annotations,
- ParamInfo receiver) {
+ @Nullable ParamInfo receiver) {
this.sym = sym;
this.tyParams = tyParams;
this.returnType = returnType;
@@ -223,26 +239,50 @@ public interface TypeBoundClass extends HeaderBoundClass {
return annotations;
}
- /** Receiver parameter. */
- public ParamInfo receiver() {
+ /** Receiver parameter (see JLS 8.4.1), or {@code null}. */
+ public @Nullable ParamInfo receiver() {
return receiver;
}
+
+ public MethodTy asType() {
+ return MethodTy.create(
+ tyParams.keySet(),
+ returnType,
+ receiver != null ? receiver.type() : null,
+ asTypes(parameters),
+ exceptions);
+ }
+
+ private static ImmutableList<Type> asTypes(ImmutableList<ParamInfo> parameters) {
+ ImmutableList.Builder<Type> result = ImmutableList.builder();
+ for (ParamInfo param : parameters) {
+ if (!param.synthetic()) {
+ result.add(param.type());
+ }
+ }
+ return result.build();
+ }
}
/** A formal parameter declaration. */
class ParamInfo {
+ private final ParamSymbol sym;
private final Type type;
- private final String name;
private final int access;
private final ImmutableList<AnnoInfo> annotations;
- public ParamInfo(Type type, String name, ImmutableList<AnnoInfo> annotations, int access) {
+ public ParamInfo(ParamSymbol sym, Type type, ImmutableList<AnnoInfo> annotations, int access) {
+ this.sym = sym;
this.type = type;
- this.name = name;
this.access = access;
this.annotations = annotations;
}
+ /** The parameter's symbol. */
+ public ParamSymbol sym() {
+ return sym;
+ }
+
/** The parameter type. */
public Type type() {
return type;
@@ -263,7 +303,7 @@ public interface TypeBoundClass extends HeaderBoundClass {
/** The parameter's name. */
public String name() {
- return name;
+ return sym.name();
}
/** The parameter's modifiers. */
diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
index 1d2eecb..66d4cf0 100644
--- a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
+++ b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
@@ -18,9 +18,9 @@ package com.google.turbine.binder.bytecode;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.turbine.binder.bound.AnnotationValue;
import com.google.turbine.binder.bound.EnumConstantValue;
import com.google.turbine.binder.bound.ModuleInfo;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
import com.google.turbine.binder.bound.TurbineClassValue;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
@@ -81,9 +81,8 @@ public class BytecodeBinder {
case UPPER:
return Type.WildUpperBoundedTy.create(
bindTy(((UpperBoundTySig) sig).bound(), scope), ImmutableList.of());
- default:
- throw new AssertionError(sig.boundKind());
}
+ throw new AssertionError(sig.boundKind());
}
static Type bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope) {
@@ -100,23 +99,22 @@ public class BytecodeBinder {
return wildTy((WildTySig) sig, scope);
case VOID_TY_SIG:
return Type.VOID;
- default:
- throw new AssertionError(sig.kind());
}
+ throw new AssertionError(sig.kind());
}
private static Type bindArrayTy(Sig.ArrayTySig arrayTySig, Function<String, TyVarSymbol> scope) {
return Type.ArrayTy.create(bindTy(arrayTySig.elementType(), scope), ImmutableList.of());
}
- public static Const bindValue(Type type, ElementValue value) {
+ public static Const bindValue(ElementValue value) {
switch (value.kind()) {
case ENUM:
return bindEnumValue((EnumConstValue) value);
case CONST:
- return bindConstValue(type, ((ConstValue) value).value());
+ return ((ConstValue) value).value();
case ARRAY:
- return bindArrayValue(type, (ArrayValue) value);
+ return bindArrayValue((ArrayValue) value);
case CLASS:
return new TurbineClassValue(
bindTy(
@@ -125,37 +123,45 @@ public class BytecodeBinder {
throw new IllegalStateException(x);
}));
case ANNOTATION:
- return bindAnnotationValue(type, ((ElementValue.AnnotationValue) value).annotation());
+ return bindAnnotationValue(((ElementValue.ConstTurbineAnnotationValue) value).annotation());
}
throw new AssertionError(value.kind());
}
- static AnnotationValue bindAnnotationValue(Type type, AnnotationInfo value) {
+ static TurbineAnnotationValue bindAnnotationValue(AnnotationInfo value) {
ClassSymbol sym = asClassSymbol(value.typeName());
ImmutableMap.Builder<String, Const> values = ImmutableMap.builder();
for (Map.Entry<String, ElementValue> e : value.elementValuePairs().entrySet()) {
- values.put(e.getKey(), bindValue(type, e.getValue()));
+ values.put(e.getKey(), bindValue(e.getValue()));
}
- return new AnnotationValue(sym, values.build());
+ return new TurbineAnnotationValue(new AnnoInfo(null, sym, null, values.build()));
}
static ImmutableList<AnnoInfo> bindAnnotations(List<AnnotationInfo> input) {
ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder();
for (AnnotationInfo annotation : input) {
- AnnotationValue anno = bindAnnotationValue(Type.VOID, annotation);
- result.add(new AnnoInfo(null, anno.sym(), null, anno.values()));
+ TurbineAnnotationValue anno = bindAnnotationValue(annotation);
+ if (!shouldSkip(anno)) {
+ result.add(anno.info());
+ }
}
return result.build();
}
+ private static boolean shouldSkip(TurbineAnnotationValue anno) {
+ // ct.sym contains fake annotations without corresponding class files.
+ return anno.sym().equals(ClassSymbol.PROFILE_ANNOTATION)
+ || anno.sym().equals(ClassSymbol.PROPRIETARY_ANNOTATION);
+ }
+
private static ClassSymbol asClassSymbol(String s) {
return new ClassSymbol(s.substring(1, s.length() - 1));
}
- private static Const bindArrayValue(Type type, ArrayValue value) {
+ private static Const bindArrayValue(ArrayValue value) {
ImmutableList.Builder<Const> elements = ImmutableList.builder();
for (ElementValue element : value.elements()) {
- elements.add(bindValue(type, element));
+ elements.add(bindValue(element));
}
return new ArrayInitValue(elements.build());
}
@@ -164,30 +170,22 @@ public class BytecodeBinder {
if (type.tyKind() != Type.TyKind.PRIM_TY) {
return value;
}
+ // Deficient numberic types and booleans are all stored as ints in the class file,
+ // coerce them to the target type.
// TODO(b/32626659): this is not bug-compatible with javac
switch (((Type.PrimTy) type).primkind()) {
case CHAR:
return new Const.CharValue(value.asChar().value());
case SHORT:
return new Const.ShortValue(value.asShort().value());
- case INT:
- return new Const.IntValue(value.asInteger().value());
- case LONG:
- return new Const.LongValue(value.asLong().value());
- case FLOAT:
- return new Const.FloatValue(value.asFloat().value());
- case DOUBLE:
- return new Const.DoubleValue(value.asDouble().value());
case BOOLEAN:
// boolean constants are encoded as integers
return new Const.BooleanValue(value.asInteger().value() != 0);
case BYTE:
return new Const.ByteValue(value.asByte().value());
- case STRING:
- case NULL:
+ default:
return value;
}
- throw new AssertionError(type);
}
private static Const bindEnumValue(EnumConstValue value) {
diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
index 6ff8cb4..b992643 100644
--- a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
+++ b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
@@ -32,6 +32,7 @@ import com.google.turbine.binder.env.Env;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.bytecode.ClassFile;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
@@ -332,7 +333,8 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
for (Sig.TySig t : sig.interfaceBounds()) {
bounds.add(BytecodeBinder.bindTy(t, scope));
}
- return new TyVarInfo(IntersectionTy.create(bounds.build()), ImmutableList.of());
+ return new TyVarInfo(
+ IntersectionTy.create(bounds.build()), /* lowerBound= */ null, ImmutableList.of());
}
@Override
@@ -350,14 +352,17 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
FieldSymbol fieldSym = new FieldSymbol(sym, cfi.name());
Type type =
BytecodeBinder.bindTy(
- new SigParser(cfi.descriptor()).parseType(),
+ new SigParser(firstNonNull(cfi.signature(), cfi.descriptor())).parseType(),
makeScope(env, sym, ImmutableMap.of()));
int access = cfi.access();
Const.Value value = cfi.value();
if (value != null) {
value = BytecodeBinder.bindConstValue(type, value);
}
- fields.add(new FieldInfo(fieldSym, type, access, ImmutableList.of(), null, value));
+ ImmutableList<AnnoInfo> annotations =
+ BytecodeBinder.bindAnnotations(cfi.annotations());
+ fields.add(
+ new FieldInfo(fieldSym, type, access, annotations, /* decl= */ null, value));
}
return fields.build();
}
@@ -374,15 +379,16 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
@Override
public ImmutableList<MethodInfo> get() {
ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder();
+ int idx = 0;
for (ClassFile.MethodInfo m : classFile.get().methods()) {
- methods.add(bindMethod(m));
+ methods.add(bindMethod(idx++, m));
}
return methods.build();
}
});
- private MethodInfo bindMethod(ClassFile.MethodInfo m) {
- MethodSymbol methodSymbol = new MethodSymbol(sym, m.name());
+ private MethodInfo bindMethod(int methodIdx, ClassFile.MethodInfo m) {
+ MethodSymbol methodSymbol = new MethodSymbol(methodIdx, sym, m.name());
Sig.MethodSig sig = new SigParser(firstNonNull(m.signature(), m.descriptor())).parseMethodSig();
ImmutableMap<String, TyVarSymbol> tyParams;
@@ -414,28 +420,43 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
ImmutableList.Builder<ParamInfo> formals = ImmutableList.builder();
int idx = 0;
for (Sig.TySig tySig : sig.params()) {
- String name = null;
+ String name;
int access = 0;
if (idx < m.parameters().size()) {
ParameterInfo paramInfo = m.parameters().get(idx);
name = paramInfo.name();
- access = paramInfo.access();
+ // ignore parameter modifiers for bug-parity with javac:
+ // https://bugs.openjdk.java.net/browse/JDK-8226216
+ // access = paramInfo.access();
+ } else {
+ name = "arg" + idx;
}
ImmutableList<AnnoInfo> annotations =
(idx < m.parameterAnnotations().size())
? BytecodeBinder.bindAnnotations(m.parameterAnnotations().get(idx))
: ImmutableList.of();
- formals.add(new ParamInfo(BytecodeBinder.bindTy(tySig, scope), name, annotations, access));
+ formals.add(
+ new ParamInfo(
+ new ParamSymbol(methodSymbol, name),
+ BytecodeBinder.bindTy(tySig, scope),
+ annotations,
+ access));
idx++;
}
ImmutableList.Builder<Type> exceptions = ImmutableList.builder();
- for (TySig e : sig.exceptions()) {
- exceptions.add(BytecodeBinder.bindTy(e, scope));
+ if (!sig.exceptions().isEmpty()) {
+ for (TySig e : sig.exceptions()) {
+ exceptions.add(BytecodeBinder.bindTy(e, scope));
+ }
+ } else {
+ for (String e : m.exceptions()) {
+ exceptions.add(ClassTy.asNonParametricClassTy(new ClassSymbol(e)));
+ }
}
Const defaultValue =
- m.defaultValue() != null ? BytecodeBinder.bindValue(ret, m.defaultValue()) : null;
+ m.defaultValue() != null ? BytecodeBinder.bindValue(m.defaultValue()) : null;
ImmutableList<AnnoInfo> annotations = BytecodeBinder.bindAnnotations(m.annotations());
@@ -487,7 +508,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
}
});
- private RetentionPolicy bindRetention(AnnotationInfo annotation) {
+ private static RetentionPolicy bindRetention(AnnotationInfo annotation) {
ElementValue val = annotation.elementValuePairs().get("value");
if (val.kind() != Kind.ENUM) {
return null;
diff --git a/java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java b/java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java
index de50a2e..b41edb0 100644
--- a/java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java
+++ b/java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java
@@ -62,14 +62,14 @@ public class CompoundTopLevelIndex implements TopLevelIndex {
}
@Override
- public Scope lookupPackage(ImmutableList<String> packagename) {
+ public PackageScope lookupPackage(Iterable<String> packagename) {
// When returning package scopes, build up a compound scope containing entries from all
// indices with matching packages.
- CompoundScope result = null;
+ PackageScope result = null;
for (TopLevelIndex index : indexes) {
- Scope packageScope = index.lookupPackage(packagename);
+ PackageScope packageScope = index.lookupPackage(packagename);
if (packageScope != null) {
- result = result == null ? CompoundScope.base(packageScope) : result.append(packageScope);
+ result = result == null ? packageScope : result.append(packageScope);
}
}
return result;
diff --git a/java/com/google/turbine/binder/lookup/ImportIndex.java b/java/com/google/turbine/binder/lookup/ImportIndex.java
index afa985a..fd57223 100644
--- a/java/com/google/turbine/binder/lookup/ImportIndex.java
+++ b/java/com/google/turbine/binder/lookup/ImportIndex.java
@@ -142,7 +142,8 @@ public class ImportIndex implements ImportScope {
TurbineLogWithSource log, TopLevelIndex cpi, ImportDecl i) {
LookupResult base = cpi.scope().lookup(new LookupKey(i.type()));
if (base == null) {
- log.error(i.position(), ErrorKind.SYMBOL_NOT_FOUND, Joiner.on(".").join(i.type()));
+ log.error(
+ i.position(), ErrorKind.SYMBOL_NOT_FOUND, new ClassSymbol(Joiner.on("/").join(i.type())));
return null;
}
return new ImportScope() {
diff --git a/java/com/google/turbine/binder/lookup/PackageScope.java b/java/com/google/turbine/binder/lookup/PackageScope.java
new file mode 100644
index 0000000..695e802
--- /dev/null
+++ b/java/com/google/turbine/binder/lookup/PackageScope.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 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.lookup;
+
+import com.google.common.collect.Iterables;
+import com.google.turbine.binder.sym.ClassSymbol;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * A scope that corresponds to a particular package, which supports iteration over its enclosed
+ * classes.
+ */
+public interface PackageScope extends Scope {
+
+ /** Returns the top-level classes enclosed by this package. */
+ Iterable<ClassSymbol> classes();
+
+ default PackageScope append(PackageScope next) {
+ return concat(this, next);
+ }
+
+ static PackageScope concat(PackageScope base, PackageScope next) {
+ return new PackageScope() {
+ @Override
+ public Iterable<ClassSymbol> classes() {
+ return Iterables.concat(base.classes(), next.classes());
+ }
+
+ @Override
+ public @Nullable LookupResult lookup(LookupKey lookupKey) {
+ LookupResult result = base.lookup(lookupKey);
+ if (result != null) {
+ return result;
+ }
+ return next.lookup(lookupKey);
+ }
+ };
+ }
+}
diff --git a/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java b/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java
index 2454319..4ec05bc 100644
--- a/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java
+++ b/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java
@@ -16,6 +16,8 @@
package com.google.turbine.binder.lookup;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.turbine.binder.sym.ClassSymbol;
import java.util.HashMap;
@@ -157,7 +159,7 @@ public class SimpleTopLevelIndex implements TopLevelIndex {
/** Returns a {@link Scope} that performs lookups in the given qualified package name. */
@Override
- public Scope lookupPackage(ImmutableList<String> packagename) {
+ public PackageScope lookupPackage(Iterable<String> packagename) {
Node curr = root;
for (String bit : packagename) {
curr = curr.lookup(bit);
@@ -168,7 +170,7 @@ public class SimpleTopLevelIndex implements TopLevelIndex {
return new PackageIndex(curr);
}
- static class PackageIndex implements Scope {
+ static class PackageIndex implements PackageScope {
private final Node node;
@@ -184,5 +186,25 @@ public class SimpleTopLevelIndex implements TopLevelIndex {
}
return null;
}
+
+ private final Supplier<ImmutableList<ClassSymbol>> classes =
+ Suppliers.memoize(
+ new Supplier<ImmutableList<ClassSymbol>>() {
+ @Override
+ public ImmutableList<ClassSymbol> get() {
+ ImmutableList.Builder<ClassSymbol> result = ImmutableList.builder();
+ for (Node child : node.children.values()) {
+ if (child.sym != null) {
+ result.add(child.sym);
+ }
+ }
+ return result.build();
+ }
+ });
+
+ @Override
+ public Iterable<ClassSymbol> classes() {
+ return classes.get();
+ }
}
}
diff --git a/java/com/google/turbine/binder/lookup/TopLevelIndex.java b/java/com/google/turbine/binder/lookup/TopLevelIndex.java
index 95782f5..a364119 100644
--- a/java/com/google/turbine/binder/lookup/TopLevelIndex.java
+++ b/java/com/google/turbine/binder/lookup/TopLevelIndex.java
@@ -16,7 +16,6 @@
package com.google.turbine.binder.lookup;
-import com.google.common.collect.ImmutableList;
/**
* An index of canonical type names.
@@ -36,5 +35,5 @@ public interface TopLevelIndex {
Scope scope();
/** Returns a scope to look up members of the given package. */
- Scope lookupPackage(ImmutableList<String> packagename);
+ PackageScope lookupPackage(Iterable<String> packagename);
}
diff --git a/java/com/google/turbine/binder/sym/ClassSymbol.java b/java/com/google/turbine/binder/sym/ClassSymbol.java
index 2adf15f..20513e7 100644
--- a/java/com/google/turbine/binder/sym/ClassSymbol.java
+++ b/java/com/google/turbine/binder/sym/ClassSymbol.java
@@ -33,6 +33,23 @@ public class ClassSymbol implements Symbol {
public static final ClassSymbol STRING = new ClassSymbol("java/lang/String");
public static final ClassSymbol ENUM = new ClassSymbol("java/lang/Enum");
public static final ClassSymbol ANNOTATION = new ClassSymbol("java/lang/annotation/Annotation");
+ public static final ClassSymbol INHERITED = new ClassSymbol("java/lang/annotation/Inherited");
+ public static final ClassSymbol CLONEABLE = new ClassSymbol("java/lang/Cloneable");
+ public static final ClassSymbol SERIALIZABLE = new ClassSymbol("java/io/Serializable");
+ public static final ClassSymbol DEPRECATED = new ClassSymbol("java/lang/Deprecated");
+ public static final ClassSymbol PROFILE_ANNOTATION = new ClassSymbol("jdk/Profile+Annotation");
+ public static final ClassSymbol PROPRIETARY_ANNOTATION =
+ new ClassSymbol("sun/Proprietary+Annotation");
+ public static final ClassSymbol ERROR = new ClassSymbol("<error>");
+
+ public static final ClassSymbol CHARACTER = new ClassSymbol("java/lang/Character");
+ public static final ClassSymbol SHORT = new ClassSymbol("java/lang/Short");
+ public static final ClassSymbol INTEGER = new ClassSymbol("java/lang/Integer");
+ public static final ClassSymbol LONG = new ClassSymbol("java/lang/Long");
+ public static final ClassSymbol FLOAT = new ClassSymbol("java/lang/Float");
+ public static final ClassSymbol DOUBLE = new ClassSymbol("java/lang/Double");
+ public static final ClassSymbol BOOLEAN = new ClassSymbol("java/lang/Boolean");
+ public static final ClassSymbol BYTE = new ClassSymbol("java/lang/Byte");
private final String className;
@@ -64,4 +81,17 @@ public class ClassSymbol implements Symbol {
public Kind symKind() {
return Kind.CLASS;
}
+
+ public String simpleName() {
+ return binaryName().substring(binaryName().lastIndexOf('/') + 1);
+ }
+
+ public String packageName() {
+ int idx = binaryName().lastIndexOf('/');
+ return idx != -1 ? binaryName().substring(0, idx) : "";
+ }
+
+ public PackageSymbol owner() {
+ return new PackageSymbol(packageName());
+ }
}
diff --git a/java/com/google/turbine/binder/sym/FieldSymbol.java b/java/com/google/turbine/binder/sym/FieldSymbol.java
index 21304e7..d6c3cbc 100644
--- a/java/com/google/turbine/binder/sym/FieldSymbol.java
+++ b/java/com/google/turbine/binder/sym/FieldSymbol.java
@@ -61,6 +61,6 @@ public class FieldSymbol implements Symbol {
@Override
public String toString() {
- return owner + "#" + name;
+ return name;
}
}
diff --git a/java/com/google/turbine/binder/sym/MethodSymbol.java b/java/com/google/turbine/binder/sym/MethodSymbol.java
index b2050f4..f4b211d 100644
--- a/java/com/google/turbine/binder/sym/MethodSymbol.java
+++ b/java/com/google/turbine/binder/sym/MethodSymbol.java
@@ -22,10 +22,17 @@ import java.util.Objects;
/** A method symbol. */
@Immutable
public class MethodSymbol implements Symbol {
+ /**
+ * The index of the method in its enclosing element. Used to implement equals and hashCode, since
+ * methods aren't uniquely identified by their name and owner.
+ */
+ private final int idx;
+
private final ClassSymbol owner;
private final String name;
- public MethodSymbol(ClassSymbol owner, String name) {
+ public MethodSymbol(int idx, ClassSymbol owner, String name) {
+ this.idx = idx;
this.owner = owner;
this.name = name;
}
@@ -56,7 +63,7 @@ public class MethodSymbol implements Symbol {
return false;
}
MethodSymbol other = (MethodSymbol) obj;
- return name().equals(other.name()) && owner().equals(other.owner());
+ return name().equals(other.name()) && owner().equals(other.owner()) && idx == other.idx;
}
@Override
diff --git a/java/com/google/turbine/binder/sym/PackageSymbol.java b/java/com/google/turbine/binder/sym/PackageSymbol.java
new file mode 100644
index 0000000..8354a34
--- /dev/null
+++ b/java/com/google/turbine/binder/sym/PackageSymbol.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 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.sym;
+
+import com.google.errorprone.annotations.Immutable;
+
+/** A package symbol. */
+@Immutable
+public class PackageSymbol implements Symbol {
+
+ final String binaryName;
+
+ public PackageSymbol(String binaryName) {
+ this.binaryName = binaryName;
+ }
+
+ @Override
+ public int hashCode() {
+ return binaryName.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof PackageSymbol && binaryName.equals(((PackageSymbol) obj).binaryName);
+ }
+
+ @Override
+ public String toString() {
+ return binaryName.replace('/', '.');
+ }
+
+ @Override
+ public Kind symKind() {
+ return Kind.PACKAGE;
+ }
+
+ public String binaryName() {
+ return binaryName;
+ }
+}
diff --git a/java/com/google/turbine/binder/sym/ParamSymbol.java b/java/com/google/turbine/binder/sym/ParamSymbol.java
new file mode 100644
index 0000000..328658e
--- /dev/null
+++ b/java/com/google/turbine/binder/sym/ParamSymbol.java
@@ -0,0 +1,66 @@
+/*
+ * 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.sym;
+
+import com.google.errorprone.annotations.Immutable;
+import java.util.Objects;
+
+/** A parameter symbol. */
+@Immutable
+public class ParamSymbol implements Symbol {
+ private final MethodSymbol owner;
+ private final String name;
+
+ public ParamSymbol(MethodSymbol owner, String name) {
+ this.owner = owner;
+ this.name = name;
+ }
+
+ /** The enclosing class. */
+ public MethodSymbol owner() {
+ return owner;
+ }
+
+ /** The parameter name. */
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public Kind symKind() {
+ return Kind.PARAMETER;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, owner);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ParamSymbol)) {
+ return false;
+ }
+ ParamSymbol other = (ParamSymbol) obj;
+ return name().equals(other.name()) && owner().equals(other.owner());
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/java/com/google/turbine/binder/sym/Symbol.java b/java/com/google/turbine/binder/sym/Symbol.java
index b2a7723..bc142cb 100644
--- a/java/com/google/turbine/binder/sym/Symbol.java
+++ b/java/com/google/turbine/binder/sym/Symbol.java
@@ -27,7 +27,9 @@ public interface Symbol {
TY_PARAM,
METHOD,
FIELD,
- MODULE
+ PARAMETER,
+ MODULE,
+ PACKAGE
}
/** The symbol kind. */
diff --git a/java/com/google/turbine/binder/sym/TyVarSymbol.java b/java/com/google/turbine/binder/sym/TyVarSymbol.java
index a73a79e..1ecec11 100644
--- a/java/com/google/turbine/binder/sym/TyVarSymbol.java
+++ b/java/com/google/turbine/binder/sym/TyVarSymbol.java
@@ -62,6 +62,6 @@ public class TyVarSymbol implements Symbol {
@Override
public String toString() {
- return owner + "#" + name;
+ return name;
}
}
diff --git a/java/com/google/turbine/bytecode/AnnotationWriter.java b/java/com/google/turbine/bytecode/AnnotationWriter.java
index 868b548..b547971 100644
--- a/java/com/google/turbine/bytecode/AnnotationWriter.java
+++ b/java/com/google/turbine/bytecode/AnnotationWriter.java
@@ -20,8 +20,8 @@ import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteArrayDataOutput;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue;
-import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.AnnotationValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ArrayValue;
+import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineAnnotationValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineClassValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue;
@@ -71,10 +71,8 @@ public class AnnotationWriter {
writeArrayElementValue((ArrayValue) value);
break;
case ANNOTATION:
- writeAnnotationElementValue((AnnotationValue) value);
+ writeAnnotationElementValue((ConstTurbineAnnotationValue) value);
break;
- default:
- throw new AssertionError(value.kind());
}
}
@@ -136,7 +134,7 @@ public class AnnotationWriter {
}
}
- private void writeAnnotationElementValue(AnnotationValue value) {
+ private void writeAnnotationElementValue(ConstTurbineAnnotationValue value) {
output.writeByte('@');
writeAnnotation(value.annotation());
}
@@ -178,8 +176,6 @@ public class AnnotationWriter {
output.writeByte(typeParameterBoundTarget.typeParameterIndex());
output.writeByte(typeParameterBoundTarget.boundIndex());
break;
- default:
- throw new AssertionError(target.kind());
}
}
}
diff --git a/java/com/google/turbine/bytecode/AttributeWriter.java b/java/com/google/turbine/bytecode/AttributeWriter.java
index 5e3ea95..c5ffd16 100644
--- a/java/com/google/turbine/bytecode/AttributeWriter.java
+++ b/java/com/google/turbine/bytecode/AttributeWriter.java
@@ -87,8 +87,6 @@ public class AttributeWriter {
case MODULE:
writeModule((Attribute.Module) attribute);
break;
- default:
- throw new AssertionError(attribute.kind());
}
}
diff --git a/java/com/google/turbine/bytecode/ClassFile.java b/java/com/google/turbine/bytecode/ClassFile.java
index 54b6983..8ee2aac 100644
--- a/java/com/google/turbine/bytecode/ClassFile.java
+++ b/java/com/google/turbine/bytecode/ClassFile.java
@@ -368,7 +368,7 @@ public class ClassFile {
public interface ElementValue {
/** The value kind. */
- Kind kind();
+ ElementValue.Kind kind();
/** Element value kinds. */
enum Kind {
@@ -391,8 +391,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.ENUM;
+ public ElementValue.Kind kind() {
+ return ElementValue.Kind.ENUM;
}
/** The type of the enum. */
@@ -417,8 +417,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.CONST;
+ public ElementValue.Kind kind() {
+ return ElementValue.Kind.CONST;
}
/** The constant value. */
@@ -437,8 +437,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.ARRAY;
+ public ElementValue.Kind kind() {
+ return ElementValue.Kind.ARRAY;
}
/** The elements of the array. */
@@ -457,8 +457,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.CLASS;
+ public ElementValue.Kind kind() {
+ return ElementValue.Kind.CLASS;
}
/** The class name. */
@@ -468,17 +468,17 @@ public class ClassFile {
}
/** A nested annotation value. */
- class AnnotationValue implements ElementValue {
+ class ConstTurbineAnnotationValue implements ElementValue {
private final AnnotationInfo annotation;
- public AnnotationValue(AnnotationInfo annotation) {
+ public ConstTurbineAnnotationValue(AnnotationInfo annotation) {
this.annotation = annotation;
}
@Override
- public Kind kind() {
- return Kind.ANNOTATION;
+ public ElementValue.Kind kind() {
+ return ElementValue.Kind.ANNOTATION;
}
/** The annotation. */
@@ -566,7 +566,7 @@ public class ClassFile {
}
/** Returns the target info kind. */
- public abstract Kind kind();
+ public abstract Target.Kind kind();
}
/** A JVMS 4.7.20.1 type_parameter_target. */
@@ -582,8 +582,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.TYPE_PARAMETER;
+ public Target.Kind kind() {
+ return Target.Kind.TYPE_PARAMETER;
}
}
@@ -596,8 +596,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.SUPERTYPE;
+ public Target.Kind kind() {
+ return Target.Kind.SUPERTYPE;
}
public int index() {
@@ -616,8 +616,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.TYPE_PARAMETER_BOUND;
+ public Target.Kind kind() {
+ return Target.Kind.TYPE_PARAMETER_BOUND;
}
public int typeParameterIndex() {
@@ -633,8 +633,8 @@ public class ClassFile {
public static final Target EMPTY_TARGET =
new Target() {
@Override
- public Kind kind() {
- return Kind.EMPTY;
+ public Target.Kind kind() {
+ return Target.Kind.EMPTY;
}
};
@@ -647,8 +647,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.FORMAL_PARAMETER;
+ public Target.Kind kind() {
+ return Target.Kind.FORMAL_PARAMETER;
}
public int index() {
@@ -665,8 +665,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.THROWS;
+ public Target.Kind kind() {
+ return Target.Kind.THROWS;
}
public int index() {
@@ -689,22 +689,22 @@ public class ClassFile {
/** Adds an array type_path_kind entry. */
public TypePath array() {
- return new TypePath(Kind.ARRAY, this);
+ return new TypePath(TypePath.Kind.ARRAY, this);
}
/** Adds a nested type type_path_kind entry. */
public TypePath nested() {
- return new TypePath(Kind.NESTED, this);
+ return new TypePath(TypePath.Kind.NESTED, this);
}
/** Adds a wildcard bound type_path_kind entry. */
public TypePath wild() {
- return new TypePath(Kind.WILDCARD_BOUND, this);
+ return new TypePath(TypePath.Kind.WILDCARD_BOUND, this);
}
/** Adds a type argument type_path_kind entry. */
public TypePath typeArgument(int idx) {
- return new TypePath(idx, Kind.TYPE_ARGUMENT, this);
+ return new TypePath(idx, TypePath.Kind.TYPE_ARGUMENT, this);
}
/** A type_path_kind. */
@@ -722,15 +722,15 @@ public class ClassFile {
}
private final TypePath parent;
- private final Kind kind;
+ private final TypePath.Kind kind;
private final int index;
- private TypePath(Kind kind, TypePath parent) {
+ private TypePath(TypePath.Kind kind, TypePath parent) {
// JVMS 4.7.20.2: type_argument_index is 0 if the bound kind is not TYPE_ARGUMENT
this(0, kind, parent);
}
- private TypePath(int index, Kind kind, TypePath parent) {
+ private TypePath(int index, TypePath.Kind kind, TypePath parent) {
this.index = index;
this.kind = kind;
this.parent = parent;
diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java
index a894997..9c79b42 100644
--- a/java/com/google/turbine/bytecode/ClassReader.java
+++ b/java/com/google/turbine/bytecode/ClassReader.java
@@ -19,9 +19,10 @@ package com.google.turbine.bytecode;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.errorprone.annotations.CheckReturnValue;
+import com.google.errorprone.annotations.FormatMethod;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue;
-import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.AnnotationValue;
+import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineAnnotationValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineClassValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue;
@@ -33,6 +34,7 @@ import com.google.turbine.bytecode.ClassFile.ModuleInfo.ProvideInfo;
import com.google.turbine.bytecode.ClassFile.ModuleInfo.RequireInfo;
import com.google.turbine.bytecode.ClassFile.ModuleInfo.UseInfo;
import com.google.turbine.model.Const;
+import com.google.turbine.model.TurbineFlag;
import java.util.ArrayList;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -59,6 +61,7 @@ public class ClassReader {
this.reader = new ByteReader(bytes, 0);
}
+ @FormatMethod
@CheckReturnValue
Error error(String format, Object... args) {
StringBuilder sb = new StringBuilder();
@@ -72,7 +75,7 @@ public class ClassReader {
private ClassFile read() {
int magic = reader.u4();
if (magic != 0xcafebabe) {
- throw error("bad magic: 0x%x", path, magic);
+ throw error("bad magic: 0x%x", magic);
}
int minorVersion = reader.u2();
int majorVersion = reader.u2();
@@ -212,6 +215,11 @@ public class ClassReader {
for (int i = 0; i < numParameters; i++) {
String name = constantPool.utf8(reader.u2());
int access = reader.u2();
+ if ((access & (TurbineFlag.ACC_SYNTHETIC | TurbineFlag.ACC_MANDATED)) != 0) {
+ // ExecutableElement#getParameters doesn't expect synthetic or mandated
+ // parameters
+ continue;
+ }
parameters.add(new ParameterInfo(name, access));
}
}
@@ -321,17 +329,22 @@ public class ClassReader {
int tag = reader.u1();
switch (tag) {
case 'B':
+ return new ConstValue(readConst(constantPool).asByte());
case 'C':
+ return new ConstValue(readConst(constantPool).asChar());
+ case 'S':
+ return new ConstValue(readConst(constantPool).asShort());
case 'D':
case 'F':
case 'I':
case 'J':
- case 'S':
- case 'Z':
case 's':
+ return new ConstValue(readConst(constantPool));
+ case 'Z':
{
- int constValueIndex = reader.u2();
- return new ConstValue(constantPool.constant(constValueIndex));
+ Const.Value value = readConst(constantPool);
+ // boolean constants are encoded as integers
+ return new ConstValue(new Const.BooleanValue(value.asInteger().value() != 0));
}
case 'e':
{
@@ -348,7 +361,7 @@ public class ClassReader {
return new ConstTurbineClassValue(className);
}
case '@':
- return new AnnotationValue(readAnnotation(constantPool));
+ return new ConstTurbineAnnotationValue(readAnnotation(constantPool));
case '[':
{
int numValues = reader.u2();
@@ -363,6 +376,11 @@ public class ClassReader {
throw new AssertionError(String.format("bad tag value %c", tag));
}
+ private Const.Value readConst(ConstantPoolReader constantPool) {
+ int constValueIndex = reader.u2();
+ return constantPool.constant(constValueIndex);
+ }
+
/** Reads JVMS 4.6 method_infos. */
private List<ClassFile.MethodInfo> readMethods(ConstantPoolReader constantPool) {
int methodsCount = reader.u2();
@@ -415,6 +433,10 @@ public class ClassReader {
for (ImmutableList.Builder<AnnotationInfo> x : parameterAnnotationsBuilder) {
parameterAnnotations.add(x.build());
}
+ if ((accessFlags & (TurbineFlag.ACC_BRIDGE | TurbineFlag.ACC_SYNTHETIC)) != 0) {
+ // javac doesn't enter synthetic members for reasons 'lost to history', so we don't either
+ continue;
+ }
methods.add(
new ClassFile.MethodInfo(
accessFlags,
@@ -454,6 +476,8 @@ public class ClassReader {
String desc = constantPool.utf8(descriptorIndex);
int attributesCount = reader.u2();
Const.Value value = null;
+ ImmutableList.Builder<ClassFile.AnnotationInfo> annotations = ImmutableList.builder();
+ String signature = null;
for (int j = 0; j < attributesCount; j++) {
String attributeName = constantPool.utf8(reader.u2());
switch (attributeName) {
@@ -461,6 +485,13 @@ public class ClassReader {
reader.u4(); // length
value = constantPool.constant(reader.u2());
break;
+ case "RuntimeInvisibleAnnotations":
+ case "RuntimeVisibleAnnotations":
+ readAnnotations(annotations, constantPool);
+ break;
+ case "Signature":
+ signature = readSignature(constantPool);
+ break;
default:
reader.skip(reader.u4());
break;
@@ -471,10 +502,10 @@ public class ClassReader {
accessFlags,
name,
desc,
- /*signature*/ null,
+ signature,
value,
- ImmutableList.of(),
- ImmutableList.of()));
+ annotations.build(),
+ /* typeAnnotations= */ ImmutableList.of()));
}
return fields;
}
diff --git a/java/com/google/turbine/bytecode/ClassWriter.java b/java/com/google/turbine/bytecode/ClassWriter.java
index 4a89ec8..c3490ca 100644
--- a/java/com/google/turbine/bytecode/ClassWriter.java
+++ b/java/com/google/turbine/bytecode/ClassWriter.java
@@ -110,8 +110,6 @@ public class ClassWriter {
case UTF8:
output.writeUTF(((StringValue) value).value());
break;
- default:
- throw new AssertionError(e.kind());
}
}
}
diff --git a/java/com/google/turbine/bytecode/ConstantPool.java b/java/com/google/turbine/bytecode/ConstantPool.java
index b423cfc..673102c 100644
--- a/java/com/google/turbine/bytecode/ConstantPool.java
+++ b/java/com/google/turbine/bytecode/ConstantPool.java
@@ -66,9 +66,8 @@ public class ConstantPool {
// "In retrospect, making 8-byte constants take two constant pool entries
// was a poor choice." -- JVMS 4.4.5
return 2;
- default:
- throw new AssertionError(kind);
}
+ throw new AssertionError(kind);
}
/** A constant pool entry. */
diff --git a/java/com/google/turbine/bytecode/sig/SigWriter.java b/java/com/google/turbine/bytecode/sig/SigWriter.java
index dab4f47..3711186 100644
--- a/java/com/google/turbine/bytecode/sig/SigWriter.java
+++ b/java/com/google/turbine/bytecode/sig/SigWriter.java
@@ -113,8 +113,6 @@ public class SigWriter {
sb.append('+');
writeTySig(((UpperBoundTySig) sig).bound());
break;
- default:
- throw new AssertionError(sig.kind());
}
}
@@ -198,8 +196,6 @@ public class SigWriter {
case WILD_TY_SIG:
wildTyArgSig((WildTySig) p);
break;
- default:
- throw new AssertionError(p.kind());
}
}
diff --git a/java/com/google/turbine/deps/Dependencies.java b/java/com/google/turbine/deps/Dependencies.java
index f76efe7..92193e8 100644
--- a/java/com/google/turbine/deps/Dependencies.java
+++ b/java/com/google/turbine/deps/Dependencies.java
@@ -22,14 +22,22 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.ClassPath;
+import com.google.turbine.binder.bound.EnumConstantValue;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
+import com.google.turbine.binder.bound.TurbineClassValue;
import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
import com.google.turbine.binder.bytecode.BytecodeBoundClass;
import com.google.turbine.binder.env.CompoundEnv;
import com.google.turbine.binder.env.Env;
import com.google.turbine.binder.env.SimpleEnv;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.lower.Lower.Lowered;
+import com.google.turbine.model.Const;
import com.google.turbine.proto.DepsProto;
+import com.google.turbine.type.AnnoInfo;
+import com.google.turbine.type.Type;
import java.io.BufferedInputStream;
import java.io.IOError;
import java.io.IOException;
@@ -82,13 +90,59 @@ public class Dependencies {
Env<ClassSymbol, TypeBoundClass> env =
CompoundEnv.<ClassSymbol, TypeBoundClass>of(new SimpleEnv<>(bound.units()))
.append(bound.classPathEnv());
- Set<ClassSymbol> closure = new LinkedHashSet<>();
+ Set<ClassSymbol> closure = new LinkedHashSet<>(lowered.symbols());
for (ClassSymbol sym : lowered.symbols()) {
- addSuperTypes(closure, env, sym);
+ TypeBoundClass info = env.get(sym);
+ addAnnotations(closure, info.annotations());
+ for (MethodInfo method : info.methods()) {
+ addAnnotations(closure, method.annotations());
+ }
+ for (FieldInfo field : info.fields()) {
+ addAnnotations(closure, field.annotations());
+ }
+ addSuperTypes(closure, env, info);
}
return closure;
}
+ private static void addAnnotations(
+ Set<ClassSymbol> closure, ImmutableList<AnnoInfo> annotations) {
+ for (AnnoInfo annoInfo : annotations) {
+ addAnnotation(closure, annoInfo);
+ }
+ }
+
+ private static void addAnnotation(Set<ClassSymbol> closure, AnnoInfo annoInfo) {
+ closure.add(annoInfo.sym());
+ for (Const c : annoInfo.values().values()) {
+ addConst(closure, c);
+ }
+ }
+
+ private static void addConst(Set<ClassSymbol> closure, Const c) {
+ switch (c.kind()) {
+ case ARRAY:
+ for (Const e : ((Const.ArrayInitValue) c).elements()) {
+ addConst(closure, e);
+ }
+ break;
+ case CLASS_LITERAL:
+ Type t = ((TurbineClassValue) c).type();
+ if (t.tyKind() == Type.TyKind.CLASS_TY) {
+ closure.add(((Type.ClassTy) t).sym());
+ }
+ break;
+ case ENUM_CONSTANT:
+ closure.add(((EnumConstantValue) c).sym().owner());
+ break;
+ case ANNOTATION:
+ addAnnotation(closure, ((TurbineAnnotationValue) c).info());
+ break;
+ case PRIMITIVE:
+ // continue below
+ }
+ }
+
private static void addSuperTypes(
Set<ClassSymbol> closure, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym) {
if (!closure.add(sym)) {
@@ -98,6 +152,11 @@ public class Dependencies {
if (info == null) {
return;
}
+ addSuperTypes(closure, env, info);
+ }
+
+ private static void addSuperTypes(
+ Set<ClassSymbol> closure, Env<ClassSymbol, TypeBoundClass> env, TypeBoundClass info) {
if (info.superclass() != null) {
addSuperTypes(closure, env, info.superclass());
}
@@ -109,11 +168,11 @@ public class Dependencies {
private static void addPackageInfos(Set<ClassSymbol> closure, BindingResult bound) {
Set<ClassSymbol> packages = new LinkedHashSet<>();
for (ClassSymbol sym : closure) {
- int idx = sym.binaryName().lastIndexOf('/');
- if (idx == -1) {
+ String packageName = sym.packageName();
+ if (packageName.isEmpty()) {
continue;
}
- packages.add(new ClassSymbol(sym.binaryName().substring(0, idx) + "/package-info"));
+ packages.add(new ClassSymbol(packageName + "/package-info"));
}
for (ClassSymbol pkg : packages) {
if (bound.classPathEnv().get(pkg) != null) {
@@ -134,6 +193,7 @@ public class Dependencies {
ImmutableList<String> depsArtifacts) {
if (directJars.isEmpty()) {
// the compilation doesn't support strict deps (e.g. proto libraries)
+ // TODO(cushon): make this a usage error
return transitiveClasspath;
}
Set<String> reduced = new HashSet<>(directJars);
@@ -153,8 +213,6 @@ public class Dependencies {
case INCOMPLETE:
case UNUSED:
break;
- default:
- throw new AssertionError(dep.getKind());
}
}
}
diff --git a/java/com/google/turbine/deps/Transitive.java b/java/com/google/turbine/deps/Transitive.java
index f9a29a1..8b0d44d 100644
--- a/java/com/google/turbine/deps/Transitive.java
+++ b/java/com/google/turbine/deps/Transitive.java
@@ -17,7 +17,6 @@
package com.google.turbine.deps;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.ClassPath;
@@ -65,7 +64,7 @@ public class Transitive {
*/
public static ClassFile trimClass(ClassFile cf) {
// drop non-constant fields
- Builder<FieldInfo> fields = ImmutableList.builder();
+ ImmutableList.Builder<FieldInfo> fields = ImmutableList.builder();
for (FieldInfo f : cf.fields()) {
if (f.value() != null) {
fields.add(f);
@@ -75,7 +74,7 @@ public class Transitive {
// To do this for javac, we would have to scan all remaining signatures and preserve attributes
// for reachable inner classes, but turbine only needs the attributes for the immediate
// children or parent of the current class.
- Builder<InnerClass> innerClasses = ImmutableList.builder();
+ ImmutableList.Builder<InnerClass> innerClasses = ImmutableList.builder();
for (InnerClass i : cf.innerClasses()) {
if (i.innerClass().equals(cf.name()) || i.outerClass().equals(cf.name())) {
innerClasses.add(i);
diff --git a/java/com/google/turbine/diag/SourceFile.java b/java/com/google/turbine/diag/SourceFile.java
index cb4133b..3868252 100644
--- a/java/com/google/turbine/diag/SourceFile.java
+++ b/java/com/google/turbine/diag/SourceFile.java
@@ -16,12 +16,25 @@
package com.google.turbine.diag;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import java.util.Objects;
+
/** A source file. */
public class SourceFile {
private final String path;
private final String source;
+ private final Supplier<LineMap> lineMap =
+ Suppliers.memoize(
+ new Supplier<LineMap>() {
+ @Override
+ public LineMap get() {
+ return LineMap.create(source);
+ }
+ });
+
public SourceFile(String path, String source) {
this.path = path;
this.source = source;
@@ -36,4 +49,22 @@ public class SourceFile {
public String source() {
return source;
}
+
+ LineMap lineMap() {
+ return lineMap.get();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof SourceFile)) {
+ return false;
+ }
+ SourceFile that = (SourceFile) obj;
+ return Objects.equals(path, that.path) && source.equals(that.source);
+ }
+
+ @Override
+ public int hashCode() {
+ return path != null ? path.hashCode() : 0;
+ }
}
diff --git a/java/com/google/turbine/diag/TurbineDiagnostic.java b/java/com/google/turbine/diag/TurbineDiagnostic.java
index 0404a8e..ccbaa7f 100644
--- a/java/com/google/turbine/diag/TurbineDiagnostic.java
+++ b/java/com/google/turbine/diag/TurbineDiagnostic.java
@@ -16,7 +16,6 @@
package com.google.turbine.diag;
-import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Iterables.getOnlyElement;
import static java.util.Objects.requireNonNull;
@@ -27,18 +26,29 @@ import com.google.common.collect.ImmutableList;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.diag.TurbineError.ErrorKind;
import java.util.Objects;
+import javax.tools.Diagnostic;
+import org.checkerframework.checker.nullness.qual.Nullable;
/** A compilation error. */
public class TurbineDiagnostic {
+ private final Diagnostic.Kind severity;
private final ErrorKind kind;
- private final String diagnostic;
private final ImmutableList<Object> args;
+ private final @Nullable SourceFile source;
+ private final int position;
- private TurbineDiagnostic(ErrorKind kind, String diagnostic, ImmutableList<Object> args) {
+ private TurbineDiagnostic(
+ Diagnostic.Kind severity,
+ ErrorKind kind,
+ ImmutableList<Object> args,
+ @Nullable SourceFile source,
+ int position) {
+ this.severity = requireNonNull(severity);
this.kind = requireNonNull(kind);
- this.diagnostic = requireNonNull(diagnostic);
this.args = requireNonNull(args);
+ this.source = source;
+ this.position = position;
}
/** The diagnostic kind. */
@@ -46,9 +56,28 @@ public class TurbineDiagnostic {
return kind;
}
+ /**
+ * The diagnostic severity (error, warning, ...). Turbine only produces errors, non-error
+ * diagnostics are only ever created by annotation processors.
+ */
+ public Diagnostic.Kind severity() {
+ return severity;
+ }
+
/** The diagnostic message. */
public String diagnostic() {
- return diagnostic;
+ StringBuilder sb = new StringBuilder(path());
+ if (line() != -1) {
+ sb.append(':').append(line());
+ }
+ sb.append(": error: ");
+ sb.append(message().trim()).append(System.lineSeparator());
+ if (line() != -1 && column() != -1) {
+ sb.append(CharMatcher.breakingWhitespace().trimTrailingFrom(source.lineMap().line(position)))
+ .append(System.lineSeparator());
+ sb.append(Strings.repeat(" ", column() - 1)).append('^');
+ }
+ return sb.toString();
}
/** The diagnostic arguments. */
@@ -57,20 +86,28 @@ public class TurbineDiagnostic {
}
private static TurbineDiagnostic create(
- ErrorKind kind, String diagnostic, ImmutableList<Object> args) {
+ Diagnostic.Kind severity,
+ ErrorKind kind,
+ ImmutableList<Object> args,
+ SourceFile source,
+ int position) {
switch (kind) {
case SYMBOL_NOT_FOUND:
{
checkArgument(
args.size() == 1 && getOnlyElement(args) instanceof ClassSymbol,
- "diagnostic (%s) has invalid argument args %s",
- diagnostic,
+ "diagnostic (%s) has invalid argument %s",
+ kind,
args);
break;
}
default: // fall out
}
- return new TurbineDiagnostic(kind, diagnostic, args);
+ return new TurbineDiagnostic(severity, kind, args, source, position);
+ }
+
+ public static TurbineDiagnostic format(Diagnostic.Kind severity, ErrorKind kind, String message) {
+ return create(severity, kind, ImmutableList.of(message), null, -1);
}
/**
@@ -81,10 +118,7 @@ public class TurbineDiagnostic {
* @param args format args
*/
public static TurbineDiagnostic format(SourceFile source, ErrorKind kind, Object... args) {
- String path = firstNonNull(source.path(), "<>");
- String message = kind.format(args);
- String diagnostic = path + ": error: " + message.trim() + System.lineSeparator();
- return create(kind, diagnostic, ImmutableList.copyOf(args));
+ return create(Diagnostic.Kind.ERROR, kind, ImmutableList.copyOf(args), source, -1);
}
/**
@@ -95,26 +129,13 @@ public class TurbineDiagnostic {
* @param args format args
*/
public static TurbineDiagnostic format(
- SourceFile source, int position, ErrorKind kind, Object... args) {
- String path = firstNonNull(source.path(), "<>");
- LineMap lineMap = LineMap.create(source.source());
- int lineNumber = lineMap.lineNumber(position);
- int column = lineMap.column(position);
- String message = kind.format(args);
-
- StringBuilder sb = new StringBuilder(path).append(":");
- sb.append(lineNumber).append(": error: ");
- sb.append(message.trim()).append(System.lineSeparator());
- sb.append(CharMatcher.breakingWhitespace().trimTrailingFrom(lineMap.line(position)))
- .append(System.lineSeparator());
- sb.append(Strings.repeat(" ", column)).append('^');
- String diagnostic = sb.toString();
- return create(kind, diagnostic, ImmutableList.copyOf(args));
+ Diagnostic.Kind severity, SourceFile source, int position, ErrorKind kind, Object... args) {
+ return create(severity, kind, ImmutableList.copyOf(args), source, position);
}
@Override
public int hashCode() {
- return Objects.hash(diagnostic, kind);
+ return Objects.hash(kind, source, position);
}
@Override
@@ -123,6 +144,26 @@ public class TurbineDiagnostic {
return false;
}
TurbineDiagnostic that = (TurbineDiagnostic) obj;
- return diagnostic.equals(that.diagnostic) && kind.equals(that.kind);
+ return severity.equals(that.severity)
+ && kind.equals(that.kind)
+ && args.equals(that.args)
+ && Objects.equals(source, that.source)
+ && position == that.position;
+ }
+
+ public String path() {
+ return source != null && source.path() != null ? source.path() : "<>";
+ }
+
+ public int line() {
+ return position != -1 ? source.lineMap().lineNumber(position) : -1;
+ }
+
+ public int column() {
+ return position != -1 ? source.lineMap().column(position) + 1 : -1;
+ }
+
+ public String message() {
+ return kind.format(args.toArray());
}
}
diff --git a/java/com/google/turbine/diag/TurbineError.java b/java/com/google/turbine/diag/TurbineError.java
index b8b2a54..39244b5 100644
--- a/java/com/google/turbine/diag/TurbineError.java
+++ b/java/com/google/turbine/diag/TurbineError.java
@@ -19,6 +19,7 @@ package com.google.turbine.diag;
import static java.util.stream.Collectors.joining;
import com.google.common.collect.ImmutableList;
+import javax.tools.Diagnostic;
/** A compilation error. */
public class TurbineError extends Error {
@@ -31,6 +32,7 @@ public class TurbineError extends Error {
UNTERMINATED_STRING("unterminated string literal"),
UNTERMINATED_CHARACTER_LITERAL("unterminated char literal"),
UNTERMINATED_EXPRESSION("unterminated expression, expected ';' not found"),
+ INVALID_UNICODE("illegal unicode escape"),
EMPTY_CHARACTER_LITERAL("empty char literal"),
EXPECTED_TOKEN("expected token %s"),
INVALID_LITERAL("invalid literal: %s"),
@@ -42,11 +44,14 @@ public class TurbineError extends Error {
INVALID_ANNOTATION_ARGUMENT("invalid annotation argument"),
CANNOT_RESOLVE("could not resolve %s"),
EXPRESSION_ERROR("could not evaluate constant expression"),
+ OPERAND_TYPE("bad operand type %s"),
CYCLIC_HIERARCHY("cycle in class hierarchy: %s"),
NOT_AN_ANNOTATION("%s is not an annotation"),
NONREPEATABLE_ANNOTATION("%s is not @Repeatable"),
DUPLICATE_DECLARATION("duplicate declaration of %s"),
- BAD_MODULE_INFO("unexpected declaration found in module-info");
+ BAD_MODULE_INFO("unexpected declaration found in module-info"),
+ UNCLOSED_COMMENT("unclosed comment"),
+ PROC("%s");
private final String message;
@@ -80,16 +85,21 @@ public class TurbineError extends Error {
public static TurbineError format(
SourceFile source, int position, ErrorKind kind, Object... args) {
return new TurbineError(
- ImmutableList.of(TurbineDiagnostic.format(source, position, kind, args)));
+ ImmutableList.of(
+ TurbineDiagnostic.format(Diagnostic.Kind.ERROR, source, position, kind, args)));
}
private final ImmutableList<TurbineDiagnostic> diagnostics;
public TurbineError(ImmutableList<TurbineDiagnostic> diagnostics) {
- super(diagnostics.stream().map(d -> d.diagnostic()).collect(joining("\n")));
this.diagnostics = diagnostics;
}
+ @Override
+ public String getMessage() {
+ return diagnostics.stream().map(d -> d.diagnostic()).collect(joining(System.lineSeparator()));
+ }
+
public ImmutableList<TurbineDiagnostic> diagnostics() {
return diagnostics;
}
diff --git a/java/com/google/turbine/diag/TurbineLog.java b/java/com/google/turbine/diag/TurbineLog.java
index fd8fc38..b336e25 100644
--- a/java/com/google/turbine/diag/TurbineLog.java
+++ b/java/com/google/turbine/diag/TurbineLog.java
@@ -18,8 +18,10 @@ package com.google.turbine.diag;
import com.google.common.collect.ImmutableList;
import com.google.turbine.diag.TurbineError.ErrorKind;
+import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
+import javax.tools.Diagnostic;
/** A log that collects diagnostics. */
public class TurbineLog {
@@ -31,11 +33,51 @@ public class TurbineLog {
}
public void maybeThrow() {
- if (!errors.isEmpty()) {
+ if (anyErrors()) {
throw new TurbineError(ImmutableList.copyOf(errors));
}
}
+ private boolean anyErrors() {
+ for (TurbineDiagnostic error : errors) {
+ if (error.severity().equals(Diagnostic.Kind.ERROR)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if a non-deferrable error was raised during annotation processing, i.e. an error
+ * reported by an annotation processor.
+ *
+ * <p>Errors reported by turbine (e.g. missing symbols) are non-fatal, since they may be fixed by
+ * code generated in later processing rounds.
+ */
+ public boolean errorRaised() {
+ for (TurbineDiagnostic error : errors) {
+ if (error.kind().equals(ErrorKind.PROC) && error.severity().equals(Diagnostic.Kind.ERROR)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** 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();
+ }
+ }
+ }
+
+ /** 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));
+ }
+
/** A log for a specific source file. */
public class TurbineLogWithSource {
@@ -45,12 +87,12 @@ public class TurbineLog {
this.source = source;
}
- public void error(ErrorKind kind, Object... args) {
- errors.add(TurbineDiagnostic.format(source, kind, args));
+ public void diagnostic(Diagnostic.Kind severity, int position, ErrorKind kind, Object... args) {
+ errors.add(TurbineDiagnostic.format(severity, source, position, kind, args));
}
public void error(int position, ErrorKind kind, Object... args) {
- errors.add(TurbineDiagnostic.format(source, position, kind, args));
+ diagnostic(Diagnostic.Kind.ERROR, position, kind, args);
}
}
}
diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java
index 16447ab..0f7bb90 100644
--- a/java/com/google/turbine/lower/Lower.java
+++ b/java/com/google/turbine/lower/Lower.java
@@ -21,10 +21,8 @@ import static com.google.turbine.binder.DisambiguateTypeAnnotations.groupRepeate
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import com.google.turbine.binder.bound.AnnotationValue;
import com.google.turbine.binder.bound.EnumConstantValue;
import com.google.turbine.binder.bound.ModuleInfo.ExportInfo;
import com.google.turbine.binder.bound.ModuleInfo.OpenInfo;
@@ -33,6 +31,7 @@ import com.google.turbine.binder.bound.ModuleInfo.RequireInfo;
import com.google.turbine.binder.bound.ModuleInfo.UseInfo;
import com.google.turbine.binder.bound.SourceModuleInfo;
import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
import com.google.turbine.binder.bound.TurbineClassValue;
import com.google.turbine.binder.bound.TypeBoundClass;
import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
@@ -264,10 +263,10 @@ public class Lower {
ImmutableList<AnnotationInfo> annotations = lowerAnnotations(info.annotations());
- ImmutableList<ClassFile.InnerClass> inners = collectInnerClasses(info.source(), sym, info);
-
ImmutableList<TypeAnnotationInfo> typeAnnotations = classTypeAnnotations(info);
+ ImmutableList<ClassFile.InnerClass> inners = collectInnerClasses(info.source(), sym, info);
+
ClassFile classfile =
new ClassFile(
access,
@@ -525,9 +524,8 @@ public class Lower {
return true;
case SOURCE:
return null;
- default:
- throw new AssertionError(retention);
}
+ throw new AssertionError(retention);
}
private ImmutableMap<String, ElementValue> annotationValues(ImmutableMap<String, Const> values) {
@@ -563,12 +561,12 @@ public class Lower {
}
case ANNOTATION:
{
- AnnotationValue annotationValue = (AnnotationValue) value;
+ TurbineAnnotationValue annotationValue = (TurbineAnnotationValue) value;
Boolean visible = isVisible(annotationValue.sym());
if (visible == null) {
visible = true;
}
- return new ElementValue.AnnotationValue(
+ return new ElementValue.ConstTurbineAnnotationValue(
new AnnotationInfo(
sig.objectType(annotationValue.sym()),
visible,
@@ -576,9 +574,8 @@ public class Lower {
}
case PRIMITIVE:
return new ElementValue.ConstValue((Const.Value) value);
- default:
- throw new AssertionError(value.kind());
}
+ throw new AssertionError(value.kind());
}
/** Lower type annotations in a class declaration's signature. */
@@ -656,7 +653,7 @@ public class Lower {
* or on bounds.
*/
private void typeParameterAnnotations(
- Builder<TypeAnnotationInfo> result,
+ ImmutableList.Builder<TypeAnnotationInfo> result,
Iterable<TyVarInfo> typeParameters,
TargetType targetType,
TargetType boundTargetType) {
@@ -675,7 +672,7 @@ public class Lower {
info));
}
int boundIndex = 0;
- for (Type i : p.bound().bounds()) {
+ for (Type i : p.upperBound().bounds()) {
if (boundIndex == 0 && isInterface(i, env)) {
// super class bound index is always 0; interface bounds start at 1
boundIndex++;
@@ -696,7 +693,10 @@ public class Lower {
}
private void lowerTypeAnnotations(
- Builder<TypeAnnotationInfo> result, Type type, TargetType targetType, Target target) {
+ ImmutableList.Builder<TypeAnnotationInfo> result,
+ Type type,
+ TargetType targetType,
+ Target target) {
new LowerTypeAnnotations(result, targetType, target)
.lowerTypeAnnotations(type, TypePath.root());
}
@@ -707,7 +707,7 @@ public class Lower {
private final Target target;
public LowerTypeAnnotations(
- Builder<TypeAnnotationInfo> result, TargetType targetType, Target target) {
+ ImmutableList.Builder<TypeAnnotationInfo> result, TargetType targetType, Target target) {
this.result = result;
this.targetType = targetType;
this.target = target;
@@ -764,8 +764,6 @@ public class Lower {
lowerTypeAnnotations(type.annotations(), path);
lowerTypeAnnotations(type.bound(), path.wild());
break;
- default:
- throw new AssertionError(type.boundKind());
}
}
diff --git a/java/com/google/turbine/lower/LowerSignature.java b/java/com/google/turbine/lower/LowerSignature.java
index fe9b912..13a7b9f 100644
--- a/java/com/google/turbine/lower/LowerSignature.java
+++ b/java/com/google/turbine/lower/LowerSignature.java
@@ -92,17 +92,8 @@ public class LowerSignature {
while (curr.targs().isEmpty() && it.hasNext()) {
curr = it.next();
}
- String pkg;
- String name;
- int idx = curr.sym().binaryName().lastIndexOf('/');
- if (idx == -1) {
- pkg = "";
- name = curr.sym().binaryName();
- } else {
- pkg = curr.sym().binaryName().substring(0, idx);
- name = curr.sym().binaryName().substring(idx + 1);
- }
- classes.add(new Sig.SimpleClassTySig(name, tyArgSigs(curr)));
+ String pkg = curr.sym().packageName();
+ classes.add(new Sig.SimpleClassTySig(curr.sym().simpleName(), tyArgSigs(curr)));
while (it.hasNext()) {
SimpleClassTy outer = curr;
curr = it.next();
@@ -128,9 +119,8 @@ public class LowerSignature {
return new UpperBoundTySig(signature(((Type.WildUpperBoundedTy) ty).bound()));
case LOWER:
return new LowerBoundTySig(signature(((Type.WildLowerBoundedTy) ty).bound()));
- default:
- throw new AssertionError(ty.boundKind());
}
+ throw new AssertionError(ty.boundKind());
}
/**
@@ -284,13 +274,13 @@ public class LowerSignature {
String identifier = sym.name();
Sig.TySig cbound = null;
ImmutableList.Builder<Sig.TySig> ibounds = ImmutableList.builder();
- if (info.bound().bounds().isEmpty()) {
+ if (info.upperBound().bounds().isEmpty()) {
cbound =
new ClassTySig(
"java/lang", ImmutableList.of(new SimpleClassTySig("Object", ImmutableList.of())));
} else {
boolean first = true;
- for (Type bound : info.bound().bounds()) {
+ for (Type bound : info.upperBound().bounds()) {
TySig sig = signature(bound);
if (first) {
if (!isInterface(bound, env)) {
diff --git a/java/com/google/turbine/main/Main.java b/java/com/google/turbine/main/Main.java
index 34421e1..1e60ae6 100644
--- a/java/com/google/turbine/main/Main.java
+++ b/java/com/google/turbine/main/Main.java
@@ -19,15 +19,21 @@ package com.google.turbine.main;
import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION;
import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.hash.Hashing;
import com.google.common.io.MoreFiles;
import com.google.turbine.binder.Binder;
import com.google.turbine.binder.Binder.BindingResult;
+import com.google.turbine.binder.Binder.Statistics;
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.binder.Processing;
+import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.deps.Dependencies;
import com.google.turbine.deps.Transitive;
import com.google.turbine.diag.SourceFile;
@@ -35,9 +41,12 @@ import com.google.turbine.diag.TurbineError;
import com.google.turbine.lower.Lower;
import com.google.turbine.lower.Lower.Lowered;
import com.google.turbine.options.TurbineOptions;
+import com.google.turbine.options.TurbineOptions.ReducedClasspathMode;
import com.google.turbine.options.TurbineOptionsParser;
import com.google.turbine.parse.Parser;
import com.google.turbine.proto.DepsProto;
+import com.google.turbine.proto.ManifestProto;
+import com.google.turbine.proto.ManifestProto.CompilationUnit;
import com.google.turbine.tree.Tree.CompUnit;
import com.google.turbine.zip.Zip;
import java.io.BufferedOutputStream;
@@ -75,7 +84,8 @@ public class Main {
public static void main(String[] args) throws IOException {
boolean ok;
try {
- ok = compile(args);
+ compile(args);
+ ok = true;
} catch (TurbineError | UsageException e) {
System.err.println(e.getMessage());
ok = false;
@@ -86,56 +96,182 @@ public class Main {
System.exit(ok ? 0 : 1);
}
- public static boolean compile(String[] args) throws IOException {
- TurbineOptions options = TurbineOptionsParser.parse(Arrays.asList(args));
- return compile(options);
+ /** The result of a turbine invocation. */
+ @AutoValue
+ public abstract static class Result {
+ /** Returns {@code true} if transitive classpath fallback occurred. */
+ public abstract boolean transitiveClasspathFallback();
+
+ /** The length of the transitive classpath. */
+ public abstract int transitiveClasspathLength();
+
+ /**
+ * The length of the reduced classpath, or {@link #transitiveClasspathLength} if classpath
+ * reduction is not supported.
+ */
+ public abstract int reducedClasspathLength();
+
+ public abstract Statistics processorStatistics();
+
+ static Result create(
+ boolean transitiveClasspathFallback,
+ int transitiveClasspathLength,
+ int reducedClasspathLength,
+ Statistics processorStatistics) {
+ return new AutoValue_Main_Result(
+ transitiveClasspathFallback,
+ transitiveClasspathLength,
+ reducedClasspathLength,
+ processorStatistics);
+ }
+ }
+
+ public static void compile(String[] args) throws IOException {
+ compile(TurbineOptionsParser.parse(Arrays.asList(args)));
}
- public static boolean compile(TurbineOptions options) throws IOException {
+ public static Result compile(TurbineOptions options) throws IOException {
usage(options);
ImmutableList<CompUnit> units = parseAll(options);
ClassPath bootclasspath = bootclasspath(options);
- Collection<String> reducedClasspath =
- Dependencies.reduceClasspath(
- options.classPath(), options.directJars(), options.depsArtifacts());
- ClassPath classpath = ClassPathBinder.bindClasspath(toPaths(reducedClasspath));
+ BindingResult bound;
+ ReducedClasspathMode reducedClasspathMode = options.reducedClasspathMode();
+ if (reducedClasspathMode == ReducedClasspathMode.JAVABUILDER_REDUCED
+ && options.directJars().isEmpty()) {
+ // the compilation doesn't support reduced classpaths
+ // TODO(cushon): make this a usage error, see TODO in Dependencies.reduceClasspath
+ reducedClasspathMode = ReducedClasspathMode.NONE;
+ }
+ boolean transitiveClasspathFallback = false;
+ ImmutableList<String> classPath = options.classPath();
+ int transitiveClasspathLength = classPath.size();
+ int reducedClasspathLength = classPath.size();
+ switch (reducedClasspathMode) {
+ case NONE:
+ bound = bind(options, units, bootclasspath, classPath);
+ break;
+ case BAZEL_FALLBACK:
+ reducedClasspathLength = options.reducedClasspathLength();
+ bound = bind(options, units, bootclasspath, classPath);
+ transitiveClasspathFallback = true;
+ break;
+ case JAVABUILDER_REDUCED:
+ Collection<String> reducedClasspath =
+ Dependencies.reduceClasspath(classPath, options.directJars(), options.depsArtifacts());
+ reducedClasspathLength = reducedClasspath.size();
+ try {
+ bound = bind(options, units, bootclasspath, reducedClasspath);
+ } catch (TurbineError e) {
+ bound = fallback(options, units, bootclasspath, classPath);
+ transitiveClasspathFallback = true;
+ }
+ break;
+ case BAZEL_REDUCED:
+ transitiveClasspathLength = options.fullClasspathLength();
+ try {
+ bound = bind(options, units, bootclasspath, classPath);
+ } catch (TurbineError e) {
+ writeJdepsForFallback(options);
+ return Result.create(
+ /* transitiveClasspathFallback= */ true,
+ /* transitiveClasspathLength= */ transitiveClasspathLength,
+ /* reducedClasspathLength= */ reducedClasspathLength,
+ Statistics.empty());
+ }
+ break;
+ default:
+ throw new AssertionError(reducedClasspathMode);
+ }
- BindingResult bound =
- Binder.bind(units, classpath, bootclasspath, /* moduleVersion=*/ Optional.empty());
+ if (options.outputDeps().isPresent()
+ || options.output().isPresent()
+ || options.outputManifest().isPresent()) {
+ // TODO(cushon): parallelize
+ Lowered lowered = Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv());
- // TODO(cushon): parallelize
- Lowered lowered = Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv());
+ if (options.outputDeps().isPresent()) {
+ DepsProto.Dependencies deps =
+ Dependencies.collectDeps(options.targetLabel(), bootclasspath, bound, lowered);
+ try (OutputStream os =
+ new BufferedOutputStream(
+ Files.newOutputStream(Paths.get(options.outputDeps().get())))) {
+ deps.writeTo(os);
+ }
+ }
+ if (options.output().isPresent()) {
+ Map<String, byte[]> transitive = Transitive.collectDeps(bootclasspath, bound);
+ writeOutput(options, bound.generatedClasses(), lowered.bytes(), transitive);
+ }
+ if (options.outputManifest().isPresent()) {
+ writeManifestProto(options, bound.units(), bound.generatedSources());
+ }
+ }
- Map<String, byte[]> transitive = Transitive.collectDeps(bootclasspath, bound);
+ writeSources(options, bound.generatedSources());
+ writeResources(options, bound.generatedClasses());
+ return Result.create(
+ /* transitiveClasspathFallback= */ transitiveClasspathFallback,
+ /* transitiveClasspathLength= */ transitiveClasspathLength,
+ /* reducedClasspathLength= */ reducedClasspathLength,
+ bound.statistics());
+ }
- if (options.outputDeps().isPresent()) {
- DepsProto.Dependencies deps =
- Dependencies.collectDeps(options.targetLabel(), bootclasspath, bound, lowered);
- try (OutputStream os =
- new BufferedOutputStream(Files.newOutputStream(Paths.get(options.outputDeps().get())))) {
- deps.writeTo(os);
- }
+ // don't inline this; we want it to show up in profiles
+ private static BindingResult fallback(
+ TurbineOptions options,
+ ImmutableList<CompUnit> units,
+ ClassPath bootclasspath,
+ ImmutableList<String> classPath)
+ throws IOException {
+ return bind(options, units, bootclasspath, classPath);
+ }
+
+ /**
+ * Writes a jdeps proto that indiciates to Blaze that the transitive classpath compilation failed,
+ * and it should fall back to the transitive classpath. Used only when {@link
+ * ReducedClasspathMode#BAZEL_REDUCED}.
+ */
+ public static void writeJdepsForFallback(TurbineOptions options) throws IOException {
+ try (OutputStream os =
+ new BufferedOutputStream(Files.newOutputStream(Paths.get(options.outputDeps().get())))) {
+ DepsProto.Dependencies.newBuilder()
+ .setRuleLabel(options.targetLabel().get())
+ .setRequiresReducedClasspathFallback(true)
+ .build()
+ .writeTo(os);
}
+ }
- writeOutput(options, lowered.bytes(), transitive);
- return true;
+ private static BindingResult bind(
+ TurbineOptions options,
+ ImmutableList<CompUnit> units,
+ ClassPath bootclasspath,
+ Collection<String> classpath)
+ throws IOException {
+ return Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(toPaths(classpath)),
+ Processing.initializeProcessors(
+ /* javacopts= */ options.javacOpts(),
+ /* processorPath= */ options.processorPath(),
+ /* processorNames= */ options.processors(),
+ /* builtinProcessors= */ options.builtinProcessors()),
+ bootclasspath,
+ /* moduleVersion=*/ Optional.empty());
}
private static void usage(TurbineOptions options) {
- if (!options.processors().isEmpty()) {
- throw new UsageException("--processors is not supported");
- }
- if (options.sources().isEmpty() && options.sourceJars().isEmpty()) {
- throw new UsageException("no sources were provided");
- }
if (options.help()) {
throw new UsageException();
}
- if (!options.output().isPresent()) {
- throw new UsageException("--output is required");
+ if (!options.output().isPresent()
+ && !options.gensrcOutput().isPresent()
+ && !options.resourceOutput().isPresent()) {
+ throw new UsageException(
+ "at least one of --output, --gensrc_output, or --resource_output is required");
}
}
@@ -188,9 +324,46 @@ public class Main {
return units.build();
}
- /** Write bytecode to the output jar. */
+ /** Writes source files generated by annotation processors. */
+ private static void writeSources(
+ TurbineOptions options, ImmutableMap<String, SourceFile> generatedSources)
+ throws IOException {
+ if (!options.gensrcOutput().isPresent()) {
+ return;
+ }
+ Path path = Paths.get(options.gensrcOutput().get());
+ try (OutputStream os = Files.newOutputStream(path);
+ BufferedOutputStream bos = new BufferedOutputStream(os, BUFFER_SIZE);
+ JarOutputStream jos = new JarOutputStream(bos)) {
+ for (SourceFile source : generatedSources.values()) {
+ addEntry(jos, source.path(), source.source().getBytes(UTF_8));
+ }
+ writeManifest(jos, manifest());
+ }
+ }
+
+ /** Writes resource files generated by annotation processors. */
+ private static void writeResources(
+ TurbineOptions options, ImmutableMap<String, byte[]> generatedResources) throws IOException {
+ if (!options.resourceOutput().isPresent()) {
+ return;
+ }
+ Path path = Paths.get(options.resourceOutput().get());
+ try (OutputStream os = Files.newOutputStream(path);
+ BufferedOutputStream bos = new BufferedOutputStream(os, BUFFER_SIZE);
+ JarOutputStream jos = new JarOutputStream(bos)) {
+ for (Map.Entry<String, byte[]> resource : generatedResources.entrySet()) {
+ addEntry(jos, resource.getKey(), resource.getValue());
+ }
+ }
+ }
+
+ /** Writes bytecode to the output jar. */
private static void writeOutput(
- TurbineOptions options, Map<String, byte[]> lowered, Map<String, byte[]> transitive)
+ TurbineOptions options,
+ Map<String, byte[]> generated,
+ Map<String, byte[]> lowered,
+ Map<String, byte[]> transitive)
throws IOException {
Path path = Paths.get(options.output().get());
try (OutputStream os = Files.newOutputStream(path);
@@ -199,17 +372,42 @@ public class Main {
for (Map.Entry<String, byte[]> entry : lowered.entrySet()) {
addEntry(jos, entry.getKey() + ".class", entry.getValue());
}
+ for (Map.Entry<String, byte[]> entry : generated.entrySet()) {
+ addEntry(jos, entry.getKey(), entry.getValue());
+ }
for (Map.Entry<String, byte[]> entry : transitive.entrySet()) {
addEntry(
jos, ClassPathBinder.TRANSITIVE_PREFIX + entry.getKey() + ".class", entry.getValue());
}
if (options.targetLabel().isPresent()) {
- addEntry(jos, MANIFEST_DIR, new byte[] {});
- addEntry(jos, MANIFEST_NAME, manifestContent(options));
+ writeManifest(jos, manifest(options));
}
}
}
+ private static void writeManifestProto(
+ TurbineOptions options,
+ ImmutableMap<ClassSymbol, SourceTypeBoundClass> units,
+ ImmutableMap<String, SourceFile> generatedSources)
+ throws IOException {
+ ManifestProto.Manifest.Builder manifest = ManifestProto.Manifest.newBuilder();
+ for (Map.Entry<ClassSymbol, SourceTypeBoundClass> e : units.entrySet()) {
+ manifest.addCompilationUnit(
+ CompilationUnit.newBuilder()
+ .setPath(e.getValue().source().path())
+ .setPkg(e.getKey().packageName())
+ .addTopLevel(e.getKey().simpleName())
+ .setGeneratedByAnnotationProcessor(
+ generatedSources.containsKey(e.getValue().source().path()))
+ .build());
+ }
+ try (OutputStream os =
+ new BufferedOutputStream(
+ Files.newOutputStream(Paths.get(options.outputManifest().get())))) {
+ manifest.build().writeTo(os);
+ }
+ }
+
/** Normalize timestamps. */
static final long DEFAULT_TIMESTAMP =
LocalDateTime.of(2010, 1, 1, 0, 0, 0)
@@ -228,7 +426,15 @@ public class Main {
jos.write(bytes);
}
- private static byte[] manifestContent(TurbineOptions turbineOptions) throws IOException {
+ private static void writeManifest(JarOutputStream jos, Manifest manifest) throws IOException {
+ addEntry(jos, MANIFEST_DIR, new byte[] {});
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ manifest.write(out);
+ addEntry(jos, MANIFEST_NAME, out.toByteArray());
+ }
+
+ /** Creates a default {@link Manifest}. */
+ private static Manifest manifest() {
Manifest manifest = new Manifest();
Attributes attributes = manifest.getMainAttributes();
attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
@@ -236,15 +442,20 @@ public class Main {
if (attributes.getValue(createdBy) == null) {
attributes.put(createdBy, "bazel");
}
+ return manifest;
+ }
+
+ /** Creates a {@link Manifest} that includes the target label and injecting rule kind. */
+ private static Manifest manifest(TurbineOptions turbineOptions) {
+ Manifest manifest = manifest();
+ Attributes attributes = manifest.getMainAttributes();
if (turbineOptions.targetLabel().isPresent()) {
attributes.put(TARGET_LABEL, turbineOptions.targetLabel().get());
}
if (turbineOptions.injectingRuleKind().isPresent()) {
attributes.put(INJECTING_RULE_KIND, turbineOptions.injectingRuleKind().get());
}
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- manifest.write(out);
- return out.toByteArray();
+ return manifest;
}
private static ImmutableList<Path> toPaths(Iterable<String> paths) {
diff --git a/java/com/google/turbine/model/Const.java b/java/com/google/turbine/model/Const.java
index 6e41bd2..ed4b072 100644
--- a/java/com/google/turbine/model/Const.java
+++ b/java/com/google/turbine/model/Const.java
@@ -18,6 +18,9 @@ package com.google.turbine.model;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
+import com.google.common.escape.SourceCodeEscapers;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
/**
* Compile-time constant expressions, including literals of primitive or String type, class
@@ -31,6 +34,9 @@ public abstract class Const {
@Override
public abstract boolean equals(Object obj);
+ @Override
+ public abstract String toString();
+
/** The constant kind. */
public abstract Kind kind();
@@ -51,7 +57,7 @@ public abstract class Const {
}
/** Subtypes of {@link Const} for primitive and String literals. */
- public abstract static class Value extends Const {
+ public abstract static class Value extends Const implements AnnotationValue {
public abstract TurbineConstantTypeKind constantTypeKind();
@Override
@@ -110,6 +116,11 @@ public abstract class Const {
}
@Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitBoolean(value, p);
+ }
+
+ @Override
public TurbineConstantTypeKind constantTypeKind() {
return TurbineConstantTypeKind.BOOLEAN;
}
@@ -119,6 +130,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public BooleanValue asBoolean() {
return this;
}
@@ -154,6 +170,11 @@ public abstract class Const {
}
@Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitInt(value, p);
+ }
+
+ @Override
public TurbineConstantTypeKind constantTypeKind() {
return TurbineConstantTypeKind.INT;
}
@@ -163,6 +184,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public IntValue asInteger() {
return this;
}
@@ -227,6 +253,11 @@ public abstract class Const {
}
@Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitLong(value, p);
+ }
+
+ @Override
public TurbineConstantTypeKind constantTypeKind() {
return TurbineConstantTypeKind.LONG;
}
@@ -236,6 +267,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public IntValue asInteger() {
return new IntValue((int) value);
}
@@ -296,7 +332,12 @@ public abstract class Const {
@Override
public String toString() {
- return "'" + value + "'";
+ return "'" + SourceCodeEscapers.javaCharEscaper().escape(String.valueOf(value)) + "'";
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitChar(value, p);
}
@Override
@@ -309,6 +350,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public IntValue asInteger() {
return new IntValue((int) value);
}
@@ -369,10 +415,18 @@ public abstract class Const {
@Override
public String toString() {
+ if (Float.isNaN(value)) {
+ return "0.0f/0.0f";
+ }
return value + "f";
}
@Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitFloat(value, p);
+ }
+
+ @Override
public TurbineConstantTypeKind constantTypeKind() {
return TurbineConstantTypeKind.FLOAT;
}
@@ -382,6 +436,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public IntValue asInteger() {
return new IntValue((int) value);
}
@@ -442,10 +501,24 @@ public abstract class Const {
@Override
public String toString() {
+ if (Double.isNaN(value)) {
+ return "0.0/0.0";
+ }
+ if (value == Double.POSITIVE_INFINITY) {
+ return "1.0/0.0";
+ }
+ if (value == Double.NEGATIVE_INFINITY) {
+ return "-1.0/0.0";
+ }
return String.valueOf(value);
}
@Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitDouble(value, p);
+ }
+
+ @Override
public TurbineConstantTypeKind constantTypeKind() {
return TurbineConstantTypeKind.DOUBLE;
}
@@ -455,6 +528,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public IntValue asInteger() {
return new IntValue((int) value);
}
@@ -515,7 +593,12 @@ public abstract class Const {
@Override
public String toString() {
- return String.format("\"%s\"", value);
+ return '"' + SourceCodeEscapers.javaCharEscaper().escape(value) + '"';
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitString(value, p);
}
@Override
@@ -528,6 +611,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public StringValue asString() {
return this;
}
@@ -557,6 +645,11 @@ public abstract class Const {
}
@Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitShort(value, p);
+ }
+
+ @Override
public TurbineConstantTypeKind constantTypeKind() {
return TurbineConstantTypeKind.SHORT;
}
@@ -566,6 +659,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public IntValue asInteger() {
return new IntValue((int) value);
}
@@ -635,6 +733,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public IntValue asInteger() {
return new IntValue((int) value);
}
@@ -686,7 +789,12 @@ public abstract class Const {
@Override
public String toString() {
- return String.valueOf(value);
+ return String.format("(byte)0x%02x", value);
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitByte(value, p);
}
}
diff --git a/java/com/google/turbine/options/TurbineOptions.java b/java/com/google/turbine/options/TurbineOptions.java
index 0ce0c13..4dcc408 100644
--- a/java/com/google/turbine/options/TurbineOptions.java
+++ b/java/com/google/turbine/options/TurbineOptions.java
@@ -16,104 +16,59 @@
package com.google.turbine.options;
-import static com.google.common.base.Preconditions.checkNotNull;
-
+import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.util.Optional;
import org.checkerframework.checker.nullness.qual.Nullable;
/** Header compilation options. */
-public class TurbineOptions {
-
- private final Optional<String> output;
- private final ImmutableList<String> classPath;
- private final ImmutableSet<String> bootClassPath;
- private final Optional<String> release;
- private final Optional<String> system;
- private final ImmutableList<String> sources;
- private final ImmutableList<String> processorPath;
- private final ImmutableSet<String> processors;
- private final ImmutableList<String> sourceJars;
- private final Optional<String> outputDeps;
- private final ImmutableSet<String> directJars;
- private final Optional<String> targetLabel;
- private final Optional<String> injectingRuleKind;
- private final ImmutableList<String> depsArtifacts;
- private final boolean javacFallback;
- private final boolean help;
- private final ImmutableList<String> javacOpts;
- private final boolean shouldReduceClassPath;
-
- private TurbineOptions(
- @Nullable String output,
- ImmutableList<String> classPath,
- ImmutableSet<String> bootClassPath,
- @Nullable String release,
- @Nullable String system,
- ImmutableList<String> sources,
- ImmutableList<String> processorPath,
- ImmutableSet<String> processors,
- ImmutableList<String> sourceJars,
- @Nullable String outputDeps,
- ImmutableSet<String> directJars,
- @Nullable String targetLabel,
- @Nullable String injectingRuleKind,
- ImmutableList<String> depsArtifacts,
- boolean javacFallback,
- boolean help,
- ImmutableList<String> javacOpts,
- boolean shouldReduceClassPath) {
- this.output = Optional.ofNullable(output);
- this.classPath = checkNotNull(classPath, "classPath must not be null");
- this.bootClassPath = checkNotNull(bootClassPath, "bootClassPath must not be null");
- this.release = Optional.ofNullable(release);
- this.system = Optional.ofNullable(system);
- this.sources = checkNotNull(sources, "sources must not be null");
- this.processorPath = checkNotNull(processorPath, "processorPath must not be null");
- this.processors = checkNotNull(processors, "processors must not be null");
- this.sourceJars = checkNotNull(sourceJars, "sourceJars must not be null");
- this.outputDeps = Optional.ofNullable(outputDeps);
- this.directJars = checkNotNull(directJars, "directJars must not be null");
- this.targetLabel = Optional.ofNullable(targetLabel);
- this.injectingRuleKind = Optional.ofNullable(injectingRuleKind);
- this.depsArtifacts = checkNotNull(depsArtifacts, "depsArtifacts must not be null");
- this.javacFallback = javacFallback;
- this.help = help;
- this.javacOpts = checkNotNull(javacOpts, "javacOpts must not be null");
- this.shouldReduceClassPath = shouldReduceClassPath;
+@AutoValue
+public abstract class TurbineOptions {
+
+ /**
+ * This modes controls how a probablistic Java classpath reduction is used. For each mode except
+ * {@code NONE} a speculative compilation is performed against a subset of the original classpath.
+ * If it fails due to a missing symbol, it is retried with the original transitive classpath.
+ */
+ public enum ReducedClasspathMode {
+ /**
+ * Bazel performs classpath reduction, and invokes turbine passing only the reduced classpath.
+ * If the compilation fails and requires fallback, turbine finishes with exit code 0 but records
+ * that the reduced classpath compilation failed in the jdeps proto.
+ */
+ BAZEL_REDUCED,
+ /**
+ * Indicates that the reduced classpath compilation failed when Bazel previously invoked
+ * turbine, and that we are retrying with a transitive classpath.
+ */
+ BAZEL_FALLBACK,
+ /**
+ * Turbine implements reduced classpaths locally, with in-process fallback if the compilation
+ * fails.
+ */
+ JAVABUILDER_REDUCED,
+ /** Reduced classpaths are disabled, and a full transitive classpath is used. */
+ NONE
}
/** Paths to the Java source files to compile. */
- public ImmutableList<String> sources() {
- return sources;
- }
+ public abstract ImmutableList<String> sources();
/** Paths to classpath artifacts. */
- public ImmutableList<String> classPath() {
- return classPath;
- }
+ public abstract ImmutableList<String> classPath();
/** Paths to compilation bootclasspath artifacts. */
- public ImmutableSet<String> bootClassPath() {
- return bootClassPath;
- }
+ public abstract ImmutableSet<String> bootClassPath();
/** The target platform version. */
- public Optional<String> release() {
- return release;
- }
+ public abstract Optional<String> release();
/** The target platform's system modules. */
- public Optional<String> system() {
- return system;
- }
+ public abstract Optional<String> system();
/** The output jar. */
- @Nullable
- public Optional<String> output() {
- return output;
- }
+ public abstract Optional<String> output();
/**
* The output jar.
@@ -123,214 +78,187 @@ public class TurbineOptions {
@Deprecated
@Nullable
public String outputFile() {
- return output.orElse(null);
+ return output().orElse(null);
}
/** Paths to annotation processor artifacts. */
- public ImmutableList<String> processorPath() {
- return processorPath;
- }
+ public abstract ImmutableList<String> processorPath();
/** Annotation processor class names. */
- public ImmutableSet<String> processors() {
- return processors;
- }
+ public abstract ImmutableSet<String> processors();
+
+ /** Class names of annotation processor that are built in. */
+ public abstract ImmutableSet<String> builtinProcessors();
/** Source jars for compilation. */
- public ImmutableList<String> sourceJars() {
- return sourceJars;
- }
+ public abstract ImmutableList<String> sourceJars();
/** Output jdeps file. */
- public Optional<String> outputDeps() {
- return outputDeps;
- }
+ public abstract Optional<String> outputDeps();
+
+ /** Output manifest file. */
+ public abstract Optional<String> outputManifest();
/** The direct dependencies. */
- public ImmutableSet<String> directJars() {
- return directJars;
- }
+ public abstract ImmutableSet<String> directJars();
/** The label of the target being compiled. */
- public Optional<String> targetLabel() {
- return targetLabel;
- }
+ public abstract Optional<String> targetLabel();
/**
* If present, the name of the rule that injected an aspect that compiles this target.
*
* <p>Note that this rule will have a completely different label to {@link #targetLabel} above.
*/
- public Optional<String> injectingRuleKind() {
- return injectingRuleKind;
- }
+ public abstract Optional<String> injectingRuleKind();
/** The .jdeps artifacts for direct dependencies. */
- public ImmutableList<String> depsArtifacts() {
- return depsArtifacts;
- }
-
- /** Fall back to javac-turbine for error reporting. */
- public boolean javacFallback() {
- return javacFallback;
- }
+ public abstract ImmutableList<String> depsArtifacts();
/** Print usage information. */
- public boolean help() {
- return help;
- }
+ public abstract boolean help();
/** Additional Java compiler flags. */
- public ImmutableList<String> javacOpts() {
- return javacOpts;
- }
+ public abstract ImmutableList<String> javacOpts();
- /** Returns true if the reduced classpath optimization is enabled. */
- public boolean shouldReduceClassPath() {
- return shouldReduceClassPath;
- }
+ /** The reduced classpath optimization mode. */
+ public abstract ReducedClasspathMode reducedClasspathMode();
+
+ /** An optional path for profiling output. */
+ public abstract Optional<String> profile();
+
+ /** An optional path for generated source output. */
+ public abstract Optional<String> gensrcOutput();
+
+ /** An optional path for generated resource output. */
+ public abstract Optional<String> resourceOutput();
+
+ public abstract int fullClasspathLength();
+
+ public abstract int reducedClasspathLength();
public static Builder builder() {
- return new Builder();
+ return new AutoValue_TurbineOptions.Builder()
+ .setSources(ImmutableList.of())
+ .setClassPath(ImmutableList.of())
+ .setBootClassPath(ImmutableList.of())
+ .setProcessorPath(ImmutableList.of())
+ .setProcessors(ImmutableList.of())
+ .setBuiltinProcessors(ImmutableList.of())
+ .setSourceJars(ImmutableList.of())
+ .setDirectJars(ImmutableList.of())
+ .setDepsArtifacts(ImmutableList.of())
+ .addAllJavacOpts(ImmutableList.of())
+ .setReducedClasspathMode(ReducedClasspathMode.NONE)
+ .setHelp(false)
+ .setFullClasspathLength(0)
+ .setReducedClasspathLength(0);
}
/** A {@link Builder} for {@link TurbineOptions}. */
- public static class Builder {
-
- private String output;
- private final ImmutableList.Builder<String> classPath = ImmutableList.builder();
- private final ImmutableList.Builder<String> sources = ImmutableList.builder();
- private final ImmutableList.Builder<String> processorPath = ImmutableList.builder();
- private final ImmutableSet.Builder<String> processors = ImmutableSet.builder();
- private final ImmutableList.Builder<String> sourceJars = ImmutableList.builder();
- private final ImmutableSet.Builder<String> bootClassPath = ImmutableSet.builder();
- @Nullable private String release;
- @Nullable private String system;
- private String outputDeps;
- private final ImmutableSet.Builder<String> directJars = ImmutableSet.builder();
- @Nullable private String targetLabel;
- @Nullable private String injectingRuleKind;
- private final ImmutableList.Builder<String> depsArtifacts = ImmutableList.builder();
- private boolean javacFallback = true;
- private boolean help = false;
- private final ImmutableList.Builder<String> javacOpts = ImmutableList.builder();
- private boolean shouldReduceClassPath = true;
-
- public TurbineOptions build() {
- return new TurbineOptions(
- output,
- classPath.build(),
- bootClassPath.build(),
- release,
- system,
- sources.build(),
- processorPath.build(),
- processors.build(),
- sourceJars.build(),
- outputDeps,
- directJars.build(),
- targetLabel,
- injectingRuleKind,
- depsArtifacts.build(),
- javacFallback,
- help,
- javacOpts.build(),
- shouldReduceClassPath);
+ @AutoValue.Builder
+ 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 Builder setOutput(String output) {
- this.output = output;
- return this;
- }
+ public abstract Builder setClassPath(ImmutableList<String> classPath);
- public Builder addClassPathEntries(Iterable<String> classPath) {
- this.classPath.addAll(classPath);
- return this;
- }
+ public abstract Builder setBootClassPath(ImmutableList<String> bootClassPath);
- public Builder addBootClassPathEntries(Iterable<String> bootClassPath) {
- this.bootClassPath.addAll(bootClassPath);
- return this;
+ /** @deprecated use {@link #setBootClassPath(ImmutableList)} instead. */
+ @Deprecated
+ public Builder addBootClassPathEntries(Iterable<String> sources) {
+ return setBootClassPath(ImmutableList.copyOf(sources));
}
- public Builder setRelease(String release) {
- this.release = release;
- return this;
- }
+ public abstract Builder setRelease(String release);
- public Builder setSystem(String system) {
- this.system = system;
- return this;
- }
+ 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) {
- this.sources.addAll(sources);
- return this;
+ return setSources(ImmutableList.copyOf(sources));
}
+ /** @deprecated use {@link #setProcessorPath(ImmutableList)} instead. */
+ @Deprecated
public Builder addProcessorPathEntries(Iterable<String> processorPath) {
- this.processorPath.addAll(processorPath);
- return this;
+ 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) {
- this.processors.addAll(processors);
- return this;
+ return setProcessors(ImmutableList.copyOf(processors));
}
- // TODO(cushon): remove this when turbine dependency is updated
- public Builder setTempDir(String tempDir) {
- return this;
- }
+ public abstract Builder setProcessors(ImmutableList<String> processors);
- public Builder setSourceJars(Iterable<String> sourceJars) {
- this.sourceJars.addAll(sourceJars);
- return this;
+ /** @deprecated use {@link #setBuiltinProcessors(ImmutableList)} instead. */
+ @Deprecated
+ public Builder addBuiltinProcessors(Iterable<String> builtinProcessors) {
+ return setBuiltinProcessors(ImmutableList.copyOf(builtinProcessors));
}
- public Builder setOutputDeps(String outputDeps) {
- this.outputDeps = outputDeps;
- return this;
- }
+ public abstract Builder setBuiltinProcessors(ImmutableList<String> builtinProcessors);
- public Builder setTargetLabel(String targetLabel) {
- this.targetLabel = targetLabel;
- return this;
- }
+ public abstract Builder setSourceJars(ImmutableList<String> sourceJars);
- public Builder setInjectingRuleKind(String injectingRuleKind) {
- this.injectingRuleKind = injectingRuleKind;
- return this;
- }
+ public abstract Builder setOutputDeps(String outputDeps);
+
+ public abstract Builder setOutputManifest(String outputManifest);
+ public abstract Builder setTargetLabel(String targetLabel);
+
+ public abstract Builder setInjectingRuleKind(String injectingRuleKind);
+
+ /** @deprecated use {@link #setDepsArtifacts(ImmutableList)} instead. */
+ @Deprecated
public Builder addAllDepsArtifacts(Iterable<String> depsArtifacts) {
- this.depsArtifacts.addAll(depsArtifacts);
- return this;
+ return setDepsArtifacts(ImmutableList.copyOf(depsArtifacts));
}
- public Builder setJavacFallback(boolean javacFallback) {
- this.javacFallback = javacFallback;
- return this;
- }
+ public abstract Builder setDepsArtifacts(ImmutableList<String> depsArtifacts);
- public Builder setHelp(boolean help) {
- this.help = help;
- return this;
- }
+ public abstract Builder setHelp(boolean help);
+
+ abstract ImmutableList.Builder<String> javacOptsBuilder();
public Builder addAllJavacOpts(Iterable<String> javacOpts) {
- this.javacOpts.addAll(javacOpts);
+ javacOptsBuilder().addAll(javacOpts);
return this;
}
- public Builder setShouldReduceClassPath(boolean shouldReduceClassPath) {
- this.shouldReduceClassPath = shouldReduceClassPath;
- return this;
- }
+ public abstract Builder setReducedClasspathMode(ReducedClasspathMode reducedClasspathMode);
- public Builder addDirectJars(ImmutableList<String> jars) {
- this.directJars.addAll(jars);
- return this;
+ /** @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);
+
+ public abstract Builder setGensrcOutput(String gensrcOutput);
+
+ public abstract Builder setResourceOutput(String resourceOutput);
+
+ public abstract Builder setFullClasspathLength(int fullClasspathLength);
+
+ public abstract Builder setReducedClasspathLength(int reducedClasspathLength);
+
+ public abstract TurbineOptions build();
}
}
diff --git a/java/com/google/turbine/options/TurbineOptionsParser.java b/java/com/google/turbine/options/TurbineOptionsParser.java
index 55697ca..17d4bf6 100644
--- a/java/com/google/turbine/options/TurbineOptionsParser.java
+++ b/java/com/google/turbine/options/TurbineOptionsParser.java
@@ -22,6 +22,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
+import com.google.turbine.options.TurbineOptions.ReducedClasspathMode;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -69,16 +70,19 @@ public class TurbineOptionsParser {
readOne(argumentDeque);
break;
case "--processors":
- builder.addProcessors(readList(argumentDeque));
+ builder.setProcessors(readList(argumentDeque));
+ break;
+ case "--builtin_processors":
+ builder.setBuiltinProcessors(readList(argumentDeque));
break;
case "--processorpath":
- builder.addProcessorPathEntries(readList(argumentDeque));
+ builder.setProcessorPath(readList(argumentDeque));
break;
case "--classpath":
- builder.addClassPathEntries(readList(argumentDeque));
+ builder.setClassPath(readList(argumentDeque));
break;
case "--bootclasspath":
- builder.addBootClassPathEntries(readList(argumentDeque));
+ builder.setBootClassPath(readList(argumentDeque));
break;
case "--release":
builder.setRelease(readOne(argumentDeque));
@@ -94,16 +98,19 @@ public class TurbineOptionsParser {
break;
}
case "--sources":
- builder.addSources(readList(argumentDeque));
+ builder.setSources(readList(argumentDeque));
break;
case "--output_deps":
builder.setOutputDeps(readOne(argumentDeque));
break;
+ case "--output_manifest_proto":
+ builder.setOutputManifest(readOne(argumentDeque));
+ break;
case "--direct_dependencies":
- builder.addDirectJars(readList(argumentDeque));
+ builder.setDirectJars(readList(argumentDeque));
break;
case "--deps_artifacts":
- builder.addAllDepsArtifacts(readList(argumentDeque));
+ builder.setDepsArtifacts(readList(argumentDeque));
break;
case "--target_label":
builder.setTargetLabel(readOne(argumentDeque));
@@ -112,16 +119,32 @@ public class TurbineOptionsParser {
builder.setInjectingRuleKind(readOne(argumentDeque));
break;
case "--javac_fallback":
- builder.setJavacFallback(true);
- break;
case "--nojavac_fallback":
- builder.setJavacFallback(false);
+ // TODO(cushon): remove this case once blaze stops passing the flag
break;
case "--reduce_classpath":
- builder.setShouldReduceClassPath(true);
+ builder.setReducedClasspathMode(ReducedClasspathMode.JAVABUILDER_REDUCED);
break;
case "--noreduce_classpath":
- builder.setShouldReduceClassPath(false);
+ builder.setReducedClasspathMode(ReducedClasspathMode.NONE);
+ break;
+ case "--reduce_classpath_mode":
+ builder.setReducedClasspathMode(ReducedClasspathMode.valueOf(readOne(argumentDeque)));
+ break;
+ case "--full_classpath_length":
+ builder.setFullClasspathLength(Integer.parseInt(readOne(argumentDeque)));
+ break;
+ case "--reduced_classpath_length":
+ builder.setReducedClasspathLength(Integer.parseInt(readOne(argumentDeque)));
+ break;
+ case "--profile":
+ builder.setProfile(readOne(argumentDeque));
+ break;
+ case "--gensrc_output":
+ builder.setGensrcOutput(readOne(argumentDeque));
+ break;
+ case "--resource_output":
+ builder.setResourceOutput(readOne(argumentDeque));
break;
case "--help":
builder.setHelp(true);
diff --git a/java/com/google/turbine/parse/ConstExpressionParser.java b/java/com/google/turbine/parse/ConstExpressionParser.java
index b3666a4..e49d51c 100644
--- a/java/com/google/turbine/parse/ConstExpressionParser.java
+++ b/java/com/google/turbine/parse/ConstExpressionParser.java
@@ -96,7 +96,7 @@ public class ConstExpressionParser {
}
}
- private Tree.Expression primary(boolean negate) {
+ private Tree.@Nullable Expression primary(boolean negate) {
switch (token) {
case INT_LITERAL:
return finishLiteral(TurbineConstantTypeKind.INT, negate);
@@ -133,8 +133,9 @@ public class ConstExpressionParser {
case LPAREN:
return maybeCast();
case LBRACE:
+ int pos = position;
eat();
- return arrayInitializer();
+ return arrayInitializer(pos);
case IDENT:
return qualIdent();
case BYTE:
@@ -244,10 +245,10 @@ public class ConstExpressionParser {
position = lexer.position();
}
- private Tree.Expression arrayInitializer() {
+ private Tree.Expression arrayInitializer(int pos) {
if (token == Token.RBRACE) {
eat();
- return new Tree.ArrayInit(position, ImmutableList.<Tree.Expression>of());
+ return new Tree.ArrayInit(pos, ImmutableList.<Tree.Expression>of());
}
ImmutableList.Builder<Tree.Expression> exprs = ImmutableList.builder();
@@ -273,11 +274,12 @@ public class ConstExpressionParser {
return null;
}
}
- return new Tree.ArrayInit(position, exprs.build());
+ return new Tree.ArrayInit(pos, exprs.build());
}
/** Finish hex, decimal, octal, and binary integer literals (see JLS 3.10.1). */
private Tree.Expression finishLiteral(TurbineConstantTypeKind kind, boolean negate) {
+ int pos = position;
String text = ident().value();
Const.Value value;
switch (kind) {
@@ -354,7 +356,7 @@ public class ConstExpressionParser {
throw new AssertionError(kind);
}
eat();
- return new Tree.Literal(position, kind, value);
+ return new Tree.Literal(pos, kind, value);
}
static boolean isOctal(String text) {
@@ -561,12 +563,16 @@ public class ConstExpressionParser {
return new Tree.TypeCast(position, new Tree.PrimTy(position, ImmutableList.of(), ty), rhs);
}
- private Tree.AnnoExpr annotation() {
+ private Tree.@Nullable AnnoExpr annotation() {
if (token != Token.AT) {
throw new AssertionError();
}
eat();
- ImmutableList<Ident> name = ((Tree.ConstVarName) qualIdent()).name();
+ Tree.ConstVarName constVarName = (Tree.ConstVarName) qualIdent();
+ if (constVarName == null) {
+ return null;
+ }
+ ImmutableList<Ident> name = constVarName.name();
ImmutableList.Builder<Tree.Expression> args = ImmutableList.builder();
if (token == Token.LPAREN) {
eat();
diff --git a/java/com/google/turbine/parse/IteratorLexer.java b/java/com/google/turbine/parse/IteratorLexer.java
index 823f9bb..1b7a9d0 100644
--- a/java/com/google/turbine/parse/IteratorLexer.java
+++ b/java/com/google/turbine/parse/IteratorLexer.java
@@ -56,7 +56,11 @@ public class IteratorLexer implements Lexer {
@Override
public int position() {
- // TODO(cushon): test expression position EOF handling
- return curr != null ? curr.position : -1;
+ return curr.position;
+ }
+
+ @Override
+ public String javadoc() {
+ return null;
}
}
diff --git a/java/com/google/turbine/parse/Lexer.java b/java/com/google/turbine/parse/Lexer.java
index 2d8422a..992bef1 100644
--- a/java/com/google/turbine/parse/Lexer.java
+++ b/java/com/google/turbine/parse/Lexer.java
@@ -31,4 +31,7 @@ public interface Lexer {
/** Returns the source file for diagnostics. */
SourceFile source();
+
+ /** Returns a saved javadoc comment. */
+ String javadoc();
}
diff --git a/java/com/google/turbine/parse/Parser.java b/java/com/google/turbine/parse/Parser.java
index ff3cc3e..4a090b3 100644
--- a/java/com/google/turbine/parse/Parser.java
+++ b/java/com/google/turbine/parse/Parser.java
@@ -23,6 +23,7 @@ import static com.google.turbine.parse.Token.RPAREN;
import static com.google.turbine.parse.Token.SEMI;
import static com.google.turbine.tree.TurbineModifier.PROTECTED;
import static com.google.turbine.tree.TurbineModifier.PUBLIC;
+import static com.google.turbine.tree.TurbineModifier.VARARGS;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -149,13 +150,14 @@ public class Parser {
break;
case AT:
{
+ int pos = position;
next();
if (token == INTERFACE) {
decls.add(annotationDeclaration(access, annos.build()));
access = EnumSet.noneOf(TurbineModifier.class);
annos = ImmutableList.builder();
} else {
- annos.add(annotation());
+ annos.add(annotation(pos));
}
break;
}
@@ -213,6 +215,7 @@ public class Parser {
}
private TyDecl interfaceDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
+ String javadoc = lexer.javadoc();
eat(Token.INTERFACE);
int pos = position;
Ident name = eatIdent();
@@ -241,10 +244,12 @@ public class Parser {
Optional.<ClassTy>empty(),
interfaces.build(),
members,
- TurbineTyKind.INTERFACE);
+ TurbineTyKind.INTERFACE,
+ javadoc);
}
private TyDecl annotationDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
+ String javadoc = lexer.javadoc();
eat(Token.INTERFACE);
int pos = position;
Ident name = eatIdent();
@@ -260,10 +265,12 @@ public class Parser {
Optional.<ClassTy>empty(),
ImmutableList.<ClassTy>of(),
members,
- TurbineTyKind.ANNOTATION);
+ TurbineTyKind.ANNOTATION,
+ javadoc);
}
private TyDecl enumDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
+ String javadoc = lexer.javadoc();
eat(Token.ENUM);
int pos = position;
Ident name = eatIdent();
@@ -287,7 +294,8 @@ public class Parser {
Optional.<ClassTy>empty(),
interfaces.build(),
members,
- TurbineTyKind.ENUM);
+ TurbineTyKind.ENUM,
+ javadoc);
}
private String moduleName() {
@@ -340,7 +348,7 @@ public class Parser {
return new ModDecl(pos, annos, open, moduleName, directives.build());
}
- private String flatname(char join, ImmutableList<Ident> idents) {
+ private static String flatname(char join, ImmutableList<Ident> idents) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Ident ident : idents) {
@@ -466,7 +474,8 @@ public class Parser {
ImmutableList.<Type>of(),
ImmutableList.of()),
name,
- Optional.<Expression>empty()));
+ Optional.<Expression>empty(),
+ null));
annos = ImmutableList.builder();
break;
}
@@ -478,8 +487,9 @@ public class Parser {
annos = ImmutableList.builder();
break OUTER;
case AT:
+ int pos = position;
next();
- annos.add(annotation());
+ annos.add(annotation(pos));
break;
default:
throw error(token);
@@ -489,6 +499,7 @@ public class Parser {
}
private TyDecl classDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
+ String javadoc = lexer.javadoc();
eat(Token.CLASS);
int pos = position;
Ident name = eatIdent();
@@ -520,7 +531,8 @@ public class Parser {
Optional.ofNullable(xtnds),
interfaces.build(),
members,
- TurbineTyKind.CLASS);
+ TurbineTyKind.CLASS,
+ javadoc);
}
private ImmutableList<Tree> classMembers() {
@@ -580,13 +592,14 @@ public class Parser {
case AT:
{
// TODO(cushon): de-dup with top-level parsing
+ int pos = position;
next();
if (token == INTERFACE) {
acc.add(annotationDeclaration(access, annos.build()));
access = EnumSet.noneOf(TurbineModifier.class);
annos = ImmutableList.builder();
} else {
- annos.add(annotation());
+ annos.add(annotation(pos));
}
break;
}
@@ -769,8 +782,9 @@ public class Parser {
}
ImmutableList.Builder<Anno> builder = ImmutableList.builder();
while (token == Token.AT) {
+ int pos = position;
next();
- builder.add(annotation());
+ builder.add(annotation(pos));
}
return builder.build();
}
@@ -807,6 +821,7 @@ public class Parser {
ImmutableList<Anno> annos,
Type baseTy,
Ident name) {
+ String javadoc = lexer.javadoc();
ImmutableList.Builder<Tree> result = ImmutableList.builder();
VariableInitializerParser initializerParser = new VariableInitializerParser(token, lexer);
List<List<SavedToken>> bits = initializerParser.parseInitializers();
@@ -831,7 +846,7 @@ public class Parser {
if (init != null && init.kind() == Tree.Kind.ARRAY_INIT) {
init = null;
}
- result.add(new VarDecl(pos, access, annos, ty, name, Optional.ofNullable(init)));
+ result.add(new VarDecl(pos, access, annos, ty, name, Optional.ofNullable(init), javadoc));
}
if (token != SEMI) {
throw TurbineError.format(lexer.source(), expressionStart, ErrorKind.UNTERMINATED_EXPRESSION);
@@ -847,6 +862,7 @@ public class Parser {
ImmutableList<TyParam> typaram,
Type result,
Ident name) {
+ String javadoc = lexer.javadoc();
eat(Token.LPAREN);
ImmutableList.Builder<VarDecl> formals = ImmutableList.builder();
formalParams(formals, access);
@@ -873,8 +889,9 @@ public class Parser {
Tree expr = cparser.expression();
token = cparser.token;
if (expr == null && token == Token.AT) {
+ int annoPos = position;
next();
- expr = annotation();
+ expr = annotation(annoPos);
}
if (expr == null) {
throw error(token);
@@ -898,7 +915,8 @@ public class Parser {
name,
formals.build(),
exceptions.build(),
- Optional.ofNullable(defaultValue));
+ Optional.ofNullable(defaultValue),
+ javadoc);
}
/**
@@ -928,6 +946,10 @@ public class Parser {
if (extra.isEmpty()) {
return type;
}
+ if (type == null) {
+ // trailing dims without a type, e.g. for a constructor declaration
+ throw error(token);
+ }
if (type.kind() == Kind.ARR_TY) {
ArrTy arrTy = (ArrTy) type;
return new ArrTy(arrTy.position(), arrTy.annos(), extraDims(arrTy.elem(), extra));
@@ -962,13 +984,29 @@ public class Parser {
private VarDecl formalParam() {
ImmutableList.Builder<Anno> annos = ImmutableList.builder();
EnumSet<TurbineModifier> access = modifiersAndAnnotations(annos);
- Type ty = referenceType(ImmutableList.of());
+ Type ty = referenceTypeWithoutDims(ImmutableList.of());
ImmutableList<Anno> typeAnnos = maybeAnnos();
- if (maybe(Token.ELLIPSIS)) {
- access.add(TurbineModifier.VARARGS);
- ty = new ArrTy(position, typeAnnos, ty);
- } else {
- ty = maybeDims(typeAnnos, ty);
+ OUTER:
+ while (true) {
+ switch (token) {
+ case LBRACK:
+ next();
+ eat(Token.RBRACK);
+ ty = new ArrTy(position, typeAnnos, ty);
+ typeAnnos = maybeAnnos();
+ break;
+ case ELLIPSIS:
+ next();
+ access.add(VARARGS);
+ ty = new ArrTy(position, typeAnnos, ty);
+ typeAnnos = ImmutableList.of();
+ break OUTER;
+ default:
+ break OUTER;
+ }
+ }
+ if (!typeAnnos.isEmpty()) {
+ throw error(token);
}
// the parameter name is `this` for receiver parameters, and a qualified this expression
// for inner classes
@@ -979,7 +1017,8 @@ public class Parser {
name = identOrThis();
}
ty = extraDims(ty);
- return new VarDecl(position, access, annos.build(), ty, name, Optional.<Expression>empty());
+ return new VarDecl(
+ position, access, annos.build(), ty, name, Optional.<Expression>empty(), null);
}
private Ident identOrThis() {
@@ -1041,13 +1080,14 @@ public class Parser {
OUTER:
while (true) {
ImmutableList<Anno> annotations = maybeAnnos();
+ int pos = position;
Ident name = eatIdent();
ImmutableList<Tree> bounds = ImmutableList.of();
if (token == Token.EXTENDS) {
next();
bounds = tybounds();
}
- acc.add(new TyParam(position, name, bounds, annotations));
+ acc.add(new TyParam(pos, name, bounds, annotations));
switch (token) {
case COMMA:
eat(Token.COMMA);
@@ -1166,49 +1206,42 @@ public class Parser {
return acc.build();
}
- private Type referenceType(ImmutableList<Anno> typeAnnos) {
- Type ty;
+ private Type referenceTypeWithoutDims(ImmutableList<Anno> typeAnnos) {
switch (token) {
case IDENT:
- ty = classty(null, typeAnnos);
- break;
+ return classty(null, typeAnnos);
case BOOLEAN:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.BOOLEAN);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.BOOLEAN);
case BYTE:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.BYTE);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.BYTE);
case SHORT:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.SHORT);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.SHORT);
case INT:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.INT);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.INT);
case LONG:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.LONG);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.LONG);
case CHAR:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.CHAR);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.CHAR);
case DOUBLE:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.DOUBLE);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.DOUBLE);
case FLOAT:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.FLOAT);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.FLOAT);
default:
throw error(token);
}
- ty = maybeDims(maybeAnnos(), ty);
- return ty;
+ }
+
+ private Type referenceType(ImmutableList<Anno> typeAnnos) {
+ Type ty = referenceTypeWithoutDims(typeAnnos);
+ return maybeDims(maybeAnnos(), ty);
}
private Type maybeDims(ImmutableList<Anno> typeAnnos, Type ty) {
@@ -1269,8 +1302,9 @@ public class Parser {
access.add(TurbineModifier.STRICTFP);
break;
case AT:
+ int pos = position;
next();
- annos.add(annotation());
+ annos.add(annotation(pos));
break;
default:
return access;
@@ -1318,8 +1352,7 @@ public class Parser {
return name.build();
}
- private Anno annotation() {
- int pos = position;
+ private Anno annotation(int pos) {
ImmutableList<Ident> name = qualIdent();
ImmutableList.Builder<Expression> args = ImmutableList.builder();
diff --git a/java/com/google/turbine/parse/StreamLexer.java b/java/com/google/turbine/parse/StreamLexer.java
index 74b0ce8..2e20c26 100644
--- a/java/com/google/turbine/parse/StreamLexer.java
+++ b/java/com/google/turbine/parse/StreamLexer.java
@@ -16,9 +16,9 @@
package com.google.turbine.parse;
+import static com.google.common.base.Verify.verify;
import static com.google.turbine.parse.UnicodeEscapePreprocessor.ASCII_SUB;
-import com.google.common.base.Verify;
import com.google.turbine.diag.SourceFile;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.diag.TurbineError.ErrorKind;
@@ -40,6 +40,9 @@ public class StreamLexer implements Lexer {
/** The value of the current string or character literal token. */
private String value = null;
+ /** A saved javadoc comment. */
+ private String javadoc = null;
+
public StreamLexer(UnicodeEscapePreprocessor reader) {
this.reader = reader;
eat();
@@ -62,6 +65,17 @@ public class StreamLexer implements Lexer {
}
@Override
+ public String javadoc() {
+ String result = javadoc;
+ javadoc = null;
+ if (result == null) {
+ return null;
+ }
+ verify(result.endsWith("*/"), result);
+ return result.substring(0, result.length() - "*/".length());
+ }
+
+ @Override
public String stringValue() {
if (value != null) {
return value;
@@ -111,30 +125,51 @@ public class StreamLexer implements Lexer {
}
eat();
break;
+ default: // fall out
}
}
case '*':
+ eat();
boolean sawStar = false;
- while (true) {
+ boolean isJavadoc = false;
+ if (ch == '*') {
eat();
+ // handle empty non-javadoc comments: `/**/`
+ if (ch == '/') {
+ eat();
+ continue OUTER;
+ }
+ isJavadoc = true;
+ readFrom();
+ }
+ while (true) {
switch (ch) {
case '*':
+ eat();
sawStar = true;
break;
case '/':
+ eat();
if (sawStar) {
- eat();
+ if (isJavadoc) {
+ // Save the comment, excluding the leading `/**` and including
+ // the trailing `/*`. The comment is trimmed and normalized later.
+ javadoc = stringValue();
+ }
continue OUTER;
}
sawStar = false;
break;
case ASCII_SUB:
if (reader.done()) {
- return Token.EOF;
+ throw TurbineError.format(
+ reader.source(), position, ErrorKind.UNCLOSED_COMMENT);
}
eat();
+ sawStar = false;
break;
default:
+ eat();
sawStar = false;
break;
}
@@ -205,7 +240,9 @@ public class StreamLexer implements Lexer {
return identifier();
case ASCII_SUB:
- Verify.verify(reader.done());
+ if (!reader.done()) {
+ throw error(ErrorKind.UNEXPECTED_EOF);
+ }
return Token.EOF;
case '-':
@@ -512,6 +549,7 @@ public class StreamLexer implements Lexer {
eat();
signedInteger();
break;
+ default: // fall out
}
return floatTypeSuffix();
}
@@ -526,6 +564,7 @@ public class StreamLexer implements Lexer {
eat();
signedInteger();
break;
+ default: // fall out
}
return floatTypeSuffix();
}
@@ -989,7 +1028,7 @@ public class StreamLexer implements Lexer {
return makeIdent(stringValue());
}
- private Token makeIdent(String s) {
+ private static Token makeIdent(String s) {
switch (s) {
case "abstract":
return Token.ABSTRACT;
diff --git a/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java b/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java
index 58f129d..3f38561 100644
--- a/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java
+++ b/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java
@@ -16,7 +16,10 @@
package com.google.turbine.parse;
+import com.google.errorprone.annotations.CheckReturnValue;
import com.google.turbine.diag.SourceFile;
+import com.google.turbine.diag.TurbineError;
+import com.google.turbine.diag.TurbineError.ErrorKind;
/** Preprocesses Unicode escape characters in Java source code, as described in JLS §3.3. */
public class UnicodeEscapePreprocessor {
@@ -85,7 +88,7 @@ public class UnicodeEscapePreprocessor {
}
/** Consumes a hex digit. */
- private static int hexDigit(char d) {
+ private int hexDigit(char d) {
switch (d) {
case '0':
case '1':
@@ -113,9 +116,9 @@ public class UnicodeEscapePreprocessor {
case 'f':
return ((d - 'a') + 10);
case ASCII_SUB:
- throw new AssertionError("unexpected end of input");
+ throw error(ErrorKind.UNEXPECTED_EOF);
default:
- throw new AssertionError(String.format("unexpected hex digit: 0x%x", (int) d));
+ throw error(ErrorKind.INVALID_UNICODE);
}
}
@@ -134,4 +137,10 @@ public class UnicodeEscapePreprocessor {
public SourceFile source() {
return source;
}
+
+ @CheckReturnValue
+ private TurbineError error(ErrorKind kind, Object... args) {
+ throw TurbineError.format(
+ source(), Math.min(position(), source().source().length() - 1), kind, args);
+ }
}
diff --git a/java/com/google/turbine/parse/VariableInitializerParser.java b/java/com/google/turbine/parse/VariableInitializerParser.java
index a39e9e8..4ad9272 100644
--- a/java/com/google/turbine/parse/VariableInitializerParser.java
+++ b/java/com/google/turbine/parse/VariableInitializerParser.java
@@ -149,8 +149,6 @@ public class VariableInitializerParser {
case START:
case TYPE:
break OUTER;
- default:
- break;
}
save();
next();
@@ -163,8 +161,6 @@ public class VariableInitializerParser {
case TYPE:
commas.add(tokens.size());
break;
- default:
- break;
}
break;
case DOT:
@@ -209,14 +205,14 @@ public class VariableInitializerParser {
result.add(
ImmutableList.<SavedToken>builder()
.addAll(tokens.subList(start, idx - 1))
- .add(new SavedToken(Token.EOF, null, -1))
+ .add(new SavedToken(Token.EOF, null, tokens.get(idx - 1).position))
.build());
start = idx;
}
result.add(
ImmutableList.<SavedToken>builder()
.addAll(tokens.subList(start, tokens.size()))
- .add(new SavedToken(Token.EOF, null, -1))
+ .add(new SavedToken(Token.EOF, null, lexer.position()))
.build());
return result;
}
@@ -284,6 +280,9 @@ public class VariableInitializerParser {
int lastType = -1;
int lastComma = -1;
for (int i = 0; i < many; i++) {
+ if (ltIndices.isEmpty()) {
+ throw error(ErrorKind.UNEXPECTED_TOKEN, ">");
+ }
lastType = ltIndices.removeLast();
lastComma = commaIndices.removeLast();
}
diff --git a/java/com/google/turbine/processing/ClassHierarchy.java b/java/com/google/turbine/processing/ClassHierarchy.java
new file mode 100644
index 0000000..50c929c
--- /dev/null
+++ b/java/com/google/turbine/processing/ClassHierarchy.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2019 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.processing;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.turbine.binder.bound.TypeBoundClass;
+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.diag.TurbineError;
+import com.google.turbine.diag.TurbineError.ErrorKind;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.TyKind;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A representation of the class hierarchy, with logic for performing search between subtypes and
+ * their supertypes.
+ */
+public class ClassHierarchy {
+
+ private final Map<ClassSymbol, HierarchyNode> cache = new HashMap<>();
+ private Env<ClassSymbol, ? extends TypeBoundClass> env;
+
+ ClassHierarchy(Env<ClassSymbol, ? extends TypeBoundClass> env) {
+ this.env = env;
+ }
+
+ public void round(CompoundEnv<ClassSymbol, TypeBoundClass> env) {
+ cache.clear();
+ this.env = env;
+ }
+
+ /** A linked list between two types in the hierarchy. */
+ private static class PathNode {
+
+ /** The class for this node. */
+ final ClassTy type;
+
+ /** The node corresponding to a direct super-type of this class, or {@code null}. */
+ final PathNode ancestor;
+
+ PathNode(ClassTy type, PathNode ancestor) {
+ this.type = type;
+ this.ancestor = ancestor;
+ }
+ }
+
+ /**
+ * A node in the type hierarchy, corresponding to a class symbol U. For each type V in the
+ * transitive supertype hierarchy of U, we save a mapping from the class symbol for V to the path
+ * from U to V in the type hierarchy.
+ */
+ private class HierarchyNode {
+
+ private final ClassSymbol sym;
+ private final Map<ClassSymbol, PathNode> ancestors = new LinkedHashMap<>();
+
+ HierarchyNode(ClassSymbol sym) {
+ this.sym = sym;
+ }
+
+ /** Adds a child (direct supertype) of this node. */
+ private void add(Type type) {
+ if (type.tyKind() != TyKind.CLASS_TY) {
+ // ignore any erroneous types that ended up in the hierarchy
+ return;
+ }
+ ClassTy classTy = (ClassTy) type;
+ HierarchyNode child = get(classTy.sym());
+ // add a new edge to the direct supertype
+ PathNode existing = ancestors.putIfAbsent(child.sym, new PathNode(classTy, null));
+ if (existing != null) {
+ // if this child has already been added don't re-process its ancestors
+ return;
+ }
+ // copy and extend edges for the transitive supertypes
+ for (Map.Entry<ClassSymbol, PathNode> n : child.ancestors.entrySet()) {
+ ancestors.putIfAbsent(n.getKey(), new PathNode(classTy, n.getValue()));
+ }
+ }
+
+ /** The supertype closure of this node. */
+ private Set<ClassSymbol> closure() {
+ return ancestors.keySet();
+ }
+ }
+
+ private HierarchyNode compute(ClassSymbol sym) {
+ HierarchyNode node = new HierarchyNode(sym);
+ TypeBoundClass info = env.get(sym);
+ if (info == null) {
+ throw TurbineError.format(/* source= */ null, ErrorKind.SYMBOL_NOT_FOUND, sym);
+ }
+ if (info.superClassType() != null) {
+ node.add(info.superClassType());
+ }
+ for (Type type : info.interfaceTypes()) {
+ node.add(type);
+ }
+ return node;
+ }
+
+ private HierarchyNode get(ClassSymbol sym) {
+ // dont use computeIfAbsent, to support re-entrant lookups
+ HierarchyNode result = cache.get(sym);
+ if (result != null) {
+ return result;
+ }
+ result = compute(sym);
+ cache.put(sym, result);
+ return result;
+ }
+
+ /**
+ * Returns a list of types on the path between the given type {@code t} and a transitive
+ * superclass {@code s}, or an empty list if no such path exists.
+ */
+ ImmutableList<ClassTy> search(Type t, ClassSymbol s) {
+ if (t.tyKind() != TyKind.CLASS_TY) {
+ return ImmutableList.of();
+ }
+ ClassTy classTy = (ClassTy) t;
+ if (classTy.sym().equals(s)) {
+ return ImmutableList.of(classTy);
+ }
+ HierarchyNode node = get(classTy.sym());
+ PathNode path = node.ancestors.get(s);
+ if (path == null) {
+ return ImmutableList.of();
+ }
+ ImmutableList.Builder<ClassTy> result = ImmutableList.builder();
+ result.add(classTy);
+ while (path != null) {
+ result.add(path.type);
+ path = path.ancestor;
+ }
+ return result.build().reverse();
+ }
+
+ /**
+ * Returns all classes in the transitive supertype hierarchy of the given class, including the
+ * class itself.
+ *
+ * <p>The iteration order of the results is undefined, and in particular no guarantees are made
+ * about the ordering of sub-types and super-types.
+ */
+ public Iterable<ClassSymbol> transitiveSupertypes(ClassSymbol s) {
+ return Iterables.concat(ImmutableList.of(s), get(s).closure());
+ }
+}
diff --git a/java/com/google/turbine/processing/ModelFactory.java b/java/com/google/turbine/processing/ModelFactory.java
new file mode 100644
index 0000000..9b782cd
--- /dev/null
+++ b/java/com/google/turbine/processing/ModelFactory.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Splitter;
+import com.google.common.base.Supplier;
+import com.google.common.base.Verify;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
+import com.google.turbine.binder.env.CompoundEnv;
+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.TopLevelIndex;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.PackageSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.model.TurbineConstantTypeKind;
+import com.google.turbine.processing.TurbineElement.TurbineExecutableElement;
+import com.google.turbine.processing.TurbineElement.TurbineFieldElement;
+import com.google.turbine.processing.TurbineElement.TurbineNoTypeElement;
+import com.google.turbine.processing.TurbineElement.TurbinePackageElement;
+import com.google.turbine.processing.TurbineElement.TurbineParameterElement;
+import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
+import com.google.turbine.processing.TurbineElement.TurbineTypeParameterElement;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineArrayType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineDeclaredType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineErrorType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineExecutableType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineIntersectionType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineNoType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineNullType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbinePackageType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbinePrimitiveType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineTypeVariable;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineVoidType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineWildcardType;
+import com.google.turbine.tree.Tree.Ident;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.ArrayTy;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.ErrorTy;
+import com.google.turbine.type.Type.IntersectionTy;
+import com.google.turbine.type.Type.MethodTy;
+import com.google.turbine.type.Type.PrimTy;
+import com.google.turbine.type.Type.TyVar;
+import com.google.turbine.type.Type.WildTy;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.NullType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A factoy for turbine's implementations of {@link Element} and {@link TypeMirror}.
+ *
+ * <p>The model provided by those interfaces contains cycles between types and elements, e.g. {@link
+ * Element#asType} and {@link javax.lang.model.type.DeclaredType#asElement}. Turbine's internal
+ * model uses an immutable representation of classes and types which cannot represent cycles
+ * directly. Instead, the implementations in {@link TurbineElement} and {@link TurbineTypeMirror}
+ * maintain a reference to this class, and use it to lazily construct edges in the type and element
+ * graph.
+ */
+public class ModelFactory {
+
+ public Env<ClassSymbol, ? extends TypeBoundClass> env;
+
+ private final AtomicInteger round = new AtomicInteger(0);
+
+ public void round(CompoundEnv<ClassSymbol, TypeBoundClass> env, TopLevelIndex tli) {
+ this.env = env;
+ this.tli = tli;
+ round.getAndIncrement();
+ cha.round(env);
+ }
+
+ private final HashMap<Type, TurbineTypeMirror> typeCache = new HashMap<>();
+
+ private final Map<FieldSymbol, TurbineFieldElement> fieldCache = new HashMap<>();
+ private final Map<MethodSymbol, TurbineExecutableElement> methodCache = new HashMap<>();
+ private final Map<ClassSymbol, TurbineTypeElement> classCache = new HashMap<>();
+ private final Map<ParamSymbol, TurbineParameterElement> paramCache = new HashMap<>();
+ private final Map<TyVarSymbol, TurbineTypeParameterElement> tyParamCache = new HashMap<>();
+ private final Map<PackageSymbol, TurbinePackageElement> packageCache = new HashMap<>();
+
+ private final HashMap<CharSequence, ClassSymbol> inferSymbolCache = new HashMap<>();
+
+ private final ClassHierarchy cha;
+ private final ClassLoader processorLoader;
+
+ private TopLevelIndex tli;
+
+ public ModelFactory(
+ Env<ClassSymbol, ? extends TypeBoundClass> env,
+ ClassLoader processorLoader,
+ TopLevelIndex tli) {
+ this.env = requireNonNull(env);
+ this.cha = new ClassHierarchy(env);
+ this.processorLoader = requireNonNull(processorLoader);
+ this.tli = requireNonNull(tli);
+ }
+
+ TypeMirror asTypeMirror(Type type) {
+ return typeCache.computeIfAbsent(type, this::createTypeMirror);
+ }
+
+ /**
+ * Returns a supplier that memoizes the result of the input supplier.
+ *
+ * <p>It ensures that the results are invalidated after each annotation processing round, to
+ * support computations that depend on information in the current round and which might change in
+ * future, e.g. as additional types are generated.
+ */
+ <T> Supplier<T> memoize(Supplier<T> s) {
+ return new Supplier<T>() {
+ T v;
+ int initializedInRound = -1;
+
+ @Override
+ public T get() {
+ int r = round.get();
+ if (initializedInRound != r) {
+ v = s.get();
+ initializedInRound = r;
+ }
+ return v;
+ }
+ };
+ }
+
+ /** Creates a {@link TurbineTypeMirror} backed by a {@link Type}. */
+ private TurbineTypeMirror createTypeMirror(Type type) {
+ switch (type.tyKind()) {
+ case PRIM_TY:
+ if (((PrimTy) type).primkind() == TurbineConstantTypeKind.STRING) {
+ return new TurbineDeclaredType(this, ClassTy.STRING);
+ }
+ return new TurbinePrimitiveType(this, (PrimTy) type);
+ case CLASS_TY:
+ return new TurbineDeclaredType(this, (ClassTy) type);
+ case ARRAY_TY:
+ return new TurbineArrayType(this, (ArrayTy) type);
+ case VOID_TY:
+ return new TurbineVoidType(this);
+ case WILD_TY:
+ return new TurbineWildcardType(this, (WildTy) type);
+ case TY_VAR:
+ return new TurbineTypeVariable(this, (TyVar) type);
+ case INTERSECTION_TY:
+ IntersectionTy intersectionTy = (IntersectionTy) type;
+ switch (intersectionTy.bounds().size()) {
+ case 0:
+ return createTypeMirror(ClassTy.OBJECT);
+ case 1:
+ return createTypeMirror(getOnlyElement(intersectionTy.bounds()));
+ default:
+ return new TurbineIntersectionType(this, intersectionTy);
+ }
+ case NONE_TY:
+ return new TurbineNoType(this);
+ case METHOD_TY:
+ return new TurbineExecutableType(this, (MethodTy) type);
+ case ERROR_TY:
+ return new TurbineErrorType(this, (ErrorTy) type);
+ }
+ throw new AssertionError(type.tyKind());
+ }
+
+ /** Creates a list of {@link TurbineTypeMirror}s backed by the given {@link Type}s. */
+ ImmutableList<TypeMirror> asTypeMirrors(Iterable<? extends Type> types) {
+ ImmutableList.Builder<TypeMirror> result = ImmutableList.builder();
+ for (Type type : types) {
+ result.add(asTypeMirror(type));
+ }
+ return result.build();
+ }
+
+ NoType noType() {
+ return (NoType) asTypeMirror(Type.NONE);
+ }
+
+ NoType packageType(PackageSymbol symbol) {
+ return new TurbinePackageType(this, symbol);
+ }
+
+ public NullType nullType() {
+ return new TurbineNullType(this);
+ }
+
+ /** Creates an {@link Element} backed by the given {@link Symbol}. */
+ Element element(Symbol symbol) {
+ switch (symbol.symKind()) {
+ case CLASS:
+ return typeElement((ClassSymbol) symbol);
+ case TY_PARAM:
+ return typeParameterElement((TyVarSymbol) symbol);
+ case METHOD:
+ return executableElement((MethodSymbol) symbol);
+ case FIELD:
+ return fieldElement((FieldSymbol) symbol);
+ case PARAMETER:
+ return parameterElement((ParamSymbol) symbol);
+ case PACKAGE:
+ return packageElement((PackageSymbol) symbol);
+ case MODULE:
+ break;
+ }
+ throw new AssertionError(symbol.symKind());
+ }
+
+ Element noElement(String name) {
+ return new TurbineNoTypeElement(this, name);
+ }
+
+ TurbineFieldElement fieldElement(FieldSymbol symbol) {
+ return fieldCache.computeIfAbsent(symbol, k -> new TurbineFieldElement(this, symbol));
+ }
+
+ TurbineExecutableElement executableElement(MethodSymbol symbol) {
+ return methodCache.computeIfAbsent(symbol, k -> new TurbineExecutableElement(this, symbol));
+ }
+
+ public TurbineTypeElement typeElement(ClassSymbol symbol) {
+ Verify.verify(!symbol.simpleName().equals("package-info"), "%s", symbol);
+ return classCache.computeIfAbsent(symbol, k -> new TurbineTypeElement(this, symbol));
+ }
+
+ TurbinePackageElement packageElement(PackageSymbol symbol) {
+ return packageCache.computeIfAbsent(symbol, k -> new TurbinePackageElement(this, symbol));
+ }
+
+ VariableElement parameterElement(ParamSymbol sym) {
+ return paramCache.computeIfAbsent(sym, k -> new TurbineParameterElement(this, sym));
+ }
+
+ TurbineTypeParameterElement typeParameterElement(TyVarSymbol sym) {
+ return tyParamCache.computeIfAbsent(sym, k -> new TurbineTypeParameterElement(this, sym));
+ }
+
+ ImmutableSet<Element> elements(ImmutableSet<? extends Symbol> symbols) {
+ ImmutableSet.Builder<Element> result = ImmutableSet.builder();
+ for (Symbol symbol : symbols) {
+ result.add(element(symbol));
+ }
+ return result.build();
+ }
+
+ public ClassSymbol inferSymbol(CharSequence name) {
+ return inferSymbolCache.computeIfAbsent(name, key -> inferSymbolImpl(name));
+ }
+
+ private ClassSymbol inferSymbolImpl(CharSequence name) {
+ LookupResult lookup = tli.scope().lookup(new LookupKey(asIdents(name)));
+ if (lookup == null) {
+ return null;
+ }
+ ClassSymbol sym = (ClassSymbol) lookup.sym();
+ for (Ident bit : lookup.remaining()) {
+ sym = getSymbol(sym).children().get(bit.value());
+ if (sym == null) {
+ return null;
+ }
+ }
+ return sym;
+ }
+
+ private static ImmutableList<Ident> asIdents(CharSequence name) {
+ ImmutableList.Builder<Ident> result = ImmutableList.builder();
+ for (String bit : Splitter.on('.').split(name)) {
+ result.add(new Ident(-1, bit));
+ }
+ return result.build();
+ }
+
+ /**
+ * Returns the {@link TypeBoundClass} for the given {@link ClassSymbol} from the current
+ * environment.
+ */
+ TypeBoundClass getSymbol(ClassSymbol sym) {
+ return env.get(sym);
+ }
+
+ MethodInfo getMethodInfo(MethodSymbol method) {
+ TypeBoundClass info = getSymbol(method.owner());
+ for (MethodInfo m : info.methods()) {
+ if (m.sym().equals(method)) {
+ return m;
+ }
+ }
+ return null;
+ }
+
+ ParamInfo getParamInfo(ParamSymbol sym) {
+ MethodInfo info = getMethodInfo(sym.owner());
+ for (ParamInfo p : info.parameters()) {
+ if (p.sym().equals(sym)) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ FieldInfo getFieldInfo(FieldSymbol symbol) {
+ TypeBoundClass info = getSymbol(symbol.owner());
+ requireNonNull(info, symbol.owner().toString());
+ for (FieldInfo field : info.fields()) {
+ if (field.sym().equals(symbol)) {
+ return field;
+ }
+ }
+ throw new AssertionError(symbol);
+ }
+
+ TyVarInfo getTyVarInfo(TyVarSymbol tyVar) {
+ Symbol owner = tyVar.owner();
+ Verify.verifyNotNull(owner); // TODO(cushon): capture variables
+ ImmutableMap<TyVarSymbol, TyVarInfo> tyParams;
+ switch (owner.symKind()) {
+ case METHOD:
+ tyParams = getMethodInfo((MethodSymbol) owner).tyParams();
+ break;
+ case CLASS:
+ tyParams = getSymbol((ClassSymbol) owner).typeParameterTypes();
+ break;
+ default:
+ throw new AssertionError(owner.symKind());
+ }
+ return tyParams.get(tyVar);
+ }
+
+ static ClassSymbol enclosingClass(Symbol sym) {
+ switch (sym.symKind()) {
+ case CLASS:
+ return (ClassSymbol) sym;
+ case TY_PARAM:
+ return enclosingClass(((TyVarSymbol) sym).owner());
+ case METHOD:
+ return ((MethodSymbol) sym).owner();
+ case FIELD:
+ return ((FieldSymbol) sym).owner();
+ case PARAMETER:
+ return ((ParamSymbol) sym).owner().owner();
+ case PACKAGE:
+ case MODULE:
+ throw new IllegalArgumentException(sym.toString());
+ }
+ throw new AssertionError(sym.symKind());
+ }
+
+ ClassHierarchy cha() {
+ return cha;
+ }
+
+ ClassLoader processorLoader() {
+ return processorLoader;
+ }
+
+ TopLevelIndex tli() {
+ return tli;
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineAnnotationMirror.java b/java/com/google/turbine/processing/TurbineAnnotationMirror.java
new file mode 100644
index 0000000..5ea3de1
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineAnnotationMirror.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getLast;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.bound.EnumConstantValue;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
+import com.google.turbine.binder.bound.TurbineClassValue;
+import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
+import com.google.turbine.model.Const;
+import com.google.turbine.model.Const.ArrayInitValue;
+import com.google.turbine.processing.TurbineElement.TurbineExecutableElement;
+import com.google.turbine.processing.TurbineElement.TurbineFieldElement;
+import com.google.turbine.type.AnnoInfo;
+import com.google.turbine.type.Type.ErrorTy;
+import com.google.turbine.type.Type.TyKind;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ErrorType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * An implementation of {@link AnnotationMirror} and {@link AnnotationValue} backed by {@link
+ * AnnoInfo} and {@link Const.Value}.
+ */
+class TurbineAnnotationMirror implements TurbineAnnotationValueMirror, AnnotationMirror {
+
+ static TurbineAnnotationValueMirror annotationValue(ModelFactory factory, Const value) {
+ switch (value.kind()) {
+ case ARRAY:
+ return new TurbineArrayConstant(factory, (ArrayInitValue) value);
+ case PRIMITIVE:
+ return new TurbinePrimitiveConstant((Const.Value) value);
+ case CLASS_LITERAL:
+ return new TurbineClassConstant(factory, (TurbineClassValue) value);
+ case ENUM_CONSTANT:
+ return new TurbineEnumConstant(factory, (EnumConstantValue) value);
+ case ANNOTATION:
+ return new TurbineAnnotationMirror(factory, (TurbineAnnotationValue) value);
+ }
+ throw new AssertionError(value.kind());
+ }
+
+ static TurbineAnnotationMirror create(ModelFactory factory, AnnoInfo anno) {
+ return new TurbineAnnotationMirror(factory, new TurbineAnnotationValue(anno));
+ }
+
+ private final TurbineAnnotationValue value;
+ private final AnnoInfo anno;
+ private final Supplier<DeclaredType> type;
+ private final Supplier<ImmutableMap<String, MethodInfo>> elements;
+ private final Supplier<ImmutableMap<ExecutableElement, AnnotationValue>> elementValues;
+ private final Supplier<ImmutableMap<ExecutableElement, AnnotationValue>>
+ elementValuesWithDefaults;
+
+ private TurbineAnnotationMirror(ModelFactory factory, TurbineAnnotationValue value) {
+ this.value = value;
+ this.anno = value.info();
+ this.type =
+ factory.memoize(
+ new Supplier<DeclaredType>() {
+ @Override
+ public DeclaredType get() {
+ if (anno.sym() == null) {
+ return (ErrorType)
+ factory.asTypeMirror(ErrorTy.create(getLast(anno.tree().name()).value()));
+ }
+ return (DeclaredType) factory.typeElement(anno.sym()).asType();
+ }
+ });
+ this.elements =
+ factory.memoize(
+ new Supplier<ImmutableMap<String, MethodInfo>>() {
+ @Override
+ public ImmutableMap<String, MethodInfo> get() {
+ ImmutableMap.Builder<String, MethodInfo> result = ImmutableMap.builder();
+ for (MethodInfo m : factory.getSymbol(anno.sym()).methods()) {
+ checkState(m.parameters().isEmpty());
+ result.put(m.name(), m);
+ }
+ return result.build();
+ }
+ });
+ this.elementValues =
+ factory.memoize(
+ new Supplier<ImmutableMap<ExecutableElement, AnnotationValue>>() {
+ @Override
+ public ImmutableMap<ExecutableElement, AnnotationValue> get() {
+ ImmutableMap.Builder<ExecutableElement, AnnotationValue> result =
+ ImmutableMap.builder();
+ for (Map.Entry<String, Const> value : anno.values().entrySet()) {
+ result.put(
+ factory.executableElement(elements.get().get(value.getKey()).sym()),
+ annotationValue(factory, value.getValue()));
+ }
+ return result.build();
+ }
+ });
+ this.elementValuesWithDefaults =
+ factory.memoize(
+ new Supplier<ImmutableMap<ExecutableElement, AnnotationValue>>() {
+ @Override
+ public ImmutableMap<ExecutableElement, AnnotationValue> get() {
+ Map<ExecutableElement, AnnotationValue> result = new LinkedHashMap<>();
+ result.putAll(getElementValues());
+ for (MethodInfo method : elements.get().values()) {
+ if (method.defaultValue() == null) {
+ continue;
+ }
+ TurbineExecutableElement element = factory.executableElement(method.sym());
+ if (result.containsKey(element)) {
+ continue;
+ }
+ result.put(element, annotationValue(factory, method.defaultValue()));
+ }
+ return ImmutableMap.copyOf(result);
+ }
+ });
+ }
+
+ @Override
+ public int hashCode() {
+ return anno.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineAnnotationMirror
+ && anno.equals(((TurbineAnnotationMirror) obj).anno);
+ }
+
+ @Override
+ public String toString() {
+ return anno.toString();
+ }
+
+ @Override
+ public DeclaredType getAnnotationType() {
+ return type.get();
+ }
+
+ public Map<? extends ExecutableElement, ? extends AnnotationValue>
+ getElementValuesWithDefaults() {
+ return elementValuesWithDefaults.get();
+ }
+
+ @Override
+ public Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValues() {
+ return elementValues.get();
+ }
+
+ @Override
+ public AnnotationMirror getValue() {
+ return this;
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitAnnotation(getValue(), p);
+ }
+
+ public AnnoInfo anno() {
+ return anno;
+ }
+
+ @Override
+ public Const value() {
+ return value;
+ }
+
+ private static class TurbineArrayConstant implements TurbineAnnotationValueMirror {
+
+ private final ArrayInitValue value;
+ private final Supplier<ImmutableList<AnnotationValue>> elements;
+
+ private TurbineArrayConstant(ModelFactory factory, ArrayInitValue value) {
+ this.value = value;
+ this.elements =
+ factory.memoize(
+ new Supplier<ImmutableList<AnnotationValue>>() {
+ @Override
+ public ImmutableList<AnnotationValue> get() {
+ ImmutableList.Builder<AnnotationValue> values = ImmutableList.builder();
+ for (Const element : value.elements()) {
+ values.add(annotationValue(factory, element));
+ }
+ return values.build();
+ }
+ });
+ }
+
+ @Override
+ public List<AnnotationValue> getValue() {
+ return elements.get();
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitArray(elements.get(), p);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("{");
+ Joiner.on(", ").appendTo(sb, elements.get());
+ sb.append("}");
+ return sb.toString();
+ }
+
+ @Override
+ public Const value() {
+ return value;
+ }
+ }
+
+ private static class TurbineClassConstant implements TurbineAnnotationValueMirror {
+
+ private final TurbineClassValue value;
+ private final TypeMirror typeMirror;
+
+ private TurbineClassConstant(ModelFactory factory, TurbineClassValue value) {
+ this.value = value;
+ this.typeMirror = factory.asTypeMirror(value.type());
+ }
+
+ @Override
+ public TypeMirror getValue() {
+ return typeMirror;
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ if (value.type().tyKind() == TyKind.ERROR_TY) {
+ // represent unresolvable class literals as the string value "<error>" for compatibility
+ // with javac: https://bugs.openjdk.java.net/browse/JDK-8229535
+ return v.visitString("<error>", p);
+ } else {
+ return v.visitType(getValue(), p);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return typeMirror + ".class";
+ }
+
+ @Override
+ public Const value() {
+ return value;
+ }
+ }
+
+ private static class TurbineEnumConstant implements TurbineAnnotationValueMirror {
+
+ private final EnumConstantValue value;
+ private final TurbineFieldElement fieldElement;
+
+ private TurbineEnumConstant(ModelFactory factory, EnumConstantValue value) {
+ this.value = value;
+ this.fieldElement = factory.fieldElement(value.sym());
+ }
+
+ @Override
+ public Object getValue() {
+ return fieldElement;
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitEnumConstant(fieldElement, p);
+ }
+
+ @Override
+ public String toString() {
+ return fieldElement.getEnclosingElement() + "." + fieldElement.getSimpleName();
+ }
+
+ @Override
+ public Const value() {
+ return value;
+ }
+ }
+
+ private static class TurbinePrimitiveConstant implements TurbineAnnotationValueMirror {
+
+ private final Const.Value value;
+
+ public TurbinePrimitiveConstant(Const.Value value) {
+ this.value = value;
+ }
+
+ @Override
+ public Object getValue() {
+ return value.getValue();
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return value.accept(v, p);
+ }
+
+ @Override
+ public String toString() {
+ return value.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbinePrimitiveConstant
+ && value.equals(((TurbinePrimitiveConstant) obj).value);
+ }
+
+ @Override
+ public Const value() {
+ return value;
+ }
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineAnnotationProxy.java b/java/com/google/turbine/processing/TurbineAnnotationProxy.java
new file mode 100644
index 0000000..c39f310
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineAnnotationProxy.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.turbine.binder.bound.EnumConstantValue;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
+import com.google.turbine.binder.bound.TurbineClassValue;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.model.Const;
+import com.google.turbine.model.Const.ArrayInitValue;
+import com.google.turbine.model.Const.Value;
+import com.google.turbine.type.AnnoInfo;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+import javax.lang.model.type.MirroredTypeException;
+import javax.lang.model.type.MirroredTypesException;
+import javax.lang.model.type.TypeMirror;
+
+/** An {@link InvocationHandler} for reflectively accessing annotations. */
+class TurbineAnnotationProxy implements InvocationHandler {
+
+ static <A extends Annotation> A create(
+ ModelFactory factory, Class<A> annotationType, AnnoInfo anno) {
+ ClassLoader loader = annotationType.getClassLoader();
+ if (loader == null) {
+ // annotation was loaded from the system classloader, e.g. java.lang.annotation.*
+ loader = factory.processorLoader();
+ }
+ return annotationType.cast(
+ Proxy.newProxyInstance(
+ loader,
+ new Class<?>[] {annotationType},
+ new TurbineAnnotationProxy(factory, loader, annotationType, anno)));
+ }
+
+ private final ModelFactory factory;
+ private final ClassLoader loader;
+ private final Class<?> annotationType;
+ private final AnnoInfo anno;
+
+ TurbineAnnotationProxy(
+ ModelFactory factory, ClassLoader loader, Class<?> annotationType, AnnoInfo anno) {
+ this.factory = factory;
+ this.loader = loader;
+ this.annotationType = annotationType;
+ this.anno = anno;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ switch (method.getName()) {
+ case "hashCode":
+ checkArgument(args == null);
+ return anno.hashCode();
+ case "annotationType":
+ checkArgument(args == null);
+ return annotationType;
+ case "equals":
+ checkArgument(args.length == 1);
+ return proxyEquals(args[0]);
+ case "toString":
+ checkArgument(args == null);
+ return anno.toString();
+ default:
+ break;
+ }
+ Const value = anno.values().get(method.getName());
+ if (value != null) {
+ return constValue(method.getReturnType(), factory, loader, value);
+ }
+ for (TypeBoundClass.MethodInfo m : factory.getSymbol(anno.sym()).methods()) {
+ if (m.name().contentEquals(method.getName())) {
+ return constValue(method.getReturnType(), factory, loader, m.defaultValue());
+ }
+ }
+ throw new NoSuchMethodError(method.getName());
+ }
+
+ public boolean proxyEquals(Object other) {
+ if (!annotationType.isInstance(other)) {
+ return false;
+ }
+ if (!Proxy.isProxyClass(other.getClass())) {
+ return false;
+ }
+ InvocationHandler handler = Proxy.getInvocationHandler(other);
+ if (!(handler instanceof TurbineAnnotationProxy)) {
+ return false;
+ }
+ TurbineAnnotationProxy that = (TurbineAnnotationProxy) handler;
+ return anno.equals(that.anno);
+ }
+
+ static Object constValue(
+ Class<?> returnType, ModelFactory factory, ClassLoader loader, Const value) {
+ switch (value.kind()) {
+ case PRIMITIVE:
+ return ((Value) value).getValue();
+ case ARRAY:
+ return constArrayValue(returnType, factory, loader, (Const.ArrayInitValue) value);
+ case ENUM_CONSTANT:
+ return constEnumValue(loader, (EnumConstantValue) value);
+ case ANNOTATION:
+ return constAnnotationValue(factory, loader, (TurbineAnnotationValue) value);
+ case CLASS_LITERAL:
+ return constClassValue(factory, (TurbineClassValue) value);
+ }
+ throw new AssertionError(value.kind());
+ }
+
+ private static Object constArrayValue(
+ Class<?> returnType, ModelFactory factory, ClassLoader loader, ArrayInitValue value) {
+ if (returnType.getComponentType().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());
+ int idx = 0;
+ for (Const element : value.elements()) {
+ Object v = constValue(returnType, factory, loader, element);
+ Array.set(result, idx++, v);
+ }
+ return result;
+ }
+
+ @SuppressWarnings("unchecked") // Enum.class
+ private static Object constEnumValue(ClassLoader loader, EnumConstantValue value) {
+ Class<?> clazz;
+ try {
+ clazz = loader.loadClass(value.sym().owner().toString());
+ } catch (ClassNotFoundException e) {
+ throw new LinkageError(e.getMessage(), e);
+ }
+ return Enum.valueOf(clazz.asSubclass(Enum.class), value.sym().name());
+ }
+
+ private static Object constAnnotationValue(
+ ModelFactory factory, ClassLoader loader, TurbineAnnotationValue value) {
+ try {
+ String name = value.sym().binaryName().replace('/', '.');
+ Class<? extends Annotation> clazz =
+ Class.forName(name, false, loader).asSubclass(Annotation.class);
+ return create(factory, clazz, value.info());
+ } catch (ClassNotFoundException e) {
+ throw new LinkageError(e.getMessage(), e);
+ }
+ }
+
+ private static Object constClassValue(ModelFactory factory, TurbineClassValue value) {
+ throw new MirroredTypeException(factory.asTypeMirror(value.type()));
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineAnnotationValueMirror.java b/java/com/google/turbine/processing/TurbineAnnotationValueMirror.java
new file mode 100644
index 0000000..a196750
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineAnnotationValueMirror.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019 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.processing;
+
+import com.google.turbine.model.Const;
+import javax.lang.model.element.AnnotationValue;
+
+/** An {@link AnnotationValue} backed by a {@link Const}. */
+public interface TurbineAnnotationValueMirror extends AnnotationValue {
+ Const value();
+}
diff --git a/java/com/google/turbine/processing/TurbineElement.java b/java/com/google/turbine/processing/TurbineElement.java
new file mode 100644
index 0000000..c22a442
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineElement.java
@@ -0,0 +1,1298 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.turbine.binder.bound.AnnotationMetadata;
+import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
+import com.google.turbine.binder.lookup.PackageScope;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.PackageSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.diag.TurbineError;
+import com.google.turbine.diag.TurbineError.ErrorKind;
+import com.google.turbine.model.Const;
+import com.google.turbine.model.Const.ArrayInitValue;
+import com.google.turbine.model.TurbineFlag;
+import com.google.turbine.model.TurbineTyKind;
+import com.google.turbine.tree.Tree.MethDecl;
+import com.google.turbine.tree.Tree.TyDecl;
+import com.google.turbine.tree.Tree.VarDecl;
+import com.google.turbine.type.AnnoInfo;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.ClassTy.SimpleClassTy;
+import com.google.turbine.type.Type.ErrorTy;
+import java.lang.annotation.Annotation;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.EnumSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/** An {@link Element} implementation backed by a {@link Symbol}. */
+public abstract class TurbineElement implements Element {
+
+ public abstract Symbol sym();
+
+ public abstract String javadoc();
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ protected final ModelFactory factory;
+ private final Supplier<ImmutableList<AnnotationMirror>> annotationMirrors;
+
+ protected <T> Supplier<T> memoize(Supplier<T> supplier) {
+ return factory.memoize(supplier);
+ }
+
+ protected TurbineElement(ModelFactory factory) {
+ this.factory = requireNonNull(factory);
+ this.annotationMirrors =
+ factory.memoize(
+ new Supplier<ImmutableList<AnnotationMirror>>() {
+ @Override
+ public ImmutableList<AnnotationMirror> get() {
+ ImmutableList.Builder<AnnotationMirror> result = ImmutableList.builder();
+ for (AnnoInfo anno : annos()) {
+ result.add(TurbineAnnotationMirror.create(factory, anno));
+ }
+ return result.build();
+ }
+ });
+ }
+
+ static AnnoInfo getAnnotation(Iterable<AnnoInfo> annos, ClassSymbol sym) {
+ for (AnnoInfo anno : annos) {
+ if (Objects.equals(anno.sym(), sym)) {
+ return anno;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+ ClassSymbol sym = new ClassSymbol(annotationType.getName().replace('.', '/'));
+ TypeBoundClass info = factory.getSymbol(sym);
+ if (info == null) {
+ return null;
+ }
+ AnnoInfo anno = getAnnotation(annos(), sym);
+ if (anno == null) {
+ return null;
+ }
+ return TurbineAnnotationProxy.create(factory, annotationType, anno);
+ }
+
+ @Override
+ public final <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType) {
+ ClassSymbol sym = new ClassSymbol(annotationType.getName().replace('.', '/'));
+ TypeBoundClass info = factory.getSymbol(sym);
+ if (info == null) {
+ return null;
+ }
+ AnnotationMetadata metadata = info.annotationMetadata();
+ if (metadata == null) {
+ return null;
+ }
+ List<A> result = new ArrayList<>();
+ for (AnnoInfo anno : annos()) {
+ if (anno.sym().equals(sym)) {
+ result.add(TurbineAnnotationProxy.create(factory, annotationType, anno));
+ continue;
+ }
+ if (anno.sym().equals(metadata.repeatable())) {
+ ArrayInitValue arrayValue = (ArrayInitValue) anno.values().get("value");
+ for (Const element : arrayValue.elements()) {
+ result.add(
+ TurbineAnnotationProxy.create(
+ factory, annotationType, ((TurbineAnnotationValue) element).info()));
+ }
+ }
+ }
+ return Iterables.toArray(result, annotationType);
+ }
+
+ @Override
+ public final List<? extends AnnotationMirror> getAnnotationMirrors() {
+ return annotationMirrors.get();
+ }
+
+ List<? extends AnnotationMirror> getAllAnnotationMirrors() {
+ return getAnnotationMirrors();
+ }
+
+ protected abstract ImmutableList<AnnoInfo> annos();
+
+ /** A {@link TypeElement} implementation backed by a {@link ClassSymbol}. */
+ static class TurbineTypeElement extends TurbineElement implements TypeElement {
+
+ @Override
+ public int hashCode() {
+ return sym.hashCode();
+ }
+
+ private final ClassSymbol sym;
+ private final Supplier<TypeBoundClass> info;
+
+ TurbineTypeElement(ModelFactory factory, ClassSymbol sym) {
+ super(factory);
+ this.sym = requireNonNull(sym);
+ this.info =
+ memoize(
+ new Supplier<TypeBoundClass>() {
+ @Override
+ public TypeBoundClass get() {
+ return factory.getSymbol(sym);
+ }
+ });
+ }
+
+ @Nullable
+ TypeBoundClass info() {
+ return info.get();
+ }
+
+ TypeBoundClass infoNonNull() {
+ TypeBoundClass info = info();
+ if (info == null) {
+ throw TurbineError.format(/* source= */ null, ErrorKind.SYMBOL_NOT_FOUND, sym);
+ }
+ return info;
+ }
+
+ @Override
+ public NestingKind getNestingKind() {
+ TypeBoundClass info = info();
+ return (info != null && info.owner() != null) ? NestingKind.MEMBER : NestingKind.TOP_LEVEL;
+ }
+
+ private final Supplier<TurbineName> qualifiedName =
+ memoize(
+ new Supplier<TurbineName>() {
+ @Override
+ public TurbineName get() {
+ TypeBoundClass info = info();
+ if (info == null || info.owner() == null) {
+ return new TurbineName(sym.toString());
+ }
+ ClassSymbol sym = sym();
+ Deque<String> flat = new ArrayDeque<>();
+ while (info.owner() != null) {
+ flat.addFirst(sym.binaryName().substring(info.owner().binaryName().length() + 1));
+ sym = info.owner();
+ info = factory.getSymbol(sym);
+ }
+ flat.addFirst(sym.toString());
+ return new TurbineName(Joiner.on('.').join(flat));
+ }
+ });
+
+ @Override
+ public Name getQualifiedName() {
+ return qualifiedName.get();
+ }
+
+ private final Supplier<TypeMirror> superclass =
+ memoize(
+ new Supplier<TypeMirror>() {
+ @Override
+ public TypeMirror get() {
+ TypeBoundClass info = infoNonNull();
+ switch (info.kind()) {
+ case CLASS:
+ case ENUM:
+ if (info.superclass() != null) {
+ return factory.asTypeMirror(info.superClassType());
+ }
+ if (info instanceof SourceTypeBoundClass) {
+ // support simple name 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()));
+ }
+ }
+ return factory.noType();
+ case INTERFACE:
+ case ANNOTATION:
+ return factory.noType();
+ }
+ throw new AssertionError(info.kind());
+ }
+ });
+
+ @Override
+ public TypeMirror getSuperclass() {
+ return superclass.get();
+ }
+
+ @Override
+ public String toString() {
+ return getQualifiedName().toString();
+ }
+
+ private final Supplier<List<TypeMirror>> interfaces =
+ memoize(
+ new Supplier<List<TypeMirror>>() {
+ @Override
+ public List<TypeMirror> get() {
+ return factory.asTypeMirrors(infoNonNull().interfaceTypes());
+ }
+ });
+
+ @Override
+ public List<? extends TypeMirror> getInterfaces() {
+ return interfaces.get();
+ }
+
+ private final Supplier<ImmutableList<TypeParameterElement>> typeParameters =
+ memoize(
+ new Supplier<ImmutableList<TypeParameterElement>>() {
+ @Override
+ public ImmutableList<TypeParameterElement> get() {
+ ImmutableList.Builder<TypeParameterElement> result = ImmutableList.builder();
+ for (TyVarSymbol p : infoNonNull().typeParameters().values()) {
+ result.add(factory.typeParameterElement(p));
+ }
+ return result.build();
+ }
+ });
+
+ @Override
+ public List<? extends TypeParameterElement> getTypeParameters() {
+ return typeParameters.get();
+ }
+
+ private final Supplier<TypeMirror> type =
+ memoize(
+ new Supplier<TypeMirror>() {
+ @Override
+ public TypeMirror get() {
+ return factory.asTypeMirror(asGenericType(sym));
+ }
+
+ ClassTy asGenericType(ClassSymbol symbol) {
+ TypeBoundClass info = info();
+ if (info == null) {
+ return ClassTy.asNonParametricClassTy(symbol);
+ }
+ Deque<Type.ClassTy.SimpleClassTy> simples = new ArrayDeque<>();
+ simples.addFirst(simple(symbol, info));
+ while (info.owner() != null && (info.access() & TurbineFlag.ACC_STATIC) == 0) {
+ symbol = info.owner();
+ info = factory.getSymbol(symbol);
+ simples.addFirst(simple(symbol, info));
+ }
+ return ClassTy.create(ImmutableList.copyOf(simples));
+ }
+
+ private SimpleClassTy simple(ClassSymbol sym, TypeBoundClass info) {
+ ImmutableList.Builder<Type> args = ImmutableList.builder();
+ for (TyVarSymbol t : info.typeParameters().values()) {
+ args.add(Type.TyVar.create(t, ImmutableList.of()));
+ }
+ return SimpleClassTy.create(sym, args.build(), ImmutableList.of());
+ }
+ });
+
+ @Override
+ public TypeMirror asType() {
+ return type.get();
+ }
+
+ @Override
+ public ElementKind getKind() {
+ TypeBoundClass info = infoNonNull();
+ switch (info.kind()) {
+ case CLASS:
+ return ElementKind.CLASS;
+ case INTERFACE:
+ return ElementKind.INTERFACE;
+ case ENUM:
+ return ElementKind.ENUM;
+ case ANNOTATION:
+ return ElementKind.ANNOTATION_TYPE;
+ }
+ throw new AssertionError(info.kind());
+ }
+
+ @Override
+ public Set<Modifier> getModifiers() {
+ return asModifierSet(ModifierOwner.TYPE, infoNonNull().access() & ~TurbineFlag.ACC_SUPER);
+ }
+
+ private final Supplier<TurbineName> simpleName =
+ memoize(
+ new Supplier<TurbineName>() {
+ @Override
+ public TurbineName get() {
+ TypeBoundClass info = info();
+ if (info == null || info.owner() == null) {
+ return new TurbineName(sym.simpleName());
+ }
+ return new TurbineName(
+ sym.binaryName().substring(info.owner().binaryName().length() + 1));
+ }
+ });
+
+ @Override
+ public Name getSimpleName() {
+ return simpleName.get();
+ }
+
+ private final Supplier<Element> enclosing =
+ memoize(
+ new Supplier<Element>() {
+ @Override
+ public Element get() {
+ return getNestingKind().equals(NestingKind.TOP_LEVEL)
+ ? factory.packageElement(sym.owner())
+ : factory.typeElement(info().owner());
+ }
+ });
+
+ @Override
+ public Element getEnclosingElement() {
+ return enclosing.get();
+ }
+
+ private final Supplier<ImmutableList<Element>> enclosed =
+ memoize(
+ new Supplier<ImmutableList<Element>>() {
+ @Override
+ public ImmutableList<Element> get() {
+ TypeBoundClass info = infoNonNull();
+ ImmutableList.Builder<Element> result = ImmutableList.builder();
+ for (FieldInfo field : info.fields()) {
+ result.add(factory.fieldElement(field.sym()));
+ }
+ for (MethodInfo method : info.methods()) {
+ result.add(factory.executableElement(method.sym()));
+ }
+ for (ClassSymbol child : info.children().values()) {
+ result.add(factory.typeElement(child));
+ }
+ return result.build();
+ }
+ });
+
+ @Override
+ public List<? extends Element> getEnclosedElements() {
+ return enclosed.get();
+ }
+
+ @Override
+ public <R, P> R accept(ElementVisitor<R, P> v, P p) {
+ return v.visitType(this, p);
+ }
+
+ @Override
+ public ClassSymbol sym() {
+ return sym;
+ }
+
+ @Override
+ public String javadoc() {
+ TypeBoundClass info = info();
+ if (!(info instanceof SourceTypeBoundClass)) {
+ return null;
+ }
+ return ((SourceTypeBoundClass) info).decl().javadoc();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineTypeElement && sym.equals(((TurbineTypeElement) obj).sym);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return infoNonNull().annotations();
+ }
+
+ @Override
+ public final <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+ ClassSymbol sym = new ClassSymbol(annotationType.getName().replace('.', '/'));
+ AnnoInfo anno = getAnnotation(annos(), sym);
+ if (anno != null) {
+ return TurbineAnnotationProxy.create(factory, annotationType, anno);
+ }
+ if (!isAnnotationInherited(sym)) {
+ return null;
+ }
+ ClassSymbol superclass = infoNonNull().superclass();
+ while (superclass != null) {
+ TypeBoundClass info = factory.getSymbol(superclass);
+ if (info == null) {
+ break;
+ }
+ anno = getAnnotation(info.annotations(), sym);
+ if (anno != null) {
+ return TurbineAnnotationProxy.create(factory, annotationType, anno);
+ }
+ superclass = info.superclass();
+ }
+ return null;
+ }
+
+ @Override
+ List<? extends AnnotationMirror> getAllAnnotationMirrors() {
+ Map<ClassSymbol, AnnotationMirror> result = new LinkedHashMap<>();
+ for (AnnoInfo anno : annos()) {
+ result.put(anno.sym(), TurbineAnnotationMirror.create(factory, anno));
+ }
+ ClassSymbol superclass = infoNonNull().superclass();
+ while (superclass != null) {
+ TypeBoundClass i = factory.getSymbol(superclass);
+ if (i == null) {
+ break;
+ }
+ for (AnnoInfo anno : i.annotations()) {
+ addAnnotationFromSuper(result, anno);
+ }
+ superclass = i.superclass();
+ }
+ return ImmutableList.copyOf(result.values());
+ }
+
+ private void addAnnotationFromSuper(Map<ClassSymbol, AnnotationMirror> result, AnnoInfo anno) {
+ if (!isAnnotationInherited(anno.sym())) {
+ return;
+ }
+ if (result.containsKey(anno.sym())) {
+ // if the same inherited annotation is present on multiple supertypes, only return one
+ return;
+ }
+ result.put(anno.sym(), TurbineAnnotationMirror.create(factory, anno));
+ }
+
+ private boolean isAnnotationInherited(ClassSymbol sym) {
+ TypeBoundClass annoInfo = factory.getSymbol(sym);
+ if (annoInfo == null) {
+ return false;
+ }
+ for (AnnoInfo anno : annoInfo.annotations()) {
+ if (anno.sym().equals(ClassSymbol.INHERITED)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /** A {@link TypeParameterElement} implementation backed by a {@link TyVarSymbol}. */
+ static class TurbineTypeParameterElement extends TurbineElement implements TypeParameterElement {
+
+ @Override
+ public int hashCode() {
+ return sym.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineTypeParameterElement
+ && sym.equals(((TurbineTypeParameterElement) obj).sym);
+ }
+
+ private final TyVarSymbol sym;
+
+ public TurbineTypeParameterElement(ModelFactory factory, TyVarSymbol sym) {
+ super(factory);
+ this.sym = sym;
+ }
+
+ private final Supplier<TyVarInfo> info =
+ memoize(
+ new Supplier<TyVarInfo>() {
+ @Override
+ public TyVarInfo get() {
+ return factory.getTyVarInfo(sym);
+ }
+ });
+
+ @Nullable
+ private TyVarInfo info() {
+ return info.get();
+ }
+
+ @Override
+ public String toString() {
+ return sym.name();
+ }
+
+ @Override
+ public Element getGenericElement() {
+ return factory.element(sym.owner());
+ }
+
+ @Override
+ public List<? extends TypeMirror> getBounds() {
+ ImmutableList<Type> bounds = info().upperBound().bounds();
+ return factory.asTypeMirrors(bounds.isEmpty() ? ImmutableList.of(ClassTy.OBJECT) : bounds);
+ }
+
+ @Override
+ public TypeMirror asType() {
+ return factory.asTypeMirror(Type.TyVar.create(sym, info().annotations()));
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.TYPE_PARAMETER;
+ }
+
+ @Override
+ public Set<Modifier> getModifiers() {
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public Name getSimpleName() {
+ return new TurbineName(sym.name());
+ }
+
+ @Override
+ public Element getEnclosingElement() {
+ return getGenericElement();
+ }
+
+ @Override
+ public List<? extends Element> getEnclosedElements() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public <R, P> R accept(ElementVisitor<R, P> v, P p) {
+ return v.visitTypeParameter(this, p);
+ }
+
+ @Override
+ public TyVarSymbol sym() {
+ return sym;
+ }
+
+ @Override
+ public String javadoc() {
+ return null;
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return info().annotations();
+ }
+ }
+
+ /** An {@link ExecutableElement} implementation backed by a {@link MethodSymbol}. */
+ static class TurbineExecutableElement extends TurbineElement implements ExecutableElement {
+
+ private final MethodSymbol sym;
+
+ private final Supplier<MethodInfo> info =
+ memoize(
+ new Supplier<MethodInfo>() {
+ @Override
+ public MethodInfo get() {
+ return factory.getMethodInfo(sym);
+ }
+ });
+
+ @Nullable
+ MethodInfo info() {
+ return info.get();
+ }
+
+ TurbineExecutableElement(ModelFactory factory, MethodSymbol sym) {
+ super(factory);
+ this.sym = sym;
+ }
+
+ @Override
+ public MethodSymbol sym() {
+ return sym;
+ }
+
+ @Override
+ public String javadoc() {
+ MethDecl decl = info().decl();
+ return decl != null ? decl.javadoc() : null;
+ }
+
+ @Override
+ public int hashCode() {
+ return sym.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineExecutableElement
+ && sym.equals(((TurbineExecutableElement) obj).sym);
+ }
+
+ @Override
+ public List<? extends TypeParameterElement> getTypeParameters() {
+ ImmutableList.Builder<TurbineTypeParameterElement> result = ImmutableList.builder();
+ for (Map.Entry<TyVarSymbol, TyVarInfo> p : info().tyParams().entrySet()) {
+ result.add(factory.typeParameterElement(p.getKey()));
+ }
+ return result.build();
+ }
+
+ @Override
+ public TypeMirror getReturnType() {
+ return factory.asTypeMirror(info().returnType());
+ }
+
+ private final Supplier<ImmutableList<VariableElement>> parameters =
+ memoize(
+ new Supplier<ImmutableList<VariableElement>>() {
+ @Override
+ public ImmutableList<VariableElement> get() {
+ ImmutableList.Builder<VariableElement> result = ImmutableList.builder();
+ for (ParamInfo param : info().parameters()) {
+ if (param.synthetic()) {
+ // ExecutableElement#getParameters doesn't expect synthetic or mandated
+ // parameters
+ continue;
+ }
+ result.add(factory.parameterElement(param.sym()));
+ }
+ return result.build();
+ }
+ });
+
+ @Override
+ public List<? extends VariableElement> getParameters() {
+ return parameters.get();
+ }
+
+ @Override
+ public String toString() {
+ MethodInfo info = info();
+ StringBuilder sb = new StringBuilder();
+ if (!info.tyParams().isEmpty()) {
+ sb.append('<');
+ Joiner.on(',').appendTo(sb, info.tyParams().keySet());
+ sb.append('>');
+ }
+ if (getKind() == ElementKind.CONSTRUCTOR) {
+ sb.append(info.sym().owner().simpleName());
+ } else {
+ sb.append(info.sym().name());
+ }
+ sb.append('(');
+ boolean first = true;
+ for (ParamInfo p : info.parameters()) {
+ if (!first) {
+ sb.append(',');
+ }
+ sb.append(p.type());
+ first = false;
+ }
+ sb.append(')');
+ return sb.toString();
+ }
+
+ @Override
+ public TypeMirror getReceiverType() {
+ return info().receiver() != null
+ ? factory.asTypeMirror(info().receiver().type())
+ : factory.noType();
+ }
+
+ @Override
+ public boolean isVarArgs() {
+ return (info().access() & TurbineFlag.ACC_VARARGS) == TurbineFlag.ACC_VARARGS;
+ }
+
+ @Override
+ public boolean isDefault() {
+ return (info().access() & TurbineFlag.ACC_DEFAULT) == TurbineFlag.ACC_DEFAULT;
+ }
+
+ @Override
+ public List<? extends TypeMirror> getThrownTypes() {
+ return factory.asTypeMirrors(info().exceptions());
+ }
+
+ @Override
+ public AnnotationValue getDefaultValue() {
+ return info().defaultValue() != null
+ ? TurbineAnnotationMirror.annotationValue(factory, info().defaultValue())
+ : null;
+ }
+
+ @Override
+ public TypeMirror asType() {
+ return factory.asTypeMirror(info().asType());
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return info().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);
+ }
+
+ @Override
+ public Name getSimpleName() {
+ return new TurbineName(info().sym().name());
+ }
+
+ @Override
+ public Element getEnclosingElement() {
+ return factory.typeElement(info().sym().owner());
+ }
+
+ @Override
+ public List<? extends Element> getEnclosedElements() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public <R, P> R accept(ElementVisitor<R, P> v, P p) {
+ return v.visitExecutable(this, p);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return info().annotations();
+ }
+ }
+
+ /** An {@link VariableElement} implementation backed by a {@link FieldSymbol}. */
+ static class TurbineFieldElement extends TurbineElement implements VariableElement {
+
+ @Override
+ public String toString() {
+ return sym.name();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineFieldElement && sym.equals(((TurbineFieldElement) obj).sym);
+ }
+
+ @Override
+ public int hashCode() {
+ return sym.hashCode();
+ }
+
+ private final FieldSymbol sym;
+
+ @Override
+ public FieldSymbol sym() {
+ return sym;
+ }
+
+ @Override
+ public String javadoc() {
+ VarDecl decl = info().decl();
+ return decl != null ? decl.javadoc() : null;
+ }
+
+ private final Supplier<FieldInfo> info =
+ memoize(
+ new Supplier<FieldInfo>() {
+ @Override
+ public FieldInfo get() {
+ return factory.getFieldInfo(sym);
+ }
+ });
+
+ @Nullable
+ FieldInfo info() {
+ return info.get();
+ }
+
+ TurbineFieldElement(ModelFactory factory, FieldSymbol sym) {
+ super(factory);
+ this.sym = sym;
+ }
+
+ @Override
+ public Object getConstantValue() {
+ if (info().value() == null) {
+ return null;
+ }
+ return info().value().getValue();
+ }
+
+ @Override
+ public TypeMirror asType() {
+ return factory.asTypeMirror(info().type());
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ((info().access() & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM)
+ ? ElementKind.ENUM_CONSTANT
+ : ElementKind.FIELD;
+ }
+
+ @Override
+ public Set<Modifier> getModifiers() {
+ return asModifierSet(ModifierOwner.FIELD, info().access());
+ }
+
+ @Override
+ public Name getSimpleName() {
+ return new TurbineName(sym.name());
+ }
+
+ @Override
+ public Element getEnclosingElement() {
+ return factory.typeElement(sym.owner());
+ }
+
+ @Override
+ public List<? extends Element> getEnclosedElements() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public <R, P> R accept(ElementVisitor<R, P> v, P p) {
+ return v.visitVariable(this, p);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return info().annotations();
+ }
+ }
+
+ private enum ModifierOwner {
+ TYPE,
+ PARAMETER,
+ FIELD,
+ METHOD
+ }
+
+ private static ImmutableSet<Modifier> asModifierSet(ModifierOwner modifierOwner, int access) {
+ EnumSet<Modifier> modifiers = EnumSet.noneOf(Modifier.class);
+ if ((access & TurbineFlag.ACC_PUBLIC) == TurbineFlag.ACC_PUBLIC) {
+ modifiers.add(Modifier.PUBLIC);
+ }
+ if ((access & TurbineFlag.ACC_PROTECTED) == TurbineFlag.ACC_PROTECTED) {
+ modifiers.add(Modifier.PROTECTED);
+ }
+ if ((access & TurbineFlag.ACC_PRIVATE) == TurbineFlag.ACC_PRIVATE) {
+ modifiers.add(Modifier.PRIVATE);
+ }
+ if ((access & TurbineFlag.ACC_ABSTRACT) == TurbineFlag.ACC_ABSTRACT) {
+ modifiers.add(Modifier.ABSTRACT);
+ }
+ if ((access & TurbineFlag.ACC_FINAL) == TurbineFlag.ACC_FINAL) {
+ modifiers.add(Modifier.FINAL);
+ }
+ if ((access & TurbineFlag.ACC_DEFAULT) == TurbineFlag.ACC_DEFAULT) {
+ modifiers.add(Modifier.DEFAULT);
+ }
+ if ((access & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) {
+ modifiers.add(Modifier.STATIC);
+ }
+ if ((access & TurbineFlag.ACC_TRANSIENT) == TurbineFlag.ACC_TRANSIENT) {
+ switch (modifierOwner) {
+ case METHOD:
+ case PARAMETER:
+ // varargs and transient use the same bits
+ break;
+ default:
+ modifiers.add(Modifier.TRANSIENT);
+ }
+ }
+ if ((access & TurbineFlag.ACC_VOLATILE) == TurbineFlag.ACC_VOLATILE) {
+ modifiers.add(Modifier.VOLATILE);
+ }
+ if ((access & TurbineFlag.ACC_SYNCHRONIZED) == TurbineFlag.ACC_SYNCHRONIZED) {
+ modifiers.add(Modifier.SYNCHRONIZED);
+ }
+ if ((access & TurbineFlag.ACC_NATIVE) == TurbineFlag.ACC_NATIVE) {
+ modifiers.add(Modifier.NATIVE);
+ }
+ if ((access & TurbineFlag.ACC_STRICT) == TurbineFlag.ACC_STRICT) {
+ modifiers.add(Modifier.STRICTFP);
+ }
+
+ return Sets.immutableEnumSet(modifiers);
+ }
+
+ /** A {@link PackageElement} implementation backed by a {@link PackageSymbol}. */
+ static class TurbinePackageElement extends TurbineElement implements PackageElement {
+
+ private final PackageSymbol sym;
+
+ public TurbinePackageElement(ModelFactory factory, PackageSymbol sym) {
+ super(factory);
+ this.sym = sym;
+ }
+
+ @Override
+ public Name getQualifiedName() {
+ return new TurbineName(sym.toString());
+ }
+
+ @Override
+ public boolean isUnnamed() {
+ return sym.binaryName().isEmpty();
+ }
+
+ @Override
+ public TypeMirror asType() {
+ return factory.packageType(sym);
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.PACKAGE;
+ }
+
+ @Override
+ public Set<Modifier> getModifiers() {
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public Name getSimpleName() {
+ return new TurbineName(sym.binaryName().substring(sym.binaryName().lastIndexOf('/') + 1));
+ }
+
+ @Override
+ public Element getEnclosingElement() {
+ // a package is not enclosed by another element
+ return null;
+ }
+
+ @Override
+ public List<TurbineTypeElement> getEnclosedElements() {
+ ImmutableSet.Builder<TurbineTypeElement> result = ImmutableSet.builder();
+ PackageScope scope = factory.tli().lookupPackage(Splitter.on('/').split(sym.binaryName()));
+ for (ClassSymbol key : scope.classes()) {
+ if (key.binaryName().contains("$") && factory.getSymbol(key).owner() != null) {
+ // Skip member classes: only top-level classes are enclosed by the package.
+ // The initial check for '$' is an optimization.
+ continue;
+ }
+ if (key.simpleName().equals("package-info")) {
+ continue;
+ }
+ result.add(factory.typeElement(key));
+ }
+ return result.build().asList();
+ }
+
+ @Override
+ public <R, P> R accept(ElementVisitor<R, P> v, P p) {
+ return v.visitPackage(this, p);
+ }
+
+ @Override
+ public PackageSymbol sym() {
+ return sym;
+ }
+
+ @Override
+ public String javadoc() {
+ return null;
+ }
+
+ @Override
+ public int hashCode() {
+ return sym.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbinePackageElement && sym.equals(((TurbinePackageElement) obj).sym);
+ }
+
+ private final Supplier<ImmutableList<AnnoInfo>> annos =
+ memoize(
+ new Supplier<ImmutableList<AnnoInfo>>() {
+ @Override
+ public ImmutableList<AnnoInfo> get() {
+ TypeBoundClass info =
+ factory.getSymbol(new ClassSymbol(sym.binaryName() + "/package-info"));
+ return info != null ? info.annotations() : ImmutableList.of();
+ }
+ });
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return annos.get();
+ }
+
+ @Override
+ public String toString() {
+ return sym.toString();
+ }
+ }
+
+ /** A {@link VariableElement} implementation backed by a {@link ParamSymbol}. */
+ static class TurbineParameterElement extends TurbineElement implements VariableElement {
+
+ @Override
+ public ParamSymbol sym() {
+ return sym;
+ }
+
+ @Override
+ public String javadoc() {
+ return null;
+ }
+
+ @Override
+ public int hashCode() {
+ return sym.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineParameterElement
+ && sym.equals(((TurbineParameterElement) obj).sym);
+ }
+
+ private final ParamSymbol sym;
+
+ private final Supplier<ParamInfo> info =
+ memoize(
+ new Supplier<ParamInfo>() {
+ @Override
+ public ParamInfo get() {
+ return factory.getParamInfo(sym);
+ }
+ });
+
+ @Nullable
+ ParamInfo info() {
+ return info.get();
+ }
+
+ public TurbineParameterElement(ModelFactory factory, ParamSymbol sym) {
+ super(factory);
+ this.sym = sym;
+ }
+
+ @Override
+ public Object getConstantValue() {
+ return null;
+ }
+
+ private final Supplier<TypeMirror> type =
+ memoize(
+ new Supplier<TypeMirror>() {
+ @Override
+ public TypeMirror get() {
+ return factory.asTypeMirror(info().type());
+ }
+ });
+
+ @Override
+ public TypeMirror asType() {
+ return type.get();
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.PARAMETER;
+ }
+
+ @Override
+ public Set<Modifier> getModifiers() {
+ return asModifierSet(ModifierOwner.PARAMETER, info().access());
+ }
+
+ @Override
+ public Name getSimpleName() {
+ return new TurbineName(sym.name());
+ }
+
+ @Override
+ public Element getEnclosingElement() {
+ return factory.executableElement(sym.owner());
+ }
+
+ @Override
+ public List<? extends Element> getEnclosedElements() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public <R, P> R accept(ElementVisitor<R, P> v, P p) {
+ return v.visitVariable(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(sym.name());
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return info().annotations();
+ }
+ }
+
+ static class TurbineNoTypeElement implements TypeElement {
+
+ private final ModelFactory factory;
+ private final String name;
+
+ public TurbineNoTypeElement(ModelFactory factory, String name) {
+ this.factory = factory;
+ this.name = requireNonNull(name);
+ }
+
+ @Override
+ public TypeMirror asType() {
+ return factory.noType();
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.CLASS;
+ }
+
+ @Override
+ public Set<Modifier> getModifiers() {
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public Name getSimpleName() {
+ return new TurbineName(name.substring(name.lastIndexOf('.') + 1));
+ }
+
+ @Override
+ public TypeMirror getSuperclass() {
+ return factory.noType();
+ }
+
+ @Override
+ public List<? extends TypeMirror> getInterfaces() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public List<? extends TypeParameterElement> getTypeParameters() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public Element getEnclosingElement() {
+ int idx = name.lastIndexOf('.');
+ String packageName;
+ if (idx == -1) {
+ packageName = "";
+ } else {
+ packageName = name.substring(0, idx).replace('.', '/');
+ }
+ return factory.packageElement(new PackageSymbol(packageName));
+ }
+
+ @Override
+ public List<? extends Element> getEnclosedElements() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public NestingKind getNestingKind() {
+ return NestingKind.TOP_LEVEL;
+ }
+
+ @Override
+ public Name getQualifiedName() {
+ return new TurbineName(name);
+ }
+
+ @Override
+ public List<? extends AnnotationMirror> getAnnotationMirrors() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public <A extends Annotation> A getAnnotation(Class<A> aClass) {
+ return null;
+ }
+
+ @Override
+ public <A extends Annotation> A[] getAnnotationsByType(Class<A> aClass) {
+ return null;
+ }
+
+ @Override
+ public <R, P> R accept(ElementVisitor<R, P> elementVisitor, P p) {
+ return elementVisitor.visitType(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return getSimpleName().toString();
+ }
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineElements.java b/java/com/google/turbine/processing/TurbineElements.java
new file mode 100644
index 0000000..9da210e
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineElements.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.MultimapBuilder;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.binder.sym.PackageSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.model.Const;
+import com.google.turbine.model.TurbineVisibility;
+import com.google.turbine.processing.TurbineElement.TurbineExecutableElement;
+import com.google.turbine.processing.TurbineElement.TurbineFieldElement;
+import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineExecutableType;
+import com.google.turbine.type.AnnoInfo;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+
+/** An implementation of {@link Elements} backed by turbine's {@link Element}. */
+public class TurbineElements implements Elements {
+
+ private final ModelFactory factory;
+ private final TurbineTypes types;
+
+ public TurbineElements(ModelFactory factory, TurbineTypes types) {
+ this.factory = factory;
+ this.types = types;
+ }
+
+ private static Symbol asSymbol(Element element) {
+ if (!(element instanceof TurbineElement)) {
+ throw new IllegalArgumentException(element.toString());
+ }
+ return ((TurbineElement) element).sym();
+ }
+
+ @Override
+ public PackageElement getPackageElement(CharSequence name) {
+ ImmutableList<String> packageName = ImmutableList.copyOf(Splitter.on('.').split(name));
+ if (factory.tli().lookupPackage(packageName) == null) {
+ return null;
+ }
+ return factory.packageElement(new PackageSymbol(Joiner.on('/').join(packageName)));
+ }
+
+ @Override
+ public TypeElement getTypeElement(CharSequence name) {
+ ClassSymbol sym = factory.inferSymbol(name);
+ if (sym == null) {
+ return null;
+ }
+ if (factory.getSymbol(sym) == null) {
+ return null;
+ }
+ return factory.typeElement(sym);
+ }
+
+ @Override
+ public Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValuesWithDefaults(
+ AnnotationMirror a) {
+ return ((TurbineAnnotationMirror) a).getElementValuesWithDefaults();
+ }
+
+ @Override
+ public String getDocComment(Element e) {
+ if (!(e instanceof TurbineElement)) {
+ throw new IllegalArgumentException(e.toString());
+ }
+ String comment = ((TurbineElement) e).javadoc();
+ if (comment == null) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (String line : Splitter.on('\n').split(comment)) {
+ int start = 0;
+ if (!first) {
+ sb.append('\n');
+ while (start < line.length() && CharMatcher.whitespace().matches(line.charAt(start))) {
+ start++;
+ }
+ while (start < line.length() && line.charAt(start) == '*') {
+ start++;
+ }
+ }
+ sb.append(line, start, line.length());
+ first = false;
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public boolean isDeprecated(Element element) {
+ if (!(element instanceof TurbineElement)) {
+ throw new IllegalArgumentException(element.toString());
+ }
+ for (AnnoInfo a : ((TurbineTypeElement) element).annos()) {
+ if (a.sym().equals(ClassSymbol.DEPRECATED)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Name getBinaryName(TypeElement element) {
+ if (!(element instanceof TurbineTypeElement)) {
+ throw new IllegalArgumentException(element.toString());
+ }
+ return getName(((TurbineTypeElement) element).sym().binaryName().replace('/', '.'));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws IllegalArgumentException for module elements
+ */
+ @Override
+ public PackageElement getPackageOf(Element element) {
+ Symbol sym = asSymbol(element);
+ return factory.packageElement(packageSymbol(sym));
+ }
+
+ private static PackageSymbol packageSymbol(Symbol sym) {
+ if (sym.symKind().equals(Symbol.Kind.PACKAGE)) {
+ return (PackageSymbol) sym;
+ }
+ return ModelFactory.enclosingClass(sym).owner();
+ }
+
+ @Override
+ public List<? extends Element> getAllMembers(TypeElement type) {
+ ClassSymbol s = (ClassSymbol) asSymbol(type);
+ PackageSymbol from = packageSymbol(s);
+
+ // keep track of processed methods grouped by their names, to handle overrides more efficiently
+ Multimap<String, TurbineExecutableElement> methods =
+ MultimapBuilder.linkedHashKeys().linkedHashSetValues().build();
+
+ // collect all members of each transitive supertype of the input
+ ImmutableList.Builder<Element> results = ImmutableList.builder();
+ for (ClassSymbol superType : factory.cha().transitiveSupertypes(s)) {
+ // Most of JSR-269 is implemented on top of turbine's model, instead of the Element and
+ // TypeMirror wrappers. We don't do that here because we need most of the Elements returned
+ // by getEnclosedElements anyways, and the work below benefits from some of the caching done
+ // by TurbineElement.
+ for (Element el : factory.typeElement(superType).getEnclosedElements()) {
+ Symbol sym = asSymbol(el);
+ switch (sym.symKind()) {
+ case METHOD:
+ TurbineExecutableElement m = (TurbineExecutableElement) el;
+ if (shouldAdd(s, from, methods, m)) {
+ methods.put(m.info().name(), m);
+ results.add(el);
+ }
+ break;
+ case FIELD:
+ if (shouldAdd(s, from, (TurbineFieldElement) el)) {
+ results.add(el);
+ }
+ break;
+ default:
+ results.add(el);
+ }
+ }
+ }
+ return results.build();
+ }
+
+ private boolean shouldAdd(
+ ClassSymbol s,
+ PackageSymbol from,
+ Multimap<String, TurbineExecutableElement> methods,
+ TurbineExecutableElement m) {
+ if (m.sym().owner().equals(s)) {
+ // always include methods (and constructors) declared in the given type
+ return true;
+ }
+ if (m.getKind() == ElementKind.CONSTRUCTOR) {
+ // skip constructors from super-types, because the spec says so
+ return false;
+ }
+ if (!isVisible(from, packageSymbol(m.sym()), TurbineVisibility.fromAccess(m.info().access()))) {
+ // skip invisible methods in supers
+ return false;
+ }
+ // otherwise check if we've seen methods that override, or are overridden by, the
+ // current method
+ Set<TurbineExecutableElement> overrides = new HashSet<>();
+ Set<TurbineExecutableElement> overridden = new HashSet<>();
+ String name = m.info().name();
+ for (TurbineExecutableElement other : methods.get(name)) {
+ if (overrides(m, other, (TypeElement) m.getEnclosingElement())) {
+ overrides.add(other);
+ continue;
+ }
+ if (overrides(other, m, (TypeElement) other.getEnclosingElement())) {
+ overridden.add(other);
+ continue;
+ }
+ }
+ if (!overridden.isEmpty()) {
+ // We've already processed method(s) that override this one; nothing to do here.
+ // If that's true, and we've *also* processed a methods that this one overrides,
+ // something has gone terribly wrong: since overriding is transitive the results
+ // contain a pair of methods that override each other.
+ checkState(overrides.isEmpty());
+ return false;
+ }
+ // Add this method, and remove any methods we've already processed that it overrides.
+ for (TurbineExecutableElement override : overrides) {
+ methods.remove(name, override);
+ }
+ return true;
+ }
+
+ private static boolean shouldAdd(ClassSymbol s, PackageSymbol from, TurbineFieldElement f) {
+ FieldSymbol sym = f.sym();
+ if (sym.owner().equals(s)) {
+ // always include fields declared in the given type
+ return true;
+ }
+ if (!isVisible(from, packageSymbol(sym), TurbineVisibility.fromAccess(f.info().access()))) {
+ // skip invisible fields in supers
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if an element with the given {@code visibility} and located in package {@from} is
+ * visible to elements in package {@code to}.
+ */
+ private static boolean isVisible(
+ PackageSymbol from, PackageSymbol to, TurbineVisibility visibility) {
+ switch (visibility) {
+ case PUBLIC:
+ case PROTECTED:
+ break;
+ case PACKAGE:
+ return from.equals(to);
+ case PRIVATE:
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public List<? extends AnnotationMirror> getAllAnnotationMirrors(Element element) {
+ return ((TurbineElement) element).getAllAnnotationMirrors();
+ }
+
+ @Override
+ public boolean hides(Element hider, Element hidden) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean overrides(
+ ExecutableElement overrider, ExecutableElement overridden, TypeElement type) {
+ if (!overrider.getSimpleName().contentEquals(overridden.getSimpleName())) {
+ return false;
+ }
+ TypeMirror a = overrider.asType();
+ TypeMirror b = types.asMemberOf((DeclaredType) type.asType(), overridden);
+ if (b == null) {
+ return false;
+ }
+ if (!types.isSubsignature((TurbineExecutableType) a, (TurbineExecutableType) b)) {
+ return false;
+ }
+ return isVisible(
+ packageSymbol(asSymbol(overrider)),
+ packageSymbol(asSymbol(overridden)),
+ TurbineVisibility.fromAccess(((TurbineExecutableElement) overridden).info().access()));
+ }
+
+ @Override
+ public String getConstantExpression(Object value) {
+ if (value instanceof Byte) {
+ return new Const.ByteValue((Byte) value).toString();
+ }
+ if (value instanceof Long) {
+ return new Const.LongValue((Long) value).toString();
+ }
+ if (value instanceof Float) {
+ return new Const.FloatValue((Float) value).toString();
+ }
+ if (value instanceof Double) {
+ return new Const.DoubleValue((Double) value).toString();
+ }
+ if (value instanceof Short) {
+ // Special-case short for consistency with javac, see:
+ // https://bugs.openjdk.java.net/browse/JDK-8227617
+ return String.format("(short)%d", (Short) value);
+ }
+ if (value instanceof String) {
+ return new Const.StringValue((String) value).toString();
+ }
+ if (value instanceof Character) {
+ return new Const.CharValue((Character) value).toString();
+ }
+ return String.valueOf(value);
+ }
+
+ @Override
+ public void printElements(Writer w, Element... elements) {
+ PrintWriter pw = new PrintWriter(w, true);
+ for (Element element : elements) {
+ pw.println(element.toString());
+ }
+ }
+
+ @Override
+ public Name getName(CharSequence cs) {
+ return new TurbineName(cs.toString());
+ }
+
+ @Override
+ public boolean isFunctionalInterface(TypeElement type) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineFiler.java b/java/com/google/turbine/processing/TurbineFiler.java
new file mode 100644
index 0000000..186eb7f
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineFiler.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.ByteStreams;
+import com.google.turbine.diag.SourceFile;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.FilerException;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.tools.FileObject;
+import javax.tools.JavaFileManager.Location;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.StandardLocation;
+
+/** Turbine's implementation of {@link Filer}. */
+public class TurbineFiler implements Filer {
+
+ /**
+ * Existing paths of file objects that cannot be regenerated, including the original compilation
+ * inputs and source or class files generated during any annotation processing round.
+ */
+ private final Set<String> seen;
+
+ /**
+ * File objects generated during the current processing round. Each entry has a unique path, which
+ * is enforced by {@link #seen}.
+ */
+ private final List<TurbineJavaFileObject> files = new ArrayList<>();
+
+ /** Loads resources from the classpath. */
+ private final Function<String, Supplier<byte[]>> classPath;
+
+ /** The {@link ClassLoader} for the annotation processor path, for loading resources. */
+ private final ClassLoader loader;
+
+ private final Map<String, SourceFile> generatedSources = new LinkedHashMap<>();
+ private final Map<String, byte[]> generatedClasses = new LinkedHashMap<>();
+
+ /** Generated source file objects from all rounds. */
+ public ImmutableMap<String, SourceFile> generatedSources() {
+ return ImmutableMap.copyOf(generatedSources);
+ }
+
+ /** Generated class file objects from all rounds. */
+ public ImmutableMap<String, byte[]> generatedClasses() {
+ return ImmutableMap.copyOf(generatedClasses);
+ }
+
+ public TurbineFiler(
+ Set<String> seen, Function<String, Supplier<byte[]>> classPath, ClassLoader loader) {
+ this.seen = seen;
+ this.classPath = classPath;
+ this.loader = loader;
+ }
+
+ /**
+ * Called when the current annotation processing round is complete, and returns the sources
+ * generated in that round.
+ */
+ public Collection<SourceFile> finishRound() {
+ Map<String, SourceFile> roundSources = new LinkedHashMap<>();
+ for (TurbineJavaFileObject e : files) {
+ String path = e.getName();
+ switch (e.getKind()) {
+ case SOURCE:
+ roundSources.put(path, new SourceFile(path, e.contents()));
+ break;
+ case CLASS:
+ generatedClasses.put(path, e.bytes());
+ break;
+ case OTHER:
+ switch (e.location()) {
+ case CLASS_OUTPUT:
+ generatedClasses.put(path, e.bytes());
+ break;
+ case SOURCE_OUTPUT:
+ this.generatedSources.put(path, new SourceFile(path, e.contents()));
+ break;
+ default:
+ throw new AssertionError(e.location());
+ }
+ break;
+ case HTML:
+ throw new UnsupportedOperationException(String.valueOf(e.getKind()));
+ }
+ }
+ files.clear();
+ this.generatedSources.putAll(roundSources);
+ return roundSources.values();
+ }
+
+ @Override
+ public JavaFileObject createSourceFile(CharSequence n, Element... originatingElements)
+ throws IOException {
+ String name = n.toString();
+ checkArgument(!name.contains("/"), "invalid type name: %s", name);
+ return create(StandardLocation.SOURCE_OUTPUT, Kind.SOURCE, name.replace('.', '/') + ".java");
+ }
+
+ @Override
+ public JavaFileObject createClassFile(CharSequence n, Element... originatingElements)
+ throws IOException {
+ String name = n.toString();
+ checkArgument(!name.contains("/"), "invalid type name: %s", name);
+ return create(StandardLocation.CLASS_OUTPUT, Kind.CLASS, name.replace('.', '/') + ".class");
+ }
+
+ @Override
+ public FileObject createResource(
+ Location location, CharSequence p, CharSequence r, Element... originatingElements)
+ throws IOException {
+ checkArgument(location instanceof StandardLocation, "%s", location);
+ String pkg = p.toString();
+ String relativeName = r.toString();
+ checkArgument(!pkg.contains("/"), "invalid package: %s", pkg);
+ String path = packageRelativePath(pkg, relativeName);
+ return create((StandardLocation) location, Kind.OTHER, path);
+ }
+
+ private JavaFileObject create(StandardLocation location, Kind kind, String path)
+ throws FilerException {
+ checkArgument(location.isOutputLocation());
+ if (!seen.add(path)) {
+ throw new FilerException("already created " + path);
+ }
+ TurbineJavaFileObject result = new TurbineJavaFileObject(location, kind, path);
+ files.add(result);
+ return result;
+ }
+
+ @Override
+ public FileObject getResource(Location location, CharSequence p, CharSequence r)
+ throws IOException {
+ String pkg = p.toString();
+ String relativeName = r.toString();
+ checkArgument(!pkg.contains("/"), "invalid package: %s", pkg);
+ checkArgument(location instanceof StandardLocation, "unsupported location %s", location);
+ StandardLocation standardLocation = (StandardLocation) location;
+ String path = packageRelativePath(pkg, relativeName);
+ switch (standardLocation) {
+ case CLASS_OUTPUT:
+ byte[] generated = generatedClasses.get(path);
+ if (generated == null) {
+ throw new FileNotFoundException(path);
+ }
+ return new BytesFileObject(path, Suppliers.ofInstance(generated));
+ case SOURCE_OUTPUT:
+ SourceFile source = generatedSources.get(path);
+ if (source == null) {
+ throw new FileNotFoundException(path);
+ }
+ return new SourceFileObject(path, source.source());
+ case ANNOTATION_PROCESSOR_PATH:
+ if (loader.getResource(path) == null) {
+ throw new FileNotFoundException(path);
+ }
+ return new ResourceFileObject(loader, path);
+ case CLASS_PATH:
+ Supplier<byte[]> bytes = classPath.apply(path);
+ if (bytes == null) {
+ throw new FileNotFoundException(path);
+ }
+ return new BytesFileObject(path, bytes);
+ default:
+ throw new IllegalArgumentException(standardLocation.getName());
+ }
+ }
+
+ private static String packageRelativePath(String pkg, String relativeName) {
+ if (pkg.isEmpty()) {
+ return relativeName;
+ }
+ return pkg.replace('.', '/') + '/' + relativeName;
+ }
+
+ private abstract static class ReadOnlyFileObject implements FileObject {
+
+ protected final String path;
+
+ public ReadOnlyFileObject(String path) {
+ this.path = path;
+ }
+
+ @Override
+ public final String getName() {
+ return path;
+ }
+
+ @Override
+ public URI toUri() {
+ return URI.create("file://" + path);
+ }
+
+ @Override
+ public final OutputStream openOutputStream() {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public final Writer openWriter() {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public final long getLastModified() {
+ return 0;
+ }
+
+ @Override
+ public final boolean delete() {
+ throw new IllegalStateException();
+ }
+ }
+
+ private abstract static class WriteOnlyFileObject implements FileObject {
+
+ @Override
+ public final InputStream openInputStream() {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public final Reader openReader(boolean ignoreEncodingErrors) {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public final CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ throw new IllegalStateException();
+ }
+ }
+
+ private static class TurbineJavaFileObject extends WriteOnlyFileObject implements JavaFileObject {
+
+ private final StandardLocation location;
+ private final Kind kind;
+ private final CharSequence name;
+ private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ public TurbineJavaFileObject(StandardLocation location, Kind kind, CharSequence name) {
+ this.location = location;
+ this.kind = kind;
+ this.name = name;
+ }
+
+ @Override
+ public Kind getKind() {
+ return kind;
+ }
+
+ @Override
+ public boolean isNameCompatible(String simpleName, Kind kind) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NestingKind getNestingKind() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Modifier getAccessLevel() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public URI toUri() {
+ return URI.create("file://" + name + kind.extension);
+ }
+
+ @Override
+ public String getName() {
+ return name.toString();
+ }
+
+ @Override
+ public OutputStream openOutputStream() {
+ return baos;
+ }
+
+ @Override
+ public Writer openWriter() {
+ return new OutputStreamWriter(openOutputStream(), UTF_8);
+ }
+
+ @Override
+ public long getLastModified() {
+ return 0;
+ }
+
+ @Override
+ public boolean delete() {
+ throw new IllegalStateException();
+ }
+
+ public byte[] bytes() {
+ return baos.toByteArray();
+ }
+
+ public String contents() {
+ return new String(baos.toByteArray(), UTF_8);
+ }
+
+ public StandardLocation location() {
+ return location;
+ }
+ }
+
+ private static class ResourceFileObject extends ReadOnlyFileObject {
+
+ private final ClassLoader loader;
+
+ public ResourceFileObject(ClassLoader loader, String path) {
+ super(path);
+ this.loader = loader;
+ }
+
+ @Override
+ public URI toUri() {
+ try {
+ return loader.getResource(path).toURI();
+ } catch (URISyntaxException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public InputStream openInputStream() {
+ return loader.getResourceAsStream(path);
+ }
+
+ @Override
+ public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
+ return new InputStreamReader(openInputStream(), UTF_8);
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+ return new String(ByteStreams.toByteArray(openInputStream()), UTF_8);
+ }
+ }
+
+ private static class BytesFileObject extends ReadOnlyFileObject {
+
+ private final Supplier<byte[]> bytes;
+
+ public BytesFileObject(String path, Supplier<byte[]> bytes) {
+ super(path);
+ this.bytes = bytes;
+ }
+
+ @Override
+ public InputStream openInputStream() {
+ return new ByteArrayInputStream(bytes.get());
+ }
+
+ @Override
+ public Reader openReader(boolean ignoreEncodingErrors) {
+ return new InputStreamReader(openInputStream(), UTF_8);
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return new String(bytes.get(), UTF_8);
+ }
+ }
+
+ private static class SourceFileObject extends ReadOnlyFileObject {
+
+ private final String source;
+
+ public SourceFileObject(String path, String source) {
+ super(path);
+ this.source = source;
+ }
+
+ @Override
+ public InputStream openInputStream() {
+ return new ByteArrayInputStream(source.getBytes(UTF_8));
+ }
+
+ @Override
+ public Reader openReader(boolean ignoreEncodingErrors) {
+ return new StringReader(source);
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return source;
+ }
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineMessager.java b/java/com/google/turbine/processing/TurbineMessager.java
new file mode 100644
index 0000000..9c333b2
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineMessager.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2019 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.processing;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.diag.SourceFile;
+import com.google.turbine.diag.TurbineError;
+import com.google.turbine.diag.TurbineLog;
+import com.google.turbine.model.Const;
+import com.google.turbine.processing.TurbineElement.TurbineNoTypeElement;
+import com.google.turbine.tree.Tree;
+import com.google.turbine.type.AnnoInfo;
+import java.util.Iterator;
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.tools.Diagnostic;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/** Turbine's implementation of {@link Messager}. */
+public class TurbineMessager implements Messager {
+ private final ModelFactory factory;
+ private final TurbineLog log;
+
+ public TurbineMessager(ModelFactory factory, TurbineLog log) {
+ this.factory = factory;
+ this.log = log;
+ }
+
+ @Override
+ public void printMessage(Diagnostic.Kind kind, CharSequence msg) {
+ // TODO(cushon): null-check `msg` after fixing affected processors
+ log.diagnostic(kind, String.valueOf(msg));
+ }
+
+ @Override
+ public void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e) {
+ if (e == null || e instanceof TurbineNoTypeElement) {
+ printMessage(kind, msg);
+ return;
+ }
+ Symbol sym = ((TurbineElement) e).sym();
+ SourceFile source = getSource(sym);
+ int position = getPosition(sym);
+ log.withSource(source).diagnostic(kind, position, TurbineError.ErrorKind.PROC, msg);
+ }
+
+ @Override
+ public void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e, AnnotationMirror a) {
+ if (a == null || e == null || e instanceof TurbineNoTypeElement) {
+ printMessage(kind, msg, e);
+ return;
+ }
+ SourceFile source = getSource(((TurbineElement) e).sym());
+ int position = ((TurbineAnnotationMirror) a).anno().tree().position();
+ log.withSource(source).diagnostic(kind, position, TurbineError.ErrorKind.PROC, msg);
+ }
+
+ @Override
+ public void printMessage(
+ Diagnostic.Kind kind, CharSequence msg, Element e, AnnotationMirror a, AnnotationValue v) {
+ if (a == null || e == null || e instanceof TurbineNoTypeElement || v == null) {
+ printMessage(kind, msg, e, a);
+ return;
+ }
+ SourceFile source = getSource(((TurbineElement) e).sym());
+ AnnoInfo anno = ((TurbineAnnotationMirror) a).anno();
+ int position = locateInAnnotation(((TurbineAnnotationValueMirror) v).value(), anno);
+ if (position == -1) {
+ position = anno.tree().position();
+ }
+ log.withSource(source).diagnostic(kind, position, TurbineError.ErrorKind.PROC, msg);
+ }
+
+ /**
+ * Returns the {@link SourceFile} that contains the declaration of the given {@link Symbol}, or
+ * {@code null} if the symbol was not compiled from source.
+ */
+ @Nullable
+ private SourceFile getSource(Symbol sym) {
+ ClassSymbol encl = ModelFactory.enclosingClass(sym);
+ TypeBoundClass info = factory.getSymbol(encl);
+ if (!(info instanceof SourceTypeBoundClass)) {
+ return null;
+ }
+ return ((SourceTypeBoundClass) info).source();
+ }
+
+ /**
+ * Returns the position of the given {@link Symbol}'s declaration, or {@code null} if it was not
+ * compiled from source.
+ */
+ private int getPosition(Symbol sym) {
+ switch (sym.symKind()) {
+ case CLASS:
+ return classPosition((ClassSymbol) sym);
+ case TY_PARAM:
+ return tyParamPosition((TyVarSymbol) sym);
+ case METHOD:
+ return methodPosition((MethodSymbol) sym);
+ case FIELD:
+ return fieldPosition((FieldSymbol) sym);
+ case PARAMETER:
+ return paramPosition((ParamSymbol) sym);
+ case MODULE:
+ case PACKAGE:
+ break;
+ }
+ throw new AssertionError(sym.symKind());
+ }
+
+ private int fieldPosition(FieldSymbol sym) {
+ Tree.VarDecl decl = factory.getFieldInfo(sym).decl();
+ return decl != null ? decl.position() : -1;
+ }
+
+ private int paramPosition(ParamSymbol sym) {
+ MethodInfo minfo = factory.getMethodInfo(sym.owner());
+ if (minfo.decl() == null) {
+ return -1;
+ }
+ int idx = minfo.parameters().indexOf(factory.getParamInfo(sym));
+ return minfo.decl().params().get(idx).position();
+ }
+
+ private int methodPosition(MethodSymbol sym) {
+ MethodInfo methodInfo = factory.getMethodInfo(sym);
+ Tree.MethDecl decl = methodInfo.decl();
+ if (decl != null) {
+ return decl.position();
+ }
+ // use the enclosing class position for synthetic methods
+ int position = classPosition(sym.owner());
+ if (position == -1) {
+ return -1;
+ }
+ // TODO(b/139079081): change diagnostic position of declarations instead of the -= 6 fixup
+ position -= 6; // adjust to start of `class ` for parity with javac
+ return position;
+ }
+
+ private int classPosition(ClassSymbol owner) {
+ TypeBoundClass symbol = factory.getSymbol(owner);
+ if (!(symbol instanceof SourceTypeBoundClass)) {
+ return -1;
+ }
+ return ((SourceTypeBoundClass) symbol).decl().position();
+ }
+
+ private int tyParamPosition(TyVarSymbol sym) {
+ TyVarSymbol tyVarSymbol = sym;
+ Symbol owner = tyVarSymbol.owner();
+ ImmutableMap<TyVarSymbol, TyVarInfo> tyVars;
+ ImmutableList<Tree.TyParam> trees;
+ switch (owner.symKind()) {
+ case CLASS:
+ TypeBoundClass cinfo = factory.getSymbol((ClassSymbol) owner);
+ if (!(cinfo instanceof SourceTypeBoundClass)) {
+ return -1;
+ }
+ tyVars = cinfo.typeParameterTypes();
+ trees = ((SourceTypeBoundClass) cinfo).decl().typarams();
+ break;
+ case METHOD:
+ MethodInfo minfo = factory.getMethodInfo((MethodSymbol) owner);
+ if (minfo.decl() == null) {
+ return -1;
+ }
+ tyVars = minfo.tyParams();
+ trees = minfo.decl().typarams();
+ break;
+ default:
+ throw new AssertionError(owner.symKind());
+ }
+ return trees.get(tyVars.keySet().asList().indexOf(tyVarSymbol)).position();
+ }
+
+ /** Returns the position of the given annotation value {@code v} in the given annotation. */
+ private static int locateInAnnotation(Const v, AnnoInfo anno) {
+ return locate(v, anno.values().values().asList(), anno.tree().args());
+ }
+
+ /**
+ * Returns the position of the given annotation value {@code toFind} within the given constant
+ * {@code v} (which may be a compound value, i.e. a nested annotation or array value), and given
+ * the corresponding expression tree.
+ */
+ private static int locate(Const toFind, Const v, Tree.Expression t) {
+ // the element name can be omitted for `value`, e.g. in `@A({1, 2, 3})`
+ t = t.kind().equals(Tree.Kind.ASSIGN) ? ((Tree.Assign) t).expr() : t;
+ if (toFind.equals(v)) {
+ return t.position();
+ }
+ switch (v.kind()) {
+ case ARRAY:
+ ImmutableList<Tree.Expression> elements =
+ t.kind().equals(Tree.Kind.ARRAY_INIT)
+ ? ((Tree.ArrayInit) t).exprs()
+ : ImmutableList.of(t);
+ return locate(toFind, ((Const.ArrayInitValue) v).elements(), elements);
+ case ANNOTATION:
+ return locateInAnnotation(toFind, ((TurbineAnnotationValue) v).info());
+ default:
+ return -1;
+ }
+ }
+
+ /**
+ * Returns the position of the given annotation value {@code toFind}, given a list of annotation
+ * values corresponding to the element values of a (possibly nested) annotation or an array value,
+ * and the corresponding expression trees.
+ */
+ private static int locate(
+ Const toFind, ImmutableList<Const> vx, ImmutableList<Tree.Expression> tx) {
+ Iterator<Const> vi = vx.iterator();
+ Iterator<Tree.Expression> ti = tx.iterator();
+ while (vi.hasNext()) {
+ int result = locate(toFind, vi.next(), ti.next());
+ if (result != -1) {
+ return result;
+ }
+ }
+ return -1;
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineName.java b/java/com/google/turbine/processing/TurbineName.java
new file mode 100644
index 0000000..584b1b1
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineName.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static java.util.Objects.requireNonNull;
+
+import javax.lang.model.element.Name;
+
+/** An implementation of {@link Name} backed by a {@link CharSequence}. */
+public class TurbineName implements Name {
+
+ private final String name;
+
+ public TurbineName(String name) {
+ requireNonNull(name);
+ this.name = name;
+ }
+
+ @Override
+ public boolean contentEquals(CharSequence cs) {
+ return name.contentEquals(cs);
+ }
+
+ @Override
+ public int length() {
+ return name.length();
+ }
+
+ @Override
+ public char charAt(int index) {
+ return name.charAt(index);
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ return name.subSequence(start, end);
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineName && contentEquals(((TurbineName) obj).name);
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineProcessingEnvironment.java b/java/com/google/turbine/processing/TurbineProcessingEnvironment.java
new file mode 100644
index 0000000..726d075
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineProcessingEnvironment.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2019 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.processing;
+
+import java.util.Locale;
+import java.util.Map;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/** Turbine's {@link ProcessingEnvironment). */
+public class TurbineProcessingEnvironment implements ProcessingEnvironment {
+
+ private final Filer filer;
+ private final Types types;
+ private final Map<String, String> processorOptions;
+ private final Elements elements;
+ private final Map<String, byte[]> statistics;
+ private final SourceVersion sourceVersion;
+ private final Messager messager;
+ private final ClassLoader processorLoader;
+
+ public TurbineProcessingEnvironment(
+ Filer filer,
+ Types types,
+ Elements elements,
+ Messager messager,
+ Map<String, String> processorOptions,
+ SourceVersion sourceVersion,
+ @Nullable ClassLoader processorLoader,
+ Map<String, byte[]> statistics) {
+ this.filer = filer;
+ this.types = types;
+ this.processorOptions = processorOptions;
+ this.sourceVersion = sourceVersion;
+ this.elements = elements;
+ this.statistics = statistics;
+ this.messager = messager;
+ this.processorLoader = processorLoader;
+ }
+
+ @Override
+ public Map<String, String> getOptions() {
+ return processorOptions;
+ }
+
+ @Override
+ public Messager getMessager() {
+ return messager;
+ }
+
+ @Override
+ public Filer getFiler() {
+ return filer;
+ }
+
+ @Override
+ public Elements getElementUtils() {
+ return elements;
+ }
+
+ @Override
+ public Types getTypeUtils() {
+ return types;
+ }
+
+ @Override
+ public SourceVersion getSourceVersion() {
+ return sourceVersion;
+ }
+
+ @Override
+ public Locale getLocale() {
+ return Locale.ENGLISH;
+ }
+
+ public ClassLoader processorLoader() {
+ return processorLoader;
+ }
+
+ public void addStatistics(String key, byte[] extension) {
+ byte[] existing = statistics.put(key, extension);
+ if (existing != null) {
+ throw new IllegalStateException("duplicate statistics reported for " + key);
+ }
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineRoundEnvironment.java b/java/com/google/turbine/processing/TurbineRoundEnvironment.java
new file mode 100644
index 0000000..d63a4e8
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineRoundEnvironment.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2019 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.processing;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/** A {@link RoundEnvironment}. */
+public class TurbineRoundEnvironment implements RoundEnvironment {
+
+ private final ModelFactory factory;
+ private final ImmutableSet<ClassSymbol> syms;
+ private final boolean processingOver;
+ private final boolean errorRaised;
+ private final ImmutableSetMultimap<ClassSymbol, Symbol> allAnnotations;
+
+ // the round environment doesn't outlive the round, so don't worry about resetting this cache
+ private final Supplier<ImmutableSet<TurbineTypeElement>> rootElements =
+ Suppliers.memoize(
+ new Supplier<ImmutableSet<TurbineTypeElement>>() {
+ @Override
+ public ImmutableSet<TurbineTypeElement> get() {
+ ImmutableSet.Builder<TurbineTypeElement> result = ImmutableSet.builder();
+ for (ClassSymbol sym : syms) {
+ if (sym.simpleName().contains("$") && factory.getSymbol(sym).owner() != null) {
+ continue;
+ }
+ if (sym.simpleName().equals("package-info")) {
+ continue;
+ }
+ result.add(factory.typeElement(sym));
+ }
+ return result.build();
+ }
+ });
+
+ public TurbineRoundEnvironment(
+ ModelFactory factory,
+ ImmutableSet<ClassSymbol> syms,
+ boolean processingOver,
+ boolean errorRaised,
+ ImmutableSetMultimap<ClassSymbol, Symbol> allAnnotations) {
+ this.factory = factory;
+ this.syms = syms;
+ this.processingOver = processingOver;
+ this.errorRaised = errorRaised;
+ this.allAnnotations = allAnnotations;
+ }
+
+ @Override
+ public boolean processingOver() {
+ return processingOver;
+ }
+
+ @Override
+ public boolean errorRaised() {
+ return errorRaised;
+ }
+
+ @Override
+ public ImmutableSet<TurbineTypeElement> getRootElements() {
+ return rootElements.get();
+ }
+
+ @Override
+ public Set<? extends Element> getElementsAnnotatedWith(TypeElement a) {
+ return factory.elements(allAnnotations.get(((TurbineTypeElement) a).sym()));
+ }
+
+ @Override
+ public Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a) {
+ return getElementsAnnotatedWith(
+ factory.typeElement(new ClassSymbol(a.getName().replace('.', '/'))));
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineTypeMirror.java b/java/com/google/turbine/processing/TurbineTypeMirror.java
new file mode 100644
index 0000000..e94672c
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineTypeMirror.java
@@ -0,0 +1,770 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.collect.Iterables.getLast;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Ascii;
+import com.google.common.base.Joiner;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
+import com.google.turbine.binder.sym.PackageSymbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.model.TurbineConstantTypeKind;
+import com.google.turbine.model.TurbineFlag;
+import com.google.turbine.model.TurbineTyKind;
+import com.google.turbine.type.AnnoInfo;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.ArrayTy;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.ErrorTy;
+import com.google.turbine.type.Type.IntersectionTy;
+import com.google.turbine.type.Type.MethodTy;
+import com.google.turbine.type.Type.PrimTy;
+import com.google.turbine.type.Type.TyVar;
+import com.google.turbine.type.Type.WildTy;
+import com.google.turbine.type.Type.WildTy.BoundKind;
+import java.lang.annotation.Annotation;
+import java.util.List;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ErrorType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.IntersectionType;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.NullType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.TypeVisitor;
+import javax.lang.model.type.WildcardType;
+
+/** A {@link TypeMirror} implementation backed by a {@link Type}. */
+public abstract class TurbineTypeMirror implements TypeMirror {
+
+ protected final ModelFactory factory;
+
+ protected TurbineTypeMirror(ModelFactory factory) {
+ this.factory = requireNonNull(factory);
+ }
+
+ protected abstract ImmutableList<AnnoInfo> annos();
+
+ @Override
+ public final List<? extends AnnotationMirror> getAnnotationMirrors() {
+ ImmutableList.Builder<AnnotationMirror> result = ImmutableList.builder();
+ for (AnnoInfo anno : annos()) {
+ result.add(TurbineAnnotationMirror.create(factory, anno));
+ }
+ return result.build();
+ }
+
+ @Override
+ public final <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public final <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType) {
+ throw new AssertionError();
+ }
+
+ public abstract Type asTurbineType();
+
+ @Override
+ public String toString() {
+ return asTurbineType().toString();
+ }
+
+ /** A {@link PrimitiveType} implementation backed by a {@link PrimTy}. */
+ static class TurbinePrimitiveType extends TurbineTypeMirror implements PrimitiveType {
+
+ @Override
+ public String toString() {
+ return Ascii.toLowerCase(type.primkind().toString());
+ }
+
+ @Override
+ public Type asTurbineType() {
+ return type;
+ }
+
+ public final PrimTy type;
+
+ TurbinePrimitiveType(ModelFactory factory, PrimTy type) {
+ super(factory);
+ if (type.primkind() == TurbineConstantTypeKind.STRING) {
+ throw new AssertionError(type);
+ }
+ this.type = type;
+ }
+
+ @Override
+ public TypeKind getKind() {
+ switch (type.primkind()) {
+ case CHAR:
+ return TypeKind.CHAR;
+ case SHORT:
+ return TypeKind.SHORT;
+ case INT:
+ return TypeKind.INT;
+ case LONG:
+ return TypeKind.LONG;
+ case FLOAT:
+ return TypeKind.FLOAT;
+ case DOUBLE:
+ return TypeKind.DOUBLE;
+ case BOOLEAN:
+ return TypeKind.BOOLEAN;
+ case BYTE:
+ return TypeKind.BYTE;
+ case NULL:
+ return TypeKind.NULL;
+ case STRING:
+ }
+ throw new AssertionError(type.primkind());
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitPrimitive(this, p);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return type.annos();
+ }
+ }
+
+ /** A {@link DeclaredType} implementation backed by a {@link ClassTy}. */
+ static class TurbineDeclaredType extends TurbineTypeMirror implements DeclaredType {
+
+ @Override
+ public int hashCode() {
+ return type.sym().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineDeclaredType && type.equals(((TurbineDeclaredType) obj).type);
+ }
+
+ @Override
+ public ClassTy asTurbineType() {
+ return type;
+ }
+
+ private final ClassTy type;
+
+ TurbineDeclaredType(ModelFactory factory, ClassTy type) {
+ super(factory);
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ return type.toString();
+ }
+
+ final Supplier<Element> element =
+ factory.memoize(
+ new Supplier<Element>() {
+ @Override
+ public Element get() {
+ return factory.typeElement(type.sym());
+ }
+ });
+
+ @Override
+ public Element asElement() {
+ return element.get();
+ }
+
+ final Supplier<TypeMirror> enclosing =
+ factory.memoize(
+ new Supplier<TypeMirror>() {
+ @Override
+ public TypeMirror get() {
+ TypeBoundClass info = factory.getSymbol(type.sym());
+ if (info != null
+ && info.owner() != null
+ && ((info.access() & TurbineFlag.ACC_STATIC) == 0)
+ && info.kind() == TurbineTyKind.CLASS) {
+ if (type.classes().size() > 1) {
+ return factory.asTypeMirror(
+ ClassTy.create(type.classes().subList(0, type.classes().size() - 1)));
+ }
+ return factory.asTypeMirror(ClassTy.asNonParametricClassTy(info.owner()));
+ }
+ return factory.noType();
+ }
+ });
+
+ @Override
+ public TypeMirror getEnclosingType() {
+ return enclosing.get();
+ }
+
+ final Supplier<ImmutableList<TypeMirror>> typeArguments =
+ factory.memoize(
+ new Supplier<ImmutableList<TypeMirror>>() {
+ @Override
+ public ImmutableList<TypeMirror> get() {
+ return factory.asTypeMirrors(getLast(type.classes()).targs());
+ }
+ });
+
+ @Override
+ public List<? extends TypeMirror> getTypeArguments() {
+ return typeArguments.get();
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.DECLARED;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitDeclared(this, p);
+ }
+
+ public ClassTy type() {
+ return type;
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return getLast(type.classes()).annos();
+ }
+ }
+
+ /** An {@link ArrayType} implementation backed by a {@link ArrayTy}. */
+ static class TurbineArrayType extends TurbineTypeMirror implements ArrayType {
+
+ @Override
+ public Type asTurbineType() {
+ return type;
+ }
+
+ private final ArrayTy type;
+
+ TurbineArrayType(ModelFactory factory, ArrayTy type) {
+ super(factory);
+ this.type = type;
+ }
+
+ @Override
+ public TypeMirror getComponentType() {
+ return factory.asTypeMirror(type.elementType());
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.ARRAY;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitArray(this, p);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return type.annos();
+ }
+ }
+
+ /** An {@link ErrorType} implementation backed by a {@link ErrorTy}. */
+ static class TurbineErrorType extends TurbineTypeMirror implements ErrorType {
+
+ private final ErrorTy type;
+
+ public TurbineErrorType(ModelFactory factory, ErrorTy type) {
+ super(factory);
+ this.type = type;
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.ERROR;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitError(this, p);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public Type asTurbineType() {
+ return type;
+ }
+
+ @Override
+ public Element asElement() {
+ return factory.noElement(type.name());
+ }
+
+ @Override
+ public TypeMirror getEnclosingType() {
+ return factory.noType();
+ }
+
+ @Override
+ public List<? extends TypeMirror> getTypeArguments() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public String toString() {
+ return type.toString();
+ }
+ }
+
+ /** A 'package type' implementation backed by a {@link PackageSymbol}. */
+ static class TurbinePackageType extends TurbineTypeMirror implements NoType {
+
+ @Override
+ public Type asTurbineType() {
+ throw new UnsupportedOperationException();
+ }
+
+ final PackageSymbol symbol;
+
+ TurbinePackageType(ModelFactory factory, PackageSymbol symbol) {
+ super(factory);
+ this.symbol = symbol;
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.PACKAGE;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitNoType(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return symbol.toString();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof TurbinePackageType
+ && symbol.equals(((TurbinePackageType) other).symbol);
+ }
+
+ @Override
+ public int hashCode() {
+ return symbol.hashCode();
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return ImmutableList.of();
+ }
+ }
+
+ /** The absence of a type, {@see javax.lang.model.util.Types#getNoType}. */
+ static class TurbineNoType extends TurbineTypeMirror implements NoType {
+
+ @Override
+ public Type asTurbineType() {
+ return Type.NONE;
+ }
+
+ TurbineNoType(ModelFactory factory) {
+ super(factory);
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.NONE;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitNoType(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "none";
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof TurbineNoType;
+ }
+
+ @Override
+ public int hashCode() {
+ return getKind().hashCode();
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return ImmutableList.of();
+ }
+ }
+
+ /** A void type, {@see javax.lang.model.util.Types#getNoType}. */
+ static class TurbineVoidType extends TurbineTypeMirror implements NoType {
+
+ @Override
+ public Type asTurbineType() {
+ return Type.VOID;
+ }
+
+ TurbineVoidType(ModelFactory factory) {
+ super(factory);
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.VOID;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitNoType(this, p);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return ImmutableList.of();
+ }
+ }
+
+ /** A {@link TypeVariable} implementation backed by a {@link TyVar}. */
+ static class TurbineTypeVariable extends TurbineTypeMirror implements TypeVariable {
+
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineTypeVariable && type.equals(((TurbineTypeVariable) obj).type);
+ }
+
+ @Override
+ public Type asTurbineType() {
+ return type;
+ }
+
+ private final TyVar type;
+
+ private final Supplier<TyVarInfo> info =
+ factory.memoize(
+ new Supplier<TyVarInfo>() {
+ @Override
+ public TyVarInfo get() {
+ return factory.getTyVarInfo(type.sym());
+ }
+ });
+
+ private TyVarInfo info() {
+ return info.get();
+ }
+
+ TurbineTypeVariable(ModelFactory factory, Type.TyVar type) {
+ super(factory);
+ this.type = type;
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.TYPEVAR;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitTypeVariable(this, p);
+ }
+
+ @Override
+ public Element asElement() {
+ return factory.typeParameterElement(type.sym());
+ }
+
+ @Override
+ public TypeMirror getUpperBound() {
+ return factory.asTypeMirror(info().upperBound());
+ }
+
+ @Override
+ public TypeMirror getLowerBound() {
+ return info().lowerBound() != null
+ ? factory.asTypeMirror(info().lowerBound())
+ : factory.noType();
+ }
+
+ @Override
+ public String toString() {
+ return type.toString();
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return type.annos();
+ }
+ }
+
+ /** A {@link WildcardType} implementation backed by a {@link WildTy}. */
+ static class TurbineWildcardType extends TurbineTypeMirror implements WildcardType {
+
+ @Override
+ public Type asTurbineType() {
+ return type;
+ }
+
+ private final WildTy type;
+
+ public TurbineWildcardType(ModelFactory factory, WildTy type) {
+ super(factory);
+ this.type = type;
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.WILDCARD;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitWildcard(this, p);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineWildcardType && type.equals(((TurbineWildcardType) obj).type);
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ @Override
+ public TypeMirror getExtendsBound() {
+ return type.boundKind() == BoundKind.UPPER ? factory.asTypeMirror(type.bound()) : null;
+ }
+
+ @Override
+ public TypeMirror getSuperBound() {
+ return type.boundKind() == BoundKind.LOWER ? factory.asTypeMirror(type.bound()) : null;
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return type.annotations();
+ }
+ }
+
+ /** A {@link IntersectionType} implementation backed by a {@link IntersectionTy}. */
+ static class TurbineIntersectionType extends TurbineTypeMirror implements IntersectionType {
+
+ @Override
+ public Type asTurbineType() {
+ return type;
+ }
+
+ private final IntersectionTy type;
+
+ TurbineIntersectionType(ModelFactory factory, IntersectionTy type) {
+ super(factory);
+ this.type = type;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineIntersectionType
+ && type.equals(((TurbineIntersectionType) obj).type);
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.INTERSECTION;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitIntersection(this, p);
+ }
+
+ final Supplier<ImmutableList<TypeMirror>> bounds =
+ factory.memoize(
+ new Supplier<ImmutableList<TypeMirror>>() {
+ @Override
+ public ImmutableList<TypeMirror> get() {
+ return factory.asTypeMirrors(TurbineTypes.getBounds(factory, type));
+ }
+ });
+
+ @Override
+ public List<? extends TypeMirror> getBounds() {
+ return bounds.get();
+ }
+
+ @Override
+ public String toString() {
+ return Joiner.on('&').join(getBounds());
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return ImmutableList.of();
+ }
+ }
+
+ /** A {@link NullType} implementation. */
+ public static class TurbineNullType extends TurbineTypeMirror implements NullType {
+
+ @Override
+ public Type asTurbineType() {
+ return Type.PrimTy.create(TurbineConstantTypeKind.NULL, ImmutableList.of());
+ }
+
+ public TurbineNullType(ModelFactory factory) {
+ super(factory);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof NullType;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.NULL;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitNull(this, p);
+ }
+ }
+
+ /** An {@link ExecutableType} implementation backed by a {@link MethodTy}. */
+ public static class TurbineExecutableType extends TurbineTypeMirror implements ExecutableType {
+
+ @Override
+ public String toString() {
+ return type.toString();
+ }
+
+ @Override
+ public MethodTy asTurbineType() {
+ return type;
+ }
+
+ public final MethodTy type;
+
+ TurbineExecutableType(ModelFactory factory, MethodTy type) {
+ super(factory);
+ this.type = type;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineExecutableType
+ && type.equals(((TurbineExecutableType) obj).type);
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ @Override
+ public List<? extends TypeVariable> getTypeVariables() {
+ ImmutableList.Builder<TypeVariable> result = ImmutableList.builder();
+ for (TyVarSymbol tyVar : type.tyParams()) {
+ result.add((TypeVariable) factory.asTypeMirror(TyVar.create(tyVar, ImmutableList.of())));
+ }
+ return result.build();
+ }
+
+ @Override
+ public TypeMirror getReturnType() {
+ return factory.asTypeMirror(type.returnType());
+ }
+
+ @Override
+ public List<? extends TypeMirror> getParameterTypes() {
+ return factory.asTypeMirrors(type.parameters());
+ }
+
+ @Override
+ public TypeMirror getReceiverType() {
+ return type.receiverType() != null
+ ? factory.asTypeMirror(type.receiverType())
+ : factory.noType();
+ }
+
+ @Override
+ public List<? extends TypeMirror> getThrownTypes() {
+ return factory.asTypeMirrors(type.thrown());
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.EXECUTABLE;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitExecutable(this, p);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return ImmutableList.of();
+ }
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineTypes.java b/java/com/google/turbine/processing/TurbineTypes.java
new file mode 100644
index 0000000..f65f921
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineTypes.java
@@ -0,0 +1,1132 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.model.TurbineConstantTypeKind;
+import com.google.turbine.model.TurbineTyKind;
+import com.google.turbine.processing.TurbineElement.TurbineExecutableElement;
+import com.google.turbine.processing.TurbineElement.TurbineFieldElement;
+import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
+import com.google.turbine.processing.TurbineElement.TurbineTypeParameterElement;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineDeclaredType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineErrorType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineTypeVariable;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.ArrayTy;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.ClassTy.SimpleClassTy;
+import com.google.turbine.type.Type.IntersectionTy;
+import com.google.turbine.type.Type.MethodTy;
+import com.google.turbine.type.Type.PrimTy;
+import com.google.turbine.type.Type.TyKind;
+import com.google.turbine.type.Type.TyVar;
+import com.google.turbine.type.Type.WildTy;
+import com.google.turbine.type.Type.WildTy.BoundKind;
+import com.google.turbine.type.Type.WildUnboundedTy;
+import com.google.turbine.types.Erasure;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.NullType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.Types;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/** An implementation of {@link Types} backed by turbine's {@link TypeMirror}. */
+public class TurbineTypes implements Types {
+
+ private final ModelFactory factory;
+
+ public TurbineTypes(ModelFactory factory) {
+ this.factory = factory;
+ }
+
+ private static Type asTurbineType(TypeMirror typeMirror) {
+ if (!(typeMirror instanceof TurbineTypeMirror)) {
+ throw new IllegalArgumentException(typeMirror.toString());
+ }
+ return ((TurbineTypeMirror) typeMirror).asTurbineType();
+ }
+
+ @Override
+ public Element asElement(TypeMirror t) {
+ switch (t.getKind()) {
+ case DECLARED:
+ return ((TurbineDeclaredType) t).asElement();
+ case TYPEVAR:
+ return ((TurbineTypeVariable) t).asElement();
+ case ERROR:
+ return ((TurbineErrorType) t).asElement();
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public boolean isSameType(TypeMirror a, TypeMirror b) {
+ Type t1 = asTurbineType(a);
+ Type t2 = asTurbineType(b);
+ if (t1.tyKind() == TyKind.WILD_TY || t2.tyKind() == TyKind.WILD_TY) {
+ // wild card types that appear at the top-level are never equal to each other.
+ // Note that generics parameterized by wildcards may be equal, so the recursive
+ // `isSameType(Type, Type)` below does handle wildcards.
+ return false;
+ }
+ return isSameType(t1, t2);
+ }
+
+ private boolean isSameType(Type a, Type b) {
+ switch (a.tyKind()) {
+ case PRIM_TY:
+ return b.tyKind() == TyKind.PRIM_TY && ((PrimTy) a).primkind() == ((PrimTy) b).primkind();
+ case VOID_TY:
+ return b.tyKind() == TyKind.VOID_TY;
+ case NONE_TY:
+ return b.tyKind() == TyKind.NONE_TY;
+ case CLASS_TY:
+ return isSameClassType((ClassTy) a, b);
+ case ARRAY_TY:
+ return b.tyKind() == TyKind.ARRAY_TY
+ && isSameType(((ArrayTy) a).elementType(), ((ArrayTy) b).elementType());
+ case TY_VAR:
+ return b.tyKind() == TyKind.TY_VAR && ((TyVar) a).sym().equals(((TyVar) b).sym());
+ case WILD_TY:
+ return isSameWildType((WildTy) a, b);
+ case INTERSECTION_TY:
+ return b.tyKind() == TyKind.INTERSECTION_TY
+ && isSameIntersectionType((IntersectionTy) a, (IntersectionTy) b);
+ case METHOD_TY:
+ return b.tyKind() == TyKind.METHOD_TY && isSameMethodType((MethodTy) a, (MethodTy) b);
+ case ERROR_TY:
+ return false;
+ }
+ throw new AssertionError(a.tyKind());
+ }
+
+ /**
+ * Returns true if the given method types are equivalent.
+ *
+ * <p>Receiver parameters are ignored, regardless of whether they were explicitly specified in
+ * source. Thrown exception types are also ignored.
+ */
+ private boolean isSameMethodType(MethodTy a, MethodTy b) {
+ ImmutableMap<TyVarSymbol, Type> mapping = getMapping(a, b);
+ if (mapping == null) {
+ return false;
+ }
+ if (!sameTypeParameterBounds(a, b, mapping)) {
+ return false;
+ }
+ if (!isSameType(a.returnType(), subst(b.returnType(), mapping))) {
+ return false;
+ }
+ if (!isSameTypes(a.parameters(), substAll(b.parameters(), mapping))) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean sameTypeParameterBounds(
+ MethodTy a, MethodTy b, ImmutableMap<TyVarSymbol, Type> mapping) {
+ if (a.tyParams().size() != b.tyParams().size()) {
+ return false;
+ }
+ Iterator<TyVarSymbol> ax = a.tyParams().iterator();
+ Iterator<TyVarSymbol> bx = b.tyParams().iterator();
+ while (ax.hasNext()) {
+ TyVarSymbol x = ax.next();
+ TyVarSymbol y = bx.next();
+ if (!isSameType(
+ factory.getTyVarInfo(x).upperBound(),
+ subst(factory.getTyVarInfo(y).upperBound(), mapping))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isSameTypes(ImmutableList<Type> a, ImmutableList<Type> b) {
+ if (a.size() != b.size()) {
+ return false;
+ }
+ Iterator<Type> ax = a.iterator();
+ Iterator<Type> bx = b.iterator();
+ while (ax.hasNext()) {
+ if (!isSameType(ax.next(), bx.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isSameIntersectionType(IntersectionTy a, IntersectionTy b) {
+ return isSameTypes(getBounds(a), getBounds(b));
+ }
+
+ private ImmutableList<Type> getBounds(IntersectionTy a) {
+ return getBounds(factory, a);
+ }
+
+ static ImmutableList<Type> getBounds(ModelFactory factory, IntersectionTy type) {
+ ImmutableList<Type> bounds = type.bounds();
+ if (implicitObjectBound(factory, bounds)) {
+ return ImmutableList.<Type>builder().add(ClassTy.OBJECT).addAll(bounds).build();
+ }
+ return bounds;
+ }
+
+ private static boolean implicitObjectBound(ModelFactory factory, ImmutableList<Type> bounds) {
+ if (bounds.isEmpty()) {
+ return true;
+ }
+ ClassTy first = (ClassTy) bounds.get(0);
+ return factory.getSymbol(first.sym()).kind().equals(TurbineTyKind.INTERFACE);
+ }
+
+ private boolean isSameWildType(WildTy a, Type other) {
+ switch (other.tyKind()) {
+ case WILD_TY:
+ break;
+ case CLASS_TY:
+ // `? super Object` = Object
+ return ((ClassTy) other).sym().equals(ClassSymbol.OBJECT)
+ && a.boundKind() == BoundKind.LOWER
+ && a.bound().tyKind() == TyKind.CLASS_TY
+ && ((ClassTy) a.bound()).sym().equals(ClassSymbol.OBJECT);
+ default:
+ return false;
+ }
+ WildTy b = (WildTy) other;
+ switch (a.boundKind()) {
+ case NONE:
+ switch (b.boundKind()) {
+ case UPPER:
+ // `?` = `? extends Object`
+ return isObjectType(b.bound());
+ case LOWER:
+ return false;
+ case NONE:
+ return true;
+ }
+ break;
+ case UPPER:
+ switch (b.boundKind()) {
+ case UPPER:
+ return isSameType(a.bound(), b.bound());
+ case LOWER:
+ return false;
+ case NONE:
+ // `? extends Object` = `?`
+ return isObjectType(a.bound());
+ }
+ break;
+ case LOWER:
+ return b.boundKind() == BoundKind.LOWER && isSameType(a.bound(), b.bound());
+ }
+ throw new AssertionError(a.boundKind());
+ }
+
+ private boolean isSameClassType(ClassTy a, Type other) {
+ switch (other.tyKind()) {
+ case CLASS_TY:
+ break;
+ case WILD_TY:
+ WildTy w = (WildTy) other;
+ return a.sym().equals(ClassSymbol.OBJECT)
+ && w.boundKind() == BoundKind.LOWER
+ && w.bound().tyKind() == TyKind.CLASS_TY
+ && ((ClassTy) w.bound()).sym().equals(ClassSymbol.OBJECT);
+ default:
+ return false;
+ }
+ ClassTy b = (ClassTy) other;
+ if (!a.sym().equals(b.sym())) {
+ return false;
+ }
+ Iterator<SimpleClassTy> ax = a.classes().reverse().iterator();
+ Iterator<SimpleClassTy> bx = b.classes().reverse().iterator();
+ while (ax.hasNext() && bx.hasNext()) {
+ if (!isSameSimpleClassType(ax.next(), bx.next())) {
+ return false;
+ }
+ }
+ // The class type may be in non-canonical form, e.g. may or may not have entries in 'classes'
+ // corresponding to enclosing instances. Don't require the enclosing instances' representations
+ // to be identical unless one of them has type arguments.
+ if (hasTyArgs(ax) || hasTyArgs(bx)) {
+ return false;
+ }
+ return true;
+ }
+
+ /** Returns true if any {@link SimpleClassTy} in the given iterator has type arguments. */
+ private static boolean hasTyArgs(Iterator<SimpleClassTy> it) {
+ while (it.hasNext()) {
+ if (!it.next().targs().isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isSameSimpleClassType(SimpleClassTy a, SimpleClassTy b) {
+ return a.sym().equals(b.sym()) && isSameTypes(a.targs(), b.targs());
+ }
+
+ /** Returns true if type {@code a} is a subtype of type {@code b}. See JLS 4.1.0, 'subtyping'. */
+ @Override
+ public boolean isSubtype(TypeMirror a, TypeMirror b) {
+ return isSubtype(asTurbineType(a), asTurbineType(b), /* strict= */ true);
+ }
+
+ /**
+ * Returns true if type {@code a} is a subtype of type {@code b}. See JLS 4.1.0, 'subtyping'.
+ *
+ * @param strict true if raw types should not be considered subtypes of parameterized types. See
+ * also {@link #isAssignable}, which sets {@code strict} to {@code false} to handle unchecked
+ * conversions.
+ */
+ private boolean isSubtype(Type a, Type b, boolean strict) {
+ if (b.tyKind() == TyKind.INTERSECTION_TY) {
+ for (Type bound : getBounds((IntersectionTy) b)) {
+ // TODO(cushon): javac rejects e.g. `|List| isAssignable Serializable&ArrayList<?>`,
+ // i.e. it does a strict subtype test against the intersection type. Is that a bug?
+ if (!isSubtype(a, bound, /* strict= */ true)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ switch (a.tyKind()) {
+ case CLASS_TY:
+ return isClassSubtype((ClassTy) a, b, strict);
+ case PRIM_TY:
+ return isPrimSubtype((PrimTy) a, b);
+ case ARRAY_TY:
+ return isArraySubtype((ArrayTy) a, b, strict);
+ case TY_VAR:
+ return isTyVarSubtype((TyVar) a, b, strict);
+ case INTERSECTION_TY:
+ return isIntersectionSubtype((IntersectionTy) a, b, strict);
+ case VOID_TY:
+ return b.tyKind() == TyKind.VOID_TY;
+ case NONE_TY:
+ return b.tyKind() == TyKind.NONE_TY;
+ case WILD_TY:
+ // TODO(cushon): javac takes wildcards as input to isSubtype and sometimes returns `true`,
+ // see JDK-8039198
+ return false;
+ case ERROR_TY:
+ // for compatibility with javac, treat error as bottom
+ return true;
+ case METHOD_TY:
+ return false;
+ }
+ throw new AssertionError(a.tyKind());
+ }
+
+ private boolean isTyVarSubtype(TyVar a, Type b, boolean strict) {
+ if (b.tyKind() == TyKind.TY_VAR) {
+ return a.sym().equals(((TyVar) b).sym());
+ }
+ TyVarInfo tyVarInfo = factory.getTyVarInfo(a.sym());
+ return isSubtype(tyVarInfo.upperBound(), b, strict);
+ }
+
+ private boolean isIntersectionSubtype(IntersectionTy a, Type b, boolean strict) {
+ for (Type bound : getBounds(a)) {
+ if (isSubtype(bound, b, strict)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // see JLS 4.10.3, 'subtyping among array types'
+ // https://docs.oracle.com/javase/specs/jls/se11/html/jls-4.html#jls-4.10.3
+ private boolean isArraySubtype(ArrayTy a, Type b, boolean strict) {
+ switch (b.tyKind()) {
+ case ARRAY_TY:
+ Type ae = a.elementType();
+ Type be = ((ArrayTy) b).elementType();
+ if (ae.tyKind() == TyKind.PRIM_TY) {
+ return isSameType(ae, be);
+ }
+ return isSubtype(ae, be, strict);
+ case CLASS_TY:
+ ClassSymbol bsym = ((ClassTy) b).sym();
+ switch (bsym.binaryName()) {
+ case "java/lang/Object":
+ case "java/lang/Cloneable":
+ case "java/io/Serializable":
+ return true;
+ default:
+ return false;
+ }
+ default:
+ return false;
+ }
+ }
+
+ // see JLS 4.10.1, 'subtyping among primitive types'
+ // https://docs.oracle.com/javase/specs/jls/se11/html/jls-4.html#jls-4.10.1
+ private static boolean isPrimSubtype(PrimTy a, Type other) {
+ if (other.tyKind() != TyKind.PRIM_TY) {
+ return false;
+ }
+ PrimTy b = (PrimTy) other;
+ switch (a.primkind()) {
+ case CHAR:
+ switch (b.primkind()) {
+ case CHAR:
+ case INT:
+ case LONG:
+ case FLOAT:
+ case DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+ case BYTE:
+ switch (b.primkind()) {
+ case BYTE:
+ case SHORT:
+ case INT:
+ case LONG:
+ case FLOAT:
+ case DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+ case SHORT:
+ switch (b.primkind()) {
+ case SHORT:
+ case INT:
+ case LONG:
+ case FLOAT:
+ case DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+ case INT:
+ switch (b.primkind()) {
+ case INT:
+ case LONG:
+ case FLOAT:
+ case DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+ case LONG:
+ switch (b.primkind()) {
+ case LONG:
+ case FLOAT:
+ case DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+ case FLOAT:
+ switch (b.primkind()) {
+ case FLOAT:
+ case DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+ case DOUBLE:
+ case STRING:
+ case BOOLEAN:
+ return a.primkind() == b.primkind();
+ case NULL:
+ break;
+ }
+ throw new AssertionError(a.primkind());
+ }
+
+ private boolean isClassSubtype(ClassTy a, Type other, boolean strict) {
+ if (other.tyKind() != TyKind.CLASS_TY) {
+ return false;
+ }
+ ClassTy b = (ClassTy) other;
+ if (!a.sym().equals(b.sym())) {
+ // find a path from a to b in the type hierarchy
+ ImmutableList<ClassTy> path = factory.cha().search(a, b.sym());
+ if (path.isEmpty()) {
+ return false;
+ }
+ // perform repeated type substitution to get an instance of B with the type arguments
+ // provided by A
+ a = path.get(0);
+ for (ClassTy ty : path) {
+ ImmutableMap<TyVarSymbol, Type> mapping = getMapping(ty);
+ if (mapping == null) {
+ // if we encounter a raw type on the path from A to B the result is erased
+ a = (ClassTy) erasure(a);
+ break;
+ }
+ a = substClassTy(a, mapping);
+ }
+ }
+ Iterator<SimpleClassTy> ax = a.classes().reverse().iterator();
+ Iterator<SimpleClassTy> bx = b.classes().reverse().iterator();
+ while (ax.hasNext() && bx.hasNext()) {
+ if (!tyArgsContains(ax.next(), bx.next(), strict)) {
+ return false;
+ }
+ }
+ return !hasTyArgs(ax) && !hasTyArgs(bx);
+ }
+
+ /**
+ * Given two parameterizations of the same {@link SimpleClassTy}, {@code a} and {@code b}, teturns
+ * true if the type arguments of {@code a} are pairwise contained by the type arguments of {@code
+ * b}.
+ *
+ * @see {@link #contains} and JLS 4.5.1.
+ */
+ private boolean tyArgsContains(SimpleClassTy a, SimpleClassTy b, boolean strict) {
+ verify(a.sym().equals(b.sym()));
+ Iterator<Type> ax = a.targs().iterator();
+ Iterator<Type> bx = b.targs().iterator();
+ while (ax.hasNext() && bx.hasNext()) {
+ if (!containedBy(ax.next(), bx.next(), strict)) {
+ return false;
+ }
+ }
+ // C<F1, ..., FN> <= |C|, but |C| is not a subtype of C<F1, ..., FN>
+ // https://docs.oracle.com/javase/specs/jls/se11/html/jls-4.html#jls-4.8
+ if (strict) {
+ return !bx.hasNext();
+ }
+ return true;
+ }
+
+ private Type subst(Type type, Map<TyVarSymbol, Type> mapping) {
+ switch (type.tyKind()) {
+ case CLASS_TY:
+ return substClassTy((ClassTy) type, mapping);
+ case ARRAY_TY:
+ return substArrayTy((ArrayTy) type, mapping);
+ case TY_VAR:
+ return substTyVar((TyVar) type, mapping);
+ case PRIM_TY:
+ case VOID_TY:
+ case NONE_TY:
+ case ERROR_TY:
+ return type;
+ case METHOD_TY:
+ return substMethod((MethodTy) type, mapping);
+ case INTERSECTION_TY:
+ return substIntersectionTy((IntersectionTy) type, mapping);
+ case WILD_TY:
+ return substWildTy((WildTy) type, mapping);
+ }
+ throw new AssertionError(type.tyKind());
+ }
+
+ private Type substWildTy(WildTy type, Map<TyVarSymbol, Type> mapping) {
+ switch (type.boundKind()) {
+ case NONE:
+ return type;
+ case UPPER:
+ return Type.WildUpperBoundedTy.create(subst(type.bound(), mapping), ImmutableList.of());
+ case LOWER:
+ return Type.WildLowerBoundedTy.create(subst(type.bound(), mapping), ImmutableList.of());
+ }
+ throw new AssertionError(type.boundKind());
+ }
+
+ private Type substIntersectionTy(IntersectionTy type, Map<TyVarSymbol, Type> mapping) {
+ return IntersectionTy.create(substAll(getBounds(type), mapping));
+ }
+
+ private MethodTy substMethod(MethodTy method, Map<TyVarSymbol, Type> mapping) {
+ return MethodTy.create(
+ method.tyParams(),
+ subst(method.returnType(), mapping),
+ method.receiverType() != null ? subst(method.receiverType(), mapping) : null,
+ substAll(method.parameters(), mapping),
+ substAll(method.thrown(), mapping));
+ }
+
+ private ImmutableList<Type> substAll(
+ ImmutableList<? extends Type> types, Map<TyVarSymbol, Type> mapping) {
+ ImmutableList.Builder<Type> result = ImmutableList.builder();
+ for (Type type : types) {
+ result.add(subst(type, mapping));
+ }
+ return result.build();
+ }
+
+ private Type substTyVar(TyVar type, Map<TyVarSymbol, Type> mapping) {
+ return mapping.getOrDefault(type.sym(), type);
+ }
+
+ private Type substArrayTy(ArrayTy type, Map<TyVarSymbol, Type> mapping) {
+ return ArrayTy.create(subst(type.elementType(), mapping), type.annos());
+ }
+
+ private ClassTy substClassTy(ClassTy type, Map<TyVarSymbol, Type> mapping) {
+ ImmutableList.Builder<SimpleClassTy> simples = ImmutableList.builder();
+ for (SimpleClassTy simple : type.classes()) {
+ ImmutableList.Builder<Type> args = ImmutableList.builder();
+ for (Type arg : simple.targs()) {
+ args.add(subst(arg, mapping));
+ }
+ simples.add(SimpleClassTy.create(simple.sym(), args.build(), simple.annos()));
+ }
+ return ClassTy.create(simples.build());
+ }
+
+ /**
+ * Returns a mapping that can be used to adapt the signature 'b' to the type parameters of 'a', or
+ * {@code null} if no such mapping exists.
+ */
+ @Nullable
+ private static ImmutableMap<TyVarSymbol, Type> getMapping(MethodTy a, MethodTy b) {
+ if (a.tyParams().size() != b.tyParams().size()) {
+ return null;
+ }
+ Iterator<TyVarSymbol> ax = a.tyParams().iterator();
+ Iterator<TyVarSymbol> bx = b.tyParams().iterator();
+ ImmutableMap.Builder<TyVarSymbol, Type> mapping = ImmutableMap.builder();
+ while (ax.hasNext()) {
+ TyVarSymbol s = ax.next();
+ TyVarSymbol t = bx.next();
+ mapping.put(t, TyVar.create(s, ImmutableList.of()));
+ }
+ return mapping.build();
+ }
+
+ /**
+ * Returns a map from formal type parameters to their arguments for a given class type, or an
+ * empty map for non-parameterized types, or {@code null} for raw types.
+ */
+ @Nullable
+ private ImmutableMap<TyVarSymbol, Type> getMapping(ClassTy ty) {
+ ImmutableMap.Builder<TyVarSymbol, Type> mapping = ImmutableMap.builder();
+ for (SimpleClassTy s : ty.classes()) {
+ TypeBoundClass info = factory.getSymbol(s.sym());
+ if (s.targs().isEmpty() && !info.typeParameters().isEmpty()) {
+ return null; // rawtypes
+ }
+ Iterator<TyVarSymbol> ax = info.typeParameters().values().iterator();
+ Iterator<Type> bx = s.targs().iterator();
+ while (ax.hasNext()) {
+ mapping.put(ax.next(), bx.next());
+ }
+ verify(!bx.hasNext());
+ }
+ return mapping.build();
+ }
+
+ @Override
+ public boolean isAssignable(TypeMirror a1, TypeMirror a2) {
+ return isAssignable(asTurbineType(a1), asTurbineType(a2));
+ }
+
+ private boolean isAssignable(Type t1, Type t2) {
+ switch (t1.tyKind()) {
+ case PRIM_TY:
+ if (t2.tyKind() == TyKind.CLASS_TY) {
+ ClassSymbol boxed = boxedClass(((PrimTy) t1).primkind());
+ t1 = ClassTy.asNonParametricClassTy(boxed);
+ }
+ break;
+ case CLASS_TY:
+ switch (t2.tyKind()) {
+ case PRIM_TY:
+ TurbineConstantTypeKind unboxed = unboxedType((ClassTy) t1);
+ if (unboxed == null) {
+ return false;
+ }
+ t1 = PrimTy.create(unboxed, ImmutableList.of());
+ break;
+ case CLASS_TY:
+ break;
+ default: // fall out
+ }
+ break;
+ default: // fall out
+ }
+ return isSubtype(t1, t2, /* strict= */ false);
+ }
+
+ private static boolean isObjectType(Type type) {
+ return type.tyKind() == TyKind.CLASS_TY && ((ClassTy) type).sym().equals(ClassSymbol.OBJECT);
+ }
+
+ @Override
+ public boolean contains(TypeMirror a, TypeMirror b) {
+ return contains(asTurbineType(a), asTurbineType(b), /* strict= */ true);
+ }
+
+ private boolean contains(Type t1, Type t2, boolean strict) {
+ return containedBy(t2, t1, strict);
+ }
+
+ // See JLS 4.5.1, 'type containment'
+ // https://docs.oracle.com/javase/specs/jls/se11/html/jls-4.html#jls-4.5.1
+ private boolean containedBy(Type t1, Type t2, boolean strict) {
+ if (t1.tyKind() == TyKind.WILD_TY) {
+ WildTy w1 = (WildTy) t1;
+ Type t;
+ switch (w1.boundKind()) {
+ case UPPER:
+ t = w1.bound();
+ if (t2.tyKind() == TyKind.WILD_TY) {
+ WildTy w2 = (WildTy) t2;
+ switch (w2.boundKind()) {
+ case UPPER:
+ // ? extends T <= ? extends S if T <: S
+ return isSubtype(t, w2.bound(), strict);
+ case NONE:
+ // ? extends T <= ? [extends Object] if T <: Object
+ return true;
+ case LOWER:
+ // ? extends T <= ? super S
+ return false;
+ }
+ throw new AssertionError(w1.boundKind());
+ }
+ return false;
+ case LOWER:
+ t = w1.bound();
+ if (t2.tyKind() == TyKind.WILD_TY) {
+ WildTy w2 = (WildTy) t2;
+ switch (w2.boundKind()) {
+ case LOWER:
+ // ? super T <= ? super S if S <: T
+ return isSubtype(w2.bound(), t, strict);
+ case NONE:
+ // ? super T <= ? [extends Object]
+ return true;
+ case UPPER:
+ // ? super T <= ? extends Object
+ return isObjectType(w2.bound());
+ }
+ throw new AssertionError(w2.boundKind());
+ }
+ // ? super Object <= Object
+ return isObjectType(t2) && isObjectType(t);
+ case NONE:
+ if (t2.tyKind() == TyKind.WILD_TY) {
+ WildTy w2 = (WildTy) t2;
+ switch (w2.boundKind()) {
+ case NONE:
+ // ? [extends Object] <= ? extends Object
+ return true;
+ case LOWER:
+ // ? [extends Object] <= ? super S
+ return false;
+ case UPPER:
+ // ? [extends Object] <= ? extends S if Object <: S
+ return isObjectType(w2.bound());
+ }
+ throw new AssertionError(w2.boundKind());
+ }
+ return false;
+ }
+ throw new AssertionError(w1.boundKind());
+ }
+ if (t2.tyKind() == TyKind.WILD_TY) {
+ WildTy w2 = (WildTy) t2;
+ switch (w2.boundKind()) {
+ case LOWER:
+ // T <= ? super S
+ return isSubtype(w2.bound(), t1, strict);
+ case UPPER:
+ // T <= ? extends S
+ return isSubtype(t1, w2.bound(), strict);
+ case NONE:
+ // T <= ? [extends Object]
+ return true;
+ }
+ throw new AssertionError(w2.boundKind());
+ }
+ if (isSameType(t1, t2)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isSubsignature(ExecutableType m1, ExecutableType m2) {
+ return isSubsignature((MethodTy) asTurbineType(m1), (MethodTy) asTurbineType(m2));
+ }
+
+ private boolean isSubsignature(MethodTy a, MethodTy b) {
+ return isSameSignature(a, b) || isSameSignature(a, (MethodTy) erasure(b));
+ }
+
+ private boolean isSameSignature(MethodTy a, MethodTy b) {
+ if (a.parameters().size() != b.parameters().size()) {
+ return false;
+ }
+ ImmutableMap<TyVarSymbol, Type> mapping = getMapping(a, b);
+ if (mapping == null) {
+ return false;
+ }
+ if (!sameTypeParameterBounds(a, b, mapping)) {
+ return false;
+ }
+ Iterator<Type> ax = a.parameters().iterator();
+ // adapt the formal parameter types of 'b' to the type parameters of 'a'
+ Iterator<Type> bx = substAll(b.parameters(), mapping).iterator();
+ while (ax.hasNext()) {
+ if (!isSameType(ax.next(), bx.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public List<? extends TypeMirror> directSupertypes(TypeMirror m) {
+ return factory.asTypeMirrors(directSupertypes(asTurbineType(m)));
+ }
+
+ public ImmutableList<Type> directSupertypes(Type t) {
+ switch (t.tyKind()) {
+ case CLASS_TY:
+ return directSupertypes((ClassTy) t);
+ case INTERSECTION_TY:
+ return ((IntersectionTy) t).bounds();
+ case TY_VAR:
+ return getBounds(factory.getTyVarInfo(((TyVar) t).sym()).upperBound());
+ case ARRAY_TY:
+ return directSupertypes((ArrayTy) t);
+ case PRIM_TY:
+ case VOID_TY:
+ case WILD_TY:
+ case ERROR_TY:
+ case NONE_TY:
+ return ImmutableList.of();
+ case METHOD_TY:
+ break;
+ }
+ throw new IllegalArgumentException(t.tyKind().name());
+ }
+
+ private ImmutableList<Type> directSupertypes(ArrayTy t) {
+ Type elem = t.elementType();
+ if (elem.tyKind() == TyKind.PRIM_TY || isObjectType(elem)) {
+ return ImmutableList.of(
+ IntersectionTy.create(
+ ImmutableList.of(ClassTy.OBJECT, ClassTy.SERIALIZABLE, ClassTy.CLONEABLE)));
+ }
+ ImmutableList<Type> ex = directSupertypes(elem);
+ return ImmutableList.of(ArrayTy.create(ex.iterator().next(), ImmutableList.of()));
+ }
+
+ private ImmutableList<Type> directSupertypes(ClassTy t) {
+ if (t.sym().equals(ClassSymbol.OBJECT)) {
+ return ImmutableList.of();
+ }
+ TypeBoundClass info = factory.getSymbol(t.sym());
+ Map<TyVarSymbol, Type> mapping = getMapping(t);
+ boolean raw = mapping == null;
+ ImmutableList.Builder<Type> builder = ImmutableList.builder();
+ if (info.superClassType() != null) {
+ builder.add(raw ? erasure(info.superClassType()) : subst(info.superClassType(), mapping));
+ } else {
+ builder.add(ClassTy.OBJECT);
+ }
+ for (Type interfaceType : info.interfaceTypes()) {
+ builder.add(raw ? erasure(interfaceType) : subst(interfaceType, mapping));
+ }
+ return builder.build();
+ }
+
+ @Override
+ public TypeMirror erasure(TypeMirror typeMirror) {
+ return factory.asTypeMirror(erasure(asTurbineType(typeMirror)));
+ }
+
+ private Type erasure(Type type) {
+ return Erasure.erase(
+ type,
+ new Function<TyVarSymbol, TyVarInfo>() {
+ @Override
+ public TyVarInfo apply(TyVarSymbol input) {
+ return factory.getTyVarInfo(input);
+ }
+ });
+ }
+
+ @Override
+ public TypeElement boxedClass(PrimitiveType p) {
+ return factory.typeElement(boxedClass(((PrimTy) asTurbineType(p)).primkind()));
+ }
+
+ static ClassSymbol boxedClass(TurbineConstantTypeKind kind) {
+ switch (kind) {
+ case CHAR:
+ return ClassSymbol.CHARACTER;
+ case SHORT:
+ return ClassSymbol.SHORT;
+ case INT:
+ return ClassSymbol.INTEGER;
+ case LONG:
+ return ClassSymbol.LONG;
+ case FLOAT:
+ return ClassSymbol.FLOAT;
+ case DOUBLE:
+ return ClassSymbol.DOUBLE;
+ case BOOLEAN:
+ return ClassSymbol.BOOLEAN;
+ case BYTE:
+ return ClassSymbol.BYTE;
+ case STRING:
+ case NULL:
+ break;
+ }
+ throw new AssertionError(kind);
+ }
+
+ @Override
+ public PrimitiveType unboxedType(TypeMirror typeMirror) {
+ Type type = asTurbineType(typeMirror);
+ if (type.tyKind() != TyKind.CLASS_TY) {
+ throw new IllegalArgumentException(type.toString());
+ }
+ TurbineConstantTypeKind unboxed = unboxedType((ClassTy) type);
+ if (unboxed == null) {
+ throw new IllegalArgumentException(type.toString());
+ }
+ return (PrimitiveType) factory.asTypeMirror(PrimTy.create(unboxed, ImmutableList.of()));
+ }
+
+ private static TurbineConstantTypeKind unboxedType(ClassTy classTy) {
+ switch (classTy.sym().binaryName()) {
+ case "java/lang/Boolean":
+ return TurbineConstantTypeKind.BOOLEAN;
+ case "java/lang/Byte":
+ return TurbineConstantTypeKind.BYTE;
+ case "java/lang/Short":
+ return TurbineConstantTypeKind.SHORT;
+ case "java/lang/Integer":
+ return TurbineConstantTypeKind.INT;
+ case "java/lang/Long":
+ return TurbineConstantTypeKind.LONG;
+ case "java/lang/Character":
+ return TurbineConstantTypeKind.CHAR;
+ case "java/lang/Float":
+ return TurbineConstantTypeKind.FLOAT;
+ case "java/lang/Double":
+ return TurbineConstantTypeKind.DOUBLE;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public TypeMirror capture(TypeMirror typeMirror) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PrimitiveType getPrimitiveType(TypeKind kind) {
+ checkArgument(kind.isPrimitive(), "%s is not a primitive type", kind);
+ return (PrimitiveType)
+ factory.asTypeMirror(PrimTy.create(primitiveType(kind), ImmutableList.of()));
+ }
+
+ private static TurbineConstantTypeKind primitiveType(TypeKind kind) {
+ switch (kind) {
+ case BOOLEAN:
+ return TurbineConstantTypeKind.BOOLEAN;
+ case BYTE:
+ return TurbineConstantTypeKind.BYTE;
+ case SHORT:
+ return TurbineConstantTypeKind.SHORT;
+ case INT:
+ return TurbineConstantTypeKind.INT;
+ case LONG:
+ return TurbineConstantTypeKind.LONG;
+ case CHAR:
+ return TurbineConstantTypeKind.CHAR;
+ case FLOAT:
+ return TurbineConstantTypeKind.FLOAT;
+ case DOUBLE:
+ return TurbineConstantTypeKind.DOUBLE;
+ default:
+ throw new IllegalArgumentException(kind + " is not a primitive type");
+ }
+ }
+
+ @Override
+ public NullType getNullType() {
+ return factory.nullType();
+ }
+
+ @Override
+ public NoType getNoType(TypeKind kind) {
+ switch (kind) {
+ case VOID:
+ return (NoType) factory.asTypeMirror(Type.VOID);
+ case NONE:
+ return factory.noType();
+ default:
+ throw new IllegalArgumentException(kind.toString());
+ }
+ }
+
+ @Override
+ public ArrayType getArrayType(TypeMirror componentType) {
+ return (ArrayType)
+ factory.asTypeMirror(ArrayTy.create(asTurbineType(componentType), ImmutableList.of()));
+ }
+
+ @Override
+ public WildcardType getWildcardType(TypeMirror extendsBound, TypeMirror superBound) {
+ WildTy type;
+ if (extendsBound != null) {
+ type = WildTy.WildUpperBoundedTy.create(asTurbineType(extendsBound), ImmutableList.of());
+ } else if (superBound != null) {
+ type = WildTy.WildLowerBoundedTy.create(asTurbineType(superBound), ImmutableList.of());
+ } else {
+ type = WildUnboundedTy.create(ImmutableList.of());
+ }
+ return (WildcardType) factory.asTypeMirror(type);
+ }
+
+ @Override
+ public DeclaredType getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs) {
+ requireNonNull(typeElem);
+ ImmutableList.Builder<Type> args = ImmutableList.builder();
+ for (TypeMirror t : typeArgs) {
+ args.add(asTurbineType(t));
+ }
+ TurbineTypeElement element = (TurbineTypeElement) typeElem;
+ return (DeclaredType)
+ factory.asTypeMirror(
+ ClassTy.create(
+ ImmutableList.of(
+ SimpleClassTy.create(element.sym(), args.build(), ImmutableList.of()))));
+ }
+
+ @Override
+ public DeclaredType getDeclaredType(
+ DeclaredType containing, TypeElement typeElem, TypeMirror... typeArgs) {
+ if (containing == null) {
+ return getDeclaredType(typeElem, typeArgs);
+ }
+ requireNonNull(typeElem);
+ ClassTy base = (ClassTy) asTurbineType(containing);
+ TurbineTypeElement element = (TurbineTypeElement) typeElem;
+ ImmutableList.Builder<Type> args = ImmutableList.builder();
+ for (TypeMirror t : typeArgs) {
+ args.add(asTurbineType(t));
+ }
+ return (DeclaredType)
+ factory.asTypeMirror(
+ ClassTy.create(
+ ImmutableList.<SimpleClassTy>builder()
+ .addAll(base.classes())
+ .add(SimpleClassTy.create(element.sym(), args.build(), ImmutableList.of()))
+ .build()));
+ }
+
+ private static ClassSymbol enclosingClass(Symbol symbol) {
+ switch (symbol.symKind()) {
+ case CLASS:
+ return (ClassSymbol) symbol;
+ case TY_PARAM:
+ return enclosingClass(((TyVarSymbol) symbol).owner());
+ case METHOD:
+ return ((MethodSymbol) symbol).owner();
+ case FIELD:
+ return ((FieldSymbol) symbol).owner();
+ case PARAMETER:
+ return ((ParamSymbol) symbol).owner().owner();
+ case MODULE:
+ case PACKAGE:
+ throw new IllegalArgumentException(symbol.symKind().toString());
+ }
+ throw new AssertionError(symbol.symKind());
+ }
+
+ private static Type type(Element element) {
+ switch (element.getKind()) {
+ case TYPE_PARAMETER:
+ return TyVar.create(((TurbineTypeParameterElement) element).sym(), ImmutableList.of());
+ case FIELD:
+ return ((TurbineFieldElement) element).info().type();
+ case METHOD:
+ case CONSTRUCTOR:
+ return ((TurbineExecutableElement) element).info().asType();
+ default:
+ throw new UnsupportedOperationException(element.toString());
+ }
+ }
+
+ /**
+ * Returns the {@link TypeMirror} of the given {@code element} as a member of {@code containing},
+ * or else {@code null} if it is not a member.
+ *
+ * <p>e.g. given a class {@code MyStringList} that implements {@code List<String>}, the type of
+ * {@code List.add} would be {@code add(String)}.
+ */
+ @Override
+ public TypeMirror asMemberOf(DeclaredType containing, Element element) {
+ ClassTy c = ((TurbineDeclaredType) containing).asTurbineType();
+ ClassSymbol symbol = enclosingClass(((TurbineElement) element).sym());
+ ImmutableList<ClassTy> path = factory.cha().search(c, enclosingClass(symbol));
+ if (path.isEmpty()) {
+ return null;
+ }
+ Type type = type(element);
+ for (ClassTy ty : path) {
+ ImmutableMap<TyVarSymbol, Type> mapping = getMapping(ty);
+ if (mapping == null) {
+ type = erasure(type);
+ break;
+ }
+ type = subst(type, mapping);
+ }
+ return factory.asTypeMirror(type);
+ }
+}
diff --git a/java/com/google/turbine/tree/Pretty.java b/java/com/google/turbine/tree/Pretty.java
index 2b9374e..b693a42 100644
--- a/java/com/google/turbine/tree/Pretty.java
+++ b/java/com/google/turbine/tree/Pretty.java
@@ -501,8 +501,6 @@ public class Pretty implements Tree.Visitor<Void, Void> {
case ACC_SYNTHETIC:
case ACC_BRIDGE:
break;
- default:
- throw new AssertionError(mod);
}
}
}
diff --git a/java/com/google/turbine/tree/Tree.java b/java/com/google/turbine/tree/Tree.java
index a20b106..d36c3ab 100644
--- a/java/com/google/turbine/tree/Tree.java
+++ b/java/com/google/turbine/tree/Tree.java
@@ -661,6 +661,7 @@ public abstract class Tree {
private final Tree ty;
private final Ident name;
private final Optional<Expression> init;
+ private final String javadoc;
public VarDecl(
int position,
@@ -668,13 +669,15 @@ public abstract class Tree {
ImmutableList<Anno> annos,
Tree ty,
Ident name,
- Optional<Expression> init) {
+ Optional<Expression> init,
+ String javadoc) {
super(position);
this.mods = ImmutableSet.copyOf(mods);
this.annos = annos;
this.ty = ty;
this.name = name;
this.init = init;
+ this.javadoc = javadoc;
}
@Override
@@ -706,6 +709,14 @@ public abstract class Tree {
public Optional<Expression> init() {
return init;
}
+
+ /**
+ * A javadoc comment, excluding the opening and closing delimiters but including all interior
+ * characters and whitespace.
+ */
+ public String javadoc() {
+ return javadoc;
+ }
}
/** A JLS 8.4 method declaration. */
@@ -718,6 +729,7 @@ public abstract class Tree {
private final ImmutableList<VarDecl> params;
private final ImmutableList<ClassTy> exntys;
private final Optional<Tree> defaultValue;
+ private final String javadoc;
public MethDecl(
int position,
@@ -728,7 +740,8 @@ public abstract class Tree {
Ident name,
ImmutableList<VarDecl> params,
ImmutableList<ClassTy> exntys,
- Optional<Tree> defaultValue) {
+ Optional<Tree> defaultValue,
+ String javadoc) {
super(position);
this.mods = ImmutableSet.copyOf(mods);
this.annos = annos;
@@ -738,6 +751,7 @@ public abstract class Tree {
this.params = params;
this.exntys = exntys;
this.defaultValue = defaultValue;
+ this.javadoc = javadoc;
}
@Override
@@ -781,6 +795,13 @@ public abstract class Tree {
public Optional<Tree> defaultValue() {
return defaultValue;
}
+ /**
+ * A javadoc comment, excluding the opening and closing delimiters but including all interior
+ * characters and whitespace.
+ */
+ public String javadoc() {
+ return javadoc;
+ }
}
/** A JLS 9.7 annotation. */
@@ -852,6 +873,7 @@ public abstract class Tree {
private final ImmutableList<ClassTy> impls;
private final ImmutableList<Tree> members;
private final TurbineTyKind tykind;
+ private final String javadoc;
public TyDecl(
int position,
@@ -862,7 +884,8 @@ public abstract class Tree {
Optional<ClassTy> xtnds,
ImmutableList<ClassTy> impls,
ImmutableList<Tree> members,
- TurbineTyKind tykind) {
+ TurbineTyKind tykind,
+ String javadoc) {
super(position);
this.mods = ImmutableSet.copyOf(mods);
this.annos = annos;
@@ -872,6 +895,7 @@ public abstract class Tree {
this.impls = impls;
this.members = members;
this.tykind = tykind;
+ this.javadoc = javadoc;
}
@Override
@@ -915,6 +939,13 @@ public abstract class Tree {
public TurbineTyKind tykind() {
return tykind;
}
+ /**
+ * A javadoc comment, excluding the opening and closing delimiters but including all interior
+ * characters and whitespace.
+ */
+ public String javadoc() {
+ return javadoc;
+ }
}
/** A JLS 4.4. type variable declaration. */
diff --git a/java/com/google/turbine/type/AnnoInfo.java b/java/com/google/turbine/type/AnnoInfo.java
index 9c907aa..ff902b3 100644
--- a/java/com/google/turbine/type/AnnoInfo.java
+++ b/java/com/google/turbine/type/AnnoInfo.java
@@ -16,6 +16,9 @@
package com.google.turbine.type;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.sym.ClassSymbol;
@@ -24,6 +27,7 @@ import com.google.turbine.model.Const;
import com.google.turbine.tree.Tree;
import com.google.turbine.tree.Tree.Anno;
import com.google.turbine.tree.Tree.Expression;
+import java.util.Map;
import java.util.Objects;
/** An annotation use. */
@@ -38,7 +42,7 @@ public class AnnoInfo {
this.source = source;
this.sym = sym;
this.tree = tree;
- this.values = values;
+ this.values = requireNonNull(values);
}
/** The annotation's source, for diagnostics. */
@@ -66,6 +70,10 @@ public class AnnoInfo {
return sym;
}
+ public Tree.Anno tree() {
+ return tree;
+ }
+
public AnnoInfo withValues(ImmutableMap<String, Const> values) {
return new AnnoInfo(source, sym, tree, values);
}
@@ -83,4 +91,27 @@ public class AnnoInfo {
AnnoInfo that = (AnnoInfo) obj;
return sym.equals(that.sym) && values.equals(that.values);
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append('@').append(sym.binaryName().replace('/', '.').replace('$', '.'));
+ boolean first = true;
+ if (values != null && !values.isEmpty()) {
+ sb.append('(');
+ if (values.size() == 1 && values.containsKey("value")) {
+ sb.append(getOnlyElement(values.values()));
+ } else {
+ for (Map.Entry<String, Const> e : values.entrySet()) {
+ if (!first) {
+ sb.append(", ");
+ }
+ sb.append(e.getKey()).append('=').append(e.getValue());
+ first = false;
+ }
+ }
+ sb.append(')');
+ }
+ return sb.toString();
+ }
}
diff --git a/java/com/google/turbine/type/Type.java b/java/com/google/turbine/type/Type.java
index 8950ec0..daba2ae 100644
--- a/java/com/google/turbine/type/Type.java
+++ b/java/com/google/turbine/type/Type.java
@@ -16,14 +16,23 @@
package com.google.turbine.type;
+import static com.google.common.collect.Iterables.getLast;
+import static java.util.Objects.requireNonNull;
+
import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.model.TurbineConstantTypeKind;
+import com.google.turbine.tree.Tree;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import org.checkerframework.checker.nullness.qual.Nullable;
/** JLS 4 types. */
public interface Type {
@@ -48,8 +57,11 @@ public interface Type {
WILD_TY,
/** An intersection type. */
INTERSECTION_TY,
+ /** A method type. */
+ METHOD_TY,
- ERROR_TY
+ ERROR_TY,
+ NONE_TY,
}
/** The type kind. */
@@ -62,9 +74,34 @@ public interface Type {
public TyKind tyKind() {
return TyKind.VOID_TY;
}
+
+ @Override
+ public final String toString() {
+ return "void";
+ }
+ };
+
+ /** The void type. */
+ Type NONE =
+ new Type() {
+ @Override
+ public TyKind tyKind() {
+ return TyKind.NONE_TY;
+ }
+
+ @Override
+ public final String toString() {
+ return "none";
+ }
};
- /** A class type. */
+ /**
+ * A class type.
+ *
+ * <p>Qualified types (e.g. {@code OuterClass<Foo>.InnerClass<Bar>}) are repesented as a list
+ * {@link SimpleClassTy}s (enclosing types first), each of which contains a {@link ClassSymbol}
+ * and an optional list of type arguments.
+ */
@AutoValue
abstract class ClassTy implements Type {
@@ -77,19 +114,17 @@ public interface Type {
/** The {@link ClassTy} for {@code java.lang.String}. */
public static final ClassTy STRING = asNonParametricClassTy(ClassSymbol.STRING);
+ public static final ClassTy CLONEABLE = asNonParametricClassTy(ClassSymbol.CLONEABLE);
+ public static final ClassTy SERIALIZABLE = asNonParametricClassTy(ClassSymbol.SERIALIZABLE);
+
/** Returns a {@link ClassTy} with no type arguments for the given {@link ClassSymbol}. */
public static ClassTy asNonParametricClassTy(ClassSymbol i) {
- return create(Arrays.asList(SimpleClassTy.create(i, ImmutableList.of(), ImmutableList.of())));
+ return ClassTy.create(
+ Arrays.asList(SimpleClassTy.create(i, ImmutableList.of(), ImmutableList.of())));
}
public abstract ImmutableList<SimpleClassTy> classes();
- /**
- * A class type. Qualified types are repesented as a list tuples, each of which contains a
- * {@link ClassSymbol} and an optional list of type arguments.
- *
- * @param classes components of a qualified class type, possibly with type arguments.
- */
public static ClassTy create(Iterable<SimpleClassTy> classes) {
return new AutoValue_Type_ClassTy(ImmutableList.copyOf(classes));
}
@@ -101,7 +136,7 @@ public interface Type {
/** The class symbol. */
public ClassSymbol sym() {
- return Iterables.getLast(classes()).sym();
+ return getLast(classes()).sym();
}
@Override
@@ -109,11 +144,15 @@ public interface Type {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (SimpleClassTy c : classes()) {
+ for (AnnoInfo anno : c.annos()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
if (!first) {
sb.append('.');
sb.append(c.sym().binaryName().substring(c.sym().binaryName().lastIndexOf('$') + 1));
} else {
- sb.append(c.sym().binaryName());
+ sb.append(c.sym().binaryName().replace('/', '.').replace('$', '.'));
}
if (!c.targs().isEmpty()) {
sb.append('<');
@@ -142,6 +181,46 @@ public interface Type {
/** The type annotations. */
public abstract ImmutableList<AnnoInfo> annos();
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+ }
+
+ @Memoized
+ @Override
+ public int hashCode() {
+ return Iterables.getLast(classes()).hashCode();
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+ if (!(obj instanceof ClassTy)) {
+ return false;
+ }
+ ClassTy that = (ClassTy) obj;
+ int i = this.classes().size() - 1;
+ int j = that.classes().size() - 1;
+ for (; i >= 0 && j >= 0; i--, j--) {
+ if (!this.classes().get(i).equals(that.classes().get(j))) {
+ return false;
+ }
+ }
+ // don't rely on canonical form for simple class names
+ if (hasTargs(this.classes(), i) || hasTargs(that.classes(), j)) {
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean hasTargs(ImmutableList<SimpleClassTy> classes, int idx) {
+ for (; idx >= 0; idx--) {
+ SimpleClassTy simple = classes.get(idx);
+ if (!simple.targs().isEmpty() || !simple.annos().isEmpty()) {
+ return true;
+ }
+ }
+ return false;
}
}
@@ -163,6 +242,22 @@ public interface Type {
/** The type annotations. */
public abstract ImmutableList<AnnoInfo> annos();
+
+ @Override
+ public final String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (AnnoInfo anno : annos()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
+ sb.append(elementType());
+ sb.append("[]");
+ return sb.toString();
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
}
/** A type variable. */
@@ -183,11 +278,21 @@ public interface Type {
@Override
public final String toString() {
- return sym().owner() + "#" + sym().name();
+ StringBuilder sb = new StringBuilder();
+ for (AnnoInfo anno : annos()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
+ sb.append(sym().name());
+ return sb.toString();
}
/** The type annotations. */
public abstract ImmutableList<AnnoInfo> annos();
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
}
/** A primitive type. */
@@ -208,6 +313,21 @@ public interface Type {
/** The type annotations. */
public abstract ImmutableList<AnnoInfo> annos();
+
+ @Override
+ public final String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (AnnoInfo anno : annos()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
+ sb.append(primkind());
+ return sb.toString();
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
}
/** A wildcard type, valid only inside (possibly nested) type arguments. */
@@ -248,6 +368,22 @@ public interface Type {
public BoundKind boundKind() {
return BoundKind.UPPER;
}
+
+ @Override
+ public final String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (AnnoInfo anno : annotations()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
+ sb.append("? extends ");
+ sb.append(bound());
+ return sb.toString();
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
}
/** An lower-bounded wildcard type. */
@@ -266,6 +402,22 @@ public interface Type {
public BoundKind boundKind() {
return BoundKind.LOWER;
}
+
+ @Override
+ public final String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (AnnoInfo anno : annotations()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
+ sb.append("? super ");
+ sb.append(bound());
+ return sb.toString();
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
}
/** An unbounded wildcard type. */
@@ -285,6 +437,21 @@ public interface Type {
public Type bound() {
throw new IllegalStateException();
}
+
+ @Override
+ public final String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (AnnoInfo anno : annotations()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
+ sb.append('?');
+ return sb.toString();
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
}
/** An intersection type. */
@@ -301,18 +468,121 @@ public interface Type {
public TyKind tyKind() {
return TyKind.INTERSECTION_TY;
}
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public final String toString() {
+ return Joiner.on('&').join(bounds());
+ }
}
- /** An error type. */
+ /** A method type. */
@AutoValue
- abstract class ErrorTy implements Type {
- public static ErrorTy create() {
- return new AutoValue_Type_ErrorTy();
+ abstract class MethodTy implements Type {
+
+ public abstract ImmutableSet<TyVarSymbol> tyParams();
+
+ public abstract Type returnType();
+
+ /** The type of the receiver parameter (see JLS 8.4.1). */
+ @Nullable
+ public abstract Type receiverType();
+
+ public abstract ImmutableList<Type> parameters();
+
+ public abstract ImmutableList<Type> thrown();
+
+ public static MethodTy create(
+ ImmutableSet<TyVarSymbol> tyParams,
+ Type returnType,
+ Type receiverType,
+ ImmutableList<Type> parameters,
+ ImmutableList<Type> thrown) {
+ return new AutoValue_Type_MethodTy(tyParams, returnType, receiverType, parameters, thrown);
+ }
+
+ @Override
+ public TyKind tyKind() {
+ return TyKind.METHOD_TY;
+ }
+
+ @Override
+ public final String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (!tyParams().isEmpty()) {
+ sb.append('<');
+ Joiner.on(',').appendTo(sb, tyParams());
+ sb.append('>');
+ }
+ sb.append('(');
+ Joiner.on(',').appendTo(sb, parameters());
+ sb.append(')');
+ sb.append(returnType());
+ return sb.toString();
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+ }
+
+ /** An error type. */
+ final class ErrorTy implements Type {
+
+ private final String name;
+
+ private ErrorTy(String name) {
+ this.name = requireNonNull(name);
+ }
+
+ /**
+ * Best-effort syntactic context for use in diagnostics or by annotation processors. This may be
+ * a simple or qualified name; it is not a canonical qualified name.
+ */
+ public String name() {
+ return name;
+ }
+
+ public static ErrorTy create(Iterable<Tree.Ident> names) {
+ List<String> bits = new ArrayList<>();
+ for (Tree.Ident ident : names) {
+ bits.add(ident.value());
+ }
+ return create(Joiner.on('.').join(bits));
+ }
+
+ public static ErrorTy create(String name) {
+ return new ErrorTy(name);
}
@Override
public TyKind tyKind() {
return TyKind.ERROR_TY;
}
+
+ @Override
+ public final String toString() {
+ return name();
+ }
+
+ @Override
+ public final int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public final boolean equals(Object other) {
+ // The name associated with an error type is context for use in diagnostics or by annotations
+ // processors. Two error types with the same name don't necessarily represent the same type.
+
+ // TODO(cushon): should error types compare equal to themselves if they correspond to the same
+ // source location? Investigate storing the source position for this type, or replacing with
+ // `this == other` (and removing interning in ModelFactory).
+
+ return false;
+ }
}
}
diff --git a/java/com/google/turbine/types/Canonicalize.java b/java/com/google/turbine/types/Canonicalize.java
index ab73618..22df069 100644
--- a/java/com/google/turbine/types/Canonicalize.java
+++ b/java/com/google/turbine/types/Canonicalize.java
@@ -100,6 +100,7 @@ public class Canonicalize {
case PRIM_TY:
case VOID_TY:
case TY_VAR:
+ case ERROR_TY:
return type;
case WILD_TY:
return canonicalizeWildTy(base, (WildTy) type);
@@ -118,6 +119,9 @@ public class Canonicalize {
}
private ClassTy canon(ClassSymbol base, ClassTy ty) {
+ if (ty.sym().equals(ClassSymbol.ERROR)) {
+ return ty;
+ }
if (isRaw(ty)) {
return Erasure.eraseClassTy(ty);
}
@@ -277,7 +281,7 @@ public class Canonicalize {
}
/** Instantiates a type argument using the given mapping. */
- private Type instantiate(Map<TyVarSymbol, Type> mapping, Type type) {
+ private static Type instantiate(Map<TyVarSymbol, Type> mapping, Type type) {
if (type == null) {
return null;
}
@@ -286,6 +290,7 @@ public class Canonicalize {
return instantiateWildTy(mapping, (WildTy) type);
case PRIM_TY:
case VOID_TY:
+ case ERROR_TY:
return type;
case CLASS_TY:
return instantiateClassTy(mapping, (ClassTy) type);
@@ -304,7 +309,7 @@ public class Canonicalize {
}
}
- private Type instantiateWildTy(Map<TyVarSymbol, Type> mapping, WildTy type) {
+ private static Type instantiateWildTy(Map<TyVarSymbol, Type> mapping, WildTy type) {
switch (type.boundKind()) {
case NONE:
return type;
@@ -314,12 +319,11 @@ public class Canonicalize {
case LOWER:
return Type.WildLowerBoundedTy.create(
instantiate(mapping, type.bound()), type.annotations());
- default:
- throw new AssertionError(type.boundKind());
}
+ throw new AssertionError(type.boundKind());
}
- private Type instantiateClassTy(Map<TyVarSymbol, Type> mapping, ClassTy type) {
+ private static Type instantiateClassTy(Map<TyVarSymbol, Type> mapping, ClassTy type) {
ImmutableList.Builder<SimpleClassTy> simples = ImmutableList.builder();
for (SimpleClassTy simple : type.classes()) {
ImmutableList.Builder<Type> args = ImmutableList.builder();
@@ -336,7 +340,7 @@ public class Canonicalize {
* reference, or else {@code null}.
*/
@Nullable
- private TyVarSymbol tyVarSym(Type type) {
+ private static TyVarSymbol tyVarSym(Type type) {
if (type.tyKind() == TyKind.TY_VAR) {
return ((TyVar) type).sym();
}
diff --git a/java/com/google/turbine/types/Erasure.java b/java/com/google/turbine/types/Erasure.java
index e2c7d8f..9042897 100644
--- a/java/com/google/turbine/types/Erasure.java
+++ b/java/com/google/turbine/types/Erasure.java
@@ -18,6 +18,7 @@ package com.google.turbine.types;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.turbine.binder.bound.SourceTypeBoundClass;
import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
import com.google.turbine.binder.sym.TyVarSymbol;
@@ -26,15 +27,14 @@ import com.google.turbine.type.Type.ArrayTy;
import com.google.turbine.type.Type.ClassTy;
import com.google.turbine.type.Type.ClassTy.SimpleClassTy;
import com.google.turbine.type.Type.IntersectionTy;
+import com.google.turbine.type.Type.MethodTy;
import com.google.turbine.type.Type.TyVar;
+import com.google.turbine.type.Type.WildTy;
/** Generic type erasure. */
public class Erasure {
public static Type erase(Type ty, Function<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tenv) {
switch (ty.tyKind()) {
- case PRIM_TY:
- case VOID_TY:
- return ty;
case CLASS_TY:
return eraseClassTy((Type.ClassTy) ty);
case ARRAY_TY:
@@ -43,20 +43,37 @@ public class Erasure {
return eraseTyVar((TyVar) ty, tenv);
case INTERSECTION_TY:
return eraseIntersectionTy((Type.IntersectionTy) ty, tenv);
- default:
- throw new AssertionError(ty.tyKind());
+ case WILD_TY:
+ return eraseWildTy((Type.WildTy) ty, tenv);
+ case METHOD_TY:
+ return erasureMethodTy((Type.MethodTy) ty, tenv);
+ case PRIM_TY:
+ case VOID_TY:
+ case ERROR_TY:
+ case NONE_TY:
+ return ty;
+ }
+ throw new AssertionError(ty.tyKind());
+ }
+
+ private static ImmutableList<Type> erase(
+ ImmutableList<Type> types, Function<TyVarSymbol, TyVarInfo> tenv) {
+ ImmutableList.Builder<Type> result = ImmutableList.builder();
+ for (Type type : types) {
+ result.add(erase(type, tenv));
}
+ return result.build();
}
private static Type eraseIntersectionTy(
IntersectionTy ty, Function<TyVarSymbol, TyVarInfo> tenv) {
- return erase(ty.bounds().get(0), tenv);
+ 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());
- return erase(info.bound(), tenv);
+ return erase(info.upperBound(), tenv);
}
private static Type.ArrayTy eraseArrayTy(
@@ -75,4 +92,24 @@ public class Erasure {
}
return ClassTy.create(classes.build());
}
+
+ private static Type eraseWildTy(WildTy ty, Function<TyVarSymbol, TyVarInfo> tenv) {
+ switch (ty.boundKind()) {
+ case NONE:
+ case LOWER:
+ return ClassTy.OBJECT;
+ case UPPER:
+ return erase(ty.bound(), tenv);
+ }
+ throw new AssertionError(ty.boundKind());
+ }
+
+ private static Type erasureMethodTy(MethodTy ty, Function<TyVarSymbol, TyVarInfo> tenv) {
+ return MethodTy.create(
+ /* tyParams= */ ImmutableSet.of(),
+ erase(ty.returnType(), tenv),
+ ty.receiverType() != null ? erase(ty.receiverType(), tenv) : null,
+ erase(ty.parameters(), tenv),
+ erase(ty.thrown(), tenv));
+ }
}
diff --git a/javatests/com/google/turbine/binder/BinderErrorTest.java b/javatests/com/google/turbine/binder/BinderErrorTest.java
index 5a4d97e..15b54eb 100644
--- a/javatests/com/google/turbine/binder/BinderErrorTest.java
+++ b/javatests/com/google/turbine/binder/BinderErrorTest.java
@@ -22,11 +22,19 @@ import static org.junit.Assert.fail;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.Processing.ProcessorInfo;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.parse.Parser;
import com.google.turbine.tree.Tree.CompUnit;
import java.util.Arrays;
import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -259,7 +267,7 @@ public class BinderErrorTest {
{
"<>:2: error: java.lang.Object is not an annotation", //
" @Object int x;",
- " ^",
+ " ^",
},
},
{
@@ -271,7 +279,7 @@ public class BinderErrorTest {
{
"<>:2: error: java.lang.Deprecated is not @Repeatable", //
" @Deprecated @Deprecated int x;",
- " ^",
+ " ^",
},
},
{
@@ -283,7 +291,7 @@ public class BinderErrorTest {
{
"<>:2: error: could not resolve NoSuch.NoSuch", //
" @NoSuch.NoSuch int x;",
- " ^",
+ " ^",
},
},
{
@@ -417,6 +425,9 @@ public class BinderErrorTest {
"}",
},
{
+ "<>:1: error: cycle in class hierarchy: Cycle",
+ "class Cycle extends Cycle {",
+ " ^",
"<>:2: error: could not resolve NoSuch", //
" NoSuch f;",
" ^",
@@ -501,7 +512,7 @@ public class BinderErrorTest {
" ^",
"<>:3: error: could not resolve NoSuchAnno",
"@NoSuchAnno",
- " ^",
+ "^",
},
},
{
@@ -568,7 +579,127 @@ public class BinderErrorTest {
"@One.A(b = {@One.NoSuch})",
" ^",
},
- }
+ },
+ {
+ {
+ "public class Test {", //
+ " @interface Anno {",
+ " Class<?> value() default Object.class;",
+ " }",
+ " @Anno(NoSuch.class) int x;",
+ " @Anno(NoSuch.class) int y;",
+ "}",
+ },
+ {
+ "<>:5: error: could not resolve NoSuch",
+ " @Anno(NoSuch.class) int x;",
+ " ^",
+ "<>:6: error: could not resolve NoSuch",
+ " @Anno(NoSuch.class) int y;",
+ " ^",
+ },
+ },
+ {
+ {
+ "public class Test {", //
+ " @A @B void f() {}",
+ "}",
+ },
+ {
+ "<>:2: error: could not resolve A",
+ " @A @B void f() {}",
+ " ^",
+ "<>:2: error: could not resolve B",
+ " @A @B void f() {}",
+ " ^",
+ },
+ },
+ {
+ {
+ "public class Test {", //
+ " @A(\"bar\") void f() {}",
+ "}",
+ },
+ {
+ "<>:2: error: could not resolve A", //
+ " @A(\"bar\") void f() {}",
+ " ^",
+ },
+ },
+ {
+ {
+ "@NoSuch",
+ "@interface A {", //
+ "}",
+ },
+ {
+ "<>:1: error: could not resolve NoSuch", //
+ "@NoSuch",
+ "^",
+ },
+ },
+ {
+ {
+ "public class Test {", //
+ " @String @String int x;",
+ "}",
+ },
+ {
+ "<>:2: error: java.lang.String is not an annotation",
+ " @String @String int x;",
+ " ^",
+ "<>:2: error: java.lang.String is not an annotation",
+ " @String @String int x;",
+ " ^",
+ },
+ },
+ {
+ {
+ "@interface Anno {",
+ " int value();",
+ "}",
+ "enum E {",
+ " ONE",
+ "}",
+ "@Anno(value = E.ONE)",
+ "interface Test {}",
+ },
+ {
+ "<>:7: error: could not evaluate constant expression", //
+ "@Anno(value = E.ONE)",
+ " ^",
+ },
+ },
+ {
+ {
+ "class T extends T {}",
+ },
+ {
+ "<>:1: error: cycle in class hierarchy: T", "class T extends T {}", " ^",
+ },
+ },
+ {
+ {
+ "class T implements T {}",
+ },
+ {
+ "<>:1: error: cycle in class hierarchy: T",
+ "class T implements T {}",
+ " ^",
+ },
+ },
+ {
+ {
+ "class T {", //
+ " static final String s = \"a\" + + \"b\";",
+ "}",
+ },
+ {
+ "<>:2: error: bad operand type String",
+ " static final String s = \"a\" + + \"b\";",
+ " ^",
+ },
+ },
};
return Arrays.asList((Object[][]) testCases);
}
@@ -596,6 +727,41 @@ public class BinderErrorTest {
}
}
+ @SupportedAnnotationTypes("*")
+ static class HelloWorldProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ return false;
+ }
+ }
+
+ // 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));
+ }
+ }
+
private static CompUnit parseLines(String... lines) {
return Parser.parse(lines(lines));
}
diff --git a/javatests/com/google/turbine/binder/BinderTest.java b/javatests/com/google/turbine/binder/BinderTest.java
index 4b1e890..e238ee0 100644
--- a/javatests/com/google/turbine/binder/BinderTest.java
+++ b/javatests/com/google/turbine/binder/BinderTest.java
@@ -36,8 +36,6 @@ import com.google.turbine.tree.Tree;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.jar.JarEntry;
@@ -55,22 +53,21 @@ public class BinderTest {
@Test
public void hello() throws Exception {
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "package a;", //
- "public class A {",
- " public class Inner1 extends b.B {",
- " }",
- " public class Inner2 extends A.Inner1 {",
- " }",
- "}"));
- units.add(
- parseLines(
- "package b;", //
- "import a.A;",
- "public class B extends A {",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "package a;", //
+ "public class A {",
+ " public class Inner1 extends b.B {",
+ " }",
+ " public class Inner2 extends A.Inner1 {",
+ " }",
+ "}"),
+ parseLines(
+ "package b;", //
+ "import a.A;",
+ "public class B extends A {",
+ "}"));
ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound =
Binder.bind(
@@ -103,20 +100,19 @@ public class BinderTest {
@Test
public void interfaces() throws Exception {
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "package com.i;", //
- "public interface I {",
- " public class IInner {",
- " }",
- "}"));
- units.add(
- parseLines(
- "package b;", //
- "class B implements com.i.I {",
- " class BInner extends IInner {}",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "package com.i;", //
+ "public interface I {",
+ " public class IInner {",
+ " }",
+ "}"),
+ parseLines(
+ "package b;", //
+ "class B implements com.i.I {",
+ " class BInner extends IInner {}",
+ "}"));
ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound =
Binder.bind(
@@ -143,20 +139,19 @@ public class BinderTest {
@Test
public void imports() throws Exception {
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "package com.test;", //
- "public class Test {",
- " public static class Inner {}",
- "}"));
- units.add(
- parseLines(
- "package other;", //
- "import com.test.Test.Inner;",
- "import no.such.Class;", // imports are resolved lazily on-demand
- "public class Foo extends Inner {",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "package com.test;", //
+ "public class Test {",
+ " public static class Inner {}",
+ "}"),
+ parseLines(
+ "package other;", //
+ "import com.test.Test.Inner;",
+ "import no.such.Class;", // imports are resolved lazily on-demand
+ "public class Foo extends Inner {",
+ "}"));
ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound =
Binder.bind(
@@ -172,21 +167,20 @@ public class BinderTest {
@Test
public void cycle() throws Exception {
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "package a;", //
- "import b.B;",
- "public class A extends B.Inner {",
- " class Inner {}",
- "}"));
- units.add(
- parseLines(
- "package b;", //
- "import a.A;",
- "public class B extends A.Inner {",
- " class Inner {}",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "package a;", //
+ "import b.B;",
+ "public class A extends B.Inner {",
+ " class Inner {}",
+ "}"),
+ parseLines(
+ "package b;", //
+ "import a.A;",
+ "public class B extends A.Inner {",
+ " class Inner {}",
+ "}"));
try {
Binder.bind(
@@ -202,12 +196,12 @@ public class BinderTest {
@Test
public void annotationDeclaration() throws Exception {
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "package com.test;", //
- "public @interface Annotation {",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "package com.test;", //
+ "public @interface Annotation {",
+ "}"));
ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound =
Binder.bind(
@@ -230,13 +224,13 @@ public class BinderTest {
@Test
public void helloBytecode() throws Exception {
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "package a;", //
- "import java.util.Map.Entry;",
- "public class A implements Entry {",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "package a;", //
+ "import java.util.Map.Entry;",
+ "public class A implements Entry {",
+ "}"));
ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound =
Binder.bind(
@@ -268,15 +262,15 @@ public class BinderTest {
jos.write(lib.get("B"));
}
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "import java.lang.annotation.Target;",
- "import java.lang.annotation.ElementType;",
- "public class C implements B {",
- " @Target(ElementType.TYPE_USE)",
- " @interface A {};",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "import java.lang.annotation.Target;",
+ "import java.lang.annotation.ElementType;",
+ "public class C implements B {",
+ " @Target(ElementType.TYPE_USE)",
+ " @interface A {};",
+ "}"));
ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound =
Binder.bind(
@@ -294,13 +288,13 @@ public class BinderTest {
// (Error reporting is deferred to javac.)
@Test
public void invalidConst() throws Exception {
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "package a;", //
- "public class A {",
- " public static final boolean b = true == 42;",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "package a;", //
+ "public class A {",
+ " public static final boolean b = true == 42;",
+ "}"));
ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound =
Binder.bind(
diff --git a/javatests/com/google/turbine/binder/ClassPathBinderTest.java b/javatests/com/google/turbine/binder/ClassPathBinderTest.java
index 3f41706..c11d814 100644
--- a/javatests/com/google/turbine/binder/ClassPathBinderTest.java
+++ b/javatests/com/google/turbine/binder/ClassPathBinderTest.java
@@ -44,7 +44,10 @@ 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.jar.JarEntry;
+import java.util.jar.JarOutputStream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -161,4 +164,18 @@ public class ClassPathBinderTest {
assertThat(e).hasMessageThat().contains("NOT_A_JAR");
}
}
+
+ @Test
+ public void resources() 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));
+ }
+ ClassPath classPath = ClassPathBinder.bindClasspath(ImmutableList.of(path));
+ assertThat(new String(classPath.resource("foo/bar/hello.txt").get(), UTF_8)).isEqualTo("hello");
+ assertThat(classPath.resource("foo/bar/Baz.class")).isNull();
+ }
}
diff --git a/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java b/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
index 2a0de48..3e841a5 100644
--- a/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
+++ b/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
@@ -22,14 +22,14 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH;
import static java.util.Objects.requireNonNull;
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
import com.google.common.io.ByteStreams;
import com.google.turbine.binder.bound.TurbineClassValue;
import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
import com.google.turbine.binder.env.CompoundEnv;
import com.google.turbine.binder.env.Env;
-import com.google.turbine.binder.env.SimpleEnv;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.type.Type;
import com.google.turbine.type.Type.ClassTy;
@@ -37,7 +37,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.UncheckedIOException;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -84,6 +86,10 @@ public class BytecodeBoundClassTest {
<X, Y extends X, Z extends Throwable> X foo(@Deprecated X bar, Y baz) throws IOException, Z {
return null;
}
+
+ void baz() throws IOException {
+ throw new IOException();
+ }
}
@Test
@@ -97,6 +103,12 @@ public class BytecodeBoundClassTest {
assertThat(m.parameters().get(0).annotations()).hasSize(1);
assertThat(m.parameters().get(0).name()).isEqualTo("bar");
assertThat(m.exceptions()).hasSize(2);
+
+ MethodInfo b =
+ getBytecodeBoundClass(HasMethod.class).methods().stream()
+ .filter(x -> x.name().equals("baz"))
+ .collect(onlyElement());
+ assertThat(b.exceptions()).hasSize(1);
}
@interface VoidAnno {
@@ -116,6 +128,53 @@ public class BytecodeBoundClassTest {
.isEqualTo(Type.TyKind.ARRAY_TY);
}
+ static class HasField {
+ @Deprecated List<String> foo;
+ }
+
+ @Test
+ public void fieldTypes() {
+ FieldInfo f =
+ getBytecodeBoundClass(HasField.class).fields().stream()
+ .filter(x -> x.name().equals("foo"))
+ .collect(onlyElement());
+
+ assertThat(Iterables.getLast(((ClassTy) f.type()).classes()).targs()).hasSize(1);
+ assertThat(f.annotations()).hasSize(1);
+ }
+
+ interface Y {
+ Object f();
+ }
+
+ interface X extends Y {
+ String f();
+ }
+
+ @Test
+ public void covariantBridges() {
+ assertThat(getBytecodeBoundClass(X.class, Y.class).methods()).hasSize(1);
+ }
+
+ interface A<T> {
+ void f(T t);
+ }
+
+ interface B<T extends Number> extends A<T> {
+ @Override
+ void f(T t);
+ }
+
+ interface C<T extends Integer> extends B<T> {
+ @Override
+ void f(T t);
+ }
+
+ @Test
+ public void genericBridges() {
+ assertThat(getBytecodeBoundClass(C.class, B.class, A.class).methods()).hasSize(1);
+ }
+
private static byte[] toByteArrayOrDie(InputStream is) {
try {
return ByteStreams.toByteArray(is);
@@ -135,15 +194,29 @@ public class BytecodeBoundClassTest {
"test.jar");
}
- private BytecodeBoundClass getBytecodeBoundClass(Class<?> clazz) {
- Env<ClassSymbol, BytecodeBoundClass> env = TURBINE_BOOTCLASSPATH.env();
- env =
- CompoundEnv.of(env)
+ private BytecodeBoundClass getBytecodeBoundClass(Class<?> clazz, Class<?>... classpath) {
+ Map<ClassSymbol, BytecodeBoundClass> map = new HashMap<>();
+ Env<ClassSymbol, BytecodeBoundClass> env =
+ CompoundEnv.of(TURBINE_BOOTCLASSPATH.env())
.append(
- new SimpleEnv<>(
- ImmutableMap.of(
- new ClassSymbol(BytecodeBoundClass.class.getName().replace('.', '/')),
- getBytecodeBoundClass(env, BytecodeBoundClassTest.class))));
+ new Env<ClassSymbol, BytecodeBoundClass>() {
+ @Override
+ public BytecodeBoundClass get(ClassSymbol sym) {
+ return map.get(sym);
+ }
+ });
+ addClass(clazz, map, env);
+ addClass(BytecodeBoundClassTest.class, map, env);
+ for (Class<?> c : classpath) {
+ addClass(c, map, env);
+ }
return getBytecodeBoundClass(env, clazz);
}
+
+ private void addClass(
+ Class<?> clazz,
+ Map<ClassSymbol, BytecodeBoundClass> map,
+ Env<ClassSymbol, BytecodeBoundClass> env) {
+ map.put(new ClassSymbol(clazz.getName().replace('.', '/')), getBytecodeBoundClass(env, clazz));
+ }
}
diff --git a/javatests/com/google/turbine/bytecode/ClassReaderTest.java b/javatests/com/google/turbine/bytecode/ClassReaderTest.java
index dda29ac..fb64541 100644
--- a/javatests/com/google/turbine/bytecode/ClassReaderTest.java
+++ b/javatests/com/google/turbine/bytecode/ClassReaderTest.java
@@ -35,6 +35,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.ModuleVisitor;
import org.objectweb.asm.Opcodes;
@@ -152,7 +153,8 @@ public class ClassReaderTest {
"<X:Ljava/lang/Object;>Ljava/lang/Object;",
"java/lang/Object",
null);
- cw.visitField(Opcodes.ACC_PUBLIC, "x", "I", null, null);
+ FieldVisitor fv = cw.visitField(Opcodes.ACC_PUBLIC, "x", "I", null, null);
+ fv.visitAnnotation("Ljava/lang/Deprecated;", true);
cw.visitField(
Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
"y",
@@ -173,7 +175,9 @@ public class ClassReaderTest {
assertThat(x.descriptor()).isEqualTo("I");
assertThat(x.signature()).isNull();
assertThat(x.value()).isNull();
- assertThat(x.annotations()).isEmpty();
+ assertThat(x.annotations()).hasSize(1);
+ ClassFile.AnnotationInfo annotation = Iterables.getOnlyElement(x.annotations());
+ assertThat(annotation.typeName()).isEqualTo("Ljava/lang/Deprecated;");
ClassFile.FieldInfo y = classFile.fields().get(1);
assertThat(y.access())
@@ -182,12 +186,13 @@ public class ClassReaderTest {
assertThat(y.descriptor()).isEqualTo("I");
assertThat(y.value().constantTypeKind()).isEqualTo(TurbineConstantTypeKind.INT);
assertThat(((Const.IntValue) y.value()).value()).isEqualTo(42);
+ assertThat(y.annotations()).isEmpty();
ClassFile.FieldInfo z = classFile.fields().get(2);
assertThat(z.name()).isEqualTo("z");
assertThat(z.descriptor()).isEqualTo("Ljava/util/List;");
- // don't bother reading signatures for fields; we only care about constants
- assertThat(z.signature()).isNull();
+ assertThat(z.signature()).isEqualTo("Ljava/util/List<TX;>;");
+ assertThat(z.annotations()).isEmpty();
}
@Test
diff --git a/javatests/com/google/turbine/bytecode/ClassWriterTest.java b/javatests/com/google/turbine/bytecode/ClassWriterTest.java
index e544c15..71cf356 100644
--- a/javatests/com/google/turbine/bytecode/ClassWriterTest.java
+++ b/javatests/com/google/turbine/bytecode/ClassWriterTest.java
@@ -17,6 +17,7 @@
package com.google.turbine.bytecode;
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 com.google.common.collect.ImmutableList;
@@ -84,7 +85,7 @@ public class ClassWriterTest {
/* classes= */ null,
fileManager.getJavaFileObjects(path));
- assertThat(task.call()).named(collector.getDiagnostics().toString()).isTrue();
+ assertWithMessage(collector.getDiagnostics().toString()).that(task.call()).isTrue();
byte[] original = Files.readAllBytes(out.resolve("test/Test.class"));
byte[] actual = ClassWriter.writeClass(ClassReader.read(null, original));
diff --git a/javatests/com/google/turbine/bytecode/sig/SigRegressionTest.java b/javatests/com/google/turbine/bytecode/sig/SigRegressionTest.java
index a0f0774..042bb80 100644
--- a/javatests/com/google/turbine/bytecode/sig/SigRegressionTest.java
+++ b/javatests/com/google/turbine/bytecode/sig/SigRegressionTest.java
@@ -102,5 +102,6 @@ public class SigRegressionTest {
input = "LA<[-[Z>.I;";
sig = new SigParser(input).parseClassSig();
+ assertThat(SigWriter.classSig(sig)).isEqualTo(input);
}
}
diff --git a/javatests/com/google/turbine/deps/AbstractTransitiveTest.java b/javatests/com/google/turbine/deps/AbstractTransitiveTest.java
index 4cb8adf..c5b68ff 100644
--- a/javatests/com/google/turbine/deps/AbstractTransitiveTest.java
+++ b/javatests/com/google/turbine/deps/AbstractTransitiveTest.java
@@ -36,7 +36,6 @@ import java.nio.file.Path;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -143,7 +142,7 @@ public abstract class AbstractTransitiveTest {
// Explicitly use turbine; javac-turbine doesn't support direct-classpath compilations.
Path libc = temporaryFolder.newFolder().toPath().resolve("out.jar");
- List<String> sources =
+ ImmutableList<String> sources =
new SourceBuilder()
.addSourceLines(
"c/C.java",
@@ -152,18 +151,17 @@ public abstract class AbstractTransitiveTest {
" @Anno(x = 2) static final Inner i; // a.A$Inner ",
" static final int X = CONST; // a.A#CONST",
"}")
- .build().stream()
+ .build()
+ .stream()
.map(Path::toString)
.collect(toImmutableList());
- boolean ok =
- Main.compile(
- optionsWithBootclasspath()
- .addSources(sources)
- .addClassPathEntries(
- ImmutableList.of(libb).stream().map(Path::toString).collect(toImmutableList()))
- .setOutput(libc.toString())
- .build());
- assertThat(ok).isTrue();
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSources(sources)
+ .setClassPath(
+ ImmutableList.of(libb).stream().map(Path::toString).collect(toImmutableList()))
+ .setOutput(libc.toString())
+ .build());
assertThat(readJar(libc).keySet())
.containsExactly(
diff --git a/javatests/com/google/turbine/deps/DependenciesTest.java b/javatests/com/google/turbine/deps/DependenciesTest.java
index b1da209..bc663cd 100644
--- a/javatests/com/google/turbine/deps/DependenciesTest.java
+++ b/javatests/com/google/turbine/deps/DependenciesTest.java
@@ -22,7 +22,6 @@ import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Streams;
import com.google.turbine.binder.Binder;
import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.ClassPathBinder;
@@ -40,7 +39,6 @@ import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -88,7 +86,7 @@ public class DependenciesTest {
static class DepsBuilder {
List<Path> classpath;
- List<CompUnit> units = new ArrayList<>();
+ ImmutableList.Builder<CompUnit> units = ImmutableList.builder();
DepsBuilder setClasspath(Path... classpath) {
this.classpath = ImmutableList.copyOf(classpath);
@@ -103,7 +101,7 @@ public class DependenciesTest {
DepsProto.Dependencies run() throws IOException {
BindingResult bound =
Binder.bind(
- units,
+ units.build(),
ClassPathBinder.bindClasspath(classpath),
TestClassPaths.TURBINE_BOOTCLASSPATH,
/* moduleVersion=*/ Optional.empty());
@@ -116,7 +114,7 @@ public class DependenciesTest {
}
private Map<Path, DepsProto.Dependency.Kind> depsMap(DepsProto.Dependencies deps) {
- return Streams.stream(deps.getDependencyList())
+ return deps.getDependencyList().stream()
.collect(Collectors.toMap(d -> Paths.get(d.getPath()), DepsProto.Dependency::getKind));
}
@@ -333,6 +331,96 @@ public class DependenciesTest {
}
}
+ @Test
+ public void annotations_recursive() throws Exception {
+ Path libA = libA();
+ Path libB = libB();
+
+ DepsProto.Dependencies deps =
+ new DepsBuilder()
+ .setClasspath(libA, libB)
+ .addSourceLines(
+ "Test.java", //
+ "import a.A;",
+ "import b.B;",
+ "@A(B.class)",
+ "class Test {",
+ "}")
+ .run();
+ assertThat(depsMap(deps))
+ .containsExactly(
+ libA, DepsProto.Dependency.Kind.EXPLICIT, libB, DepsProto.Dependency.Kind.EXPLICIT);
+ }
+
+ @Test
+ public void annotations_field() throws Exception {
+ Path libA = libA();
+ Path libB = libB();
+ DepsProto.Dependencies deps =
+ new DepsBuilder()
+ .setClasspath(libA, libB)
+ .addSourceLines(
+ "Test.java", //
+ "import a.A;",
+ "import b.B;",
+ "class Test {",
+ " @A(B.class)",
+ " int x;",
+ "}")
+ .run();
+ assertThat(depsMap(deps))
+ .containsExactly(
+ libA, DepsProto.Dependency.Kind.EXPLICIT, libB, DepsProto.Dependency.Kind.EXPLICIT);
+ }
+
+ @Test
+ public void annotations_method() throws Exception {
+ Path libA = libA();
+ Path libB = libB();
+ DepsProto.Dependencies deps =
+ new DepsBuilder()
+ .setClasspath(libA, libB)
+ .addSourceLines(
+ "Test.java", //
+ "import a.A;",
+ "import b.B;",
+ "class Test {",
+ " @A(B.class)",
+ " void f() {}",
+ "}")
+ .run();
+ assertThat(depsMap(deps))
+ .containsExactly(
+ libA, DepsProto.Dependency.Kind.EXPLICIT, libB, DepsProto.Dependency.Kind.EXPLICIT);
+ }
+
+ private Path libB() throws Exception {
+ return new LibraryBuilder()
+ .addSourceLines(
+ "b/B.java",
+ "package b;",
+ "import java.lang.annotation.Retention;",
+ "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
+ "@Retention(RUNTIME)",
+ "public @interface B {",
+ "}")
+ .compileToJar("libb.jar");
+ }
+
+ private Path libA() throws Exception {
+ return new LibraryBuilder()
+ .addSourceLines(
+ "a/A.java",
+ "package a;",
+ "import java.lang.annotation.Retention;",
+ "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
+ "@Retention(RUNTIME)",
+ "public @interface A {",
+ " Class<?> value() default Object.class;",
+ "}")
+ .compileToJar("liba.jar");
+ }
+
void writeDeps(Path path, ImmutableMap<String, DepsProto.Dependency.Kind> deps)
throws IOException {
DepsProto.Dependencies.Builder builder =
diff --git a/javatests/com/google/turbine/deps/TransitiveTest.java b/javatests/com/google/turbine/deps/TransitiveTest.java
index f8c5b50..2c9f807 100644
--- a/javatests/com/google/turbine/deps/TransitiveTest.java
+++ b/javatests/com/google/turbine/deps/TransitiveTest.java
@@ -17,7 +17,6 @@
package com.google.turbine.deps;
import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.truth.Truth.assertThat;
import static com.google.turbine.testing.TestClassPaths.optionsWithBootclasspath;
import com.google.common.collect.ImmutableList;
@@ -34,15 +33,12 @@ public class TransitiveTest extends AbstractTransitiveTest {
protected Path runTurbine(ImmutableList<Path> sources, ImmutableList<Path> classpath)
throws IOException {
Path out = temporaryFolder.newFolder().toPath().resolve("out.jar");
- boolean ok =
- Main.compile(
- optionsWithBootclasspath()
- .addSources(sources.stream().map(Path::toString).collect(toImmutableList()))
- .addClassPathEntries(
- classpath.stream().map(Path::toString).collect(toImmutableList()))
- .setOutput(out.toString())
- .build());
- assertThat(ok).isTrue();
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSources(sources.stream().map(Path::toString).collect(toImmutableList()))
+ .setClassPath(classpath.stream().map(Path::toString).collect(toImmutableList()))
+ .setOutput(out.toString())
+ .build());
return out;
}
}
diff --git a/javatests/com/google/turbine/lower/IntegrationTestSupport.java b/javatests/com/google/turbine/lower/IntegrationTestSupport.java
index 680b073..a03473d 100644
--- a/javatests/com/google/turbine/lower/IntegrationTestSupport.java
+++ b/javatests/com/google/turbine/lower/IntegrationTestSupport.java
@@ -16,11 +16,13 @@
package com.google.turbine.lower;
-import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
+import static org.junit.Assert.fail;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
@@ -29,12 +31,13 @@ import com.google.common.io.MoreFiles;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import com.google.turbine.binder.Binder;
+import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.ClassPath;
import com.google.turbine.binder.ClassPathBinder;
import com.google.turbine.diag.SourceFile;
import com.google.turbine.parse.Parser;
import com.google.turbine.testing.AsmUtils;
-import com.google.turbine.tree.Tree;
+import com.google.turbine.tree.Tree.CompUnit;
import com.sun.source.util.JavacTask;
import com.sun.tools.javac.api.JavacTool;
import com.sun.tools.javac.file.JavacFileManager;
@@ -100,8 +103,11 @@ public class IntegrationTestSupport {
public static Map<String, byte[]> canonicalize(Map<String, byte[]> in) {
List<ClassNode> classes = toClassNodes(in);
- // drop anonymous classes
- classes = classes.stream().filter(n -> !isAnonymous(n)).collect(toCollection(ArrayList::new));
+ // drop local and anonymous classes
+ classes =
+ classes.stream()
+ .filter(n -> !isAnonymous(n) && !isLocal(n))
+ .collect(toCollection(ArrayList::new));
// collect all inner classes attributes
Map<String, InnerClassNode> infos = new HashMap<>();
@@ -123,6 +129,10 @@ public class IntegrationTestSupport {
return toByteCode(classes);
}
+ private static boolean isLocal(ClassNode n) {
+ return n.outerMethod != null;
+ }
+
private static boolean isAnonymous(ClassNode n) {
// JVMS 4.7.6: if C is anonymous, the value of the inner_name_index item must be zero
return n.innerClasses.stream().anyMatch(i -> i.name.equals(n.name) && i.innerName == null);
@@ -436,15 +446,41 @@ public class IntegrationTestSupport {
ClassPath bootClassPath,
Optional<String> moduleVersion)
throws IOException {
- List<Tree.CompUnit> units =
+ BindingResult bound = turbineAnalysis(input, classpath, bootClassPath, moduleVersion);
+ return Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes();
+ }
+
+ public static BindingResult turbineAnalysis(
+ Map<String, String> input,
+ ImmutableList<Path> classpath,
+ ClassPath bootClassPath,
+ Optional<String> moduleVersion)
+ throws IOException {
+ ImmutableList<CompUnit> units =
input.entrySet().stream()
.map(e -> new SourceFile(e.getKey(), e.getValue()))
.map(Parser::parse)
- .collect(toList());
+ .collect(toImmutableList());
- Binder.BindingResult bound =
- Binder.bind(units, ClassPathBinder.bindClasspath(classpath), bootClassPath, moduleVersion);
- return Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes();
+ return Binder.bind(
+ units, ClassPathBinder.bindClasspath(classpath), bootClassPath, moduleVersion);
+ }
+
+ public static JavacTask runJavacAnalysis(
+ Map<String, String> sources, Collection<Path> classpath, ImmutableList<String> options)
+ throws Exception {
+ return runJavacAnalysis(sources, classpath, options, new DiagnosticCollector<>());
+ }
+
+ public static JavacTask runJavacAnalysis(
+ Map<String, String> sources,
+ Collection<Path> classpath,
+ ImmutableList<String> options,
+ DiagnosticCollector<JavaFileObject> collector)
+ throws Exception {
+ FileSystem fs = Jimfs.newFileSystem(Configuration.unix());
+ Path out = fs.getPath("out");
+ return setupJavac(sources, classpath, options, collector, fs, out);
}
public static Map<String, byte[]> runJavac(
@@ -457,10 +493,46 @@ public class IntegrationTestSupport {
Map<String, String> sources, Collection<Path> classpath, ImmutableList<String> options)
throws Exception {
+ DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
FileSystem fs = Jimfs.newFileSystem(Configuration.unix());
+ Path out = fs.getPath("out");
+ JavacTask task = setupJavac(sources, classpath, options, collector, fs, out);
+
+ if (!task.call()) {
+ fail(collector.getDiagnostics().stream().map(d -> d.toString()).collect(joining("\n")));
+ }
+
+ List<Path> classes = new ArrayList<>();
+ Files.walkFileTree(
+ out,
+ new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path path, BasicFileAttributes attrs)
+ throws IOException {
+ if (path.getFileName().toString().endsWith(".class")) {
+ classes.add(path);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ Map<String, byte[]> result = new LinkedHashMap<>();
+ for (Path path : classes) {
+ String r = out.relativize(path).toString();
+ result.put(r.substring(0, r.length() - ".class".length()), Files.readAllBytes(path));
+ }
+ return result;
+ }
+
+ private static JavacTask setupJavac(
+ Map<String, String> sources,
+ Collection<Path> classpath,
+ ImmutableList<String> options,
+ DiagnosticCollector<JavaFileObject> collector,
+ FileSystem fs,
+ Path out)
+ throws IOException {
Path srcs = fs.getPath("srcs");
- Path out = fs.getPath("out");
Files.createDirectories(out);
@@ -475,7 +547,6 @@ public class IntegrationTestSupport {
}
JavacTool compiler = JavacTool.create();
- DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
JavacFileManager fileManager = new JavacFileManager(new Context(), true, UTF_8);
fileManager.setLocationFromPaths(StandardLocation.CLASS_OUTPUT, ImmutableList.of(out));
fileManager.setLocationFromPaths(StandardLocation.CLASS_PATH, classpath);
@@ -487,36 +558,13 @@ public class IntegrationTestSupport {
StandardLocation.locationFor("MODULE_SOURCE_PATH"), ImmutableList.of(srcs));
}
- JavacTask task =
- compiler.getTask(
- new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.err, UTF_8)), true),
- fileManager,
- collector,
- options,
- ImmutableList.of(),
- fileManager.getJavaFileObjectsFromPaths(inputs));
-
- assertThat(task.call()).named(collector.getDiagnostics().toString()).isTrue();
-
- List<Path> classes = new ArrayList<>();
- Files.walkFileTree(
- out,
- new SimpleFileVisitor<Path>() {
- @Override
- public FileVisitResult visitFile(Path path, BasicFileAttributes attrs)
- throws IOException {
- if (path.getFileName().toString().endsWith(".class")) {
- classes.add(path);
- }
- return FileVisitResult.CONTINUE;
- }
- });
- Map<String, byte[]> result = new LinkedHashMap<>();
- for (Path path : classes) {
- String r = out.relativize(path).toString();
- result.put(r.substring(0, r.length() - ".class".length()), Files.readAllBytes(path));
- }
- return result;
+ return compiler.getTask(
+ new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.err, UTF_8)), true),
+ fileManager,
+ collector,
+ options,
+ ImmutableList.of(),
+ fileManager.getJavaFileObjectsFromPaths(inputs));
}
/** Normalizes and stringifies a collection of class files. */
@@ -535,17 +583,17 @@ public class IntegrationTestSupport {
return sb.toString();
}
- static class TestInput {
+ public static class TestInput {
- final Map<String, String> sources;
- final Map<String, String> classes;
+ public final Map<String, String> sources;
+ public final Map<String, String> classes;
public TestInput(Map<String, String> sources, Map<String, String> classes) {
this.sources = sources;
this.classes = classes;
}
- static TestInput parse(String text) {
+ public static TestInput parse(String text) {
Map<String, String> sources = new LinkedHashMap<>();
Map<String, String> classes = new LinkedHashMap<>();
String className = null;
diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
index f7e9c18..85c3450 100644
--- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
@@ -308,6 +308,9 @@ public class LowerIntegrationTest {
"shadow_inherited.test",
"static_final_boxed.test",
"anno_void.test",
+ "tyanno_varargs.test",
+ "tyanno_inner.test",
+ "local.test",
};
List<Object[]> tests =
ImmutableList.copyOf(testCases).stream().map(x -> new Object[] {x}).collect(toList());
diff --git a/javatests/com/google/turbine/lower/LowerSignatureTest.java b/javatests/com/google/turbine/lower/LowerSignatureTest.java
index 5ccbf01..08bc46d 100644
--- a/javatests/com/google/turbine/lower/LowerSignatureTest.java
+++ b/javatests/com/google/turbine/lower/LowerSignatureTest.java
@@ -83,7 +83,7 @@ public class LowerSignatureTest {
assertThat(SigWriter.type(new LowerSignature().signature(type)))
.isEqualTo("Ltest/Outer<Ljava/lang/Object;>.Inner<Ljava/lang/Object;>;");
// Type#toString is only for debugging
- assertThat(type.toString()).isEqualTo("test/Outer<java/lang/Object>.Inner<java/lang/Object>");
+ assertThat(type.toString()).isEqualTo("test.Outer<java.lang.Object>.Inner<java.lang.Object>");
}
@Test
diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java
index 0de55c3..8151e81 100644
--- a/javatests/com/google/turbine/lower/LowerTest.java
+++ b/javatests/com/google/turbine/lower/LowerTest.java
@@ -33,6 +33,7 @@ import com.google.turbine.binder.env.SimpleEnv;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.bytecode.ByteReader;
import com.google.turbine.bytecode.ConstantPoolReader;
@@ -104,12 +105,13 @@ public class LowerTest {
new ClassSymbol("test/Test$Inner"),
ImmutableList.of(),
ImmutableList.of()))))),
+ /* lowerBound= */ null,
ImmutableList.of()));
int access = TurbineFlag.ACC_SUPER | TurbineFlag.ACC_PUBLIC;
ImmutableList<SourceTypeBoundClass.MethodInfo> methods =
ImmutableList.of(
new SourceTypeBoundClass.MethodInfo(
- new MethodSymbol(new ClassSymbol("test/Test"), "f"),
+ new MethodSymbol(-1, new ClassSymbol("test/Test"), "f"),
ImmutableMap.of(),
PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()),
ImmutableList.of(),
@@ -120,9 +122,9 @@ public class LowerTest {
ImmutableList.of(),
null),
new SourceTypeBoundClass.MethodInfo(
- new MethodSymbol(new ClassSymbol("test/Test"), "g"),
+ new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"),
ImmutableMap.of(
- new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "V"),
+ new TyVarSymbol(new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"), "V"),
new SourceTypeBoundClass.TyVarInfo(
IntersectionTy.create(
ImmutableList.of(
@@ -132,8 +134,9 @@ public class LowerTest {
new ClassSymbol("java/lang/Runnable"),
ImmutableList.of(),
ImmutableList.of()))))),
+ /* lowerBound= */ null,
ImmutableList.of()),
- new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "E"),
+ new TyVarSymbol(new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"), "E"),
new SourceTypeBoundClass.TyVarInfo(
IntersectionTy.create(
ImmutableList.of(
@@ -143,17 +146,20 @@ public class LowerTest {
new ClassSymbol("java/lang/Error"),
ImmutableList.of(),
ImmutableList.of()))))),
+ /* lowerBound= */ null,
ImmutableList.of())),
Type.VOID,
ImmutableList.of(
new SourceTypeBoundClass.ParamInfo(
+ new ParamSymbol(
+ new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"), "foo"),
PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()),
- "foo",
ImmutableList.of(),
0)),
ImmutableList.of(
TyVar.create(
- new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "E"),
+ new TyVarSymbol(
+ new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"), "E"),
ImmutableList.of())),
TurbineFlag.ACC_PUBLIC,
null,
@@ -350,7 +356,7 @@ public class LowerTest {
int typeRef, TypePath typePath, String desc, boolean visible) {
path[0] = typePath;
return null;
- };
+ }
};
}
},
@@ -604,9 +610,10 @@ public class LowerTest {
assertThat(error)
.hasMessageThat()
.contains(
- "Test.java:3: error: could not locate class file for A\n"
- + " I i;\n"
- + " ^");
+ lines(
+ "Test.java:3: error: could not locate class file for A",
+ " I i;",
+ " ^"));
}
}
@@ -640,6 +647,6 @@ public class LowerTest {
}
static String lines(String... lines) {
- return Joiner.on("\n").join(lines);
+ return Joiner.on(System.lineSeparator()).join(lines);
}
}
diff --git a/javatests/com/google/turbine/lower/testdata/local.test b/javatests/com/google/turbine/lower/testdata/local.test
new file mode 100644
index 0000000..fd6d4c5
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/local.test
@@ -0,0 +1,8 @@
+=== T.java ===
+class T {
+ Object f() {
+ class Local {
+ }
+ return new Local();
+ }
+} \ No newline at end of file
diff --git a/javatests/com/google/turbine/lower/testdata/tyanno_inner.test b/javatests/com/google/turbine/lower/testdata/tyanno_inner.test
new file mode 100644
index 0000000..d42b3a3
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/tyanno_inner.test
@@ -0,0 +1,16 @@
+%%% A.java %%%
+import static java.lang.annotation.ElementType.TYPE_PARAMETER;
+
+import java.lang.annotation.Target;
+
+@interface A {
+
+ @Target(TYPE_PARAMETER)
+ @interface I {
+ }
+}
+
+=== T.java ===
+abstract class T<@A.I X> {
+
+}
diff --git a/javatests/com/google/turbine/lower/testdata/tyanno_varargs.test b/javatests/com/google/turbine/lower/testdata/tyanno_varargs.test
new file mode 100644
index 0000000..1fbc1e1
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/tyanno_varargs.test
@@ -0,0 +1,12 @@
+=== T.java ===
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE_USE;
+
+import java.lang.annotation.Target;
+
+abstract class T {
+ @Target({PARAMETER, TYPE_USE})
+ @interface A {}
+
+ void f(boolean a, @A String b, Object @A... xs) {}
+}
diff --git a/javatests/com/google/turbine/main/MainTest.java b/javatests/com/google/turbine/main/MainTest.java
index 65f3167..5d47632 100644
--- a/javatests/com/google/turbine/main/MainTest.java
+++ b/javatests/com/google/turbine/main/MainTest.java
@@ -17,17 +17,29 @@
package com.google.turbine.main;
import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_VERSION;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
+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 com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteStreams;
import com.google.common.io.MoreFiles;
+import com.google.protobuf.ExtensionRegistry;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.options.TurbineOptions;
+import com.google.turbine.proto.ManifestProto;
+import java.io.BufferedInputStream;
+import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
@@ -35,16 +47,27 @@ import java.time.ZoneId;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
+import java.util.stream.Stream;
+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.TypeElement;
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.MethodVisitor;
+import org.objectweb.asm.Opcodes;
@RunWith(JUnit4.class)
public class MainTest {
@@ -84,13 +107,11 @@ public class MainTest {
Path output = temporaryFolder.newFile("output.jar").toPath();
- boolean ok =
- Main.compile(
- optionsWithBootclasspath()
- .addSources(ImmutableList.of(src.toString()))
- .setOutput(output.toString())
- .build());
- assertThat(ok).isTrue();
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSources(ImmutableList.of(src.toString()))
+ .setOutput(output.toString())
+ .build());
Map<String, byte[]> data = readJar(output);
assertThat(data.keySet()).containsExactly("test/package-info.class");
@@ -106,13 +127,11 @@ public class MainTest {
Path output = temporaryFolder.newFile("output.jar").toPath();
- boolean ok =
- Main.compile(
- optionsWithBootclasspath()
- .setSourceJars(ImmutableList.of(srcjar.toString()))
- .setOutput(output.toString())
- .build());
- assertThat(ok).isTrue();
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSourceJars(ImmutableList.of(srcjar.toString()))
+ .setOutput(output.toString())
+ .build());
Map<String, byte[]> data = readJar(output);
assertThat(data.keySet()).containsExactly("test/package-info.class");
@@ -150,15 +169,13 @@ public class MainTest {
Path output = temporaryFolder.newFile("output.jar").toPath();
- boolean ok =
- Main.compile(
- TurbineOptions.builder()
- .setRelease("9")
- .addSources(ImmutableList.of(src.toString()))
- .setSourceJars(ImmutableList.of(srcjar.toString()))
- .setOutput(output.toString())
- .build());
- assertThat(ok).isTrue();
+ Main.compile(
+ TurbineOptions.builder()
+ .setRelease("9")
+ .setSources(ImmutableList.of(src.toString()))
+ .setSourceJars(ImmutableList.of(srcjar.toString()))
+ .setOutput(output.toString())
+ .build());
Map<String, byte[]> data = readJar(output);
assertThat(data.keySet())
@@ -171,26 +188,48 @@ public class MainTest {
MoreFiles.asCharSink(src, UTF_8).write("class Foo {}");
Path output = temporaryFolder.newFile("output.jar").toPath();
+ Path gensrcOutput = temporaryFolder.newFile("gensrcOutput.jar").toPath();
- boolean ok =
- Main.compile(
- optionsWithBootclasspath()
- .addSources(ImmutableList.of(src.toString()))
- .setTargetLabel("//foo:foo")
- .setInjectingRuleKind("foo_library")
- .setOutput(output.toString())
- .build());
- assertThat(ok).isTrue();
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSources(ImmutableList.of(src.toString()))
+ .setTargetLabel("//foo:foo")
+ .setInjectingRuleKind("foo_library")
+ .setOutput(output.toString())
+ .setGensrcOutput(gensrcOutput.toString())
+ .build());
try (JarFile jarFile = new JarFile(output.toFile())) {
+ try (Stream<JarEntry> entries = jarFile.stream()) {
+ assertThat(entries.map(JarEntry::getName))
+ .containsAtLeast("META-INF/", "META-INF/MANIFEST.MF");
+ }
Manifest manifest = jarFile.getManifest();
Attributes attributes = manifest.getMainAttributes();
- assertThat(attributes.getValue("Target-Label")).isEqualTo("//foo:foo");
- assertThat(attributes.getValue("Injecting-Rule-Kind")).isEqualTo("foo_library");
+ ImmutableMap<String, ?> entries =
+ attributes.entrySet().stream()
+ .collect(toImmutableMap(e -> e.getKey().toString(), Map.Entry::getValue));
+ assertThat(entries)
+ .containsExactly(
+ "Created-By", "bazel",
+ "Manifest-Version", "1.0",
+ "Target-Label", "//foo:foo",
+ "Injecting-Rule-Kind", "foo_library");
assertThat(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();
+ Attributes attributes = manifest.getMainAttributes();
+ ImmutableMap<String, ?> entries =
+ attributes.entrySet().stream()
+ .collect(toImmutableMap(e -> e.getKey().toString(), Map.Entry::getValue));
+ assertThat(entries)
+ .containsExactly(
+ "Created-By", "bazel",
+ "Manifest-Version", "1.0");
+ }
}
@Test
@@ -201,13 +240,11 @@ public class MainTest {
Path output = temporaryFolder.newFile("output.jar").toPath();
- boolean ok =
- Main.compile(
- TurbineOptions.builder()
- .addSources(ImmutableList.of(src.toString()))
- .setOutput(output.toString())
- .build());
- assertThat(ok).isTrue();
+ Main.compile(
+ TurbineOptions.builder()
+ .setSources(ImmutableList.of(src.toString()))
+ .setOutput(output.toString())
+ .build());
Map<String, byte[]> data = readJar(output);
assertThat(data.keySet()).containsExactly("java/lang/Object.class");
@@ -223,7 +260,7 @@ public class MainTest {
try {
Main.compile(
TurbineOptions.builder()
- .addSources(ImmutableList.of(src.toString()))
+ .setSources(ImmutableList.of(src.toString()))
.setOutput(output.toString())
.build());
fail();
@@ -238,10 +275,200 @@ public class MainTest {
MoreFiles.asCharSink(src, UTF_8).write("public class Test {}");
try {
- Main.compile(optionsWithBootclasspath().addSources(ImmutableList.of(src.toString())).build());
+ Main.compile(optionsWithBootclasspath().setSources(ImmutableList.of(src.toString())).build());
fail();
} catch (UsageException expected) {
- assertThat(expected).hasMessageThat().contains("--output is required");
+ assertThat(expected)
+ .hasMessageThat()
+ .contains("at least one of --output, --gensrc_output, or --resource_output is required");
+ }
+ }
+
+ @Test
+ public void noSources() throws IOException {
+ // Compilations with no sources (or source jars) are accepted, and create empty for requested
+ // outputs. This is helpful for the Bazel integration, which allows java_library rules to be
+ // declared without sources.
+ File gensrc = temporaryFolder.newFile("gensrc.jar");
+ Main.compile(optionsWithBootclasspath().setGensrcOutput(gensrc.toString()).build());
+ try (JarFile jarFile = new JarFile(gensrc);
+ Stream<JarEntry> entries = jarFile.stream()) {
+ assertThat(entries.map(JarEntry::getName))
+ .containsExactly("META-INF/", "META-INF/MANIFEST.MF");
+ }
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class SourceGeneratingProcessor 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 (Writer writer = processingEnv.getFiler().createSourceFile("g.Gen").openWriter()) {
+ writer.write("package g; class Gen {}");
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ first = false;
+ }
+ return false;
+ }
+ }
+
+ @Test
+ public void testManifestProto() 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 gensrcOutput = temporaryFolder.newFile("gensrcOutput.jar").toPath();
+ Path manifestProtoOutput = temporaryFolder.newFile("manifest.proto").toPath();
+
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSources(ImmutableList.of(src.toString()))
+ .setTargetLabel("//foo:foo")
+ .setInjectingRuleKind("foo_library")
+ .setOutput(output.toString())
+ .setGensrcOutput(gensrcOutput.toString())
+ .setOutputManifest(manifestProtoOutput.toString())
+ .setProcessors(ImmutableList.of(SourceGeneratingProcessor.class.getName()))
+ .build());
+
+ assertThat(readManifestProto(manifestProtoOutput))
+ .isEqualTo(
+ ManifestProto.Manifest.newBuilder()
+ .addCompilationUnit(
+ ManifestProto.CompilationUnit.newBuilder()
+ .setPkg("f")
+ .addTopLevel("Foo")
+ .setPath(src.toString())
+ .setGeneratedByAnnotationProcessor(false)
+ .build())
+ .addCompilationUnit(
+ ManifestProto.CompilationUnit.newBuilder()
+ .setPkg("g")
+ .addTopLevel("Gen")
+ .setPath("g/Gen.java")
+ .setGeneratedByAnnotationProcessor(true)
+ .build())
+ .build());
+ }
+
+ private static ManifestProto.Manifest readManifestProto(Path manifestProtoOutput)
+ throws IOException {
+ ManifestProto.Manifest.Builder manifest = ManifestProto.Manifest.newBuilder();
+ try (InputStream is = new BufferedInputStream(Files.newInputStream(manifestProtoOutput))) {
+ manifest.mergeFrom(is, ExtensionRegistry.getEmptyRegistry());
+ }
+ return manifest.build();
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class CrashyProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public synchronized void init(ProcessingEnvironment processingEnv) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ return false;
+ }
+ }
+
+ @Test
+ public void noSourcesProcessing() throws IOException {
+ // Compilations with no sources shouldn't initialize annotation processors.
+ File gensrc = temporaryFolder.newFile("gensrc.jar");
+ Main.compile(
+ optionsWithBootclasspath()
+ .setProcessors(ImmutableList.of(CrashyProcessor.class.getName()))
+ .setGensrcOutput(gensrc.toString())
+ .build());
+ try (JarFile jarFile = new JarFile(gensrc);
+ Stream<JarEntry> entries = jarFile.stream()) {
+ assertThat(entries.map(JarEntry::getName))
+ .containsExactly("META-INF/", "META-INF/MANIFEST.MF");
+ }
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class ClassGeneratingProcessor 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 (OutputStream outputStream =
+ processingEnv.getFiler().createClassFile("g.Gen").openOutputStream()) {
+ outputStream.write(dump());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ first = false;
+ }
+ return false;
+ }
+
+ public static byte[] dump() {
+ ClassWriter classWriter = new ClassWriter(0);
+ classWriter.visit(
+ Opcodes.V1_8,
+ Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
+ "g/Gen",
+ null,
+ "java/lang/Object",
+ null);
+ {
+ MethodVisitor methodVisitor =
+ classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
+ methodVisitor.visitCode();
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ methodVisitor.visitInsn(Opcodes.RETURN);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+ return classWriter.toByteArray();
+ }
+ }
+
+ @Test
+ public void classGeneration() throws IOException {
+ Path src = temporaryFolder.newFile("package-info.jar").toPath();
+ MoreFiles.asCharSink(src, UTF_8).write("@Deprecated package test;");
+ File resources = temporaryFolder.newFile("resources.jar");
+ Main.compile(
+ optionsWithBootclasspath()
+ .setProcessors(ImmutableList.of(ClassGeneratingProcessor.class.getName()))
+ .setSources(ImmutableList.of(src.toString()))
+ .setResourceOutput(resources.toString())
+ .build());
+ try (JarFile jarFile = new JarFile(resources);
+ Stream<JarEntry> entries = jarFile.stream()) {
+ assertThat(entries.map(JarEntry::getName)).containsExactly("g/Gen.class");
}
}
}
diff --git a/javatests/com/google/turbine/main/ReducedClasspathTest.java b/javatests/com/google/turbine/main/ReducedClasspathTest.java
new file mode 100644
index 0000000..d74c640
--- /dev/null
+++ b/javatests/com/google/turbine/main/ReducedClasspathTest.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2019 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.main;
+
+import static com.google.common.truth.Truth.assertThat;
+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 com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.protobuf.ExtensionRegistry;
+import com.google.turbine.diag.TurbineError;
+import com.google.turbine.lower.IntegrationTestSupport;
+import com.google.turbine.lower.IntegrationTestSupport.TestInput;
+import com.google.turbine.main.Main.Result;
+import com.google.turbine.options.TurbineOptions.ReducedClasspathMode;
+import com.google.turbine.proto.DepsProto;
+import com.google.turbine.proto.DepsProto.Dependency.Kind;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+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.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ReducedClasspathTest {
+
+ @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private Path liba;
+ private Path libb;
+ private Path libc;
+ private Path libcJdeps;
+
+ @Before
+ public void setup() throws Exception {
+ Map<String, byte[]> compiled =
+ IntegrationTestSupport.runJavac(
+ TestInput.parse(
+ String.join(
+ "\n",
+ ImmutableList.of(
+ "=== a/A.java ===",
+ "package a;",
+ "public class A {",
+ " public static class I {}",
+ "}",
+ "=== b/B.java ===",
+ "package b;",
+ "import a.A;",
+ "public class B extends A {}",
+ "=== c/C.java ===",
+ "package c;",
+ "import b.B;",
+ "public class C extends B {}")))
+ .sources,
+ /* classpath= */ ImmutableList.of());
+
+ liba = createLibrary(compiled, "liba.jar", "a/A", "a/A$I");
+ libb = createLibrary(compiled, "libb.jar", "b/B");
+ libc = createLibrary(compiled, "libc.jar", "c/C");
+
+ libcJdeps = temporaryFolder.newFile("libc.jdeps").toPath();
+ try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(libcJdeps))) {
+ DepsProto.Dependencies.newBuilder()
+ .addDependency(
+ DepsProto.Dependency.newBuilder()
+ .setKind(Kind.EXPLICIT)
+ .setPath(libb.toString())
+ .build())
+ .build()
+ .writeTo(os);
+ }
+ }
+
+ private Path createLibrary(Map<String, byte[]> compiled, String jarPath, String... classNames)
+ throws IOException {
+ Path lib = temporaryFolder.newFile(jarPath).toPath();
+ try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(lib))) {
+ for (String className : classNames) {
+ jos.putNextEntry(new JarEntry(className + ".class"));
+ jos.write(compiled.get(className));
+ }
+ }
+ return lib;
+ }
+
+ @Test
+ public void succeedsWithoutFallingBack() throws Exception {
+ Path src = temporaryFolder.newFile("Test.java").toPath();
+ Files.write(
+ src,
+ ImmutableList.of(
+ "import c.C;", //
+ "class Test extends C {",
+ "}"),
+ UTF_8);
+
+ Path output = temporaryFolder.newFile("output.jar").toPath();
+
+ Result result =
+ Main.compile(
+ optionsWithBootclasspath()
+ .setOutput(output.toString())
+ .setSources(ImmutableList.of(src.toString()))
+ .setReducedClasspathMode(ReducedClasspathMode.JAVABUILDER_REDUCED)
+ .setClassPath(
+ ImmutableList.of(
+ // ensure that the compilation succeeds without falling back by adding
+ // a jar to the transitive classpath that doesn't exist, which would cause
+ // the compilation to fail if it fell back
+ temporaryFolder.newFile("no.such.jar").toString(),
+ liba.toString(),
+ libb.toString(),
+ libc.toString()))
+ .setDirectJars(ImmutableList.of(libc.toString()))
+ .setDepsArtifacts(ImmutableList.of(libcJdeps.toString()))
+ .build());
+ assertThat(result.transitiveClasspathFallback()).isFalse();
+ }
+
+ @Test
+ public void succeedsAfterFallingBack() throws Exception {
+ Path src = temporaryFolder.newFile("Test.java").toPath();
+ Files.write(
+ src,
+ ImmutableList.of(
+ "import c.C;", //
+ "class Test extends C {",
+ " I i;",
+ "}"),
+ UTF_8);
+
+ Path output = temporaryFolder.newFile("output.jar").toPath();
+
+ Result result =
+ Main.compile(
+ optionsWithBootclasspath()
+ .setOutput(output.toString())
+ .setSources(ImmutableList.of(src.toString()))
+ .setReducedClasspathMode(ReducedClasspathMode.JAVABUILDER_REDUCED)
+ .setClassPath(ImmutableList.of(liba.toString(), libb.toString(), libc.toString()))
+ .setDirectJars(ImmutableList.of(libc.toString()))
+ .setDepsArtifacts(ImmutableList.of(libcJdeps.toString()))
+ .build());
+ assertThat(result.transitiveClasspathFallback()).isTrue();
+ assertThat(result.reducedClasspathLength()).isEqualTo(2);
+ assertThat(result.transitiveClasspathLength()).isEqualTo(3);
+ }
+
+ @Test
+ public void bazelFallback() throws Exception {
+ Path src = temporaryFolder.newFile("Test.java").toPath();
+ Files.write(
+ src,
+ ImmutableList.of(
+ "import c.C;", //
+ "class Test extends C {",
+ " I i;",
+ "}"),
+ UTF_8);
+
+ Path output = temporaryFolder.newFile("output.jar").toPath();
+ Path jdeps = temporaryFolder.newFile("output.jdeps").toPath();
+
+ Result result =
+ Main.compile(
+ optionsWithBootclasspath()
+ .setOutput(output.toString())
+ .setTargetLabel("//java/com/google/foo")
+ .setOutputDeps(jdeps.toString())
+ .setSources(ImmutableList.of(src.toString()))
+ .setReducedClasspathMode(ReducedClasspathMode.BAZEL_REDUCED)
+ .setClassPath(ImmutableList.of(libc.toString()))
+ .setReducedClasspathLength(1)
+ .setFullClasspathLength(3)
+ .build());
+ assertThat(result.transitiveClasspathFallback()).isTrue();
+ assertThat(result.reducedClasspathLength()).isEqualTo(1);
+ assertThat(result.transitiveClasspathLength()).isEqualTo(3);
+ DepsProto.Dependencies.Builder deps = DepsProto.Dependencies.newBuilder();
+ try (InputStream is = new BufferedInputStream(Files.newInputStream(jdeps))) {
+ deps.mergeFrom(is, ExtensionRegistry.getEmptyRegistry());
+ }
+ assertThat(deps.build())
+ .isEqualTo(
+ DepsProto.Dependencies.newBuilder()
+ .setRequiresReducedClasspathFallback(true)
+ .setRuleLabel("//java/com/google/foo")
+ .build());
+ }
+
+ @Test
+ public void noFallbackWithoutDirectJarsAndJdeps() throws Exception {
+ Path src = temporaryFolder.newFile("Test.java").toPath();
+ Files.write(
+ src,
+ ImmutableList.of(
+ "import c.C;", //
+ "class Test extends C {",
+ " I i;",
+ "}"),
+ UTF_8);
+
+ 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");
+ }
+ }
+
+ static String lines(String... lines) {
+ return Joiner.on(System.lineSeparator()).join(lines);
+ }
+}
diff --git a/javatests/com/google/turbine/model/ConstTest.java b/javatests/com/google/turbine/model/ConstTest.java
index a64d0bf..984fd5a 100644
--- a/javatests/com/google/turbine/model/ConstTest.java
+++ b/javatests/com/google/turbine/model/ConstTest.java
@@ -16,12 +16,19 @@
package com.google.turbine.model;
+import static com.google.common.truth.Truth.assertThat;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.testing.EqualsTester;
-import com.google.turbine.binder.bound.AnnotationValue;
+import com.google.turbine.binder.bound.EnumConstantValue;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
import com.google.turbine.binder.bound.TurbineClassValue;
import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.model.Const.ArrayInitValue;
+import com.google.turbine.model.Const.IntValue;
+import com.google.turbine.type.AnnoInfo;
import com.google.turbine.type.Type.ClassTy;
import com.google.turbine.type.Type.PrimTy;
import org.junit.Test;
@@ -62,15 +69,31 @@ public class ConstTest {
new Const.ArrayInitValue(
ImmutableList.of(new Const.IntValue(3), new Const.IntValue(4))))
.addEqualityGroup(
- new AnnotationValue(
- new ClassSymbol("test/Anno"), ImmutableMap.of("value", new Const.IntValue(3))),
- new AnnotationValue(
- new ClassSymbol("test/Anno"), ImmutableMap.of("value", new Const.IntValue(3))))
+ new TurbineAnnotationValue(
+ new AnnoInfo(
+ null,
+ new ClassSymbol("test/Anno"),
+ null,
+ ImmutableMap.of("value", new Const.IntValue(3)))),
+ new TurbineAnnotationValue(
+ new AnnoInfo(
+ null,
+ new ClassSymbol("test/Anno"),
+ null,
+ ImmutableMap.of("value", new Const.IntValue(3)))))
.addEqualityGroup(
- new AnnotationValue(
- new ClassSymbol("test/Anno"), ImmutableMap.of("value", new Const.IntValue(4))),
- new AnnotationValue(
- new ClassSymbol("test/Anno"), ImmutableMap.of("value", new Const.IntValue(4))))
+ new TurbineAnnotationValue(
+ new AnnoInfo(
+ null,
+ new ClassSymbol("test/Anno"),
+ null,
+ ImmutableMap.of("value", new Const.IntValue(4)))),
+ new TurbineAnnotationValue(
+ new AnnoInfo(
+ null,
+ new ClassSymbol("test/Anno"),
+ null,
+ ImmutableMap.of("value", new Const.IntValue(4)))))
.addEqualityGroup(
new TurbineClassValue(ClassTy.asNonParametricClassTy(new ClassSymbol("test/Clazz"))),
new TurbineClassValue(ClassTy.asNonParametricClassTy(new ClassSymbol("test/Clazz"))))
@@ -82,4 +105,37 @@ public class ConstTest {
new TurbineClassValue(PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of())))
.testEquals();
}
+
+ @Test
+ public void toStringTest() {
+ assertThat(new Const.CharValue('\t').toString()).isEqualTo("\'\\t\'");
+ assertThat(new EnumConstantValue(new FieldSymbol(new ClassSymbol("Foo"), "CONST")).toString())
+ .isEqualTo("CONST");
+ assertThat(makeAnno(ImmutableMap.of())).isEqualTo("@p.Anno");
+ assertThat(makeAnno(ImmutableMap.of("value", new IntValue(1)))).isEqualTo("@p.Anno(1)");
+ assertThat(makeAnno(ImmutableMap.of("x", new IntValue(1)))).isEqualTo("@p.Anno(x=1)");
+ assertThat(
+ makeAnno(
+ ImmutableMap.of("value", new ArrayInitValue(ImmutableList.of(new IntValue(1))))))
+ .isEqualTo("@p.Anno({1})");
+ assertThat(
+ makeAnno(
+ ImmutableMap.of(
+ "value",
+ new ArrayInitValue(ImmutableList.of(new IntValue(1), new IntValue(2))))))
+ .isEqualTo("@p.Anno({1, 2})");
+ assertThat(
+ makeAnno(ImmutableMap.of("xs", new ArrayInitValue(ImmutableList.of(new IntValue(1))))))
+ .isEqualTo("@p.Anno(xs={1})");
+ assertThat(makeAnno(ImmutableMap.of("x", new IntValue(1), "y", new IntValue(2))))
+ .isEqualTo("@p.Anno(x=1, y=2)");
+ assertThat(new Const.StringValue("\"").toString()).isEqualTo("\"\\\"\"");
+ assertThat(new Const.ByteValue((byte) 42).toString()).isEqualTo("(byte)0x2a");
+ assertThat(new Const.ShortValue((short) 42).toString()).isEqualTo("42");
+ }
+
+ private static String makeAnno(ImmutableMap<String, Const> value) {
+ return new TurbineAnnotationValue(new AnnoInfo(null, new ClassSymbol("p/Anno"), null, value))
+ .toString();
+ }
}
diff --git a/javatests/com/google/turbine/options/TurbineOptionsTest.java b/javatests/com/google/turbine/options/TurbineOptionsTest.java
index a5872d9..d4b468b 100644
--- a/javatests/com/google/turbine/options/TurbineOptionsTest.java
+++ b/javatests/com/google/turbine/options/TurbineOptionsTest.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+import com.google.turbine.options.TurbineOptions.ReducedClasspathMode;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -95,7 +96,7 @@ public class TurbineOptionsTest {
assertThat(options.outputDeps()).hasValue("out.jdeps");
assertThat(options.targetLabel()).hasValue("//java/com/google/test");
assertThat(options.injectingRuleKind()).hasValue("foo_library");
- assertThat(options.shouldReduceClassPath()).isTrue();
+ assertThat(options.reducedClasspathMode()).isEqualTo(ReducedClasspathMode.NONE);
}
@Test
@@ -261,13 +262,22 @@ public class TurbineOptionsTest {
@Test
public void javacopts() throws Exception {
String[] lines = {
- "--javacopts", "--release", "9", "--", "--sources", "Test.java",
+ "--javacopts",
+ "--release",
+ "8",
+ "--",
+ "--sources",
+ "Test.java",
+ "--javacopts",
+ "--release",
+ "9",
+ "--",
};
TurbineOptions options =
TurbineOptionsParser.parse(Iterables.concat(BASE_ARGS, Arrays.asList(lines)));
- assertThat(options.javacOpts()).containsExactly("--release", "9").inOrder();
+ assertThat(options.javacOpts()).containsExactly("--release", "8", "--release", "9").inOrder();
assertThat(options.sources()).containsExactly("Test.java");
}
@@ -315,20 +325,14 @@ public class TurbineOptionsTest {
}
@Test
- public void shouldReduceClasspath() throws Exception {
- {
- TurbineOptions options =
- TurbineOptionsParser.parse(
- Iterables.concat(BASE_ARGS, ImmutableList.of("--reduce_classpath")));
- assertThat(options.shouldReduceClassPath()).isTrue();
- }
-
- {
- TurbineOptions options =
- TurbineOptionsParser.parse(
- Iterables.concat(BASE_ARGS, ImmutableList.of("--noreduce_classpath")));
- assertThat(options.shouldReduceClassPath()).isFalse();
- }
+ public void miscOutputs() throws Exception {
+ TurbineOptions options =
+ TurbineOptionsParser.parse(
+ Iterables.concat(
+ BASE_ARGS,
+ ImmutableList.of("--gensrc_output", "gensrc.jar", "--profile", "turbine.prof")));
+ assertThat(options.gensrcOutput()).hasValue("gensrc.jar");
+ assertThat(options.profile()).hasValue("turbine.prof");
}
@Test
@@ -350,4 +354,23 @@ public class TurbineOptionsTest {
} catch (IllegalArgumentException expected) {
}
}
+
+ @Test
+ public void builtinProcessors() throws Exception {
+ String[] lines = {"--builtin_processors", "BuiltinProcessor"};
+ TurbineOptions options =
+ TurbineOptionsParser.parse(Iterables.concat(BASE_ARGS, Arrays.asList(lines)));
+ assertThat(options.builtinProcessors()).containsExactly("BuiltinProcessor");
+ }
+
+ @Test
+ public void reducedClasspathMode() throws Exception {
+ for (ReducedClasspathMode mode : ReducedClasspathMode.values()) {
+ TurbineOptions options =
+ TurbineOptionsParser.parse(
+ Iterables.concat(
+ BASE_ARGS, ImmutableList.of("--reduce_classpath_mode", mode.name())));
+ assertThat(options.reducedClasspathMode()).isEqualTo(mode);
+ }
+ }
}
diff --git a/javatests/com/google/turbine/parse/CommentParserTest.java b/javatests/com/google/turbine/parse/CommentParserTest.java
new file mode 100644
index 0000000..a2f84d5
--- /dev/null
+++ b/javatests/com/google/turbine/parse/CommentParserTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.parse;
+
+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 com.google.common.base.Joiner;
+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;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class CommentParserTest {
+
+ @Test
+ public void comments() {
+ Tree.CompUnit unit =
+ Parser.parse(
+ Joiner.on('\n')
+ .join(
+ "package p;",
+ "/** hello world */",
+ "class Test {",
+ " /**",
+ " * This is",
+ " * class A",
+ " */",
+ " class A {",
+ " /** This is a method */",
+ " void f() {}",
+ " /** This is a field */",
+ " int g;",
+ " }",
+ " /* This is not javadoc */",
+ " class B {}",
+ " /**",
+ " * This is",
+ " * class C",
+ " */",
+ " class C {}",
+ "}\n"));
+ TyDecl decl = getOnlyElement(unit.decls());
+ assertThat(decl.javadoc()).isEqualTo(" hello world ");
+ assertThat(
+ decl.members().stream()
+ .map(Tree.TyDecl.class::cast)
+ .filter(c -> c.javadoc() != null)
+ .collect(toImmutableMap(c -> c.name().value(), c -> c.javadoc())))
+ .containsExactly(
+ "A", "\n * This is\n * class A\n ",
+ "C", "\n * This is\n * class C\n ");
+ TyDecl a = (TyDecl) decl.members().get(0);
+ MethDecl f = (MethDecl) a.members().get(0);
+ assertThat(f.javadoc()).isEqualTo(" This is a method ");
+ VarDecl g = (VarDecl) a.members().get(1);
+ assertThat(g.javadoc()).isEqualTo(" This is a field ");
+ }
+}
diff --git a/javatests/com/google/turbine/parse/JavacLexer.java b/javatests/com/google/turbine/parse/JavacLexer.java
index af82dbe..d8939f1 100644
--- a/javatests/com/google/turbine/parse/JavacLexer.java
+++ b/javatests/com/google/turbine/parse/JavacLexer.java
@@ -280,8 +280,7 @@ public class JavacLexer {
case CHARLITERAL:
return String.format(
"CHAR_LITERAL(%s)", SourceCodeEscapers.javaCharEscaper().escape(token.stringVal()));
- default:
- return token.kind.toString();
}
+ return token.kind.toString();
}
}
diff --git a/javatests/com/google/turbine/parse/LexerTest.java b/javatests/com/google/turbine/parse/LexerTest.java
index 4867a22..8530d52 100644
--- a/javatests/com/google/turbine/parse/LexerTest.java
+++ b/javatests/com/google/turbine/parse/LexerTest.java
@@ -40,12 +40,6 @@ public class LexerTest {
}
@Test
- public void unterminated() {
- assertThat(lex("/* foo")).containsExactly("EOF");
- assertThat(lex("\" foo")).containsExactly("EOF");
- }
-
- @Test
public void boolLiteral() {
lexerComparisonTest("0b0101__01010");
assertThat(lex("1 + 0b1000100101"))
diff --git a/javatests/com/google/turbine/parse/ParseErrorTest.java b/javatests/com/google/turbine/parse/ParseErrorTest.java
index 49fb273..6a9ad11 100644
--- a/javatests/com/google/turbine/parse/ParseErrorTest.java
+++ b/javatests/com/google/turbine/parse/ParseErrorTest.java
@@ -228,6 +228,57 @@ public class ParseErrorTest {
}
}
+ @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,; }",
+ " ^"));
+ }
+ }
+
+ @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 {}",
+ " ^"));
+ }
+ }
+
+ @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 {}",
+ "^"));
+ }
+ }
+
private static String lines(String... lines) {
return Joiner.on(System.lineSeparator()).join(lines);
}
diff --git a/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java b/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java
index 2637091..e3f7b63 100644
--- a/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java
+++ b/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java
@@ -16,10 +16,13 @@
package com.google.turbine.parse;
+import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import com.google.turbine.diag.SourceFile;
+import com.google.turbine.diag.TurbineError;
+import com.google.turbine.diag.TurbineError.ErrorKind;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
@@ -57,15 +60,15 @@ public class UnicodeEscapePreprocessorTest {
try {
readAll("\\u00");
fail();
- } catch (AssertionError e) {
- assertThat(e).hasMessage("unexpected end of input");
+ } catch (TurbineError e) {
+ assertThat(getOnlyElement(e.diagnostics()).kind()).isEqualTo(ErrorKind.UNEXPECTED_EOF);
}
try {
readAll("\\u");
fail();
- } catch (AssertionError e) {
- assertThat(e).hasMessage("unexpected end of input");
+ } catch (TurbineError e) {
+ assertThat(getOnlyElement(e.diagnostics()).kind()).isEqualTo(ErrorKind.UNEXPECTED_EOF);
}
}
@@ -74,6 +77,16 @@ public class UnicodeEscapePreprocessorTest {
assertThat(readAll("\\u005C\\\\u005C")).containsExactly('\\', '\\', '\\');
}
+ @Test
+ public void invalidEscape() {
+ try {
+ readAll("\\uUUUU");
+ fail();
+ } catch (TurbineError e) {
+ 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<>();
diff --git a/javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java b/javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java
new file mode 100644
index 0000000..6ea6e72
--- /dev/null
+++ b/javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Types;
+
+/**
+ * A combo test for {@link TurbineTypes} that compares the behaviour of bipredicates like {@link
+ * Types#isSubtype(TypeMirror, TypeMirror)} with javac's implementation.
+ */
+abstract class AbstractTurbineTypesBiPredicateTest extends AbstractTurbineTypesTest {
+
+ final String testDescription;
+ final TypesBiFunctionInput javacInput;
+ final TypesBiFunctionInput turbineInput;
+
+ public AbstractTurbineTypesBiPredicateTest(
+ String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
+ this.testDescription = testDescription;
+ this.javacInput = javacInput;
+ this.turbineInput = turbineInput;
+ }
+
+ protected void test(String symbol, TypeBiPredicate predicate) {
+ assertWithMessage("%s = %s", javacInput.format(symbol), turbineInput.format(symbol))
+ .that(turbineInput.apply(predicate))
+ .isEqualTo(javacInput.apply(predicate));
+ }
+}
diff --git a/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java b/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java
new file mode 100644
index 0000000..e6a59bf
--- /dev/null
+++ b/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+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.stream.Collectors.joining;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.Streams;
+import com.google.turbine.binder.Binder;
+import com.google.turbine.binder.Binder.BindingResult;
+import com.google.turbine.binder.ClassPathBinder;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.parse.Parser;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.tree.Tree.CompUnit;
+import com.sun.source.util.JavacTask;
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.file.JavacFileManager;
+import com.sun.tools.javac.util.Context;
+import java.net.URI;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.ElementScanner8;
+import javax.lang.model.util.SimpleTypeVisitor8;
+import javax.lang.model.util.Types;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.SimpleJavaFileObject;
+
+class AbstractTurbineTypesTest {
+
+ protected static class TypeParameters {
+ protected final Types javacTypes;
+ protected final List<List<TypeMirror>> aGroups;
+ protected final Types turbineTypes;
+ protected final List<List<TypeMirror>> bGroups;
+
+ private TypeParameters(
+ Types javacTypes,
+ List<List<TypeMirror>> aGroups,
+ Types turbineTypes,
+ List<List<TypeMirror>> bGroups) {
+ this.javacTypes = javacTypes;
+ this.aGroups = aGroups;
+ this.turbineTypes = turbineTypes;
+ this.bGroups = bGroups;
+ }
+ }
+
+ protected interface TypeBiPredicate {
+ boolean apply(Types types, TypeMirror a, TypeMirror b);
+ }
+
+ static class TypesBiFunctionInput {
+ final Types types;
+ final TypeMirror lhs;
+ final TypeMirror rhs;
+
+ TypesBiFunctionInput(Types types, TypeMirror lhs, TypeMirror rhs) {
+ this.types = types;
+ this.lhs = lhs;
+ this.rhs = rhs;
+ }
+
+ boolean apply(TypeBiPredicate predicate) {
+ return predicate.apply(types, lhs, rhs);
+ }
+
+ String format(String symbol) {
+ return String.format("`%s` %s `%s`", lhs, symbol, rhs);
+ }
+ }
+
+ protected static Iterable<Object[]> binaryParameters() throws Exception {
+ TypeParameters typeParameters = typeParameters();
+ List<Object[]> params = new ArrayList<>();
+ for (int i = 0; i < typeParameters.aGroups.size(); i++) {
+ List<TypeMirror> ax = typeParameters.aGroups.get(i);
+ List<TypeMirror> bx = typeParameters.bGroups.get(i);
+ Streams.zip(
+ Lists.cartesianProduct(ax, ax).stream(),
+ Lists.cartesianProduct(bx, bx).stream(),
+ (a, b) ->
+ new Object[] {
+ a.get(0) + " " + a.get(1),
+ new TypesBiFunctionInput(typeParameters.javacTypes, a.get(0), a.get(1)),
+ new TypesBiFunctionInput(typeParameters.turbineTypes, b.get(0), b.get(1)),
+ })
+ .forEachOrdered(params::add);
+ }
+ return params;
+ }
+
+ protected static Iterable<Object[]> unaryParameters() throws Exception {
+ TypeParameters typeParameters = typeParameters();
+ List<Object[]> params = new ArrayList<>();
+ for (int i = 0; i < typeParameters.aGroups.size(); i++) {
+ Streams.zip(
+ typeParameters.aGroups.get(i).stream(),
+ typeParameters.bGroups.get(i).stream(),
+ (a, b) ->
+ new Object[] {
+ a.toString(), typeParameters.javacTypes, a, typeParameters.turbineTypes, b,
+ })
+ .forEachOrdered(params::add);
+ }
+ return params;
+ }
+
+ protected static TypeParameters typeParameters() throws Exception {
+ String[][] types = {
+ // generics
+ {
+ "Object",
+ "String",
+ "Cloneable",
+ "Serializable",
+ "List",
+ "Set",
+ "ArrayList",
+ "Collection",
+ "List<Object>",
+ "List<Number>",
+ "List<Integer>",
+ "ArrayList<Object>",
+ "ArrayList<Number>",
+ "ArrayList<Integer>",
+ },
+ // wildcards
+ {
+ "Object",
+ "String",
+ "Cloneable",
+ "Serializable",
+ "List",
+ "List<?>",
+ "List<Object>",
+ "List<Number>",
+ "List<Integer>",
+ "List<? extends Object>",
+ "List<? extends Number>",
+ "List<? extends Integer>",
+ "List<? super Object>",
+ "List<? super Number>",
+ "List<? super Integer>",
+ },
+ // arrays
+ {
+ "Object",
+ "String",
+ "Cloneable",
+ "Serializable",
+ "List",
+ "Object[]",
+ "Number[]",
+ "List<Integer>[]",
+ "List<? extends Integer>[]",
+ "long[]",
+ "int[]",
+ "int[][]",
+ "Long[]",
+ "Integer[]",
+ "Integer[][]",
+ },
+ // primitives
+ {
+ "Object",
+ "String",
+ "Cloneable",
+ "Serializable",
+ "List",
+ "int",
+ "char",
+ "byte",
+ "short",
+ "boolean",
+ "long",
+ "float",
+ "double",
+ "Integer",
+ "Character",
+ "Byte",
+ "Short",
+ "Boolean",
+ "Long",
+ "Float",
+ "Double",
+ },
+ };
+ List<String> files = new ArrayList<>();
+ AtomicInteger idx = new AtomicInteger();
+ for (String[] group : types) {
+ StringBuilder sb = new StringBuilder();
+ Joiner.on('\n')
+ .appendTo(
+ sb,
+ "package p;",
+ "import java.util.*;",
+ "import java.io.*;",
+ String.format("abstract class Test%s {", idx.getAndIncrement()),
+ Streams.mapWithIndex(
+ Arrays.stream(group), (x, i) -> String.format(" %s f%d;\n", x, i))
+ .collect(joining("\n")),
+ " abstract <T extends Serializable & List<T>> T f();",
+ " abstract <V extends List<V>> V g();",
+ " abstract <W extends ArrayList> W h();",
+ " abstract <X extends Serializable> X i();",
+ "}");
+ String content = sb.toString();
+ files.add(content);
+ }
+ // type hierarchies
+ files.add(
+ Joiner.on('\n')
+ .join(
+ "import java.util.*;",
+ "class Hierarchy {", //
+ " static class A<T> {",
+ " class I {}",
+ " }",
+ " static class D<T> extends A<T[]> {}",
+ " static class E<T> extends A<T> {",
+ " class J extends I {}",
+ " }",
+ " static class F<T> extends A {}",
+ " static class G<T> extends A<List<T>> {}",
+ " A rawA;",
+ " A<Object[]> a1;",
+ " A<Number[]> a2;",
+ " A<Integer[]> a3;",
+ " A<? super Object> a4;",
+ " A<? super Number> a5;",
+ " A<? super Integer> a6;",
+ " A<? extends Object> a7;",
+ " A<? extends Number> a8;",
+ " A<? extends Integer> a9;",
+ " A<List<Integer>> a10;",
+ " D<Object> d1;",
+ " D<Number> d2;",
+ " D<Integer> d3;",
+ " A<Object>.I i1;",
+ " A<Number>.I i2;",
+ " A<Integer>.I i3;",
+ " E<Object>.J j1;",
+ " E<Number>.J j2;",
+ " E<Integer>.J j3;",
+ " F<Integer> f1;",
+ " F<Number> f2;",
+ " F<Object> f3;",
+ " G<Integer> g1;",
+ " G<Number> g2;",
+ "}"));
+ // methods
+ files.add(
+ Joiner.on('\n')
+ .join(
+ "import java.io.*;",
+ "class Methods {",
+ " void f() {}",
+ " void g() {}",
+ " void f(int x) {}",
+ " void f(int x, int y) {}",
+ " abstract static class I {",
+ " abstract int f();",
+ " abstract void g() throws IOException;",
+ " abstract <T> void h();",
+ " abstract <T extends String> T i(T s);",
+ " }",
+ " abstract static class J {",
+ " abstract long f();",
+ " abstract void g();",
+ " abstract <T> void h();",
+ " abstract <T extends Number> T i(T s);",
+ " }",
+ " class K {",
+ " void f(K this, int x) {}",
+ " void g(K this) {}",
+ " <T extends Enum<T> & Serializable> void h(T t) {}",
+ " }",
+ " class L {",
+ " void f(int x) {}",
+ " void g() {}",
+ " <E extends Enum<E> & Serializable> void h(E t) {}",
+ " }",
+ "}",
+ "class M<T extends Enum<T> & Serializable> {",
+ " void h(T t) {}",
+ "}"));
+
+ Context context = new Context();
+ JavaFileManager fileManager = new JavacFileManager(context, true, UTF_8);
+ idx.set(0);
+ ImmutableList<SimpleJavaFileObject> compilationUnits =
+ files.stream()
+ .map(
+ x ->
+ new SimpleJavaFileObject(
+ URI.create("file://test" + idx.getAndIncrement() + ".java"), Kind.SOURCE) {
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return x;
+ }
+ })
+ .collect(toImmutableList());
+ JavacTask task =
+ JavacTool.create()
+ .getTask(
+ /* out= */ null,
+ fileManager,
+ /* diagnosticListener= */ null,
+ /* options= */ ImmutableList.of(),
+ /* classes= */ ImmutableList.of(),
+ compilationUnits);
+
+ Types javacTypes = task.getTypes();
+ ImmutableMap<String, Element> javacElements =
+ Streams.stream(task.analyze())
+ .collect(toImmutableMap(e -> e.getSimpleName().toString(), x -> x));
+
+ ImmutableList<CompUnit> units = files.stream().map(Parser::parse).collect(toImmutableList());
+ BindingResult bound =
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+ Env<ClassSymbol, TypeBoundClass> env =
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(bound.classPathEnv())
+ .append(new SimpleEnv<>(bound.units()));
+ ModelFactory factory = new ModelFactory(env, ClassLoader.getSystemClassLoader(), bound.tli());
+ Types turbineTypes = new TurbineTypes(factory);
+ ImmutableMap<String, Element> turbineElements =
+ bound.units().keySet().stream()
+ .filter(x -> !x.binaryName().contains("$")) // only top level classes
+ .collect(toImmutableMap(x -> x.simpleName(), factory::element));
+
+ assertThat(javacElements.keySet()).containsExactlyElementsIn(turbineElements.keySet());
+
+ List<List<TypeMirror>> aGroups = new ArrayList<>();
+ List<List<TypeMirror>> bGroups = new ArrayList<>();
+ for (String name : javacElements.keySet()) {
+
+ List<TypeMirror> aGroup = new ArrayList<>();
+ List<TypeMirror> bGroup = new ArrayList<>();
+
+ ListMultimap<String, TypeMirror> javacInputs =
+ MultimapBuilder.linkedHashKeys().arrayListValues().build();
+ javacElements
+ .get(name)
+ .getEnclosedElements()
+ .forEach(e -> getTypes(javacTypes, e, javacInputs));
+
+ ListMultimap<String, TypeMirror> turbineInputs =
+ MultimapBuilder.linkedHashKeys().arrayListValues().build();
+ turbineElements
+ .get(name)
+ .getEnclosedElements()
+ .forEach(e -> getTypes(turbineTypes, e, turbineInputs));
+
+ assertThat(turbineInputs.keySet()).containsExactlyElementsIn(javacInputs.keySet());
+
+ for (String key : javacInputs.keySet()) {
+ List<TypeMirror> a = javacInputs.get(key);
+ List<TypeMirror> b = turbineInputs.get(key);
+ assertWithMessage(key)
+ .that(b.stream().map(x -> x.getKind() + " " + x).collect(toImmutableList()))
+ .containsExactlyElementsIn(
+ a.stream().map(x -> x.getKind() + " " + x).collect(toImmutableList()))
+ .inOrder();
+ aGroup.addAll(a);
+ bGroup.addAll(b);
+ }
+ aGroups.add(aGroup);
+ bGroups.add(bGroup);
+ }
+ return new TypeParameters(javacTypes, aGroups, turbineTypes, bGroups);
+ }
+
+ /**
+ * Discover all types contained in the given element, keyed by their immediate enclosing element.
+ */
+ private static void getTypes(
+ Types typeUtils, Element element, Multimap<String, TypeMirror> types) {
+ element.accept(
+ new ElementScanner8<Void, Void>() {
+
+ /**
+ * Returns an element name qualified by all enclosing elements, to allow comparison
+ * between javac and turbine's implementation to group related types.
+ */
+ String key(Element e) {
+ Deque<String> flat = new ArrayDeque<>();
+ while (e != null) {
+ flat.addFirst(e.getSimpleName().toString());
+ if (e.getKind() == ElementKind.PACKAGE) {
+ break;
+ }
+ e = e.getEnclosingElement();
+ }
+ return Joiner.on('.').join(flat);
+ }
+
+ void addType(Element e, TypeMirror t) {
+ if (t != null) {
+ types.put(key(e), t);
+ t.accept(
+ new SimpleTypeVisitor8<Void, Void>() {
+ @Override
+ public Void visitDeclared(DeclaredType t, Void aVoid) {
+ for (TypeMirror a : t.getTypeArguments()) {
+ a.accept(this, null);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitWildcard(WildcardType t, Void aVoid) {
+ types.put(key(e), t);
+ return null;
+ }
+
+ @Override
+ public Void visitTypeVariable(TypeVariable t, Void aVoid) {
+ if (t.getUpperBound() != null) {
+ types.put(key(e), t.getUpperBound());
+ }
+ return null;
+ }
+ },
+ null);
+ }
+ }
+
+ void addType(Element e, List<? extends TypeMirror> types) {
+ for (TypeMirror type : types) {
+ addType(e, type);
+ }
+ }
+
+ @Override
+ public Void visitVariable(VariableElement e, Void unused) {
+ if (e.getSimpleName().toString().contains("this$")) {
+ // enclosing instance parameters
+ return null;
+ }
+ addType(e, e.asType());
+ return super.visitVariable(e, null);
+ }
+
+ @Override
+ public Void visitType(TypeElement e, Void unused) {
+ addType(e, e.asType());
+ return super.visitType(e, null);
+ }
+
+ @Override
+ public Void visitExecutable(ExecutableElement e, Void unused) {
+ scan(e.getTypeParameters(), null);
+ scan(e.getParameters(), null);
+ addType(e, e.asType());
+ addType(e, e.getReturnType());
+ TypeMirror receiverType = e.getReceiverType();
+ if (receiverType == null) {
+ // work around a javac bug in JDK < 14, see:
+ // https://bugs.openjdk.java.net/browse/JDK-8222369
+ receiverType = typeUtils.getNoType(TypeKind.NONE);
+ }
+ addType(e, receiverType);
+ addType(e, e.getThrownTypes());
+ return null;
+ }
+
+ @Override
+ public Void visitTypeParameter(TypeParameterElement e, Void unused) {
+ addType(e, e.asType());
+ addType(e, e.getBounds());
+ return null;
+ }
+ },
+ null);
+ }
+}
diff --git a/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java b/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java
new file mode 100644
index 0000000..ed5af6a
--- /dev/null
+++ b/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2019 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.processing;
+
+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 java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.stream.Collectors.joining;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.Binder;
+import com.google.turbine.binder.Binder.BindingResult;
+import com.google.turbine.binder.ClassPathBinder;
+import com.google.turbine.binder.Processing;
+import com.google.turbine.binder.Processing.ProcessorInfo;
+import com.google.turbine.diag.SourceFile;
+import com.google.turbine.diag.TurbineError;
+import com.google.turbine.lower.IntegrationTestSupport;
+import com.google.turbine.parse.Parser;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.tree.Tree;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardLocation;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ProcessingIntegrationTest {
+
+ @SupportedAnnotationTypes("*")
+ public static class CrashingProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ throw new RuntimeException("crash!");
+ }
+ }
+
+ 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!");
+ }
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class WarningProcessor 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) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "proc warning");
+ try {
+ JavaFileObject file = processingEnv.getFiler().createSourceFile("Gen.java");
+ try (Writer writer = file.openWriter()) {
+ writer.write("class Gen {}");
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ first = false;
+ }
+ if (roundEnv.processingOver()) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "proc error");
+ }
+ return false;
+ }
+ }
+
+ @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");
+ }
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class ResourceProcessor 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 {
+ try (Writer writer = processingEnv.getFiler().createSourceFile("Gen").openWriter()) {
+ writer.write("class Gen {}");
+ }
+ try (Writer writer =
+ processingEnv
+ .getFiler()
+ .createResource(StandardLocation.SOURCE_OUTPUT, "", "source.txt")
+ .openWriter()) {
+ writer.write("hello source output");
+ }
+ try (Writer writer =
+ processingEnv
+ .getFiler()
+ .createResource(StandardLocation.CLASS_OUTPUT, "", "class.txt")
+ .openWriter()) {
+ writer.write("hello class output");
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ first = false;
+ }
+ return false;
+ }
+ }
+
+ @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());
+ BindingResult bound =
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ ProcessorInfo.create(
+ ImmutableList.of(new ResourceProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+
+ assertThat(bound.generatedSources().keySet()).containsExactly("Gen.java", "source.txt");
+ assertThat(bound.generatedClasses().keySet()).containsExactly("class.txt");
+
+ assertThat(bound.generatedSources().get("source.txt").source())
+ .isEqualTo("hello source output");
+ assertThat(new String(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());
+ BindingResult bound =
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ ProcessorInfo.create(
+ ImmutableList.of(new ElementsAnnotatedWithProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+
+ assertThat(
+ Splitter.on(System.lineSeparator())
+ .omitEmptyStrings()
+ .split(
+ new String(
+ bound.generatedClasses().entrySet().stream()
+ .filter(s -> s.getKey().equals("output.txt"))
+ .collect(onlyElement())
+ .getValue(),
+ UTF_8)))
+ .containsExactly("A: One, Two", "B: One");
+ }
+
+ @SupportedAnnotationTypes("*")
+ private static class ElementsAnnotatedWithProcessor 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 (PrintWriter writer =
+ new PrintWriter(
+ processingEnv
+ .getFiler()
+ .createResource(StandardLocation.CLASS_OUTPUT, "", "output.txt")
+ .openWriter(),
+ /* autoFlush= */ true)) {
+ printAnnotatedElements(roundEnv, writer, "A");
+ printAnnotatedElements(roundEnv, writer, "B");
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ first = false;
+ }
+ return false;
+ }
+
+ private void printAnnotatedElements(
+ RoundEnvironment roundEnv, PrintWriter writer, String annotation) {
+ writer.println(
+ annotation
+ + ": "
+ + roundEnv
+ .getElementsAnnotatedWith(
+ processingEnv.getElementUtils().getTypeElement(annotation))
+ .stream()
+ .map(e -> e.getSimpleName().toString())
+ .collect(joining(", ")));
+ }
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineAnnotationMirrorTest.java b/javatests/com/google/turbine/processing/TurbineAnnotationMirrorTest.java
new file mode 100644
index 0000000..a049860
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineAnnotationMirrorTest.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.Multimaps;
+import com.google.turbine.binder.Binder;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.lower.IntegrationTestSupport;
+import com.google.turbine.lower.IntegrationTestSupport.TestInput;
+import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
+import com.google.turbine.testing.TestClassPaths;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.AbstractAnnotationValueVisitor8;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineAnnotationMirrorTest {
+
+ private AnnotationMirror getAnnotation(
+ List<? extends AnnotationMirror> annotationMirrors, String name) {
+ return annotationMirrors.stream()
+ .filter(x -> x.getAnnotationType().asElement().getSimpleName().contentEquals(name))
+ .findFirst()
+ .get();
+ }
+
+ private ImmutableMap<String, Object> values(AnnotationMirror a) {
+ return values(a.getElementValues());
+ }
+
+ /**
+ * Returns a map from the name of annotation elements to their values, see also {@link
+ * #getValue(AnnotationValue)}.
+ */
+ private ImmutableMap<String, Object> values(
+ Map<? extends ExecutableElement, ? extends AnnotationValue> values) {
+ return values.entrySet().stream()
+ .collect(
+ toImmutableMap(
+ e -> e.getKey().getSimpleName().toString(), e -> getValue(e.getValue())));
+ }
+
+ /**
+ * Returns the given annotation value as an Object (for primitives), or a list (for arrays), or
+ * strings (for compound annotations, enums, and class literals).
+ */
+ static Object getValue(AnnotationValue value) {
+ return value.accept(
+ new AbstractAnnotationValueVisitor8<Object, Void>() {
+ @Override
+ public Object visitBoolean(boolean b, Void unused) {
+ return b;
+ }
+
+ @Override
+ public Object visitByte(byte b, Void unused) {
+ return b;
+ }
+
+ @Override
+ public Object visitChar(char c, Void unused) {
+ return c;
+ }
+
+ @Override
+ public Object visitDouble(double d, Void unused) {
+ return d;
+ }
+
+ @Override
+ public Object visitFloat(float f, Void unused) {
+ return f;
+ }
+
+ @Override
+ public Object visitInt(int i, Void unused) {
+ return i;
+ }
+
+ @Override
+ public Object visitLong(long i, Void unused) {
+ return i;
+ }
+
+ @Override
+ public Object visitShort(short s, Void unused) {
+ return s;
+ }
+
+ @Override
+ public Object visitString(String s, Void unused) {
+ return s;
+ }
+
+ @Override
+ public Object visitType(TypeMirror t, Void unused) {
+ return value.toString();
+ }
+
+ @Override
+ public Object visitEnumConstant(VariableElement c, Void unused) {
+ return value.toString();
+ }
+
+ @Override
+ public Object visitAnnotation(AnnotationMirror a, Void unused) {
+ return value.toString();
+ }
+
+ @Override
+ public Object visitArray(List<? extends AnnotationValue> vals, Void unused) {
+ return vals.stream().map(v -> v.accept(this, null)).collect(toImmutableList());
+ }
+ },
+ null);
+ }
+
+ private static Stream<String> typeAnnotationNames(Element e) {
+ return e.asType().getAnnotationMirrors().stream()
+ .map(anno -> anno.getAnnotationType().asElement().getSimpleName().toString());
+ }
+
+ @Test
+ public void test() throws Exception {
+ TestInput input =
+ TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== Test.java ===",
+ "import java.lang.annotation.ElementType;",
+ "import java.lang.annotation.Retention;",
+ "import java.lang.annotation.RetentionPolicy;",
+ "import java.lang.annotation.Target;",
+ "import java.util.Map;",
+ "import java.util.Map.Entry;",
+ "@Retention(RetentionPolicy.RUNTIME)",
+ "@interface A {",
+ " int x() default 0;",
+ " int y() default 1;",
+ " int[] z() default {};",
+ "}",
+ "@interface B {",
+ " Class<?> c() default String.class;",
+ " ElementType e() default ElementType.TYPE_USE;",
+ " A f() default @A;",
+ "}",
+ "@Retention(RetentionPolicy.RUNTIME)",
+ "@Target(ElementType.TYPE_USE)",
+ "@interface T {}",
+ "@Target(ElementType.TYPE_USE)",
+ "@interface V {}",
+ "",
+ "@A(y = 42, z = {43})",
+ "@B",
+ "class Test {",
+ " class I {}",
+ " @T Test. @V I f;",
+ " Map. @T Entry g;",
+ " @T Entry h;",
+ "}",
+ ""));
+
+ Binder.BindingResult bound =
+ IntegrationTestSupport.turbineAnalysis(
+ input.sources,
+ ImmutableList.of(),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+
+ Env<ClassSymbol, TypeBoundClass> env =
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(bound.classPathEnv())
+ .append(new SimpleEnv<>(bound.units()));
+ ModelFactory factory = new ModelFactory(env, ClassLoader.getSystemClassLoader(), bound.tli());
+ TurbineTypes turbineTypes = new TurbineTypes(factory);
+ TurbineElements turbineElements = new TurbineElements(factory, turbineTypes);
+
+ TurbineTypeElement te = factory.typeElement(new ClassSymbol("Test"));
+
+ AnnotationMirror a = getAnnotation(te.getAnnotationMirrors(), "A");
+ ((TypeElement) a.getAnnotationType().asElement()).getQualifiedName().contentEquals("A");
+ assertThat(values(a)).containsExactly("y", 42, "z", ImmutableList.of(43));
+ assertThat(values(turbineElements.getElementValuesWithDefaults(a)))
+ .containsExactly(
+ "x", 0,
+ "y", 42,
+ "z", ImmutableList.of(43));
+
+ AnnotationMirror b = getAnnotation(te.getAnnotationMirrors(), "B");
+ assertThat(values(turbineElements.getElementValuesWithDefaults(b)))
+ .containsExactly(
+ "c", "java.lang.String.class",
+ "e", "java.lang.annotation.ElementType.TYPE_USE",
+ "f", "@A");
+
+ ListMultimap<String, String> fieldTypeAnnotations =
+ te.getEnclosedElements().stream()
+ .filter(e -> e.getKind().equals(ElementKind.FIELD))
+ .collect(
+ Multimaps.flatteningToMultimap(
+ e -> e.getSimpleName().toString(),
+ e -> typeAnnotationNames(e),
+ MultimapBuilder.linkedHashKeys().arrayListValues()::build));
+ assertThat(fieldTypeAnnotations)
+ .containsExactly(
+ "f", "V",
+ "g", "T",
+ "h", "T");
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineAnnotationProxyTest.java b/javatests/com/google/turbine/processing/TurbineAnnotationProxyTest.java
new file mode 100644
index 0000000..d339700
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineAnnotationProxyTest.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+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;
+import com.google.turbine.binder.ClassPathBinder;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.diag.SourceFile;
+import com.google.turbine.lower.IntegrationTestSupport.TestInput;
+import com.google.turbine.parse.Parser;
+import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
+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;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.MirroredTypeException;
+import javax.lang.model.type.MirroredTypesException;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineAnnotationProxyTest {
+
+ @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface A {
+ B b() default @B(-1);
+
+ ElementType e() default ElementType.PACKAGE;
+
+ int[] xs() default {};
+
+ Class<?> c() default String.class;
+
+ Class<?>[] cx() default {};
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Inherited
+ public @interface B {
+ int value();
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface C {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface RS {
+ R[] value() default {};
+ }
+
+ @Repeatable(RS.class)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface R {
+ int value() default 1;
+ }
+
+ @A
+ static class I {}
+
+ @Test
+ public void test() throws IOException {
+
+ Path lib = temporaryFolder.newFile("lib.jar").toPath();
+ try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(lib))) {
+ addClass(jos, TurbineAnnotationProxyTest.class);
+ addClass(jos, A.class);
+ addClass(jos, B.class);
+ addClass(jos, C.class);
+ addClass(jos, R.class);
+ }
+
+ TestInput input =
+ TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== Super.java ===",
+ "import " + B.class.getCanonicalName() + ";",
+ "import " + C.class.getCanonicalName() + ";",
+ "@B(42)",
+ "@C",
+ "class Super {}",
+ "=== Test.java ===",
+ "import " + A.class.getCanonicalName() + ";",
+ "import " + R.class.getCanonicalName() + ";",
+ "@A(xs = {1,2,3}, cx = {Integer.class, Long.class})",
+ "@R(1)",
+ "@R(2)",
+ "@R(3)",
+ "class Test extends Super {}",
+ ""));
+
+ ImmutableList<CompUnit> units =
+ input.sources.entrySet().stream()
+ .map(e -> new SourceFile(e.getKey(), e.getValue()))
+ .map(Parser::parse)
+ .collect(toImmutableList());
+
+ Binder.BindingResult bound =
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of(lib)),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+
+ Env<ClassSymbol, TypeBoundClass> env =
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(bound.classPathEnv())
+ .append(new SimpleEnv<>(bound.units()));
+ ModelFactory factory = new ModelFactory(env, ClassLoader.getSystemClassLoader(), bound.tli());
+ TurbineTypeElement te = factory.typeElement(new ClassSymbol("Test"));
+
+ A a = te.getAnnotation(A.class);
+ B b = te.getAnnotation(B.class);
+ assertThat(te.getAnnotation(C.class)).isNull();
+
+ assertThat(a.b().value()).isEqualTo(-1);
+ assertThat(a.e()).isEqualTo(ElementType.PACKAGE);
+ try {
+ a.c();
+ fail();
+ } catch (MirroredTypeException e) {
+ assertThat(e.getTypeMirror().getKind()).isEqualTo(TypeKind.DECLARED);
+ assertThat(getQualifiedName(e.getTypeMirror())).contains("java.lang.String");
+ }
+ try {
+ a.cx();
+ fail();
+ } catch (MirroredTypesException e) {
+ assertThat(
+ e.getTypeMirrors().stream().map(m -> getQualifiedName(m)).collect(toImmutableList()))
+ .containsExactly("java.lang.Integer", "java.lang.Long");
+ }
+ assertThat(Ints.asList(a.xs())).containsExactly(1, 2, 3).inOrder();
+ assertThat(a.annotationType()).isEqualTo(A.class);
+
+ assertThat(b.value()).isEqualTo(42);
+
+ RS container = te.getAnnotation(RS.class);
+ assertThat(container.value()).hasLength(3);
+ R[] rs = te.getAnnotationsByType(R.class);
+ assertThat(rs).hasLength(3);
+ assertThat(Arrays.toString(rs))
+ .isEqualTo(
+ String.format(
+ "[@%s(1), @%s(2), @%s(3)]",
+ R.class.getCanonicalName(),
+ R.class.getCanonicalName(),
+ R.class.getCanonicalName()));
+
+ new EqualsTester()
+ .addEqualityGroup(a, te.getAnnotation(A.class))
+ .addEqualityGroup(b, te.getAnnotation(B.class))
+ .addEqualityGroup(rs[0])
+ .addEqualityGroup(rs[1])
+ .addEqualityGroup(rs[2])
+ .addEqualityGroup(container)
+ .addEqualityGroup(I.class.getAnnotation(A.class))
+ .addEqualityGroup("unrelated")
+ .testEquals();
+ }
+
+ 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);
+ }
+ }
+
+ private static String getQualifiedName(TypeMirror typeMirror) {
+ return ((TypeElement) ((DeclaredType) typeMirror).asElement()).getQualifiedName().toString();
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineElementTest.java b/javatests/com/google/turbine/processing/TurbineElementTest.java
new file mode 100644
index 0000000..0b3448f
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineElementTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2019 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.processing;
+
+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 com.google.common.collect.MoreCollectors;
+import com.google.common.testing.EqualsTester;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.binder.sym.PackageSymbol;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.type.Type.ClassTy;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineElementTest {
+
+ private final ModelFactory factory =
+ new ModelFactory(
+ TestClassPaths.TURBINE_BOOTCLASSPATH.env(),
+ ClassLoader.getSystemClassLoader(),
+ TestClassPaths.TURBINE_BOOTCLASSPATH.index());
+
+ @Test
+ public void typeElement() {
+ TypeElement e = factory.typeElement(new ClassSymbol("java/util/Map$Entry"));
+ TypeElement m = (TypeElement) e.getEnclosingElement();
+ TypeMirror t = e.asType();
+
+ assertThat(e.getSimpleName().toString()).isEqualTo("Entry");
+ assertThat(e.getQualifiedName().toString()).isEqualTo("java.util.Map.Entry");
+ assertThat(e.toString()).isEqualTo("java.util.Map.Entry");
+ assertThat(e.asType().toString()).isEqualTo("java.util.Map.Entry<K,V>");
+ assertThat(e.getKind()).isEqualTo(ElementKind.INTERFACE);
+ assertThat(e.getNestingKind()).isEqualTo(NestingKind.MEMBER);
+ assertThat(e.getModifiers())
+ .containsExactly(Modifier.PUBLIC, Modifier.ABSTRACT, Modifier.STATIC);
+
+ assertThat(m.getSimpleName().toString()).isEqualTo("Map");
+ assertThat(m.getSuperclass().getKind()).isEqualTo(TypeKind.NONE);
+ assertThat(m.getQualifiedName().toString()).isEqualTo("java.util.Map");
+ assertThat(m.toString()).isEqualTo("java.util.Map");
+ assertThat(m.asType().toString()).isEqualTo("java.util.Map<K,V>");
+ assertThat(m.getNestingKind()).isEqualTo(NestingKind.TOP_LEVEL);
+ assertThat(m.getSuperclass().getKind()).isEqualTo(TypeKind.NONE);
+ assertThat(m.getEnclosingElement().getKind()).isEqualTo(ElementKind.PACKAGE);
+
+ assertThat(t.getKind()).isEqualTo(TypeKind.DECLARED);
+ }
+
+ @Test
+ public void superClass() {
+ TypeElement e = factory.typeElement(new ClassSymbol("java/util/HashMap"));
+ assertThat(
+ ((TypeElement) ((DeclaredType) e.getSuperclass()).asElement())
+ .getQualifiedName()
+ .toString())
+ .isEqualTo("java.util.AbstractMap");
+
+ e = factory.typeElement(new ClassSymbol("java/lang/annotation/ElementType"));
+ assertThat(
+ ((TypeElement) ((DeclaredType) e.getSuperclass()).asElement())
+ .getQualifiedName()
+ .toString())
+ .isEqualTo("java.lang.Enum");
+ }
+
+ @Test
+ public void interfaces() {
+ TypeElement e = factory.typeElement(new ClassSymbol("java/util/HashMap"));
+ assertThat(
+ e.getInterfaces().stream()
+ .map(
+ i ->
+ ((TypeElement) ((DeclaredType) i).asElement())
+ .getQualifiedName()
+ .toString())
+ .collect(toImmutableList()))
+ .contains("java.util.Map");
+ }
+
+ @Test
+ public void typeParameters() {
+ TypeElement e = factory.typeElement(new ClassSymbol("java/util/HashMap"));
+ assertThat(e.getTypeParameters().stream().map(Object::toString).collect(toImmutableList()))
+ .containsExactly("K", "V");
+ for (TypeParameterElement t : e.getTypeParameters()) {
+ assertThat(t.getGenericElement()).isEqualTo(e);
+ assertThat(t.getEnclosingElement()).isEqualTo(e);
+ assertThat(t.getBounds()).containsExactly(factory.asTypeMirror(ClassTy.OBJECT));
+ }
+ }
+
+ @Test
+ public void enclosed() {
+ assertThat(
+ factory.typeElement(new ClassSymbol("java/lang/Integer")).getEnclosedElements().stream()
+ .map(e -> e.getKind() + " " + e)
+ .collect(toImmutableList()))
+ .containsAtLeast("METHOD parseInt(java.lang.String)", "FIELD MAX_VALUE");
+ }
+
+ @Test
+ public void equals() {
+ new EqualsTester()
+ .addEqualityGroup(
+ factory.typeElement(new ClassSymbol("java/util/List")),
+ factory.typeElement(new ClassSymbol("java/util/List")))
+ .addEqualityGroup(factory.typeElement(new ClassSymbol("java/util/ArrayList")))
+ .addEqualityGroup(
+ factory.typeElement(new ClassSymbol("java/util/Map")).getTypeParameters().get(0),
+ factory.typeElement(new ClassSymbol("java/util/Map")).getTypeParameters().get(0))
+ .addEqualityGroup(
+ factory.typeElement(new ClassSymbol("java/util/ArrayList")).getTypeParameters().get(0))
+ .addEqualityGroup(
+ factory.fieldElement(
+ new FieldSymbol(new ClassSymbol("java/util/ArrayList"), "elementData")),
+ factory.fieldElement(
+ new FieldSymbol(new ClassSymbol("java/util/ArrayList"), "elementData")))
+ .addEqualityGroup(
+ factory.fieldElement(
+ new FieldSymbol(new ClassSymbol("java/util/ArrayList"), "serialVersionUID")))
+ .addEqualityGroup(
+ ((ExecutableElement)
+ factory
+ .typeElement(new ClassSymbol("java/util/ArrayList"))
+ .getEnclosedElements()
+ .stream()
+ .filter(
+ e ->
+ e.getKind().equals(ElementKind.METHOD)
+ && e.getSimpleName().contentEquals("add"))
+ .skip(1)
+ .findFirst()
+ .get())
+ .getParameters()
+ .get(0))
+ .addEqualityGroup(
+ factory
+ .typeElement(new ClassSymbol("java/util/ArrayList"))
+ .getEnclosedElements()
+ .stream()
+ .filter(e -> e.getKind().equals(ElementKind.METHOD))
+ .skip(1)
+ .findFirst()
+ .get())
+ .addEqualityGroup(
+ factory
+ .typeElement(new ClassSymbol("java/util/ArrayList"))
+ .getEnclosedElements()
+ .stream()
+ .filter(e -> e.getKind().equals(ElementKind.METHOD))
+ .findFirst()
+ .get(),
+ factory
+ .typeElement(new ClassSymbol("java/util/ArrayList"))
+ .getEnclosedElements()
+ .stream()
+ .filter(e -> e.getKind().equals(ElementKind.METHOD))
+ .findFirst()
+ .get())
+ .addEqualityGroup(
+ factory.packageElement(new PackageSymbol("java/util")),
+ factory.typeElement(new ClassSymbol("java/util/ArrayList")).getEnclosingElement())
+ .addEqualityGroup(factory.packageElement(new PackageSymbol("java/lang")))
+ .testEquals();
+ }
+
+ @Test
+ public void noElement() {
+ PackageElement p = factory.packageElement(new PackageSymbol("java/lang"));
+ assertThat(p.getEnclosingElement()).isNull();
+ }
+
+ @Test
+ public void objectSuper() {
+ assertThat(factory.typeElement(new ClassSymbol("java/lang/Object")).getSuperclass().getKind())
+ .isEqualTo(TypeKind.NONE);
+ }
+
+ @Test
+ public void typeKind() {
+ assertThat(factory.typeElement(new ClassSymbol("java/lang/annotation/Target")).getKind())
+ .isEqualTo(ElementKind.ANNOTATION_TYPE);
+ assertThat(factory.typeElement(new ClassSymbol("java/lang/annotation/ElementType")).getKind())
+ .isEqualTo(ElementKind.ENUM);
+ }
+
+ @Test
+ public void parameter() {
+ ExecutableElement equals =
+ (ExecutableElement)
+ factory.typeElement(new ClassSymbol("java/lang/Object")).getEnclosedElements().stream()
+ .filter(e -> e.getSimpleName().contentEquals("equals"))
+ .collect(MoreCollectors.onlyElement());
+ VariableElement parameter = getOnlyElement(equals.getParameters());
+ assertThat(parameter.getKind()).isEqualTo(ElementKind.PARAMETER);
+ assertThat(parameter.asType().toString()).isEqualTo("java.lang.Object");
+ assertThat(parameter.getModifiers()).isEmpty();
+ assertThat(parameter.getEnclosedElements()).isEmpty();
+ assertThat(parameter.getSimpleName().toString()).isNotEmpty();
+ assertThat(parameter.getConstantValue()).isNull();
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java b/javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java
new file mode 100644
index 0000000..11dedbf
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2019 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.processing;
+
+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.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.turbine.binder.Binder;
+import com.google.turbine.binder.ClassPathBinder;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.diag.SourceFile;
+import com.google.turbine.lower.IntegrationTestSupport;
+import com.google.turbine.lower.IntegrationTestSupport.TestInput;
+import com.google.turbine.parse.Parser;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.tree.Tree.CompUnit;
+import com.sun.source.util.JavacTask;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.util.Elements;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineElementsGetAllMembersTest {
+
+ @Parameters
+ public static Iterable<Object[]> parameters() {
+ // An array of test inputs. Each element is an array of lines of sources to compile.
+ String[][] inputs = {
+ {
+ "=== Test.java ===", //
+ "class Test {",
+ "}",
+ },
+ {
+ "=== A.java ===",
+ "interface A {",
+ " Integer f();",
+ "}",
+ "=== B.java ===",
+ "interface B {",
+ " Integer f();",
+ "}",
+ "=== Test.java ===", //
+ "class Test implements A, B {",
+ " Integer f() {",
+ " return 42;",
+ " }",
+ "}",
+ },
+ {
+ "=== I.java ===",
+ "abstract class I {",
+ " abstract Integer f();",
+ "}",
+ "=== J.java ===",
+ "interface J extends I {",
+ " default Integer f() {",
+ " return 42;",
+ " }",
+ "}",
+ "=== Test.java ===", //
+ "class Test extends I implements J {",
+ "}",
+ },
+ {
+ "=== I.java ===",
+ "interface I {",
+ " Integer f();",
+ "}",
+ "=== J.java ===",
+ "interface J extends I {",
+ " default Integer f() {",
+ " return 42;",
+ " }",
+ "}",
+ "=== Test.java ===", //
+ "class Test implements J, I {",
+ "}",
+ },
+ {
+ "=== p/A.java ===",
+ "package p;",
+ "public class A {",
+ " public boolean f() {",
+ " return true;",
+ " }",
+ "}",
+ "=== p/B.java ===",
+ "package p;",
+ "public interface B {",
+ " public boolean f();",
+ "}",
+ "=== Test.java ===", //
+ "import p.*;",
+ "class Test extends A implements B {",
+ "}",
+ },
+ {
+ "=== p/A.java ===",
+ "package p;",
+ "public class A {",
+ " public boolean f() {",
+ " return true;",
+ " }",
+ "}",
+ "=== p/B.java ===",
+ "package p;",
+ "public interface B {",
+ " public boolean f();",
+ "}",
+ "=== Middle.java ===", //
+ "import p.*;",
+ "public abstract class Middle extends A implements B {",
+ "}",
+ "=== Test.java ===", //
+ "class Test extends Middle {",
+ "}",
+ },
+ {
+ "=== A.java ===",
+ "interface A {",
+ " Integer f();",
+ "}",
+ "=== B.java ===",
+ "interface B {",
+ " Number f();",
+ "}",
+ "=== Test.java ===", //
+ "abstract class Test implements A, B {",
+ "}",
+ },
+ {
+ "=== A.java ===",
+ "interface A {",
+ " Integer f();",
+ "}",
+ "=== B.java ===",
+ "interface B {",
+ " Integer f();",
+ "}",
+ "=== Test.java ===", //
+ "abstract class Test implements A, B {",
+ "}",
+ },
+ {
+ "=== I.java ===",
+ "interface I {",
+ " int x;",
+ "}",
+ "=== J.java ===",
+ "interface J {",
+ " int x;",
+ "}",
+ "=== B.java ===",
+ "class B {",
+ " int x;",
+ "}",
+ "=== C.java ===",
+ "class C extends B {",
+ " static int x;",
+ "}",
+ "=== Test.java ===",
+ "class Test extends C implements I, J {",
+ " int x;",
+ "}",
+ },
+ {
+ "=== one/A.java ===",
+ "public class A {",
+ " int a;",
+ "}",
+ "=== two/B.java ===",
+ "public class B extends A {",
+ " int b;",
+ " private int c;",
+ " protected int d;",
+ "}",
+ "=== Test.java ===",
+ "public class Test extends B {",
+ " int x;",
+ "}",
+ },
+ {
+ "=== A.java ===",
+ "interface A {",
+ " class I {}",
+ "}",
+ "=== B.java ===",
+ "interface B {",
+ " class J {}",
+ "}",
+ "=== Test.java ===", //
+ "abstract class Test implements A, B {",
+ "}",
+ },
+ {
+ "=== A.java ===",
+ "import java.util.List;",
+ "interface A<T> {",
+ " List<? extends T> f();",
+ "}",
+ "=== Test.java ===",
+ "import java.util.List;",
+ "class Test<T extends Number> implements A<T> {",
+ " public List<? extends T> f() {",
+ " return null;",
+ " }",
+ "}",
+ },
+ };
+ return Arrays.stream(inputs)
+ .map(input -> TestInput.parse(Joiner.on('\n').join(input)))
+ .map(x -> new Object[] {x})
+ .collect(toImmutableList());
+ }
+
+ private final TestInput input;
+
+ public TurbineElementsGetAllMembersTest(TestInput input) {
+ this.input = input;
+ }
+
+ // Compile the test inputs with javac and turbine, and assert that getAllMembers returns the
+ // same elements under each implementation.
+ @Test
+ public void test() throws Exception {
+ JavacTask javacTask =
+ IntegrationTestSupport.runJavacAnalysis(
+ input.sources, ImmutableList.of(), ImmutableList.of());
+ Elements javacElements = javacTask.getElements();
+ List<? extends Element> javacMembers =
+ javacElements.getAllMembers(requireNonNull(javacElements.getTypeElement("Test")));
+
+ ImmutableList<CompUnit> units =
+ input.sources.entrySet().stream()
+ .map(e -> new SourceFile(e.getKey(), e.getValue()))
+ .map(Parser::parse)
+ .collect(toImmutableList());
+
+ Binder.BindingResult bound =
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+
+ Env<ClassSymbol, TypeBoundClass> env =
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(bound.classPathEnv())
+ .append(new SimpleEnv<>(bound.units()));
+ ModelFactory factory = new ModelFactory(env, ClassLoader.getSystemClassLoader(), bound.tli());
+ TurbineTypes turbineTypes = new TurbineTypes(factory);
+ TurbineElements turbineElements = new TurbineElements(factory, turbineTypes);
+ List<? extends Element> turbineMembers =
+ turbineElements.getAllMembers(factory.typeElement(new ClassSymbol("Test")));
+
+ assertThat(formatElements(turbineMembers))
+ .containsExactlyElementsIn(formatElements(javacMembers));
+ }
+
+ private static ImmutableList<String> formatElements(Collection<? extends Element> elements) {
+ return elements.stream()
+ .map(e -> String.format("%s %s.%s %s", e.getKind(), e.getEnclosingElement(), e, e.asType()))
+ .collect(toImmutableList());
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineElementsTest.java b/javatests/com/google/turbine/processing/TurbineElementsTest.java
new file mode 100644
index 0000000..770e6f6
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineElementsTest.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+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 com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.testing.EqualsTester;
+import com.google.turbine.binder.Binder.BindingResult;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.lower.IntegrationTestSupport;
+import com.google.turbine.testing.TestClassPaths;
+import com.sun.source.util.JavacTask;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.util.Elements;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineElementsTest {
+
+ private static final IntegrationTestSupport.TestInput SOURCES =
+ IntegrationTestSupport.TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== Test.java ===",
+ "@Deprecated",
+ "@A class Test extends One {}",
+ "=== One.java ===",
+ "/** javadoc",
+ " * for",
+ " * one",
+ " */",
+ "@B class One extends Two {",
+ " /** method javadoc */",
+ " void f() {}",
+ " /** field javadoc */",
+ " int x;",
+ "}",
+ "=== Two.java ===",
+ "/** javadoc",
+ " for",
+ " two with extra *",
+ " */",
+ "@C(1) class Two extends Three {}",
+ "=== Three.java ===",
+ "@C(2) class Three extends Four {}",
+ "=== Four.java ===",
+ "@D class Four {}",
+ "=== Annotations.java ===",
+ "import java.lang.annotation.Inherited;",
+ "@interface A {}",
+ "@interface B {}",
+ "@Inherited",
+ "@interface C {",
+ " int value() default 42;",
+ "}",
+ "@Inherited",
+ "@interface D {}",
+ "=== com/pkg/P.java ===",
+ "package com.pkg;",
+ "@interface P {}",
+ "=== com/pkg/package-info.java ===",
+ "@P",
+ "package com.pkg;",
+ "=== Const.java ===",
+ "class Const {",
+ " static final int X = 1867;",
+ "}",
+ "=== com/pkg/empty/package-info.java ===",
+ "@P",
+ "package com.pkg.empty;",
+ "import com.pkg.P;",
+ "=== com/pkg/A.java ===",
+ "package com.pkg;",
+ "class A {",
+ " class I {}",
+ "}",
+ "=== com/pkg/B.java ===",
+ "package com.pkg;",
+ "class B {}"));
+
+ Elements javacElements;
+ ModelFactory factory;
+ TurbineElements turbineElements;
+
+ @Before
+ public void setup() throws Exception {
+ JavacTask task =
+ IntegrationTestSupport.runJavacAnalysis(
+ SOURCES.sources, ImmutableList.of(), ImmutableList.of());
+ task.analyze();
+ javacElements = task.getElements();
+
+ BindingResult bound =
+ IntegrationTestSupport.turbineAnalysis(
+ SOURCES.sources,
+ ImmutableList.of(),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+ Env<ClassSymbol, TypeBoundClass> env =
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(bound.classPathEnv())
+ .append(new SimpleEnv<>(bound.units()));
+ factory = new ModelFactory(env, TurbineElementsTest.class.getClassLoader(), bound.tli());
+ TurbineTypes turbineTypes = new TurbineTypes(factory);
+ turbineElements = new TurbineElements(factory, turbineTypes);
+ }
+
+ @Test
+ public void constants() {
+ for (Object value :
+ Arrays.asList(
+ Short.valueOf((short) 1),
+ Short.MIN_VALUE,
+ Short.MAX_VALUE,
+ Byte.valueOf((byte) 1),
+ Byte.MIN_VALUE,
+ Byte.MAX_VALUE,
+ Integer.valueOf(1),
+ Integer.MIN_VALUE,
+ Integer.MAX_VALUE,
+ Long.valueOf(1),
+ Long.MIN_VALUE,
+ Long.MAX_VALUE,
+ Float.valueOf(1),
+ Float.NaN,
+ Double.NEGATIVE_INFINITY,
+ Double.POSITIVE_INFINITY,
+ Float.MAX_VALUE,
+ Float.MIN_VALUE,
+ Double.valueOf(1),
+ Double.NaN,
+ Double.NEGATIVE_INFINITY,
+ Double.POSITIVE_INFINITY,
+ Double.MAX_VALUE,
+ Double.MIN_VALUE,
+ 'a',
+ '\n',
+ "hello",
+ "\"hello\n\"")) {
+ assertThat(turbineElements.getConstantExpression(value))
+ .isEqualTo(javacElements.getConstantExpression(value));
+ }
+ }
+
+ @Test
+ public void getName() {
+ Name n = turbineElements.getName("hello");
+ assertThat(n.contentEquals("hello")).isTrue();
+ assertThat(n.contentEquals("goodbye")).isFalse();
+
+ assertThat(n.toString()).isEqualTo("hello");
+ assertThat(n.toString())
+ .isEqualTo(new String(new char[] {'h', 'e', 'l', 'l', 'o'})); // defeat interning
+
+ assertThat(n.length()).isEqualTo(5);
+
+ new EqualsTester()
+ .addEqualityGroup(turbineElements.getName("hello"), turbineElements.getName("hello"))
+ .addEqualityGroup(turbineElements.getName("goodbye"))
+ .testEquals();
+ }
+
+ @Test
+ public void getAllAnnotationMirrors() {
+ assertThat(
+ toStrings(
+ turbineElements.getAllAnnotationMirrors(
+ factory.typeElement(new ClassSymbol("Test")))))
+ .containsExactly("@java.lang.Deprecated", "@A", "@C(1)", "@D");
+ }
+
+ @Test
+ public void getTypeElement() {
+ for (String name : Arrays.asList("java.util.Map", "java.util.Map.Entry")) {
+ assertThat(turbineElements.getTypeElement(name).getQualifiedName().toString())
+ .isEqualTo(name);
+ }
+ assertThat(turbineElements.getTypeElement("NoSuch")).isNull();
+ assertThat(turbineElements.getTypeElement("java.lang.Object.NoSuch")).isNull();
+ assertThat(turbineElements.getTypeElement("java.lang.NoSuch")).isNull();
+ assertThat(turbineElements.getTypeElement("java.lang.Integer.MAX_VALUE")).isNull();
+ }
+
+ private static ImmutableList<String> toStrings(List<?> inputs) {
+ return inputs.stream().map(String::valueOf).collect(toImmutableList());
+ }
+
+ @Test
+ public void isDeprecated() {
+ assertThat(turbineElements.isDeprecated(turbineElements.getTypeElement("java.lang.Object")))
+ .isFalse();
+ assertThat(turbineElements.isDeprecated(turbineElements.getTypeElement("One"))).isFalse();
+ assertThat(turbineElements.isDeprecated(turbineElements.getTypeElement("Test"))).isTrue();
+ }
+
+ @Test
+ public void getBinaryName() {
+ assertThat(
+ turbineElements
+ .getBinaryName(turbineElements.getTypeElement("java.util.Map.Entry"))
+ .toString())
+ .isEqualTo("java.util.Map$Entry");
+ }
+
+ @Test
+ public void methodDefaultTest() {
+ assertThat(
+ ((ExecutableElement)
+ getOnlyElement(turbineElements.getTypeElement("C").getEnclosedElements()))
+ .getDefaultValue()
+ .getValue())
+ .isEqualTo(42);
+ }
+
+ @Test
+ public void constantFieldTest() {
+ assertThat(
+ ((VariableElement)
+ turbineElements.getTypeElement("Const").getEnclosedElements().stream()
+ .filter(x -> x.getKind().equals(ElementKind.FIELD))
+ .collect(onlyElement()))
+ .getConstantValue())
+ .isEqualTo(1867);
+ }
+
+ @Test
+ public void packageElement() {
+ assertThat(
+ toStrings(
+ turbineElements.getAllAnnotationMirrors(
+ turbineElements.getPackageElement("com.pkg"))))
+ .containsExactly("@com.pkg.P");
+ assertThat(
+ turbineElements.getAllAnnotationMirrors(turbineElements.getPackageElement("java.lang")))
+ .isEmpty();
+ assertThat(turbineElements.getPackageElement("com.google.no.such.pkg")).isNull();
+ }
+
+ @Test
+ public void packageMembers() {
+ assertThat(
+ turbineElements.getPackageElement("com.pkg").getEnclosedElements().stream()
+ .map(e -> ((TypeElement) e).getQualifiedName().toString())
+ .collect(toImmutableList()))
+ .containsExactly("com.pkg.P", "com.pkg.A", "com.pkg.B");
+ assertThat(turbineElements.getPackageElement("com.pkg.empty").getEnclosedElements()).isEmpty();
+ }
+
+ @Test
+ public void noElement() {
+ Element e = factory.noElement("com.google.Foo");
+ assertThat(e.getKind()).isEqualTo(ElementKind.CLASS);
+ assertThat(e.getSimpleName().toString()).isEqualTo("Foo");
+ assertThat(e.getEnclosingElement().toString()).isEqualTo("com.google");
+ assertThat(e.getEnclosingElement().getKind()).isEqualTo(ElementKind.PACKAGE);
+
+ e = factory.noElement("Foo");
+ assertThat(e.getSimpleName().toString()).isEqualTo("Foo");
+ assertThat(e.getEnclosingElement().toString()).isEmpty();
+ assertThat(e.getEnclosingElement().getKind()).isEqualTo(ElementKind.PACKAGE);
+ }
+
+ @Test
+ public void javadoc() {
+ TypeElement e = turbineElements.getTypeElement("One");
+ assertThat(turbineElements.getDocComment(e))
+ .isEqualTo(
+ " javadoc\n" //
+ + " for\n"
+ + " one\n"
+ + "");
+
+ assertThat(
+ turbineElements.getDocComment(
+ e.getEnclosedElements().stream()
+ .filter(x -> x.getKind().equals(ElementKind.FIELD))
+ .collect(onlyElement())))
+ .isEqualTo(" field javadoc ");
+
+ assertThat(
+ turbineElements.getDocComment(
+ e.getEnclosedElements().stream()
+ .filter(x -> x.getKind().equals(ElementKind.METHOD))
+ .collect(onlyElement())))
+ .isEqualTo(" method javadoc ");
+
+ e = turbineElements.getTypeElement("Two");
+ assertThat(turbineElements.getDocComment(e))
+ .isEqualTo(
+ " javadoc\n" //
+ + "for\n"
+ + "two with extra *\n"
+ + "");
+ }
+
+ @Test
+ public void syntheticParameters() {
+ assertThat(
+ ((ExecutableElement)
+ getOnlyElement(
+ turbineElements.getTypeElement("com.pkg.A.I").getEnclosedElements()))
+ .getParameters())
+ .isEmpty();
+ }
+
+ @Test
+ public void printElements() {
+ StringWriter w = new StringWriter();
+ turbineElements.printElements(
+ w,
+ turbineElements.getTypeElement("com.pkg.A"),
+ turbineElements.getTypeElement("com.pkg.A.I"));
+ assertThat(w.toString()).isEqualTo(lines("com.pkg.A", "com.pkg.A.I"));
+ }
+
+ private String lines(String... lines) {
+ return Joiner.on(System.lineSeparator()).join(lines) + System.lineSeparator();
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineFilerTest.java b/javatests/com/google/turbine/processing/TurbineFilerTest.java
new file mode 100644
index 0000000..40b78ea
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineFilerTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2019 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.processing;
+
+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 com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.CharStreams;
+import com.google.turbine.diag.SourceFile;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.processing.FilerException;
+import javax.lang.model.element.Element;
+import javax.tools.FileObject;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardLocation;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineFilerTest {
+
+ private final Set<String> seen = new HashSet<>();
+ private TurbineFiler filer;
+
+ @Before
+ public void setup() {
+ Function<String, Supplier<byte[]>> classpath =
+ new Function<String, Supplier<byte[]>>() {
+ @Nullable
+ @Override
+ public Supplier<byte[]> apply(String input) {
+ return null;
+ }
+ };
+ this.filer = new TurbineFiler(seen, classpath, TurbineFilerTest.class.getClassLoader());
+ }
+
+ @Test
+ public void hello() throws IOException {
+ JavaFileObject sourceFile = filer.createSourceFile("com.foo.Bar", (Element[]) null);
+ try (OutputStream os = sourceFile.openOutputStream()) {
+ os.write("hello".getBytes(UTF_8));
+ }
+ assertThat(sourceFile.getLastModified()).isEqualTo(0);
+
+ JavaFileObject classFile = filer.createClassFile("com.foo.Baz", (Element[]) null);
+ try (OutputStream os = classFile.openOutputStream()) {
+ os.write("goodbye".getBytes(UTF_8));
+ }
+ assertThat(classFile.getLastModified()).isEqualTo(0);
+
+ Collection<SourceFile> roundSources = filer.finishRound();
+ assertThat(roundSources).hasSize(1);
+ assertThat(filer.generatedSources()).hasSize(1);
+ assertThat(filer.generatedClasses()).hasSize(1);
+
+ SourceFile source = getOnlyElement(roundSources);
+ assertThat(source.path()).isEqualTo("com/foo/Bar.java");
+ assertThat(source.source()).isEqualTo("hello");
+
+ Map.Entry<String, byte[]> clazz = getOnlyElement(filer.generatedClasses().entrySet());
+ assertThat(clazz.getKey()).isEqualTo("com/foo/Baz.class");
+ assertThat(new String(clazz.getValue(), UTF_8)).isEqualTo("goodbye");
+ }
+
+ @Test
+ public void existing() throws IOException {
+ seen.add("com/foo/Bar.java");
+ seen.add("com/foo/Baz.class");
+
+ try {
+ filer.createSourceFile("com.foo.Bar", (Element[]) null);
+ fail();
+ } catch (FilerException expected) {
+ }
+ 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) {
+ }
+ }
+
+ @Test
+ public void get() throws IOException {
+ for (StandardLocation location :
+ Arrays.asList(
+ StandardLocation.CLASS_OUTPUT,
+ StandardLocation.SOURCE_OUTPUT,
+ StandardLocation.ANNOTATION_PROCESSOR_PATH,
+ StandardLocation.CLASS_PATH)) {
+ try {
+ filer.getResource(location, "", "NoSuch");
+ fail();
+ } catch (FileNotFoundException expected) {
+ }
+ }
+ }
+
+ @Test
+ public void sourceOutput() throws IOException {
+ JavaFileObject classFile = filer.createSourceFile("com.foo.Bar", (Element[]) null);
+ try (Writer writer = classFile.openWriter()) {
+ writer.write("hello");
+ }
+ filer.finishRound();
+
+ FileObject output = filer.getResource(StandardLocation.SOURCE_OUTPUT, "com.foo", "Bar.java");
+ assertThat(new String(ByteStreams.toByteArray(output.openInputStream()), UTF_8))
+ .isEqualTo("hello");
+ assertThat(output.getCharContent(false).toString()).isEqualTo("hello");
+ assertThat(CharStreams.toString(output.openReader(true))).isEqualTo("hello");
+ }
+
+ @Test
+ public void classOutput() throws IOException {
+ JavaFileObject classFile = filer.createClassFile("com.foo.Baz", (Element[]) null);
+ try (OutputStream os = classFile.openOutputStream()) {
+ os.write("goodbye".getBytes(UTF_8));
+ }
+ filer.finishRound();
+
+ FileObject output = filer.getResource(StandardLocation.CLASS_OUTPUT, "com.foo", "Baz.class");
+ assertThat(new String(ByteStreams.toByteArray(output.openInputStream()), UTF_8))
+ .isEqualTo("goodbye");
+ assertThat(output.getCharContent(false).toString()).isEqualTo("goodbye");
+ assertThat(CharStreams.toString(output.openReader(true))).isEqualTo("goodbye");
+ }
+
+ @Test
+ public void classpathResources() throws IOException {
+ FileObject resource =
+ filer.getResource(StandardLocation.ANNOTATION_PROCESSOR_PATH, "META-INF", "MANIFEST.MF");
+
+ assertThat(new String(ByteStreams.toByteArray(resource.openInputStream()), UTF_8))
+ .contains("Manifest-Version:");
+ assertThat(CharStreams.toString(resource.openReader(true))).contains("Manifest-Version:");
+ assertThat(resource.getCharContent(false).toString()).contains("Manifest-Version:");
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineMessagerTest.java b/javatests/com/google/turbine/processing/TurbineMessagerTest.java
new file mode 100644
index 0000000..c1e6401
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineMessagerTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.truth.Truth.assertThat;
+import static java.util.Comparator.comparing;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.Binder;
+import com.google.turbine.binder.ClassPathBinder;
+import com.google.turbine.binder.Processing;
+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;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.tree.Tree;
+import com.sun.source.util.JavacTask;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+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.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.ElementScanner8;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticCollector;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineMessagerTest {
+
+ private static final IntegrationTestSupport.TestInput SOURCES =
+ IntegrationTestSupport.TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== Test.java ===",
+ "@A class Test {",
+ " @A @B void f() {}",
+ " @B int x;",
+ "}",
+ "=== One.java ===",
+ "class One<U, V> {",
+ " @A void f(@B int x, @C int y) {}",
+ " <X, Y> void g(X x) {}",
+ "}",
+ "=== Two.java ===",
+ "class Two {",
+ " @D(value = 1) int x1;",
+ " @D(1) int x2;",
+ " @E(1) int x3;",
+ " @E({1, 2}) int x4;",
+ " @E(value = {1, 2}, y = 3) int x5;",
+ " @E(y = 0, value = {1, 2}) int x6;",
+ "}",
+ "=== Annotations.java ===",
+ "@interface A {}",
+ "@interface B {}",
+ "@interface C {}",
+ "@interface D {",
+ " int value() default 0;",
+ "}",
+ "@interface E {",
+ " int[] value() default {};",
+ " int y() default 0;",
+ "}"));
+
+ /**
+ * Tests {@link TurbineMessager} by logging a message at each {@link Element}, {@link
+ * AnnotationMirror}, and {@link AnnotationValue}.
+ */
+ @SupportedAnnotationTypes("*")
+ public static class DiagnosticTesterProcessor extends AbstractProcessor {
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ for (Element e : roundEnv.getRootElements()) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, String.valueOf(e));
+ e.accept(
+ new ElementScanner8<Void, Void>() {
+ @Override
+ public Void scan(Element e, Void unused) {
+ processingEnv
+ .getMessager()
+ .printMessage(Diagnostic.Kind.ERROR, String.valueOf(e), e);
+ for (AnnotationMirror a : e.getAnnotationMirrors()) {
+ processingEnv
+ .getMessager()
+ .printMessage(Diagnostic.Kind.ERROR, String.format("%s %s", e, a), e, a);
+ for (AnnotationValue av : a.getElementValues().values()) {
+ processAnnotation(e, a, av);
+ }
+ }
+ return null;
+ }
+
+ private void processAnnotation(Element e, AnnotationMirror a, AnnotationValue av) {
+ processingEnv
+ .getMessager()
+ .printMessage(
+ Diagnostic.Kind.ERROR, String.format("%s %s %s", e, a, av), e, a, av);
+ av.accept(
+ new SimpleAnnotationValueVisitor8<Void, Void>() {
+ @Override
+ public Void visitAnnotation(AnnotationMirror a, Void unused) {
+ visitAnnotationValues(a.getElementValues().values());
+ return null;
+ }
+
+ @Override
+ public Void visitArray(List<? extends AnnotationValue> vals, Void unused) {
+ visitAnnotationValues(vals);
+ return null;
+ }
+
+ private void visitAnnotationValues(
+ Collection<? extends AnnotationValue> values) {
+ for (AnnotationValue av : values) {
+ processAnnotation(e, a, av);
+ }
+ }
+ },
+ null);
+ }
+
+ @Override
+ public Void visitExecutable(ExecutableElement e, Void unused) {
+ scan(e.getTypeParameters(), null);
+ return super.visitExecutable(e, unused);
+ }
+
+ @Override
+ public Void visitType(TypeElement e, Void unused) {
+ scan(e.getTypeParameters(), null);
+ return super.visitType(e, unused);
+ }
+ },
+ null);
+ }
+ return false;
+ }
+ }
+
+ @Test
+ public void test() throws Exception {
+
+ // Processes the test sources with the DiagnosticTesterProcessor under both javac and turbine,
+ // and asserts that the diagnostics have the same source path, line, and column under each.
+
+ DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
+ JavacTask task =
+ IntegrationTestSupport.runJavacAnalysis(
+ SOURCES.sources, ImmutableList.of(), ImmutableList.of(), collector);
+ task.setProcessors(ImmutableList.of(new DiagnosticTesterProcessor()));
+ task.call();
+ ImmutableList<String> javacDiagnostics =
+ collector.getDiagnostics().stream()
+ // sort the diagnostics for nicer test failure messages
+ .sorted(
+ comparing(TurbineMessagerTest::shortPath)
+ .thenComparing(Diagnostic::getLineNumber)
+ .thenComparing(Diagnostic::getColumnNumber))
+ .map(TurbineMessagerTest::formatDiagnostic)
+ .collect(toImmutableList());
+
+ ImmutableList<String> turbineDiagnostics;
+ 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 DiagnosticTesterProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+ throw new AssertionError();
+ } catch (TurbineError e) {
+ turbineDiagnostics =
+ e.diagnostics().stream()
+ .sorted(
+ comparing(TurbineDiagnostic::path)
+ .thenComparing(TurbineDiagnostic::line)
+ .thenComparing(TurbineDiagnostic::column))
+ .map(TurbineMessagerTest::formatDiagnostic)
+ .collect(toImmutableList());
+ }
+
+ assertThat(turbineDiagnostics).containsExactlyElementsIn(javacDiagnostics).inOrder();
+ }
+
+ private static String formatDiagnostic(TurbineDiagnostic d) {
+ return String.format("%s:%s:%s %s", d.path(), d.line(), d.column(), d.message());
+ }
+
+ private static String formatDiagnostic(Diagnostic<? extends JavaFileObject> d) {
+ return String.format(
+ "%s:%s:%s %s",
+ shortPath(d), d.getLineNumber(), d.getColumnNumber(), d.getMessage(Locale.ENGLISH));
+ }
+
+ private static String shortPath(Diagnostic<? extends JavaFileObject> d) {
+ return d.getSource() != null
+ ? Paths.get(d.getSource().getName()).getFileName().toString()
+ : "<>";
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineNameTest.java b/javatests/com/google/turbine/processing/TurbineNameTest.java
new file mode 100644
index 0000000..f67fc82
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineNameTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineNameTest {
+
+ @Test
+ public void equals() {
+ new EqualsTester()
+ .addEqualityGroup(
+ new TurbineName("hello"), new TurbineName("hello"), new TurbineName("hello"))
+ .addEqualityGroup(new TurbineName("is"))
+ .addEqualityGroup(new TurbineName("there"))
+ .addEqualityGroup(new TurbineName("anybody"))
+ .addEqualityGroup(new TurbineName("in"))
+ .testEquals();
+ }
+
+ @Test
+ public void asd() {
+ assertThat(new TurbineName("hello").contentEquals("hello")).isTrue();
+ assertThat(new TurbineName("hello").contentEquals("goodbye")).isFalse();
+
+ assertThat(new TurbineName("hello").length()).isEqualTo(5);
+
+ assertThat(new TurbineName("hello").charAt(0)).isEqualTo('h');
+
+ assertThat(new TurbineName("hello").subSequence(1, 4).toString()).isEqualTo("ell");
+
+ assertThat(new TurbineName("hello").toString()).isEqualTo("hello");
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypeMirrorTest.java b/javatests/com/google/turbine/processing/TurbineTypeMirrorTest.java
new file mode 100644
index 0000000..bf08f89
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypeMirrorTest.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.collect.MoreCollectors.onlyElement;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.testing.EqualsTester;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.PackageSymbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.model.TurbineConstantTypeKind;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.PrimTy;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.IntersectionType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.WildcardType;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineTypeMirrorTest {
+
+ private final ModelFactory factory =
+ new ModelFactory(
+ TestClassPaths.TURBINE_BOOTCLASSPATH.env(),
+ ClassLoader.getSystemClassLoader(),
+ TestClassPaths.TURBINE_BOOTCLASSPATH.index());
+
+ @Test
+ public void primitiveTypes() {
+ for (TypeKind kind : TypeKind.values()) {
+ if (!kind.isPrimitive()) {
+ continue;
+ }
+ TurbineConstantTypeKind turbineKind = TurbineConstantTypeKind.valueOf(kind.name());
+ TypeMirror type = factory.asTypeMirror(PrimTy.create(turbineKind, ImmutableList.of()));
+ assertThat(type.getKind()).isEqualTo(kind);
+ }
+ }
+
+ @Test
+ public void equals() {
+ new EqualsTester()
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.ClassTy.create(
+ ImmutableList.of(
+ Type.ClassTy.SimpleClassTy.create(
+ new ClassSymbol("java/util/Map"),
+ ImmutableList.of(),
+ ImmutableList.of()),
+ Type.ClassTy.SimpleClassTy.create(
+ new ClassSymbol("java/util/Map$Entry"),
+ ImmutableList.of(Type.ClassTy.STRING, Type.ClassTy.STRING),
+ ImmutableList.of())))))
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.ClassTy.create(
+ ImmutableList.of(
+ Type.ClassTy.SimpleClassTy.create(
+ new ClassSymbol("java/util/Map$Entry"),
+ ImmutableList.of(Type.ClassTy.STRING, Type.ClassTy.OBJECT),
+ ImmutableList.of())))))
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/util/Map$Entry"))))
+ .addEqualityGroup(
+ factory.asTypeMirror(PrimTy.create(TurbineConstantTypeKind.LONG, ImmutableList.of())),
+ factory.asTypeMirror(PrimTy.create(TurbineConstantTypeKind.LONG, ImmutableList.of())))
+ .addEqualityGroup(
+ factory.asTypeMirror(PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of())))
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.WildLowerBoundedTy.create(
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/lang/Integer")),
+ ImmutableList.of())))
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.WildUpperBoundedTy.create(
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/lang/Integer")),
+ ImmutableList.of())))
+ .addEqualityGroup(factory.asTypeMirror(Type.WildUnboundedTy.create(ImmutableList.of())))
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.ArrayTy.create(
+ PrimTy.create(TurbineConstantTypeKind.LONG, ImmutableList.of()),
+ ImmutableList.of())))
+ .addEqualityGroup(factory.packageType(new PackageSymbol("java/lang")))
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.TyVar.create(
+ new TyVarSymbol(new ClassSymbol("java/util/List"), "V"), ImmutableList.of())))
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.IntersectionTy.create(
+ ImmutableList.of(
+ Type.ClassTy.asNonParametricClassTy(
+ new ClassSymbol("java/io/Serializable")),
+ Type.ClassTy.asNonParametricClassTy(
+ new ClassSymbol("java/lang/Cloneable"))))))
+ .addEqualityGroup(factory.noType())
+ .testEquals();
+ }
+
+ @Test
+ public void roundTrip() {
+ DeclaredType te =
+ (DeclaredType)
+ factory.asTypeMirror(
+ Type.ClassTy.create(
+ ImmutableList.of(
+ Type.ClassTy.SimpleClassTy.create(
+ new ClassSymbol("java/util/List"),
+ ImmutableList.of(
+ Type.ClassTy.asNonParametricClassTy(
+ new ClassSymbol("java/lang/String"))),
+ ImmutableList.of()))));
+ assertThat(te.asElement().asType()).isNotEqualTo(te);
+ assertThat(te.asElement().asType())
+ .isEqualTo(
+ factory.asTypeMirror(
+ Type.ClassTy.create(
+ ImmutableList.of(
+ Type.ClassTy.SimpleClassTy.create(
+ new ClassSymbol("java/util/List"),
+ ImmutableList.of(
+ Type.TyVar.create(
+ new TyVarSymbol(new ClassSymbol("java/util/List"), "E"),
+ ImmutableList.of())),
+ ImmutableList.of())))));
+ }
+
+ @Test
+ public void wildTy() {
+ WildcardType lower =
+ (WildcardType)
+ factory.asTypeMirror(
+ Type.WildLowerBoundedTy.create(
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/lang/Integer")),
+ ImmutableList.of()));
+ WildcardType upper =
+ (WildcardType)
+ factory.asTypeMirror(
+ Type.WildUpperBoundedTy.create(
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/lang/Long")),
+ ImmutableList.of()));
+ WildcardType unbound =
+ (WildcardType) factory.asTypeMirror(Type.WildUnboundedTy.create(ImmutableList.of()));
+
+ assertThat(lower.getKind()).isEqualTo(TypeKind.WILDCARD);
+ assertThat(lower.getExtendsBound()).isNull();
+ assertThat(lower.getSuperBound().getKind()).isEqualTo(TypeKind.DECLARED);
+
+ assertThat(upper.getKind()).isEqualTo(TypeKind.WILDCARD);
+ assertThat(upper.getExtendsBound().getKind()).isEqualTo(TypeKind.DECLARED);
+ assertThat(upper.getSuperBound()).isNull();
+
+ assertThat(unbound.getKind()).isEqualTo(TypeKind.WILDCARD);
+ assertThat(unbound.getExtendsBound()).isNull();
+ assertThat(unbound.getSuperBound()).isNull();
+ }
+
+ @Test
+ public void intersection() {
+ IntersectionType t =
+ (IntersectionType)
+ factory.asTypeMirror(
+ Type.IntersectionTy.create(
+ ImmutableList.of(
+ Type.ClassTy.asNonParametricClassTy(
+ new ClassSymbol("java/io/Serializable")),
+ Type.ClassTy.asNonParametricClassTy(
+ new ClassSymbol("java/lang/Cloneable")))));
+
+ assertThat(t.getKind()).isEqualTo(TypeKind.INTERSECTION);
+ assertThat(t.getBounds())
+ .containsExactlyElementsIn(
+ factory.asTypeMirrors(
+ ImmutableList.of(
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/lang/Object")),
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/io/Serializable")),
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/lang/Cloneable")))));
+ }
+
+ @Test
+ public void tyVar() {
+ TypeVariable t =
+ (TypeVariable)
+ Iterables.getOnlyElement(
+ factory
+ .typeElement(new ClassSymbol("java/util/Collections"))
+ .getEnclosedElements()
+ .stream()
+ .filter(e -> e.getSimpleName().contentEquals("sort"))
+ .filter(ExecutableElement.class::isInstance)
+ .map(ExecutableElement.class::cast)
+ .filter(e -> e.getParameters().size() == 1)
+ .findFirst()
+ .get()
+ .getTypeParameters())
+ .asType();
+ assertThat(t.getKind()).isEqualTo(TypeKind.TYPEVAR);
+ assertThat(t.getLowerBound().getKind()).isEqualTo(TypeKind.NONE);
+ assertThat(t.getUpperBound().toString()).isEqualTo("java.lang.Comparable<? super T>");
+ }
+
+ @Test
+ public void arrayType() {
+ ArrayType t =
+ (ArrayType)
+ factory.asTypeMirror(
+ Type.ArrayTy.create(
+ PrimTy.create(TurbineConstantTypeKind.LONG, ImmutableList.of()),
+ ImmutableList.of()));
+ assertThat(t.getKind()).isEqualTo(TypeKind.ARRAY);
+ assertThat(t.getComponentType().getKind()).isEqualTo(TypeKind.LONG);
+ }
+
+ @Test
+ public void declared() {
+ DeclaredType a =
+ (DeclaredType)
+ factory.asTypeMirror(
+ Type.ClassTy.create(
+ ImmutableList.of(
+ Type.ClassTy.SimpleClassTy.create(
+ new ClassSymbol("java/util/Map"),
+ ImmutableList.of(),
+ ImmutableList.of()),
+ Type.ClassTy.SimpleClassTy.create(
+ new ClassSymbol("java/util/Map$Entry"),
+ ImmutableList.of(Type.ClassTy.STRING, Type.ClassTy.STRING),
+ ImmutableList.of()))));
+ DeclaredType b =
+ (DeclaredType)
+ factory.asTypeMirror(
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/util/Map$Entry")));
+
+ assertThat(a.getEnclosingType().getKind()).isEqualTo(TypeKind.NONE);
+ assertThat(b.getEnclosingType().getKind()).isEqualTo(TypeKind.NONE);
+ }
+
+ @Test
+ public void method() {
+ ExecutableType type =
+ (ExecutableType)
+ ((TypeElement) factory.typeElement(new ClassSymbol("java/util/Collections")))
+ .getEnclosedElements().stream()
+ .filter(e -> e.getSimpleName().contentEquals("replaceAll"))
+ .collect(onlyElement())
+ .asType();
+ assertThat(type.getTypeVariables()).hasSize(1);
+ assertThat(type.toString()).isEqualTo("<T>(java.util.List<T>,T,T)boolean");
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesContainsTest.java b/javatests/com/google/turbine/processing/TurbineTypesContainsTest.java
new file mode 100644
index 0000000..e9e411f
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesContainsTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.truth.TruthJUnit.assume;
+
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.Types;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineTypesContainsTest extends AbstractTurbineTypesBiPredicateTest {
+
+ @Parameters(name = "{index}: {0}")
+ public static Iterable<Object[]> parameters() throws Exception {
+ return binaryParameters();
+ }
+
+ public TurbineTypesContainsTest(
+ String name, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
+ super(name, javacInput, turbineInput);
+ }
+
+ @Test
+ public void contains() {
+ // crashes javac
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.NONE);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.NONE);
+
+ // crashes javac
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.EXECUTABLE);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.EXECUTABLE);
+
+ test("<=", Types::contains);
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesFactoryTest.java b/javatests/com/google/turbine/processing/TurbineTypesFactoryTest.java
new file mode 100644
index 0000000..0f9e6a6
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesFactoryTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.turbine.binder.Binder.BindingResult;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.lower.IntegrationTestSupport;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.type.Type.ArrayTy;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.ClassTy.SimpleClassTy;
+import com.google.turbine.type.Type.WildLowerBoundedTy;
+import com.google.turbine.type.Type.WildUnboundedTy;
+import com.google.turbine.type.Type.WildUpperBoundedTy;
+import java.util.Optional;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineTypesFactoryTest {
+
+ private static final IntegrationTestSupport.TestInput SOURCES =
+ IntegrationTestSupport.TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== Test.java ===", //
+ "class Test {",
+ " class I {}",
+ "}"));
+
+ ModelFactory factory;
+ TurbineElements turbineElements;
+ TurbineTypes turbineTypes;
+
+ @Before
+ public void setup() throws Exception {
+
+ BindingResult bound =
+ IntegrationTestSupport.turbineAnalysis(
+ SOURCES.sources,
+ ImmutableList.of(),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+ Env<ClassSymbol, TypeBoundClass> env =
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(bound.classPathEnv())
+ .append(new SimpleEnv<>(bound.units()));
+ factory = new ModelFactory(env, getClass().getClassLoader(), bound.tli());
+ turbineTypes = new TurbineTypes(factory);
+ turbineElements = new TurbineElements(factory, turbineTypes);
+ }
+
+ @Test
+ public void primitiveTypes() {
+ for (TypeKind kind : TypeKind.values()) {
+ if (kind.isPrimitive()) {
+ PrimitiveType type = turbineTypes.getPrimitiveType(kind);
+ assertThat(type.getKind()).isEqualTo(kind);
+ } else {
+ try {
+ turbineTypes.getPrimitiveType(kind);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+ }
+ }
+
+ @Test
+ public void arrayType() {
+ assertThat(
+ turbineTypes.isSameType(
+ turbineTypes.getArrayType(
+ turbineTypes.erasure(turbineElements.getTypeElement("java.util.Map").asType())),
+ factory.asTypeMirror(
+ ArrayTy.create(
+ ClassTy.asNonParametricClassTy(new ClassSymbol("java/util/Map")),
+ ImmutableList.of()))))
+ .isTrue();
+ }
+
+ @Test
+ public void wildcardType() {
+ // wildcard types don't compare equal with isSameType, so compare their string representations
+ assertThat(turbineTypes.getWildcardType(null, null).toString())
+ .isEqualTo(factory.asTypeMirror(WildUnboundedTy.create(ImmutableList.of())).toString());
+ assertThat(
+ turbineTypes
+ .getWildcardType(turbineElements.getTypeElement("java.lang.String").asType(), null)
+ .toString())
+ .isEqualTo(
+ factory
+ .asTypeMirror(WildUpperBoundedTy.create(ClassTy.STRING, ImmutableList.of()))
+ .toString());
+ assertThat(
+ turbineTypes
+ .getWildcardType(null, turbineElements.getTypeElement("java.lang.String").asType())
+ .toString())
+ .isEqualTo(
+ factory
+ .asTypeMirror(WildLowerBoundedTy.create(ClassTy.STRING, ImmutableList.of()))
+ .toString());
+ }
+
+ @Test
+ public void declaredType() {
+ assertThat(
+ turbineTypes.isSameType(
+ turbineTypes.getDeclaredType(
+ turbineElements.getTypeElement("java.util.Map"),
+ turbineElements.getTypeElement("java.lang.String").asType(),
+ turbineElements.getTypeElement("java.lang.Integer").asType()),
+ factory.asTypeMirror(
+ ClassTy.create(
+ ImmutableList.of(
+ SimpleClassTy.create(
+ new ClassSymbol("java/util/Map"),
+ ImmutableList.of(
+ ClassTy.STRING,
+ ClassTy.asNonParametricClassTy(ClassSymbol.INTEGER)),
+ ImmutableList.of()))))))
+ .isTrue();
+ assertThat(
+ turbineTypes.isSameType(
+ turbineTypes.getDeclaredType(
+ turbineTypes.getDeclaredType(turbineElements.getTypeElement("Test")),
+ turbineElements.getTypeElement("Test.I")),
+ factory.asTypeMirror(
+ ClassTy.create(
+ ImmutableList.of(
+ SimpleClassTy.create(
+ new ClassSymbol("Test"), ImmutableList.of(), ImmutableList.of()),
+ SimpleClassTy.create(
+ new ClassSymbol("Test$I"),
+ ImmutableList.of(),
+ ImmutableList.of()))))))
+ .isTrue();
+ }
+
+ @Test
+ 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) {
+ }
+ }
+
+ @Test
+ public void nullType() {
+ assertThat(turbineTypes.getNullType().getKind()).isEqualTo(TypeKind.NULL);
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesIsAssignableTest.java b/javatests/com/google/turbine/processing/TurbineTypesIsAssignableTest.java
new file mode 100644
index 0000000..75b93b0
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesIsAssignableTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.truth.TruthJUnit.assume;
+
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.Types;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineTypesIsAssignableTest extends AbstractTurbineTypesBiPredicateTest {
+
+ @Parameters(name = "{index}: {0}")
+ public static Iterable<Object[]> parameters() throws Exception {
+ return binaryParameters();
+ }
+
+ public TurbineTypesIsAssignableTest(
+ String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
+ super(testDescription, javacInput, turbineInput);
+ }
+
+ @Test
+ public void isAssignable() {
+ // see JDK-8039198
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.WILDCARD);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.WILDCARD);
+
+ // crashes javac
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.NONE);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.NONE);
+
+ // crashes javac
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.EXECUTABLE);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.EXECUTABLE);
+
+ test("isAssignable", Types::isAssignable);
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesIsSameTypeTest.java b/javatests/com/google/turbine/processing/TurbineTypesIsSameTypeTest.java
new file mode 100644
index 0000000..ef45991
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesIsSameTypeTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 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.processing;
+
+import javax.lang.model.util.Types;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineTypesIsSameTypeTest extends AbstractTurbineTypesBiPredicateTest {
+
+ @Parameters(name = "{index}: {0}")
+ public static Iterable<Object[]> parameters() throws Exception {
+ return binaryParameters();
+ }
+
+ public TurbineTypesIsSameTypeTest(
+ String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
+ super(testDescription, javacInput, turbineInput);
+ }
+
+ @Test
+ public void isSameType() {
+ test("isSameType", Types::isSameType);
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesIsSubsignatureTest.java b/javatests/com/google/turbine/processing/TurbineTypesIsSubsignatureTest.java
new file mode 100644
index 0000000..5f315da
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesIsSubsignatureTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.truth.TruthJUnit.assume;
+
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeKind;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineTypesIsSubsignatureTest extends AbstractTurbineTypesBiPredicateTest {
+
+ @Parameters(name = "{index}: {0}")
+ public static Iterable<Object[]> parameters() throws Exception {
+ return binaryParameters();
+ }
+
+ public TurbineTypesIsSubsignatureTest(
+ String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
+ super(testDescription, javacInput, turbineInput);
+ }
+
+ @Test
+ public void isSubsignature() {
+ assume().that(javacInput.lhs.getKind()).isEqualTo(TypeKind.EXECUTABLE);
+ assume().that(javacInput.rhs.getKind()).isEqualTo(TypeKind.EXECUTABLE);
+
+ test(
+ "isSubsignature",
+ (types, x, y) -> types.isSubsignature((ExecutableType) x, (ExecutableType) y));
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesIsSubtypeTest.java b/javatests/com/google/turbine/processing/TurbineTypesIsSubtypeTest.java
new file mode 100644
index 0000000..8370126
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesIsSubtypeTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.truth.TruthJUnit.assume;
+
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.Types;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineTypesIsSubtypeTest extends AbstractTurbineTypesBiPredicateTest {
+
+ @Parameters(name = "{index}: {0}")
+ public static Iterable<Object[]> parameters() throws Exception {
+ return binaryParameters();
+ }
+
+ public TurbineTypesIsSubtypeTest(
+ String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
+ super(testDescription, javacInput, turbineInput);
+ }
+
+ @Test
+ public void isSubtype() {
+ // see JDK-8039198
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.WILDCARD);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.WILDCARD);
+
+ // crashes javac
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.NONE);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.NONE);
+
+ // crashes javac
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.EXECUTABLE);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.EXECUTABLE);
+
+ test("<:", Types::isSubtype);
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesUnaryTest.java b/javatests/com/google/turbine/processing/TurbineTypesUnaryTest.java
new file mode 100644
index 0000000..eb5ee6c
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesUnaryTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Types;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineTypesUnaryTest extends AbstractTurbineTypesTest {
+
+ @Parameters(name = "{index}: {0}")
+ public static Iterable<Object[]> parameters() throws Exception {
+ return unaryParameters();
+ }
+
+ final String testDescription;
+ final Types javacTypes;
+ final TypeMirror javacA;
+ final Types turbineTypes;
+ final TypeMirror turbineA;
+
+ public TurbineTypesUnaryTest(
+ String testDescription,
+ Types javacTypes,
+ TypeMirror javacA,
+ Types turbineTypes,
+ TypeMirror turbineA) {
+ this.testDescription = testDescription;
+ this.javacTypes = javacTypes;
+ this.javacA = javacA;
+ this.turbineTypes = turbineTypes;
+ this.turbineA = turbineA;
+ }
+
+ @Test
+ public void unboxedType() {
+ IllegalArgumentException thrown = null;
+ String expectedType = null;
+ try {
+ expectedType = javacTypes.unboxedType(javacA).toString();
+ } catch (IllegalArgumentException e) {
+ thrown = e;
+ }
+ if (thrown != null) {
+ try {
+ turbineTypes.unboxedType(turbineA).toString();
+ fail(String.format("expected unboxedType(`%s`) to throw", turbineA));
+ } catch (IllegalArgumentException expected) {
+ // expected
+ }
+ } else {
+ String actual = turbineTypes.unboxedType(turbineA).toString();
+ assertWithMessage("unboxedClass(`%s`) = unboxedClass(`%s`)", javacA, turbineA)
+ .that(actual)
+ .isEqualTo(expectedType);
+ }
+ }
+
+ @Test
+ public void boxedClass() {
+ assume().that(javacA).isInstanceOf(PrimitiveType.class);
+ assume().that(turbineA).isInstanceOf(PrimitiveType.class);
+
+ String expected = javacTypes.boxedClass((PrimitiveType) javacA).toString();
+ String actual = turbineTypes.boxedClass((PrimitiveType) turbineA).toString();
+ assertWithMessage("boxedClass(`%s`) = boxedClass(`%s`)", javacA, turbineA)
+ .that(actual)
+ .isEqualTo(expected);
+ }
+
+ @Test
+ public void erasure() {
+ String expected = javacTypes.erasure(javacA).toString();
+ String actual = turbineTypes.erasure(turbineA).toString();
+ assertWithMessage("erasure(`%s`) = erasure(`%s`)", javacA, turbineA)
+ .that(actual)
+ .isEqualTo(expected);
+ }
+
+ private static final ImmutableSet<TypeKind> UNSUPPORTED_BY_DIRECT_SUPERTYPES =
+ ImmutableSet.of(TypeKind.EXECUTABLE, TypeKind.PACKAGE);
+
+ @Test
+ public void directSupertypes() {
+ assume().that(UNSUPPORTED_BY_DIRECT_SUPERTYPES).doesNotContain(javacA.getKind());
+
+ String expected = Joiner.on(", ").join(javacTypes.directSupertypes(javacA));
+ String actual = Joiner.on(", ").join(turbineTypes.directSupertypes(turbineA));
+ assertWithMessage("directSupertypes(`%s`) = directSupertypes(`%s`)", javacA, turbineA)
+ .that(actual)
+ .isEqualTo(expected);
+ }
+
+ @Test
+ 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) {
+ }
+ }
+
+ @Test
+ public void asElement() {
+ // TODO(cushon): this looks like a javac bug
+ assume().that(javacA.getKind()).isNotEqualTo(TypeKind.INTERSECTION);
+
+ String expected = String.valueOf(javacTypes.asElement(javacA));
+ String actual = String.valueOf(turbineTypes.asElement(turbineA));
+ assertWithMessage("asElement(`%s`) = asElement(`%s`)", javacA, turbineA)
+ .that(actual)
+ .isEqualTo(expected);
+ }
+}
diff --git a/javatests/com/google/turbine/testing/TestClassPaths.java b/javatests/com/google/turbine/testing/TestClassPaths.java
index bf38913..93be916 100644
--- a/javatests/com/google/turbine/testing/TestClassPaths.java
+++ b/javatests/com/google/turbine/testing/TestClassPaths.java
@@ -67,7 +67,7 @@ public class TestClassPaths {
public static TurbineOptions.Builder optionsWithBootclasspath() {
TurbineOptions.Builder options = TurbineOptions.builder();
if (!BOOTCLASSPATH.isEmpty()) {
- options.addBootClassPathEntries(
+ options.setBootClassPath(
BOOTCLASSPATH.stream().map(Path::toString).collect(toImmutableList()));
} else {
options.setRelease("8");
diff --git a/javatests/com/google/turbine/type/TypeTest.java b/javatests/com/google/turbine/type/TypeTest.java
new file mode 100644
index 0000000..be3eb9c
--- /dev/null
+++ b/javatests/com/google/turbine/type/TypeTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019 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.type;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.testing.EqualsTester;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.tree.Tree.Ident;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.ClassTy.SimpleClassTy;
+import com.google.turbine.type.Type.ErrorTy;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TypeTest {
+
+ @Test
+ public void equals() {
+ new EqualsTester()
+ .addEqualityGroup(
+ ClassTy.create(
+ ImmutableList.of(
+ SimpleClassTy.create(
+ new ClassSymbol("java/util/Map"), ImmutableList.of(), ImmutableList.of()),
+ SimpleClassTy.create(
+ new ClassSymbol("java/util/Map$Entry"),
+ ImmutableList.of(ClassTy.STRING, ClassTy.STRING),
+ ImmutableList.of()))))
+ .addEqualityGroup(
+ SimpleClassTy.create(
+ new ClassSymbol("java/util/Map$Entry"),
+ ImmutableList.of(ClassTy.STRING, ClassTy.OBJECT),
+ ImmutableList.of()))
+ .addEqualityGroup(ClassTy.asNonParametricClassTy(new ClassSymbol("java/util/Map$Entry")))
+ .testEquals();
+ }
+
+ private static final int NO_POSITION = -1;
+
+ @Test
+ public void error() {
+ assertThat(
+ ErrorTy.create(
+ ImmutableList.of(
+ new Ident(NO_POSITION, "com"),
+ new Ident(NO_POSITION, "foo"),
+ new Ident(NO_POSITION, "Bar")))
+ .name())
+ .isEqualTo("com.foo.Bar");
+ }
+}
diff --git a/javatests/com/google/turbine/zip/ZipTest.java b/javatests/com/google/turbine/zip/ZipTest.java
index 67dcfe7..bfc9cdf 100644
--- a/javatests/com/google/turbine/zip/ZipTest.java
+++ b/javatests/com/google/turbine/zip/ZipTest.java
@@ -165,7 +165,7 @@ public class ZipTest {
actual(path);
fail();
} catch (ZipException e) {
- assertThat(e).hasMessage("zip file comment length was 33, expected 17");
+ assertThat(e).hasMessageThat().isEqualTo("zip file comment length was 33, expected 17");
}
}
}
diff --git a/pom.xml b/pom.xml
index cbef973..fa923f4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,13 +74,19 @@
<dependency>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
- <version>0.42</version>
+ <version>1.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.truth.extensions</groupId>
+ <artifactId>truth-proto-extension</artifactId>
+ <version>1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.truth.extensions</groupId>
<artifactId>truth-java8-extension</artifactId>
- <version>0.42</version>
+ <version>1.0</version>
<scope>test</scope>
</dependency>
<dependency>
diff --git a/proto/deps.proto b/proto/deps.proto
index 27013a2..e93cc95 100644
--- a/proto/deps.proto
+++ b/proto/deps.proto
@@ -20,7 +20,6 @@ option java_package = "com.google.turbine.proto";
option java_outer_classname = "DepsProto";
message Dependency {
-
enum Kind {
// Dependency used explicitly in the source.
EXPLICIT = 0;
@@ -49,4 +48,9 @@ message Dependencies {
// Whether the action was successful; even when compilation fails, partial
// dependency information can be useful.
optional bool success = 3;
+
+ // If the Java action was started with a reduced classpath and an error
+ // occurred suggesting that it should be rerun with the full classpath, this
+ // will be true.
+ optional bool requires_reduced_classpath_fallback = 5;
}
diff --git a/proto/manifest.proto b/proto/manifest.proto
new file mode 100644
index 0000000..20e85ef
--- /dev/null
+++ b/proto/manifest.proto
@@ -0,0 +1,40 @@
+// 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.
+
+// Definitions for dependency reports.
+
+syntax = "proto2";
+
+option java_package = "com.google.turbine.proto";
+option java_outer_classname = "ManifestProto";
+
+// Information about a single compilation unit (.java file)
+message CompilationUnit {
+ // The path to the compilation unit
+ optional string path = 1;
+
+ // The package of the source file
+ optional string pkg = 2;
+
+ // Whether the source was generated by an annotation processor
+ optional bool generated_by_annotation_processor = 3;
+
+ // The list of top-level types in the compilation unit
+ repeated string top_level = 4;
+}
+
+// Top-level message found in .manifest artifacts
+message Manifest {
+ repeated CompilationUnit compilation_unit = 1;
+} \ No newline at end of file