Skip to content

Commit 477c413

Browse files
committed
yul literal value is struct of u256 + optional formatting hint or 'unlimited'
- unlimited is a string literal for which bytes restrictions don't hold; can occur in builtin function arguments
1 parent 31d87b0 commit 477c413

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+535
-205
lines changed

libsolidity/codegen/ir/IRGeneratorForStatements.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <libyul/AsmPrinter.h>
3838
#include <libyul/AST.h>
3939
#include <libyul/Dialect.h>
40+
#include <libyul/Utilities.h>
4041
#include <libyul/optimiser/ASTCopier.h>
4142

4243
#include <liblangutil/Exceptions.h>
@@ -203,7 +204,7 @@ struct CopyTranslate: public yul::ASTCopier
203204
solAssert(false);
204205

205206
if (isDigit(value.front()))
206-
return yul::Literal{_identifier.debugData, yul::LiteralKind::Number, yul::YulString{value}, {}};
207+
return yul::Literal{_identifier.debugData, yul::LiteralKind::Number, yul::valueOfNumberLiteral(value), {}};
207208
else
208209
return yul::Identifier{_identifier.debugData, yul::YulString{value}};
209210
}

libsolutil/CommonData.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ std::string solidity::util::getChecksummedAddress(std::string const& _addr)
161161
return ret;
162162
}
163163

164-
bool solidity::util::isValidHex(std::string const& _string)
164+
bool solidity::util::isValidHex(std::string_view const _string)
165165
{
166166
if (_string.substr(0, 2) != "0x")
167167
return false;
@@ -170,7 +170,7 @@ bool solidity::util::isValidHex(std::string const& _string)
170170
return true;
171171
}
172172

