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.ArrayListMultimap;
22 import com.google.common.collect.ImmutableList;
23 import com.google.common.collect.ImmutableList.Builder;
24 import com.google.common.collect.ImmutableMap;
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.model.TurbineElementType;
38 import com.google.turbine.type.AnnoInfo;
39 import com.google.turbine.type.Type;
40 import com.google.turbine.type.Type.ArrayTy;
41 import com.google.turbine.type.Type.ClassTy;
42 import com.google.turbine.type.Type.ClassTy.SimpleClassTy;
43 import com.google.turbine.type.Type.PrimTy;
44 import com.google.turbine.type.Type.TyVar;
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 {
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(type, base.name(), 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, Builder<AnnoInfo> declarationAnnotations)149   private static Type disambiguate(
150       Env<ClassSymbol, TypeBoundClass> env,
151       TurbineElementType declarationTarget,
152       Type type,
153       ImmutableList<AnnoInfo> annotations,
154       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       Set<TurbineElementType> target = env.get(anno.sym()).annotationMetadata().target();
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 
bindFields( Env<ClassSymbol, TypeBoundClass> env, ImmutableList<FieldInfo> fields)171   private static ImmutableList<FieldInfo> bindFields(
172       Env<ClassSymbol, TypeBoundClass> env, ImmutableList<FieldInfo> fields) {
173     ImmutableList.Builder<FieldInfo> result = ImmutableList.builder();
174     for (FieldInfo field : fields) {
175       result.add(bindField(env, field));
176     }
177     return result.build();
178   }
179 
bindField(Env<ClassSymbol, TypeBoundClass> env, FieldInfo base)180   private static FieldInfo bindField(Env<ClassSymbol, TypeBoundClass> env, FieldInfo base) {
181     ImmutableList.Builder<AnnoInfo> declarationAnnotations = ImmutableList.builder();
182     Type type =
183         disambiguate(
184             env, TurbineElementType.FIELD, base.type(), base.annotations(), declarationAnnotations);
185     return new FieldInfo(
186         base.sym(), type, base.access(), declarationAnnotations.build(), base.decl(), base.value());
187   }
188 
189   /**
190    * Finds the left-most annotatable type in {@code type}, adds the {@code extra} type annotations
191    * to it, and removes any declaration annotations and saves them in {@code removed}.
192    *
193    * <p>The left-most type is e.g. the element type of an array, or the left-most type in a nested
194    * type declaration.
195    *
196    * <p>Note: the second case means that type annotation disambiguation has to occur on nested types
197    * before they are canonicalized.
198    */
addAnnotationsToType(Type type, ImmutableList<AnnoInfo> extra)199   private static Type addAnnotationsToType(Type type, ImmutableList<AnnoInfo> extra) {
200     switch (type.tyKind()) {
201       case PRIM_TY:
202         PrimTy primTy = (PrimTy) type;
203         return Type.PrimTy.create(primTy.primkind(), appendAnnotations(primTy.annos(), extra));
204       case CLASS_TY:
205         ClassTy classTy = (ClassTy) type;
206         SimpleClassTy base = classTy.classes().get(0);
207         SimpleClassTy simple =
208             SimpleClassTy.create(base.sym(), base.targs(), appendAnnotations(base.annos(), extra));
209         return Type.ClassTy.create(
210             ImmutableList.<SimpleClassTy>builder()
211                 .add(simple)
212                 .addAll(classTy.classes().subList(1, classTy.classes().size()))
213                 .build());
214       case ARRAY_TY:
215         ArrayTy arrayTy = (ArrayTy) type;
216         return ArrayTy.create(addAnnotationsToType(arrayTy.elementType(), extra), arrayTy.annos());
217       case TY_VAR:
218         TyVar tyVar = (TyVar) type;
219         return Type.TyVar.create(tyVar.sym(), appendAnnotations(tyVar.annos(), extra));
220       case VOID_TY:
221         return type;
222       case WILD_TY:
223         throw new AssertionError("unexpected wildcard type outside type argument context");
224       default:
225         throw new AssertionError(type.tyKind());
226     }
227   }
228 
appendAnnotations( ImmutableList<AnnoInfo> annos, ImmutableList<AnnoInfo> extra)229   private static ImmutableList<AnnoInfo> appendAnnotations(
230       ImmutableList<AnnoInfo> annos, ImmutableList<AnnoInfo> extra) {
231     return ImmutableList.<AnnoInfo>builder().addAll(annos).addAll(extra).build();
232   }
233 
234   /**
235    * Group repeated annotations and wrap them in their container annotation.
236    *
237    * <p>For example, convert {@code @Foo @Foo} to {@code @Foos({@Foo, @Foo})}.
238    *
239    * <p>This method is used by {@link DisambiguateTypeAnnotations} for declaration annotations, and
240    * by {@link com.google.turbine.lower.Lower} for type annotations. We could group type annotations
241    * here, but it would require another rewrite pass.
242    */
groupRepeated( Env<ClassSymbol, TypeBoundClass> env, ImmutableList<AnnoInfo> annotations)243   public static ImmutableList<AnnoInfo> groupRepeated(
244       Env<ClassSymbol, TypeBoundClass> env, ImmutableList<AnnoInfo> annotations) {
245     Multimap<ClassSymbol, AnnoInfo> repeated = ArrayListMultimap.create();
246     for (AnnoInfo anno : annotations) {
247       repeated.put(anno.sym(), anno);
248     }
249     Builder<AnnoInfo> result = ImmutableList.builder();
250     for (Map.Entry<ClassSymbol, Collection<AnnoInfo>> entry : repeated.asMap().entrySet()) {
251       ClassSymbol symbol = entry.getKey();
252       Collection<AnnoInfo> infos = entry.getValue();
253       if (infos.size() > 1) {
254         Builder<Const> elements = ImmutableList.builder();
255         for (AnnoInfo element : infos) {
256           elements.add(new AnnotationValue(element.sym(), element.values()));
257         }
258         ClassSymbol container = env.get(symbol).annotationMetadata().repeatable();
259         if (container == null) {
260           AnnoInfo anno = infos.iterator().next();
261           throw TurbineError.format(
262               anno.source(), anno.position(), ErrorKind.NONREPEATABLE_ANNOTATION, symbol);
263         }
264         result.add(
265             new AnnoInfo(
266                 null,
267                 container,
268                 null,
269                 ImmutableMap.of("value", new Const.ArrayInitValue(elements.build()))));
270       } else {
271         result.add(getOnlyElement(infos));
272       }
273     }
274     return result.build();
275   }
276 }
277