aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/analysis/GlobalContext.cpp34
-rw-r--r--libsolidity/analysis/PostTypeChecker.cpp3
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp44
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp52
-rw-r--r--libsolidity/analysis/StaticAnalyzer.h9
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp17
-rw-r--r--libsolidity/analysis/SyntaxChecker.h4
-rw-r--r--libsolidity/analysis/TypeChecker.cpp175
-rw-r--r--libsolidity/analysis/TypeChecker.h1
-rw-r--r--libsolidity/ast/AST.h12
-rw-r--r--libsolidity/ast/ASTAnnotations.h20
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp45
-rw-r--r--libsolidity/ast/ASTJsonConverter.h8
-rw-r--r--libsolidity/ast/Types.cpp194
-rw-r--r--libsolidity/ast/Types.h21
-rw-r--r--libsolidity/codegen/CompilerContext.cpp27
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp8
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp176
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp158
-rw-r--r--libsolidity/codegen/LValue.cpp4
-rw-r--r--libsolidity/formal/Why3Translator.cpp14
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp372
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.h119
-rw-r--r--libsolidity/inlineasm/AsmAnalysisInfo.cpp26
-rw-r--r--libsolidity/inlineasm/AsmAnalysisInfo.h61
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.cpp202
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.h33
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp16
-rw-r--r--libsolidity/inlineasm/AsmParser.h5
-rw-r--r--libsolidity/inlineasm/AsmPrinter.cpp2
-rw-r--r--libsolidity/inlineasm/AsmScope.cpp79
-rw-r--r--libsolidity/inlineasm/AsmScope.h128
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.cpp130
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.h89
-rw-r--r--libsolidity/inlineasm/AsmStack.cpp25
-rw-r--r--libsolidity/inlineasm/AsmStack.h29
-rw-r--r--libsolidity/interface/CompilerStack.cpp190
-rw-r--r--libsolidity/interface/CompilerStack.h39
-rw-r--r--libsolidity/interface/Exceptions.cpp12
-rw-r--r--libsolidity/interface/ReadFile.h45
-rw-r--r--libsolidity/interface/SourceReferenceFormatter.h11
-rw-r--r--libsolidity/interface/StandardCompiler.cpp482
-rw-r--r--libsolidity/interface/StandardCompiler.h63
-rw-r--r--libsolidity/parsing/Parser.cpp26
-rw-r--r--libsolidity/parsing/Parser.h3
-rw-r--r--libsolidity/parsing/Token.h2
46 files changed, 2479 insertions, 736 deletions
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp
index d8f1603a..a54b8c8d 100644
--- a/libsolidity/analysis/GlobalContext.cpp
+++ b/libsolidity/analysis/GlobalContext.cpp
@@ -39,39 +39,39 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)),
make_shared<MagicVariableDeclaration>("now", make_shared<IntegerType>(256)),
make_shared<MagicVariableDeclaration>("suicide",
- make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Selfdestruct)),
+ make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)),
make_shared<MagicVariableDeclaration>("selfdestruct",
- make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Selfdestruct)),
+ make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)),
make_shared<MagicVariableDeclaration>("addmod",
- make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Location::AddMod)),
+ make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod)),
make_shared<MagicVariableDeclaration>("mulmod",
- make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Location::MulMod)),
+ make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod)),
make_shared<MagicVariableDeclaration>("sha3",
- make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA3, true)),
+ make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true)),
make_shared<MagicVariableDeclaration>("keccak256",
- make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA3, true)),
+ make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true)),
make_shared<MagicVariableDeclaration>("log0",
- make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Location::Log0)),
+ make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)),
make_shared<MagicVariableDeclaration>("log1",
- make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Location::Log1)),
+ make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)),
make_shared<MagicVariableDeclaration>("log2",
- make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log2)),
+ make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log2)),
make_shared<MagicVariableDeclaration>("log3",
- make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log3)),
+ make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log3)),
make_shared<MagicVariableDeclaration>("log4",
- make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log4)),
+ make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log4)),
make_shared<MagicVariableDeclaration>("sha256",
- make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA256, true)),
+ make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA256, true)),
make_shared<MagicVariableDeclaration>("ecrecover",
- make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)),
+ make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover)),
make_shared<MagicVariableDeclaration>("ripemd160",
- make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true)),
+ make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Kind::RIPEMD160, true)),
make_shared<MagicVariableDeclaration>("assert",
- make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Location::Assert)),
+ make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Assert)),
make_shared<MagicVariableDeclaration>("require",
- make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Location::Require)),
+ make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Require)),
make_shared<MagicVariableDeclaration>("revert",
- make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Revert))})
+ make_shared<FunctionType>(strings(), strings(), FunctionType::Kind::Revert))})
{
}
diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp
index cae77c74..e8da3ca4 100644
--- a/libsolidity/analysis/PostTypeChecker.cpp
+++ b/libsolidity/analysis/PostTypeChecker.cpp
@@ -58,6 +58,9 @@ void PostTypeChecker::endVisit(ContractDefinition const&)
for (auto declaration: m_constVariables)
if (auto identifier = findCycle(declaration))
typeError(declaration->location(), "The value of the constant " + declaration->name() + " has a cyclic dependency via " + identifier->name() + ".");
+
+ m_constVariables.clear();
+ m_constVariableDependencies.clear();
}
bool PostTypeChecker::visit(VariableDeclaration const& _variable)
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index 37bcb2d9..9433976a 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -25,9 +25,12 @@
#include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/analysis/ConstantEvaluator.h>
-#include <libsolidity/inlineasm/AsmCodeGen.h>
+#include <libsolidity/inlineasm/AsmAnalysis.h>
+#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <libsolidity/inlineasm/AsmData.h>
+#include <boost/algorithm/string.hpp>
+
using namespace std;
using namespace dev;
using namespace dev::solidity;
@@ -158,21 +161,40 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
{
- // We need to perform a full code generation pass here as inline assembly does not distinguish
- // reference resolution and code generation.
// Errors created in this stage are completely ignored because we do not yet know
// the type and size of external identifiers, which would result in false errors.
+ // The only purpose of this step is to fill the inline assembly annotation with
+ // external references.
ErrorList errorsIgnored;
- assembly::CodeGenerator codeGen(_inlineAssembly.operations(), errorsIgnored);
- codeGen.typeCheck([&](assembly::Identifier const& _identifier, eth::Assembly&, assembly::CodeGenerator::IdentifierContext) {
+ assembly::ExternalIdentifierAccess::Resolver resolver =
+ [&](assembly::Identifier const& _identifier, assembly::IdentifierContext) {
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name);
+ bool isSlot = boost::algorithm::ends_with(_identifier.name, "_slot");
+ bool isOffset = boost::algorithm::ends_with(_identifier.name, "_offset");
+ if (isSlot || isOffset)
+ {
+ // special mode to access storage variables
+ if (!declarations.empty())
+ // the special identifier exists itself, we should not allow that.
+ return size_t(-1);
+ string realName = _identifier.name.substr(0, _identifier.name.size() - (
+ isSlot ?
+ string("_slot").size() :
+ string("_offset").size()
+ ));
+ declarations = m_resolver.nameFromCurrentScope(realName);
+ }
if (declarations.size() != 1)
- return false;
- _inlineAssembly.annotation().externalReferences[&_identifier] = declarations.front();
- // At this stage we neither know the code to generate nor the stack size of the identifier,
- // so we do not modify assembly.
- return true;
- });
+ return size_t(-1);
+ _inlineAssembly.annotation().externalReferences[&_identifier].isSlot = isSlot;
+ _inlineAssembly.annotation().externalReferences[&_identifier].isOffset = isOffset;
+ _inlineAssembly.annotation().externalReferences[&_identifier].declaration = declarations.front();
+ return size_t(1);
+ };
+
+ // Will be re-generated later with correct information
+ assembly::AsmAnalysisInfo analysisInfo;
+ assembly::AsmAnalyzer(analysisInfo, errorsIgnored, resolver).analyze(_inlineAssembly.operations());
return false;
}
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index c39f874e..369376fa 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -48,13 +48,65 @@ void StaticAnalyzer::endVisit(ContractDefinition const&)
bool StaticAnalyzer::visit(FunctionDefinition const& _function)
{
+ if (_function.isImplemented())
+ m_currentFunction = &_function;
+ else
+ solAssert(!m_currentFunction, "");
+ solAssert(m_localVarUseCount.empty(), "");
m_nonPayablePublic = _function.isPublic() && !_function.isPayable();
return true;
}
void StaticAnalyzer::endVisit(FunctionDefinition const&)
{
+ m_currentFunction = nullptr;
m_nonPayablePublic = false;
+ for (auto const& var: m_localVarUseCount)
+ if (var.second == 0)
+ warning(var.first->location(), "Unused local variable");
+ m_localVarUseCount.clear();
+}
+
+bool StaticAnalyzer::visit(Identifier const& _identifier)
+{
+ if (m_currentFunction)
+ if (auto var = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
+ {
+ solAssert(!var->name().empty(), "");
+ if (var->isLocalVariable())
+ m_localVarUseCount[var] += 1;
+ }
+ return true;
+}
+
+bool StaticAnalyzer::visit(VariableDeclaration const& _variable)
+{
+ if (m_currentFunction)
+ {
+ solAssert(_variable.isLocalVariable(), "");
+ if (_variable.name() != "")
+ // This is not a no-op, the entry might pre-exist.
+ m_localVarUseCount[&_variable] += 0;
+ }
+ return true;
+}
+
+bool StaticAnalyzer::visit(Return const& _return)
+{
+ // If the return has an expression, it counts as
+ // a "use" of the return parameters.
+ if (m_currentFunction && _return.expression())
+ for (auto const& var: m_currentFunction->returnParameters())
+ if (!var->name().empty())
+ m_localVarUseCount[var.get()] += 1;
+ return true;
+}
+
+bool StaticAnalyzer::visit(ExpressionStatement const& _statement)
+{
+ if (_statement.expression().annotation().isPure)
+ warning(_statement.location(), "Statement has no effect.");
+ return true;
}
bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h
index 0cb961bd..ab72e7d9 100644
--- a/libsolidity/analysis/StaticAnalyzer.h
+++ b/libsolidity/analysis/StaticAnalyzer.h
@@ -60,6 +60,10 @@ private:
virtual bool visit(FunctionDefinition const& _function) override;
virtual void endVisit(FunctionDefinition const& _function) override;
+ virtual bool visit(ExpressionStatement const& _statement) override;
+ virtual bool visit(VariableDeclaration const& _variable) override;
+ virtual bool visit(Identifier const& _identifier) override;
+ virtual bool visit(Return const& _return) override;
virtual bool visit(MemberAccess const& _memberAccess) override;
ErrorList& m_errors;
@@ -69,6 +73,11 @@ private:
/// Flag that indicates whether a public function does not contain the "payable" modifier.
bool m_nonPayablePublic = false;
+
+ /// Number of uses of each (named) local variable in a function, counter is initialized with zero.
+ std::map<VariableDeclaration const*, int> m_localVarUseCount;
+
+ FunctionDefinition const* m_currentFunction = nullptr;
};
}
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index 89014133..94e82a87 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -32,6 +32,16 @@ bool SyntaxChecker::checkSyntax(ASTNode const& _astRoot)
return Error::containsOnlyWarnings(m_errors);
}
+void SyntaxChecker::warning(SourceLocation const& _location, string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::Warning);
+ *err <<
+ errinfo_sourceLocation(_location) <<
+ errinfo_comment(_description);
+
+ m_errors.push_back(err);
+}
+
void SyntaxChecker::syntaxError(SourceLocation const& _location, std::string const& _description)
{
auto err = make_shared<Error>(Error::Type::SyntaxError);
@@ -148,6 +158,13 @@ bool SyntaxChecker::visit(Break const& _breakStatement)
return true;
}
+bool SyntaxChecker::visit(UnaryOperation const& _operation)
+{
+ if (_operation.getOperator() == Token::Add)
+ warning(_operation.location(), "Use of unary + is deprecated.");
+ return true;
+}
+
bool SyntaxChecker::visit(PlaceholderStatement const&)
{
m_placeholderFound = true;
diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h
index 308e128b..8d7dcdd3 100644
--- a/libsolidity/analysis/SyntaxChecker.h
+++ b/libsolidity/analysis/SyntaxChecker.h
@@ -32,6 +32,7 @@ namespace solidity
* The module that performs syntax analysis on the AST:
* - whether continue/break is in a for/while loop.
* - whether a modifier contains at least one '_'
+ * - issues deprecation warnings for unary '+'
*/
class SyntaxChecker: private ASTConstVisitor
{
@@ -43,6 +44,7 @@ public:
private:
/// Adds a new error to the list of errors.
+ void warning(SourceLocation const& _location, std::string const& _description);
void syntaxError(SourceLocation const& _location, std::string const& _description);
virtual bool visit(SourceUnit const& _sourceUnit) override;
@@ -60,6 +62,8 @@ private:
virtual bool visit(Continue const& _continueStatement) override;
virtual bool visit(Break const& _breakStatement) override;
+ virtual bool visit(UnaryOperation const& _operation) override;
+
virtual bool visit(PlaceholderStatement const& _placeholderStatement) override;
ErrorList& m_errors;
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 512493cd..38cdc1f8 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -24,8 +24,9 @@
#include <memory>
#include <boost/range/adaptor/reversed.hpp>
#include <libsolidity/ast/AST.h>
-#include <libevmasm/Assembly.h> // needed for inline assembly
-#include <libsolidity/inlineasm/AsmCodeGen.h>
+#include <libsolidity/inlineasm/AsmAnalysis.h>
+#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
+#include <libsolidity/inlineasm/AsmData.h>
using namespace std;
using namespace dev;
@@ -64,8 +65,10 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
{
m_scope = &_contract;
- // We force our own visiting order here.
- //@TODO structs will be visited again below, but it is probably fine.
+ // We force our own visiting order here. The structs have to be excluded below.
+ set<ASTNode const*> visited;
+ for (auto const& s: _contract.definedStructs())
+ visited.insert(s);
ASTNode::listAccept(_contract.definedStructs(), *this);
ASTNode::listAccept(_contract.baseContracts(), *this);
@@ -113,7 +116,9 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
_contract.annotation().isFullyImplemented = false;
}
- ASTNode::listAccept(_contract.subNodes(), *this);
+ for (auto const& n: _contract.subNodes())
+ if (!visited.count(n.get()))
+ n->accept(*this);
checkContractExternalTypeClashes(_contract);
// check for hash collisions in function signatures
@@ -183,13 +188,20 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>;
map<string, vector<FunTypeAndFlag>> functions;
+ bool allBaseConstructorsImplemented = true;
// Search from base to derived
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts))
for (FunctionDefinition const* function: contract->definedFunctions())
{
// Take constructors out of overload hierarchy
if (function->isConstructor())
+ {
+ if (!function->isImplemented())
+ // Base contract's constructor is not fully implemented, no way to get
+ // out of this.
+ allBaseConstructorsImplemented = false;
continue;
+ }
auto& overloads = functions[function->name()];
FunctionTypePointer funType = make_shared<FunctionType>(*function);
auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag)
@@ -207,6 +219,9 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
it->second = true;
}
+ if (!allBaseConstructorsImplemented)
+ _contract.annotation().isFullyImplemented = false;
+
// Set to not fully implemented if at least one flag is false.
for (auto const& it: functions)
for (auto const& funAndFlag: it.second)
@@ -354,6 +369,9 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
solAssert(base, "Base contract not available.");
+ if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
+ typeError(_inheritance.location(), "Interfaces cannot inherit.");
+
if (base->isLibrary())
typeError(_inheritance.location(), "Libraries cannot be inherited from.");
@@ -396,6 +414,9 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
bool TypeChecker::visit(StructDefinition const& _struct)
{
+ if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
+ typeError(_struct.location(), "Structs cannot be defined in interfaces.");
+
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
if (!type(*member)->canBeStored())
typeError(member->location(), "Type cannot be used in struct.");
@@ -451,6 +472,15 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
dynamic_cast<ContractDefinition const&>(*_function.scope()).annotation().linearizedBaseContracts :
vector<ContractDefinition const*>()
);
+ if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
+ {
+ if (_function.isImplemented())
+ typeError(_function.location(), "Functions in interfaces cannot have an implementation.");
+ if (_function.visibility() < FunctionDefinition::Visibility::Public)
+ typeError(_function.location(), "Functions in interfaces cannot be internal or private.");
+ if (_function.isConstructor())
+ typeError(_function.location(), "Constructor cannot be defined in interfaces.");
+ }
if (_function.isImplemented())
_function.body().accept(*this);
return false;
@@ -458,6 +488,9 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
bool TypeChecker::visit(VariableDeclaration const& _variable)
{
+ if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
+ typeError(_variable.location(), "Variables cannot be declared in interfaces.");
+
// Variables can be declared without type (with "var"), in which case the first assignment
// sets the type.
// Note that assignments before the first declaration are legal because of the special scoping
@@ -504,6 +537,13 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
return false;
}
+bool TypeChecker::visit(EnumDefinition const& _enum)
+{
+ if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
+ typeError(_enum.location(), "Enumerable cannot be declared in interfaces.");
+ return false;
+}
+
void TypeChecker::visitManually(
ModifierInvocation const& _modifier,
vector<ContractDefinition const*> const& _bases
@@ -582,72 +622,98 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
void TypeChecker::endVisit(FunctionTypeName const& _funType)
{
FunctionType const& fun = dynamic_cast<FunctionType const&>(*_funType.annotation().type);
- if (fun.location() == FunctionType::Location::External)
+ if (fun.kind() == FunctionType::Kind::External)
if (!fun.canBeUsedExternally(false))
typeError(_funType.location(), "External function type uses internal types.");
}
bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
{
- // Inline assembly does not have its own type-checking phase, so we just run the
- // code-generator and see whether it produces any errors.
// External references have already been resolved in a prior stage and stored in the annotation.
- auto identifierAccess = [&](
+ // We run the resolve step again regardless.
+ assembly::ExternalIdentifierAccess::Resolver identifierAccess = [&](
assembly::Identifier const& _identifier,
- eth::Assembly& _assembly,
- assembly::CodeGenerator::IdentifierContext _context
+ assembly::IdentifierContext _context
)
{
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
if (ref == _inlineAssembly.annotation().externalReferences.end())
- return false;
- Declaration const* declaration = ref->second;
+ return size_t(-1);
+ Declaration const* declaration = ref->second.declaration;
solAssert(!!declaration, "");
- if (_context == assembly::CodeGenerator::IdentifierContext::RValue)
+ if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
{
- solAssert(!!declaration->type(), "Type of declaration required but not yet determined.");
- unsigned pushes = 0;
- if (dynamic_cast<FunctionDefinition const*>(declaration))
- pushes = 1;
- else if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
+ if (ref->second.isSlot || ref->second.isOffset)
{
- if (var->isConstant())
- fatalTypeError(SourceLocation(), "Constant variables not yet implemented for inline assembly.");
- if (var->isLocalVariable())
- pushes = var->type()->sizeOnStack();
- else if (!var->type()->isValueType())
- pushes = 1;
- else
- pushes = 2; // slot number, intra slot offset
+ if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage))
+ {
+ typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables.");
+ return size_t(-1);
+ }
+ else if (_context != assembly::IdentifierContext::RValue)
+ {
+ typeError(_identifier.location, "Storage variables cannot be assigned to.");
+ return size_t(-1);
+ }
}
- else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
+ else if (var->isConstant())
{
- if (!contract->isLibrary())
- return false;
- pushes = 1;
+ typeError(_identifier.location, "Constant variables not supported by inline assembly.");
+ return size_t(-1);
+ }
+ else if (!var->isLocalVariable())
+ {
+ typeError(_identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes.");
+ return size_t(-1);
+ }
+ else if (var->type()->dataStoredIn(DataLocation::Storage))
+ {
+ typeError(_identifier.location, "You have to use the _slot or _offset prefix to access storage reference variables.");
+ return size_t(-1);
+ }
+ else if (var->type()->sizeOnStack() != 1)
+ {
+ typeError(_identifier.location, "Only types that use one stack slot are supported.");
+ return size_t(-1);
}
- else
- return false;
- for (unsigned i = 0; i < pushes; ++i)
- _assembly.append(u256(0)); // just to verify the stack height
}
- else
+ else if (_context == assembly::IdentifierContext::LValue)
+ {
+ typeError(_identifier.location, "Only local variables can be assigned to in inline assembly.");
+ return size_t(-1);
+ }
+
+ if (_context == assembly::IdentifierContext::RValue)
{
- // lvalue context
- if (auto varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
+ solAssert(!!declaration->type(), "Type of declaration required but not yet determined.");
+ if (dynamic_cast<FunctionDefinition const*>(declaration))
{
- if (!varDecl->isLocalVariable())
- return false; // only local variables are inline-assemlby lvalues
- for (unsigned i = 0; i < declaration->type()->sizeOnStack(); ++i)
- _assembly.append(Instruction::POP); // remove value just to verify the stack height
+ }
+ else if (dynamic_cast<VariableDeclaration const*>(declaration))
+ {
+ }
+ else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
+ {
+ if (!contract->isLibrary())
+ {
+ typeError(_identifier.location, "Expected a library.");
+ return size_t(-1);
+ }
}
else
- return false;
+ return size_t(-1);
}
- return true;
+ ref->second.valueSize = 1;
+ return size_t(1);
};
- assembly::CodeGenerator codeGen(_inlineAssembly.operations(), m_errors);
- if (!codeGen.typeCheck(identifierAccess))
+ solAssert(!_inlineAssembly.annotation().analysisInfo, "");
+ _inlineAssembly.annotation().analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
+ assembly::AsmAnalyzer analyzer(
+ *_inlineAssembly.annotation().analysisInfo,
+ m_errors,
+ identifierAccess
+ );
+ if (!analyzer.analyze(_inlineAssembly.operations()))
return false;
return true;
}
@@ -886,15 +952,14 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
{
if (auto callType = dynamic_cast<FunctionType const*>(type(call->expression()).get()))
{
- using Location = FunctionType::Location;
- Location location = callType->location();
+ auto kind = callType->kind();
if (
- location == Location::Bare ||
- location == Location::BareCallCode ||
- location == Location::BareDelegateCall
+ kind == FunctionType::Kind::Bare ||
+ kind == FunctionType::Kind::BareCallCode ||
+ kind == FunctionType::Kind::BareDelegateCall
)
warning(_statement.location(), "Return value of low-level calls not used.");
- else if (location == Location::Send)
+ else if (kind == FunctionType::Kind::Send)
warning(_statement.location(), "Failure condition of 'send' ignored. Consider using 'transfer' instead.");
}
}
@@ -1387,7 +1452,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
TypePointers{type},
strings(),
strings(),
- FunctionType::Location::ObjectCreation
+ FunctionType::Kind::ObjectCreation
);
_newExpression.annotation().isPure = true;
}
@@ -1636,8 +1701,8 @@ bool TypeChecker::visit(Identifier const& _identifier)
if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(annotation.referencedDeclaration))
annotation.isPure = annotation.isConstant = variableDeclaration->isConstant();
else if (dynamic_cast<MagicVariableDeclaration const*>(annotation.referencedDeclaration))
- if (auto functionType = dynamic_cast<FunctionType const*>(annotation.type.get()))
- annotation.isPure = functionType->isPure();
+ if (dynamic_cast<FunctionType const*>(annotation.type.get()))
+ annotation.isPure = true;
return false;
}
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index 46d8230a..88559f44 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -83,6 +83,7 @@ private:
virtual bool visit(StructDefinition const& _struct) override;
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(VariableDeclaration const& _variable) override;
+ virtual bool visit(EnumDefinition const& _enum) override;
/// We need to do this manually because we want to pass the bases of the current contract in
/// case this is a base constructor call.
void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> const& _bases);
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index 8031760d..02234ffc 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -316,19 +316,21 @@ protected:
class ContractDefinition: public Declaration, public Documented
{
public:
+ enum class ContractKind { Interface, Contract, Library };
+
ContractDefinition(
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<ASTNode>> const& _subNodes,
- bool _isLibrary
+ ContractKind _contractKind = ContractKind::Contract
):
Declaration(_location, _name),
Documented(_documentation),
m_baseContracts(_baseContracts),
m_subNodes(_subNodes),
- m_isLibrary(_isLibrary)
+ m_contractKind(_contractKind)
{}
virtual void accept(ASTVisitor& _visitor) override;
@@ -344,7 +346,7 @@ public:
std::vector<FunctionDefinition const*> definedFunctions() const { return filteredNodes<FunctionDefinition>(m_subNodes); }
std::vector<EventDefinition const*> events() const { return filteredNodes<EventDefinition>(m_subNodes); }
std::vector<EventDefinition const*> const& interfaceEvents() const;
- bool isLibrary() const { return m_isLibrary; }
+ bool isLibrary() const { return m_contractKind == ContractKind::Library; }
/// @returns a map of canonical function signatures to FunctionDefinitions
/// as intended for use by the ABI.
@@ -371,10 +373,12 @@ public:
virtual ContractDefinitionAnnotation& annotation() const override;
+ ContractKind contractKind() const { return m_contractKind; }
+
private:
std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts;
std::vector<ASTPointer<ASTNode>> m_subNodes;
- bool m_isLibrary;
+ ContractKind m_contractKind;
// parsed Natspec documentation of the contract.
Json::Value m_userDocumentation;
diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h
index bd297f9e..a7d89248 100644
--- a/libsolidity/ast/ASTAnnotations.h
+++ b/libsolidity/ast/ASTAnnotations.h
@@ -22,11 +22,12 @@
#pragma once
+#include <libsolidity/ast/ASTForward.h>
+
#include <map>
#include <memory>
#include <vector>
#include <set>
-#include <libsolidity/ast/ASTForward.h>
namespace dev
{
@@ -112,13 +113,24 @@ struct StatementAnnotation: ASTAnnotation, DocumentedAnnotation
namespace assembly
{
-struct Identifier; // forward
+ struct AsmAnalysisInfo;
+ struct Identifier;
}
struct InlineAssemblyAnnotation: StatementAnnotation
{
- /// Mapping containing resolved references to external identifiers.
- std::map<assembly::Identifier const*, Declaration const*> externalReferences;
+ struct ExternalIdentifierInfo
+ {
+ Declaration const* declaration = nullptr;
+ bool isSlot = false; ///< Whether the storage slot of a variable is queried.
+ bool isOffset = false; ///< Whether the intra-slot offset of a storage variable is queried.
+ size_t valueSize = size_t(-1);
+ };
+
+ /// Mapping containing resolved references to external identifiers and their value size
+ std::map<assembly::Identifier const*, ExternalIdentifierInfo> externalReferences;
+ /// Information generated during analysis phase.
+ std::shared_ptr<assembly::AsmAnalysisInfo> analysisInfo;
};
struct ReturnAnnotation: StatementAnnotation
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index 69c10c8d..9ea23687 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -40,6 +40,21 @@ void ASTJsonConverter::addJsonNode(
bool _hasChildren = false
)
{
+ ASTJsonConverter::addJsonNode(
+ _node,
+ _nodeName,
+ std::vector<pair<string const, Json::Value const>>(_attributes),
+ _hasChildren
+ );
+}
+
+void ASTJsonConverter::addJsonNode(
+ ASTNode const& _node,
+ string const& _nodeName,
+ std::vector<pair<string const, Json::Value const>> const& _attributes,
+ bool _hasChildren = false
+)
+{
Json::Value node;
node["id"] = Json::UInt64(_node.id());
@@ -183,11 +198,18 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
bool ASTJsonConverter::visit(VariableDeclaration const& _node)
{
- addJsonNode(_node, "VariableDeclaration", {
+ std::vector<pair<string const, Json::Value const>> attributes = {
make_pair("name", _node.name()),
- make_pair("type", type(_node))
- }, true);
+ make_pair("type", type(_node)),
+ make_pair("constant", _node.isConstant()),
+ make_pair("storageLocation", location(_node.referenceLocation())),
+ make_pair("visibility", visibility(_node.visibility()))
+ };
+ if (m_inEvent)
+ attributes.push_back(make_pair("indexed", _node.isIndexed()));
+ addJsonNode(_node, "VariableDeclaration", attributes, true);
return true;
+
}
bool ASTJsonConverter::visit(ModifierDefinition const& _node)
@@ -209,6 +231,7 @@ bool ASTJsonConverter::visit(TypeName const&)
bool ASTJsonConverter::visit(EventDefinition const& _node)
{
+ m_inEvent = true;
addJsonNode(_node, "EventDefinition", { make_pair("name", _node.name()) }, true);
return true;
}
@@ -502,6 +525,7 @@ void ASTJsonConverter::endVisit(ModifierInvocation const&)
void ASTJsonConverter::endVisit(EventDefinition const&)
{
+ m_inEvent = false;
goUp();
}
@@ -670,6 +694,21 @@ string ASTJsonConverter::visibility(Declaration::Visibility const& _visibility)
}
}
+string ASTJsonConverter::location(VariableDeclaration::Location _location)
+{
+ switch (_location)
+ {
+ case VariableDeclaration::Location::Default:
+ return "default";
+ case VariableDeclaration::Location::Storage:
+ return "storage";
+ case VariableDeclaration::Location::Memory:
+ return "memory";
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown declaration location."));
+ }
+}
+
string ASTJsonConverter::type(Expression const& _expression)
{
return _expression.annotation().type ? _expression.annotation().type->toString() : "Unknown";
diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h
index 49f23f99..bd5e6560 100644
--- a/libsolidity/ast/ASTJsonConverter.h
+++ b/libsolidity/ast/ASTJsonConverter.h
@@ -151,8 +151,15 @@ private:
std::initializer_list<std::pair<std::string const, Json::Value const>> _attributes,
bool _hasChildren
);
+ void addJsonNode(
+ ASTNode const& _node,
+ std::string const& _nodeName,
+ std::vector<std::pair<std::string const, Json::Value const>> const& _attributes,
+ bool _hasChildren
+ );
std::string sourceLocationToString(SourceLocation const& _location) const;
std::string visibility(Declaration::Visibility const& _visibility);
+ std::string location(VariableDeclaration::Location _location);
std::string type(Expression const& _expression);
std::string type(VariableDeclaration const& _varDecl);
inline void goUp()
@@ -161,6 +168,7 @@ private:
m_jsonNodePtrs.pop();
}
+ bool m_inEvent = false; ///< whether we are currently inside an event or not
bool processed = false;
Json::Value m_astJson;
std::stack<Json::Value*> m_jsonNodePtrs;
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index e7f53422..41ee6498 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -462,11 +462,11 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
if (isAddress())
return {
{"balance", make_shared<IntegerType >(256)},
- {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true, false, true)},
- {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true, false, true)},
- {"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)},
- {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)},
- {"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Location::Transfer)}
+ {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::Bare, true, false, true)},
+ {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, false, true)},
+ {"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareDelegateCall, true)},
+ {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
+ {"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}
};
else
return MemberList::MemberMap();
@@ -1466,7 +1466,7 @@ MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const
TypePointers{make_shared<IntegerType>(256)},
strings{string()},
strings{string()},
- isByteArray() ? FunctionType::Location::ByteArrayPush : FunctionType::Location::ArrayPush
+ isByteArray() ? FunctionType::Kind::ByteArrayPush : FunctionType::Kind::ArrayPush
)});
}
return members;
@@ -1766,7 +1766,7 @@ FunctionTypePointer StructType::constructorType() const
TypePointers{copyForLocation(DataLocation::Memory, false)},
paramNames,
strings(),
- FunctionType::Location::Internal
+ FunctionType::Kind::Internal
);
}
@@ -1967,7 +1967,7 @@ TypePointer TupleType::closestTemporaryType(TypePointer const& _targetType) cons
}
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
- m_location(_isInternal ? Location::Internal : Location::External),
+ m_kind(_isInternal ? Kind::Internal : Kind::External),
m_isConstant(_function.isDeclaredConst()),
m_isPayable(_isInternal ? false : _function.isPayable()),
m_declaration(&_function)
@@ -1998,7 +1998,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
}
FunctionType::FunctionType(VariableDeclaration const& _varDecl):
- m_location(Location::External), m_isConstant(true), m_declaration(&_varDecl)
+ m_kind(Kind::External), m_isConstant(true), m_declaration(&_varDecl)
{
TypePointers paramTypes;
vector<string> paramNames;
@@ -2058,7 +2058,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
}
FunctionType::FunctionType(EventDefinition const& _event):
- m_location(Location::Event), m_isConstant(true), m_declaration(&_event)
+ m_kind(Kind::Event), m_isConstant(true), m_declaration(&_event)
{
TypePointers params;
vector<string> paramNames;
@@ -2074,19 +2074,19 @@ FunctionType::FunctionType(EventDefinition const& _event):
}
FunctionType::FunctionType(FunctionTypeName const& _typeName):
- m_location(_typeName.visibility() == VariableDeclaration::Visibility::External ? Location::External : Location::Internal),
+ m_kind(_typeName.visibility() == VariableDeclaration::Visibility::External ? Kind::External : Kind::Internal),
m_isConstant(_typeName.isDeclaredConst()),
m_isPayable(_typeName.isPayable())
{
if (_typeName.isPayable())
{
- solAssert(m_location == Location::External, "Internal payable function type used.");
+ solAssert(m_kind == Kind::External, "Internal payable function type used.");
solAssert(!m_isConstant, "Payable constant function");
}
for (auto const& t: _typeName.parameterTypes())
{
solAssert(t->annotation().type, "Type not set for parameter.");
- if (m_location == Location::External)
+ if (m_kind == Kind::External)
solAssert(
t->annotation().type->canBeUsedExternally(false),
"Internal type used as parameter for external function."
@@ -2096,7 +2096,7 @@ FunctionType::FunctionType(FunctionTypeName const& _typeName):
for (auto const& t: _typeName.returnParameterTypes())
{
solAssert(t->annotation().type, "Type not set for return parameter.");
- if (m_location == Location::External)
+ if (m_kind == Kind::External)
solAssert(
t->annotation().type->canBeUsedExternally(false),
"Internal type used as return parameter for external function."
@@ -2126,7 +2126,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c
TypePointers{make_shared<ContractType>(_contract)},
parameterNames,
strings{""},
- Location::Creation,
+ Kind::Creation,
false,
nullptr,
false,
@@ -2151,38 +2151,38 @@ TypePointers FunctionType::parameterTypes() const
string FunctionType::identifier() const
{
string id = "t_function_";
- switch (location())
+ switch (m_kind)
{
- case Location::Internal: id += "internal"; break;
- case Location::External: id += "external"; break;
- case Location::CallCode: id += "callcode"; break;
- case Location::DelegateCall: id += "delegatecall"; break;
- case Location::Bare: id += "bare"; break;
- case Location::BareCallCode: id += "barecallcode"; break;
- case Location::BareDelegateCall: id += "baredelegatecall"; break;
- case Location::Creation: id += "creation"; break;
- case Location::Send: id += "send"; break;
- case Location::Transfer: id += "transfer"; break;
- case Location::SHA3: id += "sha3"; break;
- case Location::Selfdestruct: id += "selfdestruct"; break;
- case Location::Revert: id += "revert"; break;
- case Location::ECRecover: id += "ecrecover"; break;
- case Location::SHA256: id += "sha256"; break;
- case Location::RIPEMD160: id += "ripemd160"; break;
- case Location::Log0: id += "log0"; break;
- case Location::Log1: id += "log1"; break;
- case Location::Log2: id += "log2"; break;
- case Location::Log3: id += "log3"; break;
- case Location::Log4: id += "log4"; break;
- case Location::Event: id += "event"; break;
- case Location::SetGas: id += "setgas"; break;
- case Location::SetValue: id += "setvalue"; break;
- case Location::BlockHash: id += "blockhash"; break;
- case Location::AddMod: id += "addmod"; break;
- case Location::MulMod: id += "mulmod"; break;
- case Location::ArrayPush: id += "arraypush"; break;
- case Location::ByteArrayPush: id += "bytearraypush"; break;
- case Location::ObjectCreation: id += "objectcreation"; break;
+ case Kind::Internal: id += "internal"; break;
+ case Kind::External: id += "external"; break;
+ case Kind::CallCode: id += "callcode"; break;
+ case Kind::DelegateCall: id += "delegatecall"; break;
+ case Kind::Bare: id += "bare"; break;
+ case Kind::BareCallCode: id += "barecallcode"; break;
+ case Kind::BareDelegateCall: id += "baredelegatecall"; break;
+ case Kind::Creation: id += "creation"; break;
+ case Kind::Send: id += "send"; break;
+ case Kind::Transfer: id += "transfer"; break;
+ case Kind::SHA3: id += "sha3"; break;
+ case Kind::Selfdestruct: id += "selfdestruct"; break;
+ case Kind::Revert: id += "revert"; break;
+ case Kind::ECRecover: id += "ecrecover"; break;
+ case Kind::SHA256: id += "sha256"; break;
+ case Kind::RIPEMD160: id += "ripemd160"; break;
+ case Kind::Log0: id += "log0"; break;
+ case Kind::Log1: id += "log1"; break;
+ case Kind::Log2: id += "log2"; break;
+ case Kind::Log3: id += "log3"; break;
+ case Kind::Log4: id += "log4"; break;
+ case Kind::Event: id += "event"; break;
+ case Kind::SetGas: id += "setgas"; break;
+ case Kind::SetValue: id += "setvalue"; break;
+ case Kind::BlockHash: id += "blockhash"; break;
+ case Kind::AddMod: id += "addmod"; break;
+ case Kind::MulMod: id += "mulmod"; break;
+ case Kind::ArrayPush: id += "arraypush"; break;
+ case Kind::ByteArrayPush: id += "bytearraypush"; break;
+ case Kind::ObjectCreation: id += "objectcreation"; break;
default: solAssert(false, "Unknown function location."); break;
}
if (isConstant())
@@ -2203,7 +2203,7 @@ bool FunctionType::operator==(Type const& _other) const
return false;
FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
- if (m_location != other.m_location)
+ if (m_kind != other.m_kind)
return false;
if (m_isConstant != other.isConstant())
return false;
@@ -2231,7 +2231,7 @@ bool FunctionType::operator==(Type const& _other) const
bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- if (m_location == Location::External && _convertTo.category() == Category::Integer)
+ if (m_kind == Kind::External && _convertTo.category() == Category::Integer)
{
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
if (convertTo.isAddress())
@@ -2249,7 +2249,7 @@ TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const
string FunctionType::canonicalName(bool) const
{
- solAssert(m_location == Location::External, "");
+ solAssert(m_kind == Kind::External, "");
return "function";
}
@@ -2263,7 +2263,7 @@ string FunctionType::toString(bool _short) const
name += " constant";
if (m_isPayable)
name += " payable";
- if (m_location == Location::External)
+ if (m_kind == Kind::External)
name += " external";
if (!m_returnParameterTypes.empty())
{
@@ -2285,7 +2285,7 @@ unsigned FunctionType::calldataEncodedSize(bool _padded) const
u256 FunctionType::storageSize() const
{
- if (m_location == Location::External || m_location == Location::Internal)
+ if (m_kind == Kind::External || m_kind == Kind::Internal)
return 1;
else
BOOST_THROW_EXCEPTION(
@@ -2295,9 +2295,9 @@ u256 FunctionType::storageSize() const
unsigned FunctionType::storageBytes() const
{
- if (m_location == Location::External)
+ if (m_kind == Kind::External)
return 20 + 4;
- else if (m_location == Location::Internal)
+ else if (m_kind == Kind::Internal)
return 8; // it should really not be possible to create larger programs
else
BOOST_THROW_EXCEPTION(
@@ -2307,21 +2307,21 @@ unsigned FunctionType::storageBytes() const
unsigned FunctionType::sizeOnStack() const
{
- Location location = m_location;
- if (m_location == Location::SetGas || m_location == Location::SetValue)
+ Kind kind = m_kind;
+ if (m_kind == Kind::SetGas || m_kind == Kind::SetValue)
{
solAssert(m_returnParameterTypes.size() == 1, "");
- location = dynamic_cast<FunctionType const&>(*m_returnParameterTypes.front()).m_location;
+ kind = dynamic_cast<FunctionType const&>(*m_returnParameterTypes.front()).m_kind;
}
unsigned size = 0;
- if (location == Location::External || location == Location::CallCode || location == Location::DelegateCall)
+ if (kind == Kind::External || kind == Kind::CallCode || kind == Kind::DelegateCall)
size = 2;
- else if (location == Location::Bare || location == Location::BareCallCode || location == Location::BareDelegateCall)
+ else if (kind == Kind::Bare || kind == Kind::BareCallCode || kind == Kind::BareDelegateCall)
size = 1;
- else if (location == Location::Internal)
+ else if (kind == Kind::Internal)
size = 1;
- else if (location == Location::ArrayPush || location == Location::ByteArrayPush)
+ else if (kind == Kind::ArrayPush || kind == Kind::ByteArrayPush)
size = 1;
if (m_gasSet)
size++;
@@ -2362,26 +2362,26 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
return make_shared<FunctionType>(
paramTypes, retParamTypes,
m_parameterNames, m_returnParameterNames,
- m_location, m_arbitraryParameters,
+ m_kind, m_arbitraryParameters,
m_declaration, m_isConstant, m_isPayable
);
}
MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) const
{
- switch (m_location)
+ switch (m_kind)
{
- case Location::External:
- case Location::Creation:
- case Location::ECRecover:
- case Location::SHA256:
- case Location::RIPEMD160:
- case Location::Bare:
- case Location::BareCallCode:
- case Location::BareDelegateCall:
+ case Kind::External:
+ case Kind::Creation:
+ case Kind::ECRecover:
+ case Kind::SHA256:
+ case Kind::RIPEMD160:
+ case Kind::Bare:
+ case Kind::BareCallCode:
+ case Kind::BareDelegateCall:
{
MemberList::MemberMap members;
- if (m_location != Location::BareDelegateCall && m_location != Location::DelegateCall)
+ if (m_kind != Kind::BareDelegateCall && m_kind != Kind::DelegateCall)
{
if (m_isPayable)
members.push_back(MemberList::Member(
@@ -2391,7 +2391,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
TypePointers{copyAndSetGasOrValue(false, true)},
strings(),
strings(),
- Location::SetValue,
+ Kind::SetValue,
false,
nullptr,
false,
@@ -2401,7 +2401,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
)
));
}
- if (m_location != Location::Creation)
+ if (m_kind != Kind::Creation)
members.push_back(MemberList::Member(
"gas",
make_shared<FunctionType>(
@@ -2409,7 +2409,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
TypePointers{copyAndSetGasOrValue(true, false)},
strings(),
strings(),
- Location::SetGas,
+ Kind::SetGas,
false,
nullptr,
false,
@@ -2428,7 +2428,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
TypePointer FunctionType::encodingType() const
{
// Only external functions can be encoded, internal functions cannot leave code boundaries.
- if (m_location == Location::External)
+ if (m_kind == Kind::External)
return shared_from_this();
else
return TypePointer();
@@ -2436,7 +2436,7 @@ TypePointer FunctionType::encodingType() const
TypePointer FunctionType::interfaceType(bool /*_inLibrary*/) const
{
- if (m_location == Location::External)
+ if (m_kind == Kind::External)
return shared_from_this();
else
return TypePointer();
@@ -2478,14 +2478,14 @@ bool FunctionType::hasEqualArgumentTypes(FunctionType const& _other) const
bool FunctionType::isBareCall() const
{
- switch (m_location)
+ switch (m_kind)
{
- case Location::Bare:
- case Location::BareCallCode:
- case Location::BareDelegateCall:
- case Location::ECRecover:
- case Location::SHA256:
- case Location::RIPEMD160:
+ case Kind::Bare:
+ case Kind::BareCallCode:
+ case Kind::BareDelegateCall:
+ case Kind::ECRecover:
+ case Kind::SHA256:
+ case Kind::RIPEMD160:
return true;
default:
return false;
@@ -2520,13 +2520,13 @@ u256 FunctionType::externalIdentifier() const
bool FunctionType::isPure() const
{
return
- m_location == Location::SHA3 ||
- m_location == Location::ECRecover ||
- m_location == Location::SHA256 ||
- m_location == Location::RIPEMD160 ||
- m_location == Location::AddMod ||
- m_location == Location::MulMod ||
- m_location == Location::ObjectCreation;
+ m_kind == Kind::SHA3 ||
+ m_kind == Kind::ECRecover ||
+ m_kind == Kind::SHA256 ||
+ m_kind == Kind::RIPEMD160 ||
+ m_kind == Kind::AddMod ||
+ m_kind == Kind::MulMod ||
+ m_kind == Kind::ObjectCreation;
}
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
@@ -2545,7 +2545,7 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
m_returnParameterTypes,
m_parameterNames,
m_returnParameterNames,
- m_location,
+ m_kind,
m_arbitraryParameters,
m_declaration,
m_isConstant,
@@ -2571,18 +2571,18 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound)
parameterTypes.push_back(t);
}
- Location location = m_location;
+ Kind kind = m_kind;
if (_inLibrary)
{
solAssert(!!m_declaration, "Declaration has to be available.");
if (!m_declaration->isPublic())
- location = Location::Internal; // will be inlined
+ kind = Kind::Internal; // will be inlined
else
- location = Location::DelegateCall;
+ kind = Kind::DelegateCall;
}
TypePointers returnParameterTypes = m_returnParameterTypes;
- if (location != Location::Internal)
+ if (kind != Kind::Internal)
{
// Alter dynamic types to be non-accessible.
for (auto& param: returnParameterTypes)
@@ -2595,7 +2595,7 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound)
returnParameterTypes,
m_parameterNames,
m_returnParameterNames,
- location,
+ kind,
m_arbitraryParameters,
m_declaration,
m_isConstant,
@@ -2821,7 +2821,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
return MemberList::MemberMap({
{"coinbase", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"timestamp", make_shared<IntegerType>(256)},
- {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Location::BlockHash)},
+ {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash)},
{"difficulty", make_shared<IntegerType>(256)},
{"number", make_shared<IntegerType>(256)},
{"gaslimit", make_shared<IntegerType>(256)}
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 78326aa6..c4ffc44c 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -817,8 +817,7 @@ class FunctionType: public Type
{
public:
/// How this function is invoked on the EVM.
- /// @todo This documentation is outdated, and Location should rather be named "Type"
- enum class Location
+ enum class Kind
{
Internal, ///< stack-call using plain JUMP
External, ///< external call using CALL
@@ -868,7 +867,7 @@ public:
FunctionType(
strings const& _parameterTypes,
strings const& _returnParameterTypes,
- Location _location = Location::Internal,
+ Kind _kind = Kind::Internal,
bool _arbitraryParameters = false,
bool _constant = false,
bool _payable = false
@@ -877,7 +876,7 @@ public:
parseElementaryTypeVector(_returnParameterTypes),
strings(),
strings(),
- _location,
+ _kind,
_arbitraryParameters,
nullptr,
_constant,
@@ -895,7 +894,7 @@ public:
TypePointers const& _returnParameterTypes,
strings _parameterNames = strings(),
strings _returnParameterNames = strings(),
- Location _location = Location::Internal,
+ Kind _kind = Kind::Internal,
bool _arbitraryParameters = false,
Declaration const* _declaration = nullptr,
bool _isConstant = false,
@@ -908,7 +907,7 @@ public:
m_returnParameterTypes(_returnParameterTypes),
m_parameterNames(_parameterNames),
m_returnParameterNames(_returnParameterNames),
- m_location(_location),
+ m_kind(_kind),
m_arbitraryParameters(_arbitraryParameters),
m_gasSet(_gasSet),
m_valueSet(_valueSet),
@@ -937,11 +936,11 @@ public:
virtual std::string canonicalName(bool /*_addDataLocation*/) const override;
virtual std::string toString(bool _short) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override;
- virtual bool canBeStored() const override { return m_location == Location::Internal || m_location == Location::External; }
+ virtual bool canBeStored() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
virtual u256 storageSize() const override;
virtual unsigned storageBytes() const override;
virtual bool isValueType() const override { return true; }
- virtual bool canLiveOutsideStorage() const override { return m_location == Location::Internal || m_location == Location::External; }
+ virtual bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
virtual unsigned sizeOnStack() const override;
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
virtual TypePointer encodingType() const override;
@@ -964,7 +963,7 @@ public:
/// @returns true if the ABI is used for this call (only meaningful for external calls)
bool isBareCall() const;
- Location const& location() const { return m_location; }
+ Kind const& kind() const { return m_kind; }
/// @returns the external signature of this function type given the function name
std::string externalSignature() const;
/// @returns the external identifier of this function (the hash of the signature).
@@ -986,7 +985,7 @@ public:
ASTPointer<ASTString> documentation() const;
/// true iff arguments are to be padded to multiples of 32 bytes for external calls
- bool padArguments() const { return !(m_location == Location::SHA3 || m_location == Location::SHA256 || m_location == Location::RIPEMD160); }
+ bool padArguments() const { return !(m_kind == Kind::SHA3 || m_kind == Kind::SHA256 || m_kind == Kind::RIPEMD160); }
bool takesArbitraryParameters() const { return m_arbitraryParameters; }
bool gasSet() const { return m_gasSet; }
bool valueSet() const { return m_valueSet; }
@@ -1012,7 +1011,7 @@ private:
TypePointers m_returnParameterTypes;
std::vector<std::string> m_parameterNames;
std::vector<std::string> m_returnParameterNames;
- Location const m_location;
+ Kind const m_kind;
/// 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
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index a8316109..51dd9fd2 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -265,31 +265,40 @@ void CompilerContext::appendInlineAssembly(
}
unsigned startStackHeight = stackHeight();
- auto identifierAccess = [&](
+
+ assembly::ExternalIdentifierAccess identifierAccess;
+ identifierAccess.resolve = [&](
+ assembly::Identifier const& _identifier,
+ assembly::IdentifierContext
+ )
+ {
+ auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name);
+ return it == _localVariables.end() ? size_t(-1) : 1;
+ };
+ identifierAccess.generateCode = [&](
assembly::Identifier const& _identifier,
- eth::Assembly& _assembly,
- assembly::CodeGenerator::IdentifierContext _context
- ) {
+ assembly::IdentifierContext _context,
+ eth::Assembly& _assembly
+ )
+ {
auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name);
- if (it == _localVariables.end())
- return false;
+ solAssert(it != _localVariables.end(), "");
unsigned stackDepth = _localVariables.end() - it;
int stackDiff = _assembly.deposit() - startStackHeight + stackDepth;
- if (_context == assembly::CodeGenerator::IdentifierContext::LValue)
+ if (_context == assembly::IdentifierContext::LValue)
stackDiff -= 1;
if (stackDiff < 1 || stackDiff > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
errinfo_comment("Stack too deep, try removing local variables.")
);
- if (_context == assembly::CodeGenerator::IdentifierContext::RValue)
+ if (_context == assembly::IdentifierContext::RValue)
_assembly.append(dupInstruction(stackDiff));
else
{
_assembly.append(swapInstruction(stackDiff));
_assembly.append(Instruction::POP);
}
- return true;
};
solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, *m_asm, identifierAccess), "Failed to assemble inline assembly block.");
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 42323abd..dc0b340c 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -135,7 +135,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
}
else if (
_type.category() == Type::Category::Function &&
- dynamic_cast<FunctionType const&>(_type).location() == FunctionType::Location::External
+ dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External
)
{
solUnimplementedAssert(_padToWordBoundaries, "Non-padded store for function not implemented.");
@@ -795,7 +795,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
IntegerType const& targetType = dynamic_cast<IntegerType const&>(_targetType);
solAssert(targetType.isAddress(), "Function type can only be converted to address.");
FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack);
- solAssert(typeOnStack.location() == FunctionType::Location::External, "Only external function type can be converted.");
+ solAssert(typeOnStack.kind() == FunctionType::Kind::External, "Only external function type can be converted.");
// stack: <address> <function_id>
m_context << Instruction::POP;
@@ -820,7 +820,7 @@ void CompilerUtils::pushZeroValue(Type const& _type)
{
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
{
- if (funType->location() == FunctionType::Location::Internal)
+ if (funType->kind() == FunctionType::Kind::Internal)
{
m_context << m_context.lowLevelFunctionTag("$invalidFunction", 0, 0, [](CompilerContext& _context) {
_context.appendInvalid();
@@ -983,7 +983,7 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
unsigned numBytes = _type.calldataEncodedSize(_padToWords);
bool isExternalFunctionType = false;
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
- if (funType->location() == FunctionType::Location::External)
+ if (funType->kind() == FunctionType::Kind::External)
isExternalFunctionType = true;
if (numBytes == 0)
{
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index 6524bd03..34ef13c0 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -520,93 +520,129 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
{
ErrorList errors;
- assembly::CodeGenerator codeGen(_inlineAssembly.operations(), errors);
+ assembly::CodeGenerator codeGen(errors);
unsigned startStackHeight = m_context.stackHeight();
- codeGen.assemble(
- m_context.nonConstAssembly(),
- [&](assembly::Identifier const& _identifier, eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierContext _context) {
- auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
- if (ref == _inlineAssembly.annotation().externalReferences.end())
- return false;
- Declaration const* decl = ref->second;
- solAssert(!!decl, "");
- if (_context == assembly::CodeGenerator::IdentifierContext::RValue)
+ assembly::ExternalIdentifierAccess identifierAccess;
+ identifierAccess.resolve = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext)
+ {
+ auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
+ if (ref == _inlineAssembly.annotation().externalReferences.end())
+ return size_t(-1);
+ return ref->second.valueSize;
+ };
+ identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext _context, eth::Assembly& _assembly)
+ {
+ auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
+ solAssert(ref != _inlineAssembly.annotation().externalReferences.end(), "");
+ Declaration const* decl = ref->second.declaration;
+ solAssert(!!decl, "");
+ if (_context == assembly::IdentifierContext::RValue)
+ {
+ int const depositBefore = _assembly.deposit();
+ solAssert(!!decl->type(), "Type of declaration required but not yet determined.");
+ if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl))
{
- solAssert(!!decl->type(), "Type of declaration required but not yet determined.");
- if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl))
+ solAssert(!ref->second.isOffset && !ref->second.isSlot, "");
+ functionDef = &m_context.resolveVirtualFunction(*functionDef);
+ _assembly.append(m_context.functionEntryLabel(*functionDef).pushTag());
+ // If there is a runtime context, we have to merge both labels into the same
+ // stack slot in case we store it in storage.
+ if (CompilerContext* rtc = m_context.runtimeContext())
{
- functionDef = &m_context.resolveVirtualFunction(*functionDef);
- _assembly.append(m_context.functionEntryLabel(*functionDef).pushTag());
- // If there is a runtime context, we have to merge both labels into the same
- // stack slot in case we store it in storage.
- if (CompilerContext* rtc = m_context.runtimeContext())
- {
- _assembly.append(u256(1) << 32);
- _assembly.append(Instruction::MUL);
- _assembly.append(rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub()));
- _assembly.append(Instruction::OR);
- }
+ _assembly.append(u256(1) << 32);
+ _assembly.append(Instruction::MUL);
+ _assembly.append(rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub()));
+ _assembly.append(Instruction::OR);
}
- else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
+ }
+ else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
+ {
+ solAssert(!variable->isConstant(), "");
+ if (m_context.isStateVariable(decl))
{
- solAssert(!variable->isConstant(), "");
- if (m_context.isLocalVariable(variable))
- {
- int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable);
- if (stackDiff < 1 || stackDiff > 16)
- BOOST_THROW_EXCEPTION(
- CompilerError() <<
- errinfo_sourceLocation(_inlineAssembly.location()) <<
- errinfo_comment("Stack too deep, try removing local variables.")
- );
- for (unsigned i = 0; i < variable->type()->sizeOnStack(); ++i)
- _assembly.append(dupInstruction(stackDiff));
- }
+ auto const& location = m_context.storageLocationOfVariable(*decl);
+ if (ref->second.isSlot)
+ m_context << location.first;
+ else if (ref->second.isOffset)
+ m_context << u256(location.second);
else
+ solAssert(false, "");
+ }
+ else if (m_context.isLocalVariable(decl))
+ {
+ int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable);
+ if (ref->second.isSlot || ref->second.isOffset)
{
- solAssert(m_context.isStateVariable(variable), "Invalid variable type.");
- auto const& location = m_context.storageLocationOfVariable(*variable);
- if (!variable->type()->isValueType())
+ solAssert(variable->type()->dataStoredIn(DataLocation::Storage), "");
+ unsigned size = variable->type()->sizeOnStack();
+ if (size == 2)
{
- solAssert(location.second == 0, "Intra-slot offest assumed to be zero.");
- _assembly.append(location.first);
+ // slot plus offset
+ if (ref->second.isOffset)
+ stackDiff--;
}
else
{
- _assembly.append(location.first);
- _assembly.append(u256(location.second));
+ solAssert(size == 1, "");
+ // only slot, offset is zero
+ if (ref->second.isOffset)
+ {
+ _assembly.append(u256(0));
+ return;
+ }
}
}
- }
- else if (auto contract = dynamic_cast<ContractDefinition const*>(decl))
- {
- solAssert(contract->isLibrary(), "");
- _assembly.appendLibraryAddress(contract->fullyQualifiedName());
+ else
+ solAssert(variable->type()->sizeOnStack() == 1, "");
+ if (stackDiff < 1 || stackDiff > 16)
+ BOOST_THROW_EXCEPTION(
+ CompilerError() <<
+ errinfo_sourceLocation(_inlineAssembly.location()) <<
+ errinfo_comment("Stack too deep, try removing local variables.")
+ );
+ solAssert(variable->type()->sizeOnStack() == 1, "");
+ _assembly.append(dupInstruction(stackDiff));
}
else
- solAssert(false, "Invalid declaration type.");
- } else {
- // lvalue context
- auto variable = dynamic_cast<VariableDeclaration const*>(decl);
- solAssert(
- !!variable && m_context.isLocalVariable(variable),
- "Can only assign to stack variables in inline assembly."
- );
- unsigned size = variable->type()->sizeOnStack();
- int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable) - size;
- if (stackDiff > 16 || stackDiff < 1)
- BOOST_THROW_EXCEPTION(
- CompilerError() <<
- errinfo_sourceLocation(_inlineAssembly.location()) <<
- errinfo_comment("Stack too deep, try removing local variables.")
- );
- for (unsigned i = 0; i < size; ++i) {
- _assembly.append(swapInstruction(stackDiff));
- _assembly.append(Instruction::POP);
- }
+ solAssert(false, "");
}
- return true;
+ else if (auto contract = dynamic_cast<ContractDefinition const*>(decl))
+ {
+ solAssert(!ref->second.isOffset && !ref->second.isSlot, "");
+ solAssert(contract->isLibrary(), "");
+ _assembly.appendLibraryAddress(contract->fullyQualifiedName());
+ }
+ else
+ solAssert(false, "Invalid declaration type.");
+ solAssert(_assembly.deposit() - depositBefore == int(ref->second.valueSize), "");
}
+ else
+ {
+ // lvalue context
+ solAssert(!ref->second.isOffset && !ref->second.isSlot, "");
+ auto variable = dynamic_cast<VariableDeclaration const*>(decl);
+ solAssert(
+ !!variable && m_context.isLocalVariable(variable),
+ "Can only assign to stack variables in inline assembly."
+ );
+ solAssert(variable->type()->sizeOnStack() == 1, "");
+ int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable) - 1;
+ if (stackDiff > 16 || stackDiff < 1)
+ BOOST_THROW_EXCEPTION(
+ CompilerError() <<
+ errinfo_sourceLocation(_inlineAssembly.location()) <<
+ errinfo_comment("Stack too deep, try removing local variables.")
+ );
+ _assembly.append(swapInstruction(stackDiff));
+ _assembly.append(Instruction::POP);
+ }
+ };
+ solAssert(_inlineAssembly.annotation().analysisInfo, "");
+ codeGen.assemble(
+ _inlineAssembly.operations(),
+ *_inlineAssembly.annotation().analysisInfo,
+ m_context.nonConstAssembly(),
+ identifierAccess
);
solAssert(Error::containsOnlyWarnings(errors), "Code generation for inline assembly with errors requested.");
m_context.setStackOffset(startStackHeight);
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 744a80c4..f018b311 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -434,7 +434,6 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{
CompilerContext::LocationSetter locationSetter(m_context, _functionCall);
- using Location = FunctionType::Location;
if (_functionCall.annotation().isTypeConversion)
{
solAssert(_functionCall.arguments().size() == 1, "");
@@ -499,10 +498,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
FunctionType const& function = *functionType;
if (function.bound())
// Only delegatecall and internal functions can be bound, this might be lifted later.
- solAssert(function.location() == Location::DelegateCall || function.location() == Location::Internal, "");
- switch (function.location())
+ solAssert(function.kind() == FunctionType::Kind::DelegateCall || function.kind() == FunctionType::Kind::Internal, "");
+ switch (function.kind())
{
- case Location::Internal:
+ case FunctionType::Kind::Internal:
{
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
@@ -538,16 +537,16 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context.adjustStackOffset(returnParametersSize - parameterSize - 1);
break;
}
- case Location::External:
- case Location::CallCode:
- case Location::DelegateCall:
- case Location::Bare:
- case Location::BareCallCode:
- case Location::BareDelegateCall:
+ case FunctionType::Kind::External:
+ case FunctionType::Kind::CallCode:
+ case FunctionType::Kind::DelegateCall:
+ case FunctionType::Kind::Bare:
+ case FunctionType::Kind::BareCallCode:
+ case FunctionType::Kind::BareDelegateCall:
_functionCall.expression().accept(*this);
appendExternalFunctionCall(function, arguments);
break;
- case Location::Creation:
+ case FunctionType::Kind::Creation:
{
_functionCall.expression().accept(*this);
solAssert(!function.gasSet(), "Gas limit set for contract creation.");
@@ -592,7 +591,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << swapInstruction(1) << Instruction::POP;
break;
}
- case Location::SetGas:
+ case FunctionType::Kind::SetGas:
{
// stack layout: contract_address function_id [gas] [value]
_functionCall.expression().accept(*this);
@@ -608,7 +607,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::POP;
break;
}
- case Location::SetValue:
+ case FunctionType::Kind::SetValue:
// stack layout: contract_address function_id [gas] [value]
_functionCall.expression().accept(*this);
// Note that function is not the original function, but the ".value" function.
@@ -617,8 +616,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::POP;
arguments.front()->accept(*this);
break;
- case Location::Send:
- case Location::Transfer:
+ case FunctionType::Kind::Send:
+ case FunctionType::Kind::Transfer:
_functionCall.expression().accept(*this);
// Provide the gas stipend manually at first because we may send zero ether.
// Will be zeroed if we send more than zero ether.
@@ -637,7 +636,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
TypePointers{},
strings(),
strings(),
- Location::Bare,
+ FunctionType::Kind::Bare,
false,
nullptr,
false,
@@ -647,24 +646,24 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
),
{}
);
- if (function.location() == Location::Transfer)
+ if (function.kind() == FunctionType::Kind::Transfer)
{
// Check if zero (out of stack or not enough balance).
m_context << Instruction::ISZERO;
m_context.appendConditionalInvalid();
}
break;
- case Location::Selfdestruct:
+ case FunctionType::Kind::Selfdestruct:
arguments.front()->accept(*this);
utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), true);
m_context << Instruction::SELFDESTRUCT;
break;
- case Location::Revert:
+ case FunctionType::Kind::Revert:
// memory offset returned - zero length
m_context << u256(0) << u256(0);
m_context << Instruction::REVERT;
break;
- case Location::SHA3:
+ case FunctionType::Kind::SHA3:
{
TypePointers argumentTypes;
for (auto const& arg: arguments)
@@ -678,13 +677,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::SHA3;
break;
}
- case Location::Log0:
- case Location::Log1:
- case Location::Log2:
- case Location::Log3:
- case Location::Log4:
+ case FunctionType::Kind::Log0:
+ case FunctionType::Kind::Log1:
+ case FunctionType::Kind::Log2:
+ case FunctionType::Kind::Log3:
+ case FunctionType::Kind::Log4:
{
- unsigned logNumber = int(function.location()) - int(Location::Log0);
+ unsigned logNumber = int(function.kind()) - int(FunctionType::Kind::Log0);
for (unsigned arg = logNumber; arg > 0; --arg)
{
arguments[arg]->accept(*this);
@@ -701,7 +700,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << logInstruction(logNumber);
break;
}
- case Location::Event:
+ case FunctionType::Kind::Event:
{
_functionCall.expression().accept(*this);
auto const& event = dynamic_cast<EventDefinition const&>(function.declaration());
@@ -755,50 +754,50 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << logInstruction(numIndexed);
break;
}
- case Location::BlockHash:
+ case FunctionType::Kind::BlockHash:
{
arguments[0]->accept(*this);
utils().convertType(*arguments[0]->annotation().type, *function.parameterTypes()[0], true);
m_context << Instruction::BLOCKHASH;
break;
}
- case Location::AddMod:
- case Location::MulMod:
+ case FunctionType::Kind::AddMod:
+ case FunctionType::Kind::MulMod:
{
for (unsigned i = 0; i < 3; i ++)
{
arguments[2 - i]->accept(*this);
utils().convertType(*arguments[2 - i]->annotation().type, IntegerType(256));
}
- if (function.location() == Location::AddMod)
+ if (function.kind() == FunctionType::Kind::AddMod)
m_context << Instruction::ADDMOD;
else
m_context << Instruction::MULMOD;
break;
}
- case Location::ECRecover:
- case Location::SHA256:
- case Location::RIPEMD160:
+ case FunctionType::Kind::ECRecover:
+ case FunctionType::Kind::SHA256:
+ case FunctionType::Kind::RIPEMD160:
{
_functionCall.expression().accept(*this);
- static const map<Location, u256> contractAddresses{{Location::ECRecover, 1},
- {Location::SHA256, 2},
- {Location::RIPEMD160, 3}};
- m_context << contractAddresses.find(function.location())->second;
+ static const map<FunctionType::Kind, u256> contractAddresses{{FunctionType::Kind::ECRecover, 1},
+ {FunctionType::Kind::SHA256, 2},
+ {FunctionType::Kind::RIPEMD160, 3}};
+ m_context << contractAddresses.find(function.kind())->second;
for (unsigned i = function.sizeOnStack(); i > 0; --i)
m_context << swapInstruction(i);
appendExternalFunctionCall(function, arguments);
break;
}
- case Location::ByteArrayPush:
- case Location::ArrayPush:
+ case FunctionType::Kind::ByteArrayPush:
+ case FunctionType::Kind::ArrayPush:
{
_functionCall.expression().accept(*this);
solAssert(function.parameterTypes().size() == 1, "");
solAssert(!!function.parameterTypes()[0], "");
TypePointer paramType = function.parameterTypes()[0];
shared_ptr<ArrayType> arrayType =
- function.location() == Location::ArrayPush ?
+ function.kind() == FunctionType::Kind::ArrayPush ?
make_shared<ArrayType>(DataLocation::Storage, paramType) :
make_shared<ArrayType>(DataLocation::Storage);
// get the current length
@@ -822,13 +821,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
utils().moveToStackTop(1 + type->sizeOnStack());
utils().moveToStackTop(1 + type->sizeOnStack());
// stack: newLength argValue storageSlot slotOffset
- if (function.location() == Location::ArrayPush)
+ if (function.kind() == FunctionType::Kind::ArrayPush)
StorageItem(m_context, *paramType).storeValue(*type, _functionCall.location(), true);
else
StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true);
break;
}
- case Location::ObjectCreation:
+ case FunctionType::Kind::ObjectCreation:
{
// Will allocate at the end of memory (MSIZE) and not write at all unless the base
// type is dynamically sized.
@@ -878,15 +877,15 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::POP;
break;
}
- case Location::Assert:
- case Location::Require:
+ case FunctionType::Kind::Assert:
+ case FunctionType::Kind::Require:
{
arguments.front()->accept(*this);
utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), false);
// jump if condition was met
m_context << Instruction::ISZERO << Instruction::ISZERO;
auto success = m_context.appendConditionalJump();
- if (function.location() == Location::Assert)
+ if (function.kind() == FunctionType::Kind::Assert)
// condition was not met, flag an error
m_context << Instruction::INVALID;
else
@@ -922,7 +921,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
*funType->selfType(),
true
);
- if (funType->location() == FunctionType::Location::Internal)
+ if (funType->kind() == FunctionType::Kind::Internal)
{
FunctionDefinition const& funDef = dynamic_cast<decltype(funDef)>(funType->declaration());
utils().pushCombinedFunctionEntryLabel(funDef);
@@ -930,7 +929,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
}
else
{
- solAssert(funType->location() == FunctionType::Location::DelegateCall, "");
+ solAssert(funType->kind() == FunctionType::Kind::DelegateCall, "");
auto contract = dynamic_cast<ContractDefinition const*>(funType->declaration().scope());
solAssert(contract && contract->isLibrary(), "");
m_context.appendLibraryAddress(contract->fullyQualifiedName());
@@ -949,9 +948,9 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
solAssert(_memberAccess.annotation().type, "_memberAccess has no type");
if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
{
- switch (funType->location())
+ switch (funType->kind())
{
- case FunctionType::Location::Internal:
+ case FunctionType::Kind::Internal:
// We do not visit the expression here on purpose, because in the case of an
// internal library function call, this would push the library address forcing
// us to link against it although we actually do not need it.
@@ -960,31 +959,31 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
else
solAssert(false, "Function not found in member access");
break;
- case FunctionType::Location::Event:
+ case FunctionType::Kind::Event:
if (!dynamic_cast<EventDefinition const*>(_memberAccess.annotation().referencedDeclaration))
solAssert(false, "event not found");
// no-op, because the parent node will do the job
break;
- case FunctionType::Location::External:
- case FunctionType::Location::Creation:
- case FunctionType::Location::DelegateCall:
- case FunctionType::Location::CallCode:
- case FunctionType::Location::Send:
- case FunctionType::Location::Bare:
- case FunctionType::Location::BareCallCode:
- case FunctionType::Location::BareDelegateCall:
- case FunctionType::Location::Transfer:
+ case FunctionType::Kind::External:
+ case FunctionType::Kind::Creation:
+ case FunctionType::Kind::DelegateCall:
+ case FunctionType::Kind::CallCode:
+ case FunctionType::Kind::Send:
+ case FunctionType::Kind::Bare:
+ case FunctionType::Kind::BareCallCode:
+ case FunctionType::Kind::BareDelegateCall:
+ case FunctionType::Kind::Transfer:
_memberAccess.expression().accept(*this);
m_context << funType->externalIdentifier();
break;
- case FunctionType::Location::Log0:
- case FunctionType::Location::Log1:
- case FunctionType::Location::Log2:
- case FunctionType::Location::Log3:
- case FunctionType::Location::Log4:
- case FunctionType::Location::ECRecover:
- case FunctionType::Location::SHA256:
- case FunctionType::Location::RIPEMD160:
+ case FunctionType::Kind::Log0:
+ case FunctionType::Kind::Log1:
+ case FunctionType::Kind::Log2:
+ case FunctionType::Kind::Log3:
+ case FunctionType::Kind::Log4:
+ case FunctionType::Kind::ECRecover:
+ case FunctionType::Kind::SHA256:
+ case FunctionType::Kind::RIPEMD160:
default:
solAssert(false, "unsupported member function");
}
@@ -1372,7 +1371,7 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type
{
if (FunctionType const* funType = dynamic_cast<decltype(funType)>(&_type))
{
- if (funType->location() == FunctionType::Location::Internal)
+ if (funType->kind() == FunctionType::Kind::Internal)
{
// We have to remove the upper bits (construction time value) because they might
// be "unknown" in one of the operands and not in the other.
@@ -1555,11 +1554,10 @@ void ExpressionCompiler::appendExternalFunctionCall(
if (_functionType.bound())
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
- using FunctionKind = FunctionType::Location;
- FunctionKind funKind = _functionType.location();
- bool returnSuccessCondition = funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode;
- bool isCallCode = funKind == FunctionKind::BareCallCode || funKind == FunctionKind::CallCode;
- bool isDelegateCall = funKind == FunctionKind::BareDelegateCall || funKind == FunctionKind::DelegateCall;
+ auto funKind = _functionType.kind();
+ bool returnSuccessCondition = funKind == FunctionType::Kind::Bare || funKind == FunctionType::Kind::BareCallCode;
+ bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
+ bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
unsigned retSize = 0;
if (returnSuccessCondition)
@@ -1576,7 +1574,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
TypePointers parameterTypes = _functionType.parameterTypes();
bool manualFunctionId = false;
if (
- (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode || funKind == FunctionKind::BareDelegateCall) &&
+ (funKind == FunctionType::Kind::Bare || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall) &&
!_arguments.empty()
)
{
@@ -1611,7 +1609,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
argumentTypes.push_back(_arguments[i]->annotation().type);
}
- if (funKind == FunctionKind::ECRecover)
+ if (funKind == FunctionType::Kind::ECRecover)
{
// Clears 32 bytes of currently free memory and advances free memory pointer.
// Output area will be "start of input area" - 32.
@@ -1667,7 +1665,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
// put on stack: <size of output> <memory pos of output> <size of input> <memory pos of input>
m_context << u256(retSize);
utils().fetchFreeMemoryPointer(); // This is the start of input
- if (funKind == FunctionKind::ECRecover)
+ if (funKind == FunctionType::Kind::ECRecover)
{
// In this case, output is 32 bytes before input and has already been cleared.
m_context << u256(32) << Instruction::DUP2 << Instruction::SUB << Instruction::SWAP1;
@@ -1693,7 +1691,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
bool existenceChecked = false;
// Check the the target contract exists (has code) for non-low-level calls.
- if (funKind == FunctionKind::External || funKind == FunctionKind::CallCode || funKind == FunctionKind::DelegateCall)
+ if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::CallCode || funKind == FunctionType::Kind::DelegateCall)
{
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
m_context.appendConditionalInvalid();
@@ -1741,14 +1739,14 @@ void ExpressionCompiler::appendExternalFunctionCall(
{
// already there
}
- else if (funKind == FunctionKind::RIPEMD160)
+ else if (funKind == FunctionType::Kind::RIPEMD160)
{
// fix: built-in contract returns right-aligned data
utils().fetchFreeMemoryPointer();
utils().loadFromMemoryDynamic(IntegerType(160), false, true, false);
utils().convertType(IntegerType(160), FixedBytesType(20));
}
- else if (funKind == FunctionKind::ECRecover)
+ else if (funKind == FunctionType::Kind::ECRecover)
{
// Output is 32 bytes before input / free mem pointer.
// Failing ecrecover cannot be detected, so we clear output before the call.
diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp
index b9e141d8..a74a3d74 100644
--- a/libsolidity/codegen/LValue.cpp
+++ b/libsolidity/codegen/LValue.cpp
@@ -199,7 +199,7 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
}
else if (FunctionType const* fun = dynamic_cast<decltype(fun)>(m_dataType))
{
- if (fun->location() == FunctionType::Location::External)
+ if (fun->kind() == FunctionType::Kind::External)
{
CompilerUtils(m_context).splitExternalFunctionType(false);
cleaned = true;
@@ -256,7 +256,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
if (FunctionType const* fun = dynamic_cast<decltype(fun)>(m_dataType))
{
solAssert(_sourceType == *m_dataType, "function item stored but target is not equal to source");
- if (fun->location() == FunctionType::Location::External)
+ if (fun->kind() == FunctionType::Kind::External)
// Combine the two-item function type into a single stack slot.
utils.combineExternalFunctionType(false);
else
diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp
index 2903a4e3..b6f17907 100644
--- a/libsolidity/formal/Why3Translator.cpp
+++ b/libsolidity/formal/Why3Translator.cpp
@@ -588,14 +588,14 @@ bool Why3Translator::visit(FunctionCall const& _node)
return true;
}
FunctionType const& function = dynamic_cast<FunctionType const&>(*_node.expression().annotation().type);
- switch (function.location())
+ switch (function.kind())
{
- case FunctionType::Location::AddMod:
- case FunctionType::Location::MulMod:
+ case FunctionType::Kind::AddMod:
+ case FunctionType::Kind::MulMod:
{
//@todo require that third parameter is not zero
add("(of_int (mod (Int.(");
- add(function.location() == FunctionType::Location::AddMod ? "+" : "*");
+ add(function.kind() == FunctionType::Kind::AddMod ? "+" : "*");
add(") (to_int ");
_node.arguments().at(0)->accept(*this);
add(") (to_int ");
@@ -605,7 +605,7 @@ bool Why3Translator::visit(FunctionCall const& _node)
add(")))");
return false;
}
- case FunctionType::Location::Internal:
+ case FunctionType::Kind::Internal:
{
if (!_node.names().empty())
{
@@ -626,7 +626,7 @@ bool Why3Translator::visit(FunctionCall const& _node)
add(")");
return false;
}
- case FunctionType::Location::Bare:
+ case FunctionType::Kind::Bare:
{
if (!_node.arguments().empty())
{
@@ -654,7 +654,7 @@ bool Why3Translator::visit(FunctionCall const& _node)
add(")");
return false;
}
- case FunctionType::Location::SetValue:
+ case FunctionType::Kind::SetValue:
{
add("let amount = ");
solAssert(_node.arguments().size() == 1, "");
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index a3ddb61d..dad05a78 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -21,6 +21,9 @@
#include <libsolidity/inlineasm/AsmAnalysis.h>
#include <libsolidity/inlineasm/AsmData.h>
+#include <libsolidity/inlineasm/AsmScopeFiller.h>
+#include <libsolidity/inlineasm/AsmScope.h>
+#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/interface/Utils.h>
@@ -35,146 +38,363 @@ using namespace dev;
using namespace dev::solidity;
using namespace dev::solidity::assembly;
-
-bool Scope::registerLabel(string const& _name)
+AsmAnalyzer::AsmAnalyzer(
+ AsmAnalysisInfo& _analysisInfo,
+ ErrorList& _errors,
+ ExternalIdentifierAccess::Resolver const& _resolver
+):
+ m_resolver(_resolver), m_info(_analysisInfo), m_errors(_errors)
{
- if (exists(_name))
- return false;
- identifiers[_name] = Label();
- return true;
}
-bool Scope::registerVariable(string const& _name)
+bool AsmAnalyzer::analyze(Block const& _block)
{
- if (exists(_name))
+ if (!(ScopeFiller(m_info.scopes, m_errors))(_block))
return false;
- identifiers[_name] = Variable();
- return true;
-}
-bool Scope::registerFunction(string const& _name, size_t _arguments, size_t _returns)
-{
- if (exists(_name))
- return false;
- identifiers[_name] = Function(_arguments, _returns);
- return true;
+ return (*this)(_block);
}
-Scope::Identifier* Scope::lookup(string const& _name)
+bool AsmAnalyzer::operator()(Label const& _label)
{
- if (identifiers.count(_name))
- return &identifiers[_name];
- else if (superScope && !closedScope)
- return superScope->lookup(_name);
- else
- return nullptr;
-}
-
-bool Scope::exists(string const& _name)
-{
- if (identifiers.count(_name))
- return true;
- else if (superScope)
- return superScope->exists(_name);
- else
- return false;
+ m_info.stackHeightInfo[&_label] = m_stackHeight;
+ return true;
}
-AsmAnalyzer::AsmAnalyzer(AsmAnalyzer::Scopes& _scopes, ErrorList& _errors):
- m_scopes(_scopes), m_errors(_errors)
+bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction)
{
- // Make the Solidity ErrorTag available to inline assembly
- m_scopes[nullptr] = make_shared<Scope>();
- Scope::Label errorLabel;
- errorLabel.id = Scope::Label::errorLabelId;
- m_scopes[nullptr]->identifiers["invalidJumpLabel"] = errorLabel;
- m_currentScope = m_scopes[nullptr].get();
+ auto const& info = instructionInfo(_instruction.instruction);
+ m_stackHeight += info.ret - info.args;
+ m_info.stackHeightInfo[&_instruction] = m_stackHeight;
+ return true;
}
bool AsmAnalyzer::operator()(assembly::Literal const& _literal)
{
+ ++m_stackHeight;
if (!_literal.isNumber && _literal.value.size() > 32)
{
m_errors.push_back(make_shared<Error>(
Error::Type::TypeError,
- "String literal too long (" + boost::lexical_cast<std::string>(_literal.value.size()) + " > 32)"
+ "String literal too long (" + boost::lexical_cast<std::string>(_literal.value.size()) + " > 32)",
+ _literal.location
));
return false;
}
+ m_info.stackHeightInfo[&_literal] = m_stackHeight;
return true;
}
+bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier)
+{
+ size_t numErrorsBefore = m_errors.size();
+ bool success = true;
+ if (m_currentScope->lookup(_identifier.name, Scope::Visitor(
+ [&](Scope::Variable const& _var)
+ {
+ if (!_var.active)
+ {
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::DeclarationError,
+ "Variable " + _identifier.name + " used before it was declared.",
+ _identifier.location
+ ));
+ success = false;
+ }
+ ++m_stackHeight;
+ },
+ [&](Scope::Label const&)
+ {
+ ++m_stackHeight;
+ },
+ [&](Scope::Function const&)
+ {
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::TypeError,
+ "Function " + _identifier.name + " used without being called.",
+ _identifier.location
+ ));
+ success = false;
+ }
+ )))
+ {
+ }
+ else
+ {
+ size_t stackSize(-1);
+ if (m_resolver)
+ stackSize = m_resolver(_identifier, IdentifierContext::RValue);
+ if (stackSize == size_t(-1))
+ {
+ // Only add an error message if the callback did not do it.
+ if (numErrorsBefore == m_errors.size())
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::DeclarationError,
+ "Identifier not found.",
+ _identifier.location
+ ));
+ success = false;
+ }
+ m_stackHeight += stackSize == size_t(-1) ? 1 : stackSize;
+ }
+ m_info.stackHeightInfo[&_identifier] = m_stackHeight;
+ return success;
+}
+
bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
{
bool success = true;
for (auto const& arg: _instr.arguments | boost::adaptors::reversed)
+ {
+ int const stackHeight = m_stackHeight;
if (!boost::apply_visitor(*this, arg))
success = false;
+ if (!expectDeposit(1, stackHeight, locationOf(arg)))
+ success = false;
+ }
+ // Parser already checks that the number of arguments is correct.
+ solAssert(instructionInfo(_instr.instruction.instruction).args == int(_instr.arguments.size()), "");
if (!(*this)(_instr.instruction))
success = false;
+ m_info.stackHeightInfo[&_instr] = m_stackHeight;
return success;
}
-bool AsmAnalyzer::operator()(Label const& _item)
+bool AsmAnalyzer::operator()(assembly::Assignment const& _assignment)
{
- if (!m_currentScope->registerLabel(_item.name))
- {
- //@TODO secondary location
- m_errors.push_back(make_shared<Error>(
- Error::Type::DeclarationError,
- "Label name " + _item.name + " already taken in this scope.",
- _item.location
- ));
- return false;
- }
- return true;
+ bool success = checkAssignment(_assignment.variableName, size_t(-1));
+ m_info.stackHeightInfo[&_assignment] = m_stackHeight;
+ return success;
}
+
bool AsmAnalyzer::operator()(FunctionalAssignment const& _assignment)
{
- return boost::apply_visitor(*this, *_assignment.value);
+ int const stackHeight = m_stackHeight;
+ bool success = boost::apply_visitor(*this, *_assignment.value);
+ solAssert(m_stackHeight >= stackHeight, "Negative value size.");
+ if (!checkAssignment(_assignment.variableName, m_stackHeight - stackHeight))
+ success = false;
+ m_info.stackHeightInfo[&_assignment] = m_stackHeight;
+ return success;
}
bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl)
{
+ int const stackHeight = m_stackHeight;
bool success = boost::apply_visitor(*this, *_varDecl.value);
- if (!m_currentScope->registerVariable(_varDecl.name))
- {
- //@TODO secondary location
- m_errors.push_back(make_shared<Error>(
- Error::Type::DeclarationError,
- "Variable name " + _varDecl.name + " already taken in this scope.",
- _varDecl.location
- ));
- success = false;
- }
+ solAssert(m_stackHeight - stackHeight == 1, "Invalid value size.");
+ boost::get<Scope::Variable>(m_currentScope->identifiers.at(_varDecl.name)).active = true;
+ m_info.stackHeightInfo[&_varDecl] = m_stackHeight;
return success;
}
-bool AsmAnalyzer::operator()(assembly::FunctionDefinition const&)
+bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef)
{
- // TODO - we cannot throw an exception here because of some tests.
- return true;
+ Scope& bodyScope = scope(&_funDef.body);
+ for (auto const& var: _funDef.arguments + _funDef.returns)
+ boost::get<Scope::Variable>(bodyScope.identifiers.at(var)).active = true;
+
+ int const stackHeight = m_stackHeight;
+ m_stackHeight = _funDef.arguments.size() + _funDef.returns.size();
+ m_virtualVariablesInNextBlock = m_stackHeight;
+
+ bool success = (*this)(_funDef.body);
+
+ m_stackHeight = stackHeight;
+ m_info.stackHeightInfo[&_funDef] = m_stackHeight;
+ return success;
}
-bool AsmAnalyzer::operator()(assembly::FunctionCall const&)
+bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall)
{
- // TODO - we cannot throw an exception here because of some tests.
- return true;
+ bool success = true;
+ size_t arguments = 0;
+ size_t returns = 0;
+ if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor(
+ [&](Scope::Variable const&)
+ {
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::TypeError,
+ "Attempt to call variable instead of function.",
+ _funCall.functionName.location
+ ));
+ success = false;
+ },
+ [&](Scope::Label const&)
+ {
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::TypeError,
+ "Attempt to call label instead of function.",
+ _funCall.functionName.location
+ ));
+ success = false;
+ },
+ [&](Scope::Function const& _fun)
+ {
+ arguments = _fun.arguments;
+ returns = _fun.returns;
+ }
+ )))
+ {
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::DeclarationError,
+ "Function not found.",
+ _funCall.functionName.location
+ ));
+ success = false;
+ }
+ if (success)
+ {
+ if (_funCall.arguments.size() != arguments)
+ {
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::TypeError,
+ "Expected " +
+ boost::lexical_cast<string>(arguments) +
+ " arguments but got " +
+ boost::lexical_cast<string>(_funCall.arguments.size()) +
+ ".",
+ _funCall.functionName.location
+ ));
+ success = false;
+ }
+ }
+ for (auto const& arg: _funCall.arguments | boost::adaptors::reversed)
+ {
+ int const stackHeight = m_stackHeight;
+ if (!boost::apply_visitor(*this, arg))
+ success = false;
+ if (!expectDeposit(1, stackHeight, locationOf(arg)))
+ success = false;
+ }
+ m_stackHeight += int(returns) - int(arguments);
+ m_info.stackHeightInfo[&_funCall] = m_stackHeight;
+ return success;
}
bool AsmAnalyzer::operator()(Block const& _block)
{
bool success = true;
- auto scope = make_shared<Scope>();
- scope->superScope = m_currentScope;
- m_scopes[&_block] = scope;
- m_currentScope = scope.get();
+ m_currentScope = &scope(&_block);
+
+ int const initialStackHeight = m_stackHeight - m_virtualVariablesInNextBlock;
+ m_virtualVariablesInNextBlock = 0;
for (auto const& s: _block.statements)
if (!boost::apply_visitor(*this, s))
success = false;
+ for (auto const& identifier: scope(&_block).identifiers)
+ if (identifier.second.type() == typeid(Scope::Variable))
+ --m_stackHeight;
+
+ int const stackDiff = m_stackHeight - initialStackHeight;
+ if (stackDiff != 0)
+ {
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::DeclarationError,
+ "Unbalanced stack at the end of a block: " +
+ (
+ stackDiff > 0 ?
+ to_string(stackDiff) + string(" surplus item(s).") :
+ to_string(-stackDiff) + string(" missing item(s).")
+ ),
+ _block.location
+ ));
+ success = false;
+ }
+
m_currentScope = m_currentScope->superScope;
+ m_info.stackHeightInfo[&_block] = m_stackHeight;
+ return success;
+}
+
+bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t _valueSize)
+{
+ bool success = true;
+ size_t numErrorsBefore = m_errors.size();
+ size_t variableSize(-1);
+ if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name))
+ {
+ // Check that it is a variable
+ if (var->type() != typeid(Scope::Variable))
+ {
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::TypeError,
+ "Assignment requires variable.",
+ _variable.location
+ ));
+ success = false;
+ }
+ else if (!boost::get<Scope::Variable>(*var).active)
+ {
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::DeclarationError,
+ "Variable " + _variable.name + " used before it was declared.",
+ _variable.location
+ ));
+ success = false;
+ }
+ variableSize = 1;
+ }
+ else if (m_resolver)
+ variableSize = m_resolver(_variable, IdentifierContext::LValue);
+ if (variableSize == size_t(-1))
+ {
+ // Only add message if the callback did not.
+ if (numErrorsBefore == m_errors.size())
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::DeclarationError,
+ "Variable not found or variable not lvalue.",
+ _variable.location
+ ));
+ success = false;
+ }
+ if (_valueSize == size_t(-1))
+ _valueSize = variableSize == size_t(-1) ? 1 : variableSize;
+
+ m_stackHeight -= _valueSize;
+
+ if (_valueSize != variableSize && variableSize != size_t(-1))
+ {
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::TypeError,
+ "Variable size (" +
+ to_string(variableSize) +
+ ") and value size (" +
+ to_string(_valueSize) +
+ ") do not match.",
+ _variable.location
+ ));
+ success = false;
+ }
return success;
}
+
+bool AsmAnalyzer::expectDeposit(int const _deposit, int const _oldHeight, SourceLocation const& _location)
+{
+ int stackDiff = m_stackHeight - _oldHeight;
+ if (stackDiff != _deposit)
+ {
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::TypeError,
+ "Expected instruction(s) to deposit " +
+ boost::lexical_cast<string>(_deposit) +
+ " item(s) to the stack, but did deposit " +
+ boost::lexical_cast<string>(stackDiff) +
+ " item(s).",
+ _location
+ ));
+ return false;
+ }
+ else
+ return true;
+}
+
+Scope& AsmAnalyzer::scope(Block const* _block)
+{
+ auto scopePtr = m_info.scopes.at(_block);
+ solAssert(scopePtr, "Scope requested but not present.");
+ return *scopePtr;
+}
diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h
index 9726210d..426ee0d2 100644
--- a/libsolidity/inlineasm/AsmAnalysis.h
+++ b/libsolidity/inlineasm/AsmAnalysis.h
@@ -20,6 +20,8 @@
#pragma once
+#include <libsolidity/inlineasm/AsmStack.h>
+
#include <libsolidity/interface/Exceptions.h>
#include <boost/variant.hpp>
@@ -46,101 +48,32 @@ struct Assignment;
struct FunctionDefinition;
struct FunctionCall;
-template <class...>
-struct GenericVisitor{};
-
-template <class Visitable, class... Others>
-struct GenericVisitor<Visitable, Others...>: public GenericVisitor<Others...>
-{
- using GenericVisitor<Others...>::operator ();
- explicit GenericVisitor(
- std::function<void(Visitable&)> _visitor,
- std::function<void(Others&)>... _otherVisitors
- ):
- GenericVisitor<Others...>(_otherVisitors...),
- m_visitor(_visitor)
- {}
-
- void operator()(Visitable& _v) const { m_visitor(_v); }
-
- std::function<void(Visitable&)> m_visitor;
-};
-template <>
-struct GenericVisitor<>: public boost::static_visitor<> {
- void operator()() const {}
-};
-
-
-struct Scope
-{
- struct Variable
- {
- int stackHeight = 0;
- bool active = false;
- };
-
- struct Label
- {
- size_t id = unassignedLabelId;
- static const size_t errorLabelId = -1;
- static const size_t unassignedLabelId = 0;
- };
-
- struct Function
- {
- Function(size_t _arguments, size_t _returns): arguments(_arguments), returns(_returns) {}
- size_t arguments = 0;
- size_t returns = 0;
- };
-
- using Identifier = boost::variant<Variable, Label, Function>;
- using Visitor = GenericVisitor<Variable const, Label const, Function const>;
- using NonconstVisitor = GenericVisitor<Variable, Label, Function>;
-
- bool registerVariable(std::string const& _name);
- bool registerLabel(std::string const& _name);
- bool registerFunction(std::string const& _name, size_t _arguments, size_t _returns);
-
- /// Looks up the identifier in this or super scopes (stops and function and assembly boundaries)
- /// and returns a valid pointer if found or a nullptr if not found.
- /// The pointer will be invalidated if the scope is modified.
- Identifier* lookup(std::string const& _name);
- /// Looks up the identifier in this and super scopes (stops and function and assembly boundaries)
- /// and calls the visitor, returns false if not found.
- template <class V>
- bool lookup(std::string const& _name, V const& _visitor)
- {
- if (Identifier* id = lookup(_name))
- {
- boost::apply_visitor(_visitor, *id);
- return true;
- }
- else
- return false;
- }
- /// @returns true if the name exists in this scope or in super scopes (also searches
- /// across function and assembly boundaries).
- bool exists(std::string const& _name);
- Scope* superScope = nullptr;
- /// If true, identifiers from the super scope are not visible here, but they are still
- /// taken into account to prevent shadowing.
- bool closedScope = false;
- std::map<std::string, Identifier> identifiers;
-};
+struct Scope;
+struct AsmAnalysisInfo;
+/**
+ * Performs the full analysis stage, calls the ScopeFiller internally, then resolves
+ * references and performs other checks.
+ * If all these checks pass, code generation should not throw errors.
+ */
class AsmAnalyzer: public boost::static_visitor<bool>
{
public:
- using Scopes = std::map<assembly::Block const*, std::shared_ptr<Scope>>;
- AsmAnalyzer(Scopes& _scopes, ErrorList& _errors);
+ AsmAnalyzer(
+ AsmAnalysisInfo& _analysisInfo,
+ ErrorList& _errors,
+ ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver()
+ );
+
+ bool analyze(assembly::Block const& _block);
- bool operator()(assembly::Instruction const&) { return true; }
+ bool operator()(assembly::Instruction const&);
bool operator()(assembly::Literal const& _literal);
- bool operator()(assembly::Identifier const&) { return true; }
+ bool operator()(assembly::Identifier const&);
bool operator()(assembly::FunctionalInstruction const& _functionalInstruction);
bool operator()(assembly::Label const& _label);
- bool operator()(assembly::Assignment const&) { return true; }
+ bool operator()(assembly::Assignment const&);
bool operator()(assembly::FunctionalAssignment const& _functionalAssignment);
bool operator()(assembly::VariableDeclaration const& _variableDeclaration);
bool operator()(assembly::FunctionDefinition const& _functionDefinition);
@@ -148,8 +81,20 @@ public:
bool operator()(assembly::Block const& _block);
private:
+ /// Verifies that a variable to be assigned to exists and has the same size
+ /// as the value, @a _valueSize, unless that is equal to -1.
+ bool checkAssignment(assembly::Identifier const& _assignment, size_t _valueSize = size_t(-1));
+ bool expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location);
+ Scope& scope(assembly::Block const* _block);
+
+ /// This is used when we enter the body of a function definition. There, the parameters
+ /// and return parameters appear as variables which are already on the stack before
+ /// we enter the block.
+ int m_virtualVariablesInNextBlock = 0;
+ int m_stackHeight = 0;
+ ExternalIdentifierAccess::Resolver const& m_resolver;
Scope* m_currentScope = nullptr;
- Scopes& m_scopes;
+ AsmAnalysisInfo& m_info;
ErrorList& m_errors;
};
diff --git a/libsolidity/inlineasm/AsmAnalysisInfo.cpp b/libsolidity/inlineasm/AsmAnalysisInfo.cpp
new file mode 100644
index 00000000..22318b12
--- /dev/null
+++ b/libsolidity/inlineasm/AsmAnalysisInfo.cpp
@@ -0,0 +1,26 @@
+/*
+ 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/>.
+*/
+/**
+ * Information generated during analyzer part of inline assembly.
+ */
+
+#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
+
+#include <libsolidity/inlineasm/AsmScope.h>
+
+#include <ostream>
+
diff --git a/libsolidity/inlineasm/AsmAnalysisInfo.h b/libsolidity/inlineasm/AsmAnalysisInfo.h
new file mode 100644
index 00000000..e21eb2c5
--- /dev/null
+++ b/libsolidity/inlineasm/AsmAnalysisInfo.h
@@ -0,0 +1,61 @@
+/*
+ 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/>.
+*/
+/**
+ * Information generated during analyzer part of inline assembly.
+ */
+
+#pragma once
+
+#include <boost/variant.hpp>
+
+#include <map>
+#include <memory>
+
+namespace dev
+{
+namespace solidity
+{
+namespace assembly
+{
+
+struct Literal;
+struct Block;
+struct Label;
+struct FunctionalInstruction;
+struct FunctionalAssignment;
+struct VariableDeclaration;
+struct Instruction;
+struct Identifier;
+struct Assignment;
+struct FunctionDefinition;
+struct FunctionCall;
+
+struct Scope;
+
+using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Block>;
+
+struct AsmAnalysisInfo
+{
+ using StackHeightInfo = std::map<void const*, int>;
+ using Scopes = std::map<assembly::Block const*, std::shared_ptr<Scope>>;
+ Scopes scopes;
+ StackHeightInfo stackHeightInfo;
+};
+
+}
+}
+}
diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp
index 78a9ee27..9ef3e6e7 100644
--- a/libsolidity/inlineasm/AsmCodeGen.cpp
+++ b/libsolidity/inlineasm/AsmCodeGen.cpp
@@ -24,7 +24,9 @@
#include <libsolidity/inlineasm/AsmParser.h>
#include <libsolidity/inlineasm/AsmData.h>
+#include <libsolidity/inlineasm/AsmScope.h>
#include <libsolidity/inlineasm/AsmAnalysis.h>
+#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <libevmasm/Assembly.h>
#include <libevmasm/SourceLocation.h>
@@ -46,13 +48,8 @@ using namespace dev::solidity::assembly;
struct GeneratorState
{
- GeneratorState(ErrorList& _errors, eth::Assembly& _assembly):
- errors(_errors), assembly(_assembly) {}
-
- void addError(Error::Type _type, std::string const& _description, SourceLocation const& _location = SourceLocation())
- {
- errors.push_back(make_shared<Error>(_type, _description, _location));
- }
+ GeneratorState(ErrorList& _errors, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly):
+ errors(_errors), info(_analysisInfo), assembly(_assembly) {}
size_t newLabelId()
{
@@ -66,8 +63,8 @@ struct GeneratorState
return size_t(id);
}
- std::map<assembly::Block const*, shared_ptr<Scope>> scopes;
ErrorList& errors;
+ AsmAnalysisInfo info;
eth::Assembly& assembly;
};
@@ -80,13 +77,24 @@ public:
explicit CodeTransform(
GeneratorState& _state,
assembly::Block const& _block,
- assembly::CodeGenerator::IdentifierAccess const& _identifierAccess = assembly::CodeGenerator::IdentifierAccess()
+ assembly::ExternalIdentifierAccess const& _identifierAccess = assembly::ExternalIdentifierAccess()
+ ): CodeTransform(_state, _block, _identifierAccess, _state.assembly.deposit())
+ {
+ }
+
+private:
+ CodeTransform(
+ GeneratorState& _state,
+ assembly::Block const& _block,
+ assembly::ExternalIdentifierAccess const& _identifierAccess,
+ int _initialDeposit
):
m_state(_state),
- m_scope(*m_state.scopes.at(&_block)),
- m_initialDeposit(m_state.assembly.deposit()),
- m_identifierAccess(_identifierAccess)
+ m_scope(*m_state.info.scopes.at(&_block)),
+ m_identifierAccess(_identifierAccess),
+ m_initialDeposit(_initialDeposit)
{
+ int blockStartDeposit = m_state.assembly.deposit();
std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
m_state.assembly.setSourceLocation(_block.location);
@@ -96,31 +104,16 @@ public:
if (identifier.second.type() == typeid(Scope::Variable))
m_state.assembly.append(solidity::Instruction::POP);
- int deposit = m_state.assembly.deposit() - m_initialDeposit;
-
- // issue warnings for stack height discrepancies
- if (deposit < 0)
- {
- m_state.addError(
- Error::Type::Warning,
- "Inline assembly block is not balanced. It takes " + toString(-deposit) + " item(s) from the stack.",
- _block.location
- );
- }
- else if (deposit > 0)
- {
- m_state.addError(
- Error::Type::Warning,
- "Inline assembly block is not balanced. It leaves " + toString(deposit) + " item(s) on the stack.",
- _block.location
- );
- }
+ int deposit = m_state.assembly.deposit() - blockStartDeposit;
+ solAssert(deposit == 0, "Invalid stack height at end of block.");
}
+public:
void operator()(assembly::Instruction const& _instruction)
{
m_state.assembly.setSourceLocation(_instruction.location);
m_state.assembly.append(_instruction.instruction);
+ checkStackHeight(&_instruction);
}
void operator()(assembly::Literal const& _literal)
{
@@ -130,8 +123,9 @@ public:
else
{
solAssert(_literal.value.size() <= 32, "");
- m_state.assembly.append(_literal.value);
+ m_state.assembly.append(u256(h256(_literal.value, h256::FromBinary, h256::AlignLeft)));
}
+ checkStackHeight(&_literal);
}
void operator()(assembly::Identifier const& _identifier)
{
@@ -153,20 +147,18 @@ public:
},
[=](Scope::Function&)
{
- solAssert(false, "Not yet implemented");
+ solAssert(false, "Function not removed during desugaring.");
}
)))
{
+ return;
}
- else if (!m_identifierAccess || !m_identifierAccess(_identifier, m_state.assembly, CodeGenerator::IdentifierContext::RValue))
- {
- m_state.addError(
- Error::Type::DeclarationError,
- "Identifier not found or not unique",
- _identifier.location
- );
- m_state.assembly.append(u256(0));
- }
+ solAssert(
+ m_identifierAccess.generateCode,
+ "Identifier not found and no external access available."
+ );
+ m_identifierAccess.generateCode(_identifier, IdentifierContext::RValue, m_state.assembly);
+ checkStackHeight(&_identifier);
}
void operator()(FunctionalInstruction const& _instr)
{
@@ -174,9 +166,10 @@ public:
{
int height = m_state.assembly.deposit();
boost::apply_visitor(*this, *it);
- expectDeposit(1, height, locationOf(*it));
+ expectDeposit(1, height);
}
(*this)(_instr.instruction);
+ checkStackHeight(&_instr);
}
void operator()(assembly::FunctionCall const&)
{
@@ -186,36 +179,39 @@ public:
{
m_state.assembly.setSourceLocation(_label.location);
solAssert(m_scope.identifiers.count(_label.name), "");
- Scope::Label& label = boost::get<Scope::Label>(m_scope.identifiers[_label.name]);
+ Scope::Label& label = boost::get<Scope::Label>(m_scope.identifiers.at(_label.name));
assignLabelIdIfUnset(label);
m_state.assembly.append(eth::AssemblyItem(eth::Tag, label.id));
+ checkStackHeight(&_label);
}
void operator()(assembly::Assignment const& _assignment)
{
m_state.assembly.setSourceLocation(_assignment.location);
generateAssignment(_assignment.variableName, _assignment.location);
+ checkStackHeight(&_assignment);
}
void operator()(FunctionalAssignment const& _assignment)
{
int height = m_state.assembly.deposit();
boost::apply_visitor(*this, *_assignment.value);
- expectDeposit(1, height, locationOf(*_assignment.value));
+ expectDeposit(1, height);
m_state.assembly.setSourceLocation(_assignment.location);
generateAssignment(_assignment.variableName, _assignment.location);
+ checkStackHeight(&_assignment);
}
void operator()(assembly::VariableDeclaration const& _varDecl)
{
int height = m_state.assembly.deposit();
boost::apply_visitor(*this, *_varDecl.value);
- expectDeposit(1, height, locationOf(*_varDecl.value));
- solAssert(m_scope.identifiers.count(_varDecl.name), "");
- auto& var = boost::get<Scope::Variable>(m_scope.identifiers[_varDecl.name]);
+ expectDeposit(1, height);
+ auto& var = boost::get<Scope::Variable>(m_scope.identifiers.at(_varDecl.name));
var.stackHeight = height;
var.active = true;
}
void operator()(assembly::Block const& _block)
{
- CodeTransform(m_state, _block, m_identifierAccess);
+ CodeTransform(m_state, _block, m_identifierAccess, m_initialDeposit);
+ checkStackHeight(&_block);
}
void operator()(assembly::FunctionDefinition const&)
{
@@ -225,35 +221,22 @@ public:
private:
void generateAssignment(assembly::Identifier const& _variableName, SourceLocation const& _location)
{
- if (m_scope.lookup(_variableName.name, Scope::Visitor(
- [=](Scope::Variable const& _var)
- {
- if (int heightDiff = variableHeightDiff(_var, _location, true))
- m_state.assembly.append(solidity::swapInstruction(heightDiff - 1));
- m_state.assembly.append(solidity::Instruction::POP);
- },
- [=](Scope::Label const&)
- {
- m_state.addError(
- Error::Type::DeclarationError,
- "Label \"" + string(_variableName.name) + "\" used as variable."
- );
- },
- [=](Scope::Function const&)
- {
- m_state.addError(
- Error::Type::DeclarationError,
- "Function \"" + string(_variableName.name) + "\" used as variable."
- );
- }
- )))
+ auto var = m_scope.lookup(_variableName.name);
+ if (var)
{
+ Scope::Variable const& _var = boost::get<Scope::Variable>(*var);
+ if (int heightDiff = variableHeightDiff(_var, _location, true))
+ m_state.assembly.append(solidity::swapInstruction(heightDiff - 1));
+ m_state.assembly.append(solidity::Instruction::POP);
}
- else if (!m_identifierAccess || !m_identifierAccess(_variableName, m_state.assembly, CodeGenerator::IdentifierContext::LValue))
- m_state.addError(
- Error::Type::DeclarationError,
- "Identifier \"" + string(_variableName.name) + "\" not found, not unique or not lvalue."
+ else
+ {
+ solAssert(
+ m_identifierAccess.generateCode,
+ "Identifier not found and no external access available."
);
+ m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_state.assembly);
+ }
}
/// Determines the stack height difference to the given variables. Automatically generates
@@ -261,36 +244,33 @@ private:
/// errors and the (positive) stack height difference otherwise.
int variableHeightDiff(Scope::Variable const& _var, SourceLocation const& _location, bool _forSwap)
{
- if (!_var.active)
- {
- m_state.addError( Error::Type::TypeError, "Variable used before it was declared", _location);
- return 0;
- }
int heightDiff = m_state.assembly.deposit() - _var.stackHeight;
if (heightDiff <= (_forSwap ? 1 : 0) || heightDiff > (_forSwap ? 17 : 16))
{
- m_state.addError(
+ //@TODO move this to analysis phase.
+ m_state.errors.push_back(make_shared<Error>(
Error::Type::TypeError,
"Variable inaccessible, too deep inside stack (" + boost::lexical_cast<string>(heightDiff) + ")",
_location
- );
+ ));
return 0;
}
else
return heightDiff;
}
- void expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location)
+ void expectDeposit(int _deposit, int _oldHeight)
{
- if (m_state.assembly.deposit() != _oldHeight + 1)
- m_state.addError(Error::Type::TypeError,
- "Expected instruction(s) to deposit " +
- boost::lexical_cast<string>(_deposit) +
- " item(s) to the stack, but did deposit " +
- boost::lexical_cast<string>(m_state.assembly.deposit() - _oldHeight) +
- " item(s).",
- _location
- );
+ solAssert(m_state.assembly.deposit() == _oldHeight + _deposit, "Invalid stack deposit.");
+ }
+
+ void checkStackHeight(void const* _astElement)
+ {
+ solAssert(m_state.info.stackHeightInfo.count(_astElement), "Stack height for AST element not found.");
+ solAssert(
+ m_state.info.stackHeightInfo.at(_astElement) == m_state.assembly.deposit() - m_initialDeposit,
+ "Stack height mismatch between analysis and code generation phase."
+ );
}
/// Assigns the label's id to a value taken from eth::Assembly if it has not yet been set.
@@ -305,35 +285,29 @@ private:
GeneratorState& m_state;
Scope& m_scope;
+ ExternalIdentifierAccess m_identifierAccess;
int const m_initialDeposit;
- assembly::CodeGenerator::IdentifierAccess m_identifierAccess;
};
-bool assembly::CodeGenerator::typeCheck(assembly::CodeGenerator::IdentifierAccess const& _identifierAccess)
-{
- size_t initialErrorLen = m_errors.size();
- eth::Assembly assembly;
- GeneratorState state(m_errors, assembly);
- if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
- return false;
- CodeTransform(state, m_parsedData, _identifierAccess);
- return m_errors.size() == initialErrorLen;
-}
-
-eth::Assembly assembly::CodeGenerator::assemble(assembly::CodeGenerator::IdentifierAccess const& _identifierAccess)
+eth::Assembly assembly::CodeGenerator::assemble(
+ Block const& _parsedData,
+ AsmAnalysisInfo& _analysisInfo,
+ ExternalIdentifierAccess const& _identifierAccess
+)
{
eth::Assembly assembly;
- GeneratorState state(m_errors, assembly);
- if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
- solAssert(false, "Assembly error");
- CodeTransform(state, m_parsedData, _identifierAccess);
+ GeneratorState state(m_errors, _analysisInfo, assembly);
+ CodeTransform(state, _parsedData, _identifierAccess);
return assembly;
}
-void assembly::CodeGenerator::assemble(eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierAccess const& _identifierAccess)
+void assembly::CodeGenerator::assemble(
+ Block const& _parsedData,
+ AsmAnalysisInfo& _analysisInfo,
+ eth::Assembly& _assembly,
+ ExternalIdentifierAccess const& _identifierAccess
+)
{
- GeneratorState state(m_errors, _assembly);
- if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
- solAssert(false, "Assembly error");
- CodeTransform(state, m_parsedData, _identifierAccess);
+ GeneratorState state(m_errors, _analysisInfo, _assembly);
+ CodeTransform(state, _parsedData, _identifierAccess);
}
diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h
index bd71812e..e830e047 100644
--- a/libsolidity/inlineasm/AsmCodeGen.h
+++ b/libsolidity/inlineasm/AsmCodeGen.h
@@ -22,9 +22,11 @@
#pragma once
-#include <functional>
+#include <libsolidity/inlineasm/AsmAnalysis.h>
#include <libsolidity/interface/Exceptions.h>
+#include <functional>
+
namespace dev
{
namespace eth
@@ -36,30 +38,27 @@ namespace solidity
namespace assembly
{
struct Block;
-struct Identifier;
class CodeGenerator
{
public:
- enum class IdentifierContext { LValue, RValue };
- /// Function type that is called for external identifiers. Such a function should search for
- /// the identifier and append appropriate assembly items to the assembly. If in lvalue context,
- /// the value to assign is assumed to be on the stack and an assignment is to be performed.
- /// If in rvalue context, the function is assumed to append instructions to
- /// push the value of the identifier onto the stack. On error, the function should return false.
- using IdentifierAccess = std::function<bool(assembly::Identifier const&, eth::Assembly&, IdentifierContext)>;
- CodeGenerator(Block const& _parsedData, ErrorList& _errors):
- m_parsedData(_parsedData), m_errors(_errors) {}
- /// Performs type checks and @returns false on error.
- /// Actually runs the full code generation but discards the result.
- bool typeCheck(IdentifierAccess const& _identifierAccess = IdentifierAccess());
+ CodeGenerator(ErrorList& _errors):
+ m_errors(_errors) {}
/// Performs code generation and @returns the result.
- eth::Assembly assemble(IdentifierAccess const& _identifierAccess = IdentifierAccess());
+ eth::Assembly assemble(
+ Block const& _parsedData,
+ AsmAnalysisInfo& _analysisInfo,
+ ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
+ );
/// Performs code generation and appends generated to to _assembly.
- void assemble(eth::Assembly& _assembly, IdentifierAccess const& _identifierAccess = IdentifierAccess());
+ void assemble(
+ Block const& _parsedData,
+ AsmAnalysisInfo& _analysisInfo,
+ eth::Assembly& _assembly,
+ ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
+ );
private:
- Block const& m_parsedData;
ErrorList& m_errors;
};
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp
index 0fc0a34f..d7f78958 100644
--- a/libsolidity/inlineasm/AsmParser.cpp
+++ b/libsolidity/inlineasm/AsmParser.cpp
@@ -24,6 +24,7 @@
#include <ctype.h>
#include <algorithm>
#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/interface/Exceptions.h>
using namespace std;
using namespace dev;
@@ -68,12 +69,14 @@ assembly::Statement Parser::parseStatement()
return parseBlock();
case Token::Assign:
{
+ if (m_julia)
+ break;
assembly::Assignment assignment = createWithLocation<assembly::Assignment>();
m_scanner->next();
expectToken(Token::Colon);
assignment.variableName.location = location();
assignment.variableName.name = m_scanner->currentLiteral();
- if (instructions().count(assignment.variableName.name))
+ if (!m_julia && instructions().count(assignment.variableName.name))
fatalParserError("Identifier expected, got instruction name.");
assignment.location.end = endPosition();
expectToken(Token::Identifier);
@@ -105,7 +108,7 @@ assembly::Statement Parser::parseStatement()
{
// functional assignment
FunctionalAssignment funAss = createWithLocation<FunctionalAssignment>(identifier.location);
- if (instructions().count(identifier.name))
+ if (!m_julia && instructions().count(identifier.name))
fatalParserError("Cannot use instruction names for identifier names.");
m_scanner->next();
funAss.variableName = identifier;
@@ -180,7 +183,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
else
literal = m_scanner->currentLiteral();
// first search the set of instructions.
- if (instructions().count(literal))
+ if (!m_julia && instructions().count(literal))
{
dev::solidity::Instruction const& instr = instructions().at(literal);
if (_onlySinglePusher)
@@ -242,15 +245,13 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition()
{
expectToken(Token::Sub);
expectToken(Token::GreaterThan);
- expectToken(Token::LParen);
while (true)
{
funDef.returns.push_back(expectAsmIdentifier());
- if (m_scanner->currentToken() == Token::RParen)
+ if (m_scanner->currentToken() == Token::LBrace)
break;
expectToken(Token::Comma);
}
- expectToken(Token::RParen);
}
funDef.body = parseBlock();
funDef.location.end = funDef.body.location.end;
@@ -261,6 +262,7 @@ assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _in
{
if (_instruction.type() == typeid(Instruction))
{
+ solAssert(!m_julia, "Instructions are invalid in JULIA");
FunctionalInstruction ret;
ret.instruction = std::move(boost::get<Instruction>(_instruction));
ret.location = ret.instruction.location;
@@ -323,7 +325,7 @@ assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _in
string Parser::expectAsmIdentifier()
{
string name = m_scanner->currentLiteral();
- if (instructions().count(name))
+ if (!m_julia && instructions().count(name))
fatalParserError("Cannot use instruction names for identifier names.");
expectToken(Token::Identifier);
return name;
diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h
index 4b4a24ae..c55fd2ac 100644
--- a/libsolidity/inlineasm/AsmParser.h
+++ b/libsolidity/inlineasm/AsmParser.h
@@ -37,7 +37,7 @@ namespace assembly
class Parser: public ParserBase
{
public:
- Parser(ErrorList& _errors): ParserBase(_errors) {}
+ explicit Parser(ErrorList& _errors, bool _julia = false): ParserBase(_errors), m_julia(_julia) {}
/// Parses an inline assembly block starting with `{` and ending with `}`.
/// @returns an empty shared pointer on error.
@@ -70,6 +70,9 @@ protected:
FunctionDefinition parseFunctionDefinition();
Statement parseFunctionalInstruction(Statement&& _instruction);
std::string expectAsmIdentifier();
+
+private:
+ bool m_julia = false;
};
}
diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp
index a70b0b78..252e91f9 100644
--- a/libsolidity/inlineasm/AsmPrinter.cpp
+++ b/libsolidity/inlineasm/AsmPrinter.cpp
@@ -116,7 +116,7 @@ string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefin
{
string out = "function " + _functionDefinition.name + "(" + boost::algorithm::join(_functionDefinition.arguments, ", ") + ")";
if (!_functionDefinition.returns.empty())
- out += " -> (" + boost::algorithm::join(_functionDefinition.returns, ", ") + ")";
+ out += " -> " + boost::algorithm::join(_functionDefinition.returns, ", ");
return out + "\n" + (*this)(_functionDefinition.body);
}
diff --git a/libsolidity/inlineasm/AsmScope.cpp b/libsolidity/inlineasm/AsmScope.cpp
new file mode 100644
index 00000000..609dca16
--- /dev/null
+++ b/libsolidity/inlineasm/AsmScope.cpp
@@ -0,0 +1,79 @@
+/*
+ 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/>.
+*/
+/**
+ * Scopes for identifiers.
+ */
+
+#include <libsolidity/inlineasm/AsmScope.h>
+
+using namespace std;
+using namespace dev::solidity::assembly;
+
+
+bool Scope::registerLabel(string const& _name)
+{
+ if (exists(_name))
+ return false;
+ identifiers[_name] = Label();
+ return true;
+}
+
+bool Scope::registerVariable(string const& _name)
+{
+ if (exists(_name))
+ return false;
+ identifiers[_name] = Variable();
+ return true;
+}
+
+bool Scope::registerFunction(string const& _name, size_t _arguments, size_t _returns)
+{
+ if (exists(_name))
+ return false;
+ identifiers[_name] = Function(_arguments, _returns);
+ return true;
+}
+
+Scope::Identifier* Scope::lookup(string const& _name)
+{
+ bool crossedFunctionBoundary = false;
+ for (Scope* s = this; s; s = s->superScope)
+ {
+ auto id = s->identifiers.find(_name);
+ if (id != s->identifiers.end())
+ {
+ if (crossedFunctionBoundary && id->second.type() == typeid(Scope::Variable))
+ return nullptr;
+ else
+ return &id->second;
+ }
+
+ if (s->functionScope)
+ crossedFunctionBoundary = true;
+ }
+ return nullptr;
+}
+
+bool Scope::exists(string const& _name)
+{
+ if (identifiers.count(_name))
+ return true;
+ else if (superScope)
+ return superScope->exists(_name);
+ else
+ return false;
+}
diff --git a/libsolidity/inlineasm/AsmScope.h b/libsolidity/inlineasm/AsmScope.h
new file mode 100644
index 00000000..37e0f0b8
--- /dev/null
+++ b/libsolidity/inlineasm/AsmScope.h
@@ -0,0 +1,128 @@
+/*
+ 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/>.
+*/
+/**
+ * Scopes for identifiers.
+ */
+
+#pragma once
+
+#include <libsolidity/interface/Exceptions.h>
+
+#include <boost/variant.hpp>
+
+#include <functional>
+#include <memory>
+
+namespace dev
+{
+namespace solidity
+{
+namespace assembly
+{
+
+template <class...>
+struct GenericVisitor{};
+
+template <class Visitable, class... Others>
+struct GenericVisitor<Visitable, Others...>: public GenericVisitor<Others...>
+{
+ using GenericVisitor<Others...>::operator ();
+ explicit GenericVisitor(
+ std::function<void(Visitable&)> _visitor,
+ std::function<void(Others&)>... _otherVisitors
+ ):
+ GenericVisitor<Others...>(_otherVisitors...),
+ m_visitor(_visitor)
+ {}
+
+ void operator()(Visitable& _v) const { m_visitor(_v); }
+
+ std::function<void(Visitable&)> m_visitor;
+};
+template <>
+struct GenericVisitor<>: public boost::static_visitor<> {
+ void operator()() const {}
+};
+
+
+struct Scope
+{
+ struct Variable
+ {
+ /// Used during code generation to store the stack height. @todo move there.
+ int stackHeight = 0;
+ /// Used during analysis to check whether we already passed the declaration inside the block.
+ /// @todo move there.
+ bool active = false;
+ };
+
+ struct Label
+ {
+ size_t id = unassignedLabelId;
+ static const size_t errorLabelId = -1;
+ static const size_t unassignedLabelId = 0;
+ };
+
+ struct Function
+ {
+ Function(size_t _arguments, size_t _returns): arguments(_arguments), returns(_returns) {}
+ size_t arguments = 0;
+ size_t returns = 0;
+ };
+
+ using Identifier = boost::variant<Variable, Label, Function>;
+ using Visitor = GenericVisitor<Variable const, Label const, Function const>;
+ using NonconstVisitor = GenericVisitor<Variable, Label, Function>;
+
+ bool registerVariable(std::string const& _name);
+ bool registerLabel(std::string const& _name);
+ bool registerFunction(std::string const& _name, size_t _arguments, size_t _returns);
+
+ /// Looks up the identifier in this or super scopes and returns a valid pointer if found
+ /// or a nullptr if not found. Variable lookups up across function boundaries will fail, as
+ /// will any lookups across assembly boundaries.
+ /// The pointer will be invalidated if the scope is modified.
+ /// @param _crossedFunction if true, we already crossed a function boundary during recursive lookup
+ Identifier* lookup(std::string const& _name);
+ /// Looks up the identifier in this and super scopes (will not find variables across function
+ /// boundaries and generally stops at assembly boundaries) and calls the visitor, returns
+ /// false if not found.
+ template <class V>
+ bool lookup(std::string const& _name, V const& _visitor)
+ {
+ if (Identifier* id = lookup(_name))
+ {
+ boost::apply_visitor(_visitor, *id);
+ return true;
+ }
+ else
+ return false;
+ }
+ /// @returns true if the name exists in this scope or in super scopes (also searches
+ /// across function and assembly boundaries).
+ bool exists(std::string const& _name);
+
+ Scope* superScope = nullptr;
+ /// If true, variables from the super scope are not visible here (other identifiers are),
+ /// but they are still taken into account to prevent shadowing.
+ bool functionScope = false;
+ std::map<std::string, Identifier> identifiers;
+};
+
+}
+}
+}
diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp
new file mode 100644
index 00000000..de6fbdaa
--- /dev/null
+++ b/libsolidity/inlineasm/AsmScopeFiller.cpp
@@ -0,0 +1,130 @@
+/*
+ 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/>.
+*/
+/**
+ * Module responsible for registering identifiers inside their scopes.
+ */
+
+#include <libsolidity/inlineasm/AsmScopeFiller.h>
+
+#include <libsolidity/inlineasm/AsmData.h>
+#include <libsolidity/inlineasm/AsmScope.h>
+
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/Utils.h>
+
+#include <boost/range/adaptor/reversed.hpp>
+
+#include <memory>
+#include <functional>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+using namespace dev::solidity::assembly;
+
+ScopeFiller::ScopeFiller(ScopeFiller::Scopes& _scopes, ErrorList& _errors):
+ m_scopes(_scopes), m_errors(_errors)
+{
+ // Make the Solidity ErrorTag available to inline assembly
+ Scope::Label errorLabel;
+ errorLabel.id = Scope::Label::errorLabelId;
+ scope(nullptr).identifiers["invalidJumpLabel"] = errorLabel;
+ m_currentScope = &scope(nullptr);
+}
+
+bool ScopeFiller::operator()(Label const& _item)
+{
+ if (!m_currentScope->registerLabel(_item.name))
+ {
+ //@TODO secondary location
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::DeclarationError,
+ "Label name " + _item.name + " already taken in this scope.",
+ _item.location
+ ));
+ return false;
+ }
+ return true;
+}
+
+bool ScopeFiller::operator()(assembly::VariableDeclaration const& _varDecl)
+{
+ return registerVariable(_varDecl.name, _varDecl.location, *m_currentScope);
+}
+
+bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef)
+{
+ bool success = true;
+ if (!m_currentScope->registerFunction(_funDef.name, _funDef.arguments.size(), _funDef.returns.size()))
+ {
+ //@TODO secondary location
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::DeclarationError,
+ "Function name " + _funDef.name + " already taken in this scope.",
+ _funDef.location
+ ));
+ success = false;
+ }
+ Scope& body = scope(&_funDef.body);
+ body.superScope = m_currentScope;
+ body.functionScope = true;
+ for (auto const& var: _funDef.arguments + _funDef.returns)
+ if (!registerVariable(var, _funDef.location, body))
+ success = false;
+
+ if (!(*this)(_funDef.body))
+ success = false;
+
+ return success;
+}
+
+bool ScopeFiller::operator()(Block const& _block)
+{
+ bool success = true;
+ scope(&_block).superScope = m_currentScope;
+ m_currentScope = &scope(&_block);
+
+ for (auto const& s: _block.statements)
+ if (!boost::apply_visitor(*this, s))
+ success = false;
+
+ m_currentScope = m_currentScope->superScope;
+ return success;
+}
+
+bool ScopeFiller::registerVariable(string const& _name, SourceLocation const& _location, Scope& _scope)
+{
+ if (!_scope.registerVariable(_name))
+ {
+ //@TODO secondary location
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::DeclarationError,
+ "Variable name " + _name + " already taken in this scope.",
+ _location
+ ));
+ return false;
+ }
+ return true;
+}
+
+Scope& ScopeFiller::scope(Block const* _block)
+{
+ auto& scope = m_scopes[_block];
+ if (!scope)
+ scope = make_shared<Scope>();
+ return *scope;
+}
diff --git a/libsolidity/inlineasm/AsmScopeFiller.h b/libsolidity/inlineasm/AsmScopeFiller.h
new file mode 100644
index 00000000..bb62948b
--- /dev/null
+++ b/libsolidity/inlineasm/AsmScopeFiller.h
@@ -0,0 +1,89 @@
+/*
+ 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/>.
+*/
+/**
+ * Module responsible for registering identifiers inside their scopes.
+ */
+
+#pragma once
+
+#include <libsolidity/interface/Exceptions.h>
+
+#include <boost/variant.hpp>
+
+#include <functional>
+#include <memory>
+
+namespace dev
+{
+namespace solidity
+{
+namespace assembly
+{
+
+struct Literal;
+struct Block;
+struct Label;
+struct FunctionalInstruction;
+struct FunctionalAssignment;
+struct VariableDeclaration;
+struct Instruction;
+struct Identifier;
+struct Assignment;
+struct FunctionDefinition;
+struct FunctionCall;
+
+struct Scope;
+
+/**
+ * Fills scopes with identifiers and checks for name clashes.
+ * Does not resolve references.
+ */
+class ScopeFiller: public boost::static_visitor<bool>
+{
+public:
+ using Scopes = std::map<assembly::Block const*, std::shared_ptr<Scope>>;
+ ScopeFiller(Scopes& _scopes, ErrorList& _errors);
+
+ bool operator()(assembly::Instruction const&) { return true; }
+ bool operator()(assembly::Literal const&) { return true; }
+ bool operator()(assembly::Identifier const&) { return true; }
+ bool operator()(assembly::FunctionalInstruction const&) { return true; }
+ bool operator()(assembly::Label const& _label);
+ bool operator()(assembly::Assignment const&) { return true; }
+ bool operator()(assembly::FunctionalAssignment const&) { return true; }
+ bool operator()(assembly::VariableDeclaration const& _variableDeclaration);
+ bool operator()(assembly::FunctionDefinition const& _functionDefinition);
+ bool operator()(assembly::FunctionCall const&) { return true; }
+ bool operator()(assembly::Block const& _block);
+
+private:
+ bool registerVariable(
+ std::string const& _name,
+ SourceLocation const& _location,
+ Scope& _scope
+ );
+
+ Scope& scope(assembly::Block const* _block);
+
+ Scope* m_currentScope = nullptr;
+ Scopes& m_scopes;
+ ErrorList& m_errors;
+};
+
+}
+}
+}
diff --git a/libsolidity/inlineasm/AsmStack.cpp b/libsolidity/inlineasm/AsmStack.cpp
index 266136a1..c2a7d8ea 100644
--- a/libsolidity/inlineasm/AsmStack.cpp
+++ b/libsolidity/inlineasm/AsmStack.cpp
@@ -26,6 +26,7 @@
#include <libsolidity/inlineasm/AsmCodeGen.h>
#include <libsolidity/inlineasm/AsmPrinter.h>
#include <libsolidity/inlineasm/AsmAnalysis.h>
+#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <libsolidity/parsing/Scanner.h>
@@ -39,7 +40,10 @@ using namespace dev;
using namespace dev::solidity;
using namespace dev::solidity::assembly;
-bool InlineAssemblyStack::parse(shared_ptr<Scanner> const& _scanner)
+bool InlineAssemblyStack::parse(
+ shared_ptr<Scanner> const& _scanner,
+ ExternalIdentifierAccess::Resolver const& _resolver
+)
{
m_parserResult = make_shared<Block>();
Parser parser(m_errors);
@@ -48,8 +52,8 @@ bool InlineAssemblyStack::parse(shared_ptr<Scanner> const& _scanner)
return false;
*m_parserResult = std::move(*result);
- AsmAnalyzer::Scopes scopes;
- return (AsmAnalyzer(scopes, m_errors))(*m_parserResult);
+ AsmAnalysisInfo analysisInfo;
+ return (AsmAnalyzer(analysisInfo, m_errors, _resolver)).analyze(*m_parserResult);
}
string InlineAssemblyStack::toString()
@@ -59,14 +63,17 @@ string InlineAssemblyStack::toString()
eth::Assembly InlineAssemblyStack::assemble()
{
- CodeGenerator codeGen(*m_parserResult, m_errors);
- return codeGen.assemble();
+ AsmAnalysisInfo analysisInfo;
+ AsmAnalyzer analyzer(analysisInfo, m_errors);
+ solAssert(analyzer.analyze(*m_parserResult), "");
+ CodeGenerator codeGen(m_errors);
+ return codeGen.assemble(*m_parserResult, analysisInfo);
}
bool InlineAssemblyStack::parseAndAssemble(
string const& _input,
eth::Assembly& _assembly,
- CodeGenerator::IdentifierAccess const& _identifierAccess
+ ExternalIdentifierAccess const& _identifierAccess
)
{
ErrorList errors;
@@ -74,8 +81,12 @@ bool InlineAssemblyStack::parseAndAssemble(
auto parserResult = Parser(errors).parse(scanner);
if (!errors.empty())
return false;
+ solAssert(parserResult, "");
- CodeGenerator(*parserResult, errors).assemble(_assembly, _identifierAccess);
+ AsmAnalysisInfo analysisInfo;
+ AsmAnalyzer analyzer(analysisInfo, errors, _identifierAccess.resolve);
+ solAssert(analyzer.analyze(*parserResult), "");
+ CodeGenerator(errors).assemble(*parserResult, analysisInfo, _assembly, _identifierAccess);
// At this point, the assembly might be messed up, but we should throw an
// internal compiler error anyway.
diff --git a/libsolidity/inlineasm/AsmStack.h b/libsolidity/inlineasm/AsmStack.h
index 4d5a99a4..77a7e02a 100644
--- a/libsolidity/inlineasm/AsmStack.h
+++ b/libsolidity/inlineasm/AsmStack.h
@@ -22,10 +22,10 @@
#pragma once
+#include <libsolidity/interface/Exceptions.h>
+
#include <string>
#include <functional>
-#include <libsolidity/interface/Exceptions.h>
-#include <libsolidity/inlineasm/AsmCodeGen.h>
namespace dev
{
@@ -39,13 +39,34 @@ class Scanner;
namespace assembly
{
struct Block;
+struct Identifier;
+
+enum class IdentifierContext { LValue, RValue };
+
+/// Object that is used to resolve references and generate code for access to identifiers external
+/// to inline assembly (not used in standalone assembly mode).
+struct ExternalIdentifierAccess
+{
+ using Resolver = std::function<size_t(assembly::Identifier const&, IdentifierContext)>;
+ /// Resolve a an external reference given by the identifier in the given context.
+ /// @returns the size of the value (number of stack slots) or size_t(-1) if not found.
+ Resolver resolve;
+ using CodeGenerator = std::function<void(assembly::Identifier const&, IdentifierContext, eth::Assembly&)>;
+ /// Generate code for retrieving the value (rvalue context) or storing the value (lvalue context)
+ /// of an identifier. The code should be appended to the assembly. In rvalue context, the value is supposed
+ /// to be put onto the stack, in lvalue context, the value is assumed to be at the top of the stack.
+ CodeGenerator generateCode;
+};
class InlineAssemblyStack
{
public:
/// Parse the given inline assembly chunk starting with `{` and ending with the corresponding `}`.
/// @return false or error.
- bool parse(std::shared_ptr<Scanner> const& _scanner);
+ bool parse(
+ std::shared_ptr<Scanner> const& _scanner,
+ ExternalIdentifierAccess::Resolver const& _externalIdentifierResolver = ExternalIdentifierAccess::Resolver()
+ );
/// Converts the parser result back into a string form (not necessarily the same form
/// as the source form, but it should parse into the same parsed form again).
std::string toString();
@@ -56,7 +77,7 @@ public:
bool parseAndAssemble(
std::string const& _input,
eth::Assembly& _assembly,
- CodeGenerator::IdentifierAccess const& _identifierAccess = CodeGenerator::IdentifierAccess()
+ ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
);
ErrorList const& errors() const { return m_errors; }
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 6b0024ad..9c9c9614 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -38,6 +38,7 @@
#include <libsolidity/analysis/SyntaxChecker.h>
#include <libsolidity/codegen/Compiler.h>
#include <libsolidity/interface/InterfaceHandler.h>
+#include <libsolidity/interface/GasEstimator.h>
#include <libsolidity/formal/Why3Translator.h>
#include <libevmasm/Exceptions.h>
@@ -55,8 +56,8 @@ using namespace std;
using namespace dev;
using namespace dev::solidity;
-CompilerStack::CompilerStack(ReadFileCallback const& _readFile):
- m_readFile(_readFile), m_parseSuccessful(false) {}
+CompilerStack::CompilerStack(ReadFile::Callback const& _readFile):
+ m_readFile(_readFile) {}
void CompilerStack::setRemappings(vector<string> const& _remappings)
{
@@ -78,10 +79,12 @@ void CompilerStack::setRemappings(vector<string> const& _remappings)
void CompilerStack::reset(bool _keepSources)
{
- m_parseSuccessful = false;
if (_keepSources)
+ {
+ m_stackState = SourcesSet;
for (auto sourcePair: m_sources)
sourcePair.second.reset();
+ }
else
{
m_sources.clear();
@@ -93,6 +96,7 @@ void CompilerStack::reset(bool _keepSources)
m_sourceOrder.clear();
m_contracts.clear();
m_errors.clear();
+ m_stackState = Empty;
}
bool CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary)
@@ -101,6 +105,7 @@ bool CompilerStack::addSource(string const& _name, string const& _content, bool
reset(true);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
m_sources[_name].isLibrary = _isLibrary;
+ m_stackState = SourcesSet;
return existed;
}
@@ -113,9 +118,10 @@ void CompilerStack::setSource(string const& _sourceCode)
bool CompilerStack::parse()
{
//reset
+ if(m_stackState != SourcesSet)
+ return false;
m_errors.clear();
ASTNode::resetID();
- m_parseSuccessful = false;
if (SemVerVersion{string(VersionString)}.isPrerelease())
{
@@ -127,14 +133,12 @@ bool CompilerStack::parse()
vector<string> sourcesToParse;
for (auto const& s: m_sources)
sourcesToParse.push_back(s.first);
- map<string, SourceUnit const*> sourceUnitsByName;
for (size_t i = 0; i < sourcesToParse.size(); ++i)
{
string const& path = sourcesToParse[i];
Source& source = m_sources[path];
source.scanner->reset();
source.ast = Parser(m_errors).parse(source.scanner);
- sourceUnitsByName[path] = source.ast.get();
if (!source.ast)
solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error.");
else
@@ -149,10 +153,19 @@ bool CompilerStack::parse()
}
}
}
- if (!Error::containsOnlyWarnings(m_errors))
- // errors while parsing. should stop before type checking
+ if (Error::containsOnlyWarnings(m_errors))
+ {
+ m_stackState = ParsingSuccessful;
+ return true;
+ }
+ else
return false;
+}
+bool CompilerStack::analyze()
+{
+ if (m_stackState != ParsingSuccessful)
+ return false;
resolveImports();
bool noErrors = true;
@@ -172,6 +185,9 @@ bool CompilerStack::parse()
if (!resolver.registerDeclarations(*source->ast))
return false;
+ map<string, SourceUnit const*> sourceUnitsByName;
+ for (auto& source: m_sources)
+ sourceUnitsByName[source.first] = source.second.ast.get();
for (Source const* source: m_sourceOrder)
if (!resolver.performImports(*source->ast, sourceUnitsByName))
return false;
@@ -234,8 +250,13 @@ bool CompilerStack::parse()
noErrors = false;
}
- m_parseSuccessful = noErrors;
- return m_parseSuccessful;
+ if (noErrors)
+ {
+ m_stackState = AnalysisSuccessful;
+ return true;
+ }
+ else
+ return false;
}
bool CompilerStack::parse(string const& _sourceCode)
@@ -244,9 +265,20 @@ bool CompilerStack::parse(string const& _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_parseSuccessful)
+ if (m_stackState < AnalysisSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
vector<string> contractNames;
for (auto const& contract: m_contracts)
@@ -257,8 +289,8 @@ vector<string> CompilerStack::contractNames() const
bool CompilerStack::compile(bool _optimize, unsigned _runs, map<string, h160> const& _libraries)
{
- if (!m_parseSuccessful)
- if (!parse())
+ if (m_stackState < AnalysisSuccessful)
+ if (!parseAndAnalyze())
return false;
m_optimize = _optimize;
@@ -271,12 +303,13 @@ bool CompilerStack::compile(bool _optimize, unsigned _runs, map<string, h160> co
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
compileContract(*contract, compiledContracts);
this->link();
+ m_stackState = CompilationSuccessful;
return true;
}
bool CompilerStack::compile(string const& _sourceCode, bool _optimize, unsigned _runs)
{
- return parse(_sourceCode) && compile(_optimize, _runs);
+ return parseAndAnalyze(_sourceCode) && compile(_optimize, _runs);
}
void CompilerStack::link()
@@ -405,8 +438,9 @@ vector<string> CompilerStack::sourceNames() const
map<string, unsigned> CompilerStack::sourceIndices() const
{
map<string, unsigned> indices;
+ unsigned index = 0;
for (auto const& s: m_sources)
- indices[s.first] = indices.size();
+ indices[s.first] = index++;
return indices;
}
@@ -417,7 +451,7 @@ Json::Value const& CompilerStack::interface(string const& _contractName) const
Json::Value const& CompilerStack::metadata(string const& _contractName, DocumentationType _type) const
{
- if (!m_parseSuccessful)
+ if (m_stackState < AnalysisSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
return metadata(contract(_contractName), _type);
@@ -425,7 +459,7 @@ Json::Value const& CompilerStack::metadata(string const& _contractName, Document
Json::Value const& CompilerStack::metadata(Contract const& _contract, DocumentationType _type) const
{
- if (!m_parseSuccessful)
+ if (m_stackState < AnalysisSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
solAssert(_contract.contract, "");
@@ -456,7 +490,7 @@ Json::Value const& CompilerStack::metadata(Contract const& _contract, Documentat
string const& CompilerStack::onChainMetadata(string const& _contractName) const
{
- if (!m_parseSuccessful)
+ if (m_stackState != CompilationSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
return contract(_contractName).onChainMetadata;
@@ -522,18 +556,18 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
if (m_sources.count(importPath) || newSources.count(importPath))
continue;
- ReadFileResult result{false, string("File not supplied initially.")};
+ ReadFile::Result result{false, string("File not supplied initially.")};
if (m_readFile)
result = m_readFile(importPath);
if (result.success)
- newSources[importPath] = result.contentsOrErrorMesage;
+ newSources[importPath] = result.contentsOrErrorMessage;
else
{
auto err = make_shared<Error>(Error::Type::ParserError);
*err <<
errinfo_sourceLocation(import->location()) <<
- errinfo_comment("Source \"" + importPath + "\" not found: " + result.contentsOrErrorMesage);
+ errinfo_comment("Source \"" + importPath + "\" not found: " + result.contentsOrErrorMessage);
m_errors.push_back(std::move(err));
continue;
}
@@ -655,8 +689,33 @@ void CompilerStack::compileContract(
cborEncodedMetadata += toCompactBigEndian(cborEncodedMetadata.size(), 2);
compiler->compileContract(_contract, _compiledContracts, cborEncodedMetadata);
compiledContract.compiler = compiler;
- compiledContract.object = compiler->assembledObject();
- compiledContract.runtimeObject = compiler->runtimeObject();
+
+ try
+ {
+ compiledContract.object = compiler->assembledObject();
+ }
+ catch(eth::OptimizerException const&)
+ {
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Assembly optimizer exception for bytecode"));
+ }
+ catch(eth::AssemblyException const&)
+ {
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Assembly exception for bytecode"));
+ }
+
+ try
+ {
+ compiledContract.runtimeObject = compiler->runtimeObject();
+ }
+ catch(eth::OptimizerException const&)
+ {
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Assembly optimizer exception for deployed bytecode"));
+ }
+ catch(eth::AssemblyException const&)
+ {
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Assembly exception for deployed bytecode"));
+ }
+
compiledContract.onChainMetadata = onChainMetadata;
_compiledContracts[compiledContract.contract] = &compiler->assembly();
@@ -841,3 +900,88 @@ string CompilerStack::computeSourceMapping(eth::AssemblyItems const& _items) con
}
return ret;
}
+
+namespace
+{
+
+Json::Value gasToJson(GasEstimator::GasConsumption const& _gas)
+{
+ if (_gas.isInfinite)
+ return Json::Value("infinite");
+ else
+ return Json::Value(toString(_gas.value));
+}
+
+}
+
+Json::Value CompilerStack::gasEstimates(string const& _contractName) const
+{
+ if (!assemblyItems(_contractName) && !runtimeAssemblyItems(_contractName))
+ return Json::Value();
+
+ using Gas = GasEstimator::GasConsumption;
+ Json::Value output(Json::objectValue);
+
+ if (eth::AssemblyItems const* items = assemblyItems(_contractName))
+ {
+ Gas executionGas = GasEstimator::functionalEstimation(*items);
+ u256 bytecodeSize(runtimeObject(_contractName).bytecode.size());
+ Gas codeDepositGas = bytecodeSize * eth::GasCosts::createDataGas;
+
+ Json::Value creation(Json::objectValue);
+ creation["codeDepositCost"] = gasToJson(codeDepositGas);
+ creation["executionCost"] = gasToJson(executionGas);
+ /// TODO: implement + overload to avoid the need of +=
+ executionGas += codeDepositGas;
+ creation["totalCost"] = gasToJson(executionGas);
+ output["creation"] = creation;
+ }
+
+ if (eth::AssemblyItems const* items = runtimeAssemblyItems(_contractName))
+ {
+ /// External functions
+ ContractDefinition const& contract = contractDefinition(_contractName);
+ Json::Value externalFunctions(Json::objectValue);
+ for (auto it: contract.interfaceFunctions())
+ {
+ string sig = it.second->externalSignature();
+ externalFunctions[sig] = gasToJson(GasEstimator::functionalEstimation(*items, sig));
+ }
+
+ if (contract.fallbackFunction())
+ /// This needs to be set to an invalid signature in order to trigger the fallback,
+ /// without the shortcut (of CALLDATSIZE == 0), and therefore to receive the upper bound.
+ /// An empty string ("") would work to trigger the shortcut only.
+ externalFunctions[""] = gasToJson(GasEstimator::functionalEstimation(*items, "INVALID"));
+
+ if (!externalFunctions.empty())
+ output["external"] = externalFunctions;
+
+ /// Internal functions
+ Json::Value internalFunctions(Json::objectValue);
+ for (auto const& it: contract.definedFunctions())
+ {
+ /// Exclude externally visible functions, constructor and the fallback function
+ if (it->isPartOfExternalInterface() || it->isConstructor() || it->name().empty())
+ continue;
+
+ size_t entry = functionEntryPoint(_contractName, *it);
+ GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
+ if (entry > 0)
+ gas = GasEstimator::functionalEstimation(*items, entry, *it);
+
+ 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);
+ }
+
+ if (!internalFunctions.empty())
+ output["internal"] = internalFunctions;
+ }
+
+ return output;
+}
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index eddfea68..c1d344ca 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -36,6 +36,7 @@
#include <libevmasm/SourceLocation.h>
#include <libevmasm/LinkerObject.h>
#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/ReadFile.h>
namespace dev
{
@@ -77,18 +78,10 @@ enum class DocumentationType: uint8_t
class CompilerStack: boost::noncopyable
{
public:
- struct ReadFileResult
- {
- bool success;
- std::string contentsOrErrorMesage;
- };
-
- /// File reading callback.
- using ReadFileCallback = std::function<ReadFileResult(std::string const&)>;
-
/// Creates a new compiler stack.
- /// @param _readFile callback to used to read files for import statements. Should return
- explicit CompilerStack(ReadFileCallback const& _readFile = ReadFileCallback());
+ /// @param _readFile callback to used to read files for import statements. Must return
+ /// and must not emit exceptions.
+ explicit CompilerStack(ReadFile::Callback const& _readFile = ReadFile::Callback());
/// Sets path remappings in the format "context:prefix=target"
void setRemappings(std::vector<std::string> const& _remappings);
@@ -110,6 +103,16 @@ public:
/// 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,
+ /// 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;
std::string defaultContractName() const;
@@ -181,6 +184,9 @@ public:
std::string const& onChainMetadata(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.
@@ -230,6 +236,13 @@ private:
mutable std::unique_ptr<std::string const> sourceMapping;
mutable std::unique_ptr<std::string const> runtimeSourceMapping;
};
+ enum State {
+ Empty,
+ SourcesSet,
+ ParsingSuccessful,
+ AnalysisSuccessful,
+ CompilationSuccessful
+ };
/// 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.
@@ -263,14 +276,13 @@ private:
std::string target;
};
- ReadFileCallback m_readFile;
+ ReadFile::Callback m_readFile;
bool m_optimize = false;
unsigned m_optimizeRuns = 200;
std::map<std::string, h160> m_libraries;
/// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum
/// "context:prefix=target"
std::vector<Remapping> m_remappings;
- bool m_parseSuccessful;
std::map<std::string const, Source> m_sources;
std::shared_ptr<GlobalContext> m_globalContext;
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes;
@@ -279,6 +291,7 @@ private:
std::string m_formalTranslation;
ErrorList m_errors;
bool m_metadataLiteralSources = false;
+ State m_stackState = Empty;
};
}
diff --git a/libsolidity/interface/Exceptions.cpp b/libsolidity/interface/Exceptions.cpp
index 968a24ad..c09180de 100644
--- a/libsolidity/interface/Exceptions.cpp
+++ b/libsolidity/interface/Exceptions.cpp
@@ -33,22 +33,22 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip
switch(m_type)
{
case Type::DeclarationError:
- m_typeName = "Declaration Error";
+ m_typeName = "DeclarationError";
break;
case Type::DocstringParsingError:
- m_typeName = "Docstring Parsing Error";
+ m_typeName = "DocstringParsingError";
break;
case Type::ParserError:
- m_typeName = "Parser Error";
+ m_typeName = "ParserError";
break;
case Type::SyntaxError:
- m_typeName = "Syntax Error";
+ m_typeName = "SyntaxError";
break;
case Type::TypeError:
- m_typeName = "Type Error";
+ m_typeName = "TypeError";
break;
case Type::Why3TranslatorError:
- m_typeName = "Why3 Translator Error";
+ m_typeName = "Why3TranslatorError";
break;
case Type::Warning:
m_typeName = "Warning";
diff --git a/libsolidity/interface/ReadFile.h b/libsolidity/interface/ReadFile.h
new file mode 100644
index 00000000..2e8a6bd8
--- /dev/null
+++ b/libsolidity/interface/ReadFile.h
@@ -0,0 +1,45 @@
+/*
+ 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/>.
+*/
+
+#pragma once
+
+#include <string>
+#include <functional>
+#include <boost/noncopyable.hpp>
+
+namespace dev
+{
+
+namespace solidity
+{
+
+class ReadFile: boost::noncopyable
+{
+public:
+ /// File reading result.
+ struct Result
+ {
+ bool success;
+ std::string contentsOrErrorMessage;
+ };
+
+ /// File reading callback.
+ using Callback = std::function<Result(std::string const&)>;
+};
+
+}
+}
diff --git a/libsolidity/interface/SourceReferenceFormatter.h b/libsolidity/interface/SourceReferenceFormatter.h
index 7034f4ab..e8676d60 100644
--- a/libsolidity/interface/SourceReferenceFormatter.h
+++ b/libsolidity/interface/SourceReferenceFormatter.h
@@ -23,6 +23,7 @@
#pragma once
#include <ostream>
+#include <sstream>
#include <functional>
#include <libevmasm/SourceLocation.h>
@@ -53,6 +54,16 @@ public:
std::string const& _name,
ScannerFromSourceNameFun const& _scannerFromSourceName
);
+ static std::string formatExceptionInformation(
+ Exception const& _exception,
+ std::string const& _name,
+ ScannerFromSourceNameFun const& _scannerFromSourceName
+ )
+ {
+ std::ostringstream errorOutput;
+ printExceptionInformation(errorOutput, _exception, _name, _scannerFromSourceName);
+ return errorOutput.str();
+ }
private:
/// Prints source name if location is given.
static void printSourceName(
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
new file mode 100644
index 00000000..223cc15d
--- /dev/null
+++ b/libsolidity/interface/StandardCompiler.cpp
@@ -0,0 +1,482 @@
+/*
+ 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/>.
+*/
+/**
+ * @author Alex Beregszaszi
+ * @date 2016
+ * Standard JSON compiler interface.
+ */
+
+#include <libsolidity/interface/StandardCompiler.h>
+#include <libsolidity/interface/SourceReferenceFormatter.h>
+#include <libsolidity/ast/ASTJsonConverter.h>
+#include <libevmasm/Instruction.h>
+#include <libdevcore/JSON.h>
+#include <libdevcore/SHA3.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+namespace {
+
+Json::Value formatError(
+ bool _warning,
+ string const& _type,
+ string const& _component,
+ string const& _message,
+ string const& _formattedMessage = "",
+ Json::Value const& _sourceLocation = Json::Value()
+)
+{
+ Json::Value error = Json::objectValue;
+ error["type"] = _type;
+ error["component"] = _component;
+ error["severity"] = _warning ? "warning" : "error";
+ error["message"] = _message;
+ error["formattedMessage"] = (_formattedMessage.length() > 0) ? _formattedMessage : _message;
+ if (_sourceLocation.isObject())
+ error["sourceLocation"] = _sourceLocation;
+ return error;
+}
+
+Json::Value formatFatalError(string const& _type, string const& _message)
+{
+ Json::Value output = Json::objectValue;
+ output["errors"] = Json::arrayValue;
+ output["errors"].append(formatError(false, _type, "general", _message));
+ return output;
+}
+
+Json::Value formatErrorWithException(
+ Exception const& _exception,
+ bool const& _warning,
+ string const& _type,
+ string const& _component,
+ string const& _message,
+ function<Scanner const&(string const&)> const& _scannerFromSourceName
+)
+{
+ string message;
+ string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _message, _scannerFromSourceName);
+
+ // NOTE: the below is partially a copy from SourceReferenceFormatter
+ SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
+
+ if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
+ message = ((_message.length() > 0) ? (_message + ":") : "") + *description;
+ else
+ message = _message;
+
+ if (location && location->sourceName)
+ {
+ Json::Value sourceLocation = Json::objectValue;
+ sourceLocation["file"] = *location->sourceName;
+ sourceLocation["start"] = location->start;
+ sourceLocation["end"] = location->end;
+ }
+
+ return formatError(_warning, _type, _component, message, formattedMessage, location);
+}
+
+/// Returns true iff @a _hash (hex with 0x prefix) is the Keccak256 hash of the binary data in @a _content.
+bool hashMatchesContent(string const& _hash, string const& _content)
+{
+ try
+ {
+ return dev::h256(_hash) == dev::keccak256(_content);
+ }
+ catch (dev::BadHexCharacter)
+ {
+ return false;
+ }
+}
+
+StringMap createSourceList(Json::Value const& _input)
+{
+ StringMap sources;
+ Json::Value const& jsonSources = _input["sources"];
+ if (jsonSources.isObject())
+ for (auto const& sourceName: jsonSources.getMemberNames())
+ sources[sourceName] = jsonSources[sourceName]["content"].asString();
+ return sources;
+}
+
+Json::Value methodIdentifiers(ContractDefinition const& _contract)
+{
+ Json::Value methodIdentifiers(Json::objectValue);
+ for (auto const& it: _contract.interfaceFunctions())
+ methodIdentifiers[it.second->externalSignature()] = toHex(it.first.ref());
+ return methodIdentifiers;
+}
+
+Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkReferences)
+{
+ Json::Value ret(Json::objectValue);
+
+ for (auto const& ref: linkReferences)
+ {
+ string const& fullname = ref.second;
+ size_t colon = fullname.find(':');
+ solAssert(colon != string::npos, "");
+ string file = fullname.substr(0, colon);
+ string name = fullname.substr(colon + 1);
+
+ Json::Value fileObject = ret.get(file, Json::objectValue);
+ Json::Value libraryArray = fileObject.get(name, Json::arrayValue);
+
+ Json::Value entry = Json::objectValue;
+ entry["start"] = Json::UInt(ref.first);
+ entry["length"] = 20;
+
+ libraryArray.append(entry);
+ fileObject[name] = libraryArray;
+ ret[file] = fileObject;
+ }
+
+ return ret;
+}
+
+Json::Value collectEVMObject(eth::LinkerObject const& _object, string const* _sourceMap)
+{
+ Json::Value output = Json::objectValue;
+ output["object"] = _object.toHex();
+ output["opcodes"] = solidity::disassemble(_object.bytecode);
+ output["sourceMap"] = _sourceMap ? *_sourceMap : "";
+ output["linkReferences"] = formatLinkReferences(_object.linkReferences);
+ return output;
+}
+
+}
+
+Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
+{
+ m_compilerStack.reset(false);
+
+ if (!_input.isObject())
+ return formatFatalError("JSONError", "Input is not a JSON object.");
+
+ if (_input["language"] != "Solidity")
+ return formatFatalError("JSONError", "Only \"Solidity\" is supported as a language.");
+
+ Json::Value const& sources = _input["sources"];
+ if (!sources)
+ return formatFatalError("JSONError", "No input sources specified.");
+
+ Json::Value errors = Json::arrayValue;
+
+ for (auto const& sourceName: sources.getMemberNames())
+ {
+ string hash;
+
+ if (!sources[sourceName].isObject())
+ return formatFatalError("JSONError", "Source input is not a JSON object.");
+
+ if (sources[sourceName]["keccak256"].isString())
+ hash = sources[sourceName]["keccak256"].asString();
+
+ if (sources[sourceName]["content"].isString())
+ {
+ string content = sources[sourceName]["content"].asString();
+ if (!hash.empty() && !hashMatchesContent(hash, content))
+ errors.append(formatError(
+ false,
+ "IOError",
+ "general",
+ "Mismatch between content and supplied hash for \"" + sourceName + "\""
+ ));
+ else
+ m_compilerStack.addSource(sourceName, content);
+ }
+ else if (sources[sourceName]["urls"].isArray())
+ {
+ if (!m_readFile)
+ return formatFatalError("JSONError", "No import callback supplied, but URL is requested.");
+
+ bool found = false;
+ vector<string> failures;
+
+ for (auto const& url: sources[sourceName]["urls"])
+ {
+ ReadFile::Result result = m_readFile(url.asString());
+ if (result.success)
+ {
+ if (!hash.empty() && !hashMatchesContent(hash, result.contentsOrErrorMessage))
+ errors.append(formatError(
+ false,
+ "IOError",
+ "general",
+ "Mismatch between content and supplied hash for \"" + sourceName + "\" at \"" + url.asString() + "\""
+ ));
+ else
+ {
+ m_compilerStack.addSource(sourceName, result.contentsOrErrorMessage);
+ found = true;
+ break;
+ }
+ }
+ else
+ failures.push_back("Cannot import url (\"" + url.asString() + "\"): " + result.contentsOrErrorMessage);
+ }
+
+ for (auto const& failure: failures)
+ {
+ /// If the import succeeded, let mark all the others as warnings, otherwise all of them are errors.
+ errors.append(formatError(
+ found ? true : false,
+ "IOError",
+ "general",
+ failure
+ ));
+ }
+ }
+ else
+ return formatFatalError("JSONError", "Invalid input source specified.");
+ }
+
+ Json::Value const& settings = _input.get("settings", Json::Value());
+
+ vector<string> remappings;
+ for (auto const& remapping: settings.get("remappings", Json::Value()))
+ remappings.push_back(remapping.asString());
+ 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();
+
+ map<string, h160> libraries;
+ Json::Value jsonLibraries = settings.get("libraries", Json::Value());
+ for (auto const& sourceName: jsonLibraries.getMemberNames())
+ {
+ auto const& jsonSourceName = jsonLibraries[sourceName];
+ for (auto const& library: jsonSourceName.getMemberNames())
+ // @TODO use libraries only for the given source
+ libraries[library] = h160(jsonSourceName[library].asString());
+ }
+
+ Json::Value metadataSettings = settings.get("metadata", Json::Value());
+ m_compilerStack.useMetadataLiteralSources(metadataSettings.get("useLiteralContent", Json::Value(false)).asBool());
+
+ auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compilerStack.scanner(_sourceName); };
+
+ bool success = false;
+
+ try
+ {
+ success = m_compilerStack.compile(optimize, optimizeRuns, libraries);
+
+ for (auto const& error: m_compilerStack.errors())
+ {
+ auto err = dynamic_pointer_cast<Error const>(error);
+
+ errors.append(formatErrorWithException(
+ *error,
+ err->type() == Error::Type::Warning,
+ err->typeName(),
+ "general",
+ "",
+ scannerFromSourceName
+ ));
+ }
+ }
+ 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
+ ));
+ }
+ catch (CompilerError const& _exception)
+ {
+ errors.append(formatErrorWithException(
+ _exception,
+ false,
+ "CompilerError",
+ "general",
+ "Compiler error (" + _exception.lineInfo() + ")",
+ scannerFromSourceName
+ ));
+ }
+ catch (InternalCompilerError const& _exception)
+ {
+ errors.append(formatErrorWithException(
+ _exception,
+ false,
+ "InternalCompilerError",
+ "general",
+ "Internal compiler error (" + _exception.lineInfo() + ")", scannerFromSourceName
+ ));
+ }
+ catch (UnimplementedFeatureError const& _exception)
+ {
+ errors.append(formatErrorWithException(
+ _exception,
+ false,
+ "UnimplementedFeatureError",
+ "general",
+ "Unimplemented feature (" + _exception.lineInfo() + ")",
+ scannerFromSourceName));
+ }
+ catch (Exception const& _exception)
+ {
+ errors.append(formatError(
+ false,
+ "Exception",
+ "general",
+ "Exception during compilation: " + boost::diagnostic_information(_exception)
+ ));
+ }
+ catch (...)
+ {
+ errors.append(formatError(
+ false,
+ "Exception",
+ "general",
+ "Unknown exception during compilation."
+ ));
+ }
+
+ Json::Value output = Json::objectValue;
+
+ if (errors.size() > 0)
+ output["errors"] = errors;
+
+ /// Inconsistent state - stop here to receive error reports from users
+ if (!success && (errors.size() == 0))
+ return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");
+
+ output["sources"] = Json::objectValue;
+ unsigned sourceIndex = 0;
+ for (auto const& source: m_compilerStack.sourceNames())
+ {
+ Json::Value sourceResult = Json::objectValue;
+ sourceResult["id"] = sourceIndex++;
+ sourceResult["legacyAST"] = ASTJsonConverter(m_compilerStack.ast(source), m_compilerStack.sourceIndices()).json();
+ output["sources"][source] = sourceResult;
+ }
+
+ Json::Value contractsOutput = Json::objectValue;
+ for (string const& contractName: success ? m_compilerStack.contractNames() : vector<string>())
+ {
+ size_t colon = contractName.find(':');
+ solAssert(colon != string::npos, "");
+ string file = contractName.substr(0, colon);
+ string name = contractName.substr(colon + 1);
+
+ // ABI, documentation and metadata
+ Json::Value contractData(Json::objectValue);
+ contractData["abi"] = m_compilerStack.metadata(contractName, DocumentationType::ABIInterface);
+ contractData["metadata"] = m_compilerStack.onChainMetadata(contractName);
+ contractData["userdoc"] = m_compilerStack.metadata(contractName, DocumentationType::NatspecUser);
+ contractData["devdoc"] = m_compilerStack.metadata(contractName, DocumentationType::NatspecDev);
+
+ // EVM
+ Json::Value evmData(Json::objectValue);
+ // @TODO: add ir
+ ostringstream tmp;
+ m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), false);
+ evmData["assembly"] = tmp.str();
+ evmData["legacyAssembly"] = m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), true);
+ evmData["methodIdentifiers"] = methodIdentifiers(m_compilerStack.contractDefinition(contractName));
+ evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName);
+
+ evmData["bytecode"] = collectEVMObject(
+ m_compilerStack.object(contractName),
+ m_compilerStack.sourceMapping(contractName)
+ );
+
+ evmData["deployedBytecode"] = collectEVMObject(
+ m_compilerStack.runtimeObject(contractName),
+ m_compilerStack.runtimeSourceMapping(contractName)
+ );
+
+ contractData["evm"] = evmData;
+
+ if (!contractsOutput.isMember(file))
+ contractsOutput[file] = Json::objectValue;
+
+ contractsOutput[file][name] = contractData;
+ }
+ output["contracts"] = contractsOutput;
+
+ return output;
+}
+
+Json::Value StandardCompiler::compile(Json::Value const& _input)
+{
+ try
+ {
+ return compileInternal(_input);
+ }
+ catch (Json::LogicError const& _exception)
+ {
+ return formatFatalError("InternalCompilerError", string("JSON logic exception: ") + _exception.what());
+ }
+ catch (Json::RuntimeError const& _exception)
+ {
+ return formatFatalError("InternalCompilerError", string("JSON runtime exception: ") + _exception.what());
+ }
+ catch (Exception const& _exception)
+ {
+ return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compileInternal: " + boost::diagnostic_information(_exception));
+ }
+ catch (...)
+ {
+ return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compileInternal");
+ }
+}
+
+string StandardCompiler::compile(string const& _input)
+{
+ Json::Value input;
+ Json::Reader reader;
+
+ try
+ {
+ if (!reader.parse(_input, input, false))
+ return jsonCompactPrint(formatFatalError("JSONError", reader.getFormattedErrorMessages()));
+ }
+ catch(...)
+ {
+ return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}";
+ }
+
+ // cout << "Input: " << input.toStyledString() << endl;
+ Json::Value output = compile(input);
+ // cout << "Output: " << output.toStyledString() << endl;
+
+ try
+ {
+ return jsonCompactPrint(output);
+ }
+ catch(...)
+ {
+ return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}";
+ }
+}
diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h
new file mode 100644
index 00000000..dfaf88cd
--- /dev/null
+++ b/libsolidity/interface/StandardCompiler.h
@@ -0,0 +1,63 @@
+/*
+ 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/>.
+*/
+/**
+ * @author Alex Beregszaszi
+ * @date 2016
+ * Standard JSON compiler interface.
+ */
+
+#pragma once
+
+#include <libsolidity/interface/CompilerStack.h>
+
+namespace dev
+{
+
+namespace solidity
+{
+
+/**
+ * Standard JSON compiler interface, which expects a JSON input and returns a JSON ouput.
+ * See docs/using-the-compiler#compiler-input-and-output-json-description.
+ */
+class StandardCompiler: boost::noncopyable
+{
+public:
+ /// Creates a new StandardCompiler.
+ /// @param _readFile callback to used to read files for import statements. Must return
+ /// and must not emit exceptions.
+ StandardCompiler(ReadFile::Callback const& _readFile = ReadFile::Callback())
+ : m_compilerStack(_readFile), m_readFile(_readFile)
+ {
+ }
+
+ /// Sets all input parameters according to @a _input which conforms to the standardized input
+ /// format, performs compilation and returns a standardized output.
+ Json::Value compile(Json::Value const& _input);
+ /// Parses input as JSON and peforms the above processing steps, returning a serialized JSON
+ /// output. Parsing errors are returned as regular errors.
+ std::string compile(std::string const& _input);
+
+private:
+ Json::Value compileInternal(Json::Value const& _input);
+
+ CompilerStack m_compilerStack;
+ ReadFile::Callback m_readFile;
+};
+
+}
+}
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index e26e2908..b5130c8a 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -82,9 +82,10 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
case Token::Import:
nodes.push_back(parseImportDirective());
break;
+ case Token::Interface:
case Token::Contract:
case Token::Library:
- nodes.push_back(parseContractDefinition(token == Token::Library));
+ nodes.push_back(parseContractDefinition(token));
break;
default:
fatalParserError(string("Expected import directive or contract definition."));
@@ -193,13 +194,30 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
return nodeFactory.createNode<ImportDirective>(path, unitAlias, move(symbolAliases));
}
-ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
+ContractDefinition::ContractKind Parser::tokenToContractKind(Token::Value _token)
+{
+ switch(_token)
+ {
+ case Token::Interface:
+ return ContractDefinition::ContractKind::Interface;
+ case Token::Contract:
+ return ContractDefinition::ContractKind::Contract;
+ case Token::Library:
+ return ContractDefinition::ContractKind::Library;
+ default:
+ fatalParserError("Unsupported contract type.");
+ }
+ // FIXME: fatalParserError is not considered as throwing here
+ return ContractDefinition::ContractKind::Contract;
+}
+
+ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _expectedKind)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docString;
if (m_scanner->currentCommentLiteral() != "")
docString = make_shared<ASTString>(m_scanner->currentCommentLiteral());
- expectToken(_isLibrary ? Token::Library : Token::Contract);
+ expectToken(_expectedKind);
ASTPointer<ASTString> name = expectIdentifierToken();
vector<ASTPointer<InheritanceSpecifier>> baseContracts;
if (m_scanner->currentToken() == Token::Is)
@@ -252,7 +270,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
docString,
baseContracts,
subNodes,
- _isLibrary
+ tokenToContractKind(_expectedKind)
);
}
diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h
index 79d1d1d4..282617ab 100644
--- a/libsolidity/parsing/Parser.h
+++ b/libsolidity/parsing/Parser.h
@@ -69,7 +69,8 @@ private:
///@name Parsing functions for the AST nodes
ASTPointer<PragmaDirective> parsePragmaDirective();
ASTPointer<ImportDirective> parseImportDirective();
- ASTPointer<ContractDefinition> parseContractDefinition(bool _isLibrary);
+ ContractDefinition::ContractKind tokenToContractKind(Token::Value _token);
+ ASTPointer<ContractDefinition> parseContractDefinition(Token::Value _expectedKind);
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers);
diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h
index c6d050bb..9a557ebd 100644
--- a/libsolidity/parsing/Token.h
+++ b/libsolidity/parsing/Token.h
@@ -157,6 +157,7 @@ namespace solidity
K(Hex, "hex", 0) \
K(If, "if", 0) \
K(Indexed, "indexed", 0) \
+ K(Interface, "interface", 0) \
K(Internal, "internal", 0) \
K(Import, "import", 0) \
K(Is, "is", 0) \
@@ -225,7 +226,6 @@ namespace solidity
K(Final, "final", 0) \
K(In, "in", 0) \
K(Inline, "inline", 0) \
- K(Interface, "interface", 0) \
K(Let, "let", 0) \
K(Match, "match", 0) \
K(NullLiteral, "null", 0) \