|  | // 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(cdh, lh); | 
|  | } else if (Z_DEFLATED == lh->compression_method()) { | 
|  | if (!inflater_) { | 
|  | inflater_.reset(new Inflater()); | 
|  | } | 
|  | buffer_->DecompressEntryContents(cdh, lh, inflater_.get()); | 
|  | } else { | 
|  | diag_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)) { | 
|  | diag_errx(2, "META-INF/desugar_deps: unable to parse"); | 
|  | } | 
|  | if (!content.ConsumedEntireMessage()) { | 
|  | diag_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: %zu\n", needed_deps_.size()); | 
|  | fprintf(stderr, "Interfaces to check: %zu\n", missing_interfaces_.size()); | 
|  | fprintf(stderr, "Sub-interfaces: %zu\n", extended_interfaces_.size()); | 
|  | fprintf(stderr, "Interfaces w/ default methods: %zu\n", | 
|  | has_default_methods_.size()); | 
|  | } | 
|  | for (const 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_) { | 
|  | diag_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 (const auto &missing : missing_interfaces_) { | 
|  | if (verbose_) { | 
|  | fprintf(stderr, "Checking %s\n", missing.first.c_str()); | 
|  | } | 
|  | if (HasDefaultMethods(missing.first)) { | 
|  | if (fail_on_error_) { | 
|  | diag_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; | 
|  | } |