1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file implements \c OutputFile class methods.
11///
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Support/VirtualOutputFile.h"
15#include "llvm/Support/VirtualOutputError.h"
16#include "llvm/Support/raw_ostream.h"
17#include "llvm/Support/raw_ostream_proxy.h"
18
19using namespace llvm;
20using namespace llvm::vfs;
21
22char OutputFileImpl::ID = 0;
23char NullOutputFileImpl::ID = 0;
24
25void OutputFileImpl::anchor() {}
26void NullOutputFileImpl::anchor() {}
27
28class OutputFile::TrackedProxy : public raw_pwrite_stream_proxy {
29public:
30 void resetProxy() {
31 TrackingPointer = nullptr;
32 resetProxiedOS();
33 }
34
35 explicit TrackedProxy(TrackedProxy *&TrackingPointer, raw_pwrite_stream &OS)
36 : raw_pwrite_stream_proxy(OS), TrackingPointer(TrackingPointer) {
37 assert(!TrackingPointer && "Expected to add a proxy");
38 TrackingPointer = this;
39 }
40
41 ~TrackedProxy() override { resetProxy(); }
42
43 TrackedProxy *&TrackingPointer;
44};
45
46Expected<std::unique_ptr<raw_pwrite_stream>> OutputFile::createProxy() {
47 if (OpenProxy)
48 return make_error<OutputError>(Args: getPath(), Args: OutputErrorCode::has_open_proxy);
49
50 return std::make_unique<TrackedProxy>(args&: OpenProxy, args&: getOS());
51}
52
53Error OutputFile::keep() {
54 // Catch double-closing logic bugs.
55 if (LLVM_UNLIKELY(!Impl))
56 report_fatal_error(
57 Err: make_error<OutputError>(Args: getPath(), Args: OutputErrorCode::already_closed));
58
59 // Report a fatal error if there's an open proxy and the file is being kept.
60 // This is safer than relying on clients to remember to flush(). Also call
61 // OutputFile::discard() to give the backend a chance to clean up any
62 // side effects (such as temporaries).
63 if (LLVM_UNLIKELY(OpenProxy))
64 report_fatal_error(Err: joinErrors(
65 E1: make_error<OutputError>(Args: getPath(), Args: OutputErrorCode::has_open_proxy),
66 E2: discard()));
67
68 Error E = Impl->keep();
69 Impl = nullptr;
70 DiscardOnDestroyHandler = nullptr;
71 return E;
72}
73
74Error OutputFile::discard() {
75 // Catch double-closing logic bugs.
76 if (LLVM_UNLIKELY(!Impl))
77 report_fatal_error(
78 Err: make_error<OutputError>(Args: getPath(), Args: OutputErrorCode::already_closed));
79
80 // Be lenient about open proxies since client teardown paths won't
81 // necessarily clean up in the right order. Reset the proxy to flush any
82 // current content; if there is another write, there should be quick crash on
83 // null dereference.
84 if (OpenProxy)
85 OpenProxy->resetProxy();
86
87 Error E = Impl->discard();
88 Impl = nullptr;
89 DiscardOnDestroyHandler = nullptr;
90 return E;
91}
92
93void OutputFile::destroy() {
94 if (!Impl)
95 return;
96
97 // Clean up the file. Move the discard handler into a local since discard
98 // will reset it.
99 auto DiscardHandler = std::move(DiscardOnDestroyHandler);
100 Error E = discard();
101 assert(!Impl && "Expected discard to destroy Impl");
102
103 // If there's no handler, report a fatal error.
104 if (LLVM_UNLIKELY(!DiscardHandler))
105 llvm::report_fatal_error(Err: joinErrors(
106 E1: make_error<OutputError>(Args: getPath(), Args: OutputErrorCode::not_closed),
107 E2: std::move(E)));
108 else if (E)
109 DiscardHandler(std::move(E));
110}
111