173-
bool solidity::util::isValidDecimal(std::string const& _string)
173+
bool solidity::util::isValidDecimal(std::string_view const _string)
174174
{
175175
if (_string.empty())
176176
return false;

libsolutil/CommonData.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -584,8 +584,8 @@ bool passesAddressChecksum(std::string const& _str, bool _strict);
584584
/// @param hex strings that look like an address
585585
std::string getChecksummedAddress(std::string const& _addr);
586586

587-
bool isValidHex(std::string const& _string);
588-
bool isValidDecimal(std::string const& _string);
587+
bool isValidHex(std::string_view _string);
588+
bool isValidDecimal(std::string_view _string);
589589

590590
/// @returns a quoted string if all characters are printable ASCII chars,
591591
/// or its hex representation otherwise.

libyul/AST.cpp

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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+
#include <libyul/AST.h>
20+
#include <libyul/Exceptions.h>
21+
22+
namespace solidity::yul
23+
{
24+
25+
LiteralValue::LiteralValue(std::string _builtinStringLiteralValue):
26+
m_numericValue(std::nullopt),
27+
m_stringValue(std::make_shared<std::string>(std::move(_builtinStringLiteralValue)))
28+
{ }
29+
30+
LiteralValue::LiteralValue(solidity::yul::LiteralValue::Data const& _data, std::optional<std::string> const& _hint):
31+
m_numericValue(_data),
32+
m_stringValue(_hint ? std::make_shared<std::string>(*_hint) : nullptr)
33+
{ }
34+
35+
LiteralValue::Data const& LiteralValue::value() const
36+
{
37+
yulAssert(!unlimited());
38+
return *m_numericValue;
39+
}
40+
41+
LiteralValue::BuiltinStringLiteralData const& LiteralValue::builtinStringLiteralValue() const
42+
{
43+
yulAssert(unlimited());
44+
return *m_stringValue;
45+
}
46+
47+
bool LiteralValue::unlimited() const
48+
{
49+
return !m_numericValue.has_value();
50+
}
51+
52+
LiteralValue::RepresentationHint const& LiteralValue::hint() const
53+
{
54+
yulAssert(!unlimited());
55+
return m_stringValue;
56+
}
57+
58+
bool LiteralValue::operator==(LiteralValue const& _rhs) const
59+
{
60+
if (unlimited() != _rhs.unlimited())
61+
return false;
62+
63+
if (unlimited())
64+
return builtinStringLiteralValue() == _rhs.builtinStringLiteralValue();
65+
66+
return value() == _rhs.value();
67+
}
68+
69+
bool LiteralValue::operator<(solidity::yul::LiteralValue const& _rhs) const
70+
{
71+
if (unlimited() != _rhs.unlimited())
72+
return unlimited() < _rhs.unlimited();
73+
74+
if (unlimited())
75+
return builtinStringLiteralValue() < _rhs.builtinStringLiteralValue();
76+
77+
return value() < _rhs.value();
78+
}
79+
80+
}

libyul/AST.h

+28-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828

2929
#include <liblangutil/DebugData.h>
3030

31+
#include <libsolutil/Numeric.h>
32+
3133
#include <memory>
3234
#include <optional>
3335

@@ -41,7 +43,32 @@ using TypedNameList = std::vector<TypedName>;
4143

4244
/// Literal number or string (up to 32 bytes)
4345
enum class LiteralKind { Number, Boolean, String };
44-
struct Literal { langutil::DebugData::ConstPtr debugData; LiteralKind kind; YulString value; Type type; };
46+
/// Literal value that holds a u256 word of data, can be of LiteralKind type and - in case of arguments to
47+
/// builtins - exceed the u256 word (32 bytes), in which case the value is stored as string. The former is constructed
48+
/// via u256 word and optional hint and leads to unlimited == false, the latter is
49+
/// constructed via the string constructor and leads to unlimited == true.
50+
class LiteralValue {
51+
public:
52+
using Data = u256;
53+
using BuiltinStringLiteralData = std::string;
54+
using RepresentationHint = std::shared_ptr<std::string>;
55+
56+
LiteralValue() = default;
57+
explicit LiteralValue(std::string _builtinStringLiteralValue);
58+
explicit LiteralValue(Data const& _data, std::optional<std::string> const& _hint = std::nullopt);
59+
60+
bool operator==(LiteralValue const& _rhs) const;
61+
bool operator<(LiteralValue const& _rhs) const;
62+
Data const& value() const;
63+
BuiltinStringLiteralData const& builtinStringLiteralValue() const;
64+
bool unlimited() const;
65+
RepresentationHint const& hint() const;
66+
67+
private:
68+
std::optional<Data> m_numericValue;
69+
std::shared_ptr<std::string> m_stringValue;
70+
};
71+
struct Literal { langutil::DebugData::ConstPtr debugData; LiteralKind kind; LiteralValue value; Type type; };
4572
/// External / internal identifier or label reference
4673
struct Identifier { langutil::DebugData::ConstPtr debugData; YulString name; };
4774
/// Assignment ("x := mload(20:u256)", expects push-1-expression on the right hand

libyul/ASTForward.h

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ namespace solidity::yul
2929
{
3030

3131
enum class LiteralKind;
32+
class LiteralValue;
3233
struct Literal;
3334
struct Label;
3435
struct Identifier;

libyul/AsmAnalysis.cpp

+25-15
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141

4242
#include <fmt/format.h>
4343

44-
#include <memory>
4544
#include <functional>
4645

4746
using namespace std::string_literals;
@@ -104,24 +103,32 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect,
104103
std::vector<YulString> AsmAnalyzer::operator()(Literal const& _literal)
105104
{
106105
expectValidType(_literal.type, nativeLocationOf(_literal));
107-
if (_literal.kind == LiteralKind::String && _literal.value.str().size() > 32)
106+
bool erroneousLiteralValue = false;
107+
if (_literal.kind == LiteralKind::String && !_literal.value.unlimited() && _literal.value.hint() && _literal.value.hint()->size() > 32)
108+
{
109+
erroneousLiteralValue = true;
108110
m_errorReporter.typeError(
109111
3069_error,
110112
nativeLocationOf(_literal),
111-
"String literal too long (" + std::to_string(_literal.value.str().size()) + " > 32)"
113+
"String literal too long (" + std::to_string(formatLiteral(_literal, false /* _validated */ ).size()) + " > 32)"
112114
);
113-
else if (_literal.kind == LiteralKind::Number && bigint(_literal.value.str()) > u256(-1))
115+
}
116+
else if (_literal.kind == LiteralKind::Number && _literal.value.hint() && bigint(*_literal.value.hint()) > u256(-1))
117+
{
118+
erroneousLiteralValue = true;
114119
m_errorReporter.typeError(6708_error, nativeLocationOf(_literal), "Number literal too large (> 256 bits)");
115-
else if (_literal.kind == LiteralKind::Boolean)
116-
yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "");
120+
}
117121

118122
if (!m_dialect.validTypeForLiteral(_literal.kind, _literal.value, _literal.type))
123+
{
119124
m_errorReporter.typeError(
120125
5170_error,
121126
nativeLocationOf(_literal),
122-
"Invalid type \"" + _literal.type.str() + "\" for literal \"" + _literal.value.str() + "\"."
127+
"Invalid type \"" + _literal.type.str() + "\" for literal \"" + formatLiteral(_literal) + "\"."
123128
);
129+
}
124130

131+
yulAssert(erroneousLiteralValue ^ validLiteral(_literal), "Invalid literal after validating it through AsmAnalyzer.");
125132
return {_literal.type};
126133
}
127134

@@ -420,23 +427,25 @@ std::vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
420427
std::string functionName = _funCall.functionName.name.str();
421428
if (functionName == "datasize" || functionName == "dataoffset")
422429
{
423-
if (!m_dataNames.count(std::get<Literal>(arg).value))
430+
auto const& argumentAsLiteral = std::get<Literal>(arg);
431+
if (!m_dataNames.count(YulString(formatLiteral(argumentAsLiteral))))
424432
m_errorReporter.typeError(
425433
3517_error,
426434
nativeLocationOf(arg),
427-
"Unknown data object \"" + std::get<Literal>(arg).value.str() + "\"."
435+
"Unknown data object \"" + formatLiteral(argumentAsLiteral) + "\"."
428436
);
429437
}
430438
else if (functionName.substr(0, "verbatim_"s.size()) == "verbatim_")
431439
{
432-
if (std::get<Literal>(arg).value.empty())
440+
auto const& literalValue = std::get<Literal>(arg).value;
441+
yulAssert(literalValue.unlimited()); // verbatim literals are always unlimited
442+
if (literalValue.builtinStringLiteralValue().empty())
433443
m_errorReporter.typeError(
434444
1844_error,
435445
nativeLocationOf(arg),
436446
"The \"verbatim_*\" builtins cannot be used with empty bytecode."
437447
);
438448
}
439-
440449
argTypes.emplace_back(expectUnlimitedStringLiteral(std::get<Literal>(arg)));
441450
continue;
442451
}
@@ -495,12 +504,12 @@ void AsmAnalyzer::operator()(Switch const& _switch)
495504
(*this)(*_case.value);
496505

