Skip to content

Commit f712d53

Browse files
committed
yul literal value is struct of u256 + optional formatting hint or 'unlimited'
- unlimited meaning an argument of a builtin function with corresponding entry in
1 parent 53278ea commit f712d53

Some content is hidden

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

49 files changed

+401
-191
lines changed

libsolidity/codegen/ir/IRGeneratorForStatements.cpp

Lines changed: 2 additions & 1 deletion
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>
@@ -202,7 +203,7 @@ struct CopyTranslate: public yul::ASTCopier
202203
solAssert(false);
203204

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

libsolutil/CommonData.cpp

Lines changed: 2 additions & 2 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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(0),
27+
m_stringValue(std::make_shared<std::string>(std::move(_builtinStringLiteralValue))),
28+
m_builtinStringLiteralValue(true) {}
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+
m_builtinStringLiteralValue(false) {
34+
}
35+
36+
LiteralValue::Data const& LiteralValue::value() const
37+
{
38+
yulAssert(!unlimited() && m_numericValue.has_value());
39+
return *m_numericValue;
40+
}
41+
42+
LiteralValue::BuiltinStringLiteralData const& LiteralValue::builtinStringLiteralValue() const
43+
{
44+
yulAssert(unlimited() && m_stringValue != nullptr);
45+
return *m_stringValue;
46+
}
47+
48+
bool LiteralValue::unlimited() const
49+
{
50+
return m_builtinStringLiteralValue;
51+
}
52+
53+
LiteralValue::RepresentationHint const& LiteralValue::hint() const
54+
{
55+
yulAssert(!unlimited());
56+
return m_stringValue;
57+
}
58+
59+
bool LiteralValue::operator==(LiteralValue const& _rhs) const
60+
{
61+
if (!unlimited() && !_rhs.unlimited())
62+
return value() == _rhs.value();
63+
else
64+
return m_numericValue == _rhs.m_numericValue;
65+
}
66+
67+
}

libyul/AST.h

Lines changed: 26 additions & 1 deletion
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,30 @@ 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 set to `unlimited`
48+
class LiteralValue {
49+
public:
50+
using Data = u256;
51+
using BuiltinStringLiteralData = std::string;
52+
using RepresentationHint = std::shared_ptr<std::string>;
53+
54+
LiteralValue() = default;
55+
explicit LiteralValue(std::string _builtinStringLiteralValue);
56+
explicit LiteralValue(Data const& _data, std::optional<std::string> const& _hint = std::nullopt);
57+
58+
bool operator==(LiteralValue const& _rhs) const;
59+
Data const& value() const;
60+
BuiltinStringLiteralData const& builtinStringLiteralValue() const;
61+
bool unlimited() const;
62+
RepresentationHint const& hint() const;
63+
64+
private:
65+
std::optional<Data> m_numericValue;
66+
std::shared_ptr<std::string> m_stringValue;
67+
bool m_builtinStringLiteralValue;
68+
};
69+
struct Literal { langutil::DebugData::ConstPtr debugData; LiteralKind kind; LiteralValue value; Type type; };
4570
/// External / internal identifier or label reference
4671
struct Identifier { langutil::DebugData::ConstPtr debugData; YulString name; };
4772
/// Assignment ("x := mload(20:u256)", expects push-1-expression on the right hand

libyul/ASTForward.h

Lines changed: 1 addition & 0 deletions
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

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -101,22 +101,22 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect,
101101
std::vector<YulString> AsmAnalyzer::operator()(Literal const& _literal)
102102
{
103103
expectValidType(_literal.type, nativeLocationOf(_literal));
104-
if (_literal.kind == LiteralKind::String && _literal.value.str().size() > 32)
104+
if (_literal.kind == LiteralKind::String && !validStringLiteral(_literal))
105105
m_errorReporter.typeError(
106106
3069_error,
107107
nativeLocationOf(_literal),
108-
"String literal too long (" + std::to_string(_literal.value.str().size()) + " > 32)"
108+
"String literal too long (" + std::to_string(formatLiteral(_literal).size()) + " > 32)"
109109
);
110-
else if (_literal.kind == LiteralKind::Number && bigint(_literal.value.str()) > u256(-1))
110+
else if (_literal.kind == LiteralKind::Number && !validNumberLiteral(_literal))
111111
m_errorReporter.typeError(6708_error, nativeLocationOf(_literal), "Number literal too large (> 256 bits)");
112112
else if (_literal.kind == LiteralKind::Boolean)
113-
yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "");
113+
yulAssert(validBoolLiteral(_literal));
114114

115115
if (!m_dialect.validTypeForLiteral(_literal.kind, _literal.value, _literal.type))
116116
m_errorReporter.typeError(
117117
5170_error,
118118
nativeLocationOf(_literal),
119-
"Invalid type \"" + _literal.type.str() + "\" for literal \"" + _literal.value.str() + "\"."
119+
"Invalid type \"" + _literal.type.str() + "\" for literal \"" + formatLiteral(_literal) + "\"."
120120
);
121121

