Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
Pyston
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
Pyston
Commits
51b685f5
Commit
51b685f5
authored
9 years ago
by
Kevin Modzelewski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Basic refcounting in the interpreter
parent
e3dcb351
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
93 additions
and
37 deletions
+93
-37
src/codegen/ast_interpreter.cpp
src/codegen/ast_interpreter.cpp
+56
-13
src/codegen/irgen/hooks.cpp
src/codegen/irgen/hooks.cpp
+4
-2
src/core/stringpool.cpp
src/core/stringpool.cpp
+2
-1
src/runtime/objmodel.cpp
src/runtime/objmodel.cpp
+7
-3
src/runtime/types.cpp
src/runtime/types.cpp
+14
-11
src/runtime/types.h
src/runtime/types.h
+10
-7
No files found.
src/codegen/ast_interpreter.cpp
View file @
51b685f5
...
...
@@ -53,6 +53,8 @@
namespace
pyston
{
static
int
calculateNumVRegs
(
FunctionMetadata
*
md
);
namespace
{
class
ASTInterpreter
;
...
...
@@ -76,8 +78,8 @@ public:
private:
Value
createFunction
(
AST
*
node
,
AST_arguments
*
args
,
const
std
::
vector
<
AST_stmt
*>&
body
);
Value
doBinOp
(
AST_expr
*
node
,
Value
left
,
Value
right
,
int
op
,
BinExpType
exp_type
);
void
doStore
(
AST_expr
*
node
,
Value
value
);
void
doStore
(
AST_Name
*
name
,
Value
value
);
void
doStore
(
AST_expr
*
node
,
STOLEN
(
Value
)
value
);
void
doStore
(
AST_Name
*
name
,
STOLEN
(
Value
)
value
);
Box
*
doOSR
(
AST_Jump
*
node
);
Value
getNone
();
...
...
@@ -157,6 +159,18 @@ private:
bool
should_jit
;
public:
~
ASTInterpreter
()
{
Py_XDECREF
(
frame_info
.
boxedLocals
);
int
nvregs
=
calculateNumVRegs
(
md
);
for
(
int
i
=
0
;
i
<
nvregs
;
i
++
)
{
Py_XDECREF
(
vregs
[
i
]);
}
Py_DECREF
(
this
->
globals
);
}
llvm
::
DenseMap
<
InternedString
,
int
>&
getSymVRegMap
()
{
assert
(
source_info
->
cfg
);
return
source_info
->
cfg
->
sym_vreg_map
;
...
...
@@ -224,6 +238,7 @@ void ASTInterpreter::setFrameInfo(const FrameInfo* frame_info) {
void
ASTInterpreter
::
setGlobals
(
Box
*
globals
)
{
this
->
globals
=
globals
;
Py_INCREF
(
globals
);
}
ASTInterpreter
::
ASTInterpreter
(
FunctionMetadata
*
md
,
Box
**
vregs
)
...
...
@@ -285,6 +300,8 @@ void ASTInterpreter::startJITing(CFGBlock* block, int exit_offset) {
assert
(
ENABLE_BASELINEJIT
);
assert
(
!
jit
);
assert
(
0
&&
"refcounting not set up"
);
auto
&
code_blocks
=
md
->
code_blocks
;
JitCodeBlock
*
code_block
=
NULL
;
if
(
!
code_blocks
.
empty
())
...
...
@@ -340,7 +357,7 @@ Box* ASTInterpreter::execJITedBlock(CFGBlock* b) {
}
Box
*
ASTInterpreter
::
executeInner
(
ASTInterpreter
&
interpreter
,
CFGBlock
*
start_block
,
AST_stmt
*
start_at
)
{
Value
v
;
Value
v
(
nullptr
,
nullptr
)
;
bool
from_start
=
start_block
==
NULL
&&
start_at
==
NULL
;
...
...
@@ -366,6 +383,7 @@ Box* ASTInterpreter::executeInner(ASTInterpreter& interpreter, CFGBlock* start_b
}
interpreter
.
current_inst
=
s
;
Py_XDECREF
(
v
.
o
);
v
=
interpreter
.
visit_stmt
(
s
);
}
}
else
{
...
...
@@ -398,6 +416,7 @@ Box* ASTInterpreter::executeInner(ASTInterpreter& interpreter, CFGBlock* start_b
interpreter
.
current_inst
=
s
;
if
(
interpreter
.
jit
)
interpreter
.
jit
->
emitSetCurrentInst
(
s
);
Py_XDECREF
(
v
.
o
);
v
=
interpreter
.
visit_stmt
(
s
);
}
}
...
...
@@ -434,17 +453,19 @@ Value ASTInterpreter::doBinOp(AST_expr* node, Value left, Value right, int op, B
return
Value
();
}
void
ASTInterpreter
::
doStore
(
AST_Name
*
node
,
Value
value
)
{
void
ASTInterpreter
::
doStore
(
AST_Name
*
node
,
STOLEN
(
Value
)
value
)
{
if
(
node
->
lookup_type
==
ScopeInfo
::
VarScopeType
::
UNKNOWN
)
node
->
lookup_type
=
scope_info
->
getScopeTypeOfName
(
node
->
id
);
InternedString
name
=
node
->
id
;
ScopeInfo
::
VarScopeType
vst
=
node
->
lookup_type
;
if
(
vst
==
ScopeInfo
::
VarScopeType
::
GLOBAL
)
{
assert
(
0
&&
"check refcounting"
);
if
(
jit
)
jit
->
emitSetGlobal
(
globals
,
name
.
getBox
(),
value
);
setGlobal
(
globals
,
name
.
getBox
(),
value
.
o
);
}
else
if
(
vst
==
ScopeInfo
::
VarScopeType
::
NAME
)
{
assert
(
0
&&
"check refcounting"
);
if
(
jit
)
jit
->
emitSetItemName
(
name
.
getBox
(),
value
);
assert
(
frame_info
.
boxedLocals
!=
NULL
);
...
...
@@ -453,6 +474,7 @@ void ASTInterpreter::doStore(AST_Name* node, Value value) {
}
else
{
bool
closure
=
vst
==
ScopeInfo
::
VarScopeType
::
CLOSURE
;
if
(
jit
)
{
assert
(
0
&&
"check refcounting"
);
if
(
!
closure
)
{
bool
is_live
=
source_info
->
getLiveness
()
->
isLiveAtEnd
(
name
,
current_block
);
if
(
is_live
)
...
...
@@ -465,9 +487,12 @@ void ASTInterpreter::doStore(AST_Name* node, Value value) {
assert
(
getSymVRegMap
().
count
(
name
));
assert
(
getSymVRegMap
()[
name
]
==
node
->
vreg
);
Box
*
prev
=
vregs
[
node
->
vreg
];
vregs
[
node
->
vreg
]
=
value
.
o
;
Py_XDECREF
(
prev
);
if
(
closure
)
{
assert
(
0
&&
"check refcounting"
);
created_closure
->
elts
[
scope_info
->
getClosureOffset
(
name
)]
=
value
.
o
;
}
}
...
...
@@ -524,7 +549,7 @@ void ASTInterpreter::doStore(AST_expr* node, Value value) {
}
Value
ASTInterpreter
::
getNone
()
{
return
Value
(
None
,
jit
?
jit
->
imm
(
None
)
:
NULL
);
return
Value
(
incref
(
None
)
,
jit
?
jit
->
imm
(
None
)
:
NULL
);
}
Value
ASTInterpreter
::
visit_unaryop
(
AST_UnaryOp
*
node
)
{
...
...
@@ -538,7 +563,10 @@ Value ASTInterpreter::visit_unaryop(AST_UnaryOp* node) {
Value
ASTInterpreter
::
visit_binop
(
AST_BinOp
*
node
)
{
Value
left
=
visit_expr
(
node
->
left
);
Value
right
=
visit_expr
(
node
->
right
);
return
doBinOp
(
node
,
left
,
right
,
node
->
op_type
,
BinExpType
::
BinOp
);
Value
r
=
doBinOp
(
node
,
left
,
right
,
node
->
op_type
,
BinExpType
::
BinOp
);
Py_DECREF
(
left
.
o
);
Py_DECREF
(
right
.
o
);
return
r
;
}
Value
ASTInterpreter
::
visit_slice
(
AST_slice
*
node
)
{
...
...
@@ -599,6 +627,8 @@ Value ASTInterpreter::visit_branch(AST_Branch* node) {
next_block
=
node
->
iftrue
;
else
next_block
=
node
->
iffalse
;
// TODO could potentially avoid doing this if we skip the incref in NONZERO
Py_DECREF
(
v
.
o
);
if
(
jit
)
{
jit
->
emitJump
(
next_block
);
...
...
@@ -812,7 +842,10 @@ Value ASTInterpreter::visit_augBinOp(AST_AugBinOp* node) {
Value
left
=
visit_expr
(
node
->
left
);
Value
right
=
visit_expr
(
node
->
right
);
return
doBinOp
(
node
,
left
,
right
,
node
->
op_type
,
BinExpType
::
AugBinOp
);
Value
r
=
doBinOp
(
node
,
left
,
right
,
node
->
op_type
,
BinExpType
::
AugBinOp
);
Py_DECREF
(
left
.
o
);
Py_DECREF
(
right
.
o
);
return
r
;
}
Value
ASTInterpreter
::
visit_langPrimitive
(
AST_LangPrimitive
*
node
)
{
...
...
@@ -879,6 +912,7 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
assert
(
node
->
args
.
size
()
==
1
);
Value
obj
=
visit_expr
(
node
->
args
[
0
]);
v
=
Value
(
boxBool
(
nonzero
(
obj
.
o
)),
jit
?
jit
->
emitNonzero
(
obj
)
:
NULL
);
Py_DECREF
(
obj
.
o
);
}
else
if
(
node
->
opcode
==
AST_LangPrimitive
::
SET_EXC_INFO
)
{
assert
(
node
->
args
.
size
()
==
3
);
...
...
@@ -1235,8 +1269,7 @@ Value ASTInterpreter::visit_assign(AST_Assign* node) {
assert
(
node
->
targets
.
size
()
==
1
&&
"cfg should have lowered it to a single target"
);
Value
v
=
visit_expr
(
node
->
value
);
for
(
AST_expr
*
e
:
node
->
targets
)
doStore
(
e
,
v
);
doStore
(
node
->
targets
[
0
],
v
);
return
Value
();
}
...
...
@@ -1249,9 +1282,9 @@ Value ASTInterpreter::visit_print(AST_Print* node) {
jit
->
emitPrint
(
dest
,
var
,
node
->
nl
);
if
(
node
->
dest
)
printHelper
(
dest
.
o
,
var
.
o
,
node
->
nl
);
printHelper
(
autoDecref
(
dest
.
o
),
autoXDecref
(
var
.
o
)
,
node
->
nl
);
else
printHelper
(
getSysStdout
(),
var
.
o
,
node
->
nl
);
printHelper
(
getSysStdout
(),
autoXDecref
(
var
.
o
)
,
node
->
nl
);
return
Value
();
}
...
...
@@ -1273,7 +1306,10 @@ Value ASTInterpreter::visit_compare(AST_Compare* node) {
RELEASE_ASSERT
(
node
->
comparators
.
size
()
==
1
,
"not implemented"
);
Value
left
=
visit_expr
(
node
->
left
);
Value
right
=
visit_expr
(
node
->
comparators
[
0
]);
return
doBinOp
(
node
,
left
,
right
,
node
->
ops
[
0
],
BinExpType
::
Compare
);
Value
r
=
doBinOp
(
node
,
left
,
right
,
node
->
ops
[
0
],
BinExpType
::
Compare
);
Py_DECREF
(
left
.
o
);
Py_DECREF
(
right
.
o
);
return
r
;
}
Value
ASTInterpreter
::
visit_expr
(
AST_expr
*
node
)
{
...
...
@@ -1424,6 +1460,7 @@ Value ASTInterpreter::visit_num(AST_Num* node) {
o
=
parent_module
->
getPureImaginaryConstant
(
node
->
n_float
);
}
else
RELEASE_ASSERT
(
0
,
"not implemented"
);
Py_INCREF
(
o
);
return
Value
(
o
,
jit
?
jit
->
imm
(
o
)
:
NULL
);
}
...
...
@@ -1499,10 +1536,12 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
if
(
jit
)
v
.
var
=
jit
->
emitGetGlobal
(
globals
,
node
->
id
.
getBox
());
assert
(
0
&&
"check refcounting"
);
v
.
o
=
getGlobal
(
globals
,
node
->
id
.
getBox
());
return
v
;
}
case
ScopeInfo
:
:
VarScopeType
::
DEREF
:
{
assert
(
0
&&
"check refcounting"
);
return
Value
(
ASTInterpreterJitInterface
::
derefHelper
(
this
,
node
->
id
),
jit
?
jit
->
emitDeref
(
node
->
id
)
:
NULL
);
}
...
...
@@ -1510,6 +1549,7 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
case
ScopeInfo
:
:
VarScopeType
::
CLOSURE
:
{
Value
v
;
if
(
jit
)
{
assert
(
0
&&
"check refcounting"
);
bool
is_live
=
false
;
if
(
node
->
lookup_type
==
ScopeInfo
::
VarScopeType
::
FAST
)
is_live
=
source_info
->
getLiveness
()
->
isLiveAtEnd
(
node
->
id
,
current_block
);
...
...
@@ -1525,6 +1565,7 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
assert
(
getSymVRegMap
()[
node
->
id
]
==
node
->
vreg
);
Box
*
val
=
vregs
[
node
->
vreg
];
if
(
val
)
{
Py_INCREF
(
val
);
v
.
o
=
val
;
return
v
;
}
...
...
@@ -1533,6 +1574,7 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
RELEASE_ASSERT
(
0
,
"should be unreachable"
);
}
case
ScopeInfo
:
:
VarScopeType
::
NAME
:
{
assert
(
0
&&
"check refcounting"
);
Value
v
;
if
(
jit
)
v
.
var
=
jit
->
emitGetBoxedLocal
(
node
->
id
.
getBox
());
...
...
@@ -1566,6 +1608,7 @@ Value ASTInterpreter::visit_list(AST_List* node) {
}
Value
ASTInterpreter
::
visit_tuple
(
AST_Tuple
*
node
)
{
return
getNone
();
llvm
::
SmallVector
<
RewriterVar
*
,
8
>
items
;
BoxedTuple
*
rtn
=
BoxedTuple
::
create
(
node
->
elts
.
size
());
...
...
@@ -1804,7 +1847,7 @@ Box* astInterpretFunction(FunctionMetadata* md, Box* closure, Box* generator, Bo
interpreter
.
initArguments
((
BoxedClosure
*
)
closure
,
(
BoxedGenerator
*
)
generator
,
arg1
,
arg2
,
arg3
,
args
);
Box
*
v
=
ASTInterpreter
::
execute
(
interpreter
);
return
v
?
v
:
None
;
return
v
?
v
:
incref
(
None
)
;
}
Box
*
astInterpretFunctionEval
(
FunctionMetadata
*
md
,
Box
*
globals
,
Box
*
boxedLocals
)
{
...
...
This diff is collapsed.
Click to expand it.
src/codegen/irgen/hooks.cpp
View file @
51b685f5
...
...
@@ -323,10 +323,11 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
FutureFlags
future_flags
=
getFutureFlags
(
m
->
body
,
fn
);
ScopingAnalysis
*
scoping
=
new
ScopingAnalysis
(
m
,
true
);
std
::
unique_ptr
<
SourceInfo
>
si
(
new
SourceInfo
(
bm
,
scoping
,
future_flags
,
m
,
m
->
body
,
boxString
(
fn
)));
auto
fn_str
=
getStaticString
(
fn
);
// XXX this is not a static string
std
::
unique_ptr
<
SourceInfo
>
si
(
new
SourceInfo
(
bm
,
scoping
,
future_flags
,
m
,
m
->
body
,
fn_str
));
static
BoxedString
*
doc_str
=
getStaticString
(
"__doc__"
);
bm
->
setattr
(
doc_str
,
si
->
getDocString
(
),
NULL
);
bm
->
setattr
(
doc_str
,
autoDecref
(
si
->
getDocString
()
),
NULL
);
static
BoxedString
*
builtins_str
=
getStaticString
(
"__builtins__"
);
if
(
!
bm
->
hasattr
(
builtins_str
))
...
...
@@ -338,6 +339,7 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
UNAVOIDABLE_STAT_TIMER
(
t0
,
"us_timer_interpreted_module_toplevel"
);
Box
*
r
=
astInterpretFunction
(
md
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
);
assert
(
r
==
None
);
Py_DECREF
(
r
);
}
Box
*
evalOrExec
(
FunctionMetadata
*
md
,
Box
*
globals
,
Box
*
boxedLocals
)
{
...
...
This diff is collapsed.
Click to expand it.
src/core/stringpool.cpp
View file @
51b685f5
...
...
@@ -20,7 +20,8 @@ namespace pyston {
InternedString
InternedStringPool
::
get
(
llvm
::
StringRef
arg
)
{
// HACK: should properly track this liveness:
BoxedString
*
s
=
internStringImmortal
(
arg
);
// XXX it's not a static string
BoxedString
*
s
=
getStaticString
(
arg
);
#ifndef NDEBUG
return
InternedString
(
s
,
this
);
...
...
This diff is collapsed.
Click to expand it.
src/runtime/objmodel.cpp
View file @
51b685f5
...
...
@@ -164,13 +164,14 @@ extern "C" bool softspace(Box* b, bool newval) {
r
=
0
;
}
else
{
r
=
nonzero
(
gotten
);
Py_DECREF
(
gotten
);
}
}
catch
(
ExcInfo
e
)
{
r
=
0
;
}
try
{
setattr
(
b
,
softspace_str
,
boxInt
(
newval
));
setattr
(
b
,
softspace_str
,
autoDecref
(
boxInt
(
newval
)
));
}
catch
(
ExcInfo
e
)
{
r
=
0
;
}
...
...
@@ -193,11 +194,12 @@ extern "C" void printHelper(Box* dest, Box* var, bool nl) {
callattrInternal
<
CXX
,
NOT_REWRITABLE
>
(
dest
,
write_str
,
CLASS_OR_INST
,
0
,
ArgPassSpec
(
1
),
space_str
,
0
,
0
,
0
,
0
);
Box
*
str_or_unicode_var
=
(
var
->
cls
==
unicode_cls
)
?
var
:
str
(
var
);
Box
*
str_or_unicode_var
=
(
var
->
cls
==
unicode_cls
)
?
incref
(
var
)
:
str
(
var
);
Box
*
write_rtn
=
callattrInternal
<
CXX
,
NOT_REWRITABLE
>
(
dest
,
write_str
,
CLASS_OR_INST
,
0
,
ArgPassSpec
(
1
),
str_or_unicode_var
,
0
,
0
,
0
,
0
);
autoDecref
(
str_or_unicode_var
)
,
0
,
0
,
0
,
0
);
if
(
!
write_rtn
)
raiseAttributeError
(
dest
,
write_str
->
s
());
Py_DECREF
(
write_rtn
);
}
if
(
nl
)
{
...
...
@@ -205,6 +207,8 @@ extern "C" void printHelper(Box* dest, Box* var, bool nl) {
newline_str
,
0
,
0
,
0
,
0
);
if
(
!
write_rtn
)
raiseAttributeError
(
dest
,
write_str
->
s
());
Py_DECREF
(
write_rtn
);
if
(
!
var
)
softspace
(
dest
,
false
);
}
...
...
This diff is collapsed.
Click to expand it.
src/runtime/types.cpp
View file @
51b685f5
...
...
@@ -3427,21 +3427,24 @@ int BoxedModule::traverse(Box* _m, visitproc visit, void* arg) noexcept {
return
0
;
}
template
<
typename
CM
>
void
clearContiguousMap
(
CM
&
cm
)
{
for
(
auto
&&
p
:
cm
)
{
Py_DECREF
(
cm
.
getMapped
(
p
.
second
));
}
cm
.
~
ContiguousMap
();
}
int
BoxedModule
::
clear
(
Box
*
b
)
noexcept
{
BoxedModule
*
self
=
static_cast
<
BoxedModule
*>
(
b
);
self
->
clearAttrs
();
assert
(
!
self
->
str_constants
.
size
());
assert
(
!
self
->
unicode_constants
.
size
());
for
(
auto
p
:
self
->
int_constants
)
{
Py_DECREF
(
self
->
int_constants
.
getMapped
(
p
.
second
));
}
self
->
int_constants
.
~
ContiguousMap
();
assert
(
!
self
->
float_constants
.
size
());
assert
(
!
self
->
imaginary_constants
.
size
());
assert
(
!
self
->
long_constants
.
size
());
clearContiguousMap
(
self
->
str_constants
);
clearContiguousMap
(
self
->
unicode_constants
);
clearContiguousMap
(
self
->
int_constants
);
clearContiguousMap
(
self
->
float_constants
);
clearContiguousMap
(
self
->
imaginary_constants
);
clearContiguousMap
(
self
->
long_constants
);
assert
(
!
self
->
keep_alive
.
size
());
return
0
;
...
...
This diff is collapsed.
Click to expand it.
src/runtime/types.h
View file @
51b685f5
...
...
@@ -70,7 +70,7 @@ void setupSysEnd();
BORROWED
(
BoxedDict
*
)
getSysModulesDict
();
BORROWED
(
BoxedList
*
)
getSysPath
();
extern
"C"
B
ox
*
getSysStdout
();
extern
"C"
B
ORROWED
(
Box
*
)
getSysStdout
();
extern
"C"
BoxedTuple
*
EmptyTuple
;
extern
"C"
BoxedString
*
EmptyString
;
...
...
@@ -389,6 +389,9 @@ public:
template
<
typename
B
,
bool
Nullable
=
false
>
DecrefHandle
<
B
,
Nullable
>
autoDecref
(
B
*
b
)
{
return
DecrefHandle
<
B
,
Nullable
>
(
b
);
}
template
<
typename
B
>
DecrefHandle
<
B
,
true
>
autoXDecref
(
B
*
b
)
{
return
DecrefHandle
<
B
,
true
>
(
b
);
}
template
<
typename
B
>
B
*
incref
(
B
*
b
)
{
Py_INCREF
(
b
);
...
...
@@ -946,12 +949,12 @@ public:
BoxedModule
()
{}
// noop constructor to disable zero-initialization of cls
std
::
string
name
();
B
oxedString
*
getStringConstant
(
llvm
::
StringRef
ast_str
,
bool
intern
=
false
);
B
ox
*
getUnicodeConstant
(
llvm
::
StringRef
ast_str
);
B
oxedInt
*
getIntConstant
(
int64_t
n
);
B
oxedFloat
*
getFloatConstant
(
double
d
);
B
ox
*
getPureImaginaryConstant
(
double
d
);
B
ox
*
getLongConstant
(
llvm
::
StringRef
s
);
B
ORROWED
(
BoxedString
*
)
getStringConstant
(
llvm
::
StringRef
ast_str
,
bool
intern
=
false
);
B
ORROWED
(
Box
*
)
getUnicodeConstant
(
llvm
::
StringRef
ast_str
);
B
ORROWED
(
BoxedInt
*
)
getIntConstant
(
int64_t
n
);
B
ORROWED
(
BoxedFloat
*
)
getFloatConstant
(
double
d
);
B
ORROWED
(
Box
*
)
getPureImaginaryConstant
(
double
d
);
B
ORROWED
(
Box
*
)
getLongConstant
(
llvm
::
StringRef
s
);
static
void
dealloc
(
Box
*
b
)
noexcept
;
static
int
traverse
(
Box
*
self
,
visitproc
visit
,
void
*
arg
)
noexcept
;
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment