| // 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.AppView; |
| import com.android.tools.r8.graph.ClassAccessFlags; |
| import com.android.tools.r8.graph.DexAnnotation; |
| import com.android.tools.r8.graph.DexAnnotationSet; |
| import com.android.tools.r8.graph.DexEncodedMethod; |
| import com.android.tools.r8.graph.DexItemFactory; |
| import com.android.tools.r8.graph.DexProgramClass; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind; |
| import com.android.tools.r8.utils.DescriptorUtils; |
| import com.android.tools.r8.utils.InternalOptions; |
| import org.objectweb.asm.Attribute; |
| import org.objectweb.asm.ByteVector; |
| import org.objectweb.asm.ClassReader; |
| import org.objectweb.asm.ClassWriter; |
| import org.objectweb.asm.Label; |
| |
| public class SyntheticMarker { |
| |
| private static final String SYNTHETIC_MARKER_ATTRIBUTE_TYPE_NAME = |
| "com.android.tools.r8.SynthesizedClass"; |
| |
| public static Attribute getMarkerAttributePrototype() { |
| return MarkerAttribute.PROTOTYPE; |
| } |
| |
| public static void writeMarkerAttribute(ClassWriter writer, SyntheticKind kind) { |
| writer.visitAttribute(new MarkerAttribute(kind)); |
| } |
| |
| public static SyntheticMarker readMarkerAttribute(Attribute attribute) { |
| if (attribute instanceof MarkerAttribute) { |
| MarkerAttribute marker = (MarkerAttribute) attribute; |
| return new SyntheticMarker(marker.kind, null); |
| } |
| return null; |
| } |
| |
| private static class MarkerAttribute extends Attribute { |
| |
| private static final MarkerAttribute PROTOTYPE = new MarkerAttribute(null); |
| |
| private SyntheticKind kind; |
| |
| public MarkerAttribute(SyntheticKind kind) { |
| super(SYNTHETIC_MARKER_ATTRIBUTE_TYPE_NAME); |
| this.kind = kind; |
| } |
| |
| @Override |
| protected Attribute read( |
| ClassReader classReader, |
| int offset, |
| int length, |
| char[] charBuffer, |
| int codeAttributeOffset, |
| Label[] labels) { |
| short id = classReader.readShort(offset); |
| assert id >= 0; |
| SyntheticKind kind = SyntheticKind.fromId(id); |
| return new MarkerAttribute(kind); |
| } |
| |
| @Override |
| protected ByteVector write( |
| ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { |
| ByteVector byteVector = new ByteVector(); |
| assert 0 <= kind.id && kind.id <= Short.MAX_VALUE; |
| byteVector.putShort(kind.id); |
| return byteVector; |
| } |
| } |
| |
| public static void addMarkerToClass( |
| DexProgramClass clazz, SyntheticKind kind, InternalOptions options) { |
| // TODO(b/158159959): Consider moving this to the dex writer similar to the CF case. |
| assert !options.isGeneratingClassFiles(); |
| clazz.setAnnotations( |
| clazz |
| .annotations() |
| .getWithAddedOrReplaced( |
| DexAnnotation.createAnnotationSynthesizedClass(kind, options.itemFactory))); |
| } |
| |
| public static SyntheticMarker stripMarkerFromClass(DexProgramClass clazz, AppView<?> appView) { |
| if (clazz.originatesFromClassResource()) { |
| SyntheticMarker marker = clazz.stripSyntheticInputMarker(); |
| if (marker == null) { |
| return NO_MARKER; |
| } |
| assert marker.getContext() == null; |
| DexType contextType = |
| getSyntheticContextType(clazz.type, marker.kind, appView.dexItemFactory()); |
| SynthesizingContext context = |
| SynthesizingContext.fromSyntheticInputClass(clazz, contextType, appView); |
| return new SyntheticMarker(marker.kind, context); |
| } |
| SyntheticMarker marker = internalStripMarkerFromClass(clazz, appView); |
| assert marker != NO_MARKER |
| || !DexAnnotation.hasSynthesizedClassAnnotation( |
| clazz.annotations(), appView.dexItemFactory()); |
| return marker; |
| } |
| |
| private static SyntheticMarker internalStripMarkerFromClass( |
| DexProgramClass clazz, AppView<?> appView) { |
| ClassAccessFlags flags = clazz.accessFlags; |
| if (clazz.superType != appView.dexItemFactory().objectType) { |
| return NO_MARKER; |
| } |
| if (!flags.isSynthetic() || flags.isAbstract() || flags.isEnum()) { |
| return NO_MARKER; |
| } |
| SyntheticKind kind = |
| DexAnnotation.getSynthesizedClassAnnotationInfo( |
| clazz.annotations(), appView.dexItemFactory()); |
| if (kind == null) { |
| return NO_MARKER; |
| } |
| assert clazz.annotations().size() == 1; |
| if (kind.isSingleSyntheticMethod) { |
| if (!clazz.interfaces.isEmpty()) { |
| return NO_MARKER; |
| } |
| for (DexEncodedMethod method : clazz.methods()) { |
| if (!SyntheticMethodBuilder.isValidSingleSyntheticMethod(method)) { |
| return NO_MARKER; |
| } |
| } |
| } |
| clazz.setAnnotations(DexAnnotationSet.empty()); |
| DexType context = getSyntheticContextType(clazz.type, kind, appView.dexItemFactory()); |
| return new SyntheticMarker( |
| kind, SynthesizingContext.fromSyntheticInputClass(clazz, context, appView)); |
| } |
| |
| private static DexType getSyntheticContextType( |
| DexType type, SyntheticKind kind, DexItemFactory factory) { |
| String prefix = SyntheticNaming.getPrefixForExternalSyntheticType(kind, type); |
| return factory.createType(DescriptorUtils.getDescriptorFromClassBinaryName(prefix)); |
| } |
| |
| private static final SyntheticMarker NO_MARKER = new SyntheticMarker(null, null); |
| |
| private final SyntheticKind kind; |
| private final SynthesizingContext context; |
| |
| public SyntheticMarker(SyntheticKind kind, SynthesizingContext context) { |
| this.kind = kind; |
| this.context = context; |
| } |
| |
| public boolean isSyntheticMethods() { |
| return kind != null && kind.isSingleSyntheticMethod; |
| } |
| |
| public boolean isSyntheticClass() { |
| return kind != null && !kind.isSingleSyntheticMethod; |
| } |
| |
| public SyntheticKind getKind() { |
| return kind; |
| } |
| |
| public SynthesizingContext getContext() { |
| return context; |
| } |
| } |