Browse Source

Merge branch 'build_enhancement' of https://github.com/ethereum/cpp-ethereum into build_enhancement

cl-refactor
ethdev 10 years ago
parent
commit
af48f7cd62
  1. 2
      CMakeLists.txt
  2. 2
      cmake/EthDependencies.cmake
  3. 43
      libsolidity/Compiler.cpp
  4. 30
      libsolidity/CompilerContext.cpp
  5. 13
      libsolidity/CompilerContext.h
  6. 71
      libsolidity/CompilerUtils.cpp
  7. 63
      libsolidity/CompilerUtils.h
  8. 62
      libsolidity/ExpressionCompiler.cpp
  9. 5
      libsolidity/ExpressionCompiler.h
  10. 2
      solc/CMakeLists.txt
  11. 368
      solc/CommandLineInterface.cpp
  12. 71
      solc/CommandLineInterface.h
  13. 135
      solc/main.cpp
  14. 4
      test/solidityCompiler.cpp
  15. 35
      test/solidityEndToEndTest.cpp

2
CMakeLists.txt

@ -1,5 +1,5 @@
# cmake global # cmake global
cmake_minimum_required(VERSION 2.8.9) cmake_minimum_required(VERSION 2.8.12)
# let cmake autolink dependencies on windows # let cmake autolink dependencies on windows
# it's specified globally, cause qt libraries requires that on windows and they are also found globally # it's specified globally, cause qt libraries requires that on windows and they are also found globally
cmake_policy(SET CMP0020 NEW) cmake_policy(SET CMP0020 NEW)

2
cmake/EthDependencies.cmake

@ -125,7 +125,7 @@ elseif (UNIX)
endif() endif()
find_package(Boost 1.54.0 REQUIRED COMPONENTS thread date_time system regex chrono filesystem unit_test_framework) find_package(Boost 1.54.0 REQUIRED COMPONENTS thread date_time system regex chrono filesystem unit_test_framework program_options)
message(" - boost header: ${Boost_INCLUDE_DIRS}") message(" - boost header: ${Boost_INCLUDE_DIRS}")
message(" - boost lib : ${Boost_LIBRARIES}") message(" - boost lib : ${Boost_LIBRARIES}")

43
libsolidity/Compiler.cpp

