Commit 4480e933 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge branch 'django_dependencies'

parents 3ec5c835 4c729f7b
...@@ -293,7 +293,7 @@ STDLIB_RELEASE_OBJS := stdlib.release.bc.o ...@@ -293,7 +293,7 @@ STDLIB_RELEASE_OBJS := stdlib.release.bc.o
ASM_SRCS := $(wildcard src/runtime/*.S) ASM_SRCS := $(wildcard src/runtime/*.S)
STDMODULE_SRCS := errnomodule.c shamodule.c sha256module.c sha512module.c _math.c mathmodule.c md5.c md5module.c _randommodule.c _sre.c operator.c binascii.c pwdmodule.c posixmodule.c _struct.c datetimemodule.c _functoolsmodule.c _collectionsmodule.c itertoolsmodule.c resource.c signalmodule.c selectmodule.c fcntlmodule.c timemodule.c arraymodule.c zlibmodule.c _codecsmodule.c socketmodule.c unicodedata.c _weakref.c cStringIO.c _io/bufferedio.c _io/bytesio.c _io/fileio.c _io/iobase.c _io/_iomodule.c _io/stringio.c _io/textio.c $(EXTRA_STDMODULE_SRCS) STDMODULE_SRCS := errnomodule.c shamodule.c sha256module.c sha512module.c _math.c mathmodule.c md5.c md5module.c _randommodule.c _sre.c operator.c binascii.c pwdmodule.c posixmodule.c _struct.c datetimemodule.c _functoolsmodule.c _collectionsmodule.c itertoolsmodule.c resource.c signalmodule.c selectmodule.c fcntlmodule.c timemodule.c arraymodule.c zlibmodule.c _codecsmodule.c socketmodule.c unicodedata.c _weakref.c cStringIO.c _io/bufferedio.c _io/bytesio.c _io/fileio.c _io/iobase.c _io/_iomodule.c _io/stringio.c _io/textio.c $(EXTRA_STDMODULE_SRCS)
STDOBJECT_SRCS := structseq.c capsule.c stringobject.c exceptions.c unicodeobject.c unicodectype.c bytearrayobject.c bytes_methods.c weakrefobject.c memoryobject.c $(EXTRA_STDOBJECT_SRCS) STDOBJECT_SRCS := structseq.c capsule.c stringobject.c exceptions.c unicodeobject.c unicodectype.c bytearrayobject.c bytes_methods.c weakrefobject.c memoryobject.c iterobject.c $(EXTRA_STDOBJECT_SRCS)
STDPYTHON_SRCS := pyctype.c getargs.c formatter_string.c pystrtod.c dtoa.c formatter_unicode.c structmember.c $(EXTRA_STDPYTHON_SRCS) STDPYTHON_SRCS := pyctype.c getargs.c formatter_string.c pystrtod.c dtoa.c formatter_unicode.c structmember.c $(EXTRA_STDPYTHON_SRCS)
FROM_CPYTHON_SRCS := $(addprefix from_cpython/Modules/,$(STDMODULE_SRCS)) $(addprefix from_cpython/Objects/,$(STDOBJECT_SRCS)) $(addprefix from_cpython/Python/,$(STDPYTHON_SRCS)) FROM_CPYTHON_SRCS := $(addprefix from_cpython/Modules/,$(STDMODULE_SRCS)) $(addprefix from_cpython/Objects/,$(STDOBJECT_SRCS)) $(addprefix from_cpython/Python/,$(STDPYTHON_SRCS))
......
...@@ -18,7 +18,7 @@ add_custom_target(copy_stdlib ALL DEPENDS ${STDLIB_TARGETS}) ...@@ -18,7 +18,7 @@ add_custom_target(copy_stdlib ALL DEPENDS ${STDLIB_TARGETS})
file(GLOB_RECURSE STDMODULE_SRCS Modules errnomodule.c shamodule.c sha256module.c sha512module.c _math.c mathmodule.c md5.c md5module.c _randommodule.c _sre.c operator.c binascii.c pwdmodule.c posixmodule.c _struct.c datetimemodule.c _functoolsmodule.c _collectionsmodule.c itertoolsmodule.c resource.c signalmodule.c selectmodule.c fcntlmodule.c timemodule.c arraymodule.c zlibmodule.c _codecsmodule.c socketmodule.c unicodedata.c _weakref.c cStringIO.c bufferedio.c bytesio.c fileio.c iobase.c _iomodule.c stringio.c textio.c) file(GLOB_RECURSE STDMODULE_SRCS Modules errnomodule.c shamodule.c sha256module.c sha512module.c _math.c mathmodule.c md5.c md5module.c _randommodule.c _sre.c operator.c binascii.c pwdmodule.c posixmodule.c _struct.c datetimemodule.c _functoolsmodule.c _collectionsmodule.c itertoolsmodule.c resource.c signalmodule.c selectmodule.c fcntlmodule.c timemodule.c arraymodule.c zlibmodule.c _codecsmodule.c socketmodule.c unicodedata.c _weakref.c cStringIO.c bufferedio.c bytesio.c fileio.c iobase.c _iomodule.c stringio.c textio.c)
# compile specified files in from_cpython/Objects # compile specified files in from_cpython/Objects
file(GLOB_RECURSE STDOBJECT_SRCS Objects structseq.c capsule.c stringobject.c exceptions.c unicodeobject.c unicodectype.c bytearrayobject.c bytes_methods.c weakrefobject.c memoryobject.c) file(GLOB_RECURSE STDOBJECT_SRCS Objects structseq.c capsule.c stringobject.c exceptions.c unicodeobject.c unicodectype.c bytearrayobject.c bytes_methods.c weakrefobject.c memoryobject.c iterobject.c)
# compile specified files in from_cpython/Python # compile specified files in from_cpython/Python
file(GLOB_RECURSE STDPYTHON_SRCS Python getargs.c pyctype.c formatter_string.c pystrtod.c dtoa.c formatter_unicode.c structmember.c) file(GLOB_RECURSE STDPYTHON_SRCS Python getargs.c pyctype.c formatter_string.c pystrtod.c dtoa.c formatter_unicode.c structmember.c)
......
...@@ -14,8 +14,7 @@ extern "C" { ...@@ -14,8 +14,7 @@ extern "C" {
PyAPI_FUNC(PyObject *) PySeqIter_New(PyObject *) PYSTON_NOEXCEPT; PyAPI_FUNC(PyObject *) PySeqIter_New(PyObject *) PYSTON_NOEXCEPT;
// Pyston change: this is no longer a static object PyAPI_DATA(PyTypeObject) PyCallIter_Type;
//PyAPI_DATA(PyTypeObject) PyCallIter_Type;
#define PyCallIter_Check(op) (Py_TYPE(op) == &PyCallIter_Type) #define PyCallIter_Check(op) (Py_TYPE(op) == &PyCallIter_Type)
......
// This file is originally from CPython 2.7, with modifications for Pyston
// (actually it's just a subset of the CPython version)
#include "Python.h"
extern PyTypeObject PyCallIter_Type;
typedef struct {
PyObject_HEAD
PyObject *it_callable; /* Set to NULL when iterator is exhausted */
PyObject *it_sentinel; /* Set to NULL when iterator is exhausted */
} calliterobject;
PyObject *
PyCallIter_New(PyObject *callable, PyObject *sentinel)
{
calliterobject *it;
it = PyObject_GC_New(calliterobject, &PyCallIter_Type);
if (it == NULL)
return NULL;
Py_INCREF(callable);
it->it_callable = callable;
Py_INCREF(sentinel);
it->it_sentinel = sentinel;
_PyObject_GC_TRACK(it);
return (PyObject *)it;
}
static void
calliter_dealloc(calliterobject *it)
{
_PyObject_GC_UNTRACK(it);
Py_XDECREF(it->it_callable);
Py_XDECREF(it->it_sentinel);
PyObject_GC_Del(it);
}
static int
calliter_traverse(calliterobject *it, visitproc visit, void *arg)
{
Py_VISIT(it->it_callable);
Py_VISIT(it->it_sentinel);
return 0;
}
static PyObject *
calliter_iternext(calliterobject *it)
{
if (it->it_callable != NULL) {
PyObject *args = PyTuple_New(0);
PyObject *result;
if (args == NULL)
return NULL;
result = PyObject_Call(it->it_callable, args, NULL);
Py_DECREF(args);
if (result != NULL) {
int ok;
ok = PyObject_RichCompareBool(result,
it->it_sentinel,
Py_EQ);
if (ok == 0)
return result; /* Common case, fast path */
Py_DECREF(result);
if (ok > 0) {
Py_CLEAR(it->it_callable);
Py_CLEAR(it->it_sentinel);
}
}
else if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
PyErr_Clear();
Py_CLEAR(it->it_callable);
Py_CLEAR(it->it_sentinel);
}
}
return NULL;
}
PyTypeObject PyCallIter_Type = {
// Pyston change:
PyVarObject_HEAD_INIT(NULL /* &PyType_Type */, 0)
"callable-iterator", /* tp_name */
sizeof(calliterobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)calliter_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */
(traverseproc)calliter_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
(iternextfunc)calliter_iternext, /* tp_iternext */
0, /* tp_methods */
};
...@@ -407,6 +407,8 @@ public: ...@@ -407,6 +407,8 @@ public:
for (AST_comprehension* c : node->generators) { for (AST_comprehension* c : node->generators) {
if (!first) if (!first)
c->iter->accept(this); c->iter->accept(this);
for (auto i : c->ifs)
i->accept(this);
c->target->accept(this); c->target->accept(this);
first = false; first = false;
} }
......
...@@ -135,7 +135,9 @@ extern "C" void PyBuffer_Release(Py_buffer* view) noexcept { ...@@ -135,7 +135,9 @@ extern "C" void PyBuffer_Release(Py_buffer* view) noexcept {
PyObject* obj = view->obj; PyObject* obj = view->obj;
if (obj) { if (obj) {
assert(obj->cls == str_cls); // This is a Pyston assert
assert(isSubclass(obj->cls, str_cls));
if (obj && Py_TYPE(obj)->tp_as_buffer && Py_TYPE(obj)->tp_as_buffer->bf_releasebuffer) if (obj && Py_TYPE(obj)->tp_as_buffer && Py_TYPE(obj)->tp_as_buffer->bf_releasebuffer)
Py_TYPE(obj)->tp_as_buffer->bf_releasebuffer(obj, view); Py_TYPE(obj)->tp_as_buffer->bf_releasebuffer(obj, view);
Py_XDECREF(obj); Py_XDECREF(obj);
......
...@@ -45,23 +45,28 @@ ...@@ -45,23 +45,28 @@
#error #error
#endif #endif
using namespace pyston; namespace pyston {
// returns true iff we got a request to exit, i.e. SystemExit, placing the // returns true iff we got a request to exit, i.e. SystemExit, placing the
// return code in `*retcode`. does not touch `*retcode* if it returns false. // return code in `*retcode`. does not touch `*retcode* if it returns false.
static bool handle_toplevel_exn(const ExcInfo& e, int* retcode) { static bool handle_toplevel_exn(const ExcInfo& e, int* retcode) {
if (e.matches(SystemExit)) { if (e.matches(SystemExit)) {
Box* code = e.value->getattr("code"); Box* code = e.value->getattr("code");
*retcode = 1;
if (code && isSubclass(code->cls, pyston::int_cls)) if (!code || code == None)
*retcode = 0;
else if (isSubclass(code->cls, int_cls))
*retcode = static_cast<BoxedInt*>(code)->n; *retcode = static_cast<BoxedInt*>(code)->n;
else
*retcode = 1;
return true; return true;
} }
e.printExcAndTraceback(); e.printExcAndTraceback();
return false; return false;
} }
int main(int argc, char** argv) { static int main(int argc, char** argv) {
Timer _t("for jit startup"); Timer _t("for jit startup");
// llvm::sys::PrintStackTraceOnErrorSignal(); // llvm::sys::PrintStackTraceOnErrorSignal();
// llvm::PrettyStackTraceProgram X(argc, argv); // llvm::PrettyStackTraceProgram X(argc, argv);
...@@ -259,3 +264,9 @@ int main(int argc, char** argv) { ...@@ -259,3 +264,9 @@ int main(int argc, char** argv) {
return rtncode; return rtncode;
} }
} // namespace pyston
int main(int argc, char** argv) {
return pyston::main(argc, argv);
}
...@@ -852,10 +852,6 @@ extern "C" PyObject* PyImport_Import(PyObject* module_name) noexcept { ...@@ -852,10 +852,6 @@ extern "C" PyObject* PyImport_Import(PyObject* module_name) noexcept {
} }
extern "C" PyObject* PyCallIter_New(PyObject* callable, PyObject* sentinel) noexcept {
Py_FatalError("unimplemented");
}
extern "C" void* PyObject_Malloc(size_t sz) noexcept { extern "C" void* PyObject_Malloc(size_t sz) noexcept {
return gc_compat_malloc(sz); return gc_compat_malloc(sz);
} }
......
...@@ -57,6 +57,7 @@ public: ...@@ -57,6 +57,7 @@ public:
Box* dictViewKeysIter(Box* self); Box* dictViewKeysIter(Box* self);
Box* dictViewValuesIter(Box* self); Box* dictViewValuesIter(Box* self);
Box* dictViewItemsIter(Box* self); Box* dictViewItemsIter(Box* self);
void dictMerge(BoxedDict* self, Box* other);
} }
#endif #endif
...@@ -1055,14 +1055,27 @@ extern "C" size_t Py_UniversalNewlineFread(char* buf, size_t n, FILE* stream, Py ...@@ -1055,14 +1055,27 @@ extern "C" size_t Py_UniversalNewlineFread(char* buf, size_t n, FILE* stream, Py
return dst - buf; return dst - buf;
} }
static PyObject* file_isatty(BoxedFile* f) noexcept {
long res;
if (f->f_fp == NULL)
return err_closed();
FILE_BEGIN_ALLOW_THREADS(f)
res = isatty((int)fileno(f->f_fp));
FILE_END_ALLOW_THREADS(f)
return PyBool_FromLong(res);
}
PyDoc_STRVAR(readlines_doc, "readlines([size]) -> list of strings, each a line from the file.\n" PyDoc_STRVAR(readlines_doc, "readlines([size]) -> list of strings, each a line from the file.\n"
"\n" "\n"
"Call readline() repeatedly and return a list of the lines so read.\n" "Call readline() repeatedly and return a list of the lines so read.\n"
"The optional size argument, if given, is an approximate bound on the\n" "The optional size argument, if given, is an approximate bound on the\n"
"total number of bytes in the lines returned."); "total number of bytes in the lines returned.");
PyDoc_STRVAR(isatty_doc, "isatty() -> true or false. True if the file is connected to a tty device.");
PyMethodDef file_methods[] = { PyMethodDef file_methods[] = {
{ "readlines", (PyCFunction)file_readlines, METH_VARARGS, readlines_doc }, { "readlines", (PyCFunction)file_readlines, METH_VARARGS, readlines_doc },
{ "isatty", (PyCFunction)file_isatty, METH_NOARGS, isatty_doc },
}; };
void fileDestructor(Box* b) { void fileDestructor(Box* b) {
......
...@@ -87,6 +87,8 @@ extern "C" Box* floatAdd(BoxedFloat* lhs, Box* rhs) { ...@@ -87,6 +87,8 @@ extern "C" Box* floatAdd(BoxedFloat* lhs, Box* rhs) {
return floatAddInt(lhs, static_cast<BoxedInt*>(rhs)); return floatAddInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatAddFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatAddFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxFloat(lhs->d + PyLong_AsDouble(rhs));
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -112,6 +114,8 @@ extern "C" Box* floatDiv(BoxedFloat* lhs, Box* rhs) { ...@@ -112,6 +114,8 @@ extern "C" Box* floatDiv(BoxedFloat* lhs, Box* rhs) {
return floatDivInt(lhs, static_cast<BoxedInt*>(rhs)); return floatDivInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatDivFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatDivFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxFloat(lhs->d / PyLong_AsDouble(rhs));
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -123,6 +127,8 @@ extern "C" Box* floatTruediv(BoxedFloat* lhs, Box* rhs) { ...@@ -123,6 +127,8 @@ extern "C" Box* floatTruediv(BoxedFloat* lhs, Box* rhs) {
return floatDivInt(lhs, static_cast<BoxedInt*>(rhs)); return floatDivInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatDivFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatDivFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxFloat(lhs->d / PyLong_AsDouble(rhs));
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -148,6 +154,8 @@ extern "C" Box* floatRDiv(BoxedFloat* lhs, Box* rhs) { ...@@ -148,6 +154,8 @@ extern "C" Box* floatRDiv(BoxedFloat* lhs, Box* rhs) {
return floatRDivInt(lhs, static_cast<BoxedInt*>(rhs)); return floatRDivInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatRDivFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatRDivFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxFloat(PyLong_AsDouble(rhs) / lhs->d);
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -196,6 +204,8 @@ extern "C" Box* floatEq(BoxedFloat* lhs, Box* rhs) { ...@@ -196,6 +204,8 @@ extern "C" Box* floatEq(BoxedFloat* lhs, Box* rhs) {
return floatEqInt(lhs, static_cast<BoxedInt*>(rhs)); return floatEqInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatEqFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatEqFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxBool(lhs->d == PyLong_AsDouble(rhs));
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -219,6 +229,8 @@ extern "C" Box* floatNe(BoxedFloat* lhs, Box* rhs) { ...@@ -219,6 +229,8 @@ extern "C" Box* floatNe(BoxedFloat* lhs, Box* rhs) {
return floatNeInt(lhs, static_cast<BoxedInt*>(rhs)); return floatNeInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatNeFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatNeFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxBool(lhs->d != PyLong_AsDouble(rhs));
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -242,6 +254,8 @@ extern "C" Box* floatLt(BoxedFloat* lhs, Box* rhs) { ...@@ -242,6 +254,8 @@ extern "C" Box* floatLt(BoxedFloat* lhs, Box* rhs) {
return floatLtInt(lhs, static_cast<BoxedInt*>(rhs)); return floatLtInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatLtFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatLtFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxBool(lhs->d < PyLong_AsDouble(rhs));
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -265,6 +279,8 @@ extern "C" Box* floatLe(BoxedFloat* lhs, Box* rhs) { ...@@ -265,6 +279,8 @@ extern "C" Box* floatLe(BoxedFloat* lhs, Box* rhs) {
return floatLeInt(lhs, static_cast<BoxedInt*>(rhs)); return floatLeInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatLeFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatLeFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxBool(lhs->d <= PyLong_AsDouble(rhs));
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -288,6 +304,8 @@ extern "C" Box* floatGt(BoxedFloat* lhs, Box* rhs) { ...@@ -288,6 +304,8 @@ extern "C" Box* floatGt(BoxedFloat* lhs, Box* rhs) {
return floatGtInt(lhs, static_cast<BoxedInt*>(rhs)); return floatGtInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatGtFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatGtFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxBool(lhs->d > PyLong_AsDouble(rhs));
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -311,6 +329,8 @@ extern "C" Box* floatGe(BoxedFloat* lhs, Box* rhs) { ...@@ -311,6 +329,8 @@ extern "C" Box* floatGe(BoxedFloat* lhs, Box* rhs) {
return floatGeInt(lhs, static_cast<BoxedInt*>(rhs)); return floatGeInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatGeFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatGeFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxBool(lhs->d >= PyLong_AsDouble(rhs));
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -334,6 +354,8 @@ extern "C" Box* floatMod(BoxedFloat* lhs, Box* rhs) { ...@@ -334,6 +354,8 @@ extern "C" Box* floatMod(BoxedFloat* lhs, Box* rhs) {
return floatModInt(lhs, static_cast<BoxedInt*>(rhs)); return floatModInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatModFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatModFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxFloat(mod_float_float(lhs->d, PyLong_AsDouble(rhs)));
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -357,6 +379,8 @@ extern "C" Box* floatRMod(BoxedFloat* lhs, Box* rhs) { ...@@ -357,6 +379,8 @@ extern "C" Box* floatRMod(BoxedFloat* lhs, Box* rhs) {
return floatRModInt(lhs, static_cast<BoxedInt*>(rhs)); return floatRModInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatRModFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatRModFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxFloat(mod_float_float(PyLong_AsDouble(rhs), lhs->d));
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -380,6 +404,8 @@ extern "C" Box* floatPow(BoxedFloat* lhs, Box* rhs) { ...@@ -380,6 +404,8 @@ extern "C" Box* floatPow(BoxedFloat* lhs, Box* rhs) {
return floatPowInt(lhs, static_cast<BoxedInt*>(rhs)); return floatPowInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatPowFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatPowFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxFloat(pow(lhs->d, PyLong_AsDouble(rhs)));
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -403,6 +429,8 @@ extern "C" Box* floatMul(BoxedFloat* lhs, Box* rhs) { ...@@ -403,6 +429,8 @@ extern "C" Box* floatMul(BoxedFloat* lhs, Box* rhs) {
return floatMulInt(lhs, static_cast<BoxedInt*>(rhs)); return floatMulInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatMulFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatMulFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxFloat(lhs->d * PyLong_AsDouble(rhs));
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -426,6 +454,8 @@ extern "C" Box* floatSub(BoxedFloat* lhs, Box* rhs) { ...@@ -426,6 +454,8 @@ extern "C" Box* floatSub(BoxedFloat* lhs, Box* rhs) {
return floatSubInt(lhs, static_cast<BoxedInt*>(rhs)); return floatSubInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatSubFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatSubFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxFloat(lhs->d - PyLong_AsDouble(rhs));
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -449,6 +479,8 @@ extern "C" Box* floatRSub(BoxedFloat* lhs, Box* rhs) { ...@@ -449,6 +479,8 @@ extern "C" Box* floatRSub(BoxedFloat* lhs, Box* rhs) {
return floatRSubInt(lhs, static_cast<BoxedInt*>(rhs)); return floatRSubInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) { } else if (rhs->cls == float_cls) {
return floatRSubFloat(lhs, static_cast<BoxedFloat*>(rhs)); return floatRSubFloat(lhs, static_cast<BoxedFloat*>(rhs));
} else if (rhs->cls == long_cls) {
return boxFloat(PyLong_AsDouble(rhs) - lhs->d);
} else { } else {
return NotImplemented; return NotImplemented;
} }
...@@ -545,6 +577,9 @@ std::string floatFmt(double x, int precision, char code) { ...@@ -545,6 +577,9 @@ std::string floatFmt(double x, int precision, char code) {
} }
BoxedFloat* _floatNew(Box* a) { BoxedFloat* _floatNew(Box* a) {
// FIXME CPython uses PyUnicode_EncodeDecimal:
a = coerceUnicodeToStr(a);
if (a->cls == float_cls) { if (a->cls == float_cls) {
return static_cast<BoxedFloat*>(a); return static_cast<BoxedFloat*>(a);
} else if (isSubclass(a->cls, float_cls)) { } else if (isSubclass(a->cls, float_cls)) {
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include "gc/heap.h" #include "gc/heap.h"
#include "runtime/capi.h" #include "runtime/capi.h"
#include "runtime/classobj.h" #include "runtime/classobj.h"
#include "runtime/dict.h"
#include "runtime/file.h" #include "runtime/file.h"
#include "runtime/float.h" #include "runtime/float.h"
#include "runtime/generator.h" #include "runtime/generator.h"
...@@ -303,6 +304,9 @@ BoxedClass::BoxedClass(BoxedClass* base, gcvisit_func gc_visit, int attrs_offset ...@@ -303,6 +304,9 @@ BoxedClass::BoxedClass(BoxedClass* base, gcvisit_func gc_visit, int attrs_offset
tp_flags |= Py_TPFLAGS_HAVE_GC; tp_flags |= Py_TPFLAGS_HAVE_GC;
tp_flags |= Py_TPFLAGS_HAVE_WEAKREFS; tp_flags |= Py_TPFLAGS_HAVE_WEAKREFS;
if (base && (base->tp_flags & Py_TPFLAGS_HAVE_NEWBUFFER))
tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER;
tp_base = base; tp_base = base;
if (tp_base) { if (tp_base) {
...@@ -1940,7 +1944,7 @@ extern "C" BoxedInt* hash(Box* obj) { ...@@ -1940,7 +1944,7 @@ extern "C" BoxedInt* hash(Box* obj) {
Box* hash = getclsattr_internal(obj, "__hash__", NULL); Box* hash = getclsattr_internal(obj, "__hash__", NULL);
if (hash == NULL) { if (hash == NULL) {
ASSERT(isUserDefined(obj->cls), "%s.__hash__", getTypeName(obj)); ASSERT(isUserDefined(obj->cls) || obj->cls == function_cls, "%s.__hash__", getTypeName(obj));
// TODO not the best way to handle this... // TODO not the best way to handle this...
return static_cast<BoxedInt*>(boxInt((i64)obj)); return static_cast<BoxedInt*>(boxInt((i64)obj));
} }
...@@ -2693,7 +2697,13 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe ...@@ -2693,7 +2697,13 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe
Box* kwargs Box* kwargs
= getArg(argspec.num_args + argspec.num_keywords + (argspec.has_starargs ? 1 : 0), arg1, arg2, arg3, args); = getArg(argspec.num_args + argspec.num_keywords + (argspec.has_starargs ? 1 : 0), arg1, arg2, arg3, args);
RELEASE_ASSERT(kwargs->cls == dict_cls, "haven't implemented this for non-dicts");
if (!isSubclass(kwargs->cls, dict_cls)) {
BoxedDict* d = new BoxedDict();
dictMerge(d, kwargs);
kwargs = d;
}
assert(isSubclass(kwargs->cls, dict_cls));
BoxedDict* d_kwargs = static_cast<BoxedDict*>(kwargs); BoxedDict* d_kwargs = static_cast<BoxedDict*>(kwargs);
for (auto& p : d_kwargs->d) { for (auto& p : d_kwargs->d) {
...@@ -3288,6 +3298,7 @@ Box* compareInternal(Box* lhs, Box* rhs, int op_type, CompareRewriteArgs* rewrit ...@@ -3288,6 +3298,7 @@ Box* compareInternal(Box* lhs, Box* rhs, int op_type, CompareRewriteArgs* rewrit
#ifndef NDEBUG #ifndef NDEBUG
if ((lhs->cls == int_cls || lhs->cls == float_cls || lhs->cls == long_cls) if ((lhs->cls == int_cls || lhs->cls == float_cls || lhs->cls == long_cls)
&& (rhs->cls == int_cls || rhs->cls == float_cls || rhs->cls == long_cls)) { && (rhs->cls == int_cls || rhs->cls == float_cls || rhs->cls == long_cls)) {
printf("\n%s %s %s\n", lhs->cls->tp_name, op_name.c_str(), rhs->cls->tp_name);
Py_FatalError("missing comparison between these classes"); Py_FatalError("missing comparison between these classes");
} }
#endif #endif
......
...@@ -296,7 +296,7 @@ extern "C" PyObject* PyString_FromFormat(const char* format, ...) noexcept { ...@@ -296,7 +296,7 @@ extern "C" PyObject* PyString_FromFormat(const char* format, ...) noexcept {
} }
extern "C" Box* strAdd(BoxedString* lhs, Box* _rhs) { extern "C" Box* strAdd(BoxedString* lhs, Box* _rhs) {
assert(lhs->cls == str_cls); assert(isSubclass(lhs->cls, str_cls));
if (_rhs->cls == unicode_cls) { if (_rhs->cls == unicode_cls) {
Box* rtn = PyUnicode_Concat(lhs, _rhs); Box* rtn = PyUnicode_Concat(lhs, _rhs);
...@@ -934,7 +934,7 @@ extern "C" Box* strMod(BoxedString* lhs, Box* rhs) { ...@@ -934,7 +934,7 @@ extern "C" Box* strMod(BoxedString* lhs, Box* rhs) {
} }
extern "C" Box* strMul(BoxedString* lhs, Box* rhs) { extern "C" Box* strMul(BoxedString* lhs, Box* rhs) {
assert(lhs->cls == str_cls); assert(isSubclass(lhs->cls, str_cls));
int n; int n;
if (isSubclass(rhs->cls, int_cls)) if (isSubclass(rhs->cls, int_cls))
...@@ -952,7 +952,7 @@ extern "C" Box* strMul(BoxedString* lhs, Box* rhs) { ...@@ -952,7 +952,7 @@ extern "C" Box* strMul(BoxedString* lhs, Box* rhs) {
} }
extern "C" Box* strLt(BoxedString* lhs, Box* rhs) { extern "C" Box* strLt(BoxedString* lhs, Box* rhs) {
assert(lhs->cls == str_cls); assert(isSubclass(lhs->cls, str_cls));
if (rhs->cls != str_cls) if (rhs->cls != str_cls)
return NotImplemented; return NotImplemented;
...@@ -962,7 +962,7 @@ extern "C" Box* strLt(BoxedString* lhs, Box* rhs) { ...@@ -962,7 +962,7 @@ extern "C" Box* strLt(BoxedString* lhs, Box* rhs) {
} }
extern "C" Box* strLe(BoxedString* lhs, Box* rhs) { extern "C" Box* strLe(BoxedString* lhs, Box* rhs) {
assert(lhs->cls == str_cls); assert(isSubclass(lhs->cls, str_cls));
if (rhs->cls != str_cls) if (rhs->cls != str_cls)
return NotImplemented; return NotImplemented;
...@@ -972,7 +972,7 @@ extern "C" Box* strLe(BoxedString* lhs, Box* rhs) { ...@@ -972,7 +972,7 @@ extern "C" Box* strLe(BoxedString* lhs, Box* rhs) {
} }
extern "C" Box* strGt(BoxedString* lhs, Box* rhs) { extern "C" Box* strGt(BoxedString* lhs, Box* rhs) {
assert(lhs->cls == str_cls); assert(isSubclass(lhs->cls, str_cls));
if (rhs->cls != str_cls) if (rhs->cls != str_cls)
return NotImplemented; return NotImplemented;
...@@ -982,7 +982,7 @@ extern "C" Box* strGt(BoxedString* lhs, Box* rhs) { ...@@ -982,7 +982,7 @@ extern "C" Box* strGt(BoxedString* lhs, Box* rhs) {
} }
extern "C" Box* strGe(BoxedString* lhs, Box* rhs) { extern "C" Box* strGe(BoxedString* lhs, Box* rhs) {
assert(lhs->cls == str_cls); assert(isSubclass(lhs->cls, str_cls));
if (rhs->cls != str_cls) if (rhs->cls != str_cls)
return NotImplemented; return NotImplemented;
...@@ -992,7 +992,7 @@ extern "C" Box* strGe(BoxedString* lhs, Box* rhs) { ...@@ -992,7 +992,7 @@ extern "C" Box* strGe(BoxedString* lhs, Box* rhs) {
} }
extern "C" Box* strEq(BoxedString* lhs, Box* rhs) { extern "C" Box* strEq(BoxedString* lhs, Box* rhs) {
assert(lhs->cls == str_cls); assert(isSubclass(lhs->cls, str_cls));
if (rhs->cls != str_cls) if (rhs->cls != str_cls)
return NotImplemented; return NotImplemented;
...@@ -1002,7 +1002,7 @@ extern "C" Box* strEq(BoxedString* lhs, Box* rhs) { ...@@ -1002,7 +1002,7 @@ extern "C" Box* strEq(BoxedString* lhs, Box* rhs) {
} }
extern "C" Box* strNe(BoxedString* lhs, Box* rhs) { extern "C" Box* strNe(BoxedString* lhs, Box* rhs) {
assert(lhs->cls == str_cls); assert(isSubclass(lhs->cls, str_cls));
if (rhs->cls != str_cls) if (rhs->cls != str_cls)
return NotImplemented; return NotImplemented;
...@@ -1016,7 +1016,7 @@ extern "C" Box* strNe(BoxedString* lhs, Box* rhs) { ...@@ -1016,7 +1016,7 @@ extern "C" Box* strNe(BoxedString* lhs, Box* rhs) {
#define JUST_CENTER 2 #define JUST_CENTER 2
static Box* pad(BoxedString* self, Box* width, Box* fillchar, int justType) { static Box* pad(BoxedString* self, Box* width, Box* fillchar, int justType) {
assert(width->cls == int_cls); assert(width->cls == int_cls);
assert(fillchar->cls == str_cls); assert(isSubclass(fillchar->cls, str_cls));
assert(static_cast<BoxedString*>(fillchar)->s.size() == 1); assert(static_cast<BoxedString*>(fillchar)->s.size() == 1);
int64_t curWidth = self->s.size(); int64_t curWidth = self->s.size();
int64_t targetWidth = static_cast<BoxedInt*>(width)->n; int64_t targetWidth = static_cast<BoxedInt*>(width)->n;
...@@ -1067,13 +1067,13 @@ extern "C" Box* strCenter(BoxedString* lhs, Box* width, Box* fillchar) { ...@@ -1067,13 +1067,13 @@ extern "C" Box* strCenter(BoxedString* lhs, Box* width, Box* fillchar) {
} }
extern "C" Box* strLen(BoxedString* self) { extern "C" Box* strLen(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
return boxInt(self->s.size()); return boxInt(self->s.size());
} }
extern "C" Box* strStr(BoxedString* self) { extern "C" Box* strStr(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
return self; return self;
} }
...@@ -1099,7 +1099,7 @@ static char _hex[17] = "0123456789abcdef"; // really only needs to be 16 but cla ...@@ -1099,7 +1099,7 @@ static char _hex[17] = "0123456789abcdef"; // really only needs to be 16 but cla
extern "C" PyObject* PyString_Repr(PyObject* obj, int smartquotes) noexcept { extern "C" PyObject* PyString_Repr(PyObject* obj, int smartquotes) noexcept {
BoxedString* self = (BoxedString*)obj; BoxedString* self = (BoxedString*)obj;
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
std::ostringstream os(""); std::ostringstream os("");
...@@ -1333,22 +1333,28 @@ failed: ...@@ -1333,22 +1333,28 @@ failed:
} }
extern "C" Box* strHash(BoxedString* self) { extern "C" Box* strHash(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
std::hash<std::string> H; std::hash<std::string> H;
return boxInt(H(self->s)); return boxInt(H(self->s));
} }
extern "C" Box* strNonzero(BoxedString* self) { extern "C" Box* strNonzero(BoxedString* self) {
ASSERT(self->cls == str_cls, "%s", self->cls->tp_name); ASSERT(isSubclass(self->cls, str_cls), "%s", self->cls->tp_name);
return boxBool(self->s.size() != 0); return boxBool(self->s.size() != 0);
} }
extern "C" Box* strNew(BoxedClass* cls, Box* obj) { extern "C" Box* strNew(BoxedClass* cls, Box* obj) {
assert(cls == str_cls); assert(isSubclass(cls, str_cls));
return str(obj); Box* rtn = str(obj);
assert(rtn->cls == str_cls);
if (cls == str_cls)
return rtn;
return new (cls) BoxedString(static_cast<BoxedString*>(rtn)->s);
} }
extern "C" Box* basestringNew(BoxedClass* cls, Box* args, Box* kwargs) { extern "C" Box* basestringNew(BoxedClass* cls, Box* args, Box* kwargs) {
...@@ -1356,7 +1362,7 @@ extern "C" Box* basestringNew(BoxedClass* cls, Box* args, Box* kwargs) { ...@@ -1356,7 +1362,7 @@ extern "C" Box* basestringNew(BoxedClass* cls, Box* args, Box* kwargs) {
} }
Box* _strSlice(BoxedString* self, i64 start, i64 stop, i64 step, i64 length) { Box* _strSlice(BoxedString* self, i64 start, i64 stop, i64 step, i64 length) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
const std::string& s = self->s; const std::string& s = self->s;
...@@ -1378,7 +1384,7 @@ Box* _strSlice(BoxedString* self, i64 start, i64 stop, i64 step, i64 length) { ...@@ -1378,7 +1384,7 @@ Box* _strSlice(BoxedString* self, i64 start, i64 stop, i64 step, i64 length) {
} }
Box* strIsAlpha(BoxedString* self) { Box* strIsAlpha(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
const std::string& str(self->s); const std::string& str(self->s);
if (str.empty()) if (str.empty())
...@@ -1393,7 +1399,7 @@ Box* strIsAlpha(BoxedString* self) { ...@@ -1393,7 +1399,7 @@ Box* strIsAlpha(BoxedString* self) {
} }
Box* strIsDigit(BoxedString* self) { Box* strIsDigit(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
const std::string& str(self->s); const std::string& str(self->s);
if (str.empty()) if (str.empty())
...@@ -1408,7 +1414,7 @@ Box* strIsDigit(BoxedString* self) { ...@@ -1408,7 +1414,7 @@ Box* strIsDigit(BoxedString* self) {
} }
Box* strIsAlnum(BoxedString* self) { Box* strIsAlnum(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
const std::string& str(self->s); const std::string& str(self->s);
if (str.empty()) if (str.empty())
...@@ -1423,7 +1429,7 @@ Box* strIsAlnum(BoxedString* self) { ...@@ -1423,7 +1429,7 @@ Box* strIsAlnum(BoxedString* self) {
} }
Box* strIsLower(BoxedString* self) { Box* strIsLower(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
const std::string& str(self->s); const std::string& str(self->s);
bool lowered = false; bool lowered = false;
...@@ -1445,7 +1451,7 @@ Box* strIsLower(BoxedString* self) { ...@@ -1445,7 +1451,7 @@ Box* strIsLower(BoxedString* self) {
} }
Box* strIsUpper(BoxedString* self) { Box* strIsUpper(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
const std::string& str(self->s); const std::string& str(self->s);
bool uppered = false; bool uppered = false;
...@@ -1467,7 +1473,7 @@ Box* strIsUpper(BoxedString* self) { ...@@ -1467,7 +1473,7 @@ Box* strIsUpper(BoxedString* self) {
} }
Box* strIsSpace(BoxedString* self) { Box* strIsSpace(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
const std::string& str(self->s); const std::string& str(self->s);
if (str.empty()) if (str.empty())
...@@ -1482,7 +1488,7 @@ Box* strIsSpace(BoxedString* self) { ...@@ -1482,7 +1488,7 @@ Box* strIsSpace(BoxedString* self) {
} }
Box* strIsTitle(BoxedString* self) { Box* strIsTitle(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
const std::string& str(self->s); const std::string& str(self->s);
...@@ -1517,7 +1523,7 @@ Box* strIsTitle(BoxedString* self) { ...@@ -1517,7 +1523,7 @@ Box* strIsTitle(BoxedString* self) {
} }
Box* strJoin(BoxedString* self, Box* rhs) { Box* strJoin(BoxedString* self, Box* rhs) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
std::string output_str; std::string output_str;
llvm::raw_string_ostream os(output_str); llvm::raw_string_ostream os(output_str);
...@@ -1534,7 +1540,7 @@ Box* strJoin(BoxedString* self, Box* rhs) { ...@@ -1534,7 +1540,7 @@ Box* strJoin(BoxedString* self, Box* rhs) {
extern "C" PyObject* _PyString_Join(PyObject* sep, PyObject* x) noexcept { extern "C" PyObject* _PyString_Join(PyObject* sep, PyObject* x) noexcept {
try { try {
RELEASE_ASSERT(sep->cls == str_cls, ""); RELEASE_ASSERT(isSubclass(sep->cls, str_cls), "");
return strJoin((BoxedString*)sep, x); return strJoin((BoxedString*)sep, x);
} catch (ExcInfo e) { } catch (ExcInfo e) {
setCAPIException(e); setCAPIException(e);
...@@ -1548,6 +1554,11 @@ Box* strReplace(Box* _self, Box* _old, Box* _new, Box** _args) { ...@@ -1548,6 +1554,11 @@ Box* strReplace(Box* _self, Box* _old, Box* _new, Box** _args) {
getTypeName(_self)); getTypeName(_self));
BoxedString* self = static_cast<BoxedString*>(_self); BoxedString* self = static_cast<BoxedString*>(_self);
#ifdef Py_USING_UNICODE
if (PyUnicode_Check(_old) || PyUnicode_Check(_new))
return PyUnicode_Replace((PyObject*)self, _old, _new, -1 /*count*/);
#endif
if (_old->cls != str_cls) if (_old->cls != str_cls)
raiseExcHelper(TypeError, "expected a character buffer object"); raiseExcHelper(TypeError, "expected a character buffer object");
BoxedString* old = static_cast<BoxedString*>(_old); BoxedString* old = static_cast<BoxedString*>(_old);
...@@ -1574,8 +1585,8 @@ Box* strReplace(Box* _self, Box* _old, Box* _new, Box** _args) { ...@@ -1574,8 +1585,8 @@ Box* strReplace(Box* _self, Box* _old, Box* _new, Box** _args) {
} }
Box* strPartition(BoxedString* self, BoxedString* sep) { Box* strPartition(BoxedString* self, BoxedString* sep) {
RELEASE_ASSERT(self->cls == str_cls, ""); RELEASE_ASSERT(isSubclass(self->cls, str_cls), "");
RELEASE_ASSERT(sep->cls == str_cls, ""); RELEASE_ASSERT(isSubclass(sep->cls, str_cls), "");
size_t found_idx = self->s.find(sep->s); size_t found_idx = self->s.find(sep->s);
if (found_idx == std::string::npos) if (found_idx == std::string::npos)
...@@ -1601,11 +1612,11 @@ Box* strFormat(BoxedString* self, BoxedTuple* args, BoxedDict* kwargs) { ...@@ -1601,11 +1612,11 @@ Box* strFormat(BoxedString* self, BoxedTuple* args, BoxedDict* kwargs) {
} }
Box* strSplit(BoxedString* self, BoxedString* sep, BoxedInt* _max_split) { Box* strSplit(BoxedString* self, BoxedString* sep, BoxedInt* _max_split) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
if (_max_split->cls != int_cls) if (_max_split->cls != int_cls)
raiseExcHelper(TypeError, "an integer is required"); raiseExcHelper(TypeError, "an integer is required");
if (sep->cls == str_cls) { if (isSubclass(sep->cls, str_cls)) {
if (!sep->s.empty()) { if (!sep->s.empty()) {
llvm::SmallVector<llvm::StringRef, 16> parts; llvm::SmallVector<llvm::StringRef, 16> parts;
llvm::StringRef(self->s).split(parts, sep->s, _max_split->n); llvm::StringRef(self->s).split(parts, sep->s, _max_split->n);
...@@ -1642,9 +1653,9 @@ Box* strSplit(BoxedString* self, BoxedString* sep, BoxedInt* _max_split) { ...@@ -1642,9 +1653,9 @@ Box* strSplit(BoxedString* self, BoxedString* sep, BoxedInt* _max_split) {
} }
Box* strStrip(BoxedString* self, Box* chars) { Box* strStrip(BoxedString* self, Box* chars) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
if (chars->cls == str_cls) { if (isSubclass(chars->cls, str_cls)) {
return new BoxedString(llvm::StringRef(self->s).trim(static_cast<BoxedString*>(chars)->s)); return new BoxedString(llvm::StringRef(self->s).trim(static_cast<BoxedString*>(chars)->s));
} else if (chars->cls == none_cls) { } else if (chars->cls == none_cls) {
return new BoxedString(llvm::StringRef(self->s).trim(" \t\n\r\f\v")); return new BoxedString(llvm::StringRef(self->s).trim(" \t\n\r\f\v"));
...@@ -1654,9 +1665,9 @@ Box* strStrip(BoxedString* self, Box* chars) { ...@@ -1654,9 +1665,9 @@ Box* strStrip(BoxedString* self, Box* chars) {
} }
Box* strLStrip(BoxedString* self, Box* chars) { Box* strLStrip(BoxedString* self, Box* chars) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
if (chars->cls == str_cls) { if (isSubclass(chars->cls, str_cls)) {
return new BoxedString(llvm::StringRef(self->s).ltrim(static_cast<BoxedString*>(chars)->s)); return new BoxedString(llvm::StringRef(self->s).ltrim(static_cast<BoxedString*>(chars)->s));
} else if (chars->cls == none_cls) { } else if (chars->cls == none_cls) {
return new BoxedString(llvm::StringRef(self->s).ltrim(" \t\n\r\f\v")); return new BoxedString(llvm::StringRef(self->s).ltrim(" \t\n\r\f\v"));
...@@ -1666,9 +1677,9 @@ Box* strLStrip(BoxedString* self, Box* chars) { ...@@ -1666,9 +1677,9 @@ Box* strLStrip(BoxedString* self, Box* chars) {
} }
Box* strRStrip(BoxedString* self, Box* chars) { Box* strRStrip(BoxedString* self, Box* chars) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
if (chars->cls == str_cls) { if (isSubclass(chars->cls, str_cls)) {
return new BoxedString(llvm::StringRef(self->s).rtrim(static_cast<BoxedString*>(chars)->s)); return new BoxedString(llvm::StringRef(self->s).rtrim(static_cast<BoxedString*>(chars)->s));
} else if (chars->cls == none_cls) { } else if (chars->cls == none_cls) {
return new BoxedString(llvm::StringRef(self->s).rtrim(" \t\n\r\f\v")); return new BoxedString(llvm::StringRef(self->s).rtrim(" \t\n\r\f\v"));
...@@ -1678,7 +1689,7 @@ Box* strRStrip(BoxedString* self, Box* chars) { ...@@ -1678,7 +1689,7 @@ Box* strRStrip(BoxedString* self, Box* chars) {
} }
Box* strCapitalize(BoxedString* self) { Box* strCapitalize(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
std::string s(self->s); std::string s(self->s);
...@@ -1694,7 +1705,7 @@ Box* strCapitalize(BoxedString* self) { ...@@ -1694,7 +1705,7 @@ Box* strCapitalize(BoxedString* self) {
} }
Box* strTitle(BoxedString* self) { Box* strTitle(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
std::string s(self->s); std::string s(self->s);
bool start_of_word = false; bool start_of_word = false;
...@@ -1746,12 +1757,12 @@ Box* strTranslate(BoxedString* self, BoxedString* table, BoxedString* delete_cha ...@@ -1746,12 +1757,12 @@ Box* strTranslate(BoxedString* self, BoxedString* table, BoxedString* delete_cha
} }
Box* strLower(BoxedString* self) { Box* strLower(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
return boxString(llvm::StringRef(self->s).lower()); return boxString(llvm::StringRef(self->s).lower());
} }
Box* strUpper(BoxedString* self) { Box* strUpper(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
return boxString(llvm::StringRef(self->s).upper()); return boxString(llvm::StringRef(self->s).upper());
} }
...@@ -1769,7 +1780,7 @@ Box* strSwapcase(BoxedString* self) { ...@@ -1769,7 +1780,7 @@ Box* strSwapcase(BoxedString* self) {
} }
Box* strContains(BoxedString* self, Box* elt) { Box* strContains(BoxedString* self, Box* elt) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
if (PyUnicode_Check(elt)) { if (PyUnicode_Check(elt)) {
int r = PyUnicode_Contains(self, elt); int r = PyUnicode_Contains(self, elt);
...@@ -1796,9 +1807,6 @@ Box* strStartswith(BoxedString* self, Box* elt, Box* start, Box** _args) { ...@@ -1796,9 +1807,6 @@ Box* strStartswith(BoxedString* self, Box* elt, Box* start, Box** _args) {
raiseExcHelper(TypeError, "descriptor 'startswith' requires a 'str' object but received a '%s'", raiseExcHelper(TypeError, "descriptor 'startswith' requires a 'str' object but received a '%s'",
getTypeName(self)); getTypeName(self));
if (elt->cls != str_cls)
raiseExcHelper(TypeError, "expected a character buffer object");
Py_ssize_t istart = 0, iend = PY_SSIZE_T_MAX; Py_ssize_t istart = 0, iend = PY_SSIZE_T_MAX;
if (start) { if (start) {
int r = _PyEval_SliceIndex(start, &istart); int r = _PyEval_SliceIndex(start, &istart);
...@@ -1812,6 +1820,27 @@ Box* strStartswith(BoxedString* self, Box* elt, Box* start, Box** _args) { ...@@ -1812,6 +1820,27 @@ Box* strStartswith(BoxedString* self, Box* elt, Box* start, Box** _args) {
throwCAPIException(); throwCAPIException();
} }
if (isSubclass(elt->cls, tuple_cls)) {
for (auto e : static_cast<BoxedTuple*>(elt)->elts) {
auto b = strStartswith(self, e, start, _args);
assert(b->cls == bool_cls);
if (b == True)
return True;
}
return False;
}
if (isSubclass(elt->cls, unicode_cls)) {
int r = PyUnicode_Tailmatch(self, elt, istart, iend, -1);
if (r < 0)
throwCAPIException();
assert(r == 0 || r == 1);
return boxBool(r);
}
if (elt->cls != str_cls)
raiseExcHelper(TypeError, "expected a character buffer object");
BoxedString* sub = static_cast<BoxedString*>(elt); BoxedString* sub = static_cast<BoxedString*>(elt);
Py_ssize_t n = self->s.size(); Py_ssize_t n = self->s.size();
...@@ -1841,9 +1870,6 @@ Box* strEndswith(BoxedString* self, Box* elt, Box* start, Box** _args) { ...@@ -1841,9 +1870,6 @@ Box* strEndswith(BoxedString* self, Box* elt, Box* start, Box** _args) {
raiseExcHelper(TypeError, "descriptor 'endswith' requires a 'str' object but received a '%s'", raiseExcHelper(TypeError, "descriptor 'endswith' requires a 'str' object but received a '%s'",
getTypeName(self)); getTypeName(self));
if (elt->cls != str_cls)
raiseExcHelper(TypeError, "expected a character buffer object");
Py_ssize_t istart = 0, iend = PY_SSIZE_T_MAX; Py_ssize_t istart = 0, iend = PY_SSIZE_T_MAX;
if (start) { if (start) {
int r = _PyEval_SliceIndex(start, &istart); int r = _PyEval_SliceIndex(start, &istart);
...@@ -1857,6 +1883,27 @@ Box* strEndswith(BoxedString* self, Box* elt, Box* start, Box** _args) { ...@@ -1857,6 +1883,27 @@ Box* strEndswith(BoxedString* self, Box* elt, Box* start, Box** _args) {
throwCAPIException(); throwCAPIException();
} }
if (isSubclass(elt->cls, unicode_cls)) {
int r = PyUnicode_Tailmatch(self, elt, istart, iend, +1);
if (r < 0)
throwCAPIException();
assert(r == 0 || r == 1);
return boxBool(r);
}
if (isSubclass(elt->cls, tuple_cls)) {
for (auto e : static_cast<BoxedTuple*>(elt)->elts) {
auto b = strEndswith(self, e, start, _args);
assert(b->cls == bool_cls);
if (b == True)
return True;
}
return False;
}
if (elt->cls != str_cls)
raiseExcHelper(TypeError, "expected a character buffer object");
BoxedString* sub = static_cast<BoxedString*>(elt); BoxedString* sub = static_cast<BoxedString*>(elt);
Py_ssize_t n = self->s.size(); Py_ssize_t n = self->s.size();
...@@ -1920,7 +1967,7 @@ Box* strEncode(BoxedString* self, Box* encoding, Box* error) { ...@@ -1920,7 +1967,7 @@ Box* strEncode(BoxedString* self, Box* encoding, Box* error) {
} }
extern "C" Box* strGetitem(BoxedString* self, Box* slice) { extern "C" Box* strGetitem(BoxedString* self, Box* slice) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
if (isSubclass(slice->cls, int_cls)) { if (isSubclass(slice->cls, int_cls)) {
BoxedInt* islice = static_cast<BoxedInt*>(slice); BoxedInt* islice = static_cast<BoxedInt*>(slice);
...@@ -1989,12 +2036,12 @@ extern "C" void strIteratorGCHandler(GCVisitor* v, Box* b) { ...@@ -1989,12 +2036,12 @@ extern "C" void strIteratorGCHandler(GCVisitor* v, Box* b) {
} }
Box* strIter(BoxedString* self) { Box* strIter(BoxedString* self) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
return new BoxedStringIterator(self); return new BoxedStringIterator(self);
} }
int64_t strCount2Unboxed(BoxedString* self, Box* elt) { int64_t strCount2Unboxed(BoxedString* self, Box* elt) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
if (elt->cls != str_cls) if (elt->cls != str_cls)
raiseExcHelper(TypeError, "expected a character buffer object"); raiseExcHelper(TypeError, "expected a character buffer object");
...@@ -2020,7 +2067,7 @@ Box* strCount2(BoxedString* self, Box* elt) { ...@@ -2020,7 +2067,7 @@ Box* strCount2(BoxedString* self, Box* elt) {
} }
Box* strIndex(BoxedString* self, Box* elt) { Box* strIndex(BoxedString* self, Box* elt) {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
if (elt->cls != str_cls) if (elt->cls != str_cls)
raiseExcHelper(TypeError, "expected a character buffer object"); raiseExcHelper(TypeError, "expected a character buffer object");
...@@ -2094,14 +2141,14 @@ extern "C" PyObject* PyString_FromStringAndSize(const char* s, ssize_t n) noexce ...@@ -2094,14 +2141,14 @@ extern "C" PyObject* PyString_FromStringAndSize(const char* s, ssize_t n) noexce
} }
extern "C" char* PyString_AsString(PyObject* o) noexcept { extern "C" char* PyString_AsString(PyObject* o) noexcept {
RELEASE_ASSERT(o->cls == str_cls, ""); RELEASE_ASSERT(isSubclass(o->cls, str_cls), "");
BoxedString* s = static_cast<BoxedString*>(o); BoxedString* s = static_cast<BoxedString*>(o);
return getWriteableStringContents(s); return getWriteableStringContents(s);
} }
extern "C" Py_ssize_t PyString_Size(PyObject* op) noexcept { extern "C" Py_ssize_t PyString_Size(PyObject* op) noexcept {
if (op->cls == str_cls) if (isSubclass(op->cls, str_cls))
return static_cast<BoxedString*>(op)->s.size(); return static_cast<BoxedString*>(op)->s.size();
char* _s; char* _s;
...@@ -2115,7 +2162,7 @@ extern "C" int _PyString_Resize(PyObject** pv, Py_ssize_t newsize) noexcept { ...@@ -2115,7 +2162,7 @@ extern "C" int _PyString_Resize(PyObject** pv, Py_ssize_t newsize) noexcept {
// This is only allowed to be called when there is only one user of the string (ie a refcount of 1 in CPython) // This is only allowed to be called when there is only one user of the string (ie a refcount of 1 in CPython)
assert(pv); assert(pv);
assert((*pv)->cls == str_cls); assert(isSubclass((*pv)->cls, str_cls));
BoxedString* s = static_cast<BoxedString*>(*pv); BoxedString* s = static_cast<BoxedString*>(*pv);
s->s.resize(newsize, '\0'); s->s.resize(newsize, '\0');
return 0; return 0;
...@@ -2223,7 +2270,7 @@ static Py_ssize_t string_buffer_getreadbuf(PyObject* self, Py_ssize_t index, con ...@@ -2223,7 +2270,7 @@ static Py_ssize_t string_buffer_getreadbuf(PyObject* self, Py_ssize_t index, con
RELEASE_ASSERT(index == 0, ""); RELEASE_ASSERT(index == 0, "");
// I think maybe this can just be a non-release assert? shouldn't be able to call this with // I think maybe this can just be a non-release assert? shouldn't be able to call this with
// the wrong type // the wrong type
RELEASE_ASSERT(self->cls == str_cls, ""); RELEASE_ASSERT(isSubclass(self->cls, str_cls), "");
auto s = static_cast<BoxedString*>(self); auto s = static_cast<BoxedString*>(self);
*ptr = s->s.c_str(); *ptr = s->s.c_str();
...@@ -2232,7 +2279,7 @@ static Py_ssize_t string_buffer_getreadbuf(PyObject* self, Py_ssize_t index, con ...@@ -2232,7 +2279,7 @@ static Py_ssize_t string_buffer_getreadbuf(PyObject* self, Py_ssize_t index, con
static Py_ssize_t string_buffer_getsegcount(PyObject* o, Py_ssize_t* lenp) noexcept { static Py_ssize_t string_buffer_getsegcount(PyObject* o, Py_ssize_t* lenp) noexcept {
RELEASE_ASSERT(lenp == NULL, ""); RELEASE_ASSERT(lenp == NULL, "");
RELEASE_ASSERT(o->cls == str_cls, ""); RELEASE_ASSERT(isSubclass(o->cls, str_cls), "");
return 1; return 1;
} }
...@@ -2246,7 +2293,7 @@ static Py_ssize_t string_buffer_getcharbuf(PyStringObject* self, Py_ssize_t inde ...@@ -2246,7 +2293,7 @@ static Py_ssize_t string_buffer_getcharbuf(PyStringObject* self, Py_ssize_t inde
} }
static int string_buffer_getbuffer(BoxedString* self, Py_buffer* view, int flags) noexcept { static int string_buffer_getbuffer(BoxedString* self, Py_buffer* view, int flags) noexcept {
assert(self->cls == str_cls); assert(isSubclass(self->cls, str_cls));
return PyBuffer_FillInfo(view, (PyObject*)self, &self->s[0], self->s.size(), 1, flags); return PyBuffer_FillInfo(view, (PyObject*)self, &self->s[0], self->s.size(), 1, flags);
} }
...@@ -2358,7 +2405,7 @@ void setupStr() { ...@@ -2358,7 +2405,7 @@ void setupStr() {
str_cls->giveAttr("join", new BoxedFunction(boxRTFunction((void*)strJoin, STR, 2))); str_cls->giveAttr("join", new BoxedFunction(boxRTFunction((void*)strJoin, STR, 2)));
str_cls->giveAttr("replace", str_cls->giveAttr("replace",
new BoxedFunction(boxRTFunction((void*)strReplace, STR, 4, 1, false, false), { boxInt(-1) })); new BoxedFunction(boxRTFunction((void*)strReplace, UNKNOWN, 4, 1, false, false), { boxInt(-1) }));
str_cls->giveAttr( str_cls->giveAttr(
"split", new BoxedFunction(boxRTFunction((void*)strSplit, LIST, 3, 2, false, false), { None, boxInt(-1) })); "split", new BoxedFunction(boxRTFunction((void*)strSplit, LIST, 3, 2, false, false), { None, boxInt(-1) }));
......
...@@ -1159,10 +1159,9 @@ Box* objectStr(Box* obj) { ...@@ -1159,10 +1159,9 @@ Box* objectStr(Box* obj) {
} }
static Box* typeName(Box* b, void*) { static Box* typeName(Box* b, void*) {
assert(b->cls == type_cls); RELEASE_ASSERT(isSubclass(b->cls, type_cls), "");
BoxedClass* type = static_cast<BoxedClass*>(b); BoxedClass* type = static_cast<BoxedClass*>(b);
// TODO is this predicate right?
if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) { if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) {
BoxedHeapClass* et = static_cast<BoxedHeapClass*>(type); BoxedHeapClass* et = static_cast<BoxedHeapClass*>(type);
return et->ht_name; return et->ht_name;
...@@ -1321,6 +1320,8 @@ void setupRuntime() { ...@@ -1321,6 +1320,8 @@ void setupRuntime() {
tuple_cls->finishInitialization(); tuple_cls->finishInitialization();
list_cls->finishInitialization(); list_cls->finishInitialization();
str_cls->tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER;
module_cls = BoxedHeapClass::create(type_cls, object_cls, NULL, offsetof(BoxedModule, attrs), 0, module_cls = BoxedHeapClass::create(type_cls, object_cls, NULL, offsetof(BoxedModule, attrs), 0,
...@@ -1516,6 +1517,7 @@ void setupRuntime() { ...@@ -1516,6 +1517,7 @@ void setupRuntime() {
setupPyston(); setupPyston();
PyType_Ready(&PyCapsule_Type); PyType_Ready(&PyCapsule_Type);
PyType_Ready(&PyCallIter_Type);
initerrno(); initerrno();
init_sha(); init_sha();
......
...@@ -15,3 +15,10 @@ def f2(): ...@@ -15,3 +15,10 @@ def f2():
print list(g2) print list(g2)
f2() f2()
# Make sure that the 'ifs' part gets scoped properly
def f3():
b = True
print list(x for x in range(5) if b)
print list(x for x in range(5) if [b for b in xrange(4)])
print b
print f3()
# allow-warning: import level 0 will be treated as -1
import gzip import gzip
import io import io
......
# allow-warning: import level 0 will be treated as -1
import io import io
filename = "io_test_.txt" filename = "io_test_.txt"
......
# expected: fail
# - not supported yet
class C(object): class C(object):
def __len__(self): def __len__(self):
print "__len__" print "__len__"
...@@ -26,3 +23,20 @@ def f(a, b, c): ...@@ -26,3 +23,20 @@ def f(a, b, c):
print a, b, c print a, b, c
f(**C()) f(**C())
class MyDict(dict):
pass
d = MyDict(a=1, b=2, c=3)
print f(**d)
# Django does this:
class C(object):
pass
c = C()
c.a = 1
c.b = 3
c.c = 7
print f(**c.__dict__)
...@@ -8,7 +8,7 @@ for i in xrange(150): ...@@ -8,7 +8,7 @@ for i in xrange(150):
print t, repr(t) print t, repr(t)
def test(a, b): def test(a, b):
print a, b print repr(a), repr(b)
print a + b, b + a, a.__add__(b), b.__add__(a) print a + b, b + a, a.__add__(b), b.__add__(a)
print a - b, b - a, a.__sub__(b), b.__sub__(a) print a - b, b - a, a.__sub__(b), b.__sub__(a)
print a * b, b * a, a.__mul__(b), b.__mul__(a) print a * b, b * a, a.__mul__(b), b.__mul__(a)
...@@ -26,6 +26,9 @@ for a in [-5, -1, 1, 5, -2L, -1L, 1L, 2L]: ...@@ -26,6 +26,9 @@ for a in [-5, -1, 1, 5, -2L, -1L, 1L, 2L]:
for b in [-5, -1, 1, 5, -2L, -1L, 1L, 2L]: for b in [-5, -1, 1, 5, -2L, -1L, 1L, 2L]:
test(a, b) test(a, b)
test(1L, 2.0)
test(3.0, 2L)
print (2L).__rdiv__(-1) print (2L).__rdiv__(-1)
print (2L).__rdiv__(-1L) print (2L).__rdiv__(-1L)
print (-2L).__rdiv__(1L) print (-2L).__rdiv__(1L)
......
# allow-warning: import level 0 will be treated as -1
# Simple optparse test, taken from the optparse.py docstring: # Simple optparse test, taken from the optparse.py docstring:
from optparse import OptionParser from optparse import OptionParser
......
# allow-warning: import level 0 will be treated as -1
import pickle import pickle
l = [[], (123,)] l = [[], (123,)]
......
# allow-warning: import level 0 will be treated as -1
# skip-if: '-x' in EXTRA_JIT_ARGS # skip-if: '-x' in EXTRA_JIT_ARGS
def f(a): def f(a):
......
# allow-warning: import level 0 will be treated as -1!
def test(string, encoding): def test(string, encoding):
s = string.encode(encoding) s = string.encode(encoding)
print encoding, s print encoding, s
......
...@@ -139,3 +139,6 @@ except: ...@@ -139,3 +139,6 @@ except:
print repr("hello\tworld\t".expandtabs()) print repr("hello\tworld\t".expandtabs())
print repr("hello\tworld\t".expandtabs(12)) print repr("hello\tworld\t".expandtabs(12))
print "hello world".startswith(("x", "h"))
print "hello world".endswith(("x", "h"))
class MyStr(str):
pass
s = MyStr(1)
print repr(s)
import sys
sys.stdout.write(s)
# no-collect-stats
import sys
sys.exit(None)
# skip-if: '-x' in EXTRA_JIT_ARGS # skip-if: '-x' in EXTRA_JIT_ARGS
# allow-warning: import level 0 will be treated as -1
print repr(unicode()) print repr(unicode())
print repr(unicode('hello world')) print repr(unicode('hello world'))
...@@ -81,3 +80,12 @@ f(**{'a':2}) ...@@ -81,3 +80,12 @@ f(**{'a':2})
f(**{u'a':3}) f(**{u'a':3})
print repr('%s' % u'') # this gives a unicode object! print repr('%s' % u'') # this gives a unicode object!
print repr('hello world'.replace(u'hello', u'hi'))
print "hello world".endswith(u'hello')
print "hello world".endswith(u'world')
print "hello world".startswith(u'hello')
print "hello world".startswith(u'world')
print float(u'1.0')
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment