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.*;
031 import static org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader.Kind.*;
032
033 public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor {
034 private static final Map<JvmClassName, KotlinClassHeader.Kind> HEADER_KINDS = new HashMap<JvmClassName, KotlinClassHeader.Kind>();
035
036 static {
037 HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_CLASS), CLASS);
038 HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_PACKAGE), PACKAGE_FACADE);
039 HEADER_KINDS.put(KotlinSyntheticClass.CLASS_NAME, SYNTHETIC_CLASS);
040
041 @SuppressWarnings("deprecation")
042 List<FqName> incompatible = Arrays.asList(OLD_JET_CLASS_ANNOTATION, OLD_JET_PACKAGE_CLASS_ANNOTATION, OLD_KOTLIN_CLASS,
043 OLD_KOTLIN_PACKAGE, OLD_KOTLIN_PACKAGE_FRAGMENT, OLD_KOTLIN_TRAIT_IMPL);
044 for (FqName fqName : incompatible) {
045 HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(fqName), INCOMPATIBLE_ABI_VERSION);
046 }
047 }
048
049 private int version = AbiVersionUtil.INVALID_VERSION;
050 private String[] annotationData = null;
051 private KotlinClassHeader.Kind headerKind = null;
052 private KotlinSyntheticClass.Kind syntheticClassKind = null;
053
054 @Nullable
055 public KotlinClassHeader createHeader() {
056 if (headerKind == null) {
057 return null;
058 }
059
060 if (!AbiVersionUtil.isAbiVersionCompatible(version)) {
061 return new KotlinClassHeader(INCOMPATIBLE_ABI_VERSION, version, null, null);
062 }
063
064 if ((headerKind == CLASS || headerKind == PACKAGE_FACADE) && annotationData == null) {
065 // This means that the annotation is found and its ABI version is compatible, but there's no "data" string array in it.
066 // We tell the outside world that there's really no annotation at all
067 return null;
068 }
069
070 return new KotlinClassHeader(headerKind, version, annotationData, syntheticClassKind);
071 }
072
073 @Nullable
074 @Override
075 public AnnotationArgumentVisitor visitAnnotation(@NotNull JvmClassName annotation) {
076 KotlinClassHeader.Kind newKind = HEADER_KINDS.get(annotation);
077 if (newKind == null) return null;
078
079 if (headerKind != null) {
080 // Ignore all Kotlin annotations except the first found
081 return null;
082 }
083
084 headerKind = newKind;
085
086 if (newKind == CLASS || newKind == PACKAGE_FACADE) {
087 return kotlinClassOrPackageVisitor(annotation);
088 }
089 else if (newKind == SYNTHETIC_CLASS) {
090 return syntheticClassAnnotationVisitor();
091 }
092
093 return null;
094 }
095
096 @Override
097 public void visitEnd() {
098 }
099
100 @NotNull
101 private AnnotationArgumentVisitor kotlinClassOrPackageVisitor(@NotNull final JvmClassName annotationClassName) {
102 return new AnnotationArgumentVisitor() {
103 @Override
104 public void visit(@Nullable Name name, @Nullable Object value) {
105 visitIntValueForSupportedAnnotation(name, value, annotationClassName);
106 }
107
108 @Override
109 public void visitEnum(@NotNull Name name, @NotNull JvmClassName enumClassName, @NotNull Name enumEntryName) {
110 unexpectedArgument(name, annotationClassName);
111 }
112
113 @Override
114 @Nullable
115 public AnnotationArrayArgumentVisitor visitArray(@NotNull Name name) {
116 if (name.asString().equals(DATA_FIELD_NAME)) {
117 return stringArrayVisitor();
118 }
119 else if (isAbiVersionCompatible(version)) {
120 throw new IllegalStateException("Unexpected array argument " + name + " for annotation " + annotationClassName);
121 }
122
123 return null;
124 }
125
126 @NotNull
127 private AnnotationArrayArgumentVisitor stringArrayVisitor() {
128 final List<String> strings = new ArrayList<String>(1);
129 return new AnnotationArrayArgumentVisitor() {
130 @Override
131 public void visit(@Nullable Object value) {
132 if (!(value instanceof String)) {
133 throw new IllegalStateException("Unexpected argument value: " + value);
134 }
135
136 strings.add((String) value);
137 }
138
139 @Override
140 public void visitEnum(@NotNull JvmClassName enumClassName, @NotNull Name enumEntryName) {
141 unexpectedArgument(null, annotationClassName);
142 }
143
144 @Override
145 public void visitEnd() {
146 annotationData = strings.toArray(new String[strings.size()]);
147 }
148 };
149 }
150
151 @Override
152 public void visitEnd() {
153 }
154 };
155 }
156
157 @NotNull
158 private AnnotationArgumentVisitor syntheticClassAnnotationVisitor() {
159 return new AnnotationArgumentVisitor() {
160 @Override
161 public void visit(@Nullable Name name, @Nullable Object value) {
162 visitIntValueForSupportedAnnotation(name, value, KotlinSyntheticClass.CLASS_NAME);
163 }
164
165 @Override
166 public void visitEnum(@NotNull Name name, @NotNull JvmClassName enumClassName, @NotNull Name enumEntryName) {
167 if (enumClassName.getInternalName().equals(KotlinSyntheticClass.KIND_INTERNAL_NAME) &&
168 name.equals(KotlinSyntheticClass.KIND_FIELD_NAME)) {
169 // Don't call KotlinSyntheticClass.Kind.valueOf() here, because it will throw an exception if there's no such value,
170 // but we don't want to fail if we're loading the header with an _incompatible_ ABI version
171 syntheticClassKind = KotlinSyntheticClass.Kind.valueOfOrNull(enumEntryName.asString());
172 if (syntheticClassKind != null) return;
173 }
174 if (isAbiVersionCompatible(version)) {
175 throw new IllegalStateException("Unexpected enum entry for synthetic class annotation: " +
176 name + "=" + enumClassName + "." + enumEntryName);
177 }
178 }
179
180 @Nullable
181 @Override
182 public AnnotationArrayArgumentVisitor visitArray(@NotNull Name name) {
183 return unexpectedArgument(name, KotlinSyntheticClass.CLASS_NAME);
184 }
185
186 @Override
187 public void visitEnd() {
188 }
189 };
190 }
191
192 private void visitIntValueForSupportedAnnotation(@Nullable Name name, @Nullable Object value, @NotNull JvmClassName className) {
193 if (name != null && name.asString().equals(ABI_VERSION_FIELD_NAME)) {
194 version = value == null ? AbiVersionUtil.INVALID_VERSION : (Integer) value;
195 }
196 else {
197 unexpectedArgument(name, className);
198 }
199 }
200
201 @Nullable
202 private AnnotationArrayArgumentVisitor unexpectedArgument(@Nullable Name name, @NotNull JvmClassName annotationClassName) {
203 if (isAbiVersionCompatible(version)) {
204 throw new IllegalStateException("Unexpected argument " + name + " for annotation " + annotationClassName);
205 }
206 return null;
207 }
208 }