aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-06 20:38:08 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-06 20:38:08 +0000
commitfd58723b87850ddc7adbbc660fb4499bc67c9ea5 (patch)
treeb7c0ba640fd38e96068089eb744b59b100cfe192
parentb5fe7f1c2587f22da5d51648f13263e671b19e95 (diff)
parent661cd3505f46001cb94b65aa0fd21a3170119087 (diff)
downloadturbine-android13-qpr1-release.tar.gz
Change-Id: If4ebd892df4728845b14d5d960b9819560aba63b
-rw-r--r--.github/dependabot.yml6
-rw-r--r--.github/workflows/ci.yml20
-rw-r--r--.mvn/jvm.config10
-rwxr-xr-xandroid-annotation-stubs/gen_annotations.sh4
-rw-r--r--android-annotation-stubs/src/org/jspecify/nullness/NullMarked.java (renamed from android-annotation-stubs/src/org/checkerframework/checker/nullness/compatqual/NullableDecl.java)4
-rw-r--r--android-annotation-stubs/src/org/jspecify/nullness/Nullable.java (renamed from android-annotation-stubs/src/org/checkerframework/checker/nullness/qual/Nullable.java)2
-rw-r--r--java/com/google/common/escape/package-info.java19
-rw-r--r--java/com/google/turbine/binder/Binder.java50
-rw-r--r--java/com/google/turbine/binder/CanonicalTypeBinder.java67
-rw-r--r--java/com/google/turbine/binder/ClassPath.java2
-rw-r--r--java/com/google/turbine/binder/ClassPathBinder.java5
-rw-r--r--java/com/google/turbine/binder/CompUnitPreprocessor.java9
-rw-r--r--java/com/google/turbine/binder/ConstBinder.java29
-rw-r--r--java/com/google/turbine/binder/ConstEvaluator.java703
-rw-r--r--java/com/google/turbine/binder/CtSymClassBinder.java35
-rw-r--r--java/com/google/turbine/binder/DisambiguateTypeAnnotations.java45
-rw-r--r--java/com/google/turbine/binder/FileManagerClassBinder.java10
-rw-r--r--java/com/google/turbine/binder/HierarchyBinder.java15
-rw-r--r--java/com/google/turbine/binder/JimageClassBinder.java24
-rw-r--r--java/com/google/turbine/binder/ModuleBinder.java12
-rw-r--r--java/com/google/turbine/binder/Processing.java85
-rw-r--r--java/com/google/turbine/binder/Resolve.java51
-rw-r--r--java/com/google/turbine/binder/TypeBinder.java242
-rw-r--r--java/com/google/turbine/binder/bound/AnnotationMetadata.java11
-rw-r--r--java/com/google/turbine/binder/bound/BoundClass.java2
-rw-r--r--java/com/google/turbine/binder/bound/EnumConstantValue.java3
-rw-r--r--java/com/google/turbine/binder/bound/HeaderBoundClass.java2
-rw-r--r--java/com/google/turbine/binder/bound/ModuleInfo.java13
-rw-r--r--java/com/google/turbine/binder/bound/PackageSourceBoundClass.java3
-rw-r--r--java/com/google/turbine/binder/bound/SourceBoundClass.java7
-rw-r--r--java/com/google/turbine/binder/bound/SourceHeaderBoundClass.java9
-rw-r--r--java/com/google/turbine/binder/bound/SourceModuleInfo.java2
-rw-r--r--java/com/google/turbine/binder/bound/SourceTypeBoundClass.java40
-rw-r--r--java/com/google/turbine/binder/bound/TurbineAnnotationValue.java3
-rw-r--r--java/com/google/turbine/binder/bound/TurbineClassValue.java3
-rw-r--r--java/com/google/turbine/binder/bound/TypeBoundClass.java83
-rw-r--r--java/com/google/turbine/binder/bound/package-info.java19
-rw-r--r--java/com/google/turbine/binder/bytecode/BytecodeBinder.java18
-rw-r--r--java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java30
-rw-r--r--java/com/google/turbine/binder/bytecode/package-info.java1
-rw-r--r--java/com/google/turbine/binder/env/CompoundEnv.java6
-rw-r--r--java/com/google/turbine/binder/env/Env.java10
-rw-r--r--java/com/google/turbine/binder/env/LazyEnv.java6
-rw-r--r--java/com/google/turbine/binder/env/SimpleEnv.java8
-rw-r--r--java/com/google/turbine/binder/env/package-info.java19
-rw-r--r--java/com/google/turbine/binder/lookup/CanonicalSymbolResolver.java2
-rw-r--r--java/com/google/turbine/binder/lookup/CompoundScope.java8
-rw-r--r--java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java16
-rw-r--r--java/com/google/turbine/binder/lookup/ImportIndex.java21
-rw-r--r--java/com/google/turbine/binder/lookup/ImportScope.java9
-rw-r--r--java/com/google/turbine/binder/lookup/MemberImportIndex.java22
-rw-r--r--java/com/google/turbine/binder/lookup/PackageScope.java2
-rw-r--r--java/com/google/turbine/binder/lookup/Scope.java2
-rw-r--r--java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java25
-rw-r--r--java/com/google/turbine/binder/lookup/TopLevelIndex.java2
-rw-r--r--java/com/google/turbine/binder/lookup/WildImportIndex.java23
-rw-r--r--java/com/google/turbine/binder/lookup/package-info.java19
-rw-r--r--java/com/google/turbine/binder/package-info.java19
-rw-r--r--java/com/google/turbine/binder/sym/ClassSymbol.java4
-rw-r--r--java/com/google/turbine/binder/sym/FieldSymbol.java3
-rw-r--r--java/com/google/turbine/binder/sym/MethodSymbol.java3
-rw-r--r--java/com/google/turbine/binder/sym/ModuleSymbol.java3
-rw-r--r--java/com/google/turbine/binder/sym/PackageSymbol.java3
-rw-r--r--java/com/google/turbine/binder/sym/ParamSymbol.java3
-rw-r--r--java/com/google/turbine/binder/sym/RecordComponentSymbol.java67
-rw-r--r--java/com/google/turbine/binder/sym/Symbol.java1
-rw-r--r--java/com/google/turbine/binder/sym/TyVarSymbol.java3
-rw-r--r--java/com/google/turbine/binder/sym/package-info.java19
-rw-r--r--java/com/google/turbine/bytecode/AnnotationWriter.java19
-rw-r--r--java/com/google/turbine/bytecode/Attribute.java102
-rw-r--r--java/com/google/turbine/bytecode/AttributeWriter.java129
-rw-r--r--java/com/google/turbine/bytecode/ByteReader.java2
-rw-r--r--java/com/google/turbine/bytecode/ClassFile.java156
-rw-r--r--java/com/google/turbine/bytecode/ClassReader.java31
-rw-r--r--java/com/google/turbine/bytecode/ClassWriter.java8
-rw-r--r--java/com/google/turbine/bytecode/ConstantPoolReader.java2
-rw-r--r--java/com/google/turbine/bytecode/LowerAttributes.java27
-rw-r--r--java/com/google/turbine/bytecode/package-info.java19
-rw-r--r--java/com/google/turbine/bytecode/sig/Sig.java10
-rw-r--r--java/com/google/turbine/bytecode/sig/SigParser.java2
-rw-r--r--java/com/google/turbine/bytecode/sig/package-info.java19
-rw-r--r--java/com/google/turbine/deps/Dependencies.java2
-rw-r--r--java/com/google/turbine/deps/Transitive.java9
-rw-r--r--java/com/google/turbine/deps/package-info.java18
-rw-r--r--java/com/google/turbine/diag/SourceFile.java3
-rw-r--r--java/com/google/turbine/diag/TurbineDiagnostic.java7
-rw-r--r--java/com/google/turbine/diag/TurbineError.java2
-rw-r--r--java/com/google/turbine/diag/package-info.java18
-rw-r--r--java/com/google/turbine/lower/Lower.java148
-rw-r--r--java/com/google/turbine/lower/LowerSignature.java21
-rw-r--r--java/com/google/turbine/lower/package-info.java18
-rw-r--r--java/com/google/turbine/main/Main.java31
-rw-r--r--java/com/google/turbine/main/package-info.java18
-rw-r--r--java/com/google/turbine/model/Const.java354
-rw-r--r--java/com/google/turbine/model/TurbineElementType.java3
-rw-r--r--java/com/google/turbine/model/TurbineFlag.java6
-rw-r--r--java/com/google/turbine/model/TurbineTyKind.java3
-rw-r--r--java/com/google/turbine/model/package-info.java18
-rw-r--r--java/com/google/turbine/options/LanguageVersion.java136
-rw-r--r--java/com/google/turbine/options/TurbineOptions.java9
-rw-r--r--java/com/google/turbine/options/TurbineOptionsParser.java32
-rw-r--r--java/com/google/turbine/options/package-info.java1
-rw-r--r--java/com/google/turbine/parse/ConstExpressionParser.java104
-rw-r--r--java/com/google/turbine/parse/Parser.java331
-rw-r--r--java/com/google/turbine/parse/StreamLexer.java3
-rw-r--r--java/com/google/turbine/parse/Token.java4
-rw-r--r--java/com/google/turbine/parse/package-info.java18
-rw-r--r--java/com/google/turbine/processing/ModelFactory.java24
-rw-r--r--java/com/google/turbine/processing/TurbineAnnotationMirror.java9
-rw-r--r--java/com/google/turbine/processing/TurbineElement.java153
-rw-r--r--java/com/google/turbine/processing/TurbineElements.java87
-rw-r--r--java/com/google/turbine/processing/TurbineMessager.java9
-rw-r--r--java/com/google/turbine/processing/TurbineName.java3
-rw-r--r--java/com/google/turbine/processing/TurbineProcessingEnvironment.java2
-rw-r--r--java/com/google/turbine/processing/TurbineTypeMirror.java17
-rw-r--r--java/com/google/turbine/processing/TurbineTypes.java36
-rw-r--r--java/com/google/turbine/processing/package-info.java18
-rw-r--r--java/com/google/turbine/tree/Pretty.java113
-rw-r--r--java/com/google/turbine/tree/Tree.java176
-rw-r--r--java/com/google/turbine/tree/TurbineModifier.java7
-rw-r--r--java/com/google/turbine/tree/package-info.java19
-rw-r--r--java/com/google/turbine/type/AnnoInfo.java3
-rw-r--r--java/com/google/turbine/type/Type.java9
-rw-r--r--java/com/google/turbine/type/package-info.java18
-rw-r--r--java/com/google/turbine/types/Canonicalize.java15
-rw-r--r--java/com/google/turbine/types/package-info.java18
-rw-r--r--java/com/google/turbine/zip/package-info.java18
-rw-r--r--javatests/com/google/turbine/binder/BinderErrorTest.java158
-rw-r--r--javatests/com/google/turbine/binder/BinderTest.java30
-rw-r--r--javatests/com/google/turbine/binder/CtSymClassBinderTest.java17
-rw-r--r--javatests/com/google/turbine/binder/ProcessingTest.java83
-rw-r--r--javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java2
-rw-r--r--javatests/com/google/turbine/bytecode/ClassReaderTest.java13
-rw-r--r--javatests/com/google/turbine/bytecode/ClassWriterTest.java104
-rw-r--r--javatests/com/google/turbine/deps/DependenciesTest.java8
-rw-r--r--javatests/com/google/turbine/lower/IntegrationTestSupport.java68
-rw-r--r--javatests/com/google/turbine/lower/LongStringIntegrationTest.java105
-rw-r--r--javatests/com/google/turbine/lower/LowerIntegrationTest.java23
-rw-r--r--javatests/com/google/turbine/lower/LowerTest.java63
-rw-r--r--javatests/com/google/turbine/lower/MissingJavaBaseModule.java105
-rw-r--r--javatests/com/google/turbine/lower/ModuleIntegrationTest.java5
-rw-r--r--javatests/com/google/turbine/lower/testdata/ambiguous_identifier.test14
-rw-r--r--javatests/com/google/turbine/lower/testdata/array_class_literal.test3
-rw-r--r--javatests/com/google/turbine/lower/testdata/const_operation_order.test13
-rw-r--r--javatests/com/google/turbine/lower/testdata/empty_package_info.test3
-rw-r--r--javatests/com/google/turbine/lower/testdata/record.test46
-rw-r--r--javatests/com/google/turbine/lower/testdata/record2.test27
-rw-r--r--javatests/com/google/turbine/lower/testdata/sealed.test11
-rw-r--r--javatests/com/google/turbine/lower/testdata/string_const.test7
-rw-r--r--javatests/com/google/turbine/main/MainTest.java3
-rw-r--r--javatests/com/google/turbine/options/LanguageVersionTest.java147
-rw-r--r--javatests/com/google/turbine/options/TurbineOptionsTest.java22
-rw-r--r--javatests/com/google/turbine/parse/ExpressionParserTest.java16
-rw-r--r--javatests/com/google/turbine/parse/ParseErrorTest.java17
-rw-r--r--javatests/com/google/turbine/parse/ParserIntegrationTest.java3
-rw-r--r--javatests/com/google/turbine/parse/testdata/anno2.input3
-rw-r--r--javatests/com/google/turbine/parse/testdata/record.input53
-rw-r--r--javatests/com/google/turbine/parse/testdata/sealed.input15
-rw-r--r--javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java15
-rw-r--r--javatests/com/google/turbine/processing/ProcessingIntegrationTest.java181
-rw-r--r--javatests/com/google/turbine/processing/TurbineAnnotationMirrorTest.java7
-rw-r--r--javatests/com/google/turbine/processing/TurbineElementsHidesTest.java318
-rw-r--r--javatests/com/google/turbine/processing/TurbineFilerTest.java13
-rw-r--r--javatests/com/google/turbine/processing/TurbineMessagerTest.java57
-rw-r--r--javatests/com/google/turbine/testing/TestClassPaths.java3
-rw-r--r--javatests/com/google/turbine/zip/ZipTest.java2
-rw-r--r--pom.xml116
167 files changed, 4847 insertions, 1675 deletions
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..daec318
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: "maven"
+ directory: "/"
+ schedule:
+ interval: "daily"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 87582f0..e12698c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -29,41 +29,35 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
- java: [ 16, 11, 8 ]
+ java: [ 17, 11 ]
experimental: [ false ]
include:
# Only test on macos and windows with a single recent JDK to avoid a
# combinatorial explosion of test configurations.
- os: macos-latest
- java: 16
+ java: 17
experimental: false
- os: windows-latest
- java: 16
+ java: 17
experimental: false
- os: ubuntu-latest
- java: 17-ea
+ java: 18-ea
experimental: true
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
steps:
- name: Cancel previous
- uses: styfle/cancel-workflow-action@0.8.0
+ uses: styfle/cancel-workflow-action@0.9.1
with:
access_token: ${{ github.token }}
- name: 'Check out repository'
uses: actions/checkout@v2
- - name: 'Cache local Maven repository'
- uses: actions/cache@v2
- with:
- path: ~/.m2/repository
- key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- ${{ runner.os }}-maven-
- name: 'Set up JDK ${{ matrix.java }}'
uses: actions/setup-java@v2
with:
java-version: ${{ matrix.java }}
- distribution: 'adopt'
+ distribution: 'zulu'
+ cache: 'maven'
- name: 'Install'
shell: bash
run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
diff --git a/.mvn/jvm.config b/.mvn/jvm.config
new file mode 100644
index 0000000..504456f
--- /dev/null
+++ b/.mvn/jvm.config
@@ -0,0 +1,10 @@
+--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
+--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
+--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
+--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
+--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
+--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
+--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
+--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
+--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
diff --git a/android-annotation-stubs/gen_annotations.sh b/android-annotation-stubs/gen_annotations.sh
index 1965324..b2bb170 100755
--- a/android-annotation-stubs/gen_annotations.sh
+++ b/android-annotation-stubs/gen_annotations.sh
@@ -5,8 +5,8 @@ declare -A PARAMETER
declare -A IMPORT
ANNOTATIONS=(
- org.checkerframework.checker.nullness.qual.Nullable
- org.checkerframework.checker.nullness.compatqual.NullableDecl
+ org.jspecify.nullness.Nullable
+ org.jspecify.nullness.NullMarked
)
for a in ${ANNOTATIONS[@]}; do
diff --git a/android-annotation-stubs/src/org/checkerframework/checker/nullness/compatqual/NullableDecl.java b/android-annotation-stubs/src/org/jspecify/nullness/NullMarked.java
index 2d39b0f..ef4b562 100644
--- a/android-annotation-stubs/src/org/checkerframework/checker/nullness/compatqual/NullableDecl.java
+++ b/android-annotation-stubs/src/org/jspecify/nullness/NullMarked.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.checkerframework.checker.nullness.compatqual;
+package org.jspecify.nullness;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -37,4 +37,4 @@ import java.lang.annotation.Target;
ElementType.TYPE_USE
})
@Retention(RetentionPolicy.SOURCE)
-public @interface NullableDecl {}
+public @interface NullMarked {}
diff --git a/android-annotation-stubs/src/org/checkerframework/checker/nullness/qual/Nullable.java b/android-annotation-stubs/src/org/jspecify/nullness/Nullable.java
index 276d64c..7156d51 100644
--- a/android-annotation-stubs/src/org/checkerframework/checker/nullness/qual/Nullable.java
+++ b/android-annotation-stubs/src/org/jspecify/nullness/Nullable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.checkerframework.checker.nullness.qual;
+package org.jspecify.nullness;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
diff --git a/java/com/google/common/escape/package-info.java b/java/com/google/common/escape/package-info.java
new file mode 100644
index 0000000..b69b34e
--- /dev/null
+++ b/java/com/google/common/escape/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+@org.jspecify.nullness.NullMarked
+package com.google.common.escape;
diff --git a/java/com/google/turbine/binder/Binder.java b/java/com/google/turbine/binder/Binder.java
index 6c828b3..d2ce948 100644
--- a/java/com/google/turbine/binder/Binder.java
+++ b/java/com/google/turbine/binder/Binder.java
@@ -16,6 +16,8 @@
package com.google.turbine.binder;
+import static java.util.Objects.requireNonNull;
+
import com.google.auto.value.AutoValue;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
@@ -67,12 +69,13 @@ import com.google.turbine.type.Type;
import java.time.Duration;
import java.util.Optional;
import javax.annotation.processing.Processor;
+import org.jspecify.nullness.Nullable;
/** The entry point for analysis. */
public final class Binder {
/** Binds symbols and types to the given compilation units. */
- public static BindingResult bind(
+ public static @Nullable BindingResult bind(
ImmutableList<CompUnit> units,
ClassPath classpath,
ClassPath bootclasspath,
@@ -81,7 +84,7 @@ public final class Binder {
}
/** Binds symbols and types to the given compilation units. */
- public static BindingResult bind(
+ public static @Nullable BindingResult bind(
ImmutableList<CompUnit> units,
ClassPath classpath,
ProcessorInfo processorInfo,
@@ -179,11 +182,11 @@ public final class Binder {
ImmutableMap.Builder<ClassSymbol, SourceTypeBoundClass> result = ImmutableMap.builder();
for (ClassSymbol sym : syms) {
- result.put(sym, tenv.get(sym));
+ result.put(sym, tenv.getNonNull(sym));
}
return new BindingResult(
- result.build(),
+ result.buildOrThrow(),
boundModules,
classPathEnv,
tli,
@@ -250,11 +253,12 @@ public final class Binder {
ImportScope wildImportScope = WildImportIndex.create(importResolver, tli, unit.imports());
MemberImportIndex memberImports =
new MemberImportIndex(unit.source(), importResolver, tli, unit.imports());
- ImportScope scope =
- ImportScope.fromScope(topLevel)
- .append(wildImportScope)
- .append(ImportScope.fromScope(packageScope))
- .append(importScope);
+ ImportScope scope = ImportScope.fromScope(topLevel).append(wildImportScope);
+ // Can be null if we're compiling a package-info.java for an empty package
+ if (packageScope != null) {
+ scope = scope.append(ImportScope.fromScope(packageScope));
+ }
+ scope = scope.append(importScope);
if (unit.module().isPresent()) {
ModDecl module = unit.module().get();
modules.put(
@@ -284,12 +288,12 @@ public final class Binder {
@Override
public SourceHeaderBoundClass complete(
Env<ClassSymbol, HeaderBoundClass> henv, ClassSymbol sym) {
- PackageSourceBoundClass base = psenv.get(sym);
+ PackageSourceBoundClass base = psenv.getNonNull(sym);
return HierarchyBinder.bind(log.withSource(base.source()), sym, base, henv);
}
});
}
- return new LazyEnv<>(completers.build(), classPathEnv);
+ return new LazyEnv<>(completers.buildOrThrow(), classPathEnv);
}
private static Env<ClassSymbol, SourceTypeBoundClass> bindTypes(
@@ -299,7 +303,7 @@ public final class Binder {
Env<ClassSymbol, HeaderBoundClass> henv) {
SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder();
for (ClassSymbol sym : syms) {
- SourceHeaderBoundClass base = shenv.get(sym);
+ SourceHeaderBoundClass base = shenv.getNonNull(sym);
builder.put(sym, TypeBinder.bind(log.withSource(base.source()), henv, sym, base));
}
return builder.build();
@@ -311,7 +315,8 @@ public final class Binder {
Env<ClassSymbol, TypeBoundClass> tenv) {
SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder();
for (ClassSymbol sym : syms) {
- builder.put(sym, CanonicalTypeBinder.bind(sym, stenv.get(sym), tenv));
+ SourceTypeBoundClass base = stenv.getNonNull(sym);
+ builder.put(sym, CanonicalTypeBinder.bind(sym, base, tenv));
}
return builder.build();
}
@@ -328,7 +333,7 @@ public final class Binder {
moduleEnv.append(
new Env<ModuleSymbol, ModuleInfo>() {
@Override
- public ModuleInfo get(ModuleSymbol sym) {
+ public @Nullable ModuleInfo get(ModuleSymbol sym) {
PackageSourceBoundModule info = modules.get(sym);
if (info != null) {
return new ModuleInfo(
@@ -366,7 +371,7 @@ public final class Binder {
ImmutableMap.Builder<FieldSymbol, LazyEnv.Completer<FieldSymbol, Const.Value, Const.Value>>
completers = ImmutableMap.builder();
for (ClassSymbol sym : syms) {
- SourceTypeBoundClass info = env.get(sym);
+ SourceTypeBoundClass info = env.getNonNull(sym);
for (FieldInfo field : info.fields()) {
if (!isConst(field)) {
continue;
@@ -375,7 +380,8 @@ public final class Binder {
field.sym(),
new LazyEnv.Completer<FieldSymbol, Const.Value, Const.Value>() {
@Override
- public Const.Value complete(Env<FieldSymbol, Const.Value> env1, FieldSymbol k) {
+ public Const.@Nullable Value complete(
+ Env<FieldSymbol, Const.Value> env1, FieldSymbol k) {
try {
return new ConstEvaluator(
sym,
@@ -386,7 +392,9 @@ public final class Binder {
env1,
baseEnv,
log.withSource(info.source()))
- .evalFieldInitializer(field.decl().init().get(), field.type());
+ .evalFieldInitializer(
+ // we're processing fields bound from sources in the compilation
+ requireNonNull(field.decl()).init().get(), field.type());
} catch (LazyEnv.LazyBindingError e) {
// fields initializers are allowed to reference the field being initialized,
// but if they do they aren't constants
@@ -401,11 +409,12 @@ public final class Binder {
// lazily evaluated fields in the current compilation unit with
// constant fields in the classpath (which don't require evaluation).
Env<FieldSymbol, Const.Value> constenv =
- new LazyEnv<>(completers.build(), SimpleEnv.<FieldSymbol, Const.Value>builder().build());
+ new LazyEnv<>(
+ completers.buildOrThrow(), SimpleEnv.<FieldSymbol, Const.Value>builder().build());
SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder();
for (ClassSymbol sym : syms) {
- SourceTypeBoundClass base = env.get(sym);
+ SourceTypeBoundClass base = env.getNonNull(sym);
builder.put(
sym, new ConstBinder(constenv, sym, baseEnv, base, log.withSource(base.source())).bind());
}
@@ -447,7 +456,8 @@ public final class Binder {
Env<ClassSymbol, TypeBoundClass> tenv) {
SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder();
for (ClassSymbol sym : syms) {
- builder.put(sym, DisambiguateTypeAnnotations.bind(stenv.get(sym), tenv));
+ SourceTypeBoundClass base = stenv.getNonNull(sym);
+ builder.put(sym, DisambiguateTypeAnnotations.bind(base, tenv));
}
return builder.build();
}
diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java
index ae82b4f..5ff17bb 100644
--- a/java/com/google/turbine/binder/CanonicalTypeBinder.java
+++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java
@@ -16,6 +16,8 @@
package com.google.turbine.binder;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.bound.SourceTypeBoundClass;
@@ -23,6 +25,7 @@ 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.RecordComponentInfo;
import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
import com.google.turbine.binder.env.Env;
import com.google.turbine.binder.sym.ClassSymbol;
@@ -43,30 +46,32 @@ public final class CanonicalTypeBinder {
static SourceTypeBoundClass bind(
ClassSymbol sym, SourceTypeBoundClass base, Env<ClassSymbol, TypeBoundClass> env) {
Type superClassType = base.superClassType();
+ int pos = base.decl().position();
if (superClassType != null && superClassType.tyKind() == TyKind.CLASS_TY) {
superClassType =
Canonicalize.canonicalizeClassTy(
- base.source(), base.decl().position(), env, base.owner(), (ClassTy) superClassType);
+ base.source(), pos, env, base.owner(), (ClassTy) superClassType);
}
ImmutableList.Builder<Type> interfaceTypes = ImmutableList.builder();
for (Type i : base.interfaceTypes()) {
if (i.tyKind() == TyKind.CLASS_TY) {
- i =
- Canonicalize.canonicalizeClassTy(
- base.source(), base.decl().position(), env, base.owner(), (ClassTy) i);
+ i = Canonicalize.canonicalizeClassTy(base.source(), pos, env, base.owner(), (ClassTy) i);
}
interfaceTypes.add(i);
}
ImmutableMap<TyVarSymbol, TyVarInfo> typParamTypes =
- typeParameters(base.source(), base.decl().position(), env, sym, base.typeParameterTypes());
- ImmutableList<MethodInfo> methods =
- methods(base.source(), base.decl().position(), env, sym, base.methods());
+ typeParameters(base.source(), pos, env, sym, base.typeParameterTypes());
+ ImmutableList<RecordComponentInfo> components =
+ components(base.source(), env, sym, pos, base.components());
+ ImmutableList<MethodInfo> methods = methods(base.source(), pos, env, sym, base.methods());
ImmutableList<FieldInfo> fields = fields(base.source(), env, sym, base.fields());
return new SourceTypeBoundClass(
interfaceTypes.build(),
+ base.permits(),
superClassType,
typParamTypes,
base.access(),
+ components,
methods,
fields,
base.owner(),
@@ -92,7 +97,13 @@ public final class CanonicalTypeBinder {
result.add(
new FieldInfo(
base.sym(),
- Canonicalize.canonicalize(source, base.decl().position(), env, sym, base.type()),
+ Canonicalize.canonicalize(
+ source,
+ // we're processing fields bound from sources in the compilation
+ requireNonNull(base.decl()).position(),
+ env,
+ sym,
+ base.type()),
base.access(),
base.annotations(),
base.decl(),
@@ -113,17 +124,14 @@ public final class CanonicalTypeBinder {
ImmutableMap<TyVarSymbol, TyVarInfo> tps =
typeParameters(source, pos, env, sym, base.tyParams());
Type ret = Canonicalize.canonicalize(source, pos, env, sym, base.returnType());
- ImmutableList.Builder<ParamInfo> parameters = ImmutableList.builder();
- for (ParamInfo parameter : base.parameters()) {
- parameters.add(param(source, pos, env, sym, parameter));
- }
+ ImmutableList<ParamInfo> parameters = parameters(source, env, sym, pos, base.parameters());
ImmutableList<Type> exceptions = canonicalizeList(source, pos, env, sym, base.exceptions());
result.add(
new MethodInfo(
base.sym(),
tps,
ret,
- parameters.build(),
+ parameters,
exceptions,
base.access(),
base.defaultValue(),
@@ -134,6 +142,19 @@ public final class CanonicalTypeBinder {
return result.build();
}
+ private static ImmutableList<ParamInfo> parameters(
+ SourceFile source,
+ Env<ClassSymbol, TypeBoundClass> env,
+ ClassSymbol sym,
+ int pos,
+ ImmutableList<ParamInfo> parameters) {
+ ImmutableList.Builder<ParamInfo> result = ImmutableList.builder();
+ for (ParamInfo parameter : parameters) {
+ result.add(param(source, pos, env, sym, parameter));
+ }
+ return result.build();
+ }
+
private static ParamInfo param(
SourceFile source,
int position,
@@ -147,6 +168,24 @@ public final class CanonicalTypeBinder {
base.access());
}
+ private static ImmutableList<RecordComponentInfo> components(
+ SourceFile source,
+ Env<ClassSymbol, TypeBoundClass> env,
+ ClassSymbol sym,
+ int pos,
+ ImmutableList<RecordComponentInfo> components) {
+ ImmutableList.Builder<RecordComponentInfo> result = ImmutableList.builder();
+ for (RecordComponentInfo component : components) {
+ result.add(
+ new RecordComponentInfo(
+ component.sym(),
+ Canonicalize.canonicalize(source, pos, env, sym, component.type()),
+ component.annotations(),
+ component.access()));
+ }
+ return result.build();
+ }
+
private static ImmutableMap<TyVarSymbol, TyVarInfo> typeParameters(
SourceFile source,
int position,
@@ -160,7 +199,7 @@ public final class CanonicalTypeBinder {
(IntersectionTy) Canonicalize.canonicalize(source, position, env, sym, info.upperBound());
result.put(e.getKey(), new TyVarInfo(upperBound, /* lowerBound= */ null, info.annotations()));
}
- return result.build();
+ return result.buildOrThrow();
}
private static ImmutableList<Type> canonicalizeList(
diff --git a/java/com/google/turbine/binder/ClassPath.java b/java/com/google/turbine/binder/ClassPath.java
index eeea7c5..eb78099 100644
--- a/java/com/google/turbine/binder/ClassPath.java
+++ b/java/com/google/turbine/binder/ClassPath.java
@@ -23,6 +23,7 @@ import com.google.turbine.binder.env.Env;
import com.google.turbine.binder.lookup.TopLevelIndex;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.ModuleSymbol;
+import org.jspecify.nullness.Nullable;
/**
* A compilation classpath, e.g. the user or platform class path. May be backed by a search path of
@@ -38,5 +39,6 @@ public interface ClassPath {
/** The classpath's top level index. */
TopLevelIndex index();
+ @Nullable
Supplier<byte[]> resource(String path);
}
diff --git a/java/com/google/turbine/binder/ClassPathBinder.java b/java/com/google/turbine/binder/ClassPathBinder.java
index 1825c23..1c41e96 100644
--- a/java/com/google/turbine/binder/ClassPathBinder.java
+++ b/java/com/google/turbine/binder/ClassPathBinder.java
@@ -36,6 +36,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
+import org.jspecify.nullness.Nullable;
/** Sets up an environment for symbols on the classpath. */
public final class ClassPathBinder {
@@ -57,7 +58,7 @@ public final class ClassPathBinder {
Env<ClassSymbol, BytecodeBoundClass> benv =
new Env<ClassSymbol, BytecodeBoundClass>() {
@Override
- public BytecodeBoundClass get(ClassSymbol sym) {
+ public @Nullable BytecodeBoundClass get(ClassSymbol sym) {
return map.get(sym);
}
};
@@ -92,7 +93,7 @@ public final class ClassPathBinder {
}
@Override
- public Supplier<byte[]> resource(String path) {
+ public @Nullable Supplier<byte[]> resource(String path) {
return resources.get(path);
}
};
diff --git a/java/com/google/turbine/binder/CompUnitPreprocessor.java b/java/com/google/turbine/binder/CompUnitPreprocessor.java
index 9e9a0bb..970dc4b 100644
--- a/java/com/google/turbine/binder/CompUnitPreprocessor.java
+++ b/java/com/google/turbine/binder/CompUnitPreprocessor.java
@@ -149,7 +149,7 @@ public final class CompUnitPreprocessor {
types.add(new SourceBoundClass(sym, owner, children, access, decl));
}
}
- return result.build();
+ return result.buildOrThrow();
}
/** Desugars access flags for a class. */
@@ -175,6 +175,9 @@ public final class CompUnitPreprocessor {
case ANNOTATION:
access |= TurbineFlag.ACC_ABSTRACT | TurbineFlag.ACC_INTERFACE | TurbineFlag.ACC_ANNOTATION;
break;
+ case RECORD:
+ access |= TurbineFlag.ACC_SUPER | TurbineFlag.ACC_FINAL;
+ break;
}
return access;
}
@@ -195,12 +198,14 @@ public final class CompUnitPreprocessor {
case INTERFACE:
case ENUM:
case ANNOTATION:
+ case RECORD:
access |= TurbineFlag.ACC_STATIC;
break;
case CLASS:
if ((enclosing & (TurbineFlag.ACC_INTERFACE | TurbineFlag.ACC_ANNOTATION)) != 0) {
access |= TurbineFlag.ACC_STATIC;
}
+ break;
}
// propagate strictfp to nested types
@@ -219,6 +224,8 @@ public final class CompUnitPreprocessor {
Optional.empty(),
ImmutableList.of(),
ImmutableList.of(),
+ ImmutableList.of(),
+ ImmutableList.of(),
TurbineTyKind.INTERFACE,
/* javadoc= */ null);
}
diff --git a/java/com/google/turbine/binder/ConstBinder.java b/java/com/google/turbine/binder/ConstBinder.java
index 8511183..29ae710 100644
--- a/java/com/google/turbine/binder/ConstBinder.java
+++ b/java/com/google/turbine/binder/ConstBinder.java
@@ -29,6 +29,7 @@ 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.RecordComponentInfo;
import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
import com.google.turbine.binder.env.CompoundEnv;
import com.google.turbine.binder.env.Env;
@@ -57,6 +58,7 @@ import com.google.turbine.type.Type.WildUnboundedTy;
import com.google.turbine.type.Type.WildUpperBoundedTy;
import java.lang.annotation.RetentionPolicy;
import java.util.Map;
+import org.jspecify.nullness.Nullable;
/** Binding pass to evaluate constant expressions. */
public class ConstBinder {
@@ -103,13 +105,16 @@ public class ConstBinder {
env,
log)
.evaluateAnnotations(base.annotations());
+ ImmutableList<RecordComponentInfo> components = bindRecordComponents(base.components());
ImmutableList<TypeBoundClass.FieldInfo> fields = fields(base.fields());
ImmutableList<MethodInfo> methods = bindMethods(base.methods());
return new SourceTypeBoundClass(
bindTypes(base.interfaceTypes()),
+ base.permits(),
base.superClassType() != null ? bindType(base.superClassType()) : null,
bindTypeParameters(base.typeParameterTypes()),
base.access(),
+ components,
methods,
fields,
base.owner(),
@@ -166,7 +171,17 @@ public class ConstBinder {
return new ParamInfo(base.sym(), bindType(base.type()), annos, base.access());
}
- static AnnotationMetadata bindAnnotationMetadata(
+ private ImmutableList<RecordComponentInfo> bindRecordComponents(
+ ImmutableList<RecordComponentInfo> components) {
+ ImmutableList.Builder<RecordComponentInfo> result = ImmutableList.builder();
+ for (RecordComponentInfo base : components) {
+ ImmutableList<AnnoInfo> annos = constEvaluator.evaluateAnnotations(base.annotations());
+ result.add(new RecordComponentInfo(base.sym(), bindType(base.type()), annos, base.access()));
+ }
+ return result.build();
+ }
+
+ static @Nullable AnnotationMetadata bindAnnotationMetadata(
TurbineTyKind kind, Iterable<AnnoInfo> annotations) {
if (kind != TurbineTyKind.ANNOTATION) {
return null;
@@ -196,7 +211,7 @@ public class ConstBinder {
return new AnnotationMetadata(retention, target, repeatable);
}
- private static RetentionPolicy bindRetention(AnnoInfo annotation) {
+ private static @Nullable RetentionPolicy bindRetention(AnnoInfo annotation) {
Const value = annotation.values().get("value");
if (value == null) {
return null;
@@ -232,7 +247,7 @@ public class ConstBinder {
return result.build();
}
- private static ClassSymbol bindRepeatable(AnnoInfo annotation) {
+ private static @Nullable ClassSymbol bindRepeatable(AnnoInfo annotation) {
// requireNonNull is safe because java.lang.annotation.Repeatable declares `value`.
Const value = requireNonNull(annotation.values().get("value"));
if (value.kind() != Kind.CLASS_LITERAL) {
@@ -268,7 +283,7 @@ public class ConstBinder {
return result.build();
}
- private Value fieldValue(TypeBoundClass.FieldInfo base) {
+ private @Nullable Value fieldValue(TypeBoundClass.FieldInfo base) {
if (base.decl() == null || !base.decl().init().isPresent()) {
return null;
}
@@ -292,7 +307,9 @@ public class ConstBinder {
return null;
}
if (type.tyKind().equals(TyKind.PRIM_TY)) {
- value = ConstEvaluator.coerce(value, ((Type.PrimTy) type).primkind());
+ value =
+ constEvaluator.coerce(
+ base.decl().init().get().position(), value, ((Type.PrimTy) type).primkind());
}
return value;
}
@@ -317,7 +334,7 @@ public class ConstBinder {
/* lowerBound= */ null,
constEvaluator.evaluateAnnotations(info.annotations())));
}
- return result.build();
+ return result.buildOrThrow();
}
private Type bindType(Type type) {
diff --git a/java/com/google/turbine/binder/ConstEvaluator.java b/java/com/google/turbine/binder/ConstEvaluator.java
index bef98a7..771e87f 100644
--- a/java/com/google/turbine/binder/ConstEvaluator.java
+++ b/java/com/google/turbine/binder/ConstEvaluator.java
@@ -17,6 +17,7 @@
package com.google.turbine.binder;
import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.Objects.requireNonNull;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
@@ -43,7 +44,12 @@ 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.ArrayInitValue;
+import com.google.turbine.model.Const.CharValue;
import com.google.turbine.model.Const.ConstCastError;
+import com.google.turbine.model.Const.DoubleValue;
+import com.google.turbine.model.Const.FloatValue;
+import com.google.turbine.model.Const.StringValue;
import com.google.turbine.model.Const.Value;
import com.google.turbine.model.TurbineConstantTypeKind;
import com.google.turbine.model.TurbineFlag;
@@ -57,15 +63,18 @@ import com.google.turbine.tree.Tree.Conditional;
import com.google.turbine.tree.Tree.ConstVarName;
import com.google.turbine.tree.Tree.Expression;
import com.google.turbine.tree.Tree.Ident;
+import com.google.turbine.tree.Tree.Paren;
import com.google.turbine.tree.Tree.PrimTy;
import com.google.turbine.tree.Tree.TypeCast;
import com.google.turbine.tree.Tree.Unary;
+import com.google.turbine.tree.TurbineOperatorKind;
import com.google.turbine.type.AnnoInfo;
import com.google.turbine.type.Type;
import java.util.ArrayDeque;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
+import org.jspecify.nullness.Nullable;
/**
* Constant expression evaluation.
@@ -75,10 +84,10 @@ import java.util.Map;
public strictfp class ConstEvaluator {
/** The symbol of the originating class, for visibility checks. */
- private final ClassSymbol origin;
+ private final @Nullable ClassSymbol origin;
/** The symbol of the enclosing class, for lexical field lookups. */
- private final ClassSymbol owner;
+ private final @Nullable ClassSymbol owner;
/** Member imports of the enclosing compilation unit. */
private final MemberImportIndex memberImports;
@@ -87,7 +96,7 @@ public strictfp class ConstEvaluator {
private final SourceFile source;
/** The constant variable environment. */
- private final Env<FieldSymbol, Const.Value> values;
+ private final Env<FieldSymbol, Value> values;
/** The class environment. */
private final CompoundEnv<ClassSymbol, TypeBoundClass> env;
@@ -97,8 +106,8 @@ public strictfp class ConstEvaluator {
private final TurbineLogWithSource log;
public ConstEvaluator(
- ClassSymbol origin,
- ClassSymbol owner,
+ @Nullable ClassSymbol origin,
+ @Nullable ClassSymbol owner,
MemberImportIndex memberImports,
SourceFile source,
Scope scope,
@@ -117,11 +126,11 @@ public strictfp class ConstEvaluator {
}
/** Evaluates the given expression's value. */
- public Const eval(Tree t) {
+ public @Nullable Const eval(Tree t) {
switch (t.kind()) {
case LITERAL:
{
- Const.Value a = (Const.Value) ((Tree.Literal) t).value();
+ Value a = (Value) ((Tree.Literal) t).value();
if (a == null) {
return null;
}
@@ -148,6 +157,8 @@ public strictfp class ConstEvaluator {
return evalClassLiteral((ClassLiteral) t);
case BINARY:
return evalBinary((Binary) t);
+ case PAREN:
+ return eval(((Paren) t).expr());
case TYPE_CAST:
return evalCast((TypeCast) t);
case UNARY:
@@ -200,11 +211,11 @@ public strictfp class ConstEvaluator {
}
LookupResult result = scope.lookup(new LookupKey(ImmutableList.copyOf(flat)));
if (result == null) {
- log.error(classTy.position(), ErrorKind.CANNOT_RESOLVE, flat.peekFirst());
+ log.error(classTy.position(), ErrorKind.CANNOT_RESOLVE, flat.getFirst());
return Type.ErrorTy.create(flat);
}
if (result.sym().symKind() != Symbol.Kind.CLASS) {
- throw error(classTy.position(), ErrorKind.UNEXPECTED_TYPE_PARAMETER, flat.peekFirst());
+ throw error(classTy.position(), ErrorKind.UNEXPECTED_TYPE_PARAMETER, flat.getFirst());
}
ClassSymbol classSym = (ClassSymbol) result.sym();
for (Ident bit : result.remaining()) {
@@ -223,6 +234,7 @@ public strictfp class ConstEvaluator {
}
/** Evaluates a reference to another constant variable. */
+ @Nullable
Const evalConstVar(ConstVarName t) {
FieldInfo field = resolveField(t);
if (field == null) {
@@ -273,7 +285,7 @@ public strictfp class ConstEvaluator {
String.format("field %s", Iterables.getLast(t.name())));
}
- private FieldInfo resolveQualifiedField(ConstVarName t) {
+ private @Nullable FieldInfo resolveQualifiedField(ConstVarName t) {
if (t.name().size() <= 1) {
return null;
}
@@ -296,10 +308,10 @@ public strictfp class ConstEvaluator {
}
/** Search for constant variables in lexically enclosing scopes. */
- private FieldInfo lexicalField(
- Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym, Ident name) {
+ private @Nullable FieldInfo lexicalField(
+ Env<ClassSymbol, TypeBoundClass> env, @Nullable ClassSymbol sym, Ident name) {
while (sym != null) {
- TypeBoundClass info = env.get(sym);
+ TypeBoundClass info = env.getNonNull(sym);
FieldInfo field = Resolve.resolveField(env, origin, sym, name);
if (field != null) {
return field;
@@ -320,55 +332,311 @@ public strictfp class ConstEvaluator {
if (!value.kind().equals(Const.Kind.PRIMITIVE)) {
throw error(position, ErrorKind.EXPRESSION_ERROR);
}
- return coerce((Const.Value) value, ((Type.PrimTy) ty).primkind());
+ return coerce(position, (Value) value, ((Type.PrimTy) ty).primkind());
default:
throw new AssertionError(ty.tyKind());
}
}
/** Casts the constant value to the given type. */
- static Const.Value coerce(Const.Value value, TurbineConstantTypeKind kind) {
+ Value coerce(int position, Value value, TurbineConstantTypeKind kind) {
switch (kind) {
+ case BYTE:
+ return asByte(position, value);
+ case SHORT:
+ return asShort(position, value);
+ case INT:
+ return asInt(position, value);
+ case LONG:
+ return asLong(position, value);
+ case FLOAT:
+ return asFloat(position, value);
+ case DOUBLE:
+ return asDouble(position, value);
+ case CHAR:
+ return asChar(position, value);
case BOOLEAN:
- return value.asBoolean();
case STRING:
- return value.asString();
+ case NULL:
+ if (!value.constantTypeKind().equals(kind)) {
+ throw typeError(position, value, kind);
+ }
+ return value;
+ }
+ throw new AssertionError(kind);
+ }
+
+ private Const.BooleanValue asBoolean(int position, Value value) {
+ if (!value.constantTypeKind().equals(TurbineConstantTypeKind.BOOLEAN)) {
+ throw typeError(position, value, TurbineConstantTypeKind.BOOLEAN);
+ }
+ return (Const.BooleanValue) value;
+ }
+
+ private Const.StringValue asString(int position, Value value) {
+ if (!value.constantTypeKind().equals(TurbineConstantTypeKind.STRING)) {
+ throw typeError(position, value, TurbineConstantTypeKind.STRING);
+ }
+ return (Const.StringValue) value;
+ }
+
+ private Const.StringValue toString(int position, Value value) {
+ String result;
+ switch (value.constantTypeKind()) {
+ case CHAR:
+ result = String.valueOf(((Const.CharValue) value).value());
+ break;
+ case SHORT:
+ result = String.valueOf(((Const.ShortValue) value).value());
+ break;
+ case INT:
+ result = String.valueOf(((Const.IntValue) value).value());
+ break;
case LONG:
- return value.asLong();
+ result = String.valueOf(((Const.LongValue) value).value());
+ break;
+ case FLOAT:
+ result = String.valueOf(((Const.FloatValue) value).value());
+ break;
+ case DOUBLE:
+ result = String.valueOf(((Const.DoubleValue) value).value());
+ break;
+ case BOOLEAN:
+ result = String.valueOf(((Const.BooleanValue) value).value());
+ break;
+ case BYTE:
+ result = String.valueOf(((Const.ByteValue) value).value());
+ break;
+ case STRING:
+ return (StringValue) value;
+ default:
+ throw typeError(position, value, TurbineConstantTypeKind.STRING);
+ }
+ return new Const.StringValue(result);
+ }
+
+ private Const.CharValue asChar(int position, Value value) {
+ char result;
+ switch (value.constantTypeKind()) {
+ case CHAR:
+ return (Const.CharValue) value;
+ case BYTE:
+ result = (char) ((Const.ByteValue) value).value();
+ break;
+ case SHORT:
+ result = (char) ((Const.ShortValue) value).value();
+ break;
+ case INT:
+ result = (char) ((Const.IntValue) value).value();
+ break;
+ case LONG:
+ result = (char) ((Const.LongValue) value).value();
+ break;
+ case FLOAT:
+ result = (char) ((Const.FloatValue) value).value();
+ break;
+ case DOUBLE:
+ result = (char) ((Const.DoubleValue) value).value();
+ break;
+ default:
+ throw typeError(position, value, TurbineConstantTypeKind.CHAR);
+ }
+ return new Const.CharValue(result);
+ }
+
+ private Const.ByteValue asByte(int position, Value value) {
+ byte result;
+ switch (value.constantTypeKind()) {
+ case CHAR:
+ result = (byte) ((Const.CharValue) value).value();
+ break;
+ case BYTE:
+ return (Const.ByteValue) value;
+ case SHORT:
+ result = (byte) ((Const.ShortValue) value).value();
+ break;
+ case INT:
+ result = (byte) ((Const.IntValue) value).value();
+ break;
+ case LONG:
+ result = (byte) ((Const.LongValue) value).value();
+ break;
+ case FLOAT:
+ result = (byte) ((Const.FloatValue) value).value();
+ break;
+ case DOUBLE:
+ result = (byte) ((Const.DoubleValue) value).value();
+ break;
+ default:
+ throw typeError(position, value, TurbineConstantTypeKind.BYTE);
+ }
+ return new Const.ByteValue(result);
+ }
+
+ private Const.ShortValue asShort(int position, Value value) {
+ short result;
+ switch (value.constantTypeKind()) {
+ case CHAR:
+ result = (short) ((Const.CharValue) value).value();
+ break;
+ case BYTE:
+ result = ((Const.ByteValue) value).value();
+ break;
+ case SHORT:
+ return (Const.ShortValue) value;
+ case INT:
+ result = (short) ((Const.IntValue) value).value();
+ break;
+ case LONG:
+ result = (short) ((Const.LongValue) value).value();
+ break;
+ case FLOAT:
+ result = (short) ((Const.FloatValue) value).value();
+ break;
+ case DOUBLE:
+ result = (short) ((Const.DoubleValue) value).value();
+ break;
+ default:
+ throw typeError(position, value, TurbineConstantTypeKind.SHORT);
+ }
+ return new Const.ShortValue(result);
+ }
+
+ private Const.IntValue asInt(int position, Value value) {
+ int result;
+ switch (value.constantTypeKind()) {
+ case CHAR:
+ result = ((CharValue) value).value();
+ break;
+ case BYTE:
+ result = ((Const.ByteValue) value).value();
+ break;
+ case SHORT:
+ result = ((Const.ShortValue) value).value();
+ break;
case INT:
- return value.asInteger();
+ return (Const.IntValue) value;
+ case LONG:
+ result = (int) ((Const.LongValue) value).value();
+ break;
+ case FLOAT:
+ result = (int) ((Const.FloatValue) value).value();
+ break;
+ case DOUBLE:
+ result = (int) ((Const.DoubleValue) value).value();
+ break;
+ default:
+ throw typeError(position, value, TurbineConstantTypeKind.INT);
+ }
+ return new Const.IntValue(result);
+ }
+
+ private Const.LongValue asLong(int position, Value value) {
+ long result;
+ switch (value.constantTypeKind()) {
+ case CHAR:
+ result = ((CharValue) value).value();
+ break;
case BYTE:
- return value.asByte();
+ result = ((Const.ByteValue) value).value();
+ break;
+ case SHORT:
+ result = ((Const.ShortValue) value).value();
+ break;
+ case INT:
+ result = ((Const.IntValue) value).value();
+ break;
+ case LONG:
+ return (Const.LongValue) value;
+ case FLOAT:
+ result = (long) ((Const.FloatValue) value).value();
+ break;
+ case DOUBLE:
+ result = (long) ((Const.DoubleValue) value).value();
+ break;
+ default:
+ throw typeError(position, value, TurbineConstantTypeKind.LONG);
+ }
+ return new Const.LongValue(result);
+ }
+
+ private Const.FloatValue asFloat(int position, Value value) {
+ float result;
+ switch (value.constantTypeKind()) {
case CHAR:
- return value.asChar();
+ result = ((CharValue) value).value();
+ break;
+ case BYTE:
+ result = ((Const.ByteValue) value).value();
+ break;
case SHORT:
- return value.asShort();
+ result = ((Const.ShortValue) value).value();
+ break;
+ case INT:
+ result = (float) ((Const.IntValue) value).value();
+ break;
+ case LONG:
+ result = (float) ((Const.LongValue) value).value();
+ break;
+ case FLOAT:
+ return (FloatValue) value;
case DOUBLE:
- return value.asDouble();
+ result = (float) ((Const.DoubleValue) value).value();
+ break;
+ default:
+ throw typeError(position, value, TurbineConstantTypeKind.FLOAT);
+ }
+ return new Const.FloatValue(result);
+ }
+
+ private Const.DoubleValue asDouble(int position, Value value) {
+ double result;
+ switch (value.constantTypeKind()) {
+ case CHAR:
+ result = ((CharValue) value).value();
+ break;
+ case BYTE:
+ result = ((Const.ByteValue) value).value();
+ break;
+ case SHORT:
+ result = ((Const.ShortValue) value).value();
+ break;
+ case INT:
+ result = ((Const.IntValue) value).value();
+ break;
+ case LONG:
+ result = (double) ((Const.LongValue) value).value();
+ break;
case FLOAT:
- return value.asFloat();
+ result = ((Const.FloatValue) value).value();
+ break;
+ case DOUBLE:
+ return (DoubleValue) value;
default:
- throw new AssertionError(kind);
+ throw typeError(position, value, TurbineConstantTypeKind.DOUBLE);
}
+ return new Const.DoubleValue(result);
}
- private Const.Value evalValue(Expression tree) {
+ private @Nullable Value evalValue(Expression tree) {
Const result = eval(tree);
// TODO(cushon): consider distinguishing between constant field and annotation values,
// and only allowing class literals / enum constants in the latter
- return (result instanceof Const.Value) ? (Const.Value) result : null;
+ return (result instanceof Value) ? (Value) result : null;
}
- private Const.Value evalConditional(Conditional t) {
- Const.Value condition = evalValue(t.cond());
+ private @Nullable Value evalConditional(Conditional t) {
+ Value condition = evalValue(t.cond());
if (condition == null) {
return null;
}
- return condition.asBoolean().value() ? evalValue(t.iftrue()) : evalValue(t.iffalse());
+ return asBoolean(t.position(), condition).value()
+ ? evalValue(t.iftrue())
+ : evalValue(t.iffalse());
}
- private Const.Value evalUnary(Unary t) {
- Const.Value expr = evalValue(t.expr());
+ private @Nullable Value evalUnary(Unary t) {
+ Value expr = evalValue(t.expr());
if (expr == null) {
return null;
}
@@ -386,67 +654,67 @@ public strictfp class ConstEvaluator {
}
}
- private Value unaryNegate(int position, Value expr) {
+ private @Nullable Value unaryNegate(int position, Value expr) {
switch (expr.constantTypeKind()) {
case BOOLEAN:
- return new Const.BooleanValue(!expr.asBoolean().value());
+ return new Const.BooleanValue(!asBoolean(position, expr).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, expr.constantTypeKind());
}
}
- private Value bitwiseComp(int position, Value expr) {
+ private @Nullable Value bitwiseComp(int position, Value expr) {
expr = promoteUnary(position, expr);
switch (expr.constantTypeKind()) {
case INT:
- return new Const.IntValue(~expr.asInteger().value());
+ return new Const.IntValue(~asInt(position, expr).value());
case LONG:
- return new Const.LongValue(~expr.asLong().value());
+ return new Const.LongValue(~asLong(position, expr).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, expr.constantTypeKind());
}
}
- private Value unaryPlus(int position, Value expr) {
+ private @Nullable Value unaryPlus(int position, Value expr) {
expr = promoteUnary(position, expr);
switch (expr.constantTypeKind()) {
case INT:
- return new Const.IntValue(+expr.asInteger().value());
+ return new Const.IntValue(+asInt(position, expr).value());
case LONG:
- return new Const.LongValue(+expr.asLong().value());
+ return new Const.LongValue(+asLong(position, expr).value());
case FLOAT:
- return new Const.FloatValue(+expr.asFloat().value());
+ return new Const.FloatValue(+asFloat(position, expr).value());
case DOUBLE:
- return new Const.DoubleValue(+expr.asDouble().value());
+ return new Const.DoubleValue(+asDouble(position, expr).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, expr.constantTypeKind());
}
}
- private Value unaryMinus(int position, Value expr) {
+ private @Nullable Value unaryMinus(int position, Value expr) {
expr = promoteUnary(position, expr);
switch (expr.constantTypeKind()) {
case INT:
- return new Const.IntValue(-expr.asInteger().value());
+ return new Const.IntValue(-asInt(position, expr).value());
case LONG:
- return new Const.LongValue(-expr.asLong().value());
+ return new Const.LongValue(-asLong(position, expr).value());
case FLOAT:
- return new Const.FloatValue(-expr.asFloat().value());
+ return new Const.FloatValue(-asFloat(position, expr).value());
case DOUBLE:
- return new Const.DoubleValue(-expr.asDouble().value());
+ return new Const.DoubleValue(-asDouble(position, expr).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, expr.constantTypeKind());
}
}
- private Const.Value evalCast(TypeCast t) {
- Const.Value expr = evalValue(t.expr());
+ private @Nullable Value evalCast(TypeCast t) {
+ Value expr = evalValue(t.expr());
if (expr == null) {
return null;
}
switch (t.ty().kind()) {
case PRIM_TY:
- return coerce(expr, ((Tree.PrimTy) t.ty()).tykind());
+ return coerce(t.expr().position(), expr, ((Tree.PrimTy) t.ty()).tykind());
case CLASS_TY:
{
ClassTy classTy = (ClassTy) t.ty();
@@ -455,102 +723,102 @@ public strictfp class ConstEvaluator {
// Explicit boxing cases (e.g. `(Boolean) false`) are legal, but not const exprs.
return null;
}
- return expr.asString();
+ return toString(t.expr().position(), expr);
}
default:
throw new AssertionError(t.ty().kind());
}
}
- private Const.Value add(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value add(int position, Value a, Value b) {
if (a.constantTypeKind() == TurbineConstantTypeKind.STRING
|| b.constantTypeKind() == TurbineConstantTypeKind.STRING) {
- return new Const.StringValue(a.asString().value() + b.asString().value());
+ return new Const.StringValue(toString(position, a).value() + toString(position, b).value());
}
TurbineConstantTypeKind type = promoteBinary(position, a, b);
- a = coerce(a, type);
- b = coerce(b, type);
+ a = coerce(position, a, type);
+ b = coerce(position, b, type);
switch (type) {
case INT:
- return new Const.IntValue(a.asInteger().value() + b.asInteger().value());
+ return new Const.IntValue(asInt(position, a).value() + asInt(position, b).value());
case LONG:
- return new Const.LongValue(a.asLong().value() + b.asLong().value());
+ return new Const.LongValue(asLong(position, a).value() + asLong(position, b).value());
case FLOAT:
- return new Const.FloatValue(a.asFloat().value() + b.asFloat().value());
+ return new Const.FloatValue(asFloat(position, a).value() + asFloat(position, b).value());
case DOUBLE:
- return new Const.DoubleValue(a.asDouble().value() + b.asDouble().value());
+ return new Const.DoubleValue(asDouble(position, a).value() + asDouble(position, b).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- private Const.Value subtract(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value subtract(int position, Value a, Value b) {
TurbineConstantTypeKind type = promoteBinary(position, a, b);
- a = coerce(a, type);
- b = coerce(b, type);
+ a = coerce(position, a, type);
+ b = coerce(position, b, type);
switch (type) {
case INT:
- return new Const.IntValue(a.asInteger().value() - b.asInteger().value());
+ return new Const.IntValue(asInt(position, a).value() - asInt(position, b).value());
case LONG:
- return new Const.LongValue(a.asLong().value() - b.asLong().value());
+ return new Const.LongValue(asLong(position, a).value() - asLong(position, b).value());
case FLOAT:
- return new Const.FloatValue(a.asFloat().value() - b.asFloat().value());
+ return new Const.FloatValue(asFloat(position, a).value() - asFloat(position, b).value());
case DOUBLE:
- return new Const.DoubleValue(a.asDouble().value() - b.asDouble().value());
+ return new Const.DoubleValue(asDouble(position, a).value() - asDouble(position, b).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- private Const.Value mult(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value mult(int position, Value a, Value b) {
TurbineConstantTypeKind type = promoteBinary(position, a, b);
- a = coerce(a, type);
- b = coerce(b, type);
+ a = coerce(position, a, type);
+ b = coerce(position, b, type);
switch (type) {
case INT:
- return new Const.IntValue(a.asInteger().value() * b.asInteger().value());
+ return new Const.IntValue(asInt(position, a).value() * asInt(position, b).value());
case LONG:
- return new Const.LongValue(a.asLong().value() * b.asLong().value());
+ return new Const.LongValue(asLong(position, a).value() * asLong(position, b).value());
case FLOAT:
- return new Const.FloatValue(a.asFloat().value() * b.asFloat().value());
+ return new Const.FloatValue(asFloat(position, a).value() * asFloat(position, b).value());
case DOUBLE:
- return new Const.DoubleValue(a.asDouble().value() * b.asDouble().value());
+ return new Const.DoubleValue(asDouble(position, a).value() * asDouble(position, b).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- private Const.Value divide(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value divide(int position, Value a, Value b) {
TurbineConstantTypeKind type = promoteBinary(position, a, b);
- a = coerce(a, type);
- b = coerce(b, type);
+ a = coerce(position, a, type);
+ b = coerce(position, b, type);
switch (type) {
case INT:
- return new Const.IntValue(a.asInteger().value() / b.asInteger().value());
+ return new Const.IntValue(asInt(position, a).value() / asInt(position, b).value());
case LONG:
- return new Const.LongValue(a.asLong().value() / b.asLong().value());
+ return new Const.LongValue(asLong(position, a).value() / asLong(position, b).value());
case FLOAT:
- return new Const.FloatValue(a.asFloat().value() / b.asFloat().value());
+ return new Const.FloatValue(asFloat(position, a).value() / asFloat(position, b).value());
case DOUBLE:
- return new Const.DoubleValue(a.asDouble().value() / b.asDouble().value());
+ return new Const.DoubleValue(asDouble(position, a).value() / asDouble(position, b).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- private Const.Value mod(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value mod(int position, Value a, Value b) {
TurbineConstantTypeKind type = promoteBinary(position, a, b);
- a = coerce(a, type);
- b = coerce(b, type);
+ a = coerce(position, a, type);
+ b = coerce(position, b, type);
switch (type) {
case INT:
- return new Const.IntValue(a.asInteger().value() % b.asInteger().value());
+ return new Const.IntValue(asInt(position, a).value() % asInt(position, b).value());
case LONG:
- return new Const.LongValue(a.asLong().value() % b.asLong().value());
+ return new Const.LongValue(asLong(position, a).value() % asLong(position, b).value());
case FLOAT:
- return new Const.FloatValue(a.asFloat().value() % b.asFloat().value());
+ return new Const.FloatValue(asFloat(position, a).value() % asFloat(position, b).value());
case DOUBLE:
- return new Const.DoubleValue(a.asDouble().value() % b.asDouble().value());
+ return new Const.DoubleValue(asDouble(position, a).value() % asDouble(position, b).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, type);
}
@@ -560,289 +828,319 @@ public strictfp class ConstEvaluator {
private static final int LONG_SHIFT_MASK = 0b111111;
- private Const.Value shiftLeft(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value shiftLeft(int position, Value a, Value b) {
a = promoteUnary(position, a);
b = promoteUnary(position, b);
switch (a.constantTypeKind()) {
case INT:
return new Const.IntValue(
- a.asInteger().value() << (b.asInteger().value() & INT_SHIFT_MASK));
+ asInt(position, a).value() << (asInt(position, b).value() & INT_SHIFT_MASK));
case LONG:
- return new Const.LongValue(a.asLong().value() << (b.asInteger().value() & LONG_SHIFT_MASK));
+ return new Const.LongValue(
+ asLong(position, a).value() << (asInt(position, b).value() & LONG_SHIFT_MASK));
default:
throw error(position, ErrorKind.OPERAND_TYPE, a.constantTypeKind());
}
}
- private Const.Value shiftRight(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value shiftRight(int position, Value a, Value b) {
a = promoteUnary(position, a);
b = promoteUnary(position, b);
switch (a.constantTypeKind()) {
case INT:
return new Const.IntValue(
- a.asInteger().value() >> (b.asInteger().value() & INT_SHIFT_MASK));
+ asInt(position, a).value() >> (asInt(position, b).value() & INT_SHIFT_MASK));
case LONG:
- return new Const.LongValue(a.asLong().value() >> (b.asInteger().value() & LONG_SHIFT_MASK));
+ return new Const.LongValue(
+ asLong(position, a).value() >> (asInt(position, b).value() & LONG_SHIFT_MASK));
default:
throw error(position, ErrorKind.OPERAND_TYPE, a.constantTypeKind());
}
}
- private Const.Value unsignedShiftRight(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value unsignedShiftRight(int position, Value a, Value b) {
a = promoteUnary(position, a);
b = promoteUnary(position, b);
switch (a.constantTypeKind()) {
case INT:
return new Const.IntValue(
- a.asInteger().value() >>> (b.asInteger().value() & INT_SHIFT_MASK));
+ asInt(position, a).value() >>> (asInt(position, b).value() & INT_SHIFT_MASK));
case LONG:
return new Const.LongValue(
- a.asLong().value() >>> (b.asInteger().value() & LONG_SHIFT_MASK));
+ asLong(position, a).value() >>> (asInt(position, b).value() & LONG_SHIFT_MASK));
default:
throw error(position, ErrorKind.OPERAND_TYPE, a.constantTypeKind());
}
}
- private Const.Value lessThan(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value lessThan(int position, Value a, Value b) {
TurbineConstantTypeKind type = promoteBinary(position, a, b);
- a = coerce(a, type);
- b = coerce(b, type);
+ a = coerce(position, a, type);
+ b = coerce(position, b, type);
switch (type) {
case INT:
- return new Const.BooleanValue(a.asInteger().value() < b.asInteger().value());
+ return new Const.BooleanValue(asInt(position, a).value() < asInt(position, b).value());
case LONG:
- return new Const.BooleanValue(a.asLong().value() < b.asLong().value());
+ return new Const.BooleanValue(asLong(position, a).value() < asLong(position, b).value());
case FLOAT:
- return new Const.BooleanValue(a.asFloat().value() < b.asFloat().value());
+ return new Const.BooleanValue(asFloat(position, a).value() < asFloat(position, b).value());
case DOUBLE:
- return new Const.BooleanValue(a.asDouble().value() < b.asDouble().value());
+ return new Const.BooleanValue(
+ asDouble(position, a).value() < asDouble(position, b).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- private Const.Value lessThanEqual(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value lessThanEqual(int position, Value a, Value b) {
TurbineConstantTypeKind type = promoteBinary(position, a, b);
- a = coerce(a, type);
- b = coerce(b, type);
+ a = coerce(position, a, type);
+ b = coerce(position, b, type);
switch (type) {
case INT:
- return new Const.BooleanValue(a.asInteger().value() <= b.asInteger().value());
+ return new Const.BooleanValue(asInt(position, a).value() <= asInt(position, b).value());
case LONG:
- return new Const.BooleanValue(a.asLong().value() <= b.asLong().value());
+ return new Const.BooleanValue(asLong(position, a).value() <= asLong(position, b).value());
case FLOAT:
- return new Const.BooleanValue(a.asFloat().value() <= b.asFloat().value());
+ return new Const.BooleanValue(asFloat(position, a).value() <= asFloat(position, b).value());
case DOUBLE:
- return new Const.BooleanValue(a.asDouble().value() <= b.asDouble().value());
+ return new Const.BooleanValue(
+ asDouble(position, a).value() <= asDouble(position, b).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- private Const.Value greaterThan(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value greaterThan(int position, Value a, Value b) {
TurbineConstantTypeKind type = promoteBinary(position, a, b);
- a = coerce(a, type);
- b = coerce(b, type);
+ a = coerce(position, a, type);
+ b = coerce(position, b, type);
switch (type) {
case INT:
- return new Const.BooleanValue(a.asInteger().value() > b.asInteger().value());
+ return new Const.BooleanValue(asInt(position, a).value() > asInt(position, b).value());
case LONG:
- return new Const.BooleanValue(a.asLong().value() > b.asLong().value());
+ return new Const.BooleanValue(asLong(position, a).value() > asLong(position, b).value());
case FLOAT:
- return new Const.BooleanValue(a.asFloat().value() > b.asFloat().value());
+ return new Const.BooleanValue(asFloat(position, a).value() > asFloat(position, b).value());
case DOUBLE:
- return new Const.BooleanValue(a.asDouble().value() > b.asDouble().value());
+ return new Const.BooleanValue(
+ asDouble(position, a).value() > asDouble(position, b).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- private Const.Value greaterThanEqual(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value greaterThanEqual(int position, Value a, Value b) {
TurbineConstantTypeKind type = promoteBinary(position, a, b);
- a = coerce(a, type);
- b = coerce(b, type);
+ a = coerce(position, a, type);
+ b = coerce(position, b, type);
switch (type) {
case INT:
- return new Const.BooleanValue(a.asInteger().value() >= b.asInteger().value());
+ return new Const.BooleanValue(asInt(position, a).value() >= asInt(position, b).value());
case LONG:
- return new Const.BooleanValue(a.asLong().value() >= b.asLong().value());
+ return new Const.BooleanValue(asLong(position, a).value() >= asLong(position, b).value());
case FLOAT:
- return new Const.BooleanValue(a.asFloat().value() >= b.asFloat().value());
+ return new Const.BooleanValue(asFloat(position, a).value() >= asFloat(position, b).value());
case DOUBLE:
- return new Const.BooleanValue(a.asDouble().value() >= b.asDouble().value());
+ return new Const.BooleanValue(
+ asDouble(position, a).value() >= asDouble(position, b).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- private Const.Value equal(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value equal(int position, Value a, Value b) {
switch (a.constantTypeKind()) {
case STRING:
- return new Const.BooleanValue(a.asString().value().equals(b.asString().value()));
+ return new Const.BooleanValue(
+ asString(position, a).value().equals(asString(position, b).value()));
case BOOLEAN:
- return new Const.BooleanValue(a.asBoolean().value() == b.asBoolean().value());
+ return new Const.BooleanValue(
+ asBoolean(position, a).value() == asBoolean(position, b).value());
default:
break;
}
TurbineConstantTypeKind type = promoteBinary(position, a, b);
- a = coerce(a, type);
- b = coerce(b, type);
+ a = coerce(position, a, type);
+ b = coerce(position, b, type);
switch (type) {
case INT:
- return new Const.BooleanValue(a.asInteger().value() == b.asInteger().value());
+ return new Const.BooleanValue(asInt(position, a).value() == asInt(position, b).value());
case LONG:
- return new Const.BooleanValue(a.asLong().value() == b.asLong().value());
+ return new Const.BooleanValue(asLong(position, a).value() == asLong(position, b).value());
case FLOAT:
- return new Const.BooleanValue(a.asFloat().value() == b.asFloat().value());
+ return new Const.BooleanValue(asFloat(position, a).value() == asFloat(position, b).value());
case DOUBLE:
- return new Const.BooleanValue(a.asDouble().value() == b.asDouble().value());
+ return new Const.BooleanValue(
+ asDouble(position, a).value() == asDouble(position, b).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- private Const.Value notEqual(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value notEqual(int position, Value a, Value b) {
switch (a.constantTypeKind()) {
case STRING:
- return new Const.BooleanValue(!a.asString().value().equals(b.asString().value()));
+ return new Const.BooleanValue(
+ !asString(position, a).value().equals(asString(position, b).value()));
case BOOLEAN:
- return new Const.BooleanValue(a.asBoolean().value() != b.asBoolean().value());
+ return new Const.BooleanValue(
+ asBoolean(position, a).value() != asBoolean(position, b).value());
default:
break;
}
TurbineConstantTypeKind type = promoteBinary(position, a, b);
- a = coerce(a, type);
- b = coerce(b, type);
+ a = coerce(position, a, type);
+ b = coerce(position, b, type);
switch (type) {
case INT:
- return new Const.BooleanValue(a.asInteger().value() != b.asInteger().value());
+ return new Const.BooleanValue(asInt(position, a).value() != asInt(position, b).value());
case LONG:
- return new Const.BooleanValue(a.asLong().value() != b.asLong().value());
+ return new Const.BooleanValue(asLong(position, a).value() != asLong(position, b).value());
case FLOAT:
- return new Const.BooleanValue(a.asFloat().value() != b.asFloat().value());
+ return new Const.BooleanValue(asFloat(position, a).value() != asFloat(position, b).value());
case DOUBLE:
- return new Const.BooleanValue(a.asDouble().value() != b.asDouble().value());
+ return new Const.BooleanValue(
+ asDouble(position, a).value() != asDouble(position, b).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- private Const.Value bitwiseAnd(int position, Const.Value a, Const.Value b) {
+ private Value bitwiseAnd(int position, Value a, Value b) {
switch (a.constantTypeKind()) {
case BOOLEAN:
- return new Const.BooleanValue(a.asBoolean().value() & b.asBoolean().value());
+ return new Const.BooleanValue(
+ asBoolean(position, a).value() & asBoolean(position, b).value());
default:
break;
}
TurbineConstantTypeKind type = promoteBinary(position, a, b);
- a = coerce(a, type);
- b = coerce(b, type);
+ a = coerce(position, a, type);
+ b = coerce(position, b, type);
switch (type) {
case INT:
- return new Const.IntValue(a.asInteger().value() & b.asInteger().value());
+ return new Const.IntValue(asInt(position, a).value() & asInt(position, b).value());
case LONG:
- return new Const.LongValue(a.asLong().value() & b.asLong().value());
+ return new Const.LongValue(asLong(position, a).value() & asLong(position, b).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- private Const.Value bitwiseOr(int position, Const.Value a, Const.Value b) {
+ private Value bitwiseOr(int position, Value a, Value b) {
switch (a.constantTypeKind()) {
case BOOLEAN:
- return new Const.BooleanValue(a.asBoolean().value() | b.asBoolean().value());
+ return new Const.BooleanValue(
+ asBoolean(position, a).value() | asBoolean(position, b).value());
default:
break;
}
TurbineConstantTypeKind type = promoteBinary(position, a, b);
- a = coerce(a, type);
- b = coerce(b, type);
+ a = coerce(position, a, type);
+ b = coerce(position, b, type);
switch (type) {
case INT:
- return new Const.IntValue(a.asInteger().value() | b.asInteger().value());
+ return new Const.IntValue(asInt(position, a).value() | asInt(position, b).value());
case LONG:
- return new Const.LongValue(a.asLong().value() | b.asLong().value());
+ return new Const.LongValue(asLong(position, a).value() | asLong(position, b).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- private Const.Value bitwiseXor(int position, Const.Value a, Const.Value b) {
+ private @Nullable Value bitwiseXor(int position, Value a, Value b) {
switch (a.constantTypeKind()) {
case BOOLEAN:
- return new Const.BooleanValue(a.asBoolean().value() ^ b.asBoolean().value());
+ return new Const.BooleanValue(
+ asBoolean(position, a).value() ^ asBoolean(position, b).value());
default:
break;
}
TurbineConstantTypeKind type = promoteBinary(position, a, b);
- a = coerce(a, type);
- b = coerce(b, type);
+ a = coerce(position, a, type);
+ b = coerce(position, b, type);
switch (type) {
case INT:
- return new Const.IntValue(a.asInteger().value() ^ b.asInteger().value());
+ return new Const.IntValue(asInt(position, a).value() ^ asInt(position, b).value());
case LONG:
- return new Const.LongValue(a.asLong().value() ^ b.asLong().value());
+ return new Const.LongValue(asLong(position, a).value() ^ asLong(position, b).value());
default:
throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- private Const.Value evalBinary(Binary t) {
- Const.Value lhs = evalValue(t.lhs());
- Const.Value rhs = evalValue(t.rhs());
- if (lhs == null || rhs == null) {
- return null;
+ private @Nullable Value evalBinary(Binary t) {
+ Value result = null;
+ boolean first = true;
+ for (Expression child : t.children()) {
+ Value value = evalValue(child);
+ if (value == null) {
+ return null;
+ }
+ if (first) {
+ result = value;
+ } else {
+ result = evalBinary(child.position(), t.op(), requireNonNull(result), value);
+ }
+ first = false;
}
- switch (t.op()) {
+ return result;
+ }
+
+ private @Nullable Value evalBinary(int position, TurbineOperatorKind op, Value lhs, Value rhs) {
+ switch (op) {
case PLUS:
- return add(t.position(), lhs, rhs);
+ return add(position, lhs, rhs);
case MINUS:
- return subtract(t.position(), lhs, rhs);
+ return subtract(position, lhs, rhs);
case MULT:
- return mult(t.position(), lhs, rhs);
+ return mult(position, lhs, rhs);
case DIVIDE:
- return divide(t.position(), lhs, rhs);
+ return divide(position, lhs, rhs);
case MODULO:
- return mod(t.position(), lhs, rhs);
+ return mod(position, lhs, rhs);
case SHIFT_LEFT:
- return shiftLeft(t.position(), lhs, rhs);
+ return shiftLeft(position, lhs, rhs);
case SHIFT_RIGHT:
- return shiftRight(t.position(), lhs, rhs);
+ return shiftRight(position, lhs, rhs);
case UNSIGNED_SHIFT_RIGHT:
- return unsignedShiftRight(t.position(), lhs, rhs);
+ return unsignedShiftRight(position, lhs, rhs);
case LESS_THAN:
- return lessThan(t.position(), lhs, rhs);
+ return lessThan(position, lhs, rhs);
case GREATER_THAN:
- return greaterThan(t.position(), lhs, rhs);
+ return greaterThan(position, lhs, rhs);
case LESS_THAN_EQ:
- return lessThanEqual(t.position(), lhs, rhs);
+ return lessThanEqual(position, lhs, rhs);
case GREATER_THAN_EQ:
- return greaterThanEqual(t.position(), lhs, rhs);
+ return greaterThanEqual(position, lhs, rhs);
case EQUAL:
- return equal(t.position(), lhs, rhs);
+ return equal(position, lhs, rhs);
case NOT_EQUAL:
- return notEqual(t.position(), lhs, rhs);
+ return notEqual(position, lhs, rhs);
case AND:
- return new Const.BooleanValue(lhs.asBoolean().value() && rhs.asBoolean().value());
+ return new Const.BooleanValue(
+ asBoolean(position, lhs).value() && asBoolean(position, rhs).value());
case OR:
- return new Const.BooleanValue(lhs.asBoolean().value() || rhs.asBoolean().value());
+ return new Const.BooleanValue(
+ asBoolean(position, lhs).value() || asBoolean(position, rhs).value());
case BITWISE_AND:
- return bitwiseAnd(t.position(), lhs, rhs);
+ return bitwiseAnd(position, lhs, rhs);
case BITWISE_XOR:
- return bitwiseXor(t.position(), lhs, rhs);
+ return bitwiseXor(position, lhs, rhs);
case BITWISE_OR:
- return bitwiseOr(t.position(), lhs, rhs);
+ return bitwiseOr(position, lhs, rhs);
default:
- throw new AssertionError(t.op());
+ throw new AssertionError(op);
}
}
- private Const.Value promoteUnary(int position, Value v) {
+ private Value promoteUnary(int position, Value v) {
switch (v.constantTypeKind()) {
case CHAR:
case SHORT:
case BYTE:
- return v.asInteger();
+ return asInt(position, v);
case INT:
case LONG:
case FLOAT:
@@ -853,7 +1151,7 @@ public strictfp class ConstEvaluator {
}
}
- private TurbineConstantTypeKind promoteBinary(int position, Const.Value a, Const.Value b) {
+ private TurbineConstantTypeKind promoteBinary(int position, Value a, Value b) {
a = promoteUnary(position, a);
b = promoteUnary(position, b);
switch (a.constantTypeKind()) {
@@ -921,7 +1219,7 @@ public strictfp class ConstEvaluator {
if (info.sym() == null) {
return info;
}
- TypeBoundClass annoClass = env.get(info.sym());
+ TypeBoundClass annoClass = env.getNonNull(info.sym());
if (annoClass.kind() != TurbineTyKind.ANNOTATION) {
// we've already reported an error for non-annotation symbols used as annotations,
// skip error handling for annotation arguments
@@ -943,6 +1241,9 @@ public strictfp class ConstEvaluator {
key = assign.name().value();
expr = assign.expr();
} else {
+ if (info.args().size() != 1) {
+ throw error(arg.position(), ErrorKind.ANNOTATION_VALUE_NAME);
+ }
// expand the implicit 'value' name; `@Foo(42)` is sugar for `@Foo(value=42)`
key = "value";
expr = arg;
@@ -968,13 +1269,14 @@ public strictfp class ConstEvaluator {
}
for (MethodInfo methodInfo : template.values()) {
if (!methodInfo.hasDefaultValue()) {
- log.error(info.tree().position(), ErrorKind.MISSING_ANNOTATION_ARGUMENT, methodInfo.name());
+ throw error(
+ info.tree().position(), ErrorKind.MISSING_ANNOTATION_ARGUMENT, methodInfo.name());
}
}
return info.withValues(ImmutableMap.copyOf(values));
}
- private TurbineAnnotationValue evalAnno(Tree.Anno t) {
+ private @Nullable TurbineAnnotationValue evalAnno(Tree.Anno t) {
LookupResult result = scope.lookup(new LookupKey(t.name()));
if (result == null) {
log.error(
@@ -991,14 +1293,14 @@ public strictfp class ConstEvaluator {
if (sym == null) {
return null;
}
- if (env.get(sym).kind() != TurbineTyKind.ANNOTATION) {
+ if (env.getNonNull(sym).kind() != TurbineTyKind.ANNOTATION) {
log.error(t.position(), ErrorKind.NOT_AN_ANNOTATION, sym);
}
AnnoInfo annoInfo = evaluateAnnotation(new AnnoInfo(source, sym, t, ImmutableMap.of()));
return new TurbineAnnotationValue(annoInfo);
}
- private Const.ArrayInitValue evalArrayInit(ArrayInit t) {
+ private @Nullable ArrayInitValue evalArrayInit(ArrayInit t) {
ImmutableList.Builder<Const> elements = ImmutableList.builder();
for (Expression e : t.exprs()) {
Const arg = eval(e);
@@ -1010,6 +1312,7 @@ public strictfp class ConstEvaluator {
return new Const.ArrayInitValue(elements.build());
}
+ @Nullable
Const evalAnnotationValue(Tree tree, Type ty) {
if (ty == null) {
throw error(tree.position(), ErrorKind.EXPRESSION_ERROR);
@@ -1021,10 +1324,10 @@ public strictfp class ConstEvaluator {
}
switch (ty.tyKind()) {
case PRIM_TY:
- if (!(value instanceof Const.Value)) {
+ if (!(value instanceof Value)) {
throw error(tree.position(), ErrorKind.EXPRESSION_ERROR);
}
- return coerce((Const.Value) value, ((Type.PrimTy) ty).primkind());
+ return coerce(tree.position(), (Value) value, ((Type.PrimTy) ty).primkind());
case CLASS_TY:
case TY_VAR:
return value;
@@ -1050,13 +1353,17 @@ public strictfp class ConstEvaluator {
return TurbineError.format(source, position, kind, args);
}
- public Const.Value evalFieldInitializer(Expression expression, Type type) {
+ private TurbineError typeError(int position, Value value, TurbineConstantTypeKind kind) {
+ return error(position, ErrorKind.TYPE_CONVERSION, value, value.constantTypeKind(), kind);
+ }
+
+ public @Nullable Value evalFieldInitializer(Expression expression, Type type) {
try {
Const value = eval(expression);
if (value == null || value.kind() != Const.Kind.PRIMITIVE) {
return null;
}
- return (Const.Value) cast(expression.position(), type, value);
+ return (Value) cast(expression.position(), type, value);
} catch (TurbineError error) {
for (TurbineDiagnostic diagnostic : error.diagnostics()) {
switch (diagnostic.kind()) {
diff --git a/java/com/google/turbine/binder/CtSymClassBinder.java b/java/com/google/turbine/binder/CtSymClassBinder.java
index 1d7ece7..f0e21f2 100644
--- a/java/com/google/turbine/binder/CtSymClassBinder.java
+++ b/java/com/google/turbine/binder/CtSymClassBinder.java
@@ -24,7 +24,6 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
-import com.google.common.primitives.Ints;
import com.google.turbine.binder.bound.ModuleInfo;
import com.google.turbine.binder.bytecode.BytecodeBinder;
import com.google.turbine.binder.bytecode.BytecodeBoundClass;
@@ -36,19 +35,19 @@ import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.ModuleSymbol;
import com.google.turbine.zip.Zip;
import java.io.IOException;
-import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/** Constructs a platform {@link ClassPath} from the current JDK's ct.sym file. */
public final class CtSymClassBinder {
- @Nullable
- public static ClassPath bind(String version) throws IOException {
+ private static final int FEATURE_VERSION = Runtime.version().feature();
+
+ public static @Nullable ClassPath bind(int version) throws IOException {
String javaHome = JAVA_HOME.value();
requireNonNull(javaHome, "attempted to use --release, but JAVA_HOME is not set");
Path ctSym = Paths.get(javaHome).resolve("lib/ct.sym");
@@ -60,7 +59,7 @@ public final class CtSymClassBinder {
Env<ClassSymbol, BytecodeBoundClass> benv =
new Env<ClassSymbol, BytecodeBoundClass>() {
@Override
- public BytecodeBoundClass get(ClassSymbol sym) {
+ public @Nullable BytecodeBoundClass get(ClassSymbol sym) {
return map.get(sym);
}
};
@@ -81,7 +80,7 @@ public final class CtSymClassBinder {
if (!ze.name().substring(0, idx).contains(releaseString)) {
continue;
}
- if (isAtLeastJDK12()) {
+ if (FEATURE_VERSION >= 12) {
// JDK >= 12 includes the module name as a prefix
idx = name.indexOf('/', idx + 1);
}
@@ -118,7 +117,7 @@ public final class CtSymClassBinder {
}
@Override
- public Supplier<byte[]> resource(String input) {
+ public @Nullable Supplier<byte[]> resource(String input) {
return null;
}
};
@@ -135,26 +134,12 @@ public final class CtSymClassBinder {
}
@VisibleForTesting
- static String formatReleaseVersion(String version) {
- Integer n = Ints.tryParse(version);
- if (n == null || n <= 4 || n >= 36) {
- throw new IllegalArgumentException("invalid release version: " + version);
+ static String formatReleaseVersion(int n) {
+ if (n <= 4 || n >= 36) {
+ throw new IllegalArgumentException("invalid release version: " + n);
}
return toUpperCase(Integer.toString(n, 36));
}
- private static boolean isAtLeastJDK12() {
- int major;
- try {
- Method versionMethod = Runtime.class.getMethod("version");
- Object version = versionMethod.invoke(null);
- major = (int) version.getClass().getMethod("major").invoke(version);
- } catch (ReflectiveOperationException e) {
- // `Runtime.version()` was added in JDK 9
- return false;
- }
- return major >= 12;
- }
-
private CtSymClassBinder() {}
}
diff --git a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
index c5de8c1..65c1021 100644
--- a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
+++ b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
@@ -30,6 +30,7 @@ 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.RecordComponentInfo;
import com.google.turbine.binder.env.Env;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.diag.TurbineError;
@@ -70,9 +71,11 @@ public final class DisambiguateTypeAnnotations {
SourceTypeBoundClass base, Env<ClassSymbol, TypeBoundClass> env) {
return new SourceTypeBoundClass(
base.interfaceTypes(),
+ base.permits(),
base.superClassType(),
base.typeParameterTypes(),
base.access(),
+ bindComponents(env, base.components(), TurbineElementType.RECORD_COMPONENT),
bindMethods(env, base.methods()),
bindFields(env, base.fields()),
base.owner(),
@@ -112,36 +115,58 @@ public final class DisambiguateTypeAnnotations {
base.sym(),
base.tyParams(),
returnType,
- bindParameters(env, base.parameters()),
+ bindParameters(env, base.parameters(), TurbineElementType.PARAMETER),
base.exceptions(),
base.access(),
base.defaultValue(),
base.decl(),
declarationAnnotations.build(),
- base.receiver() != null ? bindParam(env, base.receiver()) : null);
+ base.receiver() != null
+ ? bindParam(env, base.receiver(), TurbineElementType.PARAMETER)
+ : null);
}
private static ImmutableList<ParamInfo> bindParameters(
- Env<ClassSymbol, TypeBoundClass> env, ImmutableList<ParamInfo> params) {
+ Env<ClassSymbol, TypeBoundClass> env,
+ ImmutableList<ParamInfo> params,
+ TurbineElementType declarationTarget) {
ImmutableList.Builder<ParamInfo> result = ImmutableList.builder();
for (ParamInfo param : params) {
- result.add(bindParam(env, param));
+ result.add(bindParam(env, param, declarationTarget));
}
return result.build();
}
- private static ParamInfo bindParam(Env<ClassSymbol, TypeBoundClass> env, ParamInfo base) {
+ private static ParamInfo bindParam(
+ Env<ClassSymbol, TypeBoundClass> env, ParamInfo base, TurbineElementType declarationTarget) {
ImmutableList.Builder<AnnoInfo> declarationAnnotations = ImmutableList.builder();
Type type =
disambiguate(
- env,
- TurbineElementType.PARAMETER,
- base.type(),
- base.annotations(),
- declarationAnnotations);
+ env, declarationTarget, base.type(), base.annotations(), declarationAnnotations);
return new ParamInfo(base.sym(), type, declarationAnnotations.build(), base.access());
}
+ private static ImmutableList<RecordComponentInfo> bindComponents(
+ Env<ClassSymbol, TypeBoundClass> env,
+ ImmutableList<RecordComponentInfo> components,
+ TurbineElementType declarationTarget) {
+ ImmutableList.Builder<RecordComponentInfo> result = ImmutableList.builder();
+ for (RecordComponentInfo component : components) {
+ ImmutableList.Builder<AnnoInfo> declarationAnnotations = ImmutableList.builder();
+ Type type =
+ disambiguate(
+ env,
+ declarationTarget,
+ component.type(),
+ component.annotations(),
+ declarationAnnotations);
+ result.add(
+ new RecordComponentInfo(
+ component.sym(), type, declarationAnnotations.build(), component.access()));
+ }
+ return result.build();
+ }
+
/**
* Moves type annotations in {@code annotations} to {@code type}, and adds any declaration
* annotations on {@code type} to {@code declarationAnnotations}.
diff --git a/java/com/google/turbine/binder/FileManagerClassBinder.java b/java/com/google/turbine/binder/FileManagerClassBinder.java
index 42a8162..d36d2d8 100644
--- a/java/com/google/turbine/binder/FileManagerClassBinder.java
+++ b/java/com/google/turbine/binder/FileManagerClassBinder.java
@@ -40,7 +40,7 @@ import javax.tools.FileObject;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/**
* Binds a {@link StandardJavaFileManager} to an {@link ClassPath}. This can be used to share a
@@ -54,7 +54,7 @@ public final class FileManagerClassBinder {
Env<ClassSymbol, BytecodeBoundClass> env =
new Env<ClassSymbol, BytecodeBoundClass>() {
@Override
- public BytecodeBoundClass get(ClassSymbol sym) {
+ public @Nullable BytecodeBoundClass get(ClassSymbol sym) {
return packageLookup.getPackage(this, sym.packageName()).get(sym);
}
};
@@ -77,7 +77,7 @@ public final class FileManagerClassBinder {
}
@Override
- public Supplier<byte[]> resource(String path) {
+ public @Nullable Supplier<byte[]> resource(String path) {
return packageLookup.resource(path);
}
};
@@ -138,7 +138,7 @@ public final class FileManagerClassBinder {
});
}
- public Supplier<byte[]> resource(String resource) {
+ public @Nullable Supplier<byte[]> resource(String resource) {
String dir;
String name;
int idx = resource.lastIndexOf('/');
@@ -203,7 +203,7 @@ public final class FileManagerClassBinder {
}
@Override
- public PackageScope lookupPackage(Iterable<String> names) {
+ public @Nullable PackageScope lookupPackage(Iterable<String> names) {
String packageName = Joiner.on('/').join(names);
Map<ClassSymbol, BytecodeBoundClass> pkg = packageLookup.getPackage(env, packageName);
if (pkg.isEmpty()) {
diff --git a/java/com/google/turbine/binder/HierarchyBinder.java b/java/com/google/turbine/binder/HierarchyBinder.java
index 07d255c..ac2c840 100644
--- a/java/com/google/turbine/binder/HierarchyBinder.java
+++ b/java/com/google/turbine/binder/HierarchyBinder.java
@@ -34,6 +34,7 @@ import com.google.turbine.model.TurbineTyKind;
import com.google.turbine.tree.Tree;
import com.google.turbine.tree.Tree.ClassTy;
import java.util.ArrayDeque;
+import org.jspecify.nullness.Nullable;
/** Type hierarchy binding. */
public class HierarchyBinder {
@@ -82,6 +83,9 @@ public class HierarchyBinder {
case CLASS:
superclass = !origin.equals(ClassSymbol.OBJECT) ? ClassSymbol.OBJECT : null;
break;
+ case RECORD:
+ superclass = ClassSymbol.RECORD;
+ break;
default:
throw new AssertionError(decl.tykind());
}
@@ -110,14 +114,15 @@ public class HierarchyBinder {
typeParameters.put(p.name().value(), new TyVarSymbol(origin, p.name().value()));
}
- return new SourceHeaderBoundClass(base, superclass, interfaces.build(), typeParameters.build());
+ return new SourceHeaderBoundClass(
+ base, superclass, interfaces.build(), typeParameters.buildOrThrow());
}
/**
* Resolves the {@link ClassSymbol} for the given {@link Tree.ClassTy}, with handling for
* non-canonical qualified type names.
*/
- private ClassSymbol resolveClass(Tree.ClassTy ty) {
+ private @Nullable ClassSymbol resolveClass(Tree.ClassTy ty) {
// flatten a left-recursive qualified type name to its component simple names
// e.g. Foo<Bar>.Baz -> ["Foo", "Bar"]
ArrayDeque<Tree.Ident> flat = new ArrayDeque<>();
@@ -142,7 +147,7 @@ public class HierarchyBinder {
return sym;
}
- private ClassSymbol resolveNext(ClassTy ty, ClassSymbol sym, Tree.Ident bit) {
+ private @Nullable ClassSymbol resolveNext(ClassTy ty, ClassSymbol sym, Tree.Ident bit) {
ClassSymbol next;
try {
next = Resolve.resolve(env, origin, sym, bit);
@@ -160,11 +165,11 @@ public class HierarchyBinder {
}
/** Resolve a qualified type name to a symbol. */
- private LookupResult lookup(Tree tree, LookupKey lookup) {
+ private @Nullable LookupResult lookup(Tree tree, LookupKey lookup) {
// Handle any lexically enclosing class declarations (if we're binding a member class).
// We could build out scopes for this, but it doesn't seem worth it. (And sharing the scopes
// with other members of the same enclosing declaration would be complicated.)
- for (ClassSymbol curr = base.owner(); curr != null; curr = env.get(curr).owner()) {
+ for (ClassSymbol curr = base.owner(); curr != null; curr = env.getNonNull(curr).owner()) {
ClassSymbol result;
try {
result = Resolve.resolve(env, origin, curr, lookup.first());
diff --git a/java/com/google/turbine/binder/JimageClassBinder.java b/java/com/google/turbine/binder/JimageClassBinder.java
index d11dda1..53a6a3a 100644
--- a/java/com/google/turbine/binder/JimageClassBinder.java
+++ b/java/com/google/turbine/binder/JimageClassBinder.java
@@ -17,6 +17,7 @@
package com.google.turbine.binder;
import static com.google.common.base.StandardSystemProperty.JAVA_HOME;
+import static java.util.Objects.requireNonNull;
import com.google.common.base.Joiner;
import com.google.common.base.Supplier;
@@ -52,7 +53,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/** Constructs a platform {@link ClassPath} from the current JDK's jimage file using jrtfs. */
public class JimageClassBinder {
@@ -104,11 +105,13 @@ public class JimageClassBinder {
this.modulesRoot = modules;
}
+ @Nullable
Path modulePath(String moduleName) {
Path path = modulesRoot.resolve(moduleName);
return Files.exists(path) ? path : null;
}
+ @Nullable
ModuleInfo module(String moduleName) {
ModuleInfo result = moduleMap.get(moduleName);
if (result == null) {
@@ -134,13 +137,14 @@ public class JimageClassBinder {
Env<ClassSymbol, BytecodeBoundClass> env =
new Env<ClassSymbol, BytecodeBoundClass>() {
@Override
- public BytecodeBoundClass get(ClassSymbol sym) {
+ public @Nullable BytecodeBoundClass get(ClassSymbol sym) {
return JimageClassBinder.this.env.get(sym);
}
};
for (String moduleName : moduleNames) {
if (moduleName != null) {
- Path modulePath = modulePath(moduleName);
+ // TODO(cushon): is this requireNonNull safe?
+ Path modulePath = requireNonNull(modulePath(moduleName), moduleName);
Path modulePackagePath = modulePath.resolve(packageName);
try (DirectoryStream<Path> ds = Files.newDirectoryStream(modulePackagePath)) {
for (Path path : ds) {
@@ -181,9 +185,8 @@ public class JimageClassBinder {
final Scope topLevelScope =
new Scope() {
- @Nullable
@Override
- public LookupResult lookup(LookupKey lookupKey) {
+ public @Nullable LookupResult lookup(LookupKey lookupKey) {
// Find the longest prefix of the key that corresponds to a package name.
// TODO(cushon): SimpleTopLevelIndex uses a prefix map for this, does it matter?
Scope scope = null;
@@ -213,15 +216,14 @@ public class JimageClassBinder {
}
@Override
- public PackageScope lookupPackage(Iterable<String> name) {
+ public @Nullable PackageScope lookupPackage(Iterable<String> name) {
String packageName = Joiner.on('/').join(name);
if (!initPackage(packageName)) {
return null;
}
return new PackageScope() {
- @Nullable
@Override
- public LookupResult lookup(LookupKey lookupKey) {
+ public @Nullable LookupResult lookup(LookupKey lookupKey) {
ClassSymbol sym = packageClassesBySimpleName.get(packageName, lookupKey.first().value());
return sym != null ? new LookupResult(sym, lookupKey) : null;
}
@@ -242,7 +244,7 @@ public class JimageClassBinder {
public Env<ClassSymbol, BytecodeBoundClass> env() {
return new Env<ClassSymbol, BytecodeBoundClass>() {
@Override
- public BytecodeBoundClass get(ClassSymbol sym) {
+ public @Nullable BytecodeBoundClass get(ClassSymbol sym) {
return initPackage(sym.packageName()) ? env.get(sym) : null;
}
};
@@ -252,7 +254,7 @@ public class JimageClassBinder {
public Env<ModuleSymbol, ModuleInfo> moduleEnv() {
return new Env<ModuleSymbol, ModuleInfo>() {
@Override
- public ModuleInfo get(ModuleSymbol module) {
+ public @Nullable ModuleInfo get(ModuleSymbol module) {
return module(module.name());
}
};
@@ -264,7 +266,7 @@ public class JimageClassBinder {
}
@Override
- public Supplier<byte[]> resource(String input) {
+ public @Nullable 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 04ce81d..e88440d 100644
--- a/java/com/google/turbine/binder/ModuleBinder.java
+++ b/java/com/google/turbine/binder/ModuleBinder.java
@@ -16,8 +16,6 @@
package com.google.turbine.binder;
-import static com.google.common.base.Verify.verifyNotNull;
-
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -139,16 +137,16 @@ public class ModuleBinder {
}
}
if (!requiresJavaBase) {
- // everything requires java.base, either explicitly or implicitly
+ // Everything requires java.base, either explicitly or implicitly.
ModuleInfo javaBaseModule = moduleEnv.get(ModuleSymbol.JAVA_BASE);
- verifyNotNull(javaBaseModule, ModuleSymbol.JAVA_BASE.name());
+ // Tolerate a missing java.base module, e.g. when compiling a module against a non-modular
+ // bootclasspath, and just omit the version below.
+ String javaBaseVersion = javaBaseModule != null ? javaBaseModule.version() : null;
requires =
ImmutableList.<RequireInfo>builder()
.add(
new RequireInfo(
- ModuleSymbol.JAVA_BASE.name(),
- TurbineFlag.ACC_MANDATED,
- javaBaseModule.version()))
+ ModuleSymbol.JAVA_BASE.name(), TurbineFlag.ACC_MANDATED, javaBaseVersion))
.addAll(requires.build());
}
diff --git a/java/com/google/turbine/binder/Processing.java b/java/com/google/turbine/binder/Processing.java
index 16407aa..616bf2c 100644
--- a/java/com/google/turbine/binder/Processing.java
+++ b/java/com/google/turbine/binder/Processing.java
@@ -19,7 +19,6 @@ package com.google.turbine.binder;
import static java.util.Objects.requireNonNull;
import com.google.auto.value.AutoValue;
-import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Stopwatch;
@@ -30,7 +29,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Sets;
-import com.google.common.primitives.Ints;
import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.Binder.Statistics;
import com.google.turbine.binder.bound.SourceTypeBoundClass;
@@ -61,7 +59,6 @@ import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -74,13 +71,12 @@ 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;
+import org.jspecify.nullness.Nullable;
/** Top level annotation processing logic, see also {@link Binder}. */
public class Processing {
- @Nullable
- static BindingResult process(
+ static @Nullable BindingResult process(
TurbineLog log,
final ImmutableList<CompUnit> initialSources,
final ClassPath classpath,
@@ -99,10 +95,9 @@ public class Processing {
TurbineFiler filer =
new TurbineFiler(
seen,
- new Function<String, Supplier<byte[]>>() {
- @Nullable
+ new Function<String, @Nullable Supplier<byte[]>>() {
@Override
- public Supplier<byte[]> apply(@Nullable String input) {
+ public @Nullable Supplier<byte[]> apply(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
@@ -277,7 +272,7 @@ public class Processing {
for (Processor processor : processorInfo.processors()) {
result.put(processor, SupportedAnnotationTypes.create(processor));
}
- return result.build();
+ return result.buildOrThrow();
}
@AutoValue
@@ -316,7 +311,7 @@ public class Processing {
Env<ClassSymbol, TypeBoundClass> env, Iterable<ClassSymbol> syms) {
ImmutableSetMultimap.Builder<ClassSymbol, Symbol> result = ImmutableSetMultimap.builder();
for (ClassSymbol sym : syms) {
- TypeBoundClass info = env.get(sym);
+ TypeBoundClass info = env.getNonNull(sym);
for (AnnoInfo annoInfo : info.annotations()) {
if (sym.simpleName().equals("package-info")) {
addAnno(result, annoInfo, sym.owner());
@@ -349,7 +344,7 @@ public class Processing {
// TODO(cushon): consider memoizing this (or isAnnotationInherited) if they show up in profiles
private static ImmutableSet<ClassSymbol> inheritedAnnotations(
- Set<ClassSymbol> seen, ClassSymbol sym, Env<ClassSymbol, TypeBoundClass> env) {
+ Set<ClassSymbol> seen, @Nullable ClassSymbol sym, Env<ClassSymbol, TypeBoundClass> env) {
ImmutableSet.Builder<ClassSymbol> result = ImmutableSet.builder();
ClassSymbol curr = sym;
while (curr != null && seen.add(curr)) {
@@ -394,6 +389,7 @@ public class Processing {
}
public static ProcessorInfo initializeProcessors(
+ SourceVersion sourceVersion,
ImmutableList<String> javacopts,
ImmutableSet<String> processorNames,
ClassLoader processorLoader) {
@@ -402,7 +398,6 @@ public class Processing {
}
ImmutableList<Processor> processors = instantiateProcessors(processorNames, processorLoader);
ImmutableMap<String, String> processorOptions = processorOptions(javacopts);
- SourceVersion sourceVersion = parseSourceVersion(javacopts);
return ProcessorInfo.create(processors, processorLoader, processorOptions, sourceVersion);
}
@@ -429,7 +424,7 @@ public class Processing {
}
return new URLClassLoader(
toUrls(processorPath),
- new ClassLoader(getPlatformClassLoader()) {
+ new ClassLoader(ClassLoader.getPlatformClassLoader()) {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.equals("com.google.turbine.processing.TurbineProcessingEnvironment")) {
@@ -453,54 +448,6 @@ public class Processing {
});
}
- @VisibleForTesting
- static SourceVersion parseSourceVersion(ImmutableList<String> javacopts) {
- SourceVersion sourceVersion = SourceVersion.latestSupported();
- Iterator<String> it = javacopts.iterator();
- while (it.hasNext()) {
- String option = it.next();
- switch (option) {
- case "-source":
- if (!it.hasNext()) {
- throw new IllegalArgumentException("-source requires an argument");
- }
- sourceVersion = parseSourceVersion(it.next());
- break;
- default:
- break;
- }
- }
- return sourceVersion;
- }
-
- private static SourceVersion parseSourceVersion(String value) {
- boolean hasPrefix = value.startsWith("1.");
- Integer version = Ints.tryParse(hasPrefix ? value.substring("1.".length()) : value);
- if (!isValidSourceVersion(version, hasPrefix)) {
- throw new IllegalArgumentException("invalid -source version: " + value);
- }
- try {
- return SourceVersion.valueOf("RELEASE_" + version);
- } catch (IllegalArgumentException unused) {
- throw new IllegalArgumentException("invalid -source version: " + value);
- }
- }
-
- private static boolean isValidSourceVersion(Integer version, boolean hasPrefix) {
- if (version == null) {
- return false;
- }
- if (version < 5) {
- // the earliest source version supported by JDK 8 is Java 5
- return false;
- }
- if (hasPrefix && version > 10) {
- // javac supports legacy `1.*` version numbers for source versions up to Java 10
- return false;
- }
- return true;
- }
-
private static URL[] toUrls(ImmutableList<String> processorPath) throws MalformedURLException {
URL[] urls = new URL[processorPath.size()];
int i = 0;
@@ -510,15 +457,6 @@ public class Processing {
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) {
@@ -550,8 +488,7 @@ public class Processing {
* The classloader to use for annotation processor implementations, and any annotations they
* access reflectively.
*/
- @Nullable
- abstract ClassLoader loader();
+ abstract @Nullable ClassLoader loader();
/** Command line annotation processing options, passed to javac with {@code -Akey=value}. */
abstract ImmutableMap<String, String> options();
@@ -609,7 +546,7 @@ public class Processing {
// requireNonNull is safe, barring bizarre processor implementations (e.g., anonymous class)
result.put(requireNonNull(e.getKey().getCanonicalName()), e.getValue().elapsed());
}
- return result.build();
+ return result.buildOrThrow();
}
}
diff --git a/java/com/google/turbine/binder/Resolve.java b/java/com/google/turbine/binder/Resolve.java
index 66e1036..6b76389 100644
--- a/java/com/google/turbine/binder/Resolve.java
+++ b/java/com/google/turbine/binder/Resolve.java
@@ -31,6 +31,7 @@ import com.google.turbine.tree.Tree;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
+import org.jspecify.nullness.Nullable;
/** Qualified name resolution. */
public final class Resolve {
@@ -40,17 +41,17 @@ public final class Resolve {
* qualified by the given symbol. The search considers members that are inherited from
* superclasses or interfaces.
*/
- public static ClassSymbol resolve(
+ public static @Nullable ClassSymbol resolve(
Env<ClassSymbol, ? extends HeaderBoundClass> env,
- ClassSymbol origin,
+ @Nullable ClassSymbol origin,
ClassSymbol sym,
Tree.Ident simpleName) {
return resolve(env, origin, sym, simpleName, new HashSet<>());
}
- private static ClassSymbol resolve(
+ private static @Nullable ClassSymbol resolve(
Env<ClassSymbol, ? extends HeaderBoundClass> env,
- ClassSymbol origin,
+ @Nullable ClassSymbol origin,
ClassSymbol sym,
Tree.Ident simpleName,
Set<ClassSymbol> seen) {
@@ -69,13 +70,13 @@ public final class Resolve {
}
if (bound.superclass() != null) {
result = resolve(env, origin, bound.superclass(), simpleName, seen);
- if (result != null && visible(origin, result, env.get(result))) {
+ if (result != null && visible(origin, result, env.getNonNull(result))) {
return result;
}
}
for (ClassSymbol i : bound.interfaces()) {
result = resolve(env, origin, i, simpleName, seen);
- if (result != null && visible(origin, result, env.get(result))) {
+ if (result != null && visible(origin, result, env.getNonNull(result))) {
return result;
}
}
@@ -87,10 +88,10 @@ public final class Resolve {
* env} and {@code origin} symbol.
*/
public static ResolveFunction resolveFunction(
- Env<ClassSymbol, ? extends HeaderBoundClass> env, ClassSymbol origin) {
+ Env<ClassSymbol, ? extends HeaderBoundClass> env, @Nullable ClassSymbol origin) {
return new ResolveFunction() {
@Override
- public ClassSymbol resolveOne(ClassSymbol base, Tree.Ident name) {
+ public @Nullable ClassSymbol resolveOne(ClassSymbol base, Tree.Ident name) {
try {
return Resolve.resolve(env, origin, base, name);
} catch (LazyBindingError e) {
@@ -113,24 +114,24 @@ public final class Resolve {
}
@Override
- public ClassSymbol resolveOne(ClassSymbol sym, Tree.Ident bit) {
+ public @Nullable ClassSymbol resolveOne(ClassSymbol sym, Tree.Ident bit) {
BoundClass ci = env.get(sym);
if (ci == null) {
return null;
}
- sym = ci.children().get(bit.value());
- if (sym == null) {
+ ClassSymbol result = ci.children().get(bit.value());
+ if (result == null) {
return null;
}
- if (!visible(sym)) {
+ if (!visible(result)) {
return null;
}
- return sym;
+ return result;
}
@Override
public boolean visible(ClassSymbol sym) {
- TurbineVisibility visibility = TurbineVisibility.fromAccess(env.get(sym).access());
+ TurbineVisibility visibility = TurbineVisibility.fromAccess(env.getNonNull(sym).access());
switch (visibility) {
case PUBLIC:
return true;
@@ -149,14 +150,17 @@ public final class Resolve {
* qualified by the given symbol. The search considers members that are inherited from
* superclasses or interfaces.
*/
- public static FieldInfo resolveField(
- Env<ClassSymbol, TypeBoundClass> env, ClassSymbol origin, ClassSymbol sym, Tree.Ident name) {
+ public static @Nullable FieldInfo resolveField(
+ Env<ClassSymbol, TypeBoundClass> env,
+ @Nullable ClassSymbol origin,
+ ClassSymbol sym,
+ Tree.Ident name) {
return resolveField(env, origin, sym, name, new HashSet<>());
}
- private static FieldInfo resolveField(
+ private static @Nullable FieldInfo resolveField(
Env<ClassSymbol, TypeBoundClass> env,
- ClassSymbol origin,
+ @Nullable ClassSymbol origin,
ClassSymbol sym,
Tree.Ident name,
Set<ClassSymbol> seen) {
@@ -189,23 +193,26 @@ public final class Resolve {
}
/** Is the given field visible when inherited into class origin? */
- private static boolean visible(ClassSymbol origin, FieldInfo info) {
+ private static boolean visible(@Nullable ClassSymbol origin, FieldInfo info) {
return visible(origin, info.sym().owner(), info.access());
}
/** Is the given type visible when inherited into class origin? */
- private static boolean visible(ClassSymbol origin, ClassSymbol sym, HeaderBoundClass info) {
+ private static boolean visible(
+ @Nullable ClassSymbol origin, ClassSymbol sym, HeaderBoundClass info) {
return visible(origin, sym, info.access());
}
- private static boolean visible(ClassSymbol origin, ClassSymbol owner, int access) {
+ private static boolean visible(@Nullable ClassSymbol origin, ClassSymbol owner, int access) {
TurbineVisibility visibility = TurbineVisibility.fromAccess(access);
switch (visibility) {
case PUBLIC:
case PROTECTED:
return true;
case PACKAGE:
- return Objects.equals(owner.packageName(), origin.packageName());
+ // origin can be null if we aren't in a package scope (e.g. we're processing a module
+ // declaration), in which case package-visible members aren't visible
+ return origin != null && 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.
diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java
index a28acd9..92d2827 100644
--- a/java/com/google/turbine/binder/TypeBinder.java
+++ b/java/com/google/turbine/binder/TypeBinder.java
@@ -16,6 +16,7 @@
package com.google.turbine.binder;
+import static com.google.common.collect.Iterables.getLast;
import static java.util.Objects.requireNonNull;
import com.google.common.base.Joiner;
@@ -27,6 +28,7 @@ import com.google.turbine.binder.bound.SourceTypeBoundClass;
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.RecordComponentInfo;
import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
import com.google.turbine.binder.env.Env;
import com.google.turbine.binder.lookup.CompoundScope;
@@ -37,6 +39,7 @@ 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.RecordComponentSymbol;
import com.google.turbine.binder.sym.Symbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.diag.TurbineError.ErrorKind;
@@ -63,6 +66,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.jspecify.nullness.Nullable;
/** Type binding. */
public class TypeBinder {
@@ -79,7 +83,7 @@ public class TypeBinder {
}
@Override
- public LookupResult lookup(LookupKey lookup) {
+ public @Nullable LookupResult lookup(LookupKey lookup) {
if (name.equals(lookup.first().value())) {
return new LookupResult(sym, lookup);
}
@@ -96,7 +100,7 @@ public class TypeBinder {
}
@Override
- public LookupResult lookup(LookupKey lookupKey) {
+ public @Nullable LookupResult lookup(LookupKey lookupKey) {
Symbol sym = tps.get(lookupKey.first().value());
return sym != null ? new LookupResult(sym, lookupKey) : null;
}
@@ -116,14 +120,14 @@ public class TypeBinder {
}
@Override
- public LookupResult lookup(LookupKey lookup) {
+ public @Nullable LookupResult lookup(LookupKey lookup) {
ClassSymbol curr = sym;
while (curr != null) {
- HeaderBoundClass info = env.get(curr);
Symbol result = Resolve.resolve(env, sym, curr, lookup.first());
if (result != null) {
return new LookupResult(result, lookup);
}
+ HeaderBoundClass info = env.getNonNull(curr);
result = info.typeParameters().get(lookup.first().value());
if (result != null) {
return new LookupResult(result, lookup);
@@ -168,8 +172,10 @@ public class TypeBinder {
CompoundScope enclosingScope =
base.scope()
.toScope(Resolve.resolveFunction(env, owner))
- .append(new SingletonScope(base.decl().name().value(), owner))
- .append(new ClassMemberScope(base.owner(), env));
+ .append(new SingletonScope(base.decl().name().value(), owner));
+ if (base.owner() != null) {
+ enclosingScope = enclosingScope.append(new ClassMemberScope(base.owner(), env));
+ }
ImmutableList<AnnoInfo> annotations = bindAnnotations(enclosingScope, base.decl().annos());
@@ -212,6 +218,9 @@ public class TypeBinder {
}
superClassType = Type.ClassTy.OBJECT;
break;
+ case RECORD:
+ superClassType = Type.ClassTy.asNonParametricClassTy(ClassSymbol.RECORD);
+ break;
default:
throw new AssertionError(base.decl().tykind());
}
@@ -220,26 +229,43 @@ public class TypeBinder {
interfaceTypes.add(bindClassTy(bindingScope, i));
}
+ ImmutableList.Builder<ClassSymbol> permits = ImmutableList.builder();
+ for (Tree.ClassTy i : base.decl().permits()) {
+ Type type = bindClassTy(bindingScope, i);
+ if (!type.tyKind().equals(Type.TyKind.CLASS_TY)) {
+ throw new AssertionError(type.tyKind());
+ }
+ permits.add(((Type.ClassTy) type).sym());
+ }
+
CompoundScope scope =
base.scope()
.toScope(Resolve.resolveFunction(env, owner))
.append(new SingletonScope(base.decl().name().value(), owner))
.append(new ClassMemberScope(owner, env));
- List<MethodInfo> methods =
+ SyntheticMethods syntheticMethods = new SyntheticMethods();
+
+ ImmutableList<RecordComponentInfo> components = bindComponents(scope, base.decl().components());
+
+ ImmutableList.Builder<MethodInfo> methods =
ImmutableList.<MethodInfo>builder()
- .addAll(syntheticMethods())
- .addAll(bindMethods(scope, base.decl().members()))
- .build();
+ .addAll(syntheticMethods(syntheticMethods, components))
+ .addAll(bindMethods(scope, base.decl().members(), components));
+ if (base.kind().equals(TurbineTyKind.RECORD)) {
+ methods.addAll(syntheticRecordMethods(syntheticMethods, components));
+ }
ImmutableList<FieldInfo> fields = bindFields(scope, base.decl().members());
return new SourceTypeBoundClass(
interfaceTypes.build(),
+ permits.build(),
superClassType,
typeParameterTypes,
base.access(),
- ImmutableList.copyOf(methods),
+ components,
+ methods.build(),
fields,
base.owner(),
base.kind(),
@@ -254,23 +280,79 @@ public class TypeBinder {
base.decl());
}
+ /**
+ * A generated for synthetic {@link MethodSymbol}s.
+ *
+ * <p>Each {@link MethodSymbol} contains an index into its enclosing class, to enable comparing
+ * the symbols for equality. For synthetic methods we use an arbitrary unique negative index.
+ */
+ private static class SyntheticMethods {
+
+ private int idx = -1;
+
+ MethodSymbol create(ClassSymbol owner, String name) {
+ return new MethodSymbol(idx--, owner, name);
+ }
+ }
+
+ private ImmutableList<RecordComponentInfo> bindComponents(
+ CompoundScope scope, ImmutableList<Tree.VarDecl> components) {
+ ImmutableList.Builder<RecordComponentInfo> result = ImmutableList.builder();
+ for (Tree.VarDecl p : components) {
+ int access = 0;
+ for (TurbineModifier m : p.mods()) {
+ access |= m.flag();
+ }
+ RecordComponentInfo param =
+ new RecordComponentInfo(
+ new RecordComponentSymbol(owner, p.name().value()),
+ bindTy(scope, p.ty()),
+ bindAnnotations(scope, p.annos()),
+ access);
+ result.add(param);
+ }
+ return result.build();
+ }
+
/** Collect synthetic and implicit methods, including default constructors and enum methods. */
- ImmutableList<MethodInfo> syntheticMethods() {
+ ImmutableList<MethodInfo> syntheticMethods(
+ SyntheticMethods syntheticMethods, ImmutableList<RecordComponentInfo> components) {
switch (base.kind()) {
case CLASS:
- return maybeDefaultConstructor();
+ return maybeDefaultConstructor(syntheticMethods);
+ case RECORD:
+ return maybeDefaultRecordConstructor(syntheticMethods, components);
case ENUM:
- return syntheticEnumMethods();
+ return syntheticEnumMethods(syntheticMethods);
default:
return ImmutableList.of();
}
}
- private ImmutableList<MethodInfo> maybeDefaultConstructor() {
+ private ImmutableList<MethodInfo> maybeDefaultRecordConstructor(
+ SyntheticMethods syntheticMethods, ImmutableList<RecordComponentInfo> components) {
if (hasConstructor()) {
return ImmutableList.of();
}
- MethodSymbol symbol = new MethodSymbol(-1, owner, "<init>");
+ MethodSymbol symbol = syntheticMethods.create(owner, "<init>");
+ ImmutableList.Builder<ParamInfo> params = ImmutableList.builder();
+ for (RecordComponentInfo component : components) {
+ params.add(
+ new ParamInfo(
+ new ParamSymbol(symbol, component.name()),
+ component.type(),
+ component.annotations(),
+ component.access()));
+ }
+ return ImmutableList.of(
+ syntheticConstructor(symbol, params.build(), TurbineVisibility.fromAccess(base.access())));
+ }
+
+ private ImmutableList<MethodInfo> maybeDefaultConstructor(SyntheticMethods syntheticMethods) {
+ if (hasConstructor()) {
+ return ImmutableList.of();
+ }
+ MethodSymbol symbol = syntheticMethods.create(owner, "<init>");
ImmutableList<ParamInfo> formals;
if (hasEnclosingInstance(base)) {
formals = ImmutableList.of(enclosingInstanceParameter(symbol));
@@ -285,6 +367,10 @@ public class TypeBinder {
MethodSymbol symbol, ImmutableList<ParamInfo> formals, TurbineVisibility visibility) {
int access = visibility.flag();
access |= (base.access() & TurbineFlag.ACC_STRICT);
+ if (!formals.isEmpty()
+ && (getLast(formals).access() & TurbineFlag.ACC_VARARGS) == TurbineFlag.ACC_VARARGS) {
+ access |= TurbineFlag.ACC_VARARGS;
+ }
return new MethodInfo(
symbol,
ImmutableMap.of(),
@@ -307,7 +393,7 @@ public class TypeBinder {
}
int enclosingInstances = 0;
for (ClassSymbol sym = base.owner(); sym != null; ) {
- HeaderBoundClass info = env.get(sym);
+ HeaderBoundClass info = env.getNonNull(sym);
if (((info.access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC)
|| info.owner() == null) {
break;
@@ -338,15 +424,15 @@ public class TypeBinder {
TurbineFlag.ACC_SYNTHETIC));
}
- private ImmutableList<MethodInfo> syntheticEnumMethods() {
+ private ImmutableList<MethodInfo> syntheticEnumMethods(SyntheticMethods syntheticMethods) {
ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder();
int access = 0;
access |= (base.access() & TurbineFlag.ACC_STRICT);
if (!hasConstructor()) {
- MethodSymbol symbol = new MethodSymbol(-1, owner, "<init>");
+ MethodSymbol symbol = syntheticMethods.create(owner, "<init>");
methods.add(syntheticConstructor(symbol, enumCtorParams(symbol), TurbineVisibility.PRIVATE));
}
- MethodSymbol valuesMethod = new MethodSymbol(-2, owner, "values");
+ MethodSymbol valuesMethod = syntheticMethods.create(owner, "values");
methods.add(
new MethodInfo(
valuesMethod,
@@ -359,7 +445,7 @@ public class TypeBinder {
null,
ImmutableList.of(),
null));
- MethodSymbol valueOfMethod = new MethodSymbol(-3, owner, "valueOf");
+ MethodSymbol valueOfMethod = syntheticMethods.create(owner, "valueOf");
methods.add(
new MethodInfo(
valueOfMethod,
@@ -380,6 +466,71 @@ public class TypeBinder {
return methods.build();
}
+ private ImmutableList<MethodInfo> syntheticRecordMethods(
+ SyntheticMethods syntheticMethods, ImmutableList<RecordComponentInfo> components) {
+ ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder();
+ MethodSymbol toStringMethod = syntheticMethods.create(owner, "toString");
+ methods.add(
+ new MethodInfo(
+ toStringMethod,
+ ImmutableMap.of(),
+ Type.ClassTy.STRING,
+ ImmutableList.of(),
+ ImmutableList.of(),
+ TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL,
+ null,
+ null,
+ ImmutableList.of(),
+ null));
+ MethodSymbol hashCodeMethod = syntheticMethods.create(owner, "hashCode");
+ methods.add(
+ new MethodInfo(
+ hashCodeMethod,
+ ImmutableMap.of(),
+ Type.PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()),
+ ImmutableList.of(),
+ ImmutableList.of(),
+ TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL,
+ null,
+ null,
+ ImmutableList.of(),
+ null));
+ MethodSymbol equalsMethod = syntheticMethods.create(owner, "equals");
+ methods.add(
+ new MethodInfo(
+ equalsMethod,
+ ImmutableMap.of(),
+ Type.PrimTy.create(TurbineConstantTypeKind.BOOLEAN, ImmutableList.of()),
+ ImmutableList.of(
+ new ParamInfo(
+ new ParamSymbol(equalsMethod, "other"),
+ Type.ClassTy.OBJECT,
+ ImmutableList.of(),
+ TurbineFlag.ACC_MANDATED)),
+ ImmutableList.of(),
+ TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL,
+ null,
+ null,
+ ImmutableList.of(),
+ null));
+ for (RecordComponentInfo c : components) {
+ MethodSymbol componentMethod = syntheticMethods.create(owner, c.name());
+ methods.add(
+ new MethodInfo(
+ componentMethod,
+ ImmutableMap.of(),
+ c.type(),
+ ImmutableList.of(),
+ ImmutableList.of(),
+ TurbineFlag.ACC_PUBLIC,
+ null,
+ null,
+ c.annotations(),
+ null));
+ }
+ return methods.build();
+ }
+
private boolean hasConstructor() {
for (Tree m : base.decl().members()) {
if (m.kind() != Kind.METH_DECL) {
@@ -409,21 +560,25 @@ public class TypeBinder {
new TyVarInfo(
IntersectionTy.create(bounds.build()), /* lowerBound= */ null, annotations));
}
- return result.build();
+ return result.buildOrThrow();
}
- private List<MethodInfo> bindMethods(CompoundScope scope, ImmutableList<Tree> members) {
+ private List<MethodInfo> bindMethods(
+ CompoundScope scope,
+ ImmutableList<Tree> members,
+ ImmutableList<RecordComponentInfo> components) {
List<MethodInfo> methods = new ArrayList<>();
int idx = 0;
for (Tree member : members) {
if (member.kind() == Tree.Kind.METH_DECL) {
- methods.add(bindMethod(idx++, scope, (Tree.MethDecl) member));
+ methods.add(bindMethod(idx++, scope, (MethDecl) member, components));
}
}
return methods;
}
- private MethodInfo bindMethod(int idx, CompoundScope scope, Tree.MethDecl t) {
+ private MethodInfo bindMethod(
+ int idx, CompoundScope scope, MethDecl t, ImmutableList<RecordComponentInfo> components) {
MethodSymbol sym = new MethodSymbol(idx, owner, t.name().value());
@@ -433,7 +588,7 @@ public class TypeBinder {
for (Tree.TyParam pt : t.typarams()) {
builder.put(pt.name().value(), new TyVarSymbol(sym, pt.name().value()));
}
- typeParameters = builder.build();
+ typeParameters = builder.buildOrThrow();
}
// type parameters can refer to each other in f-bounds, so update the scope first
@@ -453,8 +608,26 @@ public class TypeBinder {
if (name.equals("<init>")) {
if (hasEnclosingInstance(base)) {
parameters.add(enclosingInstanceParameter(sym));
- } else if (base.kind() == TurbineTyKind.ENUM && name.equals("<init>")) {
- parameters.addAll(enumCtorParams(sym));
+ } else {
+ switch (base.kind()) {
+ case ENUM:
+ parameters.addAll(enumCtorParams(sym));
+ break;
+ case RECORD:
+ if (t.mods().contains(TurbineModifier.COMPACT_CTOR)) {
+ for (RecordComponentInfo component : components) {
+ parameters.add(
+ new ParamInfo(
+ new ParamSymbol(sym, component.name()),
+ component.type(),
+ component.annotations(),
+ component.access()));
+ }
+ }
+ break;
+ default:
+ break;
+ }
}
}
ParamInfo receiver = null;
@@ -582,8 +755,8 @@ public class TypeBinder {
return result.build();
}
- private ClassSymbol resolveAnnoSymbol(
- Anno tree, ImmutableList<Ident> name, LookupResult lookupResult) {
+ private @Nullable ClassSymbol resolveAnnoSymbol(
+ Anno tree, ImmutableList<Ident> name, @Nullable LookupResult lookupResult) {
if (lookupResult == null) {
log.error(tree.position(), ErrorKind.CANNOT_RESOLVE, Joiner.on('.').join(name));
return null;
@@ -595,13 +768,13 @@ public class TypeBinder {
return null;
}
}
- if (env.get(sym).kind() != TurbineTyKind.ANNOTATION) {
+ if (env.getNonNull(sym).kind() != TurbineTyKind.ANNOTATION) {
log.error(tree.position(), ErrorKind.NOT_AN_ANNOTATION, sym);
}
return sym;
}
- private ClassSymbol resolveNext(ClassSymbol sym, Ident bit) {
+ private @Nullable ClassSymbol resolveNext(ClassSymbol sym, Ident bit) {
ClassSymbol next = Resolve.resolve(env, owner, sym, bit);
if (next == null) {
log.error(
@@ -705,10 +878,11 @@ public class TypeBinder {
sym, bindTyArgs(scope, flat.get(idx++).tyargs()), annotations));
for (; idx < flat.size(); idx++) {
Tree.ClassTy curr = flat.get(idx);
- sym = resolveNext(sym, curr.name());
- if (sym == null) {
+ ClassSymbol next = resolveNext(sym, curr.name());
+ if (next == null) {
return Type.ErrorTy.create(bits);
}
+ sym = next;
annotations = bindAnnotations(scope, curr.annos());
classes.add(
diff --git a/java/com/google/turbine/binder/bound/AnnotationMetadata.java b/java/com/google/turbine/binder/bound/AnnotationMetadata.java
index a4d3037..5ae04b0 100644
--- a/java/com/google/turbine/binder/bound/AnnotationMetadata.java
+++ b/java/com/google/turbine/binder/bound/AnnotationMetadata.java
@@ -23,6 +23,7 @@ import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.model.TurbineElementType;
import java.lang.annotation.RetentionPolicy;
import java.util.EnumSet;
+import org.jspecify.nullness.Nullable;
/**
* Annotation metadata, e.g. from {@link java.lang.annotation.Target}, {@link
@@ -41,12 +42,12 @@ public class AnnotationMetadata {
private final RetentionPolicy retention;
private final ImmutableSet<TurbineElementType> target;
- private final ClassSymbol repeatable;
+ private final @Nullable ClassSymbol repeatable;
public AnnotationMetadata(
- RetentionPolicy retention,
- ImmutableSet<TurbineElementType> annotationTarget,
- ClassSymbol repeatable) {
+ @Nullable RetentionPolicy retention,
+ @Nullable ImmutableSet<TurbineElementType> annotationTarget,
+ @Nullable ClassSymbol repeatable) {
this.retention = firstNonNull(retention, RetentionPolicy.CLASS);
this.target = firstNonNull(annotationTarget, DEFAULT_TARGETS);
this.repeatable = repeatable;
@@ -63,7 +64,7 @@ public class AnnotationMetadata {
}
/** The container annotation for {@code @Repeated} annotations. */
- public ClassSymbol repeatable() {
+ public @Nullable ClassSymbol repeatable() {
return repeatable;
}
}
diff --git a/java/com/google/turbine/binder/bound/BoundClass.java b/java/com/google/turbine/binder/bound/BoundClass.java
index 61dee0f..1e29b42 100644
--- a/java/com/google/turbine/binder/bound/BoundClass.java
+++ b/java/com/google/turbine/binder/bound/BoundClass.java
@@ -19,7 +19,7 @@ package com.google.turbine.binder.bound;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.model.TurbineTyKind;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/**
* The initial bound tree representation.
diff --git a/java/com/google/turbine/binder/bound/EnumConstantValue.java b/java/com/google/turbine/binder/bound/EnumConstantValue.java
index e99c6ed..20a5756 100644
--- a/java/com/google/turbine/binder/bound/EnumConstantValue.java
+++ b/java/com/google/turbine/binder/bound/EnumConstantValue.java
@@ -18,6 +18,7 @@ package com.google.turbine.binder.bound;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.model.Const;
+import org.jspecify.nullness.Nullable;
/** An enum constant. */
public class EnumConstantValue extends Const {
@@ -43,7 +44,7 @@ public class EnumConstantValue extends Const {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof EnumConstantValue && sym().equals(((EnumConstantValue) obj).sym());
}
diff --git a/java/com/google/turbine/binder/bound/HeaderBoundClass.java b/java/com/google/turbine/binder/bound/HeaderBoundClass.java
index 7aeb3d8..9658016 100644
--- a/java/com/google/turbine/binder/bound/HeaderBoundClass.java
+++ b/java/com/google/turbine/binder/bound/HeaderBoundClass.java
@@ -20,10 +20,12 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.TyVarSymbol;
+import org.jspecify.nullness.Nullable;
/** A bound node that augments {@link BoundClass} with superclasses and interfaces. */
public interface HeaderBoundClass extends BoundClass {
/** The superclass of the declaration. */
+ @Nullable
ClassSymbol superclass();
/** The interfaces of the declaration. */
diff --git a/java/com/google/turbine/binder/bound/ModuleInfo.java b/java/com/google/turbine/binder/bound/ModuleInfo.java
index f21213b..5dc8720 100644
--- a/java/com/google/turbine/binder/bound/ModuleInfo.java
+++ b/java/com/google/turbine/binder/bound/ModuleInfo.java
@@ -19,13 +19,13 @@ package com.google.turbine.binder.bound;
import com.google.common.collect.ImmutableList;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.type.AnnoInfo;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/** A bound module declaration (see JLS §7.7). */
public class ModuleInfo {
private final String name;
- @Nullable private final String version;
+ private final @Nullable String version;
private final int flags;
private final ImmutableList<AnnoInfo> annos;
private final ImmutableList<RequireInfo> requires;
@@ -59,8 +59,7 @@ public class ModuleInfo {
return name;
}
- @Nullable
- public String version() {
+ public @Nullable String version() {
return version;
}
@@ -97,9 +96,9 @@ public class ModuleInfo {
private final String moduleName;
private final int flags;
- private final String version;
+ private final @Nullable String version;
- public RequireInfo(String moduleName, int flags, String version) {
+ public RequireInfo(String moduleName, int flags, @Nullable String version) {
this.moduleName = moduleName;
this.flags = flags;
this.version = version;
@@ -113,7 +112,7 @@ public class ModuleInfo {
return flags;
}
- public String version() {
+ public @Nullable String version() {
return version;
}
}
diff --git a/java/com/google/turbine/binder/bound/PackageSourceBoundClass.java b/java/com/google/turbine/binder/bound/PackageSourceBoundClass.java
index 2dd2e4e..77832f9 100644
--- a/java/com/google/turbine/binder/bound/PackageSourceBoundClass.java
+++ b/java/com/google/turbine/binder/bound/PackageSourceBoundClass.java
@@ -23,6 +23,7 @@ import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.diag.SourceFile;
import com.google.turbine.model.TurbineTyKind;
import com.google.turbine.tree.Tree;
+import org.jspecify.nullness.Nullable;
/** A {@link BoundClass} with shared lookup scopes for the current compilation unit and package. */
public class PackageSourceBoundClass implements BoundClass {
@@ -52,7 +53,7 @@ public class PackageSourceBoundClass implements BoundClass {
}
@Override
- public ClassSymbol owner() {
+ public @Nullable ClassSymbol owner() {
return base.owner();
}
diff --git a/java/com/google/turbine/binder/bound/SourceBoundClass.java b/java/com/google/turbine/binder/bound/SourceBoundClass.java
index 9e27ff3..7a6f33f 100644
--- a/java/com/google/turbine/binder/bound/SourceBoundClass.java
+++ b/java/com/google/turbine/binder/bound/SourceBoundClass.java
@@ -20,18 +20,19 @@ import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.model.TurbineTyKind;
import com.google.turbine.tree.Tree;
+import org.jspecify.nullness.Nullable;
/** A {@link BoundClass} that corresponds to a source file being compiled. */
public class SourceBoundClass implements BoundClass {
private final ClassSymbol sym;
- private final ClassSymbol owner;
+ private final @Nullable ClassSymbol owner;
private final ImmutableMap<String, ClassSymbol> children;
private final int access;
private final Tree.TyDecl decl;
public SourceBoundClass(
ClassSymbol sym,
- ClassSymbol owner,
+ @Nullable ClassSymbol owner,
ImmutableMap<String, ClassSymbol> children,
int access,
Tree.TyDecl decl) {
@@ -52,7 +53,7 @@ public class SourceBoundClass implements BoundClass {
}
@Override
- public ClassSymbol owner() {
+ public @Nullable ClassSymbol owner() {
return owner;
}
diff --git a/java/com/google/turbine/binder/bound/SourceHeaderBoundClass.java b/java/com/google/turbine/binder/bound/SourceHeaderBoundClass.java
index c15d0dd..210ff0b 100644
--- a/java/com/google/turbine/binder/bound/SourceHeaderBoundClass.java
+++ b/java/com/google/turbine/binder/bound/SourceHeaderBoundClass.java
@@ -25,18 +25,19 @@ import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.diag.SourceFile;
import com.google.turbine.model.TurbineTyKind;
import com.google.turbine.tree.Tree;
+import org.jspecify.nullness.Nullable;
/** A {@link HeaderBoundClass} that corresponds to a source file being compiled. */
public class SourceHeaderBoundClass implements HeaderBoundClass {
private final PackageSourceBoundClass base;
- private final ClassSymbol superclass;
+ private final @Nullable ClassSymbol superclass;
private final ImmutableList<ClassSymbol> interfaces;
private final ImmutableMap<String, TyVarSymbol> typeParameters;
public SourceHeaderBoundClass(
PackageSourceBoundClass base,
- ClassSymbol superclass,
+ @Nullable ClassSymbol superclass,
ImmutableList<ClassSymbol> interfaces,
ImmutableMap<String, TyVarSymbol> typeParameters) {
this.base = base;
@@ -46,7 +47,7 @@ public class SourceHeaderBoundClass implements HeaderBoundClass {
}
@Override
- public ClassSymbol superclass() {
+ public @Nullable ClassSymbol superclass() {
return superclass;
}
@@ -66,7 +67,7 @@ public class SourceHeaderBoundClass implements HeaderBoundClass {
}
@Override
- public ClassSymbol owner() {
+ public @Nullable ClassSymbol owner() {
return base.owner();
}
diff --git a/java/com/google/turbine/binder/bound/SourceModuleInfo.java b/java/com/google/turbine/binder/bound/SourceModuleInfo.java
index 1163e9f..66ba0e4 100644
--- a/java/com/google/turbine/binder/bound/SourceModuleInfo.java
+++ b/java/com/google/turbine/binder/bound/SourceModuleInfo.java
@@ -24,7 +24,7 @@ import com.google.turbine.binder.bound.ModuleInfo.RequireInfo;
import com.google.turbine.binder.bound.ModuleInfo.UseInfo;
import com.google.turbine.diag.SourceFile;
import com.google.turbine.type.AnnoInfo;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/** A {@link ModuleInfo} that corresponds to a source file being compiled. */
public class SourceModuleInfo extends ModuleInfo {
diff --git a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java
index 69a2593..5e9817e 100644
--- a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java
+++ b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java
@@ -29,53 +29,59 @@ 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.TyKind;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/** A HeaderBoundClass for classes compiled from source. */
public class SourceTypeBoundClass implements TypeBoundClass {
private final TurbineTyKind kind;
- private final ClassSymbol owner;
+ private final @Nullable ClassSymbol owner;
private final ImmutableMap<String, ClassSymbol> children;
private final int access;
private final ImmutableMap<String, TyVarSymbol> typeParameters;
private final ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes;
- private final Type superClassType;
+ private final @Nullable Type superClassType;
private final ImmutableList<Type> interfaceTypes;
+ private final ImmutableList<ClassSymbol> permits;
+ private final ImmutableList<RecordComponentInfo> components;
private final ImmutableList<MethodInfo> methods;
private final ImmutableList<FieldInfo> fields;
private final CompoundScope enclosingScope;
private final CompoundScope scope;
private final MemberImportIndex memberImports;
- private final AnnotationMetadata annotationMetadata;
+ private final @Nullable AnnotationMetadata annotationMetadata;
private final ImmutableList<AnnoInfo> annotations;
private final Tree.TyDecl decl;
private final SourceFile source;
public SourceTypeBoundClass(
ImmutableList<Type> interfaceTypes,
- Type superClassType,
+ ImmutableList<ClassSymbol> permits,
+ @Nullable Type superClassType,
ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes,
int access,
+ ImmutableList<RecordComponentInfo> components,
ImmutableList<MethodInfo> methods,
ImmutableList<FieldInfo> fields,
- ClassSymbol owner,
+ @Nullable ClassSymbol owner,
TurbineTyKind kind,
ImmutableMap<String, ClassSymbol> children,
ImmutableMap<String, TyVarSymbol> typeParameters,
CompoundScope enclosingScope,
CompoundScope scope,
MemberImportIndex memberImports,
- AnnotationMetadata annotationMetadata,
+ @Nullable AnnotationMetadata annotationMetadata,
ImmutableList<AnnoInfo> annotations,
SourceFile source,
Tree.TyDecl decl) {
this.interfaceTypes = interfaceTypes;
+ this.permits = permits;
this.superClassType = superClassType;
this.typeParameterTypes = typeParameterTypes;
this.access = access;
+ this.components = components;
this.methods = methods;
this.fields = fields;
this.owner = owner;
@@ -92,7 +98,7 @@ public class SourceTypeBoundClass implements TypeBoundClass {
}
@Override
- public ClassSymbol superclass() {
+ public @Nullable ClassSymbol superclass() {
if (superClassType == null) {
return null;
}
@@ -114,6 +120,11 @@ public class SourceTypeBoundClass implements TypeBoundClass {
}
@Override
+ public ImmutableList<ClassSymbol> permits() {
+ return permits;
+ }
+
+ @Override
public int access() {
return access;
}
@@ -123,9 +134,8 @@ public class SourceTypeBoundClass implements TypeBoundClass {
return kind;
}
- @Nullable
@Override
- public ClassSymbol owner() {
+ public @Nullable ClassSymbol owner() {
return owner;
}
@@ -146,10 +156,16 @@ public class SourceTypeBoundClass implements TypeBoundClass {
/** The super-class type. */
@Override
- public Type superClassType() {
+ public @Nullable Type superClassType() {
return superClassType;
}
+ /** The record components. */
+ @Override
+ public ImmutableList<RecordComponentInfo> components() {
+ return components;
+ }
+
/** Declared methods. */
@Override
public ImmutableList<MethodInfo> methods() {
@@ -157,7 +173,7 @@ public class SourceTypeBoundClass implements TypeBoundClass {
}
@Override
- public AnnotationMetadata annotationMetadata() {
+ public @Nullable AnnotationMetadata annotationMetadata() {
return annotationMetadata;
}
diff --git a/java/com/google/turbine/binder/bound/TurbineAnnotationValue.java b/java/com/google/turbine/binder/bound/TurbineAnnotationValue.java
index 808d603..b6737d6 100644
--- a/java/com/google/turbine/binder/bound/TurbineAnnotationValue.java
+++ b/java/com/google/turbine/binder/bound/TurbineAnnotationValue.java
@@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.model.Const;
import com.google.turbine.type.AnnoInfo;
+import org.jspecify.nullness.Nullable;
/** An annotation literal constant. */
public class TurbineAnnotationValue extends Const {
@@ -56,7 +57,7 @@ public class TurbineAnnotationValue extends Const {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbineAnnotationValue
&& info().equals(((TurbineAnnotationValue) obj).info());
}
diff --git a/java/com/google/turbine/binder/bound/TurbineClassValue.java b/java/com/google/turbine/binder/bound/TurbineClassValue.java
index df55501..c6ba6ef 100644
--- a/java/com/google/turbine/binder/bound/TurbineClassValue.java
+++ b/java/com/google/turbine/binder/bound/TurbineClassValue.java
@@ -19,6 +19,7 @@ package com.google.turbine.binder.bound;
import com.google.turbine.model.Const;
import com.google.turbine.type.Type;
import java.util.Objects;
+import org.jspecify.nullness.Nullable;
/** A class literal constant. */
public class TurbineClassValue extends Const {
@@ -50,7 +51,7 @@ public class TurbineClassValue extends Const {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbineClassValue && type().equals(((TurbineClassValue) obj).type());
}
}
diff --git a/java/com/google/turbine/binder/bound/TypeBoundClass.java b/java/com/google/turbine/binder/bound/TypeBoundClass.java
index 99d15bb..8321bde 100644
--- a/java/com/google/turbine/binder/bound/TypeBoundClass.java
+++ b/java/com/google/turbine/binder/bound/TypeBoundClass.java
@@ -16,12 +16,13 @@
package com.google.turbine.binder.bound;
-
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+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.RecordComponentSymbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.model.Const;
import com.google.turbine.model.TurbineFlag;
@@ -31,17 +32,21 @@ 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;
+import org.jspecify.nullness.Nullable;
/** A bound node that augments {@link HeaderBoundClass} with type information. */
public interface TypeBoundClass extends HeaderBoundClass {
/** The super-class type. */
+ @Nullable
Type superClassType();
/** Implemented interface types. */
ImmutableList<Type> interfaceTypes();
+ /** The permitted direct subclasses. */
+ ImmutableList<ClassSymbol> permits();
+
ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes();
/** Declared fields. */
@@ -50,10 +55,14 @@ public interface TypeBoundClass extends HeaderBoundClass {
/** Declared methods. */
ImmutableList<MethodInfo> methods();
+ /** Record components. */
+ ImmutableList<RecordComponentInfo> components();
+
/**
* Annotation metadata, e.g. from {@link java.lang.annotation.Target}, {@link
* java.lang.annotation.Retention}, and {@link java.lang.annotation.Repeatable}.
*/
+ @Nullable
AnnotationMetadata annotationMetadata();
/** Declaration annotations. */
@@ -62,7 +71,7 @@ public interface TypeBoundClass extends HeaderBoundClass {
/** A type parameter declaration. */
class TyVarInfo {
private final IntersectionTy upperBound;
- @Nullable private final Type lowerBound;
+ private final @Nullable Type lowerBound;
private final ImmutableList<AnnoInfo> annotations;
public TyVarInfo(
@@ -81,8 +90,7 @@ public interface TypeBoundClass extends HeaderBoundClass {
}
/** The lower bound. */
- @Nullable
- public Type lowerBound() {
+ public @Nullable Type lowerBound() {
return lowerBound;
}
@@ -99,16 +107,16 @@ public interface TypeBoundClass extends HeaderBoundClass {
private final int access;
private final ImmutableList<AnnoInfo> annotations;
- private final Tree.VarDecl decl;
- private final Const.Value value;
+ private final Tree.@Nullable VarDecl decl;
+ private final Const.@Nullable Value value;
public FieldInfo(
FieldSymbol sym,
Type type,
int access,
ImmutableList<AnnoInfo> annotations,
- Tree.VarDecl decl,
- Const.Value value) {
+ Tree.@Nullable VarDecl decl,
+ Const.@Nullable Value value) {
this.sym = sym;
this.type = type;
this.access = access;
@@ -138,12 +146,12 @@ public interface TypeBoundClass extends HeaderBoundClass {
}
/** The field's declaration. */
- public Tree.VarDecl decl() {
+ public Tree.@Nullable VarDecl decl() {
return decl;
}
/** The constant field value. */
- public Const.Value value() {
+ public Const.@Nullable Value value() {
return value;
}
@@ -161,8 +169,8 @@ public interface TypeBoundClass extends HeaderBoundClass {
private final ImmutableList<ParamInfo> parameters;
private final ImmutableList<Type> exceptions;
private final int access;
- private final Const defaultValue;
- private final MethDecl decl;
+ private final @Nullable Const defaultValue;
+ private final @Nullable MethDecl decl;
private final ImmutableList<AnnoInfo> annotations;
private final @Nullable ParamInfo receiver;
@@ -173,8 +181,8 @@ public interface TypeBoundClass extends HeaderBoundClass {
ImmutableList<ParamInfo> parameters,
ImmutableList<Type> exceptions,
int access,
- Const defaultValue,
- MethDecl decl,
+ @Nullable Const defaultValue,
+ @Nullable MethDecl decl,
ImmutableList<AnnoInfo> annotations,
@Nullable ParamInfo receiver) {
this.sym = sym;
@@ -225,7 +233,7 @@ public interface TypeBoundClass extends HeaderBoundClass {
}
/** The default value of an annotation interface method. */
- public Const defaultValue() {
+ public @Nullable Const defaultValue() {
return defaultValue;
}
@@ -238,7 +246,7 @@ public interface TypeBoundClass extends HeaderBoundClass {
}
/** The declaration. */
- public MethDecl decl() {
+ public @Nullable MethDecl decl() {
return decl;
}
@@ -319,4 +327,45 @@ public interface TypeBoundClass extends HeaderBoundClass {
return access;
}
}
+
+ /** A record component. */
+ class RecordComponentInfo {
+ private final RecordComponentSymbol sym;
+ private final Type type;
+ private final int access;
+ private final ImmutableList<AnnoInfo> annotations;
+
+ public RecordComponentInfo(
+ RecordComponentSymbol sym, Type type, ImmutableList<AnnoInfo> annotations, int access) {
+ this.sym = sym;
+ this.type = type;
+ this.access = access;
+ this.annotations = annotations;
+ }
+
+ /** The record component's symbol. */
+ public RecordComponentSymbol sym() {
+ return sym;
+ }
+
+ /** The record component type. */
+ public Type type() {
+ return type;
+ }
+
+ /** Record component annotations. */
+ public ImmutableList<AnnoInfo> annotations() {
+ return annotations;
+ }
+
+ /** The Record component's name. */
+ public String name() {
+ return sym.name();
+ }
+
+ /** The Record component's modifiers. */
+ public int access() {
+ return access;
+ }
+ }
}
diff --git a/java/com/google/turbine/binder/bound/package-info.java b/java/com/google/turbine/binder/bound/package-info.java
new file mode 100644
index 0000000..8839101
--- /dev/null
+++ b/java/com/google/turbine/binder/bound/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+@org.jspecify.nullness.NullMarked
+package com.google.turbine.binder.bound;
diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
index 0f4bac1..82f8a8c 100644
--- a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
+++ b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
@@ -16,6 +16,8 @@
package com.google.turbine.binder.bytecode;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.bound.EnumConstantValue;
@@ -40,6 +42,7 @@ import com.google.turbine.bytecode.sig.Sig.WildTySig;
import com.google.turbine.bytecode.sig.SigParser;
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 com.google.turbine.type.Type;
import java.util.ArrayList;
@@ -134,7 +137,7 @@ public final class BytecodeBinder {
for (Map.Entry<String, ElementValue> e : value.elementValuePairs().entrySet()) {
values.put(e.getKey(), bindValue(e.getValue()));
}
- return new TurbineAnnotationValue(new AnnoInfo(null, sym, null, values.build()));
+ return new TurbineAnnotationValue(new AnnoInfo(null, sym, null, values.buildOrThrow()));
}
static ImmutableList<AnnoInfo> bindAnnotations(List<AnnotationInfo> input) {
@@ -175,19 +178,23 @@ public final class BytecodeBinder {
// TODO(b/32626659): this is not bug-compatible with javac
switch (((Type.PrimTy) type).primkind()) {
case CHAR:
- return new Const.CharValue(value.asChar().value());
+ return new Const.CharValue((char) asInt(value));
case SHORT:
- return new Const.ShortValue(value.asShort().value());
+ return new Const.ShortValue((short) asInt(value));
case BOOLEAN:
// boolean constants are encoded as integers
- return new Const.BooleanValue(value.asInteger().value() != 0);
+ return new Const.BooleanValue(asInt(value) != 0);
case BYTE:
- return new Const.ByteValue(value.asByte().value());
+ return new Const.ByteValue((byte) asInt(value));
default:
return value;
}
}
+ private static int asInt(Value value) {
+ return ((Const.IntValue) value).value();
+ }
+
private static Const bindEnumValue(EnumConstValue value) {
return new EnumConstantValue(
new FieldSymbol(asClassSymbol(value.typeName()), value.constName()));
@@ -201,6 +208,7 @@ public final class BytecodeBinder {
public static ModuleInfo bindModuleInfo(String path, Supplier<byte[]> bytes) {
ClassFile classFile = ClassReader.read(path, bytes.get());
ClassFile.ModuleInfo module = classFile.module();
+ requireNonNull(module, path);
return new ModuleInfo(
module.name(),
module.version(),
diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
index 82cefc1..cc97dcb 100644
--- a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
+++ b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
@@ -58,7 +58,7 @@ import com.google.turbine.type.Type.IntersectionTy;
import java.lang.annotation.RetentionPolicy;
import java.util.Map;
import java.util.function.Function;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/**
* A bound class backed by a class file.
@@ -137,9 +137,8 @@ public class BytecodeBoundClass implements TypeBoundClass {
}
});
- @Nullable
@Override
- public ClassSymbol owner() {
+ public @Nullable ClassSymbol owner() {
return owner.get();
}
@@ -158,7 +157,7 @@ public class BytecodeBoundClass implements TypeBoundClass {
result.put(inner.innerName(), new ClassSymbol(inner.innerClass()));
}
}
- return result.build();
+ return result.buildOrThrow();
}
});
@@ -213,7 +212,7 @@ public class BytecodeBoundClass implements TypeBoundClass {
for (Sig.TyParamSig p : csig.tyParams()) {
result.put(p.name(), new TyVarSymbol(sym, p.name()));
}
- return result.build();
+ return result.buildOrThrow();
}
});
@@ -307,6 +306,11 @@ public class BytecodeBoundClass implements TypeBoundClass {
return interfaceTypes.get();
}
+ @Override
+ public ImmutableList<ClassSymbol> permits() {
+ return ImmutableList.of();
+ }
+
private final Supplier<ImmutableMap<TyVarSymbol, TyVarInfo>> typeParameterTypes =
Suppliers.memoize(
new Supplier<ImmutableMap<TyVarSymbol, TyVarInfo>>() {
@@ -321,7 +325,7 @@ public class BytecodeBoundClass implements TypeBoundClass {
// typeParameters() is constructed to guarantee the requireNonNull call is safe.
tparams.put(requireNonNull(typeParameters().get(p.name())), bindTyParam(p, scope));
}
- return tparams.build();
+ return tparams.buildOrThrow();
}
});
@@ -402,7 +406,7 @@ public class BytecodeBoundClass implements TypeBoundClass {
for (Sig.TyParamSig p : sig.tyParams()) {
result.put(p.name(), new TyVarSymbol(methodSymbol, p.name()));
}
- tyParams = result.build();
+ tyParams = result.buildOrThrow();
}
ImmutableMap<TyVarSymbol, TyVarInfo> tyParamTypes;
@@ -413,15 +417,12 @@ public class BytecodeBoundClass implements TypeBoundClass {
// tyParams is constructed to guarantee the requireNonNull call is safe.
tparams.put(requireNonNull(tyParams.get(p.name())), bindTyParam(p, scope));
}
- tyParamTypes = tparams.build();
+ tyParamTypes = tparams.buildOrThrow();
}
Function<String, TyVarSymbol> scope = makeScope(env, sym, tyParams);
- Type ret = null;
- if (sig.returnType() != null) {
- ret = BytecodeBinder.bindTy(sig.returnType(), scope);
- }
+ Type ret = BytecodeBinder.bindTy(sig.returnType(), scope);
ImmutableList.Builder<ParamInfo> formals = ImmutableList.builder();
int idx = 0;
@@ -490,6 +491,11 @@ public class BytecodeBoundClass implements TypeBoundClass {
return methods.get();
}
+ @Override
+ public ImmutableList<RecordComponentInfo> components() {
+ return ImmutableList.of();
+ }
+
private final Supplier<@Nullable AnnotationMetadata> annotationMetadata =
Suppliers.memoize(
new Supplier<@Nullable AnnotationMetadata>() {
diff --git a/java/com/google/turbine/binder/bytecode/package-info.java b/java/com/google/turbine/binder/bytecode/package-info.java
index 23c59f0..d2d9708 100644
--- a/java/com/google/turbine/binder/bytecode/package-info.java
+++ b/java/com/google/turbine/binder/bytecode/package-info.java
@@ -14,4 +14,5 @@
* limitations under the License.
*/
+@org.jspecify.nullness.NullMarked
package com.google.turbine.binder.bytecode;
diff --git a/java/com/google/turbine/binder/env/CompoundEnv.java b/java/com/google/turbine/binder/env/CompoundEnv.java
index 9b216e3..391a2c3 100644
--- a/java/com/google/turbine/binder/env/CompoundEnv.java
+++ b/java/com/google/turbine/binder/env/CompoundEnv.java
@@ -19,12 +19,12 @@ package com.google.turbine.binder.env;
import static java.util.Objects.requireNonNull;
import com.google.turbine.binder.sym.Symbol;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/** An {@link Env} that chains two existing envs together. */
public class CompoundEnv<S extends Symbol, V> implements Env<S, V> {
- private final Env<S, ? extends V> base;
+ private final @Nullable Env<S, ? extends V> base;
private final Env<S, ? extends V> env;
private CompoundEnv(@Nullable Env<S, ? extends V> base, Env<S, ? extends V> env) {
@@ -33,7 +33,7 @@ public class CompoundEnv<S extends Symbol, V> implements Env<S, V> {
}
@Override
- public V get(S sym) {
+ public @Nullable V get(S sym) {
V result = env.get(sym);
if (result != null) {
return result;
diff --git a/java/com/google/turbine/binder/env/Env.java b/java/com/google/turbine/binder/env/Env.java
index a78d3e6..463c65d 100644
--- a/java/com/google/turbine/binder/env/Env.java
+++ b/java/com/google/turbine/binder/env/Env.java
@@ -18,6 +18,7 @@ package com.google.turbine.binder.env;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.Symbol;
+import org.jspecify.nullness.Nullable;
/**
* An environment that maps {@link Symbol}s {@code S} to bound nodes {@code V}.
@@ -34,5 +35,14 @@ import com.google.turbine.binder.sym.Symbol;
*/
public interface Env<S extends Symbol, V> {
/** Returns the information associated with the given symbol in this environment. */
+ @Nullable
V get(S sym);
+
+ default V getNonNull(S sym) {
+ V result = get(sym);
+ if (result == null) {
+ throw new NullPointerException(sym.toString());
+ }
+ return result;
+ }
}
diff --git a/java/com/google/turbine/binder/env/LazyEnv.java b/java/com/google/turbine/binder/env/LazyEnv.java
index a9c3bd1..0b311f7 100644
--- a/java/com/google/turbine/binder/env/LazyEnv.java
+++ b/java/com/google/turbine/binder/env/LazyEnv.java
@@ -22,6 +22,7 @@ import com.google.turbine.binder.sym.Symbol;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
+import org.jspecify.nullness.Nullable;
/**
* An env that permits an analysis pass to access information about symbols from the current pass,
@@ -48,7 +49,7 @@ public class LazyEnv<S extends Symbol, T, V extends T> implements Env<S, V> {
private final ImmutableMap<S, Completer<S, T, V>> completers;
/** Values that have already been computed. */
- private final Map<S, V> cache = new LinkedHashMap<>();
+ private final Map<S, @Nullable V> cache = new LinkedHashMap<>();
/** An underlying env of already-computed {@code T}s that can be queried during completion. */
private final Env<S, T> rec;
@@ -59,7 +60,7 @@ public class LazyEnv<S extends Symbol, T, V extends T> implements Env<S, V> {
}
@Override
- public V get(S sym) {
+ public @Nullable V get(S sym) {
V v = cache.get(sym);
if (v != null) {
return v;
@@ -80,6 +81,7 @@ public class LazyEnv<S extends Symbol, T, V extends T> implements Env<S, V> {
/** A lazy value provider which is given access to the current environment. */
public interface Completer<S extends Symbol, T, V extends T> {
/** Provides the value for the given symbol in the current environment. */
+ @Nullable
V complete(Env<S, T> env, S k);
}
diff --git a/java/com/google/turbine/binder/env/SimpleEnv.java b/java/com/google/turbine/binder/env/SimpleEnv.java
index b07bf5f..9de5c9f 100644
--- a/java/com/google/turbine/binder/env/SimpleEnv.java
+++ b/java/com/google/turbine/binder/env/SimpleEnv.java
@@ -17,9 +17,11 @@
package com.google.turbine.binder.env;
import com.google.common.collect.ImmutableMap;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.turbine.binder.sym.Symbol;
import java.util.LinkedHashMap;
import java.util.Map;
+import org.jspecify.nullness.Nullable;
/** A simple {@link ImmutableMap}-backed {@link Env}. */
public class SimpleEnv<K extends Symbol, V> implements Env<K, V> {
@@ -42,7 +44,9 @@ public class SimpleEnv<K extends Symbol, V> implements Env<K, V> {
public static class Builder<K extends Symbol, V> {
private final Map<K, V> map = new LinkedHashMap<>();
- public V put(K sym, V v) {
+ // TODO(cushon): audit the cases where this return value is being ignored
+ @CanIgnoreReturnValue
+ public @Nullable V put(K sym, V v) {
return map.put(sym, v);
}
@@ -52,7 +56,7 @@ public class SimpleEnv<K extends Symbol, V> implements Env<K, V> {
}
@Override
- public V get(K sym) {
+ public @Nullable V get(K sym) {
return map.get(sym);
}
}
diff --git a/java/com/google/turbine/binder/env/package-info.java b/java/com/google/turbine/binder/env/package-info.java
new file mode 100644
index 0000000..fa57245
--- /dev/null
+++ b/java/com/google/turbine/binder/env/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+@org.jspecify.nullness.NullMarked
+package com.google.turbine.binder.env;
diff --git a/java/com/google/turbine/binder/lookup/CanonicalSymbolResolver.java b/java/com/google/turbine/binder/lookup/CanonicalSymbolResolver.java
index 1e33d5f..d44f4e4 100644
--- a/java/com/google/turbine/binder/lookup/CanonicalSymbolResolver.java
+++ b/java/com/google/turbine/binder/lookup/CanonicalSymbolResolver.java
@@ -18,11 +18,13 @@ package com.google.turbine.binder.lookup;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.tree.Tree;
+import org.jspecify.nullness.Nullable;
/** Canonical type resolution. Breaks a circular dependency between binding and import handling. */
public interface CanonicalSymbolResolver extends ImportScope.ResolveFunction {
/** Resolves a single member type of the given symbol by canonical name. */
@Override
+ @Nullable
ClassSymbol resolveOne(ClassSymbol sym, Tree.Ident bit);
/** Returns true if the given symbol is visible from the current package. */
diff --git a/java/com/google/turbine/binder/lookup/CompoundScope.java b/java/com/google/turbine/binder/lookup/CompoundScope.java
index 11309bf..bedf775 100644
--- a/java/com/google/turbine/binder/lookup/CompoundScope.java
+++ b/java/com/google/turbine/binder/lookup/CompoundScope.java
@@ -18,21 +18,21 @@ package com.google.turbine.binder.lookup;
import static com.google.common.base.Preconditions.checkNotNull;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/** A {@link Scope} that chains other scopes together. */
public class CompoundScope implements Scope {
private final Scope scope;
- @Nullable private final Scope base;
+ private final @Nullable Scope base;
- private CompoundScope(Scope scope, Scope base) {
+ private CompoundScope(Scope scope, @Nullable Scope base) {
this.scope = checkNotNull(scope);
this.base = base;
}
@Override
- public LookupResult lookup(LookupKey key) {
+ public @Nullable LookupResult lookup(LookupKey key) {
LookupResult result = scope.lookup(key);
if (result != null) {
return result;
diff --git a/java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java b/java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java
index b41edb0..e7fa45f 100644
--- a/java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java
+++ b/java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java
@@ -19,7 +19,7 @@ package com.google.turbine.binder.lookup;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.ImmutableList;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/** A {@link TopLevelIndex} that aggregates multiple indices into one. */
// Note: this implementation doesn't detect if the indices contain incompatible information,
@@ -42,9 +42,8 @@ public class CompoundTopLevelIndex implements TopLevelIndex {
private final Scope scope =
new Scope() {
- @Nullable
@Override
- public LookupResult lookup(LookupKey lookupKey) {
+ public @Nullable LookupResult lookup(LookupKey lookupKey) {
// Return the first matching symbol.
for (TopLevelIndex index : indexes) {
LookupResult result = index.scope().lookup(lookupKey);
@@ -62,14 +61,19 @@ public class CompoundTopLevelIndex implements TopLevelIndex {
}
@Override
- public PackageScope lookupPackage(Iterable<String> packagename) {
+ public @Nullable PackageScope lookupPackage(Iterable<String> packagename) {
// When returning package scopes, build up a compound scope containing entries from all
// indices with matching packages.
PackageScope result = null;
for (TopLevelIndex index : indexes) {
PackageScope packageScope = index.lookupPackage(packagename);
- if (packageScope != null) {
- result = result == null ? packageScope : result.append(packageScope);
+ if (packageScope == null) {
+ continue;
+ }
+ if (result == null) {
+ result = packageScope;
+ } else {
+ result = 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 fd57223..bcd9366 100644
--- a/java/com/google/turbine/binder/lookup/ImportIndex.java
+++ b/java/com/google/turbine/binder/lookup/ImportIndex.java
@@ -31,6 +31,7 @@ import com.google.turbine.tree.Tree.Ident;
import com.google.turbine.tree.Tree.ImportDecl;
import java.util.HashMap;
import java.util.Map;
+import org.jspecify.nullness.Nullable;
/**
* A scope that provides entries for the single-type imports in a compilation unit.
@@ -59,7 +60,7 @@ public class ImportIndex implements ImportScope {
CanonicalSymbolResolver resolve,
final TopLevelIndex cpi,
ImmutableList<ImportDecl> imports) {
- Map<String, Supplier<ImportScope>> thunks = new HashMap<>();
+ Map<String, Supplier<@Nullable ImportScope>> thunks = new HashMap<>();
for (final Tree.ImportDecl i : imports) {
if (i.stat() || i.wild()) {
continue;
@@ -67,9 +68,9 @@ public class ImportIndex implements ImportScope {
thunks.put(
getLast(i.type()).value(),
Suppliers.memoize(
- new Supplier<ImportScope>() {
+ new Supplier<@Nullable ImportScope>() {
@Override
- public ImportScope get() {
+ public @Nullable ImportScope get() {
return namedImport(log, cpi, i, resolve);
}
}));
@@ -84,9 +85,9 @@ public class ImportIndex implements ImportScope {
thunks.putIfAbsent(
last,
Suppliers.memoize(
- new Supplier<ImportScope>() {
+ new Supplier<@Nullable ImportScope>() {
@Override
- public ImportScope get() {
+ public @Nullable ImportScope get() {
return staticNamedImport(log, cpi, i);
}
}));
@@ -95,7 +96,7 @@ public class ImportIndex implements ImportScope {
}
/** Fully resolve the canonical name of a non-static named import. */
- private static ImportScope namedImport(
+ private static @Nullable ImportScope namedImport(
TurbineLogWithSource log, TopLevelIndex cpi, ImportDecl i, CanonicalSymbolResolver resolve) {
LookupResult result = cpi.scope().lookup(new LookupKey(i.type()));
if (result == null) {
@@ -119,7 +120,7 @@ public class ImportIndex implements ImportScope {
};
}
- private static ClassSymbol resolveNext(
+ private static @Nullable ClassSymbol resolveNext(
TurbineLogWithSource log, CanonicalSymbolResolver resolve, ClassSymbol sym, Ident bit) {
ClassSymbol next = resolve.resolveOne(sym, bit);
if (next == null) {
@@ -138,7 +139,7 @@ public class ImportIndex implements ImportScope {
* hierarchy analysis is complete, so for now we resolve the base {@code java.util.HashMap} and
* defer the rest.
*/
- private static ImportScope staticNamedImport(
+ private static @Nullable ImportScope staticNamedImport(
TurbineLogWithSource log, TopLevelIndex cpi, ImportDecl i) {
LookupResult base = cpi.scope().lookup(new LookupKey(i.type()));
if (base == null) {
@@ -148,7 +149,7 @@ public class ImportIndex implements ImportScope {
}
return new ImportScope() {
@Override
- public LookupResult lookup(LookupKey lookupKey, ResolveFunction resolve) {
+ public @Nullable LookupResult lookup(LookupKey lookupKey, ResolveFunction resolve) {
ClassSymbol sym = (ClassSymbol) base.sym();
for (Tree.Ident bit : base.remaining()) {
sym = resolve.resolveOne(sym, bit);
@@ -164,7 +165,7 @@ public class ImportIndex implements ImportScope {
}
@Override
- public LookupResult lookup(LookupKey lookup, ResolveFunction resolve) {
+ public @Nullable LookupResult lookup(LookupKey lookup, ResolveFunction resolve) {
Supplier<ImportScope> thunk = thunks.get(lookup.first().value());
if (thunk == null) {
return null;
diff --git a/java/com/google/turbine/binder/lookup/ImportScope.java b/java/com/google/turbine/binder/lookup/ImportScope.java
index 2e6917e..a33a8e2 100644
--- a/java/com/google/turbine/binder/lookup/ImportScope.java
+++ b/java/com/google/turbine/binder/lookup/ImportScope.java
@@ -18,6 +18,7 @@ package com.google.turbine.binder.lookup;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.tree.Tree;
+import org.jspecify.nullness.Nullable;
/**
* A scope for imports. Non-canonical imports depend on hierarchy analysis, so to break the cycle we
@@ -32,17 +33,19 @@ public interface ImportScope {
*/
@FunctionalInterface
interface ResolveFunction {
+ @Nullable
ClassSymbol resolveOne(ClassSymbol base, Tree.Ident name);
}
/** See {@link Scope#lookup(LookupKey)}. */
+ @Nullable
LookupResult lookup(LookupKey lookupKey, ResolveFunction resolve);
/** Adds a scope to the chain, in the manner of {@link CompoundScope#append(Scope)}. */
default ImportScope append(ImportScope next) {
return new ImportScope() {
@Override
- public LookupResult lookup(LookupKey lookupKey, ResolveFunction resolve) {
+ public @Nullable LookupResult lookup(LookupKey lookupKey, ResolveFunction resolve) {
LookupResult result = next.lookup(lookupKey, resolve);
if (result != null) {
return result;
@@ -60,7 +63,7 @@ public interface ImportScope {
static ImportScope fromScope(Scope scope) {
return new ImportScope() {
@Override
- public LookupResult lookup(LookupKey lookupKey, ResolveFunction resolve) {
+ public @Nullable LookupResult lookup(LookupKey lookupKey, ResolveFunction resolve) {
return scope.lookup(lookupKey);
}
};
@@ -71,7 +74,7 @@ public interface ImportScope {
return CompoundScope.base(
new Scope() {
@Override
- public LookupResult lookup(LookupKey lookupKey) {
+ public @Nullable LookupResult lookup(LookupKey lookupKey) {
return ImportScope.this.lookup(lookupKey, resolve);
}
});
diff --git a/java/com/google/turbine/binder/lookup/MemberImportIndex.java b/java/com/google/turbine/binder/lookup/MemberImportIndex.java
index a8ecc7a..d825396 100644
--- a/java/com/google/turbine/binder/lookup/MemberImportIndex.java
+++ b/java/com/google/turbine/binder/lookup/MemberImportIndex.java
@@ -30,21 +30,22 @@ import com.google.turbine.tree.Tree.ImportDecl;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
+import org.jspecify.nullness.Nullable;
/** An index for statically imported members, in particular constant variables. */
public class MemberImportIndex {
/** A cache of resolved static imports, keyed by the simple name of the member. */
- private final Map<String, Supplier<ClassSymbol>> cache = new LinkedHashMap<>();
+ private final Map<String, Supplier<@Nullable ClassSymbol>> cache = new LinkedHashMap<>();
- private final ImmutableList<Supplier<ClassSymbol>> classes;
+ private final ImmutableList<Supplier<@Nullable ClassSymbol>> classes;
public MemberImportIndex(
SourceFile source,
CanonicalSymbolResolver resolve,
TopLevelIndex tli,
ImmutableList<ImportDecl> imports) {
- ImmutableList.Builder<Supplier<ClassSymbol>> packageScopes = ImmutableList.builder();
+ ImmutableList.Builder<Supplier<@Nullable ClassSymbol>> packageScopes = ImmutableList.builder();
for (ImportDecl i : imports) {
if (!i.stat()) {
continue;
@@ -52,9 +53,9 @@ public class MemberImportIndex {
if (i.wild()) {
packageScopes.add(
Suppliers.memoize(
- new Supplier<ClassSymbol>() {
+ new Supplier<@Nullable ClassSymbol>() {
@Override
- public ClassSymbol get() {
+ public @Nullable ClassSymbol get() {
LookupResult result = tli.scope().lookup(new LookupKey(i.type()));
if (result == null) {
return null;
@@ -70,15 +71,18 @@ public class MemberImportIndex {
cache.put(
getLast(i.type()).value(),
Suppliers.memoize(
- new Supplier<ClassSymbol>() {
+ new Supplier<@Nullable ClassSymbol>() {
@Override
- public ClassSymbol get() {
+ public @Nullable ClassSymbol get() {
LookupResult result = tli.scope().lookup(new LookupKey(i.type()));
if (result == null) {
return null;
}
ClassSymbol sym = (ClassSymbol) result.sym();
for (int i = 0; i < result.remaining().size() - 1; i++) {
+ if (sym == null) {
+ return null;
+ }
sym = resolve.resolveOne(sym, result.remaining().get(i));
}
return sym;
@@ -107,8 +111,8 @@ public class MemberImportIndex {
}
/** Resolves the owner of a single-member static import of the given simple name. */
- public ClassSymbol singleMemberImport(String simpleName) {
- Supplier<ClassSymbol> cachedResult = cache.get(simpleName);
+ public @Nullable ClassSymbol singleMemberImport(String simpleName) {
+ Supplier<@Nullable ClassSymbol> cachedResult = cache.get(simpleName);
return cachedResult != null ? cachedResult.get() : null;
}
diff --git a/java/com/google/turbine/binder/lookup/PackageScope.java b/java/com/google/turbine/binder/lookup/PackageScope.java
index 695e802..94e950f 100644
--- a/java/com/google/turbine/binder/lookup/PackageScope.java
+++ b/java/com/google/turbine/binder/lookup/PackageScope.java
@@ -18,7 +18,7 @@ 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;
+import org.jspecify.nullness.Nullable;
/**
* A scope that corresponds to a particular package, which supports iteration over its enclosed
diff --git a/java/com/google/turbine/binder/lookup/Scope.java b/java/com/google/turbine/binder/lookup/Scope.java
index 12466f4..eb9f5cb 100644
--- a/java/com/google/turbine/binder/lookup/Scope.java
+++ b/java/com/google/turbine/binder/lookup/Scope.java
@@ -16,7 +16,7 @@
package com.google.turbine.binder.lookup;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/** A scope that defines types, and supports qualified name resolution. */
public interface Scope {
diff --git a/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java b/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java
index 4ec05bc..179f603 100644
--- a/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java
+++ b/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java
@@ -23,7 +23,7 @@ import com.google.turbine.binder.sym.ClassSymbol;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/**
* An index of canonical type names where all members are known statically.
@@ -36,17 +36,17 @@ public class SimpleTopLevelIndex implements TopLevelIndex {
/** A class symbol or package. */
public static class Node {
- public Node lookup(String bit) {
+ public @Nullable Node lookup(String bit) {
return children.get(bit);
}
- @Nullable private final ClassSymbol sym;
+ private final @Nullable ClassSymbol sym;
// TODO(cushon): the set of children is typically going to be small, consider optimizing this
// to use a denser representation where appropriate.
private final Map<String, Node> children = new HashMap<>();
- Node(ClassSymbol sym) {
+ Node(@Nullable ClassSymbol sym) {
this.sym = sym;
}
@@ -56,7 +56,7 @@ public class SimpleTopLevelIndex implements TopLevelIndex {
*
* @return {@code null} if an existing symbol with the same name has already been inserted.
*/
- private Node insert(String name, ClassSymbol sym) {
+ private @Nullable Node insert(String name, @Nullable ClassSymbol sym) {
Node child = children.get(name);
if (child != null) {
if (child.sym != null) {
@@ -83,7 +83,7 @@ public class SimpleTopLevelIndex implements TopLevelIndex {
final Node root = new Node(null);
/** Inserts a {@link ClassSymbol} into the index, creating any needed packages. */
- public boolean insert(ClassSymbol sym) {
+ public void insert(ClassSymbol sym) {
String binaryName = sym.binaryName();
int start = 0;
int end = binaryName.indexOf('/');
@@ -95,7 +95,7 @@ public class SimpleTopLevelIndex implements TopLevelIndex {
// symbol), bail out. When inserting elements from the classpath, this results in the
// expected first-match-wins semantics.
if (curr == null) {
- return false;
+ return;
}
start = end + 1;
end = binaryName.indexOf('/', start);
@@ -103,9 +103,9 @@ public class SimpleTopLevelIndex implements TopLevelIndex {
String simpleName = binaryName.substring(start);
curr = curr.insert(simpleName, sym);
if (curr == null || !Objects.equals(curr.sym, sym)) {
- return false;
+ return;
}
- return true;
+ return;
}
}
@@ -133,8 +133,7 @@ public class SimpleTopLevelIndex implements TopLevelIndex {
final Scope scope =
new Scope() {
@Override
- @Nullable
- public LookupResult lookup(LookupKey lookupKey) {
+ public @Nullable LookupResult lookup(LookupKey lookupKey) {
Node curr = root;
while (true) {
curr = curr.lookup(lookupKey.first().value());
@@ -159,7 +158,7 @@ public class SimpleTopLevelIndex implements TopLevelIndex {
/** Returns a {@link Scope} that performs lookups in the given qualified package name. */
@Override
- public PackageScope lookupPackage(Iterable<String> packagename) {
+ public @Nullable PackageScope lookupPackage(Iterable<String> packagename) {
Node curr = root;
for (String bit : packagename) {
curr = curr.lookup(bit);
@@ -179,7 +178,7 @@ public class SimpleTopLevelIndex implements TopLevelIndex {
}
@Override
- public LookupResult lookup(LookupKey lookupKey) {
+ public @Nullable LookupResult lookup(LookupKey lookupKey) {
Node result = node.lookup(lookupKey.first().value());
if (result != null && result.sym != null) {
return new LookupResult(result.sym, lookupKey);
diff --git a/java/com/google/turbine/binder/lookup/TopLevelIndex.java b/java/com/google/turbine/binder/lookup/TopLevelIndex.java
index a364119..049ac5c 100644
--- a/java/com/google/turbine/binder/lookup/TopLevelIndex.java
+++ b/java/com/google/turbine/binder/lookup/TopLevelIndex.java
@@ -16,6 +16,7 @@
package com.google.turbine.binder.lookup;
+import org.jspecify.nullness.Nullable;
/**
* An index of canonical type names.
@@ -35,5 +36,6 @@ public interface TopLevelIndex {
Scope scope();
/** Returns a scope to look up members of the given package. */
+ @Nullable
PackageScope lookupPackage(Iterable<String> packagename);
}
diff --git a/java/com/google/turbine/binder/lookup/WildImportIndex.java b/java/com/google/turbine/binder/lookup/WildImportIndex.java
index cfe58c7..8b4bab1 100644
--- a/java/com/google/turbine/binder/lookup/WildImportIndex.java
+++ b/java/com/google/turbine/binder/lookup/WildImportIndex.java
@@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableList;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.tree.Tree;
import com.google.turbine.tree.Tree.ImportDecl;
+import org.jspecify.nullness.Nullable;
/**
* A scope that provides best-effort lookup for on-demand imported types in a compilation unit.
@@ -45,14 +46,14 @@ public class WildImportIndex implements ImportScope {
CanonicalSymbolResolver importResolver,
final TopLevelIndex cpi,
ImmutableList<ImportDecl> imports) {
- ImmutableList.Builder<Supplier<ImportScope>> packageScopes = ImmutableList.builder();
+ ImmutableList.Builder<Supplier<@Nullable ImportScope>> packageScopes = ImmutableList.builder();
for (final ImportDecl i : imports) {
if (i.wild()) {
packageScopes.add(
Suppliers.memoize(
- new Supplier<ImportScope>() {
+ new Supplier<@Nullable ImportScope>() {
@Override
- public ImportScope get() {
+ public @Nullable ImportScope get() {
if (i.stat()) {
return staticOnDemandImport(cpi, i, importResolver);
} else {
@@ -66,7 +67,7 @@ public class WildImportIndex implements ImportScope {
}
/** Full resolve the type for a non-static on-demand import. */
- private static ImportScope onDemandImport(
+ private static @Nullable ImportScope onDemandImport(
TopLevelIndex cpi, ImportDecl i, final CanonicalSymbolResolver importResolver) {
ImmutableList.Builder<String> flatNames = ImmutableList.builder();
for (Tree.Ident ident : i.type()) {
@@ -77,7 +78,7 @@ public class WildImportIndex implements ImportScope {
// a wildcard import of a package
return new ImportScope() {
@Override
- public LookupResult lookup(LookupKey lookupKey, ResolveFunction resolve) {
+ public @Nullable LookupResult lookup(LookupKey lookupKey, ResolveFunction resolve) {
return packageIndex.lookup(lookupKey);
}
};
@@ -92,7 +93,7 @@ public class WildImportIndex implements ImportScope {
}
return new ImportScope() {
@Override
- public LookupResult lookup(LookupKey lookupKey, ResolveFunction unused) {
+ public @Nullable LookupResult lookup(LookupKey lookupKey, ResolveFunction unused) {
return resolveMember(member, importResolver, importResolver, lookupKey);
}
};
@@ -103,7 +104,7 @@ public class WildImportIndex implements ImportScope {
* ImportScope#staticNamedImport} for an explanation of why the possibly non-canonical part is
* deferred).
*/
- private static ImportScope staticOnDemandImport(
+ private static @Nullable ImportScope staticOnDemandImport(
TopLevelIndex cpi, ImportDecl i, final CanonicalSymbolResolver importResolver) {
LookupResult result = cpi.scope().lookup(new LookupKey(i.type()));
if (result == null) {
@@ -111,7 +112,7 @@ public class WildImportIndex implements ImportScope {
}
return new ImportScope() {
@Override
- public LookupResult lookup(LookupKey lookupKey, ResolveFunction resolve) {
+ public @Nullable LookupResult lookup(LookupKey lookupKey, ResolveFunction resolve) {
ClassSymbol member = resolveImportBase(result, resolve, importResolver);
if (member == null) {
return null;
@@ -121,7 +122,7 @@ public class WildImportIndex implements ImportScope {
};
}
- private static LookupResult resolveMember(
+ private static @Nullable LookupResult resolveMember(
ClassSymbol base,
ResolveFunction resolve,
CanonicalSymbolResolver importResolver,
@@ -136,7 +137,7 @@ public class WildImportIndex implements ImportScope {
return new LookupResult(member, lookupKey);
}
- static ClassSymbol resolveImportBase(
+ static @Nullable ClassSymbol resolveImportBase(
LookupResult result, ResolveFunction resolve, CanonicalSymbolResolver importResolver) {
ClassSymbol member = (ClassSymbol) result.sym();
for (Tree.Ident bit : result.remaining()) {
@@ -152,7 +153,7 @@ public class WildImportIndex implements ImportScope {
}
@Override
- public LookupResult lookup(LookupKey lookup, ResolveFunction resolve) {
+ public @Nullable LookupResult lookup(LookupKey lookup, ResolveFunction resolve) {
for (Supplier<ImportScope> packageScope : packages) {
ImportScope scope = packageScope.get();
if (scope == null) {
diff --git a/java/com/google/turbine/binder/lookup/package-info.java b/java/com/google/turbine/binder/lookup/package-info.java
new file mode 100644
index 0000000..7784138
--- /dev/null
+++ b/java/com/google/turbine/binder/lookup/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+@org.jspecify.nullness.NullMarked
+package com.google.turbine.binder.lookup;
diff --git a/java/com/google/turbine/binder/package-info.java b/java/com/google/turbine/binder/package-info.java
new file mode 100644
index 0000000..9f550e0
--- /dev/null
+++ b/java/com/google/turbine/binder/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+@org.jspecify.nullness.NullMarked
+package com.google.turbine.binder;
diff --git a/java/com/google/turbine/binder/sym/ClassSymbol.java b/java/com/google/turbine/binder/sym/ClassSymbol.java
index 20513e7..9bb556f 100644
--- a/java/com/google/turbine/binder/sym/ClassSymbol.java
+++ b/java/com/google/turbine/binder/sym/ClassSymbol.java
@@ -17,6 +17,7 @@
package com.google.turbine.binder.sym;
import com.google.errorprone.annotations.Immutable;
+import org.jspecify.nullness.Nullable;
/**
* A class symbol.
@@ -32,6 +33,7 @@ public class ClassSymbol implements Symbol {
public static final ClassSymbol OBJECT = new ClassSymbol("java/lang/Object");
public static final ClassSymbol STRING = new ClassSymbol("java/lang/String");
public static final ClassSymbol ENUM = new ClassSymbol("java/lang/Enum");
+ public static final ClassSymbol RECORD = new ClassSymbol("java/lang/Record");
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");
@@ -68,7 +70,7 @@ public class ClassSymbol implements Symbol {
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return o instanceof ClassSymbol && className.equals(((ClassSymbol) o).className);
}
diff --git a/java/com/google/turbine/binder/sym/FieldSymbol.java b/java/com/google/turbine/binder/sym/FieldSymbol.java
index d6c3cbc..1040500 100644
--- a/java/com/google/turbine/binder/sym/FieldSymbol.java
+++ b/java/com/google/turbine/binder/sym/FieldSymbol.java
@@ -18,6 +18,7 @@ package com.google.turbine.binder.sym;
import com.google.errorprone.annotations.Immutable;
import java.util.Objects;
+import org.jspecify.nullness.Nullable;
/** A field symbol. */
@Immutable
@@ -51,7 +52,7 @@ public class FieldSymbol implements Symbol {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof FieldSymbol)) {
return false;
}
diff --git a/java/com/google/turbine/binder/sym/MethodSymbol.java b/java/com/google/turbine/binder/sym/MethodSymbol.java
index f4b211d..12c1aa5 100644
--- a/java/com/google/turbine/binder/sym/MethodSymbol.java
+++ b/java/com/google/turbine/binder/sym/MethodSymbol.java
@@ -18,6 +18,7 @@ package com.google.turbine.binder.sym;
import com.google.errorprone.annotations.Immutable;
import java.util.Objects;
+import org.jspecify.nullness.Nullable;
/** A method symbol. */
@Immutable
@@ -58,7 +59,7 @@ public class MethodSymbol implements Symbol {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof MethodSymbol)) {
return false;
}
diff --git a/java/com/google/turbine/binder/sym/ModuleSymbol.java b/java/com/google/turbine/binder/sym/ModuleSymbol.java
index e442353..4ce5c7a 100644
--- a/java/com/google/turbine/binder/sym/ModuleSymbol.java
+++ b/java/com/google/turbine/binder/sym/ModuleSymbol.java
@@ -17,6 +17,7 @@
package com.google.turbine.binder.sym;
import com.google.errorprone.annotations.Immutable;
+import org.jspecify.nullness.Nullable;
/** A module symbol. */
@Immutable
@@ -43,7 +44,7 @@ public class ModuleSymbol implements Symbol {
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
return other instanceof ModuleSymbol && name.equals(((ModuleSymbol) other).name);
}
diff --git a/java/com/google/turbine/binder/sym/PackageSymbol.java b/java/com/google/turbine/binder/sym/PackageSymbol.java
index 8354a34..be071e0 100644
--- a/java/com/google/turbine/binder/sym/PackageSymbol.java
+++ b/java/com/google/turbine/binder/sym/PackageSymbol.java
@@ -17,6 +17,7 @@
package com.google.turbine.binder.sym;
import com.google.errorprone.annotations.Immutable;
+import org.jspecify.nullness.Nullable;
/** A package symbol. */
@Immutable
@@ -34,7 +35,7 @@ public class PackageSymbol implements Symbol {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof PackageSymbol && binaryName.equals(((PackageSymbol) obj).binaryName);
}
diff --git a/java/com/google/turbine/binder/sym/ParamSymbol.java b/java/com/google/turbine/binder/sym/ParamSymbol.java
index 328658e..e939223 100644
--- a/java/com/google/turbine/binder/sym/ParamSymbol.java
+++ b/java/com/google/turbine/binder/sym/ParamSymbol.java
@@ -18,6 +18,7 @@ package com.google.turbine.binder.sym;
import com.google.errorprone.annotations.Immutable;
import java.util.Objects;
+import org.jspecify.nullness.Nullable;
/** A parameter symbol. */
@Immutable
@@ -51,7 +52,7 @@ public class ParamSymbol implements Symbol {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof ParamSymbol)) {
return false;
}
diff --git a/java/com/google/turbine/binder/sym/RecordComponentSymbol.java b/java/com/google/turbine/binder/sym/RecordComponentSymbol.java
new file mode 100644
index 0000000..c3f44f6
--- /dev/null
+++ b/java/com/google/turbine/binder/sym/RecordComponentSymbol.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.turbine.binder.sym;
+
+import com.google.errorprone.annotations.Immutable;
+import java.util.Objects;
+import org.jspecify.nullness.Nullable;
+
+/** A record component symbol. */
+@Immutable
+public class RecordComponentSymbol implements Symbol {
+ private final ClassSymbol owner;
+ private final String name;
+
+ public RecordComponentSymbol(ClassSymbol owner, String name) {
+ this.owner = owner;
+ this.name = name;
+ }
+
+ /** The enclosing class. */
+ public ClassSymbol owner() {
+ return owner;
+ }
+
+ /** The parameter name. */
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public Kind symKind() {
+ return Kind.RECORD_COMPONENT;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, owner);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof RecordComponentSymbol)) {
+ return false;
+ }
+ RecordComponentSymbol other = (RecordComponentSymbol) 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 bc142cb..b1eb8e1 100644
--- a/java/com/google/turbine/binder/sym/Symbol.java
+++ b/java/com/google/turbine/binder/sym/Symbol.java
@@ -28,6 +28,7 @@ public interface Symbol {
METHOD,
FIELD,
PARAMETER,
+ RECORD_COMPONENT,
MODULE,
PACKAGE
}
diff --git a/java/com/google/turbine/binder/sym/TyVarSymbol.java b/java/com/google/turbine/binder/sym/TyVarSymbol.java
index 1ecec11..5ba0788 100644
--- a/java/com/google/turbine/binder/sym/TyVarSymbol.java
+++ b/java/com/google/turbine/binder/sym/TyVarSymbol.java
@@ -18,6 +18,7 @@ package com.google.turbine.binder.sym;
import com.google.errorprone.annotations.Immutable;
import java.util.Objects;
+import org.jspecify.nullness.Nullable;
/** A type variable symbol. */
@Immutable
@@ -52,7 +53,7 @@ public class TyVarSymbol implements Symbol {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof TyVarSymbol)) {
return false;
}
diff --git a/java/com/google/turbine/binder/sym/package-info.java b/java/com/google/turbine/binder/sym/package-info.java
new file mode 100644
index 0000000..96f3a87
--- /dev/null
+++ b/java/com/google/turbine/binder/sym/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+@org.jspecify.nullness.NullMarked
+package com.google.turbine.binder.sym;
diff --git a/java/com/google/turbine/bytecode/AnnotationWriter.java b/java/com/google/turbine/bytecode/AnnotationWriter.java
index 34d6262..ccae0f1 100644
--- a/java/com/google/turbine/bytecode/AnnotationWriter.java
+++ b/java/com/google/turbine/bytecode/AnnotationWriter.java
@@ -33,6 +33,7 @@ import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.ThrowsTarget;
import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.TypeParameterBoundTarget;
import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.TypeParameterTarget;
import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.TypePath;
+import com.google.turbine.model.Const;
import com.google.turbine.model.Const.Value;
import java.util.Map;
@@ -79,31 +80,31 @@ public class AnnotationWriter {
private void writeConstElementValue(Value value) {
switch (value.constantTypeKind()) {
case BYTE:
- writeConst('B', pool.integer(value.asInteger().value()));
+ writeConst('B', pool.integer(((Const.ByteValue) value).value()));
break;
case CHAR:
- writeConst('C', pool.integer(value.asInteger().value()));
+ writeConst('C', pool.integer(((Const.CharValue) value).value()));
break;
case SHORT:
- writeConst('S', pool.integer(value.asInteger().value()));
+ writeConst('S', pool.integer(((Const.ShortValue) value).value()));
break;
case DOUBLE:
- writeConst('D', pool.doubleInfo(value.asDouble().value()));
+ writeConst('D', pool.doubleInfo(((Const.DoubleValue) value).value()));
break;
case FLOAT:
- writeConst('F', pool.floatInfo(value.asFloat().value()));
+ writeConst('F', pool.floatInfo(((Const.FloatValue) value).value()));
break;
case INT:
- writeConst('I', pool.integer(value.asInteger().value()));
+ writeConst('I', pool.integer(((Const.IntValue) value).value()));
break;
case LONG:
- writeConst('J', pool.longInfo(value.asLong().value()));
+ writeConst('J', pool.longInfo(((Const.LongValue) value).value()));
break;
case STRING:
- writeConst('s', pool.utf8(value.asString().value()));
+ writeConst('s', pool.utf8(((Const.StringValue) value).value()));
break;
case BOOLEAN:
- writeConst('Z', pool.integer(value.asBoolean().value() ? 1 : 0));
+ writeConst('Z', pool.integer(((Const.BooleanValue) value).value() ? 1 : 0));
break;
default:
throw new AssertionError(value.constantTypeKind());
diff --git a/java/com/google/turbine/bytecode/Attribute.java b/java/com/google/turbine/bytecode/Attribute.java
index 7b415a7..ad6ffc1 100644
--- a/java/com/google/turbine/bytecode/Attribute.java
+++ b/java/com/google/turbine/bytecode/Attribute.java
@@ -42,7 +42,11 @@ interface Attribute {
RUNTIME_INVISIBLE_TYPE_ANNOTATIONS("RuntimeInvisibleTypeAnnotations"),
METHOD_PARAMETERS("MethodParameters"),
MODULE("Module"),
- TURBINE_TRANSITIVE_JAR("TurbineTransitiveJar");
+ NEST_HOST("NestHost"),
+ NEST_MEMBERS("NestMembers"),
+ RECORD("Record"),
+ TURBINE_TRANSITIVE_JAR("TurbineTransitiveJar"),
+ PERMITTED_SUBCLASSES("PermittedSubclasses");
private final String signature;
@@ -311,6 +315,102 @@ interface Attribute {
}
}
+ /** A JVMS §4.7.28 NestHost attribute. */
+ class NestHost implements Attribute {
+
+ private final String hostClass;
+
+ public NestHost(String hostClass) {
+ this.hostClass = hostClass;
+ }
+
+ String hostClass() {
+ return hostClass;
+ }
+
+ @Override
+ public Kind kind() {
+ return Kind.NEST_HOST;
+ }
+ }
+
+ /** A JVMS §4.7.29 NestHost attribute. */
+ class NestMembers implements Attribute {
+
+ private final ImmutableList<String> classes;
+
+ public NestMembers(ImmutableList<String> classes) {
+ this.classes = classes;
+ }
+
+ ImmutableList<String> classes() {
+ return classes;
+ }
+
+ @Override
+ public Kind kind() {
+ return Kind.NEST_MEMBERS;
+ }
+ }
+
+ /** A JVMS §4.7.30 Record attribute. */
+ class Record implements Attribute {
+
+ private final ImmutableList<Component> components;
+
+ public Record(ImmutableList<Component> components) {
+ this.components = components;
+ }
+
+ @Override
+ public Kind kind() {
+ return Kind.RECORD;
+ }
+
+ ImmutableList<Component> components() {
+ return components;
+ }
+
+ /** A JVMS §4.7.30 Record component info. */
+ static class Component {
+ private final String name;
+ private final String descriptor;
+ private final List<Attribute> attributes;
+
+ Component(String name, String descriptor, List<Attribute> attributes) {
+ this.name = name;
+ this.descriptor = descriptor;
+ this.attributes = attributes;
+ }
+
+ String name() {
+ return name;
+ }
+
+ String descriptor() {
+ return descriptor;
+ }
+
+ List<Attribute> attributes() {
+ return attributes;
+ }
+ }
+ }
+
+ /** A JVMS §4.7.31 PermittedSubclasses attribute. */
+ class PermittedSubclasses implements Attribute {
+ final List<String> permits;
+
+ public PermittedSubclasses(List<String> permits) {
+ this.permits = permits;
+ }
+
+ @Override
+ public Kind kind() {
+ return Kind.PERMITTED_SUBCLASSES;
+ }
+ }
+
/** A custom attribute for recording the original jar of repackaged transitive classes. */
class TurbineTransitiveJar implements Attribute {
diff --git a/java/com/google/turbine/bytecode/AttributeWriter.java b/java/com/google/turbine/bytecode/AttributeWriter.java
index 84ca55f..6aac19a 100644
--- a/java/com/google/turbine/bytecode/AttributeWriter.java
+++ b/java/com/google/turbine/bytecode/AttributeWriter.java
@@ -42,59 +42,69 @@ import java.util.List;
public class AttributeWriter {
private final ConstantPool pool;
- private final ByteArrayDataOutput output;
- public AttributeWriter(ConstantPool pool, ByteArrayDataOutput output) {
+ public AttributeWriter(ConstantPool pool) {
this.pool = pool;
- this.output = output;
}
/** Writes a single attribute. */
- public void write(Attribute attribute) {
+ public void write(ByteArrayDataOutput output, Attribute attribute) {
switch (attribute.kind()) {
case SIGNATURE:
- writeSignatureAttribute((Signature) attribute);
+ writeSignatureAttribute(output, (Signature) attribute);
break;
case EXCEPTIONS:
- writeExceptionsAttribute((ExceptionsAttribute) attribute);
+ writeExceptionsAttribute(output, (ExceptionsAttribute) attribute);
break;
case INNER_CLASSES:
- writeInnerClasses((InnerClasses) attribute);
+ writeInnerClasses(output, (InnerClasses) attribute);
break;
case CONSTANT_VALUE:
- writeConstantValue((ConstantValue) attribute);
+ writeConstantValue(output, (ConstantValue) attribute);
break;
case RUNTIME_VISIBLE_ANNOTATIONS:
case RUNTIME_INVISIBLE_ANNOTATIONS:
- writeAnnotation((Attribute.Annotations) attribute);
+ writeAnnotation(output, (Attribute.Annotations) attribute);
break;
case ANNOTATION_DEFAULT:
- writeAnnotationDefault((Attribute.AnnotationDefault) attribute);
+ writeAnnotationDefault(output, (Attribute.AnnotationDefault) attribute);
break;
case RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS:
case RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS:
- writeParameterAnnotations((Attribute.ParameterAnnotations) attribute);
+ writeParameterAnnotations(output, (Attribute.ParameterAnnotations) attribute);
break;
case DEPRECATED:
- writeDeprecated(attribute);
+ writeDeprecated(output, attribute);
break;
case RUNTIME_INVISIBLE_TYPE_ANNOTATIONS:
case RUNTIME_VISIBLE_TYPE_ANNOTATIONS:
- writeTypeAnnotation((Attribute.TypeAnnotations) attribute);
+ writeTypeAnnotation(output, (Attribute.TypeAnnotations) attribute);
break;
case METHOD_PARAMETERS:
- writeMethodParameters((Attribute.MethodParameters) attribute);
+ writeMethodParameters(output, (Attribute.MethodParameters) attribute);
break;
case MODULE:
- writeModule((Attribute.Module) attribute);
+ writeModule(output, (Attribute.Module) attribute);
+ break;
+ case NEST_HOST:
+ writeNestHost(output, (Attribute.NestHost) attribute);
+ break;
+ case NEST_MEMBERS:
+ writeNestMembers(output, (Attribute.NestMembers) attribute);
+ break;
+ case RECORD:
+ writeRecord(output, (Attribute.Record) attribute);
+ break;
+ case PERMITTED_SUBCLASSES:
+ writePermittedSubclasses(output, (Attribute.PermittedSubclasses) attribute);
break;
case TURBINE_TRANSITIVE_JAR:
- writeTurbineTransitiveJar((Attribute.TurbineTransitiveJar) attribute);
+ writeTurbineTransitiveJar(output, (Attribute.TurbineTransitiveJar) attribute);
break;
}
}
- private void writeInnerClasses(InnerClasses attribute) {
+ private void writeInnerClasses(ByteArrayDataOutput output, InnerClasses attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
output.writeInt(attribute.inners.size() * 8 + 2);
output.writeShort(attribute.inners.size());
@@ -106,7 +116,7 @@ public class AttributeWriter {
}
}
- private void writeExceptionsAttribute(ExceptionsAttribute attribute) {
+ private void writeExceptionsAttribute(ByteArrayDataOutput output, ExceptionsAttribute attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
output.writeInt(2 + attribute.exceptions.size() * 2);
output.writeShort(attribute.exceptions.size());
@@ -115,44 +125,50 @@ public class AttributeWriter {
}
}
- private void writeSignatureAttribute(Signature attribute) {
+ private void writeSignatureAttribute(ByteArrayDataOutput output, Signature attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
output.writeInt(2);
output.writeShort(pool.utf8(attribute.signature));
}
- public void writeConstantValue(ConstantValue attribute) {
+ public void writeConstantValue(ByteArrayDataOutput output, ConstantValue attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
output.writeInt(2);
Const.Value value = attribute.value;
switch (value.constantTypeKind()) {
case INT:
+ output.writeShort(pool.integer(((Const.IntValue) value).value()));
+ break;
case CHAR:
+ output.writeShort(pool.integer(((Const.CharValue) value).value()));
+ break;
case SHORT:
+ output.writeShort(pool.integer(((Const.ShortValue) value).value()));
+ break;
case BYTE:
- output.writeShort(pool.integer(value.asInteger().value()));
+ output.writeShort(pool.integer(((Const.ByteValue) value).value()));
break;
case LONG:
- output.writeShort(pool.longInfo(value.asLong().value()));
+ output.writeShort(pool.longInfo(((Const.LongValue) value).value()));
break;
case DOUBLE:
- output.writeShort(pool.doubleInfo(value.asDouble().value()));
+ output.writeShort(pool.doubleInfo(((Const.DoubleValue) value).value()));
break;
case FLOAT:
- output.writeShort(pool.floatInfo(value.asFloat().value()));
+ output.writeShort(pool.floatInfo(((Const.FloatValue) value).value()));
break;
case BOOLEAN:
- output.writeShort(pool.integer(value.asBoolean().value() ? 1 : 0));
+ output.writeShort(pool.integer(((Const.BooleanValue) value).value() ? 1 : 0));
break;
case STRING:
- output.writeShort(pool.string(value.asString().value()));
+ output.writeShort(pool.string(((Const.StringValue) value).value()));
break;
default:
throw new AssertionError(value.constantTypeKind());
}
}
- public void writeAnnotation(Annotations attribute) {
+ public void writeAnnotation(ByteArrayDataOutput output, Annotations attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
tmp.writeShort(attribute.annotations().size());
@@ -164,7 +180,8 @@ public class AttributeWriter {
output.write(data);
}
- public void writeAnnotationDefault(Attribute.AnnotationDefault attribute) {
+ public void writeAnnotationDefault(
+ ByteArrayDataOutput output, Attribute.AnnotationDefault attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
new AnnotationWriter(pool, tmp).writeElementValue(attribute.value());
@@ -173,7 +190,8 @@ public class AttributeWriter {
output.write(data);
}
- public void writeParameterAnnotations(Attribute.ParameterAnnotations attribute) {
+ public void writeParameterAnnotations(
+ ByteArrayDataOutput output, Attribute.ParameterAnnotations attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
tmp.writeByte(attribute.annotations().size());
@@ -188,12 +206,12 @@ public class AttributeWriter {
output.write(data);
}
- private void writeDeprecated(Attribute attribute) {
+ private void writeDeprecated(ByteArrayDataOutput output, Attribute attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
output.writeInt(0);
}
- private void writeTypeAnnotation(TypeAnnotations attribute) {
+ private void writeTypeAnnotation(ByteArrayDataOutput output, TypeAnnotations attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
tmp.writeShort(attribute.annotations().size());
@@ -205,7 +223,7 @@ public class AttributeWriter {
output.write(data);
}
- private void writeMethodParameters(MethodParameters attribute) {
+ private void writeMethodParameters(ByteArrayDataOutput output, MethodParameters attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
output.writeInt(attribute.parameters().size() * 4 + 1);
output.writeByte(attribute.parameters().size());
@@ -215,7 +233,7 @@ public class AttributeWriter {
}
}
- private void writeModule(Attribute.Module attribute) {
+ private void writeModule(ByteArrayDataOutput output, Attribute.Module attribute) {
ModuleInfo module = attribute.module();
ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
@@ -271,7 +289,50 @@ public class AttributeWriter {
output.write(data);
}
- private void writeTurbineTransitiveJar(TurbineTransitiveJar attribute) {
+ private void writeNestHost(ByteArrayDataOutput output, Attribute.NestHost attribute) {
+ output.writeShort(pool.utf8(attribute.kind().signature()));
+ output.writeInt(2);
+ output.writeShort(pool.classInfo(attribute.hostClass()));
+ }
+
+ private void writeNestMembers(ByteArrayDataOutput output, Attribute.NestMembers attribute) {
+ output.writeShort(pool.utf8(attribute.kind().signature()));
+ output.writeInt(2 + attribute.classes().size() * 2);
+ output.writeShort(attribute.classes().size());
+ for (String classes : attribute.classes()) {
+ output.writeShort(pool.classInfo(classes));
+ }
+ }
+
+ private void writeRecord(ByteArrayDataOutput output, Attribute.Record attribute) {
+ output.writeShort(pool.utf8(attribute.kind().signature()));
+ ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
+ tmp.writeShort(attribute.components().size());
+ for (Attribute.Record.Component c : attribute.components()) {
+ tmp.writeShort(pool.utf8(c.name()));
+ tmp.writeShort(pool.utf8(c.descriptor()));
+ tmp.writeShort(c.attributes().size());
+ for (Attribute a : c.attributes()) {
+ write(tmp, a);
+ }
+ }
+ byte[] data = tmp.toByteArray();
+ output.writeInt(data.length);
+ output.write(data);
+ }
+
+ private void writePermittedSubclasses(
+ ByteArrayDataOutput output, Attribute.PermittedSubclasses attribute) {
+ output.writeShort(pool.utf8(attribute.kind().signature()));
+ output.writeInt(2 + attribute.permits.size() * 2);
+ output.writeShort(attribute.permits.size());
+ for (String permits : attribute.permits) {
+ output.writeShort(pool.classInfo(permits));
+ }
+ }
+
+ private void writeTurbineTransitiveJar(
+ ByteArrayDataOutput output, TurbineTransitiveJar attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
output.writeInt(2);
output.writeShort(pool.utf8(attribute.transitiveJar));
diff --git a/java/com/google/turbine/bytecode/ByteReader.java b/java/com/google/turbine/bytecode/ByteReader.java
index 5458b49..a9deff2 100644
--- a/java/com/google/turbine/bytecode/ByteReader.java
+++ b/java/com/google/turbine/bytecode/ByteReader.java
@@ -20,9 +20,11 @@ import static com.google.common.base.Verify.verify;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.io.ByteArrayInputStream;
/** A {@link ByteArrayDataInput} wrapper that tracks the current byte array index. */
+@CanIgnoreReturnValue
public class ByteReader {
private final byte[] bytes;
diff --git a/java/com/google/turbine/bytecode/ClassFile.java b/java/com/google/turbine/bytecode/ClassFile.java
index e979edc..820f17d 100644
--- a/java/com/google/turbine/bytecode/ClassFile.java
+++ b/java/com/google/turbine/bytecode/ClassFile.java
@@ -26,48 +26,63 @@ import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Map;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/** A JVMS §4.1 ClassFile. */
public class ClassFile {
private final int access;
+ private final int majorVersion;
private final String name;
- private final String signature;
- private final String superClass;
+ private final @Nullable String signature;
+ private final @Nullable String superClass;
private final List<String> interfaces;
+ private final List<String> permits;
private final List<MethodInfo> methods;
private final List<FieldInfo> fields;
private final List<AnnotationInfo> annotations;
private final List<InnerClass> innerClasses;
private final ImmutableList<TypeAnnotationInfo> typeAnnotations;
- @Nullable private final ModuleInfo module;
- @Nullable private final String transitiveJar;
+ private final @Nullable ModuleInfo module;
+ private final @Nullable String nestHost;
+ private final ImmutableList<String> nestMembers;
+ private final @Nullable RecordInfo record;
+ private final @Nullable String transitiveJar;
public ClassFile(
int access,
+ int majorVersion,
String name,
- String signature,
- String superClass,
+ @Nullable String signature,
+ @Nullable String superClass,
List<String> interfaces,
+ List<String> permits,
List<MethodInfo> methods,
List<FieldInfo> fields,
List<AnnotationInfo> annotations,
List<InnerClass> innerClasses,
ImmutableList<TypeAnnotationInfo> typeAnnotations,
@Nullable ModuleInfo module,
+ @Nullable String nestHost,
+ ImmutableList<String> nestMembers,
+ @Nullable RecordInfo record,
@Nullable String transitiveJar) {
this.access = access;
+ this.majorVersion = majorVersion;
this.name = name;
this.signature = signature;
this.superClass = superClass;
this.interfaces = interfaces;
+ this.permits = permits;
this.methods = methods;
this.fields = fields;
this.annotations = annotations;
this.innerClasses = innerClasses;
this.typeAnnotations = typeAnnotations;
this.module = module;
+ this.nestHost = nestHost;
+ this.nestMembers = nestMembers;
+ this.record = record;
this.transitiveJar = transitiveJar;
}
@@ -76,18 +91,23 @@ public class ClassFile {
return access;
}
+ /** Class file major version. */
+ public int majorVersion() {
+ return majorVersion;
+ }
+
/** The name of the class or interface. */
public String name() {
return name;
}
/** The value of the Signature attribute. */
- public String signature() {
+ public @Nullable String signature() {
return signature;
}
/** The super class. */
- public String superName() {
+ public @Nullable String superName() {
return superClass;
}
@@ -96,6 +116,11 @@ public class ClassFile {
return interfaces;
}
+ /** The permitted direct subclasses. */
+ public List<String> permits() {
+ return permits;
+ }
+
/** Methods declared by this class or interfaces type. */
public List<MethodInfo> methods() {
return methods;
@@ -122,14 +147,24 @@ public class ClassFile {
}
/** A module attribute. */
- @Nullable
- public ModuleInfo module() {
+ public @Nullable ModuleInfo module() {
return module;
}
+ public @Nullable String nestHost() {
+ return nestHost;
+ }
+
+ public ImmutableList<String> nestMembers() {
+ return nestMembers;
+ }
+
+ public @Nullable RecordInfo record() {
+ return record;
+ }
+
/** The original jar of a repackaged transitive class. */
- @Nullable
- public String transitiveJar() {
+ public @Nullable String transitiveJar() {
return transitiveJar;
}
@@ -139,8 +174,8 @@ public class ClassFile {
private final int access;
private final String name;
private final String descriptor;
- @Nullable private final String signature;
- private final Const.@Nullable Value value;
+ private final @Nullable String signature;
+ private final @Nullable Value value;
private final List<AnnotationInfo> annotations;
private final ImmutableList<TypeAnnotationInfo> typeAnnotations;
@@ -149,7 +184,7 @@ public class ClassFile {
String name,
String descriptor,
@Nullable String signature,
- Value value,
+ @Nullable Value value,
List<AnnotationInfo> annotations,
ImmutableList<TypeAnnotationInfo> typeAnnotations) {
this.access = access;
@@ -177,8 +212,7 @@ public class ClassFile {
}
/** The value of Signature attribute. */
- @Nullable
- public String signature() {
+ public @Nullable String signature() {
return signature;
}
@@ -240,7 +274,7 @@ public class ClassFile {
private final int access;
private final String name;
private final String descriptor;
- @Nullable private final String signature;
+ private final @Nullable String signature;
private final List<String> exceptions;
private final AnnotationInfo.@Nullable ElementValue defaultValue;
private final List<AnnotationInfo> annotations;
@@ -287,8 +321,7 @@ public class ClassFile {
}
/** The value of Signature attribute. */
- @Nullable
- public String signature() {
+ public @Nullable String signature() {
return signature;
}
@@ -730,16 +763,16 @@ public class ClassFile {
}
}
- private final TypePath parent;
- private final TypePath.Kind kind;
+ private final @Nullable TypePath parent;
+ private final TypePath.@Nullable Kind kind;
private final int index;
- private TypePath(TypePath.Kind kind, TypePath parent) {
+ private TypePath(TypePath.@Nullable Kind kind, @Nullable 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, TypePath.Kind kind, TypePath parent) {
+ private TypePath(int index, TypePath.@Nullable Kind kind, @Nullable TypePath parent) {
this.index = index;
this.kind = kind;
this.parent = parent;
@@ -752,13 +785,13 @@ public class ClassFile {
/** The JVMS 4.7.20.2-A serialized value of the type_path_kind. */
public byte tag() {
- return (byte) kind.tag;
+ return (byte) requireNonNull(kind).tag;
}
/** Returns a flattened view of the type path. */
public ImmutableList<TypePath> flatten() {
Deque<TypePath> flat = new ArrayDeque<>();
- for (TypePath curr = this; curr.kind != null; curr = curr.parent) {
+ for (TypePath curr = this; requireNonNull(curr).kind != null; curr = curr.parent) {
flat.addFirst(curr);
}
return ImmutableList.copyOf(flat);
@@ -770,7 +803,7 @@ public class ClassFile {
public static class ModuleInfo {
private final String name;
- private final String version;
+ private final @Nullable String version;
private final int flags;
private final ImmutableList<RequireInfo> requires;
private final ImmutableList<ExportInfo> exports;
@@ -781,7 +814,7 @@ public class ClassFile {
public ModuleInfo(
String name,
int flags,
- String version,
+ @Nullable String version,
ImmutableList<RequireInfo> requires,
ImmutableList<ExportInfo> exports,
ImmutableList<OpenInfo> opens,
@@ -805,7 +838,7 @@ public class ClassFile {
return flags;
}
- public String version() {
+ public @Nullable String version() {
return version;
}
@@ -834,9 +867,9 @@ public class ClassFile {
private final String moduleName;
private final int flags;
- private final String version;
+ private final @Nullable String version;
- public RequireInfo(String moduleName, int flags, String version) {
+ public RequireInfo(String moduleName, int flags, @Nullable String version) {
this.moduleName = moduleName;
this.flags = flags;
this.version = version;
@@ -850,7 +883,7 @@ public class ClassFile {
return flags;
}
- public String version() {
+ public @Nullable String version() {
return version;
}
}
@@ -941,4 +974,61 @@ public class ClassFile {
}
}
}
+
+ /** A JVMS §4.7.30 Record attribute. */
+ public static class RecordInfo {
+
+ /** A JVMS §4.7.30 Record component attribute. */
+ public static class RecordComponentInfo {
+
+ private final String name;
+ private final String descriptor;
+ private final @Nullable String signature;
+ private final ImmutableList<AnnotationInfo> annotations;
+ private final ImmutableList<TypeAnnotationInfo> typeAnnotations;
+
+ public RecordComponentInfo(
+ String name,
+ String descriptor,
+ @Nullable String signature,
+ ImmutableList<AnnotationInfo> annotations,
+ ImmutableList<TypeAnnotationInfo> typeAnnotations) {
+ this.name = name;
+ this.descriptor = descriptor;
+ this.signature = signature;
+ this.annotations = annotations;
+ this.typeAnnotations = typeAnnotations;
+ }
+
+ public String name() {
+ return name;
+ }
+
+ public String descriptor() {
+ return descriptor;
+ }
+
+ public @Nullable String signature() {
+ return signature;
+ }
+
+ public ImmutableList<AnnotationInfo> annotations() {
+ return annotations;
+ }
+
+ public ImmutableList<TypeAnnotationInfo> typeAnnotations() {
+ return typeAnnotations;
+ }
+ }
+
+ public RecordInfo(ImmutableList<RecordComponentInfo> recordComponents) {
+ this.recordComponents = recordComponents;
+ }
+
+ private final ImmutableList<RecordComponentInfo> recordComponents;
+
+ public ImmutableList<RecordComponentInfo> recordComponents() {
+ return recordComponents;
+ }
+ }
}
diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java
index ac8b1e1..740026a 100644
--- a/java/com/google/turbine/bytecode/ClassReader.java
+++ b/java/com/google/turbine/bytecode/ClassReader.java
@@ -16,6 +16,8 @@
package com.google.turbine.bytecode;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.errorprone.annotations.CheckReturnValue;
@@ -37,7 +39,7 @@ 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;
+import org.jspecify.nullness.Nullable;
/** A JVMS §4 class file reader. */
public class ClassReader {
@@ -53,7 +55,7 @@ public class ClassReader {
return new ClassReader(path, bytes).read();
}
- @Nullable private final String path;
+ private final @Nullable String path;
private final ByteReader reader;
private ClassReader(@Nullable String path, byte[] bytes) {
@@ -136,16 +138,21 @@ public class ClassReader {
return new ClassFile(
accessFlags,
+ majorVersion,
thisClass,
signature,
superClass,
interfaces,
+ /* permits= */ ImmutableList.of(),
methodinfos,
fieldinfos,
annotations.build(),
innerclasses,
ImmutableList.of(),
module,
+ /* nestHost= */ null,
+ /* nestMembers= */ ImmutableList.of(),
+ /* record= */ null,
transitiveJar);
}
@@ -173,6 +180,7 @@ public class ClassReader {
String innerName = innerNameIndex != 0 ? constantPool.utf8(innerNameIndex) : null;
int innerClassAccessFlags = reader.u2();
if (innerName != null && (thisClass.equals(innerClass) || thisClass.equals(outerClass))) {
+ requireNonNull(outerClass);
innerclasses.add(
new ClassFile.InnerClass(innerClass, outerClass, innerName, innerClassAccessFlags));
}
@@ -327,18 +335,18 @@ public class ClassReader {
// The runtimeVisible bit in AnnotationInfo is only used during lowering; earlier passes
// read the meta-annotations.
/* runtimeVisible= */ false,
- values.build());
+ values.buildOrThrow());
}
private ElementValue readElementValue(ConstantPoolReader constantPool) {
int tag = reader.u1();
switch (tag) {
case 'B':
- return new ConstValue(readConst(constantPool).asByte());
+ return new ConstValue(new Const.ByteValue((byte) readInt(constantPool)));
case 'C':
- return new ConstValue(readConst(constantPool).asChar());
+ return new ConstValue(new Const.CharValue((char) readInt(constantPool)));
case 'S':
- return new ConstValue(readConst(constantPool).asShort());
+ return new ConstValue(new Const.ShortValue((short) readInt(constantPool)));
case 'D':
case 'F':
case 'I':
@@ -346,11 +354,8 @@ public class ClassReader {
case 's':
return new ConstValue(readConst(constantPool));
case 'Z':
- {
- Const.Value value = readConst(constantPool);
- // boolean constants are encoded as integers
- return new ConstValue(new Const.BooleanValue(value.asInteger().value() != 0));
- }
+ // boolean constants are encoded as integers
+ return new ConstValue(new Const.BooleanValue(readInt(constantPool) != 0));
case 'e':
{
int typeNameIndex = reader.u2();
@@ -381,6 +386,10 @@ public class ClassReader {
throw new AssertionError(String.format("bad tag value %c", tag));
}
+ private int readInt(ConstantPoolReader constantPool) {
+ return ((Const.IntValue) readConst(constantPool)).value();
+ }
+
private Const.Value readConst(ConstantPoolReader constantPool) {
int constValueIndex = reader.u2();
return constantPool.constant(constValueIndex);
diff --git a/java/com/google/turbine/bytecode/ClassWriter.java b/java/com/google/turbine/bytecode/ClassWriter.java
index de975f2..da4afc7 100644
--- a/java/com/google/turbine/bytecode/ClassWriter.java
+++ b/java/com/google/turbine/bytecode/ClassWriter.java
@@ -31,10 +31,6 @@ public final class ClassWriter {
private static final int MAGIC = 0xcafebabe;
private static final int MINOR_VERSION = 0;
- // use the lowest classfile version possible given the class file features
- // TODO(cushon): is there a reason to support --release?
- private static final int MAJOR_VERSION = 52;
- private static final int MODULE_MAJOR_VERSION = 53;
/** Writes a {@link ClassFile} to bytecode. */
public static byte[] writeClass(ClassFile classfile) {
@@ -79,7 +75,7 @@ public final class ClassWriter {
ConstantPool pool, ByteArrayDataOutput body, List<Attribute> attributes) {
body.writeShort(attributes.size());
for (Attribute attribute : attributes) {
- new AttributeWriter(pool, body).write(attribute);
+ new AttributeWriter(pool).write(body, attribute);
}
}
@@ -119,7 +115,7 @@ public final class ClassWriter {
ByteArrayDataOutput result = ByteStreams.newDataOutput();
result.writeInt(MAGIC);
result.writeShort(MINOR_VERSION);
- result.writeShort(classfile.module() != null ? MODULE_MAJOR_VERSION : MAJOR_VERSION);
+ result.writeShort(classfile.majorVersion());
writeConstantPool(pool, result);
result.write(body.toByteArray());
return result.toByteArray();
diff --git a/java/com/google/turbine/bytecode/ConstantPoolReader.java b/java/com/google/turbine/bytecode/ConstantPoolReader.java
index ffcb4c3..d00ee22 100644
--- a/java/com/google/turbine/bytecode/ConstantPoolReader.java
+++ b/java/com/google/turbine/bytecode/ConstantPoolReader.java
@@ -36,6 +36,7 @@ public class ConstantPoolReader {
static final int CONSTANT_UTF8 = 1;
static final int CONSTANT_METHOD_HANDLE = 15;
static final int CONSTANT_METHOD_TYPE = 16;
+ static final int CONSTANT_DYNAMIC = 17;
static final int CONSTANT_INVOKE_DYNAMIC = 18;
static final int CONSTANT_MODULE = 19;
static final int CONSTANT_PACKAGE = 20;
@@ -88,6 +89,7 @@ public class ConstantPoolReader {
case CONSTANT_INTEGER:
case CONSTANT_FLOAT:
case CONSTANT_NAME_AND_TYPE:
+ case CONSTANT_DYNAMIC:
case CONSTANT_INVOKE_DYNAMIC:
reader.skip(4);
return 1;
diff --git a/java/com/google/turbine/bytecode/LowerAttributes.java b/java/com/google/turbine/bytecode/LowerAttributes.java
index 5ae42af..8952dff 100644
--- a/java/com/google/turbine/bytecode/LowerAttributes.java
+++ b/java/com/google/turbine/bytecode/LowerAttributes.java
@@ -45,12 +45,39 @@ public final class LowerAttributes {
if (classfile.module() != null) {
attributes.add(new Attribute.Module(classfile.module()));
}
+ if (classfile.nestHost() != null) {
+ attributes.add(new Attribute.NestHost(classfile.nestHost()));
+ }
+ if (!classfile.nestMembers().isEmpty()) {
+ attributes.add(new Attribute.NestMembers(classfile.nestMembers()));
+ }
+ if (classfile.record() != null) {
+ attributes.add(recordAttribute(classfile.record()));
+ }
+ if (!classfile.permits().isEmpty()) {
+ attributes.add(new Attribute.PermittedSubclasses(classfile.permits()));
+ }
if (classfile.transitiveJar() != null) {
attributes.add(new Attribute.TurbineTransitiveJar(classfile.transitiveJar()));
}
return attributes;
}
+ private static Attribute recordAttribute(ClassFile.RecordInfo record) {
+ ImmutableList.Builder<Attribute.Record.Component> components = ImmutableList.builder();
+ for (ClassFile.RecordInfo.RecordComponentInfo component : record.recordComponents()) {
+ List<Attribute> attributes = new ArrayList<>();
+ if (component.signature() != null) {
+ attributes.add(new Attribute.Signature(component.signature()));
+ }
+ addAllAnnotations(attributes, component.annotations());
+ addAllTypeAnnotations(attributes, component.typeAnnotations());
+ components.add(
+ new Attribute.Record.Component(component.name(), component.descriptor(), attributes));
+ }
+ return new Attribute.Record(components.build());
+ }
+
/** Collects the {@link Attribute}s for a {@link MethodInfo}. */
static List<Attribute> methodAttributes(ClassFile.MethodInfo method) {
List<Attribute> attributes = new ArrayList<>();
diff --git a/java/com/google/turbine/bytecode/package-info.java b/java/com/google/turbine/bytecode/package-info.java
new file mode 100644
index 0000000..3f0bb60
--- /dev/null
+++ b/java/com/google/turbine/bytecode/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+@org.jspecify.nullness.NullMarked
+package com.google.turbine.bytecode;
diff --git a/java/com/google/turbine/bytecode/sig/Sig.java b/java/com/google/turbine/bytecode/sig/Sig.java
index f759269..99d98cf 100644
--- a/java/com/google/turbine/bytecode/sig/Sig.java
+++ b/java/com/google/turbine/bytecode/sig/Sig.java
@@ -18,7 +18,7 @@ package com.google.turbine.bytecode.sig;
import com.google.common.collect.ImmutableList;
import com.google.turbine.model.TurbineConstantTypeKind;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/** JVMS 4.7.9.1 signatures. */
public final class Sig {
@@ -59,18 +59,18 @@ public final class Sig {
public static class TyParamSig {
private final String name;
- @Nullable private final TySig classBound;
+ private final @Nullable TySig classBound;
private final ImmutableList<TySig> interfaceBounds;
- public TyParamSig(String name, TySig classBound, ImmutableList<TySig> interfaceBounds) {
+ public TyParamSig(
+ String name, @Nullable TySig classBound, ImmutableList<TySig> interfaceBounds) {
this.name = name;
this.classBound = classBound;
this.interfaceBounds = interfaceBounds;
}
/** A single class upper-bound, or {@code null}. */
- @Nullable
- public TySig classBound() {
+ public @Nullable TySig classBound() {
return classBound;
}
diff --git a/java/com/google/turbine/bytecode/sig/SigParser.java b/java/com/google/turbine/bytecode/sig/SigParser.java
index 033fa18..1bfd762 100644
--- a/java/com/google/turbine/bytecode/sig/SigParser.java
+++ b/java/com/google/turbine/bytecode/sig/SigParser.java
@@ -17,6 +17,7 @@
package com.google.turbine.bytecode.sig;
import com.google.common.collect.ImmutableList;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.turbine.bytecode.sig.Sig.ArrayTySig;
import com.google.turbine.bytecode.sig.Sig.BaseTySig;
import com.google.turbine.bytecode.sig.Sig.ClassSig;
@@ -46,6 +47,7 @@ public class SigParser {
}
/** Returns the next character and advances. */
+ @CanIgnoreReturnValue
char eat() {
return sig.charAt(idx++);
}
diff --git a/java/com/google/turbine/bytecode/sig/package-info.java b/java/com/google/turbine/bytecode/sig/package-info.java
new file mode 100644
index 0000000..c5f449e
--- /dev/null
+++ b/java/com/google/turbine/bytecode/sig/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+@org.jspecify.nullness.NullMarked
+package com.google.turbine.bytecode.sig;
diff --git a/java/com/google/turbine/deps/Dependencies.java b/java/com/google/turbine/deps/Dependencies.java
index ef1eea9..3dd008c 100644
--- a/java/com/google/turbine/deps/Dependencies.java
+++ b/java/com/google/turbine/deps/Dependencies.java
@@ -92,7 +92,7 @@ public final class Dependencies {
.append(bound.classPathEnv());
Set<ClassSymbol> closure = new LinkedHashSet<>(lowered.symbols());
for (ClassSymbol sym : lowered.symbols()) {
- TypeBoundClass info = env.get(sym);
+ TypeBoundClass info = env.getNonNull(sym);
addAnnotations(closure, info.annotations());
for (MethodInfo method : info.methods()) {
addAnnotations(closure, method.annotations());
diff --git a/java/com/google/turbine/deps/Transitive.java b/java/com/google/turbine/deps/Transitive.java
index 75d23f6..2b8bd58 100644
--- a/java/com/google/turbine/deps/Transitive.java
+++ b/java/com/google/turbine/deps/Transitive.java
@@ -33,7 +33,7 @@ import com.google.turbine.bytecode.ClassWriter;
import com.google.turbine.model.TurbineFlag;
import java.util.LinkedHashSet;
import java.util.Set;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/**
* Collects the minimal compile-time API for symbols in the supertype closure of compiled classes.
@@ -58,7 +58,7 @@ public final class Transitive {
transitive.put(
sym.binaryName(), ClassWriter.writeClass(trimClass(info.classFile(), info.jarFile())));
}
- return transitive.build();
+ return transitive.buildOrThrow();
}
/**
@@ -90,10 +90,12 @@ public final class Transitive {
}
return new ClassFile(
cf.access(),
+ cf.majorVersion(),
cf.name(),
cf.signature(),
cf.superName(),
cf.interfaces(),
+ cf.permits(),
// drop methods, except for annotations where we need to resolve key/value information
(cf.access() & TurbineFlag.ACC_ANNOTATION) == TurbineFlag.ACC_ANNOTATION
? cf.methods()
@@ -105,6 +107,9 @@ public final class Transitive {
innerClasses.build(),
cf.typeAnnotations(),
/* module= */ null,
+ /* nestHost= */ null,
+ /* nestMembers= */ ImmutableList.of(),
+ /* record= */ null,
/* transitiveJar = */ transitiveJar);
}
diff --git a/java/com/google/turbine/deps/package-info.java b/java/com/google/turbine/deps/package-info.java
new file mode 100644
index 0000000..28df5a9
--- /dev/null
+++ b/java/com/google/turbine/deps/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+package com.google.turbine.deps;
diff --git a/java/com/google/turbine/diag/SourceFile.java b/java/com/google/turbine/diag/SourceFile.java
index 3868252..a7c3245 100644
--- a/java/com/google/turbine/diag/SourceFile.java
+++ b/java/com/google/turbine/diag/SourceFile.java
@@ -19,6 +19,7 @@ package com.google.turbine.diag;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import java.util.Objects;
+import org.jspecify.nullness.Nullable;
/** A source file. */
public class SourceFile {
@@ -55,7 +56,7 @@ public class SourceFile {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof SourceFile)) {
return false;
}
diff --git a/java/com/google/turbine/diag/TurbineDiagnostic.java b/java/com/google/turbine/diag/TurbineDiagnostic.java
index ed04a5d..1457868 100644
--- a/java/com/google/turbine/diag/TurbineDiagnostic.java
+++ b/java/com/google/turbine/diag/TurbineDiagnostic.java
@@ -27,7 +27,7 @@ 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;
+import org.jspecify.nullness.Nullable;
/** A compilation error. */
public class TurbineDiagnostic {
@@ -77,6 +77,7 @@ public class TurbineDiagnostic {
sb.append(": error: ");
sb.append(message()).append(System.lineSeparator());
if (line() != -1 && column() != -1) {
+ requireNonNull(source); // line and column imply source is non-null
sb.append(CharMatcher.breakingWhitespace().trimTrailingFrom(source.lineMap().line(position)))
.append(System.lineSeparator());
sb.append(Strings.repeat(" ", column() - 1)).append('^');
@@ -143,7 +144,7 @@ public class TurbineDiagnostic {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof TurbineDiagnostic)) {
return false;
}
@@ -159,10 +160,12 @@ public class TurbineDiagnostic {
return source != null && source.path() != null ? source.path() : "<>";
}
+ @SuppressWarnings("nullness") // position != -1 implies source is non-null
public int line() {
return position != -1 ? source.lineMap().lineNumber(position) : -1;
}
+ @SuppressWarnings("nullness") // position != -1 implies source is non-null
public int column() {
return position != -1 ? source.lineMap().column(position) + 1 : -1;
}
diff --git a/java/com/google/turbine/diag/TurbineError.java b/java/com/google/turbine/diag/TurbineError.java
index f3ebf08..f839345 100644
--- a/java/com/google/turbine/diag/TurbineError.java
+++ b/java/com/google/turbine/diag/TurbineError.java
@@ -48,8 +48,10 @@ public class TurbineError extends Error {
CANNOT_RESOLVE("could not resolve %s"),
EXPRESSION_ERROR("could not evaluate constant expression"),
OPERAND_TYPE("bad operand type %s"),
+ TYPE_CONVERSION("value %s of type %s cannot be converted to %s"),
CYCLIC_HIERARCHY("cycle in class hierarchy: %s"),
NOT_AN_ANNOTATION("%s is not an annotation"),
+ ANNOTATION_VALUE_NAME("expected an annotation value of the form name=value"),
NONREPEATABLE_ANNOTATION("%s is not @Repeatable"),
DUPLICATE_DECLARATION("duplicate declaration of %s"),
BAD_MODULE_INFO("unexpected declaration found in module-info"),
diff --git a/java/com/google/turbine/diag/package-info.java b/java/com/google/turbine/diag/package-info.java
new file mode 100644
index 0000000..f19a95c
--- /dev/null
+++ b/java/com/google/turbine/diag/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+package com.google.turbine.diag;
diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java
index 971bbe4..362316d 100644
--- a/java/com/google/turbine/lower/Lower.java
+++ b/java/com/google/turbine/lower/Lower.java
@@ -18,6 +18,8 @@ package com.google.turbine.lower;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.turbine.binder.DisambiguateTypeAnnotations.groupRepeated;
+import static java.lang.Math.max;
+import static java.util.Objects.requireNonNull;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
@@ -37,6 +39,7 @@ 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.RecordComponentInfo;
import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
import com.google.turbine.binder.bytecode.BytecodeBoundClass;
import com.google.turbine.binder.env.CompoundEnv;
@@ -66,6 +69,7 @@ import com.google.turbine.model.Const;
import com.google.turbine.model.TurbineFlag;
import com.google.turbine.model.TurbineTyKind;
import com.google.turbine.model.TurbineVisibility;
+import com.google.turbine.options.LanguageVersion;
import com.google.turbine.type.AnnoInfo;
import com.google.turbine.type.Type;
import com.google.turbine.type.Type.ArrayTy;
@@ -83,7 +87,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/** Lowering from bound classes to bytecode. */
public class Lower {
@@ -111,6 +115,7 @@ public class Lower {
/** Lowers all given classes to bytecode. */
public static Lowered lowerAll(
+ LanguageVersion languageVersion,
ImmutableMap<ClassSymbol, SourceTypeBoundClass> units,
ImmutableList<SourceModuleInfo> modules,
Env<ClassSymbol, BytecodeBoundClass> classpath) {
@@ -118,20 +123,24 @@ public class Lower {
CompoundEnv.<ClassSymbol, TypeBoundClass>of(classpath).append(new SimpleEnv<>(units));
ImmutableMap.Builder<String, byte[]> result = ImmutableMap.builder();
Set<ClassSymbol> symbols = new LinkedHashSet<>();
+ // Output Java 8 bytecode at minimum, for type annotations
+ int majorVersion = max(languageVersion.majorVersion(), 52);
for (ClassSymbol sym : units.keySet()) {
- result.put(sym.binaryName(), lower(units.get(sym), env, sym, symbols));
+ result.put(sym.binaryName(), lower(units.get(sym), env, sym, symbols, majorVersion));
}
if (modules.size() == 1) {
// single module mode: the module-info.class file is at the root
- result.put("module-info", lower(getOnlyElement(modules), env, symbols));
+ result.put("module-info", lower(getOnlyElement(modules), env, symbols, majorVersion));
} else {
// multi-module mode: the output module-info.class are in a directory corresponding to their
// package
for (SourceModuleInfo module : modules) {
- result.put(module.name().replace('.', '/') + "/module-info", lower(module, env, symbols));
+ result.put(
+ module.name().replace('.', '/') + "/module-info",
+ lower(module, env, symbols, majorVersion));
}
}
- return new Lowered(result.build(), ImmutableSet.copyOf(symbols));
+ return new Lowered(result.buildOrThrow(), ImmutableSet.copyOf(symbols));
}
/** Lowers a class to bytecode. */
@@ -139,15 +148,17 @@ public class Lower {
SourceTypeBoundClass info,
Env<ClassSymbol, TypeBoundClass> env,
ClassSymbol sym,
- Set<ClassSymbol> symbols) {
- return new Lower(env).lower(info, sym, symbols);
+ Set<ClassSymbol> symbols,
+ int majorVersion) {
+ return new Lower(env).lower(info, sym, symbols, majorVersion);
}
private static byte[] lower(
SourceModuleInfo module,
CompoundEnv<ClassSymbol, TypeBoundClass> env,
- Set<ClassSymbol> symbols) {
- return new Lower(env).lower(module, symbols);
+ Set<ClassSymbol> symbols,
+ int majorVersion) {
+ return new Lower(env).lower(module, symbols, majorVersion);
}
private final LowerSignature sig = new LowerSignature();
@@ -157,7 +168,7 @@ public class Lower {
this.env = env;
}
- private byte[] lower(SourceModuleInfo module, Set<ClassSymbol> symbols) {
+ private byte[] lower(SourceModuleInfo module, Set<ClassSymbol> symbols, int majorVersion) {
String name = "module-info";
ImmutableList<AnnotationInfo> annotations = lowerAnnotations(module.annos());
ClassFile.ModuleInfo moduleInfo = lowerModule(module);
@@ -176,16 +187,21 @@ public class Lower {
ClassFile classfile =
new ClassFile(
/* access= */ TurbineFlag.ACC_MODULE,
+ majorVersion,
name,
/* signature= */ null,
/* superClass= */ null,
/* interfaces= */ ImmutableList.of(),
+ /* permits= */ ImmutableList.of(),
/* methods= */ ImmutableList.of(),
/* fields= */ ImmutableList.of(),
annotations,
innerClasses.build(),
/* typeAnnotations= */ ImmutableList.of(),
moduleInfo,
+ /* nestHost= */ null,
+ /* nestMembers= */ ImmutableList.of(),
+ /* record= */ null,
/* transitiveJar= */ null);
symbols.addAll(sig.classes);
return ClassWriter.writeClass(classfile);
@@ -234,7 +250,8 @@ public class Lower {
provides.build());
}
- private byte[] lower(SourceTypeBoundClass info, ClassSymbol sym, Set<ClassSymbol> symbols) {
+ private byte[] lower(
+ SourceTypeBoundClass info, ClassSymbol sym, Set<ClassSymbol> symbols, int majorVersion) {
int access = classAccess(info);
String name = sig.descriptor(sym);
String signature = sig.classSignature(info, env);
@@ -243,6 +260,20 @@ public class Lower {
for (ClassSymbol i : info.interfaces()) {
interfaces.add(sig.descriptor(i));
}
+ List<String> permits = new ArrayList<>();
+ for (ClassSymbol i : info.permits()) {
+ permits.add(sig.descriptor(i));
+ }
+
+ ClassFile.RecordInfo record = null;
+ if (info.kind().equals(TurbineTyKind.RECORD)) {
+ ImmutableList.Builder<ClassFile.RecordInfo.RecordComponentInfo> components =
+ ImmutableList.builder();
+ for (RecordComponentInfo component : info.components()) {
+ components.add(lowerComponent(info, component));
+ }
+ record = new ClassFile.RecordInfo(components.build());
+ }
List<ClassFile.MethodInfo> methods = new ArrayList<>();
for (MethodInfo m : info.methods()) {
@@ -266,21 +297,34 @@ public class Lower {
ImmutableList<TypeAnnotationInfo> typeAnnotations = classTypeAnnotations(info);
+ String nestHost = null;
+ ImmutableList<String> nestMembers = ImmutableList.of();
+ // nests were added in Java 11, i.e. major version 55
+ if (majorVersion >= 55) {
+ nestHost = collectNestHost(info.source(), info.owner());
+ nestMembers = nestHost == null ? collectNestMembers(info.source(), info) : ImmutableList.of();
+ }
+
ImmutableList<ClassFile.InnerClass> inners = collectInnerClasses(info.source(), sym, info);
ClassFile classfile =
new ClassFile(
access,
+ majorVersion,
name,
signature,
superName,
interfaces,
+ permits,
methods,
fields.build(),
annotations,
inners,
typeAnnotations,
/* module= */ null,
+ nestHost,
+ nestMembers,
+ record,
/* transitiveJar= */ null);
symbols.addAll(sig.classes);
@@ -288,6 +332,18 @@ public class Lower {
return ClassWriter.writeClass(classfile);
}
+ private ClassFile.RecordInfo.RecordComponentInfo lowerComponent(
+ SourceTypeBoundClass info, RecordComponentInfo c) {
+ Function<TyVarSymbol, TyVarInfo> tenv = new TyVarEnv(info.typeParameterTypes());
+ String desc = SigWriter.type(sig.signature(Erasure.erase(c.type(), tenv)));
+ String signature = sig.fieldSignature(c.type());
+ ImmutableList.Builder<TypeAnnotationInfo> typeAnnotations = ImmutableList.builder();
+ lowerTypeAnnotations(
+ typeAnnotations, c.type(), TargetType.FIELD, TypeAnnotationInfo.EMPTY_TARGET);
+ return new ClassFile.RecordInfo.RecordComponentInfo(
+ c.name(), desc, signature, lowerAnnotations(c.annotations()), typeAnnotations.build());
+ }
+
private ClassFile.MethodInfo lowerMethod(final MethodInfo m, final ClassSymbol sym) {
int access = m.access();
Function<TyVarSymbol, TyVarInfo> tenv = new TyVarEnv(m.tyParams());
@@ -421,28 +477,74 @@ public class Lower {
if (info == null) {
throw TurbineError.format(source, ErrorKind.CLASS_FILE_NOT_FOUND, sym);
}
- ClassSymbol owner = env.get(sym).owner();
+ ClassSymbol owner = info.owner();
if (owner != null) {
addEnclosing(source, env, all, owner);
all.add(sym);
}
}
+ private @Nullable String collectNestHost(SourceFile source, @Nullable ClassSymbol sym) {
+ if (sym == null) {
+ return null;
+ }
+ while (true) {
+ TypeBoundClass info = env.get(sym);
+ if (info == null) {
+ throw TurbineError.format(source, ErrorKind.CLASS_FILE_NOT_FOUND, sym);
+ }
+ if (info.owner() == null) {
+ return sig.descriptor(sym);
+ }
+ sym = info.owner();
+ }
+ }
+
+ private ImmutableList<String> collectNestMembers(SourceFile source, SourceTypeBoundClass info) {
+ Set<ClassSymbol> nestMembers = new LinkedHashSet<>();
+ for (ClassSymbol child : info.children().values()) {
+ addNestMembers(source, env, nestMembers, child);
+ }
+ ImmutableList.Builder<String> result = ImmutableList.builder();
+ for (ClassSymbol nestMember : nestMembers) {
+ result.add(sig.descriptor(nestMember));
+ }
+ return result.build();
+ }
+
+ private static void addNestMembers(
+ SourceFile source,
+ Env<ClassSymbol, TypeBoundClass> env,
+ Set<ClassSymbol> nestMembers,
+ ClassSymbol sym) {
+ if (!nestMembers.add(sym)) {
+ return;
+ }
+ TypeBoundClass info = env.get(sym);
+ if (info == null) {
+ throw TurbineError.format(source, ErrorKind.CLASS_FILE_NOT_FOUND, sym);
+ }
+ for (ClassSymbol child : info.children().values()) {
+ addNestMembers(source, env, nestMembers, child);
+ }
+ }
+
/**
* Creates an inner class attribute, given an inner class that was referenced somewhere in the
* class.
*/
private ClassFile.InnerClass innerClass(
Env<ClassSymbol, TypeBoundClass> env, ClassSymbol innerSym) {
- TypeBoundClass inner = env.get(innerSym);
+ TypeBoundClass inner = env.getNonNull(innerSym);
+ // this inner class is known to have an owner
+ ClassSymbol owner = requireNonNull(inner.owner());
- String innerName = innerSym.binaryName().substring(inner.owner().binaryName().length() + 1);
+ String innerName = innerSym.binaryName().substring(owner.binaryName().length() + 1);
int access = inner.access();
access &= ~(TurbineFlag.ACC_SUPER | TurbineFlag.ACC_STRICT);
- return new ClassFile.InnerClass(
- innerSym.binaryName(), inner.owner().binaryName(), innerName, access);
+ return new ClassFile.InnerClass(innerSym.binaryName(), owner.binaryName(), innerName, access);
}
/** Updates visibility, and unsets access bits that can only be set in InnerClass. */
@@ -486,7 +588,7 @@ public class Lower {
// anything that lexically encloses the class being lowered
// must be in the same compilation unit, so we have source
// information for it
- TypeBoundClass owner = env.get((ClassSymbol) ownerSym);
+ TypeBoundClass owner = env.getNonNull((ClassSymbol) ownerSym);
return owner.typeParameterTypes().get(sym);
}
}
@@ -503,7 +605,7 @@ public class Lower {
return lowered.build();
}
- private AnnotationInfo lowerAnnotation(AnnoInfo annotation) {
+ private @Nullable AnnotationInfo lowerAnnotation(AnnoInfo annotation) {
Boolean visible = isVisible(annotation.sym());
if (visible == null) {
return null;
@@ -516,9 +618,9 @@ public class Lower {
* Returns true if the annotation is visible at runtime, false if it is not visible at runtime,
* and {@code null} if it should not be retained in bytecode.
*/
- @Nullable
- private Boolean isVisible(ClassSymbol sym) {
- RetentionPolicy retention = env.get(sym).annotationMetadata().retention();
+ private @Nullable Boolean isVisible(ClassSymbol sym) {
+ RetentionPolicy retention =
+ requireNonNull(env.getNonNull(sym).annotationMetadata()).retention();
switch (retention) {
case CLASS:
return false;
@@ -535,7 +637,7 @@ public class Lower {
for (Map.Entry<String, Const> entry : values.entrySet()) {
result.put(entry.getKey(), annotationValue(entry.getValue()));
}
- return result.build();
+ return result.buildOrThrow();
}
private ElementValue annotationValue(Const value) {
@@ -691,7 +793,7 @@ public class Lower {
private boolean isInterface(Type type, Env<ClassSymbol, TypeBoundClass> env) {
return type.tyKind() == TyKind.CLASS_TY
- && env.get(((ClassTy) type).sym()).kind() == TurbineTyKind.INTERFACE;
+ && env.getNonNull(((ClassTy) type).sym()).kind() == TurbineTyKind.INTERFACE;
}
private void lowerTypeAnnotations(
diff --git a/java/com/google/turbine/lower/LowerSignature.java b/java/com/google/turbine/lower/LowerSignature.java
index a08c7e8..1960f8e 100644
--- a/java/com/google/turbine/lower/LowerSignature.java
+++ b/java/com/google/turbine/lower/LowerSignature.java
@@ -46,6 +46,7 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
+import org.jspecify.nullness.Nullable;
/** Translator from {@link Type}s to {@link Sig}natures. */
public class LowerSignature {
@@ -127,7 +128,7 @@ public class LowerSignature {
* Produces a method signature attribute for a generic method, or {@code null} if the signature is
* unnecessary.
*/
- public String methodSignature(
+ public @Nullable String methodSignature(
Env<ClassSymbol, TypeBoundClass> env, TypeBoundClass.MethodInfo method, ClassSymbol sym) {
if (!needsMethodSig(sym, env, method)) {
return null;
@@ -160,14 +161,11 @@ public class LowerSignature {
private boolean needsMethodSig(
ClassSymbol sym, Env<ClassSymbol, TypeBoundClass> env, TypeBoundClass.MethodInfo m) {
- if ((env.get(sym).access() & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM
+ if ((env.getNonNull(sym).access() & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM
&& m.name().equals("<init>")) {
// JDK-8024694: javac always expects signature attribute for enum constructors
return true;
}
- if ((m.access() & TurbineFlag.ACC_SYNTH_CTOR) == TurbineFlag.ACC_SYNTH_CTOR) {
- return false;
- }
if (!m.tyParams().isEmpty()) {
return true;
}
@@ -194,16 +192,13 @@ public class LowerSignature {
* Produces a class signature attribute for a generic class, or {@code null} if the signature is
* unnecessary.
*/
- public String classSignature(SourceTypeBoundClass info, Env<ClassSymbol, TypeBoundClass> env) {
+ public @Nullable String classSignature(
+ SourceTypeBoundClass info, Env<ClassSymbol, TypeBoundClass> env) {
if (!classNeedsSig(info)) {
return null;
}
ImmutableList<Sig.TyParamSig> typarams = tyParamSig(info.typeParameterTypes(), env);
-
- ClassTySig xtnd = null;
- if (info.superClassType() != null) {
- xtnd = classTySig((ClassTy) info.superClassType());
- }
+ ClassTySig xtnd = classTySig((ClassTy) info.superClassType());
ImmutableList.Builder<ClassTySig> impl = ImmutableList.builder();
for (Type i : info.interfaceTypes()) {
impl.add(classTySig((ClassTy) i));
@@ -215,7 +210,7 @@ public class LowerSignature {
/**
* A field signature, or {@code null} if the descriptor provides all necessary type information.
*/
- public String fieldSignature(Type type) {
+ public @Nullable String fieldSignature(Type type) {
return needsSig(type) ? SigWriter.type(signature(type)) : null;
}
@@ -295,7 +290,7 @@ public class LowerSignature {
private boolean isInterface(Type type, Env<ClassSymbol, TypeBoundClass> env) {
return type.tyKind() == TyKind.CLASS_TY
- && env.get(((ClassTy) type).sym()).kind() == TurbineTyKind.INTERFACE;
+ && env.getNonNull(((ClassTy) type).sym()).kind() == TurbineTyKind.INTERFACE;
}
public String descriptor(ClassSymbol sym) {
diff --git a/java/com/google/turbine/lower/package-info.java b/java/com/google/turbine/lower/package-info.java
new file mode 100644
index 0000000..f5c54fc
--- /dev/null
+++ b/java/com/google/turbine/lower/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+package com.google.turbine.lower;
diff --git a/java/com/google/turbine/main/Main.java b/java/com/google/turbine/main/Main.java
index 59563b6..da97bcd 100644
--- a/java/com/google/turbine/main/Main.java
+++ b/java/com/google/turbine/main/Main.java
@@ -24,6 +24,7 @@ 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.errorprone.annotations.CanIgnoreReturnValue;
import com.google.turbine.binder.Binder;
import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.Binder.Statistics;
@@ -62,6 +63,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
+import java.util.OptionalInt;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -126,10 +128,12 @@ public final class Main {
}
}
- public static void compile(String[] args) throws IOException {
- compile(TurbineOptionsParser.parse(Arrays.asList(args)));
+ @CanIgnoreReturnValue
+ public static Result compile(String[] args) throws IOException {
+ return compile(TurbineOptionsParser.parse(Arrays.asList(args)));
}
+ @CanIgnoreReturnValue
public static Result compile(TurbineOptions options) throws IOException {
usage(options);
@@ -190,14 +194,16 @@ public final class Main {
|| options.output().isPresent()
|| options.outputManifest().isPresent()) {
// TODO(cushon): parallelize
- Lowered lowered = Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv());
+ Lowered lowered =
+ Lower.lowerAll(
+ options.languageVersion(), 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())))) {
+ Path path = Paths.get(options.outputDeps().get());
+ Files.createDirectories(path.getParent());
+ try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(path))) {
deps.writeTo(os);
}
}
@@ -255,6 +261,7 @@ public final class Main {
units,
ClassPathBinder.bindClasspath(toPaths(classpath)),
Processing.initializeProcessors(
+ /* sourceVersion= */ options.languageVersion().sourceVersion(),
/* javacopts= */ options.javacOpts(),
/* processorNames= */ options.processors(),
Processing.processorLoader(
@@ -278,18 +285,18 @@ public final class Main {
private static ClassPath bootclasspath(TurbineOptions options) throws IOException {
// if both --release and --bootclasspath are specified, --release wins
- if (options.release().isPresent() && options.system().isPresent()) {
+ OptionalInt release = options.languageVersion().release();
+ if (release.isPresent() && options.system().isPresent()) {
throw new UsageException("expected at most one of --release and --system");
}
- if (options.release().isPresent()) {
- String release = options.release().get();
- if (release.equals(JAVA_SPECIFICATION_VERSION.value())) {
+ if (release.isPresent()) {
+ if (release.getAsInt() == Integer.parseInt(JAVA_SPECIFICATION_VERSION.value())) {
// if --release matches the host JDK, use its jimage instead of ct.sym
return JimageClassBinder.bindDefault();
}
// ... otherwise, search ct.sym for a matching release
- ClassPath bootclasspath = CtSymClassBinder.bind(release);
+ ClassPath bootclasspath = CtSymClassBinder.bind(release.getAsInt());
if (bootclasspath == null) {
throw new UsageException("not a supported release: " + release);
}
@@ -337,7 +344,7 @@ public final class Main {
for (SourceFile source : generatedSources.values()) {
Path to = path.resolve(source.path());
Files.createDirectories(to.getParent());
- Files.write(to, source.source().getBytes(UTF_8));
+ Files.writeString(to, source.source());
}
return;
}
diff --git a/java/com/google/turbine/main/package-info.java b/java/com/google/turbine/main/package-info.java
new file mode 100644
index 0000000..71735f2
--- /dev/null
+++ b/java/com/google/turbine/main/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+package com.google.turbine.main;
diff --git a/java/com/google/turbine/model/Const.java b/java/com/google/turbine/model/Const.java
index ed4b072..bd90f59 100644
--- a/java/com/google/turbine/model/Const.java
+++ b/java/com/google/turbine/model/Const.java
@@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.escape.SourceCodeEscapers;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.AnnotationValueVisitor;
+import org.jspecify.nullness.Nullable;
/**
* Compile-time constant expressions, including literals of primitive or String type, class
@@ -32,7 +33,7 @@ public abstract class Const {
public abstract int hashCode();
@Override
- public abstract boolean equals(Object obj);
+ public abstract boolean equals(@Nullable Object obj);
@Override
public abstract String toString();
@@ -64,42 +65,6 @@ public abstract class Const {
public Kind kind() {
return Kind.PRIMITIVE;
}
-
- public IntValue asInteger() {
- throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.INT);
- }
-
- public FloatValue asFloat() {
- throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.FLOAT);
- }
-
- public DoubleValue asDouble() {
- throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.DOUBLE);
- }
-
- public LongValue asLong() {
- throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.LONG);
- }
-
- public BooleanValue asBoolean() {
- throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.BOOLEAN);
- }
-
- public StringValue asString() {
- throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.STRING);
- }
-
- public CharValue asChar() {
- throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.CHAR);
- }
-
- public ShortValue asShort() {
- throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.SHORT);
- }
-
- public ByteValue asByte() {
- throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.BYTE);
- }
}
/** A boolean literal value. */
@@ -135,22 +100,12 @@ public abstract class Const {
}
@Override
- public BooleanValue asBoolean() {
- return this;
- }
-
- @Override
- public StringValue asString() {
- return new StringValue(String.valueOf(value));
- }
-
- @Override
public int hashCode() {
return Boolean.hashCode(value);
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof BooleanValue && value == ((BooleanValue) obj).value();
}
}
@@ -189,52 +144,12 @@ public abstract class Const {
}
@Override
- public IntValue asInteger() {
- return this;
- }
-
- @Override
- public ByteValue asByte() {
- return new ByteValue((byte) value);
- }
-
- @Override
- public LongValue asLong() {
- return new LongValue((long) value);
- }
-
- @Override
- public CharValue asChar() {
- return new CharValue((char) value);
- }
-
- @Override
- public ShortValue asShort() {
- return new ShortValue((short) value);
- }
-
- @Override
- public DoubleValue asDouble() {
- return new DoubleValue((double) value);
- }
-
- @Override
- public FloatValue asFloat() {
- return new FloatValue((float) value);
- }
-
- @Override
- public StringValue asString() {
- return new StringValue(String.valueOf(value));
- }
-
- @Override
public int hashCode() {
return Integer.hashCode(value);
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof IntValue && value == ((IntValue) obj).value;
}
}
@@ -272,52 +187,12 @@ public abstract class Const {
}
@Override
- public IntValue asInteger() {
- return new IntValue((int) value);
- }
-
- @Override
- public ByteValue asByte() {
- return new ByteValue((byte) value);
- }
-
- @Override
- public LongValue asLong() {
- return this;
- }
-
- @Override
- public CharValue asChar() {
- return new CharValue((char) value);
- }
-
- @Override
- public ShortValue asShort() {
- return new ShortValue((short) value);
- }
-
- @Override
- public DoubleValue asDouble() {
- return new DoubleValue((double) value);
- }
-
- @Override
- public FloatValue asFloat() {
- return new FloatValue((float) value);
- }
-
- @Override
- public StringValue asString() {
- return new StringValue(String.valueOf(value));
- }
-
- @Override
public int hashCode() {
return Long.hashCode(value);
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof LongValue && value == ((LongValue) obj).value;
}
}
@@ -355,52 +230,12 @@ public abstract class Const {
}
@Override
- public IntValue asInteger() {
- return new IntValue((int) value);
- }
-
- @Override
- public ByteValue asByte() {
- return new ByteValue((byte) value);
- }
-
- @Override
- public LongValue asLong() {
- return new LongValue((long) value);
- }
-
- @Override
- public CharValue asChar() {
- return this;
- }
-
- @Override
- public ShortValue asShort() {
- return new ShortValue((short) value);
- }
-
- @Override
- public DoubleValue asDouble() {
- return new DoubleValue((double) value);
- }
-
- @Override
- public FloatValue asFloat() {
- return new FloatValue((float) value);
- }
-
- @Override
- public StringValue asString() {
- return new StringValue(String.valueOf(value));
- }
-
- @Override
public int hashCode() {
return Character.hashCode(value);
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof CharValue && value == ((CharValue) obj).value;
}
}
@@ -441,52 +276,12 @@ public abstract class Const {
}
@Override
- public IntValue asInteger() {
- return new IntValue((int) value);
- }
-
- @Override
- public ByteValue asByte() {
- return new ByteValue((byte) value);
- }
-
- @Override
- public LongValue asLong() {
- return new LongValue((long) value);
- }
-
- @Override
- public CharValue asChar() {
- return new CharValue((char) value);
- }
-
- @Override
- public ShortValue asShort() {
- return new ShortValue((short) value);
- }
-
- @Override
- public DoubleValue asDouble() {
- return new DoubleValue((double) value);
- }
-
- @Override
- public FloatValue asFloat() {
- return this;
- }
-
- @Override
- public StringValue asString() {
- return new StringValue(String.valueOf(value));
- }
-
- @Override
public int hashCode() {
return Float.hashCode(value);
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof FloatValue && value == ((FloatValue) obj).value;
}
}
@@ -533,52 +328,12 @@ public abstract class Const {
}
@Override
- public IntValue asInteger() {
- return new IntValue((int) value);
- }
-
- @Override
- public ByteValue asByte() {
- return new ByteValue((byte) value);
- }
-
- @Override
- public LongValue asLong() {
- return new LongValue((long) value);
- }
-
- @Override
- public CharValue asChar() {
- return new CharValue((char) value);
- }
-
- @Override
- public ShortValue asShort() {
- return new ShortValue((short) value);
- }
-
- @Override
- public DoubleValue asDouble() {
- return this;
- }
-
- @Override
- public FloatValue asFloat() {
- return new FloatValue((float) value);
- }
-
- @Override
- public StringValue asString() {
- return new StringValue(String.valueOf(value));
- }
-
- @Override
public int hashCode() {
return Double.hashCode(value);
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof DoubleValue && value == ((DoubleValue) obj).value;
}
}
@@ -616,17 +371,12 @@ public abstract class Const {
}
@Override
- public StringValue asString() {
- return this;
- }
-
- @Override
public int hashCode() {
return value.hashCode();
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof StringValue && value.equals(((StringValue) obj).value);
}
}
@@ -664,52 +414,12 @@ public abstract class Const {
}
@Override
- public IntValue asInteger() {
- return new IntValue((int) value);
- }
-
- @Override
- public ByteValue asByte() {
- return new ByteValue((byte) value);
- }
-
- @Override
- public LongValue asLong() {
- return new LongValue((long) value);
- }
-
- @Override
- public CharValue asChar() {
- return new CharValue((char) value);
- }
-
- @Override
- public ShortValue asShort() {
- return this;
- }
-
- @Override
- public DoubleValue asDouble() {
- return new DoubleValue((double) value);
- }
-
- @Override
- public FloatValue asFloat() {
- return new FloatValue((float) value);
- }
-
- @Override
- public StringValue asString() {
- return new StringValue(String.valueOf(value));
- }
-
- @Override
public int hashCode() {
return Short.hashCode(value);
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof ShortValue && value == ((ShortValue) obj).value;
}
}
@@ -738,52 +448,12 @@ public abstract class Const {
}
@Override
- public IntValue asInteger() {
- return new IntValue((int) value);
- }
-
- @Override
- public ByteValue asByte() {
- return this;
- }
-
- @Override
- public LongValue asLong() {
- return new LongValue((long) value);
- }
-
- @Override
- public CharValue asChar() {
- return new CharValue((char) value);
- }
-
- @Override
- public ShortValue asShort() {
- return new ShortValue((short) value);
- }
-
- @Override
- public DoubleValue asDouble() {
- return new DoubleValue((double) value);
- }
-
- @Override
- public FloatValue asFloat() {
- return new FloatValue((float) value);
- }
-
- @Override
- public StringValue asString() {
- return new StringValue(String.valueOf(value));
- }
-
- @Override
public int hashCode() {
return Byte.hashCode(value);
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof ByteValue && value == ((ByteValue) obj).value;
}
@@ -822,7 +492,7 @@ public abstract class Const {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof ArrayInitValue && elements.equals(((ArrayInitValue) obj).elements);
}
diff --git a/java/com/google/turbine/model/TurbineElementType.java b/java/com/google/turbine/model/TurbineElementType.java
index a68df3a..a7debf3 100644
--- a/java/com/google/turbine/model/TurbineElementType.java
+++ b/java/com/google/turbine/model/TurbineElementType.java
@@ -28,5 +28,6 @@ public enum TurbineElementType {
PARAMETER,
TYPE,
TYPE_PARAMETER,
- TYPE_USE
+ TYPE_USE,
+ RECORD_COMPONENT
}
diff --git a/java/com/google/turbine/model/TurbineFlag.java b/java/com/google/turbine/model/TurbineFlag.java
index c138d46..3e68a5e 100644
--- a/java/com/google/turbine/model/TurbineFlag.java
+++ b/java/com/google/turbine/model/TurbineFlag.java
@@ -55,5 +55,11 @@ public final class TurbineFlag {
/** Synthetic constructors (e.g. of inner classes and enums). */
public static final int ACC_SYNTH_CTOR = 1 << 18;
+ public static final int ACC_SEALED = 1 << 19;
+ public static final int ACC_NON_SEALED = 1 << 20;
+
+ /** Compact record constructor. */
+ public static final int ACC_COMPACT_CTOR = 1 << 21;
+
private TurbineFlag() {}
}
diff --git a/java/com/google/turbine/model/TurbineTyKind.java b/java/com/google/turbine/model/TurbineTyKind.java
index 6b49f50..b61d6c9 100644
--- a/java/com/google/turbine/model/TurbineTyKind.java
+++ b/java/com/google/turbine/model/TurbineTyKind.java
@@ -21,5 +21,6 @@ public enum TurbineTyKind {
CLASS,
INTERFACE,
ENUM,
- ANNOTATION
+ ANNOTATION,
+ RECORD
}
diff --git a/java/com/google/turbine/model/package-info.java b/java/com/google/turbine/model/package-info.java
new file mode 100644
index 0000000..a1e3873
--- /dev/null
+++ b/java/com/google/turbine/model/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+package com.google.turbine.model;
diff --git a/java/com/google/turbine/options/LanguageVersion.java b/java/com/google/turbine/options/LanguageVersion.java
new file mode 100644
index 0000000..e2b0ea7
--- /dev/null
+++ b/java/com/google/turbine/options/LanguageVersion.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.turbine.options;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.primitives.Ints;
+import java.util.Iterator;
+import java.util.OptionalInt;
+import javax.lang.model.SourceVersion;
+
+/**
+ * The language version being compiled, corresponding to javac's {@code -source}, {@code -target},
+ * and {@code --release} flags.
+ */
+@AutoValue
+public abstract class LanguageVersion {
+
+ /** The source version. */
+ public abstract int source();
+
+ /** The target version. */
+ public abstract int target();
+
+ /**
+ * The release version.
+ *
+ * <p>If set, system APIs will be resolved from the host JDK's ct.sym instead of using the
+ * provided {@code --bootclasspath}.
+ */
+ public abstract OptionalInt release();
+
+ /** The class file major version corresponding to the {@link #target}. */
+ public int majorVersion() {
+ return target() + 44;
+ }
+
+ public SourceVersion sourceVersion() {
+ try {
+ return SourceVersion.valueOf("RELEASE_" + source());
+ } catch (IllegalArgumentException unused) {
+ throw new IllegalArgumentException("invalid -source version: " + source());
+ }
+ }
+
+ private static LanguageVersion create(int source, int target, OptionalInt release) {
+ return new AutoValue_LanguageVersion(source, target, release);
+ }
+
+ /** The default language version. Currently Java 8. */
+ public static LanguageVersion createDefault() {
+ return create(DEFAULT, DEFAULT, OptionalInt.empty());
+ }
+
+ private static final int DEFAULT = 8;
+
+ /** Returns the effective {@code LanguageVersion} for the given list of javac options. */
+ public static LanguageVersion fromJavacopts(ImmutableList<String> javacopts) {
+ int sourceVersion = DEFAULT;
+ int targetVersion = DEFAULT;
+ OptionalInt release = OptionalInt.empty();
+ Iterator<String> it = javacopts.iterator();
+ while (it.hasNext()) {
+ String option = it.next();
+ switch (option) {
+ case "-source":
+ case "--source":
+ if (!it.hasNext()) {
+ throw new IllegalArgumentException(option + " requires an argument");
+ }
+ sourceVersion = parseVersion(it.next());
+ release = OptionalInt.empty();
+ break;
+ case "-target":
+ case "--target":
+ if (!it.hasNext()) {
+ throw new IllegalArgumentException(option + " requires an argument");
+ }
+ targetVersion = parseVersion(it.next());
+ release = OptionalInt.empty();
+ break;
+ case "--release":
+ if (!it.hasNext()) {
+ throw new IllegalArgumentException(option + " requires an argument");
+ }
+ String value = it.next();
+ Integer n = Ints.tryParse(value);
+ if (n == null) {
+ throw new IllegalArgumentException("invalid --release version: " + value);
+ }
+ release = OptionalInt.of(n);
+ sourceVersion = n;
+ targetVersion = n;
+ break;
+ default:
+ break;
+ }
+ }
+ return create(sourceVersion, targetVersion, release);
+ }
+
+ private static int parseVersion(String value) {
+ boolean hasPrefix = value.startsWith("1.");
+ Integer version = Ints.tryParse(hasPrefix ? value.substring("1.".length()) : value);
+ if (version == null || !isValidVersion(version, hasPrefix)) {
+ throw new IllegalArgumentException("invalid -source version: " + value);
+ }
+ return version;
+ }
+
+ private static boolean isValidVersion(int version, boolean hasPrefix) {
+ if (version < 5) {
+ // the earliest source version supported by JDK 8 is Java 5
+ return false;
+ }
+ if (hasPrefix && version > 10) {
+ // javac supports legacy `1.*` version numbers for source versions up to Java 10
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java/com/google/turbine/options/TurbineOptions.java b/java/com/google/turbine/options/TurbineOptions.java
index c104c54..5cd9a61 100644
--- a/java/com/google/turbine/options/TurbineOptions.java
+++ b/java/com/google/turbine/options/TurbineOptions.java
@@ -20,7 +20,7 @@ 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;
+import org.jspecify.nullness.Nullable;
/** Header compilation options. */
@AutoValue
@@ -61,8 +61,8 @@ public abstract class TurbineOptions {
/** Paths to compilation bootclasspath artifacts. */
public abstract ImmutableSet<String> bootClassPath();
- /** The target platform version. */
- public abstract Optional<String> release();
+ /** The language version. */
+ public abstract LanguageVersion languageVersion();
/** The target platform's system modules. */
public abstract Optional<String> system();
@@ -138,6 +138,7 @@ public abstract class TurbineOptions {
.setDirectJars(ImmutableList.of())
.setDepsArtifacts(ImmutableList.of())
.addAllJavacOpts(ImmutableList.of())
+ .setLanguageVersion(LanguageVersion.createDefault())
.setReducedClasspathMode(ReducedClasspathMode.NONE)
.setHelp(false)
.setFullClasspathLength(0)
@@ -153,7 +154,7 @@ public abstract class TurbineOptions {
public abstract Builder setBootClassPath(ImmutableList<String> bootClassPath);
- public abstract Builder setRelease(String release);
+ public abstract Builder setLanguageVersion(LanguageVersion languageVersion);
public abstract Builder setSystem(String system);
diff --git a/java/com/google/turbine/options/TurbineOptionsParser.java b/java/com/google/turbine/options/TurbineOptionsParser.java
index 4a8ff16..e68a546 100644
--- a/java/com/google/turbine/options/TurbineOptionsParser.java
+++ b/java/com/google/turbine/options/TurbineOptionsParser.java
@@ -17,7 +17,6 @@
package com.google.turbine.options;
import static com.google.common.base.Preconditions.checkArgument;
-import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
@@ -29,7 +28,6 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayDeque;
import java.util.Deque;
-import java.util.Iterator;
/** A command line options parser for {@link TurbineOptions}. */
public final class TurbineOptionsParser {
@@ -83,19 +81,14 @@ public final class TurbineOptionsParser {
case "--bootclasspath":
builder.setBootClassPath(readList(argumentDeque));
break;
- case "--release":
- builder.setRelease(readOne(next, argumentDeque));
- break;
case "--system":
builder.setSystem(readOne(next, argumentDeque));
break;
case "--javacopts":
- {
- ImmutableList<String> javacopts = readJavacopts(argumentDeque);
- setReleaseFromJavacopts(builder, javacopts);
- builder.addAllJavacOpts(javacopts);
- break;
- }
+ ImmutableList<String> javacOpts = readJavacopts(argumentDeque);
+ builder.setLanguageVersion(LanguageVersion.fromJavacopts(javacOpts));
+ builder.addAllJavacOpts(javacOpts);
+ break;
case "--sources":
builder.setSources(readList(argumentDeque));
break;
@@ -193,8 +186,7 @@ public final class TurbineOptionsParser {
if (!Files.exists(paramsPath)) {
throw new AssertionError("params file does not exist: " + paramsPath);
}
- expandParamsFiles(
- argumentDeque, ARG_SPLITTER.split(new String(Files.readAllBytes(paramsPath), UTF_8)));
+ expandParamsFiles(argumentDeque, ARG_SPLITTER.split(Files.readString(paramsPath)));
} else {
argumentDeque.addLast(arg);
}
@@ -237,19 +229,5 @@ public final class TurbineOptionsParser {
throw new IllegalArgumentException("javacopts should be terminated by `--`");
}
- /**
- * Parses the given javacopts for {@code --release}, and if found sets turbine's {@code --release}
- * flag.
- */
- private static void setReleaseFromJavacopts(
- TurbineOptions.Builder builder, ImmutableList<String> javacopts) {
- Iterator<String> it = javacopts.iterator();
- while (it.hasNext()) {
- if (it.next().equals("--release") && it.hasNext()) {
- builder.setRelease(it.next());
- }
- }
- }
-
private TurbineOptionsParser() {}
}
diff --git a/java/com/google/turbine/options/package-info.java b/java/com/google/turbine/options/package-info.java
index 9c12bf8..45bad5e 100644
--- a/java/com/google/turbine/options/package-info.java
+++ b/java/com/google/turbine/options/package-info.java
@@ -14,4 +14,5 @@
* limitations under the License.
*/
+@org.jspecify.nullness.NullMarked
package com.google.turbine.options;
diff --git a/java/com/google/turbine/parse/ConstExpressionParser.java b/java/com/google/turbine/parse/ConstExpressionParser.java
index ba51814..8b7466f 100644
--- a/java/com/google/turbine/parse/ConstExpressionParser.java
+++ b/java/com/google/turbine/parse/ConstExpressionParser.java
@@ -25,13 +25,14 @@ import com.google.turbine.diag.TurbineError.ErrorKind;
import com.google.turbine.model.Const;
import com.google.turbine.model.TurbineConstantTypeKind;
import com.google.turbine.tree.Tree;
+import com.google.turbine.tree.Tree.AnnoExpr;
import com.google.turbine.tree.Tree.ClassLiteral;
import com.google.turbine.tree.Tree.ClassTy;
import com.google.turbine.tree.Tree.Expression;
import com.google.turbine.tree.Tree.Ident;
import com.google.turbine.tree.TurbineOperatorKind;
import java.util.Optional;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/** A parser for compile-time constant expressions. */
public class ConstExpressionParser {
@@ -40,13 +41,13 @@ public class ConstExpressionParser {
private int position;
private final Lexer lexer;
- public ConstExpressionParser(Lexer lexer, Token token) {
+ public ConstExpressionParser(Lexer lexer, Token token, int position) {
this.lexer = lexer;
this.token = token;
- this.position = lexer.position();
+ this.position = position;
}
- private static TurbineOperatorKind operator(Token token) {
+ private static @Nullable TurbineOperatorKind operator(Token token) {
switch (token) {
case ASSIGN:
// TODO(cushon): only allow in annotations?
@@ -96,7 +97,7 @@ public class ConstExpressionParser {
}
}
- private Tree.@Nullable Expression primary(boolean negate) {
+ private @Nullable Expression primary(boolean negate) {
switch (token) {
case INT_LITERAL:
return finishLiteral(TurbineConstantTypeKind.INT, negate);
@@ -107,13 +108,19 @@ public class ConstExpressionParser {
case FLOAT_LITERAL:
return finishLiteral(TurbineConstantTypeKind.FLOAT, negate);
case TRUE:
- eat();
- return new Tree.Literal(
- position, TurbineConstantTypeKind.BOOLEAN, new Const.BooleanValue(true));
+ {
+ int pos = position;
+ eat();
+ return new Tree.Literal(
+ pos, TurbineConstantTypeKind.BOOLEAN, new Const.BooleanValue(true));
+ }
case FALSE:
- eat();
- return new Tree.Literal(
- position, TurbineConstantTypeKind.BOOLEAN, new Const.BooleanValue(false));
+ {
+ int pos = position;
+ eat();
+ return new Tree.Literal(
+ pos, TurbineConstantTypeKind.BOOLEAN, new Const.BooleanValue(false));
+ }
case CHAR_LITERAL:
return finishLiteral(TurbineConstantTypeKind.CHAR, negate);
case STRING_LITERAL:
@@ -169,7 +176,7 @@ public class ConstExpressionParser {
return finishClassLiteral(position, new Tree.PrimTy(position, ImmutableList.of(), type));
}
- private Tree.Expression maybeCast() {
+ private Expression maybeCast() {
eat();
switch (token) {
case BOOLEAN:
@@ -201,8 +208,8 @@ public class ConstExpressionParser {
}
}
- private Tree.Expression notCast() {
- Tree.Expression expr = expression(null);
+ private @Nullable Expression notCast() {
+ Expression expr = expression(null);
if (expr == null) {
return null;
}
@@ -222,13 +229,16 @@ public class ConstExpressionParser {
case NOT:
case TILDE:
case IDENT:
- return new Tree.TypeCast(
- position, asClassTy(cvar.position(), cvar.name()), primary(false));
+ Expression expression = primary(false);
+ if (expression == null) {
+ throw error(ErrorKind.EXPRESSION_ERROR);
+ }
+ return new Tree.TypeCast(position, asClassTy(cvar.position(), cvar.name()), expression);
default:
- return expr;
+ return new Tree.Paren(position, expr);
}
} else {
- return expr;
+ return new Tree.Paren(position, expr);
}
}
@@ -245,7 +255,7 @@ public class ConstExpressionParser {
position = lexer.position();
}
- private Tree.Expression arrayInitializer(int pos) {
+ private @Nullable Expression arrayInitializer(int pos) {
if (token == Token.RBRACE) {
eat();
return new Tree.ArrayInit(pos, ImmutableList.<Tree.Expression>of());
@@ -258,7 +268,7 @@ public class ConstExpressionParser {
eat();
break OUTER;
}
- Tree.Expression item = expression(null);
+ Expression item = expression(null);
if (item == null) {
return null;
}
@@ -278,7 +288,7 @@ public class ConstExpressionParser {
}
/** Finish hex, decimal, octal, and binary integer literals (see JLS 3.10.1). */
- private Tree.Expression finishLiteral(TurbineConstantTypeKind kind, boolean negate) {
+ private Expression finishLiteral(TurbineConstantTypeKind kind, boolean negate) {
int pos = position;
String text = ident().value();
Const.Value value;
@@ -381,7 +391,8 @@ public class ConstExpressionParser {
if (neg) {
text = text.substring(1);
}
- for (char c : text.toCharArray()) {
+ for (int i = 0; i < text.length(); i++) {
+ char c = text.charAt(i);
int digit;
if ('0' <= c && c <= '9') {
digit = c - '0';
@@ -402,9 +413,9 @@ public class ConstExpressionParser {
return r;
}
- private Tree.Expression unaryRest(TurbineOperatorKind op) {
+ private @Nullable Expression unaryRest(TurbineOperatorKind op) {
boolean negate = op == TurbineOperatorKind.NEG;
- Tree.Expression expr = primary(negate);
+ Expression expr = primary(negate);
if (expr == null) {
return null;
}
@@ -421,14 +432,11 @@ public class ConstExpressionParser {
return new Tree.Unary(position, expr, op);
}
- private Tree.@Nullable Expression qualIdent() {
+ private @Nullable Expression qualIdent() {
int pos = position;
ImmutableList.Builder<Ident> bits = ImmutableList.builder();
bits.add(ident());
eat();
- if (token == Token.LBRACK) {
- return finishClassLiteral(pos, asClassTy(pos, bits.build()));
- }
while (token == Token.DOT) {
eat();
switch (token) {
@@ -444,6 +452,9 @@ public class ConstExpressionParser {
}
eat();
}
+ if (token == Token.LBRACK) {
+ return finishClassLiteral(pos, asClassTy(pos, bits.build()));
+ }
return new Tree.ConstVarName(pos, bits.build());
}
@@ -451,7 +462,7 @@ public class ConstExpressionParser {
return new Ident(lexer.position(), lexer.stringValue());
}
- private Expression finishClassLiteral(int pos, Tree.Type type) {
+ private @Nullable Expression finishClassLiteral(int pos, Tree.Type type) {
while (token == Token.LBRACK) {
eat();
if (token != Token.RBRACK) {
@@ -471,8 +482,8 @@ public class ConstExpressionParser {
return new ClassLiteral(pos, type);
}
- public Tree.Expression expression() {
- Tree.Expression result = expression(null);
+ public @Nullable Expression expression() {
+ Expression result = expression(null);
switch (token) {
case EOF:
case SEMI:
@@ -485,15 +496,15 @@ public class ConstExpressionParser {
}
}
- private Tree.Expression expression(TurbineOperatorKind.Precedence prec) {
- Tree.Expression term1 = primary(false);
+ private @Nullable Expression expression(TurbineOperatorKind.Precedence prec) {
+ Expression term1 = primary(false);
if (term1 == null) {
return null;
}
return expression(term1, prec);
}
- private Tree.Expression expression(Tree.Expression term1, TurbineOperatorKind.Precedence prec) {
+ private @Nullable Expression expression(Expression term1, TurbineOperatorKind.Precedence prec) {
while (true) {
if (token == Token.EOF) {
return term1;
@@ -514,7 +525,12 @@ public class ConstExpressionParser {
term1 = assign(term1, op);
break;
default:
- term1 = new Tree.Binary(position, term1, expression(op.prec()), op);
+ int pos = position;
+ Expression term2 = expression(op.prec());
+ if (term2 == null) {
+ return null;
+ }
+ term1 = new Tree.Binary(pos, term1, term2, op);
}
if (term1 == null) {
return null;
@@ -522,7 +538,7 @@ public class ConstExpressionParser {
}
}
- private Tree.Expression assign(Tree.Expression term1, TurbineOperatorKind op) {
+ private @Nullable Expression assign(Expression term1, TurbineOperatorKind op) {
if (!(term1 instanceof Tree.ConstVarName)) {
return null;
}
@@ -531,15 +547,15 @@ public class ConstExpressionParser {
return null;
}
Ident name = getOnlyElement(names);
- Tree.Expression rhs = expression(op.prec());
+ Expression rhs = expression(op.prec());
if (rhs == null) {
return null;
}
return new Tree.Assign(term1.position(), name, rhs);
}
- private Tree.Expression ternary(Tree.Expression term1) {
- Tree.Expression thenExpr = expression(TurbineOperatorKind.Precedence.TERNARY);
+ private @Nullable Expression ternary(Expression term1) {
+ Expression thenExpr = expression(TurbineOperatorKind.Precedence.TERNARY);
if (thenExpr == null) {
return null;
}
@@ -547,26 +563,26 @@ public class ConstExpressionParser {
return null;
}
eat();
- Tree.Expression elseExpr = expression();
+ Expression elseExpr = expression();
if (elseExpr == null) {
return null;
}
return new Tree.Conditional(position, term1, thenExpr, elseExpr);
}
- private Tree.Expression castTail(TurbineConstantTypeKind ty) {
+ private @Nullable Expression castTail(TurbineConstantTypeKind ty) {
if (token != Token.RPAREN) {
return null;
}
eat();
- Tree.Expression rhs = primary(false);
+ Expression rhs = primary(false);
if (rhs == null) {
return null;
}
return new Tree.TypeCast(position, new Tree.PrimTy(position, ImmutableList.of(), ty), rhs);
}
- private Tree.@Nullable AnnoExpr annotation() {
+ private @Nullable AnnoExpr annotation() {
if (token != Token.AT) {
throw new AssertionError();
}
@@ -582,7 +598,7 @@ public class ConstExpressionParser {
eat();
while (token != Token.RPAREN) {
int argPos = position;
- Tree.Expression expression = expression();
+ Expression expression = expression();
if (expression == null) {
throw TurbineError.format(lexer.source(), argPos, ErrorKind.INVALID_ANNOTATION_ARGUMENT);
}
diff --git a/java/com/google/turbine/parse/Parser.java b/java/com/google/turbine/parse/Parser.java
index af1eabf..c370ad8 100644
--- a/java/com/google/turbine/parse/Parser.java
+++ b/java/com/google/turbine/parse/Parser.java
@@ -17,8 +17,10 @@
package com.google.turbine.parse;
import static com.google.turbine.parse.Token.COMMA;
+import static com.google.turbine.parse.Token.IDENT;
import static com.google.turbine.parse.Token.INTERFACE;
import static com.google.turbine.parse.Token.LPAREN;
+import static com.google.turbine.parse.Token.MINUS;
import static com.google.turbine.parse.Token.RPAREN;
import static com.google.turbine.parse.Token.SEMI;
import static com.google.turbine.tree.TurbineModifier.PROTECTED;
@@ -27,7 +29,7 @@ import static com.google.turbine.tree.TurbineModifier.VARARGS;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.errorprone.annotations.CheckReturnValue;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.turbine.diag.SourceFile;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.diag.TurbineError.ErrorKind;
@@ -63,7 +65,7 @@ import java.util.Deque;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/**
* A parser for the subset of Java required for header compilation.
@@ -186,6 +188,26 @@ public class Parser {
case IDENT:
{
Ident ident = ident();
+ if (ident.value().equals("record")) {
+ next();
+ decls.add(recordDeclaration(access, annos.build()));
+ access = EnumSet.noneOf(TurbineModifier.class);
+ annos = ImmutableList.builder();
+ break;
+ }
+ if (ident.value().equals("sealed")) {
+ next();
+ access.add(TurbineModifier.SEALED);
+ break;
+ }
+ if (ident.value().equals("non")) {
+ int start = position;
+ next();
+ eatNonSealed(start);
+ next();
+ access.add(TurbineModifier.NON_SEALED);
+ break;
+ }
if (access.isEmpty()
&& (ident.value().equals("module") || ident.value().equals("open"))) {
boolean open = false;
@@ -209,11 +231,68 @@ public class Parser {
}
}
+ // Handle the hypenated pseudo-keyword 'non-sealed'.
+ //
+ // This will need to be updated to handle other hyphenated keywords if when/they are introduced.
+ private void eatNonSealed(int start) {
+ eat(Token.MINUS);
+ if (token != IDENT) {
+ throw error(token);
+ }
+ if (!ident().value().equals("sealed")) {
+ throw error(token);
+ }
+ if (position != start + "non-".length()) {
+ throw error(token);
+ }
+ }
+
private void next() {
token = lexer.next();
position = lexer.position();
}
+ private TyDecl recordDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
+ String javadoc = lexer.javadoc();
+ int pos = position;
+ Ident name = eatIdent();
+ ImmutableList<TyParam> typarams;
+ if (token == Token.LT) {
+ typarams = typarams();
+ } else {
+ typarams = ImmutableList.of();
+ }
+ ImmutableList.Builder<VarDecl> formals = ImmutableList.builder();
+ if (token == Token.LPAREN) {
+ next();
+ formalParams(formals, EnumSet.noneOf(TurbineModifier.class));
+ eat(Token.RPAREN);
+ }
+ ImmutableList.Builder<ClassTy> interfaces = ImmutableList.builder();
+ if (token == Token.IMPLEMENTS) {
+ next();
+ do {
+ interfaces.add(classty());
+ } while (maybe(Token.COMMA));
+ }
+ eat(Token.LBRACE);
+ ImmutableList<Tree> members = classMembers();
+ eat(Token.RBRACE);
+ return new TyDecl(
+ pos,
+ access,
+ annos,
+ name,
+ typarams,
+ Optional.<ClassTy>empty(),
+ interfaces.build(),
+ /* permits= */ ImmutableList.of(),
+ members,
+ formals.build(),
+ TurbineTyKind.RECORD,
+ javadoc);
+ }
+
private TyDecl interfaceDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
String javadoc = lexer.javadoc();
eat(Token.INTERFACE);
@@ -232,6 +311,15 @@ public class Parser {
interfaces.add(classty());
} while (maybe(Token.COMMA));
}
+ ImmutableList.Builder<ClassTy> permits = ImmutableList.builder();
+ if (token == Token.IDENT) {
+ if (ident().value().equals("permits")) {
+ eat(Token.IDENT);
+ do {
+ permits.add(classty());
+ } while (maybe(Token.COMMA));
+ }
+ }
eat(Token.LBRACE);
ImmutableList<Tree> members = classMembers();
eat(Token.RBRACE);
@@ -243,7 +331,9 @@ public class Parser {
typarams,
Optional.<ClassTy>empty(),
interfaces.build(),
+ permits.build(),
members,
+ ImmutableList.of(),
TurbineTyKind.INTERFACE,
javadoc);
}
@@ -264,7 +354,9 @@ public class Parser {
ImmutableList.<TyParam>of(),
Optional.<ClassTy>empty(),
ImmutableList.<ClassTy>of(),
+ ImmutableList.of(),
members,
+ ImmutableList.of(),
TurbineTyKind.ANNOTATION,
javadoc);
}
@@ -293,7 +385,9 @@ public class Parser {
ImmutableList.<TyParam>of(),
Optional.<ClassTy>empty(),
interfaces.build(),
+ ImmutableList.of(),
members,
+ ImmutableList.of(),
TurbineTyKind.ENUM,
javadoc);
}
@@ -519,6 +613,15 @@ public class Parser {
interfaces.add(classty());
} while (maybe(Token.COMMA));
}
+ ImmutableList.Builder<ClassTy> permits = ImmutableList.builder();
+ if (token == Token.IDENT) {
+ if (ident().value().equals("permits")) {
+ eat(Token.IDENT);
+ do {
+ permits.add(classty());
+ } while (maybe(Token.COMMA));
+ }
+ }
switch (token) {
case LBRACE:
next();
@@ -538,7 +641,9 @@ public class Parser {
tyParams,
Optional.ofNullable(xtnds),
interfaces.build(),
+ permits.build(),
members,
+ ImmutableList.of(),
TurbineTyKind.CLASS,
javadoc);
}
@@ -613,6 +718,29 @@ public class Parser {
}
case IDENT:
+ Ident ident = ident();
+ if (ident.value().equals("non")) {
+ int pos = position;
+ next();
+ if (token != MINUS) {
+ acc.addAll(member(access, annos.build(), ImmutableList.of(), pos, ident));
+ access = EnumSet.noneOf(TurbineModifier.class);
+ annos = ImmutableList.builder();
+ } else {
+ eatNonSealed(pos);
+ next();
+ access.add(TurbineModifier.NON_SEALED);
+ }
+ break;
+ }
+ if (ident.value().equals("record")) {
+ eat(IDENT);
+ acc.add(recordDeclaration(access, annos.build()));
+ access = EnumSet.noneOf(TurbineModifier.class);
+ annos = ImmutableList.builder();
+ break;
+ }
+ // fall through
case BOOLEAN:
case BYTE:
case SHORT:
@@ -696,90 +824,118 @@ public class Parser {
return memberRest(pos, access, annos, typaram, result, name);
}
case IDENT:
+ int pos = position;
+ Ident ident = eatIdent();
+ return member(access, annos, typaram, pos, ident);
+ default:
+ throw error(token);
+ }
+ }
+
+ private ImmutableList<Tree> member(
+ EnumSet<TurbineModifier> access,
+ ImmutableList<Anno> annos,
+ ImmutableList<TyParam> typaram,
+ int pos,
+ Ident ident) {
+ Type result;
+ Ident name;
+ switch (token) {
+ case LPAREN:
+ {
+ name = ident;
+ return ImmutableList.of(methodRest(pos, access, annos, typaram, null, name));
+ }
+ case LBRACE:
+ {
+ dropBlocks();
+ name = new Ident(position, CTOR_NAME);
+ String javadoc = lexer.javadoc();
+ access.add(TurbineModifier.COMPACT_CTOR);
+ return ImmutableList.<Tree>of(
+ new MethDecl(
+ pos,
+ access,
+ annos,
+ typaram,
+ /* ret= */ Optional.empty(),
+ name,
+ /* params= */ ImmutableList.of(),
+ /* exntys= */ ImmutableList.of(),
+ /* defaultValue= */ Optional.empty(),
+ javadoc));
+ }
+ case IDENT:
{
- int pos = position;
- Ident ident = eatIdent();
- switch (token) {
- case LPAREN:
- {
- name = ident;
- return ImmutableList.of(methodRest(pos, access, annos, typaram, null, name));
- }
- case IDENT:
- {
- result =
- new ClassTy(
- position,
- Optional.<ClassTy>empty(),
- ident,
- ImmutableList.<Type>of(),
- ImmutableList.of());
- pos = position;
- name = eatIdent();
- return memberRest(pos, access, annos, typaram, result, name);
- }
- case AT:
- case LBRACK:
- {
- result =
- new ClassTy(
- position,
- Optional.<ClassTy>empty(),
- ident,
- ImmutableList.<Type>of(),
- ImmutableList.of());
- result = maybeDims(maybeAnnos(), result);
- break;
- }
- case LT:
- {
- result =
- new ClassTy(
- position, Optional.<ClassTy>empty(), ident, tyargs(), ImmutableList.of());
- result = maybeDims(maybeAnnos(), result);
- break;
- }
- case DOT:
- result =
- new ClassTy(
- position,
- Optional.<ClassTy>empty(),
- ident,
- ImmutableList.<Type>of(),
- ImmutableList.of());
- break;
- default:
- throw error(token);
- }
- if (result == null) {
- throw error(token);
- }
- if (token == Token.DOT) {
- next();
- if (!result.kind().equals(Kind.CLASS_TY)) {
- throw error(token);
- }
- result = classty((ClassTy) result);
- }
- result = maybeDims(maybeAnnos(), result);
+ result =
+ new ClassTy(
+ position,
+ Optional.<ClassTy>empty(),
+ ident,
+ ImmutableList.<Type>of(),
+ ImmutableList.of());
pos = position;
name = eatIdent();
- switch (token) {
- case LPAREN:
- return ImmutableList.of(methodRest(pos, access, annos, typaram, result, name));
- case LBRACK:
- case SEMI:
- case ASSIGN:
- case COMMA:
- {
- if (!typaram.isEmpty()) {
- throw error(ErrorKind.UNEXPECTED_TYPE_PARAMETER, typaram);
- }
- return fieldRest(pos, access, annos, result, name);
- }
- default:
- throw error(token);
+ return memberRest(pos, access, annos, typaram, result, name);
+ }
+ case AT:
+ case LBRACK:
+ {
+ result =
+ new ClassTy(
+ position,
+ Optional.<ClassTy>empty(),
+ ident,
+ ImmutableList.<Type>of(),
+ ImmutableList.of());
+ result = maybeDims(maybeAnnos(), result);
+ break;
+ }
+ case LT:
+ {
+ result =
+ new ClassTy(position, Optional.<ClassTy>empty(), ident, tyargs(), ImmutableList.of());
+ result = maybeDims(maybeAnnos(), result);
+ break;
+ }
+ case DOT:
+ result =
+ new ClassTy(
+ position,
+ Optional.<ClassTy>empty(),
+ ident,
+ ImmutableList.<Type>of(),
+ ImmutableList.of());
+ break;
+
+ default:
+ throw error(token);
+ }
+ if (result == null) {
+ throw error(token);
+ }
+ if (token == Token.DOT) {
+ next();
+ if (!result.kind().equals(Kind.CLASS_TY)) {
+ throw error(token);
+ }
+ result = classty((ClassTy) result);
+ }
+ result = maybeDims(maybeAnnos(), result);
+ pos = position;
+ name = eatIdent();
+ switch (token) {
+ case LPAREN:
+ return ImmutableList.of(methodRest(pos, access, annos, typaram, result, name));
+ case LBRACK:
+ case SEMI:
+ case ASSIGN:
+ case COMMA:
+ {
+ if (!typaram.isEmpty()) {
+ throw error(ErrorKind.UNEXPECTED_TYPE_PARAMETER, typaram);
}
+ return fieldRest(pos, access, annos, result, name);
}
default:
throw error(token);
@@ -850,7 +1006,8 @@ public class Parser {
Type ty = baseTy;
ty = parser.extraDims(ty);
// TODO(cushon): skip more fields that are definitely non-const
- ConstExpressionParser constExpressionParser = new ConstExpressionParser(lexer, lexer.next());
+ ConstExpressionParser constExpressionParser =
+ new ConstExpressionParser(lexer, lexer.next(), lexer.position());
expressionStart = lexer.position();
Expression init = constExpressionParser.expression();
if (init != null && init.kind() == Tree.Kind.ARRAY_INIT) {
@@ -895,7 +1052,8 @@ public class Parser {
break;
case DEFAULT:
{
- ConstExpressionParser cparser = new ConstExpressionParser(lexer, lexer.next());
+ ConstExpressionParser cparser =
+ new ConstExpressionParser(lexer, lexer.next(), lexer.position());
Tree expr = cparser.expression();
token = cparser.token;
if (expr == null && token == Token.AT) {
@@ -1369,7 +1527,7 @@ public class Parser {
if (token == Token.LPAREN) {
eat(LPAREN);
while (token != RPAREN) {
- ConstExpressionParser cparser = new ConstExpressionParser(lexer, token);
+ ConstExpressionParser cparser = new ConstExpressionParser(lexer, token, position);
Expression arg = cparser.expression();
if (arg == null) {
throw error(ErrorKind.INVALID_ANNOTATION_ARGUMENT);
@@ -1405,6 +1563,7 @@ public class Parser {
next();
}
+ @CanIgnoreReturnValue
private boolean maybe(Token kind) {
if (token == kind) {
next();
@@ -1413,7 +1572,6 @@ public class Parser {
return false;
}
- @CheckReturnValue
TurbineError error(Token token) {
switch (token) {
case IDENT:
@@ -1425,7 +1583,6 @@ public class Parser {
}
}
- @CheckReturnValue
private TurbineError error(ErrorKind kind, Object... args) {
return TurbineError.format(
lexer.source(),
diff --git a/java/com/google/turbine/parse/StreamLexer.java b/java/com/google/turbine/parse/StreamLexer.java
index 991b5fd..2348385 100644
--- a/java/com/google/turbine/parse/StreamLexer.java
+++ b/java/com/google/turbine/parse/StreamLexer.java
@@ -22,6 +22,7 @@ import static com.google.turbine.parse.UnicodeEscapePreprocessor.ASCII_SUB;
import com.google.turbine.diag.SourceFile;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.diag.TurbineError.ErrorKind;
+import org.jspecify.nullness.Nullable;
/** A {@link Lexer} that streams input from a {@link UnicodeEscapePreprocessor}. */
public class StreamLexer implements Lexer {
@@ -65,7 +66,7 @@ public class StreamLexer implements Lexer {
}
@Override
- public String javadoc() {
+ public @Nullable String javadoc() {
String result = javadoc;
javadoc = null;
if (result == null) {
diff --git a/java/com/google/turbine/parse/Token.java b/java/com/google/turbine/parse/Token.java
index 7d20beb..ec214a5 100644
--- a/java/com/google/turbine/parse/Token.java
+++ b/java/com/google/turbine/parse/Token.java
@@ -23,8 +23,8 @@ public enum Token {
RPAREN(")"),
LBRACE("{"),
RBRACE("}"),
- LBRACK("<"),
- RBRACK(">"),
+ LBRACK("["),
+ RBRACK("]"),
EOF("<eof>"),
SEMI(";"),
COMMA(","),
diff --git a/java/com/google/turbine/parse/package-info.java b/java/com/google/turbine/parse/package-info.java
new file mode 100644
index 0000000..ace7dcf
--- /dev/null
+++ b/java/com/google/turbine/parse/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+package com.google.turbine.parse;
diff --git a/java/com/google/turbine/processing/ModelFactory.java b/java/com/google/turbine/processing/ModelFactory.java
index 9b782cd..160d5ae 100644
--- a/java/com/google/turbine/processing/ModelFactory.java
+++ b/java/com/google/turbine/processing/ModelFactory.java
@@ -29,6 +29,7 @@ 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.RecordComponentInfo;
import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
import com.google.turbine.binder.env.CompoundEnv;
import com.google.turbine.binder.env.Env;
@@ -40,6 +41,7 @@ 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.RecordComponentSymbol;
import com.google.turbine.binder.sym.Symbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.model.TurbineConstantTypeKind;
@@ -48,6 +50,7 @@ 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.TurbineRecordComponentElement;
import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
import com.google.turbine.processing.TurbineElement.TurbineTypeParameterElement;
import com.google.turbine.processing.TurbineTypeMirror.TurbineArrayType;
@@ -110,6 +113,8 @@ public class ModelFactory {
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<RecordComponentSymbol, TurbineRecordComponentElement> recordComponentCache =
+ new HashMap<>();
private final Map<TyVarSymbol, TurbineTypeParameterElement> tyParamCache = new HashMap<>();
private final Map<PackageSymbol, TurbinePackageElement> packageCache = new HashMap<>();
@@ -230,6 +235,8 @@ public class ModelFactory {
return fieldElement((FieldSymbol) symbol);
case PARAMETER:
return parameterElement((ParamSymbol) symbol);
+ case RECORD_COMPONENT:
+ return recordComponentElement((RecordComponentSymbol) symbol);
case PACKAGE:
return packageElement((PackageSymbol) symbol);
case MODULE:
@@ -263,6 +270,11 @@ public class ModelFactory {
return paramCache.computeIfAbsent(sym, k -> new TurbineParameterElement(this, sym));
}
+ VariableElement recordComponentElement(RecordComponentSymbol sym) {
+ return recordComponentCache.computeIfAbsent(
+ sym, k -> new TurbineRecordComponentElement(this, sym));
+ }
+
TurbineTypeParameterElement typeParameterElement(TyVarSymbol sym) {
return tyParamCache.computeIfAbsent(sym, k -> new TurbineTypeParameterElement(this, sym));
}
@@ -330,6 +342,16 @@ public class ModelFactory {
return null;
}
+ RecordComponentInfo getRecordComponentInfo(RecordComponentSymbol sym) {
+ TypeBoundClass info = getSymbol(sym.owner());
+ for (RecordComponentInfo component : info.components()) {
+ if (component.sym().equals(sym)) {
+ return component;
+ }
+ }
+ return null;
+ }
+
FieldInfo getFieldInfo(FieldSymbol symbol) {
TypeBoundClass info = getSymbol(symbol.owner());
requireNonNull(info, symbol.owner().toString());
@@ -370,6 +392,8 @@ public class ModelFactory {
return ((FieldSymbol) sym).owner();
case PARAMETER:
return ((ParamSymbol) sym).owner().owner();
+ case RECORD_COMPONENT:
+ return ((RecordComponentSymbol) sym).owner();
case PACKAGE:
case MODULE:
throw new IllegalArgumentException(sym.toString());
diff --git a/java/com/google/turbine/processing/TurbineAnnotationMirror.java b/java/com/google/turbine/processing/TurbineAnnotationMirror.java
index df3bd19..f99d211 100644
--- a/java/com/google/turbine/processing/TurbineAnnotationMirror.java
+++ b/java/com/google/turbine/processing/TurbineAnnotationMirror.java
@@ -45,6 +45,7 @@ import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ErrorType;
import javax.lang.model.type.TypeMirror;
+import org.jspecify.nullness.Nullable;
/**
* An implementation of {@link AnnotationMirror} and {@link AnnotationValue} backed by {@link
@@ -105,7 +106,7 @@ class TurbineAnnotationMirror implements TurbineAnnotationValueMirror, Annotatio
checkState(m.parameters().isEmpty());
result.put(m.name(), m);
}
- return result.build();
+ return result.buildOrThrow();
}
});
this.elementValues =
@@ -125,7 +126,7 @@ class TurbineAnnotationMirror implements TurbineAnnotationValueMirror, Annotatio
factory.executableElement(methodInfo.sym()),
annotationValue(factory, value.getValue()));
}
- return result.build();
+ return result.buildOrThrow();
}
});
this.elementValuesWithDefaults =
@@ -156,7 +157,7 @@ class TurbineAnnotationMirror implements TurbineAnnotationValueMirror, Annotatio
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbineAnnotationMirror
&& anno.equals(((TurbineAnnotationMirror) obj).anno);
}
@@ -342,7 +343,7 @@ class TurbineAnnotationMirror implements TurbineAnnotationValueMirror, Annotatio
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbinePrimitiveConstant
&& value.equals(((TurbinePrimitiveConstant) obj).value);
}
diff --git a/java/com/google/turbine/processing/TurbineElement.java b/java/com/google/turbine/processing/TurbineElement.java
index f4f1675..95f0f42 100644
--- a/java/com/google/turbine/processing/TurbineElement.java
+++ b/java/com/google/turbine/processing/TurbineElement.java
@@ -21,6 +21,7 @@ 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.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@@ -32,6 +33,7 @@ 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.RecordComponentInfo;
import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
import com.google.turbine.binder.lookup.PackageScope;
import com.google.turbine.binder.sym.ClassSymbol;
@@ -39,6 +41,7 @@ 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.RecordComponentSymbol;
import com.google.turbine.binder.sym.Symbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.diag.TurbineError;
@@ -79,9 +82,10 @@ 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;
+import org.jspecify.nullness.Nullable;
/** An {@link Element} implementation backed by a {@link Symbol}. */
+@SuppressWarnings("nullness") // TODO(cushon): Address nullness diagnostics.
public abstract class TurbineElement implements Element {
public abstract Symbol sym();
@@ -92,7 +96,7 @@ public abstract class TurbineElement implements Element {
public abstract int hashCode();
@Override
- public abstract boolean equals(Object obj);
+ public abstract boolean equals(@Nullable Object obj);
protected final ModelFactory factory;
private final Supplier<ImmutableList<AnnotationMirror>> annotationMirrors;
@@ -259,6 +263,7 @@ public abstract class TurbineElement implements Element {
switch (info.kind()) {
case CLASS:
case ENUM:
+ case RECORD:
if (info.superclass() != null) {
return factory.asTypeMirror(info.superClassType());
}
@@ -375,10 +380,21 @@ public abstract class TurbineElement implements Element {
return ElementKind.ENUM;
case ANNOTATION:
return ElementKind.ANNOTATION_TYPE;
+ case RECORD:
+ return RECORD.get();
}
throw new AssertionError(info.kind());
}
+ private static final Supplier<ElementKind> RECORD =
+ Suppliers.memoize(
+ new Supplier<ElementKind>() {
+ @Override
+ public ElementKind get() {
+ return ElementKind.valueOf("RECORD");
+ }
+ });
+
@Override
public Set<Modifier> getModifiers() {
return asModifierSet(ModifierOwner.TYPE, infoNonNull().access() & ~TurbineFlag.ACC_SUPER);
@@ -426,6 +442,9 @@ public abstract class TurbineElement implements Element {
public ImmutableList<Element> get() {
TypeBoundClass info = infoNonNull();
ImmutableList.Builder<Element> result = ImmutableList.builder();
+ for (RecordComponentInfo component : info.components()) {
+ result.add(factory.recordComponentElement(component.sym()));
+ }
for (FieldInfo field : info.fields()) {
result.add(factory.fieldElement(field.sym()));
}
@@ -464,7 +483,7 @@ public abstract class TurbineElement implements Element {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbineTypeElement && sym.equals(((TurbineTypeElement) obj).sym);
}
@@ -552,7 +571,7 @@ public abstract class TurbineElement implements Element {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbineTypeParameterElement
&& sym.equals(((TurbineTypeParameterElement) obj).sym);
}
@@ -573,8 +592,7 @@ public abstract class TurbineElement implements Element {
}
});
- @Nullable
- private TyVarInfo info() {
+ private @Nullable TyVarInfo info() {
return info.get();
}
@@ -686,7 +704,7 @@ public abstract class TurbineElement implements Element {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbineExecutableElement
&& sym.equals(((TurbineExecutableElement) obj).sym);
}
@@ -834,7 +852,7 @@ public abstract class TurbineElement implements Element {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbineFieldElement && sym.equals(((TurbineFieldElement) obj).sym);
}
@@ -1032,6 +1050,7 @@ public abstract class TurbineElement implements Element {
public List<TurbineTypeElement> getEnclosedElements() {
ImmutableSet.Builder<TurbineTypeElement> result = ImmutableSet.builder();
PackageScope scope = factory.tli().lookupPackage(Splitter.on('/').split(sym.binaryName()));
+ requireNonNull(scope); // the current package exists
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.
@@ -1067,7 +1086,7 @@ public abstract class TurbineElement implements Element {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbinePackageElement && sym.equals(((TurbinePackageElement) obj).sym);
}
@@ -1112,7 +1131,7 @@ public abstract class TurbineElement implements Element {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbineParameterElement
&& sym.equals(((TurbineParameterElement) obj).sym);
}
@@ -1198,6 +1217,120 @@ public abstract class TurbineElement implements Element {
}
}
+ /** A {@link VariableElement} implementation for a record info. */
+ static class TurbineRecordComponentElement extends TurbineElement implements VariableElement {
+
+ @Override
+ public RecordComponentSymbol sym() {
+ return sym;
+ }
+
+ @Override
+ public String javadoc() {
+ return null;
+ }
+
+ @Override
+ public int hashCode() {
+ return sym.hashCode();
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ return obj instanceof TurbineRecordComponentElement
+ && sym.equals(((TurbineRecordComponentElement) obj).sym);
+ }
+
+ private final RecordComponentSymbol sym;
+
+ private final Supplier<RecordComponentInfo> info =
+ memoize(
+ new Supplier<RecordComponentInfo>() {
+ @Override
+ public RecordComponentInfo get() {
+ return factory.getRecordComponentInfo(sym);
+ }
+ });
+
+ @Nullable
+ RecordComponentInfo info() {
+ return info.get();
+ }
+
+ public TurbineRecordComponentElement(ModelFactory factory, RecordComponentSymbol 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 RECORD_COMPONENT.get();
+ }
+
+ private static final Supplier<ElementKind> RECORD_COMPONENT =
+ Suppliers.memoize(
+ new Supplier<ElementKind>() {
+ @Override
+ public ElementKind get() {
+ return ElementKind.valueOf("RECORD_COMPONENT");
+ }
+ });
+
+ @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.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
+ 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;
diff --git a/java/com/google/turbine/processing/TurbineElements.java b/java/com/google/turbine/processing/TurbineElements.java
index 7ede6e3..b5fd7f4 100644
--- a/java/com/google/turbine/processing/TurbineElements.java
+++ b/java/com/google/turbine/processing/TurbineElements.java
@@ -29,6 +29,7 @@ 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.TurbineFlag;
import com.google.turbine.model.TurbineVisibility;
import com.google.turbine.processing.TurbineElement.TurbineExecutableElement;
import com.google.turbine.processing.TurbineElement.TurbineFieldElement;
@@ -52,8 +53,10 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
+import org.jspecify.nullness.Nullable;
/** An implementation of {@link Elements} backed by turbine's {@link Element}. */
+@SuppressWarnings("nullness") // TODO(cushon): Address nullness diagnostics.
public class TurbineElements implements Elements {
private final ModelFactory factory;
@@ -289,7 +292,89 @@ public class TurbineElements implements Elements {
@Override
public boolean hides(Element hider, Element hidden) {
- throw new UnsupportedOperationException();
+ if (!(hider instanceof TurbineElement)) {
+ throw new IllegalArgumentException(hider.toString());
+ }
+ if (!(hidden instanceof TurbineElement)) {
+ throw new IllegalArgumentException(hidden.toString());
+ }
+ return hides((TurbineElement) hider, (TurbineElement) hidden);
+ }
+
+ private boolean hides(TurbineElement hider, TurbineElement hidden) {
+ if (!hider.sym().symKind().equals(hidden.sym().symKind())) {
+ return false;
+ }
+ if (!hider.getSimpleName().equals(hidden.getSimpleName())) {
+ return false;
+ }
+ if (hider.sym().equals(hidden.sym())) {
+ return false;
+ }
+ if (!isVisibleForHiding(hider, hidden)) {
+ return false;
+ }
+ if (hider.sym().symKind().equals(Symbol.Kind.METHOD)) {
+ int access = ((TurbineExecutableElement) hider).info().access();
+ if ((access & TurbineFlag.ACC_STATIC) != TurbineFlag.ACC_STATIC) {
+ return false;
+ }
+ // Static interface methods shouldn't be able to hide static methods in super-interfaces,
+ // but include them anyways for bug-compatibility with javac, see:
+ // https://bugs.openjdk.java.net/browse/JDK-8275746
+ if (!types.isSubsignature(
+ (TurbineExecutableType) hider.asType(), (TurbineExecutableType) hidden.asType())) {
+ return false;
+ }
+ }
+ Element containingHider = containingClass(hider);
+ Element containingHidden = containingClass(hidden);
+ if (containingHider == null || containingHidden == null) {
+ return false;
+ }
+ if (!types.isSubtype(containingHider.asType(), containingHidden.asType())) {
+ return false;
+ }
+ return true;
+ }
+
+ private static @Nullable Element containingClass(TurbineElement element) {
+ Element enclosing = element.getEnclosingElement();
+ if (enclosing == null) {
+ return null;
+ }
+ if (!isClassOrInterface(enclosing.getKind())) {
+ // The immediately enclosing element of a field or method is a class. For classes, annotation
+ // processing only deals with top-level and nested (but not local or anonymous) classes,
+ // so the immediately enclosing element is either an enclosing class or a package symbol.
+ return null;
+ }
+ return enclosing;
+ }
+
+ private static boolean isClassOrInterface(ElementKind kind) {
+ return kind.isClass() || kind.isInterface();
+ }
+
+ private static boolean isVisibleForHiding(TurbineElement hider, TurbineElement hidden) {
+ int access;
+ switch (hidden.sym().symKind()) {
+ case CLASS:
+ access = ((TurbineTypeElement) hidden).info().access();
+ break;
+ case FIELD:
+ access = ((TurbineFieldElement) hidden).info().access();
+ break;
+ case METHOD:
+ access = ((TurbineExecutableElement) hidden).info().access();
+ break;
+ default:
+ return false;
+ }
+ return isVisible(
+ packageSymbol(asSymbol(hider)),
+ packageSymbol(asSymbol(hidden)),
+ TurbineVisibility.fromAccess(access));
}
@Override
diff --git a/java/com/google/turbine/processing/TurbineMessager.java b/java/com/google/turbine/processing/TurbineMessager.java
index 9c333b2..8e78b8b 100644
--- a/java/com/google/turbine/processing/TurbineMessager.java
+++ b/java/com/google/turbine/processing/TurbineMessager.java
@@ -42,7 +42,7 @@ 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;
+import org.jspecify.nullness.Nullable;
/** Turbine's implementation of {@link Messager}. */
public class TurbineMessager implements Messager {
@@ -103,8 +103,7 @@ public class TurbineMessager implements Messager {
* 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) {
+ private @Nullable SourceFile getSource(Symbol sym) {
ClassSymbol encl = ModelFactory.enclosingClass(sym);
TypeBoundClass info = factory.getSymbol(encl);
if (!(info instanceof SourceTypeBoundClass)) {
@@ -129,6 +128,10 @@ public class TurbineMessager implements Messager {
return fieldPosition((FieldSymbol) sym);
case PARAMETER:
return paramPosition((ParamSymbol) sym);
+ case RECORD_COMPONENT:
+ // javac doesn't seem to provide diagnostic positions for record components, so we don't
+ // either
+ return -1;
case MODULE:
case PACKAGE:
break;
diff --git a/java/com/google/turbine/processing/TurbineName.java b/java/com/google/turbine/processing/TurbineName.java
index 584b1b1..5232491 100644
--- a/java/com/google/turbine/processing/TurbineName.java
+++ b/java/com/google/turbine/processing/TurbineName.java
@@ -19,6 +19,7 @@ package com.google.turbine.processing;
import static java.util.Objects.requireNonNull;
import javax.lang.model.element.Name;
+import org.jspecify.nullness.Nullable;
/** An implementation of {@link Name} backed by a {@link CharSequence}. */
public class TurbineName implements Name {
@@ -61,7 +62,7 @@ public class TurbineName implements Name {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable 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
index 8b44e75..4f32033 100644
--- a/java/com/google/turbine/processing/TurbineProcessingEnvironment.java
+++ b/java/com/google/turbine/processing/TurbineProcessingEnvironment.java
@@ -24,7 +24,7 @@ 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;
+import org.jspecify.nullness.Nullable;
/** Turbine's {@link ProcessingEnvironment}. */
public class TurbineProcessingEnvironment implements ProcessingEnvironment {
diff --git a/java/com/google/turbine/processing/TurbineTypeMirror.java b/java/com/google/turbine/processing/TurbineTypeMirror.java
index e94672c..4cd8ba1 100644
--- a/java/com/google/turbine/processing/TurbineTypeMirror.java
+++ b/java/com/google/turbine/processing/TurbineTypeMirror.java
@@ -58,6 +58,7 @@ import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.TypeVisitor;
import javax.lang.model.type.WildcardType;
+import org.jspecify.nullness.Nullable;
/** A {@link TypeMirror} implementation backed by a {@link Type}. */
public abstract class TurbineTypeMirror implements TypeMirror {
@@ -165,7 +166,7 @@ public abstract class TurbineTypeMirror implements TypeMirror {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbineDeclaredType && type.equals(((TurbineDeclaredType) obj).type);
}
@@ -377,7 +378,7 @@ public abstract class TurbineTypeMirror implements TypeMirror {
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
return other instanceof TurbinePackageType
&& symbol.equals(((TurbinePackageType) other).symbol);
}
@@ -421,7 +422,7 @@ public abstract class TurbineTypeMirror implements TypeMirror {
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
return other instanceof TurbineNoType;
}
@@ -473,7 +474,7 @@ public abstract class TurbineTypeMirror implements TypeMirror {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbineTypeVariable && type.equals(((TurbineTypeVariable) obj).type);
}
@@ -566,7 +567,7 @@ public abstract class TurbineTypeMirror implements TypeMirror {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbineWildcardType && type.equals(((TurbineWildcardType) obj).type);
}
@@ -607,7 +608,7 @@ public abstract class TurbineTypeMirror implements TypeMirror {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbineIntersectionType
&& type.equals(((TurbineIntersectionType) obj).type);
}
@@ -670,7 +671,7 @@ public abstract class TurbineTypeMirror implements TypeMirror {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof NullType;
}
@@ -711,7 +712,7 @@ public abstract class TurbineTypeMirror implements TypeMirror {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof TurbineExecutableType
&& type.equals(((TurbineExecutableType) obj).type);
}
diff --git a/java/com/google/turbine/processing/TurbineTypes.java b/java/com/google/turbine/processing/TurbineTypes.java
index 7d2e6c0..d2068dd 100644
--- a/java/com/google/turbine/processing/TurbineTypes.java
+++ b/java/com/google/turbine/processing/TurbineTypes.java
@@ -29,6 +29,7 @@ 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.RecordComponentSymbol;
import com.google.turbine.binder.sym.Symbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.model.TurbineConstantTypeKind;
@@ -68,9 +69,10 @@ 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;
+import org.jspecify.nullness.Nullable;
/** An implementation of {@link Types} backed by turbine's {@link TypeMirror}. */
+@SuppressWarnings("nullness") // TODO(cushon): Address nullness diagnostics.
public class TurbineTypes implements Types {
private final ModelFactory factory;
@@ -217,8 +219,15 @@ public class TurbineTypes implements Types {
if (bounds.isEmpty()) {
return true;
}
- ClassTy first = (ClassTy) bounds.get(0);
- return factory.getSymbol(first.sym()).kind().equals(TurbineTyKind.INTERFACE);
+ Type bound = bounds.get(0);
+ switch (bound.tyKind()) {
+ case TY_VAR:
+ return false;
+ case CLASS_TY:
+ return factory.getSymbol(((ClassTy) bound).sym()).kind().equals(TurbineTyKind.INTERFACE);
+ default:
+ throw new AssertionError(bound.tyKind());
+ }
}
private boolean isSameWildType(WildTy a, Type other) {
@@ -364,8 +373,8 @@ public class TurbineTypes implements Types {
}
private boolean isTyVarSubtype(TyVar a, Type b, boolean strict) {
- if (b.tyKind() == TyKind.TY_VAR) {
- return a.sym().equals(((TyVar) b).sym());
+ if (b.tyKind() == TyKind.TY_VAR && a.sym().equals(((TyVar) b).sym())) {
+ return true;
}
TyVarInfo tyVarInfo = factory.getTyVarInfo(a.sym());
return isSubtype(tyVarInfo.upperBound(), b, strict);
@@ -520,11 +529,12 @@ public class TurbineTypes implements Types {
}
/**
- * Given two parameterizations of the same {@link SimpleClassTy}, {@code a} and {@code b}, teturns
+ * Given two parameterizations of the same {@link SimpleClassTy}, {@code a} and {@code b}, returns
* 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.
+ * @see #contains
+ * @see "JLS 4.5.1"
*/
private boolean tyArgsContains(SimpleClassTy a, SimpleClassTy b, boolean strict) {
verify(a.sym().equals(b.sym()));
@@ -624,8 +634,7 @@ public class TurbineTypes implements Types {
* 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) {
+ private static @Nullable ImmutableMap<TyVarSymbol, Type> getMapping(MethodTy a, MethodTy b) {
if (a.tyParams().size() != b.tyParams().size()) {
return null;
}
@@ -637,15 +646,14 @@ public class TurbineTypes implements Types {
TyVarSymbol t = bx.next();
mapping.put(t, TyVar.create(s, ImmutableList.of()));
}
- return mapping.build();
+ return mapping.buildOrThrow();
}
/**
* 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) {
+ private @Nullable ImmutableMap<TyVarSymbol, Type> getMapping(ClassTy ty) {
ImmutableMap.Builder<TyVarSymbol, Type> mapping = ImmutableMap.builder();
for (SimpleClassTy s : ty.classes()) {
TypeBoundClass info = factory.getSymbol(s.sym());
@@ -659,7 +667,7 @@ public class TurbineTypes implements Types {
}
verify(!bx.hasNext());
}
- return mapping.build();
+ return mapping.buildOrThrow();
}
@Override
@@ -1131,6 +1139,8 @@ public class TurbineTypes implements Types {
return ((FieldSymbol) symbol).owner();
case PARAMETER:
return ((ParamSymbol) symbol).owner().owner();
+ case RECORD_COMPONENT:
+ return ((RecordComponentSymbol) symbol).owner();
case MODULE:
case PACKAGE:
throw new IllegalArgumentException(symbol.symKind().toString());
diff --git a/java/com/google/turbine/processing/package-info.java b/java/com/google/turbine/processing/package-info.java
new file mode 100644
index 0000000..abf6732
--- /dev/null
+++ b/java/com/google/turbine/processing/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+package com.google.turbine.processing;
diff --git a/java/com/google/turbine/tree/Pretty.java b/java/com/google/turbine/tree/Pretty.java
index b693a42..4ebc04f 100644
--- a/java/com/google/turbine/tree/Pretty.java
+++ b/java/com/google/turbine/tree/Pretty.java
@@ -20,6 +20,8 @@ import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.turbine.model.TurbineTyKind;
import com.google.turbine.tree.Tree.Anno;
import com.google.turbine.tree.Tree.ClassLiteral;
import com.google.turbine.tree.Tree.Ident;
@@ -33,9 +35,10 @@ import com.google.turbine.tree.Tree.ModUses;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import org.jspecify.nullness.Nullable;
/** A pretty-printer for {@link Tree}s. */
-public class Pretty implements Tree.Visitor<Void, Void> {
+public class Pretty implements Tree.Visitor<@Nullable Void, @Nullable Void> {
static String pretty(Tree tree) {
Pretty pretty = new Pretty();
@@ -60,6 +63,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
newLine = true;
}
+ @CanIgnoreReturnValue
Pretty append(char c) {
if (c == '\n') {
newLine = true;
@@ -71,6 +75,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
return this;
}
+ @CanIgnoreReturnValue
Pretty append(String s) {
if (newLine) {
sb.append(Strings.repeat(" ", indent * 2));
@@ -81,13 +86,13 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitIdent(Ident ident, Void input) {
+ public @Nullable Void visitIdent(Ident ident, @Nullable Void input) {
sb.append(ident.value());
return null;
}
@Override
- public Void visitWildTy(Tree.WildTy wildTy, Void input) {
+ public @Nullable Void visitWildTy(Tree.WildTy wildTy, @Nullable Void input) {
printAnnos(wildTy.annos());
append('?');
if (wildTy.lower().isPresent()) {
@@ -102,7 +107,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitArrTy(Tree.ArrTy arrTy, Void input) {
+ public @Nullable Void visitArrTy(Tree.ArrTy arrTy, @Nullable Void input) {
arrTy.elem().accept(this, null);
if (!arrTy.annos().isEmpty()) {
append(' ');
@@ -113,19 +118,19 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitPrimTy(Tree.PrimTy primTy, Void input) {
+ public @Nullable Void visitPrimTy(Tree.PrimTy primTy, @Nullable Void input) {
append(primTy.tykind().toString());
return null;
}
@Override
- public Void visitVoidTy(Tree.VoidTy primTy, Void input) {
+ public @Nullable Void visitVoidTy(Tree.VoidTy voidTy, @Nullable Void input) {
append("void");
return null;
}
@Override
- public Void visitClassTy(Tree.ClassTy classTy, Void input) {
+ public @Nullable Void visitClassTy(Tree.ClassTy classTy, @Nullable Void input) {
if (classTy.base().isPresent()) {
classTy.base().get().accept(this, null);
append('.');
@@ -148,13 +153,19 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitLiteral(Tree.Literal literal, Void input) {
+ public @Nullable Void visitLiteral(Tree.Literal literal, @Nullable Void input) {
append(literal.value().toString());
return null;
}
@Override
- public Void visitTypeCast(Tree.TypeCast typeCast, Void input) {
+ public @Nullable Void visitParen(Tree.Paren paren, @Nullable Void input) {
+ paren.expr().accept(this, null);
+ return null;
+ }
+
+ @Override
+ public @Nullable Void visitTypeCast(Tree.TypeCast typeCast, @Nullable Void input) {
append('(');
typeCast.ty().accept(this, null);
append(") ");
@@ -163,7 +174,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitUnary(Tree.Unary unary, Void input) {
+ public @Nullable Void visitUnary(Tree.Unary unary, @Nullable Void input) {
switch (unary.op()) {
case POST_INCR:
case POST_DECR:
@@ -186,37 +197,42 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitBinary(Tree.Binary binary, Void input) {
+ public @Nullable Void visitBinary(Tree.Binary binary, @Nullable Void input) {
append('(');
- binary.lhs().accept(this, null);
- append(" " + binary.op() + " ");
- binary.rhs().accept(this, null);
+ boolean first = true;
+ for (Tree child : binary.children()) {
+ if (!first) {
+ append(" ").append(binary.op().toString()).append(" ");
+ }
+ child.accept(this, null);
+ first = false;
+ }
append(')');
return null;
}
@Override
- public Void visitConstVarName(Tree.ConstVarName constVarName, Void input) {
+ public @Nullable Void visitConstVarName(Tree.ConstVarName constVarName, @Nullable Void input) {
append(Joiner.on('.').join(constVarName.name()));
return null;
}
@Override
- public Void visitClassLiteral(ClassLiteral classLiteral, Void input) {
- classLiteral.accept(this, input);
+ public @Nullable Void visitClassLiteral(ClassLiteral classLiteral, @Nullable Void input) {
+ classLiteral.type().accept(this, input);
append(".class");
return null;
}
@Override
- public Void visitAssign(Tree.Assign assign, Void input) {
+ public @Nullable Void visitAssign(Tree.Assign assign, @Nullable Void input) {
append(assign.name().value()).append(" = ");
assign.expr().accept(this, null);
return null;
}
@Override
- public Void visitConditional(Tree.Conditional conditional, Void input) {
+ public @Nullable Void visitConditional(Tree.Conditional conditional, @Nullable Void input) {
append("(");
conditional.cond().accept(this, null);
append(" ? ");
@@ -228,7 +244,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitArrayInit(Tree.ArrayInit arrayInit, Void input) {
+ public @Nullable Void visitArrayInit(Tree.ArrayInit arrayInit, @Nullable Void input) {
append('{');
boolean first = true;
for (Tree.Expression e : arrayInit.exprs()) {
@@ -243,7 +259,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitCompUnit(Tree.CompUnit compUnit, Void input) {
+ public @Nullable Void visitCompUnit(Tree.CompUnit compUnit, @Nullable Void input) {
if (compUnit.pkg().isPresent()) {
compUnit.pkg().get().accept(this, null);
printLine();
@@ -263,7 +279,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitImportDecl(Tree.ImportDecl importDecl, Void input) {
+ public @Nullable Void visitImportDecl(Tree.ImportDecl importDecl, @Nullable Void input) {
append("import ");
if (importDecl.stat()) {
append("static ");
@@ -277,7 +293,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitVarDecl(Tree.VarDecl varDecl, Void input) {
+ public @Nullable Void visitVarDecl(Tree.VarDecl varDecl, @Nullable Void input) {
printVarDecl(varDecl);
append(';');
return null;
@@ -302,7 +318,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitMethDecl(Tree.MethDecl methDecl, Void input) {
+ public @Nullable Void visitMethDecl(Tree.MethDecl methDecl, @Nullable Void input) {
for (Tree.Anno anno : methDecl.annos()) {
anno.accept(this, null);
printLine();
@@ -361,7 +377,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitAnno(Tree.Anno anno, Void input) {
+ public @Nullable Void visitAnno(Tree.Anno anno, @Nullable Void input) {
append('@');
append(Joiner.on('.').join(anno.name()));
if (!anno.args().isEmpty()) {
@@ -380,7 +396,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitTyDecl(Tree.TyDecl tyDecl, Void input) {
+ public @Nullable Void visitTyDecl(Tree.TyDecl tyDecl, @Nullable Void input) {
for (Tree.Anno anno : tyDecl.annos()) {
anno.accept(this, null);
printLine();
@@ -399,6 +415,9 @@ public class Pretty implements Tree.Visitor<Void, Void> {
case ANNOTATION:
append("@interface");
break;
+ case RECORD:
+ append("record");
+ break;
}
append(' ').append(tyDecl.name().value());
if (!tyDecl.typarams().isEmpty()) {
@@ -413,6 +432,18 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
append('>');
}
+ if (tyDecl.tykind().equals(TurbineTyKind.RECORD)) {
+ append("(");
+ boolean first = true;
+ for (Tree.VarDecl c : tyDecl.components()) {
+ if (!first) {
+ append(", ");
+ }
+ printVarDecl(c);
+ first = false;
+ }
+ append(")");
+ }
if (tyDecl.xtnds().isPresent()) {
append(" extends ");
tyDecl.xtnds().get().accept(this, null);
@@ -428,6 +459,17 @@ public class Pretty implements Tree.Visitor<Void, Void> {
first = false;
}
}
+ if (!tyDecl.permits().isEmpty()) {
+ append(" permits ");
+ boolean first = true;
+ for (Tree.ClassTy t : tyDecl.permits()) {
+ if (!first) {
+ append(", ");
+ }
+ t.accept(this, null);
+ first = false;
+ }
+ }
append(" {").append('\n');
indent++;
switch (tyDecl.tykind()) {
@@ -491,6 +533,8 @@ public class Pretty implements Tree.Visitor<Void, Void> {
case TRANSIENT:
case DEFAULT:
case TRANSITIVE:
+ case SEALED:
+ case NON_SEALED:
append(mod.toString()).append(' ');
break;
case ACC_SUPER:
@@ -500,13 +544,14 @@ public class Pretty implements Tree.Visitor<Void, Void> {
case ACC_ANNOTATION:
case ACC_SYNTHETIC:
case ACC_BRIDGE:
+ case COMPACT_CTOR:
break;
}
}
}
@Override
- public Void visitTyParam(Tree.TyParam tyParam, Void input) {
+ public @Nullable Void visitTyParam(Tree.TyParam tyParam, @Nullable Void input) {
printAnnos(tyParam.annos());
append(tyParam.name().value());
if (!tyParam.bounds().isEmpty()) {
@@ -524,7 +569,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitPkgDecl(Tree.PkgDecl pkgDecl, Void input) {
+ public @Nullable Void visitPkgDecl(Tree.PkgDecl pkgDecl, @Nullable Void input) {
for (Tree.Anno anno : pkgDecl.annos()) {
anno.accept(this, null);
printLine();
@@ -534,7 +579,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitModDecl(ModDecl modDecl, Void input) {
+ public @Nullable Void visitModDecl(ModDecl modDecl, @Nullable Void input) {
for (Tree.Anno anno : modDecl.annos()) {
anno.accept(this, null);
printLine();
@@ -554,7 +599,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitModRequires(ModRequires modRequires, Void input) {
+ public @Nullable Void visitModRequires(ModRequires modRequires, @Nullable Void input) {
append("requires ");
printModifiers(modRequires.mods());
append(modRequires.moduleName());
@@ -564,7 +609,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitModExports(ModExports modExports, Void input) {
+ public @Nullable Void visitModExports(ModExports modExports, @Nullable Void input) {
append("exports ");
append(modExports.packageName().replace('/', '.'));
if (!modExports.moduleNames().isEmpty()) {
@@ -586,7 +631,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitModOpens(ModOpens modOpens, Void input) {
+ public @Nullable Void visitModOpens(ModOpens modOpens, @Nullable Void input) {
append("opens ");
append(modOpens.packageName().replace('/', '.'));
if (!modOpens.moduleNames().isEmpty()) {
@@ -608,7 +653,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitModUses(ModUses modUses, Void input) {
+ public @Nullable Void visitModUses(ModUses modUses, @Nullable Void input) {
append("uses ");
append(Joiner.on('.').join(modUses.typeName()));
append(";");
@@ -617,7 +662,7 @@ public class Pretty implements Tree.Visitor<Void, Void> {
}
@Override
- public Void visitModProvides(ModProvides modProvides, Void input) {
+ public @Nullable Void visitModProvides(ModProvides modProvides, @Nullable Void input) {
append("provides ");
append(Joiner.on('.').join(modProvides.typeName()));
if (!modProvides.implNames().isEmpty()) {
diff --git a/java/com/google/turbine/tree/Tree.java b/java/com/google/turbine/tree/Tree.java
index d36c3ab..f7917b9 100644
--- a/java/com/google/turbine/tree/Tree.java
+++ b/java/com/google/turbine/tree/Tree.java
@@ -25,15 +25,19 @@ import com.google.turbine.diag.SourceFile;
import com.google.turbine.model.Const;
import com.google.turbine.model.TurbineConstantTypeKind;
import com.google.turbine.model.TurbineTyKind;
+import java.util.ArrayDeque;
+import java.util.Deque;
import java.util.Optional;
import java.util.Set;
+import org.jspecify.nullness.Nullable;
/** An AST node. */
public abstract class Tree {
public abstract Kind kind();
- public abstract <I, O> O accept(Visitor<I, O> visitor, I input);
+ public abstract <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input);
private final int position;
@@ -59,6 +63,7 @@ public abstract class Tree {
VOID_TY,
CLASS_TY,
LITERAL,
+ PAREN,
TYPE_CAST,
UNARY,
BINARY,
@@ -101,7 +106,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitIdent(this, input);
}
@@ -154,7 +160,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitWildTy(this, input);
}
@@ -192,7 +199,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitArrTy(this, input);
}
@@ -221,7 +229,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitPrimTy(this, input);
}
@@ -240,7 +249,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitVoidTy(this, input);
}
@@ -273,7 +283,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitClassTy(this, input);
}
@@ -314,7 +325,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitLiteral(this, input);
}
@@ -327,6 +339,31 @@ public abstract class Tree {
}
}
+ /** A JLS 15.8.5 parenthesized expression. */
+ public static class Paren extends Expression {
+ private final Expression expr;
+
+ public Paren(int position, Expression expr) {
+ super(position);
+ this.expr = expr;
+ }
+
+ @Override
+ public Kind kind() {
+ return Kind.PAREN;
+ }
+
+ @Override
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
+ return visitor.visitParen(this, input);
+ }
+
+ public Expression expr() {
+ return expr;
+ }
+ }
+
/** A JLS 15.16 cast expression. */
public static class TypeCast extends Expression {
private final Type ty;
@@ -344,7 +381,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitTypeCast(this, input);
}
@@ -374,7 +412,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitUnary(this, input);
}
@@ -406,16 +445,29 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitBinary(this, input);
}
- public Expression lhs() {
- return lhs;
- }
-
- public Expression rhs() {
- return rhs;
+ public Iterable<Expression> children() {
+ ImmutableList.Builder<Expression> children = ImmutableList.builder();
+ Deque<Expression> stack = new ArrayDeque<>();
+ stack.addFirst(rhs);
+ stack.addFirst(lhs);
+ while (!stack.isEmpty()) {
+ Expression curr = stack.removeFirst();
+ if (curr.kind().equals(Kind.BINARY)) {
+ Binary b = ((Binary) curr);
+ if (b.op().equals(op())) {
+ stack.addFirst(b.rhs);
+ stack.addFirst(b.lhs);
+ continue;
+ }
+ }
+ children.add(curr);
+ }
+ return children.build();
}
public TurbineOperatorKind op() {
@@ -438,7 +490,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitConstVarName(this, input);
}
@@ -463,7 +516,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitClassLiteral(this, input);
}
@@ -489,7 +543,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitAssign(this, input);
}
@@ -521,7 +576,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitConditional(this, input);
}
@@ -553,7 +609,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitArrayInit(this, input);
}
@@ -591,7 +648,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitCompUnit(this, input);
}
@@ -635,7 +693,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitImportDecl(this, input);
}
@@ -661,7 +720,7 @@ public abstract class Tree {
private final Tree ty;
private final Ident name;
private final Optional<Expression> init;
- private final String javadoc;
+ private final @Nullable String javadoc;
public VarDecl(
int position,
@@ -670,7 +729,7 @@ public abstract class Tree {
Tree ty,
Ident name,
Optional<Expression> init,
- String javadoc) {
+ @Nullable String javadoc) {
super(position);
this.mods = ImmutableSet.copyOf(mods);
this.annos = annos;
@@ -686,7 +745,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitVarDecl(this, input);
}
@@ -714,7 +774,7 @@ public abstract class Tree {
* A javadoc comment, excluding the opening and closing delimiters but including all interior
* characters and whitespace.
*/
- public String javadoc() {
+ public @Nullable String javadoc() {
return javadoc;
}
}
@@ -760,7 +820,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitMethDecl(this, input);
}
@@ -821,7 +882,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitAnno(this, input);
}
@@ -858,7 +920,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitAnno(value, input);
}
}
@@ -871,9 +934,11 @@ public abstract class Tree {
private final ImmutableList<TyParam> typarams;
private final Optional<ClassTy> xtnds;
private final ImmutableList<ClassTy> impls;
+ private final ImmutableList<ClassTy> permits;
private final ImmutableList<Tree> members;
+ private final ImmutableList<VarDecl> components;
private final TurbineTyKind tykind;
- private final String javadoc;
+ private final @Nullable String javadoc;
public TyDecl(
int position,
@@ -883,9 +948,11 @@ public abstract class Tree {
ImmutableList<TyParam> typarams,
Optional<ClassTy> xtnds,
ImmutableList<ClassTy> impls,
+ ImmutableList<ClassTy> permits,
ImmutableList<Tree> members,
+ ImmutableList<VarDecl> components,
TurbineTyKind tykind,
- String javadoc) {
+ @Nullable String javadoc) {
super(position);
this.mods = ImmutableSet.copyOf(mods);
this.annos = annos;
@@ -893,7 +960,9 @@ public abstract class Tree {
this.typarams = typarams;
this.xtnds = xtnds;
this.impls = impls;
+ this.permits = permits;
this.members = members;
+ this.components = components;
this.tykind = tykind;
this.javadoc = javadoc;
}
@@ -904,7 +973,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitTyDecl(this, input);
}
@@ -932,10 +1002,18 @@ public abstract class Tree {
return impls;
}
+ public ImmutableList<ClassTy> permits() {
+ return permits;
+ }
+
public ImmutableList<Tree> members() {
return members;
}
+ public ImmutableList<VarDecl> components() {
+ return components;
+ }
+
public TurbineTyKind tykind() {
return tykind;
}
@@ -943,7 +1021,7 @@ public abstract class Tree {
* A javadoc comment, excluding the opening and closing delimiters but including all interior
* characters and whitespace.
*/
- public String javadoc() {
+ public @Nullable String javadoc() {
return javadoc;
}
}
@@ -968,7 +1046,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitTyParam(this, input);
}
@@ -1002,7 +1081,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitPkgDecl(this, input);
}
@@ -1058,7 +1138,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitModDecl(this, input);
}
}
@@ -1094,7 +1175,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitModRequires(this, input);
}
@@ -1130,7 +1212,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitModExports(this, input);
}
@@ -1180,7 +1263,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitModOpens(this, input);
}
@@ -1210,7 +1294,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitModUses(this, input);
}
@@ -1249,7 +1334,8 @@ public abstract class Tree {
}
@Override
- public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ public <I extends @Nullable Object, O extends @Nullable Object> O accept(
+ Visitor<I, O> visitor, I input) {
return visitor.visitModProvides(this, input);
}
@@ -1260,7 +1346,7 @@ public abstract class Tree {
}
/** A visitor for {@link Tree}s. */
- public interface Visitor<I, O> {
+ public interface Visitor<I extends @Nullable Object, O extends @Nullable Object> {
O visitIdent(Ident ident, I input);
O visitWildTy(WildTy visitor, I input);
@@ -1275,6 +1361,8 @@ public abstract class Tree {
O visitLiteral(Literal literal, I input);
+ O visitParen(Paren unary, I input);
+
O visitTypeCast(TypeCast typeCast, I input);
O visitUnary(Unary unary, I input);
diff --git a/java/com/google/turbine/tree/TurbineModifier.java b/java/com/google/turbine/tree/TurbineModifier.java
index 35dc11c..2bfe53e 100644
--- a/java/com/google/turbine/tree/TurbineModifier.java
+++ b/java/com/google/turbine/tree/TurbineModifier.java
@@ -45,7 +45,10 @@ public enum TurbineModifier {
ACC_SYNTHETIC(TurbineFlag.ACC_SYNTHETIC),
ACC_BRIDGE(TurbineFlag.ACC_BRIDGE),
DEFAULT(TurbineFlag.ACC_DEFAULT),
- TRANSITIVE(TurbineFlag.ACC_TRANSITIVE);
+ TRANSITIVE(TurbineFlag.ACC_TRANSITIVE),
+ SEALED(TurbineFlag.ACC_SEALED),
+ NON_SEALED(TurbineFlag.ACC_NON_SEALED),
+ COMPACT_CTOR(TurbineFlag.ACC_COMPACT_CTOR);
private final int flag;
@@ -59,6 +62,6 @@ public enum TurbineModifier {
@Override
public String toString() {
- return name().toLowerCase(ENGLISH);
+ return name().replace('_', '-').toLowerCase(ENGLISH);
}
}
diff --git a/java/com/google/turbine/tree/package-info.java b/java/com/google/turbine/tree/package-info.java
new file mode 100644
index 0000000..2803c67
--- /dev/null
+++ b/java/com/google/turbine/tree/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+@org.jspecify.nullness.NullMarked
+package com.google.turbine.tree;
diff --git a/java/com/google/turbine/type/AnnoInfo.java b/java/com/google/turbine/type/AnnoInfo.java
index ff902b3..d42af5c 100644
--- a/java/com/google/turbine/type/AnnoInfo.java
+++ b/java/com/google/turbine/type/AnnoInfo.java
@@ -29,6 +29,7 @@ import com.google.turbine.tree.Tree.Anno;
import com.google.turbine.tree.Tree.Expression;
import java.util.Map;
import java.util.Objects;
+import org.jspecify.nullness.Nullable;
/** An annotation use. */
public class AnnoInfo {
@@ -84,7 +85,7 @@ public class AnnoInfo {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof AnnoInfo)) {
return false;
}
diff --git a/java/com/google/turbine/type/Type.java b/java/com/google/turbine/type/Type.java
index bdddc6c..085346a 100644
--- a/java/com/google/turbine/type/Type.java
+++ b/java/com/google/turbine/type/Type.java
@@ -32,7 +32,7 @@ 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;
+import org.jspecify.nullness.Nullable;
/** JLS 4 types. */
public interface Type {
@@ -194,7 +194,7 @@ public interface Type {
}
@Override
- public final boolean equals(Object obj) {
+ public final boolean equals(@Nullable Object obj) {
if (!(obj instanceof ClassTy)) {
return false;
}
@@ -491,8 +491,7 @@ public interface Type {
public abstract Type returnType();
/** The type of the receiver parameter (see JLS 8.4.1). */
- @Nullable
- public abstract Type receiverType();
+ public abstract @Nullable Type receiverType();
public abstract ImmutableList<Type> parameters();
@@ -577,7 +576,7 @@ public interface Type {
}
@Override
- public final boolean equals(Object other) {
+ public final boolean equals(@Nullable 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.
diff --git a/java/com/google/turbine/type/package-info.java b/java/com/google/turbine/type/package-info.java
new file mode 100644
index 0000000..2329130
--- /dev/null
+++ b/java/com/google/turbine/type/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+package com.google.turbine.type;
diff --git a/java/com/google/turbine/types/Canonicalize.java b/java/com/google/turbine/types/Canonicalize.java
index 22df069..f944bb5 100644
--- a/java/com/google/turbine/types/Canonicalize.java
+++ b/java/com/google/turbine/types/Canonicalize.java
@@ -16,6 +16,8 @@
package com.google.turbine.types;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.base.Verify;
import com.google.common.collect.ImmutableList;
import com.google.turbine.binder.bound.TypeBoundClass;
@@ -44,7 +46,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
/**
* Canonicalizes qualified type names so qualifiers are always the declaring class of the qualified
@@ -208,7 +210,8 @@ public class Canonicalize {
return ClassTy.create(ImmutableList.of(ty));
}
ImmutableList.Builder<ClassTy.SimpleClassTy> simples = ImmutableList.builder();
- ClassSymbol owner = getInfo(ty.sym()).owner();
+ // this inner class is known to have an owner
+ ClassSymbol owner = requireNonNull(getInfo(ty.sym()).owner());
if (owner.equals(base.sym())) {
// if the canonical prefix is the owner the next symbol in the qualified name,
// the type is already in canonical form
@@ -281,7 +284,7 @@ public class Canonicalize {
}
/** Instantiates a type argument using the given mapping. */
- private static Type instantiate(Map<TyVarSymbol, Type> mapping, Type type) {
+ private static @Nullable Type instantiate(Map<TyVarSymbol, Type> mapping, Type type) {
if (type == null) {
return null;
}
@@ -328,7 +331,8 @@ public class Canonicalize {
for (SimpleClassTy simple : type.classes()) {
ImmutableList.Builder<Type> args = ImmutableList.builder();
for (Type arg : simple.targs()) {
- args.add(instantiate(mapping, arg));
+ // result is non-null if arg is
+ args.add(requireNonNull(instantiate(mapping, arg)));
}
simples.add(SimpleClassTy.create(simple.sym(), args.build(), simple.annos()));
}
@@ -339,8 +343,7 @@ public class Canonicalize {
* Returns the type variable symbol for a concrete type argument whose type is a type variable
* reference, or else {@code null}.
*/
- @Nullable
- private static TyVarSymbol tyVarSym(Type type) {
+ private static @Nullable TyVarSymbol tyVarSym(Type type) {
if (type.tyKind() == TyKind.TY_VAR) {
return ((TyVar) type).sym();
}
diff --git a/java/com/google/turbine/types/package-info.java b/java/com/google/turbine/types/package-info.java
new file mode 100644
index 0000000..fd541d7
--- /dev/null
+++ b/java/com/google/turbine/types/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+package com.google.turbine.types;
diff --git a/java/com/google/turbine/zip/package-info.java b/java/com/google/turbine/zip/package-info.java
new file mode 100644
index 0000000..069e5e1
--- /dev/null
+++ b/java/com/google/turbine/zip/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@com.google.errorprone.annotations.CheckReturnValue
+package com.google.turbine.zip;
diff --git a/javatests/com/google/turbine/binder/BinderErrorTest.java b/javatests/com/google/turbine/binder/BinderErrorTest.java
index e6e30cb..6766470 100644
--- a/javatests/com/google/turbine/binder/BinderErrorTest.java
+++ b/javatests/com/google/turbine/binder/BinderErrorTest.java
@@ -771,12 +771,6 @@ public class BinderErrorTest {
"<>:3: error: missing required annotation argument: value",
"@Retention",
"^",
- "<>:4: error: missing required annotation argument: value",
- "@Retention",
- "^",
- "<>:3: error: java.lang.annotation.Retention is not @Repeatable",
- "@Retention",
- "^",
},
},
{
@@ -812,6 +806,158 @@ public class BinderErrorTest {
" ^",
},
},
+ {
+ {
+ "@interface A {",
+ " boolean x();",
+ " boolean value();",
+ "}",
+ "@A(x = true, false)",
+ "class T {}",
+ },
+ {
+ "<>:5: error: expected an annotation value of the form name=value",
+ "@A(x = true, false)",
+ " ^",
+ },
+ },
+ {
+ {
+ "@interface A {",
+ " boolean value();",
+ "}",
+ "class B {",
+ " static final String X = \"hello\";",
+ "}",
+ "@A(B.X)",
+ "class T {}",
+ },
+ {
+ "<>:7: error: value \"hello\" of type String cannot be converted to boolean",
+ "@A(B.X)",
+ " ^",
+ },
+ },
+ {
+ {
+ "class T {", //
+ " public static final boolean b = true == 42;",
+ "}",
+ },
+ {
+ "<>:2: error: value 42 of type int cannot be converted to boolean",
+ " public static final boolean b = true == 42;",
+ " ^",
+ },
+ },
+ {
+ {
+ "class T {", //
+ " public static final byte b = (byte) \"hello\";",
+ "}",
+ },
+ {
+ "<>:2: error: value \"hello\" of type String cannot be converted to byte",
+ " public static final byte b = (byte) \"hello\";",
+ " ^",
+ }
+ },
+ {
+ {
+ "class T {", //
+ " public static final char c = (char) \"hello\";",
+ "}",
+ },
+ {
+ "<>:2: error: value \"hello\" of type String cannot be converted to char",
+ " public static final char c = (char) \"hello\";",
+ " ^",
+ }
+ },
+ {
+ {
+ "class T {", //
+ " public static final short s = (short) \"hello\";",
+ "}",
+ },
+ {
+ "<>:2: error: value \"hello\" of type String cannot be converted to short",
+ " public static final short s = (short) \"hello\";",
+ " ^",
+ }
+ },
+ {
+ {
+ "class T {", //
+ " public static final int i = (int) \"hello\";",
+ "}",
+ },
+ {
+ "<>:2: error: value \"hello\" of type String cannot be converted to int",
+ " public static final int i = (int) \"hello\";",
+ " ^",
+ }
+ },
+ {
+ {
+ "class T {", //
+ " public static final long l = (long) \"hello\";",
+ "}",
+ },
+ {
+ "<>:2: error: value \"hello\" of type String cannot be converted to long",
+ " public static final long l = (long) \"hello\";",
+ " ^",
+ }
+ },
+ {
+ {
+ "class T {", //
+ " public static final float f = (float) \"hello\";",
+ "}",
+ },
+ {
+ "<>:2: error: value \"hello\" of type String cannot be converted to float",
+ " public static final float f = (float) \"hello\";",
+ " ^",
+ }
+ },
+ {
+ {
+ "class T {", //
+ " public static final double d = (double) \"hello\";",
+ "}",
+ },
+ {
+ "<>:2: error: value \"hello\" of type String cannot be converted to double",
+ " public static final double d = (double) \"hello\";",
+ " ^",
+ },
+ },
+ {
+ {
+ "class T {", //
+ " public static final boolean X = \"1\" == 2;",
+ "}",
+ },
+ {
+ "<>:2: error: value 2 of type int cannot be converted to String",
+ " public static final boolean X = \"1\" == 2;",
+ " ^",
+ },
+ },
+ {
+ {
+ "class T {", //
+ " public static final boolean X = \"1\" != 2;",
+ "}",
+ },
+ {
+ "<>:2: error: value 2 of type int cannot be converted to String",
+ " public static final boolean X = \"1\" != 2;",
+ " ^",
+ },
+ },
};
return Arrays.asList((Object[][]) testCases);
}
diff --git a/javatests/com/google/turbine/binder/BinderTest.java b/javatests/com/google/turbine/binder/BinderTest.java
index 820fe22..40387ac 100644
--- a/javatests/com/google/turbine/binder/BinderTest.java
+++ b/javatests/com/google/turbine/binder/BinderTest.java
@@ -16,7 +16,6 @@
package com.google.turbine.binder;
-import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH;
import static java.util.Objects.requireNonNull;
@@ -26,7 +25,6 @@ import com.google.common.base.Joiner;
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.TypeBoundClass.FieldInfo;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.lower.IntegrationTestSupport;
@@ -284,34 +282,6 @@ public class BinderTest {
assertThat(a.annotationMetadata().target()).containsExactly(TurbineElementType.TYPE_USE);
}
- // Test that we don't crash on invalid constant field initializers.
- // (Error reporting is deferred to javac.)
- @Test
- public void invalidConst() throws Exception {
- 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(
- units,
- ClassPathBinder.bindClasspath(ImmutableList.of()),
- TURBINE_BOOTCLASSPATH,
- /* moduleVersion=*/ Optional.empty())
- .units();
-
- assertThat(bound.keySet()).containsExactly(new ClassSymbol("a/A"));
-
- SourceTypeBoundClass a = getBoundClass(bound, "a/A");
- FieldInfo f = getOnlyElement(a.fields());
- assertThat(f.name()).isEqualTo("b");
- assertThat(f.value()).isNull();
- }
-
private Tree.CompUnit parseLines(String... lines) {
return Parser.parse(Joiner.on('\n').join(lines));
}
diff --git a/javatests/com/google/turbine/binder/CtSymClassBinderTest.java b/javatests/com/google/turbine/binder/CtSymClassBinderTest.java
index 2da9f4c..d3a2c0e 100644
--- a/javatests/com/google/turbine/binder/CtSymClassBinderTest.java
+++ b/javatests/com/google/turbine/binder/CtSymClassBinderTest.java
@@ -29,19 +29,20 @@ import org.junit.runners.JUnit4;
public class CtSymClassBinderTest {
@Test
public void formatReleaseVersion() {
- ImmutableList.of("5", "6", "7", "8", "9")
- .forEach(x -> assertThat(CtSymClassBinder.formatReleaseVersion(x)).isEqualTo(x));
+ ImmutableList.of(5, 6, 7, 8, 9)
+ .forEach(
+ x -> assertThat(CtSymClassBinder.formatReleaseVersion(x)).isEqualTo(String.valueOf(x)));
ImmutableMap.of(
- "10", "A",
- "11", "B",
- "12", "C",
- "35", "Z")
+ 10, "A",
+ 11, "B",
+ 12, "C",
+ 35, "Z")
.forEach((k, v) -> assertThat(CtSymClassBinder.formatReleaseVersion(k)).isEqualTo(v));
- ImmutableList.of("4", "36")
+ ImmutableList.of(4, 36)
.forEach(
x ->
assertThrows(
- x,
+ Integer.toString(x),
IllegalArgumentException.class,
() -> CtSymClassBinder.formatReleaseVersion(x)));
}
diff --git a/javatests/com/google/turbine/binder/ProcessingTest.java b/javatests/com/google/turbine/binder/ProcessingTest.java
deleted file mode 100644
index b7091e8..0000000
--- a/javatests/com/google/turbine/binder/ProcessingTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2020 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.turbine.binder;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertThrows;
-
-import com.google.common.collect.ImmutableList;
-import javax.lang.model.SourceVersion;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class ProcessingTest {
- @Test
- public void parseSourceVersion() {
- assertThat(Processing.parseSourceVersion(ImmutableList.of()))
- .isEqualTo(SourceVersion.latestSupported());
- assertThat(Processing.parseSourceVersion(ImmutableList.of("-source", "8", "-target", "11")))
- .isEqualTo(SourceVersion.RELEASE_8);
- assertThat(Processing.parseSourceVersion(ImmutableList.of("-source", "8", "-source", "7")))
- .isEqualTo(SourceVersion.RELEASE_7);
- }
-
- @Test
- public void withPrefix() {
- assertThat(Processing.parseSourceVersion(ImmutableList.of("-source", "1.7")))
- .isEqualTo(SourceVersion.RELEASE_7);
- assertThat(Processing.parseSourceVersion(ImmutableList.of("-source", "1.8")))
- .isEqualTo(SourceVersion.RELEASE_8);
- }
-
- @Test
- public void invalidPrefix() {
- IllegalArgumentException expected =
- assertThrows(
- IllegalArgumentException.class,
- () -> Processing.parseSourceVersion(ImmutableList.of("-source", "1.11")));
- assertThat(expected).hasMessageThat().contains("invalid -source version: 1.11");
- }
-
- @Test
- public void latestSupported() {
- String latest = SourceVersion.latestSupported().toString();
- assertThat(latest).startsWith("RELEASE_");
- latest = latest.substring("RELEASE_".length());
- assertThat(Processing.parseSourceVersion(ImmutableList.of("-source", latest)))
- .isEqualTo(SourceVersion.latestSupported());
- }
-
- @Test
- public void missingArgument() {
- IllegalArgumentException expected =
- assertThrows(
- IllegalArgumentException.class,
- () -> Processing.parseSourceVersion(ImmutableList.of("-source")));
- assertThat(expected).hasMessageThat().contains("-source requires an argument");
- }
-
- @Test
- public void invalidSourceVersion() {
- IllegalArgumentException expected =
- assertThrows(
- IllegalArgumentException.class,
- () -> Processing.parseSourceVersion(ImmutableList.of("-source", "NOSUCH")));
- assertThat(expected).hasMessageThat().contains("invalid -source version: NOSUCH");
- }
-}
diff --git a/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java b/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
index ec2ebbf..65d973d 100644
--- a/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
+++ b/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
@@ -42,7 +42,7 @@ import java.io.UncheckedIOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.nullness.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
diff --git a/javatests/com/google/turbine/bytecode/ClassReaderTest.java b/javatests/com/google/turbine/bytecode/ClassReaderTest.java
index 9a9fdb1..ad5b90d 100644
--- a/javatests/com/google/turbine/bytecode/ClassReaderTest.java
+++ b/javatests/com/google/turbine/bytecode/ClassReaderTest.java
@@ -39,6 +39,7 @@ import org.objectweb.asm.Attribute;
import org.objectweb.asm.ByteVector;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Handle;
import org.objectweb.asm.ModuleVisitor;
import org.objectweb.asm.Opcodes;
@@ -236,6 +237,18 @@ public class ClassReaderTest {
}
@Test
+ public void condy() {
+ ClassWriter cw = new ClassWriter(0);
+ cw.visit(52, Opcodes.ACC_SUPER, "Test", null, "java/lang/Object", null);
+ cw.newConstantDynamic(
+ "f", "Ljava/lang/String;", new Handle(Opcodes.H_INVOKESTATIC, "A", "f", "()V", false));
+ byte[] bytes = cw.toByteArray();
+
+ ClassFile cf = ClassReader.read(null, bytes);
+ assertThat(cf.name()).isEqualTo("Test");
+ }
+
+ @Test
public void v53() {
ClassWriter cw = new ClassWriter(0);
cw.visitAnnotation("Ljava/lang/Deprecated;", true);
diff --git a/javatests/com/google/turbine/bytecode/ClassWriterTest.java b/javatests/com/google/turbine/bytecode/ClassWriterTest.java
index f488bbe..a6f9234 100644
--- a/javatests/com/google/turbine/bytecode/ClassWriterTest.java
+++ b/javatests/com/google/turbine/bytecode/ClassWriterTest.java
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.google.common.jimfs.Configuration;
@@ -46,6 +47,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.objectweb.asm.ModuleVisitor;
import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.RecordComponentVisitor;
@RunWith(JUnit4.class)
public class ClassWriterTest {
@@ -154,4 +156,106 @@ public class ClassWriterTest {
assertThat(AsmUtils.textify(inputBytes, /* skipDebug= */ true))
.isEqualTo(AsmUtils.textify(outputBytes, /* skipDebug= */ true));
}
+
+ @Test
+ public void record() {
+
+ org.objectweb.asm.ClassWriter cw = new org.objectweb.asm.ClassWriter(0);
+
+ cw.visit(
+ Opcodes.V16,
+ Opcodes.ACC_FINAL | Opcodes.ACC_SUPER | Opcodes.ACC_RECORD,
+ "R",
+ /* signature= */ null,
+ "java/lang/Record",
+ /* interfaces= */ null);
+
+ RecordComponentVisitor rv =
+ cw.visitRecordComponent("x", "Ljava/util/List;", "Ljava/util/List<Ljava/lang/Integer;>;");
+ rv.visitAnnotation("LA;", true);
+ rv.visitTypeAnnotation(318767104, null, "LA;", true);
+ cw.visitRecordComponent("y", "I", null);
+
+ byte[] expectedBytes = cw.toByteArray();
+
+ ClassFile classFile =
+ new ClassFile(
+ /* access= */ Opcodes.ACC_FINAL | Opcodes.ACC_SUPER | Opcodes.ACC_RECORD,
+ /* majorVersion= */ 60,
+ /* name= */ "R",
+ /* signature= */ null,
+ /* superClass= */ "java/lang/Record",
+ /* interfaces= */ ImmutableList.of(),
+ /* permits= */ ImmutableList.of(),
+ /* methods= */ ImmutableList.of(),
+ /* fields= */ ImmutableList.of(),
+ /* annotations= */ ImmutableList.of(),
+ /* innerClasses= */ ImmutableList.of(),
+ /* typeAnnotations= */ ImmutableList.of(),
+ /* module= */ null,
+ /* nestHost= */ null,
+ /* nestMembers= */ ImmutableList.of(),
+ /* record= */ new ClassFile.RecordInfo(
+ ImmutableList.of(
+ new ClassFile.RecordInfo.RecordComponentInfo(
+ "x",
+ "Ljava/util/List;",
+ "Ljava/util/List<Ljava/lang/Integer;>;",
+ ImmutableList.of(
+ new ClassFile.AnnotationInfo("LA;", true, ImmutableMap.of())),
+ ImmutableList.of(
+ new ClassFile.TypeAnnotationInfo(
+ ClassFile.TypeAnnotationInfo.TargetType.FIELD,
+ ClassFile.TypeAnnotationInfo.EMPTY_TARGET,
+ ClassFile.TypeAnnotationInfo.TypePath.root(),
+ new ClassFile.AnnotationInfo("LA;", true, ImmutableMap.of())))),
+ new ClassFile.RecordInfo.RecordComponentInfo(
+ "y", "I", null, ImmutableList.of(), ImmutableList.of()))),
+ /* transitiveJar= */ null);
+
+ byte[] actualBytes = ClassWriter.writeClass(classFile);
+
+ assertThat(AsmUtils.textify(actualBytes, /* skipDebug= */ true))
+ .isEqualTo(AsmUtils.textify(expectedBytes, /* skipDebug= */ true));
+ }
+
+ @Test
+ public void nestHost() {
+
+ org.objectweb.asm.ClassWriter cw = new org.objectweb.asm.ClassWriter(0);
+
+ cw.visit(Opcodes.V16, Opcodes.ACC_SUPER, "N", null, null, null);
+
+ cw.visitNestHost("H");
+ cw.visitNestMember("A");
+ cw.visitNestMember("B");
+ cw.visitNestMember("C");
+
+ byte[] expectedBytes = cw.toByteArray();
+
+ ClassFile classFile =
+ new ClassFile(
+ /* access= */ Opcodes.ACC_SUPER,
+ /* majorVersion= */ 60,
+ /* name= */ "N",
+ /* signature= */ null,
+ /* superClass= */ null,
+ /* interfaces= */ ImmutableList.of(),
+ /* permits= */ ImmutableList.of(),
+ /* methods= */ ImmutableList.of(),
+ /* fields= */ ImmutableList.of(),
+ /* annotations= */ ImmutableList.of(),
+ /* innerClasses= */ ImmutableList.of(),
+ /* typeAnnotations= */ ImmutableList.of(),
+ /* module= */ null,
+ /* nestHost= */ "H",
+ /* nestMembers= */ ImmutableList.of("A", "B", "C"),
+ /* record= */ null,
+ /* transitiveJar= */ null);
+
+ byte[] actualBytes = ClassWriter.writeClass(classFile);
+
+ assertThat(AsmUtils.textify(actualBytes, /* skipDebug= */ true))
+ .isEqualTo(AsmUtils.textify(expectedBytes, /* skipDebug= */ true));
+ }
}
diff --git a/javatests/com/google/turbine/deps/DependenciesTest.java b/javatests/com/google/turbine/deps/DependenciesTest.java
index bc663cd..ba905db 100644
--- a/javatests/com/google/turbine/deps/DependenciesTest.java
+++ b/javatests/com/google/turbine/deps/DependenciesTest.java
@@ -29,6 +29,7 @@ import com.google.turbine.diag.SourceFile;
import com.google.turbine.lower.IntegrationTestSupport;
import com.google.turbine.lower.Lower;
import com.google.turbine.lower.Lower.Lowered;
+import com.google.turbine.options.LanguageVersion;
import com.google.turbine.parse.Parser;
import com.google.turbine.proto.DepsProto;
import com.google.turbine.testing.TestClassPaths;
@@ -106,7 +107,12 @@ public class DependenciesTest {
TestClassPaths.TURBINE_BOOTCLASSPATH,
/* moduleVersion=*/ Optional.empty());
- Lowered lowered = Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv());
+ Lowered lowered =
+ Lower.lowerAll(
+ LanguageVersion.createDefault(),
+ bound.units(),
+ bound.modules(),
+ bound.classPathEnv());
return Dependencies.collectDeps(
Optional.of("//test"), TestClassPaths.TURBINE_BOOTCLASSPATH, bound, lowered);
diff --git a/javatests/com/google/turbine/lower/IntegrationTestSupport.java b/javatests/com/google/turbine/lower/IntegrationTestSupport.java
index 744f341..f20962b 100644
--- a/javatests/com/google/turbine/lower/IntegrationTestSupport.java
+++ b/javatests/com/google/turbine/lower/IntegrationTestSupport.java
@@ -24,6 +24,7 @@ import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
import static org.junit.Assert.fail;
import com.google.common.base.Joiner;
@@ -37,6 +38,7 @@ 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.options.LanguageVersion;
import com.google.turbine.parse.Parser;
import com.google.turbine.testing.AsmUtils;
import com.google.turbine.tree.Tree.CompUnit;
@@ -81,6 +83,7 @@ import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.RecordComponentNode;
import org.objectweb.asm.tree.TypeAnnotationNode;
/** Support for bytecode diffing-integration tests. */
@@ -242,12 +245,22 @@ public final class IntegrationTestSupport {
for (FieldNode f : n.fields) {
sortAnnotations(f.visibleAnnotations);
sortAnnotations(f.invisibleAnnotations);
-
- sortAnnotations(f.visibleAnnotations);
- sortAnnotations(f.invisibleAnnotations);
sortTypeAnnotations(f.visibleTypeAnnotations);
sortTypeAnnotations(f.invisibleTypeAnnotations);
}
+
+ if (n.recordComponents != null) {
+ for (RecordComponentNode r : n.recordComponents) {
+ sortAnnotations(r.visibleAnnotations);
+ sortAnnotations(r.invisibleAnnotations);
+ sortTypeAnnotations(r.visibleTypeAnnotations);
+ sortTypeAnnotations(r.invisibleTypeAnnotations);
+ }
+ }
+
+ if (n.nestMembers != null) {
+ Collections.sort(n.nestMembers);
+ }
}
private static void sortParameterAnnotations(List<AnnotationNode>[] parameters) {
@@ -323,6 +336,26 @@ public final class IntegrationTestSupport {
addTypesInTypeAnnotations(types, f.visibleTypeAnnotations);
addTypesInTypeAnnotations(types, f.invisibleTypeAnnotations);
}
+ if (n.recordComponents != null) {
+ for (RecordComponentNode r : n.recordComponents) {
+ collectTypesFromSignature(types, r.descriptor);
+ collectTypesFromSignature(types, r.signature);
+
+ addTypesInAnnotations(types, r.visibleAnnotations);
+ addTypesInAnnotations(types, r.invisibleAnnotations);
+ addTypesInTypeAnnotations(types, r.visibleTypeAnnotations);
+ addTypesInTypeAnnotations(types, r.invisibleTypeAnnotations);
+ }
+ }
+
+ if (n.nestMembers != null) {
+ for (String member : n.nestMembers) {
+ InnerClassNode i = infos.get(member);
+ if (i.outerName != null) {
+ types.add(member);
+ }
+ }
+ }
List<InnerClassNode> used = new ArrayList<>();
for (InnerClassNode i : n.innerClasses) {
@@ -336,6 +369,11 @@ public final class IntegrationTestSupport {
}
addInnerChain(infos, used, n.name);
n.innerClasses = used;
+
+ if (n.nestMembers != null) {
+ Set<String> members = used.stream().map(i -> i.name).collect(toSet());
+ n.nestMembers = n.nestMembers.stream().filter(members::contains).collect(toList());
+ }
}
private static void addTypesFromParameterAnnotations(
@@ -437,20 +475,32 @@ public final class IntegrationTestSupport {
});
}
- static Map<String, byte[]> runTurbine(Map<String, String> input, ImmutableList<Path> classpath)
+ public static Map<String, byte[]> runTurbine(
+ Map<String, String> input, ImmutableList<Path> classpath) throws IOException {
+ return runTurbine(input, classpath, ImmutableList.of());
+ }
+
+ public static Map<String, byte[]> runTurbine(
+ Map<String, String> input, ImmutableList<Path> classpath, ImmutableList<String> javacopts)
throws IOException {
return runTurbine(
- input, classpath, TURBINE_BOOTCLASSPATH, /* moduleVersion= */ Optional.empty());
+ input, classpath, TURBINE_BOOTCLASSPATH, /* moduleVersion= */ Optional.empty(), javacopts);
}
static Map<String, byte[]> runTurbine(
Map<String, String> input,
ImmutableList<Path> classpath,
ClassPath bootClassPath,
- Optional<String> moduleVersion)
+ Optional<String> moduleVersion,
+ ImmutableList<String> javacopts)
throws IOException {
BindingResult bound = turbineAnalysis(input, classpath, bootClassPath, moduleVersion);
- return Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes();
+ return Lower.lowerAll(
+ LanguageVersion.fromJavacopts(javacopts),
+ bound.units(),
+ bound.modules(),
+ bound.classPathEnv())
+ .bytes();
}
public static BindingResult turbineAnalysis(
@@ -640,5 +690,9 @@ public final class IntegrationTestSupport {
}
}
+ public static int getMajor() {
+ return Runtime.version().feature();
+ }
+
private IntegrationTestSupport() {}
}
diff --git a/javatests/com/google/turbine/lower/LongStringIntegrationTest.java b/javatests/com/google/turbine/lower/LongStringIntegrationTest.java
new file mode 100644
index 0000000..a462b69
--- /dev/null
+++ b/javatests/com/google/turbine/lower/LongStringIntegrationTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.turbine.lower;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+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.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(JUnit4.class)
+public class LongStringIntegrationTest {
+
+ @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Test
+ public void test() throws Exception {
+ Map<String, byte[]> output =
+ runTurbineWithStack(
+ /* stackSize= */ 1,
+ /* input= */ ImmutableMap.of("Test.java", source()),
+ /* classpath= */ ImmutableList.of());
+
+ assertThat(output.keySet()).containsExactly("Test");
+ String result = fieldValue(output.get("Test"));
+ assertThat(result).startsWith("...");
+ assertThat(result).hasLength(10000);
+ }
+
+ private static Map<String, byte[]> runTurbineWithStack(
+ int stackSize, ImmutableMap<String, String> input, ImmutableList<Path> classpath)
+ throws InterruptedException {
+ Map<String, byte[]> output = new HashMap<>();
+ Thread t =
+ new Thread(
+ /* group= */ null,
+ () -> {
+ try {
+ output.putAll(IntegrationTestSupport.runTurbine(input, classpath));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ },
+ /* name= */ "turbine",
+ stackSize);
+ t.run();
+ t.join();
+ return output;
+ }
+
+ /** Extract the string value of a constant field from the class file. */
+ private static String fieldValue(byte[] classFile) {
+ String[] result = {null};
+ new ClassReader(classFile)
+ .accept(
+ new ClassVisitor(Opcodes.ASM9) {
+ @Override
+ public FieldVisitor visitField(
+ int access, String name, String desc, String signature, Object value) {
+ result[0] = (String) value;
+ return null;
+ }
+ },
+ 0);
+ return result[0];
+ }
+
+ /** Create a source file with a long concatenated string literal: {@code "" + "." + "." + ...}. */
+ private static String source() {
+ StringBuilder input = new StringBuilder();
+ input.append("class Test { public static final String C = \"\"");
+ for (int i = 0; i < 10000; i++) {
+ input.append("+ \".\"\n");
+ }
+ input.append("; }");
+ return input.toString();
+ }
+}
diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
index ab4e0ee..97170ca 100644
--- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
@@ -19,8 +19,10 @@ package com.google.turbine.lower;
import static com.google.common.truth.Truth.assertThat;
import static com.google.turbine.testing.TestResources.getResource;
import static java.util.stream.Collectors.toList;
+import static org.junit.Assume.assumeTrue;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import java.io.IOError;
import java.io.IOException;
@@ -41,6 +43,9 @@ import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class LowerIntegrationTest {
+ private static final ImmutableMap<String, Integer> SOURCE_VERSION =
+ ImmutableMap.of("record.test", 16, "record2.test", 16, "sealed.test", 17);
+
@Parameters(name = "{index}: {0}")
public static Iterable<Object[]> parameters() {
String[] testCases = {
@@ -55,6 +60,7 @@ public class LowerIntegrationTest {
"B8148131.test",
"abstractenum.test",
"access1.test",
+ "ambiguous_identifier.test",
"anno_const_coerce.test",
"anno_const_scope.test",
"anno_nested.test",
@@ -125,6 +131,7 @@ public class LowerIntegrationTest {
"const_multi.test",
"const_nonfinal.test",
"const_octal_underscore.test",
+ "const_operation_order.test",
"const_types.test",
"const_underscore.test",
"constlevel.test",
@@ -138,6 +145,7 @@ public class LowerIntegrationTest {
"default_simple.test",
"deficient_types_classfile.test",
"dollar.test",
+ "empty_package_info.test",
"enum1.test",
"enum_abstract.test",
"enum_final.test",
@@ -254,8 +262,11 @@ public class LowerIntegrationTest {
"rawcanon.test",
"rawfbound.test",
"receiver_param.test",
+ "record.test",
+ "record2.test",
"rek.test",
"samepkg.test",
+ "sealed.test",
"self.test",
"semi.test",
// https://bugs.openjdk.java.net/browse/JDK-8054064 ?
@@ -270,6 +281,7 @@ public class LowerIntegrationTest {
"static_type_import.test",
"strictfp.test",
"string.test",
+ "string_const.test",
"superabstract.test",
"supplierfunction.test",
"tbound.test",
@@ -363,9 +375,16 @@ public class LowerIntegrationTest {
classpathJar = ImmutableList.of(lib);
}
- Map<String, byte[]> expected = IntegrationTestSupport.runJavac(input.sources, classpathJar);
+ int version = SOURCE_VERSION.getOrDefault(test, 8);
+ assumeTrue(version <= Runtime.version().feature());
+ ImmutableList<String> javacopts =
+ ImmutableList.of("-source", String.valueOf(version), "-target", String.valueOf(version));
+
+ Map<String, byte[]> expected =
+ IntegrationTestSupport.runJavac(input.sources, classpathJar, javacopts);
- Map<String, byte[]> actual = IntegrationTestSupport.runTurbine(input.sources, classpathJar);
+ Map<String, byte[]> actual =
+ IntegrationTestSupport.runTurbine(input.sources, classpathJar, javacopts);
assertThat(IntegrationTestSupport.dump(IntegrationTestSupport.sortMembers(actual)))
.isEqualTo(IntegrationTestSupport.dump(IntegrationTestSupport.canonicalize(expected)));
diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java
index d74e829..6d3a6df 100644
--- a/javatests/com/google/turbine/lower/LowerTest.java
+++ b/javatests/com/google/turbine/lower/LowerTest.java
@@ -41,6 +41,7 @@ import com.google.turbine.diag.TurbineError;
import com.google.turbine.model.TurbineConstantTypeKind;
import com.google.turbine.model.TurbineFlag;
import com.google.turbine.model.TurbineTyKind;
+import com.google.turbine.options.LanguageVersion;
import com.google.turbine.parse.Parser;
import com.google.turbine.testing.AsmUtils;
import com.google.turbine.type.Type;
@@ -184,9 +185,11 @@ public class LowerTest {
SourceTypeBoundClass c =
new SourceTypeBoundClass(
interfaceTypes,
+ ImmutableList.of(),
xtnds,
tps,
access,
+ ImmutableList.of(),
methods,
fields,
owner,
@@ -204,11 +207,13 @@ public class LowerTest {
SourceTypeBoundClass i =
new SourceTypeBoundClass(
ImmutableList.of(),
+ ImmutableList.of(),
Type.ClassTy.OBJECT,
ImmutableMap.of(),
TurbineFlag.ACC_STATIC | TurbineFlag.ACC_PROTECTED,
ImmutableList.of(),
ImmutableList.of(),
+ ImmutableList.of(),
new ClassSymbol("test/Test"),
TurbineTyKind.CLASS,
ImmutableMap.of("Inner", new ClassSymbol("test/Test$Inner")),
@@ -227,6 +232,7 @@ public class LowerTest {
Map<String, byte[]> bytes =
Lower.lowerAll(
+ LanguageVersion.createDefault(),
ImmutableMap.of(
new ClassSymbol("test/Test"), c, new ClassSymbol("test/Test$Inner"), i),
ImmutableList.of(),
@@ -256,7 +262,12 @@ public class LowerTest {
TURBINE_BOOTCLASSPATH,
/* moduleVersion=*/ Optional.empty());
Map<String, byte[]> lowered =
- Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes();
+ Lower.lowerAll(
+ LanguageVersion.createDefault(),
+ bound.units(),
+ bound.modules(),
+ bound.classPathEnv())
+ .bytes();
List<String> attributes = new ArrayList<>();
new ClassReader(lowered.get("Test$Inner$InnerMost"))
.accept(
@@ -331,7 +342,12 @@ public class LowerTest {
TURBINE_BOOTCLASSPATH,
/* moduleVersion=*/ Optional.empty());
Map<String, byte[]> lowered =
- Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes();
+ Lower.lowerAll(
+ LanguageVersion.createDefault(),
+ bound.units(),
+ bound.modules(),
+ bound.classPathEnv())
+ .bytes();
TypePath[] path = new TypePath[1];
new ClassReader(lowered.get("Test"))
.accept(
@@ -409,7 +425,12 @@ public class LowerTest {
TURBINE_BOOTCLASSPATH,
/* moduleVersion=*/ Optional.empty());
Map<String, byte[]> lowered =
- Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes();
+ Lower.lowerAll(
+ LanguageVersion.createDefault(),
+ bound.units(),
+ bound.modules(),
+ bound.classPathEnv())
+ .bytes();
int[] acc = {0};
new ClassReader(lowered.get("Test"))
.accept(
@@ -477,7 +498,7 @@ public class LowerTest {
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
sources.forEach(
(k, v) -> builder.put(k, v.replaceAll("import static b\\.B\\.nosuch\\..*;", "")));
- noImports = builder.build();
+ noImports = builder.buildOrThrow();
}
Map<String, byte[]> expected = IntegrationTestSupport.runJavac(noImports, ImmutableList.of());
@@ -621,6 +642,40 @@ public class LowerTest {
assertThat((testAccess[0] & TurbineFlag.ACC_PROTECTED)).isNotEqualTo(TurbineFlag.ACC_PROTECTED);
}
+ @Test
+ public void minClassVersion() throws Exception {
+ BindingResult bound =
+ Binder.bind(
+ ImmutableList.of(Parser.parse("class Test {}")),
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ TURBINE_BOOTCLASSPATH,
+ /* moduleVersion=*/ Optional.empty());
+ Map<String, byte[]> lowered =
+ Lower.lowerAll(
+ LanguageVersion.fromJavacopts(ImmutableList.of("-source", "7", "-target", "7")),
+ bound.units(),
+ bound.modules(),
+ bound.classPathEnv())
+ .bytes();
+ int[] major = {0};
+ new ClassReader(lowered.get("Test"))
+ .accept(
+ new ClassVisitor(Opcodes.ASM9) {
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ major[0] = version;
+ }
+ },
+ 0);
+ assertThat(major[0]).isEqualTo(Opcodes.V1_8);
+ }
+
static String lines(String... lines) {
return Joiner.on(System.lineSeparator()).join(lines);
}
diff --git a/javatests/com/google/turbine/lower/MissingJavaBaseModule.java b/javatests/com/google/turbine/lower/MissingJavaBaseModule.java
new file mode 100644
index 0000000..230b18f
--- /dev/null
+++ b/javatests/com/google/turbine/lower/MissingJavaBaseModule.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.turbine.lower;
+
+import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_VERSION;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.ClassPath;
+import com.google.turbine.binder.CtSymClassBinder;
+import com.google.turbine.binder.JimageClassBinder;
+import com.google.turbine.binder.bound.ModuleInfo;
+import com.google.turbine.binder.bytecode.BytecodeBoundClass;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.lookup.TopLevelIndex;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.ModuleSymbol;
+import java.util.Map;
+import java.util.Optional;
+import org.jspecify.nullness.Nullable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MissingJavaBaseModule {
+
+ @Test
+ public void test() throws Exception {
+
+ Map<String, String> sources = ImmutableMap.of("module-info.java", "module foo {}");
+
+ Map<String, byte[]> expected =
+ IntegrationTestSupport.runJavac(
+ sources, ImmutableList.of(), ImmutableList.of("--release", "9", "--module-version=42"));
+
+ ClassPath base =
+ Double.parseDouble(JAVA_CLASS_VERSION.value()) < 54
+ ? JimageClassBinder.bindDefault()
+ : CtSymClassBinder.bind(9);
+ ClassPath bootclasspath =
+ new ClassPath() {
+ @Override
+ public Env<ClassSymbol, BytecodeBoundClass> env() {
+ return base.env();
+ }
+
+ @Override
+ public Env<ModuleSymbol, ModuleInfo> moduleEnv() {
+ return new Env<ModuleSymbol, ModuleInfo>() {
+ @Override
+ public @Nullable ModuleInfo get(ModuleSymbol sym) {
+ if (sym.name().equals("java.base")) {
+ return null;
+ }
+ return base.moduleEnv().get(sym);
+ }
+ };
+ }
+
+ @Override
+ public TopLevelIndex index() {
+ return base.index();
+ }
+
+ @Override
+ public @Nullable Supplier<byte[]> resource(String path) {
+ return base.resource(path);
+ }
+ };
+ Map<String, byte[]> actual =
+ IntegrationTestSupport.runTurbine(
+ sources,
+ ImmutableList.of(),
+ bootclasspath,
+ Optional.of("42"),
+ /* javacopts= */ ImmutableList.of());
+
+ assertEquals(dump(expected), dump(actual));
+ }
+
+ private String dump(Map<String, byte[]> map) throws Exception {
+ return IntegrationTestSupport.dump(
+ map.entrySet().stream()
+ .filter(e -> e.getKey().endsWith("module-info"))
+ .collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)));
+ }
+}
diff --git a/javatests/com/google/turbine/lower/ModuleIntegrationTest.java b/javatests/com/google/turbine/lower/ModuleIntegrationTest.java
index f2c0bbf..0157fea 100644
--- a/javatests/com/google/turbine/lower/ModuleIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/ModuleIntegrationTest.java
@@ -96,8 +96,9 @@ public class ModuleIntegrationTest {
classpathJar,
Double.parseDouble(JAVA_CLASS_VERSION.value()) < 54
? JimageClassBinder.bindDefault()
- : CtSymClassBinder.bind("9"),
- Optional.of("42"));
+ : CtSymClassBinder.bind(9),
+ Optional.of("42"),
+ /* javacopts= */ ImmutableList.of());
assertEquals(dump(expected), dump(actual));
}
diff --git a/javatests/com/google/turbine/lower/testdata/ambiguous_identifier.test b/javatests/com/google/turbine/lower/testdata/ambiguous_identifier.test
new file mode 100644
index 0000000..d7bbc54
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/ambiguous_identifier.test
@@ -0,0 +1,14 @@
+=== Test.java ===
+class Test {
+ static final int non = 42;
+ static final int sealed = 1;
+ // here 'non-sealed' is a binary expression subtracting two identifiers,
+ // not a contextual hyphenated keyword
+ static final int x = non-sealed;
+}
+
+// handle backtracking when we see 'non', but it isn't part of a contextualy
+// hyphenated keyword 'non-sealed'
+class non {
+ non self;
+}
diff --git a/javatests/com/google/turbine/lower/testdata/array_class_literal.test b/javatests/com/google/turbine/lower/testdata/array_class_literal.test
index 9033b04..4287cdf 100644
--- a/javatests/com/google/turbine/lower/testdata/array_class_literal.test
+++ b/javatests/com/google/turbine/lower/testdata/array_class_literal.test
@@ -1,4 +1,6 @@
=== Test.java ===
+import java.util.Map;
+
@interface Anno {
Class<?> value() default Object.class;
}
@@ -8,4 +10,5 @@ class Test {
@Anno(byte[][].class) int b;
@Anno(int[][].class) int c;
@Anno(Object[].class) int d;
+ @Anno(Map.Entry[].class) int e;
}
diff --git a/javatests/com/google/turbine/lower/testdata/const_operation_order.test b/javatests/com/google/turbine/lower/testdata/const_operation_order.test
new file mode 100644
index 0000000..a088823
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/const_operation_order.test
@@ -0,0 +1,13 @@
+=== test/A.java ===
+package test;
+
+public class A {
+ public static final double D1 = 1.0 / (2 / 3);
+ public static final double D2 = 1.0 / 2 / 3;
+ public static final double M1 = 1.0 + (2 + 3);
+ public static final double M2 = 1.0 + 2 + 3;
+ public static final double A1 = 1.0 + (2 + 3);
+ public static final double A2 = 1.0 + 2 + 3;
+ public static final double S1 = 1.0 - (2 - 3);
+ public static final double S2 = 1.0 - 2 - 3;
+}
diff --git a/javatests/com/google/turbine/lower/testdata/empty_package_info.test b/javatests/com/google/turbine/lower/testdata/empty_package_info.test
new file mode 100644
index 0000000..be28f14
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/empty_package_info.test
@@ -0,0 +1,3 @@
+=== package-info.java ===
+
+package test;
diff --git a/javatests/com/google/turbine/lower/testdata/record.test b/javatests/com/google/turbine/lower/testdata/record.test
new file mode 100644
index 0000000..7d92c2b
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/record.test
@@ -0,0 +1,46 @@
+=== Records.java ===
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import java.util.List;
+
+class Records {
+ record R1() {}
+
+ private record R2() {}
+
+ @Deprecated
+ private record R3() {}
+
+ record R4<T>() {}
+
+ record R5<T>(int x) {}
+
+ record R6<T>(@Deprecated int x) {}
+
+ record R7<T>(@Deprecated int x, int... y) {}
+
+ record R8<T>() implements Comparable<R8<T>> {
+ @Override
+ public int compareTo(R8<T> other) {
+ return 0;
+ }
+ }
+
+ record R9(int x) {
+ R9(int x) {
+ this.x = x;
+ }
+ }
+
+ @Target(ElementType.TYPE_USE)
+ @interface A {}
+
+ @Target(ElementType.RECORD_COMPONENT)
+ @interface B {}
+
+ @Target({ElementType.TYPE_USE, ElementType.RECORD_COMPONENT})
+ @interface C {}
+
+ record R10<T>(@A List<@A T> x, @B int y, @C int z) {
+ }
+}
diff --git a/javatests/com/google/turbine/lower/testdata/record2.test b/javatests/com/google/turbine/lower/testdata/record2.test
new file mode 100644
index 0000000..af4093e
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/record2.test
@@ -0,0 +1,27 @@
+=== Records.java ===
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import java.util.Objects;
+
+class Records {
+ public record R1(String one) {
+ public R1 {
+ Objects.requireNonNull(one);
+ }
+ }
+
+ public record R2(String one) {
+ @Deprecated
+ public R2 {
+ Objects.requireNonNull(one);
+ }
+ }
+
+ public record R3<T>(T x) {
+ @Deprecated
+ public R3 {
+ Objects.requireNonNull(x);
+ }
+ }
+}
diff --git a/javatests/com/google/turbine/lower/testdata/sealed.test b/javatests/com/google/turbine/lower/testdata/sealed.test
new file mode 100644
index 0000000..0bac7b1
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/sealed.test
@@ -0,0 +1,11 @@
+=== Sealed.java ===
+
+sealed class Sealed permits Sealed.Foo, Sealed.Bar {
+ static final class Foo extends Sealed {}
+ static final class Bar extends Sealed {}
+}
+
+sealed interface ISealed permits ISealed.Foo, ISealed.Bar {
+ static final class Foo implements ISealed {}
+ static non-sealed class Bar implements ISealed {}
+}
diff --git a/javatests/com/google/turbine/lower/testdata/string_const.test b/javatests/com/google/turbine/lower/testdata/string_const.test
new file mode 100644
index 0000000..cd39c37
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/string_const.test
@@ -0,0 +1,7 @@
+=== T.java ===
+
+class T {
+ public static final String A = "" + 42;
+ public static final boolean B = "1" == "2";
+ public static final boolean C = "1" != "2";
+}
diff --git a/javatests/com/google/turbine/main/MainTest.java b/javatests/com/google/turbine/main/MainTest.java
index 57940f3..3504891 100644
--- a/javatests/com/google/turbine/main/MainTest.java
+++ b/javatests/com/google/turbine/main/MainTest.java
@@ -32,6 +32,7 @@ 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.LanguageVersion;
import com.google.turbine.options.TurbineOptions;
import com.google.turbine.proto.ManifestProto;
import java.io.BufferedInputStream;
@@ -175,7 +176,7 @@ public class MainTest {
Main.compile(
TurbineOptions.builder()
- .setRelease("9")
+ .setLanguageVersion(LanguageVersion.fromJavacopts(ImmutableList.of("--release", "9")))
.setSources(ImmutableList.of(src.toString()))
.setSourceJars(ImmutableList.of(srcjar.toString()))
.setOutput(output.toString())
diff --git a/javatests/com/google/turbine/options/LanguageVersionTest.java b/javatests/com/google/turbine/options/LanguageVersionTest.java
new file mode 100644
index 0000000..601652c
--- /dev/null
+++ b/javatests/com/google/turbine/options/LanguageVersionTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2020 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.turbine.options;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import com.google.common.collect.ImmutableList;
+import javax.lang.model.SourceVersion;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class LanguageVersionTest {
+ @Test
+ public void parseSourceVersion() {
+ assertThat(LanguageVersion.fromJavacopts(ImmutableList.of()).sourceVersion())
+ .isEqualTo(SourceVersion.RELEASE_8);
+ assertThat(
+ LanguageVersion.fromJavacopts(ImmutableList.of("-source", "8", "-target", "11"))
+ .sourceVersion())
+ .isEqualTo(SourceVersion.RELEASE_8);
+ assertThat(
+ LanguageVersion.fromJavacopts(ImmutableList.of("-source", "8", "-source", "7"))
+ .sourceVersion())
+ .isEqualTo(SourceVersion.RELEASE_7);
+ }
+
+ @Test
+ public void withPrefix() {
+ assertThat(LanguageVersion.fromJavacopts(ImmutableList.of("-source", "1.7")).sourceVersion())
+ .isEqualTo(SourceVersion.RELEASE_7);
+ assertThat(LanguageVersion.fromJavacopts(ImmutableList.of("-source", "1.8")).sourceVersion())
+ .isEqualTo(SourceVersion.RELEASE_8);
+ }
+
+ @Test
+ public void invalidPrefix() {
+ assertThat(LanguageVersion.fromJavacopts(ImmutableList.of("-source", "1.10")).source())
+ .isEqualTo(10);
+ IllegalArgumentException expected =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> LanguageVersion.fromJavacopts(ImmutableList.of("-source", "1.11")));
+ assertThat(expected).hasMessageThat().contains("invalid -source version: 1.11");
+ }
+
+ @Test
+ public void latestSupported() {
+ String latest = SourceVersion.latestSupported().toString();
+ assertThat(latest).startsWith("RELEASE_");
+ latest = latest.substring("RELEASE_".length());
+ assertThat(LanguageVersion.fromJavacopts(ImmutableList.of("-source", latest)).sourceVersion())
+ .isEqualTo(SourceVersion.latestSupported());
+ }
+
+ @Test
+ public void missingArgument() {
+ for (String flag :
+ ImmutableList.of("-source", "--source", "-target", "--target", "--release")) {
+ IllegalArgumentException expected =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> LanguageVersion.fromJavacopts(ImmutableList.of(flag)));
+ assertThat(expected).hasMessageThat().contains(flag + " requires an argument");
+ }
+ }
+
+ @Test
+ public void invalidSourceVersion() {
+ IllegalArgumentException expected =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> LanguageVersion.fromJavacopts(ImmutableList.of("-source", "NOSUCH")));
+ assertThat(expected).hasMessageThat().contains("invalid -source version: NOSUCH");
+ }
+
+ @Test
+ public void invalidRelease() {
+ IllegalArgumentException expected =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> LanguageVersion.fromJavacopts(ImmutableList.of("--release", "NOSUCH")));
+ assertThat(expected).hasMessageThat().contains("invalid --release version: NOSUCH");
+ }
+
+ @Test
+ public void parseRelease() {
+ assertThat(LanguageVersion.fromJavacopts(ImmutableList.of("--release", "16")).release())
+ .hasValue(16);
+ assertThat(
+ LanguageVersion.fromJavacopts(ImmutableList.of("-source", "8", "-target", "8"))
+ .release())
+ .isEmpty();
+ }
+
+ @Test
+ public void parseTarget() {
+ assertThat(
+ LanguageVersion.fromJavacopts(
+ ImmutableList.of("--release", "12", "-source", "8", "-target", "11"))
+ .target())
+ .isEqualTo(11);
+ assertThat(
+ LanguageVersion.fromJavacopts(
+ ImmutableList.of("-source", "8", "-target", "11", "--release", "12"))
+ .target())
+ .isEqualTo(12);
+ }
+
+ @Test
+ public void releaseUnderride() {
+ assertThat(
+ LanguageVersion.fromJavacopts(ImmutableList.of("--release", "12", "-source", "8"))
+ .release())
+ .isEmpty();
+ assertThat(
+ LanguageVersion.fromJavacopts(ImmutableList.of("--release", "12", "-target", "8"))
+ .release())
+ .isEmpty();
+ }
+
+ @Test
+ public void unsupportedSourceVersion() {
+ LanguageVersion languageVersion =
+ LanguageVersion.fromJavacopts(ImmutableList.of("-source", "9999"));
+ IllegalArgumentException expected =
+ assertThrows(IllegalArgumentException.class, languageVersion::sourceVersion);
+ assertThat(expected).hasMessageThat().contains("invalid -source version:");
+ }
+}
diff --git a/javatests/com/google/turbine/options/TurbineOptionsTest.java b/javatests/com/google/turbine/options/TurbineOptionsTest.java
index 5d892c5..95eea59 100644
--- a/javatests/com/google/turbine/options/TurbineOptionsTest.java
+++ b/javatests/com/google/turbine/options/TurbineOptionsTest.java
@@ -304,28 +304,6 @@ public class TurbineOptionsTest {
}
@Test
- public void releaseJavacopts() throws Exception {
- TurbineOptions options =
- TurbineOptionsParser.parse(
- Iterables.concat(
- BASE_ARGS,
- Arrays.asList(
- "--release",
- "9",
- "--javacopts",
- "--release",
- "8",
- "--release",
- "7",
- "--release",
- "--")));
- assertThat(options.release()).hasValue("7");
- assertThat(options.javacOpts())
- .containsExactly("--release", "8", "--release", "7", "--release")
- .inOrder();
- }
-
- @Test
public void miscOutputs() throws Exception {
TurbineOptions options =
TurbineOptionsParser.parse(
diff --git a/javatests/com/google/turbine/parse/ExpressionParserTest.java b/javatests/com/google/turbine/parse/ExpressionParserTest.java
index 9fa96e2..7b5889b 100644
--- a/javatests/com/google/turbine/parse/ExpressionParserTest.java
+++ b/javatests/com/google/turbine/parse/ExpressionParserTest.java
@@ -39,7 +39,7 @@ public class ExpressionParserTest {
"14 + 42", "(14 + 42)",
},
{
- "14 + 42 + 123", "((14 + 42) + 123)",
+ "14 + 42 + 123", "(14 + 42 + 123)",
},
{
"14 / 42 + 123", "((14 / 42) + 123)",
@@ -49,7 +49,7 @@ public class ExpressionParserTest {
},
{
"1 + 2 / 3 + 4 / 5 + 6 / 7 / 8 + 9 + 10",
- "(((((1 + (2 / 3)) + (4 / 5)) + ((6 / 7) / 8)) + 9) + 10)",
+ "(1 + (2 / 3) + (4 / 5) + (6 / 7 / 8) + 9 + 10)",
},
{
"1 >> 2 || 3 ^ 4 << 3", "((1 >> 2) || (3 ^ (4 << 3)))",
@@ -64,7 +64,7 @@ public class ExpressionParserTest {
"((Object) 1 + 2)", "((Object) 1 + 2)",
},
{
- "(1) + 1 + 2", "((1 + 1) + 2)",
+ "(1) + 1 + 2", "(1 + 1 + 2)",
},
{
"((1 + 2) / (1 + 2))", "((1 + 2) / (1 + 2))",
@@ -82,7 +82,7 @@ public class ExpressionParserTest {
"(int) +2", "(int) +2",
},
{
- "(1 + 2) +2", "((1 + 2) + 2)",
+ "(1 + 2) + 2", "((1 + 2) + 2)",
},
{
"((1 + (2 / 3)) + (4 / 5))", "((1 + (2 / 3)) + (4 / 5))",
@@ -121,7 +121,7 @@ public class ExpressionParserTest {
"x.y = z", null,
},
{
- "0b100L + 0100L + 0x100L", "((4L + 64L) + 256L)",
+ "0b100L + 0100L + 0x100L", "(4L + 64L + 256L)",
},
{
"1+-2", "(1 + -2)",
@@ -132,6 +132,9 @@ public class ExpressionParserTest {
{
"A ? B : C ? D : E;", "(A ? B : (C ? D : E))",
},
+ {
+ "Foo.class", "Foo.class",
+ },
});
}
@@ -146,7 +149,8 @@ public class ExpressionParserTest {
@Test
public void test() {
StreamLexer lexer = new StreamLexer(new UnicodeEscapePreprocessor(new SourceFile(null, input)));
- Tree.Expression expression = new ConstExpressionParser(lexer, lexer.next()).expression();
+ Tree.Expression expression =
+ new ConstExpressionParser(lexer, lexer.next(), lexer.position()).expression();
if (expected == null) {
assertThat(expression).isNull();
} else {
diff --git a/javatests/com/google/turbine/parse/ParseErrorTest.java b/javatests/com/google/turbine/parse/ParseErrorTest.java
index eeb3923..2c48b81 100644
--- a/javatests/com/google/turbine/parse/ParseErrorTest.java
+++ b/javatests/com/google/turbine/parse/ParseErrorTest.java
@@ -35,7 +35,7 @@ public class ParseErrorTest {
StreamLexer lexer =
new StreamLexer(
new UnicodeEscapePreprocessor(new SourceFile("<>", String.valueOf("2147483648"))));
- ConstExpressionParser parser = new ConstExpressionParser(lexer, lexer.next());
+ ConstExpressionParser parser = new ConstExpressionParser(lexer, lexer.next(), lexer.position());
TurbineError e = assertThrows(TurbineError.class, () -> parser.expression());
assertThat(e).hasMessageThat().contains("invalid literal");
}
@@ -45,7 +45,7 @@ public class ParseErrorTest {
StreamLexer lexer =
new StreamLexer(
new UnicodeEscapePreprocessor(new SourceFile("<>", String.valueOf("0x100000000"))));
- ConstExpressionParser parser = new ConstExpressionParser(lexer, lexer.next());
+ ConstExpressionParser parser = new ConstExpressionParser(lexer, lexer.next(), lexer.position());
TurbineError e = assertThrows(TurbineError.class, () -> parser.expression());
assertThat(e).hasMessageThat().contains("invalid literal");
}
@@ -294,6 +294,19 @@ public class ParseErrorTest {
" ^"));
}
+ @Test
+ public void notCast() {
+ String input = "@j(@truetugt^(oflur)!%t";
+ TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: could not evaluate constant expression",
+ "@j(@truetugt^(oflur)!%t",
+ " ^"));
+ }
+
private static String lines(String... lines) {
return Joiner.on(System.lineSeparator()).join(lines);
}
diff --git a/javatests/com/google/turbine/parse/ParserIntegrationTest.java b/javatests/com/google/turbine/parse/ParserIntegrationTest.java
index 2503553..c758a74 100644
--- a/javatests/com/google/turbine/parse/ParserIntegrationTest.java
+++ b/javatests/com/google/turbine/parse/ParserIntegrationTest.java
@@ -42,6 +42,7 @@ public class ParserIntegrationTest {
public static Iterable<Object[]> parameters() {
String[] tests = {
"anno1.input",
+ "anno2.input",
"annodecl1.input",
"annodecl2.input",
"annodecl3.input",
@@ -75,6 +76,8 @@ public class ParserIntegrationTest {
"weirdstring.input",
"type_annotations.input",
"module-info.input",
+ "record.input",
+ "sealed.input",
};
return Iterables.transform(
Arrays.asList(tests),
diff --git a/javatests/com/google/turbine/parse/testdata/anno2.input b/javatests/com/google/turbine/parse/testdata/anno2.input
new file mode 100644
index 0000000..60b1901
--- /dev/null
+++ b/javatests/com/google/turbine/parse/testdata/anno2.input
@@ -0,0 +1,3 @@
+@Foo(bar = FooBar.Bar[].class)
+class Test {
+} \ No newline at end of file
diff --git a/javatests/com/google/turbine/parse/testdata/record.input b/javatests/com/google/turbine/parse/testdata/record.input
new file mode 100644
index 0000000..575d741
--- /dev/null
+++ b/javatests/com/google/turbine/parse/testdata/record.input
@@ -0,0 +1,53 @@
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import java.util.List;
+
+private record R() {
+}
+
+class Records {
+ record R1() {
+ }
+
+ private record R2() {
+ }
+
+ @Deprecated
+ private record R3() {
+ }
+
+ record R4<T>() {
+ }
+
+ record R5<T>(int x) {
+ }
+
+ record R6<T>(@Deprecated int x) {
+ }
+
+ record R7<T>(@Deprecated int x, int[] y) {
+ }
+
+ record R8<T>() implements Comparable<R8<T>> {
+ @Override
+ public int compareTo(R8<T> other) {}
+ }
+
+ record R9(int x) {
+ }
+
+ @Target(ElementType.TYPE_USE)
+ @interface A {
+ }
+
+ @Target(ElementType.RECORD_COMPONENT)
+ @interface B {
+ }
+
+ @Target({ElementType.TYPE_USE, ElementType.RECORD_COMPONENT})
+ @interface C {
+ }
+
+ record R10<T>(@A List<@A T> x, @B int y, @C int z) {
+ }
+}
diff --git a/javatests/com/google/turbine/parse/testdata/sealed.input b/javatests/com/google/turbine/parse/testdata/sealed.input
new file mode 100644
index 0000000..5a277b8
--- /dev/null
+++ b/javatests/com/google/turbine/parse/testdata/sealed.input
@@ -0,0 +1,15 @@
+sealed class Sealed permits Sealed.Foo, Sealed.Bar {
+ static final class Foo extends Sealed {
+ }
+
+ static final class Bar extends Sealed {
+ }
+}
+
+sealed interface ISealed permits ISealed.Foo, ISealed.Bar {
+ static final class Foo implements ISealed {
+ }
+
+ static non-sealed class Bar implements ISealed {
+ }
+}
diff --git a/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java b/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java
index d3b3836..7d8d479 100644
--- a/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java
+++ b/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java
@@ -345,6 +345,21 @@ class AbstractTurbineTypesTest {
" void h(T t) {}",
"}"));
+ // type variable bounds
+ files.add(
+ Joiner.on('\n')
+ .join(
+ "import java.util.List;",
+ "class N<X, T extends X> {",
+ " void h(T t) {}",
+ "}",
+ "class O<X extends Enum<X>, T extends X> {",
+ " void h(T t) {}",
+ "}",
+ "class P<X extends List<?>, T extends X> {",
+ " void h(T t) {}",
+ "}"));
+
Context context = new Context();
JavaFileManager fileManager = new JavacFileManager(context, true, UTF_8);
idx.set(0);
diff --git a/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java b/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java
index 96664d2..fee2c75 100644
--- a/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java
+++ b/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java
@@ -23,7 +23,10 @@ import static com.google.common.truth.Truth8.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+import static javax.lang.model.util.ElementFilter.typesIn;
import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
@@ -53,7 +56,10 @@ 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.Element;
+import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ExecutableType;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
@@ -614,6 +620,93 @@ public class ProcessingIntegrationTest {
.containsExactly("@Deprecated({})");
}
+ @SupportedAnnotationTypes("*")
+ public static class RecordProcessor 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,
+ e.getKind() + " " + e + " " + ((TypeElement) e).getSuperclass());
+ for (Element m : e.getEnclosedElements()) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, m.getKind() + " " + m);
+ }
+ }
+ return false;
+ }
+ }
+
+ @Test
+ public void recordProcessing() throws IOException {
+ assumeTrue(Runtime.version().feature() >= 15);
+ ImmutableList<Tree.CompUnit> units =
+ parseUnit(
+ "=== R.java ===", //
+ "record R<T>(@Deprecated T x, int... y) {}");
+ TurbineError e =
+ assertThrows(
+ TurbineError.class,
+ () ->
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ ProcessorInfo.create(
+ ImmutableList.of(new RecordProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty()));
+ assertThat(
+ e.diagnostics().stream()
+ .filter(d -> d.severity().equals(Diagnostic.Kind.ERROR))
+ .map(d -> d.message()))
+ .containsExactly(
+ "RECORD R java.lang.Record",
+ "RECORD_COMPONENT x",
+ "RECORD_COMPONENT y",
+ "CONSTRUCTOR R(T,int[])",
+ "METHOD toString()",
+ "METHOD hashCode()",
+ "METHOD equals(java.lang.Object)",
+ "METHOD x()",
+ "METHOD y()");
+ }
+
+ @Test
+ public void missingElementValue() {
+ ImmutableList<Tree.CompUnit> units =
+ parseUnit(
+ "=== T.java ===", //
+ "import java.lang.annotation.Retention;",
+ "@Retention() @interface T {}");
+ TurbineError e =
+ assertThrows(
+ TurbineError.class,
+ () ->
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ ProcessorInfo.create(
+ // missing annotation arguments are not a recoverable error, annotation
+ // processing shouldn't happen
+ ImmutableList.of(new CrashingProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty()));
+ assertThat(e.diagnostics().stream().map(d -> d.message()))
+ .containsExactly("missing required annotation argument: value");
+ }
+
private static ImmutableList<Tree.CompUnit> parseUnit(String... lines) {
return IntegrationTestSupport.TestInput.parse(Joiner.on('\n').join(lines))
.sources
@@ -623,4 +716,92 @@ public class ProcessingIntegrationTest {
.map(Parser::parse)
.collect(toImmutableList());
}
+
+ @SupportedAnnotationTypes("*")
+ public static class AllMethodsProcessor extends AbstractProcessor {
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+
+ ImmutableList<ExecutableElement> methods =
+ typesIn(roundEnv.getRootElements()).stream()
+ .flatMap(t -> methodsIn(t.getEnclosedElements()).stream())
+ .collect(toImmutableList());
+ for (ExecutableElement a : methods) {
+ for (ExecutableElement b : methods) {
+ if (a.equals(b)) {
+ continue;
+ }
+ ExecutableType ta = (ExecutableType) a.asType();
+ ExecutableType tb = (ExecutableType) b.asType();
+ boolean r = processingEnv.getTypeUtils().isSubsignature(ta, tb);
+ processingEnv
+ .getMessager()
+ .printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "%s#%s%s <: %s#%s%s ? %s",
+ a.getEnclosingElement(),
+ a.getSimpleName(),
+ ta,
+ b.getEnclosingElement(),
+ b.getSimpleName(),
+ tb,
+ r));
+ }
+ }
+ return false;
+ }
+ }
+
+ @Test
+ public void bound() {
+ ImmutableList<Tree.CompUnit> units =
+ parseUnit(
+ "=== A.java ===", //
+ "import java.util.List;",
+ "class A<T> {",
+ " <U extends T> U f(List<U> list) {",
+ " return list.get(0);",
+ " }",
+ "}",
+ "class B extends A<String> {",
+ " @Override",
+ " <U extends String> U f(List<U> list) {",
+ " return super.f(list);",
+ " }",
+ "}",
+ "class C extends A<Object> {",
+ " @Override",
+ " <U> U f(List<U> list) {",
+ " return super.f(list);",
+ " }",
+ "}");
+ TurbineError e =
+ assertThrows(
+ TurbineError.class,
+ () ->
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ ProcessorInfo.create(
+ ImmutableList.of(new AllMethodsProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty()));
+ assertThat(e.diagnostics().stream().map(d -> d.message()))
+ .containsExactly(
+ "A#f<U>(java.util.List<U>)U <: B#f<U>(java.util.List<U>)U ? false",
+ "A#f<U>(java.util.List<U>)U <: C#f<U>(java.util.List<U>)U ? false",
+ "B#f<U>(java.util.List<U>)U <: A#f<U>(java.util.List<U>)U ? false",
+ "B#f<U>(java.util.List<U>)U <: C#f<U>(java.util.List<U>)U ? false",
+ "C#f<U>(java.util.List<U>)U <: A#f<U>(java.util.List<U>)U ? false",
+ "C#f<U>(java.util.List<U>)U <: B#f<U>(java.util.List<U>)U ? false");
+ }
}
diff --git a/javatests/com/google/turbine/processing/TurbineAnnotationMirrorTest.java b/javatests/com/google/turbine/processing/TurbineAnnotationMirrorTest.java
index a049860..b8daced 100644
--- a/javatests/com/google/turbine/processing/TurbineAnnotationMirrorTest.java
+++ b/javatests/com/google/turbine/processing/TurbineAnnotationMirrorTest.java
@@ -20,6 +20,7 @@ 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.auto.common.AnnotationValues;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -134,17 +135,17 @@ public class TurbineAnnotationMirrorTest {
@Override
public Object visitType(TypeMirror t, Void unused) {
- return value.toString();
+ return AnnotationValues.toString(value);
}
@Override
public Object visitEnumConstant(VariableElement c, Void unused) {
- return value.toString();
+ return AnnotationValues.toString(value);
}
@Override
public Object visitAnnotation(AnnotationMirror a, Void unused) {
- return value.toString();
+ return AnnotationValues.toString(value);
}
@Override
diff --git a/javatests/com/google/turbine/processing/TurbineElementsHidesTest.java b/javatests/com/google/turbine/processing/TurbineElementsHidesTest.java
new file mode 100644
index 0000000..55e9039
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineElementsHidesTest.java
@@ -0,0 +1,318 @@
+/*
+ * 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.Arrays.stream;
+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.common.collect.ImmutableSet;
+import com.google.common.collect.ObjectArrays;
+import com.google.common.truth.Expect;
+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.processing.TurbineElement.TurbineTypeElement;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.tree.Tree.CompUnit;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskListener;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.ElementScanner8;
+import javax.lang.model.util.Elements;
+import javax.tools.DiagnosticCollector;
+import javax.tools.JavaFileObject;
+import org.junit.Rule;
+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 TurbineElementsHidesTest {
+
+ @Rule public final Expect expect = Expect.create();
+
+ @Parameters
+ public static Iterable<TestInput[]> parameters() {
+ // An array of test inputs. Each element is an array of lines of sources to compile.
+ String[][] inputs = {
+ {
+ "=== A.java ===", //
+ "abstract class A {",
+ " int f;",
+ " static int f() { return 1; }",
+ " static int f(int x) { return 1; }",
+ "}",
+ "=== B.java ===",
+ "abstract class B extends A {",
+ " int f;",
+ " int g;",
+ " static int f() { return 1; }",
+ " static int f(int x) { return 1; }",
+ " static int g() { return 1; }",
+ " static int g(int x) { return 1; }",
+ "}",
+ "=== C.java ===",
+ "abstract class C extends B {",
+ " int f;",
+ " int g;",
+ " int h;",
+ " static int f() { return 1; }",
+ " static int g() { return 1; }",
+ " static int h() { return 1; }",
+ " static int f(int x) { return 1; }",
+ " static int g(int x) { return 1; }",
+ " static int h(int x) { return 1; }",
+ "}",
+ },
+ {
+ "=== A.java ===",
+ "class A {",
+ " class I {",
+ " }",
+ "}",
+ "=== B.java ===",
+ "class B extends A {",
+ " class I extends A.I {",
+ " }",
+ "}",
+ "=== C.java ===",
+ "class C extends B {",
+ " class I extends B.I {",
+ " }",
+ "}",
+ },
+ {
+ "=== A.java ===",
+ "class A {",
+ " class I {",
+ " }",
+ "}",
+ "=== B.java ===",
+ "class B extends A {",
+ " interface I {}",
+ "}",
+ "=== C.java ===",
+ "class C extends B {",
+ " @interface I {}",
+ "}",
+ },
+ {
+ // the containing class or interface of Intf.foo is an interface
+ "=== Outer.java ===",
+ "class Outer {",
+ " static class Inner {",
+ " static void foo() {}",
+ " static class Innerer extends Inner {",
+ " interface Intf {",
+ " static void foo() {}",
+ " }",
+ " }",
+ " }",
+ "}",
+ },
+ {
+ // test two top-level classes with the same name
+ "=== one/A.java ===",
+ "package one;",
+ "public class A {",
+ "}",
+ "=== two/A.java ===",
+ "package two;",
+ "public class A {",
+ "}",
+ },
+ };
+ // https://bugs.openjdk.java.net/browse/JDK-8275746
+ if (Runtime.version().feature() >= 11) {
+ inputs =
+ ObjectArrays.concat(
+ inputs,
+ new String[][] {
+ {
+ // interfaces
+ "=== A.java ===",
+ "interface A {",
+ " static void f() {}",
+ " int x = 42;",
+ "}",
+ "=== B.java ===",
+ "interface B extends A {",
+ " static void f() {}",
+ " int x = 42;",
+ "}",
+ }
+ },
+ String[].class);
+ }
+ return stream(inputs)
+ .map(input -> TestInput.parse(Joiner.on('\n').join(input)))
+ .map(x -> new TestInput[] {x})
+ .collect(toImmutableList());
+ }
+
+ private final TestInput input;
+
+ public TurbineElementsHidesTest(TestInput input) {
+ this.input = input;
+ }
+
+ // Compile the test inputs with javac and turbine, and assert that 'hides' returns the same
+ // results under each implementation.
+ @Test
+ public void test() throws Exception {
+ HidesTester javac = runJavac();
+ HidesTester turbine = runTurbine();
+ assertThat(javac.keys()).containsExactlyElementsIn(turbine.keys());
+ for (String k1 : javac.keys()) {
+ for (String k2 : javac.keys()) {
+ expect
+ .withMessage("hides(%s, %s)", k1, k2)
+ .that(javac.test(k1, k2))
+ .isEqualTo(turbine.test(k1, k2));
+ }
+ }
+ }
+
+ static class HidesTester {
+ // The elements for a particular annotation processing implementation
+ final Elements elements;
+ // A collection of Elements to use as test inputs, keyed by unique strings that can be used to
+ // compare them across processing implementations
+ final ImmutableMap<String, Element> inputs;
+
+ HidesTester(Elements elements, ImmutableMap<String, Element> inputs) {
+ this.elements = elements;
+ this.inputs = inputs;
+ }
+
+ boolean test(String k1, String k2) {
+ return elements.hides(inputs.get(k1), inputs.get(k2));
+ }
+
+ public ImmutableSet<String> keys() {
+ return inputs.keySet();
+ }
+ }
+
+ /** Compiles the test input with turbine. */
+ private HidesTester runTurbine() throws IOException {
+ 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 elements = new TurbineElements(factory, turbineTypes);
+ ImmutableList<TurbineTypeElement> typeElements =
+ bound.units().keySet().stream().map(factory::typeElement).collect(toImmutableList());
+ return new HidesTester(elements, collectElements(typeElements));
+ }
+
+ /** Compiles the test input with turbine. */
+ private HidesTester runJavac() throws Exception {
+ DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
+ JavacTask javacTask =
+ IntegrationTestSupport.runJavacAnalysis(
+ input.sources, ImmutableList.of(), ImmutableList.of(), diagnostics);
+ List<TypeElement> typeElements = new ArrayList<>();
+ javacTask.addTaskListener(
+ new TaskListener() {
+ @Override
+ public void started(TaskEvent e) {
+ if (e.getKind().equals(TaskEvent.Kind.ANALYZE)) {
+ typeElements.add(e.getTypeElement());
+ }
+ }
+ });
+ Elements elements = javacTask.getElements();
+ if (!javacTask.call()) {
+ fail(Joiner.on("\n").join(diagnostics.getDiagnostics()));
+ }
+ return new HidesTester(elements, collectElements(typeElements));
+ }
+
+ /** Scans a test compilation for elements to use as test inputs. */
+ private ImmutableMap<String, Element> collectElements(List<? extends TypeElement> typeElements) {
+ Map<String, Element> elements = new HashMap<>();
+ for (TypeElement typeElement : typeElements) {
+ elements.put(key(typeElement), typeElement);
+ new ElementScanner8<Void, Void>() {
+ @Override
+ public Void scan(Element e, Void unused) {
+ Element p = elements.put(key(e), e);
+ if (p != null && !e.equals(p) && !p.getKind().equals(ElementKind.CONSTRUCTOR)) {
+ throw new AssertionError(key(e) + " " + p + " " + e);
+ }
+ return super.scan(e, unused);
+ }
+ }.visit(typeElement);
+ }
+ return ImmutableMap.copyOf(elements);
+ }
+
+ /** A unique string representation of an element. */
+ private static String key(Element e) {
+ ArrayDeque<Name> names = new ArrayDeque<>();
+ Element curr = e;
+ do {
+ if (curr.getSimpleName().length() > 0) {
+ names.addFirst(curr.getSimpleName());
+ }
+ curr = curr.getEnclosingElement();
+ } while (curr != null);
+ String key = e.getKind() + ":" + Joiner.on('.').join(names);
+ if (e.getKind().equals(ElementKind.METHOD)) {
+ key += ":" + e.asType();
+ }
+ return key;
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineFilerTest.java b/javatests/com/google/turbine/processing/TurbineFilerTest.java
index d433428..83dcc70 100644
--- a/javatests/com/google/turbine/processing/TurbineFilerTest.java
+++ b/javatests/com/google/turbine/processing/TurbineFilerTest.java
@@ -40,7 +40,7 @@ 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.jspecify.nullness.Nullable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,9 +56,8 @@ public class TurbineFilerTest {
public void setup() {
Function<String, Supplier<byte[]>> classpath =
new Function<String, Supplier<byte[]>>() {
- @Nullable
@Override
- public Supplier<byte[]> apply(String input) {
+ public @Nullable Supplier<byte[]> apply(String input) {
return null;
}
};
@@ -100,9 +99,9 @@ public class TurbineFilerTest {
assertThrows(
FilerException.class, () -> filer.createSourceFile("com.foo.Bar", (Element[]) null));
- filer.createSourceFile("com.foo.Baz", (Element[]) null);
+ JavaFileObject unused = filer.createSourceFile("com.foo.Baz", (Element[]) null);
- filer.createClassFile("com.foo.Bar", (Element[]) null);
+ unused = filer.createClassFile("com.foo.Bar", (Element[]) null);
assertThrows(
FilerException.class, () -> filer.createClassFile("com.foo.Baz", (Element[]) null));
}
@@ -125,7 +124,7 @@ public class TurbineFilerTest {
try (Writer writer = classFile.openWriter()) {
writer.write("hello");
}
- filer.finishRound();
+ Collection<SourceFile> unused = filer.finishRound();
FileObject output = filer.getResource(StandardLocation.SOURCE_OUTPUT, "com.foo", "Bar.java");
assertThat(new String(ByteStreams.toByteArray(output.openInputStream()), UTF_8))
@@ -140,7 +139,7 @@ public class TurbineFilerTest {
try (OutputStream os = classFile.openOutputStream()) {
os.write("goodbye".getBytes(UTF_8));
}
- filer.finishRound();
+ Collection<SourceFile> unused = filer.finishRound();
FileObject output = filer.getResource(StandardLocation.CLASS_OUTPUT, "com.foo", "Baz.class");
assertThat(new String(ByteStreams.toByteArray(output.openInputStream()), UTF_8))
diff --git a/javatests/com/google/turbine/processing/TurbineMessagerTest.java b/javatests/com/google/turbine/processing/TurbineMessagerTest.java
index 017012c..c9ca26f 100644
--- a/javatests/com/google/turbine/processing/TurbineMessagerTest.java
+++ b/javatests/com/google/turbine/processing/TurbineMessagerTest.java
@@ -20,7 +20,10 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
+import static org.junit.Assert.assertThrows;
+import com.google.auto.common.AnnotationMirrors;
+import com.google.auto.common.AnnotationValues;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -134,7 +137,13 @@ public class TurbineMessagerTest {
processingEnv
.getMessager()
.printMessage(
- Diagnostic.Kind.ERROR, String.format("%s %s %s", e, a, av), e, a, av);
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "%s %s %s",
+ e, AnnotationMirrors.toString(a), AnnotationValues.toString(av)),
+ e,
+ a,
+ av);
av.accept(
new SimpleAnnotationValueVisitor8<Void, Void>() {
@Override
@@ -199,35 +208,33 @@ public class TurbineMessagerTest {
.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());
- }
-
+ TurbineError e =
+ assertThrows(
+ TurbineError.class,
+ () ->
+ 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()));
+ ImmutableList<String> turbineDiagnostics =
+ e.diagnostics().stream()
+ .sorted(
+ comparing(TurbineDiagnostic::path)
+ .thenComparing(TurbineDiagnostic::line)
+ .thenComparing(TurbineDiagnostic::column))
+ .map(TurbineMessagerTest::formatDiagnostic)
+ .collect(toImmutableList());
assertThat(turbineDiagnostics).containsExactlyElementsIn(javacDiagnostics).inOrder();
}
diff --git a/javatests/com/google/turbine/testing/TestClassPaths.java b/javatests/com/google/turbine/testing/TestClassPaths.java
index 55e8b9e..56b471c 100644
--- a/javatests/com/google/turbine/testing/TestClassPaths.java
+++ b/javatests/com/google/turbine/testing/TestClassPaths.java
@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableList;
import com.google.turbine.binder.ClassPath;
import com.google.turbine.binder.ClassPathBinder;
import com.google.turbine.binder.JimageClassBinder;
+import com.google.turbine.options.LanguageVersion;
import com.google.turbine.options.TurbineOptions;
import java.io.File;
import java.io.IOException;
@@ -68,7 +69,7 @@ public final class TestClassPaths {
options.setBootClassPath(
BOOTCLASSPATH.stream().map(Path::toString).collect(toImmutableList()));
} else {
- options.setRelease("8");
+ options.setLanguageVersion(LanguageVersion.fromJavacopts(ImmutableList.of("--release", "8")));
}
return options;
}
diff --git a/javatests/com/google/turbine/zip/ZipTest.java b/javatests/com/google/turbine/zip/ZipTest.java
index 0d49e1a..e9dfc44 100644
--- a/javatests/com/google/turbine/zip/ZipTest.java
+++ b/javatests/com/google/turbine/zip/ZipTest.java
@@ -159,7 +159,7 @@ public class ZipTest {
createEntry(zos, "hello", "world".getBytes(UTF_8));
zos.setComment("this is a comment");
}
- Files.write(path, "trailing garbage".getBytes(UTF_8), StandardOpenOption.APPEND);
+ Files.writeString(path, "trailing garbage", StandardOpenOption.APPEND);
ZipException e = assertThrows(ZipException.class, () -> actual(path));
assertThat(e).hasMessageThat().isEqualTo("zip file comment length was 33, expected 17");
diff --git a/pom.xml b/pom.xml
index dae4b70..b007e74 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,20 +26,31 @@
<version>HEAD-SNAPSHOT</version>
<name>turbine</name>
- <description>
- turbine is a header compiler for Java
- </description>
+ <description>turbine is a header compiler for Java</description>
+ <url>https://github.com/google/turbine</url>
<properties>
- <asm.version>9.1</asm.version>
- <javac.version>9+181-r4173-1</javac.version>
- <guava.version>30.0-jre</guava.version>
- <errorprone.version>2.7.1</errorprone.version>
- <maven-javadoc-plugin.version>3.1.0</maven-javadoc-plugin.version>
+ <asm.version>9.2</asm.version>
+ <guava.version>31.0.1-jre</guava.version>
+ <errorprone.version>2.11.0</errorprone.version>
+ <maven-javadoc-plugin.version>3.3.1</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.2.1</maven-source-plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <protobuf.version>3.19.2</protobuf.version>
+ <grpc.version>1.43.2</grpc.version>
</properties>
+ <organization>
+ <name>Google Inc.</name>
+ <url>http://www.google.com/</url>
+ </organization>
+
+ <developers>
+ <developer>
+ <name>Liam Miller-Cushon</name>
+ </developer>
+ </developers>
+
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
@@ -52,15 +63,15 @@
<version>${errorprone.version}</version>
</dependency>
<dependency>
- <groupId>org.checkerframework</groupId>
- <artifactId>checker-qual</artifactId>
- <version>3.9.1</version>
+ <groupId>org.jspecify</groupId>
+ <artifactId>jspecify</artifactId>
+ <version>0.2.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
- <version>3.10.0</version>
+ <version>${protobuf.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
@@ -81,33 +92,27 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>com.google.errorprone</groupId>
- <artifactId>javac</artifactId>
- <version>${javac.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>4.13.1</version>
+ <version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
- <version>1.1</version>
+ <version>1.1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.truth.extensions</groupId>
<artifactId>truth-proto-extension</artifactId>
- <version>1.1</version>
+ <version>1.1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.truth.extensions</groupId>
<artifactId>truth-java8-extension</artifactId>
- <version>1.1</version>
+ <version>1.1.3</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -125,9 +130,15 @@
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value-annotations</artifactId>
- <version>1.7.4</version>
+ <version>1.9</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>com.google.auto</groupId>
+ <artifactId>auto-common</artifactId>
+ <version>1.2.1</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
@@ -146,32 +157,22 @@
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
- <version>1.4.0.Final</version>
+ <version>1.7.0</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>3.8.0</version>
+ <version>3.9.0</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
- <fork>true</fork>
<compilerArgs>
<arg>-parameters</arg>
<arg>-XDcompilePolicy=simple</arg>
<arg>-Xplugin:ErrorProne</arg>
- <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
- <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
- <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
- <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
- <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
- <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
- <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
- <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
- <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
@@ -190,12 +191,12 @@
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
- <version>0.5.0</version>
+ <version>0.6.1</version>
<configuration>
<protoSourceRoot>proto</protoSourceRoot>
- <protocArtifact>com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier}</protocArtifact>
+ <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
- <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.0.1:exe:${os.detected.classifier}</pluginArtifact>
+ <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
@@ -209,7 +210,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
- <version>2.19.1</version>
+ <version>2.22.2</version>
<configuration>
<!-- set heap size to work around http://github.com/travis-ci/travis-ci/issues/3396 -->
<argLine>
@@ -230,7 +231,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
- <version>2.4.3</version>
+ <version>3.2.4</version>
<executions>
<execution>
<id>shade-all-deps</id>
@@ -260,7 +261,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
- <version>3.1.1</version>
+ <version>3.3.1</version>
<configuration>
<source>8</source>
<detectJavaApiLink>false</detectJavaApiLink>
@@ -286,37 +287,6 @@
<profiles>
<profile>
- <id>java-8</id>
- <activation>
- <jdk>1.8</jdk>
- </activation>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <!-- put javac.jar on bootclasspath when executing tests -->
- <argLine>-Xbootclasspath/p:${settings.localRepository}/com/google/errorprone/javac/${javac.version}/javac-${javac.version}.jar</argLine>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <fork>true</fork>
- <compilerArgs>
- <arg>-parameters</arg>
- <arg>-XDcompilePolicy=simple</arg>
- <arg>-Xplugin:ErrorProne</arg>
- <arg>-J-Xbootclasspath/p:${settings.localRepository}/com/google/errorprone/javac/${javac.version}/javac-${javac.version}.jar</arg>
- </compilerArgs>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </profile>
- <profile>
<id>sonatype-oss-release</id>
<build>
<plugins>
@@ -349,7 +319,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
- <version>1.6</version>
+ <version>3.0.1</version>
<executions>
<execution>
<id>sign-artifacts</id>