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