Skip to content

Commit 73129ba

Browse files
support console printing of iterable proxy
1 parent fb900e8 commit 73129ba

File tree

2 files changed

+100
-12
lines changed

2 files changed

+100
-12
lines changed

src/PyIterableProxyHandler.cc

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,48 @@ static bool iterable_next(JSContext *cx, unsigned argc, JS::Value *vp) {
6666
return iter_next(cx, args, it);
6767
}
6868

69+
static bool toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp) {
70+
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
71+
72+
JS::RootedObject proxy(cx, JS::ToObject(cx, args.thisv()));
73+
if (!proxy) {
74+
return false;
75+
}
76+
77+
PyObject *self = JS::GetMaybePtrFromReservedSlot<PyObject>(proxy, PyObjectSlot);
78+
79+
_PyUnicodeWriter writer;
80+
81+
_PyUnicodeWriter_Init(&writer);
82+
83+
PyObject *s = PyObject_Repr(self);
84+
85+
if (s == nullptr) {
86+
args.rval().setString(JS_NewStringCopyZ(cx, "<cannot repr type>"));
87+
return true;
88+
}
89+
90+
int res = _PyUnicodeWriter_WriteStr(&writer, s);
91+
Py_DECREF(s);
92+
93+
if (res < 0) {
94+
args.rval().setString(JS_NewStringCopyZ(cx, "<cannot repr type>"));
95+
return true;
96+
}
97+
98+
PyObject* repr = _PyUnicodeWriter_Finish(&writer);
99+
100+
args.rval().set(jsTypeFactory(cx, repr));
101+
return true;
102+
}
103+
104+
static bool iterable_valueOf(JSContext *cx, unsigned argc, JS::Value *vp) {
105+
return toPrimitive(cx, argc, vp);
106+
}
107+
69108
JSMethodDef PyIterableProxyHandler::iterable_methods[] = {
70109
{"next", iterable_next, 0},
110+
{"valueOf", iterable_valueOf, 0},
71111
{NULL, NULL, 0}
72112
};
73113

@@ -172,7 +212,7 @@ bool PyIterableProxyHandler::getOwnPropertyDescriptor(
172212
JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
173213
JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc
174214
) const {
175-
215+
176216
// see if we're calling a function
177217
if (id.isString()) {
178218
for (size_t index = 0;; index++) {
@@ -196,11 +236,39 @@ bool PyIterableProxyHandler::getOwnPropertyDescriptor(
196236
}
197237
}
198238

239+
// "constructor" property
240+
bool isConstructorProperty;
241+
if (id.isString() && JS_StringEqualsLiteral(cx, id.toString(), "constructor", &isConstructorProperty) && isConstructorProperty) {
242+
//printf("PyIterableProxyHandler::handleGetOwnPropertyDescriptor constructor\n");
243+
JS::RootedObject global(cx, JS::GetNonCCWObjectGlobal(proxy));
244+
245+
JS::RootedObject rootedObjectPrototype(cx);
246+
if (!JS_GetClassPrototype(cx, JSProto_Object, &rootedObjectPrototype)) {
247+
return false;
248+
}
249+
250+
JS::RootedValue Object_Prototype_Constructor(cx);
251+
if (!JS_GetProperty(cx, rootedObjectPrototype, "constructor", &Object_Prototype_Constructor)) {
252+
return false;
253+
}
254+
255+
JS::RootedObject rootedObjectPrototypeConstructor(cx, Object_Prototype_Constructor.toObjectOrNull());
256+
257+
desc.set(mozilla::Some(
258+
JS::PropertyDescriptor::Data(
259+
JS::ObjectValue(*rootedObjectPrototypeConstructor),
260+
{JS::PropertyAttribute::Enumerable}
261+
)
262+
));
263+
return true;
264+
}
265+
199266
// symbol property
200267
if (id.isSymbol()) {
201268
JS::RootedSymbol rootedSymbol(cx, id.toSymbol());
269+
JS::SymbolCode symbolCode = JS::GetSymbolCode(rootedSymbol);
202270

203-
if (JS::GetSymbolCode(rootedSymbol) == JS::SymbolCode::iterator) {
271+
if (symbolCode == JS::SymbolCode::iterator) {
204272
JSFunction *newFunction = JS_NewFunction(cx, iterable_values, 0, 0, NULL);
205273
if (!newFunction) return false;
206274
JS::RootedObject funObj(cx, JS_GetFunctionObject(newFunction));
@@ -211,6 +279,18 @@ bool PyIterableProxyHandler::getOwnPropertyDescriptor(
211279
)
212280
));
213281
return true;
282+
}
283+
else if (symbolCode == JS::SymbolCode::toPrimitive) {
284+
JSFunction *newFunction = JS_NewFunction(cx, toPrimitive, 0, 0, nullptr);
285+
if (!newFunction) return false;
286+
JS::RootedObject funObj(cx, JS_GetFunctionObject(newFunction));
287+
desc.set(mozilla::Some(
288+
JS::PropertyDescriptor::Data(
289+
JS::ObjectValue(*funObj),
290+
{JS::PropertyAttribute::Enumerable}
291+
)
292+
));
293+
return true;
214294
}
215295
}
216296

src/PyObjectProxyHandler.cc

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,19 +123,27 @@ void PyObjectProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const {
123123
bool PyObjectProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const {
124124
PyObject *self = JS::GetMaybePtrFromReservedSlot<PyObject>(proxy, PyObjectSlot);
125125
PyObject *keys = PyObject_Dir(self);
126-
size_t keysLength = PyList_Size(keys);
127126

128-
PyObject *nonDunderKeys = PyList_New(0);
129-
for (size_t i = 0; i < keysLength; i++) {
130-
PyObject *key = PyList_GetItem(keys, i);
131-
if (PyObject_CallMethod(key, "startswith", "(s)", "__") == Py_False) { // if key starts with "__", ignore it
132-
PyList_Append(nonDunderKeys, key);
133-
}
134-
}
127+
if (keys != nullptr) {
128+
size_t keysLength = PyList_Size(keys);
135129

136-
size_t length = PyList_Size(nonDunderKeys);
130+
PyObject *nonDunderKeys = PyList_New(0);
131+
for (size_t i = 0; i < keysLength; i++) {
132+
PyObject *key = PyList_GetItem(keys, i);
133+
if (PyObject_CallMethod(key, "startswith", "(s)", "__") == Py_False) { // if key starts with "__", ignore it
134+
PyList_Append(nonDunderKeys, key);
135+
}
136+
}
137137

138-
return handleOwnPropertyKeys(cx, nonDunderKeys, length, props);
138+
return handleOwnPropertyKeys(cx, nonDunderKeys, PyList_Size(nonDunderKeys), props);
139+
}
140+
else {
141+
if (PyErr_Occurred()) {
142+
PyErr_Clear();
143+
}
144+
145+
return handleOwnPropertyKeys(cx, PyList_New(0), 0, props);
146+
}
139147
}
140148

141149
bool PyObjectProxyHandler::delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,

0 commit comments

Comments
 (0)