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