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.collect.ImmutableList; 20 import com.google.common.collect.ImmutableMap; 21 import com.google.turbine.binder.bound.HeaderBoundClass; 22 import com.google.turbine.binder.bound.PackageSourceBoundClass; 23 import com.google.turbine.binder.bound.SourceHeaderBoundClass; 24 import com.google.turbine.binder.env.Env; 25 import com.google.turbine.binder.env.LazyEnv.LazyBindingError; 26 import com.google.turbine.binder.lookup.LookupKey; 27 import com.google.turbine.binder.lookup.LookupResult; 28 import com.google.turbine.binder.sym.ClassSymbol; 29 import com.google.turbine.binder.sym.TyVarSymbol; 30 import com.google.turbine.diag.TurbineError.ErrorKind; 31 import com.google.turbine.diag.TurbineLog.TurbineLogWithSource; 32 import com.google.turbine.model.TurbineTyKind; 33 import com.google.turbine.tree.Tree; 34 import com.google.turbine.tree.Tree.ClassTy; 35 import java.util.ArrayDeque; 36 37 /** Type hierarchy binding. */ 38 public class HierarchyBinder { 39 40 /** Binds the type hierarchy (superclasses and interfaces) for a single class. */ bind( TurbineLogWithSource log, ClassSymbol origin, PackageSourceBoundClass base, Env<ClassSymbol, ? extends HeaderBoundClass> env)41 public static SourceHeaderBoundClass bind( 42 TurbineLogWithSource log, 43 ClassSymbol origin, 44 PackageSourceBoundClass base, 45 Env<ClassSymbol, ? extends HeaderBoundClass> env) { 46 return new HierarchyBinder(log, origin, base, env).bind(); 47 } 48 49 private final TurbineLogWithSource log; 50 private final ClassSymbol origin; 51 private final PackageSourceBoundClass base; 52 private final Env<ClassSymbol, ? extends HeaderBoundClass> env; 53 HierarchyBinder( TurbineLogWithSource log, ClassSymbol origin, PackageSourceBoundClass base, Env<ClassSymbol, ? extends HeaderBoundClass> env)54 private HierarchyBinder( 55 TurbineLogWithSource log, 56 ClassSymbol origin, 57 PackageSourceBoundClass base, 58 Env<ClassSymbol, ? extends HeaderBoundClass> env) { 59 this.log = log; 60 this.origin = origin; 61 this.base = base; 62 this.env = env; 63 } 64 bind()65 private SourceHeaderBoundClass bind() { 66 Tree.TyDecl decl = base.decl(); 67 68 ClassSymbol superclass; 69 if (decl.xtnds().isPresent()) { 70 superclass = resolveClass(decl.xtnds().get()); 71 } else { 72 switch (decl.tykind()) { 73 case ENUM: 74 superclass = ClassSymbol.ENUM; 75 break; 76 case INTERFACE: 77 case ANNOTATION: 78 case CLASS: 79 superclass = !origin.equals(ClassSymbol.OBJECT) ? ClassSymbol.OBJECT : null; 80 break; 81 default: 82 throw new AssertionError(decl.tykind()); 83 } 84 } 85 86 ImmutableList.Builder<ClassSymbol> interfaces = ImmutableList.builder(); 87 if (!decl.impls().isEmpty()) { 88 for (Tree.ClassTy i : decl.impls()) { 89 ClassSymbol result = resolveClass(i); 90 if (result == null) { 91 continue; 92 } 93 interfaces.add(result); 94 } 95 } else { 96 if (decl.tykind() == TurbineTyKind.ANNOTATION) { 97 interfaces.add(ClassSymbol.ANNOTATION); 98 } 99 } 100 101 ImmutableMap.Builder<String, TyVarSymbol> typeParameters = ImmutableMap.builder(); 102 for (Tree.TyParam p : decl.typarams()) { 103 typeParameters.put(p.name().value(), new TyVarSymbol(origin, p.name().value())); 104 } 105 106 return new SourceHeaderBoundClass(base, superclass, interfaces.build(), typeParameters.build()); 107 } 108 109 /** 110 * Resolves the {@link ClassSymbol} for the given {@link Tree.ClassTy}, with handling for 111 * non-canonical qualified type names. 112 */ resolveClass(Tree.ClassTy ty)113 private ClassSymbol resolveClass(Tree.ClassTy ty) { 114 // flatten a left-recursive qualified type name to its component simple names 115 // e.g. Foo<Bar>.Baz -> ["Foo", "Bar"] 116 ArrayDeque<Tree.Ident> flat = new ArrayDeque<>(); 117 for (Tree.ClassTy curr = ty; curr != null; curr = curr.base().orElse(null)) { 118 flat.addFirst(curr.name()); 119 } 120 // Resolve the base symbol in the qualified name. 121 LookupResult result = lookup(ty, new LookupKey(ImmutableList.copyOf(flat))); 122 if (result == null) { 123 log.error(ty.position(), ErrorKind.CANNOT_RESOLVE, ty); 124 return null; 125 } 126 // Resolve pieces in the qualified name referring to member types. 127 // This needs to consider member type declarations inherited from supertypes and interfaces. 128 ClassSymbol sym = (ClassSymbol) result.sym(); 129 for (Tree.Ident bit : result.remaining()) { 130 sym = resolveNext(ty, sym, bit); 131 if (sym == null) { 132 break; 133 } 134 } 135 return sym; 136 } 137 resolveNext(ClassTy ty, ClassSymbol sym, Tree.Ident bit)138 private ClassSymbol resolveNext(ClassTy ty, ClassSymbol sym, Tree.Ident bit) { 139 ClassSymbol next; 140 try { 141 next = Resolve.resolve(env, origin, sym, bit); 142 } catch (LazyBindingError e) { 143 log.error(ty.position(), ErrorKind.CYCLIC_HIERARCHY, e.getMessage()); 144 return null; 145 } 146 if (next == null) { 147 log.error( 148 bit.position(), 149 ErrorKind.SYMBOL_NOT_FOUND, 150 new ClassSymbol(sym.binaryName() + '$' + bit)); 151 } 152 return next; 153 } 154 155 /** Resolve a qualified type name to a symbol. */ lookup(Tree tree, LookupKey lookup)156 private LookupResult lookup(Tree tree, LookupKey lookup) { 157 // Handle any lexically enclosing class declarations (if we're binding a member class). 158 // We could build out scopes for this, but it doesn't seem worth it. (And sharing the scopes 159 // with other members of the same enclosing declaration would be complicated.) 160 for (ClassSymbol curr = base.owner(); curr != null; curr = env.get(curr).owner()) { 161 ClassSymbol result; 162 try { 163 result = Resolve.resolve(env, origin, curr, lookup.first()); 164 } catch (LazyBindingError e) { 165 log.error(tree.position(), ErrorKind.CYCLIC_HIERARCHY, e.getMessage()); 166 result = null; 167 } 168 if (result != null) { 169 return new LookupResult(result, lookup); 170 } 171 } 172 // Fall back to the top-level scopes for the compilation unit (imports, same package, then 173 // qualified name resolution). 174 return base.scope().lookup(lookup, Resolve.resolveFunction(env, origin)); 175 } 176 } 177