Skip to content

Commit a986d74

Browse files
committed
[NFC] Implement a small DSL to create metadata objects in unit tests
Some parts of the type metadata system are difficult to unit-test because they rely on structures that contain relative references, which the C compiler cannot generate. We have traditionally just relied on integration testing with the compiler. For constrained existentials, I wanted to do better, so I spent a few days hacking up this little system which can generate graphs of objects with relative references to one another. Currently it's missing the ability to generate a lot of things which I didn't need in order to adequately test the metadata system for constrained existentials.
1 parent a2f6dd4 commit a986d74

File tree

3 files changed

+899
-0
lines changed

3 files changed

+899
-0
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
//===--- MetadataObjectBuilder.h -------------------------------*- C++ -*--===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Defines routines for use with ObjectBuilder that are specifically
14+
// useful for building metadata objects.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef METADATA_OBJECT_BUILDER_H
19+
#define METADATA_OBJECT_BUILDER_H
20+
21+
#include "ObjectBuilder.h"
22+
#include "SpecifierDSL.h"
23+
#include "swift/ABI/Metadata.h"
24+
#include <sstream>
25+
#include <type_traits>
26+
27+
namespace swift {
28+
using namespace specifierDSL;
29+
30+
/// Add a simple ModuleContextDescriptor with the given name.
31+
inline void addModuleContextDescriptor(AnyObjectBuilder &builder,
32+
StringRef moduleName) {
33+
// Context descriptor flags
34+
auto contextFlags = ContextDescriptorFlags(ContextDescriptorKind::Module,
35+
/*generic*/ false,
36+
/*unique*/ true,
37+
/*version*/ 0,
38+
/*kindSpecific*/ 0);
39+
builder.add32(contextFlags.getIntValue());
40+
41+
// Parent
42+
builder.add32(0);
43+
44+
// Name
45+
builder.addRelativeReferenceToString(moduleName);
46+
}
47+
48+
/// Add a simple ProtocolDescriptor with the given base protocols
49+
/// but no other requirements.
50+
inline void addProtocolDescriptor(AnyObjectBuilder &builder,
51+
const ModuleContextDescriptor *module,
52+
StringRef protocolName,
53+
llvm::ArrayRef<ProtocolSpecifier> baseProtocols) {
54+
// Context descriptor flags
55+
auto contextFlags = ContextDescriptorFlags(ContextDescriptorKind::Protocol,
56+
/*generic*/ false,
57+
/*unique*/ true,
58+
/*version*/ 0,
59+
/*kindSpecific*/ 0);
60+
builder.add32(contextFlags.getIntValue());
61+
62+
// Parent
63+
builder.addRelativeIndirectReference(module, /*addend*/ 1);
64+
65+
// Name
66+
builder.addRelativeReferenceToString(protocolName);
67+
68+
// NumRequirementsInSignature
69+
builder.add32(baseProtocols.size());
70+
71+
// NumRequirements
72+
builder.add32(baseProtocols.size());
73+
74+
// Associated type names
75+
builder.add32(0);
76+
77+
ObjectRef<const char> selfType;
78+
if (!baseProtocols.empty()) {
79+
auto subbuilder = builder.createSubobject<const char>(/*align*/ 2);
80+
mangleGenericParamType(subbuilder, 0, 0);
81+
selfType = subbuilder.ref();
82+
}
83+
84+
// Requirement signature requirement descriptors
85+
for (auto &baseProtocol : baseProtocols) {
86+
addConformanceRequirement(builder, selfType, baseProtocol);
87+
}
88+
89+
// Protocol requirement descriptors
90+
for (auto baseProtocol : baseProtocols) {
91+
(void) baseProtocol; // Requirements don't actually collect specifics here
92+
93+
// Flags
94+
auto flags = ProtocolRequirementFlags(
95+
ProtocolRequirementFlags::Kind::BaseProtocol);
96+
builder.add32(flags.getIntValue());
97+
98+
// Default implementation
99+
builder.add32(0);
100+
}
101+
}
102+
103+
template <class T>
104+
class GlobalObjectBuilder {
105+
ObjectBuilder<T> builder;
106+
public:
107+
template <class Fn>
108+
GlobalObjectBuilder(Fn &&fn) {
109+
std::forward<Fn>(fn)(builder);
110+
(void) builder.finish();
111+
}
112+
const T *get() const { return builder.get(); }
113+
};
114+
115+
/// Build a global object with the given lambda.
116+
///
117+
/// This uses a static local to magically cache and preserve the
118+
/// global object. This global caching is uniqued by the template
119+
/// arguments to this function, so callers should not re-use lambdas
120+
/// for different calls.
121+
template <class T, class Fn>
122+
inline const T *buildGlobalObject(Fn &&fn) {
123+
static const GlobalObjectBuilder<T> builder(std::forward<Fn>(fn));
124+
return builder.get();
125+
}
126+
127+
/// Build a global ModuleContextDescriptor with the name returned by the
128+
/// given lambda.
129+
///
130+
/// This uses a static local to magically cache and preserve the
131+
/// global object. This global caching is uniqued by the template
132+
/// arguments to this function, so callers should not re-use lambdas
133+
/// for different calls.
134+
template <class Fn>
135+
inline const ModuleContextDescriptor *
136+
buildGlobalModuleContextDescriptor(Fn &&fn) {
137+
static const GlobalObjectBuilder<ModuleContextDescriptor> builder(
138+
[&](AnyObjectBuilder &builder) {
139+
addModuleContextDescriptor(builder, std::forward<Fn>(fn)());
140+
});
141+
return builder.get();
142+
}
143+
144+
/// Build a global protocol descriptor for an empty protocol with
145+
/// the name returned by the given lambda.
146+
///
147+
/// This uses a static local to magically cache and preserve the
148+
/// global object. This global caching is uniqued by the template
149+
/// arguments to this function, so callers should not re-use lambdas
150+
/// for different calls.
151+
template <class Fn>
152+
inline const ProtocolDescriptor *
153+
buildGlobalProtocolDescriptor(const ModuleContextDescriptor *module, Fn &&fn) {
154+
static const GlobalObjectBuilder<ProtocolDescriptor> builder(
155+
[&](AnyObjectBuilder &builder) {
156+
addProtocolDescriptor(builder, module, std::forward<Fn>(fn)(), {});
157+
});
158+
return builder.get();
159+
}
160+
161+
} // end namespace swift
162+
163+
#endif

0 commit comments

Comments
 (0)