aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2017-07-31 22:14:46 +0800
committerGitHub <noreply@github.com>2017-07-31 22:14:46 +0800
commitc2215d4605d1fbcef1366d6b822ec610fc031b3c (patch)
tree940ba55f0f27e8884332eaf90c11da48d5e98980 /libsolidity
parent0fb4cb1ab9bb4b6cc72e28cc5a1753ad14781f14 (diff)
parent2abfdb65c8dcda6866143280b7ff1bde094a1419 (diff)
downloaddexon-solidity-c2215d4605d1fbcef1366d6b822ec610fc031b3c.tar
dexon-solidity-c2215d4605d1fbcef1366d6b822ec610fc031b3c.tar.gz
dexon-solidity-c2215d4605d1fbcef1366d6b822ec610fc031b3c.tar.bz2
dexon-solidity-c2215d4605d1fbcef1366d6b822ec610fc031b3c.tar.lz
dexon-solidity-c2215d4605d1fbcef1366d6b822ec610fc031b3c.tar.xz
dexon-solidity-c2215d4605d1fbcef1366d6b822ec610fc031b3c.tar.zst
dexon-solidity-c2215d4605d1fbcef1366d6b822ec610fc031b3c.zip
Merge pull request #2667 from ethereum/develop
Merge develop into release in proparation for 0.4.14
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp129
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h9
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp2
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp9
-rw-r--r--libsolidity/analysis/StaticAnalyzer.h6
-rw-r--r--libsolidity/analysis/TypeChecker.cpp53
-rw-r--r--libsolidity/ast/AST.cpp45
-rw-r--r--libsolidity/ast/AST.h38
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp10
-rw-r--r--libsolidity/ast/Types.cpp169
-rw-r--r--libsolidity/ast/Types.h41
-rw-r--r--libsolidity/codegen/CompilerContext.cpp16
-rw-r--r--libsolidity/codegen/CompilerContext.h5
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp2
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp12
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp32
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp18
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp13
-rw-r--r--libsolidity/inlineasm/AsmPrinter.cpp7
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.cpp2
-rw-r--r--libsolidity/interface/CompilerStack.cpp98
-rw-r--r--libsolidity/interface/CompilerStack.h112
-rw-r--r--libsolidity/interface/ErrorReporter.cpp14
-rw-r--r--libsolidity/interface/ErrorReporter.h22
-rw-r--r--libsolidity/interface/Exceptions.cpp13
-rw-r--r--libsolidity/interface/Natspec.cpp17
-rw-r--r--libsolidity/interface/Natspec.h10
-rw-r--r--libsolidity/interface/StandardCompiler.cpp73
-rw-r--r--libsolidity/parsing/Parser.cpp2
-rw-r--r--libsolidity/parsing/Token.cpp10
-rw-r--r--libsolidity/parsing/Token.h2
-rw-r--r--libsolidity/parsing/UndefMacros.h46
32 files changed, 639 insertions, 398 deletions
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index aac90311..df83f382 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -104,29 +104,18 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
}
else
for (Declaration const* declaration: declarations)
- {
- ASTString const* name = alias.second ? alias.second.get() : &declaration->name();
- if (!target.registerDeclaration(*declaration, name))
- {
- m_errorReporter.declarationError(
- imp->location(),
- "Identifier \"" + *name + "\" already declared."
- );
+ if (!DeclarationRegistrationHelper::registerDeclaration(
+ target, *declaration, alias.second.get(), &imp->location(), true, m_errorReporter
+ ))
error = true;
- }
- }
}
else if (imp->name().empty())
for (auto const& nameAndDeclaration: scope->second->declarations())
for (auto const& declaration: nameAndDeclaration.second)
- if (!target.registerDeclaration(*declaration, &nameAndDeclaration.first))
- {
- m_errorReporter.declarationError(
- imp->location(),
- "Identifier \"" + nameAndDeclaration.first + "\" already declared."
- );
- error = true;
- }
+ if (!DeclarationRegistrationHelper::registerDeclaration(
+ target, *declaration, &nameAndDeclaration.first, &imp->location(), true, m_errorReporter
+ ))
+ error = true;
}
return !error;
}
@@ -450,6 +439,75 @@ DeclarationRegistrationHelper::DeclarationRegistrationHelper(
solAssert(m_currentScope == _currentScope, "Scopes not correctly closed.");
}
+bool DeclarationRegistrationHelper::registerDeclaration(
+ DeclarationContainer& _container,
+ Declaration const& _declaration,
+ string const* _name,
+ SourceLocation const* _errorLocation,
+ bool _warnOnShadow,
+ ErrorReporter& _errorReporter
+)
+{
+ if (!_errorLocation)
+ _errorLocation = &_declaration.location();
+
+ Declaration const* shadowedDeclaration = nullptr;
+ if (_warnOnShadow && !_declaration.name().empty())
+ for (auto const* decl: _container.resolveName(_declaration.name(), true))
+ if (decl != &_declaration)
+ {
+ shadowedDeclaration = decl;
+ break;
+ }
+
+ if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract()))
+ {
+ SourceLocation firstDeclarationLocation;
+ SourceLocation secondDeclarationLocation;
+ Declaration const* conflictingDeclaration = _container.conflictingDeclaration(_declaration, _name);
+ solAssert(conflictingDeclaration, "");
+ bool const comparable =
+ _errorLocation->sourceName &&
+ conflictingDeclaration->location().sourceName &&
+ *_errorLocation->sourceName == *conflictingDeclaration->location().sourceName;
+ if (comparable && _errorLocation->start < conflictingDeclaration->location().start)
+ {
+ firstDeclarationLocation = *_errorLocation;
+ secondDeclarationLocation = conflictingDeclaration->location();
+ }
+ else
+ {
+ firstDeclarationLocation = conflictingDeclaration->location();
+ secondDeclarationLocation = *_errorLocation;
+ }
+
+ _errorReporter.declarationError(
+ secondDeclarationLocation,
+ SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
+ "Identifier already declared."
+ );
+ return false;
+ }
+ else if (shadowedDeclaration)
+ {
+ if (dynamic_cast<MagicVariableDeclaration const*>(shadowedDeclaration))
+ _errorReporter.warning(
+ _declaration.location(),
+ "This declaration shadows a builtin symbol."
+ );
+ else
+ {
+ auto shadowedLocation = shadowedDeclaration->location();
+ _errorReporter.warning(
+ _declaration.location(),
+ "This declaration shadows an existing declaration.",
+ SecondarySourceLocation().append("The shadowed declaration is here:", shadowedLocation)
+ );
+ }
+ }
+ return true;
+}
+
bool DeclarationRegistrationHelper::visit(SourceUnit& _sourceUnit)
{
if (!m_scopes[&_sourceUnit])
@@ -590,30 +648,21 @@ void DeclarationRegistrationHelper::closeCurrentScope()
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
{
solAssert(m_currentScope && m_scopes.count(m_currentScope), "No current scope.");
- if (!m_scopes[m_currentScope]->registerDeclaration(_declaration, nullptr, !_declaration.isVisibleInContract()))
- {
- SourceLocation firstDeclarationLocation;
- SourceLocation secondDeclarationLocation;
- Declaration const* conflictingDeclaration = m_scopes[m_currentScope]->conflictingDeclaration(_declaration);
- solAssert(conflictingDeclaration, "");
- if (_declaration.location().start < conflictingDeclaration->location().start)
- {
- firstDeclarationLocation = _declaration.location();
- secondDeclarationLocation = conflictingDeclaration->location();
- }
- else
- {
- firstDeclarationLocation = conflictingDeclaration->location();
- secondDeclarationLocation = _declaration.location();
- }
+ bool warnAboutShadowing = true;
+ // Do not warn about shadowing for structs and enums because their members are
+ // not accessible without prefixes.
+ if (
+ dynamic_cast<StructDefinition const*>(m_currentScope) ||
+ dynamic_cast<EnumDefinition const*>(m_currentScope)
+ )
+ warnAboutShadowing = false;
+ // Do not warn about the constructor shadowing the contract.
+ if (auto fun = dynamic_cast<FunctionDefinition const*>(&_declaration))
+ if (fun->isConstructor())
+ warnAboutShadowing = false;
- m_errorReporter.declarationError(
- secondDeclarationLocation,
- SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
- "Identifier already declared."
- );
- }
+ registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, m_errorReporter);
_declaration.setScope(m_currentScope);
if (_opensScope)
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index 84628778..a498c7ba 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -136,6 +136,15 @@ public:
ASTNode const* _currentScope = nullptr
);
+ static bool registerDeclaration(
+ DeclarationContainer& _container,
+ Declaration const& _declaration,
+ std::string const* _name,
+ SourceLocation const* _errorLocation,
+ bool _warnOnShadow,
+ ErrorReporter& _errorReporter
+ );
+
private:
bool visit(SourceUnit& _sourceUnit) override;
void endVisit(SourceUnit& _sourceUnit) override;
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index cc95c294..8f07d43a 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -295,7 +295,7 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
else
{
typeLoc = DataLocation::Storage;
- if (!_variable.isStateVariable())
+ if (_variable.isLocalVariable())
m_errorReporter.warning(
_variable.location(),
"Variable is declared as a storage pointer. "
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index b1b31163..46477e1e 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -38,12 +38,14 @@ bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit)
bool StaticAnalyzer::visit(ContractDefinition const& _contract)
{
m_library = _contract.isLibrary();
+ m_currentContract = &_contract;
return true;
}
void StaticAnalyzer::endVisit(ContractDefinition const&)
{
m_library = false;
+ m_currentContract = nullptr;
}
bool StaticAnalyzer::visit(FunctionDefinition const& _function)
@@ -54,6 +56,7 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function)
solAssert(!m_currentFunction, "");
solAssert(m_localVarUseCount.empty(), "");
m_nonPayablePublic = _function.isPublic() && !_function.isPayable();
+ m_constructor = _function.isConstructor();
return true;
}
@@ -61,6 +64,7 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&)
{
m_currentFunction = nullptr;
m_nonPayablePublic = false;
+ m_constructor = false;
for (auto const& var: m_localVarUseCount)
if (var.second == 0)
m_errorReporter.warning(var.first->location(), "Unused local variable");
@@ -131,6 +135,11 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
"\"callcode\" has been deprecated in favour of \"delegatecall\"."
);
+ if (m_constructor && m_currentContract)
+ if (ContractType const* type = dynamic_cast<ContractType const*>(_memberAccess.expression().annotation().type.get()))
+ if (type->contractDefinition() == *m_currentContract)
+ m_errorReporter.warning(_memberAccess.location(), "\"this\" used in constructor.");
+
return true;
}
diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h
index cd6913b5..21a487df 100644
--- a/libsolidity/analysis/StaticAnalyzer.h
+++ b/libsolidity/analysis/StaticAnalyzer.h
@@ -77,6 +77,12 @@ private:
std::map<VariableDeclaration const*, int> m_localVarUseCount;
FunctionDefinition const* m_currentFunction = nullptr;
+
+ /// Flag that indicates a constructor.
+ bool m_constructor = false;
+
+ /// Current contract.
+ ContractDefinition const* m_currentContract = nullptr;
};
}
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 7306a36d..5419db2d 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -93,7 +93,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
FunctionDefinition const* fallbackFunction = nullptr;
for (FunctionDefinition const* function: _contract.definedFunctions())
{
- if (function->name().empty())
+ if (function->isFallback())
{
if (fallbackFunction)
{
@@ -482,7 +482,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
{
if (isLibraryFunction)
m_errorReporter.typeError(_function.location(), "Library functions cannot be payable.");
- if (!_function.isConstructor() && !_function.name().empty() && !_function.isPartOfExternalInterface())
+ if (!_function.isConstructor() && !_function.isFallback() && !_function.isPartOfExternalInterface())
m_errorReporter.typeError(_function.location(), "Internal functions cannot be payable.");
if (_function.isDeclaredConst())
m_errorReporter.typeError(_function.location(), "Functions cannot be constant and payable at the same time.");
@@ -510,8 +510,6 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
{
if (dynamic_cast<ContractDefinition const*>(decl))
m_errorReporter.declarationError(modifier->location(), "Base constructor already provided.");
- else
- m_errorReporter.declarationError(modifier->location(), "Modifier already used for this function.");
}
else
modifiers.insert(decl);
@@ -583,6 +581,16 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
!FunctionType(_variable).interfaceFunctionType()
)
m_errorReporter.typeError(_variable.location(), "Internal type is not allowed for public state variables.");
+
+ if (varType->category() == Type::Category::Array)
+ if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get()))
+ if (
+ ((arrayType->location() == DataLocation::Memory) ||
+ (arrayType->location() == DataLocation::CallData)) &&
+ !arrayType->validForCalldata()
+ )
+ m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded as calldata.");
+
return false;
}
@@ -723,7 +731,10 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
}
else if (var->type()->sizeOnStack() != 1)
{
- m_errorReporter.typeError(_identifier.location, "Only types that use one stack slot are supported.");
+ if (var->type()->dataStoredIn(DataLocation::CallData))
+ m_errorReporter.typeError(_identifier.location, "Call data elements cannot be accessed directly. Copy to a local variable first or use \"calldataload\" or \"calldatacopy\" with manually determined offsets and sizes.");
+ else
+ m_errorReporter.typeError(_identifier.location, "Only types that use one stack slot are supported.");
return size_t(-1);
}
}
@@ -1111,7 +1122,9 @@ bool TypeChecker::visit(Assignment const& _assignment)
_assignment.annotation().type = make_shared<TupleType>();
expectType(_assignment.rightHandSide(), *tupleType);
- checkDoubleStorageAssignment(_assignment);
+ // expectType does not cause fatal errors, so we have to check again here.
+ if (dynamic_cast<TupleType const*>(type(_assignment.rightHandSide()).get()))
+ checkDoubleStorageAssignment(_assignment);
}
else if (t->category() == Type::Category::Mapping)
{
@@ -1351,7 +1364,14 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
// (data location cannot yet be specified for type conversions)
resultType = ReferenceType::copyForLocationIfReference(argRefType->location(), resultType);
if (!argType->isExplicitlyConvertibleTo(*resultType))
- m_errorReporter.typeError(_functionCall.location(), "Explicit type conversion not allowed.");
+ m_errorReporter.typeError(
+ _functionCall.location(),
+ "Explicit type conversion not allowed from \"" +
+ argType->toString() +
+ "\" to \"" +
+ resultType->toString() +
+ "\"."
+ );
}
_functionCall.annotation().type = resultType;
_functionCall.annotation().isPure = isPure;
@@ -1628,6 +1648,25 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
annotation.isLValue = annotation.referencedDeclaration->isLValue();
}
+ if (exprType->category() == Type::Category::Contract)
+ {
+ if (auto callType = dynamic_cast<FunctionType const*>(type(_memberAccess).get()))
+ {
+ auto kind = callType->kind();
+ auto contractType = dynamic_cast<ContractType const*>(exprType.get());
+ solAssert(!!contractType, "Should be contract type.");
+
+ if (
+ (kind == FunctionType::Kind::Send || kind == FunctionType::Kind::Transfer) &&
+ !contractType->isPayable()
+ )
+ m_errorReporter.typeError(
+ _memberAccess.location(),
+ "Value transfer to a contract without a payable fallback function."
+ );
+ }
+ }
+
// TODO some members might be pure, but for example `address(0x123).balance` is not pure
// although every subexpression is, so leaving this limited for now.
if (auto tt = dynamic_cast<TypeType const*>(exprType.get()))
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 403f4b79..1d68231e 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -84,13 +84,35 @@ SourceUnitAnnotation& SourceUnit::annotation() const
return dynamic_cast<SourceUnitAnnotation&>(*m_annotation);
}
-string Declaration::sourceUnitName() const
+set<SourceUnit const*> SourceUnit::referencedSourceUnits(bool _recurse, set<SourceUnit const*> _skipList) const
+{
+ set<SourceUnit const*> sourceUnits;
+ for (ImportDirective const* importDirective: filteredNodes<ImportDirective>(nodes()))
+ {
+ auto const& sourceUnit = importDirective->annotation().sourceUnit;
+ if (!_skipList.count(sourceUnit))
+ {
+ _skipList.insert(sourceUnit);
+ sourceUnits.insert(sourceUnit);
+ if (_recurse)
+ sourceUnits += sourceUnit->referencedSourceUnits(true, _skipList);
+ }
+ }
+ return sourceUnits;
+}
+
+SourceUnit const& Declaration::sourceUnit() const
{
solAssert(!!m_scope, "");
ASTNode const* scope = m_scope;
while (dynamic_cast<Declaration const*>(scope) && dynamic_cast<Declaration const*>(scope)->m_scope)
scope = dynamic_cast<Declaration const*>(scope)->m_scope;
- return dynamic_cast<SourceUnit const&>(*scope).annotation().path;
+ return dynamic_cast<SourceUnit const&>(*scope);
+}
+
+string Declaration::sourceUnitName() const
+{
+ return sourceUnit().annotation().path;
}
ImportAnnotation& ImportDirective::annotation() const
@@ -140,7 +162,7 @@ FunctionDefinition const* ContractDefinition::fallbackFunction() const
{
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
for (FunctionDefinition const* f: contract->definedFunctions())
- if (f->name().empty())
+ if (f->isFallback())
return f;
return nullptr;
}
@@ -416,6 +438,23 @@ bool VariableDeclaration::isCallableParameter() const
return false;
}
+bool VariableDeclaration::isLocalOrReturn() const
+{
+ return isReturnParameter() || (isLocalVariable() && !isCallableParameter());
+}
+
+bool VariableDeclaration::isReturnParameter() const
+{
+ auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
+ if (!callable)
+ return false;
+ if (callable->returnParameterList())
+ for (auto const& variable: callable->returnParameterList()->parameters())
+ if (variable.get() == this)
+ return true;
+ return false;
+}
+
bool VariableDeclaration::isExternalCallableParameter() const
{
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index e8831dc0..3e97286b 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -23,19 +23,24 @@
#pragma once
-#include <string>
-#include <vector>
-#include <memory>
-#include <boost/noncopyable.hpp>
-#include <libevmasm/SourceLocation.h>
-#include <libevmasm/Instruction.h>
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/parsing/Token.h>
#include <libsolidity/ast/Types.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/ASTAnnotations.h>
+
+#include <libevmasm/SourceLocation.h>
+#include <libevmasm/Instruction.h>
+
+#include <libdevcore/FixedHash.h>
#include <json/json.h>
+#include <boost/noncopyable.hpp>
+
+#include <string>
+#include <vector>
+#include <memory>
+
namespace dev
{
namespace solidity
@@ -131,6 +136,9 @@ public:
std::vector<ASTPointer<ASTNode>> nodes() const { return m_nodes; }
+ /// @returns a set of referenced SourceUnits. Recursively if @a _recurse is true.
+ std::set<SourceUnit const*> referencedSourceUnits(bool _recurse = false, std::set<SourceUnit const*> _skipList = std::set<SourceUnit const*>()) const;
+
private:
std::vector<ASTPointer<ASTNode>> m_nodes;
};
@@ -163,6 +171,9 @@ public:
ASTNode const* scope() const { return m_scope; }
void setScope(ASTNode const* _scope) { m_scope = _scope; }
+ /// @returns the source unit this declaration is present in.
+ SourceUnit const& sourceUnit() const;
+
/// @returns the source name this declaration is present in.
/// Can be combined with annotation().canonicalName to form a globally unique name.
std::string sourceUnitName() const;
@@ -578,6 +589,7 @@ public:
virtual void accept(ASTConstVisitor& _visitor) const override;
bool isConstructor() const { return m_isConstructor; }
+ bool isFallback() const { return name().empty(); }
bool isDeclaredConst() const { return m_isDeclaredConst; }
bool isPayable() const { return m_isPayable; }
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
@@ -585,9 +597,9 @@ public:
Block const& body() const { solAssert(m_body, ""); return *m_body; }
virtual bool isVisibleInContract() const override
{
- return Declaration::isVisibleInContract() && !isConstructor() && !name().empty();
+ return Declaration::isVisibleInContract() && !isConstructor() && !isFallback();
}
- virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstructor && !name().empty(); }
+ virtual bool isPartOfExternalInterface() const override { return isPublic() && !isConstructor() && !isFallback(); }
/// @returns the external signature of the function
/// That consists of the name of the function followed by the types of the
@@ -650,6 +662,10 @@ public:
bool isLocalVariable() const { return !!dynamic_cast<CallableDeclaration const*>(scope()); }
/// @returns true if this variable is a parameter or return parameter of a function.
bool isCallableParameter() const;
+ /// @returns true if this variable is a return parameter of a function.
+ bool isReturnParameter() const;
+ /// @returns true if this variable is a local variable or return parameter.
+ bool isLocalOrReturn() const;
/// @returns true if this variable is a parameter (not return parameter) of an external function.
bool isExternalCallableParameter() const;
/// @returns true if the type of the variable does not need to be specified, i.e. it is declared
@@ -695,7 +711,7 @@ public:
ASTPointer<ParameterList> const& _parameters,
ASTPointer<Block> const& _body
):
- CallableDeclaration(_location, _name, Visibility::Default, _parameters),
+ CallableDeclaration(_location, _name, Visibility::Internal, _parameters),
Documented(_documentation),
m_body(_body)
{
@@ -782,11 +798,11 @@ public:
Declaration(SourceLocation(), std::make_shared<ASTString>(_name)), m_type(_type) {}
virtual void accept(ASTVisitor&) override
{
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("MagicVariableDeclaration used inside real AST."));
+ solAssert(false, "MagicVariableDeclaration used inside real AST.");
}
virtual void accept(ASTConstVisitor&) const override
{
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("MagicVariableDeclaration used inside real AST."));
+ solAssert(false, "MagicVariableDeclaration used inside real AST.");
}
virtual TypePointer type() const override { return m_type; }
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index a90debb2..eda70b63 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -743,7 +743,7 @@ string ASTJsonConverter::visibility(Declaration::Visibility const& _visibility)
case Declaration::Visibility::External:
return "external";
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown declaration visibility."));
+ solAssert(false, "Unknown declaration visibility.");
}
}
@@ -758,7 +758,7 @@ string ASTJsonConverter::location(VariableDeclaration::Location _location)
case VariableDeclaration::Location::Memory:
return "memory";
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown declaration location."));
+ solAssert(false, "Unknown declaration location.");
}
}
@@ -773,7 +773,7 @@ string ASTJsonConverter::contractKind(ContractDefinition::ContractKind _kind)
case ContractDefinition::ContractKind::Library:
return "library";
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of contract."));
+ solAssert(false, "Unknown kind of contract.");
}
}
@@ -788,7 +788,7 @@ string ASTJsonConverter::functionCallKind(FunctionCallKind _kind)
case FunctionCallKind::StructConstructorCall:
return "structConstructorCall";
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of function call ."));
+ solAssert(false, "Unknown kind of function call.");
}
}
@@ -804,7 +804,7 @@ string ASTJsonConverter::literalTokenKind(Token::Value _token)
case dev::solidity::Token::FalseLiteral:
return "bool";
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of literal token."));
+ solAssert(false, "Unknown kind of literal token.");
}
}
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 7dc6c4a6..3f8da501 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -196,9 +196,9 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
case Token::UInt:
return make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned);
case Token::Fixed:
- return make_shared<FixedPointType>(128, 128, FixedPointType::Modifier::Signed);
+ return make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Signed);
case Token::UFixed:
- return make_shared<FixedPointType>(128, 128, FixedPointType::Modifier::Unsigned);
+ return make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Unsigned);
case Token::Byte:
return make_shared<FixedBytesType>(1);
case Token::Address:
@@ -211,9 +211,10 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
return make_shared<ArrayType>(DataLocation::Storage, true);
//no types found
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment(
+ solAssert(
+ false,
"Unable to convert elementary typename " + _type.toString() + " to type."
- ));
+ );
}
}
@@ -352,12 +353,11 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
else if (_convertTo.category() == Category::FixedPoint)
{
FixedPointType const& convertTo = dynamic_cast<FixedPointType const&>(_convertTo);
- if (convertTo.integerBits() < m_bits || isAddress())
+
+ if (isAddress())
return false;
- else if (isSigned())
- return convertTo.isSigned();
else
- return !convertTo.isSigned() || convertTo.integerBits() > m_bits;
+ return maxValue() <= convertTo.maxIntegerValue() && minValue() >= convertTo.minIntegerValue();
}
else
return false;
@@ -413,6 +413,22 @@ u256 IntegerType::literalValue(Literal const* _literal) const
return u256(_literal->value());
}
+bigint IntegerType::minValue() const
+{
+ if (isSigned())
+ return -(bigint(1) << (m_bits - 1));
+ else
+ return bigint(0);
+}
+
+bigint IntegerType::maxValue() const
+{
+ if (isSigned())
+ return (bigint(1) << (m_bits - 1)) - 1;
+ else
+ return (bigint(1) << m_bits) - 1;
+}
+
TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
if (
@@ -471,22 +487,20 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
return MemberList::MemberMap();
}
-FixedPointType::FixedPointType(int _integerBits, int _fractionalBits, FixedPointType::Modifier _modifier):
- m_integerBits(_integerBits), m_fractionalBits(_fractionalBits), m_modifier(_modifier)
+FixedPointType::FixedPointType(int _totalBits, int _fractionalDigits, FixedPointType::Modifier _modifier):
+ m_totalBits(_totalBits), m_fractionalDigits(_fractionalDigits), m_modifier(_modifier)
{
solAssert(
- m_integerBits + m_fractionalBits > 0 &&
- m_integerBits + m_fractionalBits <= 256 &&
- m_integerBits % 8 == 0 &&
- m_fractionalBits % 8 == 0,
+ 8 <= m_totalBits && m_totalBits <= 256 && m_totalBits % 8 == 0 &&
+ 0 <= m_fractionalDigits && m_fractionalDigits <= 80,
"Invalid bit number(s) for fixed type: " +
- dev::toString(_integerBits) + "x" + dev::toString(_fractionalBits)
- );
+ dev::toString(_totalBits) + "x" + dev::toString(_fractionalDigits)
+ );
}
string FixedPointType::identifier() const
{
- return "t_" + string(isSigned() ? "" : "u") + "fixed" + std::to_string(integerBits()) + "x" + std::to_string(fractionalBits());
+ return "t_" + string(isSigned() ? "" : "u") + "fixed" + std::to_string(m_totalBits) + "x" + std::to_string(m_fractionalDigits);
}
bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
@@ -494,12 +508,10 @@ bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
if (_convertTo.category() == category())
{
FixedPointType const& convertTo = dynamic_cast<FixedPointType const&>(_convertTo);
- if (convertTo.m_integerBits < m_integerBits || convertTo.m_fractionalBits < m_fractionalBits)
+ if (convertTo.numBits() < m_totalBits || convertTo.fractionalDigits() < m_fractionalDigits)
return false;
- else if (isSigned())
- return convertTo.isSigned();
else
- return !convertTo.isSigned() || (convertTo.m_integerBits > m_integerBits);
+ return convertTo.maxIntegerValue() >= maxIntegerValue() && convertTo.minIntegerValue() <= minIntegerValue();
}
return false;
}
@@ -533,13 +545,30 @@ bool FixedPointType::operator==(Type const& _other) const
if (_other.category() != category())
return false;
FixedPointType const& other = dynamic_cast<FixedPointType const&>(_other);
- return other.m_integerBits == m_integerBits && other.m_fractionalBits == m_fractionalBits && other.m_modifier == m_modifier;
+ return other.m_totalBits == m_totalBits && other.m_fractionalDigits == m_fractionalDigits && other.m_modifier == m_modifier;
}
string FixedPointType::toString(bool) const
{
string prefix = isSigned() ? "fixed" : "ufixed";
- return prefix + dev::toString(m_integerBits) + "x" + dev::toString(m_fractionalBits);
+ return prefix + dev::toString(m_totalBits) + "x" + dev::toString(m_fractionalDigits);
+}
+
+bigint FixedPointType::maxIntegerValue() const
+{
+ bigint maxValue = (bigint(1) << (m_totalBits - (isSigned() ? 1 : 0))) - 1;
+ return maxValue / pow(bigint(10), m_fractionalDigits);
+}
+
+bigint FixedPointType::minIntegerValue() const
+{
+ if (isSigned())
+ {
+ bigint minValue = -(bigint(1) << (m_totalBits - (isSigned() ? 1 : 0)));
+ return minValue / pow(bigint(10), m_fractionalDigits);
+ }
+ else
+ return bigint(0);
}
TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
@@ -727,13 +756,9 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
else if (_convertTo.category() == Category::FixedPoint)
{
if (auto fixed = fixedPointType())
- {
- // We disallow implicit conversion if we would have to truncate (fixedPointType()
- // can return a type that requires truncation).
- rational value = m_value * (bigint(1) << fixed->fractionalBits());
- return value.denominator() == 1 && fixed->isImplicitlyConvertibleTo(_convertTo);
- }
- return false;
+ return fixed->isImplicitlyConvertibleTo(_convertTo);
+ else
+ return false;
}
else if (_convertTo.category() == Category::FixedBytes)
{
@@ -937,10 +962,9 @@ u256 RationalNumberType::literalValue(Literal const*) const
else
{
auto fixed = fixedPointType();
- solAssert(!!fixed, "");
- rational shifted = m_value * (bigint(1) << fixed->fractionalBits());
- // truncate
- shiftedValue = shifted.numerator() / shifted.denominator();
+ solAssert(fixed, "");
+ int fractionalDigits = fixed->fractionalDigits();
+ shiftedValue = (m_value.numerator() / m_value.denominator()) * pow(bigint(10), fractionalDigits);
}
// we ignore the literal and hope that the type was correctly determined
@@ -981,22 +1005,21 @@ shared_ptr<IntegerType const> RationalNumberType::integerType() const
shared_ptr<FixedPointType const> RationalNumberType::fixedPointType() const
{
bool negative = (m_value < 0);
- unsigned fractionalBits = 0;
+ unsigned fractionalDigits = 0;
rational value = abs(m_value); // We care about the sign later.
rational maxValue = negative ?
rational(bigint(1) << 255, 1):
rational((bigint(1) << 256) - 1, 1);
- while (value * 0x100 <= maxValue && value.denominator() != 1 && fractionalBits < 256)
+ while (value * 10 <= maxValue && value.denominator() != 1 && fractionalDigits < 80)
{
- value *= 0x100;
- fractionalBits += 8;
+ value *= 10;
+ fractionalDigits++;
}
if (value > maxValue)
return shared_ptr<FixedPointType const>();
- // u256(v) is the actual value that will be put on the stack
- // From here on, very similar to integerType()
+ // This means we round towards zero for positive and negative values.
bigint v = value.numerator() / value.denominator();
if (negative)
// modify value to satisfy bit requirements for negative numbers:
@@ -1006,26 +1029,11 @@ shared_ptr<FixedPointType const> RationalNumberType::fixedPointType() const
if (v > u256(-1))
return shared_ptr<FixedPointType const>();
- unsigned totalBits = bytesRequired(v) * 8;
+ unsigned totalBits = max(bytesRequired(v), 1u) * 8;
solAssert(totalBits <= 256, "");
- unsigned integerBits = totalBits >= fractionalBits ? totalBits - fractionalBits : 0;
- // Special case: Numbers between -1 and 0 have their sign bit in the fractional part.
- if (negative && abs(m_value) < 1 && totalBits > fractionalBits)
- {
- fractionalBits += 8;
- integerBits = 0;
- }
-
- if (integerBits > 256 || fractionalBits > 256 || fractionalBits + integerBits > 256)
- return shared_ptr<FixedPointType const>();
- if (integerBits == 0 && fractionalBits == 0)
- {
- integerBits = 0;
- fractionalBits = 8;
- }
return make_shared<FixedPointType>(
- integerBits, fractionalBits,
+ totalBits, fractionalDigits,
negative ? FixedPointType::Modifier::Signed : FixedPointType::Modifier::Unsigned
);
}
@@ -1169,7 +1177,7 @@ u256 BoolType::literalValue(Literal const* _literal) const
else if (_literal->token() == Token::FalseLiteral)
return u256(0);
else
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal."));
+ solAssert(false, "Bool type constructed from non-boolean literal.");
}
TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const
@@ -1214,6 +1222,12 @@ bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
_convertTo.category() == Category::Contract;
}
+bool ContractType::isPayable() const
+{
+ auto fallbackFunction = m_contract.fallbackFunction();
+ return fallbackFunction && fallbackFunction->isPayable();
+}
+
TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
{
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
@@ -1373,12 +1387,23 @@ bool ArrayType::operator==(Type const& _other) const
return isDynamicallySized() || length() == other.length();
}
-unsigned ArrayType::calldataEncodedSize(bool _padded) const
+bool ArrayType::validForCalldata() const
+{
+ return unlimitedCalldataEncodedSize(true) <= numeric_limits<unsigned>::max();
+}
+
+bigint ArrayType::unlimitedCalldataEncodedSize(bool _padded) const
{
if (isDynamicallySized())
return 32;
bigint size = bigint(length()) * (isByteArray() ? 1 : baseType()->calldataEncodedSize(_padded));
size = ((size + 31) / 32) * 32;
+ return size;
+}
+
+unsigned ArrayType::calldataEncodedSize(bool _padded) const
+{
+ bigint size = unlimitedCalldataEncodedSize(_padded);
solAssert(size <= numeric_limits<unsigned>::max(), "Array size does not fit unsigned.");
return unsigned(size);
}
@@ -1914,10 +1939,7 @@ string TupleType::toString(bool _short) const
u256 TupleType::storageSize() const
{
- BOOST_THROW_EXCEPTION(
- InternalCompilerError() <<
- errinfo_comment("Storage size of non-storable tuple type requested.")
- );
+ solAssert(false, "Storage size of non-storable tuple type requested.");
}
unsigned TupleType::sizeOnStack() const
@@ -2299,9 +2321,7 @@ u256 FunctionType::storageSize() const
if (m_kind == Kind::External || m_kind == Kind::Internal)
return 1;
else
- BOOST_THROW_EXCEPTION(
- InternalCompilerError()
- << errinfo_comment("Storage size of non-storable function type requested."));
+ solAssert(false, "Storage size of non-storable function type requested.");
}
unsigned FunctionType::storageBytes() const
@@ -2311,9 +2331,7 @@ unsigned FunctionType::storageBytes() const
else if (m_kind == Kind::Internal)
return 8; // it should really not be possible to create larger programs
else
- BOOST_THROW_EXCEPTION(
- InternalCompilerError()
- << errinfo_comment("Storage size of non-storable function type requested."));
+ solAssert(false, "Storage size of non-storable function type requested.");
}
unsigned FunctionType::sizeOnStack() const
@@ -2506,6 +2524,7 @@ bool FunctionType::isBareCall() const
string FunctionType::externalSignature() const
{
solAssert(m_declaration != nullptr, "External signature of function needs declaration");
+ solAssert(!m_declaration->name().empty(), "Fallback function has no signature.");
bool _inLibrary = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
@@ -2671,9 +2690,7 @@ bool TypeType::operator==(Type const& _other) const
u256 TypeType::storageSize() const
{
- BOOST_THROW_EXCEPTION(
- InternalCompilerError()
- << errinfo_comment("Storage size of non-storable type type requested."));
+ solAssert(false, "Storage size of non-storable type type requested.");
}
unsigned TypeType::sizeOnStack() const
@@ -2740,9 +2757,7 @@ ModifierType::ModifierType(const ModifierDefinition& _modifier)
u256 ModifierType::storageSize() const
{
- BOOST_THROW_EXCEPTION(
- InternalCompilerError()
- << errinfo_comment("Storage size of non-storable type type requested."));
+ solAssert(false, "Storage size of non-storable type type requested.");
}
string ModifierType::identifier() const
@@ -2851,7 +2866,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
{"gasprice", make_shared<IntegerType>(256)}
});
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));
+ solAssert(false, "Unknown kind of magic.");
}
}
@@ -2866,6 +2881,6 @@ string MagicType::toString(bool) const
case Kind::Transaction:
return "tx";
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));
+ solAssert(false, "Unknown kind of magic.");
}
}
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index f7a73ab5..1db46355 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -28,7 +28,6 @@
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
-#include <libdevcore/UndefMacros.h>
#include <boost/noncopyable.hpp>
#include <boost/rational.hpp>
@@ -246,10 +245,7 @@ public:
virtual std::string canonicalName(bool /*_addDataLocation*/) const { return toString(true); }
virtual u256 literalValue(Literal const*) const
{
- BOOST_THROW_EXCEPTION(
- InternalCompilerError() <<
- errinfo_comment("Literal value requested for type without literals.")
- );
+ solAssert(false, "Literal value requested for type without literals.");
}
/// @returns a (simpler) type that is encoded in the same way for external function calls.
@@ -323,6 +319,9 @@ public:
bool isAddress() const { return m_modifier == Modifier::Address; }
bool isSigned() const { return m_modifier == Modifier::Signed; }
+ bigint minValue() const;
+ bigint maxValue() const;
+
private:
int m_bits;
Modifier m_modifier;
@@ -340,7 +339,7 @@ public:
};
virtual Category category() const override { return Category::FixedPoint; }
- explicit FixedPointType(int _integerBits, int _fractionalBits, Modifier _modifier = Modifier::Unsigned);
+ explicit FixedPointType(int _totalBits, int _fractionalDigits, Modifier _modifier = Modifier::Unsigned);
virtual std::string identifier() const override;
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
@@ -350,8 +349,8 @@ public:
virtual bool operator==(Type const& _other) const override;
- virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : (m_integerBits + m_fractionalBits) / 8; }
- virtual unsigned storageBytes() const override { return (m_integerBits + m_fractionalBits) / 8; }
+ virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : m_totalBits / 8; }
+ virtual unsigned storageBytes() const override { return m_totalBits / 8; }
virtual bool isValueType() const override { return true; }
virtual std::string toString(bool _short) const override;
@@ -359,14 +358,21 @@ public:
virtual TypePointer encodingType() const override { return shared_from_this(); }
virtual TypePointer interfaceType(bool) const override { return shared_from_this(); }
- int numBits() const { return m_integerBits + m_fractionalBits; }
- int integerBits() const { return m_integerBits; }
- int fractionalBits() const { return m_fractionalBits; }
+ /// Number of bits used for this type in total.
+ int numBits() const { return m_totalBits; }
+ /// Number of decimal digits after the radix point.
+ int fractionalDigits() const { return m_fractionalDigits; }
bool isSigned() const { return m_modifier == Modifier::Signed; }
+ /// @returns the largest integer value this type con hold. Note that this is not the
+ /// largest value in general.
+ bigint maxIntegerValue() const;
+ /// @returns the smallest integer value this type can hold. Note hat this is not the
+ /// smallest value in general.
+ bigint minIntegerValue() const;
private:
- int m_integerBits;
- int m_fractionalBits;
+ int m_totalBits;
+ int m_fractionalDigits;
Modifier m_modifier;
};
@@ -614,6 +620,9 @@ public:
virtual TypePointer interfaceType(bool _inLibrary) const override;
virtual bool canBeUsedExternally(bool _inLibrary) const override;
+ /// @returns true if this is valid to be stored in calldata
+ bool validForCalldata() const;
+
/// @returns true if this is a byte array or a string
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
/// @returns true if this is a string
@@ -628,6 +637,8 @@ private:
/// String is interpreted as a subtype of Bytes.
enum class ArrayKind { Ordinary, Bytes, String };
+ bigint unlimitedCalldataEncodedSize(bool _padded) const;
+
///< Byte arrays ("bytes") and strings have different semantics from ordinary arrays.
ArrayKind m_arrayKind = ArrayKind::Ordinary;
TypePointer m_baseType;
@@ -673,6 +684,10 @@ public:
}
bool isSuper() const { return m_super; }
+
+ // @returns true if and only if the contract has a payable fallback function
+ bool isPayable() const;
+
ContractDefinition const& contractDefinition() const { return m_contract; }
/// Returns the function type of the constructor modified to return an object of the contract's type.
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index 6875bda1..bc4de3ee 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -124,14 +124,15 @@ void CompilerContext::addVariable(VariableDeclaration const& _declaration,
unsigned _offsetToCurrent)
{
solAssert(m_asm->deposit() >= 0 && unsigned(m_asm->deposit()) >= _offsetToCurrent, "");
- solAssert(m_localVariables.count(&_declaration) == 0, "Variable already present");
- m_localVariables[&_declaration] = unsigned(m_asm->deposit()) - _offsetToCurrent;
+ m_localVariables[&_declaration].push_back(unsigned(m_asm->deposit()) - _offsetToCurrent);
}
void CompilerContext::removeVariable(VariableDeclaration const& _declaration)
{
- solAssert(!!m_localVariables.count(&_declaration), "");
- m_localVariables.erase(&_declaration);
+ solAssert(m_localVariables.count(&_declaration) && !m_localVariables[&_declaration].empty(), "");
+ m_localVariables[&_declaration].pop_back();
+ if (m_localVariables[&_declaration].empty())
+ m_localVariables.erase(&_declaration);
}
eth::Assembly const& CompilerContext::compiledContract(const ContractDefinition& _contract) const
@@ -196,15 +197,15 @@ ModifierDefinition const& CompilerContext::functionModifier(string const& _name)
for (ModifierDefinition const* modifier: contract->functionModifiers())
if (modifier->name() == _name)
return *modifier;
- BOOST_THROW_EXCEPTION(InternalCompilerError()
- << errinfo_comment("Function modifier " + _name + " not found."));
+ solAssert(false, "Function modifier " + _name + " not found.");
}
unsigned CompilerContext::baseStackOffsetOfVariable(Declaration const& _declaration) const
{
auto res = m_localVariables.find(&_declaration);
solAssert(res != m_localVariables.end(), "Variable not found on stack.");
- return res->second;
+ solAssert(!res->second.empty(), "");
+ return res->second.back();
}
unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
@@ -310,6 +311,7 @@ void CompilerContext::appendInlineAssembly(
if (stackDiff < 1 || stackDiff > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
+ errinfo_sourceLocation(_identifier.location) <<
errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.")
);
if (_context == julia::IdentifierContext::RValue)
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index 1968c1e1..13821f67 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -272,7 +272,10 @@ private:
/// Storage offsets of state variables
std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables;
/// Offsets of local variables on the stack (relative to stack base).
- std::map<Declaration const*, unsigned> m_localVariables;
+ /// This needs to be a stack because if a modifier contains a local variable and this
+ /// modifier is applied twice, the position of the variable needs to be restored
+ /// after the nested modifier is left.
+ std::map<Declaration const*, std::vector<unsigned>> m_localVariables;
/// List of current inheritance hierarchy from derived to base.
std::vector<ContractDefinition const*> m_inheritanceHierarchy;
/// Stack of current visited AST nodes, used for location attachment
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 7067ddd5..782aad9d 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -504,7 +504,7 @@ void CompilerUtils::convertType(
//shift all integer bits onto the left side of the fixed type
FixedPointType const& targetFixedPointType = dynamic_cast<FixedPointType const&>(_targetType);
if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
- if (targetFixedPointType.integerBits() > typeOnStack->numBits())
+ if (targetFixedPointType.numBits() > typeOnStack->numBits())
cleanHigherOrderBits(*typeOnStack);
solUnimplemented("Not yet implemented - FixedPointType.");
}
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index cad388df..fd0998d4 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -267,18 +267,13 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
m_context << notFound;
if (fallback)
{
- m_context.setStackOffset(0);
if (!fallback->isPayable())
appendCallValueCheck();
- // Return tag is used to jump out of the function.
- eth::AssemblyItem returnTag = m_context.pushNewTag();
- fallback->accept(*this);
- m_context << returnTag;
+ solAssert(fallback->isFallback(), "");
solAssert(FunctionType(*fallback).parameterTypes().empty(), "");
solAssert(FunctionType(*fallback).returnParameterTypes().empty(), "");
- // Return tag gets consumed.
- m_context.adjustStackOffset(-1);
+ fallback->accept(*this);
m_context << Instruction::STOP;
}
else
@@ -536,7 +531,8 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
m_context.adjustStackOffset(-(int)c_returnValuesSize);
- if (!_function.isConstructor())
+ /// The constructor and the fallback function doesn't to jump out.
+ if (!_function.isConstructor() && !_function.isFallback())
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
return false;
}
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 82518e8c..02cc62be 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -174,7 +174,12 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
retSizeOnStack = returnTypes.front()->sizeOnStack();
}
solAssert(retSizeOnStack == utils().sizeOnStack(returnTypes), "");
- solAssert(retSizeOnStack <= 15, "Stack is too deep.");
+ if (retSizeOnStack > 15)
+ BOOST_THROW_EXCEPTION(
+ CompilerError() <<
+ errinfo_sourceLocation(_varDecl.location()) <<
+ errinfo_comment("Stack too deep.")
+ );
m_context << dupInstruction(retSizeOnStack + 1);
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
}
@@ -373,8 +378,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
m_context << u256(0) << Instruction::SUB;
break;
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid unary operator: " +
- string(Token::toString(_unaryOperation.getOperator()))));
+ solAssert(false, "Invalid unary operator: " + string(Token::toString(_unaryOperation.getOperator())));
}
return false;
}
@@ -895,7 +899,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break;
}
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type."));
+ solAssert(false, "Invalid function type.");
}
}
return false;
@@ -1061,7 +1065,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
true
);
else
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
+ solAssert(false, "Invalid member access to integer");
break;
case Type::Category::Function:
solAssert(!!_memberAccess.expression().annotation().type->memberType(member),
@@ -1095,7 +1099,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
m_context << u256(0) << Instruction::CALLDATALOAD
<< (u256(0xffffffff) << (256 - 32)) << Instruction::AND;
else
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member."));
+ solAssert(false, "Unknown magic member.");
break;
case Type::Category::Struct:
{
@@ -1172,7 +1176,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
break;
}
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to unknown type."));
+ solAssert(false, "Member access to unknown type.");
}
return false;
}
@@ -1327,7 +1331,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
}
else
{
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
+ solAssert(false, "Identifier type not expected in expression context.");
}
}
@@ -1410,7 +1414,7 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type
m_context << (isSigned ? Instruction::SLT : Instruction::LT);
break;
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown comparison operator."));
+ solAssert(false, "Unknown comparison operator.");
}
}
}
@@ -1422,7 +1426,7 @@ void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator
else if (Token::isBitOp(_operator))
appendBitOperatorCode(_operator);
else
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown binary operator."));
+ solAssert(false, "Unknown binary operator.");
}
void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type)
@@ -1461,7 +1465,7 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty
m_context << Instruction::EXP;
break;
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown arithmetic operator."));
+ solAssert(false, "Unknown arithmetic operator.");
}
}
@@ -1479,7 +1483,7 @@ void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
m_context << Instruction::XOR;
break;
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown bit operator."));
+ solAssert(false, "Unknown bit operator.");
}
}
@@ -1523,7 +1527,7 @@ void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type co
break;
case Token::SHR:
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown shift operator."));
+ solAssert(false, "Unknown shift operator.");
}
}
@@ -1618,7 +1622,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
// zero bytes (which we cannot detect).
solAssert(0 < retSize && retSize <= 32, "");
utils().fetchFreeMemoryPointer();
- m_context << Instruction::DUP1 << u256(0) << Instruction::MSTORE;
+ m_context << u256(0) << Instruction::DUP2 << Instruction::MSTORE;
m_context << u256(32) << Instruction::ADD;
utils().storeFreeMemoryPointer();
}
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index 7e00ffae..76b0bbd5 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -174,14 +174,20 @@ bool AsmAnalyzer::operator()(assembly::Assignment const& _assignment)
bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl)
{
- int const expectedItems = _varDecl.variables.size();
- int const stackHeight = m_stackHeight;
- bool success = boost::apply_visitor(*this, *_varDecl.value);
- if ((m_stackHeight - stackHeight) != expectedItems)
+ bool success = true;
+ int const numVariables = _varDecl.variables.size();
+ if (_varDecl.value)
{
- m_errorReporter.declarationError(_varDecl.location, "Variable count mismatch.");
- return false;
+ int const stackHeight = m_stackHeight;
+ success = boost::apply_visitor(*this, *_varDecl.value);
+ if ((m_stackHeight - stackHeight) != numVariables)
+ {
+ m_errorReporter.declarationError(_varDecl.location, "Variable count mismatch.");
+ return false;
+ }
}
+ else
+ m_stackHeight += numVariables;
for (auto const& variable: _varDecl.variables)
{
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp
index d282a30d..133f70b1 100644
--- a/libsolidity/inlineasm/AsmParser.cpp
+++ b/libsolidity/inlineasm/AsmParser.cpp
@@ -347,10 +347,15 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
else
break;
}
- expectToken(Token::Colon);
- expectToken(Token::Assign);
- varDecl.value.reset(new Statement(parseExpression()));
- varDecl.location.end = locationOf(*varDecl.value).end;
+ if (currentToken() == Token::Colon)
+ {
+ expectToken(Token::Colon);
+ expectToken(Token::Assign);
+ varDecl.value.reset(new Statement(parseExpression()));
+ varDecl.location.end = locationOf(*varDecl.value).end;
+ }
+ else
+ varDecl.location.end = varDecl.variables.back().location.end;
return varDecl;
}
diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp
index 062ff453..4f96a3e9 100644
--- a/libsolidity/inlineasm/AsmPrinter.cpp
+++ b/libsolidity/inlineasm/AsmPrinter.cpp
@@ -128,8 +128,11 @@ string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDecl
),
", "
);
- out += " := ";
- out += boost::apply_visitor(*this, *_variableDeclaration.value);
+ if (_variableDeclaration.value)
+ {
+ out += " := ";
+ out += boost::apply_visitor(*this, *_variableDeclaration.value);
+ }
return out;
}
diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp
index 5b3174b8..b70ae9ac 100644
--- a/libsolidity/inlineasm/AsmScopeFiller.cpp
+++ b/libsolidity/inlineasm/AsmScopeFiller.cpp
@@ -27,6 +27,8 @@
#include <libsolidity/interface/ErrorReporter.h>
#include <libsolidity/interface/Exceptions.h>
+#include <libdevcore/CommonData.h>
+
#include <boost/range/adaptor/reversed.hpp>
#include <memory>
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index e2507821..9689b700 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -87,6 +87,7 @@ void CompilerStack::reset(bool _keepSources)
m_stackState = Empty;
m_sources.clear();
}
+ m_libraries.clear();
m_optimize = false;
m_optimizeRuns = 200;
m_globalContext.reset();
@@ -106,12 +107,6 @@ bool CompilerStack::addSource(string const& _name, string const& _content, bool
return existed;
}
-void CompilerStack::setSource(string const& _sourceCode)
-{
- reset();
- addSource("", _sourceCode);
-}
-
bool CompilerStack::parse()
{
//reset
@@ -252,44 +247,17 @@ bool CompilerStack::analyze()
return false;
}
-bool CompilerStack::parse(string const& _sourceCode)
-{
- setSource(_sourceCode);
- return parse();
-}
-
bool CompilerStack::parseAndAnalyze()
{
return parse() && analyze();
}
-bool CompilerStack::parseAndAnalyze(std::string const& _sourceCode)
-{
- setSource(_sourceCode);
- return parseAndAnalyze();
-}
-
-vector<string> CompilerStack::contractNames() const
-{
- if (m_stackState < AnalysisSuccessful)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
- vector<string> contractNames;
- for (auto const& contract: m_contracts)
- contractNames.push_back(contract.first);
- return contractNames;
-}
-
-
-bool CompilerStack::compile(bool _optimize, unsigned _runs, map<string, h160> const& _libraries)
+bool CompilerStack::compile()
{
if (m_stackState < AnalysisSuccessful)
if (!parseAndAnalyze())
return false;
- m_optimize = _optimize;
- m_optimizeRuns = _runs;
- m_libraries = _libraries;
-
map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
@@ -300,11 +268,6 @@ bool CompilerStack::compile(bool _optimize, unsigned _runs, map<string, h160> co
return true;
}
-bool CompilerStack::compile(string const& _sourceCode, bool _optimize, unsigned _runs)
-{
- return parseAndAnalyze(_sourceCode) && compile(_optimize, _runs);
-}
-
void CompilerStack::link()
{
for (auto& contract: m_contracts)
@@ -315,6 +278,16 @@ void CompilerStack::link()
}
}
+vector<string> CompilerStack::contractNames() const
+{
+ if (m_stackState < AnalysisSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+ vector<string> contractNames;
+ for (auto const& contract: m_contracts)
+ contractNames.push_back(contract.first);
+ return contractNames;
+}
+
eth::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const
{
Contract const& currentContract = contract(_contractName);
@@ -451,18 +424,20 @@ Json::Value const& CompilerStack::natspec(Contract const& _contract, Documentati
{
case DocumentationType::NatspecUser:
doc = &_contract.userDocumentation;
+ // caches the result
+ if (!*doc)
+ doc->reset(new Json::Value(Natspec::userDocumentation(*_contract.contract)));
break;
case DocumentationType::NatspecDev:
doc = &_contract.devDocumentation;
+ // caches the result
+ if (!*doc)
+ doc->reset(new Json::Value(Natspec::devDocumentation(*_contract.contract)));
break;
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
+ solAssert(false, "Illegal documentation type.");
}
- // caches the result
- if (!*doc)
- doc->reset(new Json::Value(Natspec::documentation(*_contract.contract, _type)));
-
return *(*doc);
}
@@ -474,12 +449,12 @@ Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
return methodIdentifiers;
}
-string const& CompilerStack::onChainMetadata(string const& _contractName) const
+string const& CompilerStack::metadata(string const& _contractName) const
{
if (m_stackState != CompilationSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
- return contract(_contractName).onChainMetadata;
+ return contract(_contractName).metadata;
}
Scanner const& CompilerStack::scanner(string const& _sourceName) const
@@ -673,11 +648,11 @@ void CompilerStack::compileContract(
shared_ptr<Compiler> compiler = make_shared<Compiler>(m_optimize, m_optimizeRuns);
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
- string onChainMetadata = createOnChainMetadata(compiledContract);
+ string metadata = createMetadata(compiledContract);
bytes cborEncodedMetadata =
- // CBOR-encoding of {"bzzr0": dev::swarmHash(onChainMetadata)}
+ // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata)}
bytes{0xa1, 0x65, 'b', 'z', 'z', 'r', '0', 0x58, 0x20} +
- dev::swarmHash(onChainMetadata).asBytes();
+ dev::swarmHash(metadata).asBytes();
solAssert(cborEncodedMetadata.size() <= 0xffff, "Metadata too large");
// 16-bit big endian length
cborEncodedMetadata += toCompactBigEndian(cborEncodedMetadata.size(), 2);
@@ -690,11 +665,11 @@ void CompilerStack::compileContract(
}
catch(eth::OptimizerException const&)
{
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Assembly optimizer exception for bytecode"));
+ solAssert(false, "Assembly optimizer exception for bytecode");
}
catch(eth::AssemblyException const&)
{
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Assembly exception for bytecode"));
+ solAssert(false, "Assembly exception for bytecode");
}
try
@@ -703,14 +678,14 @@ void CompilerStack::compileContract(
}
catch(eth::OptimizerException const&)
{
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Assembly optimizer exception for deployed bytecode"));
+ solAssert(false, "Assembly optimizer exception for deployed bytecode");
}
catch(eth::AssemblyException const&)
{
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Assembly exception for deployed bytecode"));
+ solAssert(false, "Assembly exception for deployed bytecode");
}
- compiledContract.onChainMetadata = onChainMetadata;
+ compiledContract.metadata = metadata;
_compiledContracts[compiledContract.contract] = &compiler->assembly();
try
@@ -771,16 +746,25 @@ CompilerStack::Source const& CompilerStack::source(string const& _sourceName) co
return it->second;
}
-string CompilerStack::createOnChainMetadata(Contract const& _contract) const
+string CompilerStack::createMetadata(Contract const& _contract) const
{
Json::Value meta;
meta["version"] = 1;
meta["language"] = "Solidity";
meta["compiler"]["version"] = VersionStringStrict;
+ /// All the source files (including self), which should be included in the metadata.
+ set<string> referencedSources;
+ referencedSources.insert(_contract.contract->sourceUnit().annotation().path);
+ for (auto const sourceUnit: _contract.contract->sourceUnit().referencedSourceUnits(true))
+ referencedSources.insert(sourceUnit->annotation().path);
+
meta["sources"] = Json::objectValue;
for (auto const& s: m_sources)
{
+ if (!referencedSources.count(s.first))
+ continue;
+
solAssert(s.second.scanner, "Scanner not available");
meta["sources"][s.first]["keccak256"] =
"0x" + toHex(dev::keccak256(s.second.scanner->source()).asBytes());
@@ -951,7 +935,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
for (auto const& it: contract.definedFunctions())
{
/// Exclude externally visible functions, constructor and the fallback function
- if (it->isPartOfExternalInterface() || it->isConstructor() || it->name().empty())
+ if (it->isPartOfExternalInterface() || it->isConstructor() || it->isFallback())
continue;
size_t entry = functionEntryPoint(_contractName, *it);
@@ -959,12 +943,14 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
if (entry > 0)
gas = GasEstimator::functionalEstimation(*items, entry, *it);
+ /// TODO: This could move into a method shared with externalSignature()
FunctionType type(*it);
string sig = it->name() + "(";
auto paramTypes = type.parameterTypes();
for (auto it = paramTypes.begin(); it != paramTypes.end(); ++it)
sig += (*it)->toString() + (it + 1 == paramTypes.end() ? "" : ",");
sig += ")";
+
internalFunctions[sig] = gasToJson(gas);
}
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index 03a1b806..d287f224 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -93,87 +93,116 @@ public:
m_errorList(),
m_errorReporter(m_errorList) {}
- /// Sets path remappings in the format "context:prefix=target"
- void setRemappings(std::vector<std::string> const& _remappings);
+ /// @returns the list of errors that occured during parsing and type checking.
+ ErrorList const& errors() { return m_errorReporter.errors(); }
+
+ /// @returns the current state.
+ State state() const { return m_stackState; }
/// Resets the compiler to a state where the sources are not parsed or even removed.
+ /// Sets the state to SourcesSet if @a _keepSources is true, otherwise to Empty.
+ /// All settings, with the exception of remappings, are reset.
void reset(bool _keepSources = false);
- /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
- /// @returns true if a source object by the name already existed and was replaced.
- void addSources(StringMap const& _nameContents, bool _isLibrary = false)
+ /// Sets path remappings in the format "context:prefix=target"
+ void setRemappings(std::vector<std::string> const& _remappings);
+
+ /// Sets library addresses. Addresses are cleared iff @a _libraries is missing.
+ /// Will not take effect before running compile.
+ void setLibraries(std::map<std::string, h160> const& _libraries = std::map<std::string, h160>{})
{
- for (auto const& i: _nameContents) addSource(i.first, i.second, _isLibrary);
+ m_libraries = _libraries;
}
+
+ /// Changes the optimiser settings.
+ /// Will not take effect before running compile.
+ void setOptimiserSettings(bool _optimize, unsigned _runs = 200)
+ {
+ m_optimize = _optimize;
+ m_optimizeRuns = _runs;
+ }
+
+ /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
+ /// @returns true if a source object by the name already existed and was replaced.
bool addSource(std::string const& _name, std::string const& _content, bool _isLibrary = false);
- void setSource(std::string const& _sourceCode);
+
/// Parses all source units that were added
/// @returns false on error.
bool parse();
- /// Sets the given source code as the only source unit apart from standard sources and parses it.
- /// @returns false on error.
- bool parse(std::string const& _sourceCode);
- /// performs the analyisis steps (imports, scopesetting, syntaxCheck, referenceResolving,
+
+ /// Performs the analysis steps (imports, scopesetting, syntaxCheck, referenceResolving,
/// typechecking, staticAnalysis) on previously set sources
/// @returns false on error.
bool analyze();
+
/// Parses and analyzes all source units that were added
/// @returns false on error.
bool parseAndAnalyze();
- /// Sets the given source code as the only source unit apart from standard sources and parses and analyzes it.
- /// @returns false on error.
- bool parseAndAnalyze(std::string const& _sourceCode);
+
/// @returns a list of the contract names in the sources.
std::vector<std::string> contractNames() const;
/// Compiles the source units that were previously added and parsed.
/// @returns false on error.
- bool compile(
- bool _optimize = false,
- unsigned _runs = 200,
- std::map<std::string, h160> const& _libraries = std::map<std::string, h160>{}
- );
- /// Parses and compiles the given source code.
- /// @returns false on error.
- bool compile(std::string const& _sourceCode, bool _optimize = false, unsigned _runs = 200);
+ bool compile();
+
+ /// @returns the list of sources (paths) used
+ std::vector<std::string> sourceNames() const;
+
+ /// @returns a mapping assigning each source name its index inside the vector returned
+ /// by sourceNames().
+ std::map<std::string, unsigned> sourceIndices() const;
+
+ /// @returns the previously used scanner, useful for counting lines during error reporting.
+ Scanner const& scanner(std::string const& _sourceName = "") const;
+
+ /// @returns the parsed source unit with the supplied name.
+ SourceUnit const& ast(std::string const& _sourceName = "") const;
+
+ /// Helper function for logs printing. Do only use in error cases, it's quite expensive.
+ /// line and columns are numbered starting from 1 with following order:
+ /// start line, start column, end line, end column
+ std::tuple<int, int, int, int> positionFromSourceLocation(SourceLocation const& _sourceLocation) const;
+
+ /// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use
+ std::string const filesystemFriendlyName(std::string const& _contractName) const;
/// @returns the assembled object for a contract.
eth::LinkerObject const& object(std::string const& _contractName = "") const;
+
/// @returns the runtime object for the contract.
eth::LinkerObject const& runtimeObject(std::string const& _contractName = "") const;
+
/// @returns the bytecode of a contract that uses an already deployed contract via DELEGATECALL.
/// The returned bytes will contain a sequence of 20 bytes of the format "XXX...XXX" which have to
/// substituted by the actual address. Note that this sequence starts end ends in three X
/// characters but can contain anything in between.
eth::LinkerObject const& cloneObject(std::string const& _contractName = "") const;
+
/// @returns normal contract assembly items
eth::AssemblyItems const* assemblyItems(std::string const& _contractName = "") const;
+
/// @returns runtime contract assembly items
eth::AssemblyItems const* runtimeAssemblyItems(std::string const& _contractName = "") const;
+
/// @returns the string that provides a mapping between bytecode and sourcecode or a nullptr
/// if the contract does not (yet) have bytecode.
std::string const* sourceMapping(std::string const& _contractName = "") const;
+
/// @returns the string that provides a mapping between runtime bytecode and sourcecode.
/// if the contract does not (yet) have bytecode.
std::string const* runtimeSourceMapping(std::string const& _contractName = "") const;
- /// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use
- std::string const filesystemFriendlyName(std::string const& _contractName) const;
-
/// Streams a verbose version of the assembly to @a _outStream.
/// @arg _sourceCodes is the map of input files to source code strings
/// @arg _inJsonFromat shows whether the out should be in Json format
/// Prerequisite: Successful compilation.
Json::Value streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap(), bool _inJsonFormat = false) const;
- /// @returns the list of sources (paths) used
- std::vector<std::string> sourceNames() const;
- /// @returns a mapping assigning each source name its index inside the vector returned
- /// by sourceNames().
- std::map<std::string, unsigned> sourceIndices() const;
/// @returns a JSON representing the contract ABI.
/// Prerequisite: Successful call to parse or compile.
Json::Value const& contractABI(std::string const& _contractName = "") const;
+
/// @returns a JSON representing the contract's documentation.
/// Prerequisite: Successful call to parse or compile.
/// @param type The type of the documentation to get.
@@ -183,27 +212,13 @@ public:
/// @returns a JSON representing a map of method identifiers (hashes) to function names.
Json::Value methodIdentifiers(std::string const& _contractName) const;
- std::string const& onChainMetadata(std::string const& _contractName) const;
+ /// @returns the Contract Metadata
+ std::string const& metadata(std::string const& _contractName) const;
void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; }
/// @returns a JSON representing the estimated gas usage for contract creation, internal and external functions
Json::Value gasEstimates(std::string const& _contractName) const;
- /// @returns the previously used scanner, useful for counting lines during error reporting.
- Scanner const& scanner(std::string const& _sourceName = "") const;
- /// @returns the parsed source unit with the supplied name.
- SourceUnit const& ast(std::string const& _sourceName = "") const;
-
- /// Helper function for logs printing. Do only use in error cases, it's quite expensive.
- /// line and columns are numbered starting from 1 with following order:
- /// start line, start column, end line, end column
- std::tuple<int, int, int, int> positionFromSourceLocation(SourceLocation const& _sourceLocation) const;
-
- /// @returns the list of errors that occured during parsing and type checking.
- ErrorList const& errors() { return m_errorReporter.errors(); }
-
- State state() const { return m_stackState; }
-
private:
/**
* Information pertaining to one source unit, filled gradually during parsing and compilation.
@@ -223,13 +238,14 @@ private:
eth::LinkerObject object;
eth::LinkerObject runtimeObject;
eth::LinkerObject cloneObject;
- std::string onChainMetadata; ///< The metadata json that will be hashed into the chain.
+ std::string metadata; ///< The metadata json that will be hashed into the chain.
mutable std::unique_ptr<Json::Value const> abi;
mutable std::unique_ptr<Json::Value const> userDocumentation;
mutable std::unique_ptr<Json::Value const> devDocumentation;
mutable std::unique_ptr<std::string const> sourceMapping;
mutable std::unique_ptr<std::string const> runtimeSourceMapping;
};
+
/// Loads the missing sources from @a _ast (named @a _path) using the callback
/// @a m_readFile and stores the absolute paths of all imports in the AST annotations.
/// @returns the newly loaded sources.
@@ -255,7 +271,7 @@ private:
/// does not exist.
ContractDefinition const& contractDefinition(std::string const& _contractName) const;
- std::string createOnChainMetadata(Contract const& _contract) const;
+ std::string createMetadata(Contract const& _contract) const;
std::string computeSourceMapping(eth::AssemblyItems const& _items) const;
Json::Value const& contractABI(Contract const&) const;
Json::Value const& natspec(Contract const&, DocumentationType _type) const;
diff --git a/libsolidity/interface/ErrorReporter.cpp b/libsolidity/interface/ErrorReporter.cpp
index 6e2667a5..f9ef4ceb 100644
--- a/libsolidity/interface/ErrorReporter.cpp
+++ b/libsolidity/interface/ErrorReporter.cpp
@@ -42,11 +42,23 @@ void ErrorReporter::warning(string const& _description)
error(Error::Type::Warning, SourceLocation(), _description);
}
-void ErrorReporter::warning(SourceLocation const& _location, string const& _description)
+void ErrorReporter::warning(
+ SourceLocation const& _location,
+ string const& _description
+)
{
error(Error::Type::Warning, _location, _description);
}
+void ErrorReporter::warning(
+ SourceLocation const& _location,
+ string const& _description,
+ SecondarySourceLocation const& _secondaryLocation
+)
+{
+ error(Error::Type::Warning, _location, _secondaryLocation, _description);
+}
+
void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, string const& _description)
{
auto err = make_shared<Error>(_type);
diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h
index e5605d24..8b066a3e 100644
--- a/libsolidity/interface/ErrorReporter.h
+++ b/libsolidity/interface/ErrorReporter.h
@@ -41,30 +41,30 @@ public:
ErrorReporter& operator=(ErrorReporter const& _errorReporter);
- void warning(std::string const& _description = std::string());
+ void warning(std::string const& _description);
+
+ void warning(SourceLocation const& _location, std::string const& _description);
void warning(
- SourceLocation const& _location = SourceLocation(),
- std::string const& _description = std::string()
+ SourceLocation const& _location,
+ std::string const& _description,
+ SecondarySourceLocation const& _secondaryLocation
);
void error(
Error::Type _type,
- SourceLocation const& _location = SourceLocation(),
- std::string const& _description = std::string()
- );
-
- void declarationError(
SourceLocation const& _location,
- SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(),
- std::string const& _description = std::string()
+ std::string const& _description
);
void declarationError(
SourceLocation const& _location,
- std::string const& _description = std::string()
+ SecondarySourceLocation const& _secondaryLocation,
+ std::string const& _description
);
+ void declarationError(SourceLocation const& _location, std::string const& _description);
+
void fatalDeclarationError(SourceLocation const& _location, std::string const& _description);
void parserError(SourceLocation const& _location, std::string const& _description);
diff --git a/libsolidity/interface/Exceptions.cpp b/libsolidity/interface/Exceptions.cpp
index 9f2a2d06..a837dce6 100644
--- a/libsolidity/interface/Exceptions.cpp
+++ b/libsolidity/interface/Exceptions.cpp
@@ -67,16 +67,3 @@ Error::Error(Error::Type _type, const std::string& _description, const SourceLoc
*this << errinfo_sourceLocation(_location);
*this << errinfo_comment(_description);
}
-
-string Exception::lineInfo() const
-{
- char const* const* file = boost::get_error_info<boost::throw_file>(*this);
- int const* line = boost::get_error_info<boost::throw_line>(*this);
- string ret;
- if (file)
- ret += *file;
- ret += ':';
- if (line)
- ret += boost::lexical_cast<string>(*line);
- return ret;
-}
diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp
index 70486e23..7f7084ef 100644
--- a/libsolidity/interface/Natspec.cpp
+++ b/libsolidity/interface/Natspec.cpp
@@ -26,28 +26,11 @@
#include <libsolidity/interface/Natspec.h>
#include <boost/range/irange.hpp>
#include <libsolidity/ast/AST.h>
-#include <libsolidity/interface/CompilerStack.h>
using namespace std;
using namespace dev;
using namespace dev::solidity;
-Json::Value Natspec::documentation(
- ContractDefinition const& _contractDef,
- DocumentationType _type
-)
-{
- switch(_type)
- {
- case DocumentationType::NatspecUser:
- return userDocumentation(_contractDef);
- case DocumentationType::NatspecDev:
- return devDocumentation(_contractDef);
- }
-
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
-}
-
Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
{
Json::Value doc;
diff --git a/libsolidity/interface/Natspec.h b/libsolidity/interface/Natspec.h
index bec9acd2..9ac3efea 100644
--- a/libsolidity/interface/Natspec.h
+++ b/libsolidity/interface/Natspec.h
@@ -39,7 +39,6 @@ class ContractDefinition;
class Type;
using TypePointer = std::shared_ptr<Type const>;
struct DocTag;
-enum class DocumentationType: uint8_t;
enum class DocTagType: uint8_t
{
@@ -61,15 +60,6 @@ enum class CommentOwner
class Natspec
{
public:
- /// Get the given type of documentation
- /// @param _contractDef The contract definition
- /// @param _type The type of the documentation. Can be one of the
- /// types provided by @c DocumentationType
- /// @return A JSON representation of provided type
- static Json::Value documentation(
- ContractDefinition const& _contractDef,
- DocumentationType _type
- );
/// Get the User documentation of the contract
/// @param _contractDef The contract definition
/// @return A JSON representation of the contract's user documentation
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
index 15bb7592..dd135ce5 100644
--- a/libsolidity/interface/StandardCompiler.cpp
+++ b/libsolidity/interface/StandardCompiler.cpp
@@ -247,8 +247,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.setRemappings(remappings);
Json::Value optimizerSettings = settings.get("optimizer", Json::Value());
- bool optimize = optimizerSettings.get("enabled", Json::Value(false)).asBool();
- unsigned optimizeRuns = optimizerSettings.get("runs", Json::Value(200u)).asUInt();
+ bool const optimize = optimizerSettings.get("enabled", Json::Value(false)).asBool();
+ unsigned const optimizeRuns = optimizerSettings.get("runs", Json::Value(200u)).asUInt();
+ m_compilerStack.setOptimiserSettings(optimize, optimizeRuns);
map<string, h160> libraries;
Json::Value jsonLibraries = settings.get("libraries", Json::Value());
@@ -259,6 +260,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
// @TODO use libraries only for the given source
libraries[library] = h160(jsonSourceName[library].asString());
}
+ m_compilerStack.setLibraries(libraries);
Json::Value metadataSettings = settings.get("metadata", Json::Value());
m_compilerStack.useMetadataLiteralSources(metadataSettings.get("useLiteralContent", Json::Value(false)).asBool());
@@ -267,7 +269,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
try
{
- m_compilerStack.compile(optimize, optimizeRuns, libraries);
+ m_compilerStack.compile();
for (auto const& error: m_compilerStack.errors())
{
@@ -283,24 +285,27 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
));
}
}
+ /// This is only thrown in a very few locations.
catch (Error const& _error)
{
- if (_error.type() == Error::Type::DocstringParsingError)
- errors.append(formatError(
- false,
- "DocstringParsingError",
- "general",
- "Documentation parsing error: " + *boost::get_error_info<errinfo_comment>(_error)
- ));
- else
- errors.append(formatErrorWithException(
- _error,
- false,
- _error.typeName(),
- "general",
- "",
- scannerFromSourceName
- ));
+ errors.append(formatErrorWithException(
+ _error,
+ false,
+ _error.typeName(),
+ "general",
+ "Uncaught error: ",
+ scannerFromSourceName
+ ));
+ }
+ /// This should not be leaked from compile().
+ catch (FatalError const& _exception)
+ {
+ errors.append(formatError(
+ false,
+ "FatalError",
+ "general",
+ "Uncaught fatal error: " + boost::diagnostic_information(_exception)
+ ));
}
catch (CompilerError const& _exception)
{
@@ -320,7 +325,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
false,
"InternalCompilerError",
"general",
- "Internal compiler error (" + _exception.lineInfo() + ")", scannerFromSourceName
+ "Internal compiler error (" + _exception.lineInfo() + ")",
+ scannerFromSourceName
));
}
catch (UnimplementedFeatureError const& _exception)
@@ -331,7 +337,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
"UnimplementedFeatureError",
"general",
"Unimplemented feature (" + _exception.lineInfo() + ")",
- scannerFromSourceName));
+ scannerFromSourceName
+ ));
}
catch (Exception const& _exception)
{
@@ -352,27 +359,27 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
));
}
- Json::Value output = Json::objectValue;
-
- if (errors.size() > 0)
- output["errors"] = errors;
-
- bool analysisSuccess = m_compilerStack.state() >= CompilerStack::State::AnalysisSuccessful;
- bool compilationSuccess = m_compilerStack.state() == CompilerStack::State::CompilationSuccessful;
+ bool const analysisSuccess = m_compilerStack.state() >= CompilerStack::State::AnalysisSuccessful;
+ bool const compilationSuccess = m_compilerStack.state() == CompilerStack::State::CompilationSuccessful;
/// Inconsistent state - stop here to receive error reports from users
if (!compilationSuccess && (errors.size() == 0))
return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");
+ Json::Value output = Json::objectValue;
+
+ if (errors.size() > 0)
+ output["errors"] = errors;
+
output["sources"] = Json::objectValue;
unsigned sourceIndex = 0;
- for (auto const& source: analysisSuccess ? m_compilerStack.sourceNames() : vector<string>())
+ for (string const& sourceName: analysisSuccess ? m_compilerStack.sourceNames() : vector<string>())
{
Json::Value sourceResult = Json::objectValue;
sourceResult["id"] = sourceIndex++;
- sourceResult["ast"] = ASTJsonConverter(false, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(source));
- sourceResult["legacyAST"] = ASTJsonConverter(true, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(source));
- output["sources"][source] = sourceResult;
+ sourceResult["ast"] = ASTJsonConverter(false, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(sourceName));
+ sourceResult["legacyAST"] = ASTJsonConverter(true, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(sourceName));
+ output["sources"][sourceName] = sourceResult;
}
Json::Value contractsOutput = Json::objectValue;
@@ -386,7 +393,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
// ABI, documentation and metadata
Json::Value contractData(Json::objectValue);
contractData["abi"] = m_compilerStack.contractABI(contractName);
- contractData["metadata"] = m_compilerStack.onChainMetadata(contractName);
+ contractData["metadata"] = m_compilerStack.metadata(contractName);
contractData["userdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecUser);
contractData["devdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecDev);
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index b0cf364e..b98991f3 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -87,7 +87,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
nodes.push_back(parseContractDefinition(token));
break;
default:
- fatalParserError(string("Expected import directive or contract definition."));
+ fatalParserError(string("Expected pragma, import directive or contract/interface/library definition."));
}
}
return nodeFactory.createNode<SourceUnit>(nodes);
diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp
index 66312f69..9cec0303 100644
--- a/libsolidity/parsing/Token.cpp
+++ b/libsolidity/parsing/Token.cpp
@@ -70,7 +70,7 @@ void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned con
else if (_baseType == Token::UFixedMxN || _baseType == Token::FixedMxN)
{
solAssert(
- _first + _second <= 256 && _first % 8 == 0 && _second % 8 == 0,
+ _first >= 8 && _first <= 256 && _first % 8 == 0 && _second <= 80,
"No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "x" + to_string(_second) + "."
);
}
@@ -157,12 +157,8 @@ tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(s
) {
int n = parseSize(positionX + 1, _literal.end());
if (
- 0 <= m && m <= 256 &&
- 8 <= n && n <= 256 &&
- m + n > 0 &&
- m + n <= 256 &&
- m % 8 == 0 &&
- n % 8 == 0
+ 8 <= m && m <= 256 && m % 8 == 0 &&
+ 0 <= n && n <= 80
) {
if (keyword == Token::UFixed)
return make_tuple(Token::UFixedMxN, m, n);
diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h
index 39c0eff9..d412b3f0 100644
--- a/libsolidity/parsing/Token.h
+++ b/libsolidity/parsing/Token.h
@@ -44,7 +44,7 @@
#include <libdevcore/Common.h>
#include <libsolidity/interface/Exceptions.h>
-#include <libdevcore/UndefMacros.h>
+#include <libsolidity/parsing/UndefMacros.h>
namespace dev
{
diff --git a/libsolidity/parsing/UndefMacros.h b/libsolidity/parsing/UndefMacros.h
new file mode 100644
index 00000000..d96e242e
--- /dev/null
+++ b/libsolidity/parsing/UndefMacros.h
@@ -0,0 +1,46 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+/** @file UndefMacros.h
+ * @author Lefteris <lefteris@ethdev.com>
+ * @date 2015
+ *
+ * This header should be used to #undef some really evil macros defined by
+ * windows.h which result in conflict with our Token.h
+ */
+#pragma once
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+
+#undef DELETE
+#undef IN
+#undef VOID
+#undef THIS
+#undef CONST
+
+// Conflicting define on MinGW in windows.h
+// windows.h(19): #define interface struct
+#ifdef interface
+#undef interface
+#endif
+
+#elif defined(DELETE) || defined(IN) || defined(VOID) || defined(THIS) || defined(CONST) || defined(interface)
+
+#error "The preceding macros in this header file are reserved for V8's "\
+"TOKEN_LIST. Please add a platform specific define above to undefine "\
+"overlapping macros."
+
+#endif