Skip to content

Commit 63be1bb

Browse files
Merge pull request #377 from Distributive-Network/philippe/376-copy
Implement the copy protocol for string proxies
2 parents 80e90a4 + 1ad2784 commit 63be1bb

File tree

6 files changed

+68
-5
lines changed

6 files changed

+68
-5
lines changed

include/JSStringProxy.hh

+29
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,35 @@ public:
3636
* @param self - The JSStringProxy to be free'd
3737
*/
3838
static void JSStringProxy_dealloc(JSStringProxy *self);
39+
40+
/**
41+
* @brief copy protocol method for both copy and deepcopy
42+
*
43+
* @param self - The JSObjectProxy
44+
* @return a copy of the string
45+
*/
46+
static PyObject *JSStringProxy_copy_method(JSStringProxy *self);
47+
};
48+
49+
// docs for methods, copied from cpython
50+
PyDoc_STRVAR(stringproxy_deepcopy__doc__,
51+
"__deepcopy__($self, memo, /)\n"
52+
"--\n"
53+
"\n");
54+
55+
PyDoc_STRVAR(stringproxy_copy__doc__,
56+
"__copy__($self, /)\n"
57+
"--\n"
58+
"\n");
59+
60+
/**
61+
* @brief Struct for the other methods
62+
*
63+
*/
64+
static PyMethodDef JSStringProxy_methods[] = {
65+
{"__deepcopy__", (PyCFunction)JSStringProxyMethodDefinitions::JSStringProxy_copy_method, METH_O, stringproxy_deepcopy__doc__}, // ignores any memo argument
66+
{"__copy__", (PyCFunction)JSStringProxyMethodDefinitions::JSStringProxy_copy_method, METH_NOARGS, stringproxy_copy__doc__},
67+
{NULL, NULL} /* sentinel */
3968
};
4069

4170
/**

include/StrType.hh

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public:
3737
static PyObject *getPyObject(JSContext *cx, JS::HandleValue str);
3838

3939
static const char *getValue(JSContext *cx, JS::HandleValue str);
40+
41+
static PyObject *proxifyString(JSContext *cx, JS::HandleValue str);
4042
};
4143

4244
#endif

src/JSStringProxy.cc

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @file JSStringProxy.cc
3-
* @author Caleb Aikens ([email protected])
3+
* @author Caleb Aikens ([email protected]) and Philippe Laporte ([email protected])
44
* @brief JSStringProxy is a custom C-implemented python type that derives from str. It acts as a proxy for JSStrings from Spidermonkey, and behaves like a str would.
55
* @date 2024-05-15
66
*
@@ -10,7 +10,19 @@
1010

1111
#include "include/JSStringProxy.hh"
1212

13+
#include "include/StrType.hh"
14+
15+
16+
extern JSContext *GLOBAL_CX;
17+
18+
1319
void JSStringProxyMethodDefinitions::JSStringProxy_dealloc(JSStringProxy *self)
1420
{
1521
delete self->jsString;
22+
}
23+
24+
PyObject *JSStringProxyMethodDefinitions::JSStringProxy_copy_method(JSStringProxy *self) {
25+
JS::RootedString selfString(GLOBAL_CX, ((JSStringProxy *)self)->jsString->toString());
26+
JS::RootedValue selfStringValue(GLOBAL_CX, JS::StringValue(selfString));
27+
return StrType::proxifyString(GLOBAL_CX, selfStringValue);
1628
}

src/StrType.cc

+3-3
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ static PyObject *asUCS4(PyObject *pyString) {
9595
return ret;
9696
}
9797

98-
static PyObject *processString(JSContext *cx, JS::HandleValue strVal) {
98+
PyObject *StrType::proxifyString(JSContext *cx, JS::HandleValue strVal) {
9999
JS::RootedString str(cx, strVal.toString());
100100
JSLinearString *lstr = JS_EnsureLinearString(cx, str);
101101
JS::AutoCheckCannotGC nogc;
@@ -189,11 +189,11 @@ PyObject *StrType::getPyObject(JSContext *cx, JS::HandleValue str) {
189189
}
190190
}
191191

192-
return processString(cx, str);
192+
return proxifyString(cx, str);
193193
}
194194

195195
const char *StrType::getValue(JSContext *cx, JS::HandleValue str) {
196-
PyObject *pyString = processString(cx, str);
196+
PyObject *pyString = proxifyString(cx, str);
197197
const char *value = PyUnicode_AsUTF8(pyString);
198198
Py_DECREF(pyString);
199199
return value;

src/modules/pythonmonkey/pythonmonkey.cc

+2-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ PyTypeObject JSStringProxyType = {
130130
.tp_basicsize = sizeof(JSStringProxy),
131131
.tp_dealloc = (destructor)JSStringProxyMethodDefinitions::JSStringProxy_dealloc,
132132
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_UNICODE_SUBCLASS,
133-
.tp_doc = PyDoc_STR("Javascript String value"),
133+
.tp_doc = PyDoc_STR("Javascript String proxy"),
134+
.tp_methods = JSStringProxy_methods,
134135
.tp_base = &PyUnicode_Type
135136
};
136137

tests/python/test_strings.py

+19
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pythonmonkey as pm
22
import gc
33
import random
4+
import copy
45

56

67
def test_identity():
@@ -271,3 +272,21 @@ def test_eval_boxed_ucs4_string_fuzztest():
271272

272273
string1 = string2
273274
assert INITIAL_STRING == string1 # strings should still match after a bunch of iterations through JS
275+
276+
277+
def test_string_proxy_copy():
278+
world = pm.eval('(function() {return "World"})')
279+
say = pm.eval('(function(who) { return `Hello ${who}`})')
280+
who = world()
281+
hello_world = say(copy.copy(who))
282+
assert hello_world == "Hello World"
283+
assert hello_world is not who
284+
285+
def test_string_proxy_deepcopy():
286+
world = pm.eval('(function() {return "World"})')
287+
say = pm.eval('(function(who) { return `Hello ${who}`})')
288+
who = world()
289+
hello_world = say(copy.deepcopy(who))
290+
assert hello_world == "Hello World"
291+
assert hello_world is not who
292+

0 commit comments

Comments
 (0)