mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #12046 from ethereum/asm-immutable-refs
Proper source mapping for immutables.
This commit is contained in:
		
						commit
						eb0c73dafd
					
				| @ -13,6 +13,7 @@ Compiler Features: | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Bugfixes: | Bugfixes: | ||||||
|  |  * Code Generator: Fix constructor source mappings for immutables. | ||||||
|  * Commandline Interface: Fix extra newline character being appended to sources passed through standard input, affecting their hashes. |  * Commandline Interface: Fix extra newline character being appended to sources passed through standard input, affecting their hashes. | ||||||
|  * Commandline Interface: Report output selection options unsupported by the selected input mode instead of ignoring them. |  * Commandline Interface: Report output selection options unsupported by the selected input mode instead of ignoring them. | ||||||
|  * SMTChecker: Fix internal error in magic type access (``block``, ``msg``, ``tx``). |  * SMTChecker: Fix internal error in magic type access (``block``, ``msg``, ``tx``). | ||||||
|  | |||||||
| @ -64,3 +64,7 @@ This means the following source mappings represent the same information: | |||||||
| ``1:2:1;1:9:1;2:1:2;2:1:2;2:1:2`` | ``1:2:1;1:9:1;2:1:2;2:1:2;2:1:2`` | ||||||
| 
 | 
 | ||||||
| ``1:2:1;:9;2:1:2;;`` | ``1:2:1;:9;2:1:2;;`` | ||||||
|  | 
 | ||||||
|  | Important to note is that when the :ref:`verbatim <yul-verbatim>` builtin is used, | ||||||
|  | the source mappings will be invalid: The builtin is considered a single | ||||||
|  | instruction instead of potentially multiple. | ||||||
|  | |||||||
| @ -1002,6 +1002,8 @@ within one Yul subobject. If at least one ``memoryguard`` call is found in a sub | |||||||
| the additional optimiser steps will be run on it. | the additional optimiser steps will be run on it. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | .. _yul-verbatim: | ||||||
|  | 
 | ||||||
| verbatim | verbatim | ||||||
| ^^^^^^^^ | ^^^^^^^^ | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -48,11 +48,11 @@ using namespace solidity::evmasm; | |||||||
| using namespace solidity::langutil; | using namespace solidity::langutil; | ||||||
| using namespace solidity::util; | using namespace solidity::util; | ||||||
| 
 | 
 | ||||||
| AssemblyItem const& Assembly::append(AssemblyItem const& _i) | AssemblyItem const& Assembly::append(AssemblyItem _i) | ||||||
| { | { | ||||||
| 	assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow."); | 	assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow."); | ||||||
| 	m_deposit += static_cast<int>(_i.deposit()); | 	m_deposit += static_cast<int>(_i.deposit()); | ||||||
| 	m_items.emplace_back(_i); | 	m_items.emplace_back(move(_i)); | ||||||
| 	if (!m_items.back().location().isValid() && m_currentSourceLocation.isValid()) | 	if (!m_items.back().location().isValid() && m_currentSourceLocation.isValid()) | ||||||
| 		m_items.back().setLocation(m_currentSourceLocation); | 		m_items.back().setLocation(m_currentSourceLocation); | ||||||
| 	m_items.back().m_modifierDepth = m_currentModifierDepth; | 	m_items.back().m_modifierDepth = m_currentModifierDepth; | ||||||
| @ -68,7 +68,7 @@ unsigned Assembly::codeSize(unsigned subTagSize) const | |||||||
| 			ret += i.second.size(); | 			ret += i.second.size(); | ||||||
| 
 | 
 | ||||||
| 		for (AssemblyItem const& i: m_items) | 		for (AssemblyItem const& i: m_items) | ||||||
| 			ret += i.bytesRequired(tagSize); | 			ret += i.bytesRequired(tagSize, Precision::Approximate); | ||||||
| 		if (numberEncodingSize(ret) <= tagSize) | 		if (numberEncodingSize(ret) <= tagSize) | ||||||
| 			return static_cast<unsigned>(ret); | 			return static_cast<unsigned>(ret); | ||||||
| 	} | 	} | ||||||
| @ -696,8 +696,11 @@ LinkerObject const& Assembly::assemble() const | |||||||
| 			break; | 			break; | ||||||
| 		case PushImmutable: | 		case PushImmutable: | ||||||
| 			ret.bytecode.push_back(static_cast<uint8_t>(Instruction::PUSH32)); | 			ret.bytecode.push_back(static_cast<uint8_t>(Instruction::PUSH32)); | ||||||
|  | 			// Maps keccak back to the "identifier" string of that immutable.
 | ||||||
