Skip to content

Commit

Permalink
8303820: Simplify type metadata
Browse files Browse the repository at this point in the history
Reviewed-by: vromero
  • Loading branch information
mcimadamore committed Mar 10, 2023
1 parent 75d6306 commit b9951dd
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 317 deletions.
226 changes: 92 additions & 134 deletions src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -25,181 +25,55 @@

package com.sun.tools.javac.code;

import com.sun.tools.javac.util.Assert;
import com.sun.tools.javac.util.List;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Set;
import com.sun.tools.javac.util.ListBuffer;

/**
* TypeMetadata is essentially an immutable {@code EnumMap<Entry.Kind, <? extends Entry>>}
*
* A metadata class represented by a subtype of Entry can express a property on a Type instance.
* There should be at most one instance of an Entry per Entry.Kind on any given Type instance.
*
* Metadata classes of a specific kind are responsible for how they combine themselves.
*
* @implNote {@code Entry:combine} need not be commutative.
* A type metadata is an object that can be stapled on a type. This is typically done using
* {@link Type#addMetadata(TypeMetadata)}. Metadata associated to a type can also be removed,
* typically using {@link Type#dropMetadata(Class)}. To drop <em>all</em> metadata from a given type,
* the {@link Type#baseType()} method can also be used. This can be useful when comparing two
* using reference equality (see also {@link Type#equalsIgnoreMetadata(Type)}).
* <p>
* There are no constraints on how a type metadata should be defined. Typically, a type
* metadata will be defined as a small record, storing additional information (see {@link ConstantValue}).
* In other cases, type metadata can be mutable and support complex state transitions
* (see {@link Annotations}).
* <p>
* The only invariant the implementation requires is that there must be <em>one</em> metadata
* of a given kind attached to a type, as this makes accessing and dropping metadata simpler.
* If clients wish to store multiple metadata values that are logically related, they should
* define a metadata type that collects such values in e.g. a list.
*/
public class TypeMetadata {
public static final TypeMetadata EMPTY = new TypeMetadata();

private final EnumMap<Entry.Kind, Entry> contents;

/**
* Create a new empty TypeMetadata map.
*/
private TypeMetadata() {
contents = new EnumMap<>(Entry.Kind.class);
}

/**
* Create a new TypeMetadata map containing the Entry {@code elem}.
*
* @param elem the sole contents of this map
*/
public TypeMetadata(Entry elem) {
this();
Assert.checkNonNull(elem);
contents.put(elem.kind(), elem);
}

/**
* Creates a copy of TypeMetadata {@code other} with a shallow copy the other's metadata contents.
*
* @param other the TypeMetadata to copy contents from.
*/
public TypeMetadata(TypeMetadata other) {
Assert.checkNonNull(other);
contents = other.contents.clone();
}
public sealed interface TypeMetadata {

/**
* Return a copy of this TypeMetadata with the metadata entry for {@code elem.kind()} combined
* with {@code elem}.
*
* @param elem the new value
* @return a new TypeMetadata updated with {@code Entry elem}
* A type metadata object holding type annotations. This metadata needs to be mutable,
* because type annotations are sometimes set in two steps. That is, a type can be created with
* an empty set of annotations (e.g. during member enter). At some point later, the type
* is then updated to contain the correct annotations. At this point we need to augment
* the existing type (rather than creating a new one), as the type might already have been
* saved inside other symbols.
*/
public TypeMetadata combine(Entry elem) {
Assert.checkNonNull(elem);
record Annotations(ListBuffer<Attribute.TypeCompound> annotationBuffer) implements TypeMetadata {

TypeMetadata out = new TypeMetadata(this);
Entry.Kind key = elem.kind();
if (contents.containsKey(key)) {
out.add(key, this.contents.get(key).combine(elem));
} else {
out.add(key, elem);
Annotations() {
this(new ListBuffer<>());
}
return out;
}

/**
* Return a copy of this TypeMetadata with the metadata entry for all kinds from {@code other}
* combined with the same kind from this.
*
* @param other the TypeMetadata to combine with this
* @return a new TypeMetadata updated with all entries from {@code other}
*/
public TypeMetadata combineAll(TypeMetadata other) {
Assert.checkNonNull(other);

TypeMetadata out = new TypeMetadata();
Set<Entry.Kind> keys = new HashSet<>(contents.keySet());
keys.addAll(other.contents.keySet());

for(Entry.Kind key : keys) {
if (contents.containsKey(key)) {
if (other.contents.containsKey(key)) {
out.add(key, contents.get(key).combine(other.contents.get(key)));
} else {
out.add(key, contents.get(key));
}
} else if (other.contents.containsKey(key)) {
out.add(key, other.contents.get(key));
}
Annotations(List<Attribute.TypeCompound> annotations) {
this();
annotationBuffer.appendList(annotations);
}
return out;
}

/**
* Return a TypeMetadata with the metadata entry for {@code kind} removed.
*
* This may be the same instance or a new TypeMetadata.
*
* @param kind the {@code Kind} to remove metadata for
* @return a new TypeMetadata without {@code Kind kind}
*/
public TypeMetadata without(Entry.Kind kind) {
if (this == EMPTY || contents.get(kind) == null)
return this;

TypeMetadata out = new TypeMetadata(this);
out.contents.remove(kind);
return out.contents.isEmpty() ? EMPTY : out;
}

public Entry get(Entry.Kind kind) {
return contents.get(kind);
}

private void add(Entry.Kind kind, Entry elem) {
contents.put(kind, elem);
}

public interface Entry {

public enum Kind {
ANNOTATIONS
List<Attribute.TypeCompound> annotations() {
return annotationBuffer.toList();
}

/**
* Get the kind of metadata this object represents
*/
public Kind kind();

/**
* Combine this type metadata with another metadata of the
* same kind.
*
* @param other The metadata with which to combine this one.
* @return The combined metadata.
*/
public Entry combine(Entry other);
}

/**
* A type metadata object holding type annotations.
* A type metadata holding a constant value. This can be used to describe constant types,
* such as the type of a string literal, or that of a numeric constant.
*/
public static class Annotations implements Entry {
private List<Attribute.TypeCompound> annos;

public static final List<Attribute.TypeCompound> TO_BE_SET = List.nil();

public Annotations(List<Attribute.TypeCompound> annos) {
this.annos = annos;
}

/**
* Get the type annotations contained in this metadata.
*
* @return The annotations.
*/
public List<Attribute.TypeCompound> getAnnotations() {
return annos;
}

@Override
public Annotations combine(Entry other) {
Assert.check(annos == TO_BE_SET);
annos = ((Annotations)other).annos;
return this;
}

@Override
public Kind kind() { return Kind.ANNOTATIONS; }

@Override
public String toString() { return "ANNOTATIONS [ " + annos + " ]"; }
}
record ConstantValue(Object value) implements TypeMetadata { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
import com.sun.tools.javac.code.Lint.LintCategory;
import com.sun.tools.javac.code.Source.Feature;
import com.sun.tools.javac.code.Type.UndetVar.InferenceBound;
import com.sun.tools.javac.code.TypeMetadata.Entry.Kind;
import com.sun.tools.javac.code.TypeMetadata.Annotations;
import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.Check;
import com.sun.tools.javac.comp.Enter;
Expand Down Expand Up @@ -2399,7 +2399,7 @@ private Type erasure(Type t, boolean recurse) {
private TypeMapping<Boolean> erasure = new StructuralTypeMapping<Boolean>() {
private Type combineMetadata(final Type s,
final Type t) {
if (t.getMetadata() != TypeMetadata.EMPTY) {
if (t.getMetadata().nonEmpty()) {
switch (s.getKind()) {
case OTHER:
case UNION:
Expand All @@ -2410,7 +2410,7 @@ private Type combineMetadata(final Type s,
case VOID:
case ERROR:
return s;
default: return s.cloneWithMetadata(s.getMetadata().without(Kind.ANNOTATIONS));
default: return s.dropMetadata(Annotations.class);
}
} else {
return s;
Expand All @@ -2437,7 +2437,7 @@ public Type visitClassType(ClassType t, Boolean recurse) {
Type erased = t.tsym.erasure(Types.this);
if (recurse) {
erased = new ErasedClassType(erased.getEnclosingType(),erased.tsym,
t.getMetadata().without(Kind.ANNOTATIONS));
t.dropMetadata(Annotations.class).getMetadata());
return erased;
} else {
return combineMetadata(erased, t);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import com.sun.tools.javac.code.Scope.WriteableScope;
import com.sun.tools.javac.code.Source.Feature;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.TypeMetadata.Entry.Kind;
import com.sun.tools.javac.code.TypeMetadata.Annotations;
import com.sun.tools.javac.comp.Check.CheckContext;
import com.sun.tools.javac.resources.CompilerProperties.Errors;
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
Expand Down Expand Up @@ -1049,7 +1049,10 @@ public void annotateTypeSecondStage(JCTree tree, List<JCAnnotation> annotations,
typeAnnotation(() -> {
List<Attribute.TypeCompound> compounds = fromAnnotations(annotations);
Assert.check(annotations.size() == compounds.size());
storeAt.getMetadataOfKind(Kind.ANNOTATIONS).combine(new TypeMetadata.Annotations(compounds));
// the type already has annotation metadata, but it's empty
Annotations metadata = storeAt.getMetadata(Annotations.class).orElseThrow(AssertionError::new);
Assert.check(metadata.annotationBuffer().isEmpty());
metadata.annotationBuffer().appendList(compounds);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
import com.sun.tools.javac.code.Source.Feature;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.Type.*;
import com.sun.tools.javac.code.TypeMetadata.Annotations;
import com.sun.tools.javac.code.Types.FunctionDescriptorLookupError;
import com.sun.tools.javac.comp.ArgumentAttr.LocalCacheContext;
import com.sun.tools.javac.comp.Check.CheckContext;
Expand Down Expand Up @@ -5210,7 +5209,7 @@ public void visitModifiers(JCModifiers tree) {
public void visitAnnotatedType(JCAnnotatedType tree) {
attribAnnotationTypes(tree.annotations, env);
Type underlyingType = attribType(tree.underlyingType, env);
Type annotatedType = underlyingType.annotatedType(Annotations.TO_BE_SET);
Type annotatedType = underlyingType.preannotatedType();

if (!env.info.isNewClass)
annotate.annotateTypeSecondStage(tree, tree.annotations, annotatedType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,17 +221,12 @@ public class DeferredType extends Type {
SpeculativeCache speculativeCache;

DeferredType(JCExpression tree, Env<AttrContext> env) {
super(null, TypeMetadata.EMPTY);
super(null, List.nil());
this.tree = tree;
this.env = attr.copyEnv(env);
this.speculativeCache = new SpeculativeCache();
}

@Override
public DeferredType cloneWithMetadata(TypeMetadata md) {
throw new AssertionError("Cannot add metadata to a deferred type");
}

@Override
public TypeTag getTag() {
return DEFERRED;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2925,7 +2925,7 @@ private class ProxyType extends Type {
private final Name name;

public ProxyType(int index) {
super(syms.noSymbol, TypeMetadata.EMPTY);
super(syms.noSymbol, List.nil());
this.name = poolReader.getName(index);
}

Expand All @@ -2934,11 +2934,6 @@ public TypeTag getTag() {
return TypeTag.NONE;
}

@Override
public Type cloneWithMetadata(TypeMetadata metadata) {
throw new UnsupportedOperationException();
}

public Type resolve() {
return name.map(ClassReader.this::sigToType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ public static UninitializedType uninitializedObject(Type qtype, int offset) {

public final int offset; // PC where allocation took place
private UninitializedType(TypeTag tag, Type qtype, int offset,
TypeMetadata metadata) {
List<TypeMetadata> metadata) {
super(tag, qtype, metadata);
this.offset = offset;
}

@Override
public UninitializedType cloneWithMetadata(final TypeMetadata md) {
protected UninitializedType cloneWithMetadata(final List<TypeMetadata> md) {
return new UninitializedType(tag, qtype, offset, md);
}

Expand Down

1 comment on commit b9951dd

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.