| 2673 | } |
| 2674 | |
| 2675 | std::string YulUtilFunctions::bytesOrStringConcatFunction( |
| 2676 | std::vector<Type const*> const& _argumentTypes, |
| 2677 | FunctionType::Kind _functionTypeKind |
| 2678 | ) |
| 2679 | { |
| 2680 | solAssert(_functionTypeKind == FunctionType::Kind::BytesConcat || _functionTypeKind == FunctionType::Kind::StringConcat); |
| 2681 | std::string functionName = (_functionTypeKind == FunctionType::Kind::StringConcat) ? "string_concat" : "bytes_concat"; |
| 2682 | size_t totalParams = 0; |
| 2683 | std::vector<Type const*> targetTypes; |
| 2684 | |
| 2685 | for (Type const* argumentType: _argumentTypes) |
| 2686 | { |
| 2687 | if (_functionTypeKind == FunctionType::Kind::StringConcat) |
| 2688 | solAssert(argumentType->isImplicitlyConvertibleTo(*TypeProvider::stringMemory())); |
| 2689 | else if (_functionTypeKind == FunctionType::Kind::BytesConcat) |
| 2690 | solAssert( |
| 2691 | argumentType->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()) || |
| 2692 | argumentType->isImplicitlyConvertibleTo(*TypeProvider::fixedBytes(32)) |
| 2693 | ); |
| 2694 | |
| 2695 | if (argumentType->category() == Type::Category::FixedBytes) |
| 2696 | targetTypes.emplace_back(argumentType); |
| 2697 | else if ( |
| 2698 | auto const* literalType = dynamic_cast<StringLiteralType const*>(argumentType); |
| 2699 | literalType && !literalType->value().empty() && literalType->value().size() <= 32 |
| 2700 | ) |
| 2701 | targetTypes.emplace_back(TypeProvider::fixedBytes(static_cast<unsigned>(literalType->value().size()))); |
| 2702 | else |
| 2703 | { |
| 2704 | solAssert(!dynamic_cast<RationalNumberType const*>(argumentType)); |
| 2705 | targetTypes.emplace_back( |
| 2706 | _functionTypeKind == FunctionType::Kind::StringConcat ? |
| 2707 | TypeProvider::stringMemory() : |
| 2708 | TypeProvider::bytesMemory() |
| 2709 | ); |
| 2710 | } |
| 2711 | totalParams += argumentType->sizeOnStack(); |
| 2712 | functionName += "_" + argumentType->identifier(); |
| 2713 | } |
| 2714 | return m_functionCollector.createFunction(functionName, [&]() { |
| 2715 | Whiskers templ(R"( |
| 2716 | function <functionName>(<parameters>) -> outPtr { |
| 2717 | outPtr := <allocateUnbounded>() |
| 2718 | let dataStart := add(outPtr, 0x20) |
| 2719 | let dataEnd := <encodePacked>(dataStart<?+parameters>, <parameters></+parameters>) |
| 2720 | mstore(outPtr, sub(dataEnd, dataStart)) |
| 2721 | <finalizeAllocation>(outPtr, sub(dataEnd, outPtr)) |
| 2722 | } |
| 2723 | )"); |
| 2724 | templ("functionName", functionName); |
| 2725 | templ("parameters", suffixedVariableNameList("param_", 0, totalParams)); |
| 2726 | templ("allocateUnbounded", allocateUnboundedFunction()); |
| 2727 | templ("finalizeAllocation", finalizeAllocationFunction()); |
| 2728 | templ( |
| 2729 | "encodePacked", |
| 2730 | ABIFunctions{m_evmVersion, m_eofVersion, m_revertStrings, m_functionCollector}.tupleEncoderPacked( |
| 2731 | _argumentTypes, |
| 2732 | targetTypes |
no test coverage detected