Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1579,7 +1579,7 @@ public AnnotatedTypeMirror getAnnotatedType(Element elt) {
if (useCache) {
AnnotatedTypeMirror cached = elementTypeCache.get(elt);
if (cached != null) {
return cached.deepCopy();
return AnnotatedTypeMirror.COW ? cached.cowCopy() : cached.deepCopy();
}
}
// Annotations explicitly written in the source code,
Expand Down Expand Up @@ -1831,7 +1831,7 @@ public AnnotatedTypeMirror fromElement(Element elt) {
if (shouldCache) {
AnnotatedTypeMirror cached = elementCache.get(elt);
if (cached != null) {
return cached.deepCopy();
return AnnotatedTypeMirror.COW ? cached.cowCopy() : cached.deepCopy();
}
}
if (elt.getKind() == ElementKind.PACKAGE) {
Expand Down Expand Up @@ -1950,7 +1950,7 @@ private AnnotatedTypeMirror fromMember(Tree tree) {
if (shouldCache) {
AnnotatedTypeMirror cached = fromMemberTreeCache.get(tree);
if (cached != null) {
return cached.deepCopy();
return AnnotatedTypeMirror.COW ? cached.cowCopy() : cached.deepCopy();
}
}
AnnotatedTypeMirror result = TypeFromTree.fromMember(this, tree);
Expand Down Expand Up @@ -2046,7 +2046,7 @@ private AnnotatedTypeMirror fromExpression(ExpressionTree tree) {
if (shouldCache) {
AnnotatedTypeMirror cached = fromExpressionTreeCache.get(tree);
if (cached != null) {
return cached.deepCopy();
return AnnotatedTypeMirror.COW ? cached.cowCopy() : cached.deepCopy();
}
}

Expand Down Expand Up @@ -2079,7 +2079,7 @@ private AnnotatedTypeMirror fromExpression(ExpressionTree tree) {
if (shouldCache) {
AnnotatedTypeMirror cached = fromTypeTreeCache.get(tree);
if (cached != null) {
return cached.deepCopy();
return AnnotatedTypeMirror.COW ? cached.cowCopy() : cached.deepCopy();
}
}

Expand Down Expand Up @@ -2850,7 +2850,10 @@ && shouldCacheMethodAsMemberOf()
AnnotatedExecutableType cachedMethodType =
cacheKey == null ? null : methodAsMemberOfCache.get(cacheKey);
if (cachedMethodType != null) {
methodType = cachedMethodType.deepCopy();
methodType =
AnnotatedTypeMirror.COW
? (AnnotatedExecutableType) cachedMethodType.cowCopy()
: cachedMethodType.deepCopy();
} else {
methodType = computeMethodTypeAsMemberOf(tree, methodElt, receiverType, inferTypeArgs);
if (cacheKey != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,84 @@ protected final void checkMutable() {
}
}

/**
* Experimental copy-on-write toggle ({@code -Dcf.cow}). When on, post-pipeline caches hand out
* a {@link #shallowCopy()} (a fresh, non-frozen root that shares the master's frozen children)
* instead of a {@link #deepCopy()}, and the child accessors below lazily unshare a frozen child
* of a non-frozen parent ({@link #cowChild}/{@link #cowChildren}). Mutation therefore copies
* only the spine it touches; read-only hits copy one node. Off by default (behavior unchanged).
*/
static final boolean COW = Boolean.getBoolean("cf.cow");

/**
* COW fast-path flag: true only for a type that may transitively hold a frozen (shared) child —
* i.e. a {@link #cowCopy()} of a frozen cache master, or a child unshared from one. A type
* without this flag was built fresh and has no frozen children, so the COW accessors can skip
* the per-call scan entirely. This keeps {@link #cowChild}/{@link #cowChildren} off the hot
* path for the overwhelming majority of (non-cache) types.
*/
private boolean cowDirty = false;

/**
* Returns a non-frozen shallow copy of this (frozen) type, marked {@link #cowDirty} because it
* shares this type's frozen children. Used at cache boundaries and by the COW accessors.
*
* @return a cow-dirty shallow copy of this type
*/
final AnnotatedTypeMirror cowCopy() {
AnnotatedTypeMirror c = shallowCopy();
c.cowDirty = true;
return c;
}

/** Whether COW unsharing might be needed for this type's children (cheap hot-path gate). */
final boolean cowActive() {
return COW && cowDirty;
}

/**
* Copy-on-write a single child component. If COW is on and {@code this} is non-frozen but
* {@code child} is frozen (shared from a cache master), returns a fresh non-frozen shallow copy
* that the caller stores back into its field; otherwise returns {@code child} unchanged.
*
* @param <T> the static type of the child
* @param child a child component of this type, or null
* @return an unshared shallow copy when COW must unshare, else {@code child}
*/
@SuppressWarnings("unchecked")
final <T extends AnnotatedTypeMirror> @Nullable T cowChild(@Nullable T child) {
if (COW && cowDirty && !this.frozen && child != null && child.isFrozen()) {
return (T) child.cowCopy();
}
return child;
}

/**
* Copy-on-write a list of child components. Returns a new list with any frozen elements
* replaced by unshared shallow copies (when COW is on and {@code this} is non-frozen);
* otherwise returns {@code children} unchanged.
*
* @param children a list of child components
* @return a list with frozen elements unshared, or {@code children} unchanged
*/
@SuppressWarnings("unchecked")
final <T extends AnnotatedTypeMirror> List<T> cowChildren(List<T> children) {
if (!COW || !cowDirty || this.frozen || children.isEmpty()) {
return children;
}
List<T> result = null;
for (int i = 0; i < children.size(); i++) {
T c = children.get(i);
if (c != null && c.isFrozen()) {
if (result == null) {
result = new ArrayList<>(children);
}
result.set(i, (T) c.cowCopy());
}
}
return result == null ? children : result;
}

/**
* Returns true if this type has been {@linkplain #freeze() frozen}.
*
Expand Down Expand Up @@ -1298,6 +1376,7 @@ public void setTypeArguments(List<? extends AnnotatedTypeMirror> ts) {
*/
public List<AnnotatedTypeMirror> getTypeArguments() {
if (typeArgs != null) {
if (cowActive()) typeArgs = cowChildren(typeArgs);
return typeArgs;
}

Expand Down Expand Up @@ -1459,6 +1538,7 @@ public void setEnclosingType(@Nullable AnnotatedDeclaredType enclosingType) {
* @return enclosingType the enclosing type, or null if this is a top-level type
*/
public @Nullable AnnotatedDeclaredType getEnclosingType() {
if (cowActive()) enclosingType = cowChild(enclosingType);
return enclosingType;
}

Expand Down Expand Up @@ -1652,6 +1732,7 @@ public List<AnnotatedTypeMirror> getParameterTypes() {
freezeLazyComponents(paramTypes);
}
// No need to copy or wrap; it is an unmodifiable list.
if (cowActive()) paramTypes = cowChildren(paramTypes);
return paramTypes;
}

Expand Down Expand Up @@ -1761,6 +1842,7 @@ public AnnotatedTypeMirror getReturnType() {
returnTypeComputed = true;
freezeLazyComponent(returnType);
}
if (cowActive()) returnType = cowChild(returnType);
return returnType;
}

Expand Down Expand Up @@ -1811,6 +1893,7 @@ public AnnotatedTypeMirror getReturnType() {
receiverTypeComputed = true;
freezeLazyComponent(receiverType);
}
if (cowActive()) receiverType = cowChild(receiverType);
return receiverType;
}

Expand Down Expand Up @@ -1858,6 +1941,7 @@ public List<AnnotatedTypeMirror> getThrownTypes() {
freezeLazyComponents(thrownTypes);
}
// No need to copy or wrap; it is an unmodifiable list.
if (cowActive()) thrownTypes = cowChildren(thrownTypes);
return thrownTypes;
}

Expand Down Expand Up @@ -1907,6 +1991,7 @@ public List<AnnotatedTypeVariable> getTypeVariables() {
freezeLazyComponents(typeVarTypes);
}
// No need to copy or wrap; it is an unmodifiable list.
if (cowActive()) typeVarTypes = cowChildren(typeVarTypes);
return typeVarTypes;
}

Expand Down Expand Up @@ -2077,6 +2162,7 @@ public AnnotatedTypeMirror getComponentType() {
false));
freezeLazyComponent(componentType);
}
if (cowActive()) componentType = cowChild(componentType);
return componentType;
}

Expand Down Expand Up @@ -2272,6 +2358,7 @@ public AnnotatedTypeMirror getLowerBound() {
freezeLazyComponent(lowerBound);
freezeLazyComponent(upperBound);
}
if (cowActive()) lowerBound = cowChild(lowerBound);
return lowerBound;
}

