• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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