/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 | 5.52k | { |
43 | 5.52k | m_recursionDepth = 0; |
44 | 5.52k | try |
45 | 5.52k | { |
46 | 5.52k | shared_ptr<Object> object; |
47 | 5.52k | m_scanner = _scanner; |
48 | | |
49 | 5.52k | if (currentToken() == Token::LBrace) |
50 | 5.52k | { |
51 | | // Special case: Code-only form. |
52 | 5.52k | object = make_shared<Object>(); |
53 | 5.52k | object->name = "object"_yulstring; |
54 | 5.52k | auto sourceNameMapping = tryParseSourceNameMapping(); |
55 | 5.52k | object->debugData = make_shared<ObjectDebugData>(ObjectDebugData{sourceNameMapping}); |
56 | 5.52k | object->code = parseBlock(sourceNameMapping); |
57 | 5.52k | if (!object->code) |
58 | 0 | return nullptr; |
59 | 5.52k | } |
60 | 0 | else |
61 | 0 | object = parseObject(); |
62 | 5.52k | if (!_reuseScanner) |
63 | 5.52k | expectToken(Token::EOS); |
64 | 5.52k | return object; |
65 | 5.52k | } |
66 | 5.52k | catch (FatalError const&) |
67 | 5.52k | { |
68 | 0 | if (m_errorReporter.errors().empty()) |
69 | 0 | throw; // Something is weird here, rather throw again. |
70 | 0 | } |
71 | 0 | return nullptr; |
72 | 5.52k | } |
73 | | |
74 | | shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject) |
75 | 0 | { |
76 | 0 | RecursionGuard guard(*this); |
77 | |
|
78 | 0 | shared_ptr<Object> ret = make_shared<Object>(); |
79 | |
|
80 | 0 | auto sourceNameMapping = tryParseSourceNameMapping(); |
81 | 0 | ret->debugData = make_shared<ObjectDebugData>(ObjectDebugData{sourceNameMapping}); |
82 | |
|
83 | 0 | if (currentToken() != Token::Identifier || currentLiteral() != "object") |
84 | 0 | fatalParserError(4294_error, "Expected keyword \"object\"."); |
85 | 0 | advance(); |
86 | |
|
87 | 0 | ret->name = parseUniqueName(_containingObject); |
88 | |
|
89 | 0 | expectToken(Token::LBrace); |
90 | |
|
91 | 0 | ret->code = parseCode(move(sourceNameMapping)); |
92 | |
|
93 | 0 | while (currentToken() != Token::RBrace) |
94 | 0 | { |
95 | 0 | if (currentToken() == Token::Identifier && currentLiteral() == "object") |
96 | 0 | parseObject(ret.get()); |
97 | 0 | else if (currentToken() == Token::Identifier && currentLiteral() == "data") |
98 | 0 | parseData(*ret); |
99 | 0 | else |
100 | 0 | fatalParserError(8143_error, "Expected keyword \"data\" or \"object\" or \"}\"."); |
101 | 0 | } |
102 | 0 | if (_containingObject) |
103 | 0 | addNamedSubObject(*_containingObject, ret->name, ret); |
104 | |
|
105 | 0 | expectToken(Token::RBrace); |
106 | |
|
107 | 0 | return ret; |
108 | 0 | } |
109 | | |
110 | | shared_ptr<Block> ObjectParser::parseCode(optional<SourceNameMap> _sourceNames) |
111 | 0 | { |
112 | 0 | if (currentToken() != Token::Identifier || currentLiteral() != "code") |
113 | 0 | fatalParserError(4846_error, "Expected keyword \"code\"."); |
114 | 0 | advance(); |
115 | |
|
116 | 0 | return parseBlock(move(_sourceNames)); |
117 | 0 | } |
118 | | |
119 | | optional<SourceNameMap> ObjectParser::tryParseSourceNameMapping() const |
120 | 5.52k | { |
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 | 5.52k | static std::regex const lineRE = std::regex( |
129 | 5.52k | "(^|\\s)@use-src\\b", |
130 | 5.52k | std::regex_constants::ECMAScript | std::regex_constants::optimize |
131 | 5.52k | ); |
132 | 5.52k | std::smatch sm; |
133 | 5.52k | if (!std::regex_search(m_scanner->currentCommentLiteral(), sm, lineRE)) |
134 | 5.52k | 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 | 5.52k | { |
175 | 5.52k | Parser parser(m_errorReporter, m_dialect, move(_sourceNames)); |
176 | 5.52k | shared_ptr<Block> block = parser.parseInline(m_scanner); |
177 | 5.52k | yulAssert(block || m_errorReporter.hasErrors(), "Invalid block but no error!"); |
178 | 5.52k | return block; |
179 | 5.52k | } |
180 | | |
181 | | void ObjectParser::parseData(Object& _containingObject) |
182 | 0 | { |
183 | 0 | yulAssert( |
184 | 0 | currentToken() == Token::Identifier && currentLiteral() == "data", |
185 | 0 | "parseData called on wrong input." |
186 | 0 | ); |
187 | 0 | advance(); |
188 | |
|
189 | 0 | YulString name = parseUniqueName(&_containingObject); |
190 | |
|
191 | 0 | if (currentToken() == Token::HexStringLiteral) |
192 | 0 | expectToken(Token::HexStringLiteral, false); |
193 | 0 | else |
194 | 0 | expectToken(Token::StringLiteral, false); |
195 | 0 | addNamedSubObject(_containingObject, name, make_shared<Data>(name, asBytes(currentLiteral()))); |
196 | 0 | advance(); |
197 | 0 | } |
198 | | |
199 | | YulString ObjectParser::parseUniqueName(Object const* _containingObject) |
200 | 0 | { |
201 | 0 | expectToken(Token::StringLiteral, false); |
202 | 0 | YulString name{currentLiteral()}; |
203 | 0 | if (name.empty()) |
204 | 0 | parserError(3287_error, "Object name cannot be empty."); |
205 | 0 | else if (_containingObject && _containingObject->name == name) |
206 | 0 | parserError(8311_error, "Object name cannot be the same as the name of the containing object."); |
207 | 0 | else if (_containingObject && _containingObject->subIndexByName.count(name)) |
208 | 0 | parserError(8794_error, "Object name \"" + name.str() + "\" already exists inside the containing object."); |
209 | 0 | advance(); |
210 | 0 | return name; |
211 | 0 | } |
212 | | |
213 | | void ObjectParser::addNamedSubObject(Object& _container, YulString _name, shared_ptr<ObjectNode> _subObject) |
214 | 0 | { |
215 | 0 | _container.subIndexByName[_name] = _container.subObjects.size(); |
216 | 0 | _container.subObjects.emplace_back(std::move(_subObject)); |
217 | 0 | } |