this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "debugging.h"
3
4#include <iostream>
5
6#include "gmock/gmock-matchers.h"
7#include "gtest/gtest.h"
8
9#include "bytecode.h"
10#include "dict-builtins.h"
11#include "test-utils.h"
12
13namespace py {
14namespace testing {
15
16using DebuggingTests = RuntimeFixture;
17
18static RawObject makeTestCode(Thread* thread) {
19 Runtime* runtime = thread->runtime();
20 HandleScope scope(thread);
21 const byte bytes_array[] = {LOAD_CONST, 0, LOAD_ATTR, 0, RETURN_VALUE, 0};
22 Bytes bytes(&scope, runtime->newBytesWithAll(bytes_array));
23 Object const0(&scope, runtime->newStrFromCStr("const0"));
24 Tuple consts(&scope, runtime->newTupleWith1(const0));
25 Object name0(&scope, runtime->newStrFromCStr("name0"));
26 Tuple names(&scope, runtime->newTupleWith1(name0));
27 Object argument0(&scope, runtime->newStrFromCStr("argument0"));
28 Object varargs(&scope, runtime->newStrFromCStr("varargs"));
29 Object varkeyargs(&scope, runtime->newStrFromCStr("varkeyargs"));
30 Object variable0(&scope, runtime->newStrFromCStr("variable0"));
31 Tuple varnames(&scope, runtime->newTupleWith4(argument0, varargs, varkeyargs,
32 variable0));
33 Object freevar0(&scope, runtime->newStrFromCStr("freevar0"));
34 Tuple freevars(&scope, runtime->newTupleWith1(freevar0));
35 Object cellvar0(&scope, runtime->newStrFromCStr("cellvar0"));
36 Object cellvar1(&scope, runtime->newStrFromCStr("cellvar1"));
37 Object cellvar2(&scope, runtime->newStrFromCStr("cellvar2"));
38 Tuple cellvars(&scope, runtime->newTupleWith3(cellvar0, cellvar1, cellvar2));
39 Str filename(&scope, runtime->newStrFromCStr("filename0"));
40 Str name(&scope, runtime->newStrFromCStr("name0"));
41 DCHECK(freevars.length() != cellvars.length(),
42 "it's helpful for debugging if they are different lengths");
43 Object lnotab(&scope, Bytes::empty());
44 word argcount = 1;
45 word posonlyargcount = 0;
46 word kwonlyargcount = 0;
47 word nlocals = 4;
48 word stacksize = 1;
49 word flags = Code::kNested | Code::kOptimized | Code::kNewlocals |
50 Code::kVarargs | Code::kVarkeyargs;
51 return runtime->newCode(argcount, posonlyargcount, kwonlyargcount, nlocals,
52 stacksize, flags, bytes, consts, names, varnames,
53 freevars, cellvars, filename, name, 0, lnotab);
54}
55
56static RawObject makeTestFunction(Thread* thread) {
57 HandleScope scope(thread);
58 Runtime* runtime = thread->runtime();
59 Object qualname(&scope, runtime->newStrFromCStr("footype.baz"));
60 Code code(&scope, makeTestCode(thread));
61 Module module(&scope, findMainModule(runtime));
62 Function func(&scope,
63 runtime->newFunctionWithCode(thread, qualname, code, module));
64 func.setEntry(reinterpret_cast<Function::Entry>(100));
65 func.setEntryEx(reinterpret_cast<Function::Entry>(200));
66 func.setEntryKw(reinterpret_cast<Function::Entry>(300));
67 Dict annotations(&scope, runtime->newDict());
68 Str return_name(&scope, runtime->newStrFromCStr("return"));
69 Object int_type(&scope, runtime->typeAt(LayoutId::kInt));
70 dictAtPutByStr(thread, annotations, return_name, int_type);
71 func.setAnnotations(*annotations);
72 func.setClosure(runtime->emptyTuple());
73 Dict kw_defaults(&scope, runtime->newDict());
74 Str name0(&scope, runtime->newStrFromCStr("name0"));
75 Object none(&scope, NoneType::object());
76 dictAtPutByStr(thread, kw_defaults, name0, none);
77 func.setKwDefaults(*kw_defaults);
78 Object num(&scope, runtime->newInt(-9));
79 Tuple defaults(&scope, runtime->newTupleWith1(num));
80 func.setDefaults(*defaults);
81 func.setIntrinsic(reinterpret_cast<void*>(0x12340));
82 func.setModuleName(runtime->newStrFromCStr("barmodule"));
83 func.setName(runtime->newStrFromCStr("baz"));
84 Dict attrs(&scope, runtime->newDict());
85 Str attr_name(&scope, runtime->newStrFromCStr("funcattr0"));
86 Object attr_value(&scope, runtime->newInt(4));
87 dictAtPutByStr(thread, attrs, attr_name, attr_value);
88 func.setDict(*attrs);
89 return *func;
90}
91
92TEST_F(DebuggingTests, DumpExtendedCode) {
93 HandleScope scope(thread_);
94 Object code(&scope, makeTestCode(thread_));
95
96 std::stringstream ss;
97 dumpExtended(ss, *code);
98 EXPECT_EQ(ss.str(),
99 R"(code "name0":
100 flags: optimized newlocals varargs varkeyargs nested
101 argcount: 1
102 posonlyargcount: 0
103 kwonlyargcount: 0
104 nlocals: 4
105 stacksize: 1
106 filename: "filename0"
107 consts: ("const0",)
108 names: ("name0",)
109 cellvars: ("cellvar0", "cellvar1", "cellvar2")
110 freevars: ("freevar0",)
111 varnames: ("argument0", "varargs", "varkeyargs", "variable0")
112 0 LOAD_CONST 0
113 2 LOAD_ATTR 0
114 4 RETURN_VALUE 0
115)");
116}
117
118TEST_F(DebuggingTests, DumpExtendedFunction) {
119 Thread* thread = Thread::current();
120 HandleScope scope(thread);
121 Object func(&scope, makeTestFunction(thread));
122 std::stringstream ss;
123 dumpExtended(ss, *func);
124 EXPECT_EQ(ss.str(), R"(function "baz":
125 qualname: "footype.baz"
126 module: "barmodule"
127 annotations: {"return": <type "int">}
128 closure: ()
129 defaults: (-9,)
130 kwdefaults: {"name0": None}
131 intrinsic: 0x12340
132 dict: {"funcattr0": 4}
133 flags: optimized newlocals varargs varkeyargs nested interpreted
134 code: code "name0":
135 flags: optimized newlocals varargs varkeyargs nested
136 argcount: 1
137 posonlyargcount: 0
138 kwonlyargcount: 0
139 nlocals: 4
140 stacksize: 1
141 filename: "filename0"
142 consts: ("const0",)
143 names: ("name0",)
144 cellvars: ("cellvar0", "cellvar1", "cellvar2")
145 freevars: ("freevar0",)
146 varnames: ("argument0", "varargs", "varkeyargs", "variable0")
147 0 LOAD_CONST 0
148 2 LOAD_ATTR 0
149 4 RETURN_VALUE 0
150 Rewritten bytecode:
151 0 [ 0] LOAD_CONST 0
152 4 [ 1] LOAD_ATTR_ANAMORPHIC 0
153 8 [ 0] RETURN_VALUE 0
154)");
155}
156
157TEST_F(DebuggingTests, DumpExtendedInstance) {
158 HandleScope scope(thread_);
159 ASSERT_FALSE(runFromCStr(runtime_, R"(
160class C:
161 def __init__(self):
162 self.foo = 5
163 self.bar = "hello"
164i = C()
165i.baz = ()
166)")
167 .isError());
168 Object i(&scope, mainModuleAt(runtime_, "i"));
169 ASSERT_TRUE(i.isInstance());
170 std::stringstream ss;
171 dumpExtended(ss, *i);
172 std::stringstream expected;
173 expected << "heap object with layout " << static_cast<word>(i.layoutId())
174 << R"( (<type "C">):
175 (in-object) "foo" = 5
176 (in-object) "bar" = "hello"
177 (overflow) "baz" = ()
178)";
179 EXPECT_EQ(ss.str(), expected.str());
180}
181
182TEST_F(DebuggingTests, DumpExtendedInstanceWithOverflowDict) {
183 HandleScope scope(thread_);
184 Function func(&scope, makeTestFunction(thread_));
185 std::stringstream ss;
186 dumpExtendedInstance(ss, RawInstance::cast(*func));
187 std::stringstream expected;
188 word raw_flags =
189 SmallInt::cast(func.instanceVariableAt(RawFunction::kFlagsOffset))
190 .value();
191 word entry_asm =
192 SmallInt::cast(func.instanceVariableAt(RawFunction::kEntryAsmOffset))
193 .value();
194 expected << "heap object with layout " << static_cast<word>(func.layoutId())
195 << R"( (<type "function">):
196 (in-object) "__code__" = <code "name0">
197 (in-object) "_function__flags" = )"
198 << raw_flags << R"(
199 (in-object) "_function__argcount" = 1
200 (in-object) "_function__total_args" = 3
201 (in-object) "_function__total_vars" = 5
202 (in-object) "_function__stack_size" = 2
203 (in-object) "__doc__" = "const0"
204 (in-object) "__name__" = "baz"
205 (in-object) "__qualname__" = "footype.baz"
206 (in-object) "__module__" = "barmodule"
207 (in-object) "__module_object__" = <module "__main__">
208 (in-object) "_function__defaults" = (-9,)
209 (in-object) "_function__annotations" = {"return": <type "int">}
210 (in-object) "_function__kw_defaults" = {"name0": None}
211 (in-object) "_function__closure" = ()
212 (in-object) "_function__entry" = 50
213 (in-object) "_function__entry_kw" = 150
214 (in-object) "_function__entry_ex" = 100
215 (in-object) "_function__entry_asm" = )"
216 << entry_asm << R"(
217 (in-object) "_function__rewritten_bytecode" = b'd\x00\x00\x00\xff\x00\x01\x00S\x00\x00\x00'
218 (in-object) "_function__caches" = mutabletuple(None, None, None, None)
219 (in-object) "_function__dict" = {"funcattr0": 4}
220 (in-object) "_function__intrinsic" = 37280
221 overflow dict: {"funcattr0": 4}
222)";
223 EXPECT_EQ(ss.str(), expected.str());
224}
225
226TEST_F(DebuggingTests, DumpExtendedInstanceWithInvalidLayout) {
227 HandleScope scope(thread_);
228 Instance instance(&scope, runtime_->newList());
229 LayoutId old_id = instance.layoutId();
230 // Temporarily set an invalid layout id...
231 instance.setHeader(
232 instance.header().withLayoutId(static_cast<LayoutId>(9999)));
233 std::stringstream ss;
234 dumpExtendedInstance(ss, *instance);
235 instance.setHeader(instance.header().withLayoutId(old_id));
236 EXPECT_EQ(ss.str(), "heap object with layout 9999\n");
237}
238
239TEST_F(DebuggingTests, DumpExtendedInstanceWithLayoutWithoutType) {
240 HandleScope scope(thread_);
241 Instance instance(&scope, runtime_->newList());
242 Layout layout(&scope, runtime_->layoutOf(*instance));
243 Object old_type(&scope, layout.describedType());
244 // Temporarily set an invalid type...
245 layout.setDescribedType(NoneType::object());
246 std::stringstream ss;
247 dumpExtendedInstance(ss, *instance);
248 layout.setDescribedType(*old_type);
249 std::stringstream expected;
250 expected << "heap object with layout " << static_cast<word>(LayoutId::kList)
251 << '\n';
252 EXPECT_EQ(ss.str(), expected.str());
253}
254
255TEST_F(DebuggingTests, DumpExtendedLayout) {
256 HandleScope scope(thread_);
257 // Create a new layout with several overflow attributes
258 Object attr(&scope, runtime_->newStrFromCStr("myattr"));
259 Object attr2(&scope, runtime_->newStrFromCStr("myattr2"));
260 Object attr3(&scope, runtime_->newStrFromCStr("myattr3"));
261 MutableTuple overflow(&scope, runtime_->newMutableTuple(3));
262 Object* overflow_names[] = {&attr, &attr2, &attr3};
263 for (word i = 0; i < overflow.length(); i++) {
264 Object info(&scope, AttributeInfo(i, 0).asSmallInt());
265 overflow.atPut(i, runtime_->newTupleWith2(*overflow_names[i], info));
266 }
267 Layout layout(&scope, layoutCreateEmpty(thread_));
268 layout.setOverflowAttributes(overflow.becomeImmutable());
269
270 // Set some in-object attributes
271 Object inobj1(&scope, runtime_->newStrFromCStr("foo"));
272 Object inobj2(&scope, runtime_->newStrFromCStr("bar"));
273 MutableTuple inobj(&scope, runtime_->newMutableTuple(2));
274 Object* inobj_names[] = {&inobj1, &inobj2};
275 for (word i = 0; i < inobj.length(); i++) {
276 Object info(&scope, AttributeInfo(i, 0).asSmallInt());
277 inobj.atPut(i, runtime_->newTupleWith2(*inobj_names[i], info));
278 }
279 layout.setInObjectAttributes(inobj.becomeImmutable());
280 layout.setNumInObjectAttributes(9);
281 layout.setId(static_cast<LayoutId>(103));
282
283 Type type(&scope, runtime_->typeAt(LayoutId::kObject));
284 layout.setDescribedType(*type);
285
286 std::stringstream ss;
287 dumpExtended(ss, *layout);
288 EXPECT_EQ(ss.str(), R"(layout 103:
289 described type: <type "object">
290 num in-object attributes: 9
291 "foo" @ 0
292 "bar" @ 1
293 overflow tuple:
294 "myattr" @ 0
295 "myattr2" @ 1
296 "myattr3" @ 2
297)");
298}
299
300TEST_F(DebuggingTests, DumpExtendedLayoutWithSealedLayout) {
301 HandleScope scope(thread_);
302 Layout layout(&scope, layoutCreateEmpty(thread_));
303 layout.setOverflowAttributes(NoneType::object());
304 // Set some in-object attributes
305 Object inobj1(&scope, runtime_->newStrFromCStr("foo"));
306 Object inobj2(&scope, runtime_->newStrFromCStr("bar"));
307 MutableTuple inobj(&scope, runtime_->newMutableTuple(2));
308 Object* inobj_names[] = {&inobj1, &inobj2};
309 for (word i = 0; i < inobj.length(); i++) {
310 Object info(&scope, AttributeInfo(i, 0).asSmallInt());
311 inobj.atPut(i, runtime_->newTupleWith2(*inobj_names[i], info));
312 }
313 layout.setInObjectAttributes(*inobj);
314 layout.setId(static_cast<LayoutId>(13));
315 layout.setNumInObjectAttributes(2);
316
317 std::stringstream ss;
318 dumpExtended(ss, *layout);
319 EXPECT_EQ(ss.str(), R"(layout 13:
320 described type: None
321 num in-object attributes: 2
322 "foo" @ 0
323 "bar" @ 1
324 sealed
325)");
326}
327
328TEST_F(DebuggingTests, DumpExtendedLayoutWithDictOverflow) {
329 HandleScope scope(thread_);
330 Layout layout(&scope, layoutCreateEmpty(thread_));
331 layout.setOverflowAttributes(SmallInt::fromWord(654321));
332 layout.setInObjectAttributes(runtime_->emptyTuple());
333 layout.setNumInObjectAttributes(0);
334 layout.setId(static_cast<LayoutId>(1234));
335
336 std::stringstream ss;
337 dumpExtended(ss, *layout);
338 EXPECT_EQ(ss.str(), R"(layout 1234:
339 described type: None
340 num in-object attributes: 0
341 overflow dict @ 654321
342)");
343}
344
345TEST_F(DebuggingTests, DumpExtendedType) {
346 HandleScope scope(thread_);
347 ASSERT_FALSE(runFromCStr(runtime_, R"(
348class A:
349 pass
350class B(bytes):
351 pass
352class C(A, B):
353 def __init__(self):
354 self.x = 0
355 self.y = 1
356)")
357 .isError());
358 Object c(&scope, mainModuleAt(runtime_, "C"));
359 ASSERT_TRUE(c.isType());
360
361 std::stringstream ss;
362 dumpExtended(ss, *c);
363 std::stringstream expected;
364 expected << R"(type "C":
365 bases: (<type "A">, <type "B">)
366 mro: (<type "C">, <type "A">, <type "B">, <type "bytes">, <type "object">)
367 flags:
368 builtin base: <layout )"
369 << static_cast<word>(LayoutId::kBytes) << R"( ("bytes")>
370 layout )"
371 << static_cast<word>(
372 Layout::cast(Type::cast(*c).instanceLayout()).id())
373 << R"(:
374 described type: <type "C">
375 num in-object attributes: 3
376 "_UserBytes__value" @ 0
377 overflow tuple:
378)";
379 EXPECT_EQ(ss.str(), expected.str());
380}
381
382TEST_F(DebuggingTests, DumpExtendedTypePrintsFlags) {
383 HandleScope scope(thread_);
384 Type type(&scope, runtime_->newType());
385 word flags = Type::Flag::kIsAbstract | Type::Flag::kHasCustomDict |
386 Type::Flag::kHasNativeData | Type::Flag::kHasCycleGC |
387 Type::Flag::kHasDefaultDealloc | Type::Flag::kHasSlots |
388 Type::Flag::kIsFixedAttributeBase;
389 type.setFlagsAndBuiltinBase(static_cast<Type::Flag>(flags),
390 LayoutId::kUserWarning);
391
392 std::stringstream ss;
393 dumpExtended(ss, *type);
394 word builtin_base = static_cast<word>(LayoutId::kUserWarning);
395 std::stringstream expected;
396 expected << R"(type None:
397 bases: None
398 mro: None
399 flags: abstract has_custom_dict has_native_data has_cycle_gc has_default_dealloc has_slots is_fixed_attribute_base
400 builtin base: <layout )"
401 << builtin_base << R"( ("UserWarning")>
402 layout: None
403)";
404 EXPECT_EQ(ss.str(), expected.str());
405}
406
407TEST_F(DebuggingTests,
408 DumpExtendedPrefersSimpleDumperOverDumpExtendedInstance) {
409 HandleScope scope(thread_);
410 List list(&scope, runtime_->newList());
411 std::stringstream ss;
412 dumpExtended(ss, *list);
413 EXPECT_EQ(ss.str(), "[]\n");
414}
415
416TEST_F(DebuggingTests, FormatBool) {
417 std::stringstream ss;
418 ss << Bool::trueObj() << ';' << Bool::falseObj();
419 EXPECT_EQ(ss.str(), "True;False");
420}
421
422TEST_F(DebuggingTests, FormatBoundMethod) {
423 HandleScope scope(thread_);
424 ASSERT_FALSE(runFromCStr(runtime_, R"(
425class C:
426 def foo():
427 pass
428bound_method = C().foo
429)")
430 .isError());
431 Object bound_method(&scope, mainModuleAt(runtime_, "bound_method"));
432 ASSERT_TRUE(bound_method.isBoundMethod());
433 std::stringstream ss;
434 ss << bound_method;
435 EXPECT_EQ(ss.str(), "<bound_method <function \"C.foo\">, <\"C\" object>>");
436}
437
438TEST_F(DebuggingTests, FormatBoundMethodWithCallable) {
439 HandleScope scope(thread_);
440 ASSERT_FALSE(runFromCStr(runtime_, R"(
441class C:
442 def __call__(self):
443 pass
444from types import MethodType
445bound_method = MethodType(C(), 42)
446)")
447 .isError());
448 Object bound_method(&scope, mainModuleAt(runtime_, "bound_method"));
449 ASSERT_TRUE(bound_method.isBoundMethod());
450 std::stringstream ss;
451 ss << bound_method;
452 EXPECT_EQ(ss.str(), "<bound_method <\"C\" object>, 42>");
453}
454
455TEST_F(DebuggingTests, FormatBytes) {
456 std::stringstream ss;
457 const byte bytes[] = {'h', 'e', 'l', 'l', 'o', 0, 'w', '2',
458 0xa4, '"', '\'', '\t', '\r', '\n', '\\'};
459 ss << runtime_->newBytesWithAll(bytes);
460 EXPECT_EQ(ss.str(), R"(b'hello\x00w2\xa4"\'\t\r\n\\')");
461}
462
463TEST_F(DebuggingTests, FormatBytearray) {
464 ASSERT_FALSE(runFromCStr(runtime_, "ba = bytearray(b\"foo'\")").isError());
465 HandleScope scope(thread_);
466 Object bytearray(&scope, mainModuleAt(runtime_, "ba"));
467 ASSERT_TRUE(bytearray.isBytearray());
468 std::stringstream ss;
469 ss << bytearray;
470 EXPECT_EQ(ss.str(), R"(bytearray(b'foo\''))");
471}
472
473TEST_F(DebuggingTests, FormatCode) {
474 HandleScope scope(thread_);
475 Code code(&scope, newCodeWithBytes(View<byte>(nullptr, 0)));
476 code.setName(runtime_->newStrFromCStr("foobar"));
477 std::stringstream ss;
478 ss << code;
479 EXPECT_EQ(ss.str(), "<code \"foobar\">");
480}
481
482TEST_F(DebuggingTests, FormatDict) {
483 HandleScope scope(thread_);
484 Dict dict(&scope, runtime_->newDict());
485 Str key0(&scope, runtime_->newStrFromCStr("hello"));
486 Object key1(&scope, NoneType::object());
487 Object hash_obj(&scope, Interpreter::hash(thread_, key1));
488 ASSERT_FALSE(hash_obj.isErrorException());
489 word hash = SmallInt::cast(*hash_obj).value();
490 Object value0(&scope, runtime_->newInt(88));
491 Object value1(&scope, runtime_->emptyTuple());
492 dictAtPutByStr(thread_, dict, key0, value0);
493 ASSERT_TRUE(dictAtPut(thread_, dict, key1, hash, value1).isNoneType());
494 std::stringstream ss;
495 ss << dict;
496 EXPECT_TRUE(ss.str() == R"({"hello": 88, None: ()})" ||
497 ss.str() == R"({None: (), "hello": 88}")");
498}
499
500TEST_F(DebuggingTests, FormatError) {
501 std::stringstream ss;
502 ss << Error::error();
503 EXPECT_EQ(ss.str(), "Error");
504
505 ss.str("");
506 ss << Error::exception();
507 EXPECT_EQ(ss.str(), "Error<Exception>");
508
509 ss.str("");
510 ss << Error::notFound();
511 EXPECT_EQ(ss.str(), "Error<NotFound>");
512
513 ss.str("");
514 ss << Error::noMoreItems();
515 EXPECT_EQ(ss.str(), "Error<NoMoreItems>");
516
517 ss.str("");
518 ss << Error::outOfMemory();
519 EXPECT_EQ(ss.str(), "Error<OutOfMemory>");
520
521 ss.str("");
522 ss << Error::outOfBounds();
523 EXPECT_EQ(ss.str(), "Error<OutOfBounds>");
524}
525
526TEST_F(DebuggingTests, FormatFloat) {
527 std::stringstream ss;
528 ss << runtime_->newFloat(42.42);
529 EXPECT_EQ(ss.str(), "0x1.535c28f5c28f6p+5");
530}
531
532TEST_F(DebuggingTests, FormatFunction) {
533 HandleScope scope(thread_);
534 std::stringstream ss;
535 Object function(&scope, moduleAtByCStr(runtime_, "builtins", "callable"));
536 ASSERT_TRUE(function.isFunction());
537 ss << function;
538 EXPECT_EQ(ss.str(), R"(<function "callable">)");
539}
540
541TEST_F(DebuggingTests, FormatLargeInt) {
542 std::stringstream ss;
543 const uword digits[] = {0x12345, kMaxUword};
544 ss << runtime_->newLargeIntWithDigits(digits);
545 EXPECT_EQ(ss.str(), "largeint([0x0000000000012345, 0xffffffffffffffff])");
546}
547
548TEST_F(DebuggingTests, FormatLargeStr) {
549 HandleScope scope(thread_);
550 std::stringstream ss;
551 Object str(&scope, runtime_->newStrFromCStr("hello world"));
552 EXPECT_TRUE(str.isLargeStr());
553 ss << str;
554 EXPECT_EQ(ss.str(), "\"hello world\"");
555}
556
557TEST_F(DebuggingTests, FormatLayout) {
558 HandleScope scope(thread_);
559 Layout layout(&scope, layoutCreateEmpty(thread_));
560 layout.setId(static_cast<LayoutId>(101));
561 Type type(&scope, runtime_->typeAt(LayoutId::kFloat));
562 layout.setDescribedType(*type);
563
564 std::stringstream ss;
565 ss << layout;
566 EXPECT_EQ(ss.str(), "<layout 101 (\"float\")>");
567}
568
569TEST_F(DebuggingTests, FormatList) {
570 HandleScope scope(thread_);
571 List list(&scope, runtime_->newList());
572 Object o0(&scope, NoneType::object());
573 Object o1(&scope, runtime_->newInt(17));
574 runtime_->listAdd(thread_, list, o0);
575 runtime_->listAdd(thread_, list, o1);
576 std::stringstream ss;
577 ss << list;
578 EXPECT_EQ(ss.str(), "[None, 17]");
579}
580
581TEST_F(DebuggingTests, FormatModule) {
582 HandleScope scope(thread_);
583 Object name(&scope, runtime_->newStrFromCStr("foomodule"));
584 Object module(&scope, runtime_->newModule(name));
585 std::stringstream ss;
586 ss << module;
587 EXPECT_EQ(ss.str(), R"(<module "foomodule">)");
588}
589
590TEST_F(DebuggingTests, FormatNone) {
591 std::stringstream ss;
592 ss << NoneType::object();
593 EXPECT_EQ(ss.str(), "None");
594}
595
596TEST_F(DebuggingTests, FormatObjectWithBuiltinClass) {
597 std::stringstream ss;
598 ss << NotImplementedType::object();
599 EXPECT_EQ(ss.str(), R"(<"NotImplementedType" object>)");
600}
601
602TEST_F(DebuggingTests, FormatObjectWithUserDefinedClass) {
603 HandleScope scope(thread_);
604 ASSERT_FALSE(runFromCStr(runtime_, R"(
605class Foo:
606 pass
607foo = Foo()
608)")
609 .isError());
610 Object foo(&scope, mainModuleAt(runtime_, "foo"));
611 std::stringstream ss;
612 ss << foo;
613 EXPECT_EQ(ss.str(), R"(<"Foo" object>)");
614}
615
616TEST_F(DebuggingTests, FormatObjectWithTypeWithoutName) {
617 HandleScope scope(thread_);
618 Object obj(&scope, NotImplementedType::object());
619 // Phabricate a nameless type...
620 Type::cast(runtime_->typeOf(*obj)).setName(NoneType::object());
621
622 std::stringstream ss;
623 std::stringstream expected;
624 ss << obj;
625 expected << "<object with LayoutId " << static_cast<word>(obj.layoutId())
626 << ">";
627 EXPECT_EQ(ss.str(), expected.str());
628}
629
630TEST_F(DebuggingTests, FormatObjectWithInvalidLayoutId) {
631 HandleScope scope(thread_);
632 Object object(&scope, runtime_->newList());
633 LayoutId old_id = object.layoutId();
634 // Temporary set an invalid layout id.
635 HeapObject::cast(*object).setHeader(
636 HeapObject::cast(*object).header().withLayoutId(
637 static_cast<LayoutId>(9999)));
638 std::stringstream ss;
639 ss << object;
640 HeapObject::cast(*object).setHeader(
641 HeapObject::cast(*object).header().withLayoutId(old_id));
642 EXPECT_EQ(ss.str(), "<object with LayoutId 9999>");
643}
644
645TEST_F(DebuggingTests, FormatObjectWithLayoutWithInvalidType) {
646 HandleScope scope(thread_);
647 Layout layout(&scope, runtime_->layoutAt(LayoutId::kObject));
648 Object object(&scope, runtime_->newInstance(layout));
649 Object old_type(&scope, layout.describedType());
650 // Temporary set an invalid layout id.
651 layout.setDescribedType(NoneType::object());
652 std::stringstream ss;
653 ss << object;
654 layout.setDescribedType(*old_type);
655
656 std::stringstream expected;
657 expected << "<object with LayoutId " << static_cast<word>(LayoutId::kObject)
658 << '>';
659 EXPECT_EQ(ss.str(), expected.str());
660}
661
662TEST_F(DebuggingTests, FormatSmallInt) {
663 std::stringstream ss;
664 ss << SmallInt::fromWord(-42) << ';'
665 << SmallInt::fromWord(SmallInt::kMinValue) << ';'
666 << SmallInt::fromWord(SmallInt::kMaxValue);
667 std::stringstream expected;
668 expected << "-42;" << SmallInt::kMinValue << ";" << SmallInt::kMaxValue;
669 EXPECT_EQ(ss.str(), expected.str());
670}
671
672TEST_F(DebuggingTests, FormatSmallStr) {
673 HandleScope scope(thread_);
674 std::stringstream ss;
675 Object str(&scope, runtime_->newStrFromCStr("aa"));
676 EXPECT_TRUE(str.isSmallStr());
677 ss << str;
678 EXPECT_EQ(ss.str(), "\"aa\"");
679}
680
681TEST_F(DebuggingTests, FormatMutableTuple) {
682 HandleScope scope(thread_);
683 MutableTuple tuple(&scope, runtime_->newMutableTuple(2));
684 tuple.atPut(0, Bool::trueObj());
685 tuple.atPut(1, runtime_->newStrFromCStr("hey"));
686 std::stringstream ss;
687 ss << tuple;
688 EXPECT_EQ(ss.str(), R"(mutabletuple(True, "hey"))");
689}
690
691TEST_F(DebuggingTests, FormatTuple) {
692 HandleScope scope(thread_);
693 Object true_obj(&scope, Bool::trueObj());
694 Object hey(&scope, runtime_->newStrFromCStr("hey"));
695 Tuple tuple(&scope, runtime_->newTupleWith2(true_obj, hey));
696 std::stringstream ss;
697 ss << tuple;
698 EXPECT_EQ(ss.str(), R"((True, "hey"))");
699}
700
701TEST_F(DebuggingTests, FormatTupleWithoutElements) {
702 std::stringstream ss;
703 ss << runtime_->emptyTuple();
704 EXPECT_EQ(ss.str(), "()");
705}
706
707TEST_F(DebuggingTests, FormatTupleWithOneElement) {
708 HandleScope scope(thread_);
709 Object obj(&scope, runtime_->newInt(77));
710 Tuple tuple(&scope, runtime_->newTupleWith1(obj));
711 std::stringstream ss;
712 ss << tuple;
713 EXPECT_EQ(ss.str(), "(77,)");
714}
715
716TEST_F(DebuggingTests, FormatType) {
717 HandleScope scope(thread_);
718 ASSERT_FALSE(runFromCStr(runtime_, R"(
719class MyClass:
720 pass
721)")
722 .isError());
723 Object my_class(&scope, mainModuleAt(runtime_, "MyClass"));
724 std::stringstream ss;
725 ss << my_class;
726 EXPECT_EQ(ss.str(), "<type \"MyClass\">");
727}
728
729TEST_F(DebuggingTests, FormatForwardedObjects) {
730 HandleScope scope(thread_);
731 List list1(&scope, runtime_->newList());
732 Int i(&scope, runtime_->newInt(1234));
733 runtime_->listAdd(thread_, list1, i);
734 Tuple tuple(&scope, runtime_->newTupleWith1(list1));
735
736 i = runtime_->newInt(5678);
737 List list2(&scope, runtime_->newList());
738 runtime_->listAdd(thread_, list2, i);
739 list1.forwardTo(*list2);
740 std::ostringstream ss;
741 ss << tuple;
742 EXPECT_EQ(ss.str(), "(<Forward to> [5678],)");
743
744 ss.str("");
745 dumpExtended(ss, *tuple);
746 EXPECT_EQ(ss.str(), "(<Forward to> [5678],)\n");
747}
748
749TEST_F(DebuggingTests, FormatFrame) {
750 HandleScope scope(thread_);
751 ASSERT_FALSE(runFromCStr(runtime_, R"(
752def func(arg0, arg1):
753 hello = "world"
754 return arg0 + arg1
755)")
756 .isError());
757 Function func(&scope, mainModuleAt(runtime_, "func"));
758
759 Object empty_tuple(&scope, runtime_->emptyTuple());
760 Str name(&scope, runtime_->newStrFromCStr("_bytearray_check"));
761 Code code(&scope,
762 runtime_->newBuiltinCode(/*argcount=*/0, /*posonlyargcount=*/0,
763 /*kwonlyargcount=*/0,
764 /*flags=*/0, /*function=*/nullptr,
765 /*parameter_names=*/empty_tuple, name));
766 Str qualname(&scope, runtime_->newStrFromCStr("test._bytearray_check"));
767 Module module(&scope, findMainModule(runtime_));
768 Function builtin(
769 &scope, runtime_->newFunctionWithCode(thread_, qualname, code, module));
770
771 Frame* root = thread_->currentFrame();
772 ASSERT_TRUE(root->isSentinel());
773 root->setVirtualPC(8 * kCodeUnitScale);
774 thread_->stackPush(NoneType::object());
775 thread_->stackPush(*builtin);
776 thread_->pushNativeFrame(0);
777
778 Function function(&scope, makeTestFunction(thread_));
779 thread_->stackPush(*function);
780 thread_->stackPush(runtime_->newStrFromCStr("foo bar"));
781 thread_->stackPush(runtime_->emptyTuple());
782 thread_->stackPush(runtime_->newDict());
783 Frame* frame1 = thread_->pushCallFrame(*function);
784 frame1->setVirtualPC(42 * kCodeUnitScale);
785 frame1->setLocal(3, runtime_->newStrFromCStr("bar foo"));
786 frame1->setLocal(4, runtime_->newInt(88));
787 frame1->setLocal(5, runtime_->newInt(-99)); // cellvar0
788 frame1->setLocal(6, runtime_->newInt(12)); // cellvar1
789 frame1->setLocal(7, runtime_->newInt(34)); // cellvar2
790
791 thread_->stackPush(runtime_->newInt(-8));
792 thread_->stackPush(runtime_->newStrFromCStr("baz bam"));
793 thread_->stackPush(*func);
794 thread_->stackPush(runtime_->newInt(-9));
795 thread_->stackPush(runtime_->newInt(17));
796 Frame* frame2 = thread_->pushCallFrame(*func);
797 frame2->setVirtualPC(4 * kCodeUnitScale);
798 frame2->setLocal(2, runtime_->newStrFromCStr("world"));
799
800 std::stringstream ss;
801 ss << thread_->currentFrame();
802 EXPECT_EQ(ss.str(), R"(- initial frame
803 pc: 16
804 stack:
805 0: None
806- function: <function "test._bytearray_check">
807 code: "_bytearray_check"
808 pc: n/a (native)
809- function: <function "footype.baz">
810 code: "name0"
811 pc: 84 ("filename0":0)
812 locals:
813 0 "argument0": "foo bar"
814 1 "varargs": ()
815 2 "varkeyargs": {}
816 3 "variable0": "bar foo"
817 4 "freevar0": 88
818 5 "cellvar0": -99
819 6 "cellvar1": 12
820 7 "cellvar2": 34
821 stack:
822 1: -8
823 0: "baz bam"
824- function: <function "func">
825 code: "func"
826 pc: 8 ("<test string>":4)
827 locals:
828 0 "arg0": -9
829 1 "arg1": 17
830 2 "hello": "world"
831)");
832}
833
834TEST_F(DebuggingTests, FormatFrameNullptr) {
835 std::stringstream ss;
836 ss << static_cast<Frame*>(nullptr);
837 EXPECT_EQ(ss.str(), "<nullptr>");
838}
839
840TEST_F(DebuggingTests, FormatValueCellWithValue) {
841 HandleScope scope(thread_);
842 Object value(&scope, runtime_->newInt(42));
843 Object value_cell(&scope, runtime_->newValueCell());
844 ValueCell::cast(*value_cell).setValue(*value);
845 std::stringstream ss;
846 ss << value_cell;
847 EXPECT_EQ(ss.str(), "<value_cell (42)>");
848}
849
850TEST_F(DebuggingTests, FormatValueCellPlaceHolder) {
851 HandleScope scope(thread_);
852 Object value_cell(&scope, runtime_->newValueCell());
853 ValueCell::cast(*value_cell).makePlaceholder();
854 std::stringstream ss;
855 ss << value_cell;
856 EXPECT_EQ(ss.str(), "<value_cell placeholder>");
857}
858
859TEST_F(DebuggingTests, FormatWeakLink) {
860 HandleScope scope(thread_);
861 Object referent(&scope, NoneType::object());
862 Object prev(&scope, SmallInt::fromWord(1));
863 Object next(&scope, SmallInt::fromWord(2));
864 Object link(&scope, runtime_->newWeakLink(thread_, referent, prev, next));
865 std::stringstream ss;
866 ss << link;
867 std::stringstream expected;
868 expected << "<_weaklink 0x" << std::hex << link.raw() << std::dec
869 << " referent=None, next=0x4, prev=0x2>";
870 EXPECT_EQ(ss.str(), expected.str());
871}
872
873TEST_F(DebuggingTests, FormatThreadDumpsPendingException) {
874 thread_->raiseWithFmt(LayoutId::kValueError, "foo");
875 std::stringstream ss;
876 ss << thread_;
877 EXPECT_EQ(ss.str(), R"(pending exception type: <type "ValueError">
878pending exception value: "foo"
879pending exception traceback: None
880)");
881}
882
883} // namespace testing
884} // namespace py