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