Coverage Report

Created: 2023-09-21 18:56

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Interpreter/Value.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- Interpreter.h - Incremental Compiation and Execution---*- C++ -*-===//
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
// This file defines the class that used to represent a value in incremental
10
// C++.
11
//
12
//===----------------------------------------------------------------------===//
13
14
#include "clang/Interpreter/Value.h"
15
#include "clang/AST/ASTContext.h"
16
#include "clang/AST/Type.h"
17
#include "clang/Interpreter/Interpreter.h"
18
#include "llvm/ADT/StringExtras.h"
19
#include "llvm/Support/ErrorHandling.h"
20
#include "llvm/Support/raw_os_ostream.h"
21
#include <cassert>
22
#include <cstdint>
23
#include <utility>
24
25
using namespace clang;
26
27
namespace {
28
29
// This is internal buffer maintained by Value, used to hold temporaries.
30
class ValueStorage {
31
public:
32
  using DtorFunc = void (*)(void *);
33
34
  static unsigned char *CreatePayload(void *DtorF, size_t AllocSize,
35
3
                                      size_t ElementsSize) {
36
3
    if (AllocSize < sizeof(Canary))
37
0
      AllocSize = sizeof(Canary);
38
3
    unsigned char *Buf =
39
3
        new unsigned char[ValueStorage::getPayloadOffset() + AllocSize];
40
3
    ValueStorage *VS = new (Buf) ValueStorage(DtorF, AllocSize, ElementsSize);
41
3
    std::memcpy(VS->getPayload(), Canary, sizeof(Canary));
42
3
    return VS->getPayload();
43
3
  }
44
45
13
  unsigned char *getPayload() { return Storage; }
46
1
  const unsigned char *getPayload() const { return Storage; }
47
48
6
  static unsigned getPayloadOffset() {
49
6
    static ValueStorage Dummy(nullptr, 0, 0);
50
6
    return Dummy.getPayload() - reinterpret_cast<unsigned char *>(&Dummy);
51
6
  }
52
53
3
  static ValueStorage *getFromPayload(void *Payload) {
54
3
    ValueStorage *R = reinterpret_cast<ValueStorage *>(
55
3
        (unsigned char *)Payload - getPayloadOffset());
56
3
    return R;
57
3
  }
58
59
0
  void Retain() { ++RefCnt; }
60
61
3
  void Release() {
62
3
    assert(RefCnt > 0 && "Can't release if reference count is already zero");
63
3
    if (--RefCnt == 0) {
64
      // We hace a non-trivial dtor.
65
3
      if (Dtor && 
IsAlive()1
) {
66
1
        assert(Elements && "We at least should have 1 element in Value");
67
1
        size_t Stride = AllocSize / Elements;
68
2
        for (size_t Idx = 0; Idx < Elements; 
++Idx1
)
69
1
          (*Dtor)(getPayload() + Idx * Stride);
70
1
      }
71
3
      delete[] reinterpret_cast<unsigned char *>(this);
72
3
    }
73
3
  }
74
75
  // Check whether the storage is valid by validating the canary bits.
76
  // If someone accidentally write some invalid bits in the storage, the canary
77
  // will be changed first, and `IsAlive` will return false then.
78
1
  bool IsAlive() const {
79
1
    return std::memcmp(getPayload(), Canary, sizeof(Canary)) != 0;
80
1
  }
81
82
private:
83
  ValueStorage(void *DtorF, size_t AllocSize, size_t ElementsNum)
84
4
      : RefCnt(1), Dtor(reinterpret_cast<DtorFunc>(DtorF)),
85
4
        AllocSize(AllocSize), Elements(ElementsNum) {}
86
87
  mutable unsigned RefCnt;
88
  DtorFunc Dtor = nullptr;
89
  size_t AllocSize = 0;
90
  size_t Elements = 0;
91
  unsigned char Storage[1];
92
93
  // These are some canary bits that are used for protecting the storage been
94
  // damaged.
95
  static constexpr unsigned char Canary[8] = {0x4c, 0x37, 0xad, 0x8f,
96
                                              0x2d, 0x23, 0x95, 0x91};
97
};
98
} // namespace
99
100
9
static Value::Kind ConvertQualTypeToKind(const ASTContext &Ctx, QualType QT) {
101
9
  if (Ctx.hasSameType(QT, Ctx.VoidTy))
102
1
    return Value::K_Void;
103
104
8
  if (const auto *ET = QT->getAs<EnumType>())
105
0
    QT = ET->getDecl()->getIntegerType();
106
107
8
  const auto *BT = QT->getAs<BuiltinType>();
108
8
  if (!BT || 
BT->isNullPtrType()4
)
109
4
    return Value::K_PtrOrObj;
110
111
4
  switch (QT->castAs<BuiltinType>()->getKind()) {
112
0
  default:
113
0
    assert(false && "Type not supported");
114
0
    return Value::K_Unspecified;
115
0
#define X(type, name)                                                          \
116
4
  case BuiltinType::name:                                                      \
117
4
    return Value::K_##name;
118
4
    
REPL_BUILTIN_TYPES0
119
4
#undef X
120
4
  }
121
4
}
122
123
9
Value::Value(Interpreter *In, void *Ty) : Interp(In), OpaqueType(Ty) {
124
9
  setKind(ConvertQualTypeToKind(getASTContext(), getType()));
125
9
  if (ValueKind == K_PtrOrObj) {
126
4
    QualType Canon = getType().getCanonicalType();
127
4
    if ((Canon->isPointerType() || Canon->isObjectType() ||
128
4
         
Canon->isReferenceType()1
) &&
129
4
        
(3
Canon->isRecordType()3
||
Canon->isConstantArrayType()2
||
130
3
         
Canon->isMemberPointerType()2
)) {
131
3
      IsManuallyAlloc = true;
132
      // Compile dtor function.
133
3
      Interpreter &Interp = getInterpreter();
134
3
      void *DtorF = nullptr;
135
3
      size_t ElementsSize = 1;
136
3
      QualType DtorTy = getType();
137
138
3
      if (const auto *ArrTy =
139
3
              llvm::dyn_cast<ConstantArrayType>(DtorTy.getTypePtr())) {
140
0
        DtorTy = ArrTy->getElementType();
141
0
        llvm::APInt ArrSize(sizeof(size_t) * 8, 1);
142
0
        do {
143
0
          ArrSize *= ArrTy->getSize();
144
0
          ArrTy = llvm::dyn_cast<ConstantArrayType>(
145
0
              ArrTy->getElementType().getTypePtr());
146
0
        } while (ArrTy);
147
0
        ElementsSize = static_cast<size_t>(ArrSize.getZExtValue());
148
0
      }
149
3
      if (const auto *RT = DtorTy->getAs<RecordType>()) {
150
1
        if (CXXRecordDecl *CXXRD =
151
1
                llvm::dyn_cast<CXXRecordDecl>(RT->getDecl())) {
152
1
          if (llvm::Expected<llvm::orc::ExecutorAddr> Addr =
153
1
                  Interp.CompileDtorCall(CXXRD))
154
1
            DtorF = reinterpret_cast<void *>(Addr->getValue());
155
0
          else
156
0
            llvm::logAllUnhandledErrors(Addr.takeError(), llvm::errs());
157
1
        }
158
1
      }
159
160
3
      size_t AllocSize =
161
3
          getASTContext().getTypeSizeInChars(getType()).getQuantity();
162
3
      unsigned char *Payload =
163
3
          ValueStorage::CreatePayload(DtorF, AllocSize, ElementsSize);
164
3
      setPtr((void *)Payload);
165
3
    }
166
4
  }
167
9
}
168
169
Value::Value(const Value &RHS)
170
0
    : Interp(RHS.Interp), OpaqueType(RHS.OpaqueType), Data(RHS.Data),
