aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md1
-rw-r--r--libsolidity/analysis/TypeChecker.cpp7
-rw-r--r--libsolidity/ast/ASTAnnotations.h2
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp9
-rw-r--r--libsolidity/codegen/CompilerContext.cpp4
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp87
-rw-r--r--libsolidity/codegen/CompilerUtils.h26
-rw-r--r--libsolidity/interface/CompilerStack.cpp2
-rwxr-xr-xscripts/install_deps.sh6
-rw-r--r--solc/CommandLineInterface.cpp321
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp11
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()
}