diff options
author | Colin Cross <ccross@android.com> | 2022-03-23 03:19:48 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-03-23 03:19:48 +0000 |
commit | 133c1071528f6c21b9f609ce483818c81667c16e (patch) | |
tree | 60c2f0c548551743407d31a0b4be18bebf792603 /java/com/google/turbine | |
parent | b5fe7f1c2587f22da5d51648f13263e671b19e95 (diff) | |
parent | c1a0e4def84e17508a07ba5a73eb746d9363aa2b (diff) | |
download | turbine-133c1071528f6c21b9f609ce483818c81667c16e.tar.gz |
Merge remote-tracking branch 'aosp/upstream-main' am: 42b4c61d46 am: 71e7dd63f7 am: c339216720 am: c1a0e4def8
Original change: https://android-review.googlesource.com/c/platform/external/turbine/+/2029125
Change-Id: I2d2d54d844094563b0456a68260a945138ca9086
Diffstat (limited to 'java/com/google/turbine')
120 files changed, 3186 insertions, 1372 deletions
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; |