/src/solidity/libyul/ObjectParser.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | * Parser for Yul code and data object container. |
20 | | */ |
21 | | |
22 | | #include <libyul/AST.h> |
23 | | #include <libyul/ObjectParser.h> |
24 | | |
25 | | #include <libyul/AsmParser.h> |
26 | | #include <libyul/Exceptions.h> |
27 | | |
28 | | #include <liblangutil/Token.h> |
29 | | #include <liblangutil/Scanner.h> |
30 | | |
31 | | #include <libsolutil/StringUtils.h> |
32 | | |
33 | | #include <regex> |
34 | | |
35 | | using namespace std; |
36 | | using namespace solidity; |
37 | | using namespace solidity::yul; |
38 | | using namespace solidity::util; |
39 | | using namespace solidity::langutil; |
40 | | |
41 | | shared_ptr<Object> ObjectParser::parse(shared_ptr<Scanner> const& _scanner, bool _reuseScanner) |
42 | 17.9k | { |
43 | 17.9k | m_recursionDepth = 0; |
44 | 17.9k | try |
45 | 17.9k | { |
46 | 17.9k | shared_ptr<Object> object; |
47 | 17.9k | m_scanner = _scanner; |
48 | | |
49 | 17.9k | if (currentToken() == Token::LBrace) |
50 | 16.7k | { |
51 | | // Special case: Code-only form. |
52 | 16.7k | object = make_shared<Object>(); |
53 | 16.7k | object->name = "object"_yulstring; |
54 | 16.7k | auto sourceNameMapping = tryParseSourceNameMapping(); |
55 | 16.7k | object->debugData = make_shared<ObjectDebugData>(ObjectDebugData{sourceNameMapping}); |
56 | 16.7k | object->code = parseBlock(sourceNameMapping); |
57 | 16.7k | if (!object->code) |
58 | 2.04k | return nullptr; |
59 | 16.7k | } |
60 | 1.16k | else |
61 | 1.16k | object = parseObject(); |
62 | 15.8k | if (!_reuseScanner) |
63 | 14.8k | expectToken(Token::EOS); |
64 | 15.8k | return object; |
65 | 17.9k | } |
66 | 17.9k | catch (FatalError const&) |
67 | 17.9k | { |
68 | 1.07k | if (m_errorReporter.errors().empty()) |
69 | 0 | throw; // Something is weird here, rather throw again. |
70 | 1.07k | } |
71 | 1.07k | return nullptr; |
72 | 17.9k | } |
73 | | |
74 | | shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject) |
75 | 2.13k | { |
76 | 2.13k | RecursionGuard guard(*this); |
77 | | |
78 | 2.13k | shared_ptr<Object> ret = make_shared<Object>(); |
79 | | |
80 | 2.13k | auto sourceNameMapping = tryParseSourceNameMapping(); |
81 | 2.13k | ret->debugData = make_shared<ObjectDebugData>(ObjectDebugData{sourceNameMapping}); |
82 | | |
83 | 2.13k | if (currentToken() != Token::Identifier || currentLiteral() != "object") |
84 | 849 | fatalParserError(4294_error, "Expected keyword \"object\"."); |
85 | 2.13k | advance(); |
86 | | |
87 | 2.13k | ret->name = parseUniqueName(_containingObject); |
88 | | |
89 | 2.13k | expectToken(Token::LBrace); |
90 | | |
91 | 2.13k | ret->code = parseCode(move(sourceNameMapping)); |
92 | | |
93 | 3.91k | while (currentToken() != Token::RBrace) |
94 | 1.77k | { |
95 | 1.77k | if (currentToken() == Token::Identifier && currentLiteral() == "object") |
96 | 973 | parseObject(ret.get()); |
97 | 805 | else if (currentToken() == Token::Identifier && currentLiteral() == "data") |
98 | 670 | parseData(*ret); |
99 | 135 | else |
100 | 135 | fatalParserError(8143_error, "Expected keyword \"data\" or \"object\" or \"}\"."); |
101 | 1.77k | } |
102 | 2.13k | if (_containingObject) |
103 | 497 | addNamedSubObject(*_containingObject, ret->name, ret); |
104 | | |
105 | 2.13k | expectToken(Token::RBrace); |
106 | | |
107 | 2.13k | return ret; |
108 | 2.13k | } |
109 | | |
110 | | shared_ptr<Block> ObjectParser::parseCode(optional<SourceNameMap> _sourceNames) |
111 | 1.25k | { |
112 | 1.25k | if (currentToken() != Token::Identifier || currentLiteral() != "code") |
113 | 13 | fatalParserError(4846_error, "Expected keyword \"code\"."); |
114 | 1.25k | advance(); |
115 | | |
116 | 1.25k | return parseBlock(move(_sourceNames)); |
117 | 1.25k | } |
118 | | |
119 | | optional<SourceNameMap> ObjectParser::tryParseSourceNameMapping() const |
120 | 18.8k | { |
121 | | // @use-src 0:"abc.sol", 1:"foo.sol", 2:"bar.sol" |
122 | | // |
123 | | // UseSrcList := UseSrc (',' UseSrc)* |
124 | | // UseSrc := [0-9]+ ':' FileName |
125 | | // FileName := "(([^\"]|\.)*)" |
126 | | |
127 | | // Matches some "@use-src TEXT". |
128 | 18.8k | static std::regex const lineRE = std::regex( |
129 | 18.8k | "(^|\\s)@use-src\\b", |
130 | 18.8k | std::regex_constants::ECMAScript | std::regex_constants::optimize |
131 | 18.8k | ); |
132 | 18.8k | std::smatch sm; |
133 | 18.8k | if (!std::regex_search(m_scanner->currentCommentLiteral(), sm, lineRE)) |
134 | 18.8k | return nullopt; |
135 | | |
136 | 0 | solAssert(sm.size() == 2, ""); |
137 | 0 | auto text = m_scanner->currentCommentLiteral().substr(static_cast<size_t>(sm.position() + sm.length())); |
138 | 0 | CharStream charStream(text, ""); |
139 | 0 | Scanner scanner(charStream); |
140 | 0 | if (scanner.currentToken() == Token::EOS) |
141 | 0 | return SourceNameMap{}; |
142 | 0 | SourceNameMap sourceNames; |
143 | |
|
144 | 0 | while (scanner.currentToken() != Token::EOS) |
145 | 0 | { |
146 | 0 | if (scanner.currentToken() != Token::Number) |
147 | 0 | break; |
148 | 0 | auto sourceIndex = toUnsignedInt(scanner.currentLiteral()); |
149 | 0 | if (!sourceIndex) |
150 | 0 | break; |
151 | 0 | if (scanner.next() != Token::Colon) |
152 | 0 | break; |
153 | 0 | if (scanner.next() != Token::StringLiteral) |
154 | 0 | break; |
155 | 0 | sourceNames[*sourceIndex] = make_shared<string const>(scanner.currentLiteral()); |
156 | |
|
157 | 0 | Token const next = scanner.next(); |
158 | 0 | if (next == Token::EOS) |
159 | 0 | return {move(sourceNames)}; |
160 | 0 | if (next != Token::Comma) |
161 | 0 | break; |
162 | 0 | scanner.next(); |
163 | 0 | } |
164 | | |
165 | 0 | m_errorReporter.syntaxError( |
166 | 0 | 9804_error, |
167 | 0 | m_scanner->currentCommentLocation(), |
168 | 0 | "Error parsing arguments to @use-src. Expected: <number> \":\" \"<filename>\", ..." |
169 | 0 | ); |
170 | 0 | return nullopt; |
171 | 0 | } |
172 | | |
173 | | shared_ptr<Block> ObjectParser::parseBlock(optional<SourceNameMap> _sourceNames) |
174 | 17.9k | { |
175 | 17.9k | Parser parser(m_errorReporter, m_dialect, move(_sourceNames)); |
176 | 17.9k | shared_ptr<Block> block = parser.parseInline(m_scanner); |
177 | 17.9k | yulAssert(block || m_errorReporter.hasErrors(), "Invalid block but no error!"); |
178 | 17.9k | return block; |
179 | 17.9k | } |
180 | | |
181 | | void ObjectParser::parseData(Object& _containingObject) |
182 | 670 | { |
183 | 670 | yulAssert( |
184 | 670 | currentToken() == Token::Identifier && currentLiteral() == "data", |
185 | 670 | "parseData called on wrong input." |
186 | 670 | ); |
187 | 670 | advance(); |
188 | | |
189 | 670 | YulString name = parseUniqueName(&_containingObject); |
190 | | |
191 | 670 | if (currentToken() == Token::HexStringLiteral) |
192 | 22 | expectToken(Token::HexStringLiteral, false); |
193 | 648 | else |
194 | 648 | expectToken(Token::StringLiteral, false); |
195 | 670 | addNamedSubObject(_containingObject, name, make_shared<Data>(name, asBytes(currentLiteral()))); |
196 | 670 | advance(); |
197 | 670 | } |
198 | | |
199 | | YulString ObjectParser::parseUniqueName(Object const* _containingObject) |
200 | 1.95k | { |
201 | 1.95k | expectToken(Token::StringLiteral, false); |
202 | 1.95k | YulString name{currentLiteral()}; |
203 | 1.95k | if (name.empty()) |
204 | 852 | parserError(3287_error, "Object name cannot be empty."); |
205 | 1.10k | else if (_containingObject && _containingObject->name == name) |
206 | 103 | parserError(8311_error, "Object name cannot be the same as the name of the containing object."); |
207 | 1.00k | else if (_containingObject && _containingObject->subIndexByName.count(name)) |
208 | 95 | parserError(8794_error, "Object name \"" + name.str() + "\" already exists inside the containing object."); |
209 | 1.95k | advance(); |
210 | 1.95k | return name; |
211 | 1.95k | } |
212 | | |
213 | | void ObjectParser::addNamedSubObject(Object& _container, YulString _name, shared_ptr<ObjectNode> _subObject) |
214 | 1.15k | { |
215 | 1.15k | _container.subIndexByName[_name] = _container.subObjects.size(); |
216 | 1.15k | _container.subObjects.emplace_back(std::move(_subObject)); |
217 | 1.15k | } |