• 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.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