@ -26,6 +26,7 @@
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/Compiler.h> #include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h> #include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerUtils.h>
using namespace std; using namespace std;
@ -135,7 +136,7 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters()) for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
{ {
unsigned const numBytes = var->getType()->getCalldataEncodedSize(); unsigned const numBytes = var->getType()->getCalldataEncodedSize();
if (numBytes == 0) if (numBytes == 0 || numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError() BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(var->getLocation()) << errinfo_sourceLocation(var->getLocation())
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported.")); << errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
@ -154,18 +155,20 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
//@todo this can be also done more efficiently //@todo this can be also done more efficiently
unsigned dataOffset = 0; unsigned dataOffset = 0;
vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters(); vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters();
unsigned stackDepth = CompilerUtils(m_context).getSizeOnStack(parameters);
for (unsigned i = 0; i < parameters.size(); ++i) for (unsigned i = 0; i < parameters.size(); ++i)
{ {
Type const& paramType = *parameters[i]->getType(); Type const& paramType = *parameters[i]->getType();
unsigned numBytes = paramType.getCalldataEncodedSize(); unsigned numBytes = paramType.getCalldataEncodedSize();
if (numBytes == 0) if (numBytes == 0 || numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError() BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(parameters[i]->getLocation()) << errinfo_sourceLocation(parameters[i]->getLocation())
<< errinfo_comment("Type " + paramType.toString() + " not yet supported.")); << errinfo_comment("Type " + paramType.toString() + " not yet supported."));
m_context << eth::dupInstruction(parameters.size() - i); CompilerUtils(m_context).copyToStackTop(stackDepth, paramType);
if (numBytes != 32) if (numBytes != 32)
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
m_context << u256(dataOffset) << eth::Instruction::MSTORE; m_context << u256(dataOffset) << eth::Instruction::MSTORE;
stackDepth -= paramType.getSizeOnStack();
dataOffset += numBytes; dataOffset += numBytes;
} }
// note that the stack is not cleaned up here // note that the stack is not cleaned up here
@ -195,15 +198,12 @@ bool Compiler::visit(FunctionDefinition& _function)
// stack upon entry: [return address] [arg0] [arg1] ... [argn] // stack upon entry: [return address] [arg0] [arg1] ... [argn]
// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp] // reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
unsigned const numArguments = _function.getParameters().size(); for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters())
unsigned const numReturnValues = _function.getReturnParameters().size();
unsigned const numLocalVariables = _function.getLocalVariables().size();
for (ASTPointer<VariableDeclaration> const& variable: _function.getParameters() + _function.getReturnParameters())
m_context.addVariable(*variable); m_context.addVariable(*variable);
for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters())
m_context.addAndInitializeVariable(*variable);
for (VariableDeclaration const* localVariable: _function.getLocalVariables()) for (VariableDeclaration const* localVariable: _function.getLocalVariables())
m_context.addVariable(*localVariable); m_context.addAndInitializeVariable(*localVariable);
m_context.initializeLocalVariables(numReturnValues + numLocalVariables);
_function.getBody().accept(*this); _function.getBody().accept(*this);
@ -215,12 +215,16 @@ bool Compiler::visit(FunctionDefinition& _function)
// Note that the fact that the return arguments are of increasing index is vital for this // Note that the fact that the return arguments are of increasing index is vital for this
// algorithm to work. // algorithm to work.
unsigned const argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters());
unsigned const returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters());
unsigned const localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables());
vector<int> stackLayout; vector<int> stackLayout;
stackLayout.push_back(numReturnValues); // target of return address stackLayout.push_back(returnValuesSize); // target of return address
stackLayout += vector<int>(numArguments, -1); // discard all arguments stackLayout += vector<int>(argumentsSize, -1); // discard all arguments
for (unsigned i = 0; i < numReturnValues; ++i) for (unsigned i = 0; i < returnValuesSize; ++i)
stackLayout.push_back(i); stackLayout.push_back(i);
stackLayout += vector<int>(numLocalVariables, -1); stackLayout += vector<int>(localVariablesSize, -1);
while (stackLayout.back() != int(stackLayout.size() - 1)) while (stackLayout.back() != int(stackLayout.size() - 1))
if (stackLayout.back() < 0) if (stackLayout.back() < 0)
@ -298,8 +302,7 @@ bool Compiler::visit(Return& _return)
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front(); VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front();
ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType()); ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType());
unsigned stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(firstVariable)); CompilerUtils(m_context).moveToStackVariable(firstVariable);
m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
} }
m_context.appendJumpTo(m_returnTag); m_context.appendJumpTo(m_returnTag);
return false; return false;
@ -313,9 +316,7 @@ bool Compiler::visit(VariableDefinition& _variableDefinition)
ExpressionCompiler::appendTypeConversion(m_context, ExpressionCompiler::appendTypeConversion(m_context,
*expression->getType(), *expression->getType(),
*_variableDefinition.getDeclaration().getType()); *_variableDefinition.getDeclaration().getType());
unsigned baseStackOffset = m_context.getBaseStackOffsetOfVariable(_variableDefinition.getDeclaration()); CompilerUtils(m_context).moveToStackVariable(_variableDefinition.getDeclaration());
unsigned stackPosition = m_context.baseToCurrentStackOffset(baseStackOffset);
m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
} }
return false; return false;
} }
@ -324,9 +325,7 @@ bool Compiler::visit(ExpressionStatement& _expressionStatement)
{ {
Expression& expression = _expressionStatement.getExpression(); Expression& expression = _expressionStatement.getExpression();
ExpressionCompiler::compileExpression(m_context, expression); ExpressionCompiler::compileExpression(m_context, expression);
// Type::Category category = expression.getType()->getCategory(); CompilerUtils(m_context).popStackElement(*expression.getType());
for (unsigned i = 0; i < expression.getType()->getSizeOnStack(); ++i)
m_context << eth::Instruction::POP;
return false; return false;
} }

30
libsolidity/CompilerContext.cpp