497506
/// Note: the parser ensures there is only one default case
498-
if (watcher.ok() && !cases.insert(valueOfLiteral(*_case.value)).second)
507+
if (watcher.ok() && !cases.insert(_case.value->value.value()).second)
499508
m_errorReporter.declarationError(
500509
6792_error,
501510
nativeLocationOf(_case),
502511
"Duplicate case \"" +
503-
valueOfLiteral(*_case.value).str() +
512+
formatLiteral(*_case.value) +
504513
"\" defined."
505514
);
506515
}
@@ -560,8 +569,9 @@ YulString AsmAnalyzer::expectExpression(Expression const& _expr)
560569

561570
YulString AsmAnalyzer::expectUnlimitedStringLiteral(Literal const& _literal)
562571
{
563-
yulAssert(_literal.kind == LiteralKind::String, "");
564-
yulAssert(m_dialect.validTypeForLiteral(LiteralKind::String, _literal.value, _literal.type), "");
572+
yulAssert(_literal.kind == LiteralKind::String);
573+
yulAssert(m_dialect.validTypeForLiteral(LiteralKind::String, _literal.value, _literal.type));
574+
yulAssert(_literal.value.unlimited());
565575

566576
return {_literal.type};
567577
}

libyul/AsmJsonConverter.cpp

+9-8
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@
2020
* Converts inline assembly AST to JSON format
2121
*/
2222

23-
#include <libyul/AsmJsonConverter.h>
2423
#include <libyul/AST.h>
24+
#include <libyul/AsmJsonConverter.h>
2525
#include <libyul/Exceptions.h>
26+
#include <libyul/Utilities.h>
2627
#include <libsolutil/CommonData.h>
2728
#include <libsolutil/UTF8.h>
2829

@@ -47,27 +48,27 @@ Json AsmJsonConverter::operator()(TypedName const& _node) const
4748

