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(AnnotationDescriptor annotationDescriptor) {
147 ClassifierDescriptor classifierDescriptor = annotationDescriptor.getType().getConstructor().getDeclarationDescriptor();
148 RetentionPolicy rp = getRetentionPolicy(classifierDescriptor, typeMapper);
149 if (rp == RetentionPolicy.SOURCE) {
150 return null;
151 }
152
153 String descriptor = typeMapper.mapType(annotationDescriptor.getType()).getDescriptor();
154 AnnotationVisitor annotationVisitor = visitAnnotation(descriptor, rp == RetentionPolicy.RUNTIME);
155
156 genAnnotationArguments(annotationDescriptor, annotationVisitor);
157 annotationVisitor.visitEnd();
158
159 return descriptor;
160 }
161
162 private void genAnnotationArguments(AnnotationDescriptor annotationDescriptor, AnnotationVisitor annotationVisitor) {
163 for (Map.Entry<ValueParameterDescriptor, CompileTimeConstant<?>> entry : annotationDescriptor.getAllValueArguments().entrySet()) {
164 ValueParameterDescriptor descriptor = entry.getKey();
165 String name = descriptor.getName().asString();
166 genCompileTimeValue(name, entry.getValue(), annotationVisitor);
167 }
168 }
169
170 private void genCompileTimeValue(
171 @Nullable final String name,
172 @NotNull CompileTimeConstant<?> value,
173 @NotNull final AnnotationVisitor annotationVisitor
174 ) {
175 AnnotationArgumentVisitor argumentVisitor = new AnnotationArgumentVisitor<Void, Void>() {
176 @Override
177 public Void visitLongValue(@NotNull LongValue value, Void data) {
178 return visitSimpleValue(value);
179 }
180
181 @Override
182 public Void visitIntValue(IntValue value, Void data) {
183 return visitSimpleValue(value);
184 }
185
186 @Override
187 public Void visitShortValue(ShortValue value, Void data) {
188 return visitSimpleValue(value);
189 }
190
191 @Override
192 public Void visitByteValue(ByteValue value, Void data) {
193 return visitSimpleValue(value);
194 }
195
196 @Override
197 public Void visitDoubleValue(DoubleValue value, Void data) {
198 return visitSimpleValue(value);
199 }
200
201 @Override
202 public Void visitFloatValue(FloatValue value, Void data) {
203 return visitSimpleValue(value);
204 }
205
206 @Override
207 public Void visitBooleanValue(BooleanValue value, Void data) {
208 return visitSimpleValue(value);
209 }
210
211 @Override
212 public Void visitCharValue(CharValue value, Void data) {
213 return visitSimpleValue(value);
214 }
215
216 @Override
217 public Void visitStringValue(StringValue value, Void data) {
218 return visitSimpleValue(value);
219 }
220
221 @Override
222 public Void visitEnumValue(EnumValue value, Void data) {
223 String propertyName = value.getValue().getName().asString();
224 annotationVisitor.visitEnum(name, typeMapper.mapType(value.getType(KotlinBuiltIns.getInstance())).getDescriptor(), propertyName);
225 return null;
226 }
227
228 @Override
229 public Void visitArrayValue(ArrayValue value, Void data) {
230 AnnotationVisitor visitor = annotationVisitor.visitArray(name);
231 for (CompileTimeConstant<?> argument : value.getValue()) {
232 genCompileTimeValue(null, argument, visitor);
233 }
234 visitor.visitEnd();
235 return null;
236 }
237
238 @Override
239 public Void visitAnnotationValue(AnnotationValue value, Void data) {
240 String internalAnnName = typeMapper.mapType(value.getValue().getType()).getDescriptor();
241 AnnotationVisitor visitor = annotationVisitor.visitAnnotation(name, internalAnnName);
242 genAnnotationArguments(value.getValue(), visitor);
243 visitor.visitEnd();
244 return null;
245 }
246
247 @Override
248 public Void visitJavaClassValue(JavaClassValue value, Void data) {
249 annotationVisitor.visit(name, typeMapper.mapType(value.getValue()));
250 return null;
251 }
252
253 private Void visitSimpleValue(CompileTimeConstant value) {
254 annotationVisitor.visit(name, value.getValue());
255 return null;
256 }
257
258 @Override
259 public Void visitErrorValue(ErrorValue value, Void data) {
260 return visitUnsupportedValue(value);
261 }
262
263 @Override
264 public Void visitNullValue(NullValue value, Void data) {
265 return visitUnsupportedValue(value);
266 }
267
268 private Void visitUnsupportedValue(CompileTimeConstant value) {
269 throw new IllegalStateException("Don't know how to compile annotation value " + value);
270 }
271 };
272
273 value.accept(argumentVisitor, null);
274 }
275
276 private static RetentionPolicy getRetentionPolicy(ClassifierDescriptor descriptor, JetTypeMapper typeMapper) {
277 for (AnnotationDescriptor annotationDescriptor : descriptor.getAnnotations()) {
278 String internalName = typeMapper.mapType(annotationDescriptor.getType()).getInternalName();
279 if("java/lang/annotation/Retention".equals(internalName)) {
280 CompileTimeConstant<?> compileTimeConstant = annotationDescriptor.getAllValueArguments().values().iterator().next();
281 assert compileTimeConstant instanceof EnumValue : "Retention argument should be Enum value " + compileTimeConstant;
282 PropertyDescriptor propertyDescriptor = ((EnumValue) compileTimeConstant).getValue();
283 assert "java/lang/annotation/RetentionPolicy".equals(typeMapper.mapType(propertyDescriptor.getType()).getInternalName()) :
284 "Retention argument should be of type RetentionPolicy";
285 String propertyDescriptorName = propertyDescriptor.getName().asString();
286 return RetentionPolicy.valueOf(propertyDescriptorName);
287 }
288 }
289
290 return RetentionPolicy.CLASS;
291 }
292
293 @NotNull
294 abstract AnnotationVisitor visitAnnotation(String descr, boolean visible);
295
296 public static AnnotationCodegen forClass(final ClassVisitor cv, JetTypeMapper mapper) {
297 return new AnnotationCodegen(mapper) {
298 @NotNull
299 @Override
300 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
301 return safe(cv.visitAnnotation(descr, visible));
302 }
303 };
304 }
305
306 public static AnnotationCodegen forMethod(final MethodVisitor mv, JetTypeMapper mapper) {
307 return new AnnotationCodegen(mapper) {
308 @NotNull
309 @Override
310 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
311 return safe(mv.visitAnnotation(descr, visible));
312 }
313 };
314 }
315
316 public static AnnotationCodegen forField(final FieldVisitor fv, JetTypeMapper mapper) {
317 return new AnnotationCodegen(mapper) {
318 @NotNull
319 @Override
320 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
321 return safe(fv.visitAnnotation(descr, visible));
322 }
323 };
324 }
325
326 public static AnnotationCodegen forParameter(final int parameter, final MethodVisitor mv, JetTypeMapper mapper) {
327 return new AnnotationCodegen(mapper) {
328 @NotNull
329 @Override
330 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
331 return safe(mv.visitParameterAnnotation(parameter, descr, visible));
332 }
333 };
334 }
335
336 public static AnnotationCodegen forAnnotationDefaultValue(final MethodVisitor mv, JetTypeMapper mapper) {
337 return new AnnotationCodegen(mapper) {
338 @NotNull
339 @Override
340 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
341 return safe(mv.visitAnnotationDefault());
342 }
343 };
344 }
345
346 @NotNull
347 private static AnnotationVisitor safe(@Nullable AnnotationVisitor av) {
348 return av == null ? NO_ANNOTATION_VISITOR : av;
349 }
350 }