| 			ret.immutableReferences[i.data()].first = m_immutables.at(i.data()); | 			ret.immutableReferences[i.data()].first = m_immutables.at(i.data()); | ||||||
|  | 			// Record the bytecode offset of the PUSH32 argument.
 | ||||||
| 			ret.immutableReferences[i.data()].second.emplace_back(ret.bytecode.size()); | 			ret.immutableReferences[i.data()].second.emplace_back(ret.bytecode.size()); | ||||||
|  | 			// Advance bytecode by 32 bytes (default initialized).
 | ||||||
| 			ret.bytecode.resize(ret.bytecode.size() + 32); | 			ret.bytecode.resize(ret.bytecode.size() + 32); | ||||||
| 			break; | 			break; | ||||||
| 		case VerbatimBytecode: | 		case VerbatimBytecode: | ||||||
| @ -705,6 +708,7 @@ LinkerObject const& Assembly::assemble() const | |||||||
| 			break; | 			break; | ||||||
| 		case AssignImmutable: | 		case AssignImmutable: | ||||||
| 		{ | 		{ | ||||||
|  | 			// Expect 2 elements on stack (source, dest_base)
 | ||||||
| 			auto const& offsets = immutableReferencesBySub[i.data()].second; | 			auto const& offsets = immutableReferencesBySub[i.data()].second; | ||||||
| 			for (size_t i = 0; i < offsets.size(); ++i) | 			for (size_t i = 0; i < offsets.size(); ++i) | ||||||
| 			{ | 			{ | ||||||
|  | |||||||
| @ -65,7 +65,7 @@ public: | |||||||
| 	AssemblyItem newPushImmutable(std::string const& _identifier); | 	AssemblyItem newPushImmutable(std::string const& _identifier); | ||||||
| 	AssemblyItem newImmutableAssignment(std::string const& _identifier); | 	AssemblyItem newImmutableAssignment(std::string const& _identifier); | ||||||
| 
 | 
 | ||||||
| 	AssemblyItem const& append(AssemblyItem const& _i); | 	AssemblyItem const& append(AssemblyItem _i); | ||||||
| 	AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); } | 	AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); } | ||||||
| 
 | 
 | ||||||