@ -41,20 +41,30 @@ void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
m_stateVariablesSize += _declaration.getType()->getStorageSize(); m_stateVariablesSize += _declaration.getType()->getStorageSize();
} }
void CompilerContext::initializeLocalVariables(unsigned _numVariables) void CompilerContext::addVariable(VariableDeclaration const& _declaration)
{ {
if (_numVariables > 0) m_localVariables[&_declaration] = m_localVariablesSize;
{ m_localVariablesSize += _declaration.getType()->getSizeOnStack();
}
void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration)
{
addVariable(_declaration);
unsigned const size = _declaration.getType()->getSizeOnStack();
for (unsigned i = 0; i < size; ++i)
*this << u256(0); *this << u256(0);
for (unsigned i = 1; i < _numVariables; ++i) m_asm.adjustDeposit(-size);
*this << eth::Instruction::DUP1; }
m_asm.adjustDeposit(-_numVariables);
} void CompilerContext::addFunction(FunctionDefinition const& _function)
{
m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag()));
} }
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
{ {
return std::find(m_localVariables.begin(), m_localVariables.end(), _declaration) != m_localVariables.end(); return m_localVariables.count(_declaration) > 0;
} }
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const
@ -67,10 +77,10 @@ eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition cons
unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const
{ {
auto res = find(begin(m_localVariables), end(m_localVariables), &_declaration); auto res = m_localVariables.find(&_declaration);
if (asserts(res != m_localVariables.end())) if (asserts(res != m_localVariables.end()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found on stack.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found on stack."));
return unsigned(end(m_localVariables) - res - 1); return m_localVariablesSize - res->second - 1;
} }
unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const

13
libsolidity/CompilerContext.h

@ -25,6 +25,7 @@
#include <ostream> #include <ostream>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h> #include <libevmcore/Assembly.h>
#include <libsolidity/ASTForward.h>
#include <libsolidity/Types.h> #include <libsolidity/Types.h>
namespace dev { namespace dev {
@ -43,9 +44,9 @@ public:
void addMagicGlobal(MagicVariableDeclaration const& _declaration); void addMagicGlobal(MagicVariableDeclaration const& _declaration);
void addStateVariable(VariableDeclaration const& _declaration); void addStateVariable(VariableDeclaration const& _declaration);
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); } void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
void initializeLocalVariables(unsigned _numVariables); void addVariable(VariableDeclaration const& _declaration);
void addVariable(VariableDeclaration const& _declaration) { m_localVariables.push_back(&_declaration); } void addAndInitializeVariable(VariableDeclaration const& _declaration);
void addFunction(FunctionDefinition const& _function) { m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag())); } void addFunction(FunctionDefinition const& _function);
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
@ -98,8 +99,10 @@ private:
u256 m_stateVariablesSize; u256 m_stateVariablesSize;
/// Storage offsets of state variables /// Storage offsets of state variables
std::map<Declaration const*, u256> m_stateVariables; std::map<Declaration const*, u256> m_stateVariables;
/// Offsets of local variables on the stack. /// Offsets of local variables on the stack (relative to stack base).
std::vector<Declaration const*> m_localVariables; std::map<Declaration const*, unsigned> m_localVariables;
/// Sum of stack sizes of local variables
unsigned m_localVariablesSize;
/// Labels pointing to the entry points of funcitons. /// Labels pointing to the entry points of funcitons.
std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels; std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels;
}; };

71
libsolidity/CompilerUtils.cpp

@ -0,0 +1,71 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum 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.
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Routines used by both the compiler and the expression compiler.
*/
#include <libsolidity/CompilerUtils.h>
#include <libsolidity/AST.h>
#include <libevmcore/Instruction.h>
using namespace std;
namespace dev
{
namespace solidity
{
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
{
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable));
unsigned const size = _variable.getType()->getSizeOnStack();
// move variable starting from its top end in the stack
if (stackPosition - size + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation())
<< errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < size; ++i)
m_context << eth::swapInstruction(stackPosition - size + 1) << eth::Instruction::POP;
}
void CompilerUtils::copyToStackTop(unsigned _stackDepth, Type const& _type)
{
if (_stackDepth > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Stack too deep."));
unsigned const size = _type.getSizeOnStack();
for (unsigned i = 0; i < size; ++i)
m_context << eth::dupInstruction(_stackDepth);
}
void CompilerUtils::popStackElement(Type const& _type)
{
unsigned const size = _type.getSizeOnStack();
for (unsigned i = 0; i < size; ++i)
m_context << eth::Instruction::POP;
}
unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _variableTypes)
{
unsigned size = 0;
for (shared_ptr<Type const> const& type: _variableTypes)
size += type->getSizeOnStack();
return size;
}
}
}

63
libsolidity/CompilerUtils.h

