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 com.google.common.base.Joiner;
     20 import com.google.common.collect.ImmutableList;
     21 import com.google.turbine.binder.bound.BoundClass;
     22 import com.google.turbine.binder.bound.HeaderBoundClass;
     23 import com.google.turbine.binder.bound.TypeBoundClass;
     24 import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
     25 import com.google.turbine.binder.env.CompoundEnv;
     26 import com.google.turbine.binder.env.Env;
     27 import com.google.turbine.binder.env.LazyEnv.LazyBindingError;
     28 import com.google.turbine.binder.lookup.CanonicalSymbolResolver;
     29 import com.google.turbine.binder.lookup.ImportScope.ResolveFunction;
     30 import com.google.turbine.binder.lookup.LookupResult;
     31 import com.google.turbine.binder.sym.ClassSymbol;
     32 import com.google.turbine.diag.SourceFile;
     33 import com.google.turbine.diag.TurbineError;
     34 import com.google.turbine.diag.TurbineError.ErrorKind;
     35 import com.google.turbine.model.TurbineVisibility;
     36 import java.util.Objects;
     37 
     38 /** Qualified name resolution. */
     39 public class Resolve {
     40 
     41   /**
     42    * Performs JLS 6.5.5.2 qualified type name resolution of a type with the given simple name,
     43    * qualified by the given symbol. The search considers members that are inherited from
     44    * superclasses or interfaces.
     45    */
     46   public static ClassSymbol resolve(
     47       Env<ClassSymbol, ? extends HeaderBoundClass> env,
     48       ClassSymbol origin,
     49       ClassSymbol sym,
     50       String simpleName) {
     51     ClassSymbol result;
     52     HeaderBoundClass bound = env.get(sym);
     53     if (bound == null) {
     54       return null;
     55     }
     56     result = bound.children().get(simpleName);
     57     if (result != null) {
     58       return result;
     59     }
     60     if (bound.superclass() != null) {
     61       result = resolve(env, origin, bound.superclass(), simpleName);
     62       if (result != null && visible(origin, result, env.get(result))) {
     63         return result;
     64       }
     65     }
     66     for (ClassSymbol i : bound.interfaces()) {
     67       result = resolve(env, origin, i, simpleName);
     68       if (result != null && visible(origin, result, env.get(result))) {
     69         return result;
     70       }
     71     }
     72     return null;
     73   }
     74 
     75   /**
     76    * Partially applied {@link #resolve}, returning a {@link ResolveFunction} for the given {@code
     77    * env} and {@code origin} symbol.
     78    */
     79   public static ResolveFunction resolveFunction(
     80       Env<ClassSymbol, ? extends HeaderBoundClass> env, ClassSymbol origin) {
     81     return new ResolveFunction() {
     82       @Override
     83       public ClassSymbol resolveOne(ClassSymbol base, String name) {
     84         try {
     85           return Resolve.resolve(env, origin, base, name);
     86         } catch (LazyBindingError e) {
     87           // This is only used for non-canonical import resolution, and if we discover a cycle
     88           // while processing imports we want to continue and only error out if the symbol is
     89           // never found.
     90           return null;
     91         }
     92       }
     93     };
     94   }
     95 
     96   static class CanonicalResolver implements CanonicalSymbolResolver {
     97     private final String packagename;
     98     private final CompoundEnv<ClassSymbol, BoundClass> env;
     99 
    100     public CanonicalResolver(
    101         ImmutableList<String> packagename, CompoundEnv<ClassSymbol, BoundClass> env) {
    102       this.packagename = Joiner.on('/').join(packagename);
    103       this.env = env;
    104     }
    105 
    106     @Override
    107     public ClassSymbol resolve(SourceFile source, int position, LookupResult result) {
    108       ClassSymbol sym = (ClassSymbol) result.sym();
    109       for (String bit : result.remaining()) {
    110         sym = resolveOne(sym, bit);
    111         if (sym == null) {
    112           throw TurbineError.format(source, position, ErrorKind.SYMBOL_NOT_FOUND, bit);
    113         }
    114       }
    115       return sym;
    116     }
    117 
    118     @Override
    119     public ClassSymbol resolveOne(ClassSymbol sym, String bit) {
    120       BoundClass ci = env.get(sym);
    121       if (ci == null) {
    122         return null;
    123       }
    124       sym = ci.children().get(bit);
    125       if (sym == null) {
    126         return null;
    127       }
    128       if (!visible(sym)) {
    129         return null;
    130       }
    131       return sym;
    132     }
    133 
    134     @Override
    135     public boolean visible(ClassSymbol sym) {
    136       TurbineVisibility visibility = TurbineVisibility.fromAccess(env.get(sym).access());
    137       switch (visibility) {
    138         case PUBLIC:
    139           return true;
    140         case PROTECTED:
    141         case PACKAGE:
    142           return Objects.equals(packageName(sym), packagename);
    143         case PRIVATE:
    144           return false;
    145         default:
    146           throw new AssertionError(visibility);
    147       }
    148     }
    149   }
    150 
    151   /**
    152    * Performs qualified type name resolution of an instance variable with the given simple name,
    153    * qualified by the given symbol. The search considers members that are inherited from
    154    * superclasses or interfaces.
    155    */
    156   public static FieldInfo resolveField(
    157       Env<ClassSymbol, TypeBoundClass> env, ClassSymbol origin, ClassSymbol sym, String name) {
    158     TypeBoundClass info = env.get(sym);
    159     if (info == null) {
    160       return null;
    161     }
    162     for (FieldInfo f : info.fields()) {
    163       if (f.name().equals(name)) {
    164         return f;
    165       }
    166     }
    167     if (info.superclass() != null) {
    168       FieldInfo field = resolveField(env, origin, info.superclass(), name);
    169       if (field != null && visible(origin, field)) {
    170         return field;
    171       }
    172     }
    173     for (ClassSymbol i : info.interfaces()) {
    174       FieldInfo field = resolveField(env, origin, i, name);
    175       if (field != null && visible(origin, field)) {
    176         return field;
    177       }
    178     }
    179     return null;
    180   }
    181 
    182   /** Is the given field visible when inherited into class origin? */
    183   private static boolean visible(ClassSymbol origin, FieldInfo info) {
    184     return visible(origin, info.sym().owner(), info.access());
    185   }
    186 
    187   /** Is the given type visible when inherited into class origin? */
    188   private static boolean visible(ClassSymbol origin, ClassSymbol sym, HeaderBoundClass info) {
    189     return visible(origin, sym, info.access());
    190   }
    191 
    192   private static boolean visible(ClassSymbol origin, ClassSymbol owner, int access) {
    193     TurbineVisibility visibility = TurbineVisibility.fromAccess(access);
    194     switch (visibility) {
    195       case PUBLIC:
    196       case PROTECTED:
    197         return true;
    198       case PACKAGE:
    199         return Objects.equals(packageName(owner), packageName(origin));
    200       case PRIVATE:
    201         // Private members of lexically enclosing declarations are not handled,
    202         // since this visibility check is only used for inherited members.
    203         return owner.equals(origin);
    204       default:
    205         throw new AssertionError(visibility);
    206     }
    207   }
    208 
    209   private static String packageName(ClassSymbol sym) {
    210     if (sym == null) {
    211       return null;
    212     }
    213     int idx = sym.binaryName().lastIndexOf('/');
    214     if (idx == -1) {
    215       return null;
    216     }
    217     return sym.binaryName().substring(0, idx);
    218   }
    219 }
    220