diff --git a/eth/main.cpp b/eth/main.cpp index b2e0633b7..b41c7a1ca 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -612,6 +612,7 @@ int main(int argc, char** argv) else if (arg == "--opencl-device" && i + 1 < argc) try { openclDevice = stol(argv[++i]); + miningThreads = 1; } catch (...) { diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index f9720fb8d..089fc7452 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -39,7 +39,9 @@ bool dev::SignatureStruct::isValid() const { if (v > 1 || r >= h256("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141") || - s >= h256("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f")) + s >= h256("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141") || + s < h256("0x01") || + r < h256("0x01")) return false; return true; } diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp index 891d3f97d..111f92529 100644 --- a/libethash-cl/ethash_cl_miner.cpp +++ b/libethash-cl/ethash_cl_miner.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -43,6 +44,8 @@ #undef min #undef max +using namespace std; + static void add_definition(std::string& source, char const* id, unsigned value) { char buf[256]; @@ -63,7 +66,7 @@ std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _devic cl::Platform::get(&platforms); if (platforms.empty()) { - debugf("No OpenCL platforms found.\n"); + cout << "No OpenCL platforms found." << endl; return std::string(); } @@ -73,7 +76,7 @@ std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _devic platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices); if (devices.empty()) { - debugf("No OpenCL devices found.\n"); + cout << "No OpenCL devices found." << endl; return std::string(); } @@ -91,7 +94,7 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId) cl::Platform::get(&platforms); if (platforms.empty()) { - debugf("No OpenCL platforms found.\n"); + cout << "No OpenCL platforms found." << endl; return 0; } @@ -100,7 +103,7 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId) platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices); if (devices.empty()) { - debugf("No OpenCL devices found.\n"); + cout << "No OpenCL devices found." << endl; return 0; } return devices.size(); @@ -124,7 +127,7 @@ bool ethash_cl_miner::init(ethash_params const& params, std::function(_platformId, platforms.size() - 1); - fprintf(stderr, "Using platform: %s\n", platforms[_platformId].getInfo().c_str()); + cout << "Using platform: " << platforms[_platformId].getInfo().c_str() << endl; // get GPU device of the default platform std::vector devices; platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices); if (devices.empty()) { - debugf("No OpenCL devices found.\n"); + cout << "No OpenCL devices found." << endl; return false; } - // use default device + // use selected device cl::Device& device = devices[std::min(_deviceId, devices.size() - 1)]; - for (unsigned n = 0; n < devices.size(); ++n) - { - auto version = devices[n].getInfo(); - auto name = devices[n].getInfo(); - fprintf(stderr, "%s %d: %s (%s)\n", n == _deviceId ? "USING " : " ", n, name.c_str(), version.c_str()); - } std::string device_version = device.getInfo(); - fprintf(stderr, "Using device: %s (%s)\n", device.getInfo().c_str(),device_version.c_str()); + cout << "Using device: " << device.getInfo().c_str() << "(" << device_version.c_str() << ")" << endl; if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0) { - debugf("OpenCL 1.0 is not supported.\n"); + cout << "OpenCL 1.0 is not supported." << endl; return false; } if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0) @@ -190,7 +187,7 @@ bool ethash_cl_miner::init(ethash_params const& params, std::function(device).c_str()); + cout << program.getBuildInfo(device).c_str(); return false; } m_hash_kernel = cl::Kernel(program, "ethash_hash"); diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index c40ce2625..66dc953b7 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -311,7 +311,7 @@ void Ethash::GPUMiner::workLoop() auto p = EthashAux::params(m_minerSeed); auto cb = [&](void* d) { EthashAux::full(m_minerSeed, bytesRef((byte*)d, p.full_size)); }; - unsigned device = instances() > 0 ? index() : s_deviceId; + unsigned device = instances() > 1 ? index() : s_deviceId; m_miner->init(p, cb, 32, s_platformId, device); } diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 78b83d064..59a7b61c2 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -450,14 +450,11 @@ void FunctionDefinition::checkTypeRequirements() { if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); + // todo delete when will be implemented arrays as parameter type in internal functions + if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array) + BOOST_THROW_EXCEPTION(var->createTypeError("Arrays only implemented for external functions.")); if (getVisibility() >= Visibility::Public && !(var->getType()->externalType())) - { - // todo delete when will be implemented arrays as parameter type in internal functions - if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array) - BOOST_THROW_EXCEPTION(var->createTypeError("Arrays only implemented for external functions.")); - else - BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions.")); - } + BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions.")); } for (ASTPointer const& modifier: m_functionModifiers) modifier->checkTypeRequirements(isConstructor() ? diff --git a/libsolidity/ASTJsonConverter.cpp b/libsolidity/ASTJsonConverter.cpp index c30e4ca2b..be89de924 100644 --- a/libsolidity/ASTJsonConverter.cpp +++ b/libsolidity/ASTJsonConverter.cpp @@ -78,10 +78,16 @@ ASTJsonConverter::ASTJsonConverter(ASTNode const& _ast): m_ast(&_ast) void ASTJsonConverter::print(ostream& _stream) { - m_ast->accept(*this); + process(); _stream << m_astJson; } +Json::Value const& ASTJsonConverter::json() +{ + process(); + return m_astJson; +} + bool ASTJsonConverter::visit(ImportDirective const& _node) { addJsonNode("Import", { make_pair("file", _node.getIdentifier())}); @@ -460,9 +466,16 @@ void ASTJsonConverter::endVisit(Literal const&) { } +void ASTJsonConverter::process() +{ + if (!processed) + m_ast->accept(*this); + processed = true; +} + string ASTJsonConverter::getType(Expression const& _expression) { - return (_expression.getType()) ? _expression.getType()->toString() : "Unknown"; + return (_expression.getType()) ? _expression.getType()->toString() : "Unknown"; } } diff --git a/libsolidity/ASTJsonConverter.h b/libsolidity/ASTJsonConverter.h index 30a92e66c..56502ab3c 100644 --- a/libsolidity/ASTJsonConverter.h +++ b/libsolidity/ASTJsonConverter.h @@ -44,6 +44,7 @@ public: ASTJsonConverter(ASTNode const& _ast); /// Output the json representation of the AST to _stream. void print(std::ostream& _stream); + Json::Value const& json(); bool visit(ImportDirective const& _node) override; bool visit(ContractDefinition const& _node) override; @@ -114,6 +115,7 @@ public: void endVisit(Literal const&) override; private: + void process(); void addKeyValue(Json::Value& _obj, std::string const& _key, std::string const& _val); void addJsonNode(std::string const& _nodeName, std::initializer_list> _list, @@ -123,8 +125,9 @@ private: { solAssert(!m_jsonNodePtrs.empty(), "Uneven json nodes stack. Internal error."); m_jsonNodePtrs.pop(); - }; + } + bool processed = false; Json::Value m_astJson; std::stack m_jsonNodePtrs; std::string m_source; diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index d476ec684..c3c3b9dcc 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -43,9 +43,9 @@ public: bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);} /// @arg _sourceCodes is the map of input files to source code strings /// @arg _inJsonFromat shows whether the out should be in Json format - void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const + Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const { - m_context.streamAssembly(_stream, _sourceCodes, _inJsonFormat); + return m_context.streamAssembly(_stream, _sourceCodes, _inJsonFormat); } /// @returns Assembly items of the normal compiler context eth::AssemblyItems const& getAssemblyItems() const { return m_context.getAssembly().getItems(); } diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index 34a3f97cd..0ca6369dd 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/CompilerContext.h @@ -123,9 +123,9 @@ public: eth::Assembly const& getAssembly() const { return m_asm; } /// @arg _sourceCodes is the map of input files to source code strings /// @arg _inJsonFormat shows whether the out should be in Json format - void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const + Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const { - m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat); + return m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat); } bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); } diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index d6274e2c7..554d06fd7 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -184,13 +184,16 @@ dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const return dev::sha3(getRuntimeBytecode(_contractName)); } -void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const +Json::Value CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const { Contract const& contract = getContract(_contractName); if (contract.compiler) - contract.compiler->streamAssembly(_outStream, _sourceCodes, _inJsonFormat); + return contract.compiler->streamAssembly(_outStream, _sourceCodes, _inJsonFormat); else + { _outStream << "Contract not fully implemented" << endl; + return Json::Value(); + } } string const& CompilerStack::getInterface(string const& _contractName) const @@ -264,7 +267,7 @@ void CompilerStack::reset(bool _keepSources) { m_sources.clear(); if (m_addStandardSources) - addSources(StandardSources); + addSources(StandardSources, true); } m_globalContext.reset(); m_sourceOrder.clear(); diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 2e7c217d5..7d9198622 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -104,7 +105,7 @@ public: /// @arg _sourceCodes is the map of input files to source code strings /// @arg _inJsonFromat shows whether the out should be in Json format /// Prerequisite: Successful compilation. - void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap(), bool _inJsonFormat = false) const; + Json::Value streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap(), bool _inJsonFormat = false) const; /// Returns a string representing the contract interface in JSON. /// Prerequisite: Successful call to parse or compile. @@ -140,7 +141,7 @@ private: std::shared_ptr ast; std::string interface; bool isLibrary = false; - void reset() { scanner.reset(); ast.reset(); interface.clear(); isLibrary = false;} + void reset() { scanner.reset(); ast.reset(); interface.clear(); } }; struct Contract diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp index 8d3e9d2a2..07bc3cdab 100644 --- a/libsolidity/CompilerUtils.cpp +++ b/libsolidity/CompilerUtils.cpp @@ -155,6 +155,13 @@ void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize) m_context << eth::dupInstruction(_stackDepth); } +void CompilerUtils::moveToStackTop(unsigned _stackDepth) +{ + solAssert(_stackDepth <= 15, "Stack too deep."); + for (unsigned i = 0; i < _stackDepth; ++i) + m_context << eth::swapInstruction(1 + i); +} + void CompilerUtils::popStackElement(Type const& _type) { popStackSlots(_type.getSizeOnStack()); diff --git a/libsolidity/CompilerUtils.h b/libsolidity/CompilerUtils.h index 5b809beac..45f53e12e 100644 --- a/libsolidity/CompilerUtils.h +++ b/libsolidity/CompilerUtils.h @@ -77,6 +77,8 @@ public: /// Copies an item that occupies @a _itemSize stack slots from a stack depth of @a _stackDepth /// to the top of the stack. void copyToStackTop(unsigned _stackDepth, unsigned _itemSize); + /// Moves a single stack element (with _stackDepth items on top of it) to the top of the stack. + void moveToStackTop(unsigned _stackDepth); /// Removes the current value from the top of the stack. void popStackElement(Type const& _type); /// Removes element from the top of the stack _amount times. diff --git a/libsolidity/DeclarationContainer.cpp b/libsolidity/DeclarationContainer.cpp index 5f8d24e34..2130f3a01 100644 --- a/libsolidity/DeclarationContainer.cpp +++ b/libsolidity/DeclarationContainer.cpp @@ -37,19 +37,25 @@ bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, if (_update) { solAssert(!dynamic_cast(&_declaration), "Attempt to update function definition."); - m_declarations[name].clear(); - m_invisibleDeclarations[name].clear(); + m_declarations.erase(name); + m_invisibleDeclarations.erase(name); } else { + vector declarations; + if (m_declarations.count(name)) + declarations += m_declarations.at(name); + if (m_invisibleDeclarations.count(name)) + declarations += m_invisibleDeclarations.at(name); if (dynamic_cast(&_declaration)) { // check that all other declarations with the same name are functions - for (auto&& declaration: m_invisibleDeclarations[name] + m_declarations[name]) + + for (Declaration const* declaration: declarations) if (!dynamic_cast(declaration)) return false; } - else if (m_declarations.count(name) > 0 || m_invisibleDeclarations.count(name) > 0) + else if (!declarations.empty()) return false; } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index cf6a01ec1..8c07fbd19 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -531,9 +531,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) break; case Location::SHA3: { - m_context << u256(0); - appendArgumentsCopyToMemory(arguments, TypePointers(), function.padArguments()); - m_context << u256(0) << eth::Instruction::SHA3; + // we might compute a sha as part of argumentsAppendCopyToMemory, this is only a hack + // and should be removed once we have a real free memory pointer + m_context << u256(0x40); + appendArgumentsCopyToMemory(arguments, TypePointers(), function.padArguments(), false, true); + m_context << u256(0x40) << eth::Instruction::SWAP1 << eth::Instruction::SUB; + m_context << u256(0x40) << eth::Instruction::SHA3; break; } case Location::Log0: @@ -574,11 +577,19 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } solAssert(numIndexed <= 4, "Too many indexed arguments."); // Copy all non-indexed arguments to memory (data) - m_context << u256(0); + // Memory position is only a hack and should be removed once we have free memory pointer. + m_context << u256(0x40); + vector> nonIndexedArgs; + TypePointers nonIndexedTypes; for (unsigned arg = 0; arg < arguments.size(); ++arg) if (!event.getParameters()[arg]->isIndexed()) - appendExpressionCopyToMemory(*function.getParameterTypes()[arg], *arguments[arg]); - m_context << u256(0) << eth::logInstruction(numIndexed); + { + nonIndexedArgs.push_back(arguments[arg]); + nonIndexedTypes.push_back(function.getParameterTypes()[arg]); + } + appendArgumentsCopyToMemory(nonIndexedArgs, nonIndexedTypes); + m_context << u256(0x40) << eth::Instruction::SWAP1 << eth::Instruction::SUB; + m_context << u256(0x40) << eth::logInstruction(numIndexed); break; } case Location::BlockHash: @@ -1046,8 +1057,14 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio // For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes, // do not pad it to 32 bytes. - appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(), - _functionType.padArguments(), bare); + // If the function takes arbitrary parameters, copy dynamic length data in place. + appendArgumentsCopyToMemory( + _arguments, + _functionType.getParameterTypes(), + _functionType.padArguments(), + bare, + _functionType.takesArbitraryParameters() + ); // CALL arguments: outSize, outOff, inSize, (already present up to here) // inOff, value, addr, gas (stack top) @@ -1089,20 +1106,72 @@ void ExpressionCompiler::appendArgumentsCopyToMemory( vector> const& _arguments, TypePointers const& _types, bool _padToWordBoundaries, - bool _padExceptionIfFourBytes + bool _padExceptionIfFourBytes, + bool _copyDynamicDataInPlace ) { solAssert(_types.empty() || _types.size() == _arguments.size(), ""); + TypePointers types = _types; + if (_types.empty()) + for (ASTPointer const& argument: _arguments) + types.push_back(argument->getType()->getRealType()); + + vector dynamicArguments; + unsigned stackSizeOfDynamicTypes = 0; for (size_t i = 0; i < _arguments.size(); ++i) { _arguments[i]->accept(*this); - TypePointer const& expectedType = _types.empty() ? _arguments[i]->getType()->getRealType() : _types[i]; - appendTypeConversion(*_arguments[i]->getType(), *expectedType, true); + TypePointer argType = types[i]->externalType(); + solAssert(!!argType, "Externalable type expected."); + if (argType->isValueType()) + appendTypeConversion(*_arguments[i]->getType(), *argType, true); + else + argType = _arguments[i]->getType()->getRealType()->externalType(); + solAssert(!!argType, "Externalable type expected."); bool pad = _padToWordBoundaries; // Do not pad if the first argument has exactly four bytes - if (i == 0 && pad && _padExceptionIfFourBytes && expectedType->getCalldataEncodedSize(false) == 4) + if (i == 0 && pad && _padExceptionIfFourBytes && argType->getCalldataEncodedSize(false) == 4) pad = false; - appendTypeMoveToMemory(*expectedType, pad); + if (!_copyDynamicDataInPlace && argType->isDynamicallySized()) + { + solAssert(argType->getCategory() == Type::Category::Array, "Unknown dynamic type."); + auto const& arrayType = dynamic_cast(*_arguments[i]->getType()); + // move memory reference to top of stack + CompilerUtils(m_context).moveToStackTop(arrayType.getSizeOnStack()); + if (arrayType.getLocation() == ArrayType::Location::CallData) + m_context << eth::Instruction::DUP2; // length is on stack + else if (arrayType.getLocation() == ArrayType::Location::Storage) + m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD; + else + { + solAssert(arrayType.getLocation() == ArrayType::Location::Memory, ""); + m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD; + } + appendTypeMoveToMemory(IntegerType(256), true); + stackSizeOfDynamicTypes += arrayType.getSizeOnStack(); + dynamicArguments.push_back(i); + } + else + appendTypeMoveToMemory(*argType, pad); + } + + // copy dynamic values to memory + unsigned dynStackPointer = stackSizeOfDynamicTypes; + // stack layout: ... + for (size_t i: dynamicArguments) + { + auto const& arrayType = dynamic_cast(*_arguments[i]->getType()); + CompilerUtils(m_context).copyToStackTop(1 + dynStackPointer, arrayType.getSizeOnStack()); + dynStackPointer -= arrayType.getSizeOnStack(); + appendTypeMoveToMemory(arrayType, true); + } + solAssert(dynStackPointer == 0, ""); + + // remove dynamic values (and retain memory pointer) + if (stackSizeOfDynamicTypes > 0) + { + m_context << eth::swapInstruction(stackSizeOfDynamicTypes); + CompilerUtils(m_context).popStackSlots(stackSizeOfDynamicTypes); } } @@ -1114,8 +1183,13 @@ void ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, bool _padToWo void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression) { _expression.accept(*this); - appendTypeConversion(*_expression.getType(), _expectedType, true); - appendTypeMoveToMemory(_expectedType); + if (_expectedType.isValueType()) + { + appendTypeConversion(*_expression.getType(), _expectedType, true); + appendTypeMoveToMemory(_expectedType); + } + else + appendTypeMoveToMemory(*_expression.getType()->getRealType()); } void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression) diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 2577d21b5..35526662a 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -100,12 +100,18 @@ private: /// Appends code to call a function of the given type with the given arguments. void appendExternalFunctionCall(FunctionType const& _functionType, std::vector> const& _arguments, bool bare = false); - /// Appends code that evaluates the given arguments and moves the result to memory. The memory offset is - /// expected to be on the stack and is updated by this call. - void appendArgumentsCopyToMemory(std::vector> const& _arguments, - TypePointers const& _types = {}, - bool _padToWordBoundaries = true, - bool _padExceptionIfFourBytes = false); + /// Appends code that evaluates the given arguments and moves the result to memory encoded as + /// specified by the ABI. The memory offset is expected to be on the stack and is updated by + /// this call. If @a _padToWordBoundaries is set to false, all values are concatenated without + /// padding. If @a _copyDynamicDataInPlace is set, dynamic types is stored (without length) + /// together with fixed-length data. + void appendArgumentsCopyToMemory( + std::vector> const& _arguments, + TypePointers const& _types = {}, + bool _padToWordBoundaries = true, + bool _padExceptionIfFourBytes = false, + bool _copyDynamicDataInPlace = false + ); /// Appends code that moves a stack element of the given type to memory. The memory offset is /// expected below the stack element and is updated by this call. void appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries = true); diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index a445d56e1..19bc134e1 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -745,8 +745,6 @@ string ArrayType::toString() const TypePointer ArrayType::externalType() const { - if (m_location != Location::CallData) - return TypePointer(); if (m_isByteArray) return shared_from_this(); if (!m_baseType->externalType()) @@ -1218,7 +1216,9 @@ string FunctionType::externalSignature(std::string const& _name) const } string ret = funcName + "("; - TypePointers externalParameterTypes = externalFunctionType()->getParameterTypes(); + FunctionTypePointer external = externalFunctionType(); + solAssert(!!external, "External function type requested."); + TypePointers externalParameterTypes = external->getParameterTypes(); for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it) { solAssert(!!(*it), "Parameter should have external type"); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index ab41d4d4d..cc9c455f2 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -430,7 +430,7 @@ public: virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; - virtual unsigned getCalldataEncodedSize(bool _padded = true) const override + virtual unsigned getCalldataEncodedSize(bool _padded ) const override { return externalType()->getCalldataEncodedSize(_padded); } @@ -506,7 +506,7 @@ public: explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; - virtual unsigned getCalldataEncodedSize(bool _padded = true) const override + virtual unsigned getCalldataEncodedSize(bool _padded) const override { return externalType()->getCalldataEncodedSize(_padded); } @@ -558,7 +558,7 @@ public: /// appropriate external types of input/return parameters of current function. /// Returns an empty shared pointer if one of the input/return parameters does not have an /// external type. - virtual FunctionTypePointer externalFunctionType() const; + FunctionTypePointer externalFunctionType() const; virtual TypePointer externalType() const override { return externalFunctionType(); } explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index dad1ced62..c0ce36ab5 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -237,6 +237,7 @@ void ClientModel::executeSequence(vector const& _sequence, { try { + vector
deployedContracts; onStateReset(); for (TransactionSettings const& transaction: _sequence) { @@ -248,6 +249,7 @@ void ClientModel::executeSequence(vector const& _sequence, TransactionSettings stdTransaction = transaction; stdTransaction.gasAuto = true; Address address = deployContract(stdContractCode, stdTransaction); + deployedContracts.push_back(address); m_stdContractAddresses[stdTransaction.contractId] = address; m_stdContractNames[address] = stdTransaction.contractId; } @@ -280,6 +282,11 @@ void ClientModel::executeSequence(vector const& _sequence, { QSolidityType const* type = p->type(); QVariant value = transaction.parameterValues.value(p->name()); + if (type->type().type == SolidityType::Type::Address && value.toString().startsWith("<") && value.toString().endsWith(">")) + { + QStringList nb = value.toString().remove("<").remove(">").split(" - "); + value = QVariant(QString::fromStdString("0x" + toHex(deployedContracts.at(nb.back().toInt()).ref()))); + } encoder.encode(value, type->type()); } @@ -288,6 +295,7 @@ void ClientModel::executeSequence(vector const& _sequence, bytes param = encoder.encodedData(); contractCode.insert(contractCode.end(), param.begin(), param.end()); Address newAddress = deployContract(contractCode, transaction); + deployedContracts.push_back(newAddress); auto contractAddressIter = m_contractAddresses.find(transaction.contractId); if (contractAddressIter == m_contractAddresses.end() || newAddress != contractAddressIter->second) { diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index 87a27cf79..c6283b60e 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -211,8 +211,8 @@ Rectangle { anchors.top: parent.top anchors.topMargin: 15 anchors.left: parent.left; - anchors.leftMargin: machineStates.sideMargin - width: debugScrollArea.width - machineStates.sideMargin * 2 - 20 ; + anchors.leftMargin: machineStates.sideMargin + width: debugScrollArea.width - machineStates.sideMargin * 2 - 20 spacing: machineStates.sideMargin Rectangle { @@ -641,9 +641,6 @@ Rectangle { } } - - - Rectangle { id: storageRect diff --git a/mix/qml/QAddressView.qml b/mix/qml/QAddressView.qml new file mode 100644 index 000000000..2c1d43276 --- /dev/null +++ b/mix/qml/QAddressView.qml @@ -0,0 +1,96 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.3 +import QtQuick.Controls.Styles 1.3 + +Item +{ + property alias value: textinput.text + property alias contractCreationTr: ctrModel + id: editRoot + height: 20 + width: 200 + + SourceSansProBold + { + id: boldFont + } + + function init() + { + trCombobox.visible = ctrModel.count > 1; //index 0 is a blank value. + if (value.indexOf("<") === 0) + { + for (var k = 0; k < ctrModel.count; k++) + { + if ("<" + ctrModel.get(k).functionId + ">" === value) + { + trCombobox.currentIndex = k; + return; + } + } + trCombobox.currentIndex = 0; + value = ""; + } + } + + Rectangle { + anchors.fill: parent + radius: 4 + anchors.verticalCenter: parent.verticalCenter + height: 20 + TextInput { + id: textinput + text: value + width: parent.width + height: parent.width + wrapMode: Text.WrapAnywhere + clip: true + font.family: boldFont.name + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: textinput.forceActiveFocus() + } + onTextChanged: + { + if (trCombobox.selected) + { + trCombobox.currentIndex = 0; + trCombobox.selected = false; + } + } + } + } + + ListModel + { + id: ctrModel + } + + ComboBox + { + property bool selected: false + id: trCombobox + model: ctrModel + textRole: "functionId" + height: 20 + anchors.verticalCenter: parent.verticalCenter + anchors.left: textinput.parent.right + anchors.leftMargin: 3 + onCurrentIndexChanged: { + trCombobox.selected = false; + if (currentText === "") + return; + else if (currentText !== " - ") + { + textinput.text = "<" + currentText + ">"; + trCombobox.selected = true; + } + else if (textinput.text.indexOf("<") === 0) + { + textinput.text = ""; + } + } + } +} diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index a2f9a09b7..b85996b29 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -366,7 +366,12 @@ Dialog { DefaultLabel { Layout.preferredWidth: 150 - text: styleData.row >= 0 ? transactionsModel.get(styleData.row).functionId : "" + text: { + if (styleData.row >= 0) + return transactionsModel.get(styleData.row).functionId; + else + return ""; + } } } } @@ -378,7 +383,6 @@ Dialog { } } } - } RowLayout @@ -456,7 +460,6 @@ Dialog { onAccepted: { var item = transactionDialog.getItem(); - if (transactionDialog.transactionIndex < transactionsModel.count) { transactionsModel.set(transactionDialog.transactionIndex, item); stateTransactions[transactionDialog.transactionIndex] = item; diff --git a/mix/qml/StructView.qml b/mix/qml/StructView.qml index 2a0c5c68a..32ce3d618 100644 --- a/mix/qml/StructView.qml +++ b/mix/qml/StructView.qml @@ -8,17 +8,19 @@ Column id: root property alias members: repeater.model //js array property var value: ({}) + property int transactionIndex Layout.fillWidth: true - + spacing: 10 Repeater { id: repeater visible: model.length > 0 Layout.fillWidth: true + RowLayout { id: row - height: 20 + (members[index].type.category === QSolidityType.Struct ? (20 * members[index].type.members.length) : 0) + height: 30 + (members[index].type.category === QSolidityType.Struct ? (20 * members[index].type.members.length) : 0) Layout.fillWidth: true DefaultLabel { height: 20 @@ -51,10 +53,12 @@ Column return Qt.createComponent("qrc:/qml/QBoolTypeView.qml"); else if (t === QSolidityType.Bytes) return Qt.createComponent("qrc:/qml/QStringTypeView.qml"); - else if (t === QSolidityType.Hash || t === QSolidityType.Address) + else if (t === QSolidityType.Hash) return Qt.createComponent("qrc:/qml/QHashTypeView.qml"); else if (t === QSolidityType.Struct) return Qt.createComponent("qrc:/qml/StructView.qml"); + else if (t === QSolidityType.Address) + return Qt.createComponent("qrc:/qml/QAddressView.qml"); else return undefined; } @@ -63,7 +67,26 @@ Column var ptype = members[index].type; var pname = members[index].name; var vals = value; - if (ptype.category === QSolidityType.Struct && !item.members) + if (ptype.category === QSolidityType.Address) + { + item.contractCreationTr.append({"functionId": " - "}); + var trCr = -1; + for (var k = 0; k < transactionsModel.count; k++) + { + if (k >= transactionIndex) + break; + var tr = transactionsModel.get(k); + if (tr.functionId === tr.contractId) + { + trCr++; + if (modelData.type.name === qsTr("contract") + " " + tr.contractId) + item.contractCreationTr.append({ "functionId": tr.contractId + " - " + trCr }); + } + } + item.value = getValue(); + item.init(); + } + else if (ptype.category === QSolidityType.Struct && !item.members) { item.value = getValue(); item.members = ptype.members; diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index ed76d48eb..0668bfca2 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -39,6 +39,8 @@ Dialog { rowGasPrice.visible = !useTransactionDefaultValue; transactionIndex = index; + typeLoader.transactionIndex = index; + gasValueEdit.gasValue = item.gas; gasAutoCheck.checked = item.gasAuto ? true : false; gasPriceField.value = item.gasPrice; @@ -99,6 +101,18 @@ Dialog { } + function selectContract(contractName) + { + for (var k = 0; k < contractsModel.count; k++) + { + if (contractsModel.get(k).cid === contractName) + { + contractComboBox.currentIndex = k; + break; + } + } + } + function selectFunction(functionId) { var functionIndex = -1; diff --git a/mix/res.qrc b/mix/res.qrc index f175fab1f..ac0af57ac 100644 --- a/mix/res.qrc +++ b/mix/res.qrc @@ -67,5 +67,6 @@ qml/img/stop_button2x.png qml/img/warningicon.png qml/img/warningicon@2x.png + qml/QAddressView.qml diff --git a/mix/test/qml/TestMain.qml b/mix/test/qml/TestMain.qml index 778e4dc20..c0d179707 100644 --- a/mix/test/qml/TestMain.qml +++ b/mix/test/qml/TestMain.qml @@ -96,6 +96,7 @@ TestCase function test_dbg_arrayParametersAndStorage() { TestDebugger.test_arrayParametersAndStorage(); } function test_dbg_solidity() { TestDebugger.test_solidityDebugging(); } function test_dbg_vm() { TestDebugger.test_vmDebugging(); } + function test_dbg_ctrTypeAsParam() { TestDebugger.test_ctrTypeAsParam(); } function test_miner_getDefaultiner() { TestMiner.test_getDefaultMiner(); } function test_miner_selectMiner() { TestMiner.test_selectMiner(); } function test_miner_mine() { TestMiner.test_mine(); } diff --git a/mix/test/qml/js/TestDebugger.js b/mix/test/qml/js/TestDebugger.js index f8453df78..2300dd390 100644 --- a/mix/test/qml/js/TestDebugger.js +++ b/mix/test/qml/js/TestDebugger.js @@ -203,3 +203,47 @@ function test_vmDebugging() tryCompare(mainApplication.mainContent.rightPane.vmMemory.listModel, "length", 0); } +function test_ctrTypeAsParam() +{ + newProject(); + editContract( + "contract C1 {\r " + + " function get() returns (uint256)\r " + + " {\r " + + " return 159;\r " + + " }\r " + + "}\r" + + "contract C2 {\r " + + " C1 c1;\r " + + " function getFromC1() returns (uint256)\r " + + " {\r " + + " return c1.get();\r " + + " }\r " + + " function C2(C1 _c1)\r" + + " {\r " + + " c1 = _c1;\r" + + " }\r " + + "}"); + mainApplication.projectModel.stateListModel.editState(0); //C1 ctor already added + mainApplication.projectModel.stateDialog.model.addTransaction(); + var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog; + ts.waitForRendering(transactionDialog, 3000); + transactionDialog.selectContract("C2"); + transactionDialog.selectFunction("C2"); + transactionDialog.acceptAndClose(); + mainApplication.projectModel.stateDialog.model.addTransaction(); + transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog; + ts.waitForRendering(transactionDialog, 3000); + transactionDialog.selectContract("C2"); + transactionDialog.selectFunction("getFromC1"); + clickElement(transactionDialog, 406, 340); + clickElement(transactionDialog, 406, 366); + transactionDialog.acceptAndClose(); + mainApplication.projectModel.stateDialog.acceptAndClose(); + mainApplication.mainContent.startQuickDebugging(); + if (!ts.waitForSignal(mainApplication.clientModel, "debugDataReady(QObject*)", 5000)) + fail("Error running transaction"); + + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(4), "returned", "(159)"); +} + diff --git a/mix/test/qml/js/TestTutorial.js b/mix/test/qml/js/TestTutorial.js index 895b5c9c1..7f366f23b 100644 --- a/mix/test/qml/js/TestTutorial.js +++ b/mix/test/qml/js/TestTutorial.js @@ -52,7 +52,7 @@ function test_tutorial() transactionDialog.selectFunction("setRating"); clickElement(transactionDialog, 200, 310); ts.typeString("Titanic", transactionDialog); - clickElement(transactionDialog, 200, 330); + clickElement(transactionDialog, 200, 350); ts.typeString("2", transactionDialog); transactionDialog.acceptAndClose(); mainApplication.projectModel.stateDialog.acceptAndClose(); diff --git a/solc/CMakeLists.txt b/solc/CMakeLists.txt index d3a39dbc8..177823963 100644 --- a/solc/CMakeLists.txt +++ b/solc/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_policy(SET CMP0015 NEW) set(CMAKE_AUTOMOC OFF) aux_source_directory(. SRC_LIST) +list(REMOVE_ITEM SRC_LIST "./jsonCompiler.cpp") include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(BEFORE ..) @@ -18,3 +19,9 @@ target_link_libraries(${EXECUTABLE} solidity) install( TARGETS ${EXECUTABLE} DESTINATION bin ) +if (ETH_STATIC) + add_library(soljson STATIC jsonCompiler.cpp ${HEADERS}) +else() + add_library(soljson SHARED jsonCompiler.cpp ${HEADERS}) +endif() +target_link_libraries(soljson solidity) diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp new file mode 100644 index 000000000..3079df69f --- /dev/null +++ b/solc/jsonCompiler.cpp @@ -0,0 +1,124 @@ +/* + 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 . +*/ +/** + * @author Christian + * @date 2014 + * JSON interface for the solidity compiler to be used from Javascript. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace solidity; + +string formatError(Exception const& _exception, string const& _name, CompilerStack const& _compiler) +{ + ostringstream errorOutput; + SourceReferenceFormatter::printExceptionInformation(errorOutput, _exception, _name, _compiler); + + Json::Value output(Json::objectValue); + output["error"] = errorOutput.str(); + return Json::FastWriter().write(output); +} + +string compile(string _input, bool _optimize) +{ + StringMap sources; + sources[""] = _input; + + Json::Value output(Json::objectValue); + CompilerStack compiler; + try + { + compiler.compile(_input, _optimize); + } + catch (ParserError const& exception) + { + return formatError(exception, "Parser error", compiler); + } + catch (DeclarationError const& exception) + { + return formatError(exception, "Declaration error", compiler); + } + catch (TypeError const& exception) + { + return formatError(exception, "Type error", compiler); + } + catch (CompilerError const& exception) + { + return formatError(exception, "Compiler error", compiler); + } + catch (InternalCompilerError const& exception) + { + return formatError(exception, "Internal compiler error", compiler); + } + catch (Exception const& exception) + { + output["error"] = "Exception during compilation: " + boost::diagnostic_information(exception); + return Json::FastWriter().write(output); + } + catch (...) + { + output["error"] = "Unknown exception during compilation."; + return Json::FastWriter().write(output); + } + + output["contracts"] = Json::Value(Json::objectValue); + for (string const& contractName: compiler.getContractNames()) + { + Json::Value contractData(Json::objectValue); + contractData["solidity_interface"] = compiler.getSolidityInterface(contractName); + contractData["interface"] = compiler.getInterface(contractName); + contractData["bytecode"] = toHex(compiler.getBytecode(contractName)); + contractData["opcodes"] = eth::disassemble(compiler.getBytecode(contractName)); + ostringstream unused; + contractData["assembly"] = compiler.streamAssembly(unused, contractName, sources); + output["contracts"][contractName] = contractData; + } + + output["sources"] = Json::Value(Json::objectValue); + output["sources"][""] = Json::Value(Json::objectValue); + output["sources"][""]["AST"] = ASTJsonConverter(compiler.getAST("")).json(); + + return Json::FastWriter().write(output); +} + +static string outputBuffer; + +extern "C" +{ +extern char const* compileJSON(char const* _input, bool _optimize) +{ + outputBuffer = compile(_input, _optimize); + return outputBuffer.c_str(); +} +} diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 8b926d6cf..596f710b9 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2340,6 +2340,49 @@ BOOST_AUTO_TEST_CASE(event_lots_of_data) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(address,bytes32,uint256,bool)"))); } +BOOST_AUTO_TEST_CASE(event_really_lots_of_data) +{ + char const* sourceCode = R"( + contract ClientReceipt { + event Deposit(uint fixeda, bytes dynx, uint fixedb); + function deposit() { + Deposit(10, msg.data, 15); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("deposit()"); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(10, 4, 15) + FixedHash<4>(dev::sha3("deposit()")).asBytes()); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)"))); +} + +BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage) +{ + char const* sourceCode = R"( + contract ClientReceipt { + bytes x; + event Deposit(uint fixeda, bytes dynx, uint fixedb); + function deposit() { + x.length = 3; + x[0] = "A"; + x[1] = "B"; + x[2] = "C"; + Deposit(10, x, 15); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("deposit()"); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(10, 3, 15) + asBytes("ABC")); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)"))); +} + BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) { char const* sourceCode = R"( @@ -2428,6 +2471,24 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_string_literals) bytes{0x66, 0x6f, 0x6f}))); } +BOOST_AUTO_TEST_CASE(sha3_with_bytes) +{ + char const* sourceCode = R"( + contract c { + bytes data; + function foo() returns (bool) + { + data.length = 3; + data[0] = "f"; + data[1] = "o"; + data[2] = "o"; + return sha3(data) == sha3("foo"); + } + })"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("foo()") == encodeArgs(true)); +} + BOOST_AUTO_TEST_CASE(generic_call) { char const* sourceCode = R"**(