| // Copyright (c) 2018, 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.kotlin; |
| |
| import com.android.tools.r8.DiagnosticsHandler; |
| import com.android.tools.r8.graph.DexAnnotation; |
| import com.android.tools.r8.graph.DexAnnotationElement; |
| import com.android.tools.r8.graph.DexClass; |
| import com.android.tools.r8.graph.DexEncodedAnnotation; |
| import com.android.tools.r8.graph.DexString; |
| import com.android.tools.r8.graph.DexValue; |
| import com.android.tools.r8.graph.DexValue.DexValueArray; |
| import com.android.tools.r8.graph.DexValue.DexValueString; |
| import com.android.tools.r8.utils.StringDiagnostic; |
| import java.util.IdentityHashMap; |
| import java.util.Map; |
| import kotlinx.metadata.InconsistentKotlinMetadataException; |
| import kotlinx.metadata.jvm.KotlinClassHeader; |
| import kotlinx.metadata.jvm.KotlinClassMetadata; |
| |
| final class KotlinClassMetadataReader { |
| |
| static KotlinInfo getKotlinInfo( |
| Kotlin kotlin, |
| DexClass clazz, |
| DiagnosticsHandler reporter) { |
| if (clazz.annotations.isEmpty()) { |
| return null; |
| } |
| DexAnnotation meta = clazz.annotations.getFirstMatching(kotlin.metadata.kotlinMetadataType); |
| if (meta != null) { |
| try { |
| return createKotlinInfo(kotlin, clazz, meta.annotation); |
| } catch (ClassCastException | InconsistentKotlinMetadataException | MetadataError e) { |
| reporter.info( |
| new StringDiagnostic("Class " + clazz.type.toSourceString() |
| + " has malformed kotlin.Metadata: " + e.getMessage())); |
| } catch (Throwable e) { |
| reporter.info( |
| new StringDiagnostic("Unexpected error while reading " + clazz.type.toSourceString() |
| + "'s kotlin.Metadata: " + e.getMessage())); |
| } |
| } |
| return null; |
| } |
| |
| private static KotlinClassMetadata toKotlinClassMetadata( |
| Kotlin kotlin, DexEncodedAnnotation metadataAnnotation) { |
| Map<DexString, DexAnnotationElement> elementMap = new IdentityHashMap<>(); |
| for (DexAnnotationElement element : metadataAnnotation.elements) { |
| elementMap.put(element.name, element); |
| } |
| |
| DexAnnotationElement kind = elementMap.get(kotlin.metadata.kind); |
| if (kind == null) { |
| throw new MetadataError("element 'k' is missing."); |
| } |
| Integer k = (Integer) kind.value.getBoxedValue(); |
| DexAnnotationElement metadataVersion = elementMap.get(kotlin.metadata.metadataVersion); |
| int[] mv = metadataVersion == null ? null : getUnboxedIntArray(metadataVersion.value, "mv"); |
| DexAnnotationElement bytecodeVersion = elementMap.get(kotlin.metadata.bytecodeVersion); |
| int[] bv = bytecodeVersion == null ? null : getUnboxedIntArray(bytecodeVersion.value, "bv"); |
| DexAnnotationElement data1 = elementMap.get(kotlin.metadata.data1); |
| String[] d1 = data1 == null ? null : getUnboxedStringArray(data1.value, "d1"); |
| DexAnnotationElement data2 = elementMap.get(kotlin.metadata.data2); |
| String[] d2 = data2 == null ? null : getUnboxedStringArray(data2.value, "d2"); |
| DexAnnotationElement extraString = elementMap.get(kotlin.metadata.extraString); |
| String xs = extraString == null ? null : getUnboxedString(extraString.value, "xs"); |
| DexAnnotationElement packageName = elementMap.get(kotlin.metadata.packageName); |
| String pn = packageName == null ? null : getUnboxedString(packageName.value, "pn"); |
| DexAnnotationElement extraInt = elementMap.get(kotlin.metadata.extraInt); |
| Integer xi = extraInt == null ? null : (Integer) extraInt.value.getBoxedValue(); |
| |
| KotlinClassHeader header = new KotlinClassHeader(k, mv, bv, d1, d2, xs, pn, xi); |
| return KotlinClassMetadata.read(header); |
| } |
| |
| private static KotlinInfo createKotlinInfo( |
| Kotlin kotlin, DexClass clazz, DexEncodedAnnotation metadataAnnotation) { |
| KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, metadataAnnotation); |
| |
| if (kMetadata instanceof KotlinClassMetadata.Class) { |
| return KotlinClass.fromKotlinClassMetadata(kMetadata, clazz); |
| } else if (kMetadata instanceof KotlinClassMetadata.FileFacade) { |
| // e.g., B.kt becomes class `BKt` |
| return KotlinFile.fromKotlinClassMetadata(kMetadata, clazz); |
| } else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassFacade) { |
| // multi-file class with the same @JvmName. |
| return KotlinClassFacade.fromKotlinClassMetadata(kMetadata, clazz); |
| } else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassPart) { |
| // A single file, which is part of multi-file class. |
| return KotlinClassPart.fromKotlinClassMetadata(kMetadata, clazz); |
| } else if (kMetadata instanceof KotlinClassMetadata.SyntheticClass) { |
| return KotlinSyntheticClass.fromKotlinClassMetadata(kMetadata, kotlin, clazz); |
| } else { |
| throw new MetadataError("unsupported 'k' value: " + kMetadata.getHeader().getKind()); |
| } |
| } |
| |
| private static int[] getUnboxedIntArray(DexValue v, String elementName) { |
| if (!(v instanceof DexValueArray)) { |
| throw new MetadataError("invalid '" + elementName + "' value: " + v.toSourceString()); |
| } |
| DexValueArray intArrayValue = (DexValueArray) v; |
| DexValue[] values = intArrayValue.getValues(); |
| int[] result = new int [values.length]; |
| for (int i = 0; i < values.length; i++) { |
| result[i] = (Integer) values[i].getBoxedValue(); |
| } |
| return result; |
| } |
| |
| private static String[] getUnboxedStringArray(DexValue v, String elementName) { |
| if (!(v instanceof DexValueArray)) { |
| throw new MetadataError("invalid '" + elementName + "' value: " + v.toSourceString()); |
| } |
| DexValueArray stringArrayValue = (DexValueArray) v; |
| DexValue[] values = stringArrayValue.getValues(); |
| String[] result = new String [values.length]; |
| for (int i = 0; i < values.length; i++) { |
| result[i] = getUnboxedString(values[i], elementName + "[" + i + "]"); |
| } |
| return result; |
| } |
| |
| private static String getUnboxedString(DexValue v, String elementName) { |
| if (!(v instanceof DexValueString)) { |
| throw new MetadataError("invalid '" + elementName + "' value: " + v.toSourceString()); |
| } |
| return ((DexValueString) v).getValue().toString(); |
| } |
| |
| private static class MetadataError extends RuntimeException { |
| MetadataError(String cause) { |
| super(cause); |
| } |
| } |
| } |