From 3cfeca33c19fbf1a9a09907f0c5b1454b1618431 Mon Sep 17 00:00:00 2001
From: Bhargava Shastry <bhargava.shastry@ethereum.org>
Date: Mon, 23 Sep 2019 17:46:46 +0200
Subject: [PATCH 1/3] Abiv2 proto fuzzer: Refactor and add support for structs

---
 test/tools/ossfuzz/abiV2Proto.proto |   39 +-
 test/tools/ossfuzz/protoToAbiV2.cpp | 1550 ++++++++++++++-------------
 test/tools/ossfuzz/protoToAbiV2.h   |  714 ++++++++----
 3 files changed, 1339 insertions(+), 964 deletions(-)

diff --git a/test/tools/ossfuzz/abiV2Proto.proto b/test/tools/ossfuzz/abiV2Proto.proto
index bf5f782f9..c634e545c 100644
--- a/test/tools/ossfuzz/abiV2Proto.proto
+++ b/test/tools/ossfuzz/abiV2Proto.proto
@@ -17,30 +17,6 @@
 
 syntax = "proto2";
 
-// Flattened specification of array dimension
-// If is_static is false, and this array dimension is contained
-// inside another dimension e.g., x[][2] ([2] being the outer dimension)
-// then `length` for this dimension is the length of the first dynamically
-// sized array. The other (n-1) lengths are unspecified
-message ArrayDimensionInfo {
-    required uint32 length = 1;
-    required bool is_static = 2;
-}
-
-// TODO: Add more base types
-// See https://github.com/ethereum/solidity/issues/6749
-message ArrayType {
-    oneof base_type_oneof {
-        IntegerType inty = 1;
-        FixedByteType byty = 2;
-        AddressType adty = 3;
-        StructType stty = 4;
-        BoolType boolty = 5;
-        DynamicByteArrayType dynbytesty = 6;
-    }
-    repeated ArrayDimensionInfo info = 7;
-}
-
 // bool
 message BoolType {}
 
@@ -78,6 +54,16 @@ message DynamicByteArrayType {
     required DType type = 1;
 }
 
