aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AST.cpp15
-rw-r--r--AST.h31
-rw-r--r--ArrayUtils.cpp1
-rw-r--r--Compiler.cpp7
-rw-r--r--CompilerUtils.cpp4
-rw-r--r--ExpressionCompiler.cpp15
-rw-r--r--LValue.cpp1
-rw-r--r--Parser.cpp10
-rw-r--r--Parser.h3
-rw-r--r--Types.h2
10 files changed, 67 insertions, 22 deletions
diff --git a/AST.cpp b/AST.cpp
index 46c44995..d05eaf83 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -189,7 +189,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getIn
for (ContractDefinition const* contract: getLinearizedBaseContracts())
{
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());
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())
- if (v->isPublic() && functionsSeen.count(v->getName()) == 0)
+ if (functionsSeen.count(v->getName()) == 0 && v->isPartOfExternalInterface())
{
FunctionType ftype(*v);
functionsSeen.insert(v->getName());
@@ -322,8 +322,8 @@ string FunctionDefinition::getCanonicalSignature() const
bool VariableDeclaration::isLValue() const
{
- // External function parameters are Read-Only
- return !isExternalFunctionParameter();
+ // External function parameters and constant declared variables are Read-Only
+ return !isExternalFunctionParameter() && !m_isConstant;
}
void VariableDeclaration::checkTypeRequirements()
@@ -332,6 +332,13 @@ void VariableDeclaration::checkTypeRequirements()
// sets the type.
// Note that assignments before the first declaration are legal because of the special scoping
// 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)
return;
if (m_type)
diff --git a/AST.h b/AST.h
index eab53153..f5f6a3f3 100644
--- a/AST.h
+++ b/AST.h
@@ -156,6 +156,7 @@ public:
/// contract types.
virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0;
virtual bool isLValue() const { return false; }
+ virtual bool isPartOfExternalInterface() const { return false; };
protected:
virtual Visibility getDefaultVisibility() const { return Visibility::Public; }
@@ -415,6 +416,7 @@ public:
getVisibility() >= Visibility::Internal;
}
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.
void checkTypeRequirements();
@@ -440,13 +442,23 @@ private:
class VariableDeclaration: public Declaration
{
public:
- VariableDeclaration(SourceLocation const& _location, ASTPointer<TypeName> const& _type,
- ASTPointer<ASTString> const& _name, ASTPointer<Expression> _value,
- Visibility _visibility,
- bool _isStateVar = false, bool _isIndexed = false):
- Declaration(_location, _name, _visibility),
- m_typeName(_type), m_value(_value),
- m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {}
+ VariableDeclaration(
+ SourceLocation const& _location,
+ ASTPointer<TypeName> const& _type,
+ ASTPointer<ASTString> const& _name,
+ ASTPointer<Expression> _value,
+ Visibility _visibility,
+ 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(ASTConstVisitor& _visitor) const override;
@@ -459,21 +471,24 @@ public:
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
virtual bool isLValue() const override;
+ virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstant; }
void checkTypeRequirements();
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
bool isExternalFunctionParameter() const;
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
+ bool isConstant() const { return m_isConstant; }
protected:
Visibility getDefaultVisibility() const override { return Visibility::Internal; }
private:
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_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
};
diff --git a/ArrayUtils.cpp b/ArrayUtils.cpp
index f0d7d6a8..a064b2f5 100644
--- a/ArrayUtils.cpp
+++ b/ArrayUtils.cpp
@@ -125,6 +125,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, true, true, false);
else
solAssert(false, "Copying of unknown type requested: " + sourceBaseType->toString());
+ solAssert(2 + sourceBaseType->getSizeOnStack() <= 16, "Stack too deep.");
m_context << eth::dupInstruction(2 + sourceBaseType->getSizeOnStack());
StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true);
}
diff --git a/Compiler.cpp b/Compiler.cpp
index dc6e2c5a..0f4e89de 100644
--- a/Compiler.cpp
+++ b/Compiler.cpp
@@ -228,6 +228,7 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
{
// Retrieve data start offset by adding length to start offset of previous dynamic type
unsigned stackDepth = m_context.getStackHeight() - stackHeightOfPreviousDynamicArgument;
+ solAssert(stackDepth <= 16, "Stack too deep.");
m_context << eth::dupInstruction(stackDepth) << eth::dupInstruction(stackDepth);
ArrayUtils(m_context).convertLengthToSize(*previousDynamicType, true);
m_context << eth::Instruction::ADD;
@@ -275,13 +276,14 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract)
{
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts()))
for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables())
- m_context.addStateVariable(*variable);
+ if (!variable->isConstant())
+ m_context.addStateVariable(*variable);
}
void Compiler::initializeStateVariables(ContractDefinition const& _contract)
{
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
- if (variable->getValue())
+ if (variable->getValue() && !variable->isConstant())
ExpressionCompiler(m_context, m_optimize).appendStateVariableInitialization(*variable);
}
@@ -359,6 +361,7 @@ bool Compiler::visit(FunctionDefinition const& _function)
stackLayout.push_back(i);
stackLayout += vector<int>(c_localVariablesSize, -1);
+ solAssert(stackLayout.size() <= 17, "Stack too deep.");
while (stackLayout.back() != int(stackLayout.size() - 1))
if (stackLayout.back() < 0)
{
diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp
index 7b078e03..e517e384 100644
--- a/CompilerUtils.cpp
+++ b/CompilerUtils.cpp
@@ -138,6 +138,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
{
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable));
unsigned const size = _variable.getType()->getSizeOnStack();
+ solAssert(stackPosition >= size, "Variable size and position mismatch.");
// move variable starting from its top end in the stack
if (stackPosition - size + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation())
@@ -148,8 +149,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
{
- if (_stackDepth > 16)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Stack too deep."));
+ solAssert(_stackDepth <= 16, "Stack too deep.");
for (unsigned i = 0; i < _itemSize; ++i)
m_context << eth::dupInstruction(_stackDepth);
}
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index a9cacc65..92fd7043 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -233,9 +233,12 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
m_currentLValue->retrieveValue(_assignment.getLocation(), true);
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType());
if (lvalueSize > 0)
+ {
+ solAssert(itemSize + lvalueSize <= 16, "Stack too deep.");
// value [lvalue_ref] updated_value
for (unsigned i = 0; i < itemSize; ++i)
m_context << eth::swapInstruction(itemSize + lvalueSize) << eth::Instruction::POP;
+ }
}
m_currentLValue->storeValue(*_assignment.getRightHandSide().getType(), _assignment.getLocation());
m_currentLValue.reset();
@@ -557,10 +560,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case Location::SHA256:
case Location::RIPEMD160:
{
+ _functionCall.getExpression().accept(*this);
static const map<Location, u256> contractAddresses{{Location::ECRecover, 1},
{Location::SHA256, 2},
{Location::RIPEMD160, 3}};
m_context << contractAddresses.find(function.getLocation())->second;
+ for (unsigned i = function.getSizeOnStack(); i > 0; --i)
+ m_context << eth::swapInstruction(i);
appendExternalFunctionCall(function, arguments, true);
break;
}
@@ -852,8 +858,13 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
}
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag();
- else if (dynamic_cast<VariableDeclaration const*>(declaration))
- setLValueFromDeclaration(*declaration, _identifier);
+ else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
+ {
+ if (!variable->isConstant())
+ setLValueFromDeclaration(*declaration, _identifier);
+ else
+ variable->getValue()->accept(*this);
+ }
else if (dynamic_cast<ContractDefinition const*>(declaration))
{
// no-op
diff --git a/LValue.cpp b/LValue.cpp
index a56ed54c..68d6797e 100644
--- a/LValue.cpp
+++ b/LValue.cpp
@@ -167,6 +167,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
// stack: source_ref target_ref member_offset source_member_ref
StorageItem(m_context, *memberType).retrieveValue(_location, true);
// stack: source_ref target_ref member_offset source_value...
+ solAssert(2 + memberType->getSizeOnStack() <= 16, "Stack too deep.");
m_context << eth::dupInstruction(2 + memberType->getSizeOnStack())
<< eth::dupInstruction(2 + memberType->getSizeOnStack()) << eth::Instruction::ADD;
// stack: source_ref target_ref member_offset source_value... target_member_ref
diff --git a/Parser.cpp b/Parser.cpp
index 44d11159..459a34bd 100644
--- a/Parser.cpp
+++ b/Parser.cpp
@@ -317,6 +317,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
nodeFactory.setEndPositionFromNode(type);
}
bool isIndexed = false;
+ bool isDeclaredConst = false;
ASTPointer<ASTString> identifier;
Token::Value token = m_scanner->getCurrentToken();
Declaration::Visibility visibility(Declaration::Visibility::Default);
@@ -327,7 +328,13 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
isIndexed = true;
m_scanner->next();
}
+ if (token == Token::Const)
+ {
+ isDeclaredConst = true;
+ m_scanner->next();
+ }
nodeFactory.markEndPosition();
+
if (_options.allowEmptyName && m_scanner->getCurrentToken() != Token::Identifier)
{
identifier = make_shared<ASTString>("");
@@ -348,7 +355,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
}
return nodeFactory.createNode<VariableDeclaration>(type, identifier, value,
visibility, _options.isStateVariable,
- isIndexed);
+ isIndexed, isDeclaredConst);
}
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
@@ -913,6 +920,7 @@ Parser::LookAheadInfo Parser::peekStatementType() const
// In all other cases, we have an expression statement.
Token::Value token(m_scanner->getCurrentToken());
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
+
if (token == Token::Mapping || token == Token::Var ||
(mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier))
return LookAheadInfo::VariableDeclarationStatement;
diff --git a/Parser.h b/Parser.h
index cc0b2ca1..08c47c25 100644
--- a/Parser.h
+++ b/Parser.h
@@ -66,8 +66,7 @@ private:
ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<EnumDefinition> parseEnumDefinition();
ASTPointer<EnumValue> parseEnumValue();
- ASTPointer<VariableDeclaration> parseVariableDeclaration(
- VarDeclParserOptions const& _options = VarDeclParserOptions(),
+ ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions(),
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>());
ASTPointer<ModifierDefinition> parseModifierDefinition();
ASTPointer<EventDefinition> parseEventDefinition();
diff --git a/Types.h b/Types.h
index fd59a37a..853bd688 100644
--- a/Types.h
+++ b/Types.h
@@ -516,7 +516,7 @@ private:
std::vector<std::string> m_parameterNames;
std::vector<std::string> m_returnParameterNames;
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_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