| 	template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; } | 	template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; } | ||||||
|  | |||||||
| @ -65,7 +65,7 @@ void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag) | |||||||
| 	setData(data); | 	setData(data); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t AssemblyItem::bytesRequired(size_t _addressLength) const | size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision) const | ||||||
| { | { | ||||||
| 	switch (m_type) | 	switch (m_type) | ||||||
| 	{ | 	{ | ||||||
| @ -87,10 +87,25 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength) const | |||||||
| 	case PushImmutable: | 	case PushImmutable: | ||||||
| 		return 1 + 32; | 		return 1 + 32; | ||||||
| 	case AssignImmutable: | 	case AssignImmutable: | ||||||
| 		if (m_immutableOccurrences) | 	{ | ||||||
| 			return 1 + (3 + 32) * *m_immutableOccurrences; | 		unsigned long immutableOccurrences = 0; | ||||||
|  | 
 | ||||||
|  | 		// Skip exact immutables count if no precise count was requested
 | ||||||
|  | 		if (_precision == Precision::Approximate) | ||||||
|  | 			immutableOccurrences = 1; // Assume one immut. ref.
 | ||||||
| 		else | 		else | ||||||
| 			return 1 + (3 + 32) * 1024; // 1024 occurrences are beyond the maximum code size anyways.
 | 		{ | ||||||
|  | 			solAssert(m_immutableOccurrences, "No immutable references. `bytesRequired()` called before assembly()?"); | ||||||
|  | 			immutableOccurrences = m_immutableOccurrences.value(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (immutableOccurrences != 0) | ||||||
|  | 			// (DUP DUP PUSH <n> ADD MSTORE)* (PUSH <n> ADD MSTORE)
 | ||||||
|  | 			return (immutableOccurrences - 1) * (5 + 32) + (3 + 32); | ||||||
|  | 		else | ||||||
|  | 			// POP POP
 | ||||||
|  | 			return 2; | ||||||
|  | 	} | ||||||
| 	case VerbatimBytecode: | 	case VerbatimBytecode: | ||||||
| 		return std::get<2>(*m_verbatimBytecode).size(); | 		return std::get<2>(*m_verbatimBytecode).size(); | ||||||
| 	default: | 	default: | ||||||
| @ -322,6 +337,26 @@ ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item) | |||||||
| 	return _out; | 	return _out; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | size_t AssemblyItem::opcodeCount() const noexcept | ||||||
|  | { | ||||||
|  | 	switch (m_type) | ||||||
|  | 	{ | ||||||
|  | 		case AssemblyItemType::AssignImmutable: | ||||||
|  | 			// Append empty items if this AssignImmutable was referenced more than once.
 | ||||||
|  | 			// For n immutable occurrences the first (n - 1) occurrences will
 | ||||||
|  | 			// generate 5 opcodes and the last will generate 3 opcodes,
 | ||||||
|  | 			// because it is reusing the 2 top-most elements on the stack.
 | ||||||
|  | 			solAssert(m_immutableOccurrences, ""); | ||||||
|  | 
 | ||||||
|  | 			if (m_immutableOccurrences.value() != 0) | ||||||
|  | 				return (*m_immutableOccurrences - 1) * 5 + 3; | ||||||
|  | 			else | ||||||
|  | 				return 2; // two POP's
 | ||||||
|  | 		default: | ||||||
|  | 			return 1; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| std::string AssemblyItem::computeSourceMapping( | std::string AssemblyItem::computeSourceMapping( | ||||||
| 	AssemblyItems const& _items, | 	AssemblyItems const& _items, | ||||||
| 	map<string, unsigned> const& _sourceIndicesMap | 	map<string, unsigned> const& _sourceIndicesMap | ||||||
| @ -334,6 +369,7 @@ std::string AssemblyItem::computeSourceMapping( | |||||||
| 	int prevSourceIndex = -1; | 	int prevSourceIndex = -1; | ||||||
| 	int prevModifierDepth = -1; | 	int prevModifierDepth = -1; | ||||||
| 	char prevJump = 0; | 	char prevJump = 0; | ||||||
|  | 
 | ||||||
| 	for (auto const& item: _items) | 	for (auto const& item: _items) | ||||||
| 	{ | 	{ | ||||||
| 		if (!ret.empty()) | 		if (!ret.empty()) | ||||||
| @ -402,6 +438,9 @@ std::string AssemblyItem::computeSourceMapping( | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		if (item.opcodeCount() > 1) | ||||||
|  | 			ret += string(item.opcodeCount() - 1, ';'); | ||||||
|  | 
 | ||||||
| 		prevStart = location.start; | 		prevStart = location.start; | ||||||
| 		prevLength = length; | 		prevLength = length; | ||||||
| 		prevSourceIndex = sourceIndex; | 		prevSourceIndex = sourceIndex; | ||||||
|  | |||||||
| @ -27,6 +27,7 @@ | |||||||
| #include <liblangutil/SourceLocation.h> | #include <liblangutil/SourceLocation.h> | ||||||
| #include <libsolutil/Common.h> | #include <libsolutil/Common.h> | ||||||
| #include <libsolutil/Assertions.h> | #include <libsolutil/Assertions.h> | ||||||
|  | #include <optional> | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <sstream> | #include <sstream> | ||||||
| 
 | 
 | ||||||
| @ -51,6 +52,8 @@ enum AssemblyItemType | |||||||
| 	VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification.
 | 	VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification.
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | enum class Precision { Precise , Approximate }; | ||||||
|  | 
 | ||||||
| class Assembly; | class Assembly; | ||||||
| class AssemblyItem; | class AssemblyItem; | ||||||
| using AssemblyItems = std::vector<AssemblyItem>; | using AssemblyItems = std::vector<AssemblyItem>; | ||||||
| @ -147,7 +150,10 @@ public: | |||||||
| 
 | 
 | ||||||
| 	/// @returns an upper bound for the number of bytes required by this item, assuming that
 | 	/// @returns an upper bound for the number of bytes required by this item, assuming that
 | ||||||
| 	/// the value of a jump tag takes @a _addressLength bytes.
 | 	/// the value of a jump tag takes @a _addressLength bytes.
 | ||||||
| 	size_t bytesRequired(size_t _addressLength) const; | 	/// @param _precision Whether to return a precise count (which involves
 | ||||||
|  | 	///                   counting immutable references which are only set after
 | ||||||
|  | 	///                   a call to `assemble()`) or an approx. count.
 | ||||||
|  | 	size_t bytesRequired(size_t _addressLength, Precision _precision = Precision::Precise) const; | ||||||
| 	size_t arguments() const; | 	size_t arguments() const; | ||||||
| 	size_t returnValues() const; | 	size_t returnValues() const; | ||||||
| 	size_t deposit() const { return returnValues() - arguments(); } | 	size_t deposit() const { return returnValues() - arguments(); } | ||||||
| @ -169,9 +175,11 @@ public: | |||||||
| 
 | 
 | ||||||
| 	size_t m_modifierDepth = 0; | 	size_t m_modifierDepth = 0; | ||||||
| 
 | 
 | ||||||
| 	void setImmutableOccurrences(size_t _n) const { m_immutableOccurrences = std::make_shared<size_t>(_n); } | 	void setImmutableOccurrences(size_t _n) const { m_immutableOccurrences = _n; } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  | 	size_t opcodeCount() const noexcept; | ||||||
|  | 
 | ||||||
| 	AssemblyItemType m_type; | 	AssemblyItemType m_type; | ||||||
| 	Instruction m_instruction; ///< Only valid if m_type == Operation
 | 	Instruction m_instruction; ///< Only valid if m_type == Operation
 | ||||||
| 	std::shared_ptr<u256> m_data; ///< Only valid if m_type != Operation
 | 	std::shared_ptr<u256> m_data; ///< Only valid if m_type != Operation
 | ||||||
| @ -184,14 +192,14 @@ private: | |||||||
| 	/// e.g. PushSubSize, PushTag, PushSub, etc.
 | 	/// e.g. PushSubSize, PushTag, PushSub, etc.
 | ||||||
| 	mutable std::shared_ptr<u256> m_pushedValue; | 	mutable std::shared_ptr<u256> m_pushedValue; | ||||||
| 	/// Number of PushImmutable's with the same hash. Only used for AssignImmutable.
 | 	/// Number of PushImmutable's with the same hash. Only used for AssignImmutable.
 | ||||||
| 	mutable std::shared_ptr<size_t> m_immutableOccurrences; | 	mutable std::optional<size_t> m_immutableOccurrences; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| inline size_t bytesRequired(AssemblyItems const& _items, size_t _addressLength) | inline size_t bytesRequired(AssemblyItems const& _items, size_t _addressLength,  Precision _precision = Precision::Precise) | ||||||
| { | { | ||||||
| 	size_t size = 0; | 	size_t size = 0; | ||||||
| 	for (AssemblyItem const& item: _items) | 	for (AssemblyItem const& item: _items) | ||||||
| 		size += item.bytesRequired(_addressLength); | 		size += item.bytesRequired(_addressLength, _precision); | ||||||
| 	return size; | 	return size; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -103,7 +103,7 @@ bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const | |||||||
| 
 | 
 | ||||||
| size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items) | size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items) | ||||||
| { | { | ||||||
| 	return evmasm::bytesRequired(_items, 3); // assume 3 byte addresses
 | 	return evmasm::bytesRequired(_items, 3, Precision::Approximate); // assume 3 byte addresses
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ConstantOptimisationMethod::replaceConstants( | void ConstantOptimisationMethod::replaceConstants( | ||||||
|  | |||||||
| @ -63,7 +63,7 @@ template<typename RangeType> | |||||||
| uint64_t codeSize(RangeType const& _itemRange) | uint64_t codeSize(RangeType const& _itemRange) | ||||||
| { | { | ||||||
| 	return ranges::accumulate(_itemRange | ranges::views::transform( | 	return ranges::accumulate(_itemRange | ranges::views::transform( | ||||||
| 		[](auto const& _item) { return _item.bytesRequired(2); } | 		[](auto const& _item) { return _item.bytesRequired(2, Precision::Approximate); } | ||||||
| 	), 0u); | 	), 0u); | ||||||
| } | } | ||||||
| /// @returns the tag id, if @a _item is a PushTag or Tag into the current subassembly, nullopt otherwise.
 | /// @returns the tag id, if @a _item is a PushTag or Tag into the current subassembly, nullopt otherwise.
 | ||||||
|  | |||||||
| @ -388,6 +388,8 @@ size_t numberOfPops(AssemblyItems const& _items) | |||||||
| 
 | 
 | ||||||
| bool PeepholeOptimiser::optimise() | bool PeepholeOptimiser::optimise() | ||||||
| { | { | ||||||
|  | 	// Avoid referencing immutables too early by using approx. counting in bytesRequired()
 | ||||||
|  | 	auto const approx = evmasm::Precision::Approximate; | ||||||
| 	OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)}; | 	OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)}; | ||||||
| 	while (state.i < m_items.size()) | 	while (state.i < m_items.size()) | ||||||
| 		applyMethods( | 		applyMethods( | ||||||
| @ -398,7 +400,7 @@ bool PeepholeOptimiser::optimise() | |||||||
| 		); | 		); | ||||||
| 	if (m_optimisedItems.size() < m_items.size() || ( | 	if (m_optimisedItems.size() < m_items.size() || ( | ||||||
| 		m_optimisedItems.size() == m_items.size() && ( | 		m_optimisedItems.size() == m_items.size() && ( | ||||||
| 			evmasm::bytesRequired(m_optimisedItems, 3) < evmasm::bytesRequired(m_items, 3) || | 			evmasm::bytesRequired(m_optimisedItems, 3, approx) < evmasm::bytesRequired(m_items, 3, approx) || | ||||||
| 			numberOfPops(m_optimisedItems) > numberOfPops(m_items) | 			numberOfPops(m_optimisedItems) > numberOfPops(m_items) | ||||||
| 		) | 		) | ||||||
| 	)) | 	)) | ||||||
|  | |||||||
| @ -21,15 +21,16 @@ | |||||||
|  * Tests for the assembler. |  * Tests for the assembler. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <libsolutil/JSON.h> |  | ||||||
| #include <libevmasm/Assembly.h> | #include <libevmasm/Assembly.h> | ||||||
|  | #include <libsolutil/JSON.h> | ||||||
|  | #include <libyul/Exceptions.h> | ||||||
| 
 | 
 | ||||||
| #include <boost/test/unit_test.hpp> | #include <boost/test/unit_test.hpp> | ||||||
| 
 | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <memory> | ||||||
| #include <string> | #include <string> | ||||||
| #include <tuple> | #include <tuple> | ||||||
| #include <memory> |  | ||||||
| #include <libyul/Exceptions.h> |  | ||||||
| 
 | 
 | ||||||
| using namespace std; | using namespace std; | ||||||
| using namespace solidity::langutil; | using namespace solidity::langutil; | ||||||
| @ -165,6 +166,89 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) | |||||||
| 	); | 	); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps) | ||||||
|  | { | ||||||
|  | 	// Tests for 1, 2, 3 number of immutables.
 | ||||||
|  | 	for (int numImmutables = 1; numImmutables <= 3; ++numImmutables) | ||||||
|  | 	{ | ||||||
|  | 		BOOST_TEST_MESSAGE("NumImmutables: "s + to_string(numImmutables)); | ||||||
|  | 		// Tests for the cases 1, 2, 3 occurrences of an immutable reference.
 | ||||||
|  | 		for (int numActualRefs = 1; numActualRefs <= 3; ++numActualRefs) | ||||||
|  | 		{ | ||||||
|  | 			BOOST_TEST_MESSAGE("NumActualRefs: "s + to_string(numActualRefs)); | ||||||
|  | 			auto const NumExpectedMappings = | ||||||
|  | 				( | ||||||
|  | 					2 +                        // PUSH <a> PUSH <b>
 | ||||||
|  | 					(numActualRefs - 1) * 5 +  // DUP DUP PUSH <n> ADD MTOSRE
 | ||||||
|  | 					3                          // PUSH <n> ADD MSTORkhbE
 | ||||||
|  | 				) * numImmutables; | ||||||
|  | 
 | ||||||
|  | 			auto constexpr NumSubs = 1; | ||||||
|  | 			auto constexpr NumOpcodesWithoutMappings = | ||||||
|  | 				NumSubs +                  // PUSH <addr> for every sub assembly
 | ||||||
|  | 				1;                         // INVALID
 | ||||||
|  | 
 | ||||||
|  | 			auto assemblyName = make_shared<string>("root.asm"); | ||||||
|  | 			auto subName = make_shared<string>("sub.asm"); | ||||||
|  | 
 | ||||||
|  | 			map<string, unsigned> indices = { | ||||||
|  | 				{ *assemblyName, 0 }, | ||||||
|  | 				{ *subName, 1 } | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			auto subAsm = make_shared<Assembly>(); | ||||||
|  | 			for (char i = 0; i < numImmutables; ++i) | ||||||
|  | 			{ | ||||||
|  | 				for (int r = 0; r < numActualRefs; ++r) | ||||||
|  | 				{ | ||||||
|  | 					subAsm->setSourceLocation(SourceLocation{10*i, 10*i + 6 + r, subName}); | ||||||
|  | 					subAsm->appendImmutable(string(1, char('a' + i))); // "a", "b", ...
 | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Assembly assembly; | ||||||
|  | 			for (char i = 1; i <= numImmutables; ++i) | ||||||
|  | 			{ | ||||||
|  | 				assembly.setSourceLocation({10*i, 10*i + 3+i, assemblyName}); | ||||||
|  | 				assembly.append(u256(0x71));              // immutble value
 | ||||||
|  | 				assembly.append(u256(0));                 // target... modules?
 | ||||||
|  | 				assembly.appendImmutableAssignment(string(1, char('a' + i - 1))); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			assembly.appendSubroutine(subAsm); | ||||||
|  | 
 | ||||||
|  | 			checkCompilation(assembly); | ||||||
|  | 
 | ||||||
|  | 			string const sourceMappings = AssemblyItem::computeSourceMapping(assembly.items(), indices); | ||||||
|  | 			auto const numberOfMappings = std::count(sourceMappings.begin(), sourceMappings.end(), ';'); | ||||||
|  | 
 | ||||||
|  | 			LinkerObject const& obj = assembly.assemble(); | ||||||
|  | 			string const disassembly = disassemble(obj.bytecode, "\n"); | ||||||
|  | 			auto const numberOfOpcodes = std::count(disassembly.begin(), disassembly.end(), '\n'); | ||||||
|  | 
 | ||||||
|  | 			#if 0 // {{{ debug prints
 | ||||||
|  | 			{ | ||||||
|  | 				LinkerObject const& subObj = assembly.sub(0).assemble(); | ||||||
|  | 				string const subDisassembly = disassemble(subObj.bytecode, "\n"); | ||||||
|  | 				cout << '\n'; | ||||||
|  | 				cout << "### immutables: " << numImmutables << ", refs: " << numActualRefs << '\n'; | ||||||
|  | 				cout << " - srcmap: \"" << sourceMappings << "\"\n"; | ||||||
|  | 				cout << " - src mappings: " << numberOfMappings << '\n'; | ||||||
|  | 				cout << " - opcodes: " << numberOfOpcodes << '\n'; | ||||||
|  | 				cout << " - subs: " << assembly.numSubs() << '\n'; | ||||||
|  | 				cout << " - sub opcodes " << std::count(subDisassembly.begin(), subDisassembly.end(), '\n') << '\n'; | ||||||
|  | 				cout << " - sub srcmaps " << AssemblyItem::computeSourceMapping(subAsm->items(), indices) << '\n'; | ||||||
|  | 				cout << " - main bytecode:\n\t" << disassemble(obj.bytecode, "\n\t"); | ||||||
|  | 				cout << "\r - sub bytecode:\n\t" << disassemble(subObj.bytecode, "\n\t"); | ||||||
|  | 			} | ||||||
|  | 			#endif // }}}
 | ||||||
|  | 
 | ||||||
|  | 			BOOST_REQUIRE_EQUAL(NumExpectedMappings, numberOfMappings); | ||||||
|  | 			BOOST_REQUIRE_EQUAL(NumExpectedMappings, numberOfOpcodes - NumOpcodesWithoutMappings); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| BOOST_AUTO_TEST_CASE(immutable) | BOOST_AUTO_TEST_CASE(immutable) | ||||||
| { | { | ||||||
| 	map<string, unsigned> indices = { | 	map<string, unsigned> indices = { | ||||||
|  | |||||||
| @ -17,4 +17,4 @@ object "a" { | |||||||
| //   assignImmutable("0x85a5b1db611c82c46f5fa18e39ae218397536256c451e5de155a86de843a9ad6") | //   assignImmutable("0x85a5b1db611c82c46f5fa18e39ae218397536256c451e5de155a86de843a9ad6") | ||||||
| // Bytecode: 73123456789012345678901234567890123456789060005050 | // Bytecode: 73123456789012345678901234567890123456789060005050 | ||||||
| // Opcodes: PUSH20 0x1234567890123456789012345678901234567890 PUSH1 0x0 POP POP | // Opcodes: PUSH20 0x1234567890123456789012345678901234567890 PUSH1 0x0 POP POP | ||||||
| // SourceMappings: 167:42:0:-:0;58:1;32:187 | // SourceMappings: 167:42:0:-:0;58:1;32:187; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user