blob: 98060267db653a34345277eadf7a333ea64eebff [file] [log] [blame]
// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno.keeprules;
import com.android.tools.r8.keepanno.ast.KeepBindingReference;
import com.android.tools.r8.keepanno.ast.KeepBindings;
import com.android.tools.r8.keepanno.ast.KeepBindings.KeepBindingSymbol;
import com.android.tools.r8.keepanno.ast.KeepClassItemReference;
import com.android.tools.r8.keepanno.ast.KeepCondition;
import com.android.tools.r8.keepanno.ast.KeepConsequences;
import com.android.tools.r8.keepanno.ast.KeepEdge;
import com.android.tools.r8.keepanno.ast.KeepItemPattern;
import com.android.tools.r8.keepanno.ast.KeepItemReference;
import com.android.tools.r8.keepanno.ast.KeepMemberItemPattern;
import com.android.tools.r8.keepanno.ast.KeepPreconditions;
import com.android.tools.r8.keepanno.ast.KeepTarget;
import java.util.HashMap;
import java.util.Map;
/**
* Normalize a keep edge with respect to its bindings. This will systematically introduce a binding
* for each item in the edge. It will also introduce a class binding for the holder of any member
* item. By introducing a binding for each item the binding can be used as item identity.
*/
public class KeepEdgeNormalizer {
private static final String syntheticBindingPrefix = "SyntheticBinding";
public static KeepEdge normalize(KeepEdge edge) {
// Check that all referenced bindings are defined.
KeepEdgeNormalizer normalizer = new KeepEdgeNormalizer(edge);
return normalizer.run();
}
private final KeepEdge edge;
private final Map<KeepBindingSymbol, KeepItemPattern> normalizedUserBindings = new HashMap<>();
private final KeepBindings.Builder bindingsBuilder = KeepBindings.builder();
private final KeepPreconditions.Builder preconditionsBuilder = KeepPreconditions.builder();
private final KeepConsequences.Builder consequencesBuilder = KeepConsequences.builder();
private KeepEdgeNormalizer(KeepEdge edge) {
this.edge = edge;
}
private KeepEdge run() {
edge.getBindings()
.forEach(
(name, pattern) -> {
KeepItemPattern normalizedItem = normalizeItemPattern(pattern);
bindingsBuilder.addBinding(name, normalizedItem);
normalizedUserBindings.put(name, normalizedItem);
});
// TODO(b/248408342): Normalize the preconditions by identifying vacuously true conditions.
edge.getPreconditions()
.forEach(
condition ->
preconditionsBuilder.addCondition(
KeepCondition.builder()
.setItemReference(normalizeItemReference(condition.getItem()))
.build()));
edge.getConsequences()
.forEachTarget(
target -> {
consequencesBuilder.addTarget(
KeepTarget.builder()
.setConstraints(target.getConstraints())
.setItemReference(normalizeItemReference(target.getItem()))
.build());
});
return KeepEdge.builder()
.setMetaInfo(edge.getMetaInfo())
.setBindings(bindingsBuilder.build())
.setPreconditions(preconditionsBuilder.build())
.setConsequences(consequencesBuilder.build())
.build();
}
private KeepBindingSymbol synthesizeFreshBindingSymbol(KeepItemPattern item) {
KeepBindingSymbol bindingName = bindingsBuilder.generateFreshSymbol(syntheticBindingPrefix);
bindingsBuilder.addBinding(bindingName, item);
return bindingName;
}
private KeepBindingReference synthesizeFreshBindingReference(KeepItemPattern item) {
KeepBindingSymbol bindingName = synthesizeFreshBindingSymbol(item);
return KeepBindingReference.forItem(bindingName, item);
}
private KeepItemReference normalizeItemReference(KeepItemReference item) {
if (item.isBindingReference()) {
KeepBindingReference bindingReference = item.asBindingReference();
if (bindingReference.isClassType()) {
// A class-type reference is allowed to reference a member-typed binding.
// In this case, the normalized reference is to the class of the member.
KeepItemPattern boundItemPattern = normalizedUserBindings.get(bindingReference.getName());
if (boundItemPattern.isMemberItemPattern()) {
return boundItemPattern.asMemberItemPattern().getClassReference();
}
}
return item;
}
KeepItemPattern newItemPattern = normalizeItemPattern(item.asItemPattern());
return synthesizeFreshBindingReference(newItemPattern).toItemReference();
}
private KeepItemPattern normalizeItemPattern(KeepItemPattern pattern) {
if (pattern.isClassItemPattern()) {
// If the pattern is just a class pattern it is in normal form.
return pattern;
}
return normalizeMemberItemPattern(pattern.asMemberItemPattern());
}
private KeepMemberItemPattern normalizeMemberItemPattern(
KeepMemberItemPattern memberItemPattern) {
KeepClassItemReference classReference = memberItemPattern.getClassReference();
if (classReference.isBindingReference()) {
// If the class is already defined via a binding then no need to introduce a new one and
// change the member item pattern.
return memberItemPattern;
}
KeepBindingSymbol bindingName =
synthesizeFreshBindingSymbol(classReference.asClassItemPattern());
return KeepMemberItemPattern.builder()
.copyFrom(memberItemPattern)
.setClassReference(KeepBindingReference.forClass(bindingName).toClassItemReference())
.build();
}
}