001 /*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.jet.lang.resolve.kotlin.header;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.jet.lang.resolve.java.AbiVersionUtil;
022 import org.jetbrains.jet.lang.resolve.java.JvmClassName;
023 import org.jetbrains.jet.lang.resolve.name.FqName;
024 import org.jetbrains.jet.lang.resolve.name.Name;
025
026 import java.util.*;
027
028 import static org.jetbrains.jet.lang.resolve.java.AbiVersionUtil.isAbiVersionCompatible;
029 import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.*;
030 import static org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass.AnnotationArgumentVisitor;
031 import static org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass.AnnotationVisitor;
032 import static org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader.Kind.*;
033
034 public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor {
035 private static final Map<JvmClassName, KotlinClassHeader.Kind> HEADER_KINDS = new HashMap<JvmClassName, KotlinClassHeader.Kind>();
036
037 static {
038 HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_CLASS), CLASS);
039 HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_PACKAGE), PACKAGE_FACADE);
040 HEADER_KINDS.put(KotlinSyntheticClass.CLASS_NAME, SYNTHETIC_CLASS);
041
042 @SuppressWarnings("deprecation")
043 List<FqName> incompatible = Arrays.asList(OLD_JET_CLASS_ANNOTATION, OLD_JET_PACKAGE_CLASS_ANNOTATION, OLD_KOTLIN_CLASS,
044 OLD_KOTLIN_PACKAGE, OLD_KOTLIN_PACKAGE_FRAGMENT, OLD_KOTLIN_TRAIT_IMPL);
045 for (FqName fqName : incompatible) {
046 HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(fqName), INCOMPATIBLE_ABI_VERSION);
047 }
048 }
049
050 private int version = AbiVersionUtil.INVALID_VERSION;
051 private String[] annotationData = null;
052 private KotlinClassHeader.Kind headerKind = null;
053 private KotlinSyntheticClass.Kind syntheticClassKind = null;
054
055 @Nullable
056 public KotlinClassHeader createHeader() {
057 if (headerKind == null) {
058 return null;
059 }
060
061 if (!AbiVersionUtil.isAbiVersionCompatible(version)) {
062 return new KotlinClassHeader(INCOMPATIBLE_ABI_VERSION, version, null, null);
063 }
064
065 if ((headerKind == CLASS || headerKind == PACKAGE_FACADE) && annotationData == null) {
066 // This means that the annotation is found and its ABI version is compatible, but there's no "data" string array in it.
067 // We tell the outside world that there's really no annotation at all
068 return null;
069 }
070
071 return new KotlinClassHeader(headerKind, version, annotationData, syntheticClassKind);
072 }
073
074 @Nullable
075 @Override
076 public AnnotationArgumentVisitor visitAnnotation(@NotNull JvmClassName annotation) {
077 KotlinClassHeader.Kind newKind = HEADER_KINDS.get(annotation);
078 if (newKind == null) return null;
079
080 if (headerKind != null) {
081 // Ignore all Kotlin annotations except the first found
082 return null;
083 }
084
085 headerKind = newKind;
086
087 if (newKind == CLASS || newKind == PACKAGE_FACADE) {
088 return kotlinClassOrPackageVisitor(annotation);
089 }
090 else if (newKind == SYNTHETIC_CLASS) {
091 return syntheticClassAnnotationVisitor();
092 }
093
094 return null;
095 }
096
097 @Override
098 public void visitEnd() {
099 }
100
101 @NotNull
102 private AnnotationArgumentVisitor kotlinClassOrPackageVisitor(@NotNull final JvmClassName annotationClassName) {
103 return new AnnotationArgumentVisitor() {
104 @Override
105 public void visit(@Nullable Name name, @Nullable Object value) {
106 visitIntValueForSupportedAnnotation(name, value, annotationClassName);
107 }
108
109 @Override
110 public void visitEnum(@NotNull Name name, @NotNull JvmClassName enumClassName, @NotNull Name enumEntryName) {
111 unexpectedArgument(name, annotationClassName);
112 }
113
114 @Override
115 @Nullable
116 public AnnotationArgumentVisitor visitArray(@NotNull Name name) {
117 if (name.asString().equals(DATA_FIELD_NAME)) {
118 return stringArrayVisitor();
119 }
120 else if (isAbiVersionCompatible(version)) {
121 throw new IllegalStateException("Unexpected array argument " + name + " for annotation " + annotationClassName);
122 }
123
124 return null;
125 }
126
127 @NotNull
128 private AnnotationArgumentVisitor stringArrayVisitor() {
129 final List<String> strings = new ArrayList<String>(1);
130 return new AnnotationArgumentVisitor() {
131 @Override
132 public void visit(@Nullable Name name, @Nullable Object value) {
133 if (!(value instanceof String)) {
134 throw new IllegalStateException("Unexpected argument value: " + value);
135 }
136
137 strings.add((String) value);
138 }
139
140 @Override
141 public void visitEnum(@NotNull Name name, @NotNull JvmClassName enumClassName, @NotNull Name enumEntryName) {
142 unexpectedArgument(name, annotationClassName);
143 }
144
145 @Nullable
146 @Override
147 public AnnotationArgumentVisitor visitArray(@NotNull Name name) {
148 return unexpectedArgument(name, annotationClassName);
149 }
150
151 @Override
152 public void visitEnd() {
153 annotationData = strings.toArray(new String[strings.size()]);
154 }
155 };
156 }
157
158 @Override
159 public void visitEnd() {
160 }
161 };
162 }
163
164 @NotNull
165 private AnnotationArgumentVisitor syntheticClassAnnotationVisitor() {
166 return new AnnotationArgumentVisitor() {
167 @Override
168 public void visit(@Nullable Name name, @Nullable Object value) {
169 visitIntValueForSupportedAnnotation(name, value, KotlinSyntheticClass.CLASS_NAME);
170 }
171
172 @Override
173 public void visitEnum(@NotNull Name name, @NotNull JvmClassName enumClassName, @NotNull Name enumEntryName) {
174 if (enumClassName.getInternalName().equals(KotlinSyntheticClass.KIND_INTERNAL_NAME) &&
175 name.equals(KotlinSyntheticClass.KIND_FIELD_NAME)) {
176 // Don't call KotlinSyntheticClass.Kind.valueOf() here, because it will throw an exception if there's no such value,
177 // but we don't want to fail if we're loading the header with an _incompatible_ ABI version
178 syntheticClassKind = KotlinSyntheticClass.Kind.valueOfOrNull(enumEntryName.asString());
179 if (syntheticClassKind != null) return;
180 }
181 if (isAbiVersionCompatible(version)) {
182 throw new IllegalStateException("Unexpected enum entry for synthetic class annotation: " +
183 name + "=" + enumClassName + "." + enumEntryName);
184 }
185 }
186
187 @Nullable
188 @Override
189 public AnnotationArgumentVisitor visitArray(@NotNull Name name) {
190 return unexpectedArgument(name, KotlinSyntheticClass.CLASS_NAME);
191 }
192
193 @Override
194 public void visitEnd() {
195 }
196 };
197 }
198
199 private void visitIntValueForSupportedAnnotation(@Nullable Name name, @Nullable Object value, @NotNull JvmClassName className) {
200 if (name != null && name.asString().equals(ABI_VERSION_FIELD_NAME)) {
201 version = value == null ? AbiVersionUtil.INVALID_VERSION : (Integer) value;
202 }
203 else {
204 unexpectedArgument(name, className);
205 }
206 }
207
208 @Nullable
209 private AnnotationArgumentVisitor unexpectedArgument(@Nullable Name name, @NotNull JvmClassName annotationClassName) {
210 if (isAbiVersionCompatible(version)) {
211 throw new IllegalStateException("Unexpected argument " + name + " for annotation " + annotationClassName);
212 }
213 return null;
214 }
215 }