Read Metadata annotation, support option to ignore old annotations

This commit is contained in:
Alexander Udalov
2015-12-29 19:31:17 +03:00
parent 59dab0a558
commit 7906ac63b5
3 changed files with 143 additions and 30 deletions
@@ -31,17 +31,32 @@ class KotlinClassHeader(
) { ) {
// See kotlin.Metadata // See kotlin.Metadata
enum class Kind(val id: Int) { enum class Kind(val id: Int) {
UNKNOWN(0),
CLASS(1), CLASS(1),
FILE_FACADE(2), FILE_FACADE(2),
SYNTHETIC_CLASS(3), SYNTHETIC_CLASS(3),
MULTIFILE_CLASS(4), MULTIFILE_CLASS(4),
MULTIFILE_CLASS_PART(5); MULTIFILE_CLASS_PART(5);
companion object {
private val entryById = values().toMapBy(Kind::id)
@JvmStatic
fun getById(id: Int) = entryById[id] ?: UNKNOWN
}
} }
enum class SyntheticClassKind(val id: Int) { enum class SyntheticClassKind(val id: Int) {
FUNCTION(1), FUNCTION(1),
LOCAL_CLASS(2), LOCAL_CLASS(2),
INTERFACE_DEFAULT_IMPLS(3); INTERFACE_DEFAULT_IMPLS(3);
companion object {
private val entryById = values().toMapBy(SyntheticClassKind::id)
@JvmStatic
fun getById(id: Int) = entryById[id]
}
} }
override fun toString() = "$kind " + (if (isLocalClass) "(local) " else "") + "version=$metadataVersion" override fun toString() = "$kind " + (if (isLocalClass) "(local) " else "") + "version=$metadataVersion"
@@ -35,6 +35,8 @@ import static org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass.*;
import static org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader.Kind.*; import static org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader.Kind.*;
public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor { public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor {
private static final boolean IGNORE_OLD_METADATA = "true".equals(System.getProperty("kotlin.ignore.old.metadata"));
private static final Map<ClassId, KotlinClassHeader.Kind> HEADER_KINDS = new HashMap<ClassId, KotlinClassHeader.Kind>(); private static final Map<ClassId, KotlinClassHeader.Kind> HEADER_KINDS = new HashMap<ClassId, KotlinClassHeader.Kind>();
static { static {
@@ -51,6 +53,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
private String[] data = null; private String[] data = null;
private String[] strings = null; private String[] strings = null;
private KotlinClassHeader.Kind headerKind = null; private KotlinClassHeader.Kind headerKind = null;
private KotlinClassHeader.SyntheticClassKind syntheticClassKind = null;
private boolean isInterfaceDefaultImpls = false; private boolean isInterfaceDefaultImpls = false;
private boolean isLocalClass = false; private boolean isLocalClass = false;
@@ -73,7 +76,11 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
headerKind, headerKind,
metadataVersion != null ? metadataVersion : JvmMetadataVersion.INVALID_VERSION, metadataVersion != null ? metadataVersion : JvmMetadataVersion.INVALID_VERSION,
bytecodeVersion != null ? bytecodeVersion : JvmBytecodeBinaryVersion.INVALID_VERSION, bytecodeVersion != null ? bytecodeVersion : JvmBytecodeBinaryVersion.INVALID_VERSION,
data, strings, multifileClassName, isInterfaceDefaultImpls, isLocalClass data,
strings,
multifileClassName,
isInterfaceDefaultImpls || syntheticClassKind == KotlinClassHeader.SyntheticClassKind.INTERFACE_DEFAULT_IMPLS,
isLocalClass || syntheticClassKind == KotlinClassHeader.SyntheticClassKind.LOCAL_CLASS
); );
} }
@@ -87,6 +94,12 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
@Override @Override
public AnnotationArgumentVisitor visitAnnotation(@NotNull ClassId classId, @NotNull SourceElement source) { public AnnotationArgumentVisitor visitAnnotation(@NotNull ClassId classId, @NotNull SourceElement source) {
FqName fqName = classId.asSingleFqName(); FqName fqName = classId.asSingleFqName();
if (fqName.equals(METADATA)) {
return new KotlinMetadataArgumentVisitor();
}
if (IGNORE_OLD_METADATA) return null;
if (KOTLIN_INTERFACE_DEFAULT_IMPLS.equals(fqName)) { if (KOTLIN_INTERFACE_DEFAULT_IMPLS.equals(fqName)) {
isInterfaceDefaultImpls = true; isInterfaceDefaultImpls = true;
return null; return null;
@@ -114,6 +127,89 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
public void visitEnd() { public void visitEnd() {
} }
private class KotlinMetadataArgumentVisitor implements AnnotationArgumentVisitor {
@Override
public void visit(@Nullable Name name, @Nullable Object value) {
if (name == null) return;
String string = name.asString();
if (KIND_FIELD_NAME.equals(string)) {
if (value instanceof Integer) {
headerKind = KotlinClassHeader.Kind.getById((Integer) value);
}
}
else if (METADATA_VERSION_FIELD_NAME.equals(string)) {
if (value instanceof int[]) {
metadataVersion = JvmMetadataVersion.create((int[]) value);
}
}
else if (BYTECODE_VERSION_FIELD_NAME.equals(string)) {
if (value instanceof int[]) {
bytecodeVersion = JvmBytecodeBinaryVersion.create((int[]) value);
}
}
else if (SYNTHETIC_CLASS_KIND_FIELD_NAME.equals(string)) {
if (value instanceof Integer) {
syntheticClassKind = KotlinClassHeader.SyntheticClassKind.getById((Integer) value);
}
}
else if (METADATA_MULTIFILE_CLASS_NAME_FIELD_NAME.equals(string)) {
if (value instanceof String) {
multifileClassName = (String) value;
}
}
}
@Override
@Nullable
public AnnotationArrayArgumentVisitor visitArray(@NotNull Name name) {
String string = name.asString();
if (METADATA_DATA_FIELD_NAME.equals(string)) {
return dataArrayVisitor();
}
else if (METADATA_STRINGS_FIELD_NAME.equals(string)) {
return stringsArrayVisitor();
}
else {
return null;
}
}
@NotNull
private AnnotationArrayArgumentVisitor dataArrayVisitor() {
return new CollectStringArrayAnnotationVisitor() {
@Override
protected void visitEnd(@NotNull String[] result) {
data = result;
}
};
}
@NotNull
private AnnotationArrayArgumentVisitor stringsArrayVisitor() {
return new CollectStringArrayAnnotationVisitor() {
@Override
protected void visitEnd(@NotNull String[] result) {
strings = result;
}
};
}
@Override
public void visitEnum(@NotNull Name name, @NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
}
@Nullable
@Override
public AnnotationArgumentVisitor visitAnnotation(@NotNull Name name, @NotNull ClassId classId) {
return null;
}
@Override
public void visitEnd() {
}
}
private class HeaderAnnotationArgumentVisitor implements AnnotationArgumentVisitor { private class HeaderAnnotationArgumentVisitor implements AnnotationArgumentVisitor {
@Override @Override
public void visit(@Nullable Name name, @Nullable Object value) { public void visit(@Nullable Name name, @Nullable Object value) {
@@ -183,32 +279,32 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
@Override @Override
public void visitEnd() { public void visitEnd() {
} }
}
private abstract class CollectStringArrayAnnotationVisitor implements AnnotationArrayArgumentVisitor { private abstract static class CollectStringArrayAnnotationVisitor implements AnnotationArrayArgumentVisitor {
private final List<String> strings; private final List<String> strings;
public CollectStringArrayAnnotationVisitor() { public CollectStringArrayAnnotationVisitor() {
this.strings = new ArrayList<String>(); this.strings = new ArrayList<String>();
}
@Override
public void visit(@Nullable Object value) {
if (value instanceof String) {
strings.add((String) value);
}
}
@Override
public void visitEnum(@NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
}
@Override
public void visitEnd() {
//noinspection SSBasedInspection
visitEnd(strings.toArray(new String[strings.size()]));
}
protected abstract void visitEnd(@NotNull String[] data);
} }
@Override
public void visit(@Nullable Object value) {
if (value instanceof String) {
strings.add((String) value);
}
}
@Override
public void visitEnum(@NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
}
@Override
public void visitEnd() {
//noinspection SSBasedInspection
visitEnd(strings.toArray(new String[strings.size()]));
}
protected abstract void visitEnd(@NotNull String[] data);
} }
} }
@@ -14,9 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") // kotlin.Metadata
package kotlin.reflect.jvm package kotlin.reflect.jvm
import org.jetbrains.kotlin.load.kotlin.JvmNameResolver import org.jetbrains.kotlin.load.kotlin.JvmNameResolver
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.serialization.ProtoBuf import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.DeserializationContext import org.jetbrains.kotlin.serialization.deserialization.DeserializationContext
import org.jetbrains.kotlin.serialization.deserialization.MemberDeserializer import org.jetbrains.kotlin.serialization.deserialization.MemberDeserializer
@@ -24,7 +27,6 @@ import org.jetbrains.kotlin.serialization.deserialization.TypeTable
import org.jetbrains.kotlin.serialization.jvm.BitEncoding import org.jetbrains.kotlin.serialization.jvm.BitEncoding
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
import kotlin.jvm.internal.KotlinFunction
import kotlin.reflect.KFunction import kotlin.reflect.KFunction
import kotlin.reflect.jvm.internal.EmptyContainerForLocal import kotlin.reflect.jvm.internal.EmptyContainerForLocal
import kotlin.reflect.jvm.internal.KFunctionImpl import kotlin.reflect.jvm.internal.KFunctionImpl
@@ -36,11 +38,11 @@ import kotlin.reflect.jvm.internal.getOrCreateModule
* Not all features are currently supported, in particular [KCallable.call] and [KCallable.callBy] will fail at the moment. * Not all features are currently supported, in particular [KCallable.call] and [KCallable.callBy] will fail at the moment.
*/ */
fun <R> Function<R>.reflect(): KFunction<R>? { fun <R> Function<R>.reflect(): KFunction<R>? {
val callable = javaClass.getAnnotation(KotlinFunction::class.java) ?: return null val annotation = javaClass.getAnnotation(Metadata::class.java) ?: return null
val input = BitEncoding.decodeBytes(callable.data).inputStream() if (annotation.xi != KotlinClassHeader.SyntheticClassKind.FUNCTION.id) return null
val input = BitEncoding.decodeBytes(annotation.d1).inputStream()
val nameResolver = JvmNameResolver( val nameResolver = JvmNameResolver(
JvmProtoBuf.StringTableTypes.parseDelimitedFrom(input, JvmProtoBufUtil.EXTENSION_REGISTRY), JvmProtoBuf.StringTableTypes.parseDelimitedFrom(input, JvmProtoBufUtil.EXTENSION_REGISTRY), annotation.d2
callable.strings
) )
val proto = ProtoBuf.Function.parseFrom(input, JvmProtoBufUtil.EXTENSION_REGISTRY) val proto = ProtoBuf.Function.parseFrom(input, JvmProtoBufUtil.EXTENSION_REGISTRY)
val moduleData = javaClass.getOrCreateModule() val moduleData = javaClass.getOrCreateModule()