aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiam Miller-Cushon <cushon@google.com>2021-10-01 15:20:24 -0700
committerJavac Team <javac-team+copybara@google.com>2021-10-01 15:21:00 -0700
commit0b547dc65ba2628d4dc595e8395c6f4cd7652170 (patch)
treeda2d4068f949bacc1653178c8a5d7be241848d61
parent3452789854d581fbad3068d22d64b7319d19e8b4 (diff)
downloadturbine-0b547dc65ba2628d4dc595e8395c6f4cd7652170.tar.gz
Initial support for sealed classes
PiperOrigin-RevId: 400303125
-rw-r--r--java/com/google/turbine/binder/CanonicalTypeBinder.java1
-rw-r--r--java/com/google/turbine/binder/CompUnitPreprocessor.java1
-rw-r--r--java/com/google/turbine/binder/ConstBinder.java1
-rw-r--r--java/com/google/turbine/binder/DisambiguateTypeAnnotations.java1
-rw-r--r--java/com/google/turbine/binder/TypeBinder.java10
-rw-r--r--java/com/google/turbine/binder/bound/SourceTypeBoundClass.java8
-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/bytecode/Attribute.java17
-rw-r--r--java/com/google/turbine/bytecode/AttributeWriter.java13
-rw-r--r--java/com/google/turbine/bytecode/ClassFile.java8
-rw-r--r--java/com/google/turbine/bytecode/ClassReader.java1
-rw-r--r--java/com/google/turbine/bytecode/LowerAttributes.java3
-rw-r--r--java/com/google/turbine/deps/Transitive.java1
-rw-r--r--java/com/google/turbine/lower/Lower.java6
-rw-r--r--java/com/google/turbine/model/TurbineFlag.java3
-rw-r--r--java/com/google/turbine/parse/Parser.java77
-rw-r--r--java/com/google/turbine/tree/Pretty.java13
-rw-r--r--java/com/google/turbine/tree/Tree.java7
-rw-r--r--java/com/google/turbine/tree/TurbineModifier.java6
-rw-r--r--javatests/com/google/turbine/bytecode/ClassWriterTest.java2
-rw-r--r--javatests/com/google/turbine/lower/LowerIntegrationTest.java6
-rw-r--r--javatests/com/google/turbine/lower/LowerTest.java2
-rw-r--r--javatests/com/google/turbine/lower/testdata/ambiguous_identifier.test14
-rw-r--r--javatests/com/google/turbine/lower/testdata/sealed.test11
-rw-r--r--javatests/com/google/turbine/parse/ParserIntegrationTest.java1
-rw-r--r--javatests/com/google/turbine/parse/testdata/sealed.input15
27 files changed, 229 insertions, 8 deletions
diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java
index cbdc025..944563f 100644
--- a/java/com/google/turbine/binder/CanonicalTypeBinder.java
+++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java
@@ -67,6 +67,7 @@ public final class CanonicalTypeBinder {
ImmutableList<FieldInfo> 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 3323938..0e9f4e5 100644
--- a/java/com/google/turbine/binder/CompUnitPreprocessor.java
+++ b/java/com/google/turbine/binder/CompUnitPreprocessor.java
@@ -225,6 +225,7 @@ public final class CompUnitPreprocessor {
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 232370c..3ba059f 100644
--- a/java/com/google/turbine/binder/ConstBinder.java
+++ b/java/com/google/turbine/binder/ConstBinder.java
@@ -110,6 +110,7 @@ public class ConstBinder {
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(),
diff --git a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
index 4929962..65c1021 100644
--- a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
+++ b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
@@ -71,6 +71,7 @@ public final class DisambiguateTypeAnnotations {
SourceTypeBoundClass base, Env<ClassSymbol, TypeBoundClass> 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 f4b4849..ccc0ad3 100644
--- a/java/com/google/turbine/binder/TypeBinder.java
+++ b/java/com/google/turbine/binder/TypeBinder.java
@@ -229,6 +229,15 @@ 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))
@@ -251,6 +260,7 @@ public class TypeBinder {
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 fb3ee27..5e9817e 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<ClassSymbol> permits;
private final ImmutableList<RecordComponentInfo> components;
private final ImmutableList<MethodInfo> methods;
private final ImmutableList<FieldInfo> fields;
@@ -57,6 +58,7 @@ public class SourceTypeBoundClass implements TypeBoundClass {
public SourceTypeBoundClass(
ImmutableList<Type> interfaceTypes,
+ ImmutableList<ClassSymbol> permits,
@Nullable Type superClassType,
ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes,
int access,
@@ -75,6 +77,7 @@ public class SourceTypeBoundClass implements TypeBoundClass {
SourceFile source,
Tree.TyDecl decl) {
this.interfaceTypes = interfaceTypes;
+ this.permits = permits;
this.superClassType = superClassType;
this.typeParameterTypes = typeParameterTypes;
this.access = access;
@@ -117,6 +120,11 @@ public class SourceTypeBoundClass implements TypeBoundClass {
}
@Override
+ public ImmutableList<ClassSymbol> 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 cc28901..8321bde 100644
--- a/java/com/google/turbine/binder/bound/TypeBoundClass.java
+++ b/java/com/google/turbine/binder/bound/TypeBoundClass.java
@@ -18,6 +18,7 @@ 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;
@@ -43,6 +44,9 @@ public interface TypeBoundClass extends HeaderBoundClass {
/** Implemented interface types. */
ImmutableList<Type> interfaceTypes();
+ /** The permitted direct subclasses. */
+ ImmutableList<ClassSymbol> permits();
+
ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes();
/** Declared fields. */
diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
index 516a027..9be2dd1 100644
--- a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
+++ b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
@@ -306,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>>() {
diff --git a/java/com/google/turbine/bytecode/Attribute.java b/java/com/google/turbine/bytecode/Attribute.java
index 581de3d..ad6ffc1 100644
--- a/java/com/google/turbine/bytecode/Attribute.java
+++ b/java/com/google/turbine/bytecode/Attribute.java
@@ -45,7 +45,8 @@ interface Attribute {
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 @@ interface Attribute {
}
}
+ /** 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 ed7b2ab..6aac19a 100644
--- a/java/com/google/turbine/bytecode/AttributeWriter.java
+++ b/java/com/google/turbine/bytecode/AttributeWriter.java
@@ -95,6 +95,9 @@ public class AttributeWriter {
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 @@ public class AttributeWriter {
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 124e03c..820f17d 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<String> interfaces;
+ private final List<String> permits;
private final List<MethodInfo> methods;
private final List<FieldInfo> fields;
private final List<AnnotationInfo> annotations;
@@ -55,6 +56,7 @@ public class ClassFile {
@Nullable String signature,
@Nullable String superClass,
List<String> interfaces,
+ List<String> permits,
List<MethodInfo> methods,
List<FieldInfo> fields,
List<AnnotationInfo> annotations,
@@ -71,6 +73,7 @@ public class 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 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;
diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java
index b429032..0f08fc3 100644
--- a/java/com/google/turbine/bytecode/ClassReader.java
+++ b/java/com/google/turbine/bytecode/ClassReader.java
@@ -143,6 +143,7 @@ public class ClassReader {
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 54937fc..8952dff 100644
--- a/java/com/google/turbine/bytecode/LowerAttributes.java
+++ b/java/com/google/turbine/bytecode/LowerAttributes.java
@@ -54,6 +54,9 @@ public final class LowerAttributes {
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 62d15c3..fa3d475 100644
--- a/java/com/google/turbine/deps/Transitive.java
+++ b/java/com/google/turbine/deps/Transitive.java
@@ -95,6 +95,7 @@ public final class Transitive {
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 0f3eb24..ff7e13c 100644
--- a/java/com/google/turbine/lower/Lower.java
+++ b/java/com/google/turbine/lower/Lower.java
@@ -192,6 +192,7 @@ public class Lower {
/* signature= */ null,
/* superClass= */ null,
/* interfaces= */ ImmutableList.of(),
+ /* permits= */ ImmutableList.of(),
/* methods= */ ImmutableList.of(),
/* fields= */ ImmutableList.of(),
annotations,
@@ -259,6 +260,10 @@ 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)) {
@@ -310,6 +315,7 @@ public class Lower {
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 c138d46..6e8d64b 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 06b3db6..ed8958e 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 class Parser {
{
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 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();
@@ -255,6 +286,7 @@ public class Parser {
typarams,
Optional.<ClassTy>empty(),
interfaces.build(),
+ /* permits= */ ImmutableList.of(),
members,
formals.build(),
TurbineTyKind.RECORD,
@@ -279,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);
@@ -290,6 +331,7 @@ public class Parser {
typarams,
Optional.<ClassTy>empty(),
interfaces.build(),
+ permits.build(),
members,
ImmutableList.of(),
TurbineTyKind.INTERFACE,
@@ -312,6 +354,7 @@ public class Parser {
ImmutableList.<TyParam>of(),
Optional.<ClassTy>empty(),
ImmutableList.<ClassTy>of(),
+ ImmutableList.of(),
members,
ImmutableList.of(),
TurbineTyKind.ANNOTATION,
@@ -342,6 +385,7 @@ public class Parser {
ImmutableList.<TyParam>of(),
Optional.<ClassTy>empty(),
interfaces.build(),
+ ImmutableList.of(),
members,
ImmutableList.of(),
TurbineTyKind.ENUM,
@@ -569,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();
@@ -588,6 +641,7 @@ public class Parser {
tyParams,
Optional.ofNullable(xtnds),
interfaces.build(),
+ permits.build(),
members,
ImmutableList.of(),
TurbineTyKind.CLASS,
@@ -665,8 +719,22 @@ 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(Token.IDENT);
+ eat(IDENT);
acc.add(recordDeclaration(access, annos.build()));
access = EnumSet.noneOf(TurbineModifier.class);
annos = ImmutableList.builder();
@@ -758,13 +826,13 @@ public class Parser {
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<Tree> classMemberIdent(
+ private ImmutableList<Tree> member(
EnumSet<TurbineModifier> access,
ImmutableList<Anno> annos,
ImmutableList<TyParam> typaram,
@@ -820,6 +888,7 @@ public class Parser {
ImmutableList.<Type>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 84b4ab5..788ab58 100644
--- a/java/com/google/turbine/tree/Pretty.java
+++ b/java/com/google/turbine/tree/Pretty.java
@@ -459,6 +459,17 @@ public class Pretty implements Tree.Visitor<@Nullable Void, @Nullable 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()) {
@@ -522,6 +533,8 @@ public class Pretty implements Tree.Visitor<@Nullable Void, @Nullable Void> {
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 ce32628..f7917b9 100644
--- a/java/com/google/turbine/tree/Tree.java
+++ b/java/com/google/turbine/tree/Tree.java
@@ -934,6 +934,7 @@ 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;
@@ -947,6 +948,7 @@ public abstract class Tree {
ImmutableList<TyParam> typarams,
Optional<ClassTy> xtnds,
ImmutableList<ClassTy> impls,
+ ImmutableList<ClassTy> permits,
ImmutableList<Tree> members,
ImmutableList<VarDecl> components,
TurbineTyKind tykind,
@@ -958,6 +960,7 @@ 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;
@@ -999,6 +1002,10 @@ public abstract class Tree {
return impls;
}
+ public ImmutableList<ClassTy> permits() {
+ return permits;
+ }
+
public ImmutableList<Tree> members() {
return members;
}
diff --git a/java/com/google/turbine/tree/TurbineModifier.java b/java/com/google/turbine/tree/TurbineModifier.java
index 35dc11c..e5153b9 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 enum TurbineModifier {
@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 4ca5e36..a6f9234 100644
--- a/javatests/com/google/turbine/bytecode/ClassWriterTest.java
+++ b/javatests/com/google/turbine/bytecode/ClassWriterTest.java
@@ -186,6 +186,7 @@ public class ClassWriterTest {
/* 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 class ClassWriterTest {
/* 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 9e16075..db73ec0 100644
--- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
@@ -44,7 +44,9 @@ import org.junit.runners.Parameterized.Parameters;
public class LowerIntegrationTest {
private static final ImmutableMap<String, Integer> SOURCE_VERSION =
- ImmutableMap.of("record.test", 16);
+ ImmutableMap.of(
+ "record.test", 16,
+ "sealed.test", 17);
@Parameters(name = "{index}: {0}")
public static Iterable<Object[]> parameters() {
@@ -60,6 +62,7 @@ public class LowerIntegrationTest {
"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 class LowerIntegrationTest {
"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 e560321..c679228 100644
--- a/javatests/com/google/turbine/lower/LowerTest.java
+++ b/javatests/com/google/turbine/lower/LowerTest.java
@@ -185,6 +185,7 @@ public class LowerTest {
SourceTypeBoundClass c =
new SourceTypeBoundClass(
interfaceTypes,
+ ImmutableList.of(),
xtnds,
tps,
access,
@@ -206,6 +207,7 @@ public class LowerTest {
SourceTypeBoundClass i =
new SourceTypeBoundClass(
ImmutableList.of(),
+ ImmutableList.of(),
Type.ClassTy.OBJECT,
ImmutableMap.of(),
TurbineFlag.ACC_STATIC | TurbineFlag.ACC_PROTECTED,
diff --git a/javatests/com/google/turbine/lower/testdata/ambiguous_identifier.test b/javatests/com/google/turbine/lower/testdata/ambiguous_identifier.test
new file mode 100644
index 0000000..d7bbc54
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/ambiguous_identifier.test
@@ -0,0 +1,14 @@
+=== Test.java ===
+class Test {
+ static final int non = 42;
+ static final int sealed = 1;
+ // here 'non-sealed' is a binary expression subtracting two identifiers,
+ // not a contextual hyphenated keyword
+ static final int x = non-sealed;
+}
+
+// handle backtracking when we see 'non', but it isn't part of a contextualy
+// hyphenated keyword 'non-sealed'
+class non {
+ non self;
+}
diff --git a/javatests/com/google/turbine/lower/testdata/sealed.test b/javatests/com/google/turbine/lower/testdata/sealed.test
new file mode 100644
index 0000000..0bac7b1
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/sealed.test
@@ -0,0 +1,11 @@
+=== Sealed.java ===
+
+sealed class Sealed permits Sealed.Foo, Sealed.Bar {
+ static final class Foo extends Sealed {}
+ static final class Bar extends Sealed {}
+}
+
+sealed interface ISealed permits ISealed.Foo, ISealed.Bar {
+ static final class Foo implements ISealed {}
+ static non-sealed class Bar implements ISealed {}
+}
diff --git a/javatests/com/google/turbine/parse/ParserIntegrationTest.java b/javatests/com/google/turbine/parse/ParserIntegrationTest.java
index ade6bf0..2f37a84 100644
--- a/javatests/com/google/turbine/parse/ParserIntegrationTest.java
+++ b/javatests/com/google/turbine/parse/ParserIntegrationTest.java
@@ -76,6 +76,7 @@ public class ParserIntegrationTest {
"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 0000000..5a277b8
--- /dev/null
+++ b/javatests/com/google/turbine/parse/testdata/sealed.input
@@ -0,0 +1,15 @@
+sealed class Sealed permits Sealed.Foo, Sealed.Bar {
+ static final class Foo extends Sealed {
+ }
+
+ static final class Bar extends Sealed {
+ }
+}
+
+sealed interface ISealed permits ISealed.Foo, ISealed.Bar {
+ static final class Foo implements ISealed {
+ }
+
+ static non-sealed class Bar implements ISealed {
+ }
+}