Browse Source

Merge pull request #1274 from LianaHus/sol_ConstantFields

constant variables
cl-refactor
chriseth 10 years ago
parent
commit
42f5ae4c8b
  1. 15
      libsolidity/AST.cpp
  2. 31
      libsolidity/AST.h
  3. 5
      libsolidity/Compiler.cpp
  4. 9
      libsolidity/ExpressionCompiler.cpp
  5. 10
      libsolidity/Parser.cpp
  6. 3
      libsolidity/Parser.h
  7. 2
      libsolidity/Types.h
  8. 23
      test/SolidityEndToEndTest.cpp
  9. 3229
      test/SolidityEndToEndTest.cpp.orig
  10. 42
      test/SolidityNameAndTypeResolution.cpp
  11. 9
      test/SolidityParser.cpp

15
libsolidity/AST.cpp

@ -189,7 +189,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getIn
for (ContractDefinition const* contract: getLinearizedBaseContracts()) for (ContractDefinition const* contract: getLinearizedBaseContracts())
{ {
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions()) for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && functionsSeen.count(f->getName()) == 0) if (functionsSeen.count(f->getName()) == 0 && f->isPartOfExternalInterface())
{ {
functionsSeen.insert(f->getName()); functionsSeen.insert(f->getName());
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
@ -197,7 +197,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getIn
} }
for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables()) for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables())
if (v->isPublic() && functionsSeen.count(v->getName()) == 0) if (functionsSeen.count(v->getName()) == 0 && v->isPartOfExternalInterface())
{ {
FunctionType ftype(*v); FunctionType ftype(*v);
functionsSeen.insert(v->getName()); functionsSeen.insert(v->getName());
@ -322,8 +322,8 @@ string FunctionDefinition::getCanonicalSignature() const
bool VariableDeclaration::isLValue() const bool VariableDeclaration::isLValue() const
{ {
// External function parameters are Read-Only // External function parameters and constant declared variables are Read-Only
return !isExternalFunctionParameter(); return !isExternalFunctionParameter() && !m_isConstant;
} }
void VariableDeclaration::checkTypeRequirements() void VariableDeclaration::checkTypeRequirements()
@ -332,6 +332,13 @@ void VariableDeclaration::checkTypeRequirements()
// sets the type. // sets the type.
// Note that assignments before the first declaration are legal because of the special scoping // Note that assignments before the first declaration are legal because of the special scoping
// rules inherited from JavaScript. // rules inherited from JavaScript.
if (m_isConstant)
{
if (!dynamic_cast<ContractDefinition const*>(getScope()))
BOOST_THROW_EXCEPTION(createTypeError("Illegal use of \"constant\" specifier."));
if ((m_type && !m_type->isValueType()) || !m_value)
BOOST_THROW_EXCEPTION(createTypeError("Unitialized \"constant\" variable."));
}
if (!m_value) if (!m_value)
return; return;
if (m_type) if (m_type)

31
libsolidity/AST.h

@ -156,6 +156,7 @@ public:
/// contract types. /// contract types.
virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0; virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0;
virtual bool isLValue() const { return false; } virtual bool isLValue() const { return false; }
virtual bool isPartOfExternalInterface() const { return false; };
protected: protected:
virtual Visibility getDefaultVisibility() const { return Visibility::Public; } virtual Visibility getDefaultVisibility() const { return Visibility::Public; }
@ -415,6 +416,7 @@ public:
getVisibility() >= Visibility::Internal; getVisibility() >= Visibility::Internal;
} }
virtual TypePointer getType(ContractDefinition const*) const override; virtual TypePointer getType(ContractDefinition const*) const override;
virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstructor && !getName().empty(); }
/// Checks that all parameters have allowed types and calls checkTypeRequirements on the body. /// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
void checkTypeRequirements(); void checkTypeRequirements();
@ -440,13 +442,23 @@ private:
class VariableDeclaration: public Declaration class VariableDeclaration: public Declaration
{ {
public: public:
VariableDeclaration(SourceLocation const& _location, ASTPointer<TypeName> const& _type, VariableDeclaration(
ASTPointer<ASTString> const& _name, ASTPointer<Expression> _value, SourceLocation const& _location,
Visibility _visibility, ASTPointer<TypeName> const& _type,
bool _isStateVar = false, bool _isIndexed = false): ASTPointer<ASTString> const& _name,
Declaration(_location, _name, _visibility), ASTPointer<Expression> _value,
m_typeName(_type), m_value(_value), Visibility _visibility,
m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {} bool _isStateVar = false,
bool _isIndexed = false,
bool _isConstant = false
):
Declaration(_location, _name, _visibility),
m_typeName(_type),
m_value(_value),
m_isStateVariable(_isStateVar),
m_isIndexed(_isIndexed),
m_isConstant(_isConstant){}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
@ -459,21 +471,24 @@ public:
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; } void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
virtual bool isLValue() const override; virtual bool isLValue() const override;
virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstant; }
void checkTypeRequirements(); void checkTypeRequirements();
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); } bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
bool isExternalFunctionParameter() const; bool isExternalFunctionParameter() const;
bool isStateVariable() const { return m_isStateVariable; } bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; } bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_isConstant; }
protected: protected:
Visibility getDefaultVisibility() const override { return Visibility::Internal; } Visibility getDefaultVisibility() const override { return Visibility::Internal; }
private: private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var") ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
ASTPointer<Expression> m_value; ///< the assigned value, can be missing ASTPointer<Expression> m_value; ///< the assigned value, can be missing
bool m_isStateVariable; ///< Whether or not this is a contract state variable bool m_isStateVariable; ///< Whether or not this is a contract state variable
bool m_isIndexed; ///< Whether this is an indexed variable (used by events). bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
bool m_isConstant; ///< Whether the variable is a compile-time constant.
std::shared_ptr<Type const> m_type; ///< derived type, initially empty std::shared_ptr<Type const> m_type; ///< derived type, initially empty
}; };