@ -0,0 +1,63 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum 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.
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Routines used by both the compiler and the expression compiler.
*/
#pragma once
#include <libsolidity/CompilerContext.h>
#include <libsolidity/ASTForward.h>
namespace dev {
namespace solidity {
class Type; // forward
class CompilerUtils
{
public:
CompilerUtils(CompilerContext& _context): m_context(_context) {}
/// Moves the value that is at the top of the stack to a stack variable.
void moveToStackVariable(VariableDeclaration const& _variable);
/// Copies a variable of type @a _type from a stack depth of @a _stackDepth to the top of the stack.
void copyToStackTop(unsigned _stackDepth, Type const& _type);
/// Removes the current value from the top of the stack.
void popStackElement(Type const& _type);
template <class T>
static unsigned getSizeOnStack(std::vector<T> const& _variables);
static unsigned getSizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes);
private:
CompilerContext& m_context;
};
template <class T>
unsigned CompilerUtils::getSizeOnStack(std::vector<T> const& _variables)
{
unsigned size = 0;
for (T const& variable: _variables)
size += variable->getType()->getSizeOnStack();
return size;
}
}
}

62
libsolidity/ExpressionCompiler.cpp

@ -26,6 +26,7 @@
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/ExpressionCompiler.h> #include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerContext.h> #include <libsolidity/CompilerContext.h>
#include <libsolidity/CompilerUtils.h>
using namespace std; using namespace std;
@ -174,10 +175,8 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
// explicit type conversion contract -> address, nothing to do. // explicit type conversion contract -> address, nothing to do.
} }
else else
{
appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); appendTypeConversion(*firstArgument.getType(), *_functionCall.getType());
} }
}
else else
{ {
FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType()); FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType());
@ -203,13 +202,14 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
m_context.appendJump(); m_context.appendJump();
m_context << returnLabel; m_context << returnLabel;
unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes());
// callee adds return parameters, but removes arguments and return label // callee adds return parameters, but removes arguments and return label
m_context.adjustStackOffset(function.getReturnParameterTypes().size() - arguments.size() - 1); m_context.adjustStackOffset(returnParametersSize - CompilerUtils::getSizeOnStack(arguments) - 1);
// @todo for now, the return value of a function is its first return value, so remove // @todo for now, the return value of a function is its first return value, so remove
// all others // all others
for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i) for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i)
m_context << eth::Instruction::POP; CompilerUtils(m_context).popStackElement(*function.getReturnParameterTypes()[i]);
break; break;
} }
case Location::EXTERNAL: case Location::EXTERNAL:
@ -356,7 +356,7 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
{ {
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType()); StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD; m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD;
m_currentLValue = LValue(m_context, LValue::STORAGE); m_currentLValue = LValue(m_context, LValue::STORAGE, *_memberAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
break; break;
} }
@ -376,7 +376,7 @@ bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE; m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE;
m_context << u256(64) << u256(0) << eth::Instruction::SHA3; m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
m_currentLValue = LValue(m_context, LValue::STORAGE); m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
return false; return false;
@ -565,6 +565,13 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND; m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND;
} }
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),
m_stackSize(_dataType.getSizeOnStack())
{
}
void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const
{ {
switch (m_type) switch (m_type)
@ -575,6 +582,7 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep.")); << errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_stackSize; ++i)
*m_context << eth::dupInstruction(stackPos + 1); *m_context << eth::dupInstruction(stackPos + 1);
break; break;
} }
@ -583,7 +591,17 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
break; // no distinction between value and reference for non-value types break; // no distinction between value and reference for non-value types
if (!_remove) if (!_remove)
*m_context << eth::Instruction::DUP1; *m_context << eth::Instruction::DUP1;
if (m_stackSize == 1)
*m_context << eth::Instruction::SLOAD; *m_context << eth::Instruction::SLOAD;
else
for (unsigned i = 0; i < m_stackSize; ++i)
{
*m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
if (i + 1 < m_stackSize)
*m_context << u256(1) << eth::Instruction::ADD;
else
*m_context << eth::Instruction::POP;
}
break; break;
case MEMORY: case MEMORY:
if (!_expression.getType()->isValueType()) if (!_expression.getType()->isValueType())
@ -604,12 +622,13 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
{ {
case STACK: case STACK:
{ {
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_stackSize + 1;
if (stackPos > 16) if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep.")); << errinfo_comment("Stack too deep."));
else if (stackPos > 0) else if (stackDiff > 0)
*m_context << eth::swapInstruction(stackPos) << eth::Instruction::POP; for (unsigned i = 0; i < m_stackSize; ++i)
*m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
if (!_move) if (!_move)
retrieveValue(_expression); retrieveValue(_expression);
break; break;
@ -617,9 +636,27 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
case LValue::STORAGE: case LValue::STORAGE:
if (!_expression.getType()->isValueType()) if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types break; // no distinction between value and reference for non-value types
if (!_move) // stack layout: value value ... value ref
*m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1; if (!_move) // copy values
{
if (m_stackSize + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_stackSize; ++i)
*m_context << eth::dupInstruction(m_stackSize + 1) << eth::Instruction::SWAP1;
}
if (m_stackSize > 0) // store high index value first
*m_context << u256(m_stackSize - 1) << eth::Instruction::ADD;
for (unsigned i = 0; i < m_stackSize; ++i)
{
if (i + 1 >= m_stackSize)
*m_context << eth::Instruction::SSTORE; *m_context << eth::Instruction::SSTORE;
else
// v v ... v v r+x
*m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2
<< eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
}
break; break;
case LValue::MEMORY: case LValue::MEMORY:
if (!_expression.getType()->isValueType()) if (!_expression.getType()->isValueType())
@ -645,6 +682,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration) void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{ {
m_stackSize = _identifier.getType()->getSizeOnStack();
if (m_context->isLocalVariable(&_declaration)) if (m_context->isLocalVariable(&_declaration))
{ {
m_type = STACK; m_type = STACK;

5
libsolidity/ExpressionCompiler.h

@ -93,8 +93,7 @@ private:
enum LValueType { NONE, STACK, MEMORY, STORAGE }; enum LValueType { NONE, STACK, MEMORY, STORAGE };
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); } explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
LValue(CompilerContext& _compilerContext, LValueType _type, unsigned _baseStackOffset = 0): LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset = 0);
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) {}
/// Set type according to the declaration and retrieve the reference. /// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression /// @a _expression is the current expression
@ -129,6 +128,8 @@ private:
/// If m_type is STACK, this is base stack offset (@see /// If m_type is STACK, this is base stack offset (@see
/// CompilerContext::getBaseStackOffsetOfVariable) of a local variable. /// CompilerContext::getBaseStackOffsetOfVariable) of a local variable.
unsigned m_baseStackOffset; unsigned m_baseStackOffset;
/// Size of the value of this lvalue on the stack.
unsigned m_stackSize;
}; };
CompilerContext& m_context; CompilerContext& m_context;

2
solc/CMakeLists.txt

@ -11,6 +11,8 @@ set(EXECUTABLE solc)
file(GLOB HEADERS "*.h") file(GLOB HEADERS "*.h")
add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARY_RELEASE})
target_link_libraries(${EXECUTABLE} ${Boost_PROGRAM_OPTIONS_LIBRARY_RELEASE})
target_link_libraries(${EXECUTABLE} solidity) target_link_libraries(${EXECUTABLE} solidity)
install( TARGETS ${EXECUTABLE} DESTINATION bin ) install( TARGETS ${EXECUTABLE} DESTINATION bin )

