Merge branch 'develop' into fixNoMobile

This commit is contained in:
chriseth
2017-02-24 10:39:55 +01:00
committed by GitHub
21 changed files with 485 additions and 171 deletions
+77 -64
View File
@@ -21,6 +21,7 @@
*/
#include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/interface/Exceptions.h>
@@ -130,62 +131,9 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsideCode)
{
bool success = true;
try
{
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))
{
m_currentScope = m_scopes[contract->scope()].get();
solAssert(!!m_currentScope, "");
for (ASTPointer<InheritanceSpecifier> const& baseContract: contract->baseContracts())
if (!resolveNamesAndTypes(*baseContract, true))
success = false;
m_currentScope = m_scopes[contract].get();
if (success)
{
linearizeBaseContracts(*contract);
vector<ContractDefinition const*> properBases(
++contract->annotation().linearizedBaseContracts.begin(),
contract->annotation().linearizedBaseContracts.end()
);
for (ContractDefinition const* base: properBases)
importInheritedScope(*base);
}
// these can contain code, only resolve parameters for now
for (ASTPointer<ASTNode> const& node: contract->subNodes())
{
m_currentScope = m_scopes[contract].get();
if (!resolveNamesAndTypes(*node, false))
success = false;
}
if (!success)
return false;
if (!_resolveInsideCode)
return success;
m_currentScope = m_scopes[contract].get();
// now resolve references inside the code
for (ASTPointer<ASTNode> const& node: contract->subNodes())
{
m_currentScope = m_scopes[contract].get();
if (!resolveNamesAndTypes(*node, true))
success = false;
}
}
else
{
if (m_scopes.count(&_node))
m_currentScope = m_scopes[&_node].get();
return ReferencesResolver(m_errors, *this, _resolveInsideCode).resolve(_node);
}
return resolveNamesAndTypesInternal(_node, _resolveInsideCode);
}
catch (FatalError const&)
{
@@ -193,7 +141,6 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsi
throw; // Something is weird here, rather throw again.
return false;
}
return success;
}
bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
@@ -249,21 +196,25 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
solAssert(_declarations.size() > 1, "");
vector<Declaration const*> uniqueFunctions;
for (auto it = _declarations.begin(); it != _declarations.end(); ++it)
for (Declaration const* declaration: _declarations)
{
solAssert(*it, "");
solAssert(declaration, "");
// the declaration is functionDefinition, eventDefinition or a VariableDeclaration while declarations > 1
solAssert(dynamic_cast<FunctionDefinition const*>(*it) || dynamic_cast<EventDefinition const*>(*it) || dynamic_cast<VariableDeclaration const*>(*it),
"Found overloading involving something not a function or a variable");
solAssert(
dynamic_cast<FunctionDefinition const*>(declaration) ||
dynamic_cast<EventDefinition const*>(declaration) ||
dynamic_cast<VariableDeclaration const*>(declaration),
"Found overloading involving something not a function or a variable."
);
shared_ptr<FunctionType const> functionType { (*it)->functionType(false) };
FunctionTypePointer functionType { declaration->functionType(false) };
if (!functionType)
functionType = (*it)->functionType(true);
solAssert(functionType, "failed to determine the function type of the overloaded");
functionType = declaration->functionType(true);
solAssert(functionType, "Failed to determine the function type of the overloaded.");
for (auto parameter: functionType->parameterTypes() + functionType->returnParameterTypes())
if (!parameter)
reportFatalDeclarationError(_identifier.location(), "Function type can not be used in this context");
reportFatalDeclarationError(_identifier.location(), "Function type can not be used in this context.");
if (uniqueFunctions.end() == find_if(
uniqueFunctions.begin(),
@@ -276,11 +227,73 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
return newFunctionType && functionType->hasEqualArgumentTypes(*newFunctionType);
}
))
uniqueFunctions.push_back(*it);
uniqueFunctions.push_back(declaration);
}
return uniqueFunctions;
}
bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode)
{
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))
{
bool success = true;
m_currentScope = m_scopes[contract->scope()].get();
solAssert(!!m_currentScope, "");
for (ASTPointer<InheritanceSpecifier> const& baseContract: contract->baseContracts())
if (!resolveNamesAndTypes(*baseContract, true))
success = false;
m_currentScope = m_scopes[contract].get();
if (success)
{
linearizeBaseContracts(*contract);
vector<ContractDefinition const*> properBases(
++contract->annotation().linearizedBaseContracts.begin(),
contract->annotation().linearizedBaseContracts.end()
);
for (ContractDefinition const* base: properBases)
importInheritedScope(*base);
}
// these can contain code, only resolve parameters for now
for (ASTPointer<ASTNode> const& node: contract->subNodes())
{
m_currentScope = m_scopes[contract].get();
if (!resolveNamesAndTypes(*node, false))
{
success = false;
break;
}
}
if (!success)
return false;
if (!_resolveInsideCode)
return success;
m_currentScope = m_scopes[contract].get();
// now resolve references inside the code
for (ASTPointer<ASTNode> const& node: contract->subNodes())
{
m_currentScope = m_scopes[contract].get();
if (!resolveNamesAndTypes(*node, true))
success = false;
}
return success;
}
else
{
if (m_scopes.count(&_node))
m_currentScope = m_scopes[&_node].get();
return ReferencesResolver(m_errors, *this, _resolveInsideCode).resolve(_node);
}
}
void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
{
auto iterator = m_scopes.find(&_base);
@@ -89,6 +89,9 @@ public:
);
private:
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
/// Imports all members declared directly in the given contract (i.e. does not import inherited members)
/// into the current scope if they are not present already.
void importInheritedScope(ContractDefinition const& _base);
+1 -8
View File
@@ -35,14 +35,7 @@ using namespace dev::solidity;
bool ReferencesResolver::resolve(ASTNode const& _root)
{
try
{
_root.accept(*this);
}
catch (FatalError const&)
{
solAssert(m_errorOccurred, "");
}
_root.accept(*this);
return !m_errorOccurred;
}
+1 -1
View File
@@ -52,7 +52,7 @@ public:
m_resolveInsideCode(_resolveInsideCode)
{}
/// @returns true if no errors during resolving
/// @returns true if no errors during resolving and throws exceptions on fatal errors.
bool resolve(ASTNode const& _root);
private:
+1 -1
View File
@@ -611,7 +611,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
fatalTypeError(SourceLocation(), "Constant variables not yet implemented for inline assembly.");
if (var->isLocalVariable())
pushes = var->type()->sizeOnStack();
else if (var->type()->isValueType())
else if (!var->type()->isValueType())
pushes = 1;
else
pushes = 2; // slot number, intra slot offset
+4
View File
@@ -1652,6 +1652,7 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
{
TypePointer type = variable->annotation().type;
solAssert(type, "");
// Skip all mapping members if we are not in storage.
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
continue;
@@ -1967,6 +1968,8 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
if (auto structType = dynamic_cast<StructType const*>(returnType.get()))
{
for (auto const& member: structType->members(nullptr))
{
solAssert(member.type, "");
if (member.type->category() != Category::Mapping)
{
if (auto arrayType = dynamic_cast<ArrayType const*>(member.type.get()))
@@ -1975,6 +1978,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
retParams.push_back(member.type);
retParamNames.push_back(member.name);
}
}
}
else
{
+8 -1
View File
@@ -190,6 +190,10 @@ public:
}
(*this)(_instr.instruction);
}
void operator()(assembly::FunctionCall const&)
{
solAssert(false, "Function call not removed during desugaring phase.");
}
void operator()(Label const& _label)
{
m_state.assembly.setSourceLocation(_label.location);
@@ -249,7 +253,10 @@ public:
_block.location
);
}
}
void operator()(assembly::FunctionDefinition const&)
{
solAssert(false, "Function definition not removed during desugaring phase.");
}
private:
+6 -1
View File
@@ -48,17 +48,22 @@ struct Label { SourceLocation location; std::string name; };
struct Assignment { SourceLocation location; Identifier variableName; };
struct FunctionalAssignment;
struct VariableDeclaration;
struct FunctionDefinition;
struct FunctionCall;
struct Block;
using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionalInstruction, VariableDeclaration, Block>;
using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Block>;
/// Functional assignment ("x := mload(20)", expects push-1-expression on the right hand
/// side and requires x to occupy exactly one stack slot.
struct FunctionalAssignment { SourceLocation location; Identifier variableName; std::shared_ptr<Statement> value; };
/// Functional instruction, e.g. "mul(mload(20), add(2, x))"
struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector<Statement> arguments; };
struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Statement> arguments; };
/// Block-scope variable declaration ("let x := mload(20)"), non-hoisted
struct VariableDeclaration { SourceLocation location; std::string name; std::shared_ptr<Statement> value; };
/// Block that creates a scope (frees declared stack variables)
struct Block { SourceLocation location; std::vector<Statement> statements; };
/// Function definition ("function f(a, b) -> (d, e) { ... }")
struct FunctionDefinition { SourceLocation location; std::string name; std::vector<std::string> arguments; std::vector<std::string> returns; Block body; };
struct LocationExtractor: boost::static_visitor<SourceLocation>
{
+101 -39
View File
@@ -62,6 +62,8 @@ assembly::Statement Parser::parseStatement()
{
case Token::Let:
return parseVariableDeclaration();
case Token::Function:
return parseFunctionDefinition();
case Token::LBrace:
return parseBlock();
case Token::Assign:
@@ -214,10 +216,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
{
VariableDeclaration varDecl = createWithLocation<VariableDeclaration>();
expectToken(Token::Let);
varDecl.name = m_scanner->currentLiteral();
if (instructions().count(varDecl.name))
fatalParserError("Cannot use instruction names for identifier names.");
expectToken(Token::Identifier);
varDecl.name = expectAsmIdentifier();
expectToken(Token::Colon);
expectToken(Token::Assign);
varDecl.value.reset(new Statement(parseExpression()));
@@ -225,44 +224,107 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
return varDecl;
}
FunctionalInstruction Parser::parseFunctionalInstruction(assembly::Statement&& _instruction)
assembly::FunctionDefinition Parser::parseFunctionDefinition()
{
if (_instruction.type() != typeid(Instruction))
fatalParserError("Assembly instruction required in front of \"(\")");
FunctionalInstruction ret;
ret.instruction = std::move(boost::get<Instruction>(_instruction));
ret.location = ret.instruction.location;
solidity::Instruction instr = ret.instruction.instruction;
InstructionInfo instrInfo = instructionInfo(instr);
if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16)
fatalParserError("DUPi instructions not allowed for functional notation");
if (solidity::Instruction::SWAP1 <= instr && instr <= solidity::Instruction::SWAP16)
fatalParserError("SWAPi instructions not allowed for functional notation");
FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
expectToken(Token::Function);
funDef.name = expectAsmIdentifier();
expectToken(Token::LParen);
unsigned args = unsigned(instrInfo.args);
for (unsigned i = 0; i < args; ++i)
while (m_scanner->currentToken() != Token::RParen)
{
ret.arguments.emplace_back(parseExpression());
if (i != args - 1)
{
if (m_scanner->currentToken() != Token::Comma)
fatalParserError(string(
"Expected comma (" +
instrInfo.name +
" expects " +
boost::lexical_cast<string>(args) +
" arguments)"
));
else
m_scanner->next();
}
funDef.arguments.push_back(expectAsmIdentifier());
if (m_scanner->currentToken() == Token::RParen)
break;
expectToken(Token::Comma);
}
ret.location.end = endPosition();
if (m_scanner->currentToken() == Token::Comma)
fatalParserError(
string("Expected ')' (" + instrInfo.name + " expects " + boost::lexical_cast<string>(args) + " arguments)")
);
expectToken(Token::RParen);
return ret;
if (m_scanner->currentToken() == Token::Sub)
{
expectToken(Token::Sub);
expectToken(Token::GreaterThan);
expectToken(Token::LParen);
while (true)
{
funDef.returns.push_back(expectAsmIdentifier());
if (m_scanner->currentToken() == Token::RParen)
break;
expectToken(Token::Comma);
}
expectToken(Token::RParen);
}
funDef.body = parseBlock();
funDef.location.end = funDef.body.location.end;
return funDef;
}
assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _instruction)
{
if (_instruction.type() == typeid(Instruction))
{
FunctionalInstruction ret;
ret.instruction = std::move(boost::get<Instruction>(_instruction));
ret.location = ret.instruction.location;
solidity::Instruction instr = ret.instruction.instruction;
InstructionInfo instrInfo = instructionInfo(instr);
if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16)
fatalParserError("DUPi instructions not allowed for functional notation");
if (solidity::Instruction::SWAP1 <= instr && instr <= solidity::Instruction::SWAP16)
fatalParserError("SWAPi instructions not allowed for functional notation");
expectToken(Token::LParen);
unsigned args = unsigned(instrInfo.args);
for (unsigned i = 0; i < args; ++i)
{
ret.arguments.emplace_back(parseExpression());
if (i != args - 1)
{
if (m_scanner->currentToken() != Token::Comma)
fatalParserError(string(
"Expected comma (" +
instrInfo.name +
" expects " +
boost::lexical_cast<string>(args) +
" arguments)"
));
else
m_scanner->next();
}
}
ret.location.end = endPosition();
if (m_scanner->currentToken() == Token::Comma)
fatalParserError(
string("Expected ')' (" + instrInfo.name + " expects " + boost::lexical_cast<string>(args) + " arguments)")
);
expectToken(Token::RParen);
return ret;
}
else if (_instruction.type() == typeid(Identifier))
{
FunctionCall ret;
ret.functionName = std::move(boost::get<Identifier>(_instruction));
ret.location = ret.functionName.location;
expectToken(Token::LParen);
while (m_scanner->currentToken() != Token::RParen)
{
ret.arguments.emplace_back(parseExpression());
if (m_scanner->currentToken() == Token::RParen)
break;
expectToken(Token::Comma);
}
ret.location.end = endPosition();
expectToken(Token::RParen);
return ret;
}
else
fatalParserError("Assembly instruction or function name required in front of \"(\")");
return {};
}
string Parser::expectAsmIdentifier()
{
string name = m_scanner->currentLiteral();
if (instructions().count(name))
fatalParserError("Cannot use instruction names for identifier names.");
expectToken(Token::Identifier);
return name;
}
+3 -1
View File
@@ -67,7 +67,9 @@ protected:
std::map<std::string, dev::solidity::Instruction> const& instructions();
Statement parseElementaryOperation(bool _onlySinglePusher = false);
VariableDeclaration parseVariableDeclaration();
FunctionalInstruction parseFunctionalInstruction(Statement&& _instruction);
FunctionDefinition parseFunctionDefinition();
Statement parseFunctionalInstruction(Statement&& _instruction);
std::string expectAsmIdentifier();
};
}
+18
View File
@@ -112,6 +112,24 @@ string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDecl
return "let " + _variableDeclaration.name + " := " + boost::apply_visitor(*this, *_variableDeclaration.value);
}
string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefinition)
{
string out = "function " + _functionDefinition.name + "(" + boost::algorithm::join(_functionDefinition.arguments, ", ") + ")";
if (!_functionDefinition.returns.empty())
out += " -> (" + boost::algorithm::join(_functionDefinition.returns, ", ") + ")";
return out + "\n" + (*this)(_functionDefinition.body);
}
string AsmPrinter::operator()(assembly::FunctionCall const& _functionCall)
{
return
(*this)(_functionCall.functionName) + "(" +
boost::algorithm::join(
_functionCall.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)),
", " ) +
")";
}
string AsmPrinter::operator()(Block const& _block)
{
if (_block.statements.empty())
+2
View File
@@ -53,6 +53,8 @@ public:
std::string operator()(assembly::Assignment const& _assignment);
std::string operator()(assembly::FunctionalAssignment const& _functionalAssignment);
std::string operator()(assembly::VariableDeclaration const& _variableDeclaration);
std::string operator()(assembly::FunctionDefinition const& _functionDefinition);
std::string operator()(assembly::FunctionCall const& _functionCall);
std::string operator()(assembly::Block const& _block);
};