5
libsolidity/Compiler.cpp

@ -276,13 +276,14 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract)
{ {
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts())) for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts()))
for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables()) for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables())
m_context.addStateVariable(*variable); if (!variable->isConstant())
m_context.addStateVariable(*variable);
} }
void Compiler::initializeStateVariables(ContractDefinition const& _contract) void Compiler::initializeStateVariables(ContractDefinition const& _contract)
{ {
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables()) for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
if (variable->getValue()) if (variable->getValue() && !variable->isConstant())
ExpressionCompiler(m_context, m_optimize).appendStateVariableInitialization(*variable); ExpressionCompiler(m_context, m_optimize).appendStateVariableInitialization(*variable);
} }

9
libsolidity/ExpressionCompiler.cpp

@ -855,8 +855,13 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
} }
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration)) else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag(); m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag();
else if (dynamic_cast<VariableDeclaration const*>(declaration)) else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
setLValueFromDeclaration(*declaration, _identifier); {
if (!variable->isConstant())
setLValueFromDeclaration(*declaration, _identifier);
else
variable->getValue()->accept(*this);
}
else if (dynamic_cast<ContractDefinition const*>(declaration)) else if (dynamic_cast<ContractDefinition const*>(declaration))
{ {
// no-op // no-op

10
libsolidity/Parser.cpp

@ -317,6 +317,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
nodeFactory.setEndPositionFromNode(type); nodeFactory.setEndPositionFromNode(type);
} }
bool isIndexed = false; bool isIndexed = false;
bool isDeclaredConst = false;
ASTPointer<ASTString> identifier; ASTPointer<ASTString> identifier;
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
Declaration::Visibility visibility(Declaration::Visibility::Default); Declaration::Visibility visibility(Declaration::Visibility::Default);
@ -327,7 +328,13 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
isIndexed = true; isIndexed = true;
m_scanner->next(); m_scanner->next();
} }
if (token == Token::Const)
{
isDeclaredConst = true;
m_scanner->next();
}
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
if (_options.allowEmptyName && m_scanner->getCurrentToken() != Token::Identifier) if (_options.allowEmptyName && m_scanner->getCurrentToken() != Token::Identifier)
{ {
identifier = make_shared<ASTString>(""); identifier = make_shared<ASTString>("");
@ -348,7 +355,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
} }
return nodeFactory.createNode<VariableDeclaration>(type, identifier, value, return nodeFactory.createNode<VariableDeclaration>(type, identifier, value,
visibility, _options.isStateVariable, visibility, _options.isStateVariable,
isIndexed); isIndexed, isDeclaredConst);
} }
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition() ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
@ -913,6 +920,7 @@ Parser::LookAheadInfo Parser::peekStatementType() const
// In all other cases, we have an expression statement. // In all other cases, we have an expression statement.
Token::Value token(m_scanner->getCurrentToken()); Token::Value token(m_scanner->getCurrentToken());
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier); bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
if (token == Token::Mapping || token == Token::Var || if (token == Token::Mapping || token == Token::Var ||
(mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier)) (mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier))
return LookAheadInfo::VariableDeclarationStatement; return LookAheadInfo::VariableDeclarationStatement;