368
solc/CommandLineInterface.cpp

@ -0,0 +1,368 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum 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.
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Lefteris <lefteris@ethdev.com>
* @date 2014
* Solidity command line interface.
*/
#include "CommandLineInterface.h"
#include <string>
#include <iostream>
#include <fstream>
#include <boost/filesystem.hpp>
#include "BuildInfo.h"
#include <libdevcore/Common.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/ASTPrinter.h>
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Exceptions.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h>
using namespace std;
namespace po = boost::program_options;
namespace dev
{
namespace solidity
{
static void version()
{
cout << "solc, the solidity complier commandline interface " << dev::Version << endl
<< " by Christian <c@ethdev.com> and Lefteris <lefteris@ethdev.com>, (c) 2014." << endl
<< "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl;
exit(0);
}
static inline bool argToStdout(po::variables_map const& _args, const char* _name)
{
return _args.count(_name) && _args[_name].as<OutputType>() != OutputType::FILE;
}
static bool needStdout(po::variables_map const& _args)
{
return argToStdout(_args, "abi") || argToStdout(_args, "natspec-user") || argToStdout(_args, "natspec-dev") ||
argToStdout(_args, "asm") || argToStdout(_args, "opcodes") || argToStdout(_args, "binary");
}
static inline bool outputToFile(OutputType type)
{
return type == OutputType::FILE || type == OutputType::BOTH;
}
static inline bool outputToStdout(OutputType type)
{
return type == OutputType::STDOUT || type == OutputType::BOTH;
}
static std::istream& operator>>(std::istream& _in, OutputType& io_output)
{
std::string token;
_in >> token;
if (token == "stdout")
io_output = OutputType::STDOUT;
else if (token == "file")
io_output = OutputType::FILE;
else if (token == "both")
io_output = OutputType::BOTH;
else
throw boost::program_options::invalid_option_value(token);
return _in;
}
void CommandLineInterface::handleBinary(string const& _contract)
{
auto choice = m_args["binary"].as<OutputType>();
if (outputToStdout(choice))
{
cout << "Binary: " << endl;
cout << toHex(m_compiler.getBytecode(_contract)) << endl;
}
if (outputToFile(choice))
{
ofstream outFile(_contract + ".binary");
outFile << toHex(m_compiler.getBytecode(_contract));
outFile.close();
}
}
void CommandLineInterface::handleOpcode(string const& _contract)
{
// TODO: Figure out why the wrong operator << (from boost) is used here
auto choice = m_args["opcode"].as<OutputType>();
if (outputToStdout(choice))
{
cout << "Opcodes: " << endl;
dev::operator<<(cout, m_compiler.getBytecode(_contract));
cout << endl;
}
if (outputToFile(choice))
{
ofstream outFile(_contract + ".opcode");
dev::operator<<(outFile, m_compiler.getBytecode(_contract));
outFile.close();
}
}
void CommandLineInterface::handleBytecode(string const& _contract)
{
if (m_args.count("opcodes"))
handleOpcode(_contract);
if (m_args.count("binary"))
handleBinary(_contract);
}
void CommandLineInterface::handleJson(DocumentationType _type,
string const& _contract)
{
std::string argName;
std::string suffix;
std::string title;
switch(_type)
{
case DocumentationType::ABI_INTERFACE:
argName = "abi";
suffix = ".abi";
title = "Contract ABI";
break;
case DocumentationType::NATSPEC_USER:
argName = "natspec-user";
suffix = ".docuser";
title = "User Documentation";
break;
case DocumentationType::NATSPEC_DEV:
argName = "natspec-dev";
suffix = ".docdev";
title = "Developer Documentation";
break;
default:
// should never happen
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation _type"));
}
if (m_args.count(argName))
{
auto choice = m_args[argName].as<OutputType>();
if (outputToStdout(choice))
{
cout << title << endl;
cout << m_compiler.getJsonDocumentation(_contract, _type);
}
if (outputToFile(choice))
{
ofstream outFile(_contract + suffix);
outFile << m_compiler.getJsonDocumentation(_contract, _type);
outFile.close();
}
}
}
bool CommandLineInterface::parseArguments(int argc, char** argv)
{
#define OUTPUT_TYPE_STR "Legal values:\n" \
"\tstdout: Print it to standard output\n" \
"\tfile: Print it to a file with same name\n" \
"\tboth: Print both to a file and the stdout\n"
// Declare the supported options.
po::options_description desc("Allowed options");
desc.add_options()
("help", "Show help message and exit")
("version", "Show version and exit")
("optimize", po::value<bool>()->default_value(false), "Optimize bytecode for size")
("input-file", po::value<vector<string>>(), "input file")
("ast", po::value<OutputType>(),
"Request to output the AST of the contract. " OUTPUT_TYPE_STR)
("asm", po::value<OutputType>(),
"Request to output the EVM assembly of the contract. " OUTPUT_TYPE_STR)
("opcodes", po::value<OutputType>(),
"Request to output the Opcodes of the contract. " OUTPUT_TYPE_STR)
("binary", po::value<OutputType>(),
"Request to output the contract in binary (hexadecimal). " OUTPUT_TYPE_STR)
("abi", po::value<OutputType>(),
"Request to output the contract's ABI interface. " OUTPUT_TYPE_STR)
("natspec-user", po::value<OutputType>(),
"Request to output the contract's Natspec user documentation. " OUTPUT_TYPE_STR)
("natspec-dev", po::value<OutputType>(),
"Request to output the contract's Natspec developer documentation. " OUTPUT_TYPE_STR);
#undef OUTPUT_TYPE_STR
// All positional options should be interpreted as input files
po::positional_options_description p;
p.add("input-file", -1);
// parse the compiler arguments
try
{
po::store(po::command_line_parser(argc, argv).options(desc).positional(p).allow_unregistered().run(), m_args);
}
catch (po::error const& exception)
{
cout << exception.what() << endl;
return false;
}
po::notify(m_args);
if (m_args.count("help"))
{
cout << desc;
return false;
}
if (m_args.count("version"))
{
version();
return false;
}
return true;
}
bool CommandLineInterface::processInput()
{
if (!m_args.count("input-file"))
{
string s;
while (!cin.eof())
{
getline(cin, s);
m_sourceCodes["<stdin>"].append(s);
}
}
else
for (string const& infile: m_args["input-file"].as<vector<string>>())
m_sourceCodes[infile] = asString(dev::contents(infile));
try
{
for (auto const& sourceCode: m_sourceCodes)
m_compiler.addSource(sourceCode.first, sourceCode.second);
// TODO: Perhaps we should not compile unless requested
m_compiler.compile(m_args["optimize"].as<bool>());
}
catch (ParserError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", m_compiler);
return false;
}
catch (DeclarationError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", m_compiler);
return false;
}
catch (TypeError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", m_compiler);
return false;
}
catch (CompilerError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", m_compiler);
return false;
}
catch (InternalCompilerError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", m_compiler);
return false;
}
catch (Exception const& exception)
{
cerr << "Exception during compilation: " << boost::diagnostic_information(exception) << endl;
return false;
}
catch (...)
{
cerr << "Unknown exception during compilation." << endl;
return false;
}
return true;
}
void CommandLineInterface::actOnInput()
{
// do we need AST output?
if (m_args.count("ast"))
{
auto choice = m_args["ast"].as<OutputType>();
if (outputToStdout(choice))
{
cout << "Syntax trees:" << endl << endl;
for (auto const& sourceCode: m_sourceCodes)
{
cout << endl << "======= " << sourceCode.first << " =======" << endl;
ASTPrinter printer(m_compiler.getAST(sourceCode.first), sourceCode.second);
printer.print(cout);
}
}
if (outputToFile(choice))
{
for (auto const& sourceCode: m_sourceCodes)
{
boost::filesystem::path p(sourceCode.first);
ofstream outFile(p.stem().string() + ".ast");
ASTPrinter printer(m_compiler.getAST(sourceCode.first), sourceCode.second);
printer.print(outFile);
outFile.close();
}
}
}
vector<string> contracts = m_compiler.getContractNames();
for (string const& contract: contracts)
{
if (needStdout(m_args))
cout << endl << "======= " << contract << " =======" << endl;
// do we need EVM assembly?
if (m_args.count("asm"))
{
auto choice = m_args["asm"].as<OutputType>();
if (outputToStdout(choice))
{
cout << "EVM assembly:" << endl;
m_compiler.streamAssembly(cout, contract);
}
if (outputToFile(choice))
{
ofstream outFile(contract + ".evm");
m_compiler.streamAssembly(outFile, contract);
outFile.close();
}
}
handleBytecode(contract);
handleJson(DocumentationType::ABI_INTERFACE, contract);
handleJson(DocumentationType::NATSPEC_DEV, contract);
handleJson(DocumentationType::NATSPEC_USER, contract);
} // end of contracts iteration
}
}
}

