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.codegen;
018
019 import com.intellij.psi.PsiElement;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.asm4.*;
023 import org.jetbrains.jet.codegen.state.JetTypeMapper;
024 import org.jetbrains.jet.lang.descriptors.*;
025 import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
026 import org.jetbrains.jet.lang.descriptors.annotations.AnnotationArgumentVisitor;
027 import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
028 import org.jetbrains.jet.lang.psi.JetAnnotationEntry;
029 import org.jetbrains.jet.lang.psi.JetClass;
030 import org.jetbrains.jet.lang.psi.JetModifierList;
031 import org.jetbrains.jet.lang.psi.JetModifierListOwner;
032 import org.jetbrains.jet.lang.resolve.BindingContext;
033 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
034 import org.jetbrains.jet.lang.resolve.constants.*;
035 import org.jetbrains.jet.lang.resolve.constants.StringValue;
036 import org.jetbrains.jet.lang.types.JetType;
037 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
038
039 import java.lang.annotation.RetentionPolicy;
040 import java.util.*;
041
042 import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration;
043
044 public abstract class AnnotationCodegen {
045 private static final AnnotationVisitor NO_ANNOTATION_VISITOR = new AnnotationVisitor(Opcodes.ASM4) {};
046
047 private final JetTypeMapper typeMapper;
048 private final BindingContext bindingContext;
049
050 private AnnotationCodegen(JetTypeMapper mapper) {
051 typeMapper = mapper;
052 bindingContext = typeMapper.getBindingContext();
053 }
054
055 public void genAnnotations(Annotated annotated) {
056 if (annotated == null) {
057 return;
058 }
059
060 if (!(annotated instanceof DeclarationDescriptor)) {
061 return;
062 }
063
064 PsiElement psiElement = descriptorToDeclaration(bindingContext, (DeclarationDescriptor) annotated);
065
066 JetModifierList modifierList = null;
067 if (annotated instanceof ConstructorDescriptor && psiElement instanceof JetClass) {
068 modifierList = ((JetClass) psiElement).getPrimaryConstructorModifierList();
069 }
070 else if (psiElement instanceof JetModifierListOwner) {
071 modifierList = ((JetModifierListOwner) psiElement).getModifierList();
072 }
073
074 if (modifierList == null) {
075 generateAdditionalAnnotations(annotated, Collections.<String>emptySet());
076 return;
077 }
078
079 Set<String> annotationDescriptorsAlreadyPresent = new HashSet<String>();
080
081 List<JetAnnotationEntry> annotationEntries = modifierList.getAnnotationEntries();
082 for (JetAnnotationEntry annotationEntry : annotationEntries) {
083 ResolvedCall<? extends CallableDescriptor> resolvedCall =
084 bindingContext.get(BindingContext.RESOLVED_CALL, annotationEntry.getCalleeExpression());
085 if (resolvedCall == null) continue; // Skipping annotations if they are not resolved. Needed for JetLightClass generation
086
087 AnnotationDescriptor annotationDescriptor = bindingContext.get(BindingContext.ANNOTATION, annotationEntry);
088 if (annotationDescriptor == null) continue; // Skipping annotations if they are not resolved. Needed for JetLightClass generation
089 if (isVolatile(annotationDescriptor)) continue;
090
091 String descriptor = genAnnotation(annotationDescriptor);
092 if (descriptor != null) {
093 annotationDescriptorsAlreadyPresent.add(descriptor);
094 }
095 }
096
097 generateAdditionalAnnotations(annotated, annotationDescriptorsAlreadyPresent);
098 }
099
100 private void generateAdditionalAnnotations(@NotNull Annotated annotated, @NotNull Set<String> annotationDescriptorsAlreadyPresent) {
101 if (annotated instanceof CallableDescriptor) {
102 CallableDescriptor descriptor = (CallableDescriptor) annotated;
103
104 // No need to annotate privates, synthetic accessors and their parameters
105 if (isInvisibleFromTheOutside(descriptor)) return;
106 if (descriptor instanceof ValueParameterDescriptor && isInvisibleFromTheOutside(descriptor.getContainingDeclaration())) return;
107
108 generateNullabilityAnnotation(descriptor.getReturnType(), annotationDescriptorsAlreadyPresent);
109 }
110 }
111
112 private static boolean isInvisibleFromTheOutside(@Nullable DeclarationDescriptor descriptor) {
113 if (descriptor instanceof CallableMemberDescriptor && JetTypeMapper.isAccessor((CallableMemberDescriptor) descriptor)) return false;
114 if (descriptor instanceof MemberDescriptor) {
115 return AsmUtil.getVisibilityAccessFlag((MemberDescriptor) descriptor) == Opcodes.ACC_PRIVATE;
116 }
117 return false;
118 }
119
120 private void generateNullabilityAnnotation(@Nullable JetType type, @NotNull Set<String> annotationDescriptorsAlreadyPresent) {
121 if (type == null) return;
122
123 boolean isNullableType = CodegenUtil.isNullableType(type);
124 if (!isNullableType && KotlinBuiltIns.getInstance().isPrimitiveType(type)) return;
125
126 Class<?> annotationClass = isNullableType ? Nullable.class : NotNull.class;
127
128 String descriptor = Type.getType(annotationClass).getDescriptor();
129 if (!annotationDescriptorsAlreadyPresent.contains(descriptor)) {
130 visitAnnotation(descriptor, false).visitEnd();
131 }
132 }
133
134 private static boolean isVolatile(@NotNull AnnotationDescriptor annotationDescriptor) {
135 ClassifierDescriptor classDescriptor = annotationDescriptor.getType().getConstructor().getDeclarationDescriptor();
136 return KotlinBuiltIns.getInstance().getVolatileAnnotationClass().equals(classDescriptor);
137 }
138
139 public void generateAnnotationDefaultValue(CompileTimeConstant value) {
140 AnnotationVisitor visitor = visitAnnotation(null, false); // Parameters are unimportant
141 genCompileTimeValue(null, value, visitor);
142 visitor.visitEnd();
143 }
144
145 @Nullable
146 private String genAnnotation(@NotNull AnnotationDescriptor annotationDescriptor) {
147 ClassifierDescriptor classifierDescriptor = annotationDescriptor.getType().getConstructor().getDeclarationDescriptor();
148 assert classifierDescriptor != null : "Annotation descriptor has no class: " + annotationDescriptor;
149 RetentionPolicy rp = getRetentionPolicy(classifierDescriptor);
150 if (rp == RetentionPolicy.SOURCE) {
151 return null;
152 }
153
154 String descriptor = typeMapper.mapType(annotationDescriptor.getType()).getDescriptor();
155 AnnotationVisitor annotationVisitor = visitAnnotation(descriptor, rp == RetentionPolicy.RUNTIME);
156
157 genAnnotationArguments(annotationDescriptor, annotationVisitor);
158 annotationVisitor.visitEnd();
159
160 return descriptor;
161 }
162
163 private void genAnnotationArguments(AnnotationDescriptor annotationDescriptor, AnnotationVisitor annotationVisitor) {
164 for (Map.Entry<ValueParameterDescriptor, CompileTimeConstant<?>> entry : annotationDescriptor.getAllValueArguments().entrySet()) {
165 ValueParameterDescriptor descriptor = entry.getKey();
166 String name = descriptor.getName().asString();
167 genCompileTimeValue(name, entry.getValue(), annotationVisitor);
168 }
169 }
170
171 private void genCompileTimeValue(
172 @Nullable final String name,
173 @NotNull CompileTimeConstant<?> value,
174 @NotNull final AnnotationVisitor annotationVisitor
175 ) {
176 AnnotationArgumentVisitor argumentVisitor = new AnnotationArgumentVisitor<Void, Void>() {
177 @Override
178 public Void visitLongValue(@NotNull LongValue value, Void data) {
179 return visitSimpleValue(value);
180 }
181
182 @Override
183 public Void visitIntValue(IntValue value, Void data) {
184 return visitSimpleValue(value);
185 }
186
187 @Override
188 public Void visitShortValue(ShortValue value, Void data) {
189 return visitSimpleValue(value);
190 }
191
192 @Override
193 public Void visitByteValue(ByteValue value, Void data) {
194 return visitSimpleValue(value);
195 }
196
197 @Override
198 public Void visitDoubleValue(DoubleValue value, Void data) {
199 return visitSimpleValue(value);
200 }
201
202 @Override
203 public Void visitFloatValue(FloatValue value, Void data) {
204 return visitSimpleValue(value);
205 }
206
207 @Override
208 public Void visitBooleanValue(BooleanValue value, Void data) {
209 return visitSimpleValue(value);
210 }
211
212 @Override
213 public Void visitCharValue(CharValue value, Void data) {
214 return visitSimpleValue(value);
215 }
216
217 @Override
218 public Void visitStringValue(StringValue value, Void data) {
219 return visitSimpleValue(value);
220 }
221
222 @Override
223 public Void visitEnumValue(EnumValue value, Void data) {
224 String propertyName = value.getValue().getName().asString();
225 annotationVisitor.visitEnum(name, typeMapper.mapType(value.getType(KotlinBuiltIns.getInstance())).getDescriptor(), propertyName);
226 return null;
227 }
228
229 @Override
230 public Void visitArrayValue(ArrayValue value, Void data) {
231 AnnotationVisitor visitor = annotationVisitor.visitArray(name);
232 for (CompileTimeConstant<?> argument : value.getValue()) {
233 genCompileTimeValue(null, argument, visitor);
234 }
235 visitor.visitEnd();
236 return null;
237 }
238
239 @Override
240 public Void visitAnnotationValue(AnnotationValue value, Void data) {
241 String internalAnnName = typeMapper.mapType(value.getValue().getType()).getDescriptor();
242 AnnotationVisitor visitor = annotationVisitor.visitAnnotation(name, internalAnnName);
243 genAnnotationArguments(value.getValue(), visitor);
244 visitor.visitEnd();
245 return null;
246 }
247
248 @Override
249 public Void visitJavaClassValue(JavaClassValue value, Void data) {
250 annotationVisitor.visit(name, typeMapper.mapType(value.getValue()));
251 return null;
252 }
253
254 private Void visitSimpleValue(CompileTimeConstant value) {
255 annotationVisitor.visit(name, value.getValue());
256 return null;
257 }
258
259 @Override
260 public Void visitErrorValue(ErrorValue value, Void data) {
261 return visitUnsupportedValue(value);
262 }
263
264 @Override
265 public Void visitNullValue(NullValue value, Void data) {
266 return visitUnsupportedValue(value);
267 }
268
269 private Void visitUnsupportedValue(CompileTimeConstant value) {
270 throw new IllegalStateException("Don't know how to compile annotation value " + value);
271 }
272 };
273
274 value.accept(argumentVisitor, null);
275 }
276
277 @NotNull
278 private RetentionPolicy getRetentionPolicy(@NotNull Annotated descriptor) {
279 for (AnnotationDescriptor annotationDescriptor : descriptor.getAnnotations()) {
280 String internalName = typeMapper.mapType(annotationDescriptor.getType()).getInternalName();
281 if ("java/lang/annotation/Retention".equals(internalName)) {
282 Collection<CompileTimeConstant<?>> valueArguments = annotationDescriptor.getAllValueArguments().values();
283 assert valueArguments.size() == 1 : "Retention should have an argument: " + annotationDescriptor;
284 CompileTimeConstant<?> compileTimeConstant = valueArguments.iterator().next();
285 assert compileTimeConstant instanceof EnumValue : "Retention argument should be enum value: " + compileTimeConstant;
286 ClassDescriptor enumEntry = ((EnumValue) compileTimeConstant).getValue();
287 JetType classObjectType = enumEntry.getClassObjectType();
288 assert classObjectType != null : "Enum entry should have a class object: " + enumEntry;
289 assert "java/lang/annotation/RetentionPolicy".equals(typeMapper.mapType(classObjectType).getInternalName()) :
290 "Retention argument should be of type RetentionPolicy: " + enumEntry;
291 return RetentionPolicy.valueOf(enumEntry.getName().asString());
292 }
293 }
294
295 return RetentionPolicy.CLASS;
296 }
297
298 @NotNull
299 abstract AnnotationVisitor visitAnnotation(String descr, boolean visible);
300
301 public static AnnotationCodegen forClass(final ClassVisitor cv, JetTypeMapper mapper) {
302 return new AnnotationCodegen(mapper) {
303 @NotNull
304 @Override
305 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
306 return safe(cv.visitAnnotation(descr, visible));
307 }
308 };
309 }
310
311 public static AnnotationCodegen forMethod(final MethodVisitor mv, JetTypeMapper mapper) {
312 return new AnnotationCodegen(mapper) {
313 @NotNull
314 @Override
315 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
316 return safe(mv.visitAnnotation(descr, visible));
317 }
318 };
319 }
320
321 public static AnnotationCodegen forField(final FieldVisitor fv, JetTypeMapper mapper) {
322 return new AnnotationCodegen(mapper) {
323 @NotNull
324 @Override
325 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
326 return safe(fv.visitAnnotation(descr, visible));
327 }
328 };
329 }
330
331 public static AnnotationCodegen forParameter(final int parameter, final MethodVisitor mv, JetTypeMapper mapper) {
332 return new AnnotationCodegen(mapper) {
333 @NotNull
334 @Override
335 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
336 return safe(mv.visitParameterAnnotation(parameter, descr, visible));
337 }
338 };
339 }
340
341 public static AnnotationCodegen forAnnotationDefaultValue(final MethodVisitor mv, JetTypeMapper mapper) {
342 return new AnnotationCodegen(mapper) {
343 @NotNull
344 @Override
345 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
346 return safe(mv.visitAnnotationDefault());
347 }
348 };
349 }
350
351 @NotNull
352 private static AnnotationVisitor safe(@Nullable AnnotationVisitor av) {
353 return av == null ? NO_ANNOTATION_VISITOR : av;
354 }
355 }