Expand All @@ -2291,6 +2378,7 @@ public AnnotatedTypeMirror getLowerBound() {
private void fixupBoundAnnotations() {
if (!this.getAnnotationsField().isEmpty()) {
AnnotationMirrorSet newAnnos = this.getAnnotationsField();
if (cowActive()) upperBound = cowChild(upperBound);
if (upperBound != null) {
upperBound.replaceAnnotations(newAnnos);
}
Expand All @@ -2301,6 +2389,7 @@ private void fixupBoundAnnotations() {
// propagate the primary annotation to the type variable because primary annotations
// overwrite the upper and lower bounds of type variables when
// getUpperBound/getLowerBound is called.
if (cowActive()) lowerBound = cowChild(lowerBound);
if (lowerBound != null) {
lowerBound.replaceAnnotations(newAnnos);
}
Expand Down Expand Up @@ -2345,6 +2434,7 @@ public AnnotatedTypeMirror getUpperBound() {
freezeLazyComponent(upperBound);
freezeLazyComponent(lowerBound);
}
if (cowActive()) upperBound = cowChild(upperBound);
return upperBound;
}

Expand Down Expand Up @@ -2660,6 +2750,7 @@ public AnnotatedTypeMirror getSuperBound() {
freezeLazyComponent(superBound);
freezeLazyComponent(extendsBound);
}
if (cowActive()) this.superBound = cowChild(this.superBound);
return this.superBound;
}

Expand Down Expand Up @@ -2694,6 +2785,7 @@ public AnnotatedTypeMirror getExtendsBound() {
freezeLazyComponent(extendsBound);
freezeLazyComponent(superBound);
}
if (cowActive()) this.extendsBound = cowChild(this.extendsBound);
return this.extendsBound;
}

Expand All @@ -2714,9 +2806,11 @@ void freezeComponents() {
*/
private void fixupBoundAnnotations() {
if (!this.getAnnotationsField().isEmpty()) {
if (cowActive()) superBound = cowChild(superBound);
if (superBound != null) {
superBound.replaceAnnotations(this.getAnnotationsField());
}
if (cowActive()) extendsBound = cowChild(extendsBound);
if (extendsBound != null) {
extendsBound.replaceAnnotations(this.getAnnotationsField());
}
Expand Down Expand Up @@ -2864,6 +2958,7 @@ public void addAnnotation(AnnotationMirror annotation) {
public boolean removeAnnotation(AnnotationMirror a) {
boolean ret = super.removeAnnotation(a);
if (bounds != null) {
if (cowActive()) bounds = cowChildren(bounds);
for (AnnotatedTypeMirror bound : bounds) {
ret |= bound.removeAnnotation(a);
}
Expand All @@ -2879,6 +2974,7 @@ private void fixupBoundAnnotations() {
if (!this.getAnnotationsField().isEmpty()) {
AnnotationMirrorSet newAnnos = this.getAnnotationsField();
if (bounds != null) {
if (cowActive()) bounds = cowChildren(bounds);
for (AnnotatedTypeMirror bound : bounds) {
bound.replaceAnnotations(newAnnos);
}
Expand Down Expand Up @@ -2954,6 +3050,7 @@ public List<AnnotatedTypeMirror> getBounds() {
fixupBoundAnnotations();
freezeLazyComponents(bounds);
}
if (cowActive()) bounds = cowChildren(bounds);
return bounds;
}

Expand Down Expand Up @@ -3067,6 +3164,7 @@ public List<AnnotatedDeclaredType> getAlternatives() {
alternatives = Collections.unmodifiableList(res);
freezeLazyComponents(alternatives);
}
if (cowActive()) alternatives = cowChildren(alternatives);
return alternatives;
}

Expand Down
Loading