71
solc/CommandLineInterface.h

@ -0,0 +1,71 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum 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.
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Lefteris <lefteris@ethdev.com>
* @date 2014
* Solidity command line interface.
*/
#pragma once
#include <boost/program_options.hpp>
#include <libsolidity/CompilerStack.h>
namespace dev
{
namespace solidity
{
//forward declaration
enum class DocumentationType: uint8_t;
enum class OutputType: uint8_t
{
STDOUT,
FILE,
BOTH
};
class CommandLineInterface
{
public:
CommandLineInterface() {}
/// Parse command line arguments and return false if we should not continue
bool parseArguments(int argc, char** argv);
/// Parse the files and create source code objects
bool processInput();
/// Perform actions on the input depending on provided compiler arguments
void actOnInput();
private:
void handleBinary(std::string const& _contract);
void handleOpcode(std::string const& _contract);
void handleBytecode(std::string const& _contract);
void handleJson(DocumentationType _type,
std::string const& _contract);
/// Compiler arguments variable map
boost::program_options::variables_map m_args;
/// map of input files to source code strings
std::map<std::string, std::string> m_sourceCodes;
/// Solidity compiler stack
dev::solidity::CompilerStack m_compiler;
};
}
}

135
solc/main.cpp

@ -20,137 +20,16 @@
* Solidity commandline compiler. * Solidity commandline compiler.
*/ */
#include <string> #include "CommandLineInterface.h"
#include <iostream>
#include <libdevcore/Common.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/ASTPrinter.h>
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Exceptions.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h>
using namespace std;
using namespace dev;
using namespace solidity;
void help()
{
cout << "Usage solc [OPTIONS] <file>" << endl
<< "Options:" << endl
<< " -o,--optimize Optimize the bytecode for size." << endl
<< " -h,--help Show this help message and exit." << endl
<< " -V,--version Show the version and exit." << endl;
exit(0);
}
void version()
{
cout << "solc, the solidity complier commandline interface " << dev::Version << endl
<< " by Christian <c@ethdev.com>, (c) 2014." << endl
<< "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl;
exit(0);
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
vector<string> infiles; dev::solidity::CommandLineInterface cli;
bool optimize = false; if (!cli.parseArguments(argc, argv))
for (int i = 1; i < argc; ++i) return 1;
{ if (!cli.processInput())
string arg = argv[i]; return 1;
if (arg == "-o" || arg == "--optimize") cli.actOnInput();
optimize = true;
else if (arg == "-h" || arg == "--help")
help();
else if (arg == "-V" || arg == "--version")
version();
else
infiles.push_back(argv[i]);
}
map<string, string> sourceCodes;
if (infiles.empty())
{
string s;
while (!cin.eof())
{
getline(cin, s);
sourceCodes["<stdin>"].append(s);
}
}
else
for (string const& infile: infiles)
sourceCodes[infile] = asString(dev::contents(infile));
CompilerStack compiler;
try
{
for (auto const& sourceCode: sourceCodes)
compiler.addSource(sourceCode.first, sourceCode.second);
compiler.compile(optimize);
}
catch (ParserError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", compiler);
return -1;
}
catch (DeclarationError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", compiler);
return -1;
}
catch (TypeError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", compiler);
return -1;
}
catch (CompilerError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", compiler);
return -1;
}
catch (InternalCompilerError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", compiler);
return -1;
}
catch (Exception const& exception)
{
cerr << "Exception during compilation: " << boost::diagnostic_information(exception) << endl;
return -1;
}
catch (...)
{
cerr << "Unknown exception during compilation." << endl;
return -1;
}
cout << "Syntax trees:" << endl << endl;
for (auto const& sourceCode: sourceCodes)
{
cout << endl << "======= " << sourceCode.first << " =======" << endl;
ASTPrinter printer(compiler.getAST(sourceCode.first), sourceCode.second);
printer.print(cout);
}
vector<string> contracts = compiler.getContractNames();
cout << endl << "Contracts:" << endl;
for (string const& contract: contracts)
{
cout << endl << "======= " << contract << " =======" << endl
<< "EVM assembly:" << endl;
compiler.streamAssembly(cout, contract);
cout << "Opcodes:" << endl
<< eth::disassemble(compiler.getBytecode(contract)) << endl
<< "Binary: " << toHex(compiler.getBytecode(contract)) << endl
<< "Interface specification: " << compiler.getJsonDocumentation(contract, DocumentationType::ABI_INTERFACE) << endl
<< "Natspec user documentation: " << compiler.getJsonDocumentation(contract, DocumentationType::NATSPEC_USER) << endl
<< "Natspec developer documentation: " << compiler.getJsonDocumentation(contract, DocumentationType::NATSPEC_DEV) << endl;
}
return 0; return 0;
} }

