Skip to content

Commit 9234559

Browse files
committed
Retain Ruby documents in XPath wrappers
1 parent 9f3c475 commit 9234559

5 files changed

Lines changed: 66 additions & 29 deletions

File tree

ext/libxml/ruby_xml_xpath.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979

8080
VALUE mXPath;
8181

82-
VALUE rxml_xpath_to_value(xmlXPathContextPtr xctxt, xmlXPathObjectPtr xobject)
82+
VALUE rxml_xpath_to_value(VALUE document, xmlXPathContextPtr xctxt, xmlXPathObjectPtr xobject)
8383
{
8484
VALUE result;
8585
int type;
@@ -96,7 +96,7 @@ VALUE rxml_xpath_to_value(xmlXPathContextPtr xctxt, xmlXPathObjectPtr xobject)
9696
switch (type = xobject->type)
9797
{
9898
case XPATH_NODESET:
99-
result = rxml_xpath_object_wrap(xctxt->doc, xobject);
99+
result = rxml_xpath_object_wrap(document, xctxt->doc, xobject);
100100
break;
101101
case XPATH_BOOLEAN:
102102
result = (xobject->boolval != 0) ? Qtrue : Qfalse;

ext/libxml/ruby_xml_xpath.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ extern VALUE mXPath;
99

1010
void rxml_init_xpath(void);
1111

12-
VALUE rxml_xpath_to_value(xmlXPathContextPtr, xmlXPathObjectPtr);
12+
VALUE rxml_xpath_to_value(VALUE, xmlXPathContextPtr, xmlXPathObjectPtr);
1313
xmlXPathObjectPtr rxml_xpath_from_value(VALUE);
1414

1515
#endif

ext/libxml/ruby_xml_xpath_context.c

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,28 @@
2828

2929
VALUE cXMLXPathContext;
3030

31+
typedef struct rxml_xpath_context
32+
{
33+
xmlXPathContextPtr xctxt;
34+
VALUE document;
35+
} rxml_xpath_context;
36+
3137
static void rxml_xpath_context_free(void *data)
3238
{
33-
xmlXPathContextPtr ctxt = (xmlXPathContextPtr)data;
34-
xmlXPathFreeContext(ctxt);
39+
rxml_xpath_context *wrapper = (rxml_xpath_context *)data;
40+
if (wrapper == NULL) return;
41+
if (wrapper->xctxt)
42+
{
43+
xmlXPathFreeContext(wrapper->xctxt);
44+
}
45+
xfree(wrapper);
3546
}
3647

3748
static void rxml_xpath_context_mark(void *data)
3849
{
39-
xmlXPathContextPtr ctxt = (xmlXPathContextPtr)data;
40-
if (ctxt == NULL) return;
41-
VALUE value = (VALUE)ctxt->doc->_private;
42-
rb_gc_mark(value);
50+
rxml_xpath_context *wrapper = (rxml_xpath_context *)data;
51+
if (wrapper == NULL) return;
52+
rb_gc_mark(wrapper->document);
4353
}
4454

4555
static const rb_data_type_t rxml_xpath_context_data_type = {
@@ -67,14 +77,28 @@ static VALUE rxml_xpath_context_alloc(VALUE klass)
6777
static VALUE rxml_xpath_context_initialize(VALUE self, VALUE document)
6878
{
6979
xmlDocPtr xdoc;
80+
rxml_xpath_context *wrapper;
7081

7182
if (rb_obj_is_kind_of(document, cXMLDocument) != Qtrue)
7283
{
7384
rb_raise(rb_eTypeError, "Supplied argument must be a document or node.");
7485
}
7586

7687
TypedData_Get_Struct(document, xmlDoc, &rxml_document_data_type, xdoc);
77-
RTYPEDDATA_DATA(self) = xmlXPathNewContext(xdoc);
88+
TypedData_Get_Struct(self, rxml_xpath_context, &rxml_xpath_context_data_type, wrapper);
89+
90+
if (wrapper == NULL)
91+
{
92+
wrapper = ALLOC(rxml_xpath_context);
93+
RTYPEDDATA_DATA(self) = wrapper;
94+
}
95+
else if (wrapper->xctxt)
96+
{
97+
xmlXPathFreeContext(wrapper->xctxt);
98+
}
99+
100+
wrapper->document = document;
101+
wrapper->xctxt = xmlXPathNewContext(xdoc);
78102

79103
return self;
80104
}
@@ -87,12 +111,10 @@ static VALUE rxml_xpath_context_initialize(VALUE self, VALUE document)
87111
*/
88112
static VALUE rxml_xpath_context_doc(VALUE self)
89113
{
90-
xmlDocPtr xdoc = NULL;
91-
xmlXPathContextPtr ctxt;
92-
TypedData_Get_Struct(self, xmlXPathContext, &rxml_xpath_context_data_type, ctxt);
93-
94-
xdoc = ctxt->doc;
95-
return rxml_document_wrap(xdoc);
114+
rxml_xpath_context *wrapper;
115+
TypedData_Get_Struct(self, rxml_xpath_context, &rxml_xpath_context_data_type, wrapper);
116+
if (wrapper == NULL) return Qnil;
117+
return wrapper->document;
96118
}
97119

98120
/*
@@ -106,8 +128,10 @@ static VALUE rxml_xpath_context_doc(VALUE self)
106128
*/
107129
static VALUE rxml_xpath_context_register_namespace(VALUE self, VALUE prefix, VALUE uri)
108130
{
131+
rxml_xpath_context *wrapper;
109132
xmlXPathContextPtr ctxt;
110-
TypedData_Get_Struct(self, xmlXPathContext, &rxml_xpath_context_data_type, ctxt);
133+
TypedData_Get_Struct(self, rxml_xpath_context, &rxml_xpath_context_data_type, wrapper);
134+
ctxt = wrapper->xctxt;
111135

112136
/* Prefix could be a symbol. */
113137
prefix = rb_obj_as_string(prefix);
@@ -137,11 +161,13 @@ static VALUE rxml_xpath_context_register_namespace(VALUE self, VALUE prefix, VAL
137161
static VALUE rxml_xpath_context_register_namespaces_from_node(VALUE self,
138162
VALUE node)
139163
{
164+
rxml_xpath_context *wrapper;
140165
xmlXPathContextPtr xctxt;
141166
xmlNodePtr xnode;
142167
xmlNsPtr *xnsArr;
143168

144-
TypedData_Get_Struct(self, xmlXPathContext, &rxml_xpath_context_data_type, xctxt);
169+
TypedData_Get_Struct(self, rxml_xpath_context, &rxml_xpath_context_data_type, wrapper);
170+
xctxt = wrapper->xctxt;
145171

146172
if (rb_obj_is_kind_of(node, cXMLDocument) == Qtrue)
147173
{
@@ -207,9 +233,11 @@ static VALUE rxml_xpath_context_register_namespaces(VALUE self, VALUE nslist)
207233
char *cp;
208234
long i;
209235
VALUE rprefix, ruri;
236+
rxml_xpath_context *wrapper;
210237
xmlXPathContextPtr xctxt;
211238

212-
TypedData_Get_Struct(self, xmlXPathContext, &rxml_xpath_context_data_type, xctxt);
239+
TypedData_Get_Struct(self, rxml_xpath_context, &rxml_xpath_context_data_type, wrapper);
240+
xctxt = wrapper->xctxt;
213241

214242
/* Need to loop through the 2nd argument and iterate through the
215243
* list of namespaces that we want to allow */
@@ -258,10 +286,12 @@ static VALUE rxml_xpath_context_register_namespaces(VALUE self, VALUE nslist)
258286
*/
259287
static VALUE rxml_xpath_context_node_set(VALUE self, VALUE node)
260288
{
289+
rxml_xpath_context *wrapper;
261290
xmlXPathContextPtr xctxt;
262291
xmlNodePtr xnode;
263292

264-
TypedData_Get_Struct(self, xmlXPathContext, &rxml_xpath_context_data_type, xctxt);
293+
TypedData_Get_Struct(self, rxml_xpath_context, &rxml_xpath_context_data_type, wrapper);
294+
xctxt = wrapper->xctxt;
265295
TypedData_Get_Struct(node, xmlNode, &rxml_node_data_type, xnode);
266296
xctxt->node = xnode;
267297
return node;
@@ -277,11 +307,13 @@ static VALUE rxml_xpath_context_node_set(VALUE self, VALUE node)
277307
*/
278308
static VALUE rxml_xpath_context_find(VALUE self, VALUE xpath_expr)
279309
{
310+
rxml_xpath_context *wrapper;
280311
xmlXPathContextPtr xctxt;
281312
xmlXPathObjectPtr xobject;
282313
xmlXPathCompExprPtr xcompexpr;
283314

284-
TypedData_Get_Struct(self, xmlXPathContext, &rxml_xpath_context_data_type, xctxt);
315+
TypedData_Get_Struct(self, rxml_xpath_context, &rxml_xpath_context_data_type, wrapper);
316+
xctxt = wrapper->xctxt;
285317

286318
if (TYPE(xpath_expr) == T_STRING)
287319
{
@@ -299,7 +331,7 @@ static VALUE rxml_xpath_context_find(VALUE self, VALUE xpath_expr)
299331
"Argument should be an instance of a String or XPath::Expression");
300332
}
301333

302-
return rxml_xpath_to_value(xctxt, xobject);
334+
return rxml_xpath_to_value(wrapper->document, xctxt, xobject);
303335
}
304336

305337
#if LIBXML_VERSION >= 20626
@@ -317,11 +349,13 @@ static VALUE rxml_xpath_context_find(VALUE self, VALUE xpath_expr)
317349
static VALUE
318350
rxml_xpath_context_enable_cache(int argc, VALUE *argv, VALUE self)
319351
{
352+
rxml_xpath_context *wrapper;
320353
xmlXPathContextPtr xctxt;
321354
VALUE size;
322355
int value = -1;
323356

324-
TypedData_Get_Struct(self, xmlXPathContext, &rxml_xpath_context_data_type, xctxt);
357+
TypedData_Get_Struct(self, rxml_xpath_context, &rxml_xpath_context_data_type, wrapper);
358+
xctxt = wrapper->xctxt;
325359

326360
if (rb_scan_args(argc, argv, "01", &size) == 1)
327361
{
@@ -343,8 +377,10 @@ rxml_xpath_context_enable_cache(int argc, VALUE *argv, VALUE self)
343377
static VALUE
344378
rxml_xpath_context_disable_cache(VALUE self)
345379
{
380+
rxml_xpath_context *wrapper;
346381
xmlXPathContextPtr xctxt;
347-
TypedData_Get_Struct(self, xmlXPathContext, &rxml_xpath_context_data_type, xctxt);
382+
TypedData_Get_Struct(self, rxml_xpath_context, &rxml_xpath_context_data_type, wrapper);
383+
xctxt = wrapper->xctxt;
348384

349385
if (xmlXPathContextSetCache(xctxt, 0, 0, 0) == -1)
350386
rxml_raise(xmlGetLastError());

ext/libxml/ruby_xml_xpath_object.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ static const rb_data_type_t rxml_namespace_owned_data_type = {
6060
static void rxml_xpath_object_mark(void *data)
6161
{
6262
rxml_xpath_object *rxpop = (rxml_xpath_object *)data;
63-
VALUE doc = (VALUE)rxpop->xdoc->_private;
64-
rb_gc_mark(doc);
63+
rb_gc_mark(rxpop->document);
6564
rb_gc_mark(rxpop->nsnodes);
6665
}
6766

@@ -71,14 +70,15 @@ static const rb_data_type_t rxml_xpath_object_data_type = {
7170
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
7271
};
7372

74-
VALUE rxml_xpath_object_wrap(xmlDocPtr xdoc, xmlXPathObjectPtr xpop)
73+
VALUE rxml_xpath_object_wrap(VALUE document, xmlDocPtr xdoc, xmlXPathObjectPtr xpop)
7574
{
7675
int i;
7776
rxml_xpath_object *rxpopp = ALLOC(rxml_xpath_object);
7877

7978
/* Make sure Ruby's GC can find the array in the stack */
8079
VALUE nsnodes = rb_ary_new();
81-
rxpopp->xdoc =xdoc;
80+
rxpopp->document = document;
81+
rxpopp->xdoc = xdoc;
8282
rxpopp->xpop = xpop;
8383

8484
/* Find all the extra namespace nodes and wrap them. */

ext/libxml/ruby_xml_xpath_object.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ extern VALUE cXMLXPathObject;
55

66
typedef struct rxml_xpath_object
77
{
8+
VALUE document;
89
xmlDocPtr xdoc;
910
xmlXPathObjectPtr xpop;
1011
VALUE nsnodes;
1112
} rxml_xpath_object;
1213

1314

1415
void rxml_init_xpath_object(void);
15-
VALUE rxml_xpath_object_wrap(xmlDocPtr xdoc, xmlXPathObjectPtr xpop);
16+
VALUE rxml_xpath_object_wrap(VALUE document, xmlDocPtr xdoc, xmlXPathObjectPtr xpop);
1617

1718
#endif

0 commit comments

Comments
 (0)