Home | History | Annotate | Download | only in binder
      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