Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
chriseth
2020-11-03 14:05:14 +01:00
206 changed files with 1420 additions and 862 deletions
+1 -1
View File
@@ -1172,7 +1172,7 @@ void ArrayUtils::accessCallDataArrayElement(ArrayType const& _arrayType, bool _d
if (
!_arrayType.isByteArray() &&
_arrayType.baseType()->storageBytes() < 32 &&
m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)
m_context.useABICoderV2()
)
{
m_context << u256(32);
+3 -7
View File
@@ -77,11 +77,8 @@ public:
langutil::EVMVersion const& evmVersion() const { return m_evmVersion; }
/// Update currently enabled set of experimental features.
void setExperimentalFeatures(std::set<ExperimentalFeature> const& _features) { m_experimentalFeatures = _features; }
std::set<ExperimentalFeature> const& experimentalFeaturesActive() const { return m_experimentalFeatures; }
/// @returns true if the given feature is enabled.
bool experimentalFeatureActive(ExperimentalFeature _feature) const { return m_experimentalFeatures.count(_feature); }
void setUseABICoderV2(bool _value) { m_useABICoderV2 = _value; }
bool useABICoderV2() const { return m_useABICoderV2; }
void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
void addImmutable(VariableDeclaration const& _declaration);
@@ -365,8 +362,7 @@ private:
/// Version of the EVM to compile against.
langutil::EVMVersion m_evmVersion;
RevertStrings const m_revertStrings;
/// Activated experimental features.
std::set<ExperimentalFeature> m_experimentalFeatures;
bool m_useABICoderV2 = false;
/// Other already compiled contracts to be used in contract creation calls.
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> m_otherCompilers;
/// Storage offsets of state variables
+2 -2
View File
@@ -230,7 +230,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory)
{
/// Stack: <source_offset> <length>
if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
if (m_context.useABICoderV2())
{
// Use the new Yul-based decoding function
auto stackHeightBefore = m_context.stackHeight();
@@ -412,7 +412,7 @@ void CompilerUtils::encodeToMemory(
)
{
// stack: <v1> <v2> ... <vn> <mem>
bool const encoderV2 = m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2);
bool const encoderV2 = m_context.useABICoderV2();
TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes;
solAssert(targetTypes.size() == _givenTypes.size(), "");
for (TypePointer& t: targetTypes)
+4 -4
View File
@@ -127,7 +127,7 @@ void ContractCompiler::initializeContext(
map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers
)
{
m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures);
m_context.setUseABICoderV2(*_contract.sourceUnit().annotation().useABICoderV2);
m_context.setOtherCompilers(_otherCompilers);
m_context.setMostDerivedContract(_contract);
if (m_runtimeCompiler)
@@ -1349,13 +1349,13 @@ void ContractCompiler::appendModifierOrFunctionCode()
{
m_context.setArithmetic(Arithmetic::Checked);
std::set<ExperimentalFeature> experimentalFeaturesOutside = m_context.experimentalFeaturesActive();
m_context.setExperimentalFeatures(codeBlock->sourceUnit().annotation().experimentalFeatures);
bool coderV2Outside = m_context.useABICoderV2();
m_context.setUseABICoderV2(*codeBlock->sourceUnit().annotation().useABICoderV2);
m_returnTags.emplace_back(m_context.newTag(), m_context.stackHeight());
codeBlock->accept(*this);
m_context.setExperimentalFeatures(experimentalFeaturesOutside);
m_context.setUseABICoderV2(coderV2Outside);
solAssert(!m_returnTags.empty(), "");
m_context << m_returnTags.back().first;
+2 -2
View File
@@ -1676,7 +1676,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
{
solAssert(memberType->calldataEncodedSize() > 0, "");
solAssert(memberType->storageBytes() <= 32, "");
if (memberType->storageBytes() < 32 && m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
if (memberType->storageBytes() < 32 && m_context.useABICoderV2())
{
m_context << u256(32);
CompilerUtils(m_context).abiDecodeV2({memberType}, false);
@@ -2522,7 +2522,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
// memory pointer), but kept references to the return data for
// (statically-sized) arrays
bool needToUpdateFreeMemoryPtr = false;
if (dynamicReturnSize || m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
if (dynamicReturnSize || m_context.useABICoderV2())
needToUpdateFreeMemoryPtr = true;
else
for (auto const& retType: returnTypes)
+101 -9
View File
@@ -1556,13 +1556,14 @@ string YulUtilFunctions::clearStorageStructFunction(StructType const& _type)
});
}
string YulUtilFunctions::copyArrayToStorage(ArrayType const& _fromType, ArrayType const& _toType)
string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
{
solAssert(
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
""
);
solUnimplementedAssert(!_fromType.isByteArray(), "");
if (_fromType.isByteArray())
return copyByteArrayToStorageFunction(_fromType, _toType);
solUnimplementedAssert(!_fromType.dataStoredIn(DataLocation::Storage), "");
string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
@@ -1655,6 +1656,84 @@ string YulUtilFunctions::copyArrayToStorage(ArrayType const& _fromType, ArrayTyp
});
}
string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
{
solAssert(
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
""
);
solAssert(_fromType.isByteArray(), "");
solAssert(_toType.isByteArray(), "");
solUnimplementedAssert(!_fromType.dataStoredIn(DataLocation::Storage), "");
string functionName = "copy_byte_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
return m_functionCollector.createFunction(functionName, [&](){
Whiskers templ(R"(
function <functionName>(slot, src<?fromCalldata>, len</fromCalldata>) {
let newLen := <arrayLength>(src<?fromCalldata>, len</fromCalldata>)
// Make sure array length is sane
if gt(newLen, 0xffffffffffffffff) { <panic>() }
let oldLen := <byteArrayLength>(sload(slot))
<?fromMemory>
src := add(src, 0x20)
</fromMemory>
// This is not needed in all branches.
let dstDataArea
if or(gt(oldLen, 31), gt(newLen, 31)) {
dstDataArea := <dstDataLocation>(slot)
}
if gt(oldLen, 31) {
// potentially truncate data
let deleteStart := add(dstDataArea, div(add(newLen, 31), 32))
if lt(newLen, 32) { deleteStart := dstDataArea }
<clearStorageRange>(deleteStart, add(dstDataArea, div(add(oldLen, 31), 32)))
}
switch gt(newLen, 31)
case 1 {
let loopEnd := and(newLen, not(0x1f))
let dstPtr := dstDataArea
let i := 0
for { } lt(i, loopEnd) { i := add(i, 32) } {
sstore(dstPtr, <readFromCalldataOrMemory>(add(src, i)))
dstPtr := add(dstPtr, 1)
}
if lt(loopEnd, newLen) {
let lastValue := <readFromCalldataOrMemory>(add(src, i))
sstore(dstPtr, <maskBytes>(lastValue, and(newLen, 0x1f)))
}
sstore(slot, add(mul(newLen, 2), 1))
}
default {
let value := 0
if newLen {
value := <readFromCalldataOrMemory>(src)
}
sstore(slot, <byteArrayCombineShort>(value, newLen))
}
}
)");
templ("functionName", functionName);
bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData);
templ("fromMemory", _fromType.dataStoredIn(DataLocation::Memory));
templ("fromCalldata", fromCalldata);
templ("arrayLength", arrayLengthFunction(_fromType));
templ("panic", panicFunction(PanicCode::ResourceError));
templ("byteArrayLength", extractByteArrayLengthFunction());
templ("dstDataLocation", arrayDataAreaFunction(_toType));
templ("clearStorageRange", clearStorageRangeFunction(*_toType.baseType()));
templ("readFromCalldataOrMemory", readFromMemoryOrCalldata(*TypeProvider::uint256(), fromCalldata));
templ("maskBytes", maskBytesFunctionDynamic());
templ("byteArrayCombineShort", shortByteArrayEncodeUsedAreaSetLengthFunction());
return templ.render();
});
}
string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
{
string functionName = "array_convert_length_to_size_" + _type.identifier();
@@ -2207,8 +2286,9 @@ string YulUtilFunctions::updateStorageValueFunction(
solAssert(_toType.storageBytes() > 0, "Invalid storage bytes size.");
return Whiskers(R"(
function <functionName>(slot, <offset>value) {
sstore(slot, <update>(sload(slot), <offset><prepare>(value)))
function <functionName>(slot, <offset><fromValues>) {
let <toValues> := <convert>(<fromValues>)
sstore(slot, <update>(sload(slot), <offset><prepare>(<toValues>)))
}
)")
@@ -2219,6 +2299,9 @@ string YulUtilFunctions::updateStorageValueFunction(
updateByteSliceFunctionDynamic(_toType.storageBytes())
)
("offset", _offset.has_value() ? "" : "offset, ")
("convert", conversionFunction(_fromType, _toType))
("fromValues", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()))
("toValues", suffixedVariableNameList("convertedValue_", 0, _toType.sizeOnStack()))
("prepare", prepareStoreFunction(_toType))
.render();
}
@@ -2248,7 +2331,7 @@ string YulUtilFunctions::updateStorageValueFunction(
)");
templ("functionName", functionName);
templ("value", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()));
templ("copyArrayToStorage", copyArrayToStorage(
templ("copyArrayToStorage", copyArrayToStorageFunction(
dynamic_cast<ArrayType const&>(_fromType),
dynamic_cast<ArrayType const&>(_toType)
));
@@ -2478,9 +2561,16 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl
return templ.render();
}
bool leftAligned = false;
if (
_type.category() != Type::Category::Function ||
dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External
)
leftAligned = _type.leftAligned();
if (storageBytes == 32)
templ("cleaned", "value");
else if (_type.leftAligned())
else if (leftAligned)
templ("cleaned", shiftLeftFunction(256 - 8 * storageBytes) + "(value)");
else
templ("cleaned", "and(value, " + toCompactHexWithPrefix((u256(1) << (8 * storageBytes)) - 1) + ")");
@@ -2491,7 +2581,8 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl
string YulUtilFunctions::prepareStoreFunction(Type const& _type)
{
solUnimplementedAssert(_type.category() != Type::Category::Function, "");
if (_type.category() == Type::Category::Function)
solUnimplementedAssert(dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::Internal, "");
string functionName = "prepare_store_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
@@ -2718,12 +2809,13 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
_to.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(addr, functionId) -> outAddr, outFunctionId {
outAddr := addr
function <functionName>(<?external>addr, </external>functionId) -> <?external>outAddr, </external>outFunctionId {
<?external>outAddr := addr</external>
outFunctionId := functionId
}
)")
("functionName", functionName)
("external", fromType.kind() == FunctionType::Kind::External)
.render();
});
}
+5 -1
View File
@@ -205,7 +205,11 @@ public:
/// @returns the name of a function that will copy array from calldata or memory to storage
/// signature (to_slot, from_ptr) ->
std::string copyArrayToStorage(ArrayType const& _fromType, ArrayType const& _toType);
std::string copyArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
/// @returns the name of a function that will copy a byte array from calldata or memory to storage
/// signature (to_slot, from_ptr) ->
std::string copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
/// Returns the name of a function that will convert a given length to the
/// size in memory (number of storage slots or calldata/memory bytes) it
@@ -425,7 +425,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
"(" <<
("add(" + mpos + ", " + to_string(i * arrayType.memoryStride()) + ")") <<
", " <<
converted.name() <<
converted.commaSeparatedList() <<
")\n";
}
}
@@ -2543,47 +2543,50 @@ string IRGeneratorForStatements::binaryOperation(
!TokenTraits::isShiftOp(_operator),
"Have to use specific shift operation function for shifts."
);
if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
string fun;
if (TokenTraits::isBitOp(_operator))
{
string fun;
solAssert(
_type.category() == Type::Category::Integer ||
_type.category() == Type::Category::FixedBytes,
"");
switch (_operator)
{
case Token::BitOr: fun = "or"; break;
case Token::BitXor: fun = "xor"; break;
case Token::BitAnd: fun = "and"; break;
default: break;
}
}
else if (TokenTraits::isArithmeticOp(_operator))
{
IntegerType const* type = dynamic_cast<IntegerType const*>(&_type);
solAssert(type, "");
bool checked = m_context.arithmetic() == Arithmetic::Checked;
switch (_operator)
{
case Token::Add:
fun = checked ? m_utils.overflowCheckedIntAddFunction(*type) : m_utils.wrappingIntAddFunction(*type);
break;
case Token::Sub:
fun = checked ? m_utils.overflowCheckedIntSubFunction(*type) : m_utils.wrappingIntSubFunction(*type);
break;
case Token::Mul:
fun = checked ? m_utils.overflowCheckedIntMulFunction(*type) : m_utils.wrappingIntMulFunction(*type);
break;
case Token::Div:
fun = checked ? m_utils.overflowCheckedIntDivFunction(*type) : m_utils.wrappingIntDivFunction(*type);
break;
case Token::Mod:
fun = m_utils.intModFunction(*type);
break;
case Token::BitOr:
fun = "or";
break;
case Token::BitXor:
fun = "xor";
break;
case Token::BitAnd:
fun = "and";
break;
default:
break;
case Token::Add:
fun = checked ? m_utils.overflowCheckedIntAddFunction(*type) : m_utils.wrappingIntAddFunction(*type);
break;
case Token::Sub:
fun = checked ? m_utils.overflowCheckedIntSubFunction(*type) : m_utils.wrappingIntSubFunction(*type);
break;
case Token::Mul:
fun = checked ? m_utils.overflowCheckedIntMulFunction(*type) : m_utils.wrappingIntMulFunction(*type);
break;
case Token::Div:
fun = checked ? m_utils.overflowCheckedIntDivFunction(*type) : m_utils.wrappingIntDivFunction(*type);
break;
case Token::Mod:
fun = m_utils.intModFunction(*type);
break;
default:
break;
}
solUnimplementedAssert(!fun.empty(), "");
return fun + "(" + _left + ", " + _right + ")\n";
}
else
solUnimplementedAssert(false, "");
return {};
solUnimplementedAssert(!fun.empty(), "Type: " + _type.toString());
return fun + "(" + _left + ", " + _right + ")\n";
}
std::string IRGeneratorForStatements::shiftOperation(