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