• 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 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    */
resolve( Env<ClassSymbol, ? extends HeaderBoundClass> env, ClassSymbol origin, ClassSymbol sym, String simpleName)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    */
resolveFunction( Env<ClassSymbol, ? extends HeaderBoundClass> env, ClassSymbol origin)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