blob: f0378830b71e23a781705ee601a00e57d6dfd13d [file] [log] [blame]
// Copyright (c) 2021, 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.contexts;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
public class CompilationContext {
// Internal contract to compute a unique suffix for synthetics.
private abstract static class ContextDescriptorProvider {
// Method to construct a fully qualified description of the context.
// This is used to ensure that all contexts are unique during compilation.
abstract StringBuilder buildContextDescriptorForTesting(StringBuilder builder);
// Build a suffix to append to synthetic definitions.
abstract StringBuilder buildSyntheticSuffix(StringBuilder builder);
}
/**
* Create the initial compilation context.
*
* <p>This context should be a singleton for a given compilation allocated by AppView.
*/
public static CompilationContext createInitialContext(InternalOptions options) {
return new CompilationContext(options);
}
private final Consumer<String> testingConsumer;
private final Map<String, String> seenSetForTesting = new ConcurrentHashMap<>();
private int nextProcessorId = 0;
private CompilationContext(InternalOptions options) {
testingConsumer = options.testing.processingContextsConsumer;
}
private boolean verifyContext(ContextDescriptorProvider context) {
String descriptor = context.buildContextDescriptorForTesting(new StringBuilder()).toString();
String suffix = context.buildSyntheticSuffix(new StringBuilder()).toString();
assert descriptor.endsWith(suffix);
if (testingConsumer != null) {
testingConsumer.accept(descriptor);
}
assert seenSetForTesting.put(descriptor, descriptor) == null
: "Duplicated use of context descriptor: " + descriptor;
return true;
}
/**
* Creates the context for a "processor".
*
* <p>A "processor" is just a compilation task but which is deterministically ordered as part of
* the full compilation pipeline. Thus, this method should only be called on the main-thread
* ensuring that the assigned ids are deterministic. The id itself has not particular meaning.
*/
public ProcessorContext createProcessorContext() {
ProcessorContext processorContext = new ProcessorContext(this, nextProcessorId++);
assert verifyContext(processorContext);
return processorContext;
}
public static class ProcessorContext extends ContextDescriptorProvider {
private final CompilationContext parent;
private final int processorId;
private ProcessorContext(CompilationContext parent, int processorId) {
this.parent = parent;
this.processorId = processorId;
}
private boolean verifyContext(ContextDescriptorProvider context) {
assert parent.verifyContext(context);
return true;
}
/**
* Create processing context for a single method.
*
* <p>There should only ever be a single allocation of the particular method-processing context.
* This is generally ensured by, eg, a MethodProcessor, having private access to the processing
* context and ensuring a safe single allocation of the individual method-processing contexts.
*/
public MethodProcessingContext createMethodProcessingContext(ProgramMethod method) {
MethodProcessingContext methodProcessingContext = new MethodProcessingContext(this, method);
assert verifyContext(methodProcessingContext);
return methodProcessingContext;
}
private StringBuilder buildSuffix(StringBuilder builder) {
return builder.append('$').append(processorId);
}
@Override
StringBuilder buildContextDescriptorForTesting(StringBuilder builder) {
return buildSuffix(builder);
}
@Override
StringBuilder buildSyntheticSuffix(StringBuilder builder) {
return buildSuffix(builder);
}
}
/** Description of the method context from which to synthesize. */
public static class MethodProcessingContext extends ContextDescriptorProvider {
private final ProcessorContext parent;
private final ProgramMethod method;
private int nextId = 0;
private MethodProcessingContext(ProcessorContext parent, ProgramMethod method) {
this.parent = parent;
this.method = method;
}
/**
* Create a unique processing context.
*
* <p>The uniqueness of the context requires that the parent, eg, method-context, is unique and
* that the processing of that entity is such that the calls to this method happen in a
* deterministic order, eg, by the processing of method instructions being single threaded.
*/
public UniqueContext createUniqueContext() {
UniqueContext uniqueContext = new UniqueContext(this, nextId++);
assert parent.verifyContext(uniqueContext);
return uniqueContext;
}
DexProgramClass getClassContext() {
return method.getHolder();
}
public ProgramMethod getMethodContext() {
return method;
}
private StringBuilder buildSuffix(StringBuilder builder) {
// TODO(b/172194101): Sanitize the method descriptor instead of hashing.
Hasher hasher = Hashing.sha256().newHasher();
method.getReference().hash(hasher);
return builder.append('$').append(hasher.hash().toString());
}
@Override
StringBuilder buildContextDescriptorForTesting(StringBuilder builder) {
// Put the type first in the context descriptor.
builder.append(getClassContext().getType().toDescriptorString());
return buildSuffix(parent.buildContextDescriptorForTesting(builder));
}
@Override
StringBuilder buildSyntheticSuffix(StringBuilder builder) {
return buildSuffix(parent.buildSyntheticSuffix(builder));
}
}
public static class UniqueContext extends ContextDescriptorProvider {
private final MethodProcessingContext parent;
private final int positionId;
private UniqueContext(MethodProcessingContext parent, int positionId) {
this.parent = parent;
this.positionId = positionId;
}
private StringBuilder buildSuffix(StringBuilder builder) {
return builder.append('$').append(positionId);
}
@Override
StringBuilder buildContextDescriptorForTesting(StringBuilder builder) {
return buildSuffix(parent.buildContextDescriptorForTesting(builder));
}
@Override
StringBuilder buildSyntheticSuffix(StringBuilder builder) {
return buildSuffix(parent.buildSyntheticSuffix(builder));
}
public DexProgramClass getClassContext() {
return parent.getClassContext();
}
public String getSyntheticSuffix() {
return buildSyntheticSuffix(new StringBuilder()).toString();
}
}
}