diff options
author | Liam Miller-Cushon <cushon@google.com> | 2021-09-27 16:01:48 -0700 |
---|---|---|
committer | Javac Team <javac-team+copybara@google.com> | 2021-09-27 16:02:29 -0700 |
commit | ec67a6e2e67de35ba2fc52a419b6363432ce72a2 (patch) | |
tree | 0c84efd92078e288d91ae5771af4f84817bd12a4 | |
parent | 3bac9d7ffd87c0e3ed8e27d72dc9cb2a4d00e364 (diff) | |
download | turbine-ec67a6e2e67de35ba2fc52a419b6363432ce72a2.tar.gz |
Initial support for records
PiperOrigin-RevId: 399302681
16 files changed, 350 insertions, 38 deletions
diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java index 91fbac7..304005f 100644 --- a/java/com/google/turbine/binder/CanonicalTypeBinder.java +++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java @@ -60,6 +60,8 @@ public final class CanonicalTypeBinder { } ImmutableMap<TyVarSymbol, TyVarInfo> typParamTypes = typeParameters(base.source(), pos, env, sym, base.typeParameterTypes()); + ImmutableList<ParamInfo> components = + parameters(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( @@ -67,6 +69,7 @@ public final class CanonicalTypeBinder { superClassType, typParamTypes, base.access(), + components, methods, fields, base.owner(), diff --git a/java/com/google/turbine/binder/CompUnitPreprocessor.java b/java/com/google/turbine/binder/CompUnitPreprocessor.java index 079cd4c..3323938 100644 --- a/java/com/google/turbine/binder/CompUnitPreprocessor.java +++ b/java/com/google/turbine/binder/CompUnitPreprocessor.java @@ -176,8 +176,8 @@ public final class CompUnitPreprocessor { access |= TurbineFlag.ACC_ABSTRACT | TurbineFlag.ACC_INTERFACE | TurbineFlag.ACC_ANNOTATION; break; case RECORD: - // TODO(b/200222393): add support for records - throw new AssertionError(tykind); + access |= TurbineFlag.ACC_SUPER | TurbineFlag.ACC_FINAL; + break; } return access; } @@ -198,6 +198,7 @@ public final class CompUnitPreprocessor { case INTERFACE: case ENUM: case ANNOTATION: + case RECORD: access |= TurbineFlag.ACC_STATIC; break; case CLASS: @@ -205,9 +206,6 @@ public final class CompUnitPreprocessor { access |= TurbineFlag.ACC_STATIC; } break; - case RECORD: - // TODO(b/200222393): add support for records - throw new AssertionError(decl.tykind()); } // propagate strictfp to nested types diff --git a/java/com/google/turbine/binder/ConstBinder.java b/java/com/google/turbine/binder/ConstBinder.java index 5f26ddc..b2c4e1c 100644 --- a/java/com/google/turbine/binder/ConstBinder.java +++ b/java/com/google/turbine/binder/ConstBinder.java @@ -104,6 +104,7 @@ public class ConstBinder { env, log) .evaluateAnnotations(base.annotations()); + ImmutableList<TypeBoundClass.ParamInfo> components = bindParameters(base.components()); ImmutableList<TypeBoundClass.FieldInfo> fields = fields(base.fields()); ImmutableList<MethodInfo> methods = bindMethods(base.methods()); return new SourceTypeBoundClass( @@ -111,6 +112,7 @@ public class ConstBinder { base.superClassType() != null ? bindType(base.superClassType()) : null, bindTypeParameters(base.typeParameterTypes()), base.access(), + components, methods, fields, base.owner(), diff --git a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java index c5de8c1..0acd0c5 100644 --- a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java +++ b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java @@ -73,6 +73,7 @@ public final class DisambiguateTypeAnnotations { base.superClassType(), base.typeParameterTypes(), base.access(), + bindParameters(env, base.components(), TurbineElementType.RECORD_COMPONENT), bindMethods(env, base.methods()), bindFields(env, base.fields()), base.owner(), @@ -112,33 +113,34 @@ 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()); } diff --git a/java/com/google/turbine/binder/HierarchyBinder.java b/java/com/google/turbine/binder/HierarchyBinder.java index 2d33613..9a5f763 100644 --- a/java/com/google/turbine/binder/HierarchyBinder.java +++ b/java/com/google/turbine/binder/HierarchyBinder.java @@ -83,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()); } diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java index ecb8ea7..b28aa24 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; @@ -215,6 +216,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()); } @@ -229,11 +233,18 @@ public class TypeBinder { .append(new SingletonScope(base.decl().name().value(), owner)) .append(new ClassMemberScope(owner, env)); - List<MethodInfo> methods = + SyntheticMethods syntheticMethods = new SyntheticMethods(); + + ImmutableList<ParamInfo> components = + bindComponents(scope, syntheticMethods, 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())); + if (base.kind().equals(TurbineTyKind.RECORD)) { + methods.addAll(syntheticRecordMethods(syntheticMethods, components)); + } ImmutableList<FieldInfo> fields = bindFields(scope, base.decl().members()); @@ -242,7 +253,8 @@ public class TypeBinder { superClassType, typeParameterTypes, base.access(), - ImmutableList.copyOf(methods), + components, + methods.build(), fields, base.owner(), base.kind(), @@ -257,23 +269,73 @@ 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<ParamInfo> bindComponents( + CompoundScope scope, + SyntheticMethods syntheticMethods, + ImmutableList<Tree.VarDecl> components) { + ImmutableList.Builder<ParamInfo> result = ImmutableList.builder(); + for (Tree.VarDecl p : components) { + int access = 0; + for (TurbineModifier m : p.mods()) { + access |= m.flag(); + } + MethodSymbol msym = syntheticMethods.create(owner, ""); + ParamInfo param = + new ParamInfo( + new ParamSymbol(msym, 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<ParamInfo> 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<ParamInfo> components) { if (hasConstructor()) { return ImmutableList.of(); } - MethodSymbol symbol = new MethodSymbol(-1, owner, "<init>"); + MethodSymbol symbol = syntheticMethods.create(owner, "<init>"); + return ImmutableList.of( + syntheticConstructor(symbol, components, 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)); @@ -288,6 +350,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(), @@ -341,15 +407,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, @@ -362,7 +428,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, @@ -383,6 +449,71 @@ public class TypeBinder { return methods.build(); } + private ImmutableList<MethodInfo> syntheticRecordMethods( + SyntheticMethods syntheticMethods, ImmutableList<ParamInfo> 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 (ParamInfo 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) { diff --git a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java index cadd141..f1ad839 100644 --- a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java +++ b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java @@ -44,6 +44,7 @@ public class SourceTypeBoundClass implements TypeBoundClass { private final ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes; private final @Nullable Type superClassType; private final ImmutableList<Type> interfaceTypes; + private final ImmutableList<ParamInfo> components; private final ImmutableList<MethodInfo> methods; private final ImmutableList<FieldInfo> fields; private final CompoundScope enclosingScope; @@ -59,6 +60,7 @@ public class SourceTypeBoundClass implements TypeBoundClass { @Nullable Type superClassType, ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes, int access, + ImmutableList<ParamInfo> components, ImmutableList<MethodInfo> methods, ImmutableList<FieldInfo> fields, @Nullable ClassSymbol owner, @@ -76,6 +78,7 @@ public class SourceTypeBoundClass implements TypeBoundClass { this.superClassType = superClassType; this.typeParameterTypes = typeParameterTypes; this.access = access; + this.components = components; this.methods = methods; this.fields = fields; this.owner = owner; @@ -149,6 +152,12 @@ public class SourceTypeBoundClass implements TypeBoundClass { return superClassType; } + /** The record components. */ + @Override + public ImmutableList<ParamInfo> components() { + return components; + } + /** Declared methods. */ @Override public ImmutableList<MethodInfo> methods() { diff --git a/java/com/google/turbine/binder/bound/TypeBoundClass.java b/java/com/google/turbine/binder/bound/TypeBoundClass.java index 1d7a4cc..14f4f2d 100644 --- a/java/com/google/turbine/binder/bound/TypeBoundClass.java +++ b/java/com/google/turbine/binder/bound/TypeBoundClass.java @@ -16,7 +16,6 @@ package com.google.turbine.binder.bound; - import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.turbine.binder.sym.FieldSymbol; @@ -51,6 +50,9 @@ public interface TypeBoundClass extends HeaderBoundClass { /** Declared methods. */ ImmutableList<MethodInfo> methods(); + /** Record components. */ + ImmutableList<ParamInfo> components(); + /** * Annotation metadata, e.g. from {@link java.lang.annotation.Target}, {@link * java.lang.annotation.Retention}, and {@link java.lang.annotation.Repeatable}. diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java index 7317ba4..1f196f5 100644 --- a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java +++ b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java @@ -486,6 +486,11 @@ public class BytecodeBoundClass implements TypeBoundClass { return methods.get(); } + @Override + public ImmutableList<ParamInfo> components() { + return ImmutableList.of(); + } + private final Supplier<@Nullable AnnotationMetadata> annotationMetadata = Suppliers.memoize( new Supplier<@Nullable AnnotationMetadata>() { diff --git a/java/com/google/turbine/binder/sym/ClassSymbol.java b/java/com/google/turbine/binder/sym/ClassSymbol.java index 45c9adf..9bb556f 100644 --- a/java/com/google/turbine/binder/sym/ClassSymbol.java +++ b/java/com/google/turbine/binder/sym/ClassSymbol.java @@ -33,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"); diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java index 9b2d49f..d4d02bd 100644 --- a/java/com/google/turbine/lower/Lower.java +++ b/java/com/google/turbine/lower/Lower.java @@ -259,6 +259,16 @@ public class Lower { interfaces.add(sig.descriptor(i)); } + ClassFile.RecordInfo record = null; + if (info.kind().equals(TurbineTyKind.RECORD)) { + ImmutableList.Builder<ClassFile.RecordInfo.RecordComponentInfo> components = + ImmutableList.builder(); + for (ParamInfo 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()) { if (TurbineVisibility.fromAccess(m.access()) == TurbineVisibility.PRIVATE) { @@ -281,6 +291,14 @@ 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 = @@ -297,9 +315,9 @@ public class Lower { inners, typeAnnotations, /* module= */ null, - /* nestHost= */ null, - /* nestMembers= */ ImmutableList.of(), - /* record= */ null, + nestHost, + nestMembers, + record, /* transitiveJar= */ null); symbols.addAll(sig.classes); @@ -307,6 +325,18 @@ public class Lower { return ClassWriter.writeClass(classfile); } + private ClassFile.RecordInfo.RecordComponentInfo lowerComponent( + SourceTypeBoundClass info, ParamInfo 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()); @@ -440,13 +470,58 @@ public class Lower { if (info == null) { throw TurbineError.format(source, ErrorKind.CLASS_FILE_NOT_FOUND, sym); } - ClassSymbol owner = env.getNonNull(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. 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/processing/TurbineElement.java b/java/com/google/turbine/processing/TurbineElement.java index 69f6ec0..8581fda 100644 --- a/java/com/google/turbine/processing/TurbineElement.java +++ b/java/com/google/turbine/processing/TurbineElement.java @@ -379,7 +379,7 @@ public abstract class TurbineElement implements Element { case ANNOTATION: return ElementKind.ANNOTATION_TYPE; case RECORD: - // TODO(b/200222393): add support for records + return ElementKind.valueOf("RECORD"); } throw new AssertionError(info.kind()); } diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java index f832314..389f55a 100644 --- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java +++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java @@ -16,14 +16,19 @@ package com.google.turbine.lower; +import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_VERSION; +import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION; import static com.google.common.truth.Truth.assertThat; import static com.google.turbine.testing.TestResources.getResource; import static java.util.stream.Collectors.toList; +import static org.junit.Assume.assumeTrue; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import java.io.IOError; import java.io.IOException; +import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -41,6 +46,9 @@ import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class LowerIntegrationTest { + private static final ImmutableMap<String, Integer> SOURCE_VERSION = + ImmutableMap.of("record.test", 16); + @Parameters(name = "{index}: {0}") public static Iterable<Object[]> parameters() { String[] testCases = { @@ -256,6 +264,7 @@ public class LowerIntegrationTest { "rawcanon.test", "rawfbound.test", "receiver_param.test", + "record.test", "rek.test", "samepkg.test", "self.test", @@ -366,11 +375,34 @@ public class LowerIntegrationTest { classpathJar = ImmutableList.of(lib); } - Map<String, byte[]> expected = IntegrationTestSupport.runJavac(input.sources, classpathJar); + int version = SOURCE_VERSION.getOrDefault(test, 8); + assumeTrue(version <= getMajor()); + ImmutableList<String> javacopts = + ImmutableList.of("-source", String.valueOf(version), "-target", String.valueOf(version)); + + Map<String, byte[]> expected = + IntegrationTestSupport.runJavac(input.sources, classpathJar, javacopts); - Map<String, byte[]> actual = IntegrationTestSupport.runTurbine(input.sources, classpathJar); + Map<String, byte[]> actual = + IntegrationTestSupport.runTurbine(input.sources, classpathJar, javacopts); assertThat(IntegrationTestSupport.dump(IntegrationTestSupport.sortMembers(actual))) .isEqualTo(IntegrationTestSupport.dump(IntegrationTestSupport.canonicalize(expected))); } + + private static int getMajor() { + try { + Method versionMethod = Runtime.class.getMethod("version"); + Object version = versionMethod.invoke(null); + return (int) version.getClass().getMethod("major").invoke(version); + } catch (ReflectiveOperationException e) { + // continue below + } + + int version = (int) Double.parseDouble(JAVA_CLASS_VERSION.value()); + if (49 <= version && version <= 52) { + return version - (49 - 5); + } + throw new IllegalStateException("Unknown Java version: " + JAVA_SPECIFICATION_VERSION.value()); + } } diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java index 67fb549..e560321 100644 --- a/javatests/com/google/turbine/lower/LowerTest.java +++ b/javatests/com/google/turbine/lower/LowerTest.java @@ -188,6 +188,7 @@ public class LowerTest { xtnds, tps, access, + ImmutableList.of(), methods, fields, owner, @@ -210,6 +211,7 @@ public class LowerTest { TurbineFlag.ACC_STATIC | TurbineFlag.ACC_PROTECTED, ImmutableList.of(), ImmutableList.of(), + ImmutableList.of(), new ClassSymbol("test/Test"), TurbineTyKind.CLASS, ImmutableMap.of("Inner", new ClassSymbol("test/Test$Inner")), diff --git a/javatests/com/google/turbine/lower/testdata/record.test b/javatests/com/google/turbine/lower/testdata/record.test new file mode 100644 index 0000000..7d92c2b --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/record.test @@ -0,0 +1,46 @@ +=== Records.java === +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; +import java.util.List; + +class Records { + record R1() {} + + private record R2() {} + + @Deprecated + private record R3() {} + + record R4<T>() {} + + record R5<T>(int x) {} + + record R6<T>(@Deprecated int x) {} + + record R7<T>(@Deprecated int x, int... y) {} + + record R8<T>() implements Comparable<R8<T>> { + @Override + public int compareTo(R8<T> other) { + return 0; + } + } + + record R9(int x) { + R9(int x) { + this.x = x; + } + } + + @Target(ElementType.TYPE_USE) + @interface A {} + + @Target(ElementType.RECORD_COMPONENT) + @interface B {} + + @Target({ElementType.TYPE_USE, ElementType.RECORD_COMPONENT}) + @interface C {} + + record R10<T>(@A List<@A T> x, @B int y, @C int z) { + } +} |