ext/Objects/codeobject.cpp (226 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include <cmath>
#include "cpython-data.h"
#include "cpython-func.h"
#include "api-handle.h"
#include "runtime.h"
#include "set-builtins.h"
#include "tuple-builtins.h"
namespace py {
static_assert(RawCode::Flags::kOptimized == CO_OPTIMIZED, "");
static_assert(RawCode::Flags::kNewlocals == CO_NEWLOCALS, "");
static_assert(RawCode::Flags::kVarargs == CO_VARARGS, "");
static_assert(RawCode::Flags::kVarkeyargs == CO_VARKEYWORDS, "");
static_assert(RawCode::Flags::kNested == CO_NESTED, "");
static_assert(RawCode::Flags::kGenerator == CO_GENERATOR, "");
static_assert(RawCode::Flags::kNofree == CO_NOFREE, "");
static_assert(RawCode::Flags::kCoroutine == CO_COROUTINE, "");
static_assert(RawCode::Flags::kIterableCoroutine == CO_ITERABLE_COROUTINE, "");
static_assert(RawCode::Flags::kAsyncGenerator == CO_ASYNC_GENERATOR, "");
static_assert(RawCode::Flags::kFutureDivision == CO_FUTURE_DIVISION, "");
static_assert(RawCode::Flags::kFutureAbsoluteImport ==
CO_FUTURE_ABSOLUTE_IMPORT,
"");
static_assert(RawCode::Flags::kFutureWithStatement == CO_FUTURE_WITH_STATEMENT,
"");
static_assert(RawCode::Flags::kFuturePrintFunction == CO_FUTURE_PRINT_FUNCTION,
"");
static_assert(RawCode::Flags::kFutureUnicodeLiterals ==
CO_FUTURE_UNICODE_LITERALS,
"");
static_assert(RawCode::Flags::kFutureBarryAsBdfl == CO_FUTURE_BARRY_AS_BDFL,
"");
static_assert(RawCode::Flags::kFutureGeneratorStop == CO_FUTURE_GENERATOR_STOP,
"");
PY_EXPORT int PyCode_Check_Func(PyObject* obj) {
return ApiHandle::fromPyObject(obj)->asObject().isCode();
}
PY_EXPORT PyCodeObject* PyCode_NewWithPosOnlyArgs(
int argcount, int posonlyargcount, int kwonlyargcount, int nlocals,
int stacksize, int flags, PyObject* code, PyObject* consts, PyObject* names,
PyObject* varnames, PyObject* freevars, PyObject* cellvars,
PyObject* filename, PyObject* name, int firstlineno, PyObject* lnotab) {
Thread* thread = Thread::current();
if (argcount < 0 || posonlyargcount < 0 || kwonlyargcount < 0 ||
nlocals < 0 || code == nullptr || consts == nullptr || names == nullptr ||
varnames == nullptr || freevars == nullptr || cellvars == nullptr ||
name == nullptr || filename == nullptr || lnotab == nullptr) {
thread->raiseBadInternalCall();
return nullptr;
}
HandleScope scope(thread);
Object consts_obj(&scope, ApiHandle::fromPyObject(consts)->asObject());
Object names_obj(&scope, ApiHandle::fromPyObject(names)->asObject());
Object varnames_obj(&scope, ApiHandle::fromPyObject(varnames)->asObject());
Object freevars_obj(&scope, ApiHandle::fromPyObject(freevars)->asObject());
Object cellvars_obj(&scope, ApiHandle::fromPyObject(cellvars)->asObject());
Object name_obj(&scope, ApiHandle::fromPyObject(name)->asObject());
Object filename_obj(&scope, ApiHandle::fromPyObject(filename)->asObject());
Object lnotab_obj(&scope, ApiHandle::fromPyObject(lnotab)->asObject());
Object code_obj(&scope, ApiHandle::fromPyObject(code)->asObject());
Runtime* runtime = thread->runtime();
// Check argument types
// TODO(emacs): Call equivalent of PyObject_CheckReadBuffer(code) instead of
// isInstanceOfBytes
if (!runtime->isInstanceOfBytes(*code_obj) ||
!runtime->isInstanceOfTuple(*consts_obj) ||
!runtime->isInstanceOfTuple(*names_obj) ||
!runtime->isInstanceOfTuple(*varnames_obj) ||
!runtime->isInstanceOfTuple(*freevars_obj) ||
!runtime->isInstanceOfTuple(*cellvars_obj) ||
!runtime->isInstanceOfStr(*name_obj) ||
!runtime->isInstanceOfStr(*filename_obj) ||
!runtime->isInstanceOfBytes(*lnotab_obj)) {
thread->raiseBadInternalCall();
return nullptr;
}
return reinterpret_cast<PyCodeObject*>(ApiHandle::newReferenceWithManaged(
runtime,
runtime->newCode(argcount, posonlyargcount, kwonlyargcount, nlocals,
stacksize, flags, code_obj, consts_obj, names_obj,
varnames_obj, freevars_obj, cellvars_obj, filename_obj,
name_obj, firstlineno, lnotab_obj)));
}
PY_EXPORT PyCodeObject* PyCode_New(int argcount, int kwonlyargcount,
int nlocals, int stacksize, int flags,
PyObject* code, PyObject* consts,
PyObject* names, PyObject* varnames,
PyObject* freevars, PyObject* cellvars,
PyObject* filename, PyObject* name,
int firstlineno, PyObject* lnotab) {
return PyCode_NewWithPosOnlyArgs(
argcount, /*posonlyargcount=*/0, kwonlyargcount, nlocals, stacksize,
flags, code, consts, names, varnames, freevars, cellvars, filename, name,
firstlineno, lnotab);
}
PY_EXPORT PyCodeObject* PyCode_NewEmpty(const char* filename,
const char* funcname, int firstlineno) {
Thread* thread = Thread::current();
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object empty_bytes(&scope, Bytes::empty());
Object empty_tuple(&scope, runtime->emptyTuple());
Object filename_obj(&scope, Runtime::internStrFromCStr(thread, filename));
Object name_obj(&scope, Runtime::internStrFromCStr(thread, funcname));
return reinterpret_cast<PyCodeObject*>(ApiHandle::newReferenceWithManaged(
runtime, runtime->newCode(/*argcount=*/0,
/*posonlyargcount=*/0,
/*kwonlyargcount=*/0,
/*nlocals=*/0,
/*stacksize=*/0,
/*flags=*/0,
/*code=*/empty_bytes,
/*consts=*/empty_tuple,
/*names=*/empty_tuple,
/*varnames=*/empty_tuple,
/*freevars=*/empty_tuple,
/*cellvars=*/empty_tuple,
/*filename=*/filename_obj,
/*name=*/name_obj,
/*firstlineno=*/firstlineno,
/*lnotab=*/empty_bytes)));
}
PY_EXPORT PyTypeObject* PyCode_Type_Ptr() {
Runtime* runtime = Thread::current()->runtime();
return reinterpret_cast<PyTypeObject*>(
ApiHandle::borrowedReference(runtime, runtime->typeAt(LayoutId::kCode)));
}
PY_EXPORT Py_ssize_t PyCode_GetNumFree_Func(PyObject* code) {
DCHECK(code != nullptr, "code must not be null");
Thread* thread = Thread::current();
HandleScope scope(thread);
Object code_obj(&scope, ApiHandle::fromPyObject(code)->asObject());
DCHECK(code_obj.isCode(), "code must be a code object");
Code code_code(&scope, *code_obj);
Tuple freevars(&scope, code_code.freevars());
return freevars.length();
}
static RawObject constantKey(Thread* thread, const Object& obj) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
if (obj.isNoneType() || obj.isEllipsis() || obj.isSmallInt() ||
obj.isLargeInt() || obj.isStr() || obj.isCode()) {
return *obj;
}
if (obj.isBool() || obj.isBytes()) {
Object type(&scope, runtime->typeOf(*obj));
return runtime->newTupleWith2(type, obj);
}
if (obj.isFloat()) {
double d = Float::cast(*obj).value();
Object type(&scope, runtime->typeOf(*obj));
if (d == 0.0 && std::signbit(d)) {
Object none(&scope, NoneType::object());
return runtime->newTupleWith3(type, obj, none);
}
return runtime->newTupleWith2(type, obj);
}
if (obj.isComplex()) {
Complex c(&scope, *obj);
Py_complex z;
z.real = c.real();
z.imag = c.imag();
// For the complex case we must make complex(x, 0.)
// different from complex(x, -0.) and complex(0., y)
// different from complex(-0., y), for any x and y.
// All four complex zeros must be distinguished.
bool real_negzero = z.real == 0.0 && std::signbit(z.real);
bool imag_negzero = z.imag == 0.0 && std::signbit(z.imag);
// use True, False and None singleton as tags for the real and imag sign,
// to make tuples different
Object type(&scope, runtime->typeOf(*obj));
if (real_negzero && imag_negzero) {
Object tru(&scope, Bool::trueObj());
return runtime->newTupleWith3(type, obj, tru);
}
if (imag_negzero) {
Object fals(&scope, Bool::falseObj());
return runtime->newTupleWith3(type, obj, fals);
}
if (real_negzero) {
Object none(&scope, NoneType::object());
return runtime->newTupleWith3(type, obj, none);
}
return runtime->newTupleWith2(type, obj);
}
if (obj.isTuple()) {
Tuple tuple(&scope, *obj);
Object result_obj(&scope, NoneType::object());
word length = tuple.length();
if (length > 0) {
MutableTuple result(&scope, runtime->newMutableTuple(length));
Object item(&scope, NoneType::object());
Object item_key(&scope, NoneType::object());
for (word i = 0; i < length; i++) {
item = tuple.at(i);
item_key = constantKey(thread, item);
if (item_key.isError()) return *item_key;
result.atPut(i, *item_key);
}
result_obj = result.becomeImmutable();
} else {
result_obj = runtime->emptyTuple();
}
return runtime->newTupleWith2(result_obj, obj);
}
if (obj.isFrozenSet()) {
FrozenSet set(&scope, *obj);
FrozenSet result(&scope, runtime->newFrozenSet());
Object item(&scope, NoneType::object());
Object item_key(&scope, NoneType::object());
Object hash_obj(&scope, NoneType::object());
for (word j = 0, idx = 0; setNextItem(set, &idx, &item); j++) {
item_key = constantKey(thread, item);
if (item_key.isError()) return *item_key;
hash_obj = Interpreter::hash(thread, item_key);
if (hash_obj.isErrorException()) return *hash_obj;
setAdd(thread, result, item_key, SmallInt::cast(*hash_obj).value());
}
return runtime->newTupleWith2(result, obj);
}
PyObject* ptr = ApiHandle::borrowedReference(runtime, *obj);
Object obj_id(&scope, runtime->newInt(reinterpret_cast<word>(ptr)));
return runtime->newTupleWith2(obj_id, obj);
}
PY_EXPORT PyObject* _PyCode_ConstantKey(PyObject* op) {
DCHECK(op != nullptr, "op must not be null");
Thread* thread = Thread::current();
HandleScope scope(thread);
Object obj(&scope, ApiHandle::fromPyObject(op)->asObject());
Object result(&scope, constantKey(thread, obj));
if (result.isError()) {
return nullptr;
}
return ApiHandle::newReference(thread->runtime(), *result);
}
} // namespace py