1 /*
2 	This file is part of solidity.
3 
4 	solidity is free software: you can redistribute it and/or modify
5 	it under the terms of the GNU General Public License as published by
6 	the Free Software Foundation, either version 3 of the License, or
7 	(at your option) any later version.
8 
9 	solidity is distributed in the hope that it will be useful,
10 	but WITHOUT ANY WARRANTY; without even the implied warranty of
11 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 	GNU General Public License for more details.
13 
14 	You should have received a copy of the GNU General Public License
15 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16 */
17 // SPDX-License-Identifier: GPL-3.0
18 /**
19  * @author Christian <chris@ethereum.org>
20  * @date 2016
21  * Utilities to handle semantic versioning.
22  */
23 
24 #include <liblangutil/SemVerHandler.h>
25 
26 #include <liblangutil/Exceptions.h>
27 
28 #include <functional>
29 #include <limits>
30 
31 using namespace std;
32 using namespace solidity;
33 using namespace solidity::langutil;
34 
SemVerMatchExpressionParser(vector<Token> _tokens,vector<string> _literals)35 SemVerMatchExpressionParser::SemVerMatchExpressionParser(vector<Token> _tokens, vector<string> _literals):
36 	m_tokens(std::move(_tokens)), m_literals(std::move(_literals))
37 {
38 	solAssert(m_tokens.size() == m_literals.size(), "");
39 }
40 
SemVerVersion(string const & _versionString)41 SemVerVersion::SemVerVersion(string const& _versionString)
42 {
43 	auto i = _versionString.begin();
44 	auto end = _versionString.end();
45 
46 	for (unsigned level = 0; level < 3; ++level)
47 	{
48 		unsigned v = 0;
49 		for (; i != end && '0' <= *i && *i <= '9'; ++i)
50 			v = v * 10 + unsigned(*i - '0');
51 		numbers[level] = v;
52 		if (level < 2)
53 		{
54 			if (i == end || *i != '.')
55 				BOOST_THROW_EXCEPTION(SemVerError());
56 			else
57 				++i;
58 		}
59 	}
60 	if (i != end && *i == '-')
61 	{
62 		auto prereleaseStart = ++i;
63 		while (i != end && *i != '+') ++i;
64 		prerelease = string(prereleaseStart, i);
65 	}
66 	if (i != end && *i == '+')
67 	{
68 		auto buildStart = ++i;
69 		while (i != end) ++i;
70 		build = string(buildStart, i);
71 	}
72 	if (i != end)
73 		BOOST_THROW_EXCEPTION(SemVerError());
74 }
75 
matches(SemVerVersion const & _version) const76 bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _version) const
77 {
78 	if (prefix == Token::BitNot)
79 	{
80 		MatchComponent comp = *this;
81 
82 		comp.prefix = Token::GreaterThanOrEqual;
83 		if (!comp.matches(_version))
84 			return false;
85 
86 		if (levelsPresent >= 2)
87 			comp.levelsPresent = 2;
88 		else
89 			comp.levelsPresent = 1;
90 		comp.prefix = Token::LessThanOrEqual;
91 		return comp.matches(_version);
92 	}
93 	else if (prefix == Token::BitXor)
94 	{
95 		MatchComponent comp = *this;
96 
97 		comp.prefix = Token::GreaterThanOrEqual;
98 		if (!comp.matches(_version))
99 			return false;
100 
101 		if (comp.version.numbers[0] == 0 && comp.levelsPresent != 1)
102 			comp.levelsPresent = 2;
103 		else
104 			comp.levelsPresent = 1;
105 		comp.prefix = Token::LessThanOrEqual;
106 		return comp.matches(_version);
107 	}
108 	else
109 	{
110 		int cmp = 0;
111 		bool didCompare = false;
112 		for (unsigned i = 0; i < levelsPresent && cmp == 0; i++)
113 			if (version.numbers[i] != std::numeric_limits<unsigned>::max())
114 			{
115 				didCompare = true;
116 				cmp = static_cast<int>(_version.numbers[i] - version.numbers[i]);
117 			}
118 
119 		if (cmp == 0 && !_version.prerelease.empty() && didCompare)
120 			cmp = -1;
121 
122 		switch (prefix)
123 		{
124 		case Token::Assign:
125 			return cmp == 0;
126 		case Token::LessThan:
127 			return cmp < 0;
128 		case Token::LessThanOrEqual:
129 			return cmp <= 0;
130 		case Token::GreaterThan:
131 			return cmp > 0;
132 		case Token::GreaterThanOrEqual:
133 			return cmp >= 0;
134 		default:
135 			solAssert(false, "Invalid SemVer expression");
136 		}
137 		return false;
138 	}
139 }
140 
matches(SemVerVersion const & _version) const141 bool SemVerMatchExpression::Conjunction::matches(SemVerVersion const& _version) const
142 {
143 	for (auto const& component: components)
144 		if (!component.matches(_version))
145 			return false;
146 	return true;
147 }
148 
matches(SemVerVersion const & _version) const149 bool SemVerMatchExpression::matches(SemVerVersion const& _version) const
150 {
151 	if (!isValid())
152 		return false;
153 	for (auto const& range: m_disjunction)
154 		if (range.matches(_version))
155 			return true;
156 	return false;
157 }
158 
parse()159 optional<SemVerMatchExpression> SemVerMatchExpressionParser::parse()
160 {
161 	reset();
162 
163 	if (m_tokens.empty())
164 		return nullopt;
165 
166 	try
167 	{
168 		while (true)
169 		{
170 			parseMatchExpression();
171 			if (m_pos >= m_tokens.size())
172 				break;
173 			if (currentToken() != Token::Or)
174 				BOOST_THROW_EXCEPTION(SemVerError());
175 			nextToken();
176 		}
177 	}
178 	catch (SemVerError const&)
179 	{
180 		reset();
181 		return nullopt;
182 	}
183 
184 	return m_expression;
185 }
186 
187 
reset()188 void SemVerMatchExpressionParser::reset()
189 {
190 	m_expression = SemVerMatchExpression();
191 	m_pos = 0;
192 	m_posInside = 0;
193 }
194 
parseMatchExpression()195 void SemVerMatchExpressionParser::parseMatchExpression()
196 {
197 	// component - component (range)
198 	// or component component* (conjunction)
199 
200 	SemVerMatchExpression::Conjunction range;
201 	range.components.push_back(parseMatchComponent());
202 	if (currentToken() == Token::Sub)
203 	{
204 		range.components[0].prefix = Token::GreaterThanOrEqual;
205 		nextToken();
206 		range.components.push_back(parseMatchComponent());
207 		range.components[1].prefix = Token::LessThanOrEqual;
208 	}
209 	else
210 		while (currentToken() != Token::Or && currentToken() != Token::Illegal)
211 			range.components.push_back(parseMatchComponent());
212 	m_expression.m_disjunction.push_back(range);
213 }
214 
parseMatchComponent()215 SemVerMatchExpression::MatchComponent SemVerMatchExpressionParser::parseMatchComponent()
216 {
217 	SemVerMatchExpression::MatchComponent component;
218 	Token token = currentToken();
219 
220 	switch (token)
221 	{
222 	case Token::BitXor:
223 	case Token::BitNot:
224 	case Token::LessThan:
225 	case Token::LessThanOrEqual:
226 	case Token::GreaterThan:
227 	case Token::GreaterThanOrEqual:
228 	case Token::Assign:
229 		component.prefix = token;
230 		nextToken();
231 		break;
232 	default:
233 		component.prefix = Token::Assign;
234 	}
235 
236 	component.levelsPresent = 0;
237 	while (component.levelsPresent < 3)
238 	{
239 		component.version.numbers[component.levelsPresent] = parseVersionPart();
240 		component.levelsPresent++;
241 		if (currentChar() == '.')
242 			nextChar();
243 		else
244 			break;
245 	}
246 	// TODO we do not support pre and build version qualifiers for now in match expressions
247 	// (but we do support them in the actual versions)
248 	return component;
249 }
250 
parseVersionPart()251 unsigned SemVerMatchExpressionParser::parseVersionPart()
252 {
253 	auto startPos = m_pos;
254 	char c = currentChar();
255 	nextChar();
256 	if (c == 'x' || c == 'X' || c == '*')
257 		return unsigned(-1);
258 	else if (c == '0')
259 		return 0;
260 	else if ('1' <= c && c <= '9')
261 	{
262 		auto v = static_cast<unsigned>(c - '0');
263 		// If we skip to the next token, the current number is terminated.
264 		while (m_pos == startPos && '0' <= currentChar() && currentChar() <= '9')
265 		{
266 			c = currentChar();
267 			if (v * 10 < v || v * 10 + static_cast<unsigned>(c - '0') < v * 10)
268 				BOOST_THROW_EXCEPTION(SemVerError());
269 			v = v * 10 + static_cast<unsigned>(c - '0');
270 			nextChar();
271 		}
272 		return v;
273 	}
274 	else
275 		BOOST_THROW_EXCEPTION(SemVerError());
276 }
277 
currentChar() const278 char SemVerMatchExpressionParser::currentChar() const
279 {
280 	if (m_pos >= m_literals.size())
281 		return char(-1);
282 	if (m_posInside >= m_literals[m_pos].size())
283 		return char(-1);
284 	return m_literals[m_pos][m_posInside];
285 }
286 
nextChar()287 char SemVerMatchExpressionParser::nextChar()
288 {
289 	if (m_pos < m_literals.size())
290 	{
291 		if (m_posInside + 1 >= m_literals[m_pos].size())
292 			nextToken();
293 		else
294 			++m_posInside;
295 	}
296 	return currentChar();
297 }
298 
currentToken() const299 Token SemVerMatchExpressionParser::currentToken() const
300 {
301 	if (m_pos < m_tokens.size())
302 		return m_tokens[m_pos];
303 	else
304 		return Token::Illegal;
305 }
306 
nextToken()307 void SemVerMatchExpressionParser::nextToken()
308 {
309 	++m_pos;
310 	m_posInside = 0;
311 }
312