Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/src/DcrLogger.cpp
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
3
#include "Luau/DcrLogger.h"
4
5
#include <algorithm>
6
7
#include "Luau/JsonEmitter.h"
8
9
namespace Luau
10
{
11
12
template<typename T>
13
static std::string toPointerId(const T* ptr)
14
{
15
return std::to_string(reinterpret_cast<size_t>(ptr));
16
}
17
18
static std::string toPointerId(NotNull<const Constraint> ptr)
19
{
20
return std::to_string(reinterpret_cast<size_t>(ptr.get()));
21
}
22
23
namespace Json
24
{
25
26
template<typename T>
27
void write(JsonEmitter& emitter, const T* ptr)
28
{
29
write(emitter, toPointerId(ptr));
30
}
31
32
void write(JsonEmitter& emitter, NotNull<const Constraint> ptr)
33
{
34
write(emitter, toPointerId(ptr));
35
}
36
37
void write(JsonEmitter& emitter, const Location& location)
38
{
39
ArrayEmitter a = emitter.writeArray();
40
a.writeValue(location.begin.line);
41
a.writeValue(location.begin.column);
42
a.writeValue(location.end.line);
43
a.writeValue(location.end.column);
44
a.finish();
45
}
46
47
void write(JsonEmitter& emitter, const ErrorSnapshot& snapshot)
48
{
49
ObjectEmitter o = emitter.writeObject();
50
o.writePair("message", snapshot.message);
51
o.writePair("location", snapshot.location);
52
o.finish();
53
}
54
55
void write(JsonEmitter& emitter, const BindingSnapshot& snapshot)
56
{
57
ObjectEmitter o = emitter.writeObject();
58
o.writePair("typeId", snapshot.typeId);
59
o.writePair("typeString", snapshot.typeString);
60
o.writePair("location", snapshot.location);
61
o.finish();
62
}
63
64
void write(JsonEmitter& emitter, const TypeBindingSnapshot& snapshot)
65
{
66
ObjectEmitter o = emitter.writeObject();
67
o.writePair("typeId", snapshot.typeId);
68
o.writePair("typeString", snapshot.typeString);
69
o.finish();
70
}
71
72
template<typename K, typename V>
73
void write(JsonEmitter& emitter, const DenseHashMap<const K*, V>& map)
74
{
75
ObjectEmitter o = emitter.writeObject();
76
for (const auto& [k, v] : map)
77
o.writePair(toPointerId(k), v);
78
o.finish();
79
}
80
81
void write(JsonEmitter& emitter, const ExprTypesAtLocation& tys)
82
{
83
ObjectEmitter o = emitter.writeObject();
84
o.writePair("location", tys.location);
85
o.writePair("ty", toPointerId(tys.ty));
86
87
if (tys.expectedTy)
88
o.writePair("expectedTy", toPointerId(*tys.expectedTy));
89
90
o.finish();
91
}
92
93
void write(JsonEmitter& emitter, const AnnotationTypesAtLocation& tys)
94
{
95
ObjectEmitter o = emitter.writeObject();
96
o.writePair("location", tys.location);
97
o.writePair("resolvedTy", toPointerId(tys.resolvedTy));
98
o.finish();
99
}
100
101
void write(JsonEmitter& emitter, const ConstraintGenerationLog& log)
102
{
103
ObjectEmitter o = emitter.writeObject();
104
o.writePair("source", log.source);
105
o.writePair("errors", log.errors);
106
o.writePair("exprTypeLocations", log.exprTypeLocations);
107
o.writePair("annotationTypeLocations", log.annotationTypeLocations);
108
109
o.finish();
110
}
111
112
void write(JsonEmitter& emitter, const ScopeSnapshot& snapshot)
113
{
114
ObjectEmitter o = emitter.writeObject();
115
o.writePair("bindings", snapshot.bindings);
116
o.writePair("typeBindings", snapshot.typeBindings);
117
o.writePair("typePackBindings", snapshot.typePackBindings);
118
o.writePair("children", snapshot.children);
119
o.finish();
120
}
121
122
void write(JsonEmitter& emitter, const ConstraintBlock& block)
123
{
124
ObjectEmitter o = emitter.writeObject();
125
o.writePair("stringification", block.stringification);
126
127
auto go = [&o](auto&& t)
128
{
129
using T = std::decay_t<decltype(t)>;
130
131
o.writePair("id", toPointerId(t));
132
133
if constexpr (std::is_same_v<T, TypeId>)
134
{
135
o.writePair("kind", "type");
136
}
137
else if constexpr (std::is_same_v<T, TypePackId>)
138
{
139
o.writePair("kind", "typePack");
140
}
141
else if constexpr (std::is_same_v<T, NotNull<const Constraint>>)
142
{
143
o.writePair("kind", "constraint");
144
}
145
else
146
static_assert(always_false_v<T>, "non-exhaustive possibility switch");
147
};
148
149
visit(go, block.target);
150
151
o.finish();
152
}
153
154
void write(JsonEmitter& emitter, const ConstraintSnapshot& snapshot)
155
{
156
ObjectEmitter o = emitter.writeObject();
157
o.writePair("stringification", snapshot.stringification);
158
o.writePair("location", snapshot.location);
159
o.writePair("blocks", snapshot.blocks);
160
o.finish();
161
}
162
163
void write(JsonEmitter& emitter, const BoundarySnapshot& snapshot)
164
{
165
ObjectEmitter o = emitter.writeObject();
166
o.writePair("rootScope", snapshot.rootScope);
167
o.writePair("unsolvedConstraints", snapshot.unsolvedConstraints);
168
o.writePair("typeStrings", snapshot.typeStrings);
169
o.finish();
170
}
171
172
void write(JsonEmitter& emitter, const StepSnapshot& snapshot)
173
{
174
ObjectEmitter o = emitter.writeObject();
175
o.writePair("currentConstraint", snapshot.currentConstraint);
176
o.writePair("forced", snapshot.forced);
177
o.writePair("unsolvedConstraints", snapshot.unsolvedConstraints);
178
o.writePair("rootScope", snapshot.rootScope);
179
o.writePair("typeStrings", snapshot.typeStrings);
180
o.finish();
181
}
182
183
void write(JsonEmitter& emitter, const TypeSolveLog& log)
184
{
185
ObjectEmitter o = emitter.writeObject();
186
o.writePair("initialState", log.initialState);
187
o.writePair("stepStates", log.stepStates);
188
o.writePair("finalState", log.finalState);
189
o.finish();
190
}
191
192
void write(JsonEmitter& emitter, const TypeCheckLog& log)
193
{
194
ObjectEmitter o = emitter.writeObject();
195
o.writePair("errors", log.errors);
196
o.finish();
197
}
198
199
} // namespace Json
200
201
static ScopeSnapshot snapshotScope(const Scope* scope, ToStringOptions& opts)
202
{
203
std::unordered_map<Name, BindingSnapshot> bindings;
204
std::unordered_map<Name, TypeBindingSnapshot> typeBindings;
205
std::unordered_map<Name, TypeBindingSnapshot> typePackBindings;
206
std::vector<ScopeSnapshot> children;
207
208
for (const auto& [name, binding] : scope->bindings)
209
{
210
std::string id = std::to_string(reinterpret_cast<size_t>(binding.typeId));
211
ToStringResult result = toStringDetailed(binding.typeId, opts);
212
213
bindings[name.c_str()] = BindingSnapshot{
214
id,
215
result.name,
216
binding.location,
217
};
218
}
219
220
for (const auto& [name, tf] : scope->exportedTypeBindings)
221
{
222
std::string id = std::to_string(reinterpret_cast<size_t>(tf.type));
223
224
typeBindings[name] = TypeBindingSnapshot{
225
id,
226
toString(tf.type, opts),
227
};
228
}
229
230
for (const auto& [name, tf] : scope->privateTypeBindings)
231
{
232
std::string id = std::to_string(reinterpret_cast<size_t>(tf.type));
233
234
typeBindings[name] = TypeBindingSnapshot{
235
id,
236
toString(tf.type, opts),
237
};
238
}
239
240
for (const auto& [name, tp] : scope->privateTypePackBindings)
241
{
242
std::string id = std::to_string(reinterpret_cast<size_t>(tp));
243
244
typePackBindings[name] = TypeBindingSnapshot{
245
id,
246
toString(tp, opts),
247
};
248
}
249
250
for (const auto& child : scope->children)
251
{
252
children.push_back(snapshotScope(child.get(), opts));
253
}
254
255
return ScopeSnapshot{
256
std::move(bindings),
257
std::move(typeBindings),
258
std::move(typePackBindings),
259
std::move(children),
260
};
261
}
262
263
std::string DcrLogger::compileOutput()
264
{
265
Json::JsonEmitter emitter;
266
Json::ObjectEmitter o = emitter.writeObject();
267
o.writePair("generation", generationLog);
268
o.writePair("solve", solveLog);
269
o.writePair("check", checkLog);
270
o.finish();
271
272
return emitter.str();
273
}
274
275
void DcrLogger::captureSource(std::string source)
276
{
277
generationLog.source = std::move(source);
278
}
279
280
void DcrLogger::captureGenerationModule(const ModulePtr& module)
281
{
282
generationLog.exprTypeLocations.reserve(module->astTypes.size());
283
for (const auto& [expr, ty] : module->astTypes)
284
{
285
ExprTypesAtLocation tys;
286
tys.location = expr->location;
287
tys.ty = ty;
288
289
if (auto expectedTy = module->astExpectedTypes.find(expr))
290
tys.expectedTy = *expectedTy;
291
292
generationLog.exprTypeLocations.push_back(tys);
293
}
294
295
generationLog.annotationTypeLocations.reserve(module->astResolvedTypes.size());
296
for (const auto& [annot, ty] : module->astResolvedTypes)
297
{
298
AnnotationTypesAtLocation tys;
299
tys.location = annot->location;
300
tys.resolvedTy = ty;
301
302
generationLog.annotationTypeLocations.push_back(tys);
303
}
304
}
305
306
void DcrLogger::captureGenerationError(const TypeError& error)
307
{
308
std::string stringifiedError = toString(error);
309
generationLog.errors.push_back(
310
ErrorSnapshot{
311
/* message */ std::move(stringifiedError),
312
/* location */ error.location,
313
}
314
);
315
}
316
317
void DcrLogger::pushBlock(NotNull<const Constraint> constraint, TypeId block)
318
{
319
constraintBlocks[constraint].push_back(block);
320
}
321
322
void DcrLogger::pushBlock(NotNull<const Constraint> constraint, TypePackId block)
323
{
324
constraintBlocks[constraint].push_back(block);
325
}
326
327
void DcrLogger::pushBlock(NotNull<const Constraint> constraint, NotNull<const Constraint> block)
328
{
329
constraintBlocks[constraint].push_back(block);
330
}
331
332
void DcrLogger::popBlock(TypeId block)
333
{
334
for (auto& [_, list] : constraintBlocks)
335
{
336
list.erase(std::remove(list.begin(), list.end(), block), list.end());
337
}
338
}
339
340
void DcrLogger::popBlock(TypePackId block)
341
{
342
for (auto& [_, list] : constraintBlocks)
343
{
344
list.erase(std::remove(list.begin(), list.end(), block), list.end());
345
}
346
}
347
348
void DcrLogger::popBlock(NotNull<const Constraint> block)
349
{
350
for (auto& [_, list] : constraintBlocks)
351
{
352
list.erase(std::remove(list.begin(), list.end(), block), list.end());
353
}
354
}
355
356
static void snapshotTypeStrings(
357
const std::vector<ExprTypesAtLocation>& interestedExprs,
358
const std::vector<AnnotationTypesAtLocation>& interestedAnnots,
359
DenseHashMap<const void*, std::string>& map,
360
ToStringOptions& opts
361
)
362
{
363
for (const ExprTypesAtLocation& tys : interestedExprs)
364
{
365
map[tys.ty] = toString(tys.ty, opts);
366
367
if (tys.expectedTy)
368
map[*tys.expectedTy] = toString(*tys.expectedTy, opts);
369
}
370
371
for (const AnnotationTypesAtLocation& tys : interestedAnnots)
372
{
373
map[tys.resolvedTy] = toString(tys.resolvedTy, opts);
374
}
375
}
376
377
void DcrLogger::captureBoundaryState(
378
BoundarySnapshot& target,
379
const Scope* rootScope,
380
const std::vector<NotNull<const Constraint>>& unsolvedConstraints
381
)
382
{
383
target.rootScope = snapshotScope(rootScope, opts);
384
target.unsolvedConstraints.clear();
385
386
for (NotNull<const Constraint> c : unsolvedConstraints)
387
{
388
target.unsolvedConstraints[c.get()] = {
389
toString(*c.get(), opts),
390
c->location,
391
snapshotBlocks(c),
392
};
393
}
394
395
snapshotTypeStrings(generationLog.exprTypeLocations, generationLog.annotationTypeLocations, target.typeStrings, opts);
396
}
397
398
void DcrLogger::captureInitialSolverState(const Scope* rootScope, const std::vector<NotNull<const Constraint>>& unsolvedConstraints)
399
{
400
captureBoundaryState(solveLog.initialState, rootScope, unsolvedConstraints);
401
}
402
403
StepSnapshot DcrLogger::prepareStepSnapshot(
404
const Scope* rootScope,
405
NotNull<const Constraint> current,
406
bool force,
407
const std::vector<NotNull<const Constraint>>& unsolvedConstraints
408
)
409
{
410
ScopeSnapshot scopeSnapshot = snapshotScope(rootScope, opts);
411
DenseHashMap<const Constraint*, ConstraintSnapshot> constraints{nullptr};
412
413
for (NotNull<const Constraint> c : unsolvedConstraints)
414
{
415
constraints[c.get()] = {
416
toString(*c.get(), opts),
417
c->location,
418
snapshotBlocks(c),
419
};
420
}
421
422
DenseHashMap<const void*, std::string> typeStrings{nullptr};
423
snapshotTypeStrings(generationLog.exprTypeLocations, generationLog.annotationTypeLocations, typeStrings, opts);
424
425
return StepSnapshot{
426
current,
427
force,
428
std::move(constraints),
429
std::move(scopeSnapshot),
430
std::move(typeStrings),
431
};
432
}
433
434
void DcrLogger::commitStepSnapshot(StepSnapshot snapshot)
435
{
436
solveLog.stepStates.push_back(std::move(snapshot));
437
}
438
439
void DcrLogger::captureFinalSolverState(const Scope* rootScope, const std::vector<NotNull<const Constraint>>& unsolvedConstraints)
440
{
441
captureBoundaryState(solveLog.finalState, rootScope, unsolvedConstraints);
442
}
443
444
void DcrLogger::captureTypeCheckError(const TypeError& error)
445
{
446
std::string stringifiedError = toString(error);
447
checkLog.errors.push_back(
448
ErrorSnapshot{
449
/* message */ std::move(stringifiedError),
450
/* location */ error.location,
451
}
452
);
453
}
454
455
std::vector<ConstraintBlock> DcrLogger::snapshotBlocks(NotNull<const Constraint> c)
456
{
457
auto it = constraintBlocks.find(c);
458
if (it == constraintBlocks.end())
459
{
460
return {};
461
}
462
463
std::vector<ConstraintBlock> snapshot;
464
465
for (const ConstraintBlockTarget& target : it->second)
466
{
467
if (const TypeId* ty = get_if<TypeId>(&target))
468
{
469
snapshot.push_back({
470
*ty,
471
toString(*ty, opts),
472
});
473
}
474
else if (const TypePackId* tp = get_if<TypePackId>(&target))
475
{
476
snapshot.push_back({
477
*tp,
478
toString(*tp, opts),
479
});
480
}
481
else if (const NotNull<const Constraint>* c = get_if<NotNull<const Constraint>>(&target))
482
{
483
snapshot.push_back({
484
*c,
485
toString(*(c->get()), opts),
486
});
487
}
488
else
489
{
490
LUAU_ASSERT(0);
491
}
492
}
493
494
return snapshot;
495
}
496
497
} // namespace Luau
498
499