this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include <cmath>
3
4#include "cpython-data.h"
5#include "cpython-func.h"
6
7#include "api-handle.h"
8#include "runtime.h"
9#include "set-builtins.h"
10#include "tuple-builtins.h"
11
12namespace py {
13
14static_assert(RawCode::Flags::kOptimized == CO_OPTIMIZED, "");
15static_assert(RawCode::Flags::kNewlocals == CO_NEWLOCALS, "");
16static_assert(RawCode::Flags::kVarargs == CO_VARARGS, "");
17static_assert(RawCode::Flags::kVarkeyargs == CO_VARKEYWORDS, "");
18static_assert(RawCode::Flags::kNested == CO_NESTED, "");
19static_assert(RawCode::Flags::kGenerator == CO_GENERATOR, "");
20static_assert(RawCode::Flags::kNofree == CO_NOFREE, "");
21static_assert(RawCode::Flags::kCoroutine == CO_COROUTINE, "");
22static_assert(RawCode::Flags::kIterableCoroutine == CO_ITERABLE_COROUTINE, "");
23static_assert(RawCode::Flags::kAsyncGenerator == CO_ASYNC_GENERATOR, "");
24static_assert(RawCode::Flags::kFutureDivision == CO_FUTURE_DIVISION, "");
25static_assert(RawCode::Flags::kFutureAbsoluteImport ==
26 CO_FUTURE_ABSOLUTE_IMPORT,
27 "");
28static_assert(RawCode::Flags::kFutureWithStatement == CO_FUTURE_WITH_STATEMENT,
29 "");
30static_assert(RawCode::Flags::kFuturePrintFunction == CO_FUTURE_PRINT_FUNCTION,
31 "");
32static_assert(RawCode::Flags::kFutureUnicodeLiterals ==
33 CO_FUTURE_UNICODE_LITERALS,
34 "");
35static_assert(RawCode::Flags::kFutureBarryAsBdfl == CO_FUTURE_BARRY_AS_BDFL,
36 "");
37static_assert(RawCode::Flags::kFutureGeneratorStop == CO_FUTURE_GENERATOR_STOP,
38 "");
39
40PY_EXPORT int PyCode_Check_Func(PyObject* obj) {
41 return ApiHandle::asObject(ApiHandle::fromPyObject(obj)).isCode();
42}
43
44PY_EXPORT PyCodeObject* PyCode_NewWithPosOnlyArgs(
45 int argcount, int posonlyargcount, int kwonlyargcount, int nlocals,
46 int stacksize, int flags, PyObject* code, PyObject* consts, PyObject* names,
47 PyObject* varnames, PyObject* freevars, PyObject* cellvars,
48 PyObject* filename, PyObject* name, int firstlineno, PyObject* lnotab) {
49 Thread* thread = Thread::current();
50 if (argcount < 0 || posonlyargcount < 0 || kwonlyargcount < 0 ||
51 nlocals < 0 || code == nullptr || consts == nullptr || names == nullptr ||
52 varnames == nullptr || freevars == nullptr || cellvars == nullptr ||
53 name == nullptr || filename == nullptr || lnotab == nullptr) {
54 thread->raiseBadInternalCall();
55 return nullptr;
56 }
57 HandleScope scope(thread);
58 Object consts_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(consts)));
59 Object names_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(names)));
60 Object varnames_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(varnames)));
61 Object freevars_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(freevars)));
62 Object cellvars_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(cellvars)));
63 Object name_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(name)));
64 Object filename_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(filename)));
65 Object lnotab_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(lnotab)));
66 Object code_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(code)));
67 Runtime* runtime = thread->runtime();
68 // Check argument types
69 // TODO(emacs): Call equivalent of PyObject_CheckReadBuffer(code) instead of
70 // isInstanceOfBytes
71 if (!runtime->isInstanceOfBytes(*code_obj) ||
72 !runtime->isInstanceOfTuple(*consts_obj) ||
73 !runtime->isInstanceOfTuple(*names_obj) ||
74 !runtime->isInstanceOfTuple(*varnames_obj) ||
75 !runtime->isInstanceOfTuple(*freevars_obj) ||
76 !runtime->isInstanceOfTuple(*cellvars_obj) ||
77 !runtime->isInstanceOfStr(*name_obj) ||
78 !runtime->isInstanceOfStr(*filename_obj) ||
79 !runtime->isInstanceOfBytes(*lnotab_obj)) {
80 thread->raiseBadInternalCall();
81 return nullptr;
82 }
83
84 return reinterpret_cast<PyCodeObject*>(ApiHandle::newReferenceWithManaged(
85 runtime,
86 runtime->newCode(argcount, posonlyargcount, kwonlyargcount, nlocals,
87 stacksize, flags, code_obj, consts_obj, names_obj,
88 varnames_obj, freevars_obj, cellvars_obj, filename_obj,
89 name_obj, firstlineno, lnotab_obj)));
90}
91
92PY_EXPORT PyCodeObject* PyCode_New(int argcount, int kwonlyargcount,
93 int nlocals, int stacksize, int flags,
94 PyObject* code, PyObject* consts,
95 PyObject* names, PyObject* varnames,
96 PyObject* freevars, PyObject* cellvars,
97 PyObject* filename, PyObject* name,
98 int firstlineno, PyObject* lnotab) {
99 return PyCode_NewWithPosOnlyArgs(
100 argcount, /*posonlyargcount=*/0, kwonlyargcount, nlocals, stacksize,
101 flags, code, consts, names, varnames, freevars, cellvars, filename, name,
102 firstlineno, lnotab);
103}
104
105PY_EXPORT PyCodeObject* PyCode_NewEmpty(const char* filename,
106 const char* funcname, int firstlineno) {
107 Thread* thread = Thread::current();
108 HandleScope scope(thread);
109 Runtime* runtime = thread->runtime();
110 Object empty_bytes(&scope, Bytes::empty());
111 Object empty_tuple(&scope, runtime->emptyTuple());
112 Object filename_obj(&scope, Runtime::internStrFromCStr(thread, filename));
113 Object name_obj(&scope, Runtime::internStrFromCStr(thread, funcname));
114 return reinterpret_cast<PyCodeObject*>(ApiHandle::newReferenceWithManaged(
115 runtime, runtime->newCode(/*argcount=*/0,
116 /*posonlyargcount=*/0,
117 /*kwonlyargcount=*/0,
118 /*nlocals=*/0,
119 /*stacksize=*/0,
120 /*flags=*/0,
121 /*code=*/empty_bytes,
122 /*consts=*/empty_tuple,
123 /*names=*/empty_tuple,
124 /*varnames=*/empty_tuple,
125 /*freevars=*/empty_tuple,
126 /*cellvars=*/empty_tuple,
127 /*filename=*/filename_obj,
128 /*name=*/name_obj,
129 /*firstlineno=*/firstlineno,
130 /*lnotab=*/empty_bytes)));
131}
132
133PY_EXPORT PyTypeObject* PyCode_Type_Ptr() {
134 Runtime* runtime = Thread::current()->runtime();
135 return reinterpret_cast<PyTypeObject*>(
136 ApiHandle::borrowedReference(runtime, runtime->typeAt(LayoutId::kCode)));
137}
138
139PY_EXPORT Py_ssize_t PyCode_GetNumFree_Func(PyObject* code) {
140 DCHECK(code != nullptr, "code must not be null");
141 Thread* thread = Thread::current();
142 HandleScope scope(thread);
143 Object code_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(code)));
144 DCHECK(code_obj.isCode(), "code must be a code object");
145 Code code_code(&scope, *code_obj);
146 Tuple freevars(&scope, code_code.freevars());
147 return freevars.length();
148}
149
150static RawObject constantKey(Thread* thread, const Object& obj) {
151 HandleScope scope(thread);
152 Runtime* runtime = thread->runtime();
153 if (obj.isNoneType() || obj.isEllipsis() || obj.isSmallInt() ||
154 obj.isLargeInt() || obj.isStr() || obj.isCode()) {
155 return *obj;
156 }
157 if (obj.isBool() || obj.isBytes()) {
158 Object type(&scope, runtime->typeOf(*obj));
159 return runtime->newTupleWith2(type, obj);
160 }
161 if (obj.isFloat()) {
162 double d = Float::cast(*obj).value();
163 Object type(&scope, runtime->typeOf(*obj));
164 if (d == 0.0 && std::signbit(d)) {
165 Object none(&scope, NoneType::object());
166 return runtime->newTupleWith3(type, obj, none);
167 }
168 return runtime->newTupleWith2(type, obj);
169 }
170 if (obj.isComplex()) {
171 Complex c(&scope, *obj);
172 Py_complex z;
173 z.real = c.real();
174 z.imag = c.imag();
175 // For the complex case we must make complex(x, 0.)
176 // different from complex(x, -0.) and complex(0., y)
177 // different from complex(-0., y), for any x and y.
178 // All four complex zeros must be distinguished.
179 bool real_negzero = z.real == 0.0 && std::signbit(z.real);
180 bool imag_negzero = z.imag == 0.0 && std::signbit(z.imag);
181 // use True, False and None singleton as tags for the real and imag sign,
182 // to make tuples different
183 Object type(&scope, runtime->typeOf(*obj));
184 if (real_negzero && imag_negzero) {
185 Object tru(&scope, Bool::trueObj());
186 return runtime->newTupleWith3(type, obj, tru);
187 }
188 if (imag_negzero) {
189 Object fals(&scope, Bool::falseObj());
190 return runtime->newTupleWith3(type, obj, fals);
191 }
192 if (real_negzero) {
193 Object none(&scope, NoneType::object());
194 return runtime->newTupleWith3(type, obj, none);
195 }
196 return runtime->newTupleWith2(type, obj);
197 }
198 if (obj.isTuple()) {
199 Tuple tuple(&scope, *obj);
200 Object result_obj(&scope, NoneType::object());
201 word length = tuple.length();
202 if (length > 0) {
203 MutableTuple result(&scope, runtime->newMutableTuple(length));
204 Object item(&scope, NoneType::object());
205 Object item_key(&scope, NoneType::object());
206 for (word i = 0; i < length; i++) {
207 item = tuple.at(i);
208 item_key = constantKey(thread, item);
209 if (item_key.isError()) return *item_key;
210 result.atPut(i, *item_key);
211 }
212 result_obj = result.becomeImmutable();
213 } else {
214 result_obj = runtime->emptyTuple();
215 }
216 return runtime->newTupleWith2(result_obj, obj);
217 }
218 if (obj.isFrozenSet()) {
219 FrozenSet set(&scope, *obj);
220 FrozenSet result(&scope, runtime->newFrozenSet());
221 Object item(&scope, NoneType::object());
222 Object item_key(&scope, NoneType::object());
223 Object hash_obj(&scope, NoneType::object());
224 for (word idx = 0; setNextItem(set, &idx, &item);) {
225 item_key = constantKey(thread, item);
226 if (item_key.isError()) return *item_key;
227 hash_obj = Interpreter::hash(thread, item_key);
228 if (hash_obj.isErrorException()) return *hash_obj;
229 setAdd(thread, result, item_key, SmallInt::cast(*hash_obj).value());
230 }
231 return runtime->newTupleWith2(result, obj);
232 }
233 PyObject* ptr = ApiHandle::borrowedReference(runtime, *obj);
234 Object obj_id(&scope, runtime->newInt(reinterpret_cast<word>(ptr)));
235 return runtime->newTupleWith2(obj_id, obj);
236}
237
238PY_EXPORT PyObject* _PyCode_ConstantKey(PyObject* op) {
239 DCHECK(op != nullptr, "op must not be null");
240 Thread* thread = Thread::current();
241 HandleScope scope(thread);
242 Object obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(op)));
243 Object result(&scope, constantKey(thread, obj));
244 if (result.isError()) {
245 return nullptr;
246 }
247 return ApiHandle::newReference(thread->runtime(), *result);
248}
249
250} // namespace py