3
libsolidity/Parser.h

@ -66,8 +66,7 @@ private:
ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<EnumDefinition> parseEnumDefinition(); ASTPointer<EnumDefinition> parseEnumDefinition();
ASTPointer<EnumValue> parseEnumValue(); ASTPointer<EnumValue> parseEnumValue();
ASTPointer<VariableDeclaration> parseVariableDeclaration( ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions(),
VarDeclParserOptions const& _options = VarDeclParserOptions(),
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()); ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>());
ASTPointer<ModifierDefinition> parseModifierDefinition(); ASTPointer<ModifierDefinition> parseModifierDefinition();
ASTPointer<EventDefinition> parseEventDefinition(); ASTPointer<EventDefinition> parseEventDefinition();

2
libsolidity/Types.h

@ -516,7 +516,7 @@ private:
std::vector<std::string> m_parameterNames; std::vector<std::string> m_parameterNames;
std::vector<std::string> m_returnParameterNames; std::vector<std::string> m_returnParameterNames;
Location const m_location; Location const m_location;
/// true iff the function takes an arbitrary number of arguments of arbitrary types /// true if the function takes an arbitrary number of arguments of arbitrary types
bool const m_arbitraryParameters = false; bool const m_arbitraryParameters = false;
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack bool const m_valueSet = false; ///< true iff the value to be sent is on the stack

23
test/SolidityEndToEndTest.cpp

@ -3196,6 +3196,29 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base_with_gap)
BOOST_CHECK(callContractFunction("m_i()") == encodeArgs(4)); BOOST_CHECK(callContractFunction("m_i()") == encodeArgs(4));
} }
BOOST_AUTO_TEST_CASE(simple_constant_variables_test)
{
char const* sourceCode = R"(
contract Foo {
function getX() returns (uint r) { return x; }
uint constant x = 56;
})";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("getX()") == encodeArgs(56));
}
BOOST_AUTO_TEST_CASE(constant_variables)
{
//for now constant specifier is valid only for uint bytesXX and enums
char const* sourceCode = R"(
contract Foo {
uint constant x = 56;
enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
ActionChoices constant choices = ActionChoices.GoLeft;
bytes32 constant st = "abc\x00\xff__";
})";
compileAndRun(sourceCode);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

3229
test/SolidityEndToEndTest.cpp.orig

File diff suppressed because it is too large

42
test/SolidityNameAndTypeResolution.cpp

@ -1404,6 +1404,48 @@ BOOST_AUTO_TEST_CASE(test_byte_is_alias_of_byte1)
ETH_TEST_REQUIRE_NO_THROW(parseTextAndResolveNames(text), "Type resolving failed"); ETH_TEST_REQUIRE_NO_THROW(parseTextAndResolveNames(text), "Type resolving failed");
} }
BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable)
{
char const* text = R"(
contract Foo {
function changeIt() { x = 9; }
uint constant x = 56;
})";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(complex_const_variable)
{
//for now constant specifier is valid only for uint bytesXX and enums
char const* text = R"(
contract Foo {
mapping(uint => bool) constant mapVar;
})";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(uninitialized_const_variable)
{
char const* text = R"(
contract Foo {
uint constant y;
})";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(local_const_variable)
{
char const* text = R"(
contract Foo {
function localConst() returns (uint ret)
{
uint constant local = 4;
return local;
}
})";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), ParserError);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

9
test/SolidityParser.cpp

@ -822,6 +822,15 @@ BOOST_AUTO_TEST_CASE(multi_arrays)
ETH_TEST_CHECK_NO_THROW(parseText(text), "Parsing failed"); ETH_TEST_CHECK_NO_THROW(parseText(text), "Parsing failed");
} }
BOOST_AUTO_TEST_CASE(constant_is_keyword)
{
char const* text = R"(
contract Foo {
uint constant = 4;
})";
BOOST_CHECK_THROW(parseText(text), ParserError);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

Loading…
Cancel
Save