aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp22
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h3
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp2
-rw-r--r--libsolidity/analysis/TypeChecker.cpp5
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp1
-rw-r--r--libsolidity/codegen/CompilerContext.h8
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp30
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.h9
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp13
-rw-r--r--libsolidity/inlineasm/AsmScope.cpp2
-rw-r--r--libsolidity/inlineasm/AsmScope.h19
-rw-r--r--libsolidity/interface/AssemblyStack.cpp16
-rw-r--r--libsolidity/interface/AssemblyStack.h8
-rw-r--r--libsolidity/interface/CompilerStack.cpp8
-rw-r--r--libsolidity/interface/CompilerStack.h3
-rw-r--r--libsolidity/parsing/Parser.cpp48
16 files changed, 140 insertions, 57 deletions
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index 2742dcf2..aac90311 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -26,6 +26,8 @@
#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/interface/ErrorReporter.h>
+#include <boost/algorithm/string.hpp>
+
using namespace std;
namespace dev
@@ -232,6 +234,26 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
return uniqueFunctions;
}
+void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
+{
+ for (auto const& instruction: c_instructions)
+ {
+ string const instructionName{boost::algorithm::to_lower_copy(instruction.first)};
+ auto declarations = nameFromCurrentScope(instructionName);
+ for (Declaration const* const declaration: declarations)
+ {
+ solAssert(!!declaration, "");
+ if (dynamic_cast<MagicVariableDeclaration const* const>(declaration))
+ // Don't warn the user for what the user did not.
+ continue;
+ m_errorReporter.warning(
+ declaration->location(),
+ "Variable is shadowed in inline assembly by an instruction of the same name"
+ );
+ }
+ }
+}
+
bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode)
{
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index 0441867d..84628778 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -90,6 +90,9 @@ public:
std::vector<Declaration const*> const& _declarations
);
+ /// Generate and store warnings about variables that are named like instructions.
+ void warnVariablesNamedLikeInstructions();
+
private:
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index edf2fc02..2a5f27df 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -162,6 +162,8 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
{
+ m_resolver.warnVariablesNamedLikeInstructions();
+
// 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
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index b1911ef0..2a8d1ff6 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -1287,14 +1287,11 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
membersRemovedForStructConstructor = structType.membersMissingInMemory();
_functionCall.annotation().isPure = isPure;
}
- else
- {
- functionType = dynamic_pointer_cast<FunctionType const>(expressionType);
+ else if ((functionType = dynamic_pointer_cast<FunctionType const>(expressionType)))
_functionCall.annotation().isPure =
isPure &&
_functionCall.expression().annotation().isPure &&
functionType->isPure();
- }
if (!functionType)
{
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index 1de2e801..4ad1f962 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -252,6 +252,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node)
{
setJsonNode(_node, "ContractDefinition", {
make_pair("name", _node.name()),
+ make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue),
make_pair("contractKind", contractKind(_node.contractKind())),
make_pair("fullyImplemented", _node.annotation().isFullyImplemented),
make_pair("linearizedBaseContracts", getContainerIds(_node.annotation().linearizedBaseContracts)),
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index c37142c9..030b35a6 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -141,8 +141,6 @@ public:
CompilerContext& appendInvalid();
/// Appends a conditional INVALID instruction
CompilerContext& appendConditionalInvalid();
- /// Returns an "ErrorTag"
- eth::AssemblyItem errorTag() { return m_asm->errorTag(); }
/// Appends a JUMP to a specific tag
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJump(_tag); return *this; }
/// Appends pushing of a new tag and @returns the new tag.
@@ -151,10 +149,10 @@ public:
eth::AssemblyItem newTag() { return m_asm->newTag(); }
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
/// on the stack. @returns the pushsub assembly item.
- eth::AssemblyItem addSubroutine(eth::AssemblyPointer const& _assembly) { auto sub = m_asm->newSub(_assembly); m_asm->append(m_asm->newPushSubSize(size_t(sub.data()))); return sub; }
- void pushSubroutineSize(size_t _subRoutine) { m_asm->append(m_asm->newPushSubSize(_subRoutine)); }
+ eth::AssemblyItem addSubroutine(eth::AssemblyPointer const& _assembly) { return m_asm->appendSubroutine(_assembly); }
+ void pushSubroutineSize(size_t _subRoutine) { m_asm->pushSubroutineSize(_subRoutine); }
/// Pushes the offset of the subroutine.
- void pushSubroutineOffset(size_t _subRoutine) { m_asm->append(eth::AssemblyItem(eth::PushSub, _subRoutine)); }
+ void pushSubroutineOffset(size_t _subRoutine) { m_asm->pushSubroutineOffset(_subRoutine); }
/// Pushes the size of the final program
void appendProgramSize() { m_asm->appendProgramSize(); }
/// Adds data to the data section, pushes a reference to the stack
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index 13852880..1a529118 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -29,6 +29,7 @@
#include <libsolidity/interface/Utils.h>
#include <boost/range/adaptor/reversed.hpp>
+#include <boost/algorithm/string.hpp>
#include <memory>
#include <functional>
@@ -65,6 +66,7 @@ bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction)
auto const& info = instructionInfo(_instruction.instruction);
m_stackHeight += info.ret - info.args;
m_info.stackHeightInfo[&_instruction] = m_stackHeight;
+ warnOnFutureInstruction(_instruction.instruction, _instruction.location);
return true;
}
@@ -91,7 +93,7 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier)
if (m_currentScope->lookup(_identifier.name, Scope::Visitor(
[&](Scope::Variable const& _var)
{
- if (!_var.active)
+ if (!m_activeVariables.count(&_var))
{
m_errorReporter.declarationError(
_identifier.location,
@@ -149,6 +151,7 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
if (!(*this)(_instr.instruction))
success = false;
m_info.stackHeightInfo[&_instr] = m_stackHeight;
+ warnOnFutureInstruction(_instr.instruction.instruction, _instr.location);
return success;
}
@@ -185,7 +188,7 @@ bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl)
for (auto const& variable: _varDecl.variables)
{
expectValidType(variable.type, variable.location);
- boost::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name)).active = true;
+ m_activeVariables.insert(&boost::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name)));
}
m_info.stackHeightInfo[&_varDecl] = m_stackHeight;
return success;
@@ -199,7 +202,7 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef)
for (auto const& var: _funDef.arguments + _funDef.returns)
{
expectValidType(var.type, var.location);
- boost::get<Scope::Variable>(varScope.identifiers.at(var.name)).active = true;
+ m_activeVariables.insert(&boost::get<Scope::Variable>(varScope.identifiers.at(var.name)));
}
int const stackHeight = m_stackHeight;
@@ -382,7 +385,7 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t
m_errorReporter.typeError(_variable.location, "Assignment requires variable.");
success = false;
}
- else if (!boost::get<Scope::Variable>(*var).active)
+ else if (!m_activeVariables.count(&boost::get<Scope::Variable>(*var)))
{
m_errorReporter.declarationError(
_variable.location,
@@ -431,7 +434,6 @@ Scope& AsmAnalyzer::scope(Block const* _block)
solAssert(scopePtr, "Scope requested but not present.");
return *scopePtr;
}
-
void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location)
{
if (!m_julia)
@@ -443,3 +445,21 @@ void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _loc
"\"" + type + "\" is not a valid type (user defined types are not yet supported)."
);
}
+
+void AsmAnalyzer::warnOnFutureInstruction(solidity::Instruction _instr, SourceLocation const& _location)
+{
+ static set<solidity::Instruction> futureInstructions{
+ solidity::Instruction::CREATE2,
+ solidity::Instruction::RETURNDATACOPY,
+ solidity::Instruction::RETURNDATASIZE,
+ solidity::Instruction::STATICCALL
+ };
+ if (futureInstructions.count(_instr))
+ m_errorReporter.warning(
+ _location,
+ "The \"" +
+ boost::to_lower_copy(instructionInfo(_instr).name)
+ + "\" instruction is only available after " +
+ "the Metropolis hard fork. Before that it acts as an invalid instruction."
+ );
+}
diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h
index e7748bcf..2516722a 100644
--- a/libsolidity/inlineasm/AsmAnalysis.h
+++ b/libsolidity/inlineasm/AsmAnalysis.h
@@ -22,6 +22,8 @@
#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/inlineasm/AsmScope.h>
+
#include <libjulia/backends/evm/AbstractAssembly.h>
#include <boost/variant.hpp>
@@ -51,9 +53,6 @@ struct FunctionCall;
struct Switch;
using Statement = boost::variant<Instruction, Literal, Label, StackAssignment, Identifier, Assignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Switch, Block>;
-
-struct Scope;
-
struct AsmAnalysisInfo;
/**
@@ -97,10 +96,14 @@ private:
Scope& scope(assembly::Block const* _block);
void expectValidType(std::string const& type, SourceLocation const& _location);
+ void warnOnFutureInstruction(solidity::Instruction _instr, SourceLocation const& _location);
int m_stackHeight = 0;
julia::ExternalIdentifierAccess::Resolver m_resolver;
Scope* m_currentScope = nullptr;
+ /// Variables that are active at the current point in assembly (as opposed to
+ /// "part of the scope but not yet declared")
+ std::set<Scope::Variable const*> m_activeVariables;
AsmAnalysisInfo& m_info;
ErrorReporter& m_errorReporter;
bool m_julia = false;
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp
index 68a9cb03..f9b073ba 100644
--- a/libsolidity/inlineasm/AsmParser.cpp
+++ b/libsolidity/inlineasm/AsmParser.cpp
@@ -174,6 +174,19 @@ assembly::Case Parser::parseCase()
assembly::Statement Parser::parseExpression()
{
Statement operation = parseElementaryOperation(true);
+ if (operation.type() == typeid(Instruction))
+ {
+ Instruction const& instr = boost::get<Instruction>(operation);
+ int args = instructionInfo(instr.instruction).args;
+ if (args > 0 && currentToken() != Token::LParen)
+ fatalParserError(string(
+ "Expected token \"(\" (\"" +
+ instructionNames().at(instr.instruction) +
+ "\" expects " +
+ boost::lexical_cast<string>(args) +
+ " arguments)"
+ ));
+ }
if (currentToken() == Token::LParen)
return parseCall(std::move(operation));
else
diff --git a/libsolidity/inlineasm/AsmScope.cpp b/libsolidity/inlineasm/AsmScope.cpp
index 7a086846..1db5ca41 100644
--- a/libsolidity/inlineasm/AsmScope.cpp
+++ b/libsolidity/inlineasm/AsmScope.cpp
@@ -46,7 +46,7 @@ bool Scope::registerFunction(string const& _name, std::vector<JuliaType> const&
{
if (exists(_name))
return false;
- identifiers[_name] = Function(_arguments, _returns);
+ identifiers[_name] = Function{_arguments, _returns};
return true;
}
diff --git a/libsolidity/inlineasm/AsmScope.h b/libsolidity/inlineasm/AsmScope.h
index ad321f77..de9119e0 100644
--- a/libsolidity/inlineasm/AsmScope.h
+++ b/libsolidity/inlineasm/AsmScope.h
@@ -65,27 +65,12 @@ struct Scope
using JuliaType = std::string;
using LabelID = size_t;
- 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;
- JuliaType type;
- };
-
- struct Label
- {
- boost::optional<LabelID> id;
- };
-
+ struct Variable { JuliaType type; };
+ struct Label { };
struct Function
{
- Function(std::vector<JuliaType> const& _arguments, std::vector<JuliaType> const& _returns): arguments(_arguments), returns(_returns) {}
std::vector<JuliaType> arguments;
std::vector<JuliaType> returns;
- boost::optional<LabelID> id;
};
using Identifier = boost::variant<Variable, Label, Function>;
diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp
index 75877881..7dc1edc7 100644
--- a/libsolidity/interface/AssemblyStack.cpp
+++ b/libsolidity/interface/AssemblyStack.cpp
@@ -77,7 +77,7 @@ bool AssemblyStack::analyzeParsed()
return m_analysisSuccessful;
}
-eth::LinkerObject AssemblyStack::assemble(Machine _machine) const
+MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
{
solAssert(m_analysisSuccessful, "");
solAssert(m_parserResult, "");
@@ -87,21 +87,29 @@ eth::LinkerObject AssemblyStack::assemble(Machine _machine) const
{
case Machine::EVM:
{
+ MachineAssemblyObject object;
eth::Assembly assembly;
assembly::CodeGenerator::assemble(*m_parserResult, *m_analysisInfo, assembly);
- return assembly.assemble();
+ object.bytecode = make_shared<eth::LinkerObject>(assembly.assemble());
+ ostringstream tmp;
+ assembly.stream(tmp);
+ object.assembly = tmp.str();
+ return object;
}
case Machine::EVM15:
{
+ MachineAssemblyObject object;
julia::EVMAssembly assembly(true);
julia::CodeTransform(assembly, *m_analysisInfo, true).run(*m_parserResult);
- return assembly.finalize();
+ object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize());
+ /// TOOD: fill out text representation
+ return object;
}
case Machine::eWasm:
solUnimplemented("eWasm backend is not yet implemented.");
}
// unreachable
- return eth::LinkerObject();
+ return MachineAssemblyObject();
}
string AssemblyStack::print() const
diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h
index ee2a334c..2ae596ed 100644
--- a/libsolidity/interface/AssemblyStack.h
+++ b/libsolidity/interface/AssemblyStack.h
@@ -38,6 +38,12 @@ struct AsmAnalysisInfo;
struct Block;
}
+struct MachineAssemblyObject
+{
+ std::shared_ptr<eth::LinkerObject> bytecode;
+ std::string assembly;
+};
+
/*
* Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and
* eWasm as output.
@@ -64,7 +70,7 @@ public:
bool analyze(assembly::Block const& _block, Scanner const* _scanner = nullptr);
/// Run the assembly step (should only be called after parseAndAnalyze).
- eth::LinkerObject assemble(Machine _machine) const;
+ MachineAssemblyObject assemble(Machine _machine) const;
/// @returns the errors generated during parsing, analysis (and potentially assembly).
ErrorList const& errors() const { return m_errors; }
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index aca9ce39..b09108b0 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -481,6 +481,14 @@ Json::Value const& CompilerStack::natspec(Contract const& _contract, Documentati
return *(*doc);
}
+Json::Value CompilerStack::functionHashes(ContractDefinition const& _contract)
+{
+ Json::Value functionHashes(Json::objectValue);
+ for (auto const& it: _contract.interfaceFunctions())
+ functionHashes[it.second->externalSignature()] = toHex(it.first.ref());
+ return functionHashes;
+}
+
string const& CompilerStack::onChainMetadata(string const& _contractName) const
{
if (m_stackState != CompilationSuccessful)
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index bffdeabd..3250429b 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -177,6 +177,9 @@ public:
/// @param type The type of the documentation to get.
/// Can be one of 4 types defined at @c DocumentationType
Json::Value const& natspec(std::string const& _contractName, DocumentationType _type) const;
+
+ Json::Value functionHashes(ContractDefinition const& _contract);
+
std::string const& onChainMetadata(std::string const& _contractName) const;
void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; }
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index ec27f89b..88b41f20 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -347,8 +347,12 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
else if (Token::isVisibilitySpecifier(token))
{
if (result.visibility != Declaration::Visibility::Default)
- fatalParserError(string("Multiple visibility specifiers."));
- result.visibility = parseVisibilitySpecifier(token);
+ {
+ parserError(string("Multiple visibility specifiers."));
+ m_scanner->next();
+ }
+ else
+ result.visibility = parseVisibilitySpecifier(token);
}
else
break;
@@ -501,8 +505,12 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
{
if (visibility != Declaration::Visibility::Default)
- fatalParserError(string("Visibility already specified."));
- visibility = parseVisibilitySpecifier(token);
+ {
+ parserError(string("Visibility already specified."));
+ m_scanner->next();
+ }
+ else
+ visibility = parseVisibilitySpecifier(token);
}
else
{
@@ -513,14 +521,15 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token))
{
if (location != VariableDeclaration::Location::Default)
- fatalParserError(string("Location already specified."));
- if (!type)
- fatalParserError(string("Location specifier needs explicit type name."));
- location = (
- token == Token::Memory ?
- VariableDeclaration::Location::Memory :
- VariableDeclaration::Location::Storage
- );
+ parserError(string("Location already specified."));
+ else if (!type)
+ parserError(string("Location specifier needs explicit type name."));
+ else
+ location = (
+ token == Token::Memory ?
+ VariableDeclaration::Location::Memory :
+ VariableDeclaration::Location::Storage
+ );
}
else
break;
@@ -702,7 +711,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
else if (token == Token::Var)
{
if (!_allowVar)
- fatalParserError(string("Expected explicit type name."));
+ parserError(string("Expected explicit type name."));
m_scanner->next();
}
else if (token == Token::Function)
@@ -1328,16 +1337,21 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
{
// call({arg1 : 1, arg2 : 2 })
expectToken(Token::LBrace);
+
+ bool first = true;
while (m_scanner->currentToken() != Token::RBrace)
{
+ if (!first)
+ expectToken(Token::Comma);
+
+ if (m_scanner->currentToken() == Token::RBrace)
+ fatalParserError("Unexpected trailing comma.");
+
ret.second.push_back(expectIdentifierToken());
expectToken(Token::Colon);
ret.first.push_back(parseExpression());
- if (m_scanner->currentToken() == Token::Comma)
- expectToken(Token::Comma);
- else
- break;
+ first = false;
}
expectToken(Token::RBrace);
}