171
0
      ValueKind(RHS.ValueKind), IsManuallyAlloc(RHS.IsManuallyAlloc) {
172
0
  if (IsManuallyAlloc)
173
0
    ValueStorage::getFromPayload(getPtr())->Retain();
174
0
}
175
176
0
Value::Value(Value &&RHS) noexcept {
177
0
  Interp = std::exchange(RHS.Interp, nullptr);
178
0
  OpaqueType = std::exchange(RHS.OpaqueType, nullptr);
179
0
  Data = RHS.Data;
180
0
  ValueKind = std::exchange(RHS.ValueKind, K_Unspecified);
181
0
  IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false);
182
183
0
  if (IsManuallyAlloc)
184
0
    ValueStorage::getFromPayload(getPtr())->Release();
185
0
}
186
187
0
Value &Value::operator=(const Value &RHS) {
188
0
  if (IsManuallyAlloc)
189
0
    ValueStorage::getFromPayload(getPtr())->Release();
190
191
0
  Interp = RHS.Interp;
192
0
  OpaqueType = RHS.OpaqueType;
193
0
  Data = RHS.Data;
194
0
  ValueKind = RHS.ValueKind;
195
0
  IsManuallyAlloc = RHS.IsManuallyAlloc;
196
197
0
  if (IsManuallyAlloc)
198
0
    ValueStorage::getFromPayload(getPtr())->Retain();
199
200
0
  return *this;
201
0
}
202
203
18
Value &Value::operator=(Value &&RHS) noexcept {
204
18
  if (this != &RHS) {
205
18
    if (IsManuallyAlloc)
206
0
      ValueStorage::getFromPayload(getPtr())->Release();
207
208
18
    Interp = std::exchange(RHS.Interp, nullptr);
209
18
    OpaqueType = std::exchange(RHS.OpaqueType, nullptr);
210
18
    ValueKind = std::exchange(RHS.ValueKind, K_Unspecified);
211
18
    IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false);
212
213
18
    Data = RHS.Data;
214
18
  }
