aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/codegen')
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp75
-rw-r--r--libsolidity/codegen/ABIFunctions.h6
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp3
-rw-r--r--libsolidity/codegen/CompilerContext.cpp37
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp25
-rw-r--r--libsolidity/codegen/CompilerUtils.h35
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp13
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp130
8 files changed, 201 insertions, 123 deletions
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
index 9f6c55ba..080be359 100644
--- a/libsolidity/codegen/ABIFunctions.cpp
+++ b/libsolidity/codegen/ABIFunctions.cpp
@@ -87,7 +87,7 @@ string ABIFunctions::tupleEncoder(
);
elementTempl("values", valueNames);
elementTempl("pos", to_string(headPos));
- elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, false));
+ elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, true));
encodeElements += elementTempl.render();
headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize();
}
@@ -162,7 +162,7 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure)
break;
}
case Type::Category::Contract:
- templ("body", "cleaned := " + cleanupFunction(IntegerType(0, IntegerType::Modifier::Address)) + "(value)");
+ templ("body", "cleaned := " + cleanupFunction(IntegerType(160, IntegerType::Modifier::Address)) + "(value)");
break;
case Type::Category::Enum:
{
@@ -243,7 +243,7 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
toCategory == Type::Category::Integer ||
toCategory == Type::Category::Contract,
"");
- IntegerType const addressType(0, IntegerType::Modifier::Address);
+ IntegerType const addressType(160, IntegerType::Modifier::Address);
IntegerType const& to =
toCategory == Type::Category::Integer ?
dynamic_cast<IntegerType const&>(_to) :
@@ -371,7 +371,7 @@ string ABIFunctions::abiEncodingFunction(
Type const& _from,
Type const& _to,
bool _encodeAsLibraryTypes,
- bool _compacted
+ bool _fromStack
)
{
solUnimplementedAssert(
@@ -415,7 +415,7 @@ string ABIFunctions::abiEncodingFunction(
dynamic_cast<FunctionType const&>(_from),
to,
_encodeAsLibraryTypes,
- _compacted
+ _fromStack
);
solAssert(_from.sizeOnStack() == 1, "");
@@ -487,6 +487,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArray(
// TODO if this is not a byte array, we might just copy byte-by-byte anyway,
// because the encoding is position-independent, but we have to check that.
Whiskers templ(R"(
+ // <readableTypeNameFrom> -> <readableTypeNameTo>
function <functionName>(start, length, pos) -> end {
<storeLength> // might update pos
<copyFun>(start, pos, length)
@@ -495,6 +496,8 @@ string ABIFunctions::abiEncodingFunctionCalldataArray(
)");
templ("storeLength", _to.isDynamicallySized() ? "mstore(pos, length) pos := add(pos, 0x20)" : "");
templ("functionName", functionName);
+ templ("readableTypeNameFrom", _from.toString(true));
+ templ("readableTypeNameTo", _to.toString(true));
templ("copyFun", copyToMemoryFunction(true));
templ("roundUpFun", roundUpFunction());
return templ.render();
@@ -527,6 +530,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
Whiskers templ(
dynamicBase ?
R"(
+ // <readableTypeNameFrom> -> <readableTypeNameTo>
function <functionName>(value, pos) <return> {
let length := <lengthFun>(value)
<storeLength> // might update pos
@@ -538,13 +542,14 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
mstore(pos, sub(tail, headStart))
tail := <encodeToMemoryFun>(<arrayElementAccess>, tail)
srcPtr := <nextArrayElement>(srcPtr)
- pos := add(pos, <elementEncodedSize>)
+ pos := add(pos, 0x20)
}
pos := tail
<assignEnd>
}
)" :
R"(
+ // <readableTypeNameFrom> -> <readableTypeNameTo>
function <functionName>(value, pos) <return> {
let length := <lengthFun>(value)
<storeLength> // might update pos
@@ -560,6 +565,8 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
)"
);
templ("functionName", functionName);
+ templ("readableTypeNameFrom", _from.toString(true));
+ templ("readableTypeNameTo", _to.toString(true));
templ("return", dynamic ? " -> end " : "");
templ("assignEnd", dynamic ? "end := pos" : "");
templ("lengthFun", arrayLengthFunction(_from));
@@ -573,7 +580,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
*_from.baseType(),
*_to.baseType(),
_encodeAsLibraryTypes,
- true
+ false
));
templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" );
templ("nextArrayElement", nextArrayElementFunction(_from));
@@ -639,6 +646,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
{
solAssert(_to.isByteArray(), "");
Whiskers templ(R"(
+ // <readableTypeNameFrom> -> <readableTypeNameTo>
function <functionName>(value, pos) -> ret {
let slotValue := sload(value)
switch and(slotValue, 1)
@@ -665,6 +673,8 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
}
)");
templ("functionName", functionName);
+ templ("readableTypeNameFrom", _from.toString(true));
+ templ("readableTypeNameTo", _to.toString(true));
templ("arrayDataSlot", arrayDataAreaFunction(_from));
return templ.render();
}
@@ -681,6 +691,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
// more than desired, i.e. it writes beyond the end of memory.
Whiskers templ(
R"(
+ // <readableTypeNameFrom> -> <readableTypeNameTo>
function <functionName>(value, pos) <return> {
let length := <lengthFun>(value)
<storeLength> // might update pos
@@ -701,6 +712,8 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
)"
);
templ("functionName", functionName);
+ templ("readableTypeNameFrom", _from.toString(true));
+ templ("readableTypeNameTo", _to.toString(true));
templ("return", dynamic ? " -> end " : "");
templ("assignEnd", dynamic ? "end := pos" : "");
templ("lengthFun", arrayLengthFunction(_from));
@@ -716,7 +729,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
*_from.baseType(),
*_to.baseType(),
_encodeAsLibraryTypes,
- true
+ false
);
templ("encodeToMemoryFun", encodeToMemoryFun);
std::vector<std::map<std::string, std::string>> items(itemsPerSlot);
@@ -748,6 +761,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
bool fromStorage = _from.location() == DataLocation::Storage;
bool dynamic = _to.isDynamicallyEncoded();
Whiskers templ(R"(
+ // <readableTypeNameFrom> -> <readableTypeNameTo>
function <functionName>(value, pos) <return> {
let tail := add(pos, <headSize>)
<init>
@@ -761,6 +775,8 @@ string ABIFunctions::abiEncodingFunctionStruct(
}
)");
templ("functionName", functionName);
+ templ("readableTypeNameFrom", _from.toString(true));
+ templ("readableTypeNameTo", _to.toString(true));
templ("return", dynamic ? " -> end " : "");
templ("assignEnd", dynamic ? "end := tail" : "");
// to avoid multiple loads from the same slot for subsequent members
@@ -909,7 +925,7 @@ string ABIFunctions::abiEncodingFunctionFunctionType(
FunctionType const& _from,
Type const& _to,
bool _encodeAsLibraryTypes,
- bool _compacted
+ bool _fromStack
)
{
solAssert(_from.kind() == FunctionType::Kind::External, "");
@@ -920,35 +936,31 @@ string ABIFunctions::abiEncodingFunctionFunctionType(
_from.identifier() +
"_to_" +
_to.identifier() +
- (_compacted ? "_compacted" : "") +
+ (_fromStack ? "_fromStack" : "") +
(_encodeAsLibraryTypes ? "_library" : "");
- if (_compacted)
- {
+ if (_fromStack)
return createFunction(functionName, [&]() {
return Whiskers(R"(
- function <functionName>(addr_and_function_id, pos) {
- mstore(pos, <cleanExtFun>(addr_and_function_id))
+ function <functionName>(addr, function_id, pos) {
+ mstore(pos, <combineExtFun>(addr, function_id))
}
)")
("functionName", functionName)
- ("cleanExtFun", cleanupCombinedExternalFunctionIdFunction())
+ ("combineExtFun", combineExternalFunctionIdFunction())
.render();
});
- }
else
- {
return createFunction(functionName, [&]() {
return Whiskers(R"(
- function <functionName>(addr, function_id, pos) {
- mstore(pos, <combineExtFun>(addr, function_id))
+ function <functionName>(addr_and_function_id, pos) {
+ mstore(pos, <cleanExtFun>(addr_and_function_id))
}
)")
("functionName", functionName)
- ("combineExtFun", combineExternalFunctionIdFunction())
+ ("cleanExtFun", cleanupCombinedExternalFunctionIdFunction())
.render();
});
- }
}
string ABIFunctions::copyToMemoryFunction(bool _fromCalldata)
@@ -995,9 +1007,11 @@ string ABIFunctions::shiftLeftFunction(size_t _numBits)
return createFunction(functionName, [&]() {
solAssert(_numBits < 256, "");
return
- Whiskers(R"(function <functionName>(value) -> newValue {
+ Whiskers(R"(
+ function <functionName>(value) -> newValue {
newValue := mul(value, <multiplier>)
- })")
+ }
+ )")
("functionName", functionName)
("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
.render();
@@ -1010,9 +1024,11 @@ string ABIFunctions::shiftRightFunction(size_t _numBits, bool _signed)
return createFunction(functionName, [&]() {
solAssert(_numBits < 256, "");
return
- Whiskers(R"(function <functionName>(value) -> newValue {
+ Whiskers(R"(
+ function <functionName>(value) -> newValue {
newValue := <div>(value, <multiplier>)
- })")
+ }
+ )")
("functionName", functionName)
("div", _signed ? "sdiv" : "div")
("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
@@ -1025,9 +1041,11 @@ string ABIFunctions::roundUpFunction()
string functionName = "round_up_to_mul_of_32";
return createFunction(functionName, [&]() {
return
- Whiskers(R"(function <functionName>(value) -> result {
+ Whiskers(R"(
+ function <functionName>(value) -> result {
result := and(add(value, 31), not(31))
- })")
+ }
+ )")
("functionName", functionName)
.render();
});
@@ -1190,10 +1208,7 @@ size_t ABIFunctions::headSize(TypePointers const& _targetTypes)
if (t->isDynamicallyEncoded())
headSize += 0x20;
else
- {
- solAssert(t->calldataEncodedSize() > 0, "");
headSize += t->calldataEncodedSize();
- }
}
return headSize;
diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h
index de2a140a..e61f68bc 100644
--- a/libsolidity/codegen/ABIFunctions.h
+++ b/libsolidity/codegen/ABIFunctions.h
@@ -89,13 +89,13 @@ private:
/// @returns the name of the ABI encoding function with the given type
/// and queues the generation of the function to the requested functions.
- /// @param _compacted if true, the input value was just loaded from storage
+ /// @param _fromStack if false, the input value was just loaded from storage
/// or memory and thus might be compacted into a single slot (depending on the type).
std::string abiEncodingFunction(
Type const& _givenType,
Type const& _targetType,
bool _encodeAsLibraryTypes,
- bool _compacted
+ bool _fromStack
);
/// Part of @a abiEncodingFunction for array target type and given calldata array.
std::string abiEncodingFunctionCalldataArray(
@@ -143,7 +143,7 @@ private:
FunctionType const& _from,
Type const& _to,
bool _encodeAsLibraryTypes,
- bool _compacted
+ bool _fromStack
);
/// @returns a function that copies raw bytes of dynamic length from calldata
diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp
index e17188c2..ce8cbb5f 100644
--- a/libsolidity/codegen/ArrayUtils.cpp
+++ b/libsolidity/codegen/ArrayUtils.cpp
@@ -291,8 +291,11 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
CompilerUtils utils(m_context);
unsigned baseSize = 1;
if (!_sourceType.isByteArray())
+ {
// We always pad the elements, regardless of _padToWordBoundaries.
baseSize = _sourceType.baseType()->calldataEncodedSize();
+ solAssert(baseSize >= 0x20, "");
+ }
if (_sourceType.location() == DataLocation::CallData)
{
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index 5a77162e..ce9c3b7f 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -26,6 +26,7 @@
#include <libsolidity/codegen/Compiler.h>
#include <libsolidity/interface/Version.h>
#include <libsolidity/interface/ErrorReporter.h>
+#include <libsolidity/interface/SourceReferenceFormatter.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/inlineasm/AsmParser.h>
#include <libsolidity/inlineasm/AsmCodeGen.h>
@@ -37,6 +38,13 @@
#include <utility>
#include <numeric>
+// Change to "define" to output all intermediate code
+#undef SOL_OUTPUT_ASM
+#ifdef SOL_OUTPUT_ASM
+#include <libsolidity/inlineasm/AsmPrinter.h>
+#endif
+
+
using namespace std;
namespace dev
@@ -312,12 +320,31 @@ void CompilerContext::appendInlineAssembly(
ErrorReporter errorReporter(errors);
auto scanner = make_shared<Scanner>(CharStream(_assembly), "--CODEGEN--");
auto parserResult = assembly::Parser(errorReporter).parse(scanner);
- solAssert(parserResult, "Failed to parse inline assembly block.");
- solAssert(errorReporter.errors().empty(), "Failed to parse inline assembly block.");
-
+#ifdef SOL_OUTPUT_ASM
+ cout << assembly::AsmPrinter()(*parserResult) << endl;
+#endif
assembly::AsmAnalysisInfo analysisInfo;
- assembly::AsmAnalyzer analyzer(analysisInfo, errorReporter, false, identifierAccess.resolve);
- solAssert(analyzer.analyze(*parserResult), "Failed to analyze inline assembly block.");
+ bool analyzerResult = false;
+ if (parserResult)
+ analyzerResult = assembly::AsmAnalyzer(analysisInfo, errorReporter, false, identifierAccess.resolve).analyze(*parserResult);
+ if (!parserResult || !errorReporter.errors().empty() || !analyzerResult)
+ {
+ string message =
+ "Error parsing/analyzing inline assembly block:\n"
+ "------------------ Input: -----------------\n" +
+ _assembly + "\n"
+ "------------------ Errors: ----------------\n";
+ for (auto const& error: errorReporter.errors())
+ message += SourceReferenceFormatter::formatExceptionInformation(
+ *error,
+ (error->type() == Error::Type::Warning) ? "Warning" : "Error",
+ [&](string const&) -> Scanner const& { return *scanner; }
+ );
+ message += "-------------------------------------------\n";
+
+ solAssert(false, message);
+ }
+
solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block.");
assembly::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system);
}
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 37aa1aea..f9b181ae 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -191,7 +191,7 @@ void CompilerUtils::encodeToMemory(
{
// Use the new JULIA-based encoding function
auto stackHeightBefore = m_context.stackHeight();
- abiEncode(_givenTypes, targetTypes, _encodeAsLibraryTypes);
+ abiEncodeV2(_givenTypes, targetTypes, _encodeAsLibraryTypes);
solAssert(stackHeightBefore - m_context.stackHeight() == sizeOnStack(_givenTypes), "");
return;
}
@@ -302,7 +302,7 @@ void CompilerUtils::encodeToMemory(
popStackSlots(argSize + dynPointers + 1);
}
-void CompilerUtils::abiEncode(
+void CompilerUtils::abiEncodeV2(
TypePointers const& _givenTypes,
TypePointers const& _targetTypes,
bool _encodeAsLibraryTypes
@@ -541,7 +541,7 @@ void CompilerUtils::convertType(
else
{
solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, "");
- IntegerType addressType(0, IntegerType::Modifier::Address);
+ IntegerType addressType(160, IntegerType::Modifier::Address);
IntegerType const& targetType = targetTypeCategory == Type::Category::Integer
? dynamic_cast<IntegerType const&>(_targetType) : addressType;
if (stackTypeCategory == Type::Category::RationalNumber)
@@ -596,7 +596,6 @@ void CompilerUtils::convertType(
storeInMemoryDynamic(IntegerType(256));
// stack: mempos datapos
storeStringData(data);
- break;
}
else
solAssert(
@@ -810,9 +809,8 @@ void CompilerUtils::convertType(
if (_cleanupNeeded)
m_context << Instruction::ISZERO << Instruction::ISZERO;
break;
- case Type::Category::Function:
- {
- if (targetTypeCategory == Type::Category::Integer)
+ default:
+ if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Integer)
{
IntegerType const& targetType = dynamic_cast<IntegerType const&>(_targetType);
solAssert(targetType.isAddress(), "Function type can only be converted to address.");
@@ -821,17 +819,16 @@ void CompilerUtils::convertType(
// stack: <address> <function_id>
m_context << Instruction::POP;
- break;
}
- }
- // fall-through
- default:
- // All other types should not be convertible to non-equal types.
- solAssert(_typeOnStack == _targetType, "Invalid type conversion requested.");
- if (_cleanupNeeded && _targetType.canBeStored() && _targetType.storageBytes() < 32)
+ else
+ {
+ // All other types should not be convertible to non-equal types.
+ solAssert(_typeOnStack == _targetType, "Invalid type conversion requested.");
+ if (_cleanupNeeded && _targetType.canBeStored() && _targetType.storageBytes() < 32)
m_context
<< ((u256(1) << (8 * _targetType.storageBytes())) - 1)
<< Instruction::AND;
+ }
break;
}
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index 5e45699b..ad3989ad 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -102,13 +102,26 @@ public:
/// @note the locations of target reference types are ignored, because it will always be
/// memory.
void encodeToMemory(
- TypePointers const& _givenTypes = {},
- TypePointers const& _targetTypes = {},
- bool _padToWords = true,
- bool _copyDynamicDataInPlace = false,
+ TypePointers const& _givenTypes,
+ TypePointers const& _targetTypes,
+ bool _padToWords,
+ bool _copyDynamicDataInPlace,
bool _encodeAsLibraryTypes = false
);
+ /// Special case of @a encodeToMemory which assumes tight packing, e.g. no zero padding
+ /// and dynamic data is encoded in-place.
+ /// Stack pre: <value0> <value1> ... <valueN-1> <head_start>
+ /// Stack post: <mem_ptr>
+ void packedEncode(
+ TypePointers const& _givenTypes,
+ TypePointers const& _targetTypes,
+ bool _encodeAsLibraryTypes = false
+ )
+ {
+ encodeToMemory(_givenTypes, _targetTypes, false, true, _encodeAsLibraryTypes);
+ }
+
/// Special case of @a encodeToMemory which assumes that everything is padded to words
/// and dynamic data is not copied in place (i.e. a proper ABI encoding).
/// Stack pre: <value0> <value1> ... <valueN-1> <head_start>
@@ -117,6 +130,20 @@ public:
TypePointers const& _givenTypes,
TypePointers const& _targetTypes,
bool _encodeAsLibraryTypes = false
+ )
+ {
+ encodeToMemory(_givenTypes, _targetTypes, true, false, _encodeAsLibraryTypes);
+ }
+
+ /// Special case of @a encodeToMemory which assumes that everything is padded to words
+ /// and dynamic data is not copied in place (i.e. a proper ABI encoding).
+ /// Uses a new, less tested encoder implementation.
+ /// Stack pre: <value0> <value1> ... <valueN-1> <head_start>
+ /// Stack post: <mem_ptr>
+ void abiEncodeV2(
+ TypePointers const& _givenTypes,
+ TypePointers const& _targetTypes,
+ bool _encodeAsLibraryTypes = false
);
/// Zero-initialises (the data part of) an already allocated memory array.
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index 92782b8d..74565ae4 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -251,13 +251,10 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
FunctionDefinition const* fallback = _contract.fallbackFunction();
eth::AssemblyItem notFound = m_context.newTag();
- // shortcut messages without data if we have many functions in order to be able to receive
- // ether with constant gas
- if (interfaceFunctions.size() > 5 || fallback)
- {
- m_context << Instruction::CALLDATASIZE << Instruction::ISZERO;
- m_context.appendConditionalJumpTo(notFound);
- }
+ // directly jump to fallback if the data is too short to contain a function selector
+ // also guards against short data
+ m_context << u256(4) << Instruction::CALLDATASIZE << Instruction::LT;
+ m_context.appendConditionalJumpTo(notFound);
// retrieve the function signature hash from the calldata
if (!interfaceFunctions.empty())
@@ -421,7 +418,7 @@ void ContractCompiler::appendReturnValuePacker(TypePointers const& _typeParamete
utils.fetchFreeMemoryPointer();
//@todo optimization: if we return a single memory array, there should be enough space before
// its data to add the needed parts and we avoid a memory copy.
- utils.encodeToMemory(_typeParameters, _typeParameters, true, false, _isLibrary);
+ utils.abiEncode(_typeParameters, _typeParameters, _isLibrary);
utils.toSizeAfterFreeMemoryPointer();
m_context << Instruction::RETURN;
}
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index c94baa10..bb8c4a94 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -581,7 +581,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
_context << Instruction::ADD;
}
);
- utils().encodeToMemory(argumentTypes, function.parameterTypes());
+ utils().abiEncode(argumentTypes, function.parameterTypes());
// now on stack: memory_end_ptr
// need: size, offset, endowment
utils().toSizeAfterFreeMemoryPointer();
@@ -675,7 +675,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
argumentTypes.push_back(arg->annotation().type);
}
utils().fetchFreeMemoryPointer();
- utils().encodeToMemory(argumentTypes, TypePointers(), function.padArguments(), true);
+ solAssert(!function.padArguments(), "");
+ utils().packedEncode(argumentTypes, TypePointers());
utils().toSizeAfterFreeMemoryPointer();
m_context << Instruction::KECCAK256;
break;
@@ -694,11 +695,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
arguments.front()->accept(*this);
utils().fetchFreeMemoryPointer();
- utils().encodeToMemory(
+ utils().packedEncode(
{arguments.front()->annotation().type},
- {function.parameterTypes().front()},
- false,
- true);
+ {function.parameterTypes().front()}
+ );
utils().toSizeAfterFreeMemoryPointer();
m_context << logInstruction(logNumber);
break;
@@ -717,11 +717,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
if (auto const& arrayType = dynamic_pointer_cast<ArrayType const>(function.parameterTypes()[arg - 1]))
{
utils().fetchFreeMemoryPointer();
- utils().encodeToMemory(
+ utils().packedEncode(
{arguments[arg - 1]->annotation().type},
- {arrayType},
- false,
- true
+ {arrayType}
);
utils().toSizeAfterFreeMemoryPointer();
m_context << Instruction::KECCAK256;
@@ -751,7 +749,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
nonIndexedParamTypes.push_back(function.parameterTypes()[arg]);
}
utils().fetchFreeMemoryPointer();
- utils().encodeToMemory(nonIndexedArgTypes, nonIndexedParamTypes);
+ utils().abiEncode(nonIndexedArgTypes, nonIndexedParamTypes);
// need: topic1 ... topicn memsize memstart
utils().toSizeAfterFreeMemoryPointer();
m_context << logInstruction(numIndexed);
@@ -860,8 +858,15 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::DUP1 << Instruction::DUP3 << Instruction::MSTORE;
// Stack: memptr requested_length
// update free memory pointer
- m_context << Instruction::DUP1 << arrayType.baseType()->memoryHeadSize();
- m_context << Instruction::MUL << u256(32) << Instruction::ADD;
+ m_context << Instruction::DUP1;
+ // Stack: memptr requested_length requested_length
+ if (arrayType.isByteArray())
+ // Round up to multiple of 32
+ m_context << u256(31) << Instruction::ADD << u256(31) << Instruction::NOT << Instruction::AND;
+ else
+ m_context << arrayType.baseType()->memoryHeadSize() << Instruction::MUL;
+ // stacK: memptr requested_length data_size
+ m_context << u256(32) << Instruction::ADD;
m_context << Instruction::DUP3 << Instruction::ADD;
utils().storeFreeMemoryPointer();
// Stack: memptr requested_length
@@ -1014,59 +1019,65 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
switch (_memberAccess.expression().annotation().type->category())
{
case Type::Category::Contract:
+ case Type::Category::Integer:
{
bool alsoSearchInteger = false;
- ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type);
- if (type.isSuper())
+ if (_memberAccess.expression().annotation().type->category() == Type::Category::Contract)
{
- solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved.");
- utils().pushCombinedFunctionEntryLabel(m_context.superFunction(
- dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration),
- type.contractDefinition()
- ));
+ ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type);
+ if (type.isSuper())
+ {
+ solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved.");
+ utils().pushCombinedFunctionEntryLabel(m_context.superFunction(
+ dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration),
+ type.contractDefinition()
+ ));
+ }
+ else
+ {
+ // ordinary contract type
+ if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration)
+ {
+ u256 identifier;
+ if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration))
+ identifier = FunctionType(*variable).externalIdentifier();
+ else if (auto const* function = dynamic_cast<FunctionDefinition const*>(declaration))
+ identifier = FunctionType(*function).externalIdentifier();
+ else
+ solAssert(false, "Contract member is neither variable nor function.");
+ utils().convertType(type, IntegerType(160, IntegerType::Modifier::Address), true);
+ m_context << identifier;
+ }
+ else
+ // not found in contract, search in members inherited from address
+ alsoSearchInteger = true;
+ }
}
else
+ alsoSearchInteger = true;
+
+ if (alsoSearchInteger)
{
- // ordinary contract type
- if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration)
+ if (member == "balance")
{
- u256 identifier;
- if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration))
- identifier = FunctionType(*variable).externalIdentifier();
- else if (auto const* function = dynamic_cast<FunctionDefinition const*>(declaration))
- identifier = FunctionType(*function).externalIdentifier();
- else
- solAssert(false, "Contract member is neither variable nor function.");
- utils().convertType(type, IntegerType(0, IntegerType::Modifier::Address), true);
- m_context << identifier;
+ utils().convertType(
+ *_memberAccess.expression().annotation().type,
+ IntegerType(160, IntegerType::Modifier::Address),
+ true
+ );
+ m_context << Instruction::BALANCE;
}
+ else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall"}).count(member))
+ utils().convertType(
+ *_memberAccess.expression().annotation().type,
+ IntegerType(160, IntegerType::Modifier::Address),
+ true
+ );
else
- // not found in contract, search in members inherited from address
- alsoSearchInteger = true;
- }
- if (!alsoSearchInteger)
- break;
- }
- // fall-through
- case Type::Category::Integer:
- if (member == "balance")
- {
- utils().convertType(
- *_memberAccess.expression().annotation().type,
- IntegerType(0, IntegerType::Modifier::Address),
- true
- );
- m_context << Instruction::BALANCE;
+ solAssert(false, "Invalid member access to integer");
}
- else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall"}).count(member))
- utils().convertType(
- *_memberAccess.expression().annotation().type,
- IntegerType(0, IntegerType::Modifier::Address),
- true
- );
- else
- solAssert(false, "Invalid member access to integer");
break;
+ }
case Type::Category::Function:
if (member == "selector")
{
@@ -1206,11 +1217,9 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
utils().fetchFreeMemoryPointer();
// stack: base index mem
// note: the following operations must not allocate memory!
- utils().encodeToMemory(
+ utils().packedEncode(
TypePointers{_indexAccess.indexExpression()->annotation().type},
- TypePointers{keyType},
- false,
- true
+ TypePointers{keyType}
);
m_context << Instruction::SWAP1;
utils().storeInMemoryDynamic(IntegerType(256));
@@ -1712,6 +1721,9 @@ void ExpressionCompiler::appendExternalFunctionCall(
if (_functionType.gasSet())
m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
+ else if (m_context.experimentalFeatureActive(ExperimentalFeature::V050))
+ // Send all gas (requires tangerine whistle EVM)
+ m_context << Instruction::GAS;
else
{
// send all gas except the amount needed to execute "SUB" and "CALL"