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.ImmutableList.Builder; 23 import com.google.common.collect.ImmutableMap; 24 import com.google.common.collect.LinkedHashMultimap; 25 import com.google.common.collect.Multimap; 26 import com.google.turbine.binder.bound.AnnotationValue; 27 import com.google.turbine.binder.bound.SourceTypeBoundClass; 28 import com.google.turbine.binder.bound.TypeBoundClass; 29 import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; 30 import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; 31 import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo; 32 import com.google.turbine.binder.env.Env; 33 import com.google.turbine.binder.sym.ClassSymbol; 34 import com.google.turbine.diag.TurbineError; 35 import com.google.turbine.diag.TurbineError.ErrorKind; 36 import com.google.turbine.model.Const; 37 import com.google.turbine.type.AnnoInfo; 38 import com.google.turbine.type.Type; 39 import com.google.turbine.type.Type.ArrayTy; 40 import com.google.turbine.type.Type.ClassTy; 41 import com.google.turbine.type.Type.ClassTy.SimpleClassTy; 42 import com.google.turbine.type.Type.PrimTy; 43 import com.google.turbine.type.Type.TyVar; 44 import java.lang.annotation.ElementType; 45 import java.util.Collection; 46 import java.util.Map; 47 import java.util.Set; 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 { 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 } 89 90 private static ImmutableList<MethodInfo> bindMethods( 91 Env<ClassSymbol, TypeBoundClass> env, ImmutableList<MethodInfo> fields) { 92 ImmutableList.Builder<MethodInfo> result = ImmutableList.builder(); 93 for (MethodInfo field : fields) { 94 result.add(bindMethod(env, field)); 95 } 96 return result.build(); 97 } 98 99 private static MethodInfo bindMethod(Env<ClassSymbol, TypeBoundClass> env, MethodInfo base) { 100 ImmutableList.Builder<AnnoInfo> declarationAnnotations = ImmutableList.builder(); 101 Type returnType = 102 disambiguate( 103 env, 104 base.name().equals("<init>") ? ElementType.CONSTRUCTOR : ElementType.METHOD, 105 base.returnType(), 106 base.annotations(), 107 declarationAnnotations); 108 return new MethodInfo( 109 base.sym(), 110 base.tyParams(), 111 returnType, 112 bindParameters(env, base.parameters()), 113 base.exceptions(), 114 base.access(), 115 base.defaultValue(), 116 base.decl(), 117 declarationAnnotations.build(), 118 base.receiver() != null ? bindParam(env, base.receiver()) : null); 119 } 120 121 private static ImmutableList<ParamInfo> bindParameters( 122 Env<ClassSymbol, TypeBoundClass> env, ImmutableList<ParamInfo> params) { 123 ImmutableList.Builder<ParamInfo> result = ImmutableList.builder(); 124 for (ParamInfo param : params) { 125 result.add(bindParam(env, param)); 126 } 127 return result.build(); 128 } 129 130 private static ParamInfo bindParam(Env<ClassSymbol, TypeBoundClass> env, ParamInfo base) { 131 ImmutableList.Builder<AnnoInfo> declarationAnnotations = ImmutableList.builder(); 132 Type type = 133 disambiguate( 134 env, ElementType.PARAMETER, base.type(), base.annotations(), declarationAnnotations); 135 return new ParamInfo(type, base.name(), declarationAnnotations.build(), base.access()); 136 } 137 138 /** 139 * Moves type annotations in {@code annotations} to {@code type}, and adds any declaration 140 * annotations on {@code type} to {@code declarationAnnotations}. 141 */ 142 private static Type disambiguate( 143 Env<ClassSymbol, TypeBoundClass> env, 144 ElementType declarationTarget, 145 Type type, 146 ImmutableList<AnnoInfo> annotations, 147 Builder<AnnoInfo> declarationAnnotations) { 148 // desugar @Repeatable annotations before disambiguating: annotation containers may target 149 // a subset of the types targeted by their element annotation 150 annotations = groupRepeated(env, annotations); 151 ImmutableList.Builder<AnnoInfo> typeAnnotations = ImmutableList.builder(); 152 for (AnnoInfo anno : annotations) { 153 Set<ElementType> target = env.get(anno.sym()).annotationMetadata().target(); 154 if (target.contains(ElementType.TYPE_USE)) { 155 typeAnnotations.add(anno); 156 } 157 if (target.contains(declarationTarget)) { 158 declarationAnnotations.add(anno); 159 } 160 } 161 return addAnnotationsToType(type, typeAnnotations.build()); 162 } 163 164 private static ImmutableList<FieldInfo> bindFields( 165 Env<ClassSymbol, TypeBoundClass> env, ImmutableList<FieldInfo> fields) { 166 ImmutableList.Builder<FieldInfo> result = ImmutableList.builder(); 167 for (FieldInfo field : fields) { 168 result.add(bindField(env, field)); 169 } 170 return result.build(); 171 } 172 173 private static FieldInfo bindField(Env<ClassSymbol, TypeBoundClass> env, FieldInfo base) { 174 ImmutableList.Builder<AnnoInfo> declarationAnnotations = ImmutableList.builder(); 175 Type type = 176 disambiguate( 177 env, ElementType.FIELD, base.type(), base.annotations(), declarationAnnotations); 178 return new FieldInfo( 179 base.sym(), type, base.access(), declarationAnnotations.build(), base.decl(), base.value()); 180 } 181 182 /** 183 * Finds the left-most annotatable type in {@code type}, adds the {@code extra} type annotations 184 * to it, and removes any declaration annotations and saves them in {@code removed}. 185 * 186 * <p>The left-most type is e.g. the element type of an array, or the left-most type in a nested 187 * type declaration. 188 * 189 * <p>Note: the second case means that type annotation disambiguation has to occur on nested types 190 * before they are canonicalized. 191 */ 192 private static Type addAnnotationsToType(Type type, ImmutableList<AnnoInfo> extra) { 193 switch (type.tyKind()) { 194 case PRIM_TY: 195 PrimTy primTy = (PrimTy) type; 196 return new Type.PrimTy(primTy.primkind(), appendAnnotations(primTy.annos(), extra)); 197 case CLASS_TY: 198 ClassTy classTy = (ClassTy) type; 199 SimpleClassTy base = classTy.classes.get(0); 200 SimpleClassTy simple = 201 new SimpleClassTy(base.sym(), base.targs(), appendAnnotations(base.annos(), extra)); 202 return new Type.ClassTy( 203 ImmutableList.<SimpleClassTy>builder() 204 .add(simple) 205 .addAll(classTy.classes.subList(1, classTy.classes.size())) 206 .build()); 207 case ARRAY_TY: 208 ArrayTy arrayTy = (ArrayTy) type; 209 return new ArrayTy(addAnnotationsToType(arrayTy.elementType(), extra), arrayTy.annos()); 210 case TY_VAR: 211 TyVar tyVar = (TyVar) type; 212 return new Type.TyVar(tyVar.sym(), appendAnnotations(tyVar.annos(), extra)); 213 case VOID_TY: 214 return type; 215 case WILD_TY: 216 throw new AssertionError("unexpected wildcard type outside type argument context"); 217 default: 218 throw new AssertionError(type.tyKind()); 219 } 220 } 221 222 private static ImmutableList<AnnoInfo> appendAnnotations( 223 ImmutableList<AnnoInfo> annos, ImmutableList<AnnoInfo> extra) { 224 return ImmutableList.<AnnoInfo>builder().addAll(annos).addAll(extra).build(); 225 } 226 227 /** 228 * Group repeated annotations and wrap them in their container annotation. 229 * 230 * <p>For example, convert {@code @Foo @Foo} to {@code @Foos({@Foo, @Foo})}. 231 * 232 * <p>This method is used by {@link DisambiguateTypeAnnotations} for declaration annotations, and 233 * by {@link Lower} for type annotations. We could group type annotations here, but it would 234 * require another rewrite pass. 235 */ 236 public static ImmutableList<AnnoInfo> groupRepeated( 237 Env<ClassSymbol, TypeBoundClass> env, ImmutableList<AnnoInfo> annotations) { 238 Multimap<ClassSymbol, AnnoInfo> repeated = LinkedHashMultimap.create(); 239 for (AnnoInfo anno : annotations) { 240 repeated.put(anno.sym(), anno); 241 } 242 Builder<AnnoInfo> result = ImmutableList.builder(); 243 for (Map.Entry<ClassSymbol, Collection<AnnoInfo>> entry : repeated.asMap().entrySet()) { 244 ClassSymbol symbol = entry.getKey(); 245 Collection<AnnoInfo> infos = entry.getValue(); 246 if (infos.size() > 1) { 247 Builder<Const> elements = ImmutableList.builder(); 248 for (AnnoInfo element : infos) { 249 elements.add(new AnnotationValue(element.sym(), element.values())); 250 } 251 ClassSymbol container = env.get(symbol).annotationMetadata().repeatable(); 252 if (container == null) { 253 AnnoInfo anno = infos.iterator().next(); 254 throw TurbineError.format( 255 anno.source(), anno.position(), ErrorKind.NONREPEATABLE_ANNOTATION, symbol); 256 } 257 result.add( 258 new AnnoInfo( 259 null, 260 container, 261 null, 262 ImmutableMap.of("value", new Const.ArrayInitValue(elements.build())))); 263 } else { 264 result.add(getOnlyElement(infos)); 265 } 266 } 267 return result.build(); 268 } 269 } 270