122122
return {_literal.type};
@@ -417,24 +417,31 @@ std::vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
417417
std::string functionName = _funCall.functionName.name.str();
418418
if (functionName == "datasize" || functionName == "dataoffset")
419419
{
420-
if (!m_dataNames.count(std::get<Literal>(arg).value))
420+
auto const& argumentAsLiteral = std::get<Literal>(arg);
421+
if (!m_dataNames.count(YulString(formatLiteral(argumentAsLiteral))))
421422
m_errorReporter.typeError(
422423
3517_error,
423424
nativeLocationOf(arg),
424-
"Unknown data object \"" + std::get<Literal>(arg).value.str() + "\"."
425+
"Unknown data object \"" + formatLiteral(argumentAsLiteral) + "\"."
425426
);
426427
}
427428
else if (functionName.substr(0, "verbatim_"s.size()) == "verbatim_")
428429
{
429-
if (std::get<Literal>(arg).value.empty())
430+
static u256 const empty {valueOfStringLiteral("").value()};
431+
auto const& literalValue = std::get<Literal>(arg).value;
432+
if ((literalValue.unlimited() && literalValue.builtinStringLiteralValue().empty()) || (!literalValue.unlimited() && literalValue.value() == empty))
430433
m_errorReporter.typeError(
431434
1844_error,
432435
nativeLocationOf(arg),
433436
"The \"verbatim_*\" builtins cannot be used with empty bytecode."
434437
);
435438
}
436-
437-
argTypes.emplace_back(expectUnlimitedStringLiteral(std::get<Literal>(arg)));
439+
bool const isUnlimitedLiteralArgument = [&]() {
440+
if (BuiltinFunction const* f = m_dialect.builtin(_funCall.functionName.name))
441+
return f->literalArguments.size() >= i && f->literalArguments.at(i-1).has_value();
442+
return false;
443+
}();
444+
argTypes.emplace_back(expectStringLiteral(std::get<Literal>(arg), isUnlimitedLiteralArgument));
438445
continue;
439446
}
440447
}
@@ -492,12 +499,12 @@ void AsmAnalyzer::operator()(Switch const& _switch)
492499
(*this)(*_case.value);
493500

494501
/// Note: the parser ensures there is only one default case
495-
if (watcher.ok() && !cases.insert(valueOfLiteral(*_case.value)).second)
502+
if (watcher.ok() && !cases.insert(_case.value->value.value()).second)
496503
m_errorReporter.declarationError(
497504
6792_error,
498505
nativeLocationOf(_case),
499506
"Duplicate case \"" +
500-
valueOfLiteral(*_case.value).str() +
507+
formatLiteral(*_case.value) +
501508
"\" defined."
502509
);
503510
}
@@ -555,10 +562,11 @@ YulString AsmAnalyzer::expectExpression(Expression const& _expr)
555562
return types.empty() ? m_dialect.defaultType : types.front();
556563
}
557564

558-
YulString AsmAnalyzer::expectUnlimitedStringLiteral(Literal const& _literal)
565+
YulString AsmAnalyzer::expectStringLiteral(Literal const& _literal, bool const _expectUnlimitedLiteralArgument)
559566
{
560567
yulAssert(_literal.kind == LiteralKind::String, "");
561568
yulAssert(m_dialect.validTypeForLiteral(LiteralKind::String, _literal.value, _literal.type), "");
569+
yulAssert(_literal.value.unlimited() == _expectUnlimitedLiteralArgument);
562570

563571
return {_literal.type};
564572
}

libyul/AsmAnalysis.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ class AsmAnalyzer
100100
/// Visits the expression, expects that it evaluates to exactly one value and
101101
/// returns the type. Reports errors on errors and returns the default type.
102102
YulString expectExpression(Expression const& _expr);
103-
YulString expectUnlimitedStringLiteral(Literal const& _literal);
103+
YulString expectStringLiteral(Literal const& _literal, bool _expectUnlimitedLiteralArgument);
104104
/// Visits the expression and expects it to return a single boolean value.
105105
/// Reports an error otherwise.
106106
void expectBoolExpression(Expression const& _expr);

libyul/AsmJsonConverter.cpp

Lines changed: 6 additions & 8 deletions
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

@@ -51,23 +52,20 @@ Json AsmJsonConverter::operator()(Literal const& _node) const
5152
switch (_node.kind)
5253
{
5354
case LiteralKind::Number:
54-
yulAssert(
55-
util::isValidDecimal(_node.value.str()) || util::isValidHex(_node.value.str()),
56-
"Invalid number literal"
57-
);
55+
yulAssert(validNumberLiteral(_node), "Invalid number literal");
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+
if (auto const value = formatLiteral(_node); util::validateUTF8(value))
68+
ret["value"] = value;
7169
return ret;
7270
}
7371

libyul/AsmJsonImporter.cpp

Lines changed: 13 additions & 10 deletions
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,16 @@ 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);
209+
207210
return lit;
208211
}
209212

0 commit comments

Comments
 (0)