From 0b547dc65ba2628d4dc595e8395c6f4cd7652170 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 1 Oct 2021 15:20:24 -0700 Subject: [PATCH] Initial support for sealed classes PiperOrigin-RevId: 400303125 --- .../turbine/binder/CanonicalTypeBinder.java | 1 + .../turbine/binder/CompUnitPreprocessor.java | 1 + .../google/turbine/binder/ConstBinder.java | 1 + .../binder/DisambiguateTypeAnnotations.java | 1 + .../com/google/turbine/binder/TypeBinder.java | 10 +++ .../binder/bound/SourceTypeBoundClass.java | 8 ++ .../turbine/binder/bound/TypeBoundClass.java | 4 + .../binder/bytecode/BytecodeBoundClass.java | 5 ++ .../google/turbine/bytecode/Attribute.java | 17 +++- .../turbine/bytecode/AttributeWriter.java | 13 ++++ .../google/turbine/bytecode/ClassFile.java | 8 ++ .../google/turbine/bytecode/ClassReader.java | 1 + .../turbine/bytecode/LowerAttributes.java | 3 + java/com/google/turbine/deps/Transitive.java | 1 + java/com/google/turbine/lower/Lower.java | 6 ++ .../com/google/turbine/model/TurbineFlag.java | 3 + java/com/google/turbine/parse/Parser.java | 77 ++++++++++++++++++- java/com/google/turbine/tree/Pretty.java | 13 ++++ java/com/google/turbine/tree/Tree.java | 7 ++ .../google/turbine/tree/TurbineModifier.java | 6 +- .../turbine/bytecode/ClassWriterTest.java | 2 + .../turbine/lower/LowerIntegrationTest.java | 6 +- .../com/google/turbine/lower/LowerTest.java | 2 + .../lower/testdata/ambiguous_identifier.test | 14 ++++ .../google/turbine/lower/testdata/sealed.test | 11 +++ .../turbine/parse/ParserIntegrationTest.java | 1 + .../turbine/parse/testdata/sealed.input | 15 ++++ 27 files changed, 229 insertions(+), 8 deletions(-) create mode 100644 javatests/com/google/turbine/lower/testdata/ambiguous_identifier.test create mode 100644 javatests/com/google/turbine/lower/testdata/sealed.test create mode 100644 javatests/com/google/turbine/parse/testdata/sealed.input diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java index cbdc025f..944563f9 100644 --- a/java/com/google/turbine/binder/CanonicalTypeBinder.java +++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java @@ -67,6 +67,7 @@ static SourceTypeBoundClass bind( ImmutableList fields = fields(base.source(), env, sym, base.fields()); return new SourceTypeBoundClass( interfaceTypes.build(), + base.permits(), superClassType, typParamTypes, base.access(), diff --git a/java/com/google/turbine/binder/CompUnitPreprocessor.java b/java/com/google/turbine/binder/CompUnitPreprocessor.java index 33239384..0e9f4e55 100644 --- a/java/com/google/turbine/binder/CompUnitPreprocessor.java +++ b/java/com/google/turbine/binder/CompUnitPreprocessor.java @@ -225,6 +225,7 @@ private static TyDecl packageInfoTree(PkgDecl pkgDecl) { 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 232370cc..3ba059f0 100644 --- a/java/com/google/turbine/binder/ConstBinder.java +++ b/java/com/google/turbine/binder/ConstBinder.java @@ -110,6 +110,7 @@ public SourceTypeBoundClass bind() { ImmutableList methods = bindMethods(base.methods()); return new SourceTypeBoundClass( bindTypes(base.interfaceTypes()), + base.permits(), base.superClassType() != null ? bindType(base.superClassType()) : null, bindTypeParameters(base.typeParameterTypes()), base.access(), diff --git a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java index 4929962e..65c10210 100644 --- a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java +++ b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java @@ -71,6 +71,7 @@ public static SourceTypeBoundClass bind( SourceTypeBoundClass base, Env env) { return new SourceTypeBoundClass( base.interfaceTypes(), + base.permits(), base.superClassType(), base.typeParameterTypes(), base.access(), diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java index f4b48493..ccc0ad3a 100644 --- a/java/com/google/turbine/binder/TypeBinder.java +++ b/java/com/google/turbine/binder/TypeBinder.java @@ -229,6 +229,15 @@ private SourceTypeBoundClass bind() { interfaceTypes.add(bindClassTy(bindingScope, i)); } + ImmutableList.Builder 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)) @@ -251,6 +260,7 @@ private SourceTypeBoundClass bind() { return new SourceTypeBoundClass( interfaceTypes.build(), + permits.build(), superClassType, typeParameterTypes, base.access(), diff --git a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java index fb3ee277..5e9817e5 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 typeParameterTypes; private final @Nullable Type superClassType; private final ImmutableList interfaceTypes; + private final ImmutableList permits; private final ImmutableList components; private final ImmutableList methods; private final ImmutableList fields; @@ -57,6 +58,7 @@ public class SourceTypeBoundClass implements TypeBoundClass { public SourceTypeBoundClass( ImmutableList interfaceTypes, + ImmutableList permits, @Nullable Type superClassType, ImmutableMap typeParameterTypes, int access, @@ -75,6 +77,7 @@ public SourceTypeBoundClass( SourceFile source, Tree.TyDecl decl) { this.interfaceTypes = interfaceTypes; + this.permits = permits; this.superClassType = superClassType; this.typeParameterTypes = typeParameterTypes; this.access = access; @@ -116,6 +119,11 @@ public ImmutableList interfaces() { return result.build(); } + @Override + public ImmutableList permits() { + return permits; + } + @Override public int access() { return access; diff --git a/java/com/google/turbine/binder/bound/TypeBoundClass.java b/java/com/google/turbine/binder/bound/TypeBoundClass.java index cc289019..8321bde2 100644 --- a/java/com/google/turbine/binder/bound/TypeBoundClass.java +++ b/java/com/google/turbine/binder/bound/TypeBoundClass.java @@ -18,6 +18,7 @@ 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; @@ -43,6 +44,9 @@ public interface TypeBoundClass extends HeaderBoundClass { /** Implemented interface types. */ ImmutableList interfaceTypes(); + /** The permitted direct subclasses. */ + ImmutableList permits(); + ImmutableMap typeParameterTypes(); /** Declared fields. */ diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java index 516a027a..9be2dd1f 100644 --- a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java +++ b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java @@ -306,6 +306,11 @@ public ImmutableList interfaceTypes() { return interfaceTypes.get(); } + @Override + public ImmutableList permits() { + return ImmutableList.of(); + } + private final Supplier> typeParameterTypes = Suppliers.memoize( new Supplier>() { diff --git a/java/com/google/turbine/bytecode/Attribute.java b/java/com/google/turbine/bytecode/Attribute.java index 581de3d4..ad6ffc17 100644 --- a/java/com/google/turbine/bytecode/Attribute.java +++ b/java/com/google/turbine/bytecode/Attribute.java @@ -45,7 +45,8 @@ enum Kind { NEST_HOST("NestHost"), NEST_MEMBERS("NestMembers"), RECORD("Record"), - TURBINE_TRANSITIVE_JAR("TurbineTransitiveJar"); + TURBINE_TRANSITIVE_JAR("TurbineTransitiveJar"), + PERMITTED_SUBCLASSES("PermittedSubclasses"); private final String signature; @@ -396,6 +397,20 @@ List attributes() { } } + /** A JVMS ยง4.7.31 PermittedSubclasses attribute. */ + class PermittedSubclasses implements Attribute { + final List permits; + + public PermittedSubclasses(List 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 ed7b2ab2..6aac19a1 100644 --- a/java/com/google/turbine/bytecode/AttributeWriter.java +++ b/java/com/google/turbine/bytecode/AttributeWriter.java @@ -95,6 +95,9 @@ public void write(ByteArrayDataOutput output, Attribute attribute) { case RECORD: writeRecord(output, (Attribute.Record) attribute); break; + case PERMITTED_SUBCLASSES: + writePermittedSubclasses(output, (Attribute.PermittedSubclasses) attribute); + break; case TURBINE_TRANSITIVE_JAR: writeTurbineTransitiveJar(output, (Attribute.TurbineTransitiveJar) attribute); break; @@ -318,6 +321,16 @@ private void writeRecord(ByteArrayDataOutput output, Attribute.Record attribute) 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())); diff --git a/java/com/google/turbine/bytecode/ClassFile.java b/java/com/google/turbine/bytecode/ClassFile.java index 124e03c9..820f17d4 100644 --- a/java/com/google/turbine/bytecode/ClassFile.java +++ b/java/com/google/turbine/bytecode/ClassFile.java @@ -37,6 +37,7 @@ public class ClassFile { private final @Nullable String signature; private final @Nullable String superClass; private final List interfaces; + private final List permits; private final List methods; private final List fields; private final List annotations; @@ -55,6 +56,7 @@ public ClassFile( @Nullable String signature, @Nullable String superClass, List interfaces, + List permits, List methods, List fields, List annotations, @@ -71,6 +73,7 @@ public ClassFile( this.signature = signature; this.superClass = superClass; this.interfaces = interfaces; + this.permits = permits; this.methods = methods; this.fields = fields; this.annotations = annotations; @@ -113,6 +116,11 @@ public List interfaces() { return interfaces; } + /** The permitted direct subclasses. */ + public List permits() { + return permits; + } + /** Methods declared by this class or interfaces type. */ public List methods() { return methods; diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java index b4290321..0f08fc30 100644 --- a/java/com/google/turbine/bytecode/ClassReader.java +++ b/java/com/google/turbine/bytecode/ClassReader.java @@ -143,6 +143,7 @@ private ClassFile read() { signature, superClass, interfaces, + /* permits= */ ImmutableList.of(), methodinfos, fieldinfos, annotations.build(), diff --git a/java/com/google/turbine/bytecode/LowerAttributes.java b/java/com/google/turbine/bytecode/LowerAttributes.java index 54937fc1..8952dffd 100644 --- a/java/com/google/turbine/bytecode/LowerAttributes.java +++ b/java/com/google/turbine/bytecode/LowerAttributes.java @@ -54,6 +54,9 @@ static List classAttributes(ClassFile classfile) { 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())); } diff --git a/java/com/google/turbine/deps/Transitive.java b/java/com/google/turbine/deps/Transitive.java index 62d15c31..fa3d4757 100644 --- a/java/com/google/turbine/deps/Transitive.java +++ b/java/com/google/turbine/deps/Transitive.java @@ -95,6 +95,7 @@ public static ClassFile trimClass(ClassFile cf, @Nullable String jarFile) { 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() diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java index 0f3eb245..ff7e13c4 100644 --- a/java/com/google/turbine/lower/Lower.java +++ b/java/com/google/turbine/lower/Lower.java @@ -192,6 +192,7 @@ private byte[] lower(SourceModuleInfo module, Set symbols, int majo /* signature= */ null, /* superClass= */ null, /* interfaces= */ ImmutableList.of(), + /* permits= */ ImmutableList.of(), /* methods= */ ImmutableList.of(), /* fields= */ ImmutableList.of(), annotations, @@ -259,6 +260,10 @@ private byte[] lower( for (ClassSymbol i : info.interfaces()) { interfaces.add(sig.descriptor(i)); } + List permits = new ArrayList<>(); + for (ClassSymbol i : info.permits()) { + permits.add(sig.descriptor(i)); + } ClassFile.RecordInfo record = null; if (info.kind().equals(TurbineTyKind.RECORD)) { @@ -310,6 +315,7 @@ record = new ClassFile.RecordInfo(components.build()); signature, superName, interfaces, + permits, methods, fields.build(), annotations, diff --git a/java/com/google/turbine/model/TurbineFlag.java b/java/com/google/turbine/model/TurbineFlag.java index c138d467..6e8d64bc 100644 --- a/java/com/google/turbine/model/TurbineFlag.java +++ b/java/com/google/turbine/model/TurbineFlag.java @@ -55,5 +55,8 @@ 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; + private TurbineFlag() {} } diff --git a/java/com/google/turbine/parse/Parser.java b/java/com/google/turbine/parse/Parser.java index 06b3db65..ed8958ee 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; @@ -187,12 +189,25 @@ public CompUnit compilationUnit() { { Ident ident = ident(); if (ident.value().equals("record")) { - ident = eatIdent(); + 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; @@ -216,6 +231,22 @@ public CompUnit compilationUnit() { } } + // 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(); @@ -255,6 +286,7 @@ private TyDecl recordDeclaration(EnumSet access, ImmutableList< typarams, Optional.empty(), interfaces.build(), + /* permits= */ ImmutableList.of(), members, formals.build(), TurbineTyKind.RECORD, @@ -279,6 +311,15 @@ private TyDecl interfaceDeclaration(EnumSet access, ImmutableLi interfaces.add(classty()); } while (maybe(Token.COMMA)); } + ImmutableList.Builder 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 members = classMembers(); eat(Token.RBRACE); @@ -290,6 +331,7 @@ private TyDecl interfaceDeclaration(EnumSet access, ImmutableLi typarams, Optional.empty(), interfaces.build(), + permits.build(), members, ImmutableList.of(), TurbineTyKind.INTERFACE, @@ -312,6 +354,7 @@ private TyDecl annotationDeclaration(EnumSet access, ImmutableL ImmutableList.of(), Optional.empty(), ImmutableList.of(), + ImmutableList.of(), members, ImmutableList.of(), TurbineTyKind.ANNOTATION, @@ -342,6 +385,7 @@ private TyDecl enumDeclaration(EnumSet access, ImmutableListof(), Optional.empty(), interfaces.build(), + ImmutableList.of(), members, ImmutableList.of(), TurbineTyKind.ENUM, @@ -569,6 +613,15 @@ private TyDecl classDeclaration(EnumSet access, ImmutableList 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(); @@ -588,6 +641,7 @@ private TyDecl classDeclaration(EnumSet access, ImmutableList classMembers() { 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(Token.IDENT); + eat(IDENT); acc.add(recordDeclaration(access, annos.build())); access = EnumSet.noneOf(TurbineModifier.class); annos = ImmutableList.builder(); @@ -758,13 +826,13 @@ private ImmutableList classMember( case IDENT: int pos = position; Ident ident = eatIdent(); - return classMemberIdent(access, annos, typaram, pos, ident); + return member(access, annos, typaram, pos, ident); default: throw error(token); } } - private ImmutableList classMemberIdent( + private ImmutableList member( EnumSet access, ImmutableList annos, ImmutableList typaram, @@ -820,6 +888,7 @@ private ImmutableList classMemberIdent( ImmutableList.of(), ImmutableList.of()); break; + default: throw error(token); } diff --git a/java/com/google/turbine/tree/Pretty.java b/java/com/google/turbine/tree/Pretty.java index 84b4ab5b..788ab58d 100644 --- a/java/com/google/turbine/tree/Pretty.java +++ b/java/com/google/turbine/tree/Pretty.java @@ -459,6 +459,17 @@ private void printAnnos(ImmutableList annos) { 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()) { @@ -522,6 +533,8 @@ private void printModifiers(ImmutableSet mods) { case TRANSIENT: case DEFAULT: case TRANSITIVE: + case SEALED: + case NON_SEALED: append(mod.toString()).append(' '); break; case ACC_SUPER: diff --git a/java/com/google/turbine/tree/Tree.java b/java/com/google/turbine/tree/Tree.java index ce326287..f7917b9b 100644 --- a/java/com/google/turbine/tree/Tree.java +++ b/java/com/google/turbine/tree/Tree.java @@ -934,6 +934,7 @@ public static class TyDecl extends Tree { private final ImmutableList typarams; private final Optional xtnds; private final ImmutableList impls; + private final ImmutableList permits; private final ImmutableList members; private final ImmutableList components; private final TurbineTyKind tykind; @@ -947,6 +948,7 @@ public TyDecl( ImmutableList typarams, Optional xtnds, ImmutableList impls, + ImmutableList permits, ImmutableList members, ImmutableList components, TurbineTyKind tykind, @@ -958,6 +960,7 @@ public TyDecl( this.typarams = typarams; this.xtnds = xtnds; this.impls = impls; + this.permits = permits; this.members = members; this.components = components; this.tykind = tykind; @@ -999,6 +1002,10 @@ public ImmutableList impls() { return impls; } + public ImmutableList permits() { + return permits; + } + public ImmutableList members() { return members; } diff --git a/java/com/google/turbine/tree/TurbineModifier.java b/java/com/google/turbine/tree/TurbineModifier.java index 35dc11c8..e5153b91 100644 --- a/java/com/google/turbine/tree/TurbineModifier.java +++ b/java/com/google/turbine/tree/TurbineModifier.java @@ -45,7 +45,9 @@ 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); private final int flag; @@ -59,6 +61,6 @@ public int flag() { @Override public String toString() { - return name().toLowerCase(ENGLISH); + return name().replace('_', '-').toLowerCase(ENGLISH); } } diff --git a/javatests/com/google/turbine/bytecode/ClassWriterTest.java b/javatests/com/google/turbine/bytecode/ClassWriterTest.java index 4ca5e363..a6f92342 100644 --- a/javatests/com/google/turbine/bytecode/ClassWriterTest.java +++ b/javatests/com/google/turbine/bytecode/ClassWriterTest.java @@ -186,6 +186,7 @@ public void record() { /* signature= */ null, /* superClass= */ "java/lang/Record", /* interfaces= */ ImmutableList.of(), + /* permits= */ ImmutableList.of(), /* methods= */ ImmutableList.of(), /* fields= */ ImmutableList.of(), /* annotations= */ ImmutableList.of(), @@ -240,6 +241,7 @@ public void nestHost() { /* signature= */ null, /* superClass= */ null, /* interfaces= */ ImmutableList.of(), + /* permits= */ ImmutableList.of(), /* methods= */ ImmutableList.of(), /* fields= */ ImmutableList.of(), /* annotations= */ ImmutableList.of(), diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java index 9e160750..db73ec0f 100644 --- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java +++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java @@ -44,7 +44,9 @@ public class LowerIntegrationTest { private static final ImmutableMap SOURCE_VERSION = - ImmutableMap.of("record.test", 16); + ImmutableMap.of( + "record.test", 16, + "sealed.test", 17); @Parameters(name = "{index}: {0}") public static Iterable parameters() { @@ -60,6 +62,7 @@ public static Iterable parameters() { "B8148131.test", "abstractenum.test", "access1.test", + "ambiguous_identifier.test", "anno_const_coerce.test", "anno_const_scope.test", "anno_nested.test", @@ -264,6 +267,7 @@ public static Iterable parameters() { "record.test", "rek.test", "samepkg.test", + "sealed.test", "self.test", "semi.test", // https://bugs.openjdk.java.net/browse/JDK-8054064 ? diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java index e560321b..c6792281 100644 --- a/javatests/com/google/turbine/lower/LowerTest.java +++ b/javatests/com/google/turbine/lower/LowerTest.java @@ -185,6 +185,7 @@ public void hello() throws Exception { SourceTypeBoundClass c = new SourceTypeBoundClass( interfaceTypes, + ImmutableList.of(), xtnds, tps, access, @@ -205,6 +206,7 @@ public void hello() throws Exception { SourceTypeBoundClass i = new SourceTypeBoundClass( + ImmutableList.of(), ImmutableList.of(), Type.ClassTy.OBJECT, ImmutableMap.of(), diff --git a/javatests/com/google/turbine/lower/testdata/ambiguous_identifier.test b/javatests/com/google/turbine/lower/testdata/ambiguous_identifier.test new file mode 100644 index 00000000..d7bbc546 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/ambiguous_identifier.test @@ -0,0 +1,14 @@ +=== Test.java === +class Test { + static final int non = 42; + static final int sealed = 1; + // here 'non-sealed' is a binary expression subtracting two identifiers, + // not a contextual hyphenated keyword + static final int x = non-sealed; +} + +// handle backtracking when we see 'non', but it isn't part of a contextualy +// hyphenated keyword 'non-sealed' +class non { + non self; +} diff --git a/javatests/com/google/turbine/lower/testdata/sealed.test b/javatests/com/google/turbine/lower/testdata/sealed.test new file mode 100644 index 00000000..0bac7b1c --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/sealed.test @@ -0,0 +1,11 @@ +=== Sealed.java === + +sealed class Sealed permits Sealed.Foo, Sealed.Bar { + static final class Foo extends Sealed {} + static final class Bar extends Sealed {} +} + +sealed interface ISealed permits ISealed.Foo, ISealed.Bar { + static final class Foo implements ISealed {} + static non-sealed class Bar implements ISealed {} +} diff --git a/javatests/com/google/turbine/parse/ParserIntegrationTest.java b/javatests/com/google/turbine/parse/ParserIntegrationTest.java index ade6bf09..2f37a844 100644 --- a/javatests/com/google/turbine/parse/ParserIntegrationTest.java +++ b/javatests/com/google/turbine/parse/ParserIntegrationTest.java @@ -76,6 +76,7 @@ public static Iterable parameters() { "type_annotations.input", "module-info.input", "record.input", + "sealed.input", }; return Iterables.transform( Arrays.asList(tests), diff --git a/javatests/com/google/turbine/parse/testdata/sealed.input b/javatests/com/google/turbine/parse/testdata/sealed.input new file mode 100644 index 00000000..5a277b85 --- /dev/null +++ b/javatests/com/google/turbine/parse/testdata/sealed.input @@ -0,0 +1,15 @@ +sealed class Sealed permits Sealed.Foo, Sealed.Bar { + static final class Foo extends Sealed { + } + + static final class Bar extends Sealed { + } +} + +sealed interface ISealed permits ISealed.Foo, ISealed.Bar { + static final class Foo implements ISealed { + } + + static non-sealed class Bar implements ISealed { + } +}