001 /*
002 * Copyright 2010-2015 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.kotlin.load.kotlin.header;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.kotlin.descriptors.SourceElement;
022 import org.jetbrains.kotlin.load.java.AbiVersionUtil;
023 import org.jetbrains.kotlin.name.ClassId;
024 import org.jetbrains.kotlin.name.FqName;
025 import org.jetbrains.kotlin.name.Name;
026 import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion;
027
028 import java.util.ArrayList;
029 import java.util.HashMap;
030 import java.util.List;
031 import java.util.Map;
032
033 import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.*;
034 import static org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass.*;
035 import static org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader.Kind.*;
036
037 public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor {
038 private static final Map<ClassId, KotlinClassHeader.Kind> HEADER_KINDS = new HashMap<ClassId, KotlinClassHeader.Kind>();
039
040 static {
041 HEADER_KINDS.put(ClassId.topLevel(KOTLIN_CLASS), CLASS);
042 HEADER_KINDS.put(ClassId.topLevel(KOTLIN_PACKAGE), PACKAGE_FACADE);
043 HEADER_KINDS.put(ClassId.topLevel(KOTLIN_FILE_FACADE), FILE_FACADE);
044 HEADER_KINDS.put(ClassId.topLevel(KOTLIN_MULTIFILE_CLASS), MULTIFILE_CLASS);
045 HEADER_KINDS.put(ClassId.topLevel(KOTLIN_MULTIFILE_CLASS_PART), MULTIFILE_CLASS_PART);
046 HEADER_KINDS.put(ClassId.topLevel(KOTLIN_SYNTHETIC_CLASS), SYNTHETIC_CLASS);
047 }
048
049 private BinaryVersion version = AbiVersionUtil.INVALID_VERSION;
050 private String multifileClassName = null;
051 private String[] filePartClassNames = null;
052 private String[] annotationData = null;
053 private String[] strings = null;
054 private KotlinClassHeader.Kind headerKind = null;
055 private String syntheticClassKind = null;
056 private boolean isInterfaceDefaultImpls = false;
057 private boolean isLocalClass = false;
058
059 @Nullable
060 public KotlinClassHeader createHeader() {
061 if (headerKind == null) {
062 return null;
063 }
064
065 if (!AbiVersionUtil.isAbiVersionCompatible(version)) {
066 annotationData = null;
067 }
068 else if (shouldHaveData() && annotationData == null) {
069 // This means that the annotation is found and its ABI version is compatible, but there's no "data" string array in it.
070 // We tell the outside world that there's really no annotation at all
071 return null;
072 }
073
074 return new KotlinClassHeader(
075 headerKind, version, annotationData, strings, syntheticClassKind, filePartClassNames, multifileClassName,
076 isInterfaceDefaultImpls, isLocalClass
077 );
078 }
079
080 private boolean shouldHaveData() {
081 return headerKind == CLASS ||
082 headerKind == PACKAGE_FACADE ||
083 headerKind == FILE_FACADE ||
084 headerKind == MULTIFILE_CLASS_PART;
085 }
086
087 @Nullable
088 @Override
089 public AnnotationArgumentVisitor visitAnnotation(@NotNull ClassId classId, @NotNull SourceElement source) {
090 FqName fqName = classId.asSingleFqName();
091 if (KOTLIN_INTERFACE_DEFAULT_IMPLS.equals(fqName)) {
092 isInterfaceDefaultImpls = true;
093 return null;
094 }
095 else if (KOTLIN_LOCAL_CLASS.equals(fqName)) {
096 isLocalClass = true;
097 return null;
098 }
099
100 if (headerKind != null) {
101 // Ignore all Kotlin annotations except the first found
102 return null;
103 }
104
105 KotlinClassHeader.Kind newKind = HEADER_KINDS.get(classId);
106 if (newKind != null) {
107 headerKind = newKind;
108
109 switch (newKind) {
110 case CLASS:
111 case PACKAGE_FACADE:
112 case FILE_FACADE:
113 case MULTIFILE_CLASS:
114 case MULTIFILE_CLASS_PART:
115 return new HeaderAnnotationArgumentVisitor();
116 case SYNTHETIC_CLASS:
117 return new SyntheticClassHeaderReader();
118 default:
119 return null;
120 }
121 }
122
123 return null;
124 }
125
126 @Override
127 public void visitEnd() {
128 }
129
130 private class HeaderAnnotationArgumentVisitor implements AnnotationArgumentVisitor {
131 @Override
132 public void visit(@Nullable Name name, @Nullable Object value) {
133 if (name == null) return;
134
135 String string = name.asString();
136 if (VERSION_FIELD_NAME.equals(string)) {
137 version = value instanceof int[] ? BinaryVersion.create((int[]) value) : AbiVersionUtil.INVALID_VERSION;
138 }
139 else if (MULTIFILE_CLASS_NAME_FIELD_NAME.equals(string)) {
140 multifileClassName = value instanceof String ? (String) value : null;
141 }
142 else if (OLD_ABI_VERSION_FIELD_NAME.equals(string)) {
143 if (version == AbiVersionUtil.INVALID_VERSION && value instanceof Integer && (Integer) value > 0) {
144 version = BinaryVersion.create(0, (Integer) value, 0);
145 }
146 }
147 }
148
149 @Override
150 @Nullable
151 public AnnotationArrayArgumentVisitor visitArray(@NotNull Name name) {
152 String string = name.asString();
153 if (DATA_FIELD_NAME.equals(string)) {
154 return dataArrayVisitor();
155 }
156 else if (STRINGS_FIELD_NAME.equals(string)) {
157 return stringsArrayVisitor();
158 }
159 else if (FILE_PART_CLASS_NAMES_FIELD_NAME.equals(string)) {
160 return filePartClassNamesVisitor();
161 }
162 else {
163 return null;
164 }
165 }
166
167 @NotNull
168 private AnnotationArrayArgumentVisitor filePartClassNamesVisitor() {
169 return new CollectStringArrayAnnotationVisitor() {
170 @Override
171 protected void visitEnd(@NotNull String[] data) {
172 filePartClassNames = data;
173 }
174 };
175 }
176
177 @NotNull
178 private AnnotationArrayArgumentVisitor dataArrayVisitor() {
179 return new CollectStringArrayAnnotationVisitor() {
180 @Override
181 protected void visitEnd(@NotNull String[] data) {
182 annotationData = data;
183 }
184 };
185 }
186
187 @NotNull
188 private AnnotationArrayArgumentVisitor stringsArrayVisitor() {
189 return new CollectStringArrayAnnotationVisitor() {
190 @Override
191 protected void visitEnd(@NotNull String[] data) {
192 strings = data;
193 }
194 };
195 }
196
197 @Override
198 public void visitEnum(@NotNull Name name, @NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
199 }
200
201 @Nullable
202 @Override
203 public AnnotationArgumentVisitor visitAnnotation(@NotNull Name name, @NotNull ClassId classId) {
204 return null;
205 }
206
207 @Override
208 public void visitEnd() {
209 }
210
211 private abstract class CollectStringArrayAnnotationVisitor implements AnnotationArrayArgumentVisitor {
212 private final List<String> strings;
213
214 public CollectStringArrayAnnotationVisitor() {
215 this.strings = new ArrayList<String>();
216 }
217
218 @Override
219 public void visit(@Nullable Object value) {
220 if (value instanceof String) {
221 strings.add((String) value);
222 }
223 }
224
225 @Override
226 public void visitEnum(@NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
227 }
228
229 @Override
230 public void visitEnd() {
231 //noinspection SSBasedInspection
232 visitEnd(strings.toArray(new String[strings.size()]));
233 }
234
235 protected abstract void visitEnd(@NotNull String[] data);
236 }
237 }
238
239 private class SyntheticClassHeaderReader extends HeaderAnnotationArgumentVisitor {
240 @Override
241 public void visitEnum(@NotNull Name name, @NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
242 if ("Kind".equals(enumClassId.getShortClassName().asString()) &&
243 enumClassId.isNestedClass() &&
244 enumClassId.getOuterClassId().equals(ClassId.topLevel(KOTLIN_SYNTHETIC_CLASS)) &&
245 "kind".equals(name.asString())) {
246 syntheticClassKind = enumEntryName.asString();
247 }
248 }
249 }
250 }