+message ArrayType {
+    required Type t = 1;
+    required uint32 length = 2;
+    required bool is_static = 3;
+}
+
+message StructType {
+    repeated Type t = 1;
+}
+
 message NonValueType {
     oneof nonvalue_type_oneof {
         DynamicByteArrayType dynbytearray = 1;
@@ -86,6 +72,8 @@ message NonValueType {
     }
 }
 
+// TODO: Add more types
+// See https://github.com/ethereum/solidity/issues/6749
 message Type {
     oneof type_oneof {
         ValueType vtype = 1;
@@ -93,9 +81,6 @@ message Type {
     }
 }
 
-// TODO: This must not reference itself either directly or indirectly
-message StructType {}
-
 message VarDecl {
     required Type type = 1;
 }
diff --git a/test/tools/ossfuzz/protoToAbiV2.cpp b/test/tools/ossfuzz/protoToAbiV2.cpp
index b20fca810..aa35c80c8 100644
--- a/test/tools/ossfuzz/protoToAbiV2.cpp
+++ b/test/tools/ossfuzz/protoToAbiV2.cpp
@@ -1,732 +1,224 @@
-#include <regex>
-#include <numeric>
-#include <boost/range/adaptor/reversed.hpp>
 #include <test/tools/ossfuzz/protoToAbiV2.h>
-#include <libdevcore/StringUtils.h>
-#include <libdevcore/Whiskers.h>
-#include <liblangutil/Exceptions.h>
 
 using namespace std;
 using namespace dev;
 using namespace dev::test::abiv2fuzzer;
 
-// Create a new variable declaration and append said variable to function parameter lists
-// of coder functions.
-// Declared name is x_<i>; parameterized name is c_<i>
-// where <i> is a monotonically increasing integer.
-void ProtoConverter::createDeclAndParamList(
-	std::string const& _type,
-	DataType _dataType,
-	std::string& _varName,
-	std::string& _paramName
-)
-{
-	auto varNames = newVarNames(getNextVarCounter());
-	_varName = varNames.first;
-	_paramName = varNames.second;
-
-	// Declare array
-	appendVarDeclToOutput(_type, _varName, getQualifier(_dataType));
-
-	// Add typed params for calling public and external functions with said type
-	appendTypedParams(
-		CalleeType::PUBLIC,
-		isValueType(_dataType),
-		_type,
-		_paramName,
-		((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
-	);
-	appendTypedParams(
-		CalleeType::EXTERNAL,
-		isValueType(_dataType),
-		_type,
-		_paramName,
-		((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
-	);
-}
-
-void ProtoConverter::visitArrayType(std::string const& _baseType, ArrayType const& _x)
-{
-	std::string type = arrayTypeAsString(_baseType, _x);
-	std::string varName, paramName;
-	createDeclAndParamList(type, DataType::ARRAY, varName, paramName);
-	// Resize-initialize array and add checks
-	resizeInitArray(_x, _baseType, varName, paramName);
-}
-
-void ProtoConverter::visitType(
-	DataType _dataType,
-	std::string const& _type,
-	std::string const& _value
-)
-{
-	std::string varName, paramName;
-	createDeclAndParamList(_type, _dataType, varName, paramName);
-	addCheckedVarDef(_dataType, varName, paramName, _value);
-}
-
-void ProtoConverter::appendVarDeclToOutput(
-	std::string const& _type,
-	std::string const& _varName,
-	std::string const& _qualifier
+string ProtoConverter::appendVarDeclToOutput(
+	string const& _type,
+	string const& _varName,
+	string const& _qualifier
 )
 {
 	// One level of indentation for state variable declarations
 	// Two levels of indentation for local variable declarations
-	m_output << Whiskers(R"(
+	return Whiskers(R"(
 	<?isLocalVar>	</isLocalVar><type><?qual> <qualifier></qual> <varName>;)"
-	)
+		)
 		("isLocalVar", !m_isStateVar)
 		("type", _type)
 		("qual", !_qualifier.empty())
 		("qualifier", _qualifier)
 		("varName", _varName)
-		.render();
+		.render() +
+		"\n";
 }
 
-void ProtoConverter::appendChecks(
-	DataType _type,
-	std::string const& _varName,
-	std::string const& _rhs
+pair<string, string> ProtoConverter::visit(Type const& _type)
+{
+	switch (_type.type_oneof_case())
+	{
+	case Type::kVtype:
+		return visit(_type.vtype());
+	case Type::kNvtype:
+		return visit(_type.nvtype());
+	case Type::TYPE_ONEOF_NOT_SET:
+		return make_pair("", "");
+	}
+}
+
+pair<string, string> ProtoConverter::visit(ValueType const& _type)
+{
+	switch (_type.value_type_oneof_case())
+	{
+	case ValueType::kBoolty:
+		return visit(_type.boolty());
+	case ValueType::kInty:
+		return visit(_type.inty());
+	case ValueType::kByty:
+		return visit(_type.byty());
+	case ValueType::kAdty:
+		return visit(_type.adty());
+	case ValueType::VALUE_TYPE_ONEOF_NOT_SET:
+		return make_pair("", "");
+	}
+}
+
+pair<string, string> ProtoConverter::visit(NonValueType const& _type)
+{
+	switch (_type.nonvalue_type_oneof_case())
+	{
+	case NonValueType::kDynbytearray:
+		return visit(_type.dynbytearray());
+	case NonValueType::kArrtype:
+		if (ValidityVisitor().visit(_type.arrtype()))
+			return visit(_type.arrtype());
+		else
+			return make_pair("", "");
+	case NonValueType::kStype:
+		if (ValidityVisitor().visit(_type.stype()))
+			return visit(_type.stype());
+		else
+			return make_pair("", "");
+	case NonValueType::NONVALUE_TYPE_ONEOF_NOT_SET:
+		return make_pair("", "");
+	}
+}
+
+pair<string, string> ProtoConverter::visit(BoolType const& _type)
+{
+	return processType(_type, true);
+}
+
+pair<string, string> ProtoConverter::visit(IntegerType const& _type)
+{
+	return processType(_type, true);
+}
+
+pair<string, string> ProtoConverter::visit(FixedByteType const& _type)
+{
+	return processType(_type, true);
+}
+
+pair<string, string> ProtoConverter::visit(AddressType const& _type)
+{
+	return processType(_type, true);
+}
+
+pair<string, string> ProtoConverter::visit(DynamicByteArrayType const& _type)
+{
+	return processType(_type, false);
+}
+
+pair<string, string> ProtoConverter::visit(ArrayType const& _type)
+{
+	return processType(_type, false);
+}
+
+pair<string, string> ProtoConverter::visit(StructType const& _type)
+{
+	return processType(_type, false);
+}
+
+template <typename T>
+pair<string, string> ProtoConverter::processType(T const& _type, bool _isValueType)
+{
+	ostringstream local, global;
+	auto varNames = newVarNames(getNextVarCounter());
+	string varName = varNames.first;
+	string paramName = varNames.second;
+	string location{};
+	if (!m_isStateVar && !_isValueType)
+		location = "memory";
+
+	auto varDeclBuffers = varDecl(
+		varName,
+		paramName,
+		_type,
+		_isValueType,
+		location
+	);
+	global << varDeclBuffers.first;
+	local << varDeclBuffers.second;
+	auto assignCheckBuffers = assignChecker(varName, paramName, _type);
+	global << assignCheckBuffers.first;
+	local << assignCheckBuffers.second;
+
+	m_structCounter += m_numStructsAdded;
+	return make_pair(global.str(), local.str());
+}
+
+template <typename T>
+pair<string, string> ProtoConverter::varDecl(
+	string const& _varName,
+	string const& _paramName,
+	T _type,
+	bool _isValueType,
+	string const& _location
 )
 {
-	std::string check = {};
-	switch (_type)
-	{
-	case DataType::STRING:
-		check = Whiskers(R"(!bytesCompare(bytes(<varName>), <value>))")
-			("varName", _varName)
-			("value", _rhs)
-			.render();
-		break;
-	case DataType::BYTES:
-		check = Whiskers(R"(!bytesCompare(<varName>, <value>))")
-			("varName", _varName)
-			("value", _rhs)
-			.render();
-		break;
-	case DataType::VALUE:
-		check = Whiskers(R"(<varName> != <value>)")
-			("varName", _varName)
-			("value", _rhs)
-			.render();
-		break;
-	case DataType::ARRAY:
-		solUnimplemented("Proto ABIv2 fuzzer: Invalid data type.");
-	}
+	ostringstream local, global;
 
-	// Each (failing) check returns a unique value to simplify debugging.
-	m_checks << Whiskers(R"(
-		if (<check>) return <returnVal>;)"
-	)
-		("check", check)
-		("returnVal", std::to_string(m_returnValue++))
-		.render();
+	TypeVisitor tVisitor(m_structCounter);
+	string typeStr = tVisitor.visit(_type);
+	if (typeStr.empty())
+		return make_pair("", "");
+
+	// Append struct defs
+	global << tVisitor.structDef();
+	m_numStructsAdded = tVisitor.numStructs();
+
+	// variable declaration
+	if (m_isStateVar)
+		global << appendVarDeclToOutput(typeStr, _varName, _location);
+	else
+		local << appendVarDeclToOutput(typeStr, _varName, _location);
+
+	// Add typed params for calling public and external functions with said type
+	appendTypedParams(
+		CalleeType::PUBLIC,
+		_isValueType,
+		typeStr,
+		_paramName,
+		((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
+	);
+	appendTypedParams(
+		CalleeType::EXTERNAL,
+		_isValueType,
+		typeStr,
+		_paramName,
+		((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
+	);
+
+	// Update dyn param only if necessary
+	if (tVisitor.isLastDynParamRightPadded())
+		m_isLastDynParamRightPadded = true;
+
+	return make_pair(global.str(), local.str());
 }
 
-void ProtoConverter::addVarDef(std::string const& _varName, std::string const& _rhs)
+template <typename T>
+pair<string, string> ProtoConverter::assignChecker(
+	string const& _varName,
+	string const& _paramName,
+	T _type
+)
 {
-	std::string varDefString = Whiskers(R"(
-		<varName> = <rhs>;)"
-		)
-		("varName", _varName)
-		("rhs", _rhs)
-		.render();
+	ostringstream local;
+	AssignCheckVisitor acVisitor(
+		_varName,
+		_paramName,
+		m_returnValue,
+		m_isStateVar,
+		m_counter,
+		m_structCounter
+	);
+	pair<string, string> assignCheckStrPair = acVisitor.visit(_type);
+	m_returnValue += acVisitor.errorStmts();
+	m_counter += acVisitor.counted();
+
+	m_checks << assignCheckStrPair.second;
 
 	// State variables cannot be assigned in contract-scope
 	// Therefore, we buffer their assignments and
 	// render them in function scope later.
-	if (m_isStateVar)
-		m_statebuffer << varDefString;
-	else
-		m_output << varDefString;
+	local << assignCheckStrPair.first;
+	return make_pair("", local.str());
 }
 
-void ProtoConverter::addCheckedVarDef(
-	DataType _type,
-	std::string const& _varName,
-	std::string const& _paramName,
-	std::string const& _rhs)
+pair<string, string> ProtoConverter::visit(VarDecl const& _x)
 {
-	addVarDef(_varName, _rhs);
-	appendChecks(_type, _paramName, _rhs);
-}
-
-// Runtime check for array length.
-void ProtoConverter::checkResizeOp(std::string const& _paramName, unsigned _len)
-{
-	appendChecks(DataType::VALUE, _paramName + ".length", std::to_string(_len));
-}
-
-std::string ProtoConverter::boolValueAsString(unsigned _counter)
-{
-	return ((_counter % 2) ? "true" : "false");
-}
-
-/* Input(s)
- *   - Unsigned integer to be hashed
- *   - Width of desired uint value
- * Processing
- *   - Take hash of first parameter and mask it with the max unsigned value for given bit width
- * Output
- *   - string representation of uint value
- */
-std::string ProtoConverter::uintValueAsString(unsigned _width, unsigned _counter)
-{
-	solAssert(
-		(_width % 8 == 0),
-		"Proto ABIv2 Fuzzer: Unsigned integer width is not a multiple of 8"
-	);
-	return maskUnsignedIntToHex(_counter, _width/4);
-}
-
-/* Input(s)
- *   - counter to be hashed to derive a value for Integer type
- *   - Width of desired int value
- * Processing
- *   - Take hash of first parameter and mask it with the max signed value for given bit width
- * Output
- *   - string representation of int value
- */
-std::string ProtoConverter::intValueAsString(unsigned _width, unsigned _counter)
-{
-	solAssert(
-		(_width % 8 == 0),
-		"Proto ABIv2 Fuzzer: Signed integer width is not a multiple of 8"
-	);
-	return maskUnsignedIntToHex(_counter, ((_width/4) - 1));
-}
-
-std::string ProtoConverter::addressValueAsString(unsigned _counter)
-{
-	return Whiskers(R"(address(<value>))")
-		("value", uintValueAsString(160, _counter))
-		.render();
-}
-
-std::string ProtoConverter::croppedString(
-	unsigned _numBytes,
-	unsigned _counter,
-	bool _isHexLiteral
-)
-{
-	// _numBytes can not be zero or exceed 32 bytes
-	solAssert(
-		_numBytes > 0 && _numBytes <= 32,
-		"Proto ABIv2 fuzzer: Too short or too long a cropped string"
-	);
-
-	// Number of masked nibbles is twice the number of bytes for a
-	// hex literal of _numBytes bytes. For a string literal, each nibble
-	// is treated as a character.
-	unsigned numMaskNibbles = _isHexLiteral ? _numBytes * 2 : _numBytes;
-
-	// Start position of substring equals totalHexStringLength - numMaskNibbles
-	// totalHexStringLength = 64 + 2 = 66
-	// e.g., 0x12345678901234567890123456789012 is a total of 66 characters
-	//      |---------------------^-----------|
-	//      <--- start position---><--numMask->
-	//      <-----------total length --------->
-	// Note: This assumes that maskUnsignedIntToHex() invokes toHex(..., HexPrefix::Add)
-	unsigned startPos = 66 - numMaskNibbles;
-	// Extracts the least significant numMaskNibbles from the result
-	// of maskUnsignedIntToHex().
-	return maskUnsignedIntToHex(
-		_counter,
-		numMaskNibbles
-	).substr(startPos, numMaskNibbles);
-}
-
-std::string ProtoConverter::hexValueAsString(
-	unsigned _numBytes,
-	unsigned _counter,
-	bool _isHexLiteral,
-	bool _decorate
-)
-{
-	solAssert(_numBytes > 0 && _numBytes <= 32,
-		"Proto ABIv2 fuzzer: Invalid hex length"
-	);
-
-	// If _decorate is set, then we return a hex"" or a "" string.
-	if (_numBytes == 0)
-		return Whiskers(R"(<?decorate><?isHex>hex</isHex>""</decorate>)")
-			("decorate", _decorate)
-			("isHex", _isHexLiteral)
-			.render();
-
-	// This is needed because solidity interprets a 20-byte 0x prefixed hex literal as an address
-	// payable type.
-	return Whiskers(R"(<?decorate><?isHex>hex</isHex>"</decorate><value><?decorate>"</decorate>)")
-		("decorate", _decorate)
-		("isHex", _isHexLiteral)
-		("value", croppedString(_numBytes, _counter, _isHexLiteral))
-		.render();
-}
-
-std::string ProtoConverter::variableLengthValueAsString(
-	unsigned _numBytes,
-	unsigned _counter,
-	bool _isHexLiteral
-)
-{
-	solAssert(_numBytes >= 0 && _numBytes <= s_maxDynArrayLength,
-		"Proto ABIv2 fuzzer: Invalid hex length"
-	);
-	if (_numBytes == 0)
-		return Whiskers(R"(<?isHex>hex</isHex>"")")
-			("isHex", _isHexLiteral)
-			.render();
-
-	unsigned numBytesRemaining = _numBytes;
-	// Stores the literal
-	string output{};
-	// If requested value is shorter than or exactly 32 bytes,
-	// the literal is the return value of hexValueAsString.
-	if (numBytesRemaining <= 32)
-		output = hexValueAsString(
-			numBytesRemaining,
-			_counter,
-			_isHexLiteral,
-			/*decorate=*/false
-		);
-	// If requested value is longer than 32 bytes, the literal
-	// is obtained by duplicating the return value of hexValueAsString
-	// until we reach a value of the requested size.
-	else
-	{
-		// Create a 32-byte value to be duplicated and
-		// update number of bytes to be appended.
-		// Stores the cached literal that saves us
-		// (expensive) calls to keccak256.
-		string cachedString = hexValueAsString(
-			/*numBytes=*/32,
-			_counter,
-			_isHexLiteral,
-			/*decorate=*/false
-		);
-		output = cachedString;
-		numBytesRemaining -= 32;
-
-		// Append bytes from cachedString until
-		// we create a value of desired length.
-		unsigned numAppendedBytes;
-		while (numBytesRemaining > 0)
-		{
-			// We append at most 32 bytes at a time
-			numAppendedBytes = numBytesRemaining >= 32 ? 32 : numBytesRemaining;
-			output += cachedString.substr(
-				0,
-				// Double the substring length for hex literals since each
-				// character is actually half a byte (or a nibble).
-				_isHexLiteral ? numAppendedBytes * 2 : numAppendedBytes
-			);
-			numBytesRemaining -= numAppendedBytes;
-		}
-		solAssert(
-			numBytesRemaining == 0,
-			"Proto ABIv2 fuzzer: Logic flaw in variable literal creation"
-		);
-	}
-
-	if (_isHexLiteral)
-		solAssert(
-			output.size() == 2 * _numBytes,
-			"Proto ABIv2 fuzzer: Generated hex literal is of incorrect length"
-		);
-	else
-		solAssert(
-			output.size() == _numBytes,
-			"Proto ABIv2 fuzzer: Generated string literal is of incorrect length"
-		);
-
-	// Decorate output
-	return Whiskers(R"(<?isHexLiteral>hex</isHexLiteral>"<value>")")
-		("isHexLiteral", _isHexLiteral)
-		("value", output)
-		.render();
-}
-
-std::string ProtoConverter::fixedByteValueAsString(unsigned _width, unsigned _counter)
-{
-	solAssert(
-		(_width >= 1 && _width <= 32),
-		"Proto ABIv2 Fuzzer: Fixed byte width is not between 1--32"
-	);
-	return hexValueAsString(_width, _counter, /*isHexLiteral=*/true);
-}
-
-std::string ProtoConverter::integerValueAsString(bool _sign, unsigned _width, unsigned _counter)
-{
-	if (_sign)
-		return intValueAsString(_width, _counter);
-	else
-		return uintValueAsString(_width, _counter);
-}
-
-std::string ProtoConverter::bytesArrayTypeAsString(DynamicByteArrayType const& _x)
-{
-	switch (_x.type())
-	{
-	case DynamicByteArrayType::BYTES:
-		return "bytes";
-	case DynamicByteArrayType::STRING:
-		return "string";
-	}
-}
-
-std::string ProtoConverter::structTypeAsString(StructType const&)
-{
-	// TODO: Implement this
-	return {};
-}
-
-void ProtoConverter::visit(BoolType const&)
-{
-	visitType(
-		DataType::VALUE,
-		getBoolTypeAsString(),
-		boolValueAsString(getNextCounter())
-	);
-}
-
-void ProtoConverter::visit(IntegerType const& _x)
-{
-	visitType(
-		DataType::VALUE,
-		getIntTypeAsString(_x),
-		integerValueAsString(isIntSigned(_x), getIntWidth(_x), getNextCounter())
-	);
-}
-
-void ProtoConverter::visit(AddressType const& _x)
-{
-	visitType(
-		DataType::VALUE,
-		getAddressTypeAsString(_x),
-		addressValueAsString(getNextCounter())
-	);
-}
-
-void ProtoConverter::visit(FixedByteType const& _x)
-{
-	visitType(
-		DataType::VALUE,
-		getFixedByteTypeAsString(_x),
-		fixedByteValueAsString(getFixedByteWidth(_x), getNextCounter())
-	);
-}
-
-void ProtoConverter::visit(ValueType const& _x)
-{
-	switch (_x.value_type_oneof_case())
-	{
-		case ValueType::kInty:
-			visit(_x.inty());
-			break;
-		case ValueType::kByty:
-			visit(_x.byty());
-			break;
-		case ValueType::kAdty:
-			visit(_x.adty());
-			break;
-		case ValueType::kBoolty:
-			visit(_x.boolty());
-			break;
-		case ValueType::VALUE_TYPE_ONEOF_NOT_SET:
-			break;
-	}
-}
-
-void ProtoConverter::visit(DynamicByteArrayType const& _x)
-{
-	bool isBytes = _x.type() == DynamicByteArrayType::BYTES;
-	visitType(
-		isBytes ? DataType::BYTES : DataType::STRING,
-		bytesArrayTypeAsString(_x),
-		bytesArrayValueAsString(
-			getNextCounter(),
-			isBytes
-		)
-	);
-	// Update right padding of type
-	m_isLastDynParamRightPadded = true;
-}
-
-// TODO: Implement struct visitor
-void ProtoConverter::visit(StructType const&)
-{
-}
-
-std::string ProtoConverter::arrayDimInfoAsString(ArrayDimensionInfo const& _x)
-{
-	return Whiskers(R"([<?isStatic><length></isStatic>])")
-		("isStatic", _x.is_static())
-		("length", std::to_string(getStaticArrayLengthFromFuzz(_x.length())))
-		.render();
-}
-
-void ProtoConverter::arrayDimensionsAsStringVector(
-	ArrayType const& _x,
-	std::vector<std::string>& _vecOfStr)
-{
-	solAssert(_x.info_size() > 0, "Proto ABIv2 Fuzzer: Array dimensions empty.");
-	for (auto const& dim: _x.info())
-		_vecOfStr.push_back(arrayDimInfoAsString(dim));
-}
-
-ProtoConverter::VecOfBoolUnsigned ProtoConverter::arrayDimensionsAsPairVector(
-	ArrayType const& _x
-)
-{
-	VecOfBoolUnsigned arrayDimsPairVector = {};
-	for (auto const& dim: _x.info())
-		arrayDimsPairVector.push_back(arrayDimInfoAsPair(dim));
-	solAssert(!arrayDimsPairVector.empty(), "Proto ABIv2 Fuzzer: Array dimensions empty.");
-	return arrayDimsPairVector;
-}
-
-std::string ProtoConverter::getValueByBaseType(ArrayType const& _x)
-{
-	switch (_x.base_type_oneof_case())
-	{
-	case ArrayType::kInty:
-		return integerValueAsString(isIntSigned(_x.inty()), getIntWidth(_x.inty()), getNextCounter());
-	case ArrayType::kByty:
-		return fixedByteValueAsString(getFixedByteWidth(_x.byty()), getNextCounter());
-	case ArrayType::kAdty:
-		return addressValueAsString(getNextCounter());
-	case ArrayType::kBoolty:
-		return boolValueAsString(getNextCounter());
-	case ArrayType::kDynbytesty:
-		return bytesArrayValueAsString(
-			getNextCounter(),
-			_x.dynbytesty().type() == DynamicByteArrayType::BYTES
-		);
-	// TODO: Implement structs.
-	case ArrayType::kStty:
-	case ArrayType::BASE_TYPE_ONEOF_NOT_SET:
-		solAssert(false, "Proto ABIv2 fuzzer: Invalid array base type");
-	}
-}
-
-ProtoConverter::DataType ProtoConverter::getDataTypeByBaseType(ArrayType const& _x)
-{
-	switch (_x.base_type_oneof_case())
-	{
-	case ArrayType::kInty:
-	case ArrayType::kByty:
-	case ArrayType::kAdty:
-	case ArrayType::kBoolty:
-		return DataType::VALUE;
-	case ArrayType::kDynbytesty:
-		return getDataTypeOfDynBytesType(_x.dynbytesty());
-	case ArrayType::kStty:
-	case ArrayType::BASE_TYPE_ONEOF_NOT_SET:
-		solUnimplemented("Proto ABIv2 fuzzer: Invalid array base type");
-	}
-}
-
-// Adds a resize operation for a given dimension of type `_type` and expression referenced
-// by `_var`. `_isStatic` is true for statically sized dimensions, false otherwise.
-// `_arrayLen` is equal to length of statically sized array dimension. For dynamically
-// sized dimension, we use `getDynArrayLengthFromFuzz()` and a monotonically increasing
-// counter to obtain actual length. Function returns dimension length.
-unsigned ProtoConverter::resizeDimension(
-	bool _isStatic,
-	unsigned _arrayLen,
-	std::string const& _var,
-	std::string const& _param,
-	std::string const& _type
-)
-{
-	unsigned length;
-	if (_isStatic)
-		length = _arrayLen;
-	else
-	{
-		length = getDynArrayLengthFromFuzz(_arrayLen, getNextCounter());
-
-		// If local var, new T(l);
-		// Else, l;
-		std::string lhs, rhs;
-		if (m_isStateVar)
-		{
-			lhs = _var + ".length";
-			rhs = Whiskers(R"(<length>)")
-				("length", std::to_string(length))
-				.render();
-		}
-		else
-		{
-			lhs = _var;
-			rhs = Whiskers(R"(new <type>(<length>))")
-				("type", _type)
-				("length", std::to_string(length))
-				.render();
-		}
-		// If local var, x = new T(l);
-		// Else, x.length = l;
-		addVarDef(lhs, rhs);
-	}
-
-	// if (c.length != l)
-	checkResizeOp(_param, length);
-	return length;
-}
-
-void ProtoConverter::resizeHelper(
-	ArrayType const& _x,
-	std::vector<std::string> _arrStrVec,
-	VecOfBoolUnsigned _arrInfoVec,
-	std::string const& _varName,
-	std::string const& _paramName
-)
-{
-	// Initialize value expressions if we have arrived at leaf node,
-	// (depth-first) recurse otherwise.
-	if (_arrInfoVec.empty())
-	{
-		// expression name is _var
-		// value is a value of base type
-		std::string value = getValueByBaseType(_x);
-		// add assignment and check
-		DataType dataType = getDataTypeByBaseType(_x);
-		addCheckedVarDef(dataType, _varName, _paramName, value);
-	}
-	else
-	{
-		auto& dim = _arrInfoVec.back();
-
-		std::string type = std::accumulate(
-			_arrStrVec.begin(),
-			_arrStrVec.end(),
-			std::string("")
-		);
-		unsigned length = resizeDimension(dim.first, dim.second, _varName, _paramName, type);
-		// Recurse one level dimension down.
-		_arrStrVec.pop_back();
-		_arrInfoVec.pop_back();
-		for (unsigned i = 0; i < length; i++)
-			resizeHelper(
-				_x,
-				_arrStrVec,
-				_arrInfoVec,
-				_varName + "[" + std::to_string(i) + "]",
-				_paramName + "[" + std::to_string(i) + "]"
-			);
-	}
-}
-
-// This function takes care of properly resizing and initializing ArrayType.
-// In parallel, it adds runtime checks on array bound and values.
-void ProtoConverter::resizeInitArray(
-	ArrayType const& _x,
-	std::string const& _baseType,
-	std::string const& _varName,
-	std::string const& _paramName
-)
-{
-	VecOfBoolUnsigned arrInfoVec = arrayDimensionsAsPairVector(_x);
-	std::vector<std::string> arrStrVec = {_baseType};
-	arrayDimensionsAsStringVector(_x, arrStrVec);
-	resizeHelper(_x, arrStrVec, arrInfoVec, _varName, _paramName);
-}
-
-// Returns array type from it's base type (e.g., int8) and array dimensions info contained in
-// ArrayType.
-std::string ProtoConverter::arrayTypeAsString(std::string const& _baseType, ArrayType const& _x)
-{
-	std::vector<std::string> typeStringVec = {_baseType};
-	arrayDimensionsAsStringVector(_x, typeStringVec);
-
-	return std::accumulate(
-		typeStringVec.begin(),
-		typeStringVec.end(),
-		std::string("")
-	);
-}
-
-void ProtoConverter::visit(ArrayType const& _x)
-{
-	// Bail out if input contains too few or too many dimensions.
-	if (_x.info_size() == 0 || _x.info_size() > (int)s_maxArrayDimensions)
-		return;
-
-	// Array type is dynamically encoded if one of the following is true
-	//   - array base type is "bytes" or "string"
-	//   - at least one array dimension is dynamically sized.
-	if (_x.base_type_oneof_case() == ArrayType::kDynbytesty)
-		m_isLastDynParamRightPadded = true;
-	else
-		for (auto const& dim: _x.info())
-			if (!dim.is_static())
-			{
-				m_isLastDynParamRightPadded = true;
-				break;
-			}
-
-	string baseType = {};
-	switch (_x.base_type_oneof_case())
-	{
-	case ArrayType::kInty:
-		baseType = getIntTypeAsString(_x.inty());
-		break;
-	case ArrayType::kByty:
-		baseType = getFixedByteTypeAsString(_x.byty());
-		break;
-	case ArrayType::kAdty:
-		baseType = getAddressTypeAsString(_x.adty());
-		break;
-	case ArrayType::kBoolty:
-		baseType = getBoolTypeAsString();
-		break;
-	case ArrayType::kDynbytesty:
-		baseType = bytesArrayTypeAsString(_x.dynbytesty());
-		break;
-	case ArrayType::kStty:
-	case ArrayType::BASE_TYPE_ONEOF_NOT_SET:
-		return;
-	}
-	visitArrayType(baseType, _x);
-}
-
-void ProtoConverter::visit(NonValueType const& _x)
-{
-	switch (_x.nonvalue_type_oneof_case())
-	{
-	case NonValueType::kDynbytearray:
-		visit(_x.dynbytearray());
-		break;
-	case NonValueType::kArrtype:
-		visit(_x.arrtype());
-		break;
-	case NonValueType::kStype:
-		visit(_x.stype());
-		break;
-	case NonValueType::NONVALUE_TYPE_ONEOF_NOT_SET:
-		break;
-	}
-}
-
-void ProtoConverter::visit(Type const& _x)
-{
-	switch (_x.type_oneof_case())
-	{
-	case Type::kVtype:
-		visit(_x.vtype());
-		break;
-	case Type::kNvtype:
-		visit(_x.nvtype());
-		break;
-	case Type::TYPE_ONEOF_NOT_SET:
-		break;
-	}
-}
-
-void ProtoConverter::visit(VarDecl const& _x)
-{
-	visit(_x.type());
+	return visit(_x.type());
 }
 
 std::string ProtoConverter::equalityChecksAsString()
@@ -821,20 +313,34 @@ std::string ProtoConverter::typedParametersAsString(CalleeType _calleeType)
 }
 
 /// Test function to be called externally.
-void ProtoConverter::visit(TestFunction const& _x)
+string ProtoConverter::visit(TestFunction const& _x, string const& _storageVarDefs)
 {
-	m_output << R"(
-
-	function test() public returns (uint) {
-	)";
-
-	// Define state variables in function scope
-	m_output << m_statebuffer.str();
-
 	// TODO: Support more than one but less than N local variables
-	visit(_x.local_vars());
+	auto localVarBuffers = visit(_x.local_vars());
 
-	m_output << Whiskers(R"(
+	string structTypeDecl = localVarBuffers.first;
+	string localVarDefs = localVarBuffers.second;
+
+	ostringstream testBuffer;
+	string functionDecl = "function test() public returns (uint)";
+	testBuffer << Whiskers(R"(<structTypeDecl>
+	<functionDecl> {
+<storageVarDefs>
+<localVarDefs>
+<testCode>
+	})")
+		("structTypeDecl", structTypeDecl)
+		("functionDecl", functionDecl)
+		("storageVarDefs", _storageVarDefs)
+		("localVarDefs", localVarDefs)
+		("testCode", testCode(_x.invalid_encoding_length()))
+		.render();
+	return testBuffer.str();
+}
+
+string ProtoConverter::testCode(unsigned _invalidLength)
+{
+	return Whiskers(R"(
 		uint returnVal = this.coder_public(<parameterNames>);
 		if (returnVal != 0)
 			return returnVal;
@@ -865,18 +371,18 @@ void ProtoConverter::visit(TestFunction const& _x)
 			return uint(200000) + returnVal;
 		</atLeastOneVar>
 		return 0;
-	}
-	)")
-	("parameterNames", dev::suffixedVariableNameList(s_varNamePrefix, 0, m_varCounter))
-	("invalidLengthFuzz", std::to_string(_x.invalid_encoding_length()))
-	("isRightPadded", isLastDynParamRightPadded() ? "true" : "false")
-	("atLeastOneVar", m_varCounter > 0)
-	.render();
+		)")
+		("parameterNames", dev::suffixedVariableNameList(s_varNamePrefix, 0, m_varCounter))
+		("invalidLengthFuzz", std::to_string(_invalidLength))
+		("isRightPadded", isLastDynParamRightPadded() ? "true" : "false")
+		("atLeastOneVar", m_varCounter > 0)
+		.render();
 }
 
-void ProtoConverter::writeHelperFunctions()
+string ProtoConverter::helperFunctions()
 {
-	m_output << R"(
+	stringstream helperFuncs;
+	helperFuncs << R"(
 	function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) {
 		if(a.length != b.length)
 			return false;
@@ -954,14 +460,14 @@ void ProtoConverter::writeHelperFunctions()
 	// These are callee functions that encode from storage, decode to
 	// memory/calldata and check if decoded value matches storage value
 	// return true on successful match, false otherwise
-	m_output << Whiskers(R"(
+	helperFuncs << Whiskers(R"(
 	function coder_public(<parameters_memory>) public pure returns (uint) {
-		<equality_checks>
+<equality_checks>
 		return 0;
 	}
 
 	function coder_external(<parameters_calldata>) external pure returns (uint) {
-		<equality_checks>
+<equality_checks>
 		return 0;
 	}
 	)")
@@ -969,26 +475,610 @@ void ProtoConverter::writeHelperFunctions()
 	("equality_checks", equalityChecksAsString())
 	("parameters_calldata", typedParametersAsString(CalleeType::EXTERNAL))
 	.render();
+	return helperFuncs.str();
 }
 
 void ProtoConverter::visit(Contract const& _x)
 {
-	m_output << R"(pragma solidity >=0.0;
-pragma experimental ABIEncoderV2;
+	string pragmas = R"(pragma solidity >=0.0;
+pragma experimental ABIEncoderV2;)";
 
-contract C {
-)";
 	// TODO: Support more than one but less than N state variables
-	visit(_x.state_vars());
+	auto storageBuffers = visit(_x.state_vars());
+	string storageVarDecls = storageBuffers.first;
+	string storageVarDefs = storageBuffers.second;
 	m_isStateVar = false;
-	// Test function
-	visit(_x.testfunction());
-	writeHelperFunctions();
-	m_output << "\n}";
+	string testFunction = visit(_x.testfunction(), storageVarDefs);
+	/* Structure of contract body
+	 * - Storage variable declarations
+	 * - Struct type declarations
+	 * - Test function
+     *     - Storage variable definitions
+	 *     - Local variable definitions
+	 *     - Test code proper (calls public and external functions)
+	 * - Helper functions
+	 */
+	ostringstream contractBody;
+	contractBody << storageVarDecls
+	             << testFunction
+	             << helperFunctions();
+	m_output << Whiskers(R"(<pragmas>
+<contractStart>
+<contractBody>
+<contractEnd>)")
+		("pragmas", pragmas)
+		("contractStart", "contract C {")
+		("contractBody", contractBody.str())
+		("contractEnd", "}")
+		.render();
 }
 
 string ProtoConverter::contractToString(Contract const& _input)
 {
 	visit(_input);
 	return m_output.str();
+}
+
+/// Type visitor
+string TypeVisitor::visit(BoolType const&)
+{
+	m_baseType = "bool";
+	return m_baseType;
+}
+
+string TypeVisitor::visit(IntegerType const& _type)
+{
+	m_baseType = getIntTypeAsString(_type);
+	return m_baseType;
+}
+
+string TypeVisitor::visit(FixedByteType const& _type)
+{
+	m_baseType = getFixedByteTypeAsString(_type);
+	return m_baseType;
+}
+
+string TypeVisitor::visit(AddressType const& _type)
+{
+	m_baseType = getAddressTypeAsString(_type);
+	return m_baseType;
+}
+
+string TypeVisitor::visit(ArrayType const& _type)
+{
+	if (!ValidityVisitor().visit(_type))
+		return "";
+	string baseType = visit(_type.t());
+	solAssert(!baseType.empty(), "");
+	string arrayBraces = _type.is_static() ?
+	                     string("[") +
+	                     to_string(getStaticArrayLengthFromFuzz(_type.length())) +
+	                     string("]") :
+	                     string("[]");
+	m_baseType += arrayBraces;
+	if (!_type.is_static())
+		m_isLastDynParamRightPadded = true;
+	return baseType + arrayBraces;
+}
+
+string TypeVisitor::visit(DynamicByteArrayType const& _type)
+{
+	m_isLastDynParamRightPadded = true;
+	m_baseType = bytesArrayTypeAsString(_type);
+	return m_baseType;
+}
+
+void TypeVisitor::structDefinition(StructType const& _type)
+{
+	// Return an empty string if struct is empty
+	solAssert(ValidityVisitor().visit(_type), "");
+
+	// Reset field counter and indentation
+	unsigned wasFieldCounter = m_structFieldCounter;
+	unsigned wasIndentation = m_indentation;
+
+	m_indentation = 1;
+	m_structFieldCounter = 0;
+
+	// Commence struct declaration
+	string structDef = lineString(
+		"struct " +
+		string(s_structNamePrefix) +
+		to_string(m_structCounter) +
+		" {"
+	);
+
+	// Increase indentation for struct fields
+	m_indentation++;
+	for (auto const& t: _type.t())
+	{
+		string type{};
+
+		if (!ValidityVisitor().visit(t))
+			continue;
+
+		TypeVisitor tVisitor(m_structCounter + 1);
+		type = tVisitor.visit(t);
+		m_structCounter += tVisitor.numStructs();
+		m_structDef << tVisitor.structDef();
+
+		solAssert(!type.empty(), "");
+
+		structDef += lineString(
+			Whiskers(R"(<type> <member>;)")
+				("type", type)
+				("member", "m" + to_string(m_structFieldCounter++))
+				.render()
+		);
+	}
+	m_indentation--;
+	structDef += lineString("}");
+	m_structCounter++;
+	m_structDef << structDef;
+	m_indentation = wasIndentation;
+	m_structFieldCounter = wasFieldCounter;
+}
+
+string TypeVisitor::visit(StructType const& _type)
+{
+	if (ValidityVisitor().visit(_type))
+	{
+		// Add struct definition
+		structDefinition(_type);
+		// Set last dyn param if struct contains a dyn param e.g., bytes, array etc.
+		m_isLastDynParamRightPadded = DynParamVisitor().visit(_type);
+		// If top-level struct is a non-emtpy struct, assign the name S<suffix>
+		m_baseType = s_structTypeName + to_string(m_structStartCounter);
+	}
+	else
+		m_baseType = {};
+
+	return m_baseType;
+}
+
+/// AssignCheckVisitor implementation
+pair<string, string> AssignCheckVisitor::visit(BoolType const& _type)
+{
+	string value = ValueGetterVisitor(counter()).visit(_type);
+	return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE);
+}
+
+pair<string, string> AssignCheckVisitor::visit(IntegerType const& _type)
+{
+	string value = ValueGetterVisitor(counter()).visit(_type);
+	return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE);
+}
+
+pair<string, string> AssignCheckVisitor::visit(FixedByteType const& _type)
+{
+	string value = ValueGetterVisitor(counter()).visit(_type);
+	return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE);
+}
+
+pair<string, string> AssignCheckVisitor::visit(AddressType const& _type)
+{
+	string value = ValueGetterVisitor(counter()).visit(_type);
+	return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE);
+}
+
+pair<string, string> AssignCheckVisitor::visit(DynamicByteArrayType const& _type)
+{
+	string value = ValueGetterVisitor(counter()).visit(_type);
+	DataType dataType = _type.type() == DynamicByteArrayType::BYTES ? DataType::BYTES :	DataType::STRING;
+	return assignAndCheckStringPair(m_varName, m_paramName, value, value, dataType);
+}
+
+pair<string, string> AssignCheckVisitor::visit(ArrayType const& _type)
+{
+	if (!ValidityVisitor().visit(_type))
+		return make_pair("", "");
+
+	// Obtain type of array to be resized and initialized
+	string typeStr{};
+
+	unsigned wasStructCounter = m_structCounter;
+	TypeVisitor tVisitor(m_structCounter);
+	typeStr = tVisitor.visit(_type);
+
+	pair<string, string> resizeBuffer;
+	string lengthStr;
+	unsigned length;
+
+	// Resize dynamic arrays
+	if (!_type.is_static())
+	{
+		length = getDynArrayLengthFromFuzz(_type.length(), counter());
+		lengthStr = to_string(length);
+		if (m_stateVar)
+			resizeBuffer = assignAndCheckStringPair(
+				m_varName + ".length",
+				m_paramName + ".length",
+				lengthStr,
+				lengthStr,
+				DataType::VALUE
+				);
+		else
+		{
+			// Resizing memory arrays via the new operator
+			string resizeOp = Whiskers(R"(new <fullTypeStr>(<length>))")
+				("fullTypeStr", typeStr)
+				("length", lengthStr)
+				.render();
+			resizeBuffer = assignAndCheckStringPair(
+				m_varName,
+				m_paramName + ".length",
+				resizeOp,
+				lengthStr,
+				DataType::VALUE
+				);
+		}
+	}
+	else
+	{
+		length = getStaticArrayLengthFromFuzz(_type.length());
+		lengthStr = to_string(length);
+		// Add check on length
+		resizeBuffer.second = checkString(m_paramName + ".length", lengthStr, DataType::VALUE);
+	}
+
+	// Add assignCheckBuffer and check statements
+	pair<string, string> assignCheckBuffer;
+	string wasVarName = m_varName;
+	string wasParamName = m_paramName;
+	for (unsigned i = 0; i < length; i++)
+	{
+		m_varName = wasVarName + "[" + to_string(i) + "]";
+		m_paramName = wasParamName + "[" + to_string(i) + "]";
+		pair<string, string> assign = visit(_type.t());
+		assignCheckBuffer.first += assign.first;
+		assignCheckBuffer.second += assign.second;
+		if (i < length - 1)
+			m_structCounter = wasStructCounter;
+	}
+
+	// Since struct visitor won't be called for zero-length
+	// arrays, struct counter will not get incremented. Therefore,
+	// we need to manually force a recursive struct visit.
+	if (length == 0 && TypeVisitor().arrayOfStruct(_type))
+		visit(_type.t());
+
+	m_varName = wasVarName;
+	m_paramName = wasParamName;
+
+	// Compose resize and initialization assignment and check
+	return make_pair(
+		resizeBuffer.first + assignCheckBuffer.first,
+		resizeBuffer.second + assignCheckBuffer.second
+	);
+}
+
+pair<string, string> AssignCheckVisitor::visit(StructType const& _type)
+{
+	if (!ValidityVisitor().visit(_type))
+		return make_pair("", "");
+
+	pair<string, string> assignCheckBuffer;
+	unsigned i = 0;
+
+	// Increment struct counter
+	m_structCounter++;
+
+	string wasVarName = m_varName;
+	string wasParamName = m_paramName;
+
+	for (auto const& t: _type.t())
+	{
+		m_varName = wasVarName + ".m" + to_string(i);
+		m_paramName = wasParamName + ".m" + to_string(i);
+		pair<string, string> assign = visit(t);
+		// If type is not well formed continue without
+		// updating state.
+		if (assign.first.empty() && assign.second.empty())
+			continue;
+		assignCheckBuffer.first += assign.first;
+		assignCheckBuffer.second += assign.second;
+		i++;
+	}
+	m_varName = wasVarName;
+	m_paramName = wasParamName;
+	return assignCheckBuffer;
+}
+
+pair<string, string> AssignCheckVisitor::assignAndCheckStringPair(
+	string const& _varRef,
+	string const& _checkRef,
+	string const& _assignValue,
+	string const& _checkValue,
+	DataType _type
+)
+{
+	return make_pair(assignString(_varRef, _assignValue), checkString(_checkRef, _checkValue, _type));
+}
+
+string AssignCheckVisitor::assignString(string const& _ref, string const& _value)
+{
+	string assignStmt = Whiskers(R"(<ref> = <value>;)")
+		("ref", _ref)
+		("value", _value)
+		.render();
+	return indentation() + assignStmt + "\n";
+}
+
+string AssignCheckVisitor::checkString(string const& _ref, string const& _value, DataType _type)
+{
+	string checkPred;
+	switch (_type)
+	{
+	case DataType::STRING:
+		checkPred = Whiskers(R"(!bytesCompare(bytes(<varName>), <value>))")
+			("varName", _ref)
+			("value", _value)
+			.render();
+		break;
+	case DataType::BYTES:
+		checkPred = Whiskers(R"(!bytesCompare(<varName>, <value>))")
+			("varName", _ref)
+			("value", _value)
+			.render();
+		break;
+	case DataType::VALUE:
+		checkPred = Whiskers(R"(<varName> != <value>)")
+			("varName", _ref)
+			("value", _value)
+			.render();
+		break;
+	case DataType::ARRAY:
+		solUnimplemented("Proto ABIv2 fuzzer: Invalid data type.");
+	}
+	string checkStmt = Whiskers(R"(if (<checkPred>) return <errCode>;)")
+		("checkPred", checkPred)
+		("errCode", to_string(m_errorCode++))
+		.render();
+	return indentation() + checkStmt + "\n";
+}
+
+/// ValueGetterVisitor
+string ValueGetterVisitor::visit(BoolType const&)
+{
+	return counter() % 2 ? "true" : "false";
+}
+
+string ValueGetterVisitor::visit(IntegerType const& _type)
+{
+	return integerValueAsString(
+		_type.is_signed(),
+		getIntWidth(_type),
+		counter()
+	);
+}
+
+string ValueGetterVisitor::visit(FixedByteType const& _type)
+{
+	return fixedByteValueAsString(
+		getFixedByteWidth(_type),
+		counter()
+	);
+}
+
+string ValueGetterVisitor::visit(AddressType const&)
+{
+	return addressValueAsString(counter());
+}
+
+string ValueGetterVisitor::visit(DynamicByteArrayType const& _type)
+{
+	return bytesArrayValueAsString(
+		counter(),
+		getDataTypeOfDynBytesType(_type) == DataType::BYTES
+	);
+}
+
+std::string ValueGetterVisitor::integerValueAsString(bool _sign, unsigned _width, unsigned _counter)
+{
+	if (_sign)
+		return intValueAsString(_width, _counter);
+	else
+		return uintValueAsString(_width, _counter);
+}
+
+/* Input(s)
+ *   - Unsigned integer to be hashed
+ *   - Width of desired uint value
+ * Processing
+ *   - Take hash of first parameter and mask it with the max unsigned value for given bit width
+ * Output
+ *   - string representation of uint value
+ */
+std::string ValueGetterVisitor::uintValueAsString(unsigned _width, unsigned _counter)
+{
+	solAssert(
+		(_width % 8 == 0),
+		"Proto ABIv2 Fuzzer: Unsigned integer width is not a multiple of 8"
+	);
+	return maskUnsignedIntToHex(_counter, _width/4);
+}
+
+/* Input(s)
+ *   - counter to be hashed to derive a value for Integer type
+ *   - Width of desired int value
+ * Processing
+ *   - Take hash of first parameter and mask it with the max signed value for given bit width
+ * Output
+ *   - string representation of int value
+ */
+std::string ValueGetterVisitor::intValueAsString(unsigned _width, unsigned _counter)
+{
+	solAssert(
+		(_width % 8 == 0),
+		"Proto ABIv2 Fuzzer: Signed integer width is not a multiple of 8"
+	);
+	return maskUnsignedIntToHex(_counter, ((_width/4) - 1));
+}
+
+std::string ValueGetterVisitor::croppedString(
+	unsigned _numBytes,
+	unsigned _counter,
+	bool _isHexLiteral
+)
+{
+	solAssert(
+		_numBytes > 0 && _numBytes <= 32,
+		"Proto ABIv2 fuzzer: Too short or too long a cropped string"
+	);
+
+	// Number of masked nibbles is twice the number of bytes for a
+	// hex literal of _numBytes bytes. For a string literal, each nibble
+	// is treated as a character.
+	unsigned numMaskNibbles = _isHexLiteral ? _numBytes * 2 : _numBytes;
+
+	// Start position of substring equals totalHexStringLength - numMaskNibbles
+	// totalHexStringLength = 64 + 2 = 66
+	// e.g., 0x12345678901234567890123456789012 is a total of 66 characters
+	//      |---------------------^-----------|
+	//      <--- start position---><--numMask->
+	//      <-----------total length --------->
+	// Note: This assumes that maskUnsignedIntToHex() invokes toHex(..., HexPrefix::Add)
+	unsigned startPos = 66 - numMaskNibbles;
+	// Extracts the least significant numMaskNibbles from the result
+	// of maskUnsignedIntToHex().
+	return maskUnsignedIntToHex(
+		_counter,
+		numMaskNibbles
+	).substr(startPos, numMaskNibbles);
+}
+
+std::string ValueGetterVisitor::hexValueAsString(
+	unsigned _numBytes,
+	unsigned _counter,
+	bool _isHexLiteral,
+	bool _decorate
+)
+{
+	solAssert(_numBytes > 0 && _numBytes <= 32,
+	          "Proto ABIv2 fuzzer: Invalid hex length"
+	);
+
+	// If _decorate is set, then we return a hex"" or a "" string.
+	if (_numBytes == 0)
+		return Whiskers(R"(<?decorate><?isHex>hex</isHex>""</decorate>)")
+			("decorate", _decorate)
+			("isHex", _isHexLiteral)
+			.render();
+
+	// This is needed because solidity interprets a 20-byte 0x prefixed hex literal as an address
+	// payable type.
+	return Whiskers(R"(<?decorate><?isHex>hex</isHex>"</decorate><value><?decorate>"</decorate>)")
+		("decorate", _decorate)
+		("isHex", _isHexLiteral)
+		("value", croppedString(_numBytes, _counter, _isHexLiteral))
+		.render();
+}
+
+std::string ValueGetterVisitor::fixedByteValueAsString(unsigned _width, unsigned _counter)
+{
+	solAssert(
+		(_width >= 1 && _width <= 32),
+		"Proto ABIv2 Fuzzer: Fixed byte width is not between 1--32"
+	);
+	return hexValueAsString(_width, _counter, /*isHexLiteral=*/true);
+}
+
+std::string ValueGetterVisitor::addressValueAsString(unsigned _counter)
+{
+	return Whiskers(R"(address(<value>))")
+		("value", uintValueAsString(160, _counter))
+		.render();
+}
+
+std::string ValueGetterVisitor::variableLengthValueAsString(
+	unsigned _numBytes,
+	unsigned _counter,
+	bool _isHexLiteral
+)
+{
+	// TODO: Move this to caller
+//	solAssert(_numBytes >= 0 && _numBytes <= s_maxDynArrayLength,
+//	          "Proto ABIv2 fuzzer: Invalid hex length"
+//	);
+	if (_numBytes == 0)
+		return Whiskers(R"(<?isHex>hex</isHex>"")")
+			("isHex", _isHexLiteral)
+			.render();
+
+	unsigned numBytesRemaining = _numBytes;
+	// Stores the literal
+	string output{};
+	// If requested value is shorter than or exactly 32 bytes,
+	// the literal is the return value of hexValueAsString.
+	if (numBytesRemaining <= 32)
+		output = hexValueAsString(
+			numBytesRemaining,
+			_counter,
+			_isHexLiteral,
+			/*decorate=*/false
+		);
+		// If requested value is longer than 32 bytes, the literal
+		// is obtained by duplicating the return value of hexValueAsString
+		// until we reach a value of the requested size.
+	else
+	{
+		// Create a 32-byte value to be duplicated and
+		// update number of bytes to be appended.
+		// Stores the cached literal that saves us
+		// (expensive) calls to keccak256.
+		string cachedString = hexValueAsString(
+			/*numBytes=*/32,
+			             _counter,
+			             _isHexLiteral,
+			/*decorate=*/false
+		);
+		output = cachedString;
+		numBytesRemaining -= 32;
+
+		// Append bytes from cachedString until
+		// we create a value of desired length.
+		unsigned numAppendedBytes;
+		while (numBytesRemaining > 0)
+		{
+			// We append at most 32 bytes at a time
+			numAppendedBytes = numBytesRemaining >= 32 ? 32 : numBytesRemaining;
+			output += cachedString.substr(
+				0,
+				// Double the substring length for hex literals since each
+				// character is actually half a byte (or a nibble).
+				_isHexLiteral ? numAppendedBytes * 2 : numAppendedBytes
+			);
+			numBytesRemaining -= numAppendedBytes;
+		}
+		solAssert(
+			numBytesRemaining == 0,
+			"Proto ABIv2 fuzzer: Logic flaw in variable literal creation"
+		);
+	}
+
+	if (_isHexLiteral)
+		solAssert(
+			output.size() == 2 * _numBytes,
+			"Proto ABIv2 fuzzer: Generated hex literal is of incorrect length"
+		);
+	else
+		solAssert(
+			output.size() == _numBytes,
+			"Proto ABIv2 fuzzer: Generated string literal is of incorrect length"
+		);
+
+	// Decorate output
+	return Whiskers(R"(<?isHexLiteral>hex</isHexLiteral>"<value>")")
+		("isHexLiteral", _isHexLiteral)
+		("value", output)
+		.render();
+}
+
+string ValueGetterVisitor::bytesArrayValueAsString(unsigned _counter, bool _isHexLiteral)
+{
+	return variableLengthValueAsString(
+		getVarLength(_counter),
+		_counter,
+		_isHexLiteral
+	);
 }
\ No newline at end of file
diff --git a/test/tools/ossfuzz/protoToAbiV2.h b/test/tools/ossfuzz/protoToAbiV2.h
index 401867364..a04e36af2 100644
--- a/test/tools/ossfuzz/protoToAbiV2.h
+++ b/test/tools/ossfuzz/protoToAbiV2.h
@@ -1,10 +1,18 @@
 #pragma once
 
+#include <test/tools/ossfuzz/abiV2Proto.pb.h>
+
 #include <libdevcore/FixedHash.h>
 #include <libdevcore/Keccak256.h>
+#include <libdevcore/StringUtils.h>
 #include <libdevcore/Whiskers.h>
-#include <test/tools/ossfuzz/abiV2Proto.pb.h>
+
+#include <liblangutil/Exceptions.h>
+
+#include <boost/variant/static_visitor.hpp>
 #include <boost/algorithm/string.hpp>
+#include <boost/variant.hpp>
+
 #include <ostream>
 #include <sstream>
 
@@ -89,32 +97,29 @@
  */
 
 
-namespace dev
-{
-namespace test
-{
-namespace abiv2fuzzer
-{
+namespace dev {
+namespace test {
+namespace abiv2fuzzer {
+
+/// Converts a protobuf input into a Solidity program that tests
+/// abi coding.
 class ProtoConverter
 {
 public:
-	ProtoConverter():
+	ProtoConverter() :
 		m_isStateVar(true),
 		m_counter(0),
 		m_varCounter(0),
 		m_returnValue(1),
-		m_isLastDynParamRightPadded(false)
+		m_isLastDynParamRightPadded(false),
+		m_structCounter(0),
+		m_numStructsAdded(0)
 	{}
 
 	ProtoConverter(ProtoConverter const&) = delete;
-
 	ProtoConverter(ProtoConverter&&) = delete;
-
 	std::string contractToString(Contract const& _input);
-
 private:
-	using VecOfBoolUnsigned = std::vector<std::pair<bool, unsigned>>;
-
 	enum class Delimiter
 	{
 		ADD,
@@ -125,79 +130,40 @@ private:
 		PUBLIC,
 		EXTERNAL
 	};
-	enum class DataType
-	{
-		BYTES,
-		STRING,
-		VALUE,
-		ARRAY
-	};
-
-	void visit(BoolType const&);
-
-	void visit(IntegerType const&);
-
-	void visit(FixedByteType const&);
-
-	void visit(AddressType const&);
-
-	void visit(ArrayType const&);
-
-	void visit(DynamicByteArrayType const&);
-
-	void visit(StructType const&);
-
-	void visit(ValueType const&);
-
-	void visit(NonValueType const&);
-
-	void visit(Type const&);
-
-	void visit(VarDecl const&);
-
-	void visit(TestFunction const&);
-
+	std::pair<std::string, std::string> visit(VarDecl const&);
+	std::string visit(TestFunction const&, std::string const&);
 	void visit(Contract const&);
 
-	std::string getValueByBaseType(ArrayType const&);
+	template <typename T>
+	std::pair<std::string, std::string> processType(T const& _type, bool _isValueType);
 
-	DataType getDataTypeByBaseType(ArrayType const& _x);
-
-	void resizeInitArray(
-		ArrayType const& _x,
-		std::string const& _baseType,
-		std::string const& _varName,
-		std::string const& _paramName
-	);
-
-	unsigned resizeDimension(
-		bool _isStatic,
-		unsigned _arrayLen,
-		std::string const& _var,
-		std::string const& _param,
-		std::string const& _type
-	);
-
-	void resizeHelper(
-		ArrayType const& _x,
-		std::vector<std::string> _arrStrVec,
-		VecOfBoolUnsigned _arrInfoVec,
-		std::string const& _varName,
-		std::string const& _paramName
-	);
-
-	// Utility functions
-	void appendChecks(DataType _type, std::string const& _varName, std::string const& _rhs);
-
-	void addVarDef(std::string const& _varName, std::string const& _rhs);
-
-	void addCheckedVarDef(
-		DataType _type,
+	template <typename T>
+	std::pair<std::string, std::string> assignChecker(
 		std::string const& _varName,
 		std::string const& _paramName,
-		std::string const& _rhs
+		T _type
 	);
 
+	template <typename T>
+	std::pair<std::string, std::string> varDecl(
+		std::string const& _varName,
+		std::string const& _paramName,
+		T _type,
+		bool _isValueType,
+		std::string const& _location
+	);
+	std::pair<std::string, std::string> visit(Type const&);
+	std::pair<std::string, std::string> visit(ValueType const&);
+	std::pair<std::string, std::string> visit(NonValueType const&);
+	std::pair<std::string, std::string> visit(BoolType const&);
+	std::pair<std::string, std::string> visit(IntegerType const&);
+	std::pair<std::string, std::string> visit(FixedByteType const&);
+	std::pair<std::string, std::string> visit(AddressType const&);
+	std::pair<std::string, std::string> visit(DynamicByteArrayType const&);
+	std::pair<std::string, std::string> visit(ArrayType const&);
+	std::pair<std::string, std::string> visit(StructType const&);
+
+	// Utility functions
 	void appendTypedParams(
 		CalleeType _calleeType,
 		bool _isValueType,
@@ -220,38 +186,23 @@ private:
 		Delimiter _delimiter = Delimiter::ADD
 	);
 
-	void appendVarDeclToOutput(
+	std::string appendVarDeclToOutput(
 		std::string const& _type,
 		std::string const& _varName,
 		std::string const& _qualifier
 	);
 
-	void checkResizeOp(std::string const& _varName, unsigned _len);
-
-	void visitType(DataType _dataType, std::string const& _type, std::string const& _value);
-
-	void visitArrayType(std::string const&, ArrayType const&);
-
-	void createDeclAndParamList(
-		std::string const& _type,
-		DataType _dataType,
-		std::string& _varName,
-		std::string& _paramName
-	);
-
 	std::string equalityChecksAsString();
 
 	std::string typedParametersAsString(CalleeType _calleeType);
 
-	void writeHelperFunctions();
+	std::string helperFunctions();
+
+	std::string testCode(unsigned _invalidLength);
+
+	std::pair<std::string, unsigned> structDecl(StructType const& _type);
 
 	// Function definitions
-	// m_counter is used to derive values for typed variables
-	unsigned getNextCounter()
-	{
-		return m_counter++;
-	}
-
 	// m_varCounter is used to derive declared and parameterized variable names
 	unsigned getNextVarCounter()
 	{
@@ -261,7 +212,7 @@ private:
 	// Accepts an unsigned counter and returns a pair of strings
 	// First string is declared name (s_varNamePrefix<varcounter_value>)
 	// Second string is parameterized name (s_paramPrefix<varcounter_value>)
-	auto newVarNames(unsigned _varCounter)
+	static std::pair<std::string, std::string> newVarNames(unsigned _varCounter)
 	{
 		return std::make_pair(
 			s_varNamePrefix + std::to_string(_varCounter),
@@ -269,62 +220,79 @@ private:
 		);
 	}
 
-	std::string getQualifier(DataType _dataType)
-	{
-		return ((isValueType(_dataType) || m_isStateVar) ? "" : "memory");
-	}
-
 	bool isLastDynParamRightPadded()
 	{
 		return m_isLastDynParamRightPadded;
 	}
 
-	// Static declarations
-	static std::string structTypeAsString(StructType const& _x);
-	static std::string boolValueAsString(unsigned _counter);
-	static std::string intValueAsString(unsigned _width, unsigned _counter);
-	static std::string uintValueAsString(unsigned _width, unsigned _counter);
-	static std::string integerValueAsString(bool _sign, unsigned _width, unsigned _counter);
-	static std::string addressValueAsString(unsigned _counter);
-	static std::string fixedByteValueAsString(unsigned _width, unsigned _counter);
-
-	/// Returns a hex literal if _isHexLiteral is true, a string literal otherwise.
-	/// The size of the returned literal is _numBytes bytes.
-	/// @param _decorate If true, the returned string is enclosed within double quotes
-	/// if _isHexLiteral is false.
-	/// @param _isHexLiteral If true, the returned string is enclosed within
-	/// double quotes prefixed by the string "hex" if _decorate is true. If
-	/// _decorate is false, the returned string is returned as-is.
-	/// @return hex value as string
-	static std::string hexValueAsString(
-		unsigned _numBytes,
-		unsigned _counter,
-		bool _isHexLiteral,
-		bool _decorate = true
-	);
-
-	/// Concatenates the hash value obtained from monotonically increasing counter
-	/// until the desired number of bytes determined by _numBytes.
-	/// @param _width Desired number of bytes for hex value
-	/// @param _counter A counter value used for creating a keccak256 hash
-	/// @param _isHexLiteral Since this routine may be used to construct
-	/// string or hex literals, this flag is used to construct a valid output.
-	/// @return Valid hex or string literal of size _width bytes
-	static std::string variableLengthValueAsString(
-		unsigned _width,
-		unsigned _counter,
-		bool _isHexLiteral
-	);
-	static std::vector<std::pair<bool, unsigned>> arrayDimensionsAsPairVector(ArrayType const& _x);
-	static std::string arrayDimInfoAsString(ArrayDimensionInfo const& _x);
-	static void arrayDimensionsAsStringVector(
-		ArrayType const& _x,
-		std::vector<std::string>&
-	);
-	static std::string bytesArrayTypeAsString(DynamicByteArrayType const& _x);
-	static std::string arrayTypeAsString(std::string const&, ArrayType const&);
 	static std::string delimiterToString(Delimiter _delimiter);
-	static std::string croppedString(unsigned _numBytes, unsigned _counter, bool _isHexLiteral);
+
+	/// Contains the test program
+	std::ostringstream m_output;
+	/// Contains a subset of the test program. This subset contains
+	/// checks to be encoded in the test program
+	std::ostringstream m_checks;
+	/// Contains typed parameter list to be passed to callee functions
+	std::ostringstream m_typedParamsExternal;
+	std::ostringstream m_typedParamsPublic;
+	/// Predicate that is true if we are in contract scope
+	bool m_isStateVar;
+	unsigned m_counter;
+	unsigned m_varCounter;
+	/// Monotonically increasing return value for error reporting
+	unsigned m_returnValue;
+	/// Flag that indicates if last dynamically encoded parameter
+	/// passed to a function call is of a type that is going to be
+	/// right padded by the ABI encoder.
+	bool m_isLastDynParamRightPadded;
+	/// Struct counter
+	unsigned m_structCounter;
+	unsigned m_numStructsAdded;
+	/// Prefixes for declared and parameterized variable names
+	static auto constexpr s_varNamePrefix = "x_";
+	static auto constexpr s_paramNamePrefix = "c_";
+};
+
+/// Visitor interface for Solidity protobuf types.
+template <typename T>
+class AbiV2ProtoVisitor
+{
+public:
+	virtual ~AbiV2ProtoVisitor() = default;
+
+	virtual T visit(BoolType const& _node) = 0;
+	virtual T visit(IntegerType const& _node) = 0;
+	virtual T visit(FixedByteType const& _node) = 0;
+	virtual T visit(AddressType const& _node) = 0;
+	virtual T visit(DynamicByteArrayType const& _node) = 0;
+	virtual T visit(ArrayType const& _node) = 0;
+	virtual T visit(StructType const& _node) = 0;
+	virtual T visit(ValueType const& _node)
+	{
+		return visitValueType(_node);
+	}
+	virtual T visit(NonValueType const& _node)
+	{
+		return visitNonValueType(_node);
+	}
+	virtual T visit(Type const& _node)
+	{
+		return visitType(_node);
+	}
+
+	enum class DataType
+	{
+		BYTES,
+		STRING,
+		VALUE,
+		ARRAY
+	};
+
+	/// Prefixes for declared and parameterized variable names
+	static auto constexpr s_varNamePrefix = "x_";
+	static auto constexpr s_paramNamePrefix = "c_";
+	static auto constexpr s_structNamePrefix = "S";
+	static auto constexpr s_structFieldPrefix = "m";
 
 	// Static function definitions
 	static bool isValueType(DataType _dataType)
@@ -342,11 +310,6 @@ private:
 		return _x.is_signed();
 	}
 
-	static std::string getBoolTypeAsString()
-	{
-		return "bool";
-	}
-
 	static std::string getIntTypeAsString(IntegerType const& _x)
 	{
 		return ((isIntSigned(_x) ? "int" : "uint") + std::to_string(getIntWidth(_x)));
@@ -364,7 +327,7 @@ private:
 
 	static std::string getAddressTypeAsString(AddressType const& _x)
 	{
-		return (_x.payable() ? "address payable": "address");
+		return (_x.payable() ? "address payable" : "address");
 	}
 
 	static DataType getDataTypeOfDynBytesType(DynamicByteArrayType const& _x)
@@ -375,12 +338,6 @@ private:
 			return DataType::BYTES;
 	}
 
-	/// Returns true if input is either a string or bytes, false otherwise.
-	static bool isDataTypeBytesOrString(DataType _type)
-	{
-		return _type == DataType::STRING || _type == DataType::BYTES;
-	}
-
 	// Convert _counter to string and return its keccak256 hash
 	static u256 hashUnsignedInt(unsigned _counter)
 	{
@@ -389,7 +346,7 @@ private:
 
 	static u256 maskUnsignedInt(unsigned _counter, unsigned _numMaskNibbles)
 	{
-	  return hashUnsignedInt(_counter) & u256("0x" + std::string(_numMaskNibbles, 'f'));
+		return hashUnsignedInt(_counter) & u256("0x" + std::string(_numMaskNibbles, 'f'));
 	}
 
 	// Requires caller to pass number of nibbles (twice the number of bytes) as second argument.
@@ -414,15 +371,6 @@ private:
 		return _fuzz % s_maxArrayLength + 1;
 	}
 
-	static std::pair<bool, unsigned> arrayDimInfoAsPair(ArrayDimensionInfo const& _x)
-	{
-		return (
-			_x.is_static() ?
-			std::make_pair(true, getStaticArrayLengthFromFuzz(_x.length())) :
-			std::make_pair(false, getDynArrayLengthFromFuzz(_x.length(), 0))
-		);
-	}
-
 	/// Returns a pseudo-random value for the size of a string/hex
 	/// literal. Used for creating variable length hex/string literals.
 	/// @param _counter Monotonically increasing counter value
@@ -435,48 +383,400 @@ private:
 		return (_counter + 879) * 32 % (s_maxDynArrayLength + 1);
 	}
 
+	static std::string bytesArrayTypeAsString(DynamicByteArrayType const& _x)
+	{
+		switch (_x.type()) {
+		case DynamicByteArrayType::BYTES:
+			return "bytes";
+		case DynamicByteArrayType::STRING:
+			return "string";
+		}
+	}
+protected:
+T visitValueType(ValueType const& _type)
+{
+	switch (_type.value_type_oneof_case())
+	{
+	case ValueType::kInty:
+		return visit(_type.inty());
+	case ValueType::kByty:
+		return visit(_type.byty());
+	case ValueType::kAdty:
+		return visit(_type.adty());
+	case ValueType::kBoolty:
+		return visit(_type.boolty());
+	case ValueType::VALUE_TYPE_ONEOF_NOT_SET:
+		return T();
+	}
+}
+
+T visitNonValueType(NonValueType const& _type)
+{
+	switch (_type.nonvalue_type_oneof_case())
+	{
+	case NonValueType::kDynbytearray:
+		return visit(_type.dynbytearray());
+	case NonValueType::kArrtype:
+		return visit(_type.arrtype());
+	case NonValueType::kStype:
+		return visit(_type.stype());
+	case NonValueType::NONVALUE_TYPE_ONEOF_NOT_SET:
+		return T();
+	}
+}
+
+T visitType(Type const& _type)
+{
+	switch (_type.type_oneof_case())
+	{
+	case Type::kVtype:
+		return visit(_type.vtype());
+	case Type::kNvtype:
+		return visit(_type.nvtype());
+	case Type::TYPE_ONEOF_NOT_SET:
+		return T();
+	}
+}
+private:
+	static unsigned constexpr s_maxArrayLength = 4;
+	static unsigned constexpr s_maxDynArrayLength = 256;
+};
+
+/// Converts a protobuf type into a Solidity type string.
+class TypeVisitor: public AbiV2ProtoVisitor<std::string>
+{
+public:
+	TypeVisitor(unsigned _structSuffix = 0):
+		m_indentation(1),
+		m_structCounter(_structSuffix),
+		m_structStartCounter(_structSuffix),
+		m_structFieldCounter(0),
+		m_isLastDynParamRightPadded(false)
+	{}
+
+	std::string visit(BoolType const&) override;
+	std::string visit(IntegerType const&) override;
+	std::string visit(FixedByteType const&) override;
+	std::string visit(AddressType const&) override;
+	std::string visit(ArrayType const&) override;
+	std::string visit(DynamicByteArrayType const&) override;
+	std::string visit(StructType const&) override;
+	using AbiV2ProtoVisitor<std::string>::visit;
+	std::string baseType()
+	{
+		return m_baseType;
+	}
+	bool isLastDynParamRightPadded()
+	{
+		return m_isLastDynParamRightPadded;
+	}
+	std::string structDef()
+	{
+		return m_structDef.str();
+	}
+	unsigned numStructs()
+	{
+		return m_structCounter - m_structStartCounter;
+	}
+	static bool arrayOfStruct(ArrayType const& _type)
+	{
+		Type const& baseType = _type.t();
+		if (baseType.has_nvtype() && baseType.nvtype().has_stype())
+			return true;
+		else if (baseType.has_nvtype() && baseType.nvtype().has_arrtype())
+			return arrayOfStruct(baseType.nvtype().arrtype());
+		else
+			return false;
+	}
+private:
+	void structDefinition(StructType const&);
+
+	std::string indentation()
+	{
+		return std::string(m_indentation * 1, '\t');
+	}
+	std::string lineString(std::string const& _line)
+	{
+		return indentation() + _line + "\n";
+	}
+
+	std::string m_baseType;
+	std::ostringstream m_structDef;
+	unsigned m_indentation;
+	unsigned m_structCounter;
+	unsigned m_structStartCounter;
+	unsigned m_structFieldCounter;
+	bool m_isLastDynParamRightPadded;
+
+	static auto constexpr s_structTypeName = "S";
+};
+
+/// Returns a pair of strings, first of which contains assignment statements
+/// to initialize a given type, and second of which contains checks to be
+/// placed inside the coder function to test abi en/decoding.
+class AssignCheckVisitor: public AbiV2ProtoVisitor<std::pair<std::string, std::string>>
+{
+public:
+	AssignCheckVisitor(
+		std::string _varName,
+		std::string _paramName,
+		unsigned _errorStart,
+		bool _stateVar,
+		unsigned _counter,
+		unsigned _structCounter
+	)
+	{
+		m_counter = m_counterStart = _counter;
+		m_varName = _varName;
+		m_paramName = _paramName;
+		m_errorCode = m_errorStart = _errorStart;
+		m_indentation = 2;
+		m_stateVar = _stateVar;
+		m_structCounter = m_structStart = _structCounter;
+		m_arrayOfStruct = false;
+	}
+	std::pair<std::string, std::string> visit(BoolType const&) override;
+	std::pair<std::string, std::string> visit(IntegerType const&) override;
+	std::pair<std::string, std::string> visit(FixedByteType const&) override;
+	std::pair<std::string, std::string> visit(AddressType const&) override;
+	std::pair<std::string, std::string> visit(ArrayType const&) override;
+	std::pair<std::string, std::string> visit(DynamicByteArrayType const&) override;
+	std::pair<std::string, std::string> visit(StructType const&) override;
+	using AbiV2ProtoVisitor<std::pair<std::string, std::string>>::visit;
+
+	unsigned errorStmts()
+	{
+		return m_errorCode - m_errorStart;
+	}
+
+	unsigned counted()
+	{
+		return m_counter - m_counterStart;
+	}
+
+	unsigned structs()
+	{
+		return m_structCounter - m_structStart;
+	}
+private:
+	std::string indentation()
+	{
+		return std::string(m_indentation * 1, '\t');
+	}
+	unsigned counter()
+	{
+		return m_counter++;
+	}
+
+	std::pair<std::string, std::string> assignAndCheckStringPair(
+		std::string const& _varRef,
+		std::string const& _checkRef,
+		std::string const& _assignValue,
+		std::string const& _checkValue,
+		DataType _type
+	);
+	std::string assignString(std::string const&, std::string const&);
+	std::string checkString(std::string const&, std::string const&, DataType);
+	unsigned m_counter;
+	unsigned m_counterStart;
+	std::string m_varName;
+	std::string m_paramName;
+	unsigned m_errorCode;
+	unsigned m_errorStart;
+	unsigned m_indentation;
+	bool m_stateVar;
+	unsigned m_structCounter;
+	unsigned m_structStart;
+	bool m_arrayOfStruct;
+};
+
+/// Returns a valid value (as a string) for a given type.
+class ValueGetterVisitor: AbiV2ProtoVisitor<std::string>
+{
+public:
+	ValueGetterVisitor(unsigned _counter = 0): m_counter(_counter) {}
+
+	std::string visit(BoolType const&) override;
+	std::string visit(IntegerType const&) override;
+	std::string visit(FixedByteType const&) override;
+	std::string visit(AddressType const&) override;
+	std::string visit(DynamicByteArrayType const&) override;
+	std::string visit(ArrayType const&) override
+	{
+		solAssert(false, "ABIv2 proto fuzzer: Cannot call valuegettervisitor on complex type");
+	}
+	std::string visit(StructType const&) override
+	{
+		solAssert(false, "ABIv2 proto fuzzer: Cannot call valuegettervisitor on complex type");
+	}
+	using AbiV2ProtoVisitor<std::string>::visit;
+private:
+	unsigned counter()
+	{
+		return m_counter++;
+	}
+
+	static std::string intValueAsString(unsigned _width, unsigned _counter);
+	static std::string uintValueAsString(unsigned _width, unsigned _counter);
+	static std::string integerValueAsString(bool _sign, unsigned _width, unsigned _counter);
+	static std::string addressValueAsString(unsigned _counter);
+	static std::string fixedByteValueAsString(unsigned _width, unsigned _counter);
+
+	/// Returns a hex literal if _isHexLiteral is true, a string literal otherwise.
+	/// The size of the returned literal is _numBytes bytes.
+	/// @param _decorate If true, the returned string is enclosed within double quotes
+	/// if _isHexLiteral is false.
+	/// @param _isHexLiteral If true, the returned string is enclosed within
+	/// double quotes prefixed by the string "hex" if _decorate is true. If
+	/// _decorate is false, the returned string is returned as-is.
+	/// @return hex value as string
+	static std::string hexValueAsString(
+		unsigned _numBytes,
+		unsigned _counter,
+		bool _isHexLiteral,
+		bool _decorate = true
+	);
+
 	/// Returns a hex/string literal of variable length whose value and
 	/// size are pseudo-randomly determined from the counter value.
 	/// @param _counter A monotonically increasing counter value
 	/// @param _isHexLiteral Flag that indicates whether hex (if true) or
 	/// string literal (false) is desired
 	/// @return A variable length hex/string value
-	static std::string bytesArrayValueAsString(unsigned _counter, bool _isHexLiteral)
+	static std::string bytesArrayValueAsString(unsigned _counter, bool _isHexLiteral);
+
+	/// Concatenates the hash value obtained from monotonically increasing counter
+	/// until the desired number of bytes determined by _numBytes.
+	/// @param _width Desired number of bytes for hex value
+	/// @param _counter A counter value used for creating a keccak256 hash
+	/// @param _isHexLiteral Since this routine may be used to construct
+	/// string or hex literals, this flag is used to construct a valid output.
+	/// @return Valid hex or string literal of size _width bytes
+	static std::string variableLengthValueAsString(
+		unsigned _width,
+		unsigned _counter,
+		bool _isHexLiteral
+	);
+
+	/// Returns a value that is @a _numBytes bytes long.
+	/// @param _numBytes: Number of bytes of desired value
+	/// @param _counter: A counter value
+	/// @param _isHexLiteral: True if desired value is a hex literal, false otherwise
+	static std::string croppedString(unsigned _numBytes, unsigned _counter, bool _isHexLiteral);
+
+	unsigned m_counter;
+};
+
+/// Returns true if protobuf array specification is well-formed, false otherwise
+class ValidityVisitor: AbiV2ProtoVisitor<bool>
+{
+public:
+	ValidityVisitor() = default;
+
+	bool visit(BoolType const&) override
 	{
-		return variableLengthValueAsString(
-			getVarLength(_counter),
-			_counter,
-			_isHexLiteral
-		);
+		return true;
 	}
 
-	/// Contains the test program
-	std::ostringstream m_output;
-	/// Temporary storage for state variable definitions
-	std::ostringstream m_statebuffer;
-	/// Contains a subset of the test program. This subset contains
-	/// checks to be encoded in the test program
-	std::ostringstream m_checks;
-	/// Contains typed parameter list to be passed to callee functions
-	std::ostringstream m_typedParamsExternal;
-	std::ostringstream m_typedParamsPublic;
-	/// Predicate that is true if we are in contract scope
-	bool m_isStateVar;
-	unsigned m_counter;
-	unsigned m_varCounter;
-	/// Monotonically increasing return value for error reporting
-	unsigned m_returnValue;
-	/// Flag that indicates if last dynamically encoded parameter
-	/// passed to a function call is of a type that is going to be
-	/// right padded by the ABI encoder.
-	bool m_isLastDynParamRightPadded;
-	static unsigned constexpr s_maxArrayLength = 4;
-	static unsigned constexpr s_maxArrayDimensions = 4;
-	static unsigned constexpr s_maxDynArrayLength = 256;
-	/// Prefixes for declared and parameterized variable names
-	static auto constexpr s_varNamePrefix = "x_";
-	static auto constexpr s_paramNamePrefix = "c_";
+	bool visit(IntegerType const&) override
+	{
+		return true;
+	}
+
+	bool visit(FixedByteType const&) override
+	{
+		return true;
+	}
+
+	bool visit(AddressType const&) override
+	{
+		return true;
+	}
+
+	bool visit(DynamicByteArrayType const&) override
+	{
+		return true;
+	}
+
+	bool visit(ArrayType const& _type) override
+	{
+		return visit(_type.t());
+	}
+
+	bool visit(StructType const& _type) override
+	{
+		for (auto const& t: _type.t())
+			if (visit(t))
+				return true;
+		return false;
+	}
+
+	using AbiV2ProtoVisitor<bool>::visit;
+};
+
+/// Returns true if visited type is dynamically encoded by the abi coder,
+/// false otherwise.
+class DynParamVisitor: AbiV2ProtoVisitor<bool>
+{
+public:
+	DynParamVisitor() = default;
+
+	bool visit(BoolType const&) override
+	{
+		return false;
+	}
+
+	bool visit(IntegerType const&) override
+	{
+		return false;
+	}
+
+	bool visit(FixedByteType const&) override
+	{
+		return false;
+	}
+
+	bool visit(AddressType const&) override
+	{
+		return false;
+	}
+
+	bool visit(DynamicByteArrayType const&) override
+	{
+		return true;
+	}
+
+	bool visit(ArrayType const& _type) override
+	{
+		// Return early if array spec is not well-formed
+		if (!ValidityVisitor().visit(_type))
+			return false;
+
+		// Array is dynamically encoded if it at least one of the following is true
+		//   - at least one dimension is dynamically sized
+		//   - base type is dynamically encoded
+		if (!_type.is_static())
+			return true;
+		else
+			return visit(_type.t());
+	}
+
+	bool visit(StructType const& _type) override
+	{
+		// Return early if empty struct
+		if (!ValidityVisitor().visit(_type))
+			return false;
+
+		// Struct is dynamically encoded if at least one of its fields
+		// is dynamically encoded.
+		for (auto const& t: _type.t())
+			if (visit(t))
+				return true;
+		return false;
+	}
+
+	using AbiV2ProtoVisitor<bool>::visit;
 };
 }
 }
-}
+}
\ No newline at end of file

From 12ed08eddb7610409d5c470a5b4c64598b4a0a36 Mon Sep 17 00:00:00 2001
From: Bhargava Shastry <bhargava.shastry@ethereum.org>
Date: Sun, 6 Oct 2019 09:02:27 +0200
Subject: [PATCH 2/3] Place upper bound number on number of array dimensions

---
 test/tools/ossfuzz/protoToAbiV2.cpp | 1 +
 test/tools/ossfuzz/protoToAbiV2.h   | 9 ++++++---
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/test/tools/ossfuzz/protoToAbiV2.cpp b/test/tools/ossfuzz/protoToAbiV2.cpp
index aa35c80c8..01af91cde 100644
--- a/test/tools/ossfuzz/protoToAbiV2.cpp
+++ b/test/tools/ossfuzz/protoToAbiV2.cpp
@@ -548,6 +548,7 @@ string TypeVisitor::visit(ArrayType const& _type)
 {
 	if (!ValidityVisitor().visit(_type))
 		return "";
+
 	string baseType = visit(_type.t());
 	solAssert(!baseType.empty(), "");
 	string arrayBraces = _type.is_static() ?
diff --git a/test/tools/ossfuzz/protoToAbiV2.h b/test/tools/ossfuzz/protoToAbiV2.h
index a04e36af2..9b6349ea4 100644
--- a/test/tools/ossfuzz/protoToAbiV2.h
+++ b/test/tools/ossfuzz/protoToAbiV2.h
@@ -258,6 +258,7 @@ template <typename T>
 class AbiV2ProtoVisitor
 {
 public:
+	static unsigned constexpr s_maxArrayDimensions = 3;
 	virtual ~AbiV2ProtoVisitor() = default;
 
 	virtual T visit(BoolType const& _node) = 0;
@@ -533,7 +534,6 @@ public:
 		m_indentation = 2;
 		m_stateVar = _stateVar;
 		m_structCounter = m_structStart = _structCounter;
-		m_arrayOfStruct = false;
 	}
 	std::pair<std::string, std::string> visit(BoolType const&) override;
 	std::pair<std::string, std::string> visit(IntegerType const&) override;
@@ -587,7 +587,6 @@ private:
 	bool m_stateVar;
 	unsigned m_structCounter;
 	unsigned m_structStart;
-	bool m_arrayOfStruct;
 };
 
 /// Returns a valid value (as a string) for a given type.
@@ -671,7 +670,7 @@ private:
 class ValidityVisitor: AbiV2ProtoVisitor<bool>
 {
 public:
-	ValidityVisitor() = default;
+	ValidityVisitor(): m_arrayDimensions(0) {}
 
 	bool visit(BoolType const&) override
 	{
@@ -700,6 +699,9 @@ public:
 
 	bool visit(ArrayType const& _type) override
 	{
+		m_arrayDimensions++;
+		if (m_arrayDimensions > s_maxArrayDimensions)
+			return false;
 		return visit(_type.t());
 	}
 
@@ -711,6 +713,7 @@ public:
 		return false;
 	}
 
+	unsigned m_arrayDimensions;
 	using AbiV2ProtoVisitor<bool>::visit;
 };
 

From 04becb94586e5f02e264f3e16387db62c8d2bb82 Mon Sep 17 00:00:00 2001
From: Bhargava Shastry <bhargava.shastry@ethereum.org>
Date: Tue, 8 Oct 2019 02:09:59 +0200
Subject: [PATCH 3/3] Minor refactoring

---
 test/tools/ossfuzz/protoToAbiV2.cpp |  27 ++---
 test/tools/ossfuzz/protoToAbiV2.h   | 149 +++++++++++++++++++++-------
 2 files changed, 127 insertions(+), 49 deletions(-)

diff --git a/test/tools/ossfuzz/protoToAbiV2.cpp b/test/tools/ossfuzz/protoToAbiV2.cpp
index 01af91cde..9415bdcd4 100644
--- a/test/tools/ossfuzz/protoToAbiV2.cpp
+++ b/test/tools/ossfuzz/protoToAbiV2.cpp
@@ -4,7 +4,7 @@ using namespace std;
 using namespace dev;
 using namespace dev::test::abiv2fuzzer;
 
-string ProtoConverter::appendVarDeclToOutput(
+string ProtoConverter::getVarDecl(
 	string const& _type,
 	string const& _varName,
 	string const& _qualifier
@@ -114,9 +114,7 @@ template <typename T>
 pair<string, string> ProtoConverter::processType(T const& _type, bool _isValueType)
 {
 	ostringstream local, global;
-	auto varNames = newVarNames(getNextVarCounter());
-	string varName = varNames.first;
-	string paramName = varNames.second;
+	auto [varName, paramName] = newVarNames(getNextVarCounter());
 	string location{};
 	if (!m_isStateVar && !_isValueType)
 		location = "memory";
@@ -160,9 +158,9 @@ pair<string, string> ProtoConverter::varDecl(
 
 	// variable declaration
 	if (m_isStateVar)
-		global << appendVarDeclToOutput(typeStr, _varName, _location);
+		global << getVarDecl(typeStr, _varName, _location);
 	else
-		local << appendVarDeclToOutput(typeStr, _varName, _location);
+		local << getVarDecl(typeStr, _varName, _location);
 
 	// Add typed params for calling public and external functions with said type
 	appendTypedParams(
@@ -312,7 +310,7 @@ std::string ProtoConverter::typedParametersAsString(CalleeType _calleeType)
 	}
 }
 
-/// Test function to be called externally.
+// Test function to be called externally.
 string ProtoConverter::visit(TestFunction const& _x, string const& _storageVarDefs)
 {
 	// TODO: Support more than one but less than N local variables
@@ -491,10 +489,10 @@ pragma experimental ABIEncoderV2;)";
 	string testFunction = visit(_x.testfunction(), storageVarDefs);
 	/* Structure of contract body
 	 * - Storage variable declarations
-	 * - Struct type declarations
+	 * - Struct definitions
 	 * - Test function
-     *     - Storage variable definitions
-	 *     - Local variable definitions
+	 *     - Storage variable assignments
+	 *     - Local variable definitions and assignments
 	 *     - Test code proper (calls public and external functions)
 	 * - Helper functions
 	 */
@@ -557,8 +555,13 @@ string TypeVisitor::visit(ArrayType const& _type)
 	                     string("]") :
 	                     string("[]");
 	m_baseType += arrayBraces;
-	if (!_type.is_static())
-		m_isLastDynParamRightPadded = true;
+
+	// If we don't know yet if the array will be dynamically encoded,
+	// check again. If we already know that it will be, there's no
+	// need to do anything.
+	if (!m_isLastDynParamRightPadded)
+		m_isLastDynParamRightPadded = DynParamVisitor().visit(_type);
+
 	return baseType + arrayBraces;
 }
 
diff --git a/test/tools/ossfuzz/protoToAbiV2.h b/test/tools/ossfuzz/protoToAbiV2.h
index 9b6349ea4..bd20b1628 100644
--- a/test/tools/ossfuzz/protoToAbiV2.h
+++ b/test/tools/ossfuzz/protoToAbiV2.h
@@ -97,16 +97,18 @@
  */
 
 
-namespace dev {
-namespace test {
-namespace abiv2fuzzer {
-
+namespace dev
+{
+namespace test
+{
+namespace abiv2fuzzer
+{
 /// Converts a protobuf input into a Solidity program that tests
 /// abi coding.
 class ProtoConverter
 {
 public:
-	ProtoConverter() :
+	ProtoConverter():
 		m_isStateVar(true),
 		m_counter(0),
 		m_varCounter(0),
@@ -125,33 +127,31 @@ private:
 		ADD,
 		SKIP
 	};
+	/// Enum of possible function types that decode abi
+	/// encoded parameters.
 	enum class CalleeType
 	{
 		PUBLIC,
 		EXTERNAL
 	};
-	std::pair<std::string, std::string> visit(VarDecl const&);
-	std::string visit(TestFunction const&, std::string const&);
+
+	/// Visitors for various Protobuf types
+	/// Visit top-level contract specification
 	void visit(Contract const&);
 
-	template <typename T>
-	std::pair<std::string, std::string> processType(T const& _type, bool _isValueType);
+	/// Convert test function specification into Solidity test
+	/// function
+	/// @param _testSpec: Protobuf test function specification
+	/// @param _storageDefs: String containing Solidity assignment
+	/// statements to be placed inside the scope of the test function.
+	std::string visit(TestFunction const& _testSpec, std::string const& _storageDefs);
 
-	template <typename T>
-	std::pair<std::string, std::string> assignChecker(
-		std::string const& _varName,
-		std::string const& _paramName,
-		T _type
-	);
-
-	template <typename T>
-	std::pair<std::string, std::string> varDecl(
-		std::string const& _varName,
-		std::string const& _paramName,
-		T _type,
-		bool _isValueType,
-		std::string const& _location
-	);
+	/// Visitors for the remaining protobuf types. They convert
+	/// the input protobuf specification type into Solidity code.
+	/// @return A pair of strings, first of which contains Solidity
+	/// code to be placed inside contract scope, second of which contains
+	/// Solidity code to be placed inside test function scope.
+	std::pair<std::string, std::string> visit(VarDecl const&);
 	std::pair<std::string, std::string> visit(Type const&);
 	std::pair<std::string, std::string> visit(ValueType const&);
 	std::pair<std::string, std::string> visit(NonValueType const&);
@@ -163,7 +163,59 @@ private:
 	std::pair<std::string, std::string> visit(ArrayType const&);
 	std::pair<std::string, std::string> visit(StructType const&);
 
-	// Utility functions
+	/// Convert a protobuf type @a _T into Solidity code to be placed
+	/// inside contract and test function scopes.
+	/// @param: _type (of parameterized type protobuf type T) is the type
+	/// of protobuf input to be converted.
+	/// @param: _isValueType is true if _type is a Solidity value type e.g., uint
+	/// and false otherwise e.g., string
+	/// @return: A pair of strings, first of which contains Solidity
+	/// code to be placed inside contract scope, second of which contains
+	/// Solidity code to be placed inside test function scope.
+	template <typename T>
+	std::pair<std::string, std::string> processType(T const& _type, bool _isValueType);
+
+	/// Convert a protobuf type @a _T into Solidity variable assignment and check
+	/// statements to be placed inside contract and test function scopes.
+	/// @param: _varName is the name of the Solidity variable
+	/// @param: _paramName is the name of the Solidity parameter that is passed
+	/// to the test function
+	/// @param: _type (of parameterized type protobuf type T) is the type
+	/// of protobuf input to be converted.
+	/// @return: A pair of strings, first of which contains Solidity
+	/// statements to be placed inside contract scope, second of which contains
+	/// Solidity statements to be placed inside test function scope.
+	template <typename T>
+	std::pair<std::string, std::string> assignChecker(
+		std::string const& _varName,
+		std::string const& _paramName,
+		T _type
+	);
+
+	/// Convert a protobuf type @a _T into Solidity variable declaration statement.
+	/// @param: _varName is the name of the Solidity variable
+	/// @param: _paramName is the name of the Solidity parameter that is passed
+	/// to the test function
+	/// @param: _type (of parameterized type protobuf type T) is the type
+	/// of protobuf input to be converted.
+	/// @param: _isValueType is a boolean that is true if _type is a
+	/// Solidity value type e.g., uint and false otherwise e.g., string
+	/// @param: _location is the Solidity location qualifier string to
+	/// be used inside variable declaration statements
+	/// @return: A pair of strings, first of which contains Solidity
+	/// variable declaration statement to be placed inside contract scope,
+	/// second of which contains Solidity variable declaration statement
+	/// to be placed inside test function scope.
+	template <typename T>
+	std::pair<std::string, std::string> varDecl(
+		std::string const& _varName,
+		std::string const& _paramName,
+		T _type,
+		bool _isValueType,
+		std::string const& _location
+	);
+
+	/// Appends a function parameter to the function parameter stream.
 	void appendTypedParams(
 		CalleeType _calleeType,
 		bool _isValueType,
@@ -172,6 +224,8 @@ private:
 		Delimiter _delimiter
 	);
 
+	/// Appends a function parameter to the public test function's
+	/// parameter stream.
 	void appendTypedParamsPublic(
 		bool _isValueType,
 		std::string const& _typeString,
@@ -179,6 +233,8 @@ private:
 		Delimiter _delimiter = Delimiter::ADD
 	);
 
+	/// Appends a function parameter to the external test function's
+	/// parameter stream.
 	void appendTypedParamsExternal(
 		bool _isValueType,
 		std::string const& _typeString,
@@ -186,32 +242,41 @@ private:
 		Delimiter _delimiter = Delimiter::ADD
 	);
 
-	std::string appendVarDeclToOutput(
+	/// Returns a Solidity variable declaration statement
+	/// @param _type: string containing Solidity type of the
+	/// variable to be declared.
+	/// @param _varName: string containing Solidity variable
+	/// name
+	/// @param _qualifier: string containing location where
+	/// the variable will be placed
+	std::string getVarDecl(
 		std::string const& _type,
 		std::string const& _varName,
 		std::string const& _qualifier
 	);
 
+	/// Return checks that are encoded as Solidity if statements
+	/// as string
 	std::string equalityChecksAsString();
 
+	/// Return comma separated typed function parameters as string
 	std::string typedParametersAsString(CalleeType _calleeType);
 
+	/// Return Solidity helper functions as string
 	std::string helperFunctions();
 
+	/// Return top-level test code as string
 	std::string testCode(unsigned _invalidLength);
 
-	std::pair<std::string, unsigned> structDecl(StructType const& _type);
-
-	// Function definitions
-	// m_varCounter is used to derive declared and parameterized variable names
+	/// Return the next variable count that is used for
+	/// variable naming.
 	unsigned getNextVarCounter()
 	{
 		return m_varCounter++;
 	}
 
-	// Accepts an unsigned counter and returns a pair of strings
-	// First string is declared name (s_varNamePrefix<varcounter_value>)
-	// Second string is parameterized name (s_paramPrefix<varcounter_value>)
+	/// Return a pair of names for Solidity variable and the same variable when
+	/// passed as a function parameter.
 	static std::pair<std::string, std::string> newVarNames(unsigned _varCounter)
 	{
 		return std::make_pair(
@@ -220,11 +285,14 @@ private:
 		);
 	}
 
+	/// Checks if the last dynamically encoded Solidity type is right
+	/// padded, returning true if it is and false otherwise.
 	bool isLastDynParamRightPadded()
 	{
 		return m_isLastDynParamRightPadded;
 	}
 
+	/// Convert delimter to a comma or null string.
 	static std::string delimiterToString(Delimiter _delimiter);
 
 	/// Contains the test program
@@ -290,10 +358,7 @@ public:
 	};
 
 	/// Prefixes for declared and parameterized variable names
-	static auto constexpr s_varNamePrefix = "x_";
-	static auto constexpr s_paramNamePrefix = "c_";
 	static auto constexpr s_structNamePrefix = "S";
-	static auto constexpr s_structFieldPrefix = "m";
 
 	// Static function definitions
 	static bool isValueType(DataType _dataType)
@@ -386,7 +451,8 @@ public:
 
 	static std::string bytesArrayTypeAsString(DynamicByteArrayType const& _x)
 	{
-		switch (_x.type()) {
+		switch (_x.type())
+		{
 		case DynamicByteArrayType::BYTES:
 			return "bytes";
 		case DynamicByteArrayType::STRING:
@@ -699,6 +765,12 @@ public:
 
 	bool visit(ArrayType const& _type) override
 	{
+		// Mark array type as invalid in one of the following is true
+		//  - contains more than s_maxArrayDimensions dimensions
+		//  - contains an invalid base type, which happens in the
+		//  following cases
+		//    - array base type is invalid
+		//    - array base type is empty
 		m_arrayDimensions++;
 		if (m_arrayDimensions > s_maxArrayDimensions)
 			return false;
@@ -707,6 +779,9 @@ public:
 
 	bool visit(StructType const& _type) override
 	{
+		// A struct is marked invalid only if all of its fields
+		// are invalid. This is done to prevent an empty struct
+		// being defined (which is a Solidity error).
 		for (auto const& t: _type.t())
 			if (visit(t))
 				return true;