From 7ce4bd6707985ec3f49de59ff6b9996b7cf9cd24 Mon Sep 17 00:00:00 2001 From: IgorKoval Date: Mon, 4 May 2020 19:02:43 +0300 Subject: [PATCH] latest fixes --- README.md | Bin 2878 -> 2956 bytes compiler/libsolidity/analysis/TypeChecker.cpp | 30 +- compiler/libsolidity/analysis/TypeChecker.h | 1 + compiler/libsolidity/ast/AST.h | 25 ++ compiler/libsolidity/ast/ASTVisitor.h | 4 + compiler/libsolidity/ast/AST_accept.h | 18 ++ compiler/libsolidity/ast/Types.cpp | 86 ++---- compiler/libsolidity/ast/Types.h | 2 +- compiler/libsolidity/codegen/TVM.cpp | 20 +- compiler/libsolidity/codegen/TVMABI.cpp | 284 +++++++++++++++++- compiler/libsolidity/codegen/TVMABI.hpp | 69 ++++- compiler/libsolidity/codegen/TVMAnalyzer.cpp | 18 +- compiler/libsolidity/codegen/TVMCommons.cpp | 17 +- compiler/libsolidity/codegen/TVMCommons.hpp | 5 + compiler/libsolidity/codegen/TVMConstants.hpp | 5 +- .../codegen/TVMContractCompiler.cpp | 202 ++++++++----- .../codegen/TVMContractCompiler.hpp | 6 + .../codegen/TVMExpressionCompiler.cpp | 205 +++++-------- .../codegen/TVMExpressionCompiler.hpp | 12 - .../libsolidity/codegen/TVMFunctionCall.cpp | 212 ++++++------- .../libsolidity/codegen/TVMFunctionCall.hpp | 2 +- .../codegen/TVMFunctionCompiler.cpp | 159 +++++----- .../codegen/TVMFunctionCompiler.hpp | 6 +- .../libsolidity/codegen/TVMIntrinsics.cpp | 38 ++- .../libsolidity/codegen/TVMOptimizations.cpp | 134 ++++++++- compiler/libsolidity/codegen/TVMPusher.cpp | 260 +++------------- compiler/libsolidity/codegen/TVMPusher.hpp | 56 +--- .../libsolidity/codegen/TVMTypeChecker.cpp | 20 +- compiler/libsolidity/parsing/Parser.cpp | 5 + compiler/solc/CommandLineInterface.cpp | 22 +- lib/stdlib_sol.tvm | 32 +- 31 files changed, 1107 insertions(+), 848 deletions(-) diff --git a/README.md b/README.md index 6f2f044ea94dd24453ed4b065eee7b7b54ab3a90..b93c319db8340ded29c4eb5117b715dd69676186 100644 GIT binary patch delta 165 zcmdld)+4^bjb$t0R)mVcVN*VGd&t{d^ zcL7S20dWpcX#q$Pg93vySX(YbB10ZS3XmTF6onX6GMSfcBFJ#n7<9uKxPT%cJ=s80 b6c}`Yx)dg_XVd0{i%y=%a&Yq__84XWHisImplicitlyConvertibleTo(*var.annotation().type); - - if (auto map1 = dynamic_cast(valueComponentType)) - if (auto map2 = dynamic_cast(var.annotation().type)) - result = map1->isImplicitlyConvertibleTo(map2); - if (!result) { auto errorMsg = "Type " + @@ -2031,7 +2026,14 @@ TypeChecker::isArgumentAPublicFunction(FunctionCall const& _functionCall) { m_errorReporter.fatalTypeError(expr->location(), errorText); } auto identifier = dynamic_cast(expr); - Declaration const* declaration = identifier->annotation().referencedDeclaration; + Declaration const* declaration = nullptr; + if (identifier) + declaration = identifier->annotation().referencedDeclaration; + else if (auto member = dynamic_cast(expr)) { + declaration = member->annotation().referencedDeclaration; + } + if (!declaration) + m_errorReporter.fatalTypeError(expr->location(), errorText); if (!declaration->isPublic()) { m_errorReporter.fatalTypeError(expr->location(), errorText); } @@ -3066,6 +3068,16 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr) _expr.annotation().isPure = true; } +void TypeChecker::endVisit(MappingNameExpression const& _expr) +{ + _expr.annotation().type = TypeProvider::typeType(TypeProvider::mapping( + _expr.type().keyType().annotation().type, + _expr.type().valueType().annotation().type, + DataLocation::Memory + )); + _expr.annotation().isPure = true; +} + void TypeChecker::endVisit(Literal const& _literal) { if (_literal.looksLikeAddress()) @@ -3174,14 +3186,8 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte { _expression.accept(*this); - BoolResult result = type(_expression)->isImplicitlyConvertibleTo(_expectedType); - if (auto map1 = dynamic_cast(type(_expression))) - if (auto map2 = dynamic_cast(&_expectedType)) - result = map1->isImplicitlyConvertibleTo(map2); - - if (!result) { auto errorMsg = "Type " + diff --git a/compiler/libsolidity/analysis/TypeChecker.h b/compiler/libsolidity/analysis/TypeChecker.h index 8e59e95b..dd9176cd 100644 --- a/compiler/libsolidity/analysis/TypeChecker.h +++ b/compiler/libsolidity/analysis/TypeChecker.h @@ -149,6 +149,7 @@ class TypeChecker: private ASTConstVisitor bool visit(IndexRangeAccess const& _indexRangeAccess) override; bool visit(Identifier const& _identifier) override; void endVisit(ElementaryTypeNameExpression const& _expr) override; + void endVisit(MappingNameExpression const& _expr) override; void endVisit(Literal const& _literal) override; bool visit(Mapping const& _mapping) override; diff --git a/compiler/libsolidity/ast/AST.h b/compiler/libsolidity/ast/AST.h index c209e3ca..7fcb5362 100644 --- a/compiler/libsolidity/ast/AST.h +++ b/compiler/libsolidity/ast/AST.h @@ -2015,6 +2015,31 @@ class ElementaryTypeNameExpression: public PrimaryExpression ASTPointer m_type; }; +/** + * mapping(uint=>address) + * for example slice.decode(mapping(uint=>address)) + */ +class MappingNameExpression : public PrimaryExpression +{ +public: + MappingNameExpression( + int64_t _id, + SourceLocation const& _location, + ASTPointer const& _type + ): + PrimaryExpression(_id, _location), + m_type(_type) + { + } + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + Mapping const& type() const { return *m_type; } + +private: + ASTPointer m_type; +}; + /** * A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value. */ diff --git a/compiler/libsolidity/ast/ASTVisitor.h b/compiler/libsolidity/ast/ASTVisitor.h index 90dad003..484883a2 100644 --- a/compiler/libsolidity/ast/ASTVisitor.h +++ b/compiler/libsolidity/ast/ASTVisitor.h @@ -93,6 +93,7 @@ class ASTVisitor virtual bool visit(ElementaryTypeNameExpression& _node) { return visitNode(_node); } virtual bool visit(Literal& _node) { return visitNode(_node); } virtual bool visit(StructuredDocumentation& _node) { return visitNode(_node); } + virtual bool visit(MappingNameExpression& _node) { return visitNode(_node); } virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); } virtual void endVisit(PragmaDirective& _node) { endVisitNode(_node); } @@ -145,6 +146,7 @@ class ASTVisitor virtual void endVisit(ElementaryTypeNameExpression& _node) { endVisitNode(_node); } virtual void endVisit(Literal& _node) { endVisitNode(_node); } virtual void endVisit(StructuredDocumentation& _node) { endVisitNode(_node); } + virtual void endVisit(MappingNameExpression& _node) { endVisitNode(_node); } protected: /// Generic function called by default for each node, to be overridden by derived classes @@ -210,6 +212,7 @@ class ASTConstVisitor virtual bool visit(ElementaryTypeNameExpression const& _node) { return visitNode(_node); } virtual bool visit(Literal const& _node) { return visitNode(_node); } virtual bool visit(StructuredDocumentation const& _node) { return visitNode(_node); } + virtual bool visit(MappingNameExpression const& _node) { return visitNode(_node); } virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); } virtual void endVisit(PragmaDirective const& _node) { endVisitNode(_node); } @@ -262,6 +265,7 @@ class ASTConstVisitor virtual void endVisit(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); } virtual void endVisit(Literal const& _node) { endVisitNode(_node); } virtual void endVisit(StructuredDocumentation const& _node) { endVisitNode(_node); } + virtual void endVisit(MappingNameExpression const& _node) { endVisitNode(_node); } protected: /// Generic function called by default for each node, to be overridden by derived classes diff --git a/compiler/libsolidity/ast/AST_accept.h b/compiler/libsolidity/ast/AST_accept.h index 13d9f6b9..c4b4ef86 100644 --- a/compiler/libsolidity/ast/AST_accept.h +++ b/compiler/libsolidity/ast/AST_accept.h @@ -943,6 +943,24 @@ void ElementaryTypeNameExpression::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void MappingNameExpression::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_type->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void MappingNameExpression::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_type->accept(_visitor); + } + _visitor.endVisit(*this); +} + void Literal::accept(ASTVisitor& _visitor) { _visitor.visit(*this); diff --git a/compiler/libsolidity/ast/Types.cpp b/compiler/libsolidity/ast/Types.cpp index 051abf8a..1f0eac92 100644 --- a/compiler/libsolidity/ast/Types.cpp +++ b/compiler/libsolidity/ast/Types.cpp @@ -1591,39 +1591,15 @@ BoolResult ArrayType::isImplicitlyConvertibleTo(Type const& _convertTo) const if (_convertTo.category() != category()) return false; auto& convertTo = dynamic_cast(_convertTo); - if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString()) - return false; - // memory/calldata to storage can be converted, but only to a direct storage reference - if (convertTo.location() == DataLocation::Storage && location() != DataLocation::Storage && convertTo.isPointer()) - return false; - if (convertTo.location() == DataLocation::CallData && location() != convertTo.location()) - return false; - if (convertTo.location() == DataLocation::Storage && !convertTo.isPointer()) - { - // Less restrictive conversion, since we need to copy anyway. - if (!baseType()->isImplicitlyConvertibleTo(*convertTo.baseType())) - return false; - if (convertTo.isDynamicallySized()) - return true; - return !isDynamicallySized() && convertTo.length() >= length(); - } - else - { - // Conversion to storage pointer or to memory, we de not copy element-for-element here, so - // require that the base type is the same, not only convertible. - // This disallows assignment of nested dynamic arrays from storage to memory for now. - if ( - *TypeProvider::withLocationIfReference(location(), baseType()) != - *TypeProvider::withLocationIfReference(location(), convertTo.baseType()) - ) - return false; - if (isDynamicallySized() != convertTo.isDynamicallySized()) - return false; - // We also require that the size is the same. - if (!isDynamicallySized() && length() != convertTo.length()) - return false; - return true; + if (isByteArray() || isString()) + return convertTo.isByteArray() || convertTo.isString(); + Type const* t0 = baseType(); + Type const* t1 = convertTo.baseType(); + while (dynamic_cast(t0) && dynamic_cast(t1)) { + t0 = dynamic_cast(t0)->baseType(); + t1 = dynamic_cast(t1)->baseType(); } + return *t0 == *t1; } BoolResult ArrayType::isExplicitlyConvertibleTo(Type const& _convertTo) const @@ -3544,10 +3520,13 @@ Type const* MappingType::encodingType() const return TypeProvider::integer(256, IntegerType::Modifier::Unsigned); } -BoolResult MappingType::isImplicitlyConvertibleTo(const MappingType * _other) const +BoolResult MappingType::isImplicitlyConvertibleTo(Type const& _other) const { - return keyType()->isImplicitlyConvertibleTo(*_other->keyType()) && - valueType()->isImplicitlyConvertibleTo(*_other->valueType()); + if (_other.category() != category()) + return false; + auto map = dynamic_cast(&_other); + return keyType()->isImplicitlyConvertibleTo(*map->keyType()) && + valueType()->isImplicitlyConvertibleTo(*map->valueType()); } string MappingType::richIdentifier() const @@ -3825,24 +3804,6 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const {"resetStorage", TypeProvider::function(strings(), strings(), FunctionType::Kind::TVMResetStorage, false, StateMutability::NonPayable)}, {"log", TypeProvider::function(strings{"bytes32"}, strings{}, FunctionType::Kind::LogTVM,false, StateMutability::Pure)} }; - members.emplace_back("transfer", TypeProvider::function( - TypePointers{TypeProvider::address(), TypeProvider::integer(128, IntegerType::Modifier::Unsigned), - TypeProvider::boolean(), TypeProvider::integer(16, IntegerType::Modifier::Unsigned)}, - TypePointers{}, - strings{string(), string(), string(), string()}, - strings{}, - FunctionType::Kind::TVMTransfer, - false, StateMutability::Pure - )); - members.emplace_back("transfer", TypeProvider::function( - TypePointers{TypeProvider::address(), TypeProvider::integer(128, IntegerType::Modifier::Unsigned), - TypeProvider::boolean(), TypeProvider::integer(16, IntegerType::Modifier::Unsigned), TypeProvider::tvmcell()}, - TypePointers{}, - strings{string(), string(), string(), string(), string()}, - strings{}, - FunctionType::Kind::TVMTransfer, - false, StateMutability::Pure - )); members.emplace_back("setcode", TypeProvider::function( TypePointers{TypeProvider::tvmcell()}, TypePointers{}, @@ -3875,14 +3836,6 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const FunctionType::Kind::TVMHash, false, StateMutability::Pure )); - members.emplace_back("hash", TypeProvider::function( - TypePointers{TypeProvider::array(DataLocation::Memory)}, - TypePointers{TypeProvider::uint256()}, - strings{string()}, - strings{string()}, - FunctionType::Kind::TVMHash, - false, StateMutability::Pure - )); members.emplace_back("hash", TypeProvider::function( TypePointers{TypeProvider::array(DataLocation::Memory, true)}, TypePointers{TypeProvider::uint256()}, @@ -3994,6 +3947,17 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const FunctionType::Kind::TVMDeploy, true, StateMutability::Pure )); + + members.emplace_back("deployAndCallConstructorWithFlag", TypeProvider::function( + TypePointers{TypeProvider::tvmcell(), TypeProvider::address(), + TypeProvider::uint(128), TypeProvider::uint(8), TypeProvider::uint(32)}, + TypePointers{}, + strings{string(), string(), string(), string(), string()}, + strings{}, + FunctionType::Kind::TVMDeploy, + true, StateMutability::Pure + )); + members.emplace_back("functionId", TypeProvider::function( TypePointers{}, TypePointers{TypeProvider::uint(32)}, diff --git a/compiler/libsolidity/ast/Types.h b/compiler/libsolidity/ast/Types.h index fa46f7df..98741e35 100644 --- a/compiler/libsolidity/ast/Types.h +++ b/compiler/libsolidity/ast/Types.h @@ -1417,7 +1417,7 @@ class MappingType: public Type Category category() const override { return Category::Mapping; } - BoolResult isImplicitlyConvertibleTo(const MappingType * _other) const; + BoolResult isImplicitlyConvertibleTo(Type const& _other) const override; std::string richIdentifier() const override; bool operator==(Type const& _other) const override; std::string toString(bool _short) const override; diff --git a/compiler/libsolidity/codegen/TVM.cpp b/compiler/libsolidity/codegen/TVM.cpp index d842564d..7bd6c621 100644 --- a/compiler/libsolidity/codegen/TVM.cpp +++ b/compiler/libsolidity/codegen/TVM.cpp @@ -36,11 +36,19 @@ void TVMCompilerProceedContract(ContractDefinition const& _contract, if (!TVMContractCompiler::m_optionsEnabled) return; - std::string mainContract = (TVMContractCompiler::m_mainContractName == "") ? + std::string mainContract = (TVMContractCompiler::m_mainContractName.empty()) ? getLastContractName() : TVMContractCompiler::m_mainContractName; - if (_contract.name() != mainContract) - return; + if (TVMContractCompiler::m_outputFolder.empty()) { + if (_contract.name() != mainContract) + return; + } else { + if (_contract.abstract() || _contract.isInterface()) + return; + TVMContractCompiler::m_outputToFile = true; + namespace fs = boost::filesystem; + TVMContractCompiler::m_fileName = (fs::path(TVMContractCompiler::m_outputFolder) / _contract.name()).string(); + } for (ContractDefinition const* c : TVMContractCompiler::m_allContracts) { TVMTypeChecker::check(c, *pragmaDirectives); @@ -78,11 +86,7 @@ void TVMSetAllContracts(const std::vector& allContrac } void TVMSetFileName(std::string _fileName) { - std::string fileName = _fileName; - if (auto point = fileName.find("."); point != std::string::npos) - fileName = fileName.substr(0, point); - - TVMContractCompiler::m_fileName = fileName; + TVMContractCompiler::m_fileName = boost::filesystem::path(_fileName).stem().string(); } bool TVMIsOutputProduced() { diff --git a/compiler/libsolidity/codegen/TVMABI.cpp b/compiler/libsolidity/codegen/TVMABI.cpp index 897a72e4..c2ac168c 100644 --- a/compiler/libsolidity/codegen/TVMABI.cpp +++ b/compiler/libsolidity/codegen/TVMABI.cpp @@ -15,13 +15,17 @@ * @date 2019 */ +#include "libsolutil/picosha2.h" + #include "TVMABI.hpp" +#include "TVMPusher.hpp" #include "TVMStructCompiler.hpp" +#include "TVMExpressionCompiler.hpp" using namespace solidity::frontend; -void TVMABI::generateABI(ContractDefinition const *contract, const vector &m_allContracts, - std::vector const &pragmaDirectives, ostream *out) { +void TVMABI::generateABI(ContractDefinition const *contract, std::vector const &pragmaDirectives, + ostream *out) { PragmaDirectiveHelper pdh{pragmaDirectives}; TVMCompilerContext ctx(contract, pdh); @@ -31,7 +35,8 @@ void TVMABI::generateABI(ContractDefinition const *contract, const vectorconstructor(); main_constr != nullptr) publicFunctions.push_back(contract->constructor()); - for (auto c : m_allContracts) { + + for (auto c : contract->annotation().linearizedBaseContracts) { for (const auto &_function : c->definedFunctions()) { if (_function->isPublic() && !isTvmIntrinsic(_function->name()) && !_function->isConstructor() && !_function->isReceive() && !_function->isFallback() && !_function->isOnBounce()) @@ -223,6 +228,10 @@ Json::Value TVMABI::processFunction(const string &fname, const ast_vecfunctionID(); function["id"] = oss.str(); + } else if (funcDef && funcDef->name() == "offchainConstructor") { + std::ostringstream oss; + oss << "0x" << std::hex << std::uppercase << 3; + function["id"] = oss.str(); } function["inputs"] = inputs; function["outputs"] = outputs; @@ -392,8 +401,7 @@ DecodePositionAbiV2::DecodePositionAbiV2(int minBits, int maxBits, const ast_vec } for (int i = 0; i < static_cast(types.size()); ++i) { Type const* t = types[i]; - if ((t->category() == Type::Category::Array && to(t)->isByteArray()) || - t->category() == Type::Category::TvmCell) { + if (isRefType(t)) { lastRefType = i; } } @@ -621,3 +629,269 @@ void DecodeFunctionParams::decodeParameter(VariableDeclaration const *variable, cast_error(*variable, "Unsupported parameter type for decoding: " + type->toString()); } } + + +EncodePosition::EncodePosition(int bits, const std::vector &_types) : + restSliceBits{TvmConst::CellBitLength - bits}, + restFef{4}, + qtyOfCreatedBuilders{0} { + + for (Type const * t : _types) { + init(t); + } + + for (int i = 0; i < static_cast(types.size()); ++i) { + Type const* t = types[i]; + if (isRefType(t)) { + lastRefType = i; + } + } + + for (int i = 0; i < static_cast(types.size()); ++i) { + isNeedNewCell.push_back(updateState(i)); + } +} + +bool EncodePosition::needNewCell(Type const *type) { + solAssert(*type == *types[currentIndex], ""); + return isNeedNewCell[currentIndex++]; +} + +bool EncodePosition::updateState(int i) { + ABITypeSize size(types[i]); + solAssert(0 <= size.maxRefs && size.maxRefs <= 1, ""); + + restSliceBits -= size.maxBits; + restFef -= size.maxRefs; + + if (i == lastRefType && restFef == 0 && i + 1 == static_cast(types.size())) { + return false; + } + + if (restSliceBits < 0 || restFef == 0) { + restSliceBits = TvmConst::CellBitLength - size.maxBits; + restFef = 4 - size.maxRefs; + ++qtyOfCreatedBuilders; + return true; + } + return false; +} + +void EncodePosition::init(Type const* t) { + if (t->category() == Type::Category::Struct) { + auto members = to(t)->structDefinition().members(); + for (const auto &m : members) { + init(m->type()); + } + } else { + types.push_back(t); + } +} + +int EncodePosition::countOfCreatedBuilders() const { + return qtyOfCreatedBuilders; +} + +void EncodeFunctionParams::createMsgBodyAndAppendToBuilder2(const ast_vec &arguments, + const ReasonOfOutboundMessage reason, + const CallableDeclaration* funcDef, + int builderSize) { + const int saveStackSize = pusher->getStack().size(); + const ast_vec ¶meters = funcDef->parameters(); + solAssert(parameters.size() == arguments.size(), ""); + createMsgBodyAndAppendToBuilder( + [&](size_t idx) { + pusher->push(0, ";; " + parameters[idx]->name()); + TVMExpressionCompiler{*pusher}.compileNewExpr(arguments[idx].get()); + }, + reason, + funcDef, + false, + builderSize + ); + solAssert(saveStackSize == pusher->getStack().size(), ""); +} + +uint32_t EncodeFunctionParams::calculateFunctionID(const CallableDeclaration * funcDef) { + std::stringstream ss; + ss << funcDef->name() << "("; + bool comma = false; + if (pusher->ctx().pragmaHelper().abiVersion() == 1) { + ss << "time"; + comma = true; + } + for (const auto& input : funcDef->parameters()) { + std::string typestr = getTypeString(input->type(), funcDef); + solAssert(!typestr.empty(), "Wrong type in remote function params."); + if (comma) + ss << ","; + ss << typestr; + comma = true; + } + ss << ")"; + comma = false; + if (funcDef->returnParameterList()) { + ss << "("; + for (const auto& output : funcDef->returnParameters()) { + std::string typestr = getTypeString(output->type(), funcDef); + solAssert(!typestr.empty(), "Wrong type in remote function params."); + if (comma) + ss << ","; + ss << typestr; + comma = true; + } + ss << ")"; + } + if (pusher->ctx().pragmaHelper().abiVersion() == 2) + ss << "v2"; + else + ss << "v1"; + + std::string str = ss.str(); + bytes hash = picosha2::hash256(bytes( + str.begin(), + str.end() + )); + uint32_t funcID = 0; + for (size_t i = 0; i < 4; i++) { + funcID <<= 8; + funcID += hash[i]; + } + + return funcID; +} + +void EncodeFunctionParams::createMsgBodyAndAppendToBuilder(const std::function &pushParam, + const ReasonOfOutboundMessage &reason, + const CallableDeclaration * funcDef, + bool encodeReturnParam, + const int bitSizeBuilder) { + + const ast_vec ¶meters = + encodeReturnParam? funcDef->returnParameters() : funcDef->parameters(); + std::vector types; + std::vector nodes; + for (const ASTPointer& param : parameters) { + types.push_back(param->annotation().type); + nodes.push_back(param.get()); + } + + uint32_t funcID = calculateFunctionID(funcDef); + if (reason == ReasonOfOutboundMessage::FunctionReturnExternal) + funcID |= 0x80000000; + else if (reason == ReasonOfOutboundMessage::EmitEventExternal) + funcID &= 0x7FFFFFFF; + else if (reason == ReasonOfOutboundMessage::RemoteCallInternal) + funcID &= 0x7FFFFFFF; + std::stringstream ss; + ss << "x" << std::hex << std::setfill('0') << std::setw(8) << funcID; + + std::unique_ptr position = std::make_unique(bitSizeBuilder + 32, types); + const bool doAppend = position->countOfCreatedBuilders() == 0; + if (doAppend) { + pusher->stzeroes(1); + pusher->push(0, "STSLICECONST " + ss.str()); + } else { + pusher->stones(1); + position = std::make_unique(32, types); + pusher->push(+1, "NEWC"); + pusher->push(0, "STSLICECONST " + ss.str()); + } + encodeParameters(types, nodes, pushParam, *position); + if (!doAppend) { + pusher->push(-1, "STBREFR"); + } +} + +void +EncodeFunctionParams::encodeParameters(const std::vector &types, const std::vector &nodes, + const std::function &pushParam, + EncodePosition &position) { + // builder must be situated on top stack + solAssert(types.size() == nodes.size(), ""); + for (size_t idx = 0; idx < types.size(); idx++) { + auto type = types[idx]; + encodeParameter(type, position, [&](){pushParam(idx);}, nodes[idx]); + } + for (int idx = 0; idx < position.countOfCreatedBuilders(); idx++) { + pusher->push(-1, "STBREFR"); + } +} + +std::string EncodeFunctionParams::getTypeString(Type const * type, const CallableDeclaration * funcDef) { + if (auto structType = to(type)) { + std::string ret = "("; + for (size_t i = 0; i < structType->structDefinition().members().size(); i++) { + if (i != 0) ret += ","; + ret += getTypeString(structType->structDefinition().members()[i]->type(), funcDef); + } + ret += ")"; + return ret; + } else if (auto arrayType = to(type)) { + if (!arrayType->isByteArray()) + return getTypeString(arrayType->baseType(), funcDef) + "[]"; + } else if (auto mapping = to(type)) { + return "map(" + getTypeString(mapping->keyType(), funcDef) + "," + + getTypeString(mapping->valueType(), funcDef) + ")"; + } + + return TVMABI::getParamTypeString(type, *funcDef); +} + +void EncodeFunctionParams::encodeParameter(Type const *type, EncodePosition &position, + const std::function &pushParam, ASTNode const *node) { + // stack: builder... + if (auto structType = to(type)) { + pushParam(); // builder... struct + encodeStruct(structType, node, position); // stack: builder... + } else { + if (position.needNewCell(type)) { + pusher->push(+1, "NEWC"); + } + + if (isIntegralType(type) || isAddressOrContractType(type)) { + pushParam(); + pusher->push(-1, storeIntegralOrAddress(type, true)); + } else if (auto arrayType = to(type)) { + if (arrayType->isByteArray()) { + pushParam(); + pusher->push(-1, "STREFR"); + } else { + pushParam(); + // builder array + pusher->push(-1 + 2, "UNPAIR"); // builder size dict + pusher->exchange(0, 2); // dict size builder + pusher->push(-1, "STU 32"); // dict builder + pusher->push(-1, "STDICT"); // builder + } + } else if (to(type)) { + pushParam(); + pusher->push(-1, "STREFR"); + } else if (to(type)) { + pushParam(); + pusher->push(0, "SWAP"); + pusher->push(-1, "STDICT"); + } else { + cast_error(*node, "Unsupported type for encoding: " + type->toString()); + } + } +} + +void EncodeFunctionParams::encodeStruct(const StructType* structType, ASTNode const* node, EncodePosition& position) { + // builder... builder struct + const int saveStackSize0 = pusher->getStack().size() - 2; + ast_vec const& members = structType->structDefinition().members(); + const int memberQty = members.size(); + pusher->untuple(memberQty); // builder... builder values... + pusher->blockSwap(1, memberQty); // builder... values... builder + for (int i = 0; i < memberQty; ++i) { + encodeParameter(members[i]->type(), position, [&]() { + const int index = pusher->getStack().size() - saveStackSize0 - 1 - i; + pusher->pushS(index); + }, node); + } + + // builder... values... builder... + const int builderQty = pusher->getStack().size() - saveStackSize0 - memberQty; + pusher->dropUnder(builderQty, memberQty); +} diff --git a/compiler/libsolidity/codegen/TVMABI.hpp b/compiler/libsolidity/codegen/TVMABI.hpp index 05cc2588..d9425110 100644 --- a/compiler/libsolidity/codegen/TVMABI.hpp +++ b/compiler/libsolidity/codegen/TVMABI.hpp @@ -18,14 +18,17 @@ #pragma once -#include "TVMPusher.hpp" +#include "TVMCommons.hpp" namespace solidity::frontend { +class StackPusherHelper; + class TVMABI { public: - static void generateABI(ContractDefinition const* contract, const vector& m_allContracts, - std::vector const& pragmaDirectives, std::ostream* out = &cout); + static void generateABI(ContractDefinition const* contract, + std::vector const& pragmaDirectives, std::ostream* out = &cout); + static string getParamTypeString(Type const* type, ASTNode const& node); private: static void printData(const Json::Value& json, std::ostream* out); static void print(const Json::Value& json, std::ostream* out); @@ -36,7 +39,6 @@ class TVMABI { FunctionDefinition const* funcDef = nullptr ); static Json::Value encodeParams(const ast_vec& params); - static string getParamTypeString(Type const* type, ASTNode const& node); static Json::Value setupType(const string& name, const Type* type, ASTNode const& node); static Json::Value setupStructComponents(const StructType* type, ASTNode const& node); }; @@ -104,4 +106,63 @@ class DecodeFunctionParams : private boost::noncopyable { StackPusherHelper *pusher{}; }; + + + +enum class ReasonOfOutboundMessage { + EmitEventExternal, + FunctionReturnExternal, + RemoteCallInternal +}; + +class EncodePosition : private boost::noncopyable { + int restSliceBits{}; + int restFef{}; + int qtyOfCreatedBuilders{}; + std::vector types; + int currentIndex{}; + std::vector isNeedNewCell; + int lastRefType{}; + +public: + explicit EncodePosition(int bits, const std::vector &types); + bool needNewCell(Type const* type); +private: + bool updateState(int i); + void init(Type const* t); +public: + int countOfCreatedBuilders() const; +}; + +class EncodeFunctionParams : private boost::noncopyable { +public: + explicit EncodeFunctionParams(StackPusherHelper *pusher) : pusher{pusher} {} + void createMsgBodyAndAppendToBuilder2(const ast_vec& arguments, + const ReasonOfOutboundMessage reason, + const CallableDeclaration *funcDef, + int builderSize); + +public: + uint32_t calculateFunctionID(const CallableDeclaration *funcDef); + +public: + void createMsgBodyAndAppendToBuilder(const std::function& pushParam, + const ReasonOfOutboundMessage& reason, + const CallableDeclaration *funcDef, + bool encodeReturnParam, + const int bitSizeBuilder); + void encodeParameters(const std::vector& types, + const std::vector& nodes, + const std::function& pushParam, + EncodePosition& position); + +private: + std::string getTypeString(Type const * type, const CallableDeclaration * funcDef); + void encodeParameter(Type const* type, EncodePosition& position, const std::function& pushParam, ASTNode const* node); + void encodeStruct(const StructType* structType, ASTNode const* node, EncodePosition& position); + +private: + StackPusherHelper *pusher{}; +}; + } // solidity::frontend diff --git a/compiler/libsolidity/codegen/TVMAnalyzer.cpp b/compiler/libsolidity/codegen/TVMAnalyzer.cpp index e11ff370..df5badb6 100644 --- a/compiler/libsolidity/codegen/TVMAnalyzer.cpp +++ b/compiler/libsolidity/codegen/TVMAnalyzer.cpp @@ -23,11 +23,11 @@ bool TVMAnalyzer::visit(Assignment const& _variable) { if (auto ident = dynamic_cast(&_variable.leftHandSide())) lefts.push_back(ident); if (auto tuple = dynamic_cast(&_variable.leftHandSide())) - for(auto tup: tuple->components()) + for (auto tup: tuple->components()) if (auto ident = dynamic_cast(tup.get())) lefts.push_back(ident); - for(auto ident: lefts) { + for (auto ident: lefts) { if (auto var = dynamic_cast(ident->annotation().referencedDeclaration)) { solAssert(!var->name().empty(), ""); if (dynamic_cast(var->type()) && var->isLocalVariable()) @@ -39,11 +39,11 @@ bool TVMAnalyzer::visit(Assignment const& _variable) { if (auto ident = dynamic_cast(&_variable.leftHandSide())) lefts2.push_back(ident); if (auto tuple = dynamic_cast(&_variable.leftHandSide())) - for(auto tup: tuple->components()) + for (auto tup: tuple->components()) if (auto ident = dynamic_cast(tup.get())) lefts2.push_back(ident); - for(auto member: lefts2) { + for (auto member: lefts2) { if (auto ident = dynamic_cast(&member->expression())) if (auto var = dynamic_cast(ident->annotation().referencedDeclaration)) { solAssert(!var->name().empty(), ""); @@ -57,11 +57,11 @@ bool TVMAnalyzer::visit(Assignment const& _variable) { if (auto ident = dynamic_cast(&_variable.rightHandSide())) rights.push_back(ident); if (auto tuple = dynamic_cast(&_variable.rightHandSide())) - for(auto tup: tuple->components()) + for (auto tup: tuple->components()) if (auto ident = dynamic_cast(tup.get())) rights.push_back(ident); - for(auto ident: rights) { + for (auto ident: rights) { if (auto var = dynamic_cast(ident->annotation().referencedDeclaration)) { solAssert(!var->name().empty(), ""); if (dynamic_cast(var->type())) { @@ -100,7 +100,7 @@ bool TVMAnalyzer::visit(FunctionDefinition const& _function) { else solAssert(!m_currentFunction, ""); - for(auto ret: _function.returnParameters()) + for (auto ret: _function.returnParameters()) m_declaredReturns.push_back(make_pair(ret->id(), ret.get())); return true; } @@ -116,7 +116,7 @@ bool TVMAnalyzer::visit(Return const& _return) { if (auto ident = dynamic_cast(tup.get())) rets.push_back(ident); - for(auto ident: rets) { + for (auto ident: rets) { if (auto var = dynamic_cast(ident->annotation().referencedDeclaration)) { solAssert(!var->name().empty(), ""); if (dynamic_cast(var->type())) { @@ -131,7 +131,7 @@ bool TVMAnalyzer::visit(Return const& _return) { } bool TVMAnalyzer::visit(FunctionCall const& _functionCall) { - for(ASTPointer argument: _functionCall.arguments()) { + for (ASTPointer argument: _functionCall.arguments()) { if (auto ident = dynamic_cast(argument.get())) { if (auto var = dynamic_cast(ident->annotation().referencedDeclaration)) { solAssert(!var->name().empty(), ""); diff --git a/compiler/libsolidity/codegen/TVMCommons.cpp b/compiler/libsolidity/codegen/TVMCommons.cpp index 85d9927d..2b8d7b83 100644 --- a/compiler/libsolidity/codegen/TVMCommons.cpp +++ b/compiler/libsolidity/codegen/TVMCommons.cpp @@ -20,9 +20,6 @@ #include "TVMPusher.hpp" -using namespace solidity::frontend; - - namespace solidity::frontend { std::string functionName(FunctionDefinition const *_function) { @@ -68,6 +65,11 @@ void cast_warning(const ASTNode &node, const string &error_message) { cerr << ASTNode2String(node, error_message, true) << endl; } +void fatal_error(const string &error_message) { + cerr << error_message << '\n'; + std::exit(EXIT_FAILURE); +} + const ContractDefinition * getSuperContract(const ContractDefinition *currentContract, const ContractDefinition *mainContract, const string &fname) { ContractDefinition const* prev = nullptr; @@ -154,6 +156,11 @@ bool isStringOrStringLiteralOrBytes(const Type *type) { return type->category() == Type::Category::StringLiteral || (arrayType && arrayType->isByteArray()); } +bool isRefType(Type const* t) { + return (t->category() == Type::Category::Array && to(t)->isByteArray()) || + t->category() == Type::Category::TvmCell; +} + std::string typeToDictChar(Type const *keyType) { TypeInfo ti(keyType); if (ti.isNumeric) { @@ -303,6 +310,4 @@ vector getContractFunctions(ContractDefinition const return result; } - - -} +} // end namespace solidity::frontend diff --git a/compiler/libsolidity/codegen/TVMCommons.hpp b/compiler/libsolidity/codegen/TVMCommons.hpp index 4972801c..e933b574 100644 --- a/compiler/libsolidity/codegen/TVMCommons.hpp +++ b/compiler/libsolidity/codegen/TVMCommons.hpp @@ -252,6 +252,8 @@ bool isIntegralType(const Type* type); bool isStringOrStringLiteralOrBytes(const Type* type); +bool isRefType(const Type* type); + std::string typeToDictChar(Type const* keyType); int lengthOfDictKey(Type const* key); @@ -296,6 +298,9 @@ void cast_error(const ASTNode& node, const string& error_message); void cast_warning(const ASTNode& node, const string& error_message); +[[noreturn]] +void fatal_error(const string &error_message); + class PragmaDirectiveHelper { public: explicit PragmaDirectiveHelper(std::vector const& _pragmaDirectives) : diff --git a/compiler/libsolidity/codegen/TVMConstants.hpp b/compiler/libsolidity/codegen/TVMConstants.hpp index 82178e20..4faac0ee 100644 --- a/compiler/libsolidity/codegen/TVMConstants.hpp +++ b/compiler/libsolidity/codegen/TVMConstants.hpp @@ -47,6 +47,7 @@ namespace TvmConst { } const int CellBitLength = 1023; const int ArrayKeyLength = 32; + const int MaxPushSliceLength = 249; // PUSHSLICE xSSSS; SSSS.length() <= MaxPushSliceLength namespace RuntimeException { const int ArrayIndexOutOfRange = 50; @@ -60,7 +61,7 @@ namespace TvmConst { } namespace FunctionId { - const int First = 3; + const int First = 4; const int64_t Last = 0xFFFFFFFD; } @@ -77,6 +78,6 @@ namespace TvmConst { } namespace Attributes { - const std::string MuteStructWarning = "maybe_unsaved"; + const std::string MuteStructWarning{"maybe_unsaved"}; } } diff --git a/compiler/libsolidity/codegen/TVMContractCompiler.cpp b/compiler/libsolidity/codegen/TVMContractCompiler.cpp index 6ab4f48a..1c009b7a 100644 --- a/compiler/libsolidity/codegen/TVMContractCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMContractCompiler.cpp @@ -63,32 +63,15 @@ void TVMConstructorCompiler::dfs(ContractDefinition const *c) { void TVMConstructorCompiler::generateConstructors() { m_pusher.generateGlobl("constructor", true); - // copy c4 to c7 - m_pusher.pushLines(R"( -GETGLOB 1 -ISNULL -)"); - m_pusher.startContinuation(); - m_pusher.pushPrivateFunctionOrMacroCall(0, "c4_to_c7_with_init_storage"); - - m_pusher.endContinuation(); - m_pusher.pushLines(R"( -IF -)"); - // generate constructor protection - m_pusher.pushLines(R"( -;; constructor protection -GETGLOB 6 -THROWIF 51 -PUSHINT 1 -SETGLOB 6 -;; end constructor protection -)"); - - m_pusher.push(+1, ""); // push encoded params of constructor + if (m_pusher.ctx().haveOffChainConstructor()) { + m_pusher.pushPrivateFunctionOrMacroCall(0, "c4_to_c7"); + } else { + c4ToC7WithMemoryInitAndConstructorProtection(); + } + m_pusher.push(+1, ""); // push encoded params of constructor std::vector linearizedBaseContracts = m_pusher.ctx().getContract()->annotation().linearizedBaseContracts; // from derived to base for (ContractDefinition const* c : linearizedBaseContracts) { @@ -132,7 +115,7 @@ SETGLOB 6 } TVMFunctionCompiler functionCompiler{m_pusher, false, 0, c->constructor(), - m_pusher.getStack().size() - static_cast(c->constructor()->parameters().size())}; + m_pusher.getStack().size() - static_cast(c->constructor()->parameters().size())}; functionCompiler.makeInlineFunctionCall(false); } @@ -148,6 +131,48 @@ SETGLOB 6 m_pusher.push(0, " "); } +void TVMConstructorCompiler::generateOffChainConstructor() { + FunctionDefinition const* function = m_pusher.ctx().m_currentFunction; + if (function->visibility() != Visibility::External) { + cast_error(*function, "This function must be defined as \"external\"."); + } + + m_pusher.generateInternal("offchainConstructor", 3); + + c4ToC7WithMemoryInitAndConstructorProtection(); + + TVMFunctionCompiler functionCompiler(m_pusher, true, 0, function, 0); + functionCompiler.decodeFunctionParamsAndLocateVars(); + functionCompiler.visitFunctionWithModifiers(); + + m_pusher.pushPrivateFunctionOrMacroCall(0, "c7_to_c4"); + m_pusher.push(0, "TRUE"); + m_pusher.push(0, "SETGLOB 7"); + m_pusher.push(0, " "); +} + +void TVMConstructorCompiler::c4ToC7WithMemoryInitAndConstructorProtection() { + // copy c4 to c7 + m_pusher.pushLines(R"( +GETGLOB 1 +ISNULL +)"); + m_pusher.startContinuation(); + m_pusher.pushPrivateFunctionOrMacroCall(0, "c4_to_c7_with_init_storage"); + m_pusher.endContinuation(); + m_pusher.push(0, "IF"); + + // generate constructor protection + m_pusher.pushLines(R"( +;; constructor protection +GETGLOB 6 +THROWIF 51 +PUSHINT 1 +SETGLOB 6 +;; end constructor protection +)"); +} + bool TVMContractCompiler::m_optionsEnabled = false; TvmOption TVMContractCompiler::m_tvmOption = TvmOption::Code; bool TVMContractCompiler::m_outputProduced = false; @@ -157,20 +182,24 @@ std::string TVMContractCompiler::m_outputWarnings; std::vector TVMContractCompiler::m_allContracts; std::string TVMContractCompiler::m_mainContractName; std::string TVMContractCompiler::m_fileName; +std::string TVMContractCompiler::m_outputFolder; bool TVMContractCompiler::m_outputToFile = false; void TVMContractCompiler::generateABI(ContractDefinition const *contract, - std::vector const &pragmaDirectives) { + std::vector const &pragmaDirectives) { m_outputProduced = true; - ofstream ofile; if (m_outputToFile) { + ofstream ofile; + ensurePathExists(); ofile.open(m_fileName + ".abi.json"); - TVMABI::generateABI(contract, m_allContracts, pragmaDirectives, &ofile); + if (!ofile) + fatal_error("Failed to open the output file: " + m_fileName + ".abi.json"); + TVMABI::generateABI(contract, pragmaDirectives, &ofile); ofile.close(); cout << "ABI was generated and saved to file " << m_fileName << ".abi.json" << endl; } else { - TVMABI::generateABI(contract, m_allContracts, pragmaDirectives); + TVMABI::generateABI(contract, pragmaDirectives); } } @@ -212,7 +241,10 @@ void TVMContractCompiler::proceedContract(ContractDefinition const *contract, Pr if (m_outputToFile) { ofstream ofile; + ensurePathExists(); ofile.open(m_fileName + ".code"); + if (!ofile) + fatal_error("Failed to open the output file: " + m_fileName + ".code"); ofile << code.str(); ofile.close(); cout << "Code was generated and saved to file " << m_fileName << ".code" << endl; @@ -258,63 +290,58 @@ TVMContractCompiler::proceedContractMode1(ContractDefinition const *contract, Pr optimize_and_append_code(code, pusher, g_disable_optimizer); } - for (FunctionDefinition const* _function : ctx.m_functionsList) { - ctx.m_currentFunction = _function; - solAssert(!ctx.isPureFunction(_function), ""); - if (_function->isConstructor() || _function->isReceive() || _function->isFallback() || - _function->isOnBounce()) { - continue; - } else if (_function->visibility() == Visibility::TvmGetter) { - StackPusherHelper pusher{&ctx}; - TVMFunctionCompiler tvm(pusher, true, 0, _function, 0); - tvm.generateTvmGetter(_function); - optimize_and_append_code(code, pusher, g_disable_optimizer); - } else if (isMacro(_function->name())) { - // TODO: These four lines below are copied many times across this file. - // Would it be possible to shorted it by making a pattern? - StackPusherHelper pusher{&ctx}; - TVMFunctionCompiler tvm(pusher, false, 0, _function, 0); - tvm.generateMacro(); - optimize_and_append_code(code, pusher, g_disable_optimizer); - } else if (_function->name() == "onCodeUpgrade") { - StackPusherHelper pusher{&ctx}; - TVMFunctionCompiler tvm(pusher, false, 0, _function, 0); - tvm.generateOnCodeUpgrade(); - optimize_and_append_code(code, pusher, g_disable_optimizer); - } else if (_function->name() == "onTickTock") { - StackPusherHelper pusher{&ctx}; - TVMFunctionCompiler tvm(pusher, false, 0, _function, 0); - tvm.generateOnTickTock(); - optimize_and_append_code(code, pusher, g_disable_optimizer); - } else { - if (_function->isPublic()) { - bool isBaseMethod = _function != getContractFunctions(contract, _function->name()).back(); - if (!isBaseMethod) { - StackPusherHelper pusher0{&ctx}; - TVMFunctionCompiler tvm0(pusher0, true, 0, _function, 0); - tvm0.generatePublicFunction(); - optimize_and_append_code(code, pusher0, g_disable_optimizer); + for (ContractDefinition const* c : contract->annotation().linearizedBaseContracts | boost::adaptors::reversed) { + for (FunctionDefinition const* _function : c->definedFunctions()) { + if (_function->isConstructor() || _function->isReceive() || _function->isFallback() || + _function->isOnBounce() || !_function->isImplemented() || isTvmIntrinsic(_function->name()) || + isFunctionForInlining(_function)) { + continue; + } + ctx.m_currentFunction = _function; + if (_function->visibility() == Visibility::TvmGetter) { + StackPusherHelper pusher{&ctx}; + TVMFunctionCompiler tvm(pusher, true, 0, _function, 0); + tvm.generateTvmGetter(_function); + optimize_and_append_code(code, pusher, g_disable_optimizer); + } else if (isMacro(_function->name())) { + // TODO: These four lines below are copied many times across this file. + // Would it be possible to shorted it by making a pattern? + StackPusherHelper pusher{&ctx}; + TVMFunctionCompiler tvm(pusher, false, 0, _function, 0); + tvm.generateMacro(); + optimize_and_append_code(code, pusher, g_disable_optimizer); + } else if (_function->name() == "onCodeUpgrade") { + StackPusherHelper pusher{&ctx}; + TVMFunctionCompiler tvm(pusher, false, 0, _function, 0); + tvm.generateOnCodeUpgrade(); + optimize_and_append_code(code, pusher, g_disable_optimizer); + } else if (_function->name() == "onTickTock") { + StackPusherHelper pusher{&ctx}; + TVMFunctionCompiler tvm(pusher, false, 0, _function, 0); + tvm.generateOnTickTock(); + optimize_and_append_code(code, pusher, g_disable_optimizer); + } else if (_function->name() == "offchainConstructor") { + StackPusherHelper pusher{&ctx}; + TVMConstructorCompiler constructorCompiler(pusher); + constructorCompiler.generateOffChainConstructor(); + optimize_and_append_code(code, pusher, g_disable_optimizer); + } else { + if (_function->isPublic()) { + bool isBaseMethod = _function != getContractFunctions(contract, _function->name()).back(); + if (!isBaseMethod) { + StackPusherHelper pusher0{&ctx}; + TVMFunctionCompiler tvm0(pusher0, true, 0, _function, 0); + tvm0.generatePublicFunction(); + optimize_and_append_code(code, pusher0, g_disable_optimizer); + } } + StackPusherHelper pusher{&ctx}; + TVMFunctionCompiler tvm(pusher, false, 0, _function, 0); + tvm.generatePrivateFunction(); + optimize_and_append_code(code, pusher, g_disable_optimizer); } - StackPusherHelper pusher{&ctx}; - TVMFunctionCompiler tvm(pusher, false, 0, _function, 0); - tvm.generatePrivateFunction(); - optimize_and_append_code(code, pusher, g_disable_optimizer); } } - for (const auto& fname : ctx.m_remoteFunctions) { - StackPusherHelper pusher{&ctx}; - pusher.generateGlobl(fname, false); - pusher.push(0, " "); - optimize_and_append_code(code, pusher, g_disable_optimizer); - } - for (auto event : ctx.events()) { - StackPusherHelper pusher{&ctx}; - const string& ename = event->name(); - pusher.generateGlobl(ename, false); - pusher.push(0, " "); - optimize_and_append_code(code, pusher, g_disable_optimizer); - } if (!ctx.isStdlib()) { { @@ -402,3 +429,16 @@ void TVMContractCompiler::fillInlineFunctions(TVMCompilerContext &ctx, ContractD ctx.m_inlinedFunctions[fname + "_without"] = codeWithout; } } + +void TVMContractCompiler::ensurePathExists() +{ + if (m_outputFolder.empty()) + return; + + namespace fs = boost::filesystem; + // create directory if not existent + fs::path p(m_outputFolder); + // Do not try creating the directory if the first item is . or .. + if (p.filename() != "." && p.filename() != "..") + fs::create_directories(p); +} diff --git a/compiler/libsolidity/codegen/TVMContractCompiler.hpp b/compiler/libsolidity/codegen/TVMContractCompiler.hpp index 3eb56575..6f40c782 100644 --- a/compiler/libsolidity/codegen/TVMContractCompiler.hpp +++ b/compiler/libsolidity/codegen/TVMContractCompiler.hpp @@ -37,6 +37,9 @@ class TVMConstructorCompiler: private boost::noncopyable { explicit TVMConstructorCompiler(StackPusherHelper& pusher); void dfs(ContractDefinition const* c); void generateConstructors(); + void generateOffChainConstructor(); +private: + void c4ToC7WithMemoryInitAndConstructorProtection(); }; class TVMContractCompiler: private boost::noncopyable { @@ -45,6 +48,7 @@ class TVMContractCompiler: private boost::noncopyable { static std::string m_mainContractName; static bool m_outputToFile; static std::string m_fileName; + static std::string m_outputFolder; static bool m_optionsEnabled; static TvmOption m_tvmOption; static bool m_outputProduced; @@ -60,6 +64,8 @@ class TVMContractCompiler: private boost::noncopyable { static CodeLines proceedContractMode0(ContractDefinition const* contract, PragmaDirectiveHelper const& pragmaHelper); static CodeLines proceedContractMode1(ContractDefinition const* contract, PragmaDirectiveHelper const& pragmaHelper); static void fillInlineFunctions(TVMCompilerContext& ctx, ContractDefinition const* contract); + + static void ensurePathExists(); }; } // end solidity::frontend diff --git a/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp b/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp index e7432bd9..79047532 100644 --- a/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMExpressionCompiler.cpp @@ -145,12 +145,12 @@ void TVMExpressionCompiler::visitStringLiteralAbiV2(Literal const &_node) { << (static_cast(str.at(index)) & 0xFF); slice += ss.str(); } - if (slice.size() / 2 >= 126) { + if (slice.size() > TvmConst::MaxPushSliceLength) { + m_pusher.push(+1, "PUSHSLICE x" + slice.substr(TvmConst::MaxPushSliceLength)); + m_pusher.push(+1, "PUSHSLICE x" + slice.substr(0, TvmConst::MaxPushSliceLength)); m_pusher.push(+1, "NEWC"); - m_pusher.push(+1, "PUSHSLICE x" + slice.substr(0, slice.size() / 2)); - m_pusher.push(-1, "STSLICER"); - m_pusher.push(+1, "PUSHSLICE x" + slice.substr(slice.size() / 2)); - m_pusher.push(-1, "STSLICER"); + m_pusher.push(-1, "STSLICE"); + m_pusher.push(-1, "STSLICE"); } else { m_pusher.push(+1, "PUSHSLICE x" + slice); m_pusher.push(+1, "NEWC"); @@ -946,45 +946,15 @@ bool TVMExpressionCompiler::checkAbiMethodCall(FunctionCall const &_functionCall return false; } -int TVMExpressionCompiler::encodeOutboundMessageBody2(const string &name, const ast_vec &arguments, - const ast_vec ¶meters, - const StackPusherHelper::ReasonOfOutboundMessage reason) { - solAssert(m_expressionDepth == -1, ""); - m_isResultNeeded = true; - return encodeOutboundMessageBody(name, arguments, parameters, reason); -} - -int TVMExpressionCompiler::encodeOutboundMessageBody(const string &name, const ast_vec &arguments, - const ast_vec ¶meters, - const StackPusherHelper::ReasonOfOutboundMessage reason) { - solAssert(parameters.size() == arguments.size(), ""); - auto& stack = m_pusher.getStack(); - auto savedStackSize = stack.size(); - - std::vector types; - std::vector nodes; - for (const auto & argument : parameters) { - types.push_back(argument->annotation().type); - nodes.push_back(argument.get()); - } - - int bodySize = m_pusher.encodeFunctionAndParams( - name, - types, - nodes, - [&](size_t idx) { - m_pusher.push(0, ";; " + parameters[idx]->name()); - acceptExpr(arguments[idx].get()); - }, - reason - ); - - stack.ensureSize(savedStackSize + 1, "encodeRemoteFunctionCall"); - return bodySize; -} - bool TVMExpressionCompiler::checkRemoteMethodCall(FunctionCall const &_functionCall) { const ast_vec arguments = _functionCall.arguments(); + + std::map exprs; + std::map constParams = {{TvmConst::int_msg_info::ihr_disabled, "1"}, + {TvmConst::int_msg_info::bounce, "1"}}; + std::function appendBody; + Expression const *sendrawmsgFlag{}; + if (auto functionOptions = to(&_functionCall.expression())) { if (isCurrentResultNeeded()) cast_error(_functionCall, "Calls to remote contract do not return result."); @@ -1000,12 +970,6 @@ bool TVMExpressionCompiler::checkRemoteMethodCall(FunctionCall const &_functionC if (!isIn(*option, "flag", "value", "currencies")) cast_error(_functionCall, "Unsupported function call option: " + *option); - std::map exprs; - std::map constParams = {{TvmConst::int_msg_info::ihr_disabled, "1"}, - {TvmConst::int_msg_info::bounce, "1"}}; - Expression const* sendrawmsgFlag{}; - - // Search for currencies option auto currenciesIt = std::find_if(optionNames.begin(), optionNames.end(), [](auto el) { return *el == "currencies"; }); @@ -1028,12 +992,12 @@ bool TVMExpressionCompiler::checkRemoteMethodCall(FunctionCall const &_functionC // remote_addr exprs[TvmConst::int_msg_info::dest] = &memberAccess->expression(); - auto pushBody = [&]() { + appendBody = [&](int builderSize) { const FunctionDefinition *fdef = getRemoteFunctionDefinition(memberAccess); solAssert(fdef, ""); - const auto &functionName = memberAccess->memberName(); - return encodeOutboundMessageBody(functionName, arguments, fdef->parameters(), - StackPusherHelper::ReasonOfOutboundMessage::RemoteCallInternal); + EncodeFunctionParams{&m_pusher}.createMsgBodyAndAppendToBuilder2(arguments, + ReasonOfOutboundMessage::RemoteCallInternal, + fdef, builderSize); }; @@ -1043,67 +1007,57 @@ bool TVMExpressionCompiler::checkRemoteMethodCall(FunctionCall const &_functionC size_t index = flagIt - optionNames.begin(); sendrawmsgFlag = functionOptions->options()[index].get(); } + } else { + constParams[TvmConst::int_msg_info::grams] = StackPusherHelper::gramsToBinaryString(10'000'000); - if (sendrawmsgFlag) - m_pusher.sendIntMsg(exprs, constParams, pushBody, [&](){acceptExpr(sendrawmsgFlag);}); - else - m_pusher.sendIntMsg(exprs, constParams, pushBody, nullptr); - return true; - } - + Expression const *currentExpression = &_functionCall.expression(); + while (true) { + auto currentFunctionCall = to(currentExpression); + if (currentFunctionCall == nullptr) { + break; + } - std::map exprs; - std::map constParams = {{TvmConst::int_msg_info::ihr_disabled, "1"}, - {TvmConst::int_msg_info::bounce, "1"}}; - constParams[TvmConst::int_msg_info::grams] = StackPusherHelper::gramsToBinaryString(10'000'000); - Expression const *sendrawmsgFlag{}; + auto memberAccess = to(¤tFunctionCall->expression()); + if (memberAccess == nullptr) { + return false; + } - Expression const* currentExpression = &_functionCall.expression(); - while (true) { - auto currentFunctionCall = to(currentExpression); - if (currentFunctionCall == nullptr) { - break; + if (memberAccess->memberName() == "flag") { + sendrawmsgFlag = currentFunctionCall->arguments()[0].get(); + currentExpression = &memberAccess->expression(); + } else if (memberAccess->memberName() == "value") { + exprs[TvmConst::int_msg_info::grams] = currentFunctionCall->arguments()[0].get(); + constParams.erase(TvmConst::int_msg_info::grams); + currentExpression = &memberAccess->expression(); + } else { + break; + } } - - auto memberAccess = to(¤tFunctionCall->expression()); - if (memberAccess == nullptr) { + auto memberValue = to(currentExpression); + if (memberValue == nullptr) { + return false; + } + exprs[TvmConst::int_msg_info::dest] = &memberValue->expression(); + const FunctionDefinition *fdef = getRemoteFunctionDefinition(memberValue); + if (fdef == nullptr) { return false; } - if (memberAccess->memberName() == "flag") { - sendrawmsgFlag = currentFunctionCall->arguments()[0].get(); - currentExpression = &memberAccess->expression(); - } else if (memberAccess->memberName() == "value") { - exprs[TvmConst::int_msg_info::grams] = currentFunctionCall->arguments()[0].get(); - constParams.erase(TvmConst::int_msg_info::grams); - currentExpression = &memberAccess->expression(); - } else { - break; + if (isCurrentResultNeeded()) { + cast_error(_functionCall, "Calls to remote contract do not return result."); } - } - auto memberValue = to(currentExpression); - if (memberValue == nullptr) { - return false; - } - exprs[TvmConst::int_msg_info::dest] = &memberValue->expression(); - const FunctionDefinition *fdef = getRemoteFunctionDefinition(memberValue); - if (fdef == nullptr) { - return false; - } - if (isCurrentResultNeeded()) { - cast_error(_functionCall, "Calls to remote contract do not return result."); + appendBody = [&](int builderSize) { + return EncodeFunctionParams{&m_pusher}.createMsgBodyAndAppendToBuilder2(arguments, + ReasonOfOutboundMessage::RemoteCallInternal, + fdef, builderSize); + }; } - auto pushBody = [&]() { - return encodeOutboundMessageBody(memberValue->memberName(), arguments, fdef->parameters(), - StackPusherHelper::ReasonOfOutboundMessage::RemoteCallInternal); - }; - if (sendrawmsgFlag) - m_pusher.sendIntMsg(exprs, constParams, pushBody, [&](){acceptExpr(sendrawmsgFlag);}); + m_pusher.sendIntMsg(exprs, constParams, appendBody, [&]() { acceptExpr(sendrawmsgFlag); }); else - m_pusher.sendIntMsg(exprs, constParams, pushBody, nullptr); + m_pusher.sendIntMsg(exprs, constParams, appendBody, nullptr); return true; } @@ -1111,19 +1065,17 @@ const FunctionDefinition *TVMExpressionCompiler::getRemoteFunctionDefinition(con auto expr = &memberAccess->expression(); if (isSuper(expr)) return nullptr; - if (auto ctype = to(getType(expr))) { - auto remoteContract = &ctype->contractDefinition(); - const string& fname = memberAccess->memberName(); - if (auto f = getFunction(remoteContract, fname)) { - if (!m_pusher.ctx().getLocalFunction(fname)) - m_pusher.ctx().m_remoteFunctions.insert(fname); - m_pusher.push( 0, ";; Remote call " + remoteContract->name() + "." + fname); - return f; - } - cast_error(*memberAccess, "Unsupported remote function call."); - } else { + auto ctype = to(getType(expr)); + if (!ctype) return nullptr; + Declaration const* decl = memberAccess->annotation().referencedDeclaration; + auto f = to(decl); + if (!f) { + cast_error(*memberAccess, "Unsupported remote function call."); } + ContractDefinition const& remoteContract = ctype->contractDefinition(); + m_pusher.push( 0, ";; Remote call " + remoteContract.name() + "." + f->name()); + return f; } bool TVMExpressionCompiler::checkForArrayMethods(FunctionCall const &_functionCall) { @@ -1473,29 +1425,20 @@ void TVMExpressionCompiler::visit2(FunctionCall const &_functionCall) { return; } - if (!checkAbiMethodCall(_functionCall) && - !checkForMappingOrCurrenciesMethods(_functionCall)) { - FunctionCallCompiler fcc(m_pusher, this); - fcc.compile(_functionCall); - } - - if (!isCurrentResultNeeded()) { - if (_functionCall.expression().annotation().type->category() == Type::Category::Function) { - auto ft = to(_functionCall.expression().annotation().type); - auto size = ft->returnParameterNames().size(); - if (size != 0) { - m_pusher.drop(size); - } - if (auto ma = to(&_functionCall.expression())) { - if (ma->expression().annotation().type->category() == Type::Category::TvmSlice) { - if (ma->memberName() == "decode") { - auto types = TypePointers{_functionCall.annotation().type}; - m_pusher.drop(types.size()); - } - } + if (checkAbiMethodCall(_functionCall) || + checkForMappingOrCurrenciesMethods(_functionCall)) { + if (!isCurrentResultNeeded()) { + if (auto t = to(_functionCall.annotation().type)) { + m_pusher.drop(t->components().size()); + } else { + m_pusher.drop(1); } } + return ; } + + FunctionCallCompiler fcc(m_pusher, this); + fcc.compile(_functionCall, isCurrentResultNeeded()); } void TVMExpressionCompiler::visit2(Conditional const &_conditional) { diff --git a/compiler/libsolidity/codegen/TVMExpressionCompiler.hpp b/compiler/libsolidity/codegen/TVMExpressionCompiler.hpp index e5831cd1..843e7c7c 100644 --- a/compiler/libsolidity/codegen/TVMExpressionCompiler.hpp +++ b/compiler/libsolidity/codegen/TVMExpressionCompiler.hpp @@ -74,19 +74,7 @@ class TVMExpressionCompiler { void visit2(IndexAccess const& indexAccess); bool checkAbiMethodCall(FunctionCall const& _functionCall); -public: - int encodeOutboundMessageBody2( - const string& name, - const ast_vec& arguments, - const ast_vec& parameters, - const StackPusherHelper::ReasonOfOutboundMessage reason); - protected: - int encodeOutboundMessageBody( - const string& name, - const ast_vec& arguments, - const ast_vec& parameters, - const StackPusherHelper::ReasonOfOutboundMessage reason); bool checkRemoteMethodCall(FunctionCall const& _functionCall); const FunctionDefinition* getRemoteFunctionDefinition(const MemberAccess* memberAccess); bool checkForArrayMethods(FunctionCall const& _functionCall); diff --git a/compiler/libsolidity/codegen/TVMFunctionCall.cpp b/compiler/libsolidity/codegen/TVMFunctionCall.cpp index 98a401fe..3327d91e 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCall.cpp +++ b/compiler/libsolidity/codegen/TVMFunctionCall.cpp @@ -25,6 +25,17 @@ using namespace solidity::frontend; +CallableDeclaration const * getCallableDeclaration(Expression const* expr) { + CallableDeclaration const * result = nullptr; + + if (auto identifier = to(expr)) + result = to(identifier->annotation().referencedDeclaration); + else if (auto member = dynamic_cast(expr)) + result = to(member->annotation().referencedDeclaration); + solAssert(result, "Failed to get CallableDeclaration."); + return result; +} + void FunctionCallCompiler::acceptExpr(const Expression *expr) { m_exprCompiler->compileNewExpr(expr); } @@ -45,53 +56,60 @@ void FunctionCallCompiler::structConstructorCall(FunctionCall const &_functionCa structCompiler.structConstructor(_functionCall.names()); // create struct and drop args } -void FunctionCallCompiler::compile(FunctionCall const &_functionCall) { - - if (checkNewExpression(_functionCall)) return; - if (checkTvmIntrinsic(_functionCall)) return; - if (checkAddressThis(_functionCall)) return; - if (checkSolidityUnits(_functionCall)) return; - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) { +void FunctionCallCompiler::compile(FunctionCall const &_functionCall, bool isCurrentResultNeeded) { + if (checkNewExpression(_functionCall)) { + } else if (checkTvmIntrinsic(_functionCall)){ + } else if (checkAddressThis(_functionCall)) { + } else if (checkSolidityUnits(_functionCall)) { + } else if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) { structConstructorCall(_functionCall); - return ; - } - if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) { + } else if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) { typeConversion(_functionCall); - return; - } - if (checkForIdentifier(_functionCall)) return; - - auto arguments = _functionCall.arguments(); - auto expr = &_functionCall.expression(); - if (auto ma = to(expr)) { - auto category = getType(&ma->expression())->category(); - auto ident = to(&ma->expression()); - if (checkForTvmSliceMethods(*ma, category, arguments, _functionCall)) return; - if (checkForTvmBuilderMethods(*ma, category, arguments)) return; - if (checkForStringMethods(*ma, arguments)) return; - if (category == Type::Category::Magic && ident != nullptr && ident->name() == "tvm") { - if (checkForTvmSendFunction(*ma, category, arguments)) return; - if (checkForTvmConfigParamFunction(*ma, category, arguments)) return; - if (checkForTvmFunction(*ma, category, arguments)) return; - if (checkForTvmDeployMethods(*ma, category, arguments, _functionCall)) return; - } else { - if (!isIn(ma->memberName(), "log", "transfer", "checkSign", "deployAndCallConstructor")) { // TODO delete log, checkSign - for (const auto &arg : arguments) { - acceptExpr(arg.get()); + } else if (checkForIdentifier(_functionCall)) { + } else { + auto arguments = _functionCall.arguments(); + auto expr = &_functionCall.expression(); + if (auto ma = to(expr)) { + auto category = getType(&ma->expression())->category(); + auto ident = to(&ma->expression()); + if (checkForTvmSliceMethods(*ma, category, arguments, _functionCall)) { + } else if (checkForTvmBuilderMethods(*ma, category, arguments)) { + } else if (checkForStringMethods(*ma, arguments)) { + } else if (category == Type::Category::Magic && ident != nullptr && ident->name() == "tvm") { + if (checkForTvmSendFunction(*ma, category, arguments)) { + } else if (checkForTvmConfigParamFunction(*ma, category, arguments)) { + } else if (checkForTvmFunction(*ma, category, arguments)) { + } else if (checkForTvmDeployMethods(*ma, category, arguments, _functionCall)) { + } else { + cast_error(_functionCall, "Unsupported function call"); + } + } else { + if (!isIn(ma->memberName(), "log", "transfer", "checkSign", + "deployAndCallConstructor")) { // TODO delete log, checkSign + for (const auto &arg : arguments) { + acceptExpr(arg.get()); + } + } + if (checkForSuper(*ma, category)) { + } else if (ma->expression().annotation().type->category() == Type::Category::Address) { + addressMethod(_functionCall); + } else if (checkForTvmCellMethods(*ma, category, arguments)) { + } else if (checkForMemberAccessTypeType(*ma, category)) { + } else if (checkForMsgFunction(*ma, category, arguments)) { + } else if (checkForTypeTypeMember(*ma, category)) { + } else { + cast_error(_functionCall, "Unsupported function call"); } } - if (checkForSuper(*ma, category)) return; - if (ma->expression().annotation().type->category() == Type::Category::Address) { - addressMethod(_functionCall); - return; - } - if (checkForTvmCellMethods(*ma, category, arguments)) return; - if (checkForMemberAccessTypeType(*ma, category)) return; - if (checkForMsgFunction(*ma, category, arguments)) return; - if (checkForTypeTypeMember(*ma, category)) return; } } - cast_error(_functionCall, "Unsupported function call"); + if (!isCurrentResultNeeded) { + if (auto t = to(_functionCall.annotation().type)) { + m_pusher.drop(t->components().size()); + } else { + m_pusher.drop(1); + } + } } bool FunctionCallCompiler::checkForSuper(MemberAccess const &_node, Type::Category) { @@ -170,6 +188,8 @@ void FunctionCallCompiler::loadTypeFromSlice(MemberAccess const &_node, TypePoin TypeInfo ti{type}; solAssert(ti.isNumeric, ""); m_pusher.push(+1, (ti.isSigned ? "LDI " : "LDU ") + toString(ti.numBits)); + } else if (category == Type::Category::Mapping) { + m_pusher.push(+1, "LDDICT"); } else { cast_error(_node, "Unsupported parameter type for decoding: " + type->toString()); } @@ -188,25 +208,25 @@ bool FunctionCallCompiler::checkForTvmDeployMethods(MemberAccess const &_node, T if (category != Type::Category::Magic || functionType->kind() != FunctionType::Kind::TVMDeploy) return false; - if(_node.memberName() == "buildStateInit") { + if (_node.memberName() == "buildStateInit") { pushArgs(); m_pusher.pushPrivateFunctionOrMacroCall(-2 + 1, "build_state_init_macro"); return true; } - if(_node.memberName() == "insertPubkey") { + if (_node.memberName() == "insertPubkey") { pushArgs(); m_pusher.pushPrivateFunctionOrMacroCall(-2 + 1, "insert_pubkey_macro"); return true; } - if(_node.memberName() == "deploy") { + if (_node.memberName() == "deploy") { pushArgs(); m_pusher.pushPrivateFunctionOrMacroCall(-4, "deploy_contract2_macro"); return true; } - if(_node.memberName() == "deployAndCallConstructor") { + if (_node.memberName() == "deployAndCallConstructor") { for (size_t i = 0; i < 4; i++) acceptExpr(arguments[i].get()); std::vector types; @@ -216,13 +236,37 @@ bool FunctionCallCompiler::checkForTvmDeployMethods(MemberAccess const &_node, T nodes.push_back(arguments[i].get()); } m_pusher.push(+1, "NEWC"); - StackPusherHelper::EncodePosition position{0}; - m_pusher.encodeParameters(types, nodes, [&](std::size_t index) { + // FIXME function id is a part of body + EncodePosition position{32, types}; + EncodeFunctionParams{&m_pusher}.encodeParameters(types, nodes, [&](std::size_t index) { acceptExpr(arguments[index + 4].get()); }, position); - m_pusher.pushPrivateFunctionOrMacroCall(-5, "deploy_contract_macro"); + m_pusher.pushInt(1); + m_pusher.pushPrivateFunctionOrMacroCall(-6, "deploy_contract_macro"); + return true; + } + + if(_node.memberName() == "deployAndCallConstructorWithFlag") { + for (size_t i = 0; i < 5; i++) + if (i != 3) + acceptExpr(arguments[i].get()); + std::vector types; + std::vector nodes; + for (size_t i = 5; i < arguments.size(); ++i) { + types.push_back(arguments[i]->annotation().type); + nodes.push_back(arguments[i].get()); + } + m_pusher.push(+1, "NEWC"); + // FIXME function id is a part of body + EncodePosition position{32, types}; + EncodeFunctionParams{&m_pusher}.encodeParameters(types, nodes, [&](std::size_t index) { + acceptExpr(arguments[index + 5].get()); + }, position); + acceptExpr(arguments[3].get()); + m_pusher.pushPrivateFunctionOrMacroCall(-6, "deploy_contract_macro"); return true; } + return false; } @@ -276,11 +320,7 @@ bool FunctionCallCompiler::checkForTvmSliceMethods(MemberAccess const &_node, Ty } if (_node.memberName() == "decodeFunctionParams") { const int saveStackSize = m_pusher.getStack().size(); - - Expression const* expr = arguments.at(0).get(); - auto identifier = to(expr); - Declaration const* declaration = identifier->annotation().referencedDeclaration; - auto functionDefinition = to(declaration); + auto functionDefinition = getCallableDeclaration(arguments.at(0).get()); const TVMExpressionCompiler::LValueInfo lValueInfo = m_exprCompiler->expandLValue(&_node.expression(),true, false, _node.expression().annotation().isLValue); @@ -303,10 +343,10 @@ bool FunctionCallCompiler::checkForTvmSliceMethods(MemberAccess const &_node, Ty std::string cmd = "LD"; cmd += (_node.memberName() == "loadSigned" ? "I" : "U"); if (auto bitsLiteral = to(arguments[0].get())) - m_pusher.push(+1, cmd + " " + bitsLiteral->value()); + m_pusher.push(-1 + 2, cmd + " " + bitsLiteral->value()); else { acceptExpr(arguments[0].get()); - m_pusher.push(-1+1, cmd + "X"); + m_pusher.push(-2 + 2, cmd + "X"); } m_exprCompiler->collectLValue(lValueInfo, true, false); return true; @@ -337,7 +377,7 @@ void FunctionCallCompiler::store(MemberAccess const &_node, TypePointer type, bo m_pusher.untuple(members.size()); m_pusher.reverse(members.size(), 0); m_pusher.blockSwap(1, members.size()); - for(auto member: members) + for (const auto& member : members) store(_node, member->type(), false); } else if (auto arrayType = to(type)) { if (!reverse) @@ -367,7 +407,7 @@ bool FunctionCallCompiler::checkForTvmBuilderMethods(MemberAccess const &_node, acceptExpr(arguments[0].get()); m_pusher.push(-1, "STBREFR"); } else if (_node.memberName() == "store") { - for (auto argument: arguments) { + for (const auto& argument: arguments) { acceptExpr(argument.get()); store(_node, argument->annotation().type); } @@ -485,7 +525,7 @@ void FunctionCallCompiler::addressMethod(FunctionCall const &_functionCall) { if (!_functionCall.names().empty()) { std::map exprs; std::map constParams{{TvmConst::int_msg_info::ihr_disabled, "1"}, {TvmConst::int_msg_info::bounce, "1"}}; - std::function pushBody; + std::function appendBody; std::function pushSendrawmsgFlag; exprs[TvmConst::int_msg_info::dest] = &_node->expression(); @@ -505,9 +545,11 @@ void FunctionCallCompiler::addressMethod(FunctionCall const &_functionCall) { }; break; case str2int("body"): - pushBody = [e = arguments[arg], this](){ + appendBody = [e = arguments[arg], this](int /*size*/){ + m_pusher.stones(1); TVMExpressionCompiler{m_pusher}.compileNewExpr(e.get()); - return -1; + m_pusher.push(-1, "STREFR"); + return false; }; break; case str2int("currencies"): @@ -515,7 +557,7 @@ void FunctionCallCompiler::addressMethod(FunctionCall const &_functionCall) { break; } } - m_pusher.sendIntMsg(exprs, constParams, pushBody, pushSendrawmsgFlag); + m_pusher.sendIntMsg(exprs, constParams, appendBody, pushSendrawmsgFlag); } else { if (arguments.size() == 3) { const std::map exprs{ @@ -537,12 +579,11 @@ void FunctionCallCompiler::addressMethod(FunctionCall const &_functionCall) { m_pusher.sendIntMsg( exprs, {{TvmConst::int_msg_info::ihr_disabled, "1"}}, - [&]() { + [&](int /*size*/) { + m_pusher.stones(1); acceptExpr(arguments[3].get()); - m_pusher.push(0, "CTOS"); - m_pusher.push(+1, "NEWC"); - m_pusher.push(-1, "STSLICE"); - return -1; + m_pusher.push(-1, "STREFR"); + return false; }, [&]() { acceptExpr(arguments[2].get()); }); } else if (arguments.size() == 1) { @@ -849,38 +890,6 @@ bool FunctionCallCompiler::checkForTvmFunction(const MemberAccess &_node, Type:: m_pusher.push(0, "ACCEPT"); return true; } - if (_node.memberName() == "transfer") { // tvm.transfer - if (arguments.size() == 4) { - const std::map exprs { - {TvmConst::int_msg_info::grams, arguments[1].get()}, - {TvmConst::int_msg_info::bounce, arguments[2].get()}, - {TvmConst::int_msg_info::dest, arguments[0].get()}, - }; - m_pusher.sendIntMsg( - exprs, - {{TvmConst::int_msg_info::ihr_disabled, "1"}}, - nullptr, - [&](){acceptExpr(arguments[3].get());}); - } else { - const std::map exprs { - {TvmConst::int_msg_info::grams, arguments[1].get()}, - {TvmConst::int_msg_info::bounce, arguments[2].get()}, - {TvmConst::int_msg_info::dest, arguments[0].get()}, - }; - m_pusher.sendIntMsg( - exprs, - {{TvmConst::int_msg_info::ihr_disabled, "1"}}, - [&](){ - acceptExpr(arguments[4].get()); - m_pusher.push(0, "CTOS"); - m_pusher.push(+1, "NEWC"); - m_pusher.push(-1, "STSLICE"); - return -1; - }, - [&](){acceptExpr(arguments[3].get());}); - } - return true; - } if (_node.memberName() == "hash") { // tvm.hash pushArgs(); m_pusher.push(0, "HASHCU"); @@ -944,10 +953,9 @@ bool FunctionCallCompiler::checkForTvmFunction(const MemberAccess &_node, Type:: return true; } if (_node.memberName() == "functionId") { // tvm.functionId - Expression const* expr = arguments.at(0).get(); - auto identifier = to(expr); - Declaration const* declaration = identifier->annotation().referencedDeclaration; - m_pusher.push(+1, "PUSHINT $" + declaration->name() + "$"); + auto callDef = getCallableDeclaration(arguments.at(0).get()); + EncodeFunctionParams encoder(&m_pusher); + m_pusher.push(+1, "PUSHINT " + to_string(encoder.calculateFunctionID(callDef) & 0x7fffffff)); return true; } return false; diff --git a/compiler/libsolidity/codegen/TVMFunctionCall.hpp b/compiler/libsolidity/codegen/TVMFunctionCall.hpp index 6303e82e..05b9f3c4 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCall.hpp +++ b/compiler/libsolidity/codegen/TVMFunctionCall.hpp @@ -34,7 +34,7 @@ class FunctionCallCompiler { public: FunctionCallCompiler(StackPusherHelper& m_pusher, TVMExpressionCompiler* exprCompiler); void structConstructorCall(FunctionCall const& _functionCall); - void compile(FunctionCall const& _functionCall); + void compile(FunctionCall const& _functionCall, bool isCurrentResultNeeded); protected: bool checkForSuper(MemberAccess const& _node, Type::Category); diff --git a/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp b/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp index 121ad4a6..84f2632d 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp +++ b/compiler/libsolidity/codegen/TVMFunctionCompiler.cpp @@ -249,6 +249,7 @@ IFELSE void TVMFunctionCompiler::generateTvmGetter(FunctionDefinition const *_function) { m_pusher.generateGlobl(_function->name(), true); + decodeFunctionParamsAndLocateVars(); visitFunctionWithModifiers(); m_pusher.push(0, " "); } @@ -320,9 +321,56 @@ void TVMFunctionCompiler::generateOnTickTock() { m_pusher.push(0, " "); } +void TVMFunctionCompiler::decodeFunctionParamsAndLocateVars() { + // decode function params + // stack: transaction_id arguments-in-slice + m_pusher.push(+1, ""); // arguments-in-slice + DecodeFunctionParams{&m_pusher}.decodeParameters(m_function->parameters()); + // stack: transaction_id arguments... + m_pusher.getStack().change(-static_cast(m_function->parameters().size())); + for (const ASTPointer& variable: m_function->parameters()) { + auto name = variable->name(); + m_pusher.push(0, string(";; param: ") + name); + m_pusher.getStack().add(variable.get(), true); + } +} + void TVMFunctionCompiler::generatePublicFunction() { m_pusher.generateGlobl(m_function->name(), m_function->isPublic()); + + // c4_to_c7 if need + if (m_function->stateMutability() != StateMutability::Pure) { + m_pusher.pushLines(R"( +GETGLOB 1 +ISNULL +)"); + m_pusher.startContinuation(); + m_pusher.pushPrivateFunctionOrMacroCall(0, "c4_to_c7"); + m_pusher.endContinuation(); + m_pusher.pushLines("IF"); + } + + decodeFunctionParamsAndLocateVars(); + visitFunctionWithModifiers(); + + // c7_to_c4 if need + solAssert(m_pusher.getStack().size() == 0, ""); + if (m_function->stateMutability() == StateMutability::NonPayable || + m_function->stateMutability() == StateMutability::Payable) { + m_pusher.pushPrivateFunctionOrMacroCall(0, "c7_to_c4"); + } else { + m_pusher.push(0, "EQINT -1"); // is it ext msg? + m_pusher.startContinuation(); + m_pusher.pushPrivateFunctionOrMacroCall(0, "c7_to_c4"); + m_pusher.endContinuation(); + m_pusher.push(0, "IF"); + } + + // set flag meaning function is called + m_pusher.push(0, "TRUE"); + m_pusher.push(0, "SETGLOB 7"); + m_pusher.push(0, " "); } @@ -346,40 +394,30 @@ void TVMFunctionCompiler::generatePrivateFunctionWithoutHeader() { } void TVMFunctionCompiler::emitOnPublicFunctionReturn() { - const auto& params = m_function->returnParameters(); - auto count = params.size(); - if (count == 0) { + const std::vector>& params = m_function->returnParameters(); + if (params.empty()) { return; } - m_pusher.push( 0, ";; emitting " + toString(count) + " value(s)"); - - std::vector types; - std::vector nodes; - for (const auto & param : params) { - types.push_back(param->annotation().type); - nodes.push_back(param.get()); - } + m_pusher.push( 0, ";; emitting " + toString(params.size()) + " value(s)"); const int prevStackSize = m_pusher.getStack().size(); - auto pushBody = [&]() { - return m_pusher.encodeFunctionAndParams( - TVMCompilerContext::getFunctionExternalName(m_function), - types, - nodes, + auto appendBody = [&](int builderSize) { + return EncodeFunctionParams{&m_pusher}.createMsgBodyAndAppendToBuilder( [&](size_t idx) { int pos = (m_pusher.getStack().size() - prevStackSize) + (static_cast(params.size()) - static_cast(idx) - 1); m_pusher.pushS(pos); }, - StackPusherHelper::ReasonOfOutboundMessage::FunctionReturnExternal + ReasonOfOutboundMessage::FunctionReturnExternal, + m_function, + true, + builderSize ); }; - - - sendExternalMessage(pushBody, nullptr); + sendExternalMessage(appendBody, nullptr); } -void TVMFunctionCompiler::sendExternalMessage(const std::function &pushBody, Expression const* destAddr) { +void TVMFunctionCompiler::sendExternalMessage(const std::function &appendBody, Expression const* destAddr) { // stack: builder with encoded params if (m_pusher.ctx().haveSetDestAddr()) { if (destAddr == nullptr) { @@ -395,9 +433,9 @@ IF } else { acceptExpr(destAddr, true); } - m_pusher.sendMsg({TvmConst::ext_msg_info::dest}, {}, pushBody, nullptr, false); + m_pusher.sendMsg({TvmConst::ext_msg_info::dest}, {}, appendBody, nullptr, false); } else { - m_pusher.sendMsg({}, {}, pushBody, nullptr, false); + m_pusher.sendMsg({}, {}, appendBody, nullptr, false); } } @@ -502,14 +540,15 @@ void TVMFunctionCompiler::visitFunctionAfterModifiers() { if (m_isPublic) { // emit function result // stack: transaction_id return-params... + const int retQty = m_function->returnParameters().size(); + const int targetStackSize = m_pusher.getStack().size() - retQty; bool dontEmitReturn = getFunction(m_pusher.ctx().getContract(), "tvm_dont_emit_events_on_return") != nullptr; if (!dontEmitReturn) { emitOnPublicFunctionReturn(); } - // drop function result - const int retQty = m_function->returnParameters().size(); - m_pusher.drop(retQty); + // drop function result if still on stack + m_pusher.drop(m_pusher.getStack().size() - targetStackSize); } else { if (!functionModifiers().empty()) { if (!m_function->returnParameters().empty()) { @@ -527,31 +566,7 @@ void TVMFunctionCompiler::visitFunctionWithModifiers() { solAssert(m_startStackSize >= 0, ""); if (m_currentModifier == 0) { - if (m_isPublic) { - // c4_to_c7 if need - if (m_function->stateMutability() != StateMutability::Pure) { - m_pusher.pushLines(R"( -GETGLOB 1 -ISNULL -)"); - m_pusher.startContinuation(); - m_pusher.pushPrivateFunctionOrMacroCall(0, "c4_to_c7"); - m_pusher.endContinuation(); - m_pusher.pushLines("IF"); - } - - // decode function params - // stack: transaction_id arguments-in-slice - m_pusher.push(+1, ""); // arguments-in-slice - DecodeFunctionParams{&m_pusher}.decodeParameters(m_function->parameters()); - // stack: transaction_id arguments... - m_pusher.getStack().change(-static_cast(m_function->parameters().size())); - for (const ASTPointer& variable: m_function->parameters()) { - auto name = variable->name(); - m_pusher.push(0, string(";; param: ") + name); - m_pusher.getStack().add(variable.get(), true); - } - } else { + if (!m_isPublic) { // function params are allocated solAssert(m_pusher.getStack().size() >= static_cast(m_function->parameters().size()), ""); } @@ -583,24 +598,7 @@ ISNULL if (m_currentModifier == 0) { - if (m_isPublic) { - // c7_to_c4 if need - solAssert(m_pusher.getStack().size() == 0, ""); - if (m_function->stateMutability() == StateMutability::NonPayable || - m_function->stateMutability() == StateMutability::Payable) { - m_pusher.pushPrivateFunctionOrMacroCall(0, "c7_to_c4"); - } else { - m_pusher.push(0, "EQINT -1"); // is it ext msg? - m_pusher.startContinuation(); - m_pusher.pushPrivateFunctionOrMacroCall(0, "c7_to_c4"); - m_pusher.endContinuation(); - m_pusher.push(0, "IF"); - } - - // set flag meaning function is called - m_pusher.push(0, "TRUE"); - m_pusher.push(0, "SETGLOB 7"); - } else { + if (!m_isPublic) { if (!functionModifiers().empty()) { if (!m_function->returnParameters().empty()) { const int retQty = m_function->returnParameters().size(); @@ -1135,21 +1133,23 @@ bool TVMFunctionCompiler::visit(EmitStatement const &_emit) { solAssert(eventName, ""); string name = eventName->name(); m_pusher.push(0, ";; emit " + name); - auto event = m_pusher.ctx().getEvent(name); - solAssert(event, ""); TVMExpressionCompiler ec(m_pusher); - auto pushBody = [&]() { - return ec.encodeOutboundMessageBody2( - name, + auto identifier = to(&eventCall->expression()); + Declaration const * decl = identifier->annotation().referencedDeclaration; + auto eventDef = to(decl); + solAssert(eventDef, "Event Declaration was not found"); + auto appendBody = [&](int builderSize) { + return EncodeFunctionParams{&m_pusher}.createMsgBodyAndAppendToBuilder2( eventCall->arguments(), - event->parameterList().parameters(), - StackPusherHelper::ReasonOfOutboundMessage::EmitEventExternal); + ReasonOfOutboundMessage::EmitEventExternal, + eventDef, + builderSize); }; if (auto externalAddress = _emit.externalAddress()) { - sendExternalMessage(pushBody, externalAddress.get()); + sendExternalMessage(appendBody, externalAddress.get()); } else - sendExternalMessage(pushBody, nullptr); + sendExternalMessage(appendBody, nullptr); return false; } @@ -1458,6 +1458,8 @@ SETGLOB 9 s += R"( PUSHCONT { PUSH S1 + LDSLICE 32 + NIP CALL $:onBounce$ } IFJMP @@ -1546,4 +1548,3 @@ bool TVMFunctionCompiler::visit(PlaceholderStatement const &) { tvm.visitFunctionWithModifiers(); return false; } - diff --git a/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp b/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp index afbc39a3..98002a31 100644 --- a/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp +++ b/compiler/libsolidity/codegen/TVMFunctionCompiler.hpp @@ -71,6 +71,8 @@ class TVMFunctionCompiler: public ASTConstVisitor, private boost::noncopyable void generatePublicFunction(); void generateTvmGetter(FunctionDefinition const* _function); +public: + void decodeFunctionParamsAndLocateVars(); protected: ast_vec functionModifiers(); void endContinuation2(const bool doDrop); @@ -81,14 +83,16 @@ class TVMFunctionCompiler: public ASTConstVisitor, private boost::noncopyable static LocationReturn notNeedsPushContWhenInlining(Block const& _block); CodeLines loadFromC4(); void emitOnPublicFunctionReturn(); - void sendExternalMessage(const std::function &pushBody, Expression const* destAddr); + void sendExternalMessage(const std::function &appendBody, Expression const* destAddr); void pushReturnParameters(const ast_vec& returnParameters); void acceptExpr(const Expression* expr, const bool isResultNeeded = true); void visitModifierOrFunctionBlock(Block const& body, bool isFunc); void visitFunctionAfterModifiers(); +public: void visitFunctionWithModifiers(); +private: void visitForOrWhileCondiction(const ContInfo& ci, const ControlFlowInfo& info, Expression const* condition); bool visitNode(ASTNode const&) override { solAssert(false, "Internal error: unreachable"); } diff --git a/compiler/libsolidity/codegen/TVMIntrinsics.cpp b/compiler/libsolidity/codegen/TVMIntrinsics.cpp index dd5e6b6c..f1862a68 100644 --- a/compiler/libsolidity/codegen/TVMIntrinsics.cpp +++ b/compiler/libsolidity/codegen/TVMIntrinsics.cpp @@ -562,7 +562,6 @@ bool IntrinsicsCompiler::checkTvmIntrinsic(FunctionCall const &_functionCall) { } m_pusher.pushPrivateFunctionOrMacroCall(-4, "deploy_contract2_macro"); } else { -// pushLog("insr"); auto identifierAnnotation = to(&_functionCall.expression().annotation()); auto functionDefinition = to(identifierAnnotation->referencedDeclaration); std::vector types; @@ -571,13 +570,14 @@ bool IntrinsicsCompiler::checkTvmIntrinsic(FunctionCall const &_functionCall) { types.push_back(functionDefinition->parameters()[i]->annotation().type); nodes.push_back(arguments[i].get()); } + // DELETE ME m_pusher.push(+1, "NEWC"); - StackPusherHelper::EncodePosition position{0}; - m_pusher.encodeParameters(types, nodes, [&](std::size_t index) { + EncodePosition position{0, types}; + EncodeFunctionParams{&m_pusher}.encodeParameters(types, nodes, [&](std::size_t index) { acceptExpr(arguments[index + 4].get()); }, position); -// pushLog("insr1"); - m_pusher.pushPrivateFunctionOrMacroCall(-5, "deploy_contract_macro"); + m_pusher.pushInt(1); + m_pusher.pushPrivateFunctionOrMacroCall(-6, "deploy_contract_macro"); } return true; } @@ -1045,13 +1045,27 @@ IF } if (iname == "tvm_migratePubkey") { m_pusher.pushLines(R"( -PUSHINT 0 -GETGLOB 2 -PUSHINT 64 -DICTUGET -THROWIFNOT 62 -PLDU 256 -SETGLOB 2 +DEPTH +TUPLEVAR +SETGLOB 8 +PUSHCONT { + PUSHINT 0 + GETGLOB 2 + PUSHINT 64 + DICTUGET + THROWIFNOT 62 + PLDU 256 + SETGLOB 2 +} +PUSHCONT { + PRINTSTR hello + DROP2 +} +TRY +GETGLOB 8 +DUP +TLEN +UNTUPLEVAR )"); return true; } diff --git a/compiler/libsolidity/codegen/TVMOptimizations.cpp b/compiler/libsolidity/codegen/TVMOptimizations.cpp index a837435e..fc84ddfd 100644 --- a/compiler/libsolidity/codegen/TVMOptimizations.cpp +++ b/compiler/libsolidity/codegen/TVMOptimizations.cpp @@ -17,6 +17,7 @@ */ #include "TVMOptimizations.hpp" +#include "TVMPusher.hpp" #include namespace solidity::frontend { @@ -121,19 +122,24 @@ struct TVMOptimizer { solAssert(false, ""); } - int get_push_index() const { - solAssert(is_PUSH(), ""); - if (is_DUP()) return 0; + int get_index() const { string s = rest(); + solAssert(isIn(s.at(0), 's', 'S'), ""); s.erase(s.begin()); // skipping char S return atoi(s.c_str()); } + int get_push_index() const { + solAssert(is_PUSH(), ""); + if (is_DUP()) return 0; + return get_index(); + } + std::pair get_push2_indexes() const { solAssert(is("PUSH2"), ""); std::string target = rest(); std::smatch sm; - std::regex re1("[S|s](\\d+),\\s*[S|s](\\d+)"); + std::regex re1(R"([S|s](\d+),\s*[S|s](\d+))"); std::regex_search(target, sm, re1); int si = std::stoi(sm[1]); @@ -433,7 +439,9 @@ struct TVMOptimizer { return Result::Replace(2, make_DROP(q)); } if (cmd1.is_simple_command_ && cmd1.inputs_count_ == 0 && cmd1.outputs_count_ == 1 && cmd2.is_NIP()) { - return Result::Replace(2, make_DROP(1), cmd1.without_prefix()); + std::vector dropOpcodes = make_DROP(1); + dropOpcodes.push_back(cmd1.without_prefix()); + return Result(true, 2, dropOpcodes); } if (cmd1.is_NIP() && cmd2.is_drop_kind()) { return Result::Replace(2, make_DROP(1 + cmd2.get_drop_index())); @@ -452,7 +460,6 @@ struct TVMOptimizer { i = next_command_line(i); } if (total > 1) { - // TODO: consider (total > 15) case return Result::Replace(n, make_DROP(total)); } } @@ -539,9 +546,108 @@ struct TVMOptimizer { cmd2.is("THROWIFNOT")) { return Result::Replace(2, "THROWIFNOT " + cmd2.rest()); } + if (cmd1.is("PUSH")) { + // PUSH Sx + // XCHG n + // BLKDROP n + int pushIndex = cmd1.get_index(); + if (cmd2.is("XCHG")) { + int xghIndex = cmd2.get_index(); + if (cmd3.is_drop_kind()) { + int dropedQty = cmd3.get_drop_index(); + if (xghIndex == dropedQty && dropedQty <= 15) { + int i = std::min(pushIndex, xghIndex - 1); + int j = std::max(pushIndex, xghIndex - 1); + if (i != j) { + if (pushIndex + 1 < dropedQty) { + std::vector opcodes{"XCHG S" + toString(i) + ", S" + toString(j)}; + std::vector dropOpcodes = make_DROP(dropedQty - 1); + opcodes.insert(opcodes.end(), dropOpcodes.begin(), dropOpcodes.end()); + return Result(true, 3, opcodes); + } + } else { + return Result::Replace(3, make_DROP(dropedQty - 1)); + } + } + } + } + } + if (cmd1.is("ROT") && cmd2.is("ROTREV")) { + return Result::Replace(2); + } + if (cmd1.is("ROTREV") && cmd2.is("ROT")) { + return Result::Replace(2); + } + if (cmd1.is("PUSHINT") && cmd2.is("STZEROES") && cmd3.is("STSLICECONST") && cmd3.rest() == "0") { + return Result::Replace(3, "PUSHINT " + toString(cmd1.fetch_int() + 1), "STZEROES"); + } + + if (cmd1.is("PUSHSLICE") && + cmd2.is("NEWC") && + cmd3.is("STSLICE") && + cmd4.is("STSLICECONST")) { + std::vector opcodes = unitSlices(cmd1.rest(), cmd4.rest()); + opcodes.emplace_back("NEWC"); + opcodes.emplace_back("STSLICE"); + return Result(true, 4, opcodes); + } + if (cmd1.is("PUSHSLICE") && + cmd2.is("STSLICER") && + cmd3.is("STSLICECONST")) { + std::vector opcodes = unitSlices(cmd1.rest(), cmd3.rest()); + opcodes.emplace_back("STSLICER"); + return Result(true, 3, opcodes); + } + return Result(false); } + std::string toBitString(const std::string& slice) const { + std::string bitString; + if (slice.at(0) == 'x') { + for (std::size_t i = 1; i < slice.size(); ++i) { + if (i + 2 == slice.size() && slice[i + 1] == '_') { + size_t pos{}; + int value = std::stoi(slice.substr(i, 1), &pos, 16); + solAssert(pos == 1, ""); + int bitLen = 4; + while (true) { + bool isOne = value % 2 == 1; + --bitLen; + value /= 2; + if (isOne) { + break; + } + } + StackPusherHelper::addBinaryNumberToString(bitString, value, bitLen); + break; + } + size_t pos{}; + int value = std::stoi(slice.substr(i, 1), &pos, 16); + solAssert(pos == 1, ""); + StackPusherHelper::addBinaryNumberToString(bitString, value, 4); + } + } else { + if (isIn(slice, "0", "1")) { + return slice; + } + solAssert(false, ""); + } + return bitString; + } + + std::vector unitSlices(const std::string& sliceA, const std::string& sliceB) const { + const std::string& bitString = toBitString(sliceA) + toBitString(sliceB); + std::vector opcodes; + for (int i = 0; i < static_cast(bitString.length()); i += 4 * TvmConst::MaxPushSliceLength) { + opcodes.push_back(bitString.substr(i, std::min(4 * TvmConst::MaxPushSliceLength, static_cast(bitString.length()) - i))); + } + for (std::string& opcode : opcodes) { + opcode = "PUSHSLICE x" + StackPusherHelper::binaryStringToSlice(opcode); + } + return opcodes; + } + bool try_simulate(int i, int stack_size, int& remove_count, vector& commands) const { if (!valid(i)) return false; @@ -608,8 +714,10 @@ struct TVMOptimizer { if (c.is_drop_kind()) { int n = c.get_drop_index(); if (stack_size <= n) { - if (n > 1) - commands.push_back(make_DROP(n - 1)); + if (n > 1) { + std::vector dropOpcodes = make_DROP(n - 1); + commands.insert(commands.end(), dropOpcodes.begin(), dropOpcodes.end()); + } remove_count++; break; } else { @@ -680,12 +788,12 @@ struct TVMOptimizer { return Result(false); } - static string make_DROP(int n) { + static std::vector make_DROP(int n) { solAssert(n > 0, ""); - if (n == 1) return "DROP"; - if (n == 2) return "DROP2"; - // TODO: use DROPX for n > 15 - return "BLKDROP " + toString(n); + if (n == 1) return {"DROP"}; + if (n == 2) return {"DROP2"}; + if (n <= 15) return {"BLKDROP " + toString(n)}; + return {"PUSHINT " + toString(n), "DROPX"}; } static string make_PUSH(int n) { diff --git a/compiler/libsolidity/codegen/TVMPusher.cpp b/compiler/libsolidity/codegen/TVMPusher.cpp index 99286539..cd329901 100644 --- a/compiler/libsolidity/codegen/TVMPusher.cpp +++ b/compiler/libsolidity/codegen/TVMPusher.cpp @@ -19,16 +19,17 @@ #include "TVMPusher.hpp" #include "TVMContractCompiler.hpp" #include "TVMExpressionCompiler.hpp" +#include "TVMABI.hpp" using namespace solidity::frontend; StackPusherHelper::StackPusherHelper(const TVMCompilerContext *ctx, const int stackSize) : m_ctx(ctx), m_structCompiler{new StructCompiler{this, - ctx->notConstantStateVariables(), - 256 + (m_ctx->storeTimestampInC4()? 64 : 0) + 1, // pubkey + timestamp + constructor_flag - 1, - true}} { + ctx->notConstantStateVariables(), + 256 + (m_ctx->storeTimestampInC4()? 64 : 0) + 1, // pubkey + timestamp + constructor_flag + 1, + true}} { m_stack.change(stackSize); } @@ -721,90 +722,6 @@ void StackPusherHelper::ensureValueFitsType(const ElementaryTypeNameToken &typeN } } -void StackPusherHelper::encodeParameter(Type const *type, StackPusherHelper::EncodePosition &position, - const std::function &pushParam, ASTNode const *node) { - // stack: builder... - if (auto structType = to(type)) { - pushParam(); // builder... struct - encodeStruct(structType, node, position); // stack: builder... - } else { - if (position.needNewCell(type)) { - push(+1, "NEWC"); - } - - if (isIntegralType(type) || isAddressOrContractType(type)) { - pushParam(); - push(-1, storeIntegralOrAddress(type, true)); - } else if (auto arrayType = to(type)) { - if (arrayType->isByteArray()) { - pushParam(); - push(-1, "STREFR"); - } else { - pushParam(); - // builder array - push(-1 + 2, "UNPAIR"); // builder size dict - exchange(0, 2); // dict size builder - push(-1, "STU 32"); // dict builder - push(-1, "STDICT"); // builder - } - } else if (to(type)) { - pushParam(); - push(-1, "STREFR"); - } else if (to(type)) { - pushParam(); - push(0, "SWAP"); - push(-1, "STDICT"); - } else { - cast_error(*node, "Unsupported type for encoding: " + type->toString()); - } - } -} - -void -StackPusherHelper::encodeParameters(const std::vector &types, const std::vector &nodes, - const std::function &pushParam, - StackPusherHelper::EncodePosition &position) { - // builder must be situated on top stack - solAssert(types.size() == nodes.size(), ""); - for (size_t idx = 0; idx < types.size(); idx++) { - auto type = types[idx]; - encodeParameter(type, position, [&](){pushParam(idx);}, nodes[idx]); - } - for (int idx = 0; idx < position.countOfCreatedBuilders(); idx++) { - push(-1, "STBREFR"); - } -} - -int StackPusherHelper::encodeFunctionAndParams(const string &functionName, const std::vector &types, - const std::vector &nodes, - const std::function &pushParam, - const StackPusherHelper::ReasonOfOutboundMessage &reason) { - push(+1, "PUSHINT $" + functionName + "$"); - switch (reason) { - case ReasonOfOutboundMessage::FunctionReturnExternal: - push(+1, "PUSHINT " + to_string(0x80000000)); - push(-1, "OR"); - break; - - case ReasonOfOutboundMessage::EmitEventExternal: - push(+1, "PUSHINT " + to_string(0x7fffffff)); - push(-1, "AND"); - break; - - default: - break; - } - push(+1, "NEWC"); - push(-1, "STU 32"); - EncodePosition position{32}; - - encodeParameters(types, nodes, pushParam, position); - - if (position.countOfCreatedBuilders() == 0) - return TvmConst::CellBitLength - position.restBits(); - return -1; -} - void StackPusherHelper::prepareKeyForDictOperations(Type const *key) { // stack: key dict if (isStringOrStringLiteralOrBytes(key)) { @@ -826,8 +743,8 @@ StackPusherHelper::int_msg_info(const std::set &isParamOnStack, const std:: const std::vector zeroes {1, 1, 1, 2, 2, - 4, 1, 4, 4, - 64, 32}; + 4, 1, 4, 4, + 64, 32}; std::string bitString = "0"; int maxBitStringSize = 0; push(+1, "NEWC"); @@ -876,7 +793,7 @@ StackPusherHelper::ext_msg_info(const set &isParamOnStack) { // created_lt:uint64 created_at:uint32 = CommonMsgInfo; const std::vector zeroes {2, 2, - 64, 32}; + 64, 32}; std::string bitString = "11"; int maxBitStringSize = 0; push(+1, "NEWC"); @@ -948,65 +865,38 @@ void StackPusherHelper::sendrawmsg() { } void StackPusherHelper::sendIntMsg(const std::map &exprs, - const std::map &constParams, - const std::function &pushBody, - const std::function &pushSendrawmsgFlag) { + const std::map &constParams, + const std::function &appendBody, + const std::function &pushSendrawmsgFlag) { std::set isParamOnStack; for (auto &[param, expr] : exprs | boost::adaptors::reversed) { isParamOnStack.insert(param); TVMExpressionCompiler{*this}.compileNewExpr(expr); } - sendMsg(isParamOnStack, constParams, pushBody, pushSendrawmsgFlag, true); + sendMsg(isParamOnStack, constParams, appendBody, pushSendrawmsgFlag, true); } void StackPusherHelper::sendMsg(const std::set& isParamOnStack, - const std::map &constParams, - const std::function &pushBody, - const std::function &pushSendrawmsgFlag, - bool isInternalMessage ) { + const std::map &constParams, + const std::function &appendBody, + const std::function &pushSendrawmsgFlag, + bool isInternalMessage ) { std::string bitString; - int builderSize; + int msgInfoSize; if (isInternalMessage) { - std::tie(bitString, builderSize) = int_msg_info(isParamOnStack, constParams); + std::tie(bitString, msgInfoSize) = int_msg_info(isParamOnStack, constParams); } else { - std::tie(bitString, builderSize) = ext_msg_info(isParamOnStack); + std::tie(bitString, msgInfoSize) = ext_msg_info(isParamOnStack); } // stack: builder - if (pushBody) { + if (appendBody) { appendToBuilder(bitString + "0"); // there is no StateInit - ++builderSize; - - int bodySize = pushBody(); - // stack: builder body - exchange(0, 1); // stack: body builder - if (bodySize == -1) { - // check that brembits(builder) > sbits(body) - pushLines(R"( -; merge body and builder -PUSH S1 -BBITS -PUSH S1 -BREMBITS -GREATER -PUSHCONT { - STSLICECONST 1 - STBREF -} -PUSHCONT { - STSLICECONST 0 - STB -} -IFELSE -)"); - push(-2 + 1, ""); - } else if (bodySize + builderSize <= TvmConst::CellBitLength) { - stzeroes(1); - push(-1, "STB"); - } else { - stones(1); - push(-1, "STBREF"); - } + ++msgInfoSize; + + // stack: values... builder + appendBody(msgInfoSize); + // stack: builder body or builder-with-body } else { appendToBuilder(bitString + "00"); // there is no StateInit and no body } @@ -1031,8 +921,6 @@ CodeLines solidity::frontend::switchSelectorIfNeed(FunctionDefinition const *f) return code; } -TVMStack::TVMStack() : m_size(0) {} - int TVMStack::size() const { return m_size; } @@ -1066,7 +954,7 @@ int TVMStack::getStackSize(Declaration const *name) const { void TVMStack::ensureSize(int savedStackSize, const string &location) const { solAssert(savedStackSize == m_size, "stack: " + toString(savedStackSize) - + " vs " + toString(m_size) + " at " + location); + + " vs " + toString(m_size) + " at " + location); } string CodeLines::str(const string &indent) const { @@ -1116,12 +1004,6 @@ void CodeLines::append(const CodeLines &oth) { } } -void TVMCompilerContext::addEvent(EventDefinition const *event) { - std::string name = event->name(); - solAssert(m_events.count(name) == 0, "Duplicate event " + name); - m_events[name] = event; -} - void TVMCompilerContext::addFunction(FunctionDefinition const *_function) { if (!_function->isConstructor()) { string name = functionName(_function); @@ -1132,19 +1014,21 @@ void TVMCompilerContext::addFunction(FunctionDefinition const *_function) { void TVMCompilerContext::initMembers(ContractDefinition const *contract) { solAssert(!m_contract, ""); m_contract = contract; - for (ContractDefinition const* c : getContractsChain(contract)) { - for (EventDefinition const *event : c->events()) - addEvent(event); - } for (const auto &pair : getContractFunctionPairs(contract)) { m_function2contract.insert(pair); } for (ContractDefinition const* base : contract->annotation().linearizedBaseContracts) { for (FunctionDefinition const* f : base->definedFunctions()) { - ignoreIntOverflow |= f->name() == "tvm_ignore_integer_overflow"; // delete check override, check pragma - m_haveSetDestAddr |= - f->name() == "tvm_set_ext_dest_address"; // delete check override, check tvm.set_ext_dest_address + ignoreIntOverflow |= f->name() == "tvm_ignore_integer_overflow"; + m_haveSetDestAddr |= f->name() == "tvm_set_ext_dest_address"; + if (f->name() == "offchainConstructor") { + if (m_haveOffChainConstructor) { + cast_error(*f, "This function can not be overrived/overloaded."); + } else { + m_haveOffChainConstructor = true; + } + } haveFallback |= f->isFallback(); haveOnBounce |= f->isOnBounce(); haveReceive |= f->isReceive(); @@ -1160,20 +1044,13 @@ void TVMCompilerContext::initMembers(ContractDefinition const *contract) { continue; addFunction(f); } - for (const auto &pair : getContractFunctionPairs(contract)) { - auto f = pair.first; - if (!isTvmIntrinsic(f->name()) && !isPureFunction(f) && !isFunctionForInlining(f)) { - m_functionsList.push_back(f); - } - } - for (VariableDeclaration const *variable: notConstantStateVariables()) { m_stateVarIndex[variable] = 10 + m_stateVarIndex.size(); } } TVMCompilerContext::TVMCompilerContext(ContractDefinition const *contract, - PragmaDirectiveHelper const &pragmaHelper) : m_pragmaHelper{pragmaHelper} { + PragmaDirectiveHelper const &pragmaHelper) : m_pragmaHelper{pragmaHelper} { initMembers(contract); } @@ -1255,10 +1132,6 @@ const FunctionDefinition *TVMCompilerContext::getLocalFunction(const string& fna return get_from_map(m_functions, fname, nullptr); } -const EventDefinition *TVMCompilerContext::getEvent(const string &name) const { - return get_from_map(m_events, name, nullptr); -} - bool TVMCompilerContext::haveFallbackFunction() const { return haveFallback; } @@ -1275,13 +1148,8 @@ bool TVMCompilerContext::ignoreIntegerOverflow() const { return ignoreIntOverflow; } -std::vector TVMCompilerContext::events() const { - std::vector result; - for (const auto& [name, event] : m_events) { - (void)name; - result.push_back(event); - } - return result; +bool TVMCompilerContext::haveOffChainConstructor() const { + return m_haveOffChainConstructor; } FunctionDefinition const *TVMCompilerContext::afterSignatureCheck() const { @@ -1297,54 +1165,6 @@ bool TVMCompilerContext::storeTimestampInC4() const { return haveTimeInAbiHeader() && afterSignatureCheck() == nullptr; } -StackPusherHelper::EncodePosition::EncodePosition(int bits) : - restSliceBits{TvmConst::CellBitLength - bits}, - restFef{4}, - qtyOfCreatedBuilders{0} -{ - -} - -bool StackPusherHelper::EncodePosition::needNewCell(Type const *type) { - ABITypeSize size(type); - solAssert(0 <= size.maxRefs && size.maxRefs <= 1, ""); - - restSliceBits -= size.maxBits; - restFef -= size.maxRefs; - - if (restSliceBits < 0 || restFef == 0) { - restSliceBits = TvmConst::CellBitLength - size.maxBits; - restFef = 4 - size.maxRefs; - ++qtyOfCreatedBuilders; - return true; - } - return false; -} - -int StackPusherHelper::EncodePosition::countOfCreatedBuilders() const { - return qtyOfCreatedBuilders; -} - - -void StackPusherHelper::encodeStruct(const StructType* structType, ASTNode const* node, EncodePosition& position) { - // builder... builder struct - const int saveStackSize0 = getStack().size() - 2; - ast_vec const& members = structType->structDefinition().members(); - const int memberQty = members.size(); - untuple(memberQty); // builder... builder values... - blockSwap(1, memberQty); // builder... values... builder - for (int i = 0; i < memberQty; ++i) { - encodeParameter(members[i]->type(), position, [&]() { - const int index = getStack().size() - saveStackSize0 - 1 - i; - pushS(index); - }, node); - } - - // builder... values... builder... - const int builderQty = getStack().size() - saveStackSize0 - memberQty; - dropUnder(builderQty, memberQty); -} - void StackPusherHelper::pushDefaultValue(Type const* type, bool isResultBuilder) { Type::Category cat = type->category(); switch (cat) { @@ -1429,8 +1249,8 @@ void StackPusherHelper::pushDefaultValue(Type const* type, bool isResultBuilder) } void StackPusherHelper::getFromDict(Type const& keyType, Type const& valueType, ASTNode const& node, - const DictOperation op, - const bool resultAsSliceForStruct) { + const DictOperation op, + const bool resultAsSliceForStruct) { // stack: index dict const Type::Category valueCategory = valueType.category(); prepareKeyForDictOperations(&keyType); diff --git a/compiler/libsolidity/codegen/TVMPusher.hpp b/compiler/libsolidity/codegen/TVMPusher.hpp index b81053d0..bf98b0c5 100644 --- a/compiler/libsolidity/codegen/TVMPusher.hpp +++ b/compiler/libsolidity/codegen/TVMPusher.hpp @@ -27,6 +27,7 @@ #include "TVMCommons.hpp" #include "TVMConstants.hpp" +#include "TVMABI.hpp" using namespace std; using namespace solidity; @@ -36,13 +37,14 @@ using namespace solidity::util; namespace solidity::frontend { +class StructCompiler; + class TVMStack { - int m_size; + int m_size{}; // map parameters or local variables to their absolute stack position std::map m_params; public: - TVMStack(); int size() const; void change(int diff); bool isParam(Declaration const* name) const; @@ -70,27 +72,24 @@ class TVMCompilerContext { const ContractDefinition* m_contract = nullptr; string_map m_functions; map m_function2contract; - string_map m_events; bool haveFallback = false; bool haveOnBounce = false; bool haveReceive = false; bool ignoreIntOverflow = false; bool m_haveSetDestAddr = false; + bool m_haveOffChainConstructor = false; PragmaDirectiveHelper const& m_pragmaHelper; std::map m_stateVarIndex; - void addEvent(EventDefinition const *event); void addFunction(FunctionDefinition const* _function); void initMembers(ContractDefinition const* contract); public: TVMCompilerContext(ContractDefinition const* contract, PragmaDirectiveHelper const& pragmaHelper); - mutable set m_remoteFunctions; - vector m_functionsList; - FunctionDefinition const* m_currentFunction = nullptr; - map m_inlinedFunctions; + FunctionDefinition const* m_currentFunction = nullptr; + map m_inlinedFunctions; int getStateVarIndex(VariableDeclaration const *variable) const; std::vector notConstantStateVariables() const; @@ -104,18 +103,15 @@ class TVMCompilerContext { const ContractDefinition* getContract() const; const ContractDefinition* getContract(const FunctionDefinition* f) const; const FunctionDefinition* getLocalFunction(const string& fname) const; - const EventDefinition* getEvent(const string& name) const; bool haveFallbackFunction() const; bool haveReceiveFunction() const; bool haveOnBounceHandler() const; bool ignoreIntegerOverflow() const; - std::vector events() const; + bool haveOffChainConstructor() const; FunctionDefinition const* afterSignatureCheck() const; bool storeTimestampInC4() const; }; -class StructCompiler; - class StackPusherHelper { protected: TVMStack m_stack; @@ -165,11 +161,13 @@ class StackPusherHelper { void preload(const Type* type); void pushZeroAddress(); void generateC7ToT4Macro(); + static void addBinaryNumberToString(std::string &s, u256 value, int bitlen = 256); static std::string binaryStringToSlice(const std::string & s); static std::string gramsToBinaryString(Literal const* literal); static std::string gramsToBinaryString(u256 value); std::string literalToSliceAddress(Literal const* literal, bool pushSlice = true); + bool tryImplicitConvert(Type const *leftType, Type const *rightType); void push(const CodeLines& codeLines); void pushPrivateFunctionOrMacroCall(const int stackDelta, const string& fname); @@ -207,44 +205,14 @@ class StackPusherHelper { void ensureValueFitsType(const ElementaryTypeNameToken& typeName, const ASTNode& node); - enum class ReasonOfOutboundMessage { - EmitEventExternal, - FunctionReturnExternal, - RemoteCallInternal - }; - - class EncodePosition : private boost::noncopyable { - int restSliceBits; - int restFef; - int qtyOfCreatedBuilders; - - public: - explicit EncodePosition(int bits); - bool needNewCell(Type const* type); - int countOfCreatedBuilders() const; - int restBits() const { return restSliceBits; } - }; - - int encodeFunctionAndParams(const string& functionName, - const std::vector& types, - const std::vector& nodes, - const std::function& pushParam, const ReasonOfOutboundMessage& reason); - - void encodeParameters(const std::vector& types, - const std::vector& nodes, - const std::function& pushParam, - EncodePosition& position); - - void encodeParameter(Type const* type, EncodePosition& position, const std::function& pushParam, ASTNode const* node); - void encodeStruct(const StructType* structType, ASTNode const* node, EncodePosition& position); void pushDefaultValue(Type const* type, bool isResultBuilder = false); void sendIntMsg(const std::map& exprs, const std::map &constParams, - const std::function &pushBody, + const std::function &appendBody, const std::function &pushSendrawmsgFlag); void sendMsg(const std::set& isParamOnStack, const std::map &constParams, - const std::function &pushBody, + const std::function &appendBody, const std::function &pushSendrawmsgFlag, bool isInternalMessage = true); }; diff --git a/compiler/libsolidity/codegen/TVMTypeChecker.cpp b/compiler/libsolidity/codegen/TVMTypeChecker.cpp index c7676ded..9976ba42 100644 --- a/compiler/libsolidity/codegen/TVMTypeChecker.cpp +++ b/compiler/libsolidity/codegen/TVMTypeChecker.cpp @@ -26,20 +26,6 @@ using namespace solidity::frontend; -class TVMDeprecated: public ASTConstVisitor, private boost::noncopyable { -public: - bool visit(MemberAccess const& _node) override { - auto identifier = to(&_node.expression()); - if (!identifier) { - return true; - } - if (identifier->name() == "tvm" && _node.memberName() == "transfer") { - cast_warning(_node, "tvm.transfer(...) is deprecated. Use addr.transfer(...)."); - } - return true; - } -}; - TVMTypeChecker::TVMTypeChecker(ContractDefinition const *contractDefinition, std::vector const &pragmaDirectives) : contractDefinition{contractDefinition}, @@ -59,8 +45,6 @@ void TVMTypeChecker::check(ContractDefinition const *contractDefinition, checker.checkPragma(); } checker.check_onCodeUpgrade(); - TVMDeprecated d{}; - contractDefinition->accept(d); } void TVMTypeChecker::checkPragma() { @@ -222,7 +206,7 @@ void TVMTypeChecker::checkTvmIntrinsic(FunctionDefinition const *f, ContractDefi deprecatedFunctionsReplacement["tvm_accept"] = "tvm.accept()"; deprecatedFunctionsReplacement["tvm_unpack_address"] = "address.unpack()"; deprecatedFunctionsReplacement["tvm_make_address"] = "address.makeAddrStd()"; - deprecatedFunctionsReplacement["tvm_transfer"] = "tvm.transfer()"; + deprecatedFunctionsReplacement["tvm_transfer"] = "address.transfer()"; deprecatedFunctionsReplacement["tvm_make_zero_address"] = "address.makeAddrStd()"; deprecatedFunctionsReplacement["tvm_setcode"] = "tvm.setcode()"; deprecatedFunctionsReplacement["tvm_is_zero_address"] = "address.isNone()"; @@ -289,7 +273,7 @@ void TVMTypeChecker::checkTvmIntrinsic(FunctionDefinition const *f, ContractDefi TypeInfo ti(type3); if (!ti.isNumeric || ti.numBits != 32 || ti.isSigned) cast_error(*(f->parameters()[3].get()),"Constructor id argument should be of type uint32."); - } + } } } diff --git a/compiler/libsolidity/parsing/Parser.cpp b/compiler/libsolidity/parsing/Parser.cpp index cc039392..2a190ce3 100644 --- a/compiler/libsolidity/parsing/Parser.cpp +++ b/compiler/libsolidity/parsing/Parser.cpp @@ -1980,6 +1980,11 @@ ASTPointer Parser::parsePrimaryExpression() expression = nodeFactory.createNode(expressionType); m_scanner->next(); } + else if (token == Token::Mapping) + { + ASTPointer map = parseMapping(); + expression = nodeFactory.createNode(map); + } else fatalParserError(string("Expected primary expression.")); break; diff --git a/compiler/solc/CommandLineInterface.cpp b/compiler/solc/CommandLineInterface.cpp index 0fe9765a..eb074aa7 100644 --- a/compiler/solc/CommandLineInterface.cpp +++ b/compiler/solc/CommandLineInterface.cpp @@ -70,6 +70,7 @@ #include #include +#include #if !defined(STDERR_FILENO) #define STDERR_FILENO 2 @@ -148,7 +149,7 @@ static string const g_strNatspecUser = "userdoc"; //static string const g_strOptimize = "optimize"; //static string const g_strOptimizeRuns = "optimize-runs"; //static string const g_strOptimizeYul = "optimize-yul"; -//static string const g_strOutputDir = "output-dir"; +static string const g_strOutputDir = "output-dir"; //static string const g_strOverwrite = "overwrite"; //static string const g_strRevertStrings = "revert-strings"; @@ -207,7 +208,7 @@ static string const g_argNatspecUser = g_strNatspecUser; //static string const g_argOpcodes = g_strOpcodes; //static string const g_argOptimize = g_strOptimize; //static string const g_argOptimizeRuns = g_strOptimizeRuns; -//static string const g_argOutputDir = g_strOutputDir; +static string const g_argOutputDir = g_strOutputDir; //static string const g_argSignatureHashes = g_strSignatureHashes; //static string const g_argStandardJSON = g_strStandardJSON; //static string const g_argStrictAssembly = g_strStrictAssembly; @@ -745,12 +746,12 @@ Allowed options)", // g_strRevertStrings.c_str(), // po::value()->value_name(boost::join(g_revertStringsArgs, ",")), // "Strip revert (and require) reason strings or add additional debugging information." -// ) -// ( -// (g_argOutputDir + ",o").c_str(), -// po::value()->value_name("path"), -// "If given, creates one file per component and contract/file at the specified directory." // ) + ( + (g_argOutputDir + ",o").c_str(), + po::value()->value_name("path"), + "If given, creates one file per component and contract/file at the specified directory." + ) ( (g_argSetContract + ",c").c_str(), po::value()->value_name("contract"), @@ -883,7 +884,9 @@ Allowed options)", const bool tvmMute = m_args.count(g_argTvmMuteFlagWarning); if ((tvmAbi || tvmCode) && !tvmMute) { - serr() << "Warning: options --tvm and --tvm-abi are deprecated. Use solc without that options to produce TVM assembly and ABI." << endl; + serr() << "Warning: options --tvm and --tvm-abi are deprecated. Use solc without options to produce TVM assembly and ABI:" << endl; + serr() << " solc contract.sol" << endl; + serr() << "This command compiles the contract and generates contract.code and contract.abi.json files." << endl; } if (m_args.count(g_argTvmPeephole)) { @@ -1208,6 +1211,9 @@ bool CommandLineInterface::processInput() if (m_args.count(g_argSetContract)) m_compiler->setMainContract(m_args[g_argSetContract].as()); + if (m_args.count(g_argOutputDir)) + TVMContractCompiler::m_outputFolder = m_args[g_argOutputDir].as(); + bool successful = m_compiler->compile(); for (auto const& error: m_compiler->errors()) diff --git a/lib/stdlib_sol.tvm b/lib/stdlib_sol.tvm index b2e3f174..49caff36 100644 --- a/lib/stdlib_sol.tvm +++ b/lib/stdlib_sol.tvm @@ -72,12 +72,9 @@ PUSHCONT { ; end colValue } WHILE -DROP ; end for ;; return ;; push identifier builder -PUSH S3 -XCHG s4 BLKDROP 4 ; end function abi_encode_packed_macro @@ -87,12 +84,13 @@ BLKDROP 4 ;; param: grams_value ;; param: constuctor_id ;; param: encodedParams +;; param: flag ; function deploy_contract_macro NEWC ;; decl: msg_builder ;; push identifier grams_value ;; push identifier remote_addr -PUSH2 S3, S4 +PUSH2 S4, S5 PUSHINT 0 ;; param: value ;; param: remote_addr @@ -123,7 +121,7 @@ STBR PUSHINT 1 STONES ;; push identifier my_contract -PUSH S5 +PUSH S6 ;; push identifier msg_builder ;; param: x ;; param: builder @@ -169,9 +167,8 @@ IFELSE ;; end if ;; return ;; push identifier builder -PUSH S1 -XCHG s3 -BLKDROP 3 +XCHG S1, S2 +DROP2 ; end function store_either_for_cell ; expValue ; end expValue @@ -181,10 +178,10 @@ NIP NEWC ;; decl: body_builder ;; push identifier constuctor_id -PUSH S3 +PUSH S4 STUR 32 ;; push identifier encodedParams -PUSH S2 +PUSH S3 STBR ;; push identifier body_builder ;; push identifier msg_builder @@ -227,9 +224,10 @@ POP s2 ;; push identifier msg_builder PUSH S1 ENDC -PUSHINT 1 +;; push identifier flag +PUSH S3 SENDRAWMSG -BLKDROP 7 +BLKDROP 8 ; end function deploy_contract_macro .macro deploy_contract2_macro @@ -319,9 +317,8 @@ IFELSE ;; end if ;; return ;; push identifier builder -PUSH S1 -XCHG s3 -BLKDROP 3 +XCHG S1, S2 +DROP2 ; end function store_either_for_cell ; expValue ; end expValue @@ -374,9 +371,8 @@ IFELSE ;; end if ;; return ;; push identifier builder -PUSH S1 -XCHG s3 -BLKDROP 3 +XCHG S1, S2 +DROP2 ; end function store_either_for_cell ; expValue ; end expValue