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 }