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