adding parser support for code keyword for data location

This commit is contained in:
nishant-sachdeva 2022-08-30 19:38:12 +05:30
parent 095363eff7
commit 7b1b1a0a84
36 changed files with 213 additions and 37 deletions

View File

@ -45,19 +45,19 @@ Solidity language without a compiler change.
pragma solidity >=0.4.16 <0.9.0;
library GetCode {
function at(address addr) public view returns (bytes memory code) {
function at(address addr) public view returns (bytes memory codeObject) {
assembly {
// retrieve the size of the code, this needs assembly
// retrieve the size of the codeObject, this needs assembly
let size := extcodesize(addr)
// allocate output byte array - this could also be done without assembly
// by using code = new bytes(size)
code := mload(0x40)
// by using codeObject = new bytes(size)
codeObject := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
mstore(0x40, add(codeObject, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// store length in memory
mstore(code, size)
// actually retrieve the code, this needs assembly
extcodecopy(addr, add(code, 0x20), 0, size)
mstore(codeObject, size)
// actually retrieve the codeObject, this needs assembly
extcodecopy(addr, add(codeObject, 0x20), 0, size)
}
}
}

View File

@ -18,6 +18,7 @@ Bool: 'bool';
Break: 'break';
Bytes: 'bytes';
Calldata: 'calldata';
Code: 'code';
Catch: 'catch';
Constant: 'constant';
Constructor: 'constructor';

View File

@ -336,7 +336,7 @@ locals [boolean visibilitySet = false, boolean mutabilitySet = false]
* The declaration of a single variable.
*/
variableDeclaration: type=typeName location=dataLocation? name=identifier;
dataLocation: Memory | Storage | Calldata;
dataLocation: Memory | Storage | Calldata | Code;
/**
* Complex expression.
@ -348,7 +348,7 @@ dataLocation: Memory | Storage | Calldata;
expression:
expression LBrack index=expression? RBrack # IndexAccess
| expression LBrack start=expression? Colon end=expression? RBrack # IndexRangeAccess
| expression Period (identifier | Address) # MemberAccess
| expression Period (identifier | Address | Code) # MemberAccess
| expression LBrace (namedArgument (Comma namedArgument)*)? RBrace # FunctionCallOptions
| expression callArgumentList # FunctionCall
| Payable callArgumentList # PayableConversion

View File

@ -185,6 +185,7 @@ namespace solidity::langutil
K(Returns, "returns", 0) \
K(Storage, "storage", 0) \
K(CallData, "calldata", 0) \
K(Code, "code", 0) \
K(Struct, "struct", 0) \
K(Throw, "throw", 0) \
K(Try, "try", 0) \
@ -307,7 +308,7 @@ namespace TokenTraits
constexpr bool isShiftOp(Token op) { return (Token::SHL <= op) && (op <= Token::SHR); }
constexpr bool isVariableVisibilitySpecifier(Token op) { return op == Token::Public || op == Token::Private || op == Token::Internal; }
constexpr bool isVisibilitySpecifier(Token op) { return isVariableVisibilitySpecifier(op) || op == Token::External; }
constexpr bool isLocationSpecifier(Token op) { return op == Token::Memory || op == Token::Storage || op == Token::CallData; }
constexpr bool isLocationSpecifier(Token op) { return op == Token::Memory || op == Token::Storage || op == Token::CallData || op == Token::Code; }
constexpr bool isStateMutabilitySpecifier(Token op)
{
@ -322,7 +323,7 @@ namespace TokenTraits
{
return tok == Token::Function || tok == Token::Let || tok == Token::If || tok == Token::Switch || tok == Token::Case ||
tok == Token::Default || tok == Token::For || tok == Token::Break || tok == Token::Continue || tok == Token::Leave ||
tok == Token::TrueLiteral || tok == Token::FalseLiteral || tok == Token::HexStringLiteral || tok == Token::Hex;
tok == Token::TrueLiteral || tok == Token::FalseLiteral || tok == Token::HexStringLiteral || tok == Token::Hex || tok == Token::Code;
}
bool isYulKeyword(std::string const& _literal);

View File

@ -364,6 +364,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
case Location::Memory: return "\"memory\"";
case Location::Storage: return "\"storage\"";
case Location::CallData: return "\"calldata\"";
case Location::Code: return "\"code\"";
case Location::Unspecified: return "none";
}
return {};
@ -433,6 +434,9 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
case Location::CallData:
typeLoc = DataLocation::CallData;
break;
case Location::Code:
typeLoc = DataLocation::Code;
break;
case Location::Unspecified:
solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set.");
}

View File

@ -496,3 +496,11 @@ bool SyntaxChecker::visit(StructDefinition const& _struct)
return true;
}
bool SyntaxChecker::visit(VariableDeclaration const& _node)
{
if (_node.referenceLocation() == VariableDeclaration::Location::Code)
m_errorReporter.syntaxError(2397_error, _node.location(), "Usage of \"code\" as a data location is not yet supported.");
return true;
}

View File

@ -97,6 +97,8 @@ private:
bool visit(StructDefinition const& _struct) override;
bool visit(Literal const& _literal) override;
bool visit(VariableDeclaration const& _node) override;
langutil::ErrorReporter& m_errorReporter;
bool m_useYulOptimizer = false;

View File

@ -1007,7 +1007,7 @@ private:
class VariableDeclaration: public Declaration, public StructurallyDocumented
{
public:
enum Location { Unspecified, Storage, Memory, CallData };
enum Location { Unspecified, Storage, Memory, CallData, Code};
enum class Mutability { Mutable, Immutable, Constant };
static std::string mutabilityToString(Mutability _mutability)
{

View File

@ -1006,6 +1006,8 @@ string ASTJsonExporter::location(VariableDeclaration::Location _location)
return "memory";
case VariableDeclaration::Location::CallData:
return "calldata";
case VariableDeclaration::Location::Code:
return "code";
}
// To make the compiler happy
return {};

View File

@ -34,6 +34,7 @@ InaccessibleDynamicType const TypeProvider::m_inaccessibleDynamic{};
unique_ptr<ArrayType> TypeProvider::m_bytesStorage;
unique_ptr<ArrayType> TypeProvider::m_bytesMemory;
unique_ptr<ArrayType> TypeProvider::m_bytesCalldata;
unique_ptr<ArrayType> TypeProvider::m_bytesCode;
unique_ptr<ArrayType> TypeProvider::m_stringStorage;
unique_ptr<ArrayType> TypeProvider::m_stringMemory;
@ -181,6 +182,7 @@ void TypeProvider::reset()
clearCache(m_bytesStorage);
clearCache(m_bytesMemory);
clearCache(m_bytesCalldata);
clearCache(m_bytesCode);
clearCache(m_stringStorage);
clearCache(m_stringMemory);
clearCache(m_emptyTuple);
@ -281,6 +283,8 @@ Type const* TypeProvider::fromElementaryTypeName(string const& _name)
location = DataLocation::CallData;
else if (nameParts[1] == "memory")
location = DataLocation::Memory;
else if (nameParts[1] == "code")
location = DataLocation::Code;
else
solAssert(false, "Unknown data location: " + nameParts[1]);
}
@ -325,6 +329,15 @@ ArrayType const* TypeProvider::bytesCalldata()
return m_bytesCalldata.get();
}
ArrayType const* TypeProvider::bytesCode()
{
solUnimplemented("\"code\" as data location not yet implemented.");
// TODO :: Remove this Assert when implementing DataLocation::Code
if (!m_bytesCode)
m_bytesCode = make_unique<ArrayType>(DataLocation::Code, false);
return m_bytesCode.get();
}
ArrayType const* TypeProvider::stringStorage()
{
if (!m_stringStorage)

View File

@ -57,7 +57,7 @@ public:
static Type const* fromElementaryTypeName(ElementaryTypeNameToken const& _type, std::optional<StateMutability> _stateMutability = {});
/// Converts a given elementary type name with optional data location
/// suffix " storage", " calldata" or " memory" to a type pointer. If suffix not given, defaults to " storage".
/// suffix " storage", " calldata", "code" or " memory" to a type pointer. If suffix not given, defaults to " storage".
static Type const* fromElementaryTypeName(std::string const& _name);
/// @returns boolean type.
@ -69,6 +69,7 @@ public:
static ArrayType const* bytesStorage();
static ArrayType const* bytesMemory();
static ArrayType const* bytesCalldata();
static ArrayType const* bytesCode();
static ArrayType const* stringStorage();
static ArrayType const* stringMemory();
@ -217,6 +218,7 @@ private:
static std::unique_ptr<ArrayType> m_bytesStorage;
static std::unique_ptr<ArrayType> m_bytesMemory;
static std::unique_ptr<ArrayType> m_bytesCalldata;
static std::unique_ptr<ArrayType> m_bytesCode;
static std::unique_ptr<ArrayType> m_stringStorage;
static std::unique_ptr<ArrayType> m_stringMemory;

View File

@ -1505,6 +1505,9 @@ TypeResult ReferenceType::unaryOperatorResult(Token _operator) const
return TypeProvider::emptyTuple();
case DataLocation::Storage:
return isPointer() ? nullptr : TypeProvider::emptyTuple();
case DataLocation::Code:
solUnimplemented("\"code\" as data location is not yet implemented");
break;
}
return nullptr;
}
@ -1532,6 +1535,8 @@ string ReferenceType::stringForReferencePart() const
return "calldata";
case DataLocation::Memory:
return "memory";
case DataLocation::Code:
return "code";
}
solAssert(false, "");
return "";
@ -1551,6 +1556,9 @@ string ReferenceType::identifierLocationSuffix() const
case DataLocation::CallData:
id += "_calldata";
break;
case DataLocation::Code:
id += "_code";
break;
}
if (isPointer())
id += "_ptr";
@ -1709,6 +1717,9 @@ BoolResult ArrayType::validForLocation(DataLocation _loc) const
if (storageSizeUpperBound() >= bigint(1) << 256)
return BoolResult::err("Type too large for storage.");
break;
case DataLocation::Code:
solUnimplemented("\"code\" as a data location is not yet implemented");
break;
}
return true;
}
@ -1789,6 +1800,9 @@ vector<tuple<string, Type const*>> ArrayType::makeStackItems() const
case DataLocation::Storage:
// byte offset inside storage value is omitted
return {std::make_tuple("slot", TypeProvider::uint256())};
case DataLocation::Code:
solUnimplemented("\"code\" as a data location is not yet implemented");
break;
}
solAssert(false, "");
}
@ -2529,6 +2543,9 @@ vector<tuple<string, Type const*>> StructType::makeStackItems() const
return {std::make_tuple("mpos", TypeProvider::uint256())};
case DataLocation::Storage:
return {std::make_tuple("slot", TypeProvider::uint256())};
case DataLocation::Code:
solUnimplemented("\"code\" as a data location is not yet implemented");
break;
}
solAssert(false, "");
}

View File

@ -70,7 +70,7 @@ inline rational makeRational(bigint const& _numerator, bigint const& _denominato
return rational(_numerator, _denominator);
}
enum class DataLocation { Storage, CallData, Memory };
enum class DataLocation { Storage, CallData, Memory, Code };
/**

View File

@ -1022,6 +1022,9 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType, unsigned _stackDept
if (_arrayType.isByteArrayOrString())
m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1);
break;
case DataLocation::Code:
solUnimplemented("\"code\" as data location is not yet implemented");
break;
}
}
}
@ -1048,6 +1051,9 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b
// stack: <base_ref> <index>
switch (location)
{
case DataLocation::Code:
solUnimplemented("\"code\" as data location is not yet implemented");
break;
case DataLocation::Memory:
// stack: <base_ref> <index>
if (!_arrayType.isByteArrayOrString())

View File

@ -1111,6 +1111,9 @@ void CompilerUtils::convertType(
"Invalid conversion to calldata type."
);
break;
case DataLocation::Code:
solUnimplemented("\"code\" as data location is not yet implemented");
break;
}
break;
}
@ -1231,12 +1234,18 @@ void CompilerUtils::convertType(
case DataLocation::Memory:
// nothing to do
break;
case DataLocation::Code:
solUnimplemented("\"code\" as data location is not yet implemented");
break;
}
break;
case DataLocation::CallData:
solAssert(_typeOnStack == _targetType);
// nothing to do
break;
case DataLocation::Code:
solUnimplemented("\"code\" as data location is not yet implemented");
break;
}
break;
}

View File

@ -1978,6 +1978,9 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
case DataLocation::Memory:
m_context << Instruction::MLOAD;
break;
case DataLocation::Code:
solUnimplemented("\"code\" as data location is not yet implemented");
break;
}
}
else if (member == "push" || member == "pop")
@ -2124,6 +2127,9 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
case DataLocation::CallData:
ArrayUtils(m_context).accessCallDataArrayElement(arrayType);
break;
case DataLocation::Code:
solUnimplemented("\"code\" as data location is not yet implemented");
break;
}
break;
}

View File

@ -2467,6 +2467,9 @@ string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
templ("advance", toCompactHexWithPrefix(size));
break;
}
case DataLocation::Code:
solUnimplemented("\"code\" as data location is not yet implemented");
break;
}
return templ.render();
});

View File

@ -2282,6 +2282,9 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
define(_indexAccess) << indexAccessFunctionCall << "\n";
break;
}
case DataLocation::Code:
solUnimplemented("\"code\" as data location is not yet implemented");
break;
}
}
else if (baseType.category() == Type::Category::FixedBytes)

View File

@ -812,6 +812,9 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
case Token::CallData:
location = VariableDeclaration::Location::CallData;
break;
case Token::Code:
location = VariableDeclaration::Location::Code;
break;
default:
solAssert(false, "Unknown data location.");
}
@ -1025,12 +1028,12 @@ ASTPointer<Identifier> Parser::parseIdentifier()
return nodeFactory.createNode<Identifier>(expectIdentifierToken());
}
ASTPointer<Identifier> Parser::parseIdentifierOrAddress()
ASTPointer<Identifier> Parser::parseIdentifierOrAddressOrCode()
{
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition();
return nodeFactory.createNode<Identifier>(expectIdentifierTokenOrAddress());
return nodeFactory.createNode<Identifier>(expectIdentifierTokenOrAddressOrCode());
}
ASTPointer<UserDefinedTypeName> Parser::parseUserDefinedTypeName()
@ -1905,7 +1908,7 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression(
advance();
nodeFactory.markEndPosition();
SourceLocation memberLocation = currentLocation();
ASTPointer<ASTString> memberName = expectIdentifierTokenOrAddress();
ASTPointer<ASTString> memberName = expectIdentifierTokenOrAddressOrCode();
expression = nodeFactory.createNode<MemberAccess>(expression, std::move(memberName), std::move(memberLocation));
break;
}
@ -2252,7 +2255,7 @@ Parser::IndexAccessedPath Parser::parseIndexAccessedPath()
while (m_scanner->currentToken() == Token::Period)
{
advance();
iap.path.push_back(parseIdentifierOrAddress());
iap.path.push_back(parseIdentifierOrAddressOrCode());
}
}
else
@ -2382,7 +2385,7 @@ ASTPointer<ASTString> Parser::expectIdentifierToken()
return getLiteralAndAdvance();
}
ASTPointer<ASTString> Parser::expectIdentifierTokenOrAddress()
ASTPointer<ASTString> Parser::expectIdentifierTokenOrAddressOrCode()
{
ASTPointer<ASTString> result;
if (m_scanner->currentToken() == Token::Address)
@ -2390,6 +2393,11 @@ ASTPointer<ASTString> Parser::expectIdentifierTokenOrAddress()
result = make_shared<ASTString>("address");
advance();
}
else if (m_scanner->currentToken() == Token::Code)
{
result = make_shared<ASTString>("code");
advance();
}
else
{
expectToken(Token::Identifier, false /* do not advance */);

View File

@ -115,7 +115,7 @@ private:
ASTPointer<UsingForDirective> parseUsingDirective();
ASTPointer<ModifierInvocation> parseModifierInvocation();
ASTPointer<Identifier> parseIdentifier();
ASTPointer<Identifier> parseIdentifierOrAddress();
ASTPointer<Identifier> parseIdentifierOrAddressOrCode();
ASTPointer<UserDefinedTypeName> parseUserDefinedTypeName();
ASTPointer<IdentifierPath> parseIdentifierPath();
ASTPointer<TypeName> parseTypeNameSuffix(ASTPointer<TypeName> type, ASTNodeFactory& nodeFactory);
@ -214,7 +214,7 @@ private:
ASTPointer<Expression> expressionFromIndexAccessStructure(IndexAccessedPath const& _pathAndIndices);
ASTPointer<ASTString> expectIdentifierToken();
ASTPointer<ASTString> expectIdentifierTokenOrAddress();
ASTPointer<ASTString> expectIdentifierTokenOrAddressOrCode();
ASTPointer<ASTString> getLiteralAndAdvance();
///@}

View File

@ -109,10 +109,7 @@ shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject)
shared_ptr<Block> ObjectParser::parseCode(optional<SourceNameMap> _sourceNames)
{
if (currentToken() != Token::Identifier || currentLiteral() != "code")
fatalParserError(4846_error, "Expected keyword \"code\".");
advance();
expectToken(Token::Code);
return parseBlock(std::move(_sourceNames));
}

View File

@ -3,18 +3,18 @@ contract C {
require(b, "failure");
return x - y;
}
function onlyPanic(bool b, uint x, uint y) public returns (uint r, uint code) {
function onlyPanic(bool b, uint x, uint y) public returns (uint r, uint codeObject) {
try this.uf(b, x, y) returns (uint b) {
r = b;
} catch Panic(uint c) {
code = c;
codeObject = c;
}
}
function panicAndError(bool b, uint x, uint y) public returns (uint r, uint code, string memory msg_) {
function panicAndError(bool b, uint x, uint y) public returns (uint r, uint codeObject, string memory msg_) {
try this.uf(b, x, y) returns (uint b) {
r = b;
} catch Panic(uint c) {
code = c;
codeObject = c;
} catch Error(string memory _errmsg) {
msg_ = _errmsg;
}

View File

@ -1,6 +1,6 @@
// Test to see if type.code.length does extcodesize(type) only when type is an address.
struct S {
bytes32 code;
bytes32 codeObject;
bytes32 another;
}
@ -8,7 +8,7 @@ contract C {
S s;
function f() public returns (uint, uint, bool) {
return (s.code.length, s.another.length, address(this).code.length > 50);
return (s.codeObject.length, s.another.length, address(this).code.length > 50);
}
}
// ====

View File

@ -0,0 +1,18 @@
contract X {
function bar(string code input) external pure {
string code test = input;
}
function g(string code) external {}
function i(string code str) external {
this.g(str);
}
}
// ----
// SyntaxError 2397: (30-47): Usage of "code" as a data location is not yet supported.
// SyntaxError 2397: (73-89): Usage of "code" as a data location is not yet supported.
// SyntaxError 2397: (120-131): Usage of "code" as a data location is not yet supported.
// SyntaxError 2397: (160-175): Usage of "code" as a data location is not yet supported.
// TypeError 6651: (30-47): Data location must be "memory" or "calldata" for parameter in external function, but "code" was given.
// TypeError 6651: (73-89): Data location must be "storage", "memory" or "calldata" for variable, but "code" was given.
// TypeError 6651: (120-131): Data location must be "memory" or "calldata" for parameter in external function, but "code" was given.
// TypeError 6651: (160-175): Data location must be "memory" or "calldata" for parameter in external function, but "code" was given.

View File

@ -0,0 +1,17 @@
interface testInterface {
function C(function (string code) external) external;
function D(string code) external;
}
contract testContract {
function k(string code str) external pure {
abi.encodeCall(testInterface.D, (str));
}
}
// ----
// SyntaxError 2397: (51-62): Usage of "code" as a data location is not yet supported.
// SyntaxError 2397: (99-110): Usage of "code" as a data location is not yet supported.
// SyntaxError 2397: (164-179): Usage of "code" as a data location is not yet supported.
// TypeError 6651: (51-62): Data location must be "memory" or "calldata" for parameter in function, but "code" was given.
// TypeError 6651: (99-110): Data location must be "memory" or "calldata" for parameter in external function, but "code" was given.
// TypeError 6651: (164-179): Data location must be "memory" or "calldata" for parameter in external function, but "code" was given.

View File

@ -0,0 +1,18 @@
contract X {
function foo(string code input) internal pure {
string code test = input;
}
function g(string code) internal {}
function i(string code str) internal {
g(str);
}
}
// ----
// SyntaxError 2397: (30-47): Usage of "code" as a data location is not yet supported.
// SyntaxError 2397: (73-89): Usage of "code" as a data location is not yet supported.
// SyntaxError 2397: (120-131): Usage of "code" as a data location is not yet supported.
// SyntaxError 2397: (160-175): Usage of "code" as a data location is not yet supported.
// TypeError 6651: (30-47): Data location must be "storage", "memory" or "calldata" for parameter in function, but "code" was given.
// TypeError 6651: (73-89): Data location must be "storage", "memory" or "calldata" for variable, but "code" was given.
// TypeError 6651: (120-131): Data location must be "storage", "memory" or "calldata" for parameter in function, but "code" was given.
// TypeError 6651: (160-175): Data location must be "storage", "memory" or "calldata" for parameter in function, but "code" was given.

View File

@ -0,0 +1,8 @@
contract X {
function codeAsBytes() external {
bytes32 calldata code;
}
}
// ----
// ParserError 3548: (76-80): Location already specified.
// ParserError 2314: (80-81): Expected identifier but got ';'

View File

@ -0,0 +1,10 @@
contract X {
function g(string calldata str) external {}
function foo(string calldata code) external {
this.g(code);
}
}
// ----
// ParserError 3548: (95-99): Location already specified.
// ParserError 6933: (127-131): Expected primary expression.

View File

@ -0,0 +1,8 @@
contract X {
function codeAsString() external {
uint32 calldata code;
}
}
// ----
// ParserError 3548: (76-80): Location already specified.
// ParserError 2314: (80-81): Expected identifier but got ';'

View File

@ -0,0 +1,8 @@
contract X {
function codeAsInt() external {
string calldata code;
}
}
// ----
// ParserError 3548: (73-77): Location already specified.
// ParserError 2314: (77-78): Expected identifier but got ';'

View File

@ -0,0 +1,7 @@
contract X {
struct codeInStruct {
string code;
}
}
// ----
// ParserError 2314: (54-58): Expected identifier but got 'code'

View File

@ -3,4 +3,4 @@ object "A" {
code {}
}
// ----
// ParserError 4846: (15-19): Expected keyword "code".
// ParserError 2314: (15-19): Expected 'code' but got identifier

View File

@ -2,4 +2,4 @@ object "A" {
data "tmp" ""
}
// ----
// ParserError 4846: (15-19): Expected keyword "code".
// ParserError 2314: (15-19): Expected 'code' but got identifier

View File

@ -1,4 +1,4 @@
object "A" {
}
// ----
// ParserError 4846: (13-14): Expected keyword "code".
// ParserError 2314: (13-14): Expected 'code' but got '}'

View File

@ -2,4 +2,4 @@ object "" {
}
// ----
// ParserError 3287: (7-9): Object name cannot be empty.
// ParserError 4846: (12-13): Expected keyword "code".
// ParserError 2314: (12-13): Expected 'code' but got '}'

View File

@ -6,4 +6,4 @@ object "A" {
code {}
}
// ----
// ParserError 4846: (15-21): Expected keyword "code".
// ParserError 2314: (15-21): Expected 'code' but got identifier