aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/singlejar/desugar_checking.cc
blob: 13f45f95833fa5be004b18ea2bcba295afc846b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2017 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "src/tools/singlejar/desugar_checking.h"
#include "src/tools/singlejar/diag.h"
#include "src/main/protobuf/desugar_deps.pb.h"

bool Java8DesugarDepsChecker::Merge(const CDH *cdh, const LH *lh) {
  // Throw away anything previously read, no need to concatenate
  buffer_.reset(new TransientBytes());
  if (Z_NO_COMPRESSION == lh->compression_method()) {
    buffer_->ReadEntryContents(lh);
  } else if (Z_DEFLATED == lh->compression_method()) {
    if (!inflater_.get()) {
      inflater_.reset(new Inflater());
    }
    buffer_->DecompressEntryContents(cdh, lh, inflater_.get());
  } else {
    errx(2, "META-INF/desugar_deps is neither stored nor deflated");
  }

  // TODO(kmb): Wrap buffer_ as ZeroCopyInputStream to avoid copying out.
  // Note we only copy one file at a time, so overhead should be modest.
  uint32_t checksum;
  const size_t data_size = buffer_->data_size();
  uint8_t *buf = reinterpret_cast<uint8_t *>(malloc(data_size));
  buffer_->CopyOut(reinterpret_cast<uint8_t *>(buf), &checksum);
  buffer_.reset();  // release buffer eagerly

  bazel::tools::desugar::DesugarDepsInfo deps_info;
  google::protobuf::io::CodedInputStream content(buf, data_size);
  if (!deps_info.ParseFromCodedStream(&content)) {
    errx(2, "META-INF/desugar_deps: unable to parse");
  }
  if (!content.ConsumedEntireMessage()) {
    errx(2, "META-INF/desugar_deps: unexpected trailing content");
  }
  free(buf);

  for (const auto &assume_present : deps_info.assume_present()) {
    // This means we need file named <target>.class in the output.  Remember
    // the first origin of this requirement for error messages, drop others.
    needed_deps_.emplace(assume_present.target().binary_name() + ".class",
                         assume_present.origin().binary_name());
  }

  for (const auto &missing : deps_info.missing_interface()) {
    // Remember the first origin of this requirement for error messages, drop
    // subsequent ones.
    missing_interfaces_.emplace(missing.target().binary_name(),
                                missing.origin().binary_name());
  }

  for (const auto &extends : deps_info.interface_with_supertypes()) {
    // Remember interface hierarchy the first time we see this interface, drop
    // subsequent ones for consistency with how singlejar will keep the first
    // occurrence of the file defining the interface.  We'll lazily derive
    // whether missing_interfaces_ inherit default methods with this data later.
    if (extends.extended_interface_size() > 0) {
      std::vector<std::string> extended;
      extended.reserve(extends.extended_interface_size());
      for (const auto &itf : extends.extended_interface()) {
        extended.push_back(itf.binary_name());
      }
      extended_interfaces_.emplace(extends.origin().binary_name(),
                                   std::move(extended));
    }
  }

  for (const auto &companion : deps_info.interface_with_companion()) {
    // Only remember interfaces that definitely have default methods for now.
    // For all other interfaces we'll transitively check extended interfaces
    // in HasDefaultMethods.
    if (companion.num_default_methods() > 0) {
      has_default_methods_[companion.origin().binary_name()] = true;
    }
  }
  return true;
}

void *Java8DesugarDepsChecker::OutputEntry(bool compress) {
  if (verbose_) {
    fprintf(stderr, "Needed deps: %lu\n", needed_deps_.size());
    fprintf(stderr, "Interfaces to check: %lu\n", missing_interfaces_.size());
    fprintf(stderr, "Sub-interfaces: %lu\n", extended_interfaces_.size());
    fprintf(stderr, "Interfaces w/ default methods: %lu\n",
            has_default_methods_.size());
  }
  for (auto needed : needed_deps_) {
    if (verbose_) {
      fprintf(stderr, "Looking for %s\n", needed.first.c_str());
    }
    if (!known_member_(needed.first)) {
      if (fail_on_error_) {
        errx(2, "%s referenced by %s but not found.  Is the former defined in "
                "a neverlink library?",
                needed.first.c_str(), needed.second.c_str());
      } else {
        error_ = true;
      }
    }
  }

  for (auto missing : missing_interfaces_) {
    if (verbose_) {
      fprintf(stderr, "Checking %s\n", missing.first.c_str());
    }
    if (HasDefaultMethods(missing.first)) {
      if (fail_on_error_) {
        errx(2, "%s needed on the classpath for desugaring %s.  Please add the "
                "missing dependency to the target containing the latter.",
                missing.first.c_str(), missing.second.c_str());
      } else {
        error_ = true;
      }
    }
  }

  // We don't want these files in the output, just check them for consistency
  return nullptr;
}

bool Java8DesugarDepsChecker::HasDefaultMethods(
    const std::string &interface_name) {
  auto cached = has_default_methods_.find(interface_name);
  if (cached != has_default_methods_.end()) {
    return cached->second;
  }

  // Prime with false in case there's a cycle.  We'll update with the true value
  // (ignoring the cycle) below.
  has_default_methods_.emplace(interface_name, false);

  for (const std::string &extended : extended_interfaces_[interface_name]) {
    if (HasDefaultMethods(extended)) {
      has_default_methods_[interface_name] = true;
      return true;
    }
  }
  has_default_methods_[interface_name] = false;
  return false;
}