blob: 644a359d79de655245531c4eb464b5bff3691a69 [file] [log] [blame]
// Copyright (c) 2020, 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.synthesis;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.DescriptorUtils;
public class SyntheticNaming {
/**
* Enumeration of all kinds of synthetic items.
*
* <p>The synthetic kinds are used to provide hinting about what a synthetic item represents. The
* kinds must *not* be used be the compiler and are only meant for "debugging". The compiler and
* its test may use the kind information as part of asserting properties of the compiler. The kind
* will be put into any non-minified synthetic name and thus the kind "descriptor" must be a
* distinct for each kind.
*/
public enum SyntheticKind {
// Class synthetics.
LAMBDA("Lambda", false),
// Method synthetics.
BACKPORT("Backport", true),
STATIC_INTERFACE_CALL("StaticInterfaceCall", true),
TO_STRING_IF_NOT_NULL("ToStringIfNotNull", true),
THROW_CCE_IF_NOT_NULL("ThrowCCEIfNotNull", true);
public final String descriptor;
public final boolean isSingleSyntheticMethod;
SyntheticKind(String descriptor, boolean isSingleSyntheticMethod) {
this.descriptor = descriptor;
this.isSingleSyntheticMethod = isSingleSyntheticMethod;
}
public static SyntheticKind fromDescriptor(String descriptor) {
for (SyntheticKind kind : values()) {
if (kind.descriptor.equals(descriptor)) {
return kind;
}
}
return null;
}
}
/**
* The internal synthetic class separator is only used for representing synthetic items during
* compilation. In particular, this separator must never be used to write synthetic classes to the
* final compilation result.
*/
private static final String INTERNAL_SYNTHETIC_CLASS_SEPARATOR = "-$$InternalSynthetic";
/**
* The external synthetic class separator is used when writing classes. It may appear in types
* during compilation as the output of a compilation may be the input to another.
*/
private static final String EXTERNAL_SYNTHETIC_CLASS_SEPARATOR = "-$$ExternalSynthetic";
/** Method prefix when generating synthetic methods in a class. */
static final String INTERNAL_SYNTHETIC_METHOD_PREFIX = "m";
// TODO(b/158159959): Remove usage of name-based identification.
public static boolean isSyntheticName(String typeName) {
return typeName.contains(INTERNAL_SYNTHETIC_CLASS_SEPARATOR)
|| typeName.contains(EXTERNAL_SYNTHETIC_CLASS_SEPARATOR);
}
static DexType createInternalType(
SyntheticKind kind, SynthesizingContext context, String id, DexItemFactory factory) {
return createType(
INTERNAL_SYNTHETIC_CLASS_SEPARATOR,
kind,
context.getSynthesizingContextType(),
id,
factory);
}
static DexType createExternalType(
SyntheticKind kind, DexType context, String id, DexItemFactory factory) {
return createType(EXTERNAL_SYNTHETIC_CLASS_SEPARATOR, kind, context, id, factory);
}
private static DexType createType(
String separator, SyntheticKind kind, DexType context, String id, DexItemFactory factory) {
return factory.createType(createDescriptor(separator, kind, context.getInternalName(), id));
}
private static String createDescriptor(
String separator, SyntheticKind kind, String context, String id) {
return DescriptorUtils.getDescriptorFromClassBinaryName(
context + separator + kind.descriptor + id);
}
public static boolean verifyNotInternalSynthetic(DexType type) {
return verifyNotInternalSynthetic(type.toDescriptorString());
}
public static boolean verifyNotInternalSynthetic(ClassReference reference) {
return verifyNotInternalSynthetic(reference.getDescriptor());
}
public static boolean verifyNotInternalSynthetic(String typeBinaryNameOrDescriptor) {
assert !typeBinaryNameOrDescriptor.contains(INTERNAL_SYNTHETIC_CLASS_SEPARATOR);
return true;
}
// Visible via package protection in SyntheticItemsTestUtils.
enum Phase {
INTERNAL,
EXTERNAL
}
static String getPhaseSeparator(Phase phase) {
assert phase != null;
return phase == Phase.INTERNAL
? INTERNAL_SYNTHETIC_CLASS_SEPARATOR
: EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
}
static ClassReference makeSyntheticReferenceForTest(
ClassReference context, SyntheticKind kind, String id) {
return Reference.classFromDescriptor(
createDescriptor(EXTERNAL_SYNTHETIC_CLASS_SEPARATOR, kind, context.getBinaryName(), id));
}
static boolean isSynthetic(ClassReference clazz, Phase phase, SyntheticKind kind) {
String typeName = clazz.getTypeName();
String separator = getPhaseSeparator(phase);
int i = typeName.indexOf(separator);
return i >= 0 && checkMatchFrom(kind, typeName, i, separator);
}
private static boolean checkMatchFrom(
SyntheticKind kind, String name, int i, String externalSyntheticClassSeparator) {
int end = i + externalSyntheticClassSeparator.length() + kind.descriptor.length();
if (end >= name.length()) {
return false;
}
String prefix = name.substring(i, end);
return prefix.equals(externalSyntheticClassSeparator + kind.descriptor)
&& isInt(name.substring(end));
}
private static boolean isInt(String str) {
if (str.isEmpty()) {
return false;
}
if ('0' == str.charAt(0)) {
return str.length() == 1;
}
for (int i = 0; i < str.length(); i++) {
if (!Character.isDigit(str.charAt(i))) {
return false;
}
}
return true;
}
}