1 /* 2 * Copyright 2016 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.turbine.binder; 18 19 import static com.google.common.collect.Iterables.getOnlyElement; 20 21 import com.google.common.collect.ImmutableList; 22 import com.google.common.collect.ImmutableMap; 23 import com.google.common.collect.ImmutableSet; 24 import com.google.common.collect.Multimap; 25 import com.google.common.collect.MultimapBuilder; 26 import com.google.turbine.binder.bound.AnnotationMetadata; 27 import com.google.turbine.binder.bound.SourceTypeBoundClass; 28 import com.google.turbine.binder.bound.TurbineAnnotationValue; 29 import com.google.turbine.binder.bound.TypeBoundClass; 30 import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; 31 import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; 32 import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo; 33 import com.google.turbine.binder.env.Env; 34 import com.google.turbine.binder.sym.ClassSymbol; 35 import com.google.turbine.diag.TurbineError; 36 import com.google.turbine.diag.TurbineError.ErrorKind; 37 import com.google.turbine.model.Const; 38 import com.google.turbine.model.TurbineElementType; 39 import com.google.turbine.type.AnnoInfo; 40 import com.google.turbine.type.Type; 41 import com.google.turbine.type.Type.ArrayTy; 42 import com.google.turbine.type.Type.ClassTy; 43 import com.google.turbine.type.Type.ClassTy.SimpleClassTy; 44 import com.google.turbine.type.Type.PrimTy; 45 import com.google.turbine.type.Type.TyVar; 46 import java.util.Collection; 47 import java.util.Map; 48 49 /** 50 * Disambiguate annotations on field, parameter, and method return types that could be declaration 51 * or type annotations. 52 * 53 * <p>Given a declaration like {@code private @A int x;} or {@code @A private int x;}, there are 54 * three possibilities: 55 * 56 * <ol> 57 * <li>{@code @A} is a declaration annotation on the field. 58 * <li>{@code @A} is a {@code TYPE_USE} annotation on the type. 59 * <li>{@code @A} sets {@code TYPE_USE} <em>and</em> {@code FIELD} targets, and appears in the 60 * bytecode as both a declaration annotation and as a type annotation. 61 * </ol> 62 * 63 * <p>This can't be disambiguated syntactically (note that the presence of other modifiers before or 64 * after the annotation has no bearing on whether it's a type annotation). So, we wait until 65 * constant binding is done, read the {@code @Target} meta-annotation for each ambiguous annotation, 66 * and move it to the appropriate location. 67 */ 68 public class DisambiguateTypeAnnotations { bind( SourceTypeBoundClass base, Env<ClassSymbol, TypeBoundClass> env)69 public static SourceTypeBoundClass bind( 70 SourceTypeBoundClass base, Env<ClassSymbol, TypeBoundClass> env) { 71 return new SourceTypeBoundClass( 72 base.interfaceTypes(), 73 base.superClassType(), 74 base.typeParameterTypes(), 75 base.access(), 76 bindMethods(env, base.methods()), 77 bindFields(env, base.fields()), 78 base.owner(), 79 base.kind(), 80 base.children(), 81 base.typeParameters(), 82 base.enclosingScope(), 83 base.scope(), 84 base.memberImports(), 85 base.annotationMetadata(), 86 groupRepeated(env, base.annotations()), 87 base.source(), 88 base.decl()); 89 } 90 bindMethods( Env<ClassSymbol, TypeBoundClass> env, ImmutableList<MethodInfo> fields)91 private static ImmutableList<MethodInfo> bindMethods( 92 Env<ClassSymbol, TypeBoundClass> env, ImmutableList<MethodInfo> fields) { 93 ImmutableList.Builder<MethodInfo> result = ImmutableList.builder(); 94 for (MethodInfo field : fields) { 95 result.add(bindMethod(env, field)); 96 } 97 return result.build(); 98 } 99 bindMethod(Env<ClassSymbol, TypeBoundClass> env, MethodInfo base)100 private static MethodInfo bindMethod(Env<ClassSymbol, TypeBoundClass> env, MethodInfo base) { 101 ImmutableList.Builder<AnnoInfo> declarationAnnotations = ImmutableList.builder(); 102 Type returnType = 103 disambiguate( 104 env, 105 base.name().equals("<init>") 106 ? TurbineElementType.CONSTRUCTOR 107 : TurbineElementType.METHOD, 108 base.returnType(), 109 base.annotations(), 110 declarationAnnotations); 111 return new MethodInfo( 112 base.sym(), 113 base.tyParams(), 114 returnType, 115 bindParameters(env, base.parameters()), 116 base.exceptions(), 117 base.access(), 118 base.defaultValue(), 119 base.decl(), 120 declarationAnnotations.build(), 121 base.receiver() != null ? bindParam(env, base.receiver()) : null); 122 } 123 bindParameters( Env<ClassSymbol, TypeBoundClass> env, ImmutableList<ParamInfo> params)124 private static ImmutableList<ParamInfo> bindParameters( 125 Env<ClassSymbol, TypeBoundClass> env, ImmutableList<ParamInfo> params) { 126 ImmutableList.Builder<ParamInfo> result = ImmutableList.builder(); 127 for (ParamInfo param : params) { 128 result.add(bindParam(env, param)); 129 } 130 return result.build(); 131 } 132 bindParam(Env<ClassSymbol, TypeBoundClass> env, ParamInfo base)133 private static ParamInfo bindParam(Env<ClassSymbol, TypeBoundClass> env, ParamInfo base) { 134 ImmutableList.Builder<AnnoInfo> declarationAnnotations = ImmutableList.builder(); 135 Type type = 136 disambiguate( 137 env, 138 TurbineElementType.PARAMETER, 139 base.type(), 140 base.annotations(), 141 declarationAnnotations); 142 return new ParamInfo(base.sym(), type, declarationAnnotations.build(), base.access()); 143 } 144 145 /** 146 * Moves type annotations in {@code annotations} to {@code type}, and adds any declaration 147 * annotations on {@code type} to {@code declarationAnnotations}. 148 */ disambiguate( Env<ClassSymbol, TypeBoundClass> env, TurbineElementType declarationTarget, Type type, ImmutableList<AnnoInfo> annotations, ImmutableList.Builder<AnnoInfo> declarationAnnotations)149 private static Type disambiguate( 150 Env<ClassSymbol, TypeBoundClass> env, 151 TurbineElementType declarationTarget, 152 Type type, 153 ImmutableList<AnnoInfo> annotations, 154 ImmutableList.Builder<AnnoInfo> declarationAnnotations) { 155 // desugar @Repeatable annotations before disambiguating: annotation containers may target 156 // a subset of the types targeted by their element annotation 157 annotations = groupRepeated(env, annotations); 158 ImmutableList.Builder<AnnoInfo> typeAnnotations = ImmutableList.builder(); 159 for (AnnoInfo anno : annotations) { 160 ImmutableSet<TurbineElementType> target = getTarget(env, anno); 161 if (target.contains(TurbineElementType.TYPE_USE)) { 162 typeAnnotations.add(anno); 163 } 164 if (target.contains(declarationTarget)) { 165 declarationAnnotations.add(anno); 166 } 167 } 168 return addAnnotationsToType(type, typeAnnotations.build()); 169 } 170 getTarget( Env<ClassSymbol, TypeBoundClass> env, AnnoInfo anno)171 private static ImmutableSet<TurbineElementType> getTarget( 172 Env<ClassSymbol, TypeBoundClass> env, AnnoInfo anno) { 173 ClassSymbol sym = anno.sym(); 174 if (sym == null) { 175 return AnnotationMetadata.DEFAULT_TARGETS; 176 } 177 TypeBoundClass info = env.get(sym); 178 if (info == null) { 179 return AnnotationMetadata.DEFAULT_TARGETS; 180 } 181 AnnotationMetadata metadata = info.annotationMetadata(); 182 if (metadata == null) { 183 return AnnotationMetadata.DEFAULT_TARGETS; 184 } 185 return metadata.target(); 186 } 187 bindFields( Env<ClassSymbol, TypeBoundClass> env, ImmutableList<FieldInfo> fields)188 private static ImmutableList<FieldInfo> bindFields( 189 Env<ClassSymbol, TypeBoundClass> env, ImmutableList<FieldInfo> fields) { 190 ImmutableList.Builder<FieldInfo> result = ImmutableList.builder(); 191 for (FieldInfo field : fields) { 192 result.add(bindField(env, field)); 193 } 194 return result.build(); 195 } 196 bindField(Env<ClassSymbol, TypeBoundClass> env, FieldInfo base)197 private static FieldInfo bindField(Env<ClassSymbol, TypeBoundClass> env, FieldInfo base) { 198 ImmutableList.Builder<AnnoInfo> declarationAnnotations = ImmutableList.builder(); 199 Type type = 200 disambiguate( 201 env, TurbineElementType.FIELD, base.type(), base.annotations(), declarationAnnotations); 202 return new FieldInfo( 203 base.sym(), type, base.access(), declarationAnnotations.build(), base.decl(), base.value()); 204 } 205 206 /** 207 * Finds the left-most annotatable type in {@code type}, adds the {@code extra} type annotations 208 * to it, and removes any declaration annotations and saves them in {@code removed}. 209 * 210 * <p>The left-most type is e.g. the element type of an array, or the left-most type in a nested 211 * type declaration. 212 * 213 * <p>Note: the second case means that type annotation disambiguation has to occur on nested types 214 * before they are canonicalized. 215 */ addAnnotationsToType(Type type, ImmutableList<AnnoInfo> extra)216 private static Type addAnnotationsToType(Type type, ImmutableList<AnnoInfo> extra) { 217 switch (type.tyKind()) { 218 case PRIM_TY: 219 PrimTy primTy = (PrimTy) type; 220 return Type.PrimTy.create(primTy.primkind(), appendAnnotations(primTy.annos(), extra)); 221 case CLASS_TY: 222 ClassTy classTy = (ClassTy) type; 223 SimpleClassTy base = classTy.classes().get(0); 224 SimpleClassTy simple = 225 SimpleClassTy.create(base.sym(), base.targs(), appendAnnotations(base.annos(), extra)); 226 return Type.ClassTy.create( 227 ImmutableList.<SimpleClassTy>builder() 228 .add(simple) 229 .addAll(classTy.classes().subList(1, classTy.classes().size())) 230 .build()); 231 case ARRAY_TY: 232 ArrayTy arrayTy = (ArrayTy) type; 233 return ArrayTy.create(addAnnotationsToType(arrayTy.elementType(), extra), arrayTy.annos()); 234 case TY_VAR: 235 TyVar tyVar = (TyVar) type; 236 return Type.TyVar.create(tyVar.sym(), appendAnnotations(tyVar.annos(), extra)); 237 case VOID_TY: 238 case ERROR_TY: 239 return type; 240 case WILD_TY: 241 throw new AssertionError("unexpected wildcard type outside type argument context"); 242 default: 243 throw new AssertionError(type.tyKind()); 244 } 245 } 246 appendAnnotations( ImmutableList<AnnoInfo> annos, ImmutableList<AnnoInfo> extra)247 private static ImmutableList<AnnoInfo> appendAnnotations( 248 ImmutableList<AnnoInfo> annos, ImmutableList<AnnoInfo> extra) { 249 return ImmutableList.<AnnoInfo>builder().addAll(annos).addAll(extra).build(); 250 } 251 252 /** 253 * Group repeated annotations and wrap them in their container annotation. 254 * 255 * <p>For example, convert {@code @Foo @Foo} to {@code @Foos({@Foo, @Foo})}. 256 * 257 * <p>This method is used by {@link DisambiguateTypeAnnotations} for declaration annotations, and 258 * by {@link com.google.turbine.lower.Lower} for type annotations. We could group type annotations 259 * here, but it would require another rewrite pass. 260 */ groupRepeated( Env<ClassSymbol, TypeBoundClass> env, ImmutableList<AnnoInfo> annotations)261 public static ImmutableList<AnnoInfo> groupRepeated( 262 Env<ClassSymbol, TypeBoundClass> env, ImmutableList<AnnoInfo> annotations) { 263 Multimap<ClassSymbol, AnnoInfo> repeated = 264 MultimapBuilder.linkedHashKeys().arrayListValues().build(); 265 ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder(); 266 for (AnnoInfo anno : annotations) { 267 if (anno.sym() == null) { 268 result.add(anno); 269 continue; 270 } 271 repeated.put(anno.sym(), anno); 272 } 273 for (Map.Entry<ClassSymbol, Collection<AnnoInfo>> entry : repeated.asMap().entrySet()) { 274 ClassSymbol symbol = entry.getKey(); 275 Collection<AnnoInfo> infos = entry.getValue(); 276 if (infos.size() > 1) { 277 ImmutableList.Builder<Const> elements = ImmutableList.builder(); 278 for (AnnoInfo element : infos) { 279 elements.add(new TurbineAnnotationValue(element)); 280 } 281 TypeBoundClass info = env.get(symbol); 282 if (info == null || info.annotationMetadata() == null) { 283 continue; 284 } 285 ClassSymbol container = info.annotationMetadata().repeatable(); 286 if (container == null) { 287 if (isKotlinRepeatable(info)) { 288 continue; 289 } 290 AnnoInfo anno = infos.iterator().next(); 291 throw TurbineError.format( 292 anno.source(), anno.position(), ErrorKind.NONREPEATABLE_ANNOTATION, symbol); 293 } 294 result.add( 295 new AnnoInfo( 296 null, 297 container, 298 null, 299 ImmutableMap.of("value", new Const.ArrayInitValue(elements.build())))); 300 } else { 301 result.add(getOnlyElement(infos)); 302 } 303 } 304 return result.build(); 305 } 306 307 // Work-around for https://youtrack.jetbrains.net/issue/KT-34189. 308 // Kotlin stubs include repeated annotations that are valid in Kotlin (i.e. meta-annotated with 309 // @kotlin.annotation.Repeatable), even though they are invalid Java. 310 // TODO(b/142002426): kill this with fire isKotlinRepeatable(TypeBoundClass info)311 static boolean isKotlinRepeatable(TypeBoundClass info) { 312 for (AnnoInfo metaAnno : info.annotations()) { 313 if (metaAnno.sym() != null 314 && metaAnno.sym().binaryName().equals("kotlin/annotation/Repeatable")) { 315 return true; 316 } 317 } 318 return false; 319 } 320 } 321