parent
54581749e3
commit
8cdbed9239
@ -0,0 +1,64 @@ |
|||||||
|
'use strict' |
||||||
|
var AstWalker = require('../util/astWalker') |
||||||
|
|
||||||
|
/** |
||||||
|
* return all contract definitions of the given @astList |
||||||
|
* |
||||||
|
* @param {Object} sourcesList - sources list (containing root AST node) |
||||||
|
* @return {Object} - returns a mapping from AST node ids to AST nodes for the contracts |
||||||
|
*/ |
||||||
|
function extractContractsDefinition (sourcesList) { |
||||||
|
var ret = { |
||||||
|
contractsIds: {}, |
||||||
|
contractsNames: {} |
||||||
|
} |
||||||
|
var walker = new AstWalker() |
||||||
|
walker.walkAstList(sourcesList, { 'ContractDefinition': function (node) { |
||||||
|
ret.contractsIds[node.id] = node |
||||||
|
ret.contractsNames[node.attributes.name] = node.id |
||||||
|
return false |
||||||
|
}}) |
||||||
|
return ret |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the linearized base contracts of the contract @arg id |
||||||
|
* |
||||||
|
* @param {Int} id - contract id to resolve |
||||||
|
* @param {Map} contracts - all contracts defined in the current context |
||||||
|
* @return {Array} - array of base contracts in derived to base order as AST nodes. |
||||||
|
*/ |
||||||
|
function getLinearizedBaseContracts (id, contracts) { |
||||||
|
return contracts[id].attributes.linearizedBaseContracts.map(function (id) { return contracts[id] }) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* return state var and type definition of the given contract |
||||||
|
* |
||||||
|
* @param {String} contractName - contract for which state var should be resolved |
||||||
|
* @param {Object} sourcesList - sources list (containing root AST node) |
||||||
|
* @return {Array} - return an array of AST node of all state variables (including inherited) (this will include all enum/struct declarations) |
||||||
|
*/ |
||||||
|
function extractStateVariables (contractName, sourcesList) { |
||||||
|
var contracts = extractContractsDefinition(sourcesList) |
||||||
|
var id = contracts.contractsNames[contractName] |
||||||
|
if (id) { |
||||||
|
var stateVar = [] |
||||||
|
var baseContracts = getLinearizedBaseContracts(id, contracts.contractsIds) |
||||||
|
baseContracts.reverse() |
||||||
|
for (var k in baseContracts) { |
||||||
|
var ctr = baseContracts[k] |
||||||
|
for (var i in ctr.children) { |
||||||
|
stateVar.push(ctr.children[i]) |
||||||
|
} |
||||||
|
} |
||||||
|
return stateVar |
||||||
|
} |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
extractStateVariables: extractStateVariables, |
||||||
|
extractContractsDefinition: extractContractsDefinition, |
||||||
|
getLinearizedBaseContracts: getLinearizedBaseContracts |
||||||
|
} |
@ -0,0 +1,353 @@ |
|||||||
|
'use strict' |
||||||
|
|
||||||
|
/** |
||||||
|
* Uint decode the given @arg type |
||||||
|
* |
||||||
|
* @param {String} type - type given by the AST |
||||||
|
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName, decoder} |
||||||
|
*/ |
||||||
|
function Uint (type) { |
||||||
|
if (type.split(' ')) { |
||||||
|
type = type.split(' ')[0] |
||||||
|
} |
||||||
|
return { |
||||||
|
needsFreeStorageSlot: false, |
||||||
|
storageBytes: parseInt(type.replace('uint', '')) / 8, |
||||||
|
typeName: type, |
||||||
|
name: 'uint' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Address decode the given @arg type |
||||||
|
* |
||||||
|
* @param {String} type - type given by the AST |
||||||
|
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName, decoder} |
||||||
|
*/ |
||||||
|
function Address (type) { |
||||||
|
return { |
||||||
|
needsFreeStorageSlot: false, |
||||||
|
storageBytes: 20, |
||||||
|
typeName: 'address', |
||||||
|
name: 'address' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Bool decode the given @arg type |
||||||
|
* |
||||||
|
* @param {String} type - type given by the AST |
||||||
|
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName, decoder} |
||||||
|
*/ |
||||||
|
function Bool (type) { |
||||||
|
return { |
||||||
|
needsFreeStorageSlot: false, |
||||||
|
storageBytes: 1, |
||||||
|
typeName: 'bool', |
||||||
|
name: 'bool' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* DynamicByteArray decode the given @arg type |
||||||
|
* |
||||||
|
* @param {String} type - type given by the AST |
||||||
|
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName, decoder} |
||||||
|
*/ |
||||||
|
function DynamicByteArray (type) { |
||||||
|
return { |
||||||
|
needsFreeStorageSlot: true, |
||||||
|
storageBytes: 32, |
||||||
|
typeName: 'bytes', |
||||||
|
name: 'dynamicByteArray' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* FixedByteArray decode the given @arg type |
||||||
|
* |
||||||
|
* @param {String} type - type given by the AST |
||||||
|
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName, decoder} |
||||||
|
*/ |
||||||
|
function FixedByteArray (type) { |
||||||
|
if (type.split(' ')) { |
||||||
|
type = type.split(' ')[0] |
||||||
|
} |
||||||
|
return { |
||||||
|
needsFreeStorageSlot: false, |
||||||
|
storageBytes: parseInt(type.replace('bytes', '')), |
||||||
|
typeName: type.split(' ')[0], |
||||||
|
name: 'fixedByteArray' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Int decode the given @arg type |
||||||
|
* |
||||||
|
* @param {String} type - type given by the AST |
||||||
|
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName, decoder} |
||||||
|
*/ |
||||||
|
function Int (type) { |
||||||
|
if (type.split(' ')) { |
||||||
|
type = type.split(' ')[0] |
||||||
|
} |
||||||
|
return { |
||||||
|
needsFreeStorageSlot: false, |
||||||
|
storageBytes: parseInt(type.replace('int', '')) / 8, |
||||||
|
typeName: type, |
||||||
|
name: 'int' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* StringType decode the given @arg type |
||||||
|
* |
||||||
|
* @param {String} type - type given by the AST |
||||||
|
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName, decoder} |
||||||
|
*/ |
||||||
|
function StringType (type) { |
||||||
|
return { |
||||||
|
needsFreeStorageSlot: true, |
||||||
|
storageBytes: 32, |
||||||
|
typeName: 'string', |
||||||
|
name: 'stringType' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* ArrayType decode the given @arg type |
||||||
|
* |
||||||
|
* @param {String} type - type given by the AST |
||||||
|
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName, decoder, arraySize, subArray} |
||||||
|
*/ |
||||||
|
function ArrayType (type, stateDefinitions) { |
||||||
|
var arraySize |
||||||
|
var storageBytes |
||||||
|
|
||||||
|
var underlyingType = extractUnderlyingType(type) |
||||||
|
|
||||||
|
arraySize = extractArraySize(type) |
||||||
|
var dimensions = extractDimensions(type) |
||||||
|
type = underlyingType + dimensions.join('') |
||||||
|
|
||||||
|
var subArrayType = type.substring(0, type.lastIndexOf('[')) |
||||||
|
var subArray = null |
||||||
|
if (subArrayType.indexOf('[') !== -1) { |
||||||
|
subArray = decode(subArrayType, stateDefinitions) |
||||||
|
} |
||||||
|
|
||||||
|
underlyingType = decode(underlyingType, stateDefinitions) |
||||||
|
storageBytes = underlyingType.storageBytes |
||||||
|
|
||||||
|
if (arraySize === 'dynamic') { |
||||||
|
storageBytes = 32 |
||||||
|
} else { |
||||||
|
if (subArray) { |
||||||
|
storageBytes = subArray.storageBytes // size on storage of one item of the array
|
||||||
|
} |
||||||
|
storageBytes = Math.ceil(storageBytes * arraySize) // size on storage of one the whole array
|
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
needsFreeStorageSlot: true, |
||||||
|
storageBytes: storageBytes, |
||||||
|
typeName: type, |
||||||
|
arraySize: arraySize, |
||||||
|
subArray: subArray, |
||||||
|
name: 'arrayType' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Enum decode the given @arg type |
||||||
|
* |
||||||
|
* @param {String} type - type given by the AST |
||||||
|
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName, decoder} |
||||||
|
*/ |
||||||
|
function Enum (type, stateDefinitions) { |
||||||
|
var extracted = type.split(' ') |
||||||
|
return { |
||||||
|
needsFreeStorageSlot: false, |
||||||
|
storageBytes: 1, |
||||||
|
typeName: extracted[0] + ' ' + extracted[1], |
||||||
|
enum: getEnum(type, stateDefinitions), |
||||||
|
name: 'enum' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Struct decode the given @arg type |
||||||
|
* |
||||||
|
* @param {String} type - type given by the AST |
||||||
|
* @return {Object} returns decoded info about the current type: { needsFreeStorageSlot, storageBytes, typeName, decoder, members} |
||||||
|
*/ |
||||||
|
function Struct (type, stateDefinitions) { |
||||||
|
var extracted = type.split(' ') |
||||||
|
type = extracted[0] + ' ' + extracted[1] |
||||||
|
var membersDetails = getStructMembers(type, stateDefinitions) |
||||||
|
return { |
||||||
|
needsFreeStorageSlot: true, |
||||||
|
storageBytes: membersDetails.storageBytes, |
||||||
|
typeName: type, |
||||||
|
members: membersDetails.members, |
||||||
|
name: 'struct' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* retrieve enum declaration of the given @arg type |
||||||
|
* |
||||||
|
* @param {String} type - type given by the AST |
||||||
|
* @param {Object} stateDefinitions - all state declarations given by the AST (including struct and enum type declaration) |
||||||
|
* @return {Array} - containing all value declaration of the current enum type |
||||||
|
*/ |
||||||
|
function getEnum (type, stateDefinitions) { |
||||||
|
for (var k in stateDefinitions) { |
||||||
|
var dec = stateDefinitions[k] |
||||||
|
if (dec.name === 'EnumDefinition' && type.indexOf('enum ' + dec.attributes.name) === 0) { |
||||||
|
return dec.children |
||||||
|
} |
||||||
|
} |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* retrieve memebers declared in the given @arg tye |
||||||
|
* |
||||||
|
* @param {String} typeName - name of the struct type (e.g struct <name>) |
||||||
|
* @param {Object} stateDefinitions - all state definition given by the AST (including struct and enum type declaration) |
||||||
|
* @return {Array} containing all members of the current struct type |
||||||
|
*/ |
||||||
|
function getStructMembers (typeName, stateDefinitions) { |
||||||
|
var members = [] |
||||||
|
var storageBytes = 0 |
||||||
|
for (var k in stateDefinitions) { |
||||||
|
var dec = stateDefinitions[k] |
||||||
|
if (dec.name === 'StructDefinition' && typeName.indexOf('struct ' + dec.attributes.name) === 0) { |
||||||
|
for (var i in dec.children) { |
||||||
|
var member = dec.children[i] |
||||||
|
var decoded = decode(member.attributes.type, stateDefinitions) |
||||||
|
members.push(decoded) |
||||||
|
if (decoded.needsFreeStorageSlot) { |
||||||
|
storageBytes = Math.ceil(storageBytes / 32) * 32 |
||||||
|
} |
||||||
|
storageBytes += decoded.storageBytes |
||||||
|
} |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
return { |
||||||
|
members: members, |
||||||
|
storageBytes: storageBytes |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* return the size of the current array |
||||||
|
* |
||||||
|
* @param {String} typeName - short type ( e.g uint[][4] ) |
||||||
|
* @return {String|Int} return 'dynamic' if dynamic array | return size of the array |
||||||
|
*/ |
||||||
|
function extractArraySize (typeName) { |
||||||
|
if (typeName.indexOf('[') !== -1) { |
||||||
|
var squareBracket = /\[([0-9]+|\s*)\]/g |
||||||
|
var dim = typeName.match(squareBracket) |
||||||
|
var size = dim[dim.length - 1] |
||||||
|
if (size === '[]') { |
||||||
|
return 'dynamic' |
||||||
|
} else { |
||||||
|
return parseInt(dim[dim.length - 1].replace('[', '').replace(']')) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* extract the underlying type |
||||||
|
* |
||||||
|
* @param {String} fullType - type given by the AST (ex: uint[2] storage ref[2]) |
||||||
|
* @return {String} return the first part of the full type. don not keep the array declaration ( uint[2] storage ref[2] will return uint) |
||||||
|
*/ |
||||||
|
function extractUnderlyingType (fullType) { |
||||||
|
var splitted = fullType.split(' ') |
||||||
|
if (fullType.indexOf('enum') === 0 || fullType.indexOf('struct') === 0) { |
||||||
|
return splitted[0] + ' ' + splitted[1] |
||||||
|
} |
||||||
|
if (splitted.length > 0) { |
||||||
|
fullType = splitted[0] |
||||||
|
} |
||||||
|
if (fullType[fullType.length - 1] === ']') { |
||||||
|
return fullType.substring(0, fullType.indexOf('[')) |
||||||
|
} |
||||||
|
return fullType |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get the array dimensions |
||||||
|
* |
||||||
|
* @param {String} fullType - type given by the AST |
||||||
|
* @return {Array} containing all the dimensions and size of the array (e.g ['[3]', '[]'] ) |
||||||
|
*/ |
||||||
|
function extractDimensions (fullType) { |
||||||
|
var ret = [] |
||||||
|
if (fullType.indexOf('[') !== -1) { |
||||||
|
var squareBracket = /\[([0-9]+|\s*)\]/g |
||||||
|
var dim = fullType.match(squareBracket) |
||||||
|
return dim |
||||||
|
} |
||||||
|
return ret |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* parse the full type |
||||||
|
* |
||||||
|
* @param {String} fullType - type given by the AST (ex: uint[2] storage ref[2]) |
||||||
|
* @return {String} returns the token type (used to instanciate the right decoder) (uint[2] storage ref[2] will return 'array', uint256 will return uintX) |
||||||
|
*/ |
||||||
|
function extractTokenType (fullType) { |
||||||
|
if (fullType.indexOf('[') !== -1) { |
||||||
|
return 'array' |
||||||
|
} |
||||||
|
if (fullType.indexOf(' ') !== -1) { |
||||||
|
fullType = fullType.split(' ')[0] |
||||||
|
} |
||||||
|
var char = fullType.indexOf('bytes') === 0 ? 'X' : '' |
||||||
|
return fullType.replace(/[0-9]+/g, char) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* parse the type and return an object representing the type |
||||||
|
* |
||||||
|
* @param {Object} type - type name given by the ast node |
||||||
|
* @param {Object} stateDefinitions - all state stateDefinitions given by the AST (including struct and enum type declaration) |
||||||
|
* @return {Object} - return the corresponding decoder |
||||||
|
*/ |
||||||
|
function decode (type, stateDefinitions) { |
||||||
|
var decodeInfos = { |
||||||
|
'address': Address, |
||||||
|
'array': ArrayType, |
||||||
|
'bool': Bool, |
||||||
|
'bytes': DynamicByteArray, |
||||||
|
'bytesX': FixedByteArray, |
||||||
|
'enum': Enum, |
||||||
|
'string': StringType, |
||||||
|
'struct': Struct, |
||||||
|
'int': Int, |
||||||
|
'uint': Uint |
||||||
|
} |
||||||
|
var currentType = extractTokenType(type) |
||||||
|
return decodeInfos[currentType](type, stateDefinitions) |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
decode: decode, |
||||||
|
Uint: Uint, |
||||||
|
Address: Address, |
||||||
|
Bool: Bool, |
||||||
|
DynamicByteArray: DynamicByteArray, |
||||||
|
FixedByteArray: FixedByteArray, |
||||||
|
Int: Int, |
||||||
|
StringType: StringType, |
||||||
|
ArrayType: ArrayType, |
||||||
|
Enum: Enum, |
||||||
|
Struct: Struct |
||||||
|
} |
Loading…
Reference in new issue