4
test/solidityCompiler.cpp

@ -125,8 +125,8 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers)
byte(Instruction::JUMP), // end of f byte(Instruction::JUMP), // end of f
byte(Instruction::JUMPDEST), // beginning of g byte(Instruction::JUMPDEST), // beginning of g
byte(Instruction::PUSH1), 0x0, byte(Instruction::PUSH1), 0x0,
byte(Instruction::DUP1), // initialized e and h byte(Instruction::PUSH1), 0x0, // initialized e and h
byte(Instruction::PUSH1), byte(0x29 + shift), // ret address byte(Instruction::PUSH1), byte(0x2a + shift), // ret address
byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND),
byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND),
byte(Instruction::PUSH1), 0x3, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), byte(Instruction::PUSH1), 0x3, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND),

35
test/solidityEndToEndTest.cpp

@ -1026,6 +1026,41 @@ BOOST_AUTO_TEST_CASE(calls_to_this)
BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 10)); BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 10));
} }
BOOST_AUTO_TEST_CASE(inter_contract_calls_with_local_vars)
{
// note that a reference to another contract's function occupies two stack slots,
// so this tests correct stack slot allocation
char const* sourceCode = R"(
contract Helper {
function multiply(uint a, uint b) returns (uint c) {
return a * b;
}
}
contract Main {
Helper h;
function callHelper(uint a, uint b) returns (uint c) {
var fu = h.multiply;
var y = 9;
var ret = fu(a, b);
return ret + y;
}
function getHelper() returns (address haddress) {
return address(h);
}
function setHelper(address haddress) {
h = Helper(haddress);
}
})";
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
u256 a(3456789);
u256 b("0x282837623374623234aa74");
BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 9));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

Loading…
Cancel
Save