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
925c13d5
Commit
925c13d5
authored
May 27, 2015
by
Kevin Modzelewski
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #559 from kmod/threading_local4
switch to CPython's thread._local implementation
parents
73140839
d48961f6
Changes
20
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
482 additions
and
230 deletions
+482
-230
from_cpython/CMakeLists.txt
from_cpython/CMakeLists.txt
+1
-0
from_cpython/Include/methodobject.h
from_cpython/Include/methodobject.h
+11
-2
from_cpython/Include/pyconfig.h
from_cpython/Include/pyconfig.h
+2
-2
from_cpython/Modules/threadmodule.c
from_cpython/Modules/threadmodule.c
+22
-5
src/capi/modsupport.cpp
src/capi/modsupport.cpp
+1
-2
src/capi/typeobject.cpp
src/capi/typeobject.cpp
+9
-6
src/capi/types.h
src/capi/types.h
+30
-24
src/core/types.h
src/core/types.h
+1
-0
src/gc/collector.cpp
src/gc/collector.cpp
+23
-13
src/gc/collector.h
src/gc/collector.h
+5
-2
src/gc/heap.cpp
src/gc/heap.cpp
+22
-4
src/runtime/builtin_modules/sys.cpp
src/runtime/builtin_modules/sys.cpp
+1
-1
src/runtime/builtin_modules/thread.cpp
src/runtime/builtin_modules/thread.cpp
+10
-85
src/runtime/capi.cpp
src/runtime/capi.cpp
+219
-22
src/runtime/objmodel.cpp
src/runtime/objmodel.cpp
+22
-28
src/runtime/types.cpp
src/runtime/types.cpp
+63
-21
test/tests/class_noctor.py
test/tests/class_noctor.py
+5
-6
test/tests/object_new_arguments.py
test/tests/object_new_arguments.py
+9
-5
test/tests/threading_local.py
test/tests/threading_local.py
+7
-2
test/tests/weakrefs_extensions.py
test/tests/weakrefs_extensions.py
+19
-0
No files found.
from_cpython/CMakeLists.txt
View file @
925c13d5
...
...
@@ -64,6 +64,7 @@ file(GLOB_RECURSE STDMODULE_SRCS Modules
stringio.c
stropmodule.c
textio.c
threadmodule.c
timemodule.c
unicodedata.c
util.c
...
...
from_cpython/Include/methodobject.h
View file @
925c13d5
...
...
@@ -14,8 +14,8 @@ extern "C" {
// Pyston change: this is no longer a static object
//PyAPI_DATA(PyTypeObject) PyCFunction_Type;
PyAPI_DATA
(
PyTypeObject
*
)
builtin_function_or_method
_cls
;
#define PyCFunction_Type (*
builtin_function_or_method
_cls)
PyAPI_DATA
(
PyTypeObject
*
)
capifunc
_cls
;
#define PyCFunction_Type (*
capifunc
_cls)
#define PyCFunction_Check(op) (Py_TYPE(op) == &PyCFunction_Type)
...
...
@@ -28,6 +28,8 @@ PyAPI_FUNC(PyCFunction) PyCFunction_GetFunction(PyObject *) PYSTON_NOEXCEPT;
PyAPI_FUNC
(
PyObject
*
)
PyCFunction_GetSelf
(
PyObject
*
)
PYSTON_NOEXCEPT
;
PyAPI_FUNC
(
int
)
PyCFunction_GetFlags
(
PyObject
*
)
PYSTON_NOEXCEPT
;
// Pyston change: changed to function calls
#if 0
/* Macros for direct access to these values. Type checks are *not*
done, so use with care. */
#define PyCFunction_GET_FUNCTION(func) \
...
...
@@ -36,6 +38,13 @@ PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *) PYSTON_NOEXCEPT;
(((PyCFunctionObject *)func) -> m_self)
#define PyCFunction_GET_FLAGS(func) \
(((PyCFunctionObject *)func) -> m_ml -> ml_flags)
#endif
#define PyCFunction_GET_FUNCTION(func) \
PyCFunction_GetFunction((PyObject*)(func))
#define PyCFunction_GET_SELF(func) \
PyCFunction_GetSelf((PyObject*)(func))
#define PyCFunction_GET_FLAGS(func) \
PyCFunction_GetFlags((PyObject*)(func))
PyAPI_FUNC
(
PyObject
*
)
PyCFunction_Call
(
PyObject
*
,
PyObject
*
,
PyObject
*
)
PYSTON_NOEXCEPT
;
struct
PyMethodDef
{
...
...
from_cpython/Include/pyconfig.h
View file @
925c13d5
...
...
@@ -244,14 +244,14 @@
#define HAVE_SYS_WAIT_H 1
#define HAVE_TCGETPGRP 1
#define HAVE_TCSETPGRP 1
#define HAVE_TEMPNAM 1
//#define HAVE_TEMPNAM 1 // pyston change: we have them but I dislike the compiler warnings they generate
#define HAVE_TERM_H 1
#define HAVE_TERMIOS_H 1
#define HAVE_TGAMMA 1
#define HAVE_TIMEGM 1
#define HAVE_TIMES 1
#define HAVE_TMPFILE 1
#define HAVE_TMPNAM 1
//#define HAVE_TMPNAM 1 // pyston change: we have them but I dislike the compiler warnings they generate
#define HAVE_TMPNAM_R 1
#define HAVE_TM_ZONE 1
#define HAVE_TRUNCATE 1
...
...
from_cpython/Modules/threadmodule.c
View file @
925c13d5
...
...
@@ -13,8 +13,10 @@
#include "pythread.h"
static
PyObject
*
ThreadError
;
// Pyston change: we're only using part of this file
static
PyObject
*
str_dict
;
#if 0
static PyObject *ThreadError;
static long nb_threads = 0;
/* Lock objects */
...
...
@@ -173,6 +175,7 @@ newlockobject(void)
}
return self;
}
#endif
/* Thread-local objects */
...
...
@@ -236,7 +239,7 @@ static PyTypeObject localdummytype = {
/* tp_name */
"_thread._localdummy"
,
/* tp_basicsize */
sizeof
(
localdummyobject
),
/* tp_itemsize */
0
,
/* tp_dealloc */
(
destructor
)
localdummy_dealloc
,
/* tp_dealloc */
(
destructor
)
/*localdummy_dealloc*/
NULL
,
/* tp_print */
0
,
/* tp_getattr */
0
,
/* tp_setattr */
0
,
...
...
@@ -389,6 +392,10 @@ local_traverse(localobject *self, visitproc visit, void *arg)
static
int
local_clear
(
localobject
*
self
)
{
// Pyston change:
Py_FatalError
(
"unexpected call to local_clear()"
);
abort
();
#if 0
PyThreadState *tstate;
Py_CLEAR(self->args);
Py_CLEAR(self->kw);
...
...
@@ -406,6 +413,7 @@ local_clear(localobject *self)
PyDict_DelItem(tstate->dict, self->key);
}
return 0;
#endif
}
static
void
...
...
@@ -490,7 +498,7 @@ static PyTypeObject localtype = {
/* tp_name */
"thread._local"
,
/* tp_basicsize */
sizeof
(
localobject
),
/* tp_itemsize */
0
,
/* tp_dealloc */
(
destructor
)
local_dealloc
,
/* tp_dealloc */
(
destructor
)
/*local_dealloc*/
NULL
,
/* tp_print */
0
,
/* tp_getattr */
0
,
/* tp_setattr */
0
,
...
...
@@ -589,6 +597,8 @@ _localdummy_destroyed(PyObject *localweakref, PyObject *dummyweakref)
Py_RETURN_NONE
;
}
// Pyston change:
#if 0
/* Module functions */
struct bootstate {
...
...
@@ -846,8 +856,11 @@ requiring allocation in multiples of the system memory page size\n\
- platform documentation should be referred to for more information\n\
(4kB pages are common; using multiples of 4096 for the stack size is\n\
the suggested approach in the absence of more specific information).");
#endif
static
PyMethodDef
thread_methods
[]
=
{
// Pyston change:
#if 0
{"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread,
METH_VARARGS,
start_new_doc},
...
...
@@ -871,6 +884,7 @@ static PyMethodDef thread_methods[] = {
{"stack_size", (PyCFunction)thread_stack_size,
METH_VARARGS,
stack_size_doc},
#endif
{
NULL
,
NULL
}
/* sentinel */
};
...
...
@@ -909,6 +923,8 @@ initthread(void)
if
(
m
==
NULL
)
return
;
// Pyston change:
#if 0
/* Add a symbolic constant */
d = PyModule_GetDict(m);
ThreadError = PyGC_AddRoot(PyErr_NewException("thread.error", NULL, NULL));
...
...
@@ -918,17 +934,18 @@ initthread(void)
return;
Py_INCREF(&Locktype);
PyDict_SetItemString(d, "LockType", (PyObject *)&Locktype);
#endif
Py_INCREF
(
&
localtype
);
if
(
PyModule_AddObject
(
m
,
"_local"
,
(
PyObject
*
)
&
localtype
)
<
0
)
return
;
nb_threads
=
0
;
//nb_threads = 0; // pyston change
str_dict
=
PyGC_AddRoot
(
PyString_InternFromString
(
"__dict__"
));
if
(
str_dict
==
NULL
)
return
;
/* Initialize the C thread library */
PyThread_init_thread
();
//PyThread_init_thread(); // pyston change
}
src/capi/modsupport.cpp
View file @
925c13d5
...
...
@@ -425,8 +425,7 @@ extern "C" PyObject* Py_InitModule4(const char* name, PyMethodDef* methods, cons
while
(
methods
&&
methods
->
ml_name
)
{
RELEASE_ASSERT
((
methods
->
ml_flags
&
(
~
(
METH_VARARGS
|
METH_KEYWORDS
|
METH_NOARGS
|
METH_O
)))
==
0
,
"%d"
,
methods
->
ml_flags
);
module
->
giveAttr
(
methods
->
ml_name
,
new
BoxedCApiFunction
(
methods
->
ml_flags
,
passthrough
,
methods
->
ml_name
,
methods
->
ml_meth
,
boxString
(
name
)));
module
->
giveAttr
(
methods
->
ml_name
,
new
BoxedCApiFunction
(
methods
,
passthrough
,
boxString
(
name
)));
methods
++
;
}
...
...
src/capi/typeobject.cpp
View file @
925c13d5
...
...
@@ -27,6 +27,8 @@ static const std::string _getattr_str("__getattr__");
static
const
std
::
string
_getattribute_str
(
"__getattribute__"
);
typedef
int
(
*
update_callback
)(
PyTypeObject
*
,
void
*
);
static
PyObject
*
tp_new_wrapper
(
PyTypeObject
*
self
,
BoxedTuple
*
args
,
Box
*
kwds
)
noexcept
;
extern
"C"
void
conservativeGCHandler
(
GCVisitor
*
v
,
Box
*
b
)
noexcept
{
v
->
visitPotentialRange
((
void
*
const
*
)
b
,
(
void
*
const
*
)((
char
*
)
b
+
b
->
cls
->
tp_basicsize
));
}
...
...
@@ -1591,8 +1593,6 @@ static const slotdef* update_one_slot(BoxedClass* type, const slotdef* p) noexce
else
use_generic
=
1
;
}
// TODO Pyston doesn't support PyCFunction_Type yet I think?
#if 0
}
else
if
(
Py_TYPE
(
descr
)
==
&
PyCFunction_Type
&&
PyCFunction_GET_FUNCTION
(
descr
)
==
(
PyCFunction
)
tp_new_wrapper
&&
ptr
==
(
void
**
)
&
type
->
tp_new
)
{
/* The __new__ wrapper is not a wrapper descriptor,
...
...
@@ -1611,7 +1611,6 @@ static const slotdef* update_one_slot(BoxedClass* type, const slotdef* p) noexce
in this reasoning that requires additional
sanity checks. I'll buy the first person to
point out a bug in this reasoning a beer. */
#endif
}
else
if
(
offset
==
offsetof
(
BoxedClass
,
tp_descr_get
)
&&
descr
->
cls
==
function_cls
&&
static_cast
<
BoxedFunction
*>
(
descr
)
->
f
->
always_use_version
)
{
type
->
tpp_descr_get
=
(
descrgetfunc
)
static_cast
<
BoxedFunction
*>
(
descr
)
->
f
->
always_use_version
->
code
;
...
...
@@ -1745,12 +1744,16 @@ static PyObject* tp_new_wrapper(PyTypeObject* self, BoxedTuple* args, Box* kwds)
return
self
->
tp_new
(
subtype
,
new_args
,
kwds
);
}
static
struct
PyMethodDef
tp_new_methoddef
[]
=
{
{
"__new__"
,
(
PyCFunction
)
tp_new_wrapper
,
METH_VARARGS
|
METH_KEYWORDS
,
PyDoc_STR
(
"T.__new__(S, ...) -> "
"a new object with type S, a subtype of T"
)
},
{
0
,
0
,
0
,
0
}
};
static
void
add_tp_new_wrapper
(
BoxedClass
*
type
)
noexcept
{
if
(
type
->
getattr
(
"__new__"
))
return
;
type
->
giveAttr
(
"__new__"
,
new
BoxedCApiFunction
(
METH_VARARGS
|
METH_KEYWORDS
,
type
,
"__new__"
,
(
PyCFunction
)
tp_new_wrapper
));
type
->
giveAttr
(
"__new__"
,
new
BoxedCApiFunction
(
tp_new_methoddef
,
type
));
}
void
add_operators
(
BoxedClass
*
cls
)
noexcept
{
...
...
@@ -2931,7 +2934,7 @@ extern "C" void PyType_Modified(PyTypeObject* type) noexcept {
extern
"C"
int
PyType_Ready
(
PyTypeObject
*
cls
)
noexcept
{
ASSERT
(
!
cls
->
is_pyston_class
,
"should not call this on Pyston classes"
);
gc
::
registerNonheapRootObject
(
cls
);
gc
::
registerNonheapRootObject
(
cls
,
sizeof
(
PyTypeObject
)
);
// unhandled fields:
int
ALLOWABLE_FLAGS
=
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
|
Py_TPFLAGS_HAVE_GC
|
Py_TPFLAGS_CHECKTYPES
...
...
src/capi/types.h
View file @
925c13d5
...
...
@@ -37,24 +37,22 @@ struct wrapper_def {
extern
"C"
BoxedClass
*
capifunc_cls
,
*
wrapperdescr_cls
,
*
wrapperobject_cls
;
class
BoxedCApiFunction
:
public
Box
{
private:
int
ml_flags
;
Box
*
passthrough
;
const
char
*
name
;
PyCFunction
func
;
public:
PyMethodDef
*
method_def
;
PyObject
*
passthrough
;
Box
*
module
;
public:
BoxedCApiFunction
(
int
ml_flags
,
Box
*
passthrough
,
const
char
*
name
,
PyCFunction
func
,
Box
*
module
=
NULL
)
:
m
l_flags
(
ml_flags
),
passthrough
(
passthrough
),
name
(
name
),
func
(
func
),
module
(
module
)
{}
BoxedCApiFunction
(
PyMethodDef
*
method_def
,
Box
*
passthrough
,
Box
*
module
=
NULL
)
:
m
ethod_def
(
method_def
),
passthrough
(
passthrough
),
module
(
module
)
{}
DEFAULT_CLASS
(
capifunc_cls
);
PyCFunction
getFunction
()
{
return
method_def
->
ml_meth
;
}
static
BoxedString
*
__repr__
(
BoxedCApiFunction
*
self
)
{
assert
(
self
->
cls
==
capifunc_cls
);
return
boxStrConstant
(
self
->
name
);
return
boxStrConstant
(
self
->
method_def
->
ml_
name
);
}
static
Box
*
__call__
(
BoxedCApiFunction
*
self
,
BoxedTuple
*
varargs
,
BoxedDict
*
kwargs
)
{
...
...
@@ -66,24 +64,28 @@ public:
threading
::
GLPromoteRegion
_gil_lock
;
Box
*
rtn
;
if
(
self
->
ml_flags
==
METH_VARARGS
)
{
int
flags
=
self
->
method_def
->
ml_flags
;
auto
func
=
self
->
method_def
->
ml_meth
;
if
(
flags
==
METH_VARARGS
)
{
assert
(
kwargs
->
d
.
size
()
==
0
);
rtn
=
(
Box
*
)
self
->
func
(
self
->
passthrough
,
varargs
);
}
else
if
(
self
->
ml_
flags
==
(
METH_VARARGS
|
METH_KEYWORDS
))
{
rtn
=
(
Box
*
)((
PyCFunctionWithKeywords
)
self
->
func
)(
self
->
passthrough
,
varargs
,
kwargs
);
}
else
if
(
self
->
ml_
flags
==
METH_NOARGS
)
{
rtn
=
(
Box
*
)
func
(
self
->
passthrough
,
varargs
);
}
else
if
(
flags
==
(
METH_VARARGS
|
METH_KEYWORDS
))
{
rtn
=
(
Box
*
)((
PyCFunctionWithKeywords
)
func
)(
self
->
passthrough
,
varargs
,
kwargs
);
}
else
if
(
flags
==
METH_NOARGS
)
{
assert
(
kwargs
->
d
.
size
()
==
0
);
assert
(
varargs
->
size
()
==
0
);
rtn
=
(
Box
*
)
self
->
func
(
self
->
passthrough
,
NULL
);
}
else
if
(
self
->
ml_
flags
==
METH_O
)
{
rtn
=
(
Box
*
)
func
(
self
->
passthrough
,
NULL
);
}
else
if
(
flags
==
METH_O
)
{
if
(
kwargs
->
d
.
size
()
!=
0
)
{
raiseExcHelper
(
TypeError
,
"%s() takes no keyword arguments"
,
self
->
name
);
raiseExcHelper
(
TypeError
,
"%s() takes no keyword arguments"
,
self
->
method_def
->
ml_
name
);
}
if
(
varargs
->
size
()
!=
1
)
{
raiseExcHelper
(
TypeError
,
"%s() takes exactly one argument (%d given)"
,
self
->
name
,
varargs
->
size
());
raiseExcHelper
(
TypeError
,
"%s() takes exactly one argument (%d given)"
,
self
->
method_def
->
ml_name
,
varargs
->
size
());
}
rtn
=
(
Box
*
)
self
->
func
(
self
->
passthrough
,
varargs
->
elts
[
0
]);
}
else
if
(
self
->
ml_
flags
==
METH_OLDARGS
)
{
rtn
=
(
Box
*
)
func
(
self
->
passthrough
,
varargs
->
elts
[
0
]);
}
else
if
(
flags
==
METH_OLDARGS
)
{
/* the really old style */
if
(
kwargs
==
NULL
||
PyDict_Size
(
kwargs
)
==
0
)
{
int
size
=
PyTuple_GET_SIZE
(
varargs
);
...
...
@@ -92,12 +94,12 @@ public:
arg
=
PyTuple_GET_ITEM
(
varargs
,
0
);
else
if
(
size
==
0
)
arg
=
NULL
;
rtn
=
self
->
func
(
self
->
passthrough
,
arg
);
rtn
=
func
(
self
->
passthrough
,
arg
);
}
else
{
raiseExcHelper
(
TypeError
,
"%.200s() takes no keyword arguments"
,
self
->
name
);
raiseExcHelper
(
TypeError
,
"%.200s() takes no keyword arguments"
,
self
->
method_def
->
ml_
name
);
}
}
else
{
RELEASE_ASSERT
(
0
,
"0x%x"
,
self
->
ml_
flags
);
RELEASE_ASSERT
(
0
,
"0x%x"
,
flags
);
}
checkAndThrowCAPIException
();
...
...
@@ -107,7 +109,7 @@ public:
static
Box
*
getname
(
Box
*
b
,
void
*
)
{
RELEASE_ASSERT
(
b
->
cls
==
capifunc_cls
,
""
);
const
char
*
s
=
static_cast
<
BoxedCApiFunction
*>
(
b
)
->
name
;
const
char
*
s
=
static_cast
<
BoxedCApiFunction
*>
(
b
)
->
method_def
->
ml_
name
;
if
(
s
)
return
boxStrConstant
(
s
);
return
None
;
...
...
@@ -125,6 +127,10 @@ public:
v
->
visit
(
o
->
module
);
}
};
static_assert
(
sizeof
(
BoxedCApiFunction
)
==
sizeof
(
PyCFunctionObject
),
""
);
static_assert
(
offsetof
(
BoxedCApiFunction
,
method_def
)
==
offsetof
(
PyCFunctionObject
,
m_ml
),
""
);
static_assert
(
offsetof
(
BoxedCApiFunction
,
passthrough
)
==
offsetof
(
PyCFunctionObject
,
m_self
),
""
);
static_assert
(
offsetof
(
BoxedCApiFunction
,
module
)
==
offsetof
(
PyCFunctionObject
,
m_module
),
""
);
class
BoxedWrapperDescriptor
:
public
Box
{
public:
...
...
src/core/types.h
View file @
925c13d5
...
...
@@ -390,6 +390,7 @@ enum class GCKind : uint8_t {
PRECISE
=
3
,
UNTRACKED
=
4
,
HIDDEN_CLASS
=
5
,
CONSERVATIVE_PYTHON
=
6
,
};
extern
"C"
void
*
gc_alloc
(
size_t
nbytes
,
GCKind
kind
);
...
...
src/gc/collector.cpp
View file @
925c13d5
...
...
@@ -162,12 +162,13 @@ static std::unordered_set<void*> nonheap_roots;
// way to verify it's not a nonheap root (the full check requires a hashtable lookup).
static
void
*
max_nonheap_root
=
0
;
static
void
*
min_nonheap_root
=
(
void
*
)
~
0
;
void
registerNonheapRootObject
(
void
*
obj
)
{
void
registerNonheapRootObject
(
void
*
obj
,
int
size
)
{
// I suppose that things could work fine even if this were true, but why would it happen?
assert
(
global_heap
.
getAllocationFromInteriorPointer
(
obj
)
==
NULL
);
assert
(
nonheap_roots
.
count
(
obj
)
==
0
);
nonheap_roots
.
insert
(
obj
);
registerPotentialRootRange
(
obj
,
((
uint8_t
*
)
obj
)
+
size
);
max_nonheap_root
=
std
::
max
(
obj
,
max_nonheap_root
);
min_nonheap_root
=
std
::
min
(
obj
,
min_nonheap_root
);
...
...
@@ -179,10 +180,29 @@ bool isNonheapRoot(void* p) {
return
nonheap_roots
.
count
(
p
)
!=
0
;
}
bool
isValidGC
Object
(
void
*
p
)
{
bool
isValidGC
Memory
(
void
*
p
)
{
return
isNonheapRoot
(
p
)
||
(
global_heap
.
getAllocationFromInteriorPointer
(
p
)
->
user_data
==
p
);
}
bool
isValidGCObject
(
void
*
p
)
{
if
(
isNonheapRoot
(
p
))
return
true
;
GCAllocation
*
al
=
global_heap
.
getAllocationFromInteriorPointer
(
p
);
if
(
!
al
)
return
false
;
return
al
->
user_data
==
p
&&
(
al
->
kind_id
==
GCKind
::
CONSERVATIVE_PYTHON
||
al
->
kind_id
==
GCKind
::
PYTHON
);
}
void
setIsPythonObject
(
Box
*
b
)
{
auto
al
=
global_heap
.
getAllocationFromInteriorPointer
(
b
);
assert
(
al
->
user_data
==
(
char
*
)
b
);
if
(
al
->
kind_id
==
GCKind
::
CONSERVATIVE
)
{
al
->
kind_id
=
GCKind
::
CONSERVATIVE_PYTHON
;
}
else
{
assert
(
al
->
kind_id
==
GCKind
::
PYTHON
);
}
}
static
std
::
unordered_set
<
GCRootHandle
*>*
getRootHandles
()
{
static
std
::
unordered_set
<
GCRootHandle
*>
root_handles
;
return
&
root_handles
;
...
...
@@ -259,16 +279,6 @@ void markPhase() {
threading
::
visitAllStacks
(
&
visitor
);
gatherInterpreterRoots
(
&
visitor
);
for
(
void
*
p
:
nonheap_roots
)
{
Box
*
b
=
reinterpret_cast
<
Box
*>
(
p
);
BoxedClass
*
cls
=
b
->
cls
;
if
(
cls
)
{
ASSERT
(
cls
->
gc_visit
,
"%s"
,
getTypeName
(
b
));
cls
->
gc_visit
(
&
visitor
,
b
);
}
}
for
(
auto
h
:
*
getRootHandles
())
{
visitor
.
visit
(
h
->
value
);
}
...
...
@@ -289,7 +299,7 @@ void markPhase() {
GCKind
kind_id
=
al
->
kind_id
;
if
(
kind_id
==
GCKind
::
UNTRACKED
)
{
continue
;
}
else
if
(
kind_id
==
GCKind
::
CONSERVATIVE
)
{
}
else
if
(
kind_id
==
GCKind
::
CONSERVATIVE
||
kind_id
==
GCKind
::
CONSERVATIVE_PYTHON
)
{
uint32_t
bytes
=
al
->
kind_data
;
if
(
DEBUG
>=
2
)
{
if
(
global_heap
.
small_arena
.
contains
(
p
))
{
...
...
src/gc/collector.h
View file @
925c13d5
...
...
@@ -31,7 +31,8 @@ void deregisterPermanentRoot(void* root_obj);
// Register an object that was not allocated through this collector, as a root for this collector.
// The motivating usecase is statically-allocated PyTypeObject objects, which are full Python objects
// even if they are not heap allocated.
void
registerNonheapRootObject
(
void
*
obj
);
// This memory will be scanned conservatively.
void
registerNonheapRootObject
(
void
*
obj
,
int
size
);
void
registerPotentialRootRange
(
void
*
start
,
void
*
end
);
...
...
@@ -59,8 +60,10 @@ void disableGC();
void
enableGC
();
// These are mostly for debugging:
bool
isValidGCObject
(
void
*
p
);
bool
isValidGCMemory
(
void
*
p
);
// if p is a valid gc-allocated pointer (or a non-heap root)
bool
isValidGCObject
(
void
*
p
);
// whether p is valid gc memory and is set to have Python destructor semantics applied
bool
isNonheapRoot
(
void
*
p
);
void
setIsPythonObject
(
Box
*
b
);
// Debugging/validation helpers: if a GC should not happen in certain sections (ex during unwinding),
// use these functions to mark that. This is different from disableGC/enableGC, since it causes an
...
...
src/gc/heap.cpp
View file @
925c13d5
...
...
@@ -152,7 +152,7 @@ bool _doFree(GCAllocation* al, std::vector<Box*>* weakly_referenced) {
VALGRIND_ENABLE_ERROR_REPORTING
;
#endif
if
(
alloc_kind
==
GCKind
::
PYTHON
)
{
if
(
alloc_kind
==
GCKind
::
PYTHON
||
alloc_kind
==
GCKind
::
CONSERVATIVE_PYTHON
)
{
#ifndef NVALGRIND
VALGRIND_DISABLE_ERROR_REPORTING
;
#endif
...
...
@@ -170,7 +170,11 @@ bool _doFree(GCAllocation* al, std::vector<Box*>* weakly_referenced) {
}
}
ASSERT
(
b
->
cls
->
tp_dealloc
==
NULL
,
"%s"
,
getTypeName
(
b
));
// XXX: we are currently ignoring destructors (tp_dealloc) for extension objects, since we have
// historically done that (whoops) and there are too many to be worth changing for now as long
// as we can get real destructor support soon.
ASSERT
(
b
->
cls
->
tp_dealloc
==
NULL
||
alloc_kind
==
GCKind
::
CONSERVATIVE_PYTHON
,
"%s"
,
getTypeName
(
b
));
if
(
b
->
cls
->
simple_destructor
)
b
->
cls
->
simple_destructor
(
b
);
}
...
...
@@ -208,7 +212,7 @@ struct HeapStatistics {
int
num_hcls_by_attrs
[
HCLS_ATTRS_STAT_MAX
+
1
];
int
num_hcls_by_attrs_exceed
;
TypeStats
python
,
conservative
,
untracked
,
hcls
,
precise
;
TypeStats
python
,
conservative
,
conservative_python
,
untracked
,
hcls
,
precise
;
TypeStats
total
;
HeapStatistics
(
bool
collect_cls_stats
,
bool
collect_hcls_stats
)
...
...
@@ -247,6 +251,17 @@ void addStatistic(HeapStatistics* stats, GCAllocation* al, int nbytes) {
}
else
if
(
al
->
kind_id
==
GCKind
::
CONSERVATIVE
)
{
stats
->
conservative
.
nallocs
++
;
stats
->
conservative
.
nbytes
+=
nbytes
;
}
else
if
(
al
->
kind_id
==
GCKind
::
CONSERVATIVE_PYTHON
)
{
stats
->
conservative_python
.
nallocs
++
;
stats
->
conservative_python
.
nbytes
+=
nbytes
;
if
(
stats
->
collect_cls_stats
)
{
Box
*
b
=
(
Box
*
)
al
->
user_data
;
auto
&
t
=
stats
->
by_cls
[
b
->
cls
];
t
.
nallocs
++
;
t
.
nbytes
+=
nbytes
;
}
}
else
if
(
al
->
kind_id
==
GCKind
::
UNTRACKED
)
{
stats
->
untracked
.
nallocs
++
;
stats
->
untracked
.
nbytes
+=
nbytes
;
...
...
@@ -288,6 +303,7 @@ void Heap::dumpHeapStatistics(int level) {
stats
.
python
.
print
(
"python"
);
stats
.
conservative
.
print
(
"conservative"
);
stats
.
conservative_python
.
print
(
"conservative_python"
);
stats
.
untracked
.
print
(
"untracked"
);
stats
.
hcls
.
print
(
"hcls"
);
stats
.
precise
.
print
(
"precise"
);
...
...
@@ -454,7 +470,9 @@ SmallArena::Block** SmallArena::_freeChain(Block** head, std::vector<Box*>& weak
}
else
{
if
(
_doFree
(
al
,
&
weakly_referenced
))
{
b
->
isfree
.
set
(
atom_idx
);
// memset(al->user_data, 0, b->size - sizeof(GCAllocation));
#ifndef NDEBUG
memset
(
al
->
user_data
,
0xbb
,
b
->
size
-
sizeof
(
GCAllocation
));
#endif
}
}
}
...
...
src/runtime/builtin_modules/sys.cpp
View file @
925c13d5
...
...
@@ -498,7 +498,7 @@ void setupSys() {
sys_flags_cls
->
freeze
();
for
(
auto
&
md
:
sys_methods
)
{
sys_module
->
giveAttr
(
md
.
ml_name
,
new
BoxedCApiFunction
(
md
.
ml_flags
,
sys_module
,
md
.
ml_name
,
md
.
ml_meth
));
sys_module
->
giveAttr
(
md
.
ml_name
,
new
BoxedCApiFunction
(
&
md
,
sys_module
));
}
sys_module
->
giveAttr
(
"flags"
,
new
BoxedSysFlags
());
...
...
src/runtime/builtin_modules/thread.cpp
View file @
925c13d5
...
...
@@ -27,6 +27,8 @@
using
namespace
pyston
::
threading
;
extern
"C"
void
initthread
();
static
int
initialized
;
static
void
PyThread__init_thread
(
void
);
/* Forward */
...
...
@@ -57,8 +59,6 @@ static size_t _pythread_stacksize = 0;
namespace
pyston
{
BoxedModule
*
thread_module
;
static
void
*
thread_start
(
Box
*
target
,
Box
*
varargs
,
Box
*
kwargs
)
{
assert
(
target
);
assert
(
varargs
);
...
...
@@ -156,73 +156,6 @@ Box* allocateLock() {
return
new
BoxedThreadLock
();
}
static
BoxedClass
*
thread_local_cls
;
class
BoxedThreadLocal
:
public
Box
{
public:
BoxedThreadLocal
()
{}
static
Box
*
getThreadLocalObject
(
Box
*
obj
)
{
BoxedDict
*
dict
=
static_cast
<
BoxedDict
*>
(
PyThreadState_GetDict
());
Box
*
tls_obj
=
dict
->
getOrNull
(
obj
);
if
(
tls_obj
==
NULL
)
{
tls_obj
=
new
BoxedDict
();
setitem
(
dict
,
obj
,
tls_obj
);
}
return
tls_obj
;
}
static
Box
*
setattrPyston
(
Box
*
obj
,
Box
*
name
,
Box
*
val
)
noexcept
{
if
(
isSubclass
(
name
->
cls
,
str_cls
)
&&
static_cast
<
BoxedString
*>
(
name
)
->
s
()
==
"__dict__"
)
{
raiseExcHelper
(
AttributeError
,
"'%.50s' object attribute '__dict__' is read-only"
,
Py_TYPE
(
obj
)
->
tp_name
);
}
Box
*
tls_obj
=
getThreadLocalObject
(
obj
);
setitem
(
tls_obj
,
name
,
val
);
return
None
;
}
static
int
setattro
(
Box
*
obj
,
Box
*
name
,
Box
*
val
)
noexcept
{
try
{
setattrPyston
(
obj
,
name
,
val
);
}
catch
(
ExcInfo
e
)
{
setCAPIException
(
e
);
return
-
1
;
}
return
0
;
}
static
Box
*
getattro
(
Box
*
obj
,
Box
*
name
)
noexcept
{
assert
(
name
->
cls
==
str_cls
);
llvm
::
StringRef
s
=
static_cast
<
BoxedString
*>
(
name
)
->
s
();
Box
*
tls_obj
=
getThreadLocalObject
(
obj
);
if
(
s
==
"__dict__"
)
return
tls_obj
;
try
{
return
getitem
(
tls_obj
,
name
);
}
catch
(
ExcInfo
e
)
{
}
try
{
Box
*
r
=
getattrInternalGeneric
(
obj
,
s
,
NULL
,
false
,
false
,
NULL
,
NULL
);
if
(
r
)
return
r
;
}
catch
(
ExcInfo
e
)
{
setCAPIException
(
e
);
return
NULL
;
}
PyErr_Format
(
PyExc_AttributeError
,
"'%.50s' object has no attribute '%.400s'"
,
Py_TYPE
(
obj
)
->
tp_name
,
s
.
data
());
return
NULL
;
}
static
Box
*
hash
(
Box
*
obj
)
{
return
boxInt
(
PyThread_get_thread_ident
());
}
DEFAULT_CLASS
(
thread_local_cls
);
};
Box
*
getIdent
()
{
return
boxInt
(
pthread_self
());
}
...
...
@@ -232,7 +165,14 @@ Box* stackSize() {
}
void
setupThread
()
{
thread_module
=
createModule
(
"thread"
);
// Hacky: we want to use some of CPython's implementation of the thread module (the threading local stuff),
// and some of ours (thread handling). Start off by calling a cut-down version of initthread, and then
// add our own attributes to the module it creates.
initthread
();
RELEASE_ASSERT
(
!
PyErr_Occurred
(),
""
);
Box
*
thread_module
=
getSysModulesDict
()
->
getOrNull
(
boxString
(
"thread"
));
assert
(
thread_module
);
thread_module
->
giveAttr
(
"start_new_thread"
,
new
BoxedBuiltinFunctionOrMethod
(
boxRTFunction
((
void
*
)
startNewThread
,
BOXED_INT
,
3
,
1
,
false
,
false
),
...
...
@@ -256,21 +196,6 @@ void setupThread() {
thread_lock_cls
->
giveAttr
(
"__exit__"
,
new
BoxedFunction
(
boxRTFunction
((
void
*
)
BoxedThreadLock
::
exit
,
NONE
,
4
)));
thread_lock_cls
->
freeze
();
thread_local_cls
=
new
(
0
)
BoxedHeapClass
(
object_cls
,
NULL
,
0
,
0
,
sizeof
(
BoxedThreadLocal
),
false
,
static_cast
<
BoxedString
*>
(
boxString
(
"_local"
)));
thread_local_cls
->
giveAttr
(
"__module__"
,
boxStrConstant
(
"thread"
));
thread_local_cls
->
giveAttr
(
"__hash__"
,
new
BoxedFunction
(
boxRTFunction
((
void
*
)
BoxedThreadLocal
::
hash
,
BOXED_INT
,
1
)));
thread_local_cls
->
giveAttr
(
"__setattr__"
,
new
BoxedFunction
(
boxRTFunction
((
void
*
)
BoxedThreadLocal
::
setattrPyston
,
UNKNOWN
,
3
)));
thread_module
->
giveAttr
(
"_local"
,
thread_local_cls
);
thread_local_cls
->
tp_setattro
=
BoxedThreadLocal
::
setattro
;
thread_local_cls
->
tp_getattro
=
BoxedThreadLocal
::
getattro
;
add_operators
(
thread_local_cls
);
thread_local_cls
->
finishInitialization
();
thread_local_cls
->
freeze
();
BoxedClass
*
ThreadError
=
BoxedHeapClass
::
create
(
type_cls
,
Exception
,
NULL
,
Exception
->
attrs_offset
,
Exception
->
tp_weaklistoffset
,
Exception
->
tp_basicsize
,
false
,
"error"
);
...
...
src/runtime/capi.cpp
View file @
925c13d5
...
...
@@ -85,17 +85,6 @@ extern "C" void _PyErr_BadInternalCall(const char* filename, int lineno) noexcep
Py_FatalError
(
"unimplemented"
);
}
extern
"C"
PyVarObject
*
PyObject_InitVar
(
PyVarObject
*
op
,
PyTypeObject
*
tp
,
Py_ssize_t
size
)
noexcept
{
assert
(
gc
::
isValidGCObject
(
op
));
assert
(
gc
::
isValidGCObject
(
tp
));
RELEASE_ASSERT
(
op
,
""
);
RELEASE_ASSERT
(
tp
,
""
);
Py_TYPE
(
op
)
=
tp
;
op
->
ob_size
=
size
;
return
op
;
}
extern
"C"
PyObject
*
PyObject_Format
(
PyObject
*
obj
,
PyObject
*
format_spec
)
noexcept
{
PyObject
*
empty
=
NULL
;
PyObject
*
result
=
NULL
;
...
...
@@ -269,15 +258,215 @@ extern "C" PyObject* PyObject_GenericGetAttr(PyObject* o, PyObject* name) noexce
}
}
// Note (kmod): I don't feel great about including an alternate code-path for lookups. I also, however, don't feel
// great about modifying our code paths to take a custom dict, and since this code is just copied from CPython
// I feel like the risk is pretty low.
extern
"C"
PyObject
*
_PyObject_GenericGetAttrWithDict
(
PyObject
*
obj
,
PyObject
*
name
,
PyObject
*
dict
)
noexcept
{
fatalOrError
(
PyExc_NotImplementedError
,
"unimplemented"
);
return
nullptr
;
PyTypeObject
*
tp
=
Py_TYPE
(
obj
);
PyObject
*
descr
=
NULL
;
PyObject
*
res
=
NULL
;
descrgetfunc
f
;
Py_ssize_t
dictoffset
;
PyObject
**
dictptr
;
if
(
!
PyString_Check
(
name
))
{
#ifdef Py_USING_UNICODE
/* The Unicode to string conversion is done here because the
existing tp_setattro slots expect a string object as name
and we wouldn't want to break those. */
if
(
PyUnicode_Check
(
name
))
{
name
=
PyUnicode_AsEncodedString
(
name
,
NULL
,
NULL
);
if
(
name
==
NULL
)
return
NULL
;
}
else
#endif
{
PyErr_Format
(
PyExc_TypeError
,
"attribute name must be string, not '%.200s'"
,
Py_TYPE
(
name
)
->
tp_name
);
return
NULL
;
}
}
else
Py_INCREF
(
name
);
if
(
tp
->
tp_dict
==
NULL
)
{
if
(
PyType_Ready
(
tp
)
<
0
)
goto
done
;
}
#if 0 /* XXX this is not quite _PyType_Lookup anymore */
/* Inline _PyType_Lookup */
{
Py_ssize_t i, n;
PyObject *mro, *base, *dict;
/* Look in tp_dict of types in MRO */
mro = tp->tp_mro;
assert(mro != NULL);
assert(PyTuple_Check(mro));
n = PyTuple_GET_SIZE(mro);
for (i = 0; i < n; i++) {
base = PyTuple_GET_ITEM(mro, i);
if (PyClass_Check(base))
dict = ((PyClassObject *)base)->cl_dict;
else {
assert(PyType_Check(base));
dict = ((PyTypeObject *)base)->tp_dict;
}
assert(dict && PyDict_Check(dict));
descr = PyDict_GetItem(dict, name);
if (descr != NULL)
break;
}
}
#else
descr
=
_PyType_Lookup
(
tp
,
name
);
#endif
Py_XINCREF
(
descr
);
f
=
NULL
;
if
(
descr
!=
NULL
&&
PyType_HasFeature
(
descr
->
cls
,
Py_TPFLAGS_HAVE_CLASS
))
{
f
=
descr
->
cls
->
tp_descr_get
;
if
(
f
!=
NULL
&&
PyDescr_IsData
(
descr
))
{
res
=
f
(
descr
,
obj
,
(
PyObject
*
)
obj
->
cls
);
Py_DECREF
(
descr
);
goto
done
;
}
}
if
(
dict
==
NULL
)
{
/* Inline _PyObject_GetDictPtr */
dictoffset
=
tp
->
tp_dictoffset
;
if
(
dictoffset
!=
0
)
{
if
(
dictoffset
<
0
)
{
Py_ssize_t
tsize
;
size_t
size
;
tsize
=
((
PyVarObject
*
)
obj
)
->
ob_size
;
if
(
tsize
<
0
)
tsize
=
-
tsize
;
size
=
_PyObject_VAR_SIZE
(
tp
,
tsize
);
dictoffset
+=
(
long
)
size
;
assert
(
dictoffset
>
0
);
assert
(
dictoffset
%
SIZEOF_VOID_P
==
0
);
}
dictptr
=
(
PyObject
**
)((
char
*
)
obj
+
dictoffset
);
dict
=
*
dictptr
;
}
}
if
(
dict
!=
NULL
)
{
Py_INCREF
(
dict
);
res
=
PyDict_GetItem
(
dict
,
name
);
if
(
res
!=
NULL
)
{
Py_INCREF
(
res
);
Py_XDECREF
(
descr
);
Py_DECREF
(
dict
);
goto
done
;
}
Py_DECREF
(
dict
);
}
if
(
f
!=
NULL
)
{
res
=
f
(
descr
,
obj
,
(
PyObject
*
)
Py_TYPE
(
obj
));
Py_DECREF
(
descr
);
goto
done
;
}
if
(
descr
!=
NULL
)
{
res
=
descr
;
/* descr was already increfed above */
goto
done
;
}
PyErr_Format
(
PyExc_AttributeError
,
"'%.50s' object has no attribute '%.400s'"
,
tp
->
tp_name
,
PyString_AS_STRING
(
name
));
done:
Py_DECREF
(
name
);
return
res
;
}
// (see note for _PyObject_GenericGetAttrWithDict)
extern
"C"
int
_PyObject_GenericSetAttrWithDict
(
PyObject
*
obj
,
PyObject
*
name
,
PyObject
*
value
,
PyObject
*
dict
)
noexcept
{
fatalOrError
(
PyExc_NotImplementedError
,
"unimplemented"
);
return
-
1
;
PyTypeObject
*
tp
=
Py_TYPE
(
obj
);
PyObject
*
descr
;
descrsetfunc
f
;
PyObject
**
dictptr
;
int
res
=
-
1
;
if
(
!
PyString_Check
(
name
))
{
#ifdef Py_USING_UNICODE
/* The Unicode to string conversion is done here because the
existing tp_setattro slots expect a string object as name
and we wouldn't want to break those. */
if
(
PyUnicode_Check
(
name
))
{
name
=
PyUnicode_AsEncodedString
(
name
,
NULL
,
NULL
);
if
(
name
==
NULL
)
return
-
1
;
}
else
#endif
{
PyErr_Format
(
PyExc_TypeError
,
"attribute name must be string, not '%.200s'"
,
Py_TYPE
(
name
)
->
tp_name
);
return
-
1
;
}
}
else
Py_INCREF
(
name
);
if
(
tp
->
tp_dict
==
NULL
)
{
if
(
PyType_Ready
(
tp
)
<
0
)
goto
done
;
}
descr
=
_PyType_Lookup
(
tp
,
name
);
f
=
NULL
;
if
(
descr
!=
NULL
&&
PyType_HasFeature
(
descr
->
cls
,
Py_TPFLAGS_HAVE_CLASS
))
{
f
=
descr
->
cls
->
tp_descr_set
;
if
(
f
!=
NULL
&&
PyDescr_IsData
(
descr
))
{
res
=
f
(
descr
,
obj
,
value
);
goto
done
;
}
}
if
(
dict
==
NULL
)
{
dictptr
=
_PyObject_GetDictPtr
(
obj
);
if
(
dictptr
!=
NULL
)
{
dict
=
*
dictptr
;
if
(
dict
==
NULL
&&
value
!=
NULL
)
{
dict
=
PyDict_New
();
if
(
dict
==
NULL
)
goto
done
;
*
dictptr
=
dict
;
}
}
}
if
(
dict
!=
NULL
)
{
Py_INCREF
(
dict
);
if
(
value
==
NULL
)
res
=
PyDict_DelItem
(
dict
,
name
);
else
res
=
PyDict_SetItem
(
dict
,
name
,
value
);
if
(
res
<
0
&&
PyErr_ExceptionMatches
(
PyExc_KeyError
))
PyErr_SetObject
(
PyExc_AttributeError
,
name
);
Py_DECREF
(
dict
);
goto
done
;
}
if
(
f
!=
NULL
)
{
res
=
f
(
descr
,
obj
,
value
);
goto
done
;
}
if
(
descr
==
NULL
)
{
PyErr_Format
(
PyExc_AttributeError
,
"'%.100s' object has no attribute '%.200s'"
,
tp
->
tp_name
,
PyString_AS_STRING
(
name
));
goto
done
;
}
PyErr_Format
(
PyExc_AttributeError
,
"'%.50s' object attribute '%.400s' is read-only"
,
tp
->
tp_name
,
PyString_AS_STRING
(
name
));
done:
Py_DECREF
(
name
);
return
res
;
}
...
...
@@ -1265,7 +1454,7 @@ extern "C" PyObject* Py_FindMethod(PyMethodDef* methods, PyObject* self, const c
extern
"C"
PyObject
*
PyCFunction_NewEx
(
PyMethodDef
*
ml
,
PyObject
*
self
,
PyObject
*
module
)
noexcept
{
assert
((
ml
->
ml_flags
&
(
~
(
METH_VARARGS
|
METH_KEYWORDS
|
METH_NOARGS
|
METH_O
)))
==
0
);
return
new
BoxedCApiFunction
(
ml
->
ml_flags
,
self
,
ml
->
ml_name
,
ml
->
ml_meth
,
module
);
return
new
BoxedCApiFunction
(
ml
,
self
,
module
);
}
extern
"C"
PyCFunction
PyCFunction_GetFunction
(
PyObject
*
op
)
noexcept
{
...
...
@@ -1273,7 +1462,15 @@ extern "C" PyCFunction PyCFunction_GetFunction(PyObject* op) noexcept {
PyErr_BadInternalCall
();
return
NULL
;
}
return
((
PyCFunctionObject
*
)
op
)
->
m_ml
->
ml_meth
;
return
static_cast
<
BoxedCApiFunction
*>
(
op
)
->
getFunction
();
}
extern
"C"
PyObject
*
PyCFunction_GetSelf
(
PyObject
*
op
)
noexcept
{
if
(
!
PyCFunction_Check
(
op
))
{
PyErr_BadInternalCall
();
return
NULL
;
}
return
static_cast
<
BoxedCApiFunction
*>
(
op
)
->
passthrough
;
}
extern
"C"
int
_PyEval_SliceIndex
(
PyObject
*
v
,
Py_ssize_t
*
pi
)
noexcept
{
...
...
@@ -1413,18 +1610,18 @@ Box* BoxedCApiFunction::callInternal(BoxedFunctionBase* func, CallRewriteArgs* r
assert
(
arg1
->
cls
==
capifunc_cls
);
BoxedCApiFunction
*
capifunc
=
static_cast
<
BoxedCApiFunction
*>
(
arg1
);
if
(
capifunc
->
ml_flags
!=
METH_O
)
if
(
capifunc
->
m
ethod_def
->
m
l_flags
!=
METH_O
)
return
callFunc
(
func
,
rewrite_args
,
argspec
,
arg1
,
arg2
,
arg3
,
args
,
keyword_names
);
if
(
rewrite_args
)
{
rewrite_args
->
arg1
->
addGuard
((
intptr_t
)
arg1
);
RewriterVar
*
r_passthrough
=
rewrite_args
->
arg1
->
getAttr
(
offsetof
(
BoxedCApiFunction
,
passthrough
));
rewrite_args
->
out_rtn
=
rewrite_args
->
rewriter
->
call
(
true
,
(
void
*
)
capifunc
->
func
,
r_passthrough
,
rewrite_args
->
arg2
);
rewrite_args
->
out_rtn
=
rewrite_args
->
rewriter
->
call
(
true
,
(
void
*
)
capifunc
->
method_def
->
ml_meth
,
r_passthrough
,
rewrite_args
->
arg2
);
rewrite_args
->
rewriter
->
call
(
true
,
(
void
*
)
checkAndThrowCAPIException
);
rewrite_args
->
out_success
=
true
;
}
Box
*
r
=
capifunc
->
func
(
capifunc
->
passthrough
,
arg2
);
Box
*
r
=
capifunc
->
method_def
->
ml_meth
(
capifunc
->
passthrough
,
arg2
);
checkAndThrowCAPIException
();
assert
(
r
);
return
r
;
...
...
@@ -1448,7 +1645,7 @@ static Box* methodGetDoc(Box* b, void*) {
extern
"C"
PyObject
*
_PyObject_GC_Malloc
(
size_t
basicsize
)
noexcept
{
Box
*
r
=
((
PyObject
*
)
PyObject_MALLOC
(
basicsize
));
RELEASE_ASSERT
(
gc
::
isValidGC
Object
(
r
),
""
);
RELEASE_ASSERT
(
gc
::
isValidGC
Memory
(
r
),
""
);
return
r
;
}
...
...
src/runtime/objmodel.cpp
View file @
925c13d5
...
...
@@ -901,6 +901,16 @@ void Box::setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite
abort
();
}
extern
"C"
PyObject
*
_PyType_Lookup
(
PyTypeObject
*
type
,
PyObject
*
name
)
noexcept
{
RELEASE_ASSERT
(
name
->
cls
==
str_cls
,
""
);
try
{
return
typeLookup
(
type
,
static_cast
<
BoxedString
*>
(
name
)
->
s
(),
NULL
);
}
catch
(
ExcInfo
e
)
{
setCAPIException
(
e
);
return
NULL
;
}
}
Box
*
typeLookup
(
BoxedClass
*
cls
,
const
std
::
string
&
attr
,
GetattrRewriteArgs
*
rewrite_args
)
{
Box
*
val
;
...
...
@@ -2328,7 +2338,7 @@ extern "C" void dumpEx(void* p, int levels) {
printf
(
"
\n
"
);
printf
(
"Raw address: %p
\n
"
,
p
);
bool
is_gc
=
gc
::
isValidGC
Object
(
p
);
bool
is_gc
=
gc
::
isValidGC
Memory
(
p
);
if
(
!
is_gc
)
{
printf
(
"non-gc memory
\n
"
);
return
;
...
...
@@ -4513,23 +4523,10 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa
// Notably, "type" itself does not. For instance, assuming M is a subclass of
// type, type.__new__(M, 1) will return the int class, which is not an instance of M.
static
Box
*
object_new
=
NULL
;
static
Box
*
object_init
=
NULL
;
// this is ok with not using StlCompatAllocator since we will manually register these objects with the GC
static
std
::
vector
<
Box
*>
allowable_news
;
if
(
!
object_new
)
{
object_new
=
typeLookup
(
object_cls
,
new_str
,
NULL
);
assert
(
object_new
);
// I think this is unnecessary, but good form:
gc
::
registerPermanentRoot
(
object_new
);
object_init
=
typeLookup
(
object_cls
,
init_str
,
NULL
);
assert
(
object_init
);
gc
::
registerPermanentRoot
(
object_init
);
allowable_news
.
push_back
(
object_new
);
for
(
BoxedClass
*
allowed_cls
:
{
enumerate_cls
,
xrange_cls
})
{
if
(
allowable_news
.
empty
())
{
for
(
BoxedClass
*
allowed_cls
:
{
object_cls
,
enumerate_cls
,
xrange_cls
})
{
auto
new_obj
=
typeLookup
(
allowed_cls
,
new_str
,
NULL
);
gc
::
registerPermanentRoot
(
new_obj
);
allowable_news
.
push_back
(
new_obj
);
...
...
@@ -4585,16 +4582,9 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa
RewriterVar
*
r_made
=
NULL
;
ArgPassSpec
new_argspec
=
argspec
;
if
(
npassed_args
>
1
&&
new_attr
==
object_new
)
{
if
(
init_attr
==
object_init
)
{
raiseExcHelper
(
TypeError
,
objectNewParameterTypeErrorMsg
());
}
else
{
new_argspec
=
ArgPassSpec
(
1
);
}
}
if
(
rewrite_args
)
{
if
(
new_attr
==
object_new
&&
init_attr
!=
object
_init
)
{
if
(
cls
->
tp_new
==
object_cls
->
tp_new
&&
cls
->
tp_init
!=
object_cls
->
tp
_init
)
{
// Fast case: if we are calling object_new, we normally doesn't look at the arguments at all.
// (Except in the case when init_attr != object_init, in which case object_new looks at the number
// of arguments and throws an exception.)
...
...
@@ -4660,7 +4650,7 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa
}
}
if
(
init_attr
&&
init_attr
!=
object
_init
)
{
if
(
init_attr
&&
made
->
cls
->
tp_init
!=
object_cls
->
tp
_init
)
{
// TODO apply the same descriptor special-casing as in callattr?
Box
*
initrtn
;
...
...
@@ -4722,8 +4712,10 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa
Box
*
typeCall
(
Box
*
obj
,
BoxedTuple
*
vararg
,
BoxedDict
*
kwargs
)
{
assert
(
vararg
->
cls
==
tuple_cls
);
bool
pass_kwargs
=
(
kwargs
&&
kwargs
->
d
.
size
());
int
n
=
vararg
->
size
();
int
args_to_pass
=
n
+
2
;
// 1 for obj, 1 for kwargs
int
args_to_pass
=
n
+
1
+
(
pass_kwargs
?
1
:
0
)
;
// 1 for obj, 1 for kwargs
Box
**
args
=
NULL
;
if
(
args_to_pass
>
3
)
...
...
@@ -4734,9 +4726,11 @@ Box* typeCall(Box* obj, BoxedTuple* vararg, BoxedDict* kwargs) {
for
(
int
i
=
0
;
i
<
n
;
i
++
)
{
getArg
(
i
+
1
,
arg1
,
arg2
,
arg3
,
args
)
=
vararg
->
elts
[
i
];
}
getArg
(
n
+
1
,
arg1
,
arg2
,
arg3
,
args
)
=
kwargs
;
return
typeCallInternal
(
NULL
,
NULL
,
ArgPassSpec
(
n
+
1
,
0
,
false
,
true
),
arg1
,
arg2
,
arg3
,
args
,
NULL
);
if
(
pass_kwargs
)
getArg
(
n
+
1
,
arg1
,
arg2
,
arg3
,
args
)
=
kwargs
;
return
typeCallInternal
(
NULL
,
NULL
,
ArgPassSpec
(
n
+
1
,
0
,
false
,
pass_kwargs
),
arg1
,
arg2
,
arg3
,
args
,
NULL
);
}
extern
"C"
void
delGlobal
(
Box
*
globals
,
const
std
::
string
*
name
)
{
...
...
src/runtime/types.cpp
View file @
925c13d5
...
...
@@ -95,7 +95,8 @@ bool IN_SHUTDOWN = false;
extern
"C"
PyObject
*
PystonType_GenericAlloc
(
BoxedClass
*
cls
,
Py_ssize_t
nitems
)
noexcept
{
assert
(
cls
);
const
size_t
size
=
_PyObject_VAR_SIZE
(
cls
,
nitems
);
// See PyType_GenericAlloc for note about the +1 here:
const
size_t
size
=
_PyObject_VAR_SIZE
(
cls
,
nitems
+
1
);
#ifndef NDEBUG
#if 0
...
...
@@ -144,8 +145,11 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
extern
"C"
PyObject
*
PyType_GenericAlloc
(
PyTypeObject
*
type
,
Py_ssize_t
nitems
)
noexcept
{
PyObject
*
obj
;
const
size_t
size
=
_PyObject_VAR_SIZE
(
type
,
nitems
);
const
size_t
size
=
_PyObject_VAR_SIZE
(
type
,
nitems
+
1
);
// I don't understand why there is a +1 in this method; _PyObject_NewVar doesn't do that.
// CPython has the following comment:
/* note that we need to add one, for the sentinel */
// I think that regardless of the reasoning behind them having it, we should do what they do?
if
(
PyType_IS_GC
(
type
))
obj
=
_PyObject_GC_Malloc
(
size
);
...
...
@@ -1731,26 +1735,45 @@ Box* objectNewNoArgs(BoxedClass* cls) {
return
new
(
cls
)
Box
();
}
Box
*
objectNew
(
BoxedClass
*
cls
,
BoxedTuple
*
args
,
BoxedDict
*
kwargs
)
{
assert
(
isSubclass
(
cls
->
cls
,
type_cls
));
assert
(
args
->
cls
==
tuple_cls
);
assert
(
kwargs
->
cls
==
dict_cls
);
static
int
excess_args
(
PyObject
*
args
,
PyObject
*
kwds
)
noexcept
{
return
PyTuple_GET_SIZE
(
args
)
||
(
kwds
&&
PyDict_Check
(
kwds
)
&&
PyDict_Size
(
kwds
));
}
// We use a different strategy from CPython: we let object.__new__ take extra
// arguments, but raise an error if they wouldn't be handled by the corresponding init.
// TODO switch to the CPython approach?
if
(
args
->
size
()
!=
0
||
kwargs
->
d
.
size
()
!=
0
)
{
// TODO slow (We already cache these in typeCall -- should use that here too?)
if
(
typeLookup
(
cls
,
"__new__"
,
NULL
)
!=
typeLookup
(
object_cls
,
"__new__"
,
NULL
)
||
typeLookup
(
cls
,
"__init__"
,
NULL
)
==
typeLookup
(
object_cls
,
"__init__"
,
NULL
))
raiseExcHelper
(
TypeError
,
objectNewParameterTypeErrorMsg
());
}
static
PyObject
*
object_new
(
PyTypeObject
*
type
,
PyObject
*
args
,
PyObject
*
kwds
)
noexcept
;
return
new
(
cls
)
Box
();
static
int
object_init
(
PyObject
*
self
,
PyObject
*
args
,
PyObject
*
kwds
)
noexcept
{
int
err
=
0
;
if
(
excess_args
(
args
,
kwds
))
{
PyTypeObject
*
type
=
Py_TYPE
(
self
);
if
(
type
->
tp_init
!=
object_init
&&
type
->
tp_new
!=
object_new
)
{
err
=
PyErr_WarnEx
(
PyExc_DeprecationWarning
,
"object.__init__() takes no parameters"
,
1
);
}
else
if
(
type
->
tp_init
!=
object_init
||
type
->
tp_new
==
object_new
)
{
PyErr_SetString
(
PyExc_TypeError
,
"object.__init__() takes no parameters"
);
err
=
-
1
;
}
}
return
err
;
}
Box
*
objectInit
(
Box
*
b
,
BoxedTuple
*
args
)
{
return
None
;
static
PyObject
*
object_new
(
PyTypeObject
*
type
,
PyObject
*
args
,
PyObject
*
kwds
)
noexcept
{
int
err
=
0
;
if
(
excess_args
(
args
,
kwds
))
{
if
(
type
->
tp_new
!=
object_new
&&
type
->
tp_init
!=
object_init
)
{
err
=
PyErr_WarnEx
(
PyExc_DeprecationWarning
,
"object() takes no parameters"
,
1
);
}
else
if
(
type
->
tp_new
!=
object_new
||
type
->
tp_init
==
object_init
)
{
PyErr_SetString
(
PyExc_TypeError
,
"object() takes no parameters"
);
err
=
-
1
;
}
}
if
(
err
<
0
)
return
NULL
;
if
(
type
->
tp_flags
&
Py_TPFLAGS_IS_ABSTRACT
)
{
// I don't know what this is or when it happens, but
// CPython does something special with it
Py_FatalError
(
"unimplemented"
);
}
return
type
->
tp_alloc
(
type
,
0
);
}
Box
*
objectRepr
(
Box
*
obj
)
{
...
...
@@ -2209,13 +2232,30 @@ inline void initUserAttrs(Box* obj, BoxedClass* cls) {
extern
"C"
void
PyCallIter_AddHasNext
();
extern
"C"
PyVarObject
*
PyObject_InitVar
(
PyVarObject
*
op
,
PyTypeObject
*
tp
,
Py_ssize_t
size
)
noexcept
{
assert
(
op
);
assert
(
tp
);
assert
(
gc
::
isValidGCMemory
(
op
));
assert
(
gc
::
isValidGCObject
(
tp
));
gc
::
setIsPythonObject
(
op
);
Py_TYPE
(
op
)
=
tp
;
op
->
ob_size
=
size
;
return
op
;
}
extern
"C"
PyObject
*
PyObject_Init
(
PyObject
*
op
,
PyTypeObject
*
tp
)
noexcept
{
assert
(
op
);
assert
(
tp
);
assert
(
gc
::
isValidGC
Object
(
op
));
assert
(
gc
::
isValidGC
Memory
(
op
));
assert
(
gc
::
isValidGCObject
(
tp
));
gc
::
setIsPythonObject
(
op
);
Py_TYPE
(
op
)
=
tp
;
if
(
PyType_SUPPORTS_WEAKREFS
(
tp
))
{
...
...
@@ -2428,6 +2468,8 @@ void setupRuntime() {
object_cls
->
tp_getattro
=
PyObject_GenericGetAttr
;
object_cls
->
tp_setattro
=
PyObject_GenericSetAttr
;
object_cls
->
tp_init
=
object_init
;
object_cls
->
tp_new
=
object_new
;
add_operators
(
object_cls
);
object_cls
->
finishInitialization
();
...
...
@@ -2492,8 +2534,6 @@ void setupRuntime() {
SET
=
typeFromClass
(
set_cls
);
FROZENSET
=
typeFromClass
(
frozenset_cls
);
object_cls
->
giveAttr
(
"__new__"
,
new
BoxedFunction
(
boxRTFunction
((
void
*
)
objectNew
,
UNKNOWN
,
1
,
0
,
true
,
true
)));
object_cls
->
giveAttr
(
"__init__"
,
new
BoxedFunction
(
boxRTFunction
((
void
*
)
objectInit
,
UNKNOWN
,
1
,
0
,
true
,
false
)));
object_cls
->
giveAttr
(
"__repr__"
,
new
BoxedFunction
(
boxRTFunction
((
void
*
)
objectRepr
,
UNKNOWN
,
1
,
0
,
false
,
false
)));
object_cls
->
giveAttr
(
"__str__"
,
new
BoxedFunction
(
boxRTFunction
((
void
*
)
objectStr
,
UNKNOWN
,
1
,
0
,
false
,
false
)));
object_cls
->
giveAttr
(
"__hash__"
,
...
...
@@ -2546,6 +2586,8 @@ void setupRuntime() {
}
object_cls
->
giveAttr
(
"__class__"
,
new
(
pyston_getset_cls
)
BoxedGetsetDescriptor
(
objectClass
,
objectSetClass
,
NULL
));
object_cls
->
freeze
();
assert
(
object_cls
->
tp_init
==
object_init
);
assert
(
object_cls
->
tp_new
==
object_new
);
setupBool
();
setupLong
();
...
...
test/tests/class_noctor.py
View file @
925c13d5
# should_error
# skip-if: sys.version_info < (2, 7, 4)
# - Error message changed in 2.7.4
# Regression test:
# If the init function doesn't exist, shouldn't just silently ignore any args
# that got passed
...
...
@@ -16,5 +12,8 @@ print "This should have worked"
class
D
(
object
):
pass
d
=
D
(
1
)
print
"This should have failed"
try
:
d
=
D
(
1
)
print
"This should have failed"
except
TypeError
as
e
:
print
"expected exception"
# the message got changed in 2.7.4
test/tests/object_new_arguments.py
View file @
925c13d5
# should_error
# skip-if: sys.version_info < (2, 7, 4)
# - Error message changed in 2.7.4
# object.__new__ doesn't complain if __init__ is overridden:
class
C1
(
object
):
...
...
@@ -16,4 +12,12 @@ object.__new__(C1, 1)
object
.
__new__
(
C1
,
a
=
1
)
print
"Trying C2"
object
.
__new__
(
C2
,
1
)
try
:
object
.
__new__
(
C2
,
1
)
except
TypeError
as
e
:
print
"caught TypeError"
# These are some tricky cases, since they can potentially look like arguments
# are being passed, but really they are not.
type
.
__call__
(
*
[
C2
])
type
.
__call__
(
C2
,
**
{})
test/tests/threading_local.py
View file @
925c13d5
...
...
@@ -12,12 +12,14 @@ class CustomThreadingLocal(threading.local):
self
.
n
+=
1
print
self
.
a
,
self
.
n
print
CustomThreadingLocal
().
a
print
CustomThreadingLocal
().
a
c
=
CustomThreadingLocal
()
print
c
.
a
def
f
():
print
a
.
x
=
"goodbye world"
print
a
.
x
print
CustomThreadingLocal
()
.
a
print
c
.
a
print
CustomThreadingLocal
().
a
def
test
():
...
...
@@ -36,3 +38,6 @@ print a.x
a
.
__setattr__
(
'x'
,
5
)
print
a
.
x
print
sorted
(
a
.
__dict__
.
items
())
del
a
.
x
print
sorted
(
a
.
__dict__
.
items
())
test/tests/weakrefs_extensions.py
0 → 100644
View file @
925c13d5
# Make sure we can support weakrefs on extension objects.
# The _sre.SRE_Pattern type is one of the few builtin types that supports weakrefs natively.
import
_sre
def
f
(
n
):
if
n
:
return
f
(
n
-
1
)
p
=
_sre
.
compile
(
''
,
0
,
[
1
])
print
type
(
p
)
return
weakref
.
ref
(
p
)
import
weakref
r
=
f
(
20
)
import
gc
gc
.
collect
()
print
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