Deshim VirtualExecutor in folly
[hiphop-php.git] / hphp / hhbbc / show.cpp
blob98cb97661e6d31448d10252f32a288f9b6c5f96f
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/hhbbc/representation.h"
18 #include <sstream>
19 #include <boost/algorithm/string.hpp>
20 #include <memory>
21 #include <vector>
23 #include <folly/Conv.h>
24 #include <folly/Format.h>
25 #include <folly/String.h>
26 #include <folly/Range.h>
27 #include <folly/gen/Base.h>
28 #include <folly/gen/String.h>
30 #include "hphp/hhbbc/analyze.h"
31 #include "hphp/hhbbc/cfg.h"
32 #include "hphp/hhbbc/class-util.h"
33 #include "hphp/hhbbc/context.h"
34 #include "hphp/hhbbc/func-util.h"
35 #include "hphp/hhbbc/index.h"
36 #include "hphp/hhbbc/type-structure.h"
37 #include "hphp/hhbbc/type-system.h"
39 #include "hphp/util/text-util.h"
41 namespace HPHP::HHBBC {
43 //////////////////////////////////////////////////////////////////////
46 * Pretty printer for debuggin'.
49 //////////////////////////////////////////////////////////////////////
51 namespace {
53 // Truncate stringization of ArrLikeVals above this size.
54 constexpr size_t maxArrLikeValSize = 1000;
56 std::string indent(int level, const std::string& s) {
57 // Whee; make as many std::string copies as possible.
58 auto const space = std::string(level, ' ');
59 return space + boost::trim_copy(
60 boost::replace_all_copy(s, "\n", "\n" + space)) + "\n";
63 void appendExnTreeString(const php::Func& func,
64 std::string& ret,
65 ExnNodeId p) {
66 do {
67 ret += " " + folly::to<std::string>(p);
68 p = func.exnNodes[p].parent;
69 } while (p != NoExnNodeId);
72 std::string escaped_string(SString str) {
73 std::string ret;
74 if (!str) {
75 ret = "<nullptr>";
76 return ret;
78 auto const sl = str->slice();
79 folly::toAppend("\"", folly::cEscape<std::string>(sl), "\"", &ret);
80 return ret;
83 std::string array_string(SArray arr) {
84 std::string str;
85 staticArrayStreamer(const_cast<ArrayData*>(arr), str);
86 return str;
91 //////////////////////////////////////////////////////////////////////
93 namespace php {
95 std::string local_string(const Func& func, LocalId lid) {
96 if (lid == StackDupId) return "Dup";
97 if (lid == StackThisId) return "This";
98 assertx(lid <= MaxLocalId);
99 auto const& loc = func.locals[lid];
100 return loc.name
101 ? folly::to<std::string>("$", loc.name->data())
102 : folly::to<std::string>("$<unnamed:", lid, ">");
105 std::string show(const Func& func, const Bytecode& bc) {
106 std::string ret;
108 auto append_vsa = [&] (const CompactVector<LSString>& keys) {
109 ret += "<";
110 auto delim = "";
111 for (auto& s : keys) {
112 ret += delim + escaped_string(s);
113 delim = ",";
115 ret += ">";
118 auto append_switch = [&] (const SwitchTab& tab) {
119 ret += "<";
120 for (auto& target : tab) {
121 folly::toAppend(target, " ", &ret);
123 ret += ">";
126 auto append_sswitch = [&] (const SSwitchTab& tab) {
127 ret += "<";
128 for (auto& kv : tab) {
129 folly::toAppend(escaped_string(kv.first), ":", kv.second, " ", &ret);
131 ret += ">";
134 auto append_mkey = [&](MKey mkey) {
135 ret += memberCodeString(mkey.mcode);
137 switch (mkey.mcode) {
138 case MEL: case MPL:
139 folly::toAppend(':', local_string(func, mkey.local.id), &ret);
140 break;
141 case MEC: case MPC:
142 folly::toAppend(':', mkey.idx, &ret);
143 break;
144 case MEI:
145 folly::toAppend(':', mkey.int64, &ret);
146 break;
147 case MET: case MPT: case MQT:
148 folly::toAppend(
149 ":\"", escapeStringForCPP(mkey.litstr->data(), mkey.litstr->size()),
150 '"', &ret
152 break;
153 case MW:
154 break;
158 auto append_lar = [&](const LocalRange& range) {
159 if (!range.count) {
160 folly::toAppend("L:-", &ret);
161 } else {
162 folly::toAppend("L:", local_string(func, range.first), "+",
163 range.count, &ret);
167 auto append_ita = [&](const IterArgs& ita) {
168 auto const print = [&](int32_t local) { return local_string(func, local); };
169 folly::toAppend(show(ita, print), &ret);
172 #define IMM_BLA(n) ret += " "; append_switch(data.targets);
173 #define IMM_SLA(n) ret += " "; append_sswitch(data.targets);
174 #define IMM_IVA(n) folly::toAppend(" ", data.arg##n, &ret);
175 #define IMM_I64A(n) folly::toAppend(" ", data.arg##n, &ret);
176 #define IMM_LA(n) ret += " " + local_string(func, data.loc##n);
177 #define IMM_NLA(n) ret += " " + local_string(func, data.nloc##n.id);
178 #define IMM_ILA(n) ret += " " + local_string(func, data.loc##n);
179 #define IMM_IA(n) folly::toAppend(" iter:", data.iter##n, &ret);
180 #define IMM_DA(n) folly::toAppend(" ", data.dbl##n, &ret);
181 #define IMM_SA(n) folly::toAppend(" ", escaped_string(data.str##n), &ret);
182 #define IMM_RATA(n) folly::toAppend(" ", show(data.rat), &ret);
183 #define IMM_AA(n) ret += " " + array_string(data.arr##n);
184 #define IMM_BA(n) folly::toAppend(" <blk:", data.target##n, ">", &ret);
185 #define IMM_OA_IMPL(n) folly::toAppend(" ", subopToName(data.subop##n), &ret);
186 #define IMM_OA(type) IMM_OA_IMPL
187 #define IMM_VSA(n) ret += " "; append_vsa(data.keys);
188 #define IMM_KA(n) ret += " "; append_mkey(data.mkey);
189 #define IMM_LAR(n) ret += " "; append_lar(data.locrange);
190 #define IMM_ITA(n) ret += " "; append_ita(data.ita);
191 #define IMM_FCA(n) do { \
192 auto const aeTarget = data.fca.asyncEagerTarget() != NoBlockId \
193 ? folly::sformat("<aeblk:{}>", data.fca.asyncEagerTarget()) \
194 : "-"; \
195 folly::toAppend( \
196 " ", show(data.fca.base(), data.fca.inoutArgs(), \
197 data.fca.readonlyArgs(), aeTarget, \
198 data.fca.context()), &ret); \
199 } while (false);
201 #define IMM_NA
202 #define IMM_ONE(x) IMM_##x(1)
203 #define IMM_TWO(x, y) IMM_##x(1); IMM_##y(2);
204 #define IMM_THREE(x, y, z) IMM_TWO(x, y); IMM_##z(3);
205 #define IMM_FOUR(x, y, z, n) IMM_THREE(x, y, z); IMM_##n(4);
206 #define IMM_FIVE(x, y, z, n, m) IMM_FOUR(x, y, z, n); IMM_##m(5);
207 #define IMM_SIX(x, y, z, n, m, o) IMM_FIVE(x, y, z, n, m); IMM_##o(6);
209 #define O(opcode, imms, inputs, outputs, flags) \
210 case Op::opcode: \
212 UNUSED auto const& data = bc.opcode; \
213 UNUSED auto const curOpcode = Op::opcode; \
214 folly::toAppend(#opcode, &ret); \
215 IMM_##imms \
217 break;
219 switch (bc.op) { OPCODES }
221 #undef O
223 #undef IMM_BLA
224 #undef IMM_SLA
225 #undef IMM_IVA
226 #undef IMM_I64A
227 #undef IMM_LA
228 #undef IMM_NLA
229 #undef IMM_ILA
230 #undef IMM_IA
231 #undef IMM_DA
232 #undef IMM_SA
233 #undef IMM_RATA
234 #undef IMM_AA
235 #undef IMM_BA
236 #undef IMM_OA_IMPL
237 #undef IMM_OA
238 #undef IMM_KA
239 #undef IMM_LAR
240 #undef IMM_FCA
242 #undef IMM_NA
243 #undef IMM_ONE
244 #undef IMM_TWO
245 #undef IMM_THREE
246 #undef IMM_FOUR
247 #undef IMM_FIVE
248 #undef IMM_SIX
250 return ret;
253 std::string show(const Func& func, const Block& block) {
254 std::string ret;
256 if (block.exnNodeId != NoExnNodeId) {
257 ret += "(exnNode:";
258 appendExnTreeString(func, ret, block.exnNodeId);
259 ret += ")\n";
262 for (auto& bc : block.hhbcs) ret += show(func, bc) + "\n";
264 if (block.fallthrough != NoBlockId) {
265 folly::toAppend(
266 "(fallthrough blk:",
267 block.fallthrough,
268 ")\n",
269 &ret
273 if (block.throwExit != NoBlockId) {
274 ret += folly::sformat("(throw: blk:{})\n", block.throwExit);
277 return ret;
280 std::string dot_instructions(const Func& func, const Block& b) {
281 using namespace folly::gen;
282 return from(b.hhbcs)
283 | map([&] (const Bytecode& bc) {
284 return "\"" + folly::cEscape<std::string>(show(func, bc)) + "\\n\"";
286 | unsplit<std::string>("+\n")
290 // Output DOT-format graph. Paste into dot -Txlib or similar.
291 std::string dot_cfg(const php::WideFunc& func) {
292 std::string ret;
293 for (auto const bid : rpoSortAddDVs(func)) {
294 auto const b = func.blocks()[bid].get();
295 ret += folly::format(
296 "B{} [ label = \"blk:{}\\n\"+{} ]\n",
297 bid, bid, dot_instructions(*func, *b)).str();
298 bool outputed = false;
299 forEachNormalSuccessor(*b, [&] (BlockId target) {
300 ret += folly::format("B{} -> B{};", bid, target).str();
301 outputed = true;
303 if (outputed) ret += "\n";
304 if (!is_single_nop(*b) && b->throwExit != NoBlockId) {
305 ret += folly::sformat("B{} -> B{} [color=red];\n", bid, b->throwExit);
308 return ret;
311 std::string show(const Func& f) {
312 std::string ret;
313 auto const func = php::WideFunc::cns(&f);
315 #define X(what) if (func->what) folly::toAppend(#what "\n", &ret)
316 X(isClosureBody);
317 X(isAsync);
318 X(isGenerator);
319 X(isPairGenerator);
320 #undef X
322 if (getenv("HHBBC_DUMP_DOT")) {
323 folly::format(&ret,
324 "digraph {} {{\n node [shape=box];\n{}}}\n",
325 func->name, indent(2, dot_cfg(func)));
328 for (auto const bid : func.blockRange()) {
329 auto const blk = func.blocks()[bid].get();
330 if (blk->dead) continue;
331 folly::format(&ret, "block #{}\n{}", bid, indent(2, show(*func, *blk)));
334 visitExnLeaves(*func, [&] (const php::ExnNode& node) {
335 folly::format(&ret, "exn node #{} ", node.idx);
336 if (node.parent != NoExnNodeId) {
337 folly::format(&ret, "(^{}) ", node.parent);
339 ret += folly::to<std::string>("catch->", node.region.catchEntry) + '\n';
342 return ret;
345 std::string show(const Class& cls) {
346 std::string ret;
347 folly::toAppend(
348 "class ",
349 cls.name->data(),
350 &ret
352 if (cls.parentName) {
353 folly::toAppend(" extends ", cls.parentName->data(), &ret);
355 ret += ":\n";
356 for (auto& i : cls.interfaceNames) {
357 folly::toAppend(" implements ", i->data(), "\n", &ret);
359 for (auto& m : cls.methods) {
360 if (!m) continue;
361 folly::toAppend(
362 " method ",
363 m->name->data(), ":\n",
364 indent(4, show(*m)),
365 &ret
368 return ret;
371 std::string show(const Unit& unit,
372 const std::vector<const php::Class*>& classes,
373 const std::vector<const php::Func*>& funcs) {
374 std::string ret;
375 folly::toAppend(
376 "Unit ", unit.filename->data(), "\n",
377 &ret
380 for (auto const c : classes) {
381 folly::toAppend(
382 indent(2, show(*c)),
383 &ret
387 for (auto const f : funcs) {
388 folly::toAppend(
389 " function ", f->name->data(), ":\n",
390 indent(4, show(*f)),
391 &ret
395 folly::toAppend("\n", &ret);
396 return ret;
399 std::string show(const Unit& unit, const Index& index) {
400 std::vector<const php::Class*> classes;
401 std::vector<const php::Func*> funcs;
402 index.for_each_unit_class(
403 unit,
404 [&] (const php::Class& c) { classes.emplace_back(&c); }
406 index.for_each_unit_func(
407 unit,
408 [&] (const php::Func& f) { funcs.emplace_back(&f); }
411 std::sort(
412 begin(classes), end(classes),
413 [] (const php::Class* a, const php::Class* b) {
414 return string_data_lt_type{}(a->name, b->name);
417 std::sort(
418 begin(funcs), end(funcs),
419 [] (const php::Func* a, const php::Func* b) {
420 return string_data_lt{}(a->name, b->name);
424 return show(unit, classes, funcs);
427 std::string show(const Unit& unit, const Program& p) {
428 std::vector<const php::Class*> classes;
429 std::vector<const php::Func*> funcs;
430 for (auto const& c : p.classes) {
431 if (c->unit != unit.filename) continue;
432 classes.emplace_back(c.get());
434 for (auto const& f : p.funcs) {
435 if (f->unit != unit.filename) continue;
436 funcs.emplace_back(f.get());
438 return show(unit, classes, funcs);
441 std::string show(const Program& p, const Index& index) {
442 using namespace folly::gen;
443 return from(p.units)
444 | map([&] (const std::unique_ptr<php::Unit>& u) { return show(*u, index); })
445 | unsplit<std::string>("--------------\n")
449 std::string show(SrcLoc loc) {
450 return folly::sformat("{}:{}-{}:{}",
451 loc.start.line, loc.start.col,
452 loc.past.line, loc.past.col);
457 //////////////////////////////////////////////////////////////////////
459 std::string show(const Type& t) {
461 * Type pretty printing
463 * When you allow arbitrary trep combinations with specializations,
464 * pretty printing the Type in its most concise form can be tricky.
466 * treps (in general) are printed by attempting a greedy match among
467 * the predefined treps (the ones with names). For a trep b, until
468 * there are no bits left in b, find the largest (most bits)
469 * predefined trep which is fully contained within b. Add that
470 * predefined trep's name to the output list and remove its bits
471 * from b. This should produce the shortest list of names (joined
472 * with a |) which represents the trep.
474 * If a specialization is present, first all of the bits which
475 * support that specialization are gathered together (using the
476 * above scheme) and printed first. If there is more than one name,
477 * they are surrounded with {}. Then the specialization is attached
478 * to the end. Any remaining trep bits are then processed (again
479 * using the above scheme) and combined with |s as usual. This
480 * scheme makes it clear which bits the specialization applies to,
481 * and which ones it does not.
483 * TInitNull is treated a bit different. Instead of literally
484 * TInitNull, it is rendered as a ? prefix. Even a predefined type
485 * like TOptInt gets this treatment (it becomes ?Int). If the
486 * remaining bits have multiple names, they are again grouped inside
487 * a {} and the ? is applied to the front of the grouping. This
488 * makes it clear it binds to the group (like ?{Int|Str}), whereas
489 * something like ?Int|Str is more ambiguous.
491 * Putting it all together, for example, the union of
492 * TDict(Int:Int), TKeyset(Int,Int), TInitNull, TBool, and TObj
493 * would be rendered as:
495 * ?{{Dict|Keyset}(Int:Int)|Bool|Obj}
498 // NB: We want this function be usuable even on invalid/malformed
499 // types (for example if we want to print the type from within
500 // checkInvariants()). Therefore we don't make any assumptions about
501 // the Type being sane and don't assert anything. We also just deal
502 // with bits and don't copy or manipulate the actual Type.
504 // Sorted vector of all the predefined types, from highest bitcount
505 // to shortest. This lets us implement a greedy covering of bits to
506 // names.
507 static auto const sorted = [] {
508 std::vector<std::pair<trep, std::string>> types{
509 #define X(y, ...) { B##y, #y },
510 HHBBC_TYPE_PREDEFINED(X)
511 #undef X
513 std::sort(
514 types.begin(), types.end(),
515 [](auto const& a, auto const& b) {
516 auto const pop1 = folly::popcount((uint64_t)a.first);
517 auto const pop2 = folly::popcount((uint64_t)b.first);
518 if (pop1 != pop2) return pop1 > pop2;
519 // Special case here: The static/counted axis of array bits
520 // has the same size as the empty/non-empty axis of array
521 // bits. We want to give preferencial treatment to the
522 // empty/non-empty bits to break this symmetric, and also
523 // because it produces more natural looking unions. This is
524 // purely an aesthetic choice.
525 auto const sizish = [] (trep bits) {
526 return couldBe(bits, BArrLikeE) != couldBe(bits, BArrLikeN);
528 auto const s1 = sizish(a.first);
529 auto const s2 = sizish(b.first);
530 if (s1 != s2) return s1 > s2;
531 return a.second < b.second;
534 return types;
535 }();
537 // Given a trep, produce a string of the predefined types (joined
538 // with |) which represent this type. The number of such names is
539 // also returned.
540 auto const gather = [&] (trep bits) -> std::pair<std::string, size_t> {
541 // Below loop only works if there's a bit. If there's not, we know
542 // we're Bottom anyways.
543 if (!bits) return std::make_pair("Bottom", 1);
545 std::string ret;
546 size_t count = 0;
547 for (auto const& ty : sorted) {
548 // Bottom ends up in the list. Just skip it.
549 if (!ty.first) continue;
550 // Check if this predefined type is covered by our bits.
551 if ((ty.first & bits) != ty.first) continue;
552 // It is, remove it from the bits and add its name to the list.
553 bits -= ty.first;
555 if (!ret.empty()) ret += "|";
556 // Special handling of Opt* types, turn them into ?.
557 if (!ty.second.compare(0, 3, "Opt", 3)) {
558 auto temp = ty.second.substr(2);
559 temp[0] = '?';
560 ret += temp;
561 } else {
562 ret += ty.second;
564 ++count;
566 if (!bits) break;
568 if (bits) {
569 // We'll only get here if we exhausted all the predefined types
570 // and didn't consume all the bits. This can only happen with
571 // invalid types. However, since we want to be robust, just let
572 // it be known something's wrong and continue.
573 ret += ret.empty() ? "???" : "|???";
574 ++count;
576 return std::make_pair(ret, count);
579 auto const showElem = [&] (const Type& key, const Type& val) -> std::string {
580 return show(key) + ":" + show(val);
583 auto const showMapElem = [&] (TypedValue k, const MapElem& m) {
584 auto const key = [&] {
585 if (isIntType(k.m_type)) return ival(k.m_data.num);
586 assertx(k.m_type == KindOfPersistentString);
587 switch (m.keyStaticness) {
588 case TriBool::Yes: return sval(k.m_data.pstr);
589 case TriBool::Maybe: return sval_nonstatic(k.m_data.pstr);
590 case TriBool::No: return sval_counted(k.m_data.pstr);
592 always_assert(false);
593 }();
594 return showElem(key, m.val);
597 // Given a trep, first gather together the support bits for any
598 // specialization. Add the specialization string, then gather the
599 // remaining (non-support) bits. If there's no specialization, just
600 // delegate to gather().
601 auto const gatherForSpec = [&] (trep bits) {
602 // Gather the supoprt and the non-support bits, then combine them
603 // into a string (with the spec in the middle).
604 auto const impl = [&] (trep mask, const std::string& spec) {
605 auto const [specPart, specMatches] = gather(bits & mask);
606 auto const [restPart, restMatches] = [&] {
607 return (bits & ~mask)
608 ? gather(bits & ~mask)
609 : std::make_pair(std::string{}, size_t{0});
610 }();
612 auto const ret = folly::sformat(
613 "{}{}{}{}{}{}",
614 specMatches > 1 ? "{" : "",
615 specPart,
616 specMatches > 1 ? "}" : "",
617 spec,
618 restMatches > 0 ? "|" : "",
619 restPart
621 return std::make_pair(ret, specMatches + restMatches);
624 auto const showDCls = [&] (const DCls& dcls, bool isObj) {
625 auto const lt = [&] {
626 assertx(!dcls.isExact());
627 return !isObj && !dcls.containsNonRegular() ? "<-" : "<=";
630 auto const eq = [&] (res::Class cls) {
631 return isObj || dcls.containsNonRegular() || cls.hasCompleteChildren()
632 ? "=" : "=-";
635 std::string ret;
636 if (dcls.isExact()) {
637 folly::toAppend(eq(dcls.cls()), show(dcls.cls()), &ret);
638 } else if (dcls.isSub()) {
639 folly::toAppend(lt(), show(dcls.cls()), &ret);
640 } else if (dcls.isIsect()) {
641 folly::toAppend(
642 lt(),
643 "{",
644 [&] {
645 using namespace folly::gen;
646 return from(dcls.isect())
647 | map([] (res::Class c) { return show(c); })
648 | unsplit<std::string>("&");
649 }(),
650 "}",
651 &ret
653 } else {
654 auto const [e, i] = dcls.isectAndExact();
655 folly::toAppend(
656 eq(e),
657 "{",
658 show(e),
659 "}&",
660 lt(),
661 "{",
662 [&, i=i] {
663 using namespace folly::gen;
664 return from(*i)
665 | map([] (res::Class c) { return show(c); })
666 | unsplit<std::string>("&");
667 }(),
668 "}",
669 &ret
672 if (dcls.isCtx()) folly::toAppend(" this", &ret);
673 return ret;
676 switch (t.m_dataTag) {
677 case DataTag::Obj:
678 return impl(BObj, showDCls(t.m_data.dobj, true));
679 case DataTag::WaitHandle:
680 return impl(
681 BObj,
682 folly::sformat("=WaitH<{}>", show(t.m_data.dwh->inner))
684 case DataTag::Cls:
685 return impl(BCls, showDCls(t.m_data.dcls, false));
686 case DataTag::ArrLikePacked:
687 return impl(
688 BArrLikeN,
689 folly::sformat(
690 "({})",
691 [&] {
692 using namespace folly::gen;
693 return from(t.m_data.packed->elems)
694 | map([&] (const Type& t) { return show(t); })
695 | unsplit<std::string>(",");
699 case DataTag::ArrLikePackedN:
700 return impl(
701 BArrLikeN,
702 folly::sformat("([{}])", show(t.m_data.packedn->type))
704 case DataTag::ArrLikeMap:
705 return impl(
706 BArrLikeN,
707 folly::sformat(
708 "({}{})",
709 [&] {
710 using namespace folly::gen;
711 return from(t.m_data.map->map)
712 | map([&] (const std::pair<TypedValue,MapElem>& kv) {
713 return showMapElem(kv.first, kv.second);
715 | unsplit<std::string>(",");
716 }(),
717 [&] {
718 if (!t.m_data.map->hasOptElements()) return std::string{};
719 return folly::sformat(
720 ",...[{}]",
721 showElem(t.m_data.map->optKey, t.m_data.map->optVal)
726 case DataTag::ArrLikeMapN:
727 return impl(
728 BArrLikeN,
729 folly::sformat(
730 "([{}])",
731 showElem(t.m_data.mapn->key, t.m_data.mapn->val)
734 case DataTag::ArrLikeVal:
735 return impl(
736 BArrLikeN,
737 folly::sformat(
738 "~{}",
739 [&] {
740 auto s = array_string(t.m_data.aval);
741 if (s.size() > maxArrLikeValSize) {
742 s.resize(maxArrLikeValSize);
743 s += "...(truncated)";
745 return s;
749 case DataTag::Str:
750 return impl(BStr, folly::sformat("={}", escaped_string(t.m_data.sval)));
751 case DataTag::LazyCls:
752 return impl(BLazyCls,
753 folly::sformat("={}",
754 escaped_string(t.m_data.lazyclsval)));
755 case DataTag::EnumClassLabel:
756 return impl(BEnumClassLabel,
757 folly::sformat("={}", escaped_string(t.m_data.eclval)));
758 case DataTag::Int: return impl(BInt, folly::sformat("={}", t.m_data.ival));
759 case DataTag::Dbl: return impl(BDbl, folly::sformat("={}", t.m_data.dval));
760 case DataTag::None: return gather(bits);
762 not_reached();
765 // Produce the string, then perform the TInitNull special processing
766 // as described above.
767 auto ret = [&] {
768 auto gathered = gatherForSpec(t.bits());
769 // If there trep has TInitNull in it, but the gathering only
770 // matched one thing, it was a Opt* type, and it was already
771 // turned into ?, so we're done. If not, redo the gathering with
772 // the TInitNull removed. Add it to the front of the resultant
773 // string.
774 if (couldBe(t.bits(), BInitNull) && gathered.second > 1) {
775 gathered = gatherForSpec(t.bits() & ~BInitNull);
776 return folly::sformat(
777 "?{}{}{}",
778 gathered.second > 1 ? "{" : "",
779 gathered.first,
780 gathered.second > 1 ? "}" : ""
783 return gathered.first;
784 }();
786 return ret;
789 //////////////////////////////////////////////////////////////////////
791 std::string show(Context ctx) {
792 if (!ctx.func) {
793 if (!ctx.cls) {
794 if (!ctx.unit) return "-";
795 return ctx.unit->toCppString();
797 return ctx.cls->name->toCppString();
799 auto ret = std::string{};
800 if (ctx.cls) {
801 ret = ctx.cls->name->toCppString();
802 if (ctx.cls != ctx.func->cls) {
803 folly::format(&ret, "({})", ctx.func->cls->name);
805 ret += "::";
807 ret += ctx.func->name->toCppString();
808 return ret;
811 //////////////////////////////////////////////////////////////////////
813 std::string show(const ConstIndex& idx) {
814 return folly::sformat("{}:{}", idx.cls, idx.idx);
817 std::string show(const MethRef& m) {
818 return folly::sformat("{}:{}", m.cls, m.idx);
821 //////////////////////////////////////////////////////////////////////
823 std::string show(FuncClsUnit fc) {
824 if (auto const f = fc.func()) {
825 return folly::sformat("func {}", f->name);
826 } else if (auto const c = fc.cls()) {
827 return folly::sformat("class {}", c->name);
828 } else if (auto const u = fc.unit()) {
829 return folly::sformat("unit {}", u->filename);
830 } else {
831 return "(null)";
835 //////////////////////////////////////////////////////////////////////
837 std::string show(const PropLookupResult& r) {
838 return folly::sformat(
839 "{{{},ty:{},found:{},const:{},late:{},init:{},internal:{}}}",
840 r.name,
841 show(r.ty),
842 show(r.found),
843 show(r.isConst),
844 show(r.lateInit),
845 r.classInitMightRaise,
846 show(r.internal)
850 std::string show(const PropMergeResult& r) {
851 return folly::sformat(
852 "{{adjusted:{},throws:{}}}",
853 show(r.adjusted),
854 show(r.throws)
858 std::string show(const ClsConstLookupResult& r) {
859 return folly::sformat(
860 "{{ty:{},found:{},throw:{}}}",
861 show(r.ty),
862 show(r.found),
863 r.mightThrow
867 std::string show(const ClsTypeConstLookupResult& r) {
868 return folly::sformat(
869 "{{ty:{},fail:{},sens:{},found:{},abstract:{}}}",
870 show(r.resolution.type),
871 r.resolution.mightFail,
872 r.resolution.contextSensitive,
873 show(r.found),
874 show(r.abstract)
878 std::string show(const ConstraintType& t) {
879 const char* cts;
880 switch (t.coerceClassToString) {
881 case TriBool::Yes: cts = "yes"; break;
882 case TriBool::No: cts = "no"; break;
883 case TriBool::Maybe: cts = "maybe"; break;
885 return folly::sformat("ConstraintType{{lower:{}, upper:{}, coerceClassToString:{}, maybeMixed:{}}}",
886 show(t.lower),
887 show(t.upper),
888 cts,
889 t.maybeMixed ? "true" : "false"
893 //////////////////////////////////////////////////////////////////////