aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiam Miller-Cushon <cushon@google.com>2021-09-27 16:01:48 -0700
committerJavac Team <javac-team+copybara@google.com>2021-09-27 16:02:29 -0700
commitec67a6e2e67de35ba2fc52a419b6363432ce72a2 (patch)
tree0c84efd92078e288d91ae5771af4f84817bd12a4
parent3bac9d7ffd87c0e3ed8e27d72dc9cb2a4d00e364 (diff)
downloadturbine-ec67a6e2e67de35ba2fc52a419b6363432ce72a2.tar.gz
Initial support for records
PiperOrigin-RevId: 399302681
-rw-r--r--java/com/google/turbine/binder/CanonicalTypeBinder.java3
-rw-r--r--java/com/google/turbine/binder/CompUnitPreprocessor.java8
-rw-r--r--java/com/google/turbine/binder/ConstBinder.java2
-rw-r--r--java/com/google/turbine/binder/DisambiguateTypeAnnotations.java22
-rw-r--r--java/com/google/turbine/binder/HierarchyBinder.java3
-rw-r--r--java/com/google/turbine/binder/TypeBinder.java159
-rw-r--r--java/com/google/turbine/binder/bound/SourceTypeBoundClass.java9
-rw-r--r--java/com/google/turbine/binder/bound/TypeBoundClass.java4
-rw-r--r--java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java5
-rw-r--r--java/com/google/turbine/binder/sym/ClassSymbol.java1
-rw-r--r--java/com/google/turbine/lower/Lower.java83
-rw-r--r--java/com/google/turbine/model/TurbineElementType.java3
-rw-r--r--java/com/google/turbine/processing/TurbineElement.java2
-rw-r--r--javatests/com/google/turbine/lower/LowerIntegrationTest.java36
-rw-r--r--javatests/com/google/turbine/lower/LowerTest.java2
-rw-r--r--javatests/com/google/turbine/lower/testdata/record.test46
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) {
+ }
+}