Coverage Report

Created: 2022-08-24 06:40

/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
}