4849
Json AsmJsonConverter::operator()(Literal const& _node) const
4950
{
51+
yulAssert(validLiteral(_node));
5052
Json ret = createAstNode(originLocationOf(_node), nativeLocationOf(_node), "YulLiteral");
5153
switch (_node.kind)
5254
{
5355
case LiteralKind::Number:
54-
yulAssert(
55-
util::isValidDecimal(_node.value.str()) || util::isValidHex(_node.value.str()),
56-
"Invalid number literal"
57-
);
5856
ret["kind"] = "number";
5957
break;
6058
case LiteralKind::Boolean:
6159
ret["kind"] = "bool";
6260
break;
6361
case LiteralKind::String:
6462
ret["kind"] = "string";
65-
ret["hexValue"] = util::toHex(util::asBytes(_node.value.str()));
63+
ret["hexValue"] = util::toHex(util::asBytes(formatLiteral(_node)));
6664
break;
6765
}
6866
ret["type"] = _node.type.str();
69-
if (util::validateUTF8(_node.value.str()))
70-
ret["value"] = _node.value.str();
67+
{
68+
auto const formattedLiteral = formatLiteral(_node);
69+
if (util::validateUTF8(formattedLiteral))
70+
ret["value"] = formattedLiteral;
71+
}
7172
return ret;
7273
}
7374

libyul/AsmJsonImporter.cpp

+14-10
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <libyul/AsmJsonImporter.h>
2626
#include <libyul/AST.h>
2727
#include <libyul/Exceptions.h>
28+
#include <libyul/Utilities.h>
2829

2930
#include <liblangutil/Exceptions.h>
3031
#include <liblangutil/Scanner.h>
@@ -165,26 +166,25 @@ Literal AsmJsonImporter::createLiteral(Json const& _node)
165166
std::string kind = member(_node, "kind").get<std::string>();
166167

167168
solAssert(member(_node, "hexValue").is_string() || member(_node, "value").is_string(), "");
169+
std::string value;
168170
if (_node.contains("hexValue"))
169-
lit.value = YulString{util::asString(util::fromHex(member(_node, "hexValue").get<std::string>()))};
171+
value = util::asString(util::fromHex(member(_node, "hexValue").get<std::string>()));
170172
else
171-
lit.value = YulString{member(_node, "value").get<std::string>()};
172-
173-
lit.type= YulString{member(_node, "type").get<std::string>()};
174-
173+
value = member(_node, "value").get<std::string>();
174+
lit.type = YulString{member(_node, "type").get<std::string>()};
175175
if (kind == "number")
176176
{
177-
langutil::CharStream charStream(lit.value.str(), "");
177+
langutil::CharStream charStream(value, "");
178178
langutil::Scanner scanner{charStream};
179179
lit.kind = LiteralKind::Number;
180180
yulAssert(
181181
scanner.currentToken() == Token::Number,
182-
"Expected number but got " + langutil::TokenTraits::friendlyName(scanner.currentToken()) + std::string(" while scanning ") + lit.value.str()
182+
"Expected number but got " + langutil::TokenTraits::friendlyName(scanner.currentToken()) + std::string(" while scanning ") + value
183183
);
184184
}
185185
else if (kind == "bool")
186186
{
187-
langutil::CharStream charStream(lit.value.str(), "");
187+
langutil::CharStream charStream(value, "");
188188
langutil::Scanner scanner{charStream};
189189
lit.kind = LiteralKind::Boolean;
190190
yulAssert(
@@ -197,13 +197,17 @@ Literal AsmJsonImporter::createLiteral(Json const& _node)
197197
{
198198
lit.kind = LiteralKind::String;
199199
yulAssert(
200-
lit.value.str().size() <= 32,
201-
"String literal too long (" + std::to_string(lit.value.str().size()) + " > 32)"
200+
value.size() <= 32,
201+
"String literal too long (" + std::to_string(value.size()) + " > 32)"
202202
);
203203
}
204204
else
205205
yulAssert(false, "unknown type of literal");
206206

207+
// import only for inline assembly, no unlimited string literals there
208+
lit.value = valueOfLiteral(value, lit.kind, false /* _unlimitedLiteralArgument */);
209+
210+
yulAssert(validLiteral(lit));
207211
return lit;
208212
}
209213

0 commit comments

Comments
 (0)