1// Copyright 2017 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.android.desugar;
15
16import static com.google.common.base.Preconditions.checkNotNull;
17import static com.google.common.base.Preconditions.checkState;
18
19import java.util.HashMap;
20import javax.annotation.Nullable;
21import org.objectweb.asm.ClassReader;
22
23/**
24 * Simple memoizer for whether types are classes or interfaces.
25 */
26class ClassVsInterface {
27  /** Map from internal names to whether they are an interface ({@code false} thus means class). */
28  private final HashMap<String, Boolean> known = new HashMap<>();
29  private final ClassReaderFactory classpath;
30
31  public ClassVsInterface(ClassReaderFactory classpath) {
32    this.classpath = classpath;
33  }
34
35  public ClassVsInterface addKnownClass(@Nullable String internalName) {
36    if (internalName != null) {
37      Boolean previous = known.put(internalName, false);
38      checkState(previous == null || !previous, "Already recorded as interface: %s", internalName);
39    }
40    return this;
41  }
42
43  public ClassVsInterface addKnownInterfaces(String... internalNames) {
44    for (String internalName : internalNames) {
45      Boolean previous = known.put(internalName, true);
46      checkState(previous == null || previous, "Already recorded as class: %s", internalName);
47    }
48    return this;
49  }
50
51  public boolean isOuterInterface(String outerName, String innerName) {
52    Boolean result = known.get(outerName);
53    if (result == null) {
54      // We could just load the outer class here, but this tolerates incomplete classpaths better.
55      // Note the outer class should be in the Jar we're desugaring, so it should always be there.
56      ClassReader outerClass = checkNotNull(classpath.readIfKnown(outerName),
57          "Couldn't find outer class %s of %s", outerName, innerName);
58      result = BitFlags.isInterface(outerClass.getAccess());
59      known.put(outerName, result);
60    }
61    return result;
62  }
63}
64