Thread safety Functions#329
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Refactors REXML XPath function handling to use instance-scoped state (context/namespaces/variables) instead of module-level shared state, improving isolation and enabling safer concurrent use.
Changes:
- Introduces
FunctionsClasswith instance variables forcontext,namespace_context, andvariables, plus a new default singletonFunctions. - Updates
XPathParserto use an instance ofFunctionsClassfor boolean/number/string conversions and function dispatch. - Removes
include FunctionsfromREXML::XPathandREXML::QuickPath.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| lib/rexml/xpath_parser.rb | Switches function evaluation and coercions to an instance (@functions) rather than global Functions state. |
| lib/rexml/xpath.rb | Removes include Functions, altering how XPath functions are accessed through REXML::XPath. |
| lib/rexml/quickpath.rb | Removes include Functions, altering QuickPath’s access to XPath functions. |
| lib/rexml/functions.rb | Replaces module Functions with FunctionsClass and introduces a default singleton instance Functions. |
Comments suppressed due to low confidence (1)
lib/rexml/functions.rb:1
@@available_functionsis automatically populated from all non-internal instance methods viamethod_added, andsendwill then invoke them viasuper(bypassing visibility checks). This makes it easy to accidentally expose non-XPath helper methods as callable XPath functions when new instance methods are added in the future. A safer approach is to explicitly whitelist exported XPath functions (or mark helper methods private and exclude private methods from registration), rather than implicitly exporting everything not listed inINTERNAL_METHODS.
# frozen_string_literal: false
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ] | ||
| class << self | ||
| def singleton_method_added(name) | ||
| def method_added(name) |
There was a problem hiding this comment.
As copilot also mentions:
This makes it easy to accidentally expose non-XPath helper methods as callable XPath functions when new instance methods are added in the future.
Relying on this is not good. As a result, compare-language is exposed as a function.
Just an idea, perhaps introducing register method something like this may improve maintainability. (Similar problem also exist in Reline's key binding method)
register_function def position()
...
end|
@tompng |
REXML::Functions have a state and is not thread-safe. Change it to a class, always create a new instance in XPath#match. `REXML::Functions = REXML::FunctionsClass.new` is kept for untested REXML::Quickpath and for test code.
| @functions.context = target_context | ||
| return @functions.send(func_name, *args) |
REXML::Functions have a state and is not thread-safe.
Change it to a class, always create a new instance in
XPath#match.REXML::Functions = REXML::FunctionsClass.newis kept for untestedREXML::Quickpathand for test code.Bug reproduction code
After about 10 seconds, it raises
"Shouldn't happen"About deleted
include FunctionsFunctions doesn't have instance methods, only have one constant for internal use.
include Functionshad no effect.