diff options
| author | Arseny Kapoulkine <arseny.kapoulkine@gmail.com> | 2017-02-05 21:34:54 -0800 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-02-05 21:34:54 -0800 | 
| commit | a9fe2bb62e0976ab74fdb5266cc3725250eca075 (patch) | |
| tree | 85c96e92afb2e3437b0eb20c805227b91e5e69f2 /src | |
| parent | d3b9e4e1e85d0aca562d0e6b62533e68e5a4a749 (diff) | |
| parent | 10676b6b8548ddbf9458993062e6a27c2c233d48 (diff) | |
Merge pull request #131 from zeux/xpath-noeh
XPath: Remove exceptional control flow
Diffstat (limited to 'src')
| -rw-r--r-- | src/pugixml.cpp | 521 | 
1 files changed, 295 insertions, 226 deletions
| diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 0813ae6..7e6fe64 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -29,9 +29,6 @@  #ifndef PUGIXML_NO_XPATH  #	include <math.h>  #	include <float.h> -#	ifdef PUGIXML_NO_EXCEPTIONS -#		include <setjmp.h> -#	endif  #endif  #ifndef PUGIXML_NO_STL @@ -47,10 +44,8 @@  #	pragma warning(push)  #	pragma warning(disable: 4127) // conditional expression is constant  #	pragma warning(disable: 4324) // structure was padded due to __declspec(align()) -#	pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable  #	pragma warning(disable: 4702) // unreachable code  #	pragma warning(disable: 4996) // this function or variable may be unsafe -#	pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged  #endif  #ifdef __INTEL_COMPILER @@ -6118,7 +6113,7 @@ namespace pugi  			if (j != _root)  				result[--offset] = delimiter; -			if (j->name && *j->name) +			if (j->name)  			{  				size_t length = impl::strlength(j->name); @@ -6137,7 +6132,7 @@ namespace pugi  	{  		xml_node found = *this; // Current search context. -		if (!_root || !path_ || !path_[0]) return found; +		if (!_root || !path_[0]) return found;  		if (path_[0] == delimiter)  		{ @@ -7392,24 +7387,17 @@ PUGI__NS_BEGIN  		};  	}; -	class xpath_allocator +	struct xpath_allocator  	{  		xpath_memory_block* _root;  		size_t _root_size; +		bool* _error; -	public: -	#ifdef PUGIXML_NO_EXCEPTIONS -		jmp_buf* error_handler; -	#endif - -		xpath_allocator(xpath_memory_block* root, size_t root_size = 0): _root(root), _root_size(root_size) +		xpath_allocator(xpath_memory_block* root, bool* error = 0): _root(root), _root_size(0), _error(error)  		{ -		#ifdef PUGIXML_NO_EXCEPTIONS -			error_handler = 0; -		#endif  		} -		void* allocate_nothrow(size_t size) +		void* allocate(size_t size)  		{  			// round size up to block alignment boundary  			size = (size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); @@ -7430,7 +7418,11 @@ PUGI__NS_BEGIN  				size_t block_size = block_capacity + offsetof(xpath_memory_block, data);  				xpath_memory_block* block = static_cast<xpath_memory_block*>(xml_memory::allocate(block_size)); -				if (!block) return 0; +				if (!block) +				{ +					if (_error) *_error = true; +					return 0; +				}  				block->next = _root;  				block->capacity = block_capacity; @@ -7442,23 +7434,6 @@ PUGI__NS_BEGIN  			}  		} -		void* allocate(size_t size) -		{ -			void* result = allocate_nothrow(size); - -			if (!result) -			{ -			#ifdef PUGIXML_NO_EXCEPTIONS -				assert(error_handler); -				longjmp(*error_handler, 1); -			#else -				throw std::bad_alloc(); -			#endif -			} - -			return result; -		} -  		void* reallocate(void* ptr, size_t old_size, size_t new_size)  		{  			// round size up to block alignment boundary @@ -7468,33 +7443,35 @@ PUGI__NS_BEGIN  			// we can only reallocate the last object  			assert(ptr == 0 || static_cast<char*>(ptr) + old_size == &_root->data[0] + _root_size); -			// adjust root size so that we have not allocated the object at all -			bool only_object = (_root_size == old_size); - -			if (ptr) _root_size -= old_size; +			// try to reallocate the object inplace +			if (ptr && _root_size - old_size + new_size <= _root->capacity) +			{ +				_root_size = _root_size - old_size + new_size; +				return ptr; +			} -			// allocate a new version (this will obviously reuse the memory if possible) +			// allocate a new block  			void* result = allocate(new_size); -			assert(result); +			if (!result) return 0;  			// we have a new block -			if (result != ptr && ptr) +			if (ptr)  			{ -				// copy old data +				// copy old data (we only support growing)  				assert(new_size >= old_size);  				memcpy(result, ptr, old_size);  				// free the previous page if it had no other objects -				if (only_object) -				{ -					assert(_root->data == result); -					assert(_root->next); +				assert(_root->data == result); +				assert(_root->next); +				if (_root->next->data == ptr) +				{ +					// deallocate the whole page, unless it was the first one  					xpath_memory_block* next = _root->next->next;  					if (next)  					{ -						// deallocate the whole page, unless it was the first one  						xml_memory::deallocate(_root->next);  						_root->next = next;  					} @@ -7566,22 +7543,15 @@ PUGI__NS_BEGIN  		xpath_allocator result;  		xpath_allocator temp;  		xpath_stack stack; +		bool oom; -	#ifdef PUGIXML_NO_EXCEPTIONS -		jmp_buf error_handler; -	#endif - -		xpath_stack_data(): result(blocks + 0), temp(blocks + 1) +		xpath_stack_data(): result(blocks + 0, &oom), temp(blocks + 1, &oom), oom(false)  		{  			blocks[0].next = blocks[1].next = 0;  			blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data);  			stack.result = &result;  			stack.temp = &temp; - -		#ifdef PUGIXML_NO_EXCEPTIONS -			result.error_handler = temp.error_handler = &error_handler; -		#endif  		}  		~xpath_stack_data() @@ -7603,7 +7573,7 @@ PUGI__NS_BEGIN  		static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc)  		{  			char_t* result = static_cast<char_t*>(alloc->allocate((length + 1) * sizeof(char_t))); -			assert(result); +			if (!result) return 0;  			memcpy(result, string, length * sizeof(char_t));  			result[length] = 0; @@ -7632,9 +7602,13 @@ PUGI__NS_BEGIN  		{  			assert(begin <= end); +			if (begin == end) +				return xpath_string(); +  			size_t length = static_cast<size_t>(end - begin); +			const char_t* data = duplicate_string(begin, length, alloc); -			return length == 0 ? xpath_string() : xpath_string(duplicate_string(begin, length, alloc), true, length); +			return data ? xpath_string(data, true, length) : xpath_string();  		}  		xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0) @@ -7660,7 +7634,7 @@ PUGI__NS_BEGIN  				// allocate new buffer  				char_t* result = static_cast<char_t*>(alloc->reallocate(_uses_heap ? const_cast<char_t*>(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t))); -				assert(result); +				if (!result) return;  				// append first string to the new buffer in case there was no reallocation  				if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t)); @@ -7692,8 +7666,11 @@ PUGI__NS_BEGIN  			if (!_uses_heap)  			{  				size_t length_ = strlength(_buffer); +				const char_t* data_ = duplicate_string(_buffer, length_, alloc); -				_buffer = duplicate_string(_buffer, length_, alloc); +				if (!data_) return 0; + +				_buffer = data_;  				_uses_heap = true;  				_length_heap = length_;  			} @@ -8117,7 +8094,7 @@ PUGI__NS_BEGIN  		// allocate a buffer of suitable length for the number  		size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4;  		char_t* result = static_cast<char_t*>(alloc->allocate(sizeof(char_t) * result_size)); -		assert(result); +		if (!result) return xpath_string();  		// make the number!  		char_t* s = result; @@ -8401,12 +8378,10 @@ PUGI__NS_BEGIN  			if (!table[i])  				table[i] = static_cast<unsigned char>(i); -		void* result = alloc->allocate_nothrow(sizeof(table)); +		void* result = alloc->allocate(sizeof(table)); +		if (!result) return 0; -		if (result) -		{ -			memcpy(result, table, sizeof(table)); -		} +		memcpy(result, table, sizeof(table));  		return static_cast<unsigned char*>(result);  	} @@ -8749,7 +8724,7 @@ PUGI__NS_BEGIN  			{  				// reallocate the old array or allocate a new one  				xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node))); -				assert(data); +				if (!data) return;  				// finalize  				_begin = data; @@ -8801,7 +8776,7 @@ PUGI__NS_BEGIN  		// reallocate the old array or allocate a new one  		xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node))); -		assert(data); +		if (!data) return;  		// finalize  		_begin = data; @@ -10387,7 +10362,7 @@ PUGI__NS_BEGIN  			if (count > sizeof(static_buffer) / sizeof(static_buffer[0]))  			{  				buffer = static_cast<xpath_string*>(stack.temp->allocate(count * sizeof(xpath_string))); -				assert(buffer); +				if (!buffer) return xpath_string();  			}  			// evaluate all strings to temporary stack @@ -10405,7 +10380,7 @@ PUGI__NS_BEGIN  			// create final string  			char_t* result = static_cast<char_t*>(stack.result->allocate((length + 1) * sizeof(char_t))); -			assert(result); +			if (!result) return xpath_string();  			char_t* ri = result; @@ -10572,6 +10547,8 @@ PUGI__NS_BEGIN  				xpath_string s = string_value(c.n, stack.result);  				char_t* begin = s.data(stack.result); +				if (!begin) return xpath_string(); +  				char_t* end = normalize_space(begin);  				return xpath_string::from_heap_preallocated(begin, end); @@ -10582,6 +10559,8 @@ PUGI__NS_BEGIN  				xpath_string s = _left->eval_string(c, stack);  				char_t* begin = s.data(stack.result); +				if (!begin) return xpath_string(); +  				char_t* end = normalize_space(begin);  				return xpath_string::from_heap_preallocated(begin, end); @@ -10598,6 +10577,8 @@ PUGI__NS_BEGIN  				xpath_string to = _right->_next->eval_string(c, swapped_stack);  				char_t* begin = s.data(stack.result); +				if (!begin) return xpath_string(); +  				char_t* end = translate(begin, from.c_str(), to.c_str(), to.length());  				return xpath_string::from_heap_preallocated(begin, end); @@ -10608,6 +10589,8 @@ PUGI__NS_BEGIN  				xpath_string s = _left->eval_string(c, stack);  				char_t* begin = s.data(stack.result); +				if (!begin) return xpath_string(); +  				char_t* end = translate_table(begin, _data.table);  				return xpath_string::from_heap_preallocated(begin, end); @@ -10917,66 +10900,77 @@ PUGI__NS_BEGIN  		char_t _scratch[32]; -	#ifdef PUGIXML_NO_EXCEPTIONS -		jmp_buf _error_handler; -	#endif - -		void throw_error(const char* message) +		xpath_ast_node* error(const char* message)  		{  			_result->error = message;  			_result->offset = _lexer.current_pos() - _query; -		#ifdef PUGIXML_NO_EXCEPTIONS -			longjmp(_error_handler, 1); -		#else -			throw xpath_exception(*_result); -		#endif +			return 0;  		} -		void throw_error_oom() +		xpath_ast_node* error_oom()  		{ -		#ifdef PUGIXML_NO_EXCEPTIONS -			throw_error("Out of memory"); -		#else -			throw std::bad_alloc(); -		#endif +			assert(_alloc->_error); +			*_alloc->_error = true; + +			return 0;  		}  		void* alloc_node()  		{ -			void* result = _alloc->allocate_nothrow(sizeof(xpath_ast_node)); +			return _alloc->allocate(sizeof(xpath_ast_node)); +		} -			if (!result) throw_error_oom(); +		xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, const char_t* value) +		{ +			void* memory = alloc_node(); +			return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0; +		} -			return result; +		xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, double value) +		{ +			void* memory = alloc_node(); +			return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0;  		} -		const char_t* alloc_string(const xpath_lexer_string& value) +		xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_variable* value)  		{ -			if (value.begin) -			{ -				size_t length = static_cast<size_t>(value.end - value.begin); +			void* memory = alloc_node(); +			return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0; +		} -				char_t* c = static_cast<char_t*>(_alloc->allocate_nothrow((length + 1) * sizeof(char_t))); -				if (!c) throw_error_oom(); -				assert(c); // workaround for clang static analysis +		xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_ast_node* left = 0, xpath_ast_node* right = 0) +		{ +			void* memory = alloc_node(); +			return memory ? new (memory) xpath_ast_node(type, rettype, left, right) : 0; +		} -				memcpy(c, value.begin, length * sizeof(char_t)); -				c[length] = 0; +		xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents) +		{ +			void* memory = alloc_node(); +			return memory ? new (memory) xpath_ast_node(type, left, axis, test, contents) : 0; +		} -				return c; -			} -			else return 0; +		xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test) +		{ +			void* memory = alloc_node(); +			return memory ? new (memory) xpath_ast_node(type, left, right, test) : 0;  		} -		xpath_ast_node* parse_function_helper(ast_type_t type0, ast_type_t type1, size_t argc, xpath_ast_node* args[2]) +		const char_t* alloc_string(const xpath_lexer_string& value)  		{ -			assert(argc <= 1); +			if (!value.begin) +				return PUGIXML_TEXT(""); + +			size_t length = static_cast<size_t>(value.end - value.begin); -			if (argc == 1 && args[0]->rettype() != xpath_type_node_set) -				throw_error("Function has to be applied to node set"); +			char_t* c = static_cast<char_t*>(_alloc->allocate((length + 1) * sizeof(char_t))); +			if (!c) return 0; -			return new (alloc_node()) xpath_ast_node(argc == 0 ? type0 : type1, xpath_type_string, args[0]); +			memcpy(c, value.begin, length * sizeof(char_t)); +			c[length] = 0; + +			return c;  		}  		xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2]) @@ -10985,103 +10979,110 @@ PUGI__NS_BEGIN  			{  			case 'b':  				if (name == PUGIXML_TEXT("boolean") && argc == 1) -					return new (alloc_node()) xpath_ast_node(ast_func_boolean, xpath_type_boolean, args[0]); +					return alloc_node(ast_func_boolean, xpath_type_boolean, args[0]);  				break;  			case 'c':  				if (name == PUGIXML_TEXT("count") && argc == 1)  				{ -					if (args[0]->rettype() != xpath_type_node_set) -						throw_error("Function has to be applied to node set"); - -					return new (alloc_node()) xpath_ast_node(ast_func_count, xpath_type_number, args[0]); +					if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); +					return alloc_node(ast_func_count, xpath_type_number, args[0]);  				}  				else if (name == PUGIXML_TEXT("contains") && argc == 2) -					return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_boolean, args[0], args[1]); +					return alloc_node(ast_func_contains, xpath_type_boolean, args[0], args[1]);  				else if (name == PUGIXML_TEXT("concat") && argc >= 2) -					return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]); +					return alloc_node(ast_func_concat, xpath_type_string, args[0], args[1]);  				else if (name == PUGIXML_TEXT("ceiling") && argc == 1) -					return new (alloc_node()) xpath_ast_node(ast_func_ceiling, xpath_type_number, args[0]); +					return alloc_node(ast_func_ceiling, xpath_type_number, args[0]);  				break;  			case 'f':  				if (name == PUGIXML_TEXT("false") && argc == 0) -					return new (alloc_node()) xpath_ast_node(ast_func_false, xpath_type_boolean); +					return alloc_node(ast_func_false, xpath_type_boolean);  				else if (name == PUGIXML_TEXT("floor") && argc == 1) -					return new (alloc_node()) xpath_ast_node(ast_func_floor, xpath_type_number, args[0]); +					return alloc_node(ast_func_floor, xpath_type_number, args[0]);  				break;  			case 'i':  				if (name == PUGIXML_TEXT("id") && argc == 1) -					return new (alloc_node()) xpath_ast_node(ast_func_id, xpath_type_node_set, args[0]); +					return alloc_node(ast_func_id, xpath_type_node_set, args[0]);  				break;  			case 'l':  				if (name == PUGIXML_TEXT("last") && argc == 0) -					return new (alloc_node()) xpath_ast_node(ast_func_last, xpath_type_number); +					return alloc_node(ast_func_last, xpath_type_number);  				else if (name == PUGIXML_TEXT("lang") && argc == 1) -					return new (alloc_node()) xpath_ast_node(ast_func_lang, xpath_type_boolean, args[0]); +					return alloc_node(ast_func_lang, xpath_type_boolean, args[0]);  				else if (name == PUGIXML_TEXT("local-name") && argc <= 1) -					return parse_function_helper(ast_func_local_name_0, ast_func_local_name_1, argc, args); +				{ +					if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); +					return alloc_node(argc == 0 ? ast_func_local_name_0 : ast_func_local_name_1, xpath_type_string, args[0]); +				}  				break;  			case 'n':  				if (name == PUGIXML_TEXT("name") && argc <= 1) -					return parse_function_helper(ast_func_name_0, ast_func_name_1, argc, args); +				{ +					if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); +					return alloc_node(argc == 0 ? ast_func_name_0 : ast_func_name_1, xpath_type_string, args[0]); +				}  				else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) -					return parse_function_helper(ast_func_namespace_uri_0, ast_func_namespace_uri_1, argc, args); +				{ +					if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); +					return alloc_node(argc == 0 ? ast_func_namespace_uri_0 : ast_func_namespace_uri_1, xpath_type_string, args[0]); +				}  				else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) -					return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]); +					return alloc_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]);  				else if (name == PUGIXML_TEXT("not") && argc == 1) -					return new (alloc_node()) xpath_ast_node(ast_func_not, xpath_type_boolean, args[0]); +					return alloc_node(ast_func_not, xpath_type_boolean, args[0]);  				else if (name == PUGIXML_TEXT("number") && argc <= 1) -					return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]); +					return alloc_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]);  				break;  			case 'p':  				if (name == PUGIXML_TEXT("position") && argc == 0) -					return new (alloc_node()) xpath_ast_node(ast_func_position, xpath_type_number); +					return alloc_node(ast_func_position, xpath_type_number);  				break;  			case 'r':  				if (name == PUGIXML_TEXT("round") && argc == 1) -					return new (alloc_node()) xpath_ast_node(ast_func_round, xpath_type_number, args[0]); +					return alloc_node(ast_func_round, xpath_type_number, args[0]);  				break;  			case 's':  				if (name == PUGIXML_TEXT("string") && argc <= 1) -					return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]); +					return alloc_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]);  				else if (name == PUGIXML_TEXT("string-length") && argc <= 1) -					return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]); +					return alloc_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]);  				else if (name == PUGIXML_TEXT("starts-with") && argc == 2) -					return new (alloc_node()) xpath_ast_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); +					return alloc_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]);  				else if (name == PUGIXML_TEXT("substring-before") && argc == 2) -					return new (alloc_node()) xpath_ast_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); +					return alloc_node(ast_func_substring_before, xpath_type_string, args[0], args[1]);  				else if (name == PUGIXML_TEXT("substring-after") && argc == 2) -					return new (alloc_node()) xpath_ast_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); +					return alloc_node(ast_func_substring_after, xpath_type_string, args[0], args[1]);  				else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) -					return new (alloc_node()) xpath_ast_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]); +					return alloc_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]);  				else if (name == PUGIXML_TEXT("sum") && argc == 1)  				{ -					if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); -					return new (alloc_node()) xpath_ast_node(ast_func_sum, xpath_type_number, args[0]); +					if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); +					return alloc_node(ast_func_sum, xpath_type_number, args[0]);  				}  				break;  			case 't':  				if (name == PUGIXML_TEXT("translate") && argc == 3) -					return new (alloc_node()) xpath_ast_node(ast_func_translate, xpath_type_string, args[0], args[1]); +					return alloc_node(ast_func_translate, xpath_type_string, args[0], args[1]);  				else if (name == PUGIXML_TEXT("true") && argc == 0) -					return new (alloc_node()) xpath_ast_node(ast_func_true, xpath_type_boolean); +					return alloc_node(ast_func_true, xpath_type_boolean);  				break; @@ -11089,9 +11090,7 @@ PUGI__NS_BEGIN  				break;  			} -			throw_error("Unrecognized function or wrong parameter count"); - -			return 0; +			return error("Unrecognized function or wrong parameter count");  		}  		axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified) @@ -11207,18 +11206,18 @@ PUGI__NS_BEGIN  				xpath_lexer_string name = _lexer.contents();  				if (!_variables) -					throw_error("Unknown variable: variable set is not provided"); +					return error("Unknown variable: variable set is not provided");  				xpath_variable* var = 0;  				if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var)) -					throw_error_oom(); +					return error_oom();  				if (!var) -					throw_error("Unknown variable: variable set does not contain the given name"); +					return error("Unknown variable: variable set does not contain the given name");  				_lexer.next(); -				return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var); +				return alloc_node(ast_variable, var->type(), var);  			}  			case lex_open_brace: @@ -11226,9 +11225,10 @@ PUGI__NS_BEGIN  				_lexer.next();  				xpath_ast_node* n = parse_expression(); +				if (!n) return 0;  				if (_lexer.current() != lex_close_brace) -					throw_error("Unmatched braces"); +					return error("Expected ')' to match an opening '('");  				_lexer.next(); @@ -11238,11 +11238,11 @@ PUGI__NS_BEGIN  			case lex_quoted_string:  			{  				const char_t* value = alloc_string(_lexer.contents()); +				if (!value) return 0; -				xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_string_constant, xpath_type_string, value);  				_lexer.next(); -				return n; +				return alloc_node(ast_string_constant, xpath_type_string, value);  			}  			case lex_number: @@ -11250,12 +11250,11 @@ PUGI__NS_BEGIN  				double value = 0;  				if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value)) -					throw_error_oom(); +					return error_oom(); -				xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_number_constant, xpath_type_number, value);  				_lexer.next(); -				return n; +				return alloc_node(ast_number_constant, xpath_type_number, value);  			}  			case lex_string: @@ -11269,19 +11268,20 @@ PUGI__NS_BEGIN  				xpath_ast_node* last_arg = 0;  				if (_lexer.current() != lex_open_brace) -					throw_error("Unrecognized function call"); +					return error("Unrecognized function call");  				_lexer.next(); -				if (_lexer.current() != lex_close_brace) -					args[argc++] = parse_expression(); -  				while (_lexer.current() != lex_close_brace)  				{ -					if (_lexer.current() != lex_comma) -						throw_error("No comma between function arguments"); -					_lexer.next(); +					if (argc > 0) +					{ +						if (_lexer.current() != lex_comma) +							return error("No comma between function arguments"); +						_lexer.next(); +					}  					xpath_ast_node* n = parse_expression(); +					if (!n) return 0;  					if (argc < 2) args[argc] = n;  					else last_arg->set_next(n); @@ -11296,9 +11296,7 @@ PUGI__NS_BEGIN  			}  			default: -				throw_error("Unrecognizable primary expression"); - -				return 0; +				return error("Unrecognizable primary expression");  			}  		} @@ -11308,20 +11306,23 @@ PUGI__NS_BEGIN  		xpath_ast_node* parse_filter_expression()  		{  			xpath_ast_node* n = parse_primary_expression(); +			if (!n) return 0;  			while (_lexer.current() == lex_open_square_brace)  			{  				_lexer.next(); -				xpath_ast_node* expr = parse_expression(); -  				if (n->rettype() != xpath_type_node_set) -					throw_error("Predicate has to be applied to node set"); +					return error("Predicate has to be applied to node set"); + +				xpath_ast_node* expr = parse_expression(); +				if (!expr) return 0; -				n = new (alloc_node()) xpath_ast_node(ast_filter, n, expr, predicate_default); +				n = alloc_node(ast_filter, n, expr, predicate_default); +				if (!n) return 0;  				if (_lexer.current() != lex_close_square_brace) -					throw_error("Unmatched square brace"); +					return error("Expected ']' to match an opening '['");  				_lexer.next();  			} @@ -11337,7 +11338,7 @@ PUGI__NS_BEGIN  		xpath_ast_node* parse_step(xpath_ast_node* set)  		{  			if (set && set->rettype() != xpath_type_node_set) -				throw_error("Step has to be applied to node set"); +				return error("Step has to be applied to node set");  			bool axis_specified = false;  			axis_t axis = axis_child; // implied child axis @@ -11353,13 +11354,19 @@ PUGI__NS_BEGIN  			{  				_lexer.next(); -				return new (alloc_node()) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0); +				if (_lexer.current() == lex_open_square_brace) +					return error("Predicates are not allowed after an abbreviated step"); + +				return alloc_node(ast_step, set, axis_self, nodetest_type_node, 0);  			}  			else if (_lexer.current() == lex_double_dot)  			{  				_lexer.next(); -				return new (alloc_node()) xpath_ast_node(ast_step, set, axis_parent, nodetest_type_node, 0); +				if (_lexer.current() == lex_open_square_brace) +					return error("Predicates are not allowed after an abbreviated step"); + +				return alloc_node(ast_step, set, axis_parent, nodetest_type_node, 0);  			}  			nodetest_t nt_type = nodetest_none; @@ -11376,12 +11383,12 @@ PUGI__NS_BEGIN  				{  					// parse axis name  					if (axis_specified) -						throw_error("Two axis specifiers in one step"); +						return error("Two axis specifiers in one step");  					axis = parse_axis_name(nt_name, axis_specified);  					if (!axis_specified) -						throw_error("Unknown axis"); +						return error("Unknown axis");  					// read actual node test  					_lexer.next(); @@ -11397,7 +11404,10 @@ PUGI__NS_BEGIN  						nt_name = _lexer.contents();  						_lexer.next();  					} -					else throw_error("Unrecognized node test"); +					else +					{ +						return error("Unrecognized node test"); +					}  				}  				if (nt_type == nodetest_none) @@ -11414,26 +11424,26 @@ PUGI__NS_BEGIN  							nt_type = parse_node_test_type(nt_name);  							if (nt_type == nodetest_none) -								throw_error("Unrecognized node type"); +								return error("Unrecognized node type");  							nt_name = xpath_lexer_string();  						}  						else if (nt_name == PUGIXML_TEXT("processing-instruction"))  						{  							if (_lexer.current() != lex_quoted_string) -								throw_error("Only literals are allowed as arguments to processing-instruction()"); +								return error("Only literals are allowed as arguments to processing-instruction()");  							nt_type = nodetest_pi;  							nt_name = _lexer.contents();  							_lexer.next();  							if (_lexer.current() != lex_close_brace) -								throw_error("Unmatched brace near processing-instruction()"); +								return error("Unmatched brace near processing-instruction()");  							_lexer.next();  						}  						else  						{ -							throw_error("Unmatched brace near node type test"); +							return error("Unmatched brace near node type test");  						}  					}  					// QName or NCName:* @@ -11459,11 +11469,14 @@ PUGI__NS_BEGIN  			}  			else  			{ -				throw_error("Unrecognized node test"); +				return error("Unrecognized node test");  			}  			const char_t* nt_name_copy = alloc_string(nt_name); -			xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, nt_name_copy); +			if (!nt_name_copy) return 0; + +			xpath_ast_node* n = alloc_node(ast_step, set, axis, nt_type, nt_name_copy); +			if (!n) return 0;  			xpath_ast_node* last = 0; @@ -11472,11 +11485,13 @@ PUGI__NS_BEGIN  				_lexer.next();  				xpath_ast_node* expr = parse_expression(); +				if (!expr) return 0; -				xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, 0, expr, predicate_default); +				xpath_ast_node* pred = alloc_node(ast_predicate, 0, expr, predicate_default); +				if (!pred) return 0;  				if (_lexer.current() != lex_close_square_brace) -					throw_error("Unmatched square brace"); +					return error("Expected ']' to match an opening '['");  				_lexer.next();  				if (last) last->set_next(pred); @@ -11492,6 +11507,7 @@ PUGI__NS_BEGIN  		xpath_ast_node* parse_relative_location_path(xpath_ast_node* set)  		{  			xpath_ast_node* n = parse_step(set); +			if (!n) return 0;  			while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash)  			{ @@ -11499,9 +11515,13 @@ PUGI__NS_BEGIN  				_lexer.next();  				if (l == lex_double_slash) -					n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); +				{ +					n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); +					if (!n) return 0; +				}  				n = parse_step(n); +				if (!n) return 0;  			}  			return n; @@ -11515,7 +11535,8 @@ PUGI__NS_BEGIN  			{  				_lexer.next(); -				xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); +				xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set); +				if (!n) return 0;  				// relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path  				lexeme_t l = _lexer.current(); @@ -11529,8 +11550,11 @@ PUGI__NS_BEGIN  			{  				_lexer.next(); -				xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); -				n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); +				xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set); +				if (!n) return 0; + +				n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); +				if (!n) return 0;  				return parse_relative_location_path(n);  			} @@ -11553,7 +11577,6 @@ PUGI__NS_BEGIN  			// PrimaryExpr begins with '$' in case of it being a variable reference,  			// '(' in case of it being an expression, string literal, number constant or  			// function call. -  			if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace ||  				_lexer.current() == lex_quoted_string || _lexer.current() == lex_number ||  				_lexer.current() == lex_string) @@ -11565,7 +11588,8 @@ PUGI__NS_BEGIN  					while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state; -					if (*state != '(') return parse_location_path(); +					if (*state != '(') +						return parse_location_path();  					// This looks like a function call; however this still can be a node-test. Check it.  					if (parse_node_test_type(_lexer.contents()) != nodetest_none) @@ -11573,6 +11597,7 @@ PUGI__NS_BEGIN  				}  				xpath_ast_node* n = parse_filter_expression(); +				if (!n) return 0;  				if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash)  				{ @@ -11582,9 +11607,10 @@ PUGI__NS_BEGIN  					if (l == lex_double_slash)  					{  						if (n->rettype() != xpath_type_node_set) -							throw_error("Step has to be applied to node set"); +							return error("Step has to be applied to node set"); -						n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); +						n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); +						if (!n) return 0;  					}  					// select from location path @@ -11598,9 +11624,10 @@ PUGI__NS_BEGIN  				_lexer.next();  				// precedence 7+ - only parses union expressions -				xpath_ast_node* expr = parse_expression_rec(parse_path_or_unary_expression(), 7); +				xpath_ast_node* n = parse_expression(7); +				if (!n) return 0; -				return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr); +				return alloc_node(ast_op_negate, xpath_type_number, n);  			}  			else  			{ @@ -11683,20 +11710,23 @@ PUGI__NS_BEGIN  				_lexer.next();  				xpath_ast_node* rhs = parse_path_or_unary_expression(); +				if (!rhs) return 0;  				binary_op_t nextop = binary_op_t::parse(_lexer);  				while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence)  				{  					rhs = parse_expression_rec(rhs, nextop.precedence); +					if (!rhs) return 0;  					nextop = binary_op_t::parse(_lexer);  				}  				if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set)) -					throw_error("Union operator has to be applied to node sets"); +					return error("Union operator has to be applied to node sets"); -				lhs = new (alloc_node()) xpath_ast_node(op.asttype, op.rettype, lhs, rhs); +				lhs = alloc_node(op.asttype, op.rettype, lhs, rhs); +				if (!lhs) return 0;  				op = binary_op_t::parse(_lexer);  			} @@ -11722,9 +11752,12 @@ PUGI__NS_BEGIN  		//						  | MultiplicativeExpr '*' UnaryExpr  		//						  | MultiplicativeExpr 'div' UnaryExpr  		//						  | MultiplicativeExpr 'mod' UnaryExpr -		xpath_ast_node* parse_expression() +		xpath_ast_node* parse_expression(int limit = 0)  		{ -			return parse_expression_rec(parse_path_or_unary_expression(), 0); +			xpath_ast_node* n = parse_path_or_unary_expression(); +			if (!n) return 0; + +			return parse_expression_rec(n, limit);  		}  		xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result) @@ -11733,26 +11766,21 @@ PUGI__NS_BEGIN  		xpath_ast_node* parse()  		{ -			xpath_ast_node* result = parse_expression(); +			xpath_ast_node* n = parse_expression(); +			if (!n) return 0;  			// check if there are unparsed tokens left  			if (_lexer.current() != lex_eof) -				throw_error("Incorrect query"); +				return error("Incorrect query"); -			return result; +			return n;  		}  		static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result)  		{  			xpath_parser parser(query, variables, alloc, result); -		#ifdef PUGIXML_NO_EXCEPTIONS -			int error = setjmp(parser._error_handler); - -			return (error == 0) ? parser.parse() : 0; -		#else  			return parser.parse(); -		#endif  		}  	}; @@ -11775,7 +11803,7 @@ PUGI__NS_BEGIN  			xml_memory::deallocate(impl);  		} -		xpath_query_impl(): root(0), alloc(&block) +		xpath_query_impl(): root(0), alloc(&block, &oom), oom(false)  		{  			block.next = 0;  			block.capacity = sizeof(block.data); @@ -11784,19 +11812,27 @@ PUGI__NS_BEGIN  		xpath_ast_node* root;  		xpath_allocator alloc;  		xpath_memory_block block; +		bool oom;  	};  	PUGI__FN xpath_string evaluate_string_impl(xpath_query_impl* impl, const xpath_node& n, xpath_stack_data& sd)  	{  		if (!impl) return xpath_string(); -	#ifdef PUGIXML_NO_EXCEPTIONS -		if (setjmp(sd.error_handler)) return xpath_string(); -	#endif -  		xpath_context c(n, 1, 1); -		return impl->root->eval_string(c, sd.stack); +		xpath_string r = impl->root->eval_string(c, sd.stack); + +		if (sd.oom) +		{ +		#ifdef PUGIXML_NO_EXCEPTIONS +			return xpath_string(); +		#else +			throw std::bad_alloc(); +		#endif +		} + +		return r;  	}  	PUGI__FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl) @@ -12369,6 +12405,15 @@ namespace pugi  				_impl = impl.release();  				_result.error = 0;  			} +			else +			{ +			#ifdef PUGIXML_NO_EXCEPTIONS +				if (qimpl->oom) _result.error = "Out of memory"; +			#else +				if (qimpl->oom) throw std::bad_alloc(); +				throw xpath_exception(_result); +			#endif +			}  		}  	} @@ -12421,11 +12466,18 @@ namespace pugi  		impl::xpath_context c(n, 1, 1);  		impl::xpath_stack_data sd; -	#ifdef PUGIXML_NO_EXCEPTIONS -		if (setjmp(sd.error_handler)) return false; -	#endif +		bool r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_boolean(c, sd.stack); -		return static_cast<impl::xpath_query_impl*>(_impl)->root->eval_boolean(c, sd.stack); +		if (sd.oom) +		{ +		#ifdef PUGIXML_NO_EXCEPTIONS +			return false; +		#else +			throw std::bad_alloc(); +		#endif +		} + +		return r;  	}  	PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const @@ -12435,11 +12487,18 @@ namespace pugi  		impl::xpath_context c(n, 1, 1);  		impl::xpath_stack_data sd; -	#ifdef PUGIXML_NO_EXCEPTIONS -		if (setjmp(sd.error_handler)) return impl::gen_nan(); -	#endif +		double r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_number(c, sd.stack); + +		if (sd.oom) +		{ +		#ifdef PUGIXML_NO_EXCEPTIONS +			return impl::gen_nan(); +		#else +			throw std::bad_alloc(); +		#endif +		} -		return static_cast<impl::xpath_query_impl*>(_impl)->root->eval_number(c, sd.stack); +		return r;  	}  #ifndef PUGIXML_NO_STL @@ -12481,12 +12540,17 @@ namespace pugi  		impl::xpath_context c(n, 1, 1);  		impl::xpath_stack_data sd; -	#ifdef PUGIXML_NO_EXCEPTIONS -		if (setjmp(sd.error_handler)) return xpath_node_set(); -	#endif -  		impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all); +		if (sd.oom) +		{ +		#ifdef PUGIXML_NO_EXCEPTIONS +			return xpath_node_set(); +		#else +			throw std::bad_alloc(); +		#endif +		} +  		return xpath_node_set(r.begin(), r.end(), r.type());  	} @@ -12498,12 +12562,17 @@ namespace pugi  		impl::xpath_context c(n, 1, 1);  		impl::xpath_stack_data sd; -	#ifdef PUGIXML_NO_EXCEPTIONS -		if (setjmp(sd.error_handler)) return xpath_node(); -	#endif -  		impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first); +		if (sd.oom) +		{ +		#ifdef PUGIXML_NO_EXCEPTIONS +			return xpath_node(); +		#else +			throw std::bad_alloc(); +		#endif +		} +  		return r.first();  	} | 