215
18
  return *this;
216
18
}
217
218
54
void Value::clear() {
219
54
  if (IsManuallyAlloc)
220
3
    ValueStorage::getFromPayload(getPtr())->Release();
221
54
  ValueKind = K_Unspecified;
222
54
  OpaqueType = nullptr;
223
54
  Interp = nullptr;
224
54
  IsManuallyAlloc = false;
225
54
}
226
227
54
Value::~Value() { clear(); }
228
229
6
void *Value::getPtr() const {
230
6
  assert(ValueKind == K_PtrOrObj);
231
6
  return Data.m_Ptr;
232
6
}
233
234
31
QualType Value::getType() const {
235
31
  return QualType::getFromOpaquePtr(OpaqueType);
236
31
}
237
238
15
Interpreter &Value::getInterpreter() {
239
15
  assert(Interp != nullptr &&
240
15
         "Can't get interpreter from a default constructed value");
241
15
  return *Interp;
242
15
}
243
244
0
const Interpreter &Value::getInterpreter() const {
245
0
  assert(Interp != nullptr &&
246
0
         "Can't get interpreter from a default constructed value");
247
0
  return *Interp;
248
0
}
249
250
12
ASTContext &Value::getASTContext() { return getInterpreter().getASTContext(); }
251
252
0
const ASTContext &Value::getASTContext() const {
253
0
  return getInterpreter().getASTContext();
254
0
}
255
256
0
void Value::dump() const { print(llvm::outs()); }
257
258
0
void Value::printType(llvm::raw_ostream &Out) const {
259
0
  Out << "Not implement yet.\n";
260
0
}
261
0
void Value::printData(llvm::raw_ostream &Out) const {
262
0
  Out << "Not implement yet.\n";
263
0
}
264
0
void Value::print(llvm::raw_ostream &Out) const {
265
0
  assert(OpaqueType != nullptr && "Can't print default Value");
266
0
  Out << "Not implement yet.\n";
267
0
}