diff options
-rw-r--r-- | Changelog.md | 1 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 7 | ||||
-rw-r--r-- | libsolidity/ast/ASTAnnotations.h | 2 | ||||
-rw-r--r-- | libsolidity/codegen/ArrayUtils.cpp | 9 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerContext.cpp | 4 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 87 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.h | 26 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 2 | ||||
-rwxr-xr-x | scripts/install_deps.sh | 6 | ||||
-rw-r--r-- | solc/CommandLineInterface.cpp | 321 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 11 |
11 files changed, 300 insertions, 176 deletions
diff --git a/Changelog.md b/Changelog.md index 9bc465ee..dfeaa381 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: * Bitshift operators. * Type checker: Warn when ``msg.value`` is used in non-payable function. * Code generator: Inject the Swarm hash of a metadata file into the bytecode. + * Code generator: Replace expensive memcpy precompile by simple assembly loop. * Optimizer: Some dead code elimination. Bugfixes: diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 7235b57a..e414e27c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1529,6 +1529,8 @@ bool TypeChecker::visit(Identifier const& _identifier) !!annotation.referencedDeclaration, "Referenced declaration is null after overload resolution." ); + auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(annotation.referencedDeclaration); + annotation.isConstant = variableDeclaration != nullptr && variableDeclaration->isConstant(); annotation.isLValue = annotation.referencedDeclaration->isLValue(); annotation.type = annotation.referencedDeclaration->type(); if (!annotation.type) @@ -1612,7 +1614,10 @@ void TypeChecker::requireLValue(Expression const& _expression) { _expression.annotation().lValueRequested = true; _expression.accept(*this); - if (!_expression.annotation().isLValue) + + if (_expression.annotation().isConstant) + typeError(_expression.location(), "Cannot assign to a constant variable."); + else if (!_expression.annotation().isLValue) typeError(_expression.location(), "Expression has to be an lvalue."); } diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 768e56db..9c4c3ae8 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -154,6 +154,8 @@ struct ExpressionAnnotation: ASTAnnotation { /// Inferred type of the expression. TypePointer type; + /// Whether the expression is a constant variable + bool isConstant = false; /// Whether it is an LValue (i.e. something that can be assigned to). bool isLValue = false; /// Whether the expression is used in a context where the LValue is actually required. diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 2c982982..352c7177 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -335,9 +335,14 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord if (baseSize > 1) m_context << u256(baseSize) << Instruction::MUL; // stack: <target> <source> <size> - //@TODO do not use ::CALL if less than 32 bytes? m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::DUP4; - utils.memoryCopy(); + // We can resort to copying full 32 bytes only if + // - the length is known to be a multiple of 32 or + // - we will pad to full 32 bytes later anyway. + if (((baseSize % 32) == 0) || _padToWordBoundaries) + utils.memoryCopy32(); + else + utils.memoryCopy(); m_context << Instruction::SWAP1 << Instruction::POP; // stack: <target> <size> diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 2de5a3ec..c14ab845 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -202,6 +202,8 @@ void CompilerContext::appendInlineAssembly( return false; unsigned stackDepth = _localVariables.end() - it; int stackDiff = _assembly.deposit() - startStackHeight + stackDepth; + if (_context == assembly::CodeGenerator::IdentifierContext::LValue) + stackDiff -= 1; if (stackDiff < 1 || stackDiff > 16) BOOST_THROW_EXCEPTION( CompilerError() << @@ -217,7 +219,7 @@ void CompilerContext::appendInlineAssembly( return true; }; - solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, *m_asm, identifierAccess), ""); + solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, *m_asm, identifierAccess), "Failed to assemble inline assembly block."); } FunctionDefinition const& CompilerContext::resolveVirtualFunction( diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 7c159ff7..7d382aba 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -298,22 +298,73 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) m_context << Instruction::SWAP1 << Instruction::POP; } +void CompilerUtils::memoryCopyPrecompile() +{ + // Stack here: size target source + + m_context.appendInlineAssembly(R"( + { + let words := div(add(len, 31), 32) + let cost := add(15, mul(3, words)) + jumpi(invalidJumpLabel, iszero(call(cost, $identityContractAddress, 0, src, len, dst, len))) + } + )", + { "len", "dst", "src" }, + map<string, string> { + { "$identityContractAddress", toString(identityContractAddress) } + } + ); + m_context << Instruction::POP << Instruction::POP << Instruction::POP; +} + +void CompilerUtils::memoryCopy32() +{ + // Stack here: size target source + + m_context.appendInlineAssembly(R"( + { + jumpi(end, eq(len, 0)) + start: + mstore(dst, mload(src)) + jumpi(end, iszero(gt(len, 32))) + dst := add(dst, 32) + src := add(src, 32) + len := sub(len, 32) + jump(start) + end: + } + )", + { "len", "dst", "src" } + ); + m_context << Instruction::POP << Instruction::POP << Instruction::POP; +} + void CompilerUtils::memoryCopy() { // Stack here: size target source - // stack for call: outsize target size source value contract gas - //@TODO do not use ::CALL if less than 32 bytes? - m_context << Instruction::DUP3 << Instruction::SWAP1; - m_context << u256(0) << u256(identityContractAddress); - // compute gas costs - m_context << u256(32) << Instruction::DUP5 << u256(31) << Instruction::ADD; - static unsigned c_identityGas = 15; - static unsigned c_identityWordGas = 3; - m_context << Instruction::DIV << u256(c_identityWordGas) << Instruction::MUL; - m_context << u256(c_identityGas) << Instruction::ADD; - m_context << Instruction::CALL; - m_context << Instruction::ISZERO; - m_context.appendConditionalJumpTo(m_context.errorTag()); + + m_context.appendInlineAssembly(R"( + { + // copy 32 bytes at once + start32: + jumpi(end32, lt(len, 32)) + mstore(dst, mload(src)) + dst := add(dst, 32) + src := add(src, 32) + len := sub(len, 32) + jump(start32) + end32: + + // copy the remainder (0 < len < 32) + let mask := sub(exp(256, sub(32, len)), 1) + let srcpart := and(mload(src), not(mask)) + let dstpart := and(mload(dst), mask) + mstore(dst, or(srcpart, dstpart)) + } + )", + { "len", "dst", "src" } + ); + m_context << Instruction::POP << Instruction::POP << Instruction::POP; } void CompilerUtils::splitExternalFunctionType(bool _leftAligned) @@ -901,9 +952,9 @@ void CompilerUtils::storeStringData(bytesConstRef _data) } } -unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries) +unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords) { - unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries); + unsigned numBytes = _type.calldataEncodedSize(_padToWords); bool isExternalFunctionType = false; if (auto const* funType = dynamic_cast<FunctionType const*>(&_type)) if (funType->location() == FunctionType::Location::External) @@ -942,9 +993,9 @@ void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack) m_context << ((u256(1) << _typeOnStack.numBits()) - 1) << Instruction::AND; } -unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) +unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWords) { - unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries); + unsigned numBytes = _type.calldataEncodedSize(_padToWords); bool leftAligned = _type.category() == Type::Category::FixedBytes; if (numBytes == 0) m_context << Instruction::POP; @@ -952,7 +1003,7 @@ unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBou { solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested."); convertType(_type, _type, true); - if (numBytes != 32 && !leftAligned && !_padToWordBoundaries) + if (numBytes != 32 && !leftAligned && !_padToWords) // shift the value accordingly before storing m_context << (u256(1) << ((32 - numBytes) * 8)) << Instruction::MUL; } diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 0a5d8e1c..b9ed6757 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -52,13 +52,13 @@ public: /// @param _offset offset in memory (or calldata) /// @param _type data type to load /// @param _fromCalldata if true, load from calldata, not from memory - /// @param _padToWordBoundaries if true, assume the data is padded to word (32 byte) boundaries + /// @param _padToWords if true, assume the data is padded to full words (32 bytes) /// @returns the number of bytes consumed in memory. unsigned loadFromMemory( unsigned _offset, Type const& _type = IntegerType(256), bool _fromCalldata = false, - bool _padToWordBoundaries = false + bool _padToWords = false ); /// Dynamic version of @see loadFromMemory, expects the memory offset on the stack. /// Stack pre: memory_offset @@ -66,7 +66,7 @@ public: void loadFromMemoryDynamic( Type const& _type, bool _fromCalldata = false, - bool _padToWordBoundaries = true, + bool _padToWords = true, bool _keepUpdatedMemoryOffset = true ); /// Stores a 256 bit integer from stack in memory. @@ -76,11 +76,11 @@ public: /// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack /// and also updates that. For reference types, only copies the data pointer. Fails for /// non-memory-references. - /// @param _padToWordBoundaries if true, adds zeros to pad to multiple of 32 bytes. Array elements + /// @param _padToWords if true, adds zeros to pad to multiple of 32 bytes. Array elements /// are always padded (except for byte arrays), regardless of this parameter. /// Stack pre: memory_offset value... /// Stack post: (memory_offset+length) - void storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries = true); + void storeInMemoryDynamic(Type const& _type, bool _padToWords = true); /// Copies values (of types @a _givenTypes) given on the stack to a location in memory given /// at the stack top, encoding them according to the ABI as the given types @a _targetTypes. @@ -88,7 +88,7 @@ public: /// Stack pre: <v1> <v2> ... <vn> <memptr> /// Stack post: <memptr_updated> /// Does not touch the memory-free pointer. - /// @param _padToWordBoundaries if false, all values are concatenated without padding. + /// @param _padToWords if false, all values are concatenated without padding. /// @param _copyDynamicDataInPlace if true, dynamic types is stored (without length) /// together with fixed-length data. /// @param _encodeAsLibraryTypes if true, encodes for a library function, e.g. does not @@ -98,7 +98,7 @@ public: void encodeToMemory( TypePointers const& _givenTypes = {}, TypePointers const& _targetTypes = {}, - bool _padToWordBoundaries = true, + bool _padToWords = true, bool _copyDynamicDataInPlace = false, bool _encodeAsLibraryTypes = false ); @@ -112,6 +112,14 @@ public: /// Uses a CALL to the identity contract to perform a memory-to-memory copy. /// Stack pre: <size> <target> <source> /// Stack post: + void memoryCopyPrecompile(); + /// Copies full 32 byte words in memory (regions cannot overlap), i.e. may copy more than length. + /// Stack pre: <size> <target> <source> + /// Stack post: + void memoryCopy32(); + /// Copies data in memory (regions cannot overlap). + /// Stack pre: <size> <target> <source> + /// Stack post: void memoryCopy(); /// Converts the combined and left-aligned (right-aligned if @a _rightAligned is true) @@ -185,9 +193,9 @@ private: void cleanHigherOrderBits(IntegerType const& _typeOnStack); /// Prepares the given type for storing in memory by shifting it if necessary. - unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries); + unsigned prepareMemoryStore(Type const& _type, bool _padToWords); /// Loads type from memory assuming memory offset is on stack top. - unsigned loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries); + unsigned loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords); CompilerContext& m_context; }; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index eb588fc2..0b8cecc7 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -146,7 +146,7 @@ bool CompilerStack::parse() } } if (!Error::containsOnlyWarnings(m_errors)) - // errors while parsing. sould stop before type checking + // errors while parsing. should stop before type checking return false; resolveImports(); diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index c9f82769..255176ab 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -83,12 +83,6 @@ case $(uname -s) in ;; 10.12) echo "Installing solidity dependencies on macOS 10.12 Sierra." - echo "" - echo "NOTE - You are in unknown territory with this preview OS." - echo "Even Homebrew doesn't have official support yet, and there are" - echo "known issues (see https://github.com/ethereum/webthree-umbrella/issues/614)." - echo "If you would like to partner with us to work through these issues, that" - echo "would be fantastic. Please just comment on that issue. Thanks!" ;; *) echo "Unsupported macOS version." diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index c83432ca..e322455b 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -65,38 +65,83 @@ namespace dev namespace solidity { -static string const g_argAbiStr = "abi"; -static string const g_argSignatureHashes = "hashes"; -static string const g_argGas = "gas"; -static string const g_argAsmStr = "asm"; -static string const g_argAsmJsonStr = "asm-json"; -static string const g_argAstStr = "ast"; -static string const g_argAstJson = "ast-json"; -static string const g_argBinaryStr = "bin"; -static string const g_argRuntimeBinaryStr = "bin-runtime"; -static string const g_argCloneBinaryStr = "clone-bin"; -static string const g_argOpcodesStr = "opcodes"; -static string const g_argNatspecDevStr = "devdoc"; -static string const g_argNatspecUserStr = "userdoc"; -static string const g_argMetadata = "metadata"; -static string const g_argAddStandard = "add-std"; -static string const g_stdinFileName = "<stdin>"; +static string const g_strAbi = "abi"; +static string const g_strAddStandard = "add-std"; +static string const g_strAsm = "asm"; +static string const g_strAsmJson = "asm-json"; +static string const g_strAssemble = "assemble"; +static string const g_strAst = "ast"; +static string const g_strAstJson = "ast-json"; +static string const g_strBinary = "bin"; +static string const g_strBinaryRuntime = "bin-runtime"; +static string const g_strCloneBinary = "clone-bin"; +static string const g_strCombinedJson = "combined-json"; +static string const g_strContracts = "contracts"; +static string const g_strFormal = "formal"; +static string const g_strGas = "gas"; +static string const g_strHelp = "help"; +static string const g_strInputFile = "input-file"; +static string const g_strInterface = "interface"; +static string const g_strLibraries = "libraries"; +static string const g_strLink = "link"; +static string const g_strMetadata = "metadata"; +static string const g_strNatspecDev = "devdoc"; +static string const g_strNatspecUser = "userdoc"; +static string const g_strOpcodes = "opcodes"; +static string const g_strOptimize = "optimize"; +static string const g_strOptimizeRuns = "optimize-runs"; +static string const g_strOutputDir = "output-dir"; +static string const g_strSignatureHashes = "hashes"; +static string const g_strSources = "sources"; +static string const g_strSourceList = "sourceList"; +static string const g_strSrcMap = "srcmap"; +static string const g_strSrcMapRuntime = "srcmap-runtime"; +static string const g_strVersion = "version"; +static string const g_stdinFileNameStr = "<stdin>"; + +static string const g_argAbi = g_strAbi; +static string const g_argAddStandard = g_strAddStandard; +static string const g_argAsm = g_strAsm; +static string const g_argAsmJson = g_strAsmJson; +static string const g_argAssemble = g_strAssemble; +static string const g_argAst = g_strAst; +static string const g_argAstJson = g_strAstJson; +static string const g_argBinary = g_strBinary; +static string const g_argBinaryRuntime = g_strBinaryRuntime; +static string const g_argCloneBinary = g_strCloneBinary; +static string const g_argCombinedJson = g_strCombinedJson; +static string const g_argFormal = g_strFormal; +static string const g_argGas = g_strGas; +static string const g_argHelp = g_strHelp; +static string const g_argInputFile = g_strInputFile; +static string const g_argLibraries = g_strLibraries; +static string const g_argLink = g_strLink; +static string const g_argMetadata = g_strMetadata; +static string const g_argNatspecDev = g_strNatspecDev; +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_argSignatureHashes = g_strSignatureHashes; +static string const g_argVersion = g_strVersion; +static string const g_stdinFileName = g_stdinFileNameStr; /// Possible arguments to for --combined-json static set<string> const g_combinedJsonArgs{ - "bin", - "bin-runtime", - "clone-bin", - "srcmap", - "srcmap-runtime", - "opcodes", - "abi", - "interface", - "metadata", - "asm", - "ast", - "userdoc", - "devdoc" + g_strAbi, + g_strAsm, + g_strAst, + g_strBinary, + g_strBinaryRuntime, + g_strCloneBinary, + g_strInterface, + g_strMetadata, + g_strNatspecUser, + g_strNatspecDev, + g_strOpcodes, + g_strSrcMap, + g_strSrcMapRuntime }; static void version() @@ -114,22 +159,22 @@ static bool needsHumanTargetedStdout(po::variables_map const& _args) { if (_args.count(g_argGas)) return true; - if (_args.count("output-dir")) + if (_args.count(g_argOutputDir)) return false; for (string const& arg: { - g_argAbiStr, - g_argSignatureHashes, - g_argMetadata, - g_argNatspecUserStr, + g_argAbi, + g_argAsm, + g_argAsmJson, g_argAstJson, - g_argNatspecDevStr, - g_argAsmStr, - g_argAsmJsonStr, - g_argOpcodesStr, - g_argBinaryStr, - g_argRuntimeBinaryStr, - g_argCloneBinaryStr, - string("formal") + g_argBinary, + g_argBinaryRuntime, + g_argCloneBinary, + g_argFormal, + g_argMetadata, + g_argNatspecUser, + g_argNatspecDev, + g_argOpcodes, + g_argSignatureHashes }) if (_args.count(arg)) return true; @@ -138,9 +183,9 @@ static bool needsHumanTargetedStdout(po::variables_map const& _args) void CommandLineInterface::handleBinary(string const& _contract) { - if (m_args.count(g_argBinaryStr)) + if (m_args.count(g_argBinary)) { - if (m_args.count("output-dir")) + if (m_args.count(g_argOutputDir)) createFile(_contract + ".bin", m_compiler->object(_contract).toHex()); else { @@ -148,9 +193,9 @@ void CommandLineInterface::handleBinary(string const& _contract) cout << m_compiler->object(_contract).toHex() << endl; } } - if (m_args.count(g_argCloneBinaryStr)) + if (m_args.count(g_argCloneBinary)) { - if (m_args.count("output-dir")) + if (m_args.count(g_argOutputDir)) createFile(_contract + ".clone_bin", m_compiler->cloneObject(_contract).toHex()); else { @@ -158,9 +203,9 @@ void CommandLineInterface::handleBinary(string const& _contract) cout << m_compiler->cloneObject(_contract).toHex() << endl; } } - if (m_args.count(g_argRuntimeBinaryStr)) + if (m_args.count(g_argBinaryRuntime)) { - if (m_args.count("output-dir")) + if (m_args.count(g_argOutputDir)) createFile(_contract + ".bin-runtime", m_compiler->runtimeObject(_contract).toHex()); else { @@ -172,7 +217,7 @@ void CommandLineInterface::handleBinary(string const& _contract) void CommandLineInterface::handleOpcode(string const& _contract) { - if (m_args.count("output-dir")) + if (m_args.count(g_argOutputDir)) createFile(_contract + ".opcode", solidity::disassemble(m_compiler->object(_contract).bytecode)); else { @@ -184,9 +229,9 @@ void CommandLineInterface::handleOpcode(string const& _contract) void CommandLineInterface::handleBytecode(string const& _contract) { - if (m_args.count(g_argOpcodesStr)) + if (m_args.count(g_argOpcodes)) handleOpcode(_contract); - if (m_args.count(g_argBinaryStr) || m_args.count(g_argCloneBinaryStr) || m_args.count(g_argRuntimeBinaryStr)) + if (m_args.count(g_argBinary) || m_args.count(g_argCloneBinary) || m_args.count(g_argBinaryRuntime)) handleBinary(_contract); } @@ -199,7 +244,7 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract) for (auto const& it: m_compiler->contractDefinition(_contract).interfaceFunctions()) out += toHex(it.first.ref()) + ": " + it.second->externalSignature() + "\n"; - if (m_args.count("output-dir")) + if (m_args.count(g_argOutputDir)) createFile(_contract + ".signatures", out); else cout << "Function signatures: " << endl << out; @@ -225,17 +270,17 @@ void CommandLineInterface::handleMeta(DocumentationType _type, string const& _co switch(_type) { case DocumentationType::ABIInterface: - argName = g_argAbiStr; + argName = g_argAbi; suffix = ".abi"; title = "Contract JSON ABI"; break; case DocumentationType::NatspecUser: - argName = g_argNatspecUserStr; + argName = g_argNatspecUser; suffix = ".docuser"; title = "User Documentation"; break; case DocumentationType::NatspecDev: - argName = g_argNatspecDevStr; + argName = g_argNatspecDev; suffix = ".docdev"; title = "Developer Documentation"; break; @@ -252,7 +297,7 @@ void CommandLineInterface::handleMeta(DocumentationType _type, string const& _co else output = dev::jsonPrettyPrint(m_compiler->metadata(_contract, _type)); - if (m_args.count("output-dir")) + if (m_args.count(g_argOutputDir)) createFile(_contract + suffix, output); else { @@ -314,10 +359,10 @@ void CommandLineInterface::handleGasEstimation(string const& _contract) void CommandLineInterface::handleFormal() { - if (!m_args.count("formal")) + if (!m_args.count(g_argFormal)) return; - if (m_args.count("output-dir")) + if (m_args.count(g_argOutputDir)) createFile("solidity.mlw", m_compiler->formalTranslation()); else cout << "Formal version:" << endl << m_compiler->formalTranslation() << endl; @@ -326,10 +371,10 @@ void CommandLineInterface::handleFormal() void CommandLineInterface::readInputFilesAndConfigureRemappings() { bool addStdin = false; - if (!m_args.count("input-file")) + if (!m_args.count(g_argInputFile)) addStdin = true; else - for (string path: m_args["input-file"].as<vector<string>>()) + for (string path: m_args[g_argInputFile].as<vector<string>>()) { auto eq = find(path.begin(), path.end(), '='); if (eq != path.end()) @@ -404,7 +449,7 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da { namespace fs = boost::filesystem; // create directory if not existent - fs::path p(m_args.at("output-dir").as<string>()); + fs::path p(m_args.at(g_argOutputDir).as<string>()); fs::create_directories(p); string pathName = (p / _fileName).string(); ofstream outFile(pathName); @@ -431,66 +476,66 @@ Allowed options)", po::options_description::m_default_line_length, po::options_description::m_default_line_length - 23); desc.add_options() - ("help", "Show help message and exit.") - ("version", "Show version and exit.") - ("optimize", "Enable bytecode optimizer.") + (g_argHelp.c_str(), "Show help message and exit.") + (g_argVersion.c_str(), "Show version and exit.") + (g_argOptimize.c_str(), "Enable bytecode optimizer.") ( - "optimize-runs", + g_argOptimizeRuns.c_str(), po::value<unsigned>()->value_name("n")->default_value(200), "Estimated number of contract runs for optimizer tuning." ) (g_argAddStandard.c_str(), "Add standard contracts.") ( - "libraries", + g_argLibraries.c_str(), po::value<vector<string>>()->value_name("libs"), "Direct string or file containing library addresses. Syntax: " "<libraryName>: <address> [, or whitespace] ...\n" "Address is interpreted as a hex string optionally prefixed by 0x." ) ( - "output-dir,o", + (g_argOutputDir + ",o").c_str(), po::value<string>()->value_name("path"), "If given, creates one file per component and contract/file at the specified directory." ) ( - "combined-json", + g_argCombinedJson.c_str(), po::value<string>()->value_name(boost::join(g_combinedJsonArgs, ",")), "Output a single json document containing the specified information." ) (g_argGas.c_str(), "Print an estimate of the maximal gas usage for each function.") ( - "assemble", + g_argAssemble.c_str(), "Switch to assembly mode, ignoring all options and assumes input is assembly." ) ( - "link", + g_argLink.c_str(), "Switch to linker mode, ignoring all options apart from --libraries " "and modify binaries in place." ); po::options_description outputComponents("Output Components"); outputComponents.add_options() - (g_argAstStr.c_str(), "AST of all source files.") + (g_argAst.c_str(), "AST of all source files.") (g_argAstJson.c_str(), "AST of all source files in JSON format.") - (g_argAsmStr.c_str(), "EVM assembly of the contracts.") - (g_argAsmJsonStr.c_str(), "EVM assembly of the contracts in JSON format.") - (g_argOpcodesStr.c_str(), "Opcodes of the contracts.") - (g_argBinaryStr.c_str(), "Binary of the contracts in hex.") - (g_argRuntimeBinaryStr.c_str(), "Binary of the runtime part of the contracts in hex.") - (g_argCloneBinaryStr.c_str(), "Binary of the clone contracts in hex.") - (g_argAbiStr.c_str(), "ABI specification of the contracts.") + (g_argAsm.c_str(), "EVM assembly of the contracts.") + (g_argAsmJson.c_str(), "EVM assembly of the contracts in JSON format.") + (g_argOpcodes.c_str(), "Opcodes of the contracts.") + (g_argBinary.c_str(), "Binary of the contracts in hex.") + (g_argBinaryRuntime.c_str(), "Binary of the runtime part of the contracts in hex.") + (g_argCloneBinary.c_str(), "Binary of the clone contracts in hex.") + (g_argAbi.c_str(), "ABI specification of the contracts.") (g_argSignatureHashes.c_str(), "Function signature hashes of the contracts.") - (g_argNatspecUserStr.c_str(), "Natspec user documentation of all contracts.") - (g_argNatspecDevStr.c_str(), "Natspec developer documentation of all contracts.") + (g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.") + (g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.") (g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.") - ("formal", "Translated source suitable for formal analysis."); + (g_argFormal.c_str(), "Translated source suitable for formal analysis."); desc.add(outputComponents); po::options_description allOptions = desc; - allOptions.add_options()("input-file", po::value<vector<string>>(), "input file"); + allOptions.add_options()(g_argInputFile.c_str(), po::value<vector<string>>(), "input file"); // All positional options should be interpreted as input files po::positional_options_description filesPositions; - filesPositions.add("input-file", -1); + filesPositions.add(g_argInputFile.c_str(), -1); // parse the compiler arguments try @@ -505,22 +550,22 @@ Allowed options)", return false; } - if (m_args.count("help") || (isatty(fileno(stdin)) && _argc == 1)) + if (m_args.count(g_argHelp) || (isatty(fileno(stdin)) && _argc == 1)) { cout << desc; return false; } - if (m_args.count("version")) + if (m_args.count(g_argVersion)) { version(); return false; } - if (m_args.count("combined-json")) + if (m_args.count(g_argCombinedJson)) { vector<string> requests; - for (string const& item: boost::split(requests, m_args["combined-json"].as<string>(), boost::is_any_of(","))) + for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as<string>(), boost::is_any_of(","))) if (!g_combinedJsonArgs.count(item)) { cerr << "Invalid option to --combined-json: " << item << endl; @@ -536,18 +581,18 @@ bool CommandLineInterface::processInput() { readInputFilesAndConfigureRemappings(); - if (m_args.count("libraries")) - for (string const& library: m_args["libraries"].as<vector<string>>()) + if (m_args.count(g_argLibraries)) + for (string const& library: m_args[g_argLibraries].as<vector<string>>()) if (!parseLibraryOption(library)) return false; - if (m_args.count("assemble")) + if (m_args.count(g_argAssemble)) { // switch to assembly mode m_onlyAssemble = true; return assemble(); } - if (m_args.count("link")) + if (m_args.count(g_argLink)) { // switch to linker mode m_onlyLink = true; @@ -589,16 +634,16 @@ bool CommandLineInterface::processInput() auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compiler->scanner(_sourceName); }; try { - if (m_args.count("input-file")) - m_compiler->setRemappings(m_args["input-file"].as<vector<string>>()); + if (m_args.count(g_argInputFile)) + m_compiler->setRemappings(m_args[g_argInputFile].as<vector<string>>()); for (auto const& sourceCode: m_sourceCodes) m_compiler->addSource(sourceCode.first, sourceCode.second); // TODO: Perhaps we should not compile unless requested - bool optimize = m_args.count("optimize") > 0; - unsigned runs = m_args["optimize-runs"].as<unsigned>(); + bool optimize = m_args.count(g_argOptimize) > 0; + unsigned runs = m_args[g_argOptimizeRuns].as<unsigned>(); bool successful = m_compiler->compile(optimize, runs, m_libraries); - if (successful && m_args.count("formal")) + if (successful && m_args.count(g_argFormal)) if (!m_compiler->prepareFormalAnalysis()) successful = false; @@ -655,73 +700,73 @@ bool CommandLineInterface::processInput() void CommandLineInterface::handleCombinedJSON() { - if (!m_args.count("combined-json")) + if (!m_args.count(g_argCombinedJson)) return; Json::Value output(Json::objectValue); - output["version"] = ::dev::solidity::VersionString; + output[g_strVersion] = ::dev::solidity::VersionString; set<string> requests; - boost::split(requests, m_args["combined-json"].as<string>(), boost::is_any_of(",")); + boost::split(requests, m_args[g_argCombinedJson].as<string>(), boost::is_any_of(",")); vector<string> contracts = m_compiler->contractNames(); if (!contracts.empty()) - output["contracts"] = Json::Value(Json::objectValue); + output[g_strContracts] = Json::Value(Json::objectValue); for (string const& contractName: contracts) { Json::Value contractData(Json::objectValue); - if (requests.count("abi")) - contractData["abi"] = dev::jsonCompactPrint(m_compiler->interface(contractName)); + if (requests.count(g_strAbi)) + contractData[g_strAbi] = dev::jsonCompactPrint(m_compiler->interface(contractName)); if (requests.count("metadata")) contractData["metadata"] = m_compiler->onChainMetadata(contractName); - if (requests.count("bin")) - contractData["bin"] = m_compiler->object(contractName).toHex(); - if (requests.count("bin-runtime")) - contractData["bin-runtime"] = m_compiler->runtimeObject(contractName).toHex(); - if (requests.count("clone-bin")) - contractData["clone-bin"] = m_compiler->cloneObject(contractName).toHex(); - if (requests.count("opcodes")) - contractData["opcodes"] = solidity::disassemble(m_compiler->object(contractName).bytecode); - if (requests.count("asm")) + if (requests.count(g_strBinary)) + contractData[g_strBinary] = m_compiler->object(contractName).toHex(); + if (requests.count(g_strBinaryRuntime)) + contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex(); + if (requests.count(g_strCloneBinary)) + contractData[g_strCloneBinary] = m_compiler->cloneObject(contractName).toHex(); + if (requests.count(g_strOpcodes)) + contractData[g_strOpcodes] = solidity::disassemble(m_compiler->object(contractName).bytecode); + if (requests.count(g_strAsm)) { ostringstream unused; - contractData["asm"] = m_compiler->streamAssembly(unused, contractName, m_sourceCodes, true); + contractData[g_strAsm] = m_compiler->streamAssembly(unused, contractName, m_sourceCodes, true); } - if (requests.count("srcmap")) + if (requests.count(g_strSrcMap)) { auto map = m_compiler->sourceMapping(contractName); - contractData["srcmap"] = map ? *map : ""; + contractData[g_strSrcMap] = map ? *map : ""; } - if (requests.count("srcmap-runtime")) + if (requests.count(g_strSrcMapRuntime)) { auto map = m_compiler->runtimeSourceMapping(contractName); - contractData["srcmap-runtime"] = map ? *map : ""; + contractData[g_strSrcMapRuntime] = map ? *map : ""; } - if (requests.count("devdoc")) - contractData["devdoc"] = dev::jsonCompactPrint(m_compiler->metadata(contractName, DocumentationType::NatspecDev)); - if (requests.count("userdoc")) - contractData["userdoc"] = dev::jsonCompactPrint(m_compiler->metadata(contractName, DocumentationType::NatspecUser)); - output["contracts"][contractName] = contractData; + if (requests.count(g_strNatspecDev)) + contractData[g_strNatspecDev] = dev::jsonCompactPrint(m_compiler->metadata(contractName, DocumentationType::NatspecDev)); + if (requests.count(g_strNatspecUser)) + contractData[g_strNatspecUser] = dev::jsonCompactPrint(m_compiler->metadata(contractName, DocumentationType::NatspecUser)); + output[g_strContracts][contractName] = contractData; } - bool needsSourceList = requests.count("ast") || requests.count("srcmap") || requests.count("srcmap-runtime"); + bool needsSourceList = requests.count(g_strAst) || requests.count(g_strSrcMap) || requests.count(g_strSrcMapRuntime); if (needsSourceList) { // Indices into this array are used to abbreviate source names in source locations. - output["sourceList"] = Json::Value(Json::arrayValue); + output[g_strSourceList] = Json::Value(Json::arrayValue); for (auto const& source: m_compiler->sourceNames()) - output["sourceList"].append(source); + output[g_strSourceList].append(source); } - if (requests.count("ast")) + if (requests.count(g_strAst)) { - output["sources"] = Json::Value(Json::objectValue); + output[g_strSources] = Json::Value(Json::objectValue); for (auto const& sourceCode: m_sourceCodes) { ASTJsonConverter converter(m_compiler->ast(sourceCode.first), m_compiler->sourceIndices()); - output["sources"][sourceCode.first] = Json::Value(Json::objectValue); - output["sources"][sourceCode.first]["AST"] = converter.json(); + output[g_strSources][sourceCode.first] = Json::Value(Json::objectValue); + output[g_strSources][sourceCode.first]["AST"] = converter.json(); } } cout << dev::jsonCompactPrint(output) << endl; @@ -731,7 +776,7 @@ void CommandLineInterface::handleAst(string const& _argStr) { string title; - if (_argStr == g_argAstStr) + if (_argStr == g_argAst) title = "Syntax trees:"; else if (_argStr == g_argAstJson) title = "JSON AST:"; @@ -751,13 +796,13 @@ void CommandLineInterface::handleAst(string const& _argStr) asts ); - if (m_args.count("output-dir")) + if (m_args.count(g_argOutputDir)) { for (auto const& sourceCode: m_sourceCodes) { stringstream data; string postfix = ""; - if (_argStr == g_argAstStr) + if (_argStr == g_argAst) { ASTPrinter printer(m_compiler->ast(sourceCode.first), sourceCode.second); printer.print(data); @@ -778,7 +823,7 @@ void CommandLineInterface::handleAst(string const& _argStr) for (auto const& sourceCode: m_sourceCodes) { cout << endl << "======= " << sourceCode.first << " =======" << endl; - if (_argStr == g_argAstStr) + if (_argStr == g_argAst) { ASTPrinter printer( m_compiler->ast(sourceCode.first), @@ -903,7 +948,7 @@ void CommandLineInterface::outputCompilationResults() handleCombinedJSON(); // do we need AST output? - handleAst(g_argAstStr); + handleAst(g_argAst); handleAst(g_argAstJson); vector<string> contracts = m_compiler->contractNames(); @@ -913,18 +958,18 @@ void CommandLineInterface::outputCompilationResults() cout << endl << "======= " << contract << " =======" << endl; // do we need EVM assembly? - if (m_args.count(g_argAsmStr) || m_args.count(g_argAsmJsonStr)) + if (m_args.count(g_argAsm) || m_args.count(g_argAsmJson)) { - if (m_args.count("output-dir")) + if (m_args.count(g_argOutputDir)) { stringstream data; - m_compiler->streamAssembly(data, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr)); - createFile(contract + (m_args.count(g_argAsmJsonStr) ? "_evm.json" : ".evm"), data.str()); + m_compiler->streamAssembly(data, contract, m_sourceCodes, m_args.count(g_argAsmJson)); + createFile(contract + (m_args.count(g_argAsmJson) ? "_evm.json" : ".evm"), data.str()); } else { cout << "EVM assembly:" << endl; - m_compiler->streamAssembly(cout, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr)); + m_compiler->streamAssembly(cout, contract, m_sourceCodes, m_args.count(g_argAsmJson)); } } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index a4e601f7..576421fd 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -4865,6 +4865,17 @@ BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_modifier_following_non_payable_p CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(assignment_to_constant) +{ + char const* text = R"( + contract c { + uint constant a = 1; + function f() { a = 2; } + } + )"; + CHECK_ERROR(text, TypeError, "Cannot assign to a constant variable."); +} + BOOST_AUTO_TEST_SUITE_END() } |