nokogiri 1.19.4-java → 1.19.5-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE-DEPENDENCIES.md +29 -0
- data/ext/java/nokogiri/XmlNode.java +69 -38
- data/ext/java/nokogiri/XmlReader.java +4 -1
- data/ext/java/nokogiri/XmlSchema.java +5 -74
- data/ext/java/nokogiri/XmlXpathContext.java +5 -15
- data/ext/java/nokogiri/internals/NokogiriEntityResolver.java +5 -7
- data/ext/java/nokogiri/internals/NokogiriHelpers.java +68 -0
- data/ext/java/nokogiri/internals/NokogiriXPathContext.java +37 -0
- data/ext/nokogiri/extconf.rb +1 -0
- data/ext/nokogiri/gumbo.c +83 -6
- data/ext/nokogiri/html4_document.c +10 -3
- data/ext/nokogiri/libxml2_polyfill.c +12 -0
- data/ext/nokogiri/nokogiri.c +84 -2
- data/ext/nokogiri/xml_document.c +42 -16
- data/ext/nokogiri/xml_node.c +73 -38
- data/ext/nokogiri/xml_node_set.c +9 -9
- data/ext/nokogiri/xml_reader.c +198 -107
- data/ext/nokogiri/xml_sax_parser.c +3 -3
- data/ext/nokogiri/xml_sax_parser_context.c +38 -7
- data/ext/nokogiri/xml_sax_push_parser.c +12 -0
- data/ext/nokogiri/xml_xpath_context.c +124 -26
- data/ext/nokogiri/xslt_stylesheet.c +123 -27
- data/lib/nokogiri/css/parser.rb +35 -18
- data/lib/nokogiri/css/parser.y +35 -17
- data/lib/nokogiri/css/xpath_visitor.rb +71 -28
- data/lib/nokogiri/html5/document.rb +9 -0
- data/lib/nokogiri/html5/document_fragment.rb +16 -1
- data/lib/nokogiri/html5/node.rb +1 -1
- data/lib/nokogiri/html5.rb +16 -0
- data/lib/nokogiri/nokogiri.jar +0 -0
- data/lib/nokogiri/version/constant.rb +1 -1
- data/lib/nokogiri/xml/attr.rb +2 -0
- data/lib/nokogiri/xml/attribute_decl.rb +2 -1
- data/lib/nokogiri/xml/builder.rb +24 -32
- data/lib/nokogiri/xml/document.rb +1 -0
- data/lib/nokogiri/xml/dtd.rb +2 -1
- data/lib/nokogiri/xml/element_decl.rb +2 -1
- data/lib/nokogiri/xml/entity_decl.rb +2 -1
- data/lib/nokogiri/xml/node.rb +8 -6
- metadata +3 -2
|
@@ -4,10 +4,25 @@ VALUE cNokogiriXmlSaxParserContext ;
|
|
|
4
4
|
|
|
5
5
|
static ID id_read;
|
|
6
6
|
|
|
7
|
+
typedef struct {
|
|
8
|
+
xmlParserCtxtPtr ctxt;
|
|
9
|
+
VALUE rb_input;
|
|
10
|
+
} nokogiriSaxParserContextTuple;
|
|
11
|
+
|
|
12
|
+
static void
|
|
13
|
+
_noko_xml_sax_parser_context_mark(void *data)
|
|
14
|
+
{
|
|
15
|
+
nokogiriSaxParserContextTuple *tuple = data;
|
|
16
|
+
/* Pin the input because libxml2 retains the IO callback's Ruby handle. */
|
|
17
|
+
rb_gc_mark(tuple->rb_input);
|
|
18
|
+
}
|
|
19
|
+
|
|
7
20
|
static void
|
|
8
21
|
xml_sax_parser_context_type_free(void *data)
|
|
9
22
|
{
|
|
10
|
-
|
|
23
|
+
nokogiriSaxParserContextTuple *tuple = data;
|
|
24
|
+
xmlParserCtxtPtr ctxt = tuple->ctxt;
|
|
25
|
+
|
|
11
26
|
ctxt->sax = NULL;
|
|
12
27
|
if (ctxt->myDoc) {
|
|
13
28
|
xmlFreeDoc(ctxt->myDoc);
|
|
@@ -15,6 +30,7 @@ xml_sax_parser_context_type_free(void *data)
|
|
|
15
30
|
if (ctxt) {
|
|
16
31
|
xmlFreeParserCtxt(ctxt);
|
|
17
32
|
}
|
|
33
|
+
ruby_xfree(tuple);
|
|
18
34
|
}
|
|
19
35
|
|
|
20
36
|
/*
|
|
@@ -24,6 +40,7 @@ xml_sax_parser_context_type_free(void *data)
|
|
|
24
40
|
static const rb_data_type_t xml_sax_parser_context_type = {
|
|
25
41
|
.wrap_struct_name = "xmlParserCtxt",
|
|
26
42
|
.function = {
|
|
43
|
+
.dmark = _noko_xml_sax_parser_context_mark,
|
|
27
44
|
.dfree = xml_sax_parser_context_type_free,
|
|
28
45
|
},
|
|
29
46
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
|
|
@@ -32,15 +49,29 @@ static const rb_data_type_t xml_sax_parser_context_type = {
|
|
|
32
49
|
xmlParserCtxtPtr
|
|
33
50
|
noko_xml_sax_parser_context_unwrap(VALUE rb_context)
|
|
34
51
|
{
|
|
35
|
-
|
|
36
|
-
TypedData_Get_Struct(rb_context,
|
|
37
|
-
return
|
|
52
|
+
nokogiriSaxParserContextTuple *tuple;
|
|
53
|
+
TypedData_Get_Struct(rb_context, nokogiriSaxParserContextTuple, &xml_sax_parser_context_type, tuple);
|
|
54
|
+
return tuple->ctxt;
|
|
38
55
|
}
|
|
39
56
|
|
|
40
57
|
VALUE
|
|
41
58
|
noko_xml_sax_parser_context_wrap(VALUE klass, xmlParserCtxtPtr c_context)
|
|
42
59
|
{
|
|
43
|
-
|
|
60
|
+
nokogiriSaxParserContextTuple *tuple;
|
|
61
|
+
VALUE rb_context = TypedData_Make_Struct(klass, nokogiriSaxParserContextTuple, &xml_sax_parser_context_type, tuple);
|
|
62
|
+
|
|
63
|
+
tuple->ctxt = c_context;
|
|
64
|
+
tuple->rb_input = Qnil;
|
|
65
|
+
|
|
66
|
+
return rb_context;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static void
|
|
70
|
+
_noko_xml_sax_parser_context_set_input(VALUE rb_context, VALUE rb_input)
|
|
71
|
+
{
|
|
72
|
+
nokogiriSaxParserContextTuple *tuple;
|
|
73
|
+
TypedData_Get_Struct(rb_context, nokogiriSaxParserContextTuple, &xml_sax_parser_context_type, tuple);
|
|
74
|
+
RB_OBJ_WRITE(rb_context, &tuple->rb_input, rb_input);
|
|
44
75
|
}
|
|
45
76
|
|
|
46
77
|
void
|
|
@@ -103,7 +134,7 @@ noko_xml_sax_parser_context_s_native_io(VALUE rb_class, VALUE rb_io, VALUE rb_en
|
|
|
103
134
|
}
|
|
104
135
|
|
|
105
136
|
VALUE rb_context = noko_xml_sax_parser_context_wrap(rb_class, c_context);
|
|
106
|
-
|
|
137
|
+
_noko_xml_sax_parser_context_set_input(rb_context, rb_io);
|
|
107
138
|
|
|
108
139
|
return rb_context;
|
|
109
140
|
}
|
|
@@ -158,7 +189,7 @@ noko_xml_sax_parser_context_s_native_memory(VALUE rb_class, VALUE rb_input, VALU
|
|
|
158
189
|
}
|
|
159
190
|
|
|
160
191
|
VALUE rb_context = noko_xml_sax_parser_context_wrap(rb_class, c_context);
|
|
161
|
-
|
|
192
|
+
_noko_xml_sax_parser_context_set_input(rb_context, rb_input);
|
|
162
193
|
|
|
163
194
|
return rb_context;
|
|
164
195
|
}
|
|
@@ -14,10 +14,22 @@ xml_sax_push_parser_free(void *data)
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/* The @sax_parser ivar marks the parser; libxml2 also retains its movable VALUE in _private. */
|
|
18
|
+
static void
|
|
19
|
+
_noko_xml_sax_push_parser_update_references(void *data)
|
|
20
|
+
{
|
|
21
|
+
xmlParserCtxtPtr ctx = data;
|
|
22
|
+
|
|
23
|
+
if (ctx->_private) {
|
|
24
|
+
ctx->_private = (void *)rb_gc_location((VALUE)ctx->_private);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
static const rb_data_type_t xml_sax_push_parser_type = {
|
|
18
29
|
.wrap_struct_name = "xmlParserCtxt",
|
|
19
30
|
.function = {
|
|
20
31
|
.dfree = xml_sax_push_parser_free,
|
|
32
|
+
.dcompact = _noko_xml_sax_push_parser_update_references,
|
|
21
33
|
},
|
|
22
34
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
|
|
23
35
|
};
|
|
@@ -231,6 +231,7 @@ Nokogiri_marshal_xpath_funcall_and_return_values(
|
|
|
231
231
|
{
|
|
232
232
|
VALUE rb_retval;
|
|
233
233
|
VALUE *argv;
|
|
234
|
+
VALUE argv_handle;
|
|
234
235
|
VALUE rb_node_set = Qnil;
|
|
235
236
|
xmlNodeSetPtr c_node_set = NULL;
|
|
236
237
|
xmlXPathObjectPtr c_xpath_object;
|
|
@@ -238,10 +239,7 @@ Nokogiri_marshal_xpath_funcall_and_return_values(
|
|
|
238
239
|
assert(ctxt->context->doc);
|
|
239
240
|
assert(DOC_RUBY_OBJECT_TEST(ctxt->context->doc));
|
|
240
241
|
|
|
241
|
-
argv = (VALUE
|
|
242
|
-
for (int j = 0 ; j < argc ; ++j) {
|
|
243
|
-
rb_gc_register_address(&argv[j]);
|
|
244
|
-
}
|
|
242
|
+
argv = ALLOCV_N(VALUE, argv_handle, argc);
|
|
245
243
|
|
|
246
244
|
for (int j = argc - 1 ; j >= 0 ; --j) {
|
|
247
245
|
c_xpath_object = valuePop(ctxt);
|
|
@@ -259,10 +257,7 @@ Nokogiri_marshal_xpath_funcall_and_return_values(
|
|
|
259
257
|
argv
|
|
260
258
|
);
|
|
261
259
|
|
|
262
|
-
|
|
263
|
-
rb_gc_unregister_address(&argv[j]);
|
|
264
|
-
}
|
|
265
|
-
ruby_xfree(argv);
|
|
260
|
+
ALLOCV_END(argv_handle);
|
|
266
261
|
|
|
267
262
|
switch (TYPE(rb_retval)) {
|
|
268
263
|
case T_FLOAT:
|
|
@@ -300,37 +295,116 @@ Nokogiri_marshal_xpath_funcall_and_return_values(
|
|
|
300
295
|
}
|
|
301
296
|
}
|
|
302
297
|
|
|
298
|
+
/*
|
|
299
|
+
* A Ruby exception must not unwind through libxml2's stack frames, because that skips libxml2's
|
|
300
|
+
* cleanup and leaks memory. So the handler is called under rb_protect, and the exception is
|
|
301
|
+
* re-raised after xmlXPathEvalExpression returns.
|
|
302
|
+
*
|
|
303
|
+
* Until then, no Ruby code may run: the pending exception lives only in the thread's errinfo,
|
|
304
|
+
* which a rescue elsewhere would overwrite.
|
|
305
|
+
*/
|
|
306
|
+
typedef struct {
|
|
307
|
+
VALUE handler;
|
|
308
|
+
int exception_state;
|
|
309
|
+
} _noko_xml_xpath_context__handler_call_t;
|
|
310
|
+
|
|
311
|
+
typedef struct {
|
|
312
|
+
VALUE handler;
|
|
313
|
+
const xmlChar *name;
|
|
314
|
+
const xmlChar *ns_uri;
|
|
315
|
+
} _noko_xml_xpath_context__lookup_args_t;
|
|
316
|
+
|
|
317
|
+
typedef struct {
|
|
318
|
+
xmlXPathParserContextPtr ctxt;
|
|
319
|
+
int argc;
|
|
320
|
+
} _noko_xml_xpath_context__invoke_args_t;
|
|
321
|
+
|
|
322
|
+
PRINTFLIKE_DECL(2, 3)
|
|
323
|
+
static void
|
|
324
|
+
_noko_xml_xpath_context__ignore_error(void *data, const char *msg, ...)
|
|
325
|
+
{
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
static void
|
|
329
|
+
_noko_xml_xpath_context__abort_evaluation(xmlXPathParserContextPtr ctxt)
|
|
330
|
+
{
|
|
331
|
+
/* silence the error that some libxml2 versions report, which would run Ruby code */
|
|
332
|
+
xmlSetStructuredErrorFunc(NULL, NULL);
|
|
333
|
+
xmlSetGenericErrorFunc(NULL, _noko_xml_xpath_context__ignore_error);
|
|
334
|
+
|
|
335
|
+
ctxt->error = XPATH_EXPR_ERROR;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
static VALUE
|
|
339
|
+
_noko_xml_xpath_context__invoke_handler(VALUE data)
|
|
340
|
+
{
|
|
341
|
+
_noko_xml_xpath_context__invoke_args_t *args = (_noko_xml_xpath_context__invoke_args_t *)data;
|
|
342
|
+
_noko_xml_xpath_context__handler_call_t *handler_call = args->ctxt->context->userData;
|
|
343
|
+
|
|
344
|
+
Nokogiri_marshal_xpath_funcall_and_return_values(
|
|
345
|
+
args->ctxt,
|
|
346
|
+
args->argc,
|
|
347
|
+
handler_call->handler,
|
|
348
|
+
(const char *)(args->ctxt->context->function)
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
return Qnil;
|
|
352
|
+
}
|
|
353
|
+
|
|
303
354
|
static void
|
|
304
355
|
_noko_xml_xpath_context__handler_invoker(xmlXPathParserContextPtr ctxt, int argc)
|
|
305
356
|
{
|
|
306
|
-
|
|
307
|
-
|
|
357
|
+
_noko_xml_xpath_context__handler_call_t *handler_call;
|
|
358
|
+
_noko_xml_xpath_context__invoke_args_t args = { ctxt, argc };
|
|
308
359
|
|
|
309
360
|
assert(ctxt);
|
|
310
361
|
assert(ctxt->context);
|
|
311
362
|
assert(ctxt->context->userData);
|
|
312
363
|
assert(ctxt->context->function);
|
|
313
364
|
|
|
314
|
-
|
|
315
|
-
method_name = (const char *)(ctxt->context->function);
|
|
365
|
+
handler_call = ctxt->context->userData;
|
|
316
366
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
367
|
+
if (handler_call->exception_state == 0) {
|
|
368
|
+
rb_protect(_noko_xml_xpath_context__invoke_handler, (VALUE)&args, &handler_call->exception_state);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (handler_call->exception_state != 0) {
|
|
372
|
+
_noko_xml_xpath_context__abort_evaluation(ctxt);
|
|
373
|
+
}
|
|
323
374
|
}
|
|
324
375
|
|
|
325
|
-
static
|
|
326
|
-
|
|
376
|
+
static VALUE
|
|
377
|
+
_noko_xml_xpath_context__handler_responds_to(VALUE data)
|
|
327
378
|
{
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
379
|
+
_noko_xml_xpath_context__lookup_args_t *args = (_noko_xml_xpath_context__lookup_args_t *)data;
|
|
380
|
+
|
|
381
|
+
if (rb_respond_to(args->handler, rb_intern((const char *)args->name))) {
|
|
382
|
+
if (args->ns_uri == NULL) {
|
|
331
383
|
NOKO_WARN_DEPRECATION("A custom XPath or CSS handler function named '%s' is being invoked without a namespace. Please update your query to reference this function as 'nokogiri:%s'. Invoking custom handler functions without a namespace is deprecated and will become an error in Nokogiri v1.17.0.",
|
|
332
|
-
|
|
384
|
+
args->name, args->name); // TODO deprecated in v1.15.0, remove in v1.19.0
|
|
333
385
|
}
|
|
386
|
+
return Qtrue;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return Qfalse;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
static xmlXPathFunction
|
|
393
|
+
_noko_xml_xpath_context_handler_lookup(void *data, const xmlChar *c_name, const xmlChar *c_ns_uri)
|
|
394
|
+
{
|
|
395
|
+
_noko_xml_xpath_context__handler_call_t *handler_call = data;
|
|
396
|
+
_noko_xml_xpath_context__lookup_args_t args = { handler_call->handler, c_name, c_ns_uri };
|
|
397
|
+
VALUE responds_to = Qfalse;
|
|
398
|
+
|
|
399
|
+
if (handler_call->exception_state == 0) {
|
|
400
|
+
responds_to = rb_protect(_noko_xml_xpath_context__handler_responds_to, (VALUE)&args, &handler_call->exception_state);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/*
|
|
404
|
+
* After an exception, return the invoker anyway, so that it aborts the evaluation. Returning NULL
|
|
405
|
+
* would make libxml2 fall back to its own functions, or report an unknown function.
|
|
406
|
+
*/
|
|
407
|
+
if (handler_call->exception_state != 0 || RTEST(responds_to)) {
|
|
334
408
|
return _noko_xml_xpath_context__handler_invoker;
|
|
335
409
|
}
|
|
336
410
|
|
|
@@ -381,6 +455,8 @@ noko_xml_xpath_context_evaluate(int argc, VALUE *argv, VALUE rb_context)
|
|
|
381
455
|
VALUE rb_errors = rb_ary_new();
|
|
382
456
|
xmlXPathObjectPtr c_xpath_object;
|
|
383
457
|
VALUE rb_xpath_object = Qnil;
|
|
458
|
+
_noko_xml_xpath_context__handler_call_t handler_call = { Qnil, 0 };
|
|
459
|
+
void *previous_user_data;
|
|
384
460
|
|
|
385
461
|
TypedData_Get_Struct(rb_context, xmlXPathContext, &_noko_xml_xpath_context_type, c_context);
|
|
386
462
|
|
|
@@ -388,13 +464,17 @@ noko_xml_xpath_context_evaluate(int argc, VALUE *argv, VALUE rb_context)
|
|
|
388
464
|
|
|
389
465
|
c_expression_str = (xmlChar *)StringValueCStr(rb_expression);
|
|
390
466
|
|
|
467
|
+
/* restored afterwards, because a handler may call evaluate re-entrantly on this context */
|
|
468
|
+
previous_user_data = c_context->userData;
|
|
469
|
+
|
|
391
470
|
if (Qnil != rb_function_lookup_handler) {
|
|
471
|
+
handler_call.handler = rb_function_lookup_handler;
|
|
392
472
|
/* FIXME: not sure if this is the correct place to shove private data. */
|
|
393
|
-
c_context->userData =
|
|
473
|
+
c_context->userData = &handler_call;
|
|
394
474
|
xmlXPathRegisterFuncLookup(
|
|
395
475
|
c_context,
|
|
396
476
|
_noko_xml_xpath_context_handler_lookup,
|
|
397
|
-
|
|
477
|
+
&handler_call
|
|
398
478
|
);
|
|
399
479
|
}
|
|
400
480
|
|
|
@@ -408,6 +488,12 @@ noko_xml_xpath_context_evaluate(int argc, VALUE *argv, VALUE rb_context)
|
|
|
408
488
|
xmlSetGenericErrorFunc(NULL, NULL);
|
|
409
489
|
|
|
410
490
|
xmlXPathRegisterFuncLookup(c_context, NULL, NULL);
|
|
491
|
+
c_context->userData = previous_user_data;
|
|
492
|
+
|
|
493
|
+
if (handler_call.exception_state != 0) {
|
|
494
|
+
xmlXPathFreeObject(c_xpath_object);
|
|
495
|
+
rb_jump_tag(handler_call.exception_state);
|
|
496
|
+
}
|
|
411
497
|
|
|
412
498
|
if (c_xpath_object == NULL) {
|
|
413
499
|
rb_exc_raise(rb_ary_entry(rb_errors, 0));
|
|
@@ -436,6 +522,12 @@ noko_xml_xpath_context_new(VALUE klass, VALUE rb_node)
|
|
|
436
522
|
xmlXPathContextPtr c_context;
|
|
437
523
|
VALUE rb_context;
|
|
438
524
|
|
|
525
|
+
if (!rb_obj_is_kind_of(rb_node, cNokogiriXmlNode)) {
|
|
526
|
+
rb_raise(rb_eTypeError,
|
|
527
|
+
"expected a Nokogiri::XML::Node, received %"PRIsVALUE,
|
|
528
|
+
rb_obj_class(rb_node));
|
|
529
|
+
}
|
|
530
|
+
|
|
439
531
|
Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
|
|
440
532
|
|
|
441
533
|
#if LIBXML_VERSION < 21000
|
|
@@ -468,6 +560,12 @@ noko_xml_xpath_context_set_node(VALUE rb_context, VALUE rb_node)
|
|
|
468
560
|
xmlNodePtr c_node;
|
|
469
561
|
xmlXPathContextPtr c_context;
|
|
470
562
|
|
|
563
|
+
if (!rb_obj_is_kind_of(rb_node, cNokogiriXmlNode)) {
|
|
564
|
+
rb_raise(rb_eTypeError,
|
|
565
|
+
"expected a Nokogiri::XML::Node, received %"PRIsVALUE,
|
|
566
|
+
rb_obj_class(rb_node));
|
|
567
|
+
}
|
|
568
|
+
|
|
471
569
|
TypedData_Get_Struct(rb_context, xmlXPathContext, &_noko_xml_xpath_context_type, c_context);
|
|
472
570
|
Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
|
|
473
571
|
|
|
@@ -6,7 +6,19 @@ static void
|
|
|
6
6
|
mark(void *data)
|
|
7
7
|
{
|
|
8
8
|
nokogiriXsltStylesheetTuple *wrapper = (nokogiriXsltStylesheetTuple *)data;
|
|
9
|
+
long i;
|
|
10
|
+
|
|
11
|
+
/* GC can run while Nokogiri_wrap_xslt_stylesheet allocates func_instances. */
|
|
12
|
+
if (!RB_TYPE_P(wrapper->func_instances, T_ARRAY)) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
9
16
|
rb_gc_mark(wrapper->func_instances);
|
|
17
|
+
|
|
18
|
+
/* libxslt retains each extension handler instance's movable VALUE as module data. */
|
|
19
|
+
for (i = 0; i < RARRAY_LEN(wrapper->func_instances); i++) {
|
|
20
|
+
rb_gc_mark(RARRAY_AREF(wrapper->func_instances, i));
|
|
21
|
+
}
|
|
10
22
|
}
|
|
11
23
|
|
|
12
24
|
static void
|
|
@@ -18,11 +30,23 @@ dealloc(void *data)
|
|
|
18
30
|
ruby_xfree(wrapper);
|
|
19
31
|
}
|
|
20
32
|
|
|
33
|
+
/* libxslt retains this Stylesheet's own movable VALUE in _private. */
|
|
34
|
+
static void
|
|
35
|
+
update_references(void *data)
|
|
36
|
+
{
|
|
37
|
+
nokogiriXsltStylesheetTuple *wrapper = (nokogiriXsltStylesheetTuple *)data;
|
|
38
|
+
|
|
39
|
+
if (wrapper->ss && wrapper->ss->_private) {
|
|
40
|
+
wrapper->ss->_private = (void *)rb_gc_location((VALUE)wrapper->ss->_private);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
21
44
|
static const rb_data_type_t nokogiri_xslt_stylesheet_tuple_type = {
|
|
22
45
|
.wrap_struct_name = "nokogiriXsltStylesheetTuple",
|
|
23
46
|
.function = {
|
|
24
47
|
.dmark = mark,
|
|
25
48
|
.dfree = dealloc,
|
|
49
|
+
.dcompact = update_references,
|
|
26
50
|
},
|
|
27
51
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
28
52
|
};
|
|
@@ -47,6 +71,29 @@ xslt_generic_error_handler(void *ctx, const char *msg, ...)
|
|
|
47
71
|
rb_str_concat((VALUE)ctx, message);
|
|
48
72
|
}
|
|
49
73
|
|
|
74
|
+
PRINTFLIKE_DECL(2, 3)
|
|
75
|
+
static void
|
|
76
|
+
ignore_error(void *ctx, const char *msg, ...)
|
|
77
|
+
{
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
* A Ruby exception must not unwind through libxslt's stack frames, because that skips libxslt's
|
|
82
|
+
* cleanup and leaks memory, and skips the reset of the error handlers. So an extension function is
|
|
83
|
+
* called under rb_protect, and the exception is re-raised after the transform returns.
|
|
84
|
+
*
|
|
85
|
+
* Until then, no Ruby code may run: the pending exception lives only in the thread's errinfo,
|
|
86
|
+
* which a rescue elsewhere would overwrite.
|
|
87
|
+
*/
|
|
88
|
+
typedef struct {
|
|
89
|
+
int exception_state;
|
|
90
|
+
} _noko_xslt_stylesheet__function_call_t;
|
|
91
|
+
|
|
92
|
+
typedef struct {
|
|
93
|
+
xmlXPathParserContextPtr ctxt;
|
|
94
|
+
int nargs;
|
|
95
|
+
} _noko_xslt_stylesheet__invoke_args_t;
|
|
96
|
+
|
|
50
97
|
VALUE
|
|
51
98
|
Nokogiri_wrap_xslt_stylesheet(xsltStylesheetPtr ss)
|
|
52
99
|
{
|
|
@@ -137,15 +184,14 @@ rb_xslt_stylesheet_serialize(VALUE self, VALUE xmlobj)
|
|
|
137
184
|
/*
|
|
138
185
|
* Build the C-string params array passed to xsltApplyStylesheet.
|
|
139
186
|
*
|
|
140
|
-
* Note: params[j] is a
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* no longer reads params[].
|
|
187
|
+
* Note: params[j] is a copy of the Ruby string's bytes, not a pointer into its buffer, because GC
|
|
188
|
+
* compaction moves the bytes of short strings that are embedded in their object slots. Compaction
|
|
189
|
+
* can run during a #to_str conversion in this loop and, when libxml2 uses Ruby's allocator, during
|
|
190
|
+
* libxslt's own allocations. Free params with free_xslt_params.
|
|
145
191
|
*/
|
|
146
192
|
typedef struct {
|
|
147
193
|
VALUE rb_param;
|
|
148
|
-
|
|
194
|
+
rb_len_t param_len;
|
|
149
195
|
const char **params;
|
|
150
196
|
} build_xslt_params_args_t;
|
|
151
197
|
|
|
@@ -154,14 +200,23 @@ build_xslt_params(VALUE args_ptr)
|
|
|
154
200
|
{
|
|
155
201
|
build_xslt_params_args_t *args = (build_xslt_params_args_t *)args_ptr;
|
|
156
202
|
|
|
157
|
-
for (
|
|
203
|
+
for (rb_len_t j = 0; j < args->param_len; j++) {
|
|
158
204
|
VALUE entry = rb_ary_entry(args->rb_param, j);
|
|
159
|
-
args->params[j] = StringValueCStr(entry);
|
|
205
|
+
args->params[j] = ruby_strdup(StringValueCStr(entry));
|
|
160
206
|
}
|
|
161
207
|
|
|
162
208
|
return Qnil;
|
|
163
209
|
}
|
|
164
210
|
|
|
211
|
+
static void
|
|
212
|
+
free_xslt_params(const char **params, rb_len_t param_len)
|
|
213
|
+
{
|
|
214
|
+
for (rb_len_t j = 0; j < param_len; j++) {
|
|
215
|
+
ruby_xfree((void *)params[j]);
|
|
216
|
+
}
|
|
217
|
+
ruby_xfree(params);
|
|
218
|
+
}
|
|
219
|
+
|
|
165
220
|
/*
|
|
166
221
|
* call-seq:
|
|
167
222
|
* transform(document)
|
|
@@ -283,9 +338,10 @@ rb_xslt_stylesheet_transform(int argc, VALUE *argv, VALUE self)
|
|
|
283
338
|
xmlDocPtr c_result_document ;
|
|
284
339
|
nokogiriXsltStylesheetTuple *wrapper;
|
|
285
340
|
const char **params ;
|
|
286
|
-
|
|
341
|
+
rb_len_t param_len ;
|
|
287
342
|
int parse_error_occurred ;
|
|
288
343
|
int defensive_copy_p = 0;
|
|
344
|
+
_noko_xslt_stylesheet__function_call_t function_call = { 0 };
|
|
289
345
|
|
|
290
346
|
rb_scan_args(argc, argv, "11", &rb_document, &rb_param);
|
|
291
347
|
if (NIL_P(rb_param)) { rb_param = rb_ary_new2(0L) ; }
|
|
@@ -314,7 +370,7 @@ rb_xslt_stylesheet_transform(int argc, VALUE *argv, VALUE self)
|
|
|
314
370
|
|
|
315
371
|
rb_protect(build_xslt_params, (VALUE)&args, &state);
|
|
316
372
|
if (state) {
|
|
317
|
-
|
|
373
|
+
free_xslt_params(params, param_len);
|
|
318
374
|
rb_jump_tag(state);
|
|
319
375
|
}
|
|
320
376
|
}
|
|
@@ -329,13 +385,24 @@ rb_xslt_stylesheet_transform(int argc, VALUE *argv, VALUE self)
|
|
|
329
385
|
}
|
|
330
386
|
xsltFreeTransformContext(c_transform_context);
|
|
331
387
|
|
|
388
|
+
c_transform_context = xsltNewTransformContext(wrapper->ss, c_document);
|
|
389
|
+
if (c_transform_context == NULL) {
|
|
390
|
+
free_xslt_params(params, param_len);
|
|
391
|
+
if (defensive_copy_p) {
|
|
392
|
+
xmlFreeDoc(c_document);
|
|
393
|
+
}
|
|
394
|
+
rb_raise(rb_eRuntimeError, "could not create an XSLT transform context");
|
|
395
|
+
}
|
|
396
|
+
c_transform_context->_private = &function_call;
|
|
397
|
+
|
|
332
398
|
rb_error_str = rb_str_new(0, 0);
|
|
333
399
|
xsltSetGenericErrorFunc((void *)rb_error_str, xslt_generic_error_handler);
|
|
334
400
|
xmlSetGenericErrorFunc((void *)rb_error_str, xslt_generic_error_handler);
|
|
335
401
|
|
|
336
|
-
c_result_document =
|
|
402
|
+
c_result_document = xsltApplyStylesheetUser(wrapper->ss, c_document, params, NULL, NULL, c_transform_context);
|
|
403
|
+
xsltFreeTransformContext(c_transform_context);
|
|
337
404
|
|
|
338
|
-
|
|
405
|
+
free_xslt_params(params, param_len);
|
|
339
406
|
if (defensive_copy_p) {
|
|
340
407
|
xmlFreeDoc(c_document);
|
|
341
408
|
c_document = NULL;
|
|
@@ -344,6 +411,11 @@ rb_xslt_stylesheet_transform(int argc, VALUE *argv, VALUE self)
|
|
|
344
411
|
xsltSetGenericErrorFunc(NULL, NULL);
|
|
345
412
|
xmlSetGenericErrorFunc(NULL, NULL);
|
|
346
413
|
|
|
414
|
+
if (function_call.exception_state != 0) {
|
|
415
|
+
xmlFreeDoc(c_result_document);
|
|
416
|
+
rb_jump_tag(function_call.exception_state);
|
|
417
|
+
}
|
|
418
|
+
|
|
347
419
|
parse_error_occurred = (Qfalse == rb_funcall(rb_error_str, rb_intern("empty?"), 0));
|
|
348
420
|
|
|
349
421
|
if (parse_error_occurred) {
|
|
@@ -353,25 +425,42 @@ rb_xslt_stylesheet_transform(int argc, VALUE *argv, VALUE self)
|
|
|
353
425
|
return noko_xml_document_wrap((VALUE)0, c_result_document) ;
|
|
354
426
|
}
|
|
355
427
|
|
|
356
|
-
static
|
|
357
|
-
|
|
428
|
+
static VALUE
|
|
429
|
+
invoke_function(VALUE data)
|
|
358
430
|
{
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
const xmlChar *functionURI;
|
|
363
|
-
|
|
364
|
-
transform = xsltXPathGetTransformContext(ctxt);
|
|
365
|
-
functionURI = ctxt->context->functionURI;
|
|
366
|
-
handler = (VALUE)xsltGetExtData(transform, functionURI);
|
|
367
|
-
function_name = (const char *)(ctxt->context->function);
|
|
431
|
+
_noko_xslt_stylesheet__invoke_args_t *args = (_noko_xslt_stylesheet__invoke_args_t *)data;
|
|
432
|
+
xsltTransformContextPtr transform = xsltXPathGetTransformContext(args->ctxt);
|
|
433
|
+
VALUE handler = (VALUE)xsltGetExtData(transform, args->ctxt->context->functionURI);
|
|
368
434
|
|
|
369
435
|
Nokogiri_marshal_xpath_funcall_and_return_values(
|
|
370
|
-
ctxt,
|
|
371
|
-
nargs,
|
|
436
|
+
args->ctxt,
|
|
437
|
+
args->nargs,
|
|
372
438
|
handler,
|
|
373
|
-
(const char *)
|
|
439
|
+
(const char *)(args->ctxt->context->function)
|
|
374
440
|
);
|
|
441
|
+
|
|
442
|
+
return Qnil;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
static void
|
|
446
|
+
method_caller(xmlXPathParserContextPtr ctxt, int nargs)
|
|
447
|
+
{
|
|
448
|
+
xsltTransformContextPtr transform = xsltXPathGetTransformContext(ctxt);
|
|
449
|
+
_noko_xslt_stylesheet__function_call_t *function_call = transform->_private;
|
|
450
|
+
_noko_xslt_stylesheet__invoke_args_t args = { ctxt, nargs };
|
|
451
|
+
|
|
452
|
+
if (function_call->exception_state == 0) {
|
|
453
|
+
rb_protect(invoke_function, (VALUE)&args, &function_call->exception_state);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (function_call->exception_state != 0) {
|
|
457
|
+
/* silence the errors libxslt reports while it stops, which would run Ruby code */
|
|
458
|
+
xsltSetGenericErrorFunc(NULL, ignore_error);
|
|
459
|
+
xmlSetGenericErrorFunc(NULL, ignore_error);
|
|
460
|
+
|
|
461
|
+
ctxt->error = XPATH_EXPR_ERROR;
|
|
462
|
+
transform->state = XSLT_STATE_STOPPED;
|
|
463
|
+
}
|
|
375
464
|
}
|
|
376
465
|
|
|
377
466
|
static void *
|
|
@@ -412,6 +501,7 @@ shutdownFunc(xsltTransformContextPtr ctxt,
|
|
|
412
501
|
const xmlChar *uri, void *data)
|
|
413
502
|
{
|
|
414
503
|
nokogiriXsltStylesheetTuple *wrapper;
|
|
504
|
+
long i;
|
|
415
505
|
|
|
416
506
|
TypedData_Get_Struct(
|
|
417
507
|
(VALUE)ctxt->style->_private,
|
|
@@ -420,7 +510,13 @@ shutdownFunc(xsltTransformContextPtr ctxt,
|
|
|
420
510
|
wrapper
|
|
421
511
|
);
|
|
422
512
|
|
|
423
|
-
|
|
513
|
+
/* Other transform contexts, such as a nested transform's, may still use their instances. */
|
|
514
|
+
for (i = 0; i < RARRAY_LEN(wrapper->func_instances); i++) {
|
|
515
|
+
if (RARRAY_AREF(wrapper->func_instances, i) == (VALUE)data) {
|
|
516
|
+
rb_ary_delete_at(wrapper->func_instances, i);
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
424
520
|
}
|
|
425
521
|
|
|
426
522
|
/* docstring is in lib/nokogiri/xslt.rb */
|