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
b594728e
Commit
b594728e
authored
Aug 01, 2014
by
Travis Hance
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support for __get__ and __set__ for descriptors
parent
7be0644b
Changes
27
Show whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
1938 additions
and
338 deletions
+1938
-338
src/asm_writing/rewriter.cpp
src/asm_writing/rewriter.cpp
+10
-7
src/asm_writing/rewriter.h
src/asm_writing/rewriter.h
+3
-2
src/codegen/compvars.cpp
src/codegen/compvars.cpp
+1
-1
src/codegen/patchpoints.cpp
src/codegen/patchpoints.cpp
+7
-7
src/runtime/builtin_modules/builtins.cpp
src/runtime/builtin_modules/builtins.cpp
+3
-3
src/runtime/objmodel.cpp
src/runtime/objmodel.cpp
+616
-312
src/runtime/objmodel.h
src/runtime/objmodel.h
+3
-2
src/runtime/types.cpp
src/runtime/types.cpp
+29
-1
src/runtime/types.h
src/runtime/types.h
+4
-1
test/tests/class_noctor.py
test/tests/class_noctor.py
+1
-0
test/tests/data_descriptors.py
test/tests/data_descriptors.py
+118
-0
test/tests/data_descriptors_calling.py
test/tests/data_descriptors_calling.py
+121
-0
test/tests/data_descriptors_classes.py
test/tests/data_descriptors_classes.py
+33
-0
test/tests/descriptors_callattr_cls_ics.py
test/tests/descriptors_callattr_cls_ics.py
+84
-0
test/tests/descriptors_callattr_ics.py
test/tests/descriptors_callattr_ics.py
+60
-0
test/tests/descriptors_double.py
test/tests/descriptors_double.py
+121
-0
test/tests/descriptors_getattr_ics.py
test/tests/descriptors_getattr_ics.py
+43
-0
test/tests/descriptors_getclsattr_ics.py
test/tests/descriptors_getclsattr_ics.py
+76
-0
test/tests/descriptors_guards.py
test/tests/descriptors_guards.py
+166
-0
test/tests/descriptors_nonfunc_types.py
test/tests/descriptors_nonfunc_types.py
+226
-0
test/tests/descriptors_setattr_ics.py
test/tests/descriptors_setattr_ics.py
+20
-0
test/tests/dunder_descriptors.py
test/tests/dunder_descriptors.py
+0
-1
test/tests/function_instancemethod.py
test/tests/function_instancemethod.py
+119
-0
test/tests/getattr_classmember_ordering.py
test/tests/getattr_classmember_ordering.py
+21
-0
test/tests/instancemethod_guards.py
test/tests/instancemethod_guards.py
+48
-0
test/tests/object_new_arguments.py
test/tests/object_new_arguments.py
+1
-0
tools/tester.py
tools/tester.py
+4
-1
No files found.
src/asm_writing/rewriter.cpp
View file @
b594728e
...
...
@@ -157,7 +157,7 @@ void RewriterVarUsage::addGuardNotEq(uint64_t val) {
assembler
->
je
(
assembler
::
JumpDestination
::
fromStart
(
rewriter
->
rewrite
->
getSlotSize
()));
}
void
RewriterVarUsage
::
addAttrGuard
(
int
offset
,
uint64_t
val
)
{
void
RewriterVarUsage
::
addAttrGuard
(
int
offset
,
uint64_t
val
,
bool
negate
)
{
assertValid
();
Rewriter
*
rewriter
=
var
->
rewriter
;
...
...
@@ -167,13 +167,16 @@ void RewriterVarUsage::addAttrGuard(int offset, uint64_t val) {
assembler
::
Register
this_reg
=
var
->
getInReg
();
if
(
val
<
(
-
1L
<<
31
)
||
val
>=
(
1L
<<
31
)
-
1
)
{
assembler
::
Register
reg
=
rewriter
->
allocReg
(
Location
::
any
());
assembler
::
Register
reg
=
rewriter
->
allocReg
(
Location
::
any
()
,
/* otherThan */
this_reg
);
assert
(
reg
!=
this_reg
);
assembler
->
mov
(
assembler
::
Immediate
(
val
),
reg
);
assembler
->
cmp
(
assembler
::
Indirect
(
this_reg
,
offset
),
reg
);
}
else
{
assembler
->
cmp
(
assembler
::
Indirect
(
this_reg
,
offset
),
assembler
::
Immediate
(
val
));
}
if
(
negate
)
assembler
->
je
(
assembler
::
JumpDestination
::
fromStart
(
rewriter
->
rewrite
->
getSlotSize
()));
else
assembler
->
jne
(
assembler
::
JumpDestination
::
fromStart
(
rewriter
->
rewrite
->
getSlotSize
()));
}
...
...
@@ -772,8 +775,8 @@ RewriterVarUsage Rewriter::allocateAndCopy(RewriterVarUsage array_ptr, int n) {
std
::
pair
<
RewriterVarUsage
,
int
>
allocation
=
_allocate
(
n
);
int
offset
=
allocation
.
second
;
assembler
::
Register
tmp
=
allocReg
(
Location
::
any
());
assembler
::
Register
src_ptr
=
array_ptr
.
var
->
getInReg
();
assembler
::
Register
tmp
=
allocReg
(
Location
::
any
(),
/* otherThan */
src_ptr
);
assert
(
tmp
!=
src_ptr
);
// TODO how to ensure this?
for
(
int
i
=
0
;
i
<
n
;
i
++
)
{
...
...
@@ -872,14 +875,14 @@ void Rewriter::spillRegister(assembler::XMMRegister reg) {
removeLocationFromVar
(
var
,
reg
);
}
assembler
::
Register
Rewriter
::
allocReg
(
Location
dest
)
{
assembler
::
Register
Rewriter
::
allocReg
(
Location
dest
,
Location
otherThan
)
{
if
(
dest
.
type
==
Location
::
AnyReg
)
{
for
(
assembler
::
Register
reg
:
allocatable_regs
)
{
if
(
vars_by_location
.
count
(
reg
)
==
0
)
if
(
Location
(
reg
)
!=
otherThan
&&
vars_by_location
.
count
(
reg
)
==
0
)
return
reg
;
}
// TODO maybe should do some sort of round-robin or LRU eviction strategy?
return
allocReg
(
assembler
::
R15
);
return
allocReg
(
otherThan
==
assembler
::
R15
?
assembler
::
R14
:
assembler
::
R15
);
}
else
if
(
dest
.
type
==
Location
::
Register
)
{
assembler
::
Register
reg
(
dest
.
regnum
);
...
...
src/asm_writing/rewriter.h
View file @
b594728e
...
...
@@ -159,7 +159,7 @@ public:
void
addGuard
(
uint64_t
val
);
void
addGuardNotEq
(
uint64_t
val
);
void
addAttrGuard
(
int
offset
,
uint64_t
val
);
void
addAttrGuard
(
int
offset
,
uint64_t
val
,
bool
negate
=
false
);
RewriterVarUsage
getAttr
(
int
offset
,
KillFlag
kill
,
Location
loc
=
Location
::
any
());
void
setAttr
(
int
offset
,
RewriterVarUsage
other
);
RewriterVarUsage
cmp
(
AST_TYPE
::
AST_TYPE
cmp_type
,
RewriterVarUsage
other
,
Location
loc
=
Location
::
any
());
...
...
@@ -229,7 +229,8 @@ private:
void
kill
(
RewriterVar
*
var
);
// Allocates a register. dest must be of type Register or AnyReg
assembler
::
Register
allocReg
(
Location
dest
);
// If otherThan is a register, guaranteed to not use that register.
assembler
::
Register
allocReg
(
Location
dest
,
Location
otherThan
=
Location
::
any
());
// Allocates an 8-byte region in the scratch space
Location
allocScratch
();
assembler
::
Indirect
indirectFor
(
Location
l
);
...
...
src/codegen/compvars.cpp
View file @
b594728e
...
...
@@ -270,7 +270,7 @@ public:
llvm
::
Value
*
rtn
;
if
(
do_patchpoint
)
{
PatchpointSetupInfo
*
pp
=
patchpoints
::
createGenericPatchpoint
(
emitter
.
currentFunction
(),
info
.
getTypeRecorder
(),
true
,
160
);
=
patchpoints
::
createGenericPatchpoint
(
emitter
.
currentFunction
(),
info
.
getTypeRecorder
(),
true
,
256
);
std
::
vector
<
llvm
::
Value
*>
llvm_args
;
llvm_args
.
push_back
(
var
->
getValue
());
...
...
src/codegen/patchpoints.cpp
View file @
b594728e
...
...
@@ -120,23 +120,23 @@ PatchpointSetupInfo* createGenericPatchpoint(CompiledFunction* parent_cf, TypeRe
}
PatchpointSetupInfo
*
createGetattrPatchpoint
(
CompiledFunction
*
parent_cf
,
TypeRecorder
*
type_recorder
)
{
return
PatchpointSetupInfo
::
initialize
(
true
,
1
,
196
,
parent_cf
,
Getattr
,
type_recorder
);
return
PatchpointSetupInfo
::
initialize
(
true
,
1
,
512
,
parent_cf
,
Getattr
,
type_recorder
);
}
PatchpointSetupInfo
*
createGetitemPatchpoint
(
CompiledFunction
*
parent_cf
,
TypeRecorder
*
type_recorder
)
{
return
PatchpointSetupInfo
::
initialize
(
true
,
1
,
128
,
parent_cf
,
Getitem
,
type_recorder
);
return
PatchpointSetupInfo
::
initialize
(
true
,
1
,
512
,
parent_cf
,
Getitem
,
type_recorder
);
}
PatchpointSetupInfo
*
createSetitemPatchpoint
(
CompiledFunction
*
parent_cf
,
TypeRecorder
*
type_recorder
)
{
return
PatchpointSetupInfo
::
initialize
(
true
,
1
,
144
,
parent_cf
,
Setitem
,
type_recorder
);
return
PatchpointSetupInfo
::
initialize
(
true
,
1
,
256
,
parent_cf
,
Setitem
,
type_recorder
);
}
PatchpointSetupInfo
*
createDelitemPatchpoint
(
CompiledFunction
*
parent_cf
,
TypeRecorder
*
type_recorder
)
{
return
PatchpointSetupInfo
::
initialize
(
false
,
1
,
144
,
parent_cf
,
Delitem
,
type_recorder
);
return
PatchpointSetupInfo
::
initialize
(
false
,
1
,
256
,
parent_cf
,
Delitem
,
type_recorder
);
}
PatchpointSetupInfo
*
createSetattrPatchpoint
(
CompiledFunction
*
parent_cf
,
TypeRecorder
*
type_recorder
)
{
return
PatchpointSetupInfo
::
initialize
(
false
,
2
,
128
,
parent_cf
,
Setattr
,
type_recorder
);
return
PatchpointSetupInfo
::
initialize
(
false
,
2
,
512
,
parent_cf
,
Setattr
,
type_recorder
);
}
PatchpointSetupInfo
*
createDelattrPatchpoint
(
CompiledFunction
*
parent_cf
,
TypeRecorder
*
type_recorder
)
{
...
...
@@ -147,7 +147,7 @@ PatchpointSetupInfo* createCallsitePatchpoint(CompiledFunction* parent_cf, TypeR
// TODO These are very large, but could probably be made much smaller with IC optimizations
// - using rewriter2 for better code
// - not emitting duplicate guards
return
PatchpointSetupInfo
::
initialize
(
true
,
3
,
48
0
+
48
*
num_args
,
parent_cf
,
Callsite
,
type_recorder
);
return
PatchpointSetupInfo
::
initialize
(
true
,
3
,
64
0
+
48
*
num_args
,
parent_cf
,
Callsite
,
type_recorder
);
}
PatchpointSetupInfo
*
createGetGlobalPatchpoint
(
CompiledFunction
*
parent_cf
,
TypeRecorder
*
type_recorder
)
{
...
...
@@ -155,7 +155,7 @@ PatchpointSetupInfo* createGetGlobalPatchpoint(CompiledFunction* parent_cf, Type
}
PatchpointSetupInfo
*
createBinexpPatchpoint
(
CompiledFunction
*
parent_cf
,
TypeRecorder
*
type_recorder
)
{
return
PatchpointSetupInfo
::
initialize
(
true
,
4
,
320
,
parent_cf
,
Binexp
,
type_recorder
);
return
PatchpointSetupInfo
::
initialize
(
true
,
4
,
512
,
parent_cf
,
Binexp
,
type_recorder
);
}
PatchpointSetupInfo
*
createNonzeroPatchpoint
(
CompiledFunction
*
parent_cf
,
TypeRecorder
*
type_recorder
)
{
...
...
src/runtime/builtin_modules/builtins.cpp
View file @
b594728e
...
...
@@ -55,7 +55,7 @@ extern "C" Box* dir(Box* obj) {
}
// If __dict__ is present use its keys and add the reset below
Box
*
obj_dict
=
getattr
_internal
(
obj
,
"__dict__"
,
false
,
true
,
nullptr
);
Box
*
obj_dict
=
getattr
Internal
(
obj
,
"__dict__"
,
nullptr
);
if
(
obj_dict
&&
obj_dict
->
cls
==
dict_cls
)
{
result
=
new
BoxedList
();
for
(
auto
&
kv
:
static_cast
<
BoxedDict
*>
(
obj_dict
)
->
d
)
{
...
...
@@ -331,7 +331,7 @@ Box* getattrFunc(Box* obj, Box* _str, Box* default_value) {
}
BoxedString
*
str
=
static_cast
<
BoxedString
*>
(
_str
);
Box
*
rtn
=
getattr
_internal
(
obj
,
str
->
s
,
true
,
true
,
NULL
);
Box
*
rtn
=
getattr
Internal
(
obj
,
str
->
s
,
NULL
);
if
(
!
rtn
)
{
if
(
default_value
)
...
...
@@ -350,7 +350,7 @@ Box* hasattr(Box* obj, Box* _str) {
}
BoxedString
*
str
=
static_cast
<
BoxedString
*>
(
_str
);
Box
*
attr
=
getattr
_internal
(
obj
,
str
->
s
,
true
,
true
,
NULL
);
Box
*
attr
=
getattr
Internal
(
obj
,
str
->
s
,
NULL
);
Box
*
rtn
=
attr
?
True
:
False
;
return
rtn
;
...
...
src/runtime/objmodel.cpp
View file @
b594728e
...
...
@@ -672,41 +672,6 @@ void Box::setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite
attrs
->
attr_list
->
attrs
[
numattrs
]
=
val
;
}
static
Box
*
_handleClsAttr
(
Box
*
obj
,
Box
*
attr
)
{
if
(
attr
->
cls
==
function_cls
||
attr
->
cls
==
method_cls
)
{
Box
*
rtn
=
boxInstanceMethod
(
obj
,
attr
);
return
rtn
;
}
if
(
attr
->
cls
==
member_cls
)
{
BoxedMemberDescriptor
*
member_desc
=
static_cast
<
BoxedMemberDescriptor
*>
(
attr
);
switch
(
member_desc
->
type
)
{
case
BoxedMemberDescriptor
:
:
OBJECT
:
{
assert
(
member_desc
->
offset
%
sizeof
(
Box
*
)
==
0
);
Box
*
rtn
=
reinterpret_cast
<
Box
**>
(
obj
)[
member_desc
->
offset
/
sizeof
(
Box
*
)];
// be careful about returning NULLs; I'm not sure what the correct behavior is here:
RELEASE_ASSERT
(
rtn
,
""
);
return
rtn
;
}
case
BoxedMemberDescriptor
:
:
BYTE
:
{
int8_t
rtn
=
reinterpret_cast
<
int8_t
*>
(
obj
)[
member_desc
->
offset
];
return
boxInt
(
rtn
);
}
case
BoxedMemberDescriptor
:
:
BOOL
:
{
bool
rtn
=
reinterpret_cast
<
bool
*>
(
obj
)[
member_desc
->
offset
];
return
boxBool
(
rtn
);
}
case
BoxedMemberDescriptor
:
:
INT
:
{
int
rtn
=
reinterpret_cast
<
int
*>
(
obj
)[
member_desc
->
offset
/
sizeof
(
int
)];
return
boxInt
(
rtn
);
}
default:
RELEASE_ASSERT
(
0
,
"%d"
,
member_desc
->
type
);
}
abort
();
}
return
attr
;
}
Box
*
typeLookup
(
BoxedClass
*
cls
,
const
std
::
string
&
attr
,
GetattrRewriteArgs
*
rewrite_args
)
{
Box
*
val
;
...
...
@@ -737,43 +702,150 @@ Box* typeLookup(BoxedClass* cls, const std::string& attr, GetattrRewriteArgs* re
}
}
Box
*
getclsattr_internal
(
Box
*
obj
,
const
std
::
string
&
attr
,
GetattrRewriteArgs
*
rewrite_args
)
{
Box
*
val
;
bool
isNondataDescriptorInstanceSpecialCase
(
Box
*
descr
)
{
return
descr
->
cls
==
function_cls
;
}
Box
*
nondataDescriptorInstanceSpecialCases
(
GetattrRewriteArgs
*
rewrite_args
,
Box
*
obj
,
Box
*
descr
,
RewriterVarUsage
&
r_descr
,
bool
for_call
,
bool
*
should_bind_out
)
{
// Special case: non-data descriptor: function
if
(
descr
->
cls
==
function_cls
)
{
if
(
!
for_call
)
{
if
(
rewrite_args
)
{
RewriterVarUsage
rcls
=
rewrite_args
->
obj
.
getAttr
(
BOX_CLS_OFFSET
,
RewriterVarUsage
::
NoKill
);
// can't guard after because we make this call... the call is trivial enough
// that we can probably work around it if it's important, but otherwise, if
// this triggers, just abort rewriting, I guess
assert
(
!
rewrite_args
->
more_guards_after
);
rewrite_args
->
rewriter
->
setDoneGuarding
();
rewrite_args
->
out_rtn
=
rewrite_args
->
rewriter
->
call
(
false
,
(
void
*
)
boxInstanceMethod
,
std
::
move
(
rewrite_args
->
obj
),
std
::
move
(
r_descr
));
rewrite_args
->
out_success
=
true
;
}
return
boxInstanceMethod
(
obj
,
descr
);
}
else
{
if
(
rewrite_args
)
{
if
(
!
rewrite_args
->
more_guards_after
)
rewrite_args
->
rewriter
->
setDoneGuarding
();
rewrite_args
->
obj
.
setDoneUsing
();
rewrite_args
->
out_rtn
=
std
::
move
(
r_descr
);
rewrite_args
->
out_success
=
true
;
}
*
should_bind_out
=
true
;
return
descr
;
}
}
GetattrRewriteArgs
sub_rewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
rcls
),
Location
::
forArg
(
1
),
rewrite_args
->
more_guards_after
);
return
NULL
;
}
val
=
typeLookup
(
obj
->
cls
,
attr
,
&
sub_rewrite_args
);
Box
*
descriptorClsSpecialCases
(
GetattrRewriteArgs
*
rewrite_args
,
BoxedClass
*
cls
,
Box
*
descr
,
RewriterVarUsage
&
r_descr
,
bool
for_call
,
bool
*
should_bind_out
)
{
// Special case: functions
if
(
descr
->
cls
==
function_cls
)
{
if
(
rewrite_args
)
r_descr
.
addAttrGuard
(
BOX_CLS_OFFSET
,
(
uint64_t
)
descr
->
cls
);
if
(
!
sub_rewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
if
(
!
for_call
)
{
if
(
rewrite_args
)
{
assert
(
!
rewrite_args
->
more_guards_after
);
rewrite_args
->
rewriter
->
setDoneGuarding
();
rewrite_args
->
obj
.
setDoneUsing
();
// return an unbound instancemethod
rewrite_args
->
out_rtn
=
rewrite_args
->
rewriter
->
call
(
false
,
(
void
*
)
boxUnboundInstanceMethod
,
std
::
move
(
r_descr
));
rewrite_args
->
out_success
=
true
;
}
return
boxUnboundInstanceMethod
(
descr
);
}
else
{
if
(
val
)
{
rewrite_args
->
out_rtn
=
std
::
move
(
sub_rewrite_args
.
out_rtn
);
if
(
rewrite_args
)
{
if
(
!
rewrite_args
->
more_guards_after
)
rewrite_args
->
rewriter
->
setDoneGuarding
();
rewrite_args
->
obj
.
setDoneUsing
();
rewrite_args
->
out_success
=
true
;
rewrite_args
->
out_rtn
=
std
::
move
(
r_descr
);
}
// leave should_bind_out set to false
return
descr
;
}
}
else
{
val
=
typeLookup
(
obj
->
cls
,
attr
,
NULL
);
}
if
(
val
==
NULL
)
{
// Special case: member descriptor
if
(
descr
->
cls
==
member_cls
)
{
if
(
rewrite_args
)
r_descr
.
addAttrGuard
(
BOX_CLS_OFFSET
,
(
uint64_t
)
descr
->
cls
);
if
(
rewrite_args
)
{
assert
(
!
rewrite_args
->
more_guards_after
);
rewrite_args
->
rewriter
->
setDoneGuarding
();
rewrite_args
->
obj
.
setDoneUsing
();
// Actually just return val (it's a descriptor but only
// has special behaviour for instance lookups - see below)
rewrite_args
->
out_rtn
=
std
::
move
(
r_descr
);
rewrite_args
->
out_success
=
true
;
return
val
;
}
return
descr
;
}
return
NULL
;
}
Box
*
dataDescriptorInstanceSpecialCases
(
GetattrRewriteArgs
*
rewrite_args
,
Box
*
obj
,
Box
*
descr
,
RewriterVarUsage
&
r_descr
,
bool
for_call
,
bool
*
should_bind_out
)
{
// Special case: data descriptor: member descriptor
if
(
descr
->
cls
==
member_cls
)
{
BoxedMemberDescriptor
*
member_desc
=
static_cast
<
BoxedMemberDescriptor
*>
(
descr
);
// TODO should also have logic to raise a type error if type of obj is wrong
if
(
rewrite_args
)
{
// Ok this is a lie, _handleClsAttr can call back into python because it does GC collection.
// I guess it should disable GC or something...
RewriterVarUsage
rrtn
=
rewrite_args
->
rewriter
->
call
(
false
,
(
void
*
)
_handleClsAttr
,
std
::
move
(
rewrite_args
->
obj
),
std
::
move
(
rewrite_args
->
out_rtn
));
rewrite_args
->
out_rtn
=
std
::
move
(
rrtn
);
if
(
!
rewrite_args
->
more_guards_after
)
rewrite_args
->
rewriter
->
setDoneGuarding
();
r_descr
.
setDoneUsing
();
// TODO figure out guards
// do i need to have the rewriter write code to lookup the
// offset or do the guards protect that?
rewrite_args
->
out_rtn
=
rewrite_args
->
obj
.
getAttr
(
member_desc
->
offset
,
RewriterVarUsage
::
KillFlag
::
Kill
,
rewrite_args
->
destination
);
rewrite_args
->
out_success
=
true
;
}
return
_handleClsAttr
(
obj
,
val
);
switch
(
member_desc
->
type
)
{
case
BoxedMemberDescriptor
:
:
OBJECT
:
{
assert
(
member_desc
->
offset
%
sizeof
(
Box
*
)
==
0
);
Box
*
rtn
=
reinterpret_cast
<
Box
**>
(
obj
)[
member_desc
->
offset
/
sizeof
(
Box
*
)];
RELEASE_ASSERT
(
rtn
,
""
);
return
rtn
;
}
case
BoxedMemberDescriptor
:
:
BYTE
:
{
// TODO rewriter stuff for these other cases as well
rewrite_args
=
NULL
;
int8_t
rtn
=
reinterpret_cast
<
int8_t
*>
(
obj
)[
member_desc
->
offset
];
return
boxInt
(
rtn
);
}
case
BoxedMemberDescriptor
:
:
BOOL
:
{
rewrite_args
=
NULL
;
bool
rtn
=
reinterpret_cast
<
bool
*>
(
obj
)[
member_desc
->
offset
];
return
boxBool
(
rtn
);
}
case
BoxedMemberDescriptor
:
:
INT
:
{
rewrite_args
=
NULL
;
int
rtn
=
reinterpret_cast
<
int
*>
(
obj
)[
member_desc
->
offset
/
sizeof
(
int
)];
return
boxInt
(
rtn
);
}
default:
RELEASE_ASSERT
(
0
,
"%d"
,
member_desc
->
type
);
}
}
return
NULL
;
}
inline
Box
*
getclsattr_internal
(
Box
*
obj
,
const
std
::
string
&
attr
,
GetattrRewriteArgs
*
rewrite_args
)
{
return
getattrInternalGeneral
(
obj
,
attr
,
rewrite_args
,
/* cls_only */
true
,
/* for_call */
false
,
NULL
);
}
extern
"C"
Box
*
getclsattr
(
Box
*
obj
,
const
char
*
attr
)
{
...
...
@@ -822,16 +894,61 @@ static Box* (*runtimeCall2)(Box*, ArgPassSpec, Box*, Box*) = (Box * (*)(Box*, Ar
static
Box
*
(
*
runtimeCall3
)(
Box
*
,
ArgPassSpec
,
Box
*
,
Box
*
,
Box
*
)
=
(
Box
*
(
*
)(
Box
*
,
ArgPassSpec
,
Box
*
,
Box
*
,
Box
*
))
runtimeCall
;
Box
*
getattr_internal
(
Box
*
obj
,
const
std
::
string
&
attr
,
bool
check_cls
,
bool
allow_custom
,
GetattrRewriteArgs
*
rewrite_args
)
{
if
(
allow_custom
)
{
Box
*
getattrInternalGeneral
(
Box
*
obj
,
const
std
::
string
&
attr
,
GetattrRewriteArgs
*
rewrite_args
,
bool
cls_only
,
bool
for_call
,
bool
*
should_bind_out
)
{
if
(
for_call
)
{
*
should_bind_out
=
false
;
}
if
(
obj
->
cls
==
closure_cls
)
{
Box
*
val
=
NULL
;
if
(
rewrite_args
)
{
GetattrRewriteArgs
hrewrite_args
(
rewrite_args
->
rewriter
,
rewrite_args
->
obj
.
addUse
(),
rewrite_args
->
destination
,
true
);
val
=
obj
->
getattr
(
attr
,
&
hrewrite_args
);
if
(
!
hrewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
if
(
val
)
{
if
(
!
rewrite_args
->
more_guards_after
)
rewrite_args
->
rewriter
->
setDoneGuarding
();
rewrite_args
->
out_rtn
=
std
::
move
(
hrewrite_args
.
out_rtn
);
rewrite_args
->
out_success
=
true
;
rewrite_args
->
obj
.
setDoneUsing
();
return
val
;
}
}
else
{
val
=
obj
->
getattr
(
attr
,
NULL
);
if
(
val
)
{
return
val
;
}
}
// If val doesn't exist, then we move up to the parent closure
// TODO closures should get their own treatment, but now just piggy-back on the
// normal hidden-class IC logic.
// Can do better since we don't need to guard on the cls (always going to be closure)
BoxedClosure
*
closure
=
static_cast
<
BoxedClosure
*>
(
obj
);
if
(
closure
->
parent
)
{
if
(
rewrite_args
)
{
rewrite_args
->
obj
=
rewrite_args
->
obj
.
getAttr
(
offsetof
(
BoxedClosure
,
parent
),
RewriterVarUsage
::
NoKill
);
if
(
!
rewrite_args
->
more_guards_after
)
rewrite_args
->
rewriter
->
setDoneGuarding
();
}
return
getattrInternal
(
closure
->
parent
,
attr
,
rewrite_args
);
}
raiseExcHelper
(
NameError
,
"free variable '%s' referenced before assignment in enclosing scope"
,
attr
.
c_str
());
}
if
(
!
cls_only
)
{
// Don't need to pass icentry args, since we special-case __getattribtue__ and __getattr__ to use
// invalidation rather than guards
Box
*
getattribute
=
getclsattr_internal
(
obj
,
"__getattribute__"
,
NULL
);
// TODO since you changed this to typeLookup you need to guard
Box
*
getattribute
=
typeLookup
(
obj
->
cls
,
"__getattribute__"
,
NULL
);
if
(
getattribute
)
{
// TODO this is a good candidate for interning?
Box
*
boxstr
=
boxString
(
attr
);
Box
*
rtn
=
runtimeCall
1
(
getattribute
,
ArgPassSpec
(
1
)
,
boxstr
);
Box
*
rtn
=
runtimeCall
2
(
getattribute
,
ArgPassSpec
(
2
),
obj
,
boxstr
);
return
rtn
;
}
...
...
@@ -840,79 +957,312 @@ Box* getattr_internal(Box* obj, const std::string& attr, bool check_cls, bool al
}
}
if
(
obj
->
cls
==
type_cls
)
{
// Handle descriptor logic here.
// A descriptor is either a data descriptor or a non-data descriptor.
// data descriptors define both __get__ and __set__. non-data descriptors
// only define __get__. Rules are different for the two types, which means
// that even though __get__ is the one we might call, we still have to check
// if __set__ exists.
// If __set__ exists, it's a data descriptor, and it takes precedence over
// the instance attribute.
// Otherwise, it's non-data, and we only call __get__ if the instance
// attribute doesn't exist.
// In the cls_only case, we ignore the instance attribute
// (so we don't have to check if __set__ exists at all)
// Look up the class attribute (called `descr` here because it might
// be a descriptor).
Box
*
descr
=
NULL
;
RewriterVarUsage
r_descr
(
RewriterVarUsage
::
empty
());
if
(
rewrite_args
)
{
GetattrRewriteArgs
grewrite_args
(
rewrite_args
->
rewriter
,
rewrite_args
->
obj
.
addUse
(),
rewrite_args
->
destination
,
true
);
RewriterVarUsage
r_obj_cls
=
rewrite_args
->
obj
.
getAttr
(
BOX_CLS_OFFSET
,
RewriterVarUsage
::
NoKill
,
Location
::
any
());
GetattrRewriteArgs
grewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_obj_cls
),
rewrite_args
->
destination
,
true
);
descr
=
typeLookup
(
obj
->
cls
,
attr
,
&
grewrite_args
);
Box
*
val
=
typeLookup
(
static_cast
<
BoxedClass
*>
(
obj
),
attr
,
&
grewrite_args
);
if
(
val
)
{
rewrite_args
->
obj
.
setDoneUsing
();
rewrite_args
->
out_rtn
=
std
::
move
(
grewrite_args
.
out_rtn
);
rewrite_args
->
out_success
=
true
;
if
(
!
rewrite_args
->
more_guards_after
)
rewrite_args
->
rewriter
->
setDoneGuarding
();
return
val
;
if
(
!
grewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
if
(
descr
)
{
r_descr
=
std
::
move
(
grewrite_args
.
out_rtn
);
}
}
else
{
descr
=
typeLookup
(
obj
->
cls
,
attr
,
NULL
);
}
// Check if it's a data descriptor
Box
*
_get_
=
NULL
;
RewriterVarUsage
r_get
(
RewriterVarUsage
::
empty
());
if
(
descr
)
{
if
(
rewrite_args
)
r_descr
.
addAttrGuard
(
BOX_CLS_OFFSET
,
(
uint64_t
)
descr
->
cls
);
// Special-case data descriptors (e.g., member descriptors)
Box
*
res
=
dataDescriptorInstanceSpecialCases
(
rewrite_args
,
obj
,
descr
,
r_descr
,
for_call
,
should_bind_out
);
if
(
res
)
{
return
res
;
}
// Let's only check if __get__ exists if it's not a special case
// nondata descriptor. The nondata case is handled below, but
// we can immediately know to skip this part if it's one of the
// special case nondata descriptors.
if
(
!
isNondataDescriptorInstanceSpecialCase
(
descr
))
{
// Check if __get__ exists
if
(
rewrite_args
)
{
RewriterVarUsage
r_descr_cls
=
r_descr
.
getAttr
(
BOX_CLS_OFFSET
,
RewriterVarUsage
::
NoKill
,
Location
::
any
());
GetattrRewriteArgs
grewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_descr_cls
),
Location
::
any
(),
true
);
_get_
=
typeLookup
(
descr
->
cls
,
"__get__"
,
&
grewrite_args
);
if
(
!
grewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
if
(
_get_
)
{
r_get
=
std
::
move
(
grewrite_args
.
out_rtn
);
}
}
else
{
Box
*
val
=
typeLookup
(
static_cast
<
BoxedClass
*>
(
obj
),
attr
,
NULL
);
if
(
val
)
return
val
;
_get_
=
typeLookup
(
descr
->
cls
,
"__get__"
,
NULL
);
}
if
(
_get_
&&
!
cls_only
)
{
// Check if __set__ exists
Box
*
_set_
=
NULL
;
if
(
rewrite_args
)
{
RewriterVarUsage
r_descr_cls
=
r_descr
.
getAttr
(
BOX_CLS_OFFSET
,
RewriterVarUsage
::
NoKill
,
Location
::
any
());
GetattrRewriteArgs
grewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_descr_cls
),
Location
::
any
(),
true
);
_set_
=
typeLookup
(
descr
->
cls
,
"__set__"
,
&
grewrite_args
);
if
(
!
grewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
if
(
_set_
)
{
grewrite_args
.
out_rtn
.
setDoneUsing
();
}
}
else
{
Box
*
val
=
NULL
;
_set_
=
typeLookup
(
descr
->
cls
,
"__set__"
,
NULL
);
}
// Call __get__(descr, obj, obj->cls)
if
(
_set_
)
{
// this could happen for the callattr path...
if
(
rewrite_args
&&
rewrite_args
->
more_guards_after
)
rewrite_args
=
NULL
;
Box
*
res
;
if
(
rewrite_args
)
{
CallRewriteArgs
crewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_get
),
rewrite_args
->
destination
,
rewrite_args
->
more_guards_after
);
crewrite_args
.
arg1
=
std
::
move
(
r_descr
);
crewrite_args
.
arg2
=
rewrite_args
->
obj
.
addUse
();
crewrite_args
.
arg3
=
rewrite_args
->
obj
.
getAttr
(
BOX_CLS_OFFSET
,
RewriterVarUsage
::
Kill
,
Location
::
any
());
res
=
runtimeCallInternal
(
_get_
,
&
crewrite_args
,
ArgPassSpec
(
3
),
descr
,
obj
,
obj
->
cls
,
NULL
,
NULL
);
if
(
!
crewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
{
rewrite_args
->
out_success
=
true
;
rewrite_args
->
out_rtn
=
std
::
move
(
crewrite_args
.
out_rtn
);
}
}
else
{
r_descr
.
ensureDoneUsing
();
r_get
.
ensureDoneUsing
();
res
=
runtimeCallInternal
(
_get_
,
NULL
,
ArgPassSpec
(
3
),
descr
,
obj
,
obj
->
cls
,
NULL
,
NULL
);
}
return
res
;
}
}
}
}
if
(
!
cls_only
)
{
if
(
obj
->
cls
!=
type_cls
)
{
// Look up the val in the object's dictionary and if you find it, return it.
Box
*
val
;
RewriterVarUsage
r_val
(
RewriterVarUsage
::
empty
());
if
(
rewrite_args
)
{
GetattrRewriteArgs
hrewrite_args
(
rewrite_args
->
rewriter
,
rewrite_args
->
obj
.
addUse
(),
rewrite_args
->
destination
,
true
);
val
=
obj
->
getattr
(
attr
,
&
hrewrite_args
);
if
(
hrewrite_args
.
out_success
)
{
if
(
!
hrewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
if
(
val
)
{
r_val
=
std
::
move
(
hrewrite_args
.
out_rtn
);
}
}
else
{
val
=
obj
->
getattr
(
attr
,
NULL
);
}
if
(
val
)
{
rewrite_args
->
out_rtn
=
std
::
move
(
hrewrite_args
.
out_rtn
);
if
(
rewrite_args
)
{
if
(
!
rewrite_args
->
more_guards_after
)
rewrite_args
->
rewriter
->
setDoneGuarding
();
rewrite_args
->
obj
.
setDoneUsing
();
rewrite_args
->
out_rtn
=
std
::
move
(
r_val
);
rewrite_args
->
out_success
=
true
;
}
r_descr
.
ensureDoneUsing
();
r_get
.
ensureDoneUsing
();
return
val
;
}
}
else
{
// More complicated when obj is a type
// We have to look up the attr in the entire
// class hierarchy, and we also have to check if it is a descriptor,
// in addition to the data/nondata descriptor logic.
// (in CPython, see type_getattro in typeobject.c)
Box
*
val
;
RewriterVarUsage
r_val
(
RewriterVarUsage
::
empty
());
if
(
rewrite_args
)
{
GetattrRewriteArgs
grewrite_args
(
rewrite_args
->
rewriter
,
rewrite_args
->
obj
.
addUse
(),
rewrite_args
->
destination
,
true
);
val
=
typeLookup
(
static_cast
<
BoxedClass
*>
(
obj
),
attr
,
&
grewrite_args
);
if
(
!
grewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
if
(
val
)
{
r_val
=
std
::
move
(
grewrite_args
.
out_rtn
);
}
}
else
{
val
=
obj
->
getattr
(
attr
,
NULL
);
val
=
typeLookup
(
static_cast
<
BoxedClass
*>
(
obj
),
attr
,
NULL
);
}
if
(
val
)
{
r_get
.
ensureDoneUsing
();
r_descr
.
ensureDoneUsing
();
Box
*
res
=
descriptorClsSpecialCases
(
rewrite_args
,
static_cast
<
BoxedClass
*>
(
obj
),
val
,
r_val
,
for_call
,
should_bind_out
);
if
(
res
)
{
return
res
;
}
// Lookup __get__
RewriterVarUsage
r_get
(
RewriterVarUsage
::
empty
());
Box
*
local_get
;
if
(
rewrite_args
)
{
RewriterVarUsage
r_val_cls
=
r_val
.
getAttr
(
BOX_CLS_OFFSET
,
RewriterVarUsage
::
NoKill
,
Location
::
any
());
GetattrRewriteArgs
grewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_val_cls
),
Location
::
any
(),
true
);
local_get
=
typeLookup
(
val
->
cls
,
"__get__"
,
&
grewrite_args
);
if
(
!
grewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
if
(
local_get
)
{
r_get
=
std
::
move
(
grewrite_args
.
out_rtn
);
}
}
else
{
local_get
=
typeLookup
(
val
->
cls
,
"__get__"
,
NULL
);
}
// Call __get__(val, None, obj)
if
(
local_get
)
{
Box
*
res
;
// this could happen for the callattr path...
if
(
rewrite_args
&&
rewrite_args
->
more_guards_after
)
rewrite_args
=
NULL
;
if
(
rewrite_args
)
{
CallRewriteArgs
crewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_get
),
rewrite_args
->
destination
,
rewrite_args
->
more_guards_after
);
crewrite_args
.
arg1
=
std
::
move
(
r_val
);
crewrite_args
.
arg2
=
rewrite_args
->
rewriter
->
loadConst
((
intptr_t
)
None
,
Location
::
any
());
crewrite_args
.
arg3
=
std
::
move
(
rewrite_args
->
obj
);
res
=
runtimeCallInternal
(
local_get
,
&
crewrite_args
,
ArgPassSpec
(
3
),
val
,
None
,
obj
,
NULL
,
NULL
);
if
(
!
crewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
{
rewrite_args
->
out_success
=
true
;
rewrite_args
->
out_rtn
=
std
::
move
(
crewrite_args
.
out_rtn
);
}
}
else
{
r_val
.
ensureDoneUsing
();
r_get
.
ensureDoneUsing
();
res
=
runtimeCallInternal
(
local_get
,
NULL
,
ArgPassSpec
(
3
),
val
,
None
,
obj
,
NULL
,
NULL
);
}
return
res
;
}
// If there was no local __get__, just return val
if
(
rewrite_args
)
{
if
(
!
rewrite_args
->
more_guards_after
)
rewrite_args
->
rewriter
->
setDoneGuarding
();
rewrite_args
->
obj
.
setDoneUsing
();
rewrite_args
->
out_rtn
=
std
::
move
(
r_val
);
rewrite_args
->
out_success
=
true
;
}
else
{
r_val
.
ensureDoneUsing
();
}
return
val
;
}
}
}
// If descr and __get__ exist, then call __get__
if
(
descr
)
{
// Special cases first
Box
*
res
=
nondataDescriptorInstanceSpecialCases
(
rewrite_args
,
obj
,
descr
,
r_descr
,
for_call
,
should_bind_out
);
if
(
res
)
{
return
res
;
}
// We looked up __get__ above. If we found it, call it and return
// the result.
if
(
_get_
)
{
// this could happen for the callattr path...
if
(
rewrite_args
&&
rewrite_args
->
more_guards_after
)
rewrite_args
=
NULL
;
// TODO closures should get their own treatment, but now just piggy-back on the
// normal hidden-class IC logic.
// Can do better since we don't need to guard on the cls (always going to be closure)
if
(
obj
->
cls
==
closure_cls
)
{
BoxedClosure
*
closure
=
static_cast
<
BoxedClosure
*>
(
obj
);
if
(
closure
->
parent
)
{
Box
*
res
;
if
(
rewrite_args
)
{
rewrite_args
->
obj
=
rewrite_args
->
obj
.
getAttr
(
offsetof
(
BoxedClosure
,
parent
),
RewriterVarUsage
::
NoKill
);
CallRewriteArgs
crewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_get
),
rewrite_args
->
destination
,
rewrite_args
->
more_guards_after
);
crewrite_args
.
arg1
=
std
::
move
(
r_descr
);
crewrite_args
.
arg2
=
rewrite_args
->
obj
.
addUse
();
crewrite_args
.
arg3
=
rewrite_args
->
obj
.
getAttr
(
BOX_CLS_OFFSET
,
RewriterVarUsage
::
Kill
,
Location
::
any
());
res
=
runtimeCallInternal
(
_get_
,
&
crewrite_args
,
ArgPassSpec
(
3
),
descr
,
obj
,
obj
->
cls
,
NULL
,
NULL
);
if
(
!
crewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
{
rewrite_args
->
out_success
=
true
;
rewrite_args
->
out_rtn
=
std
::
move
(
crewrite_args
.
out_rtn
);
}
return
getattr_internal
(
closure
->
parent
,
attr
,
false
,
false
,
rewrite_args
);
}
else
{
r_descr
.
ensureDoneUsing
();
r_get
.
ensureDoneUsing
();
res
=
runtimeCallInternal
(
_get_
,
NULL
,
ArgPassSpec
(
3
),
descr
,
obj
,
obj
->
cls
,
NULL
,
NULL
);
}
raiseExcHelper
(
NameError
,
"free variable '%s' referenced before assignment in enclosing scope"
,
attr
.
c_str
());
return
res
;
}
// Otherwise, just return descr.
if
(
rewrite_args
)
{
rewrite_args
->
obj
.
setDoneUsing
();
if
(
!
rewrite_args
->
more_guards_after
)
rewrite_args
->
rewriter
->
setDoneGuarding
();
rewrite_args
->
out_rtn
=
std
::
move
(
r_descr
);
rewrite_args
->
out_success
=
true
;
}
return
descr
;
}
// Finally, check __getattr__
if
(
allow_custom
)
{
// Don't need to pass icentry args, since we special-case __getattrib
tu
e__ and __getattr__ to use
if
(
!
cls_only
)
{
// Don't need to pass icentry args, since we special-case __getattrib
ut
e__ and __getattr__ to use
// invalidation rather than guards
Box
*
getattr
=
getclsattr_internal
(
obj
,
"__getattr__"
,
NULL
);
rewrite_args
=
NULL
;
Box
*
getattr
=
typeLookup
(
obj
->
cls
,
"__getattr__"
,
NULL
);
if
(
getattr
)
{
Box
*
boxstr
=
boxString
(
attr
);
Box
*
rtn
=
runtimeCall1
(
getattr
,
ArgPassSpec
(
1
),
boxstr
);
Box
*
rtn
=
runtimeCall2
(
getattr
,
ArgPassSpec
(
2
),
obj
,
boxstr
);
if
(
rewrite_args
)
rewrite_args
->
out_rtn
.
ensureDoneUsing
();
return
rtn
;
}
...
...
@@ -921,31 +1271,17 @@ Box* getattr_internal(Box* obj, const std::string& attr, bool check_cls, bool al
}
}
Box
*
rtn
=
NULL
;
if
(
check_cls
)
{
if
(
rewrite_args
)
{
GetattrRewriteArgs
crewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
rewrite_args
->
obj
),
rewrite_args
->
destination
,
rewrite_args
->
more_guards_after
);
rtn
=
getclsattr_internal
(
obj
,
attr
,
&
crewrite_args
);
if
(
!
crewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
{
if
(
rtn
)
rewrite_args
->
out_rtn
=
std
::
move
(
crewrite_args
.
out_rtn
);
else
rewrite_args
->
obj
=
std
::
move
(
crewrite_args
.
obj
);
}
}
else
{
rtn
=
getclsattr_internal
(
obj
,
attr
,
NULL
);
}
}
if
(
rewrite_args
)
{
rewrite_args
->
obj
.
ensureDoneUsing
();
rewrite_args
->
out_success
=
true
;
}
return
NULL
;
}
return
rtn
;
Box
*
getattrInternal
(
Box
*
obj
,
const
std
::
string
&
attr
,
GetattrRewriteArgs
*
rewrite_args
)
{
return
getattrInternalGeneral
(
obj
,
attr
,
rewrite_args
,
/* cls_only */
false
,
/* for_call */
false
,
NULL
);
}
extern
"C"
Box
*
getattr
(
Box
*
obj
,
const
char
*
attr
)
{
...
...
@@ -965,7 +1301,6 @@ extern "C" Box* getattr(Box* obj, const char* attr) {
Box
*
val
;
if
(
rewriter
.
get
())
{
// rewriter->trap();
Location
dest
;
TypeRecorder
*
recorder
=
rewriter
->
getTypeRecorder
();
if
(
recorder
)
...
...
@@ -973,7 +1308,12 @@ extern "C" Box* getattr(Box* obj, const char* attr) {
else
dest
=
rewriter
->
getReturnDestination
();
GetattrRewriteArgs
rewrite_args
(
rewriter
.
get
(),
rewriter
->
getArg
(
0
),
dest
,
false
);
val
=
getattr_internal
(
obj
,
attr
,
true
,
true
,
&
rewrite_args
);
val
=
getattrInternal
(
obj
,
attr
,
&
rewrite_args
);
// should make sure getattrInternal calls finishes using obj itself
// if it is successful
if
(
!
rewrite_args
.
out_success
)
rewrite_args
.
obj
.
ensureDoneUsing
();
if
(
rewrite_args
.
out_success
&&
val
)
{
if
(
recorder
)
{
...
...
@@ -988,7 +1328,7 @@ extern "C" Box* getattr(Box* obj, const char* attr) {
}
}
}
else
{
val
=
getattr
_internal
(
obj
,
attr
,
true
,
true
,
NULL
);
val
=
getattr
Internal
(
obj
,
attr
,
NULL
);
}
if
(
val
)
{
...
...
@@ -997,6 +1337,71 @@ extern "C" Box* getattr(Box* obj, const char* attr) {
raiseAttributeError
(
obj
,
attr
);
}
static
void
setattr_internal
(
Box
*
obj
,
const
std
::
string
&
attr
,
Box
*
val
,
SetattrRewriteArgs
*
rewrite_args
)
{
// Lookup a descriptor
Box
*
descr
=
NULL
;
RewriterVarUsage
r_descr
(
RewriterVarUsage
::
empty
());
// TODO probably check that the cls is user-defined or something like that
// (figure out exactly what)
// (otherwise no need to check descriptor logic)
if
(
rewrite_args
)
{
RewriterVarUsage
r_cls
=
rewrite_args
->
obj
.
getAttr
(
BOX_CLS_OFFSET
,
RewriterVarUsage
::
KillFlag
::
NoKill
,
Location
::
any
());
GetattrRewriteArgs
crewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_cls
),
rewrite_args
->
rewriter
->
getReturnDestination
(),
true
);
descr
=
typeLookup
(
obj
->
cls
,
attr
,
&
crewrite_args
);
if
(
!
crewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
if
(
descr
)
{
r_descr
=
std
::
move
(
crewrite_args
.
out_rtn
);
}
}
else
{
descr
=
typeLookup
(
obj
->
cls
,
attr
,
NULL
);
}
Box
*
_set_
=
NULL
;
RewriterVarUsage
r_set
(
RewriterVarUsage
::
empty
());
if
(
descr
)
{
if
(
rewrite_args
)
{
RewriterVarUsage
r_cls
=
r_descr
.
getAttr
(
BOX_CLS_OFFSET
,
RewriterVarUsage
::
KillFlag
::
NoKill
,
Location
::
any
());
GetattrRewriteArgs
trewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_cls
),
Location
::
any
(),
true
);
_set_
=
typeLookup
(
descr
->
cls
,
"__set__"
,
&
trewrite_args
);
if
(
!
trewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
if
(
_set_
)
{
r_set
=
std
::
move
(
trewrite_args
.
out_rtn
);
}
}
else
{
_set_
=
typeLookup
(
descr
->
cls
,
"__set__"
,
NULL
);
}
}
// If `descr` has __set__ (thus making it a descriptor) we should call
// __set__ with `val` rather than directly calling setattr
if
(
descr
&&
_set_
)
{
if
(
rewrite_args
)
{
CallRewriteArgs
crewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_set
),
Location
::
any
(),
rewrite_args
->
more_guards_after
);
crewrite_args
.
arg1
=
std
::
move
(
r_descr
);
crewrite_args
.
arg2
=
std
::
move
(
rewrite_args
->
obj
);
crewrite_args
.
arg3
=
std
::
move
(
rewrite_args
->
attrval
);
runtimeCallInternal
(
_set_
,
&
crewrite_args
,
ArgPassSpec
(
3
),
descr
,
obj
,
val
,
NULL
,
NULL
);
if
(
crewrite_args
.
out_success
)
{
crewrite_args
.
out_rtn
.
setDoneUsing
();
rewrite_args
->
out_success
=
true
;
}
}
else
{
runtimeCallInternal
(
_set_
,
NULL
,
ArgPassSpec
(
3
),
descr
,
obj
,
val
,
NULL
,
NULL
);
}
}
else
{
r_descr
.
ensureDoneUsing
();
r_set
.
ensureDoneUsing
();
obj
->
setattr
(
attr
,
val
,
rewrite_args
);
}
}
extern
"C"
void
setattr
(
Box
*
obj
,
const
char
*
attr
,
Box
*
attr_val
)
{
assert
(
strcmp
(
attr
,
"__class__"
)
!=
0
);
...
...
@@ -1021,15 +1426,15 @@ extern "C" void setattr(Box* obj, const char* attr, Box* attr_val) {
if
(
rewriter
.
get
())
{
// rewriter->trap();
SetattrRewriteArgs
rewrite_args
(
rewriter
.
get
(),
rewriter
->
getArg
(
0
),
rewriter
->
getArg
(
2
),
false
);
obj
->
setattr
(
attr
,
attr_val
,
&
rewrite_args
);
setattr_internal
(
obj
,
attr
,
attr_val
,
&
rewrite_args
);
if
(
rewrite_args
.
out_success
)
{
rewriter
->
commit
();
}
else
{
rewrite_args
.
obj
.
set
DoneUsing
();
rewrite_args
.
attrval
.
set
DoneUsing
();
rewrite_args
.
obj
.
ensure
DoneUsing
();
rewrite_args
.
attrval
.
ensure
DoneUsing
();
}
}
else
{
obj
->
setattr
(
attr
,
attr_val
,
NULL
);
setattr_internal
(
obj
,
attr
,
attr_val
,
NULL
);
}
}
...
...
@@ -1101,7 +1506,9 @@ extern "C" bool nonzero(Box* obj) {
// int id = Stats::getStatId("slowpath_nonzero_" + *getTypeName(obj));
// Stats::log(id);
// go through descriptor logic
Box
*
func
=
getclsattr_internal
(
obj
,
"__nonzero__"
,
NULL
);
if
(
func
==
NULL
)
{
RELEASE_ASSERT
(
isUserDefined
(
obj
->
cls
),
"%s.__nonzero__"
,
getTypeName
(
obj
)
->
c_str
());
// TODO
return
true
;
...
...
@@ -1188,7 +1595,9 @@ extern "C" BoxedInt* hash(Box* obj) {
static
StatCounter
slowpath_hash
(
"slowpath_hash"
);
slowpath_hash
.
log
();
// goes through descriptor logic
Box
*
hash
=
getclsattr_internal
(
obj
,
"__hash__"
,
NULL
);
if
(
hash
==
NULL
)
{
ASSERT
(
isUserDefined
(
obj
->
cls
),
"%s.__hash__"
,
getTypeName
(
obj
)
->
c_str
());
// TODO not the best way to handle this...
...
...
@@ -1318,21 +1727,6 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
const
std
::
vector
<
const
std
::
string
*>*
keyword_names
)
{
int
npassed_args
=
argspec
.
totalPassed
();
// if (rewrite_args) {
// if (VERBOSITY()) {
// printf("callattrInternal: %d", rewrite_args->obj.getArgnum());
// if (npassed_args >= 1) printf(" %d", rewrite_args->arg1.getArgnum());
// if (npassed_args >= 2) printf(" %d", rewrite_args->arg2.getArgnum());
// if (npassed_args >= 3) printf(" %d", rewrite_args->arg3.getArgnum());
// if (npassed_args >= 4) printf(" %d", rewrite_args->args.getArgnum());
// printf("\n");
//}
// if (rewrite_args->obj.getArgnum() == -1) {
// rewrite_args->rewriter->trap();
// rewrite_args->obj = rewrite_args->obj.move(-3);
// }
// }
if
(
rewrite_args
&&
!
rewrite_args
->
args_guarded
)
{
// TODO duplication with runtime_call
// TODO should know which args don't need to be guarded, ex if we're guaranteed that they
...
...
@@ -1358,90 +1752,41 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
}
}
// right now I don't think this is ever called with INST_ONLY?
assert
(
scope
!=
INST_ONLY
);
if
(
checkInst
(
scope
))
{
Box
*
inst_attr
;
RewriterVarUsage
r_instattr
(
RewriterVarUsage
::
empty
());
if
(
rewrite_args
)
{
GetattrRewriteArgs
ga_rewrite_args
(
rewrite_args
->
rewriter
,
rewrite_args
->
obj
.
addUse
(),
rewrite_args
->
destination
,
true
);
inst_attr
=
getattr_internal
(
obj
,
*
attr
,
false
,
true
,
&
ga_rewrite_args
);
if
(
!
ga_rewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
{
if
(
inst_attr
)
{
r_instattr
=
std
::
move
(
ga_rewrite_args
.
out_rtn
);
}
}
}
else
{
inst_attr
=
getattr_internal
(
obj
,
*
attr
,
false
,
true
,
NULL
);
}
if
(
inst_attr
)
{
Box
*
rtn
;
if
(
inst_attr
->
cls
!=
function_cls
)
{
rewrite_args
=
NULL
;
}
if
(
rewrite_args
)
{
rewrite_args
->
args_guarded
=
true
;
r_instattr
.
addGuard
((
intptr_t
)
inst_attr
);
r_instattr
.
setDoneUsing
();
rewrite_args
->
func_guarded
=
true
;
rtn
=
runtimeCallInternal
(
inst_attr
,
rewrite_args
,
argspec
,
arg1
,
arg2
,
arg3
,
args
,
keyword_names
);
}
else
{
rtn
=
runtimeCallInternal
(
inst_attr
,
NULL
,
argspec
,
arg1
,
arg2
,
arg3
,
args
,
keyword_names
);
}
if
(
!
rtn
)
{
raiseExcHelper
(
TypeError
,
"'%s' object is not callable"
,
getTypeName
(
inst_attr
)
->
c_str
());
}
r_instattr
.
ensureDoneUsing
();
return
rtn
;
}
r_instattr
.
ensureDoneUsing
();
}
Box
*
clsattr
=
NULL
;
RewriterVarUsage
r_clsattr
(
RewriterVarUsage
::
empty
());
if
(
checkClass
(
scope
))
{
// Look up the argument. Pass in the arguments to getattrInternalGeneral or getclsattr_general
// that will shortcut functions by not putting them into instancemethods
bool
should_bind
;
Box
*
val
;
RewriterVarUsage
r_val
(
RewriterVarUsage
::
empty
());
if
(
rewrite_args
)
{
RewriterVarUsage
r_cls
=
std
::
move
(
rewrite_args
->
obj
.
getAttr
(
BOX_CLS_OFFSET
,
RewriterVarUsage
::
KillFlag
::
NoKill
,
Location
::
any
()));
GetattrRewriteArgs
ga_rewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_cls
),
rewrite_args
->
destination
,
true
);
// r_cls.assertValid();
clsattr
=
typeLookup
(
obj
->
cls
,
*
attr
,
&
ga_rewrite_args
);
if
(
!
ga_rewrite_args
.
out_success
)
{
GetattrRewriteArgs
grewrite_args
(
rewrite_args
->
rewriter
,
rewrite_args
->
obj
.
addUse
(),
Location
::
any
(),
true
);
val
=
getattrInternalGeneral
(
obj
,
*
attr
,
&
grewrite_args
,
scope
==
CLASS_ONLY
,
true
,
&
should_bind
);
if
(
!
grewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
}
else
{
if
(
clsattr
)
r_clsattr
=
std
::
move
(
ga_rewrite_args
.
out_rtn
);
}
else
if
(
val
)
{
r_val
=
std
::
move
(
grewrite_args
.
out_rtn
);
}
}
else
{
clsattr
=
typeLookup
(
obj
->
cls
,
*
attr
,
NULL
);
}
val
=
getattrInternalGeneral
(
obj
,
*
attr
,
NULL
,
scope
==
CLASS_ONLY
,
true
,
&
should_bind
);
}
if
(
!
clsattr
)
{
if
(
val
==
NULL
)
{
if
(
rewrite_args
)
{
rewrite_args
->
ensureAllDone
();
rewrite_args
->
arg1
.
ensureDoneUsing
();
rewrite_args
->
arg2
.
ensureDoneUsing
();
rewrite_args
->
arg3
.
ensureDoneUsing
();
rewrite_args
->
args
.
ensureDoneUsing
();
rewrite_args
->
out_success
=
true
;
rewrite_args
->
obj
.
setDoneUsing
();
}
return
NULL
;
return
val
;
}
if
(
clsattr
->
cls
==
function_cls
)
{
if
(
should_bind
)
{
if
(
rewrite_args
)
{
r_
clsattr
.
addGuard
((
int64_t
)
clsattr
);
r_
val
.
addGuard
((
int64_t
)
val
);
}
// TODO copy from runtimeCall
...
...
@@ -1450,7 +1795,7 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
if
(
npassed_args
<=
2
)
{
Box
*
rtn
;
if
(
rewrite_args
)
{
CallRewriteArgs
srewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_
clsattr
),
rewrite_args
->
destination
,
CallRewriteArgs
srewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_
val
),
rewrite_args
->
destination
,
rewrite_args
->
more_guards_after
);
srewrite_args
.
arg1
=
std
::
move
(
rewrite_args
->
obj
);
...
...
@@ -1463,9 +1808,8 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
srewrite_args
.
func_guarded
=
true
;
srewrite_args
.
args_guarded
=
true
;
rtn
=
runtimeCallInternal
(
clsattr
,
&
srewrite_args
,
ArgPassSpec
(
argspec
.
num_args
+
1
,
argspec
.
num_keywords
,
argspec
.
has_starargs
,
argspec
.
has_kwargs
),
rtn
=
runtimeCallInternal
(
val
,
&
srewrite_args
,
ArgPassSpec
(
argspec
.
num_args
+
1
,
argspec
.
num_keywords
,
argspec
.
has_starargs
,
argspec
.
has_kwargs
),
obj
,
arg1
,
arg2
,
NULL
,
keyword_names
);
if
(
!
srewrite_args
.
out_success
)
{
...
...
@@ -1474,7 +1818,7 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
rewrite_args
->
out_rtn
=
std
::
move
(
srewrite_args
.
out_rtn
);
}
}
else
{
rtn
=
runtimeCallInternal
(
clsattr
,
NULL
,
ArgPassSpec
(
argspec
.
num_args
+
1
,
argspec
.
num_keywords
,
rtn
=
runtimeCallInternal
(
val
,
NULL
,
ArgPassSpec
(
argspec
.
num_args
+
1
,
argspec
.
num_keywords
,
argspec
.
has_starargs
,
argspec
.
has_kwargs
),
obj
,
arg1
,
arg2
,
NULL
,
keyword_names
);
}
...
...
@@ -1491,49 +1835,7 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
Box
*
rtn
;
if
(
rewrite_args
)
{
// const bool annotate = 0;
// if (annotate)
// rewrite_args->rewriter->trap();
// if (VERBOSITY()) printf("have to remunge: %d %d %d %d\n", rewrite_args->arg1.getArgnum(),
// rewrite_args->arg2.getArgnum(), rewrite_args->arg3.getArgnum(), rewrite_args->args.getArgnum());
// The above line seems to print one of:
// 4 5 6 7
// 2 3 4 5
// Want to move them to
// 1 2 X X
// if (npassed_args >= 1) rewrite_args->arg1 = rewrite_args->arg1.move(1);
// if (npassed_args >= 2) rewrite_args->arg2 = rewrite_args->arg2.move(2);
// if (npassed_args >= 3) rewrite_args->arg3 = rewrite_args->arg3.move(4);
// if (npassed_args >= 4) rewrite_args->args = rewrite_args->args.move(5);
// int new_alloca_reg = -3;
// RewriterVar r_new_args = rewrite_args->rewriter->alloca_(alloca_size, new_alloca_reg);
// r_clsattr.push();
// if (rewrite_args->arg3.isInReg())
// r_new_args.setAttr(0, rewrite_args->arg3, /* user_visible = */ false);
// else {
// r_new_args.setAttr(0, rewrite_args->arg3.move(-2), /* user_visible = */ false);
//}
// arg3 is now dead
// for (int i = 0; i < npassed_args - 3; i++) {
// RewriterVar arg;
// if (rewrite_args->args.isInReg())
// arg = rewrite_args->args.getAttr(i * sizeof(Box*), -2);
// else {
// // TODO this is really bad:
// arg = rewrite_args->args.move(-2).getAttr(i * sizeof(Box*), -2);
// }
// r_new_args.setAttr((i + 1) * sizeof(Box*), arg, /* user_visible = */ false);
//}
// args is now dead
// RewriterVarUsage r_new_args = rewrite_args->alloc
CallRewriteArgs
srewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_clsattr
),
rewrite_args
->
destination
,
CallRewriteArgs
srewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_val
),
rewrite_args
->
destination
,
rewrite_args
->
more_guards_after
);
srewrite_args
.
arg1
=
std
::
move
(
rewrite_args
->
obj
);
srewrite_args
.
arg2
=
std
::
move
(
rewrite_args
->
arg1
);
...
...
@@ -1545,14 +1847,9 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
srewrite_args
.
args_guarded
=
true
;
srewrite_args
.
func_guarded
=
true
;
// if (annotate)
// rewrite_args->rewriter->annotate(0);
rtn
=
runtimeCallInternal
(
clsattr
,
&
srewrite_args
,
ArgPassSpec
(
argspec
.
num_args
+
1
,
argspec
.
num_keywords
,
argspec
.
has_starargs
,
argspec
.
has_kwargs
),
rtn
=
runtimeCallInternal
(
val
,
&
srewrite_args
,
ArgPassSpec
(
argspec
.
num_args
+
1
,
argspec
.
num_keywords
,
argspec
.
has_starargs
,
argspec
.
has_kwargs
),
obj
,
arg1
,
arg2
,
new_args
,
keyword_names
);
// if (annotate)
// rewrite_args->rewriter->annotate(1);
if
(
!
srewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
...
...
@@ -1561,10 +1858,8 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
rewrite_args
->
out_success
=
true
;
}
// if (annotate)
// rewrite_args->rewriter->annotate(2);
}
else
{
rtn
=
runtimeCallInternal
(
clsattr
,
NULL
,
ArgPassSpec
(
argspec
.
num_args
+
1
,
argspec
.
num_keywords
,
rtn
=
runtimeCallInternal
(
val
,
NULL
,
ArgPassSpec
(
argspec
.
num_args
+
1
,
argspec
.
num_keywords
,
argspec
.
has_starargs
,
argspec
.
has_kwargs
),
obj
,
arg1
,
arg2
,
new_args
,
keyword_names
);
}
...
...
@@ -1576,18 +1871,13 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
}
Box
*
rtn
;
if
(
clsattr
->
cls
!=
function
_cls
)
{
if
(
val
->
cls
!=
function_cls
&&
val
->
cls
!=
instancemethod
_cls
)
{
rewrite_args
=
NULL
;
r_
clsattr
.
ensureDoneUsing
();
r_
val
.
ensureDoneUsing
();
}
auto
old_clsattr
=
clsattr
;
clsattr
=
_handleClsAttr
(
obj
,
clsattr
);
if
(
clsattr
!=
old_clsattr
)
rewrite_args
=
NULL
;
if
(
rewrite_args
)
{
CallRewriteArgs
srewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_
clsattr
),
rewrite_args
->
destination
,
CallRewriteArgs
srewrite_args
(
rewrite_args
->
rewriter
,
std
::
move
(
r_
val
),
rewrite_args
->
destination
,
rewrite_args
->
more_guards_after
);
if
(
npassed_args
>=
1
)
srewrite_args
.
arg1
=
std
::
move
(
rewrite_args
->
arg1
);
...
...
@@ -1599,7 +1889,7 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
srewrite_args
.
args
=
std
::
move
(
rewrite_args
->
args
);
srewrite_args
.
args_guarded
=
true
;
rtn
=
runtimeCallInternal
(
clsattr
,
&
srewrite_args
,
argspec
,
arg1
,
arg2
,
arg3
,
args
,
keyword_names
);
rtn
=
runtimeCallInternal
(
val
,
&
srewrite_args
,
argspec
,
arg1
,
arg2
,
arg3
,
args
,
keyword_names
);
if
(
!
srewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
...
...
@@ -1607,11 +1897,11 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
rewrite_args
->
out_rtn
=
std
::
move
(
srewrite_args
.
out_rtn
);
}
}
else
{
rtn
=
runtimeCallInternal
(
clsattr
,
NULL
,
argspec
,
arg1
,
arg2
,
arg3
,
args
,
keyword_names
);
rtn
=
runtimeCallInternal
(
val
,
NULL
,
argspec
,
arg1
,
arg2
,
arg3
,
args
,
keyword_names
);
}
if
(
!
rtn
)
{
raiseExcHelper
(
TypeError
,
"'%s' object is not callable"
,
getTypeName
(
clsattr
)
->
c_str
());
raiseExcHelper
(
TypeError
,
"'%s' object is not callable"
,
getTypeName
(
val
)
->
c_str
());
}
if
(
rewrite_args
)
...
...
@@ -2117,8 +2407,6 @@ Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, ArgPassSpec ar
Box
**
args
,
const
std
::
vector
<
const
std
::
string
*>*
keyword_names
)
{
int
npassed_args
=
argspec
.
totalPassed
();
Box
*
orig_obj
=
obj
;
if
(
obj
->
cls
!=
function_cls
&&
obj
->
cls
!=
instancemethod_cls
)
{
Box
*
rtn
;
if
(
rewrite_args
)
{
...
...
@@ -2178,6 +2466,26 @@ Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, ArgPassSpec ar
rewrite_args
->
obj
.
addAttrGuard
(
INSTANCEMETHOD_FUNC_OFFSET
,
(
intptr_t
)
im
->
func
);
}
// Guard on which type of instancemethod (bound or unbound)
// That is, if im->obj is NULL, guard on it being NULL
// otherwise, guard on it being non-NULL
if
(
rewrite_args
)
{
rewrite_args
->
obj
.
addAttrGuard
(
INSTANCEMETHOD_OBJ_OFFSET
,
0
,
im
->
obj
!=
NULL
);
}
// TODO guard on im->obj being NULL or not
if
(
im
->
obj
==
NULL
)
{
Box
*
f
=
im
->
func
;
if
(
rewrite_args
)
{
rewrite_args
->
func_guarded
=
true
;
rewrite_args
->
args_guarded
=
true
;
rewrite_args
->
obj
=
rewrite_args
->
obj
.
getAttr
(
INSTANCEMETHOD_FUNC_OFFSET
,
RewriterVarUsage
::
Kill
,
Location
::
any
());
}
Box
*
res
=
runtimeCallInternal
(
f
,
rewrite_args
,
argspec
,
arg1
,
arg2
,
arg3
,
args
,
keyword_names
);
return
res
;
}
if
(
npassed_args
<=
2
)
{
Box
*
rtn
;
if
(
rewrite_args
)
{
...
...
@@ -2245,8 +2553,6 @@ extern "C" Box* runtimeCall(Box* obj, ArgPassSpec argspec, Box* arg1, Box* arg2,
Box
*
rtn
;
if
(
rewriter
.
get
())
{
// rewriter->trap();
// TODO feel weird about doing this; it either isn't necessary
// or this kind of thing is necessary in a lot more places
// rewriter->getArg(1).addGuard(npassed_args);
...
...
@@ -2283,8 +2589,6 @@ extern "C" Box* binopInternal(Box* lhs, Box* rhs, int op_type, bool inplace, Bin
// of the other!
if
(
rewrite_args
)
{
// rewriter->trap();
// TODO probably don't need to guard on the lhs_cls since it
// will get checked no matter what, but the check that should be
// removed is probably the later one.
...
...
@@ -2304,9 +2608,9 @@ extern "C" Box* binopInternal(Box* lhs, Box* rhs, int op_type, bool inplace, Bin
srewrite_args
.
args_guarded
=
true
;
irtn
=
callattrInternal1
(
lhs
,
&
iop_name
,
CLASS_ONLY
,
&
srewrite_args
,
ArgPassSpec
(
1
),
rhs
);
if
(
!
srewrite_args
.
out_success
)
if
(
!
srewrite_args
.
out_success
)
{
rewrite_args
=
NULL
;
else
if
(
irtn
)
{
}
else
if
(
irtn
)
{
if
(
irtn
==
NotImplemented
)
srewrite_args
.
out_rtn
.
ensureDoneUsing
();
else
...
...
@@ -2457,15 +2761,15 @@ extern "C" Box* augbinop(Box* lhs, Box* rhs, int op_type) {
Box
*
rtn
;
if
(
rewriter
.
get
())
{
// rewriter->trap();
BinopRewriteArgs
rewrite_args
(
rewriter
.
get
(),
rewriter
->
getArg
(
0
),
rewriter
->
getArg
(
1
),
rewriter
->
getReturnDestination
(),
false
);
rtn
=
binopInternal
(
lhs
,
rhs
,
op_type
,
true
,
&
rewrite_args
);
if
(
!
rewrite_args
.
out_success
)
{
rewrite_args
.
ensureAllDone
();
rewriter
.
reset
(
NULL
);
}
else
}
else
{
rewriter
->
commitReturning
(
std
::
move
(
rewrite_args
.
out_rtn
));
}
}
else
{
rtn
=
binopInternal
(
lhs
,
rhs
,
op_type
,
true
,
NULL
);
}
...
...
@@ -2805,10 +3109,10 @@ extern "C" void delattr_internal(Box* obj, const std::string& attr, bool allow_c
}
}
// first check wether the deleting attribute is a descriptor
// first check w
h
ether the deleting attribute is a descriptor
Box
*
clsAttr
=
typeLookup
(
obj
->
cls
,
attr
,
NULL
);
if
(
clsAttr
!=
NULL
)
{
Box
*
delAttr
=
getattr_internal
(
clsAttr
,
delete_str
,
false
,
true
,
NULL
);
Box
*
delAttr
=
typeLookup
(
static_cast
<
BoxedClass
*>
(
clsAttr
->
cls
),
delete_str
,
NULL
);
if
(
delAttr
!=
NULL
)
{
Box
*
boxstr
=
boxString
(
attr
);
...
...
@@ -2818,7 +3122,7 @@ extern "C" void delattr_internal(Box* obj, const std::string& attr, bool allow_c
}
// check if the attribute is in the instance's __dict__
Box
*
attrVal
=
getattr_internal
(
obj
,
attr
,
false
,
false
,
NULL
);
Box
*
attrVal
=
obj
->
getattr
(
attr
,
NULL
);
if
(
attrVal
!=
NULL
)
{
obj
->
delattr
(
attr
,
NULL
);
}
else
{
...
...
src/runtime/objmodel.h
View file @
b594728e
...
...
@@ -102,8 +102,9 @@ extern "C" void delattr_internal(Box* obj, const std::string& attr, bool allow_c
DelattrRewriteArgs
*
rewrite_args
);
struct
CompareRewriteArgs
;
Box
*
compareInternal
(
Box
*
lhs
,
Box
*
rhs
,
int
op_type
,
CompareRewriteArgs
*
rewrite_args
);
Box
*
getattr_internal
(
Box
*
obj
,
const
std
::
string
&
attr
,
bool
check_cls
,
bool
allow_custom
,
GetattrRewriteArgs
*
rewrite_args
);
Box
*
getattrInternal
(
Box
*
obj
,
const
std
::
string
&
attr
,
GetattrRewriteArgs
*
rewrite_args
);
Box
*
getattrInternalGeneral
(
Box
*
obj
,
const
std
::
string
&
attr
,
GetattrRewriteArgs
*
rewrite_args
,
bool
cls_only
,
bool
for_call
,
bool
*
should_bind_out
);
Box
*
typeLookup
(
BoxedClass
*
cls
,
const
std
::
string
&
attr
,
GetattrRewriteArgs
*
rewrite_args
);
...
...
src/runtime/types.cpp
View file @
b594728e
...
...
@@ -203,7 +203,9 @@ extern "C" void typeGCHandler(GCVisitor* v, Box* b) {
extern
"C"
void
instancemethodGCHandler
(
GCVisitor
*
v
,
Box
*
b
)
{
BoxedInstanceMethod
*
im
=
(
BoxedInstanceMethod
*
)
b
;
if
(
im
->
obj
)
{
v
->
visit
(
im
->
obj
);
}
v
->
visit
(
im
->
func
);
}
...
...
@@ -331,6 +333,10 @@ extern "C" Box* boxInstanceMethod(Box* obj, Box* func) {
return
new
BoxedInstanceMethod
(
obj
,
func
);
}
extern
"C"
Box
*
boxUnboundInstanceMethod
(
Box
*
func
)
{
return
new
BoxedInstanceMethod
(
NULL
,
func
);
}
extern
"C"
BoxedString
*
noneRepr
(
Box
*
v
)
{
return
new
BoxedString
(
"None"
);
}
...
...
@@ -412,6 +418,27 @@ Box* instancemethodRepr(BoxedInstanceMethod* self) {
return
boxStrConstant
(
"<unbound instancemethod object>"
);
}
Box
*
instancemethodEq
(
BoxedInstanceMethod
*
self
,
Box
*
rhs
)
{
if
(
rhs
->
cls
!=
instancemethod_cls
)
{
return
boxBool
(
false
);
}
BoxedInstanceMethod
*
rhs_im
=
static_cast
<
BoxedInstanceMethod
*>
(
rhs
);
if
(
self
->
func
==
rhs_im
->
func
)
{
if
(
self
->
obj
==
NULL
&&
rhs_im
->
obj
==
NULL
)
{
return
boxBool
(
true
);
}
else
{
if
(
self
->
obj
!=
NULL
&&
rhs_im
->
obj
!=
NULL
)
{
return
compareInternal
(
self
->
obj
,
rhs_im
->
obj
,
AST_TYPE
::
Eq
,
NULL
);
}
else
{
return
boxBool
(
false
);
}
}
}
else
{
return
boxBool
(
false
);
}
}
Box
*
sliceRepr
(
BoxedSlice
*
self
)
{
BoxedString
*
start
=
static_cast
<
BoxedString
*>
(
repr
(
self
->
start
));
BoxedString
*
stop
=
static_cast
<
BoxedString
*>
(
repr
(
self
->
stop
));
...
...
@@ -701,6 +728,7 @@ void setupRuntime() {
instancemethod_cls
->
giveAttr
(
"__name__"
,
boxStrConstant
(
"instancemethod"
));
instancemethod_cls
->
giveAttr
(
"__repr__"
,
new
BoxedFunction
(
boxRTFunction
((
void
*
)
instancemethodRepr
,
STR
,
1
)));
instancemethod_cls
->
giveAttr
(
"__eq__"
,
new
BoxedFunction
(
boxRTFunction
((
void
*
)
instancemethodEq
,
UNKNOWN
,
2
)));
instancemethod_cls
->
freeze
();
slice_cls
->
giveAttr
(
"__name__"
,
boxStrConstant
(
"slice"
));
...
...
src/runtime/types.h
View file @
b594728e
...
...
@@ -90,6 +90,7 @@ extern "C" Box* boxInt(i64);
extern
"C"
i64
unboxInt
(
Box
*
);
extern
"C"
Box
*
boxFloat
(
double
d
);
extern
"C"
Box
*
boxInstanceMethod
(
Box
*
obj
,
Box
*
func
);
extern
"C"
Box
*
boxUnboundInstanceMethod
(
Box
*
func
);
extern
"C"
Box
*
boxStringPtr
(
const
std
::
string
*
s
);
Box
*
boxString
(
const
std
::
string
&
s
);
Box
*
boxString
(
std
::
string
&&
s
);
...
...
@@ -212,6 +213,7 @@ public:
class
BoxedInstanceMethod
:
public
Box
{
public:
// obj is NULL for unbound instancemethod
Box
*
obj
,
*
func
;
BoxedInstanceMethod
(
Box
*
obj
,
Box
*
func
)
__attribute__
((
visibility
(
"default"
)))
...
...
@@ -331,7 +333,8 @@ public:
int
offset
;
BoxedMemberDescriptor
(
MemberType
type
,
int
offset
)
:
Box
(
member_cls
),
type
(
type
),
offset
(
offset
)
{}
BoxedMemberDescriptor
(
PyMemberDef
*
member
)
:
Box
(
member_cls
),
type
((
MemberType
)
member
->
type
),
offset
(
member
->
offset
)
{}
BoxedMemberDescriptor
(
PyMemberDef
*
member
)
:
Box
(
member_cls
),
type
((
MemberType
)
member
->
type
),
offset
(
member
->
offset
)
{}
};
// TODO is there any particular reason to make this a Box, ie a python-level object?
...
...
test/tests/class_noctor.py
View file @
b594728e
# expected: fail
# Regression test:
# If the init function doesn't exist, shouldn't just silently ignore any args
# that got passed
...
...
test/tests/data_descriptors.py
0 → 100644
View file @
b594728e
class
DataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
type
):
print
"__get__ called"
if
obj
!=
None
:
return
obj
.
a_store
def
__set__
(
self
,
obj
,
value
):
print
"__set__ called with value"
,
value
obj
.
a_store
=
value
+
1
class
NonDataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
type
):
return
2
# Don't define __set__
class
C
(
object
):
dd
=
DataDescriptor
()
ndd
=
NonDataDescriptor
()
inst
=
C
()
print
'ndd is %s'
%
str
(
inst
.
ndd
)
inst
.
dd
=
14
print
'dd is %s'
%
str
(
inst
.
dd
)
inst
.
ndd
=
20
print
inst
.
ndd
# should print out 20, having overridden the NonDataDescriptor
inst
.
dd2
=
99
C
.
dd2
=
DataDescriptor
()
print
'inst.dd2 is %s'
%
str
(
inst
.
dd2
)
print
'C.dd is %s'
%
str
(
C
.
dd
)
print
'C.ndd is %s'
%
str
(
C
.
ndd
)
C
.
dd
=
6
C
.
ndd
=
7
#TODO it would be nice to print these out (once __dict__ is implemented)
#print C.__dict__['dd']
#print C.__dict__['ndd']
print
c
.
dd
print
c
.
ndd
# Repeat all of the above for subclasses of the descriptors
class
SubDataDescriptor
(
DataDescriptor
):
pass
class
SubNonDataDescriptor
(
NonDataDescriptor
):
pass
class
D
(
object
):
dd
=
SubDataDescriptor
()
ndd
=
SubNonDataDescriptor
()
inst
=
D
()
print
'ndd is %d'
%
inst
.
ndd
inst
.
dd
=
14
print
'dd is %d'
%
inst
.
dd
inst
.
ndd
=
20
print
inst
.
ndd
# should print out 20, having overridden the NonDataDescriptor
inst
.
dd2
=
99
C
.
dd2
=
DataDescriptor
()
print
'inst.dd2 is %s'
%
str
(
inst
.
dd2
)
print
'D.dd is %s'
%
str
(
D
.
dd
)
print
'D.ndd is %s'
%
str
(
D
.
ndd
)
D
.
dd
=
6
D
.
ndd
=
7
#print D.__dict__['dd']
#print D.__dict__['ndd']
class
DataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
type
):
return
1
def
__set__
(
self
,
obj
,
value
):
pass
class
NonDataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
type
):
return
2
# Don't define __set__
class
C1
(
object
):
a
=
DataDescriptor
()
class
D1
(
C1
):
a
=
3
d1
=
D1
()
print
d1
.
a
print
'D1.a is %s'
%
str
(
D1
.
a
)
D1
.
a
=
6
#print D1.__dict__['a']
class
C2
(
object
):
a
=
4
class
D2
(
C2
):
a
=
DataDescriptor
()
d2
=
D2
()
print
d2
.
a
print
'D2.a is %s'
%
str
(
D2
.
a
)
D2
.
a
=
6
#print D2.__dict__['a']
class
C3
(
object
):
a
=
NonDataDescriptor
()
class
D3
(
C3
):
a
=
5
d3
=
D3
()
print
d3
.
a
print
'D3.a is %s'
%
str
(
D3
.
a
)
D3
.
a
=
6
#print D3.__dict__['a']
class
C4
(
object
):
a
=
6
class
D4
(
C4
):
a
=
NonDataDescriptor
()
d4
=
D4
()
#print d4.a
print
'D4.a is %s'
%
str
(
D4
.
a
)
D4
.
a
=
6
#print D4.__dict__['a']
test/tests/data_descriptors_calling.py
0 → 100644
View file @
b594728e
class
DataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
type
):
if
obj
!=
None
:
return
obj
.
a_store
else
:
def
f
():
print
'__get__ function called'
return
100
return
f
def
__set__
(
self
,
obj
,
value
):
obj
.
a_store
=
value
class
NonDataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
type
):
def
f
():
print
'__get__ function called'
return
1
return
f
# Don't define __set__
class
C
(
object
):
dd
=
DataDescriptor
()
ndd
=
NonDataDescriptor
()
inst
=
C
()
print
'ndd() is %d'
%
inst
.
ndd
()
inst
.
dd
=
lambda
:
14
print
'dd() is %d'
%
inst
.
dd
()
inst
.
ndd
=
lambda
:
20
print
inst
.
ndd
()
# should print out 20, having overridden the NonDataDescriptor
inst
.
dd2
=
lambda
:
99
C
.
dd2
=
DataDescriptor
()
print
'inst.dd2 is %s'
%
str
(
inst
.
dd2
())
print
'C.dd() is %s'
%
str
(
C
.
dd
())
print
'C.ndd() is %s'
%
str
(
C
.
ndd
())
C
.
dd
=
lambda
:
6
C
.
ndd
=
lambda
:
7
#TODO uncomment these
#print C.__dict__['dd']()
#print C.__dict__['ndd']()
# Repeat all of the above for subclasses of the descriptors
class
SubDataDescriptor
(
DataDescriptor
):
pass
class
SubNonDataDescriptor
(
NonDataDescriptor
):
pass
class
D
(
object
):
dd
=
SubDataDescriptor
()
ndd
=
SubNonDataDescriptor
()
inst
=
D
()
print
'ndd() is %d'
%
inst
.
ndd
()
inst
.
dd
=
lambda
:
14
print
'dd() is %d'
%
inst
.
dd
()
inst
.
ndd
=
lambda
:
20
print
inst
.
ndd
()
# should print out 20, having overridden the NonDataDescriptor
inst
.
dd2
=
lambda
:
99
C
.
dd2
=
DataDescriptor
()
print
'inst.dd2 is %s'
%
str
(
inst
.
dd2
())
print
'D.dd() is %s'
%
str
(
D
.
dd
())
print
'D.ndd() is %s'
%
str
(
D
.
ndd
())
D
.
dd
=
lambda
:
6
D
.
ndd
=
lambda
:
7
#print D.__dict__['dd']()
#print D.__dict__['ndd']()
class
DataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
type
):
return
(
lambda
:
1
)
def
__set__
(
self
,
obj
,
value
):
pass
class
NonDataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
type
):
return
(
lambda
:
2
)
# Don't define __set__
class
C1
(
object
):
a
=
DataDescriptor
()
class
D1
(
C1
):
a
=
lambda
self
:
3
d1
=
D1
()
print
d1
.
a
()
print
'D1.a() is %s'
%
str
(
D1
.
a
(
d1
))
D1
.
a
=
lambda
:
6
#print D1.__dict__['a']()
class
C2
(
object
):
a
=
lambda
self
:
4
class
D2
(
C2
):
a
=
DataDescriptor
()
d2
=
D2
()
print
d2
.
a
()
print
'D2.a() is %s'
%
str
(
D2
.
a
())
D2
.
a
=
lambda
:
6
#print D2.__dict__['a']()
class
C3
(
object
):
a
=
NonDataDescriptor
()
class
D3
(
C3
):
a
=
lambda
self
:
5
d3
=
D3
()
print
d3
.
a
()
print
'D3.a() is %s'
%
str
(
D3
.
a
(
d3
))
D3
.
a
=
lambda
:
6
#print D3.__dict__['a']()
class
C4
(
object
):
a
=
lambda
self
:
6
class
D4
(
C4
):
a
=
NonDataDescriptor
()
d4
=
D4
()
print
d4
.
a
()
print
'D4.a() is %s'
%
str
(
D4
.
a
())
D4
.
a
=
lambda
:
6
#print D4.__dict__['a']()
test/tests/data_descriptors_classes.py
0 → 100644
View file @
b594728e
class
DataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
type
):
print
"__get__ called"
if
obj
!=
None
:
return
obj
.
a_store
def
__set__
(
self
,
obj
,
value
):
print
"__set__ called with value"
,
value
obj
.
a_store
=
value
+
1
class
NonDataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
type
):
return
2
# Don't define __set__
class
C
(
object
):
# see what happens when we just use the class of the descriptor rather than an instance
dd
=
DataDescriptor
ndd
=
NonDataDescriptor
inst
=
C
()
print
'ndd is %s'
%
str
(
inst
.
ndd
)
inst
.
dd
=
14
print
'dd is %s'
%
str
(
inst
.
dd
)
inst
.
ndd
=
20
print
inst
.
ndd
# should print out 20, having overridden the NonDataDescriptor
print
'C.dd is %s'
%
str
(
C
.
dd
)
print
'C.ndd is %s'
,
str
(
C
.
ndd
)
C
.
dd
=
6
C
.
ndd
=
7
#TODO uncomment these:
#print C.__dict__['dd']
#print C.__dict__['ndd']
test/tests/descriptors_callattr_cls_ics.py
0 → 100644
View file @
b594728e
# expected: statfail
# run_args: -n
# statcheck: stats['slowpath_callattr'] <= 100
# Right now this won't work because callattr involves two calls
# one call to __get__ and then another call to the returned function.
# Of course, if the callattr were split up into getattr and a call,
# each could be re-written separately...
# Not sure if this is a case worth handling or what is the best way
# to handle it, but I'm throwing the test in here anyway to remind us.
# Note: difference between DataDescriptor and NonDataDescriptor shouldn't matter
# __enter__ is looked up via callattr with class_only set to true
class
DataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
typ
):
print
'__get__ called'
print
type
(
self
)
print
type
(
obj
)
print
typ
def
enter
():
print
'enter called (1)'
return
enter
def
__set__
(
self
,
obj
,
value
):
pass
class
NonDataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
typ
):
print
'__get__ called'
print
type
(
self
)
print
type
(
obj
)
print
typ
def
enter
():
print
'enter called (2)'
return
enter
class
C1
(
object
):
__enter__
=
DataDescriptor
()
def
__exit__
(
self
,
typ
,
value
,
traceback
):
pass
class
C2
(
object
):
__enter__
=
NonDataDescriptor
()
def
__exit__
(
self
,
typ
,
value
,
traceback
):
pass
class
C3
(
object
):
__enter__
=
NonDataDescriptor
()
def
__exit__
(
self
,
typ
,
value
,
traceback
):
pass
class
C4
(
object
):
def
__exit__
(
self
,
typ
,
value
,
traceback
):
pass
c1
=
C1
()
c2
=
C2
()
c3
=
C3
()
c4
=
C4
()
def
enter3
(
type
):
print
'enter called (3)'
c3
.
__enter__
=
enter3
# this should not get called
def
enter4
(
type
):
print
'enter called (4)'
c4
.
__enter__
=
enter4
# this should not get called
C4
.
__enter__
=
DataDescriptor
()
def
f
():
with
c1
:
print
'in with statement (1)'
with
c2
:
print
'in with statement (2)'
with
c3
:
print
'in with statement (3)'
with
c4
:
print
'in with statement (4)'
for
i
in
xrange
(
1000
):
f
()
test/tests/descriptors_callattr_ics.py
0 → 100644
View file @
b594728e
# expected: statfail
# run_args: -n
# statcheck: stats['slowpath_callattr'] <= 80
# statcheck: stats['slowpath_getattr'] <= 80
# Right now this won't work because callattr involves two calls
# one call to __get__ and then another call to the returned function.
# Of course, if the callattr were split up into getattr and a call,
# each could be re-written separately...
# Not sure if this is a case worth handling or what is the best way
# to handle it, but I'm throwing the test in here anyway to remind us.
def
g
():
print
'in g'
return
0
def
h
():
print
'in h'
return
1
class
DataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
typ
):
print
'__get__ called'
print
type
(
self
)
print
type
(
obj
)
print
typ
return
g
def
__set__
(
self
,
obj
,
value
):
pass
class
NonDataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
typ
):
print
'__get__ called'
print
type
(
self
)
print
type
(
obj
)
print
typ
return
g
class
C
(
object
):
a
=
DataDescriptor
()
b
=
NonDataDescriptor
()
c
=
NonDataDescriptor
()
inst
=
C
()
inst
.
c
=
h
inst
.
d
=
h
C
.
d
=
DataDescriptor
()
def
f
():
print
inst
.
a
()
print
inst
.
b
()
print
inst
.
c
()
print
inst
.
d
()
print
C
.
a
()
print
C
.
b
()
for
i
in
xrange
(
1000
):
f
()
test/tests/descriptors_double.py
0 → 100644
View file @
b594728e
# TODO This is a hodgepodge of stuff, should probably organize it better
# maybe merge some of it into dunder_descriptors?
class
Descriptor2
(
object
):
def
__get__
(
self
,
obj
,
type
):
def
get2
(
self
,
obj
,
type
):
print
'__get__ called'
print
self
print
obj
print
type
return
get2
class
Descriptor
(
object
):
__get__
=
Descriptor2
()
class
DescriptorNonzero
(
object
):
def
__get__
(
self
,
obj
,
type
):
print
'nonzero __get__ called'
def
nonzero
():
print
'nonzero called'
return
True
return
nonzero
class
C
(
object
):
desc
=
Descriptor
()
__nonzero__
=
DescriptorNonzero
()
# Should throw type error: Descriptor2 is not callable
try
:
print
C
().
desc
except
TypeError
:
# TODO should print the whole stacktrace and error message probably
print
'got type error (1)'
# Should print True; in particular, it should look up __nonzero__
# using the descriptor protocol
if
C
():
print
'True'
else
:
print
'False'
# this should *not* override it
c
=
C
()
c
.
__nonzero__
=
lambda
x
:
False
if
c
:
print
'True'
else
:
print
'False'
# this should
C
.
__nonzero__
=
lambda
x
:
False
if
c
:
print
'True'
else
:
print
'False'
# __getattr__ and __setattr__
# Looks like __getattr__ and __setattr__ should *not* be looked up with
# the descriptor protocol
class
DescriptorGetattr
(
object
):
def
__get__
(
self
,
obj
,
type
):
print
'getattr __get__ called'
def
getattr
(
attr
):
print
'getattr called for attr'
,
attr
return
1337
return
getattr
class
DescriptorSetattr
(
object
):
def
__get__
(
self
,
obj
,
type
):
print
'setattr __get__ called'
def
setattr
(
attr
,
val
):
print
'setattr called for attr'
,
attr
,
val
class
D
(
object
):
__getattr__
=
DescriptorGetattr
__setattr__
=
DescriptorSetattr
d
=
D
()
try
:
print
d
.
a
except
TypeError
:
print
'got type error (2)'
#TODO enable this once __setattr__ is implemented
#try:
# d.b = 12
#except TypeError:
# print 'got type error (3)'
# with, __enter__ and __exit__
class
DescriptorEnter
(
object
):
def
__get__
(
self
,
obj
,
type
):
print
'enter __get__ called'
def
enter
():
print
'enter called'
return
1337
return
enter
class
DescriptorExit
(
object
):
def
__get__
(
self
,
obj
,
type
):
print
'exit __get__ called'
def
exit
(
type
,
value
,
traceback
):
print
'enter called'
return
1337
return
exit
class
E
(
object
):
__enter__
=
DescriptorEnter
()
__exit__
=
DescriptorExit
()
with
E
():
print
'in with'
# what if __get__ is just an instance attribute
class
Descriptor
(
object
):
pass
desc
=
Descriptor
()
desc
.
__get__
=
lambda
self
,
obj
,
type
:
5
def
s
(
self
,
obj
,
value
):
print
'in __set__'
desc
.
__set__
=
s
class
F
(
object
):
at
=
desc
f
=
F
()
print
type
(
f
.
at
)
# should not call __get__
f
.
at
=
12
# should not call __set__, should print nothing
#TODO uncomment this:
#print f.__dict__['at']
test/tests/descriptors_getattr_ics.py
0 → 100644
View file @
b594728e
# run_args: -n
# statcheck: stats['slowpath_getattr'] <= 80
class
DataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
typ
):
print
'__get__ called'
print
type
(
self
)
print
type
(
obj
)
print
typ
return
0
def
__set__
(
self
,
obj
,
value
):
pass
class
NonDataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
typ
):
print
'__get__ called'
print
type
(
self
)
print
type
(
obj
)
print
typ
return
1
class
C
(
object
):
a
=
DataDescriptor
()
b
=
NonDataDescriptor
()
c
=
NonDataDescriptor
()
inst
=
C
()
inst
.
c
=
100
inst
.
d
=
101
C
.
d
=
DataDescriptor
()
def
f
():
print
inst
.
a
print
inst
.
b
print
inst
.
c
print
inst
.
d
print
C
.
a
print
C
.
b
for
i
in
xrange
(
1000
):
f
()
test/tests/descriptors_getclsattr_ics.py
0 → 100644
View file @
b594728e
# run_args: -n
# statcheck: stats['slowpath_getclsattr'] <= 60
# Note: difference between DataDescriptor and NonDataDescriptor shouldn't matter
# for getclsattr (which is how __exit__ is looked up for with statements)
class
DataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
typ
):
print
'__get__ called'
print
type
(
self
)
print
type
(
obj
)
print
typ
def
exit
(
type
,
value
,
traceback
):
print
'exit called (1)'
return
exit
def
__set__
(
self
,
obj
,
value
):
pass
class
NonDataDescriptor
(
object
):
def
__get__
(
self
,
obj
,
typ
):
print
'__get__ called'
print
type
(
self
)
print
type
(
obj
)
print
typ
def
exit
(
type
,
value
,
traceback
):
print
'exit called (2)'
return
exit
class
C1
(
object
):
__exit__
=
DataDescriptor
()
def
__enter__
(
self
):
pass
class
C2
(
object
):
__exit__
=
NonDataDescriptor
()
def
__enter__
(
self
):
pass
class
C3
(
object
):
__exit__
=
NonDataDescriptor
()
def
__enter__
(
self
):
pass
class
C4
(
object
):
def
__enter__
(
self
):
pass
c1
=
C1
()
c2
=
C2
()
c3
=
C3
()
c4
=
C4
()
def
exit3
(
type
,
value
,
traceback
):
print
'exit called (3)'
c3
.
__exit__
=
exit3
# this should not get called
def
exit4
(
type
,
value
,
traceback
):
print
'exit called (4)'
c4
.
__exit__
=
exit4
# this should not get called
C4
.
__exit__
=
DataDescriptor
()
def
f
():
with
c1
:
print
'in with statement (1)'
with
c2
:
print
'in with statement (2)'
with
c3
:
print
'in with statement (3)'
with
c4
:
print
'in with statement (4)'
for
i
in
xrange
(
1000
):
f
()
test/tests/descriptors_guards.py
0 → 100644
View file @
b594728e
# All the ways of invalidating getattr
# TODO should test getclsattr as well
# TODO should also test some crazier stuff, like descriptors with inheritance
def
get
(
self
,
obj
,
typ
):
print
'__get__ called'
print
type
(
self
)
print
type
(
obj
)
print
typ
return
self
.
elem
def
set
(
self
,
obj
,
typ
):
print
'__get__ called'
print
type
(
self
)
print
type
(
obj
)
print
typ
class
Descriptor
(
object
):
def
__init__
(
self
,
elem
):
self
.
elem
=
elem
def
__str__
(
self
):
return
'Descriptor object'
class
C
(
object
):
a
=
Descriptor
(
0
)
b
=
Descriptor
(
lambda
:
0
)
c
=
C
()
def
f
():
print
c
.
a
print
C
.
a
def
g
():
try
:
print
c
.
b
()
except
TypeError
:
print
'got TypeError'
try
:
print
C
.
b
()
except
TypeError
:
print
'got TypeError'
def
h
():
c
.
c
=
10
for
i
in
xrange
(
2000
):
f
()
g
()
h
()
if
i
==
50
:
Descriptor
.
__get__
=
get
if
i
==
100
:
Descriptor
.
__set__
=
set
if
i
==
150
:
del
Descriptor
.
__get__
if
i
==
200
:
del
Descriptor
.
__set__
if
i
==
250
:
Descriptor
.
__set__
=
set
if
i
==
300
:
Descriptor
.
__get__
=
get
if
i
==
350
:
del
Descriptor
.
__set__
if
i
==
400
:
del
Descriptor
.
__get__
if
i
==
450
:
Descriptor
.
__get__
=
get
Descriptor
.
__set__
=
set
if
i
==
500
:
del
Descriptor
.
__get__
del
Descriptor
.
__set__
if
i
==
550
:
Descriptor
.
__get__
=
get
if
i
==
600
:
Descriptor
.
__set__
=
set
del
Descriptor
.
__get__
if
i
==
650
:
Descriptor
.
__get__
=
get
del
Descriptor
.
__set__
if
i
==
700
:
c
.
a
=
5
c
.
b
=
lambda
:
5
if
i
==
750
:
del
c
.
a
del
c
.
b
if
i
==
800
:
Descriptor
.
__set__
=
set
if
i
==
850
:
del
Descriptor
.
__set__
c
.
a
=
5
c
.
b
=
lambda
:
5
Descriptor
.
__set__
=
set
if
i
==
900
:
del
Descriptor
.
__set__
del
c
.
a
del
c
.
b
Descriptor
.
__set__
=
set
if
i
==
950
:
del
Descriptor
.
__get__
if
i
==
1000
:
del
Descriptor
.
__set__
c
.
a
=
5
c
.
b
=
lambda
:
5
Descriptor
.
__set__
=
set
if
i
==
1050
:
del
Descriptor
.
__set__
del
c
.
a
del
c
.
b
Descriptor
.
__set__
=
set
if
i
==
1100
:
del
Descriptor
.
__set__
if
i
==
1150
:
c
.
a
=
5
c
.
b
=
lambda
:
5
if
i
==
1200
:
del
c
.
a
del
c
.
b
if
i
==
1250
:
c
.
a
=
5
c
.
b
=
lambda
:
5
if
i
==
1350
:
Descriptor
.
__get__
=
get
if
i
==
1400
:
Descriptor
.
__set__
=
set
if
i
==
1450
:
del
Descriptor
.
__get__
if
i
==
1500
:
del
Descriptor
.
__set__
if
i
==
1550
:
Descriptor
.
__set__
=
set
if
i
==
1600
:
Descriptor
.
__get__
=
get
if
i
==
1650
:
del
Descriptor
.
__set__
if
i
==
1700
:
del
Descriptor
.
__get__
if
i
==
1750
:
Descriptor
.
__get__
=
get
Descriptor
.
__set__
=
set
if
i
==
1800
:
del
Descriptor
.
__get__
del
Descriptor
.
__set__
if
i
==
1850
:
Descriptor
.
__get__
=
get
if
i
==
1900
:
Descriptor
.
__set__
=
set
del
Descriptor
.
__get__
if
i
==
1950
:
Descriptor
.
__get__
=
get
del
Descriptor
.
__set__
test/tests/descriptors_nonfunc_types.py
0 → 100644
View file @
b594728e
# expected: fail
# See what happens when we make __get__ and __set__ things other than functions...
# TODO add some with __del__
import
traceback
class
CallableGet
(
object
):
def
__call__
(
self
,
a
,
b
,
c
):
print
'Callable get'
print
self
print
a
print
b
print
c
class
CallableSet
(
object
):
def
__call__
(
a
,
b
,
c
):
print
'Callable set'
print
a
print
b
print
c
class
InstanceMethodMaker
(
object
):
def
getBoundInstanceMethod
(
self
,
a
,
b
,
c
):
print
'__get__ bound'
print
a
print
b
print
c
def
setBoundInstanceMethod
(
a
,
b
,
c
):
print
'__set__ bound'
print
a
print
b
print
c
def
getUnboundInstanceMethod
(
a
,
b
,
c
):
print
'__get__ unbound'
print
a
print
b
print
c
def
setUnboundInstanceMethod
(
a
,
b
,
c
):
print
'__set__ unbound'
print
a
print
b
print
c
imm
=
InstanceMethodMaker
()
def
closureGet
():
a
=
5
def
f
(
b
,
c
,
d
):
print
'closure __get__'
print
a
print
b
print
c
print
d
return
f
def
closureSet
():
def
f
(
b
,
c
,
d
):
print
'closure __set__'
print
a
print
b
print
c
print
d
return
f
class
A
(
object
):
# If __get__ or __set__ is an int
class
DescGetInt
(
object
):
__get__
=
1
descGetInt
=
DescGetInt
()
class
DescSetInt
(
object
):
__set__
=
1
descSetInt
=
DescSetInt
()
class
DescGetSetInt
(
object
):
def
__get__
(
a
,
b
,
c
):
print
'DescGetSetInt __get__ called'
print
a
print
b
print
c
__set__
=
1
descGetSetInt
=
DescGetSetInt
()
class
DescGetCall
(
object
):
__get__
=
CallableGet
()
descGetCall
=
DescGetCall
()
class
DescSetCall
(
object
):
__set__
=
CallableSet
()
descSetCall
=
DescSetCall
()
class
DescGetSetCall
(
object
):
def
__get__
(
a
,
b
,
c
):
print
'DescGetSetCall __get__ called'
print
a
print
b
print
c
__set__
=
CallableSet
()
descGetSetCall
=
DescGetSetCall
()
class
DescGetBoundInstanceMethod
(
object
):
__get__
=
imm
.
getBoundInstanceMethod
descGetBoundInstanceMethod
=
DescGetBoundInstanceMethod
()
class
DescSetBoundInstanceMethod
(
object
):
__set__
=
imm
.
setBoundInstanceMethod
descSetBoundInstanceMethod
=
DescSetBoundInstanceMethod
()
class
DescGetSetBoundInstanceMethod
(
object
):
def
__get__
(
a
,
b
,
c
):
print
'DescGetSetBoundInstanceMethod __get__ called'
print
a
print
b
print
c
__set__
=
imm
.
setBoundInstanceMethod
descGetSetBoundInstanceMethod
=
DescGetSetBoundInstanceMethod
()
class
DescGetUnboundInstanceMethod
(
object
):
__get__
=
InstanceMethodMaker
.
getUnboundInstanceMethod
descGetUnboundInstanceMethod
=
DescGetUnboundInstanceMethod
()
class
DescSetUnboundInstanceMethod
(
object
):
__set__
=
InstanceMethodMaker
.
setUnboundInstanceMethod
descSetUnboundInstanceMethod
=
DescSetUnboundInstanceMethod
()
class
DescGetSetUnboundInstanceMethod
(
object
):
def
__get__
(
a
,
b
,
c
):
print
'DescGetSetUnboundInstanceMethod __get__ called'
print
a
print
b
print
c
__set__
=
imm
.
setUnboundInstanceMethod
descGetSetUnboundInstanceMethod
=
DescGetSetUnboundInstanceMethod
()
class
DescGetClosure
(
object
):
__get__
=
closureGet
()
descGetClosure
=
DescGetClosure
()
class
DescSetClosure
(
object
):
__set__
=
closureSet
()
descSetClosure
=
DescSetClosure
()
class
DescGetSetClosure
(
object
):
def
__get__
(
a
,
b
,
c
):
print
'DescGetSetClosure __get__ called'
print
a
print
b
print
c
__set__
=
closureSet
()
descGetSetClosure
=
DescGetSetClosure
()
class
DescGetGenerator
(
object
):
def
__get__
(
self
,
obj
,
type
):
print
'DescGetGenerator __get__ called'
print
self
print
obj
print
type
yield
15
print
'__get__ post yield'
descGetGenerator
=
DescGetGenerator
()
class
DescSetGenerator
(
object
):
def
__set__
(
self
,
obj
,
value
):
print
'DescSetGenerator __set__ called'
print
self
print
obj
print
value
yield
15
print
'__set__ post yield'
descSetGenerator
=
DescSetGenerator
()
class
DescGetSetGenerator
(
object
):
def
__get__
(
a
,
b
,
c
):
print
'DescGetSetGenerator __get__ called'
print
a
print
b
print
c
def
__set__
(
self
,
obj
,
value
):
print
'DescGetSetGenerator __set__ called'
print
self
print
obj
print
value
yield
15
print
'DescGetSetGenerator __set__ post yield'
descGetSetGenerator
=
DescGetSetGenerator
()
descSetClosure
=
DescSetClosure
()
a
=
A
()
print
'int'
try
:
print
a
.
descGetInt
except
:
traceback
.
print_exc
()
try
:
a
.
descSetInt
=
5
except
:
traceback
.
print_exc
()
a
.
__dict__
[
'descGetSetInt'
]
=
3
print
a
.
descGetSetInt
print
'object with __call__'
print
a
.
descGetCall
a
.
descSetCall
=
5
a
.
__dict__
[
'descGetSetCall'
]
=
3
print
a
.
descGetSetCall
print
'bound instance method'
print
a
.
descGetBoundInstanceMethod
a
.
descSetBoundInstanceMethod
=
5
a
.
__dict__
[
'descGetSetBoundInstanceMethod'
]
=
3
print
a
.
descGetSetBoundInstanceMethod
print
'unbound instance method'
try
:
print
a
.
descGetUnboundInstanceMethod
except
:
traceback
.
print_exc
()
try
:
a
.
descSetUnboundInstanceMethod
=
5
except
:
traceback
.
print_exc
()
a
.
__dict__
[
'descGetSetUnboundInstanceMethod'
]
=
3
print
a
.
descGetSetUnboundInstanceMethod
print
'closure'
print
a
.
descGetClosure
a
.
descSetClosure
=
5
a
.
__dict__
[
'descGetSetClosure'
]
=
3
print
a
.
descGetClosure
print
'generator'
print
a
.
descGetGenerator
a
.
descSetGenerator
=
5
a
.
__dict__
[
'descGetSetGenerator'
]
=
3
print
a
.
descGetGenerator
test/tests/descriptors_setattr_ics.py
0 → 100644
View file @
b594728e
# run_args: -n
# statcheck: stats['slowpath_setattr'] <= 120
class
Descriptor
(
object
):
def
__set__
(
self
,
obj
,
value
):
print
'__set__ called'
print
type
(
self
)
print
type
(
obj
)
print
type
(
value
)
class
C
(
object
):
a
=
Descriptor
()
c
=
C
()
def
f
(
i
):
c
.
a
=
i
for
i
in
xrange
(
1000
):
f
(
i
)
test/tests/dunder_descriptors.py
View file @
b594728e
# expected: fail
# - descriptors
# Descriptors get processed when fetched as part of a dunder lookup
...
...
test/tests/function_instancemethod.py
0 → 100644
View file @
b594728e
# TODO test all of this with getclsattr
# TODO should make an ics test
class
C
(
object
):
def
f
():
pass
def
g
():
print
'running g'
print
C
.
f
==
C
.
f
print
C
.
f
is
C
.
f
print
C
().
f
==
C
().
f
print
C
().
f
is
C
().
f
#### Check the types of stuff
print
type
(
C
.
f
)
# instancemethod
print
type
(
C
().
f
)
# instancemethod
print
type
(
g
)
# function
C
.
g
=
g
print
type
(
C
.
g
)
# instancemethod
print
type
(
C
().
g
)
# instancemethod
#### Assign a function to an instance
c
=
C
()
c
.
g
=
g
print
type
(
c
.
g
)
# function
c
.
g
()
print
c
.
g
==
c
.
g
print
c
.
g
is
c
.
g
#### Assign a function to a class
def
l
(
inst
):
print
'running l'
,
inst
.
i
C
.
l
=
l
print
type
(
C
.
l
)
#instancemethod
print
type
(
C
().
l
)
#instancemethod
c1
=
C
()
c1
.
i
=
1
C
.
l
(
c1
)
c1
.
l
()
print
c1
.
l
==
c1
.
l
print
c1
.
l
is
c1
.
l
print
C
.
l
==
C
.
l
print
C
.
l
is
C
.
l
#### Assign a bound instancemethod to a class
C
.
k
=
c1
.
l
# set C.k to a bound instancemethod
C
.
k
()
# this should call l with with c1 as the arg
c2
=
C
()
c2
.
i
=
2
c2
.
k
()
# this should just call l with c1 as the arg, not try to bind anything else
print
type
(
C
.
k
)
# instancemethod
print
type
(
c2
.
k
)
# instancemethod
print
c2
.
k
==
c2
.
k
print
c2
.
k
is
c2
.
k
print
C
.
k
==
C
.
k
print
C
.
k
is
C
.
k
print
C
.
k
==
c2
.
k
print
C
.
k
is
c2
.
k
#### Assign an unbound instancemethod to a class
#### Getting is will bind it like a normal function
# TODO implement instancemethod stuff so this case works
"""
C.m = C.l
print type(C.m) #instancemethod
print type(C().m) #instancemethod
c3 = C()
c3.i = 3
C.m(c3)
c3.m()
print c3.m == c3.m
print c3.m is c3.m
print C.m == C.m
print C.m is C.m
"""
### Assign a bound instancemethod to an instance
c4
=
C
()
c4
.
i
=
4
c4
.
z
=
c1
.
l
print
type
(
c4
.
z
)
# instancemethod
c4
.
z
()
# should call l(c1)
print
c4
.
z
==
c4
.
z
print
c4
.
z
is
c4
.
z
### Assign an unbound instancemethod to an instance
c4
=
C
()
c4
.
i
=
4
c4
.
z
=
C
.
l
print
type
(
c4
.
z
)
# instancemethod
c4
.
z
(
c1
)
# should call l(c1)
print
c4
.
z
==
c4
.
z
print
c4
.
z
is
c4
.
z
### Call a bound instancemethod on its own (not through the callattr path)
bound_instancemethod
=
c1
.
l
bound_instancemethod
()
print
type
(
bound_instancemethod
)
### Call an unbound instancemethod on its own (not through the callattr path)
unbound_instancemethod
=
C
.
l
unbound_instancemethod
(
c2
)
print
type
(
unbound_instancemethod
)
test/tests/getattr_classmember_ordering.py
0 → 100644
View file @
b594728e
class
C
(
object
):
i
=
5
def
__getattr__
(
self
,
attr
):
print
'__getattr__ called on attr'
,
attr
return
6
c
=
C
()
print
c
.
i
# should print 5, not call __getattr__
class
D
(
object
):
i
=
5
class
E
(
D
):
def
__getattr__
(
self
,
attr
):
print
'__getattr__ called on attr'
,
attr
return
6
pass
e
=
E
()
print
e
.
i
# should print 5, not call __getattr__
test/tests/instancemethod_guards.py
0 → 100644
View file @
b594728e
# Test what happens when we swap out a function (which we handle special-cased
# in the descriptor logic) for a descriptor of another type
# TODO should be more thorough
# TODO should write similar tests for MemberDescriptors too
def
g
():
print
'in g'
class
Descriptor
(
object
):
def
__str__
(
self
):
return
'Descriptor'
def
__get__
(
self
,
obj
,
objtype
):
return
g
class
C
(
object
):
def
f
(
self
):
print
'in f'
c
=
C
()
def
run
():
c
.
f
()
t
=
c
.
f
t
()
for
i
in
xrange
(
100
):
run
()
if
i
==
50
:
C
.
f
=
Descriptor
()
# Swap an unbound for a bound
class
C
(
object
):
def
f
(
self
,
a
=
0
):
print
"f"
,
a
def
__str__
(
self
):
return
"C obj"
def
call
(
f
,
c
):
#TODO uncomment this
#print f
f
(
c
)
c
=
C
()
call
(
C
.
f
,
c
)
call
(
c
.
f
,
c
)
test/tests/object_new_arguments.py
View file @
b594728e
# expected: fail
# object.__new__ doesn't complain if __init__ is overridden:
class
C1
(
object
):
...
...
tools/tester.py
View file @
b594728e
...
...
@@ -197,7 +197,10 @@ def run_test(fn, check_stats, run_memcheck):
os
.
unlink
(
out_fn
)
raise
Exception
(
"Failed on %s:
\
n
%s"
%
(
fn
,
diff
))
elif
not
TEST_PYPY
and
canonicalize_stderr
(
stderr
)
!=
canonicalize_stderr
(
expected_err
):
if
KEEP_GOING
:
if
expected
==
"fail"
:
r
+=
" Expected failure (bad stderr)"
return
r
elif
KEEP_GOING
:
r
+=
"
\
033
[31mFAILED
\
033
[0m (bad stderr)"
failed
.
append
(
fn
)
return
r
...
...
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